flapjack 1.6.0 → 2.0.0b1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (301) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +4 -6
  3. data/.gitmodules +1 -1
  4. data/.rspec +1 -1
  5. data/.ruby-version +1 -1
  6. data/.travis.yml +12 -13
  7. data/CHANGELOG.md +2 -9
  8. data/CONTRIBUTING.md +7 -2
  9. data/Gemfile +4 -13
  10. data/LICENCE +1 -0
  11. data/README.md +8 -2
  12. data/Rakefile +2 -2
  13. data/bin/flapjack +3 -12
  14. data/build.sh +4 -2
  15. data/etc/flapjack_config.toml.example +273 -0
  16. data/features/ack_after_sched_maint.feature +18 -21
  17. data/features/cli.feature +11 -71
  18. data/features/cli_flapjack-feed-events.feature +14 -15
  19. data/features/cli_flapjack-nagios-receiver.feature +12 -41
  20. data/features/cli_flapper.feature +12 -41
  21. data/features/cli_purge.feature +5 -6
  22. data/features/cli_receive-events.feature +6 -7
  23. data/features/cli_simulate-failed-check.feature +5 -6
  24. data/features/events.feature +206 -181
  25. data/features/events_check_names.feature +4 -7
  26. data/features/notification_rules.feature +144 -223
  27. data/features/notifications.feature +65 -57
  28. data/features/rollup.feature +45 -47
  29. data/features/steps/cli_steps.rb +4 -5
  30. data/features/steps/events_steps.rb +163 -373
  31. data/features/steps/notifications_steps.rb +408 -264
  32. data/features/steps/packaging-lintian_steps.rb +0 -4
  33. data/features/steps/time_travel_steps.rb +0 -26
  34. data/features/support/daemons.rb +6 -31
  35. data/features/support/env.rb +65 -74
  36. data/flapjack.gemspec +22 -24
  37. data/lib/flapjack.rb +14 -7
  38. data/lib/flapjack/cli/flapper.rb +74 -173
  39. data/lib/flapjack/cli/maintenance.rb +278 -109
  40. data/lib/flapjack/cli/migrate.rb +950 -0
  41. data/lib/flapjack/cli/purge.rb +19 -22
  42. data/lib/flapjack/cli/receiver.rb +150 -326
  43. data/lib/flapjack/cli/server.rb +8 -235
  44. data/lib/flapjack/cli/simulate.rb +42 -57
  45. data/lib/flapjack/configuration.rb +51 -37
  46. data/lib/flapjack/coordinator.rb +138 -129
  47. data/lib/flapjack/data/acknowledgement.rb +177 -0
  48. data/lib/flapjack/data/alert.rb +97 -158
  49. data/lib/flapjack/data/check.rb +611 -0
  50. data/lib/flapjack/data/condition.rb +70 -0
  51. data/lib/flapjack/data/contact.rb +226 -456
  52. data/lib/flapjack/data/event.rb +96 -184
  53. data/lib/flapjack/data/extensions/associations.rb +59 -0
  54. data/lib/flapjack/data/extensions/short_name.rb +25 -0
  55. data/lib/flapjack/data/medium.rb +428 -0
  56. data/lib/flapjack/data/metrics.rb +194 -0
  57. data/lib/flapjack/data/notification.rb +22 -281
  58. data/lib/flapjack/data/rule.rb +473 -0
  59. data/lib/flapjack/data/scheduled_maintenance.rb +244 -0
  60. data/lib/flapjack/data/state.rb +221 -0
  61. data/lib/flapjack/data/statistic.rb +112 -0
  62. data/lib/flapjack/data/tag.rb +277 -0
  63. data/lib/flapjack/data/test_notification.rb +182 -0
  64. data/lib/flapjack/data/unscheduled_maintenance.rb +159 -0
  65. data/lib/flapjack/data/validators/id_validator.rb +20 -0
  66. data/lib/flapjack/exceptions.rb +6 -0
  67. data/lib/flapjack/filters/acknowledgement.rb +23 -16
  68. data/lib/flapjack/filters/base.rb +0 -5
  69. data/lib/flapjack/filters/delays.rb +53 -43
  70. data/lib/flapjack/filters/ok.rb +23 -14
  71. data/lib/flapjack/filters/scheduled_maintenance.rb +3 -3
  72. data/lib/flapjack/filters/unscheduled_maintenance.rb +12 -3
  73. data/lib/flapjack/gateways/aws_sns.rb +65 -49
  74. data/lib/flapjack/gateways/aws_sns/alert.text.erb +2 -2
  75. data/lib/flapjack/gateways/aws_sns/alert_subject.text.erb +2 -2
  76. data/lib/flapjack/gateways/aws_sns/rollup_subject.text.erb +1 -1
  77. data/lib/flapjack/gateways/email.rb +107 -90
  78. data/lib/flapjack/gateways/email/alert.html.erb +19 -18
  79. data/lib/flapjack/gateways/email/alert.text.erb +20 -14
  80. data/lib/flapjack/gateways/email/alert_subject.text.erb +2 -1
  81. data/lib/flapjack/gateways/email/rollup.html.erb +14 -13
  82. data/lib/flapjack/gateways/email/rollup.text.erb +13 -10
  83. data/lib/flapjack/gateways/jabber.rb +679 -671
  84. data/lib/flapjack/gateways/jabber/alert.text.erb +9 -6
  85. data/lib/flapjack/gateways/jsonapi.rb +164 -350
  86. data/lib/flapjack/gateways/jsonapi/data/join_descriptor.rb +44 -0
  87. data/lib/flapjack/gateways/jsonapi/data/method_descriptor.rb +21 -0
  88. data/lib/flapjack/gateways/jsonapi/helpers/headers.rb +63 -0
  89. data/lib/flapjack/gateways/jsonapi/helpers/miscellaneous.rb +136 -0
  90. data/lib/flapjack/gateways/jsonapi/helpers/resources.rb +227 -0
  91. data/lib/flapjack/gateways/jsonapi/helpers/serialiser.rb +313 -0
  92. data/lib/flapjack/gateways/jsonapi/helpers/swagger_docs.rb +322 -0
  93. data/lib/flapjack/gateways/jsonapi/methods/association_delete.rb +115 -0
  94. data/lib/flapjack/gateways/jsonapi/methods/association_get.rb +288 -0
  95. data/lib/flapjack/gateways/jsonapi/methods/association_patch.rb +178 -0
  96. data/lib/flapjack/gateways/jsonapi/methods/association_post.rb +116 -0
  97. data/lib/flapjack/gateways/jsonapi/methods/metrics.rb +71 -0
  98. data/lib/flapjack/gateways/jsonapi/methods/resource_delete.rb +119 -0
  99. data/lib/flapjack/gateways/jsonapi/methods/resource_get.rb +186 -0
  100. data/lib/flapjack/gateways/jsonapi/methods/resource_patch.rb +239 -0
  101. data/lib/flapjack/gateways/jsonapi/methods/resource_post.rb +197 -0
  102. data/lib/flapjack/gateways/jsonapi/middleware/array_param_fixer.rb +27 -0
  103. data/lib/flapjack/gateways/jsonapi/{rack → middleware}/json_params_parser.rb +7 -6
  104. data/lib/flapjack/gateways/jsonapi/middleware/request_timestamp.rb +18 -0
  105. data/lib/flapjack/gateways/oobetet.rb +222 -170
  106. data/lib/flapjack/gateways/pager_duty.rb +388 -0
  107. data/lib/flapjack/gateways/pager_duty/alert.text.erb +13 -0
  108. data/lib/flapjack/gateways/slack.rb +56 -48
  109. data/lib/flapjack/gateways/slack/alert.text.erb +1 -1
  110. data/lib/flapjack/gateways/slack/rollup.text.erb +1 -1
  111. data/lib/flapjack/gateways/sms_aspsms.rb +155 -0
  112. data/lib/flapjack/gateways/sms_aspsms/alert.text.erb +7 -0
  113. data/lib/flapjack/gateways/sms_aspsms/rollup.text.erb +2 -0
  114. data/lib/flapjack/gateways/sms_messagenet.rb +77 -57
  115. data/lib/flapjack/gateways/sms_messagenet/alert.text.erb +3 -2
  116. data/lib/flapjack/gateways/sms_nexmo.rb +53 -51
  117. data/lib/flapjack/gateways/sms_nexmo/alert.text.erb +2 -2
  118. data/lib/flapjack/gateways/sms_nexmo/rollup.text.erb +1 -1
  119. data/lib/flapjack/gateways/sms_twilio.rb +79 -62
  120. data/lib/flapjack/gateways/sms_twilio/alert.text.erb +3 -2
  121. data/lib/flapjack/gateways/web.rb +437 -345
  122. data/lib/flapjack/gateways/web/middleware/request_timestamp.rb +18 -0
  123. data/lib/flapjack/gateways/web/public/css/bootstrap.css +3793 -4340
  124. data/lib/flapjack/gateways/web/public/css/bootstrap.css.map +1 -0
  125. data/lib/flapjack/gateways/web/public/fonts/glyphicons-halflings-regular.eot +0 -0
  126. data/lib/flapjack/gateways/web/public/fonts/glyphicons-halflings-regular.svg +273 -214
  127. data/lib/flapjack/gateways/web/public/fonts/glyphicons-halflings-regular.ttf +0 -0
  128. data/lib/flapjack/gateways/web/public/fonts/glyphicons-halflings-regular.woff +0 -0
  129. data/lib/flapjack/gateways/web/public/fonts/glyphicons-halflings-regular.woff2 +0 -0
  130. data/lib/flapjack/gateways/web/public/js/bootstrap.js +1637 -1607
  131. data/lib/flapjack/gateways/web/public/js/self_stats.js +1 -2
  132. data/lib/flapjack/gateways/web/views/_pagination.html.erb +19 -0
  133. data/lib/flapjack/gateways/web/views/check.html.erb +159 -121
  134. data/lib/flapjack/gateways/web/views/checks.html.erb +82 -41
  135. data/lib/flapjack/gateways/web/views/contact.html.erb +59 -71
  136. data/lib/flapjack/gateways/web/views/contacts.html.erb +32 -8
  137. data/lib/flapjack/gateways/web/views/index.html.erb +2 -2
  138. data/lib/flapjack/gateways/web/views/{layout.erb → layout.html.erb} +7 -23
  139. data/lib/flapjack/gateways/web/views/self_stats.html.erb +32 -33
  140. data/lib/flapjack/gateways/web/views/tag.html.erb +32 -0
  141. data/lib/flapjack/gateways/web/views/tags.html.erb +51 -0
  142. data/lib/flapjack/logger.rb +34 -3
  143. data/lib/flapjack/notifier.rb +180 -112
  144. data/lib/flapjack/patches.rb +8 -63
  145. data/lib/flapjack/pikelet.rb +185 -143
  146. data/lib/flapjack/processor.rb +323 -191
  147. data/lib/flapjack/record_queue.rb +33 -0
  148. data/lib/flapjack/redis_proxy.rb +66 -0
  149. data/lib/flapjack/utility.rb +21 -15
  150. data/lib/flapjack/version.rb +2 -1
  151. data/libexec/httpbroker.go +218 -14
  152. data/libexec/oneoff.go +13 -10
  153. data/spec/lib/flapjack/configuration_spec.rb +286 -0
  154. data/spec/lib/flapjack/coordinator_spec.rb +103 -157
  155. data/spec/lib/flapjack/data/check_spec.rb +175 -0
  156. data/spec/lib/flapjack/data/contact_spec.rb +26 -349
  157. data/spec/lib/flapjack/data/event_spec.rb +76 -291
  158. data/spec/lib/flapjack/data/medium_spec.rb +19 -0
  159. data/spec/lib/flapjack/data/rule_spec.rb +43 -0
  160. data/spec/lib/flapjack/data/scheduled_maintenance_spec.rb +976 -0
  161. data/spec/lib/flapjack/data/unscheduled_maintenance_spec.rb +34 -0
  162. data/spec/lib/flapjack/gateways/aws_sns_spec.rb +111 -60
  163. data/spec/lib/flapjack/gateways/email_spec.rb +194 -161
  164. data/spec/lib/flapjack/gateways/jabber_spec.rb +961 -162
  165. data/spec/lib/flapjack/gateways/jsonapi/methods/check_links_spec.rb +155 -0
  166. data/spec/lib/flapjack/gateways/jsonapi/methods/checks_spec.rb +426 -0
  167. data/spec/lib/flapjack/gateways/jsonapi/methods/contact_links_spec.rb +217 -0
  168. data/spec/lib/flapjack/gateways/jsonapi/methods/contacts_spec.rb +425 -0
  169. data/spec/lib/flapjack/gateways/jsonapi/methods/events_spec.rb +271 -0
  170. data/spec/lib/flapjack/gateways/jsonapi/methods/media_spec.rb +257 -0
  171. data/spec/lib/flapjack/gateways/jsonapi/methods/medium_links_spec.rb +163 -0
  172. data/spec/lib/flapjack/gateways/jsonapi/methods/metrics_spec.rb +8 -0
  173. data/spec/lib/flapjack/gateways/jsonapi/methods/rule_links_spec.rb +212 -0
  174. data/spec/lib/flapjack/gateways/jsonapi/methods/rules_spec.rb +289 -0
  175. data/spec/lib/flapjack/gateways/jsonapi/methods/scheduled_maintenance_links_spec.rb +49 -0
  176. data/spec/lib/flapjack/gateways/jsonapi/methods/scheduled_maintenances_spec.rb +242 -0
  177. data/spec/lib/flapjack/gateways/jsonapi/methods/tag_links_spec.rb +274 -0
  178. data/spec/lib/flapjack/gateways/jsonapi/methods/tags_spec.rb +302 -0
  179. data/spec/lib/flapjack/gateways/jsonapi/methods/unscheduled_maintenance_links_spec.rb +49 -0
  180. data/spec/lib/flapjack/gateways/jsonapi/methods/unscheduled_maintenances_spec.rb +339 -0
  181. data/spec/lib/flapjack/gateways/jsonapi_spec.rb +1 -1
  182. data/spec/lib/flapjack/gateways/oobetet_spec.rb +151 -79
  183. data/spec/lib/flapjack/gateways/pager_duty_spec.rb +353 -0
  184. data/spec/lib/flapjack/gateways/slack_spec.rb +53 -53
  185. data/spec/lib/flapjack/gateways/sms_aspsms_spec.rb +106 -0
  186. data/spec/lib/flapjack/gateways/sms_messagenet_spec.rb +111 -54
  187. data/spec/lib/flapjack/gateways/sms_nexmo_spec.rb +50 -51
  188. data/spec/lib/flapjack/gateways/sms_twilio_spec.rb +108 -48
  189. data/spec/lib/flapjack/gateways/web_spec.rb +144 -216
  190. data/spec/lib/flapjack/notifier_spec.rb +132 -1
  191. data/spec/lib/flapjack/pikelet_spec.rb +111 -50
  192. data/spec/lib/flapjack/processor_spec.rb +210 -40
  193. data/spec/lib/flapjack/redis_proxy_spec.rb +45 -0
  194. data/spec/lib/flapjack/utility_spec.rb +11 -15
  195. data/spec/service_consumers/fixture_data.rb +547 -0
  196. data/spec/service_consumers/pact_helper.rb +21 -32
  197. data/spec/service_consumers/pacts/flapjack-diner_v2.0.json +4652 -0
  198. data/spec/service_consumers/provider_states_for_flapjack-diner.rb +279 -322
  199. data/spec/service_consumers/provider_support.rb +8 -0
  200. data/spec/spec_helper.rb +34 -44
  201. data/spec/support/erb_view_helper.rb +1 -1
  202. data/spec/support/factories.rb +58 -0
  203. data/spec/support/jsonapi_helper.rb +15 -26
  204. data/spec/support/mock_logger.rb +43 -0
  205. data/spec/support/xmpp_comparable.rb +24 -0
  206. data/src/flapjack/transport_test.go +30 -1
  207. data/tasks/dump_keys.rake +82 -0
  208. data/tasks/events.rake +7 -7
  209. data/tasks/support/flapjack_config_benchmark.toml +28 -0
  210. data/tasks/support/flapjack_config_benchmark.yaml +0 -2
  211. metadata +175 -222
  212. data/Guardfile +0 -14
  213. data/etc/flapjack_config.yaml.example +0 -477
  214. data/features/cli_flapjack-populator.feature +0 -90
  215. data/features/support/silent_system.rb +0 -4
  216. data/lib/flapjack/cli/import.rb +0 -108
  217. data/lib/flapjack/data/entity.rb +0 -652
  218. data/lib/flapjack/data/entity_check.rb +0 -1044
  219. data/lib/flapjack/data/message.rb +0 -56
  220. data/lib/flapjack/data/migration.rb +0 -234
  221. data/lib/flapjack/data/notification_rule.rb +0 -425
  222. data/lib/flapjack/data/semaphore.rb +0 -44
  223. data/lib/flapjack/data/tagged.rb +0 -48
  224. data/lib/flapjack/gateways/jsonapi/check_methods.rb +0 -206
  225. data/lib/flapjack/gateways/jsonapi/check_presenter.rb +0 -221
  226. data/lib/flapjack/gateways/jsonapi/contact_methods.rb +0 -186
  227. data/lib/flapjack/gateways/jsonapi/entity_methods.rb +0 -223
  228. data/lib/flapjack/gateways/jsonapi/medium_methods.rb +0 -185
  229. data/lib/flapjack/gateways/jsonapi/metrics_methods.rb +0 -132
  230. data/lib/flapjack/gateways/jsonapi/notification_rule_methods.rb +0 -141
  231. data/lib/flapjack/gateways/jsonapi/pagerduty_credential_methods.rb +0 -139
  232. data/lib/flapjack/gateways/jsonapi/report_methods.rb +0 -146
  233. data/lib/flapjack/gateways/pagerduty.rb +0 -318
  234. data/lib/flapjack/gateways/pagerduty/alert.text.erb +0 -10
  235. data/lib/flapjack/gateways/web/public/css/select2-bootstrap.css +0 -87
  236. data/lib/flapjack/gateways/web/public/css/select2.css +0 -615
  237. data/lib/flapjack/gateways/web/public/css/tablesort.css +0 -67
  238. data/lib/flapjack/gateways/web/public/img/select2-spinner.gif +0 -0
  239. data/lib/flapjack/gateways/web/public/img/select2.png +0 -0
  240. data/lib/flapjack/gateways/web/public/img/select2x2.png +0 -0
  241. data/lib/flapjack/gateways/web/public/js/backbone.js +0 -1581
  242. data/lib/flapjack/gateways/web/public/js/backbone.jsonapi.js +0 -322
  243. data/lib/flapjack/gateways/web/public/js/flapjack.js +0 -82
  244. data/lib/flapjack/gateways/web/public/js/jquery.tablesorter.js +0 -1640
  245. data/lib/flapjack/gateways/web/public/js/jquery.tablesorter.widgets.js +0 -1390
  246. data/lib/flapjack/gateways/web/public/js/modules/contact.js +0 -520
  247. data/lib/flapjack/gateways/web/public/js/modules/entity.js +0 -28
  248. data/lib/flapjack/gateways/web/public/js/modules/medium.js +0 -40
  249. data/lib/flapjack/gateways/web/public/js/select2.js +0 -3397
  250. data/lib/flapjack/gateways/web/public/js/tablesort.js +0 -44
  251. data/lib/flapjack/gateways/web/public/js/underscore.js +0 -1276
  252. data/lib/flapjack/gateways/web/views/edit_contacts.html.erb +0 -173
  253. data/lib/flapjack/gateways/web/views/entities.html.erb +0 -30
  254. data/lib/flapjack/gateways/web/views/entity.html.erb +0 -51
  255. data/lib/flapjack/rack_logger.rb +0 -47
  256. data/lib/flapjack/redis_pool.rb +0 -42
  257. data/spec/lib/flapjack/data/entity_check_spec.rb +0 -1418
  258. data/spec/lib/flapjack/data/entity_spec.rb +0 -872
  259. data/spec/lib/flapjack/data/message_spec.rb +0 -30
  260. data/spec/lib/flapjack/data/migration_spec.rb +0 -104
  261. data/spec/lib/flapjack/data/notification_rule_spec.rb +0 -232
  262. data/spec/lib/flapjack/data/notification_spec.rb +0 -53
  263. data/spec/lib/flapjack/data/semaphore_spec.rb +0 -24
  264. data/spec/lib/flapjack/filters/acknowledgement_spec.rb +0 -6
  265. data/spec/lib/flapjack/filters/delays_spec.rb +0 -6
  266. data/spec/lib/flapjack/filters/ok_spec.rb +0 -6
  267. data/spec/lib/flapjack/filters/scheduled_maintenance_spec.rb +0 -6
  268. data/spec/lib/flapjack/filters/unscheduled_maintenance_spec.rb +0 -6
  269. data/spec/lib/flapjack/gateways/jsonapi/check_methods_spec.rb +0 -315
  270. data/spec/lib/flapjack/gateways/jsonapi/check_presenter_spec.rb +0 -223
  271. data/spec/lib/flapjack/gateways/jsonapi/contact_methods_spec.rb +0 -131
  272. data/spec/lib/flapjack/gateways/jsonapi/entity_methods_spec.rb +0 -389
  273. data/spec/lib/flapjack/gateways/jsonapi/medium_methods_spec.rb +0 -231
  274. data/spec/lib/flapjack/gateways/jsonapi/notification_rule_methods_spec.rb +0 -169
  275. data/spec/lib/flapjack/gateways/jsonapi/pagerduty_credential_methods_spec.rb +0 -114
  276. data/spec/lib/flapjack/gateways/jsonapi/report_methods_spec.rb +0 -590
  277. data/spec/lib/flapjack/gateways/pagerduty_spec.rb +0 -249
  278. data/spec/lib/flapjack/gateways/web/views/check.html.erb_spec.rb +0 -21
  279. data/spec/lib/flapjack/gateways/web/views/contact.html.erb_spec.rb +0 -24
  280. data/spec/lib/flapjack/gateways/web/views/index.html.erb_spec.rb +0 -16
  281. data/spec/lib/flapjack/redis_pool_spec.rb +0 -29
  282. data/spec/service_consumers/pacts/flapjack-diner_v1.0.json +0 -4702
  283. data/tasks/entities.rake +0 -151
  284. data/tasks/profile.rake +0 -282
  285. data/tmp/acknowledge.rb +0 -13
  286. data/tmp/create_config_yaml.rb +0 -16
  287. data/tmp/create_event_ok.rb +0 -30
  288. data/tmp/create_event_unknown.rb +0 -30
  289. data/tmp/create_events_failure.rb +0 -34
  290. data/tmp/create_events_ok.rb +0 -32
  291. data/tmp/create_events_ok_fail_ack_ok.rb +0 -53
  292. data/tmp/create_events_ok_failure.rb +0 -41
  293. data/tmp/create_events_ok_failure_ack.rb +0 -53
  294. data/tmp/dummy_contacts.json +0 -43
  295. data/tmp/dummy_entities.json +0 -37
  296. data/tmp/generate_nagios_test_hosts.rb +0 -16
  297. data/tmp/notification_rules.rb +0 -73
  298. data/tmp/parse_config_yaml.rb +0 -7
  299. data/tmp/redis_find_spurious_unknown_states.rb +0 -52
  300. data/tmp/test_json_post.rb +0 -19
  301. data/tmp/test_notification_rules_api.rb +0 -171
