tasks/jenkinsServer.js
var q = require('q'),
_ = require('underscore'),
request = require('request');
function JenkinsServer(serverUrl, defaultOptions, fileSystem, grunt, jobUrlResolutionStrategy) {
function verboseLogRequest(options) {
var method = options.method || 'GET';
grunt.verbose.writeln([method, options.url].join(' '));
}
function verboseLogResponse(r) {
grunt.verbose.writeln(['response code', r.statusCode].join(' '));
}
function authenticationFailed(r) {
// NOTE: Jenkins returns a webpage that says 'status 401'
// but it is actually returning a status code of 403
var failed = r.statusCode === 403 || r.statusCode === 401;
if (failed) {
grunt.log.error('Failed to authenticate.');
}
return failed;
}
function hasError(e, r) {
var isNotSuccessful = r.statusCode !== 200;
if (e) {
grunt.log.error(e);
}
if (isNotSuccessful) {
grunt.log.error([r.statusCode, 'from', r.request.method, r.request.uri.href].join(' '));
}
return !_.isNull(e) || isNotSuccessful;
}
// NOTE: unauthenticated endpoint
this.fetchJobs = function() {
var deferred = q.defer(),
options = _.extend(defaultOptions, {
url: [serverUrl, 'api', 'json'].join('/')
});
request(options, function(e, r, body) {
verboseLogRequest(options);
verboseLogResponse(r);
if (hasError(e, r)) {
return deferred.reject(e);
}
var jobs = JSON.parse(body).jobs;
grunt.log.writeln(['Found', jobs.length, 'jobs.'].join(' '));
deferred.resolve(_.map(jobs, function(j) {
var url;
if (jobUrlResolutionStrategy === 'PROVIDED') {
url = j.url;
} else if (jobUrlResolutionStrategy === 'REWRITE_WITH_SERVER_ADDRESS') {
var path = j.url.replace(/^https?\:\/{2}[^\/]+\/(.*)/, '$1');
url = [serverUrl, path].join('/');
} else {
grunt.log.error(jobUrlResolutionStrategy + ' is not a valid value: [PROVIDED, REWRITE_WITH_SERVER_ADDRESS]');
}
return {
name: j.name,
url: url
};
}));
});
return deferred.promise;
};
this.createOrUpdateJobs = function(directories) {
var deferred = q.defer();
function resolve(val) {
deferred.resolve(val);
}
directories.forEach(function(folder) {
fetchJobConfigurationStrategy(folder).
then(applyStrategy).
then(resolve);
});
return deferred.promise;
};
this.installPlugins = function(plugins) {
var deferred = q.defer(),
options = _.extend(defaultOptions, {
url: [serverUrl, 'pluginManager', 'installNecessaryPlugins'].join('/'),
method: 'POST',
body: plugins.xml
});
request(options, function(e, r, b) {
verboseLogRequest(options);
verboseLogResponse(r);
if (hasError(e, r) || authenticationFailed(r)) {
return deferred.reject(e);
}
_.each(plugins.plugins, function(p) {
grunt.log.ok('install: ' + p.id + ' @ ' + p.version);
});
deferred.resolve(r.statusCode === 200);
});
return deferred.promise;
};
// NOTE: unauthenticated endpoint
this.fetchEnabledPlugins = function() {
var deferred = q.defer(),
options = _.extend(defaultOptions, {
url: [serverUrl, 'pluginManager', 'api', 'json?depth=1'].join('/')
});
request(options, function(e, r, body) {
verboseLogRequest(options);
verboseLogResponse(r);
if (hasError(e, r)) {
return deferred.reject(e);
}
var result = _.filter(JSON.parse(body).plugins, function(p) {
return p.enabled;
});
var plugins = _.map(result, function(p) {
return {
id: p.shortName,
version: p.version
};
});
deferred.resolve(plugins);
});
return deferred.promise;
};
this.fetchJobConfigurations = function(jobs) {
var deferred = q.defer();
var promises = _.map(jobs, function(j) {
var d = q.defer(),
options = _.extend(defaultOptions, {
url: [j.url, 'config.xml'].join('')
});
request(options, function(e, r, body) {
verboseLogRequest(options);
verboseLogResponse(r);
if (hasError(e, r) || authenticationFailed(r)) {
return d.reject(e);
}
j.config = body;
d.resolve(j);
});
return d.promise;
});
q.allSettled(promises).
then(function(promises) {
if (_.all(promises, function(p) {
return p.state === 'fulfilled';
})) {
deferred.resolve(_.map(promises, function(p) {
return p.value;
}));
} else {
deferred.reject();
}
});
return deferred.promise;
};
function createJob(config) {
var deferred = q.defer(),
options = _.extend(defaultOptions, {
url: [serverUrl, 'createItem'].join('/'),
method: 'POST',
qs: {
name: config.jobName
},
headers: {
'Content-Type': 'text/xml'
},
body: config.fileContents
});
request(options, function(e, r, b) {
verboseLogRequest(options);
verboseLogResponse(r);
if (hasError(e, r) || authenticationFailed(r)) {
return deferred.reject(e);
}
deferred.resolve(r.statusCode === 200);
});
return deferred.promise;
}
function updateJob(config) {
var deferred = q.defer(),
options = _.extend(defaultOptions, {
url: [serverUrl, 'job', config.jobName, 'config.xml'].join('/'),
method: 'POST',
body: config.fileContents
});
request(options, function(e, r, b) {
verboseLogRequest(options);
verboseLogResponse(r);
if (hasError(e, r) || authenticationFailed(r)) {
return deferred.reject(e);
}
deferred.resolve(r.statusCode === 200);
});
return deferred.promise;
}
// NOTE: unauthenticated endpoint
function fetchJobConfigurationStrategy(job) {
var deferred = q.defer(),
options = _.extend(defaultOptions, {
url: [serverUrl, 'job', job, 'config.xml'].join('/')
});
request(options, function(e, r, b) {
verboseLogRequest(options);
verboseLogResponse(r);
if (hasError(e, r)) {
return deferred.reject(e);
}
var strategy = r.statusCode === 200 ? 'update' : 'create';
grunt.log.ok(strategy + ': ' + job);
deferred.resolve({
strategy: strategy,
jobName: job
});
});
return deferred.promise;
}
function applyStrategy(strategyObj) {
var deferred = q.defer(),
filename = [fileSystem.pipelineDirectory, strategyObj.jobName, 'config.xml'].join('/'),
fileStrategy = {
fileName: filename,
jobName: strategyObj.jobName
};
function resolve(val) {
deferred.resolve(val);
}
if (strategyObj.strategy === 'create') {
fileSystem.readFile(fileStrategy).
then(createJob).
then(resolve);
} else if (strategyObj.strategy === 'update') {
fileSystem.readFile(fileStrategy).
then(updateJob).
then(resolve);
}
return deferred.promise;
}
}
module.exports = JenkinsServer;