Skip to content

Commit 93ac4e9

Browse files
committed
chore(jenkins): refactor jenkins-status using events
1 parent e9adcb4 commit 93ac4e9

File tree

4 files changed

+146
-98
lines changed

4 files changed

+146
-98
lines changed

app.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ app.use(bunyanMiddleware({
3030
}))
3131

3232
require('./lib/github-events')(app, events)
33+
require('./lib/jenkins-events')(app, events)
3334

3435
app.use(function logUnhandledErrors (err, req, res, next) {
3536
logger.error(err, 'Unhandled error while responding to incoming HTTP request')

lib/jenkins-events.js

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
'use strict'
2+
3+
const pushJenkinsUpdate = require('../lib/push-jenkins-update')
4+
5+
const debug = require('debug')('jenkins-events')
6+
const enabledRepos = ['citgm', 'http-parser', 'node', 'node-auto-test']
7+
8+
const listOfKnownJenkinsIps = process.env.JENKINS_WORKER_IPS ? process.env.JENKINS_WORKER_IPS.split(',') : []
9+
10+
function isKnownJenkinsIp (req) {
11+
const ip = req.connection.remoteAddress.split(':').pop()
12+
13+
if (listOfKnownJenkinsIps.length && !listOfKnownJenkinsIps.includes(ip)) {
14+
req.log.warn({ ip }, 'Ignoring, not allowed to push Jenkins updates')
15+
return false
16+
}
17+
18+
return true
19+
}
20+
21+
function isRelatedToPullRequest (gitRef) {
22+
// refs/pull/12345/head vs refs/heads/v8.x-staging/head
23+
return gitRef.includes('/pull/')
24+
}
25+
26+
module.exports = (app, events) => {
27+
app.post('/:repo/jenkins/:event', async (req, res) => {
28+
const isValid = pushJenkinsUpdate.validate(req.body)
29+
const repo = req.params.repo
30+
const event = req.params.event
31+
const owner = req.body.owner || process.env.JENKINS_DEFAULT_GH_OWNER || 'nodejs'
32+
33+
if (!isValid) {
34+
return res.status(400).end('Invalid payload')
35+
}
36+
37+
if (!isRelatedToPullRequest(req.body.ref)) {
38+
return res.status(400).end('Will only push builds related to pull requests')
39+
}
40+
41+
if (!enabledRepos.includes(repo)) {
42+
return res.status(400).end('Invalid repository')
43+
}
44+
45+
if (!isKnownJenkinsIp(req)) {
46+
return res.status(401).end('Invalid Jenkins IP')
47+
}
48+
49+
const data = {
50+
...req.body,
51+
owner,
52+
repo,
53+
event
54+
}
55+
56+
try {
57+
await app.emitJenkinsEvent(event, data, req.log)
58+
res.status(200)
59+
} catch (err) {
60+
req.log.error(err, 'Error while emitting Jenkins event')
61+
res.status(500)
62+
}
63+
64+
res.end()
65+
})
66+
67+
app.emitJenkinsEvent = function emitJenkinsEvent (event, data, logger) {
68+
const { identifier } = data
69+
70+
// create unique logger which is easily traceable throughout the entire app
71+
// by having e.g. "nodejs/nodejs.org/#1337" part of every subsequent log statement
72+
data.logger = logger.child({ identifier, event }, true)
73+
74+
data.logger.info('Emitting Jenkins event')
75+
debug(data)
76+
77+
return events.emit(`jenkins.${event}`, data)
78+
}
79+
}

scripts/jenkins-status.js

Lines changed: 24 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -1,84 +1,36 @@
11
'use strict'
22

33
const pushJenkinsUpdate = require('../lib/push-jenkins-update')
4-
const enabledRepos = ['citgm', 'http-parser', 'node']
54

6-
const jenkinsIpWhitelist = process.env.JENKINS_WORKER_IPS ? process.env.JENKINS_WORKER_IPS.split(',') : []
5+
function handleJenkinsStart (event) {
6+
const { repo, owner } = event
77

8-
function isJenkinsIpWhitelisted (req) {
9-
const ip = req.connection.remoteAddress.split(':').pop()
10-
11-
if (jenkinsIpWhitelist.length && !jenkinsIpWhitelist.includes(ip)) {
12-
req.log.warn({ ip }, 'Ignoring, not allowed to push Jenkins updates')
13-
return false
14-
}
15-
16-
return true
17-
}
18-
19-
function isRelatedToPullRequest (gitRef) {
20-
// refs/pull/12345/head vs refs/heads/v8.x-staging/head
21-
return gitRef.includes('/pull/')
22-
}
23-
24-
module.exports = function (app) {
25-
app.post('/:repo/jenkins/start', (req, res) => {
26-
const isValid = pushJenkinsUpdate.validate(req.body)
27-
const repo = req.params.repo
28-
29-
if (!isValid) {
30-
return res.status(400).end('Invalid payload')
31-
}
32-
33-
if (!isRelatedToPullRequest(req.body.ref)) {
34-
return res.status(400).end('Will only push builds related to pull requests')
35-
}
36-
37-
if (!enabledRepos.includes(repo)) {
38-
return res.status(400).end('Invalid repository')
39-
}
40-
41-
if (!isJenkinsIpWhitelisted(req)) {
42-
return res.status(401).end('Invalid Jenkins IP')
8+
pushJenkinsUpdate.pushStarted({
9+
owner,
10+
repo,
11+
logger: event.logger
12+
}, event, (err) => {
13+
if (err) {
14+
event.logger.error(err, 'Error while handling Jenkins start event')
4315
}
44-
45-
pushJenkinsUpdate.pushStarted({
46-
owner: 'nodejs',
47-
repo,
48-
logger: req.log
49-
}, req.body, (err) => {
50-
const statusCode = err !== null ? 500 : 201
51-
res.status(statusCode).end()
52-
})
5316
})
17+
}
5418

55-
app.post('/:repo/jenkins/end', (req, res) => {
56-
const isValid = pushJenkinsUpdate.validate(req.body)
57-
const repo = req.params.repo
58-
59-
if (!isValid) {
60-
return res.status(400).end('Invalid payload')
61-
}
62-
63-
if (!isRelatedToPullRequest(req.body.ref)) {
64-
return res.status(400).end('Will only push builds related to pull requests')
65-
}
66-
67-
if (!enabledRepos.includes(repo)) {
68-
return res.status(400).end('Invalid repository')
69-
}
19+
function handleJenkinsStop (event) {
20+
const { repo, owner } = event
7021

71-
if (!isJenkinsIpWhitelisted(req)) {
72-
return res.status(401).end('Invalid Jenkins IP')
22+
pushJenkinsUpdate.pushEnded({
23+
owner,
24+
repo,
25+
logger: event.logger
26+
}, event, (err) => {
27+
if (err) {
28+
event.logger.error(err, 'Error while handling Jenkins end event')
7329
}
74-
75-
pushJenkinsUpdate.pushEnded({
76-
owner: 'nodejs',
77-
repo,
78-
logger: req.log
79-
}, req.body, (err) => {
80-
const statusCode = err !== null ? 500 : 201
81-
res.status(statusCode).end()
82-
})
8330
})
8431
}
32+
33+
module.exports = function (_, event) {
34+
event.on('jenkins.start', handleJenkinsStart)
35+
event.on('jenkins.end', handleJenkinsStop)
36+
}

test/integration/push-jenkins-update.test.js

Lines changed: 42 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -14,74 +14,89 @@ require('../../scripts/jenkins-status')(app, events)
1414
tap.test('Sends POST requests to https://api.github.com/repos/nodejs/node/statuses/<SHA>', (t) => {
1515
const jenkinsPayload = readFixture('success-payload.json')
1616

17-
const prCommitsScope = setupGetCommitsMock('node')
18-
const scope = nock('https://api.github.com')
17+
setupGetCommitsMock('node')
18+
.on('replied', (req, interceptor) => {
19+
t.doesNotThrow(() => interceptor.scope.done())
20+
})
21+
nock('https://api.github.com')
1922
.filteringPath(ignoreQueryParams)
2023
.post('/repos/nodejs/node/statuses/8a5fec2a6bade91e544a30314d7cf21f8a200de1')
2124
.reply(201)
25+
.on('replied', (req, interceptor) => {
26+
t.doesNotThrow(() => interceptor.scope.done())
27+
})
2228

23-
t.plan(1)
29+
t.plan(3)
2430

2531
supertest(app)
2632
.post('/node/jenkins/start')
2733
.send(jenkinsPayload)
28-
.expect(201)
34+
.expect(200)
2935
.end((err, res) => {
30-
prCommitsScope.done()
31-
scope.done()
3236
t.equal(err, null)
3337
})
3438
})
3539

3640
tap.test('Allows repository name to be provided with URL parameter when pushing job started', (t) => {
3741
const jenkinsPayload = readFixture('pending-payload.json')
3842

39-
const prCommitsScope = setupGetCommitsMock('citgm')
40-
const scope = nock('https://api.github.com')
43+
setupGetCommitsMock('citgm')
44+
.on('replied', (req, interceptor) => {
45+
t.doesNotThrow(() => interceptor.scope.done())
46+
})
47+
nock('https://api.github.com')
4148
.filteringPath(ignoreQueryParams)
4249
.post('/repos/nodejs/citgm/statuses/8a5fec2a6bade91e544a30314d7cf21f8a200de1')
4350
.reply(201)
51+
.on('replied', (req, interceptor) => {
52+
t.doesNotThrow(() => interceptor.scope.done())
53+
})
4454

45-
t.plan(1)
55+
t.plan(3)
4656

4757
supertest(app)
4858
.post('/citgm/jenkins/start')
4959
.send(jenkinsPayload)
50-
.expect(201)
60+
.expect(200)
5161
.end((err, res) => {
52-
prCommitsScope.done()
53-
scope.done()
5462
t.equal(err, null)
5563
})
5664
})
5765

5866
tap.test('Allows repository name to be provided with URL parameter when pushing job ended', (t) => {
5967
const jenkinsPayload = readFixture('success-payload.json')
6068

61-
const prCommitsScope = setupGetCommitsMock('citgm')
62-
const scope = nock('https://api.github.com')
69+
setupGetCommitsMock('citgm')
70+
.on('replied', (req, interceptor) => {
71+
t.doesNotThrow(() => interceptor.scope.done())
72+
})
73+
nock('https://api.github.com')
6374
.filteringPath(ignoreQueryParams)
6475
.post('/repos/nodejs/citgm/statuses/8a5fec2a6bade91e544a30314d7cf21f8a200de1')
6576
.reply(201)
77+
.on('replied', (req, interceptor) => {
78+
t.doesNotThrow(() => interceptor.scope.done())
79+
})
6680

67-
t.plan(1)
81+
t.plan(3)
6882

6983
supertest(app)
7084
.post('/citgm/jenkins/end')
7185
.send(jenkinsPayload)
72-
.expect(201)
86+
.expect(200)
7387
.end((err, res) => {
74-
prCommitsScope.done()
75-
scope.done()
7688
t.equal(err, null)
7789
})
7890
})
7991

8092
tap.test('Forwards payload provided in incoming POST to GitHub status API', (t) => {
8193
const fixture = readFixture('success-payload.json')
8294

83-
const prCommitsScope = setupGetCommitsMock('node')
84-
const scope = nock('https://api.github.com')
95+
setupGetCommitsMock('node')
96+
.on('replied', (req, interceptor) => {
97+
t.doesNotThrow(() => interceptor.scope.done())
98+
})
99+
nock('https://api.github.com')
85100
.filteringPath(ignoreQueryParams)
86101
.post('/repos/nodejs/node/statuses/8a5fec2a6bade91e544a30314d7cf21f8a200de1', {
87102
state: 'success',
@@ -90,16 +105,17 @@ tap.test('Forwards payload provided in incoming POST to GitHub status API', (t)
90105
target_url: 'https://ci.nodejs.org/job/node-test-commit-osx/3157/'
91106
})
92107
.reply(201)
108+
.on('replied', (req, interceptor) => {
109+
t.doesNotThrow(() => interceptor.scope.done())
110+
})
93111

94-
t.plan(1)
112+
t.plan(3)
95113

96114
supertest(app)
97115
.post('/node/jenkins/start')
98116
.send(fixture)
99-
.expect(201)
117+
.expect(200)
100118
.end((err, res) => {
101-
prCommitsScope.done()
102-
scope.done()
103119
t.equal(err, null)
104120
})
105121
})
@@ -123,7 +139,7 @@ tap.test('Posts a CI comment in the related PR when Jenkins build is named node-
123139
supertest(app)
124140
.post('/node/jenkins/start')
125141
.send(fixture)
126-
.expect(201)
142+
.expect(200)
127143
.end((err, res) => {
128144
commentScope.done()
129145
t.equal(err, null)
@@ -151,7 +167,7 @@ tap.test('Posts a CI comment in the related PR when Jenkins build is named node-
151167
supertest(app)
152168
.post('/node/jenkins/start')
153169
.send(fixture)
154-
.expect(201)
170+
.expect(200)
155171
.end((err, res) => {
156172
commentScope.done()
157173
t.equal(err, null)

0 commit comments

Comments
 (0)