From 4a124469fa80724917e35201295d23ca9f1bb8e7 Mon Sep 17 00:00:00 2001 From: Tito Lins Date: Fri, 23 Aug 2024 11:22:06 +0200 Subject: [PATCH] Check is config is default by comparing hashes (#92296) --- pkg/services/ngalert/remote/alertmanager.go | 19 ++++++- .../ngalert/remote/alertmanager_test.go | 49 ++++++++++++++++++- 2 files changed, 65 insertions(+), 3 deletions(-) diff --git a/pkg/services/ngalert/remote/alertmanager.go b/pkg/services/ngalert/remote/alertmanager.go index e5c10056221..48350b02ff1 100644 --- a/pkg/services/ngalert/remote/alertmanager.go +++ b/pkg/services/ngalert/remote/alertmanager.go @@ -263,7 +263,24 @@ func (am *Alertmanager) CompareAndSendConfiguration(ctx context.Context, config if !am.shouldSendConfig(ctx, decrypted) { return nil } - return am.sendConfiguration(ctx, decrypted, config.ConfigurationHash, config.CreatedAt, config.Default) + + isDefault, err := am.isDefaultConfiguration(decrypted) + if err != nil { + return err + } + + return am.sendConfiguration(ctx, decrypted, config.ConfigurationHash, config.CreatedAt, isDefault) +} + +func (am *Alertmanager) isDefaultConfiguration(cfg *apimodels.PostableUserConfig) (bool, error) { + rawCfg, err := json.Marshal(cfg) + if err != nil { + return false, err + } + + configHash := fmt.Sprintf("%x", md5.Sum(rawCfg)) + + return configHash == am.defaultConfigHash, nil } func (am *Alertmanager) decryptConfiguration(ctx context.Context, cfg *apimodels.PostableUserConfig) (*apimodels.PostableUserConfig, error) { diff --git a/pkg/services/ngalert/remote/alertmanager_test.go b/pkg/services/ngalert/remote/alertmanager_test.go index 51e9361e5ab..5d6b9644e34 100644 --- a/pkg/services/ngalert/remote/alertmanager_test.go +++ b/pkg/services/ngalert/remote/alertmanager_test.go @@ -44,8 +44,9 @@ import ( const ( // Valid Grafana Alertmanager configurations. - testGrafanaConfig = `{"template_files":{},"alertmanager_config":{"route":{"receiver":"grafana-default-email","group_by":["grafana_folder","alertname"]},"receivers":[{"name":"grafana-default-email","grafana_managed_receiver_configs":[{"uid":"","name":"some other name","type":"email","disableResolveMessage":false,"settings":{"addresses":"\u003cexample@email.com\u003e"}}]}]}}` - testGrafanaConfigWithSecret = `{"template_files":{},"alertmanager_config":{"route":{"receiver":"grafana-default-email","group_by":["grafana_folder","alertname"]},"receivers":[{"name":"grafana-default-email","grafana_managed_receiver_configs":[{"uid":"dde6ntuob69dtf","name":"WH","type":"webhook","disableResolveMessage":false,"settings":{"url":"http://localhost:8080","username":"test"},"secureSettings":{"password":"test"}}]}]}}` + testGrafanaConfig = `{"template_files":{},"alertmanager_config":{"route":{"receiver":"grafana-default-email","group_by":["grafana_folder","alertname"]},"receivers":[{"name":"grafana-default-email","grafana_managed_receiver_configs":[{"uid":"","name":"some other name","type":"email","disableResolveMessage":false,"settings":{"addresses":"\u003cexample@email.com\u003e"}}]}]}}` + testGrafanaConfigWithSecret = `{"template_files":{},"alertmanager_config":{"route":{"receiver":"grafana-default-email","group_by":["grafana_folder","alertname"]},"receivers":[{"name":"grafana-default-email","grafana_managed_receiver_configs":[{"uid":"dde6ntuob69dtf","name":"WH","type":"webhook","disableResolveMessage":false,"settings":{"url":"http://localhost:8080","username":"test"},"secureSettings":{"password":"test"}}]}]}}` + testGrafanaDefaultConfigWithDifferentFieldOrder = `{"alertmanager_config":{"route":{"group_by":["alertname","grafana_folder"],"receiver":"grafana-default-email"},"receivers":[{"grafana_managed_receiver_configs":[{"uid":"","name":"email receiver","type":"email","settings":{"addresses":""}}],"name":"grafana-default-email"}]}}` // Valid Alertmanager state base64 encoded. testSilence1 = "lwEKhgEKATESFxIJYWxlcnRuYW1lGgp0ZXN0X2FsZXJ0EiMSDmdyYWZhbmFfZm9sZGVyGhF0ZXN0X2FsZXJ0X2ZvbGRlchoMCN2CkbAGEJbKrMsDIgwI7Z6RsAYQlsqsywMqCwiAkrjDmP7///8BQgxHcmFmYW5hIFRlc3RKDFRlc3QgU2lsZW5jZRIMCO2ekbAGEJbKrMsD" @@ -334,6 +335,50 @@ func TestCompareAndSendConfiguration(t *testing.T) { } } +func Test_isDefaultConfiguration(t *testing.T) { + parsedDefaultConfig, _ := notifier.Load([]byte(defaultGrafanaConfig)) + parsedTestConfig, _ := notifier.Load([]byte(testGrafanaConfig)) + parsedDefaultConfigWithDifferentFieldOrder, _ := notifier.Load([]byte(testGrafanaDefaultConfigWithDifferentFieldOrder)) + rawDefaultCfg, _ := json.Marshal(parsedDefaultConfig) + + tests := []struct { + name string + config *apimodels.PostableUserConfig + expected bool + }{ + { + "empty configuration", + nil, + false, + }, + { + "valid configuration", + parsedTestConfig, + false, + }, + { + "default configuration", + parsedDefaultConfig, + true, + }, + { + "default configuration with different field order", + parsedDefaultConfigWithDifferentFieldOrder, + false, + }, + } + for _, test := range tests { + t.Run(test.name, func(tt *testing.T) { + am := &Alertmanager{ + defaultConfig: string(rawDefaultCfg), + defaultConfigHash: fmt.Sprintf("%x", md5.Sum(rawDefaultCfg)), + } + isDefault, _ := am.isDefaultConfiguration(test.config) + require.Equal(tt, test.expected, isDefault) + }) + } +} + func TestIntegrationRemoteAlertmanagerConfiguration(t *testing.T) { if testing.Short() { t.Skip("skipping integration test")