Skip to content

Commit

Permalink
fix(1159): Configure build timeout in Jenkins (#22)
Browse files Browse the repository at this point in the history
  • Loading branch information
Atsuto Yamashita authored and tkyi committed Jul 27, 2018
1 parent 968429a commit 6b1a397
Show file tree
Hide file tree
Showing 5 changed files with 87 additions and 6 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ The class provides a couple options that are configurable in the instantiation o
| config.jenkins.host | String | | The hostname for the Jenkins cluster |
| config.jenkins.port | Number | 8080 | The port number for the Jenkins cluster |
| config.jenkins.nodeLabel | String | 'screwdriver' | Node labels of Jenkins slaves |
| config.jenkins.buildTimeout | Number | 90 | Number of minutes to allow a build to run before considering it is timed out |
| config.jenkins.maxBuildTimeout | Number | 120 | Max timeout user can configure up to |
| config.docker.composeCommand | String | 'docker'-compose | The path to the docker-compose command |
| config.docker.launchVersion | String | 'stable' | Launcher container version to use |
| config.docker.prefix | String | '' | Prefix to container names |
Expand Down
5 changes: 5 additions & 0 deletions config/job.xml.tim
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@
<description/>
<defaultValue/>
</hudson.model.TextParameterDefinition>
<hudson.model.TextParameterDefinition>
<name>SD_BUILD_TIMEOUT</name>
<description/>
<defaultValue/>
</hudson.model.TextParameterDefinition>
</parameterDefinitions>
</hudson.model.ParametersDefinitionProperty>
</properties>
Expand Down
15 changes: 14 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,13 @@ const jenkins = require('jenkins');
const xmlescape = require('xml-escape');
const tinytim = require('tinytim');
const Breaker = require('circuit-fuses');
const hoek = require('hoek');

const password = Symbol('password');
const baseUrl = Symbol('baseUrl');

const ANNOTATE_BUILD_TIMEOUT = 'beta.screwdriver.cd/timeout';

class JenkinsExecutor extends Executor {
/**
* JenkinsClient command to run
Expand Down Expand Up @@ -211,6 +214,8 @@ class JenkinsExecutor extends Executor {
* @param {String} [options.jenkins.username='screwdriver'] Jenkins username
* @param {String} options.jenkins.password Jenkins password/token
* @param {String} [options.jenkins.nodeLabel='screwdriver'] Node labels of Jenkins slaves
* @param {Number} [options.jenkins.buildTimeout=90] Number of minutes to allow a build to run before considering it is
* @param {Number} [options.jenkins.maxBuildTimeout=120] Max timeout user can configure up to
* @param {String} [options.docker.composeCommand='docker-compose'] THe path to the docker-compose command
* @param {String} [options.docker.launchVersion='stable'] Launcher container version to use
* @param {String} [options.docker.prefix=''] Prefix to all container names
Expand All @@ -230,6 +235,8 @@ class JenkinsExecutor extends Executor {
this.username = options.jenkins.username || 'screwdriver';
this[password] = options.jenkins.password;
this.nodeLabel = options.jenkins.nodeLabel || 'screwdriver';
this.buildTimeout = options.jenkins.buildTimeout || 90;
this.maxBuildTimeout = options.jenkins.maxBuildTimeout || 120;
this.composeCommand = (options.docker && options.docker.composeCommand) || 'docker-compose';
this.launchVersion = (options.docker && options.docker.launchVersion) || 'stable';
this.prefix = (options.docker && options.docker.prefix) || '';
Expand Down Expand Up @@ -264,6 +271,11 @@ class JenkinsExecutor extends Executor {
async _start(config) {
const jobName = this._jobName(config.buildId);
const xml = this._loadJobXml(config);
const annotations = hoek.reach(config, 'annotations', { default: {} });

const buildTimeout = annotations[ANNOTATE_BUILD_TIMEOUT]
? Math.min(annotations[ANNOTATE_BUILD_TIMEOUT], this.maxBuildTimeout)
: this.buildTimeout;

await this._jenkinsJobCreateOrUpdate(jobName, xml);

Expand All @@ -281,7 +293,8 @@ class JenkinsExecutor extends Executor {
SD_TOKEN: config.token,
SD_CONTAINER: config.container,
SD_API: this.ecosystem.api,
SD_STORE: this.ecosystem.store
SD_STORE: this.ecosystem.store,
SD_BUILD_TIMEOUT: buildTimeout
}
}]
});
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
},
"dependencies": {
"circuit-fuses": "^2.2.1",
"hoek": "^5.0.3",
"jenkins": "^0.20.0",
"request": "^2.72.0",
"screwdriver-executor-base": "^6.1.0",
Expand Down
70 changes: 65 additions & 5 deletions test/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ const TEST_JOB_XML = `
</project>
`;

const CONFIGURED_BUILD_TIMEOUT = 45;
const DEFAULT_BUILD_TIMEOUT = 90;
const MAX_BUILD_TIMEOUT = 120;
const TEST_COMPOSE_YAML = `
version: '2'
services:
Expand Down Expand Up @@ -74,7 +77,8 @@ describe('index', () => {
buildId: 1993,
container: 'node:4',
apiUri: 'http://localhost:8080',
token: 'abcdefg'
token: 'abcdefg',
annotations: {}
};

const jobName = `SD-${config.buildId}`;
Expand All @@ -87,12 +91,23 @@ describe('index', () => {
store: 'store'
};

const buildParameters = {
const buildParams = {
SD_BUILD_ID: String(config.buildId),
SD_TOKEN: 'someBuildToken',
SD_CONTAINER: config.container,
SD_API: ecosystem.api,
SD_STORE: ecosystem.store
SD_STORE: ecosystem.store,
SD_BUILD_TIMEOUT: DEFAULT_BUILD_TIMEOUT
};

const maxTimeoutBuildParams = {
...buildParams,
SD_BUILD_TIMEOUT: MAX_BUILD_TIMEOUT
};

const configuredTimeoutBuildParams = {
...buildParams,
SD_BUILD_TIMEOUT: CONFIGURED_BUILD_TIMEOUT
};

const fakeJobInfo = {
Expand Down Expand Up @@ -180,6 +195,8 @@ describe('index', () => {
let configOpts;
let existsOpts;
let buildOpts;
let configuredBuildTimeoutOpts;
let maxBuildTimeoutOpts;
let exchangeTokenStub;
const fakeXml = 'fake_xml';

Expand Down Expand Up @@ -207,13 +224,32 @@ describe('index', () => {
action: 'build',
params: [{
name: jobName,
parameters: buildParameters
parameters: buildParams
}]
};

configuredBuildTimeoutOpts = {
module: 'job',
action: 'build',
params: [{
name: jobName,
parameters: configuredTimeoutBuildParams
}]
};

maxBuildTimeoutOpts = {
module: 'job',
action: 'build',
params: [{
name: jobName,
parameters: maxTimeoutBuildParams
}]
};

sinon.stub(executor, '_loadJobXml').returns(fakeXml);
exchangeTokenStub = sinon.stub(executor, 'exchangeTokenForBuild');
exchangeTokenStub.resolves('someBuildToken');
config.annotations = {};
});

it('return null when the job is successfully created', (done) => {
Expand All @@ -238,6 +274,30 @@ describe('index', () => {
});
});

it('sets the build timeout if configured by user', (done) => {
breakerMock.runCommand.withArgs(existsOpts).resolves(false);

config.annotations = { 'beta.screwdriver.cd/timeout': CONFIGURED_BUILD_TIMEOUT };
executor.start(config).then(() => {
assert.calledWith(breakerMock.runCommand, existsOpts);
assert.calledWith(breakerMock.runCommand, createOpts);
assert.calledWith(breakerMock.runCommand, configuredBuildTimeoutOpts);
done();
});
});

it('sets the timeout to maxBuildTimeout if user specified a higher timeout', (done) => {
breakerMock.runCommand.withArgs(existsOpts).resolves(false);

config.annotations = { 'beta.screwdriver.cd/timeout': 220 };
executor.start(config).then(() => {
assert.calledWith(breakerMock.runCommand, existsOpts);
assert.calledWith(breakerMock.runCommand, createOpts);
assert.calledWith(breakerMock.runCommand, maxBuildTimeoutOpts);
done();
});
});

it('return error when job.create is getting error', (done) => {
const error = new Error('job.create error');

Expand Down Expand Up @@ -609,7 +669,7 @@ rm -f docker-compose.yml
executor.start(config).then(() => {
assert.calledWith(jenkinsMock.job.create, { name: jobName, xml: fakeXml });
assert.calledWith(jenkinsMock.job.build,
{ name: jobName, parameters: buildParameters });
{ name: jobName, parameters: buildParams });
done();
});
});
Expand Down

0 comments on commit 6b1a397

Please sign in to comment.