Skip to content

Instantly share code, notes, and snippets.

@jippi
Created June 25, 2016 18:27
Show Gist options
  • Save jippi/992e1a3ab7954e7d011a7979e7681ebf to your computer and use it in GitHub Desktop.
Save jippi/992e1a3ab7954e7d011a7979e7681ebf to your computer and use it in GitHub Desktop.
"use strict";
const log = require('bunyan').createLogger({name: 'ip-failover'});
const axios = require('axios');
const consul = require('consul')();
const os = require("os")
const ip = require('ip');
const argv = require('minimist')(process.argv.slice(2));
const getConfigVariable = (envName, argvName, defaultValue) => {
if (envName in process.env) {
return process.env[envName];
}
if (argvName in argv) {
return argv[argvName];
}
if (defaultValue) {
return defaultValue;
}
log.error('missing "--' + argvName + '" argument or "' + envName + '" environment');
process.exit(1);
};
const failoverIp = getConfigVariable('FAILOVER_IP', 'failover-ip');
const instanceIp = getConfigVariable('INSTANCE_IP', 'instance-ip', ip.address('eth0', 'ipv4'));
const apiToken = getConfigVariable('ONLINE_API_KEY', 'api-key');
// the consul sessionId ID
let sessionId;
// consul lock object
let lock;
// track if the lock is held or not
let lockHeld = false;
// key/value path to use as exchange
const lockPath = 'ip-failover/' + failoverIp;
// consul session configuration
const sessionConfig = {
name: 'ip-failover-' + os.hostname(),
lockdelay: '1s',
behavior: 'delete'
};
// default
const api = axios.create({
baseURL: 'https://api.online.net/api/v1',
headers: {
"Authorization": "Bearer " + apiToken,
"X-Pretty-JSON": 1
}
});
/**
* Graceful Shutdown handler
*
* @return void
*/
function gracefulShutdown() {
if (lockHeld) {
log.warn('Releasing lock...')
lock.release();
}
if (sessionId) {
log.warn('Destroying session ...')
consul.session.destroy(sessionId, function() {
log.info('All done... bye bye');
process.exit();
});
} else {
log.info('All done... bye bye');
process.exit();
}
};
/**
* Handler for SIGINT (Ctrl-C)
*
* @return void
*/
function sigIntHandler() {
log.warn("Gracefully shutting down from SIGINT (Ctrl-C)");
gracefulShutdown();
};
/**
* Handler for SIGTERM (e.g. supervisor / default value for "kill")
*
* @return void
*/
function sigTermHandler() {
log.warn("Gracefully shutting down from SIGTERM");
gracefulShutdown();
};
/**
* Handler for SIGHUP
*
* @todo Not sure this one should shut down the process,
* should probably rather reload configuration
*
* @return void
*/
function sigHupHandler() {
log.warn("Gracefully shutting down from SIGHUP");
gracefulShutdown();
};
/**
* Handler for when the process is exciting
*
* @param int code exit code
* @return void
*/
function exitHandler(code) {
log.warn('Bye bye... exit with code:', code);
};
function requestIPmove() {
log.info('Requesting IP move...');
api.post('/server/failover/edit', {
source: failoverIp,
destination: instanceIp
})
.then((response) => {
log.info('Failover IP (' + failoverIp + ') now point to our instance (' + instanceIp + ') ....');
})
.catch((error) => {
if (error.data.code === 10) {
log.info('Failover IP (' + failoverIp + ') already point to our instance (' + instanceIp + ') ....');
return;
}
log.error('Error:')
log.error(error.data);
log.error(error.status);
log.error(error.statusText);
});
};
process.on('SIGINT', () => { sigIntHandler(); });
process.on('SIGHUP', () => { sigHupHandler(); });
process.on('SIGTERM', () => { sigIntHandler() });
process.on('exit', (code) => { return exitHandler(code); });
consul.session.create(sessionConfig, (err, result) => {
if (err) { throw err; }
sessionId = result.ID;
log.info('Consul Session created: ' + sessionId);
// Refresh session every 5 minute
setInterval(() => {
consul.session.renew(sessionId, (err, renew) => {
if (err) throw err;
log.info('Successfully renewed session');
});
}, 300000);
lock = consul.lock({ key: lockPath, value: os.hostname(), session: sessionId });
lock.on('acquire', () => {
log.info('lock acquired');
lockHeld = true;
requestIPmove();
});
lock.on('release', () => {
log.info('lock released');
lockHeld = false;
});
lock.on('error', (err) => {
log.error('lock error:', err);
lockHeld = false;
});
lock.on('end', (err) => {
lockHeld = false;
log.warn('lock released or there was a permanent failure');
gracefulShutdown();
});
log.info('Trying to acquire consul lock')
lock.acquire();
});
log.info('IP fail-over for "' + instanceIp + '" <=> "' + failoverIp + '" running');
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment