Chore: Remove bus from ngalert (#44465)

* pass notification service down to the notifiers

* add ns to all notifiers

* remove bus from ngalert notifiers

* use smaller interfaces for notificationservice

* attempt to fix the tests

* remove unused struct field

* simplify notification service mock

* trying to resolve issues in the tests

* make linter happy

* make linter even happier

* linter, you are annoying
pull/44485/head
Serge Zaitsev 4 years ago committed by GitHub
parent ad4a9a48d2
commit 84a5910e56
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 14
      pkg/server/test_env.go
  2. 8
      pkg/server/wire.go
  3. 2
      pkg/services/ngalert/api/api_alertmanager_test.go
  4. 55
      pkg/services/ngalert/ngalert.go
  5. 69
      pkg/services/ngalert/notifier/alertmanager.go
  6. 2
      pkg/services/ngalert/notifier/alertmanager_test.go
  7. 8
      pkg/services/ngalert/notifier/channels/dingding.go
  8. 13
      pkg/services/ngalert/notifier/channels/dingding_test.go
  9. 8
      pkg/services/ngalert/notifier/channels/discord.go
  10. 13
      pkg/services/ngalert/notifier/channels/discord_test.go
  11. 8
      pkg/services/ngalert/notifier/channels/email.go
  12. 26
      pkg/services/ngalert/notifier/channels/email_test.go
  13. 8
      pkg/services/ngalert/notifier/channels/googlechat.go
  14. 13
      pkg/services/ngalert/notifier/channels/googlechat_test.go
  15. 8
      pkg/services/ngalert/notifier/channels/kafka.go
  16. 17
      pkg/services/ngalert/notifier/channels/kafka_test.go
  17. 8
      pkg/services/ngalert/notifier/channels/line.go
  18. 17
      pkg/services/ngalert/notifier/channels/line_test.go
  19. 8
      pkg/services/ngalert/notifier/channels/opsgenie.go
  20. 16
      pkg/services/ngalert/notifier/channels/opsgenie_test.go
  21. 8
      pkg/services/ngalert/notifier/channels/pagerduty.go
  22. 13
      pkg/services/ngalert/notifier/channels/pagerduty_test.go
  23. 8
      pkg/services/ngalert/notifier/channels/pushover.go
  24. 13
      pkg/services/ngalert/notifier/channels/pushover_test.go
  25. 8
      pkg/services/ngalert/notifier/channels/sensugo.go
  26. 13
      pkg/services/ngalert/notifier/channels/sensugo_test.go
  27. 8
      pkg/services/ngalert/notifier/channels/teams.go
  28. 13
      pkg/services/ngalert/notifier/channels/teams_test.go
  29. 8
      pkg/services/ngalert/notifier/channels/telegram.go
  30. 3
      pkg/services/ngalert/notifier/channels/telegram_test.go
  31. 24
      pkg/services/ngalert/notifier/channels/testing.go
  32. 8
      pkg/services/ngalert/notifier/channels/threema.go
  33. 13
      pkg/services/ngalert/notifier/channels/threema_test.go
  34. 8
      pkg/services/ngalert/notifier/channels/victorops.go
  35. 15
      pkg/services/ngalert/notifier/channels/victorops_test.go
  36. 8
      pkg/services/ngalert/notifier/channels/webhook.go
  37. 21
      pkg/services/ngalert/notifier/channels/webhook_test.go
  38. 8
      pkg/services/ngalert/notifier/channels/wecom.go
  39. 13
      pkg/services/ngalert/notifier/channels/wecom_test.go
  40. 8
      pkg/services/ngalert/notifier/multiorg_alertmanager.go
  41. 6
      pkg/services/ngalert/notifier/multiorg_alertmanager_test.go
  42. 2
      pkg/services/ngalert/schedule/schedule_unit_test.go
  43. 2
      pkg/services/ngalert/tests/util.go
  44. 33
      pkg/services/notifications/mock.go
  45. 15
      pkg/services/notifications/notifications.go
  46. 30
      pkg/services/sqlstore/migrations/ualert/ualert.go
  47. 111
      pkg/tests/api/alerting/api_notification_channel_test.go
  48. 7
      pkg/tests/testinfra/testinfra.go

@ -1,12 +1,16 @@
package server
import "github.com/grafana/grafana/pkg/services/sqlstore"
import (
"github.com/grafana/grafana/pkg/services/notifications"
"github.com/grafana/grafana/pkg/services/sqlstore"
)
func ProvideTestEnv(server *Server, store *sqlstore.SQLStore) (*TestEnv, error) {
return &TestEnv{server, store}, nil
func ProvideTestEnv(server *Server, store *sqlstore.SQLStore, ns *notifications.NotificationServiceMock) (*TestEnv, error) {
return &TestEnv{server, store, ns}, nil
}
type TestEnv struct {
Server *Server
SQLStore *sqlstore.SQLStore
Server *Server
SQLStore *sqlstore.SQLStore
NotificationService *notifications.NotificationServiceMock
}

@ -190,6 +190,9 @@ var wireSet = wire.NewSet(
wireBasicSet,
sqlstore.ProvideService,
ngmetrics.ProvideService,
wire.Bind(new(notifications.Service), new(*notifications.NotificationService)),
wire.Bind(new(notifications.WebhookSender), new(*notifications.NotificationService)),
wire.Bind(new(notifications.EmailSender), new(*notifications.NotificationService)),
)
var wireTestSet = wire.NewSet(
@ -197,6 +200,11 @@ var wireTestSet = wire.NewSet(
ProvideTestEnv,
sqlstore.ProvideServiceForTests,
ngmetrics.ProvideServiceForTest,
notifications.MockNotificationService,
wire.Bind(new(notifications.Service), new(*notifications.NotificationServiceMock)),
wire.Bind(new(notifications.WebhookSender), new(*notifications.NotificationServiceMock)),
wire.Bind(new(notifications.EmailSender), new(*notifications.NotificationServiceMock)),
)
func Initialize(cla setting.CommandLineArgs, opts Options, apiOpts api.ServerOptions) (*Server, error) {

@ -261,7 +261,7 @@ func createMultiOrgAlertmanager(t *testing.T) *notifier.MultiOrgAlertmanager {
}, // do not poll in tests.
}
mam, err := notifier.NewMultiOrgAlertmanager(cfg, &configStore, &orgStore, kvStore, decryptFn, m.GetMultiOrgAlertmanagerMetrics(), log.New("testlogger"))
mam, err := notifier.NewMultiOrgAlertmanager(cfg, &configStore, &orgStore, kvStore, decryptFn, m.GetMultiOrgAlertmanagerMetrics(), nil, log.New("testlogger"))
require.NoError(t, err)
t.Cleanup(cleanOrgDirectories(tmpDir, t))
err = mam.LoadAndSyncAlertmanagersForOrgs(context.Background())

@ -21,6 +21,7 @@ import (
"github.com/grafana/grafana/pkg/services/ngalert/schedule"
"github.com/grafana/grafana/pkg/services/ngalert/state"
"github.com/grafana/grafana/pkg/services/ngalert/store"
"github.com/grafana/grafana/pkg/services/notifications"
"github.com/grafana/grafana/pkg/services/quota"
"github.com/grafana/grafana/pkg/services/secrets"
"github.com/grafana/grafana/pkg/services/sqlstore"
@ -40,19 +41,20 @@ const (
func ProvideService(cfg *setting.Cfg, dataSourceCache datasources.CacheService, routeRegister routing.RouteRegister,
sqlStore *sqlstore.SQLStore, kvStore kvstore.KVStore, expressionService *expr.Service, dataProxy *datasourceproxy.DataSourceProxyService,
quotaService *quota.QuotaService, secretsService secrets.Service, m *metrics.NGAlert) (*AlertNG, error) {
quotaService *quota.QuotaService, secretsService secrets.Service, notificationService notifications.Service, m *metrics.NGAlert) (*AlertNG, error) {
ng := &AlertNG{
Cfg: cfg,
DataSourceCache: dataSourceCache,
RouteRegister: routeRegister,
SQLStore: sqlStore,
KVStore: kvStore,
ExpressionService: expressionService,
DataProxy: dataProxy,
QuotaService: quotaService,
SecretsService: secretsService,
Metrics: m,
Log: log.New("ngalert"),
Cfg: cfg,
DataSourceCache: dataSourceCache,
RouteRegister: routeRegister,
SQLStore: sqlStore,
KVStore: kvStore,
ExpressionService: expressionService,
DataProxy: dataProxy,
QuotaService: quotaService,
SecretsService: secretsService,
Metrics: m,
NotificationService: notificationService,
Log: log.New("ngalert"),
}
if ng.IsDisabled() {
@ -68,19 +70,20 @@ func ProvideService(cfg *setting.Cfg, dataSourceCache datasources.CacheService,
// AlertNG is the service for evaluating the condition of an alert definition.
type AlertNG struct {
Cfg *setting.Cfg
DataSourceCache datasources.CacheService
RouteRegister routing.RouteRegister
SQLStore *sqlstore.SQLStore
KVStore kvstore.KVStore
ExpressionService *expr.Service
DataProxy *datasourceproxy.DataSourceProxyService
QuotaService *quota.QuotaService
SecretsService secrets.Service
Metrics *metrics.NGAlert
Log log.Logger
schedule schedule.ScheduleService
stateManager *state.Manager
Cfg *setting.Cfg
DataSourceCache datasources.CacheService
RouteRegister routing.RouteRegister
SQLStore *sqlstore.SQLStore
KVStore kvstore.KVStore
ExpressionService *expr.Service
DataProxy *datasourceproxy.DataSourceProxyService
QuotaService *quota.QuotaService
SecretsService secrets.Service
Metrics *metrics.NGAlert
NotificationService notifications.Service
Log log.Logger
schedule schedule.ScheduleService
stateManager *state.Manager
// Alerting notification services
MultiOrgAlertmanager *notifier.MultiOrgAlertmanager
@ -104,7 +107,7 @@ func (ng *AlertNG) init() error {
decryptFn := ng.SecretsService.GetDecryptedValue
multiOrgMetrics := ng.Metrics.GetMultiOrgAlertmanagerMetrics()
ng.MultiOrgAlertmanager, err = notifier.NewMultiOrgAlertmanager(ng.Cfg, store, store, ng.KVStore, decryptFn, multiOrgMetrics, log.New("ngalert.multiorg.alertmanager"))
ng.MultiOrgAlertmanager, err = notifier.NewMultiOrgAlertmanager(ng.Cfg, store, store, ng.KVStore, decryptFn, multiOrgMetrics, ng.NotificationService, log.New("ngalert.multiorg.alertmanager"))
if err != nil {
return err
}

@ -40,6 +40,7 @@ import (
ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models"
"github.com/grafana/grafana/pkg/services/ngalert/notifier/channels"
"github.com/grafana/grafana/pkg/services/ngalert/store"
"github.com/grafana/grafana/pkg/services/notifications"
"github.com/grafana/grafana/pkg/setting"
)
@ -86,10 +87,11 @@ type ClusterPeer interface {
type Alertmanager struct {
logger log.Logger
Settings *setting.Cfg
Store store.AlertingStore
fileStore *FileStore
Metrics *metrics.Alertmanager
Settings *setting.Cfg
Store store.AlertingStore
fileStore *FileStore
Metrics *metrics.Alertmanager
NotificationService notifications.Service
notificationLog *nflog.Log
marker types.Marker
@ -125,20 +127,21 @@ type Alertmanager struct {
}
func newAlertmanager(ctx context.Context, orgID int64, cfg *setting.Cfg, store store.AlertingStore, kvStore kvstore.KVStore,
peer ClusterPeer, decryptFn channels.GetDecryptedValueFn, m *metrics.Alertmanager) (*Alertmanager, error) {
peer ClusterPeer, decryptFn channels.GetDecryptedValueFn, ns notifications.Service, m *metrics.Alertmanager) (*Alertmanager, error) {
am := &Alertmanager{
Settings: cfg,
stopc: make(chan struct{}),
logger: log.New("alertmanager", "org", orgID),
marker: types.NewMarker(m.Registerer),
stageMetrics: notify.NewMetrics(m.Registerer),
dispatcherMetrics: dispatch.NewDispatcherMetrics(false, m.Registerer),
Store: store,
peer: peer,
peerTimeout: cfg.UnifiedAlerting.HAPeerTimeout,
Metrics: m,
orgID: orgID,
decryptFn: decryptFn,
Settings: cfg,
stopc: make(chan struct{}),
logger: log.New("alertmanager", "org", orgID),
marker: types.NewMarker(m.Registerer),
stageMetrics: notify.NewMetrics(m.Registerer),
dispatcherMetrics: dispatch.NewDispatcherMetrics(false, m.Registerer),
Store: store,
peer: peer,
peerTimeout: cfg.UnifiedAlerting.HAPeerTimeout,
Metrics: m,
NotificationService: ns,
orgID: orgID,
decryptFn: decryptFn,
}
am.fileStore = NewFileStore(am.orgID, kvStore, am.WorkingDirPath())
@ -496,39 +499,39 @@ func (am *Alertmanager) buildReceiverIntegration(r *apimodels.PostableGrafanaRec
)
switch r.Type {
case "email":
n, err = channels.NewEmailNotifier(cfg, tmpl) // Email notifier already has a default template.
n, err = channels.NewEmailNotifier(cfg, am.NotificationService, tmpl) // Email notifier already has a default template.
case "pagerduty":
n, err = channels.NewPagerdutyNotifier(cfg, tmpl, am.decryptFn)
n, err = channels.NewPagerdutyNotifier(cfg, am.NotificationService, tmpl, am.decryptFn)
case "pushover":
n, err = channels.NewPushoverNotifier(cfg, tmpl, am.decryptFn)
n, err = channels.NewPushoverNotifier(cfg, am.NotificationService, tmpl, am.decryptFn)
case "slack":
n, err = channels.NewSlackNotifier(cfg, tmpl, am.decryptFn)
case "telegram":
n, err = channels.NewTelegramNotifier(cfg, tmpl, am.decryptFn)
n, err = channels.NewTelegramNotifier(cfg, am.NotificationService, tmpl, am.decryptFn)
case "victorops":
n, err = channels.NewVictoropsNotifier(cfg, tmpl)
n, err = channels.NewVictoropsNotifier(cfg, am.NotificationService, tmpl)
case "teams":
n, err = channels.NewTeamsNotifier(cfg, tmpl)
n, err = channels.NewTeamsNotifier(cfg, am.NotificationService, tmpl)
case "dingding":
n, err = channels.NewDingDingNotifier(cfg, tmpl)
n, err = channels.NewDingDingNotifier(cfg, am.NotificationService, tmpl)
case "kafka":
n, err = channels.NewKafkaNotifier(cfg, tmpl)
n, err = channels.NewKafkaNotifier(cfg, am.NotificationService, tmpl)
case "webhook":
n, err = channels.NewWebHookNotifier(cfg, tmpl, am.decryptFn)
n, err = channels.NewWebHookNotifier(cfg, am.NotificationService, tmpl, am.decryptFn)
case "wecom":
n, err = channels.NewWeComNotifier(cfg, tmpl, am.decryptFn)
n, err = channels.NewWeComNotifier(cfg, am.NotificationService, tmpl, am.decryptFn)
case "sensugo":
n, err = channels.NewSensuGoNotifier(cfg, tmpl, am.decryptFn)
n, err = channels.NewSensuGoNotifier(cfg, am.NotificationService, tmpl, am.decryptFn)
case "discord":
n, err = channels.NewDiscordNotifier(cfg, tmpl)
n, err = channels.NewDiscordNotifier(cfg, am.NotificationService, tmpl)
case "googlechat":
n, err = channels.NewGoogleChatNotifier(cfg, tmpl)
n, err = channels.NewGoogleChatNotifier(cfg, am.NotificationService, tmpl)
case "LINE":
n, err = channels.NewLineNotifier(cfg, tmpl, am.decryptFn)
n, err = channels.NewLineNotifier(cfg, am.NotificationService, tmpl, am.decryptFn)
case "threema":
n, err = channels.NewThreemaNotifier(cfg, tmpl, am.decryptFn)
n, err = channels.NewThreemaNotifier(cfg, am.NotificationService, tmpl, am.decryptFn)
case "opsgenie":
n, err = channels.NewOpsgenieNotifier(cfg, tmpl, am.decryptFn)
n, err = channels.NewOpsgenieNotifier(cfg, am.NotificationService, tmpl, am.decryptFn)
case "prometheus-alertmanager":
n, err = channels.NewAlertmanagerNotifier(cfg, tmpl, am.decryptFn)
default:

@ -50,7 +50,7 @@ func setupAMTest(t *testing.T) *Alertmanager {
kvStore := NewFakeKVStore(t)
secretsService := secretsManager.SetupTestService(t, database.ProvideSecretsStore(sqlStore))
decryptFn := secretsService.GetDecryptedValue
am, err := newAlertmanager(context.Background(), 1, cfg, s, kvStore, &NilPeer{}, decryptFn, m)
am, err := newAlertmanager(context.Background(), 1, cfg, s, kvStore, &NilPeer{}, decryptFn, nil, m)
require.NoError(t, err)
return am
}

@ -9,15 +9,15 @@ import (
"github.com/prometheus/alertmanager/template"
"github.com/prometheus/alertmanager/types"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/notifications"
)
const defaultDingdingMsgType = "link"
// NewDingDingNotifier is the constructor for the Dingding notifier
func NewDingDingNotifier(model *NotificationChannelConfig, t *template.Template) (*DingDingNotifier, error) {
func NewDingDingNotifier(model *NotificationChannelConfig, ns notifications.WebhookSender, t *template.Template) (*DingDingNotifier, error) {
if model.Settings == nil {
return nil, receiverInitError{Cfg: *model, Reason: "no settings supplied"}
}
@ -42,6 +42,7 @@ func NewDingDingNotifier(model *NotificationChannelConfig, t *template.Template)
Message: model.Settings.Get("message").MustString(`{{ template "default.message" .}}`),
log: log.New("alerting.notifier.dingding"),
tmpl: t,
ns: ns,
}, nil
}
@ -52,6 +53,7 @@ type DingDingNotifier struct {
URL string
Message string
tmpl *template.Template
ns notifications.WebhookSender
log log.Logger
}
@ -115,7 +117,7 @@ func (dd *DingDingNotifier) Notify(ctx context.Context, as ...*types.Alert) (boo
Body: string(body),
}
if err := bus.Dispatch(ctx, cmd); err != nil {
if err := dd.ns.SendWebhookSync(ctx, cmd); err != nil {
return false, fmt.Errorf("send notification to dingding: %w", err)
}

@ -11,9 +11,7 @@ import (
"github.com/prometheus/common/model"
"github.com/stretchr/testify/require"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/models"
)
func TestDingdingNotifier(t *testing.T) {
@ -99,19 +97,14 @@ func TestDingdingNotifier(t *testing.T) {
Settings: settingsJSON,
}
pn, err := NewDingDingNotifier(m, tmpl)
webhookSender := mockNotificationService()
pn, err := NewDingDingNotifier(m, webhookSender, tmpl)
if c.expInitError != "" {
require.Equal(t, c.expInitError, err.Error())
return
}
require.NoError(t, err)
body := ""
bus.AddHandler("test", func(ctx context.Context, webhook *models.SendWebhookSync) error {
body = webhook.Body
return nil
})
ctx := notify.WithGroupKey(context.Background(), "alertname")
ctx = notify.WithGroupLabels(ctx, model.LabelSet{"alertname": ""})
ok, err := pn.Notify(ctx, c.alerts...)
@ -127,7 +120,7 @@ func TestDingdingNotifier(t *testing.T) {
expBody, err := json.Marshal(c.expMsg)
require.NoError(t, err)
require.JSONEq(t, string(expBody), body)
require.JSONEq(t, string(expBody), webhookSender.Webhook.Body)
})
}
}

@ -9,16 +9,17 @@ import (
"github.com/prometheus/alertmanager/template"
"github.com/prometheus/alertmanager/types"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/notifications"
"github.com/grafana/grafana/pkg/setting"
)
type DiscordNotifier struct {
*Base
log log.Logger
ns notifications.WebhookSender
tmpl *template.Template
Content string
AvatarURL string
@ -26,7 +27,7 @@ type DiscordNotifier struct {
UseDiscordUsername bool
}
func NewDiscordNotifier(model *NotificationChannelConfig, t *template.Template) (*DiscordNotifier, error) {
func NewDiscordNotifier(model *NotificationChannelConfig, ns notifications.WebhookSender, t *template.Template) (*DiscordNotifier, error) {
if model.Settings == nil {
return nil, receiverInitError{Cfg: *model, Reason: "no settings supplied"}
}
@ -55,6 +56,7 @@ func NewDiscordNotifier(model *NotificationChannelConfig, t *template.Template)
AvatarURL: avatarURL,
WebhookURL: discordURL,
log: log.New("alerting.notifier.discord"),
ns: ns,
tmpl: t,
UseDiscordUsername: useDiscordUsername,
}, nil
@ -114,7 +116,7 @@ func (d DiscordNotifier) Notify(ctx context.Context, as ...*types.Alert) (bool,
Body: string(body),
}
if err := bus.Dispatch(ctx, cmd); err != nil {
if err := d.ns.SendWebhookSync(ctx, cmd); err != nil {
d.log.Error("Failed to send notification to Discord", "error", err)
return false, err
}

@ -11,9 +11,7 @@ import (
"github.com/prometheus/common/model"
"github.com/stretchr/testify/require"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/setting"
)
@ -143,19 +141,14 @@ func TestDiscordNotifier(t *testing.T) {
Settings: settingsJson,
}
dn, err := NewDiscordNotifier(m, tmpl)
webhookSender := mockNotificationService()
dn, err := NewDiscordNotifier(m, webhookSender, tmpl)
if c.expInitError != "" {
require.Equal(t, c.expInitError, err.Error())
return
}
require.NoError(t, err)
body := ""
bus.AddHandler("test", func(ctx context.Context, webhook *models.SendWebhookSync) error {
body = webhook.Body
return nil
})
ctx := notify.WithGroupKey(context.Background(), "alertname")
ctx = notify.WithGroupLabels(ctx, model.LabelSet{"alertname": ""})
ok, err := dn.Notify(ctx, c.alerts...)
@ -171,7 +164,7 @@ func TestDiscordNotifier(t *testing.T) {
expBody, err := json.Marshal(c.expMsg)
require.NoError(t, err)
require.JSONEq(t, string(expBody), body)
require.JSONEq(t, string(expBody), webhookSender.Webhook.Body)
})
}
}

@ -8,9 +8,9 @@ import (
"github.com/prometheus/alertmanager/template"
"github.com/prometheus/alertmanager/types"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/notifications"
"github.com/grafana/grafana/pkg/util"
)
@ -22,12 +22,13 @@ type EmailNotifier struct {
SingleEmail bool
Message string
log log.Logger
ns notifications.EmailSender
tmpl *template.Template
}
// NewEmailNotifier is the constructor function
// for the EmailNotifier.
func NewEmailNotifier(model *NotificationChannelConfig, t *template.Template) (*EmailNotifier, error) {
func NewEmailNotifier(model *NotificationChannelConfig, ns notifications.EmailSender, t *template.Template) (*EmailNotifier, error) {
if model.Settings == nil {
return nil, receiverInitError{Cfg: *model, Reason: "no settings supplied"}
}
@ -54,6 +55,7 @@ func NewEmailNotifier(model *NotificationChannelConfig, t *template.Template) (*
SingleEmail: singleEmail,
Message: model.Settings.Get("message").MustString(),
log: log.New("alerting.notifier.email"),
ns: ns,
tmpl: t,
}, nil
}
@ -103,7 +105,7 @@ func (en *EmailNotifier) Notify(ctx context.Context, as ...*types.Alert) (bool,
en.log.Warn("failed to template email message", "err", tmplErr.Error())
}
if err := bus.Dispatch(ctx, cmd); err != nil {
if err := en.ns.SendEmailCommandHandlerSync(ctx, cmd); err != nil {
return false, err
}

@ -10,9 +10,7 @@ import (
"github.com/prometheus/common/model"
"github.com/stretchr/testify/require"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/models"
)
func TestEmailNotifier(t *testing.T) {
@ -32,7 +30,7 @@ func TestEmailNotifier(t *testing.T) {
Settings: settingsJSON,
}
_, err := NewEmailNotifier(model, tmpl)
_, err := NewEmailNotifier(model, nil, tmpl)
require.Error(t, err)
})
@ -44,25 +42,16 @@ func TestEmailNotifier(t *testing.T) {
settingsJSON, err := simplejson.NewJson([]byte(json))
require.NoError(t, err)
emailSender := mockNotificationService()
emailNotifier, err := NewEmailNotifier(&NotificationChannelConfig{
Name: "ops",
Type: "email",
Settings: settingsJSON,
}, tmpl)
}, emailSender, tmpl)
require.NoError(t, err)
expected := map[string]interface{}{}
bus.AddHandler("test", func(ctx context.Context, cmd *models.SendEmailCommandSync) error {
expected["subject"] = cmd.SendEmailCommand.Subject
expected["to"] = cmd.SendEmailCommand.To
expected["single_email"] = cmd.SendEmailCommand.SingleEmail
expected["template"] = cmd.SendEmailCommand.Template
expected["data"] = cmd.SendEmailCommand.Data
return nil
})
alerts := []*types.Alert{
{
Alert: model.Alert{
@ -76,6 +65,13 @@ func TestEmailNotifier(t *testing.T) {
require.NoError(t, err)
require.True(t, ok)
expected := map[string]interface{}{
"subject": emailSender.Email.Subject,
"to": emailSender.Email.To,
"single_email": emailSender.Email.SingleEmail,
"template": emailSender.Email.Template,
"data": emailSender.Email.Data,
}
require.Equal(t, map[string]interface{}{
"subject": "[FIRING:1] (AlwaysFiring warning)",
"to": []string{"someops@example.com", "somedev@example.com"},

@ -9,9 +9,9 @@ import (
"github.com/prometheus/alertmanager/template"
"github.com/prometheus/alertmanager/types"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/notifications"
"github.com/grafana/grafana/pkg/setting"
)
@ -21,11 +21,12 @@ type GoogleChatNotifier struct {
*Base
URL string
log log.Logger
ns notifications.WebhookSender
tmpl *template.Template
content string
}
func NewGoogleChatNotifier(model *NotificationChannelConfig, t *template.Template) (*GoogleChatNotifier, error) {
func NewGoogleChatNotifier(model *NotificationChannelConfig, ns notifications.WebhookSender, t *template.Template) (*GoogleChatNotifier, error) {
if model.Settings == nil {
return nil, receiverInitError{Cfg: *model, Reason: "no settings supplied"}
}
@ -47,6 +48,7 @@ func NewGoogleChatNotifier(model *NotificationChannelConfig, t *template.Templat
}),
URL: url,
log: log.New("alerting.notifier.googlechat"),
ns: ns,
tmpl: t,
content: content,
}, nil
@ -137,7 +139,7 @@ func (gcn *GoogleChatNotifier) Notify(ctx context.Context, as ...*types.Alert) (
Body: string(body),
}
if err := bus.Dispatch(ctx, cmd); err != nil {
if err := gcn.ns.SendWebhookSync(ctx, cmd); err != nil {
gcn.log.Error("Failed to send Google Hangouts Chat alert", "error", err, "webhook", gcn.Name)
return false, err
}

@ -12,9 +12,7 @@ import (
"github.com/prometheus/common/model"
"github.com/stretchr/testify/require"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/setting"
)
@ -274,7 +272,8 @@ func TestGoogleChatNotifier(t *testing.T) {
Settings: settingsJSON,
}
pn, err := NewGoogleChatNotifier(m, tmpl)
webhookSender := mockNotificationService()
pn, err := NewGoogleChatNotifier(m, webhookSender, tmpl)
if c.expInitError != "" {
require.Error(t, err)
require.Equal(t, c.expInitError, err.Error())
@ -282,12 +281,6 @@ func TestGoogleChatNotifier(t *testing.T) {
}
require.NoError(t, err)
body := ""
bus.AddHandler("test", func(ctx context.Context, webhook *models.SendWebhookSync) error {
body = webhook.Body
return nil
})
ctx := notify.WithGroupKey(context.Background(), "alertname")
ctx = notify.WithGroupLabels(ctx, model.LabelSet{"alertname": ""})
ok, err := pn.Notify(ctx, c.alerts...)
@ -303,7 +296,7 @@ func TestGoogleChatNotifier(t *testing.T) {
expBody, err := json.Marshal(c.expMsg)
require.NoError(t, err)
require.JSONEq(t, string(expBody), body)
require.JSONEq(t, string(expBody), webhookSender.Webhook.Body)
})
}
}

@ -9,10 +9,10 @@ import (
"github.com/prometheus/alertmanager/types"
"github.com/prometheus/common/model"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/notifications"
)
// KafkaNotifier is responsible for sending
@ -22,11 +22,12 @@ type KafkaNotifier struct {
Endpoint string
Topic string
log log.Logger
ns notifications.WebhookSender
tmpl *template.Template
}
// NewKafkaNotifier is the constructor function for the Kafka notifier.
func NewKafkaNotifier(model *NotificationChannelConfig, t *template.Template) (*KafkaNotifier, error) {
func NewKafkaNotifier(model *NotificationChannelConfig, ns notifications.WebhookSender, t *template.Template) (*KafkaNotifier, error) {
if model.Settings == nil {
return nil, receiverInitError{Cfg: *model, Reason: "no settings supplied"}
}
@ -51,6 +52,7 @@ func NewKafkaNotifier(model *NotificationChannelConfig, t *template.Template) (*
Endpoint: endpoint,
Topic: topic,
log: log.New("alerting.notifier.kafka"),
ns: ns,
tmpl: t,
}, nil
}
@ -112,7 +114,7 @@ func (kn *KafkaNotifier) Notify(ctx context.Context, as ...*types.Alert) (bool,
},
}
if err := bus.Dispatch(ctx, cmd); err != nil {
if err := kn.ns.SendWebhookSync(ctx, cmd); err != nil {
kn.log.Error("Failed to send notification to Kafka", "error", err, "body", string(body))
return false, err
}

@ -10,9 +10,7 @@ import (
"github.com/prometheus/common/model"
"github.com/stretchr/testify/require"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/models"
)
func TestKafkaNotifier(t *testing.T) {
@ -117,7 +115,8 @@ func TestKafkaNotifier(t *testing.T) {
Settings: settingsJSON,
}
pn, err := NewKafkaNotifier(m, tmpl)
webhookSender := mockNotificationService()
pn, err := NewKafkaNotifier(m, webhookSender, tmpl)
if c.expInitError != "" {
require.Error(t, err)
require.Equal(t, c.expInitError, err.Error())
@ -125,14 +124,6 @@ func TestKafkaNotifier(t *testing.T) {
}
require.NoError(t, err)
body := ""
actUrl := ""
bus.AddHandler("test", func(ctx context.Context, webhook *models.SendWebhookSync) error {
body = webhook.Body
actUrl = webhook.Url
return nil
})
ctx := notify.WithGroupKey(context.Background(), "alertname")
ctx = notify.WithGroupLabels(ctx, model.LabelSet{"alertname": ""})
ok, err := pn.Notify(ctx, c.alerts...)
@ -145,8 +136,8 @@ func TestKafkaNotifier(t *testing.T) {
require.NoError(t, err)
require.True(t, ok)
require.Equal(t, c.expUrl, actUrl)
require.JSONEq(t, c.expMsg, body)
require.Equal(t, c.expUrl, webhookSender.Webhook.Url)
require.JSONEq(t, c.expMsg, webhookSender.Webhook.Body)
})
}
}

@ -6,9 +6,9 @@ import (
"net/url"
"path"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/notifications"
"github.com/prometheus/alertmanager/template"
"github.com/prometheus/alertmanager/types"
)
@ -18,7 +18,7 @@ var (
)
// NewLineNotifier is the constructor for the LINE notifier
func NewLineNotifier(model *NotificationChannelConfig, t *template.Template, fn GetDecryptedValueFn) (*LineNotifier, error) {
func NewLineNotifier(model *NotificationChannelConfig, ns notifications.WebhookSender, t *template.Template, fn GetDecryptedValueFn) (*LineNotifier, error) {
if model.Settings == nil {
return nil, receiverInitError{Cfg: *model, Reason: "no settings supplied"}
}
@ -41,6 +41,7 @@ func NewLineNotifier(model *NotificationChannelConfig, t *template.Template, fn
}),
Token: token,
log: log.New("alerting.notifier.line"),
ns: ns,
tmpl: t,
}, nil
}
@ -51,6 +52,7 @@ type LineNotifier struct {
*Base
Token string
log log.Logger
ns notifications.WebhookSender
tmpl *template.Template
}
@ -86,7 +88,7 @@ func (ln *LineNotifier) Notify(ctx context.Context, as ...*types.Alert) (bool, e
Body: form.Encode(),
}
if err := bus.Dispatch(ctx, cmd); err != nil {
if err := ln.ns.SendWebhookSync(ctx, cmd); err != nil {
ln.log.Error("Failed to send notification to LINE", "error", err, "body", body)
return false, err
}

@ -10,9 +10,7 @@ import (
"github.com/prometheus/common/model"
"github.com/stretchr/testify/require"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/secrets/fakes"
secretsManager "github.com/grafana/grafana/pkg/services/secrets/manager"
)
@ -92,9 +90,10 @@ func TestLineNotifier(t *testing.T) {
SecureSettings: secureSettings,
}
webhookSender := mockNotificationService()
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
decryptFn := secretsService.GetDecryptedValue
pn, err := NewLineNotifier(m, tmpl, decryptFn)
pn, err := NewLineNotifier(m, webhookSender, tmpl, decryptFn)
if c.expInitError != "" {
require.Error(t, err)
require.Equal(t, c.expInitError, err.Error())
@ -102,14 +101,6 @@ func TestLineNotifier(t *testing.T) {
}
require.NoError(t, err)
body := ""
var headers map[string]string
bus.AddHandler("test", func(ctx context.Context, webhook *models.SendWebhookSync) error {
body = webhook.Body
headers = webhook.HttpHeader
return nil
})
ctx := notify.WithGroupKey(context.Background(), "alertname")
ctx = notify.WithGroupLabels(ctx, model.LabelSet{"alertname": ""})
ok, err := pn.Notify(ctx, c.alerts...)
@ -122,8 +113,8 @@ func TestLineNotifier(t *testing.T) {
require.NoError(t, err)
require.True(t, ok)
require.Equal(t, c.expHeaders, headers)
require.Equal(t, c.expMsg, body)
require.Equal(t, c.expHeaders, webhookSender.Webhook.HttpHeader)
require.Equal(t, c.expMsg, webhookSender.Webhook.Body)
})
}
}

@ -7,10 +7,10 @@ import (
"net/http"
"sort"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/notifications"
"github.com/prometheus/alertmanager/notify"
"github.com/prometheus/alertmanager/template"
"github.com/prometheus/alertmanager/types"
@ -38,10 +38,11 @@ type OpsgenieNotifier struct {
SendTagsAs string
tmpl *template.Template
log log.Logger
ns notifications.WebhookSender
}
// NewOpsgenieNotifier is the constructor for the Opsgenie notifier
func NewOpsgenieNotifier(model *NotificationChannelConfig, t *template.Template, fn GetDecryptedValueFn) (*OpsgenieNotifier, error) {
func NewOpsgenieNotifier(model *NotificationChannelConfig, ns notifications.WebhookSender, t *template.Template, fn GetDecryptedValueFn) (*OpsgenieNotifier, error) {
if model.Settings == nil {
return nil, receiverInitError{Cfg: *model, Reason: "no settings supplied"}
}
@ -81,6 +82,7 @@ func NewOpsgenieNotifier(model *NotificationChannelConfig, t *template.Template,
SendTagsAs: sendTagsAs,
tmpl: t,
log: log.New("alerting.notifier." + model.Name),
ns: ns,
}, nil
}
@ -120,7 +122,7 @@ func (on *OpsgenieNotifier) Notify(ctx context.Context, as ...*types.Alert) (boo
},
}
if err := bus.Dispatch(ctx, cmd); err != nil {
if err := on.ns.SendWebhookSync(ctx, cmd); err != nil {
return false, fmt.Errorf("send notification to Opsgenie: %w", err)
}

@ -6,9 +6,7 @@ import (
"testing"
"time"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/secrets/fakes"
secretsManager "github.com/grafana/grafana/pkg/services/secrets/manager"
@ -172,9 +170,11 @@ func TestOpsgenieNotifier(t *testing.T) {
SecureSettings: secureSettings,
}
webhookSender := mockNotificationService()
webhookSender.Webhook.Body = "<not-sent>"
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
decryptFn := secretsService.GetDecryptedValue
pn, err := NewOpsgenieNotifier(m, tmpl, decryptFn)
pn, err := NewOpsgenieNotifier(m, webhookSender, tmpl, decryptFn)
if c.expInitError != "" {
require.Error(t, err)
require.Equal(t, c.expInitError, err.Error())
@ -182,12 +182,6 @@ func TestOpsgenieNotifier(t *testing.T) {
}
require.NoError(t, err)
body := "<not-sent>"
bus.AddHandler("test", func(ctx context.Context, webhook *models.SendWebhookSync) error {
body = webhook.Body
return nil
})
ctx := notify.WithGroupKey(context.Background(), "alertname")
ctx = notify.WithGroupLabels(ctx, model.LabelSet{"alertname": ""})
ok, err := pn.Notify(ctx, c.alerts...)
@ -202,9 +196,9 @@ func TestOpsgenieNotifier(t *testing.T) {
if c.expMsg == "" {
// No notification was expected.
require.Equal(t, "<not-sent>", body)
require.Equal(t, "<not-sent>", webhookSender.Webhook.Body)
} else {
require.JSONEq(t, c.expMsg, body)
require.JSONEq(t, c.expMsg, webhookSender.Webhook.Body)
}
})
}

@ -6,9 +6,9 @@ import (
"fmt"
"os"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/notifications"
"github.com/prometheus/alertmanager/notify"
"github.com/prometheus/alertmanager/template"
"github.com/prometheus/alertmanager/types"
@ -37,10 +37,11 @@ type PagerdutyNotifier struct {
Summary string
tmpl *template.Template
log log.Logger
ns notifications.WebhookSender
}
// NewPagerdutyNotifier is the constructor for the PagerDuty notifier
func NewPagerdutyNotifier(model *NotificationChannelConfig, t *template.Template, fn GetDecryptedValueFn) (*PagerdutyNotifier, error) {
func NewPagerdutyNotifier(model *NotificationChannelConfig, ns notifications.WebhookSender, t *template.Template, fn GetDecryptedValueFn) (*PagerdutyNotifier, error) {
if model.Settings == nil {
return nil, receiverInitError{Cfg: *model, Reason: "no settings supplied"}
}
@ -75,6 +76,7 @@ func NewPagerdutyNotifier(model *NotificationChannelConfig, t *template.Template
Summary: model.Settings.Get("summary").MustString(DefaultMessageTitleEmbed),
tmpl: t,
log: log.New("alerting.notifier." + model.Name),
ns: ns,
}, nil
}
@ -105,7 +107,7 @@ func (pn *PagerdutyNotifier) Notify(ctx context.Context, as ...*types.Alert) (bo
"Content-Type": "application/json",
},
}
if err := bus.Dispatch(ctx, cmd); err != nil {
if err := pn.ns.SendWebhookSync(ctx, cmd); err != nil {
return false, fmt.Errorf("send notification to Pagerduty: %w", err)
}

@ -7,9 +7,7 @@ import (
"os"
"testing"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/secrets/fakes"
secretsManager "github.com/grafana/grafana/pkg/services/secrets/manager"
@ -138,9 +136,10 @@ func TestPagerdutyNotifier(t *testing.T) {
SecureSettings: secureSettings,
}
webhookSender := mockNotificationService()
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
decryptFn := secretsService.GetDecryptedValue
pn, err := NewPagerdutyNotifier(m, tmpl, decryptFn)
pn, err := NewPagerdutyNotifier(m, webhookSender, tmpl, decryptFn)
if c.expInitError != "" {
require.Error(t, err)
require.Equal(t, c.expInitError, err.Error())
@ -148,12 +147,6 @@ func TestPagerdutyNotifier(t *testing.T) {
}
require.NoError(t, err)
body := ""
bus.AddHandler("test", func(ctx context.Context, webhook *models.SendWebhookSync) error {
body = webhook.Body
return nil
})
ctx := notify.WithGroupKey(context.Background(), "alertname")
ctx = notify.WithGroupLabels(ctx, model.LabelSet{"alertname": ""})
ok, err := pn.Notify(ctx, c.alerts...)
@ -169,7 +162,7 @@ func TestPagerdutyNotifier(t *testing.T) {
expBody, err := json.Marshal(c.expMsg)
require.NoError(t, err)
require.JSONEq(t, string(expBody), body)
require.JSONEq(t, string(expBody), webhookSender.Webhook.Body)
})
}
}

@ -7,9 +7,9 @@ import (
"mime/multipart"
"strconv"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/notifications"
"github.com/prometheus/alertmanager/template"
"github.com/prometheus/alertmanager/types"
"github.com/prometheus/common/model"
@ -36,10 +36,11 @@ type PushoverNotifier struct {
Message string
tmpl *template.Template
log log.Logger
ns notifications.WebhookSender
}
// NewSlackNotifier is the constructor for the Slack notifier
func NewPushoverNotifier(model *NotificationChannelConfig, t *template.Template, fn GetDecryptedValueFn) (*PushoverNotifier, error) {
func NewPushoverNotifier(model *NotificationChannelConfig, ns notifications.WebhookSender, t *template.Template, fn GetDecryptedValueFn) (*PushoverNotifier, error) {
if model.Settings == nil {
return nil, receiverInitError{Cfg: *model, Reason: "no settings supplied"}
}
@ -92,6 +93,7 @@ func NewPushoverNotifier(model *NotificationChannelConfig, t *template.Template,
Message: model.Settings.Get("message").MustString(`{{ template "default.message" .}}`),
tmpl: t,
log: log.New("alerting.notifier.pushover"),
ns: ns,
}, nil
}
@ -110,7 +112,7 @@ func (pn *PushoverNotifier) Notify(ctx context.Context, as ...*types.Alert) (boo
Body: uploadBody.String(),
}
if err := bus.Dispatch(ctx, cmd); err != nil {
if err := pn.ns.SendWebhookSync(ctx, cmd); err != nil {
pn.log.Error("Failed to send pushover notification", "error", err, "webhook", pn.Name)
return false, err
}

@ -11,9 +11,7 @@ import (
"strings"
"testing"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/secrets/fakes"
secretsManager "github.com/grafana/grafana/pkg/services/secrets/manager"
@ -146,9 +144,10 @@ func TestPushoverNotifier(t *testing.T) {
SecureSettings: secureSettings,
}
webhookSender := mockNotificationService()
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
decryptFn := secretsService.GetDecryptedValue
pn, err := NewPushoverNotifier(m, tmpl, decryptFn)
pn, err := NewPushoverNotifier(m, webhookSender, tmpl, decryptFn)
if c.expInitError != "" {
require.Error(t, err)
require.Equal(t, c.expInitError, err.Error())
@ -156,12 +155,6 @@ func TestPushoverNotifier(t *testing.T) {
}
require.NoError(t, err)
body := ""
bus.AddHandler("test", func(ctx context.Context, webhook *models.SendWebhookSync) error {
body = webhook.Body
return nil
})
ctx := notify.WithGroupKey(context.Background(), "alertname")
ctx = notify.WithGroupLabels(ctx, model.LabelSet{"alertname": ""})
ok, err := pn.Notify(ctx, c.alerts...)
@ -174,7 +167,7 @@ func TestPushoverNotifier(t *testing.T) {
require.NoError(t, err)
require.True(t, ok)
bodyReader := multipart.NewReader(strings.NewReader(body), boundary)
bodyReader := multipart.NewReader(strings.NewReader(webhookSender.Webhook.Body), boundary)
for {
part, err := bodyReader.NextPart()
if part == nil || errors.Is(err, io.EOF) {

@ -6,9 +6,9 @@ import (
"fmt"
"strings"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/notifications"
"github.com/prometheus/alertmanager/template"
"github.com/prometheus/alertmanager/types"
"github.com/prometheus/common/model"
@ -17,6 +17,7 @@ import (
type SensuGoNotifier struct {
*Base
log log.Logger
ns notifications.WebhookSender
tmpl *template.Template
URL string
@ -29,7 +30,7 @@ type SensuGoNotifier struct {
}
// NewSensuGoNotifier is the constructor for the SensuGo notifier
func NewSensuGoNotifier(model *NotificationChannelConfig, t *template.Template, fn GetDecryptedValueFn) (*SensuGoNotifier, error) {
func NewSensuGoNotifier(model *NotificationChannelConfig, ns notifications.WebhookSender, t *template.Template, fn GetDecryptedValueFn) (*SensuGoNotifier, error) {
if model.Settings == nil {
return nil, receiverInitError{Cfg: *model, Reason: "no settings supplied"}
}
@ -63,6 +64,7 @@ func NewSensuGoNotifier(model *NotificationChannelConfig, t *template.Template,
APIKey: apikey,
Message: model.Settings.Get("message").MustString(`{{ template "default.message" .}}`),
log: log.New("alerting.notifier.sensugo"),
ns: ns,
tmpl: t,
}, nil
}
@ -145,7 +147,7 @@ func (sn *SensuGoNotifier) Notify(ctx context.Context, as ...*types.Alert) (bool
"Authorization": fmt.Sprintf("Key %s", sn.APIKey),
},
}
if err := bus.Dispatch(ctx, cmd); err != nil {
if err := sn.ns.SendWebhookSync(ctx, cmd); err != nil {
sn.log.Error("Failed to send Sensu Go event", "error", err, "sensugo", sn.Name)
return false, err
}

@ -7,9 +7,7 @@ import (
"testing"
"time"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/secrets/fakes"
secretsManager "github.com/grafana/grafana/pkg/services/secrets/manager"
@ -146,9 +144,10 @@ func TestSensuGoNotifier(t *testing.T) {
SecureSettings: secureSettings,
}
webhookSender := mockNotificationService()
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
decryptFn := secretsService.GetDecryptedValue
sn, err := NewSensuGoNotifier(m, tmpl, decryptFn)
sn, err := NewSensuGoNotifier(m, webhookSender, tmpl, decryptFn)
if c.expInitError != "" {
require.Error(t, err)
require.Equal(t, c.expInitError, err.Error())
@ -156,12 +155,6 @@ func TestSensuGoNotifier(t *testing.T) {
}
require.NoError(t, err)
body := ""
bus.AddHandler("test", func(ctx context.Context, webhook *models.SendWebhookSync) error {
body = webhook.Body
return nil
})
ctx := notify.WithGroupKey(context.Background(), "alertname")
ctx = notify.WithGroupLabels(ctx, model.LabelSet{"alertname": ""})
ok, err := sn.Notify(ctx, c.alerts...)
@ -177,7 +170,7 @@ func TestSensuGoNotifier(t *testing.T) {
expBody, err := json.Marshal(c.expMsg)
require.NoError(t, err)
require.JSONEq(t, string(expBody), body)
require.JSONEq(t, string(expBody), webhookSender.Webhook.Body)
})
}
}

@ -8,9 +8,9 @@ import (
"github.com/prometheus/alertmanager/template"
"github.com/prometheus/alertmanager/types"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/notifications"
)
// TeamsNotifier is responsible for sending
@ -21,10 +21,11 @@ type TeamsNotifier struct {
Message string
tmpl *template.Template
log log.Logger
ns notifications.WebhookSender
}
// NewTeamsNotifier is the constructor for Teams notifier.
func NewTeamsNotifier(model *NotificationChannelConfig, t *template.Template) (*TeamsNotifier, error) {
func NewTeamsNotifier(model *NotificationChannelConfig, ns notifications.WebhookSender, t *template.Template) (*TeamsNotifier, error) {
if model.Settings == nil {
return nil, receiverInitError{Cfg: *model, Reason: "no settings supplied"}
}
@ -45,6 +46,7 @@ func NewTeamsNotifier(model *NotificationChannelConfig, t *template.Template) (*
URL: u,
Message: model.Settings.Get("message").MustString(`{{ template "teams.default.message" .}}`),
log: log.New("alerting.notifier.teams"),
ns: ns,
tmpl: t,
}, nil
}
@ -97,7 +99,7 @@ func (tn *TeamsNotifier) Notify(ctx context.Context, as ...*types.Alert) (bool,
}
cmd := &models.SendWebhookSync{Url: u, Body: string(b)}
if err := bus.Dispatch(ctx, cmd); err != nil {
if err := tn.ns.SendWebhookSync(ctx, cmd); err != nil {
return false, errors.Wrap(err, "send notification to Teams")
}

@ -11,9 +11,7 @@ import (
"github.com/prometheus/common/model"
"github.com/stretchr/testify/require"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/models"
)
func TestTeamsNotifier(t *testing.T) {
@ -123,7 +121,8 @@ func TestTeamsNotifier(t *testing.T) {
Settings: settingsJSON,
}
pn, err := NewTeamsNotifier(m, tmpl)
webhookSender := mockNotificationService()
pn, err := NewTeamsNotifier(m, webhookSender, tmpl)
if c.expInitError != "" {
require.Error(t, err)
require.Equal(t, c.expInitError, err.Error())
@ -131,12 +130,6 @@ func TestTeamsNotifier(t *testing.T) {
}
require.NoError(t, err)
body := ""
bus.AddHandler("test", func(ctx context.Context, webhook *models.SendWebhookSync) error {
body = webhook.Body
return nil
})
ctx := notify.WithGroupKey(context.Background(), "alertname")
ctx = notify.WithGroupLabels(ctx, model.LabelSet{"alertname": ""})
ok, err := pn.Notify(ctx, c.alerts...)
@ -152,7 +145,7 @@ func TestTeamsNotifier(t *testing.T) {
expBody, err := json.Marshal(c.expMsg)
require.NoError(t, err)
require.JSONEq(t, string(expBody), body)
require.JSONEq(t, string(expBody), webhookSender.Webhook.Body)
})
}
}

@ -6,9 +6,9 @@ import (
"fmt"
"mime/multipart"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/notifications"
"github.com/prometheus/alertmanager/template"
"github.com/prometheus/alertmanager/types"
)
@ -25,11 +25,12 @@ type TelegramNotifier struct {
ChatID string
Message string
log log.Logger
ns notifications.WebhookSender
tmpl *template.Template
}
// NewTelegramNotifier is the constructor for the Telegram notifier
func NewTelegramNotifier(model *NotificationChannelConfig, t *template.Template, fn GetDecryptedValueFn) (*TelegramNotifier, error) {
func NewTelegramNotifier(model *NotificationChannelConfig, ns notifications.WebhookSender, t *template.Template, fn GetDecryptedValueFn) (*TelegramNotifier, error) {
if model.Settings == nil {
return nil, receiverInitError{Cfg: *model, Reason: "no settings supplied"}
}
@ -62,6 +63,7 @@ func NewTelegramNotifier(model *NotificationChannelConfig, t *template.Template,
Message: message,
tmpl: t,
log: log.New("alerting.notifier.telegram"),
ns: ns,
}, nil
}
@ -109,7 +111,7 @@ func (tn *TelegramNotifier) Notify(ctx context.Context, as ...*types.Alert) (boo
},
}
if err := bus.Dispatch(ctx, cmd); err != nil {
if err := tn.ns.SendWebhookSync(ctx, cmd); err != nil {
tn.log.Error("Failed to send webhook", "error", err, "webhook", tn.Name)
return false, err
}

@ -98,9 +98,10 @@ func TestTelegramNotifier(t *testing.T) {
SecureSettings: secureSettings,
}
webhookSender := mockNotificationService()
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
decryptFn := secretsService.GetDecryptedValue
pn, err := NewTelegramNotifier(m, tmpl, decryptFn)
pn, err := NewTelegramNotifier(m, webhookSender, tmpl, decryptFn)
if c.expInitError != "" {
require.Error(t, err)
require.Equal(t, c.expInitError, err.Error())

@ -1,6 +1,11 @@
package channels
import "time"
import (
"context"
"time"
"github.com/grafana/grafana/pkg/models"
)
// mockTimeNow replaces function timeNow to return constant time.
// It returns a function that resets the variable back to its original value.
@ -21,3 +26,20 @@ func mockTimeNow(constTime time.Time) func() {
func resetTimeNow() {
timeNow = time.Now
}
type notificationServiceMock struct {
Webhook models.SendWebhookSync
Email models.SendEmailCommandSync
ShouldError error
}
func (ns *notificationServiceMock) SendWebhookSync(ctx context.Context, cmd *models.SendWebhookSync) error {
ns.Webhook = *cmd
return ns.ShouldError
}
func (ns *notificationServiceMock) SendEmailCommandHandlerSync(ctx context.Context, cmd *models.SendEmailCommandSync) error {
ns.Email = *cmd
return ns.ShouldError
}
func mockNotificationService() *notificationServiceMock { return &notificationServiceMock{} }

@ -7,9 +7,9 @@ import (
"path"
"strings"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/notifications"
"github.com/prometheus/alertmanager/template"
"github.com/prometheus/alertmanager/types"
"github.com/prometheus/common/model"
@ -27,11 +27,12 @@ type ThreemaNotifier struct {
RecipientID string
APISecret string
log log.Logger
ns notifications.WebhookSender
tmpl *template.Template
}
// NewThreemaNotifier is the constructor for the Threema notifier
func NewThreemaNotifier(model *NotificationChannelConfig, t *template.Template, fn GetDecryptedValueFn) (*ThreemaNotifier, error) {
func NewThreemaNotifier(model *NotificationChannelConfig, ns notifications.WebhookSender, t *template.Template, fn GetDecryptedValueFn) (*ThreemaNotifier, error) {
if model.Settings == nil {
return nil, receiverInitError{Cfg: *model, Reason: "no settings supplied"}
}
@ -74,6 +75,7 @@ func NewThreemaNotifier(model *NotificationChannelConfig, t *template.Template,
RecipientID: recipientID,
APISecret: apiSecret,
log: log.New("alerting.notifier.threema"),
ns: ns,
tmpl: t,
}, nil
}
@ -119,7 +121,7 @@ func (tn *ThreemaNotifier) Notify(ctx context.Context, as ...*types.Alert) (bool
"Content-Type": "application/x-www-form-urlencoded",
},
}
if err := bus.Dispatch(ctx, cmd); err != nil {
if err := tn.ns.SendWebhookSync(ctx, cmd); err != nil {
tn.log.Error("Failed to send threema notification", "error", err, "webhook", tn.Name)
return false, err
}

@ -5,9 +5,7 @@ import (
"net/url"
"testing"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/secrets/fakes"
secretsManager "github.com/grafana/grafana/pkg/services/secrets/manager"
@ -110,9 +108,10 @@ func TestThreemaNotifier(t *testing.T) {
SecureSettings: secureSettings,
}
webhookSender := mockNotificationService()
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
decryptFn := secretsService.GetDecryptedValue
pn, err := NewThreemaNotifier(m, tmpl, decryptFn)
pn, err := NewThreemaNotifier(m, webhookSender, tmpl, decryptFn)
if c.expInitError != "" {
require.Error(t, err)
require.Equal(t, c.expInitError, err.Error())
@ -120,12 +119,6 @@ func TestThreemaNotifier(t *testing.T) {
}
require.NoError(t, err)
body := ""
bus.AddHandler("test", func(ctx context.Context, webhook *models.SendWebhookSync) error {
body = webhook.Body
return nil
})
ctx := notify.WithGroupKey(context.Background(), "alertname")
ctx = notify.WithGroupLabels(ctx, model.LabelSet{"alertname": ""})
ok, err := pn.Notify(ctx, c.alerts...)
@ -138,7 +131,7 @@ func TestThreemaNotifier(t *testing.T) {
require.NoError(t, err)
require.True(t, ok)
require.Equal(t, c.expMsg, body)
require.Equal(t, c.expMsg, webhookSender.Webhook.Body)
})
}
}

@ -10,10 +10,10 @@ import (
"github.com/prometheus/alertmanager/types"
"github.com/prometheus/common/model"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/notifications"
"github.com/grafana/grafana/pkg/setting"
)
@ -27,7 +27,7 @@ const (
// NewVictoropsNotifier creates an instance of VictoropsNotifier that
// handles posting notifications to Victorops REST API
func NewVictoropsNotifier(model *NotificationChannelConfig, t *template.Template) (*VictoropsNotifier, error) {
func NewVictoropsNotifier(model *NotificationChannelConfig, ns notifications.WebhookSender, t *template.Template) (*VictoropsNotifier, error) {
if model.Settings == nil {
return nil, receiverInitError{Cfg: *model, Reason: "no settings supplied"}
}
@ -48,6 +48,7 @@ func NewVictoropsNotifier(model *NotificationChannelConfig, t *template.Template
URL: url,
MessageType: model.Settings.Get("messageType").MustString(),
log: log.New("alerting.notifier.victorops"),
ns: ns,
tmpl: t,
}, nil
}
@ -60,6 +61,7 @@ type VictoropsNotifier struct {
URL string
MessageType string
log log.Logger
ns notifications.WebhookSender
tmpl *template.Template
}
@ -109,7 +111,7 @@ func (vn *VictoropsNotifier) Notify(ctx context.Context, as ...*types.Alert) (bo
Body: string(b),
}
if err := bus.Dispatch(ctx, cmd); err != nil {
if err := vn.ns.SendWebhookSync(ctx, cmd); err != nil {
vn.log.Error("Failed to send Victorops notification", "error", err, "webhook", vn.Name)
return false, err
}

@ -11,9 +11,7 @@ import (
"github.com/prometheus/common/model"
"github.com/stretchr/testify/require"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/setting"
)
@ -95,7 +93,8 @@ func TestVictoropsNotifier(t *testing.T) {
Settings: settingsJSON,
}
pn, err := NewVictoropsNotifier(m, tmpl)
webhookSender := mockNotificationService()
pn, err := NewVictoropsNotifier(m, webhookSender, tmpl)
if c.expInitError != "" {
require.Error(t, err)
require.Equal(t, c.expInitError, err.Error())
@ -103,12 +102,6 @@ func TestVictoropsNotifier(t *testing.T) {
}
require.NoError(t, err)
body := ""
bus.AddHandler("test", func(ctx context.Context, webhook *models.SendWebhookSync) error {
body = webhook.Body
return nil
})
ctx := notify.WithGroupKey(context.Background(), "alertname")
ctx = notify.WithGroupLabels(ctx, model.LabelSet{"alertname": ""})
ok, err := pn.Notify(ctx, c.alerts...)
@ -122,12 +115,12 @@ func TestVictoropsNotifier(t *testing.T) {
require.True(t, ok)
// Remove the non-constant timestamp
j, err := simplejson.NewJson([]byte(body))
j, err := simplejson.NewJson([]byte(webhookSender.Webhook.Body))
require.NoError(t, err)
j.Del("timestamp")
b, err := j.MarshalJSON()
require.NoError(t, err)
body = string(b)
body := string(b)
expJson, err := json.Marshal(c.expMsg)
require.NoError(t, err)

@ -4,9 +4,9 @@ import (
"context"
"encoding/json"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/notifications"
"github.com/prometheus/alertmanager/notify"
"github.com/prometheus/alertmanager/template"
"github.com/prometheus/alertmanager/types"
@ -23,13 +23,14 @@ type WebhookNotifier struct {
HTTPMethod string
MaxAlerts int
log log.Logger
ns notifications.WebhookSender
tmpl *template.Template
orgID int64
}
// NewWebHookNotifier is the constructor for
// the WebHook notifier.
func NewWebHookNotifier(model *NotificationChannelConfig, t *template.Template, fn GetDecryptedValueFn) (*WebhookNotifier, error) {
func NewWebHookNotifier(model *NotificationChannelConfig, ns notifications.WebhookSender, t *template.Template, fn GetDecryptedValueFn) (*WebhookNotifier, error) {
if model.Settings == nil {
return nil, receiverInitError{Cfg: *model, Reason: "no settings supplied"}
}
@ -55,6 +56,7 @@ func NewWebHookNotifier(model *NotificationChannelConfig, t *template.Template,
HTTPMethod: model.Settings.Get("httpMethod").MustString("POST"),
MaxAlerts: model.Settings.Get("maxAlerts").MustInt(0),
log: log.New("alerting.notifier.webhook"),
ns: ns,
tmpl: t,
}, nil
}
@ -118,7 +120,7 @@ func (wn *WebhookNotifier) Notify(ctx context.Context, as ...*types.Alert) (bool
HttpMethod: wn.HTTPMethod,
}
if err := bus.Dispatch(ctx, cmd); err != nil {
if err := wn.ns.SendWebhookSync(ctx, cmd); err != nil {
return false, err
}

@ -6,9 +6,7 @@ import (
"net/url"
"testing"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/secrets/fakes"
secretsManager "github.com/grafana/grafana/pkg/services/secrets/manager"
@ -192,9 +190,10 @@ func TestWebhookNotifier(t *testing.T) {
SecureSettings: secureSettings,
}
webhookSender := mockNotificationService()
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
decryptFn := secretsService.GetDecryptedValue
pn, err := NewWebHookNotifier(m, tmpl, decryptFn)
pn, err := NewWebHookNotifier(m, webhookSender, tmpl, decryptFn)
if c.expInitError != "" {
require.Error(t, err)
require.Equal(t, c.expInitError, err.Error())
@ -202,12 +201,6 @@ func TestWebhookNotifier(t *testing.T) {
}
require.NoError(t, err)
var payload *models.SendWebhookSync
bus.AddHandler("test", func(ctx context.Context, webhook *models.SendWebhookSync) error {
payload = webhook
return nil
})
ctx := notify.WithGroupKey(context.Background(), "alertname")
ctx = notify.WithGroupLabels(ctx, model.LabelSet{"alertname": ""})
ctx = notify.WithReceiverName(ctx, "my_receiver")
@ -224,11 +217,11 @@ func TestWebhookNotifier(t *testing.T) {
expBody, err := json.Marshal(c.expMsg)
require.NoError(t, err)
require.JSONEq(t, string(expBody), payload.Body)
require.Equal(t, c.expUrl, payload.Url)
require.Equal(t, c.expUsername, payload.User)
require.Equal(t, c.expPassword, payload.Password)
require.Equal(t, c.expHttpMethod, payload.HttpMethod)
require.JSONEq(t, string(expBody), webhookSender.Webhook.Body)
require.Equal(t, c.expUrl, webhookSender.Webhook.Url)
require.Equal(t, c.expUsername, webhookSender.Webhook.User)
require.Equal(t, c.expPassword, webhookSender.Webhook.Password)
require.Equal(t, c.expHttpMethod, webhookSender.Webhook.HttpMethod)
})
}
}

@ -7,14 +7,14 @@ import (
"github.com/prometheus/alertmanager/types"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/notifications"
"github.com/prometheus/alertmanager/template"
)
// NewWeComNotifier is the constructor for WeCom notifier.
func NewWeComNotifier(model *NotificationChannelConfig, t *template.Template, fn GetDecryptedValueFn) (*WeComNotifier, error) {
func NewWeComNotifier(model *NotificationChannelConfig, ns notifications.WebhookSender, t *template.Template, fn GetDecryptedValueFn) (*WeComNotifier, error) {
url := fn(context.Background(), model.SecureSettings, "url", model.Settings.Get("url").MustString())
if url == "" {
@ -31,6 +31,7 @@ func NewWeComNotifier(model *NotificationChannelConfig, t *template.Template, fn
}),
URL: url,
log: log.New("alerting.notifier.wecom"),
ns: ns,
Message: model.Settings.Get("message").MustString(`{{ template "default.message" .}}`),
tmpl: t,
}, nil
@ -43,6 +44,7 @@ type WeComNotifier struct {
Message string
tmpl *template.Template
log log.Logger
ns notifications.WebhookSender
}
// Notify send an alert notification to WeCom.
@ -74,7 +76,7 @@ func (w *WeComNotifier) Notify(ctx context.Context, as ...*types.Alert) (bool, e
Body: string(body),
}
if err := bus.Dispatch(ctx, cmd); err != nil {
if err := w.ns.SendWebhookSync(ctx, cmd); err != nil {
w.log.Error("failed to send WeCom webhook", "error", err, "notification", w.Name)
return false, err
}

@ -14,9 +14,7 @@ import (
"github.com/prometheus/common/model"
"github.com/stretchr/testify/require"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/models"
)
func TestWeComNotifier(t *testing.T) {
@ -96,21 +94,16 @@ func TestWeComNotifier(t *testing.T) {
Settings: settingsJSON,
}
webhookSender := mockNotificationService()
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
decryptFn := secretsService.GetDecryptedValue
pn, err := NewWeComNotifier(m, tmpl, decryptFn)
pn, err := NewWeComNotifier(m, webhookSender, tmpl, decryptFn)
if c.expInitError != "" {
require.Equal(t, c.expInitError, err.Error())
return
}
require.NoError(t, err)
body := ""
bus.AddHandler("test", func(ctx context.Context, webhook *models.SendWebhookSync) error {
body = webhook.Body
return nil
})
ctx := notify.WithGroupKey(context.Background(), "alertname")
ctx = notify.WithGroupLabels(ctx, model.LabelSet{"alertname": ""})
ok, err := pn.Notify(ctx, c.alerts...)
@ -126,7 +119,7 @@ func TestWeComNotifier(t *testing.T) {
expBody, err := json.Marshal(c.expMsg)
require.NoError(t, err)
require.JSONEq(t, string(expBody), body)
require.JSONEq(t, string(expBody), webhookSender.Webhook.Body)
})
}
}

@ -10,6 +10,7 @@ import (
"time"
"github.com/grafana/grafana/pkg/services/ngalert/notifier/channels"
"github.com/grafana/grafana/pkg/services/notifications"
"github.com/prometheus/alertmanager/cluster"
"github.com/prometheus/client_golang/prometheus"
@ -45,10 +46,12 @@ type MultiOrgAlertmanager struct {
decryptFn channels.GetDecryptedValueFn
metrics *metrics.MultiOrgAlertmanager
ns notifications.Service
}
func NewMultiOrgAlertmanager(cfg *setting.Cfg, configStore store.AlertingStore, orgStore store.OrgStore,
kvStore kvstore.KVStore, decryptFn channels.GetDecryptedValueFn, m *metrics.MultiOrgAlertmanager, l log.Logger,
kvStore kvstore.KVStore, decryptFn channels.GetDecryptedValueFn, m *metrics.MultiOrgAlertmanager,
ns notifications.Service, l log.Logger,
) (*MultiOrgAlertmanager, error) {
moa := &MultiOrgAlertmanager{
logger: l,
@ -59,6 +62,7 @@ func NewMultiOrgAlertmanager(cfg *setting.Cfg, configStore store.AlertingStore,
kvStore: kvStore,
decryptFn: decryptFn,
metrics: m,
ns: ns,
}
clusterLogger := l.New("component", "cluster")
@ -170,7 +174,7 @@ func (moa *MultiOrgAlertmanager) SyncAlertmanagersForOrgs(ctx context.Context, o
// To export them, we need to translate the metrics from each individual registry and,
// then aggregate them on the main registry.
m := metrics.NewAlertmanagerMetrics(moa.metrics.GetOrCreateOrgRegistry(orgID))
am, err := newAlertmanager(ctx, orgID, moa.settings, moa.configStore, moa.kvStore, moa.peer, moa.decryptFn, m)
am, err := newAlertmanager(ctx, orgID, moa.settings, moa.configStore, moa.kvStore, moa.peer, moa.decryptFn, moa.ns, m)
if err != nil {
moa.logger.Error("unable to create Alertmanager for org", "org", orgID, "err", err)
}

@ -46,7 +46,7 @@ func TestMultiOrgAlertmanager_SyncAlertmanagersForOrgs(t *testing.T) {
DisabledOrgs: map[int64]struct{}{5: {}},
}, // do not poll in tests.
}
mam, err := NewMultiOrgAlertmanager(cfg, configStore, orgStore, kvStore, decryptFn, m.GetMultiOrgAlertmanagerMetrics(), log.New("testlogger"))
mam, err := NewMultiOrgAlertmanager(cfg, configStore, orgStore, kvStore, decryptFn, m.GetMultiOrgAlertmanagerMetrics(), nil, log.New("testlogger"))
require.NoError(t, err)
ctx := context.Background()
@ -173,7 +173,7 @@ func TestMultiOrgAlertmanager_SyncAlertmanagersForOrgsWithFailures(t *testing.T)
DefaultConfiguration: setting.GetAlertmanagerDefaultConfiguration(),
}, // do not poll in tests.
}
mam, err := NewMultiOrgAlertmanager(cfg, configStore, orgStore, kvStore, decryptFn, m.GetMultiOrgAlertmanagerMetrics(), log.New("testlogger"))
mam, err := NewMultiOrgAlertmanager(cfg, configStore, orgStore, kvStore, decryptFn, m.GetMultiOrgAlertmanagerMetrics(), nil, log.New("testlogger"))
require.NoError(t, err)
ctx := context.Background()
@ -224,7 +224,7 @@ func TestMultiOrgAlertmanager_AlertmanagerFor(t *testing.T) {
decryptFn := secretsService.GetDecryptedValue
reg := prometheus.NewPedanticRegistry()
m := metrics.NewNGAlert(reg)
mam, err := NewMultiOrgAlertmanager(cfg, configStore, orgStore, kvStore, decryptFn, m.GetMultiOrgAlertmanagerMetrics(), log.New("testlogger"))
mam, err := NewMultiOrgAlertmanager(cfg, configStore, orgStore, kvStore, decryptFn, m.GetMultiOrgAlertmanagerMetrics(), nil, log.New("testlogger"))
require.NoError(t, err)
ctx := context.Background()

@ -923,7 +923,7 @@ func setupScheduler(t *testing.T, rs store.RuleStore, is store.InstanceStore, ac
m := metrics.NewNGAlert(registry)
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
decryptFn := secretsService.GetDecryptedValue
moa, err := notifier.NewMultiOrgAlertmanager(&setting.Cfg{}, &notifier.FakeConfigStore{}, &notifier.FakeOrgStore{}, &notifier.FakeKVStore{}, decryptFn, nil, log.New("testlogger"))
moa, err := notifier.NewMultiOrgAlertmanager(&setting.Cfg{}, &notifier.FakeConfigStore{}, &notifier.FakeOrgStore{}, &notifier.FakeKVStore{}, decryptFn, m.GetMultiOrgAlertmanagerMetrics(), nil, log.New("testlogger"))
require.NoError(t, err)
schedCfg := SchedulerCfg{

@ -39,7 +39,7 @@ func SetupTestEnv(t *testing.T, baseInterval time.Duration) (*ngalert.AlertNG, *
secretsService := secretsManager.SetupTestService(t, database.ProvideSecretsStore(sqlStore))
ng, err := ngalert.ProvideService(
cfg, nil, routing.NewRouteRegister(), sqlStore,
nil, nil, nil, nil, secretsService, m,
nil, nil, nil, nil, secretsService, nil, m,
)
require.NoError(t, err)
return ng, &store.DBstore{

@ -0,0 +1,33 @@
package notifications
import (
"context"
"github.com/grafana/grafana/pkg/models"
)
type NotificationServiceMock struct {
Webhook models.SendWebhookSync
Email models.SendEmailCommandSync
ShouldError error
WebhookHandler func(context.Context, *models.SendWebhookSync) error
EmailHandler func(context.Context, *models.SendEmailCommandSync) error
}
func (ns *NotificationServiceMock) SendWebhookSync(ctx context.Context, cmd *models.SendWebhookSync) error {
ns.Webhook = *cmd
if ns.WebhookHandler != nil {
return ns.WebhookHandler(ctx, cmd)
}
return ns.ShouldError
}
func (ns *NotificationServiceMock) SendEmailCommandHandlerSync(ctx context.Context, cmd *models.SendEmailCommandSync) error {
ns.Email = *cmd
if ns.EmailHandler != nil {
return ns.EmailHandler(ctx, cmd)
}
return ns.ShouldError
}
func MockNotificationService() *NotificationServiceMock { return &NotificationServiceMock{} }

@ -17,6 +17,17 @@ import (
"github.com/grafana/grafana/pkg/util"
)
type WebhookSender interface {
SendWebhookSync(ctx context.Context, cmd *models.SendWebhookSync) error
}
type EmailSender interface {
SendEmailCommandHandlerSync(ctx context.Context, cmd *models.SendEmailCommandSync) error
}
type Service interface {
WebhookSender
EmailSender
}
var mailTemplates *template.Template
var tmplResetPassword = "reset_password"
var tmplSignUpStarted = "signup_started"
@ -36,7 +47,7 @@ func ProvideService(bus bus.Bus, cfg *setting.Cfg, mailer Mailer) (*Notification
ns.Bus.AddHandler(ns.validateResetPasswordCode)
ns.Bus.AddHandler(ns.sendEmailCommandHandler)
ns.Bus.AddHandler(ns.sendEmailCommandHandlerSync)
ns.Bus.AddHandler(ns.SendEmailCommandHandlerSync)
ns.Bus.AddHandler(ns.SendWebhookSync)
ns.Bus.AddEventListener(ns.signUpStartedHandler)
@ -119,7 +130,7 @@ func subjectTemplateFunc(obj map[string]interface{}, value string) string {
return ""
}
func (ns *NotificationService) sendEmailCommandHandlerSync(ctx context.Context, cmd *models.SendEmailCommandSync) error {
func (ns *NotificationService) SendEmailCommandHandlerSync(ctx context.Context, cmd *models.SendEmailCommandSync) error {
message, err := ns.buildEmailMessage(&models.SendEmailCommand{
Data: cmd.Data,
Info: cmd.Info,

@ -493,37 +493,37 @@ func (m *migration) validateAlertmanagerConfig(orgID int64, config *PostableUser
switch gr.Type {
case "email":
_, err = channels.NewEmailNotifier(cfg, nil) // Email notifier already has a default template.
_, err = channels.NewEmailNotifier(cfg, nil, nil) // Email notifier already has a default template.
case "pagerduty":
_, err = channels.NewPagerdutyNotifier(cfg, nil, decryptFunc)
_, err = channels.NewPagerdutyNotifier(cfg, nil, nil, decryptFunc)
case "pushover":
_, err = channels.NewPushoverNotifier(cfg, nil, decryptFunc)
_, err = channels.NewPushoverNotifier(cfg, nil, nil, decryptFunc)
case "slack":
_, err = channels.NewSlackNotifier(cfg, nil, decryptFunc)
case "telegram":
_, err = channels.NewTelegramNotifier(cfg, nil, decryptFunc)
_, err = channels.NewTelegramNotifier(cfg, nil, nil, decryptFunc)
case "victorops":
_, err = channels.NewVictoropsNotifier(cfg, nil)
_, err = channels.NewVictoropsNotifier(cfg, nil, nil)
case "teams":
_, err = channels.NewTeamsNotifier(cfg, nil)
_, err = channels.NewTeamsNotifier(cfg, nil, nil)
case "dingding":
_, err = channels.NewDingDingNotifier(cfg, nil)
_, err = channels.NewDingDingNotifier(cfg, nil, nil)
case "kafka":
_, err = channels.NewKafkaNotifier(cfg, nil)
_, err = channels.NewKafkaNotifier(cfg, nil, nil)
case "webhook":
_, err = channels.NewWebHookNotifier(cfg, nil, decryptFunc)
_, err = channels.NewWebHookNotifier(cfg, nil, nil, decryptFunc)
case "sensugo":
_, err = channels.NewSensuGoNotifier(cfg, nil, decryptFunc)
_, err = channels.NewSensuGoNotifier(cfg, nil, nil, decryptFunc)
case "discord":
_, err = channels.NewDiscordNotifier(cfg, nil)
_, err = channels.NewDiscordNotifier(cfg, nil, nil)
case "googlechat":
_, err = channels.NewGoogleChatNotifier(cfg, nil)
_, err = channels.NewGoogleChatNotifier(cfg, nil, nil)
case "LINE":
_, err = channels.NewLineNotifier(cfg, nil, decryptFunc)
_, err = channels.NewLineNotifier(cfg, nil, nil, decryptFunc)
case "threema":
_, err = channels.NewThreemaNotifier(cfg, nil, decryptFunc)
_, err = channels.NewThreemaNotifier(cfg, nil, nil, decryptFunc)
case "opsgenie":
_, err = channels.NewOpsgenieNotifier(cfg, nil, decryptFunc)
_, err = channels.NewOpsgenieNotifier(cfg, nil, nil, decryptFunc)
case "prometheus-alertmanager":
_, err = channels.NewAlertmanagerNotifier(cfg, nil, decryptFunc)
default:

@ -41,10 +41,10 @@ func TestTestReceivers(t *testing.T) {
EnableUnifiedAlerting: true,
})
grafanaListedAddr, store := testinfra.StartGrafana(t, dir, path)
store.Bus = bus.GetBus()
grafanaListedAddr, env := testinfra.StartGrafanaEnv(t, dir, path)
env.SQLStore.Bus = bus.GetBus()
createUser(t, store, models.CreateUserCommand{
createUser(t, env.SQLStore, models.CreateUserCommand{
DefaultOrgRole: string(models.ROLE_EDITOR),
Login: "grafana",
Password: "password",
@ -74,21 +74,17 @@ func TestTestReceivers(t *testing.T) {
EnableUnifiedAlerting: true,
})
grafanaListedAddr, store := testinfra.StartGrafana(t, dir, path)
store.Bus = bus.GetBus()
grafanaListedAddr, env := testinfra.StartGrafanaEnv(t, dir, path)
env.SQLStore.Bus = bus.GetBus()
createUser(t, store, models.CreateUserCommand{
createUser(t, env.SQLStore, models.CreateUserCommand{
DefaultOrgRole: string(models.ROLE_EDITOR),
Login: "grafana",
Password: "password",
})
oldEmailBus := bus.GetHandlerCtx("SendEmailCommandSync")
mockEmails := &mockEmailHandler{}
bus.AddHandler("", mockEmails.sendEmailCommandHandlerSync)
t.Cleanup(func() {
bus.AddHandler("", oldEmailBus)
})
env.NotificationService.EmailHandler = mockEmails.sendEmailCommandHandlerSync
testReceiversURL := fmt.Sprintf("http://grafana:password@%s/api/alertmanager/grafana/config/api/v1/receivers/test", grafanaListedAddr)
// nolint
@ -162,21 +158,17 @@ func TestTestReceivers(t *testing.T) {
EnableUnifiedAlerting: true,
})
grafanaListedAddr, store := testinfra.StartGrafana(t, dir, path)
store.Bus = bus.GetBus()
grafanaListedAddr, env := testinfra.StartGrafanaEnv(t, dir, path)
env.SQLStore.Bus = bus.GetBus()
createUser(t, store, models.CreateUserCommand{
createUser(t, env.SQLStore, models.CreateUserCommand{
DefaultOrgRole: string(models.ROLE_EDITOR),
Login: "grafana",
Password: "password",
})
oldEmailBus := bus.GetHandlerCtx("SendEmailCommandSync")
mockEmails := &mockEmailHandler{}
bus.AddHandler("", mockEmails.sendEmailCommandHandlerSync)
t.Cleanup(func() {
bus.AddHandler("", oldEmailBus)
})
env.NotificationService.EmailHandler = mockEmails.sendEmailCommandHandlerSync
testReceiversURL := fmt.Sprintf("http://grafana:password@%s/api/alertmanager/grafana/config/api/v1/receivers/test", grafanaListedAddr)
// nolint
@ -245,23 +237,19 @@ func TestTestReceivers(t *testing.T) {
EnableUnifiedAlerting: true,
})
grafanaListedAddr, store := testinfra.StartGrafana(t, dir, path)
store.Bus = bus.GetBus()
grafanaListedAddr, env := testinfra.StartGrafanaEnv(t, dir, path)
env.SQLStore.Bus = bus.GetBus()
createUser(t, store, models.CreateUserCommand{
createUser(t, env.SQLStore, models.CreateUserCommand{
DefaultOrgRole: string(models.ROLE_EDITOR),
Login: "grafana",
Password: "password",
})
oldEmailBus := bus.GetHandlerCtx("SendEmailCommandSync")
mockEmails := &mockEmailHandlerWithTimeout{
timeout: 5 * time.Second,
}
bus.AddHandler("", mockEmails.sendEmailCommandHandlerSync)
t.Cleanup(func() {
bus.AddHandler("", oldEmailBus)
})
env.NotificationService.EmailHandler = mockEmails.sendEmailCommandHandlerSync
testReceiversURL := fmt.Sprintf("http://grafana:password@%s/api/alertmanager/grafana/config/api/v1/receivers/test", grafanaListedAddr)
req, err := http.NewRequest(http.MethodPost, testReceiversURL, strings.NewReader(`{
@ -338,23 +326,19 @@ func TestTestReceivers(t *testing.T) {
EnableUnifiedAlerting: true,
})
grafanaListedAddr, store := testinfra.StartGrafana(t, dir, path)
store.Bus = bus.GetBus()
grafanaListedAddr, env := testinfra.StartGrafanaEnv(t, dir, path)
env.SQLStore.Bus = bus.GetBus()
createUser(t, store, models.CreateUserCommand{
createUser(t, env.SQLStore, models.CreateUserCommand{
DefaultOrgRole: string(models.ROLE_EDITOR),
Login: "grafana",
Password: "password",
})
oldEmailBus := bus.GetHandlerCtx("SendEmailCommandSync")
mockEmails := &mockEmailHandlerWithTimeout{
timeout: 5 * time.Second,
}
bus.AddHandler("", mockEmails.sendEmailCommandHandlerSync)
t.Cleanup(func() {
bus.AddHandler("", oldEmailBus)
})
env.NotificationService.EmailHandler = mockEmails.sendEmailCommandHandlerSync
testReceiversURL := fmt.Sprintf("http://grafana:password@%s/api/alertmanager/grafana/config/api/v1/receivers/test", grafanaListedAddr)
req, err := http.NewRequest(http.MethodPost, testReceiversURL, strings.NewReader(`{
@ -457,21 +441,17 @@ func TestTestReceiversAlertCustomization(t *testing.T) {
EnableUnifiedAlerting: true,
})
grafanaListedAddr, store := testinfra.StartGrafana(t, dir, path)
store.Bus = bus.GetBus()
grafanaListedAddr, env := testinfra.StartGrafanaEnv(t, dir, path)
env.SQLStore.Bus = bus.GetBus()
createUser(t, store, models.CreateUserCommand{
createUser(t, env.SQLStore, models.CreateUserCommand{
DefaultOrgRole: string(models.ROLE_EDITOR),
Login: "grafana",
Password: "password",
})
oldEmailBus := bus.GetHandlerCtx("SendEmailCommandSync")
mockEmails := &mockEmailHandler{}
bus.AddHandler("", mockEmails.sendEmailCommandHandlerSync)
t.Cleanup(func() {
bus.AddHandler("", oldEmailBus)
})
env.NotificationService.EmailHandler = mockEmails.sendEmailCommandHandlerSync
testReceiversURL := fmt.Sprintf("http://grafana:password@%s/api/alertmanager/grafana/config/api/v1/receivers/test", grafanaListedAddr)
// nolint
@ -556,21 +536,17 @@ func TestTestReceiversAlertCustomization(t *testing.T) {
EnableUnifiedAlerting: true,
})
grafanaListedAddr, store := testinfra.StartGrafana(t, dir, path)
store.Bus = bus.GetBus()
grafanaListedAddr, env := testinfra.StartGrafanaEnv(t, dir, path)
env.SQLStore.Bus = bus.GetBus()
createUser(t, store, models.CreateUserCommand{
createUser(t, env.SQLStore, models.CreateUserCommand{
DefaultOrgRole: string(models.ROLE_EDITOR),
Login: "grafana",
Password: "password",
})
oldEmailBus := bus.GetHandlerCtx("SendEmailCommandSync")
mockEmails := &mockEmailHandler{}
bus.AddHandler("", mockEmails.sendEmailCommandHandlerSync)
t.Cleanup(func() {
bus.AddHandler("", oldEmailBus)
})
env.NotificationService.EmailHandler = mockEmails.sendEmailCommandHandlerSync
testReceiversURL := fmt.Sprintf("http://grafana:password@%s/api/alertmanager/grafana/config/api/v1/receivers/test", grafanaListedAddr)
// nolint
@ -650,21 +626,17 @@ func TestTestReceiversAlertCustomization(t *testing.T) {
EnableUnifiedAlerting: true,
})
grafanaListedAddr, store := testinfra.StartGrafana(t, dir, path)
store.Bus = bus.GetBus()
grafanaListedAddr, env := testinfra.StartGrafanaEnv(t, dir, path)
env.SQLStore.Bus = bus.GetBus()
createUser(t, store, models.CreateUserCommand{
createUser(t, env.SQLStore, models.CreateUserCommand{
DefaultOrgRole: string(models.ROLE_EDITOR),
Login: "grafana",
Password: "password",
})
oldEmailBus := bus.GetHandlerCtx("SendEmailCommandSync")
mockEmails := &mockEmailHandler{}
bus.AddHandler("", mockEmails.sendEmailCommandHandlerSync)
t.Cleanup(func() {
bus.AddHandler("", oldEmailBus)
})
env.NotificationService.EmailHandler = mockEmails.sendEmailCommandHandlerSync
testReceiversURL := fmt.Sprintf("http://grafana:password@%s/api/alertmanager/grafana/config/api/v1/receivers/test", grafanaListedAddr)
// nolint
@ -745,8 +717,8 @@ func TestNotificationChannels(t *testing.T) {
DisableAnonymous: true,
})
grafanaListedAddr, s := testinfra.StartGrafana(t, dir, path)
s.Bus = bus.GetBus()
grafanaListedAddr, env := testinfra.StartGrafanaEnv(t, dir, path)
env.SQLStore.Bus = bus.GetBus()
mockChannel := newMockNotificationChannel(t, grafanaListedAddr)
amConfig := getAlertmanagerConfig(mockChannel.server.Addr)
@ -757,13 +729,11 @@ func TestNotificationChannels(t *testing.T) {
channels.TelegramAPIURL, channels.PushoverEndpoint, channels.GetBoundary,
channels.LineNotifyURL, channels.ThreemaGwBaseURL
originalTemplate := channels.DefaultTemplateString
originalEmailBus := bus.GetHandlerCtx("SendEmailCommandSync")
t.Cleanup(func() {
channels.SlackAPIEndpoint, channels.PagerdutyEventAPIURL,
channels.TelegramAPIURL, channels.PushoverEndpoint, channels.GetBoundary,
channels.LineNotifyURL, channels.ThreemaGwBaseURL = os, opa, ot, opu, ogb, ol, oth
channels.DefaultTemplateString = originalTemplate
bus.AddHandler("", originalEmailBus)
})
channels.DefaultTemplateString = channels.TemplateForTestsString
channels.SlackAPIEndpoint = fmt.Sprintf("http://%s/slack_recvX/slack_testX", mockChannel.server.Addr)
@ -773,10 +743,19 @@ func TestNotificationChannels(t *testing.T) {
channels.LineNotifyURL = fmt.Sprintf("http://%s/line_recv/line_test", mockChannel.server.Addr)
channels.ThreemaGwBaseURL = fmt.Sprintf("http://%s/threema_recv/threema_test", mockChannel.server.Addr)
channels.GetBoundary = func() string { return "abcd" }
bus.AddHandler("", mockEmail.sendEmailCommandHandlerSync)
env.NotificationService.EmailHandler = mockEmail.sendEmailCommandHandlerSync
// As we are using a NotificationService mock here, but he test expects real NotificationService -
// we try to issue a real POST request here
env.NotificationService.WebhookHandler = func(_ context.Context, cmd *models.SendWebhookSync) error {
if res, err := http.Post(cmd.Url, "", strings.NewReader(cmd.Body)); err == nil {
_ = res.Body.Close()
}
return nil
}
// Create a user to make authenticated requests
createUser(t, s, models.CreateUserCommand{
createUser(t, env.SQLStore, models.CreateUserCommand{
DefaultOrgRole: string(models.ROLE_EDITOR),
Password: "password",
Login: "grafana",
@ -793,7 +772,7 @@ func TestNotificationChannels(t *testing.T) {
{
// Create the namespace we'll save our alerts to.
_, err := createFolder(t, s, 0, "default")
_, err := createFolder(t, env.SQLStore, 0, "default")
require.NoError(t, err)
// Post the alertmanager config.

@ -27,6 +27,11 @@ import (
// StartGrafana starts a Grafana server.
// The server address is returned.
func StartGrafana(t *testing.T, grafDir, cfgPath string) (string, *sqlstore.SQLStore) {
addr, env := StartGrafanaEnv(t, grafDir, cfgPath)
return addr, env.SQLStore
}
func StartGrafanaEnv(t *testing.T, grafDir, cfgPath string) (string, *server.TestEnv) {
t.Helper()
ctx := context.Background()
@ -65,7 +70,7 @@ func StartGrafana(t *testing.T, grafDir, cfgPath string) (string, *sqlstore.SQLS
t.Logf("Grafana is listening on %s", addr)
return addr, env.SQLStore
return addr, env
}
// SetUpDatabase sets up the Grafana database.

Loading…
Cancel
Save