config_reader_test.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  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. nt = nts[1]
  60. So(nt.Name, ShouldEqual, "another-not-default-notification")
  61. So(nt.Type, ShouldEqual, "email")
  62. So(nt.OrgId, ShouldEqual, 3)
  63. So(nt.Uid, ShouldEqual, "notifier2")
  64. So(nt.IsDefault, ShouldBeFalse)
  65. nt = nts[2]
  66. So(nt.Name, ShouldEqual, "check-unset-is_default-is-false")
  67. So(nt.Type, ShouldEqual, "slack")
  68. So(nt.OrgId, ShouldEqual, 3)
  69. So(nt.Uid, ShouldEqual, "notifier3")
  70. So(nt.IsDefault, ShouldBeFalse)
  71. nt = nts[3]
  72. So(nt.Name, ShouldEqual, "Added notification with whitespaces in name")
  73. So(nt.Type, ShouldEqual, "email")
  74. So(nt.Uid, ShouldEqual, "notifier4")
  75. So(nt.OrgId, ShouldEqual, 3)
  76. deleteNts := ntCfg.DeleteNotifications
  77. So(len(deleteNts), ShouldEqual, 4)
  78. deleteNt := deleteNts[0]
  79. So(deleteNt.Name, ShouldEqual, "default-slack-notification")
  80. So(deleteNt.Uid, ShouldEqual, "notifier1")
  81. So(deleteNt.OrgId, ShouldEqual, 2)
  82. deleteNt = deleteNts[1]
  83. So(deleteNt.Name, ShouldEqual, "deleted-notification-without-orgId")
  84. So(deleteNt.OrgId, ShouldEqual, 1)
  85. So(deleteNt.Uid, ShouldEqual, "notifier2")
  86. deleteNt = deleteNts[2]
  87. So(deleteNt.Name, ShouldEqual, "deleted-notification-with-0-orgId")
  88. So(deleteNt.OrgId, ShouldEqual, 1)
  89. So(deleteNt.Uid, ShouldEqual, "notifier3")
  90. deleteNt = deleteNts[3]
  91. So(deleteNt.Name, ShouldEqual, "Deleted notification with whitespaces in name")
  92. So(deleteNt.OrgId, ShouldEqual, 1)
  93. So(deleteNt.Uid, ShouldEqual, "notifier4")
  94. })
  95. Convey("One configured notification", func() {
  96. Convey("no notification in database", func() {
  97. dc := newNotificationProvisioner(logger)
  98. err := dc.applyChanges(twoNotificationsConfig)
  99. if err != nil {
  100. t.Fatalf("applyChanges return an error %v", err)
  101. }
  102. notificationsQuery := m.GetAllAlertNotificationsQuery{OrgId: 1}
  103. err = sqlstore.GetAllAlertNotifications(&notificationsQuery)
  104. So(err, ShouldBeNil)
  105. So(notificationsQuery.Result, ShouldNotBeNil)
  106. So(len(notificationsQuery.Result), ShouldEqual, 2)
  107. })
  108. Convey("One notification in database with same name and uid", func() {
  109. existingNotificationCmd := m.CreateAlertNotificationCommand{
  110. Name: "channel1",
  111. OrgId: 1,
  112. Uid: "notifier1",
  113. Type: "slack",
  114. }
  115. err := sqlstore.CreateAlertNotificationCommand(&existingNotificationCmd)
  116. So(err, ShouldBeNil)
  117. So(existingNotificationCmd.Result, ShouldNotBeNil)
  118. notificationsQuery := m.GetAllAlertNotificationsQuery{OrgId: 1}
  119. err = sqlstore.GetAllAlertNotifications(&notificationsQuery)
  120. So(err, ShouldBeNil)
  121. So(notificationsQuery.Result, ShouldNotBeNil)
  122. So(len(notificationsQuery.Result), ShouldEqual, 1)
  123. Convey("should update one notification", func() {
  124. dc := newNotificationProvisioner(logger)
  125. err = dc.applyChanges(twoNotificationsConfig)
  126. if err != nil {
  127. t.Fatalf("applyChanges return an error %v", err)
  128. }
  129. err = sqlstore.GetAllAlertNotifications(&notificationsQuery)
  130. So(err, ShouldBeNil)
  131. So(notificationsQuery.Result, ShouldNotBeNil)
  132. So(len(notificationsQuery.Result), ShouldEqual, 2)
  133. nts := notificationsQuery.Result
  134. nt1 := nts[0]
  135. So(nt1.Type, ShouldEqual, "email")
  136. So(nt1.Name, ShouldEqual, "channel1")
  137. So(nt1.Uid, ShouldEqual, "notifier1")
  138. nt2 := nts[1]
  139. So(nt2.Type, ShouldEqual, "slack")
  140. So(nt2.Name, ShouldEqual, "channel2")
  141. So(nt2.Uid, ShouldEqual, "notifier2")
  142. })
  143. })
  144. Convey("Two notifications with is_default", func() {
  145. dc := newNotificationProvisioner(logger)
  146. err := dc.applyChanges(doubleNotificationsConfig)
  147. Convey("should both be inserted", func() {
  148. So(err, ShouldBeNil)
  149. notificationsQuery := m.GetAllAlertNotificationsQuery{OrgId: 1}
  150. err = sqlstore.GetAllAlertNotifications(&notificationsQuery)
  151. So(err, ShouldBeNil)
  152. So(notificationsQuery.Result, ShouldNotBeNil)
  153. So(len(notificationsQuery.Result), ShouldEqual, 2)
  154. So(notificationsQuery.Result[0].IsDefault, ShouldBeTrue)
  155. So(notificationsQuery.Result[1].IsDefault, ShouldBeTrue)
  156. })
  157. })
  158. })
  159. Convey("Two configured notification", func() {
  160. Convey("two other notifications in database", func() {
  161. existingNotificationCmd := m.CreateAlertNotificationCommand{
  162. Name: "channel0",
  163. OrgId: 1,
  164. Uid: "notifier0",
  165. Type: "slack",
  166. }
  167. err := sqlstore.CreateAlertNotificationCommand(&existingNotificationCmd)
  168. So(err, ShouldBeNil)
  169. existingNotificationCmd = m.CreateAlertNotificationCommand{
  170. Name: "channel3",
  171. OrgId: 1,
  172. Uid: "notifier3",
  173. Type: "slack",
  174. }
  175. err = sqlstore.CreateAlertNotificationCommand(&existingNotificationCmd)
  176. So(err, ShouldBeNil)
  177. notificationsQuery := m.GetAllAlertNotificationsQuery{OrgId: 1}
  178. err = sqlstore.GetAllAlertNotifications(&notificationsQuery)
  179. So(err, ShouldBeNil)
  180. So(notificationsQuery.Result, ShouldNotBeNil)
  181. So(len(notificationsQuery.Result), ShouldEqual, 2)
  182. Convey("should have two new notifications", func() {
  183. dc := newNotificationProvisioner(logger)
  184. err := dc.applyChanges(twoNotificationsConfig)
  185. if err != nil {
  186. t.Fatalf("applyChanges return an error %v", err)
  187. }
  188. notificationsQuery = m.GetAllAlertNotificationsQuery{OrgId: 1}
  189. err = sqlstore.GetAllAlertNotifications(&notificationsQuery)
  190. So(err, ShouldBeNil)
  191. So(notificationsQuery.Result, ShouldNotBeNil)
  192. So(len(notificationsQuery.Result), ShouldEqual, 4)
  193. })
  194. })
  195. })
  196. Convey("Can read correct properties with orgName instead of orgId", func() {
  197. existingOrg1 := m.CreateOrgCommand{Name: "Main Org. 1"}
  198. err := sqlstore.CreateOrg(&existingOrg1)
  199. So(err, ShouldBeNil)
  200. So(existingOrg1.Result, ShouldNotBeNil)
  201. existingOrg2 := m.CreateOrgCommand{Name: "Main Org. 2"}
  202. err = sqlstore.CreateOrg(&existingOrg2)
  203. So(err, ShouldBeNil)
  204. So(existingOrg2.Result, ShouldNotBeNil)
  205. existingNotificationCmd := m.CreateAlertNotificationCommand{
  206. Name: "default-notification-delete",
  207. OrgId: existingOrg2.Result.Id,
  208. Uid: "notifier2",
  209. Type: "slack",
  210. }
  211. err = sqlstore.CreateAlertNotificationCommand(&existingNotificationCmd)
  212. So(err, ShouldBeNil)
  213. dc := newNotificationProvisioner(logger)
  214. err = dc.applyChanges(correct_properties_with_orgName)
  215. if err != nil {
  216. t.Fatalf("applyChanges return an error %v", err)
  217. }
  218. notificationsQuery := m.GetAllAlertNotificationsQuery{OrgId: existingOrg2.Result.Id}
  219. err = sqlstore.GetAllAlertNotifications(&notificationsQuery)
  220. So(err, ShouldBeNil)
  221. So(notificationsQuery.Result, ShouldNotBeNil)
  222. So(len(notificationsQuery.Result), ShouldEqual, 1)
  223. nt := notificationsQuery.Result[0]
  224. So(nt.Name, ShouldEqual, "default-notification-create")
  225. So(nt.OrgId, ShouldEqual, existingOrg2.Result.Id)
  226. })
  227. Convey("Config doesn't contain required field", func() {
  228. dc := newNotificationProvisioner(logger)
  229. err := dc.applyChanges(no_required_fields)
  230. So(err, ShouldNotBeNil)
  231. errString := err.Error()
  232. So(errString, ShouldContainSubstring, "Deleted alert notification item 1 in configuration doesn't contain required field uid")
  233. So(errString, ShouldContainSubstring, "Deleted alert notification item 2 in configuration doesn't contain required field name")
  234. So(errString, ShouldContainSubstring, "Added alert notification item 1 in configuration doesn't contain required field name")
  235. So(errString, ShouldContainSubstring, "Added alert notification item 2 in configuration doesn't contain required field uid")
  236. })
  237. Convey("Empty yaml file", func() {
  238. Convey("should have not changed repo", func() {
  239. dc := newNotificationProvisioner(logger)
  240. err := dc.applyChanges(emptyFile)
  241. if err != nil {
  242. t.Fatalf("applyChanges return an error %v", err)
  243. }
  244. notificationsQuery := m.GetAllAlertNotificationsQuery{OrgId: 1}
  245. err = sqlstore.GetAllAlertNotifications(&notificationsQuery)
  246. So(err, ShouldBeNil)
  247. So(notificationsQuery.Result, ShouldBeEmpty)
  248. })
  249. })
  250. Convey("Broken yaml should return error", func() {
  251. reader := &configReader{log: log.New("test logger")}
  252. _, err := reader.readConfig(brokenYaml)
  253. So(err, ShouldNotBeNil)
  254. })
  255. Convey("Skip invalid directory", func() {
  256. cfgProvifer := &configReader{log: log.New("test logger")}
  257. cfg, err := cfgProvifer.readConfig(emptyFolder)
  258. if err != nil {
  259. t.Fatalf("readConfig return an error %v", err)
  260. }
  261. So(len(cfg), ShouldEqual, 0)
  262. })
  263. Convey("Unknown notifier should return error", func() {
  264. cfgProvifer := &configReader{log: log.New("test logger")}
  265. _, err := cfgProvifer.readConfig(unknownNotifier)
  266. So(err, ShouldNotBeNil)
  267. So(err.Error(), ShouldEqual, "Unsupported notification type")
  268. })
  269. Convey("Read incorrect properties", func() {
  270. cfgProvifer := &configReader{log: log.New("test logger")}
  271. _, err := cfgProvifer.readConfig(incorrect_settings)
  272. So(err, ShouldNotBeNil)
  273. So(err.Error(), ShouldEqual, "Alert validation error: Could not find url property in settings")
  274. })
  275. })
  276. }