src/executors/ExecutorJenkins.js
import { default as JenkinsLib } from 'jenkins';
import { Executor } from '../agent/Executor';
import { AgentUtils } from '../agent/AgentUtils';
import KmsUtils from '../KmsUtils';
/**
* Sample .githubTaskManager.json task config/
*
* see: https://github.com/zotoio/github-task-manager/wiki/Structure-of-.githubTaskManager.json
*
* {
"executor": "Jenkins",
"context": "Build and deploy",
"options": {
"jobName": "JENKINS_JOBNAME",
"parameters": {
"ENVIRONMENT" : "env_dev"
}
}
*/
export class ExecutorJenkins extends Executor {
constructor(eventData, log) {
super(eventData, log);
this.log = log;
KmsUtils.logger = log;
this.options = this.getOptions();
}
async initJenkins() {
if (!this.jenkins) {
// If set, this will return bool:true, else bool:false
let useCsrf = this.options.GTM_JENKINS_CSRF === 'true';
this.jenkins = JenkinsLib({
baseUrl: AgentUtils.formatBasicAuth(
this.options.GTM_JENKINS_USER,
await KmsUtils.getDecrypted(this.options.GTM_CRYPT_JENKINS_TOKEN),
this.options.GTM_JENKINS_URL
),
crumbIssuer: useCsrf,
promisify: true
});
}
return this.jenkins;
}
async waitForBuildToExist(buildName, buildNumber) {
let log = this.log;
return new Promise(async (resolve, reject) => {
let exists = false;
let maxRetries = 600;
let tries = 0;
while (!exists && tries++ < maxRetries) {
exists = await this.jenkins.build.get(buildName, buildNumber).then(
function() {
log.info(`Build ${buildName} #${buildNumber} Started!`);
return true;
},
async function() {
log.debug(`Build ${buildName} #${buildNumber} Hasn't Started: ${tries}`);
await AgentUtils.timeout(10000);
return false;
}
);
}
exists ? resolve(true) : reject();
});
}
async waitForBuild(buildName, buildNumber) {
let log = this.log;
let buildDict = await this.jenkins.build.get(buildName, buildNumber).then(function(data) {
return data;
});
let tries = 1;
while (buildDict.result === null) {
await AgentUtils.timeout(5000);
buildDict = await this.jenkins.build.get(buildName, buildNumber).then(function(data) {
log.debug(`Waiting for Build '${buildName}' to Finish: ${tries++}`);
return data;
});
}
log.info(`Build Finished: ${buildName} #${buildNumber} - ${buildDict.result}`);
return buildDict;
}
async buildNumberfromQueue(queueId) {
let log = this.log;
let queueData = await this.jenkins.queue.item(queueId).then(function(data) {
return data;
});
while (!queueData.executable) {
try {
log.info(`Build Not Ready: ${queueData.why}`);
} catch (error) {
log.warn(`Build Not Ready: No Reason Provided. Retrying in 3 seconds...`);
}
await AgentUtils.timeout(3000);
queueData = await this.jenkins.queue.item(queueId).then(function(data) {
return data;
});
}
return queueData.executable.number;
}
async executeTask(task) {
this.jenkins = await this.initJenkins();
let log = this.log;
let jobName = task.options.jobName || null;
let buildParams = task.options.parameters || null;
let configuration = {};
if (jobName == null) {
await AgentUtils.timeout(4000);
task.results = {
passed: false,
url: this.options.GTM_JENKINS_URL,
message: `Required Parameter 'Job Name' not Specified`
};
return Promise.reject(task);
} else {
configuration.name = jobName;
}
if (buildParams != null && buildParams != []) configuration.parameters = buildParams;
log.info('Starting Jenkins Job: ' + jobName);
// TODO: Check if Job Exists
let queueNumber = await this.jenkins.job.build(configuration);
let buildNumber = await this.buildNumberfromQueue(queueNumber);
let buildExists = await this.waitForBuildToExist(jobName, buildNumber);
console.debug(buildExists);
let result = await this.waitForBuild(jobName, buildNumber);
let resultBool = result.result === 'SUCCESS';
task.results = {
passed: resultBool,
url: result.url,
message: `${jobName} #${buildNumber} - ${result.result}`,
details: `Job '${jobName} #${buildNumber}' Finished: ${result.result}`,
meta: {
jobName: jobName,
buildNumber: buildNumber
}
};
return resultBool ? Promise.resolve(task) : Promise.reject(task);
}
}
Executor.register('Jenkins', ExecutorJenkins);