From 07bfa64bb47ccbb308e3cd76a04b4fcaee4d4dc9 Mon Sep 17 00:00:00 2001 From: "Grot (@grafanabot)" <43478413+grafanabot@users.noreply.github.com> Date: Sun, 20 Jun 2021 22:54:29 -0400 Subject: [PATCH] Alerting: Decouple default template from channel tests (#35239) (#35973) Signed-off-by: Ganesh Vernekar (cherry picked from commit 33d6e11175cd40d810d5e6e213b136edd4142699) Co-authored-by: Ganesh Vernekar <15064823+codesome@users.noreply.github.com> --- .../notifier/channels/default_template.go | 30 +++- .../channels/default_template_test.go | 142 ++++++++++++++++++ .../alerting/api_notification_channel_test.go | 3 + 3 files changed, 173 insertions(+), 2 deletions(-) create mode 100644 pkg/services/ngalert/notifier/channels/default_template_test.go diff --git a/pkg/services/ngalert/notifier/channels/default_template.go b/pkg/services/ngalert/notifier/channels/default_template.go index 6ee1602539b..4a516c091eb 100644 --- a/pkg/services/ngalert/notifier/channels/default_template.go +++ b/pkg/services/ngalert/notifier/channels/default_template.go @@ -9,7 +9,33 @@ import ( "github.com/stretchr/testify/require" ) -const DefaultTemplateString = ` +var DefaultTemplateString = ` +{{ define "__subject" }}[{{ .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 }}{{ end }} + +{{ define "__text_alert_list" }}{{ range . }} +Labels: +{{ range .Labels.SortedPairs }} - {{ .Name }} = {{ .Value }} +{{ end }}Annotations: +{{ range .Annotations.SortedPairs }} - {{ .Name }} = {{ .Value }} +{{ end }}{{ if gt (len .GeneratorURL) 0 }}Source: {{ .GeneratorURL }} +{{ end }}{{ if gt (len .SilenceURL) 0 }}Silence: {{ .SilenceURL }} +{{ end }}{{ if gt (len .DashboardURL) 0 }}Dashboard: {{ .DashboardURL }} +{{ end }}{{ if gt (len .PanelURL) 0 }}Panel: {{ .PanelURL }} +{{ end }}{{ end }}{{ end }} + +{{ define "default.title" }}{{ template "__subject" . }}{{ end }} + +{{ define "default.message" }}{{ if gt (len .Alerts.Firing) 0 }}**Firing** +{{ template "__text_alert_list" .Alerts.Firing }}{{ if gt (len .Alerts.Resolved) 0 }} + +{{ end }}{{ end }}{{ if gt (len .Alerts.Resolved) 0 }}**Resolved** +{{ template "__text_alert_list" .Alerts.Resolved }}{{ end }}{{ end }} +` + +// TemplateForTestsString is the template used for unit tests and integration tests. +// We have it separate from above default template because any tiny change in the template +// will require updating almost all channel tests (15+ files) and it's very time consuming. +const TemplateForTestsString = ` {{ define "__subject" }}[{{ .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 }}{{ end }} {{ define "__text_alert_list" }}{{ range . }} @@ -40,7 +66,7 @@ func templateForTests(t *testing.T) *template.Template { require.NoError(t, os.RemoveAll(f.Name())) }) - _, err = f.WriteString(DefaultTemplateString) + _, err = f.WriteString(TemplateForTestsString) require.NoError(t, err) tmpl, err := template.FromGlobs(f.Name()) diff --git a/pkg/services/ngalert/notifier/channels/default_template_test.go b/pkg/services/ngalert/notifier/channels/default_template_test.go new file mode 100644 index 00000000000..d0bd86bccb2 --- /dev/null +++ b/pkg/services/ngalert/notifier/channels/default_template_test.go @@ -0,0 +1,142 @@ +package channels + +import ( + "context" + "io/ioutil" + "net/url" + "os" + "testing" + "time" + + "github.com/grafana/grafana/pkg/infra/log" + "github.com/prometheus/alertmanager/template" + "github.com/prometheus/alertmanager/types" + "github.com/prometheus/common/model" + "github.com/stretchr/testify/require" +) + +func TestDefaultTemplateString(t *testing.T) { + alerts := []*types.Alert{ + { // Firing with dashboard and panel ID. + Alert: model.Alert{ + Labels: model.LabelSet{"alertname": "alert1", "lbl1": "val1"}, + Annotations: model.LabelSet{ + "ann1": "annv1", "__dashboardUid__": "dbuid123", "__panelId__": "puid123", + }, + StartsAt: time.Now(), + EndsAt: time.Now().Add(1 * time.Hour), + GeneratorURL: "http://localhost/alert1", + }, + }, { // Firing without dashboard and panel ID. + Alert: model.Alert{ + Labels: model.LabelSet{"alertname": "alert1", "lbl1": "val2"}, + Annotations: model.LabelSet{"ann1": "annv2"}, + StartsAt: time.Now(), + EndsAt: time.Now().Add(2 * time.Hour), + GeneratorURL: "http://localhost/alert2", + }, + }, { // Resolved with dashboard and panel ID. + Alert: model.Alert{ + Labels: model.LabelSet{"alertname": "alert1", "lbl1": "val3"}, + Annotations: model.LabelSet{ + "ann1": "annv3", "__dashboardUid__": "dbuid456", "__panelId__": "puid456", + }, + StartsAt: time.Now().Add(-1 * time.Hour), + EndsAt: time.Now().Add(-30 * time.Minute), + GeneratorURL: "http://localhost/alert3", + }, + }, { // Resolved without dashboard and panel ID. + Alert: model.Alert{ + Labels: model.LabelSet{"alertname": "alert1", "lbl1": "val4"}, + Annotations: model.LabelSet{"ann1": "annv4"}, + StartsAt: time.Now().Add(-2 * time.Hour), + EndsAt: time.Now().Add(-3 * time.Hour), + GeneratorURL: "http://localhost/alert4", + }, + }, + } + + f, err := ioutil.TempFile("/tmp", "template") + require.NoError(t, err) + + t.Cleanup(func() { + require.NoError(t, os.RemoveAll(f.Name())) + }) + + _, err = f.WriteString(DefaultTemplateString) + require.NoError(t, err) + + tmpl, err := template.FromGlobs(f.Name()) + require.NoError(t, err) + + externalURL, err := url.Parse("http://localhost/grafana") + require.NoError(t, err) + tmpl.ExternalURL = externalURL + + var tmplErr error + l := log.New("default-template-test") + expand, _ := TmplText(context.Background(), tmpl, alerts, l, &tmplErr) + + cases := []struct { + templateString string + expected string + }{ + { + templateString: `{{ template "default.title" .}}`, + expected: `[FIRING:2] (alert1)`, + }, + { + templateString: `{{ template "default.message" .}}`, + expected: `**Firing** + +Labels: + - alertname = alert1 + - lbl1 = val1 +Annotations: + - ann1 = annv1 +Source: http://localhost/alert1 +Silence: http://localhost/grafana/alerting/silence/new?alertmanager=grafana&matchers=alertname%3Dalert1%2Clbl1%3Dval1 +Dashboard: http://localhost/grafana/d/dbuid123 +Panel: http://localhost/grafana/d/dbuid123?viewPanel=puid123 + +Labels: + - alertname = alert1 + - lbl1 = val2 +Annotations: + - ann1 = annv2 +Source: http://localhost/alert2 +Silence: http://localhost/grafana/alerting/silence/new?alertmanager=grafana&matchers=alertname%3Dalert1%2Clbl1%3Dval2 + + +**Resolved** + +Labels: + - alertname = alert1 + - lbl1 = val3 +Annotations: + - ann1 = annv3 +Source: http://localhost/alert3 +Silence: http://localhost/grafana/alerting/silence/new?alertmanager=grafana&matchers=alertname%3Dalert1%2Clbl1%3Dval3 +Dashboard: http://localhost/grafana/d/dbuid456 +Panel: http://localhost/grafana/d/dbuid456?viewPanel=puid456 + +Labels: + - alertname = alert1 + - lbl1 = val4 +Annotations: + - ann1 = annv4 +Source: http://localhost/alert4 +Silence: http://localhost/grafana/alerting/silence/new?alertmanager=grafana&matchers=alertname%3Dalert1%2Clbl1%3Dval4 +`, + }, + } + + for _, c := range cases { + t.Run(c.templateString, func(t *testing.T) { + act := expand(c.templateString) + require.NoError(t, tmplErr) + require.Equal(t, c.expected, act) + }) + } + require.NoError(t, tmplErr) +} diff --git a/pkg/tests/api/alerting/api_notification_channel_test.go b/pkg/tests/api/alerting/api_notification_channel_test.go index 43213ec9c48..788aa672f6f 100644 --- a/pkg/tests/api/alerting/api_notification_channel_test.go +++ b/pkg/tests/api/alerting/api_notification_channel_test.go @@ -45,10 +45,13 @@ func TestNotificationChannels(t *testing.T) { os, opa, ot, opu, ogb, ol, oth := channels.SlackAPIEndpoint, channels.PagerdutyEventAPIURL, channels.TelegramAPIURL, channels.PushoverEndpoint, channels.GetBoundary, channels.LineNotifyURL, channels.ThreemaGwBaseURL + originalTemplate := channels.DefaultTemplateString + channels.DefaultTemplateString = channels.TemplateForTestsString t.Cleanup(func() { channels.SlackAPIEndpoint, channels.PagerdutyEventAPIURL, channels.TelegramAPIURL, channels.PushoverEndpoint, channels.GetBoundary, channels.LineNotifyURL, channels.ThreemaGwBaseURL = os, opa, ot, opu, ogb, ol, oth + channels.DefaultTemplateString = originalTemplate }) channels.SlackAPIEndpoint = fmt.Sprintf("http://%s/slack_recvX/slack_testX", mockChannel.server.Addr) channels.PagerdutyEventAPIURL = fmt.Sprintf("http://%s/pagerduty_recvX/pagerduty_testX", mockChannel.server.Addr)