Skip to content

Commit eab6365

Browse files
authored
Alerting: Add file provisioning for notification policies (grafana#52877)
1 parent d9cace4 commit eab6365

File tree

9 files changed

+172
-16
lines changed

9 files changed

+172
-16
lines changed

pkg/services/provisioning/alerting/config_reader_test.go

+19
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ const (
2323
testFileMissingUID = "./testdata/contact_points/missing-uid"
2424
testFileWhitespaceUID = "./testdata/contact_points/whitespace-uid"
2525
testFileMultipleCps = "./testdata/contact_points/multiple-contact-points"
26+
testFileCorrectProperties_np = "./testdata/notificiation_policies/correct-properties"
27+
testFileCorrectPropertiesWithOrg_np = "./testdata/notificiation_policies/correct-properties-with-org"
28+
testFileMultipleNps = "./testdata/notificiation_policies/multiple-policies"
2629
)
2730

2831
func TestConfigReader(t *testing.T) {
@@ -100,4 +103,20 @@ func TestConfigReader(t *testing.T) {
100103
require.NoError(t, err)
101104
require.Len(t, file[0].ContactPoints, 2)
102105
})
106+
t.Run("a notification policy file with correct properties and specific org should not error", func(t *testing.T) {
107+
_, err := configReader.readConfig(ctx, testFileCorrectProperties_np)
108+
require.NoError(t, err)
109+
})
110+
t.Run("a notification policy file with correct properties and specific org should not error", func(t *testing.T) {
111+
file, err := configReader.readConfig(ctx, testFileCorrectPropertiesWithOrg_np)
112+
require.NoError(t, err)
113+
t.Run("when an organization is set it should not overwrite it with the default of 1", func(t *testing.T) {
114+
require.Equal(t, int64(1337), file[0].Policies[0].OrgID)
115+
})
116+
})
117+
t.Run("the config reader should be able to read a file with multiple notification policies", func(t *testing.T) {
118+
file, err := configReader.readConfig(ctx, testFileMultipleNps)
119+
require.NoError(t, err)
120+
require.Len(t, file[0].Policies, 2)
121+
})
103122
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package alerting
2+
3+
import (
4+
"context"
5+
"fmt"
6+
7+
"github.com/grafana/grafana/pkg/infra/log"
8+
"github.com/grafana/grafana/pkg/services/ngalert/models"
9+
"github.com/grafana/grafana/pkg/services/ngalert/provisioning"
10+
)
11+
12+
type NotificationPolicyProvisioner interface {
13+
Provision(ctx context.Context, files []*AlertingFile) error
14+
Unprovision(ctx context.Context, files []*AlertingFile) error
15+
}
16+
17+
type defaultNotificationPolicyProvisioner struct {
18+
logger log.Logger
19+
notificationPolicyService provisioning.NotificationPolicyService
20+
}
21+
22+
func NewNotificationPolicyProvisoner(logger log.Logger,
23+
notificationPolicyService provisioning.NotificationPolicyService) NotificationPolicyProvisioner {
24+
return &defaultNotificationPolicyProvisioner{
25+
logger: logger,
26+
notificationPolicyService: notificationPolicyService,
27+
}
28+
}
29+
30+
func (c *defaultNotificationPolicyProvisioner) Provision(ctx context.Context,
31+
files []*AlertingFile) error {
32+
for _, file := range files {
33+
for _, np := range file.Policies {
34+
err := c.notificationPolicyService.UpdatePolicyTree(ctx, np.OrgID,
35+
np.Policy, models.ProvenanceFile)
36+
if err != nil {
37+
return fmt.Errorf("%s: %w", file.Filename, err)
38+
}
39+
}
40+
}
41+
return nil
42+
}
43+
44+
func (c *defaultNotificationPolicyProvisioner) Unprovision(ctx context.Context,
45+
files []*AlertingFile) error {
46+
for _, file := range files {
47+
for _, orgID := range file.ResetPolicies {
48+
_, err := c.notificationPolicyService.ResetPolicyTree(ctx, int64(orgID))
49+
if err != nil {
50+
return fmt.Errorf("%s: %w", file.Filename, err)
51+
}
52+
}
53+
}
54+
return nil
55+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package alerting
2+
3+
import (
4+
"github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
5+
"github.com/grafana/grafana/pkg/services/provisioning/values"
6+
)
7+
8+
type NotificiationPolicyV1 struct {
9+
OrgID values.Int64Value `json:"orgId" yaml:"orgId"`
10+
Policy definitions.Route `json:",inline" yaml:",inline"`
11+
}
12+
13+
func (v1 *NotificiationPolicyV1) mapToModel() NotificiationPolicy {
14+
orgID := v1.OrgID.Value()
15+
if orgID < 1 {
16+
orgID = 1
17+
}
18+
// we don't need any further validation here as it's done by
19+
// the notification policy service
20+
return NotificiationPolicy{
21+
OrgID: orgID,
22+
Policy: v1.Policy,
23+
}
24+
}
25+
26+
type NotificiationPolicy struct {
27+
OrgID int64
28+
Policy definitions.Route
29+
}

pkg/services/provisioning/alerting/provisioner.go

+15-7
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,12 @@ import (
99
)
1010

1111
type ProvisionerConfig struct {
12-
Path string
13-
DashboardService dashboards.DashboardService
14-
DashboardProvService dashboards.DashboardProvisioningService
15-
RuleService provisioning.AlertRuleService
16-
ContactPointService provisioning.ContactPointService
12+
Path string
13+
DashboardService dashboards.DashboardService
14+
DashboardProvService dashboards.DashboardProvisioningService
15+
RuleService provisioning.AlertRuleService
16+
ContactPointService provisioning.ContactPointService
17+
NotificiationPolicyService provisioning.NotificationPolicyService
1718
}
1819

1920
func Provision(ctx context.Context, cfg ProvisionerConfig) error {
@@ -39,8 +40,15 @@ func Provision(ctx context.Context, cfg ProvisionerConfig) error {
3940
if err != nil {
4041
return err
4142
}
42-
// TODO: provision notificiation policy in between so that when applying it
43-
// new objects already exists and old ones are still there
43+
npProvisioner := NewNotificationPolicyProvisoner(logger, cfg.NotificiationPolicyService)
44+
err = npProvisioner.Provision(ctx, files)
45+
if err != nil {
46+
return err
47+
}
48+
err = npProvisioner.Unprovision(ctx, files)
49+
if err != nil {
50+
return err
51+
}
4452
err = cpProvisioner.Unprovision(ctx, files)
4553
if err != nil {
4654
return err
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
apiVersion: 1
2+
policies:
3+
- orgId: 1337
4+
receiver: grafana-default-email
5+
group_by:
6+
- grafana_folder
7+
- alertname
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
apiVersion: 1
2+
policies:
3+
- receiver: grafana-default-email
4+
group_by:
5+
- grafana_folder
6+
- alertname
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
apiVersion: 1
2+
policies:
3+
- receiver: grafana-default-email
4+
group_by:
5+
- grafana_folder
6+
- alertname
7+
- orgId: 1337
8+
receiver: grafana-default-email
9+
group_by:
10+
- grafana_folder
11+
- alertname

pkg/services/provisioning/alerting/types.go

+22-4
Original file line numberDiff line numberDiff line change
@@ -10,25 +10,33 @@ type configVersion struct {
1010
APIVersion values.Int64Value `json:"apiVersion" yaml:"apiVersion"`
1111
}
1212

13+
type OrgID int64
14+
1315
type AlertingFile struct {
1416
configVersion
17+
Filename string
1518
Groups []AlertRuleGroup
1619
DeleteRules []RuleDelete
1720
ContactPoints []ContactPoint
1821
DeleteContactPoints []DeleteContactPoint
22+
Policies []NotificiationPolicy
23+
ResetPolicies []OrgID
1924
}
2025

2126
type AlertingFileV1 struct {
2227
configVersion
2328
Filename string
24-
Groups []AlertRuleGroupV1 `json:"groups" yaml:"groups"`
25-
DeleteRules []RuleDeleteV1 `json:"deleteRules" yaml:"deleteRules"`
26-
ContactPoints []ContactPointV1 `json:"contactPoints" yaml:"contactPoints"`
27-
DeleteContactPoints []DeleteContactPointV1 `json:"deleteContactPoints" yaml:"deleteContactPoints"`
29+
Groups []AlertRuleGroupV1 `json:"groups" yaml:"groups"`
30+
DeleteRules []RuleDeleteV1 `json:"deleteRules" yaml:"deleteRules"`
31+
ContactPoints []ContactPointV1 `json:"contactPoints" yaml:"contactPoints"`
32+
DeleteContactPoints []DeleteContactPointV1 `json:"deleteContactPoints" yaml:"deleteContactPoints"`
33+
Policies []NotificiationPolicyV1 `json:"policies" yaml:"policies"`
34+
ResetPolicies []values.Int64Value `json:"resetPolicies" yaml:"resetPolicies"`
2835
}
2936

3037
func (fileV1 *AlertingFileV1) MapToModel() (AlertingFile, error) {
3138
alertingFile := AlertingFile{}
39+
alertingFile.Filename = fileV1.Filename
3240
err := fileV1.mapRules(&alertingFile)
3341
if err != nil {
3442
return AlertingFile{}, fmt.Errorf("failure parsing rules: %w", err)
@@ -37,9 +45,19 @@ func (fileV1 *AlertingFileV1) MapToModel() (AlertingFile, error) {
3745
if err != nil {
3846
return AlertingFile{}, fmt.Errorf("failure parsing contact points: %w", err)
3947
}
48+
fileV1.mapPolicies(&alertingFile)
4049
return alertingFile, nil
4150
}
4251

52+
func (fileV1 *AlertingFileV1) mapPolicies(alertingFile *AlertingFile) {
53+
for _, npV1 := range fileV1.Policies {
54+
alertingFile.Policies = append(alertingFile.Policies, npV1.mapToModel())
55+
}
56+
for _, orgIDV1 := range fileV1.ResetPolicies {
57+
alertingFile.ResetPolicies = append(alertingFile.ResetPolicies, OrgID(orgIDV1.Value()))
58+
}
59+
}
60+
4361
func (fileV1 *AlertingFileV1) mapContactPoint(alertingFile *AlertingFile) error {
4462
for _, dcp := range fileV1.DeleteContactPoints {
4563
alertingFile.DeleteContactPoints = append(alertingFile.DeleteContactPoints, dcp.MapToModel())

pkg/services/provisioning/provisioning.go

+8-5
Original file line numberDiff line numberDiff line change
@@ -272,12 +272,15 @@ func (ps *ProvisioningServiceImpl) ProvisionAlerting(ctx context.Context) error
272272
ps.log)
273273
contactPointService := provisioning.NewContactPointService(&st, ps.secretService,
274274
st, ps.SQLStore, ps.log)
275+
notificationPolicyService := provisioning.NewNotificationPolicyService(&st,
276+
st, ps.SQLStore, ps.Cfg.UnifiedAlerting, ps.log)
275277
cfg := prov_alerting.ProvisionerConfig{
276-
Path: alertingPath,
277-
RuleService: *ruleService,
278-
DashboardService: ps.dashboardService,
279-
DashboardProvService: ps.dashboardProvisioningService,
280-
ContactPointService: *contactPointService,
278+
Path: alertingPath,
279+
RuleService: *ruleService,
280+
DashboardService: ps.dashboardService,
281+
DashboardProvService: ps.dashboardProvisioningService,
282+
ContactPointService: *contactPointService,
283+
NotificiationPolicyService: *notificationPolicyService,
281284
}
282285
return ps.provisionAlerting(ctx, cfg)
283286
}

0 commit comments

Comments
 (0)