@@ -0,0 +1,388 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'net/http'
4
+ require 'uri'
5
+ require 'uri/https'
6
+
7
+ require 'flapjack/redis_proxy'
8
+ require 'flapjack/record_queue'
9
+ require 'flapjack/exceptions'
10
+
11
+ require 'flapjack/data/alert'
12
+ require 'flapjack/data/check'
13
+ require 'flapjack/data/event'
14
+
15
+ module Flapjack
16
+
17
+ module Gateways
18
+
19
+ class PagerDuty
20
+
21
+ # FIXME trap JSON errors
22
+ def self.send_pagerduty_event(opts = {})
23
+ event = { 'service_key' => opts[:service_key],
24
+ 'incident_key' => opts[:incident_key],
25
+ 'event_type' => opts[:event_type],
26
+ 'description' => opts[:description] }
27
+
28
+ uri = URI::HTTPS.build(:host => 'events.pagerduty.com',
29
+ :port => 443,
30
+ :path => '/generic/2010-04-15/create_event.json')
31
+ http = Net::HTTP.new(uri.host, uri.port)
32
+ http.use_ssl = true
33
+ http.verify_mode = OpenSSL::SSL::VERIFY_PEER
34
+ request = Net::HTTP::Post.new(uri.request_uri,
35
+ {'Content-type' =>'application/json'})
36
+ request.body = Flapjack.dump_json(event)
37
+ http_response = http.request(request)
38
+ status = http_response.code
39
+
40
+ response = nil
41
+ begin
42
+ response = Flapjack.load_json(http_response.body)
43
+ rescue JSON::JSONError
44
+ Flapjack.logger.error("failed to parse json from a post to #{uri.request_uri} ... response headers and body follows...")
45
+ end
46
+
47
+ Flapjack.logger.debug "send_pagerduty_event got a return code of #{status} - #{response.inspect}"
48
+ unless status.to_i == 200
49
+ raise "Error sending event to PagerDuty: status: #{status.to_s} - #{response.inspect}" +
50
+ " posted data: #{options[:body]}"
51
+ end
52
+ [status, response]
53
+ end
54
+
55
+ def self.test_pagerduty_connection
56
+ code, results = send_pagerduty_event(:service_key => '11111111111111111111111111111111',
57
+ :incident_key => 'Flapjack is running a NOOP',
58
+ :event_type => 'nop',
59
+ :description => 'I love APIs with noops.')
60
+ return true if '200'.eql?(code) && results['status'] =~ /success/i
61
+ Flapjack.logger.error "Error: test_pagerduty_connection: API returned #{code.to_s} #{results.inspect}"
62
+ false
63
+ end
64
+
65
+ class Notifier
66
+
67
+ include Flapjack::Utility
68
+
69
+ def initialize(opts = {})
70
+ @lock = opts[:lock]
71
+ @config = opts[:config]
72
+
73
+ # TODO support for config reloading
74
+ @queue = Flapjack::RecordQueue.new(@config['queue'] || 'pagerduty_notifications',
75
+ Flapjack::Data::Alert)
76
+
77
+ Flapjack.logger.debug("New PagerDuty::Notifier pikelet with the following options: #{@config.inspect}")
78
+ end
79
+
80
+ def start
81
+ until Flapjack::Gateways::PagerDuty.test_pagerduty_connection
82
+ Flapjack.logger.error("Can't connect to the PagerDuty API, retrying after 10 seconds")
83
+ Kernel.sleep(10)
84
+ end
85
+
86
+ begin
87
+ Zermelo.redis = Flapjack.redis
88
+
89
+ loop do
90
+ @lock.synchronize do
91
+ @queue.foreach {|alert| handle_alert(alert) }
92
+ end
93
+
94
+ @queue.wait
95
+ end
96
+ ensure
97
+ Flapjack.redis.quit
98
+ end
99
+ end
100
+
101
+ def stop_type
102
+ :exception
103
+ end
104
+
105
+ private
106
+
107
+ def handle_alert(alert)
108
+ check = alert.check
109
+
110
+ address = alert.address
111
+
112
+ Flapjack.logger.debug("processing PagerDuty notification service_key: #{address}, " +
113
+ "check: '#{check.name}', state: #{alert.state}, summary: #{alert.summary}")
114
+
115
+ message_template_erb, message_template =
116
+ load_template(@config['templates'], 'alert',
117
+ 'text', File.join(File.dirname(__FILE__), 'pager_duty'))
118
+
119
+ @alert = alert
120
+ bnd = binding
121
+
122
+ msg = nil
123
+ begin
124
+ msg = message_template_erb.result(bnd).chomp
125
+ rescue => e
126
+ Flapjack.logger.error "Error while executing the ERB for a PagerDuty message, " +
127
+ "ERB being executed: #{message_template}"
128
+ raise
129
+ end
130
+
131
+ pagerduty_type = case alert.type
132
+ when 'acknowledgement'
133
+ 'acknowledge'
134
+ when 'problem'
135
+ 'trigger'
136
+ when 'recovery'
137
+ 'resolve'
138
+ when 'test'
139
+ 'trigger'
140
+ end
141
+
142
+ # quick fix, may not be true in all cases
143
+ host_name, service_name = check.name.split(':', 2)
144
+
145
+ # Setting the HOSTNAME and the SERVICE makes them visible in the PagerDuty UI
146
+ Flapjack::Gateways::PagerDuty.send_pagerduty_event(
147
+ :service_key => address,
148
+ :incident_key => check.name,
149
+ :event_type => pagerduty_type,
150
+ :description => msg,
151
+ :details => {'HOSTNAME' => host_name,
152
+ 'SERVICE' => service_name}
153
+ )
154
+
155
+ Flapjack.logger.info "Sent alert successfully: #{alert.to_s}"
156
+ rescue => e
157
+ Flapjack.logger.error "Error generating or dispatching PagerDuty message: #{e.class}: #{e.message}\n" +
158
+ e.backtrace.join("\n")
159
+ Flapjack.logger.debug "Alert that could not be processed: \n" + alert.inspect
160
+ end
161
+
162
+ end
163
+
164
+ class AckFinder
165
+ SEM_PAGERDUTY_ACKS_RUNNING = 'sem_pagerduty_acks_running'
166
+ SEM_PAGERDUTY_ACKS_RUNNING_TIMEOUT = 3600
167
+
168
+ def initialize(opts = {})
169
+ @lock = opts[:lock]
170
+ @config = opts[:config]
171
+
172
+ @initial = true
173
+
174
+ Flapjack.logger.debug("New PagerDuty::AckFinder pikelet with the following options: #{@config.inspect}")
175
+
176
+ # TODO: only clear this if there isn't another PagerDuty gateway instance running
177
+ # or better, include an instance ID in the semaphore key name
178
+ Flapjack.redis.del(SEM_PAGERDUTY_ACKS_RUNNING)
179
+ end
180
+
181
+ def start
182
+ until Flapjack::Gateways::PagerDuty.test_pagerduty_connection
183
+ Flapjack.logger.error("Can't connect to the PagerDuty API, retrying after 10 seconds")
184
+ Kernel.sleep(10)
185
+ end
186
+
187
+ begin
188
+ Zermelo.redis = Flapjack.redis
189
+
190
+ loop do
191
+ @lock.synchronize do
192
+ # ensure we're the only instance of the PagerDuty acknowledgement check running (with a naive
193
+ # timeout of one hour to guard against stale locks caused by crashing code) either in this
194
+ # process or in other processes
195
+ if Flapjack.redis.setnx(SEM_PAGERDUTY_ACKS_RUNNING, 'true') == 0
196
+ Flapjack.logger.debug("skipping looking for acks in PagerDuty as this is already happening")
197
+ else
198
+ Flapjack.redis.expire(SEM_PAGERDUTY_ACKS_RUNNING, SEM_PAGERDUTY_ACKS_RUNNING_TIMEOUT)
199
+ find_pagerduty_acknowledgements
200
+ Flapjack.redis.del(SEM_PAGERDUTY_ACKS_RUNNING)
201
+ end
202
+
203
+ Kernel.sleep 10
204
+ end
205
+ end
206
+ ensure
207
+ Flapjack.redis.quit
208
+ end
209
+ end
210
+
211
+ def stop_type
212
+ :exception
213
+ end
214
+
215
+ def find_pagerduty_acknowledgements
216
+ Flapjack.logger.debug("looking for acks in PagerDuty for unack'd problems")
217
+
218
+ time = Time.now
219
+
220
+ unacked_failing_checks = []
221
+
222
+ Flapjack::Data::Check.lock(Flapjack::Data::ScheduledMaintenance,
223
+ Flapjack::Data::UnscheduledMaintenance) do
224
+
225
+ unacked_failing_checks = Flapjack::Data::Check.
226
+ intersect(:failing => true).reject do |check|
227
+
228
+ check.in_unscheduled_maintenance?(time) ||
229
+ check.in_scheduled_maintenance?(time)
230
+ end
231
+ end
232
+
233
+ if unacked_failing_checks.empty?
234
+ Flapjack.logger.debug "found no unacknowledged failing checks"
235
+ return
236
+ end
237
+
238
+ Flapjack.logger.debug "found unacknowledged failing checks as follows: " +
239
+ unacked_failing_checks.map(&:name).join(', ')
240
+
241
+ check_ids_by_medium(unacked_failing_checks.map(&:id), :time => time).each_pair do |medium, check_ids|
242
+ next if check_ids.empty?
243
+ checks = unacked_failing_checks.select {|c| check_ids.include?(c.id)}
244
+ next if checks.empty?
245
+
246
+ pagerduty_acknowledgements(time, medium.pagerduty_subdomain,
247
+ medium.pagerduty_token,
248
+ checks.map(&:name)).each do |incident|
249
+
250
+ inc_key = incident['incident_key']
251
+
252
+ pg_acknowledged_by = incident['last_status_change_by']
253
+ Flapjack.logger.info "#{inc_key} is acknowledged in PagerDuty, creating flapjack acknowledgement... "
254
+
255
+ who_text = ""
256
+
257
+ if !pg_acknowledged_by.nil? && !pg_acknowledged_by['name'].nil?
258
+ who_text = " by #{pg_acknowledged_by['name']}"
259
+ end
260
+
261
+ # default to 4 hours if no duration set in the medium
262
+ ack_duration = medium.pagerduty_ack_duration || (4 * 60 * 60)
263
+
264
+ Flapjack::Data::Event.create_acknowledgements(
265
+ @config['processor_queue'] || 'events',
266
+ [checks.detect {|c| c.name == inc_key}],
267
+ :summary => "Acknowledged on PagerDuty" + who_text,
268
+ :duration => ack_duration)
269
+ end
270
+ end
271
+ end
272
+
273
+ def check_ids_by_medium(filter_check_ids, opts = {})
274
+ time = opts[:time]
275
+
276
+ Flapjack::Data::Medium.lock(Flapjack::Data::Check, Flapjack::Data::Rule) do
277
+
278
+ media = Flapjack::Data::Medium.intersect(:transport => 'pagerduty')
279
+
280
+ already_acking_ids = []
281
+
282
+ media.all.each_with_object({}) do |medium, memo|
283
+ init_scope = Flapjack::Data::Check.intersect(:id => filter_check_ids)
284
+ ch_ids = medium.checks(:initial_scope => init_scope, :time => time).ids
285
+
286
+ to_ack_ids = (ch_ids & filter_check_ids) - already_acking_ids
287
+ already_acking_ids.push(*to_ack_ids)
288
+ memo[medium] = to_ack_ids
289
+ end
290
+ end
291
+ end
292
+
293
+ # returns any PagerDuty acknowledgements for the named checks
294
+ def pagerduty_acknowledgements(time, subdomain, token, check_names)
295
+ if subdomain.blank? || token.blank?
296
+ Flapjack.logger.warn("pagerduty_acknowledgements?: Unable to look for acknowledgements on PagerDuty" \
297
+ " as the following options are required: subdomain (#{subdomain}), token (#{token})")
298
+ return
299
+ end
300
+
301
+ t = time.utc
302
+
303
+ # handle paginated results
304
+ cumulative_incidents = []
305
+
306
+ offset = 0
307
+ requesting = true
308
+
309
+ while requesting do
310
+ response = pagerduty_acknowledgements_request(t, subdomain, token, 100, offset)
311
+
312
+ if response.nil?
313
+ cumulative_incidents = []
314
+ requesting = false
315
+ else
316
+ cumulative_incidents += response['incidents'].select do |incident|
317
+ check_names.include?(incident['incident_key'])
318
+ end
319
+
320
+ offset = response['offset'] + response['incidents'].size
321
+
322
+ requesting = (offset < response['total'])
323
+ end
324
+ end
325
+
326
+ @initial = false
327
+
328
+ cumulative_incidents
329
+ end
330
+
331
+ def pagerduty_acknowledgements_request(base_time, subdomain, token, limit, offset)
332
+ since_offset, until_offset = if @initial
333
+ # the last week -> one hour in the future
334
+ [(60 * 60 * 24 * 7), (60 * 60)]
335
+ else
336
+ # the last 15 minutes -> one hour in the future
337
+ [(60 * 15), (60 * 60)]
338
+ end
339
+
340
+ query = {'fields' => 'incident_key,incident_number,last_status_change_by',
341
+ 'since' => (base_time - since_offset).iso8601,
342
+ 'until' => (base_time + until_offset).iso8601,
343
+ 'status' => 'acknowledged'}
344
+
345
+ if (limit != 100) || (offset != 0)
346
+ query.update(:limit => limit, :offset => offset)
347
+ end
348
+
349
+ uri = URI::HTTPS.build(:host => "#{subdomain}.pagerduty.com",
350
+ :path => '/api/v1/incidents',
351
+ :port => 443,
352
+ :query => URI.encode_www_form(query))
353
+
354
+ request = Net::HTTP::Get.new(uri.request_uri,
355
+ {'Content-type' => 'application/json',
356
+ 'Authorization' => "Token token=#{token}"})
357
+
358
+ http = Net::HTTP.new(uri.host, uri.port)
359
+ http.use_ssl = true
360
+ http.verify_mode = OpenSSL::SSL::VERIFY_PEER
361
+
362
+ Flapjack.logger.debug("pagerduty_acknowledgements: request to #{uri.request_uri}")
363
+ Flapjack.logger.debug("pagerduty_acknowledgements: query: #{query.inspect}")
364
+ Flapjack.logger.debug("pagerduty_acknowledgements: auth: #{token}")
365
+
366
+ http_response = http.request(request)
367
+ Flapjack.logger.debug(http_response.inspect)
368
+
369
+ response = nil
370
+ begin
371
+ response = Flapjack.load_json(http_response.body)
372
+ rescue JSON::JSONError
373
+ Flapjack.logger.error("failed to parse json from a post to #{url} ... response headers and body follows...")
374
+ end
375
+
376
+ Flapjack.logger.debug("pagerduty_acknowledgements: decoded response as: #{response.inspect}")
377
+ if response.nil? || !response.has_key?('incidents') || !response['incidents'].is_a?(Array)
378
+ Flapjack.logger.error('no valid response received from PagerDuty!')
379
+ return
380
+ end
381
+
382
+ response
383
+ end
384
+
385
+ end
386
+ end
387
+ end
388
+ end
@@ -0,0 +1,13 @@
1
+ <% check = @alert.check -%>
2
+ <% type = @alert.type -%>
3
+ <%= @alert.type_sentence_case %>: <%= '"' + check.name + '"' -%>
4
+ <% unless ['acknowledgement', 'test'].include?(type) -%>
5
+ is <%= @alert.state_title_case -%>
6
+ <% end -%>
7
+ <% if 'acknowledgement'.eql?(type) -%>
8
+ has been acknowledged, unscheduled maintenance created for <%= time_period_in_words(@alert.acknowledgement_duration) -%>
9
+ <% end -%>
10
+ <% summary = @alert.summary -%>
11
+ <% unless summary.nil? || summary.empty? -%>
12
+ , <%= summary -%>
13
+ <% end -%>
@@ -1,13 +1,13 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- require 'em-synchrony'
4
- require 'em-synchrony/em-http'
5
- require 'active_support/inflector'
3
+ require 'erb'
6
4
 
