mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Alerting: Add file provisioning for notification policies (#52877)
This commit is contained in:
parent
d9cace4dca
commit
eab6365cf7
@ -23,6 +23,9 @@ const (
|
|||||||
testFileMissingUID = "./testdata/contact_points/missing-uid"
|
testFileMissingUID = "./testdata/contact_points/missing-uid"
|
||||||
testFileWhitespaceUID = "./testdata/contact_points/whitespace-uid"
|
testFileWhitespaceUID = "./testdata/contact_points/whitespace-uid"
|
||||||
testFileMultipleCps = "./testdata/contact_points/multiple-contact-points"
|
testFileMultipleCps = "./testdata/contact_points/multiple-contact-points"
|
||||||
|
testFileCorrectProperties_np = "./testdata/notificiation_policies/correct-properties"
|
||||||
|
testFileCorrectPropertiesWithOrg_np = "./testdata/notificiation_policies/correct-properties-with-org"
|
||||||
|
testFileMultipleNps = "./testdata/notificiation_policies/multiple-policies"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestConfigReader(t *testing.T) {
|
func TestConfigReader(t *testing.T) {
|
||||||
@ -100,4 +103,20 @@ func TestConfigReader(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Len(t, file[0].ContactPoints, 2)
|
require.Len(t, file[0].ContactPoints, 2)
|
||||||
})
|
})
|
||||||
|
t.Run("a notification policy file with correct properties and specific org should not error", func(t *testing.T) {
|
||||||
|
_, err := configReader.readConfig(ctx, testFileCorrectProperties_np)
|
||||||
|
require.NoError(t, err)
|
||||||
|
})
|
||||||
|
t.Run("a notification policy file with correct properties and specific org should not error", func(t *testing.T) {
|
||||||
|
file, err := configReader.readConfig(ctx, testFileCorrectPropertiesWithOrg_np)
|
||||||
|
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].Policies[0].OrgID)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
t.Run("the config reader should be able to read a file with multiple notification policies", func(t *testing.T) {
|
||||||
|
file, err := configReader.readConfig(ctx, testFileMultipleNps)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Len(t, file[0].Policies, 2)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,55 @@
|
|||||||
|
package alerting
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
|
"github.com/grafana/grafana/pkg/services/ngalert/models"
|
||||||
|
"github.com/grafana/grafana/pkg/services/ngalert/provisioning"
|
||||||
|
)
|
||||||
|
|
||||||
|
type NotificationPolicyProvisioner interface {
|
||||||
|
Provision(ctx context.Context, files []*AlertingFile) error
|
||||||
|
Unprovision(ctx context.Context, files []*AlertingFile) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type defaultNotificationPolicyProvisioner struct {
|
||||||
|
logger log.Logger
|
||||||
|
notificationPolicyService provisioning.NotificationPolicyService
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewNotificationPolicyProvisoner(logger log.Logger,
|
||||||
|
notificationPolicyService provisioning.NotificationPolicyService) NotificationPolicyProvisioner {
|
||||||
|
return &defaultNotificationPolicyProvisioner{
|
||||||
|
logger: logger,
|
||||||
|
notificationPolicyService: notificationPolicyService,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *defaultNotificationPolicyProvisioner) Provision(ctx context.Context,
|
||||||
|
files []*AlertingFile) error {
|
||||||
|
for _, file := range files {
|
||||||
|
for _, np := range file.Policies {
|
||||||
|
err := c.notificationPolicyService.UpdatePolicyTree(ctx, np.OrgID,
|
||||||
|
np.Policy, models.ProvenanceFile)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("%s: %w", file.Filename, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *defaultNotificationPolicyProvisioner) Unprovision(ctx context.Context,
|
||||||
|
files []*AlertingFile) error {
|
||||||
|
for _, file := range files {
|
||||||
|
for _, orgID := range file.ResetPolicies {
|
||||||
|
_, err := c.notificationPolicyService.ResetPolicyTree(ctx, int64(orgID))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("%s: %w", file.Filename, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
package alerting
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
|
||||||
|
"github.com/grafana/grafana/pkg/services/provisioning/values"
|
||||||
|
)
|
||||||
|
|
||||||
|
type NotificiationPolicyV1 struct {
|
||||||
|
OrgID values.Int64Value `json:"orgId" yaml:"orgId"`
|
||||||
|
Policy definitions.Route `json:",inline" yaml:",inline"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v1 *NotificiationPolicyV1) mapToModel() NotificiationPolicy {
|
||||||
|
orgID := v1.OrgID.Value()
|
||||||
|
if orgID < 1 {
|
||||||
|
orgID = 1
|
||||||
|
}
|
||||||
|
// we don't need any further validation here as it's done by
|
||||||
|
// the notification policy service
|
||||||
|
return NotificiationPolicy{
|
||||||
|
OrgID: orgID,
|
||||||
|
Policy: v1.Policy,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type NotificiationPolicy struct {
|
||||||
|
OrgID int64
|
||||||
|
Policy definitions.Route
|
||||||
|
}
|
@ -14,6 +14,7 @@ type ProvisionerConfig struct {
|
|||||||
DashboardProvService dashboards.DashboardProvisioningService
|
DashboardProvService dashboards.DashboardProvisioningService
|
||||||
RuleService provisioning.AlertRuleService
|
RuleService provisioning.AlertRuleService
|
||||||
ContactPointService provisioning.ContactPointService
|
ContactPointService provisioning.ContactPointService
|
||||||
|
NotificiationPolicyService provisioning.NotificationPolicyService
|
||||||
}
|
}
|
||||||
|
|
||||||
func Provision(ctx context.Context, cfg ProvisionerConfig) error {
|
func Provision(ctx context.Context, cfg ProvisionerConfig) error {
|
||||||
@ -39,8 +40,15 @@ func Provision(ctx context.Context, cfg ProvisionerConfig) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// TODO: provision notificiation policy in between so that when applying it
|
npProvisioner := NewNotificationPolicyProvisoner(logger, cfg.NotificiationPolicyService)
|
||||||
// new objects already exists and old ones are still there
|
err = npProvisioner.Provision(ctx, files)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = npProvisioner.Unprovision(ctx, files)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
err = cpProvisioner.Unprovision(ctx, files)
|
err = cpProvisioner.Unprovision(ctx, files)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -0,0 +1,7 @@
|
|||||||
|
apiVersion: 1
|
||||||
|
policies:
|
||||||
|
- orgId: 1337
|
||||||
|
receiver: grafana-default-email
|
||||||
|
group_by:
|
||||||
|
- grafana_folder
|
||||||
|
- alertname
|
@ -0,0 +1,6 @@
|
|||||||
|
apiVersion: 1
|
||||||
|
policies:
|
||||||
|
- receiver: grafana-default-email
|
||||||
|
group_by:
|
||||||
|
- grafana_folder
|
||||||
|
- alertname
|
@ -0,0 +1,11 @@
|
|||||||
|
apiVersion: 1
|
||||||
|
policies:
|
||||||
|
- receiver: grafana-default-email
|
||||||
|
group_by:
|
||||||
|
- grafana_folder
|
||||||
|
- alertname
|
||||||
|
- orgId: 1337
|
||||||
|
receiver: grafana-default-email
|
||||||
|
group_by:
|
||||||
|
- grafana_folder
|
||||||
|
- alertname
|
@ -10,12 +10,17 @@ type configVersion struct {
|
|||||||
APIVersion values.Int64Value `json:"apiVersion" yaml:"apiVersion"`
|
APIVersion values.Int64Value `json:"apiVersion" yaml:"apiVersion"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type OrgID int64
|
||||||
|
|
||||||
type AlertingFile struct {
|
type AlertingFile struct {
|
||||||
configVersion
|
configVersion
|
||||||
|
Filename string
|
||||||
Groups []AlertRuleGroup
|
Groups []AlertRuleGroup
|
||||||
DeleteRules []RuleDelete
|
DeleteRules []RuleDelete
|
||||||
ContactPoints []ContactPoint
|
ContactPoints []ContactPoint
|
||||||
DeleteContactPoints []DeleteContactPoint
|
DeleteContactPoints []DeleteContactPoint
|
||||||
|
Policies []NotificiationPolicy
|
||||||
|
ResetPolicies []OrgID
|
||||||
}
|
}
|
||||||
|
|
||||||
type AlertingFileV1 struct {
|
type AlertingFileV1 struct {
|
||||||
@ -25,10 +30,13 @@ type AlertingFileV1 struct {
|
|||||||
DeleteRules []RuleDeleteV1 `json:"deleteRules" yaml:"deleteRules"`
|
DeleteRules []RuleDeleteV1 `json:"deleteRules" yaml:"deleteRules"`
|
||||||
ContactPoints []ContactPointV1 `json:"contactPoints" yaml:"contactPoints"`
|
ContactPoints []ContactPointV1 `json:"contactPoints" yaml:"contactPoints"`
|
||||||
DeleteContactPoints []DeleteContactPointV1 `json:"deleteContactPoints" yaml:"deleteContactPoints"`
|
DeleteContactPoints []DeleteContactPointV1 `json:"deleteContactPoints" yaml:"deleteContactPoints"`
|
||||||
|
Policies []NotificiationPolicyV1 `json:"policies" yaml:"policies"`
|
||||||
|
ResetPolicies []values.Int64Value `json:"resetPolicies" yaml:"resetPolicies"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fileV1 *AlertingFileV1) MapToModel() (AlertingFile, error) {
|
func (fileV1 *AlertingFileV1) MapToModel() (AlertingFile, error) {
|
||||||
alertingFile := AlertingFile{}
|
alertingFile := AlertingFile{}
|
||||||
|
alertingFile.Filename = fileV1.Filename
|
||||||
err := fileV1.mapRules(&alertingFile)
|
err := fileV1.mapRules(&alertingFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return AlertingFile{}, fmt.Errorf("failure parsing rules: %w", err)
|
return AlertingFile{}, fmt.Errorf("failure parsing rules: %w", err)
|
||||||
@ -37,9 +45,19 @@ func (fileV1 *AlertingFileV1) MapToModel() (AlertingFile, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return AlertingFile{}, fmt.Errorf("failure parsing contact points: %w", err)
|
return AlertingFile{}, fmt.Errorf("failure parsing contact points: %w", err)
|
||||||
}
|
}
|
||||||
|
fileV1.mapPolicies(&alertingFile)
|
||||||
return alertingFile, nil
|
return alertingFile, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (fileV1 *AlertingFileV1) mapPolicies(alertingFile *AlertingFile) {
|
||||||
|
for _, npV1 := range fileV1.Policies {
|
||||||
|
alertingFile.Policies = append(alertingFile.Policies, npV1.mapToModel())
|
||||||
|
}
|
||||||
|
for _, orgIDV1 := range fileV1.ResetPolicies {
|
||||||
|
alertingFile.ResetPolicies = append(alertingFile.ResetPolicies, OrgID(orgIDV1.Value()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (fileV1 *AlertingFileV1) mapContactPoint(alertingFile *AlertingFile) error {
|
func (fileV1 *AlertingFileV1) mapContactPoint(alertingFile *AlertingFile) error {
|
||||||
for _, dcp := range fileV1.DeleteContactPoints {
|
for _, dcp := range fileV1.DeleteContactPoints {
|
||||||
alertingFile.DeleteContactPoints = append(alertingFile.DeleteContactPoints, dcp.MapToModel())
|
alertingFile.DeleteContactPoints = append(alertingFile.DeleteContactPoints, dcp.MapToModel())
|
||||||
|
@ -272,12 +272,15 @@ func (ps *ProvisioningServiceImpl) ProvisionAlerting(ctx context.Context) error
|
|||||||
ps.log)
|
ps.log)
|
||||||
contactPointService := provisioning.NewContactPointService(&st, ps.secretService,
|
contactPointService := provisioning.NewContactPointService(&st, ps.secretService,
|
||||||
st, ps.SQLStore, ps.log)
|
st, ps.SQLStore, ps.log)
|
||||||
|
notificationPolicyService := provisioning.NewNotificationPolicyService(&st,
|
||||||
|
st, ps.SQLStore, ps.Cfg.UnifiedAlerting, ps.log)
|
||||||
cfg := prov_alerting.ProvisionerConfig{
|
cfg := prov_alerting.ProvisionerConfig{
|
||||||
Path: alertingPath,
|
Path: alertingPath,
|
||||||
RuleService: *ruleService,
|
RuleService: *ruleService,
|
||||||
DashboardService: ps.dashboardService,
|
DashboardService: ps.dashboardService,
|
||||||
DashboardProvService: ps.dashboardProvisioningService,
|
DashboardProvService: ps.dashboardProvisioningService,
|
||||||
ContactPointService: *contactPointService,
|
ContactPointService: *contactPointService,
|
||||||
|
NotificiationPolicyService: *notificationPolicyService,
|
||||||
}
|
}
|
||||||
return ps.provisionAlerting(ctx, cfg)
|
return ps.provisionAlerting(ctx, cfg)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user