Alerting: Distinguish conflict violation errors (#86634)
* update generator to set ID = 0 and do not set 0 if unique is needed * return proper message when the constraint violation
This commit is contained in:
@@ -347,7 +347,7 @@ func TestPatchPartialAlertRule(t *testing.T) {
|
|||||||
t.Run(testCase.name, func(t *testing.T) {
|
t.Run(testCase.name, func(t *testing.T) {
|
||||||
var existing *AlertRule
|
var existing *AlertRule
|
||||||
for {
|
for {
|
||||||
existing = AlertRuleGen()()
|
existing = AlertRuleGen(WithUniqueID())()
|
||||||
cloned := *existing
|
cloned := *existing
|
||||||
// make sure the generated rule does not match the mutated one
|
// make sure the generated rule does not match the mutated one
|
||||||
testCase.mutator(&cloned)
|
testCase.mutator(&cloned)
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ func AlertRuleGen(mutators ...AlertRuleMutator) func() *AlertRule {
|
|||||||
}
|
}
|
||||||
|
|
||||||
rule := &AlertRule{
|
rule := &AlertRule{
|
||||||
ID: rand.Int63n(1500),
|
ID: 0,
|
||||||
OrgID: rand.Int63n(1500) + 1, // Prevent OrgID=0 as this does not pass alert rule validation.
|
OrgID: rand.Int63n(1500) + 1, // Prevent OrgID=0 as this does not pass alert rule validation.
|
||||||
Title: "TEST-ALERT-" + util.GenerateShortUID(),
|
Title: "TEST-ALERT-" + util.GenerateShortUID(),
|
||||||
Condition: "A",
|
Condition: "A",
|
||||||
@@ -110,7 +110,7 @@ func WithUniqueID() AlertRuleMutator {
|
|||||||
usedID := make(map[int64]struct{})
|
usedID := make(map[int64]struct{})
|
||||||
return func(rule *AlertRule) {
|
return func(rule *AlertRule) {
|
||||||
for {
|
for {
|
||||||
id := rand.Int63n(1500)
|
id := rand.Int63n(1500) + 1
|
||||||
if _, ok := usedID[id]; !ok {
|
if _, ok := usedID[id]; !ok {
|
||||||
usedID[id] = struct{}{}
|
usedID[id] = struct{}{}
|
||||||
rule.ID = id
|
rule.ID = id
|
||||||
|
|||||||
@@ -169,7 +169,7 @@ func (st DBstore) InsertAlertRules(ctx context.Context, rules []ngmodels.AlertRu
|
|||||||
for i := range newRules {
|
for i := range newRules {
|
||||||
if _, err := sess.Insert(&newRules[i]); err != nil {
|
if _, err := sess.Insert(&newRules[i]); err != nil {
|
||||||
if st.SQLStore.GetDialect().IsUniqueConstraintViolation(err) {
|
if st.SQLStore.GetDialect().IsUniqueConstraintViolation(err) {
|
||||||
return ngmodels.ErrAlertRuleConflict(newRules[i], ngmodels.ErrAlertRuleUniqueConstraintViolation)
|
return ruleConstraintViolationToErr(newRules[i], err)
|
||||||
}
|
}
|
||||||
return fmt.Errorf("failed to create new rules: %w", err)
|
return fmt.Errorf("failed to create new rules: %w", err)
|
||||||
}
|
}
|
||||||
@@ -215,7 +215,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 updated, err := sess.ID(r.Existing.ID).AllCols().Update(r.New); err != nil || updated == 0 {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if st.SQLStore.GetDialect().IsUniqueConstraintViolation(err) {
|
if st.SQLStore.GetDialect().IsUniqueConstraintViolation(err) {
|
||||||
return ngmodels.ErrAlertRuleConflict(r.New, ngmodels.ErrAlertRuleUniqueConstraintViolation)
|
return ruleConstraintViolationToErr(r.New, err)
|
||||||
}
|
}
|
||||||
return fmt.Errorf("failed to update rule [%s] %s: %w", r.New.UID, r.New.Title, err)
|
return fmt.Errorf("failed to update rule [%s] %s: %w", r.New.UID, r.New.Title, err)
|
||||||
}
|
}
|
||||||
@@ -758,3 +758,14 @@ func (st DBstore) RenameReceiverInNotificationSettings(ctx context.Context, orgI
|
|||||||
}
|
}
|
||||||
return len(updates), st.UpdateAlertRules(ctx, updates)
|
return len(updates), st.UpdateAlertRules(ctx, updates)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ruleConstraintViolationToErr(rule ngmodels.AlertRule, err error) error {
|
||||||
|
msg := err.Error()
|
||||||
|
if strings.Contains(msg, "UQE_alert_rule_org_id_namespace_uid_title") || strings.Contains(msg, "alert_rule.org_id, alert_rule.namespace_uid, alert_rule.title") {
|
||||||
|
return ngmodels.ErrAlertRuleConflict(rule, ngmodels.ErrAlertRuleUniqueConstraintViolation)
|
||||||
|
} else if strings.Contains(msg, "UQE_alert_rule_org_id_uid") || strings.Contains(msg, "alert_rule.org_id, alert_rule.uid") {
|
||||||
|
return ngmodels.ErrAlertRuleConflict(rule, errors.New("rule UID under the same organisation should be unique"))
|
||||||
|
} else {
|
||||||
|
return ngmodels.ErrAlertRuleConflict(rule, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -603,6 +603,7 @@ func TestIntegrationInsertAlertRules(t *testing.T) {
|
|||||||
t.Skip("skipping integration test")
|
t.Skip("skipping integration test")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
orgID := int64(1)
|
||||||
sqlStore := db.InitTestDB(t)
|
sqlStore := db.InitTestDB(t)
|
||||||
cfg := setting.NewCfg()
|
cfg := setting.NewCfg()
|
||||||
cfg.UnifiedAlerting.BaseInterval = 1 * time.Second
|
cfg.UnifiedAlerting.BaseInterval = 1 * time.Second
|
||||||
@@ -613,7 +614,7 @@ func TestIntegrationInsertAlertRules(t *testing.T) {
|
|||||||
Cfg: cfg.UnifiedAlerting,
|
Cfg: cfg.UnifiedAlerting,
|
||||||
}
|
}
|
||||||
|
|
||||||
rules := models.GenerateAlertRules(5, models.AlertRuleGen(models.WithOrgID(1), withIntervalMatching(store.Cfg.BaseInterval)))
|
rules := models.GenerateAlertRules(5, models.AlertRuleGen(models.WithOrgID(orgID), withIntervalMatching(store.Cfg.BaseInterval)))
|
||||||
deref := make([]models.AlertRule, 0, len(rules))
|
deref := make([]models.AlertRule, 0, len(rules))
|
||||||
for _, rule := range rules {
|
for _, rule := range rules {
|
||||||
deref = append(deref, *rule)
|
deref = append(deref, *rule)
|
||||||
@@ -641,14 +642,30 @@ func TestIntegrationInsertAlertRules(t *testing.T) {
|
|||||||
require.Truef(t, found, "Rule with key %#v was not found in database", keyWithID)
|
require.Truef(t, found, "Rule with key %#v was not found in database", keyWithID)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = store.InsertAlertRules(context.Background(), []models.AlertRule{deref[0]})
|
t.Run("fail to insert rules with same ID", func(t *testing.T) {
|
||||||
require.ErrorIs(t, err, models.ErrAlertRuleUniqueConstraintViolation)
|
_, err = store.InsertAlertRules(context.Background(), []models.AlertRule{deref[0]})
|
||||||
require.NotEqual(t, deref[0].UID, "")
|
require.ErrorIs(t, err, models.ErrAlertRuleConflictBase)
|
||||||
require.NotEqual(t, deref[0].Title, "")
|
})
|
||||||
require.NotEqual(t, deref[0].NamespaceUID, "")
|
t.Run("fail insert rules with the same title in a folder", func(t *testing.T) {
|
||||||
require.ErrorContains(t, err, deref[0].UID)
|
cp := models.CopyRule(&deref[0])
|
||||||
require.ErrorContains(t, err, deref[0].Title)
|
cp.UID = cp.UID + "-new"
|
||||||
require.ErrorContains(t, err, deref[0].NamespaceUID)
|
_, err = store.InsertAlertRules(context.Background(), []models.AlertRule{*cp})
|
||||||
|
require.ErrorIs(t, err, models.ErrAlertRuleConflictBase)
|
||||||
|
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)
|
||||||
|
})
|
||||||
|
t.Run("should not let insert rules with the same UID", func(t *testing.T) {
|
||||||
|
cp := models.CopyRule(&deref[0])
|
||||||
|
cp.Title = "unique-test-title"
|
||||||
|
_, err = store.InsertAlertRules(context.Background(), []models.AlertRule{*cp})
|
||||||
|
require.ErrorIs(t, err, models.ErrAlertRuleConflictBase)
|
||||||
|
require.ErrorContains(t, err, "rule UID under the same organisation should be unique")
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestIntegrationAlertRulesNotificationSettings(t *testing.T) {
|
func TestIntegrationAlertRulesNotificationSettings(t *testing.T) {
|
||||||
|
|||||||
Reference in New Issue
Block a user