diff --git a/pkg/services/ngalert/remote/alertmanager.go b/pkg/services/ngalert/remote/alertmanager.go index eda40a57f16..6862fba4e99 100644 --- a/pkg/services/ngalert/remote/alertmanager.go +++ b/pkg/services/ngalert/remote/alertmanager.go @@ -269,8 +269,21 @@ func (am *Alertmanager) CompareAndSendState(ctx context.Context) error { return nil } +// SaveAndApplyConfig decrypts and sends a configuration to the remote Alertmanager. func (am *Alertmanager) SaveAndApplyConfig(ctx context.Context, cfg *apimodels.PostableUserConfig) error { - return nil + // Get the hash for the encrypted configuration. + rawCfg, err := json.Marshal(cfg) + if err != nil { + return err + } + hash := fmt.Sprintf("%x", md5.Sum(rawCfg)) + + // Decrypt and send. + decrypted, err := am.decryptConfiguration(ctx, cfg) + if err != nil { + return err + } + return am.sendConfiguration(ctx, decrypted, hash, time.Now().Unix(), false) } // SaveAndApplyDefaultConfig sends the default Grafana Alertmanager configuration to the remote Alertmanager. diff --git a/pkg/services/ngalert/remote/alertmanager_test.go b/pkg/services/ngalert/remote/alertmanager_test.go index 9d812551331..188889fd56b 100644 --- a/pkg/services/ngalert/remote/alertmanager_test.go +++ b/pkg/services/ngalert/remote/alertmanager_test.go @@ -145,7 +145,7 @@ func TestApplyConfig(t *testing.T) { // The encrypted configuration should be different than the one we will send. encryptedConfig, err := json.Marshal(c) require.NoError(t, err) - require.NotEqual(t, testGrafanaConfig, encryptedConfig) + require.NotEqual(t, testGrafanaConfigWithSecret, encryptedConfig) // ApplyConfig performs a readiness check at startup. // A non-200 response should result in an error. @@ -316,7 +316,7 @@ func TestIntegrationRemoteAlertmanagerConfiguration(t *testing.T) { require.NoError(t, store.Set(ctx, cfg.OrgID, "alertmanager", notifier.SilencesFilename, testSilence1)) require.NoError(t, store.Set(ctx, cfg.OrgID, "alertmanager", notifier.NotificationLogFilename, testNflog1)) - secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore()) + secretsService := secretsManager.SetupTestService(t, database.ProvideSecretsStore(db.InitTestDB(t))) m := metrics.NewRemoteAlertmanagerMetrics(prometheus.NewRegistry()) am, err := NewAlertmanager(cfg, fstore, secretsService.Decrypt, defaultGrafanaConfig, m) require.NoError(t, err) @@ -386,6 +386,36 @@ func TestIntegrationRemoteAlertmanagerConfiguration(t *testing.T) { require.Equal(t, encodedFullState, state.State) } + // `SaveAndApplyConfig` is called whenever a user manually changes the Alertmanager configuration. + // Calling this method should decrypt and send a configuration to the remote Alertmanager. + { + postableCfg, err := notifier.Load([]byte(testGrafanaConfigWithSecret)) + require.NoError(t, err) + err = notifier.EncryptReceiverConfigs(postableCfg.AlertmanagerConfig.Receivers, func(ctx context.Context, payload []byte) ([]byte, error) { + return secretsService.Encrypt(ctx, payload, secrets.WithoutScope()) + }) + require.NoError(t, err) + + // The encrypted configuration should be different than the one we will send. + encryptedConfig, err := json.Marshal(postableCfg) + require.NoError(t, err) + require.NotEqual(t, testGrafanaConfigWithSecret, encryptedConfig) + + // Call `SaveAndApplyConfig` with the encrypted configuration. + require.NoError(t, err) + require.NoError(t, am.SaveAndApplyConfig(ctx, postableCfg)) + + // Check that the configuration was uploaded to the remote Alertmanager. + config, err := am.mimirClient.GetGrafanaAlertmanagerConfig(ctx) + require.NoError(t, err) + got, err := json.Marshal(config.GrafanaAlertmanagerConfig) + require.NoError(t, err) + + require.JSONEq(t, testGrafanaConfigWithSecret, string(got)) + require.Equal(t, fmt.Sprintf("%x", md5.Sum(encryptedConfig)), config.Hash) + require.False(t, config.Default) + } + // `SaveAndApplyDefaultConfig` should send the default Alertmanager configuration to the remote Alertmanager. { require.NoError(t, am.SaveAndApplyDefaultConfig(ctx))