diff --git a/pkg/services/ngalert/notifier/channels/teams.go b/pkg/services/ngalert/notifier/channels/teams.go index 0fc0ea9b918..0b9799f4d91 100644 --- a/pkg/services/ngalert/notifier/channels/teams.go +++ b/pkg/services/ngalert/notifier/channels/teams.go @@ -24,6 +24,7 @@ type TeamsNotifier struct { tmpl *template.Template log log.Logger ns notifications.WebhookSender + images ImageStore } type TeamsConfig struct { @@ -42,7 +43,7 @@ func TeamsFactory(fc FactoryConfig) (NotificationChannel, error) { Cfg: *fc.Config, } } - return NewTeamsNotifier(cfg, fc.NotificationService, fc.Template), nil + return NewTeamsNotifier(cfg, fc.NotificationService, fc.ImageStore, fc.Template), nil } func NewTeamsConfig(config *NotificationChannelConfig) (*TeamsConfig, error) { @@ -59,8 +60,12 @@ func NewTeamsConfig(config *NotificationChannelConfig) (*TeamsConfig, error) { }, nil } +type teamsImage struct { + Image string `json:"image"` +} + // NewTeamsNotifier is the constructor for Teams notifier. -func NewTeamsNotifier(config *TeamsConfig, ns notifications.WebhookSender, t *template.Template) *TeamsNotifier { +func NewTeamsNotifier(config *TeamsConfig, ns notifications.WebhookSender, images ImageStore, t *template.Template) *TeamsNotifier { return &TeamsNotifier{ Base: NewBase(&models.AlertNotification{ Uid: config.UID, @@ -75,6 +80,7 @@ func NewTeamsNotifier(config *TeamsConfig, ns notifications.WebhookSender, t *te SectionTitle: config.SectionTitle, log: log.New("alerting.notifier.teams"), ns: ns, + images: images, tmpl: t, } } @@ -86,7 +92,36 @@ func (tn *TeamsNotifier) Notify(ctx context.Context, as ...*types.Alert) (bool, ruleURL := joinUrlPath(tn.tmpl.ExternalURL.String(), "/alerting/list", tn.log) + images := []teamsImage{} + for i := range as { + imgToken := getTokenFromAnnotations(as[i].Annotations) + timeoutCtx, cancel := context.WithTimeout(ctx, ImageStoreTimeout) + imgURL, err := tn.images.GetURL(timeoutCtx, imgToken) + cancel() + if err != nil { + if !errors.Is(err, ErrImagesUnavailable) { + // Ignore errors. Don't log "ImageUnavailable", which means the storage doesn't exist. + tn.log.Warn("failed to retrieve image url from store", "error", err) + } + } + if len(imgURL) > 0 { + images = append(images, teamsImage{Image: imgURL}) + } + } + + // Note: these template calls must remain in this order title := tmpl(tn.Title) + sections := []map[string]interface{}{ + { + "title": tmpl(tn.SectionTitle), + "text": tmpl(tn.Message), + }, + } + + if len(images) != 0 { + sections[0]["images"] = images + } + body := map[string]interface{}{ "@type": "MessageCard", "@context": "http://schema.org/extensions", @@ -95,12 +130,7 @@ func (tn *TeamsNotifier) Notify(ctx context.Context, as ...*types.Alert) (bool, "summary": title, "title": title, "themeColor": getAlertStatusColor(types.Alerts(as...).Status()), - "sections": []map[string]interface{}{ - { - "title": tmpl(tn.SectionTitle), - "text": tmpl(tn.Message), - }, - }, + "sections": sections, "potentialAction": []map[string]interface{}{ { "@context": "http://schema.org", diff --git a/pkg/services/ngalert/notifier/channels/teams_test.go b/pkg/services/ngalert/notifier/channels/teams_test.go index 3e0ed242454..68834b881d2 100644 --- a/pkg/services/ngalert/notifier/channels/teams_test.go +++ b/pkg/services/ngalert/notifier/channels/teams_test.go @@ -220,7 +220,7 @@ func TestTeamsNotifier(t *testing.T) { ctx := notify.WithGroupKey(context.Background(), "alertname") ctx = notify.WithGroupLabels(ctx, model.LabelSet{"alertname": ""}) - pn := NewTeamsNotifier(cfg, webhookSender, tmpl) + pn := NewTeamsNotifier(cfg, webhookSender, &UnavailableImageStore{}, tmpl) ok, err := pn.Notify(ctx, c.alerts...) if c.expMsgError != nil { require.False(t, ok)