config_reader_test.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. package alert_notifications
  2. import (
  3. "testing"
  4. "github.com/grafana/grafana/pkg/log"
  5. m "github.com/grafana/grafana/pkg/models"
  6. "github.com/grafana/grafana/pkg/services/alerting"
  7. "github.com/grafana/grafana/pkg/services/alerting/notifiers"
  8. "github.com/grafana/grafana/pkg/services/sqlstore"
  9. . "github.com/smartystreets/goconvey/convey"
  10. )
  11. var (
  12. logger = log.New("fake.log")
  13. correct_properties = "./test-configs/correct-properties"
  14. incorrect_settings = "./test-configs/incorrect-settings"
  15. no_required_fields = "./test-configs/no-required-fields"
  16. correct_properties_with_orgName = "./test-configs/correct-properties-with-orgName"
  17. brokenYaml = "./test-configs/broken-yaml"
  18. doubleNotificationsConfig = "./test-configs/double-default"
  19. emptyFolder = "./test-configs/empty_folder"
  20. emptyFile = "./test-configs/empty"
  21. twoNotificationsConfig = "./test-configs/two-notifications"
  22. unknownNotifier = "./test-configs/unknown-notifier"
  23. )
  24. func TestNotificationAsConfig(t *testing.T) {
  25. Convey("Testing notification as configuration", t, func() {
  26. sqlstore.InitTestDB(t)
  27. alerting.RegisterNotifier(&alerting.NotifierPlugin{
  28. Type: "slack",
  29. Name: "slack",
  30. Factory: notifiers.NewSlackNotifier,
  31. })
  32. alerting.RegisterNotifier(&alerting.NotifierPlugin{
  33. Type: "email",
  34. Name: "email",
  35. Factory: notifiers.NewEmailNotifier,
  36. })
  37. Convey("Can read correct properties", func() {
  38. cfgProvifer := &configReader{log: log.New("test logger")}
  39. cfg, err := cfgProvifer.readConfig(correct_properties)
  40. if err != nil {
  41. t.Fatalf("readConfig return an error %v", err)
  42. }
  43. So(len(cfg), ShouldEqual, 1)
  44. ntCfg := cfg[0]
  45. nts := ntCfg.Notifications
  46. So(len(nts), ShouldEqual, 4)
  47. nt := nts[0]
  48. So(nt.Name, ShouldEqual, "default-slack-notification")
  49. So(nt.Type, ShouldEqual, "slack")
  50. So(nt.OrgId, ShouldEqual, 2)
  51. So(nt.Uid, ShouldEqual, "notifier1")
  52. So(nt.IsDefault, ShouldBeTrue)
  53. So(nt.Settings, ShouldResemble, map[string]interface{}{
  54. "recipient": "XXX", "token": "xoxb", "uploadImage": true, "url": "https://slack.com",
  55. })
  56. nt = nts[1]
  57. So(nt.Name, ShouldEqual, "another-not-default-notification")
  58. So(nt.Type, ShouldEqual, "email")
  59. So(nt.OrgId, ShouldEqual, 3)
  60. So(nt.Uid, ShouldEqual, "notifier2")
  61. So(nt.IsDefault, ShouldBeFalse)
  62. nt = nts[2]
  63. So(nt.Name, ShouldEqual, "check-unset-is_default-is-false")
  64. So(nt.Type, ShouldEqual, "slack")
  65. So(nt.OrgId, ShouldEqual, 3)
  66. So(nt.Uid, ShouldEqual, "notifier3")
  67. So(nt.IsDefault, ShouldBeFalse)
  68. nt = nts[3]
  69. So(nt.Name, ShouldEqual, "Added notification with whitespaces in name")
  70. So(nt.Type, ShouldEqual, "email")
  71. So(nt.Uid, ShouldEqual, "notifier4")
  72. So(nt.OrgId, ShouldEqual, 3)
  73. deleteNts := ntCfg.DeleteNotifications
  74. So(len(deleteNts), ShouldEqual, 4)
  75. deleteNt := deleteNts[0]
  76. So(deleteNt.Name, ShouldEqual, "default-slack-notification")
  77. So(deleteNt.Uid, ShouldEqual, "notifier1")
  78. So(deleteNt.OrgId, ShouldEqual, 2)
  79. deleteNt = deleteNts[1]
  80. So(deleteNt.Name, ShouldEqual, "deleted-notification-without-orgId")
  81. So(deleteNt.OrgId, ShouldEqual, 1)
  82. So(deleteNt.Uid, ShouldEqual, "notifier2")
  83. deleteNt = deleteNts[2]
  84. So(deleteNt.Name, ShouldEqual, "deleted-notification-with-0-orgId")
  85. So(deleteNt.OrgId, ShouldEqual, 1)
  86. So(deleteNt.Uid, ShouldEqual, "notifier3")
  87. deleteNt = deleteNts[3]
  88. So(deleteNt.Name, ShouldEqual, "Deleted notification with whitespaces in name")
  89. So(deleteNt.OrgId, ShouldEqual, 1)
  90. So(deleteNt.Uid, ShouldEqual, "notifier4")
  91. })
  92. Convey("One configured notification", func() {
  93. Convey("no notification in database", func() {
  94. dc := newNotificationProvisioner(logger)
  95. err := dc.applyChanges(twoNotificationsConfig)
  96. if err != nil {
  97. t.Fatalf("applyChanges return an error %v", err)
  98. }
  99. notificationsQuery := m.GetAllAlertNotificationsQuery{OrgId: 1}
  100. err = sqlstore.GetAllAlertNotifications(&notificationsQuery)
  101. So(err, ShouldBeNil)
  102. So(notificationsQuery.Result, ShouldNotBeNil)
  103. So(len(notificationsQuery.Result), ShouldEqual, 2)
  104. })
  105. Convey("One notification in database with same name and uid", func() {
  106. existingNotificationCmd := m.CreateAlertNotificationCommand{
  107. Name: "channel1",
  108. OrgId: 1,
  109. Uid: "notifier1",
  110. Type: "slack",
  111. }
  112. err := sqlstore.CreateAlertNotificationCommand(&existingNotificationCmd)
  113. So(err, ShouldBeNil)
  114. So(existingNotificationCmd.Result, ShouldNotBeNil)
  115. notificationsQuery := m.GetAllAlertNotificationsQuery{OrgId: 1}
  116. err = sqlstore.GetAllAlertNotifications(&notificationsQuery)
  117. So(err, ShouldBeNil)
  118. So(notificationsQuery.Result, ShouldNotBeNil)
  119. So(len(notificationsQuery.Result), ShouldEqual, 1)
  120. Convey("should update one notification", func() {
  121. dc := newNotificationProvisioner(logger)
  122. err = dc.applyChanges(twoNotificationsConfig)
  123. if err != nil {
  124. t.Fatalf("applyChanges return an error %v", err)
  125. }
  126. err = sqlstore.GetAllAlertNotifications(&notificationsQuery)
  127. So(err, ShouldBeNil)
  128. So(notificationsQuery.Result, ShouldNotBeNil)
  129. So(len(notificationsQuery.Result), ShouldEqual, 2)
  130. nts := notificationsQuery.Result
  131. nt1 := nts[0]
  132. So(nt1.Type, ShouldEqual, "email")
  133. So(nt1.Name, ShouldEqual, "channel1")
  134. So(nt1.Uid, ShouldEqual, "notifier1")
  135. nt2 := nts[1]
  136. So(nt2.Type, ShouldEqual, "slack")
  137. So(nt2.Name, ShouldEqual, "channel2")
  138. So(nt2.Uid, ShouldEqual, "notifier2")
  139. })
  140. })
  141. Convey("Two notifications with is_default", func() {
  142. dc := newNotificationProvisioner(logger)
  143. err := dc.applyChanges(doubleNotificationsConfig)
  144. Convey("should both be inserted", func() {
  145. So(err, ShouldBeNil)
  146. notificationsQuery := m.GetAllAlertNotificationsQuery{OrgId: 1}
  147. err = sqlstore.GetAllAlertNotifications(&notificationsQuery)
  148. So(err, ShouldBeNil)
  149. So(notificationsQuery.Result, ShouldNotBeNil)
  150. So(len(notificationsQuery.Result), ShouldEqual, 2)
  151. So(notificationsQuery.Result[0].IsDefault, ShouldBeTrue)
  152. So(notificationsQuery.Result[1].IsDefault, ShouldBeTrue)
  153. })
  154. })
  155. })
  156. Convey("Two configured notification", func() {
  157. Convey("two other notifications in database", func() {
  158. existingNotificationCmd := m.CreateAlertNotificationCommand{
  159. Name: "channel0",
  160. OrgId: 1,
  161. Uid: "notifier0",
  162. Type: "slack",
  163. }
  164. err := sqlstore.CreateAlertNotificationCommand(&existingNotificationCmd)
  165. So(err, ShouldBeNil)
  166. existingNotificationCmd = m.CreateAlertNotificationCommand{
  167. Name: "channel3",
  168. OrgId: 1,
  169. Uid: "notifier3",
  170. Type: "slack",
  171. }
  172. err = sqlstore.CreateAlertNotificationCommand(&existingNotificationCmd)
  173. So(err, ShouldBeNil)
  174. notificationsQuery := m.GetAllAlertNotificationsQuery{OrgId: 1}
  175. err = sqlstore.GetAllAlertNotifications(&notificationsQuery)
  176. So(err, ShouldBeNil)
  177. So(notificationsQuery.Result, ShouldNotBeNil)
  178. So(len(notificationsQuery.Result), ShouldEqual, 2)
  179. Convey("should have two new notifications", func() {
  180. dc := newNotificationProvisioner(logger)
  181. err := dc.applyChanges(twoNotificationsConfig)
  182. if err != nil {
  183. t.Fatalf("applyChanges return an error %v", err)
  184. }
  185. notificationsQuery = m.GetAllAlertNotificationsQuery{OrgId: 1}
  186. err = sqlstore.GetAllAlertNotifications(&notificationsQuery)
  187. So(err, ShouldBeNil)
  188. So(notificationsQuery.Result, ShouldNotBeNil)
  189. So(len(notificationsQuery.Result), ShouldEqual, 4)
  190. })
  191. })
  192. })
  193. Convey("Can read correct properties with orgName instead of orgId", func() {
  194. existingOrg1 := m.CreateOrgCommand{Name: "Main Org. 1"}
  195. err := sqlstore.CreateOrg(&existingOrg1)
  196. So(err, ShouldBeNil)
  197. So(existingOrg1.Result, ShouldNotBeNil)
  198. existingOrg2 := m.CreateOrgCommand{Name: "Main Org. 2"}
  199. err = sqlstore.CreateOrg(&existingOrg2)
  200. So(err, ShouldBeNil)
  201. So(existingOrg2.Result, ShouldNotBeNil)
  202. existingNotificationCmd := m.CreateAlertNotificationCommand{
  203. Name: "default-notification-delete",
  204. OrgId: existingOrg2.Result.Id,
  205. Uid: "notifier2",
  206. Type: "slack",
  207. }
  208. err = sqlstore.CreateAlertNotificationCommand(&existingNotificationCmd)
  209. So(err, ShouldBeNil)
  210. dc := newNotificationProvisioner(logger)
  211. err = dc.applyChanges(correct_properties_with_orgName)
  212. if err != nil {
  213. t.Fatalf("applyChanges return an error %v", err)
  214. }
  215. notificationsQuery := m.GetAllAlertNotificationsQuery{OrgId: existingOrg2.Result.Id}
  216. err = sqlstore.GetAllAlertNotifications(&notificationsQuery)
  217. So(err, ShouldBeNil)
  218. So(notificationsQuery.Result, ShouldNotBeNil)
  219. So(len(notificationsQuery.Result), ShouldEqual, 1)
  220. nt := notificationsQuery.Result[0]
  221. So(nt.Name, ShouldEqual, "default-notification-create")
  222. So(nt.OrgId, ShouldEqual, existingOrg2.Result.Id)
  223. })
  224. Convey("Config doesn't contain required field", func() {
  225. dc := newNotificationProvisioner(logger)
  226. err := dc.applyChanges(no_required_fields)
  227. So(err, ShouldNotBeNil)
  228. errString := err.Error()
  229. So(errString, ShouldContainSubstring, "Deleted alert notification item 1 in configuration doesn't contain required field uid")
  230. So(errString, ShouldContainSubstring, "Deleted alert notification item 2 in configuration doesn't contain required field name")
  231. So(errString, ShouldContainSubstring, "Added alert notification item 1 in configuration doesn't contain required field name")
  232. So(errString, ShouldContainSubstring, "Added alert notification item 2 in configuration doesn't contain required field uid")
  233. })
  234. Convey("Empty yaml file", func() {
  235. Convey("should have not changed repo", func() {
  236. dc := newNotificationProvisioner(logger)
  237. err := dc.applyChanges(emptyFile)
  238. if err != nil {
  239. t.Fatalf("applyChanges return an error %v", err)
  240. }
  241. notificationsQuery := m.GetAllAlertNotificationsQuery{OrgId: 1}
  242. err = sqlstore.GetAllAlertNotifications(&notificationsQuery)
  243. So(err, ShouldBeNil)
  244. So(notificationsQuery.Result, ShouldBeEmpty)
  245. })
  246. })
  247. Convey("Broken yaml should return error", func() {
  248. reader := &configReader{log: log.New("test logger")}
  249. _, err := reader.readConfig(brokenYaml)
  250. So(err, ShouldNotBeNil)
  251. })
  252. Convey("Skip invalid directory", func() {
  253. cfgProvifer := &configReader{log: log.New("test logger")}
  254. cfg, err := cfgProvifer.readConfig(emptyFolder)
  255. if err != nil {
  256. t.Fatalf("readConfig return an error %v", err)
  257. }
  258. So(len(cfg), ShouldEqual, 0)
  259. })
  260. Convey("Unknown notifier should return error", func() {
  261. cfgProvifer := &configReader{log: log.New("test logger")}
  262. _, err := cfgProvifer.readConfig(unknownNotifier)
  263. So(err, ShouldEqual, ErrInvalidNotifierType)
  264. })
  265. Convey("Read incorrect properties", func() {
  266. cfgProvifer := &configReader{log: log.New("test logger")}
  267. _, err := cfgProvifer.readConfig(incorrect_settings)
  268. So(err, ShouldNotBeNil)
  269. So(err.Error(), ShouldEqual, "Alert validation error: Could not find url property in settings")
  270. })
  271. })
  272. }