diff --git a/pkg/services/ngalert/notifier/channels/email_test.go b/pkg/services/ngalert/notifier/channels/email_test.go index 42a738ba196..7e6da5adb7e 100644 --- a/pkg/services/ngalert/notifier/channels/email_test.go +++ b/pkg/services/ngalert/notifier/channels/email_test.go @@ -10,7 +10,10 @@ import ( "github.com/prometheus/common/model" "github.com/stretchr/testify/require" + "github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/components/simplejson" + "github.com/grafana/grafana/pkg/services/notifications" + "github.com/grafana/grafana/pkg/setting" ) func TestEmailNotifier(t *testing.T) { @@ -102,3 +105,202 @@ func TestEmailNotifier(t *testing.T) { }, expected) }) } + +func TestEmailNotifierIntegration(t *testing.T) { + ns := createCoreEmailService(t) + + emailTmpl := templateForTests(t) + externalURL, err := url.Parse("http://localhost/base") + require.NoError(t, err) + emailTmpl.ExternalURL = externalURL + + cases := []struct { + name string + alerts []*types.Alert + messageTmpl string + expSubject string + expSnippets []string + }{ + { + name: "single alert with templated message", + alerts: []*types.Alert{ + { + Alert: model.Alert{ + Labels: model.LabelSet{"alertname": "AlwaysFiring", "severity": "warning"}, + Annotations: model.LabelSet{"runbook_url": "http://fix.me", "__dashboardUid__": "abc", "__panelId__": "5"}, + }, + }, + }, + messageTmpl: `Hi, this is a custom template. + {{ if gt (len .Alerts.Firing) 0 }} + You have {{ len .Alerts.Firing }} alerts firing. + {{ range .Alerts.Firing }} Firing: {{ .Labels.alertname }} at {{ .Labels.severity }} {{ end }} + {{ end }}`, + expSubject: "[FIRING:1] (AlwaysFiring warning)", + expSnippets: []string{ + "Hi, this is a custom template.", + "You have 1 alerts firing.", + "Firing: AlwaysFiring at warning", + }, + }, + { + name: "multiple alerts with templated message", + alerts: []*types.Alert{ + { + Alert: model.Alert{ + Labels: model.LabelSet{"alertname": "FiringOne", "severity": "warning"}, + Annotations: model.LabelSet{"runbook_url": "http://fix.me", "__dashboardUid__": "abc", "__panelId__": "5"}, + }, + }, + { + Alert: model.Alert{ + Labels: model.LabelSet{"alertname": "FiringTwo", "severity": "critical"}, + Annotations: model.LabelSet{"runbook_url": "http://fix.me", "__dashboardUid__": "abc", "__panelId__": "5"}, + }, + }, + }, + messageTmpl: `Hi, this is a custom template. + {{ if gt (len .Alerts.Firing) 0 }} + You have {{ len .Alerts.Firing }} alerts firing. + {{ range .Alerts.Firing }} Firing: {{ .Labels.alertname }} at {{ .Labels.severity }} {{ end }} + {{ end }}`, + expSubject: "[FIRING:2] ", + expSnippets: []string{ + "Hi, this is a custom template.", + "You have 2 alerts firing.", + "Firing: FiringOne at warning", + "Firing: FiringTwo at critical", + }, + }, + { + name: "empty message with alerts uses default template content", + alerts: []*types.Alert{ + { + Alert: model.Alert{ + Labels: model.LabelSet{"alertname": "FiringOne", "severity": "warning"}, + Annotations: model.LabelSet{"runbook_url": "http://fix.me", "__dashboardUid__": "abc", "__panelId__": "5"}, + }, + }, + { + Alert: model.Alert{ + Labels: model.LabelSet{"alertname": "FiringTwo", "severity": "critical"}, + Annotations: model.LabelSet{"runbook_url": "http://fix.me", "__dashboardUid__": "abc", "__panelId__": "5"}, + }, + }, + }, + messageTmpl: "", + expSubject: "[FIRING:2] ", + expSnippets: []string{ + "Firing: 2 alerts", + "
  • alertname: FiringOne
  • severity: warning
  • ", + "
  • alertname: FiringTwo
  • severity: critical
  • ", + "Hi, this is a custom template. + {{ if gt (len .Alerts.Firing) 0 }} +
      + {{range .Alerts.Firing }}
    1. Firing: {{ .Labels.alertname }} at {{ .Labels.severity }}
    2. {{ end }} +
    + {{ end }}`, + expSubject: "[FIRING:1] (AlwaysFiring warning)", + expSnippets: []string{ + "<marquee>Hi, this is a custom template.</marquee>", + "<li>Firing: AlwaysFiring at warning </li>", + }, + }, + } + + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + emailNotifier := createSut(t, c.messageTmpl, emailTmpl, ns) + + ok, err := emailNotifier.Notify(context.Background(), c.alerts...) + require.NoError(t, err) + require.True(t, ok) + + sentMsg := getSingleSentMessage(t, ns) + + require.NotNil(t, sentMsg) + + require.Equal(t, "\"Grafana Admin\" ", sentMsg.From) + require.Equal(t, sentMsg.To[0], "someops@example.com") + + require.Equal(t, c.expSubject, sentMsg.Subject) + + require.Contains(t, sentMsg.Body, "text/html") + html := sentMsg.Body["text/html"] + require.NotNil(t, html) + + for _, s := range c.expSnippets { + require.Contains(t, html, s) + } + }) + } +} + +func createCoreEmailService(t *testing.T) *notifications.NotificationService { + t.Helper() + + bus := bus.New() + cfg := setting.NewCfg() + cfg.StaticRootPath = "../../../../../public/" + cfg.BuildVersion = "4.0.0" + cfg.Smtp.Enabled = true + cfg.Smtp.TemplatesPatterns = []string{"emails/*.html", "emails/*.txt"} + cfg.Smtp.FromAddress = "from@address.com" + cfg.Smtp.FromName = "Grafana Admin" + cfg.Smtp.ContentTypes = []string{"text/html", "text/plain"} + cfg.Smtp.Host = "localhost:1234" + mailer := notifications.NewFakeMailer() + + ns, err := notifications.ProvideService(bus, cfg, mailer) + require.NoError(t, err) + + return ns +} + +func createSut(t *testing.T, messageTmpl string, emailTmpl *template.Template, ns notifications.EmailSender) *EmailNotifier { + t.Helper() + + json := `{ + "addresses": "someops@example.com;somedev@example.com", + "singleEmail": true + }` + settingsJSON, err := simplejson.NewJson([]byte(json)) + if messageTmpl != "" { + settingsJSON.Set("message", messageTmpl) + } + require.NoError(t, err) + + emailNotifier, err := NewEmailNotifier(&NotificationChannelConfig{ + Name: "ops", + Type: "email", + Settings: settingsJSON, + }, ns, emailTmpl) + require.NoError(t, err) + + return emailNotifier +} + +func getSingleSentMessage(t *testing.T, ns *notifications.NotificationService) *notifications.Message { + t.Helper() + + mailer := ns.GetMailer().(*notifications.FakeMailer) + require.Len(t, mailer.Sent, 1) + sent := mailer.Sent[0] + mailer.Sent = []*notifications.Message{} + return sent +} diff --git a/pkg/services/notifications/notifications.go b/pkg/services/notifications/notifications.go index 3ed26249234..9d13c0404c3 100644 --- a/pkg/services/notifications/notifications.go +++ b/pkg/services/notifications/notifications.go @@ -118,6 +118,10 @@ func (ns *NotificationService) Run(ctx context.Context) error { } } +func (ns *NotificationService) GetMailer() Mailer { + return ns.mailer +} + func (ns *NotificationService) SendWebhookSync(ctx context.Context, cmd *models.SendWebhookSync) error { return ns.sendWebRequestSync(ctx, &Webhook{ Url: cmd.Url,