mirror of https://github.com/grafana/grafana.git
Alerting: Add feature toggle to use the old simplified routing hash generation (#111900)
Actionlint / Lint GitHub Actions files (push) Waiting to run
Details
Backend Code Checks / Detect whether code changed (push) Waiting to run
Details
Backend Code Checks / Validate Backend Configs (push) Blocked by required conditions
Details
Backend Unit Tests / Detect whether code changed (push) Waiting to run
Details
Backend Unit Tests / Grafana (${{ matrix.shard }}) (1/8) (push) Blocked by required conditions
Details
Backend Unit Tests / Grafana (${{ matrix.shard }}) (2/8) (push) Blocked by required conditions
Details
Backend Unit Tests / Grafana (${{ matrix.shard }}) (3/8) (push) Blocked by required conditions
Details
Backend Unit Tests / Grafana (${{ matrix.shard }}) (4/8) (push) Blocked by required conditions
Details
Backend Unit Tests / Grafana (${{ matrix.shard }}) (5/8) (push) Blocked by required conditions
Details
Backend Unit Tests / Grafana (${{ matrix.shard }}) (6/8) (push) Blocked by required conditions
Details
Backend Unit Tests / Grafana (${{ matrix.shard }}) (7/8) (push) Blocked by required conditions
Details
Backend Unit Tests / Grafana (${{ matrix.shard }}) (8/8) (push) Blocked by required conditions
Details
Backend Unit Tests / Grafana Enterprise (${{ matrix.shard }}) (1/8) (push) Blocked by required conditions
Details
Backend Unit Tests / Grafana Enterprise (${{ matrix.shard }}) (2/8) (push) Blocked by required conditions
Details
Backend Unit Tests / Grafana Enterprise (${{ matrix.shard }}) (3/8) (push) Blocked by required conditions
Details
Backend Unit Tests / Grafana Enterprise (${{ matrix.shard }}) (4/8) (push) Blocked by required conditions
Details
Backend Unit Tests / Grafana Enterprise (${{ matrix.shard }}) (5/8) (push) Blocked by required conditions
Details
Backend Unit Tests / Grafana Enterprise (${{ matrix.shard }}) (6/8) (push) Blocked by required conditions
Details
Backend Unit Tests / Grafana Enterprise (${{ matrix.shard }}) (7/8) (push) Blocked by required conditions
Details
Backend Unit Tests / Grafana Enterprise (${{ matrix.shard }}) (8/8) (push) Blocked by required conditions
Details
Backend Unit Tests / All backend unit tests complete (push) Blocked by required conditions
Details
CodeQL checks / Detect whether code changed (push) Waiting to run
Details
CodeQL checks / Analyze (actions) (push) Blocked by required conditions
Details
CodeQL checks / Analyze (go) (push) Blocked by required conditions
Details
CodeQL checks / Analyze (javascript) (push) Blocked by required conditions
Details
Lint Frontend / Detect whether code changed (push) Waiting to run
Details
Lint Frontend / Lint (push) Blocked by required conditions
Details
Lint Frontend / Typecheck (push) Blocked by required conditions
Details
Lint Frontend / Verify API clients (push) Waiting to run
Details
Lint Frontend / Verify API clients (enterprise) (push) Waiting to run
Details
golangci-lint / Detect whether code changed (push) Waiting to run
Details
golangci-lint / go-fmt (push) Blocked by required conditions
Details
golangci-lint / lint-go (push) Blocked by required conditions
Details
Verify i18n / verify-i18n (push) Waiting to run
Details
End-to-end tests / Detect whether code changed (push) Waiting to run
Details
End-to-end tests / Build & Package Grafana (push) Blocked by required conditions
Details
End-to-end tests / Build E2E test runner (push) Blocked by required conditions
Details
End-to-end tests / push-docker-image (push) Blocked by required conditions
Details
End-to-end tests / ${{ matrix.suite }} (--flags="--env dashboardScene=false", e2e/old-arch/dashboards-suite, dashboards-suite (old arch)) (push) Blocked by required conditions
Details
End-to-end tests / ${{ matrix.suite }} (--flags="--env dashboardScene=false", e2e/old-arch/panels-suite, panels-suite (old arch)) (push) Blocked by required conditions
Details
End-to-end tests / ${{ matrix.suite }} (--flags="--env dashboardScene=false", e2e/old-arch/smoke-tests-suite, smoke-tests-suite (old arch)) (push) Blocked by required conditions
Details
End-to-end tests / ${{ matrix.suite }} (--flags="--env dashboardScene=false", e2e/old-arch/various-suite, various-suite (old arch)) (push) Blocked by required conditions
Details
End-to-end tests / Verify Storybook (Playwright) (push) Blocked by required conditions
Details
End-to-end tests / Playwright E2E tests (${{ matrix.shard }}/${{ matrix.shardTotal }}) (1, 8) (push) Blocked by required conditions
Details
End-to-end tests / Playwright E2E tests (${{ matrix.shard }}/${{ matrix.shardTotal }}) (2, 8) (push) Blocked by required conditions
Details
End-to-end tests / Playwright E2E tests (${{ matrix.shard }}/${{ matrix.shardTotal }}) (3, 8) (push) Blocked by required conditions
Details
End-to-end tests / Playwright E2E tests (${{ matrix.shard }}/${{ matrix.shardTotal }}) (4, 8) (push) Blocked by required conditions
Details
End-to-end tests / Playwright E2E tests (${{ matrix.shard }}/${{ matrix.shardTotal }}) (5, 8) (push) Blocked by required conditions
Details
End-to-end tests / Playwright E2E tests (${{ matrix.shard }}/${{ matrix.shardTotal }}) (6, 8) (push) Blocked by required conditions
Details
End-to-end tests / Playwright E2E tests (${{ matrix.shard }}/${{ matrix.shardTotal }}) (7, 8) (push) Blocked by required conditions
Details
End-to-end tests / Playwright E2E tests (${{ matrix.shard }}/${{ matrix.shardTotal }}) (8, 8) (push) Blocked by required conditions
Details
End-to-end tests / run-azure-monitor-e2e (push) Blocked by required conditions
Details
End-to-end tests / All Playwright tests complete (push) Blocked by required conditions
Details
End-to-end tests / A11y test (push) Blocked by required conditions
Details
End-to-end tests / Publish metrics (push) Blocked by required conditions
Details
End-to-end tests / All E2E tests complete (push) Blocked by required conditions
Details
Frontend tests / Detect whether code changed (push) Waiting to run
Details
Frontend tests / Unit tests (${{ matrix.shard }} / ${{ matrix.total }}) (1, 16) (push) Blocked by required conditions
Details
Frontend tests / Unit tests (${{ matrix.shard }} / ${{ matrix.total }}) (10, 16) (push) Blocked by required conditions
Details
Frontend tests / Unit tests (${{ matrix.shard }} / ${{ matrix.total }}) (11, 16) (push) Blocked by required conditions
Details
Frontend tests / Unit tests (${{ matrix.shard }} / ${{ matrix.total }}) (12, 16) (push) Blocked by required conditions
Details
Frontend tests / Unit tests (${{ matrix.shard }} / ${{ matrix.total }}) (13, 16) (push) Blocked by required conditions
Details
Frontend tests / Unit tests (${{ matrix.shard }} / ${{ matrix.total }}) (14, 16) (push) Blocked by required conditions
Details
Frontend tests / Unit tests (${{ matrix.shard }} / ${{ matrix.total }}) (15, 16) (push) Blocked by required conditions
Details
Frontend tests / Unit tests (${{ matrix.shard }} / ${{ matrix.total }}) (16, 16) (push) Blocked by required conditions
Details
Frontend tests / Unit tests (${{ matrix.shard }} / ${{ matrix.total }}) (2, 16) (push) Blocked by required conditions
Details
Frontend tests / Unit tests (${{ matrix.shard }} / ${{ matrix.total }}) (3, 16) (push) Blocked by required conditions
Details
Frontend tests / Unit tests (${{ matrix.shard }} / ${{ matrix.total }}) (4, 16) (push) Blocked by required conditions
Details
Frontend tests / Unit tests (${{ matrix.shard }} / ${{ matrix.total }}) (5, 16) (push) Blocked by required conditions
Details
Frontend tests / Unit tests (${{ matrix.shard }} / ${{ matrix.total }}) (6, 16) (push) Blocked by required conditions
Details
Frontend tests / Unit tests (${{ matrix.shard }} / ${{ matrix.total }}) (7, 16) (push) Blocked by required conditions
Details
Frontend tests / Unit tests (${{ matrix.shard }} / ${{ matrix.total }}) (8, 16) (push) Blocked by required conditions
Details
Frontend tests / Unit tests (${{ matrix.shard }} / ${{ matrix.total }}) (9, 16) (push) Blocked by required conditions
Details
Frontend tests / Decoupled plugin tests (push) Blocked by required conditions
Details
Frontend tests / Packages unit tests (push) Blocked by required conditions
Details
Frontend tests / All frontend unit tests complete (push) Blocked by required conditions
Details
Integration Tests / Detect whether code changed (push) Waiting to run
Details
Integration Tests / Sqlite (${{ matrix.shard }}) (1/4) (push) Blocked by required conditions
Details
Integration Tests / Sqlite (${{ matrix.shard }}) (2/4) (push) Blocked by required conditions
Details
Integration Tests / Sqlite (${{ matrix.shard }}) (3/4) (push) Blocked by required conditions
Details
Integration Tests / Sqlite (${{ matrix.shard }}) (4/4) (push) Blocked by required conditions
Details
Integration Tests / Sqlite Without CGo (${{ matrix.shard }}) (1/4) (push) Blocked by required conditions
Details
Integration Tests / Sqlite Without CGo (${{ matrix.shard }}) (2/4) (push) Blocked by required conditions
Details
Integration Tests / Sqlite Without CGo (${{ matrix.shard }}) (3/4) (push) Blocked by required conditions
Details
Integration Tests / Sqlite Without CGo (${{ matrix.shard }}) (4/4) (push) Blocked by required conditions
Details
Integration Tests / MySQL (${{ matrix.shard }}) (1/16) (push) Blocked by required conditions
Details
Integration Tests / MySQL (${{ matrix.shard }}) (10/16) (push) Blocked by required conditions
Details
Integration Tests / MySQL (${{ matrix.shard }}) (11/16) (push) Blocked by required conditions
Details
Integration Tests / MySQL (${{ matrix.shard }}) (12/16) (push) Blocked by required conditions
Details
Integration Tests / MySQL (${{ matrix.shard }}) (13/16) (push) Blocked by required conditions
Details
Integration Tests / MySQL (${{ matrix.shard }}) (14/16) (push) Blocked by required conditions
Details
Integration Tests / MySQL (${{ matrix.shard }}) (15/16) (push) Blocked by required conditions
Details
Integration Tests / MySQL (${{ matrix.shard }}) (16/16) (push) Blocked by required conditions
Details
Integration Tests / MySQL (${{ matrix.shard }}) (2/16) (push) Blocked by required conditions
Details
Integration Tests / MySQL (${{ matrix.shard }}) (3/16) (push) Blocked by required conditions
Details
Integration Tests / MySQL (${{ matrix.shard }}) (4/16) (push) Blocked by required conditions
Details
Integration Tests / MySQL (${{ matrix.shard }}) (5/16) (push) Blocked by required conditions
Details
Integration Tests / MySQL (${{ matrix.shard }}) (6/16) (push) Blocked by required conditions
Details
Integration Tests / MySQL (${{ matrix.shard }}) (7/16) (push) Blocked by required conditions
Details
Integration Tests / MySQL (${{ matrix.shard }}) (8/16) (push) Blocked by required conditions
Details
Integration Tests / MySQL (${{ matrix.shard }}) (9/16) (push) Blocked by required conditions
Details
Integration Tests / Postgres (${{ matrix.shard }}) (1/16) (push) Blocked by required conditions
Details
Integration Tests / Postgres (${{ matrix.shard }}) (10/16) (push) Blocked by required conditions
Details
Integration Tests / Postgres (${{ matrix.shard }}) (11/16) (push) Blocked by required conditions
Details
Integration Tests / Postgres (${{ matrix.shard }}) (12/16) (push) Blocked by required conditions
Details
Integration Tests / Postgres (${{ matrix.shard }}) (13/16) (push) Blocked by required conditions
Details
Integration Tests / Postgres (${{ matrix.shard }}) (14/16) (push) Blocked by required conditions
Details
Integration Tests / Postgres (${{ matrix.shard }}) (15/16) (push) Blocked by required conditions
Details
Integration Tests / Postgres (${{ matrix.shard }}) (16/16) (push) Blocked by required conditions
Details
Integration Tests / Postgres (${{ matrix.shard }}) (2/16) (push) Blocked by required conditions
Details
Integration Tests / Postgres (${{ matrix.shard }}) (3/16) (push) Blocked by required conditions
Details
Integration Tests / Postgres (${{ matrix.shard }}) (4/16) (push) Blocked by required conditions
Details
Integration Tests / Postgres (${{ matrix.shard }}) (5/16) (push) Blocked by required conditions
Details
Integration Tests / Postgres (${{ matrix.shard }}) (6/16) (push) Blocked by required conditions
Details
Integration Tests / Postgres (${{ matrix.shard }}) (7/16) (push) Blocked by required conditions
Details
Integration Tests / Postgres (${{ matrix.shard }}) (8/16) (push) Blocked by required conditions
Details
Integration Tests / Postgres (${{ matrix.shard }}) (9/16) (push) Blocked by required conditions
Details
Integration Tests / All backend integration tests complete (push) Blocked by required conditions
Details
Reject GitHub secrets / reject-gh-secrets (push) Waiting to run
Details
Run dashboard schema v2 e2e / dashboard-schema-v2-e2e (push) Waiting to run
Details
Shellcheck / Shellcheck scripts (push) Waiting to run
Details
Run Storybook a11y tests / Detect whether code changed (push) Waiting to run
Details
Run Storybook a11y tests / Run Storybook a11y tests (push) Blocked by required conditions
Details
Swagger generated code / Detect whether code changed (push) Waiting to run
Details
Swagger generated code / Verify committed API specs match (push) Blocked by required conditions
Details
Dispatch sync to mirror / dispatch-job (push) Waiting to run
Details
trigger-dashboard-search-e2e / trigger-search-e2e (push) Waiting to run
Details
Crowdin Upload Action / upload-sources-to-crowdin (push) Has been cancelled
Details
Documentation / Build & Verify Docs (push) Has been cancelled
Details
publish-technical-documentation-next / sync (push) Has been cancelled
Details
Trivy Scan / trivy-scan (push) Has been cancelled
Details
Actionlint / Lint GitHub Actions files (push) Waiting to run
Details
Backend Code Checks / Detect whether code changed (push) Waiting to run
Details
Backend Code Checks / Validate Backend Configs (push) Blocked by required conditions
Details
Backend Unit Tests / Detect whether code changed (push) Waiting to run
Details
Backend Unit Tests / Grafana (${{ matrix.shard }}) (1/8) (push) Blocked by required conditions
Details
Backend Unit Tests / Grafana (${{ matrix.shard }}) (2/8) (push) Blocked by required conditions
Details
Backend Unit Tests / Grafana (${{ matrix.shard }}) (3/8) (push) Blocked by required conditions
Details
Backend Unit Tests / Grafana (${{ matrix.shard }}) (4/8) (push) Blocked by required conditions
Details
Backend Unit Tests / Grafana (${{ matrix.shard }}) (5/8) (push) Blocked by required conditions
Details
Backend Unit Tests / Grafana (${{ matrix.shard }}) (6/8) (push) Blocked by required conditions
Details
Backend Unit Tests / Grafana (${{ matrix.shard }}) (7/8) (push) Blocked by required conditions
Details
Backend Unit Tests / Grafana (${{ matrix.shard }}) (8/8) (push) Blocked by required conditions
Details
Backend Unit Tests / Grafana Enterprise (${{ matrix.shard }}) (1/8) (push) Blocked by required conditions
Details
Backend Unit Tests / Grafana Enterprise (${{ matrix.shard }}) (2/8) (push) Blocked by required conditions
Details
Backend Unit Tests / Grafana Enterprise (${{ matrix.shard }}) (3/8) (push) Blocked by required conditions
Details
Backend Unit Tests / Grafana Enterprise (${{ matrix.shard }}) (4/8) (push) Blocked by required conditions
Details
Backend Unit Tests / Grafana Enterprise (${{ matrix.shard }}) (5/8) (push) Blocked by required conditions
Details
Backend Unit Tests / Grafana Enterprise (${{ matrix.shard }}) (6/8) (push) Blocked by required conditions
Details
Backend Unit Tests / Grafana Enterprise (${{ matrix.shard }}) (7/8) (push) Blocked by required conditions
Details
Backend Unit Tests / Grafana Enterprise (${{ matrix.shard }}) (8/8) (push) Blocked by required conditions
Details
Backend Unit Tests / All backend unit tests complete (push) Blocked by required conditions
Details
CodeQL checks / Detect whether code changed (push) Waiting to run
Details
CodeQL checks / Analyze (actions) (push) Blocked by required conditions
Details
CodeQL checks / Analyze (go) (push) Blocked by required conditions
Details
CodeQL checks / Analyze (javascript) (push) Blocked by required conditions
Details
Lint Frontend / Detect whether code changed (push) Waiting to run
Details
Lint Frontend / Lint (push) Blocked by required conditions
Details
Lint Frontend / Typecheck (push) Blocked by required conditions
Details
Lint Frontend / Verify API clients (push) Waiting to run
Details
Lint Frontend / Verify API clients (enterprise) (push) Waiting to run
Details
golangci-lint / Detect whether code changed (push) Waiting to run
Details
golangci-lint / go-fmt (push) Blocked by required conditions
Details
golangci-lint / lint-go (push) Blocked by required conditions
Details
Verify i18n / verify-i18n (push) Waiting to run
Details
End-to-end tests / Detect whether code changed (push) Waiting to run
Details
End-to-end tests / Build & Package Grafana (push) Blocked by required conditions
Details
End-to-end tests / Build E2E test runner (push) Blocked by required conditions
Details
End-to-end tests / push-docker-image (push) Blocked by required conditions
Details
End-to-end tests / ${{ matrix.suite }} (--flags="--env dashboardScene=false", e2e/old-arch/dashboards-suite, dashboards-suite (old arch)) (push) Blocked by required conditions
Details
End-to-end tests / ${{ matrix.suite }} (--flags="--env dashboardScene=false", e2e/old-arch/panels-suite, panels-suite (old arch)) (push) Blocked by required conditions
Details
End-to-end tests / ${{ matrix.suite }} (--flags="--env dashboardScene=false", e2e/old-arch/smoke-tests-suite, smoke-tests-suite (old arch)) (push) Blocked by required conditions
Details
End-to-end tests / ${{ matrix.suite }} (--flags="--env dashboardScene=false", e2e/old-arch/various-suite, various-suite (old arch)) (push) Blocked by required conditions
Details
End-to-end tests / Verify Storybook (Playwright) (push) Blocked by required conditions
Details
End-to-end tests / Playwright E2E tests (${{ matrix.shard }}/${{ matrix.shardTotal }}) (1, 8) (push) Blocked by required conditions
Details
End-to-end tests / Playwright E2E tests (${{ matrix.shard }}/${{ matrix.shardTotal }}) (2, 8) (push) Blocked by required conditions
Details
End-to-end tests / Playwright E2E tests (${{ matrix.shard }}/${{ matrix.shardTotal }}) (3, 8) (push) Blocked by required conditions
Details
End-to-end tests / Playwright E2E tests (${{ matrix.shard }}/${{ matrix.shardTotal }}) (4, 8) (push) Blocked by required conditions
Details
End-to-end tests / Playwright E2E tests (${{ matrix.shard }}/${{ matrix.shardTotal }}) (5, 8) (push) Blocked by required conditions
Details
End-to-end tests / Playwright E2E tests (${{ matrix.shard }}/${{ matrix.shardTotal }}) (6, 8) (push) Blocked by required conditions
Details
End-to-end tests / Playwright E2E tests (${{ matrix.shard }}/${{ matrix.shardTotal }}) (7, 8) (push) Blocked by required conditions
Details
End-to-end tests / Playwright E2E tests (${{ matrix.shard }}/${{ matrix.shardTotal }}) (8, 8) (push) Blocked by required conditions
Details
End-to-end tests / run-azure-monitor-e2e (push) Blocked by required conditions
Details
End-to-end tests / All Playwright tests complete (push) Blocked by required conditions
Details
End-to-end tests / A11y test (push) Blocked by required conditions
Details
End-to-end tests / Publish metrics (push) Blocked by required conditions
Details
End-to-end tests / All E2E tests complete (push) Blocked by required conditions
Details
Frontend tests / Detect whether code changed (push) Waiting to run
Details
Frontend tests / Unit tests (${{ matrix.shard }} / ${{ matrix.total }}) (1, 16) (push) Blocked by required conditions
Details
Frontend tests / Unit tests (${{ matrix.shard }} / ${{ matrix.total }}) (10, 16) (push) Blocked by required conditions
Details
Frontend tests / Unit tests (${{ matrix.shard }} / ${{ matrix.total }}) (11, 16) (push) Blocked by required conditions
Details
Frontend tests / Unit tests (${{ matrix.shard }} / ${{ matrix.total }}) (12, 16) (push) Blocked by required conditions
Details
Frontend tests / Unit tests (${{ matrix.shard }} / ${{ matrix.total }}) (13, 16) (push) Blocked by required conditions
Details
Frontend tests / Unit tests (${{ matrix.shard }} / ${{ matrix.total }}) (14, 16) (push) Blocked by required conditions
Details
Frontend tests / Unit tests (${{ matrix.shard }} / ${{ matrix.total }}) (15, 16) (push) Blocked by required conditions
Details
Frontend tests / Unit tests (${{ matrix.shard }} / ${{ matrix.total }}) (16, 16) (push) Blocked by required conditions
Details
Frontend tests / Unit tests (${{ matrix.shard }} / ${{ matrix.total }}) (2, 16) (push) Blocked by required conditions
Details
Frontend tests / Unit tests (${{ matrix.shard }} / ${{ matrix.total }}) (3, 16) (push) Blocked by required conditions
Details
Frontend tests / Unit tests (${{ matrix.shard }} / ${{ matrix.total }}) (4, 16) (push) Blocked by required conditions
Details
Frontend tests / Unit tests (${{ matrix.shard }} / ${{ matrix.total }}) (5, 16) (push) Blocked by required conditions
Details
Frontend tests / Unit tests (${{ matrix.shard }} / ${{ matrix.total }}) (6, 16) (push) Blocked by required conditions
Details
Frontend tests / Unit tests (${{ matrix.shard }} / ${{ matrix.total }}) (7, 16) (push) Blocked by required conditions
Details
Frontend tests / Unit tests (${{ matrix.shard }} / ${{ matrix.total }}) (8, 16) (push) Blocked by required conditions
Details
Frontend tests / Unit tests (${{ matrix.shard }} / ${{ matrix.total }}) (9, 16) (push) Blocked by required conditions
Details
Frontend tests / Decoupled plugin tests (push) Blocked by required conditions
Details
Frontend tests / Packages unit tests (push) Blocked by required conditions
Details
Frontend tests / All frontend unit tests complete (push) Blocked by required conditions
Details
Integration Tests / Detect whether code changed (push) Waiting to run
Details
Integration Tests / Sqlite (${{ matrix.shard }}) (1/4) (push) Blocked by required conditions
Details
Integration Tests / Sqlite (${{ matrix.shard }}) (2/4) (push) Blocked by required conditions
Details
Integration Tests / Sqlite (${{ matrix.shard }}) (3/4) (push) Blocked by required conditions
Details
Integration Tests / Sqlite (${{ matrix.shard }}) (4/4) (push) Blocked by required conditions
Details
Integration Tests / Sqlite Without CGo (${{ matrix.shard }}) (1/4) (push) Blocked by required conditions
Details
Integration Tests / Sqlite Without CGo (${{ matrix.shard }}) (2/4) (push) Blocked by required conditions
Details
Integration Tests / Sqlite Without CGo (${{ matrix.shard }}) (3/4) (push) Blocked by required conditions
Details
Integration Tests / Sqlite Without CGo (${{ matrix.shard }}) (4/4) (push) Blocked by required conditions
Details
Integration Tests / MySQL (${{ matrix.shard }}) (1/16) (push) Blocked by required conditions
Details
Integration Tests / MySQL (${{ matrix.shard }}) (10/16) (push) Blocked by required conditions
Details
Integration Tests / MySQL (${{ matrix.shard }}) (11/16) (push) Blocked by required conditions
Details
Integration Tests / MySQL (${{ matrix.shard }}) (12/16) (push) Blocked by required conditions
Details
Integration Tests / MySQL (${{ matrix.shard }}) (13/16) (push) Blocked by required conditions
Details
Integration Tests / MySQL (${{ matrix.shard }}) (14/16) (push) Blocked by required conditions
Details
Integration Tests / MySQL (${{ matrix.shard }}) (15/16) (push) Blocked by required conditions
Details
Integration Tests / MySQL (${{ matrix.shard }}) (16/16) (push) Blocked by required conditions
Details
Integration Tests / MySQL (${{ matrix.shard }}) (2/16) (push) Blocked by required conditions
Details
Integration Tests / MySQL (${{ matrix.shard }}) (3/16) (push) Blocked by required conditions
Details
Integration Tests / MySQL (${{ matrix.shard }}) (4/16) (push) Blocked by required conditions
Details
Integration Tests / MySQL (${{ matrix.shard }}) (5/16) (push) Blocked by required conditions
Details
Integration Tests / MySQL (${{ matrix.shard }}) (6/16) (push) Blocked by required conditions
Details
Integration Tests / MySQL (${{ matrix.shard }}) (7/16) (push) Blocked by required conditions
Details
Integration Tests / MySQL (${{ matrix.shard }}) (8/16) (push) Blocked by required conditions
Details
Integration Tests / MySQL (${{ matrix.shard }}) (9/16) (push) Blocked by required conditions
Details
Integration Tests / Postgres (${{ matrix.shard }}) (1/16) (push) Blocked by required conditions
Details
Integration Tests / Postgres (${{ matrix.shard }}) (10/16) (push) Blocked by required conditions
Details
Integration Tests / Postgres (${{ matrix.shard }}) (11/16) (push) Blocked by required conditions
Details
Integration Tests / Postgres (${{ matrix.shard }}) (12/16) (push) Blocked by required conditions
Details
Integration Tests / Postgres (${{ matrix.shard }}) (13/16) (push) Blocked by required conditions
Details
Integration Tests / Postgres (${{ matrix.shard }}) (14/16) (push) Blocked by required conditions
Details
Integration Tests / Postgres (${{ matrix.shard }}) (15/16) (push) Blocked by required conditions
Details
Integration Tests / Postgres (${{ matrix.shard }}) (16/16) (push) Blocked by required conditions
Details
Integration Tests / Postgres (${{ matrix.shard }}) (2/16) (push) Blocked by required conditions
Details
Integration Tests / Postgres (${{ matrix.shard }}) (3/16) (push) Blocked by required conditions
Details
Integration Tests / Postgres (${{ matrix.shard }}) (4/16) (push) Blocked by required conditions
Details
Integration Tests / Postgres (${{ matrix.shard }}) (5/16) (push) Blocked by required conditions
Details
Integration Tests / Postgres (${{ matrix.shard }}) (6/16) (push) Blocked by required conditions
Details
Integration Tests / Postgres (${{ matrix.shard }}) (7/16) (push) Blocked by required conditions
Details
Integration Tests / Postgres (${{ matrix.shard }}) (8/16) (push) Blocked by required conditions
Details
Integration Tests / Postgres (${{ matrix.shard }}) (9/16) (push) Blocked by required conditions
Details
Integration Tests / All backend integration tests complete (push) Blocked by required conditions
Details
Reject GitHub secrets / reject-gh-secrets (push) Waiting to run
Details
Run dashboard schema v2 e2e / dashboard-schema-v2-e2e (push) Waiting to run
Details
Shellcheck / Shellcheck scripts (push) Waiting to run
Details
Run Storybook a11y tests / Detect whether code changed (push) Waiting to run
Details
Run Storybook a11y tests / Run Storybook a11y tests (push) Blocked by required conditions
Details
Swagger generated code / Detect whether code changed (push) Waiting to run
Details
Swagger generated code / Verify committed API specs match (push) Blocked by required conditions
Details
Dispatch sync to mirror / dispatch-job (push) Waiting to run
Details
trigger-dashboard-search-e2e / trigger-search-e2e (push) Waiting to run
Details
Crowdin Upload Action / upload-sources-to-crowdin (push) Has been cancelled
Details
Documentation / Build & Verify Docs (push) Has been cancelled
Details
publish-technical-documentation-next / sync (push) Has been cancelled
Details
Trivy Scan / trivy-scan (push) Has been cancelled
Details
* Revert "Alerting: Generate simplified routing routes with old fingerprint function (#111893)"
This reverts commit 0da9d49896
.
* Add alertingUseNewSimplifiedRoutingHashAlgorithm flag
* Alerting: Add feature toggle to use the old simplified routing hash generation
This commit is contained in:
parent
4e2c3f19df
commit
169bf2ce73
|
@ -885,6 +885,11 @@ export interface FeatureToggles {
|
|||
*/
|
||||
alertingJiraIntegration?: boolean;
|
||||
/**
|
||||
*
|
||||
* @default true
|
||||
*/
|
||||
alertingUseNewSimplifiedRoutingHashAlgorithm?: boolean;
|
||||
/**
|
||||
* Use the scopes navigation endpoint instead of the dashboardbindings endpoint
|
||||
*/
|
||||
useScopesNavigationEndpoint?: boolean;
|
||||
|
@ -1009,11 +1014,6 @@ export interface FeatureToggles {
|
|||
*/
|
||||
alertRuleUseFiredAtForStartsAt?: boolean;
|
||||
/**
|
||||
* Generate simplified routing with old hashes
|
||||
* @default false
|
||||
*/
|
||||
alertingGenerateSimplifiedRoutingWithOldHashes?: boolean;
|
||||
/**
|
||||
* Enables the alerting bulk actions in the UI
|
||||
* @default true
|
||||
*/
|
||||
|
|
|
@ -1520,6 +1520,16 @@ var (
|
|||
FrontendOnly: true,
|
||||
HideFromDocs: true,
|
||||
},
|
||||
{
|
||||
Name: "alertingUseNewSimplifiedRoutingHashAlgorithm",
|
||||
Description: "",
|
||||
Stage: FeatureStagePublicPreview,
|
||||
Owner: grafanaAlertingSquad,
|
||||
HideFromAdminPage: true,
|
||||
HideFromDocs: true,
|
||||
RequiresRestart: true,
|
||||
Expression: "true",
|
||||
},
|
||||
{
|
||||
Name: "useScopesNavigationEndpoint",
|
||||
Description: "Use the scopes navigation endpoint instead of the dashboardbindings endpoint",
|
||||
|
@ -1739,13 +1749,6 @@ var (
|
|||
Owner: grafanaAlertingSquad,
|
||||
Expression: "false",
|
||||
},
|
||||
{
|
||||
Name: "alertingGenerateSimplifiedRoutingWithOldHashes",
|
||||
Description: "Generate simplified routing with old hashes",
|
||||
Stage: FeatureStageExperimental,
|
||||
Owner: grafanaAlertingSquad,
|
||||
Expression: "false",
|
||||
},
|
||||
{
|
||||
Name: "alertingBulkActionsInUI",
|
||||
Description: "Enables the alerting bulk actions in the UI",
|
||||
|
|
|
@ -198,6 +198,7 @@ fetchRulesUsingPost,experimental,@grafana/alerting-squad,false,false,false
|
|||
newLogsPanel,experimental,@grafana/observability-logs,false,false,true
|
||||
grafanaconThemes,GA,@grafana/grafana-frontend-platform,false,true,false
|
||||
alertingJiraIntegration,experimental,@grafana/alerting-squad,false,false,true
|
||||
alertingUseNewSimplifiedRoutingHashAlgorithm,preview,@grafana/alerting-squad,false,true,false
|
||||
useScopesNavigationEndpoint,experimental,@grafana/grafana-frontend-platform,false,false,true
|
||||
scopeSearchAllLevels,experimental,@grafana/grafana-frontend-platform,false,false,false
|
||||
alertingRuleVersionHistoryRestore,GA,@grafana/alerting-squad,false,false,true
|
||||
|
@ -226,7 +227,6 @@ tempoAlerting,experimental,@grafana/observability-traces-and-profiling,false,fal
|
|||
pluginsAutoUpdate,experimental,@grafana/plugins-platform-backend,false,false,false
|
||||
alertingListViewV2PreviewToggle,privatePreview,@grafana/alerting-squad,false,false,true
|
||||
alertRuleUseFiredAtForStartsAt,experimental,@grafana/alerting-squad,false,false,false
|
||||
alertingGenerateSimplifiedRoutingWithOldHashes,experimental,@grafana/alerting-squad,false,false,false
|
||||
alertingBulkActionsInUI,GA,@grafana/alerting-squad,false,false,true
|
||||
kubernetesAuthzApis,experimental,@grafana/identity-access-team,false,false,false
|
||||
kubernetesAuthZHandlerRedirect,experimental,@grafana/identity-access-team,false,false,false
|
||||
|
|
|
|
@ -803,6 +803,9 @@ const (
|
|||
// Enables the new Jira integration for contact points in cloud alert managers.
|
||||
FlagAlertingJiraIntegration = "alertingJiraIntegration"
|
||||
|
||||
// FlagAlertingUseNewSimplifiedRoutingHashAlgorithm
|
||||
FlagAlertingUseNewSimplifiedRoutingHashAlgorithm = "alertingUseNewSimplifiedRoutingHashAlgorithm"
|
||||
|
||||
// FlagUseScopesNavigationEndpoint
|
||||
// Use the scopes navigation endpoint instead of the dashboardbindings endpoint
|
||||
FlagUseScopesNavigationEndpoint = "useScopesNavigationEndpoint"
|
||||
|
@ -915,10 +918,6 @@ const (
|
|||
// Use FiredAt for StartsAt when sending alerts to Alertmaanger
|
||||
FlagAlertRuleUseFiredAtForStartsAt = "alertRuleUseFiredAtForStartsAt"
|
||||
|
||||
// FlagAlertingGenerateSimplifiedRoutingWithOldHashes
|
||||
// Generate simplified routing with old hashes
|
||||
FlagAlertingGenerateSimplifiedRoutingWithOldHashes = "alertingGenerateSimplifiedRoutingWithOldHashes"
|
||||
|
||||
// FlagAlertingBulkActionsInUI
|
||||
// Enables the alerting bulk actions in the UI
|
||||
FlagAlertingBulkActionsInUI = "alertingBulkActionsInUI"
|
||||
|
|
|
@ -307,19 +307,6 @@
|
|||
"hideFromDocs": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"metadata": {
|
||||
"name": "alertingGenerateSimplifiedRoutingWithOldHashes",
|
||||
"resourceVersion": "1759334385382",
|
||||
"creationTimestamp": "2025-10-01T15:59:45Z"
|
||||
},
|
||||
"spec": {
|
||||
"description": "Generate simplified routing with old hashes",
|
||||
"stage": "experimental",
|
||||
"codeowner": "@grafana/alerting-squad",
|
||||
"expression": "false"
|
||||
}
|
||||
},
|
||||
{
|
||||
"metadata": {
|
||||
"name": "alertingImportAlertmanagerAPI",
|
||||
|
@ -623,6 +610,46 @@
|
|||
"expression": "true"
|
||||
}
|
||||
},
|
||||
{
|
||||
"metadata": {
|
||||
"name": "alertingUseNewSimplifiedRoutingHashAlgorithm",
|
||||
"resourceVersion": "1759339813575",
|
||||
"creationTimestamp": "2025-10-01T17:28:42Z",
|
||||
"deletionTimestamp": "2025-10-01T17:29:29Z",
|
||||
"annotations": {
|
||||
"grafana.app/updatedTimestamp": "2025-10-01 17:30:13.575464 +0000 UTC"
|
||||
}
|
||||
},
|
||||
"spec": {
|
||||
"description": "",
|
||||
"stage": "preview",
|
||||
"codeowner": "@grafana/alerting-squad",
|
||||
"requiresRestart": true,
|
||||
"hideFromAdminPage": true,
|
||||
"hideFromDocs": true,
|
||||
"expression": "true"
|
||||
}
|
||||
},
|
||||
{
|
||||
"metadata": {
|
||||
"name": "alertingUseOldSimplifiedRoutingHashAlgorithm",
|
||||
"resourceVersion": "1759339782639",
|
||||
"creationTimestamp": "2025-10-01T17:29:29Z",
|
||||
"deletionTimestamp": "2025-10-01T17:30:13Z",
|
||||
"annotations": {
|
||||
"grafana.app/updatedTimestamp": "2025-10-01 17:29:42.63941 +0000 UTC"
|
||||
}
|
||||
},
|
||||
"spec": {
|
||||
"description": "",
|
||||
"stage": "deprecated",
|
||||
"codeowner": "@grafana/alerting-squad",
|
||||
"requiresRestart": true,
|
||||
"hideFromAdminPage": true,
|
||||
"hideFromDocs": true,
|
||||
"expression": "false"
|
||||
}
|
||||
},
|
||||
{
|
||||
"metadata": {
|
||||
"name": "alertmanagerRemotePrimary",
|
||||
|
|
|
@ -291,7 +291,8 @@ func TestAlertmanagerAutogenConfig(t *testing.T) {
|
|||
1: {AlertmanagerConfiguration: validConfig, OrgID: 1},
|
||||
2: {AlertmanagerConfiguration: validConfigWithoutAutogen, OrgID: 2},
|
||||
}
|
||||
sut.mam = createMultiOrgAlertmanager(t, configs)
|
||||
ft := featuremgmt.WithFeatures(featuremgmt.FlagAlertingUseNewSimplifiedRoutingHashAlgorithm)
|
||||
sut.mam = createMultiOrgAlertmanager(t, configs, withAMFeatureToggles(ft))
|
||||
return sut, configs
|
||||
}
|
||||
|
||||
|
@ -577,9 +578,29 @@ func createSut(t *testing.T) AlertmanagerSrv {
|
|||
}
|
||||
}
|
||||
|
||||
func createMultiOrgAlertmanager(t *testing.T, configs map[int64]*ngmodels.AlertConfiguration) *notifier.MultiOrgAlertmanager {
|
||||
type createMultiOrgAMOptions struct {
|
||||
featureToggles featuremgmt.FeatureToggles
|
||||
}
|
||||
|
||||
type createMultiOrgAMOptionsFunc func(*createMultiOrgAMOptions)
|
||||
|
||||
func withAMFeatureToggles(toggles featuremgmt.FeatureToggles) createMultiOrgAMOptionsFunc {
|
||||
return func(opts *createMultiOrgAMOptions) {
|
||||
opts.featureToggles = toggles
|
||||
}
|
||||
}
|
||||
|
||||
func createMultiOrgAlertmanager(t *testing.T, configs map[int64]*ngmodels.AlertConfiguration, opts ...createMultiOrgAMOptionsFunc) *notifier.MultiOrgAlertmanager {
|
||||
t.Helper()
|
||||
|
||||
options := createMultiOrgAMOptions{
|
||||
featureToggles: featuremgmt.WithFeatures(),
|
||||
}
|
||||
|
||||
for _, opt := range opts {
|
||||
opt(&options)
|
||||
}
|
||||
|
||||
configStore := notifier.NewFakeConfigStore(t, configs)
|
||||
orgStore := notifier.NewFakeOrgStore(t, []int64{1, 2, 3})
|
||||
provStore := ngfakes.NewFakeProvisioningStore()
|
||||
|
@ -610,7 +631,7 @@ func createMultiOrgAlertmanager(t *testing.T, configs map[int64]*ngmodels.AlertC
|
|||
ngfakes.NewFakeReceiverPermissionsService(),
|
||||
log.New("testlogger"),
|
||||
secretsService,
|
||||
featuremgmt.WithManager(),
|
||||
options.featureToggles,
|
||||
nil,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
|
|
@ -113,7 +113,7 @@ func (srv TestingApiSrv) RouteTestGrafanaRuleConfig(c *contextmodel.ReqContext,
|
|||
now,
|
||||
rule,
|
||||
results,
|
||||
state.GetRuleExtraLabels(log.New("testing"), rule, folder.Fullpath, includeFolder),
|
||||
state.GetRuleExtraLabels(log.New("testing"), rule, folder.Fullpath, includeFolder, srv.featureManager),
|
||||
nil,
|
||||
)
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
"unsafe"
|
||||
|
||||
"github.com/grafana/grafana-plugin-sdk-go/data"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/prometheus/common/model"
|
||||
)
|
||||
|
||||
|
@ -102,12 +103,12 @@ func (s *NotificationSettings) Validate() error {
|
|||
// - AutogeneratedRouteLabel: "true"
|
||||
// - AutogeneratedRouteReceiverNameLabel: Receiver
|
||||
// - AutogeneratedRouteSettingsHashLabel: Fingerprint (if the NotificationSettings are not all default)
|
||||
func (s *NotificationSettings) ToLabels() data.Labels {
|
||||
func (s *NotificationSettings) ToLabels(features featuremgmt.FeatureToggles) data.Labels {
|
||||
result := make(data.Labels, 3)
|
||||
result[AutogeneratedRouteLabel] = "true"
|
||||
result[AutogeneratedRouteReceiverNameLabel] = s.Receiver
|
||||
if !s.IsAllDefault() {
|
||||
result[AutogeneratedRouteSettingsHashLabel] = s.Fingerprint().String()
|
||||
result[AutogeneratedRouteSettingsHashLabel] = s.Fingerprint(features).String()
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
@ -160,7 +161,7 @@ func NewDefaultNotificationSettings(receiver string) NotificationSettings {
|
|||
// Fingerprint calculates a hash value to uniquely identify a NotificationSettings by its attributes.
|
||||
// The hash is calculated by concatenating the strings and durations of the NotificationSettings attributes
|
||||
// and using an invalid UTF-8 sequence as a separator.
|
||||
func (s *NotificationSettings) Fingerprint() data.Fingerprint {
|
||||
func (s *NotificationSettings) Fingerprint(features featuremgmt.FeatureToggles) data.Fingerprint {
|
||||
h := fnv.New64()
|
||||
tmp := make([]byte, 8)
|
||||
|
||||
|
@ -192,49 +193,13 @@ func (s *NotificationSettings) Fingerprint() data.Fingerprint {
|
|||
}
|
||||
// Add a separator between the time intervals to avoid collisions
|
||||
// when all settings are the same including interval names except for the interval type (mute vs active).
|
||||
_, _ = h.Write([]byte{255})
|
||||
// Use new algorithm by default, unless feature flag is explicitly disabled
|
||||
if features == nil || (features != nil && features.IsEnabledGlobally(featuremgmt.FlagAlertingUseNewSimplifiedRoutingHashAlgorithm)) {
|
||||
_, _ = h.Write([]byte{255})
|
||||
}
|
||||
for _, interval := range s.ActiveTimeIntervals {
|
||||
writeString(interval)
|
||||
}
|
||||
|
||||
return data.Fingerprint(h.Sum64())
|
||||
}
|
||||
|
||||
// FingerprintOld calculates a hash value using the old algorithm (before the separator fix in the Fingerpring function).
|
||||
// This is used temporarily during migration to support existing alerts with old hash labels.
|
||||
// TODO: Remove this method once the migration is complete.
|
||||
func (s *NotificationSettings) FingerprintOld() data.Fingerprint {
|
||||
h := fnv.New64()
|
||||
tmp := make([]byte, 8)
|
||||
|
||||
writeString := func(s string) {
|
||||
// save on extra slice allocation when string is converted to bytes.
|
||||
_, _ = h.Write(unsafe.Slice(unsafe.StringData(s), len(s))) //nolint:gosec
|
||||
// ignore errors returned by Write method because fnv never returns them.
|
||||
_, _ = h.Write([]byte{255}) // use an invalid utf-8 sequence as separator
|
||||
}
|
||||
writeDuration := func(d *model.Duration) {
|
||||
if d == nil {
|
||||
_, _ = h.Write([]byte{255})
|
||||
} else {
|
||||
binary.LittleEndian.PutUint64(tmp, uint64(*d))
|
||||
_, _ = h.Write(tmp)
|
||||
_, _ = h.Write([]byte{255})
|
||||
}
|
||||
}
|
||||
|
||||
writeString(s.Receiver)
|
||||
for _, gb := range s.NormalizedGroupBy() {
|
||||
writeString(gb)
|
||||
}
|
||||
writeDuration(s.GroupWait)
|
||||
writeDuration(s.GroupInterval)
|
||||
writeDuration(s.RepeatInterval)
|
||||
for _, interval := range s.MuteTimeIntervals {
|
||||
writeString(interval)
|
||||
}
|
||||
for _, interval := range s.ActiveTimeIntervals {
|
||||
writeString(interval)
|
||||
}
|
||||
return data.Fingerprint(h.Sum64())
|
||||
}
|
||||
|
|
|
@ -195,61 +195,12 @@ func TestNotificationSettingsLabels(t *testing.T) {
|
|||
|
||||
for _, tt := range testCases {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
labels := tt.notificationSettings.ToLabels()
|
||||
labels := tt.notificationSettings.ToLabels(nil)
|
||||
require.Equal(t, tt.labels, labels)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNotificationSettings_OldFingerprint(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
notificationSettings NotificationSettings
|
||||
expectedOldFingerprint string
|
||||
}{
|
||||
{
|
||||
name: "default notification settings with hardcoded default group by",
|
||||
notificationSettings: NotificationSettings{
|
||||
Receiver: "receiver name",
|
||||
GroupBy: DefaultNotificationSettingsGroupBy,
|
||||
},
|
||||
expectedOldFingerprint: "6027cdeaff62ba3f",
|
||||
},
|
||||
{
|
||||
name: "custom notification settings",
|
||||
notificationSettings: NotificationSettings{
|
||||
Receiver: "receiver name",
|
||||
GroupBy: []string{"label1", "label2"},
|
||||
GroupWait: util.Pointer(model.Duration(1 * time.Minute)),
|
||||
GroupInterval: util.Pointer(model.Duration(2 * time.Minute)),
|
||||
RepeatInterval: util.Pointer(model.Duration(3 * time.Minute)),
|
||||
MuteTimeIntervals: []string{"maintenance1", "maintenance2"},
|
||||
},
|
||||
expectedOldFingerprint: "47164c92f2986a35",
|
||||
},
|
||||
{
|
||||
name: "custom notification settings with active time interval",
|
||||
notificationSettings: NotificationSettings{
|
||||
Receiver: "receiver name",
|
||||
GroupBy: []string{"label1", "label2"},
|
||||
GroupWait: util.Pointer(model.Duration(1 * time.Minute)),
|
||||
GroupInterval: util.Pointer(model.Duration(2 * time.Minute)),
|
||||
RepeatInterval: util.Pointer(model.Duration(3 * time.Minute)),
|
||||
MuteTimeIntervals: []string{"maintenance1", "maintenance2"},
|
||||
ActiveTimeIntervals: []string{"active1", "active2"},
|
||||
},
|
||||
expectedOldFingerprint: "a173df6210e43af0",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range testCases {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
oldFp := tt.notificationSettings.FingerprintOld()
|
||||
require.Equal(t, tt.expectedOldFingerprint, oldFp.String())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNotificationSettings_TimeIntervals(t *testing.T) {
|
||||
// Create notification settings with default settings and usign the same
|
||||
// time interval, but in one case as a mute time interval and in another case
|
||||
|
@ -268,7 +219,7 @@ func TestNotificationSettings_TimeIntervals(t *testing.T) {
|
|||
ActiveTimeIntervals: []string{timeInterval},
|
||||
}
|
||||
|
||||
require.NotEqual(t, activeSettings.Fingerprint(), muteSettings.Fingerprint())
|
||||
require.NotEqual(t, activeSettings.Fingerprint(nil), muteSettings.Fingerprint(nil))
|
||||
}
|
||||
|
||||
func TestNormalizedGroupBy(t *testing.T) {
|
||||
|
|
|
@ -51,7 +51,7 @@ func newAutogeneratedRoute[R receiver](ctx context.Context, logger log.Logger, s
|
|||
// contact point even if no rules are using it. This will prevent race conditions between AM sync and rule sync.
|
||||
for _, receiver := range cfg.GetReceivers() {
|
||||
setting := models.NewDefaultNotificationSettings(receiver.GetName())
|
||||
fp := setting.Fingerprint()
|
||||
fp := setting.Fingerprint(features)
|
||||
notificationSettings[fp] = setting
|
||||
}
|
||||
|
||||
|
@ -66,7 +66,7 @@ func newAutogeneratedRoute[R receiver](ctx context.Context, logger log.Logger, s
|
|||
}
|
||||
return autogeneratedRoute{}, fmt.Errorf("invalid notification settings for rule %s: %w", ruleKey.UID, err)
|
||||
}
|
||||
fp := setting.Fingerprint()
|
||||
fp := setting.Fingerprint(features)
|
||||
// Keep only unique settings.
|
||||
if _, ok := notificationSettings[fp]; ok {
|
||||
continue
|
||||
|
@ -77,7 +77,7 @@ func newAutogeneratedRoute[R receiver](ctx context.Context, logger log.Logger, s
|
|||
if len(notificationSettings) == 0 {
|
||||
return autogeneratedRoute{}, nil
|
||||
}
|
||||
newAutogenRoute, err := generateRouteFromSettings(cfg.GetRoute().Receiver, notificationSettings, features)
|
||||
newAutogenRoute, err := generateRouteFromSettings(cfg.GetRoute().Receiver, notificationSettings)
|
||||
if err != nil {
|
||||
return autogeneratedRoute{}, fmt.Errorf("failed to create autogenerated route: %w", err)
|
||||
}
|
||||
|
@ -92,7 +92,7 @@ type autogeneratedRoute struct {
|
|||
// 1. with matcher by label models.AutogeneratedRouteLabel equals 'true'.
|
||||
// 2. with matcher by receiver name.
|
||||
// 3. with matcher by unique combination of optional settings. It is created only if there are optional settings.
|
||||
func generateRouteFromSettings(defaultReceiver string, settings map[data.Fingerprint]models.NotificationSettings, features featuremgmt.FeatureToggles) (autogeneratedRoute, error) {
|
||||
func generateRouteFromSettings(defaultReceiver string, settings map[data.Fingerprint]models.NotificationSettings) (autogeneratedRoute, error) {
|
||||
keys := maps.Keys(settings)
|
||||
// sort keys to make sure that the hash we calculate using it is stable
|
||||
slices.Sort(keys)
|
||||
|
@ -136,34 +136,26 @@ func generateRouteFromSettings(defaultReceiver string, settings map[data.Fingerp
|
|||
if s.IsAllDefault() {
|
||||
continue
|
||||
}
|
||||
|
||||
settingMatcher, err := labels.NewMatcher(labels.MatchEqual, models.AutogeneratedRouteSettingsHashLabel, fingerprint.String())
|
||||
if err != nil {
|
||||
return autogeneratedRoute{}, err
|
||||
}
|
||||
normalized := s.NormalizedGroupBy()
|
||||
groupByAll, groupBy := toGroupBy(normalized...)
|
||||
receiverRoute.Routes = append(receiverRoute.Routes, &definitions.Route{
|
||||
Receiver: s.Receiver,
|
||||
ObjectMatchers: definitions.ObjectMatchers{settingMatcher},
|
||||
Continue: false, // Only a single setting-specific route should match.
|
||||
|
||||
fingerprints := []data.Fingerprint{fingerprint}
|
||||
if features.IsEnabledGlobally(featuremgmt.FlagAlertingGenerateSimplifiedRoutingWithOldHashes) {
|
||||
fingerprints = append([]data.Fingerprint{s.FingerprintOld()}, fingerprints...)
|
||||
}
|
||||
|
||||
for _, fp := range fingerprints {
|
||||
matcher, err := labels.NewMatcher(labels.MatchEqual, models.AutogeneratedRouteSettingsHashLabel, fp.String())
|
||||
if err != nil {
|
||||
return autogeneratedRoute{}, err
|
||||
}
|
||||
receiverRoute.Routes = append(receiverRoute.Routes, &definitions.Route{
|
||||
Receiver: s.Receiver,
|
||||
ObjectMatchers: definitions.ObjectMatchers{matcher},
|
||||
Continue: false, // Only a single setting-specific route should match.
|
||||
GroupByStr: normalized,
|
||||
GroupBy: groupBy,
|
||||
GroupByAll: groupByAll,
|
||||
MuteTimeIntervals: s.MuteTimeIntervals,
|
||||
ActiveTimeIntervals: s.ActiveTimeIntervals,
|
||||
GroupWait: s.GroupWait,
|
||||
GroupInterval: s.GroupInterval,
|
||||
RepeatInterval: s.RepeatInterval,
|
||||
})
|
||||
}
|
||||
GroupByStr: normalized,
|
||||
GroupBy: groupBy,
|
||||
GroupByAll: groupByAll,
|
||||
MuteTimeIntervals: s.MuteTimeIntervals,
|
||||
ActiveTimeIntervals: s.ActiveTimeIntervals,
|
||||
GroupWait: s.GroupWait,
|
||||
GroupInterval: s.GroupInterval,
|
||||
RepeatInterval: s.RepeatInterval,
|
||||
})
|
||||
}
|
||||
|
||||
return autogeneratedRoute{
|
||||
|
|
|
@ -13,15 +13,12 @@ import (
|
|||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/log/logtest"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/models"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
)
|
||||
|
||||
func TestAddAutogenConfig(t *testing.T) {
|
||||
withOldHashFeature := featuremgmt.WithFeatures(featuremgmt.FlagAlertingGenerateSimplifiedRoutingWithOldHashes)
|
||||
|
||||
rootRoute := func() *definitions.Route {
|
||||
return &definitions.Route{
|
||||
Receiver: "default",
|
||||
|
@ -72,7 +69,6 @@ func TestAddAutogenConfig(t *testing.T) {
|
|||
existingConfig *definitions.PostableApiAlertingConfig
|
||||
storeSettings []models.NotificationSettings
|
||||
skipInvalid bool
|
||||
featureToggles featuremgmt.FeatureToggles
|
||||
expRoute *definitions.Route
|
||||
expErrorContains string
|
||||
}{
|
||||
|
@ -80,14 +76,12 @@ func TestAddAutogenConfig(t *testing.T) {
|
|||
name: "no settings or receivers, no change",
|
||||
existingConfig: configGen(nil, nil),
|
||||
storeSettings: []models.NotificationSettings{},
|
||||
featureToggles: withOldHashFeature,
|
||||
expRoute: rootRoute(),
|
||||
},
|
||||
{
|
||||
name: "no settings but some receivers, add default routes for receivers",
|
||||
existingConfig: configGen([]string{"receiver1", "receiver2", "receiver3"}, nil),
|
||||
storeSettings: []models.NotificationSettings{},
|
||||
featureToggles: withOldHashFeature,
|
||||
expRoute: withChildRoutes(rootRoute(), &definitions.Route{
|
||||
Receiver: "default",
|
||||
ObjectMatchers: matcher(models.AutogeneratedRouteLabel, "true"),
|
||||
|
@ -115,7 +109,6 @@ func TestAddAutogenConfig(t *testing.T) {
|
|||
{
|
||||
name: "settings with custom options, add option-specific routes",
|
||||
existingConfig: configGen([]string{"receiver1", "receiver2", "receiver3", "receiver4", "receiver5"}, []string{"maintenance", "active"}),
|
||||
featureToggles: withOldHashFeature,
|
||||
storeSettings: []models.NotificationSettings{
|
||||
models.CopyNotificationSettings(models.NewDefaultNotificationSettings("receiver1"), models.NSMuts.WithGroupInterval(util.Pointer(1*time.Minute))),
|
||||
models.CopyNotificationSettings(models.NewDefaultNotificationSettings("receiver2"), models.NSMuts.WithGroupWait(util.Pointer(2*time.Minute))),
|
||||
|
@ -137,117 +130,50 @@ func TestAddAutogenConfig(t *testing.T) {
|
|||
Receiver: "default",
|
||||
ObjectMatchers: matcher(models.AutogeneratedRouteLabel, "true"),
|
||||
Routes: []*definitions.Route{
|
||||
withChildRoutes(basicContactRoute("receiver1"),
|
||||
// Old hash for complex settings
|
||||
&definitions.Route{
|
||||
Receiver: "receiver1",
|
||||
ObjectMatchers: matcher(models.AutogeneratedRouteSettingsHashLabel, "f134b8faf7db083c"),
|
||||
GroupByStr: []string{models.FolderTitleLabel, model.AlertNameLabel, "custom"},
|
||||
GroupInterval: util.Pointer(model.Duration(1 * time.Minute)),
|
||||
GroupWait: util.Pointer(model.Duration(2 * time.Minute)),
|
||||
RepeatInterval: util.Pointer(model.Duration(3 * time.Minute)),
|
||||
MuteTimeIntervals: []string{"maintenance"},
|
||||
ActiveTimeIntervals: []string{"active"},
|
||||
},
|
||||
// New hash for complex settings
|
||||
&definitions.Route{
|
||||
Receiver: "receiver1",
|
||||
ObjectMatchers: matcher(models.AutogeneratedRouteSettingsHashLabel, "02466789dc88da23"),
|
||||
GroupByStr: []string{models.FolderTitleLabel, model.AlertNameLabel, "custom"},
|
||||
GroupInterval: util.Pointer(model.Duration(1 * time.Minute)),
|
||||
GroupWait: util.Pointer(model.Duration(2 * time.Minute)),
|
||||
RepeatInterval: util.Pointer(model.Duration(3 * time.Minute)),
|
||||
MuteTimeIntervals: []string{"maintenance"},
|
||||
ActiveTimeIntervals: []string{"active"},
|
||||
},
|
||||
// Old hash for simple GroupInterval
|
||||
&definitions.Route{
|
||||
Receiver: "receiver1",
|
||||
ObjectMatchers: matcher(models.AutogeneratedRouteSettingsHashLabel, "dde34b8127e68f31"),
|
||||
GroupInterval: util.Pointer(model.Duration(1 * time.Minute)),
|
||||
},
|
||||
// New hash for simple GroupInterval
|
||||
&definitions.Route{
|
||||
Receiver: "receiver1",
|
||||
ObjectMatchers: matcher(models.AutogeneratedRouteSettingsHashLabel, "efc87d76ccc550bc"),
|
||||
GroupInterval: util.Pointer(model.Duration(1 * time.Minute)),
|
||||
},
|
||||
),
|
||||
withChildRoutes(basicContactRoute("receiver2"),
|
||||
// Old hash
|
||||
&definitions.Route{
|
||||
Receiver: "receiver2",
|
||||
ObjectMatchers: matcher(models.AutogeneratedRouteSettingsHashLabel, "27e1d1717c9ef621"),
|
||||
GroupWait: util.Pointer(model.Duration(2 * time.Minute)),
|
||||
},
|
||||
// New hash
|
||||
&definitions.Route{
|
||||
Receiver: "receiver2",
|
||||
ObjectMatchers: matcher(models.AutogeneratedRouteSettingsHashLabel, "63ad04d6c21c3aec"),
|
||||
GroupWait: util.Pointer(model.Duration(2 * time.Minute)),
|
||||
},
|
||||
),
|
||||
withChildRoutes(basicContactRoute("receiver5"),
|
||||
// Old hash for active intervals
|
||||
&definitions.Route{
|
||||
Receiver: "receiver5",
|
||||
ObjectMatchers: matcher(models.AutogeneratedRouteSettingsHashLabel, "cd6cd2089632453c"),
|
||||
ActiveTimeIntervals: []string{"active"},
|
||||
},
|
||||
// New hash for active intervals
|
||||
&definitions.Route{
|
||||
Receiver: "receiver5",
|
||||
ObjectMatchers: matcher(models.AutogeneratedRouteSettingsHashLabel, "8cd5f9adeac58123"),
|
||||
ActiveTimeIntervals: []string{"active"},
|
||||
},
|
||||
// Old hash for mute intervals
|
||||
&definitions.Route{
|
||||
Receiver: "receiver5",
|
||||
ObjectMatchers: matcher(models.AutogeneratedRouteSettingsHashLabel, "030d6474aec0b553"),
|
||||
MuteTimeIntervals: []string{"maintenance"},
|
||||
},
|
||||
// New hash for mute intervals
|
||||
&definitions.Route{
|
||||
Receiver: "receiver5",
|
||||
ObjectMatchers: matcher(models.AutogeneratedRouteSettingsHashLabel, "f0770544f1741cf6"),
|
||||
MuteTimeIntervals: []string{"maintenance"},
|
||||
},
|
||||
),
|
||||
withChildRoutes(basicContactRoute("receiver4"),
|
||||
// Old hash
|
||||
&definitions.Route{
|
||||
Receiver: "receiver4",
|
||||
ObjectMatchers: matcher(models.AutogeneratedRouteSettingsHashLabel, "b3a2fa5e615dcc7e"),
|
||||
GroupByStr: []string{models.FolderTitleLabel, model.AlertNameLabel, "custom"},
|
||||
},
|
||||
// New hash
|
||||
&definitions.Route{
|
||||
Receiver: "receiver4",
|
||||
ObjectMatchers: matcher(models.AutogeneratedRouteSettingsHashLabel, "9bbbec5f72627ae5"),
|
||||
GroupByStr: []string{models.FolderTitleLabel, model.AlertNameLabel, "custom"},
|
||||
},
|
||||
),
|
||||
withChildRoutes(basicContactRoute("receiver3"),
|
||||
// Old hash
|
||||
&definitions.Route{
|
||||
Receiver: "receiver3",
|
||||
ObjectMatchers: matcher(models.AutogeneratedRouteSettingsHashLabel, "9e282ef0193d830a"),
|
||||
RepeatInterval: util.Pointer(model.Duration(3 * time.Minute)),
|
||||
},
|
||||
// New hash
|
||||
&definitions.Route{
|
||||
Receiver: "receiver3",
|
||||
ObjectMatchers: matcher(models.AutogeneratedRouteSettingsHashLabel, "fbcacbfae385a901"),
|
||||
RepeatInterval: util.Pointer(model.Duration(3 * time.Minute)),
|
||||
},
|
||||
),
|
||||
withChildRoutes(basicContactRoute("receiver1"), &definitions.Route{
|
||||
Receiver: "receiver1",
|
||||
ObjectMatchers: matcher(models.AutogeneratedRouteSettingsHashLabel, "02466789dc88da23"),
|
||||
GroupByStr: []string{models.FolderTitleLabel, model.AlertNameLabel, "custom"},
|
||||
GroupInterval: util.Pointer(model.Duration(1 * time.Minute)),
|
||||
GroupWait: util.Pointer(model.Duration(2 * time.Minute)),
|
||||
RepeatInterval: util.Pointer(model.Duration(3 * time.Minute)),
|
||||
MuteTimeIntervals: []string{"maintenance"},
|
||||
ActiveTimeIntervals: []string{"active"},
|
||||
}, &definitions.Route{
|
||||
Receiver: "receiver1",
|
||||
ObjectMatchers: matcher(models.AutogeneratedRouteSettingsHashLabel, "efc87d76ccc550bc"),
|
||||
GroupInterval: util.Pointer(model.Duration(1 * time.Minute)),
|
||||
}),
|
||||
withChildRoutes(basicContactRoute("receiver2"), &definitions.Route{
|
||||
Receiver: "receiver2",
|
||||
ObjectMatchers: matcher(models.AutogeneratedRouteSettingsHashLabel, "63ad04d6c21c3aec"),
|
||||
GroupWait: util.Pointer(model.Duration(2 * time.Minute)),
|
||||
}),
|
||||
withChildRoutes(basicContactRoute("receiver5"), &definitions.Route{
|
||||
Receiver: "receiver5",
|
||||
ObjectMatchers: matcher(models.AutogeneratedRouteSettingsHashLabel, "8cd5f9adeac58123"),
|
||||
ActiveTimeIntervals: []string{"active"},
|
||||
}, &definitions.Route{
|
||||
Receiver: "receiver5",
|
||||
ObjectMatchers: matcher(models.AutogeneratedRouteSettingsHashLabel, "f0770544f1741cf6"),
|
||||
MuteTimeIntervals: []string{"maintenance"},
|
||||
}),
|
||||
withChildRoutes(basicContactRoute("receiver4"), &definitions.Route{
|
||||
Receiver: "receiver4",
|
||||
ObjectMatchers: matcher(models.AutogeneratedRouteSettingsHashLabel, "9bbbec5f72627ae5"),
|
||||
GroupByStr: []string{models.FolderTitleLabel, model.AlertNameLabel, "custom"},
|
||||
}),
|
||||
withChildRoutes(basicContactRoute("receiver3"), &definitions.Route{
|
||||
Receiver: "receiver3",
|
||||
ObjectMatchers: matcher(models.AutogeneratedRouteSettingsHashLabel, "fbcacbfae385a901"),
|
||||
RepeatInterval: util.Pointer(model.Duration(3 * time.Minute)),
|
||||
}),
|
||||
},
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "settings with custom options and nil groupBy, groupBy should inherit from parent",
|
||||
existingConfig: configGen([]string{"receiver1"}, nil),
|
||||
featureToggles: withOldHashFeature,
|
||||
storeSettings: []models.NotificationSettings{
|
||||
models.CopyNotificationSettings(models.NewDefaultNotificationSettings("receiver1"), models.NSMuts.WithGroupInterval(util.Pointer(1*time.Minute)), models.NSMuts.WithGroupBy()),
|
||||
},
|
||||
|
@ -255,29 +181,18 @@ func TestAddAutogenConfig(t *testing.T) {
|
|||
Receiver: "default",
|
||||
ObjectMatchers: matcher(models.AutogeneratedRouteLabel, "true"),
|
||||
Routes: []*definitions.Route{
|
||||
withChildRoutes(basicContactRoute("receiver1"),
|
||||
// Old hash
|
||||
&definitions.Route{
|
||||
Receiver: "receiver1",
|
||||
ObjectMatchers: matcher(models.AutogeneratedRouteSettingsHashLabel, "dde34b8127e68f31"),
|
||||
GroupByStr: nil,
|
||||
GroupInterval: util.Pointer(model.Duration(1 * time.Minute)),
|
||||
},
|
||||
// New hash
|
||||
&definitions.Route{
|
||||
Receiver: "receiver1",
|
||||
ObjectMatchers: matcher(models.AutogeneratedRouteSettingsHashLabel, "efc87d76ccc550bc"),
|
||||
GroupByStr: nil,
|
||||
GroupInterval: util.Pointer(model.Duration(1 * time.Minute)),
|
||||
},
|
||||
),
|
||||
withChildRoutes(basicContactRoute("receiver1"), &definitions.Route{
|
||||
Receiver: "receiver1",
|
||||
ObjectMatchers: matcher(models.AutogeneratedRouteSettingsHashLabel, "efc87d76ccc550bc"),
|
||||
GroupByStr: nil,
|
||||
GroupInterval: util.Pointer(model.Duration(1 * time.Minute)),
|
||||
}),
|
||||
},
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "settings with nil groupBy should have different fingerprint than default groupBy",
|
||||
existingConfig: configGen([]string{"receiver1"}, nil),
|
||||
featureToggles: withOldHashFeature,
|
||||
storeSettings: []models.NotificationSettings{
|
||||
models.CopyNotificationSettings(models.NewDefaultNotificationSettings("receiver1"), models.NSMuts.WithGroupInterval(util.Pointer(1*time.Minute)), models.NSMuts.WithGroupBy()),
|
||||
models.CopyNotificationSettings(models.NewDefaultNotificationSettings("receiver1"), models.NSMuts.WithGroupInterval(util.Pointer(1*time.Minute)), models.NSMuts.WithGroupBy(models.DefaultNotificationSettingsGroupBy...)),
|
||||
|
@ -286,36 +201,17 @@ func TestAddAutogenConfig(t *testing.T) {
|
|||
Receiver: "default",
|
||||
ObjectMatchers: matcher(models.AutogeneratedRouteLabel, "true"),
|
||||
Routes: []*definitions.Route{
|
||||
withChildRoutes(basicContactRoute("receiver1"),
|
||||
// Old hash for explicit default groupBy
|
||||
&definitions.Route{
|
||||
Receiver: "receiver1",
|
||||
ObjectMatchers: matcher(models.AutogeneratedRouteSettingsHashLabel, "e1f3a275a8918385"),
|
||||
GroupByStr: []string{models.FolderTitleLabel, model.AlertNameLabel},
|
||||
GroupInterval: util.Pointer(model.Duration(1 * time.Minute)),
|
||||
},
|
||||
// New hash for explicit default groupBy
|
||||
&definitions.Route{
|
||||
Receiver: "receiver1",
|
||||
ObjectMatchers: matcher(models.AutogeneratedRouteSettingsHashLabel, "828092ed6f427a00"),
|
||||
GroupByStr: []string{models.FolderTitleLabel, model.AlertNameLabel},
|
||||
GroupInterval: util.Pointer(model.Duration(1 * time.Minute)),
|
||||
},
|
||||
// Old hash for nil groupBy
|
||||
&definitions.Route{
|
||||
Receiver: "receiver1",
|
||||
ObjectMatchers: matcher(models.AutogeneratedRouteSettingsHashLabel, "dde34b8127e68f31"),
|
||||
GroupByStr: nil,
|
||||
GroupInterval: util.Pointer(model.Duration(1 * time.Minute)),
|
||||
},
|
||||
// New hash for nil groupBy
|
||||
&definitions.Route{
|
||||
Receiver: "receiver1",
|
||||
ObjectMatchers: matcher(models.AutogeneratedRouteSettingsHashLabel, "efc87d76ccc550bc"),
|
||||
GroupByStr: nil,
|
||||
GroupInterval: util.Pointer(model.Duration(1 * time.Minute)),
|
||||
},
|
||||
),
|
||||
withChildRoutes(basicContactRoute("receiver1"), &definitions.Route{
|
||||
Receiver: "receiver1",
|
||||
ObjectMatchers: matcher(models.AutogeneratedRouteSettingsHashLabel, "828092ed6f427a00"), // Different hash.
|
||||
GroupByStr: []string{models.FolderTitleLabel, model.AlertNameLabel},
|
||||
GroupInterval: util.Pointer(model.Duration(1 * time.Minute)),
|
||||
}, &definitions.Route{
|
||||
Receiver: "receiver1",
|
||||
ObjectMatchers: matcher(models.AutogeneratedRouteSettingsHashLabel, "efc87d76ccc550bc"),
|
||||
GroupByStr: nil,
|
||||
GroupInterval: util.Pointer(model.Duration(1 * time.Minute)),
|
||||
}),
|
||||
},
|
||||
}),
|
||||
},
|
||||
|
@ -327,27 +223,16 @@ func TestAddAutogenConfig(t *testing.T) {
|
|||
models.CopyNotificationSettings(models.NewDefaultNotificationSettings("receiver1"), models.NSMuts.WithGroupInterval(util.Pointer(1*time.Minute)), models.NSMuts.WithGroupBy(model.AlertNameLabel)),
|
||||
models.CopyNotificationSettings(models.NewDefaultNotificationSettings("receiver1"), models.NSMuts.WithGroupInterval(util.Pointer(1*time.Minute)), models.NSMuts.WithGroupBy(models.DefaultNotificationSettingsGroupBy...)),
|
||||
},
|
||||
featureToggles: withOldHashFeature,
|
||||
expRoute: withChildRoutes(rootRoute(), &definitions.Route{
|
||||
Receiver: "default",
|
||||
ObjectMatchers: matcher(models.AutogeneratedRouteLabel, "true"),
|
||||
Routes: []*definitions.Route{
|
||||
withChildRoutes(basicContactRoute("receiver1"),
|
||||
// Old hash
|
||||
&definitions.Route{
|
||||
Receiver: "receiver1",
|
||||
ObjectMatchers: matcher(models.AutogeneratedRouteSettingsHashLabel, "e1f3a275a8918385"),
|
||||
GroupByStr: []string{models.FolderTitleLabel, model.AlertNameLabel},
|
||||
GroupInterval: util.Pointer(model.Duration(1 * time.Minute)),
|
||||
},
|
||||
// New hash
|
||||
&definitions.Route{
|
||||
Receiver: "receiver1",
|
||||
ObjectMatchers: matcher(models.AutogeneratedRouteSettingsHashLabel, "828092ed6f427a00"),
|
||||
GroupByStr: []string{models.FolderTitleLabel, model.AlertNameLabel},
|
||||
GroupInterval: util.Pointer(model.Duration(1 * time.Minute)),
|
||||
},
|
||||
),
|
||||
withChildRoutes(basicContactRoute("receiver1"), &definitions.Route{
|
||||
Receiver: "receiver1",
|
||||
ObjectMatchers: matcher(models.AutogeneratedRouteSettingsHashLabel, "828092ed6f427a00"),
|
||||
GroupByStr: []string{models.FolderTitleLabel, model.AlertNameLabel},
|
||||
GroupInterval: util.Pointer(model.Duration(1 * time.Minute)),
|
||||
}),
|
||||
},
|
||||
}),
|
||||
},
|
||||
|
@ -359,8 +244,7 @@ func TestAddAutogenConfig(t *testing.T) {
|
|||
models.CopyNotificationSettings(models.NewDefaultNotificationSettings("receiver1"), models.NSMuts.WithMuteTimeIntervals("maintenance")), // Doesn't exist.
|
||||
models.CopyNotificationSettings(models.NewDefaultNotificationSettings("receiver2"), models.NSMuts.WithGroupWait(util.Pointer(-2*time.Minute))), // Negative.
|
||||
},
|
||||
featureToggles: withOldHashFeature,
|
||||
skipInvalid: true,
|
||||
skipInvalid: true,
|
||||
expRoute: withChildRoutes(rootRoute(), &definitions.Route{
|
||||
Receiver: "default",
|
||||
ObjectMatchers: matcher(models.AutogeneratedRouteLabel, "true"),
|
||||
|
@ -392,27 +276,6 @@ func TestAddAutogenConfig(t *testing.T) {
|
|||
skipInvalid: false,
|
||||
expErrorContains: "group wait",
|
||||
},
|
||||
{
|
||||
name: "without feature toggle, only new hash route is generated",
|
||||
existingConfig: configGen([]string{"receiver1"}, nil),
|
||||
storeSettings: []models.NotificationSettings{
|
||||
models.CopyNotificationSettings(models.NewDefaultNotificationSettings("receiver1"), models.NSMuts.WithGroupInterval(util.Pointer(1*time.Minute))),
|
||||
},
|
||||
expRoute: withChildRoutes(rootRoute(), &definitions.Route{
|
||||
Receiver: "default",
|
||||
ObjectMatchers: matcher(models.AutogeneratedRouteLabel, "true"),
|
||||
Routes: []*definitions.Route{
|
||||
withChildRoutes(basicContactRoute("receiver1"),
|
||||
// Only new hash route
|
||||
&definitions.Route{
|
||||
Receiver: "receiver1",
|
||||
ObjectMatchers: matcher(models.AutogeneratedRouteSettingsHashLabel, "efc87d76ccc550bc"),
|
||||
GroupInterval: util.Pointer(model.Duration(1 * time.Minute)),
|
||||
},
|
||||
),
|
||||
},
|
||||
}),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range testCases {
|
||||
|
@ -427,12 +290,7 @@ func TestAddAutogenConfig(t *testing.T) {
|
|||
store.notificationSettings[orgId][models.AlertRuleKey{OrgID: orgId, UID: util.GenerateShortUID()}] = []models.NotificationSettings{setting}
|
||||
}
|
||||
|
||||
features := tt.featureToggles
|
||||
if features == nil {
|
||||
features = featuremgmt.WithFeatures()
|
||||
}
|
||||
|
||||
err := AddAutogenConfig(context.Background(), &logtest.Fake{}, store, orgId, tt.existingConfig, tt.skipInvalid, features)
|
||||
err := AddAutogenConfig(context.Background(), &logtest.Fake{}, store, orgId, tt.existingConfig, tt.skipInvalid, nil)
|
||||
if tt.expErrorContains != "" {
|
||||
require.Error(t, err)
|
||||
require.ErrorContains(t, err, tt.expErrorContains)
|
||||
|
|
|
@ -471,7 +471,7 @@ func (a *alertRule) evaluate(ctx context.Context, e *Evaluation, span trace.Span
|
|||
e.scheduledAt,
|
||||
e.rule,
|
||||
results,
|
||||
state.GetRuleExtraLabels(logger, e.rule, e.folderTitle, !a.disableGrafanaFolder),
|
||||
state.GetRuleExtraLabels(logger, e.rule, e.folderTitle, !a.disableGrafanaFolder, a.featureToggles),
|
||||
func(ctx context.Context, statesToSend state.StateTransitions) {
|
||||
start := a.clock.Now()
|
||||
alerts := a.send(ctx, logger, statesToSend)
|
||||
|
|
|
@ -1317,7 +1317,7 @@ func stateForRule(rule *models.AlertRule, ts time.Time, evalState eval.State) *s
|
|||
for k, v := range rule.Labels {
|
||||
s.Labels[k] = v
|
||||
}
|
||||
for k, v := range state.GetRuleExtraLabels(&logtest.Fake{}, rule, "", true) {
|
||||
for k, v := range state.GetRuleExtraLabels(&logtest.Fake{}, rule, "", true, nil) {
|
||||
if _, ok := s.Labels[k]; !ok {
|
||||
s.Labels[k] = v
|
||||
}
|
||||
|
|
|
@ -304,7 +304,7 @@ func (r ruleWithFolder) Fingerprint() fingerprint {
|
|||
}
|
||||
|
||||
for _, setting := range rule.NotificationSettings {
|
||||
binary.LittleEndian.PutUint64(tmp, uint64(setting.Fingerprint()))
|
||||
binary.LittleEndian.PutUint64(tmp, uint64(setting.Fingerprint(nil)))
|
||||
writeBytes(tmp)
|
||||
}
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@ import (
|
|||
"github.com/grafana/grafana/pkg/apimachinery/errutil"
|
||||
"github.com/grafana/grafana/pkg/expr"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/eval"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/models"
|
||||
"github.com/grafana/grafana/pkg/services/screenshot"
|
||||
|
@ -753,7 +754,7 @@ func ParseFormattedState(stateStr string) (eval.State, string, error) {
|
|||
}
|
||||
|
||||
// GetRuleExtraLabels returns a map of built-in labels that should be added to an alert before it is sent to the Alertmanager or its state is cached.
|
||||
func GetRuleExtraLabels(l log.Logger, rule *models.AlertRule, folderTitle string, includeFolder bool) map[string]string {
|
||||
func GetRuleExtraLabels(l log.Logger, rule *models.AlertRule, folderTitle string, includeFolder bool, features featuremgmt.FeatureToggles) map[string]string {
|
||||
extraLabels := make(map[string]string, 4)
|
||||
|
||||
extraLabels[alertingModels.NamespaceUIDLabel] = rule.NamespaceUID
|
||||
|
@ -771,7 +772,7 @@ func GetRuleExtraLabels(l log.Logger, rule *models.AlertRule, folderTitle string
|
|||
ignored, _ := json.Marshal(rule.NotificationSettings[1:])
|
||||
l.Error("Detected multiple notification settings, which is not supported. Only the first will be applied", "ignored_settings", string(ignored))
|
||||
}
|
||||
return mergeLabels(extraLabels, rule.NotificationSettings[0].ToLabels())
|
||||
return mergeLabels(extraLabels, rule.NotificationSettings[0].ToLabels(features))
|
||||
}
|
||||
return extraLabels
|
||||
}
|
||||
|
|
|
@ -779,7 +779,7 @@ func TestGetRuleExtraLabels(t *testing.T) {
|
|||
models.RuleUIDLabel: rule.UID,
|
||||
ngmodels.AutogeneratedRouteLabel: "true",
|
||||
ngmodels.AutogeneratedRouteReceiverNameLabel: ns.Receiver,
|
||||
ngmodels.AutogeneratedRouteSettingsHashLabel: ns.Fingerprint().String(),
|
||||
ngmodels.AutogeneratedRouteSettingsHashLabel: ns.Fingerprint(nil).String(),
|
||||
},
|
||||
},
|
||||
"ignore_multiple_notifications": {
|
||||
|
@ -794,14 +794,14 @@ func TestGetRuleExtraLabels(t *testing.T) {
|
|||
models.RuleUIDLabel: rule.UID,
|
||||
ngmodels.AutogeneratedRouteLabel: "true",
|
||||
ngmodels.AutogeneratedRouteReceiverNameLabel: ns.Receiver,
|
||||
ngmodels.AutogeneratedRouteSettingsHashLabel: ns.Fingerprint().String(),
|
||||
ngmodels.AutogeneratedRouteSettingsHashLabel: ns.Fingerprint(nil).String(),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range testCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
result := GetRuleExtraLabels(logger, tc.rule, folderTitle, tc.includeFolder)
|
||||
result := GetRuleExtraLabels(logger, tc.rule, folderTitle, tc.includeFolder, nil)
|
||||
require.Equal(t, tc.expected, result)
|
||||
})
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue