From ecf080825e77f36a86f1278db41d1d9cd101c722 Mon Sep 17 00:00:00 2001 From: Joe Blubaugh Date: Thu, 9 Jun 2022 10:01:58 +0800 Subject: [PATCH] Alerting: Fix image embed in email template. (#50370) The ng_alert_notification email template did not include templating for linked or embedded images. This change updates that. Additionally, this change supports embedding an image for each alert in an email batch. Fixes #50315 --- .../ngalert/notifier/channels/email.go | 54 +++++++++---------- .../notifier/channels/template_data.go | 25 ++++----- public/emails/ng_alert_notification.html | 15 ++++++ 3 files changed, 54 insertions(+), 40 deletions(-) diff --git a/pkg/services/ngalert/notifier/channels/email.go b/pkg/services/ngalert/notifier/channels/email.go index 01e6e745569..ebf7dac61b8 100644 --- a/pkg/services/ngalert/notifier/channels/email.go +++ b/pkg/services/ngalert/notifier/channels/email.go @@ -89,12 +89,11 @@ func NewEmailNotifier(config *EmailConfig, ns notifications.EmailSender, images } // Notify sends the alert notification. -func (en *EmailNotifier) Notify(ctx context.Context, as ...*types.Alert) (bool, error) { +func (en *EmailNotifier) Notify(ctx context.Context, alerts ...*types.Alert) (bool, error) { var tmplErr error - tmpl, data := TmplText(ctx, en.tmpl, as, en.log, &tmplErr) + tmpl, data := TmplText(ctx, en.tmpl, alerts, en.log, &tmplErr) subject := tmpl(en.Subject) - alertPageURL := en.tmpl.ExternalURL.String() ruleURL := en.tmpl.ExternalURL.String() u, err := url.Parse(en.tmpl.ExternalURL.String()) @@ -108,6 +107,26 @@ func (en *EmailNotifier) Notify(ctx context.Context, as ...*types.Alert) (bool, en.log.Debug("failed to parse external URL", "url", en.tmpl.ExternalURL.String(), "err", err.Error()) } + // Extend alerts data with images, if available. + var embeddedFiles []string + _ = withStoredImages(ctx, en.log, en.images, + func(index int, image *ngmodels.Image) error { + if image != nil { + if len(image.URL) != 0 { + data.Alerts[index].ImageURL = image.URL + } else if len(image.Path) != 0 { + _, err := os.Stat(image.Path) + if err == nil { + data.Alerts[index].EmbeddedImage = path.Base(image.Path) + embeddedFiles = append(embeddedFiles, image.Path) + } else { + en.log.Warn("failed to get image file for email attachment", "file", image.Path, "err", err) + } + } + } + return nil + }, alerts...) + cmd := &models.SendEmailCommandSync{ SendEmailCommand: models.SendEmailCommand{ Subject: subject, @@ -123,34 +142,13 @@ func (en *EmailNotifier) Notify(ctx context.Context, as ...*types.Alert) (bool, "RuleUrl": ruleURL, "AlertPageUrl": alertPageURL, }, - To: en.Addresses, - SingleEmail: en.SingleEmail, - Template: "ng_alert_notification", + EmbeddedFiles: embeddedFiles, + To: en.Addresses, + SingleEmail: en.SingleEmail, + Template: "ng_alert_notification", }, } - // TODO: modify the email sender code to support multiple file or image URL - // fields. We cannot use images from every alert yet. - _ = withStoredImage(ctx, en.log, en.images, - func(index int, image *ngmodels.Image) error { - if image == nil { - return nil - } - - if len(image.URL) != 0 { - cmd.Data["ImageLink"] = image.URL - } else if len(image.Path) != 0 { - file, err := os.Stat(image.Path) - if err == nil { - cmd.EmbeddedFiles = []string{image.Path} - cmd.Data["EmbeddedImage"] = file.Name() - } else { - en.log.Warn("failed to access email notification image attachment data", "err", err) - } - } - return nil - }, 0, as...) - if tmplErr != nil { en.log.Warn("failed to template email message", "err", tmplErr.Error()) } diff --git a/pkg/services/ngalert/notifier/channels/template_data.go b/pkg/services/ngalert/notifier/channels/template_data.go index 93490f2c4c8..c021d8ecc37 100644 --- a/pkg/services/ngalert/notifier/channels/template_data.go +++ b/pkg/services/ngalert/notifier/channels/template_data.go @@ -18,18 +18,19 @@ import ( ) type ExtendedAlert struct { - Status string `json:"status"` - Labels template.KV `json:"labels"` - Annotations template.KV `json:"annotations"` - StartsAt time.Time `json:"startsAt"` - EndsAt time.Time `json:"endsAt"` - GeneratorURL string `json:"generatorURL"` - Fingerprint string `json:"fingerprint"` - SilenceURL string `json:"silenceURL"` - DashboardURL string `json:"dashboardURL"` - PanelURL string `json:"panelURL"` - ValueString string `json:"valueString"` - ImageURL string `json:"imageURL,omitempty"` + Status string `json:"status"` + Labels template.KV `json:"labels"` + Annotations template.KV `json:"annotations"` + StartsAt time.Time `json:"startsAt"` + EndsAt time.Time `json:"endsAt"` + GeneratorURL string `json:"generatorURL"` + Fingerprint string `json:"fingerprint"` + SilenceURL string `json:"silenceURL"` + DashboardURL string `json:"dashboardURL"` + PanelURL string `json:"panelURL"` + ValueString string `json:"valueString"` + ImageURL string `json:"imageURL,omitempty"` + EmbeddedImage string `json:"embeddedImage,omitempty"` } type ExtendedAlerts []ExtendedAlert diff --git a/public/emails/ng_alert_notification.html b/public/emails/ng_alert_notification.html index 6c0e96c2f69..23ea84f08f0 100644 --- a/public/emails/ng_alert_notification.html +++ b/public/emails/ng_alert_notification.html @@ -210,6 +210,21 @@ text-decoration: underline; {{Subject .Subject "{{.Title}}"}} {{ define "alert" }} + + {{if ne .ImageURL "" }} + + + Alerting Panel + + + {{end}} + {{if ne .EmbeddedImage "" }} + + + Alerting Chart Attached Below + + + {{end}} Value: {{ .ValueString }}