diff --git a/pkg/services/ngalert/api/api_ruler.go b/pkg/services/ngalert/api/api_ruler.go index e79a280ca94..1b34667456e 100644 --- a/pkg/services/ngalert/api/api_ruler.go +++ b/pkg/services/ngalert/api/api_ruler.go @@ -230,6 +230,7 @@ func (srv RulerSrv) RoutePostNameRulesConfig(c *models.ReqContext, ruleGroupConf return response.Error(http.StatusBadRequest, "rule group name is not valid", nil) } + var alertRuleUIDs []string for _, r := range ruleGroupConfig.Rules { cond := ngmodels.Condition{ Condition: r.GrafanaManagedAlert.Condition, @@ -239,6 +240,7 @@ func (srv RulerSrv) RoutePostNameRulesConfig(c *models.ReqContext, ruleGroupConf if err := validateCondition(cond, c.SignedInUser, c.SkipCache, srv.DatasourceCache); err != nil { return response.Error(http.StatusBadRequest, fmt.Sprintf("failed to validate alert rule %s", r.GrafanaManagedAlert.Title), err) } + alertRuleUIDs = append(alertRuleUIDs, r.GrafanaManagedAlert.UID) } if err := srv.store.UpdateRuleGroup(store.UpdateRuleGroupCmd{ @@ -254,6 +256,10 @@ func (srv RulerSrv) RoutePostNameRulesConfig(c *models.ReqContext, ruleGroupConf return response.Error(http.StatusInternalServerError, "failed to update rule group", err) } + for _, uid := range alertRuleUIDs { + srv.manager.RemoveByRuleUID(c.OrgId, uid) + } + return response.JSON(http.StatusAccepted, util.DynMap{"message": "rule group updated successfully"}) } diff --git a/pkg/services/ngalert/store/alert_rule.go b/pkg/services/ngalert/store/alert_rule.go index 23fa5e68bb2..5b1e7cf2707 100644 --- a/pkg/services/ngalert/store/alert_rule.go +++ b/pkg/services/ngalert/store/alert_rule.go @@ -40,6 +40,7 @@ type RuleStore interface { DeleteAlertRuleByUID(orgID int64, ruleUID string) error DeleteNamespaceAlertRules(orgID int64, namespaceUID string) ([]string, error) DeleteRuleGroupAlertRules(orgID int64, namespaceUID string, ruleGroup string) ([]string, error) + DeleteAlertInstancesByRuleUID(orgID int64, ruleUID string) error GetAlertRuleByUID(*ngmodels.GetAlertRuleByUIDQuery) error GetAlertRulesForScheduling(query *ngmodels.ListAlertRulesQuery) error GetOrgAlertRules(query *ngmodels.ListAlertRulesQuery) error @@ -157,6 +158,17 @@ func (st DBstore) DeleteRuleGroupAlertRules(orgID int64, namespaceUID string, ru return ruleUIDs, err } +// DeleteAlertInstanceByRuleUID is a handler for deleting alert instances by alert rule UID when a rule has been updated +func (st DBstore) DeleteAlertInstancesByRuleUID(orgID int64, ruleUID string) error { + return st.SQLStore.WithTransactionalDbSession(context.Background(), func(sess *sqlstore.DBSession) error { + _, err := sess.Exec("DELETE FROM alert_instance WHERE def_org_id = ? AND def_uid = ?", orgID, ruleUID) + if err != nil { + return err + } + return nil + }) +} + // GetAlertRuleByUID is a handler for retrieving an alert rule from that database by its UID and organisation ID. // It returns ngmodels.ErrAlertRuleNotFound if no alert rule is found for the provided ID. func (st DBstore) GetAlertRuleByUID(query *ngmodels.GetAlertRuleByUIDQuery) error { @@ -507,6 +519,15 @@ func (st DBstore) UpdateRuleGroup(cmd UpdateRuleGroupCmd) error { return err } + // delete instances for rules that will not be removed + for _, rule := range existingGroupRules { + if _, ok := existingGroupRulesUIDs[rule.UID]; !ok { + if err := st.DeleteAlertInstancesByRuleUID(cmd.OrgID, rule.UID); err != nil { + return err + } + } + } + // delete the remaining rules for ruleUID := range existingGroupRulesUIDs { if err := st.DeleteAlertRuleByUID(cmd.OrgID, ruleUID); err != nil {