From 6e44b36a30d8e43b07d8bd049cc47a8f5a77c6e9 Mon Sep 17 00:00:00 2001 From: George Robinson Date: Wed, 22 Jun 2022 11:03:08 +0100 Subject: [PATCH] Alerting: Add support for images in Kafka alerts (#50758) --- .../ngalert/notifier/channels/kafka.go | 22 +++++++++++++++++-- .../ngalert/notifier/channels/kafka_test.go | 16 +++++++++----- 2 files changed, 30 insertions(+), 8 deletions(-) diff --git a/pkg/services/ngalert/notifier/channels/kafka.go b/pkg/services/ngalert/notifier/channels/kafka.go index 435407bb2de..b812fe2cc49 100644 --- a/pkg/services/ngalert/notifier/channels/kafka.go +++ b/pkg/services/ngalert/notifier/channels/kafka.go @@ -13,6 +13,7 @@ import ( "github.com/grafana/grafana/pkg/components/simplejson" "github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/models" + ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models" "github.com/grafana/grafana/pkg/services/notifications" ) @@ -23,6 +24,7 @@ type KafkaNotifier struct { Endpoint string Topic string log log.Logger + images ImageStore ns notifications.WebhookSender tmpl *template.Template } @@ -41,7 +43,7 @@ func KafkaFactory(fc FactoryConfig) (NotificationChannel, error) { Cfg: *fc.Config, } } - return NewKafkaNotifier(cfg, fc.NotificationService, fc.Template), nil + return NewKafkaNotifier(cfg, fc.ImageStore, fc.NotificationService, fc.Template), nil } func NewKafkaConfig(config *NotificationChannelConfig) (*KafkaConfig, error) { @@ -61,7 +63,7 @@ func NewKafkaConfig(config *NotificationChannelConfig) (*KafkaConfig, error) { } // NewKafkaNotifier is the constructor function for the Kafka notifier. -func NewKafkaNotifier(config *KafkaConfig, ns notifications.WebhookSender, t *template.Template) *KafkaNotifier { +func NewKafkaNotifier(config *KafkaConfig, images ImageStore, ns notifications.WebhookSender, t *template.Template) *KafkaNotifier { return &KafkaNotifier{ Base: NewBase(&models.AlertNotification{ Uid: config.UID, @@ -73,6 +75,7 @@ func NewKafkaNotifier(config *KafkaConfig, ns notifications.WebhookSender, t *te Endpoint: config.Endpoint, Topic: config.Topic, log: log.New("alerting.notifier.kafka"), + images: images, ns: ns, tmpl: t, } @@ -102,6 +105,21 @@ func (kn *KafkaNotifier) Notify(ctx context.Context, as ...*types.Alert) (bool, ruleURL := joinUrlPath(kn.tmpl.ExternalURL.String(), "/alerting/list", kn.log) bodyJSON.Set("client_url", ruleURL) + var contexts []interface{} + _ = withStoredImages(ctx, kn.log, kn.images, + func(index int, image *ngmodels.Image) error { + if image != nil && image.URL != "" { + imageJSON := simplejson.New() + imageJSON.Set("type", "image") + imageJSON.Set("src", image.URL) + contexts = append(contexts, imageJSON) + } + return nil + }, as...) + if len(contexts) > 0 { + bodyJSON.Set("contexts", contexts) + } + groupKey, err := notify.ExtractGroupKey(ctx) if err != nil { return false, err diff --git a/pkg/services/ngalert/notifier/channels/kafka_test.go b/pkg/services/ngalert/notifier/channels/kafka_test.go index 054431e52a9..4532083e1ee 100644 --- a/pkg/services/ngalert/notifier/channels/kafka_test.go +++ b/pkg/services/ngalert/notifier/channels/kafka_test.go @@ -16,6 +16,8 @@ import ( func TestKafkaNotifier(t *testing.T) { tmpl := templateForTests(t) + images := newFakeImageStore(2) + externalURL, err := url.Parse("http://localhost") require.NoError(t, err) tmpl.ExternalURL = externalURL @@ -29,7 +31,7 @@ func TestKafkaNotifier(t *testing.T) { expMsgError error }{ { - name: "One alert", + name: "A single alert with image", settings: `{ "kafkaRestProxy": "http://localhost", "kafkaTopic": "sometopic" @@ -38,7 +40,7 @@ func TestKafkaNotifier(t *testing.T) { { Alert: model.Alert{ Labels: model.LabelSet{"alertname": "alert1", "lbl1": "val1"}, - Annotations: model.LabelSet{"ann1": "annv1", "__dashboardUid__": "abcd", "__panelId__": "efgh"}, + Annotations: model.LabelSet{"ann1": "annv1", "__dashboardUid__": "abcd", "__panelId__": "efgh", "__alertScreenshotToken__": "test-image-1"}, }, }, }, @@ -50,6 +52,7 @@ func TestKafkaNotifier(t *testing.T) { "alert_state": "alerting", "client": "Grafana", "client_url": "http://localhost/alerting/list", + "contexts": [{"type": "image", "src": "https://www.example.com/test-image-1.jpg"}], "description": "[FIRING:1] (val1)", "details": "**Firing**\n\nValue: [no value]\nLabels:\n - alertname = alert1\n - lbl1 = val1\nAnnotations:\n - ann1 = annv1\nSilence: http://localhost/alerting/silence/new?alertmanager=grafana&matcher=alertname%3Dalert1&matcher=lbl1%3Dval1\nDashboard: http://localhost/d/abcd\nPanel: http://localhost/d/abcd?viewPanel=efgh\n", "incident_key": "6e3538104c14b583da237e9693b76debbc17f0f8058ef20492e5853096cf8733" @@ -59,7 +62,7 @@ func TestKafkaNotifier(t *testing.T) { }`, expMsgError: nil, }, { - name: "Multiple alerts", + name: "Multiple alerts with images", settings: `{ "kafkaRestProxy": "http://localhost", "kafkaTopic": "sometopic" @@ -68,12 +71,12 @@ func TestKafkaNotifier(t *testing.T) { { Alert: model.Alert{ Labels: model.LabelSet{"alertname": "alert1", "lbl1": "val1"}, - Annotations: model.LabelSet{"ann1": "annv1"}, + Annotations: model.LabelSet{"ann1": "annv1", "__alertScreenshotToken__": "test-image-1"}, }, }, { Alert: model.Alert{ Labels: model.LabelSet{"alertname": "alert1", "lbl1": "val2"}, - Annotations: model.LabelSet{"ann1": "annv2"}, + Annotations: model.LabelSet{"ann1": "annv2", "__alertScreenshotToken__": "test-image-2"}, }, }, }, @@ -85,6 +88,7 @@ func TestKafkaNotifier(t *testing.T) { "alert_state": "alerting", "client": "Grafana", "client_url": "http://localhost/alerting/list", + "contexts": [{"type": "image", "src": "https://www.example.com/test-image-1.jpg"}, {"type": "image", "src": "https://www.example.com/test-image-2.jpg"}], "description": "[FIRING:2] ", "details": "**Firing**\n\nValue: [no value]\nLabels:\n - alertname = alert1\n - lbl1 = val1\nAnnotations:\n - ann1 = annv1\nSilence: http://localhost/alerting/silence/new?alertmanager=grafana&matcher=alertname%3Dalert1&matcher=lbl1%3Dval1\n\nValue: [no value]\nLabels:\n - alertname = alert1\n - lbl1 = val2\nAnnotations:\n - ann1 = annv2\nSilence: http://localhost/alerting/silence/new?alertmanager=grafana&matcher=alertname%3Dalert1&matcher=lbl1%3Dval2\n", "incident_key": "6e3538104c14b583da237e9693b76debbc17f0f8058ef20492e5853096cf8733" @@ -127,7 +131,7 @@ func TestKafkaNotifier(t *testing.T) { ctx := notify.WithGroupKey(context.Background(), "alertname") ctx = notify.WithGroupLabels(ctx, model.LabelSet{"alertname": ""}) - pn := NewKafkaNotifier(cfg, webhookSender, tmpl) + pn := NewKafkaNotifier(cfg, images, webhookSender, tmpl) ok, err := pn.Notify(ctx, c.alerts...) if c.expMsgError != nil { require.False(t, ok)