mirror of https://github.com/grafana/grafana
Merge pull request #14229 from pbakulev/configurable-alert-notification
Configurable alert notificationpull/15107/head
commit
c6f80ecec2
@ -0,0 +1,25 @@ |
||||
# # config file version |
||||
apiVersion: 1 |
||||
|
||||
# notifiers: |
||||
# - name: default-slack-temp |
||||
# type: slack |
||||
# org_name: Main Org. |
||||
# is_default: true |
||||
# uid: notifier1 |
||||
# settings: |
||||
# recipient: "XXX" |
||||
# token: "xoxb" |
||||
# uploadImage: true |
||||
# url: https://slack.com |
||||
# - name: default-email |
||||
# type: email |
||||
# org_id: 1 |
||||
# uid: notifier2 |
||||
# is_default: false |
||||
# settings: |
||||
# addresses: example11111@example.com |
||||
# delete_notifiers: |
||||
# - name: default-slack-temp |
||||
# org_name: Main Org. |
||||
# uid: notifier1 |
||||
@ -0,0 +1,180 @@ |
||||
package notifiers |
||||
|
||||
import ( |
||||
"errors" |
||||
|
||||
"github.com/grafana/grafana/pkg/bus" |
||||
"github.com/grafana/grafana/pkg/log" |
||||
"github.com/grafana/grafana/pkg/models" |
||||
) |
||||
|
||||
var ( |
||||
ErrInvalidConfigTooManyDefault = errors.New("Alert notification provisioning config is invalid. Only one alert notification can be marked as default") |
||||
) |
||||
|
||||
func Provision(configDirectory string) error { |
||||
dc := newNotificationProvisioner(log.New("provisioning.notifiers")) |
||||
return dc.applyChanges(configDirectory) |
||||
} |
||||
|
||||
type NotificationProvisioner struct { |
||||
log log.Logger |
||||
cfgProvider *configReader |
||||
} |
||||
|
||||
func newNotificationProvisioner(log log.Logger) NotificationProvisioner { |
||||
return NotificationProvisioner{ |
||||
log: log, |
||||
cfgProvider: &configReader{log: log}, |
||||
} |
||||
} |
||||
|
||||
func (dc *NotificationProvisioner) apply(cfg *notificationsAsConfig) error { |
||||
if err := dc.deleteNotifications(cfg.DeleteNotifications); err != nil { |
||||
return err |
||||
} |
||||
|
||||
if err := dc.mergeNotifications(cfg.Notifications); err != nil { |
||||
return err |
||||
} |
||||
|
||||
return nil |
||||
} |
||||
|
||||
func (dc *NotificationProvisioner) deleteNotifications(notificationToDelete []*deleteNotificationConfig) error { |
||||
for _, notification := range notificationToDelete { |
||||
dc.log.Info("Deleting alert notification", "name", notification.Name, "uid", notification.Uid) |
||||
|
||||
if notification.OrgId == 0 && notification.OrgName != "" { |
||||
getOrg := &models.GetOrgByNameQuery{Name: notification.OrgName} |
||||
if err := bus.Dispatch(getOrg); err != nil { |
||||
return err |
||||
} |
||||
notification.OrgId = getOrg.Result.Id |
||||
} else if notification.OrgId < 0 { |
||||
notification.OrgId = 1 |
||||
} |
||||
|
||||
getNotification := &models.GetAlertNotificationsWithUidQuery{Uid: notification.Uid, OrgId: notification.OrgId} |
||||
|
||||
if err := bus.Dispatch(getNotification); err != nil { |
||||
return err |
||||
} |
||||
|
||||
if getNotification.Result != nil { |
||||
cmd := &models.DeleteAlertNotificationWithUidCommand{Uid: getNotification.Result.Uid, OrgId: getNotification.OrgId} |
||||
if err := bus.Dispatch(cmd); err != nil { |
||||
return err |
||||
} |
||||
} |
||||
} |
||||
|
||||
return nil |
||||
} |
||||
|
||||
func (dc *NotificationProvisioner) mergeNotifications(notificationToMerge []*notificationFromConfig) error { |
||||
for _, notification := range notificationToMerge { |
||||
|
||||
if notification.OrgId == 0 && notification.OrgName != "" { |
||||
getOrg := &models.GetOrgByNameQuery{Name: notification.OrgName} |
||||
if err := bus.Dispatch(getOrg); err != nil { |
||||
return err |
||||
} |
||||
notification.OrgId = getOrg.Result.Id |
||||
} else if notification.OrgId < 0 { |
||||
notification.OrgId = 1 |
||||
} |
||||
|
||||
cmd := &models.GetAlertNotificationsWithUidQuery{OrgId: notification.OrgId, Uid: notification.Uid} |
||||
err := bus.Dispatch(cmd) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
|
||||
if cmd.Result == nil { |
||||
dc.log.Info("Inserting alert notification from configuration ", "name", notification.Name, "uid", notification.Uid) |
||||
insertCmd := &models.CreateAlertNotificationCommand{ |
||||
Uid: notification.Uid, |
||||
Name: notification.Name, |
||||
Type: notification.Type, |
||||
IsDefault: notification.IsDefault, |
||||
Settings: notification.SettingsToJson(), |
||||
OrgId: notification.OrgId, |
||||
DisableResolveMessage: notification.DisableResolveMessage, |
||||
Frequency: notification.Frequency, |
||||
SendReminder: notification.SendReminder, |
||||
} |
||||
|
||||
if err := bus.Dispatch(insertCmd); err != nil { |
||||
return err |
||||
} |
||||
} else { |
||||
dc.log.Info("Updating alert notification from configuration", "name", notification.Name) |
||||
updateCmd := &models.UpdateAlertNotificationWithUidCommand{ |
||||
Uid: notification.Uid, |
||||
Name: notification.Name, |
||||
Type: notification.Type, |
||||
IsDefault: notification.IsDefault, |
||||
Settings: notification.SettingsToJson(), |
||||
OrgId: notification.OrgId, |
||||
DisableResolveMessage: notification.DisableResolveMessage, |
||||
Frequency: notification.Frequency, |
||||
SendReminder: notification.SendReminder, |
||||
} |
||||
|
||||
if err := bus.Dispatch(updateCmd); err != nil { |
||||
return err |
||||
} |
||||
} |
||||
} |
||||
|
||||
return nil |
||||
} |
||||
|
||||
func (cfg *notificationsAsConfig) mapToNotificationFromConfig() *notificationsAsConfig { |
||||
r := ¬ificationsAsConfig{} |
||||
if cfg == nil { |
||||
return r |
||||
} |
||||
|
||||
for _, notification := range cfg.Notifications { |
||||
r.Notifications = append(r.Notifications, ¬ificationFromConfig{ |
||||
Uid: notification.Uid, |
||||
OrgId: notification.OrgId, |
||||
OrgName: notification.OrgName, |
||||
Name: notification.Name, |
||||
Type: notification.Type, |
||||
IsDefault: notification.IsDefault, |
||||
Settings: notification.Settings, |
||||
DisableResolveMessage: notification.DisableResolveMessage, |
||||
Frequency: notification.Frequency, |
||||
SendReminder: notification.SendReminder, |
||||
}) |
||||
} |
||||
|
||||
for _, notification := range cfg.DeleteNotifications { |
||||
r.DeleteNotifications = append(r.DeleteNotifications, &deleteNotificationConfig{ |
||||
Uid: notification.Uid, |
||||
OrgId: notification.OrgId, |
||||
OrgName: notification.OrgName, |
||||
Name: notification.Name, |
||||
}) |
||||
} |
||||
|
||||
return r |
||||
} |
||||
|
||||
func (dc *NotificationProvisioner) applyChanges(configPath string) error { |
||||
configs, err := dc.cfgProvider.readConfig(configPath) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
|
||||
for _, cfg := range configs { |
||||
if err := dc.apply(cfg); err != nil { |
||||
return err |
||||
} |
||||
} |
||||
|
||||
return nil |
||||
} |
||||
@ -0,0 +1,163 @@ |
||||
package notifiers |
||||
|
||||
import ( |
||||
"fmt" |
||||
"io/ioutil" |
||||
"os" |
||||
"path/filepath" |
||||
"strings" |
||||
|
||||
"github.com/grafana/grafana/pkg/log" |
||||
m "github.com/grafana/grafana/pkg/models" |
||||
"github.com/grafana/grafana/pkg/services/alerting" |
||||
"gopkg.in/yaml.v2" |
||||
) |
||||
|
||||
type configReader struct { |
||||
log log.Logger |
||||
} |
||||
|
||||
func (cr *configReader) readConfig(path string) ([]*notificationsAsConfig, error) { |
||||
var notifications []*notificationsAsConfig |
||||
cr.log.Debug("Looking for alert notification provisioning files", "path", path) |
||||
|
||||
files, err := ioutil.ReadDir(path) |
||||
if err != nil { |
||||
cr.log.Error("Can't read alert notification provisioning files from directory", "path", path) |
||||
return notifications, nil |
||||
} |
||||
|
||||
for _, file := range files { |
||||
if strings.HasSuffix(file.Name(), ".yaml") || strings.HasSuffix(file.Name(), ".yml") { |
||||
cr.log.Debug("Parsing alert notifications provisioning file", "path", path, "file.Name", file.Name()) |
||||
notifs, err := cr.parseNotificationConfig(path, file) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
if notifs != nil { |
||||
notifications = append(notifications, notifs) |
||||
} |
||||
} |
||||
} |
||||
|
||||
cr.log.Debug("Validating alert notifications") |
||||
if err = validateRequiredField(notifications); err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
checkOrgIdAndOrgName(notifications) |
||||
|
||||
err = validateNotifications(notifications) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
return notifications, nil |
||||
} |
||||
|
||||
func (cr *configReader) parseNotificationConfig(path string, file os.FileInfo) (*notificationsAsConfig, error) { |
||||
filename, _ := filepath.Abs(filepath.Join(path, file.Name())) |
||||
yamlFile, err := ioutil.ReadFile(filename) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
var cfg *notificationsAsConfig |
||||
err = yaml.Unmarshal(yamlFile, &cfg) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
return cfg.mapToNotificationFromConfig(), nil |
||||
} |
||||
|
||||
func checkOrgIdAndOrgName(notifications []*notificationsAsConfig) { |
||||
for i := range notifications { |
||||
for _, notification := range notifications[i].Notifications { |
||||
if notification.OrgId < 1 { |
||||
if notification.OrgName == "" { |
||||
notification.OrgId = 1 |
||||
} else { |
||||
notification.OrgId = 0 |
||||
} |
||||
} |
||||
} |
||||
|
||||
for _, notification := range notifications[i].DeleteNotifications { |
||||
if notification.OrgId < 1 { |
||||
if notification.OrgName == "" { |
||||
notification.OrgId = 1 |
||||
} else { |
||||
notification.OrgId = 0 |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
func validateRequiredField(notifications []*notificationsAsConfig) error { |
||||
for i := range notifications { |
||||
var errStrings []string |
||||
for index, notification := range notifications[i].Notifications { |
||||
if notification.Name == "" { |
||||
errStrings = append( |
||||
errStrings, |
||||
fmt.Sprintf("Added alert notification item %d in configuration doesn't contain required field name", index+1), |
||||
) |
||||
} |
||||
|
||||
if notification.Uid == "" { |
||||
errStrings = append( |
||||
errStrings, |
||||
fmt.Sprintf("Added alert notification item %d in configuration doesn't contain required field uid", index+1), |
||||
) |
||||
} |
||||
} |
||||
|
||||
for index, notification := range notifications[i].DeleteNotifications { |
||||
if notification.Name == "" { |
||||
errStrings = append( |
||||
errStrings, |
||||
fmt.Sprintf("Deleted alert notification item %d in configuration doesn't contain required field name", index+1), |
||||
) |
||||
} |
||||
|
||||
if notification.Uid == "" { |
||||
errStrings = append( |
||||
errStrings, |
||||
fmt.Sprintf("Deleted alert notification item %d in configuration doesn't contain required field uid", index+1), |
||||
) |
||||
} |
||||
} |
||||
|
||||
if len(errStrings) != 0 { |
||||
return fmt.Errorf(strings.Join(errStrings, "\n")) |
||||
} |
||||
} |
||||
|
||||
return nil |
||||
} |
||||
|
||||
func validateNotifications(notifications []*notificationsAsConfig) error { |
||||
|
||||
for i := range notifications { |
||||
if notifications[i].Notifications == nil { |
||||
continue |
||||
} |
||||
|
||||
for _, notification := range notifications[i].Notifications { |
||||
_, err := alerting.InitNotifier(&m.AlertNotification{ |
||||
Name: notification.Name, |
||||
Settings: notification.SettingsToJson(), |
||||
Type: notification.Type, |
||||
}) |
||||
|
||||
if err != nil { |
||||
return err |
||||
} |
||||
} |
||||
} |
||||
|
||||
return nil |
||||
} |
||||
@ -0,0 +1,313 @@ |
||||
package notifiers |
||||
|
||||
import ( |
||||
"testing" |
||||
|
||||
"github.com/grafana/grafana/pkg/log" |
||||
m "github.com/grafana/grafana/pkg/models" |
||||
"github.com/grafana/grafana/pkg/services/alerting" |
||||
"github.com/grafana/grafana/pkg/services/alerting/notifiers" |
||||
"github.com/grafana/grafana/pkg/services/sqlstore" |
||||
. "github.com/smartystreets/goconvey/convey" |
||||
) |
||||
|
||||
var ( |
||||
correct_properties = "./testdata/test-configs/correct-properties" |
||||
incorrect_settings = "./testdata/test-configs/incorrect-settings" |
||||
no_required_fields = "./testdata/test-configs/no-required-fields" |
||||
correct_properties_with_orgName = "./testdata/test-configs/correct-properties-with-orgName" |
||||
brokenYaml = "./testdata/test-configs/broken-yaml" |
||||
doubleNotificationsConfig = "./testdata/test-configs/double-default" |
||||
emptyFolder = "./testdata/test-configs/empty_folder" |
||||
emptyFile = "./testdata/test-configs/empty" |
||||
twoNotificationsConfig = "./testdata/test-configs/two-notifications" |
||||
unknownNotifier = "./testdata/test-configs/unknown-notifier" |
||||
) |
||||
|
||||
func TestNotificationAsConfig(t *testing.T) { |
||||
logger := log.New("fake.log") |
||||
|
||||
Convey("Testing notification as configuration", t, func() { |
||||
sqlstore.InitTestDB(t) |
||||
|
||||
alerting.RegisterNotifier(&alerting.NotifierPlugin{ |
||||
Type: "slack", |
||||
Name: "slack", |
||||
Factory: notifiers.NewSlackNotifier, |
||||
}) |
||||
|
||||
alerting.RegisterNotifier(&alerting.NotifierPlugin{ |
||||
Type: "email", |
||||
Name: "email", |
||||
Factory: notifiers.NewEmailNotifier, |
||||
}) |
||||
|
||||
Convey("Can read correct properties", func() { |
||||
cfgProvifer := &configReader{log: log.New("test logger")} |
||||
cfg, err := cfgProvifer.readConfig(correct_properties) |
||||
if err != nil { |
||||
t.Fatalf("readConfig return an error %v", err) |
||||
} |
||||
So(len(cfg), ShouldEqual, 1) |
||||
|
||||
ntCfg := cfg[0] |
||||
nts := ntCfg.Notifications |
||||
So(len(nts), ShouldEqual, 4) |
||||
|
||||
nt := nts[0] |
||||
So(nt.Name, ShouldEqual, "default-slack-notification") |
||||
So(nt.Type, ShouldEqual, "slack") |
||||
So(nt.OrgId, ShouldEqual, 2) |
||||
So(nt.Uid, ShouldEqual, "notifier1") |
||||
So(nt.IsDefault, ShouldBeTrue) |
||||
So(nt.Settings, ShouldResemble, map[string]interface{}{ |
||||
"recipient": "XXX", "token": "xoxb", "uploadImage": true, "url": "https://slack.com", |
||||
}) |
||||
|
||||
nt = nts[1] |
||||
So(nt.Name, ShouldEqual, "another-not-default-notification") |
||||
So(nt.Type, ShouldEqual, "email") |
||||
So(nt.OrgId, ShouldEqual, 3) |
||||
So(nt.Uid, ShouldEqual, "notifier2") |
||||
So(nt.IsDefault, ShouldBeFalse) |
||||
|
||||
nt = nts[2] |
||||
So(nt.Name, ShouldEqual, "check-unset-is_default-is-false") |
||||
So(nt.Type, ShouldEqual, "slack") |
||||
So(nt.OrgId, ShouldEqual, 3) |
||||
So(nt.Uid, ShouldEqual, "notifier3") |
||||
So(nt.IsDefault, ShouldBeFalse) |
||||
|
||||
nt = nts[3] |
||||
So(nt.Name, ShouldEqual, "Added notification with whitespaces in name") |
||||
So(nt.Type, ShouldEqual, "email") |
||||
So(nt.Uid, ShouldEqual, "notifier4") |
||||
So(nt.OrgId, ShouldEqual, 3) |
||||
|
||||
deleteNts := ntCfg.DeleteNotifications |
||||
So(len(deleteNts), ShouldEqual, 4) |
||||
|
||||
deleteNt := deleteNts[0] |
||||
So(deleteNt.Name, ShouldEqual, "default-slack-notification") |
||||
So(deleteNt.Uid, ShouldEqual, "notifier1") |
||||
So(deleteNt.OrgId, ShouldEqual, 2) |
||||
|
||||
deleteNt = deleteNts[1] |
||||
So(deleteNt.Name, ShouldEqual, "deleted-notification-without-orgId") |
||||
So(deleteNt.OrgId, ShouldEqual, 1) |
||||
So(deleteNt.Uid, ShouldEqual, "notifier2") |
||||
|
||||
deleteNt = deleteNts[2] |
||||
So(deleteNt.Name, ShouldEqual, "deleted-notification-with-0-orgId") |
||||
So(deleteNt.OrgId, ShouldEqual, 1) |
||||
So(deleteNt.Uid, ShouldEqual, "notifier3") |
||||
|
||||
deleteNt = deleteNts[3] |
||||
So(deleteNt.Name, ShouldEqual, "Deleted notification with whitespaces in name") |
||||
So(deleteNt.OrgId, ShouldEqual, 1) |
||||
So(deleteNt.Uid, ShouldEqual, "notifier4") |
||||
}) |
||||
|
||||
Convey("One configured notification", func() { |
||||
Convey("no notification in database", func() { |
||||
dc := newNotificationProvisioner(logger) |
||||
err := dc.applyChanges(twoNotificationsConfig) |
||||
if err != nil { |
||||
t.Fatalf("applyChanges return an error %v", err) |
||||
} |
||||
notificationsQuery := m.GetAllAlertNotificationsQuery{OrgId: 1} |
||||
err = sqlstore.GetAllAlertNotifications(¬ificationsQuery) |
||||
So(err, ShouldBeNil) |
||||
So(notificationsQuery.Result, ShouldNotBeNil) |
||||
So(len(notificationsQuery.Result), ShouldEqual, 2) |
||||
}) |
||||
|
||||
Convey("One notification in database with same name and uid", func() { |
||||
existingNotificationCmd := m.CreateAlertNotificationCommand{ |
||||
Name: "channel1", |
||||
OrgId: 1, |
||||
Uid: "notifier1", |
||||
Type: "slack", |
||||
} |
||||
err := sqlstore.CreateAlertNotificationCommand(&existingNotificationCmd) |
||||
So(err, ShouldBeNil) |
||||
So(existingNotificationCmd.Result, ShouldNotBeNil) |
||||
notificationsQuery := m.GetAllAlertNotificationsQuery{OrgId: 1} |
||||
err = sqlstore.GetAllAlertNotifications(¬ificationsQuery) |
||||
So(err, ShouldBeNil) |
||||
So(notificationsQuery.Result, ShouldNotBeNil) |
||||
So(len(notificationsQuery.Result), ShouldEqual, 1) |
||||
|
||||
Convey("should update one notification", func() { |
||||
dc := newNotificationProvisioner(logger) |
||||
err = dc.applyChanges(twoNotificationsConfig) |
||||
if err != nil { |
||||
t.Fatalf("applyChanges return an error %v", err) |
||||
} |
||||
err = sqlstore.GetAllAlertNotifications(¬ificationsQuery) |
||||
So(err, ShouldBeNil) |
||||
So(notificationsQuery.Result, ShouldNotBeNil) |
||||
So(len(notificationsQuery.Result), ShouldEqual, 2) |
||||
|
||||
nts := notificationsQuery.Result |
||||
nt1 := nts[0] |
||||
So(nt1.Type, ShouldEqual, "email") |
||||
So(nt1.Name, ShouldEqual, "channel1") |
||||
So(nt1.Uid, ShouldEqual, "notifier1") |
||||
|
||||
nt2 := nts[1] |
||||
So(nt2.Type, ShouldEqual, "slack") |
||||
So(nt2.Name, ShouldEqual, "channel2") |
||||
So(nt2.Uid, ShouldEqual, "notifier2") |
||||
}) |
||||
}) |
||||
Convey("Two notifications with is_default", func() { |
||||
dc := newNotificationProvisioner(logger) |
||||
err := dc.applyChanges(doubleNotificationsConfig) |
||||
Convey("should both be inserted", func() { |
||||
So(err, ShouldBeNil) |
||||
notificationsQuery := m.GetAllAlertNotificationsQuery{OrgId: 1} |
||||
err = sqlstore.GetAllAlertNotifications(¬ificationsQuery) |
||||
So(err, ShouldBeNil) |
||||
So(notificationsQuery.Result, ShouldNotBeNil) |
||||
So(len(notificationsQuery.Result), ShouldEqual, 2) |
||||
|
||||
So(notificationsQuery.Result[0].IsDefault, ShouldBeTrue) |
||||
So(notificationsQuery.Result[1].IsDefault, ShouldBeTrue) |
||||
}) |
||||
}) |
||||
}) |
||||
|
||||
Convey("Two configured notification", func() { |
||||
Convey("two other notifications in database", func() { |
||||
existingNotificationCmd := m.CreateAlertNotificationCommand{ |
||||
Name: "channel0", |
||||
OrgId: 1, |
||||
Uid: "notifier0", |
||||
Type: "slack", |
||||
} |
||||
err := sqlstore.CreateAlertNotificationCommand(&existingNotificationCmd) |
||||
So(err, ShouldBeNil) |
||||
existingNotificationCmd = m.CreateAlertNotificationCommand{ |
||||
Name: "channel3", |
||||
OrgId: 1, |
||||
Uid: "notifier3", |
||||
Type: "slack", |
||||
} |
||||
err = sqlstore.CreateAlertNotificationCommand(&existingNotificationCmd) |
||||
So(err, ShouldBeNil) |
||||
|
||||
notificationsQuery := m.GetAllAlertNotificationsQuery{OrgId: 1} |
||||
err = sqlstore.GetAllAlertNotifications(¬ificationsQuery) |
||||
So(err, ShouldBeNil) |
||||
So(notificationsQuery.Result, ShouldNotBeNil) |
||||
So(len(notificationsQuery.Result), ShouldEqual, 2) |
||||
|
||||
Convey("should have two new notifications", func() { |
||||
dc := newNotificationProvisioner(logger) |
||||
err := dc.applyChanges(twoNotificationsConfig) |
||||
if err != nil { |
||||
t.Fatalf("applyChanges return an error %v", err) |
||||
} |
||||
notificationsQuery = m.GetAllAlertNotificationsQuery{OrgId: 1} |
||||
err = sqlstore.GetAllAlertNotifications(¬ificationsQuery) |
||||
So(err, ShouldBeNil) |
||||
So(notificationsQuery.Result, ShouldNotBeNil) |
||||
So(len(notificationsQuery.Result), ShouldEqual, 4) |
||||
}) |
||||
}) |
||||
}) |
||||
|
||||
Convey("Can read correct properties with orgName instead of orgId", func() { |
||||
existingOrg1 := m.CreateOrgCommand{Name: "Main Org. 1"} |
||||
err := sqlstore.CreateOrg(&existingOrg1) |
||||
So(err, ShouldBeNil) |
||||
So(existingOrg1.Result, ShouldNotBeNil) |
||||
existingOrg2 := m.CreateOrgCommand{Name: "Main Org. 2"} |
||||
err = sqlstore.CreateOrg(&existingOrg2) |
||||
So(err, ShouldBeNil) |
||||
So(existingOrg2.Result, ShouldNotBeNil) |
||||
|
||||
existingNotificationCmd := m.CreateAlertNotificationCommand{ |
||||
Name: "default-notification-delete", |
||||
OrgId: existingOrg2.Result.Id, |
||||
Uid: "notifier2", |
||||
Type: "slack", |
||||
} |
||||
err = sqlstore.CreateAlertNotificationCommand(&existingNotificationCmd) |
||||
So(err, ShouldBeNil) |
||||
|
||||
dc := newNotificationProvisioner(logger) |
||||
err = dc.applyChanges(correct_properties_with_orgName) |
||||
if err != nil { |
||||
t.Fatalf("applyChanges return an error %v", err) |
||||
} |
||||
|
||||
notificationsQuery := m.GetAllAlertNotificationsQuery{OrgId: existingOrg2.Result.Id} |
||||
err = sqlstore.GetAllAlertNotifications(¬ificationsQuery) |
||||
So(err, ShouldBeNil) |
||||
So(notificationsQuery.Result, ShouldNotBeNil) |
||||
So(len(notificationsQuery.Result), ShouldEqual, 1) |
||||
|
||||
nt := notificationsQuery.Result[0] |
||||
So(nt.Name, ShouldEqual, "default-notification-create") |
||||
So(nt.OrgId, ShouldEqual, existingOrg2.Result.Id) |
||||
|
||||
}) |
||||
|
||||
Convey("Config doesn't contain required field", func() { |
||||
dc := newNotificationProvisioner(logger) |
||||
err := dc.applyChanges(no_required_fields) |
||||
So(err, ShouldNotBeNil) |
||||
|
||||
errString := err.Error() |
||||
So(errString, ShouldContainSubstring, "Deleted alert notification item 1 in configuration doesn't contain required field uid") |
||||
So(errString, ShouldContainSubstring, "Deleted alert notification item 2 in configuration doesn't contain required field name") |
||||
So(errString, ShouldContainSubstring, "Added alert notification item 1 in configuration doesn't contain required field name") |
||||
So(errString, ShouldContainSubstring, "Added alert notification item 2 in configuration doesn't contain required field uid") |
||||
}) |
||||
|
||||
Convey("Empty yaml file", func() { |
||||
Convey("should have not changed repo", func() { |
||||
dc := newNotificationProvisioner(logger) |
||||
err := dc.applyChanges(emptyFile) |
||||
if err != nil { |
||||
t.Fatalf("applyChanges return an error %v", err) |
||||
} |
||||
notificationsQuery := m.GetAllAlertNotificationsQuery{OrgId: 1} |
||||
err = sqlstore.GetAllAlertNotifications(¬ificationsQuery) |
||||
So(err, ShouldBeNil) |
||||
So(notificationsQuery.Result, ShouldBeEmpty) |
||||
}) |
||||
}) |
||||
|
||||
Convey("Broken yaml should return error", func() { |
||||
reader := &configReader{log: log.New("test logger")} |
||||
_, err := reader.readConfig(brokenYaml) |
||||
So(err, ShouldNotBeNil) |
||||
}) |
||||
|
||||
Convey("Skip invalid directory", func() { |
||||
cfgProvifer := &configReader{log: log.New("test logger")} |
||||
cfg, err := cfgProvifer.readConfig(emptyFolder) |
||||
if err != nil { |
||||
t.Fatalf("readConfig return an error %v", err) |
||||
} |
||||
So(len(cfg), ShouldEqual, 0) |
||||
}) |
||||
|
||||
Convey("Unknown notifier should return error", func() { |
||||
cfgProvifer := &configReader{log: log.New("test logger")} |
||||
_, err := cfgProvifer.readConfig(unknownNotifier) |
||||
So(err, ShouldNotBeNil) |
||||
So(err.Error(), ShouldEqual, "Unsupported notification type") |
||||
}) |
||||
|
||||
Convey("Read incorrect properties", func() { |
||||
cfgProvifer := &configReader{log: log.New("test logger")} |
||||
_, err := cfgProvifer.readConfig(incorrect_settings) |
||||
So(err, ShouldNotBeNil) |
||||
So(err.Error(), ShouldEqual, "Alert validation error: Could not find url property in settings") |
||||
}) |
||||
}) |
||||
} |
||||
@ -0,0 +1,9 @@ |
||||
notifiers: |
||||
- name: notification-channel-1 |
||||
type: slack |
||||
org_id: 2 |
||||
is_default: true |
||||
settings: |
||||
recipient: "XXX" |
||||
token: "xoxb" |
||||
uploadImage: true |
||||
@ -0,0 +1,6 @@ |
||||
#sfxzgnsxzcvnbzcvn |
||||
cvbn |
||||
cvbn |
||||
c |
||||
vbn |
||||
cvbncvbn |
||||
@ -0,0 +1,12 @@ |
||||
notifiers: |
||||
- name: default-notification-create |
||||
type: email |
||||
uid: notifier2 |
||||
settings: |
||||
addresses: example@example.com |
||||
org_name: Main Org. 2 |
||||
is_default: false |
||||
delete_notifiers: |
||||
- name: default-notification-delete |
||||
org_name: Main Org. 2 |
||||
uid: notifier2 |
||||
@ -0,0 +1,42 @@ |
||||
notifiers: |
||||
- name: default-slack-notification |
||||
type: slack |
||||
uid: notifier1 |
||||
org_id: 2 |
||||
uid: "notifier1" |
||||
is_default: true |
||||
settings: |
||||
recipient: "XXX" |
||||
token: "xoxb" |
||||
uploadImage: true |
||||
url: https://slack.com |
||||
- name: another-not-default-notification |
||||
type: email |
||||
settings: |
||||
addresses: example@exmaple.com |
||||
org_id: 3 |
||||
uid: "notifier2" |
||||
is_default: false |
||||
- name: check-unset-is_default-is-false |
||||
type: slack |
||||
org_id: 3 |
||||
uid: "notifier3" |
||||
settings: |
||||
url: https://slack.com |
||||
- name: Added notification with whitespaces in name |
||||
type: email |
||||
org_id: 3 |
||||
uid: "notifier4" |
||||
settings: |
||||
addresses: example@exmaple.com |
||||
delete_notifiers: |
||||
- name: default-slack-notification |
||||
org_id: 2 |
||||
uid: notifier1 |
||||
- name: deleted-notification-without-orgId |
||||
uid: "notifier2" |
||||
- name: deleted-notification-with-0-orgId |
||||
org_id: 0 |
||||
uid: "notifier3" |
||||
- name: Deleted notification with whitespaces in name |
||||
uid: "notifier4" |
||||
@ -0,0 +1,7 @@ |
||||
notifiers: |
||||
- name: first-default |
||||
type: slack |
||||
uid: notifier1 |
||||
is_default: true |
||||
settings: |
||||
url: https://slack.com |
||||
@ -0,0 +1,7 @@ |
||||
notifiers: |
||||
- name: second-default |
||||
type: email |
||||
uid: notifier2 |
||||
is_default: true |
||||
settings: |
||||
addresses: example@example.com |
||||
@ -0,0 +1,4 @@ |
||||
# Ignore everything in this directory |
||||
* |
||||
# Except this file |
||||
!.gitignore |
||||
@ -0,0 +1,10 @@ |
||||
notifiers: |
||||
- name: slack-notification-without-url-in-settings |
||||
type: slack |
||||
org_id: 2 |
||||
uid: notifier1 |
||||
is_default: true |
||||
settings: |
||||
recipient: "XXX" |
||||
token: "xoxb" |
||||
uploadImage: true |
||||
@ -0,0 +1,35 @@ |
||||
notifiers: |
||||
- type: slack |
||||
org_id: 2 |
||||
uid: no-name_added-notification |
||||
is_default: true |
||||
settings: |
||||
recipient: "XXX" |
||||
token: "xoxb" |
||||
uploadImage: true |
||||
- name: no-uid |
||||
type: slack |
||||
org_id: 2 |
||||
is_default: true |
||||
settings: |
||||
recipient: "XXX" |
||||
token: "xoxb" |
||||
uploadImage: true |
||||
delete_notifiers: |
||||
- name: no-uid |
||||
type: slack |
||||
org_id: 2 |
||||
is_default: true |
||||
settings: |
||||
recipient: "XXX" |
||||
token: "xoxb" |
||||
uploadImage: true |
||||
- type: slack |
||||
org_id: 2 |
||||
uid: no-name_added-notification |
||||
is_default: true |
||||
settings: |
||||
recipient: "XXX" |
||||
token: "xoxb" |
||||
uploadImage: true |
||||
|
||||
@ -0,0 +1,12 @@ |
||||
notifiers: |
||||
- name: channel1 |
||||
type: email |
||||
uid: notifier1 |
||||
org_id: 1 |
||||
settings: |
||||
addresses: example@example.com |
||||
- name: channel2 |
||||
type: slack |
||||
uid: notifier2 |
||||
settings: |
||||
url: http://slack.com |
||||
@ -0,0 +1,4 @@ |
||||
notifiers: |
||||
- name: unknown-notifier |
||||
type: nonexisting |
||||
uid: notifier1 |
||||
@ -0,0 +1,38 @@ |
||||
package notifiers |
||||
|
||||
import "github.com/grafana/grafana/pkg/components/simplejson" |
||||
|
||||
type notificationsAsConfig struct { |
||||
Notifications []*notificationFromConfig `json:"notifiers" yaml:"notifiers"` |
||||
DeleteNotifications []*deleteNotificationConfig `json:"delete_notifiers" yaml:"delete_notifiers"` |
||||
} |
||||
|
||||
type deleteNotificationConfig struct { |
||||
Uid string `json:"uid" yaml:"uid"` |
||||
Name string `json:"name" yaml:"name"` |
||||
OrgId int64 `json:"org_id" yaml:"org_id"` |
||||
OrgName string `json:"org_name" yaml:"org_name"` |
||||
} |
||||
|
||||
type notificationFromConfig struct { |
||||
Uid string `json:"uid" yaml:"uid"` |
||||
OrgId int64 `json:"org_id" yaml:"org_id"` |
||||
OrgName string `json:"org_name" yaml:"org_name"` |
||||
Name string `json:"name" yaml:"name"` |
||||
Type string `json:"type" yaml:"type"` |
||||
SendReminder bool `json:"send_reminder" yaml:"send_reminder"` |
||||
DisableResolveMessage bool `json:"disable_resolve_message" yaml:"disable_resolve_message"` |
||||
Frequency string `json:"frequency" yaml:"frequency"` |
||||
IsDefault bool `json:"is_default" yaml:"is_default"` |
||||
Settings map[string]interface{} `json:"settings" yaml:"settings"` |
||||
} |
||||
|
||||
func (notification notificationFromConfig) SettingsToJson() *simplejson.Json { |
||||
settings := simplejson.New() |
||||
if len(notification.Settings) > 0 { |
||||
for k, v := range notification.Settings { |
||||
settings.Set(k, v) |
||||
} |
||||
} |
||||
return settings |
||||
} |
||||
Loading…
Reference in new issue