mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Alerting: Update provisioning services that handle Alertmanager configuraiton to access config via storage (#79814)
* extract get and save operations to a alertmanagerConfigStore. this removes duplicated code in service (currently only mute timings) and improves testing * replace generic errors with errutils one with better messages. * update provisioning services to use new store --------- Co-authored-by: Alexander Weaver <weaver.alex.d@gmail.com>
This commit is contained in:
parent
8dc04ea63a
commit
494f36e0bd
@ -3,15 +3,15 @@ package provisioning
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/models"
|
||||
)
|
||||
|
||||
func deserializeAlertmanagerConfig(config []byte) (*definitions.PostableUserConfig, error) {
|
||||
result := definitions.PostableUserConfig{}
|
||||
if err := json.Unmarshal(config, &result); err != nil {
|
||||
return nil, fmt.Errorf("failed to deserialize alertmanager configuration: %w", err)
|
||||
return nil, makeErrBadAlertmanagerConfiguration(err)
|
||||
}
|
||||
return &result, nil
|
||||
}
|
||||
@ -33,7 +33,7 @@ func getLastConfiguration(ctx context.Context, orgID int64, store AMConfigStore)
|
||||
}
|
||||
|
||||
if alertManagerConfig == nil {
|
||||
return nil, fmt.Errorf("no alertmanager configuration present in this org")
|
||||
return nil, ErrNoAlertmanagerConfiguration.Errorf("")
|
||||
}
|
||||
|
||||
concurrencyToken := alertManagerConfig.ConfigurationHash
|
||||
@ -48,3 +48,26 @@ func getLastConfiguration(ctx context.Context, orgID int64, store AMConfigStore)
|
||||
version: alertManagerConfig.ConfigurationVersion,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type alertmanagerConfigStoreImpl struct {
|
||||
store AMConfigStore
|
||||
}
|
||||
|
||||
func (a alertmanagerConfigStoreImpl) Get(ctx context.Context, orgID int64) (*cfgRevision, error) {
|
||||
return getLastConfiguration(ctx, orgID, a.store)
|
||||
}
|
||||
|
||||
func (a alertmanagerConfigStoreImpl) Save(ctx context.Context, revision *cfgRevision, orgID int64) error {
|
||||
serialized, err := serializeAlertmanagerConfig(*revision.cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cmd := models.SaveAlertmanagerConfigurationCmd{
|
||||
AlertmanagerConfiguration: string(serialized),
|
||||
ConfigurationVersion: revision.version,
|
||||
FetchedConfigurationHash: revision.concurrencyToken,
|
||||
Default: false,
|
||||
OrgID: orgID,
|
||||
}
|
||||
return PersistConfig(ctx, a.store, &cmd)
|
||||
}
|
||||
|
127
pkg/services/ngalert/provisioning/config_test.go
Normal file
127
pkg/services/ngalert/provisioning/config_test.go
Normal file
@ -0,0 +1,127 @@
|
||||
package provisioning
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/models"
|
||||
)
|
||||
|
||||
func TestAlertmanagerConfigStoreGet(t *testing.T) {
|
||||
orgID := int64(1)
|
||||
|
||||
t.Run("should read the latest config for giving organization", func(t *testing.T) {
|
||||
storeMock := &MockAMConfigStore{}
|
||||
store := &alertmanagerConfigStoreImpl{store: storeMock}
|
||||
|
||||
expected := models.AlertConfiguration{
|
||||
ID: 1,
|
||||
AlertmanagerConfiguration: defaultConfig,
|
||||
ConfigurationHash: "config-hash-123",
|
||||
ConfigurationVersion: "123",
|
||||
CreatedAt: time.Now().Unix(),
|
||||
Default: false,
|
||||
OrgID: orgID,
|
||||
}
|
||||
|
||||
expectedCfg := definitions.PostableUserConfig{}
|
||||
require.NoError(t, json.Unmarshal([]byte(defaultConfig), &expectedCfg))
|
||||
|
||||
storeMock.EXPECT().GetLatestAlertmanagerConfiguration(mock.Anything, mock.Anything).Return(&expected, nil)
|
||||
|
||||
revision, err := store.Get(context.Background(), orgID)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, expected.ConfigurationVersion, revision.version)
|
||||
require.Equal(t, expected.ConfigurationHash, revision.concurrencyToken)
|
||||
require.Equal(t, expectedCfg, *revision.cfg)
|
||||
|
||||
storeMock.AssertCalled(t, "GetLatestAlertmanagerConfiguration", mock.Anything, orgID)
|
||||
})
|
||||
|
||||
t.Run("propagate errors", func(t *testing.T) {
|
||||
t.Run("when underlying store fails", func(t *testing.T) {
|
||||
storeMock := &MockAMConfigStore{}
|
||||
store := &alertmanagerConfigStoreImpl{store: storeMock}
|
||||
expectedErr := errors.New("test=err")
|
||||
storeMock.EXPECT().GetLatestAlertmanagerConfiguration(mock.Anything, mock.Anything).Return(nil, expectedErr)
|
||||
|
||||
_, err := store.Get(context.Background(), orgID)
|
||||
require.ErrorIs(t, err, expectedErr)
|
||||
})
|
||||
|
||||
t.Run("return ErrNoAlertmanagerConfiguration config does not exist", func(t *testing.T) {
|
||||
storeMock := &MockAMConfigStore{}
|
||||
store := &alertmanagerConfigStoreImpl{store: storeMock}
|
||||
storeMock.EXPECT().GetLatestAlertmanagerConfiguration(mock.Anything, mock.Anything).Return(nil, nil)
|
||||
|
||||
_, err := store.Get(context.Background(), orgID)
|
||||
require.Truef(t, ErrNoAlertmanagerConfiguration.Is(err), "expected ErrNoAlertmanagerConfiguration but got %s", err.Error())
|
||||
})
|
||||
|
||||
t.Run("when config cannot be unmarshalled", func(t *testing.T) {
|
||||
storeMock := &MockAMConfigStore{}
|
||||
store := &alertmanagerConfigStoreImpl{store: storeMock}
|
||||
storeMock.EXPECT().GetLatestAlertmanagerConfiguration(mock.Anything, mock.Anything).Return(&models.AlertConfiguration{
|
||||
AlertmanagerConfiguration: "invalid-json",
|
||||
}, nil)
|
||||
|
||||
_, err := store.Get(context.Background(), orgID)
|
||||
require.Truef(t, ErrBadAlertmanagerConfiguration.Base.Is(err), "expected ErrBadAlertmanagerConfiguration but got %s", err.Error())
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func TestAlertmanagerConfigStoreSave(t *testing.T) {
|
||||
orgID := int64(1)
|
||||
|
||||
cfg := definitions.PostableUserConfig{}
|
||||
require.NoError(t, json.Unmarshal([]byte(defaultConfig), &cfg))
|
||||
expectedCfg, err := serializeAlertmanagerConfig(cfg)
|
||||
require.NoError(t, err)
|
||||
|
||||
revision := cfgRevision{
|
||||
cfg: &cfg,
|
||||
concurrencyToken: "config-hash-123",
|
||||
version: "123",
|
||||
}
|
||||
|
||||
t.Run("should save the config to store", func(t *testing.T) {
|
||||
storeMock := &MockAMConfigStore{}
|
||||
store := &alertmanagerConfigStoreImpl{store: storeMock}
|
||||
|
||||
storeMock.EXPECT().UpdateAlertmanagerConfiguration(mock.Anything, mock.Anything).RunAndReturn(func(ctx context.Context, cmd *models.SaveAlertmanagerConfigurationCmd) error {
|
||||
assert.Equal(t, string(expectedCfg), cmd.AlertmanagerConfiguration)
|
||||
assert.Equal(t, orgID, cmd.OrgID)
|
||||
assert.Equal(t, revision.version, cmd.ConfigurationVersion)
|
||||
assert.Equal(t, false, cmd.Default)
|
||||
assert.Equal(t, revision.concurrencyToken, cmd.FetchedConfigurationHash)
|
||||
return nil
|
||||
})
|
||||
|
||||
err := store.Save(context.Background(), &revision, orgID)
|
||||
require.NoError(t, err)
|
||||
|
||||
storeMock.AssertCalled(t, "UpdateAlertmanagerConfiguration", mock.Anything, mock.Anything)
|
||||
})
|
||||
|
||||
t.Run("propagates errors when underlying storage returns error", func(t *testing.T) {
|
||||
storeMock := &MockAMConfigStore{}
|
||||
store := &alertmanagerConfigStoreImpl{store: storeMock}
|
||||
|
||||
expectedErr := errors.New("test-err")
|
||||
storeMock.EXPECT().UpdateAlertmanagerConfiguration(mock.Anything, mock.Anything).Return(expectedErr)
|
||||
|
||||
err := store.Save(context.Background(), &revision, orgID)
|
||||
|
||||
require.ErrorIs(t, err, expectedErr)
|
||||
})
|
||||
}
|
@ -3,7 +3,6 @@ package provisioning
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sort"
|
||||
@ -23,7 +22,7 @@ import (
|
||||
)
|
||||
|
||||
type ContactPointService struct {
|
||||
amStore AMConfigStore
|
||||
configStore *alertmanagerConfigStoreImpl
|
||||
encryptionService secrets.Service
|
||||
provenanceStore ProvisioningStore
|
||||
xact TransactionManager
|
||||
@ -34,7 +33,9 @@ type ContactPointService struct {
|
||||
func NewContactPointService(store AMConfigStore, encryptionService secrets.Service,
|
||||
provenanceStore ProvisioningStore, xact TransactionManager, log log.Logger, ac accesscontrol.AccessControl) *ContactPointService {
|
||||
return &ContactPointService{
|
||||
amStore: store,
|
||||
configStore: &alertmanagerConfigStoreImpl{
|
||||
store: store,
|
||||
},
|
||||
encryptionService: encryptionService,
|
||||
provenanceStore: provenanceStore,
|
||||
xact: xact,
|
||||
@ -68,7 +69,7 @@ func (ecp *ContactPointService) GetContactPoints(ctx context.Context, q ContactP
|
||||
if q.Decrypt && !ecp.canDecryptSecrets(ctx, u) {
|
||||
return nil, fmt.Errorf("%w: user requires Admin role or alert.provisioning.secrets:read permission to view decrypted secure settings", ErrPermissionDenied)
|
||||
}
|
||||
revision, err := getLastConfiguration(ctx, q.OrgID, ecp.amStore)
|
||||
revision, err := ecp.configStore.Get(ctx, q.OrgID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -108,7 +109,7 @@ func (ecp *ContactPointService) GetContactPoints(ctx context.Context, q ContactP
|
||||
// getContactPointDecrypted is an internal-only function that gets full contact point info, included encrypted fields.
|
||||
// nil is returned if no matching contact point exists.
|
||||
func (ecp *ContactPointService) getContactPointDecrypted(ctx context.Context, orgID int64, uid string) (apimodels.EmbeddedContactPoint, error) {
|
||||
revision, err := getLastConfiguration(ctx, orgID, ecp.amStore)
|
||||
revision, err := ecp.configStore.Get(ctx, orgID)
|
||||
if err != nil {
|
||||
return apimodels.EmbeddedContactPoint{}, err
|
||||
}
|
||||
@ -118,7 +119,7 @@ func (ecp *ContactPointService) getContactPointDecrypted(ctx context.Context, or
|
||||
}
|
||||
embeddedContactPoint, err := PostableGrafanaReceiverToEmbeddedContactPoint(
|
||||
receiver,
|
||||
models.ProvenanceNone,
|
||||
models.ProvenanceNone, // TODO should be correct provenance?
|
||||
ecp.decryptValueOrRedacted(true, receiver.UID),
|
||||
)
|
||||
if err != nil {
|
||||
@ -135,7 +136,7 @@ func (ecp *ContactPointService) CreateContactPoint(ctx context.Context, orgID in
|
||||
return apimodels.EmbeddedContactPoint{}, fmt.Errorf("%w: %s", ErrValidation, err.Error())
|
||||
}
|
||||
|
||||
revision, err := getLastConfiguration(ctx, orgID, ecp.amStore)
|
||||
revision, err := ecp.configStore.Get(ctx, orgID)
|
||||
if err != nil {
|
||||
return apimodels.EmbeddedContactPoint{}, err
|
||||
}
|
||||
@ -201,28 +202,11 @@ func (ecp *ContactPointService) CreateContactPoint(ctx context.Context, orgID in
|
||||
})
|
||||
}
|
||||
|
||||
data, err := json.Marshal(revision.cfg)
|
||||
if err != nil {
|
||||
return apimodels.EmbeddedContactPoint{}, err
|
||||
}
|
||||
|
||||
err = ecp.xact.InTransaction(ctx, func(ctx context.Context) error {
|
||||
err = PersistConfig(ctx, ecp.amStore, &models.SaveAlertmanagerConfigurationCmd{
|
||||
AlertmanagerConfiguration: string(data),
|
||||
FetchedConfigurationHash: revision.concurrencyToken,
|
||||
ConfigurationVersion: revision.version,
|
||||
Default: false,
|
||||
OrgID: orgID,
|
||||
})
|
||||
if err != nil {
|
||||
if err := ecp.configStore.Save(ctx, revision, orgID); err != nil {
|
||||
return err
|
||||
}
|
||||
err = ecp.provenanceStore.SetProvenance(ctx, &contactPoint, orgID, provenance)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
contactPoint.Provenance = string(provenance)
|
||||
return nil
|
||||
return ecp.provenanceStore.SetProvenance(ctx, &contactPoint, orgID, provenance)
|
||||
})
|
||||
if err != nil {
|
||||
return apimodels.EmbeddedContactPoint{}, err
|
||||
@ -292,7 +276,7 @@ func (ecp *ContactPointService) UpdateContactPoint(ctx context.Context, orgID in
|
||||
SecureSettings: extractedSecrets,
|
||||
}
|
||||
// save to store
|
||||
revision, err := getLastConfiguration(ctx, orgID, ecp.amStore)
|
||||
revision, err := ecp.configStore.Get(ctx, orgID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -302,32 +286,20 @@ func (ecp *ContactPointService) UpdateContactPoint(ctx context.Context, orgID in
|
||||
return fmt.Errorf("contact point with uid '%s' not found", mergedReceiver.UID)
|
||||
}
|
||||
|
||||
data, err := json.Marshal(revision.cfg)
|
||||
err = ecp.xact.InTransaction(ctx, func(ctx context.Context) error {
|
||||
if err := ecp.configStore.Save(ctx, revision, orgID); err != nil {
|
||||
return err
|
||||
}
|
||||
return ecp.provenanceStore.SetProvenance(ctx, &contactPoint, orgID, provenance)
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return ecp.xact.InTransaction(ctx, func(ctx context.Context) error {
|
||||
err = PersistConfig(ctx, ecp.amStore, &models.SaveAlertmanagerConfigurationCmd{
|
||||
AlertmanagerConfiguration: string(data),
|
||||
FetchedConfigurationHash: revision.concurrencyToken,
|
||||
ConfigurationVersion: revision.version,
|
||||
Default: false,
|
||||
OrgID: orgID,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = ecp.provenanceStore.SetProvenance(ctx, &contactPoint, orgID, provenance)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
contactPoint.Provenance = string(provenance)
|
||||
return nil
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ecp *ContactPointService) DeleteContactPoint(ctx context.Context, orgID int64, uid string) error {
|
||||
revision, err := getLastConfiguration(ctx, orgID, ecp.amStore)
|
||||
revision, err := ecp.configStore.Get(ctx, orgID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -355,25 +327,15 @@ func (ecp *ContactPointService) DeleteContactPoint(ctx context.Context, orgID in
|
||||
if fullRemoval && isContactPointInUse(name, []*apimodels.Route{revision.cfg.AlertmanagerConfig.Route}) {
|
||||
return fmt.Errorf("contact point '%s' is currently used by a notification policy", name)
|
||||
}
|
||||
data, err := json.Marshal(revision.cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return ecp.xact.InTransaction(ctx, func(ctx context.Context) error {
|
||||
if err := ecp.configStore.Save(ctx, revision, orgID); err != nil {
|
||||
return err
|
||||
}
|
||||
target := &apimodels.EmbeddedContactPoint{
|
||||
UID: uid,
|
||||
}
|
||||
err := ecp.provenanceStore.DeleteProvenance(ctx, target, orgID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return PersistConfig(ctx, ecp.amStore, &models.SaveAlertmanagerConfigurationCmd{
|
||||
AlertmanagerConfiguration: string(data),
|
||||
FetchedConfigurationHash: revision.concurrencyToken,
|
||||
ConfigurationVersion: revision.version,
|
||||
Default: false,
|
||||
OrgID: orgID,
|
||||
})
|
||||
return ecp.provenanceStore.DeleteProvenance(ctx, target, orgID)
|
||||
})
|
||||
}
|
||||
|
||||
@ -423,8 +385,8 @@ func (ecp *ContactPointService) encryptValue(value string) (string, error) {
|
||||
return base64.StdEncoding.EncodeToString(encryptedData), nil
|
||||
}
|
||||
|
||||
// stitchReceiver modifies a receiver, target, in an alertmanager config. It modifies the given config in-place.
|
||||
// Returns true if the config was altered in any way, and false otherwise.
|
||||
// stitchReceiver modifies a receiver, target, in an alertmanager configStore. It modifies the given configStore in-place.
|
||||
// Returns true if the configStore was altered in any way, and false otherwise.
|
||||
func stitchReceiver(cfg *apimodels.PostableUserConfig, target *apimodels.PostableGrafanaReceiver) bool {
|
||||
// Algorithm to fix up receivers. Receivers are very complex and depend heavily on internal consistency.
|
||||
// All receivers in a given receiver group have the same name. We must maintain this across renames.
|
||||
|
@ -239,14 +239,14 @@ func TestContactPointService(t *testing.T) {
|
||||
t.Run("service respects concurrency token when updating", func(t *testing.T) {
|
||||
sut := createContactPointServiceSut(t, secretsService)
|
||||
newCp := createTestContactPoint()
|
||||
config, err := sut.amStore.GetLatestAlertmanagerConfiguration(context.Background(), 1)
|
||||
config, err := sut.configStore.store.GetLatestAlertmanagerConfiguration(context.Background(), 1)
|
||||
require.NoError(t, err)
|
||||
expectedConcurrencyToken := config.ConfigurationHash
|
||||
|
||||
_, err = sut.CreateContactPoint(context.Background(), 1, newCp, models.ProvenanceAPI)
|
||||
require.NoError(t, err)
|
||||
|
||||
fake := sut.amStore.(*fakeAMConfigStore)
|
||||
fake := sut.configStore.store.(*fakeAMConfigStore)
|
||||
intercepted := fake.lastSaveCommand
|
||||
require.Equal(t, expectedConcurrencyToken, intercepted.FetchedConfigurationHash)
|
||||
})
|
||||
@ -349,7 +349,7 @@ func createContactPointServiceSut(t *testing.T, secretService secrets.Service) *
|
||||
require.NoError(t, err)
|
||||
|
||||
return &ContactPointService{
|
||||
amStore: newFakeAMConfigStore(string(raw)),
|
||||
configStore: &alertmanagerConfigStoreImpl{store: newFakeAMConfigStore(string(raw))},
|
||||
provenanceStore: NewFakeProvisioningStore(),
|
||||
xact: newNopTransactionManager(),
|
||||
encryptionService: secretService,
|
||||
|
@ -3,8 +3,26 @@ package provisioning
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/grafana/grafana/pkg/util/errutil"
|
||||
)
|
||||
|
||||
var ErrValidation = fmt.Errorf("invalid object specification")
|
||||
var ErrNotFound = fmt.Errorf("object not found")
|
||||
var ErrPermissionDenied = errors.New("permission denied")
|
||||
|
||||
var (
|
||||
ErrNoAlertmanagerConfiguration = errutil.Internal("alerting.notification.configMissing", errutil.WithPublicMessage("No alertmanager configuration present in this organization"))
|
||||
ErrBadAlertmanagerConfiguration = errutil.Internal("alerting.notification.configCorrupted").MustTemplate("Failed to unmarshal the Alertmanager configuration", errutil.WithPublic("Current Alertmanager configuration in the storage is corrupted. Reset the configuration or rollback to a recent valid one."))
|
||||
)
|
||||
|
||||
func makeErrBadAlertmanagerConfiguration(err error) error {
|
||||
data := errutil.TemplateData{
|
||||
Public: map[string]interface{}{
|
||||
"Error": err.Error(),
|
||||
},
|
||||
Error: err,
|
||||
}
|
||||
|
||||
return ErrBadAlertmanagerConfiguration.Build(data)
|
||||
}
|
||||
|
@ -12,24 +12,24 @@ import (
|
||||
)
|
||||
|
||||
type MuteTimingService struct {
|
||||
config AMConfigStore
|
||||
prov ProvisioningStore
|
||||
xact TransactionManager
|
||||
log log.Logger
|
||||
configStore *alertmanagerConfigStoreImpl
|
||||
provenanceStore ProvisioningStore
|
||||
xact TransactionManager
|
||||
log log.Logger
|
||||
}
|
||||
|
||||
func NewMuteTimingService(config AMConfigStore, prov ProvisioningStore, xact TransactionManager, log log.Logger) *MuteTimingService {
|
||||
return &MuteTimingService{
|
||||
config: config,
|
||||
prov: prov,
|
||||
xact: xact,
|
||||
log: log,
|
||||
configStore: &alertmanagerConfigStoreImpl{store: config},
|
||||
provenanceStore: prov,
|
||||
xact: xact,
|
||||
log: log,
|
||||
}
|
||||
}
|
||||
|
||||
// GetMuteTimings returns a slice of all mute timings within the specified org.
|
||||
func (svc *MuteTimingService) GetMuteTimings(ctx context.Context, orgID int64) ([]definitions.MuteTimeInterval, error) {
|
||||
rev, err := getLastConfiguration(ctx, orgID, svc.config)
|
||||
rev, err := svc.configStore.Get(ctx, orgID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -51,7 +51,7 @@ func (svc *MuteTimingService) CreateMuteTiming(ctx context.Context, mt definitio
|
||||
return nil, fmt.Errorf("%w: %s", ErrValidation, err.Error())
|
||||
}
|
||||
|
||||
revision, err := getLastConfiguration(ctx, orgID, svc.config)
|
||||
revision, err := svc.configStore.Get(ctx, orgID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -66,32 +66,15 @@ func (svc *MuteTimingService) CreateMuteTiming(ctx context.Context, mt definitio
|
||||
}
|
||||
revision.cfg.AlertmanagerConfig.MuteTimeIntervals = append(revision.cfg.AlertmanagerConfig.MuteTimeIntervals, mt.MuteTimeInterval)
|
||||
|
||||
serialized, err := serializeAlertmanagerConfig(*revision.cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cmd := models.SaveAlertmanagerConfigurationCmd{
|
||||
AlertmanagerConfiguration: string(serialized),
|
||||
ConfigurationVersion: revision.version,
|
||||
FetchedConfigurationHash: revision.concurrencyToken,
|
||||
Default: false,
|
||||
OrgID: orgID,
|
||||
}
|
||||
err = svc.xact.InTransaction(ctx, func(ctx context.Context) error {
|
||||
err = PersistConfig(ctx, svc.config, &cmd)
|
||||
if err != nil {
|
||||
if err := svc.configStore.Save(ctx, revision, orgID); err != nil {
|
||||
return err
|
||||
}
|
||||
err = svc.prov.SetProvenance(ctx, &mt, orgID, models.Provenance(mt.Provenance))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
return svc.provenanceStore.SetProvenance(ctx, &mt, orgID, models.Provenance(mt.Provenance))
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &mt, nil
|
||||
}
|
||||
|
||||
@ -101,7 +84,7 @@ func (svc *MuteTimingService) UpdateMuteTiming(ctx context.Context, mt definitio
|
||||
return nil, fmt.Errorf("%w: %s", ErrValidation, err.Error())
|
||||
}
|
||||
|
||||
revision, err := getLastConfiguration(ctx, orgID, svc.config)
|
||||
revision, err := svc.configStore.Get(ctx, orgID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -121,38 +104,21 @@ func (svc *MuteTimingService) UpdateMuteTiming(ctx context.Context, mt definitio
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
serialized, err := serializeAlertmanagerConfig(*revision.cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cmd := models.SaveAlertmanagerConfigurationCmd{
|
||||
AlertmanagerConfiguration: string(serialized),
|
||||
ConfigurationVersion: revision.version,
|
||||
FetchedConfigurationHash: revision.concurrencyToken,
|
||||
Default: false,
|
||||
OrgID: orgID,
|
||||
}
|
||||
err = svc.xact.InTransaction(ctx, func(ctx context.Context) error {
|
||||
err = PersistConfig(ctx, svc.config, &cmd)
|
||||
if err != nil {
|
||||
if err := svc.configStore.Save(ctx, revision, orgID); err != nil {
|
||||
return err
|
||||
}
|
||||
err = svc.prov.SetProvenance(ctx, &mt, orgID, models.Provenance(mt.Provenance))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
return svc.provenanceStore.SetProvenance(ctx, &mt, orgID, models.Provenance(mt.Provenance))
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &mt, err
|
||||
}
|
||||
|
||||
// DeleteMuteTiming deletes the mute timing with the given name in the given org. If the mute timing does not exist, no error is returned.
|
||||
func (svc *MuteTimingService) DeleteMuteTiming(ctx context.Context, name string, orgID int64) error {
|
||||
revision, err := getLastConfiguration(ctx, orgID, svc.config)
|
||||
revision, err := svc.configStore.Get(ctx, orgID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -170,28 +136,12 @@ func (svc *MuteTimingService) DeleteMuteTiming(ctx context.Context, name string,
|
||||
}
|
||||
}
|
||||
|
||||
serialized, err := serializeAlertmanagerConfig(*revision.cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cmd := models.SaveAlertmanagerConfigurationCmd{
|
||||
AlertmanagerConfiguration: string(serialized),
|
||||
ConfigurationVersion: revision.version,
|
||||
FetchedConfigurationHash: revision.concurrencyToken,
|
||||
Default: false,
|
||||
OrgID: orgID,
|
||||
}
|
||||
return svc.xact.InTransaction(ctx, func(ctx context.Context) error {
|
||||
err = PersistConfig(ctx, svc.config, &cmd)
|
||||
if err != nil {
|
||||
if err := svc.configStore.Save(ctx, revision, orgID); err != nil {
|
||||
return err
|
||||
}
|
||||
target := definitions.MuteTimeInterval{MuteTimeInterval: config.MuteTimeInterval{Name: name}}
|
||||
err := svc.prov.DeleteProvenance(ctx, &target, orgID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
return svc.provenanceStore.DeleteProvenance(ctx, &target, orgID)
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -17,7 +17,7 @@ import (
|
||||
func TestMuteTimingService(t *testing.T) {
|
||||
t.Run("service returns timings from config file", func(t *testing.T) {
|
||||
sut := createMuteTimingSvcSut()
|
||||
sut.config.(*MockAMConfigStore).EXPECT().
|
||||
sut.configStore.store.(*MockAMConfigStore).EXPECT().
|
||||
GetsConfig(models.AlertConfiguration{
|
||||
AlertmanagerConfiguration: configWithMuteTimings,
|
||||
})
|
||||
@ -31,7 +31,7 @@ func TestMuteTimingService(t *testing.T) {
|
||||
|
||||
t.Run("service returns empty list when config file contains no mute timings", func(t *testing.T) {
|
||||
sut := createMuteTimingSvcSut()
|
||||
sut.config.(*MockAMConfigStore).EXPECT().
|
||||
sut.configStore.store.(*MockAMConfigStore).EXPECT().
|
||||
GetsConfig(models.AlertConfiguration{
|
||||
AlertmanagerConfiguration: defaultConfig,
|
||||
})
|
||||
@ -45,7 +45,7 @@ func TestMuteTimingService(t *testing.T) {
|
||||
t.Run("service propagates errors", func(t *testing.T) {
|
||||
t.Run("when unable to read config", func(t *testing.T) {
|
||||
sut := createMuteTimingSvcSut()
|
||||
sut.config.(*MockAMConfigStore).EXPECT().
|
||||
sut.configStore.store.(*MockAMConfigStore).EXPECT().
|
||||
GetLatestAlertmanagerConfiguration(mock.Anything, mock.Anything).
|
||||
Return(nil, fmt.Errorf("failed"))
|
||||
|
||||
@ -56,25 +56,25 @@ func TestMuteTimingService(t *testing.T) {
|
||||
|
||||
t.Run("when config is invalid", func(t *testing.T) {
|
||||
sut := createMuteTimingSvcSut()
|
||||
sut.config.(*MockAMConfigStore).EXPECT().
|
||||
sut.configStore.store.(*MockAMConfigStore).EXPECT().
|
||||
GetsConfig(models.AlertConfiguration{
|
||||
AlertmanagerConfiguration: brokenConfig,
|
||||
})
|
||||
|
||||
_, err := sut.GetMuteTimings(context.Background(), 1)
|
||||
|
||||
require.ErrorContains(t, err, "failed to deserialize")
|
||||
require.Truef(t, ErrBadAlertmanagerConfiguration.Base.Is(err), "expected ErrBadAlertmanagerConfiguration but got %s", err.Error())
|
||||
})
|
||||
|
||||
t.Run("when no AM config in current org", func(t *testing.T) {
|
||||
sut := createMuteTimingSvcSut()
|
||||
sut.config.(*MockAMConfigStore).EXPECT().
|
||||
sut.configStore.store.(*MockAMConfigStore).EXPECT().
|
||||
GetLatestAlertmanagerConfiguration(mock.Anything, mock.Anything).
|
||||
Return(nil, nil)
|
||||
|
||||
_, err := sut.GetMuteTimings(context.Background(), 1)
|
||||
|
||||
require.ErrorContains(t, err, "no alertmanager configuration")
|
||||
require.Truef(t, ErrNoAlertmanagerConfiguration.Is(err), "expected ErrNoAlertmanagerConfiguration but got %s", err.Error())
|
||||
})
|
||||
})
|
||||
|
||||
@ -96,7 +96,7 @@ func TestMuteTimingService(t *testing.T) {
|
||||
t.Run("when unable to read config", func(t *testing.T) {
|
||||
sut := createMuteTimingSvcSut()
|
||||
timing := createMuteTiming()
|
||||
sut.config.(*MockAMConfigStore).EXPECT().
|
||||
sut.configStore.store.(*MockAMConfigStore).EXPECT().
|
||||
GetLatestAlertmanagerConfiguration(mock.Anything, mock.Anything).
|
||||
Return(nil, fmt.Errorf("failed"))
|
||||
|
||||
@ -108,37 +108,37 @@ func TestMuteTimingService(t *testing.T) {
|
||||
t.Run("when config is invalid", func(t *testing.T) {
|
||||
sut := createMuteTimingSvcSut()
|
||||
timing := createMuteTiming()
|
||||
sut.config.(*MockAMConfigStore).EXPECT().
|
||||
sut.configStore.store.(*MockAMConfigStore).EXPECT().
|
||||
GetsConfig(models.AlertConfiguration{
|
||||
AlertmanagerConfiguration: brokenConfig,
|
||||
})
|
||||
|
||||
_, err := sut.CreateMuteTiming(context.Background(), timing, 1)
|
||||
|
||||
require.ErrorContains(t, err, "failed to deserialize")
|
||||
require.Truef(t, ErrBadAlertmanagerConfiguration.Base.Is(err), "expected ErrBadAlertmanagerConfiguration but got %s", err.Error())
|
||||
})
|
||||
|
||||
t.Run("when no AM config in current org", func(t *testing.T) {
|
||||
sut := createMuteTimingSvcSut()
|
||||
timing := createMuteTiming()
|
||||
sut.config.(*MockAMConfigStore).EXPECT().
|
||||
sut.configStore.store.(*MockAMConfigStore).EXPECT().
|
||||
GetLatestAlertmanagerConfiguration(mock.Anything, mock.Anything).
|
||||
Return(nil, nil)
|
||||
|
||||
_, err := sut.CreateMuteTiming(context.Background(), timing, 1)
|
||||
|
||||
require.ErrorContains(t, err, "no alertmanager configuration")
|
||||
require.Truef(t, ErrNoAlertmanagerConfiguration.Is(err), "expected ErrNoAlertmanagerConfiguration but got %s", err.Error())
|
||||
})
|
||||
|
||||
t.Run("when provenance fails to save", func(t *testing.T) {
|
||||
sut := createMuteTimingSvcSut()
|
||||
timing := createMuteTiming()
|
||||
sut.config.(*MockAMConfigStore).EXPECT().
|
||||
sut.configStore.store.(*MockAMConfigStore).EXPECT().
|
||||
GetsConfig(models.AlertConfiguration{
|
||||
AlertmanagerConfiguration: configWithMuteTimings,
|
||||
})
|
||||
sut.config.(*MockAMConfigStore).EXPECT().SaveSucceeds()
|
||||
sut.prov.(*MockProvisioningStore).EXPECT().
|
||||
sut.configStore.store.(*MockAMConfigStore).EXPECT().SaveSucceeds()
|
||||
sut.provenanceStore.(*MockProvisioningStore).EXPECT().
|
||||
SetProvenance(mock.Anything, mock.Anything, mock.Anything, mock.Anything).
|
||||
Return(fmt.Errorf("failed to save provenance"))
|
||||
|
||||
@ -150,14 +150,14 @@ func TestMuteTimingService(t *testing.T) {
|
||||
t.Run("when AM config fails to save", func(t *testing.T) {
|
||||
sut := createMuteTimingSvcSut()
|
||||
timing := createMuteTiming()
|
||||
sut.config.(*MockAMConfigStore).EXPECT().
|
||||
sut.configStore.store.(*MockAMConfigStore).EXPECT().
|
||||
GetsConfig(models.AlertConfiguration{
|
||||
AlertmanagerConfiguration: configWithMuteTimings,
|
||||
})
|
||||
sut.config.(*MockAMConfigStore).EXPECT().
|
||||
sut.configStore.store.(*MockAMConfigStore).EXPECT().
|
||||
UpdateAlertmanagerConfiguration(mock.Anything, mock.Anything).
|
||||
Return(fmt.Errorf("failed to save config"))
|
||||
sut.prov.(*MockProvisioningStore).EXPECT().SaveSucceeds()
|
||||
Return(fmt.Errorf("failed to save configStore"))
|
||||
sut.provenanceStore.(*MockProvisioningStore).EXPECT().SaveSucceeds()
|
||||
|
||||
_, err := sut.CreateMuteTiming(context.Background(), timing, 1)
|
||||
|
||||
@ -184,12 +184,12 @@ func TestMuteTimingService(t *testing.T) {
|
||||
sut := createMuteTimingSvcSut()
|
||||
timing := createMuteTiming()
|
||||
timing.Name = "does not exist"
|
||||
sut.config.(*MockAMConfigStore).EXPECT().
|
||||
sut.configStore.store.(*MockAMConfigStore).EXPECT().
|
||||
GetsConfig(models.AlertConfiguration{
|
||||
AlertmanagerConfiguration: configWithMuteTimings,
|
||||
})
|
||||
sut.config.(*MockAMConfigStore).EXPECT().SaveSucceeds()
|
||||
sut.prov.(*MockProvisioningStore).EXPECT().SaveSucceeds()
|
||||
sut.configStore.store.(*MockAMConfigStore).EXPECT().SaveSucceeds()
|
||||
sut.provenanceStore.(*MockProvisioningStore).EXPECT().SaveSucceeds()
|
||||
|
||||
updated, err := sut.UpdateMuteTiming(context.Background(), timing, 1)
|
||||
|
||||
@ -202,7 +202,7 @@ func TestMuteTimingService(t *testing.T) {
|
||||
sut := createMuteTimingSvcSut()
|
||||
timing := createMuteTiming()
|
||||
timing.Name = "asdf"
|
||||
sut.config.(*MockAMConfigStore).EXPECT().
|
||||
sut.configStore.store.(*MockAMConfigStore).EXPECT().
|
||||
GetLatestAlertmanagerConfiguration(mock.Anything, mock.Anything).
|
||||
Return(nil, fmt.Errorf("failed"))
|
||||
|
||||
@ -215,39 +215,39 @@ func TestMuteTimingService(t *testing.T) {
|
||||
sut := createMuteTimingSvcSut()
|
||||
timing := createMuteTiming()
|
||||
timing.Name = "asdf"
|
||||
sut.config.(*MockAMConfigStore).EXPECT().
|
||||
sut.configStore.store.(*MockAMConfigStore).EXPECT().
|
||||
GetsConfig(models.AlertConfiguration{
|
||||
AlertmanagerConfiguration: brokenConfig,
|
||||
})
|
||||
|
||||
_, err := sut.UpdateMuteTiming(context.Background(), timing, 1)
|
||||
|
||||
require.ErrorContains(t, err, "failed to deserialize")
|
||||
require.Truef(t, ErrBadAlertmanagerConfiguration.Base.Is(err), "expected ErrBadAlertmanagerConfiguration but got %s", err.Error())
|
||||
})
|
||||
|
||||
t.Run("when no AM config in current org", func(t *testing.T) {
|
||||
sut := createMuteTimingSvcSut()
|
||||
timing := createMuteTiming()
|
||||
timing.Name = "asdf"
|
||||
sut.config.(*MockAMConfigStore).EXPECT().
|
||||
sut.configStore.store.(*MockAMConfigStore).EXPECT().
|
||||
GetLatestAlertmanagerConfiguration(mock.Anything, mock.Anything).
|
||||
Return(nil, nil)
|
||||
|
||||
_, err := sut.UpdateMuteTiming(context.Background(), timing, 1)
|
||||
|
||||
require.ErrorContains(t, err, "no alertmanager configuration")
|
||||
require.Truef(t, ErrNoAlertmanagerConfiguration.Is(err), "expected ErrNoAlertmanagerConfiguration but got %s", err.Error())
|
||||
})
|
||||
|
||||
t.Run("when provenance fails to save", func(t *testing.T) {
|
||||
sut := createMuteTimingSvcSut()
|
||||
timing := createMuteTiming()
|
||||
timing.Name = "asdf"
|
||||
sut.config.(*MockAMConfigStore).EXPECT().
|
||||
sut.configStore.store.(*MockAMConfigStore).EXPECT().
|
||||
GetsConfig(models.AlertConfiguration{
|
||||
AlertmanagerConfiguration: configWithMuteTimings,
|
||||
})
|
||||
sut.config.(*MockAMConfigStore).EXPECT().SaveSucceeds()
|
||||
sut.prov.(*MockProvisioningStore).EXPECT().
|
||||
sut.configStore.store.(*MockAMConfigStore).EXPECT().SaveSucceeds()
|
||||
sut.provenanceStore.(*MockProvisioningStore).EXPECT().
|
||||
SetProvenance(mock.Anything, mock.Anything, mock.Anything, mock.Anything).
|
||||
Return(fmt.Errorf("failed to save provenance"))
|
||||
|
||||
@ -260,14 +260,14 @@ func TestMuteTimingService(t *testing.T) {
|
||||
sut := createMuteTimingSvcSut()
|
||||
timing := createMuteTiming()
|
||||
timing.Name = "asdf"
|
||||
sut.config.(*MockAMConfigStore).EXPECT().
|
||||
sut.configStore.store.(*MockAMConfigStore).EXPECT().
|
||||
GetsConfig(models.AlertConfiguration{
|
||||
AlertmanagerConfiguration: configWithMuteTimings,
|
||||
})
|
||||
sut.config.(*MockAMConfigStore).EXPECT().
|
||||
sut.configStore.store.(*MockAMConfigStore).EXPECT().
|
||||
UpdateAlertmanagerConfiguration(mock.Anything, mock.Anything).
|
||||
Return(fmt.Errorf("failed to save config"))
|
||||
sut.prov.(*MockProvisioningStore).EXPECT().SaveSucceeds()
|
||||
sut.provenanceStore.(*MockProvisioningStore).EXPECT().SaveSucceeds()
|
||||
|
||||
_, err := sut.UpdateMuteTiming(context.Background(), timing, 1)
|
||||
|
||||
@ -279,12 +279,12 @@ func TestMuteTimingService(t *testing.T) {
|
||||
t.Run("deleting mute timings", func(t *testing.T) {
|
||||
t.Run("returns nil if timing does not exist", func(t *testing.T) {
|
||||
sut := createMuteTimingSvcSut()
|
||||
sut.config.(*MockAMConfigStore).EXPECT().
|
||||
sut.configStore.store.(*MockAMConfigStore).EXPECT().
|
||||
GetsConfig(models.AlertConfiguration{
|
||||
AlertmanagerConfiguration: configWithMuteTimings,
|
||||
})
|
||||
sut.config.(*MockAMConfigStore).EXPECT().SaveSucceeds()
|
||||
sut.prov.(*MockProvisioningStore).EXPECT().SaveSucceeds()
|
||||
sut.configStore.store.(*MockAMConfigStore).EXPECT().SaveSucceeds()
|
||||
sut.provenanceStore.(*MockProvisioningStore).EXPECT().SaveSucceeds()
|
||||
|
||||
err := sut.DeleteMuteTiming(context.Background(), "does not exist", 1)
|
||||
|
||||
@ -294,7 +294,7 @@ func TestMuteTimingService(t *testing.T) {
|
||||
t.Run("propagates errors", func(t *testing.T) {
|
||||
t.Run("when unable to read config", func(t *testing.T) {
|
||||
sut := createMuteTimingSvcSut()
|
||||
sut.config.(*MockAMConfigStore).EXPECT().
|
||||
sut.configStore.store.(*MockAMConfigStore).EXPECT().
|
||||
GetLatestAlertmanagerConfiguration(mock.Anything, mock.Anything).
|
||||
Return(nil, fmt.Errorf("failed"))
|
||||
|
||||
@ -305,35 +305,35 @@ func TestMuteTimingService(t *testing.T) {
|
||||
|
||||
t.Run("when config is invalid", func(t *testing.T) {
|
||||
sut := createMuteTimingSvcSut()
|
||||
sut.config.(*MockAMConfigStore).EXPECT().
|
||||
sut.configStore.store.(*MockAMConfigStore).EXPECT().
|
||||
GetsConfig(models.AlertConfiguration{
|
||||
AlertmanagerConfiguration: brokenConfig,
|
||||
})
|
||||
|
||||
err := sut.DeleteMuteTiming(context.Background(), "asdf", 1)
|
||||
|
||||
require.ErrorContains(t, err, "failed to deserialize")
|
||||
require.Truef(t, ErrBadAlertmanagerConfiguration.Base.Is(err), "expected ErrBadAlertmanagerConfiguration but got %s", err.Error())
|
||||
})
|
||||
|
||||
t.Run("when no AM config in current org", func(t *testing.T) {
|
||||
sut := createMuteTimingSvcSut()
|
||||
sut.config.(*MockAMConfigStore).EXPECT().
|
||||
sut.configStore.store.(*MockAMConfigStore).EXPECT().
|
||||
GetLatestAlertmanagerConfiguration(mock.Anything, mock.Anything).
|
||||
Return(nil, nil)
|
||||
|
||||
err := sut.DeleteMuteTiming(context.Background(), "asdf", 1)
|
||||
|
||||
require.ErrorContains(t, err, "no alertmanager configuration")
|
||||
require.Truef(t, ErrNoAlertmanagerConfiguration.Is(err), "expected ErrNoAlertmanagerConfiguration but got %s", err.Error())
|
||||
})
|
||||
|
||||
t.Run("when provenance fails to save", func(t *testing.T) {
|
||||
sut := createMuteTimingSvcSut()
|
||||
sut.config.(*MockAMConfigStore).EXPECT().
|
||||
sut.configStore.store.(*MockAMConfigStore).EXPECT().
|
||||
GetsConfig(models.AlertConfiguration{
|
||||
AlertmanagerConfiguration: configWithMuteTimings,
|
||||
})
|
||||
sut.config.(*MockAMConfigStore).EXPECT().SaveSucceeds()
|
||||
sut.prov.(*MockProvisioningStore).EXPECT().
|
||||
sut.configStore.store.(*MockAMConfigStore).EXPECT().SaveSucceeds()
|
||||
sut.provenanceStore.(*MockProvisioningStore).EXPECT().
|
||||
DeleteProvenance(mock.Anything, mock.Anything, mock.Anything).
|
||||
Return(fmt.Errorf("failed to save provenance"))
|
||||
|
||||
@ -344,14 +344,14 @@ func TestMuteTimingService(t *testing.T) {
|
||||
|
||||
t.Run("when AM config fails to save", func(t *testing.T) {
|
||||
sut := createMuteTimingSvcSut()
|
||||
sut.config.(*MockAMConfigStore).EXPECT().
|
||||
sut.configStore.store.(*MockAMConfigStore).EXPECT().
|
||||
GetsConfig(models.AlertConfiguration{
|
||||
AlertmanagerConfiguration: configWithMuteTimings,
|
||||
})
|
||||
sut.config.(*MockAMConfigStore).EXPECT().
|
||||
sut.configStore.store.(*MockAMConfigStore).EXPECT().
|
||||
UpdateAlertmanagerConfiguration(mock.Anything, mock.Anything).
|
||||
Return(fmt.Errorf("failed to save config"))
|
||||
sut.prov.(*MockProvisioningStore).EXPECT().SaveSucceeds()
|
||||
sut.provenanceStore.(*MockProvisioningStore).EXPECT().SaveSucceeds()
|
||||
|
||||
err := sut.DeleteMuteTiming(context.Background(), "asdf", 1)
|
||||
|
||||
@ -360,7 +360,7 @@ func TestMuteTimingService(t *testing.T) {
|
||||
|
||||
t.Run("when mute timing is used in route", func(t *testing.T) {
|
||||
sut := createMuteTimingSvcSut()
|
||||
sut.config.(*MockAMConfigStore).EXPECT().
|
||||
sut.configStore.store.(*MockAMConfigStore).EXPECT().
|
||||
GetsConfig(models.AlertConfiguration{
|
||||
AlertmanagerConfiguration: configWithMuteTimingsInRoute,
|
||||
})
|
||||
@ -375,10 +375,10 @@ func TestMuteTimingService(t *testing.T) {
|
||||
|
||||
func createMuteTimingSvcSut() *MuteTimingService {
|
||||
return &MuteTimingService{
|
||||
config: &MockAMConfigStore{},
|
||||
prov: &MockProvisioningStore{},
|
||||
xact: newNopTransactionManager(),
|
||||
log: log.NewNopLogger(),
|
||||
configStore: &alertmanagerConfigStoreImpl{store: &MockAMConfigStore{}},
|
||||
provenanceStore: &MockProvisioningStore{},
|
||||
xact: newNopTransactionManager(),
|
||||
log: log.NewNopLogger(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -11,7 +11,7 @@ import (
|
||||
)
|
||||
|
||||
type NotificationPolicyService struct {
|
||||
amStore AMConfigStore
|
||||
configStore *alertmanagerConfigStoreImpl
|
||||
provenanceStore ProvisioningStore
|
||||
xact TransactionManager
|
||||
log log.Logger
|
||||
@ -21,7 +21,7 @@ type NotificationPolicyService struct {
|
||||
func NewNotificationPolicyService(am AMConfigStore, prov ProvisioningStore,
|
||||
xact TransactionManager, settings setting.UnifiedAlertingSettings, log log.Logger) *NotificationPolicyService {
|
||||
return &NotificationPolicyService{
|
||||
amStore: am,
|
||||
configStore: &alertmanagerConfigStoreImpl{store: am},
|
||||
provenanceStore: prov,
|
||||
xact: xact,
|
||||
log: log,
|
||||
@ -30,30 +30,25 @@ func NewNotificationPolicyService(am AMConfigStore, prov ProvisioningStore,
|
||||
}
|
||||
|
||||
func (nps *NotificationPolicyService) GetAMConfigStore() AMConfigStore {
|
||||
return nps.amStore
|
||||
return nps.configStore.store
|
||||
}
|
||||
|
||||
func (nps *NotificationPolicyService) GetPolicyTree(ctx context.Context, orgID int64) (definitions.Route, error) {
|
||||
alertManagerConfig, err := nps.amStore.GetLatestAlertmanagerConfiguration(ctx, orgID)
|
||||
rev, err := nps.configStore.Get(ctx, orgID)
|
||||
if err != nil {
|
||||
return definitions.Route{}, err
|
||||
}
|
||||
|
||||
cfg, err := deserializeAlertmanagerConfig([]byte(alertManagerConfig.AlertmanagerConfiguration))
|
||||
if err != nil {
|
||||
return definitions.Route{}, err
|
||||
}
|
||||
|
||||
if cfg.AlertmanagerConfig.Config.Route == nil {
|
||||
if rev.cfg.AlertmanagerConfig.Config.Route == nil {
|
||||
return definitions.Route{}, fmt.Errorf("no route present in current alertmanager config")
|
||||
}
|
||||
|
||||
provenance, err := nps.provenanceStore.GetProvenance(ctx, cfg.AlertmanagerConfig.Route, orgID)
|
||||
provenance, err := nps.provenanceStore.GetProvenance(ctx, rev.cfg.AlertmanagerConfig.Route, orgID)
|
||||
if err != nil {
|
||||
return definitions.Route{}, err
|
||||
}
|
||||
|
||||
result := *cfg.AlertmanagerConfig.Route
|
||||
result := *rev.cfg.AlertmanagerConfig.Route
|
||||
result.Provenance = definitions.Provenance(provenance)
|
||||
|
||||
return result, nil
|
||||
@ -65,7 +60,7 @@ func (nps *NotificationPolicyService) UpdatePolicyTree(ctx context.Context, orgI
|
||||
return fmt.Errorf("%w: %s", ErrValidation, err.Error())
|
||||
}
|
||||
|
||||
revision, err := getLastConfiguration(ctx, orgID, nps.amStore)
|
||||
revision, err := nps.configStore.Get(ctx, orgID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -91,33 +86,12 @@ func (nps *NotificationPolicyService) UpdatePolicyTree(ctx context.Context, orgI
|
||||
|
||||
revision.cfg.AlertmanagerConfig.Config.Route = &tree
|
||||
|
||||
serialized, err := serializeAlertmanagerConfig(*revision.cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cmd := models.SaveAlertmanagerConfigurationCmd{
|
||||
AlertmanagerConfiguration: string(serialized),
|
||||
ConfigurationVersion: revision.version,
|
||||
FetchedConfigurationHash: revision.concurrencyToken,
|
||||
Default: false,
|
||||
OrgID: orgID,
|
||||
}
|
||||
err = nps.xact.InTransaction(ctx, func(ctx context.Context) error {
|
||||
err = PersistConfig(ctx, nps.amStore, &cmd)
|
||||
if err != nil {
|
||||
return nps.xact.InTransaction(ctx, func(ctx context.Context) error {
|
||||
if err := nps.configStore.Save(ctx, revision, orgID); err != nil {
|
||||
return err
|
||||
}
|
||||
err = nps.provenanceStore.SetProvenance(ctx, &tree, orgID, p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
return nps.provenanceStore.SetProvenance(ctx, &tree, orgID, p)
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (nps *NotificationPolicyService) ResetPolicyTree(ctx context.Context, orgID int64) (definitions.Route, error) {
|
||||
@ -128,7 +102,7 @@ func (nps *NotificationPolicyService) ResetPolicyTree(ctx context.Context, orgID
|
||||
}
|
||||
route := defaultCfg.AlertmanagerConfig.Route
|
||||
|
||||
revision, err := getLastConfiguration(ctx, orgID, nps.amStore)
|
||||
revision, err := nps.configStore.Get(ctx, orgID)
|
||||
if err != nil {
|
||||
return definitions.Route{}, err
|
||||
}
|
||||
@ -138,31 +112,16 @@ func (nps *NotificationPolicyService) ResetPolicyTree(ctx context.Context, orgID
|
||||
return definitions.Route{}, err
|
||||
}
|
||||
|
||||
serialized, err := serializeAlertmanagerConfig(*revision.cfg)
|
||||
if err != nil {
|
||||
return definitions.Route{}, err
|
||||
}
|
||||
cmd := models.SaveAlertmanagerConfigurationCmd{
|
||||
AlertmanagerConfiguration: string(serialized),
|
||||
ConfigurationVersion: revision.version,
|
||||
FetchedConfigurationHash: revision.concurrencyToken,
|
||||
Default: false,
|
||||
OrgID: orgID,
|
||||
}
|
||||
err = nps.xact.InTransaction(ctx, func(ctx context.Context) error {
|
||||
err := PersistConfig(ctx, nps.amStore, &cmd)
|
||||
if err != nil {
|
||||
if err := nps.configStore.Save(ctx, revision, orgID); err != nil {
|
||||
return err
|
||||
}
|
||||
err = nps.provenanceStore.DeleteProvenance(ctx, route, orgID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
return nps.provenanceStore.DeleteProvenance(ctx, route, orgID)
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return definitions.Route{}, nil
|
||||
}
|
||||
} // TODO should be error?
|
||||
|
||||
return *route, nil
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ func TestNotificationPolicyService(t *testing.T) {
|
||||
|
||||
t.Run("error if referenced mute time interval is not existing", func(t *testing.T) {
|
||||
sut := createNotificationPolicyServiceSut()
|
||||
sut.amStore = &MockAMConfigStore{}
|
||||
sut.configStore.store = &MockAMConfigStore{}
|
||||
cfg := createTestAlertingConfig()
|
||||
cfg.AlertmanagerConfig.MuteTimeIntervals = []config.MuteTimeInterval{
|
||||
{
|
||||
@ -37,9 +37,9 @@ func TestNotificationPolicyService(t *testing.T) {
|
||||
},
|
||||
}
|
||||
data, _ := serializeAlertmanagerConfig(*cfg)
|
||||
sut.amStore.(*MockAMConfigStore).On("GetLatestAlertmanagerConfiguration", mock.Anything, mock.Anything).
|
||||
sut.configStore.store.(*MockAMConfigStore).On("GetLatestAlertmanagerConfiguration", mock.Anything, mock.Anything).
|
||||
Return(&models.AlertConfiguration{AlertmanagerConfiguration: string(data)}, nil)
|
||||
sut.amStore.(*MockAMConfigStore).EXPECT().
|
||||
sut.configStore.store.(*MockAMConfigStore).EXPECT().
|
||||
UpdateAlertmanagerConfiguration(mock.Anything, mock.Anything).
|
||||
Return(nil)
|
||||
newRoute := createTestRoutingTree()
|
||||
@ -54,7 +54,7 @@ func TestNotificationPolicyService(t *testing.T) {
|
||||
|
||||
t.Run("pass if referenced mute time interval is existing", func(t *testing.T) {
|
||||
sut := createNotificationPolicyServiceSut()
|
||||
sut.amStore = &MockAMConfigStore{}
|
||||
sut.configStore.store = &MockAMConfigStore{}
|
||||
cfg := createTestAlertingConfig()
|
||||
cfg.AlertmanagerConfig.MuteTimeIntervals = []config.MuteTimeInterval{
|
||||
{
|
||||
@ -63,9 +63,9 @@ func TestNotificationPolicyService(t *testing.T) {
|
||||
},
|
||||
}
|
||||
data, _ := serializeAlertmanagerConfig(*cfg)
|
||||
sut.amStore.(*MockAMConfigStore).On("GetLatestAlertmanagerConfiguration", mock.Anything, mock.Anything).
|
||||
sut.configStore.store.(*MockAMConfigStore).On("GetLatestAlertmanagerConfiguration", mock.Anything, mock.Anything).
|
||||
Return(&models.AlertConfiguration{AlertmanagerConfiguration: string(data)}, nil)
|
||||
sut.amStore.(*MockAMConfigStore).EXPECT().
|
||||
sut.configStore.store.(*MockAMConfigStore).EXPECT().
|
||||
UpdateAlertmanagerConfiguration(mock.Anything, mock.Anything).
|
||||
Return(nil)
|
||||
newRoute := createTestRoutingTree()
|
||||
@ -105,12 +105,12 @@ func TestNotificationPolicyService(t *testing.T) {
|
||||
|
||||
t.Run("existing receiver reference will pass", func(t *testing.T) {
|
||||
sut := createNotificationPolicyServiceSut()
|
||||
sut.amStore = &MockAMConfigStore{}
|
||||
sut.configStore.store = &MockAMConfigStore{}
|
||||
cfg := createTestAlertingConfig()
|
||||
data, _ := serializeAlertmanagerConfig(*cfg)
|
||||
sut.amStore.(*MockAMConfigStore).On("GetLatestAlertmanagerConfiguration", mock.Anything, mock.Anything).
|
||||
sut.configStore.store.(*MockAMConfigStore).On("GetLatestAlertmanagerConfiguration", mock.Anything, mock.Anything).
|
||||
Return(&models.AlertConfiguration{AlertmanagerConfiguration: string(data)}, nil)
|
||||
sut.amStore.(*MockAMConfigStore).EXPECT().
|
||||
sut.configStore.store.(*MockAMConfigStore).EXPECT().
|
||||
UpdateAlertmanagerConfiguration(mock.Anything, mock.Anything).
|
||||
Return(nil)
|
||||
newRoute := createTestRoutingTree()
|
||||
@ -183,7 +183,7 @@ func TestNotificationPolicyService(t *testing.T) {
|
||||
|
||||
t.Run("deleting route with missing default receiver restores receiver", func(t *testing.T) {
|
||||
sut := createNotificationPolicyServiceSut()
|
||||
sut.amStore = &MockAMConfigStore{}
|
||||
sut.configStore.store = &MockAMConfigStore{}
|
||||
cfg := createTestAlertingConfig()
|
||||
cfg.AlertmanagerConfig.Route = &definitions.Route{
|
||||
Receiver: "a new receiver",
|
||||
@ -197,17 +197,17 @@ func TestNotificationPolicyService(t *testing.T) {
|
||||
// No default receiver! Only our custom one.
|
||||
}
|
||||
data, _ := serializeAlertmanagerConfig(*cfg)
|
||||
sut.amStore.(*MockAMConfigStore).On("GetLatestAlertmanagerConfiguration", mock.Anything, mock.Anything).
|
||||
sut.configStore.store.(*MockAMConfigStore).On("GetLatestAlertmanagerConfiguration", mock.Anything, mock.Anything).
|
||||
Return(&models.AlertConfiguration{AlertmanagerConfiguration: string(data)}, nil)
|
||||
var interceptedSave = models.SaveAlertmanagerConfigurationCmd{}
|
||||
sut.amStore.(*MockAMConfigStore).EXPECT().SaveSucceedsIntercept(&interceptedSave)
|
||||
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 config is semantically valid.
|
||||
// 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)
|
||||
@ -216,7 +216,7 @@ func TestNotificationPolicyService(t *testing.T) {
|
||||
|
||||
func createNotificationPolicyServiceSut() *NotificationPolicyService {
|
||||
return &NotificationPolicyService{
|
||||
amStore: newFakeAMConfigStore(defaultAlertmanagerConfigJSON),
|
||||
configStore: &alertmanagerConfigStoreImpl{store: newFakeAMConfigStore(defaultAlertmanagerConfigJSON)},
|
||||
provenanceStore: NewFakeProvisioningStore(),
|
||||
xact: newNopTransactionManager(),
|
||||
log: log.NewNopLogger(),
|
||||
|
@ -10,23 +10,23 @@ import (
|
||||
)
|
||||
|
||||
type TemplateService struct {
|
||||
config AMConfigStore
|
||||
prov ProvisioningStore
|
||||
xact TransactionManager
|
||||
log log.Logger
|
||||
configStore *alertmanagerConfigStoreImpl
|
||||
provenanceStore ProvisioningStore
|
||||
xact TransactionManager
|
||||
log log.Logger
|
||||
}
|
||||
|
||||
func NewTemplateService(config AMConfigStore, prov ProvisioningStore, xact TransactionManager, log log.Logger) *TemplateService {
|
||||
return &TemplateService{
|
||||
config: config,
|
||||
prov: prov,
|
||||
xact: xact,
|
||||
log: log,
|
||||
configStore: &alertmanagerConfigStoreImpl{store: config},
|
||||
provenanceStore: prov,
|
||||
xact: xact,
|
||||
log: log,
|
||||
}
|
||||
}
|
||||
|
||||
func (t *TemplateService) GetTemplates(ctx context.Context, orgID int64) (map[string]string, error) {
|
||||
revision, err := getLastConfiguration(ctx, orgID, t.config)
|
||||
revision, err := t.configStore.Get(ctx, orgID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -44,7 +44,7 @@ func (t *TemplateService) SetTemplate(ctx context.Context, orgID int64, tmpl def
|
||||
return definitions.NotificationTemplate{}, fmt.Errorf("%w: %s", ErrValidation, err.Error())
|
||||
}
|
||||
|
||||
revision, err := getLastConfiguration(ctx, orgID, t.config)
|
||||
revision, err := t.configStore.Get(ctx, orgID)
|
||||
if err != nil {
|
||||
return definitions.NotificationTemplate{}, err
|
||||
}
|
||||
@ -59,27 +59,11 @@ func (t *TemplateService) SetTemplate(ctx context.Context, orgID int64, tmpl def
|
||||
}
|
||||
revision.cfg.AlertmanagerConfig.Templates = tmpls
|
||||
|
||||
serialized, err := serializeAlertmanagerConfig(*revision.cfg)
|
||||
if err != nil {
|
||||
return definitions.NotificationTemplate{}, err
|
||||
}
|
||||
cmd := models.SaveAlertmanagerConfigurationCmd{
|
||||
AlertmanagerConfiguration: string(serialized),
|
||||
ConfigurationVersion: revision.version,
|
||||
FetchedConfigurationHash: revision.concurrencyToken,
|
||||
Default: false,
|
||||
OrgID: orgID,
|
||||
}
|
||||
err = t.xact.InTransaction(ctx, func(ctx context.Context) error {
|
||||
err = PersistConfig(ctx, t.config, &cmd)
|
||||
if err != nil {
|
||||
if err := t.configStore.Save(ctx, revision, orgID); err != nil {
|
||||
return err
|
||||
}
|
||||
err = t.prov.SetProvenance(ctx, &tmpl, orgID, models.Provenance(tmpl.Provenance))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
return t.provenanceStore.SetProvenance(ctx, &tmpl, orgID, models.Provenance(tmpl.Provenance))
|
||||
})
|
||||
if err != nil {
|
||||
return definitions.NotificationTemplate{}, err
|
||||
@ -89,42 +73,20 @@ func (t *TemplateService) SetTemplate(ctx context.Context, orgID int64, tmpl def
|
||||
}
|
||||
|
||||
func (t *TemplateService) DeleteTemplate(ctx context.Context, orgID int64, name string) error {
|
||||
revision, err := getLastConfiguration(ctx, orgID, t.config)
|
||||
revision, err := t.configStore.Get(ctx, orgID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
delete(revision.cfg.TemplateFiles, name)
|
||||
|
||||
serialized, err := serializeAlertmanagerConfig(*revision.cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd := models.SaveAlertmanagerConfigurationCmd{
|
||||
AlertmanagerConfiguration: string(serialized),
|
||||
ConfigurationVersion: revision.version,
|
||||
FetchedConfigurationHash: revision.concurrencyToken,
|
||||
Default: false,
|
||||
OrgID: orgID,
|
||||
}
|
||||
err = t.xact.InTransaction(ctx, func(ctx context.Context) error {
|
||||
err = PersistConfig(ctx, t.config, &cmd)
|
||||
if err != nil {
|
||||
return t.xact.InTransaction(ctx, func(ctx context.Context) error {
|
||||
if err := t.configStore.Save(ctx, revision, orgID); err != nil {
|
||||
return err
|
||||
}
|
||||
tgt := definitions.NotificationTemplate{
|
||||
Name: name,
|
||||
}
|
||||
err = t.prov.DeleteProvenance(ctx, &tgt, orgID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
return t.provenanceStore.DeleteProvenance(ctx, &tgt, orgID)
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ import (
|
||||
func TestTemplateService(t *testing.T) {
|
||||
t.Run("service returns templates from config file", func(t *testing.T) {
|
||||
sut := createTemplateServiceSut()
|
||||
sut.config.(*MockAMConfigStore).EXPECT().
|
||||
sut.configStore.store.(*MockAMConfigStore).EXPECT().
|
||||
GetsConfig(models.AlertConfiguration{
|
||||
AlertmanagerConfiguration: configWithTemplates,
|
||||
})
|
||||
@ -30,7 +30,7 @@ func TestTemplateService(t *testing.T) {
|
||||
|
||||
t.Run("service returns empty map when config file contains no templates", func(t *testing.T) {
|
||||
sut := createTemplateServiceSut()
|
||||
sut.config.(*MockAMConfigStore).EXPECT().
|
||||
sut.configStore.store.(*MockAMConfigStore).EXPECT().
|
||||
GetsConfig(models.AlertConfiguration{
|
||||
AlertmanagerConfiguration: defaultConfig,
|
||||
})
|
||||
@ -44,7 +44,7 @@ func TestTemplateService(t *testing.T) {
|
||||
t.Run("service propagates errors", func(t *testing.T) {
|
||||
t.Run("when unable to read config", func(t *testing.T) {
|
||||
sut := createTemplateServiceSut()
|
||||
sut.config.(*MockAMConfigStore).EXPECT().
|
||||
sut.configStore.store.(*MockAMConfigStore).EXPECT().
|
||||
GetLatestAlertmanagerConfiguration(mock.Anything, mock.Anything).
|
||||
Return(nil, fmt.Errorf("failed"))
|
||||
|
||||
@ -55,25 +55,25 @@ func TestTemplateService(t *testing.T) {
|
||||
|
||||
t.Run("when config is invalid", func(t *testing.T) {
|
||||
sut := createTemplateServiceSut()
|
||||
sut.config.(*MockAMConfigStore).EXPECT().
|
||||
sut.configStore.store.(*MockAMConfigStore).EXPECT().
|
||||
GetsConfig(models.AlertConfiguration{
|
||||
AlertmanagerConfiguration: brokenConfig,
|
||||
})
|
||||
|
||||
_, err := sut.GetTemplates(context.Background(), 1)
|
||||
|
||||
require.ErrorContains(t, err, "failed to deserialize")
|
||||
require.Truef(t, ErrBadAlertmanagerConfiguration.Base.Is(err), "expected ErrBadAlertmanagerConfiguration but got %s", err.Error())
|
||||
})
|
||||
|
||||
t.Run("when no AM config in current org", func(t *testing.T) {
|
||||
sut := createTemplateServiceSut()
|
||||
sut.config.(*MockAMConfigStore).EXPECT().
|
||||
sut.configStore.store.(*MockAMConfigStore).EXPECT().
|
||||
GetLatestAlertmanagerConfiguration(mock.Anything, mock.Anything).
|
||||
Return(nil, nil)
|
||||
|
||||
_, err := sut.GetTemplates(context.Background(), 1)
|
||||
|
||||
require.ErrorContains(t, err, "no alertmanager configuration")
|
||||
require.Truef(t, ErrNoAlertmanagerConfiguration.Is(err), "expected ErrNoAlertmanagerConfiguration but got %s", err.Error())
|
||||
})
|
||||
})
|
||||
|
||||
@ -94,7 +94,7 @@ func TestTemplateService(t *testing.T) {
|
||||
t.Run("when unable to read config", func(t *testing.T) {
|
||||
sut := createTemplateServiceSut()
|
||||
tmpl := createNotificationTemplate()
|
||||
sut.config.(*MockAMConfigStore).EXPECT().
|
||||
sut.configStore.store.(*MockAMConfigStore).EXPECT().
|
||||
GetLatestAlertmanagerConfiguration(mock.Anything, mock.Anything).
|
||||
Return(nil, fmt.Errorf("failed"))
|
||||
|
||||
@ -106,37 +106,37 @@ func TestTemplateService(t *testing.T) {
|
||||
t.Run("when config is invalid", func(t *testing.T) {
|
||||
sut := createTemplateServiceSut()
|
||||
tmpl := createNotificationTemplate()
|
||||
sut.config.(*MockAMConfigStore).EXPECT().
|
||||
sut.configStore.store.(*MockAMConfigStore).EXPECT().
|
||||
GetsConfig(models.AlertConfiguration{
|
||||
AlertmanagerConfiguration: brokenConfig,
|
||||
})
|
||||
|
||||
_, err := sut.SetTemplate(context.Background(), 1, tmpl)
|
||||
|
||||
require.ErrorContains(t, err, "failed to deserialize")
|
||||
require.Truef(t, ErrBadAlertmanagerConfiguration.Base.Is(err), "expected ErrBadAlertmanagerConfiguration but got %s", err.Error())
|
||||
})
|
||||
|
||||
t.Run("when no AM config in current org", func(t *testing.T) {
|
||||
sut := createTemplateServiceSut()
|
||||
tmpl := createNotificationTemplate()
|
||||
sut.config.(*MockAMConfigStore).EXPECT().
|
||||
sut.configStore.store.(*MockAMConfigStore).EXPECT().
|
||||
GetLatestAlertmanagerConfiguration(mock.Anything, mock.Anything).
|
||||
Return(nil, nil)
|
||||
|
||||
_, err := sut.SetTemplate(context.Background(), 1, tmpl)
|
||||
|
||||
require.ErrorContains(t, err, "no alertmanager configuration")
|
||||
require.Truef(t, ErrNoAlertmanagerConfiguration.Is(err), "expected ErrNoAlertmanagerConfiguration but got %s", err.Error())
|
||||
})
|
||||
|
||||
t.Run("when provenance fails to save", func(t *testing.T) {
|
||||
sut := createTemplateServiceSut()
|
||||
tmpl := createNotificationTemplate()
|
||||
sut.config.(*MockAMConfigStore).EXPECT().
|
||||
sut.configStore.store.(*MockAMConfigStore).EXPECT().
|
||||
GetsConfig(models.AlertConfiguration{
|
||||
AlertmanagerConfiguration: configWithTemplates,
|
||||
})
|
||||
sut.config.(*MockAMConfigStore).EXPECT().SaveSucceeds()
|
||||
sut.prov.(*MockProvisioningStore).EXPECT().
|
||||
sut.configStore.store.(*MockAMConfigStore).EXPECT().SaveSucceeds()
|
||||
sut.provenanceStore.(*MockProvisioningStore).EXPECT().
|
||||
SetProvenance(mock.Anything, mock.Anything, mock.Anything, mock.Anything).
|
||||
Return(fmt.Errorf("failed to save provenance"))
|
||||
|
||||
@ -148,14 +148,14 @@ func TestTemplateService(t *testing.T) {
|
||||
t.Run("when AM config fails to save", func(t *testing.T) {
|
||||
sut := createTemplateServiceSut()
|
||||
tmpl := createNotificationTemplate()
|
||||
sut.config.(*MockAMConfigStore).EXPECT().
|
||||
sut.configStore.store.(*MockAMConfigStore).EXPECT().
|
||||
GetsConfig(models.AlertConfiguration{
|
||||
AlertmanagerConfiguration: configWithTemplates,
|
||||
})
|
||||
sut.config.(*MockAMConfigStore).EXPECT().
|
||||
sut.configStore.store.(*MockAMConfigStore).EXPECT().
|
||||
UpdateAlertmanagerConfiguration(mock.Anything, mock.Anything).
|
||||
Return(fmt.Errorf("failed to save config"))
|
||||
sut.prov.(*MockProvisioningStore).EXPECT().SaveSucceeds()
|
||||
sut.provenanceStore.(*MockProvisioningStore).EXPECT().SaveSucceeds()
|
||||
|
||||
_, err := sut.SetTemplate(context.Background(), 1, tmpl)
|
||||
|
||||
@ -166,12 +166,12 @@ func TestTemplateService(t *testing.T) {
|
||||
t.Run("adds new template to config file on success", func(t *testing.T) {
|
||||
sut := createTemplateServiceSut()
|
||||
tmpl := createNotificationTemplate()
|
||||
sut.config.(*MockAMConfigStore).EXPECT().
|
||||
sut.configStore.store.(*MockAMConfigStore).EXPECT().
|
||||
GetsConfig(models.AlertConfiguration{
|
||||
AlertmanagerConfiguration: configWithTemplates,
|
||||
})
|
||||
sut.config.(*MockAMConfigStore).EXPECT().SaveSucceeds()
|
||||
sut.prov.(*MockProvisioningStore).EXPECT().SaveSucceeds()
|
||||
sut.configStore.store.(*MockAMConfigStore).EXPECT().SaveSucceeds()
|
||||
sut.provenanceStore.(*MockProvisioningStore).EXPECT().SaveSucceeds()
|
||||
|
||||
_, err := sut.SetTemplate(context.Background(), 1, tmpl)
|
||||
|
||||
@ -181,12 +181,12 @@ func TestTemplateService(t *testing.T) {
|
||||
t.Run("succeeds when stitching config file with no templates", func(t *testing.T) {
|
||||
sut := createTemplateServiceSut()
|
||||
tmpl := createNotificationTemplate()
|
||||
sut.config.(*MockAMConfigStore).EXPECT().
|
||||
sut.configStore.store.(*MockAMConfigStore).EXPECT().
|
||||
GetsConfig(models.AlertConfiguration{
|
||||
AlertmanagerConfiguration: defaultConfig,
|
||||
})
|
||||
sut.config.(*MockAMConfigStore).EXPECT().SaveSucceeds()
|
||||
sut.prov.(*MockProvisioningStore).EXPECT().SaveSucceeds()
|
||||
sut.configStore.store.(*MockAMConfigStore).EXPECT().SaveSucceeds()
|
||||
sut.provenanceStore.(*MockProvisioningStore).EXPECT().SaveSucceeds()
|
||||
|
||||
_, err := sut.SetTemplate(context.Background(), 1, tmpl)
|
||||
|
||||
@ -199,12 +199,12 @@ func TestTemplateService(t *testing.T) {
|
||||
Name: "name",
|
||||
Template: "content",
|
||||
}
|
||||
sut.config.(*MockAMConfigStore).EXPECT().
|
||||
sut.configStore.store.(*MockAMConfigStore).EXPECT().
|
||||
GetsConfig(models.AlertConfiguration{
|
||||
AlertmanagerConfiguration: defaultConfig,
|
||||
})
|
||||
sut.config.(*MockAMConfigStore).EXPECT().SaveSucceeds()
|
||||
sut.prov.(*MockProvisioningStore).EXPECT().SaveSucceeds()
|
||||
sut.configStore.store.(*MockAMConfigStore).EXPECT().SaveSucceeds()
|
||||
sut.provenanceStore.(*MockProvisioningStore).EXPECT().SaveSucceeds()
|
||||
|
||||
result, _ := sut.SetTemplate(context.Background(), 1, tmpl)
|
||||
|
||||
@ -218,12 +218,12 @@ func TestTemplateService(t *testing.T) {
|
||||
Name: "name",
|
||||
Template: "{{define \"name\"}}content{{end}}",
|
||||
}
|
||||
sut.config.(*MockAMConfigStore).EXPECT().
|
||||
sut.configStore.store.(*MockAMConfigStore).EXPECT().
|
||||
GetsConfig(models.AlertConfiguration{
|
||||
AlertmanagerConfiguration: defaultConfig,
|
||||
})
|
||||
sut.config.(*MockAMConfigStore).EXPECT().SaveSucceeds()
|
||||
sut.prov.(*MockProvisioningStore).EXPECT().SaveSucceeds()
|
||||
sut.configStore.store.(*MockAMConfigStore).EXPECT().SaveSucceeds()
|
||||
sut.provenanceStore.(*MockProvisioningStore).EXPECT().SaveSucceeds()
|
||||
|
||||
result, _ := sut.SetTemplate(context.Background(), 1, tmpl)
|
||||
|
||||
@ -236,12 +236,12 @@ func TestTemplateService(t *testing.T) {
|
||||
Name: "name",
|
||||
Template: "{{ .MyField }",
|
||||
}
|
||||
sut.config.(*MockAMConfigStore).EXPECT().
|
||||
sut.configStore.store.(*MockAMConfigStore).EXPECT().
|
||||
GetsConfig(models.AlertConfiguration{
|
||||
AlertmanagerConfiguration: defaultConfig,
|
||||
})
|
||||
sut.config.(*MockAMConfigStore).EXPECT().SaveSucceeds()
|
||||
sut.prov.(*MockProvisioningStore).EXPECT().SaveSucceeds()
|
||||
sut.configStore.store.(*MockAMConfigStore).EXPECT().SaveSucceeds()
|
||||
sut.provenanceStore.(*MockProvisioningStore).EXPECT().SaveSucceeds()
|
||||
|
||||
_, err := sut.SetTemplate(context.Background(), 1, tmpl)
|
||||
|
||||
@ -254,12 +254,12 @@ func TestTemplateService(t *testing.T) {
|
||||
Name: "name",
|
||||
Template: "{{ .NotAField }}",
|
||||
}
|
||||
sut.config.(*MockAMConfigStore).EXPECT().
|
||||
sut.configStore.store.(*MockAMConfigStore).EXPECT().
|
||||
GetsConfig(models.AlertConfiguration{
|
||||
AlertmanagerConfiguration: defaultConfig,
|
||||
})
|
||||
sut.config.(*MockAMConfigStore).EXPECT().SaveSucceeds()
|
||||
sut.prov.(*MockProvisioningStore).EXPECT().SaveSucceeds()
|
||||
sut.configStore.store.(*MockAMConfigStore).EXPECT().SaveSucceeds()
|
||||
sut.provenanceStore.(*MockProvisioningStore).EXPECT().SaveSucceeds()
|
||||
|
||||
_, err := sut.SetTemplate(context.Background(), 1, tmpl)
|
||||
|
||||
@ -271,7 +271,7 @@ func TestTemplateService(t *testing.T) {
|
||||
t.Run("propagates errors", func(t *testing.T) {
|
||||
t.Run("when unable to read config", func(t *testing.T) {
|
||||
sut := createTemplateServiceSut()
|
||||
sut.config.(*MockAMConfigStore).EXPECT().
|
||||
sut.configStore.store.(*MockAMConfigStore).EXPECT().
|
||||
GetLatestAlertmanagerConfiguration(mock.Anything, mock.Anything).
|
||||
Return(nil, fmt.Errorf("failed"))
|
||||
|
||||
@ -282,35 +282,35 @@ func TestTemplateService(t *testing.T) {
|
||||
|
||||
t.Run("when config is invalid", func(t *testing.T) {
|
||||
sut := createTemplateServiceSut()
|
||||
sut.config.(*MockAMConfigStore).EXPECT().
|
||||
sut.configStore.store.(*MockAMConfigStore).EXPECT().
|
||||
GetsConfig(models.AlertConfiguration{
|
||||
AlertmanagerConfiguration: brokenConfig,
|
||||
})
|
||||
|
||||
err := sut.DeleteTemplate(context.Background(), 1, "template")
|
||||
|
||||
require.ErrorContains(t, err, "failed to deserialize")
|
||||
require.Truef(t, ErrBadAlertmanagerConfiguration.Base.Is(err), "expected ErrBadAlertmanagerConfiguration but got %s", err.Error())
|
||||
})
|
||||
|
||||
t.Run("when no AM config in current org", func(t *testing.T) {
|
||||
sut := createTemplateServiceSut()
|
||||
sut.config.(*MockAMConfigStore).EXPECT().
|
||||
sut.configStore.store.(*MockAMConfigStore).EXPECT().
|
||||
GetLatestAlertmanagerConfiguration(mock.Anything, mock.Anything).
|
||||
Return(nil, nil)
|
||||
|
||||
err := sut.DeleteTemplate(context.Background(), 1, "template")
|
||||
|
||||
require.ErrorContains(t, err, "no alertmanager configuration")
|
||||
require.Truef(t, ErrNoAlertmanagerConfiguration.Is(err), "expected ErrNoAlertmanagerConfiguration but got %s", err.Error())
|
||||
})
|
||||
|
||||
t.Run("when provenance fails to save", func(t *testing.T) {
|
||||
sut := createTemplateServiceSut()
|
||||
sut.config.(*MockAMConfigStore).EXPECT().
|
||||
sut.configStore.store.(*MockAMConfigStore).EXPECT().
|
||||
GetsConfig(models.AlertConfiguration{
|
||||
AlertmanagerConfiguration: configWithTemplates,
|
||||
})
|
||||
sut.config.(*MockAMConfigStore).EXPECT().SaveSucceeds()
|
||||
sut.prov.(*MockProvisioningStore).EXPECT().
|
||||
sut.configStore.store.(*MockAMConfigStore).EXPECT().SaveSucceeds()
|
||||
sut.provenanceStore.(*MockProvisioningStore).EXPECT().
|
||||
DeleteProvenance(mock.Anything, mock.Anything, mock.Anything).
|
||||
Return(fmt.Errorf("failed to save provenance"))
|
||||
|
||||
@ -321,14 +321,14 @@ func TestTemplateService(t *testing.T) {
|
||||
|
||||
t.Run("when AM config fails to save", func(t *testing.T) {
|
||||
sut := createTemplateServiceSut()
|
||||
sut.config.(*MockAMConfigStore).EXPECT().
|
||||
sut.configStore.store.(*MockAMConfigStore).EXPECT().
|
||||
GetsConfig(models.AlertConfiguration{
|
||||
AlertmanagerConfiguration: configWithTemplates,
|
||||
})
|
||||
sut.config.(*MockAMConfigStore).EXPECT().
|
||||
sut.configStore.store.(*MockAMConfigStore).EXPECT().
|
||||
UpdateAlertmanagerConfiguration(mock.Anything, mock.Anything).
|
||||
Return(fmt.Errorf("failed to save config"))
|
||||
sut.prov.(*MockProvisioningStore).EXPECT().SaveSucceeds()
|
||||
sut.provenanceStore.(*MockProvisioningStore).EXPECT().SaveSucceeds()
|
||||
|
||||
err := sut.DeleteTemplate(context.Background(), 1, "template")
|
||||
|
||||
@ -338,12 +338,12 @@ func TestTemplateService(t *testing.T) {
|
||||
|
||||
t.Run("deletes template from config file on success", func(t *testing.T) {
|
||||
sut := createTemplateServiceSut()
|
||||
sut.config.(*MockAMConfigStore).EXPECT().
|
||||
sut.configStore.store.(*MockAMConfigStore).EXPECT().
|
||||
GetsConfig(models.AlertConfiguration{
|
||||
AlertmanagerConfiguration: configWithTemplates,
|
||||
})
|
||||
sut.config.(*MockAMConfigStore).EXPECT().SaveSucceeds()
|
||||
sut.prov.(*MockProvisioningStore).EXPECT().SaveSucceeds()
|
||||
sut.configStore.store.(*MockAMConfigStore).EXPECT().SaveSucceeds()
|
||||
sut.provenanceStore.(*MockProvisioningStore).EXPECT().SaveSucceeds()
|
||||
|
||||
err := sut.DeleteTemplate(context.Background(), 1, "a")
|
||||
|
||||
@ -352,12 +352,12 @@ func TestTemplateService(t *testing.T) {
|
||||
|
||||
t.Run("does not error when deleting templates that do not exist", func(t *testing.T) {
|
||||
sut := createTemplateServiceSut()
|
||||
sut.config.(*MockAMConfigStore).EXPECT().
|
||||
sut.configStore.store.(*MockAMConfigStore).EXPECT().
|
||||
GetsConfig(models.AlertConfiguration{
|
||||
AlertmanagerConfiguration: configWithTemplates,
|
||||
})
|
||||
sut.config.(*MockAMConfigStore).EXPECT().SaveSucceeds()
|
||||
sut.prov.(*MockProvisioningStore).EXPECT().SaveSucceeds()
|
||||
sut.configStore.store.(*MockAMConfigStore).EXPECT().SaveSucceeds()
|
||||
sut.provenanceStore.(*MockProvisioningStore).EXPECT().SaveSucceeds()
|
||||
|
||||
err := sut.DeleteTemplate(context.Background(), 1, "does not exist")
|
||||
|
||||
@ -366,12 +366,12 @@ func TestTemplateService(t *testing.T) {
|
||||
|
||||
t.Run("succeeds when deleting from config file with no template section", func(t *testing.T) {
|
||||
sut := createTemplateServiceSut()
|
||||
sut.config.(*MockAMConfigStore).EXPECT().
|
||||
sut.configStore.store.(*MockAMConfigStore).EXPECT().
|
||||
GetsConfig(models.AlertConfiguration{
|
||||
AlertmanagerConfiguration: defaultConfig,
|
||||
})
|
||||
sut.config.(*MockAMConfigStore).EXPECT().SaveSucceeds()
|
||||
sut.prov.(*MockProvisioningStore).EXPECT().SaveSucceeds()
|
||||
sut.configStore.store.(*MockAMConfigStore).EXPECT().SaveSucceeds()
|
||||
sut.provenanceStore.(*MockProvisioningStore).EXPECT().SaveSucceeds()
|
||||
|
||||
err := sut.DeleteTemplate(context.Background(), 1, "a")
|
||||
|
||||
@ -382,10 +382,10 @@ func TestTemplateService(t *testing.T) {
|
||||
|
||||
func createTemplateServiceSut() *TemplateService {
|
||||
return &TemplateService{
|
||||
config: &MockAMConfigStore{},
|
||||
prov: &MockProvisioningStore{},
|
||||
xact: newNopTransactionManager(),
|
||||
log: log.NewNopLogger(),
|
||||
configStore: &alertmanagerConfigStoreImpl{store: &MockAMConfigStore{}},
|
||||
provenanceStore: &MockProvisioningStore{},
|
||||
xact: newNopTransactionManager(),
|
||||
log: log.NewNopLogger(),
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user