diff --git a/pkg/services/ngalert/remote/alertmanager.go b/pkg/services/ngalert/remote/alertmanager.go index e57bd723696..87bfc8a1f6e 100644 --- a/pkg/services/ngalert/remote/alertmanager.go +++ b/pkg/services/ngalert/remote/alertmanager.go @@ -19,6 +19,7 @@ import ( "github.com/prometheus/client_golang/prometheus" alertingClusterPB "github.com/grafana/alerting/cluster/clusterpb" + alertingModels "github.com/grafana/alerting/models" alertingNotify "github.com/grafana/alerting/notify" "gopkg.in/yaml.v3" @@ -467,6 +468,16 @@ func (am *Alertmanager) GetAlertGroups(ctx context.Context, active, silenced, in } func (am *Alertmanager) PutAlerts(ctx context.Context, alerts apimodels.PostableAlerts) error { + for _, a := range alerts.PostableAlerts { + for k, v := range a.Labels { + // The Grafana Alertmanager skips empty and namespace UID labels. + // To get the same alert fingerprint we need to remove these labels too. + // https://github.com/grafana/alerting/blob/2dda1c67ec02625ac9fc8607157b3d5825d47919/notify/grafana_alertmanager.go#L722-L724 + if len(v) == 0 || k == alertingModels.NamespaceUIDLabel { + delete(a.Labels, k) + } + } + } am.log.Debug("Sending alerts to a remote alertmanager", "url", am.url, "alerts", len(alerts.PostableAlerts)) am.sender.SendAlerts(alerts) return nil diff --git a/pkg/services/ngalert/remote/alertmanager_test.go b/pkg/services/ngalert/remote/alertmanager_test.go index ec3296badbc..b04c7194ee0 100644 --- a/pkg/services/ngalert/remote/alertmanager_test.go +++ b/pkg/services/ngalert/remote/alertmanager_test.go @@ -21,6 +21,7 @@ import ( "github.com/stretchr/testify/require" "github.com/grafana/alerting/definition" + alertingModels "github.com/grafana/alerting/models" "github.com/grafana/grafana/pkg/infra/db" "github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/infra/tracing" @@ -668,9 +669,9 @@ func TestIntegrationRemoteAlertmanagerAlerts(t *testing.T) { require.Equal(t, 0, len(alertGroups)) // Let's create two active alerts and one expired one. - alert1 := genAlert(true, map[string]string{"test_1": "test_1"}) - alert2 := genAlert(true, map[string]string{"test_2": "test_2"}) - alert3 := genAlert(false, map[string]string{"test_3": "test_3"}) + alert1 := genAlert(true, map[string]string{"test_1": "test_1", "empty": "", alertingModels.NamespaceUIDLabel: "test_1"}) + alert2 := genAlert(true, map[string]string{"test_2": "test_2", "empty": "", alertingModels.NamespaceUIDLabel: "test_2"}) + alert3 := genAlert(false, map[string]string{"test_3": "test_3", "empty": "", alertingModels.NamespaceUIDLabel: "test_3"}) postableAlerts := apimodels.PostableAlerts{ PostableAlerts: []amv2.PostableAlert{alert1, alert2, alert3}, } @@ -688,6 +689,12 @@ func TestIntegrationRemoteAlertmanagerAlerts(t *testing.T) { require.NoError(t, err) require.Equal(t, 1, len(alertGroups)) + // Labels with empty values and the namespace UID label should be removed. + for _, a := range alertGroups { + require.NotContains(t, a.Labels, "empty") + require.NotContains(t, a.Labels, alertingModels.NamespaceUIDLabel) + } + // Filtering by `test_1=test_1` should return one alert. alerts, err = am.GetAlerts(context.Background(), true, true, true, []string{"test_1=test_1"}, "") require.NoError(t, err)