7
- require 'flapjack/redis_pool'
5
+ require 'flapjack/redis_proxy'
6
+ require 'flapjack/record_queue'
7
+ require 'flapjack/utility'
8
+ require 'flapjack/exceptions'
8
9
 
9
10
  require 'flapjack/data/alert'
10
- require 'flapjack/utility'
11
11
 
12
12
  module Flapjack
13
13
  module Gateways
@@ -15,64 +15,67 @@ module Flapjack
15
15
 
16
16
  include Flapjack::Utility
17
17
 
18
+ attr_accessor :sent
19
+
18
20
  def initialize(opts = {})
21
+ @lock = opts[:lock]
19
22
  @config = opts[:config]
20
- @logger = opts[:logger]
21
- @redis_config = opts[:redis_config] || {}
22
- @redis = Flapjack::RedisPool.new(:config => @redis_config, :size => 1, :logger => @logger)
23
23
 
24
- @logger.info("starting")
25
- @logger.debug("new slack gateway pikelet with the following options: #{@config.inspect}")
24
+ # TODO support for config reloading
25
+ @queue = Flapjack::RecordQueue.new(@config['queue'] || 'slack_notifications',
26
+ Flapjack::Data::Alert)
26
27
 
27
28
  @sent = 0
28
29
  end
29
30
 
