diff --git a/pkg/services/ngalert/api/tooling/definitions/alertmanager_validation.go b/pkg/services/ngalert/api/tooling/definitions/alertmanager_validation.go index 72ca0ebb730..3c1b227c3e5 100644 --- a/pkg/services/ngalert/api/tooling/definitions/alertmanager_validation.go +++ b/pkg/services/ngalert/api/tooling/definitions/alertmanager_validation.go @@ -2,10 +2,8 @@ package definitions import ( "fmt" - tmplhtml "html/template" "regexp" "strings" - tmpltext "text/template" "github.com/prometheus/alertmanager/template" "gopkg.in/yaml.v3" @@ -35,17 +33,12 @@ func (t *NotificationTemplate) Validate() error { t.Template = content // Validate template contents. We try to stick as close to what will actually happen when the templates are parsed - // by the alertmanager as possible. That means parsing with both the text and html parsers and making sure we set - // the template name and options. - ttext := tmpltext.New(t.Name).Option("missingkey=zero") - ttext.Funcs(tmpltext.FuncMap(template.DefaultFuncs)) - if _, err := ttext.Parse(t.Template); err != nil { - return fmt.Errorf("invalid template: %w", err) + // by the alertmanager as possible. + tmpl, err := template.New() + if err != nil { + return fmt.Errorf("failed to create template: %w", err) } - - thtml := tmplhtml.New(t.Name).Option("missingkey=zero") - thtml.Funcs(tmplhtml.FuncMap(template.DefaultFuncs)) - if _, err := thtml.Parse(t.Template); err != nil { + if err := tmpl.Parse(strings.NewReader(t.Template)); err != nil { return fmt.Errorf("invalid template: %w", err) } diff --git a/pkg/services/ngalert/api/tooling/definitions/alertmanager_validation_test.go b/pkg/services/ngalert/api/tooling/definitions/alertmanager_validation_test.go index 4fbbd983b13..74f186003d7 100644 --- a/pkg/services/ngalert/api/tooling/definitions/alertmanager_validation_test.go +++ b/pkg/services/ngalert/api/tooling/definitions/alertmanager_validation_test.go @@ -442,7 +442,6 @@ func TestValidateNotificationTemplates(t *testing.T) { expError: errors.New("invalid template: template: Different name than definition:1: template: multiple definition of template \"Alert Instance Template\""), }, { - // This is fine as long as the template name is different from the definition, it just ignores the extra text. name: "Extra text outside definition block - different template name and definition", template: NotificationTemplate{ Name: "Different name than definition", @@ -452,16 +451,16 @@ func TestValidateNotificationTemplates(t *testing.T) { expContent: `{{ define "Alert Instance Template" }}\nFiring: {{ .Labels.alertname }}\nSilence: {{ .SilenceURL }}\n{{ end }}[what is this?]`, expError: nil, }, + // This test used to error because our template code parsed the template with the filename as template name. + // However, we have since moved away from this. We keep this test to ensure we don't regress. { - // This is NOT fine as the template name is the same as the definition. - // GO template parser will treat it as if it's wrapped in {{ define "Alert Instance Template" }}, thus creating a duplicate definition. name: "Extra text outside definition block - same template name and definition", template: NotificationTemplate{ Name: "Alert Instance Template", Template: `{{ define "Alert Instance Template" }}\nFiring: {{ .Labels.alertname }}\nSilence: {{ .SilenceURL }}\n{{ end }}[what is this?]`, Provenance: "test", }, - expError: errors.New("invalid template: template: Alert Instance Template:1: template: multiple definition of template \"Alert Instance Template\""), + expContent: `{{ define "Alert Instance Template" }}\nFiring: {{ .Labels.alertname }}\nSilence: {{ .SilenceURL }}\n{{ end }}[what is this?]`, }, } diff --git a/pkg/services/ngalert/notifier/templates.go b/pkg/services/ngalert/notifier/templates.go index 9f9a49f722c..5611bbe2b4c 100644 --- a/pkg/services/ngalert/notifier/templates.go +++ b/pkg/services/ngalert/notifier/templates.go @@ -15,8 +15,8 @@ type TestTemplatesResults = alertingNotify.TestTemplatesResults var ( DefaultLabels = map[string]string{ - prometheusModel.AlertNameLabel: `alert title`, - alertingModels.FolderTitleLabel: `folder title`, + prometheusModel.AlertNameLabel: `TestAlert`, + alertingModels.FolderTitleLabel: `Test Folder`, } DefaultAnnotations = map[string]string{ alertingModels.ValuesAnnotation: `{"B":22,"C":1}`, diff --git a/pkg/services/ngalert/notifier/templates_test.go b/pkg/services/ngalert/notifier/templates_test.go index 902c534a058..1b1da5a2890 100644 --- a/pkg/services/ngalert/notifier/templates_test.go +++ b/pkg/services/ngalert/notifier/templates_test.go @@ -84,7 +84,7 @@ CommonAnnotations: {{ range .CommonAnnotations.SortedPairs }}{{ .Name }}={{ .Val expected: TestTemplatesResults{ Results: []alertingNotify.TestTemplatesResult{{ Name: "slack.title", - Text: "\nReceiver: TestReceiver\nStatus: firing\nExternalURL: http://localhost:9093\nAlerts: 1\nFiring Alerts: 1\nResolved Alerts: 0\nGroupLabels: group_label=group_label_value \nCommonLabels: alertname=alert1 grafana_folder=folder title lbl1=val1 \nCommonAnnotations: ann1=annv1 \n", + Text: "\nReceiver: TestReceiver\nStatus: firing\nExternalURL: http://localhost:9093\nAlerts: 1\nFiring Alerts: 1\nResolved Alerts: 0\nGroupLabels: group_label=group_label_value \nCommonLabels: alertname=alert1 grafana_folder=Test Folder lbl1=val1 \nCommonAnnotations: ann1=annv1 \n", Scope: alertingNotify.TemplateScope(apimodels.RootScope), }}, Errors: nil, diff --git a/pkg/services/ngalert/notifier/testreceivers.go b/pkg/services/ngalert/notifier/testreceivers.go index 5844ff3aa30..cbd1ca29f87 100644 --- a/pkg/services/ngalert/notifier/testreceivers.go +++ b/pkg/services/ngalert/notifier/testreceivers.go @@ -5,6 +5,7 @@ import ( "encoding/json" alertingNotify "github.com/grafana/alerting/notify" + v2 "github.com/prometheus/alertmanager/api/v2" apimodels "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions" ) @@ -30,13 +31,17 @@ func (am *alertmanager) TestReceivers(ctx context.Context, c apimodels.TestRecei }, }) } - var alert *alertingNotify.TestReceiversConfigAlertParams + a := &alertingNotify.PostableAlert{} if c.Alert != nil { - alert = &alertingNotify.TestReceiversConfigAlertParams{Annotations: c.Alert.Annotations, Labels: c.Alert.Labels} + a.Annotations = v2.ModelLabelSetToAPILabelSet(c.Alert.Annotations) + a.Labels = v2.ModelLabelSetToAPILabelSet(c.Alert.Labels) } - + AddDefaultLabelsAndAnnotations(a) return am.Base.TestReceivers(ctx, alertingNotify.TestReceiversConfigBodyParams{ - Alert: alert, + Alert: &alertingNotify.TestReceiversConfigAlertParams{ + Annotations: v2.APILabelSetToModelLabelSet(a.Annotations), + Labels: v2.APILabelSetToModelLabelSet(a.Labels), + }, Receivers: receivers, }) } diff --git a/pkg/tests/api/alerting/api_notification_channel_test.go b/pkg/tests/api/alerting/api_notification_channel_test.go index b653b3a8184..b3442b3ed6f 100644 --- a/pkg/tests/api/alerting/api_notification_channel_test.go +++ b/pkg/tests/api/alerting/api_notification_channel_test.go @@ -131,10 +131,15 @@ func TestIntegrationTestReceivers(t *testing.T) { "alert": { "annotations": { "summary": "Notification test", - "__value_string__": "[ metric='foo' labels={instance=bar} value=10 ]" + "__dashboardUid__": "dashboard_uid", + "__orgId__": "1", + "__panelId__": "1", + "__value_string__": "[ var='B' labels={__name__=go_threads, instance=host.docker.internal:3000, job=grafana} value=22 ], [ var='C' labels={__name__=go_threads, instance=host.docker.internal:3000, job=grafana} value=1 ]", + "__values__": "{\"B\":22,\"C\":1}" }, "labels": { "alertname": "TestAlert", + "grafana_folder": "Test Folder", "instance": "Grafana" } }, @@ -214,10 +219,15 @@ func TestIntegrationTestReceivers(t *testing.T) { "alert": { "annotations": { "summary": "Notification test", - "__value_string__": "[ metric='foo' labels={instance=bar} value=10 ]" + "__dashboardUid__": "dashboard_uid", + "__orgId__": "1", + "__panelId__": "1", + "__value_string__": "[ var='B' labels={__name__=go_threads, instance=host.docker.internal:3000, job=grafana} value=22 ], [ var='C' labels={__name__=go_threads, instance=host.docker.internal:3000, job=grafana} value=1 ]", + "__values__": "{\"B\":22,\"C\":1}" }, "labels": { "alertname": "TestAlert", + "grafana_folder": "Test Folder", "instance": "Grafana" } }, @@ -315,10 +325,15 @@ func TestIntegrationTestReceivers(t *testing.T) { "alert": { "annotations": { "summary": "Notification test", - "__value_string__": "[ metric='foo' labels={instance=bar} value=10 ]" + "__dashboardUid__": "dashboard_uid", + "__orgId__": "1", + "__panelId__": "1", + "__value_string__": "[ var='B' labels={__name__=go_threads, instance=host.docker.internal:3000, job=grafana} value=22 ], [ var='C' labels={__name__=go_threads, instance=host.docker.internal:3000, job=grafana} value=1 ]", + "__values__": "{\"B\":22,\"C\":1}" }, "labels": { "alertname": "TestAlert", + "grafana_folder": "Test Folder", "instance": "Grafana" } }, @@ -392,10 +407,15 @@ func TestIntegrationTestReceivers(t *testing.T) { "alert": { "annotations": { "summary": "Notification test", - "__value_string__": "[ metric='foo' labels={instance=bar} value=10 ]" + "__dashboardUid__": "dashboard_uid", + "__orgId__": "1", + "__panelId__": "1", + "__value_string__": "[ var='B' labels={__name__=go_threads, instance=host.docker.internal:3000, job=grafana} value=22 ], [ var='C' labels={__name__=go_threads, instance=host.docker.internal:3000, job=grafana} value=1 ]", + "__values__": "{\"B\":22,\"C\":1}" }, "labels": { "alertname": "TestAlert", + "grafana_folder": "Test Folder", "instance": "Grafana" } }, @@ -480,10 +500,15 @@ func TestIntegrationTestReceivers(t *testing.T) { "alert": { "annotations": { "summary": "Notification test", - "__value_string__": "[ metric='foo' labels={instance=bar} value=10 ]" + "__dashboardUid__": "dashboard_uid", + "__orgId__": "1", + "__panelId__": "1", + "__value_string__": "[ var='B' labels={__name__=go_threads, instance=host.docker.internal:3000, job=grafana} value=22 ], [ var='C' labels={__name__=go_threads, instance=host.docker.internal:3000, job=grafana} value=1 ]", + "__values__": "{\"B\":22,\"C\":1}" }, "labels": { "alertname": "TestAlert", + "grafana_folder": "Test Folder", "instance": "Grafana" } }, @@ -581,10 +606,15 @@ func TestIntegrationTestReceivers(t *testing.T) { "alert": { "annotations": { "summary": "Notification test", - "__value_string__": "[ metric='foo' labels={instance=bar} value=10 ]" + "__dashboardUid__": "dashboard_uid", + "__orgId__": "1", + "__panelId__": "1", + "__value_string__": "[ var='B' labels={__name__=go_threads, instance=host.docker.internal:3000, job=grafana} value=22 ], [ var='C' labels={__name__=go_threads, instance=host.docker.internal:3000, job=grafana} value=1 ]", + "__values__": "{\"B\":22,\"C\":1}" }, "labels": { "alertname": "TestAlert", + "grafana_folder": "Test Folder", "instance": "Grafana" } }, @@ -687,10 +717,15 @@ func TestIntegrationTestReceiversAlertCustomization(t *testing.T) { "annotations": { "annotation1": "value1", "summary": "Notification test", - "__value_string__": "[ metric='foo' labels={instance=bar} value=10 ]" + "__dashboardUid__": "dashboard_uid", + "__orgId__": "1", + "__panelId__": "1", + "__value_string__": "[ metric='foo' labels={instance=bar} value=10 ]", + "__values__": "{\"B\":22,\"C\":1}" }, "labels": { "alertname": "TestAlert", + "grafana_folder": "Test Folder", "instance": "Grafana", "label1": "value1" } @@ -776,10 +811,15 @@ func TestIntegrationTestReceiversAlertCustomization(t *testing.T) { "alert": { "annotations": { "summary": "This is a custom annotation", - "__value_string__": "[ metric='foo' labels={instance=bar} value=10 ]" + "__dashboardUid__": "dashboard_uid", + "__orgId__": "1", + "__panelId__": "1", + "__value_string__": "[ metric='foo' labels={instance=bar} value=10 ]", + "__values__": "{\"B\":22,\"C\":1}" }, "labels": { "alertname": "TestAlert", + "grafana_folder": "Test Folder", "instance": "Grafana" } }, @@ -863,10 +903,15 @@ func TestIntegrationTestReceiversAlertCustomization(t *testing.T) { "alert": { "annotations": { "summary": "Notification test", - "__value_string__": "[ metric='foo' labels={instance=bar} value=10 ]" + "__dashboardUid__": "dashboard_uid", + "__orgId__": "1", + "__panelId__": "1", + "__value_string__": "[ var='B' labels={__name__=go_threads, instance=host.docker.internal:3000, job=grafana} value=22 ], [ var='C' labels={__name__=go_threads, instance=host.docker.internal:3000, job=grafana} value=1 ]", + "__values__": "{\"B\":22,\"C\":1}" }, "labels": { "alertname": "This is a custom label", + "grafana_folder": "Test Folder", "instance": "Grafana" } }, diff --git a/public/app/features/alerting/unified/components/contact-points/EditContactPoint.test.tsx b/public/app/features/alerting/unified/components/contact-points/EditContactPoint.test.tsx index 9ee0fae551b..3a7895c1a29 100644 --- a/public/app/features/alerting/unified/components/contact-points/EditContactPoint.test.tsx +++ b/public/app/features/alerting/unified/components/contact-points/EditContactPoint.test.tsx @@ -1,7 +1,7 @@ import 'core-js/stable/structured-clone'; import { Route, Routes } from 'react-router-dom-v5-compat'; import { clickSelectOption } from 'test/helpers/selectOptionInTest'; -import { render, screen } from 'test/test-utils'; +import { render, screen, within } from 'test/test-utils'; import EditContactPoint from 'app/features/alerting/unified/components/contact-points/EditContactPoint'; import { AccessControlAction } from 'app/types'; @@ -15,6 +15,20 @@ const Index = () => { return
{preview.text ?? ''}
-