mirror of
https://github.com/Unitech/pm2.git
synced 2025-12-08 20:35:53 +00:00
auto detective interpreter in fork mode; add corresponding testcase
This commit is contained in:
parent
2128843740
commit
5bce6bba94
59
lib/CLI.js
59
lib/CLI.js
@ -12,6 +12,7 @@ var Log = require('./Log');
|
||||
var Satan = require('./Satan');
|
||||
var cst = require('../constants.js');
|
||||
var pkg = require('../package.json');
|
||||
var extItps = require('./interpreter.json')
|
||||
|
||||
var CLI = module.exports = {};
|
||||
require('colors');
|
||||
@ -25,7 +26,7 @@ CLI.startFile = function(script) {
|
||||
if (commander.name)
|
||||
appConf['name'] = commander.name;
|
||||
if (commander.instances)
|
||||
appConf['instances'] = commander.instances;
|
||||
appConf['instances'] = commander.instances;
|
||||
if (commander.error)
|
||||
appConf['error_file'] = commander.error;
|
||||
if (commander.output)
|
||||
@ -34,17 +35,19 @@ CLI.startFile = function(script) {
|
||||
appConf['pid_file'] = commander.pid;
|
||||
if (commander.cron)
|
||||
appConf['cron_restart'] = commander.cron;
|
||||
|
||||
|
||||
if (commander.interpreter)
|
||||
appConf['exec_interpreter'] = commander.interpreter;
|
||||
else if (extItps[path.extname(commander.args[0])])
|
||||
appConf['exec_interpreter'] = extItps[path.extname(commander.args[0])]
|
||||
else
|
||||
appConf['exec_interpreter'] = 'node';
|
||||
|
||||
|
||||
if (commander.executeCommand)
|
||||
appConf['exec_mode'] = 'fork_mode';
|
||||
else
|
||||
appConf['exec_mode'] = 'cluster_mode';
|
||||
|
||||
|
||||
if (commander.startOneTime)
|
||||
appConf['one_launch_only'] = cst.ONE_LAUNCH_STATUS;
|
||||
|
||||
@ -52,7 +55,7 @@ CLI.startFile = function(script) {
|
||||
process.version.match(/0.10/)) {
|
||||
console.log(cst.PREFIX_MSG_ERR + ' [Warning], you\'re using the 0.10.x node version, it\'s prefered that you switch to fork mode by adding the -x parameter.');
|
||||
}
|
||||
|
||||
|
||||
// Script arguments
|
||||
var env = commander.rawArgs.indexOf('--') + 1;
|
||||
if (env > 1)
|
||||
@ -63,7 +66,7 @@ CLI.startFile = function(script) {
|
||||
console.log(cst.PREFIX_MSG + 'Writing configuration to ', dst_path);
|
||||
fs.writeFileSync(dst_path, JSON.stringify(appConf));
|
||||
}
|
||||
|
||||
|
||||
Satan.executeRemote('findByFullPath', path.resolve(process.cwd(), script), function(err, exec) {
|
||||
if (exec && exec[0].pm2_env.status == cst.STOPPED_STATUS) {
|
||||
var app_name = exec[0].pm2_env.name;
|
||||
@ -169,23 +172,23 @@ CLI.startup = function(platform) {
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
var INIT_SCRIPT = "/etc/init.d/pm2-init.sh";
|
||||
var script = fs.readFileSync(path.join(__dirname, cst.STARTUP_SCRIPT));
|
||||
|
||||
|
||||
script = script.toString().replace(/%PM2_PATH%/g, process.mainModule.filename);
|
||||
script = script.toString().replace(/%HOME_PATH%/g, process.env.HOME);
|
||||
script = script.toString().replace(/%NODE_PATH%/g, process.execPath);
|
||||
script = script.toString().replace(/%USER%/g, commander.user || 'root');
|
||||
|
||||
|
||||
fs.writeFileSync(INIT_SCRIPT, script);
|
||||
|
||||
if (fs.existsSync(INIT_SCRIPT) == false) {
|
||||
|
||||
if (fs.existsSync(INIT_SCRIPT) == false) {
|
||||
console.log(script);
|
||||
console.log(cst.PREFIX_MSG_ERR + ' There is a problem when trying to write file : ' + INIT_SCRIPT);
|
||||
process.exit(cst.ERROR_EXIT);
|
||||
}
|
||||
|
||||
|
||||
var cmd;
|
||||
|
||||
if (platform == 'centos')
|
||||
@ -281,7 +284,7 @@ CLI.restartAll = function() {
|
||||
console.error('Error retrieving process list: ' + err);
|
||||
process.exit(cst.ERROR_EXIT);
|
||||
}
|
||||
|
||||
|
||||
list.forEach(function(l) {
|
||||
Satan.executeRemote('restartProcessId', l.pm2_env.pm_id, function(err, res) {
|
||||
if (err) {
|
||||
@ -289,9 +292,9 @@ CLI.restartAll = function() {
|
||||
process.exit(cst.ERROR_EXIT);
|
||||
}
|
||||
console.log(cst.PREFIX_MSG + 'Process ' + l.pm2_env.name + ' restarted');
|
||||
});
|
||||
});
|
||||
setTimeout(function() {
|
||||
});
|
||||
});
|
||||
setTimeout(function() {
|
||||
console.log('\n' + cst.PREFIX_MSG + 'Process restarted');
|
||||
speedList();
|
||||
}, 1000);
|
||||
@ -317,7 +320,7 @@ CLI.deleteProcess = function(process_name) {
|
||||
process.exit(cst.ERROR_EXIT);
|
||||
}
|
||||
UX.processing.stop();
|
||||
speedList();
|
||||
speedList();
|
||||
});
|
||||
}
|
||||
else if (!isNaN(parseInt(process_name))) {
|
||||
@ -361,7 +364,7 @@ CLI.stopId = function(pm2_id) {
|
||||
console.error(cst.PREFIX_MSG_ERR + pm2_id + ' : pm2 id not found');
|
||||
process.exit(cst.ERROR_EXIT);
|
||||
}
|
||||
console.log(cst.PREFIX_MSG + ' Process stopped');
|
||||
console.log(cst.PREFIX_MSG + ' Process stopped');
|
||||
UX.processing.stop();
|
||||
speedList();
|
||||
});
|
||||
@ -437,7 +440,7 @@ CLI.monit = function() {
|
||||
console.log(cst.PREFIX_MSG + 'No online process to monitor');
|
||||
process.exit(cst.ERROR_EXIT);
|
||||
}
|
||||
|
||||
|
||||
Monit.init(list);
|
||||
|
||||
function refresh(cb) {
|
||||
@ -458,7 +461,7 @@ CLI.monit = function() {
|
||||
|
||||
CLI.streamLogs = function(id) {
|
||||
var tdb = {};
|
||||
|
||||
|
||||
Satan.executeRemote('getMonitorData', {}, function(err, list) {
|
||||
if (err) {
|
||||
console.error('Error retrieving process list: ' + err);
|
||||
@ -467,7 +470,7 @@ CLI.streamLogs = function(id) {
|
||||
list.forEach(function(l) {
|
||||
tdb[l.pm2_env.pm_exec_path] = l;
|
||||
});
|
||||
|
||||
|
||||
console.log('########### Starting streaming logs for [%s] process', id || 'all');
|
||||
for (var k in tdb) {
|
||||
if (((!id || (id && !isNaN(parseInt(id)) && tdb[k].pm2_env.pm_id == id)) ||
|
||||
@ -517,7 +520,7 @@ function validate(appConf) {
|
||||
var instances = appConf['instances'];
|
||||
var script = appConf['script'];
|
||||
var cron_pattern = appConf['cron_restart'];
|
||||
|
||||
|
||||
if (instances && isNaN(parseInt(instances)) && instances != 'max') {
|
||||
console.error(cst.PREFIX_MSG_ERR + 'Instance option must be an integer or the "max" string');
|
||||
process.exit(cst.ERROR_EXIT);
|
||||
@ -543,12 +546,12 @@ function validate(appConf) {
|
||||
function resolvePaths(app) {
|
||||
|
||||
validate(app);
|
||||
|
||||
|
||||
app.env = {
|
||||
pm_cwd : process.cwd(),
|
||||
NODE_ENV : "production" // set default node_env as production
|
||||
NODE_ENV : "production" // set default node_env as production
|
||||
};
|
||||
|
||||
|
||||
if (!('exec_mode' in app)) app['exec_mode'] = 'cluster_mode';
|
||||
|
||||
app["pm_exec_path"] = path.resolve(process.cwd(), app.script);
|
||||
@ -558,12 +561,12 @@ function resolvePaths(app) {
|
||||
app["name"] = p.basename(app["pm_exec_path"]);
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (fs.existsSync(app.pm_exec_path) == false) {
|
||||
console.error(cst.PREFIX_MSG_ERR + 'script not found : ' + app.pm_exec_path);
|
||||
process.exit(cst.ERROR_EXIT);
|
||||
}
|
||||
|
||||
|
||||
if (app.out_file)
|
||||
app["pm_out_log_path"] = path.resolve(process.cwd(), app.out_file);
|
||||
else {
|
||||
@ -574,7 +577,7 @@ function resolvePaths(app) {
|
||||
app["pm_out_log_path"] = path.resolve(cst.DEFAULT_LOG_PATH, [app.name, '-out.log'].join(''));
|
||||
app.out_file = app["pm_out_log_path"];
|
||||
}
|
||||
delete app.out_file;
|
||||
delete app.out_file;
|
||||
|
||||
if (app.error_file)
|
||||
app["pm_err_log_path"] = path.resolve(process.cwd(), app.error_file);
|
||||
|
||||
77
lib/God.js
77
lib/God.js
@ -48,7 +48,7 @@ function handleExit(clu, exit_code) {
|
||||
|
||||
var stopping = (clu.pm2_env.status == 'stopping' || clu.pm2_env.status == cst.ERRORED_STATUS) ? true : false;
|
||||
var overlimit = false;
|
||||
|
||||
|
||||
if (stopping) clu.process.pid = 0;
|
||||
|
||||
if (clu.pm2_env.status != cst.ERRORED_STATUS)
|
||||
@ -57,15 +57,15 @@ function handleExit(clu, exit_code) {
|
||||
|
||||
/**
|
||||
* Avoid infinite reloop if an error is present
|
||||
*/
|
||||
*/
|
||||
// If the process has been created less than 15seconds ago
|
||||
if ((Date.now() - clu.pm2_env.created_at) < 15000) {
|
||||
// And if the process has an uptime less than a second
|
||||
if ((Date.now() - clu.pm2_env.pm_uptime) < (1000 || clu.pm2_env.min_uptime)) {
|
||||
// Increment unstable restart
|
||||
clu.pm2_env.unstable_restarts += 1;
|
||||
clu.pm2_env.unstable_restarts += 1;
|
||||
}
|
||||
|
||||
|
||||
if (clu.pm2_env.unstable_restarts >= 15) {
|
||||
// Too many unstable restart in less than 15 seconds
|
||||
// Set the process as "ERRORED"
|
||||
@ -80,19 +80,19 @@ function handleExit(clu, exit_code) {
|
||||
overlimit = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
God.bus.emit('process:exit', clu);
|
||||
|
||||
if (!stopping)
|
||||
clu.pm2_env.restart_time = clu.pm2_env.restart_time + 1;
|
||||
|
||||
|
||||
if (!stopping && !overlimit) executeApp(clu.pm2_env);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* For Node apps - Cluster mode
|
||||
* It will wrap the code and enable load-balancing mode
|
||||
* It will wrap the code and enable load-balancing mode
|
||||
*/
|
||||
|
||||
function nodeApp(pm2_env, cb){
|
||||
@ -107,7 +107,7 @@ function nodeApp(pm2_env, cb){
|
||||
try {
|
||||
clu = cluster.fork(pm2_env);
|
||||
} catch(e) { console.error(e); }
|
||||
|
||||
|
||||
// Receive message from child
|
||||
clu.on('message', function(msg) {
|
||||
switch (msg.type) {
|
||||
@ -121,12 +121,12 @@ function nodeApp(pm2_env, cb){
|
||||
|
||||
// Avoid circular dependency
|
||||
delete clu.process._handle.owner;
|
||||
|
||||
|
||||
clu.once('online', function() {
|
||||
if (cb) return cb(null, clu);
|
||||
return false;
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@ -137,10 +137,11 @@ function nodeApp(pm2_env, cb){
|
||||
function forkMode(pm2_env, cb) {
|
||||
log('Entering in fork mode');
|
||||
var spawn = require('child_process').spawn;
|
||||
|
||||
|
||||
var interpreter = pm2_env.exec_interpreter || 'node';
|
||||
|
||||
var script = [pm2_env.pm_exec_path];
|
||||
|
||||
|
||||
var out = fs.openSync(pm2_env.pm_out_log_path, 'a');
|
||||
var err = fs.openSync(pm2_env.pm_err_log_path, 'a');
|
||||
|
||||
@ -149,7 +150,7 @@ function forkMode(pm2_env, cb) {
|
||||
// Concat args if present
|
||||
if (pm2_env.args)
|
||||
script = script.concat(eval((pm2_env.args)));
|
||||
|
||||
|
||||
var cspr = spawn(interpreter, script, {
|
||||
env : pm2_env,
|
||||
cwd : pm2_env.pm_cwd || process.cwd(),
|
||||
@ -159,7 +160,7 @@ function forkMode(pm2_env, cb) {
|
||||
|
||||
cspr.unref();
|
||||
fs.writeFileSync(pidFile, cspr.pid);
|
||||
|
||||
|
||||
cspr.once('close', function(status) {
|
||||
fs.close(out);
|
||||
fs.close(err);
|
||||
@ -167,10 +168,10 @@ function forkMode(pm2_env, cb) {
|
||||
fs.unlinkSync(pidFile);
|
||||
}catch(e) {}
|
||||
});
|
||||
|
||||
|
||||
// Avoid circular dependency
|
||||
delete cspr._handle.owner;
|
||||
|
||||
|
||||
|
||||
|
||||
cspr.process = {};
|
||||
@ -179,7 +180,7 @@ function forkMode(pm2_env, cb) {
|
||||
|
||||
if (cb) return cb(null, cspr);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Forced entry to initialize cluster monitoring
|
||||
@ -187,8 +188,8 @@ function forkMode(pm2_env, cb) {
|
||||
|
||||
(function initEngine() {
|
||||
cluster.on('online', function(clu) {
|
||||
console.log("%s - id%d worker online", clu.pm2_env.pm_exec_path, clu.pm2_env.pm_id);
|
||||
clu.pm2_env.status = cst.ONLINE_STATUS;
|
||||
console.log("%s - id%d worker online", clu.pm2_env.pm_exec_path, clu.pm2_env.pm_id);
|
||||
clu.pm2_env.status = cst.ONLINE_STATUS;
|
||||
God.bus.emit('process:online', clu);
|
||||
});
|
||||
|
||||
@ -201,7 +202,7 @@ function forkMode(pm2_env, cb) {
|
||||
* Launch the specified script (present in env)
|
||||
*
|
||||
* @param {Mixed} env
|
||||
* @param {Function} cb
|
||||
* @param {Function} cb
|
||||
* @api private
|
||||
*/
|
||||
|
||||
@ -221,27 +222,27 @@ function executeApp(env, cb) {
|
||||
|
||||
if (!env.created_at)
|
||||
env['created_at'] = Date.now();
|
||||
|
||||
|
||||
env['pm_uptime'] = Date.now();
|
||||
env['status'] = 'launching';
|
||||
|
||||
|
||||
// Raw env copy
|
||||
var post_env = JSON.parse(JSON.stringify(env));
|
||||
|
||||
|
||||
util._extend(post_env, env.env);
|
||||
|
||||
|
||||
if (env['exec_mode'] == 'fork_mode') {
|
||||
// If fork mode enabled
|
||||
forkMode(post_env, function(err, clu) {
|
||||
clu['pm2_env'] = env;
|
||||
clu.pm2_env.status = cst.ONLINE_STATUS;
|
||||
God.clusters_db[env.pm_id] = clu;
|
||||
|
||||
|
||||
clu.once('error', function(err) {
|
||||
console.log(err);
|
||||
clu.pm2_env.status = cst.ERRORED_STATUS;
|
||||
});
|
||||
|
||||
|
||||
clu.once('close', function(code) {
|
||||
handleExit(clu, code);
|
||||
});
|
||||
@ -293,7 +294,7 @@ God.prepare = function(env, cb) {
|
||||
});
|
||||
})(env.instances);
|
||||
}
|
||||
else {
|
||||
else {
|
||||
return executeApp(env, function(err, dt) {
|
||||
cb(err, dt);
|
||||
});
|
||||
@ -360,7 +361,7 @@ God.getMonitorData = function(env, cb) {
|
||||
usage.lookup(pro.pid, { keepHistory : true }, function(err, res) {
|
||||
if (err)
|
||||
return cb(new Error('Looks like a process in on infinite loop, wait 10s to get more informations'));
|
||||
|
||||
|
||||
pro['monit'] = res;
|
||||
arr.push(pro);
|
||||
return ex(i - 1);
|
||||
@ -465,7 +466,7 @@ God.startProcessId = function(id, cb) {
|
||||
* Stop a process and set it on state "stopped"
|
||||
*/
|
||||
|
||||
God.stopProcessId = function(id, cb) {
|
||||
God.stopProcessId = function(id, cb) {
|
||||
if (!(id in God.clusters_db))
|
||||
return cb(new Error({msg : "PM ID unknown"}), {});
|
||||
God.clusters_db[id].pm2_env.status = 'stopping';
|
||||
@ -491,7 +492,7 @@ God.deleteProcessId = function(id, cb) {
|
||||
delete God.clusters_db[id];
|
||||
setTimeout(function() {
|
||||
cb(null, God.getFormatedProcesses());
|
||||
}, 200);
|
||||
}, 200);
|
||||
});
|
||||
};
|
||||
/**
|
||||
@ -522,13 +523,13 @@ God.restartProcessId = function(id, cb) {
|
||||
|
||||
God.restartProcessName = function(name, cb) {
|
||||
var arr = Object.keys(God.clusters_db);
|
||||
|
||||
|
||||
(function ex(arr) {
|
||||
if (arr[0] == null) return cb(null, God.getFormatedProcesses());
|
||||
|
||||
|
||||
var key = arr[0];
|
||||
var proc_env = God.clusters_db[key].pm2_env;
|
||||
|
||||
|
||||
if (p.basename(proc_env.pm_exec_path) == name || proc_env.name == name) {
|
||||
if (proc_env.status == cst.ONLINE_STATUS) {
|
||||
process.kill(God.clusters_db[key].process.pid);
|
||||
@ -549,7 +550,7 @@ God.restartProcessName = function(name, cb) {
|
||||
return ex(arr);
|
||||
}
|
||||
return false;
|
||||
})(arr);
|
||||
})(arr);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -559,7 +560,7 @@ God.restartProcessName = function(name, cb) {
|
||||
God.stopProcessName = function(name, cb) {
|
||||
var arr = Object.keys(God.clusters_db);
|
||||
var stopped_proc = 0;
|
||||
|
||||
|
||||
(function ex(arr) {
|
||||
if (arr[0] == null) {
|
||||
if (stopped_proc == 0)
|
||||
@ -592,7 +593,7 @@ God.stopProcessName = function(name, cb) {
|
||||
|
||||
God.deleteProcessName = function(name, cb) {
|
||||
var arr = Object.keys(God.clusters_db);
|
||||
|
||||
|
||||
(function ex(arr) {
|
||||
if (arr[0] == null) return cb(null, God.getFormatedProcesses());
|
||||
var key = arr[0];
|
||||
@ -620,7 +621,7 @@ God.deleteProcessName = function(name, cb) {
|
||||
|
||||
God.deleteAll = function(opts, cb) {
|
||||
var arr = Object.keys(God.clusters_db);
|
||||
|
||||
|
||||
(function ex(arr) {
|
||||
if (arr[0] == null) return cb(null, God.getFormatedProcesses());
|
||||
var key = arr[0];
|
||||
@ -640,7 +641,7 @@ God.deleteAll = function(opts, cb) {
|
||||
God.killMe = function(env, cb) {
|
||||
for (var id in God.clusters_db) {
|
||||
God.stopProcessId(id, function() {});
|
||||
};
|
||||
};
|
||||
setTimeout(function() {
|
||||
cb(null, {msg : 'pm2 killed'});
|
||||
process.exit(cst.SUCCESS_EXIT);
|
||||
|
||||
9
lib/interpreter.json
Normal file
9
lib/interpreter.json
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
".js": "node",
|
||||
".coffee": "coffee",
|
||||
".sh": "bash",
|
||||
".py": "python",
|
||||
".rb": "ruby",
|
||||
".php": "php",
|
||||
".go": "go"
|
||||
}
|
||||
@ -49,7 +49,6 @@ function ispec {
|
||||
success "$1"
|
||||
}
|
||||
|
||||
|
||||
function should {
|
||||
OUT=`$pm2 prettylist | grep -o "$2" | wc -l`
|
||||
[ $OUT -eq $3 ] || fail "$1"
|
||||
|
||||
3
test/fixtures/echo.coffee
vendored
Normal file
3
test/fixtures/echo.coffee
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
#!/usr/bin/env coffee
|
||||
|
||||
setInterval (-> console.log 'ok'), 500
|
||||
12
test/fork.sh
12
test/fork.sh
@ -49,13 +49,10 @@ function ispec {
|
||||
success "$1"
|
||||
}
|
||||
|
||||
|
||||
function should()
|
||||
{
|
||||
function should {
|
||||
OUT=`$pm2 prettylist | grep -o "$2" | wc -l`
|
||||
[ $OUT -eq $3 ] || fail "$1"
|
||||
success "$1"
|
||||
|
||||
}
|
||||
|
||||
cd $file_path
|
||||
@ -75,6 +72,13 @@ $pm2 kill
|
||||
$pm2 start bashscript.sh -x --interpreter bash
|
||||
should 'should has forked app' 'fork' 1
|
||||
|
||||
########### Auto Detective Interpreter In Fork mode
|
||||
|
||||
$pm2 kill
|
||||
|
||||
$pm2 start echo.coffee -x
|
||||
should 'should has forked app' 'fork' 1
|
||||
|
||||
### Dump resurect should be ok
|
||||
$pm2 dump
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user