30
- def stop
31
- @logger.info("stopping")
32
- @should_quit = true
31
+ def start
32
+ Flapjack.logger.debug("new slack gateway pikelet with the following options: #{@config.inspect}")
33
33
 
34
- redis_uri = @redis_config[:path] ||
35
- "redis://#{@redis_config[:host] || '127.0.0.1'}:#{@redis_config[:port] || '6379'}/#{@redis_config[:db] || '0'}"
36
- shutdown_redis = EM::Hiredis.connect(redis_uri)
37
- shutdown_redis.rpush(@config['queue'], Flapjack.dump_json('notification_type' => 'shutdown'))
38
- end
34
+ begin
35
+ Zermelo.redis = Flapjack.redis
39
36
 
40
- def start
41
- queue = @config['queue']
42
-
43
- until @should_quit
44
- begin
45
- @logger.debug("slack gateway is going into blpop mode on #{queue}")
46
- alert = Flapjack::Data::Alert.next(queue, :redis => @redis, :logger => @logger)
47
- deliver(alert) unless alert.nil?
48
- rescue => e
49
- @logger.error "Error generating or dispatching Slack message: #{e.class}: #{e.message}\n" +
50
- e.backtrace.join("\n")
37
+ loop do
38
+ @lock.synchronize do
39
+ @queue.foreach {|alert| handle_alert(alert) }
40
+ end
41
+
42
+ @queue.wait
51
43
  end
