From 2ba4f7ed7d0a88f36114689de5246f0a70cffc4d Mon Sep 17 00:00:00 2001 From: Alexander Weaver Date: Mon, 23 May 2022 18:15:44 -0500 Subject: [PATCH] Alerting: Attach image URLs to Google Chat notifications. (#49445) * Add a test card to see what styles look like * Add text and image cards * Address feedback --- .../ngalert/notifier/channels/googlechat.go | 64 ++++++++++++++++++- .../notifier/channels/googlechat_test.go | 3 +- 2 files changed, 64 insertions(+), 3 deletions(-) diff --git a/pkg/services/ngalert/notifier/channels/googlechat.go b/pkg/services/ngalert/notifier/channels/googlechat.go index be7f7537ec9..2179221ffcc 100644 --- a/pkg/services/ngalert/notifier/channels/googlechat.go +++ b/pkg/services/ngalert/notifier/channels/googlechat.go @@ -23,6 +23,7 @@ type GoogleChatNotifier struct { URL string log log.Logger ns notifications.WebhookSender + images ImageStore tmpl *template.Template content string } @@ -41,7 +42,7 @@ func GoogleChatFactory(fc FactoryConfig) (NotificationChannel, error) { Cfg: *fc.Config, } } - return NewGoogleChatNotifier(cfg, fc.NotificationService, fc.Template), nil + return NewGoogleChatNotifier(cfg, fc.ImageStore, fc.NotificationService, fc.Template), nil } func NewGoogleChatConfig(config *NotificationChannelConfig) (*GoogleChatConfig, error) { @@ -56,7 +57,7 @@ func NewGoogleChatConfig(config *NotificationChannelConfig) (*GoogleChatConfig, }, nil } -func NewGoogleChatNotifier(config *GoogleChatConfig, ns notifications.WebhookSender, t *template.Template) *GoogleChatNotifier { +func NewGoogleChatNotifier(config *GoogleChatConfig, images ImageStore, ns notifications.WebhookSender, t *template.Template) *GoogleChatNotifier { return &GoogleChatNotifier{ Base: NewBase(&models.AlertNotification{ Uid: config.UID, @@ -69,6 +70,7 @@ func NewGoogleChatNotifier(config *GoogleChatConfig, ns notifications.WebhookSen URL: config.URL, log: log.New("alerting.notifier.googlechat"), ns: ns, + images: images, tmpl: t, } } @@ -138,6 +140,9 @@ func (gcn *GoogleChatNotifier) Notify(ctx context.Context, as ...*types.Alert) ( }, }, } + if screenshots := gcn.buildScreenshotCard(ctx, as); screenshots != nil { + res.Cards = append(res.Cards, *screenshots) + } if tmplErr != nil { gcn.log.Warn("failed to template GoogleChat message", "err", tmplErr.Error()) @@ -176,6 +181,53 @@ func (gcn *GoogleChatNotifier) SendResolved() bool { return !gcn.GetDisableResolveMessage() } +func (gcn *GoogleChatNotifier) buildScreenshotCard(ctx context.Context, alerts []*types.Alert) *card { + card := card{ + Header: header{ + Title: "Screenshots", + }, + Sections: []section{}, + } + for _, alert := range alerts { + imgToken := getTokenFromAnnotations(alert.Annotations) + if len(imgToken) == 0 { + continue + } + + timeoutCtx, cancel := context.WithTimeout(ctx, ImageStoreTimeout) + imgURL, err := gcn.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. + gcn.log.Warn("failed to retrieve image url from store", "error", err) + } + } + + if len(imgURL) > 0 { + section := section{ + Widgets: []widget{ + textParagraphWidget{ + Text: text{ + Text: fmt.Sprintf("%s: %s", alert.Status(), alert.Name()), + }, + }, + imageWidget{ + Image: imageData{ + ImageURL: imgURL, + }, + }, + }, + } + card.Sections = append(card.Sections, section) + } + } + if len(card.Sections) == 0 { + return nil + } + return &card +} + // Structs used to build a custom Google Hangouts Chat message card. // See: https://developers.google.com/hangouts/chat/reference/message-formats/cards type outerStruct struct { @@ -208,6 +260,14 @@ type textParagraphWidget struct { Text text `json:"textParagraph"` } +type imageWidget struct { + Image imageData `json:"image"` +} + +type imageData struct { + ImageURL string `json:"imageUrl"` +} + type text struct { Text string `json:"text"` } diff --git a/pkg/services/ngalert/notifier/channels/googlechat_test.go b/pkg/services/ngalert/notifier/channels/googlechat_test.go index 7b4629cec57..f4b56e8df86 100644 --- a/pkg/services/ngalert/notifier/channels/googlechat_test.go +++ b/pkg/services/ngalert/notifier/channels/googlechat_test.go @@ -329,10 +329,11 @@ func TestGoogleChatNotifier(t *testing.T) { return } require.NoError(t, err) + imageStore := &UnavailableImageStore{} ctx := notify.WithGroupKey(context.Background(), "alertname") ctx = notify.WithGroupLabels(ctx, model.LabelSet{"alertname": ""}) - pn := NewGoogleChatNotifier(cfg, webhookSender, tmpl) + pn := NewGoogleChatNotifier(cfg, imageStore, webhookSender, tmpl) ok, err := pn.Notify(ctx, c.alerts...) if c.expMsgError != nil { require.False(t, ok)