diff --git a/pkg/services/ngalert/api/api_prometheus.go b/pkg/services/ngalert/api/api_prometheus.go index e8344434860..e3dea4e93f1 100644 --- a/pkg/services/ngalert/api/api_prometheus.go +++ b/pkg/services/ngalert/api/api_prometheus.go @@ -101,7 +101,7 @@ func (srv PrometheusSrv) RouteGetRuleStatuses(c *models.ReqContext) response.Res DashboardUID: dashboardUID, PanelID: panelID, } - if err := srv.store.GetOrgRuleGroups(&ruleGroupQuery); err != nil { + if err := srv.store.GetOrgRuleGroups(c.Req.Context(), &ruleGroupQuery); err != nil { ruleResponse.DiscoveryBase.Status = "error" ruleResponse.DiscoveryBase.Error = fmt.Sprintf("failure getting rule groups: %s", err.Error()) ruleResponse.DiscoveryBase.ErrorType = apiv1.ErrServer @@ -113,7 +113,7 @@ func (srv PrometheusSrv) RouteGetRuleStatuses(c *models.ReqContext) response.Res DashboardUID: dashboardUID, PanelID: panelID, } - if err := srv.store.GetOrgAlertRules(&alertRuleQuery); err != nil { + if err := srv.store.GetOrgAlertRules(c.Req.Context(), &alertRuleQuery); err != nil { ruleResponse.DiscoveryBase.Status = "error" ruleResponse.DiscoveryBase.Error = fmt.Sprintf("failure getting rules: %s", err.Error()) ruleResponse.DiscoveryBase.ErrorType = apiv1.ErrServer diff --git a/pkg/services/ngalert/api/api_ruler.go b/pkg/services/ngalert/api/api_ruler.go index 5268f72a6c1..eb8177bd27f 100644 --- a/pkg/services/ngalert/api/api_ruler.go +++ b/pkg/services/ngalert/api/api_ruler.go @@ -38,7 +38,7 @@ func (srv RulerSrv) RouteDeleteNamespaceRulesConfig(c *models.ReqContext) respon return toNamespaceErrorResponse(err) } - uids, err := srv.store.DeleteNamespaceAlertRules(c.SignedInUser.OrgId, namespace.Uid) + uids, err := srv.store.DeleteNamespaceAlertRules(c.Req.Context(), c.SignedInUser.OrgId, namespace.Uid) if err != nil { return ErrResp(http.StatusInternalServerError, err, "failed to delete namespace alert rules") } @@ -60,7 +60,7 @@ func (srv RulerSrv) RouteDeleteRuleGroupConfig(c *models.ReqContext) response.Re return toNamespaceErrorResponse(err) } ruleGroup := web.Params(c.Req)[":Groupname"] - uids, err := srv.store.DeleteRuleGroupAlertRules(c.SignedInUser.OrgId, namespace.Uid, ruleGroup) + uids, err := srv.store.DeleteRuleGroupAlertRules(c.Req.Context(), c.SignedInUser.OrgId, namespace.Uid, ruleGroup) if err != nil { if errors.Is(err, ngmodels.ErrRuleGroupNamespaceNotFound) { @@ -90,7 +90,7 @@ func (srv RulerSrv) RouteGetNamespaceRulesConfig(c *models.ReqContext) response. OrgID: c.SignedInUser.OrgId, NamespaceUID: namespace.Uid, } - if err := srv.store.GetNamespaceAlertRules(&q); err != nil { + if err := srv.store.GetNamespaceAlertRules(c.Req.Context(), &q); err != nil { return ErrResp(http.StatusInternalServerError, err, "failed to update rule group") } @@ -133,7 +133,7 @@ func (srv RulerSrv) RouteGetRulegGroupConfig(c *models.ReqContext) response.Resp NamespaceUID: namespace.Uid, RuleGroup: ruleGroup, } - if err := srv.store.GetRuleGroupAlertRules(&q); err != nil { + if err := srv.store.GetRuleGroupAlertRules(c.Req.Context(), &q); err != nil { return ErrResp(http.StatusInternalServerError, err, "failed to get group alert rules") } @@ -187,7 +187,7 @@ func (srv RulerSrv) RouteGetRulesConfig(c *models.ReqContext) response.Response PanelID: panelID, } - if err := srv.store.GetOrgAlertRules(&q); err != nil { + if err := srv.store.GetOrgAlertRules(c.Req.Context(), &q); err != nil { return ErrResp(http.StatusInternalServerError, err, "failed to get alert rules") } @@ -282,7 +282,7 @@ func (srv RulerSrv) RoutePostNameRulesConfig(c *models.ReqContext, ruleGroupConf } } - if err := srv.store.UpdateRuleGroup(store.UpdateRuleGroupCmd{ + if err := srv.store.UpdateRuleGroup(c.Req.Context(), store.UpdateRuleGroupCmd{ OrgID: c.SignedInUser.OrgId, NamespaceUID: namespace.Uid, RuleGroupConfig: ruleGroupConfig, diff --git a/pkg/services/ngalert/ngalert.go b/pkg/services/ngalert/ngalert.go index 6a48388c453..287ebcd1b7f 100644 --- a/pkg/services/ngalert/ngalert.go +++ b/pkg/services/ngalert/ngalert.go @@ -169,7 +169,7 @@ func (ng *AlertNG) init() error { // Run starts the scheduler and Alertmanager. func (ng *AlertNG) Run(ctx context.Context) error { ng.Log.Debug("ngalert starting") - ng.stateManager.Warm() + ng.stateManager.Warm(ctx) children, subCtx := errgroup.WithContext(ctx) diff --git a/pkg/services/ngalert/schedule/fetcher.go b/pkg/services/ngalert/schedule/fetcher.go index 36c36b2acb9..9f20f4ac23d 100644 --- a/pkg/services/ngalert/schedule/fetcher.go +++ b/pkg/services/ngalert/schedule/fetcher.go @@ -1,12 +1,13 @@ package schedule import ( + "context" "time" "github.com/grafana/grafana/pkg/services/ngalert/models" ) -func (sch *schedule) getAlertRules(disabledOrgs []int64) []*models.AlertRule { +func (sch *schedule) getAlertRules(ctx context.Context, disabledOrgs []int64) []*models.AlertRule { start := time.Now() defer func() { sch.metrics.GetAlertRulesDuration.Observe(time.Since(start).Seconds()) @@ -15,7 +16,7 @@ func (sch *schedule) getAlertRules(disabledOrgs []int64) []*models.AlertRule { q := models.ListAlertRulesQuery{ ExcludeOrgs: disabledOrgs, } - err := sch.ruleStore.GetAlertRulesForScheduling(&q) + err := sch.ruleStore.GetAlertRulesForScheduling(ctx, &q) if err != nil { sch.log.Error("failed to fetch alert definitions", "err", err) return nil diff --git a/pkg/services/ngalert/schedule/schedule.go b/pkg/services/ngalert/schedule/schedule.go index 148e4e47e58..3c7937ab069 100644 --- a/pkg/services/ngalert/schedule/schedule.go +++ b/pkg/services/ngalert/schedule/schedule.go @@ -377,7 +377,7 @@ func (sch *schedule) schedulePeriodic(ctx context.Context) error { disabledOrgs = append(disabledOrgs, disabledOrg) } - alertRules := sch.getAlertRules(disabledOrgs) + alertRules := sch.getAlertRules(ctx, disabledOrgs) sch.log.Debug("alert rules fetched", "count", len(alertRules), "disabled_orgs", disabledOrgs) // registeredDefinitions is a map used for finding deleted alert rules @@ -528,9 +528,9 @@ func (sch *schedule) ruleRoutine(grafanaCtx context.Context, key models.AlertRul notify(expiredAlerts, logger) } - updateRule := func(oldRule *models.AlertRule) (*models.AlertRule, error) { + updateRule := func(ctx context.Context, oldRule *models.AlertRule) (*models.AlertRule, error) { q := models.GetAlertRuleByUIDQuery{OrgID: key.OrgID, UID: key.UID} - err := sch.ruleStore.GetAlertRuleByUID(&q) + err := sch.ruleStore.GetAlertRuleByUID(ctx, &q) if err != nil { logger.Error("failed to fetch alert rule", "err", err) return nil, err @@ -591,7 +591,7 @@ func (sch *schedule) ruleRoutine(grafanaCtx context.Context, key models.AlertRul case <-updateCh: logger.Info("fetching new version of the rule") err := retryIfError(func(attempt int64) error { - newRule, err := updateRule(currentRule) + newRule, err := updateRule(grafanaCtx, currentRule) if err != nil { return err } @@ -622,7 +622,7 @@ func (sch *schedule) ruleRoutine(grafanaCtx context.Context, key models.AlertRul err := retryIfError(func(attempt int64) error { // fetch latest alert rule version if currentRule == nil || currentRule.Version < ctx.version { - newRule, err := updateRule(currentRule) + newRule, err := updateRule(grafanaCtx, currentRule) if err != nil { return err } diff --git a/pkg/services/ngalert/schedule/schedule_test.go b/pkg/services/ngalert/schedule/schedule_test.go index c707f9ea540..208433d8991 100644 --- a/pkg/services/ngalert/schedule/schedule_test.go +++ b/pkg/services/ngalert/schedule/schedule_test.go @@ -36,10 +36,12 @@ type evalAppliedInfo struct { func TestWarmStateCache(t *testing.T) { evaluationTime, err := time.Parse("2006-01-02", "2021-03-25") require.NoError(t, err) + + ctx := context.Background() _, dbstore := tests.SetupTestEnv(t, 1) const mainOrgID int64 = 1 - rule := tests.CreateTestAlertRule(t, dbstore, 600, mainOrgID) + rule := tests.CreateTestAlertRule(t, ctx, dbstore, 600, mainOrgID) expectedEntries := []*state.State{ { @@ -104,8 +106,9 @@ func TestWarmStateCache(t *testing.T) { Metrics: testMetrics.GetSchedulerMetrics(), AdminConfigPollInterval: 10 * time.Minute, // do not poll in unit tests. } + st := state.NewManager(schedCfg.Logger, testMetrics.GetStateMetrics(), nil, dbstore, dbstore) - st.Warm() + st.Warm(ctx) t.Run("instance cache has expected entries", func(t *testing.T) { for _, entry := range expectedEntries { @@ -121,13 +124,14 @@ func TestWarmStateCache(t *testing.T) { } func TestAlertingTicker(t *testing.T) { + ctx := context.Background() _, dbstore := tests.SetupTestEnv(t, 1) alerts := make([]*models.AlertRule, 0) const mainOrgID int64 = 1 // create alert rule under main org with one second interval - alerts = append(alerts, tests.CreateTestAlertRule(t, dbstore, 1, mainOrgID)) + alerts = append(alerts, tests.CreateTestAlertRule(t, ctx, dbstore, 1, mainOrgID)) const disabledOrgID int64 = 3 @@ -162,8 +166,6 @@ func TestAlertingTicker(t *testing.T) { } sched := schedule.NewScheduler(schedCfg, nil, appUrl, st) - ctx := context.Background() - go func() { err := sched.Run(ctx) require.NoError(t, err) @@ -178,7 +180,7 @@ func TestAlertingTicker(t *testing.T) { // add alert rule under main org with three seconds interval var threeSecInterval int64 = 3 - alerts = append(alerts, tests.CreateTestAlertRule(t, dbstore, threeSecInterval, mainOrgID)) + alerts = append(alerts, tests.CreateTestAlertRule(t, ctx, dbstore, threeSecInterval, mainOrgID)) t.Logf("alert rule: %v added with interval: %d", alerts[1].GetKey(), threeSecInterval) expectedAlertRulesEvaluated = []models.AlertRuleKey{alerts[0].GetKey()} @@ -200,7 +202,7 @@ func TestAlertingTicker(t *testing.T) { }) key := alerts[0].GetKey() - err := dbstore.DeleteAlertRuleByUID(alerts[0].OrgID, alerts[0].UID) + err := dbstore.DeleteAlertRuleByUID(ctx, alerts[0].OrgID, alerts[0].UID) require.NoError(t, err) t.Logf("alert rule: %v deleted", key) @@ -221,7 +223,7 @@ func TestAlertingTicker(t *testing.T) { }) // create alert rule with one second interval - alerts = append(alerts, tests.CreateTestAlertRule(t, dbstore, 1, mainOrgID)) + alerts = append(alerts, tests.CreateTestAlertRule(t, ctx, dbstore, 1, mainOrgID)) expectedAlertRulesEvaluated = []models.AlertRuleKey{alerts[2].GetKey()} t.Run(fmt.Sprintf("on 7th tick alert rules: %s should be evaluated", concatenate(expectedAlertRulesEvaluated)), func(t *testing.T) { @@ -230,7 +232,7 @@ func TestAlertingTicker(t *testing.T) { }) // create alert rule with one second interval under disabled org - alerts = append(alerts, tests.CreateTestAlertRule(t, dbstore, 1, disabledOrgID)) + alerts = append(alerts, tests.CreateTestAlertRule(t, ctx, dbstore, 1, disabledOrgID)) expectedAlertRulesEvaluated = []models.AlertRuleKey{alerts[2].GetKey()} t.Run(fmt.Sprintf("on 8th tick alert rules: %s should be evaluated", concatenate(expectedAlertRulesEvaluated)), func(t *testing.T) { diff --git a/pkg/services/ngalert/schedule/schedule_unit_test.go b/pkg/services/ngalert/schedule/schedule_unit_test.go index 2443afec549..69b2f6bf961 100644 --- a/pkg/services/ngalert/schedule/schedule_unit_test.go +++ b/pkg/services/ngalert/schedule/schedule_unit_test.go @@ -481,6 +481,7 @@ func TestSchedule_ruleRoutine(t *testing.T) { evalChan := make(chan *evalContext) evalAppliedChan := make(chan time.Time) + ctx := context.Background() sch, ruleStore, _, _, _ := createSchedule(evalAppliedChan) rule := CreateTestAlertRule(t, ruleStore, 10, rand.Int63(), randomNormalState()) @@ -503,7 +504,7 @@ func TestSchedule_ruleRoutine(t *testing.T) { // Now update the rule newRule := *rule newRule.Version++ - ruleStore.putRule(&newRule) + ruleStore.putRule(ctx, &newRule) // and call with new version expectedTime = expectedTime.Add(time.Duration(rand.Intn(10)) * time.Second) @@ -680,6 +681,7 @@ func TestSchedule_ruleRoutine(t *testing.T) { evalAppliedChan := make(chan time.Time) updateChan := make(chan struct{}) + ctx := context.Background() sch, ruleStore, _, _, _ := createSchedule(evalAppliedChan) sch.senders[orgID] = s @@ -727,7 +729,7 @@ func TestSchedule_ruleRoutine(t *testing.T) { wg.Wait() newRule := rule newRule.Version++ - ruleStore.putRule(&newRule) + ruleStore.putRule(ctx, &newRule) wg.Add(1) updateChan <- struct{}{} wg.Wait() @@ -1040,6 +1042,8 @@ func setupScheduler(t *testing.T, rs store.RuleStore, is store.InstanceStore, ac // createTestAlertRule creates a dummy alert definition to be used by the tests. func CreateTestAlertRule(t *testing.T, dbstore *fakeRuleStore, intervalSeconds int64, orgID int64, evalResult eval.State) *models.AlertRule { + ctx := context.Background() + t.Helper() records := make([]interface{}, 0, len(dbstore.recordedOps)) copy(records, dbstore.recordedOps) @@ -1079,7 +1083,7 @@ func CreateTestAlertRule(t *testing.T, dbstore *fakeRuleStore, intervalSeconds i require.Fail(t, "Alert rule with desired evaluation result NoData is not supported yet") } - err := dbstore.UpdateRuleGroup(store.UpdateRuleGroupCmd{ + err := dbstore.UpdateRuleGroup(ctx, store.UpdateRuleGroupCmd{ OrgID: orgID, NamespaceUID: "namespace", RuleGroupConfig: apimodels.PostableRuleGroupConfig{ @@ -1117,7 +1121,7 @@ func CreateTestAlertRule(t *testing.T, dbstore *fakeRuleStore, intervalSeconds i NamespaceUID: "namespace", RuleGroup: ruleGroup, } - err = dbstore.GetRuleGroupAlertRules(&q) + err = dbstore.GetRuleGroupAlertRules(ctx, &q) require.NoError(t, err) require.NotEmpty(t, q.Result) diff --git a/pkg/services/ngalert/schedule/testing.go b/pkg/services/ngalert/schedule/testing.go index 353bc4424a3..c18af06311f 100644 --- a/pkg/services/ngalert/schedule/testing.go +++ b/pkg/services/ngalert/schedule/testing.go @@ -70,7 +70,7 @@ type fakeRuleStore struct { } // putRule puts the rule in the rules map. If there are existing rule in the same namespace, they will be overwritten -func (f *fakeRuleStore) putRule(r *models.AlertRule) { +func (f *fakeRuleStore) putRule(_ context.Context, r *models.AlertRule) { f.mtx.Lock() defer f.mtx.Unlock() f.rules[r.OrgID][r.RuleGroup][r.NamespaceUID] = []*models.AlertRule{ @@ -94,15 +94,17 @@ func (f *fakeRuleStore) getRecordedCommands(predicate func(cmd interface{}) (int return result } -func (f *fakeRuleStore) DeleteAlertRuleByUID(_ int64, _ string) error { return nil } -func (f *fakeRuleStore) DeleteNamespaceAlertRules(_ int64, _ string) ([]string, error) { +func (f *fakeRuleStore) DeleteAlertRuleByUID(_ context.Context, _ int64, _ string) error { return nil } +func (f *fakeRuleStore) DeleteNamespaceAlertRules(_ context.Context, _ int64, _ string) ([]string, error) { return []string{}, nil } -func (f *fakeRuleStore) DeleteRuleGroupAlertRules(_ int64, _ string, _ string) ([]string, error) { +func (f *fakeRuleStore) DeleteRuleGroupAlertRules(_ context.Context, _ int64, _ string, _ string) ([]string, error) { return []string{}, nil } -func (f *fakeRuleStore) DeleteAlertInstancesByRuleUID(_ int64, _ string) error { return nil } -func (f *fakeRuleStore) GetAlertRuleByUID(q *models.GetAlertRuleByUIDQuery) error { +func (f *fakeRuleStore) DeleteAlertInstancesByRuleUID(_ context.Context, _ int64, _ string) error { + return nil +} +func (f *fakeRuleStore) GetAlertRuleByUID(_ context.Context, q *models.GetAlertRuleByUIDQuery) error { f.mtx.Lock() defer f.mtx.Unlock() f.recordedOps = append(f.recordedOps, *q) @@ -129,7 +131,7 @@ func (f *fakeRuleStore) GetAlertRuleByUID(q *models.GetAlertRuleByUIDQuery) erro } // For now, we're not implementing namespace filtering. -func (f *fakeRuleStore) GetAlertRulesForScheduling(q *models.ListAlertRulesQuery) error { +func (f *fakeRuleStore) GetAlertRulesForScheduling(_ context.Context, q *models.ListAlertRulesQuery) error { f.mtx.Lock() defer f.mtx.Unlock() f.recordedOps = append(f.recordedOps, *q) @@ -146,19 +148,19 @@ func (f *fakeRuleStore) GetAlertRulesForScheduling(q *models.ListAlertRulesQuery return nil } -func (f *fakeRuleStore) GetOrgAlertRules(q *models.ListAlertRulesQuery) error { +func (f *fakeRuleStore) GetOrgAlertRules(_ context.Context, q *models.ListAlertRulesQuery) error { f.mtx.Lock() defer f.mtx.Unlock() f.recordedOps = append(f.recordedOps, *q) return nil } -func (f *fakeRuleStore) GetNamespaceAlertRules(q *models.ListNamespaceAlertRulesQuery) error { +func (f *fakeRuleStore) GetNamespaceAlertRules(_ context.Context, q *models.ListNamespaceAlertRulesQuery) error { f.mtx.Lock() defer f.mtx.Unlock() f.recordedOps = append(f.recordedOps, *q) return nil } -func (f *fakeRuleStore) GetRuleGroupAlertRules(q *models.ListRuleGroupAlertRulesQuery) error { +func (f *fakeRuleStore) GetRuleGroupAlertRules(_ context.Context, q *models.ListRuleGroupAlertRulesQuery) error { f.mtx.Lock() defer f.mtx.Unlock() f.recordedOps = append(f.recordedOps, *q) @@ -196,7 +198,7 @@ func (f *fakeRuleStore) GetNamespaces(_ context.Context, _ int64, _ *models2.Sig func (f *fakeRuleStore) GetNamespaceByTitle(_ context.Context, _ string, _ int64, _ *models2.SignedInUser, _ bool) (*models2.Folder, error) { return nil, nil } -func (f *fakeRuleStore) GetOrgRuleGroups(q *models.ListOrgRuleGroupsQuery) error { +func (f *fakeRuleStore) GetOrgRuleGroups(_ context.Context, q *models.ListOrgRuleGroupsQuery) error { f.mtx.Lock() defer f.mtx.Unlock() f.recordedOps = append(f.recordedOps, *q) @@ -206,7 +208,7 @@ func (f *fakeRuleStore) GetOrgRuleGroups(q *models.ListOrgRuleGroupsQuery) error return nil } -func (f *fakeRuleStore) UpsertAlertRules(q []store.UpsertRule) error { +func (f *fakeRuleStore) UpsertAlertRules(_ context.Context, q []store.UpsertRule) error { f.mtx.Lock() defer f.mtx.Unlock() f.recordedOps = append(f.recordedOps, q) @@ -215,7 +217,7 @@ func (f *fakeRuleStore) UpsertAlertRules(q []store.UpsertRule) error { } return nil } -func (f *fakeRuleStore) UpdateRuleGroup(cmd store.UpdateRuleGroupCmd) error { +func (f *fakeRuleStore) UpdateRuleGroup(_ context.Context, cmd store.UpdateRuleGroupCmd) error { f.mtx.Lock() defer f.mtx.Unlock() f.recordedOps = append(f.recordedOps, cmd) diff --git a/pkg/services/ngalert/state/manager.go b/pkg/services/ngalert/state/manager.go index a1db3b23ef2..7045a3e8124 100644 --- a/pkg/services/ngalert/state/manager.go +++ b/pkg/services/ngalert/state/manager.go @@ -51,7 +51,7 @@ func (st *Manager) Close() { st.quit <- struct{}{} } -func (st *Manager) Warm() { +func (st *Manager) Warm(ctx context.Context) { st.log.Info("warming cache for startup") st.ResetCache() @@ -66,7 +66,7 @@ func (st *Manager) Warm() { ruleCmd := ngModels.ListAlertRulesQuery{ OrgID: orgId, } - if err := st.ruleStore.GetOrgAlertRules(&ruleCmd); err != nil { + if err := st.ruleStore.GetOrgAlertRules(ctx, &ruleCmd); err != nil { st.log.Error("unable to fetch previous state", "msg", err.Error()) } diff --git a/pkg/services/ngalert/state/manager_test.go b/pkg/services/ngalert/state/manager_test.go index 21ec276a9d8..39d58ac65b9 100644 --- a/pkg/services/ngalert/state/manager_test.go +++ b/pkg/services/ngalert/state/manager_test.go @@ -1511,10 +1511,11 @@ func TestStaleResultsHandler(t *testing.T) { t.Fatalf("error parsing date format: %s", err.Error()) } + ctx := context.Background() _, dbstore := tests.SetupTestEnv(t, 1) const mainOrgID int64 = 1 - rule := tests.CreateTestAlertRule(t, dbstore, 600, mainOrgID) + rule := tests.CreateTestAlertRule(t, ctx, dbstore, 600, mainOrgID) saveCmd1 := &models.SaveAlertInstanceCommand{ RuleOrgID: rule.OrgID, @@ -1587,8 +1588,10 @@ func TestStaleResultsHandler(t *testing.T) { } for _, tc := range testCases { + ctx := context.Background() st := state.NewManager(log.New("test_stale_results_handler"), testMetrics.GetStateMetrics(), nil, dbstore, dbstore) - st.Warm() + st.Warm(ctx) + existingStatesForRule := st.GetStatesForRuleUID(rule.OrgID, rule.UID) // We have loaded the expected number of entries from the db diff --git a/pkg/services/ngalert/store/alert_rule.go b/pkg/services/ngalert/store/alert_rule.go index e4a991d931c..a0a16ec7871 100644 --- a/pkg/services/ngalert/store/alert_rule.go +++ b/pkg/services/ngalert/store/alert_rule.go @@ -39,20 +39,20 @@ type UpsertRule struct { // Store is the interface for persisting alert rules and instances 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 - GetNamespaceAlertRules(query *ngmodels.ListNamespaceAlertRulesQuery) error - GetRuleGroupAlertRules(query *ngmodels.ListRuleGroupAlertRulesQuery) error + DeleteAlertRuleByUID(ctx context.Context, orgID int64, ruleUID string) error + DeleteNamespaceAlertRules(ctx context.Context, orgID int64, namespaceUID string) ([]string, error) + DeleteRuleGroupAlertRules(ctx context.Context, orgID int64, namespaceUID string, ruleGroup string) ([]string, error) + DeleteAlertInstancesByRuleUID(ctx context.Context, orgID int64, ruleUID string) error + GetAlertRuleByUID(ctx context.Context, query *ngmodels.GetAlertRuleByUIDQuery) error + GetAlertRulesForScheduling(ctx context.Context, query *ngmodels.ListAlertRulesQuery) error + GetOrgAlertRules(ctx context.Context, query *ngmodels.ListAlertRulesQuery) error + GetNamespaceAlertRules(ctx context.Context, query *ngmodels.ListNamespaceAlertRulesQuery) error + GetRuleGroupAlertRules(ctx context.Context, query *ngmodels.ListRuleGroupAlertRulesQuery) error GetNamespaces(context.Context, int64, *models.SignedInUser) (map[string]*models.Folder, error) GetNamespaceByTitle(context.Context, string, int64, *models.SignedInUser, bool) (*models.Folder, error) - GetOrgRuleGroups(query *ngmodels.ListOrgRuleGroupsQuery) error - UpsertAlertRules([]UpsertRule) error - UpdateRuleGroup(UpdateRuleGroupCmd) error + GetOrgRuleGroups(ctx context.Context, query *ngmodels.ListOrgRuleGroupsQuery) error + UpsertAlertRules(ctx context.Context, rule []UpsertRule) error + UpdateRuleGroup(ctx context.Context, cmd UpdateRuleGroupCmd) error } func getAlertRuleByUID(sess *sqlstore.DBSession, alertRuleUID string, orgID int64) (*ngmodels.AlertRule, error) { @@ -69,8 +69,8 @@ func getAlertRuleByUID(sess *sqlstore.DBSession, alertRuleUID string, orgID int6 } // DeleteAlertRuleByUID is a handler for deleting an alert rule. -func (st DBstore) DeleteAlertRuleByUID(orgID int64, ruleUID string) error { - return st.SQLStore.WithTransactionalDbSession(context.Background(), func(sess *sqlstore.DBSession) error { +func (st DBstore) DeleteAlertRuleByUID(ctx context.Context, orgID int64, ruleUID string) error { + return st.SQLStore.WithTransactionalDbSession(ctx, func(sess *sqlstore.DBSession) error { _, err := sess.Exec("DELETE FROM alert_rule WHERE org_id = ? AND uid = ?", orgID, ruleUID) if err != nil { return err @@ -91,10 +91,10 @@ func (st DBstore) DeleteAlertRuleByUID(orgID int64, ruleUID string) error { } // DeleteNamespaceAlertRules is a handler for deleting namespace alert rules. A list of deleted rule UIDs are returned. -func (st DBstore) DeleteNamespaceAlertRules(orgID int64, namespaceUID string) ([]string, error) { +func (st DBstore) DeleteNamespaceAlertRules(ctx context.Context, orgID int64, namespaceUID string) ([]string, error) { ruleUIDs := []string{} - err := st.SQLStore.WithTransactionalDbSession(context.Background(), func(sess *sqlstore.DBSession) error { + err := st.SQLStore.WithTransactionalDbSession(ctx, func(sess *sqlstore.DBSession) error { if err := sess.SQL("SELECT uid FROM alert_rule WHERE org_id = ? and namespace_uid = ?", orgID, namespaceUID).Find(&ruleUIDs); err != nil { return err } @@ -123,10 +123,10 @@ func (st DBstore) DeleteNamespaceAlertRules(orgID int64, namespaceUID string) ([ } // DeleteRuleGroupAlertRules is a handler for deleting rule group alert rules. A list of deleted rule UIDs are returned. -func (st DBstore) DeleteRuleGroupAlertRules(orgID int64, namespaceUID string, ruleGroup string) ([]string, error) { +func (st DBstore) DeleteRuleGroupAlertRules(ctx context.Context, orgID int64, namespaceUID string, ruleGroup string) ([]string, error) { ruleUIDs := []string{} - err := st.SQLStore.WithTransactionalDbSession(context.Background(), func(sess *sqlstore.DBSession) error { + err := st.SQLStore.WithTransactionalDbSession(ctx, func(sess *sqlstore.DBSession) error { if err := sess.SQL("SELECT uid FROM alert_rule WHERE org_id = ? and namespace_uid = ? and rule_group = ?", orgID, namespaceUID, ruleGroup).Find(&ruleUIDs); err != nil { return err @@ -161,8 +161,8 @@ func (st DBstore) DeleteRuleGroupAlertRules(orgID int64, namespaceUID string, ru } // 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 { +func (st DBstore) DeleteAlertInstancesByRuleUID(ctx context.Context, orgID int64, ruleUID string) error { + return st.SQLStore.WithTransactionalDbSession(ctx, func(sess *sqlstore.DBSession) error { _, err := sess.Exec("DELETE FROM alert_instance WHERE rule_org_id = ? AND rule_uid = ?", orgID, ruleUID) if err != nil { return err @@ -173,8 +173,8 @@ func (st DBstore) DeleteAlertInstancesByRuleUID(orgID int64, ruleUID string) err // 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 { - return st.SQLStore.WithDbSession(context.Background(), func(sess *sqlstore.DBSession) error { +func (st DBstore) GetAlertRuleByUID(ctx context.Context, query *ngmodels.GetAlertRuleByUIDQuery) error { + return st.SQLStore.WithDbSession(ctx, func(sess *sqlstore.DBSession) error { alertRule, err := getAlertRuleByUID(sess, query.UID, query.OrgID) if err != nil { return err @@ -185,8 +185,8 @@ func (st DBstore) GetAlertRuleByUID(query *ngmodels.GetAlertRuleByUIDQuery) erro } // UpsertAlertRules is a handler for creating/updating alert rules. -func (st DBstore) UpsertAlertRules(rules []UpsertRule) error { - return st.SQLStore.WithTransactionalDbSession(context.Background(), func(sess *sqlstore.DBSession) error { +func (st DBstore) UpsertAlertRules(ctx context.Context, rules []UpsertRule) error { + return st.SQLStore.WithTransactionalDbSession(ctx, func(sess *sqlstore.DBSession) error { newRules := make([]ngmodels.AlertRule, 0, len(rules)) ruleVersions := make([]ngmodels.AlertRuleVersion, 0, len(rules)) for _, r := range rules { @@ -318,8 +318,8 @@ func (st DBstore) UpsertAlertRules(rules []UpsertRule) error { } // GetOrgAlertRules is a handler for retrieving alert rules of specific organisation. -func (st DBstore) GetOrgAlertRules(query *ngmodels.ListAlertRulesQuery) error { - return st.SQLStore.WithDbSession(context.Background(), func(sess *sqlstore.DBSession) error { +func (st DBstore) GetOrgAlertRules(ctx context.Context, query *ngmodels.ListAlertRulesQuery) error { + return st.SQLStore.WithDbSession(ctx, func(sess *sqlstore.DBSession) error { alertRules := make([]*ngmodels.AlertRule, 0) q := "SELECT * FROM alert_rule WHERE org_id = ?" params := []interface{}{query.OrgID} @@ -354,8 +354,8 @@ func (st DBstore) GetOrgAlertRules(query *ngmodels.ListAlertRulesQuery) error { } // GetNamespaceAlertRules is a handler for retrieving namespace alert rules of specific organisation. -func (st DBstore) GetNamespaceAlertRules(query *ngmodels.ListNamespaceAlertRulesQuery) error { - return st.SQLStore.WithDbSession(context.Background(), func(sess *sqlstore.DBSession) error { +func (st DBstore) GetNamespaceAlertRules(ctx context.Context, query *ngmodels.ListNamespaceAlertRulesQuery) error { + return st.SQLStore.WithDbSession(ctx, func(sess *sqlstore.DBSession) error { alertRules := make([]*ngmodels.AlertRule, 0) // TODO rewrite using group by namespace_uid, rule_group q := "SELECT * FROM alert_rule WHERE org_id = ? and namespace_uid = ?" @@ -369,8 +369,8 @@ func (st DBstore) GetNamespaceAlertRules(query *ngmodels.ListNamespaceAlertRules } // GetRuleGroupAlertRules is a handler for retrieving rule group alert rules of specific organisation. -func (st DBstore) GetRuleGroupAlertRules(query *ngmodels.ListRuleGroupAlertRulesQuery) error { - return st.SQLStore.WithDbSession(context.Background(), func(sess *sqlstore.DBSession) error { +func (st DBstore) GetRuleGroupAlertRules(ctx context.Context, query *ngmodels.ListRuleGroupAlertRulesQuery) error { + return st.SQLStore.WithDbSession(ctx, func(sess *sqlstore.DBSession) error { q := "SELECT * FROM alert_rule WHERE org_id = ? and namespace_uid = ? and rule_group = ?" args := []interface{}{query.OrgID, query.NamespaceUID, query.RuleGroup} @@ -440,8 +440,8 @@ func (st DBstore) GetNamespaceByTitle(ctx context.Context, namespace string, org // GetAlertRulesForScheduling returns alert rule info (identifier, interval, version state) // that is useful for it's scheduling. -func (st DBstore) GetAlertRulesForScheduling(query *ngmodels.ListAlertRulesQuery) error { - return st.SQLStore.WithDbSession(context.Background(), func(sess *sqlstore.DBSession) error { +func (st DBstore) GetAlertRulesForScheduling(ctx context.Context, query *ngmodels.ListAlertRulesQuery) error { + return st.SQLStore.WithDbSession(ctx, func(sess *sqlstore.DBSession) error { alerts := make([]*ngmodels.AlertRule, 0) q := "SELECT uid, org_id, interval_seconds, version FROM alert_rule" if len(query.ExcludeOrgs) > 0 { @@ -511,15 +511,15 @@ func (st DBstore) validateAlertRule(alertRule ngmodels.AlertRule) error { } // UpdateRuleGroup creates new rules and updates and/or deletes existing rules -func (st DBstore) UpdateRuleGroup(cmd UpdateRuleGroupCmd) error { - return st.SQLStore.WithTransactionalDbSession(context.Background(), func(sess *sqlstore.DBSession) error { +func (st DBstore) UpdateRuleGroup(ctx context.Context, cmd UpdateRuleGroupCmd) error { + return st.SQLStore.WithTransactionalDbSession(ctx, func(sess *sqlstore.DBSession) error { ruleGroup := cmd.RuleGroupConfig.Name q := &ngmodels.ListRuleGroupAlertRulesQuery{ OrgID: cmd.OrgID, NamespaceUID: cmd.NamespaceUID, RuleGroup: ruleGroup, } - if err := st.GetRuleGroupAlertRules(q); err != nil { + if err := st.GetRuleGroupAlertRules(ctx, q); err != nil { return err } existingGroupRules := q.Result @@ -578,7 +578,7 @@ func (st DBstore) UpdateRuleGroup(cmd UpdateRuleGroupCmd) error { upsertRules = append(upsertRules, upsertRule) } - if err := st.UpsertAlertRules(upsertRules); err != nil { + if err := st.UpsertAlertRules(ctx, upsertRules); err != nil { if st.SQLStore.Dialect.IsUniqueConstraintViolation(err) { return ngmodels.ErrAlertRuleUniqueConstraintViolation } @@ -588,7 +588,7 @@ func (st DBstore) UpdateRuleGroup(cmd UpdateRuleGroupCmd) error { // 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 { + if err := st.DeleteAlertInstancesByRuleUID(ctx, cmd.OrgID, rule.UID); err != nil { return err } } @@ -596,7 +596,7 @@ func (st DBstore) UpdateRuleGroup(cmd UpdateRuleGroupCmd) error { // delete the remaining rules for ruleUID := range existingGroupRulesUIDs { - if err := st.DeleteAlertRuleByUID(cmd.OrgID, ruleUID); err != nil { + if err := st.DeleteAlertRuleByUID(ctx, cmd.OrgID, ruleUID); err != nil { return err } } @@ -604,8 +604,8 @@ func (st DBstore) UpdateRuleGroup(cmd UpdateRuleGroupCmd) error { }) } -func (st DBstore) GetOrgRuleGroups(query *ngmodels.ListOrgRuleGroupsQuery) error { - return st.SQLStore.WithDbSession(context.Background(), func(sess *sqlstore.DBSession) error { +func (st DBstore) GetOrgRuleGroups(ctx context.Context, query *ngmodels.ListOrgRuleGroupsQuery) error { + return st.SQLStore.WithDbSession(ctx, func(sess *sqlstore.DBSession) error { var ruleGroups [][]string q := ` SELECT DISTINCT diff --git a/pkg/services/ngalert/store/instance_database_test.go b/pkg/services/ngalert/store/instance_database_test.go index 274d3416bcf..8b0ee24daec 100644 --- a/pkg/services/ngalert/store/instance_database_test.go +++ b/pkg/services/ngalert/store/instance_database_test.go @@ -4,6 +4,7 @@ package store_test import ( + "context" "testing" "time" @@ -26,20 +27,21 @@ func mockTimeNow() { } func TestAlertInstanceOperations(t *testing.T) { + ctx := context.Background() _, dbstore := tests.SetupTestEnv(t, baseIntervalSeconds) const mainOrgID int64 = 1 - alertRule1 := tests.CreateTestAlertRule(t, dbstore, 60, mainOrgID) + alertRule1 := tests.CreateTestAlertRule(t, ctx, dbstore, 60, mainOrgID) orgID := alertRule1.OrgID - alertRule2 := tests.CreateTestAlertRule(t, dbstore, 60, mainOrgID) + alertRule2 := tests.CreateTestAlertRule(t, ctx, dbstore, 60, mainOrgID) require.Equal(t, orgID, alertRule2.OrgID) - alertRule3 := tests.CreateTestAlertRule(t, dbstore, 60, mainOrgID) + alertRule3 := tests.CreateTestAlertRule(t, ctx, dbstore, 60, mainOrgID) require.Equal(t, orgID, alertRule3.OrgID) - alertRule4 := tests.CreateTestAlertRule(t, dbstore, 60, mainOrgID) + alertRule4 := tests.CreateTestAlertRule(t, ctx, dbstore, 60, mainOrgID) require.Equal(t, orgID, alertRule4.OrgID) t.Run("can save and read new alert instance", func(t *testing.T) { diff --git a/pkg/services/ngalert/tests/util.go b/pkg/services/ngalert/tests/util.go index d5fc939e3fa..3e11cb1c722 100644 --- a/pkg/services/ngalert/tests/util.go +++ b/pkg/services/ngalert/tests/util.go @@ -1,6 +1,7 @@ package tests import ( + "context" "encoding/json" "fmt" "testing" @@ -50,9 +51,9 @@ func SetupTestEnv(t *testing.T, baseInterval time.Duration) (*ngalert.AlertNG, * } // CreateTestAlertRule creates a dummy alert definition to be used by the tests. -func CreateTestAlertRule(t *testing.T, dbstore *store.DBstore, intervalSeconds int64, orgID int64) *models.AlertRule { +func CreateTestAlertRule(t *testing.T, ctx context.Context, dbstore *store.DBstore, intervalSeconds int64, orgID int64) *models.AlertRule { ruleGroup := fmt.Sprintf("ruleGroup-%s", util.GenerateShortUID()) - err := dbstore.UpdateRuleGroup(store.UpdateRuleGroupCmd{ + err := dbstore.UpdateRuleGroup(ctx, store.UpdateRuleGroupCmd{ OrgID: orgID, NamespaceUID: "namespace", RuleGroupConfig: apimodels.PostableRuleGroupConfig{ @@ -92,7 +93,7 @@ func CreateTestAlertRule(t *testing.T, dbstore *store.DBstore, intervalSeconds i NamespaceUID: "namespace", RuleGroup: ruleGroup, } - err = dbstore.GetRuleGroupAlertRules(&q) + err = dbstore.GetRuleGroupAlertRules(ctx, &q) require.NoError(t, err) require.NotEmpty(t, q.Result) @@ -102,7 +103,7 @@ func CreateTestAlertRule(t *testing.T, dbstore *store.DBstore, intervalSeconds i } // updateTestAlertRule update a dummy alert definition to be used by the tests. -func UpdateTestAlertRuleIntervalSeconds(t *testing.T, dbstore *store.DBstore, existingRule *models.AlertRule, intervalSeconds int64) *models.AlertRule { +func UpdateTestAlertRuleIntervalSeconds(t *testing.T, ctx context.Context, dbstore *store.DBstore, existingRule *models.AlertRule, intervalSeconds int64) *models.AlertRule { cmd := store.UpdateRuleGroupCmd{ OrgID: 1, NamespaceUID: "namespace", @@ -119,7 +120,7 @@ func UpdateTestAlertRuleIntervalSeconds(t *testing.T, dbstore *store.DBstore, ex }, } - err := dbstore.UpdateRuleGroup(cmd) + err := dbstore.UpdateRuleGroup(ctx, cmd) require.NoError(t, err) q := models.ListRuleGroupAlertRulesQuery{ @@ -127,7 +128,7 @@ func UpdateTestAlertRuleIntervalSeconds(t *testing.T, dbstore *store.DBstore, ex NamespaceUID: "namespace", RuleGroup: existingRule.RuleGroup, } - err = dbstore.GetRuleGroupAlertRules(&q) + err = dbstore.GetRuleGroupAlertRules(ctx, &q) require.NoError(t, err) require.NotEmpty(t, q.Result)