mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Alerting: Add support for image annotation in Alertmanager alerts (#50686)
This commit is contained in:
parent
6f2c0d467f
commit
c8466d285c
@ -5,14 +5,17 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/prometheus/alertmanager/template"
|
"github.com/prometheus/alertmanager/template"
|
||||||
"github.com/prometheus/alertmanager/types"
|
"github.com/prometheus/alertmanager/types"
|
||||||
|
"github.com/prometheus/common/model"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/infra/log"
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"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
|
// GetDecryptedValueFn is a function that returns the decrypted value of
|
||||||
@ -60,11 +63,11 @@ func AlertmanagerFactory(fc FactoryConfig) (NotificationChannel, error) {
|
|||||||
Cfg: *fc.Config,
|
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.
|
// 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{
|
return &AlertmanagerNotifier{
|
||||||
Base: NewBase(&models.AlertNotification{
|
Base: NewBase(&models.AlertNotification{
|
||||||
Uid: config.UID,
|
Uid: config.UID,
|
||||||
@ -72,6 +75,7 @@ func NewAlertmanagerNotifier(config *AlertmanagerConfig, _ *template.Template, f
|
|||||||
DisableResolveMessage: config.DisableResolveMessage,
|
DisableResolveMessage: config.DisableResolveMessage,
|
||||||
Settings: config.Settings,
|
Settings: config.Settings,
|
||||||
}),
|
}),
|
||||||
|
images: images,
|
||||||
urls: config.URLs,
|
urls: config.URLs,
|
||||||
basicAuthUser: config.BasicAuthUser,
|
basicAuthUser: config.BasicAuthUser,
|
||||||
basicAuthPassword: config.BasicAuthPassword,
|
basicAuthPassword: config.BasicAuthPassword,
|
||||||
@ -82,6 +86,7 @@ func NewAlertmanagerNotifier(config *AlertmanagerConfig, _ *template.Template, f
|
|||||||
// AlertmanagerNotifier sends alert notifications to the alert manager
|
// AlertmanagerNotifier sends alert notifications to the alert manager
|
||||||
type AlertmanagerNotifier struct {
|
type AlertmanagerNotifier struct {
|
||||||
*Base
|
*Base
|
||||||
|
images ImageStore
|
||||||
|
|
||||||
urls []*url.URL
|
urls []*url.URL
|
||||||
basicAuthUser string
|
basicAuthUser string
|
||||||
@ -96,6 +101,16 @@ func (n *AlertmanagerNotifier) Notify(ctx context.Context, as ...*types.Alert) (
|
|||||||
return true, nil
|
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)
|
body, err := json.Marshal(as)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
|
@ -66,7 +66,7 @@ func TestNewAlertmanagerNotifier(t *testing.T) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
sn := NewAlertmanagerNotifier(cfg, tmpl, decryptFn)
|
sn := NewAlertmanagerNotifier(cfg, &UnavailableImageStore{}, tmpl, decryptFn)
|
||||||
require.NotNil(t, sn)
|
require.NotNil(t, sn)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -75,6 +75,8 @@ func TestNewAlertmanagerNotifier(t *testing.T) {
|
|||||||
func TestAlertmanagerNotifier_Notify(t *testing.T) {
|
func TestAlertmanagerNotifier_Notify(t *testing.T) {
|
||||||
tmpl := templateForTests(t)
|
tmpl := templateForTests(t)
|
||||||
|
|
||||||
|
images := newFakeImageStore(1)
|
||||||
|
|
||||||
externalURL, err := url.Parse("http://localhost")
|
externalURL, err := url.Parse("http://localhost")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
tmpl.ExternalURL = externalURL
|
tmpl.ExternalURL = externalURL
|
||||||
@ -99,8 +101,19 @@ func TestAlertmanagerNotifier_Notify(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
receiverName: "Alertmanager",
|
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",
|
name: "Default config with one alert with empty receiver name",
|
||||||
settings: `{"url": "https://alertmanager.com"}`,
|
settings: `{"url": "https://alertmanager.com"}`,
|
||||||
alerts: []*types.Alert{
|
alerts: []*types.Alert{
|
||||||
@ -146,7 +159,7 @@ func TestAlertmanagerNotifier_Notify(t *testing.T) {
|
|||||||
decryptFn := secretsService.GetDecryptedValue
|
decryptFn := secretsService.GetDecryptedValue
|
||||||
cfg, err := NewAlertmanagerConfig(m, decryptFn)
|
cfg, err := NewAlertmanagerConfig(m, decryptFn)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
sn := NewAlertmanagerNotifier(cfg, tmpl, decryptFn)
|
sn := NewAlertmanagerNotifier(cfg, images, tmpl, decryptFn)
|
||||||
var body []byte
|
var body []byte
|
||||||
origSendHTTPRequest := sendHTTPRequest
|
origSendHTTPRequest := sendHTTPRequest
|
||||||
t.Cleanup(func() {
|
t.Cleanup(func() {
|
||||||
|
@ -7,7 +7,6 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||||
@ -26,13 +25,6 @@ import (
|
|||||||
func TestSlackNotifier(t *testing.T) {
|
func TestSlackNotifier(t *testing.T) {
|
||||||
tmpl := templateForTests(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{
|
fakeImageStore := &fakeImageStore{
|
||||||
Images: []*models.Image{
|
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")
|
externalURL, err := url.Parse("http://localhost")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
tmpl.ExternalURL = externalURL
|
tmpl.ExternalURL = externalURL
|
||||||
|
Loading…
Reference in New Issue
Block a user