@@ -2,31 +2,44 @@ import { config } from '~/src/config/index.js'
2
2
import { serviceToPagerDutyServiceOverride } from '~/src/config/pagerduty-service-override.js'
3
3
import { sendAlert } from '~/src/helpers/pagerduty/send-alert.js'
4
4
import { fetchService } from '~/src/helpers/fetch/fetch-service.js'
5
- import { fetchTeam } from '~/src/helpers/fetch/fetch-team.js'
5
+
6
6
import crypto from 'crypto'
7
+ import { createLogger } from '~/src/helpers/logging/logger.js'
7
8
8
- const sendAlerts = config . get ( 'pagerduty.sendAlerts' )
9
+ const logger = createLogger ( )
9
10
10
- function createDedupeKey ( payload ) {
11
- const str = `${ payload . summary } ${ payload . service } ${ payload . environment } ${ payload . startsAt } `
11
+ /**
12
+ * Generates a pagerduty dedupe key based off the message's content.
13
+ * @param {{summary: string, service: string, environment: string, startsAt: string}|null } payload
14
+ * @returns {string }
15
+ */
16
+ export function createDedupeKey ( payload ) {
17
+ const str = `${ payload ?. summary } ${ payload ?. service } ${ payload ?. environment } ${ payload ?. startsAt } `
12
18
return crypto . createHash ( 'md5' ) . update ( str ) . digest ( 'hex' )
13
19
}
14
20
15
21
/**
16
- *
22
+ * Decides if an alert should be sent based off the environment it originated in.
17
23
* @param {Alert } alert
24
+ * @param {string[] } environments
18
25
* @returns {boolean }
19
26
*/
20
- function shouldSendAlert ( alert ) {
27
+ export function shouldSendAlert ( alert , environments ) {
21
28
const overrides =
22
29
serviceToPagerDutyServiceOverride [ alert . service ] ?. environments
23
30
24
- const alertEnvironments = overrides || config . get ( 'alertEnvironments' )
31
+ const alertEnvironments = overrides || environments
25
32
return alertEnvironments . includes ( alert . environment )
26
33
}
27
34
28
- async function getTeams ( alert , logger ) {
29
- let teams = serviceToPagerDutyServiceOverride [ alert . service ] ?. teams
35
+ /**
36
+ * Gets the teams associated to an alert.
37
+ * Uses overrides first if set, otherwise uses owners of service from portal-backend.
38
+ * @param {Alert } alert
39
+ * @returns {Promise<string[]> }
40
+ */
41
+ export async function getTeams ( alert ) {
42
+ const teams = serviceToPagerDutyServiceOverride [ alert . service ] ?. teams
30
43
31
44
if ( teams ) {
32
45
return teams
@@ -40,62 +53,86 @@ async function getTeams(alert, logger) {
40
53
41
54
return [ ]
42
55
}
43
- teams = service . teams . map ( async ( team ) => await fetchTeam ( team . teamId ) )
44
- return await Promise . all ( teams )
56
+
57
+ return service . teams . map ( ( team ) => team . name )
45
58
}
46
59
60
+ /**
61
+ * Gets integration key for a team from config.js
62
+ * @param {string } team
63
+ * @returns {string|null }
64
+ */
47
65
function findIntegrationKeyForTeam ( team ) {
48
- return config . get ( `pagerduty.teams.${ team } .integrationKey` )
66
+ try {
67
+ return config . get ( `pagerduty.teams.${ team } .integrationKey` )
68
+ } catch {
69
+ return null
70
+ }
49
71
}
50
72
51
- // If there is an integration key, it's assumed that we should send a PagerDuty alert
52
- function findIntegrationKeyForService ( alert ) {
73
+ /**
74
+ * If there is an integration key, it's assumed that we should send a PagerDuty alert
75
+ * @param {Alert } alert
76
+ * @returns {string|null }
77
+ */
78
+ export function findIntegrationKeyForService ( alert ) {
53
79
const overrides =
54
80
serviceToPagerDutyServiceOverride [ alert . service ] ?. technicalService
55
81
56
82
const service = overrides || alert . service
57
83
58
- return config . get ( `pagerduty.services.${ service } .integrationKey` )
84
+ try {
85
+ return config . get ( `pagerduty.services.${ service } .integrationKey` )
86
+ } catch {
87
+ return null
88
+ }
59
89
}
60
90
61
- async function handleGrafanaPagerDutyAlert ( message , server ) {
62
- const logger = server . logger
91
+ /**
92
+ *
93
+ * @param {{MessageId: string, Body: string} } message
94
+ * @returns {Promise<void> }
95
+ */
96
+ export async function handleGrafanaPagerDutyAlert ( message ) {
63
97
const payload = JSON . parse ( message . Body )
64
98
65
- if ( ! shouldSendAlert ( payload ) ) {
99
+ if ( ! shouldSendAlert ( payload , config . get ( 'alertEnvironments' ) ) ) {
66
100
return
67
101
}
68
102
69
103
if ( ! payload ?. service ) {
70
104
logger . warn (
71
105
`alert did not contain a service field:\n${ JSON . stringify ( payload ) } `
72
106
)
73
- return [ ]
107
+ return
74
108
}
75
109
76
- const teams = await getTeams ( payload , logger )
110
+ const teams = await getTeams ( payload )
77
111
78
- let integrationKeys = teams
112
+ const integrationKeys = teams
79
113
. map ( ( team ) => findIntegrationKeyForTeam ( team ) )
80
114
. filter ( ( t ) => t )
81
115
82
- if ( ! integrationKeys ) {
83
- integrationKeys = [ findIntegrationKeyForService ( payload , server . logger ) ]
116
+ if ( integrationKeys . length === 0 ) {
117
+ const key = findIntegrationKeyForService ( payload )
118
+ if ( key ) {
119
+ integrationKeys . push ( key )
120
+ }
84
121
}
85
122
86
- if ( ! integrationKeys . length ) {
87
- server . logger . debug (
123
+ if ( integrationKeys . length === 0 ) {
124
+ logger . info (
88
125
`No integration key found for ${ payload . service } . Not sending alert - MessageId: ${ message . MessageId } `
89
126
)
90
127
return
91
128
}
92
129
93
- server . logger . info (
130
+ logger . info (
94
131
`Grafana alert ${ payload . status } for ${ payload . service } in ${ payload . environment } - Alert: ${ payload . alertName } MessageId: ${ message . MessageId } `
95
132
)
96
133
97
- if ( sendAlerts ) {
98
- server . logger . info ( 'Sending PagerDuty alert' )
134
+ if ( config . get ( 'pagerduty. sendAlerts' ) ) {
135
+ logger . info ( 'Sending PagerDuty alert' )
99
136
100
137
let eventAction
101
138
@@ -104,7 +141,7 @@ async function handleGrafanaPagerDutyAlert(message, server) {
104
141
} else if ( payload . status === 'resolved' ) {
105
142
eventAction = 'resolve'
106
143
} else {
107
- server . logger . warn (
144
+ logger . warn (
108
145
`Unexpected status ${ payload . status } not sending alert:\n${ JSON . stringify ( payload ) } `
109
146
)
110
147
return
@@ -115,8 +152,17 @@ async function handleGrafanaPagerDutyAlert(message, server) {
115
152
const alerts = integrationKeys . map ( async ( integrationKey ) => {
116
153
await sendAlert ( integrationKey , payload , teams , dedupeKey , eventAction )
117
154
} )
118
- await Promise . all ( alerts )
155
+ const result = await Promise . allSettled ( alerts )
156
+ result
157
+ . filter ( ( r ) => r . status === 'rejected' )
158
+ . forEach ( ( r ) => {
159
+ logger . error ( `failed to send pager duty alert! ${ r . reason } ` )
160
+ } )
161
+ } else {
162
+ logger . warn ( 'NOT Sending PagerDuty alert (sendAlerts disabled in config)' )
119
163
}
120
164
}
121
165
122
- export { handleGrafanaPagerDutyAlert }
166
+ /**
167
+ * @import { Logger } from 'pino'
168
+ */
0 commit comments