Alerting: Support simplified routing receivers in Prometheus conversion API (#105135)

Adds ability to set notifications settings using the Prometheus conversion API.

The API now supports a new optional header: X-Grafana-Alerting-Notification-Settings which can be used to specify notification settings.

The value of the header is the AlertRuleNotificationSettings structure in JSON:
mimirtool rules load alerts.yaml --extra-headers 'X-Grafana-Alerting-Notification-Settings: {"receiver": "my-webhook", "group_by": ["cluster", "pod"]}'
This commit is contained in:
Alexander Akhmetov
2025-05-12 22:07:02 +02:00
committed by GitHub
parent e965b85e19
commit c17b019ab1
12 changed files with 559 additions and 25 deletions
@@ -2,6 +2,7 @@ package api
import (
"context"
"encoding/json"
"net/http"
"net/http/httptest"
"testing"
@@ -425,6 +426,97 @@ func TestRouteConvertPrometheusPostRuleGroup(t *testing.T) {
require.NotNil(t, remaining[0].Record)
require.Equal(t, targetDSUID, remaining[0].Record.TargetDatasourceUID)
})
t.Run("sets notification settings for rules if specified", func(t *testing.T) {
srv, _, ruleStore, _ := createConvertPrometheusSrv(t)
rc := createRequestCtx()
receiver := "test-receiver"
groupBy := []string{"cluster", "pod"}
settings := apimodels.AlertRuleNotificationSettings{
Receiver: receiver,
GroupBy: groupBy,
}
settingsJSON, err := json.Marshal(settings)
require.NoError(t, err)
rc.Req.Header.Set(notificationSettingsHeader, string(settingsJSON))
simpleGroup := apimodels.PrometheusRuleGroup{
Name: "Test Group",
Interval: prommodel.Duration(1 * time.Minute),
Rules: []apimodels.PrometheusRule{
{
Alert: "TestAlert",
Expr: "up == 0",
For: util.Pointer(prommodel.Duration(5 * time.Minute)),
Labels: map[string]string{
"severity": "critical",
},
},
},
}
response := srv.RouteConvertPrometheusPostRuleGroup(rc, "test", simpleGroup)
require.Equal(t, http.StatusAccepted, response.Status())
createdRules, err := ruleStore.ListAlertRules(context.Background(), &models.ListAlertRulesQuery{
OrgID: 1,
})
require.NoError(t, err)
require.Len(t, createdRules, 1)
require.Len(t, createdRules[0].NotificationSettings, 1)
require.Equal(t, receiver, createdRules[0].NotificationSettings[0].Receiver)
require.Equal(t, groupBy, createdRules[0].NotificationSettings[0].GroupBy)
})
t.Run("returns error when notification settings header contains invalid JSON", func(t *testing.T) {
srv, _, _, _ := createConvertPrometheusSrv(t)
rc := createRequestCtx()
rc.Req.Header.Set(notificationSettingsHeader, "{invalid json")
simpleGroup := apimodels.PrometheusRuleGroup{
Name: "Test Group",
Interval: prommodel.Duration(1 * time.Minute),
Rules: []apimodels.PrometheusRule{
{
Alert: "TestAlert",
Expr: "up == 0",
},
},
}
response := srv.RouteConvertPrometheusPostRuleGroup(rc, "test", simpleGroup)
require.Equal(t, http.StatusBadRequest, response.Status())
require.Contains(t, string(response.Body()), "Invalid value for header X-Grafana-Alerting-Notification-Settings")
})
t.Run("returns error when notification settings contain invalid values", func(t *testing.T) {
srv, _, _, _ := createConvertPrometheusSrv(t)
rc := createRequestCtx()
settings := apimodels.AlertRuleNotificationSettings{
Receiver: "", // empty receiver is invalid
}
settingsJSON, err := json.Marshal(settings)
require.NoError(t, err)
rc.Req.Header.Set(notificationSettingsHeader, string(settingsJSON))
simpleGroup := apimodels.PrometheusRuleGroup{
Name: "Test Group",
Interval: prommodel.Duration(1 * time.Minute),
Rules: []apimodels.PrometheusRule{
{
Alert: "TestAlert",
Expr: "up == 0",
},
},
}
response := srv.RouteConvertPrometheusPostRuleGroup(rc, "test", simpleGroup)
require.Equal(t, http.StatusBadRequest, response.Status())
require.Contains(t, string(response.Body()), "Invalid value for header X-Grafana-Alerting-Notification-Settings")
})
}
func TestRouteConvertPrometheusGetRuleGroup(t *testing.T) {