44
+ ensure
45
+ Flapjack.redis.quit
52
46
  end
53
47
  end
54
48
 
55
- def deliver(alert)
56
- account_sid = @config["account_sid"]
57
- endpoint = @config["endpoint"]
58
- icon_emoji = @config["icon_emoji"] || ':ghost:'
49
+ def stop_type
50
+ :exception
51
+ end
52
+
53
+ private
54
+
55
+ def handle_alert(alert)
56
+ Flapjack.logger.debug "Woo, got an alert to send out: #{alert.inspect}"
57
+
58
+ account_sid = @config['account_sid']
59
+ endpoint = @config['endpoint']
60
+ icon_emoji = @config['icon_emoji'] || ':ghost:'
59
61
 
60
- channel = "##{alert.address}"
62
+ channel = "##{alert.medium.address}"
61
63
  channel = '#general' if (channel.size == 1)
62
64
  notification_id = alert.notification_id
63
65
  message_type = alert.rollup ? 'rollup' : 'alert'
64
66
 
65
67
  slack_template_erb, slack_template =
66
- load_template(@config['templates'], message_type, 'text',
67
- File.join(File.dirname(__FILE__), 'slack'))
68
+ load_template(@config['templates'], message_type,
69
+ 'text', File.join(File.dirname(__FILE__), 'slack'))
68
70
 
