- 
          
 - 
                Notifications
    
You must be signed in to change notification settings  - Fork 123
 
chore(jenkins): refactor jenkins-status using events #271
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
          
     Merged
      
      
    
  
     Merged
                    Changes from all commits
      Commits
    
    
  File filter
Filter by extension
Conversations
          Failed to load comments.   
        
        
          
      Loading
        
  Jump to
        
          Jump to file
        
      
      
          Failed to load files.   
        
        
          
      Loading
        
  Diff view
Diff view
There are no files selected for viewing
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
              
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
              | Original file line number | Diff line number | Diff line change | 
|---|---|---|
| @@ -0,0 +1,79 @@ | ||
| 'use strict' | ||
| 
     | 
||
| const pushJenkinsUpdate = require('../lib/push-jenkins-update') | ||
| 
     | 
||
| const debug = require('debug')('jenkins-events') | ||
| const enabledRepos = ['citgm', 'http-parser', 'node', 'node-auto-test'] | ||
| 
     | 
||
| const listOfKnownJenkinsIps = process.env.JENKINS_WORKER_IPS ? process.env.JENKINS_WORKER_IPS.split(',') : [] | ||
| 
     | 
||
| function isKnownJenkinsIp (req) { | ||
| const ip = req.connection.remoteAddress.split(':').pop() | ||
| 
     | 
||
| if (listOfKnownJenkinsIps.length && !listOfKnownJenkinsIps.includes(ip)) { | ||
| req.log.warn({ ip }, 'Ignoring, not allowed to push Jenkins updates') | ||
| return false | ||
| } | ||
| 
     | 
||
| return true | ||
| } | ||
| 
     | 
||
| function isRelatedToPullRequest (gitRef) { | ||
| // refs/pull/12345/head vs refs/heads/v8.x-staging/head | ||
| return gitRef.includes('/pull/') | ||
| } | ||
| 
     | 
||
| module.exports = (app, events) => { | ||
| app.post('/:repo/jenkins/:event', async (req, res) => { | ||
| const isValid = pushJenkinsUpdate.validate(req.body) | ||
| const repo = req.params.repo | ||
| const event = req.params.event | ||
| const owner = req.body.owner || process.env.JENKINS_DEFAULT_GH_OWNER || 'nodejs' | ||
| 
     | 
||
| if (!isValid) { | ||
| return res.status(400).end('Invalid payload') | ||
| } | ||
| 
     | 
||
| if (!isRelatedToPullRequest(req.body.ref)) { | ||
| return res.status(400).end('Will only push builds related to pull requests') | ||
| } | ||
| 
     | 
||
| if (!enabledRepos.includes(repo)) { | ||
| return res.status(400).end('Invalid repository') | ||
| } | ||
| 
     | 
||
| if (!isKnownJenkinsIp(req)) { | ||
| return res.status(401).end('Invalid Jenkins IP') | ||
| } | ||
| 
     | 
||
| const data = { | ||
| ...req.body, | ||
| owner, | ||
| repo, | ||
| event | ||
| } | ||
| 
     | 
||
| try { | ||
| await app.emitJenkinsEvent(event, data, req.log) | ||
| res.status(200) | ||
| } catch (err) { | ||
| req.log.error(err, 'Error while emitting Jenkins event') | ||
| res.status(500) | ||
| } | ||
| 
     | 
||
| res.end() | ||
| }) | ||
| 
     | 
||
| app.emitJenkinsEvent = function emitJenkinsEvent (event, data, logger) { | ||
| const { identifier } = data | ||
| 
     | 
||
| // create unique logger which is easily traceable throughout the entire app | ||
| // by having e.g. "nodejs/nodejs.org/#1337" part of every subsequent log statement | ||
| data.logger = logger.child({ identifier, event }, true) | ||
| 
     | 
||
| data.logger.info('Emitting Jenkins event') | ||
| debug(data) | ||
| 
     | 
||
| return events.emit(`jenkins.${event}`, data) | ||
| } | ||
| } | 
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
              | Original file line number | Diff line number | Diff line change | 
|---|---|---|
| @@ -1,84 +1,36 @@ | ||
| 'use strict' | ||
| 
     | 
||
| const pushJenkinsUpdate = require('../lib/push-jenkins-update') | ||
| const enabledRepos = ['citgm', 'http-parser', 'node'] | ||
| 
     | 
