diff --git a/lib/CLI.js b/lib/CLI.js index 2c17b7b3..7ee7e5a0 100644 --- a/lib/CLI.js +++ b/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); diff --git a/lib/God.js b/lib/God.js index 9e0e3663..b0829759 100644 --- a/lib/God.js +++ b/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); diff --git a/lib/interpreter.json b/lib/interpreter.json new file mode 100644 index 00000000..5a257b4a --- /dev/null +++ b/lib/interpreter.json @@ -0,0 +1,9 @@ +{ + ".js": "node", + ".coffee": "coffee", + ".sh": "bash", + ".py": "python", + ".rb": "ruby", + ".php": "php", + ".go": "go" +} \ No newline at end of file diff --git a/test/cli2.sh b/test/cli2.sh index ae3d25ec..54944ce3 100755 --- a/test/cli2.sh +++ b/test/cli2.sh @@ -49,7 +49,6 @@ function ispec { success "$1" } - function should { OUT=`$pm2 prettylist | grep -o "$2" | wc -l` [ $OUT -eq $3 ] || fail "$1" diff --git a/test/fixtures/echo.coffee b/test/fixtures/echo.coffee new file mode 100644 index 00000000..db3eaad6 --- /dev/null +++ b/test/fixtures/echo.coffee @@ -0,0 +1,3 @@ +#!/usr/bin/env coffee + +setInterval (-> console.log 'ok'), 500 \ No newline at end of file diff --git a/test/fork.sh b/test/fork.sh index edd3e67e..44032736 100644 --- a/test/fork.sh +++ b/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