jiny
Version:
Jiny - runs tests/jobs in parallel
205 lines (138 loc) • 4.54 kB
JavaScript
var Class = require('plus.class');
var Slaves = new Class({
init: function () {
var self = this;
this.slaves = [];
this.underscore = require('underscore');
this.async = require('async');
this.child_process = require('child_process');
this.queueStream = require('async-queue-stream');
var Events = require('events').EventEmitter;
this.events = new Events();
this.initQueue();
},
initQueue: function () {
var self = this;
this.jobQueue = this.async.queue(function (command, next) {
self.execute(command.toString(), function () {
next();
});
});
this.jobQueue.drain = function () {
setTimeout(function () {
if (!self.jobQueue.length()) {
self.events.emit('jobs.done');
}
}, 500);
}
self.resultStream = self.queueStream();
this.events.on('jobs.done', function () {
self.resultStream.end();
self.resultStream = self.queueStream();
});
this.events.on('slaves.changed', function () {
self.jobQueue.concurrency = self.count();
});
},
count: function () {
return this.slaves.length;
},
add: function (slave) {
this.slaves.push(slave);
slave.id = this.slaves.length;
this.events.emit('slaves.changed');
},
getFree: function () {
return this.underscore.find(this.slaves, function (slave) {
return !slave.busy;
});
},
getFreeAsync: function (next) {
var slave = this.getFree();
if (slave) return next(null, slave);
var interval = setInterval(function () {
var slave = this.getFree();
if (slave) {
clearInterval(interval);
next(null, slave)
}
}.bind(this), 500);
},
getBySocket: function (socket) {
return this.underscore.find(this.slaves, function (slave) {
return slave.api.socket == socket;
});
},
remove: function (slave) {
var idx = this.slaves.indexOf(slave);
if (idx >= 0) {
this.slaves.splice(idx, 1);
this.events.emit('slaves.changed');
}
},
removeBySocket: function (socket) {
var slave = this.getBySocket(socket);
if (slave) {
this.remove(slave);
}
},
upload: function (dir, next) {
var spawn = this.child_process.spawn;
var packer = spawn('tar', ['-C', dir, '-cz', '.']);
var fileStream = packer.stdout;
this.async.map(this.slaves, function (slave, next) {
console.log('#' + slave.id, slave.ip, 'uploading...');
slave.upload(fileStream, function (err, info) {
next(err, info);
});
}, next);
},
run: function (command, next) {
this.async.map(this.slaves, function (slave, next) {
slave.command(command, function (error, info) {
next(null, info);
});
}, next);
},
job: function (command, next) {
this.jobQueue.push(command, function () {
});
next(null, command);
},
jobs: function (jobsStream, next) {
jobsStream.pipe(this.queueStream(function (command, next) {
this.job(command, next);
}.bind(this)));
this.events.on('jobs.done', function () {
setTimeout(next, 500);
});
},
execute: function (command, next) {
var self = this;
this.getFreeAsync(function (err, slave) {
slave.busy = true;
slave.command(command, function (error, info) {
slave.busy = false;
self.resultStream.write(info);
next(null, info)
});
});
},
stop: function (next) {
if(!this.count()) return next();
this.async.map(this.slaves, function (slave, next) {
slave.stop(next);
}, next);
},
waitSlaves: function (quantity, next) {
var handle = function () {
if (this.count() >= quantity) {
this.events.removeListener('slaves.changed', handle);
next();
}
}.bind(this);
this.events.on('slaves.changed', handle);
handle();
}
});
module.exports = Slaves;