Skip to content

Instantly share code, notes, and snippets.

@battlecow
Last active January 27, 2016 22:13
Show Gist options
  • Save battlecow/cd0c2233e9f197ec0049 to your computer and use it in GitHub Desktop.
Save battlecow/cd0c2233e9f197ec0049 to your computer and use it in GitHub Desktop.
var constants = require('./constants');
var logging = require('./logging');
var cache = require('memory-cache');
var Q = require('q');
var Client = require('node-rest-client').Client;
var client = new Client();
var artifactory = module.exports = {};
artifactory.verifyWar = function (instance) {
var deferred = Q.defer();
if (instance._branchName.indexOf('/') === -1 && instance._branchName !== 'develop') {
client.get(constants.ARTIFACTORY_API + 'storage/libs-release-local/' + instance._branchName + '/JSS/ROOT.war', function (data, response) {
if (response.statusCode !== 200) {
deferred.reject({
error: {
errorCode: data.errors[0].message,
message: 'ROOT.war not found, please run a Bamboo build and try again'
}
});
} else {
var marshaledData = marshalArtifactData(JSON.parse(data.toString('UTF8')));
deferred.resolve(marshaledData);
}
});
} else if (instance._branchName.indexOf('release/') !== -1) {
var releaseVersion = instance._branchName.split('release/').join('');
client.get(constants.ARTIFACTORY_API + 'storage/libs-release-local/' + releaseVersion + '/JSS/ROOT.war', function (data, response) {
if (response.statusCode !== 200) {
deferred.reject({
error: {
errorCode: data.errors[0].message,
message: 'ROOT.war not found, please run a Bamboo build and try again'
}
});
} else {
var marshaledData = marshalArtifactData(JSON.parse(data.toString('UTF8')));
deferred.resolve(marshaledData);
}
});
} else {
client.get(constants.ARTIFACTORY_API + 'storage/libs-snapshot-local/' + instance._branchName + '/JSS/ROOT.war', function (data, response) {
if (response.statusCode !== 200) {
deferred.reject({
error: {
errorCode: data.errors[0].message,
message: 'ROOT.war not found, please run a Bamboo build and try again'
}
});
} else {
var marshaledData = marshalArtifactData(JSON.parse(data.toString('UTF8')));
deferred.resolve(marshaledData);
}
});
}
return deferred.promise;
};
function marshalArtifactData(rawData) {
delete rawData.repo;
delete rawData.path;
delete rawData.mimeType;
delete rawData.checksums;
delete rawData.originalChecksums;
return rawData;
};
artifactory.getBuildProperties = function (uri) {
var deferred = Q.defer();
client.get(uri + '?properties', function (data, response) {
if (response.statusCode !== 200) {
deferred.reject({
error: {
errorCode: data.errors[0].message,
message: 'Unable to retrieve artifact properties, please try your request again later'
}
});
} else {
var buildProps = JSON.parse(data.toString('UTF8'));
var marshaledProps = {
buildName: buildProps.properties['build.name'][0],
buildNumber: buildProps.properties['build.number'][0]
};
deferred.resolve(marshaledProps);
}
}).on('error', function (err) {
logging.error(err);
deferred.reject({
error: {
errorCode: 500,
message: 'Unable to retrieve build properties from Artifactory'
}
})
});
return deferred.promise;
};
artifactory.getBuildInfo = function (build) {
var deferred = Q.defer();
client.get(constants.ARTIFACTORY_API + 'build/' + encodeURI(build.buildName) + '/' + build.buildNumber, function (data, response) {
if (response.statusCode !== 200) {
deferred.reject({
error: {
errorCode: data.errors[0].message,
message: 'Unable to retrieve build information, please try your request again later'
}
});
} else {
var rawBuildInfo = JSON.parse(data.toString('UTF8'));
var sha;
if (!rawBuildInfo.buildInfo.hasOwnProperty('properties')) {
sha = 'N/A';
} else {
sha = rawBuildInfo.buildInfo.properties['buildInfo.env.planRepository.revision'];
}
var buildInfo = {
sha: sha,
link: rawBuildInfo.buildInfo.url
};
deferred.resolve(buildInfo);
}
}).on('error', function (err) {
logging.error(err);
deferred.reject({
error: {
errorCode: 500,
message: 'Unable to retrieve build information from Artifactory'
}
})
});
return deferred.promise;
};
function getReleases(cacheBust) {
var cacheBust = cacheBust || false;
var deferred = Q.defer();
var releases = cache.get('releases');
if (releases && !cacheBust) {
logging.debug('Getting releases from cache');
deferred.resolve(releases);
} else {
client.get(constants.ARTIFACTORY_API + 'storage/libs-release-local/', function (data, response) {
if (response.statusCode !== 200) {
logging.debug('Failed to retrieve releases Error: ' + data.errors[0].message);
deferred.reject({
error: {
errorCode: data.errors[0].message,
message: 'Unable to retrieve release options from Artifactory'
}
});
} else {
var releases = JSON.parse(data.toString('UTF8')).children;
cache.put('releases', releases);
deferred.resolve(releases);
}
}).on('error', function (err) {
logging.error(err);
deferred.reject({
error: {
errorCode: 500,
message: 'Unable to retrieve release options from Artifactory'
}
})
});
}
return deferred.promise;
}
function filterReleases(releases, name, retried) {
var retried = retried || false;
var releaseVersion = name.split('release/').join('');
var filteredReleases = releases.filter(function (element) {
return (element.uri.indexOf(releaseVersion) !== -1);
});
if (filteredReleases.length === 0 && !retried) {
logging.info(name + ' not found in cache, busting cache and trying again');
getReleases(true).then(function (response) {
filterReleases(response, name, true);
});
}
return filteredReleases.map(function (r) {
return {
branchName: 'release' + r.uri,
version: r.uri.split('/')[1]
}
});
}
artifactory.getReleaseOptions = function (name) {
logging.debug('Retrieving release options for: ' + name);
return getReleases().then(function (response) {
return filterReleases(response, name);
});
};
function queryDockerTags(image) {
var deferred = Q.defer();
client.get(constants.ARTIFACTORY_API + 'docker/docker/v2/' + image + '/tags/list', function (data, response) {
if (response.statusCode !== 200) {
logging.debug('Failed to retrieve Docker tags for image: ' + image + ' Error: ' + data.errors[0].message);
deferred.reject({
error: {
errorCode: data.errors[0].message,
message: 'Docker tags not found for image: ' + image
}
});
} else {
deferred.resolve(data.tags);
}
}).on('error', function (err) {
logging.error(err);
deferred.reject({
error: {
errorCode: 500,
message: 'Unable to retrieve Docker options from Artifactory'
}
})
});
return deferred.promise;
};
artifactory.getDockerTags = function (image) {
logging.debug('Retrieving docker tags for image: ' + image);
return queryDockerTags(image).then(function (response) {
return response.filter(function (element) {
return ((element !== 'master') && (element !== 'latest'));
});
});
};
artifactory.ping = function () {
logging.debug('Pinging artifactory api');
var deferred = Q.defer();
client.get(constants.ARTIFACTORY_API + 'system/version', function (data, response) {
if (response.statusCode !== 200) {
deferred.resolve({
status: response.statusCode,
version: 'Unknown',
errorMessage: 'Unknown problem accessing Artifactory API'
});
} else {
var info = JSON.parse(data.toString('UTF8'));
deferred.resolve({status: response.statusCode, version: info.version});
}
}).on('error', function (err) {
logging.error(err);
deferred.resolve({
status: 500,
version: 'Unknown',
errorMessage: 'Unknown problem accessing Artifactory API'
})
});
return deferred.promise;
};
var Q = require('q');
var fleetCtl = require('./fleetctl');
var logging = require('./logging');
var fleetapi = require('./fleetapi');
var fleet = module.exports = {};
fleet.createInstance = function (host) {
var deferred = Q.defer();
var customUnits;
getRequiredUnits(host).then(function (units) {
customUnits = units.map(function (unit) {
return customReplace(unit, host);
});
var promises = customUnits.map(function (unit) {
logging.debug('creating unit: ' + unit.name);
return fleetCtl.createUnit(unit);
});
return Q.all(promises);
}).then(function (response) {
var units = customUnits.map(function (unit) {
delete unit.options;
unit.systemdActiveState = 'inactive';
return unit;
});
deferred.resolve(units);
}).catch(function (err) {
deferred.reject(err);
});
return deferred.promise;
};
fleet.upgradeInstance = function (host) {
var upgradeUnitName = {name: 'jss-upgrade@.service', _ID: host._ID};
var jssUnitName = 'jss-app-' + host._tomcatVersion + '@' + host._ID + '.service';
logging.info('Creating upgrade unit: ' + upgradeUnitName.name);
return fleet.createCustomUnit(upgradeUnitName).then(function (response) {
logging.debug('Upgrade unit created: ' + response.name);
upgradeUnitName.name = response.name;
logging.debug('Check if upgrade unit is inactive before starting JSS shutdown');
return fleetCtl.retryState('inactive', upgradeUnitName.name);
}).then(function (response) {
logging.debug('Upgrade unit run completed, shutting down the JSS');
return fleetCtl.toggleUnitState(jssUnitName, {desiredState: 'inactive'});
}).then(function (response) {
logging.debug('JSS Shutdown started, ensure it is shutdown before attempting startup');
return fleetCtl.retryState('inactive', jssUnitName);
}).then(function (response) {
logging.debug('JSS shutdown completed, starting up the JSS for upgrade');
return fleetCtl.toggleUnitState(jssUnitName, {desiredState: 'launched'});
}).then(function (response) {
logging.debug('JSS startup started, check to ensure it becomes active');
return fleetCtl.retryState('active', jssUnitName);
}).finally(function (response) {
logging.debug('Remove upgrade unit from Fleet');
return fleetCtl.deleteUnit(upgradeUnitName.name);
});
};
fleet.createCustomUnit = function (customUnit) {
var deferred = Q.defer();
var units = customizedOptionalUnits();
units = units.filter(function (u) {
return (u === customUnit.name);
});
if (units.length !== 1) {
logging.error(customUnit.name + ' unit template does not exist');
deferred.reject({
errors: [
{message: 'Customized unit template does not exist'}
]
});
} else {
getOptionalUnit(units[0]).then(function (unit) {
var customU = customReplace(unit, customUnit);
customU.desiredState = 'launched';
return fleetCtl.createUnit(customU).then(function (response) {
logging.info(customU.name + ' custom unit has been created');
delete customU.options;
customU.systemdActiveState = 'inactive';
deferred.resolve(customU);
});
}).catch(function (err) {
logging.error(units[0] + ' ' + err.error.message);
deferred.reject({
errors: [
{message: err.error.message}
]
});
});
}
return deferred.promise;
};
fleet.deleteUnits = function (host) {
var units = customizedRequiredUnits(host);
var promises = units.map(function (u) {
return fleetCtl.deleteUnit(u).then(function (response) {
logging.debug('Deleted unit: ' + u);
return response;
}).catch(function (err) {
logging.error('Error deleting unit ' + u + ': ' + JSON.stringify(err));
return err;
});
});
return Q.allSettled(promises).then(function (promises) {
return promises.map(function (r) {
return r.value;
});
});
};
fleet.deleteOrphanedUnits = function (units) {
var promises = units.map(function (u) {
return fleetCtl.deleteUnit(u);
});
return Q.allSettled(promises).then(function (promises) {
var results = promises.map(function (r) {
return r.value;
});
return {results: results};
});
};
fleet.orphanedUnits = function () {
return fleetCtl.listCachedUnits().then(function (cachedUnits) {
var units = collateUnits(cachedUnits);
var promises = units.map(function (u) {
return fleetapi.verifyHost(u.id).then(function (response) {
Object.assign(u, response);
return u;
});
});
return Q.allSettled(promises).then(function (promises) {
return promises.map(function (r) {
return r.value;
}).filter(function (u) {
if (!u.exists) {
return true;
}
});
});
});
};
function validUnitTemplates() {
return [
'mysql-data',
'mysql',
'jss-app-discovery',
'jss-app-6',
'jss-app-7',
'jss-app-8',
'jss-data',
'mysql-discovery',
'jss-load-computers'];
}
function checkValidName(templateName) {
var templates = validUnitTemplates();
return !!(templates.find(function (t) {
if (t === templateName) {
return true;
}
}));
}
function collateUnits(units) {
var newArr = [];
units.map(function (unit) {
try {
var splitName = unit.name.split('@');
var id;
if (checkValidName(splitName[0])) {
id = splitName[1].match(/\d+/g);
}
if (!id) {
return undefined;
}
} catch (err) {
return undefined;
}
return {id: Number(id[0]), name: unit.name};
}).map(function (unit) {
if (!unit) {
return;
}
var found = newArr.find(function (u, index) {
if (u.id === unit.id) {
return true;
}
});
if (!found) {
newArr.push({id: unit.id, units: [unit.name]});
} else {
found.units.push(unit.name);
}
return;
});
return newArr;
};
function getOptionalUnit(unitName) {
return fleetCtl.getUnit(unitName);
}
function requiredUnitTemplates(host) {
return [
'mysql-data@.service',
'mysql@.service',
'jss-app-discovery@.service',
'jss-app-' + host._tomcatVersion + '@.service',
'jss-data@.service',
'mysql-discovery@.service'];
}
function getRequiredUnits(host) {
var deferred = Q.defer();
var requiredUnits = requiredUnitTemplates(host);
var promises = requiredUnits.map(function (unit) {
return fleetCtl.getUnit(unit);
});
Q.all(promises).then(function (response) {
deferred.resolve(response);
}).catch(function (err) {
deferred.reject(err);
});
return deferred.promise;
}
var unitVars = [
{name: 'jss-data@.service', properties: [{name: '{{issueKey}}', replace: '_branchName'}]},
{name: 'mysql-data@.service', properties: [{name: '{{issueKey}}', replace: '_branchName'}]},
{
name: 'jss-app-6@.service',
properties: [
{name: '{{war.file}}', replace: '_downloadUri'},
{name: '{{tomcat.version}}', replace: '_tomcatFullVersion'}
]
},
{
name: 'jss-app-7@.service',
properties: [
{name: '{{war.file}}', replace: '_downloadUri'},
{name: '{{tomcat.version}}', replace: '_tomcatFullVersion'}
]
},
{
name: 'jss-app-8@.service',
properties: [
{name: '{{war.file}}', replace: '_downloadUri'},
{name: '{{tomcat.version}}', replace: '_tomcatFullVersion'}
]
},
{
name: 'jss-app-discovery@.service', properties: [
{name: '{{appVersion}}', replace: '_tomcatVersion'},
{name: '{{hostname}}', replace: '_name'}]
},
{
name: 'mysql@.service', properties: [
{name: '{{mysqlType}}', replace: '_mysqlType'},
{name: '{{hostname}}', replace: '_name'}]
},
{
name: 'jss-load-computers@.service', properties: [
{name: '{{appVersion}}', replace: '_tomcatVersion'},
{name: '{{hostname}}', replace: '_name'},
{name: '{{computers}}', replace: 'computers'}]
},
{name: 'mysql-discovery@.service', properties: [{name: '{{hostname}}', replace: '_name'}]},
{name: 'update@.service', properties: [{name: '{{machineID}}', replace: '_coreID'}]}
];
var customReplace = function (unit, host) {
unitVars.map(function (uv) {
if (unit.name === uv.name) {
unit.options.map(function (o) {
for (var i = 0; i < uv.properties.length; i++) {
if (o.value.search(uv.properties[i].name) !== -1) {
o.value = o.value.split(uv.properties[i].name).join(host[uv.properties[i].replace]);
}
}
});
}
});
unit.name = unit.name.replace(/\@/, '@' + host._ID);
unit.desiredState = 'launched';
return unit;
};
function customizedRequiredUnits(host) {
var units = requiredUnitTemplates(host);
return units.map(function (u) {
return u.replace(/\@/, '@' + host._ID);
});
}
function customizedOptionalUnits() {
return [
'jss-load-computers@.service',
'update@.service',
'jss-upgrade@.service'
];
}
var constants = require('./constants');
var Q = require('q');
var logging = require('./logging');
var cache = require('memory-cache');
var CronJob = require('cron').CronJob;
var Client = require('node-rest-client').Client;
var client = new Client();
var api = module.exports = {};
api.startCronJobs = function () {
this.canListMachines = true;
this.canListUnits = true;
this.canListStates = true;
var fleet = this;
new CronJob('*/8 * * * * *', function () {
if (fleet.canListMachines) {
logging.debug('canListMachines CronJob fired, making request');
fleet.canListMachines = false;
queryMachines().then(function () {
logging.debug('canListMachines CronJob completed');
}).catch(function (err) {
logging.error('canListMachines error: ' + err.error.errorCode + ': ' + err.error.message);
}).finally(function (resp) {
fleet.canListMachines = true;
});
}
else {
logging.debug('canListMachines CronJob fired but last request still pending');
}
}, null, true);
new CronJob('*/13 * * * * *', function () {
if (fleet.canListUnits) {
logging.debug('canListUnits CronJob fired, making request');
fleet.canListUnits = false;
getNextPage().then(function () {
logging.debug('canListUnits CronJob completed');
}).catch(function (err) {
logging.error('canListUnits error: ' + err.error.errorCode + ': ' + err.error.message);
}).finally(function (resp) {
fleet.canListUnits = true;
});
}
else {
logging.debug('canListUnits CronJob fired but last request still pending');
}
}, null, true);
new CronJob('*/9 * * * * *', function () {
if (fleet.canListStates) {
logging.debug('canListStates CronJob fired, making request');
fleet.canListStates = false;
getNextStatesPage().then(function () {
logging.debug('canListStates CronJob completed');
}).catch(function (err) {
logging.error('canListStates error: ' + err.error.errorCode + ': ' + err.error.message);
}).finally(function (resp) {
fleet.canListStates = true;
});
}
else {
logging.debug('canListUnits CronJob fired but last request still pending');
}
}, null, true);
logging.info('fleetctl cronjobs started');
};
function getNextStatesPage(pagedStates, deferred, nextPageToken) {
var states = pagedStates || [];
var defer = deferred || Q.defer();
var nextPageToken = nextPageToken || '';
var nextPageTokenQuery = '';
if (nextPageToken !== '') {
nextPageTokenQuery = '?nextPageToken=' + nextPageToken;
}
var success = false;
client.get(constants.FLEET_SERVER + 'state' + nextPageTokenQuery, function (data, response) {
if (data.error || response.statusCode !== 200) {
defer.reject(data);
}
states.push.apply(states, data.states);
if (typeof data.nextPageToken === 'undefined') {
logging.debug('Finished getting states, reached last page');
success = true;
}
if (success) {
cache.put('states', states);
defer.resolve(states);
} else {
getNextStatesPage(states, defer, data.nextPageToken);
}
}).on('error', function (err) {
logging.error(err);
defer.reject({
error: {
errorCode: err.code,
message: 'Unable to contact Fleet host, please try your request again later'
}
});
});
return defer.promise;
}
function getNextPage(pagedUnits, deferred, nextPageToken) {
var units = pagedUnits || [];
var defer = deferred || Q.defer();
var nextPageToken = nextPageToken || '';
var nextPageTokenQuery = '';
if (nextPageToken !== '') {
nextPageTokenQuery = '?nextPageToken=' + nextPageToken;
}
var success = false;
logging.debug('Start getting unit page: ' + constants.FLEET_SERVER + 'units' + nextPageTokenQuery);
client.get(constants.FLEET_SERVER + 'units' + nextPageTokenQuery, function (data, response) {
logging.debug('Finished unit page: ' + constants.FLEET_SERVER + 'units' + nextPageTokenQuery);
if (data.error || response.statusCode !== 200) {
defer.reject(data);
}
units.push.apply(units, data.units);
if (typeof data.nextPageToken === 'undefined') {
logging.debug('Finished getting units, reached last page');
success = true;
}
if (success) {
cache.put('units', units);
defer.resolve(units);
} else {
getNextPage(units, defer, data.nextPageToken);
}
}).on('error', function (err) {
logging.error(err);
defer.reject({
error: {
errorCode: err.code,
message: 'Unable to contact Fleet host, please try your request again later'
}
});
});
return defer.promise;
}
api.listUnits = function () {
var units = cache.get('units');
if (units) {
return Q(units);
} else {
return Q({error: 'Cache is priming, please wait...'});
}
};
api.listCachedUnits = function () {
var deferred = Q.defer();
var units = cache.get('units');
if (units) {
deferred.resolve(units);
} else {
deferred.reject({error: 'Cache is priming, please wait...'});
}
return deferred.promise;
};
queryUnit = function (unitName) {
var deferred = Q.defer();
client.get(constants.FLEET_SERVER + 'units/' + unitName, function (data, response) {
if (response.statusCode !== 200) {
deferred.reject(data);
} else {
deferred.resolve(data);
}
}).on('error', function (err) {
logging.error(err);
deferred.reject({
error: {
errorCode: err.code,
message: 'Unable to contact Fleet host, please try your request again later'
}
});
});
return deferred.promise;
};
api.getUnit = function (unitName) {
return queryUnit(unitName);
};
api.createUnit = function (unit) {
var deferred = Q.defer();
client.put(constants.FLEET_SERVER + 'units/' + unit.name, {
headers: {'Content-Type': 'application/json'},
data: unit
}, function (data, response) {
if (response.statusCode !== 201) {
deferred.reject(data);
} else {
deferred.resolve(data);
}
}).on('error', function (err) {
logging.error(err);
deferred.reject({
error: {
errorCode: err.code,
message: 'Unable to contact Fleet host, please try your request again later'
}
});
});
return deferred.promise;
};
api.toggleUnitState = function (unitName, unit) {
var deferred = Q.defer();
client.put(constants.FLEET_SERVER + 'units/' + unitName, {
headers: {'Content-Type': 'application/json'},
data: unit
}, function (data, response) {
if (response.statusCode !== 204) {
deferred.reject(data);
} else {
deferred.resolve(data);
}
}).on('error', function (err) {
logging.error(err);
deferred.reject({
error: {
errorCode: err.code,
message: 'Unable to contact Fleet host, please try your request again later'
}
});
});
return deferred.promise;
};
queryStates = function () {
var deferred = Q.defer();
logging.debug('Getting all states');
client.get(constants.FLEET_SERVER + 'state', function (data, response) {
if (response.statusCode !== 200) {
deferred.reject(data);
} else {
cache.put('states', data);
deferred.resolve(data);
}
}).on('error', function (err) {
logging.error(err);
deferred.reject({
error: {
errorCode: err.code,
message: 'Unable to contact Fleet host, please try your request again later'
}
});
});
return deferred.promise;
}
api.getStates = function () {
var states = cache.get('states');
if (states) {
return Q(states);
} else {
return Q({error: 'Cache is priming, please wait...'});
}
};
queryUnitState = function (unitName) {
var deferred = Q.defer();
client.get(constants.FLEET_SERVER + 'state?unitName=' + unitName, function (data, response) {
if (response.statusCode !== 200) {
deferred.reject(data);
} else {
if (data.hasOwnProperty('states')) {
deferred.resolve(data.states[0]);
} else {
deferred.resolve(data);
}
}
}).on('error', function (err) {
logging.error(err);
deferred.reject({
error: {
errorCode: err.code,
message: 'Unable to contact Fleet host, please try your request again later'
}
});
});
return deferred.promise;
};
api.getUnitState = function (unitName) {
logging.debug('Getting unit state for: ' + unitName);
var states = cache.get('states');
if (!states || states.length === 0) {
return queryUnitState(unitName);
}
var state = states.filter(function (element) {
return (element.name === unitName);
});
if (state.length === 0) {
logging.debug(unitName + ' not found in state cache, trying fleet');
return queryUnitState(unitName);
} else {
return Q(state[0]);
}
};
api.queryUnitState = function (unitName) {
logging.debug('Querying unit state for: ' + unitName);
return queryUnitState(unitName);
};
function queryMachines() {
var deferred = Q.defer();
client.get(constants.FLEET_SERVER + 'machines', function (data, response) {
if (response.statusCode !== 200) {
deferred.reject(data);
} else {
cache.put('machines', data);
deferred.resolve(data);
}
}).on('error', function (err) {
logging.error(err);
deferred.reject({
error: {
errorCode: err.code,
message: 'Unable to contact Fleet host, please try your request again later'
}
});
});
return deferred.promise;
}
api.getMachines = function () {
var machines = cache.get('machines');
if (machines) {
return Q(machines);
} else {
return queryMachines();
}
};
api.getMachineState = function (machineID) {
var deferred = Q.defer();
logging.debug('Getting machine state for: ' + machineID);
client.get(constants.FLEET_SERVER + 'state?machineID=' + machineID, function (data, response) {
if (response.statusCode !== 200) {
deferred.reject(data);
} else {
deferred.resolve(data);
}
}).on('error', function (err) {
logging.error(err);
deferred.reject({
error: {
errorCode: err.code,
message: 'Unable to contact Fleet host, please try your request again later'
}
});
});
return deferred.promise;
};
api.deleteUnit = function (unitName) {
var deferred = Q.defer();
logging.debug('Deleting unit: ' + unitName);
client.delete(constants.FLEET_SERVER + 'units/' + unitName, function (data, response) {
if (response.statusCode !== 204) {
deferred.reject(data);
} else {
deferred.resolve({});
}
}).on('error', function (err) {
logging.error(err);
deferred.reject({
error: {
errorCode: err.code,
message: 'Unable to contact Fleet host, please try your request again later'
}
});
});
return deferred.promise;
};
api.retryState = function (desiredState, unitName, maxRetries, maxFailures, deferred, backoff) {
maxRetries = (maxRetries || maxRetries === 0) ? maxRetries : 5;
maxFailures = (maxFailures || maxFailures === 0) ? maxFailures : 5;
deferred = deferred || Q.defer();
backoff = backoff || 1;
var state;
var success = null;
queryUnitState(unitName).then(function (response) {
if (typeof (response) !== 'undefined') {
if (response.systemdActiveState === 'activating') {
maxFailures = 0;
}
if (response.systemdActiveState === desiredState || (desiredState === 'inactive' && isEmpty(response))) {
success = true;
state = response;
} else if (response.systemdActiveState === 'failed') {
maxFailures--;
if (maxFailures <= 0) {
deferred.reject(response);
return;
}
}
}
if (success) {
deferred.resolve(state);
} else if (maxRetries > 0) {
setTimeout(function () {
if (backoff < 30) {
backoff *= 2;
} else {
backoff = 30;
}
api.retryState(desiredState, unitName, maxRetries - 1, maxFailures, deferred, backoff);
}, backoff * 1000);
} else if (maxRetries === 0) {
deferred.reject(false);
}
});
return deferred.promise;
};
api.ping = function () {
var deferred = Q.defer();
logging.debug('Pinging Fleetd api');
client.get(constants.FLEET_SERVER + 'discovery', function (data, response) {
if (response.statusCode !== 200) {
deferred.resolve({
status: response.statusCode,
version: response.headers.server.split('/')[1],
errorMessage: 'Unknown problem accessing Fleet API'
});
} else {
deferred.resolve({
status: response.statusCode,
version: response.headers.server.split('/')[1]
});
}
}).on('error', function (err) {
logging.error(err);
deferred.resolve({
status: 500,
version: 'Unknown',
errorMessage: err.code + ': Unknown problem accessing Fleet API'
});
});
return deferred.promise;
};
function isEmpty(obj) {
for (var x in obj) {
return false;
}
return true;
}
var loggingLevel = process.env.LOGLEVEL || 'INFO';
var logLevel;
(function init() {
switch (loggingLevel) {
case 'DEBUG':
logLevel = 0;
break;
case 'INFO':
logLevel = 1;
break;
case 'WARNING':
logLevel = 2;
break;
case 'ERROR':
logLevel = 3;
break;
}
return;
})();
var api = module.exports = {};
function timestamp() {
return '[' + new Date().toUTCString() + ']: ';
}
api.debug = function (msg) {
if (logLevel === 0) {
console.log((timestamp() + msg));
}
};
api.info = function (msg) {
if (logLevel <= 1) {
console.log(timestamp() + msg);
}
};
api.warning = function (msg) {
if (logLevel <= 2) {
console.log(timestamp() + msg);
}
};
api.error = function (msg) {
if (logLevel <= 3) {
console.log(timestamp() + msg);
}
};
var zmq = require('zmq');
var zmqResponder = zmq.socket('rep');
var fleet = require('./fleet');
var artifactory = require('./artifactory');
var logging = require('./logging');
var api = module.exports = {};
// Register to monitoring events
zmqResponder.on('connect', function (fd, ep) {
console.log('connect, endpoint:', ep);
});
zmqResponder.on('connect_delay', function (fd, ep) {
console.log('connect_delay, endpoint:', ep);
});
zmqResponder.on('connect_retry', function (fd, ep) {
console.log('connect_retry, endpoint:', ep);
});
zmqResponder.on('listen', function (fd, ep) {
console.log('listen, endpoint:', ep);
});
zmqResponder.on('bind_error', function (fd, ep) {
console.log('bind_error, endpoint:', ep);
});
zmqResponder.on('accept', function (fd, ep) {
console.log('accept, endpoint:', ep);
});
zmqResponder.on('accept_error', function (fd, ep) {
console.log('accept_error, endpoint:', ep);
});
zmqResponder.on('close', function (fd, ep) {
console.log('close, endpoint:', ep);
});
zmqResponder.on('close_error', function (fd, ep) {
console.log('close_error, endpoint:', ep);
});
zmqResponder.on('disconnect', function (fd, ep) {
console.log('disconnect, endpoint:', ep);
});
// Handle monitor error
zmqResponder.on('monitor_error', function (err) {
console.log('Error in monitoring: %s, will restart monitoring in 5 seconds', err);
setTimeout(function () {
zmqResponder.monitor(500, 0);
}, 5000);
});
// Call monitor, check for events every 500ms and get all available events.
console.log('Start monitoring...');
zmqResponder.monitor(500, 0);
zmqResponder.connect('tcp://127.0.0.1:5668');
zmqResponder.on('message', function (msg, data) {
var parsed = JSON.parse(msg);
logging.info('ZMQ Request received: ' + parsed.event);
switch (parsed.event) {
case 'create':
return artifactory.verifyWar(parsed.data).then(function (response) {
parsed.data['_downloadUri'] = response.downloadUri;
parsed.data['_lastModified'] = response.lastModified;
return artifactory.getBuildProperties(response.uri);
}).then(function (response) {
parsed.data['buildName'] = response.buildName;
parsed.data['buildNumber'] = response.buildNumber;
return artifactory.getBuildInfo(response);
}).then(function (response) {
parsed.data['sha'] = response.sha;
parsed.data['link'] = response.link;
return fleet.createInstance(parsed.data).then(function (response) {
var parsedResponse = {
downloadUri: parsed.data['_downloadUri'],
lastModified: parsed.data['_lastModified'],
sha: parsed.data['sha'],
link: parsed.data['link'],
buildName: parsed.data['buildName'],
buildNumber: parsed.data['buildNumber'],
units: response
};
logging.info('Sending fleets creation response');
zmqResponder.send(JSON.stringify(parsedResponse));
}).catch(function (err) {
zmqResponder.send(JSON.stringify(err));
logging.error(JSON.stringify(err));
});
}).catch(function (err) {
zmqResponder.send(JSON.stringify(err));
logging.error(JSON.stringify(err));
});
break;
case 'delete':
return fleet.deleteUnits(parsed.data).then(function (response) {
logging.info('Removed units successfully');
zmqResponder.send(JSON.stringify(response));
});
break;
case 'upgrade':
var parsedReponse = {};
return fleet.upgradeInstance(parsed.data).then(function (response) {
logging.info('Upgraded: ' + parsed.data._ID);
return artifactory.verifyWar(parsed.data).then(function (response) {
parsedReponse.downloadUri = response.downloadUri;
parsedReponse.lastModified = response.lastModified;
return artifactory.getBuildProperties(response.uri);
}).then(function (response) {
parsedReponse.buildName = response.buildName;
parsedReponse.buildNumber = response.buildNumber;
return artifactory.getBuildInfo(response);
}).then(function (response) {
parsedReponse.sha = response.sha;
parsedReponse.link = response.link;
logging.info('Sending fleets upgrade response');
zmqResponder.send(JSON.stringify(parsedReponse));
}).catch(function (err) {
zmqResponder.send(JSON.stringify(err));
logging.error(JSON.stringify(err));
});
}).catch(function (err) {
logging.error(JSON.stringify({error: {message: 'JSS Upgrade attempt failed'}}));
zmqResponder.send(JSON.stringify(err));
});
break;
case 'test':
setTimeout(function () {
console.log('received: ' + parsed.data);
zmqResponder.send(JSON.stringify('Message ID: ' + parsed.data));
}, (Math.floor(Math.random() * (10 - 5 + 1)) + 5) * 1000);
break;
}
});
zmqResponder.on('error', function (err) {
logging.error(err);
console.log(zmqResponder);
zmqResponder.send(JSON.stringify(err));
});
zmqResponder.bind('tcp://*:5668', function (err) {
if (err) {
logging.error(err);
} else {
logging.info("ZMQ awaiting orders on port 5668");
}
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment