Alerting: Add file provisioning for text templates (#52952)

This commit is contained in:
Jean-Philippe Quéméner 2022-08-01 20:50:03 +02:00 committed by GitHub
parent 3d39594d75
commit 8fc8d00167
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 177 additions and 0 deletions

View File

@ -29,6 +29,9 @@ const (
testFileCorrectProperties_mt = "./testdata/mute_times/correct-properties"
testFileCorrectPropertiesWithOrg_mt = "./testdata/mute_times/correct-properties-with-org"
testFileMultipleMts = "./testdata/mute_times/multiple-mute-times"
testFileCorrectProperties_t = "./testdata/templates/correct-properties"
testFileCorrectPropertiesWithOrg_t = "./testdata/templates/correct-properties-with-org"
testFileMultipleTs = "./testdata/templates/multiple-templates"
)
func TestConfigReader(t *testing.T) {
@ -138,4 +141,20 @@ func TestConfigReader(t *testing.T) {
require.NoError(t, err)
require.Len(t, file[0].MuteTimes, 2)
})
t.Run("a template file with correct properties and specific org should not error", func(t *testing.T) {
_, err := configReader.readConfig(ctx, testFileCorrectProperties_t)
require.NoError(t, err)
})
t.Run("a template file with correct properties and specific org should not error", func(t *testing.T) {
file, err := configReader.readConfig(ctx, testFileCorrectPropertiesWithOrg_t)
require.NoError(t, err)
t.Run("when an organization is set it should not overwrite it with the default of 1", func(t *testing.T) {
require.Equal(t, int64(1337), file[0].Templates[0].OrgID)
})
})
t.Run("the config reader should be able to read a file with multiple mute times", func(t *testing.T) {
file, err := configReader.readConfig(ctx, testFileMultipleTs)
require.NoError(t, err)
require.Len(t, file[0].Templates, 2)
})
}

View File

