mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Alerting: refactor receiver validation to be reusable (#46103)
This commit is contained in:
parent
00f67cad1b
commit
e135b8531a
@ -451,11 +451,6 @@ func (am *Alertmanager) buildIntegrationsMap(receivers []*apimodels.PostableApiR
|
||||
return integrationsMap, nil
|
||||
}
|
||||
|
||||
type NotificationChannel interface {
|
||||
notify.Notifier
|
||||
notify.ResolvedSender
|
||||
}
|
||||
|
||||
// buildReceiverIntegrations builds a list of integration notifiers off of a receiver config.
|
||||
func (am *Alertmanager) buildReceiverIntegrations(receiver *apimodels.PostableApiReceiver, tmpl *template.Template) ([]notify.Integration, error) {
|
||||
var integrations []notify.Integration
|
||||
@ -469,7 +464,7 @@ func (am *Alertmanager) buildReceiverIntegrations(receiver *apimodels.PostableAp
|
||||
return integrations, nil
|
||||
}
|
||||
|
||||
func (am *Alertmanager) buildReceiverIntegration(r *apimodels.PostableGrafanaReceiver, tmpl *template.Template) (NotificationChannel, error) {
|
||||
func (am *Alertmanager) buildReceiverIntegration(r *apimodels.PostableGrafanaReceiver, tmpl *template.Template) (channels.NotificationChannel, error) {
|
||||
// secure settings are already encrypted at this point
|
||||
secureSettings := make(map[string][]byte, len(r.SecureSettings))
|
||||
|
||||
@ -494,60 +489,28 @@ func (am *Alertmanager) buildReceiverIntegration(r *apimodels.PostableGrafanaRec
|
||||
Settings: r.Settings,
|
||||
SecureSettings: secureSettings,
|
||||
}
|
||||
n NotificationChannel
|
||||
err error
|
||||
)
|
||||
switch r.Type {
|
||||
case "email":
|
||||
n, err = channels.NewEmailNotifier(cfg, am.NotificationService, tmpl) // Email notifier already has a default template.
|
||||
case "pagerduty":
|
||||
n, err = channels.NewPagerdutyNotifier(cfg, am.NotificationService, tmpl, am.decryptFn)
|
||||
case "pushover":
|
||||
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, am.NotificationService, tmpl, am.decryptFn)
|
||||
case "victorops":
|
||||
n, err = channels.NewVictoropsNotifier(cfg, am.NotificationService, tmpl)
|
||||
case "teams":
|
||||
n, err = channels.NewTeamsNotifier(cfg, am.NotificationService, tmpl)
|
||||
case "dingding":
|
||||
n, err = channels.NewDingDingNotifier(cfg, am.NotificationService, tmpl)
|
||||
case "kafka":
|
||||
n, err = channels.NewKafkaNotifier(cfg, am.NotificationService, tmpl)
|
||||
case "webhook":
|
||||
n, err = channels.NewWebHookNotifier(cfg, am.NotificationService, tmpl, am.decryptFn)
|
||||
case "wecom":
|
||||
n, err = channels.NewWeComNotifier(cfg, am.NotificationService, tmpl, am.decryptFn)
|
||||
case "sensugo":
|
||||
n, err = channels.NewSensuGoNotifier(cfg, am.NotificationService, tmpl, am.decryptFn)
|
||||
case "discord":
|
||||
n, err = channels.NewDiscordNotifier(cfg, am.NotificationService, tmpl)
|
||||
case "googlechat":
|
||||
n, err = channels.NewGoogleChatNotifier(cfg, am.NotificationService, tmpl)
|
||||
case "LINE":
|
||||
n, err = channels.NewLineNotifier(cfg, am.NotificationService, tmpl, am.decryptFn)
|
||||
case "threema":
|
||||
n, err = channels.NewThreemaNotifier(cfg, am.NotificationService, tmpl, am.decryptFn)
|
||||
case "opsgenie":
|
||||
n, err = channels.NewOpsgenieNotifier(cfg, am.NotificationService, tmpl, am.decryptFn)
|
||||
case "prometheus-alertmanager":
|
||||
n, err = channels.NewAlertmanagerNotifier(cfg, tmpl, am.decryptFn)
|
||||
default:
|
||||
return nil, InvalidReceiverError{
|
||||
Receiver: r,
|
||||
Err: fmt.Errorf("notifier %s is not supported", r.Type),
|
||||
}
|
||||
}
|
||||
|
||||
factoryConfig, err := channels.NewFactoryConfig(cfg, am.NotificationService, am.decryptFn, tmpl)
|
||||
if err != nil {
|
||||
return nil, InvalidReceiverError{
|
||||
Receiver: r,
|
||||
Err: err,
|
||||
}
|
||||
}
|
||||
receiverFactory, exists := channels.Factory(r.Type)
|
||||
if !exists {
|
||||
return nil, InvalidReceiverError{
|
||||
Receiver: r,
|
||||
Err: fmt.Errorf("notifier %s is not supported", r.Type),
|
||||
}
|
||||
}
|
||||
n, err := receiverFactory(factoryConfig)
|
||||
if err != nil {
|
||||
return nil, InvalidReceiverError{
|
||||
Receiver: r,
|
||||
Err: err,
|
||||
}
|
||||
}
|
||||
|
||||
return n, nil
|
||||
}
|
||||
|
||||
|
@ -3,6 +3,7 @@ package channels
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
@ -17,49 +18,64 @@ import (
|
||||
// the given key. If the key is not present, then it returns the fallback value.
|
||||
type GetDecryptedValueFn func(ctx context.Context, sjd map[string][]byte, key string, fallback string) string
|
||||
|
||||
// NewAlertmanagerNotifier returns a new Alertmanager notifier.
|
||||
func NewAlertmanagerNotifier(model *NotificationChannelConfig, _ *template.Template, fn GetDecryptedValueFn) (*AlertmanagerNotifier, error) {
|
||||
if model.Settings == nil {
|
||||
return nil, receiverInitError{Reason: "no settings supplied"}
|
||||
}
|
||||
if model.SecureSettings == nil {
|
||||
return nil, receiverInitError{Cfg: *model, Reason: "no secure settings supplied"}
|
||||
}
|
||||
urlStr := model.Settings.Get("url").MustString()
|
||||
if urlStr == "" {
|
||||
return nil, receiverInitError{Reason: "could not find url property in settings", Cfg: *model}
|
||||
}
|
||||
type AlertmanagerConfig struct {
|
||||
*NotificationChannelConfig
|
||||
URLs []*url.URL
|
||||
BasicAuthUser string
|
||||
BasicAuthPassword string
|
||||
}
|
||||
|
||||
func NewAlertmanagerConfig(config *NotificationChannelConfig, fn GetDecryptedValueFn) (*AlertmanagerConfig, error) {
|
||||
urlStr := config.Settings.Get("url").MustString()
|
||||
if urlStr == "" {
|
||||
return nil, errors.New("could not find url property in settings")
|
||||
}
|
||||
var urls []*url.URL
|
||||
for _, uS := range strings.Split(urlStr, ",") {
|
||||
uS = strings.TrimSpace(uS)
|
||||
if uS == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
uS = strings.TrimSuffix(uS, "/") + "/api/v1/alerts"
|
||||
u, err := url.Parse(uS)
|
||||
url, err := url.Parse(uS)
|
||||
if err != nil {
|
||||
return nil, receiverInitError{Reason: "invalid url property in settings", Cfg: *model, Err: err}
|
||||
return nil, fmt.Errorf("invalid url property in settings: %w", err)
|
||||
}
|
||||
|
||||
urls = append(urls, u)
|
||||
urls = append(urls, url)
|
||||
}
|
||||
basicAuthUser := model.Settings.Get("basicAuthUser").MustString()
|
||||
basicAuthPassword := fn(context.Background(), model.SecureSettings, "basicAuthPassword", model.Settings.Get("basicAuthPassword").MustString())
|
||||
return &AlertmanagerConfig{
|
||||
NotificationChannelConfig: config,
|
||||
URLs: urls,
|
||||
BasicAuthUser: config.Settings.Get("basicAuthUser").MustString(),
|
||||
BasicAuthPassword: fn(context.Background(), config.SecureSettings, "basicAuthPassword", config.Settings.Get("basicAuthPassword").MustString()),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func AlertmanagerFactory(fc FactoryConfig) (NotificationChannel, error) {
|
||||
config, err := NewAlertmanagerConfig(fc.Config, fc.DecryptFunc)
|
||||
if err != nil {
|
||||
return nil, receiverInitError{
|
||||
Reason: err.Error(),
|
||||
Cfg: *fc.Config,
|
||||
}
|
||||
}
|
||||
return NewAlertmanagerNotifier(config, nil, fc.DecryptFunc), nil
|
||||
}
|
||||
|
||||
// NewAlertmanagerNotifier returns a new Alertmanager notifier.
|
||||
func NewAlertmanagerNotifier(config *AlertmanagerConfig, _ *template.Template, fn GetDecryptedValueFn) *AlertmanagerNotifier {
|
||||
return &AlertmanagerNotifier{
|
||||
Base: NewBase(&models.AlertNotification{
|
||||
Uid: model.UID,
|
||||
Name: model.Name,
|
||||
DisableResolveMessage: model.DisableResolveMessage,
|
||||
Settings: model.Settings,
|
||||
Uid: config.UID,
|
||||
Name: config.Name,
|
||||
DisableResolveMessage: config.DisableResolveMessage,
|
||||
Settings: config.Settings,
|
||||
}),
|
||||
urls: urls,
|
||||
basicAuthUser: basicAuthUser,
|
||||
basicAuthPassword: basicAuthPassword,
|
||||
urls: config.URLs,
|
||||
basicAuthUser: config.BasicAuthUser,
|
||||
basicAuthPassword: config.BasicAuthPassword,
|
||||
logger: log.New("alerting.notifier.prometheus-alertmanager"),
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
// AlertmanagerNotifier sends alert notifications to the alert manager
|
||||
|
@ -35,13 +35,13 @@ func TestNewAlertmanagerNotifier(t *testing.T) {
|
||||
{
|
||||
name: "Error in initing: missing URL",
|
||||
settings: `{}`,
|
||||
expectedInitError: `failed to validate receiver of type "alertmanager": could not find url property in settings`,
|
||||
expectedInitError: `could not find url property in settings`,
|
||||
}, {
|
||||
name: "Error in initing: invalid URL",
|
||||
settings: `{
|
||||
"url": "://alertmanager.com"
|
||||
}`,
|
||||
expectedInitError: `failed to validate receiver "Alertmanager" of type "alertmanager": invalid url property in settings: parse "://alertmanager.com/api/v1/alerts": missing protocol scheme`,
|
||||
expectedInitError: `invalid url property in settings: parse "://alertmanager.com/api/v1/alerts": missing protocol scheme`,
|
||||
receiverName: "Alertmanager",
|
||||
},
|
||||
}
|
||||
@ -53,19 +53,20 @@ func TestNewAlertmanagerNotifier(t *testing.T) {
|
||||
|
||||
m := &NotificationChannelConfig{
|
||||
Name: c.receiverName,
|
||||
Type: "alertmanager",
|
||||
Type: "prometheus-alertmanager",
|
||||
Settings: settingsJSON,
|
||||
SecureSettings: secureSettings,
|
||||
}
|
||||
|
||||
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
|
||||
decryptFn := secretsService.GetDecryptedValue
|
||||
sn, err := NewAlertmanagerNotifier(m, tmpl, decryptFn)
|
||||
cfg, err := NewAlertmanagerConfig(m, decryptFn)
|
||||
if c.expectedInitError != "" {
|
||||
require.Equal(t, c.expectedInitError, err.Error())
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
sn := NewAlertmanagerNotifier(cfg, tmpl, decryptFn)
|
||||
require.NotNil(t, sn)
|
||||
})
|
||||
}
|
||||
@ -136,16 +137,16 @@ func TestAlertmanagerNotifier_Notify(t *testing.T) {
|
||||
|
||||
m := &NotificationChannelConfig{
|
||||
Name: c.receiverName,
|
||||
Type: "alertmanager",
|
||||
Type: "prometheus-alertmanager",
|
||||
Settings: settingsJSON,
|
||||
SecureSettings: secureSettings,
|
||||
}
|
||||
|
||||
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
|
||||
decryptFn := secretsService.GetDecryptedValue
|
||||
sn, err := NewAlertmanagerNotifier(m, tmpl, decryptFn)
|
||||
cfg, err := NewAlertmanagerConfig(m, decryptFn)
|
||||
require.NoError(t, err)
|
||||
|
||||
sn := NewAlertmanagerNotifier(cfg, tmpl, decryptFn)
|
||||
var body []byte
|
||||
origSendHTTPRequest := sendHTTPRequest
|
||||
t.Cleanup(func() {
|
||||
|
@ -3,6 +3,7 @@ package channels
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
|
||||
@ -16,34 +17,53 @@ import (
|
||||
|
||||
const defaultDingdingMsgType = "link"
|
||||
|
||||
// NewDingDingNotifier is the constructor for the Dingding notifier
|
||||
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"}
|
||||
}
|
||||
type DingDingConfig struct {
|
||||
*NotificationChannelConfig
|
||||
MsgType string
|
||||
Message string
|
||||
URL string
|
||||
}
|
||||
|
||||
url := model.Settings.Get("url").MustString()
|
||||
func NewDingDingConfig(config *NotificationChannelConfig) (*DingDingConfig, error) {
|
||||
url := config.Settings.Get("url").MustString()
|
||||
if url == "" {
|
||||
return nil, receiverInitError{Reason: "could not find url property in settings", Cfg: *model}
|
||||
return nil, errors.New("could not find url property in settings")
|
||||
}
|
||||
return &DingDingConfig{
|
||||
NotificationChannelConfig: config,
|
||||
MsgType: config.Settings.Get("msgType").MustString(defaultDingdingMsgType),
|
||||
Message: config.Settings.Get("message").MustString(`{{ template "default.message" .}}`),
|
||||
URL: config.Settings.Get("url").MustString(),
|
||||
}, nil
|
||||
}
|
||||
func DingDingFactory(fc FactoryConfig) (NotificationChannel, error) {
|
||||
cfg, err := NewDingDingConfig(fc.Config)
|
||||
if err != nil {
|
||||
return nil, receiverInitError{
|
||||
Reason: err.Error(),
|
||||
Cfg: *fc.Config,
|
||||
}
|
||||
}
|
||||
return NewDingDingNotifier(cfg, fc.NotificationService, fc.Template), nil
|
||||
}
|
||||
|
||||
msgType := model.Settings.Get("msgType").MustString(defaultDingdingMsgType)
|
||||
|
||||
// NewDingDingNotifier is the constructor for the Dingding notifier
|
||||
func NewDingDingNotifier(config *DingDingConfig, ns notifications.WebhookSender, t *template.Template) *DingDingNotifier {
|
||||
return &DingDingNotifier{
|
||||
Base: NewBase(&models.AlertNotification{
|
||||
Uid: model.UID,
|
||||
Name: model.Name,
|
||||
Type: model.Type,
|
||||
DisableResolveMessage: model.DisableResolveMessage,
|
||||
Settings: model.Settings,
|
||||
Uid: config.UID,
|
||||
Name: config.Name,
|
||||
Type: config.Type,
|
||||
DisableResolveMessage: config.DisableResolveMessage,
|
||||
Settings: config.Settings,
|
||||
}),
|
||||
MsgType: msgType,
|
||||
URL: url,
|
||||
Message: model.Settings.Get("message").MustString(`{{ template "default.message" .}}`),
|
||||
MsgType: config.MsgType,
|
||||
Message: config.Message,
|
||||
URL: config.URL,
|
||||
log: log.New("alerting.notifier.dingding"),
|
||||
tmpl: t,
|
||||
ns: ns,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
// DingDingNotifier is responsible for sending alert notifications to ding ding.
|
||||
|
@ -82,7 +82,7 @@ func TestDingdingNotifier(t *testing.T) {
|
||||
}, {
|
||||
name: "Error in initing",
|
||||
settings: `{}`,
|
||||
expInitError: `failed to validate receiver "dingding_testing" of type "dingding": could not find url property in settings`,
|
||||
expInitError: `could not find url property in settings`,
|
||||
},
|
||||
}
|
||||
|
||||
@ -98,7 +98,7 @@ func TestDingdingNotifier(t *testing.T) {
|
||||
}
|
||||
|
||||
webhookSender := mockNotificationService()
|
||||
pn, err := NewDingDingNotifier(m, webhookSender, tmpl)
|
||||
cfg, err := NewDingDingConfig(m)
|
||||
if c.expInitError != "" {
|
||||
require.Equal(t, c.expInitError, err.Error())
|
||||
return
|
||||
@ -107,6 +107,7 @@ func TestDingdingNotifier(t *testing.T) {
|
||||
|
||||
ctx := notify.WithGroupKey(context.Background(), "alertname")
|
||||
ctx = notify.WithGroupLabels(ctx, model.LabelSet{"alertname": ""})
|
||||
pn := NewDingDingNotifier(cfg, webhookSender, tmpl)
|
||||
ok, err := pn.Notify(ctx, c.alerts...)
|
||||
if c.expMsgError != nil {
|
||||
require.False(t, ok)
|
||||
|
@ -3,6 +3,7 @@ package channels
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
@ -27,39 +28,57 @@ type DiscordNotifier struct {
|
||||
UseDiscordUsername bool
|
||||
}
|
||||
|
||||
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"}
|
||||
}
|
||||
type DiscordConfig struct {
|
||||
*NotificationChannelConfig
|
||||
Content string
|
||||
AvatarURL string
|
||||
WebhookURL string
|
||||
UseDiscordUsername bool
|
||||
}
|
||||
|
||||
avatarURL := model.Settings.Get("avatar_url").MustString()
|
||||
|
||||
discordURL := model.Settings.Get("url").MustString()
|
||||
func NewDiscordConfig(config *NotificationChannelConfig) (*DiscordConfig, error) {
|
||||
discordURL := config.Settings.Get("url").MustString()
|
||||
if discordURL == "" {
|
||||
return nil, receiverInitError{Reason: "could not find webhook url property in settings", Cfg: *model}
|
||||
return nil, errors.New("could not find webhook url property in settings")
|
||||
}
|
||||
return &DiscordConfig{
|
||||
NotificationChannelConfig: config,
|
||||
Content: config.Settings.Get("message").MustString(`{{ template "default.message" . }}`),
|
||||
AvatarURL: config.Settings.Get("avatar_url").MustString(),
|
||||
WebhookURL: discordURL,
|
||||
UseDiscordUsername: config.Settings.Get("use_discord_username").MustBool(false),
|
||||
}, nil
|
||||
}
|
||||
|
||||
useDiscordUsername := model.Settings.Get("use_discord_username").MustBool(false)
|
||||
|
||||
content := model.Settings.Get("message").MustString(`{{ template "default.message" . }}`)
|
||||
func DiscrodFactory(fc FactoryConfig) (NotificationChannel, error) {
|
||||
cfg, err := NewDiscordConfig(fc.Config)
|
||||
if err != nil {
|
||||
return nil, receiverInitError{
|
||||
Reason: err.Error(),
|
||||
Cfg: *fc.Config,
|
||||
}
|
||||
}
|
||||
return NewDiscordNotifier(cfg, fc.NotificationService, fc.Template), nil
|
||||
}
|
||||
|
||||
func NewDiscordNotifier(config *DiscordConfig, ns notifications.WebhookSender, t *template.Template) *DiscordNotifier {
|
||||
return &DiscordNotifier{
|
||||
Base: NewBase(&models.AlertNotification{
|
||||
Uid: model.UID,
|
||||
Name: model.Name,
|
||||
Type: model.Type,
|
||||
DisableResolveMessage: model.DisableResolveMessage,
|
||||
Settings: model.Settings,
|
||||
SecureSettings: model.SecureSettings,
|
||||
Uid: config.UID,
|
||||
Name: config.Name,
|
||||
Type: config.Type,
|
||||
DisableResolveMessage: config.DisableResolveMessage,
|
||||
Settings: config.Settings,
|
||||
SecureSettings: config.SecureSettings,
|
||||
}),
|
||||
Content: content,
|
||||
AvatarURL: avatarURL,
|
||||
WebhookURL: discordURL,
|
||||
Content: config.Content,
|
||||
AvatarURL: config.AvatarURL,
|
||||
WebhookURL: config.WebhookURL,
|
||||
log: log.New("alerting.notifier.discord"),
|
||||
ns: ns,
|
||||
tmpl: t,
|
||||
UseDiscordUsername: useDiscordUsername,
|
||||
}, nil
|
||||
UseDiscordUsername: config.UseDiscordUsername,
|
||||
}
|
||||
}
|
||||
|
||||
func (d DiscordNotifier) Notify(ctx context.Context, as ...*types.Alert) (bool, error) {
|
||||
|
@ -98,7 +98,7 @@ func TestDiscordNotifier(t *testing.T) {
|
||||
{
|
||||
name: "Error in initialization",
|
||||
settings: `{}`,
|
||||
expInitError: `failed to validate receiver "discord_testing" of type "discord": could not find webhook url property in settings`,
|
||||
expInitError: `could not find webhook url property in settings`,
|
||||
},
|
||||
{
|
||||
name: "Invalid template returns error",
|
||||
@ -151,7 +151,7 @@ func TestDiscordNotifier(t *testing.T) {
|
||||
}
|
||||
|
||||
webhookSender := mockNotificationService()
|
||||
dn, err := NewDiscordNotifier(m, webhookSender, tmpl)
|
||||
cfg, err := NewDiscordConfig(m)
|
||||
if c.expInitError != "" {
|
||||
require.Equal(t, c.expInitError, err.Error())
|
||||
return
|
||||
@ -160,6 +160,7 @@ func TestDiscordNotifier(t *testing.T) {
|
||||
|
||||
ctx := notify.WithGroupKey(context.Background(), "alertname")
|
||||
ctx = notify.WithGroupLabels(ctx, model.LabelSet{"alertname": ""})
|
||||
dn := NewDiscordNotifier(cfg, webhookSender, tmpl)
|
||||
ok, err := dn.Notify(ctx, c.alerts...)
|
||||
if c.expMsgError != nil {
|
||||
require.False(t, ok)
|
||||
|
@ -2,6 +2,7 @@ package channels
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net/url"
|
||||
"path"
|
||||
|
||||
@ -26,38 +27,57 @@ type EmailNotifier struct {
|
||||
tmpl *template.Template
|
||||
}
|
||||
|
||||
// NewEmailNotifier is the constructor function
|
||||
// for the EmailNotifier.
|
||||
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"}
|
||||
type EmailConfig struct {
|
||||
*NotificationChannelConfig
|
||||
SingleEmail bool
|
||||
Addresses []string
|
||||
Message string
|
||||
}
|
||||
|
||||
func EmailFactory(fc FactoryConfig) (NotificationChannel, error) {
|
||||
cfg, err := NewEmailConfig(fc.Config)
|
||||
if err != nil {
|
||||
return nil, receiverInitError{
|
||||
Reason: err.Error(),
|
||||
Cfg: *fc.Config,
|
||||
}
|
||||
}
|
||||
return NewEmailNotifier(cfg, fc.NotificationService, fc.Template), nil
|
||||
}
|
||||
|
||||
addressesString := model.Settings.Get("addresses").MustString()
|
||||
singleEmail := model.Settings.Get("singleEmail").MustBool(false)
|
||||
|
||||
func NewEmailConfig(config *NotificationChannelConfig) (*EmailConfig, error) {
|
||||
addressesString := config.Settings.Get("addresses").MustString()
|
||||
if addressesString == "" {
|
||||
return nil, receiverInitError{Reason: "could not find addresses in settings", Cfg: *model}
|
||||
return nil, errors.New("could not find addresses in settings")
|
||||
}
|
||||
|
||||
// split addresses with a few different ways
|
||||
addresses := util.SplitEmails(addressesString)
|
||||
return &EmailConfig{
|
||||
NotificationChannelConfig: config,
|
||||
SingleEmail: config.Settings.Get("singleEmail").MustBool(false),
|
||||
Message: config.Settings.Get("message").MustString(),
|
||||
Addresses: addresses,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// NewEmailNotifier is the constructor function
|
||||
// for the EmailNotifier.
|
||||
func NewEmailNotifier(config *EmailConfig, ns notifications.EmailSender, t *template.Template) *EmailNotifier {
|
||||
return &EmailNotifier{
|
||||
Base: NewBase(&models.AlertNotification{
|
||||
Uid: model.UID,
|
||||
Name: model.Name,
|
||||
Type: model.Type,
|
||||
DisableResolveMessage: model.DisableResolveMessage,
|
||||
Settings: model.Settings,
|
||||
Uid: config.UID,
|
||||
Name: config.Name,
|
||||
Type: config.Type,
|
||||
DisableResolveMessage: config.DisableResolveMessage,
|
||||
Settings: config.Settings,
|
||||
}),
|
||||
Addresses: addresses,
|
||||
SingleEmail: singleEmail,
|
||||
Message: model.Settings.Get("message").MustString(),
|
||||
Addresses: config.Addresses,
|
||||
SingleEmail: config.SingleEmail,
|
||||
Message: config.Message,
|
||||
log: log.New("alerting.notifier.email"),
|
||||
ns: ns,
|
||||
tmpl: t,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Notify sends the alert notification.
|
||||
|
@ -33,7 +33,7 @@ func TestEmailNotifier(t *testing.T) {
|
||||
Settings: settingsJSON,
|
||||
}
|
||||
|
||||
_, err := NewEmailNotifier(model, nil, tmpl)
|
||||
_, err := NewEmailConfig(model)
|
||||
require.Error(t, err)
|
||||
})
|
||||
|
||||
@ -46,14 +46,13 @@ func TestEmailNotifier(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
emailSender := mockNotificationService()
|
||||
|
||||
emailNotifier, err := NewEmailNotifier(&NotificationChannelConfig{
|
||||
cfg, err := NewEmailConfig(&NotificationChannelConfig{
|
||||
Name: "ops",
|
||||
Type: "email",
|
||||
Settings: settingsJSON,
|
||||
}, emailSender, tmpl)
|
||||
|
||||
})
|
||||
require.NoError(t, err)
|
||||
emailNotifier := NewEmailNotifier(cfg, emailSender, tmpl)
|
||||
|
||||
alerts := []*types.Alert{
|
||||
{
|
||||
@ -284,13 +283,13 @@ func createSut(t *testing.T, messageTmpl string, emailTmpl *template.Template, n
|
||||
settingsJSON.Set("message", messageTmpl)
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
emailNotifier, err := NewEmailNotifier(&NotificationChannelConfig{
|
||||
cfg, err := NewEmailConfig(&NotificationChannelConfig{
|
||||
Name: "ops",
|
||||
Type: "email",
|
||||
Settings: settingsJSON,
|
||||
}, ns, emailTmpl)
|
||||
})
|
||||
require.NoError(t, err)
|
||||
emailNotifier := NewEmailNotifier(cfg, ns, emailTmpl)
|
||||
|
||||
return emailNotifier
|
||||
}
|
||||
|
61
pkg/services/ngalert/notifier/channels/factory.go
Normal file
61
pkg/services/ngalert/notifier/channels/factory.go
Normal file
@ -0,0 +1,61 @@
|
||||
package channels
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
"github.com/grafana/grafana/pkg/services/notifications"
|
||||
"github.com/prometheus/alertmanager/template"
|
||||
)
|
||||
|
||||
type FactoryConfig struct {
|
||||
Config *NotificationChannelConfig
|
||||
NotificationService notifications.Service
|
||||
DecryptFunc GetDecryptedValueFn
|
||||
Template *template.Template
|
||||
}
|
||||
|
||||
func NewFactoryConfig(config *NotificationChannelConfig, notificationService notifications.Service,
|
||||
decryptFunc GetDecryptedValueFn, template *template.Template) (FactoryConfig, error) {
|
||||
if config.Settings == nil {
|
||||
return FactoryConfig{}, errors.New("no settings supplied")
|
||||
}
|
||||
// not all receivers do need secure settings, we still might interact with
|
||||
// them, so we make sure they are never nil
|
||||
if config.SecureSettings == nil {
|
||||
config.SecureSettings = map[string][]byte{}
|
||||
}
|
||||
return FactoryConfig{
|
||||
Config: config,
|
||||
NotificationService: notificationService,
|
||||
DecryptFunc: decryptFunc,
|
||||
Template: template,
|
||||
}, nil
|
||||
}
|
||||
|
||||
var receiverFactories = map[string]func(FactoryConfig) (NotificationChannel, error){
|
||||
"prometheus-alertmanager": AlertmanagerFactory,
|
||||
"dingding": DingDingFactory,
|
||||
"discord": DiscrodFactory,
|
||||
"email": EmailFactory,
|
||||
"googlechat": GoogleChatFactory,
|
||||
"kafka": KafkaFactory,
|
||||
"line": LineFactory,
|
||||
"opsgenie": OpsgenieFactory,
|
||||
"pagerduty": PagerdutyFactory,
|
||||
"pushover": PushoverFactory,
|
||||
"sensugo": SensuGoFactory,
|
||||
"slack": SlackFactory,
|
||||
"teams": TeamsFactory,
|
||||
"telegram": TelegramFactory,
|
||||
"threema": ThreemaFactory,
|
||||
"victorops": VictorOpsFactory,
|
||||
"webhook": WebHookFactory,
|
||||
"wecom": WeComFactory,
|
||||
}
|
||||
|
||||
func Factory(receiverType string) (func(FactoryConfig) (NotificationChannel, error), bool) {
|
||||
receiverType = strings.ToLower(receiverType)
|
||||
factory, exists := receiverFactories[receiverType]
|
||||
return factory, exists
|
||||
}
|
@ -3,6 +3,7 @@ package channels
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
@ -26,32 +27,50 @@ type GoogleChatNotifier struct {
|
||||
content string
|
||||
}
|
||||
|
||||
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"}
|
||||
}
|
||||
type GoogleChatConfig struct {
|
||||
*NotificationChannelConfig
|
||||
URL string
|
||||
Content string
|
||||
}
|
||||
|
||||
url := model.Settings.Get("url").MustString()
|
||||
func GoogleChatFactory(fc FactoryConfig) (NotificationChannel, error) {
|
||||
cfg, err := NewGoogleChatConfig(fc.Config)
|
||||
if err != nil {
|
||||
return nil, receiverInitError{
|
||||
Reason: err.Error(),
|
||||
Cfg: *fc.Config,
|
||||
}
|
||||
}
|
||||
return NewGoogleChatNotifier(cfg, fc.NotificationService, fc.Template), nil
|
||||
}
|
||||
|
||||
func NewGoogleChatConfig(config *NotificationChannelConfig) (*GoogleChatConfig, error) {
|
||||
url := config.Settings.Get("url").MustString()
|
||||
if url == "" {
|
||||
return nil, receiverInitError{Cfg: *model, Reason: "could not find url property in settings"}
|
||||
return nil, errors.New("could not find url property in settings")
|
||||
}
|
||||
return &GoogleChatConfig{
|
||||
NotificationChannelConfig: config,
|
||||
URL: url,
|
||||
Content: config.Settings.Get("message").MustString(`{{ template "default.message" . }}`),
|
||||
}, nil
|
||||
}
|
||||
|
||||
content := model.Settings.Get("message").MustString(`{{ template "default.message" . }}`)
|
||||
|
||||
func NewGoogleChatNotifier(config *GoogleChatConfig, ns notifications.WebhookSender, t *template.Template) *GoogleChatNotifier {
|
||||
return &GoogleChatNotifier{
|
||||
Base: NewBase(&models.AlertNotification{
|
||||
Uid: model.UID,
|
||||
Name: model.Name,
|
||||
Type: model.Type,
|
||||
DisableResolveMessage: model.DisableResolveMessage,
|
||||
Settings: model.Settings,
|
||||
Uid: config.UID,
|
||||
Name: config.Name,
|
||||
Type: config.Type,
|
||||
DisableResolveMessage: config.DisableResolveMessage,
|
||||
Settings: config.Settings,
|
||||
}),
|
||||
URL: url,
|
||||
content: config.Content,
|
||||
URL: config.URL,
|
||||
log: log.New("alerting.notifier.googlechat"),
|
||||
ns: ns,
|
||||
tmpl: t,
|
||||
content: content,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Notify send an alert notification to Google Chat.
|
||||
|
@ -149,7 +149,7 @@ func TestGoogleChatNotifier(t *testing.T) {
|
||||
}, {
|
||||
name: "Error in initing",
|
||||
settings: `{}`,
|
||||
expInitError: `failed to validate receiver "googlechat_testing" of type "googlechat": could not find url property in settings`,
|
||||
expInitError: `could not find url property in settings`,
|
||||
}, {
|
||||
name: "Customized message",
|
||||
settings: `{"url": "http://localhost", "message": "I'm a custom template and you have {{ len .Alerts.Firing }} firing alert."}`,
|
||||
@ -273,7 +273,7 @@ func TestGoogleChatNotifier(t *testing.T) {
|
||||
}
|
||||
|
||||
webhookSender := mockNotificationService()
|
||||
pn, err := NewGoogleChatNotifier(m, webhookSender, tmpl)
|
||||
cfg, err := NewGoogleChatConfig(m)
|
||||
if c.expInitError != "" {
|
||||
require.Error(t, err)
|
||||
require.Equal(t, c.expInitError, err.Error())
|
||||
@ -283,6 +283,7 @@ func TestGoogleChatNotifier(t *testing.T) {
|
||||
|
||||
ctx := notify.WithGroupKey(context.Background(), "alertname")
|
||||
ctx = notify.WithGroupLabels(ctx, model.LabelSet{"alertname": ""})
|
||||
pn := NewGoogleChatNotifier(cfg, webhookSender, tmpl)
|
||||
ok, err := pn.Notify(ctx, c.alerts...)
|
||||
if c.expMsgError != nil {
|
||||
require.False(t, ok)
|
||||
|
@ -2,6 +2,7 @@ package channels
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
"github.com/prometheus/alertmanager/notify"
|
||||
@ -26,35 +27,55 @@ type KafkaNotifier struct {
|
||||
tmpl *template.Template
|
||||
}
|
||||
|
||||
// NewKafkaNotifier is the constructor function for the Kafka notifier.
|
||||
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"}
|
||||
}
|
||||
type KafkaConfig struct {
|
||||
*NotificationChannelConfig
|
||||
Endpoint string
|
||||
Topic string
|
||||
}
|
||||
|
||||
endpoint := model.Settings.Get("kafkaRestProxy").MustString()
|
||||
func KafkaFactory(fc FactoryConfig) (NotificationChannel, error) {
|
||||
cfg, err := NewKafkaConfig(fc.Config)
|
||||
if err != nil {
|
||||
return nil, receiverInitError{
|
||||
Reason: err.Error(),
|
||||
Cfg: *fc.Config,
|
||||
}
|
||||
}
|
||||
return NewKafkaNotifier(cfg, fc.NotificationService, fc.Template), nil
|
||||
}
|
||||
|
||||
func NewKafkaConfig(config *NotificationChannelConfig) (*KafkaConfig, error) {
|
||||
endpoint := config.Settings.Get("kafkaRestProxy").MustString()
|
||||
if endpoint == "" {
|
||||
return nil, receiverInitError{Cfg: *model, Reason: "could not find kafka rest proxy endpoint property in settings"}
|
||||
return nil, errors.New("could not find kafka rest proxy endpoint property in settings")
|
||||
}
|
||||
topic := model.Settings.Get("kafkaTopic").MustString()
|
||||
topic := config.Settings.Get("kafkaTopic").MustString()
|
||||
if topic == "" {
|
||||
return nil, receiverInitError{Cfg: *model, Reason: "could not find kafka topic property in settings"}
|
||||
return nil, errors.New("could not find kafka topic property in settings")
|
||||
}
|
||||
return &KafkaConfig{
|
||||
NotificationChannelConfig: config,
|
||||
Endpoint: endpoint,
|
||||
Topic: topic,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// NewKafkaNotifier is the constructor function for the Kafka notifier.
|
||||
func NewKafkaNotifier(config *KafkaConfig, ns notifications.WebhookSender, t *template.Template) *KafkaNotifier {
|
||||
return &KafkaNotifier{
|
||||
Base: NewBase(&models.AlertNotification{
|
||||
Uid: model.UID,
|
||||
Name: model.Name,
|
||||
Type: model.Type,
|
||||
DisableResolveMessage: model.DisableResolveMessage,
|
||||
Settings: model.Settings,
|
||||
Uid: config.UID,
|
||||
Name: config.Name,
|
||||
Type: config.Type,
|
||||
DisableResolveMessage: config.DisableResolveMessage,
|
||||
Settings: config.Settings,
|
||||
}),
|
||||
Endpoint: endpoint,
|
||||
Topic: topic,
|
||||
Endpoint: config.Endpoint,
|
||||
Topic: config.Topic,
|
||||
log: log.New("alerting.notifier.kafka"),
|
||||
ns: ns,
|
||||
tmpl: t,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Notify sends the alert notification.
|
||||
|
@ -96,11 +96,11 @@ func TestKafkaNotifier(t *testing.T) {
|
||||
}, {
|
||||
name: "Endpoint missing",
|
||||
settings: `{"kafkaTopic": "sometopic"}`,
|
||||
expInitError: `failed to validate receiver "kafka_testing" of type "kafka": could not find kafka rest proxy endpoint property in settings`,
|
||||
expInitError: `could not find kafka rest proxy endpoint property in settings`,
|
||||
}, {
|
||||
name: "Topic missing",
|
||||
settings: `{"kafkaRestProxy": "http://localhost"}`,
|
||||
expInitError: `failed to validate receiver "kafka_testing" of type "kafka": could not find kafka topic property in settings`,
|
||||
expInitError: `could not find kafka topic property in settings`,
|
||||
},
|
||||
}
|
||||
|
||||
@ -116,7 +116,7 @@ func TestKafkaNotifier(t *testing.T) {
|
||||
}
|
||||
|
||||
webhookSender := mockNotificationService()
|
||||
pn, err := NewKafkaNotifier(m, webhookSender, tmpl)
|
||||
cfg, err := NewKafkaConfig(m)
|
||||
if c.expInitError != "" {
|
||||
require.Error(t, err)
|
||||
require.Equal(t, c.expInitError, err.Error())
|
||||
@ -126,6 +126,8 @@ func TestKafkaNotifier(t *testing.T) {
|
||||
|
||||
ctx := notify.WithGroupKey(context.Background(), "alertname")
|
||||
ctx = notify.WithGroupLabels(ctx, model.LabelSet{"alertname": ""})
|
||||
|
||||
pn := NewKafkaNotifier(cfg, webhookSender, tmpl)
|
||||
ok, err := pn.Notify(ctx, c.alerts...)
|
||||
if c.expMsgError != nil {
|
||||
require.False(t, ok)
|
||||
|
@ -2,6 +2,7 @@ package channels
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"path"
|
||||
@ -17,33 +18,48 @@ var (
|
||||
LineNotifyURL string = "https://notify-api.line.me/api/notify"
|
||||
)
|
||||
|
||||
// NewLineNotifier is the constructor for the LINE notifier
|
||||
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"}
|
||||
}
|
||||
if model.SecureSettings == nil {
|
||||
return nil, receiverInitError{Cfg: *model, Reason: "no secure settings supplied"}
|
||||
}
|
||||
type LineConfig struct {
|
||||
*NotificationChannelConfig
|
||||
Token string
|
||||
}
|
||||
|
||||
token := fn(context.Background(), model.SecureSettings, "token", model.Settings.Get("token").MustString())
|
||||
func LineFactory(fc FactoryConfig) (NotificationChannel, error) {
|
||||
cfg, err := NewLineConfig(fc.Config, fc.DecryptFunc)
|
||||
if err != nil {
|
||||
return nil, receiverInitError{
|
||||
Reason: err.Error(),
|
||||
Cfg: *fc.Config,
|
||||
}
|
||||
}
|
||||
return NewLineNotifier(cfg, fc.NotificationService, fc.Template), nil
|
||||
}
|
||||
|
||||
func NewLineConfig(config *NotificationChannelConfig, decryptFunc GetDecryptedValueFn) (*LineConfig, error) {
|
||||
token := decryptFunc(context.Background(), config.SecureSettings, "token", config.Settings.Get("token").MustString())
|
||||
if token == "" {
|
||||
return nil, receiverInitError{Cfg: *model, Reason: "could not find token in settings"}
|
||||
return nil, errors.New("could not find token in settings")
|
||||
}
|
||||
return &LineConfig{
|
||||
NotificationChannelConfig: config,
|
||||
Token: token,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// NewLineNotifier is the constructor for the LINE notifier
|
||||
func NewLineNotifier(config *LineConfig, ns notifications.WebhookSender, t *template.Template) *LineNotifier {
|
||||
return &LineNotifier{
|
||||
Base: NewBase(&models.AlertNotification{
|
||||
Uid: model.UID,
|
||||
Name: model.Name,
|
||||
Type: model.Type,
|
||||
DisableResolveMessage: model.DisableResolveMessage,
|
||||
Settings: model.Settings,
|
||||
Uid: config.UID,
|
||||
Name: config.Name,
|
||||
Type: config.Type,
|
||||
DisableResolveMessage: config.DisableResolveMessage,
|
||||
Settings: config.Settings,
|
||||
}),
|
||||
Token: token,
|
||||
Token: config.Token,
|
||||
log: log.New("alerting.notifier.line"),
|
||||
ns: ns,
|
||||
tmpl: t,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
// LineNotifier is responsible for sending
|
||||
|
@ -73,7 +73,7 @@ func TestLineNotifier(t *testing.T) {
|
||||
}, {
|
||||
name: "Token missing",
|
||||
settings: `{}`,
|
||||
expInitError: `failed to validate receiver "line_testing" of type "line": could not find token in settings`,
|
||||
expInitError: `could not find token in settings`,
|
||||
},
|
||||
}
|
||||
|
||||
@ -93,7 +93,7 @@ func TestLineNotifier(t *testing.T) {
|
||||
webhookSender := mockNotificationService()
|
||||
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
|
||||
decryptFn := secretsService.GetDecryptedValue
|
||||
pn, err := NewLineNotifier(m, webhookSender, tmpl, decryptFn)
|
||||
cfg, err := NewLineConfig(m, decryptFn)
|
||||
if c.expInitError != "" {
|
||||
require.Error(t, err)
|
||||
require.Equal(t, c.expInitError, err.Error())
|
||||
@ -103,6 +103,7 @@ func TestLineNotifier(t *testing.T) {
|
||||
|
||||
ctx := notify.WithGroupKey(context.Background(), "alertname")
|
||||
ctx = notify.WithGroupLabels(ctx, model.LabelSet{"alertname": ""})
|
||||
pn := NewLineNotifier(cfg, webhookSender, tmpl)
|
||||
ok, err := pn.Notify(ctx, c.alerts...)
|
||||
if c.expMsgError != nil {
|
||||
require.False(t, ok)
|
||||
|
@ -3,6 +3,7 @@ package channels
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"sort"
|
||||
@ -41,49 +42,66 @@ type OpsgenieNotifier struct {
|
||||
ns notifications.WebhookSender
|
||||
}
|
||||
|
||||
// NewOpsgenieNotifier is the constructor for the Opsgenie notifier
|
||||
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"}
|
||||
}
|
||||
if model.SecureSettings == nil {
|
||||
return nil, receiverInitError{Cfg: *model, Reason: "no secure settings supplied"}
|
||||
}
|
||||
autoClose := model.Settings.Get("autoClose").MustBool(true)
|
||||
overridePriority := model.Settings.Get("overridePriority").MustBool(true)
|
||||
apiKey := fn(context.Background(), model.SecureSettings, "apiKey", model.Settings.Get("apiKey").MustString())
|
||||
apiURL := model.Settings.Get("apiUrl").MustString()
|
||||
if apiKey == "" {
|
||||
return nil, receiverInitError{Cfg: *model, Reason: "could not find api key property in settings"}
|
||||
}
|
||||
if apiURL == "" {
|
||||
apiURL = OpsgenieAlertURL
|
||||
}
|
||||
type OpsgenieConfig struct {
|
||||
*NotificationChannelConfig
|
||||
APIKey string
|
||||
APIUrl string
|
||||
AutoClose bool
|
||||
OverridePriority bool
|
||||
SendTagsAs string
|
||||
}
|
||||
|
||||
sendTagsAs := model.Settings.Get("sendTagsAs").MustString(OpsgenieSendTags)
|
||||
if sendTagsAs != OpsgenieSendTags && sendTagsAs != OpsgenieSendDetails && sendTagsAs != OpsgenieSendBoth {
|
||||
return nil, receiverInitError{Cfg: *model,
|
||||
Reason: fmt.Sprintf("invalid value for sendTagsAs: %q", sendTagsAs),
|
||||
func OpsgenieFactory(fc FactoryConfig) (NotificationChannel, error) {
|
||||
cfg, err := NewOpsgenieConfig(fc.Config, fc.DecryptFunc)
|
||||
if err != nil {
|
||||
return nil, receiverInitError{
|
||||
Reason: err.Error(),
|
||||
Cfg: *fc.Config,
|
||||
}
|
||||
}
|
||||
return NewOpsgenieNotifier(cfg, fc.NotificationService, fc.Template, fc.DecryptFunc), nil
|
||||
}
|
||||
|
||||
func NewOpsgenieConfig(config *NotificationChannelConfig, decryptFunc GetDecryptedValueFn) (*OpsgenieConfig, error) {
|
||||
apiKey := decryptFunc(context.Background(), config.SecureSettings, "apiKey", config.Settings.Get("apiKey").MustString())
|
||||
if apiKey == "" {
|
||||
return nil, errors.New("could not find api key property in settings")
|
||||
}
|
||||
sendTagsAs := config.Settings.Get("sendTagsAs").MustString(OpsgenieSendTags)
|
||||
if sendTagsAs != OpsgenieSendTags &&
|
||||
sendTagsAs != OpsgenieSendDetails &&
|
||||
sendTagsAs != OpsgenieSendBoth {
|
||||
return nil, fmt.Errorf("invalid value for sendTagsAs: %q", sendTagsAs)
|
||||
}
|
||||
return &OpsgenieConfig{
|
||||
NotificationChannelConfig: config,
|
||||
APIKey: apiKey,
|
||||
APIUrl: config.Settings.Get("apiUrl").MustString(OpsgenieAlertURL),
|
||||
AutoClose: config.Settings.Get("autoClose").MustBool(true),
|
||||
OverridePriority: config.Settings.Get("overridePriority").MustBool(true),
|
||||
SendTagsAs: sendTagsAs,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// NewOpsgenieNotifier is the constructor for the Opsgenie notifier
|
||||
func NewOpsgenieNotifier(config *OpsgenieConfig, ns notifications.WebhookSender, t *template.Template, fn GetDecryptedValueFn) *OpsgenieNotifier {
|
||||
return &OpsgenieNotifier{
|
||||
Base: NewBase(&models.AlertNotification{
|
||||
Uid: model.UID,
|
||||
Name: model.Name,
|
||||
Type: model.Type,
|
||||
DisableResolveMessage: model.DisableResolveMessage,
|
||||
Settings: model.Settings,
|
||||
Uid: config.UID,
|
||||
Name: config.Name,
|
||||
Type: config.Type,
|
||||
DisableResolveMessage: config.DisableResolveMessage,
|
||||
Settings: config.Settings,
|
||||
}),
|
||||
APIKey: apiKey,
|
||||
APIUrl: apiURL,
|
||||
AutoClose: autoClose,
|
||||
OverridePriority: overridePriority,
|
||||
SendTagsAs: sendTagsAs,
|
||||
APIKey: config.APIKey,
|
||||
APIUrl: config.APIUrl,
|
||||
AutoClose: config.AutoClose,
|
||||
OverridePriority: config.OverridePriority,
|
||||
SendTagsAs: config.SendTagsAs,
|
||||
tmpl: t,
|
||||
log: log.New("alerting.notifier." + model.Name),
|
||||
log: log.New("alerting.notifier." + config.Name),
|
||||
ns: ns,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Notify sends an alert notification to Opsgenie
|
||||
|
@ -153,7 +153,7 @@ func TestOpsgenieNotifier(t *testing.T) {
|
||||
{
|
||||
name: "Error when incorrect settings",
|
||||
settings: `{}`,
|
||||
expInitError: `failed to validate receiver "opsgenie_testing" of type "opsgenie": could not find api key property in settings`,
|
||||
expInitError: `could not find api key property in settings`,
|
||||
},
|
||||
}
|
||||
|
||||
@ -174,7 +174,7 @@ func TestOpsgenieNotifier(t *testing.T) {
|
||||
webhookSender.Webhook.Body = "<not-sent>"
|
||||
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
|
||||
decryptFn := secretsService.GetDecryptedValue
|
||||
pn, err := NewOpsgenieNotifier(m, webhookSender, tmpl, decryptFn)
|
||||
cfg, err := NewOpsgenieConfig(m, decryptFn)
|
||||
if c.expInitError != "" {
|
||||
require.Error(t, err)
|
||||
require.Equal(t, c.expInitError, err.Error())
|
||||
@ -184,6 +184,7 @@ func TestOpsgenieNotifier(t *testing.T) {
|
||||
|
||||
ctx := notify.WithGroupKey(context.Background(), "alertname")
|
||||
ctx = notify.WithGroupLabels(ctx, model.LabelSet{"alertname": ""})
|
||||
pn := NewOpsgenieNotifier(cfg, webhookSender, tmpl, decryptFn)
|
||||
ok, err := pn.Notify(ctx, c.alerts...)
|
||||
if c.expMsgError != nil {
|
||||
require.False(t, ok)
|
||||
|
@ -3,6 +3,7 @@ package channels
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
@ -40,44 +41,69 @@ type PagerdutyNotifier struct {
|
||||
ns notifications.WebhookSender
|
||||
}
|
||||
|
||||
// NewPagerdutyNotifier is the constructor for the PagerDuty notifier
|
||||
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"}
|
||||
}
|
||||
if model.SecureSettings == nil {
|
||||
return nil, receiverInitError{Cfg: *model, Reason: "no secure settings supplied"}
|
||||
}
|
||||
type PagerdutyConfig struct {
|
||||
*NotificationChannelConfig
|
||||
Key string
|
||||
Severity string
|
||||
Class string
|
||||
Component string
|
||||
Group string
|
||||
Summary string
|
||||
}
|
||||
|
||||
key := fn(context.Background(), model.SecureSettings, "integrationKey", model.Settings.Get("integrationKey").MustString())
|
||||
func PagerdutyFactory(fc FactoryConfig) (NotificationChannel, error) {
|
||||
cfg, err := NewPagerdutyConfig(fc.Config, fc.DecryptFunc)
|
||||
if err != nil {
|
||||
return nil, receiverInitError{
|
||||
Reason: err.Error(),
|
||||
Cfg: *fc.Config,
|
||||
}
|
||||
}
|
||||
return NewPagerdutyNotifier(cfg, fc.NotificationService, fc.Template), nil
|
||||
}
|
||||
|
||||
func NewPagerdutyConfig(config *NotificationChannelConfig, decryptFunc GetDecryptedValueFn) (*PagerdutyConfig, error) {
|
||||
key := decryptFunc(context.Background(), config.SecureSettings, "integrationKey", config.Settings.Get("integrationKey").MustString())
|
||||
if key == "" {
|
||||
return nil, receiverInitError{Cfg: *model, Reason: "could not find integration key property in settings"}
|
||||
return nil, errors.New("could not find integration key property in settings")
|
||||
}
|
||||
return &PagerdutyConfig{
|
||||
NotificationChannelConfig: config,
|
||||
Key: key,
|
||||
Severity: config.Settings.Get("severity").MustString("critical"),
|
||||
Class: config.Settings.Get("class").MustString("default"),
|
||||
Component: config.Settings.Get("component").MustString("Grafana"),
|
||||
Group: config.Settings.Get("group").MustString("default"),
|
||||
Summary: config.Settings.Get("summary").MustString(DefaultMessageTitleEmbed),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// NewPagerdutyNotifier is the constructor for the PagerDuty notifier
|
||||
func NewPagerdutyNotifier(config *PagerdutyConfig, ns notifications.WebhookSender, t *template.Template) *PagerdutyNotifier {
|
||||
return &PagerdutyNotifier{
|
||||
Base: NewBase(&models.AlertNotification{
|
||||
Uid: model.UID,
|
||||
Name: model.Name,
|
||||
Type: model.Type,
|
||||
DisableResolveMessage: model.DisableResolveMessage,
|
||||
Settings: model.Settings,
|
||||
Uid: config.UID,
|
||||
Name: config.Name,
|
||||
Type: config.Type,
|
||||
DisableResolveMessage: config.DisableResolveMessage,
|
||||
Settings: config.Settings,
|
||||
}),
|
||||
Key: key,
|
||||
Key: config.Key,
|
||||
CustomDetails: map[string]string{
|
||||
"firing": `{{ template "__text_alert_list" .Alerts.Firing }}`,
|
||||
"resolved": `{{ template "__text_alert_list" .Alerts.Resolved }}`,
|
||||
"num_firing": `{{ .Alerts.Firing | len }}`,
|
||||
"num_resolved": `{{ .Alerts.Resolved | len }}`,
|
||||
},
|
||||
Severity: model.Settings.Get("severity").MustString("critical"),
|
||||
Class: model.Settings.Get("class").MustString("default"),
|
||||
Component: model.Settings.Get("component").MustString("Grafana"),
|
||||
Group: model.Settings.Get("group").MustString("default"),
|
||||
Summary: model.Settings.Get("summary").MustString(DefaultMessageTitleEmbed),
|
||||
Severity: config.Severity,
|
||||
Class: config.Class,
|
||||
Component: config.Component,
|
||||
Group: config.Group,
|
||||
Summary: config.Summary,
|
||||
tmpl: t,
|
||||
log: log.New("alerting.notifier." + model.Name),
|
||||
log: log.New("alerting.notifier." + config.Name),
|
||||
ns: ns,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Notify sends an alert notification to PagerDuty
|
||||
|
@ -119,7 +119,7 @@ func TestPagerdutyNotifier(t *testing.T) {
|
||||
}, {
|
||||
name: "Error in initing",
|
||||
settings: `{}`,
|
||||
expInitError: `failed to validate receiver "pageduty_testing" of type "pagerduty": could not find integration key property in settings`,
|
||||
expInitError: `could not find integration key property in settings`,
|
||||
},
|
||||
}
|
||||
|
||||
@ -139,7 +139,7 @@ func TestPagerdutyNotifier(t *testing.T) {
|
||||
webhookSender := mockNotificationService()
|
||||
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
|
||||
decryptFn := secretsService.GetDecryptedValue
|
||||
pn, err := NewPagerdutyNotifier(m, webhookSender, tmpl, decryptFn)
|
||||
cfg, err := NewPagerdutyConfig(m, decryptFn)
|
||||
if c.expInitError != "" {
|
||||
require.Error(t, err)
|
||||
require.Equal(t, c.expInitError, err.Error())
|
||||
@ -149,6 +149,7 @@ func TestPagerdutyNotifier(t *testing.T) {
|
||||
|
||||
ctx := notify.WithGroupKey(context.Background(), "alertname")
|
||||
ctx = notify.WithGroupLabels(ctx, model.LabelSet{"alertname": ""})
|
||||
pn := NewPagerdutyNotifier(cfg, webhookSender, tmpl)
|
||||
ok, err := pn.Notify(ctx, c.alerts...)
|
||||
if c.expMsgError != nil {
|
||||
require.False(t, ok)
|
||||
|
@ -3,6 +3,7 @@ package channels
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"mime/multipart"
|
||||
"strconv"
|
||||
@ -39,62 +40,93 @@ type PushoverNotifier struct {
|
||||
ns notifications.WebhookSender
|
||||
}
|
||||
|
||||
// NewSlackNotifier is the constructor for the Slack notifier
|
||||
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"}
|
||||
}
|
||||
if model.SecureSettings == nil {
|
||||
return nil, receiverInitError{Cfg: *model, Reason: "no secure settings supplied"}
|
||||
}
|
||||
type PushoverConfig struct {
|
||||
*NotificationChannelConfig
|
||||
UserKey string
|
||||
APIToken string
|
||||
AlertingPriority int
|
||||
OKPriority int
|
||||
Retry int
|
||||
Expire int
|
||||
Device string
|
||||
AlertingSound string
|
||||
OKSound string
|
||||
Upload bool
|
||||
Message string
|
||||
}
|
||||
|
||||
userKey := fn(context.Background(), model.SecureSettings, "userKey", model.Settings.Get("userKey").MustString())
|
||||
APIToken := fn(context.Background(), model.SecureSettings, "apiToken", model.Settings.Get("apiToken").MustString())
|
||||
device := model.Settings.Get("device").MustString()
|
||||
alertingPriority, err := strconv.Atoi(model.Settings.Get("priority").MustString("0")) // default Normal
|
||||
func PushoverFactory(fc FactoryConfig) (NotificationChannel, error) {
|
||||
cfg, err := NewPushoverConfig(fc.Config, fc.DecryptFunc)
|
||||
if err != nil {
|
||||
return nil, receiverInitError{
|
||||
Reason: err.Error(),
|
||||
Cfg: *fc.Config,
|
||||
}
|
||||
}
|
||||
return NewPushoverNotifier(cfg, fc.NotificationService, fc.Template), nil
|
||||
}
|
||||
|
||||
func NewPushoverConfig(config *NotificationChannelConfig, decryptFunc GetDecryptedValueFn) (*PushoverConfig, error) {
|
||||
userKey := decryptFunc(context.Background(), config.SecureSettings, "userKey", config.Settings.Get("userKey").MustString())
|
||||
if userKey == "" {
|
||||
return nil, errors.New("user key not found")
|
||||
}
|
||||
APIToken := decryptFunc(context.Background(), config.SecureSettings, "apiToken", config.Settings.Get("apiToken").MustString())
|
||||
if APIToken == "" {
|
||||
return nil, errors.New("API token not found")
|
||||
}
|
||||
alertingPriority, err := strconv.Atoi(config.Settings.Get("priority").MustString("0")) // default Normal
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to convert alerting priority to integer: %w", err)
|
||||
}
|
||||
okPriority, err := strconv.Atoi(model.Settings.Get("okPriority").MustString("0")) // default Normal
|
||||
okPriority, err := strconv.Atoi(config.Settings.Get("okPriority").MustString("0")) // default Normal
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to convert OK priority to integer: %w", err)
|
||||
}
|
||||
retry, _ := strconv.Atoi(model.Settings.Get("retry").MustString())
|
||||
expire, _ := strconv.Atoi(model.Settings.Get("expire").MustString())
|
||||
alertingSound := model.Settings.Get("sound").MustString()
|
||||
okSound := model.Settings.Get("okSound").MustString()
|
||||
uploadImage := model.Settings.Get("uploadImage").MustBool(true)
|
||||
retry, _ := strconv.Atoi(config.Settings.Get("retry").MustString())
|
||||
expire, _ := strconv.Atoi(config.Settings.Get("expire").MustString())
|
||||
return &PushoverConfig{
|
||||
NotificationChannelConfig: config,
|
||||
APIToken: APIToken,
|
||||
UserKey: userKey,
|
||||
AlertingPriority: alertingPriority,
|
||||
OKPriority: okPriority,
|
||||
Retry: retry,
|
||||
Expire: expire,
|
||||
Device: config.Settings.Get("device").MustString(),
|
||||
AlertingSound: config.Settings.Get("sound").MustString(),
|
||||
OKSound: config.Settings.Get("okSound").MustString(),
|
||||
Upload: config.Settings.Get("uploadImage").MustBool(true),
|
||||
Message: config.Settings.Get("message").MustString(`{{ template "default.message" .}}`),
|
||||
}, nil
|
||||
}
|
||||
|
||||
if userKey == "" {
|
||||
return nil, receiverInitError{Cfg: *model, Reason: "user key not found"}
|
||||
}
|
||||
if APIToken == "" {
|
||||
return nil, receiverInitError{Cfg: *model, Reason: "API token not found"}
|
||||
}
|
||||
// NewSlackNotifier is the constructor for the Slack notifier
|
||||
func NewPushoverNotifier(config *PushoverConfig, ns notifications.WebhookSender, t *template.Template) *PushoverNotifier {
|
||||
return &PushoverNotifier{
|
||||
Base: NewBase(&models.AlertNotification{
|
||||
Uid: model.UID,
|
||||
Name: model.Name,
|
||||
Type: model.Type,
|
||||
DisableResolveMessage: model.DisableResolveMessage,
|
||||
Settings: model.Settings,
|
||||
SecureSettings: model.SecureSettings,
|
||||
Uid: config.UID,
|
||||
Name: config.Name,
|
||||
Type: config.Type,
|
||||
DisableResolveMessage: config.DisableResolveMessage,
|
||||
Settings: config.Settings,
|
||||
SecureSettings: config.SecureSettings,
|
||||
}),
|
||||
UserKey: userKey,
|
||||
APIToken: APIToken,
|
||||
AlertingPriority: alertingPriority,
|
||||
OKPriority: okPriority,
|
||||
Retry: retry,
|
||||
Expire: expire,
|
||||
Device: device,
|
||||
AlertingSound: alertingSound,
|
||||
OKSound: okSound,
|
||||
Upload: uploadImage,
|
||||
Message: model.Settings.Get("message").MustString(`{{ template "default.message" .}}`),
|
||||
UserKey: config.UserKey,
|
||||
APIToken: config.APIToken,
|
||||
AlertingPriority: config.AlertingPriority,
|
||||
OKPriority: config.OKPriority,
|
||||
Retry: config.Retry,
|
||||
Expire: config.Expire,
|
||||
Device: config.Device,
|
||||
AlertingSound: config.AlertingSound,
|
||||
OKSound: config.OKSound,
|
||||
Upload: config.Upload,
|
||||
Message: config.Message,
|
||||
tmpl: t,
|
||||
log: log.New("alerting.notifier.pushover"),
|
||||
ns: ns,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Notify sends an alert notification to Slack.
|
||||
|
@ -112,13 +112,13 @@ func TestPushoverNotifier(t *testing.T) {
|
||||
settings: `{
|
||||
"apiToken": "<apiToken>"
|
||||
}`,
|
||||
expInitError: `failed to validate receiver "pushover_testing" of type "pushover": user key not found`,
|
||||
expInitError: `user key not found`,
|
||||
}, {
|
||||
name: "Missing api key",
|
||||
settings: `{
|
||||
"userKey": "<userKey>"
|
||||
}`,
|
||||
expInitError: `failed to validate receiver "pushover_testing" of type "pushover": API token not found`,
|
||||
expInitError: `API token not found`,
|
||||
},
|
||||
}
|
||||
|
||||
@ -147,7 +147,7 @@ func TestPushoverNotifier(t *testing.T) {
|
||||
webhookSender := mockNotificationService()
|
||||
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
|
||||
decryptFn := secretsService.GetDecryptedValue
|
||||
pn, err := NewPushoverNotifier(m, webhookSender, tmpl, decryptFn)
|
||||
cfg, err := NewPushoverConfig(m, decryptFn)
|
||||
if c.expInitError != "" {
|
||||
require.Error(t, err)
|
||||
require.Equal(t, c.expInitError, err.Error())
|
||||
@ -157,6 +157,7 @@ func TestPushoverNotifier(t *testing.T) {
|
||||
|
||||
ctx := notify.WithGroupKey(context.Background(), "alertname")
|
||||
ctx = notify.WithGroupLabels(ctx, model.LabelSet{"alertname": ""})
|
||||
pn := NewPushoverNotifier(cfg, webhookSender, tmpl)
|
||||
ok, err := pn.Notify(ctx, c.alerts...)
|
||||
if c.expMsgError != nil {
|
||||
require.Error(t, err)
|
||||
|
@ -3,6 +3,7 @@ package channels
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
@ -29,44 +30,71 @@ type SensuGoNotifier struct {
|
||||
Message string
|
||||
}
|
||||
|
||||
// NewSensuGoNotifier is the constructor for the SensuGo notifier
|
||||
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"}
|
||||
type SensuGoConfig struct {
|
||||
*NotificationChannelConfig
|
||||
URL string
|
||||
Entity string
|
||||
Check string
|
||||
Namespace string
|
||||
Handler string
|
||||
APIKey string
|
||||
Message string
|
||||
}
|
||||
|
||||
func SensuGoFactory(fc FactoryConfig) (NotificationChannel, error) {
|
||||
cfg, err := NewSensuGoConfig(fc.Config, fc.DecryptFunc)
|
||||
if err != nil {
|
||||
return nil, receiverInitError{
|
||||
Reason: err.Error(),
|
||||
Cfg: *fc.Config,
|
||||
}
|
||||
}
|
||||
if model.SecureSettings == nil {
|
||||
return nil, receiverInitError{Cfg: *model, Reason: "no secure settings supplied"}
|
||||
}
|
||||
url := model.Settings.Get("url").MustString()
|
||||
return NewSensuGoNotifier(cfg, fc.NotificationService, fc.Template), nil
|
||||
}
|
||||
|
||||
func NewSensuGoConfig(config *NotificationChannelConfig, decryptFunc GetDecryptedValueFn) (*SensuGoConfig, error) {
|
||||
url := config.Settings.Get("url").MustString()
|
||||
if url == "" {
|
||||
return nil, receiverInitError{Cfg: *model, Reason: "could not find URL property in settings"}
|
||||
return nil, errors.New("could not find URL property in settings")
|
||||
}
|
||||
|
||||
apikey := fn(context.Background(), model.SecureSettings, "apikey", model.Settings.Get("apikey").MustString())
|
||||
apikey := decryptFunc(context.Background(), config.SecureSettings, "apikey", config.Settings.Get("apikey").MustString())
|
||||
if apikey == "" {
|
||||
return nil, receiverInitError{Cfg: *model, Reason: "could not find the API key property in settings"}
|
||||
return nil, errors.New("could not find the API key property in settings")
|
||||
}
|
||||
return &SensuGoConfig{
|
||||
NotificationChannelConfig: config,
|
||||
URL: url,
|
||||
Entity: config.Settings.Get("entity").MustString(),
|
||||
Check: config.Settings.Get("check").MustString(),
|
||||
Namespace: config.Settings.Get("namespace").MustString(),
|
||||
Handler: config.Settings.Get("handler").MustString(),
|
||||
APIKey: apikey,
|
||||
Message: config.Settings.Get("message").MustString(`{{ template "default.message" .}}`),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// NewSensuGoNotifier is the constructor for the SensuGo notifier
|
||||
func NewSensuGoNotifier(config *SensuGoConfig, ns notifications.WebhookSender, t *template.Template) *SensuGoNotifier {
|
||||
return &SensuGoNotifier{
|
||||
Base: NewBase(&models.AlertNotification{
|
||||
Uid: model.UID,
|
||||
Name: model.Name,
|
||||
Type: model.Type,
|
||||
DisableResolveMessage: model.DisableResolveMessage,
|
||||
Settings: model.Settings,
|
||||
SecureSettings: model.SecureSettings,
|
||||
Uid: config.UID,
|
||||
Name: config.Name,
|
||||
Type: config.Type,
|
||||
DisableResolveMessage: config.DisableResolveMessage,
|
||||
Settings: config.Settings,
|
||||
SecureSettings: config.SecureSettings,
|
||||
}),
|
||||
URL: url,
|
||||
Entity: model.Settings.Get("entity").MustString(),
|
||||
Check: model.Settings.Get("check").MustString(),
|
||||
Namespace: model.Settings.Get("namespace").MustString(),
|
||||
Handler: model.Settings.Get("handler").MustString(),
|
||||
APIKey: apikey,
|
||||
Message: model.Settings.Get("message").MustString(`{{ template "default.message" .}}`),
|
||||
URL: config.URL,
|
||||
Entity: config.Entity,
|
||||
Check: config.Check,
|
||||
Namespace: config.Namespace,
|
||||
Handler: config.Handler,
|
||||
APIKey: config.APIKey,
|
||||
Message: config.Message,
|
||||
log: log.New("alerting.notifier.sensugo"),
|
||||
ns: ns,
|
||||
tmpl: t,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Notify sends an alert notification to Sensu Go
|
||||
|
@ -121,13 +121,13 @@ func TestSensuGoNotifier(t *testing.T) {
|
||||
settings: `{
|
||||
"apikey": "<apikey>"
|
||||
}`,
|
||||
expInitError: `failed to validate receiver "Sensu Go" of type "sensugo": could not find URL property in settings`,
|
||||
expInitError: `could not find URL property in settings`,
|
||||
}, {
|
||||
name: "Error in initing: missing API key",
|
||||
settings: `{
|
||||
"url": "http://sensu-api.local:8080"
|
||||
}`,
|
||||
expInitError: `failed to validate receiver "Sensu Go" of type "sensugo": could not find the API key property in settings`,
|
||||
expInitError: `could not find the API key property in settings`,
|
||||
},
|
||||
}
|
||||
|
||||
@ -147,7 +147,7 @@ func TestSensuGoNotifier(t *testing.T) {
|
||||
webhookSender := mockNotificationService()
|
||||
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
|
||||
decryptFn := secretsService.GetDecryptedValue
|
||||
sn, err := NewSensuGoNotifier(m, webhookSender, tmpl, decryptFn)
|
||||
cfg, err := NewSensuGoConfig(m, decryptFn)
|
||||
if c.expInitError != "" {
|
||||
require.Error(t, err)
|
||||
require.Equal(t, c.expInitError, err.Error())
|
||||
@ -157,6 +157,7 @@ func TestSensuGoNotifier(t *testing.T) {
|
||||
|
||||
ctx := notify.WithGroupKey(context.Background(), "alertname")
|
||||
ctx = notify.WithGroupLabels(ctx, model.LabelSet{"alertname": ""})
|
||||
sn := NewSensuGoNotifier(cfg, webhookSender, tmpl)
|
||||
ok, err := sn.Notify(ctx, c.alerts...)
|
||||
if c.expMsgError != nil {
|
||||
require.False(t, ok)
|
||||
|
@ -5,6 +5,7 @@ import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
@ -21,6 +22,8 @@ import (
|
||||
"github.com/prometheus/alertmanager/types"
|
||||
)
|
||||
|
||||
var SlackAPIEndpoint = "https://slack.com/api/chat.postMessage"
|
||||
|
||||
// SlackNotifier is responsible for sending
|
||||
// alert notification to Slack.
|
||||
type SlackNotifier struct {
|
||||
@ -41,43 +44,55 @@ type SlackNotifier struct {
|
||||
Token string
|
||||
}
|
||||
|
||||
var SlackAPIEndpoint = "https://slack.com/api/chat.postMessage"
|
||||
type SlackConfig struct {
|
||||
*NotificationChannelConfig
|
||||
URL *url.URL
|
||||
Username string
|
||||
IconEmoji string
|
||||
IconURL string
|
||||
Recipient string
|
||||
Text string
|
||||
Title string
|
||||
MentionUsers []string
|
||||
MentionGroups []string
|
||||
MentionChannel string
|
||||
Token string
|
||||
}
|
||||
|
||||
// NewSlackNotifier is the constructor for the Slack notifier
|
||||
func NewSlackNotifier(model *NotificationChannelConfig, t *template.Template, fn GetDecryptedValueFn) (*SlackNotifier, error) {
|
||||
if model.Settings == nil {
|
||||
return nil, receiverInitError{Cfg: *model, Reason: "no settings supplied"}
|
||||
}
|
||||
if model.SecureSettings == nil {
|
||||
return nil, receiverInitError{Cfg: *model, Reason: "no secure settings supplied"}
|
||||
func SlackFactory(fc FactoryConfig) (NotificationChannel, error) {
|
||||
cfg, err := NewSlackConfig(fc.Config, fc.DecryptFunc)
|
||||
if err != nil {
|
||||
return nil, receiverInitError{
|
||||
Reason: err.Error(),
|
||||
Cfg: *fc.Config,
|
||||
}
|
||||
}
|
||||
return NewSlackNotifier(cfg, fc.Template), nil
|
||||
}
|
||||
|
||||
endpointURL := model.Settings.Get("endpointUrl").MustString(SlackAPIEndpoint)
|
||||
|
||||
slackURL := fn(context.Background(), model.SecureSettings, "url", model.Settings.Get("url").MustString())
|
||||
func NewSlackConfig(config *NotificationChannelConfig, decryptFunc GetDecryptedValueFn) (*SlackConfig, error) {
|
||||
endpointURL := config.Settings.Get("endpointUrl").MustString(SlackAPIEndpoint)
|
||||
slackURL := decryptFunc(context.Background(), config.SecureSettings, "url", config.Settings.Get("url").MustString())
|
||||
if slackURL == "" {
|
||||
slackURL = endpointURL
|
||||
}
|
||||
apiURL, err := url.Parse(slackURL)
|
||||
if err != nil {
|
||||
return nil, receiverInitError{Cfg: *model, Reason: fmt.Sprintf("invalid URL %q", slackURL), Err: err}
|
||||
return nil, fmt.Errorf("invalid URL %q", slackURL)
|
||||
}
|
||||
|
||||
recipient := strings.TrimSpace(model.Settings.Get("recipient").MustString())
|
||||
if recipient == "" && apiURL.String() == endpointURL {
|
||||
return nil, receiverInitError{Cfg: *model,
|
||||
Reason: "recipient must be specified when using the Slack chat API",
|
||||
}
|
||||
recipient := strings.TrimSpace(config.Settings.Get("recipient").MustString())
|
||||
if recipient == "" && apiURL.String() == SlackAPIEndpoint {
|
||||
return nil, errors.New("recipient must be specified when using the Slack chat API")
|
||||
}
|
||||
|
||||
mentionChannel := model.Settings.Get("mentionChannel").MustString()
|
||||
mentionChannel := config.Settings.Get("mentionChannel").MustString()
|
||||
if mentionChannel != "" && mentionChannel != "here" && mentionChannel != "channel" {
|
||||
return nil, receiverInitError{Cfg: *model,
|
||||
Reason: fmt.Sprintf("invalid value for mentionChannel: %q", mentionChannel),
|
||||
}
|
||||
return nil, fmt.Errorf("invalid value for mentionChannel: %q", mentionChannel)
|
||||
}
|
||||
|
||||
mentionUsersStr := model.Settings.Get("mentionUsers").MustString()
|
||||
token := decryptFunc(context.Background(), config.SecureSettings, "token", config.Settings.Get("token").MustString())
|
||||
if token == "" && apiURL.String() == SlackAPIEndpoint {
|
||||
return nil, errors.New("token must be specified when using the Slack chat API")
|
||||
}
|
||||
mentionUsersStr := config.Settings.Get("mentionUsers").MustString()
|
||||
mentionUsers := []string{}
|
||||
for _, u := range strings.Split(mentionUsersStr, ",") {
|
||||
u = strings.TrimSpace(u)
|
||||
@ -85,8 +100,7 @@ func NewSlackNotifier(model *NotificationChannelConfig, t *template.Template, fn
|
||||
mentionUsers = append(mentionUsers, u)
|
||||
}
|
||||
}
|
||||
|
||||
mentionGroupsStr := model.Settings.Get("mentionGroups").MustString()
|
||||
mentionGroupsStr := config.Settings.Get("mentionGroups").MustString()
|
||||
mentionGroups := []string{}
|
||||
for _, g := range strings.Split(mentionGroupsStr, ",") {
|
||||
g = strings.TrimSpace(g)
|
||||
@ -94,36 +108,46 @@ func NewSlackNotifier(model *NotificationChannelConfig, t *template.Template, fn
|
||||
mentionGroups = append(mentionGroups, g)
|
||||
}
|
||||
}
|
||||
return &SlackConfig{
|
||||
NotificationChannelConfig: config,
|
||||
Recipient: strings.TrimSpace(config.Settings.Get("recipient").MustString()),
|
||||
MentionChannel: config.Settings.Get("mentionChannel").MustString(),
|
||||
MentionUsers: mentionUsers,
|
||||
MentionGroups: mentionGroups,
|
||||
URL: apiURL,
|
||||
Username: config.Settings.Get("username").MustString("Grafana"),
|
||||
IconEmoji: config.Settings.Get("icon_emoji").MustString(),
|
||||
IconURL: config.Settings.Get("icon_url").MustString(),
|
||||
Token: token,
|
||||
Text: config.Settings.Get("text").MustString(`{{ template "default.message" . }}`),
|
||||
Title: config.Settings.Get("title").MustString(DefaultMessageTitleEmbed),
|
||||
}, nil
|
||||
}
|
||||
|
||||
token := fn(context.Background(), model.SecureSettings, "token", model.Settings.Get("token").MustString())
|
||||
if token == "" && apiURL.String() == SlackAPIEndpoint {
|
||||
return nil, receiverInitError{Cfg: *model,
|
||||
Reason: "token must be specified when using the Slack chat API",
|
||||
}
|
||||
}
|
||||
|
||||
// NewSlackNotifier is the constructor for the Slack notifier
|
||||
func NewSlackNotifier(config *SlackConfig, t *template.Template) *SlackNotifier {
|
||||
return &SlackNotifier{
|
||||
Base: NewBase(&models.AlertNotification{
|
||||
Uid: model.UID,
|
||||
Name: model.Name,
|
||||
Type: model.Type,
|
||||
DisableResolveMessage: model.DisableResolveMessage,
|
||||
Settings: model.Settings,
|
||||
Uid: config.UID,
|
||||
Name: config.Name,
|
||||
Type: config.Type,
|
||||
DisableResolveMessage: config.DisableResolveMessage,
|
||||
Settings: config.Settings,
|
||||
}),
|
||||
URL: apiURL,
|
||||
Recipient: recipient,
|
||||
MentionUsers: mentionUsers,
|
||||
MentionGroups: mentionGroups,
|
||||
MentionChannel: mentionChannel,
|
||||
Username: model.Settings.Get("username").MustString("Grafana"),
|
||||
IconEmoji: model.Settings.Get("icon_emoji").MustString(),
|
||||
IconURL: model.Settings.Get("icon_url").MustString(),
|
||||
Token: token,
|
||||
Text: model.Settings.Get("text").MustString(`{{ template "default.message" . }}`),
|
||||
Title: model.Settings.Get("title").MustString(DefaultMessageTitleEmbed),
|
||||
URL: config.URL,
|
||||
Recipient: config.Recipient,
|
||||
MentionUsers: config.MentionUsers,
|
||||
MentionGroups: config.MentionGroups,
|
||||
MentionChannel: config.MentionChannel,
|
||||
Username: config.Username,
|
||||
IconEmoji: config.IconEmoji,
|
||||
IconURL: config.IconURL,
|
||||
Token: config.Token,
|
||||
Text: config.Text,
|
||||
Title: config.Title,
|
||||
log: log.New("alerting.notifier.slack"),
|
||||
tmpl: t,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
// slackMessage is the slackMessage for sending a slack notification.
|
||||
|
@ -152,13 +152,13 @@ func TestSlackNotifier(t *testing.T) {
|
||||
settings: `{
|
||||
"recipient": "#testchannel"
|
||||
}`,
|
||||
expInitError: `failed to validate receiver "slack_testing" of type "slack": token must be specified when using the Slack chat API`,
|
||||
expInitError: `token must be specified when using the Slack chat API`,
|
||||
}, {
|
||||
name: "Missing recipient",
|
||||
settings: `{
|
||||
"token": "1234"
|
||||
}`,
|
||||
expInitError: `failed to validate receiver "slack_testing" of type "slack": recipient must be specified when using the Slack chat API`,
|
||||
expInitError: `recipient must be specified when using the Slack chat API`,
|
||||
},
|
||||
{
|
||||
name: "Custom endpoint url",
|
||||
@ -213,7 +213,7 @@ func TestSlackNotifier(t *testing.T) {
|
||||
|
||||
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
|
||||
decryptFn := secretsService.GetDecryptedValue
|
||||
pn, err := NewSlackNotifier(m, tmpl, decryptFn)
|
||||
cfg, err := NewSlackConfig(m, decryptFn)
|
||||
if c.expInitError != "" {
|
||||
require.Error(t, err)
|
||||
require.Equal(t, c.expInitError, err.Error())
|
||||
@ -246,6 +246,7 @@ func TestSlackNotifier(t *testing.T) {
|
||||
|
||||
ctx := notify.WithGroupKey(context.Background(), "alertname")
|
||||
ctx = notify.WithGroupLabels(ctx, model.LabelSet{"alertname": ""})
|
||||
pn := NewSlackNotifier(cfg, tmpl)
|
||||
ok, err := pn.Notify(ctx, c.alerts...)
|
||||
if c.expMsgError != nil {
|
||||
require.Error(t, err)
|
||||
|
@ -24,31 +24,51 @@ type TeamsNotifier struct {
|
||||
ns notifications.WebhookSender
|
||||
}
|
||||
|
||||
type TeamsConfig struct {
|
||||
*NotificationChannelConfig
|
||||
URL string
|
||||
Message string
|
||||
}
|
||||
|
||||
func TeamsFactory(fc FactoryConfig) (NotificationChannel, error) {
|
||||
cfg, err := NewTeamsConfig(fc.Config)
|
||||
if err != nil {
|
||||
return nil, receiverInitError{
|
||||
Reason: err.Error(),
|
||||
Cfg: *fc.Config,
|
||||
}
|
||||
}
|
||||
return NewTeamsNotifier(cfg, fc.NotificationService, fc.Template), nil
|
||||
}
|
||||
|
||||
func NewTeamsConfig(config *NotificationChannelConfig) (*TeamsConfig, error) {
|
||||
URL := config.Settings.Get("url").MustString()
|
||||
if URL == "" {
|
||||
return nil, errors.New("could not find url property in settings")
|
||||
}
|
||||
return &TeamsConfig{
|
||||
NotificationChannelConfig: config,
|
||||
URL: URL,
|
||||
Message: config.Settings.Get("message").MustString(`{{ template "teams.default.message" .}}`),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// NewTeamsNotifier is the constructor for Teams notifier.
|
||||
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"}
|
||||
}
|
||||
|
||||
u := model.Settings.Get("url").MustString()
|
||||
if u == "" {
|
||||
return nil, receiverInitError{Cfg: *model, Reason: "could not find url property in settings"}
|
||||
}
|
||||
|
||||
func NewTeamsNotifier(config *TeamsConfig, ns notifications.WebhookSender, t *template.Template) *TeamsNotifier {
|
||||
return &TeamsNotifier{
|
||||
Base: NewBase(&models.AlertNotification{
|
||||
Uid: model.UID,
|
||||
Name: model.Name,
|
||||
Type: model.Type,
|
||||
DisableResolveMessage: model.DisableResolveMessage,
|
||||
Settings: model.Settings,
|
||||
Uid: config.UID,
|
||||
Name: config.Name,
|
||||
Type: config.Type,
|
||||
DisableResolveMessage: config.DisableResolveMessage,
|
||||
Settings: config.Settings,
|
||||
}),
|
||||
URL: u,
|
||||
Message: model.Settings.Get("message").MustString(`{{ template "teams.default.message" .}}`),
|
||||
URL: config.URL,
|
||||
Message: config.Message,
|
||||
log: log.New("alerting.notifier.teams"),
|
||||
ns: ns,
|
||||
tmpl: t,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Notify send an alert notification to Microsoft teams.
|
||||
|
@ -106,7 +106,7 @@ func TestTeamsNotifier(t *testing.T) {
|
||||
}, {
|
||||
name: "Error in initing",
|
||||
settings: `{}`,
|
||||
expInitError: `failed to validate receiver "teams_testing" of type "teams": could not find url property in settings`,
|
||||
expInitError: `could not find url property in settings`,
|
||||
},
|
||||
}
|
||||
|
||||
@ -122,7 +122,7 @@ func TestTeamsNotifier(t *testing.T) {
|
||||
}
|
||||
|
||||
webhookSender := mockNotificationService()
|
||||
pn, err := NewTeamsNotifier(m, webhookSender, tmpl)
|
||||
cfg, err := NewTeamsConfig(m)
|
||||
if c.expInitError != "" {
|
||||
require.Error(t, err)
|
||||
require.Equal(t, c.expInitError, err.Error())
|
||||
@ -132,6 +132,7 @@ func TestTeamsNotifier(t *testing.T) {
|
||||
|
||||
ctx := notify.WithGroupKey(context.Background(), "alertname")
|
||||
ctx = notify.WithGroupLabels(ctx, model.LabelSet{"alertname": ""})
|
||||
pn := NewTeamsNotifier(cfg, webhookSender, tmpl)
|
||||
ok, err := pn.Notify(ctx, c.alerts...)
|
||||
if c.expMsgError != nil {
|
||||
require.False(t, ok)
|
||||
|
@ -3,6 +3,7 @@ package channels
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"mime/multipart"
|
||||
|
||||
@ -29,42 +30,58 @@ type TelegramNotifier struct {
|
||||
tmpl *template.Template
|
||||
}
|
||||
|
||||
// NewTelegramNotifier is the constructor for the Telegram notifier
|
||||
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"}
|
||||
}
|
||||
if model.SecureSettings == nil {
|
||||
return nil, receiverInitError{Cfg: *model, Reason: "no secure settings supplied"}
|
||||
}
|
||||
type TelegramConfig struct {
|
||||
*NotificationChannelConfig
|
||||
BotToken string
|
||||
ChatID string
|
||||
Message string
|
||||
}
|
||||
|
||||
botToken := fn(context.Background(), model.SecureSettings, "bottoken", model.Settings.Get("bottoken").MustString())
|
||||
chatID := model.Settings.Get("chatid").MustString()
|
||||
message := model.Settings.Get("message").MustString(`{{ template "default.message" . }}`)
|
||||
func TelegramFactory(fc FactoryConfig) (NotificationChannel, error) {
|
||||
config, err := NewTelegramConfig(fc.Config, fc.DecryptFunc)
|
||||
if err != nil {
|
||||
return nil, receiverInitError{
|
||||
Reason: err.Error(),
|
||||
Cfg: *fc.Config,
|
||||
}
|
||||
}
|
||||
return NewTelegramNotifier(config, fc.NotificationService, fc.Template), nil
|
||||
}
|
||||
|
||||
func NewTelegramConfig(config *NotificationChannelConfig, fn GetDecryptedValueFn) (*TelegramConfig, error) {
|
||||
botToken := fn(context.Background(), config.SecureSettings, "bottoken", config.Settings.Get("bottoken").MustString())
|
||||
if botToken == "" {
|
||||
return nil, receiverInitError{Cfg: *model, Reason: "could not find Bot Token in settings"}
|
||||
return &TelegramConfig{}, errors.New("could not find Bot Token in settings")
|
||||
}
|
||||
|
||||
chatID := config.Settings.Get("chatid").MustString()
|
||||
if chatID == "" {
|
||||
return nil, receiverInitError{Cfg: *model, Reason: "could not find Chat Id in settings"}
|
||||
return &TelegramConfig{}, errors.New("could not find Chat Id in settings")
|
||||
}
|
||||
return &TelegramConfig{
|
||||
NotificationChannelConfig: config,
|
||||
BotToken: botToken,
|
||||
ChatID: chatID,
|
||||
Message: config.Settings.Get("message").MustString(`{{ template "default.message" . }}`),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// NewTelegramNotifier is the constructor for the Telegram notifier
|
||||
func NewTelegramNotifier(config *TelegramConfig, ns notifications.WebhookSender, t *template.Template) *TelegramNotifier {
|
||||
return &TelegramNotifier{
|
||||
Base: NewBase(&models.AlertNotification{
|
||||
Uid: model.UID,
|
||||
Name: model.Name,
|
||||
Type: model.Type,
|
||||
DisableResolveMessage: model.DisableResolveMessage,
|
||||
Settings: model.Settings,
|
||||
Uid: config.UID,
|
||||
Name: config.Name,
|
||||
Type: config.Type,
|
||||
DisableResolveMessage: config.DisableResolveMessage,
|
||||
Settings: config.Settings,
|
||||
}),
|
||||
BotToken: botToken,
|
||||
ChatID: chatID,
|
||||
Message: message,
|
||||
BotToken: config.BotToken,
|
||||
ChatID: config.ChatID,
|
||||
Message: config.Message,
|
||||
tmpl: t,
|
||||
log: log.New("alerting.notifier.telegram"),
|
||||
ns: ns,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Notify send an alert notification to Telegram.
|
||||
|
@ -81,7 +81,7 @@ func TestTelegramNotifier(t *testing.T) {
|
||||
}, {
|
||||
name: "Error in initing",
|
||||
settings: `{}`,
|
||||
expInitError: `failed to validate receiver "telegram_testing" of type "telegram": could not find Bot Token in settings`,
|
||||
expInitError: `could not find Bot Token in settings`,
|
||||
},
|
||||
}
|
||||
|
||||
@ -101,16 +101,16 @@ func TestTelegramNotifier(t *testing.T) {
|
||||
webhookSender := mockNotificationService()
|
||||
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
|
||||
decryptFn := secretsService.GetDecryptedValue
|
||||
pn, err := NewTelegramNotifier(m, webhookSender, tmpl, decryptFn)
|
||||
cfg, err := NewTelegramConfig(m, decryptFn)
|
||||
if c.expInitError != "" {
|
||||
require.Error(t, err)
|
||||
require.Equal(t, c.expInitError, err.Error())
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
ctx := notify.WithGroupKey(context.Background(), "alertname")
|
||||
ctx = notify.WithGroupLabels(ctx, model.LabelSet{"alertname": ""})
|
||||
pn := NewTelegramNotifier(cfg, webhookSender, tmpl)
|
||||
msg, err := pn.buildTelegramMessage(ctx, c.alerts)
|
||||
if c.expMsgError != nil {
|
||||
require.Error(t, err)
|
||||
|
@ -2,6 +2,7 @@ package channels
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"path"
|
||||
@ -31,53 +32,71 @@ type ThreemaNotifier struct {
|
||||
tmpl *template.Template
|
||||
}
|
||||
|
||||
// NewThreemaNotifier is the constructor for the Threema notifier
|
||||
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"}
|
||||
}
|
||||
if model.SecureSettings == nil {
|
||||
return nil, receiverInitError{Cfg: *model, Reason: "no secure settings supplied"}
|
||||
}
|
||||
gatewayID := model.Settings.Get("gateway_id").MustString()
|
||||
recipientID := model.Settings.Get("recipient_id").MustString()
|
||||
apiSecret := fn(context.Background(), model.SecureSettings, "api_secret", model.Settings.Get("api_secret").MustString())
|
||||
type ThreemaConfig struct {
|
||||
*NotificationChannelConfig
|
||||
GatewayID string
|
||||
RecipientID string
|
||||
APISecret string
|
||||
}
|
||||
|
||||
// Validation
|
||||
func ThreemaFactory(fc FactoryConfig) (NotificationChannel, error) {
|
||||
cfg, err := NewThreemaConfig(fc.Config, fc.DecryptFunc)
|
||||
if err != nil {
|
||||
return nil, receiverInitError{
|
||||
Reason: err.Error(),
|
||||
Cfg: *fc.Config,
|
||||
}
|
||||
}
|
||||
return NewThreemaNotifier(cfg, fc.NotificationService, fc.Template), nil
|
||||
}
|
||||
|
||||
func NewThreemaConfig(config *NotificationChannelConfig, decryptFunc GetDecryptedValueFn) (*ThreemaConfig, error) {
|
||||
gatewayID := config.Settings.Get("gateway_id").MustString()
|
||||
if gatewayID == "" {
|
||||
return nil, receiverInitError{Cfg: *model, Reason: "could not find Threema Gateway ID in settings"}
|
||||
return nil, errors.New("could not find Threema Gateway ID in settings")
|
||||
}
|
||||
if !strings.HasPrefix(gatewayID, "*") {
|
||||
return nil, receiverInitError{Cfg: *model, Reason: "invalid Threema Gateway ID: Must start with a *"}
|
||||
return nil, errors.New("invalid Threema Gateway ID: Must start with a *")
|
||||
}
|
||||
if len(gatewayID) != 8 {
|
||||
return nil, receiverInitError{Cfg: *model, Reason: "invalid Threema Gateway ID: Must be 8 characters long"}
|
||||
return nil, errors.New("invalid Threema Gateway ID: Must be 8 characters long")
|
||||
}
|
||||
recipientID := config.Settings.Get("recipient_id").MustString()
|
||||
if recipientID == "" {
|
||||
return nil, receiverInitError{Cfg: *model, Reason: "could not find Threema Recipient ID in settings"}
|
||||
return nil, errors.New("could not find Threema Recipient ID in settings")
|
||||
}
|
||||
if len(recipientID) != 8 {
|
||||
return nil, receiverInitError{Cfg: *model, Reason: "invalid Threema Recipient ID: Must be 8 characters long"}
|
||||
return nil, errors.New("invalid Threema Recipient ID: Must be 8 characters long")
|
||||
}
|
||||
apiSecret := decryptFunc(context.Background(), config.SecureSettings, "api_secret", config.Settings.Get("api_secret").MustString())
|
||||
if apiSecret == "" {
|
||||
return nil, receiverInitError{Cfg: *model, Reason: "could not find Threema API secret in settings"}
|
||||
return nil, errors.New("could not find Threema API secret in settings")
|
||||
}
|
||||
return &ThreemaConfig{
|
||||
NotificationChannelConfig: config,
|
||||
GatewayID: gatewayID,
|
||||
RecipientID: recipientID,
|
||||
APISecret: apiSecret,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// NewThreemaNotifier is the constructor for the Threema notifier
|
||||
func NewThreemaNotifier(config *ThreemaConfig, ns notifications.WebhookSender, t *template.Template) *ThreemaNotifier {
|
||||
return &ThreemaNotifier{
|
||||
Base: NewBase(&models.AlertNotification{
|
||||
Uid: model.UID,
|
||||
Name: model.Name,
|
||||
Type: model.Type,
|
||||
DisableResolveMessage: model.DisableResolveMessage,
|
||||
Settings: model.Settings,
|
||||
Uid: config.UID,
|
||||
Name: config.Name,
|
||||
Type: config.Type,
|
||||
DisableResolveMessage: config.DisableResolveMessage,
|
||||
Settings: config.Settings,
|
||||
}),
|
||||
GatewayID: gatewayID,
|
||||
RecipientID: recipientID,
|
||||
APISecret: apiSecret,
|
||||
GatewayID: config.GatewayID,
|
||||
RecipientID: config.RecipientID,
|
||||
APISecret: config.APISecret,
|
||||
log: log.New("alerting.notifier.threema"),
|
||||
ns: ns,
|
||||
tmpl: t,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Notify send an alert notification to Threema
|
||||
|
@ -76,7 +76,7 @@ func TestThreemaNotifier(t *testing.T) {
|
||||
"recipient_id": "87654321",
|
||||
"api_secret": "supersecret"
|
||||
}`,
|
||||
expInitError: `failed to validate receiver "threema_testing" of type "threema": invalid Threema Gateway ID: Must start with a *`,
|
||||
expInitError: `invalid Threema Gateway ID: Must start with a *`,
|
||||
}, {
|
||||
name: "Invalid receipent id",
|
||||
settings: `{
|
||||
@ -84,14 +84,14 @@ func TestThreemaNotifier(t *testing.T) {
|
||||
"recipient_id": "8765432",
|
||||
"api_secret": "supersecret"
|
||||
}`,
|
||||
expInitError: `failed to validate receiver "threema_testing" of type "threema": invalid Threema Recipient ID: Must be 8 characters long`,
|
||||
expInitError: `invalid Threema Recipient ID: Must be 8 characters long`,
|
||||
}, {
|
||||
name: "No API secret",
|
||||
settings: `{
|
||||
"gateway_id": "*1234567",
|
||||
"recipient_id": "87654321"
|
||||
}`,
|
||||
expInitError: `failed to validate receiver "threema_testing" of type "threema": could not find Threema API secret in settings`,
|
||||
expInitError: `could not find Threema API secret in settings`,
|
||||
},
|
||||
}
|
||||
|
||||
@ -111,7 +111,7 @@ func TestThreemaNotifier(t *testing.T) {
|
||||
webhookSender := mockNotificationService()
|
||||
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
|
||||
decryptFn := secretsService.GetDecryptedValue
|
||||
pn, err := NewThreemaNotifier(m, webhookSender, tmpl, decryptFn)
|
||||
cfg, err := NewThreemaConfig(m, decryptFn)
|
||||
if c.expInitError != "" {
|
||||
require.Error(t, err)
|
||||
require.Equal(t, c.expInitError, err.Error())
|
||||
@ -121,6 +121,7 @@ func TestThreemaNotifier(t *testing.T) {
|
||||
|
||||
ctx := notify.WithGroupKey(context.Background(), "alertname")
|
||||
ctx = notify.WithGroupLabels(ctx, model.LabelSet{"alertname": ""})
|
||||
pn := NewThreemaNotifier(cfg, webhookSender, tmpl)
|
||||
ok, err := pn.Notify(ctx, c.alerts...)
|
||||
if c.expMsgError != nil {
|
||||
require.False(t, ok)
|
||||
|
@ -12,6 +12,7 @@ import (
|
||||
"path"
|
||||
"time"
|
||||
|
||||
"github.com/prometheus/alertmanager/notify"
|
||||
"github.com/prometheus/common/model"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
@ -60,6 +61,10 @@ func getAlertStatusColor(status model.AlertStatus) string {
|
||||
return ColorAlertResolved
|
||||
}
|
||||
|
||||
type NotificationChannel interface {
|
||||
notify.Notifier
|
||||
notify.ResolvedSender
|
||||
}
|
||||
type NotificationChannelConfig struct {
|
||||
OrgID int64 // only used internally
|
||||
UID string `json:"uid"`
|
||||
|
@ -2,6 +2,7 @@ package channels
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@ -25,32 +26,52 @@ const (
|
||||
victoropsAlertStateRecovery = "RECOVERY"
|
||||
)
|
||||
|
||||
type VictorOpsConfig struct {
|
||||
*NotificationChannelConfig
|
||||
URL string
|
||||
MessageType string
|
||||
}
|
||||
|
||||
func VictorOpsFactory(fc FactoryConfig) (NotificationChannel, error) {
|
||||
cfg, err := NewVictorOpsConfig(fc.Config)
|
||||
if err != nil {
|
||||
return nil, receiverInitError{
|
||||
Reason: err.Error(),
|
||||
Cfg: *fc.Config,
|
||||
}
|
||||
}
|
||||
return NewVictoropsNotifier(cfg, fc.NotificationService, fc.Template), nil
|
||||
}
|
||||
|
||||
func NewVictorOpsConfig(config *NotificationChannelConfig) (*VictorOpsConfig, error) {
|
||||
url := config.Settings.Get("url").MustString()
|
||||
if url == "" {
|
||||
return nil, errors.New("could not find victorops url property in settings")
|
||||
}
|
||||
return &VictorOpsConfig{
|
||||
NotificationChannelConfig: config,
|
||||
URL: url,
|
||||
MessageType: config.Settings.Get("messageType").MustString(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// NewVictoropsNotifier creates an instance of VictoropsNotifier that
|
||||
// handles posting notifications to Victorops REST API
|
||||
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"}
|
||||
}
|
||||
|
||||
url := model.Settings.Get("url").MustString()
|
||||
if url == "" {
|
||||
return nil, receiverInitError{Cfg: *model, Reason: "could not find victorops url property in settings"}
|
||||
}
|
||||
|
||||
func NewVictoropsNotifier(config *VictorOpsConfig, ns notifications.WebhookSender, t *template.Template) *VictoropsNotifier {
|
||||
return &VictoropsNotifier{
|
||||
Base: NewBase(&models.AlertNotification{
|
||||
Uid: model.UID,
|
||||
Name: model.Name,
|
||||
Type: model.Type,
|
||||
DisableResolveMessage: model.DisableResolveMessage,
|
||||
Settings: model.Settings,
|
||||
Uid: config.UID,
|
||||
Name: config.Name,
|
||||
Type: config.Type,
|
||||
DisableResolveMessage: config.DisableResolveMessage,
|
||||
Settings: config.Settings,
|
||||
}),
|
||||
URL: url,
|
||||
MessageType: model.Settings.Get("messageType").MustString(),
|
||||
URL: config.URL,
|
||||
MessageType: config.MessageType,
|
||||
log: log.New("alerting.notifier.victorops"),
|
||||
ns: ns,
|
||||
tmpl: t,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
// VictoropsNotifier defines URL property for Victorops REST API
|
||||
|
@ -78,7 +78,7 @@ func TestVictoropsNotifier(t *testing.T) {
|
||||
}, {
|
||||
name: "Error in initing, no URL",
|
||||
settings: `{}`,
|
||||
expInitError: `failed to validate receiver "victorops_testing" of type "victorops": could not find victorops url property in settings`,
|
||||
expInitError: `could not find victorops url property in settings`,
|
||||
},
|
||||
}
|
||||
|
||||
@ -94,7 +94,7 @@ func TestVictoropsNotifier(t *testing.T) {
|
||||
}
|
||||
|
||||
webhookSender := mockNotificationService()
|
||||
pn, err := NewVictoropsNotifier(m, webhookSender, tmpl)
|
||||
cfg, err := NewVictorOpsConfig(m)
|
||||
if c.expInitError != "" {
|
||||
require.Error(t, err)
|
||||
require.Equal(t, c.expInitError, err.Error())
|
||||
@ -104,6 +104,7 @@ func TestVictoropsNotifier(t *testing.T) {
|
||||
|
||||
ctx := notify.WithGroupKey(context.Background(), "alertname")
|
||||
ctx = notify.WithGroupLabels(ctx, model.LabelSet{"alertname": ""})
|
||||
pn := NewVictoropsNotifier(cfg, webhookSender, tmpl)
|
||||
ok, err := pn.Notify(ctx, c.alerts...)
|
||||
if c.expMsgError != nil {
|
||||
require.False(t, ok)
|
||||
|
@ -3,6 +3,7 @@ package channels
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
@ -28,37 +29,62 @@ type WebhookNotifier struct {
|
||||
orgID int64
|
||||
}
|
||||
|
||||
type WebhookConfig struct {
|
||||
*NotificationChannelConfig
|
||||
URL string
|
||||
User string
|
||||
Password string
|
||||
HTTPMethod string
|
||||
MaxAlerts int
|
||||
}
|
||||
|
||||
func WebHookFactory(fc FactoryConfig) (NotificationChannel, error) {
|
||||
cfg, err := NewWebHookConfig(fc.Config, fc.DecryptFunc)
|
||||
if err != nil {
|
||||
return nil, receiverInitError{
|
||||
Reason: err.Error(),
|
||||
Cfg: *fc.Config,
|
||||
}
|
||||
}
|
||||
return NewWebHookNotifier(cfg, fc.NotificationService, fc.Template), nil
|
||||
}
|
||||
|
||||
func NewWebHookConfig(config *NotificationChannelConfig, decryptFunc GetDecryptedValueFn) (*WebhookConfig, error) {
|
||||
url := config.Settings.Get("url").MustString()
|
||||
if url == "" {
|
||||
return nil, errors.New("could not find url property in settings")
|
||||
}
|
||||
return &WebhookConfig{
|
||||
NotificationChannelConfig: config,
|
||||
URL: url,
|
||||
User: config.Settings.Get("username").MustString(),
|
||||
Password: decryptFunc(context.Background(), config.SecureSettings, "password", config.Settings.Get("password").MustString()),
|
||||
HTTPMethod: config.Settings.Get("httpMethod").MustString("POST"),
|
||||
MaxAlerts: config.Settings.Get("maxAlerts").MustInt(0),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// NewWebHookNotifier is the constructor for
|
||||
// the WebHook notifier.
|
||||
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"}
|
||||
}
|
||||
if model.SecureSettings == nil {
|
||||
return nil, receiverInitError{Cfg: *model, Reason: "no secure settings supplied"}
|
||||
}
|
||||
url := model.Settings.Get("url").MustString()
|
||||
if url == "" {
|
||||
return nil, receiverInitError{Cfg: *model, Reason: "could not find url property in settings"}
|
||||
}
|
||||
func NewWebHookNotifier(config *WebhookConfig, ns notifications.WebhookSender, t *template.Template) *WebhookNotifier {
|
||||
return &WebhookNotifier{
|
||||
Base: NewBase(&models.AlertNotification{
|
||||
Uid: model.UID,
|
||||
Name: model.Name,
|
||||
Type: model.Type,
|
||||
DisableResolveMessage: model.DisableResolveMessage,
|
||||
Settings: model.Settings,
|
||||
Uid: config.UID,
|
||||
Name: config.Name,
|
||||
Type: config.Type,
|
||||
DisableResolveMessage: config.DisableResolveMessage,
|
||||
Settings: config.Settings,
|
||||
}),
|
||||
orgID: model.OrgID,
|
||||
URL: url,
|
||||
User: model.Settings.Get("username").MustString(),
|
||||
Password: fn(context.Background(), model.SecureSettings, "password", model.Settings.Get("password").MustString()),
|
||||
HTTPMethod: model.Settings.Get("httpMethod").MustString("POST"),
|
||||
MaxAlerts: model.Settings.Get("maxAlerts").MustInt(0),
|
||||
orgID: config.OrgID,
|
||||
URL: config.URL,
|
||||
User: config.User,
|
||||
Password: config.Password,
|
||||
HTTPMethod: config.HTTPMethod,
|
||||
MaxAlerts: config.MaxAlerts,
|
||||
log: log.New("alerting.notifier.webhook"),
|
||||
ns: ns,
|
||||
tmpl: t,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
// webhookMessage defines the JSON object send to webhook endpoints.
|
||||
|
@ -172,7 +172,7 @@ func TestWebhookNotifier(t *testing.T) {
|
||||
}, {
|
||||
name: "Error in initing",
|
||||
settings: `{}`,
|
||||
expInitError: `failed to validate receiver "webhook_testing" of type "webhook": could not find url property in settings`,
|
||||
expInitError: `could not find url property in settings`,
|
||||
},
|
||||
}
|
||||
|
||||
@ -193,7 +193,7 @@ func TestWebhookNotifier(t *testing.T) {
|
||||
webhookSender := mockNotificationService()
|
||||
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
|
||||
decryptFn := secretsService.GetDecryptedValue
|
||||
pn, err := NewWebHookNotifier(m, webhookSender, tmpl, decryptFn)
|
||||
cfg, err := NewWebHookConfig(m, decryptFn)
|
||||
if c.expInitError != "" {
|
||||
require.Error(t, err)
|
||||
require.Equal(t, c.expInitError, err.Error())
|
||||
@ -204,6 +204,7 @@ func TestWebhookNotifier(t *testing.T) {
|
||||
ctx := notify.WithGroupKey(context.Background(), "alertname")
|
||||
ctx = notify.WithGroupLabels(ctx, model.LabelSet{"alertname": ""})
|
||||
ctx = notify.WithReceiverName(ctx, "my_receiver")
|
||||
pn := NewWebHookNotifier(cfg, webhookSender, tmpl)
|
||||
ok, err := pn.Notify(ctx, c.alerts...)
|
||||
if c.expMsgError != nil {
|
||||
require.False(t, ok)
|
||||
|
@ -3,6 +3,7 @@ package channels
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/prometheus/alertmanager/types"
|
||||
@ -13,28 +14,51 @@ import (
|
||||
"github.com/prometheus/alertmanager/template"
|
||||
)
|
||||
|
||||
// NewWeComNotifier is the constructor for WeCom notifier.
|
||||
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())
|
||||
type WeComConfig struct {
|
||||
*NotificationChannelConfig
|
||||
URL string
|
||||
Message string
|
||||
}
|
||||
|
||||
if url == "" {
|
||||
return nil, receiverInitError{Cfg: *model, Reason: "could not find webhook URL in settings"}
|
||||
func WeComFactory(fc FactoryConfig) (NotificationChannel, error) {
|
||||
cfg, err := NewWeComConfig(fc.Config, fc.DecryptFunc)
|
||||
if err != nil {
|
||||
return nil, receiverInitError{
|
||||
Reason: err.Error(),
|
||||
Cfg: *fc.Config,
|
||||
}
|
||||
}
|
||||
return NewWeComNotifier(cfg, fc.NotificationService, fc.Template), nil
|
||||
}
|
||||
|
||||
func NewWeComConfig(config *NotificationChannelConfig, decryptFunc GetDecryptedValueFn) (*WeComConfig, error) {
|
||||
url := decryptFunc(context.Background(), config.SecureSettings, "url", config.Settings.Get("url").MustString())
|
||||
if url == "" {
|
||||
return nil, errors.New("could not find webhook URL in settings")
|
||||
}
|
||||
return &WeComConfig{
|
||||
NotificationChannelConfig: config,
|
||||
URL: url,
|
||||
Message: config.Settings.Get("message").MustString(`{{ template "default.message" .}}`),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// NewWeComNotifier is the constructor for WeCom notifier.
|
||||
func NewWeComNotifier(config *WeComConfig, ns notifications.WebhookSender, t *template.Template) *WeComNotifier {
|
||||
return &WeComNotifier{
|
||||
Base: NewBase(&models.AlertNotification{
|
||||
Uid: model.UID,
|
||||
Name: model.Name,
|
||||
Type: model.Type,
|
||||
DisableResolveMessage: model.DisableResolveMessage,
|
||||
Settings: model.Settings,
|
||||
Uid: config.UID,
|
||||
Name: config.Name,
|
||||
Type: config.Type,
|
||||
DisableResolveMessage: config.DisableResolveMessage,
|
||||
Settings: config.Settings,
|
||||
}),
|
||||
URL: url,
|
||||
URL: config.URL,
|
||||
Message: config.Message,
|
||||
log: log.New("alerting.notifier.wecom"),
|
||||
ns: ns,
|
||||
Message: model.Settings.Get("message").MustString(`{{ template "default.message" .}}`),
|
||||
tmpl: t,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
// WeComNotifier is responsible for sending alert notifications to WeCom.
|
||||
|
@ -79,7 +79,7 @@ func TestWeComNotifier(t *testing.T) {
|
||||
}, {
|
||||
name: "Error in initing",
|
||||
settings: `{}`,
|
||||
expInitError: `failed to validate receiver "wecom_testing" of type "wecom": could not find webhook URL in settings`,
|
||||
expInitError: `could not find webhook URL in settings`,
|
||||
},
|
||||
}
|
||||
|
||||
@ -97,7 +97,7 @@ func TestWeComNotifier(t *testing.T) {
|
||||
webhookSender := mockNotificationService()
|
||||
secretsService := secretsManager.SetupTestService(t, fakes.NewFakeSecretsStore())
|
||||
decryptFn := secretsService.GetDecryptedValue
|
||||
pn, err := NewWeComNotifier(m, webhookSender, tmpl, decryptFn)
|
||||
cfg, err := NewWeComConfig(m, decryptFn)
|
||||
if c.expInitError != "" {
|
||||
require.Equal(t, c.expInitError, err.Error())
|
||||
return
|
||||
@ -106,6 +106,7 @@ func TestWeComNotifier(t *testing.T) {
|
||||
|
||||
ctx := notify.WithGroupKey(context.Background(), "alertname")
|
||||
ctx = notify.WithGroupLabels(ctx, model.LabelSet{"alertname": ""})
|
||||
pn := NewWeComNotifier(cfg, webhookSender, tmpl)
|
||||
ok, err := pn.Notify(ctx, c.alerts...)
|
||||
if c.expMsgError != nil {
|
||||
require.False(t, ok)
|
||||
|
@ -490,46 +490,15 @@ func (m *migration) validateAlertmanagerConfig(orgID int64, config *PostableUser
|
||||
}
|
||||
return fallback
|
||||
}
|
||||
|
||||
switch gr.Type {
|
||||
case "email":
|
||||
_, err = channels.NewEmailNotifier(cfg, nil, nil) // Email notifier already has a default template.
|
||||
case "pagerduty":
|
||||
_, err = channels.NewPagerdutyNotifier(cfg, nil, nil, decryptFunc)
|
||||
case "pushover":
|
||||
_, err = channels.NewPushoverNotifier(cfg, nil, nil, decryptFunc)
|
||||
case "slack":
|
||||
_, err = channels.NewSlackNotifier(cfg, nil, decryptFunc)
|
||||
case "telegram":
|
||||
_, err = channels.NewTelegramNotifier(cfg, nil, nil, decryptFunc)
|
||||
case "victorops":
|
||||
_, err = channels.NewVictoropsNotifier(cfg, nil, nil)
|
||||
case "teams":
|
||||
_, err = channels.NewTeamsNotifier(cfg, nil, nil)
|
||||
case "dingding":
|
||||
_, err = channels.NewDingDingNotifier(cfg, nil, nil)
|
||||
case "kafka":
|
||||
_, err = channels.NewKafkaNotifier(cfg, nil, nil)
|
||||
case "webhook":
|
||||
_, err = channels.NewWebHookNotifier(cfg, nil, nil, decryptFunc)
|
||||
case "sensugo":
|
||||
_, err = channels.NewSensuGoNotifier(cfg, nil, nil, decryptFunc)
|
||||
case "discord":
|
||||
_, err = channels.NewDiscordNotifier(cfg, nil, nil)
|
||||
case "googlechat":
|
||||
_, err = channels.NewGoogleChatNotifier(cfg, nil, nil)
|
||||
case "LINE":
|
||||
_, err = channels.NewLineNotifier(cfg, nil, nil, decryptFunc)
|
||||
case "threema":
|
||||
_, err = channels.NewThreemaNotifier(cfg, nil, nil, decryptFunc)
|
||||
case "opsgenie":
|
||||
_, err = channels.NewOpsgenieNotifier(cfg, nil, nil, decryptFunc)
|
||||
case "prometheus-alertmanager":
|
||||
_, err = channels.NewAlertmanagerNotifier(cfg, nil, decryptFunc)
|
||||
default:
|
||||
receiverFactory, exists := channels.Factory(gr.Type)
|
||||
if !exists {
|
||||
return fmt.Errorf("notifier %s is not supported", gr.Type)
|
||||
}
|
||||
|
||||
factoryConfig, err := channels.NewFactoryConfig(cfg, nil, decryptFunc, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = receiverFactory(factoryConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ func Test_validateAlertmanagerConfig(t *testing.T) {
|
||||
SecureSettings: map[string]string{"url": invalidUri},
|
||||
},
|
||||
},
|
||||
err: fmt.Errorf("failed to validate receiver \"SlackWithBadURL\" of type \"slack\": invalid URL %q: parse %q: net/url: invalid control character in URL", invalidUri, invalidUri),
|
||||
err: fmt.Errorf("failed to validate receiver \"SlackWithBadURL\" of type \"slack\": invalid URL %q", invalidUri),
|
||||
},
|
||||
{
|
||||
name: "when a slack receiver has an invalid recipient - it should not error",
|
||||
|
Loading…
Reference in New Issue
Block a user