mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Alerting: Include rule uid, title, namespace in unique constraint errors (#82011)
* Alerting: Include rule_uid, title, namespace_uid in unique constraint errors
This commit is contained in:
parent
645684df15
commit
dd0ca1263b
@ -27,7 +27,7 @@ var (
|
||||
ErrCannotEditNamespace = errors.New("user does not have permissions to edit the namespace")
|
||||
ErrRuleGroupNamespaceNotFound = errors.New("rule group not found under this namespace")
|
||||
ErrAlertRuleFailedValidation = errors.New("invalid alert rule")
|
||||
ErrAlertRuleUniqueConstraintViolation = errors.New("a conflicting alert rule is found: rule title under the same organisation and folder should be unique")
|
||||
ErrAlertRuleUniqueConstraintViolation = errors.New("rule title under the same organisation and folder should be unique")
|
||||
ErrQuotaReached = errors.New("quota has been exceeded")
|
||||
// ErrNoDashboard is returned when the alert rule does not have a Dashboard UID
|
||||
// in its annotations or the dashboard does not exist.
|
||||
|
15
pkg/services/ngalert/models/errors.go
Normal file
15
pkg/services/ngalert/models/errors.go
Normal file
@ -0,0 +1,15 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"github.com/grafana/grafana/pkg/util/errutil"
|
||||
)
|
||||
|
||||
var (
|
||||
errAlertRuleConflictMsg = "conflicting alert rule found [rule_uid: '{{ .Public.RuleUID }}', title: '{{ .Public.Title }}', namespace_uid: '{{ .Public.NamespaceUID }}']: {{ .Public.Error }}"
|
||||
ErrAlertRuleConflictBase = errutil.Conflict("alerting.alert-rule.conflict").
|
||||
MustTemplate(errAlertRuleConflictMsg, errutil.WithPublic(errAlertRuleConflictMsg))
|
||||
)
|
||||
|
||||
func ErrAlertRuleConflict(rule AlertRule, underlying error) error {
|
||||
return ErrAlertRuleConflictBase.Build(errutil.TemplateData{Public: map[string]any{"RuleUID": rule.UID, "Title": rule.Title, "NamespaceUID": rule.NamespaceUID, "Error": underlying.Error()}, Error: underlying})
|
||||
}
|
@ -165,7 +165,7 @@ func (st DBstore) InsertAlertRules(ctx context.Context, rules []ngmodels.AlertRu
|
||||
for i := range newRules {
|
||||
if _, err := sess.Insert(&newRules[i]); err != nil {
|
||||
if st.SQLStore.GetDialect().IsUniqueConstraintViolation(err) {
|
||||
return ngmodels.ErrAlertRuleUniqueConstraintViolation
|
||||
return ngmodels.ErrAlertRuleConflict(newRules[i], ngmodels.ErrAlertRuleUniqueConstraintViolation)
|
||||
}
|
||||
return fmt.Errorf("failed to create new rules: %w", err)
|
||||
}
|
||||
@ -208,7 +208,7 @@ func (st DBstore) UpdateAlertRules(ctx context.Context, rules []ngmodels.UpdateR
|
||||
if updated, err := sess.ID(r.Existing.ID).AllCols().Update(r.New); err != nil || updated == 0 {
|
||||
if err != nil {
|
||||
if st.SQLStore.GetDialect().IsUniqueConstraintViolation(err) {
|
||||
return ngmodels.ErrAlertRuleUniqueConstraintViolation
|
||||
return ngmodels.ErrAlertRuleConflict(r.New, ngmodels.ErrAlertRuleUniqueConstraintViolation)
|
||||
}
|
||||
return fmt.Errorf("failed to update rule [%s] %s: %w", r.New.UID, r.New.Title, err)
|
||||
}
|
||||
|
@ -317,6 +317,28 @@ func TestIntegrationUpdateAlertRulesWithUniqueConstraintViolation(t *testing.T)
|
||||
require.Equal(t, newRule3.Title, dbrule3.Title)
|
||||
require.Equal(t, newRule4.Title, dbrule4.Title)
|
||||
})
|
||||
|
||||
t.Run("should fail with unique constraint violation", func(t *testing.T) {
|
||||
rule1 := createRuleInFolder("unique-rule1", 1, "my-namespace")
|
||||
rule2 := createRuleInFolder("unique-rule2", 1, "my-namespace")
|
||||
|
||||
newRule1 := models.CopyRule(rule1)
|
||||
newRule2 := models.CopyRule(rule2)
|
||||
newRule2.Title = newRule1.Title
|
||||
|
||||
err := store.UpdateAlertRules(context.Background(), []models.UpdateRule{{
|
||||
Existing: rule2,
|
||||
New: *newRule2,
|
||||
},
|
||||
})
|
||||
require.ErrorIs(t, err, models.ErrAlertRuleUniqueConstraintViolation)
|
||||
require.NotEqual(t, newRule2.UID, "")
|
||||
require.NotEqual(t, newRule2.Title, "")
|
||||
require.NotEqual(t, newRule2.NamespaceUID, "")
|
||||
require.ErrorContains(t, err, newRule2.UID)
|
||||
require.ErrorContains(t, err, newRule2.Title)
|
||||
require.ErrorContains(t, err, newRule2.NamespaceUID)
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntegration_GetAlertRulesForScheduling(t *testing.T) {
|
||||
@ -617,6 +639,15 @@ func TestIntegrationInsertAlertRules(t *testing.T) {
|
||||
}
|
||||
require.Truef(t, found, "Rule with key %#v was not found in database", keyWithID)
|
||||
}
|
||||
|
||||
_, err = store.InsertAlertRules(context.Background(), []models.AlertRule{deref[0]})
|
||||
require.ErrorIs(t, err, models.ErrAlertRuleUniqueConstraintViolation)
|
||||
require.NotEqual(t, deref[0].UID, "")
|
||||
require.NotEqual(t, deref[0].Title, "")
|
||||
require.NotEqual(t, deref[0].NamespaceUID, "")
|
||||
require.ErrorContains(t, err, deref[0].UID)
|
||||
require.ErrorContains(t, err, deref[0].Title)
|
||||
require.ErrorContains(t, err, deref[0].NamespaceUID)
|
||||
}
|
||||
|
||||
// createAlertRule creates an alert rule in the database and returns it.
|
||||
|
@ -841,11 +841,11 @@ func TestIntegrationAlertRuleConflictingTitle(t *testing.T) {
|
||||
rulesWithUID.Rules = append(rulesWithUID.Rules, rules.Rules[0]) // Create new copy of first rule.
|
||||
|
||||
_, status, body := apiClient.PostRulesGroupWithStatus(t, "folder1", &rulesWithUID)
|
||||
assert.Equal(t, http.StatusInternalServerError, status)
|
||||
assert.Equal(t, http.StatusConflict, status)
|
||||
|
||||
var res map[string]any
|
||||
require.NoError(t, json.Unmarshal([]byte(body), &res))
|
||||
require.Equal(t, "failed to update rule group: failed to add rules: a conflicting alert rule is found: rule title under the same organisation and folder should be unique", res["message"])
|
||||
require.Contains(t, res["message"], ngmodels.ErrAlertRuleUniqueConstraintViolation.Error())
|
||||
})
|
||||
|
||||
t.Run("trying to update an alert to the title of an existing alert in the same folder should fail", func(t *testing.T) {
|
||||
@ -853,11 +853,11 @@ func TestIntegrationAlertRuleConflictingTitle(t *testing.T) {
|
||||
rulesWithUID.Rules[1].GrafanaManagedAlert.Title = "AlwaysFiring"
|
||||
|
||||
_, status, body := apiClient.PostRulesGroupWithStatus(t, "folder1", &rulesWithUID)
|
||||
assert.Equal(t, http.StatusInternalServerError, status)
|
||||
assert.Equal(t, http.StatusConflict, status)
|
||||
|
||||
var res map[string]any
|
||||
require.NoError(t, json.Unmarshal([]byte(body), &res))
|
||||
require.Equal(t, "failed to update rule group: failed to update rules: a conflicting alert rule is found: rule title under the same organisation and folder should be unique", res["message"])
|
||||
require.Contains(t, res["message"], ngmodels.ErrAlertRuleUniqueConstraintViolation.Error())
|
||||
})
|
||||
|
||||
t.Run("trying to create alert with same title under another folder should succeed", func(t *testing.T) {
|
||||
|
Loading…
Reference in New Issue
Block a user