69
71
  @alert = alert
70
72
  bnd = binding
71
73
 
74
+ message = nil
72
75
  begin
73
76
  message = slack_template_erb.result(bnd).chomp
74
77
  rescue => e
75
- @logger.error "Error while executing the ERB for an sms: " +
78
+ Flapjack.logger.error 'Error while executing the ERB for a slack message: ' \
76
79
  "ERB being executed: #{slack_template}"
77
80
  raise
78
81
  end
@@ -80,8 +83,8 @@ module Flapjack
80
83
  errors = []
81
84
 
82
85
  [
83
- [endpoint, "Slack endpoint is missing"],
84
- [account_sid, "Slack account_sid is missing"]
86
+ [endpoint, 'Slack endpoint is missing'],
87
+ [account_sid, 'Slack account_sid is missing']
85
88
  ].each do |val_err|
86
89
 
87
90
  next unless val_err.first.nil? || (val_err.first.respond_to?(:empty?) && val_err.first.empty?)
@@ -89,7 +92,7 @@ module Flapjack
89
92
  end
90
93
 
91
94
  unless errors.empty?
92
- errors.each {|err| @logger.error err }
95
+ errors.each {|err| Flapjack.logger.error err }
93
96
  return
94
97
  end
95
98
 
@@ -99,25 +102,30 @@ module Flapjack
99
102
  'text' => message,
