diff --git a/pkg/services/ngalert/notifier/crypto.go b/pkg/services/ngalert/notifier/crypto.go index 9d1551417cc..a44ff34af88 100644 --- a/pkg/services/ngalert/notifier/crypto.go +++ b/pkg/services/ngalert/notifier/crypto.go @@ -10,7 +10,9 @@ import ( "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/notifier/channels_config" + "github.com/grafana/grafana/pkg/services/ngalert/notifier/legacy_storage" "github.com/grafana/grafana/pkg/services/ngalert/store" "github.com/grafana/grafana/pkg/services/secrets" ) @@ -302,3 +304,49 @@ func (c *ExtraConfigsCrypto) DecryptExtraConfigs(ctx context.Context, config *de return nil } + +func DecryptedReceivers(receivers []*definitions.PostableApiReceiver, decryptFn models.DecryptFn) ([]*definitions.PostableApiReceiver, error) { + decrypted := make([]*definitions.PostableApiReceiver, len(receivers)) + for i, r := range receivers { + // We don't care about the provenance here, so we pass ProvenanceNone. + rcv, err := legacy_storage.PostableApiReceiverToReceiver(r, models.ProvenanceNone, models.ResourceOriginGrafana) + if err != nil { + return nil, err + } + + err = rcv.Decrypt(decryptFn) + if err != nil { + return nil, fmt.Errorf("failed to decrypt receiver %q: %w", rcv.Name, err) + } + + postable, err := legacy_storage.ReceiverToPostableApiReceiver(rcv) + if err != nil { + return nil, fmt.Errorf("failed to convert Receiver %q to APIReceiver: %w", rcv.Name, err) + } + decrypted[i] = postable + } + return decrypted, nil +} + +func EncryptedReceivers(receivers []*definitions.PostableApiReceiver, encryptFn models.EncryptFn) ([]*definitions.PostableApiReceiver, error) { + encrypted := make([]*definitions.PostableApiReceiver, len(receivers)) + for i, r := range receivers { + // We don't care about the provenance here, so we pass ProvenanceNone. + rcv, err := legacy_storage.PostableApiReceiverToReceiver(r, models.ProvenanceNone, models.ResourceOriginGrafana) + if err != nil { + return nil, err + } + + err = rcv.Encrypt(encryptFn) + if err != nil { + return nil, fmt.Errorf("failed to decrypt receiver %q: %w", rcv.Name, err) + } + + postable, err := legacy_storage.ReceiverToPostableApiReceiver(rcv) + if err != nil { + return nil, fmt.Errorf("failed to convert Receiver %q to APIReceiver: %w", rcv.Name, err) + } + encrypted[i] = postable + } + return encrypted, nil +} diff --git a/pkg/services/ngalert/notifier/legacy_storage/receivers.go b/pkg/services/ngalert/notifier/legacy_storage/receivers.go index d791e6ca31c..96f1eff5751 100644 --- a/pkg/services/ngalert/notifier/legacy_storage/receivers.go +++ b/pkg/services/ngalert/notifier/legacy_storage/receivers.go @@ -11,7 +11,7 @@ import ( "github.com/grafana/grafana/pkg/util" ) -type Provenances map[string]models.Provenance +type provenances = map[string]models.Provenance func (rev *ConfigRevision) DeleteReceiver(uid string) { // Remove the receiver from the configuration. @@ -39,11 +39,11 @@ func (rev *ConfigRevision) CreateReceiver(receiver *models.Receiver) (*models.Re rev.Config.AlertmanagerConfig.Receivers = append(rev.Config.AlertmanagerConfig.Receivers, postable) - if err := rev.ValidateReceiver(postable); err != nil { + if err := rev.validateReceiver(postable); err != nil { return nil, err } - return PostableApiReceiverToReceiver(postable, receiver.Provenance, receiver.Origin) + return PostableApiReceiverToReceiver(postable, receiver.Provenance, models.ResourceOriginGrafana) } func (rev *ConfigRevision) UpdateReceiver(receiver *models.Receiver) (*models.Receiver, error) { @@ -65,11 +65,11 @@ func (rev *ConfigRevision) UpdateReceiver(receiver *models.Receiver) (*models.Re rev.Config.AlertmanagerConfig.Receivers[existingIdx] = newReceiver - if err := rev.ValidateReceiver(newReceiver); err != nil { + if err := rev.validateReceiver(newReceiver); err != nil { return nil, err } - return PostableApiReceiverToReceiver(newReceiver, receiver.Provenance, receiver.Origin) + return PostableApiReceiverToReceiver(newReceiver, receiver.Provenance, models.ResourceOriginGrafana) } // ReceiverNameUsedByRoutes checks if a receiver name is used in any routes. @@ -84,7 +84,7 @@ func (rev *ConfigRevision) ReceiverUseByName() map[string]int { return m } -func (rev *ConfigRevision) GetReceiver(uid string, prov Provenances) (*models.Receiver, error) { +func (rev *ConfigRevision) GetReceiver(uid string, prov provenances) (*models.Receiver, error) { for _, r := range rev.Config.AlertmanagerConfig.Receivers { if NameToUid(r.GetName()) != uid { continue @@ -98,7 +98,7 @@ func (rev *ConfigRevision) GetReceiver(uid string, prov Provenances) (*models.Re return nil, ErrReceiverNotFound.Errorf("") } -func (rev *ConfigRevision) GetReceivers(uids []string, prov Provenances) ([]*models.Receiver, error) { +func (rev *ConfigRevision) GetReceivers(uids []string, prov provenances) ([]*models.Receiver, error) { capacity := len(uids) if capacity == 0 { capacity = len(rev.Config.AlertmanagerConfig.Receivers) @@ -127,60 +127,14 @@ func (rev *ConfigRevision) GetReceiversNames() map[string]struct{} { return result } -func DecryptedReceivers(receivers []*definitions.PostableApiReceiver, decryptFn models.DecryptFn) ([]*definitions.PostableApiReceiver, error) { - decrypted := make([]*definitions.PostableApiReceiver, len(receivers)) - for i, r := range receivers { - // We don't care about the provenance here, so we pass ProvenanceNone. - rcv, err := PostableApiReceiverToReceiver(r, models.ProvenanceNone, models.ResourceOriginGrafana) - if err != nil { - return nil, err - } - - err = rcv.Decrypt(decryptFn) - if err != nil { - return nil, fmt.Errorf("failed to decrypt receiver %q: %w", rcv.Name, err) - } - - postable, err := ReceiverToPostableApiReceiver(rcv) - if err != nil { - return nil, fmt.Errorf("failed to convert Receiver %q to APIReceiver: %w", rcv.Name, err) - } - decrypted[i] = postable - } - return decrypted, nil -} - -func EncryptedReceivers(receivers []*definitions.PostableApiReceiver, encryptFn models.EncryptFn) ([]*definitions.PostableApiReceiver, error) { - encrypted := make([]*definitions.PostableApiReceiver, len(receivers)) - for i, r := range receivers { - // We don't care about the provenance here, so we pass ProvenanceNone. - rcv, err := PostableApiReceiverToReceiver(r, models.ProvenanceNone, models.ResourceOriginGrafana) - if err != nil { - return nil, err - } - - err = rcv.Encrypt(encryptFn) - if err != nil { - return nil, fmt.Errorf("failed to decrypt receiver %q: %w", rcv.Name, err) - } - - postable, err := ReceiverToPostableApiReceiver(rcv) - if err != nil { - return nil, fmt.Errorf("failed to convert Receiver %q to APIReceiver: %w", rcv.Name, err) - } - encrypted[i] = postable - } - return encrypted, nil -} - // RenameReceiverInRoutes renames all references to a receiver in routes. Returns number of routes that were updated func (rev *ConfigRevision) RenameReceiverInRoutes(oldName, newName string) int { - return RenameReceiverInRoute(oldName, newName, rev.Config.AlertmanagerConfig.Route) + return renameReceiverInRoute(oldName, newName, rev.Config.AlertmanagerConfig.Route) } -// ValidateReceiver checks if the given receiver conflicts in name or integration UID with existing receivers. +// validateReceiver checks if the given receiver conflicts in name or integration UID with existing receivers. // We only check the receiver being modified to prevent existing issues from other receivers being reported. -func (rev *ConfigRevision) ValidateReceiver(p *definitions.PostableApiReceiver) error { +func (rev *ConfigRevision) validateReceiver(p *definitions.PostableApiReceiver) error { uids := make(map[string]struct{}, len(rev.Config.AlertmanagerConfig.Receivers)) for _, integrations := range p.GrafanaManagedReceivers { if _, exists := uids[integrations.UID]; exists { @@ -207,7 +161,7 @@ func (rev *ConfigRevision) ValidateReceiver(p *definitions.PostableApiReceiver) return nil } -func RenameReceiverInRoute(oldName, newName string, routes ...*definitions.Route) int { +func renameReceiverInRoute(oldName, newName string, routes ...*definitions.Route) int { if len(routes) == 0 { return 0 } @@ -217,7 +171,7 @@ func RenameReceiverInRoute(oldName, newName string, routes ...*definitions.Route route.Receiver = newName updated++ } - updated += RenameReceiverInRoute(oldName, newName, route.Routes...) + updated += renameReceiverInRoute(oldName, newName, route.Routes...) } return updated } diff --git a/pkg/services/ngalert/notifier/legacy_storage/receivers_test.go b/pkg/services/ngalert/notifier/legacy_storage/receivers_test.go index acc406a96c4..4bf74aa7866 100644 --- a/pkg/services/ngalert/notifier/legacy_storage/receivers_test.go +++ b/pkg/services/ngalert/notifier/legacy_storage/receivers_test.go @@ -1,11 +1,20 @@ package legacy_storage import ( + "encoding/json" + "slices" + "strings" "testing" + "github.com/grafana/alerting/definition" + "github.com/grafana/alerting/notify" + "github.com/prometheus/alertmanager/config" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions" + "github.com/grafana/grafana/pkg/services/ngalert/models" + "github.com/grafana/grafana/pkg/util" ) func TestReceiverInUse(t *testing.T) { @@ -38,3 +47,477 @@ func TestReceiverInUse(t *testing.T) { }) require.False(t, result) } + +func TestDeleteReceiver(t *testing.T) { + testCases := []struct { + name string + receiverUID string + assert func(t *testing.T, rev *ConfigRevision) + }{ + { + name: "should remove receiver if exists", + receiverUID: NameToUid("receiver1"), + assert: func(t *testing.T, rev *ConfigRevision) { + require.False(t, slices.ContainsFunc(rev.Config.AlertmanagerConfig.Receivers, func(receiver *definition.PostableApiReceiver) bool { + return receiver.Name == "receiver1" + })) + }, + }, + { + name: "should do nothing if receiver does not exist", + receiverUID: NameToUid("not-existing"), + assert: func(t *testing.T, rev *ConfigRevision) { + require.Equal(t, getConfigRevisionForTest(), rev) + }, + }, + { + name: "should remove all receivers with the same name", + receiverUID: NameToUid("dupe-receiver"), + assert: func(t *testing.T, rev *ConfigRevision) { + require.False(t, slices.ContainsFunc(rev.Config.AlertmanagerConfig.Receivers, func(receiver *definition.PostableApiReceiver) bool { + return receiver.Name == "dupe-receiver" + })) + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + rev := getConfigRevisionForTest() + rev.DeleteReceiver(tc.receiverUID) + tc.assert(t, rev) + }) + } +} + +func TestCreateReceiver(t *testing.T) { + rawCfg := notify.AllKnownConfigsForTesting["webhook"] + cfgSchema, err := models.IntegrationConfigFromType(rawCfg.NotifierType, util.Pointer("v1")) + require.NoError(t, err) + settings := map[string]any{} + require.NoError(t, json.Unmarshal([]byte(rawCfg.Config), &settings)) + + testCases := []struct { + name string + receiver *models.Receiver + expectedError error + assertResponse func(t *testing.T, rev *ConfigRevision, receiver *models.Receiver) + }{ + { + name: "should error if receiver already exists by UID", + receiver: &models.Receiver{ + UID: NameToUid("receiver1"), + Name: "New receiver name", + }, + expectedError: ErrReceiverExists, + }, + { + name: "should error if receiver already exists by name", + receiver: &models.Receiver{ + UID: "some-uid", + Name: "receiver1", + }, + expectedError: ErrReceiverInvalid, + }, + { + name: "should fail if integration UID is not valid", + receiver: &models.Receiver{ + UID: "some-uid", + Name: "receiver", + Integrations: []*models.Integration{ + { + UID: strings.Repeat("*", 256), + Config: cfgSchema, + Settings: settings, + }, + }, + }, + expectedError: ErrReceiverInvalid, + }, + { + name: "should fail if integration UID already exists", + receiver: &models.Receiver{ + UID: "some-uid", + Name: "receiver", + Integrations: []*models.Integration{ + { + UID: "integration-uid-1", + Config: cfgSchema, + Settings: settings, + }, + }, + }, + expectedError: ErrReceiverInvalid, + }, + { + name: "should add the receiver to configuration and set integrations UID", + receiver: &models.Receiver{ + UID: "some-uid", + Name: "receiver2", + Provenance: "test", + Integrations: []*models.Integration{ + { + Config: cfgSchema, + Settings: settings, + }, + }, + }, + expectedError: nil, + assertResponse: func(t *testing.T, rev *ConfigRevision, receiver *models.Receiver) { + t.Helper() + idx := slices.IndexFunc(rev.Config.AlertmanagerConfig.Receivers, func(r *definition.PostableApiReceiver) bool { + return r.Name == "receiver2" + }) + assert.Greaterf(t, idx, -1, "receiver was not added to the configuration") + postable := rev.Config.AlertmanagerConfig.Receivers[idx] + require.Len(t, postable.GrafanaManagedReceivers, 1) + require.Equal(t, receiver.Name, postable.Name) + require.NotEmpty(t, postable.GrafanaManagedReceivers[0].UID) + require.JSONEq(t, string(postable.GrafanaManagedReceivers[0].Settings), rawCfg.Config) + + assert.Equal(t, models.ResourceOriginGrafana, receiver.Origin) + assert.Equal(t, NameToUid("receiver2"), receiver.UID) + assert.Equal(t, postable.GrafanaManagedReceivers[0].UID, receiver.Integrations[0].UID) + assert.EqualValues(t, "test", receiver.Provenance) + }, + }, + } + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + rev := getConfigRevisionForTest() + recv, err := rev.CreateReceiver(tc.receiver) + if tc.expectedError != nil { + require.ErrorIs(t, err, tc.expectedError) + return + } + require.NoError(t, err) + tc.assertResponse(t, rev, recv) + }) + } +} + +func TestUpdateReceiver(t *testing.T) { + rawCfg := notify.AllKnownConfigsForTesting["webhook"] + cfgSchema, err := models.IntegrationConfigFromType(rawCfg.NotifierType, util.Pointer("v1")) + require.NoError(t, err) + settings := map[string]any{} + require.NoError(t, json.Unmarshal([]byte(rawCfg.Config), &settings)) + + testCases := []struct { + name string + receiver *models.Receiver + expectedError error + assertResponse func(t *testing.T, rev *ConfigRevision, receiver *models.Receiver) + }{ + { + name: "should error if receiver does not exists by UID", + receiver: &models.Receiver{ + UID: NameToUid("receiver2"), + Name: "receiver1", + }, + expectedError: ErrReceiverNotFound, + }, + { + name: "should fail if integration UID is not valid", + receiver: &models.Receiver{ + UID: NameToUid("receiver1"), + Name: "receiver1", + Integrations: []*models.Integration{ + { + UID: strings.Repeat("*", 256), + Config: cfgSchema, + Settings: settings, + }, + }, + }, + expectedError: ErrReceiverInvalid, + }, + { + name: "should fail if integration UID already exists", + receiver: &models.Receiver{ + UID: NameToUid("receiver1"), + Name: "receiver1", + Integrations: []*models.Integration{ + { + UID: "integration-uid-2", + Config: cfgSchema, + Settings: settings, + }, + }, + }, + expectedError: ErrReceiverInvalid, + }, + { + name: "should update the existing receiver", + receiver: &models.Receiver{ + UID: NameToUid("receiver1"), + Name: "receiver-new", + Provenance: "test", + Integrations: []*models.Integration{ + { + Config: cfgSchema, + Settings: settings, + }, + }, + }, + expectedError: nil, + assertResponse: func(t *testing.T, rev *ConfigRevision, receiver *models.Receiver) { + t.Helper() + idx := slices.IndexFunc(rev.Config.AlertmanagerConfig.Receivers, func(r *definition.PostableApiReceiver) bool { + return r.Name == "receiver-new" + }) + assert.Greaterf(t, idx, -1, "receiver was not found to the configuration") + old := getConfigRevisionForTest().Config.AlertmanagerConfig.Receivers[idx] + require.Equalf(t, old.Name, "receiver1", "the receiver should be updated in place") + + postable := rev.Config.AlertmanagerConfig.Receivers[idx] + require.Len(t, postable.GrafanaManagedReceivers, 1) + require.Equal(t, receiver.Name, postable.Name) + require.NotEmpty(t, postable.GrafanaManagedReceivers[0].UID) + require.JSONEq(t, string(postable.GrafanaManagedReceivers[0].Settings), rawCfg.Config) + + assert.Equal(t, postable.GrafanaManagedReceivers[0].UID, receiver.Integrations[0].UID) + assert.Equal(t, NameToUid("receiver-new"), receiver.UID) + assert.EqualValues(t, "test", receiver.Provenance) + }, + }, + } + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + rev := getConfigRevisionForTest() + recv, err := rev.UpdateReceiver(tc.receiver) + if tc.expectedError != nil { + require.ErrorIs(t, err, tc.expectedError) + return + } + require.NoError(t, err) + tc.assertResponse(t, rev, recv) + }) + } +} + +func TestGetReceiver(t *testing.T) { + rawCfg := notify.AllKnownConfigsForTesting["webhook"] + cfgSchema, err := models.IntegrationConfigFromType(rawCfg.NotifierType, util.Pointer("v1")) + require.NoError(t, err) + settings := map[string]any{} + require.NoError(t, json.Unmarshal([]byte(rawCfg.Config), &settings)) + + t.Run("should return ErrReceiverNotFound if receiver does not exists", func(t *testing.T) { + rev := getConfigRevisionForTest() + _, err := rev.GetReceiver("not-found", nil) + require.ErrorIs(t, err, ErrReceiverNotFound) + }) + + t.Run("should return receiver if exists", func(t *testing.T) { + prov := provenances{ + "integration-uid-1": "test", + } + + expected := &models.Receiver{ + UID: NameToUid("receiver1"), + Name: "receiver1", + Provenance: models.Provenance("test"), + Origin: models.ResourceOriginGrafana, + Version: "6e2fb0f572bc90f7", + Integrations: []*models.Integration{ + { + UID: "integration-uid-1", + Config: cfgSchema, + Settings: settings, + SecureSettings: make(map[string]string), + }, + }, + } + rev := getConfigRevisionForTest() + result, err := rev.GetReceiver(NameToUid("receiver1"), prov) + require.NoError(t, err) + require.Equal(t, expected, result) + }) +} + +func TestGetReceivers(t *testing.T) { + rev := getConfigRevisionForTest() + + t.Run("should return all receivers with correct provenance", func(t *testing.T) { + prov := provenances{ + "integration-uid-1": "test", + "integration-uid-2": "some", + } + receivers, err := rev.GetReceivers(nil, prov) + require.NoError(t, err) + require.Len(t, receivers, len(rev.Config.AlertmanagerConfig.Receivers)) + for _, r := range receivers { + assert.Equalf(t, NameToUid(r.Name), r.UID, "receiver UID should be function of receiver name") + assert.Equal(t, r.Origin, models.ResourceOriginGrafana) + if r.Name == "receiver1" { + assert.EqualValues(t, "test", r.Provenance) + } else if r.Name == "dupe-receiver" && r.Integrations[0].UID == "integration-uid-2" { + assert.EqualValues(t, "some", r.Provenance) + } else { + assert.Empty(t, r.Provenance) + } + } + }) + t.Run("should filter by uids", func(t *testing.T) { + receivers, err := rev.GetReceivers([]string{"not-found-1", "not-found-2"}, nil) + require.NoError(t, err) + require.Empty(t, receivers) + receivers, err = rev.GetReceivers([]string{NameToUid("receiver1")}, nil) + require.NoError(t, err) + require.Len(t, receivers, 1) + expected, err := rev.GetReceiver(NameToUid("receiver1"), nil) + require.NoError(t, err) + require.Equal(t, expected, receivers[0]) + }) +} + +func TestReceiverNameUsedByRoutes(t *testing.T) { + testCases := []struct { + name string + receiverName string + expectedResult bool + }{ + { + name: "should return true if receiver is used by routes", + receiverName: "receiver1", + expectedResult: true, + }, + { + name: "should return false if receiver is not used by routes", + receiverName: "receiver2", + expectedResult: false, + }, + } + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + rev := getConfigRevisionForTest() + result := rev.ReceiverNameUsedByRoutes(tc.receiverName) + require.Equal(t, tc.expectedResult, result) + }) + } +} + +func TestReceiverUseByName(t *testing.T) { + rev := getConfigRevisionForTest() + rev.Config.AlertmanagerConfig.Route.Routes = append(rev.Config.AlertmanagerConfig.Route.Routes, + &definitions.Route{ + Routes: []*definitions.Route{ + { + Receiver: "receiver1", + }, + { + Receiver: "dupe-receiver", + }, + { + Receiver: "some-missing-receiver", + }, + }, + }) + expected := map[string]int{ + "": 1, // some routes do not have receiver set + "receiver1": 2, + "dupe-receiver": 1, + "some-missing-receiver": 1, + } + require.Equal(t, expected, rev.ReceiverUseByName()) +} + +func TestRenameReceiverInRoutes(t *testing.T) { + rev := getConfigRevisionForTest() + rev.Config.AlertmanagerConfig.Route.Routes = append(rev.Config.AlertmanagerConfig.Route.Routes, &definitions.Route{ + Receiver: "receiver1", + Routes: []*definitions.Route{ + { + Receiver: "receiver1", + Routes: []*definitions.Route{ + { + Receiver: "missing-receiver", + }, + }, + }, + { + Receiver: "dupe-receiver", + Routes: []*definitions.Route{ + { + Receiver: "receiver1", + }, + }, + }, + }, + }) + + t.Run("should do nothing if receiver is not used by routes ", func(t *testing.T) { + result := rev.RenameReceiverInRoutes("not-found", "found") + require.Zero(t, result) + expected := map[string]int{ + "receiver1": 4, + "missing-receiver": 1, + "dupe-receiver": 1, + } + require.Equal(t, expected, rev.ReceiverUseByName()) + }) + + t.Run("should rename all references", func(t *testing.T) { + result := rev.RenameReceiverInRoutes("receiver1", "found") + require.Equal(t, result, 4) + expected := map[string]int{ + "found": 4, + "missing-receiver": 1, + "dupe-receiver": 1, + } + require.Equal(t, expected, rev.ReceiverUseByName()) + }) +} + +func getConfigRevisionForTest() *ConfigRevision { + return &ConfigRevision{ + Config: &definitions.PostableUserConfig{ + AlertmanagerConfig: definitions.PostableApiAlertingConfig{ + Config: definitions.Config{ + Route: &definitions.Route{Receiver: "receiver1"}, + }, + Receivers: []*definition.PostableApiReceiver{ + { + Receiver: config.Receiver{ + Name: "receiver1", + }, + PostableGrafanaReceivers: definition.PostableGrafanaReceivers{ + GrafanaManagedReceivers: []*definition.PostableGrafanaReceiver{ + { + UID: "integration-uid-1", + Type: "webhook", + Settings: definitions.RawMessage(notify.AllKnownConfigsForTesting["webhook"].Config), + }, + }, + }, + }, + { + Receiver: config.Receiver{Name: "dupe-receiver"}, + PostableGrafanaReceivers: definition.PostableGrafanaReceivers{ + GrafanaManagedReceivers: []*definition.PostableGrafanaReceiver{ + { + UID: "integration-uid-2", + Type: "webhook", + Settings: definitions.RawMessage(notify.AllKnownConfigsForTesting["webhook"].Config), + }, + }, + }, + }, + { + Receiver: config.Receiver{Name: "dupe-receiver"}, + PostableGrafanaReceivers: definition.PostableGrafanaReceivers{ + GrafanaManagedReceivers: []*definition.PostableGrafanaReceiver{ + { + UID: "integration-uid-3", + Type: "email", + Settings: definitions.RawMessage(notify.AllKnownConfigsForTesting["email"].Config), + }, + }, + }, + }, + }, + }, + }, + } +} diff --git a/pkg/services/ngalert/remote/alertmanager.go b/pkg/services/ngalert/remote/alertmanager.go index 73c929e5fc2..7048810195f 100644 --- a/pkg/services/ngalert/remote/alertmanager.go +++ b/pkg/services/ngalert/remote/alertmanager.go @@ -36,7 +36,6 @@ import ( "github.com/grafana/grafana/pkg/services/ngalert/metrics" "github.com/grafana/grafana/pkg/services/ngalert/models" "github.com/grafana/grafana/pkg/services/ngalert/notifier" - "github.com/grafana/grafana/pkg/services/ngalert/notifier/legacy_storage" remoteClient "github.com/grafana/grafana/pkg/services/ngalert/remote/client" "github.com/grafana/grafana/pkg/services/ngalert/sender" "github.com/grafana/grafana/pkg/util/cmputil" @@ -315,7 +314,7 @@ func (am *Alertmanager) buildConfiguration(ctx context.Context, raw []byte, crea } // Decrypt the receivers in the configuration. - decryptedReceivers, err := legacy_storage.DecryptedReceivers(c.AlertmanagerConfig.Receivers, decrypter(ctx, am.crypto)) + decryptedReceivers, err := notifier.DecryptedReceivers(c.AlertmanagerConfig.Receivers, decrypter(ctx, am.crypto)) if err != nil { return remoteClient.UserGrafanaConfig{}, fmt.Errorf("unable to decrypt receivers: %w", err) } @@ -605,7 +604,7 @@ func (am *Alertmanager) GetReceivers(ctx context.Context) ([]apimodels.Receiver, } func (am *Alertmanager) TestReceivers(ctx context.Context, c apimodels.TestReceiversConfigBodyParams) (*alertingNotify.TestReceiversResult, int, error) { - decryptedReceivers, err := legacy_storage.DecryptedReceivers(c.Receivers, decrypter(ctx, am.crypto)) + decryptedReceivers, err := notifier.DecryptedReceivers(c.Receivers, decrypter(ctx, am.crypto)) if err != nil { return nil, 0, fmt.Errorf("failed to decrypt receivers: %w", err) } diff --git a/pkg/services/ngalert/remote/alertmanager_test.go b/pkg/services/ngalert/remote/alertmanager_test.go index 88e91def216..0ca0bbc950f 100644 --- a/pkg/services/ngalert/remote/alertmanager_test.go +++ b/pkg/services/ngalert/remote/alertmanager_test.go @@ -41,7 +41,6 @@ import ( "github.com/grafana/grafana/pkg/services/ngalert/metrics" ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models" "github.com/grafana/grafana/pkg/services/ngalert/notifier" - "github.com/grafana/grafana/pkg/services/ngalert/notifier/legacy_storage" "github.com/grafana/grafana/pkg/services/ngalert/remote/client" ngfakes "github.com/grafana/grafana/pkg/services/ngalert/tests/fakes" "github.com/grafana/grafana/pkg/services/secrets" @@ -152,7 +151,7 @@ func TestGetRemoteState(t *testing.T) { // getOkHandler allows us to specify a full state the test server is going to respond with. getOkHandler := func(state string) http.HandlerFunc { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { require.Equal(t, tenantID, r.Header.Get(client.MimirTenantHeader)) require.Equal(t, "true", r.Header.Get(client.RemoteAlertmanagerHeader)) @@ -164,7 +163,7 @@ func TestGetRemoteState(t *testing.T) { } w.Header().Add("content-type", "application/json") require.NoError(t, json.NewEncoder(w).Encode(res)) - }) + } } // errorHandler makes the test server return a 500 status code and a non-JSON response. @@ -302,7 +301,7 @@ func TestIntegrationApplyConfig(t *testing.T) { var c apimodels.PostableUserConfig require.NoError(t, json.Unmarshal([]byte(testGrafanaConfigWithSecret), &c)) secretsService := secretsManager.SetupTestService(t, database.ProvideSecretsStore(db.InitTestDB(t))) - encryptedReceivers, err := legacy_storage.EncryptedReceivers(c.AlertmanagerConfig.Receivers, func(payload string) (string, error) { + encryptedReceivers, err := notifier.EncryptedReceivers(c.AlertmanagerConfig.Receivers, func(payload string) (string, error) { encrypted, err := secretsService.Encrypt(context.Background(), []byte(payload), secrets.WithoutScope()) if err != nil { return "", err @@ -381,7 +380,7 @@ func TestIntegrationApplyConfig(t *testing.T) { // should result in us sending the configuration but not the state. am.syncInterval = 0 config = &ngmodels.AlertConfiguration{ - AlertmanagerConfiguration: string(testGrafanaConfig), + AlertmanagerConfiguration: testGrafanaConfig, } require.NoError(t, am.ApplyConfig(ctx, config)) require.Equal(t, 2, configSyncs) @@ -467,7 +466,7 @@ func TestCompareAndSendConfiguration(t *testing.T) { // Create a config with correctly encrypted and encoded secrets. var inputCfg apimodels.PostableUserConfig require.NoError(t, json.Unmarshal([]byte(testGrafanaConfigWithSecret), &inputCfg)) - encryptedReceivers, err := legacy_storage.EncryptedReceivers(inputCfg.AlertmanagerConfig.Receivers, func(payload string) (string, error) { + encryptedReceivers, err := notifier.EncryptedReceivers(inputCfg.AlertmanagerConfig.Receivers, func(payload string) (string, error) { encrypted, err := secretsService.Encrypt(context.Background(), []byte(payload), secrets.WithoutScope()) if err != nil { return "", err @@ -669,7 +668,7 @@ func Test_TestReceiversDecryptsSecureSettings(t *testing.T) { var inputCfg apimodels.PostableUserConfig require.NoError(t, json.Unmarshal([]byte(testGrafanaConfigWithSecret), &inputCfg)) - encryptedReceivers, err := legacy_storage.EncryptedReceivers(inputCfg.AlertmanagerConfig.Receivers, func(payload string) (string, error) { + encryptedReceivers, err := notifier.EncryptedReceivers(inputCfg.AlertmanagerConfig.Receivers, func(payload string) (string, error) { encrypted, err := secretsService.Encrypt(context.Background(), []byte(payload), secrets.WithoutScope()) if err != nil { return "", err @@ -1045,7 +1044,7 @@ func TestIntegrationRemoteAlertmanagerConfiguration(t *testing.T) { { postableCfg, err := notifier.Load([]byte(testGrafanaConfigWithSecret)) require.NoError(t, err) - encryptedReceivers, err := legacy_storage.EncryptedReceivers(postableCfg.AlertmanagerConfig.Receivers, func(payload string) (string, error) { + encryptedReceivers, err := notifier.EncryptedReceivers(postableCfg.AlertmanagerConfig.Receivers, func(payload string) (string, error) { encrypted, err := secretsService.Encrypt(context.Background(), []byte(payload), secrets.WithoutScope()) if err != nil { return "", err