mirror of
https://github.com/grafana/grafana.git
synced 2024-11-25 18:30:41 -06:00
2188516a21
Terraform Issue: grafana/terraform-provider-grafana#1007 Nested routes should be allowed to inherit the contact point from the root (or direct parent) route but this fails in the provisioning API (it works in the UI)
278 lines
10 KiB
Go
278 lines
10 KiB
Go
package provisioning
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
|
|
"github.com/prometheus/alertmanager/config"
|
|
"github.com/prometheus/alertmanager/timeinterval"
|
|
"github.com/prometheus/common/model"
|
|
"github.com/stretchr/testify/mock"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"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/tests/fakes"
|
|
"github.com/grafana/grafana/pkg/setting"
|
|
)
|
|
|
|
func TestNotificationPolicyService(t *testing.T) {
|
|
t.Run("service gets policy tree from org's AM config", func(t *testing.T) {
|
|
sut := createNotificationPolicyServiceSut()
|
|
|
|
tree, err := sut.GetPolicyTree(context.Background(), 1)
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, "grafana-default-email", tree.Receiver)
|
|
})
|
|
|
|
t.Run("error if referenced mute time interval is not existing", func(t *testing.T) {
|
|
sut := createNotificationPolicyServiceSut()
|
|
sut.configStore.store = &MockAMConfigStore{}
|
|
cfg := createTestAlertingConfig()
|
|
cfg.AlertmanagerConfig.MuteTimeIntervals = []config.MuteTimeInterval{
|
|
{
|
|
Name: "not-the-one-we-need",
|
|
TimeIntervals: []timeinterval.TimeInterval{},
|
|
},
|
|
}
|
|
data, _ := serializeAlertmanagerConfig(*cfg)
|
|
sut.configStore.store.(*MockAMConfigStore).On("GetLatestAlertmanagerConfiguration", mock.Anything, mock.Anything).
|
|
Return(&models.AlertConfiguration{AlertmanagerConfiguration: string(data)}, nil)
|
|
sut.configStore.store.(*MockAMConfigStore).EXPECT().
|
|
UpdateAlertmanagerConfiguration(mock.Anything, mock.Anything).
|
|
Return(nil)
|
|
newRoute := createTestRoutingTree()
|
|
newRoute.Routes = append(newRoute.Routes, &definitions.Route{
|
|
Receiver: "slack receiver",
|
|
MuteTimeIntervals: []string{"not-existing"},
|
|
})
|
|
|
|
err := sut.UpdatePolicyTree(context.Background(), 1, newRoute, models.ProvenanceNone)
|
|
require.Error(t, err)
|
|
})
|
|
|
|
t.Run("pass if referenced mute time interval is existing", func(t *testing.T) {
|
|
sut := createNotificationPolicyServiceSut()
|
|
sut.configStore.store = &MockAMConfigStore{}
|
|
cfg := createTestAlertingConfig()
|
|
cfg.AlertmanagerConfig.MuteTimeIntervals = []config.MuteTimeInterval{
|
|
{
|
|
Name: "existing",
|
|
TimeIntervals: []timeinterval.TimeInterval{},
|
|
},
|
|
}
|
|
data, _ := serializeAlertmanagerConfig(*cfg)
|
|
sut.configStore.store.(*MockAMConfigStore).On("GetLatestAlertmanagerConfiguration", mock.Anything, mock.Anything).
|
|
Return(&models.AlertConfiguration{AlertmanagerConfiguration: string(data)}, nil)
|
|
sut.configStore.store.(*MockAMConfigStore).EXPECT().
|
|
UpdateAlertmanagerConfiguration(mock.Anything, mock.Anything).
|
|
Return(nil)
|
|
newRoute := createTestRoutingTree()
|
|
newRoute.Routes = append(newRoute.Routes, &definitions.Route{
|
|
Receiver: "slack receiver",
|
|
MuteTimeIntervals: []string{"existing"},
|
|
})
|
|
|
|
err := sut.UpdatePolicyTree(context.Background(), 1, newRoute, models.ProvenanceNone)
|
|
require.NoError(t, err)
|
|
})
|
|
|
|
t.Run("service stitches policy tree into org's AM config", func(t *testing.T) {
|
|
sut := createNotificationPolicyServiceSut()
|
|
|
|
newRoute := createTestRoutingTree()
|
|
|
|
err := sut.UpdatePolicyTree(context.Background(), 1, newRoute, models.ProvenanceNone)
|
|
require.NoError(t, err)
|
|
|
|
updated, err := sut.GetPolicyTree(context.Background(), 1)
|
|
require.NoError(t, err)
|
|
require.Equal(t, "slack receiver", updated.Receiver)
|
|
})
|
|
|
|
t.Run("no root receiver will error", func(t *testing.T) {
|
|
sut := createNotificationPolicyServiceSut()
|
|
|
|
newRoute := createTestRoutingTree()
|
|
newRoute.Receiver = ""
|
|
newRoute.Routes = append(newRoute.Routes, &definitions.Route{
|
|
Receiver: "",
|
|
})
|
|
|
|
err := sut.UpdatePolicyTree(context.Background(), 1, newRoute, models.ProvenanceNone)
|
|
require.EqualError(t, err, "invalid object specification: root route must specify a default receiver")
|
|
})
|
|
|
|
t.Run("allow receiver inheritance", func(t *testing.T) {
|
|
sut := createNotificationPolicyServiceSut()
|
|
|
|
newRoute := createTestRoutingTree()
|
|
newRoute.Routes = append(newRoute.Routes, &definitions.Route{
|
|
Receiver: "",
|
|
})
|
|
|
|
err := sut.UpdatePolicyTree(context.Background(), 1, newRoute, models.ProvenanceNone)
|
|
require.NoError(t, err)
|
|
})
|
|
|
|
t.Run("not existing receiver reference will error", func(t *testing.T) {
|
|
sut := createNotificationPolicyServiceSut()
|
|
|
|
newRoute := createTestRoutingTree()
|
|
newRoute.Routes = append(newRoute.Routes, &definitions.Route{
|
|
Receiver: "not-existing",
|
|
})
|
|
|
|
err := sut.UpdatePolicyTree(context.Background(), 1, newRoute, models.ProvenanceNone)
|
|
require.Error(t, err)
|
|
})
|
|
|
|
t.Run("existing receiver reference will pass", func(t *testing.T) {
|
|
sut := createNotificationPolicyServiceSut()
|
|
sut.configStore.store = &MockAMConfigStore{}
|
|
cfg := createTestAlertingConfig()
|
|
data, _ := serializeAlertmanagerConfig(*cfg)
|
|
sut.configStore.store.(*MockAMConfigStore).On("GetLatestAlertmanagerConfiguration", mock.Anything, mock.Anything).
|
|
Return(&models.AlertConfiguration{AlertmanagerConfiguration: string(data)}, nil)
|
|
sut.configStore.store.(*MockAMConfigStore).EXPECT().
|
|
UpdateAlertmanagerConfiguration(mock.Anything, mock.Anything).
|
|
Return(nil)
|
|
newRoute := createTestRoutingTree()
|
|
newRoute.Routes = append(newRoute.Routes, &definitions.Route{
|
|
Receiver: "existing",
|
|
})
|
|
|
|
err := sut.UpdatePolicyTree(context.Background(), 1, newRoute, models.ProvenanceNone)
|
|
require.NoError(t, err)
|
|
})
|
|
|
|
t.Run("default provenance of records is none", func(t *testing.T) {
|
|
sut := createNotificationPolicyServiceSut()
|
|
|
|
tree, err := sut.GetPolicyTree(context.Background(), 1)
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, models.ProvenanceNone, models.Provenance(tree.Provenance))
|
|
})
|
|
|
|
t.Run("service returns upgraded provenance value", func(t *testing.T) {
|
|
sut := createNotificationPolicyServiceSut()
|
|
newRoute := createTestRoutingTree()
|
|
|
|
err := sut.UpdatePolicyTree(context.Background(), 1, newRoute, models.ProvenanceAPI)
|
|
require.NoError(t, err)
|
|
|
|
updated, err := sut.GetPolicyTree(context.Background(), 1)
|
|
require.NoError(t, err)
|
|
require.Equal(t, models.ProvenanceAPI, models.Provenance(updated.Provenance))
|
|
})
|
|
|
|
t.Run("service respects concurrency token when updating", func(t *testing.T) {
|
|
sut := createNotificationPolicyServiceSut()
|
|
newRoute := createTestRoutingTree()
|
|
config, err := sut.GetAMConfigStore().GetLatestAlertmanagerConfiguration(context.Background(), 1)
|
|
require.NoError(t, err)
|
|
expectedConcurrencyToken := config.ConfigurationHash
|
|
|
|
err = sut.UpdatePolicyTree(context.Background(), 1, newRoute, models.ProvenanceAPI)
|
|
require.NoError(t, err)
|
|
|
|
fake := sut.GetAMConfigStore().(*fakes.FakeAlertmanagerConfigStore)
|
|
intercepted := fake.LastSaveCommand
|
|
require.Equal(t, expectedConcurrencyToken, intercepted.FetchedConfigurationHash)
|
|
})
|
|
|
|
t.Run("updating invalid route returns ValidationError", func(t *testing.T) {
|
|
sut := createNotificationPolicyServiceSut()
|
|
invalid := createTestRoutingTree()
|
|
repeat := model.Duration(0)
|
|
invalid.RepeatInterval = &repeat
|
|
|
|
err := sut.UpdatePolicyTree(context.Background(), 1, invalid, models.ProvenanceNone)
|
|
|
|
require.Error(t, err)
|
|
require.ErrorIs(t, err, ErrValidation)
|
|
})
|
|
|
|
t.Run("deleting route replaces with default", func(t *testing.T) {
|
|
sut := createNotificationPolicyServiceSut()
|
|
|
|
tree, err := sut.ResetPolicyTree(context.Background(), 1)
|
|
|
|
require.NoError(t, err)
|
|
require.Equal(t, "grafana-default-email", tree.Receiver)
|
|
require.Nil(t, tree.Routes)
|
|
require.Equal(t, []model.LabelName{models.FolderTitleLabel, model.AlertNameLabel}, tree.GroupBy)
|
|
})
|
|
|
|
t.Run("deleting route with missing default receiver restores receiver", func(t *testing.T) {
|
|
sut := createNotificationPolicyServiceSut()
|
|
sut.configStore.store = &MockAMConfigStore{}
|
|
cfg := createTestAlertingConfig()
|
|
cfg.AlertmanagerConfig.Route = &definitions.Route{
|
|
Receiver: "slack receiver",
|
|
}
|
|
cfg.AlertmanagerConfig.Receivers = []*definitions.PostableApiReceiver{
|
|
{
|
|
Receiver: config.Receiver{
|
|
Name: "slack receiver",
|
|
},
|
|
},
|
|
// No default receiver! Only our custom one.
|
|
}
|
|
data, _ := serializeAlertmanagerConfig(*cfg)
|
|
sut.configStore.store.(*MockAMConfigStore).On("GetLatestAlertmanagerConfiguration", mock.Anything, mock.Anything).
|
|
Return(&models.AlertConfiguration{AlertmanagerConfiguration: string(data)}, nil)
|
|
var interceptedSave = models.SaveAlertmanagerConfigurationCmd{}
|
|
sut.configStore.store.(*MockAMConfigStore).EXPECT().SaveSucceedsIntercept(&interceptedSave)
|
|
|
|
tree, err := sut.ResetPolicyTree(context.Background(), 1)
|
|
|
|
require.NoError(t, err)
|
|
require.Equal(t, "grafana-default-email", tree.Receiver)
|
|
require.NotEmpty(t, interceptedSave.AlertmanagerConfiguration)
|
|
// Deserializing with no error asserts that the saved configStore is semantically valid.
|
|
newCfg, err := deserializeAlertmanagerConfig([]byte(interceptedSave.AlertmanagerConfiguration))
|
|
require.NoError(t, err)
|
|
require.Len(t, newCfg.AlertmanagerConfig.Receivers, 2)
|
|
})
|
|
}
|
|
|
|
func createNotificationPolicyServiceSut() *NotificationPolicyService {
|
|
return &NotificationPolicyService{
|
|
configStore: &alertmanagerConfigStoreImpl{store: fakes.NewFakeAlertmanagerConfigStore(defaultAlertmanagerConfigJSON)},
|
|
provenanceStore: fakes.NewFakeProvisioningStore(),
|
|
xact: newNopTransactionManager(),
|
|
log: log.NewNopLogger(),
|
|
settings: setting.UnifiedAlertingSettings{
|
|
DefaultConfiguration: setting.GetAlertmanagerDefaultConfiguration(),
|
|
},
|
|
}
|
|
}
|
|
|
|
func createTestRoutingTree() definitions.Route {
|
|
return definitions.Route{
|
|
Receiver: "slack receiver",
|
|
}
|
|
}
|
|
|
|
func createTestAlertingConfig() *definitions.PostableUserConfig {
|
|
cfg, _ := deserializeAlertmanagerConfig([]byte(defaultConfig))
|
|
cfg.AlertmanagerConfig.Receivers = append(cfg.AlertmanagerConfig.Receivers,
|
|
&definitions.PostableApiReceiver{
|
|
Receiver: config.Receiver{
|
|
// default one from createTestRoutingTree()
|
|
Name: "slack receiver",
|
|
},
|
|
})
|
|
cfg.AlertmanagerConfig.Receivers = append(cfg.AlertmanagerConfig.Receivers,
|
|
&definitions.PostableApiReceiver{
|
|
Receiver: config.Receiver{
|
|
Name: "existing",
|
|
},
|
|
})
|
|
return cfg
|
|
}
|