diff --git a/plugins/queue/scheduler.js b/plugins/queue/scheduler.js index 6d322fa..c452903 100644 --- a/plugins/queue/scheduler.js +++ b/plugins/queue/scheduler.js @@ -20,6 +20,16 @@ const TEMPORAL_TOKEN_TIMEOUT = 12 * 60; // 12 hours in minutes const TEMPORAL_UNZIP_TOKEN_TIMEOUT = 2 * 60; // 2 hours in minutes const BLOCKED_BY_SAME_JOB_WAIT_TIME = 5; +/** + * Checks whether the job associated with the build is virtual or not + * @method isVirtualJob + * @param {Object} annotations Job Annotations + * @return {Boolean} + */ +function isVirtualJob(annotations) { + return annotations && annotations['screwdriver.cd/virtualJob']; +} + /** * Posts a new build event to the API * @method postBuildEvent @@ -300,7 +310,8 @@ async function start(executor, config) { apiUri, pipeline, isPR, - prParentJobId + prParentJobId, + annotations } = config; const forceStart = /\[(force start)\]/.test(causeMessage); @@ -411,48 +422,75 @@ async function start(executor, config) { throw value.error; } - const token = executor.tokenGen(Object.assign(tokenConfig, { scope: ['temporal'] }), TEMPORAL_TOKEN_TIMEOUT); - - // set the start time in the queue - Object.assign(config, { token }); - // Store the config in redis - await executor.redisBreaker.runCommand('hset', executor.buildConfigTable, buildId, JSON.stringify(config)); - - const blockedBySameJob = reach(config, 'annotations>screwdriver.cd/blockedBySameJob', { - separator: '>', - default: true - }); - const blockedBySameJobWaitTime = reach(config, 'annotations>screwdriver.cd/blockedBySameJobWaitTime', { - separator: '>', - default: BLOCKED_BY_SAME_JOB_WAIT_TIME - }); + if (isVirtualJob(annotations)) { + // Bypass execution of the build if the job is virtual + const payload = { + status: 'SUCCESS', + statusMessage: 'Skipping execution of the virtual job' + }; - // Note: arguments to enqueue are [queue name, job name, array of args] - enq = await executor.queueBreaker.runCommand('enqueue', executor.buildQueue, 'start', [ - { - buildId, - jobId, - blockedBy: blockedBy.toString(), - blockedBySameJob, - blockedBySameJobWaitTime - } - ]); - if (buildStats) { await helper .updateBuild( { buildId, token: buildToken, apiUri, - payload: { stats: build.stats, status: 'QUEUED' } + payload }, helper.requestRetryStrategy ) .catch(err => { - logger.error(`Failed to update build status for build ${buildId}: ${err}`); + logger.error(`virtualBuilds: failed to update build status for build ${buildId}: ${err}`); throw err; }); + } else { + const token = executor.tokenGen( + Object.assign(tokenConfig, { scope: ['temporal'] }), + TEMPORAL_TOKEN_TIMEOUT + ); + + // set the start time in the queue + Object.assign(config, { token }); + // Store the config in redis + await executor.redisBreaker.runCommand('hset', executor.buildConfigTable, buildId, JSON.stringify(config)); + + const blockedBySameJob = reach(config, 'annotations>screwdriver.cd/blockedBySameJob', { + separator: '>', + default: true + }); + const blockedBySameJobWaitTime = reach(config, 'annotations>screwdriver.cd/blockedBySameJobWaitTime', { + separator: '>', + default: BLOCKED_BY_SAME_JOB_WAIT_TIME + }); + + // Note: arguments to enqueue are [queue name, job name, array of args] + enq = await executor.queueBreaker.runCommand('enqueue', executor.buildQueue, 'start', [ + { + buildId, + jobId, + blockedBy: blockedBy.toString(), + blockedBySameJob, + blockedBySameJobWaitTime + } + ]); + if (buildStats) { + await helper + .updateBuild( + { + buildId, + token: buildToken, + apiUri, + payload: { stats: build.stats, status: 'QUEUED' } + }, + helper.requestRetryStrategy + ) + .catch(err => { + logger.error(`Failed to update build status for build ${buildId}: ${err}`); + + throw err; + }); + } } } diff --git a/test/plugins/queue/scheduler.test.js b/test/plugins/queue/scheduler.test.js index fd5e8b3..50abc46 100644 --- a/test/plugins/queue/scheduler.test.js +++ b/test/plugins/queue/scheduler.test.js @@ -774,6 +774,36 @@ describe('scheduler test', () => { assert.calledWith(queueMock.enqueue, 'builds', 'start', [partialTestDefaultConfig]); }); }); + + it('skip execution of the build and update the status to SUCCESS for virtual job', () => { + const dateNow = Date.now(); + const sandbox = sinon.createSandbox({ + useFakeTimers: false + }); + + sandbox.useFakeTimers(dateNow); + buildMock.stats = {}; + testConfig.build = buildMock; + testConfig.annotations['screwdriver.cd/virtualJob'] = true; + + return scheduler.start(executor, testConfig).then(() => { + assert.calledTwice(queueMock.connect); + assert.notCalled(redisMock.hset); + assert.notCalled(queueMock.enqueue); + assert.calledOnce(executor.tokenGen); + assert.calledWith( + helperMock.updateBuild, + { + buildId, + token: 'buildToken', + apiUri: 'http://api.com', + payload: { status: 'SUCCESS', statusMessage: 'Skipping execution of the virtual job' } + }, + helperMock.requestRetryStrategy + ); + sandbox.restore(); + }); + }); }); describe('startFrozen', () => {