seaport-haproxy
Version:
Sync seaport and haproxy to keep your services balanced
135 lines (109 loc) • 3.17 kB
JavaScript
var Emitter = require('events').EventEmitter;
var ejs = require('ejs');
var mfs = require('fs');
var oop = require('oop-utils');
/**
* SeaportHaproxySyncService
*
* Syncs seaport with haproxy by auto-managing backends.
*
* @contructor
* @extends events.EventEmitter
* @param {Seaport} seaport
* @param {Haproxy} haproxy
* @param {Object} config
*/
function SeaportHaproxySyncService(seaport, haproxy, config) {
var sh = this;
this.seaport = seaport;
this.haproxy = haproxy;
this.maxconn = config.maxconn || 65536;
this.config = config;
this.template = ejs.compile(
mfs.readFileSync(config.template).toString()
);
this.destination = haproxy.cfg;
// Internal variables
this._reloading = false;
this.seaport.on('register', function(service) {
sh.emit('seaport:register', service);
sh.emit('seaport:change', service);
});
this.seaport.on('free', function(service) {
sh.emit('seaport:free', service);
sh.emit('seaport:change', service);
});
this.seaport.on('stale', function(service) {
sh.emit('seaport:stale', service);
sh.emit('seaport:change', service);
});
sh.on('seaport:change', this.onSeaportChange);
this.seaport.once('synced', function() {
sh.emit('seaport:change');
});
}
// exports
exports.SeaportHaproxySyncService = SeaportHaproxySyncService;
exports.createService = function createService (seaport, haproxy, config) {
return (new SeaportHaproxySyncService(seaport, haproxy, config));
};
// @extends events.EventEmitter
var p = oop.inherits(SeaportHaproxySyncService, Emitter);
/**
* onSeaportChange
*
* The seaport registry has changes. Re-generate and re-load the configuration
* for haproxy.
*/
p.onSeaportChange = function onSeaportChange () {
var sh = this;
if (this._reloading) { return; }
this._reloading = true;
var services = this.seaport.query(null);
var hosts = Object.create(null);
var roles = Object.create(null);
var service = null;
for (var i = 0, il = services.length; i < il; i += 1) {
service = services[i];
hosts[service.host + ':' + service.port] = true;
if (!roles[service.role]) { roles[service.role] = []; }
roles[service.role].push(service);
}
var opts = this.config.options || {};
opts.maxconn = Math.floor(sh.maxconn / Object.keys(hosts).length);
opts.roles = roles;
var cfg = sh.template(opts);
mfs.writeFile(sh.destination, cfg, doneConfig);
function doneConfig (err) {
if (err) { return sh.emit('error', err); }
sh.reloadHaproxy(reloaded);
}
function reloaded (err) {
if (err) { return sh.emit('error', err); }
sh.emit('haproxy:reload');
sh._reloading = false;
}
};
/**
* reloadHaproxy
*
* Ensure haproxy is started, otherwise reload
*/
p.reloadHaproxy = function reloadHaproxy (done) {
var sh = this;
sh.haproxy.running(runningCheck);
function runningCheck (err, running) {
if (err) { return done(err); }
else if (!running) { return sh.haproxy.start(started); }
started(null, true);
}
function started (err, reload) {
if (err) { return done(err); }
else if (reload) { return sh.haproxy.reload(reloaded); }
done();
}
function reloaded (err) {
if (err) { return done(err); }
done();
}
};