diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 60e4bc67e68..4492c36d77b 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -133,6 +133,7 @@ /pkg/services/playlist/ @grafana/grafana-app-platform-squad /pkg/services/preference/ @grafana/grafana-backend-group /pkg/services/provisioning/ @grafana/grafana-search-and-storage +/pkg/services/provisioning/alerting/ @grafana/alerting-backend /pkg/services/query/ @grafana/grafana-app-platform-squad /pkg/services/queryhistory/ @grafana/explore-squad /pkg/services/quota/ @grafana/grafana-search-and-storage diff --git a/pkg/services/provisioning/alerting/config_reader_test.go b/pkg/services/provisioning/alerting/config_reader_test.go index edcf431e690..c13780633c3 100644 --- a/pkg/services/provisioning/alerting/config_reader_test.go +++ b/pkg/services/provisioning/alerting/config_reader_test.go @@ -16,6 +16,7 @@ const ( testFileSupportedFiletypes = "./testdata/common/supported-filetypes" testFileCorrectProperties = "./testdata/alert_rules/correct-properties" testFileCorrectPropertiesWithOrg = "./testdata/alert_rules/correct-properties-with-org" + testFileDasboardTypoSupport = "./testdata/alert_rules/dasboard-typo-support" testFileMultipleRules = "./testdata/alert_rules/multiple-rules" testFileMultipleFiles = "./testdata/alert_rules/multiple-files" testFileCorrectProperties_cp = "./testdata/contact_points/correct-properties" @@ -159,4 +160,32 @@ func TestConfigReader(t *testing.T) { require.NoError(t, err) require.Len(t, file[0].Templates, 2) }) + t.Run("a rule file with dasboard typo", func(t *testing.T) { + ruleFiles, err := configReader.readConfig(ctx, testFileDasboardTypoSupport) + require.NoError(t, err) + t.Run("only dasboard present", func(t *testing.T) { + for _, ruleFile := range ruleFiles { + group := ruleFile.Groups[0] + t.Run(group.Title, func(t *testing.T) { + require.Equal(t, "dasboardUid", *group.Rules[0].DashboardUID) + }) + } + }) + t.Run("only dashboard present", func(t *testing.T) { + for _, ruleFile := range ruleFiles { + group := ruleFile.Groups[0] + t.Run(group.Title, func(t *testing.T) { + require.Equal(t, "dashboardUid", *group.Rules[1].DashboardUID) + }) + } + }) + t.Run("both dasboard and dashboard present", func(t *testing.T) { + for _, ruleFile := range ruleFiles { + group := ruleFile.Groups[0] + t.Run(group.Title, func(t *testing.T) { + require.Equal(t, "dashboardUid", *group.Rules[2].DashboardUID) + }) + } + }) + }) } diff --git a/pkg/services/provisioning/alerting/rules_types.go b/pkg/services/provisioning/alerting/rules_types.go index cce86eedba4..805ca5b1506 100644 --- a/pkg/services/provisioning/alerting/rules_types.go +++ b/pkg/services/provisioning/alerting/rules_types.go @@ -66,7 +66,8 @@ type AlertRuleV1 struct { Title values.StringValue `json:"title" yaml:"title"` Condition values.StringValue `json:"condition" yaml:"condition"` Data []QueryV1 `json:"data" yaml:"data"` - DashboardUID values.StringValue `json:"dasboardUid" yaml:"dashboardUid"` + DasboardUID values.StringValue `json:"dasboardUid" yaml:"dasboardUid"` // TODO: Grandfathered typo support. TODO: This should be removed in V2. + DashboardUID values.StringValue `json:"dashboardUid" yaml:"dashboardUid"` PanelID values.Int64Value `json:"panelId" yaml:"panelId"` NoDataState values.StringValue `json:"noDataState" yaml:"noDataState"` ExecErrState values.StringValue `json:"execErrState" yaml:"execErrState"` @@ -78,6 +79,13 @@ type AlertRuleV1 struct { Record *RecordV1 `json:"record" yaml:"record"` } +func withFallback(value, fallback string) *string { + if value == "" { + return &fallback + } + return &value +} + func (rule *AlertRuleV1) mapToModel(orgID int64) (models.AlertRule, error) { alertRule := models.AlertRule{} alertRule.Title = rule.Title.Value() @@ -94,8 +102,9 @@ func (rule *AlertRuleV1) mapToModel(orgID int64) (models.AlertRule, error) { return models.AlertRule{}, fmt.Errorf("rule '%s' failed to parse: %w", alertRule.Title, err) } alertRule.For = time.Duration(duration) + dasboardUID := rule.DasboardUID.Value() dashboardUID := rule.DashboardUID.Value() - alertRule.DashboardUID = &dashboardUID + alertRule.DashboardUID = withFallback(dashboardUID, dasboardUID) // Use correct spelling over supported typo. panelID := rule.PanelID.Value() alertRule.PanelID = &panelID execErrStateValue := strings.TrimSpace(rule.ExecErrState.Value()) diff --git a/pkg/services/provisioning/alerting/testdata/alert_rules/dasboard-typo-support/rules.json b/pkg/services/provisioning/alerting/testdata/alert_rules/dasboard-typo-support/rules.json new file mode 100644 index 00000000000..2e2b35d9cd4 --- /dev/null +++ b/pkg/services/provisioning/alerting/testdata/alert_rules/dasboard-typo-support/rules.json @@ -0,0 +1,49 @@ +{ + "apiVersion": 1, + "groups": [ + { + "name": "json_group", + "folder": "my_folder", + "interval": "10s", + "rules": [ + { + "title": "with_typo", + "uid": "with_typo", + "dasboardUid": "dasboardUid", + "condition": "A", + "for": "1m", + "data": [ + { + "refId": "A" + } + ] + }, + { + "title": "without_typo", + "uid": "without_typo", + "dashboardUid": "dashboardUid", + "condition": "A", + "for": "1m", + "data": [ + { + "refId": "A" + } + ] + }, + { + "title": "with_both", + "uid": "with_both", + "dashboardUid": "dashboardUid", + "dasboardUid": "dasboardUid", + "condition": "A", + "for": "1m", + "data": [ + { + "refId": "A" + } + ] + } + ] + } + ] +} diff --git a/pkg/services/provisioning/alerting/testdata/alert_rules/dasboard-typo-support/rules.yml b/pkg/services/provisioning/alerting/testdata/alert_rules/dasboard-typo-support/rules.yml new file mode 100644 index 00000000000..1936e33619c --- /dev/null +++ b/pkg/services/provisioning/alerting/testdata/alert_rules/dasboard-typo-support/rules.yml @@ -0,0 +1,28 @@ +apiVersion: 1 +groups: + - name: yaml_group + folder: my_folder + interval: 10s + rules: + - title: with_typo + uid: with_typo + dasboardUid: dasboardUid + condition: A + for: 1m + data: + - refId: A + - title: without_typo + uid: without_typo + dashboardUid: dashboardUid + condition: A + for: 1m + data: + - refId: A + - title: with_both + uid: with_both + dashboardUid: dashboardUid + dasboardUid: dasboardUid + condition: A + for: 1m + data: + - refId: A