@ -17,6 +17,7 @@ type ProvisionerConfig struct {
ContactPointService provisioning.ContactPointService
NotificiationPolicyService provisioning.NotificationPolicyService
MuteTimingService provisioning.MuteTimingService
TemplateService provisioning.TemplateService
}
func Provision(ctx context.Context, cfg ProvisionerConfig) error {
@ -47,6 +48,11 @@ func Provision(ctx context.Context, cfg ProvisionerConfig) error {
if err != nil {
return fmt.Errorf("mute times: %w", err)
}
ttProvsioner := NewTextTemplateProvisioner(logger, cfg.TemplateService)
err = ttProvsioner.Provision(ctx, files)
if err != nil {
return fmt.Errorf("text templates: %w", err)
}
npProvisioner := NewNotificationPolicyProvisoner(logger, cfg.NotificiationPolicyService)
err = npProvisioner.Provision(ctx, files)
if err != nil {
@ -64,6 +70,10 @@ func Provision(ctx context.Context, cfg ProvisionerConfig) error {
if err != nil {
return fmt.Errorf("mute times: %w", err)
}
err = ttProvsioner.Unprovision(ctx, files)
if err != nil {
return fmt.Errorf("text templates: %w", err)
}
logger.Info("finished to provision alerting")
return nil
}

View File

@ -0,0 +1,5 @@
apiVersion: 1
templates:
- orgId: 1337
name: my_first_template
template: "test"

View File

@ -0,0 +1,4 @@
apiVersion: 1
templates:
- name: my_first_template
template: "test"

View File

@ -0,0 +1,7 @@
apiVersion: 1
templates:
- orgId: 1337
name: my_first_template
template: "test"
- name: my_second_template
template: "test"

View File

@ -0,0 +1,54 @@
package alerting
import (
"context"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/services/ngalert/models"
"github.com/grafana/grafana/pkg/services/ngalert/provisioning"
)
type TextTemplateProvisioner interface {
Provision(ctx context.Context, files []*AlertingFile) error
Unprovision(ctx context.Context, files []*AlertingFile) error
}
type defaultTextTemplateProvisioner struct {
logger log.Logger
templateService provisioning.TemplateService
}
func NewTextTemplateProvisioner(logger log.Logger,
templateService provisioning.TemplateService) TextTemplateProvisioner {
return &defaultTextTemplateProvisioner{
logger: logger,
templateService: templateService,
}
}
func (c *defaultTextTemplateProvisioner) Provision(ctx context.Context,
files []*AlertingFile) error {
for _, file := range files {
for _, template := range file.Templates {
template.Data.Provenance = models.ProvenanceFile
_, err := c.templateService.SetTemplate(ctx, template.OrgID, template.Data)
if err != nil {
return err
}
}
}
return nil
}
func (c *defaultTextTemplateProvisioner) Unprovision(ctx context.Context,
files []*AlertingFile) error {
for _, file := range files {
for _, deleteTemplate := range file.DeleteTemplates {
err := c.templateService.DeleteTemplate(ctx, deleteTemplate.OrgID, deleteTemplate.Name)
if err != nil {
return err
}
}
}
return nil
}

View File

@ -0,0 +1,55 @@
package alerting
import (
"errors"
"strings"
"github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
"github.com/grafana/grafana/pkg/services/provisioning/values"
)
type TemplateV1 struct {
OrgID values.Int64Value `json:"orgId" yaml:"orgId"`
Template definitions.MessageTemplate `json:",inline" yaml:",inline"`
}
func (v1 *TemplateV1) mapToModel() Template {
orgID := v1.OrgID.Value()
if orgID < 1 {
orgID = 1
}
return Template{
Data: v1.Template,
OrgID: orgID,
}
}
type Template struct {
OrgID int64
Data definitions.MessageTemplate
}
type DeleteTemplateV1 struct {
OrgID values.Int64Value `json:"orgId" yaml:"orgId"`
Name values.StringValue `json:"name" yaml:"name"`
}
func (v1 *DeleteTemplateV1) mapToModel() (DeleteTemplate, error) {
name := strings.TrimSpace(v1.Name.Value())
if name == "" {
return DeleteTemplate{}, errors.New("delete template missing name")
}
orgID := v1.OrgID.Value()
if orgID < 1 {
orgID = 1
}
return DeleteTemplate{
Name: name,
OrgID: orgID,
}, nil
}
type DeleteTemplate struct {
OrgID int64
Name string
}

View File

@ -23,6 +23,8 @@ type AlertingFile struct {
ResetPolicies []OrgID
MuteTimes []MuteTime
DeleteMuteTimes []DeleteMuteTime
Templates []Template
DeleteTemplates []DeleteTemplate
}
type AlertingFileV1 struct {
@ -36,6 +38,8 @@ type AlertingFileV1 struct {
ResetPolicies []values.Int64Value `json:"resetPolicies" yaml:"resetPolicies"`
MuteTimes []MuteTimeV1 `json:"muteTimes" yaml:"muteTimes"`
DeleteMuteTimes []DeleteMuteTimeV1 `json:"deleteMuteTimes" yaml:"deleteMuteTimes"`
Templates []TemplateV1 `json:"templates" yaml:"templates"`
DeleteTemplates []DeleteTemplateV1 `json:"deleteTemplates" yaml:"deleteTemplates"`
}
func (fileV1 *AlertingFileV1) MapToModel() (AlertingFile, error) {
@ -51,9 +55,26 @@ func (fileV1 *AlertingFileV1) MapToModel() (AlertingFile, error) {
if err := fileV1.mapMuteTimes(&alertingFile); err != nil {
return AlertingFile{}, fmt.Errorf("failure parsing mute times: %w", err)
}
if err := fileV1.mapTemplates(&alertingFile); err != nil {
return AlertingFile{}, fmt.Errorf("failure parsing templates: %w", err)
}
return alertingFile, nil
}
func (fileV1 *AlertingFileV1) mapTemplates(alertingFile *AlertingFile) error {
for _, ttV1 := range fileV1.Templates {
alertingFile.Templates = append(alertingFile.Templates, ttV1.mapToModel())
}
for _, deleteV1 := range fileV1.DeleteTemplates {
delReq, err := deleteV1.mapToModel()
if err != nil {
return err
}
alertingFile.DeleteTemplates = append(alertingFile.DeleteTemplates, delReq)
}
return nil
}
func (fileV1 *AlertingFileV1) mapMuteTimes(alertingFile *AlertingFile) error {
for _, mtV1 := range fileV1.MuteTimes {
alertingFile.MuteTimes = append(alertingFile.MuteTimes, mtV1.mapToModel())

View File

@ -275,6 +275,7 @@ func (ps *ProvisioningServiceImpl) ProvisionAlerting(ctx context.Context) error
notificationPolicyService := provisioning.NewNotificationPolicyService(&st,
st, ps.SQLStore, ps.Cfg.UnifiedAlerting, ps.log)
mutetimingsService := provisioning.NewMuteTimingService(&st, st, &st, ps.log)
templateService := provisioning.NewTemplateService(&st, st, &st, ps.log)
cfg := prov_alerting.ProvisionerConfig{
Path: alertingPath,
RuleService: *ruleService,
@ -283,6 +284,7 @@ func (ps *ProvisioningServiceImpl) ProvisionAlerting(ctx context.Context) error
ContactPointService: *contactPointService,
NotificiationPolicyService: *notificationPolicyService,
MuteTimingService: *mutetimingsService,
TemplateService: *templateService,
}
return ps.provisionAlerting(ctx, cfg)
}