diff --git a/pkg/services/ngalert/api/api_convert_prometheus_test.go b/pkg/services/ngalert/api/api_convert_prometheus_test.go index e5833280319..35d3da6d72a 100644 --- a/pkg/services/ngalert/api/api_convert_prometheus_test.go +++ b/pkg/services/ngalert/api/api_convert_prometheus_test.go @@ -291,6 +291,30 @@ func TestRouteConvertPrometheusPostRuleGroup(t *testing.T) { } }) + t.Run("with empty rule group name should return 400", func(t *testing.T) { + srv, _, _ := createConvertPrometheusSrv(t) + rc := createRequestCtx() + + emptyNameGroup := apimodels.PrometheusRuleGroup{ + Name: "", + 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", emptyNameGroup) + require.Equal(t, http.StatusBadRequest, response.Status()) + require.Contains(t, string(response.Body()), "rule group name must not be empty") + }) + t.Run("with valid request should return 202", func(t *testing.T) { srv, _, _ := createConvertPrometheusSrv(t) rc := createRequestCtx() diff --git a/pkg/services/ngalert/prom/convert_test.go b/pkg/services/ngalert/prom/convert_test.go index 16b43e1a92d..befa524f468 100644 --- a/pkg/services/ngalert/prom/convert_test.go +++ b/pkg/services/ngalert/prom/convert_test.go @@ -143,6 +143,22 @@ func TestPrometheusRulesToGrafana(t *testing.T) { }, expectError: false, }, + { + name: "rule group with empty name", + orgID: 1, + namespace: "namespaceUID", + promGroup: PrometheusRuleGroup{ + Name: "", + Rules: []PrometheusRule{ + { + Alert: "alert-1", + Expr: "up == 0", + }, + }, + }, + expectError: true, + errorMsg: "rule group name must not be empty", + }, { name: "recording rule", orgID: 1, diff --git a/pkg/services/ngalert/prom/models.go b/pkg/services/ngalert/prom/models.go index 1ac5ad1d0d9..1664a31c1b6 100644 --- a/pkg/services/ngalert/prom/models.go +++ b/pkg/services/ngalert/prom/models.go @@ -7,10 +7,14 @@ import ( ) var ( - ErrPrometheusRuleValidationFailed = errutil.ValidationFailed("alerting.prometheusRuleInvalid") - ErrPrometheusRuleGroupValidationFailed = errutil.ValidationFailed("alerting.prometheusRuleGroupInvalid") + errPrometheusRuleGroupValidationFailedMsg = "{{.Public.Message}}" + ErrPrometheusRuleGroupValidationFailed = errutil.ValidationFailed("alerting.prometheusRuleGroupInvalid").MustTemplate(errPrometheusRuleGroupValidationFailedMsg, errutil.WithPublic(errPrometheusRuleGroupValidationFailedMsg)) ) +func errPrometheusRuleGroupValidationFailed(message string) error { + return ErrPrometheusRuleGroupValidationFailed.Build(errutil.TemplateData{Public: map[string]any{"Message": message}}) +} + type PrometheusRulesFile struct { Groups []PrometheusRuleGroup `yaml:"groups"` } @@ -25,12 +29,16 @@ type PrometheusRuleGroup struct { } func (g *PrometheusRuleGroup) Validate() error { + if g.Name == "" { + return errPrometheusRuleGroupValidationFailed("rule group name must not be empty") + } + if g.Limit != 0 { - return ErrPrometheusRuleGroupValidationFailed.Errorf("limit is not supported") + return errPrometheusRuleGroupValidationFailed("limit is not supported") } if g.QueryOffset != nil && *g.QueryOffset < prommodel.Duration(0) { - return ErrPrometheusRuleGroupValidationFailed.Errorf("query_offset must be >= 0") + return errPrometheusRuleGroupValidationFailed("query_offset must be >= 0") } return nil