Created
June 25, 2016 18:27
-
-
Save jippi/992e1a3ab7954e7d011a7979e7681ebf to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"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