From 549ff9eefd7484dfd69ca46493f1b58bd8972b2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Mierzwa?= Date: Thu, 10 Aug 2017 22:08:12 -0700 Subject: [PATCH 1/3] Add KEEP_LABELS option for whitelisting labels This allows to provide a list of labels that are the only labels allowed in the UI, reversing the logic of STRIP_LABELS. Fixes #170 --- README.md | 15 ++++++++++++ internal/alertmanager/models.go | 2 +- internal/config/config.go | 1 + internal/transform/strip.go | 10 ++++++-- internal/transform/strip_test.go | 40 +++++++++++++++++++++++++++++++- 5 files changed, 64 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 402bb3608..6fa4be97c 100644 --- a/README.md +++ b/README.md @@ -281,6 +281,21 @@ This option can also be set using `-jira.regex` flag. Example: This variable is optional and default is not set (no rule will be applied). +#### KEEP_LABELS + +List of label names to show on the UI, all other labels will be stripped. +This allows to hide all labels except selected few that are useful on the +alert dashboard. Accepts space separated list of label names. Examples: + + KEEP_LABELS=instance + KEEP_LABELS="host severity" + +This option can also be set using `-keep.labels` flag. Example: + + $ unsee -keep.labels "host severity" + +This variable is optional and default is not set (all labels will be shown). + #### PORT HTTP port to listen on. Example: diff --git a/internal/alertmanager/models.go b/internal/alertmanager/models.go index ba6149e04..65b638e6a 100644 --- a/internal/alertmanager/models.go +++ b/internal/alertmanager/models.go @@ -174,7 +174,7 @@ func (am *Alertmanager) pullAlerts(version string) error { } alert.Annotations, alert.Links = transform.DetectLinks(alert.Annotations) - alert.Labels = transform.StripLables(config.Config.StripLabels, alert.Labels) + alert.Labels = transform.StripLables(config.Config.KeepLabels, config.Config.StripLabels, alert.Labels) transform.ColorLabel(colors, "@receiver", alert.Receiver) for k, v := range alert.Labels { diff --git a/internal/config/config.go b/internal/config/config.go index 9ffed4ca0..9a503448b 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -36,6 +36,7 @@ type configEnvs struct { SentryDSN string `envconfig:"SENTRY_DSN" help:"Sentry DSN for Go exceptions"` SentryPublicDSN string `envconfig:"SENTRY_PUBLIC_DSN" help:"Sentry DSN for javascript exceptions"` StripLabels spaceSeparatedList `envconfig:"STRIP_LABELS" help:"List of labels to ignore"` + KeepLabels spaceSeparatedList `envconfig:"KEEP_LABELS" help:"List of labels to keep, all other labels will be stripped"` WebPrefix string `envconfig:"WEB_PREFIX" default:"/" help:"URL prefix"` } diff --git a/internal/transform/strip.go b/internal/transform/strip.go index 91b9c71c4..7e25a115d 100644 --- a/internal/transform/strip.go +++ b/internal/transform/strip.go @@ -9,10 +9,16 @@ import ( // StripLables allows filtering out some labels from alerts // it takes the list of label keys to ignore and alert label map // it will return label map without labels found on the ignore list -func StripLables(ignoredLabels []string, sourceLabels map[string]string) map[string]string { +func StripLables(keptLabels, ignoredLabels []string, sourceLabels map[string]string) map[string]string { + // empty keep list means keep everything by default + keepAll := len(keptLabels) == 0 labels := map[string]string{} for label, value := range sourceLabels { - if !slices.StringInSlice(ignoredLabels, label) { + // is explicitly marked to be kept + inKeep := slices.StringInSlice(keptLabels, label) + // is explicitly marked to be stripped + inStrip := slices.StringInSlice(ignoredLabels, label) + if (keepAll || inKeep) && !inStrip { // strip leading and trailung space in label value // this is to normalize values in case space is added by Alertmanager rules labels[label] = strings.TrimSpace(value) diff --git a/internal/transform/strip_test.go b/internal/transform/strip_test.go index 2152588ea..a73f8d8f6 100644 --- a/internal/transform/strip_test.go +++ b/internal/transform/strip_test.go @@ -9,6 +9,7 @@ import ( type stripTest struct { strip []string + keep []string before map[string]string after map[string]string } @@ -16,6 +17,7 @@ type stripTest struct { var stripTests = []stripTest{ stripTest{ strip: []string{"env"}, + keep: []string{}, before: map[string]string{ "host": "localhost", "env": "production", @@ -28,6 +30,7 @@ var stripTests = []stripTest{ }, stripTest{ strip: []string{"server"}, + keep: []string{}, before: map[string]string{ "host": "localhost", "env": "production", @@ -41,6 +44,7 @@ var stripTests = []stripTest{ }, stripTest{ strip: []string{}, + keep: []string{}, before: map[string]string{ "host": "localhost", "env": "production", @@ -54,16 +58,50 @@ var stripTests = []stripTest{ }, stripTest{ strip: []string{"host"}, + keep: []string{}, before: map[string]string{ "host": "localhost", }, after: map[string]string{}, }, + stripTest{ + strip: []string{}, + keep: []string{"env"}, + before: map[string]string{ + "host": "localhost", + "env": "production", + "level": "info", + }, + after: map[string]string{ + "env": "production", + }, + }, + stripTest{ + strip: []string{"env"}, + keep: []string{"host"}, + before: map[string]string{ + "host": "localhost", + "env": "production", + "level": "info", + }, + after: map[string]string{ + "host": "localhost", + }, + }, + stripTest{ + strip: []string{}, + keep: []string{"env"}, + before: map[string]string{ + "host": "localhost", + "level": "info", + }, + after: map[string]string{}, + }, } func TestStripLables(t *testing.T) { for _, testCase := range stripTests { - labels := transform.StripLables(testCase.strip, testCase.before) + 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) } From d1d4a854098884fbcfe818cd3859e309d3692616 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Mierzwa?= Date: Thu, 10 Aug 2017 22:22:01 -0700 Subject: [PATCH 2/3] Add a test for total number of alerts we get from mock, should be 24 --- internal/alertmanager/dedup_test.go | 30 +++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/internal/alertmanager/dedup_test.go b/internal/alertmanager/dedup_test.go index 1ed366400..bf8b424ab 100644 --- a/internal/alertmanager/dedup_test.go +++ b/internal/alertmanager/dedup_test.go @@ -35,9 +35,39 @@ func TestDedupAlerts(t *testing.T) { t.Error(err) } alertGroups := alertmanager.DedupAlerts() + if len(alertGroups) != 10 { t.Errorf("Expected %d alert groups, got %d", 10, len(alertGroups)) } + + totalAlerts := 0 + for _, ag := range alertGroups { + totalAlerts += len(ag.Alerts) + } + if totalAlerts != 24 { + t.Errorf("Expected %d total alerts, got %d", 24, totalAlerts) + } +} + +func TestDedupAlertsWithoutLabels(t *testing.T) { + config.Config.KeepLabels = []string{"xyz"} + if err := pullAlerts(); err != nil { + t.Error(err) + } + alertGroups := alertmanager.DedupAlerts() + config.Config.KeepLabels = []string{} + + if len(alertGroups) != 10 { + t.Errorf("Expected %d alert groups, got %d", 10, len(alertGroups)) + } + + totalAlerts := 0 + for _, ag := range alertGroups { + totalAlerts += len(ag.Alerts) + } + if totalAlerts != 24 { + t.Errorf("Expected %d total alerts, got %d", 24, totalAlerts) + } } func TestDedupAutocomplete(t *testing.T) { From 3608ccba216adef113208532789f8cfa59a2a599 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Mierzwa?= Date: Thu, 10 Aug 2017 22:23:47 -0700 Subject: [PATCH 3/3] Strip labels in dedup, not on pull Strippling labels should be done after alerts are deduplicated, when user requests some alerts with a filter, not during data collection from Alertmanager. When labels are stripped before dedup then they are merged into one, because labels are needed to calculate uniqueness, which means we will return wrong data --- internal/alertmanager/dedup.go | 4 ++++ internal/alertmanager/models.go | 2 -- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/internal/alertmanager/dedup.go b/internal/alertmanager/dedup.go index b20a1545a..8b2c55b32 100644 --- a/internal/alertmanager/dedup.go +++ b/internal/alertmanager/dedup.go @@ -3,8 +3,10 @@ package alertmanager import ( "sort" + "github.com/cloudflare/unsee/internal/config" "github.com/cloudflare/unsee/internal/models" "github.com/cloudflare/unsee/internal/slices" + "github.com/cloudflare/unsee/internal/transform" ) // DedupAlerts will collect alert groups from all defined Alertmanager @@ -61,6 +63,8 @@ func DedupAlerts() []models.AlertGroup { ag := models.AlertGroup(agList[0]) ag.Alerts = models.AlertList{} for _, alert := range alerts { + // strip labels user doesn't want to see in the UI + alert.Labels = transform.StripLables(config.Config.KeepLabels, config.Config.StripLabels, alert.Labels) // calculate final alert state based on the most important value found // in the list of states from all instances alertLFP := alert.LabelsFingerprint() diff --git a/internal/alertmanager/models.go b/internal/alertmanager/models.go index 65b638e6a..c1714d4a9 100644 --- a/internal/alertmanager/models.go +++ b/internal/alertmanager/models.go @@ -6,7 +6,6 @@ import ( "sync" "time" - "github.com/cloudflare/unsee/internal/config" "github.com/cloudflare/unsee/internal/mapper" "github.com/cloudflare/unsee/internal/models" "github.com/cloudflare/unsee/internal/transform" @@ -174,7 +173,6 @@ func (am *Alertmanager) pullAlerts(version string) error { } alert.Annotations, alert.Links = transform.DetectLinks(alert.Annotations) - alert.Labels = transform.StripLables(config.Config.KeepLabels, config.Config.StripLabels, alert.Labels) transform.ColorLabel(colors, "@receiver", alert.Receiver) for k, v := range alert.Labels {