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
This commit is contained in:
Joe Blubaugh 2022-06-09 10:01:58 +08:00 committed by GitHub
parent 2b73326785
commit ecf080825e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 54 additions and 40 deletions

View File

@ -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())
}

View File

@ -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

View File

@ -210,6 +210,21 @@ text-decoration: underline;
{{Subject .Subject "{{.Title}}"}}
{{ define "alert" }}
{{if ne .ImageURL "" }}
<tr style="vertical-align: top; padding: 0;" align="left">
<td colspan="2" style="word-break: break-word; -webkit-hyphens: auto; -moz-hyphens: auto; hyphens: auto; border-collapse: collapse !important; color: #222222; font-family: 'Open Sans', 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; font-weight: normal; line-height: 19px; font-size: 14px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; margin: 0; padding: 24px 0 0;" align="left" valign="top">
<img src="{{.ImageURL}}" class="fluid-centered" alt="Alerting Panel" style="outline: none !important; text-decoration: none !important; -ms-interpolation-mode: bicubic; width: auto; clear: both; display: block; border: 0;" align="left" />
</td>
</tr>
{{end}}
{{if ne .EmbeddedImage "" }}
<tr style="vertical-align: top; padding: 0;" align="left">
<td colspan="2" style="word-break: break-word; -webkit-hyphens: auto; -moz-hyphens: auto; hyphens: auto; border-collapse: collapse !important; color: #222222; font-family: 'Open Sans', 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; font-weight: normal; line-height: 19px; font-size: 14px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; margin: 0; padding: 24px 0 0;" align="left" valign="top">
<img src="cid:{{.EmbeddedImage}}" alt="Alerting Chart Attached Below" style="outline: none !important; text-decoration: none !important; -ms-interpolation-mode: bicubic; width: auto; clear: both; display: block; border: 0;" align="left" />
</td>
</tr>
{{end}}
<tr style="vertical-align: top; padding: 0;" align="left">
<td colspan="2" class="value" style="word-break: break-word; -webkit-hyphens: auto; -moz-hyphens: auto; hyphens: auto; border-collapse: collapse !important; color: #222222; font-family: 'Open Sans', 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; font-weight: normal; line-height: 19px; font-size: 14px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; margin: 0; padding: 24px 0 0;" align="left" valign="top">
<span class="value-heading" style="font-weight: bold;">Value:</span> <span class="value-value" style="padding-left: 8px;">{{ .ValueString }}</span>