diff --git a/pkg/services/ngalert/notifier/channels/alertmanager.go b/pkg/services/ngalert/notifier/channels/alertmanager.go index bf6d18c4d95..b7405dafb5d 100644 --- a/pkg/services/ngalert/notifier/channels/alertmanager.go +++ b/pkg/services/ngalert/notifier/channels/alertmanager.go @@ -5,14 +5,17 @@ import ( "encoding/json" "errors" "fmt" + "net/url" "strings" "github.com/prometheus/alertmanager/template" "github.com/prometheus/alertmanager/types" + "github.com/prometheus/common/model" "github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/models" + ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models" ) // GetDecryptedValueFn is a function that returns the decrypted value of @@ -60,11 +63,11 @@ func AlertmanagerFactory(fc FactoryConfig) (NotificationChannel, error) { Cfg: *fc.Config, } } - return NewAlertmanagerNotifier(config, nil, fc.DecryptFunc), nil + return NewAlertmanagerNotifier(config, fc.ImageStore, nil, fc.DecryptFunc), nil } // NewAlertmanagerNotifier returns a new Alertmanager notifier. -func NewAlertmanagerNotifier(config *AlertmanagerConfig, _ *template.Template, fn GetDecryptedValueFn) *AlertmanagerNotifier { +func NewAlertmanagerNotifier(config *AlertmanagerConfig, images ImageStore, _ *template.Template, fn GetDecryptedValueFn) *AlertmanagerNotifier { return &AlertmanagerNotifier{ Base: NewBase(&models.AlertNotification{ Uid: config.UID, @@ -72,6 +75,7 @@ func NewAlertmanagerNotifier(config *AlertmanagerConfig, _ *template.Template, f DisableResolveMessage: config.DisableResolveMessage, Settings: config.Settings, }), + images: images, urls: config.URLs, basicAuthUser: config.BasicAuthUser, basicAuthPassword: config.BasicAuthPassword, @@ -82,6 +86,7 @@ func NewAlertmanagerNotifier(config *AlertmanagerConfig, _ *template.Template, f // AlertmanagerNotifier sends alert notifications to the alert manager type AlertmanagerNotifier struct { *Base + images ImageStore urls []*url.URL basicAuthUser string @@ -96,6 +101,16 @@ func (n *AlertmanagerNotifier) Notify(ctx context.Context, as ...*types.Alert) ( return true, nil } + _ = withStoredImages(ctx, n.logger, n.images, + func(index int, image *ngmodels.Image) error { + // If there is an image for this alert and the image has been uploaded + // to a public URL then include it as an annotation + if image != nil && image.URL != "" { + as[index].Annotations["image"] = model.LabelValue(image.URL) + } + return nil + }, as...) + body, err := json.Marshal(as) if err != nil { return false, err diff --git a/pkg/services/ngalert/notifier/channels/alertmanager_test.go b/pkg/services/ngalert/notifier/channels/alertmanager_test.go index fb77c415e11..bfa808253f9 100644 --- a/pkg/services/ngalert/notifier/channels/alertmanager_test.go +++ b/pkg/services/ngalert/notifier/channels/alertmanager_test.go @@ -66,7 +66,7 @@ func TestNewAlertmanagerNotifier(t *testing.T) { return } require.NoError(t, err) - sn := NewAlertmanagerNotifier(cfg, tmpl, decryptFn) + sn := NewAlertmanagerNotifier(cfg, &UnavailableImageStore{}, tmpl, decryptFn) require.NotNil(t, sn) }) } @@ -75,6 +75,8 @@ func TestNewAlertmanagerNotifier(t *testing.T) { func TestAlertmanagerNotifier_Notify(t *testing.T) { tmpl := templateForTests(t) + images := newFakeImageStore(1) + externalURL, err := url.Parse("http://localhost") require.NoError(t, err) tmpl.ExternalURL = externalURL @@ -99,8 +101,19 @@ func TestAlertmanagerNotifier_Notify(t *testing.T) { }, }, receiverName: "Alertmanager", - }, - { + }, { + name: "Default config with one alert with image URL", + settings: `{"url": "https://alertmanager.com"}`, + alerts: []*types.Alert{ + { + Alert: model.Alert{ + Labels: model.LabelSet{"__alert_rule_uid__": "rule uid", "alertname": "alert1"}, + Annotations: model.LabelSet{"__alertScreenshotToken__": "test-image-1"}, + }, + }, + }, + receiverName: "Alertmanager", + }, { name: "Default config with one alert with empty receiver name", settings: `{"url": "https://alertmanager.com"}`, alerts: []*types.Alert{ @@ -146,7 +159,7 @@ func TestAlertmanagerNotifier_Notify(t *testing.T) { decryptFn := secretsService.GetDecryptedValue cfg, err := NewAlertmanagerConfig(m, decryptFn) require.NoError(t, err) - sn := NewAlertmanagerNotifier(cfg, tmpl, decryptFn) + sn := NewAlertmanagerNotifier(cfg, images, tmpl, decryptFn) var body []byte origSendHTTPRequest := sendHTTPRequest t.Cleanup(func() { diff --git a/pkg/services/ngalert/notifier/channels/slack_test.go b/pkg/services/ngalert/notifier/channels/slack_test.go index 83899b44f59..e76bd0b15b0 100644 --- a/pkg/services/ngalert/notifier/channels/slack_test.go +++ b/pkg/services/ngalert/notifier/channels/slack_test.go @@ -7,7 +7,6 @@ import ( "net/http" "net/http/httptest" "net/url" - "os" "testing" "github.com/grafana/grafana/pkg/components/simplejson" @@ -26,13 +25,6 @@ import ( func TestSlackNotifier(t *testing.T) { tmpl := templateForTests(t) - // Create our temporary image file. - f, err := os.CreateTemp("", "ngalert-images-example*.png") - if err != nil { - panic("Temp file error!") - } - defer func() { _ = os.Remove(f.Name()) }() - fakeImageStore := &fakeImageStore{ Images: []*models.Image{ { @@ -42,9 +34,6 @@ func TestSlackNotifier(t *testing.T) { }, } - _, _ = f.Write([]byte("test image")) - _ = f.Close() - externalURL, err := url.Parse("http://localhost") require.NoError(t, err) tmpl.ExternalURL = externalURL