Alerting: Add provenance to Prometheus API (#106596)

This commit adds provenance information to the Prometheus API in the ngalert service to enable compatibility with the new alert list page.
This commit is contained in:
Moustafa Baiou
2025-06-17 23:31:42 +02:00
committed by GitHub
parent 693bc51693
commit 74e800e427
4 changed files with 121 additions and 29 deletions
@@ -749,6 +749,7 @@ func TestRouteGetRuleStatuses(t *testing.T) {
fakeSch,
ruleStore,
&fakeRuleAccessControlService{},
fakes.NewFakeProvisioningStore(),
)
response := api.RouteGetRuleStatuses(c)
@@ -811,6 +812,7 @@ func TestRouteGetRuleStatuses(t *testing.T) {
newFakeSchedulerReader(t).setupStates(fakeAIM),
ruleStore,
accesscontrol.NewRuleService(acimpl.ProvideAccessControl(featuremgmt.WithFeatures())),
fakes.NewFakeProvisioningStore(),
)
permissions := createPermissionsForRules(slices.Concat(rulesInGroup1, rulesInGroup2, rulesInGroup3), orgID)
@@ -937,6 +939,7 @@ func TestRouteGetRuleStatuses(t *testing.T) {
newFakeSchedulerReader(t).setupStates(fakeAIM),
ruleStore,
accesscontrol.NewRuleService(acimpl.ProvideAccessControl(featuremgmt.WithFeatures())),
fakes.NewFakeProvisioningStore(),
)
permissions := createPermissionsForRules(allRules, orgID)
@@ -1081,6 +1084,7 @@ func TestRouteGetRuleStatuses(t *testing.T) {
newFakeSchedulerReader(t).setupStates(fakeAIM),
ruleStore,
accesscontrol.NewRuleService(acimpl.ProvideAccessControl(featuremgmt.WithFeatures())),
fakes.NewFakeProvisioningStore(),
)
c := &contextmodel.ReqContext{Context: &web.Context{Req: req}, SignedInUser: &user.SignedInUser{OrgID: orgID, Permissions: createPermissionsForRules(rules, orgID)}}
@@ -1980,13 +1984,73 @@ func TestRouteGetRuleStatuses(t *testing.T) {
require.Equal(t, "webhook-a", rg.Rules[0].NotificationSettings.Receiver)
})
})
t.Run("provenance as expected", func(t *testing.T) {
fakeStore, fakeAIM, api, provStore := setupAPIFull(t)
// Rule without provenance
ruleNoProv := gen.With(gen.WithOrgID(orgID), asFixture(), withClassicConditionSingleQuery()).GenerateRef()
fakeAIM.GenerateAlertInstances(orgID, ruleNoProv.UID, 1)
fakeStore.PutRule(context.Background(), ruleNoProv)
// Rule with provenance
ruleWithProv := gen.With(gen.WithOrgID(orgID), asFixture(), withClassicConditionSingleQuery()).GenerateRef()
ruleWithProv.UID = "provRuleUID"
ruleWithProv.Title = "ProvisionedRule"
fakeAIM.GenerateAlertInstances(orgID, ruleWithProv.UID, 1)
fakeStore.PutRule(context.Background(), ruleWithProv)
// Add provenance for ruleWithProv
err := provStore.SetProvenance(context.Background(), ruleWithProv, orgID, ngmodels.ProvenanceAPI)
require.NoError(t, err)
req, err := http.NewRequest("GET", "/api/v1/rules", nil)
require.NoError(t, err)
c := &contextmodel.ReqContext{
Context: &web.Context{Req: req},
SignedInUser: &user.SignedInUser{
OrgID: orgID,
Permissions: map[int64]map[string][]string{orgID: {datasources.ActionQuery: {datasources.ScopeAll}}},
},
}
resp := api.RouteGetRuleStatuses(c)
require.Equal(t, http.StatusOK, resp.Status())
var res apimodels.RuleResponse
require.NoError(t, json.Unmarshal(resp.Body(), &res))
// Should have two rules in one group
require.Len(t, res.Data.RuleGroups, 1)
rg := res.Data.RuleGroups[0]
require.Len(t, rg.Rules, 2)
// Find rules by UID
var foundNoProv, foundWithProv bool
for _, rule := range rg.Rules {
switch rule.UID {
case ruleNoProv.UID:
foundNoProv = true
require.Equal(t, apimodels.Provenance(ngmodels.ProvenanceNone), rule.Provenance, "non-provisioned rule should have empty provenance")
case ruleWithProv.UID:
foundWithProv = true
require.Equal(t, apimodels.Provenance(ngmodels.ProvenanceAPI), rule.Provenance, "provisioned rule should have provenance set")
}
}
require.True(t, foundNoProv, "should find rule without provenance")
require.True(t, foundWithProv, "should find rule with provenance")
})
}
func setupAPI(t *testing.T) (*fakes.RuleStore, *fakeAlertInstanceManager, PrometheusSrv) {
fakeStore, fakeAIM, api, _ := setupAPIFull(t)
return fakeStore, fakeAIM, api
}
func setupAPIFull(t *testing.T) (*fakes.RuleStore, *fakeAlertInstanceManager, PrometheusSrv, *fakes.FakeProvisioningStore) {
fakeStore := fakes.NewRuleStore(t)
fakeAIM := NewFakeAlertInstanceManager(t)
fakeSch := newFakeSchedulerReader(t).setupStates(fakeAIM)
fakeAuthz := &fakeRuleAccessControlService{}
fakeProvisioning := fakes.NewFakeProvisioningStore()
api := *NewPrometheusSrv(
log.NewNopLogger(),
@@ -1994,9 +2058,10 @@ func setupAPI(t *testing.T) (*fakes.RuleStore, *fakeAlertInstanceManager, Promet
fakeSch,
fakeStore,
fakeAuthz,
fakeProvisioning,
)
return fakeStore, fakeAIM, api
return fakeStore, fakeAIM, api, fakeProvisioning
}
func generateRuleAndInstanceWithQuery(t *testing.T, orgID int64, fakeAIM *fakeAlertInstanceManager, fakeStore *fakes.RuleStore, query ngmodels.AlertRuleMutator, additionalMutators ...ngmodels.AlertRuleMutator) {