2022-04-05 16:48:51 -05:00
|
|
|
package provisioning
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"testing"
|
|
|
|
|
2022-07-05 11:09:57 -05:00
|
|
|
"github.com/prometheus/alertmanager/config"
|
2022-07-05 12:09:17 -05:00
|
|
|
"github.com/prometheus/alertmanager/timeinterval"
|
2022-04-27 15:15:41 -05:00
|
|
|
"github.com/prometheus/common/model"
|
2022-07-05 12:09:17 -05:00
|
|
|
"github.com/stretchr/testify/mock"
|
2022-04-05 16:48:51 -05:00
|
|
|
"github.com/stretchr/testify/require"
|
2023-01-30 02:55:35 -06:00
|
|
|
|
|
|
|
"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"
|
2024-01-24 16:15:55 -06:00
|
|
|
"github.com/grafana/grafana/pkg/services/ngalert/tests/fakes"
|
2023-01-30 02:55:35 -06:00
|
|
|
"github.com/grafana/grafana/pkg/setting"
|
2022-04-05 16:48:51 -05:00
|
|
|
)
|
|
|
|
|
|
|
|
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)
|
|
|
|
})
|
|
|
|
|
2022-07-05 12:09:17 -05:00
|
|
|
t.Run("error if referenced mute time interval is not existing", func(t *testing.T) {
|
|
|
|
sut := createNotificationPolicyServiceSut()
|
2024-01-05 15:15:18 -06:00
|
|
|
sut.configStore.store = &MockAMConfigStore{}
|
2023-03-28 03:34:35 -05:00
|
|
|
cfg := createTestAlertingConfig()
|
|
|
|
cfg.AlertmanagerConfig.MuteTimeIntervals = []config.MuteTimeInterval{
|
|
|
|
{
|
|
|
|
Name: "not-the-one-we-need",
|
|
|
|
TimeIntervals: []timeinterval.TimeInterval{},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
data, _ := serializeAlertmanagerConfig(*cfg)
|
2024-01-05 15:15:18 -06:00
|
|
|
sut.configStore.store.(*MockAMConfigStore).On("GetLatestAlertmanagerConfiguration", mock.Anything, mock.Anything).
|
2023-03-28 03:34:35 -05:00
|
|
|
Return(&models.AlertConfiguration{AlertmanagerConfiguration: string(data)}, nil)
|
2024-01-05 15:15:18 -06:00
|
|
|
sut.configStore.store.(*MockAMConfigStore).EXPECT().
|
2022-07-05 12:09:17 -05:00
|
|
|
UpdateAlertmanagerConfiguration(mock.Anything, mock.Anything).
|
|
|
|
Return(nil)
|
|
|
|
newRoute := createTestRoutingTree()
|
|
|
|
newRoute.Routes = append(newRoute.Routes, &definitions.Route{
|
2024-01-24 16:15:55 -06:00
|
|
|
Receiver: "slack receiver",
|
2022-07-05 12:09:17 -05:00
|
|
|
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()
|
2024-01-05 15:15:18 -06:00
|
|
|
sut.configStore.store = &MockAMConfigStore{}
|
2023-03-28 03:34:35 -05:00
|
|
|
cfg := createTestAlertingConfig()
|
|
|
|
cfg.AlertmanagerConfig.MuteTimeIntervals = []config.MuteTimeInterval{
|
|
|
|
{
|
|
|
|
Name: "existing",
|
|
|
|
TimeIntervals: []timeinterval.TimeInterval{},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
data, _ := serializeAlertmanagerConfig(*cfg)
|
2024-01-05 15:15:18 -06:00
|
|
|
sut.configStore.store.(*MockAMConfigStore).On("GetLatestAlertmanagerConfiguration", mock.Anything, mock.Anything).
|
2023-03-28 03:34:35 -05:00
|
|
|
Return(&models.AlertConfiguration{AlertmanagerConfiguration: string(data)}, nil)
|
2024-01-05 15:15:18 -06:00
|
|
|
sut.configStore.store.(*MockAMConfigStore).EXPECT().
|
2022-07-05 12:09:17 -05:00
|
|
|
UpdateAlertmanagerConfiguration(mock.Anything, mock.Anything).
|
|
|
|
Return(nil)
|
|
|
|
newRoute := createTestRoutingTree()
|
|
|
|
newRoute.Routes = append(newRoute.Routes, &definitions.Route{
|
2024-01-24 16:15:55 -06:00
|
|
|
Receiver: "slack receiver",
|
2022-07-05 12:09:17 -05:00
|
|
|
MuteTimeIntervals: []string{"existing"},
|
|
|
|
})
|
|
|
|
|
|
|
|
err := sut.UpdatePolicyTree(context.Background(), 1, newRoute, models.ProvenanceNone)
|
|
|
|
require.NoError(t, err)
|
|
|
|
})
|
|
|
|
|
2022-04-05 16:48:51 -05:00
|
|
|
t.Run("service stitches policy tree into org's AM config", func(t *testing.T) {
|
|
|
|
sut := createNotificationPolicyServiceSut()
|
2022-07-05 11:09:57 -05:00
|
|
|
|
2022-04-05 16:48:51 -05:00
|
|
|
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)
|
2024-01-24 16:15:55 -06:00
|
|
|
require.Equal(t, "slack receiver", updated.Receiver)
|
2022-04-05 16:48:51 -05:00
|
|
|
})
|
|
|
|
|
2024-03-26 11:31:59 -05:00
|
|
|
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)
|
|
|
|
})
|
|
|
|
|
2022-07-05 11:09:57 -05:00
|
|
|
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()
|
2024-01-05 15:15:18 -06:00
|
|
|
sut.configStore.store = &MockAMConfigStore{}
|
2023-03-28 03:34:35 -05:00
|
|
|
cfg := createTestAlertingConfig()
|
|
|
|
data, _ := serializeAlertmanagerConfig(*cfg)
|
2024-01-05 15:15:18 -06:00
|
|
|
sut.configStore.store.(*MockAMConfigStore).On("GetLatestAlertmanagerConfiguration", mock.Anything, mock.Anything).
|
2023-03-28 03:34:35 -05:00
|
|
|
Return(&models.AlertConfiguration{AlertmanagerConfiguration: string(data)}, nil)
|
2024-01-05 15:15:18 -06:00
|
|
|
sut.configStore.store.(*MockAMConfigStore).EXPECT().
|
2022-07-05 11:09:57 -05:00
|
|
|
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)
|
|
|
|
})
|
|
|
|
|
2022-04-05 16:48:51 -05:00
|
|
|
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)
|
|
|
|
|
2023-02-27 16:57:15 -06:00
|
|
|
require.Equal(t, models.ProvenanceNone, models.Provenance(tree.Provenance))
|
2022-04-05 16:48:51 -05:00
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("service returns upgraded provenance value", func(t *testing.T) {
|
|
|
|
sut := createNotificationPolicyServiceSut()
|
|
|
|
newRoute := createTestRoutingTree()
|
|
|
|
|
2022-04-13 15:15:55 -05:00
|
|
|
err := sut.UpdatePolicyTree(context.Background(), 1, newRoute, models.ProvenanceAPI)
|
2022-04-05 16:48:51 -05:00
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
updated, err := sut.GetPolicyTree(context.Background(), 1)
|
|
|
|
require.NoError(t, err)
|
2023-02-27 16:57:15 -06:00
|
|
|
require.Equal(t, models.ProvenanceAPI, models.Provenance(updated.Provenance))
|
2022-04-05 16:48:51 -05:00
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("service respects concurrency token when updating", func(t *testing.T) {
|
|
|
|
sut := createNotificationPolicyServiceSut()
|
|
|
|
newRoute := createTestRoutingTree()
|
2023-12-12 06:49:54 -06:00
|
|
|
config, err := sut.GetAMConfigStore().GetLatestAlertmanagerConfiguration(context.Background(), 1)
|
2022-04-05 16:48:51 -05:00
|
|
|
require.NoError(t, err)
|
2023-03-28 03:34:35 -05:00
|
|
|
expectedConcurrencyToken := config.ConfigurationHash
|
2022-04-05 16:48:51 -05:00
|
|
|
|
2022-04-13 15:15:55 -05:00
|
|
|
err = sut.UpdatePolicyTree(context.Background(), 1, newRoute, models.ProvenanceAPI)
|
2022-04-05 16:48:51 -05:00
|
|
|
require.NoError(t, err)
|
|
|
|
|
2024-01-24 16:15:55 -06:00
|
|
|
fake := sut.GetAMConfigStore().(*fakes.FakeAlertmanagerConfigStore)
|
|
|
|
intercepted := fake.LastSaveCommand
|
2022-04-05 16:48:51 -05:00
|
|
|
require.Equal(t, expectedConcurrencyToken, intercepted.FetchedConfigurationHash)
|
|
|
|
})
|
2022-04-27 15:15:41 -05:00
|
|
|
|
|
|
|
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)
|
|
|
|
})
|
2022-07-08 16:23:18 -05:00
|
|
|
|
|
|
|
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)
|
2022-07-11 11:24:43 -05:00
|
|
|
require.Equal(t, []model.LabelName{models.FolderTitleLabel, model.AlertNameLabel}, tree.GroupBy)
|
2022-07-08 16:23:18 -05:00
|
|
|
})
|
2022-09-07 09:28:10 -05:00
|
|
|
|
|
|
|
t.Run("deleting route with missing default receiver restores receiver", func(t *testing.T) {
|
|
|
|
sut := createNotificationPolicyServiceSut()
|
2024-01-05 15:15:18 -06:00
|
|
|
sut.configStore.store = &MockAMConfigStore{}
|
2023-03-28 03:34:35 -05:00
|
|
|
cfg := createTestAlertingConfig()
|
|
|
|
cfg.AlertmanagerConfig.Route = &definitions.Route{
|
2024-01-24 16:15:55 -06:00
|
|
|
Receiver: "slack receiver",
|
2023-03-28 03:34:35 -05:00
|
|
|
}
|
|
|
|
cfg.AlertmanagerConfig.Receivers = []*definitions.PostableApiReceiver{
|
|
|
|
{
|
|
|
|
Receiver: config.Receiver{
|
2024-01-24 16:15:55 -06:00
|
|
|
Name: "slack receiver",
|
2023-03-28 03:34:35 -05:00
|
|
|
},
|
|
|
|
},
|
|
|
|
// No default receiver! Only our custom one.
|
|
|
|
}
|
|
|
|
data, _ := serializeAlertmanagerConfig(*cfg)
|
2024-01-05 15:15:18 -06:00
|
|
|
sut.configStore.store.(*MockAMConfigStore).On("GetLatestAlertmanagerConfiguration", mock.Anything, mock.Anything).
|
2023-03-28 03:34:35 -05:00
|
|
|
Return(&models.AlertConfiguration{AlertmanagerConfiguration: string(data)}, nil)
|
2022-09-07 09:28:10 -05:00
|
|
|
var interceptedSave = models.SaveAlertmanagerConfigurationCmd{}
|
2024-01-05 15:15:18 -06:00
|
|
|
sut.configStore.store.(*MockAMConfigStore).EXPECT().SaveSucceedsIntercept(&interceptedSave)
|
2022-09-07 09:28:10 -05:00
|
|
|
|
|
|
|
tree, err := sut.ResetPolicyTree(context.Background(), 1)
|
|
|
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, "grafana-default-email", tree.Receiver)
|
|
|
|
require.NotEmpty(t, interceptedSave.AlertmanagerConfiguration)
|
2024-01-05 15:15:18 -06:00
|
|
|
// Deserializing with no error asserts that the saved configStore is semantically valid.
|
2022-09-07 09:28:10 -05:00
|
|
|
newCfg, err := deserializeAlertmanagerConfig([]byte(interceptedSave.AlertmanagerConfiguration))
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Len(t, newCfg.AlertmanagerConfig.Receivers, 2)
|
|
|
|
})
|
2022-04-05 16:48:51 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
func createNotificationPolicyServiceSut() *NotificationPolicyService {
|
|
|
|
return &NotificationPolicyService{
|
2024-01-24 16:15:55 -06:00
|
|
|
configStore: &alertmanagerConfigStoreImpl{store: fakes.NewFakeAlertmanagerConfigStore(defaultAlertmanagerConfigJSON)},
|
|
|
|
provenanceStore: fakes.NewFakeProvisioningStore(),
|
2022-04-05 16:48:51 -05:00
|
|
|
xact: newNopTransactionManager(),
|
|
|
|
log: log.NewNopLogger(),
|
2022-07-08 16:23:18 -05:00
|
|
|
settings: setting.UnifiedAlertingSettings{
|
|
|
|
DefaultConfiguration: setting.GetAlertmanagerDefaultConfiguration(),
|
|
|
|
},
|
2022-04-05 16:48:51 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func createTestRoutingTree() definitions.Route {
|
|
|
|
return definitions.Route{
|
2024-01-24 16:15:55 -06:00
|
|
|
Receiver: "slack receiver",
|
2022-04-05 16:48:51 -05:00
|
|
|
}
|
|
|
|
}
|
2022-09-07 09:28:10 -05:00
|
|
|
|
|
|
|
func createTestAlertingConfig() *definitions.PostableUserConfig {
|
|
|
|
cfg, _ := deserializeAlertmanagerConfig([]byte(defaultConfig))
|
|
|
|
cfg.AlertmanagerConfig.Receivers = append(cfg.AlertmanagerConfig.Receivers,
|
|
|
|
&definitions.PostableApiReceiver{
|
|
|
|
Receiver: config.Receiver{
|
|
|
|
// default one from createTestRoutingTree()
|
2024-01-24 16:15:55 -06:00
|
|
|
Name: "slack receiver",
|
2022-09-07 09:28:10 -05:00
|
|
|
},
|
|
|
|
})
|
|
|
|
cfg.AlertmanagerConfig.Receivers = append(cfg.AlertmanagerConfig.Receivers,
|
|
|
|
&definitions.PostableApiReceiver{
|
|
|
|
Receiver: config.Receiver{
|
|
|
|
Name: "existing",
|
|
|
|
},
|
|
|
|
})
|
|
|
|
return cfg
|
|
|
|
}
|