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
This commit is contained in:
Alexander Weaver 2022-05-23 18:15:44 -05:00 committed by GitHub
parent b1ba0bc7b0
commit 2ba4f7ed7d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 64 additions and 3 deletions

View File

@ -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"`
}

View File

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