Alerting: decouple api models from domain\dto models: separate Provenance status + converters (#63594)

* move conversions of domain models to api models and reverse from definition package to api package
pull/63828/head
Yuri Tseretyan 2 years ago committed by GitHub
parent dc01e1ee6a
commit f561e71de8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 8
      pkg/services/ngalert/api/api_alertmanager_guards.go
  2. 36
      pkg/services/ngalert/api/api_alertmanager_guards_test.go
  3. 10
      pkg/services/ngalert/api/api_alertmanager_test.go
  4. 22
      pkg/services/ngalert/api/api_provisioning.go
  5. 6
      pkg/services/ngalert/api/api_provisioning_test.go
  6. 2
      pkg/services/ngalert/api/api_ruler.go
  7. 4
      pkg/services/ngalert/api/api_ruler_test.go
  8. 91
      pkg/services/ngalert/api/compat.go
  9. 14
      pkg/services/ngalert/api/compat_test.go
  10. 27
      pkg/services/ngalert/api/tooling/definitions/alertmanager.go
  11. 2
      pkg/services/ngalert/api/tooling/definitions/cortex-ruler.go
  12. 80
      pkg/services/ngalert/api/tooling/definitions/provisioning_alert_rules.go
  13. 4
      pkg/services/ngalert/api/tooling/definitions/provisioning_mute_timings.go
  14. 10
      pkg/services/ngalert/api/tooling/definitions/provisioning_templates.go
  15. 14
      pkg/services/ngalert/notifier/alertmanager_config.go
  16. 4
      pkg/services/ngalert/provisioning/mute_timings.go
  17. 2
      pkg/services/ngalert/provisioning/notification_policies.go
  18. 4
      pkg/services/ngalert/provisioning/notification_policies_test.go
  19. 2
      pkg/services/ngalert/provisioning/templates.go
  20. 2
      pkg/services/provisioning/alerting/mute_times_provisioner.go
  21. 3
      pkg/services/provisioning/alerting/text_templates.go

@ -34,7 +34,7 @@ func checkRoutes(currentConfig apimodels.GettableUserConfig, newConfig apimodels
reporter := cmputil.DiffReporter{} reporter := cmputil.DiffReporter{}
options := []cmp.Option{cmp.Reporter(&reporter), cmpopts.EquateEmpty(), cmpopts.IgnoreUnexported(labels.Matcher{})} options := []cmp.Option{cmp.Reporter(&reporter), cmpopts.EquateEmpty(), cmpopts.IgnoreUnexported(labels.Matcher{})}
routesEqual := cmp.Equal(currentConfig.AlertmanagerConfig.Route, newConfig.AlertmanagerConfig.Route, options...) routesEqual := cmp.Equal(currentConfig.AlertmanagerConfig.Route, newConfig.AlertmanagerConfig.Route, options...)
if !routesEqual && currentConfig.AlertmanagerConfig.Route.Provenance != ngmodels.ProvenanceNone { if !routesEqual && currentConfig.AlertmanagerConfig.Route.Provenance != apimodels.Provenance(ngmodels.ProvenanceNone) {
return fmt.Errorf("policies were provisioned and cannot be changed through the UI") return fmt.Errorf("policies were provisioned and cannot be changed through the UI")
} }
return nil return nil
@ -44,7 +44,7 @@ func checkTemplates(currentConfig apimodels.GettableUserConfig, newConfig apimod
for name, template := range currentConfig.TemplateFiles { for name, template := range currentConfig.TemplateFiles {
provenance := ngmodels.ProvenanceNone provenance := ngmodels.ProvenanceNone
if prov, present := currentConfig.TemplateFileProvenances[name]; present { if prov, present := currentConfig.TemplateFileProvenances[name]; present {
provenance = prov provenance = ngmodels.Provenance(prov)
} }
if provenance == ngmodels.ProvenanceNone { if provenance == ngmodels.ProvenanceNone {
continue // we are only interested in non none continue // we are only interested in non none
@ -76,7 +76,7 @@ func checkContactPoints(currReceivers []*apimodels.GettableApiReceiver, newRecei
} }
for _, existingReceiver := range currReceivers { for _, existingReceiver := range currReceivers {
for _, contactPoint := range existingReceiver.GrafanaManagedReceivers { for _, contactPoint := range existingReceiver.GrafanaManagedReceivers {
if contactPoint.Provenance == ngmodels.ProvenanceNone { if contactPoint.Provenance == apimodels.Provenance(ngmodels.ProvenanceNone) {
continue // we are only interested in non none continue // we are only interested in non none
} }
postedContactPoint, present := newCPs[contactPoint.UID] postedContactPoint, present := newCPs[contactPoint.UID]
@ -133,7 +133,7 @@ func checkMuteTimes(currentConfig apimodels.GettableUserConfig, newConfig apimod
for _, muteTime := range currentConfig.AlertmanagerConfig.MuteTimeIntervals { for _, muteTime := range currentConfig.AlertmanagerConfig.MuteTimeIntervals {
provenance := ngmodels.ProvenanceNone provenance := ngmodels.ProvenanceNone
if prov, present := currentConfig.AlertmanagerConfig.MuteTimeProvenances[muteTime.Name]; present { if prov, present := currentConfig.AlertmanagerConfig.MuteTimeProvenances[muteTime.Name]; present {
provenance = prov provenance = ngmodels.Provenance(prov)
} }
if provenance == ngmodels.ProvenanceNone { if provenance == ngmodels.ProvenanceNone {
continue // we are only interested in non none continue // we are only interested in non none

@ -65,7 +65,7 @@ func gettableRoute(t *testing.T, provenance models.Provenance) definitions.Getta
AlertmanagerConfig: definitions.GettableApiAlertingConfig{ AlertmanagerConfig: definitions.GettableApiAlertingConfig{
Config: definitions.Config{ Config: definitions.Config{
Route: &definitions.Route{ Route: &definitions.Route{
Provenance: provenance, Provenance: definitions.Provenance(provenance),
Continue: true, Continue: true,
GroupBy: []model.LabelName{ GroupBy: []model.LabelName{
"...", "...",
@ -100,7 +100,7 @@ func postableRoute(t *testing.T, provenace models.Provenance) definitions.Postab
AlertmanagerConfig: definitions.PostableApiAlertingConfig{ AlertmanagerConfig: definitions.PostableApiAlertingConfig{
Config: definitions.Config{ Config: definitions.Config{
Route: &definitions.Route{ Route: &definitions.Route{
Provenance: provenace, Provenance: definitions.Provenance(provenace),
Continue: true, Continue: true,
GroupBy: []model.LabelName{ GroupBy: []model.LabelName{
"...", "...",
@ -199,8 +199,8 @@ func gettableTemplates(t *testing.T, name string, provenance models.Provenance)
TemplateFiles: map[string]string{ TemplateFiles: map[string]string{
name: "some-template", name: "some-template",
}, },
TemplateFileProvenances: map[string]models.Provenance{ TemplateFileProvenances: map[string]definitions.Provenance{
name: provenance, name: definitions.Provenance(provenance),
}, },
} }
} }
@ -315,7 +315,7 @@ func defaultGettableReceiver(t *testing.T, uid string, provenance models.Provena
Name: "yeah", Name: "yeah",
Type: "slack", Type: "slack",
DisableResolveMessage: true, DisableResolveMessage: true,
Provenance: provenance, Provenance: definitions.Provenance(provenance),
SecureFields: map[string]bool{ SecureFields: map[string]bool{
"url": true, "url": true,
}, },
@ -368,8 +368,8 @@ func TestCheckMuteTimes(t *testing.T) {
TimeIntervals: defaultInterval(t), TimeIntervals: defaultInterval(t),
}, },
}, },
map[string]models.Provenance{ map[string]definitions.Provenance{
"test-1": models.ProvenanceNone, "test-1": definitions.Provenance(models.ProvenanceNone),
}), }),
newConfig: postableMuteIntervals(t, newConfig: postableMuteIntervals(t,
[]amConfig.MuteTimeInterval{ []amConfig.MuteTimeInterval{
@ -393,8 +393,8 @@ func TestCheckMuteTimes(t *testing.T) {
TimeIntervals: defaultInterval(t), TimeIntervals: defaultInterval(t),
}, },
}, },
map[string]models.Provenance{ map[string]definitions.Provenance{
"test-1": models.ProvenanceNone, "test-1": definitions.Provenance(models.ProvenanceNone),
}), }),
newConfig: postableMuteIntervals(t, []amConfig.MuteTimeInterval{}), newConfig: postableMuteIntervals(t, []amConfig.MuteTimeInterval{}),
}, },
@ -412,8 +412,8 @@ func TestCheckMuteTimes(t *testing.T) {
TimeIntervals: defaultInterval(t), TimeIntervals: defaultInterval(t),
}, },
}, },
map[string]models.Provenance{ map[string]definitions.Provenance{
"test-1": models.ProvenanceAPI, "test-1": definitions.Provenance(models.ProvenanceAPI),
}), }),
newConfig: postableMuteIntervals(t, []amConfig.MuteTimeInterval{ newConfig: postableMuteIntervals(t, []amConfig.MuteTimeInterval{
{ {
@ -432,8 +432,8 @@ func TestCheckMuteTimes(t *testing.T) {
TimeIntervals: defaultInterval(t), TimeIntervals: defaultInterval(t),
}, },
}, },
map[string]models.Provenance{ map[string]definitions.Provenance{
"test-1": models.ProvenanceNone, "test-1": definitions.Provenance(models.ProvenanceNone),
}), }),
newConfig: postableMuteIntervals(t, newConfig: postableMuteIntervals(t,
[]amConfig.MuteTimeInterval{ []amConfig.MuteTimeInterval{
@ -457,8 +457,8 @@ func TestCheckMuteTimes(t *testing.T) {
TimeIntervals: defaultInterval(t), TimeIntervals: defaultInterval(t),
}, },
}, },
map[string]models.Provenance{ map[string]definitions.Provenance{
"test-1": models.ProvenanceNone, "test-1": definitions.Provenance(models.ProvenanceNone),
}), }),
newConfig: postableMuteIntervals(t, newConfig: postableMuteIntervals(t,
[]amConfig.MuteTimeInterval{ []amConfig.MuteTimeInterval{
@ -487,8 +487,8 @@ func TestCheckMuteTimes(t *testing.T) {
TimeIntervals: defaultInterval(t), TimeIntervals: defaultInterval(t),
}, },
}, },
map[string]models.Provenance{ map[string]definitions.Provenance{
"test-1": models.ProvenanceAPI, "test-1": definitions.Provenance(models.ProvenanceAPI),
}), }),
newConfig: postableMuteIntervals(t, newConfig: postableMuteIntervals(t,
[]amConfig.MuteTimeInterval{ []amConfig.MuteTimeInterval{
@ -520,7 +520,7 @@ func TestCheckMuteTimes(t *testing.T) {
} }
} }
func gettableMuteIntervals(t *testing.T, muteTimeIntervals []amConfig.MuteTimeInterval, provenances map[string]models.Provenance) definitions.GettableUserConfig { func gettableMuteIntervals(t *testing.T, muteTimeIntervals []amConfig.MuteTimeInterval, provenances map[string]definitions.Provenance) definitions.GettableUserConfig {
return definitions.GettableUserConfig{ return definitions.GettableUserConfig{
AlertmanagerConfig: definitions.GettableApiAlertingConfig{ AlertmanagerConfig: definitions.GettableApiAlertingConfig{
MuteTimeProvenances: provenances, MuteTimeProvenances: provenances,

@ -223,7 +223,7 @@ func TestAlertmanagerConfig(t *testing.T) {
response := sut.RouteGetAlertingConfig(rc) response := sut.RouteGetAlertingConfig(rc)
body := asGettableUserConfig(t, response) body := asGettableUserConfig(t, response)
require.Equal(t, ngmodels.ProvenanceNone, body.AlertmanagerConfig.Route.Provenance) require.Equal(t, apimodels.Provenance(ngmodels.ProvenanceNone), body.AlertmanagerConfig.Route.Provenance)
}) })
t.Run("contact point from GET config has no provenance", func(t *testing.T) { t.Run("contact point from GET config has no provenance", func(t *testing.T) {
sut := createSut(t, nil) sut := createSut(t, nil)
@ -232,7 +232,7 @@ func TestAlertmanagerConfig(t *testing.T) {
response := sut.RouteGetAlertingConfig(rc) response := sut.RouteGetAlertingConfig(rc)
body := asGettableUserConfig(t, response) body := asGettableUserConfig(t, response)
require.Equal(t, ngmodels.ProvenanceNone, body.AlertmanagerConfig.Receivers[0].GrafanaManagedReceivers[0].Provenance) require.Equal(t, apimodels.Provenance(ngmodels.ProvenanceNone), body.AlertmanagerConfig.Receivers[0].GrafanaManagedReceivers[0].Provenance)
}) })
t.Run("templates from GET config have no provenance", func(t *testing.T) { t.Run("templates from GET config have no provenance", func(t *testing.T) {
sut := createSut(t, nil) sut := createSut(t, nil)
@ -254,7 +254,7 @@ func TestAlertmanagerConfig(t *testing.T) {
response := sut.RouteGetAlertingConfig(rc) response := sut.RouteGetAlertingConfig(rc)
body := asGettableUserConfig(t, response) body := asGettableUserConfig(t, response)
require.Equal(t, ngmodels.ProvenanceAPI, body.AlertmanagerConfig.Route.Provenance) require.Equal(t, apimodels.Provenance(ngmodels.ProvenanceAPI), body.AlertmanagerConfig.Route.Provenance)
}) })
t.Run("contact point from GET config has expected provenance", func(t *testing.T) { t.Run("contact point from GET config has expected provenance", func(t *testing.T) {
sut := createSut(t, nil) sut := createSut(t, nil)
@ -274,7 +274,7 @@ func TestAlertmanagerConfig(t *testing.T) {
response = sut.RouteGetAlertingConfig(rc) response = sut.RouteGetAlertingConfig(rc)
body = asGettableUserConfig(t, response) body = asGettableUserConfig(t, response)
require.Equal(t, ngmodels.ProvenanceAPI, body.AlertmanagerConfig.Receivers[0].GrafanaManagedReceivers[0].Provenance) require.Equal(t, apimodels.Provenance(ngmodels.ProvenanceAPI), body.AlertmanagerConfig.Receivers[0].GrafanaManagedReceivers[0].Provenance)
}) })
t.Run("templates from GET config have expected provenance", func(t *testing.T) { t.Run("templates from GET config have expected provenance", func(t *testing.T) {
sut := createSut(t, nil) sut := createSut(t, nil)
@ -286,7 +286,7 @@ func TestAlertmanagerConfig(t *testing.T) {
body := asGettableUserConfig(t, response) body := asGettableUserConfig(t, response)
require.NotNil(t, body.TemplateFileProvenances) require.NotNil(t, body.TemplateFileProvenances)
require.Len(t, body.TemplateFileProvenances, 1) require.Len(t, body.TemplateFileProvenances, 1)
require.Equal(t, ngmodels.ProvenanceAPI, body.TemplateFileProvenances["a"]) require.Equal(t, apimodels.Provenance(ngmodels.ProvenanceAPI), body.TemplateFileProvenances["a"])
}) })
}) })
} }

@ -177,7 +177,7 @@ func (srv *ProvisioningSrv) RoutePutTemplate(c *contextmodel.ReqContext, body de
tmpl := definitions.NotificationTemplate{ tmpl := definitions.NotificationTemplate{
Name: name, Name: name,
Template: body.Template, Template: body.Template,
Provenance: alerting_models.ProvenanceAPI, Provenance: definitions.Provenance(alerting_models.ProvenanceAPI),
} }
modified, err := srv.templates.SetTemplate(c.Req.Context(), c.OrgID, tmpl) modified, err := srv.templates.SetTemplate(c.Req.Context(), c.OrgID, tmpl)
if err != nil { if err != nil {
@ -219,7 +219,7 @@ func (srv *ProvisioningSrv) RouteGetMuteTimings(c *contextmodel.ReqContext) resp
} }
func (srv *ProvisioningSrv) RoutePostMuteTiming(c *contextmodel.ReqContext, mt definitions.MuteTimeInterval) response.Response { func (srv *ProvisioningSrv) RoutePostMuteTiming(c *contextmodel.ReqContext, mt definitions.MuteTimeInterval) response.Response {
mt.Provenance = alerting_models.ProvenanceAPI mt.Provenance = definitions.Provenance(alerting_models.ProvenanceAPI)
created, err := srv.muteTimings.CreateMuteTiming(c.Req.Context(), mt, c.OrgID) created, err := srv.muteTimings.CreateMuteTiming(c.Req.Context(), mt, c.OrgID)
if err != nil { if err != nil {
if errors.Is(err, provisioning.ErrValidation) { if errors.Is(err, provisioning.ErrValidation) {
@ -232,7 +232,7 @@ func (srv *ProvisioningSrv) RoutePostMuteTiming(c *contextmodel.ReqContext, mt d
func (srv *ProvisioningSrv) RoutePutMuteTiming(c *contextmodel.ReqContext, mt definitions.MuteTimeInterval, name string) response.Response { func (srv *ProvisioningSrv) RoutePutMuteTiming(c *contextmodel.ReqContext, mt definitions.MuteTimeInterval, name string) response.Response {
mt.Name = name mt.Name = name
mt.Provenance = alerting_models.ProvenanceAPI mt.Provenance = definitions.Provenance(alerting_models.ProvenanceAPI)
updated, err := srv.muteTimings.UpdateMuteTiming(c.Req.Context(), mt, c.OrgID) updated, err := srv.muteTimings.UpdateMuteTiming(c.Req.Context(), mt, c.OrgID)
if err != nil { if err != nil {
if errors.Is(err, provisioning.ErrValidation) { if errors.Is(err, provisioning.ErrValidation) {
@ -259,7 +259,7 @@ func (srv *ProvisioningSrv) RouteGetAlertRules(c *contextmodel.ReqContext) respo
if err != nil { if err != nil {
return ErrResp(http.StatusInternalServerError, err, "") return ErrResp(http.StatusInternalServerError, err, "")
} }
return response.JSON(http.StatusOK, definitions.NewAlertRules(rules)) return response.JSON(http.StatusOK, ProvisionedAlertRuleFromAlertRules(rules))
} }
func (srv *ProvisioningSrv) RouteRouteGetAlertRule(c *contextmodel.ReqContext, UID string) response.Response { func (srv *ProvisioningSrv) RouteRouteGetAlertRule(c *contextmodel.ReqContext, UID string) response.Response {
@ -267,11 +267,11 @@ func (srv *ProvisioningSrv) RouteRouteGetAlertRule(c *contextmodel.ReqContext, U
if err != nil { if err != nil {
return ErrResp(http.StatusInternalServerError, err, "") return ErrResp(http.StatusInternalServerError, err, "")
} }
return response.JSON(http.StatusOK, definitions.NewAlertRule(rule, provenace)) return response.JSON(http.StatusOK, ProvisionedAlertRuleFromAlertRule(rule, provenace))
} }
func (srv *ProvisioningSrv) RoutePostAlertRule(c *contextmodel.ReqContext, ar definitions.ProvisionedAlertRule) response.Response { func (srv *ProvisioningSrv) RoutePostAlertRule(c *contextmodel.ReqContext, ar definitions.ProvisionedAlertRule) response.Response {
upstreamModel, err := ar.UpstreamModel() upstreamModel, err := AlertRuleFromProvisionedAlertRule(ar)
upstreamModel.OrgID = c.OrgID upstreamModel.OrgID = c.OrgID
if err != nil { if err != nil {
return ErrResp(http.StatusBadRequest, err, "") return ErrResp(http.StatusBadRequest, err, "")
@ -291,12 +291,12 @@ func (srv *ProvisioningSrv) RoutePostAlertRule(c *contextmodel.ReqContext, ar de
return ErrResp(http.StatusInternalServerError, err, "") return ErrResp(http.StatusInternalServerError, err, "")
} }
resp := definitions.NewAlertRule(createdAlertRule, provenance) resp := ProvisionedAlertRuleFromAlertRule(createdAlertRule, provenance)
return response.JSON(http.StatusCreated, resp) return response.JSON(http.StatusCreated, resp)
} }
func (srv *ProvisioningSrv) RoutePutAlertRule(c *contextmodel.ReqContext, ar definitions.ProvisionedAlertRule, UID string) response.Response { func (srv *ProvisioningSrv) RoutePutAlertRule(c *contextmodel.ReqContext, ar definitions.ProvisionedAlertRule, UID string) response.Response {
updated, err := ar.UpstreamModel() updated, err := AlertRuleFromProvisionedAlertRule(ar)
if err != nil { if err != nil {
ErrResp(http.StatusBadRequest, err, "") ErrResp(http.StatusBadRequest, err, "")
} }
@ -317,7 +317,7 @@ func (srv *ProvisioningSrv) RoutePutAlertRule(c *contextmodel.ReqContext, ar def
return ErrResp(http.StatusInternalServerError, err, "") return ErrResp(http.StatusInternalServerError, err, "")
} }
resp := definitions.NewAlertRule(updatedAlertRule, provenance) resp := ProvisionedAlertRuleFromAlertRule(updatedAlertRule, provenance)
return response.JSON(http.StatusOK, resp) return response.JSON(http.StatusOK, resp)
} }
@ -337,7 +337,7 @@ func (srv *ProvisioningSrv) RouteGetAlertRuleGroup(c *contextmodel.ReqContext, f
} }
return ErrResp(http.StatusInternalServerError, err, "") return ErrResp(http.StatusInternalServerError, err, "")
} }
return response.JSON(http.StatusOK, definitions.NewAlertRuleGroupFromModel(g)) return response.JSON(http.StatusOK, AlertRuleGroupToApi(g))
} }
// RouteGetAlertRulesExport retrieves all alert rules in a format compatible with file provisioning. // RouteGetAlertRulesExport retrieves all alert rules in a format compatible with file provisioning.
@ -403,7 +403,7 @@ func (srv *ProvisioningSrv) RouteGetAlertRuleExport(c *contextmodel.ReqContext,
func (srv *ProvisioningSrv) RoutePutAlertRuleGroup(c *contextmodel.ReqContext, ag definitions.AlertRuleGroup, folderUID string, group string) response.Response { func (srv *ProvisioningSrv) RoutePutAlertRuleGroup(c *contextmodel.ReqContext, ag definitions.AlertRuleGroup, folderUID string, group string) response.Response {
ag.FolderUID = folderUID ag.FolderUID = folderUID
ag.Title = group ag.Title = group
groupModel, err := ag.ToModel() groupModel, err := AlertRuleGroupFromApi(ag)
if err != nil { if err != nil {
ErrResp(http.StatusBadRequest, err, "") ErrResp(http.StatusBadRequest, err, "")
} }

@ -274,7 +274,7 @@ func TestProvisioningApi(t *testing.T) {
require.Equal(t, 201, response.Status()) require.Equal(t, 201, response.Status())
created := deserializeRule(t, response.Body()) created := deserializeRule(t, response.Body())
require.Equal(t, int64(3), created.OrgID) require.Equal(t, int64(3), created.OrgID)
require.Equal(t, models.ProvenanceNone, created.Provenance) require.Equal(t, definitions.Provenance(models.ProvenanceNone), created.Provenance)
}) })
t.Run("PUT sets expected fields with no provenance", func(t *testing.T) { t.Run("PUT sets expected fields with no provenance", func(t *testing.T) {
@ -293,7 +293,7 @@ func TestProvisioningApi(t *testing.T) {
require.Equal(t, 200, response.Status()) require.Equal(t, 200, response.Status())
created := deserializeRule(t, response.Body()) created := deserializeRule(t, response.Body())
require.Equal(t, int64(3), created.OrgID) require.Equal(t, int64(3), created.OrgID)
require.Equal(t, models.ProvenanceNone, created.Provenance) require.Equal(t, definitions.Provenance(models.ProvenanceNone), created.Provenance)
}) })
}) })
@ -921,7 +921,7 @@ func (f *fakeNotificationPolicyService) GetPolicyTree(ctx context.Context, orgID
return definitions.Route{}, store.ErrNoAlertmanagerConfiguration return definitions.Route{}, store.ErrNoAlertmanagerConfiguration
} }
result := f.tree result := f.tree
result.Provenance = f.prov result.Provenance = definitions.Provenance(f.prov)
return result, nil return result, nil
} }

@ -481,7 +481,7 @@ func toGettableExtendedRuleNode(r ngmodels.AlertRule, namespaceID int64, provena
RuleGroup: r.RuleGroup, RuleGroup: r.RuleGroup,
NoDataState: apimodels.NoDataState(r.NoDataState), NoDataState: apimodels.NoDataState(r.NoDataState),
ExecErrState: apimodels.ExecutionErrorState(r.ExecErrState), ExecErrState: apimodels.ExecutionErrorState(r.ExecErrState),
Provenance: provenance, Provenance: apimodels.Provenance(provenance),
IsPaused: r.IsPaused, IsPaused: r.IsPaused,
}, },
} }

@ -367,10 +367,10 @@ func TestRouteGetNamespaceRulesConfig(t *testing.T) {
for _, group := range groups { for _, group := range groups {
for _, actualRule := range group.Rules { for _, actualRule := range group.Rules {
if actualRule.GrafanaManagedAlert.UID == expectedRules[0].UID { if actualRule.GrafanaManagedAlert.UID == expectedRules[0].UID {
require.Equal(t, models.ProvenanceAPI, actualRule.GrafanaManagedAlert.Provenance) require.Equal(t, apimodels.Provenance(models.ProvenanceAPI), actualRule.GrafanaManagedAlert.Provenance)
found = true found = true
} else { } else {
require.Equal(t, models.ProvenanceNone, actualRule.GrafanaManagedAlert.Provenance) require.Equal(t, apimodels.Provenance(models.ProvenanceNone), actualRule.GrafanaManagedAlert.Provenance)
} }
} }
} }

@ -0,0 +1,91 @@
package api
import (
"time"
"github.com/prometheus/common/model"
"github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
"github.com/grafana/grafana/pkg/services/ngalert/models"
)
// AlertRuleFromProvisionedAlertRule converts definitions.ProvisionedAlertRule to models.AlertRule
func AlertRuleFromProvisionedAlertRule(a definitions.ProvisionedAlertRule) (models.AlertRule, error) {
return models.AlertRule{
ID: a.ID,
UID: a.UID,
OrgID: a.OrgID,
NamespaceUID: a.FolderUID,
RuleGroup: a.RuleGroup,
Title: a.Title,
Condition: a.Condition,
Data: a.Data,
Updated: a.Updated,
NoDataState: a.NoDataState,
ExecErrState: a.ExecErrState,
For: time.Duration(a.For),
Annotations: a.Annotations,
Labels: a.Labels,
IsPaused: a.IsPaused,
}, nil
}
// ProvisionedAlertRuleFromAlertRule converts models.AlertRule to definitions.ProvisionedAlertRule and sets provided provenance status
func ProvisionedAlertRuleFromAlertRule(rule models.AlertRule, provenance models.Provenance) definitions.ProvisionedAlertRule {
return definitions.ProvisionedAlertRule{
ID: rule.ID,
UID: rule.UID,
OrgID: rule.OrgID,
FolderUID: rule.NamespaceUID,
RuleGroup: rule.RuleGroup,
Title: rule.Title,
For: model.Duration(rule.For),
Condition: rule.Condition,
Data: rule.Data,
Updated: rule.Updated,
NoDataState: rule.NoDataState,
ExecErrState: rule.ExecErrState,
Annotations: rule.Annotations,
Labels: rule.Labels,
Provenance: definitions.Provenance(provenance), // TODO validate enum conversion?
IsPaused: rule.IsPaused,
}
}
// ProvisionedAlertRuleFromAlertRules converts a collection of models.AlertRule to definitions.ProvisionedAlertRules with provenance status models.ProvenanceNone
func ProvisionedAlertRuleFromAlertRules(rules []*models.AlertRule) definitions.ProvisionedAlertRules {
result := make([]definitions.ProvisionedAlertRule, 0, len(rules))
for _, r := range rules {
result = append(result, ProvisionedAlertRuleFromAlertRule(*r, models.ProvenanceNone))
}
return result
}
func AlertRuleGroupFromApi(a definitions.AlertRuleGroup) (models.AlertRuleGroup, error) {
ruleGroup := models.AlertRuleGroup{
Title: a.Title,
FolderUID: a.FolderUID,
Interval: a.Interval,
}
for i := range a.Rules {
converted, err := AlertRuleFromProvisionedAlertRule(a.Rules[i])
if err != nil {
return models.AlertRuleGroup{}, err
}
ruleGroup.Rules = append(ruleGroup.Rules, converted)
}
return ruleGroup, nil
}
func AlertRuleGroupToApi(d models.AlertRuleGroup) definitions.AlertRuleGroup {
rules := make([]definitions.ProvisionedAlertRule, 0, len(d.Rules))
for i := range d.Rules {
rules = append(rules, ProvisionedAlertRuleFromAlertRule(d.Rules[i], d.Provenance))
}
return definitions.AlertRuleGroup{
Title: d.Title,
FolderUID: d.FolderUID,
Interval: d.Interval,
Rules: rules,
}
}

@ -1,34 +1,36 @@
package definitions package api
import ( import (
"testing" "testing"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
) )
func TestToModel(t *testing.T) { func TestToModel(t *testing.T) {
t.Run("if no rules are provided the rule field should be nil", func(t *testing.T) { t.Run("if no rules are provided the rule field should be nil", func(t *testing.T) {
ruleGroup := AlertRuleGroup{ ruleGroup := definitions.AlertRuleGroup{
Title: "123", Title: "123",
FolderUID: "123", FolderUID: "123",
Interval: 10, Interval: 10,
} }
tm, err := ruleGroup.ToModel() tm, err := AlertRuleGroupFromApi(ruleGroup)
require.NoError(t, err) require.NoError(t, err)
require.Nil(t, tm.Rules) require.Nil(t, tm.Rules)
}) })
t.Run("if rules are provided the rule field should be not nil", func(t *testing.T) { t.Run("if rules are provided the rule field should be not nil", func(t *testing.T) {
ruleGroup := AlertRuleGroup{ ruleGroup := definitions.AlertRuleGroup{
Title: "123", Title: "123",
FolderUID: "123", FolderUID: "123",
Interval: 10, Interval: 10,
Rules: []ProvisionedAlertRule{ Rules: []definitions.ProvisionedAlertRule{
{ {
UID: "1", UID: "1",
}, },
}, },
} }
tm, err := ruleGroup.ToModel() tm, err := AlertRuleGroupFromApi(ruleGroup)
require.NoError(t, err) require.NoError(t, err)
require.Len(t, tm.Rules, 1) require.Len(t, tm.Rules, 1)
}) })

@ -17,7 +17,6 @@ import (
"github.com/prometheus/common/model" "github.com/prometheus/common/model"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
"github.com/grafana/grafana/pkg/services/ngalert/models"
"github.com/grafana/grafana/pkg/services/secrets" "github.com/grafana/grafana/pkg/services/secrets"
"github.com/grafana/grafana/pkg/util" "github.com/grafana/grafana/pkg/util"
) )
@ -567,11 +566,13 @@ func (c *PostableUserConfig) UnmarshalYAML(value *yaml.Node) error {
return nil return nil
} }
type Provenance string
// swagger:model // swagger:model
type GettableUserConfig struct { type GettableUserConfig struct {
TemplateFiles map[string]string `yaml:"template_files" json:"template_files"` TemplateFiles map[string]string `yaml:"template_files" json:"template_files"`
TemplateFileProvenances map[string]models.Provenance `yaml:"template_file_provenances,omitempty" json:"template_file_provenances,omitempty"` TemplateFileProvenances map[string]Provenance `yaml:"template_file_provenances,omitempty" json:"template_file_provenances,omitempty"`
AlertmanagerConfig GettableApiAlertingConfig `yaml:"alertmanager_config" json:"alertmanager_config"` AlertmanagerConfig GettableApiAlertingConfig `yaml:"alertmanager_config" json:"alertmanager_config"`
// amSimple stores a map[string]interface of the decoded alertmanager config. // amSimple stores a map[string]interface of the decoded alertmanager config.
// This enables circumventing the underlying alertmanager secret type // This enables circumventing the underlying alertmanager secret type
@ -635,7 +636,7 @@ func (c *GettableUserConfig) GetGrafanaReceiverMap() map[string]*GettableGrafana
type GettableApiAlertingConfig struct { type GettableApiAlertingConfig struct {
Config `yaml:",inline"` Config `yaml:",inline"`
MuteTimeProvenances map[string]models.Provenance `yaml:"muteTimeProvenances,omitempty" json:"muteTimeProvenances,omitempty"` MuteTimeProvenances map[string]Provenance `yaml:"muteTimeProvenances,omitempty" json:"muteTimeProvenances,omitempty"`
// Override with our superset receiver type // Override with our superset receiver type
Receivers []*GettableApiReceiver `yaml:"receivers,omitempty" json:"receivers,omitempty"` Receivers []*GettableApiReceiver `yaml:"receivers,omitempty" json:"receivers,omitempty"`
} }
@ -722,7 +723,7 @@ type Route struct {
GroupInterval *model.Duration `yaml:"group_interval,omitempty" json:"group_interval,omitempty"` GroupInterval *model.Duration `yaml:"group_interval,omitempty" json:"group_interval,omitempty"`
RepeatInterval *model.Duration `yaml:"repeat_interval,omitempty" json:"repeat_interval,omitempty"` RepeatInterval *model.Duration `yaml:"repeat_interval,omitempty" json:"repeat_interval,omitempty"`
Provenance models.Provenance `yaml:"provenance,omitempty" json:"provenance,omitempty"` Provenance Provenance `yaml:"provenance,omitempty" json:"provenance,omitempty"`
} }
// UnmarshalYAML implements the yaml.Unmarshaler interface for Route. This is a copy of alertmanager's upstream except it removes validation on the label key. // UnmarshalYAML implements the yaml.Unmarshaler interface for Route. This is a copy of alertmanager's upstream except it removes validation on the label key.
@ -1006,13 +1007,13 @@ func (r RawMessage) MarshalYAML() (interface{}, error) {
} }
type GettableGrafanaReceiver struct { type GettableGrafanaReceiver struct {
UID string `json:"uid"` UID string `json:"uid"`
Name string `json:"name"` Name string `json:"name"`
Type string `json:"type"` Type string `json:"type"`
DisableResolveMessage bool `json:"disableResolveMessage"` DisableResolveMessage bool `json:"disableResolveMessage"`
Settings RawMessage `json:"settings,omitempty"` Settings RawMessage `json:"settings,omitempty"`
SecureFields map[string]bool `json:"secureFields"` SecureFields map[string]bool `json:"secureFields"`
Provenance models.Provenance `json:"provenance,omitempty"` Provenance Provenance `json:"provenance,omitempty"`
} }
type PostableGrafanaReceiver struct { type PostableGrafanaReceiver struct {

@ -393,6 +393,6 @@ type GettableGrafanaRule struct {
RuleGroup string `json:"rule_group" yaml:"rule_group"` RuleGroup string `json:"rule_group" yaml:"rule_group"`
NoDataState NoDataState `json:"no_data_state" yaml:"no_data_state"` NoDataState NoDataState `json:"no_data_state" yaml:"no_data_state"`
ExecErrState ExecutionErrorState `json:"exec_err_state" yaml:"exec_err_state"` ExecErrState ExecutionErrorState `json:"exec_err_state" yaml:"exec_err_state"`
Provenance models.Provenance `json:"provenance,omitempty" yaml:"provenance,omitempty"` Provenance Provenance `json:"provenance,omitempty" yaml:"provenance,omitempty"`
IsPaused bool `json:"is_paused" yaml:"is_paused"` IsPaused bool `json:"is_paused" yaml:"is_paused"`
} }

@ -133,60 +133,11 @@ type ProvisionedAlertRule struct {
// example: {"team": "sre-team-1"} // example: {"team": "sre-team-1"}
Labels map[string]string `json:"labels,omitempty"` Labels map[string]string `json:"labels,omitempty"`
// readonly: true // readonly: true
Provenance models.Provenance `json:"provenance,omitempty"` Provenance Provenance `json:"provenance,omitempty"`
// example: false // example: false
IsPaused bool `json:"isPaused"` IsPaused bool `json:"isPaused"`
} }
func (a *ProvisionedAlertRule) UpstreamModel() (models.AlertRule, error) {
return models.AlertRule{
ID: a.ID,
UID: a.UID,
OrgID: a.OrgID,
NamespaceUID: a.FolderUID,
RuleGroup: a.RuleGroup,
Title: a.Title,
Condition: a.Condition,
Data: a.Data,
Updated: a.Updated,
NoDataState: a.NoDataState,
ExecErrState: a.ExecErrState,
For: time.Duration(a.For),
Annotations: a.Annotations,
Labels: a.Labels,
IsPaused: a.IsPaused,
}, nil
}
func NewAlertRule(rule models.AlertRule, provenance models.Provenance) ProvisionedAlertRule {
return ProvisionedAlertRule{
ID: rule.ID,
UID: rule.UID,
OrgID: rule.OrgID,
FolderUID: rule.NamespaceUID,
RuleGroup: rule.RuleGroup,
Title: rule.Title,
For: model.Duration(rule.For),
Condition: rule.Condition,
Data: rule.Data,
Updated: rule.Updated,
NoDataState: rule.NoDataState,
ExecErrState: rule.ExecErrState,
Annotations: rule.Annotations,
Labels: rule.Labels,
Provenance: provenance,
IsPaused: rule.IsPaused,
}
}
func NewAlertRules(rules []*models.AlertRule) ProvisionedAlertRules {
result := make([]ProvisionedAlertRule, 0, len(rules))
for _, r := range rules {
result = append(result, NewAlertRule(*r, models.ProvenanceNone))
}
return result
}
// swagger:route GET /api/v1/provisioning/folder/{FolderUID}/rule-groups/{Group} provisioning stable RouteGetAlertRuleGroup // swagger:route GET /api/v1/provisioning/folder/{FolderUID}/rule-groups/{Group} provisioning stable RouteGetAlertRuleGroup
// //
// Get a rule group. // Get a rule group.
@ -268,32 +219,3 @@ type AlertRuleGroup struct {
// AlertingFileExport is the full provisioned file export. // AlertingFileExport is the full provisioned file export.
// swagger:model // swagger:model
type AlertingFileExport = file.AlertingFileExport type AlertingFileExport = file.AlertingFileExport
func (a *AlertRuleGroup) ToModel() (models.AlertRuleGroup, error) {
ruleGroup := models.AlertRuleGroup{
Title: a.Title,
FolderUID: a.FolderUID,
Interval: a.Interval,
}
for i := range a.Rules {
converted, err := a.Rules[i].UpstreamModel()
if err != nil {
return models.AlertRuleGroup{}, err
}
ruleGroup.Rules = append(ruleGroup.Rules, converted)
}
return ruleGroup, nil
}
func NewAlertRuleGroupFromModel(d models.AlertRuleGroup) AlertRuleGroup {
rules := make([]ProvisionedAlertRule, 0, len(d.Rules))
for i := range d.Rules {
rules = append(rules, NewAlertRule(d.Rules[i], d.Provenance))
}
return AlertRuleGroup{
Title: d.Title,
FolderUID: d.FolderUID,
Interval: d.Interval,
Rules: rules,
}
}

@ -2,8 +2,6 @@ package definitions
import ( import (
"github.com/prometheus/alertmanager/config" "github.com/prometheus/alertmanager/config"
"github.com/grafana/grafana/pkg/services/ngalert/models"
) )
// swagger:route GET /api/v1/provisioning/mute-timings provisioning stable RouteGetMuteTimings // swagger:route GET /api/v1/provisioning/mute-timings provisioning stable RouteGetMuteTimings
@ -71,7 +69,7 @@ type MuteTimingPayload struct {
// swagger:model // swagger:model
type MuteTimeInterval struct { type MuteTimeInterval struct {
config.MuteTimeInterval `json:",inline" yaml:",inline"` config.MuteTimeInterval `json:",inline" yaml:",inline"`
Provenance models.Provenance `json:"provenance,omitempty"` Provenance Provenance `json:"provenance,omitempty"`
} }
func (mt *MuteTimeInterval) ResourceType() string { func (mt *MuteTimeInterval) ResourceType() string {

@ -1,9 +1,5 @@
package definitions package definitions
import (
"github.com/grafana/grafana/pkg/services/ngalert/models"
)
// swagger:route GET /api/v1/provisioning/templates provisioning stable RouteGetTemplates // swagger:route GET /api/v1/provisioning/templates provisioning stable RouteGetTemplates
// //
// Get all notification templates. // Get all notification templates.
@ -47,9 +43,9 @@ type RouteGetTemplateParam struct {
// swagger:model // swagger:model
type NotificationTemplate struct { type NotificationTemplate struct {
Name string `json:"name"` Name string `json:"name"`
Template string `json:"template"` Template string `json:"template"`
Provenance models.Provenance `json:"provenance,omitempty"` Provenance Provenance `json:"provenance,omitempty"`
} }
// swagger:model // swagger:model

@ -129,7 +129,7 @@ func (moa *MultiOrgAlertmanager) mergeProvenance(ctx context.Context, config def
if err != nil { if err != nil {
return definitions.GettableUserConfig{}, err return definitions.GettableUserConfig{}, err
} }
config.AlertmanagerConfig.Route.Provenance = provenance config.AlertmanagerConfig.Route.Provenance = definitions.Provenance(provenance)
} }
cp := definitions.EmbeddedContactPoint{} cp := definitions.EmbeddedContactPoint{}
@ -140,7 +140,7 @@ func (moa *MultiOrgAlertmanager) mergeProvenance(ctx context.Context, config def
for _, receiver := range config.AlertmanagerConfig.Receivers { for _, receiver := range config.AlertmanagerConfig.Receivers {
for _, contactPoint := range receiver.GrafanaManagedReceivers { for _, contactPoint := range receiver.GrafanaManagedReceivers {
if provenance, exists := cpProvs[contactPoint.UID]; exists { if provenance, exists := cpProvs[contactPoint.UID]; exists {
contactPoint.Provenance = provenance contactPoint.Provenance = definitions.Provenance(provenance)
} }
} }
} }
@ -150,14 +150,20 @@ func (moa *MultiOrgAlertmanager) mergeProvenance(ctx context.Context, config def
if err != nil { if err != nil {
return definitions.GettableUserConfig{}, nil return definitions.GettableUserConfig{}, nil
} }
config.TemplateFileProvenances = tmplProvs config.TemplateFileProvenances = make(map[string]definitions.Provenance, len(tmplProvs))
for key, provenance := range tmplProvs {
config.TemplateFileProvenances[key] = definitions.Provenance(provenance)
}
mt := definitions.MuteTimeInterval{} mt := definitions.MuteTimeInterval{}
mtProvs, err := moa.ProvStore.GetProvenances(ctx, org, mt.ResourceType()) mtProvs, err := moa.ProvStore.GetProvenances(ctx, org, mt.ResourceType())
if err != nil { if err != nil {
return definitions.GettableUserConfig{}, nil return definitions.GettableUserConfig{}, nil
} }
config.AlertmanagerConfig.MuteTimeProvenances = mtProvs config.AlertmanagerConfig.MuteTimeProvenances = make(map[string]definitions.Provenance, len(mtProvs))
for key, provenance := range mtProvs {
config.AlertmanagerConfig.MuteTimeProvenances[key] = definitions.Provenance(provenance)
}
return config, nil return config, nil
} }

@ -82,7 +82,7 @@ func (svc *MuteTimingService) CreateMuteTiming(ctx context.Context, mt definitio
if err != nil { if err != nil {
return err return err
} }
err = svc.prov.SetProvenance(ctx, &mt, orgID, mt.Provenance) err = svc.prov.SetProvenance(ctx, &mt, orgID, models.Provenance(mt.Provenance))
if err != nil { if err != nil {
return err return err
} }
@ -137,7 +137,7 @@ func (svc *MuteTimingService) UpdateMuteTiming(ctx context.Context, mt definitio
if err != nil { if err != nil {
return err return err
} }
err = svc.prov.SetProvenance(ctx, &mt, orgID, mt.Provenance) err = svc.prov.SetProvenance(ctx, &mt, orgID, models.Provenance(mt.Provenance))
if err != nil { if err != nil {
return err return err
} }

@ -57,7 +57,7 @@ func (nps *NotificationPolicyService) GetPolicyTree(ctx context.Context, orgID i
} }
result := *cfg.AlertmanagerConfig.Route result := *cfg.AlertmanagerConfig.Route
result.Provenance = provenance result.Provenance = definitions.Provenance(provenance)
return result, nil return result, nil
} }

@ -146,7 +146,7 @@ func TestNotificationPolicyService(t *testing.T) {
tree, err := sut.GetPolicyTree(context.Background(), 1) tree, err := sut.GetPolicyTree(context.Background(), 1)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, models.ProvenanceNone, tree.Provenance) require.Equal(t, models.ProvenanceNone, models.Provenance(tree.Provenance))
}) })
t.Run("service returns upgraded provenance value", func(t *testing.T) { t.Run("service returns upgraded provenance value", func(t *testing.T) {
@ -158,7 +158,7 @@ func TestNotificationPolicyService(t *testing.T) {
updated, err := sut.GetPolicyTree(context.Background(), 1) updated, err := sut.GetPolicyTree(context.Background(), 1)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, models.ProvenanceAPI, updated.Provenance) require.Equal(t, models.ProvenanceAPI, models.Provenance(updated.Provenance))
}) })
t.Run("service respects concurrency token when updating", func(t *testing.T) { t.Run("service respects concurrency token when updating", func(t *testing.T) {

@ -70,7 +70,7 @@ func (t *TemplateService) SetTemplate(ctx context.Context, orgID int64, tmpl def
if err != nil { if err != nil {
return err return err
} }
err = t.prov.SetProvenance(ctx, &tmpl, orgID, tmpl.Provenance) err = t.prov.SetProvenance(ctx, &tmpl, orgID, models.Provenance(tmpl.Provenance))
if err != nil { if err != nil {
return err return err
} }

@ -42,7 +42,7 @@ func (c *defaultMuteTimesProvisioner) Provision(ctx context.Context,
cache[muteTiming.OrgID][interval.Name] = interval cache[muteTiming.OrgID][interval.Name] = interval
} }
} }
muteTiming.MuteTime.Provenance = models.ProvenanceFile muteTiming.MuteTime.Provenance = definitions.Provenance(models.ProvenanceFile)
if _, exists := cache[muteTiming.OrgID][muteTiming.MuteTime.Name]; exists { if _, exists := cache[muteTiming.OrgID][muteTiming.MuteTime.Name]; exists {
_, err := c.muteTimingService.UpdateMuteTiming(ctx, muteTiming.MuteTime, muteTiming.OrgID) _, err := c.muteTimingService.UpdateMuteTiming(ctx, muteTiming.MuteTime, muteTiming.OrgID)
if err != nil { if err != nil {

@ -4,6 +4,7 @@ import (
"context" "context"
"github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
"github.com/grafana/grafana/pkg/services/ngalert/models" "github.com/grafana/grafana/pkg/services/ngalert/models"
"github.com/grafana/grafana/pkg/services/ngalert/provisioning" "github.com/grafana/grafana/pkg/services/ngalert/provisioning"
) )
@ -30,7 +31,7 @@ func (c *defaultTextTemplateProvisioner) Provision(ctx context.Context,
files []*AlertingFile) error { files []*AlertingFile) error {
for _, file := range files { for _, file := range files {
for _, template := range file.Templates { for _, template := range file.Templates {
template.Data.Provenance = models.ProvenanceFile template.Data.Provenance = definitions.Provenance(models.ProvenanceFile)
_, err := c.templateService.SetTemplate(ctx, template.OrgID, template.Data) _, err := c.templateService.SetTemplate(ctx, template.OrgID, template.Data)
if err != nil { if err != nil {
return err return err

Loading…
Cancel
Save