mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Alerting: Add provenance guard to config api (#50147)
* Alerting: add provenance guard to config api * add tests * only guard if config valid * adapt error message * simplify logic * rename arguments * make logic more straight forward * rename opt to options * remove useless maps
This commit is contained in:
parent
0cec12b8a9
commit
4cc8c6f745
@ -217,7 +217,15 @@ func (srv AlertmanagerSrv) RouteGetSilences(c *models.ReqContext) response.Respo
|
||||
}
|
||||
|
||||
func (srv AlertmanagerSrv) RoutePostAlertingConfig(c *models.ReqContext, body apimodels.PostableUserConfig) response.Response {
|
||||
err := srv.mam.ApplyAlertmanagerConfiguration(c.Req.Context(), c.OrgId, body)
|
||||
currentConfig, err := srv.mam.GetAlertmanagerConfiguration(c.Req.Context(), c.OrgId)
|
||||
// If a config is present and valid we proceed with the guard, otherwise we
|
||||
// just bypass the guard which is okay as we are anyway in an invalid state.
|
||||
if err == nil {
|
||||
if err := srv.provenanceGuard(currentConfig, body); err != nil {
|
||||
return ErrResp(http.StatusBadRequest, err, "")
|
||||
}
|
||||
}
|
||||
err = srv.mam.ApplyAlertmanagerConfiguration(c.Req.Context(), c.OrgId, body)
|
||||
if err == nil {
|
||||
return response.JSON(http.StatusAccepted, util.DynMap{"message": "configuration created"})
|
||||
}
|
||||
|
146
pkg/services/ngalert/api/api_alertmanager_guards.go
Normal file
146
pkg/services/ngalert/api/api_alertmanager_guards.go
Normal file
@ -0,0 +1,146 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/google/go-cmp/cmp/cmpopts"
|
||||
apimodels "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
|
||||
ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models"
|
||||
"github.com/grafana/grafana/pkg/util/cmputil"
|
||||
amConfig "github.com/prometheus/alertmanager/config"
|
||||
"github.com/prometheus/alertmanager/pkg/labels"
|
||||
)
|
||||
|
||||
func (srv AlertmanagerSrv) provenanceGuard(currentConfig apimodels.GettableUserConfig, newConfig apimodels.PostableUserConfig) error {
|
||||
if err := checkRoutes(currentConfig, newConfig); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := checkTemplates(currentConfig, newConfig); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := checkContactPoints(currentConfig.AlertmanagerConfig.Receivers, newConfig.AlertmanagerConfig.Receivers); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := checkMuteTimes(currentConfig, newConfig); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkRoutes(currentConfig apimodels.GettableUserConfig, newConfig apimodels.PostableUserConfig) error {
|
||||
reporter := cmputil.DiffReporter{}
|
||||
options := []cmp.Option{cmp.Reporter(&reporter), cmpopts.EquateEmpty(), cmpopts.IgnoreUnexported(labels.Matcher{})}
|
||||
routesEqual := cmp.Equal(currentConfig.AlertmanagerConfig.Route, newConfig.AlertmanagerConfig.Route, options...)
|
||||
if !routesEqual && currentConfig.AlertmanagerConfig.Route.Provenance != ngmodels.ProvenanceNone {
|
||||
return fmt.Errorf("policies were provisioned and cannot be changed through the UI")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkTemplates(currentConfig apimodels.GettableUserConfig, newConfig apimodels.PostableUserConfig) error {
|
||||
for name, template := range currentConfig.TemplateFiles {
|
||||
provenance := ngmodels.ProvenanceNone
|
||||
if prov, present := currentConfig.TemplateFileProvenances[name]; present {
|
||||
provenance = prov
|
||||
}
|
||||
if provenance == ngmodels.ProvenanceNone {
|
||||
continue // we are only interested in non none
|
||||
}
|
||||
found := false
|
||||
for newName, newTemplate := range newConfig.TemplateFiles {
|
||||
if name != newName {
|
||||
continue
|
||||
}
|
||||
found = true
|
||||
if template != newTemplate {
|
||||
return fmt.Errorf("cannot save provisioned template '%s'", name)
|
||||
}
|
||||
break // we found the template and we can proceed
|
||||
}
|
||||
if !found {
|
||||
return fmt.Errorf("cannot delete provisioned template '%s'", name)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkContactPoints(currReceivers []*apimodels.GettableApiReceiver, newReceivers []*apimodels.PostableApiReceiver) error {
|
||||
newCPs := make(map[string]*apimodels.PostableGrafanaReceiver)
|
||||
for _, postedReceiver := range newReceivers {
|
||||
for _, postedContactPoint := range postedReceiver.GrafanaManagedReceivers {
|
||||
newCPs[postedContactPoint.UID] = postedContactPoint
|
||||
}
|
||||
}
|
||||
for _, existingReceiver := range currReceivers {
|
||||
for _, contactPoint := range existingReceiver.GrafanaManagedReceivers {
|
||||
if contactPoint.Provenance == ngmodels.ProvenanceNone {
|
||||
continue // we are only interested in non none
|
||||
}
|
||||
postedContactPoint, present := newCPs[contactPoint.UID]
|
||||
if !present {
|
||||
return fmt.Errorf("cannot delete provisioned contact point '%s'", contactPoint.Name)
|
||||
}
|
||||
editErr := fmt.Errorf("cannot save provisioned contact point '%s'", contactPoint.Name)
|
||||
if contactPoint.DisableResolveMessage != postedContactPoint.DisableResolveMessage {
|
||||
return editErr
|
||||
}
|
||||
if contactPoint.Name != postedContactPoint.Name {
|
||||
return editErr
|
||||
}
|
||||
if contactPoint.Type != postedContactPoint.Type {
|
||||
return editErr
|
||||
}
|
||||
for key := range contactPoint.SecureFields {
|
||||
if value, present := postedContactPoint.SecureSettings[key]; present && value != "" {
|
||||
return editErr
|
||||
}
|
||||
}
|
||||
existingSettings, err := contactPoint.Settings.Map()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
newSettings, err := postedContactPoint.Settings.Map()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for key, val := range existingSettings {
|
||||
if newVal, present := newSettings[key]; present {
|
||||
if val != newVal {
|
||||
return editErr
|
||||
}
|
||||
} else {
|
||||
return editErr
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkMuteTimes(currentConfig apimodels.GettableUserConfig, newConfig apimodels.PostableUserConfig) error {
|
||||
newMTs := make(map[string]amConfig.MuteTimeInterval)
|
||||
for _, newMuteTime := range newConfig.AlertmanagerConfig.MuteTimeIntervals {
|
||||
newMTs[newMuteTime.Name] = newMuteTime
|
||||
}
|
||||
for _, muteTime := range currentConfig.AlertmanagerConfig.MuteTimeIntervals {
|
||||
provenance := ngmodels.ProvenanceNone
|
||||
if prov, present := currentConfig.AlertmanagerConfig.MuteTimeProvenances[muteTime.Name]; present {
|
||||
provenance = prov
|
||||
}
|
||||
if provenance == ngmodels.ProvenanceNone {
|
||||
continue // we are only interested in non none
|
||||
}
|
||||
postedMT, present := newMTs[muteTime.Name]
|
||||
if !present {
|
||||
return fmt.Errorf("cannot delete provisioned mute time '%s'", muteTime.Name)
|
||||
}
|
||||
reporter := cmputil.DiffReporter{}
|
||||
options := []cmp.Option{cmp.Reporter(&reporter), cmpopts.EquateEmpty()}
|
||||
timesEqual := cmp.Equal(muteTime.TimeIntervals, postedMT.TimeIntervals, options...)
|
||||
if !timesEqual {
|
||||
return fmt.Errorf("cannot save provisioned mute time '%s'", muteTime.Name)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
589
pkg/services/ngalert/api/api_alertmanager_guards_test.go
Normal file
589
pkg/services/ngalert/api/api_alertmanager_guards_test.go
Normal file
@ -0,0 +1,589 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/models"
|
||||
amConfig "github.com/prometheus/alertmanager/config"
|
||||
"github.com/prometheus/alertmanager/pkg/labels"
|
||||
"github.com/prometheus/alertmanager/timeinterval"
|
||||
"github.com/prometheus/common/model"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestCheckRoute(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
shouldErr bool
|
||||
currentConfig definitions.GettableUserConfig
|
||||
newConfig definitions.PostableUserConfig
|
||||
}{
|
||||
{
|
||||
name: "equal configs should not error",
|
||||
shouldErr: false,
|
||||
currentConfig: gettableRoute(t, models.ProvenanceAPI),
|
||||
newConfig: postableRoute(t, models.ProvenanceAPI),
|
||||
},
|
||||
{
|
||||
name: "editing a non provisioned object should not fail",
|
||||
shouldErr: false,
|
||||
currentConfig: gettableRoute(t, models.ProvenanceNone),
|
||||
newConfig: func() definitions.PostableUserConfig {
|
||||
cfg := postableRoute(t, models.ProvenanceNone)
|
||||
cfg.AlertmanagerConfig.Route.Matchers[0].Value = "123"
|
||||
return cfg
|
||||
}(),
|
||||
},
|
||||
{
|
||||
name: "editing a provisioned object should fail",
|
||||
shouldErr: true,
|
||||
currentConfig: gettableRoute(t, models.ProvenanceAPI),
|
||||
newConfig: func() definitions.PostableUserConfig {
|
||||
cfg := postableRoute(t, models.ProvenanceAPI)
|
||||
cfg.AlertmanagerConfig.Route.Matchers[0].Value = "123"
|
||||
return cfg
|
||||
}(),
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
err := checkRoutes(test.currentConfig, test.newConfig)
|
||||
if test.shouldErr {
|
||||
require.Error(t, err)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func gettableRoute(t *testing.T, provenance models.Provenance) definitions.GettableUserConfig {
|
||||
t.Helper()
|
||||
return definitions.GettableUserConfig{
|
||||
AlertmanagerConfig: definitions.GettableApiAlertingConfig{
|
||||
Config: definitions.Config{
|
||||
Route: &definitions.Route{
|
||||
Provenance: provenance,
|
||||
Continue: true,
|
||||
GroupBy: []model.LabelName{
|
||||
"...",
|
||||
},
|
||||
Matchers: amConfig.Matchers{
|
||||
{
|
||||
Name: "a",
|
||||
Type: labels.MatchEqual,
|
||||
Value: "b",
|
||||
},
|
||||
},
|
||||
Routes: []*definitions.Route{
|
||||
{
|
||||
Matchers: amConfig.Matchers{
|
||||
{
|
||||
Name: "x",
|
||||
Type: labels.MatchNotEqual,
|
||||
Value: "y",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func postableRoute(t *testing.T, provenace models.Provenance) definitions.PostableUserConfig {
|
||||
t.Helper()
|
||||
return definitions.PostableUserConfig{
|
||||
AlertmanagerConfig: definitions.PostableApiAlertingConfig{
|
||||
Config: definitions.Config{
|
||||
Route: &definitions.Route{
|
||||
Provenance: provenace,
|
||||
Continue: true,
|
||||
GroupBy: []model.LabelName{
|
||||
"...",
|
||||
},
|
||||
Matchers: amConfig.Matchers{
|
||||
{
|
||||
Name: "a",
|
||||
Type: labels.MatchEqual,
|
||||
Value: "b",
|
||||
},
|
||||
},
|
||||
Routes: []*definitions.Route{
|
||||
{
|
||||
Matchers: amConfig.Matchers{
|
||||
{
|
||||
Name: "x",
|
||||
Type: labels.MatchNotEqual,
|
||||
Value: "y",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckTemplates(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
shouldErr bool
|
||||
currentConfig definitions.GettableUserConfig
|
||||
newConfig definitions.PostableUserConfig
|
||||
}{
|
||||
{
|
||||
name: "equal configs should not error",
|
||||
shouldErr: false,
|
||||
currentConfig: gettableTemplates(t, "test-1", models.ProvenanceAPI),
|
||||
newConfig: postableTemplate(t, "test-1"),
|
||||
},
|
||||
{
|
||||
name: "removing a non provisioned object should not fail",
|
||||
shouldErr: false,
|
||||
currentConfig: gettableTemplates(t, "test-1", models.ProvenanceNone),
|
||||
newConfig: definitions.PostableUserConfig{},
|
||||
},
|
||||
{
|
||||
name: "removing a provisioned object should fail",
|
||||
shouldErr: true,
|
||||
currentConfig: gettableTemplates(t, "test-1", models.ProvenanceAPI),
|
||||
newConfig: definitions.PostableUserConfig{},
|
||||
},
|
||||
{
|
||||
name: "adding a non provisioned object should not fail",
|
||||
shouldErr: false,
|
||||
currentConfig: gettableTemplates(t, "test-1", models.ProvenanceAPI),
|
||||
newConfig: postableTemplate(t, "test-1", "test-2"),
|
||||
},
|
||||
{
|
||||
name: "editing a non provisioned object should not fail",
|
||||
shouldErr: false,
|
||||
currentConfig: gettableTemplates(t, "test-1", models.ProvenanceNone),
|
||||
newConfig: func() definitions.PostableUserConfig {
|
||||
cfg := postableTemplate(t, "test-1")
|
||||
cfg.TemplateFiles["test-1"] = "some updated value"
|
||||
return cfg
|
||||
}(),
|
||||
},
|
||||
{
|
||||
name: "editing a provisioned object should fail",
|
||||
shouldErr: true,
|
||||
currentConfig: gettableTemplates(t, "test-1", models.ProvenanceAPI),
|
||||
newConfig: func() definitions.PostableUserConfig {
|
||||
cfg := postableTemplate(t, "test-1")
|
||||
cfg.TemplateFiles["test-1"] = "some updated value"
|
||||
return cfg
|
||||
}(),
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
err := checkTemplates(test.currentConfig, test.newConfig)
|
||||
if test.shouldErr {
|
||||
require.Error(t, err)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func gettableTemplates(t *testing.T, name string, provenance models.Provenance) definitions.GettableUserConfig {
|
||||
t.Helper()
|
||||
return definitions.GettableUserConfig{
|
||||
TemplateFiles: map[string]string{
|
||||
name: "some-template",
|
||||
},
|
||||
TemplateFileProvenances: map[string]models.Provenance{
|
||||
name: provenance,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func postableTemplate(t *testing.T, names ...string) definitions.PostableUserConfig {
|
||||
t.Helper()
|
||||
files := map[string]string{}
|
||||
for _, name := range names {
|
||||
files[name] = "some-template"
|
||||
}
|
||||
return definitions.PostableUserConfig{
|
||||
TemplateFiles: files,
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckContactPoints(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
shouldErr bool
|
||||
currentConfig []*definitions.GettableApiReceiver
|
||||
newConfig []*definitions.PostableApiReceiver
|
||||
}{
|
||||
{
|
||||
name: "equal configs should not error",
|
||||
shouldErr: false,
|
||||
currentConfig: []*definitions.GettableApiReceiver{
|
||||
defaultGettableReceiver(t, "test-1", models.ProvenanceAPI),
|
||||
},
|
||||
newConfig: []*definitions.PostableApiReceiver{
|
||||
defaultPostableReceiver(t, "test-1"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "removing a non provisioned object should not fail",
|
||||
shouldErr: false,
|
||||
currentConfig: []*definitions.GettableApiReceiver{
|
||||
defaultGettableReceiver(t, "test-1", models.ProvenanceNone),
|
||||
},
|
||||
newConfig: []*definitions.PostableApiReceiver{},
|
||||
},
|
||||
{
|
||||
name: "removing a provisioned object should fail",
|
||||
shouldErr: true,
|
||||
currentConfig: []*definitions.GettableApiReceiver{
|
||||
defaultGettableReceiver(t, "test-1", models.ProvenanceAPI),
|
||||
},
|
||||
newConfig: []*definitions.PostableApiReceiver{},
|
||||
},
|
||||
{
|
||||
name: "adding a non provisioned object should not fail",
|
||||
shouldErr: false,
|
||||
currentConfig: []*definitions.GettableApiReceiver{
|
||||
defaultGettableReceiver(t, "test-1", models.ProvenanceAPI),
|
||||
},
|
||||
newConfig: []*definitions.PostableApiReceiver{
|
||||
defaultPostableReceiver(t, "test-1"),
|
||||
defaultPostableReceiver(t, "test-2"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "editing a non provisioned object should not fail",
|
||||
shouldErr: false,
|
||||
currentConfig: []*definitions.GettableApiReceiver{
|
||||
defaultGettableReceiver(t, "test-1", models.ProvenanceNone),
|
||||
},
|
||||
newConfig: []*definitions.PostableApiReceiver{
|
||||
func() *definitions.PostableApiReceiver {
|
||||
receiver := defaultPostableReceiver(t, "test-1")
|
||||
receiver.GrafanaManagedReceivers[0].SecureSettings = map[string]string{
|
||||
"url": "newUrl",
|
||||
}
|
||||
return receiver
|
||||
}(),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "editing a provisioned object should fail",
|
||||
shouldErr: true,
|
||||
currentConfig: []*definitions.GettableApiReceiver{
|
||||
defaultGettableReceiver(t, "test-1", models.ProvenanceAPI),
|
||||
},
|
||||
newConfig: []*definitions.PostableApiReceiver{
|
||||
func() *definitions.PostableApiReceiver {
|
||||
receiver := defaultPostableReceiver(t, "test-1")
|
||||
receiver.GrafanaManagedReceivers[0].SecureSettings = map[string]string{
|
||||
"url": "newUrl",
|
||||
}
|
||||
return receiver
|
||||
}(),
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
err := checkContactPoints(test.currentConfig, test.newConfig)
|
||||
if test.shouldErr {
|
||||
require.Error(t, err)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func defaultGettableReceiver(t *testing.T, uid string, provenance models.Provenance) *definitions.GettableApiReceiver {
|
||||
t.Helper()
|
||||
return &definitions.GettableApiReceiver{
|
||||
GettableGrafanaReceivers: definitions.GettableGrafanaReceivers{
|
||||
GrafanaManagedReceivers: []*definitions.GettableGrafanaReceiver{
|
||||
{
|
||||
UID: "123",
|
||||
Name: "yeah",
|
||||
Type: "slack",
|
||||
DisableResolveMessage: true,
|
||||
Provenance: provenance,
|
||||
SecureFields: map[string]bool{
|
||||
"url": true,
|
||||
},
|
||||
Settings: simplejson.NewFromAny(map[string]interface{}{
|
||||
"hello": "world",
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func defaultPostableReceiver(t *testing.T, uid string) *definitions.PostableApiReceiver {
|
||||
t.Helper()
|
||||
return &definitions.PostableApiReceiver{
|
||||
PostableGrafanaReceivers: definitions.PostableGrafanaReceivers{
|
||||
GrafanaManagedReceivers: []*definitions.PostableGrafanaReceiver{
|
||||
{
|
||||
UID: "123",
|
||||
Name: "yeah",
|
||||
Type: "slack",
|
||||
DisableResolveMessage: true,
|
||||
Settings: simplejson.NewFromAny(map[string]interface{}{
|
||||
"hello": "world",
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckMuteTimes(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
shouldErr bool
|
||||
currentConfig definitions.GettableUserConfig
|
||||
newConfig definitions.PostableUserConfig
|
||||
}{
|
||||
{
|
||||
name: "equal configs should not error",
|
||||
shouldErr: false,
|
||||
currentConfig: gettableMuteIntervals(t,
|
||||
[]amConfig.MuteTimeInterval{
|
||||
{
|
||||
Name: "test-1",
|
||||
TimeIntervals: defaultInterval(t),
|
||||
},
|
||||
{
|
||||
Name: "test-2",
|
||||
TimeIntervals: defaultInterval(t),
|
||||
},
|
||||
},
|
||||
map[string]models.Provenance{
|
||||
"test-1": models.ProvenanceNone,
|
||||
}),
|
||||
newConfig: postableMuteIntervals(t,
|
||||
[]amConfig.MuteTimeInterval{
|
||||
{
|
||||
Name: "test-1",
|
||||
TimeIntervals: defaultInterval(t),
|
||||
},
|
||||
{
|
||||
Name: "test-2",
|
||||
TimeIntervals: defaultInterval(t),
|
||||
},
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "removing a non provisioned object should not fail",
|
||||
shouldErr: false,
|
||||
currentConfig: gettableMuteIntervals(t,
|
||||
[]amConfig.MuteTimeInterval{
|
||||
{
|
||||
Name: "test-1",
|
||||
TimeIntervals: defaultInterval(t),
|
||||
},
|
||||
},
|
||||
map[string]models.Provenance{
|
||||
"test-1": models.ProvenanceNone,
|
||||
}),
|
||||
newConfig: postableMuteIntervals(t, []amConfig.MuteTimeInterval{}),
|
||||
},
|
||||
{
|
||||
name: "removing a provisioned object should fail",
|
||||
shouldErr: true,
|
||||
currentConfig: gettableMuteIntervals(t,
|
||||
[]amConfig.MuteTimeInterval{
|
||||
{
|
||||
Name: "test-1",
|
||||
TimeIntervals: defaultInterval(t),
|
||||
},
|
||||
{
|
||||
Name: "test-2",
|
||||
TimeIntervals: defaultInterval(t),
|
||||
},
|
||||
},
|
||||
map[string]models.Provenance{
|
||||
"test-1": models.ProvenanceAPI,
|
||||
}),
|
||||
newConfig: postableMuteIntervals(t, []amConfig.MuteTimeInterval{
|
||||
{
|
||||
Name: "test-2",
|
||||
TimeIntervals: defaultInterval(t),
|
||||
},
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "adding a non provisioned object should not fail",
|
||||
shouldErr: false,
|
||||
currentConfig: gettableMuteIntervals(t,
|
||||
[]amConfig.MuteTimeInterval{
|
||||
{
|
||||
Name: "test-1",
|
||||
TimeIntervals: defaultInterval(t),
|
||||
},
|
||||
},
|
||||
map[string]models.Provenance{
|
||||
"test-1": models.ProvenanceNone,
|
||||
}),
|
||||
newConfig: postableMuteIntervals(t,
|
||||
[]amConfig.MuteTimeInterval{
|
||||
{
|
||||
Name: "test-1",
|
||||
TimeIntervals: defaultInterval(t),
|
||||
},
|
||||
{
|
||||
Name: "test-2",
|
||||
TimeIntervals: defaultInterval(t),
|
||||
},
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "editing a non provisioned object should not fail",
|
||||
shouldErr: false,
|
||||
currentConfig: gettableMuteIntervals(t,
|
||||
[]amConfig.MuteTimeInterval{
|
||||
{
|
||||
Name: "test-1",
|
||||
TimeIntervals: defaultInterval(t),
|
||||
},
|
||||
},
|
||||
map[string]models.Provenance{
|
||||
"test-1": models.ProvenanceNone,
|
||||
}),
|
||||
newConfig: postableMuteIntervals(t,
|
||||
[]amConfig.MuteTimeInterval{
|
||||
{
|
||||
Name: "test-1",
|
||||
TimeIntervals: func() []timeinterval.TimeInterval {
|
||||
intervals := defaultInterval(t)
|
||||
intervals[0].Times = []timeinterval.TimeRange{
|
||||
{
|
||||
StartMinute: 10,
|
||||
EndMinute: 50,
|
||||
},
|
||||
}
|
||||
return intervals
|
||||
}(),
|
||||
},
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "editing a provisioned object should fail",
|
||||
shouldErr: true,
|
||||
currentConfig: gettableMuteIntervals(t,
|
||||
[]amConfig.MuteTimeInterval{
|
||||
{
|
||||
Name: "test-1",
|
||||
TimeIntervals: defaultInterval(t),
|
||||
},
|
||||
},
|
||||
map[string]models.Provenance{
|
||||
"test-1": models.ProvenanceAPI,
|
||||
}),
|
||||
newConfig: postableMuteIntervals(t,
|
||||
[]amConfig.MuteTimeInterval{
|
||||
{
|
||||
Name: "test-1",
|
||||
TimeIntervals: func() []timeinterval.TimeInterval {
|
||||
intervals := defaultInterval(t)
|
||||
intervals[0].Times = []timeinterval.TimeRange{
|
||||
{
|
||||
StartMinute: 10,
|
||||
EndMinute: 50,
|
||||
},
|
||||
}
|
||||
return intervals
|
||||
}(),
|
||||
},
|
||||
}),
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
err := checkMuteTimes(test.currentConfig, test.newConfig)
|
||||
if test.shouldErr {
|
||||
require.Error(t, err)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func gettableMuteIntervals(t *testing.T, muteTimeIntervals []amConfig.MuteTimeInterval, provenances map[string]models.Provenance) definitions.GettableUserConfig {
|
||||
return definitions.GettableUserConfig{
|
||||
AlertmanagerConfig: definitions.GettableApiAlertingConfig{
|
||||
MuteTimeProvenances: provenances,
|
||||
Config: definitions.Config{
|
||||
MuteTimeIntervals: muteTimeIntervals,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func postableMuteIntervals(t *testing.T, muteTimeIntervals []amConfig.MuteTimeInterval) definitions.PostableUserConfig {
|
||||
t.Helper()
|
||||
return definitions.PostableUserConfig{
|
||||
AlertmanagerConfig: definitions.PostableApiAlertingConfig{
|
||||
Config: definitions.Config{
|
||||
MuteTimeIntervals: muteTimeIntervals,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func defaultInterval(t *testing.T) []timeinterval.TimeInterval {
|
||||
t.Helper()
|
||||
return []timeinterval.TimeInterval{
|
||||
{
|
||||
Years: []timeinterval.YearRange{
|
||||
{
|
||||
InclusiveRange: timeinterval.InclusiveRange{
|
||||
Begin: 2002,
|
||||
End: 2008,
|
||||
},
|
||||
},
|
||||
},
|
||||
Times: []timeinterval.TimeRange{
|
||||
{
|
||||
StartMinute: 10,
|
||||
EndMinute: 40,
|
||||
},
|
||||
},
|
||||
Weekdays: []timeinterval.WeekdayRange{
|
||||
{
|
||||
InclusiveRange: timeinterval.InclusiveRange{
|
||||
Begin: 1,
|
||||
End: 5,
|
||||
},
|
||||
},
|
||||
},
|
||||
DaysOfMonth: []timeinterval.DayOfMonthRange{
|
||||
{
|
||||
InclusiveRange: timeinterval.InclusiveRange{
|
||||
Begin: 1,
|
||||
End: 20,
|
||||
},
|
||||
},
|
||||
},
|
||||
Months: []timeinterval.MonthRange{
|
||||
{
|
||||
InclusiveRange: timeinterval.InclusiveRange{
|
||||
Begin: 1,
|
||||
End: 6,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user