100
103
  'icon_emoji' => icon_emoji
101
104
  )
102
- @logger.debug "payload: #{payload.inspect}"
105
+ Flapjack.logger.debug "payload: #{payload.inspect}"
103
106
 
104
- http = EM::HttpRequest.new("#{endpoint}").post(:body => {'payload' => payload})
107
+ uri = URI.parse(endpoint)
105
108
 
106
- @logger.debug "server response: #{http.response}"
109
+ http = Net::HTTP.new(uri.host, uri.port)
110
+ http.use_ssl = true
111
+ http.verify_mode = OpenSSL::SSL::VERIFY_PEER
112
+ request = Net::HTTP::Post.new(uri.request_uri)
113
+ request.body = Flapjack.dump_json(:payload => payload)
114
+ http_response = http.request(request)
115
+ status = http_response.code
107
116
 
108
- status = (http.nil? || http.response_header.nil?) ? nil : http.response_header.status
109
117
  if (status >= 200) && (status <= 206)
110
118
  @sent += 1
111
119
  alert.record_send_success!
112
- @logger.debug "Sent message via Slack, response status is #{status}, " +
120
+ Flapjack.logger.debug "Sent message via Slack, response status is #{status}, " +
113
121
  "notification_id: #{notification_id}"
114
122
  else
115
- @logger.error "Failed to send message via Slack, response status is #{status}, " +
123
+ Flapjack.logger.error "Failed to send message via Slack, response status is #{status}, " +
116
124
  "notification_id: #{notification_id}"
117
125
  end
118
126
  rescue => e
119
- @logger.error "Error generating or delivering sms to #{alert.address}: #{e.class}: #{e.message}"
120
- @logger.error e.backtrace.join("\n")
127
+ Flapjack.logger.error "Error generating or delivering Slack message to #{alert.medium.address}: #{e.class}: #{e.message}"
128
+ Flapjack.logger.error e.backtrace.join("\n")
121
129
  raise
122
130
  end
123
131