From 860375650e56752039e11920e1b36ad7135e855e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Mierzwa?= Date: Sat, 2 Dec 2017 21:51:31 -0800 Subject: [PATCH] Add --receivers.keep for consistency with other options there's --labels.keep so same should apply to receivers, add new option, tests and fixes as it wasn't working properly --- docs/CONFIGURATION.md | 19 +++++--- internal/alertmanager/dedup.go | 14 ++++-- internal/alertmanager/dedup_test.go | 13 +++++ internal/config/config.go | 3 ++ internal/config/models.go | 3 +- internal/transform/strip.go | 17 ++++--- internal/transform/strip_test.go | 75 +++++++++++++++++++++++++---- 7 files changed, 116 insertions(+), 28 deletions(-) diff --git a/docs/CONFIGURATION.md b/docs/CONFIGURATION.md index 7590df2b8..951fe4cf3 100644 --- a/docs/CONFIGURATION.md +++ b/docs/CONFIGURATION.md @@ -355,19 +355,19 @@ jira: [] ### Receivers `receivers` section allows configuring how alerts from different receivers are -handled by unsee. +handled by unsee. If alerts are routed to multiple receivers they can be +duplicated in the UI, each instance will have different value for `@receiver`. Syntax: ```yaml receivers: + keep: list of strings strip: list of strings ``` -* `strip` - list of receiver names that should be ignored when collecting data - from Alertmanager API. If alerts are routed to multiple receivers they can be - duplicated in the UI, each instance will have different value for `@receiver`. - This options allows to set a list of receivers that will not be shown in the - UI. +* `keep` - list of receivers name that are allowed, if empty all receivers are + allowed. +* `strip` - list of receiver names that will not be shown in the UI. Example where alerts that are routed to the `alertmanage2es` receiver are ignored by unsee. @@ -433,3 +433,10 @@ Exaceptions: Environment variables are mapped in a similiar way as command line flags, `alertmanager:interval` is accessible as `ALERTMANAGER_INTERVAL` env. Same exceptions apply as with command line flags. + +* `HOST` - used by gin webserver, same effect as setting `listen:address` config + option +* `PORT` - used by gin webserver, same effect as setting `listen:port` config + option +* `SENTRY_DSN` - is used by Sentry itself, same effect as passing value to + `sentry:private` config option. diff --git a/internal/alertmanager/dedup.go b/internal/alertmanager/dedup.go index 145dd868d..5886bcab6 100644 --- a/internal/alertmanager/dedup.go +++ b/internal/alertmanager/dedup.go @@ -31,6 +31,11 @@ func DedupAlerts() []models.AlertGroup { alerts := map[string]models.Alert{} for _, ag := range agList { for _, alert := range ag.Alerts { + // remove all alerts for receiver(s) that the user doesn't + // want to see in the UI + if transform.StripReceivers(config.Config.Receivers.Keep, config.Config.Receivers.Strip, alert.Receiver) { + continue + } alertLFP := alert.LabelsFingerprint() a, found := alerts[alertLFP] if found { @@ -60,14 +65,13 @@ func DedupAlerts() []models.AlertGroup { } } } + // skip empty groups + if len(alerts) == 0 { + continue + } ag := models.AlertGroup(agList[0]) ag.Alerts = models.AlertList{} for _, alert := range alerts { - // remove all alerts for receiver(s) that the user doesn't - // want to see in the UI - if transform.StripReceivers(config.Config.Receivers.Strip, alert.Receiver) { - continue - } // strip labels user doesn't want to see in the UI alert.Labels = transform.StripLables(config.Config.Labels.Keep, config.Config.Labels.Strip, alert.Labels) // calculate final alert state based on the most important value found diff --git a/internal/alertmanager/dedup_test.go b/internal/alertmanager/dedup_test.go index 068d956dd..a580a0280 100644 --- a/internal/alertmanager/dedup_test.go +++ b/internal/alertmanager/dedup_test.go @@ -99,3 +99,16 @@ func TestDedupColors(t *testing.T) { t.Errorf("Expected %d color keys, got %d", expected, len(colors)) } } + +func TestStripReceivers(t *testing.T) { + os.Setenv("RECEIVERS_STRIP", "by-name by-cluster-service") + os.Setenv("ALERTMANAGER_URI", "http://localhost") + config.Config.Read() + if err := pullAlerts(); err != nil { + t.Error(err) + } + alerts := alertmanager.DedupAlerts() + if len(alerts) > 0 { + t.Errorf("Expected no alerts after stripping all receivers, got %d", len(alerts)) + } +} diff --git a/internal/config/config.go b/internal/config/config.go index 5927afafc..573065772 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -58,6 +58,8 @@ func init() { pflag.String("log.level", "info", "Log level, one of: debug, info, warning, error, fatal and panic") + pflag.StringSlice("receivers.keep", []string{}, + "List of receivers to keep, all alerts with different receivers will be ignored") pflag.StringSlice("receivers.strip", []string{}, "List of receivers to not display alerts for") @@ -121,6 +123,7 @@ func (config *configSchema) Read() { config.Listen.Prefix = v.GetString("listen.prefix") config.Log.Config = v.GetBool("log.config") config.Log.Level = v.GetString("log.level") + config.Receivers.Keep = v.GetStringSlice("receivers.keep") config.Receivers.Strip = v.GetStringSlice("receivers.strip") config.Sentry.Private = v.GetString("sentry.private") config.Sentry.Public = v.GetString("sentry.public") diff --git a/internal/config/models.go b/internal/config/models.go index 13194624e..d418ef9e7 100644 --- a/internal/config/models.go +++ b/internal/config/models.go @@ -36,8 +36,8 @@ type configSchema struct { Default []string } Labels struct { - Strip []string Keep []string + Strip []string } Listen struct { Address string @@ -50,6 +50,7 @@ type configSchema struct { } JIRA []jiraRule Receivers struct { + Keep []string Strip []string } Sentry struct { diff --git a/internal/transform/strip.go b/internal/transform/strip.go index 70309327e..c9bc35c96 100644 --- a/internal/transform/strip.go +++ b/internal/transform/strip.go @@ -28,11 +28,16 @@ func StripLables(keptLabels, ignoredLabels []string, sourceLabels map[string]str } // StripReceivers allows filtering all alerts for specified receiver(s) -func StripReceivers(ignoredReceivers []string, alertReceiver string) bool { - for _, ignoredReceiver := range ignoredReceivers { - if alertReceiver == ignoredReceiver { - return true - } +// it will return true if alert uses receiver that should be stripped +func StripReceivers(keptReceivers, ignoredReceivers []string, alertReceiver string) bool { + // true if we keep by default + keepAll := len(keptReceivers) == 0 + // is this receiver on the whitelist ? + inKeep := slices.StringInSlice(keptReceivers, alertReceiver) + // is this receiver on the blacklist ? + inStrip := slices.StringInSlice(ignoredReceivers, alertReceiver) + if (keepAll || inKeep) && !inStrip { + return false } - return false + return true } diff --git a/internal/transform/strip_test.go b/internal/transform/strip_test.go index a73f8d8f6..8009e42df 100644 --- a/internal/transform/strip_test.go +++ b/internal/transform/strip_test.go @@ -7,15 +7,15 @@ import ( "github.com/cloudflare/unsee/internal/transform" ) -type stripTest struct { +type stripLabelTest struct { strip []string keep []string before map[string]string after map[string]string } -var stripTests = []stripTest{ - stripTest{ +var stripLabelTests = []stripLabelTest{ + stripLabelTest{ strip: []string{"env"}, keep: []string{}, before: map[string]string{ @@ -28,7 +28,7 @@ var stripTests = []stripTest{ "level": "info", }, }, - stripTest{ + stripLabelTest{ strip: []string{"server"}, keep: []string{}, before: map[string]string{ @@ -42,7 +42,7 @@ var stripTests = []stripTest{ "level": "info", }, }, - stripTest{ + stripLabelTest{ strip: []string{}, keep: []string{}, before: map[string]string{ @@ -56,7 +56,7 @@ var stripTests = []stripTest{ "level": "info", }, }, - stripTest{ + stripLabelTest{ strip: []string{"host"}, keep: []string{}, before: map[string]string{ @@ -64,7 +64,7 @@ var stripTests = []stripTest{ }, after: map[string]string{}, }, - stripTest{ + stripLabelTest{ strip: []string{}, keep: []string{"env"}, before: map[string]string{ @@ -76,7 +76,7 @@ var stripTests = []stripTest{ "env": "production", }, }, - stripTest{ + stripLabelTest{ strip: []string{"env"}, keep: []string{"host"}, before: map[string]string{ @@ -88,7 +88,7 @@ var stripTests = []stripTest{ "host": "localhost", }, }, - stripTest{ + stripLabelTest{ strip: []string{}, keep: []string{"env"}, before: map[string]string{ @@ -100,10 +100,65 @@ var stripTests = []stripTest{ } func TestStripLables(t *testing.T) { - for _, testCase := range stripTests { + for _, testCase := range stripLabelTests { labels := transform.StripLables(testCase.keep, testCase.strip, testCase.before) if !reflect.DeepEqual(labels, testCase.after) { t.Errorf("StripLables failed, expected %v, got %v", testCase.after, labels) } } } + +type stripReceiverTest struct { + strip []string + keep []string + receiver string + stripped bool +} + +var stripReceiverTests = []stripReceiverTest{ + stripReceiverTest{ + strip: []string{}, + keep: []string{}, + receiver: "default", + stripped: false, + }, + stripReceiverTest{ + strip: []string{"default"}, + keep: []string{}, + receiver: "default", + stripped: true, + }, + stripReceiverTest{ + strip: []string{"default"}, + keep: []string{"default"}, + receiver: "default", + stripped: true, + }, + stripReceiverTest{ + strip: []string{}, + keep: []string{"default"}, + receiver: "default", + stripped: false, + }, + stripReceiverTest{ + strip: []string{"foo", "bar"}, + keep: []string{}, + receiver: "default", + stripped: false, + }, + stripReceiverTest{ + strip: []string{"foo", "default"}, + keep: []string{"foo", "bar"}, + receiver: "default", + stripped: true, + }, +} + +func TestStripReceivers(t *testing.T) { + for _, testCase := range stripReceiverTests { + stripped := transform.StripReceivers(testCase.keep, testCase.strip, testCase.receiver) + if stripped != testCase.stripped { + t.Errorf("StripReceivers failed, expected %v, got %v", testCase.stripped, stripped) + } + } +}