From d172c58a1aa27bb1c8bdbcb29658f4762b1c1cec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Mierzwa?= Date: Mon, 1 Nov 2021 10:46:25 +0000 Subject: [PATCH] feat(backend): add labels:order config option Fixes #3500 --- CHANGELOG.md | 1 + .../testscript/059_log_full_config_env.txt | 12 +++- .../testscript/060_log_full_config_file.txt | 6 ++ .../testscript/065_proxy-with-readonly.txt | 1 + cmd/karma/tests/testscript/066_proxy.txt | 1 + cmd/karma/tests/testscript/067_readonly.txt | 1 + cmd/karma/tests/testscript/068_sentry.txt | 1 + .../tests/testscript/070_upper_case_keys.txt | 1 + .../tests/testscript/097_proxy_url_config.txt | 1 + demo/karma.yaml | 3 + docs/CONFIGURATION.md | 4 ++ internal/config/config.go | 3 + internal/config/config_test.go | 2 + internal/config/models.go | 1 + internal/models/alert.go | 15 +++++ internal/models/alert_test.go | 57 ++++++++++++++++--- internal/models/annotation.go | 3 +- 17 files changed, 102 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d63a69573..eafeaf734 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ - Loading user groups from HTTP headers, #3361 (@supertassu). - Added `labels:keep_re`, `labels:strip_re` and `labels:valueOnly_re` config options #3659 (@aalexk). +- `labels:order` config option to allow customising order of labels #3500. ### Changed diff --git a/cmd/karma/tests/testscript/059_log_full_config_env.txt b/cmd/karma/tests/testscript/059_log_full_config_env.txt index 91bb007ea..604a9c4b0 100644 --- a/cmd/karma/tests/testscript/059_log_full_config_env.txt +++ b/cmd/karma/tests/testscript/059_log_full_config_env.txt @@ -38,6 +38,7 @@ env GRID_GROUPLIMIT=1 env KARMA_NAME=karma-demo +env LABELS_ORDER='job instance' env LABELS_COLOR_STATIC=job env LABELS_COLOR_UNIQUE='@receiver instance cluster' env LABELS_KEEP='keep1 keep2' @@ -45,6 +46,7 @@ env LABELS_KEEP_RE='keep_re1 keep_re2' env LABELS_STRIP='strip1 strip2' env LABELS_STRIP_RE='strip_re1 strip_re2' env LABELS_VALUEONLY='value1 value2' +env LABELS_VALUEONLY_RE='foo.+' env LISTEN_ADDRESS=127.0.0.1 env LISTEN_PORT=1234 @@ -169,6 +171,9 @@ level=info msg=" rewrite: []" level=info msg="karma:" level=info msg=" name: karma-demo" level=info msg="labels:" +level=info msg=" order:" +level=info msg=" - job" +level=info msg=" - instance" level=info msg=" keep:" level=info msg=" - keep1" level=info msg=" - keep2" @@ -181,8 +186,11 @@ level=info msg=" - strip2" level=info msg=" strip_re:" level=info msg=" - strip_re1" level=info msg=" - strip_re2" -level=info msg=" valueOnly: []" -level=info msg=" valueOnly_re: []" +level=info msg=" valueOnly:" +level=info msg=" - value1" +level=info msg=" - value2" +level=info msg=" valueOnly_re:" +level=info msg=" - foo.+" level=info msg=" color:" level=info msg=" custom: {}" level=info msg=" static:" diff --git a/cmd/karma/tests/testscript/060_log_full_config_file.txt b/cmd/karma/tests/testscript/060_log_full_config_file.txt index e22a83f65..c93e40acf 100644 --- a/cmd/karma/tests/testscript/060_log_full_config_file.txt +++ b/cmd/karma/tests/testscript/060_log_full_config_file.txt @@ -181,6 +181,9 @@ level=info msg=" uri: https://prod-$1.example.com" level=info msg="karma:" level=info msg=" name: karma-demo" level=info msg="labels:" +level=info msg=" order:" +level=info msg=" - order1" +level=info msg=" - order2" level=info msg=" keep:" level=info msg=" - keep1" level=info msg=" - keep2" @@ -397,6 +400,9 @@ history: karma: name: karma-demo labels: + order: + - order1 + - order2 keep: - keep1 - keep2 diff --git a/cmd/karma/tests/testscript/065_proxy-with-readonly.txt b/cmd/karma/tests/testscript/065_proxy-with-readonly.txt index dba329c2f..1654f1521 100644 --- a/cmd/karma/tests/testscript/065_proxy-with-readonly.txt +++ b/cmd/karma/tests/testscript/065_proxy-with-readonly.txt @@ -82,6 +82,7 @@ level=info msg=" rewrite: []" level=info msg="karma:" level=info msg=" name: karma" level=info msg="labels:" +level=info msg=" order: []" level=info msg=" keep: []" level=info msg=" keep_re: []" level=info msg=" strip: []" diff --git a/cmd/karma/tests/testscript/066_proxy.txt b/cmd/karma/tests/testscript/066_proxy.txt index c61ca554f..ece56cbff 100644 --- a/cmd/karma/tests/testscript/066_proxy.txt +++ b/cmd/karma/tests/testscript/066_proxy.txt @@ -82,6 +82,7 @@ level=info msg=" rewrite: []" level=info msg="karma:" level=info msg=" name: karma" level=info msg="labels:" +level=info msg=" order: []" level=info msg=" keep: []" level=info msg=" keep_re: []" level=info msg=" strip: []" diff --git a/cmd/karma/tests/testscript/067_readonly.txt b/cmd/karma/tests/testscript/067_readonly.txt index 968ebc09e..d930c8d93 100644 --- a/cmd/karma/tests/testscript/067_readonly.txt +++ b/cmd/karma/tests/testscript/067_readonly.txt @@ -82,6 +82,7 @@ level=info msg=" rewrite: []" level=info msg="karma:" level=info msg=" name: karma" level=info msg="labels:" +level=info msg=" order: []" level=info msg=" keep: []" level=info msg=" keep_re: []" level=info msg=" strip: []" diff --git a/cmd/karma/tests/testscript/068_sentry.txt b/cmd/karma/tests/testscript/068_sentry.txt index ce3126e10..3e03703bb 100644 --- a/cmd/karma/tests/testscript/068_sentry.txt +++ b/cmd/karma/tests/testscript/068_sentry.txt @@ -84,6 +84,7 @@ level=info msg=" rewrite: []" level=info msg="karma:" level=info msg=" name: karma" level=info msg="labels:" +level=info msg=" order: []" level=info msg=" keep: []" level=info msg=" keep_re: []" level=info msg=" strip: []" diff --git a/cmd/karma/tests/testscript/070_upper_case_keys.txt b/cmd/karma/tests/testscript/070_upper_case_keys.txt index a0c9c5977..aba887e3c 100644 --- a/cmd/karma/tests/testscript/070_upper_case_keys.txt +++ b/cmd/karma/tests/testscript/070_upper_case_keys.txt @@ -82,6 +82,7 @@ level=info msg=" rewrite: []" level=info msg="karma:" level=info msg=" name: karma" level=info msg="labels:" +level=info msg=" order: []" level=info msg=" keep: []" level=info msg=" keep_re: []" level=info msg=" strip: []" diff --git a/cmd/karma/tests/testscript/097_proxy_url_config.txt b/cmd/karma/tests/testscript/097_proxy_url_config.txt index c2afad100..3730bfe8f 100644 --- a/cmd/karma/tests/testscript/097_proxy_url_config.txt +++ b/cmd/karma/tests/testscript/097_proxy_url_config.txt @@ -82,6 +82,7 @@ level=info msg=" rewrite: []" level=info msg="karma:" level=info msg=" name: karma" level=info msg="labels:" +level=info msg=" order: []" level=info msg=" keep: []" level=info msg=" keep_re: []" level=info msg=" strip: []" diff --git a/demo/karma.yaml b/demo/karma.yaml index 9de010325..3aee78955 100644 --- a/demo/karma.yaml +++ b/demo/karma.yaml @@ -72,6 +72,9 @@ history: karma: name: karma-demo labels: + order: + - instance + - job color: static: - job diff --git a/docs/CONFIGURATION.md b/docs/CONFIGURATION.md index aef43314b..1cddd006a 100644 --- a/docs/CONFIGURATION.md +++ b/docs/CONFIGURATION.md @@ -859,6 +859,7 @@ labels: - value: string value_re: regex color: string + order: list of strings keep: list of strings keep_re: list of regex strip: list of strings @@ -891,6 +892,9 @@ labels: Note: this option is not available via environment variables, you can only set it via the config file. +- `order` - custom order of label names. All labels listed here will + appear first in the order specified here. Remaining labels will be sorted + alphabetically and appended at the end. - `keep` - list of allowed labels, if both `keep` and `keep_re` are empty all labels are allowed. - `keep_re` - list of Go compatible [regular expressions](https://golang.org/pkg/regexp/) diff --git a/internal/config/config.go b/internal/config/config.go index bdf6099af..1ef59626d 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -85,6 +85,7 @@ func SetupFlags(f *pflag.FlagSet) { f.StringSlice("filters.default", []string{}, "List of default filters") + f.StringSlice("labels.order", []string{}, "Preferred order of label names") f.StringSlice("labels.color.static", []string{}, "List of label names that should have the same (but distinct) color") f.StringSlice("labels.color.unique", []string{}, @@ -226,6 +227,8 @@ func readEnvVariables(k *koanf.Koanf) { return "labels.keep_re" case "LABELS_STRIP_RE": return "labels.strip_re" + case "LABELS_VALUEONLY": + return "labels.valueOnly" case "LABELS_VALUEONLY_RE": return "labels.valueOnly_re" case "SILENCEFORM_STRIP_LABELS": diff --git a/internal/config/config_test.go b/internal/config/config_test.go index c59a77ff1..09d5f028d 100644 --- a/internal/config/config_test.go +++ b/internal/config/config_test.go @@ -101,6 +101,7 @@ history: karma: name: another karma labels: + order: [] keep: - foo - bar @@ -376,6 +377,7 @@ func TestDefaultConfig(t *testing.T) { expectedConfig.Annotations.Strip = []string{} expectedConfig.Annotations.Actions = []string{} expectedConfig.Annotations.Order = []string{} + expectedConfig.Labels.Order = []string{} expectedConfig.Labels.Keep = []string{} expectedConfig.Labels.KeepRegex = []string{} expectedConfig.Labels.CompiledKeepRegex = []*regexp.Regexp{} diff --git a/internal/config/models.go b/internal/config/models.go index 91b02eea5..938388976 100644 --- a/internal/config/models.go +++ b/internal/config/models.go @@ -148,6 +148,7 @@ type configSchema struct { Name string } Labels struct { + Order []string Keep []string KeepRegex []string `yaml:"keep_re" koanf:"keep_re"` CompiledKeepRegex []*regexp.Regexp `yaml:"-"` diff --git a/internal/models/alert.go b/internal/models/alert.go index 196b50ce9..6683930ae 100644 --- a/internal/models/alert.go +++ b/internal/models/alert.go @@ -7,6 +7,7 @@ import ( "github.com/cnf/structhash" "github.com/fvbommel/sortorder" + "github.com/prymitive/karma/internal/config" ) // AlertStateUnprocessed means that Alertmanager notify didn't yet process it @@ -62,6 +63,20 @@ func (ls Labels) Swap(i, j int) { } func (ls Labels) Less(i, j int) bool { + ai, aj := -1, -1 + for index, name := range config.Config.Labels.Order { + if ls[i].Name == name { + ai = index + } else if ls[j].Name == name { + aj = index + } + if ai >= 0 && aj >= 0 { + return ai < aj + } + } + if ai != aj { + return aj < ai + } if ls[i].Name == ls[j].Name { return sortorder.NaturalLess(ls[i].Value, ls[j].Value) } diff --git a/internal/models/alert_test.go b/internal/models/alert_test.go index 6e1cf1f7c..a683ad4b8 100644 --- a/internal/models/alert_test.go +++ b/internal/models/alert_test.go @@ -1,10 +1,12 @@ package models_test import ( + "fmt" "sort" "testing" "github.com/google/go-cmp/cmp" + "github.com/prymitive/karma/internal/config" "github.com/prymitive/karma/internal/models" ) @@ -41,13 +43,15 @@ func TestLabelsSet(t *testing.T) { } type sortLabelsTestCase struct { - in models.Labels - out models.Labels + order []string + in models.Labels + out models.Labels } func TestSortLabels(t *testing.T) { testCases := []sortLabelsTestCase{ { + order: []string{}, in: models.Labels{ {Name: "foo", Value: "bar"}, }, @@ -56,6 +60,7 @@ func TestSortLabels(t *testing.T) { }, }, { + order: []string{}, in: models.Labels{ {Name: "foo", Value: "bar"}, {Name: "bar", Value: "foo"}, @@ -66,6 +71,7 @@ func TestSortLabels(t *testing.T) { }, }, { + order: []string{}, in: models.Labels{ {Name: "bar", Value: "foo"}, {Name: "foo", Value: "bar"}, @@ -76,6 +82,7 @@ func TestSortLabels(t *testing.T) { }, }, { + order: []string{}, in: models.Labels{ {Name: "foo", Value: "foo"}, {Name: "bar", Value: "foo"}, @@ -88,6 +95,7 @@ func TestSortLabels(t *testing.T) { }, }, { + order: []string{}, in: models.Labels{ {Name: "1", Value: "a12"}, {Name: "1", Value: "1"}, @@ -99,13 +107,46 @@ func TestSortLabels(t *testing.T) { {Name: "1", Value: "a12"}, }, }, + { + order: []string{"bar"}, + in: models.Labels{ + {Name: "baz", Value: "1"}, + {Name: "bar", Value: "1"}, + {Name: "foo", Value: "1"}, + }, + out: models.Labels{ + {Name: "bar", Value: "1"}, + {Name: "baz", Value: "1"}, + {Name: "foo", Value: "1"}, + }, + }, + { + order: []string{"foo", "bar"}, + in: models.Labels{ + {Name: "foo", Value: "a10"}, + {Name: "bar", Value: "1"}, + {Name: "foo", Value: "a3"}, + }, + out: models.Labels{ + {Name: "foo", Value: "a3"}, + {Name: "foo", Value: "a10"}, + {Name: "bar", Value: "1"}, + }, + }, } - for _, testCase := range testCases { - sort.Sort(testCase.in) - if diff := cmp.Diff(testCase.in, testCase.out); diff != "" { - t.Errorf("Incorrectly sorted labels (-want +got):\n%s", diff) - break - } + defer func() { + config.Config.Labels.Order = []string{} + }() + + for i, testCase := range testCases { + t.Run(fmt.Sprintf("[%d] order=%v", i, testCase.order), func(t *testing.T) { + config.Config.Labels.Order = testCase.order + sort.Sort(testCase.in) + if diff := cmp.Diff(testCase.in, testCase.out); diff != "" { + t.Errorf("Incorrectly sorted labels (-want +got):\n%s", diff) + t.FailNow() + } + }) } } diff --git a/internal/models/annotation.go b/internal/models/annotation.go index 437f63822..e73496d82 100644 --- a/internal/models/annotation.go +++ b/internal/models/annotation.go @@ -5,6 +5,7 @@ import ( "sort" "strings" + "github.com/fvbommel/sortorder" "github.com/prymitive/karma/internal/config" "github.com/prymitive/karma/internal/slices" ) @@ -50,7 +51,7 @@ func (a Annotations) Less(i, j int) bool { return aj < ai } // If neither annotation was in c.C.A.Order, sort alphabetically. - return a[i].Name < a[j].Name + return sortorder.NaturalLess(a[i].Name, a[j].Name) } // AnnotationsFromMap will convert a map[string]string to a list of Annotation