mirror of
https://github.com/grafana/grafana.git
synced 2026-02-03 20:49:50 -05:00
Unified Storage: Implement field selector list with search (#117100)
Some checks are pending
Actionlint / Lint GitHub Actions files (push) Waiting to run
Backend Code Checks / Detect whether code changed (push) Waiting to run
Backend Code Checks / Validate Backend Configs (push) Blocked by required conditions
Backend Unit Tests / Detect whether code changed (push) Waiting to run
Backend Unit Tests / Grafana (1/8) (push) Blocked by required conditions
Backend Unit Tests / Grafana (2/8) (push) Blocked by required conditions
Backend Unit Tests / Grafana (3/8) (push) Blocked by required conditions
Backend Unit Tests / Grafana (4/8) (push) Blocked by required conditions
Backend Unit Tests / Grafana (5/8) (push) Blocked by required conditions
Backend Unit Tests / Grafana (6/8) (push) Blocked by required conditions
Backend Unit Tests / Grafana (7/8) (push) Blocked by required conditions
Backend Unit Tests / Grafana (8/8) (push) Blocked by required conditions
Backend Unit Tests / Grafana Enterprise (1/8) (push) Blocked by required conditions
Backend Unit Tests / Grafana Enterprise (2/8) (push) Blocked by required conditions
Backend Unit Tests / Grafana Enterprise (3/8) (push) Blocked by required conditions
Backend Unit Tests / Grafana Enterprise (4/8) (push) Blocked by required conditions
Backend Unit Tests / Grafana Enterprise (5/8) (push) Blocked by required conditions
Backend Unit Tests / Grafana Enterprise (6/8) (push) Blocked by required conditions
Backend Unit Tests / Grafana Enterprise (7/8) (push) Blocked by required conditions
Backend Unit Tests / Grafana Enterprise (8/8) (push) Blocked by required conditions
Backend Unit Tests / All backend unit tests complete (push) Blocked by required conditions
CodeQL checks / Detect whether code changed (push) Waiting to run
CodeQL checks / Analyze (push) Blocked by required conditions
Deploy Storybook / Detect whether code changed (push) Waiting to run
Deploy Storybook / Deploy Storybook (push) Blocked by required conditions
Lint Frontend / Detect whether code changed (push) Waiting to run
Lint Frontend / Lint (push) Blocked by required conditions
Lint Frontend / Typecheck (push) Blocked by required conditions
Lint Frontend / Verify API clients (push) Waiting to run
Lint Frontend / Verify API clients (enterprise) (push) Waiting to run
Lint Frontend / Verify packed frontend packages (push) Blocked by required conditions
golangci-lint / Detect whether code changed (push) Waiting to run
golangci-lint / go-fmt (push) Blocked by required conditions
golangci-lint / lint-go (push) Blocked by required conditions
Crowdin Upload Action / upload-sources-to-crowdin (push) Waiting to run
Verify i18n / verify-i18n (push) Waiting to run
Documentation / Build & Verify Docs (push) Waiting to run
End-to-end tests / Detect whether code changed (push) Waiting to run
End-to-end tests / Build & Package Grafana (push) Blocked by required conditions
End-to-end tests / Build E2E test runner (push) Blocked by required conditions
End-to-end tests / push-docker-image (push) Blocked by required conditions
End-to-end tests / dashboards-suite (old arch) (push) Blocked by required conditions
End-to-end tests / panels-suite (old arch) (push) Blocked by required conditions
End-to-end tests / smoke-tests-suite (old arch) (push) Blocked by required conditions
End-to-end tests / various-suite (old arch) (push) Blocked by required conditions
End-to-end tests / Verify Storybook (Playwright) (push) Blocked by required conditions
End-to-end tests / Playwright E2E tests (1/8) (push) Blocked by required conditions
End-to-end tests / Playwright E2E tests (2/8) (push) Blocked by required conditions
End-to-end tests / Playwright E2E tests (3/8) (push) Blocked by required conditions
End-to-end tests / Playwright E2E tests (4/8) (push) Blocked by required conditions
End-to-end tests / Playwright E2E tests (5/8) (push) Blocked by required conditions
End-to-end tests / Playwright E2E tests (6/8) (push) Blocked by required conditions
End-to-end tests / Playwright E2E tests (7/8) (push) Blocked by required conditions
End-to-end tests / Playwright E2E tests (8/8) (push) Blocked by required conditions
End-to-end tests / run-azure-monitor-e2e (push) Blocked by required conditions
End-to-end tests / All Playwright tests complete (push) Blocked by required conditions
End-to-end tests / A11y test (push) Blocked by required conditions
End-to-end tests / Publish metrics (push) Blocked by required conditions
End-to-end tests / All E2E tests complete (push) Blocked by required conditions
Frontend tests / Detect whether code changed (push) Waiting to run
Frontend tests / Unit tests (1 / 16) (push) Blocked by required conditions
Frontend tests / Unit tests (10 / 16) (push) Blocked by required conditions
Frontend tests / Unit tests (11 / 16) (push) Blocked by required conditions
Frontend tests / Unit tests (12 / 16) (push) Blocked by required conditions
Frontend tests / Unit tests (13 / 16) (push) Blocked by required conditions
Frontend tests / Unit tests (14 / 16) (push) Blocked by required conditions
Frontend tests / Unit tests (15 / 16) (push) Blocked by required conditions
Frontend tests / Unit tests (16 / 16) (push) Blocked by required conditions
Frontend tests / Unit tests (2 / 16) (push) Blocked by required conditions
Frontend tests / Unit tests (3 / 16) (push) Blocked by required conditions
Frontend tests / Unit tests (4 / 16) (push) Blocked by required conditions
Frontend tests / Unit tests (5 / 16) (push) Blocked by required conditions
Frontend tests / Unit tests (6 / 16) (push) Blocked by required conditions
Frontend tests / Unit tests (7 / 16) (push) Blocked by required conditions
Frontend tests / Unit tests (8 / 16) (push) Blocked by required conditions
Frontend tests / Unit tests (9 / 16) (push) Blocked by required conditions
Frontend tests / Decoupled plugin tests (push) Blocked by required conditions
Frontend tests / Packages unit tests (push) Blocked by required conditions
Frontend tests / All frontend unit tests complete (push) Blocked by required conditions
Frontend tests / Devenv frontend-service build (push) Blocked by required conditions
Integration Tests / Detect whether code changed (push) Waiting to run
Integration Tests / Sqlite (1/4) (push) Blocked by required conditions
Integration Tests / Sqlite (2/4) (push) Blocked by required conditions
Integration Tests / Sqlite (3/4) (push) Blocked by required conditions
Integration Tests / Sqlite (4/4) (push) Blocked by required conditions
Integration Tests / Sqlite Without CGo (1/4) (push) Blocked by required conditions
Integration Tests / Sqlite Without CGo (2/4) (push) Blocked by required conditions
Integration Tests / Sqlite Without CGo (3/4) (push) Blocked by required conditions
Integration Tests / Sqlite Without CGo (4/4) (push) Blocked by required conditions
Integration Tests / Sqlite Without CGo (profiled) (push) Blocked by required conditions
Integration Tests / MySQL (1/16) (push) Blocked by required conditions
Integration Tests / MySQL (10/16) (push) Blocked by required conditions
Integration Tests / MySQL (11/16) (push) Blocked by required conditions
Integration Tests / MySQL (12/16) (push) Blocked by required conditions
Integration Tests / MySQL (13/16) (push) Blocked by required conditions
Integration Tests / MySQL (14/16) (push) Blocked by required conditions
Integration Tests / MySQL (15/16) (push) Blocked by required conditions
Integration Tests / MySQL (16/16) (push) Blocked by required conditions
Integration Tests / MySQL (2/16) (push) Blocked by required conditions
Integration Tests / MySQL (3/16) (push) Blocked by required conditions
Integration Tests / MySQL (4/16) (push) Blocked by required conditions
Integration Tests / MySQL (5/16) (push) Blocked by required conditions
Integration Tests / MySQL (6/16) (push) Blocked by required conditions
Integration Tests / MySQL (7/16) (push) Blocked by required conditions
Integration Tests / MySQL (8/16) (push) Blocked by required conditions
Integration Tests / MySQL (9/16) (push) Blocked by required conditions
Integration Tests / Postgres (1/16) (push) Blocked by required conditions
Integration Tests / Postgres (10/16) (push) Blocked by required conditions
Integration Tests / Postgres (11/16) (push) Blocked by required conditions
Integration Tests / Postgres (12/16) (push) Blocked by required conditions
Integration Tests / Postgres (13/16) (push) Blocked by required conditions
Integration Tests / Postgres (14/16) (push) Blocked by required conditions
Integration Tests / Postgres (15/16) (push) Blocked by required conditions
Integration Tests / Postgres (16/16) (push) Blocked by required conditions
Integration Tests / Postgres (2/16) (push) Blocked by required conditions
Integration Tests / Postgres (3/16) (push) Blocked by required conditions
Integration Tests / Postgres (4/16) (push) Blocked by required conditions
Integration Tests / Postgres (5/16) (push) Blocked by required conditions
Integration Tests / Postgres (6/16) (push) Blocked by required conditions
Integration Tests / Postgres (7/16) (push) Blocked by required conditions
Integration Tests / Postgres (8/16) (push) Blocked by required conditions
Integration Tests / Postgres (9/16) (push) Blocked by required conditions
Integration Tests / Sqlite Enterprise (1/4) (push) Blocked by required conditions
Integration Tests / Sqlite Enterprise (2/4) (push) Blocked by required conditions
Integration Tests / Sqlite Enterprise (3/4) (push) Blocked by required conditions
Integration Tests / Sqlite Enterprise (4/4) (push) Blocked by required conditions
Integration Tests / Sqlite Without CGo Enterprise (1/4) (push) Blocked by required conditions
Integration Tests / Sqlite Without CGo Enterprise (2/4) (push) Blocked by required conditions
Integration Tests / Sqlite Without CGo Enterprise (3/4) (push) Blocked by required conditions
Integration Tests / Sqlite Without CGo Enterprise (4/4) (push) Blocked by required conditions
Integration Tests / Sqlite Without CGo Enterprise (profiled) (push) Blocked by required conditions
Integration Tests / MySQL Enterprise (1/16) (push) Blocked by required conditions
Integration Tests / MySQL Enterprise (10/16) (push) Blocked by required conditions
Integration Tests / MySQL Enterprise (11/16) (push) Blocked by required conditions
Integration Tests / MySQL Enterprise (12/16) (push) Blocked by required conditions
Integration Tests / MySQL Enterprise (13/16) (push) Blocked by required conditions
Integration Tests / MySQL Enterprise (14/16) (push) Blocked by required conditions
Integration Tests / MySQL Enterprise (15/16) (push) Blocked by required conditions
Integration Tests / MySQL Enterprise (16/16) (push) Blocked by required conditions
Integration Tests / MySQL Enterprise (2/16) (push) Blocked by required conditions
Integration Tests / MySQL Enterprise (3/16) (push) Blocked by required conditions
Integration Tests / MySQL Enterprise (4/16) (push) Blocked by required conditions
Integration Tests / MySQL Enterprise (5/16) (push) Blocked by required conditions
Integration Tests / MySQL Enterprise (6/16) (push) Blocked by required conditions
Integration Tests / MySQL Enterprise (7/16) (push) Blocked by required conditions
Integration Tests / MySQL Enterprise (8/16) (push) Blocked by required conditions
Integration Tests / MySQL Enterprise (9/16) (push) Blocked by required conditions
Integration Tests / Postgres Enterprise (1/16) (push) Blocked by required conditions
Integration Tests / Postgres Enterprise (10/16) (push) Blocked by required conditions
Integration Tests / Postgres Enterprise (11/16) (push) Blocked by required conditions
Integration Tests / Postgres Enterprise (12/16) (push) Blocked by required conditions
Integration Tests / Postgres Enterprise (13/16) (push) Blocked by required conditions
Integration Tests / Postgres Enterprise (14/16) (push) Blocked by required conditions
Integration Tests / Postgres Enterprise (15/16) (push) Blocked by required conditions
Integration Tests / Postgres Enterprise (16/16) (push) Blocked by required conditions
Integration Tests / Postgres Enterprise (2/16) (push) Blocked by required conditions
Integration Tests / Postgres Enterprise (3/16) (push) Blocked by required conditions
Integration Tests / Postgres Enterprise (4/16) (push) Blocked by required conditions
Integration Tests / Postgres Enterprise (5/16) (push) Blocked by required conditions
Integration Tests / Postgres Enterprise (6/16) (push) Blocked by required conditions
Integration Tests / Postgres Enterprise (7/16) (push) Blocked by required conditions
Integration Tests / Postgres Enterprise (8/16) (push) Blocked by required conditions
Integration Tests / Postgres Enterprise (9/16) (push) Blocked by required conditions
Integration Tests / All backend integration tests complete (push) Blocked by required conditions
publish-kinds-next / main (push) Waiting to run
publish-technical-documentation-next / sync (push) Waiting to run
Reject GitHub secrets / reject-gh-secrets (push) Waiting to run
Build Release Packages / setup (push) Waiting to run
Build Release Packages / Dispatch grafana-enterprise build (push) Blocked by required conditions
Build Release Packages / / darwin-amd64 (push) Blocked by required conditions
Build Release Packages / / darwin-arm64 (push) Blocked by required conditions
Build Release Packages / / linux-amd64 (push) Blocked by required conditions
Build Release Packages / / linux-armv6 (push) Blocked by required conditions
Build Release Packages / / linux-armv7 (push) Blocked by required conditions
Build Release Packages / / linux-arm64 (push) Blocked by required conditions
Build Release Packages / / linux-s390x (push) Blocked by required conditions
Build Release Packages / / windows-amd64 (push) Blocked by required conditions
Build Release Packages / / windows-arm64 (push) Blocked by required conditions
Build Release Packages / Upload artifacts (push) Blocked by required conditions
Build Release Packages / publish-dockerhub (push) Blocked by required conditions
Build Release Packages / Dispatch publish NPM canaries (push) Blocked by required conditions
Build Release Packages / notify-pr (push) Blocked by required conditions
Run dashboard schema v2 e2e / dashboard-schema-v2-e2e (push) Waiting to run
Shellcheck / Shellcheck scripts (push) Waiting to run
Run Storybook a11y tests / Detect whether code changed (push) Waiting to run
Run Storybook a11y tests / Run Storybook a11y tests (light theme) (push) Blocked by required conditions
Run Storybook a11y tests / Run Storybook a11y tests (dark theme) (push) Blocked by required conditions
Swagger generated code / Detect whether code changed (push) Waiting to run
Swagger generated code / Verify committed API specs match (push) Blocked by required conditions
Dispatch sync to mirror / dispatch-job (push) Waiting to run
trigger-dashboard-search-e2e / trigger-search-e2e (push) Waiting to run
Trivy Scan / trivy-scan (push) Waiting to run
Some checks are pending
Actionlint / Lint GitHub Actions files (push) Waiting to run
Backend Code Checks / Detect whether code changed (push) Waiting to run
Backend Code Checks / Validate Backend Configs (push) Blocked by required conditions
Backend Unit Tests / Detect whether code changed (push) Waiting to run
Backend Unit Tests / Grafana (1/8) (push) Blocked by required conditions
Backend Unit Tests / Grafana (2/8) (push) Blocked by required conditions
Backend Unit Tests / Grafana (3/8) (push) Blocked by required conditions
Backend Unit Tests / Grafana (4/8) (push) Blocked by required conditions
Backend Unit Tests / Grafana (5/8) (push) Blocked by required conditions
Backend Unit Tests / Grafana (6/8) (push) Blocked by required conditions
Backend Unit Tests / Grafana (7/8) (push) Blocked by required conditions
Backend Unit Tests / Grafana (8/8) (push) Blocked by required conditions
Backend Unit Tests / Grafana Enterprise (1/8) (push) Blocked by required conditions
Backend Unit Tests / Grafana Enterprise (2/8) (push) Blocked by required conditions
Backend Unit Tests / Grafana Enterprise (3/8) (push) Blocked by required conditions
Backend Unit Tests / Grafana Enterprise (4/8) (push) Blocked by required conditions
Backend Unit Tests / Grafana Enterprise (5/8) (push) Blocked by required conditions
Backend Unit Tests / Grafana Enterprise (6/8) (push) Blocked by required conditions
Backend Unit Tests / Grafana Enterprise (7/8) (push) Blocked by required conditions
Backend Unit Tests / Grafana Enterprise (8/8) (push) Blocked by required conditions
Backend Unit Tests / All backend unit tests complete (push) Blocked by required conditions
CodeQL checks / Detect whether code changed (push) Waiting to run
CodeQL checks / Analyze (push) Blocked by required conditions
Deploy Storybook / Detect whether code changed (push) Waiting to run
Deploy Storybook / Deploy Storybook (push) Blocked by required conditions
Lint Frontend / Detect whether code changed (push) Waiting to run
Lint Frontend / Lint (push) Blocked by required conditions
Lint Frontend / Typecheck (push) Blocked by required conditions
Lint Frontend / Verify API clients (push) Waiting to run
Lint Frontend / Verify API clients (enterprise) (push) Waiting to run
Lint Frontend / Verify packed frontend packages (push) Blocked by required conditions
golangci-lint / Detect whether code changed (push) Waiting to run
golangci-lint / go-fmt (push) Blocked by required conditions
golangci-lint / lint-go (push) Blocked by required conditions
Crowdin Upload Action / upload-sources-to-crowdin (push) Waiting to run
Verify i18n / verify-i18n (push) Waiting to run
Documentation / Build & Verify Docs (push) Waiting to run
End-to-end tests / Detect whether code changed (push) Waiting to run
End-to-end tests / Build & Package Grafana (push) Blocked by required conditions
End-to-end tests / Build E2E test runner (push) Blocked by required conditions
End-to-end tests / push-docker-image (push) Blocked by required conditions
End-to-end tests / dashboards-suite (old arch) (push) Blocked by required conditions
End-to-end tests / panels-suite (old arch) (push) Blocked by required conditions
End-to-end tests / smoke-tests-suite (old arch) (push) Blocked by required conditions
End-to-end tests / various-suite (old arch) (push) Blocked by required conditions
End-to-end tests / Verify Storybook (Playwright) (push) Blocked by required conditions
End-to-end tests / Playwright E2E tests (1/8) (push) Blocked by required conditions
End-to-end tests / Playwright E2E tests (2/8) (push) Blocked by required conditions
End-to-end tests / Playwright E2E tests (3/8) (push) Blocked by required conditions
End-to-end tests / Playwright E2E tests (4/8) (push) Blocked by required conditions
End-to-end tests / Playwright E2E tests (5/8) (push) Blocked by required conditions
End-to-end tests / Playwright E2E tests (6/8) (push) Blocked by required conditions
End-to-end tests / Playwright E2E tests (7/8) (push) Blocked by required conditions
End-to-end tests / Playwright E2E tests (8/8) (push) Blocked by required conditions
End-to-end tests / run-azure-monitor-e2e (push) Blocked by required conditions
End-to-end tests / All Playwright tests complete (push) Blocked by required conditions
End-to-end tests / A11y test (push) Blocked by required conditions
End-to-end tests / Publish metrics (push) Blocked by required conditions
End-to-end tests / All E2E tests complete (push) Blocked by required conditions
Frontend tests / Detect whether code changed (push) Waiting to run
Frontend tests / Unit tests (1 / 16) (push) Blocked by required conditions
Frontend tests / Unit tests (10 / 16) (push) Blocked by required conditions
Frontend tests / Unit tests (11 / 16) (push) Blocked by required conditions
Frontend tests / Unit tests (12 / 16) (push) Blocked by required conditions
Frontend tests / Unit tests (13 / 16) (push) Blocked by required conditions
Frontend tests / Unit tests (14 / 16) (push) Blocked by required conditions
Frontend tests / Unit tests (15 / 16) (push) Blocked by required conditions
Frontend tests / Unit tests (16 / 16) (push) Blocked by required conditions
Frontend tests / Unit tests (2 / 16) (push) Blocked by required conditions
Frontend tests / Unit tests (3 / 16) (push) Blocked by required conditions
Frontend tests / Unit tests (4 / 16) (push) Blocked by required conditions
Frontend tests / Unit tests (5 / 16) (push) Blocked by required conditions
Frontend tests / Unit tests (6 / 16) (push) Blocked by required conditions
Frontend tests / Unit tests (7 / 16) (push) Blocked by required conditions
Frontend tests / Unit tests (8 / 16) (push) Blocked by required conditions
Frontend tests / Unit tests (9 / 16) (push) Blocked by required conditions
Frontend tests / Decoupled plugin tests (push) Blocked by required conditions
Frontend tests / Packages unit tests (push) Blocked by required conditions
Frontend tests / All frontend unit tests complete (push) Blocked by required conditions
Frontend tests / Devenv frontend-service build (push) Blocked by required conditions
Integration Tests / Detect whether code changed (push) Waiting to run
Integration Tests / Sqlite (1/4) (push) Blocked by required conditions
Integration Tests / Sqlite (2/4) (push) Blocked by required conditions
Integration Tests / Sqlite (3/4) (push) Blocked by required conditions
Integration Tests / Sqlite (4/4) (push) Blocked by required conditions
Integration Tests / Sqlite Without CGo (1/4) (push) Blocked by required conditions
Integration Tests / Sqlite Without CGo (2/4) (push) Blocked by required conditions
Integration Tests / Sqlite Without CGo (3/4) (push) Blocked by required conditions
Integration Tests / Sqlite Without CGo (4/4) (push) Blocked by required conditions
Integration Tests / Sqlite Without CGo (profiled) (push) Blocked by required conditions
Integration Tests / MySQL (1/16) (push) Blocked by required conditions
Integration Tests / MySQL (10/16) (push) Blocked by required conditions
Integration Tests / MySQL (11/16) (push) Blocked by required conditions
Integration Tests / MySQL (12/16) (push) Blocked by required conditions
Integration Tests / MySQL (13/16) (push) Blocked by required conditions
Integration Tests / MySQL (14/16) (push) Blocked by required conditions
Integration Tests / MySQL (15/16) (push) Blocked by required conditions
Integration Tests / MySQL (16/16) (push) Blocked by required conditions
Integration Tests / MySQL (2/16) (push) Blocked by required conditions
Integration Tests / MySQL (3/16) (push) Blocked by required conditions
Integration Tests / MySQL (4/16) (push) Blocked by required conditions
Integration Tests / MySQL (5/16) (push) Blocked by required conditions
Integration Tests / MySQL (6/16) (push) Blocked by required conditions
Integration Tests / MySQL (7/16) (push) Blocked by required conditions
Integration Tests / MySQL (8/16) (push) Blocked by required conditions
Integration Tests / MySQL (9/16) (push) Blocked by required conditions
Integration Tests / Postgres (1/16) (push) Blocked by required conditions
Integration Tests / Postgres (10/16) (push) Blocked by required conditions
Integration Tests / Postgres (11/16) (push) Blocked by required conditions
Integration Tests / Postgres (12/16) (push) Blocked by required conditions
Integration Tests / Postgres (13/16) (push) Blocked by required conditions
Integration Tests / Postgres (14/16) (push) Blocked by required conditions
Integration Tests / Postgres (15/16) (push) Blocked by required conditions
Integration Tests / Postgres (16/16) (push) Blocked by required conditions
Integration Tests / Postgres (2/16) (push) Blocked by required conditions
Integration Tests / Postgres (3/16) (push) Blocked by required conditions
Integration Tests / Postgres (4/16) (push) Blocked by required conditions
Integration Tests / Postgres (5/16) (push) Blocked by required conditions
Integration Tests / Postgres (6/16) (push) Blocked by required conditions
Integration Tests / Postgres (7/16) (push) Blocked by required conditions
Integration Tests / Postgres (8/16) (push) Blocked by required conditions
Integration Tests / Postgres (9/16) (push) Blocked by required conditions
Integration Tests / Sqlite Enterprise (1/4) (push) Blocked by required conditions
Integration Tests / Sqlite Enterprise (2/4) (push) Blocked by required conditions
Integration Tests / Sqlite Enterprise (3/4) (push) Blocked by required conditions
Integration Tests / Sqlite Enterprise (4/4) (push) Blocked by required conditions
Integration Tests / Sqlite Without CGo Enterprise (1/4) (push) Blocked by required conditions
Integration Tests / Sqlite Without CGo Enterprise (2/4) (push) Blocked by required conditions
Integration Tests / Sqlite Without CGo Enterprise (3/4) (push) Blocked by required conditions
Integration Tests / Sqlite Without CGo Enterprise (4/4) (push) Blocked by required conditions
Integration Tests / Sqlite Without CGo Enterprise (profiled) (push) Blocked by required conditions
Integration Tests / MySQL Enterprise (1/16) (push) Blocked by required conditions
Integration Tests / MySQL Enterprise (10/16) (push) Blocked by required conditions
Integration Tests / MySQL Enterprise (11/16) (push) Blocked by required conditions
Integration Tests / MySQL Enterprise (12/16) (push) Blocked by required conditions
Integration Tests / MySQL Enterprise (13/16) (push) Blocked by required conditions
Integration Tests / MySQL Enterprise (14/16) (push) Blocked by required conditions
Integration Tests / MySQL Enterprise (15/16) (push) Blocked by required conditions
Integration Tests / MySQL Enterprise (16/16) (push) Blocked by required conditions
Integration Tests / MySQL Enterprise (2/16) (push) Blocked by required conditions
Integration Tests / MySQL Enterprise (3/16) (push) Blocked by required conditions
Integration Tests / MySQL Enterprise (4/16) (push) Blocked by required conditions
Integration Tests / MySQL Enterprise (5/16) (push) Blocked by required conditions
Integration Tests / MySQL Enterprise (6/16) (push) Blocked by required conditions
Integration Tests / MySQL Enterprise (7/16) (push) Blocked by required conditions
Integration Tests / MySQL Enterprise (8/16) (push) Blocked by required conditions
Integration Tests / MySQL Enterprise (9/16) (push) Blocked by required conditions
Integration Tests / Postgres Enterprise (1/16) (push) Blocked by required conditions
Integration Tests / Postgres Enterprise (10/16) (push) Blocked by required conditions
Integration Tests / Postgres Enterprise (11/16) (push) Blocked by required conditions
Integration Tests / Postgres Enterprise (12/16) (push) Blocked by required conditions
Integration Tests / Postgres Enterprise (13/16) (push) Blocked by required conditions
Integration Tests / Postgres Enterprise (14/16) (push) Blocked by required conditions
Integration Tests / Postgres Enterprise (15/16) (push) Blocked by required conditions
Integration Tests / Postgres Enterprise (16/16) (push) Blocked by required conditions
Integration Tests / Postgres Enterprise (2/16) (push) Blocked by required conditions
Integration Tests / Postgres Enterprise (3/16) (push) Blocked by required conditions
Integration Tests / Postgres Enterprise (4/16) (push) Blocked by required conditions
Integration Tests / Postgres Enterprise (5/16) (push) Blocked by required conditions
Integration Tests / Postgres Enterprise (6/16) (push) Blocked by required conditions
Integration Tests / Postgres Enterprise (7/16) (push) Blocked by required conditions
Integration Tests / Postgres Enterprise (8/16) (push) Blocked by required conditions
Integration Tests / Postgres Enterprise (9/16) (push) Blocked by required conditions
Integration Tests / All backend integration tests complete (push) Blocked by required conditions
publish-kinds-next / main (push) Waiting to run
publish-technical-documentation-next / sync (push) Waiting to run
Reject GitHub secrets / reject-gh-secrets (push) Waiting to run
Build Release Packages / setup (push) Waiting to run
Build Release Packages / Dispatch grafana-enterprise build (push) Blocked by required conditions
Build Release Packages / / darwin-amd64 (push) Blocked by required conditions
Build Release Packages / / darwin-arm64 (push) Blocked by required conditions
Build Release Packages / / linux-amd64 (push) Blocked by required conditions
Build Release Packages / / linux-armv6 (push) Blocked by required conditions
Build Release Packages / / linux-armv7 (push) Blocked by required conditions
Build Release Packages / / linux-arm64 (push) Blocked by required conditions
Build Release Packages / / linux-s390x (push) Blocked by required conditions
Build Release Packages / / windows-amd64 (push) Blocked by required conditions
Build Release Packages / / windows-arm64 (push) Blocked by required conditions
Build Release Packages / Upload artifacts (push) Blocked by required conditions
Build Release Packages / publish-dockerhub (push) Blocked by required conditions
Build Release Packages / Dispatch publish NPM canaries (push) Blocked by required conditions
Build Release Packages / notify-pr (push) Blocked by required conditions
Run dashboard schema v2 e2e / dashboard-schema-v2-e2e (push) Waiting to run
Shellcheck / Shellcheck scripts (push) Waiting to run
Run Storybook a11y tests / Detect whether code changed (push) Waiting to run
Run Storybook a11y tests / Run Storybook a11y tests (light theme) (push) Blocked by required conditions
Run Storybook a11y tests / Run Storybook a11y tests (dark theme) (push) Blocked by required conditions
Swagger generated code / Detect whether code changed (push) Waiting to run
Swagger generated code / Verify committed API specs match (push) Blocked by required conditions
Dispatch sync to mirror / dispatch-job (push) Waiting to run
trigger-dashboard-search-e2e / trigger-search-e2e (push) Waiting to run
Trivy Scan / trivy-scan (push) Waiting to run
* implement field selector list with search * dont modify input array when filtering fields filter metadata.namespace field and unsupported operators (!=) update tests * put list request in table test struct * address the nits * fix linter error - prealloc * skip rows we dont have permission to Read() * makes folder title a selectable field and fixes cyclic import with selectable field utils * make gofmt
This commit is contained in:
parent
c2055f2167
commit
f3db9cd52e
4 changed files with 567 additions and 2 deletions
|
|
@ -18,6 +18,10 @@ type ContinueToken struct {
|
|||
ResourceVersion int64 `json:"v"`
|
||||
// SortAscending indicates the sort order (used by list history).
|
||||
SortAscending bool `json:"s,omitempty"`
|
||||
// SearchAfter is a []string of sort values for search pagination.
|
||||
SearchAfter []string `json:"sa,omitempty"`
|
||||
// SearchBefore is a []string of sort values for search pagination.
|
||||
SearchBefore []string `json:"sb,omitempty"`
|
||||
}
|
||||
|
||||
func (c ContinueToken) String() string {
|
||||
|
|
@ -39,3 +43,8 @@ func GetContinueToken(token string) (*ContinueToken, error) {
|
|||
|
||||
return t, nil
|
||||
}
|
||||
|
||||
// NewSearchContinueToken encodes SearchAfter values into a continue token string.
|
||||
func NewSearchContinueToken(searchAfter []string, rv int64) (string, error) {
|
||||
return ContinueToken{SearchAfter: searchAfter, ResourceVersion: rv}.String(), nil
|
||||
}
|
||||
|
|
|
|||
114
pkg/storage/unified/resource/list_with_field_selectors.go
Normal file
114
pkg/storage/unified/resource/list_with_field_selectors.go
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
package resource
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resourcepb"
|
||||
)
|
||||
|
||||
func (s *server) listWithFieldSelectors(ctx context.Context, req *resourcepb.ListRequest) (*resourcepb.ListResponse, error) {
|
||||
if req.Options.Key.Namespace == "" {
|
||||
return &resourcepb.ListResponse{
|
||||
Error: NewBadRequestError("namespace must be specified for list with filter"),
|
||||
}, nil
|
||||
}
|
||||
|
||||
for _, v := range req.Options.Fields {
|
||||
v.Key = SEARCH_SELECTABLE_FIELDS_PREFIX + v.Key
|
||||
}
|
||||
|
||||
srq := &resourcepb.ResourceSearchRequest{
|
||||
Options: req.Options,
|
||||
Limit: req.Limit,
|
||||
}
|
||||
|
||||
var listRv int64
|
||||
if req.NextPageToken != "" {
|
||||
token, err := GetContinueToken(req.NextPageToken)
|
||||
if err != nil {
|
||||
return &resourcepb.ListResponse{
|
||||
Error: NewBadRequestError("invalid continue token"),
|
||||
}, nil
|
||||
}
|
||||
listRv = token.ResourceVersion
|
||||
srq.SearchAfter = token.SearchAfter
|
||||
srq.SearchBefore = token.SearchBefore
|
||||
}
|
||||
|
||||
searchResp, err := s.searchClient.Search(ctx, srq)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// If it's the first page, set the listRv to the search response RV
|
||||
if listRv <= 0 {
|
||||
listRv = searchResp.ResourceVersion
|
||||
}
|
||||
|
||||
pageBytes := 0
|
||||
rsp := &resourcepb.ListResponse{
|
||||
ResourceVersion: listRv,
|
||||
}
|
||||
|
||||
// Using searchResp.GetResults().GetRows() will not panic if anything is nil on the path.
|
||||
for _, row := range searchResp.GetResults().GetRows() {
|
||||
// TODO: use batch reads
|
||||
// The Read() will also handle permission checks here
|
||||
val, err := s.Read(ctx, &resourcepb.ReadRequest{
|
||||
Key: row.Key,
|
||||
ResourceVersion: row.ResourceVersion,
|
||||
})
|
||||
if err != nil {
|
||||
return &resourcepb.ListResponse{Error: AsErrorResult(err)}, nil
|
||||
}
|
||||
if val.Error != nil {
|
||||
if val.Error.Code == http.StatusForbidden {
|
||||
continue
|
||||
}
|
||||
return &resourcepb.ListResponse{Error: val.Error}, nil
|
||||
}
|
||||
pageBytes += len(val.Value)
|
||||
rsp.Items = append(rsp.Items, &resourcepb.ResourceWrapper{
|
||||
Value: val.Value,
|
||||
ResourceVersion: val.ResourceVersion,
|
||||
})
|
||||
if (req.Limit > 0 && len(rsp.Items) >= int(req.Limit)) || pageBytes >= s.maxPageSizeBytes {
|
||||
token, err := NewSearchContinueToken(row.GetSortFields(), listRv)
|
||||
if err != nil {
|
||||
return &resourcepb.ListResponse{
|
||||
Error: NewBadRequestError("invalid continue token"),
|
||||
}, nil
|
||||
}
|
||||
rsp.NextPageToken = token
|
||||
return rsp, nil
|
||||
}
|
||||
}
|
||||
|
||||
return rsp, nil
|
||||
}
|
||||
|
||||
func filterFieldSelectors(req *resourcepb.ListRequest) *resourcepb.ListRequest {
|
||||
fields := make([]*resourcepb.Requirement, 0, len(req.Options.Fields))
|
||||
for _, f := range req.Options.Fields {
|
||||
if (f.Operator != "=" && f.Operator != "==") || f.Key == "metadata.namespace" {
|
||||
continue
|
||||
}
|
||||
fields = append(fields, f)
|
||||
}
|
||||
req.Options.Fields = fields
|
||||
|
||||
return req
|
||||
}
|
||||
|
||||
func (s *server) useFieldSelectorSearch(req *resourcepb.ListRequest) bool {
|
||||
if s.searchClient == nil || req.Source != resourcepb.ListRequest_STORE || len(req.Options.Fields) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
if req.VersionMatchV2 == resourcepb.ResourceVersionMatchV2_Exact || req.VersionMatchV2 == resourcepb.ResourceVersionMatchV2_NotOlderThan {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
434
pkg/storage/unified/resource/list_with_field_selectors_test.go
Normal file
434
pkg/storage/unified/resource/list_with_field_selectors_test.go
Normal file
|
|
@ -0,0 +1,434 @@
|
|||
package resource
|
||||
|
||||
import (
|
||||
"context"
|
||||
"iter"
|
||||
"net/http"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"google.golang.org/grpc"
|
||||
|
||||
claims "github.com/grafana/authlib/types"
|
||||
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resourcepb"
|
||||
"github.com/grafana/grafana/pkg/util/scheduler"
|
||||
)
|
||||
|
||||
func TestUseFieldSelectorSearch(t *testing.T) {
|
||||
tests := map[string]struct {
|
||||
disableSearch bool
|
||||
req *resourcepb.ListRequest
|
||||
expectedAllowed bool
|
||||
}{
|
||||
"false when no search client": {
|
||||
disableSearch: true,
|
||||
req: &resourcepb.ListRequest{
|
||||
Source: resourcepb.ListRequest_STORE,
|
||||
Options: &resourcepb.ListOptions{
|
||||
Key: &resourcepb.ResourceKey{Namespace: "ns"},
|
||||
Fields: []*resourcepb.Requirement{{Key: "spec.foo"}},
|
||||
},
|
||||
},
|
||||
expectedAllowed: false,
|
||||
},
|
||||
"false when source is not store": {
|
||||
req: &resourcepb.ListRequest{
|
||||
Source: resourcepb.ListRequest_HISTORY,
|
||||
Options: &resourcepb.ListOptions{
|
||||
Key: &resourcepb.ResourceKey{Namespace: "ns"},
|
||||
Fields: []*resourcepb.Requirement{{Key: "spec.foo"}},
|
||||
},
|
||||
},
|
||||
expectedAllowed: false,
|
||||
},
|
||||
"false when no field selectors": {
|
||||
req: &resourcepb.ListRequest{
|
||||
Source: resourcepb.ListRequest_STORE,
|
||||
Options: &resourcepb.ListOptions{
|
||||
Key: &resourcepb.ResourceKey{Namespace: "ns"},
|
||||
},
|
||||
},
|
||||
expectedAllowed: false,
|
||||
},
|
||||
"false when version match exact": {
|
||||
req: &resourcepb.ListRequest{
|
||||
Source: resourcepb.ListRequest_STORE,
|
||||
VersionMatchV2: resourcepb.ResourceVersionMatchV2_Exact,
|
||||
Options: &resourcepb.ListOptions{
|
||||
Key: &resourcepb.ResourceKey{Namespace: "ns"},
|
||||
Fields: []*resourcepb.Requirement{{Key: "spec.foo"}},
|
||||
},
|
||||
},
|
||||
expectedAllowed: false,
|
||||
},
|
||||
"false when version match not older than": {
|
||||
req: &resourcepb.ListRequest{
|
||||
Source: resourcepb.ListRequest_STORE,
|
||||
VersionMatchV2: resourcepb.ResourceVersionMatchV2_NotOlderThan,
|
||||
Options: &resourcepb.ListOptions{
|
||||
Key: &resourcepb.ResourceKey{Namespace: "ns"},
|
||||
Fields: []*resourcepb.Requirement{{Key: "spec.foo"}},
|
||||
},
|
||||
},
|
||||
expectedAllowed: false,
|
||||
},
|
||||
"true when store, fields, and search client": {
|
||||
req: &resourcepb.ListRequest{
|
||||
Source: resourcepb.ListRequest_STORE,
|
||||
Options: &resourcepb.ListOptions{
|
||||
Key: &resourcepb.ResourceKey{Namespace: "ns"},
|
||||
Fields: []*resourcepb.Requirement{{Key: "spec.foo"}},
|
||||
},
|
||||
},
|
||||
expectedAllowed: true,
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range tests {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
s := &server{}
|
||||
if !tc.disableSearch {
|
||||
s.searchClient = &stubSearchClient{}
|
||||
}
|
||||
|
||||
require.Equal(t, tc.expectedAllowed, s.useFieldSelectorSearch(tc.req))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFilterFieldSelectors(t *testing.T) {
|
||||
tests := map[string]struct {
|
||||
req *resourcepb.ListRequest
|
||||
wantFieldKeys []string
|
||||
}{
|
||||
"removes metadata.namespace and keep valid field": {
|
||||
req: &resourcepb.ListRequest{
|
||||
Options: &resourcepb.ListOptions{
|
||||
Key: &resourcepb.ResourceKey{Namespace: "ns"},
|
||||
Fields: []*resourcepb.Requirement{
|
||||
{Key: "metadata.namespace", Operator: "=", Values: []string{"ns"}},
|
||||
{Key: "spec.foo", Operator: "="},
|
||||
},
|
||||
},
|
||||
},
|
||||
wantFieldKeys: []string{"spec.foo"},
|
||||
},
|
||||
"removes multiple unsupported fields": {
|
||||
req: &resourcepb.ListRequest{
|
||||
Options: &resourcepb.ListOptions{
|
||||
Key: &resourcepb.ResourceKey{Namespace: "ns"},
|
||||
Fields: []*resourcepb.Requirement{
|
||||
{Key: "metadata.namespace", Operator: "=", Values: []string{"ns", "other"}},
|
||||
{Key: "spec.foo", Operator: "!="},
|
||||
},
|
||||
},
|
||||
},
|
||||
wantFieldKeys: []string{},
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range tests {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
out := filterFieldSelectors(tc.req)
|
||||
|
||||
gotKeys := make([]string, 0, len(out.Options.Fields))
|
||||
for _, f := range out.Options.Fields {
|
||||
gotKeys = append(gotKeys, f.Key)
|
||||
}
|
||||
require.Equal(t, tc.wantFieldKeys, gotKeys)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestListWithFieldSelectors(t *testing.T) {
|
||||
searchServerRv := int64(100)
|
||||
|
||||
t.Run("a single page result will have index rv and no next page token", func(t *testing.T) {
|
||||
ctx := identity.WithServiceIdentityContext(context.Background(), 1)
|
||||
searchClient := &stubSearchClient{
|
||||
resp: &resourcepb.ResourceSearchResponse{
|
||||
ResourceVersion: searchServerRv,
|
||||
Results: &resourcepb.ResourceTable{
|
||||
Rows: []*resourcepb.ResourceTableRow{
|
||||
{
|
||||
Key: &resourcepb.ResourceKey{Namespace: "ns", Group: "g", Resource: "r", Name: "a"},
|
||||
ResourceVersion: 1,
|
||||
SortFields: []string{"s1"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
s := createTestServer(searchClient, 1024)
|
||||
req := &resourcepb.ListRequest{
|
||||
Limit: 10,
|
||||
Options: &resourcepb.ListOptions{
|
||||
Key: &resourcepb.ResourceKey{Namespace: "ns"},
|
||||
Fields: []*resourcepb.Requirement{{Key: "spec.foo"}},
|
||||
},
|
||||
}
|
||||
|
||||
resp, err := s.listWithFieldSelectors(ctx, req)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, resp)
|
||||
require.Len(t, resp.Items, 1)
|
||||
require.Equal(t, searchServerRv, resp.ResourceVersion)
|
||||
require.Empty(t, resp.NextPageToken)
|
||||
})
|
||||
|
||||
t.Run("skips results when Read returns forbidden", func(t *testing.T) {
|
||||
ctx := identity.WithServiceIdentityContext(context.Background(), 1)
|
||||
searchClient := &stubSearchClient{
|
||||
resp: &resourcepb.ResourceSearchResponse{
|
||||
ResourceVersion: searchServerRv,
|
||||
Results: &resourcepb.ResourceTable{
|
||||
Rows: []*resourcepb.ResourceTableRow{
|
||||
{
|
||||
Key: &resourcepb.ResourceKey{Namespace: "ns", Group: "g", Resource: "r", Name: "a"},
|
||||
ResourceVersion: 1,
|
||||
SortFields: []string{"s1"},
|
||||
},
|
||||
{
|
||||
Key: &resourcepb.ResourceKey{Namespace: "ns", Group: "g", Resource: "r", Name: "b"},
|
||||
ResourceVersion: 2,
|
||||
SortFields: []string{"s2"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
s := &server{
|
||||
searchClient: searchClient,
|
||||
backend: &fakeBackend{forbidden: map[string]struct{}{"a": {}}},
|
||||
access: claims.FixedAccessClient(true),
|
||||
queue: scheduler.NewNoopQueue(),
|
||||
queueConfig: QueueConfig{Timeout: time.Second, MinBackoff: time.Millisecond, MaxBackoff: time.Millisecond, MaxRetries: 1},
|
||||
maxPageSizeBytes: 1024,
|
||||
}
|
||||
req := &resourcepb.ListRequest{
|
||||
Limit: 10,
|
||||
Options: &resourcepb.ListOptions{
|
||||
Key: &resourcepb.ResourceKey{Namespace: "ns"},
|
||||
Fields: []*resourcepb.Requirement{{Key: "spec.foo"}},
|
||||
},
|
||||
}
|
||||
|
||||
resp, err := s.listWithFieldSelectors(ctx, req)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, resp)
|
||||
require.Len(t, resp.Items, 1)
|
||||
require.Equal(t, searchServerRv, resp.ResourceVersion)
|
||||
})
|
||||
|
||||
t.Run("first page of paginated result will have next page token set and correct number of results", func(t *testing.T) {
|
||||
ctx := identity.WithServiceIdentityContext(context.Background(), 1)
|
||||
searchClient := &stubSearchClient{
|
||||
resp: &resourcepb.ResourceSearchResponse{
|
||||
ResourceVersion: searchServerRv,
|
||||
Results: &resourcepb.ResourceTable{
|
||||
Rows: []*resourcepb.ResourceTableRow{
|
||||
{
|
||||
Key: &resourcepb.ResourceKey{Namespace: "ns", Group: "g", Resource: "r", Name: "a"},
|
||||
ResourceVersion: 1,
|
||||
SortFields: []string{"s1"},
|
||||
},
|
||||
{
|
||||
Key: &resourcepb.ResourceKey{Namespace: "ns", Group: "g", Resource: "r", Name: "b"},
|
||||
ResourceVersion: 2,
|
||||
SortFields: []string{"s2"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
s := createTestServer(searchClient, 1024)
|
||||
req := &resourcepb.ListRequest{
|
||||
Limit: 1,
|
||||
Options: &resourcepb.ListOptions{
|
||||
Key: &resourcepb.ResourceKey{Namespace: "ns"},
|
||||
Fields: []*resourcepb.Requirement{{Key: "spec.foo"}},
|
||||
},
|
||||
}
|
||||
|
||||
resp, err := s.listWithFieldSelectors(ctx, req)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, resp)
|
||||
require.Equal(t, searchServerRv, resp.ResourceVersion)
|
||||
require.Len(t, resp.Items, 1)
|
||||
require.NotEmpty(t, resp.NextPageToken)
|
||||
token, err := GetContinueToken(resp.NextPageToken)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, token)
|
||||
require.Equal(t, []string{"s1"}, token.SearchAfter)
|
||||
require.Equal(t, searchServerRv, token.ResourceVersion)
|
||||
})
|
||||
|
||||
t.Run("can handle pagination when list request has a token present", func(t *testing.T) {
|
||||
ctx := identity.WithServiceIdentityContext(context.Background(), 1)
|
||||
continueToken, err := NewSearchContinueToken([]string{"s1"}, searchServerRv)
|
||||
require.NoError(t, err)
|
||||
|
||||
searchClient := &stubSearchClient{
|
||||
resp: &resourcepb.ResourceSearchResponse{
|
||||
ResourceVersion: searchServerRv,
|
||||
Results: &resourcepb.ResourceTable{
|
||||
Rows: []*resourcepb.ResourceTableRow{
|
||||
{
|
||||
Key: &resourcepb.ResourceKey{Namespace: "ns", Group: "g", Resource: "r", Name: "b"},
|
||||
ResourceVersion: 2,
|
||||
SortFields: []string{"s2"},
|
||||
},
|
||||
{
|
||||
Key: &resourcepb.ResourceKey{Namespace: "ns", Group: "g", Resource: "r", Name: "c"},
|
||||
ResourceVersion: 2,
|
||||
SortFields: []string{"s3"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
s := createTestServer(searchClient, 1024)
|
||||
req := &resourcepb.ListRequest{
|
||||
Limit: 1,
|
||||
NextPageToken: continueToken,
|
||||
Options: &resourcepb.ListOptions{
|
||||
Key: &resourcepb.ResourceKey{Namespace: "ns"},
|
||||
Fields: []*resourcepb.Requirement{{Key: "spec.foo"}},
|
||||
},
|
||||
}
|
||||
|
||||
resp, err := s.listWithFieldSelectors(ctx, req)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, resp)
|
||||
require.Equal(t, searchServerRv, resp.ResourceVersion)
|
||||
require.Len(t, resp.Items, 1)
|
||||
require.NotEmpty(t, resp.NextPageToken)
|
||||
|
||||
parsedToken, err := GetContinueToken(continueToken)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, searchClient.last)
|
||||
require.Equal(t, parsedToken.SearchAfter, searchClient.last.SearchAfter)
|
||||
require.Equal(t, parsedToken.SearchBefore, searchClient.last.SearchBefore)
|
||||
|
||||
token, err := GetContinueToken(resp.NextPageToken)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, token)
|
||||
require.Equal(t, []string{"s2"}, token.SearchAfter)
|
||||
require.Equal(t, searchServerRv, token.ResourceVersion)
|
||||
})
|
||||
|
||||
t.Run("will paginate when max page size bytes is reached", func(t *testing.T) {
|
||||
ctx := identity.WithServiceIdentityContext(context.Background(), 1)
|
||||
searchClient := &stubSearchClient{
|
||||
resp: &resourcepb.ResourceSearchResponse{
|
||||
ResourceVersion: searchServerRv,
|
||||
Results: &resourcepb.ResourceTable{
|
||||
Rows: []*resourcepb.ResourceTableRow{
|
||||
{
|
||||
Key: &resourcepb.ResourceKey{Namespace: "ns", Group: "g", Resource: "r", Name: "a"},
|
||||
ResourceVersion: 1,
|
||||
SortFields: []string{"s1"},
|
||||
},
|
||||
{
|
||||
Key: &resourcepb.ResourceKey{Namespace: "ns", Group: "g", Resource: "r", Name: "b"},
|
||||
ResourceVersion: 2,
|
||||
SortFields: []string{"s2"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
s := createTestServer(searchClient, 5)
|
||||
req := &resourcepb.ListRequest{
|
||||
Limit: 10,
|
||||
Options: &resourcepb.ListOptions{
|
||||
Key: &resourcepb.ResourceKey{Namespace: "ns"},
|
||||
Fields: []*resourcepb.Requirement{{Key: "spec.foo"}},
|
||||
},
|
||||
}
|
||||
|
||||
resp, err := s.listWithFieldSelectors(ctx, req)
|
||||
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, resp)
|
||||
require.Len(t, resp.Items, 1)
|
||||
require.Equal(t, searchServerRv, resp.ResourceVersion)
|
||||
require.NotEmpty(t, resp.NextPageToken)
|
||||
|
||||
parsedToken, err := GetContinueToken(resp.NextPageToken)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, []string{"s1"}, parsedToken.SearchAfter)
|
||||
require.Equal(t, searchServerRv, parsedToken.ResourceVersion)
|
||||
})
|
||||
}
|
||||
|
||||
func createTestServer(searchClient resourcepb.ResourceIndexClient, maxPageSizeBytes int) *server {
|
||||
return &server{
|
||||
searchClient: searchClient,
|
||||
backend: &fakeBackend{},
|
||||
access: claims.FixedAccessClient(true),
|
||||
queue: scheduler.NewNoopQueue(),
|
||||
queueConfig: QueueConfig{Timeout: time.Second, MinBackoff: time.Millisecond, MaxBackoff: time.Millisecond, MaxRetries: 1},
|
||||
maxPageSizeBytes: maxPageSizeBytes,
|
||||
}
|
||||
}
|
||||
|
||||
type stubSearchClient struct {
|
||||
resp *resourcepb.ResourceSearchResponse
|
||||
err error
|
||||
last *resourcepb.ResourceSearchRequest
|
||||
}
|
||||
|
||||
func (s *stubSearchClient) Search(_ context.Context, req *resourcepb.ResourceSearchRequest, _ ...grpc.CallOption) (*resourcepb.ResourceSearchResponse, error) {
|
||||
s.last = req
|
||||
return s.resp, s.err
|
||||
}
|
||||
|
||||
func (*stubSearchClient) GetStats(_ context.Context, _ *resourcepb.ResourceStatsRequest, _ ...grpc.CallOption) (*resourcepb.ResourceStatsResponse, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (*stubSearchClient) RebuildIndexes(_ context.Context, _ *resourcepb.RebuildIndexesRequest, _ ...grpc.CallOption) (*resourcepb.RebuildIndexesResponse, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
type fakeBackend struct {
|
||||
forbidden map[string]struct{}
|
||||
}
|
||||
|
||||
func (*fakeBackend) WriteEvent(context.Context, WriteEvent) (int64, error) { return 0, nil }
|
||||
func (b *fakeBackend) ReadResource(_ context.Context, req *resourcepb.ReadRequest) *BackendReadResponse {
|
||||
if b != nil && b.forbidden != nil {
|
||||
if _, ok := b.forbidden[req.Key.Name]; ok {
|
||||
return &BackendReadResponse{
|
||||
Key: req.Key,
|
||||
Error: &resourcepb.ErrorResult{Code: http.StatusForbidden},
|
||||
}
|
||||
}
|
||||
}
|
||||
return &BackendReadResponse{
|
||||
Key: req.Key,
|
||||
ResourceVersion: req.ResourceVersion,
|
||||
Value: []byte("value"),
|
||||
}
|
||||
}
|
||||
func (*fakeBackend) ListIterator(context.Context, *resourcepb.ListRequest, func(ListIterator) error) (int64, error) {
|
||||
return 0, nil
|
||||
}
|
||||
func (*fakeBackend) ListHistory(context.Context, *resourcepb.ListRequest, func(ListIterator) error) (int64, error) {
|
||||
return 0, nil
|
||||
}
|
||||
func (*fakeBackend) ListModifiedSince(context.Context, NamespacedResource, int64) (int64, iter.Seq2[*ModifiedResource, error]) {
|
||||
return 0, func(func(*ModifiedResource, error) bool) {}
|
||||
}
|
||||
func (*fakeBackend) WatchWriteEvents(context.Context) (<-chan *WrittenEvent, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (*fakeBackend) GetResourceStats(context.Context, NamespacedResource, int) ([]ResourceStats, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (*fakeBackend) GetResourceLastImportTimes(context.Context) iter.Seq2[ResourceLastImportTime, error] {
|
||||
return func(func(ResourceLastImportTime, error) bool) {}
|
||||
}
|
||||
|
|
@ -380,6 +380,7 @@ func NewResourceServer(opts ResourceServerOptions) (*server, error) {
|
|||
queueConfig: opts.QOSConfig,
|
||||
overridesService: opts.OverridesService,
|
||||
storageEnabled: true,
|
||||
searchClient: opts.SearchClient,
|
||||
|
||||
artificialSuccessfulWriteDelay: opts.Search.IndexMinUpdateInterval,
|
||||
}
|
||||
|
|
@ -432,6 +433,7 @@ type server struct {
|
|||
blob BlobSupport
|
||||
secure secrets.InlineSecureValueSupport
|
||||
search *searchServer
|
||||
searchClient resourcepb.ResourceIndexClient
|
||||
diagnostics resourcepb.DiagnosticsServer
|
||||
access claims.AccessClient
|
||||
writeHooks WriteAccessHooks
|
||||
|
|
@ -1101,10 +1103,16 @@ func (s *server) List(ctx context.Context, req *resourcepb.ListRequest) (*resour
|
|||
if req.Limit < 1 {
|
||||
req.Limit = 500 // default max 500 items in a page
|
||||
}
|
||||
maxPageBytes := s.maxPageSizeBytes
|
||||
pageBytes := 0
|
||||
rsp := &resourcepb.ListResponse{}
|
||||
|
||||
req = filterFieldSelectors(req)
|
||||
if s.useFieldSelectorSearch(req) {
|
||||
// If we get here, we're doing list with selectable fields. Let's do search instead, since
|
||||
// we index all selectable fields, and fetch resulting documents one by one.
|
||||
return s.listWithFieldSelectors(ctx, req)
|
||||
}
|
||||
|
||||
key := req.Options.Key
|
||||
//nolint:staticcheck // SA1019: Compile is deprecated but BatchCheck is not yet fully implemented
|
||||
checker, _, err := s.access.Compile(ctx, user, claims.ListRequest{
|
||||
|
|
@ -1157,7 +1165,7 @@ func (s *server) List(ctx context.Context, req *resourcepb.ListRequest) (*resour
|
|||
|
||||
pageBytes += len(item.Value)
|
||||
rsp.Items = append(rsp.Items, item)
|
||||
if (req.Limit > 0 && len(rsp.Items) >= int(req.Limit)) || pageBytes >= maxPageBytes {
|
||||
if (req.Limit > 0 && len(rsp.Items) >= int(req.Limit)) || pageBytes >= s.maxPageSizeBytes {
|
||||
t := iter.ContinueToken()
|
||||
if iter.Next() {
|
||||
rsp.NextPageToken = t
|
||||
|
|
|
|||
Loading…
Reference in a new issue