diff --git a/pkg/services/ngalert/models/alert_rule.go b/pkg/services/ngalert/models/alert_rule.go index fb5ecad3970..3f95cfd8b62 100644 --- a/pkg/services/ngalert/models/alert_rule.go +++ b/pkg/services/ngalert/models/alert_rule.go @@ -153,6 +153,10 @@ const ( // MigratedMessageAnnotation is created during legacy migration to store the migrated alert message. MigratedMessageAnnotation = "message" + // ConvertedPrometheusRuleLabel is a label that indicates that the alert rule was converted from a Prometheus rule + // using the import APIs. + ConvertedPrometheusRuleLabel = "__converted_prometheus_rule__" + // AutogeneratedRouteLabel a label name used to distinguish alerts that are supposed to be handled by the autogenerated policy. Only expected value is `true`. AutogeneratedRouteLabel = "__grafana_autogenerated__" // AutogeneratedRouteReceiverNameLabel a label name that contains the name of the receiver that should be used to send notifications for the alert. diff --git a/pkg/services/ngalert/prom/convert.go b/pkg/services/ngalert/prom/convert.go index df0adedbff5..e4ab9ce0a60 100644 --- a/pkg/services/ngalert/prom/convert.go +++ b/pkg/services/ngalert/prom/convert.go @@ -231,10 +231,13 @@ func (p *Converter) convertRule(orgID int64, namespaceUID string, promGroup Prom title = rule.Alert } - labels := make(map[string]string, len(rule.Labels)+len(promGroup.Labels)) + labels := make(map[string]string, len(rule.Labels)+len(promGroup.Labels)+1) maps.Copy(labels, promGroup.Labels) maps.Copy(labels, rule.Labels) + // Add a special label to indicate that this rule was converted from a Prometheus rule. + labels[models.ConvertedPrometheusRuleLabel] = "true" + originalRuleDefinition, err := yaml.Marshal(rule) if err != nil { return models.AlertRule{}, fmt.Errorf("failed to marshal original rule definition: %w", err) diff --git a/pkg/services/ngalert/prom/convert_test.go b/pkg/services/ngalert/prom/convert_test.go index 9fed80776e7..1d4668d2b27 100644 --- a/pkg/services/ngalert/prom/convert_test.go +++ b/pkg/services/ngalert/prom/convert_test.go @@ -322,6 +322,7 @@ func TestPrometheusRulesToGrafana(t *testing.T) { expectedLabels := make(map[string]string, len(promRule.Labels)+len(tc.promGroup.Labels)) maps.Copy(expectedLabels, tc.promGroup.Labels) maps.Copy(expectedLabels, promRule.Labels) + expectedLabels = withInternalLabel(expectedLabels) uidData := fmt.Sprintf("%d|%s|%s|%d", tc.orgID, tc.namespace, tc.promGroup.Name, j) u := uuid.NewSHA1(uuid.NameSpaceOID, []byte(uidData)) @@ -550,11 +551,11 @@ func TestPrometheusRulesToGrafana_GroupLabels(t *testing.T) { // Check that the labels are merged and the rule label takes precedence require.Equal( t, - map[string]string{ + withInternalLabel(map[string]string{ "group_label": "group_value", "rule_label": "rule_value", "common_label": "rule_value", - }, + }), grafanaGroup.Rules[0].Labels, ) }) @@ -586,11 +587,11 @@ func TestPrometheusRulesToGrafana_GroupLabels(t *testing.T) { // Check that the labels are merged and the rule label takes precedence require.Equal( t, - map[string]string{ + withInternalLabel(map[string]string{ "group_label": "group_value", "rule_label": "rule_value", "common_label": "rule_value", - }, + }), grafanaGroup.Rules[0].Labels, ) }) @@ -614,8 +615,7 @@ func TestPrometheusRulesToGrafana_GroupLabels(t *testing.T) { grafanaGroup, err := converter.PrometheusRulesToGrafana(1, "namespace", promGroup) require.NoError(t, err) require.Len(t, grafanaGroup.Rules, 1) - - require.Equal(t, promGroup.Labels, grafanaGroup.Rules[0].Labels) + require.Equal(t, withInternalLabel(promGroup.Labels), grafanaGroup.Rules[0].Labels) }) t.Run("rule and group with nil labels", func(t *testing.T) { @@ -633,7 +633,7 @@ func TestPrometheusRulesToGrafana_GroupLabels(t *testing.T) { grafanaGroup, err := converter.PrometheusRulesToGrafana(1, "namespace", promGroup) require.NoError(t, err) require.Len(t, grafanaGroup.Rules, 1) - require.Empty(t, grafanaGroup.Rules[0].Labels) + require.Equal(t, withInternalLabel(map[string]string{}), grafanaGroup.Rules[0].Labels) }) } @@ -847,3 +847,12 @@ func TestQueryModelContainsRequiredParameters(t *testing.T) { require.True(t, isNumber, "maxDataPoints should be a number") } } + +func withInternalLabel(l map[string]string) map[string]string { + result := map[string]string{ + models.ConvertedPrometheusRuleLabel: "true", + } + maps.Copy(result, l) + + return result +}