From 0c26d8ac4f961581d5a10e731af936c430116615 Mon Sep 17 00:00:00 2001 From: Herman Date: Tue, 25 Apr 2023 19:46:47 +0200 Subject: [PATCH] feat: add support for static jira labels (#154) Signed-off-by: Herman Ewert Co-authored-by: Herman Ewert --- examples/jiralert.yml | 6 ++++- pkg/config/config.go | 4 +++ pkg/config/config_test.go | 53 +++++++++++++++++++++++++++++++++++---- pkg/notify/notify.go | 4 ++- pkg/notify/notify_test.go | 41 ++++++++++++++++++++++++++++++ 5 files changed, 101 insertions(+), 7 deletions(-) diff --git a/examples/jiralert.yml b/examples/jiralert.yml index a6713de..f270338 100644 --- a/examples/jiralert.yml +++ b/examples/jiralert.yml @@ -6,7 +6,7 @@ defaults: user: jiralert password: 'JIRAlert' - # Exclude labels in JIRA issue. + # Exclude labels in JIRA issue. exclude_keys: [] # The type of JIRA issue to create. Required. @@ -26,6 +26,8 @@ defaults: # Amount of time after being closed that an issue should be reopened, after which, a new issue is created. # Optional (default: always reopen) reopen_duration: 0h + # Static label that will be added to the JIRA ticket alongisde the JIRALERT{...} or ALERT{...} label + static_labels: ["custom"] # Receiver definitions. At least one must be defined. receivers: @@ -37,6 +39,8 @@ receivers: add_group_labels: false exclude_keys: - pod + # Will be merged with the static_labels from the default map + static_labels: ["anotherLabel"] - name: 'jira-xy' project: XY diff --git a/pkg/config/config.go b/pkg/config/config.go index fc0adac..307965d 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -145,6 +145,7 @@ type ReceiverConfig struct { WontFixResolution string `yaml:"wont_fix_resolution" json:"wont_fix_resolution"` Fields map[string]interface{} `yaml:"fields" json:"fields"` Components []string `yaml:"components" json:"components"` + StaticLabels []string `yaml:"static_labels" json:"static_labels"` // ExcludeKeys settings ExcludeKeys []string `yaml:"exclude_keys" json:"exclude_keys"` @@ -326,6 +327,9 @@ func (c *Config) UnmarshalYAML(unmarshal func(interface{}) error) error { } } } + if len(c.Defaults.StaticLabels) > 0 { + rc.StaticLabels = append(rc.StaticLabels, c.Defaults.StaticLabels...) + } } if len(c.Receivers) == 0 { diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go index 3bc7de7..d851f72 100644 --- a/pkg/config/config_test.go +++ b/pkg/config/config_test.go @@ -46,6 +46,7 @@ defaults: # Amount of time after being closed that an issue should be reopened, after which, a new issue is created. # Optional (default: always reopen) reopen_duration: 0h + static_labels: ["defaultlabel"] # Receiver definitions. At least one must be defined. receivers: @@ -55,6 +56,7 @@ receivers: project: AB # Copy all Prometheus labels into separate JIRA labels. Optional (default: false). add_group_labels: false + static_labels: ["somelabel"] - name: 'jira-xy' project: XY @@ -122,10 +124,11 @@ type receiverTestConfig struct { ReopenState string `yaml:"reopen_state,omitempty"` ReopenDuration string `yaml:"reopen_duration,omitempty"` - Priority string `yaml:"priority,omitempty"` - Description string `yaml:"description,omitempty"` - WontFixResolution string `yaml:"wont_fix_resolution,omitempty"` - AddGroupLabels bool `yaml:"add_group_labels,omitempty"` + Priority string `yaml:"priority,omitempty"` + Description string `yaml:"description,omitempty"` + WontFixResolution string `yaml:"wont_fix_resolution,omitempty"` + AddGroupLabels bool `yaml:"add_group_labels,omitempty"` + StaticLabels []string `yaml:"static_labels" json:"static_labels"` AutoResolve *AutoResolve `yaml:"auto_resolve" json:"auto_resolve"` @@ -328,8 +331,9 @@ func TestReceiverOverrides(t *testing.T) { {"WontFixResolution", "Won't Fix", "Won't Fix"}, {"AddGroupLabels", false, false}, {"AutoResolve", &AutoResolve{State: "Done"}, &autoResolve}, + {"StaticLabels", []string{"somelabel"}, []string{"somelabel"}}, } { - optionalFields := []string{"Priority", "Description", "WontFixResolution", "AddGroupLabels", "AutoResolve"} + optionalFields := []string{"Priority", "Description", "WontFixResolution", "AddGroupLabels", "AutoResolve", "StaticLabels"} defaultsConfig := newReceiverTestConfig(mandatoryReceiverFields(), optionalFields) receiverConfig := newReceiverTestConfig([]string{"Name"}, optionalFields) @@ -383,6 +387,8 @@ func newReceiverTestConfig(mandatory []string, optional []string) *receiverTestC value = reflect.ValueOf(true) } else if name == "AutoResolve" { value = reflect.ValueOf(&AutoResolve{State: "Done"}) + } else if name == "StaticLabels" { + value = reflect.ValueOf([]string{}) } else { value = reflect.ValueOf(name) } @@ -459,3 +465,40 @@ func TestAutoResolveConfigDefault(t *testing.T) { configErrorTestRunner(t, config, "bad config in defaults section: state cannot be empty") } + +func TestStaticLabelsConfigMerge(t *testing.T) { + + for i, test := range []struct { + defaultValue []string + receiverValue []string + expectedElements []string + }{ + {[]string{"defaultlabel"}, []string{"receiverlabel"}, []string{"defaultlabel", "receiverlabel"}}, + {[]string{}, []string{"receiverlabel"}, []string{"receiverlabel"}}, + {[]string{"defaultlabel"}, []string{}, []string{"defaultlabel"}}, + {[]string{}, []string{}, []string{}}, + } { + mandatory := mandatoryReceiverFields() + + defaultsConfig := newReceiverTestConfig(mandatory, []string{}) + defaultsConfig.StaticLabels = test.defaultValue + + receiverConfig := newReceiverTestConfig([]string{"Name"}, []string{"StaticLabels"}) + receiverConfig.StaticLabels = test.receiverValue + + config := testConfig{ + Defaults: defaultsConfig, + Receivers: []*receiverTestConfig{receiverConfig}, + Template: "jiralert.tmpl", + } + + yamlConfig, err := yaml.Marshal(&config) + require.NoError(t, err) + + cfg, err := Load(string(yamlConfig)) + require.NoError(t, err) + + receiver := cfg.Receivers[0] + require.ElementsMatch(t, receiver.StaticLabels, test.expectedElements, "Elements should match (failing index: %v)", i) + } +} diff --git a/pkg/notify/notify.go b/pkg/notify/notify.go index 0191814..05fbc34 100644 --- a/pkg/notify/notify.go +++ b/pkg/notify/notify.go @@ -155,13 +155,15 @@ func (r *Receiver) Notify(data *alertmanager.Data, hashJiraLabel bool, updateSum return false, errors.Wrap(err, "render issue type") } + staticLabels := r.conf.StaticLabels + issue = &jira.Issue{ Fields: &jira.IssueFields{ Project: jira.Project{Key: project}, Type: jira.IssueType{Name: issueType}, Description: issueDesc, Summary: issueSummary, - Labels: []string{issueGroupLabel}, + Labels: append(staticLabels, issueGroupLabel), Unknowns: tcontainer.NewMarshalMap(), }, } diff --git a/pkg/notify/notify_test.go b/pkg/notify/notify_test.go index 4d3fe65..8e73f4a 100644 --- a/pkg/notify/notify_test.go +++ b/pkg/notify/notify_test.go @@ -188,6 +188,18 @@ func testReceiverConfigAutoResolve() *config.ReceiverConfig { } } +func testReceiverConfigWithStaticLabels() *config.ReceiverConfig { + reopen := config.Duration(1 * time.Hour) + return &config.ReceiverConfig{ + Project: "abc", + Summary: `[{{ .Status | toUpper }}{{ if eq .Status "firing" }}:{{ .Alerts.Firing | len }}{{ end }}] {{ .GroupLabels.SortedPairs.Values | join " " }} {{ if gt (len .CommonLabels) (len .GroupLabels) }}({{ with .CommonLabels.Remove .GroupLabels.Names }}{{ .Values | join " " }}{{ end }}){{ end }}`, + ReopenDuration: &reopen, + ReopenState: "reopened", + WontFixResolution: "won't-fix", + StaticLabels: []string{"somelabel"}, + } +} + func TestNotify_JIRAInteraction(t *testing.T) { testNowTime := time.Now() @@ -536,6 +548,35 @@ func TestNotify_JIRAInteraction(t *testing.T) { }, }, }, + { + name: "empty jira, new alert group with StaticLabels", + inputConfig: testReceiverConfigWithStaticLabels(), + initJira: func(t *testing.T) *fakeJira { return newTestFakeJira() }, + inputAlert: &alertmanager.Data{ + Alerts: alertmanager.Alerts{ + {Status: alertmanager.AlertFiring}, + {Status: "not firing"}, + {Status: alertmanager.AlertFiring}, + }, + Status: alertmanager.AlertFiring, + GroupLabels: alertmanager.KV{"a": "b", "c": "d"}, + }, + expectedJiraIssues: map[string]*jira.Issue{ + "1": { + ID: "1", + Key: "1", + Fields: &jira.IssueFields{ + Project: jira.Project{Key: testReceiverConfig1().Project}, + Labels: []string{"somelabel", "JIRALERT{819ba5ecba4ea5946a8d17d285cb23f3bb6862e08bb602ab08fd231cd8e1a83a1d095b0208a661787e9035f0541817634df5a994d1b5d4200d6c68a7663c97f5}"}, + Status: &jira.Status{ + StatusCategory: jira.StatusCategory{Key: "NotDone"}, + }, + Unknowns: tcontainer.MarshalMap{}, + Summary: "[FIRING:2] b d ", + }, + }, + }, + }, } { if ok := t.Run(tcase.name, func(t *testing.T) { fakeJira := tcase.initJira(t)