UNPKG

5.29 kBtext/coffeescriptView Raw
1_ = require 'lodash'
2async = require 'async'
3request = require 'request'
4debug = (require 'debug')('octoblu-flow-canary:slack')
5
6class Slack
7
8 constructor: ({@CANARY_UPDATE_INTERVAL,@CANARY_HEALTH_CHECK_MAX_DIFF}={}) ->
9 @slackNotifications = {}
10
11 @SLACK_CHANNEL_URL = process.env.SLACK_CHANNEL_URL
12 @SLACK_EMERGENCY_CHANNEL = process.env.SLACK_EMERGENCY_CHANNEL
13 @SLACK_EMERGENCY_CHANNEL ?= "#performance-problems"
14 throw new Error('SLACK_CHANNEL_URL must be defined') unless @SLACK_CHANNEL_URL
15 @startTime = Date.now()
16
17 sendSlackNotifications: (stats, callback) =>
18 notifications = []
19 update = false
20 ded = false
21
22 if !@slackNotifications['lastNotify']
23 lowerResponseTime = (@CANARY_UPDATE_INTERVAL - @CANARY_HEALTH_CHECK_MAX_DIFF) / 1000
24 upperResponseTime = (@CANARY_UPDATE_INTERVAL + @CANARY_HEALTH_CHECK_MAX_DIFF) / 1000
25 notifications.push @curryPostSlackNotification {
26 attachments: [{color:"good",text:"The flow-canary is alive! Expected response time is between #{lowerResponseTime} and #{upperResponseTime} seconds"}]
27 }
28 @slackNotifications['lastNotify'] = Date.now()
29
30 @slackNotifications['lastError'] ?= 0
31 stats.errors ?= []
32 _.each stats.errors.reverse(), (errorInfo) =>
33 if @slackNotifications['lastError'] < errorInfo.time
34 @slackNotifications['lastError'] = errorInfo.time
35 notifications.push @curryPostSlackNotification {
36 icon_emoji: ':bird:'
37 username: 'flow-canary-wut'
38 attachments: [{color:"warning",text:"Error: #{errorInfo.url}"}]
39 }
40
41 _.forIn stats.flows, (flow, flowId) =>
42 @slackNotifications[flowId] ?= true
43
44 @slackNotifications['lastFailure'] ?= 0
45 flow.failures ?= []
46 _.each flow.failures.reverse(), (errorInfo) =>
47 if @slackNotifications['lastFailure'] < errorInfo.time
48 update = true
49 ded = true
50 @slackNotifications['lastFailure'] = errorInfo.time
51 @slackNotifications[flowId] = false
52 timeDiffInSeconds = errorInfo.timeDiff / 1000
53 notifications.push @curryPostSlackNotification {
54 icon_emoji: ':skull:'
55 username: 'flow-canary-ded'
56 attachments: [
57 {
58 color:"danger",
59 text:"Flow #{flow.name} (#{flowId}) failed because it took #{timeDiffInSeconds} seconds to respond"
60 }
61 ]
62 }
63
64 # _.each flow.startTime?.reverse(), (lastStart) =>
65 # if @slackNotifications['lastNotify'] < lastStart
66 # notifications.push @curryPostSlackNotification {
67 # icon_emoji: ':skull:'
68 # username: 'flow-canary-ded'
69 # attachments: [
70 # {
71 # color:"danger",
72 # text:"Flow #{flow.name} (#{flowId}) was restarted at #{new Date(lastStart)}"
73 # }
74 # ]
75 # }
76
77 if flow.passing and !@slackNotifications[flowId]
78 @slackNotifications[flowId] = true
79 update = true
80 notifications.push @curryPostSlackNotification {
81 attachments: [{color:"good",text:"Flow #{flow.name} (#{flowId}) is now passing"}]
82 }
83
84 if update
85 failingFlows = ""
86 failCount = 0
87 _.each stats.flows, (flowInfo) =>
88 if !flowInfo.passing
89 failingFlows += ">#{flowInfo.name}< "
90 failCount += 1
91 if failCount == 0
92 notifications.push @curryPostSlackNotification {
93 attachments: [{color:"good",text: "All flows are now passing"}]
94 }
95 if failCount == stats.flows?.length && (Date.now()-@startTime) > 5*60*1000
96 notifications.push @curryPostSlackNotification {
97 icon_emoji: ':skull:'
98 username: 'flow-canary-ded'
99 attachments: [{color:"danger",text: "All flows are failing!"}]
100 }, true
101 else if ded
102 notifications.push @curryPostSlackNotification {
103 icon_emoji: ':skull:'
104 username: 'flow-canary-ded'
105 attachments: [{color:"danger",text:"#{failCount} flows are failing: #{failingFlows}"}]
106 }
107 else if failCount != 0
108 notifications.push @curryPostSlackNotification {
109 attachments: [{color:"danger",text:"#{failCount} flows are failing: #{failingFlows}"}]
110 }
111
112 lastUpdate = Date.now() - @slackNotifications['lastNotify']
113 if !stats.passing and lastUpdate >= 60*60*1000
114 notifications.push @curryPostSlackNotification {
115 icon_emoji: ':skull:'
116 username: 'flow-canary-ded'
117 attachments: [{color:"danger",text:"Flow-canary is still dead!"}]
118 }
119
120 @slackNotifications['lastNotify'] = Date.now()
121 async.series notifications, callback
122
123 curryPostSlackNotification: (payload, emergency)=>
124 defaultPayload =
125 username: 'flow-canary'
126 icon_emoji: ':baby_chick:'
127
128 channel = @SLACK_EMERGENCY_CHANNEL if emergency
129
130 options =
131 uri: @SLACK_CHANNEL_URL
132 channel: channel
133 method: 'POST'
134 body: _.merge defaultPayload, payload
135 json: true
136
137 return (callback) =>
138 debug JSON.stringify options
139 request options, (error, response, body) =>
140 console.error 'Slack Error', error if error?
141 debug {body}
142 callback()
143
144module.exports = Slack