mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Alerting: Add file provisioning for mute timings (#52936)
This commit is contained in:
parent
7b249d19b2
commit
3d39594d75
@ -26,6 +26,9 @@ const (
|
|||||||
testFileCorrectProperties_np = "./testdata/notificiation_policies/correct-properties"
|
testFileCorrectProperties_np = "./testdata/notificiation_policies/correct-properties"
|
||||||
testFileCorrectPropertiesWithOrg_np = "./testdata/notificiation_policies/correct-properties-with-org"
|
testFileCorrectPropertiesWithOrg_np = "./testdata/notificiation_policies/correct-properties-with-org"
|
||||||
testFileMultipleNps = "./testdata/notificiation_policies/multiple-policies"
|
testFileMultipleNps = "./testdata/notificiation_policies/multiple-policies"
|
||||||
|
testFileCorrectProperties_mt = "./testdata/mute_times/correct-properties"
|
||||||
|
testFileCorrectPropertiesWithOrg_mt = "./testdata/mute_times/correct-properties-with-org"
|
||||||
|
testFileMultipleMts = "./testdata/mute_times/multiple-mute-times"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestConfigReader(t *testing.T) {
|
func TestConfigReader(t *testing.T) {
|
||||||
@ -119,4 +122,20 @@ func TestConfigReader(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Len(t, file[0].Policies, 2)
|
require.Len(t, file[0].Policies, 2)
|
||||||
})
|
})
|
||||||
|
t.Run("a mute times file with correct properties and specific org should not error", func(t *testing.T) {
|
||||||
|
_, err := configReader.readConfig(ctx, testFileCorrectProperties_mt)
|
||||||
|
require.NoError(t, err)
|
||||||
|
})
|
||||||
|
t.Run("a mute times file with correct properties and specific org should not error", func(t *testing.T) {
|
||||||
|
file, err := configReader.readConfig(ctx, testFileCorrectPropertiesWithOrg_mt)
|
||||||
|
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].MuteTimes[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, testFileMultipleMts)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Len(t, file[0].MuteTimes, 2)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
73
pkg/services/provisioning/alerting/mute_times_provisioner.go
Normal file
73
pkg/services/provisioning/alerting/mute_times_provisioner.go
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
package alerting
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"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/provisioning"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MuteTimesProvisioner interface {
|
||||||
|
Provision(ctx context.Context, files []*AlertingFile) error
|
||||||
|
Unprovision(ctx context.Context, files []*AlertingFile) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type defaultMuteTimesProvisioner struct {
|
||||||
|
logger log.Logger
|
||||||
|
muteTimingService provisioning.MuteTimingService
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMuteTimesProvisioner(logger log.Logger,
|
||||||
|
muteTimingService provisioning.MuteTimingService) MuteTimesProvisioner {
|
||||||
|
return &defaultMuteTimesProvisioner{
|
||||||
|
logger: logger,
|
||||||
|
muteTimingService: muteTimingService,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *defaultMuteTimesProvisioner) Provision(ctx context.Context,
|
||||||
|
files []*AlertingFile) error {
|
||||||
|
cache := map[int64]map[string]definitions.MuteTimeInterval{}
|
||||||
|
for _, file := range files {
|
||||||
|
for _, muteTiming := range file.MuteTimes {
|
||||||
|
if _, exists := cache[muteTiming.OrgID]; !exists {
|
||||||
|
intervals, err := c.muteTimingService.GetMuteTimings(ctx, muteTiming.OrgID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
cache[muteTiming.OrgID] = make(map[string]definitions.MuteTimeInterval, len(intervals))
|
||||||
|
for _, interval := range intervals {
|
||||||
|
cache[muteTiming.OrgID][interval.Name] = interval
|
||||||
|
}
|
||||||
|
}
|
||||||
|
muteTiming.MuteTime.Provenance = models.ProvenanceFile
|
||||||
|
if _, exists := cache[muteTiming.OrgID][muteTiming.MuteTime.Name]; exists {
|
||||||
|
_, err := c.muteTimingService.UpdateMuteTiming(ctx, muteTiming.MuteTime, muteTiming.OrgID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
_, err := c.muteTimingService.CreateMuteTiming(ctx, muteTiming.MuteTime, muteTiming.OrgID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *defaultMuteTimesProvisioner) Unprovision(ctx context.Context,
|
||||||
|
files []*AlertingFile) error {
|
||||||
|
for _, file := range files {
|
||||||
|
for _, deleteMuteTime := range file.DeleteMuteTimes {
|
||||||
|
err := c.muteTimingService.DeleteMuteTiming(ctx, deleteMuteTime.Name, deleteMuteTime.OrgID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
55
pkg/services/provisioning/alerting/mute_times_types.go
Normal file
55
pkg/services/provisioning/alerting/mute_times_types.go
Normal 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 MuteTimeV1 struct {
|
||||||
|
OrgID values.Int64Value `json:"orgId" yaml:"orgId"`
|
||||||
|
MuteTime definitions.MuteTimeInterval `json:",inline" yaml:",inline"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v1 *MuteTimeV1) mapToModel() MuteTime {
|
||||||
|
orgID := v1.OrgID.Value()
|
||||||
|
if orgID < 1 {
|
||||||
|
orgID = 1
|
||||||
|
}
|
||||||
|
return MuteTime{
|
||||||
|
OrgID: orgID,
|
||||||
|
MuteTime: v1.MuteTime,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type MuteTime struct {
|
||||||
|
OrgID int64
|
||||||
|
MuteTime definitions.MuteTimeInterval
|
||||||
|
}
|
||||||
|
|
||||||
|
type DeleteMuteTimeV1 struct {
|
||||||
|
OrgID values.Int64Value `json:"orgId" yaml:"orgId"`
|
||||||
|
Name values.StringValue `json:"name" yaml:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v1 *DeleteMuteTimeV1) mapToModel() (DeleteMuteTime, error) {
|
||||||
|
name := strings.TrimSpace(v1.Name.Value())
|
||||||
|
if name == "" {
|
||||||
|
return DeleteMuteTime{}, errors.New("delete mute time missing name")
|
||||||
|
}
|
||||||
|
orgID := v1.OrgID.Value()
|
||||||
|
if orgID < 1 {
|
||||||
|
orgID = 1
|
||||||
|
}
|
||||||
|
return DeleteMuteTime{
|
||||||
|
OrgID: orgID,
|
||||||
|
Name: name,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type DeleteMuteTime struct {
|
||||||
|
OrgID int64
|
||||||
|
Name string
|
||||||
|
}
|
@ -2,6 +2,7 @@ package alerting
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/infra/log"
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||||
@ -15,6 +16,7 @@ type ProvisionerConfig struct {
|
|||||||
RuleService provisioning.AlertRuleService
|
RuleService provisioning.AlertRuleService
|
||||||
ContactPointService provisioning.ContactPointService
|
ContactPointService provisioning.ContactPointService
|
||||||
NotificiationPolicyService provisioning.NotificationPolicyService
|
NotificiationPolicyService provisioning.NotificationPolicyService
|
||||||
|
MuteTimingService provisioning.MuteTimingService
|
||||||
}
|
}
|
||||||
|
|
||||||
func Provision(ctx context.Context, cfg ProvisionerConfig) error {
|
func Provision(ctx context.Context, cfg ProvisionerConfig) error {
|
||||||
@ -33,25 +35,34 @@ func Provision(ctx context.Context, cfg ProvisionerConfig) error {
|
|||||||
cfg.RuleService)
|
cfg.RuleService)
|
||||||
err = ruleProvisioner.Provision(ctx, files)
|
err = ruleProvisioner.Provision(ctx, files)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("alert rules: %w", err)
|
||||||
}
|
}
|
||||||
cpProvisioner := NewContactPointProvisoner(logger, cfg.ContactPointService)
|
cpProvisioner := NewContactPointProvisoner(logger, cfg.ContactPointService)
|
||||||
err = cpProvisioner.Provision(ctx, files)
|
err = cpProvisioner.Provision(ctx, files)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("contact points: %w", err)
|
||||||
|
}
|
||||||
|
mtProvisioner := NewMuteTimesProvisioner(logger, cfg.MuteTimingService)
|
||||||
|
err = mtProvisioner.Provision(ctx, files)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("mute times: %w", err)
|
||||||
}
|
}
|
||||||
npProvisioner := NewNotificationPolicyProvisoner(logger, cfg.NotificiationPolicyService)
|
npProvisioner := NewNotificationPolicyProvisoner(logger, cfg.NotificiationPolicyService)
|
||||||
err = npProvisioner.Provision(ctx, files)
|
err = npProvisioner.Provision(ctx, files)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("notification policies: %w", err)
|
||||||
}
|
}
|
||||||
err = npProvisioner.Unprovision(ctx, files)
|
err = npProvisioner.Unprovision(ctx, files)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("notification policies: %w", err)
|
||||||
}
|
}
|
||||||
err = cpProvisioner.Unprovision(ctx, files)
|
err = cpProvisioner.Unprovision(ctx, files)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("contact points: %w", err)
|
||||||
|
}
|
||||||
|
err = mtProvisioner.Unprovision(ctx, files)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("mute times: %w", err)
|
||||||
}
|
}
|
||||||
logger.Info("finished to provision alerting")
|
logger.Info("finished to provision alerting")
|
||||||
return nil
|
return nil
|
||||||
|
@ -0,0 +1,10 @@
|
|||||||
|
apiVersion: 1
|
||||||
|
muteTimes:
|
||||||
|
- orgId: 1337
|
||||||
|
name: test
|
||||||
|
time_intervals:
|
||||||
|
- times:
|
||||||
|
- start_time: '06:00'
|
||||||
|
end_time: '23:59'
|
||||||
|
weekdays:
|
||||||
|
- monday
|
11
pkg/services/provisioning/alerting/testdata/mute_times/correct-properties/mute_times.yml
vendored
Normal file
11
pkg/services/provisioning/alerting/testdata/mute_times/correct-properties/mute_times.yml
vendored
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
apiVersion: 1
|
||||||
|
muteTimes:
|
||||||
|
- name: test
|
||||||
|
time_intervals:
|
||||||
|
- times:
|
||||||
|
- start_time: '06:00'
|
||||||
|
end_time: '23:59'
|
||||||
|
weekdays: ['monday:wednesday','saturday', 'sunday']
|
||||||
|
months: ['1:3', 'may:august', 'december']
|
||||||
|
years: ['2020:2022', '2030']
|
||||||
|
days_of_month: ['1:5', '-3:-1']
|
17
pkg/services/provisioning/alerting/testdata/mute_times/multiple-mute-times/mute_times.yml
vendored
Normal file
17
pkg/services/provisioning/alerting/testdata/mute_times/multiple-mute-times/mute_times.yml
vendored
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
apiVersion: 1
|
||||||
|
muteTimes:
|
||||||
|
- name: test
|
||||||
|
time_intervals:
|
||||||
|
- times:
|
||||||
|
- start_time: '06:00'
|
||||||
|
end_time: '23:59'
|
||||||
|
weekdays:
|
||||||
|
- monday
|
||||||
|
- orgId: 1337
|
||||||
|
name: test
|
||||||
|
time_intervals:
|
||||||
|
- times:
|
||||||
|
- start_time: '06:00'
|
||||||
|
end_time: '23:59'
|
||||||
|
weekdays:
|
||||||
|
- monday
|
@ -21,6 +21,8 @@ type AlertingFile struct {
|
|||||||
DeleteContactPoints []DeleteContactPoint
|
DeleteContactPoints []DeleteContactPoint
|
||||||
Policies []NotificiationPolicy
|
Policies []NotificiationPolicy
|
||||||
ResetPolicies []OrgID
|
ResetPolicies []OrgID
|
||||||
|
MuteTimes []MuteTime
|
||||||
|
DeleteMuteTimes []DeleteMuteTime
|
||||||
}
|
}
|
||||||
|
|
||||||
type AlertingFileV1 struct {
|
type AlertingFileV1 struct {
|
||||||
@ -32,23 +34,40 @@ type AlertingFileV1 struct {
|
|||||||
DeleteContactPoints []DeleteContactPointV1 `json:"deleteContactPoints" yaml:"deleteContactPoints"`
|
DeleteContactPoints []DeleteContactPointV1 `json:"deleteContactPoints" yaml:"deleteContactPoints"`
|
||||||
Policies []NotificiationPolicyV1 `json:"policies" yaml:"policies"`
|
Policies []NotificiationPolicyV1 `json:"policies" yaml:"policies"`
|
||||||
ResetPolicies []values.Int64Value `json:"resetPolicies" yaml:"resetPolicies"`
|
ResetPolicies []values.Int64Value `json:"resetPolicies" yaml:"resetPolicies"`
|
||||||
|
MuteTimes []MuteTimeV1 `json:"muteTimes" yaml:"muteTimes"`
|
||||||
|
DeleteMuteTimes []DeleteMuteTimeV1 `json:"deleteMuteTimes" yaml:"deleteMuteTimes"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fileV1 *AlertingFileV1) MapToModel() (AlertingFile, error) {
|
func (fileV1 *AlertingFileV1) MapToModel() (AlertingFile, error) {
|
||||||
alertingFile := AlertingFile{}
|
alertingFile := AlertingFile{}
|
||||||
alertingFile.Filename = fileV1.Filename
|
alertingFile.Filename = fileV1.Filename
|
||||||
err := fileV1.mapRules(&alertingFile)
|
if err := fileV1.mapRules(&alertingFile); err != nil {
|
||||||
if err != nil {
|
|
||||||
return AlertingFile{}, fmt.Errorf("failure parsing rules: %w", err)
|
return AlertingFile{}, fmt.Errorf("failure parsing rules: %w", err)
|
||||||
}
|
}
|
||||||
err = fileV1.mapContactPoint(&alertingFile)
|
if err := fileV1.mapContactPoint(&alertingFile); 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)
|
fileV1.mapPolicies(&alertingFile)
|
||||||
|
if err := fileV1.mapMuteTimes(&alertingFile); err != nil {
|
||||||
|
return AlertingFile{}, fmt.Errorf("failure parsing mute times: %w", err)
|
||||||
|
}
|
||||||
return alertingFile, nil
|
return alertingFile, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (fileV1 *AlertingFileV1) mapMuteTimes(alertingFile *AlertingFile) error {
|
||||||
|
for _, mtV1 := range fileV1.MuteTimes {
|
||||||
|
alertingFile.MuteTimes = append(alertingFile.MuteTimes, mtV1.mapToModel())
|
||||||
|
}
|
||||||
|
for _, deleteV1 := range fileV1.DeleteMuteTimes {
|
||||||
|
delReq, err := deleteV1.mapToModel()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
alertingFile.DeleteMuteTimes = append(alertingFile.DeleteMuteTimes, delReq)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (fileV1 *AlertingFileV1) mapPolicies(alertingFile *AlertingFile) {
|
func (fileV1 *AlertingFileV1) mapPolicies(alertingFile *AlertingFile) {
|
||||||
for _, npV1 := range fileV1.Policies {
|
for _, npV1 := range fileV1.Policies {
|
||||||
alertingFile.Policies = append(alertingFile.Policies, npV1.mapToModel())
|
alertingFile.Policies = append(alertingFile.Policies, npV1.mapToModel())
|
||||||
|
@ -274,6 +274,7 @@ func (ps *ProvisioningServiceImpl) ProvisionAlerting(ctx context.Context) error
|
|||||||
st, ps.SQLStore, ps.log)
|
st, ps.SQLStore, ps.log)
|
||||||
notificationPolicyService := provisioning.NewNotificationPolicyService(&st,
|
notificationPolicyService := provisioning.NewNotificationPolicyService(&st,
|
||||||
st, ps.SQLStore, ps.Cfg.UnifiedAlerting, ps.log)
|
st, ps.SQLStore, ps.Cfg.UnifiedAlerting, ps.log)
|
||||||
|
mutetimingsService := provisioning.NewMuteTimingService(&st, st, &st, ps.log)
|
||||||
cfg := prov_alerting.ProvisionerConfig{
|
cfg := prov_alerting.ProvisionerConfig{
|
||||||
Path: alertingPath,
|
Path: alertingPath,
|
||||||
RuleService: *ruleService,
|
RuleService: *ruleService,
|
||||||
@ -281,6 +282,7 @@ func (ps *ProvisioningServiceImpl) ProvisionAlerting(ctx context.Context) error
|
|||||||
DashboardProvService: ps.dashboardProvisioningService,
|
DashboardProvService: ps.dashboardProvisioningService,
|
||||||
ContactPointService: *contactPointService,
|
ContactPointService: *contactPointService,
|
||||||
NotificiationPolicyService: *notificationPolicyService,
|
NotificiationPolicyService: *notificationPolicyService,
|
||||||
|
MuteTimingService: *mutetimingsService,
|
||||||
}
|
}
|
||||||
return ps.provisionAlerting(ctx, cfg)
|
return ps.provisionAlerting(ctx, cfg)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user