230 lines
4.6 KiB
JavaScript
230 lines
4.6 KiB
JavaScript
|
|
||
|
/*!
|
||
|
* Cluster - cli
|
||
|
* Copyright (c) 2011 LearnBoost <dev@learnboost.com>
|
||
|
* MIT Licensed
|
||
|
*/
|
||
|
|
||
|
/**
|
||
|
* Module dependencies.
|
||
|
*/
|
||
|
|
||
|
var fs = require('fs')
|
||
|
, Log = require('log');
|
||
|
|
||
|
/**
|
||
|
* Commands.
|
||
|
*/
|
||
|
|
||
|
var commands = [];
|
||
|
|
||
|
/**
|
||
|
* Adds a command-line interface to your cluster.
|
||
|
*
|
||
|
* This plugin requires that you use `pidfiles()`
|
||
|
* above `cli()`, so that the pidfile directory
|
||
|
* is exposed.
|
||
|
*
|
||
|
* Examples:
|
||
|
*
|
||
|
* cluster(server)
|
||
|
* .use(cluster.pidfiles())
|
||
|
* .use(cluster.cli())
|
||
|
* .listen(3000);
|
||
|
*
|
||
|
* Once set up our server script serves as both
|
||
|
* the master, and the CLI. For example we may
|
||
|
* still launch the server(s) as shown below.
|
||
|
*
|
||
|
* $ nohup node server.js &
|
||
|
*
|
||
|
* However now we may also utilize commands
|
||
|
* provided by this plugin.
|
||
|
*
|
||
|
* $ node server.js status
|
||
|
*
|
||
|
* master 3281 dead
|
||
|
* worker 0 3282 dead
|
||
|
*
|
||
|
* For more command information use `--help`.
|
||
|
*
|
||
|
* $ node server.js --help
|
||
|
*
|
||
|
* @return {Function}
|
||
|
* @api public
|
||
|
*/
|
||
|
|
||
|
exports = module.exports = function(){
|
||
|
return function(master){
|
||
|
requirePIDs(master);
|
||
|
|
||
|
// augment master
|
||
|
master.killall = function(sig){
|
||
|
var pid = master.pidof('master');
|
||
|
try {
|
||
|
// signal master
|
||
|
process.kill(pid, sig);
|
||
|
} catch (err) {
|
||
|
if ('ESRCH' != err.code) throw err;
|
||
|
// signal children individually
|
||
|
master.workerpids().forEach(function(pid){
|
||
|
try {
|
||
|
process.kill(pid, sig);
|
||
|
} catch (err) {
|
||
|
if ('ESRCH' != err.code) throw err;
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
};
|
||
|
|
||
|
var args = process.argv.slice(2)
|
||
|
, len = commands.length
|
||
|
, command
|
||
|
, arg;
|
||
|
|
||
|
// parse arguments
|
||
|
while (args.length) {
|
||
|
arg = args.shift();
|
||
|
for (var i = 0; i < len; ++i) {
|
||
|
command = commands[i];
|
||
|
if (~command.flags.indexOf(arg)) {
|
||
|
command.callback(master);
|
||
|
master.preventDefault = true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Define command `name` with the given callback `fn(master)`
|
||
|
* and a short `desc`.
|
||
|
*
|
||
|
* @param {String} name
|
||
|
* @param {Function} fn
|
||
|
* @param {String} desc
|
||
|
* @return {Object} exports for chaining
|
||
|
* @api public
|
||
|
*/
|
||
|
|
||
|
var define = exports.define = function(name, fn, desc){
|
||
|
commands.push({
|
||
|
flags: name.split(/ *, */)
|
||
|
, desc: desc
|
||
|
, callback: fn
|
||
|
});
|
||
|
return exports;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Report master / worker status based on
|
||
|
* the PID files saved by the pidfiles()
|
||
|
* plugin.
|
||
|
*/
|
||
|
|
||
|
define('-s, --status, status', function(master){
|
||
|
var dir = master.pidfiles
|
||
|
, files = fs.readdirSync(dir);
|
||
|
|
||
|
// null signal failed previous
|
||
|
// to this release
|
||
|
if (process.version < 'v0.4.1') {
|
||
|
console.log('status will not work with node < 0.4.1');
|
||
|
console.log('due to SIGTERM globbering the null signal');
|
||
|
process.exit(1);
|
||
|
}
|
||
|
|
||
|
console.log();
|
||
|
|
||
|
// only pids
|
||
|
files.filter(function(file){
|
||
|
return file.match(/\.pid$/);
|
||
|
// check status
|
||
|
}).forEach(function(file){
|
||
|
var name = file.replace('.pid', '')
|
||
|
, pid = master.pidof(name)
|
||
|
, name = name.replace('.', ' ')
|
||
|
, color
|
||
|
, status;
|
||
|
|
||
|
try {
|
||
|
process.kill(pid, 0);
|
||
|
status = 'alive';
|
||
|
color = '36';
|
||
|
} catch (err) {
|
||
|
if ('ESRCH' == err.code) {
|
||
|
color = '31';
|
||
|
status = 'dead';
|
||
|
} else {
|
||
|
throw err;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
console.log(' %s\033[90m %d\033[0m \033[' + color + 'm%s\033[0m'
|
||
|
, name
|
||
|
, pid
|
||
|
, status);
|
||
|
});
|
||
|
|
||
|
console.log();
|
||
|
}, 'Output cluster status');
|
||
|
|
||
|
/**
|
||
|
* Restart workers.
|
||
|
*/
|
||
|
|
||
|
define('-r, --restart, restart', function(master){
|
||
|
master.killall('SIGUSR2');
|
||
|
}, 'Restart master by sending the SIGUSR2 signal');
|
||
|
|
||
|
/**
|
||
|
* Graceful shutdown.
|
||
|
*/
|
||
|
|
||
|
define('-g, --shutdown, shutdown', function(master){
|
||
|
master.killall('SIGQUIT');
|
||
|
}, 'Graceful shutdown by sending the SIGQUIT signal');
|
||
|
|
||
|
/**
|
||
|
* Hard shutdown.
|
||
|
*/
|
||
|
|
||
|
define('-S, --stop, stop', function(master){
|
||
|
master.killall('SIGTERM');
|
||
|
}, 'Hard shutdown by sending the SIGTERM signal');
|
||
|
|
||
|
/**
|
||
|
* Display help information.
|
||
|
*/
|
||
|
|
||
|
define('-h, --help, help', function(master){
|
||
|
console.log('\n Usage: node <file> <command>\n');
|
||
|
commands.forEach(function(command){
|
||
|
console.log(' '
|
||
|
+ command.flags.join(', ')
|
||
|
+ '\n '
|
||
|
+ '\033[90m' + command.desc + '\033[0m'
|
||
|
+ '\n');
|
||
|
});
|
||
|
console.log();
|
||
|
}, 'Show help information');
|
||
|
|
||
|
/**
|
||
|
* Output cluster version.
|
||
|
*/
|
||
|
|
||
|
define('-v, --version', function(master){
|
||
|
console.log(require('../cluster').version);
|
||
|
}, 'Output cluster version');
|
||
|
|
||
|
/**
|
||
|
* Require `pidfiles()` plugin.
|
||
|
*
|
||
|
* @param {Master} master
|
||
|
* @api private
|
||
|
*/
|
||
|
|
||
|
function requirePIDs(master) {
|
||
|
if (master.pidfiles) return;
|
||
|
throw new Error('cli() plugin requires pidfiles(), please add pidfiles() before cli()');
|
||
|
}
|