||
| const jenkinsIpWhitelist = process.env.JENKINS_WORKER_IPS ? process.env.JENKINS_WORKER_IPS.split(',') : [] | ||
| function handleJenkinsStart (event) { | ||
| const { repo, owner } = event | ||
| 
     | 
||
| function isJenkinsIpWhitelisted (req) { | ||
| const ip = req.connection.remoteAddress.split(':').pop() | ||
| 
     | 
||
| if (jenkinsIpWhitelist.length && !jenkinsIpWhitelist.includes(ip)) { | ||
| req.log.warn({ ip }, 'Ignoring, not allowed to push Jenkins updates') | ||
| return false | ||
| } | ||
| 
     | 
||
| return true | ||
| } | ||
| 
     | 
||
| function isRelatedToPullRequest (gitRef) { | ||
| // refs/pull/12345/head vs refs/heads/v8.x-staging/head | ||
| return gitRef.includes('/pull/') | ||
| } | ||
| 
     | 
||
| module.exports = function (app) { | ||
| app.post('/:repo/jenkins/start', (req, res) => { | ||
| const isValid = pushJenkinsUpdate.validate(req.body) | ||
| const repo = req.params.repo | ||
| 
     | 
||
| if (!isValid) { | ||
| return res.status(400).end('Invalid payload') | ||
| } | ||
| 
     | 
||
| if (!isRelatedToPullRequest(req.body.ref)) { | ||
| return res.status(400).end('Will only push builds related to pull requests') | ||
| } | ||
| 
     | 
||
| if (!enabledRepos.includes(repo)) { | ||
| return res.status(400).end('Invalid repository') | ||
| } | ||
| 
     | 
||
| if (!isJenkinsIpWhitelisted(req)) { | ||
| return res.status(401).end('Invalid Jenkins IP') | ||
| pushJenkinsUpdate.pushStarted({ | ||
| owner, | ||
| repo, | ||
| logger: event.logger | ||
| }, event, (err) => { | ||
| if (err) { | ||
| event.logger.error(err, 'Error while handling Jenkins start event') | ||
| } | ||
| 
     | 
||
| pushJenkinsUpdate.pushStarted({ | ||
| owner: 'nodejs', | ||
| repo, | ||
| logger: req.log | ||
| }, req.body, (err) => { | ||
| const statusCode = err !== null ? 500 : 201 | ||
| res.status(statusCode).end() | ||
| }) | ||
| }) | ||
| } | ||
| 
     | 
||
| app.post('/:repo/jenkins/end', (req, res) => { | ||
| const isValid = pushJenkinsUpdate.validate(req.body) | ||
| const repo = req.params.repo | ||
| 
     | 
||
| if (!isValid) { | ||
| return res.status(400).end('Invalid payload') | ||
| } | ||
| 
     | 
||
| if (!isRelatedToPullRequest(req.body.ref)) { | ||
| return res.status(400).end('Will only push builds related to pull requests') | ||
| } | ||
| 
     | 
||
| if (!enabledRepos.includes(repo)) { | ||
| return res.status(400).end('Invalid repository') | ||
| } | ||
| function handleJenkinsStop (event) { | ||
| const { repo, owner } = event | ||
| 
     | 
||
| if (!isJenkinsIpWhitelisted(req)) { | ||
| return res.status(401).end('Invalid Jenkins IP') | ||
| pushJenkinsUpdate.pushEnded({ | ||
| owner, | ||
| repo, | ||
| logger: event.logger | ||
| }, event, (err) => { | ||
| if (err) { | ||
| event.logger.error(err, 'Error while handling Jenkins end event') | ||
| } | ||
| 
     | 
||
| pushJenkinsUpdate.pushEnded({ | ||
| owner: 'nodejs', | ||
| repo, | ||
| logger: req.log | ||
| }, req.body, (err) => { | ||
| const statusCode = err !== null ? 500 : 201 | ||
| res.status(statusCode).end() | ||
| }) | ||
| }) | ||
| } | ||
| 
     | 
||
| module.exports = function (_, event) { | ||
| event.on('jenkins.start', handleJenkinsStart) | ||
| event.on('jenkins.end', handleJenkinsStop) | ||
| } | 
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
              
  Add this suggestion to a batch that can be applied as a single commit.
  This suggestion is invalid because no changes were made to the code.
  Suggestions cannot be applied while the pull request is closed.
  Suggestions cannot be applied while viewing a subset of changes.
  Only one suggestion per line can be applied in a batch.
  Add this suggestion to a batch that can be applied as a single commit.
  Applying suggestions on deleted lines is not supported.
  You must change the existing code in this line in order to create a valid suggestion.
  Outdated suggestions cannot be applied.
  This suggestion has been applied or marked resolved.
  Suggestions cannot be applied from pending reviews.
  Suggestions cannot be applied on multi-line comments.
  Suggestions cannot be applied while the pull request is queued to merge.
  Suggestion cannot be applied right now. Please check back later.
  
    
  
    
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interesting alternative approach to verifying scopes that I haven't used before. Any obvious upsides compared to the previous approach?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now that we're using EventEmitter and Promises, the timing is different and as a result the callback below to run before we made a call to the
scopeURL, so we get an error when we callscope.done()There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ahh okey, so that's another approach to fixing what was my biggest headache in #258 basically.
What I ended up doing there, was to make sure all event handlers returned a
Promise. That way the resultingPromisecoming out of theapp.emitJenkinsEvent()-equivalent of #258 would actually resolve after all handlers had completed. In the end we can be sure that all handlers (and therebyscope) has finished when the request gets the 200 response back.No biggie tho, just wanted to understand the pros & reasons behind this new approach introduced.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That works too, but no reason to delay the response until app.emitJenkinsEvent finishes since Jenkins doesn't do anything with 200/500 status codes. We could for testability if we wanted to, but this approach seems fine and more robust?