mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Alerting: Remove dependency on Grafana notifications package in alerting notifiers (#60271)
* create sender service interface and bridge to grafana notifier service * update notifiers to use local sender interface
This commit is contained in:
parent
07b5043222
commit
7c3ab4a715
@ -514,7 +514,7 @@ func (am *Alertmanager) buildReceiverIntegration(r *apimodels.PostableGrafanaRec
|
|||||||
SecureSettings: secureSettings,
|
SecureSettings: secureSettings,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
factoryConfig, err := channels.NewFactoryConfig(cfg, am.NotificationService, am.decryptFn, tmpl, am.Store)
|
factoryConfig, err := channels.NewFactoryConfig(cfg, NewNotificationSender(am.NotificationService), am.decryptFn, tmpl, am.Store)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, InvalidReceiverError{
|
return nil, InvalidReceiverError{
|
||||||
Receiver: r,
|
Receiver: r,
|
||||||
|
@ -12,7 +12,6 @@ import (
|
|||||||
|
|
||||||
"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"
|
||||||
"github.com/grafana/grafana/pkg/services/notifications"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const defaultDingdingMsgType = "link"
|
const defaultDingdingMsgType = "link"
|
||||||
@ -73,7 +72,7 @@ func newDingDingNotifier(fc FactoryConfig) (*DingDingNotifier, error) {
|
|||||||
type DingDingNotifier struct {
|
type DingDingNotifier struct {
|
||||||
*Base
|
*Base
|
||||||
log log.Logger
|
log log.Logger
|
||||||
ns notifications.WebhookSender
|
ns WebhookSender
|
||||||
tmpl *template.Template
|
tmpl *template.Template
|
||||||
settings dingDingSettings
|
settings dingDingSettings
|
||||||
}
|
}
|
||||||
@ -107,9 +106,9 @@ func (dd *DingDingNotifier) Notify(ctx context.Context, as ...*types.Alert) (boo
|
|||||||
u = dd.settings.URL
|
u = dd.settings.URL
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd := &models.SendWebhookSync{Url: u, Body: b}
|
cmd := &SendWebhookSettings{Url: u, Body: b}
|
||||||
|
|
||||||
if err := dd.ns.SendWebhookSync(ctx, cmd); err != nil {
|
if err := dd.ns.SendWebhook(ctx, cmd); err != nil {
|
||||||
return false, fmt.Errorf("send notification to dingding: %w", err)
|
return false, fmt.Errorf("send notification to dingding: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,14 +20,13 @@ import (
|
|||||||
"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"
|
ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models"
|
||||||
"github.com/grafana/grafana/pkg/services/notifications"
|
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
)
|
)
|
||||||
|
|
||||||
type DiscordNotifier struct {
|
type DiscordNotifier struct {
|
||||||
*Base
|
*Base
|
||||||
log log.Logger
|
log log.Logger
|
||||||
ns notifications.WebhookSender
|
ns WebhookSender
|
||||||
images ImageStore
|
images ImageStore
|
||||||
tmpl *template.Template
|
tmpl *template.Template
|
||||||
settings discordSettings
|
settings discordSettings
|
||||||
@ -179,7 +178,7 @@ func (d DiscordNotifier) Notify(ctx context.Context, as ...*types.Alert) (bool,
|
|||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := d.ns.SendWebhookSync(ctx, cmd); err != nil {
|
if err := d.ns.SendWebhook(ctx, cmd); err != nil {
|
||||||
d.log.Error("failed to send notification to Discord", "error", err)
|
d.log.Error("failed to send notification to Discord", "error", err)
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
@ -236,8 +235,8 @@ func (d DiscordNotifier) constructAttachments(ctx context.Context, as []*types.A
|
|||||||
return attachments
|
return attachments
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d DiscordNotifier) buildRequest(url string, body []byte, attachments []discordAttachment) (*models.SendWebhookSync, error) {
|
func (d DiscordNotifier) buildRequest(url string, body []byte, attachments []discordAttachment) (*SendWebhookSettings, error) {
|
||||||
cmd := &models.SendWebhookSync{
|
cmd := &SendWebhookSettings{
|
||||||
Url: url,
|
Url: url,
|
||||||
HttpMethod: "POST",
|
HttpMethod: "POST",
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,6 @@ import (
|
|||||||
"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"
|
ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models"
|
||||||
"github.com/grafana/grafana/pkg/services/notifications"
|
|
||||||
"github.com/grafana/grafana/pkg/util"
|
"github.com/grafana/grafana/pkg/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -27,7 +26,7 @@ type EmailNotifier struct {
|
|||||||
Message string
|
Message string
|
||||||
Subject string
|
Subject string
|
||||||
log log.Logger
|
log log.Logger
|
||||||
ns notifications.EmailSender
|
ns EmailSender
|
||||||
images ImageStore
|
images ImageStore
|
||||||
tmpl *template.Template
|
tmpl *template.Template
|
||||||
}
|
}
|
||||||
@ -69,7 +68,7 @@ func NewEmailConfig(config *NotificationChannelConfig) (*EmailConfig, error) {
|
|||||||
|
|
||||||
// NewEmailNotifier is the constructor function
|
// NewEmailNotifier is the constructor function
|
||||||
// for the EmailNotifier.
|
// for the EmailNotifier.
|
||||||
func NewEmailNotifier(config *EmailConfig, ns notifications.EmailSender, images ImageStore, t *template.Template) *EmailNotifier {
|
func NewEmailNotifier(config *EmailConfig, ns EmailSender, images ImageStore, t *template.Template) *EmailNotifier {
|
||||||
return &EmailNotifier{
|
return &EmailNotifier{
|
||||||
Base: NewBase(&models.AlertNotification{
|
Base: NewBase(&models.AlertNotification{
|
||||||
Uid: config.UID,
|
Uid: config.UID,
|
||||||
@ -126,33 +125,31 @@ func (en *EmailNotifier) Notify(ctx context.Context, alerts ...*types.Alert) (bo
|
|||||||
return nil
|
return nil
|
||||||
}, alerts...)
|
}, alerts...)
|
||||||
|
|
||||||
cmd := &models.SendEmailCommandSync{
|
cmd := &SendEmailSettings{
|
||||||
SendEmailCommand: models.SendEmailCommand{
|
Subject: subject,
|
||||||
Subject: subject,
|
Data: map[string]interface{}{
|
||||||
Data: map[string]interface{}{
|
"Title": subject,
|
||||||
"Title": subject,
|
"Message": tmpl(en.Message),
|
||||||
"Message": tmpl(en.Message),
|
"Status": data.Status,
|
||||||
"Status": data.Status,
|
"Alerts": data.Alerts,
|
||||||
"Alerts": data.Alerts,
|
"GroupLabels": data.GroupLabels,
|
||||||
"GroupLabels": data.GroupLabels,
|
"CommonLabels": data.CommonLabels,
|
||||||
"CommonLabels": data.CommonLabels,
|
"CommonAnnotations": data.CommonAnnotations,
|
||||||
"CommonAnnotations": data.CommonAnnotations,
|
"ExternalURL": data.ExternalURL,
|
||||||
"ExternalURL": data.ExternalURL,
|
"RuleUrl": ruleURL,
|
||||||
"RuleUrl": ruleURL,
|
"AlertPageUrl": alertPageURL,
|
||||||
"AlertPageUrl": alertPageURL,
|
|
||||||
},
|
|
||||||
EmbeddedFiles: embeddedFiles,
|
|
||||||
To: en.Addresses,
|
|
||||||
SingleEmail: en.SingleEmail,
|
|
||||||
Template: "ng_alert_notification",
|
|
||||||
},
|
},
|
||||||
|
EmbeddedFiles: embeddedFiles,
|
||||||
|
To: en.Addresses,
|
||||||
|
SingleEmail: en.SingleEmail,
|
||||||
|
Template: "ng_alert_notification",
|
||||||
}
|
}
|
||||||
|
|
||||||
if tmplErr != nil {
|
if tmplErr != nil {
|
||||||
en.log.Warn("failed to template email message", "error", tmplErr.Error())
|
en.log.Warn("failed to template email message", "error", tmplErr.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := en.ns.SendEmailCommandHandlerSync(ctx, cmd); err != nil {
|
if err := en.ns.SendEmail(ctx, cmd); err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,7 +104,7 @@ func TestEmailNotifier(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestEmailNotifierIntegration(t *testing.T) {
|
func TestEmailNotifierIntegration(t *testing.T) {
|
||||||
ns := CreateNotificationService(t)
|
ns := createEmailSender(t)
|
||||||
|
|
||||||
emailTmpl := templateForTests(t)
|
emailTmpl := templateForTests(t)
|
||||||
externalURL, err := url.Parse("http://localhost/base")
|
externalURL, err := url.Parse("http://localhost/base")
|
||||||
@ -267,7 +267,7 @@ func TestEmailNotifierIntegration(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func createSut(t *testing.T, messageTmpl string, subjectTmpl string, emailTmpl *template.Template, ns notifications.EmailSender) *EmailNotifier {
|
func createSut(t *testing.T, messageTmpl string, subjectTmpl string, emailTmpl *template.Template, ns *emailSender) *EmailNotifier {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
json := `{
|
json := `{
|
||||||
@ -295,10 +295,10 @@ func createSut(t *testing.T, messageTmpl string, subjectTmpl string, emailTmpl *
|
|||||||
return emailNotifier
|
return emailNotifier
|
||||||
}
|
}
|
||||||
|
|
||||||
func getSingleSentMessage(t *testing.T, ns *notifications.NotificationService) *notifications.Message {
|
func getSingleSentMessage(t *testing.T, ns *emailSender) *notifications.Message {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
mailer := ns.GetMailer().(*notifications.FakeMailer)
|
mailer := ns.ns.GetMailer().(*notifications.FakeMailer)
|
||||||
require.Len(t, mailer.Sent, 1)
|
require.Len(t, mailer.Sent, 1)
|
||||||
sent := mailer.Sent[0]
|
sent := mailer.Sent[0]
|
||||||
mailer.Sent = []*notifications.Message{}
|
mailer.Sent = []*notifications.Message{}
|
||||||
|
@ -8,12 +8,11 @@ import (
|
|||||||
"github.com/prometheus/alertmanager/template"
|
"github.com/prometheus/alertmanager/template"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/services/ngalert/models"
|
"github.com/grafana/grafana/pkg/services/ngalert/models"
|
||||||
"github.com/grafana/grafana/pkg/services/notifications"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type FactoryConfig struct {
|
type FactoryConfig struct {
|
||||||
Config *NotificationChannelConfig
|
Config *NotificationChannelConfig
|
||||||
NotificationService notifications.Service
|
NotificationService NotificationSender
|
||||||
DecryptFunc GetDecryptedValueFn
|
DecryptFunc GetDecryptedValueFn
|
||||||
ImageStore ImageStore
|
ImageStore ImageStore
|
||||||
// Used to retrieve image URLs for messages, or data for uploads.
|
// Used to retrieve image URLs for messages, or data for uploads.
|
||||||
@ -24,7 +23,7 @@ type ImageStore interface {
|
|||||||
GetImage(ctx context.Context, token string) (*models.Image, error)
|
GetImage(ctx context.Context, token string) (*models.Image, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewFactoryConfig(config *NotificationChannelConfig, notificationService notifications.Service,
|
func NewFactoryConfig(config *NotificationChannelConfig, notificationService NotificationSender,
|
||||||
decryptFunc GetDecryptedValueFn, template *template.Template, imageStore ImageStore) (FactoryConfig, error) {
|
decryptFunc GetDecryptedValueFn, template *template.Template, imageStore ImageStore) (FactoryConfig, error) {
|
||||||
if config.Settings == nil {
|
if config.Settings == nil {
|
||||||
return FactoryConfig{}, errors.New("no settings supplied")
|
return FactoryConfig{}, errors.New("no settings supplied")
|
||||||
|
@ -14,7 +14,6 @@ import (
|
|||||||
"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"
|
ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models"
|
||||||
"github.com/grafana/grafana/pkg/services/notifications"
|
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -23,7 +22,7 @@ import (
|
|||||||
type GoogleChatNotifier struct {
|
type GoogleChatNotifier struct {
|
||||||
*Base
|
*Base
|
||||||
log log.Logger
|
log log.Logger
|
||||||
ns notifications.WebhookSender
|
ns WebhookSender
|
||||||
images ImageStore
|
images ImageStore
|
||||||
tmpl *template.Template
|
tmpl *template.Template
|
||||||
settings googleChatSettings
|
settings googleChatSettings
|
||||||
@ -160,7 +159,7 @@ func (gcn *GoogleChatNotifier) Notify(ctx context.Context, as ...*types.Alert) (
|
|||||||
return false, fmt.Errorf("marshal json: %w", err)
|
return false, fmt.Errorf("marshal json: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd := &models.SendWebhookSync{
|
cmd := &SendWebhookSettings{
|
||||||
Url: u,
|
Url: u,
|
||||||
HttpMethod: "POST",
|
HttpMethod: "POST",
|
||||||
HttpHeader: map[string]string{
|
HttpHeader: map[string]string{
|
||||||
@ -169,7 +168,7 @@ func (gcn *GoogleChatNotifier) Notify(ctx context.Context, as ...*types.Alert) (
|
|||||||
Body: string(body),
|
Body: string(body),
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := gcn.ns.SendWebhookSync(ctx, cmd); err != nil {
|
if err := gcn.ns.SendWebhook(ctx, cmd); err != nil {
|
||||||
gcn.log.Error("Failed to send Google Hangouts Chat alert", "error", err, "webhook", gcn.Name)
|
gcn.log.Error("Failed to send Google Hangouts Chat alert", "error", err, "webhook", gcn.Name)
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,6 @@ import (
|
|||||||
"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"
|
ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models"
|
||||||
"github.com/grafana/grafana/pkg/services/notifications"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// KafkaNotifier is responsible for sending
|
// KafkaNotifier is responsible for sending
|
||||||
@ -23,7 +22,7 @@ type KafkaNotifier struct {
|
|||||||
*Base
|
*Base
|
||||||
log log.Logger
|
log log.Logger
|
||||||
images ImageStore
|
images ImageStore
|
||||||
ns notifications.WebhookSender
|
ns WebhookSender
|
||||||
tmpl *template.Template
|
tmpl *template.Template
|
||||||
settings kafkaSettings
|
settings kafkaSettings
|
||||||
}
|
}
|
||||||
@ -91,7 +90,7 @@ func (kn *KafkaNotifier) Notify(ctx context.Context, as ...*types.Alert) (bool,
|
|||||||
kn.log.Warn("failed to template Kafka message", "error", tmplErr.Error())
|
kn.log.Warn("failed to template Kafka message", "error", tmplErr.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd := &models.SendWebhookSync{
|
cmd := &SendWebhookSettings{
|
||||||
Url: topicURL,
|
Url: topicURL,
|
||||||
Body: body,
|
Body: body,
|
||||||
HttpMethod: "POST",
|
HttpMethod: "POST",
|
||||||
@ -101,7 +100,7 @@ func (kn *KafkaNotifier) Notify(ctx context.Context, as ...*types.Alert) (bool,
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = kn.ns.SendWebhookSync(ctx, cmd); err != nil {
|
if err = kn.ns.SendWebhook(ctx, cmd); err != nil {
|
||||||
kn.log.Error("Failed to send notification to Kafka", "error", err, "body", body)
|
kn.log.Error("Failed to send notification to Kafka", "error", err, "body", body)
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,6 @@ import (
|
|||||||
|
|
||||||
"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"
|
||||||
"github.com/grafana/grafana/pkg/services/notifications"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -24,7 +23,7 @@ var (
|
|||||||
type LineNotifier struct {
|
type LineNotifier struct {
|
||||||
*Base
|
*Base
|
||||||
log log.Logger
|
log log.Logger
|
||||||
ns notifications.WebhookSender
|
ns WebhookSender
|
||||||
tmpl *template.Template
|
tmpl *template.Template
|
||||||
settings lineSettings
|
settings lineSettings
|
||||||
}
|
}
|
||||||
@ -79,7 +78,7 @@ func (ln *LineNotifier) Notify(ctx context.Context, as ...*types.Alert) (bool, e
|
|||||||
form := url.Values{}
|
form := url.Values{}
|
||||||
form.Add("message", body)
|
form.Add("message", body)
|
||||||
|
|
||||||
cmd := &models.SendWebhookSync{
|
cmd := &SendWebhookSettings{
|
||||||
Url: LineNotifyURL,
|
Url: LineNotifyURL,
|
||||||
HttpMethod: "POST",
|
HttpMethod: "POST",
|
||||||
HttpHeader: map[string]string{
|
HttpHeader: map[string]string{
|
||||||
@ -89,7 +88,7 @@ func (ln *LineNotifier) Notify(ctx context.Context, as ...*types.Alert) (bool, e
|
|||||||
Body: form.Encode(),
|
Body: form.Encode(),
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := ln.ns.SendWebhookSync(ctx, cmd); err != nil {
|
if err := ln.ns.SendWebhook(ctx, cmd); err != nil {
|
||||||
ln.log.Error("failed to send notification to LINE", "error", err, "body", body)
|
ln.log.Error("failed to send notification to LINE", "error", err, "body", body)
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,6 @@ import (
|
|||||||
"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"
|
ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models"
|
||||||
"github.com/grafana/grafana/pkg/services/notifications"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -39,7 +38,7 @@ type OpsgenieNotifier struct {
|
|||||||
*Base
|
*Base
|
||||||
tmpl *template.Template
|
tmpl *template.Template
|
||||||
log log.Logger
|
log log.Logger
|
||||||
ns notifications.WebhookSender
|
ns WebhookSender
|
||||||
images ImageStore
|
images ImageStore
|
||||||
settings *opsgenieSettings
|
settings *opsgenieSettings
|
||||||
}
|
}
|
||||||
@ -163,7 +162,7 @@ func (on *OpsgenieNotifier) Notify(ctx context.Context, as ...*types.Alert) (boo
|
|||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd := &models.SendWebhookSync{
|
cmd := &SendWebhookSettings{
|
||||||
Url: url,
|
Url: url,
|
||||||
Body: string(body),
|
Body: string(body),
|
||||||
HttpMethod: http.MethodPost,
|
HttpMethod: http.MethodPost,
|
||||||
@ -173,7 +172,7 @@ func (on *OpsgenieNotifier) Notify(ctx context.Context, as ...*types.Alert) (boo
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := on.ns.SendWebhookSync(ctx, cmd); err != nil {
|
if err := on.ns.SendWebhook(ctx, cmd); err != nil {
|
||||||
return false, fmt.Errorf("send notification to Opsgenie: %w", err)
|
return false, fmt.Errorf("send notification to Opsgenie: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,7 +16,6 @@ import (
|
|||||||
"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"
|
ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models"
|
||||||
"github.com/grafana/grafana/pkg/services/notifications"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -45,7 +44,7 @@ type PagerdutyNotifier struct {
|
|||||||
*Base
|
*Base
|
||||||
tmpl *template.Template
|
tmpl *template.Template
|
||||||
log log.Logger
|
log log.Logger
|
||||||
ns notifications.WebhookSender
|
ns WebhookSender
|
||||||
images ImageStore
|
images ImageStore
|
||||||
settings *pagerdutySettings
|
settings *pagerdutySettings
|
||||||
}
|
}
|
||||||
@ -166,7 +165,7 @@ func (pn *PagerdutyNotifier) Notify(ctx context.Context, as ...*types.Alert) (bo
|
|||||||
}
|
}
|
||||||
|
|
||||||
pn.log.Info("notifying Pagerduty", "event_type", eventType)
|
pn.log.Info("notifying Pagerduty", "event_type", eventType)
|
||||||
cmd := &models.SendWebhookSync{
|
cmd := &SendWebhookSettings{
|
||||||
Url: PagerdutyEventAPIURL,
|
Url: PagerdutyEventAPIURL,
|
||||||
Body: string(body),
|
Body: string(body),
|
||||||
HttpMethod: "POST",
|
HttpMethod: "POST",
|
||||||
@ -174,7 +173,7 @@ func (pn *PagerdutyNotifier) Notify(ctx context.Context, as ...*types.Alert) (bo
|
|||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
if err := pn.ns.SendWebhookSync(ctx, cmd); err != nil {
|
if err := pn.ns.SendWebhook(ctx, cmd); err != nil {
|
||||||
return false, fmt.Errorf("send notification to Pagerduty: %w", err)
|
return false, fmt.Errorf("send notification to Pagerduty: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,7 +20,6 @@ import (
|
|||||||
"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"
|
ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models"
|
||||||
"github.com/grafana/grafana/pkg/services/notifications"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -44,7 +43,7 @@ type PushoverNotifier struct {
|
|||||||
tmpl *template.Template
|
tmpl *template.Template
|
||||||
log log.Logger
|
log log.Logger
|
||||||
images ImageStore
|
images ImageStore
|
||||||
ns notifications.WebhookSender
|
ns WebhookSender
|
||||||
settings pushoverSettings
|
settings pushoverSettings
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -173,14 +172,14 @@ func (pn *PushoverNotifier) Notify(ctx context.Context, as ...*types.Alert) (boo
|
|||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd := &models.SendWebhookSync{
|
cmd := &SendWebhookSettings{
|
||||||
Url: PushoverEndpoint,
|
Url: PushoverEndpoint,
|
||||||
HttpMethod: "POST",
|
HttpMethod: "POST",
|
||||||
HttpHeader: headers,
|
HttpHeader: headers,
|
||||||
Body: uploadBody.String(),
|
Body: uploadBody.String(),
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := pn.ns.SendWebhookSync(ctx, cmd); err != nil {
|
if err := pn.ns.SendWebhook(ctx, cmd); err != nil {
|
||||||
pn.log.Error("failed to send pushover notification", "error", err, "webhook", pn.Name)
|
pn.log.Error("failed to send pushover notification", "error", err, "webhook", pn.Name)
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
46
pkg/services/ngalert/notifier/channels/sender.go
Normal file
46
pkg/services/ngalert/notifier/channels/sender.go
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
package channels
|
||||||
|
|
||||||
|
import "context"
|
||||||
|
|
||||||
|
type SendWebhookSettings struct {
|
||||||
|
Url string
|
||||||
|
User string
|
||||||
|
Password string
|
||||||
|
Body string
|
||||||
|
HttpMethod string
|
||||||
|
HttpHeader map[string]string
|
||||||
|
ContentType string
|
||||||
|
Validation func(body []byte, statusCode int) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// SendEmailSettings is the command for sending emails
|
||||||
|
type SendEmailSettings struct {
|
||||||
|
To []string
|
||||||
|
SingleEmail bool
|
||||||
|
Template string
|
||||||
|
Subject string
|
||||||
|
Data map[string]interface{}
|
||||||
|
Info string
|
||||||
|
ReplyTo []string
|
||||||
|
EmbeddedFiles []string
|
||||||
|
AttachedFiles []*SendEmailAttachFile
|
||||||
|
}
|
||||||
|
|
||||||
|
// SendEmailAttachFile is a definition of the attached files without path
|
||||||
|
type SendEmailAttachFile struct {
|
||||||
|
Name string
|
||||||
|
Content []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
type WebhookSender interface {
|
||||||
|
SendWebhook(ctx context.Context, cmd *SendWebhookSettings) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type EmailSender interface {
|
||||||
|
SendEmail(ctx context.Context, cmd *SendEmailSettings) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type NotificationSender interface {
|
||||||
|
WebhookSender
|
||||||
|
EmailSender
|
||||||
|
}
|
@ -14,14 +14,13 @@ import (
|
|||||||
"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"
|
ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models"
|
||||||
"github.com/grafana/grafana/pkg/services/notifications"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type SensuGoNotifier struct {
|
type SensuGoNotifier struct {
|
||||||
*Base
|
*Base
|
||||||
log log.Logger
|
log log.Logger
|
||||||
images ImageStore
|
images ImageStore
|
||||||
ns notifications.WebhookSender
|
ns WebhookSender
|
||||||
tmpl *template.Template
|
tmpl *template.Template
|
||||||
settings sensuGoSettings
|
settings sensuGoSettings
|
||||||
}
|
}
|
||||||
@ -172,7 +171,7 @@ func (sn *SensuGoNotifier) Notify(ctx context.Context, as ...*types.Alert) (bool
|
|||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd := &models.SendWebhookSync{
|
cmd := &SendWebhookSettings{
|
||||||
Url: fmt.Sprintf("%s/api/core/v2/namespaces/%s/events", strings.TrimSuffix(sn.settings.URL, "/"), namespace),
|
Url: fmt.Sprintf("%s/api/core/v2/namespaces/%s/events", strings.TrimSuffix(sn.settings.URL, "/"), namespace),
|
||||||
Body: string(body),
|
Body: string(body),
|
||||||
HttpMethod: "POST",
|
HttpMethod: "POST",
|
||||||
@ -181,7 +180,7 @@ func (sn *SensuGoNotifier) Notify(ctx context.Context, as ...*types.Alert) (bool
|
|||||||
"Authorization": fmt.Sprintf("Key %s", sn.settings.APIKey),
|
"Authorization": fmt.Sprintf("Key %s", sn.settings.APIKey),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
if err := sn.ns.SendWebhookSync(ctx, cmd); err != nil {
|
if err := sn.ns.SendWebhook(ctx, cmd); err != nil {
|
||||||
sn.log.Error("failed to send Sensu Go event", "error", err, "sensugo", sn.Name)
|
sn.log.Error("failed to send Sensu Go event", "error", err, "sensugo", sn.Name)
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,6 @@ import (
|
|||||||
"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"
|
ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models"
|
||||||
"github.com/grafana/grafana/pkg/services/notifications"
|
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -67,7 +66,7 @@ type SlackNotifier struct {
|
|||||||
log log.Logger
|
log log.Logger
|
||||||
tmpl *template.Template
|
tmpl *template.Template
|
||||||
images ImageStore
|
images ImageStore
|
||||||
webhookSender notifications.WebhookSender
|
webhookSender WebhookSender
|
||||||
sendFn sendFunc
|
sendFn sendFunc
|
||||||
settings slackSettings
|
settings slackSettings
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,6 @@ import (
|
|||||||
"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"
|
ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models"
|
||||||
"github.com/grafana/grafana/pkg/services/notifications"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -253,7 +252,7 @@ type TeamsNotifier struct {
|
|||||||
*Base
|
*Base
|
||||||
tmpl *template.Template
|
tmpl *template.Template
|
||||||
log log.Logger
|
log log.Logger
|
||||||
ns notifications.WebhookSender
|
ns WebhookSender
|
||||||
images ImageStore
|
images ImageStore
|
||||||
settings teamsSettings
|
settings teamsSettings
|
||||||
}
|
}
|
||||||
@ -355,25 +354,27 @@ func (tn *TeamsNotifier) Notify(ctx context.Context, as ...*types.Alert) (bool,
|
|||||||
return false, fmt.Errorf("failed to marshal JSON: %w", err)
|
return false, fmt.Errorf("failed to marshal JSON: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd := &models.SendWebhookSync{Url: u, Body: string(b)}
|
cmd := &SendWebhookSettings{Url: u, Body: string(b)}
|
||||||
// Teams sometimes does not use status codes to show when a request has failed. Instead, the
|
// Teams sometimes does not use status codes to show when a request has failed. Instead, the
|
||||||
// response can contain an error message, irrespective of status code (i.e. https://docs.microsoft.com/en-us/microsoftteams/platform/webhooks-and-connectors/how-to/connectors-using?tabs=cURL#rate-limiting-for-connectors)
|
// response can contain an error message, irrespective of status code (i.e. https://docs.microsoft.com/en-us/microsoftteams/platform/webhooks-and-connectors/how-to/connectors-using?tabs=cURL#rate-limiting-for-connectors)
|
||||||
cmd.Validation = func(b []byte, statusCode int) error {
|
cmd.Validation = validateResponse
|
||||||
// The request succeeded if the response is "1"
|
|
||||||
// https://docs.microsoft.com/en-us/microsoftteams/platform/webhooks-and-connectors/how-to/connectors-using?tabs=cURL#send-messages-using-curl-and-powershell
|
|
||||||
if !bytes.Equal(b, []byte("1")) {
|
|
||||||
return errors.New(string(b))
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := tn.ns.SendWebhookSync(ctx, cmd); err != nil {
|
if err := tn.ns.SendWebhook(ctx, cmd); err != nil {
|
||||||
return false, errors.Wrap(err, "send notification to Teams")
|
return false, errors.Wrap(err, "send notification to Teams")
|
||||||
}
|
}
|
||||||
|
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func validateResponse(b []byte, statusCode int) error {
|
||||||
|
// The request succeeded if the response is "1"
|
||||||
|
// https://docs.microsoft.com/en-us/microsoftteams/platform/webhooks-and-connectors/how-to/connectors-using?tabs=cURL#send-messages-using-curl-and-powershell
|
||||||
|
if !bytes.Equal(b, []byte("1")) {
|
||||||
|
return errors.New(string(b))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (tn *TeamsNotifier) SendResolved() bool {
|
func (tn *TeamsNotifier) SendResolved() bool {
|
||||||
return !tn.GetDisableResolveMessage()
|
return !tn.GetDisableResolveMessage()
|
||||||
}
|
}
|
||||||
|
@ -3,11 +3,8 @@ package channels
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"math/rand"
|
||||||
"io"
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/prometheus/alertmanager/notify"
|
"github.com/prometheus/alertmanager/notify"
|
||||||
@ -16,7 +13,6 @@ import (
|
|||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||||
"github.com/grafana/grafana/pkg/services/notifications"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestTeamsNotifier(t *testing.T) {
|
func TestTeamsNotifier(t *testing.T) {
|
||||||
@ -30,7 +26,6 @@ func TestTeamsNotifier(t *testing.T) {
|
|||||||
name string
|
name string
|
||||||
settings string
|
settings string
|
||||||
alerts []*types.Alert
|
alerts []*types.Alert
|
||||||
response *mockResponse
|
|
||||||
expMsg map[string]interface{}
|
expMsg map[string]interface{}
|
||||||
expInitError string
|
expInitError string
|
||||||
expMsgError error
|
expMsgError error
|
||||||
@ -250,23 +245,6 @@ func TestTeamsNotifier(t *testing.T) {
|
|||||||
name: "Error in initing",
|
name: "Error in initing",
|
||||||
settings: `{}`,
|
settings: `{}`,
|
||||||
expInitError: `could not find url property in settings`,
|
expInitError: `could not find url property in settings`,
|
||||||
}, {
|
|
||||||
name: "webhook returns error message in body with 200",
|
|
||||||
settings: `{"url": "http://localhost"}`,
|
|
||||||
alerts: []*types.Alert{
|
|
||||||
{
|
|
||||||
Alert: model.Alert{
|
|
||||||
Labels: model.LabelSet{"alertname": "alert1", "lbl1": "val1"},
|
|
||||||
Annotations: model.LabelSet{"ann1": "annv1", "__dashboardUid__": "abcd", "__panelId__": "efgh"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
response: &mockResponse{
|
|
||||||
status: 200,
|
|
||||||
body: "some error message",
|
|
||||||
error: nil,
|
|
||||||
},
|
|
||||||
expMsgError: errors.New("send notification to Teams: webhook failed validation: some error message"),
|
|
||||||
}}
|
}}
|
||||||
|
|
||||||
for _, c := range cases {
|
for _, c := range cases {
|
||||||
@ -280,14 +258,7 @@ func TestTeamsNotifier(t *testing.T) {
|
|||||||
Settings: settingsJSON,
|
Settings: settingsJSON,
|
||||||
}
|
}
|
||||||
|
|
||||||
webhookSender := CreateNotificationService(t)
|
webhookSender := mockNotificationService()
|
||||||
|
|
||||||
originalClient := notifications.NetClient
|
|
||||||
defer func() {
|
|
||||||
notifications.SetWebhookClient(*originalClient)
|
|
||||||
}()
|
|
||||||
clientStub := newMockClient(c.response)
|
|
||||||
notifications.SetWebhookClient(clientStub)
|
|
||||||
|
|
||||||
fc := FactoryConfig{
|
fc := FactoryConfig{
|
||||||
Config: m,
|
Config: m,
|
||||||
@ -317,54 +288,24 @@ func TestTeamsNotifier(t *testing.T) {
|
|||||||
require.True(t, ok)
|
require.True(t, ok)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
require.NotEmpty(t, clientStub.lastRequest.URL.String())
|
require.NotNil(t, webhookSender.Webhook)
|
||||||
|
lastRequest := webhookSender.Webhook
|
||||||
|
|
||||||
|
require.NotEmpty(t, lastRequest.Url)
|
||||||
|
|
||||||
expBody, err := json.Marshal(c.expMsg)
|
expBody, err := json.Marshal(c.expMsg)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
body, err := io.ReadAll(clientStub.lastRequest.Body)
|
require.JSONEq(t, string(expBody), lastRequest.Body)
|
||||||
require.NoError(t, err)
|
|
||||||
require.JSONEq(t, string(expBody), string(body))
|
require.NotNil(t, lastRequest.Validation)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type mockClient struct {
|
func Test_ValidateResponse(t *testing.T) {
|
||||||
response mockResponse
|
require.NoError(t, validateResponse([]byte("1"), rand.Int()))
|
||||||
lastRequest *http.Request
|
err := validateResponse([]byte("some error message"), rand.Int())
|
||||||
}
|
require.Error(t, err)
|
||||||
|
require.Equal(t, "some error message", err.Error())
|
||||||
type mockResponse struct {
|
|
||||||
status int
|
|
||||||
body string
|
|
||||||
error error
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *mockClient) Do(req *http.Request) (*http.Response, error) {
|
|
||||||
// Do Nothing
|
|
||||||
c.lastRequest = req
|
|
||||||
return makeResponse(c.response.status, c.response.body), c.response.error
|
|
||||||
}
|
|
||||||
|
|
||||||
func newMockClient(resp *mockResponse) *mockClient {
|
|
||||||
client := &mockClient{}
|
|
||||||
|
|
||||||
if resp != nil {
|
|
||||||
client.response = *resp
|
|
||||||
} else {
|
|
||||||
client.response = mockResponse{
|
|
||||||
status: 200,
|
|
||||||
body: "1",
|
|
||||||
error: nil,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return client
|
|
||||||
}
|
|
||||||
|
|
||||||
func makeResponse(status int, body string) *http.Response {
|
|
||||||
return &http.Response{
|
|
||||||
StatusCode: status,
|
|
||||||
Body: io.NopCloser(strings.NewReader(body)),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,6 @@ import (
|
|||||||
"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"
|
ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models"
|
||||||
"github.com/grafana/grafana/pkg/services/notifications"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -38,7 +37,7 @@ type TelegramNotifier struct {
|
|||||||
*Base
|
*Base
|
||||||
log log.Logger
|
log log.Logger
|
||||||
images ImageStore
|
images ImageStore
|
||||||
ns notifications.WebhookSender
|
ns WebhookSender
|
||||||
tmpl *template.Template
|
tmpl *template.Template
|
||||||
settings telegramSettings
|
settings telegramSettings
|
||||||
}
|
}
|
||||||
@ -140,7 +139,7 @@ func (tn *TelegramNotifier) Notify(ctx context.Context, as ...*types.Alert) (boo
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return false, fmt.Errorf("failed to create telegram message: %w", err)
|
return false, fmt.Errorf("failed to create telegram message: %w", err)
|
||||||
}
|
}
|
||||||
if err := tn.ns.SendWebhookSync(ctx, cmd); err != nil {
|
if err := tn.ns.SendWebhook(ctx, cmd); err != nil {
|
||||||
return false, fmt.Errorf("failed to send telegram message: %w", err)
|
return false, fmt.Errorf("failed to send telegram message: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -168,7 +167,7 @@ func (tn *TelegramNotifier) Notify(ctx context.Context, as ...*types.Alert) (boo
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to create image: %w", err)
|
return fmt.Errorf("failed to create image: %w", err)
|
||||||
}
|
}
|
||||||
if err := tn.ns.SendWebhookSync(ctx, cmd); err != nil {
|
if err := tn.ns.SendWebhook(ctx, cmd); err != nil {
|
||||||
return fmt.Errorf("failed to upload image to telegram: %w", err)
|
return fmt.Errorf("failed to upload image to telegram: %w", err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@ -207,7 +206,7 @@ func (tn *TelegramNotifier) buildTelegramMessage(ctx context.Context, as []*type
|
|||||||
return m, nil
|
return m, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tn *TelegramNotifier) newWebhookSyncCmd(action string, fn func(writer *multipart.Writer) error) (*models.SendWebhookSync, error) {
|
func (tn *TelegramNotifier) newWebhookSyncCmd(action string, fn func(writer *multipart.Writer) error) (*SendWebhookSettings, error) {
|
||||||
b := bytes.Buffer{}
|
b := bytes.Buffer{}
|
||||||
w := multipart.NewWriter(&b)
|
w := multipart.NewWriter(&b)
|
||||||
|
|
||||||
@ -234,7 +233,7 @@ func (tn *TelegramNotifier) newWebhookSyncCmd(action string, fn func(writer *mul
|
|||||||
return nil, fmt.Errorf("failed to close multipart: %w", err)
|
return nil, fmt.Errorf("failed to close multipart: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd := &models.SendWebhookSync{
|
cmd := &SendWebhookSettings{
|
||||||
Url: fmt.Sprintf(TelegramAPIURL, tn.settings.BotToken, action),
|
Url: fmt.Sprintf(TelegramAPIURL, tn.settings.BotToken, action),
|
||||||
Body: b.String(),
|
Body: b.String(),
|
||||||
HttpMethod: "POST",
|
HttpMethod: "POST",
|
||||||
|
@ -129,28 +129,50 @@ func resetTimeNow() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type notificationServiceMock struct {
|
type notificationServiceMock struct {
|
||||||
Webhook models.SendWebhookSync
|
Webhook SendWebhookSettings
|
||||||
EmailSync models.SendEmailCommandSync
|
EmailSync SendEmailSettings
|
||||||
Emailx models.SendEmailCommand
|
|
||||||
ShouldError error
|
ShouldError error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ns *notificationServiceMock) SendWebhookSync(ctx context.Context, cmd *models.SendWebhookSync) error {
|
func (ns *notificationServiceMock) SendWebhook(ctx context.Context, cmd *SendWebhookSettings) error {
|
||||||
ns.Webhook = *cmd
|
ns.Webhook = *cmd
|
||||||
return ns.ShouldError
|
return ns.ShouldError
|
||||||
}
|
}
|
||||||
func (ns *notificationServiceMock) SendEmailCommandHandlerSync(ctx context.Context, cmd *models.SendEmailCommandSync) error {
|
func (ns *notificationServiceMock) SendEmail(ctx context.Context, cmd *SendEmailSettings) error {
|
||||||
ns.EmailSync = *cmd
|
ns.EmailSync = *cmd
|
||||||
return ns.ShouldError
|
return ns.ShouldError
|
||||||
}
|
}
|
||||||
func (ns *notificationServiceMock) SendEmailCommandHandler(ctx context.Context, cmd *models.SendEmailCommand) error {
|
|
||||||
ns.Emailx = *cmd
|
|
||||||
return ns.ShouldError
|
|
||||||
}
|
|
||||||
|
|
||||||
func mockNotificationService() *notificationServiceMock { return ¬ificationServiceMock{} }
|
func mockNotificationService() *notificationServiceMock { return ¬ificationServiceMock{} }
|
||||||
|
|
||||||
func CreateNotificationService(t *testing.T) *notifications.NotificationService {
|
type emailSender struct {
|
||||||
|
ns *notifications.NotificationService
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e emailSender) SendEmail(ctx context.Context, cmd *SendEmailSettings) error {
|
||||||
|
attached := make([]*models.SendEmailAttachFile, 0, len(cmd.AttachedFiles))
|
||||||
|
for _, file := range cmd.AttachedFiles {
|
||||||
|
attached = append(attached, &models.SendEmailAttachFile{
|
||||||
|
Name: file.Name,
|
||||||
|
Content: file.Content,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return e.ns.SendEmailCommandHandlerSync(ctx, &models.SendEmailCommandSync{
|
||||||
|
SendEmailCommand: models.SendEmailCommand{
|
||||||
|
To: cmd.To,
|
||||||
|
SingleEmail: cmd.SingleEmail,
|
||||||
|
Template: cmd.Template,
|
||||||
|
Subject: cmd.Subject,
|
||||||
|
Data: cmd.Data,
|
||||||
|
Info: cmd.Info,
|
||||||
|
ReplyTo: cmd.ReplyTo,
|
||||||
|
EmbeddedFiles: cmd.EmbeddedFiles,
|
||||||
|
AttachedFiles: attached,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func createEmailSender(t *testing.T) *emailSender {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
tracer := tracing.InitializeTracerForTest()
|
tracer := tracing.InitializeTracerForTest()
|
||||||
@ -170,5 +192,5 @@ func CreateNotificationService(t *testing.T) *notifications.NotificationService
|
|||||||
ns, err := notifications.ProvideService(bus, cfg, mailer, nil)
|
ns, err := notifications.ProvideService(bus, cfg, mailer, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
return ns
|
return &emailSender{ns: ns}
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,6 @@ import (
|
|||||||
"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"
|
ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models"
|
||||||
"github.com/grafana/grafana/pkg/services/notifications"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -28,7 +27,7 @@ type ThreemaNotifier struct {
|
|||||||
*Base
|
*Base
|
||||||
log log.Logger
|
log log.Logger
|
||||||
images ImageStore
|
images ImageStore
|
||||||
ns notifications.WebhookSender
|
ns WebhookSender
|
||||||
tmpl *template.Template
|
tmpl *template.Template
|
||||||
settings threemaSettings
|
settings threemaSettings
|
||||||
}
|
}
|
||||||
@ -123,7 +122,7 @@ func (tn *ThreemaNotifier) Notify(ctx context.Context, as ...*types.Alert) (bool
|
|||||||
data.Set("secret", tn.settings.APISecret)
|
data.Set("secret", tn.settings.APISecret)
|
||||||
data.Set("text", tn.buildMessage(ctx, as...))
|
data.Set("text", tn.buildMessage(ctx, as...))
|
||||||
|
|
||||||
cmd := &models.SendWebhookSync{
|
cmd := &SendWebhookSettings{
|
||||||
Url: ThreemaGwBaseURL,
|
Url: ThreemaGwBaseURL,
|
||||||
Body: data.Encode(),
|
Body: data.Encode(),
|
||||||
HttpMethod: "POST",
|
HttpMethod: "POST",
|
||||||
@ -131,7 +130,7 @@ func (tn *ThreemaNotifier) Notify(ctx context.Context, as ...*types.Alert) (bool
|
|||||||
"Content-Type": "application/x-www-form-urlencoded",
|
"Content-Type": "application/x-www-form-urlencoded",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
if err := tn.ns.SendWebhookSync(ctx, cmd); err != nil {
|
if err := tn.ns.SendWebhook(ctx, cmd); err != nil {
|
||||||
tn.log.Error("Failed to send threema notification", "error", err, "webhook", tn.Name)
|
tn.log.Error("Failed to send threema notification", "error", err, "webhook", tn.Name)
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,6 @@ import (
|
|||||||
"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"
|
ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models"
|
||||||
"github.com/grafana/grafana/pkg/services/notifications"
|
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -100,7 +99,7 @@ type VictoropsNotifier struct {
|
|||||||
*Base
|
*Base
|
||||||
log log.Logger
|
log log.Logger
|
||||||
images ImageStore
|
images ImageStore
|
||||||
ns notifications.WebhookSender
|
ns WebhookSender
|
||||||
tmpl *template.Template
|
tmpl *template.Template
|
||||||
settings victorOpsSettings
|
settings victorOpsSettings
|
||||||
}
|
}
|
||||||
@ -161,12 +160,12 @@ func (vn *VictoropsNotifier) Notify(ctx context.Context, as ...*types.Alert) (bo
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
cmd := &models.SendWebhookSync{
|
cmd := &SendWebhookSettings{
|
||||||
Url: u,
|
Url: u,
|
||||||
Body: string(b),
|
Body: string(b),
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := vn.ns.SendWebhookSync(ctx, cmd); err != nil {
|
if err := vn.ns.SendWebhook(ctx, cmd); err != nil {
|
||||||
vn.log.Error("failed to send notification", "error", err, "webhook", vn.Name)
|
vn.log.Error("failed to send notification", "error", err, "webhook", vn.Name)
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,6 @@ import (
|
|||||||
"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"
|
ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models"
|
||||||
"github.com/grafana/grafana/pkg/services/notifications"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const webexAPIURL = "https://webexapis.com/v1/messages"
|
const webexAPIURL = "https://webexapis.com/v1/messages"
|
||||||
@ -21,7 +20,7 @@ const webexAPIURL = "https://webexapis.com/v1/messages"
|
|||||||
// WebexNotifier is responsible for sending alert notifications as webex messages.
|
// WebexNotifier is responsible for sending alert notifications as webex messages.
|
||||||
type WebexNotifier struct {
|
type WebexNotifier struct {
|
||||||
*Base
|
*Base
|
||||||
ns notifications.WebhookSender
|
ns WebhookSender
|
||||||
log log.Logger
|
log log.Logger
|
||||||
images ImageStore
|
images ImageStore
|
||||||
tmpl *template.Template
|
tmpl *template.Template
|
||||||
@ -152,7 +151,7 @@ func (wn *WebexNotifier) Notify(ctx context.Context, as ...*types.Alert) (bool,
|
|||||||
return false, tmplErr
|
return false, tmplErr
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd := &models.SendWebhookSync{
|
cmd := &SendWebhookSettings{
|
||||||
Url: parsedURL,
|
Url: parsedURL,
|
||||||
Body: string(body),
|
Body: string(body),
|
||||||
HttpMethod: http.MethodPost,
|
HttpMethod: http.MethodPost,
|
||||||
@ -164,7 +163,7 @@ func (wn *WebexNotifier) Notify(ctx context.Context, as ...*types.Alert) (bool,
|
|||||||
cmd.HttpHeader = headers
|
cmd.HttpHeader = headers
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := wn.ns.SendWebhookSync(ctx, cmd); err != nil {
|
if err := wn.ns.SendWebhook(ctx, cmd); err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,7 +16,6 @@ import (
|
|||||||
"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"
|
ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models"
|
||||||
"github.com/grafana/grafana/pkg/services/notifications"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// WebhookNotifier is responsible for sending
|
// WebhookNotifier is responsible for sending
|
||||||
@ -24,7 +23,7 @@ import (
|
|||||||
type WebhookNotifier struct {
|
type WebhookNotifier struct {
|
||||||
*Base
|
*Base
|
||||||
log log.Logger
|
log log.Logger
|
||||||
ns notifications.WebhookSender
|
ns WebhookSender
|
||||||
images ImageStore
|
images ImageStore
|
||||||
tmpl *template.Template
|
tmpl *template.Template
|
||||||
orgID int64
|
orgID int64
|
||||||
@ -204,7 +203,7 @@ func (wn *WebhookNotifier) Notify(ctx context.Context, as ...*types.Alert) (bool
|
|||||||
return false, tmplErr
|
return false, tmplErr
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd := &models.SendWebhookSync{
|
cmd := &SendWebhookSettings{
|
||||||
Url: parsedURL,
|
Url: parsedURL,
|
||||||
User: wn.settings.User,
|
User: wn.settings.User,
|
||||||
Password: wn.settings.Password,
|
Password: wn.settings.Password,
|
||||||
@ -213,7 +212,7 @@ func (wn *WebhookNotifier) Notify(ctx context.Context, as ...*types.Alert) (bool
|
|||||||
HttpHeader: headers,
|
HttpHeader: headers,
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := wn.ns.SendWebhookSync(ctx, cmd); err != nil {
|
if err := wn.ns.SendWebhook(ctx, cmd); err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,7 +14,6 @@ import (
|
|||||||
|
|
||||||
"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"
|
||||||
"github.com/grafana/grafana/pkg/services/notifications"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var weComEndpoint = "https://qyapi.weixin.qq.com"
|
var weComEndpoint = "https://qyapi.weixin.qq.com"
|
||||||
@ -130,7 +129,7 @@ type WeComNotifier struct {
|
|||||||
*Base
|
*Base
|
||||||
tmpl *template.Template
|
tmpl *template.Template
|
||||||
log log.Logger
|
log log.Logger
|
||||||
ns notifications.WebhookSender
|
ns WebhookSender
|
||||||
settings wecomSettings
|
settings wecomSettings
|
||||||
tok *WeComAccessToken
|
tok *WeComAccessToken
|
||||||
tokExpireAt time.Time
|
tokExpireAt time.Time
|
||||||
@ -183,12 +182,12 @@ func (w *WeComNotifier) Notify(ctx context.Context, as ...*types.Alert) (bool, e
|
|||||||
w.log.Warn("failed to template WeCom message", "error", tmplErr.Error())
|
w.log.Warn("failed to template WeCom message", "error", tmplErr.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd := &models.SendWebhookSync{
|
cmd := &SendWebhookSettings{
|
||||||
Url: url,
|
Url: url,
|
||||||
Body: string(body),
|
Body: string(body),
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = w.ns.SendWebhookSync(ctx, cmd); err != nil {
|
if err = w.ns.SendWebhook(ctx, cmd); err != nil {
|
||||||
w.log.Error("failed to send WeCom webhook", "error", err, "notification", w.Name)
|
w.log.Error("failed to send WeCom webhook", "error", err, "notification", w.Name)
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
56
pkg/services/ngalert/notifier/sender.go
Normal file
56
pkg/services/ngalert/notifier/sender.go
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
package notifier
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/grafana/grafana/pkg/models"
|
||||||
|
"github.com/grafana/grafana/pkg/services/ngalert/notifier/channels"
|
||||||
|
"github.com/grafana/grafana/pkg/services/notifications"
|
||||||
|
)
|
||||||
|
|
||||||
|
type sender struct {
|
||||||
|
ns notifications.Service
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s sender) SendWebhook(ctx context.Context, cmd *channels.SendWebhookSettings) error {
|
||||||
|
return s.ns.SendWebhookSync(ctx, &models.SendWebhookSync{
|
||||||
|
Url: cmd.Url,
|
||||||
|
User: cmd.User,
|
||||||
|
Password: cmd.Password,
|
||||||
|
Body: cmd.Body,
|
||||||
|
HttpMethod: cmd.HttpMethod,
|
||||||
|
HttpHeader: cmd.HttpHeader,
|
||||||
|
ContentType: cmd.ContentType,
|
||||||
|
Validation: cmd.Validation,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s sender) SendEmail(ctx context.Context, cmd *channels.SendEmailSettings) error {
|
||||||
|
var attached []*models.SendEmailAttachFile
|
||||||
|
if cmd.AttachedFiles != nil {
|
||||||
|
attached = make([]*models.SendEmailAttachFile, 0, len(cmd.AttachedFiles))
|
||||||
|
for _, file := range cmd.AttachedFiles {
|
||||||
|
attached = append(attached, &models.SendEmailAttachFile{
|
||||||
|
Name: file.Name,
|
||||||
|
Content: file.Content,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return s.ns.SendEmailCommandHandlerSync(ctx, &models.SendEmailCommandSync{
|
||||||
|
SendEmailCommand: models.SendEmailCommand{
|
||||||
|
To: cmd.To,
|
||||||
|
SingleEmail: cmd.SingleEmail,
|
||||||
|
Template: cmd.Template,
|
||||||
|
Subject: cmd.Subject,
|
||||||
|
Data: cmd.Data,
|
||||||
|
Info: cmd.Info,
|
||||||
|
ReplyTo: cmd.ReplyTo,
|
||||||
|
EmbeddedFiles: cmd.EmbeddedFiles,
|
||||||
|
AttachedFiles: attached,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewNotificationSender(ns notifications.Service) channels.NotificationSender {
|
||||||
|
return &sender{ns: ns}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user