Alerting: Add support for image annotation in Alertmanager alerts (#50686)

This commit is contained in:
George Robinson 2022-06-21 09:06:00 +01:00 committed by GitHub
parent 6f2c0d467f
commit c8466d285c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 34 additions and 17 deletions

View File

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

View File

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

View File

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