config_reader_test.go 11 KB

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