From f7d836feedbb78e9df29ae1f24670c98dee6eaa2 Mon Sep 17 00:00:00 2001 From: Yuri Tseretyan Date: Thu, 14 Mar 2024 12:04:10 -0400 Subject: [PATCH] Alerting: Update rule provisioning service to accept user (#84480) --- pkg/services/ngalert/api/api_provisioning.go | 42 +++--- .../ngalert/provisioning/alert_rules.go | 99 ++++++------- .../ngalert/provisioning/alert_rules_test.go | 134 +++++++++--------- .../alerting/rules_provisioner.go | 23 +-- 4 files changed, 156 insertions(+), 142 deletions(-) diff --git a/pkg/services/ngalert/api/api_provisioning.go b/pkg/services/ngalert/api/api_provisioning.go index 91b6d51af0bd..d4425cf678ae 100644 --- a/pkg/services/ngalert/api/api_provisioning.go +++ b/pkg/services/ngalert/api/api_provisioning.go @@ -58,17 +58,17 @@ type MuteTimingService interface { } type AlertRuleService interface { - GetAlertRules(ctx context.Context, orgID int64) ([]*alerting_models.AlertRule, map[string]alerting_models.Provenance, error) - GetAlertRule(ctx context.Context, orgID int64, ruleUID string) (alerting_models.AlertRule, alerting_models.Provenance, error) + GetAlertRules(ctx context.Context, user identity.Requester) ([]*alerting_models.AlertRule, map[string]alerting_models.Provenance, error) + GetAlertRule(ctx context.Context, user identity.Requester, ruleUID string) (alerting_models.AlertRule, alerting_models.Provenance, error) CreateAlertRule(ctx context.Context, user identity.Requester, rule alerting_models.AlertRule, provenance alerting_models.Provenance) (alerting_models.AlertRule, error) - UpdateAlertRule(ctx context.Context, rule alerting_models.AlertRule, provenance alerting_models.Provenance) (alerting_models.AlertRule, error) - DeleteAlertRule(ctx context.Context, orgID int64, ruleUID string, provenance alerting_models.Provenance) error - GetRuleGroup(ctx context.Context, orgID int64, folder, group string) (alerting_models.AlertRuleGroup, error) - ReplaceRuleGroup(ctx context.Context, orgID int64, group alerting_models.AlertRuleGroup, userID int64, provenance alerting_models.Provenance) error - DeleteRuleGroup(ctx context.Context, orgID int64, folder, group string, provenance alerting_models.Provenance) error - GetAlertRuleWithFolderTitle(ctx context.Context, orgID int64, ruleUID string) (provisioning.AlertRuleWithFolderTitle, error) - GetAlertRuleGroupWithFolderTitle(ctx context.Context, orgID int64, folder, group string) (alerting_models.AlertRuleGroupWithFolderTitle, error) - GetAlertGroupsWithFolderTitle(ctx context.Context, orgID int64, folderUIDs []string) ([]alerting_models.AlertRuleGroupWithFolderTitle, error) + UpdateAlertRule(ctx context.Context, user identity.Requester, rule alerting_models.AlertRule, provenance alerting_models.Provenance) (alerting_models.AlertRule, error) + DeleteAlertRule(ctx context.Context, user identity.Requester, ruleUID string, provenance alerting_models.Provenance) error + GetRuleGroup(ctx context.Context, user identity.Requester, folder, group string) (alerting_models.AlertRuleGroup, error) + ReplaceRuleGroup(ctx context.Context, user identity.Requester, group alerting_models.AlertRuleGroup, provenance alerting_models.Provenance) error + DeleteRuleGroup(ctx context.Context, user identity.Requester, folder, group string, provenance alerting_models.Provenance) error + GetAlertRuleWithFolderTitle(ctx context.Context, user identity.Requester, ruleUID string) (provisioning.AlertRuleWithFolderTitle, error) + GetAlertRuleGroupWithFolderTitle(ctx context.Context, user identity.Requester, folder, group string) (alerting_models.AlertRuleGroupWithFolderTitle, error) + GetAlertGroupsWithFolderTitle(ctx context.Context, user identity.Requester, folderUIDs []string) ([]alerting_models.AlertRuleGroupWithFolderTitle, error) } func (srv *ProvisioningSrv) RouteGetPolicyTree(c *contextmodel.ReqContext) response.Response { @@ -309,7 +309,7 @@ func (srv *ProvisioningSrv) RouteDeleteMuteTiming(c *contextmodel.ReqContext, na } func (srv *ProvisioningSrv) RouteGetAlertRules(c *contextmodel.ReqContext) response.Response { - rules, provenances, err := srv.alertRules.GetAlertRules(c.Req.Context(), c.SignedInUser.GetOrgID()) + rules, provenances, err := srv.alertRules.GetAlertRules(c.Req.Context(), c.SignedInUser) if err != nil { return ErrResp(http.StatusInternalServerError, err, "") } @@ -317,7 +317,7 @@ func (srv *ProvisioningSrv) RouteGetAlertRules(c *contextmodel.ReqContext) respo } func (srv *ProvisioningSrv) RouteRouteGetAlertRule(c *contextmodel.ReqContext, UID string) response.Response { - rule, provenace, err := srv.alertRules.GetAlertRule(c.Req.Context(), c.SignedInUser.GetOrgID(), UID) + rule, provenace, err := srv.alertRules.GetAlertRule(c.Req.Context(), c.SignedInUser, UID) if err != nil { if errors.Is(err, alerting_models.ErrAlertRuleNotFound) { return response.Empty(http.StatusNotFound) @@ -363,7 +363,7 @@ func (srv *ProvisioningSrv) RoutePutAlertRule(c *contextmodel.ReqContext, ar def updated.OrgID = c.SignedInUser.GetOrgID() updated.UID = UID provenance := determineProvenance(c) - updatedAlertRule, err := srv.alertRules.UpdateAlertRule(c.Req.Context(), updated, alerting_models.Provenance(provenance)) + updatedAlertRule, err := srv.alertRules.UpdateAlertRule(c.Req.Context(), c.SignedInUser, updated, alerting_models.Provenance(provenance)) if errors.Is(err, alerting_models.ErrAlertRuleUniqueConstraintViolation) { return ErrResp(http.StatusBadRequest, err, "") } @@ -386,7 +386,7 @@ func (srv *ProvisioningSrv) RoutePutAlertRule(c *contextmodel.ReqContext, ar def func (srv *ProvisioningSrv) RouteDeleteAlertRule(c *contextmodel.ReqContext, UID string) response.Response { provenance := determineProvenance(c) - err := srv.alertRules.DeleteAlertRule(c.Req.Context(), c.SignedInUser.GetOrgID(), UID, alerting_models.Provenance(provenance)) + err := srv.alertRules.DeleteAlertRule(c.Req.Context(), c.SignedInUser, UID, alerting_models.Provenance(provenance)) if err != nil { return ErrResp(http.StatusInternalServerError, err, "") } @@ -394,7 +394,7 @@ func (srv *ProvisioningSrv) RouteDeleteAlertRule(c *contextmodel.ReqContext, UID } func (srv *ProvisioningSrv) RouteGetAlertRuleGroup(c *contextmodel.ReqContext, folder string, group string) response.Response { - g, err := srv.alertRules.GetRuleGroup(c.Req.Context(), c.SignedInUser.GetOrgID(), folder, group) + g, err := srv.alertRules.GetRuleGroup(c.Req.Context(), c.SignedInUser, folder, group) if err != nil { return response.ErrOrFallback(http.StatusInternalServerError, "", err) } @@ -422,7 +422,7 @@ func (srv *ProvisioningSrv) RouteGetAlertRulesExport(c *contextmodel.ReqContext) return srv.RouteGetAlertRuleGroupExport(c, folderUIDs[0], group) } - groupsWithTitle, err := srv.alertRules.GetAlertGroupsWithFolderTitle(c.Req.Context(), c.SignedInUser.GetOrgID(), folderUIDs) + groupsWithTitle, err := srv.alertRules.GetAlertGroupsWithFolderTitle(c.Req.Context(), c.SignedInUser, folderUIDs) if err != nil { return ErrResp(http.StatusInternalServerError, err, "failed to get alert rules") } @@ -440,7 +440,7 @@ func (srv *ProvisioningSrv) RouteGetAlertRulesExport(c *contextmodel.ReqContext) // RouteGetAlertRuleGroupExport retrieves the given alert rule group in a format compatible with file provisioning. func (srv *ProvisioningSrv) RouteGetAlertRuleGroupExport(c *contextmodel.ReqContext, folder string, group string) response.Response { - g, err := srv.alertRules.GetAlertRuleGroupWithFolderTitle(c.Req.Context(), c.SignedInUser.GetOrgID(), folder, group) + g, err := srv.alertRules.GetAlertRuleGroupWithFolderTitle(c.Req.Context(), c.SignedInUser, folder, group) if err != nil { return response.ErrOrFallback(http.StatusInternalServerError, "failed to get alert rule group", err) } @@ -455,7 +455,7 @@ func (srv *ProvisioningSrv) RouteGetAlertRuleGroupExport(c *contextmodel.ReqCont // RouteGetAlertRuleExport retrieves the given alert rule in a format compatible with file provisioning. func (srv *ProvisioningSrv) RouteGetAlertRuleExport(c *contextmodel.ReqContext, UID string) response.Response { - rule, err := srv.alertRules.GetAlertRuleWithFolderTitle(c.Req.Context(), c.SignedInUser.GetOrgID(), UID) + rule, err := srv.alertRules.GetAlertRuleWithFolderTitle(c.Req.Context(), c.SignedInUser, UID) if err != nil { if errors.Is(err, alerting_models.ErrAlertRuleNotFound) { return ErrResp(http.StatusNotFound, err, "") @@ -481,9 +481,7 @@ func (srv *ProvisioningSrv) RoutePutAlertRuleGroup(c *contextmodel.ReqContext, a ErrResp(http.StatusBadRequest, err, "") } provenance := determineProvenance(c) - - userID, _ := identity.UserIdentifier(c.SignedInUser.GetNamespacedID()) - err = srv.alertRules.ReplaceRuleGroup(c.Req.Context(), c.SignedInUser.GetOrgID(), groupModel, userID, alerting_models.Provenance(provenance)) + err = srv.alertRules.ReplaceRuleGroup(c.Req.Context(), c.SignedInUser, groupModel, alerting_models.Provenance(provenance)) if errors.Is(err, alerting_models.ErrAlertRuleUniqueConstraintViolation) { return ErrResp(http.StatusBadRequest, err, "") } @@ -501,7 +499,7 @@ func (srv *ProvisioningSrv) RoutePutAlertRuleGroup(c *contextmodel.ReqContext, a func (srv *ProvisioningSrv) RouteDeleteAlertRuleGroup(c *contextmodel.ReqContext, folderUID string, group string) response.Response { provenance := determineProvenance(c) - err := srv.alertRules.DeleteRuleGroup(c.Req.Context(), c.SignedInUser.GetOrgID(), folderUID, group, alerting_models.Provenance(provenance)) + err := srv.alertRules.DeleteRuleGroup(c.Req.Context(), c.SignedInUser, folderUID, group, alerting_models.Provenance(provenance)) if err != nil { return response.ErrOrFallback(http.StatusInternalServerError, "", err) } diff --git a/pkg/services/ngalert/provisioning/alert_rules.go b/pkg/services/ngalert/provisioning/alert_rules.go index 7ce5ac32a9f3..20c6046028fc 100644 --- a/pkg/services/ngalert/provisioning/alert_rules.go +++ b/pkg/services/ngalert/provisioning/alert_rules.go @@ -62,9 +62,9 @@ func NewAlertRuleService(ruleStore RuleStore, } } -func (service *AlertRuleService) GetAlertRules(ctx context.Context, orgID int64) ([]*models.AlertRule, map[string]models.Provenance, error) { +func (service *AlertRuleService) GetAlertRules(ctx context.Context, user identity.Requester) ([]*models.AlertRule, map[string]models.Provenance, error) { q := models.ListAlertRulesQuery{ - OrgID: orgID, + OrgID: user.GetOrgID(), } rules, err := service.ruleStore.ListAlertRules(ctx, &q) if err != nil { @@ -73,7 +73,7 @@ func (service *AlertRuleService) GetAlertRules(ctx context.Context, orgID int64) provenances := make(map[string]models.Provenance) if len(rules) > 0 { resourceType := rules[0].ResourceType() - provenances, err = service.provenanceStore.GetProvenances(ctx, orgID, resourceType) + provenances, err = service.provenanceStore.GetProvenances(ctx, user.GetOrgID(), resourceType) if err != nil { return nil, nil, err } @@ -81,16 +81,16 @@ func (service *AlertRuleService) GetAlertRules(ctx context.Context, orgID int64) return rules, provenances, nil } -func (service *AlertRuleService) GetAlertRule(ctx context.Context, orgID int64, ruleUID string) (models.AlertRule, models.Provenance, error) { +func (service *AlertRuleService) GetAlertRule(ctx context.Context, user identity.Requester, ruleUID string) (models.AlertRule, models.Provenance, error) { query := &models.GetAlertRuleByUIDQuery{ - OrgID: orgID, + OrgID: user.GetOrgID(), UID: ruleUID, } rule, err := service.ruleStore.GetAlertRuleByUID(ctx, query) if err != nil { return models.AlertRule{}, models.ProvenanceNone, err } - provenance, err := service.provenanceStore.GetProvenance(ctx, rule, orgID) + provenance, err := service.provenanceStore.GetProvenance(ctx, rule, user.GetOrgID()) if err != nil { return models.AlertRule{}, models.ProvenanceNone, err } @@ -103,9 +103,9 @@ type AlertRuleWithFolderTitle struct { } // GetAlertRuleWithFolderTitle returns a single alert rule with its folder title. -func (service *AlertRuleService) GetAlertRuleWithFolderTitle(ctx context.Context, orgID int64, ruleUID string) (AlertRuleWithFolderTitle, error) { +func (service *AlertRuleService) GetAlertRuleWithFolderTitle(ctx context.Context, user identity.Requester, ruleUID string) (AlertRuleWithFolderTitle, error) { query := &models.GetAlertRuleByUIDQuery{ - OrgID: orgID, + OrgID: user.GetOrgID(), UID: ruleUID, } rule, err := service.ruleStore.GetAlertRuleByUID(ctx, query) @@ -114,7 +114,7 @@ func (service *AlertRuleService) GetAlertRuleWithFolderTitle(ctx context.Context } dq := dashboards.GetDashboardQuery{ - OrgID: orgID, + OrgID: user.GetOrgID(), UID: rule.NamespaceUID, } @@ -184,12 +184,7 @@ func (service *AlertRuleService) CreateAlertRule(ctx context.Context, user ident return errors.New("couldn't find newly created id") } - // default to 0 if there is no user - userID := int64(0) - if user != nil { - userID, _ = identity.UserIdentifier(user.GetNamespacedID()) - } - if err = service.checkLimitsTransactionCtx(ctx, rule.OrgID, userID); err != nil { + if err = service.checkLimitsTransactionCtx(ctx, user); err != nil { return err } @@ -201,9 +196,9 @@ func (service *AlertRuleService) CreateAlertRule(ctx context.Context, user ident return rule, nil } -func (service *AlertRuleService) GetRuleGroup(ctx context.Context, orgID int64, namespaceUID, group string) (models.AlertRuleGroup, error) { +func (service *AlertRuleService) GetRuleGroup(ctx context.Context, user identity.Requester, namespaceUID, group string) (models.AlertRuleGroup, error) { q := models.ListAlertRulesQuery{ - OrgID: orgID, + OrgID: user.GetOrgID(), NamespaceUIDs: []string{namespaceUID}, RuleGroup: group, } @@ -229,13 +224,13 @@ func (service *AlertRuleService) GetRuleGroup(ctx context.Context, orgID int64, } // UpdateRuleGroup will update the interval for all rules in the group. -func (service *AlertRuleService) UpdateRuleGroup(ctx context.Context, orgID int64, namespaceUID string, ruleGroup string, intervalSeconds int64) error { +func (service *AlertRuleService) UpdateRuleGroup(ctx context.Context, user identity.Requester, namespaceUID string, ruleGroup string, intervalSeconds int64) error { if err := models.ValidateRuleGroupInterval(intervalSeconds, service.baseIntervalSeconds); err != nil { return err } return service.xact.InTransaction(ctx, func(ctx context.Context) error { query := &models.ListAlertRulesQuery{ - OrgID: orgID, + OrgID: user.GetOrgID(), NamespaceUIDs: []string{namespaceUID}, RuleGroup: ruleGroup, } @@ -259,12 +254,12 @@ func (service *AlertRuleService) UpdateRuleGroup(ctx context.Context, orgID int6 }) } -func (service *AlertRuleService) ReplaceRuleGroup(ctx context.Context, orgID int64, group models.AlertRuleGroup, userID int64, provenance models.Provenance) error { +func (service *AlertRuleService) ReplaceRuleGroup(ctx context.Context, user identity.Requester, group models.AlertRuleGroup, provenance models.Provenance) error { if err := models.ValidateRuleGroupInterval(group.Interval, service.baseIntervalSeconds); err != nil { return err } - delta, err := service.calcDelta(ctx, orgID, group) + delta, err := service.calcDelta(ctx, user, group) if err != nil { return err } @@ -286,13 +281,13 @@ func (service *AlertRuleService) ReplaceRuleGroup(ctx context.Context, orgID int } } - return service.persistDelta(ctx, orgID, delta, userID, provenance) + return service.persistDelta(ctx, user, delta, provenance) } -func (service *AlertRuleService) DeleteRuleGroup(ctx context.Context, orgID int64, namespaceUID, group string, provenance models.Provenance) error { +func (service *AlertRuleService) DeleteRuleGroup(ctx context.Context, user identity.Requester, namespaceUID, group string, provenance models.Provenance) error { // List all rules in the group. q := models.ListAlertRulesQuery{ - OrgID: orgID, + OrgID: user.GetOrgID(), NamespaceUIDs: []string{namespaceUID}, RuleGroup: group, } @@ -317,16 +312,16 @@ func (service *AlertRuleService) DeleteRuleGroup(ctx context.Context, orgID int6 // Delete all rules. return service.xact.InTransaction(ctx, func(ctx context.Context) error { - return service.deleteRules(ctx, orgID, ruleList...) + return service.deleteRules(ctx, user.GetOrgID(), ruleList...) }) } -func (service *AlertRuleService) calcDelta(ctx context.Context, orgID int64, group models.AlertRuleGroup) (*store.GroupDelta, error) { +func (service *AlertRuleService) calcDelta(ctx context.Context, user identity.Requester, group models.AlertRuleGroup) (*store.GroupDelta, error) { // If the provided request did not provide the rules list at all, treat it as though it does not wish to change rules. // This is done for backwards compatibility. Requests which specify only the interval must update only the interval. if group.Rules == nil { listRulesQuery := models.ListAlertRulesQuery{ - OrgID: orgID, + OrgID: user.GetOrgID(), NamespaceUIDs: []string{group.FolderUID}, RuleGroup: group.Title, } @@ -347,12 +342,12 @@ func (service *AlertRuleService) calcDelta(ctx context.Context, orgID int64, gro } key := models.AlertRuleGroupKey{ - OrgID: orgID, + OrgID: user.GetOrgID(), NamespaceUID: group.FolderUID, RuleGroup: group.Title, } rules := make([]*models.AlertRuleWithOptionals, len(group.Rules)) - group = *syncGroupRuleFields(&group, orgID) + group = *syncGroupRuleFields(&group, user.GetOrgID()) for i := range group.Rules { if err := group.Rules[i].SetDashboardAndPanelFromAnnotations(); err != nil { return nil, err @@ -368,13 +363,13 @@ func (service *AlertRuleService) calcDelta(ctx context.Context, orgID int64, gro return store.UpdateCalculatedRuleFields(delta), nil } -func (service *AlertRuleService) persistDelta(ctx context.Context, orgID int64, delta *store.GroupDelta, userID int64, provenance models.Provenance) error { +func (service *AlertRuleService) persistDelta(ctx context.Context, user identity.Requester, delta *store.GroupDelta, provenance models.Provenance) error { return service.xact.InTransaction(ctx, func(ctx context.Context) error { // Delete first as this could prevent future unique constraint violations. if len(delta.Delete) > 0 { for _, del := range delta.Delete { // check that provenance is not changed in an invalid way - storedProvenance, err := service.provenanceStore.GetProvenance(ctx, del, orgID) + storedProvenance, err := service.provenanceStore.GetProvenance(ctx, del, user.GetOrgID()) if err != nil { return err } @@ -382,7 +377,7 @@ func (service *AlertRuleService) persistDelta(ctx context.Context, orgID int64, return fmt.Errorf("cannot update with provided provenance '%s', needs '%s'", provenance, storedProvenance) } } - if err := service.deleteRules(ctx, orgID, delta.Delete...); err != nil { + if err := service.deleteRules(ctx, user.GetOrgID(), delta.Delete...); err != nil { return err } } @@ -391,7 +386,7 @@ func (service *AlertRuleService) persistDelta(ctx context.Context, orgID int64, updates := make([]models.UpdateRule, 0, len(delta.Update)) for _, update := range delta.Update { // check that provenance is not changed in an invalid way - storedProvenance, err := service.provenanceStore.GetProvenance(ctx, update.New, orgID) + storedProvenance, err := service.provenanceStore.GetProvenance(ctx, update.New, user.GetOrgID()) if err != nil { return err } @@ -407,7 +402,7 @@ func (service *AlertRuleService) persistDelta(ctx context.Context, orgID int64, return fmt.Errorf("failed to update alert rules: %w", err) } for _, update := range delta.Update { - if err := service.provenanceStore.SetProvenance(ctx, update.New, orgID, provenance); err != nil { + if err := service.provenanceStore.SetProvenance(ctx, update.New, user.GetOrgID(), provenance); err != nil { return err } } @@ -419,13 +414,13 @@ func (service *AlertRuleService) persistDelta(ctx context.Context, orgID int64, return fmt.Errorf("failed to insert alert rules: %w", err) } for _, key := range uids { - if err := service.provenanceStore.SetProvenance(ctx, &models.AlertRule{UID: key.UID}, orgID, provenance); err != nil { + if err := service.provenanceStore.SetProvenance(ctx, &models.AlertRule{UID: key.UID}, user.GetOrgID(), provenance); err != nil { return err } } } - if err := service.checkLimitsTransactionCtx(ctx, orgID, userID); err != nil { + if err := service.checkLimitsTransactionCtx(ctx, user); err != nil { return err } @@ -434,8 +429,8 @@ func (service *AlertRuleService) persistDelta(ctx context.Context, orgID int64, } // UpdateAlertRule updates an alert rule. -func (service *AlertRuleService) UpdateAlertRule(ctx context.Context, rule models.AlertRule, provenance models.Provenance) (models.AlertRule, error) { - storedRule, storedProvenance, err := service.GetAlertRule(ctx, rule.OrgID, rule.UID) +func (service *AlertRuleService) UpdateAlertRule(ctx context.Context, user identity.Requester, rule models.AlertRule, provenance models.Provenance) (models.AlertRule, error) { + storedRule, storedProvenance, err := service.GetAlertRule(ctx, user, rule.UID) if err != nil { return models.AlertRule{}, err } @@ -478,9 +473,9 @@ func (service *AlertRuleService) UpdateAlertRule(ctx context.Context, rule model return rule, err } -func (service *AlertRuleService) DeleteAlertRule(ctx context.Context, orgID int64, ruleUID string, provenance models.Provenance) error { +func (service *AlertRuleService) DeleteAlertRule(ctx context.Context, user identity.Requester, ruleUID string, provenance models.Provenance) error { rule := &models.AlertRule{ - OrgID: orgID, + OrgID: user.GetOrgID(), UID: ruleUID, } // check that provenance is not changed in an invalid way @@ -492,14 +487,22 @@ func (service *AlertRuleService) DeleteAlertRule(ctx context.Context, orgID int6 return fmt.Errorf("cannot delete with provided provenance '%s', needs '%s'", provenance, storedProvenance) } return service.xact.InTransaction(ctx, func(ctx context.Context) error { - return service.deleteRules(ctx, orgID, rule) + return service.deleteRules(ctx, user.GetOrgID(), rule) }) } // checkLimitsTransactionCtx checks whether the current transaction (as identified by the ctx) breaches configured alert rule limits. -func (service *AlertRuleService) checkLimitsTransactionCtx(ctx context.Context, orgID, userID int64) error { +func (service *AlertRuleService) checkLimitsTransactionCtx(ctx context.Context, user identity.Requester) error { + // default to 0 if there is no user + userID := int64(0) + u, err := identity.UserIdentifier(user.GetNamespacedID()) + if err != nil { + return fmt.Errorf("failed to check alert rule quota: %w", err) + } + userID = u + limitReached, err := service.quotas.CheckQuotaReached(ctx, models.QuotaTargetSrv, "a.ScopeParameters{ - OrgID: orgID, + OrgID: user.GetOrgID(), UserID: userID, }) if err != nil { @@ -532,9 +535,9 @@ func (service *AlertRuleService) deleteRules(ctx context.Context, orgID int64, t } // GetAlertRuleGroupWithFolderTitle returns the alert rule group with folder title. -func (service *AlertRuleService) GetAlertRuleGroupWithFolderTitle(ctx context.Context, orgID int64, namespaceUID, group string) (models.AlertRuleGroupWithFolderTitle, error) { +func (service *AlertRuleService) GetAlertRuleGroupWithFolderTitle(ctx context.Context, user identity.Requester, namespaceUID, group string) (models.AlertRuleGroupWithFolderTitle, error) { q := models.ListAlertRulesQuery{ - OrgID: orgID, + OrgID: user.GetOrgID(), NamespaceUIDs: []string{namespaceUID}, RuleGroup: group, } @@ -547,7 +550,7 @@ func (service *AlertRuleService) GetAlertRuleGroupWithFolderTitle(ctx context.Co } dq := dashboards.GetDashboardQuery{ - OrgID: orgID, + OrgID: user.GetOrgID(), UID: namespaceUID, } dash, err := service.dashboardService.GetDashboard(ctx, &dq) @@ -560,9 +563,9 @@ func (service *AlertRuleService) GetAlertRuleGroupWithFolderTitle(ctx context.Co } // GetAlertGroupsWithFolderTitle returns all groups with folder title in the folders identified by folderUID that have at least one alert. If argument folderUIDs is nil or empty - returns groups in all folders. -func (service *AlertRuleService) GetAlertGroupsWithFolderTitle(ctx context.Context, orgID int64, folderUIDs []string) ([]models.AlertRuleGroupWithFolderTitle, error) { +func (service *AlertRuleService) GetAlertGroupsWithFolderTitle(ctx context.Context, user identity.Requester, folderUIDs []string) ([]models.AlertRuleGroupWithFolderTitle, error) { q := models.ListAlertRulesQuery{ - OrgID: orgID, + OrgID: user.GetOrgID(), } if len(folderUIDs) > 0 { diff --git a/pkg/services/ngalert/provisioning/alert_rules_test.go b/pkg/services/ngalert/provisioning/alert_rules_test.go index cd063331d193..f44a94f86ccc 100644 --- a/pkg/services/ngalert/provisioning/alert_rules_test.go +++ b/pkg/services/ngalert/provisioning/alert_rules_test.go @@ -11,6 +11,7 @@ import ( "github.com/stretchr/testify/require" "github.com/grafana/grafana/pkg/expr" + "github.com/grafana/grafana/pkg/services/user" "github.com/grafana/grafana/pkg/util" "github.com/grafana/grafana/pkg/infra/db" @@ -25,17 +26,21 @@ import ( func TestAlertRuleService(t *testing.T) { ruleService := createAlertRuleService(t) var orgID int64 = 1 + u := &user.SignedInUser{ + UserID: 1, + OrgID: orgID, + } t.Run("group creation should set the right provenance", func(t *testing.T) { group := createDummyGroup("group-test-1", orgID) - err := ruleService.ReplaceRuleGroup(context.Background(), orgID, group, 0, models.ProvenanceAPI) + err := ruleService.ReplaceRuleGroup(context.Background(), u, group, models.ProvenanceAPI) require.NoError(t, err) - readGroup, err := ruleService.GetRuleGroup(context.Background(), orgID, "my-namespace", "group-test-1") + readGroup, err := ruleService.GetRuleGroup(context.Background(), u, "my-namespace", "group-test-1") require.NoError(t, err) require.NotEmpty(t, readGroup.Rules) for _, rule := range readGroup.Rules { - _, provenance, err := ruleService.GetAlertRule(context.Background(), orgID, rule.UID) + _, provenance, err := ruleService.GetAlertRule(context.Background(), u, rule.UID) require.NoError(t, err) require.Equal(t, models.ProvenanceAPI, provenance) } @@ -44,15 +49,15 @@ func TestAlertRuleService(t *testing.T) { t.Run("alert rule group should be updated correctly", func(t *testing.T) { rule := dummyRule("test#3", orgID) rule.RuleGroup = "a" - rule, err := ruleService.CreateAlertRule(context.Background(), nil, rule, models.ProvenanceNone) + rule, err := ruleService.CreateAlertRule(context.Background(), u, rule, models.ProvenanceNone) require.NoError(t, err) require.Equal(t, int64(60), rule.IntervalSeconds) var interval int64 = 120 - err = ruleService.UpdateRuleGroup(context.Background(), orgID, rule.NamespaceUID, rule.RuleGroup, 120) + err = ruleService.UpdateRuleGroup(context.Background(), u, rule.NamespaceUID, rule.RuleGroup, 120) require.NoError(t, err) - rule, _, err = ruleService.GetAlertRule(context.Background(), orgID, rule.UID) + rule, _, err = ruleService.GetAlertRule(context.Background(), u, rule.UID) require.NoError(t, err) require.Equal(t, interval, rule.IntervalSeconds) }) @@ -61,24 +66,24 @@ func TestAlertRuleService(t *testing.T) { var orgID int64 = 2 rule := dummyRule("test#1", orgID) rule.NamespaceUID = "123abc" - rule, err := ruleService.CreateAlertRule(context.Background(), nil, rule, models.ProvenanceNone) + u := &user.SignedInUser{OrgID: orgID} + rule, err := ruleService.CreateAlertRule(context.Background(), u, rule, models.ProvenanceNone) require.NoError(t, err) rule.NamespaceUID = "abc123" - _, err = ruleService.UpdateAlertRule(context.Background(), - rule, models.ProvenanceNone) + _, err = ruleService.UpdateAlertRule(context.Background(), u, rule, models.ProvenanceNone) require.NoError(t, err) }) t.Run("group update should propagate folderUID from group to rules", func(t *testing.T) { ruleService := createAlertRuleService(t) - group := createDummyGroup("namespace-test", 1) + group := createDummyGroup("namespace-test", orgID) group.Rules[0].NamespaceUID = "" - err := ruleService.ReplaceRuleGroup(context.Background(), 1, group, 0, models.ProvenanceAPI) + err := ruleService.ReplaceRuleGroup(context.Background(), u, group, models.ProvenanceAPI) require.NoError(t, err) - readGroup, err := ruleService.GetRuleGroup(context.Background(), orgID, "my-namespace", "namespace-test") + readGroup, err := ruleService.GetRuleGroup(context.Background(), u, "my-namespace", "namespace-test") require.NoError(t, err) require.NotEmpty(t, readGroup.Rules) require.Equal(t, "my-namespace", readGroup.Rules[0].NamespaceUID) @@ -88,10 +93,10 @@ func TestAlertRuleService(t *testing.T) { group := createDummyGroup("group-test-3", orgID) group.Rules[0].RuleGroup = "something different" - err := ruleService.ReplaceRuleGroup(context.Background(), orgID, group, 0, models.ProvenanceAPI) + err := ruleService.ReplaceRuleGroup(context.Background(), u, group, models.ProvenanceAPI) require.NoError(t, err) - readGroup, err := ruleService.GetRuleGroup(context.Background(), orgID, "my-namespace", "group-test-3") + readGroup, err := ruleService.GetRuleGroup(context.Background(), u, "my-namespace", "group-test-3") require.NoError(t, err) require.NotEmpty(t, readGroup.Rules) for _, rule := range readGroup.Rules { @@ -102,16 +107,16 @@ func TestAlertRuleService(t *testing.T) { t.Run("alert rule should get interval from existing rule group", func(t *testing.T) { rule := dummyRule("test#4", orgID) rule.RuleGroup = "b" - rule, err := ruleService.CreateAlertRule(context.Background(), nil, rule, models.ProvenanceNone) + rule, err := ruleService.CreateAlertRule(context.Background(), u, rule, models.ProvenanceNone) require.NoError(t, err) var interval int64 = 120 - err = ruleService.UpdateRuleGroup(context.Background(), orgID, rule.NamespaceUID, rule.RuleGroup, 120) + err = ruleService.UpdateRuleGroup(context.Background(), u, rule.NamespaceUID, rule.RuleGroup, 120) require.NoError(t, err) rule = dummyRule("test#4-1", orgID) rule.RuleGroup = "b" - rule, err = ruleService.CreateAlertRule(context.Background(), nil, rule, models.ProvenanceNone) + rule, err = ruleService.CreateAlertRule(context.Background(), u, rule, models.ProvenanceNone) require.NoError(t, err) require.Equal(t, interval, rule.IntervalSeconds) }) @@ -124,22 +129,23 @@ func TestAlertRuleService(t *testing.T) { ruleGroup = "abc" newInterval int64 = 120 ) + u := &user.SignedInUser{OrgID: orgID} rule := dummyRule("my_rule", orgID) rule.UID = ruleUID rule.RuleGroup = ruleGroup rule.NamespaceUID = namespaceUID - _, err := ruleService.CreateAlertRule(context.Background(), nil, rule, models.ProvenanceNone) + _, err := ruleService.CreateAlertRule(context.Background(), u, rule, models.ProvenanceNone) require.NoError(t, err) - rule, _, err = ruleService.GetAlertRule(context.Background(), orgID, ruleUID) + rule, _, err = ruleService.GetAlertRule(context.Background(), u, ruleUID) require.NoError(t, err) require.Equal(t, int64(1), rule.Version) require.Equal(t, int64(60), rule.IntervalSeconds) - err = ruleService.UpdateRuleGroup(context.Background(), orgID, namespaceUID, ruleGroup, newInterval) + err = ruleService.UpdateRuleGroup(context.Background(), u, namespaceUID, ruleGroup, newInterval) require.NoError(t, err) - rule, _, err = ruleService.GetAlertRule(context.Background(), orgID, ruleUID) + rule, _, err = ruleService.GetAlertRule(context.Background(), u, ruleUID) require.NoError(t, err) require.Equal(t, int64(2), rule.Version) require.Equal(t, newInterval, rule.IntervalSeconds) @@ -147,16 +153,16 @@ func TestAlertRuleService(t *testing.T) { t.Run("updating a group by updating a rule should bump that rule's data and version number", func(t *testing.T) { group := createDummyGroup("group-test-5", orgID) - err := ruleService.ReplaceRuleGroup(context.Background(), orgID, group, 0, models.ProvenanceAPI) + err := ruleService.ReplaceRuleGroup(context.Background(), u, group, models.ProvenanceAPI) require.NoError(t, err) - updatedGroup, err := ruleService.GetRuleGroup(context.Background(), orgID, "my-namespace", "group-test-5") + updatedGroup, err := ruleService.GetRuleGroup(context.Background(), u, "my-namespace", "group-test-5") require.NoError(t, err) updatedGroup.Rules[0].Title = "some-other-title-asdf" - err = ruleService.ReplaceRuleGroup(context.Background(), orgID, updatedGroup, 0, models.ProvenanceAPI) + err = ruleService.ReplaceRuleGroup(context.Background(), u, updatedGroup, models.ProvenanceAPI) require.NoError(t, err) - readGroup, err := ruleService.GetRuleGroup(context.Background(), orgID, "my-namespace", "group-test-5") + readGroup, err := ruleService.GetRuleGroup(context.Background(), u, "my-namespace", "group-test-5") require.NoError(t, err) require.NotEmpty(t, readGroup.Rules) require.Len(t, readGroup.Rules, 1) @@ -175,17 +181,17 @@ func TestAlertRuleService(t *testing.T) { dummyRule("overlap-test-rule-2", orgID), }, } - err := ruleService.ReplaceRuleGroup(context.Background(), orgID, group, 0, models.ProvenanceAPI) + err := ruleService.ReplaceRuleGroup(context.Background(), u, group, models.ProvenanceAPI) require.NoError(t, err) - updatedGroup, err := ruleService.GetRuleGroup(context.Background(), orgID, "my-namespace", "overlap-test") + updatedGroup, err := ruleService.GetRuleGroup(context.Background(), u, "my-namespace", "overlap-test") require.NoError(t, err) updatedGroup.Rules[0].Title = "overlap-test-rule-2" updatedGroup.Rules[1].Title = "overlap-test-rule-3" - err = ruleService.ReplaceRuleGroup(context.Background(), orgID, updatedGroup, 0, models.ProvenanceAPI) + err = ruleService.ReplaceRuleGroup(context.Background(), u, updatedGroup, models.ProvenanceAPI) require.NoError(t, err) - readGroup, err := ruleService.GetRuleGroup(context.Background(), orgID, "my-namespace", "overlap-test") + readGroup, err := ruleService.GetRuleGroup(context.Background(), u, "my-namespace", "overlap-test") require.NoError(t, err) require.NotEmpty(t, readGroup.Rules) require.Len(t, readGroup.Rules, 2) @@ -206,17 +212,17 @@ func TestAlertRuleService(t *testing.T) { dummyRule("swap-test-rule-2", orgID), }, } - err := ruleService.ReplaceRuleGroup(context.Background(), orgID, group, 0, models.ProvenanceAPI) + err := ruleService.ReplaceRuleGroup(context.Background(), u, group, models.ProvenanceAPI) require.NoError(t, err) - updatedGroup, err := ruleService.GetRuleGroup(context.Background(), orgID, "my-namespace", "swap-test") + updatedGroup, err := ruleService.GetRuleGroup(context.Background(), u, "my-namespace", "swap-test") require.NoError(t, err) updatedGroup.Rules[0].Title = "swap-test-rule-2" updatedGroup.Rules[1].Title = "swap-test-rule-1" - err = ruleService.ReplaceRuleGroup(context.Background(), orgID, updatedGroup, 0, models.ProvenanceAPI) + err = ruleService.ReplaceRuleGroup(context.Background(), u, updatedGroup, models.ProvenanceAPI) require.NoError(t, err) - readGroup, err := ruleService.GetRuleGroup(context.Background(), orgID, "my-namespace", "swap-test") + readGroup, err := ruleService.GetRuleGroup(context.Background(), u, "my-namespace", "swap-test") require.NoError(t, err) require.NotEmpty(t, readGroup.Rules) require.Len(t, readGroup.Rules, 2) @@ -238,18 +244,18 @@ func TestAlertRuleService(t *testing.T) { dummyRule("cycle-test-rule-3", orgID), }, } - err := ruleService.ReplaceRuleGroup(context.Background(), orgID, group, 0, models.ProvenanceAPI) + err := ruleService.ReplaceRuleGroup(context.Background(), u, group, models.ProvenanceAPI) require.NoError(t, err) - updatedGroup, err := ruleService.GetRuleGroup(context.Background(), orgID, "my-namespace", "cycle-test") + updatedGroup, err := ruleService.GetRuleGroup(context.Background(), u, "my-namespace", "cycle-test") require.NoError(t, err) updatedGroup.Rules[0].Title = "cycle-test-rule-2" updatedGroup.Rules[1].Title = "cycle-test-rule-3" updatedGroup.Rules[2].Title = "cycle-test-rule-1" - err = ruleService.ReplaceRuleGroup(context.Background(), orgID, updatedGroup, 0, models.ProvenanceAPI) + err = ruleService.ReplaceRuleGroup(context.Background(), u, updatedGroup, models.ProvenanceAPI) require.NoError(t, err) - readGroup, err := ruleService.GetRuleGroup(context.Background(), orgID, "my-namespace", "cycle-test") + readGroup, err := ruleService.GetRuleGroup(context.Background(), u, "my-namespace", "cycle-test") require.NoError(t, err) require.NotEmpty(t, readGroup.Rules) require.Len(t, readGroup.Rules, 3) @@ -276,9 +282,9 @@ func TestAlertRuleService(t *testing.T) { dummyRule("multi-cycle-test-rule-5", orgID), }, } - err := ruleService.ReplaceRuleGroup(context.Background(), orgID, group, 0, models.ProvenanceAPI) + err := ruleService.ReplaceRuleGroup(context.Background(), u, group, models.ProvenanceAPI) require.NoError(t, err) - updatedGroup, err := ruleService.GetRuleGroup(context.Background(), orgID, "my-namespace", "multi-cycle-test") + updatedGroup, err := ruleService.GetRuleGroup(context.Background(), u, "my-namespace", "multi-cycle-test") require.NoError(t, err) updatedGroup.Rules[0].Title = "multi-cycle-test-rule-2" @@ -288,10 +294,10 @@ func TestAlertRuleService(t *testing.T) { updatedGroup.Rules[3].Title = "multi-cycle-test-rule-5" updatedGroup.Rules[4].Title = "multi-cycle-test-rule-3" - err = ruleService.ReplaceRuleGroup(context.Background(), orgID, updatedGroup, 0, models.ProvenanceAPI) + err = ruleService.ReplaceRuleGroup(context.Background(), u, updatedGroup, models.ProvenanceAPI) require.NoError(t, err) - readGroup, err := ruleService.GetRuleGroup(context.Background(), orgID, "my-namespace", "multi-cycle-test") + readGroup, err := ruleService.GetRuleGroup(context.Background(), u, "my-namespace", "multi-cycle-test") require.NoError(t, err) require.NotEmpty(t, readGroup.Rules) require.Len(t, readGroup.Rules, 5) @@ -317,7 +323,7 @@ func TestAlertRuleService(t *testing.T) { dummyRule("recreate-test-rule-1", orgID), }, } - err := ruleService.ReplaceRuleGroup(context.Background(), orgID, group, 0, models.ProvenanceAPI) + err := ruleService.ReplaceRuleGroup(context.Background(), u, group, models.ProvenanceAPI) require.NoError(t, err) updatedGroup := models.AlertRuleGroup{ Title: "recreate-test", @@ -327,10 +333,10 @@ func TestAlertRuleService(t *testing.T) { dummyRule("recreate-test-rule-1", orgID), }, } - err = ruleService.ReplaceRuleGroup(context.Background(), orgID, updatedGroup, 0, models.ProvenanceAPI) + err = ruleService.ReplaceRuleGroup(context.Background(), u, updatedGroup, models.ProvenanceAPI) require.NoError(t, err) - readGroup, err := ruleService.GetRuleGroup(context.Background(), orgID, "my-namespace", "recreate-test") + readGroup, err := ruleService.GetRuleGroup(context.Background(), u, "my-namespace", "recreate-test") require.NoError(t, err) require.NotEmpty(t, readGroup.Rules) require.Len(t, readGroup.Rules, 1) @@ -348,17 +354,17 @@ func TestAlertRuleService(t *testing.T) { dummyRule("create-overlap-test-rule-1", orgID), }, } - err := ruleService.ReplaceRuleGroup(context.Background(), orgID, group, 0, models.ProvenanceAPI) + err := ruleService.ReplaceRuleGroup(context.Background(), u, group, models.ProvenanceAPI) require.NoError(t, err) - updatedGroup, err := ruleService.GetRuleGroup(context.Background(), orgID, "my-namespace", "create-overlap-test") + updatedGroup, err := ruleService.GetRuleGroup(context.Background(), u, "my-namespace", "create-overlap-test") require.NoError(t, err) updatedGroup.Rules[0].Title = "create-overlap-test-rule-2" updatedGroup.Rules = append(updatedGroup.Rules, dummyRule("create-overlap-test-rule-1", orgID)) - err = ruleService.ReplaceRuleGroup(context.Background(), orgID, updatedGroup, 0, models.ProvenanceAPI) + err = ruleService.ReplaceRuleGroup(context.Background(), u, updatedGroup, models.ProvenanceAPI) require.NoError(t, err) - readGroup, err := ruleService.GetRuleGroup(context.Background(), orgID, "my-namespace", "create-overlap-test") + readGroup, err := ruleService.GetRuleGroup(context.Background(), u, "my-namespace", "create-overlap-test") require.NoError(t, err) require.NotEmpty(t, readGroup.Rules) require.Len(t, readGroup.Rules, 2) @@ -377,9 +383,9 @@ func TestAlertRuleService(t *testing.T) { models.PanelIDAnnotation: strconv.FormatInt(panelId, 10), } - err := ruleService.ReplaceRuleGroup(context.Background(), orgID, group, 0, models.ProvenanceAPI) + err := ruleService.ReplaceRuleGroup(context.Background(), u, group, models.ProvenanceAPI) require.NoError(t, err) - updatedGroup, err := ruleService.GetRuleGroup(context.Background(), orgID, "my-namespace", "group-test-5") + updatedGroup, err := ruleService.GetRuleGroup(context.Background(), u, "my-namespace", "group-test-5") require.NoError(t, err) require.NotNil(t, updatedGroup.Rules[0].DashboardUID) @@ -434,12 +440,11 @@ func TestAlertRuleService(t *testing.T) { } for _, test := range tests { t.Run(test.name, func(t *testing.T) { - var orgID int64 = 1 rule := dummyRule(t.Name(), orgID) - rule, err := ruleService.CreateAlertRule(context.Background(), nil, rule, test.from) + rule, err := ruleService.CreateAlertRule(context.Background(), u, rule, test.from) require.NoError(t, err) - _, err = ruleService.UpdateAlertRule(context.Background(), rule, test.to) + _, err = ruleService.UpdateAlertRule(context.Background(), u, rule, test.to) if test.errNil { require.NoError(t, err) } else { @@ -497,11 +502,11 @@ func TestAlertRuleService(t *testing.T) { t.Run(test.name, func(t *testing.T) { var orgID int64 = 1 group := createDummyGroup(t.Name(), orgID) - err := ruleService.ReplaceRuleGroup(context.Background(), 1, group, 0, test.from) + err := ruleService.ReplaceRuleGroup(context.Background(), u, group, test.from) require.NoError(t, err) group.Rules[0].Title = t.Name() - err = ruleService.ReplaceRuleGroup(context.Background(), 1, group, 0, test.to) + err = ruleService.ReplaceRuleGroup(context.Background(), u, group, test.to) if test.errNil { require.NoError(t, err) } else { @@ -517,7 +522,7 @@ func TestAlertRuleService(t *testing.T) { checker.EXPECT().LimitExceeded() ruleService.quotas = checker - _, err := ruleService.CreateAlertRule(context.Background(), nil, dummyRule("test#1", orgID), models.ProvenanceNone) + _, err := ruleService.CreateAlertRule(context.Background(), u, dummyRule("test#1", orgID), models.ProvenanceNone) require.ErrorIs(t, err, models.ErrQuotaReached) }) @@ -528,8 +533,8 @@ func TestAlertRuleService(t *testing.T) { checker.EXPECT().LimitExceeded() ruleService.quotas = checker - group := createDummyGroup("quota-reached", 1) - err := ruleService.ReplaceRuleGroup(context.Background(), 1, group, 0, models.ProvenanceAPI) + group := createDummyGroup("quota-reached", orgID) + err := ruleService.ReplaceRuleGroup(context.Background(), u, group, models.ProvenanceAPI) require.ErrorIs(t, err, models.ErrQuotaReached) }) @@ -538,18 +543,19 @@ func TestAlertRuleService(t *testing.T) { func TestCreateAlertRule(t *testing.T) { ruleService := createAlertRuleService(t) var orgID int64 = 1 + u := &user.SignedInUser{OrgID: orgID} t.Run("should return the created id", func(t *testing.T) { - rule, err := ruleService.CreateAlertRule(context.Background(), nil, dummyRule("test#1", orgID), models.ProvenanceNone) + rule, err := ruleService.CreateAlertRule(context.Background(), u, dummyRule("test#1", orgID), models.ProvenanceNone) require.NoError(t, err) require.NotEqual(t, 0, rule.ID, "expected to get the created id and not the zero value") }) t.Run("should set the right provenance", func(t *testing.T) { - rule, err := ruleService.CreateAlertRule(context.Background(), nil, dummyRule("test#2", orgID), models.ProvenanceAPI) + rule, err := ruleService.CreateAlertRule(context.Background(), u, dummyRule("test#2", orgID), models.ProvenanceAPI) require.NoError(t, err) - _, provenance, err := ruleService.GetAlertRule(context.Background(), orgID, rule.UID) + _, provenance, err := ruleService.GetAlertRule(context.Background(), u, rule.UID) require.NoError(t, err) require.Equal(t, models.ProvenanceAPI, provenance) }) @@ -558,17 +564,17 @@ func TestCreateAlertRule(t *testing.T) { t.Run("return error if it is not valid UID", func(t *testing.T) { rule := dummyRule("test#3", orgID) rule.UID = strings.Repeat("1", util.MaxUIDLength+1) - rule, err := ruleService.CreateAlertRule(context.Background(), nil, rule, models.ProvenanceNone) + rule, err := ruleService.CreateAlertRule(context.Background(), u, rule, models.ProvenanceNone) require.ErrorIs(t, err, models.ErrAlertRuleFailedValidation) }) t.Run("should create a new rule with this UID", func(t *testing.T) { rule := dummyRule("test#3", orgID) uid := util.GenerateShortUID() rule.UID = uid - created, err := ruleService.CreateAlertRule(context.Background(), nil, rule, models.ProvenanceNone) + created, err := ruleService.CreateAlertRule(context.Background(), u, rule, models.ProvenanceNone) require.NoError(t, err) require.Equal(t, uid, created.UID) - _, _, err = ruleService.GetAlertRule(context.Background(), orgID, uid) + _, _, err = ruleService.GetAlertRule(context.Background(), u, uid) require.NoError(t, err) }) }) diff --git a/pkg/services/provisioning/alerting/rules_provisioner.go b/pkg/services/provisioning/alerting/rules_provisioner.go index d4af1460ff9a..205018c7ef90 100644 --- a/pkg/services/provisioning/alerting/rules_provisioner.go +++ b/pkg/services/provisioning/alerting/rules_provisioner.go @@ -7,10 +7,12 @@ import ( "github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/infra/metrics" + "github.com/grafana/grafana/pkg/services/auth/identity" "github.com/grafana/grafana/pkg/services/dashboards" "github.com/grafana/grafana/pkg/services/folder" alert_models "github.com/grafana/grafana/pkg/services/ngalert/models" "github.com/grafana/grafana/pkg/services/ngalert/provisioning" + "github.com/grafana/grafana/pkg/services/user" "github.com/grafana/grafana/pkg/util" ) @@ -42,6 +44,7 @@ func (prov *defaultAlertRuleProvisioner) Provision(ctx context.Context, files []*AlertingFile) error { for _, file := range files { for _, group := range file.Groups { + u := provisionerUser(group.OrgID) folderUID, err := prov.getOrCreateFolderUID(ctx, group.FolderTitle, group.OrgID) if err != nil { return err @@ -54,19 +57,18 @@ func (prov *defaultAlertRuleProvisioner) Provision(ctx context.Context, for _, rule := range group.Rules { rule.NamespaceUID = folderUID rule.RuleGroup = group.Title - err = prov.provisionRule(ctx, group.OrgID, rule) + err = prov.provisionRule(ctx, u, rule) if err != nil { return err } } - err = prov.ruleService.UpdateRuleGroup(ctx, group.OrgID, folderUID, group.Title, group.Interval) + err = prov.ruleService.UpdateRuleGroup(ctx, u, folderUID, group.Title, group.Interval) if err != nil { return err } } for _, deleteRule := range file.DeleteRules { - err := prov.ruleService.DeleteAlertRule(ctx, deleteRule.OrgID, - deleteRule.UID, alert_models.ProvenanceFile) + err := prov.ruleService.DeleteAlertRule(ctx, provisionerUser(deleteRule.OrgID), deleteRule.UID, alert_models.ProvenanceFile) if err != nil { return err } @@ -77,20 +79,20 @@ func (prov *defaultAlertRuleProvisioner) Provision(ctx context.Context, func (prov *defaultAlertRuleProvisioner) provisionRule( ctx context.Context, - orgID int64, + user identity.Requester, rule alert_models.AlertRule) error { prov.logger.Debug("provisioning alert rule", "uid", rule.UID, "org", rule.OrgID) - _, _, err := prov.ruleService.GetAlertRule(ctx, orgID, rule.UID) + _, _, err := prov.ruleService.GetAlertRule(ctx, user, rule.UID) if err != nil && !errors.Is(err, alert_models.ErrAlertRuleNotFound) { return err } else if err != nil { prov.logger.Debug("creating rule", "uid", rule.UID, "org", rule.OrgID) // a nil user is passed in as then the quota logic will only check for // the organization quota since we don't have any user scope here. - _, err = prov.ruleService.CreateAlertRule(ctx, nil, rule, alert_models.ProvenanceFile) + _, err = prov.ruleService.CreateAlertRule(ctx, user, rule, alert_models.ProvenanceFile) } else { prov.logger.Debug("updating rule", "uid", rule.UID, "org", rule.OrgID) - _, err = prov.ruleService.UpdateAlertRule(ctx, rule, alert_models.ProvenanceFile) + _, err = prov.ruleService.UpdateAlertRule(ctx, user, rule, alert_models.ProvenanceFile) } return err } @@ -129,3 +131,8 @@ func (prov *defaultAlertRuleProvisioner) getOrCreateFolderUID( return cmdResult.UID, nil } + +// UserID is 0 to use org quota +var provisionerUser = func(orgID int64) identity.Requester { + return &user.SignedInUser{UserID: 0, Login: "alert_provisioner", OrgID: orgID} +}