Skip to content

Commit 60953a9

Browse files
Merge pull request #14 from DEFRA/test-coverage
adds tests, some bug fixes, adds test key override
2 parents 45f1e1c + d967efd commit 60953a9

File tree

6 files changed

+286
-33
lines changed

6 files changed

+286
-33
lines changed

src/config/index.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,14 @@ const config = convict({
377377
default: 'key',
378378
env: 'PLATFORM_INTEGRATION_KEY'
379379
}
380+
},
381+
'platform-tenant-cko': {
382+
integrationKey: {
383+
doc: 'Integration key for digital service',
384+
format: String,
385+
default: 'key',
386+
env: 'PLATFORM_TENANT_CKO_INTEGRATION_KEY'
387+
}
380388
}
381389
},
382390
services: {

src/config/pagerduty-service-override.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ const serviceToPagerDutyServiceOverride = {
3535
},
3636
'cdp-waf': {
3737
teams: ['platform']
38+
},
39+
'cdp-canary': {
40+
teams: ['platform-tenant-cko']
3841
}
3942
}
4043

src/helpers/fetch/fetch-service.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import { config } from '~/src/config/index.js'
22

33
/**
4+
* Fetch Service info from portal backend.
45
* @param {string} name
5-
* @returns {Promise<{teams: [{teamId: string}] }|null>}
6+
* @returns {Promise<{teams: [{name: string}] }|null>}
67
*/
78
async function fetchService(name) {
89
const endpoint = config.get('portalBackendUrl') + `/services/${name}`

src/helpers/sqs/sqs-listener.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ const grafanaAlertListener = {
7070
server.logger.error(error, `SQS ${queueUrl}: ${error.message}`)
7171
}
7272
try {
73-
await handleGrafanaPagerDutyAlert(message, server)
73+
await handleGrafanaPagerDutyAlert(message)
7474
const receiptHandle = message.ReceiptHandle
7575
await deleteSqsMessage(server.sqs, queueUrl, receiptHandle)
7676
} catch (error) {

src/listeners/grafana/pagerduty/handle-grafana-pagerduty-alerts.js

Lines changed: 77 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -2,31 +2,44 @@ import { config } from '~/src/config/index.js'
22
import { serviceToPagerDutyServiceOverride } from '~/src/config/pagerduty-service-override.js'
33
import { sendAlert } from '~/src/helpers/pagerduty/send-alert.js'
44
import { fetchService } from '~/src/helpers/fetch/fetch-service.js'
5-
import { fetchTeam } from '~/src/helpers/fetch/fetch-team.js'
5+
66
import crypto from 'crypto'
7+
import { createLogger } from '~/src/helpers/logging/logger.js'
78

8-
const sendAlerts = config.get('pagerduty.sendAlerts')
9+
const logger = createLogger()
910

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}`
1218
return crypto.createHash('md5').update(str).digest('hex')
1319
}
1420

1521
/**
16-
*
22+
* Decides if an alert should be sent based off the environment it originated in.
1723
* @param {Alert} alert
24+
* @param {string[]} environments
1825
* @returns {boolean}
1926
*/
20-
function shouldSendAlert(alert) {
27+
export function shouldSendAlert(alert, environments) {
2128
const overrides =
2229
serviceToPagerDutyServiceOverride[alert.service]?.environments
2330

24-
const alertEnvironments = overrides || config.get('alertEnvironments')
31+
const alertEnvironments = overrides || environments
2532
return alertEnvironments.includes(alert.environment)
2633
}
2734

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
3043

3144
if (teams) {
3245
return teams
@@ -40,62 +53,86 @@ async function getTeams(alert, logger) {
4053

4154
return []
4255
}
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)
4558
}
4659

60+
/**
61+
* Gets integration key for a team from config.js
62+
* @param {string} team
63+
* @returns {string|null}
64+
*/
4765
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+
}
4971
}
5072

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) {
5379
const overrides =
5480
serviceToPagerDutyServiceOverride[alert.service]?.technicalService
5581

5682
const service = overrides || alert.service
5783

58-
return config.get(`pagerduty.services.${service}.integrationKey`)
84+
try {
85+
return config.get(`pagerduty.services.${service}.integrationKey`)
86+
} catch {
87+
return null
88+
}
5989
}
6090

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) {
6397
const payload = JSON.parse(message.Body)
6498

65-
if (!shouldSendAlert(payload)) {
99+
if (!shouldSendAlert(payload, config.get('alertEnvironments'))) {
66100
return
67101
}
68102

69103
if (!payload?.service) {
70104
logger.warn(
71105
`alert did not contain a service field:\n${JSON.stringify(payload)}`
72106
)
73-
return []
107+
return
74108
}
75109

76-
const teams = await getTeams(payload, logger)
110+
const teams = await getTeams(payload)
77111

78-
let integrationKeys = teams
112+
const integrationKeys = teams
79113
.map((team) => findIntegrationKeyForTeam(team))
80114
.filter((t) => t)
81115

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+
}
84121
}
85122

86-
if (!integrationKeys.length) {
87-
server.logger.debug(
123+
if (integrationKeys.length === 0) {
124+
logger.info(
88125
`No integration key found for ${payload.service}. Not sending alert - MessageId: ${message.MessageId}`
89126
)
90127
return
91128
}
92129

93-
server.logger.info(
130+
logger.info(
94131
`Grafana alert ${payload.status} for ${payload.service} in ${payload.environment} - Alert: ${payload.alertName} MessageId: ${message.MessageId}`
95132
)
96133

97-
if (sendAlerts) {
98-
server.logger.info('Sending PagerDuty alert')
134+
if (config.get('pagerduty.sendAlerts')) {
135+
logger.info('Sending PagerDuty alert')
99136

100137
let eventAction
101138

@@ -104,7 +141,7 @@ async function handleGrafanaPagerDutyAlert(message, server) {
104141
} else if (payload.status === 'resolved') {
105142
eventAction = 'resolve'
106143
} else {
107-
server.logger.warn(
144+
logger.warn(
108145
`Unexpected status ${payload.status} not sending alert:\n${JSON.stringify(payload)}`
109146
)
110147
return
@@ -115,8 +152,17 @@ async function handleGrafanaPagerDutyAlert(message, server) {
115152
const alerts = integrationKeys.map(async (integrationKey) => {
116153
await sendAlert(integrationKey, payload, teams, dedupeKey, eventAction)
117154
})
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)')
119163
}
120164
}
121165

122-
export { handleGrafanaPagerDutyAlert }
166+
/**
167+
* @import { Logger } from 'pino'
168+
*/

0 commit comments

Comments
 (0)