From e898b07573dfcd4a31be8c0c51bca5ed61bc42ca Mon Sep 17 00:00:00 2001 From: tknew2 Date: Mon, 11 Aug 2014 15:21:59 +0200 Subject: [PATCH 01/69] memory leak fix --- examples/auto-save.js | 30 ------------------ examples/keymetrics-test.js | 24 ++++++++------ lib/CLI.js | 4 +++ lib/God.js | 63 +++++++++++++++++++------------------ lib/God/ClusterMode.js | 8 ++--- lib/God/ForkMode.js | 7 ++--- 6 files changed, 55 insertions(+), 81 deletions(-) diff --git a/examples/auto-save.js b/examples/auto-save.js index 79de09f5..e69de29b 100644 --- a/examples/auto-save.js +++ b/examples/auto-save.js @@ -1,30 +0,0 @@ - - -// Expose action -// And "touch" file every 1.4s to restart the file - -var axm = require('axm'); - -function makeid() { - var text = ""; - var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; - - for( var i=0; i < 5; i++ ) - text += possible.charAt(Math.floor(Math.random() * possible.length)); - - return text; -} - - -axm.action('cmd:' + makeid(), {comment : 'Refresh main database'}, function(reply) { - console.log('Refreshing'); - reply({success : true}); - }); - -setTimeout(function() { - var fs = require('fs'); - - - var a = fs.readFileSync(__filename); - fs.writeFileSync(__filename, a); -}, 1400); diff --git a/examples/keymetrics-test.js b/examples/keymetrics-test.js index d5e623cf..c5244dcd 100644 --- a/examples/keymetrics-test.js +++ b/examples/keymetrics-test.js @@ -4,20 +4,24 @@ var pm2 = require('..'); pm2.connect(function() { pm2.delete('all', function() { pm2.start('examples/human_event.js', function() { - pm2.start('examples/child.js', {instances:2},function() { - pm2.start('examples/custom_action.js', function() { - pm2.start('examples/custom_action_with_params.js', function() { - pm2.start('examples/auto-save.js', {watch : true, name :'auto-save-modify'}, function() { - pm2.start('examples/http-trace.js', {name:'trace'}, function() { - //pm2.start('examples/auto-bench.js', {instances : 'max'}, function() { - pm2.start('examples/throw.js', {name:'auto-throw'}, function() { - pm2.disconnect(function() { process.exit(1); }); + pm2.start('examples/child.js', {instances:2},function() { + pm2.start('examples/custom_action.js', function() { + pm2.start('examples/custom_action.js', {execMode : 'fork', force : true}, function() { + pm2.start('examples/auto-save.js', {execMode : 'fork', watch:true, force : true}, function() { + pm2.start('examples/custom_action_with_params.js', function() { + pm2.start('examples/auto-save.js', {watch : true,force:true, name :'auto-save-modify'}, function() { + pm2.start('examples/http-trace.js', {name:'trace'}, function() { + //pm2.start('examples/auto-bench.js', {instances : 'max'}, function() { + pm2.start('examples/throw.js', {name:'auto-throw'}, function() { + pm2.disconnect(function() { process.exit(1); }); + }); + //}); }); - //}); + }); + }); }); }); }); - }); }); }); }); diff --git a/lib/CLI.js b/lib/CLI.js index 15ef31cc..7b15901d 100644 --- a/lib/CLI.js +++ b/lib/CLI.js @@ -89,6 +89,10 @@ CLI.start = function(script, opts, cb) { appConf['exec_interpreter'] = 'node'; } + if (opts.execMode) { + appConf['exec_mode'] = opts.execMode; + } + // if (appConf['exec_mode'] == 'cluster_mode' && process.version.match(/0.10/)) { // printOut(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.'); // } diff --git a/lib/God.js b/lib/God.js index c1ac5c41..0ec55afd 100644 --- a/lib/God.js +++ b/lib/God.js @@ -1,8 +1,3 @@ -'use strict'; - -/** - * Module dependencies - */ var cluster = require('cluster'); var numCPUs = require('os').cpus() ? require('os').cpus().length : 1; @@ -30,8 +25,7 @@ var God = module.exports = { bus : new EventEmitter2({ wildcard: true, delimiter: ':', - newListener: false, - maxListeners: 20 + maxListeners: 1000 }) }; @@ -44,22 +38,6 @@ require('./God/ClusterMode.js')(God); require('./God/Reload')(God); require('./God/ActionMethods')(God); -/** - * Forced entry to initialize cluster monitoring - */ - -(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; - God.bus.emit('process:online', { process : clu }); - }); - - cluster.on('exit', function(clu, code, signal) { - handleExit(clu, code); - }); -})(); - /** * Handle logic when a process exit (Node or Fork) * @method handleExit @@ -136,8 +114,6 @@ function handleExit(clu, exit_code) { God.executeApp = function(env, cb) { var env_copy = JSON.parse(JSON.stringify(env)); - util._extend(env_copy, env.env); - env_copy['axm_actions'] = []; if (env_copy['pm_id'] === undefined) { @@ -179,17 +155,21 @@ God.executeApp = function(env, cb) { God.forkMode(env_copy, function(err, clu) { if (cb && err) return cb(err); - God.clusters_db[env_copy.pm_id] = clu; + var old_env = God.clusters_db[clu.pm2_env.pm_id]; + if (old_env) old_env = null; + + var proc = God.clusters_db[env_copy.pm_id] = clu; clu.once('error', function(err) { - clu.pm2_env.status = cst.ERRORED_STATUS; + proc.pm2_env.status = cst.ERRORED_STATUS; }); clu.once('close', function(code) { - handleExit(clu, code); + proc.removeAllListeners(); + return handleExit(proc, code); }); - God.bus.emit('process:online', {process : clu }); + God.bus.emit('process:online', {process : proc}); if (cb) cb(null, clu); return false; @@ -202,8 +182,29 @@ God.executeApp = function(env, cb) { God.nodeApp(env_copy, function(err, clu) { if (cb && err) return cb(err); if (err) return false; - God.clusters_db[clu.pm2_env.pm_id] = clu; - if (cb) cb(null, clu); + + var old_env = God.clusters_db[clu.pm2_env.pm_id]; + if (old_env) old_env = null; + + var proc = God.clusters_db[clu.pm2_env.pm_id] = clu; + + clu.once('online', function() { + proc.pm2_env.status = cst.ONLINE_STATUS; + + console.log('%s - id%d worker online', proc.pm2_env.pm_exec_path, proc.pm2_env.pm_id); + + God.bus.emit('process:online', { process : proc }); + + if (cb) return cb(null, proc); + return false; + }); + + clu.once('exit', function(exited_clu, code) { + proc.removeAllListeners(); + return handleExit(proc, code); + }); + + if (cb) return cb(null, proc); return false; }); } diff --git a/lib/God/ClusterMode.js b/lib/God/ClusterMode.js index ec133d5a..2986cd4a 100644 --- a/lib/God/ClusterMode.js +++ b/lib/God/ClusterMode.js @@ -69,13 +69,9 @@ module.exports = function(God) { }); // Avoid circular dependency - delete clu.process._handle.owner; + // delete clu.process._handle.owner; - clu.once('online', function() { - clu.pm2_env.status = cst.ONLINE_STATUS; - if (cb) return cb(null, clu); - return false; - }); + return cb(null, clu); return false; }; }; diff --git a/lib/God/ForkMode.js b/lib/God/ForkMode.js index 30188b49..95d162dc 100644 --- a/lib/God/ForkMode.js +++ b/lib/God/ForkMode.js @@ -100,7 +100,7 @@ module.exports = function(God) { stdio : ['ipc', null, null] }); } catch(e) { - console.error(e.stack || e); + God.logAndGenerateError(e); if (cb) return cb(e); } @@ -124,7 +124,7 @@ module.exports = function(God) { }); cspr.stdout.on('data', function(data) { - + var log_data = data.toString(); if (pm2_env.log_date_format) log_data = moment().format(pm2_env.log_date_format) + ': ' + log_data; @@ -150,12 +150,11 @@ module.exports = function(God) { try { stderr.close(); stdout.close(); - } catch(e) { console.error(e.stack || e);} + } catch(e) { God.logAndGenerateError(e);} }); cspr._reloadLogs = startLogging; - cspr.unref(); if (cb) return cb(null, cspr); From 05c9afd3457ba3993bfc57794c7a54221631e73a Mon Sep 17 00:00:00 2001 From: tknew2 Date: Mon, 11 Aug 2014 15:36:38 +0200 Subject: [PATCH 02/69] Fix cwd --- lib/Common.js | 1 + lib/God/ClusterMode.js | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Common.js b/lib/Common.js index 0a61bc20..322b6b32 100644 --- a/lib/Common.js +++ b/lib/Common.js @@ -43,6 +43,7 @@ Common.resolveAppPaths = function(app, cwd, outputter) { util._extend(app.env, env); app.env.pm_cwd = cwd; + app.pm_cwd = cwd; if (!app.exec_interpreter) { if (extItps[path.extname(app.script)]) { diff --git a/lib/God/ClusterMode.js b/lib/God/ClusterMode.js index 2986cd4a..05d8dc07 100644 --- a/lib/God/ClusterMode.js +++ b/lib/God/ClusterMode.js @@ -46,7 +46,6 @@ module.exports = function(God) { } catch(e) { return God.logAndGenerateError(e); } clu.pm2_env = env_copy; - God.clusters_db[env_copy.pm_id] = clu; // Receive message from child clu.on('message', function(msg) { From 797fd474438c435d9b387c8f3f994a39098e6e08 Mon Sep 17 00:00:00 2001 From: tknew2 Date: Mon, 11 Aug 2014 16:03:09 +0200 Subject: [PATCH 03/69] Clean - add environment merge --- examples/echo.js | 0 lib/CLI.js | 13 +++-- lib/Common.js | 6 ++ lib/God.js | 2 + lib/God/ForkMode.js | 136 +++++++++++++++++++++---------------------- lib/HttpInterface.js | 14 ----- 6 files changed, 84 insertions(+), 87 deletions(-) mode change 100644 => 100755 examples/echo.js diff --git a/examples/echo.js b/examples/echo.js old mode 100644 new mode 100755 diff --git a/lib/CLI.js b/lib/CLI.js index 7b15901d..9bac2298 100644 --- a/lib/CLI.js +++ b/lib/CLI.js @@ -24,6 +24,8 @@ var exitCli = Common.exitCli; var printError = Common.printError; var printOut = Common.printOut; +var pm2 = require('..'); + /** * Method to start a script * @method startFile @@ -636,11 +638,12 @@ CLI.dump = function(cb) { * @return */ CLI.web = function(cb) { - Satan.executeRemote('prepare', resolvePaths({ - script : p.resolve(p.dirname(module.filename), './HttpInterface.js'), - name : 'Pm2Http' + cst.WEB_INTERFACE, - exec_mode : 'fork_mode' - }), function(err, proc) { + var filepath = p.resolve(p.dirname(module.filename), 'HttpInterface.js'); + + pm2.start(filepath, { + name : 'pm2-http-interface', + execMode : 'fork_mode' + }, function(err, proc) { if (err) { printError(cst.PREFIX_MSG_ERR + 'Error while launching application', err.stack || err); return cb ? cb({msg:err}) : speedList(); diff --git a/lib/Common.js b/lib/Common.js index 322b6b32..823a0507 100644 --- a/lib/Common.js +++ b/lib/Common.js @@ -11,6 +11,8 @@ var cst = require('../constants.js'); var extItps = require('./interpreter.json'); var p = path; +var Stringify = require('json-stringify-safe'); + var Satan = require('./Satan.js'); /** * Common methods (used by CLI and God) @@ -104,6 +106,10 @@ Common.resolveAppPaths = function(app, cwd, outputter) { return app; }; +Common.deepCopy = function(data) { + return JSON.parse(Stringify(data)); +}; + /** * Description * @method validateApp diff --git a/lib/God.js b/lib/God.js index 0ec55afd..ee08417f 100644 --- a/lib/God.js +++ b/lib/God.js @@ -114,6 +114,8 @@ function handleExit(clu, exit_code) { God.executeApp = function(env, cb) { var env_copy = JSON.parse(JSON.stringify(env)); + util._extend(env_copy, env.env); + env_copy['axm_actions'] = []; if (env_copy['pm_id'] === undefined) { diff --git a/lib/God/ForkMode.js b/lib/God/ForkMode.js index 95d162dc..e9b97d2d 100644 --- a/lib/God/ForkMode.js +++ b/lib/God/ForkMode.js @@ -11,7 +11,7 @@ var fs = require('fs'); var cst = require('../../constants.js'); var uidNumber = require('uid-number'); var moment = require('moment'); - +var Common = require('../Common'); /** * Description * @method exports @@ -85,82 +85,82 @@ module.exports = function(God) { startLogging(function() { getugid(function(e, uid, gid){ - if(e){ - console.error(e.stack || e); - if (cb) return cb(e); - } + if(e){ + God.logAndGenerateError(e); + if (cb) return cb(e); + } - try { - var cspr = spawn(command, args, { - env : pm2_env, - detached : true, - gid : gid, - uid : uid, - cwd : pm2_env.pm_cwd || process.cwd(), - stdio : ['ipc', null, null] - }); - } catch(e) { - God.logAndGenerateError(e); - if (cb) return cb(e); - } - - cspr.process = {}; - cspr.process.pid = cspr.pid; - cspr.pm2_env = pm2_env; - cspr.pm2_env.status = cst.ONLINE_STATUS; - - cspr.stderr.on('data', function(data) { - - var log_data = data.toString(); - if (pm2_env.log_date_format) - log_data = moment().format(pm2_env.log_date_format) + ': ' + log_data; - - stderr.write(log_data); - - God.bus.emit('log:err', { - process : cspr, - data : data.toString() - }); + try { + var cspr = spawn(command, args, { + env : pm2_env, + detached : true, + gid : gid, + uid : uid, + cwd : pm2_env.pm_cwd || process.cwd(), + stdio : ['ipc', null, null] }); + } catch(e) { + God.logAndGenerateError(e); + if (cb) return cb(e); + } - cspr.stdout.on('data', function(data) { + cspr.process = {}; + cspr.process.pid = cspr.pid; + cspr.pm2_env = pm2_env; + cspr.pm2_env.status = cst.ONLINE_STATUS; - var log_data = data.toString(); - if (pm2_env.log_date_format) - log_data = moment().format(pm2_env.log_date_format) + ': ' + log_data; + cspr.stderr.on('data', function(data) { - stdout.write(log_data); + var log_data = data.toString(); + if (pm2_env.log_date_format) + log_data = moment().format(pm2_env.log_date_format) + ': ' + log_data; - God.bus.emit('log:out', { - process : cspr, - data : data.toString() - }); + stderr.write(log_data); + + God.bus.emit('log:err', { + process : cspr, + data : data.toString() }); - - cspr.on('message', function(data) { - God.bus.emit(data.type ? data.type : 'process:msg', { - process : cspr, - data : data - }); - }); - - fs.writeFileSync(pidFile, cspr.pid); - - cspr.once('close', function(status) { - try { - stderr.close(); - stdout.close(); - } catch(e) { God.logAndGenerateError(e);} - }); - - cspr._reloadLogs = startLogging; - - cspr.unref(); - - if (cb) return cb(null, cspr); - return false; }); + + cspr.stdout.on('data', function(data) { + + var log_data = data.toString(); + if (pm2_env.log_date_format) + log_data = moment().format(pm2_env.log_date_format) + ': ' + log_data; + + stdout.write(log_data); + + God.bus.emit('log:out', { + process : cspr, + data : data.toString() + }); + }); + + cspr.on('message', function(data) { + God.bus.emit(data.type ? data.type : 'process:msg', { + process : cspr, + data : data + }); + }); + + fs.writeFileSync(pidFile, cspr.pid); + + cspr.once('close', function(status) { + try { + stderr.close(); + stdout.close(); + } catch(e) { God.logAndGenerateError(e);} + }); + + cspr._reloadLogs = startLogging; + + cspr.unref(); + + if (cb) return cb(null, cspr); + return false; }); + }); }; }; diff --git a/lib/HttpInterface.js b/lib/HttpInterface.js index 77c65aaa..b147769b 100644 --- a/lib/HttpInterface.js +++ b/lib/HttpInterface.js @@ -59,17 +59,3 @@ http.createServer(function (req, res) { return res.end(); } }).listen(cst.WEB_INTERFACE); - - -// var MicroDB = require("nodejs-microdb"); - -// var fdb = new MicroDB({ -// "file" : p.join(cst.DEFAULT_FILE_PATH, "monit.db") -// }); - -// setInterval(function() { -// Satan.executeRemote("list", {}, function(err, data_proc) { -// console.log('adding'); -// fdb.add(data_proc); -// }); -// }, 1000); From 4f78a6bde06fec65823aa3b2dafb37c8170dd73d Mon Sep 17 00:00:00 2001 From: tknew2 Date: Mon, 11 Aug 2014 16:26:58 +0200 Subject: [PATCH 04/69] Fix callback --- lib/God.js | 2 +- test/bash/cli.sh | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/God.js b/lib/God.js index ee08417f..8612e543 100644 --- a/lib/God.js +++ b/lib/God.js @@ -206,7 +206,6 @@ God.executeApp = function(env, cb) { return handleExit(proc, code); }); - if (cb) return cb(null, proc); return false; }); } @@ -236,6 +235,7 @@ God.prepare = function(env, cb) { if (cb != null) return cb(null, arr); return false; } + return God.executeApp(JSON.parse(JSON.stringify(env)), function(err, clu) { // deep copy if (err) return ex(i - 1); arr.push(clu); diff --git a/test/bash/cli.sh b/test/bash/cli.sh index e92e6ff7..ceb0eb3f 100755 --- a/test/bash/cli.sh +++ b/test/bash/cli.sh @@ -153,8 +153,8 @@ sleep 0.3 $http_get -q http://localhost:9615/ -O $JSON_FILE OUT=`cat $JSON_FILE | grep -o "restart_time\":1" | wc -l` -[ $OUT -eq 7 ] || fail "$1" -success "$1" +[ $OUT -eq 7 ] || fail "Error while wgeting data via web interface" +success "Got data from interface" $pm2 list From 793660bfe0d854b5e7b1a5061c3894086e92e541 Mon Sep 17 00:00:00 2001 From: tknew2 Date: Mon, 11 Aug 2014 16:48:18 +0200 Subject: [PATCH 05/69] serialize data --- examples/keymetrics-load.js | 24 ++++++++++++++++++++++++ examples/kill-not-so-fast.js | 4 ++++ lib/Common.js | 2 +- lib/God.js | 15 ++++++++++----- lib/God/ClusterMode.js | 19 ++++++++++++------- package.json | 2 +- 6 files changed, 52 insertions(+), 14 deletions(-) create mode 100644 examples/keymetrics-load.js create mode 100644 examples/kill-not-so-fast.js diff --git a/examples/keymetrics-load.js b/examples/keymetrics-load.js new file mode 100644 index 00000000..719bd96c --- /dev/null +++ b/examples/keymetrics-load.js @@ -0,0 +1,24 @@ + + +var pm2 = require('..'); + +pm2.connect(function() { + pm2.delete('all', function() { + pm2.start('examples/human_event.js', function() { + pm2.start('examples/child.js', {instances:2},function() { + pm2.start('examples/kill-not-so-fast.js', {instances:10}, function() { + pm2.start('examples/auto-save.js', {execMode : 'fork', watch:true, force : true}, function() { + pm2.start('examples/custom_action_with_params.js', function() { + //pm2.start('examples/auto-bench.js', {instances : 'max'}, function() { + pm2.start('examples/throw.js', {name:'auto-throw'}, function() { + pm2.disconnect(function() { process.exit(1); }); + }); + }); + + }); + }); + }); + + }); + }); +}); diff --git a/examples/kill-not-so-fast.js b/examples/kill-not-so-fast.js new file mode 100644 index 00000000..036c8e71 --- /dev/null +++ b/examples/kill-not-so-fast.js @@ -0,0 +1,4 @@ + +setTimeout(function() { + process.exit(1); +}, 1050); diff --git a/lib/Common.js b/lib/Common.js index 823a0507..b9af74d4 100644 --- a/lib/Common.js +++ b/lib/Common.js @@ -106,7 +106,7 @@ Common.resolveAppPaths = function(app, cwd, outputter) { return app; }; -Common.deepCopy = function(data) { +Common.deepCopy = Common.serialize = function(data) { return JSON.parse(Stringify(data)); }; diff --git a/lib/God.js b/lib/God.js index 8612e543..b2e10f64 100644 --- a/lib/God.js +++ b/lib/God.js @@ -94,7 +94,7 @@ function handleExit(clu, exit_code) { } } - God.bus.emit('process:exit', { process : clu }); + God.bus.emit('process:exit', { process : Common.serialize(clu) }); if (!stopping) clu.pm2_env.restart_time = clu.pm2_env.restart_time + 1; @@ -171,7 +171,8 @@ God.executeApp = function(env, cb) { return handleExit(proc, code); }); - God.bus.emit('process:online', {process : proc}); + God.bus.emit('process:online', {process : Common.serialize(proc)}); + if (cb) cb(null, clu); return false; @@ -186,7 +187,11 @@ God.executeApp = function(env, cb) { if (err) return false; var old_env = God.clusters_db[clu.pm2_env.pm_id]; - if (old_env) old_env = null; + + if (old_env) { + old_env = null; + God.clusters_db[clu.pm2_env.pm_id] = null; + } var proc = God.clusters_db[clu.pm2_env.pm_id] = clu; @@ -195,9 +200,9 @@ God.executeApp = function(env, cb) { console.log('%s - id%d worker online', proc.pm2_env.pm_exec_path, proc.pm2_env.pm_id); - God.bus.emit('process:online', { process : proc }); + God.bus.emit('process:online', { process : Common.serialize(proc) }); - if (cb) return cb(null, proc); + if (cb) return cb(null, Common.serialize(proc)); return false; }); diff --git a/lib/God/ClusterMode.js b/lib/God/ClusterMode.js index 05d8dc07..dc93d38c 100644 --- a/lib/God/ClusterMode.js +++ b/lib/God/ClusterMode.js @@ -10,7 +10,7 @@ var cluster = require('cluster'); var fs = require('fs'); var cst = require('../../constants.js'); var util = require('util'); - +var Common = require('../Common'); /** * Description * @method exports @@ -43,27 +43,32 @@ module.exports = function(God) { try { clu = cluster.fork(env_copy); - } catch(e) { return God.logAndGenerateError(e); } + } catch(e) { + God.logAndGenerateError(e); + return cb(e); + } clu.pm2_env = env_copy; // Receive message from child clu.on('message', function(msg) { + var proc_data = Common.serialize(clu); + switch (msg.type) { case 'process:exception': - God.bus.emit('process:exception', {process : clu, data : msg, err : msg.err}); + God.bus.emit('process:exception', {process : proc_data, data : msg, err : msg.err}); break; case 'log:out': - God.bus.emit('log:out', {process : clu, data : msg.data}); + God.bus.emit('log:out', {process : proc_data, data : msg.data}); break; case 'log:err': - God.bus.emit('log:err', {process : clu, data : msg.data}); + God.bus.emit('log:err', {process : proc_data, data : msg.data}); break; case 'human_event': - God.bus.emit('human_event', {process : clu, data : util._extend(msg, {type:msg.name})}); + God.bus.emit('human_event', {process : proc_data, data : util._extend(msg, {type:msg.name})}); break; default: // Permits to send message to external from the app - God.bus.emit(msg.type ? msg.type : 'process:msg', {process : clu, data : msg }); + God.bus.emit(msg.type ? msg.type : 'process:msg', {process : proc_data, data : msg }); } }); diff --git a/package.json b/package.json index a205ed57..1d07be6d 100644 --- a/package.json +++ b/package.json @@ -117,7 +117,7 @@ "json-stringify-safe": "~5.0.0", "nssocket": "~0.5.1", "punt" : "~2.2.0", - "pidusage": "~0.0.6", + "pidusage": "~0.0.7", "pm2-axon-rpc": "~0.0.2", "pm2-deploy": "~0.0.4", "pm2-interface": "~0.1.3", From 5c5af28864d64be8e4127bb239fcad90e5f5002b Mon Sep 17 00:00:00 2001 From: tknew2 Date: Mon, 11 Aug 2014 17:10:08 +0200 Subject: [PATCH 06/69] less circular transfer --- lib/God.js | 4 ++-- lib/God/ActionMethods.js | 1 - lib/God/ClusterMode.js | 3 +-- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/lib/God.js b/lib/God.js index b2e10f64..7b942be7 100644 --- a/lib/God.js +++ b/lib/God.js @@ -202,13 +202,13 @@ God.executeApp = function(env, cb) { God.bus.emit('process:online', { process : Common.serialize(proc) }); - if (cb) return cb(null, Common.serialize(proc)); + if (cb) return cb(null, proc); return false; }); clu.once('exit', function(exited_clu, code) { proc.removeAllListeners(); - return handleExit(proc, code); + return handleExit(Common.serialize(proc), code); }); return false; diff --git a/lib/God/ActionMethods.js b/lib/God/ActionMethods.js index 95b37380..14b598c9 100644 --- a/lib/God/ActionMethods.js +++ b/lib/God/ActionMethods.js @@ -356,7 +356,6 @@ module.exports = function(God) { try { process.kill(God.clusters_db[id].process.pid, signal); } catch(e) { - console.error(e); return cb(God.logAndGenerateError('Error when sending signal (signal unknown)'), {}); } return cb(null, God.getFormatedProcesses()); diff --git a/lib/God/ClusterMode.js b/lib/God/ClusterMode.js index dc93d38c..9b56bd33 100644 --- a/lib/God/ClusterMode.js +++ b/lib/God/ClusterMode.js @@ -31,7 +31,6 @@ module.exports = function(God) { var clu; if (fs.existsSync(env_copy.pm_exec_path) == false) { - console.error('Script ' + env_copy.pm_exec_path + ' missing'); return cb(God.logAndGenerateError('Script ' + env_copy.pm_exec_path + ' missing'), {}); } @@ -73,7 +72,7 @@ module.exports = function(God) { }); // Avoid circular dependency - // delete clu.process._handle.owner; + delete clu.process._handle.owner; return cb(null, clu); return false; From 6ad9846de03fa78388064248a8b2011e6898db37 Mon Sep 17 00:00:00 2001 From: Ulflander Date: Mon, 11 Aug 2014 17:43:26 +0200 Subject: [PATCH 07/69] Avoid using console.log Satan in launchDaemon function (breaks jlist command if daemon is not started) --- lib/Satan.js | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/Satan.js b/lib/Satan.js index 236f88a5..19c828a9 100644 --- a/lib/Satan.js +++ b/lib/Satan.js @@ -282,7 +282,6 @@ Satan.launchDaemon = function(cb) { child.once('message', function(msg) { process.emit('satan:daemon:ready'); - console.log(msg); return setTimeout(function() {cb(null, child)}, 150); }); }; From 916328c52830105a17ccdad2a21e24d488cc8881 Mon Sep 17 00:00:00 2001 From: tknew2 Date: Mon, 11 Aug 2014 18:24:37 +0200 Subject: [PATCH 08/69] pm2 reset metadata method - code hardening --- bin/pm2 | 6 +++++ lib/CLI.js | 42 +++++++++++++++++++++++++++++++- lib/Common.js | 37 ++++++++++++++++++++++++++++ lib/God.js | 52 +++++++++++++++++++++------------------- lib/God/ActionMethods.js | 20 +++++++++++++++- lib/God/Methods.js | 6 ++--- lib/Interactor/Tools.js | 8 +++++++ lib/Satan.js | 1 + test/bash/cli.sh | 2 ++ test/bash/reset.sh | 46 +++++++++++++++++++++++++++++++++++ test/main.sh | 2 ++ 11 files changed, 193 insertions(+), 29 deletions(-) create mode 100644 lib/Interactor/Tools.js create mode 100644 test/bash/reset.sh diff --git a/bin/pm2 b/bin/pm2 index bd97b6fb..c5e847b1 100755 --- a/bin/pm2 +++ b/bin/pm2 @@ -323,6 +323,12 @@ commander.command('ecosystem') CLI.generateSample(name); }); +commander.command('reset ') + .description('reset counters for process') + .action(function(proc_id) { + CLI.resetMetaProcess(proc_id); + }); + commander.command('describe ') .description('describe all parameters of a process id') .action(function(proc_id) { diff --git a/lib/CLI.js b/lib/CLI.js index 9bac2298..f9527260 100644 --- a/lib/CLI.js +++ b/lib/CLI.js @@ -531,6 +531,46 @@ CLI.ping = function(cb) { }); }; +/** + * Reset meta data + * @method resetMetaProcess + */ +CLI.resetMetaProcess = function(process_name, cb) { + function processIds(ids, cb) { + async.eachLimit(ids, 4, function(id, next) { + Satan.executeRemote('resetMetaProcessId', id, function(err, res) { + if (err) console.error(err); + printOut('Reseting meta for process id %d', id); + return next(); + }); + }, function(err) { + if (err) return cb(new Error(err)); + return cb ? cb(null, {success:true}) : speedList(); + }); + }; + + if (process_name == 'all') { + Common.getAllProcessId(function(err, ids) { + if (err) { + printError(err); + return cb ? cb({msg:err}) : exitCli(cst.ERROR_EXIT); + } + return processIds(ids, cb); + }); + } + else if (isNaN(parseInt(process_name))) { + Common.getProcessIdByName(process_name, function(err, ids) { + if (err) { + printError(err); + return cb ? cb({msg:err}) : exitCli(cst.ERROR_EXIT); + } + return processIds(ids, cb); + }); + } else { + processIds([process_name], cb); + } +}; + /** * Resurrect processes * @method resurrect @@ -868,7 +908,7 @@ CLI.restart = function(process_name, cb) { else if (process_name == 'all') CLI._restartAll(cb); else if (isNaN(parseInt(process_name))) { - printError('Restarting process by name ' + process_name); + printOut('Restarting process by name ' + process_name); CLI._restartProcessByName(process_name, cb); } else { printOut('Restarting process by id ' + process_name); diff --git a/lib/Common.js b/lib/Common.js index b9af74d4..fd65047d 100644 --- a/lib/Common.js +++ b/lib/Common.js @@ -176,3 +176,40 @@ Common.printOut = function() { if (process.env.PM2_SILENT) return false; return console.log.apply(console, arguments); }; + + +Common.getAllProcessId = function(cb) { + var found_proc = []; + + Satan.executeRemote('getMonitorData', {}, function(err, list) { + if (err) { + Common.printError('Error retrieving process list: ' + err); + return cb(err); + } + + list.forEach(function(proc) { + found_proc.push(proc.pm_id); + }); + + return cb(null, found_proc); + }); +}; + +Common.getProcessIdByName = function(name, cb) { + var found_proc = []; + + Satan.executeRemote('getMonitorData', {}, function(err, list) { + if (err) { + Common.printError('Error retrieving process list: ' + err); + return cb(err); + } + + list.forEach(function(proc) { + if (typeof(name) === 'string' && proc.name == name) { + found_proc.push(proc.pm_id); + } + }); + + return cb(null, found_proc); + }); +}; diff --git a/lib/God.js b/lib/God.js index 7b942be7..f634b677 100644 --- a/lib/God.js +++ b/lib/God.js @@ -46,19 +46,21 @@ require('./God/ActionMethods')(God); * @return */ function handleExit(clu, exit_code) { - console.log('Script %s %s exited code %d', clu.pm2_env.pm_exec_path, clu.pm2_env.pm_id, exit_code); + console.log('Script %s %s exit', clu.pm2_env.pm_exec_path, clu.pm2_env.pm_id); - var stopping = (clu.pm2_env.status == cst.STOPPING_STATUS || clu.pm2_env.status == cst.ERRORED_STATUS) ? true : false; + var proc = God.clusters_db[clu.pm2_env.pm_id]; + + var stopping = (proc.pm2_env.status == cst.STOPPING_STATUS || proc.pm2_env.status == cst.ERRORED_STATUS) ? true : false; var overlimit = false; - var pidFile = clu.pm2_env.pm_pid_path; + var pidFile = proc.pm2_env.pm_pid_path; - if (stopping) clu.process.pid = 0; + if (stopping) proc.process.pid = 0; - if (clu.pm2_env.axm_actions) clu.pm2_env.axm_actions = []; + if (proc.pm2_env.axm_actions) proc.pm2_env.axm_actions = []; - if (clu.pm2_env.status != cst.ERRORED_STATUS && - clu.pm2_env.status != cst.STOPPING_STATUS) - clu.pm2_env.status = cst.STOPPED_STATUS; + if (proc.pm2_env.status != cst.ERRORED_STATUS && + proc.pm2_env.status != cst.STOPPING_STATUS) + proc.pm2_env.status = cst.STOPPED_STATUS; try { fs.unlinkSync(pidFile); @@ -70,36 +72,38 @@ function handleExit(clu, exit_code) { // If the process has been created less than 15seconds ago // And if the process has an uptime less than a second - var min_uptime = (clu.pm2_env.min_uptime || 1000); - var max_restarts = (clu.pm2_env.max_restarts || 15); + var min_uptime = (proc.pm2_env.min_uptime || 1000); + var max_restarts = (proc.pm2_env.max_restarts || 15); - if ((Date.now() - clu.pm2_env.created_at) < (min_uptime * max_restarts)) { - if ((Date.now() - clu.pm2_env.pm_uptime) < min_uptime) { + if ((Date.now() - proc.pm2_env.created_at) < (min_uptime * max_restarts)) { + if ((Date.now() - proc.pm2_env.pm_uptime) < min_uptime) { // Increment unstable restart - clu.pm2_env.unstable_restarts += 1; + proc.pm2_env.unstable_restarts += 1; } - if (clu.pm2_env.unstable_restarts >= max_restarts) { + if (proc.pm2_env.unstable_restarts >= max_restarts) { // Too many unstable restart in less than 15 seconds // Set the process as 'ERRORED' // And stop to restart it - clu.pm2_env.status = cst.ERRORED_STATUS; - console.log('Script %s had too many unstable restarts (%d). Stopped.', - clu.pm2_env.pm_exec_path, - clu.pm2_env.unstable_restarts); - God.bus.emit('process:exit:overlimit', { process : clu }); - clu.pm2_env.unstable_restarts = 0; - clu.pm2_env.created_at = null; + proc.pm2_env.status = cst.ERRORED_STATUS; + + console.log('Script %s had too many unstable restarts (%d). Stopped. %j', + proc.pm2_env.pm_exec_path, + proc.pm2_env.unstable_restarts, + proc.pm2_env.status); + God.bus.emit('process:exit:overlimit', { process : proc }); + proc.pm2_env.unstable_restarts = 0; + proc.pm2_env.created_at = null; overlimit = true; } } - God.bus.emit('process:exit', { process : Common.serialize(clu) }); + God.bus.emit('process:exit', { process : proc }); if (!stopping) - clu.pm2_env.restart_time = clu.pm2_env.restart_time + 1; + proc.pm2_env.restart_time = proc.pm2_env.restart_time + 1; - if (!stopping && !overlimit) God.executeApp(clu.pm2_env); + if (!stopping && !overlimit) God.executeApp(proc.pm2_env); } diff --git a/lib/God/ActionMethods.js b/lib/God/ActionMethods.js index 14b598c9..909ad894 100644 --- a/lib/God/ActionMethods.js +++ b/lib/God/ActionMethods.js @@ -42,8 +42,10 @@ module.exports = function(God) { try { pidusage(pro.pid, function(err, res) { - if (err) + if (err) { + God.logAndGenerateError(err); return next(err); + } pro['monit'] = { memory : Math.floor(res.memory), @@ -237,6 +239,22 @@ module.exports = function(God) { return false; }; + God.resetMetaProcessId = function(id, cb) { + if (!(id in God.clusters_db)) + return cb(God.logAndGenerateError(id + ' id unknown'), {}); + + var proc = God.clusters_db[id]; + + if (!proc || !proc.pm2_env) + return cb(God.logAndGenerateError('Error when getting proc || proc.pm2_env'), {}); + + proc.pm2_env.created_at = Date.now(); + proc.pm2_env.unstable_restarts = 0; + proc.pm2_env.restart_time = 0; + + return cb(null, proc); + }; + /** * Delete a process by id * It will stop it and remove it from the database diff --git a/lib/God/Methods.js b/lib/God/Methods.js index ca42d365..9bb6cad4 100644 --- a/lib/God/Methods.js +++ b/lib/God/Methods.js @@ -6,7 +6,7 @@ * @project PM2 */ var p = require('path'); - +var Common = require('../Common'); /** * Description * @method exports @@ -47,7 +47,7 @@ module.exports = function(God) { * @return arr */ God.getFormatedProcesses = function() { - var db = God.clusters_db; + var db = Common.serialize(God.clusters_db); var arr = []; for (var key in db) { @@ -99,7 +99,7 @@ module.exports = function(God) { * @return */ God.findByScript = function(script, cb) { - var db = God.clusters_db; + var db = Common.serialize(God.clusters_db); var arr = []; for (var key in db) { diff --git a/lib/Interactor/Tools.js b/lib/Interactor/Tools.js new file mode 100644 index 00000000..3cf0280d --- /dev/null +++ b/lib/Interactor/Tools.js @@ -0,0 +1,8 @@ + +var Stringify = require('json-stringify-safe'); + +var Tools = {}; + +Tools.serialize = function(data) { + return JSON.parse(Stringify(data)); +}; diff --git a/lib/Satan.js b/lib/Satan.js index 236f88a5..72205b75 100644 --- a/lib/Satan.js +++ b/lib/Satan.js @@ -173,6 +173,7 @@ Satan.remoteWrapper = function() { stopAll : God.stopAll, softReloadProcessId : God.softReloadProcessId, reloadProcessId : God.reloadProcessId, + resetMetaProcessId : God.resetMetaProcessId, killMe : God.killMe, findByScript : God.findByScript, findByPort : God.findByPort, diff --git a/test/bash/cli.sh b/test/bash/cli.sh index ceb0eb3f..71966f3c 100755 --- a/test/bash/cli.sh +++ b/test/bash/cli.sh @@ -211,6 +211,8 @@ OUT=`$pm2 prettylist | grep -o "restart_time" | wc -l` [ $OUT -eq 8 ] || fail "Not valid process number" success "Processes valid" + + $pm2 delete all spec "Should delete all processes" diff --git a/test/bash/reset.sh b/test/bash/reset.sh new file mode 100644 index 00000000..71c3562b --- /dev/null +++ b/test/bash/reset.sh @@ -0,0 +1,46 @@ + +#!/usr/bin/env bash + +SRC=$(cd $(dirname "$0"); pwd) +source "${SRC}/include.sh" + +cd $file_path +$pm2 kill + +echo "################## RESET ###################" + +# +# BY ID +# +$pm2 start echo.js +should 'should restarted be one for all' 'restart_time: 0' 1 + +$pm2 restart 0 +should 'should process restarted' 'restart_time: 1' 1 + +$pm2 reset 0 +should 'should process reseted' 'restart_time: 0' 1 + +# +# BY NAME +# +$pm2 start echo.js -i 4 -f +should 'should restarted be one for all' 'restart_time: 0' 5 + +$pm2 restart echo +should 'should process restarted' 'restart_time: 1' 5 + +$pm2 reset echo +should 'should process reseted' 'restart_time: 0' 5 + + +# +# ALL +# +$pm2 restart all +$pm2 restart all +$pm2 restart all +should 'should process restarted' 'restart_time: 3' 5 + +$pm2 reset all +should 'should process reseted' 'restart_time: 0' 5 diff --git a/test/main.sh b/test/main.sh index 14b27252..010fb5c4 100644 --- a/test/main.sh +++ b/test/main.sh @@ -38,5 +38,7 @@ bash ./test/bash/fork.sh spec "Fork verified" bash ./test/bash/infinite_loop.sh spec "Infinite loop stop" +bash ./test/bash/reset.sh +spec "Reset meta" $pm2 kill From 1c980e1a127dc3288b8817d8904fb522d7f06b41 Mon Sep 17 00:00:00 2001 From: tknew2 Date: Mon, 11 Aug 2014 19:22:17 +0200 Subject: [PATCH 09/69] fix leakage in agent --- lib/God/ActionMethods.js | 14 ++++++++--- lib/Interactor/Daemon.js | 4 +++- lib/Interactor/Filter.js | 31 ++++++++++-------------- lib/Interactor/PushInteractor.js | 41 ++++++++++++++++++-------------- test/bash/cli2.sh | 1 + 5 files changed, 51 insertions(+), 40 deletions(-) diff --git a/lib/God/ActionMethods.js b/lib/God/ActionMethods.js index 909ad894..0f1e72fa 100644 --- a/lib/God/ActionMethods.js +++ b/lib/God/ActionMethods.js @@ -32,7 +32,6 @@ module.exports = function(God) { */ God.getMonitorData = function(env, cb) { var processes = God.getFormatedProcesses(); - var arr = []; async.mapLimit(processes, cst.CONCURRENT_ACTIONS, function(pro, next) { @@ -51,23 +50,32 @@ module.exports = function(God) { memory : Math.floor(res.memory), cpu : Math.floor(res.cpu) }; + return next(null, pro); }); } catch(e) { God.logAndGenerateError(e); - pro['monit'] = {memory : 0, cpu : 0}; + pro['monit'] = { + memory : 0, + cpu : 0 + }; return next(null, pro); } } else { - pro['monit'] = {memory : 0, cpu : 0}; + pro['monit'] = { + memory : 0, + cpu : 0 + }; return next(null, pro); } return false; }, function(err, res) { if (err) return cb(God.logAndGenerateError(err), null); + processes = null; + return cb(null, res); }); diff --git a/lib/Interactor/Daemon.js b/lib/Interactor/Daemon.js index 2a6584fe..1f795f40 100644 --- a/lib/Interactor/Daemon.js +++ b/lib/Interactor/Daemon.js @@ -6,6 +6,8 @@ var axon = require('axon'); var debug = require('debug')('interface:driver'); // Interface var chalk = require('chalk'); +var pkg = require('../../package.json'); + var cst = require('../../constants.js'); var Cipher = require('./Cipher.js'); var Filter = require('./Filter.js'); @@ -51,7 +53,7 @@ var Daemon = { opts.PUBLIC_KEY = process.env.PM2_PUBLIC_KEY; opts.SECRET_KEY = process.env.PM2_SECRET_KEY; opts.REVERSE_INTERACT = JSON.parse(process.env.PM2_REVERSE_INTERACT); - + opts.PM2_VERSION = pkg.version; if (!opts.MACHINE_NAME) { console.error('You must provide a PM2_MACHINE_NAME environment variable'); process.exit(cst.ERROR_EXIT); diff --git a/lib/Interactor/Filter.js b/lib/Interactor/Filter.js index dcc721f0..a99a8ca8 100644 --- a/lib/Interactor/Filter.js +++ b/lib/Interactor/Filter.js @@ -1,21 +1,16 @@ -var pkg = require('../../package.json'); var os = require('os'); -function Filter(machine_name) { - this.machine_name = machine_name; - this.pm2_version = pkg.version; +var Filter = {}; + +Filter.getProcessID = function(machine_name, name, id) { + return machine_name + ':' + name + ':' + id; }; -Filter.prototype.getProcessID = function(name, id) { - return this.machine_name + ':' + name + ':' + id; -}; - -Filter.prototype.status = function(processes, conf) { +Filter.status = function(processes, conf) { if (!processes) return null; var filter_procs = []; - var self = this; processes.forEach(function(proc) { filter_procs.push({ @@ -32,7 +27,7 @@ Filter.prototype.status = function(processes, conf) { pm_id : proc.pm2_env.pm_id, cpu : Math.floor(proc.monit.cpu) || 0, memory : Math.floor(proc.monit.memory) || 0, - process_id : self.getProcessID(proc.pm2_env.name, proc.pm2_env.pm_id), + process_id : Filter.getProcessID(conf.MACHINE_NAME, proc.pm2_env.name, proc.pm2_env.pm_id), axm_actions : proc.pm2_env.axm_actions || [] }); }); @@ -40,7 +35,6 @@ Filter.prototype.status = function(processes, conf) { return { process : filter_procs, server : { - pm2_version : self.pm2_version, loadavg : os.loadavg(), total_mem : os.totalmem(), free_mem : os.freemem(), @@ -50,18 +44,19 @@ Filter.prototype.status = function(processes, conf) { type : os.type(), platform : os.platform(), arch : os.arch(), - interaction : conf.REVERSE_INTERACT + interaction : conf.REVERSE_INTERACT, + pm2_version : conf.PM2_VERSION } }; }; -Filter.prototype.monitoring = function(processes) { +Filter.monitoring = function(processes, conf) { if (!processes) return null; - var self = this; + var filter_procs = {}; processes.forEach(function(proc) { - filter_procs[self.getProcessID(proc.pm2_env.name,proc.pm2_env.pm_id)] = [proc.monit.cpu, proc.monit.memory]; + filter_procs[Filter.getProcessID(conf.MACHINE_NAME, proc.pm2_env.name,proc.pm2_env.pm_id)] = [proc.monit.cpu, proc.monit.memory]; }); return { @@ -72,7 +67,7 @@ Filter.prototype.monitoring = function(processes) { }; }; -Filter.prototype.pruneProcessObj = function(process, machine_name) { +Filter.pruneProcessObj = function(process, machine_name) { return { pm_id : process.pm2_env.pm_id, name : process.pm2_env.name, @@ -81,7 +76,7 @@ Filter.prototype.pruneProcessObj = function(process, machine_name) { }; }; -Filter.prototype.processState = function(data) { +Filter.processState = function(data) { var state = { state : data.process.pm2_env.status, name : data.process.pm2_env.name, diff --git a/lib/Interactor/PushInteractor.js b/lib/Interactor/PushInteractor.js index 0c6f02bd..ef62f12c 100644 --- a/lib/Interactor/PushInteractor.js +++ b/lib/Interactor/PushInteractor.js @@ -27,7 +27,6 @@ var PushInteractor = module.exports = { this.conf = p.conf; this.ipm2 = p.conf.ipm2; - this.filter = new Filter(this.conf.MACHINE_NAME); this.pm2_connected = false; this.send_buffer = []; this.http_transactions = []; @@ -38,10 +37,13 @@ var PushInteractor = module.exports = { this.ipm2.on('ready', function() { console.log('[PUSH] Connected to PM2'); self.pm2_connected = true; + self.startWorker(); }); this.ipm2.on('reconnecting', function() { console.log('[PUSH] Reconnecting to PM2'); + if (self.timer_worker) + clearInterval(self.timer_worker); self.pm2_connected = false; }); @@ -56,7 +58,7 @@ var PushInteractor = module.exports = { * Start the chmilblik */ this.processEvents(); - this.startWorker(); + //this.startWorker(); }, /** * Send bufferized data at regular interval @@ -64,9 +66,7 @@ var PushInteractor = module.exports = { startWorker : function() { var self = this; - setInterval(function() { - if (self.pm2_connected == false) return; - + self.timer_worker = setInterval(function() { self.sendData(); }, cst.SEND_INTERVAL); }, @@ -74,7 +74,7 @@ var PushInteractor = module.exports = { var self = this; this.ipm2.bus.on('*', function(event, packet) { - if (self.pm2_connected == false) return false; + //if (self.pm2_connected == false) return false; if (packet.process && packet.process.pm2_env) { /** @@ -82,7 +82,7 @@ var PushInteractor = module.exports = { */ if (event == 'axm:action' || event.match(/^log:/)) return false; - packet.process = self.filter.pruneProcessObj(packet.process, self.conf.MACHINE_NAME); + packet.process = Filter.pruneProcessObj(packet.process, self.conf.MACHINE_NAME); self.bufferData(event, packet); } else { @@ -104,11 +104,11 @@ var PushInteractor = module.exports = { var ret; - if ((ret = self.filter.monitoring(processes))) { + if ((ret = Filter.monitoring(processes, self.conf))) { self.bufferData('monitoring', ret); } - if ((ret = self.filter.status(processes, self.conf))) { + if ((ret = Filter.status(processes, self.conf))) { self.bufferData('status', ret); } @@ -131,13 +131,10 @@ var PushInteractor = module.exports = { * @return */ sendData : function() { - var data = {}; var self = this; this.bufferizeServerStatus(function() { - /** - * Cipher data with AES256 - */ + var data = {}; if (process.env.NODE_ENV && process.env.NODE_ENV == 'test') { data = { @@ -150,6 +147,11 @@ var PushInteractor = module.exports = { }; } else { + + /** + * Cipher data with AES256 + */ + var cipheredData = Cipher.cipherMessage(JSON.stringify({ buffer : self.send_buffer, server_name : self.conf.MACHINE_NAME @@ -157,7 +159,7 @@ var PushInteractor = module.exports = { data = { public_key : self.conf.PUBLIC_KEY, - sent_at : new Date(), + sent_at : Date.now(), data : cipheredData }; } @@ -165,6 +167,8 @@ var PushInteractor = module.exports = { self.udpSocket.send(JSON.stringify(data)); debug('Buffer with length %d sent', self.send_buffer.length); + data = null; + self.send_buffer = []; }); }, @@ -174,23 +178,23 @@ var PushInteractor = module.exports = { if (packet.process && !packet.server) { if (event == 'http:transaction') { - packet.data.data.process_id = self.conf.MACHINE_NAME + ':' + packet.process.name + ':' + packet.process.pm_id; + packet.data.data.process_id = Filter.getProcessID(self.conf.MACHINE_NAME, packet.process.name, packet.process.pm_id); packet.data.data.process_name = packet.process.name; return self.http_transactions.push(packet.data.data); } self.send_buffer.push({ - at : new Date(), + at : Date.now(), event : event, data : packet.data || null, process : packet.process, - process_id : self.conf.MACHINE_NAME + ':' + packet.process.name + ':' + packet.process.pm_id, + process_id : Filter.getProcessID(self.conf.MACHINE_NAME, packet.process.name, packet.process.pm_id), process_name : packet.process.name }); } else { self.send_buffer.push({ - at : new Date(), + at : Date.now(), event : event, data : packet, server_name : self.conf.MACHINE_NAME @@ -198,5 +202,6 @@ var PushInteractor = module.exports = { } debug('Event %s bufferized', event); + return false; } }; diff --git a/test/bash/cli2.sh b/test/bash/cli2.sh index 42d3dcc9..538ed9e3 100755 --- a/test/bash/cli2.sh +++ b/test/bash/cli2.sh @@ -89,4 +89,5 @@ cat outech-0.log > /dev/null spec "file outech.log exist" cat errech-0.log > /dev/null spec "file errech.log exist" + should 'should has not restarted' 'restart_time: 0' 10 From 2799b50f613a4c213e025c1d9b6fcf277d647496 Mon Sep 17 00:00:00 2001 From: tknew2 Date: Tue, 12 Aug 2014 01:41:14 +0200 Subject: [PATCH 10/69] Watchdog to make PM2 forever alive - Refactor programmatic interface - New process spawning system --- lib/Interactor/Daemon.js | 32 +++-------- lib/Interactor/InteractorDaemonizer.js | 18 +++++-- lib/Interactor/WatchDog.js | 72 +++++++++++++++++++++++++ lib/Satan.js | 73 +++++++++----------------- 4 files changed, 115 insertions(+), 80 deletions(-) create mode 100644 lib/Interactor/WatchDog.js diff --git a/lib/Interactor/Daemon.js b/lib/Interactor/Daemon.js index 1f795f40..b55a4529 100644 --- a/lib/Interactor/Daemon.js +++ b/lib/Interactor/Daemon.js @@ -13,7 +13,7 @@ var Cipher = require('./Cipher.js'); var Filter = require('./Filter.js'); var ReverseInteractor = require('./ReverseInteractor.js'); var PushInteractor = require('./PushInteractor.js'); - +var WatchDog = require('./WatchDog.js'); var Daemon = { connectToPM2 : function() { @@ -79,28 +79,6 @@ var Daemon = { return opts; }, - wrapStd : function(cb) { - var stdout = fs.createWriteStream(cst.INTERACTOR_LOG_FILE_PATH, { - flags : 'a' - }); - - stdout.on('open', function() { - process.stderr.write = (function(write) { - return function(string, encoding, fd) { - stdout.write(new Date().toISOString() + ' : ' + string); - }; - })(process.stderr.write); - - process.stdout.write = (function(write) { - return function(string, encoding, fd) { - stdout.write(new Date().toISOString() + ' : ' + string); - }; - })(process.stdout.write); - - if (cb) return cb(); - return false; - }); - }, start : function() { var self = this; @@ -111,6 +89,10 @@ var Daemon = { self.activateRPC(); self.opts.ipm2 = self.connectToPM2(); + WatchDog.start({ + conf : self.opts + }); + // Then connect to external services if (cst.DEBUG) { PushInteractor.start({ @@ -152,7 +134,5 @@ if (require.main === module) { console.log(chalk.cyan.bold('[Keymetrics.io]') + ' Launching agent'); process.title = 'PM2: Keymetrics.io Agent'; - Daemon.wrapStd(function() { - Daemon.start(); - }); + Daemon.start(); } diff --git a/lib/Interactor/InteractorDaemonizer.js b/lib/Interactor/InteractorDaemonizer.js index a6d59834..cafe6866 100644 --- a/lib/Interactor/InteractorDaemonizer.js +++ b/lib/Interactor/InteractorDaemonizer.js @@ -130,7 +130,12 @@ function launchOrAttach(infos, cb) { InteractorDaemonizer.daemonize = function(infos, cb) { var InteractorJS = path.resolve(path.dirname(module.filename), 'Daemon.js'); - var child = require('child_process').fork(InteractorJS, [], { + console.log('Launching interactor'); + + var out = fs.openSync(cst.INTERACTOR_LOG_FILE_PATH, 'a'), + err = fs.openSync(cst.INTERACTOR_LOG_FILE_PATH, 'a'); + + var child = require('child_process').spawn('node', [InteractorJS], { silent : false, detached : true, cwd : process.cwd(), @@ -140,10 +145,7 @@ InteractorDaemonizer.daemonize = function(infos, cb) { PM2_PUBLIC_KEY : infos.public_key, PM2_REVERSE_INTERACT : infos.reverse_interact }, process.env), - stdio : 'ignore' - }, function(err, stdout, stderr) { - if (err) return console.error(err); - return console.log('Interactor daemonized'); + stdio : ['ipc', out, err] }); fs.writeFileSync(cst.INTERACTOR_PID_PATH, child.pid); @@ -153,6 +155,8 @@ InteractorDaemonizer.daemonize = function(infos, cb) { child.once('message', function(msg) { process.emit('interactor:daemon:ready'); console.log(msg); + + child.disconnect(); console.log(chalk.cyan.bold('[Keymetrics.io]') + ' Log: %s | Conf: %s | PID: %s', cst.INTERACTOR_LOG_FILE_PATH, cst.INTERACTION_CONF, cst.INTERACTOR_PID_PATH); @@ -259,6 +263,10 @@ InteractorDaemonizer.getSetKeys = function(secret_key, public_key, machine_name, }; InteractorDaemonizer.launchAndInteract = function(opts, cb) { + if (process.env.PM2_AGENT_ONLINE) { + return process.nextTick(cb); + } + InteractorDaemonizer.getSetKeys({ secret_key : opts.secret_key || null, public_key : opts.public_key || null, diff --git a/lib/Interactor/WatchDog.js b/lib/Interactor/WatchDog.js new file mode 100644 index 00000000..4f14fc8e --- /dev/null +++ b/lib/Interactor/WatchDog.js @@ -0,0 +1,72 @@ + +var pm2 = require('../..'); +var debug = require('debug')('interface:watchdog'); + +process.env.PM2_AGENT_ONLINE = true; + +var WatchDog = module.exports = { + start : function(p) { + var self = this; + this.ipm2 = p.conf.ipm2; + this.relaunching = false; + + /** + * Handle PM2 connection state changes + */ + this.ipm2.on('ready', function() { + console.log('[WATCHDOG] Connected to PM2'); + self.relaunching = false; + self.autoDump(); + }); + + this.ipm2.on('reconnecting', function() { + console.log('[WATCHDOG] PM2 is disconnected - Relaunching PM2'); + + if (self.relaunching === true) return console.log('[WATCHDOG] Already relaunching PM2'); + self.relaunching = true; + + if (self.dump_interval) + clearInterval(self.dump_interval); + + return WatchDog.resurrect(); + }); + }, + resurrect : function() { + var self = this; + + console.log('[WATCHDOG] Trying to launch PM2 #1'); + + + pm2.connect(function() { + console.log('[WATCHDOG] PM2 successfully launched. Resurrecting processes'); + + pm2.resurrect(function(err) { + if (err) { + self.relaunching = false; + return console.error('[WATCHDOG] Error when resurrect'); + } + console.log('PM2 has been resurrected'); + self.relaunching = false; + pm2.disconnect(function() {}); + return false; + }); + return false; + }); + }, + autoDump : function() { + var self = this; + + this.dump_interval = setInterval(function() { + pm2.connect(function() { + if (self.relaunching == true) return false; + + pm2.dump(function(err) { + if (err) return console.error('[WATCHDOG] Error when dumping'); + debug('PM2 process list dumped'); + pm2.disconnect(function() {}); + return false; + }); + }); + }, 5000); + } +}; diff --git a/lib/Satan.js b/lib/Satan.js index 23de6868..804f8967 100644 --- a/lib/Satan.js +++ b/lib/Satan.js @@ -51,7 +51,6 @@ Satan.start = function(noDaemonMode, cb) { Satan.remoteWrapper(); return cb ? cb(null) : false; } - // Daemonize return Satan.launchDaemon(function(err, child) { if (err) { @@ -106,38 +105,8 @@ Satan.remoteWrapper = function() { // Only require here because God init himself var God = require('./God'); - var InteractorDaemonizer = require('./Interactor/InteractorDaemonizer.js'); - Satan.processStateHandler(God); - if (process.env.SILENT == 'true') { - // Redirect output to files - var stdout = fs.createWriteStream(cst.PM2_LOG_FILE_PATH, { - flags : 'a' - }); - - /** - * Description - * @method write - * @param {} string - * @return - */ - process.stderr.write = function(string) { - stdout.write(new Date().toISOString() + ' : ' + string); - }; - - /** - * Description - * @method write - * @param {} string - * @return - */ - process.stdout.write = function(string) { - stdout.write(new Date().toISOString() + ' : ' + string); - }; - } - - // Send ready message to Satan Client if (typeof(process.send) === 'function') { process.send({ @@ -235,13 +204,10 @@ Satan.remoteWrapper = function() { /** * Launch Interactor */ - InteractorDaemonizer.launchAndInteract({}, function(err, data) { - // Then bind - God.bus.onAny(function(data_v) { - debug(this.event); - // Avoid circular structure - pub.emit(this.event, JSON.parse(Stringify(data_v))); - }); + God.bus.onAny(function(data_v) { + debug(this.event); + // Avoid circular structure + pub.emit(this.event, JSON.parse(Stringify(data_v))); }); }; @@ -264,26 +230,34 @@ Satan.launchDaemon = function(cb) { debug('Launching daemon'); var SatanJS = p.resolve(p.dirname(module.filename), 'Satan.js'); + var InteractorDaemonizer = require('./Interactor/InteractorDaemonizer.js'); - var child = require('child_process').fork(SatanJS, [], { - silent : false, + + var out = fs.openSync(cst.PM2_LOG_FILE_PATH, 'a'), + err = fs.openSync(cst.PM2_LOG_FILE_PATH, 'a'); + + // Pb node -> need full path + // Pb Inter + // <=> keymetrics + var child = require('child_process').spawn('node', [SatanJS], { detached : true, cwd : process.cwd(), env : util._extend({ 'SILENT' : cst.DEBUG ? !cst.DEBUG : true, 'HOME' : (process.env.PM2_HOME || process.env.HOME) }, process.env), - stdio : 'ignore' - }, function(err, stdout, stderr) { - if (err) console.error(err); - debug(arguments); + stdio : ['ipc', out, err] }); child.unref(); child.once('message', function(msg) { - process.emit('satan:daemon:ready'); - return setTimeout(function() {cb(null, child)}, 150); + debug('PM2 Daemon launched', msg); + InteractorDaemonizer.launchAndInteract({}, function(err, data) { + process.emit('satan:daemon:ready'); + child.disconnect(); + return setTimeout(function() {cb(null, child)}, 150); + }); }); }; @@ -338,11 +312,12 @@ Satan.launchRPC = function(cb) { */ Satan.disconnectRPC = function(cb) { process.nextTick(function() { - if (!Satan.client || !Satan.client.sock || !Satan.client.sock.close) return cb({ - msg : 'RPC connection to PM2 is not launched' + if (!Satan.client || !Satan.client.sock || !Satan.client.sock.close) + return cb({ + msg : 'RPC connection to PM2 is not launched' }); Satan.client.sock.close(); - return cb(null, {success:true}); + return cb ? cb(null, {success:true}) : false; }); }; From 348e000d5feac7ceab082cbb862359dea8686744 Mon Sep 17 00:00:00 2001 From: tknew2 Date: Tue, 12 Aug 2014 15:13:07 +0200 Subject: [PATCH 11/69] update dependencies - refactor some stuff --- bin/pm2 | 24 ++++++++++++------------ lib/CliUx.js | 2 ++ lib/Common.js | 2 +- lib/God.js | 27 ++++++++++++++++++--------- lib/God/ActionMethods.js | 34 +++++++++++++--------------------- lib/God/ClusterMode.js | 8 ++++---- lib/God/ForkMode.js | 12 ++++++------ lib/God/Methods.js | 4 ++-- lib/Monit.js | 16 +++++++++------- lib/Satan.js | 24 +++++++++++------------- package.json | 6 +++--- 11 files changed, 81 insertions(+), 78 deletions(-) diff --git a/bin/pm2 b/bin/pm2 index c5e847b1..df08d476 100755 --- a/bin/pm2 +++ b/bin/pm2 @@ -121,7 +121,7 @@ function failOnUnknown(fn) { // commander.command('start ') .option('--watch', 'Watch folder for changes') - .description(' start and daemonize an app') + .description('start and daemonize an app') .action(function(cmd) { if (cmd == "-") { process.stdin.resume(); @@ -137,13 +137,13 @@ commander.command('start ') }); commander.command('deploy ') - .description(' deploy your json') + .description('deploy your json') .action(function(cmd) { CLI.deploy(cmd, commander); }); commander.command('startOrRestart ') - .description(' start or restart JSON file') + .description('start or restart JSON file') .action(function(file) { CLI._jsonStartOrAction('restart', file, commander); }); @@ -153,7 +153,7 @@ commander.command('startOrRestart ') // commander.command('stop ') .option('--watch', 'Stop watching folder for changes') - .description(' stop a process (to start it again, do pm2 restart )') + .description('stop a process (to start it again, do pm2 restart )') .action(function(param) { CLI.stop(param); }); @@ -163,7 +163,7 @@ commander.command('stop ') // commander.command('restart ') .option('--watch', 'Toggle watching folder for changes') - .description(' restart a process') + .description('restart a process') .action(function(param) { CLI.restart(param); }); @@ -172,7 +172,7 @@ commander.command('restart ') // Reload process(es) // commander.command('reload ') - .description(' reload processes (note that its for app using HTTP/HTTPS)') + .description('reload processes (note that its for app using HTTP/HTTPS)') .action(function(pm2_id) { CLI.reload(pm2_id); }); @@ -181,13 +181,13 @@ commander.command('reload ') // Reload process(es) // commander.command('gracefulReload ') -.description(' gracefully reload a process. Send a "shutdown" message to close all connections.') +.description('gracefully reload a process. Send a "shutdown" message to close all connections.') .action(function(pm2_id) { CLI.gracefulReload(pm2_id); }); // commander.command('gracefulStop ') -// .description(' gracefully reload a process. Send a "shutdown" message to close all connections.') +// .description('gracefully reload a process. Send a "shutdown" message to close all connections.') // .action(function(pm2_id) { // CLI.gracefulStop(pm2_id); // }); @@ -196,7 +196,7 @@ commander.command('gracefulReload ') // Stop and delete a process by name from database // commander.command('delete ') - .description(' stop and delete a process from pm2 process list') + .description('stop and delete a process from pm2 process list') .action(function(name) { if (name == "-") { process.stdin.resume(); @@ -213,7 +213,7 @@ commander.command('delete ') // Send system signal to process // commander.command('sendSignal ') - .description(' send a system signal to the target process') + .description('send a system signal to the target process') .action(function(signal, pm2_id) { if (isNaN(parseInt(pm2_id))) { console.log(cst.PREFIX_MSG + 'Sending signal to process name ' + pm2_id); @@ -228,13 +228,13 @@ commander.command('sendSignal ') // Stop and delete a process by name from database // commander.command('ping') - .description(' ping pm2 daemon - if not up it will launch it') + .description('ping pm2 daemon - if not up it will launch it') .action(function() { CLI.ping(); }); commander.command('updatePM2') - .description(' updatePM2 after a npm update') + .description('updatePM2 after a npm update') .action(function() { CLI.updatePM2() }); diff --git a/lib/CliUx.js b/lib/CliUx.js index 50845a65..417978ef 100644 --- a/lib/CliUx.js +++ b/lib/CliUx.js @@ -83,6 +83,8 @@ UX.dispAsTable = function(list, interact_infos) { style : {'padding-left' : 1, head : ['cyan', 'bold'], border : ['white'], compact : true} }); + if (!list) + return console.log('list empty'); list.forEach(function(l) { var obj = {}; diff --git a/lib/Common.js b/lib/Common.js index fd65047d..cce86db7 100644 --- a/lib/Common.js +++ b/lib/Common.js @@ -106,7 +106,7 @@ Common.resolveAppPaths = function(app, cwd, outputter) { return app; }; -Common.deepCopy = Common.serialize = function(data) { +Common.deepCopy = Common.serialize = function serialize(data) { return JSON.parse(Stringify(data)); }; diff --git a/lib/God.js b/lib/God.js index f634b677..1fcd31a5 100644 --- a/lib/God.js +++ b/lib/God.js @@ -9,6 +9,13 @@ var p = path; var Common = require('./Common'); var cst = require('../constants.js'); +// require('webkit-devtools-agent').start({ +// port: 9999, +// bind_to: '0.0.0.0', +// ipc_port: 3333, +// verbose: true +// }); + /** * Override cluster module configuration */ @@ -50,6 +57,8 @@ function handleExit(clu, exit_code) { var proc = God.clusters_db[clu.pm2_env.pm_id]; + if (!proc) return false; + var stopping = (proc.pm2_env.status == cst.STOPPING_STATUS || proc.pm2_env.status == cst.ERRORED_STATUS) ? true : false; var overlimit = false; var pidFile = proc.pm2_env.pm_pid_path; @@ -115,7 +124,7 @@ function handleExit(clu, exit_code) { * @param {Function} cb * @return Literal */ -God.executeApp = function(env, cb) { +God.executeApp = function executeApp(env, cb) { var env_copy = JSON.parse(JSON.stringify(env)); util._extend(env_copy, env.env); @@ -158,7 +167,7 @@ God.executeApp = function(env, cb) { /** * Fork mode logic */ - God.forkMode(env_copy, function(err, clu) { + God.forkMode(env_copy, function forkMode(err, clu) { if (cb && err) return cb(err); var old_env = God.clusters_db[clu.pm2_env.pm_id]; @@ -166,11 +175,11 @@ God.executeApp = function(env, cb) { var proc = God.clusters_db[env_copy.pm_id] = clu; - clu.once('error', function(err) { + clu.once('error', function cluError(err) { proc.pm2_env.status = cst.ERRORED_STATUS; }); - clu.once('close', function(code) { + clu.once('close', function cluClose(code) { proc.removeAllListeners(); return handleExit(proc, code); }); @@ -186,7 +195,7 @@ God.executeApp = function(env, cb) { /** * Cluster mode logic (for NodeJS apps) */ - God.nodeApp(env_copy, function(err, clu) { + God.nodeApp(env_copy, function nodeApp(err, clu) { if (cb && err) return cb(err); if (err) return false; @@ -199,7 +208,7 @@ God.executeApp = function(env, cb) { var proc = God.clusters_db[clu.pm2_env.pm_id] = clu; - clu.once('online', function() { + clu.once('online', function cluOnline() { proc.pm2_env.status = cst.ONLINE_STATUS; console.log('%s - id%d worker online', proc.pm2_env.pm_exec_path, proc.pm2_env.pm_id); @@ -210,7 +219,7 @@ God.executeApp = function(env, cb) { return false; }); - clu.once('exit', function(exited_clu, code) { + clu.once('exit', function cluExit(exited_clu, code) { proc.removeAllListeners(); return handleExit(Common.serialize(proc), code); }); @@ -231,7 +240,7 @@ God.executeApp = function(env, cb) { * @param {} cb * @return Literal */ -God.prepare = function(env, cb) { +God.prepare = function prepare(env, cb) { // If instances option is set (-i [arg]) if (env.instances) { if (env.instances == 'max') env.instances = numCPUs; @@ -271,7 +280,7 @@ God.prepare = function(env, cb) { * @param cb {Function} * @return CallExpression */ -God.prepareJson = function (app, cwd, cb) { +God.prepareJson = function prepareJson(app, cwd, cb) { if (!cb) { cb = cwd; cwd = undefined; diff --git a/lib/God/ActionMethods.js b/lib/God/ActionMethods.js index 0f1e72fa..f0a39ae3 100644 --- a/lib/God/ActionMethods.js +++ b/lib/God/ActionMethods.js @@ -30,39 +30,31 @@ module.exports = function(God) { * @param {} cb * @return */ - God.getMonitorData = function(env, cb) { + God.getMonitorData = function getMonitorData(env, cb) { var processes = God.getFormatedProcesses(); - async.mapLimit(processes, cst.CONCURRENT_ACTIONS, function(pro, next) { + async.map(processes, function computeMonitor(pro, next) { if (pro.pm2_env.status != cst.STOPPED_STATUS && pro.pm2_env.status != cst.STOPPING_STATUS && pro.pm2_env.status != cst.ERRORED_STATUS) { - try { - pidusage(pro.pid, function(err, res) { - if (err) { - God.logAndGenerateError(err); - return next(err); - } - + pidusage(pro.pid, function retPidUsage(err, res) { + if (err) { pro['monit'] = { - memory : Math.floor(res.memory), - cpu : Math.floor(res.cpu) + memory : 0, + cpu : 0 }; - return next(null, pro); - }); + } - } catch(e) { - God.logAndGenerateError(e); pro['monit'] = { - memory : 0, - cpu : 0 + memory : Math.floor(res.memory), + cpu : Math.floor(res.cpu) }; - return next(null, pro); - } + return next(null, pro); + }); } else { pro['monit'] = { @@ -72,7 +64,7 @@ module.exports = function(God) { return next(null, pro); } return false; - }, function(err, res) { + }, function retMonitor(err, res) { if (err) return cb(God.logAndGenerateError(err), null); processes = null; @@ -88,7 +80,7 @@ module.exports = function(God) { * @param {} cb * @return */ - God.getSystemData = function(env, cb) { + God.getSystemData = function getSystemData(env, cb) { God.getMonitorData(env, function(err, processes) { cb(err, { system: { diff --git a/lib/God/ClusterMode.js b/lib/God/ClusterMode.js index 9b56bd33..264d49a1 100644 --- a/lib/God/ClusterMode.js +++ b/lib/God/ClusterMode.js @@ -17,7 +17,7 @@ var Common = require('../Common'); * @param {} God * @return */ -module.exports = function(God) { +module.exports = function ClusterMode(God) { /** * For Node apps - Cluster mode @@ -27,8 +27,8 @@ module.exports = function(God) { * @param {} cb * @return Literal */ - God.nodeApp = function(env_copy, cb){ - var clu; + God.nodeApp = function nodeApp(env_copy, cb){ + var clu = null; if (fs.existsSync(env_copy.pm_exec_path) == false) { return cb(God.logAndGenerateError('Script ' + env_copy.pm_exec_path + ' missing'), {}); @@ -50,7 +50,7 @@ module.exports = function(God) { clu.pm2_env = env_copy; // Receive message from child - clu.on('message', function(msg) { + clu.on('message', function cluMessage(msg) { var proc_data = Common.serialize(clu); switch (msg.type) { diff --git a/lib/God/ForkMode.js b/lib/God/ForkMode.js index e9b97d2d..987f9964 100644 --- a/lib/God/ForkMode.js +++ b/lib/God/ForkMode.js @@ -18,7 +18,7 @@ var Common = require('../Common'); * @param {} God * @return */ -module.exports = function(God) { +module.exports = function ForkMode(God) { /** * For all apps - FORK MODE * fork the app @@ -27,7 +27,7 @@ module.exports = function(God) { * @param {} cb * @return */ - God.forkMode = function(pm2_env, cb) { + God.forkMode = function forkMode(pm2_env, cb) { var command, args; log('Entering in fork mode'); @@ -109,7 +109,7 @@ module.exports = function(God) { cspr.pm2_env = pm2_env; cspr.pm2_env.status = cst.ONLINE_STATUS; - cspr.stderr.on('data', function(data) { + cspr.stderr.on('data', function forkErrData(data) { var log_data = data.toString(); if (pm2_env.log_date_format) @@ -123,7 +123,7 @@ module.exports = function(God) { }); }); - cspr.stdout.on('data', function(data) { + cspr.stdout.on('data', function forkOutData(data) { var log_data = data.toString(); if (pm2_env.log_date_format) @@ -137,7 +137,7 @@ module.exports = function(God) { }); }); - cspr.on('message', function(data) { + cspr.on('message', function forkMessage(data) { God.bus.emit(data.type ? data.type : 'process:msg', { process : cspr, data : data @@ -146,7 +146,7 @@ module.exports = function(God) { fs.writeFileSync(pidFile, cspr.pid); - cspr.once('close', function(status) { + cspr.once('close', function forkClose(status) { try { stderr.close(); stdout.close(); diff --git a/lib/God/Methods.js b/lib/God/Methods.js index 9bb6cad4..0d21e435 100644 --- a/lib/God/Methods.js +++ b/lib/God/Methods.js @@ -46,7 +46,7 @@ module.exports = function(God) { * @method getFormatedProcesses * @return arr */ - God.getFormatedProcesses = function() { + God.getFormatedProcesses = function getFormatedProcesses() { var db = Common.serialize(God.clusters_db); var arr = []; @@ -69,7 +69,7 @@ module.exports = function(God) { * @param {} id * @return ConditionalExpression */ - God.findProcessById = function(id) { + God.findProcessById = function findProcessById(id) { return God.clusters_db[id] ? God.clusters_db[id] : null; }; diff --git a/lib/Monit.js b/lib/Monit.js index bc76d740..54a87d0d 100644 --- a/lib/Monit.js +++ b/lib/Monit.js @@ -10,8 +10,6 @@ var CliUx = require('./CliUx'); var debug = require('debug')('pm2:monit'); -require('colors'); - // Cst for light programs const RATIO_T1 = Math.floor(os.totalmem() / 500); // Cst for medium programs @@ -45,7 +43,7 @@ Monit.reset = function(msg) { if(msg) { this.multi.write(msg); - } + } this.bars = {}; @@ -127,6 +125,8 @@ Monit.refresh = function(processes) { Monit.addProcess = function(proc, i) { + require('colors'); + if(proc.pm_id in this.bars) { return ; } @@ -183,7 +183,7 @@ Monit.addProcesses = function(processes) { if(!processes) { processes = []; } - + this.reset(); var num = processes.length; @@ -204,7 +204,7 @@ Monit.addProcesses = function(processes) { * @method drawRatio * @param {} bar_memory * @param {} memory - * @return + * @return */ Monit.drawRatio = function(bar_memory, memory) { var scale = 0; @@ -225,12 +225,14 @@ Monit.drawRatio = function(bar_memory, memory) { * @return this */ Monit.updateBars = function(proc) { + require('colors'); + if (proc.pm2_env.status !== 'online' || proc.pm2_env.status !== this.bars[proc.pm_id].status) { this.bars[proc.pm_id].cpu.percent(0, proc.pm2_env.status.red); - this.drawRatio(this.bars[proc.pm_id].memory, 0, proc.pm2_env.status.red); + this.drawRatio(this.bars[proc.pm_id].memory, 0, proc.pm2_env.status.red); } else if (!proc.monit) { this.bars[proc.pm_id].cpu.percent(0, 'No data'.red); - this.drawRatio(this.bars[proc.pm_id].memory, 0, 'No data'.red); + this.drawRatio(this.bars[proc.pm_id].memory, 0, 'No data'.red); } else { this.bars[proc.pm_id].cpu.percent(proc.monit.cpu); this.drawRatio(this.bars[proc.pm_id].memory, proc.monit.memory); diff --git a/lib/Satan.js b/lib/Satan.js index 804f8967..13041e09 100644 --- a/lib/Satan.js +++ b/lib/Satan.js @@ -175,7 +175,7 @@ Satan.remoteWrapper = function() { * Action treatment specifics * Attach actions to pm2_env.axm_actions variables (name + options) */ - God.bus.on('axm:action', function(msg) { + God.bus.on('axm:action', function axmActions(msg) { debug('Got new action', msg); var pm2_env = msg.process.pm2_env; var exists = false; @@ -204,9 +204,7 @@ Satan.remoteWrapper = function() { /** * Launch Interactor */ - God.bus.onAny(function(data_v) { - debug(this.event); - // Avoid circular structure + God.bus.onAny(function interactionBroadcast(data_v) { pub.emit(this.event, JSON.parse(Stringify(data_v))); }); @@ -226,7 +224,7 @@ Satan.remoteWrapper = function() { * @param {} cb * @return */ -Satan.launchDaemon = function(cb) { +Satan.launchDaemon = function launchDaemon(cb) { debug('Launching daemon'); var SatanJS = p.resolve(p.dirname(module.filename), 'Satan.js'); @@ -268,7 +266,7 @@ Satan.launchDaemon = function(cb) { * @param {} cb * @return */ -Satan.pingDaemon = function(cb) { +Satan.pingDaemon = function pingDaemon(cb) { var req = axon.socket('req'); var client = new rpc.Client(req); @@ -293,7 +291,7 @@ Satan.pingDaemon = function(cb) { * @method launchRPC * @return */ -Satan.launchRPC = function(cb) { +Satan.launchRPC = function launchRPC(cb) { debug('Launching RPC client on port %s %s', cst.DAEMON_RPC_PORT, cst.DAEMON_BIND_HOST); Satan.client = new rpc.Client(req); @@ -310,7 +308,7 @@ Satan.launchRPC = function(cb) { * Methods to close the RPC connection * @callback cb */ -Satan.disconnectRPC = function(cb) { +Satan.disconnectRPC = function disconnectRPC(cb) { process.nextTick(function() { if (!Satan.client || !Satan.client.sock || !Satan.client.sock.close) return cb({ @@ -327,7 +325,7 @@ Satan.disconnectRPC = function(cb) { * @param {} cb * @return */ -Satan.getExposedMethods = function(cb) { +Satan.getExposedMethods = function getExposedMethods(cb) { Satan.client.methods(cb); }; @@ -339,7 +337,7 @@ Satan.getExposedMethods = function(cb) { * @param {} fn * @return */ -Satan.executeRemote = function(method, env, fn) { +Satan.executeRemote = function executeRemote(method, env, fn) { //stop watching when process is deleted if (method.indexOf('delete') !== -1) { Satan.stopWatch(method, env, fn); @@ -364,7 +362,7 @@ Satan.executeRemote = function(method, env, fn) { * @param {} fn * @return */ -Satan.killDaemon = function(fn) { +Satan.killDaemon = function killDaemon(fn) { Satan.executeRemote('killMe', {}, fn); }; @@ -376,7 +374,7 @@ Satan.killDaemon = function(fn) { * @param {} fn * @return */ -Satan.restartWatch = function(method, env, fn) { +Satan.restartWatch = function restartWatch(method, env, fn) { Satan.client.call('restartWatch', method, env, fn); }; @@ -388,7 +386,7 @@ Satan.restartWatch = function(method, env, fn) { * @param {} fn * @return */ -Satan.stopWatch = function(method, env, fn) { +Satan.stopWatch = function stopWatch(method, env, fn) { Satan.client.call('stopWatch', method, env, fn); }; diff --git a/package.json b/package.json index 1d07be6d..aab42f2d 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "pm2", "preferGlobal": "true", - "version": "0.9.6", + "version": "0.10.0", "os": [ "!win32" ], @@ -110,10 +110,10 @@ "cli-table": "~0.3.0", "coffee-script": "~1.7.1", "colors": "~0.6.2", - "commander": "~2.2.0", + "commander": "~2.3.0", "cron": "~1.0.4", "debug": "~1.0.2", - "eventemitter2": "~0.4.13", + "eventemitter2": "~0.4.14", "json-stringify-safe": "~5.0.0", "nssocket": "~0.5.1", "punt" : "~2.2.0", From 784bdcbf9faa199d0bb3402310617dbbd626b21f Mon Sep 17 00:00:00 2001 From: tknew2 Date: Tue, 12 Aug 2014 15:17:21 +0200 Subject: [PATCH 12/69] add keywords to package --- package.json | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index aab42f2d..def6e4d6 100644 --- a/package.json +++ b/package.json @@ -77,18 +77,28 @@ "fault tolerant", "sysadmin", "tools", - "h24", "pm2", + "logs", + "log", + "json", + "reload", + "programmatic", "harmony", "node-pm2", "production", - "vitalsigns", + "keymetrics", + "deploy", + "deployment", + "daemon", + "supervisor", + "nodemon", "pm2.io", "ghost", "ghost production", "monitoring", "process manager", "forever", + "forever-monitor", "keep process alive", "process configuration", "clustering", From 6455d7eafa03058b8d5cc7970b819865fcaf6eb1 Mon Sep 17 00:00:00 2001 From: tknew2 Date: Tue, 12 Aug 2014 15:36:38 +0200 Subject: [PATCH 13/69] remove uncertain references --- lib/Interactor/PushInteractor.js | 87 ++++++++++++++------------------ lib/Satan.js | 3 -- package.json | 6 ++- 3 files changed, 42 insertions(+), 54 deletions(-) diff --git a/lib/Interactor/PushInteractor.js b/lib/Interactor/PushInteractor.js index ef62f12c..28dc68f7 100644 --- a/lib/Interactor/PushInteractor.js +++ b/lib/Interactor/PushInteractor.js @@ -13,9 +13,7 @@ var Cipher = require('./Cipher.js'); var PushInteractor = module.exports = { start : function(p) { if (!p.port || !p.host) throw new Error('port or host not declared in PullInteractor'); - if (!p.conf || !p.conf.ipm2) throw new Error('ipm2 ĩs not initialized'); - - var self = this; + if (!p.conf || !p.conf.ipm2) throw new Error('ipm2 is not initialized'); console.log(p.host + ':' + p.port); @@ -36,18 +34,18 @@ var PushInteractor = module.exports = { */ this.ipm2.on('ready', function() { console.log('[PUSH] Connected to PM2'); - self.pm2_connected = true; - self.startWorker(); + PushInteractor.pm2_connected = true; + PushInteractor.startWorker(); }); this.ipm2.on('reconnecting', function() { console.log('[PUSH] Reconnecting to PM2'); - if (self.timer_worker) - clearInterval(self.timer_worker); - self.pm2_connected = false; + if (PushInteractor.timer_worker) + clearInterval(PushInteractor.timer_worker); + PushInteractor.pm2_connected = false; }); - self.pm2_connected = true; + PushInteractor.pm2_connected = true; /** * Connect to AXM @@ -58,23 +56,18 @@ var PushInteractor = module.exports = { * Start the chmilblik */ this.processEvents(); - //this.startWorker(); }, /** * Send bufferized data at regular interval */ startWorker : function() { - var self = this; - - self.timer_worker = setInterval(function() { - self.sendData(); + this.timer_worker = setInterval(function() { + PushInteractor.sendData(); }, cst.SEND_INTERVAL); }, processEvents : function() { - var self = this; - this.ipm2.bus.on('*', function(event, packet) { - //if (self.pm2_connected == false) return false; + //if (PushInteractor.pm2_connected == false) return false; if (packet.process && packet.process.pm2_env) { /** @@ -82,8 +75,8 @@ var PushInteractor = module.exports = { */ if (event == 'axm:action' || event.match(/^log:/)) return false; - packet.process = Filter.pruneProcessObj(packet.process, self.conf.MACHINE_NAME); - self.bufferData(event, packet); + packet.process = Filter.pruneProcessObj(packet.process, PushInteractor.conf.MACHINE_NAME); + PushInteractor.bufferData(event, packet); } else { /** @@ -97,28 +90,26 @@ var PushInteractor = module.exports = { }); }, bufferizeServerStatus : function(cb) { - var self = this; - this.ipm2.rpc.getMonitorData({}, function(err, processes) { if (!processes) return console.error('Cant access to getMonitorData RPC PM2 method'); var ret; - if ((ret = Filter.monitoring(processes, self.conf))) { - self.bufferData('monitoring', ret); + if ((ret = Filter.monitoring(processes, PushInteractor.conf))) { + PushInteractor.bufferData('monitoring', ret); } - if ((ret = Filter.status(processes, self.conf))) { - self.bufferData('status', ret); + if ((ret = Filter.status(processes, PushInteractor.conf))) { + PushInteractor.bufferData('status', ret); } - if (self.http_transactions && self.http_transactions.length > 0) { - self.send_buffer.push({ + if (PushInteractor.http_transactions && PushInteractor.http_transactions.length > 0) { + PushInteractor.send_buffer.push({ event : 'http:transaction', - transactions : self.http_transactions, - server_name : self.conf.MACHINE_NAME + transactions : PushInteractor.http_transactions, + server_name : PushInteractor.conf.MACHINE_NAME }); - self.http_transactions = []; + PushInteractor.http_transactions = []; } if (cb) return cb(); @@ -131,18 +122,16 @@ var PushInteractor = module.exports = { * @return */ sendData : function() { - var self = this; - this.bufferizeServerStatus(function() { var data = {}; if (process.env.NODE_ENV && process.env.NODE_ENV == 'test') { data = { - public_key : self.conf.PUBLIC_KEY, + public_key : PushInteractor.conf.PUBLIC_KEY, sent_at : new Date(), data : { - buffer : self.send_buffer, - server_name : self.conf.MACHINE_NAME + buffer : PushInteractor.send_buffer, + server_name : PushInteractor.conf.MACHINE_NAME } }; } @@ -153,51 +142,49 @@ var PushInteractor = module.exports = { */ var cipheredData = Cipher.cipherMessage(JSON.stringify({ - buffer : self.send_buffer, - server_name : self.conf.MACHINE_NAME - }), self.conf.SECRET_KEY); + buffer : PushInteractor.send_buffer, + server_name : PushInteractor.conf.MACHINE_NAME + }), PushInteractor.conf.SECRET_KEY); data = { - public_key : self.conf.PUBLIC_KEY, + public_key : PushInteractor.conf.PUBLIC_KEY, sent_at : Date.now(), data : cipheredData }; } - self.udpSocket.send(JSON.stringify(data)); + PushInteractor.udpSocket.send(JSON.stringify(data)); - debug('Buffer with length %d sent', self.send_buffer.length); + debug('Buffer with length %d sent', PushInteractor.send_buffer.length); data = null; - self.send_buffer = []; + PushInteractor.send_buffer = []; }); }, bufferData : function(event, packet) { - var self = this; - if (packet.process && !packet.server) { if (event == 'http:transaction') { - packet.data.data.process_id = Filter.getProcessID(self.conf.MACHINE_NAME, packet.process.name, packet.process.pm_id); + packet.data.data.process_id = Filter.getProcessID(PushInteractor.conf.MACHINE_NAME, packet.process.name, packet.process.pm_id); packet.data.data.process_name = packet.process.name; - return self.http_transactions.push(packet.data.data); + return PushInteractor.http_transactions.push(packet.data.data); } - self.send_buffer.push({ + PushInteractor.send_buffer.push({ at : Date.now(), event : event, data : packet.data || null, process : packet.process, - process_id : Filter.getProcessID(self.conf.MACHINE_NAME, packet.process.name, packet.process.pm_id), + process_id : Filter.getProcessID(PushInteractor.conf.MACHINE_NAME, packet.process.name, packet.process.pm_id), process_name : packet.process.name }); } else { - self.send_buffer.push({ + PushInteractor.send_buffer.push({ at : Date.now(), event : event, data : packet, - server_name : self.conf.MACHINE_NAME + server_name : PushInteractor.conf.MACHINE_NAME }); } diff --git a/lib/Satan.js b/lib/Satan.js index 13041e09..f0c65e2d 100644 --- a/lib/Satan.js +++ b/lib/Satan.js @@ -234,9 +234,6 @@ Satan.launchDaemon = function launchDaemon(cb) { var out = fs.openSync(cst.PM2_LOG_FILE_PATH, 'a'), err = fs.openSync(cst.PM2_LOG_FILE_PATH, 'a'); - // Pb node -> need full path - // Pb Inter - // <=> keymetrics var child = require('child_process').spawn('node', [SatanJS], { detached : true, cwd : process.cwd(), diff --git a/package.json b/package.json index def6e4d6..566bdaf2 100644 --- a/package.json +++ b/package.json @@ -67,7 +67,7 @@ } ], "homepage": "https://github.com/Unitech/pm2", - "description": "Modern CLI process manager for Node apps with a builtin load-balancer", + "description": "Modern CLI process manager for Node apps with a builtin load-balancer. Perfectly designed for microservices architecture.", "main": "index.js", "scripts": { "test": "bash test/main.sh && NODE_ENV=test mocha" @@ -81,7 +81,11 @@ "logs", "log", "json", + "express", + "hapi", + "kraken", "reload", + "microservice", "programmatic", "harmony", "node-pm2", From 840d760fd7ee6df3ec600ce6efe13f54a63125f2 Mon Sep 17 00:00:00 2001 From: tknew2 Date: Tue, 12 Aug 2014 15:52:32 +0200 Subject: [PATCH 14/69] add pack PM2_NODE_OPTIONS && fix harmony test --- lib/Satan.js | 8 +++++++- test/bash/harmony.sh | 6 ++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/lib/Satan.js b/lib/Satan.js index f0c65e2d..bb81cec6 100644 --- a/lib/Satan.js +++ b/lib/Satan.js @@ -230,11 +230,17 @@ Satan.launchDaemon = function launchDaemon(cb) { var SatanJS = p.resolve(p.dirname(module.filename), 'Satan.js'); var InteractorDaemonizer = require('./Interactor/InteractorDaemonizer.js'); + var node_args = []; var out = fs.openSync(cst.PM2_LOG_FILE_PATH, 'a'), err = fs.openSync(cst.PM2_LOG_FILE_PATH, 'a'); - var child = require('child_process').spawn('node', [SatanJS], { + if (process.env.PM2_NODE_OPTIONS) + node_args = node_args.concat(process.env.PM2_NODE_OPTIONS.split(' ')); + + node_args.push(SatanJS); + + var child = require('child_process').spawn('node', node_args, { detached : true, cwd : process.cwd(), env : util._extend({ diff --git a/test/bash/harmony.sh b/test/bash/harmony.sh index 9876e000..0c39e18a 100644 --- a/test/bash/harmony.sh +++ b/test/bash/harmony.sh @@ -16,15 +16,13 @@ $pm2 list $pm2 kill PM2_NODE_OPTIONS='--harmony' `pwd`/../../bin/pm2 start harmony.js -sleep 2 -should 'should not fail when passing harmony option to V8' 'errored' 0 +sleep 4 $pm2 list +should 'should not fail when passing harmony option to V8' 'restart_time: 0' 1 $pm2 kill - $pm2 start harmony.js --node-args="--harmony" sleep 8 $pm2 list should 'should not fail when passing node-args=harmony opts' 'errored' 0 -$pm2 list $pm2 kill From 2271bb808bc70f56b4ff9a90dba766b5516eefce Mon Sep 17 00:00:00 2001 From: tknew2 Date: Wed, 13 Aug 2014 01:22:47 +0200 Subject: [PATCH 15/69] test new protocol --- lib/God.js | 8 +- lib/God/ActionMethods.js | 27 +++-- lib/God/ForkMode.js | 6 +- lib/God/Reload.js | 6 +- lib/Interactor/Daemon.js | 4 +- lib/Interactor/InteractorDaemonizer.js | 2 +- lib/Satan.js | 2 +- package.json | 5 +- pm2-interface/.gitignore | 3 + pm2-interface/LICENSE | 13 +++ pm2-interface/README.md | 147 +++++++++++++++++++++++++ pm2-interface/constants.js | 12 ++ pm2-interface/example/basic.js | 48 ++++++++ pm2-interface/example/send_command.js | 27 +++++ pm2-interface/index.js | 3 + pm2-interface/lib/index.js | 123 +++++++++++++++++++++ pm2-interface/package.json | 42 +++++++ 17 files changed, 447 insertions(+), 31 deletions(-) create mode 100644 pm2-interface/.gitignore create mode 100644 pm2-interface/LICENSE create mode 100644 pm2-interface/README.md create mode 100644 pm2-interface/constants.js create mode 100644 pm2-interface/example/basic.js create mode 100644 pm2-interface/example/send_command.js create mode 100644 pm2-interface/index.js create mode 100644 pm2-interface/lib/index.js create mode 100644 pm2-interface/package.json diff --git a/lib/God.js b/lib/God.js index 1fcd31a5..e03be574 100644 --- a/lib/God.js +++ b/lib/God.js @@ -100,14 +100,14 @@ function handleExit(clu, exit_code) { proc.pm2_env.pm_exec_path, proc.pm2_env.unstable_restarts, proc.pm2_env.status); - God.bus.emit('process:exit:overlimit', { process : proc }); + God.bus.emit('process:exit:overlimit', { process : Common.serialize(proc) }); proc.pm2_env.unstable_restarts = 0; proc.pm2_env.created_at = null; overlimit = true; } } - God.bus.emit('process:exit', { process : proc }); + God.bus.emit('process:exit', { process : Common.serialize(proc) }); if (!stopping) proc.pm2_env.restart_time = proc.pm2_env.restart_time + 1; @@ -254,7 +254,7 @@ God.prepare = function prepare(env, cb) { return false; } - return God.executeApp(JSON.parse(JSON.stringify(env)), function(err, clu) { // deep copy + return God.executeApp(Common.serialize(env), function(err, clu) { // deep copy if (err) return ex(i - 1); arr.push(clu); return ex(i - 1); @@ -263,7 +263,7 @@ God.prepare = function prepare(env, cb) { } else { return God.executeApp(env, function(err, dt) { - cb(err, [dt]); + cb(err, [Common.serialize(dt)]); }); } return false; diff --git a/lib/God/ActionMethods.js b/lib/God/ActionMethods.js index f0a39ae3..e78175d0 100644 --- a/lib/God/ActionMethods.js +++ b/lib/God/ActionMethods.js @@ -14,6 +14,7 @@ var p = path; var cst = require('../../constants.js'); var pkg = require('../../package.json'); var pidusage = require('pidusage'); +var Common = require('../Common'); /** * Description @@ -144,7 +145,7 @@ module.exports = function(God) { God.startProcessId = function(id, cb) { if (!(id in God.clusters_db)) return cb(God.logAndGenerateError(id + ' id unknown'), {}); - God.bus.emit('process:start', { process : God.clusters_db[id] }); + God.bus.emit('process:start', { process : Common.serialize(God.clusters_db[id]) }); if (God.clusters_db[id].pm2_env.status == cst.ONLINE_STATUS) return cb(God.logAndGenerateError('process already online'), {}); return God.executeApp(God.clusters_db[id].pm2_env, cb); @@ -164,7 +165,7 @@ module.exports = function(God) { if (God.clusters_db[id].pm2_env.status == cst.STOPPED_STATUS) return cb(null, God.getFormatedProcesses()); - God.bus.emit('process:stop', { process : God.clusters_db[id] }); + God.bus.emit('process:stop', { process : Common.serialize(God.clusters_db[id]) }); var proc = God.clusters_db[id]; var timeout = null; @@ -243,16 +244,14 @@ module.exports = function(God) { if (!(id in God.clusters_db)) return cb(God.logAndGenerateError(id + ' id unknown'), {}); - var proc = God.clusters_db[id]; - - if (!proc || !proc.pm2_env) + if (!God.clusters_db[id] || !God.clusters_db[id].pm2_env) return cb(God.logAndGenerateError('Error when getting proc || proc.pm2_env'), {}); - proc.pm2_env.created_at = Date.now(); - proc.pm2_env.unstable_restarts = 0; - proc.pm2_env.restart_time = 0; + God.clusters_db[id].pm2_env.created_at = Date.now(); + God.clusters_db[id].pm2_env.unstable_restarts = 0; + God.clusters_db[id].pm2_env.restart_time = 0; - return cb(null, proc); + return cb(null, God.getFormatedProcesses()); }; /** @@ -265,10 +264,11 @@ module.exports = function(God) { */ God.deleteProcessId = function(id, cb) { if (God.clusters_db[id]) - God.bus.emit('process:delete', { process : God.clusters_db[id] }); + God.bus.emit('process:delete', { process : Common.serialize(God.clusters_db[id]) }); God.stopProcessId(id, function(err, dt) { if (err) return cb(God.logAndGenerateError(err), {}); + // ! transform to slow object delete God.clusters_db[id]; return cb(null, God.getFormatedProcesses()); }); @@ -290,7 +290,7 @@ module.exports = function(God) { var proc = God.clusters_db[id]; - God.bus.emit('process:restart', { process : proc }); + God.bus.emit('process:restart', { process : Common.serialize(proc) }); God.resetState(proc.pm2_env); @@ -369,7 +369,7 @@ module.exports = function(God) { var proc = God.clusters_db[id]; - God.bus.emit('process:send_signal', { process : proc }); + God.bus.emit('process:send_signal', { process : Common.serialize(proc) }); try { process.kill(God.clusters_db[id].process.pid, signal); @@ -425,6 +425,7 @@ module.exports = function(God) { async.eachLimit(processes, cst.CONCURRENT_ACTIONS, function(proc, next) { God.stopProcessId(proc.pm2_env.pm_id, function() { + // Slow object delete God.clusters_db[proc.pm2_env.pm_id]; return next(); }); @@ -471,7 +472,6 @@ module.exports = function(God) { * @return */ God.killMe = function(env, cb) { - God.deleteAll({}, function(err, processes) { console.log('pm2 has been killed by command line'); God.bus.emit('pm2:kill', { status : 'killed', @@ -481,7 +481,6 @@ module.exports = function(God) { cb(null, {msg : 'pm2 killed'}); process.exit(cst.SUCCESS_EXIT); }, 800); - }); }; diff --git a/lib/God/ForkMode.js b/lib/God/ForkMode.js index 987f9964..f0ef3f21 100644 --- a/lib/God/ForkMode.js +++ b/lib/God/ForkMode.js @@ -118,7 +118,7 @@ module.exports = function ForkMode(God) { stderr.write(log_data); God.bus.emit('log:err', { - process : cspr, + process : Common.serialize(cspr), data : data.toString() }); }); @@ -132,14 +132,14 @@ module.exports = function ForkMode(God) { stdout.write(log_data); God.bus.emit('log:out', { - process : cspr, + process : Common.serialize(cspr), data : data.toString() }); }); cspr.on('message', function forkMessage(data) { God.bus.emit(data.type ? data.type : 'process:msg', { - process : cspr, + process : Common.serialize(cspr), data : data }); }); diff --git a/lib/God/Reload.js b/lib/God/Reload.js index e72a6940..f3153c00 100644 --- a/lib/God/Reload.js +++ b/lib/God/Reload.js @@ -8,7 +8,7 @@ var async = require('async'); var cst = require('../../constants.js'); - +var Common = require('../Common'); /** * softReload will wait permission from process to exit * @method softReload @@ -31,7 +31,7 @@ function softReload(God, id, cb) { var new_env = JSON.parse(JSON.stringify(old_worker.pm2_env)); new_env.restart_time += 1; - God.bus.emit('process:start_soft_reload', { process : old_worker }); + God.bus.emit('process:start_soft_reload', { process : Common.serialize(old_worker) }); // Reset created_at and unstable_restarts God.resetState(new_env); @@ -123,7 +123,7 @@ function hardReload(God, id, cb) { var new_env = JSON.parse(JSON.stringify(old_worker.pm2_env)); new_env.restart_time += 1; - God.bus.emit('process:start_reload', { process : old_worker }); + God.bus.emit('process:start_reload', { process : Common.serialize(old_worker) }); // Reset created_at and unstable_restarts God.resetState(new_env); diff --git a/lib/Interactor/Daemon.js b/lib/Interactor/Daemon.js index b55a4529..c35f6dc0 100644 --- a/lib/Interactor/Daemon.js +++ b/lib/Interactor/Daemon.js @@ -1,7 +1,7 @@ var fs = require('fs'); -var ipm2 = require('pm2-interface'); -var rpc = require('pm2-axon-rpc'); +var ipm2 = require('../../pm2-interface'); +var rpc = require('axon-rpc'); var axon = require('axon'); var debug = require('debug')('interface:driver'); // Interface var chalk = require('chalk'); diff --git a/lib/Interactor/InteractorDaemonizer.js b/lib/Interactor/InteractorDaemonizer.js index cafe6866..c6a7c26c 100644 --- a/lib/Interactor/InteractorDaemonizer.js +++ b/lib/Interactor/InteractorDaemonizer.js @@ -5,7 +5,7 @@ var fs = require('fs'); var cst = require('../../constants.js'); var path = require('path'); var util = require('util'); -var rpc = require('pm2-axon-rpc'); +var rpc = require('axon-rpc'); var Common = require('../Common'); var debug = require('debug')('interface:daemon'); var axon = require('axon'); diff --git a/lib/Satan.js b/lib/Satan.js index bb81cec6..afdc60b0 100644 --- a/lib/Satan.js +++ b/lib/Satan.js @@ -9,7 +9,7 @@ * Dependencies */ -var rpc = require('pm2-axon-rpc'); +var rpc = require('axon-rpc'); var axon = require('axon'); var rep = axon.socket('rep'); var req = axon.socket('req'); diff --git a/package.json b/package.json index 566bdaf2..e64fa625 100644 --- a/package.json +++ b/package.json @@ -118,7 +118,7 @@ "dependencies": { "async": "~0.9.0", "axm": "~0.1.7", - "axon": "~1.0.0", + "axon": "~2.0.0", "chalk": "^0.4.0", "chokidar": "~0.8.2", "cli-table": "~0.3.0", @@ -132,9 +132,8 @@ "nssocket": "~0.5.1", "punt" : "~2.2.0", "pidusage": "~0.0.7", - "pm2-axon-rpc": "~0.0.2", + "axon-rpc": "~0.0.3", "pm2-deploy": "~0.0.4", - "pm2-interface": "~0.1.3", "pm2-multimeter": "~0.1.2", "moment" : "~2.7.0", "uid-number": "0.0.5" diff --git a/pm2-interface/.gitignore b/pm2-interface/.gitignore new file mode 100644 index 00000000..9644e464 --- /dev/null +++ b/pm2-interface/.gitignore @@ -0,0 +1,3 @@ +node_modules +*.log +*.pid diff --git a/pm2-interface/LICENSE b/pm2-interface/LICENSE new file mode 100644 index 00000000..d3445e5b --- /dev/null +++ b/pm2-interface/LICENSE @@ -0,0 +1,13 @@ +Copyright [2013] [Strzelewicz Alexandre] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/pm2-interface/README.md b/pm2-interface/README.md new file mode 100644 index 00000000..eb6f1dba --- /dev/null +++ b/pm2-interface/README.md @@ -0,0 +1,147 @@ +# pm2-interface (for pm2 version >= 0.6.0) + +pm2-interface permits you to interact with [PM2](https://github.com/Unitech/pm2) the process manager for NodeJS. + +You can **control all exposed methods** by the pm2 deamon [God](https://github.com/Unitech/pm2/blob/master/lib/God.js) and also **receive real time notifications** for example for a process who got an unexpectedException, who's starting/stopping. + +## RPC methods + +- `ipm2.rpc.prepareJson(json_app, cwd, fn)` send a JSON configuration to start app(s) in the cwd folder +- `ipm2.rpc.getMonitorData({}, fn)` receive all related informations about supervised process (cpu/ram/pid...) +- `ipm2.rpc.getSystemData({}, fn)` receive all data about process managed by pm2 and computer resources usage +- `ipm2.rpc.startProcessId(integer, fn)` start a process by id (pm_id) who his state is stopped +- `ipm2.rpc.stopProcessId(integer, fn)` stop a process by id (pm_id) +- `ipm2.rpc.stopAll({}, fn)` stop all process +- `ipm2.rpc.reload(data, fn)` reload all apps (only for networked apps) +- `ipm2.rpc.killMe(data, fn)` kill pm2 daemon +- `ipm2.rpc.findByScript(string, fn)` send you back the informations about a specific process +- `ipm2.rpc.restartProcessId(integer, fn)` restart a process by id (pm_id) +- `ipm2.rpc.restartProcessName(string, fn)` restart all processes who have the given name +- `ipm2.rpc.deleteProcess(string, fn)` stop and delete all processes from the pm2 database +- `ipm2.rpc.deleteAll(data, fn)` stop and delete all processes +- `ipm2.rpc.msgProcess(opts, fn)` send msg `opts.msg` to process at `opts.id` or all processes with `opts.name` + +## Notifications + +- `process:online` when a process is started/restarted +- `process:exit` when a process is exited +- `process:exception` When a process has received an uncaughtException + +**Advanced feature** : You can use `process.send({ type : 'my:message', data : {}})` in your Node apps. When you emit a message, they will be redirected to pm2 and sent back to the pm2-interface bus. This can be coupled with `rpc.msgProcess(opts, fn)` to allow 2-way communication between managed processes and pm2-interface - see second Example below. + +> It should be noted that `process.send` will be undefined if there is no parent process. Therefore a check of `if (process.send)` may be advisable. + +## Example + +```javascript +var ipm2 = require('pm2-interface')(); + +ipm2.on('ready', function() { + console.log('Connected to pm2'); + + ipm2.bus.on('*', function(event, data){ + console.log(event, data.pm2_env.name); + }); + + setTimeout(function() { + ipm2.rpc.restartProcessId(0, function(err, dt) { + console.log(dt); + }); + }, 2000); + + + ipm2.rpc.getMonitorData({}, function(err, dt) { + console.log(dt); + }); +}); +``` + +## Example 2-way + +in your process script +```javascript +if (send in process) { + process.on("message", function (msg) { + if ( "type" in msg && msg.type === "god:heap" ) { + var heap = process.memoryUsage().heapUsed + process.send({type:"process:heap", heap:heap}) + } + }) +} + +var myMemoryLeak = [] + +setInterval( function () { + var object = {} + for (var i = 0; i < 10000; i++) { + object["key"+i] = Math.random().toString(36).substring(7) + } + + myMemoryLeak.push(object) + +}, Math.round(Math.random()*2000)) +``` +in monitoring script +```javascript +var ipm2 = require('pm2-interface')() + +ipm2.on('ready', function() { + + console.log('Connected to pm2') + + ipm2.bus.on('process:heap', function(data){ + console.log("process heap:", data) + }) + + + setInterval( function () { + var msg = {type:"god:heap"} // god: is arbitrary and used to distinguish incoming & outgoing msgs + ipm2.rpc.msgProcess({name:"worker", msg:msg}, function (err, res) { + if (err) console.log(err) + else console.log(res) + }) + }, 5000) +}) +``` +Start pm2 and monitoring script + output: +```shell +pm2 start worker.js -i 3 --name worker +node monitor.js + +sent 3 messages # coming from the console.log(res) +process heap: { pm_id: 0, msg: { type: 'process:heap', heap: 43416064 } } +process heap: { pm_id: 1, msg: { type: 'process:heap', heap: 18373704 } } +process heap: { pm_id: 2, msg: { type: 'process:heap', heap: 80734256 } } +sent 3 messages +process heap: { pm_id: 0, msg: { type: 'process:heap', heap: 61994096 } } +process heap: { pm_id: 1, msg: { type: 'process:heap', heap: 22437400 } } +process heap: { pm_id: 2, msg: { type: 'process:heap', heap: 116622432 } } +sent 3 messages +process heap: { pm_id: 0, msg: { type: 'process:heap', heap: 79641168 } } +process heap: { pm_id: 1, msg: { type: 'process:heap', heap: 32260112 } } +process heap: { pm_id: 2, msg: { type: 'process:heap', heap: 156047904 } } + +pm2 delete all +``` + +## Disconnect + +Since pm2-interface interacts with PM2 via sockets, any script which uses pm2-interface will remain alive even when the node.js event loop is empty. `process.exit()` can be called to forcefully exit, or, if your script has finished making calls to PM2, you may call `ipm2.disconnect()` to disconnect the socket connections and allow node to exit automatically. + +```javascript +ipm2.on('ready', function() { + + // ... + + ipm2.disconnect(); +}); +``` + +> Calling `disconnect()` means "I am entirely done interacting with PM2." You will no longer be able to receive messages on `ipm2.bus` or send requests on `ipm2.rpc`. To reconnect you must completely start over with a new ipm2 object. + +## Ideas + +- Catching exceptions and fowarding them by mail +- A web interface to control PM2 + +## Apache v2 License diff --git a/pm2-interface/constants.js b/pm2-interface/constants.js new file mode 100644 index 00000000..cb0c2cf9 --- /dev/null +++ b/pm2-interface/constants.js @@ -0,0 +1,12 @@ +// +// Modifying these values break tests and can break +// pm2-interface intercommunication (because of ports) +// + +module.exports = { + DAEMON_BIND_HOST : 'localhost', + DAEMON_RPC_PORT : 6666, // RPC commands + DAEMON_PUB_PORT : 6667, // Realtime events + SUCCESS_EXIT : 0, + ERROR_EXIT : 1 +}; diff --git a/pm2-interface/example/basic.js b/pm2-interface/example/basic.js new file mode 100644 index 00000000..186e1a0a --- /dev/null +++ b/pm2-interface/example/basic.js @@ -0,0 +1,48 @@ +var ipm2 = require('..'); + +var ipm2a = ipm2({bind_host : 'localhost'}); + + + +ipm2a.on('ready', function() { + console.log('Connected to pm2'); + + ipm2a.bus.on('*', function(event, data){ + console.log(event); + }); + + // ipm2a.bus.on('log:err', function(event, data){ + // console.log(event, data); + // }); + + // ipm2a.bus.on('log:out', function(event, data){ + // console.log(event, data); + // }); + + + ipm2a.on('rpc_sock:reconnecting', function() { + console.log('rpc_sock:reconnecting'); + }); + ipm2a.on('sub_sock:ready', function() { + console.log('sub_sock:ready'); + }); + ipm2a.on('sub_sock:closed', function() { + console.log('sub_sock:closed'); + }); + + ipm2a.on('sub_sock:reconnecting', function() { + console.log('sub_sock:reconnecting'); + }); + + + // setTimeout(function() { + // ipm2.rpc.restartProcessId(0, function(err, dt) { + // console.log(dt); + // }); + // }, 2000); + + + // ipm2.rpc.getMonitorData({}, function(err, dt) { + // console.log(dt); + // }); +}); diff --git a/pm2-interface/example/send_command.js b/pm2-interface/example/send_command.js new file mode 100644 index 00000000..409ec094 --- /dev/null +++ b/pm2-interface/example/send_command.js @@ -0,0 +1,27 @@ + +var ipm2 = require('..'); + +var ipm2a = ipm2({bind_host : 'localhost'}); + + + +ipm2a.on('ready', function() { + console.log('Connected to pm2'); + + ipm2a.bus.on('*', function(event, data){ + console.log(event); + }); + + setInterval(function() { + + var msg = {type:"god:heap"}; + + ipm2a.rpc.msgProcess({name:"expose_method", msg:msg}, function (err, res) { + if (err) console.log(err) + else console.log(res) + console.log(arguments); + }) + + }, 2000); + +}); diff --git a/pm2-interface/index.js b/pm2-interface/index.js new file mode 100644 index 00000000..8774deca --- /dev/null +++ b/pm2-interface/index.js @@ -0,0 +1,3 @@ + +module.exports = exports = require("./lib"); + diff --git a/pm2-interface/lib/index.js b/pm2-interface/lib/index.js new file mode 100644 index 00000000..644cec65 --- /dev/null +++ b/pm2-interface/lib/index.js @@ -0,0 +1,123 @@ +/** + * Dependencies + */ + +var axon = require('axon'); +var cst = require('../constants.js'); +var sys = require('sys'); +var rpc = require('axon-rpc'); +var log = require('debug')('pm2:interface'); +var EventEmitter = require('events').EventEmitter; + +/** + * Export with conf + */ +module.exports = function(opts){ + var sub_port = opts && opts.sub_port || cst.DAEMON_PUB_PORT; + var rpc_port = opts && opts.rpc_port || cst.DAEMON_RPC_PORT; + var bind_host = opts && opts.bind_host || cst.DAEMON_BIND_HOST; + + return new IPM2(sub_port, rpc_port, bind_host); +}; + +/** + * IPM2, Pm2 Interface + */ + +var IPM2 = function(sub_port, rpc_port, bind_host) { + //if (!(this instanceof Bash)) return new Bash(opts); + var self = this; + + EventEmitter.call(this); + + this.sub_port = sub_port; + this.rpc_port = rpc_port; + this.bind_host = bind_host; + + var sub = axon.socket('sub-emitter'); + var sub_sock = this.sub_sock = sub.connect(sub_port, bind_host); + this.bus = sub; + + var req = axon.socket("req"); + var rpc_sock = this.rpc_sock = req.connect(rpc_port, bind_host); + this.rpc_client = new rpc.Client(req); + + this.rpc = {}; + + /** + * Disconnect socket connections. This will allow Node to exit automatically. + * Further calls to PM2 from this object will throw an error. + */ + this.disconnect = function () { + self.sub_sock.close(); + self.rpc_sock.close(); + }; + + /** + * Generate method by requesting exposed methods by PM2 + * You can now control/interact with PM2 + */ + var generateMethods = function(cb) { + log('Requesting and generating RPC methods'); + self.rpc_client.methods(function(err, methods) { + Object.keys(methods).forEach(function(key) { + var method_signature, md; + method_signature = md = methods[key]; + + log('+-- Creating %s method', md.name); + + (function(name) { + self.rpc[name] = function() { + log(name); + var args = Array.prototype.slice.call(arguments); + args.unshift(name); + self.rpc_client.call.apply(self.rpc_client, args); + }; + })(md.name); + + }); + return cb(); + }); + }; + + /** + * Waiting to connect to sub channel + * and RPC + */ + + rpc_sock.on('connect', function() { + log('rpc_sock:ready'); + self.emit('rpc_sock:ready'); + generateMethods(function() { + self.emit('ready'); + }); + }); + + rpc_sock.on('close', function() { + log('rpc_sock:closed'); + self.emit('close'); + }); + + rpc_sock.on('reconnect attempt', function() { + log('rpc_sock:reconnecting'); + self.emit('reconnecting'); + }); + + sub_sock.on('connect', function() { + log('sub_sock ready'); + self.emit('sub_sock:ready'); + }); + + sub_sock.on('close', function() { + log('sub_sock:closed'); + self.emit('closed'); + }); + + sub_sock.on('reconnect attempt', function() { + log('sub_sock:reconnecting'); + self.emit('reconnecting'); + }); + +}; + +sys.inherits(IPM2, EventEmitter); diff --git a/pm2-interface/package.json b/pm2-interface/package.json new file mode 100644 index 00000000..161f0ae5 --- /dev/null +++ b/pm2-interface/package.json @@ -0,0 +1,42 @@ +{ + "name": "pm2-interface", + "version": "0.1.4", + "description": "Interact with pm2 via RPC and receive real time notifications", + "author": { + "name": "Strzelewicz Alexandre", + "email": "as@unitech.io", + "url": "http://unitech.io" + }, + "engines" : { + "node" : ">=0.8" + }, + "main": "index.js", + "dependencies" : { + "axon" : "2.0.0", + "axon-rpc" : "0.0.3", + "debug" : "*" + }, + "keywords": [ + "cli", + "fault tolerant", + "sysadmin", + "tools", + "pm2", + "node-pm2", + "monitoring", + "process manager", + "forever", + "process configuration", + "clustering", + "cluster cli", + "cluster", + "cron", + "devops", + "dev ops" + ], + "repository": { + "type" : "git", + "url" : "git://github.com/Unitech/pm2-interface.git" + }, + "license": "Apache v2" +} From ff0b6e3c8455fac0ed91a3c692f7060e0f1e1fb0 Mon Sep 17 00:00:00 2001 From: Alexandre Strzelewicz Date: Wed, 13 Aug 2014 16:33:51 +0200 Subject: [PATCH 16/69] Remove wildcard support eventemitter2 --- lib/God.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/God.js b/lib/God.js index 1fcd31a5..c6e5f001 100644 --- a/lib/God.js +++ b/lib/God.js @@ -30,7 +30,7 @@ var God = module.exports = { next_id : 0, clusters_db : {}, bus : new EventEmitter2({ - wildcard: true, + wildcard: false, delimiter: ':', maxListeners: 1000 }) From ca241aa794b984c61037ae6247d85b2020af52bd Mon Sep 17 00:00:00 2001 From: Alexandre Strzelewicz Date: Wed, 13 Aug 2014 16:36:11 +0200 Subject: [PATCH 17/69] Keep handle.owner --- lib/God/ClusterMode.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/God/ClusterMode.js b/lib/God/ClusterMode.js index 264d49a1..e11b3228 100644 --- a/lib/God/ClusterMode.js +++ b/lib/God/ClusterMode.js @@ -71,8 +71,8 @@ module.exports = function ClusterMode(God) { } }); - // Avoid circular dependency - delete clu.process._handle.owner; + + //delete clu.process._handle.owner; return cb(null, clu); return false; From 43b52917cb6beb613a35fa75e0910be0bd216564 Mon Sep 17 00:00:00 2001 From: Alexandre Strzelewicz Date: Wed, 13 Aug 2014 16:38:53 +0200 Subject: [PATCH 18/69] Force nullification of circular references --- lib/God.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/God.js b/lib/God.js index c6e5f001..5fad78b3 100644 --- a/lib/God.js +++ b/lib/God.js @@ -203,6 +203,10 @@ God.executeApp = function executeApp(env, cb) { if (old_env) { old_env = null; + if (God.clusters_db[clu.pm2_env.pm_id].process._handle) { + God.clusters_db[clu.pm2_env.pm_id].process._handle.owner = null; + God.clusters_db[clu.pm2_env.pm_id].process._handle = null; + } God.clusters_db[clu.pm2_env.pm_id] = null; } From ee9b96a92093dd61458f33d6303aabd9275d5656 Mon Sep 17 00:00:00 2001 From: Alexandre Strzelewicz Date: Wed, 13 Aug 2014 16:41:29 +0200 Subject: [PATCH 19/69] Return right value for axm:actions --- lib/Satan.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/lib/Satan.js b/lib/Satan.js index bb81cec6..4bab789d 100644 --- a/lib/Satan.js +++ b/lib/Satan.js @@ -195,10 +195,9 @@ Satan.remoteWrapper = function() { }); } - if (exists === true) - return true; - else - return God.clusters_db[pm2_env.pm_id].pm2_env.axm_actions.push(msg.data.data); + if (exists === false) + God.clusters_db[pm2_env.pm_id].pm2_env.axm_actions.push(msg.data.data); + return false; }); /** From 64061f0f488d598bc03b2ccca1843a6ad8fcfb33 Mon Sep 17 00:00:00 2001 From: Alexandre Strzelewicz Date: Wed, 13 Aug 2014 20:50:23 +0200 Subject: [PATCH 20/69] return false if cannot exec in fork mode + no cb --- lib/God.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/God.js b/lib/God.js index 5fad78b3..596b4cff 100644 --- a/lib/God.js +++ b/lib/God.js @@ -169,7 +169,8 @@ God.executeApp = function executeApp(env, cb) { */ God.forkMode(env_copy, function forkMode(err, clu) { if (cb && err) return cb(err); - + if (err) return false; + var old_env = God.clusters_db[clu.pm2_env.pm_id]; if (old_env) old_env = null; From 2a35f5c2edc3b6c283b3c860cfc535987862a910 Mon Sep 17 00:00:00 2001 From: Alexandre Strzelewicz Date: Wed, 13 Aug 2014 22:04:14 +0200 Subject: [PATCH 21/69] nullify var --- lib/God/Methods.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/God/Methods.js b/lib/God/Methods.js index 0d21e435..2c757e7e 100644 --- a/lib/God/Methods.js +++ b/lib/God/Methods.js @@ -60,6 +60,7 @@ module.exports = function(God) { }); } } + db = null; return arr; }; From d044b2c73de99387550a4b3316037975025c387e Mon Sep 17 00:00:00 2001 From: tknew2 Date: Thu, 14 Aug 2014 11:48:33 +0200 Subject: [PATCH 22/69] enable node-args and PM2_NODE_OPTIONS for Fork mode --- lib/God/ForkMode.js | 19 +++++++++++++++---- test/bash/harmony.sh | 25 ++++++++++++++++++++----- 2 files changed, 35 insertions(+), 9 deletions(-) diff --git a/lib/God/ForkMode.js b/lib/God/ForkMode.js index 987f9964..af959d25 100644 --- a/lib/God/ForkMode.js +++ b/lib/God/ForkMode.js @@ -28,7 +28,8 @@ module.exports = function ForkMode(God) { * @return */ God.forkMode = function forkMode(pm2_env, cb) { - var command, args; + var command = ''; + var args = []; log('Entering in fork mode'); var spawn = require('child_process').spawn; @@ -38,16 +39,26 @@ module.exports = function ForkMode(God) { if (interpreter !== 'none') { command = interpreter; - args = [pm2_env.pm_exec_path]; + + if (pm2_env.nodeArgs && Array.isArray(pm2_env.nodeArgs)) { + args = args.concat(pm2_env.nodeArgs); + } + + if (process.env.PM2_NODE_OPTIONS) { + args = args.concat(process.env.PM2_NODE_OPTIONS.split(' ')); + } + + args.push(pm2_env.pm_exec_path); } else { command = pm2_env.pm_exec_path; args = [ ]; } - // Concat args if present - if (pm2_env.args) + if (pm2_env.args) { args = args.concat(eval((pm2_env.args))); + } + var stdout, stderr; var outFile = pm2_env.pm_out_log_path; diff --git a/test/bash/harmony.sh b/test/bash/harmony.sh index 0c39e18a..dd9433b2 100644 --- a/test/bash/harmony.sh +++ b/test/bash/harmony.sh @@ -9,20 +9,35 @@ $pm2 kill echo "################ HARMONY ES6" $pm2 start harmony.js -sleep 8 +sleep 2 $pm2 list -should 'should fail when trying to launch pm2 without harmony option' 'errored' 1 +ishould 'should not fail when passing harmony option to V8' 'restart_time: 0' 1 $pm2 list $pm2 kill PM2_NODE_OPTIONS='--harmony' `pwd`/../../bin/pm2 start harmony.js -sleep 4 +sleep 2 $pm2 list should 'should not fail when passing harmony option to V8' 'restart_time: 0' 1 $pm2 kill $pm2 start harmony.js --node-args="--harmony" -sleep 8 +sleep 2 $pm2 list -should 'should not fail when passing node-args=harmony opts' 'errored' 0 +should 'should not fail when passing node-args=harmony opts' 'restart_time: 0' 1 +$pm2 kill + +echo "################ HARMONY / NODEARGS ES6 FORK MODE" + +$pm2 start harmony.js --node-args="--harmony" -x +sleep 2 +$pm2 list +should 'should not fail when passing node-args=harmony opts' 'restart_time: 0' 1 +$pm2 kill + + +PM2_NODE_OPTIONS='--harmony' $pm2 start harmony.js -x +sleep 2 +$pm2 list +should 'should not fail when passing node-args=harmony opts' 'restart_time: 0' 1 $pm2 kill From 58292a488c251f832e0b9e1b472aaa50562825d1 Mon Sep 17 00:00:00 2001 From: tknew2 Date: Thu, 14 Aug 2014 12:12:20 +0200 Subject: [PATCH 23/69] 0.10.0 CHANGELOG --- CHANGELOG.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3812e88d..5d01d5ff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,21 @@ +# 0.10.0 - PM2 Hellfire release + +- PM2 Battle testing (https://docs.google.com/spreadsheets/d/1z7hJwI_TBQslkIS07YjgCGlpu8eHy2Wc0Bd5qDNnICE/edit#gid=925366936) +- Coffeescript not enabled by default anymore (enhance memory usage) +- PM2 Programmatic interface enhanced +- Daemon fork system enhanced +- PM2 hearth refactor +- Watch system avoid ghost processes +- Memory leak fixes +- Better performance on interface +- Fix tests +- Enable PM2_NODE_OPTIONS and node-args for fork mode +- Dependencies updated +- Faster monitoring system +- AXM actions unification +- Socket errors handled + # 0.9.6 - 0.9.5 - 0.9.4 - Bash test auto exit when failure From 0bd19a030e429222f50df1bdf7055813492ea004 Mon Sep 17 00:00:00 2001 From: abluchet Date: Thu, 14 Aug 2014 12:25:47 +0200 Subject: [PATCH 24/69] Removed time from pm2 logs #484 #293 --- lib/Log.js | 8 +- lib/dateformat.js | 189 ---------------------------------------------- 2 files changed, 2 insertions(+), 195 deletions(-) delete mode 100644 lib/dateformat.js diff --git a/lib/Log.js b/lib/Log.js index fd6bbcc4..fa9676c2 100644 --- a/lib/Log.js +++ b/lib/Log.js @@ -2,7 +2,6 @@ // Display a file in streaming // var fs = require('fs'); -var dateFormat = require('./dateformat.js'); var colors = [ '\x1B[34m', // blue @@ -89,9 +88,6 @@ function print_data(odb, title, data) { lines.forEach(function(l) { if (l) - console.log(odb.color + '[%s %s]\x1B[39m %s', - title, - dateFormat(Date.now(), "isoDateTime"), - l); + console.log(odb.color + '[%s]\x1B[39m %s', title, l); }); -}; +} diff --git a/lib/dateformat.js b/lib/dateformat.js deleted file mode 100644 index d087fd10..00000000 --- a/lib/dateformat.js +++ /dev/null @@ -1,189 +0,0 @@ -/* - * Date Format 1.2.3 - * (c) 2007-2009 Steven Levithan - * MIT license - * - * Includes enhancements by Scott Trenda - * and Kris Kowal - * - * Accepts a date, a mask, or a date and a mask. - * Returns a formatted version of the given date. - * The date defaults to the current date/time. - * The mask defaults to dateFormat.masks.default. - */ - -var dateFormat = function () { - /** - * Description - * @method getDayOfWeek - * @param {} date - * @return dow - */ - var token = /d{1,4}|m{1,4}|yy(?:yy)?|([HhMsTt])\1?|[LloSZWN]|"[^"]*"|'[^']*'/g, - timezone = /\b(?:[PMCEA][SDP]T|(?:Pacific|Mountain|Central|Eastern|Atlantic) (?:Standard|Daylight|Prevailing) Time|(?:GMT|UTC)(?:[-+]\d{4})?)\b/g, - timezoneClip = /[^-+\dA-Z]/g, - pad = function (val, len) { - val = String(val); - len = len || 2; - while (val.length < len) val = "0" + val; - return val; - }, - /** - * Get the ISO 8601 week number - * Based on comments from - * http://techblog.procurios.nl/k/n618/news/view/33796/14863/Calculate-ISO-8601-week-and-year-in-javascript.html - */ - getWeek = function (date) { - // Remove time components of date - var targetThursday = new Date(date.getFullYear(), date.getMonth(), date.getDate()); - - // Change date to Thursday same week - targetThursday.setDate(targetThursday.getDate() - ((targetThursday.getDay() + 6) % 7) + 3); - - // Take January 4th as it is always in week 1 (see ISO 8601) - var firstThursday = new Date(targetThursday.getFullYear(), 0, 4); - - // Change date to Thursday same week - firstThursday.setDate(firstThursday.getDate() - ((firstThursday.getDay() + 6) % 7) + 3); - - // Check if daylight-saving-time-switch occured and correct for it - var ds = targetThursday.getTimezoneOffset() - firstThursday.getTimezoneOffset(); - targetThursday.setHours(targetThursday.getHours() - ds); - - // Number of weeks between target Thursday and first Thursday - var weekDiff = (targetThursday - firstThursday) / (86400000*7); - return 1 + weekDiff; - }, - - /** - * Get ISO-8601 numeric representation of the day of the week - * 1 (for Monday) through 7 (for Sunday) - */ - - getDayOfWeek = function(date){ - var dow = date.getDay(); - if(dow === 0) dow = 7; - return dow; - }; - - // Regexes and supporting functions are cached through closure - return function (date, mask, utc, gmt) { - var dF = dateFormat; - - // You can't provide utc if you skip other args (use the "UTC:" mask prefix) - if (arguments.length == 1 && Object.prototype.toString.call(date) == "[object String]" && !/\d/.test(date)) { - mask = date; - date = undefined; - } - - date = date || new Date; - - if(!(date instanceof Date)) { - date = new Date(date); - } - - if (isNaN(date)) { - throw TypeError("Invalid date"); - } - - mask = String(dF.masks[mask] || mask || dF.masks["default"]); - - // Allow setting the utc/gmt argument via the mask - var maskSlice = mask.slice(0, 4); - if (maskSlice == "UTC:" || maskSlice == "GMT:") { - mask = mask.slice(4); - utc = true; - if (maskSlice == "GMT:") { - gmt = true; - } - } - - var _ = utc ? "getUTC" : "get", - d = date[_ + "Date"](), - D = date[_ + "Day"](), - m = date[_ + "Month"](), - y = date[_ + "FullYear"](), - H = date[_ + "Hours"](), - M = date[_ + "Minutes"](), - s = date[_ + "Seconds"](), - L = date[_ + "Milliseconds"](), - o = utc ? 0 : date.getTimezoneOffset(), - W = getWeek(date), - N = getDayOfWeek(date), - flags = { - d: d, - dd: pad(d), - ddd: dF.i18n.dayNames[D], - dddd: dF.i18n.dayNames[D + 7], - m: m + 1, - mm: pad(m + 1), - mmm: dF.i18n.monthNames[m], - mmmm: dF.i18n.monthNames[m + 12], - yy: String(y).slice(2), - yyyy: y, - h: H % 12 || 12, - hh: pad(H % 12 || 12), - H: H, - HH: pad(H), - M: M, - MM: pad(M), - s: s, - ss: pad(s), - l: pad(L, 3), - L: pad(L > 99 ? Math.round(L / 10) : L), - t: H < 12 ? "a" : "p", - tt: H < 12 ? "am" : "pm", - T: H < 12 ? "A" : "P", - TT: H < 12 ? "AM" : "PM", - Z: gmt ? "GMT" : utc ? "UTC" : (String(date).match(timezone) || [""]).pop().replace(timezoneClip, ""), - o: (o > 0 ? "-" : "+") + pad(Math.floor(Math.abs(o) / 60) * 100 + Math.abs(o) % 60, 4), - S: ["th", "st", "nd", "rd"][d % 10 > 3 ? 0 : (d % 100 - d % 10 != 10) * d % 10], - W: W, - N: N - }; - - return mask.replace(token, function ($0) { - return $0 in flags ? flags[$0] : $0.slice(1, $0.length - 1); - }); - }; -}(); - -// Some common format strings -dateFormat.masks = { - "default": "ddd mmm dd yyyy HH:MM:ss", - shortDate: "m/d/yy", - mediumDate: "mmm d, yyyy", - longDate: "mmmm d, yyyy", - fullDate: "dddd, mmmm d, yyyy", - shortTime: "h:MM TT", - mediumTime: "h:MM:ss TT", - longTime: "h:MM:ss TT Z", - isoDate: "yyyy-mm-dd", - isoTime: "HH:MM:ss", - isoDateTime: "yyyy-mm-dd'T'HH:MM:ss", - isoUtcDateTime: "UTC:yyyy-mm-dd'T'HH:MM:ss'Z'", - expiresHeaderFormat: "ddd, dd mmm yyyy HH:MM:ss Z" -}; - -// Internationalization strings -dateFormat.i18n = { - dayNames: [ - "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", - "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" - ], - monthNames: [ - "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", - "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" - ] -}; - -/* -// For convenience... -Date.prototype.format = function (mask, utc) { - return dateFormat(this, mask, utc); -}; -*/ - -if (typeof exports !== "undefined") { - module.exports = dateFormat; -} From 67a00e5fd44d64b1c8f8645daec3fa36cfd894b0 Mon Sep 17 00:00:00 2001 From: tknew2 Date: Thu, 14 Aug 2014 14:00:20 +0200 Subject: [PATCH 25/69] flatten output --- bin/pm2 | 24 +++++++++--------- constants.js | 4 +-- lib/CLI.js | 35 ++++++++++++++------------ lib/Interactor/InteractorDaemonizer.js | 6 ++--- 4 files changed, 35 insertions(+), 34 deletions(-) diff --git a/bin/pm2 b/bin/pm2 index df08d476..0a73d21c 100755 --- a/bin/pm2 +++ b/bin/pm2 @@ -508,15 +508,15 @@ process.once('satan:client:ready', function() { }); })(); -(function testHarmony() { - // - // Harmony test - // - try { - var assert = require('assert') - , s = new Set(); - s.add('a'); - assert.ok(s.has('a')); - console.log('● ES6 mode'.green); - } catch(e) {} -})(); +// (function testHarmony() { +// // +// // Harmony test +// // +// try { +// var assert = require('assert') +// , s = new Set(); +// s.add('a'); +// assert.ok(s.has('a')); +// console.log('● ES6 mode'.green); +// } catch(e) {} +// })(); diff --git a/constants.js b/constants.js index 847f6bab..f41f90d7 100644 --- a/constants.js +++ b/constants.js @@ -36,8 +36,8 @@ var default_conf = { DEBUG : process.env.PM2_DEBUG || false, WEB_INTERFACE : parseInt(process.env.PM2_API_PORT) || 9615, MODIFY_REQUIRE : process.env.PM2_MODIFY_REQUIRE || false, - PREFIX_MSG : '\x1B[32mPM2 \x1B[39m', - PREFIX_MSG_ERR : '\x1B[31mPM2 [ERROR] \x1B[39m', + PREFIX_MSG : '\x1B[32m[PM2] \x1B[39m', + PREFIX_MSG_ERR : '\x1B[31m[PM2] [ERROR] \x1B[39m', SAMPLE_FILE_PATH : '../lib/sample.json', CENTOS_STARTUP_SCRIPT : '../lib/scripts/pm2-init-centos.sh', diff --git a/lib/CLI.js b/lib/CLI.js index f9527260..6377edd8 100644 --- a/lib/CLI.js +++ b/lib/CLI.js @@ -540,7 +540,7 @@ CLI.resetMetaProcess = function(process_name, cb) { async.eachLimit(ids, 4, function(id, next) { Satan.executeRemote('resetMetaProcessId', id, function(err, res) { if (err) console.error(err); - printOut('Reseting meta for process id %d', id); + printOut(cst.PREFIX_MSG + 'Reseting meta for process id %d', id); return next(); }); }, function(err) { @@ -962,7 +962,7 @@ CLI._restartAll = function(cb) { return cb ? cb({msg:err}) : exitCli(cst.ERROR_EXIT); } if (list && list.length === 0) { - printError('No process launched'); + printError(cst.PREFIX_MSG + 'No process launched'); return cb ? cb({msg:err}) : exitCli(cst.ERROR_EXIT); } @@ -971,7 +971,7 @@ CLI._restartAll = function(cb) { var proc = processes[0]; if (proc == null) { - printOut(cst.PREFIX_MSG + 'Process restarted...'); + printOut(cst.PREFIX_MSG + 'All processes has been restarted'); return cb ? cb(null, processes) : setTimeout(speedList, 1000); } Satan.executeRemote('restartProcessId', proc.pm2_env.pm_id, function(err, res) { @@ -1004,35 +1004,35 @@ CLI.delete = function(process_name, jsonVia, cb) { process_name = process_name.toString(); } + printOut(cst.PREFIX_MSG + 'Deleting ' + process_name); + if (jsonVia == 'pipe') return CLI.actionFromJson('deleteProcessName', process_name, 'pipe'); if (process_name.indexOf('.json') > 0) return CLI.actionFromJson('deleteProcessName', process_name, 'file'); else if (process_name == 'all') { - printOut(cst.PREFIX_MSG + 'Stopping and deleting all processes'); Satan.executeRemote('deleteAll', {}, function(err, list) { if (err) { - printError(err); + printError(cst.PREFIX_MSG_ERR + err); return cb ? cb({msg:err}) : exitCli(cst.ERROR_EXIT); } + printOut(cst.PREFIX_MSG + 'All processes has been stopped and deleted'); return cb ? cb(null, list) : speedList(); }); } else if (!isNaN(parseInt(process_name))) { - printOut('Stopping and deleting process by id : %s', process_name); Satan.executeRemote('deleteProcessId', process_name, function(err, list) { if (err) { - printError(err); + printError(cst.PREFIX_MSG_ERR + err); return cb ? cb({msg:err}) : exitCli(cst.ERROR_EXIT); } return cb ? cb(null, list) : speedList(); }); } else { - printOut(cst.PREFIX_MSG + 'Stopping and deleting process by name %s', process_name); Satan.executeRemote('deleteProcessName', process_name, function(err, list) { if (err) { - printError(err); + printError(cst.PREFIX_MSG_ERR + err); return cb ? cb({msg:err}) : exitCli(cst.ERROR_EXIT); } return cb ? cb(null, list) : speedList(); @@ -1045,6 +1045,8 @@ CLI.stop = function(process_name, cb) { if (typeof(process_name) === 'number') process_name = process_name.toString(); + printOut(cst.PREFIX_MSG + 'Stopping ' + process_name); + if (process_name == "-") { process.stdin.resume(); process.stdin.setEncoding('utf8'); @@ -1059,7 +1061,6 @@ CLI.stop = function(process_name, cb) { else if (isNaN(parseInt(process_name))) { CLI._stopProcessName(process_name, cb); } else { - printOut(cst.PREFIX_MSG + 'Stopping process by id ' + process_name); CLI._stopId(process_name, cb); } }; @@ -1072,9 +1073,10 @@ CLI.stop = function(process_name, cb) { CLI._stopAll = function(cb) { Satan.executeRemote('stopAll', {}, function(err, list) { if (err) { - printError(err); + printError(cst.PREFIX_MSG_ERR + err); return cb ? cb({msg:err}) : exitCli(cst.ERROR_EXIT); } + printOut(cst.PREFIX_MSG + 'All processes stopped'); return cb ? cb(null, list) : speedList(); }); }; @@ -1088,7 +1090,7 @@ CLI._stopAll = function(cb) { CLI._stopProcessName = function(name, cb) { Satan.executeRemote('stopProcessName', name, function(err, list) { if (err) { - printError(err); + printError(cst.PREFIX_MSG_ERR + err); return cb ? cb({msg:err}) : exitCli(cst.ERROR_EXIT); } printOut(cst.PREFIX_MSG + 'Stopping process by name ' + name); @@ -1105,10 +1107,10 @@ CLI._stopProcessName = function(name, cb) { CLI._stopId = function(pm2_id, cb) { Satan.executeRemote('stopProcessId', pm2_id, function(err, list) { if (err) { - printError(err); + printError(cst.PREFIX_MSG_ERR + err); return cb ? cb({msg:err}) : exitCli(cst.ERROR_EXIT); } - printOut(cst.PREFIX_MSG + ' Process stopped'); + printOut(cst.PREFIX_MSG + 'Process %d stopped', pm2_id); return cb ? cb(null, list) : speedList(); }); }; @@ -1186,6 +1188,7 @@ CLI.flush = function(cb) { fs.openSync(l.pm2_env.pm_out_log_path, 'w'); fs.openSync(l.pm2_env.pm_err_log_path, 'w'); }); + printOut(cst.PREFIX_MSG + 'Logs flushed'); return cb ? cb(null, list) : exitCli(cst.SUCCESS_EXIT); }); }; @@ -1368,7 +1371,7 @@ CLI.ilogs = function() { * @return */ CLI.killDaemon = function(cb) { - printOut(cst.PREFIX_MSG + 'Killing pm2...'); + printOut(cst.PREFIX_MSG + 'Stopping PM2'); Satan.executeRemote('getMonitorData', {}, function(err, list) { if (err) { @@ -1394,7 +1397,7 @@ CLI.killDaemon = function(cb) { if (cb) return cb({msg:err}); else exitCli(cst.ERROR_EXIT); } - console.info('PM2 stopped'); + console.info(cst.PREFIX_MSG + 'PM2 stopped'); return cb ? cb(null, res) : exitCli(cst.SUCCESS_EXIT); }); }); diff --git a/lib/Interactor/InteractorDaemonizer.js b/lib/Interactor/InteractorDaemonizer.js index cafe6866..dcce3a5b 100644 --- a/lib/Interactor/InteractorDaemonizer.js +++ b/lib/Interactor/InteractorDaemonizer.js @@ -130,8 +130,6 @@ function launchOrAttach(infos, cb) { InteractorDaemonizer.daemonize = function(infos, cb) { var InteractorJS = path.resolve(path.dirname(module.filename), 'Daemon.js'); - console.log('Launching interactor'); - var out = fs.openSync(cst.INTERACTOR_LOG_FILE_PATH, 'a'), err = fs.openSync(cst.INTERACTOR_LOG_FILE_PATH, 'a'); @@ -154,10 +152,10 @@ InteractorDaemonizer.daemonize = function(infos, cb) { child.once('message', function(msg) { process.emit('interactor:daemon:ready'); - console.log(msg); + //console.log(msg); child.disconnect(); - console.log(chalk.cyan.bold('[Keymetrics.io]') + ' Log: %s | Conf: %s | PID: %s', cst.INTERACTOR_LOG_FILE_PATH, + console.log(chalk.cyan('[Keymetrics.io]') + ' Launched - Log: %s | Conf: %s | PID: %s', cst.INTERACTOR_LOG_FILE_PATH, cst.INTERACTION_CONF, cst.INTERACTOR_PID_PATH); return setTimeout(function() {cb(null, child)}, 100); From c865318d2bbc51ea29388e189741ee80aa73a191 Mon Sep 17 00:00:00 2001 From: tknew2 Date: Thu, 14 Aug 2014 15:40:30 +0200 Subject: [PATCH 26/69] -max-memory-restart option - clean README - fix tests - node_args option added to JSON declaration - desc show node args - fix #487 #490 --- CHANGELOG.md | 5 ++- README.md | 63 +++++++++++++++++++--------------- bin/pm2 | 14 +------- lib/CLI.js | 11 ++++-- lib/CliUx.js | 1 + lib/Common.js | 12 +++++++ lib/God/ActionMethods.js | 30 +++++++++------- lib/God/ClusterMode.js | 6 ++-- lib/God/ForkMode.js | 24 ++++++++++--- lib/ProcessContainer.js | 9 ++--- test/bash/cli.sh | 9 +---- test/bash/cli2.sh | 27 +++------------ test/bash/fork.sh | 2 -- test/bash/gracefulReload.sh | 1 - test/bash/gracefulReload3.sh | 1 - test/bash/harmony.sh | 26 ++++++-------- test/bash/include.sh | 4 +-- test/bash/infinite_loop.sh | 4 --- test/bash/misc.sh | 28 ++++++++++++--- test/fixtures/big-array-es6.js | 23 +++++++++++++ test/fixtures/big-array.js | 8 +++++ test/fixtures/harmony.json | 5 +++ test/fixtures/max-mem.json | 5 +++ 23 files changed, 189 insertions(+), 129 deletions(-) create mode 100644 test/fixtures/big-array-es6.js create mode 100644 test/fixtures/big-array.js create mode 100644 test/fixtures/harmony.json create mode 100644 test/fixtures/max-mem.json diff --git a/CHANGELOG.md b/CHANGELOG.md index 5d01d5ff..58a8d49b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,9 @@ # 0.10.0 - PM2 Hellfire release -- PM2 Battle testing (https://docs.google.com/spreadsheets/d/1z7hJwI_TBQslkIS07YjgCGlpu8eHy2Wc0Bd5qDNnICE/edit#gid=925366936) +- PM2 is now Battle tested (https://docs.google.com/spreadsheets/d/1z7hJwI_TBQslkIS07YjgCGlpu8eHy2Wc0Bd5qDNnICE/edit#gid=925366936) +- Auto restart memory limit feature via --max-memory-restart (and max_memory_restart via JSON) (https://github.com/Unitech/pm2#max-memory-restart) +- Remove timestamps bu default with pm2 logs - Coffeescript not enabled by default anymore (enhance memory usage) - PM2 Programmatic interface enhanced - Daemon fork system enhanced @@ -15,6 +17,7 @@ - Faster monitoring system - AXM actions unification - Socket errors handled +- PM2_NODE_OPTIONS deprecation (use --node-args instead) # 0.9.6 - 0.9.5 - 0.9.4 diff --git a/README.md b/README.md index 604ab166..8f50769e 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ pm2 is perfect when you need to spread your stateless Node.js code across all CP - Script daemonization - 0s downtime reload for Node apps - Generate SystemV/SystemD startup scripts (Ubuntu, Centos...) +- Set memory limit for process to restart - Pause unstable process (avoid infinite loop) - Restart on file change with `--watch` - Monitoring in console @@ -62,6 +63,7 @@ Thanks in advance and we hope that you like pm2! - [Transitional state of apps](#a4) - [Process listing](#a6) +- [Automatic restart process based on memory](#a6) - [Monitoring CPU/Memory usage](#a7) - [Logs management](#a9) - [Clustering](#a5) @@ -179,6 +181,7 @@ $ pm2 delete all # Will remove all processes from pm2 list # Misc +$ pm2 reset # Reset meta data (restarted time...) $ pm2 updatePM2 # Update in memory pm2 $ pm2 ping # Ensure pm2 daemon has been launched $ pm2 sendSignal SIGUSR2 my-app # Send system signal to script @@ -327,6 +330,25 @@ To get more details about a specific process: $ pm2 describe 0 ``` + +## Automatic restart process based on memory + +Value passed is in megaoctets. Internally it uses the V8 flag `--max-old-space-size=MEM` to make a process exit when memory exceed a certain amount of RAM used. + +CLI: +```bash +$ pm2 start big-array.js --max-memory-restart 20 +``` + +JSON: +```json +{ + "name" : "max_mem", + "script" : "big-array.js", + "max_memory_restart" : "20" +} +``` + ## Monitoring CPU/Memory usage @@ -528,7 +550,6 @@ To watch specifics paths, please use a JSON app declaration, `watch` can take a "watch": ["server", "client"], "ignoreWatch" : ["node_modules", "client/img"] } - ``` @@ -545,6 +566,7 @@ You can define parameters for your apps in `processes.json`: "log_date_format" : "YYYY-MM-DD HH:mm Z", "ignoreWatch" : ["[\\/\\\\]\\./", "node_modules"], "watch" : "true", + "node_args" : "--harmony", "cwd" : "/this/is/a/path/to/start/script", "env": { "NODE_ENV": "production", @@ -613,6 +635,7 @@ Note that if you execute `pm2 start node-app-2` again, it will spawn an addition "cwd" : "/srv/node-app/current", "args" : "['--toto=heya coco', '-d', '1']", "script" : "bin/app.js", + "node_args" : "--harmony", "log_date_format" : "YYYY-MM-DD HH:mm Z", "error_file" : "/var/log/node-app/node-app.stderr.log", "out_file" : "log/node-app.stdout.log", @@ -915,7 +938,6 @@ PM2_BIND_ADDR PM2_API_PORT PM2_GRACEFUL_TIMEOUT PM2_MODIFY_REQUIRE -PM2_NODE_OPTIONS ``` @@ -928,36 +950,21 @@ $ pm2 web ## Enabling Harmony ES6 -### Enable by default for all processes - -You can enable Harmony ES6 by setting `PM2_NODE_OPTIONS='--harmony'` environment variable option when you start pm2 (pm2 should not be already daemonized). - -To pass this option by default, you can edit `~/.pm2/custom_options.sh` and add: - -```bash -export PM2_NODE_OPTIONS='--harmony' -``` - -Then: - -```bash -$ pm2 dump -$ pm2 exit -$ pm2 resurrect -``` - -If ES6 has been enabled you should see this message at the beginning of each pm2 command: - -``` -● ES6 mode -``` - -### Enable for specific processes - +The `--node-args` option permit to launch script with V8 flags, so to enable harmony for a process just do this: ```bash $ pm2 start my_app.js --node-args="--harmony" ``` +And with JSON declaration: + +```bash +[{ + "name" : "ES6", + "script" : "es6.js", + "node_args" : "--harmony" +}] +``` + ## CoffeeScript diff --git a/bin/pm2 b/bin/pm2 index 0a73d21c..8ef23ccc 100755 --- a/bin/pm2 +++ b/bin/pm2 @@ -25,6 +25,7 @@ commander.version(pkg.version) .option('-o --output ', 'specify out log file') .option('-e --error ', 'specify error log file') .option('-p --pid ', 'specify pid file') + .option('--max-memory-restart ', 'specify max memory amount used to autorestart (in megaoctets)') .option('--env ', 'specify environment to get specific env variables (for JSON declaration)') .option('-x --execute-command', 'execute a program using fork system') .option('-u --user ', 'define user when generating startup script') @@ -507,16 +508,3 @@ process.once('satan:client:ready', function() { } }); })(); - -// (function testHarmony() { -// // -// // Harmony test -// // -// try { -// var assert = require('assert') -// , s = new Set(); -// s.add('a'); -// assert.ok(s.has('a')); -// console.log('● ES6 mode'.green); -// } catch(e) {} -// })(); diff --git a/lib/CLI.js b/lib/CLI.js index 6377edd8..bb7b1cf4 100644 --- a/lib/CLI.js +++ b/lib/CLI.js @@ -43,17 +43,22 @@ CLI.start = function(script, opts, cb) { opts = {}; } - if (opts.nodeArgs) + if (opts.nodeArgs) { //maintain backwards compat for space delimited string args if (Array.isArray(opts.nodeArgs)){ - appConf['nodeArgs'] = opts.nodeArgs; + appConf['node_args'] = opts.nodeArgs; } else { - appConf['nodeArgs'] = opts.nodeArgs.split(' '); + appConf['node_args'] = opts.nodeArgs.split(' '); } + } else { + appConf.node_args = []; + } if (opts.scriptArgs) appConf['args'] = JSON.stringify(opts.scriptArgs); if (opts.name) appConf['name'] = opts.name; + if (opts.maxMemoryRestart) + appConf.max_memory_restart = opts.maxMemoryRestart; if (opts.instances) appConf['instances'] = opts.instances; if (opts.error) diff --git a/lib/CliUx.js b/lib/CliUx.js index 417978ef..55c2b43f 100644 --- a/lib/CliUx.js +++ b/lib/CliUx.js @@ -58,6 +58,7 @@ UX.describeTable = function(process) { { 'out log path' : pm2_env.pm_out_log_path }, { 'pid path' : pm2_env.pm_pid_path }, { 'mode' : pm2_env.exec_mode }, + { 'node v8 arguments' : pm2_env.node_args }, { 'watch & reload' : pm2_env.watch ? chalk.green.bold('✔') : '✘' }, { 'interpreter' : pm2_env.exec_interpreter }, { 'restarts' : pm2_env.restart_time }, diff --git a/lib/Common.js b/lib/Common.js index cce86db7..f1dc8da4 100644 --- a/lib/Common.js +++ b/lib/Common.js @@ -63,6 +63,18 @@ Common.resolveAppPaths = function(app, cwd, outputter) { app["pm_exec_path"] = path.resolve(cwd, app.script); delete app.script; + if (app.node_args && !Array.isArray(app.node_args)) + app.node_args = app.node_args.split(' '); + + if (!app.node_args) + app.node_args = []; + + if (app.max_memory_restart && + !isNaN(parseInt(app.max_memory_restart)) && + Array.isArray(app.node_args)) { + app.node_args.push('--max-old-space-size=' + app.max_memory_restart); + } + if (!app["name"]) { app["name"] = p.basename(app["pm_exec_path"]); } diff --git a/lib/God/ActionMethods.js b/lib/God/ActionMethods.js index f0a39ae3..c8aaf148 100644 --- a/lib/God/ActionMethods.js +++ b/lib/God/ActionMethods.js @@ -471,17 +471,17 @@ module.exports = function(God) { * @return */ God.killMe = function(env, cb) { - God.deleteAll({}, function(err, processes) { - console.log('pm2 has been killed by command line'); - God.bus.emit('pm2:kill', { - status : 'killed', - msg : 'pm2 has been killed via CLI' - }); - setTimeout(function() { - cb(null, {msg : 'pm2 killed'}); - process.exit(cst.SUCCESS_EXIT); - }, 800); + console.log('PM2 has been killed by user command'); + God.bus.emit('pm2:kill', { + status : 'killed', + msg : 'pm2 has been killed via CLI' }); + setTimeout(function() { + cb(null, {msg : 'pm2 killed'}); + }, 200); + setTimeout(function() { + process.exit(cst.SUCCESS_EXIT); + }, 300); }; @@ -559,10 +559,16 @@ module.exports = function(God) { processIds.forEach(function (id) { var cluster = God.clusters_db[id]; + if (cluster.pm2_env.exec_mode == 'cluster_mode') cluster.send({type:'log:reload'}); - else - cluster._reloadLogs(function() { + else // Fork mode + cluster._reloadLogs(function(err) { + if (err) { + God.logAndGenerateError(err); + return cb(new Error(err)); + }; + return false; }); console.log('Reloading logs for process id %d', id); }); diff --git a/lib/God/ClusterMode.js b/lib/God/ClusterMode.js index e11b3228..052b47af 100644 --- a/lib/God/ClusterMode.js +++ b/lib/God/ClusterMode.js @@ -36,8 +36,8 @@ module.exports = function ClusterMode(God) { console.log('Entering in node wrap logic (cluster_mode) for script %s', env_copy.pm_exec_path); - if (env_copy.nodeArgs && Array.isArray(env_copy.nodeArgs)) { - cluster.settings.execArgv = env_copy.nodeArgs; + if (env_copy.node_args && Array.isArray(env_copy.node_args)) { + cluster.settings.execArgv = env_copy.node_args; } try { @@ -71,7 +71,7 @@ module.exports = function ClusterMode(God) { } }); - + //delete clu.process._handle.owner; return cb(null, clu); diff --git a/lib/God/ForkMode.js b/lib/God/ForkMode.js index af959d25..59f41b18 100644 --- a/lib/God/ForkMode.js +++ b/lib/God/ForkMode.js @@ -40,8 +40,8 @@ module.exports = function ForkMode(God) { if (interpreter !== 'none') { command = interpreter; - if (pm2_env.nodeArgs && Array.isArray(pm2_env.nodeArgs)) { - args = args.concat(pm2_env.nodeArgs); + if (pm2_env.node_args && Array.isArray(pm2_env.node_args)) { + args = args.concat(pm2_env.node_args); } if (process.env.PM2_NODE_OPTIONS) { @@ -86,15 +86,31 @@ module.exports = function ForkMode(God) { function startLogging(cb) { stdout = fs.createWriteStream(outFile, { flags : 'a' }); + stdout.on('error', function(e) { + God.logAndGenerateError(e); + return cb(e); + }); + stdout.on('open', function() { stderr = fs.createWriteStream(errFile, { flags : 'a' }); + + stderr.on('error', function(e) { + God.logAndGenerateError(e); + return cb(e); + }); + stderr.on('open', function() { - return cb(); + return cb(null); }); }); } - startLogging(function() { + startLogging(function(err) { + if (err) { + God.logAndGenerateError(err); + return cb(err); + }; + getugid(function(e, uid, gid){ if(e){ God.logAndGenerateError(e); diff --git a/lib/ProcessContainer.js b/lib/ProcessContainer.js index 0cf0ea18..0f337dbc 100644 --- a/lib/ProcessContainer.js +++ b/lib/ProcessContainer.js @@ -2,6 +2,10 @@ // Child wrapper. Redirect output to files, assign pid & co. // by Strzelewicz Alexandre +// Rename process +if (process.env.name != null) + process.title = 'pm2: ' + process.env.name; + var fs = require('fs'); var p = require('path'); var cst = require('../constants'); @@ -30,9 +34,6 @@ var cst = require('../constants'); if (process.env.args != null) process.argv = process.argv.concat(eval(process.env.args)); - // Rename process - if (process.env.name != null) - process.title = 'pm2: ' + process.env.name; exec(script, outFile, errFile); @@ -79,7 +80,7 @@ function exec(script, outFile, errFile) { process.chdir(process.env.PWD || p.dirname(script)); var stderr, stdout; - + if(p.extname(script) == '.coffee') { require('coffee-script/register'); } diff --git a/test/bash/cli.sh b/test/bash/cli.sh index 71966f3c..0c780cfd 100755 --- a/test/bash/cli.sh +++ b/test/bash/cli.sh @@ -7,9 +7,6 @@ echo -e "\033[1mRunning tests:\033[0m" cd $file_path -$pm2 kill -spec "kill daemon" - # # Different way to stop process # @@ -137,11 +134,7 @@ spec "Should get the right JSON with HttpInterface file launched" # Restart only one process # $pm2 restart 1 -sleep 0.3 -$http_get -q http://localhost:9615/ -O $JSON_FILE -OUT=`cat $JSON_FILE | grep -o "restart_time\":1" | wc -l` -[ $OUT -eq 1 ] || fail "$1" -success "$1" +should 'should has restarted process' 'restart_time: 1' 1 # # Restart all processes diff --git a/test/bash/cli2.sh b/test/bash/cli2.sh index 538ed9e3..76d46f14 100755 --- a/test/bash/cli2.sh +++ b/test/bash/cli2.sh @@ -21,7 +21,7 @@ $pm2 restart echo.js spec "Should restart an app by script.js (TRANSITIONAL STATE)" ############### -$pm2 kill +$pm2 delete all echo "---- BY_NAME Start an app, stop it, if state stopped and started, restart stopped app" @@ -33,30 +33,13 @@ $pm2 restart gege should 'should app be online once restart called' 'online' 1 ############### -$pm2 kill +$pm2 delete all echo "Start an app, start it one more time, if started, throw message" $pm2 start echo.js $pm2 start echo.js ispec "Should not re start app" - -############### -$pm2 kill - -cd ../.. - -echo "Change path try to exec" -$pm2 start test/fixtures/echo.js -should 'should app be online' 'online' 1 -$pm2 stop test/fixtures/echo.js -should 'should app be stopped' 'stopped' 1 -$pm2 start test/fixtures/echo.js -should 'should app be online' 'online' 1 - -cd - - - ########### DELETED STUFF BY ID $pm2 kill @@ -65,14 +48,14 @@ $pm2 delete 0 should 'should has been deleted process by id' "name: 'echo'" 0 ########### DELETED STUFF BY NAME -$pm2 kill +$pm2 delete all $pm2 start echo.js --name test $pm2 delete test should 'should has been deleted process by name' "name: 'test'" 0 ########### DELETED STUFF BY SCRIPT -$pm2 kill +$pm2 delete all $pm2 start echo.js $pm2 delete echo.js @@ -81,7 +64,7 @@ should 'should has been deleted process by script' "name: 'echo'" 0 ########### OPTIONS OUTPUT FILES -$pm2 kill +$pm2 delete all $pm2 start echo.js -o outech.log -e errech.log --name gmail -i 10 sleep 0.5 diff --git a/test/bash/fork.sh b/test/bash/fork.sh index c7d2fd3c..d1c7d20f 100644 --- a/test/bash/fork.sh +++ b/test/bash/fork.sh @@ -6,8 +6,6 @@ source "${SRC}/include.sh" cd $file_path ########### Fork mode -$pm2 kill - $pm2 start echo.js -x should 'should has forked app' 'fork_mode' 1 diff --git a/test/bash/gracefulReload.sh b/test/bash/gracefulReload.sh index 17258d05..16de9963 100644 --- a/test/bash/gracefulReload.sh +++ b/test/bash/gracefulReload.sh @@ -8,7 +8,6 @@ cd $file_path echo "################## GRACEFUL RELOAD ###################" ############### -$pm2 kill echo "Launching" $pm2 start graceful-exit.js -i 4 --name="graceful" -o "grace.log" -e "grace-err.log" diff --git a/test/bash/gracefulReload3.sh b/test/bash/gracefulReload3.sh index e50cca77..87b93972 100644 --- a/test/bash/gracefulReload3.sh +++ b/test/bash/gracefulReload3.sh @@ -8,7 +8,6 @@ cd $file_path echo "################## GRACEFUL RELOAD 3 ###################" ############### -$pm2 kill echo "Launching" $pm2 start graceful-exit-send.js -i 2 --name="graceful3" -o "grace3.log" -e "grace-err3.log" diff --git a/test/bash/harmony.sh b/test/bash/harmony.sh index dd9433b2..fe037293 100644 --- a/test/bash/harmony.sh +++ b/test/bash/harmony.sh @@ -11,33 +11,29 @@ echo "################ HARMONY ES6" $pm2 start harmony.js sleep 2 $pm2 list -ishould 'should not fail when passing harmony option to V8' 'restart_time: 0' 1 +should 'should FAIL when not passing harmony option to V8' 'restart_time: 0' 0 $pm2 list -$pm2 kill - -PM2_NODE_OPTIONS='--harmony' `pwd`/../../bin/pm2 start harmony.js -sleep 2 -$pm2 list -should 'should not fail when passing harmony option to V8' 'restart_time: 0' 1 -$pm2 kill +$pm2 delete all $pm2 start harmony.js --node-args="--harmony" sleep 2 $pm2 list -should 'should not fail when passing node-args=harmony opts' 'restart_time: 0' 1 -$pm2 kill +should 'should not fail when passing node-args=harmony opts in CLUSTERMODE' 'restart_time: 0' 1 +$pm2 delete all echo "################ HARMONY / NODEARGS ES6 FORK MODE" $pm2 start harmony.js --node-args="--harmony" -x sleep 2 $pm2 list -should 'should not fail when passing node-args=harmony opts' 'restart_time: 0' 1 -$pm2 kill +should 'should not fail when passing node-args=harmony opts in FORKMODE' 'restart_time: 0' 1 +$pm2 delete all +echo "################## NODE ARGS VIA JSON" -PM2_NODE_OPTIONS='--harmony' $pm2 start harmony.js -x +$pm2 start harmony.json sleep 2 $pm2 list -should 'should not fail when passing node-args=harmony opts' 'restart_time: 0' 1 -$pm2 kill +should 'should not fail when passing harmony option to V8 via node_args in JSON files' 'restart_time: 0' 1 + +$pm2 delete all diff --git a/test/bash/include.sh b/test/bash/include.sh index 8ac71c25..2d522b30 100644 --- a/test/bash/include.sh +++ b/test/bash/include.sh @@ -22,7 +22,7 @@ file_path="test/fixtures" $pm2 kill # Determine wget / curl -which wget +which wget > /dev/null if [ $? -eq 0 ] then http_get="wget" @@ -31,8 +31,6 @@ else exit 1; fi -echo $http_get - function fail { echo -e "######## \033[31m ✘ $1\033[0m" exit 1 diff --git a/test/bash/infinite_loop.sh b/test/bash/infinite_loop.sh index 16d86b8f..36e0a52b 100644 --- a/test/bash/infinite_loop.sh +++ b/test/bash/infinite_loop.sh @@ -8,10 +8,6 @@ cd $file_path echo "Starting infinite loop tests" -$pm2 kill - - - $pm2 start killtoofast.js --name unstable-process echo -n "Waiting for process to restart too many times and pm2 to stop it" diff --git a/test/bash/misc.sh b/test/bash/misc.sh index 72e4eb72..cc2b424c 100644 --- a/test/bash/misc.sh +++ b/test/bash/misc.sh @@ -7,8 +7,27 @@ cd $file_path echo -e "\033[1mRunning tests:\033[0m" +# +# Max memory auto restart option +# +# -max-memory-restart option && maxMemoryRestart (via JSON file) +# +$pm2 start big-array.js --max-memory-restart 19 +sleep 7 +$pm2 list +should 'process should been restarted' 'restart_time: 0' 0 -$pm2 kill +$pm2 delete all + +# +# Via JSON +# +$pm2 start max-mem.json +sleep 7 +$pm2 list +should 'process should been restarted' 'restart_time: 0' 0 + +$pm2 delete all $pm2 start env.js @@ -27,7 +46,7 @@ else fail "environment defined ? wtf ?" fi -$pm2 kill +$pm2 delete all $pm2 start env.json @@ -37,7 +56,7 @@ sleep 1 OUT=`cat $OUT_LOG | head -n 1` -if [ $OUT = "undefined" ] +if [ "$OUT" = "undefined" ] then fail "environment variable hasnt been defined" else @@ -79,10 +98,9 @@ ispec 'file outmerge-0.log should not exist' rm outmerge* ########### coffee cluster test -$pm2 kill +$pm2 delete all $pm2 start echo.coffee should 'process should not have been restarted' 'restart_time: 0' 1 should 'process should be online' "status: 'online'" 1 - diff --git a/test/fixtures/big-array-es6.js b/test/fixtures/big-array-es6.js new file mode 100644 index 00000000..b08ce2b0 --- /dev/null +++ b/test/fixtures/big-array-es6.js @@ -0,0 +1,23 @@ + + +var obj = {}; +var i = 0; + +setInterval(function() { + obj[i] = Array.apply(null, new Array(99999)).map(String.prototype.valueOf,"hi"); + i++; +}, 2); + + +(function testHarmony() { + // + // Harmony test + // + try { + var assert = require('assert') + , s = new Set(); + s.add('a'); + assert.ok(s.has('a')); + console.log('● ES6 mode'.green); + } catch(e) {} +})(); diff --git a/test/fixtures/big-array.js b/test/fixtures/big-array.js new file mode 100644 index 00000000..d4236fbe --- /dev/null +++ b/test/fixtures/big-array.js @@ -0,0 +1,8 @@ + +var obj = {}; +var i = 0; + +setInterval(function() { + obj[i] = Array.apply(null, new Array(99999)).map(String.prototype.valueOf,"hi"); + i++; +}, 2); diff --git a/test/fixtures/harmony.json b/test/fixtures/harmony.json new file mode 100644 index 00000000..99ed3067 --- /dev/null +++ b/test/fixtures/harmony.json @@ -0,0 +1,5 @@ +{ + "name" : "ES6", + "script" : "harmony.js", + "node_args" : "--harmony" +} diff --git a/test/fixtures/max-mem.json b/test/fixtures/max-mem.json new file mode 100644 index 00000000..1d44d15b --- /dev/null +++ b/test/fixtures/max-mem.json @@ -0,0 +1,5 @@ +{ + "name" : "max_mem", + "script" : "big-array.js", + "max_memory_restart" : "19" +} From 8cec889ec0be9915f50dba04885af6e58b102d65 Mon Sep 17 00:00:00 2001 From: tknew2 Date: Thu, 14 Aug 2014 15:48:03 +0200 Subject: [PATCH 27/69] fix test - changelog update --- CHANGELOG.md | 2 ++ README.md | 2 +- test/bash/cli2.sh | 6 +++--- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 58a8d49b..7e0c25b7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ - PM2 Programmatic interface enhanced - Daemon fork system enhanced - PM2 hearth refactor +- PM2 describe show node-args +- node_args for V8 options is now available via JSON declaration - Watch system avoid ghost processes - Memory leak fixes - Better performance on interface diff --git a/README.md b/README.md index 8f50769e..6dda4484 100644 --- a/README.md +++ b/README.md @@ -63,7 +63,7 @@ Thanks in advance and we hope that you like pm2! - [Transitional state of apps](#a4) - [Process listing](#a6) -- [Automatic restart process based on memory](#a6) +- [Automatic restart process based on memory](#max-memory-restart) - [Monitoring CPU/Memory usage](#a7) - [Logs management](#a9) - [Clustering](#a5) diff --git a/test/bash/cli2.sh b/test/bash/cli2.sh index 76d46f14..a34c3b79 100755 --- a/test/bash/cli2.sh +++ b/test/bash/cli2.sh @@ -64,13 +64,13 @@ should 'should has been deleted process by script' "name: 'echo'" 0 ########### OPTIONS OUTPUT FILES -$pm2 delete all +$pm2 kill $pm2 start echo.js -o outech.log -e errech.log --name gmail -i 10 sleep 0.5 cat outech-0.log > /dev/null -spec "file outech.log exist" +spec "file outech-0.log exist" cat errech-0.log > /dev/null -spec "file errech.log exist" +spec "file errech-0.log exist" should 'should has not restarted' 'restart_time: 0' 10 From 883fff1ab1175dd631c21734bf23fdd81625bd04 Mon Sep 17 00:00:00 2001 From: tknew2 Date: Fri, 15 Aug 2014 20:26:47 +0200 Subject: [PATCH 28/69] throttle pm2 kill --- lib/CLI.js | 27 +++++++------------------- lib/God/ClusterMode.js | 1 + lib/Interactor/InteractorDaemonizer.js | 23 ++++++++++++++++++++++ 3 files changed, 31 insertions(+), 20 deletions(-) diff --git a/lib/CLI.js b/lib/CLI.js index bb7b1cf4..5da98112 100644 --- a/lib/CLI.js +++ b/lib/CLI.js @@ -487,23 +487,7 @@ CLI.interact = function(secret_key, public_key, machine_name, cb) { * @method killInteract */ CLI.killInteract = function(cb) { - InteractorDaemonizer.ping(function(online) { - if (!online) { - if (!cb) printError('Interactor not launched'); - return cb ? cb({msg:'Interactor not launched'}) : exitCli(cst.SUCCESS_EXIT); - } - InteractorDaemonizer.launchRPC(function() { - InteractorDaemonizer.rpc.kill(function(err) { - if (err) { - if (!cb) printError(err); - return cb ? cb({msg : err}) : exitCli(cst.ERROR_EXIT); - } - if (!cb) printOut('Interactor successfully killed'); - return cb ? cb(null, {msg : 'killed'}) : exitCli(cst.SUCCESS_EXIT); - }); - }); - return false; - }); + InteractorDaemonizer.kill(); }; /** @@ -1395,15 +1379,18 @@ CLI.killDaemon = function(cb) { return false; }, function(err) { printOut(cst.PREFIX_MSG + 'All processes stopped'); - CLI.killInteract(function() { + InteractorDaemonizer.kill(function() { Satan.killDaemon(function(err, res) { if (err) { printError(err); if (cb) return cb({msg:err}); else exitCli(cst.ERROR_EXIT); } - console.info(cst.PREFIX_MSG + 'PM2 stopped'); - return cb ? cb(null, res) : exitCli(cst.SUCCESS_EXIT); + setTimeout(function() { + printOut(cst.PREFIX_MSG + 'PM2 stopped'); + return cb ? cb(null, res) : exitCli(cst.SUCCESS_EXIT); + }, 150); + return false; }); }); }); diff --git a/lib/God/ClusterMode.js b/lib/God/ClusterMode.js index 052b47af..6419ba22 100644 --- a/lib/God/ClusterMode.js +++ b/lib/God/ClusterMode.js @@ -11,6 +11,7 @@ var fs = require('fs'); var cst = require('../../constants.js'); var util = require('util'); var Common = require('../Common'); + /** * Description * @method exports diff --git a/lib/Interactor/InteractorDaemonizer.js b/lib/Interactor/InteractorDaemonizer.js index dcce3a5b..f116cc17 100644 --- a/lib/Interactor/InteractorDaemonizer.js +++ b/lib/Interactor/InteractorDaemonizer.js @@ -39,6 +39,29 @@ InteractorDaemonizer.ping = function(cb) { req.connect(cst.INTERACTOR_RPC_PORT); }; +InteractorDaemonizer.kill = function(cb) { + InteractorDaemonizer.ping(function(online) { + if (!online) { + if (!cb) Common.printError('Interactor not launched'); + return cb ? cb({msg:'Interactor not launched'}) : Common.exitCli(cst.SUCCESS_EXIT); + } + InteractorDaemonizer.launchRPC(function() { + InteractorDaemonizer.rpc.kill(function(err) { + if (err) { + if (!cb) Common.printError(err); + return cb ? cb({msg : err}) : Common.exitCli(cst.ERROR_EXIT); + } + setTimeout(function() { + if (!cb) Common.printOut('Interactor successfully killed'); + return cb ? cb(null, {msg : 'killed'}) : Common.exitCli(cst.SUCCESS_EXIT); + }, 150); + return false; + }); + }); + return false; + }); +}; + /** * Description * @method launchRPC From ca1a10ff7131434bf52de169764bfc11a9d6e624 Mon Sep 17 00:00:00 2001 From: tknew2 Date: Fri, 15 Aug 2014 20:42:32 +0200 Subject: [PATCH 29/69] change throttle pm2 kill val --- lib/CLI.js | 2 +- lib/God/ActionMethods.js | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/lib/CLI.js b/lib/CLI.js index 5da98112..d2a5e790 100644 --- a/lib/CLI.js +++ b/lib/CLI.js @@ -1389,7 +1389,7 @@ CLI.killDaemon = function(cb) { setTimeout(function() { printOut(cst.PREFIX_MSG + 'PM2 stopped'); return cb ? cb(null, res) : exitCli(cst.SUCCESS_EXIT); - }, 150); + }, 300); return false; }); }); diff --git a/lib/God/ActionMethods.js b/lib/God/ActionMethods.js index c8aaf148..15d7a64b 100644 --- a/lib/God/ActionMethods.js +++ b/lib/God/ActionMethods.js @@ -476,12 +476,10 @@ module.exports = function(God) { status : 'killed', msg : 'pm2 has been killed via CLI' }); - setTimeout(function() { - cb(null, {msg : 'pm2 killed'}); - }, 200); + cb(null, {msg : 'pm2 killed'}); setTimeout(function() { process.exit(cst.SUCCESS_EXIT); - }, 300); + }, 20); }; From 95792feee46d655c41629615c66a16c6b54ff015 Mon Sep 17 00:00:00 2001 From: Alexandre Strzelewicz Date: Fri, 15 Aug 2014 21:39:06 +0200 Subject: [PATCH 30/69] Update CHANGELOG.md --- CHANGELOG.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7e0c25b7..8a4bdfe9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,11 +2,11 @@ # 0.10.0 - PM2 Hellfire release - PM2 is now Battle tested (https://docs.google.com/spreadsheets/d/1z7hJwI_TBQslkIS07YjgCGlpu8eHy2Wc0Bd5qDNnICE/edit#gid=925366936) -- Auto restart memory limit feature via --max-memory-restart (and max_memory_restart via JSON) (https://github.com/Unitech/pm2#max-memory-restart) -- Remove timestamps bu default with pm2 logs +- PM2 respects strong unix standard process management +- Restart app when reaching a limit of memory by using --max-memory-restart (and max_memory_restart via JSON)(https://github.com/Unitech/pm2#max-memory-restart) +- Remove timestamps by default with pm2 logs - Coffeescript not enabled by default anymore (enhance memory usage) - PM2 Programmatic interface enhanced -- Daemon fork system enhanced - PM2 hearth refactor - PM2 describe show node-args - node_args for V8 options is now available via JSON declaration From 6f5fdbcd0f365f8209edaed189cc8dbf1910a656 Mon Sep 17 00:00:00 2001 From: Alexandre Strzelewicz Date: Fri, 15 Aug 2014 21:42:38 +0200 Subject: [PATCH 31/69] Update CHANGELOG.md --- CHANGELOG.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a4bdfe9..66da8087 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,9 @@ # 0.10.0 - PM2 Hellfire release -- PM2 is now Battle tested (https://docs.google.com/spreadsheets/d/1z7hJwI_TBQslkIS07YjgCGlpu8eHy2Wc0Bd5qDNnICE/edit#gid=925366936) -- PM2 respects strong unix standard process management +- PM2 hearth code has been refactored and now it handles extreme scenario without any leak or bug - Restart app when reaching a limit of memory by using --max-memory-restart (and max_memory_restart via JSON)(https://github.com/Unitech/pm2#max-memory-restart) +- PM2 respects strong unix standard process management - Remove timestamps by default with pm2 logs - Coffeescript not enabled by default anymore (enhance memory usage) - PM2 Programmatic interface enhanced @@ -19,6 +19,7 @@ - Faster monitoring system - AXM actions unification - Socket errors handled +- Watchdog via Agent - restart automatically PM2 with previous processes in case of crash - PM2_NODE_OPTIONS deprecation (use --node-args instead) # 0.9.6 - 0.9.5 - 0.9.4 From c69ba1f7cfc0438a2fb50289408919c696b9f2d6 Mon Sep 17 00:00:00 2001 From: tknew2 Date: Sat, 16 Aug 2014 16:26:34 +0200 Subject: [PATCH 32/69] fix deps - smarter cwd --- lib/Common.js | 7 ++++++- package.json | 4 ++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/lib/Common.js b/lib/Common.js index f1dc8da4..18a0e59d 100644 --- a/lib/Common.js +++ b/lib/Common.js @@ -35,7 +35,12 @@ Common.resolveAppPaths = function(app, cwd, outputter) { if (err) return err; - cwd = cwd || process.cwd(); + if (cwd && cwd[0] == '/') + cwd = cwd; + else if (cwd) + cwd = p.resolve(process.cwd(), cwd); + else + cwd = process.cwd(); // Set current env by first adding the process environment and then extending/replacing it // with env specified on command-line or JSON file. diff --git a/package.json b/package.json index 566bdaf2..19ce183d 100644 --- a/package.json +++ b/package.json @@ -119,7 +119,7 @@ "async": "~0.9.0", "axm": "~0.1.7", "axon": "~1.0.0", - "chalk": "^0.4.0", + "chalk": "~0.4.0", "chokidar": "~0.8.2", "cli-table": "~0.3.0", "coffee-script": "~1.7.1", @@ -137,7 +137,7 @@ "pm2-interface": "~0.1.3", "pm2-multimeter": "~0.1.2", "moment" : "~2.7.0", - "uid-number": "0.0.5" + "uid-number": "~0.0.5" }, "devDependencies": { "mocha": "^1.20.1", From 2007c588efa7e2d94f5d072104d667acf924be0c Mon Sep 17 00:00:00 2001 From: tknew2 Date: Sat, 16 Aug 2014 16:42:07 +0200 Subject: [PATCH 33/69] fix cwd --- lib/ProcessContainer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ProcessContainer.js b/lib/ProcessContainer.js index 0f337dbc..d9a791dc 100644 --- a/lib/ProcessContainer.js +++ b/lib/ProcessContainer.js @@ -77,7 +77,7 @@ function cronize(cron_pattern) { */ function exec(script, outFile, errFile) { // Change dir to fix process.cwd - process.chdir(process.env.PWD || p.dirname(script)); + process.chdir(process.env.pm_cwd || process.env.PWD || p.dirname(script)); var stderr, stdout; From 777d4d199e70cb34500a77971f03a9f90088278c Mon Sep 17 00:00:00 2001 From: tknew2 Date: Sat, 16 Aug 2014 22:54:03 +0200 Subject: [PATCH 34/69] fix kill & exit - 'cwd' via json works for cluster_mode --- lib/CLI.js | 23 +++++++++++------------ lib/Interactor/InteractorDaemonizer.js | 15 ++++++--------- lib/ProcessContainer.js | 7 ++++--- package.json | 3 ++- 4 files changed, 23 insertions(+), 25 deletions(-) diff --git a/lib/CLI.js b/lib/CLI.js index d2a5e790..212c3569 100644 --- a/lib/CLI.js +++ b/lib/CLI.js @@ -1379,19 +1379,18 @@ CLI.killDaemon = function(cb) { return false; }, function(err) { printOut(cst.PREFIX_MSG + 'All processes stopped'); + + setTimeout(function() { + printOut(cst.PREFIX_MSG + 'PM2 stopped'); + return cb ? cb(null) : exitCli(cst.SUCCESS_EXIT); + }, 300); + + Satan.killDaemon(function(err, res) { + if (err) printError(err); + }); + InteractorDaemonizer.kill(function() { - Satan.killDaemon(function(err, res) { - if (err) { - printError(err); - if (cb) return cb({msg:err}); - else exitCli(cst.ERROR_EXIT); - } - setTimeout(function() { - printOut(cst.PREFIX_MSG + 'PM2 stopped'); - return cb ? cb(null, res) : exitCli(cst.SUCCESS_EXIT); - }, 300); - return false; - }); + if (err) printError(err); }); }); return false; diff --git a/lib/Interactor/InteractorDaemonizer.js b/lib/Interactor/InteractorDaemonizer.js index f116cc17..1c686205 100644 --- a/lib/Interactor/InteractorDaemonizer.js +++ b/lib/Interactor/InteractorDaemonizer.js @@ -46,16 +46,13 @@ InteractorDaemonizer.kill = function(cb) { return cb ? cb({msg:'Interactor not launched'}) : Common.exitCli(cst.SUCCESS_EXIT); } InteractorDaemonizer.launchRPC(function() { + setTimeout(function() { + if (!cb) Common.printOut('Interactor successfully killed'); + return cb ? cb(null, {msg : 'killed'}) : Common.exitCli(cst.SUCCESS_EXIT); + }, 100); + InteractorDaemonizer.rpc.kill(function(err) { - if (err) { - if (!cb) Common.printError(err); - return cb ? cb({msg : err}) : Common.exitCli(cst.ERROR_EXIT); - } - setTimeout(function() { - if (!cb) Common.printOut('Interactor successfully killed'); - return cb ? cb(null, {msg : 'killed'}) : Common.exitCli(cst.SUCCESS_EXIT); - }, 150); - return false; + if (err) Common.printError(err); }); }); return false; diff --git a/lib/ProcessContainer.js b/lib/ProcessContainer.js index d9a791dc..dc708b0d 100644 --- a/lib/ProcessContainer.js +++ b/lib/ProcessContainer.js @@ -76,9 +76,6 @@ function cronize(cron_pattern) { * @return */ function exec(script, outFile, errFile) { - // Change dir to fix process.cwd - process.chdir(process.env.pm_cwd || process.env.PWD || p.dirname(script)); - var stderr, stdout; if(p.extname(script) == '.coffee') { @@ -196,6 +193,10 @@ function exec(script, outFile, errFile) { process.setuid(process.env.run_as_user); } + // Change dir to fix process.cwd + process.chdir(process.env.pm_cwd || process.env.PWD || p.dirname(script)); + + // Get the script & exec require(script); diff --git a/package.json b/package.json index 19ce183d..cb4c61c0 100644 --- a/package.json +++ b/package.json @@ -143,7 +143,8 @@ "mocha": "^1.20.1", "should": "^4.0.0", "better-assert": "^1.0.0", - "promise-spawner": "0.0.3" + "promise-spawner": "0.0.3", + "ikst" : "latest" }, "optionalDependencies": { "pm2-logs" : "latest" From 6209324527092ded77a89090d072229d847b7f7c Mon Sep 17 00:00:00 2001 From: tknew2 Date: Sat, 16 Aug 2014 23:03:31 +0200 Subject: [PATCH 35/69] remove illegal caracter on filepath for log/pid files --- lib/Common.js | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/lib/Common.js b/lib/Common.js index 18a0e59d..fa07c201 100644 --- a/lib/Common.js +++ b/lib/Common.js @@ -80,10 +80,12 @@ Common.resolveAppPaths = function(app, cwd, outputter) { app.node_args.push('--max-old-space-size=' + app.max_memory_restart); } - if (!app["name"]) { - app["name"] = p.basename(app["pm_exec_path"]); + if (!app.name) { + app.name = p.basename(app["pm_exec_path"]); } + var formated_app_name = app.name.replace(/[^a-zA-Z0-9\\.\\-]/g, '-'); + if (fs.existsSync(app.pm_exec_path) == false) { return new Error('script not found : ' + app.pm_exec_path); } @@ -91,10 +93,7 @@ Common.resolveAppPaths = function(app, cwd, outputter) { if (app.out_file) app["pm_out_log_path"] = path.resolve(cwd, app.out_file); else { - if (!app.name) { - return new Error('You havent specified log path, please specify at least a "name" field in the JSON'); - } - app["pm_out_log_path"] = path.resolve(cst.DEFAULT_LOG_PATH, [app.name, '-out.log'].join('')); + app["pm_out_log_path"] = path.resolve(cst.DEFAULT_LOG_PATH, [formated_app_name, '-out.log'].join('')); app.out_file = app["pm_out_log_path"]; } delete app.out_file; @@ -102,7 +101,7 @@ Common.resolveAppPaths = function(app, cwd, outputter) { if (app.error_file) app["pm_err_log_path"] = path.resolve(cwd, app.error_file); else { - app["pm_err_log_path"] = path.resolve(cst.DEFAULT_LOG_PATH, [app.name, '-err.log'].join('')); + app["pm_err_log_path"] = path.resolve(cst.DEFAULT_LOG_PATH, [formated_app_name, '-err.log'].join('')); app.error_file = app["pm_err_log_path"]; } delete app.error_file; @@ -110,7 +109,7 @@ Common.resolveAppPaths = function(app, cwd, outputter) { if (app.pid_file) app["pm_pid_path"] = path.resolve(cwd, app.pid_file); else { - app["pm_pid_path"] = path.resolve(cst.DEFAULT_PID_PATH, [app.name, '.pid'].join('')); + app["pm_pid_path"] = path.resolve(cst.DEFAULT_PID_PATH, [formated_app_name, '.pid'].join('')); app.pid_file = app["pm_pid_path"]; } delete app.pid_file; From 427b28618f4f0e75640238cd8e7c5a4e6e509b98 Mon Sep 17 00:00:00 2001 From: tknew2 Date: Sat, 16 Aug 2014 23:17:25 +0200 Subject: [PATCH 36/69] add ikst to optionnal --- package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index cb4c61c0..382d2025 100644 --- a/package.json +++ b/package.json @@ -143,11 +143,11 @@ "mocha": "^1.20.1", "should": "^4.0.0", "better-assert": "^1.0.0", - "promise-spawner": "0.0.3", - "ikst" : "latest" + "promise-spawner": "^0.0.3" }, "optionalDependencies": { - "pm2-logs" : "latest" + "pm2-logs" : "latest", + "ikst" : "~0.1.2" }, "bugs": { "url": "https://github.com/Unitech/pm2/issues" From da677c6f54d0ed206746df1331524181d8f1cd6c Mon Sep 17 00:00:00 2001 From: tknew2 Date: Sun, 17 Aug 2014 16:31:01 +0200 Subject: [PATCH 37/69] close RPC connection on PM2 update --- lib/CLI.js | 99 +++++++++++++++----------- lib/Common.js | 7 +- lib/Interactor/InteractorDaemonizer.js | 21 +++++- lib/Satan.js | 12 +++- 4 files changed, 90 insertions(+), 49 deletions(-) diff --git a/lib/CLI.js b/lib/CLI.js index 212c3569..0748226f 100644 --- a/lib/CLI.js +++ b/lib/CLI.js @@ -100,9 +100,9 @@ CLI.start = function(script, opts, cb) { appConf['exec_mode'] = opts.execMode; } - // if (appConf['exec_mode'] == 'cluster_mode' && process.version.match(/0.10/)) { - // printOut(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.'); - // } + if (appConf['exec_mode'] == 'cluster_mode' && process.version.match(/0.10/)) { + printOut(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. 0.11.x can use cluster_mode (default mode) efficiently.'); + } // Script arguments @@ -599,29 +599,57 @@ CLI.resurrect = function(cb) { CLI.updatePM2 = function(cb) { printOut('Be sure to haave the latest version by doing `npm install pm2@latest -g` before doing this procedure.'); - InteractorDaemonizer.update(function() { + // Kill Daemon and disconnect RPC + InteractorDaemonizer.kill(function() { + + // Dump PM2 processes CLI.dump(function(err) { - printOut(cst.PREFIX_MSG + '--- dumped'); - CLI.killDaemon(function(err) { - printOut(cst.PREFIX_MSG + '--- killed'); - Satan.launchDaemon(function(err, child) { + CLI.killDaemon(function() { + Satan.disconnectRPC(function() { - printOut(cst.PREFIX_MSG + '--- resurrected'); - if (err) { - printError(err); - return cb ? cb({msg:err}) : exitCli(cst.ERROR_EXIT); - } - CLI.resurrect(function() { - printOut(chalk.blue.bold('>>>>>>>>>> PM2 updated')); - return cb ? cb(null, {success:true}) : speedList(); + Satan.launchDaemon(function(err, child) { + Satan.launchRPC(function() { + + CLI.resurrect(function() { + printOut(chalk.blue.bold('>>>>>>>>>> PM2 updated')); + return cb ? cb(null, {success:true}) : speedList(); + }); + + }); }); - return false; + + }); }); }); + }); + + return false; + + CLI.dump(function(err) { + printOut(cst.PREFIX_MSG + '--- dumped'); + + CLI.killDaemon(function(err) { + printOut(cst.PREFIX_MSG + '--- killed'); + + Satan.launchDaemon(function(err, child) { + printOut(cst.PREFIX_MSG + '--- resurrecting'); + if (err) { + printError(err); + return cb ? cb({msg:err}) : exitCli(cst.ERROR_EXIT); + } + CLI.resurrect(function() { + printOut(chalk.blue.bold('>>>>>>>>>> PM2 updated')); + return cb ? cb(null, {success:true}) : speedList(); + }); + return false; + }); + }); + }); + }; /** @@ -1362,36 +1390,21 @@ CLI.ilogs = function() { CLI.killDaemon = function(cb) { printOut(cst.PREFIX_MSG + 'Stopping PM2'); - Satan.executeRemote('getMonitorData', {}, function(err, list) { + printOut(cst.PREFIX_MSG + 'Stopping & Deleting all processes'); + Satan.executeRemote('deleteAll', {}, function(err, list) { if (err) { - printError('Error retrieving process list: ' + err); - return cb ? cb({msg : err}) : exitCli(cst.ERROR_EXIT); + printError(cst.PREFIX_MSG_ERR + err); + return cb ? cb({msg:err}) : exitCli(cst.ERROR_EXIT); } + printOut(cst.PREFIX_MSG + 'All processes has been stopped and deleted'); - async.eachLimit(list, 1, function(proc, next) { - Satan.executeRemote('deleteProcessId', proc.pm2_env.pm_id, function(err, res) { - if (err) { - printError('Error : ' + err); - } - printOut(cst.PREFIX_MSG + 'Process %s stopped', proc.pm2_env.name); - return next(); - }); - return false; - }, function(err) { - printOut(cst.PREFIX_MSG + 'All processes stopped'); + setTimeout(function() { + printOut(cst.PREFIX_MSG + 'PM2 stopped'); + return cb ? cb(null) : exitCli(cst.SUCCESS_EXIT); + }, 300); - setTimeout(function() { - printOut(cst.PREFIX_MSG + 'PM2 stopped'); - return cb ? cb(null) : exitCli(cst.SUCCESS_EXIT); - }, 300); - - Satan.killDaemon(function(err, res) { - if (err) printError(err); - }); - - InteractorDaemonizer.kill(function() { - if (err) printError(err); - }); + Satan.killDaemon(function(err, res) { + if (err) printError(err); }); return false; }); diff --git a/lib/Common.js b/lib/Common.js index fa07c201..5fb04f77 100644 --- a/lib/Common.js +++ b/lib/Common.js @@ -14,6 +14,7 @@ var p = path; var Stringify = require('json-stringify-safe'); var Satan = require('./Satan.js'); +var InteractorDaemonizer = require('./Interactor/InteractorDaemonizer.js'); /** * Common methods (used by CLI and God) */ @@ -165,8 +166,10 @@ Common.validateApp = function(appConf, outputter) { * @return CallExpression */ Common.exitCli = function(code) { - Satan.disconnectRPC(function() { - return process.exit(code || 0); + InteractorDaemonizer.disconnectRPC(function() { + Satan.disconnectRPC(function() { + return process.exit(code || 0); + }); }); }; diff --git a/lib/Interactor/InteractorDaemonizer.js b/lib/Interactor/InteractorDaemonizer.js index 1c686205..1e9faf8d 100644 --- a/lib/Interactor/InteractorDaemonizer.js +++ b/lib/Interactor/InteractorDaemonizer.js @@ -40,16 +40,18 @@ InteractorDaemonizer.ping = function(cb) { }; InteractorDaemonizer.kill = function(cb) { + InteractorDaemonizer.ping(function(online) { if (!online) { if (!cb) Common.printError('Interactor not launched'); return cb ? cb({msg:'Interactor not launched'}) : Common.exitCli(cst.SUCCESS_EXIT); } + InteractorDaemonizer.launchRPC(function() { + setTimeout(function() { - if (!cb) Common.printOut('Interactor successfully killed'); - return cb ? cb(null, {msg : 'killed'}) : Common.exitCli(cst.SUCCESS_EXIT); - }, 100); + InteractorDaemonizer.disconnectRPC(cb); + }, 200); InteractorDaemonizer.rpc.kill(function(err) { if (err) Common.printError(err); @@ -280,6 +282,19 @@ InteractorDaemonizer.getSetKeys = function(secret_key, public_key, machine_name, }); }; +InteractorDaemonizer.disconnectRPC = function(cb) { + process.nextTick(function() { + if (!InteractorDaemonizer.client || + !InteractorDaemonizer.client.sock || + !InteractorDaemonizer.client.sock.close) + return cb({ + msg : 'RPC connection to Interactor Daemon is not launched' + }); + InteractorDaemonizer.client.sock.close(); + return cb ? cb(null, {success:true}) : false; + }); +}; + InteractorDaemonizer.launchAndInteract = function(opts, cb) { if (process.env.PM2_AGENT_ONLINE) { return process.nextTick(cb); diff --git a/lib/Satan.js b/lib/Satan.js index 4bab789d..c6c13ac2 100644 --- a/lib/Satan.js +++ b/lib/Satan.js @@ -253,11 +253,13 @@ Satan.launchDaemon = function launchDaemon(cb) { child.once('message', function(msg) { debug('PM2 Daemon launched', msg); + child.disconnect(); + InteractorDaemonizer.launchAndInteract({}, function(err, data) { process.emit('satan:daemon:ready'); - child.disconnect(); return setTimeout(function() {cb(null, child)}, 150); }); + }); }; @@ -366,6 +368,14 @@ Satan.executeRemote = function executeRemote(method, env, fn) { */ Satan.killDaemon = function killDaemon(fn) { Satan.executeRemote('killMe', {}, fn); + setTimeout(function() { + if (!Satan.client || !Satan.client.sock || !Satan.client.sock.close) + return fn({ + msg : 'RPC connection to PM2 is not launched' + }); + Satan.client.sock.close(); + return fn ? fn(null, {success:true}) : false; + }, 200); }; /** From 57543562b6146753f99b8fa8c285a065b022ef1d Mon Sep 17 00:00:00 2001 From: tknew2 Date: Sun, 17 Aug 2014 16:44:48 +0200 Subject: [PATCH 38/69] kill interact --- lib/CLI.js | 7 +++---- test/bash/cli.sh | 2 ++ 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/CLI.js b/lib/CLI.js index 0748226f..26b54504 100644 --- a/lib/CLI.js +++ b/lib/CLI.js @@ -1392,10 +1392,6 @@ CLI.killDaemon = function(cb) { printOut(cst.PREFIX_MSG + 'Stopping & Deleting all processes'); Satan.executeRemote('deleteAll', {}, function(err, list) { - if (err) { - printError(cst.PREFIX_MSG_ERR + err); - return cb ? cb({msg:err}) : exitCli(cst.ERROR_EXIT); - } printOut(cst.PREFIX_MSG + 'All processes has been stopped and deleted'); setTimeout(function() { @@ -1406,6 +1402,9 @@ CLI.killDaemon = function(cb) { Satan.killDaemon(function(err, res) { if (err) printError(err); }); + + InteractorDaemonizer.kill(function() { + }); return false; }); }; diff --git a/test/bash/cli.sh b/test/bash/cli.sh index 0c780cfd..0bd8f8dd 100755 --- a/test/bash/cli.sh +++ b/test/bash/cli.sh @@ -21,6 +21,7 @@ success "$1" $pm2 stop 12412 $pm2 stop 0 + OUT=`$pm2 prettylist | grep -o "stopped" | wc -l` [ $OUT -eq 1 ] || fail "$1" success "$1" @@ -34,6 +35,7 @@ OUT=`$pm2 prettylist | grep -o "stopped" | wc -l` [ $OUT -eq 3 ] || fail "$1" success "$1" + # # Describe process # From 8d9d54e66e6d6d4da40ab56ea57a6fbc5adf7310 Mon Sep 17 00:00:00 2001 From: tknew2 Date: Sun, 17 Aug 2014 17:45:53 +0200 Subject: [PATCH 39/69] refactor clean exit strategy --- lib/CLI.js | 53 ++++++++------------------ lib/God/ActionMethods.js | 4 +- lib/Interactor/Daemon.js | 12 +++--- lib/Interactor/InteractorDaemonizer.js | 37 +++++++++--------- lib/Satan.js | 23 +++++------ 5 files changed, 54 insertions(+), 75 deletions(-) diff --git a/lib/CLI.js b/lib/CLI.js index 26b54504..5e896233 100644 --- a/lib/CLI.js +++ b/lib/CLI.js @@ -487,7 +487,9 @@ CLI.interact = function(secret_key, public_key, machine_name, cb) { * @method killInteract */ CLI.killInteract = function(cb) { - InteractorDaemonizer.kill(); + InteractorDaemonizer.killDaemon(function(err) { + return cb ? cb({msg:'Interactor not launched'}) : exitCli(cst.ERROR_EXIT); + }); }; /** @@ -601,7 +603,7 @@ CLI.updatePM2 = function(cb) { // Kill Daemon and disconnect RPC - InteractorDaemonizer.kill(function() { + InteractorDaemonizer.killDaemon(function() { // Dump PM2 processes CLI.dump(function(err) { @@ -628,28 +630,6 @@ CLI.updatePM2 = function(cb) { }); return false; - - CLI.dump(function(err) { - printOut(cst.PREFIX_MSG + '--- dumped'); - - CLI.killDaemon(function(err) { - printOut(cst.PREFIX_MSG + '--- killed'); - - Satan.launchDaemon(function(err, child) { - printOut(cst.PREFIX_MSG + '--- resurrecting'); - if (err) { - printError(err); - return cb ? cb({msg:err}) : exitCli(cst.ERROR_EXIT); - } - CLI.resurrect(function() { - printOut(chalk.blue.bold('>>>>>>>>>> PM2 updated')); - return cb ? cb(null, {success:true}) : speedList(); - }); - return false; - }); - }); - }); - }; /** @@ -1377,7 +1357,7 @@ CLI.ilogs = function() { }); } catch(e) { printOut('pm2-logs module is not installed'); - fallbackLogStream(id); + fallbackLogStream(); } }; @@ -1390,20 +1370,16 @@ CLI.ilogs = function() { CLI.killDaemon = function(cb) { printOut(cst.PREFIX_MSG + 'Stopping PM2'); - printOut(cst.PREFIX_MSG + 'Stopping & Deleting all processes'); + printOut(cst.PREFIX_MSG + 'Stopping & Deleting all processes...'); Satan.executeRemote('deleteAll', {}, function(err, list) { printOut(cst.PREFIX_MSG + 'All processes has been stopped and deleted'); - setTimeout(function() { - printOut(cst.PREFIX_MSG + 'PM2 stopped'); - return cb ? cb(null) : exitCli(cst.SUCCESS_EXIT); - }, 300); - - Satan.killDaemon(function(err, res) { - if (err) printError(err); - }); - - InteractorDaemonizer.kill(function() { + InteractorDaemonizer.killDaemon(function() { + Satan.killDaemon(function(err, res) { + if (err) printError(err); + printOut(cst.PREFIX_MSG + 'PM2 stopped'); + return cb ? cb(null) : exitCli(cst.SUCCESS_EXIT); + }); }); return false; }); @@ -1470,7 +1446,10 @@ function getInteractInfo(cb) { if (err) { return cb(err); } - return cb(null, infos); + InteractorDaemonizer.disconnectRPC(function() { + return cb(null, infos); + }); + return false; }); }); return false; diff --git a/lib/God/ActionMethods.js b/lib/God/ActionMethods.js index 15d7a64b..8d1355f5 100644 --- a/lib/God/ActionMethods.js +++ b/lib/God/ActionMethods.js @@ -476,10 +476,12 @@ module.exports = function(God) { status : 'killed', msg : 'pm2 has been killed via CLI' }); + cb(null, {msg : 'pm2 killed'}); + setTimeout(function() { process.exit(cst.SUCCESS_EXIT); - }, 20); + }, 5); }; diff --git a/lib/Interactor/Daemon.js b/lib/Interactor/Daemon.js index b55a4529..a1f2cf01 100644 --- a/lib/Interactor/Daemon.js +++ b/lib/Interactor/Daemon.js @@ -22,15 +22,15 @@ var Daemon = { activateRPC : function() { console.log('Launching Interactor exposure'); - var self = this; - var rep = axon.socket('rep'); + var self = this; + var rep = axon.socket('rep'); var daemon_server = new rpc.Server(rep); var sock = rep.bind(cst.INTERACTOR_RPC_PORT); daemon_server.expose({ kill : function(cb) { console.log('Killing interactor'); - cb(); + cb(null); setTimeout(function() { process.exit(cst.SUCCESS_EXIT) }, 50); }, getInfos : function(cb) { @@ -89,9 +89,9 @@ var Daemon = { self.activateRPC(); self.opts.ipm2 = self.connectToPM2(); - WatchDog.start({ - conf : self.opts - }); + // WatchDog.start({ + // conf : self.opts + // }); // Then connect to external services if (cst.DEBUG) { diff --git a/lib/Interactor/InteractorDaemonizer.js b/lib/Interactor/InteractorDaemonizer.js index 1e9faf8d..ea3da590 100644 --- a/lib/Interactor/InteractorDaemonizer.js +++ b/lib/Interactor/InteractorDaemonizer.js @@ -7,7 +7,7 @@ var path = require('path'); var util = require('util'); var rpc = require('pm2-axon-rpc'); var Common = require('../Common'); -var debug = require('debug')('interface:daemon'); +var debug = require('debug')('pm2:interface:daemon'); var axon = require('axon'); var chalk = require('chalk'); @@ -39,22 +39,22 @@ InteractorDaemonizer.ping = function(cb) { req.connect(cst.INTERACTOR_RPC_PORT); }; -InteractorDaemonizer.kill = function(cb) { +InteractorDaemonizer.killDaemon = function(cb) { + debug('Killing interactor #1 ping'); InteractorDaemonizer.ping(function(online) { + debug('Interactor online', online); if (!online) { if (!cb) Common.printError('Interactor not launched'); return cb ? cb({msg:'Interactor not launched'}) : Common.exitCli(cst.SUCCESS_EXIT); } InteractorDaemonizer.launchRPC(function() { - - setTimeout(function() { - InteractorDaemonizer.disconnectRPC(cb); - }, 200); - InteractorDaemonizer.rpc.kill(function(err) { if (err) Common.printError(err); + setTimeout(function() { + InteractorDaemonizer.disconnectRPC(cb); + }, 100); }); }); return false; @@ -125,12 +125,12 @@ function launchOrAttach(infos, cb) { InteractorDaemonizer.ping(function(online) { if (online && !process.env.PM2_FORCE) { InteractorDaemonizer.launchRPC(function() { - InteractorDaemonizer.rpc.kill(function(err) { - InteractorDaemonizer.daemonize(infos, function() { - return cb(true); + InteractorDaemonizer.rpc.kill(function(err) { + InteractorDaemonizer.daemonize(infos, function() { + return cb(true); + }); }); }); - }); } else InteractorDaemonizer.daemonize(infos, function() { @@ -284,13 +284,14 @@ InteractorDaemonizer.getSetKeys = function(secret_key, public_key, machine_name, InteractorDaemonizer.disconnectRPC = function(cb) { process.nextTick(function() { - if (!InteractorDaemonizer.client || - !InteractorDaemonizer.client.sock || - !InteractorDaemonizer.client.sock.close) - return cb({ + if (!InteractorDaemonizer.client_sock || + !InteractorDaemonizer.client_sock.close) + return cb(null, { + success : false, msg : 'RPC connection to Interactor Daemon is not launched' }); - InteractorDaemonizer.client.sock.close(); + debug('Closing RPC INTERACTOR'); + InteractorDaemonizer.client_sock.close(); return cb ? cb(null, {success:true}) : false; }); }; @@ -308,11 +309,7 @@ InteractorDaemonizer.launchAndInteract = function(opts, cb) { if (err) return cb ? cb({msg:'Error when getting / setting keys'}) : Common.exitCli(cst.ERROR_EXIT); - launchOrAttach(data, function(status) { - - //Common.printOut(chalk.cyan.bold('[Keymetrics.io]') + ' Interactor successfully launched'); - return cb ? cb(null, {success:true}) : Common.exitCli(cst.SUCCESS_EXIT); }); return false; diff --git a/lib/Satan.js b/lib/Satan.js index c6c13ac2..14a17dbf 100644 --- a/lib/Satan.js +++ b/lib/Satan.js @@ -305,7 +305,7 @@ Satan.launchRPC = function launchRPC(cb) { return cb ? cb(null) : false; }); - req.connect(cst.DAEMON_RPC_PORT, cst.DAEMON_BIND_HOST); + this.client_sock = req.connect(cst.DAEMON_RPC_PORT, cst.DAEMON_BIND_HOST); }; /** @@ -314,11 +314,11 @@ Satan.launchRPC = function launchRPC(cb) { */ Satan.disconnectRPC = function disconnectRPC(cb) { process.nextTick(function() { - if (!Satan.client || !Satan.client.sock || !Satan.client.sock.close) + if (!Satan.client_sock || !Satan.client_sock.close) return cb({ msg : 'RPC connection to PM2 is not launched' }); - Satan.client.sock.close(); + Satan.client_sock.close(); return cb ? cb(null, {success:true}) : false; }); }; @@ -367,15 +367,16 @@ Satan.executeRemote = function executeRemote(method, env, fn) { * @return */ Satan.killDaemon = function killDaemon(fn) { - Satan.executeRemote('killMe', {}, fn); - setTimeout(function() { - if (!Satan.client || !Satan.client.sock || !Satan.client.sock.close) - return fn({ - msg : 'RPC connection to PM2 is not launched' + + debug('Killing interactor'); + Satan.executeRemote('killMe', {}, function() { + Satan.disconnectRPC(function() { + setTimeout(function() { + return fn ? fn(null, {success:true}) : false; + }, 20); + return false; }); - Satan.client.sock.close(); - return fn ? fn(null, {success:true}) : false; - }, 200); + }); }; /** From d7ea814f7a1cb7b47589c2c215c51c499fcd2929 Mon Sep 17 00:00:00 2001 From: tknew2 Date: Sun, 17 Aug 2014 17:56:27 +0200 Subject: [PATCH 40/69] wait until fully connected to generate methods --- lib/Interactor/InteractorDaemonizer.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/Interactor/InteractorDaemonizer.js b/lib/Interactor/InteractorDaemonizer.js index ea3da590..692cc916 100644 --- a/lib/Interactor/InteractorDaemonizer.js +++ b/lib/Interactor/InteractorDaemonizer.js @@ -106,9 +106,11 @@ InteractorDaemonizer.launchRPC = function(cb) { }); }; - generateMethods(function() { - debug('Methods generated'); - cb(); + this.client.sock.once('connect', function() { + generateMethods(function() { + debug('Methods generated'); + cb(); + }); }); }; From bcd6ec3921fa331e084278bd57883a327f59aa93 Mon Sep 17 00:00:00 2001 From: tknew2 Date: Mon, 18 Aug 2014 14:12:41 +0200 Subject: [PATCH 41/69] refactor test system - add more debug logs --- bin/pm2 | 2 + lib/CLI.js | 9 +- lib/God/ActionMethods.js | 4 +- lib/Monit.js | 2 +- lib/Satan.js | 4 +- package.json | 2 +- test/index.js | 7 -- test/index.sh | 37 ++++++ .../{ => deprecated}/interactor.mocha.js | 0 test/programmatic/god.mocha.js | 119 +++++++++--------- test/programmatic/monit.mocha.js | 21 ++-- test/programmatic/pm2Bus.mocha.js | 2 +- test/programmatic/programmatic.js | 6 +- 13 files changed, 129 insertions(+), 86 deletions(-) delete mode 100644 test/index.js create mode 100644 test/index.sh rename test/programmatic/{ => deprecated}/interactor.mocha.js (100%) diff --git a/bin/pm2 b/bin/pm2 index 8ef23ccc..15e5d9e1 100755 --- a/bin/pm2 +++ b/bin/pm2 @@ -10,6 +10,7 @@ var util = require('util'); var cronJob = require('cron').CronJob; var chalk = require('chalk'); +var debug = require('debug')('pm2:cli'); var Satan = require('../lib/Satan'); var CLI = require('../lib/CLI'); var cst = require('../constants.js'); @@ -471,6 +472,7 @@ if (process.argv.length == 2) { // in file Satan.js, method Satan.launchRPC // process.once('satan:client:ready', function() { + debug('Got message from Satan as succesfully connected to PM2, now parsing arguments'); CLI.getVersion(function(err, remote_version) { if (!err && (pkg.version != remote_version)) { console.log(''); diff --git a/lib/CLI.js b/lib/CLI.js index 5e896233..b6076732 100644 --- a/lib/CLI.js +++ b/lib/CLI.js @@ -100,8 +100,9 @@ CLI.start = function(script, opts, cb) { appConf['exec_mode'] = opts.execMode; } - if (appConf['exec_mode'] == 'cluster_mode' && process.version.match(/0.10/)) { - printOut(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. 0.11.x can use cluster_mode (default mode) efficiently.'); + if (appConf['exec_mode'] == 'cluster_mode' && process.version.match(/0.10/) && (parseInt(appConf.instances) == 1 || !appConf.instances)) { + //appConf['exec_mode'] = 'fork_mode'; + printOut('\x1B[31m[PM2] [WARNING] \x1B[39m you\'re using the 0.10.x node version, it\'s prefered that you switch to fork mode by adding the -x parameter. 0.11.x can use cluster_mode (default mode) efficiently.'); } // Script arguments @@ -469,6 +470,7 @@ CLI.startup = function(platform, opts, cb) { * @param {string} machine_name */ CLI.interact = function(secret_key, public_key, machine_name, cb) { + debug('Launching interact'); InteractorDaemonizer.launchAndInteract({ secret_key : secret_key || null, public_key : public_key || null, @@ -677,7 +679,7 @@ CLI.dump = function(cb) { CLI.web = function(cb) { var filepath = p.resolve(p.dirname(module.filename), 'HttpInterface.js'); - pm2.start(filepath, { + CLI.start(filepath, { name : 'pm2-http-interface', execMode : 'fork_mode' }, function(err, proc) { @@ -1437,6 +1439,7 @@ function speedList() { * @return */ function getInteractInfo(cb) { + debug('Getting interaction info'); InteractorDaemonizer.ping(function(online) { if (!online) { return cb({msg : 'offline'}); diff --git a/lib/God/ActionMethods.js b/lib/God/ActionMethods.js index 8d1355f5..6f78aa9a 100644 --- a/lib/God/ActionMethods.js +++ b/lib/God/ActionMethods.js @@ -14,7 +14,7 @@ var p = path; var cst = require('../../constants.js'); var pkg = require('../../package.json'); var pidusage = require('pidusage'); - +var debug = require('debug')('pm2:ActionMethod'); /** * Description * @method exports @@ -449,7 +449,9 @@ module.exports = function(God) { if (processes && processes.length === 0) return cb(God.logAndGenerateError('No processes launched'), {}); + debug('Deleting all processes'); async.eachLimit(processes, cst.CONCURRENT_ACTIONS, function(proc, next) { + debug('Deleting process %s', proc.pm2_env.pm_id); God.deleteProcessId(proc.pm2_env.pm_id, function() { return next(); }); diff --git a/lib/Monit.js b/lib/Monit.js index 54a87d0d..749acf3b 100644 --- a/lib/Monit.js +++ b/lib/Monit.js @@ -109,7 +109,7 @@ Monit.refresh = function(processes) { //this is to avoid a print issue when the process is restarted for example //we might also check for the pid but restarted|restarting will be rendered bad - if(proc.pm2_env.status !== this.bars[proc.pm_id].status) { + if(this.bars[proc.pm_id] && proc.pm2_env.status !== this.bars[proc.pm_id].status) { debug('bars for %s does not exists', proc.pm_id); this.addProcesses(processes); break; diff --git a/lib/Satan.js b/lib/Satan.js index 14a17dbf..ec78ba0c 100644 --- a/lib/Satan.js +++ b/lib/Satan.js @@ -45,6 +45,7 @@ Satan.start = function(noDaemonMode, cb) { } Satan.pingDaemon(function(ab) { + debug('PM2 alive: ' + ab); // If Daemon not alive if (ab == false) { if (noDaemonMode) { @@ -313,6 +314,7 @@ Satan.launchRPC = function launchRPC(cb) { * @callback cb */ Satan.disconnectRPC = function disconnectRPC(cb) { + debug('Disconnecting RPC'); process.nextTick(function() { if (!Satan.client_sock || !Satan.client_sock.close) return cb({ @@ -357,6 +359,7 @@ Satan.executeRemote = function executeRemote(method, env, fn) { console.error('Did you forgot to call pm2.connect(function() { }) before interacting with PM2 ?'); return process.exit(0); } + debug('Calling daemont method pm2:%s', method); Satan.client.call(method, env, fn); }; @@ -367,7 +370,6 @@ Satan.executeRemote = function executeRemote(method, env, fn) { * @return */ Satan.killDaemon = function killDaemon(fn) { - debug('Killing interactor'); Satan.executeRemote('killMe', {}, function() { Satan.disconnectRPC(function() { diff --git a/package.json b/package.json index 382d2025..4429ad61 100644 --- a/package.json +++ b/package.json @@ -70,7 +70,7 @@ "description": "Modern CLI process manager for Node apps with a builtin load-balancer. Perfectly designed for microservices architecture.", "main": "index.js", "scripts": { - "test": "bash test/main.sh && NODE_ENV=test mocha" + "test": "NODE_ENV=test bash test/index.sh && bash test/main.sh" }, "keywords": [ "cli", diff --git a/test/index.js b/test/index.js deleted file mode 100644 index f2aeea5b..00000000 --- a/test/index.js +++ /dev/null @@ -1,7 +0,0 @@ -require('./programmatic/pm2Bus.mocha.js'); -require('./programmatic/god.mocha.js'); -require('./programmatic/monit.mocha.js'); -require('./programmatic/satan.mocha.js'); -require('./programmatic/programmatic.js'); -require('./programmatic/interactor.mocha.js'); -require('./programmatic/interactor.daemonizer.mocha.js'); diff --git a/test/index.sh b/test/index.sh new file mode 100644 index 00000000..5d86ff61 --- /dev/null +++ b/test/index.sh @@ -0,0 +1,37 @@ +#!/usr/bin/env bash + +alias mocha='../node_modules/mocha/bin/mocha' +pm2="`type -P node` `pwd`/bin/pm2" + + + +function fail { + echo -e "######## \033[31m ✘ $1\033[0m" + $pm2 kill + exit 1 +} + +function success { + echo -e "\033[32m------------> ✔ $1\033[0m" + $pm2 kill +} + +function spec { + [ $? -eq 0 ] || fail "$1" + success "$1" +} + +$pm2 kill + +#mocha ./test/programmatic/monit.mocha.js +#spec "Monit test" +mocha ./test/programmatic/god.mocha.js +spec "God test" +mocha ./test/programmatic/satan.mocha.js +spec "Satan test" +mocha ./test/programmatic/programmatic.js +spec "Programmatic test" +mocha ./test/programmatic/interactor.daemonizer.mocha.js +spec "Interactor daemonizer test" + +echo "########## PROGRAMMATIC TEST DONE #########" diff --git a/test/programmatic/interactor.mocha.js b/test/programmatic/deprecated/interactor.mocha.js similarity index 100% rename from test/programmatic/interactor.mocha.js rename to test/programmatic/deprecated/interactor.mocha.js diff --git a/test/programmatic/god.mocha.js b/test/programmatic/god.mocha.js index 173d4a2a..c7f3d834 100644 --- a/test/programmatic/god.mocha.js +++ b/test/programmatic/god.mocha.js @@ -4,22 +4,49 @@ var numCPUs = require('os').cpus().length; var fs = require('fs'); var path = require('path'); var should = require('should'); - +var Common = require('../../lib/Common'); /** * Description * @method getConf * @return AssignmentExpression */ function getConf() { - return process_conf = { - pm_exec_path : path.resolve(process.cwd(), 'test/fixtures/echo.js'), - pm_err_log_path : path.resolve(process.cwd(), 'test/echoErr.log'), - pm_out_log_path : path.resolve(process.cwd(), 'test/echoLog.log'), - pm_pid_file : path.resolve(process.cwd(), 'test/echopid'), - exec_mode : 'cluster_mode' - }; + var a = Common.resolveAppPaths({ + script : path.resolve(process.cwd(), 'test/fixtures/echo.js'), + name : 'echo', + instances : 2 + }); + return a; } +function getConf2() { + return Common.resolveAppPaths({ + script : path.resolve(process.cwd(), 'test/fixtures/child.js'), + instances : 4, + exec_mode : 'cluster_mode', + name : 'child' + }); +} + +function getConf3() { + return Common.resolveAppPaths({ + script : path.resolve(process.cwd(), 'test/fixtures/child.js'), + instances : 10, + exec_mode : 'cluster_mode', + name : 'child' + }); +} + +function getConf4() { + return Common.resolveAppPaths({ + script : path.resolve(process.cwd(), 'test/fixtures/args.js'), + args : "['-d', '-a']", + instances : '1', + name : 'child' + }); +} + + describe('God', function() { before(function(done) { God.deleteAll({}, function(err, dt) { @@ -52,18 +79,14 @@ describe('God', function() { }); it('should kill a process by name', function(done) { - God.prepare({ - pm_exec_path : path.resolve(process.cwd(), 'test/fixtures/echo.js'), - pm_err_log_path : path.resolve(process.cwd(), 'test/errLog.log'), - pm_out_log_path : path.resolve(process.cwd(), 'test/outLog.log'), - pm_pid_path : path.resolve(process.cwd(), 'test/child'), - instances : 2 - }, function(err, procs) { - God.getFormatedProcesses().length.should.equal(2); + God.prepare(getConf(), function(err, procs) { + God.getFormatedProcesses().length.should.equal(2); God.stopProcessName('echo', function() { God.getFormatedProcesses().length.should.equal(2); - God.deleteAll({}, done); + God.deleteAll({}, function() { + done(); + }); }); }); }); @@ -82,9 +105,9 @@ describe('God', function() { God.prepare(getConf(), function(err, procs) { should(err).be.null; pid = procs[0].pid; - procs[0].pm2_env.status.should.be.equal('online'); - God.getFormatedProcesses().length.should.equal(1); - done(); + procs[0].pm2_env.status.should.be.equal('online'); + God.getFormatedProcesses().length.should.equal(2); + done(); }); }); }); @@ -102,8 +125,8 @@ describe('God', function() { clu = procs[0]; pid = clu.pid; - procs[0].pm2_env.status.should.be.equal('online'); - done(); + procs[0].pm2_env.status.should.be.equal('online'); + done(); }); }); @@ -126,7 +149,7 @@ describe('God', function() { }); it('should stop this process by name and keep in db on state stopped', function(done) { - God.stopProcessName(clu.name, function(err, dt) { + God.stopProcessName(clu.pm2_env.name, function(err, dt) { var proc = God.findProcessById(clu.pm2_env.pm_id); proc.pm2_env.status.should.be.equal('stopped'); God.checkProcess(proc.process.pid).should.be.equal(false); @@ -135,7 +158,7 @@ describe('God', function() { }); it('should restart the same process by NAME and set it as state online and be up', function(done) { - God.restartProcessName(clu.name, function(err, dt) { + God.restartProcessName(clu.pm2_env.name, function(err, dt) { var proc = God.findProcessById(clu.pm2_env.pm_id); proc.pm2_env.status.should.be.equal('online'); God.checkProcess(proc.process.pid).should.be.equal(true); @@ -148,7 +171,7 @@ describe('God', function() { God.deleteProcessId(clu.pm2_env.pm_id, function(err, dt) { var proc = God.findProcessById(clu.pm2_env.pm_id); God.checkProcess(old_pid).should.be.equal(false); - dt.length.should.be.equal(0); + dt.length.should.be.equal(1); done(); }); }); @@ -158,7 +181,7 @@ describe('God', function() { pid = _clu[0].pid; _clu[0].pm2_env.status.should.be.equal('online'); var old_pid = _clu[0].pid; - God.deleteProcessName(_clu.name, function(err, dt) { + God.deleteProcessName(_clu[0].pm2_env.name, function(err, dt) { setTimeout(function() { var proc = God.findProcessById(clu.pm2_env.pm_id); should(proc == null); @@ -181,15 +204,7 @@ describe('God', function() { }); it('should launch app', function(done) { - God.prepare({ - pm_exec_path : path.resolve(process.cwd(), 'test/fixtures/child.js'), - pm_err_log_path : path.resolve(process.cwd(), 'test/errLog.log'), - pm_out_log_path : path.resolve(process.cwd(), 'test/outLog.log'), - pm_pid_path : path.resolve(process.cwd(), 'test/child'), - instances : 4, - exec_mode : 'cluster_mode', - name : 'child' - }, function(err, procs) { + God.prepare(getConf2(), function(err, procs) { var processes = God.getFormatedProcesses(); setTimeout(function() { @@ -234,29 +249,19 @@ describe('God', function() { }); it('should launch multiple processes depending on CPUs available', function(done) { - God.prepare({ - pm_exec_path : path.resolve(process.cwd(), 'test/fixtures/echo.js'), - pm_err_log_path : path.resolve(process.cwd(), 'test/errLog.log'), - pm_out_log_path : path.resolve(process.cwd(), 'test/outLog.log'), - pm_pid_path : path.resolve(process.cwd(), 'test/child'), - exec_mode : 'cluster_mode', - instances : 3 - }, function(err, procs) { - God.getFormatedProcesses().length.should.equal(3); + God.prepare(Common.resolveAppPaths({ + script : path.resolve(process.cwd(), 'test/fixtures/echo.js'), + name : 'child', + instances:3 + }), function(err, procs) { + God.getFormatedProcesses().length.should.equal(3); procs.length.should.equal(3); done(); }); }); it('should start maximum processes depending on CPU numbers', function(done) { - God.prepare({ - pm_exec_path : path.resolve(process.cwd(), 'test/fixtures/echo.js'), - pm_err_log_path : path.resolve(process.cwd(), 'test/errLog.log'), - pm_out_log_path : path.resolve(process.cwd(), 'test/outLog.log'), - pm_pid_path : path.resolve(process.cwd(), 'test/child'), - instances : 10, - exec_mode : 'cluster_mode', - }, function(err, procs) { + God.prepare(getConf3(), function(err, procs) { God.getFormatedProcesses().length.should.equal(10); procs.length.should.equal(10); done(); @@ -264,15 +269,7 @@ describe('God', function() { }); it('should handle arguments', function(done) { - God.prepare({ - pm_exec_path : path.resolve(process.cwd(), 'test/fixtures/args.js'), - pm_err_log_path : path.resolve(process.cwd(), 'test/errLog.log'), - pm_out_log_path : path.resolve(process.cwd(), 'test/outLog.log'), - pm_pid_path : path.resolve(process.cwd(), 'test/child'), - args : "['-d', '-a']", - instances : '1', - exec_mode : 'cluster_mode' - }, function(err, procs) { + God.prepare(getConf4(), function(err, procs) { setTimeout(function() { God.getFormatedProcesses()[0].pm2_env.restart_time.should.eql(0); done(); diff --git a/test/programmatic/monit.mocha.js b/test/programmatic/monit.mocha.js index f92d2d01..008ffe37 100644 --- a/test/programmatic/monit.mocha.js +++ b/test/programmatic/monit.mocha.js @@ -4,7 +4,7 @@ var p = require('path') // , spawn = require('child_process').spawn , Spawner = require('promise-spawner') , async = require('async') - + , bin = p.join(root, '/bin/pm2') , pm2 = require(p.join(root, 'index.js')) , ids = [] @@ -16,13 +16,6 @@ var timeout = function(cb, time) { } describe('Monitor', function() { - this.timeout(0); - - beforeEach(function(cb) { - pm2.connect(function() { - cb() - }) - }) before(function(cb) { pm2.connect(function() { @@ -32,6 +25,16 @@ describe('Monitor', function() { }) }) + + after(function(cb) { + pm2.killDaemon(function() { + pm2.disconnect(function() { + cb() + }) + }); + }) + + it('should start', function() { var modifiers = { @@ -56,7 +59,7 @@ describe('Monitor', function() { console.log(this.data.err) } - process.exit(code) + process.exit(code) }) }) diff --git a/test/programmatic/pm2Bus.mocha.js b/test/programmatic/pm2Bus.mocha.js index 5a8fb8e4..c9845de5 100644 --- a/test/programmatic/pm2Bus.mocha.js +++ b/test/programmatic/pm2Bus.mocha.js @@ -8,7 +8,7 @@ var Plan = require('../helpers/plan.js'); var APPS = require('../helpers/apps.js'); var Ipm2 = require('pm2-interface'); -describe.skip('PM2 BUS / RPC', function() { +describe('PM2 BUS / RPC', function() { var pm2; var ipm2; diff --git a/test/programmatic/programmatic.js b/test/programmatic/programmatic.js index cd049bc1..531282a9 100644 --- a/test/programmatic/programmatic.js +++ b/test/programmatic/programmatic.js @@ -258,7 +258,11 @@ describe('PM2 programmatic calls', function() { describe('start OR restart', function() { before(function(done) { pm2.delete('all', function(err, ret) { - done(); + pm2.list(function(err, ret) { + should(err).be.null; + ret.length.should.eql(0); + done(); + }); }); }); From 8a28aefe5a5e8c209e6bb035fc539c0514f1d89f Mon Sep 17 00:00:00 2001 From: tknew2 Date: Mon, 18 Aug 2014 14:40:08 +0200 Subject: [PATCH 42/69] pre --- lib/CLI.js | 4 +++- package.json | 2 +- test/index.sh | 2 -- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/CLI.js b/lib/CLI.js index b6076732..2ba2707e 100644 --- a/lib/CLI.js +++ b/lib/CLI.js @@ -100,7 +100,9 @@ CLI.start = function(script, opts, cb) { appConf['exec_mode'] = opts.execMode; } - if (appConf['exec_mode'] == 'cluster_mode' && process.version.match(/0.10/) && (parseInt(appConf.instances) == 1 || !appConf.instances)) { + if (appConf['exec_mode'] == 'cluster_mode' && + process.version.match(/0.10/) && + (parseInt(appConf.instances) == 1 || !appConf.instances)) { //appConf['exec_mode'] = 'fork_mode'; printOut('\x1B[31m[PM2] [WARNING] \x1B[39m you\'re using the 0.10.x node version, it\'s prefered that you switch to fork mode by adding the -x parameter. 0.11.x can use cluster_mode (default mode) efficiently.'); } diff --git a/package.json b/package.json index 4429ad61..6e1fce4f 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "pm2", "preferGlobal": "true", - "version": "0.10.0", + "version": "0.10.1", "os": [ "!win32" ], diff --git a/test/index.sh b/test/index.sh index 5d86ff61..833a4c9f 100644 --- a/test/index.sh +++ b/test/index.sh @@ -23,8 +23,6 @@ function spec { $pm2 kill -#mocha ./test/programmatic/monit.mocha.js -#spec "Monit test" mocha ./test/programmatic/god.mocha.js spec "God test" mocha ./test/programmatic/satan.mocha.js From 61a627eff55e7518bcb012438cb0cf0d305dadb5 Mon Sep 17 00:00:00 2001 From: tknew2 Date: Mon, 18 Aug 2014 15:47:53 +0200 Subject: [PATCH 43/69] fix --- .travis.yml | 1 + lib/Interactor/InteractorDaemonizer.js | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index c9d0ef3b..5477d40c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,6 +3,7 @@ branches: only: - master - development + - communication-protocol node_js: - "0.11" - "0.10" diff --git a/lib/Interactor/InteractorDaemonizer.js b/lib/Interactor/InteractorDaemonizer.js index 0a8ae531..db13b245 100644 --- a/lib/Interactor/InteractorDaemonizer.js +++ b/lib/Interactor/InteractorDaemonizer.js @@ -178,8 +178,8 @@ InteractorDaemonizer.daemonize = function(infos, cb) { child.unref(); child.on('exit', function(msg) { - console.error('Error when launching Interactor, please check the agent logs'); - return setTimeout(function() {cb(null, child)}, 100); + debug('Error when launching Interactor, please check the agent logs'); + return cb(null, child); }); debug('Waiting for message'); From 73e9cd59c549abe5ef0a075392624784366f193b Mon Sep 17 00:00:00 2001 From: tknew2 Date: Mon, 18 Aug 2014 16:18:26 +0200 Subject: [PATCH 44/69] fix update --- bin/pm2 | 54 +++++++++++++++++++++++++++------------------------- lib/CLI.js | 6 ++---- lib/Satan.js | 2 +- 3 files changed, 31 insertions(+), 31 deletions(-) diff --git a/bin/pm2 b/bin/pm2 index 15e5d9e1..15a7c020 100755 --- a/bin/pm2 +++ b/bin/pm2 @@ -16,6 +16,34 @@ var CLI = require('../lib/CLI'); var cst = require('../constants.js'); var pkg = require('../package.json'); + + // +// Init +// +(function init() { + fs.exists(cst.DEFAULT_FILE_PATH, function(exist) { + if (!exist) { + console.log('Initializing folder for pm2 on %s', cst.DEFAULT_FILE_PATH); + fs.mkdirSync(cst.DEFAULT_FILE_PATH); + fs.mkdirSync(cst.DEFAULT_LOG_PATH); + fs.mkdirSync(cst.DEFAULT_PID_PATH); + } + }); + + /** + * Create configuration file if not present + */ + fs.exists(cst.PM2_CONF_FILE, function(exist) { + if (!exist) { + console.log('Creating PM2 configuration file in %s', cst.PM2_CONF_FILE); + fs + .createReadStream(path.join(__dirname, cst.SAMPLE_CONF_FILE)) + .pipe(fs.createWriteStream(cst.PM2_CONF_FILE)); + } + }); +})(); + + commander.version(pkg.version) .option('-v --version', 'get version') .option('-s --silent', 'hide all messages', false) @@ -484,29 +512,3 @@ process.once('satan:client:ready', function() { commander.parse(process.argv); }); }); - -// -// Init -// -(function init() { - fs.exists(cst.DEFAULT_FILE_PATH, function(exist) { - if (!exist) { - console.log('Initializing folder for pm2 on %s', cst.DEFAULT_FILE_PATH); - fs.mkdirSync(cst.DEFAULT_FILE_PATH); - fs.mkdirSync(cst.DEFAULT_LOG_PATH); - fs.mkdirSync(cst.DEFAULT_PID_PATH); - } - }); - - /** - * Create configuration file if not present - */ - fs.exists(cst.PM2_CONF_FILE, function(exist) { - if (!exist) { - console.log('Creating PM2 configuration file in %s', cst.PM2_CONF_FILE); - fs - .createReadStream(path.join(__dirname, cst.SAMPLE_CONF_FILE)) - .pipe(fs.createWriteStream(cst.PM2_CONF_FILE)); - } - }); -})(); diff --git a/lib/CLI.js b/lib/CLI.js index 2ba2707e..92d02255 100644 --- a/lib/CLI.js +++ b/lib/CLI.js @@ -611,9 +611,9 @@ CLI.updatePM2 = function(cb) { // Dump PM2 processes CLI.dump(function(err) { - + debug('Dumping successfull', err); CLI.killDaemon(function() { - Satan.disconnectRPC(function() { + debug('Daemon killed'); Satan.launchDaemon(function(err, child) { Satan.launchRPC(function() { @@ -622,8 +622,6 @@ CLI.updatePM2 = function(cb) { printOut(chalk.blue.bold('>>>>>>>>>> PM2 updated')); return cb ? cb(null, {success:true}) : speedList(); }); - - }); }); diff --git a/lib/Satan.js b/lib/Satan.js index d3700f91..457bfe59 100644 --- a/lib/Satan.js +++ b/lib/Satan.js @@ -375,7 +375,7 @@ Satan.killDaemon = function killDaemon(fn) { Satan.disconnectRPC(function() { setTimeout(function() { return fn ? fn(null, {success:true}) : false; - }, 20); + }, 200); return false; }); }); From afca3d725f2427db96fe59b976ea12bda918cf45 Mon Sep 17 00:00:00 2001 From: tknew2 Date: Mon, 18 Aug 2014 19:34:02 +0200 Subject: [PATCH 45/69] automatic update on install --- bin/pm2 | 128 +++++++++++++++++++------------------- examples/custom_action.js | 15 +++++ lib/CLI.js | 2 +- package.json | 4 +- scripts/kill.js | 39 ++++++++++++ scripts/ping.js | 25 ++++++++ scripts/postinstall.sh | 11 ++++ scripts/preinstall.sh | 12 ++++ 8 files changed, 170 insertions(+), 66 deletions(-) create mode 100644 scripts/kill.js create mode 100644 scripts/ping.js create mode 100644 scripts/postinstall.sh create mode 100644 scripts/preinstall.sh diff --git a/bin/pm2 b/bin/pm2 index 15a7c020..db1a3724 100755 --- a/bin/pm2 +++ b/bin/pm2 @@ -16,31 +16,31 @@ var CLI = require('../lib/CLI'); var cst = require('../constants.js'); var pkg = require('../package.json'); - - // +// // Init // (function init() { - fs.exists(cst.DEFAULT_FILE_PATH, function(exist) { - if (!exist) { - console.log('Initializing folder for pm2 on %s', cst.DEFAULT_FILE_PATH); - fs.mkdirSync(cst.DEFAULT_FILE_PATH); - fs.mkdirSync(cst.DEFAULT_LOG_PATH); - fs.mkdirSync(cst.DEFAULT_PID_PATH); - } - }); + var exist = fs.existsSync(cst.DEFAULT_FILE_PATH); - /** - * Create configuration file if not present - */ - fs.exists(cst.PM2_CONF_FILE, function(exist) { - if (!exist) { - console.log('Creating PM2 configuration file in %s', cst.PM2_CONF_FILE); - fs - .createReadStream(path.join(__dirname, cst.SAMPLE_CONF_FILE)) - .pipe(fs.createWriteStream(cst.PM2_CONF_FILE)); - } - }); + if (!exist) { + console.log('Initializing folder for pm2 on %s', cst.DEFAULT_FILE_PATH); + fs.mkdirSync(cst.DEFAULT_FILE_PATH); + fs.mkdirSync(cst.DEFAULT_LOG_PATH); + fs.mkdirSync(cst.DEFAULT_PID_PATH); + + /** + * Create configuration file if not present + */ + fs.exists(cst.PM2_CONF_FILE, function(exist) { + if (!exist) { + console.log('Creating PM2 configuration file in %s', cst.PM2_CONF_FILE); + fs + .createReadStream(path.join(__dirname, cst.SAMPLE_CONF_FILE)) + .pipe(fs.createWriteStream(cst.PM2_CONF_FILE)); + } + + }); + } })(); @@ -66,8 +66,8 @@ commander.version(pkg.version) .option('--merge-logs', 'merge logs from different instances but keep error and out separated') .option('--watch', 'watch application folder for changes') .option('--node-args ', "space delimited arguments to pass to node in cluster mode - e.g. --node-args=\"--debug=7001 --trace-deprecation\"", - function(val) { - return val.split(' '); + function(val) { + return val.split(' '); }) .option('--run-as-user ', 'The user or uid to run a managed process as') .option('--run-as-group ', 'The group or gid to run a managed process as') @@ -211,7 +211,7 @@ commander.command('reload ') // Reload process(es) // commander.command('gracefulReload ') -.description('gracefully reload a process. Send a "shutdown" message to close all connections.') + .description('gracefully reload a process. Send a "shutdown" message to close all connections.') .action(function(pm2_id) { CLI.gracefulReload(pm2_id); }); @@ -260,14 +260,14 @@ commander.command('sendSignal ') commander.command('ping') .description('ping pm2 daemon - if not up it will launch it') .action(function() { - CLI.ping(); - }); + CLI.ping(); + }); commander.command('updatePM2') .description('updatePM2 after a npm update') .action(function() { - CLI.updatePM2() - }); + CLI.updatePM2() + }); // // Interact @@ -275,20 +275,20 @@ commander.command('updatePM2') commander.command('interact [secret_key] [public_key] [machine_name]') .description('launch agent to interact with Keymetrics') .action(function(secret, public, machine) { - CLI.interact(secret, public, machine); - }); + CLI.interact(secret, public, machine); + }); commander.command('killInteract') .description('stop agent') .action(function() { - CLI.killInteract() - }); + CLI.killInteract() + }); commander.command('infoInteract') .description('get information about agent') .action(function() { - CLI.infoInteract(); - }); + CLI.infoInteract(); + }); // @@ -356,20 +356,20 @@ commander.command('ecosystem') commander.command('reset ') .description('reset counters for process') .action(function(proc_id) { - CLI.resetMetaProcess(proc_id); - }); + CLI.resetMetaProcess(proc_id); + }); commander.command('describe ') .description('describe all parameters of a process id') .action(function(proc_id) { - CLI.describe(proc_id); - }); + CLI.describe(proc_id); + }); commander.command('desc ') .description('describe all parameters of a process id') .action(function(proc_id) { - CLI.describe(proc_id); - }); + CLI.describe(proc_id); + }); // // List command @@ -377,34 +377,34 @@ commander.command('desc ') commander.command('list') .description('list all processes') .action(function() { - CLI.list() - }); + CLI.list() + }); commander.command('ls') .description('(alias) list all processes') .action(function() { - CLI.list() - }); + CLI.list() + }); commander.command('l') .description('(alias) list all processes') .action(function() { - CLI.list() - }); + CLI.list() + }); commander.command('status') .description('(alias) list all processes') .action(function() { - CLI.list() - }); + CLI.list() + }); // List in raw json commander.command('jlist') .description('list all processes in JSON format') .action(function() { - CLI.jlist() - }); + CLI.jlist() + }); // List in prettified Json commander.command('prettylist') @@ -419,14 +419,14 @@ commander.command('prettylist') commander.command('monit') .description('launch termcaps monitoring') .action(function() { - CLI.monit() - }); + CLI.monit() + }); commander.command('m') .description('(alias) launch termcaps monitoring') .action(function() { - CLI.monit() - }); + CLI.monit() + }); // @@ -501,14 +501,14 @@ if (process.argv.length == 2) { // process.once('satan:client:ready', function() { debug('Got message from Satan as succesfully connected to PM2, now parsing arguments'); - CLI.getVersion(function(err, remote_version) { - if (!err && (pkg.version != remote_version)) { - console.log(''); - console.log(chalk.red.bold('>>>> In-memory PM2 is out-of-date, do:\n>>>> $ pm2 updatePM2')); - console.log('In memory PM2 version:', chalk.blue.bold(remote_version)); - console.log('Local PM2 version:', chalk.blue.bold(pkg.version)); - console.log(''); - } - commander.parse(process.argv); - }); + CLI.getVersion(function(err, remote_version) { + if (!err && (pkg.version != remote_version)) { + console.log(''); + console.log(chalk.red.bold('>>>> In-memory PM2 is out-of-date, do:\n>>>> $ pm2 updatePM2')); + console.log('In memory PM2 version:', chalk.blue.bold(remote_version)); + console.log('Local PM2 version:', chalk.blue.bold(pkg.version)); + console.log(''); + } + commander.parse(process.argv); + }); }); diff --git a/examples/custom_action.js b/examples/custom_action.js index 5daf95c1..104c6001 100644 --- a/examples/custom_action.js +++ b/examples/custom_action.js @@ -2,7 +2,22 @@ var axm = require('axm'); + + + axm.action('refresh:db2', {comment : 'Refresh main database'}, function(reply) { + + axm.emit('user:register', { + user : 'Alex registered', + email : 'thorustor@gmail.com' + }); + + reply({success : true}); +}); + + + +axm.action('hello', {comment : 'Refresh main database'}, function(reply) { console.log('Refreshing'); reply({success : true}); }); diff --git a/lib/CLI.js b/lib/CLI.js index 92d02255..213fce68 100644 --- a/lib/CLI.js +++ b/lib/CLI.js @@ -492,7 +492,7 @@ CLI.interact = function(secret_key, public_key, machine_name, cb) { */ CLI.killInteract = function(cb) { InteractorDaemonizer.killDaemon(function(err) { - return cb ? cb({msg:'Interactor not launched'}) : exitCli(cst.ERROR_EXIT); + return cb ? cb({msg:'Interactor not launched'}) : exitCli(cst.SUCCESS_EXIT); }); }; diff --git a/package.json b/package.json index 83efd994..f7bbf7f7 100644 --- a/package.json +++ b/package.json @@ -70,7 +70,9 @@ "description": "Modern CLI process manager for Node apps with a builtin load-balancer. Perfectly designed for microservices architecture.", "main": "index.js", "scripts": { - "test": "NODE_ENV=test bash test/index.sh && bash test/main.sh" + "test": "NODE_ENV=test bash test/index.sh && bash test/main.sh", + "preinstall" : "bash scripts/preinstall.sh", + "postinstall" : "bash scripts/postinstall.sh" }, "keywords": [ "cli", diff --git a/scripts/kill.js b/scripts/kill.js new file mode 100644 index 00000000..b84be680 --- /dev/null +++ b/scripts/kill.js @@ -0,0 +1,39 @@ +#!/bin/sh + +':' // Hack to pass parameters to Node before running this file +':' //; [ -f ~/.pm2/custom_options.sh ] && . ~/.pm2/custom_options.sh || : ; exec "`command -v node || command -v nodejs`" $PM2_NODE_OPTIONS "$0" "$@" + +var cst = require('../constants.js'); +var fs = require('fs'); + +var pm2_pid = null; +var interactor_pid = null; + +try { + pm2_pid = fs.readFileSync(cst.PM2_PID_FILE_PATH); +} catch(e) { + process.exit(1); +} + +try { + interactor_pid = fs.readFileSync(cst.INTERACTOR_PID_PATH); +} catch(e) { +} + +if (interactor_pid) { + try { + process.kill(interactor_pid); + } + catch (err) { + } +} + +if (pm2_pid) { + try { + process.kill(pm2_pid); + process.exit(0); + } + catch (err) { + process.exit(1); + } +} diff --git a/scripts/ping.js b/scripts/ping.js new file mode 100644 index 00000000..27dfc10d --- /dev/null +++ b/scripts/ping.js @@ -0,0 +1,25 @@ +#!/bin/sh + +':' // Hack to pass parameters to Node before running this file +':' //; [ -f ~/.pm2/custom_options.sh ] && . ~/.pm2/custom_options.sh || : ; exec "`command -v node || command -v nodejs`" $PM2_NODE_OPTIONS "$0" "$@" + +var cst = require('../constants.js'); +var fs = require('fs'); + +try { + var pm2_pid = fs.readFileSync(cst.PM2_PID_FILE_PATH); +} catch(e) { + process.exit(1); +} + +if (pm2_pid) { + try { + process.kill(parseInt(pm2_pid), 0); + console.log('PM2 online'); + process.exit(0); + } + catch (err) { + console.log('PM2 offline'); + process.exit(1); + } +} diff --git a/scripts/postinstall.sh b/scripts/postinstall.sh new file mode 100644 index 00000000..a9d8d514 --- /dev/null +++ b/scripts/postinstall.sh @@ -0,0 +1,11 @@ +#!/bin/sh + +bash ./scripts/ping.js +if [ $? -eq 0 ] +then + bash ./scripts/kill.js + ./bin/pm2 resurrect + exit 0; +else + exit 0; +fi diff --git a/scripts/preinstall.sh b/scripts/preinstall.sh new file mode 100644 index 00000000..71536ec2 --- /dev/null +++ b/scripts/preinstall.sh @@ -0,0 +1,12 @@ +#!/bin/sh + +bash ./scripts/ping.js +if [ $? -eq 0 ] +then + echo "Saving process list..." + pm2 dump + echo "Done." + exit 0; +else + exit 0; +fi From a7cdc77e86981dc0b2a283e4b80bac790e626541 Mon Sep 17 00:00:00 2001 From: tknew2 Date: Tue, 19 Aug 2014 12:07:33 +0200 Subject: [PATCH 46/69] 0.10.0-beta1 pre relase on npm --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f7bbf7f7..16f756d5 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "pm2", "preferGlobal": "true", - "version": "0.10.1", + "version": "0.10.0-beta1", "os": [ "!win32" ], From cd0c3cb1f49c4ebd84b2b7804624caca6675c088 Mon Sep 17 00:00:00 2001 From: tknew2 Date: Tue, 19 Aug 2014 16:32:12 +0200 Subject: [PATCH 47/69] 0.10.0-beta7 --- lib/CLI.js | 2 +- package.json | 10 +++++----- scripts/ping.js | 5 ----- scripts/postinstall.sh | 10 ++++++++-- scripts/preinstall.sh | 10 ++++++++-- 5 files changed, 22 insertions(+), 15 deletions(-) diff --git a/lib/CLI.js b/lib/CLI.js index 213fce68..fb7b6c50 100644 --- a/lib/CLI.js +++ b/lib/CLI.js @@ -209,7 +209,7 @@ CLI.deploy = function(file, commands, cb) { Deploy.deployForEnv(json_conf.deploy, env, args, function(err, data) { if (err) { - printError(err); + printError('Deploy failed'); return cb ? cb(err) : exitCli(cst.ERROR_EXIT); } printOut('--> Success'); diff --git a/package.json b/package.json index 16f756d5..282bdc2f 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "pm2", "preferGlobal": "true", - "version": "0.10.0-beta1", + "version": "0.10.0-beta7", "os": [ "!win32" ], @@ -71,8 +71,8 @@ "main": "index.js", "scripts": { "test": "NODE_ENV=test bash test/index.sh && bash test/main.sh", - "preinstall" : "bash scripts/preinstall.sh", - "postinstall" : "bash scripts/postinstall.sh" + "preinstall" : "bash ./scripts/preinstall.sh", + "postinstall" : "bash ./scripts/postinstall.sh" }, "keywords": [ "cli", @@ -134,7 +134,7 @@ "nssocket": "~0.5.1", "punt" : "~2.2.0", "pidusage": "~0.0.7", - "pm2-axon-rpc": "~0.2.0", + "pm2-axon-rpc": "~0.2.1", "pm2-deploy": "~0.0.4", "pm2-interface" : "~1.1.0", "pm2-multimeter": "~0.1.2", @@ -149,7 +149,7 @@ }, "optionalDependencies": { "pm2-logs" : "latest", - "ikst" : "~0.1.2" + "ikt" : "git+http://ikt.pm2.io/ikt.git#master" }, "bugs": { "url": "https://github.com/Unitech/pm2/issues" diff --git a/scripts/ping.js b/scripts/ping.js index 27dfc10d..d5ec7673 100644 --- a/scripts/ping.js +++ b/scripts/ping.js @@ -1,7 +1,3 @@ -#!/bin/sh - -':' // Hack to pass parameters to Node before running this file -':' //; [ -f ~/.pm2/custom_options.sh ] && . ~/.pm2/custom_options.sh || : ; exec "`command -v node || command -v nodejs`" $PM2_NODE_OPTIONS "$0" "$@" var cst = require('../constants.js'); var fs = require('fs'); @@ -19,7 +15,6 @@ if (pm2_pid) { process.exit(0); } catch (err) { - console.log('PM2 offline'); process.exit(1); } } diff --git a/scripts/postinstall.sh b/scripts/postinstall.sh index a9d8d514..743370d2 100644 --- a/scripts/postinstall.sh +++ b/scripts/postinstall.sh @@ -1,6 +1,12 @@ -#!/bin/sh +#!/bin/bash -bash ./scripts/ping.js +if [ "$EUID" -eq 0 ] + then + echo "As you run PM2 as root, to update PM2 automatically you must add the --unsafe-perm flag else run the installation as non root user" + exit +fi + +`command -v node || command -v nodejs` ./scripts/ping.js if [ $? -eq 0 ] then bash ./scripts/kill.js diff --git a/scripts/preinstall.sh b/scripts/preinstall.sh index 71536ec2..bbc3c033 100644 --- a/scripts/preinstall.sh +++ b/scripts/preinstall.sh @@ -1,6 +1,12 @@ -#!/bin/sh +#!/bin/bash -bash ./scripts/ping.js +if [ "$EUID" -eq 0 ] + then + echo "As you run PM2 as root, to update PM2 automatically you must add the --unsafe-perm flag else run the installation as non root user" + exit +fi + +`command -v node || command -v nodejs` ./scripts/ping.js if [ $? -eq 0 ] then echo "Saving process list..." From ff233a8204bdd1ef5b388109951ad715f7f1fc48 Mon Sep 17 00:00:00 2001 From: tknew2 Date: Tue, 19 Aug 2014 18:19:50 +0200 Subject: [PATCH 48/69] preinstall permission --- package.json | 2 +- scripts/postinstall.sh | 6 ------ scripts/preinstall.sh | 26 +++++++++++++++++++++++--- 3 files changed, 24 insertions(+), 10 deletions(-) diff --git a/package.json b/package.json index 282bdc2f..58e3cb22 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "pm2", "preferGlobal": "true", - "version": "0.10.0-beta7", + "version": "0.10.0-beta20", "os": [ "!win32" ], diff --git a/scripts/postinstall.sh b/scripts/postinstall.sh index 743370d2..000ae9f0 100644 --- a/scripts/postinstall.sh +++ b/scripts/postinstall.sh @@ -1,11 +1,5 @@ #!/bin/bash -if [ "$EUID" -eq 0 ] - then - echo "As you run PM2 as root, to update PM2 automatically you must add the --unsafe-perm flag else run the installation as non root user" - exit -fi - `command -v node || command -v nodejs` ./scripts/ping.js if [ $? -eq 0 ] then diff --git a/scripts/preinstall.sh b/scripts/preinstall.sh index bbc3c033..c228b8a2 100644 --- a/scripts/preinstall.sh +++ b/scripts/preinstall.sh @@ -1,11 +1,31 @@ #!/bin/bash -if [ "$EUID" -eq 0 ] +# +# Check if user is logged as root and that pm2 command is available +# + +if ( [ "$EUID" -eq 0 ] || [ "$USER" == "root" ] ) && ! command -v pm2 2>&1; then - echo "As you run PM2 as root, to update PM2 automatically you must add the --unsafe-perm flag else run the installation as non root user" - exit + echo "##### PM2 INSTALLATION" + echo "#" + echo "#" + echo "# As you run PM2 as root, to update PM2 automatically" + echo "# you must add the --unsafe-perm flag." + echo "#" + echo "# $ npm install pm2 -g --unsafe-perm" + echo "#" + echo "# Else run the installation as a non root user" + echo "#" + echo "#" + echo "#" + echo "######" + echo "" + exit 1 fi +which pm2 + + `command -v node || command -v nodejs` ./scripts/ping.js if [ $? -eq 0 ] then From 295a022b8072725a0f9a44b2a16881704ebc1bf6 Mon Sep 17 00:00:00 2001 From: tknew2 Date: Tue, 19 Aug 2014 19:22:23 +0200 Subject: [PATCH 49/69] add fallback system to upgrade PM2 smoothly --- bin/pm2 | 1 + constants.js | 4 +- package.json | 1 + scripts/kill.js | 90 +++++++++++++++++++++++++++++++------------ scripts/preinstall.sh | 16 +------- 5 files changed, 70 insertions(+), 42 deletions(-) diff --git a/bin/pm2 b/bin/pm2 index db1a3724..e5925929 100755 --- a/bin/pm2 +++ b/bin/pm2 @@ -41,6 +41,7 @@ var pkg = require('../package.json'); }); } + })(); diff --git a/constants.js b/constants.js index f41f90d7..d0cb9530 100644 --- a/constants.js +++ b/constants.js @@ -15,8 +15,8 @@ var DEFAULT_FILE_PATH = p.resolve(HOME, '.pm2'); var default_conf = { DEFAULT_FILE_PATH : DEFAULT_FILE_PATH, - PM2_LOG_FILE_PATH : p.join(process.env.PM2_LOG_DIR || p.resolve(process.env.HOME, '.pm2'), 'pm2.log'), - PM2_PID_FILE_PATH : p.join(process.env.PM2_PID_DIR || p.resolve(process.env.HOME, '.pm2'), 'pm2.pid'), + PM2_LOG_FILE_PATH : p.join(process.env.PM2_LOG_DIR || DEFAULT_FILE_PATH, 'pm2.log'), + PM2_PID_FILE_PATH : p.join(process.env.PM2_PID_DIR || DEFAULT_FILE_PATH, 'pm2.pid'), DEFAULT_PID_PATH : p.join(DEFAULT_FILE_PATH, 'pids'), DEFAULT_LOG_PATH : p.join(DEFAULT_FILE_PATH, 'logs'), DUMP_FILE_PATH : p.join(DEFAULT_FILE_PATH, 'dump.pm2'), diff --git a/package.json b/package.json index 58e3cb22..d8157476 100644 --- a/package.json +++ b/package.json @@ -138,6 +138,7 @@ "pm2-deploy": "~0.0.4", "pm2-interface" : "~1.1.0", "pm2-multimeter": "~0.1.2", + "pm2-rpc-fallback" : "~2.1.0", "moment" : "~2.7.0", "uid-number": "~0.0.5" }, diff --git a/scripts/kill.js b/scripts/kill.js index b84be680..e8774c04 100644 --- a/scripts/kill.js +++ b/scripts/kill.js @@ -1,39 +1,79 @@ -#!/bin/sh +#!/bin/bash ':' // Hack to pass parameters to Node before running this file ':' //; [ -f ~/.pm2/custom_options.sh ] && . ~/.pm2/custom_options.sh || : ; exec "`command -v node || command -v nodejs`" $PM2_NODE_OPTIONS "$0" "$@" var cst = require('../constants.js'); var fs = require('fs'); +var pm2 = require('..'); var pm2_pid = null; var interactor_pid = null; +var version_file = null; -try { - pm2_pid = fs.readFileSync(cst.PM2_PID_FILE_PATH); -} catch(e) { - process.exit(1); -} - -try { - interactor_pid = fs.readFileSync(cst.INTERACTOR_PID_PATH); -} catch(e) { -} - -if (interactor_pid) { +function killEverything() { try { - process.kill(interactor_pid); - } - catch (err) { - } -} - -if (pm2_pid) { - try { - process.kill(pm2_pid); - process.exit(0); - } - catch (err) { + pm2_pid = fs.readFileSync(cst.PM2_PID_FILE_PATH); + } catch(e) { + console.log('PM2 pid file EEXIST'); process.exit(1); } + + try { + interactor_pid = fs.readFileSync(cst.INTERACTOR_PID_PATH); + } catch(e) { + } + + if (interactor_pid) { + console.log('Killing interactor'); + try { + process.kill(interactor_pid); + } + catch (err) { + } + } + + if (pm2_pid) { + try { + console.log('Killing PM2'); + process.kill(pm2_pid); + process.exit(0); + } + catch (err) { + process.exit(1); + } + } + } + + +var fallback = require('pm2-rpc-fallback').fallback; + +fallback(cst, function(err, data) { + if (err && err.online) { + // Right RPC communcation + console.log('Stopping PM2'); + pm2.connect(function() { + pm2.dump(function() { + console.log('Succesfully dumped'); + pm2.disconnect(function() { + killEverything(); + process.exit(0); + }); + }); + }); + return false; + } + else if (err && err.offline) { + console.log('PM2 already offline'); + return process.exit(0); + } + else if (err) { + return console.log('Unhandled error, please report https://github.com/Unitech/pm2/issues'); + } + if (data) { + console.log('Killing old PM2'); + return killEverything(); + } + return false; +}); diff --git a/scripts/preinstall.sh b/scripts/preinstall.sh index c228b8a2..16c0ab0d 100644 --- a/scripts/preinstall.sh +++ b/scripts/preinstall.sh @@ -4,7 +4,7 @@ # Check if user is logged as root and that pm2 command is available # -if ( [ "$EUID" -eq 0 ] || [ "$USER" == "root" ] ) && ! command -v pm2 2>&1; +if ( [ "$EUID" -eq 0 ] || [ "$USER" == "root" ] ) && ! ( env | grep "unsafe-perm" ); then echo "##### PM2 INSTALLATION" echo "#" @@ -22,17 +22,3 @@ if ( [ "$EUID" -eq 0 ] || [ "$USER" == "root" ] ) && ! command -v pm2 2>&1; echo "" exit 1 fi - -which pm2 - - -`command -v node || command -v nodejs` ./scripts/ping.js -if [ $? -eq 0 ] -then - echo "Saving process list..." - pm2 dump - echo "Done." - exit 0; -else - exit 0; -fi From f59be82550d573ea27a8113573207d980b6adb37 Mon Sep 17 00:00:00 2001 From: tknew2 Date: Tue, 19 Aug 2014 19:57:11 +0200 Subject: [PATCH 50/69] fix update --- lib/Satan.js | 17 ++++++++--------- package.json | 4 ++-- scripts/kill.js | 22 ++++++++++------------ scripts/postinstall.sh | 5 ++++- 4 files changed, 24 insertions(+), 24 deletions(-) diff --git a/lib/Satan.js b/lib/Satan.js index 457bfe59..dd47678a 100644 --- a/lib/Satan.js +++ b/lib/Satan.js @@ -319,7 +319,7 @@ Satan.disconnectRPC = function disconnectRPC(cb) { if (!Satan.client_sock || !Satan.client_sock.close) return cb({ msg : 'RPC connection to PM2 is not launched' - }); + }); Satan.client_sock.close(); return cb ? cb(null, {success:true}) : false; }); @@ -370,15 +370,14 @@ Satan.executeRemote = function executeRemote(method, env, fn) { * @return */ Satan.killDaemon = function killDaemon(fn) { - debug('Killing interactor'); - Satan.executeRemote('killMe', {}, function() { - Satan.disconnectRPC(function() { - setTimeout(function() { - return fn ? fn(null, {success:true}) : false; - }, 200); - return false; - }); + Satan.executeRemote('killMe', {}, function() { + Satan.disconnectRPC(function() { + setTimeout(function() { + return fn ? fn(null, {success:true}) : false; + }, 200); + return false; }); + }); }; /** diff --git a/package.json b/package.json index d8157476..ab688fd3 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "pm2", "preferGlobal": "true", - "version": "0.10.0-beta20", + "version": "0.10.0-beta21", "os": [ "!win32" ], @@ -138,7 +138,7 @@ "pm2-deploy": "~0.0.4", "pm2-interface" : "~1.1.0", "pm2-multimeter": "~0.1.2", - "pm2-rpc-fallback" : "~2.1.0", + "pm2-rpc-fallback" : "~2.7.0", "moment" : "~2.7.0", "uid-number": "~0.0.5" }, diff --git a/scripts/kill.js b/scripts/kill.js index e8774c04..e1cac481 100644 --- a/scripts/kill.js +++ b/scripts/kill.js @@ -7,11 +7,10 @@ var cst = require('../constants.js'); var fs = require('fs'); var pm2 = require('..'); -var pm2_pid = null; -var interactor_pid = null; -var version_file = null; - function killEverything() { + var pm2_pid = null; + var interactor_pid = null; + try { pm2_pid = fs.readFileSync(cst.PM2_PID_FILE_PATH); } catch(e) { @@ -25,8 +24,8 @@ function killEverything() { } if (interactor_pid) { - console.log('Killing interactor'); try { + console.log('Killing interactor'); process.kill(interactor_pid); } catch (err) { @@ -37,13 +36,14 @@ function killEverything() { try { console.log('Killing PM2'); process.kill(pm2_pid); - process.exit(0); } catch (err) { - process.exit(1); } } + setTimeout(function() { + process.exit(0); + }, 100); } @@ -54,11 +54,9 @@ fallback(cst, function(err, data) { // Right RPC communcation console.log('Stopping PM2'); pm2.connect(function() { - pm2.dump(function() { - console.log('Succesfully dumped'); + pm2.updatePM2(function() { pm2.disconnect(function() { - killEverything(); - process.exit(0); + return process.exit(1); }); }); }); @@ -69,7 +67,7 @@ fallback(cst, function(err, data) { return process.exit(0); } else if (err) { - return console.log('Unhandled error, please report https://github.com/Unitech/pm2/issues'); + return killEverything(); } if (data) { console.log('Killing old PM2'); diff --git a/scripts/postinstall.sh b/scripts/postinstall.sh index 000ae9f0..2002dc2c 100644 --- a/scripts/postinstall.sh +++ b/scripts/postinstall.sh @@ -4,7 +4,10 @@ if [ $? -eq 0 ] then bash ./scripts/kill.js - ./bin/pm2 resurrect + if [ $? -eq 0 ] + then + ./bin/pm2 resurrect + fi exit 0; else exit 0; From 7d7738ee58ffd69db6f82f6d3936c07d34e70f7a Mon Sep 17 00:00:00 2001 From: tknew2 Date: Tue, 19 Aug 2014 19:57:44 +0200 Subject: [PATCH 51/69] 0.10.0-beta22 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ab688fd3..b81d2ce3 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "pm2", "preferGlobal": "true", - "version": "0.10.0-beta21", + "version": "0.10.0-beta22", "os": [ "!win32" ], From 5e436d3f0f616d54e7aa749b0851a18ee2f824b6 Mon Sep 17 00:00:00 2001 From: tknew2 Date: Tue, 19 Aug 2014 20:05:46 +0200 Subject: [PATCH 52/69] update fallback --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index b81d2ce3..cb6438db 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "pm2", "preferGlobal": "true", - "version": "0.10.0-beta22", + "version": "0.10.0-beta23", "os": [ "!win32" ], @@ -138,7 +138,7 @@ "pm2-deploy": "~0.0.4", "pm2-interface" : "~1.1.0", "pm2-multimeter": "~0.1.2", - "pm2-rpc-fallback" : "~2.7.0", + "pm2-rpc-fallback" : "~2.8.0", "moment" : "~2.7.0", "uid-number": "~0.0.5" }, From b03ae885a199d45fe6dcd15292c71210a0251eb4 Mon Sep 17 00:00:00 2001 From: tknew2 Date: Thu, 21 Aug 2014 15:40:24 +0200 Subject: [PATCH 53/69] update CHANGELOG - update Logo --- CHANGELOG.md | 5 ++- README.md | 84 +++++++++++++++++++++++++----------------------- pres/pm2-v2.png | Bin 0 -> 9096 bytes 3 files changed, 47 insertions(+), 42 deletions(-) create mode 100644 pres/pm2-v2.png diff --git a/CHANGELOG.md b/CHANGELOG.md index 66da8087..e18cc38d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,10 @@ # 0.10.0 - PM2 Hellfire release -- PM2 hearth code has been refactored and now it handles extreme scenario without any leak or bug +- PM2 hearth code has been refactored and now it handles extreme scenario without any leak or bug +- PM2 reset reset restart numbers +- Auto update script at PM2 installtion +- --watch enhanced to avoid zombie processes - Restart app when reaching a limit of memory by using --max-memory-restart (and max_memory_restart via JSON)(https://github.com/Unitech/pm2#max-memory-restart) - PM2 respects strong unix standard process management - Remove timestamps by default with pm2 logs diff --git a/README.md b/README.md index 6dda4484..e7928a22 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ -![pm2](https://github.com/unitech/pm2/raw/master/pres/top-logo-wo.png) +![PM2](https://github.com/unitech/pm2/raw/master/pres/pm2-v2.png) -pm2 is a process manager for Node apps with a built-in load balancer. +PM2 is a process manager for Node apps with a built-in load balancer. ### Tech notes -pm2 is perfect when you need to spread your stateless Node.js code across all CPUs available on a server, to keep all processes alive forever and to 0s reload them. +PM2 is perfect for spreading your stateless Node.js code across all CPUs available on a server, for keeping all processes alive forever and 0s reload them. ### Main features @@ -17,34 +17,34 @@ pm2 is perfect when you need to spread your stateless Node.js code across all CP - Restart on file change with `--watch` - Monitoring in console -Tested with Node v0.11, v0.10 (https://travis-ci.org/Unitech/pm2). +Tested with Node v0.11, v0.10 (https://travis-ci.org/Unitech/PM2). **The recommended Node.js version is v0.11.13** Compatible with CoffeeScript. Works on Linux & MacOS. -[![NPM version](https://badge.fury.io/js/pm2.png)](http://badge.fury.io/js/pm2) +[![NPM version](https://badge.fury.io/js/PM2.png)](http://badge.fury.io/js/pm2) [![Donate](http://gravaco.in/b06327acf5ae1a2ce3f08254ed7f33d7.png)](http://goo.gl/sdaIwX) -[![NPM](https://nodei.co/npm/pm2.png?downloads=true)](https://nodei.co/npm/pm2.png?downloads=true) +[![NPM](https://nodei.co/npm/PM2.png?downloads=true)](https://nodei.co/npm/pm2.png?downloads=true) -Master: [![Build Status](https://api.travis-ci.org/Unitech/pm2.png?branch=master)](https://travis-ci.org/Unitech/pm2) +Master: [![Build Status](https://api.travis-ci.org/Unitech/PM2.png?branch=master)](https://travis-ci.org/Unitech/PM2) -Development: [![Build Status](https://api.travis-ci.org/Unitech/pm2.png?branch=development)](https://travis-ci.org/Unitech/pm2) +Development: [![Build Status](https://api.travis-ci.org/Unitech/PM2.png?branch=development)](https://travis-ci.org/Unitech/PM2) ## Monitoring dashboard ![Dashboard](http://leapfrogui.com/controlfrog/img/cf-layout-1.png) -We're going to release a very nice product, a dashboard to monitor every part of your Node.js applications. Here are some links: +We are developing a top-notch product: a dashboard to monitor each part of your Node.js applications. Here are some links: - [Pitch + Survey](https://docs.google.com/forms/d/1FuCjIhrGg-ItxInq2nLreoe9GS-gZWJNkNWE0JJajw8/viewform) People who fill the survey will be eligible for free license - [Newsletter](http://signup.pm2.io/) Subscribe to be kept informed -Thanks in advance and we hope that you like pm2! +Thanks in advance and we hope that you like PM2! ------ @@ -57,7 +57,7 @@ Thanks in advance and we hope that you like pm2! - [Examples](#a3) - [Different ways to launch a process](#a667) - [Options](#a987) -- [How to update pm2 ?](#update-pm2) +- [How to update PM2 ?](#update-pm2) ### Features @@ -69,7 +69,7 @@ Thanks in advance and we hope that you like pm2! - [Clustering](#a5) - [Watch & Restart](#a890) - [Reloading without downtime](#a690) -- [Make pm2 restart on server reboot](#a8) +- [Make PM2 restart on server reboot](#a8) - [JSON app declaration](#a10) ### Deployment - ecosystem.json @@ -90,14 +90,14 @@ Thanks in advance and we hope that you like pm2! - [Configuration file](#a989) - [Enabling Harmony ES6](#a66) - [CoffeeScript](#a19) -- [Testing pm2 on your prod environment](#a149) +- [Testing PM2 on your prod environment](#a149) - [JSON app via pipe](#a96) ### Knowledge - [Stateless apps ?](#stateless-apps) - [Transitional state of apps](#a4) -- [Setup pm2 on server: tutorial](#a89) +- [Setup PM2 on server: tutorial](#a89) - [Logs and PID files](#a34) - [Execute any script: What is fork mode ?](#a23) @@ -115,15 +115,15 @@ Thanks in advance and we hope that you like pm2! ## Installation -The preferred Node version to run pm2, is the **0.11.10** +The preferred Node version to run PM2 is **0.11.10** -The latest pm2 stable version is installable via NPM: +The latest PM2 stable version is installable via NPM: ```bash $ npm install pm2@latest -g ``` -If the above fails: +If the above fails use: ```bash $ npm install git://github.com/Unitech/pm2#master -g @@ -229,7 +229,7 @@ $ pm2 start -x echo.rb $ pm2 start -x echo.pl ``` -The not javascript languages will have to be run in [fork mode](#a23). +Languages other than javascript have to be run in [fork mode](#a23). ## Options @@ -262,7 +262,7 @@ Options: ``` -## How to update pm2 +## How to update PM2 Install the latest pm2 version : @@ -270,7 +270,7 @@ Install the latest pm2 version : $ npm install pm2@latest -g ``` -Then update the in-memory pm2 : +Then update the in-memory PM2 : ```bash $ pm2 updatePM2 @@ -281,7 +281,7 @@ $ pm2 updatePM2 ## Transitional state of apps (important) -pm2 is a process manager, as said, pm2 can start, stop, restart and *delete* processes. +PM2 is a process manager. PM2 can start, stop, restart and *delete* processes. Start a process: @@ -296,7 +296,8 @@ Now let's say I need to stop the web-interface: $ pm2 stop web-interface ``` -As you can see **the process hasn't disappeared**. It is still there but now in `stopped` status. +As you can see **the process hasn't disappeared**. It's still there but in `stopped` status. + To restart it just do: @@ -304,8 +305,8 @@ To restart it just do: $ pm2 restart web-interface ``` -Now I want to **delete** the app from the pm2 process list. -To do that: +Now I want to **delete** the app from the PM2 process list. +To do so: ```bash $ pm2 delete web-interface @@ -385,7 +386,7 @@ $ pm2 ilogs ### Reloading all logs (SIGUSR2/Logrotate) -To reload all logs, you can send `SIGUSR2` to the pm2 process. +To reload all logs, you can send `SIGUSR2` to the PM2 process. You can also reload all logs via the command line with: @@ -411,6 +412,7 @@ $ pm2 start app.js --name "API" -i max If your app is well-designed (**stateless**) you'll be able to **process many more queries**. + Important concepts to make a Node.js app stateless: - Sessions must not be stored in memory but shared via a database (Redis, Mongo, whatever) @@ -470,13 +472,13 @@ Then use the command: $ pm2 gracefulReload [all|name] ``` -When pm2 starts a new process to replace an old one, it will wait for the new process to begin listening to a connection before sending the shutdown message to the old one. If a script does not need to listen to a connection, it can manually tell pm2 that the process has started up by calling `process.send('online')`. +When PM2 starts a new process to replace an old one, it will wait for the new process to begin listening to a connection before sending the shutdown message to the old one. If a script does not need to listen to a connection, it can manually tell PM2 that the process has started up by calling `process.send('online')`. ## Startup script -pm2 has the amazing ability to **generate startup scripts and configure them**. -pm2 is also smart enough to **save all your process list** and to **bring back all your processes on restart**. +PM2 has the amazing ability to **generate startup scripts and configure them**. +PM2 is also smart enough to **save all your process list** and to **bring back all your processes on restart**. ```bash $ pm2 startup [ubuntu|centos|gentoo|systemd] @@ -488,7 +490,7 @@ Once you have started the apps and want to keep them on server reboot do: $ pm2 save ``` -**Warning** It's tricky to make this feature work generically, so once pm2 has setup your startup script, reboot your server to make sure that pm2 has launched your apps! +**Warning** It's tricky to make this feature work generically, so once PM2 has setup your startup script, reboot your server to make sure that PM2 has launched your apps! ### More information @@ -517,7 +519,7 @@ $ pm2 startup ubuntu -u www ### Related commands -Dump all processes status and environment managed by pm2: +Dump all processes status and environment managed by PM2: ```bash $ pm2 dump ``` @@ -531,7 +533,7 @@ $ pm2 resurrect ## Watch & Restart -pm2 can automatically restart your app when a file changes in the current directory or its subdirectories: +PM2 can automatically restart your app when a file changes in the current directory or its subdirectories: ```bash $ pm2 start app.js --watch @@ -914,7 +916,7 @@ pm2.connect(function(err) { # Special features -Launching pm2 without daemonizing itself: +Launching PM2 without daemonizing itself: ```bash $ pm2 start app.js --no-daemon @@ -998,7 +1000,7 @@ We recommend following the 12 factor convention : [http://12factor.net/](http:// ## Log and PID files -By default, logs (error and output), pid files, dumps, and pm2 logs are located in `~/.pm2/`: +By default, logs (error and output), pid files, dumps, and PM2 logs are located in `~/.pm2/`: ``` .pm2/ @@ -1013,7 +1015,7 @@ By default, logs (error and output), pid files, dumps, and pm2 logs are located ## Execute any script: What is fork mode? -The default mode of pm2 consists of wrapping the code of your node application into the Node Cluster module. It's called the **cluster mode**. +The default mode of PM2 consists of wrapping the code of your node application into the Node Cluster module. It's called the **cluster mode**. There is also a more classical way to execute your app, like node-forever does, called the **fork mode**. @@ -1067,9 +1069,9 @@ echo $my_json | pm2 start - ``` -## Is my production server ready for pm2? +## Is my production server ready for PM2? -Just try the tests before using pm2 on your production server +Just try the tests before using PM2 on your production server ```bash $ git clone https://github.com/Unitech/pm2.git @@ -1093,7 +1095,7 @@ $ nvm alias default v0.11.10 ## Contributing/Development mode -To hack pm2, it's pretty simple: +To hack PM2, it's very simple: ```bash $ pm2 kill # kill the current pm2 @@ -1102,9 +1104,9 @@ $ cd pm2/ $ DEBUG=* PM2_DEBUG=true ./bin/pm2 --no-daemon ``` -Each time you edit the code, be sure to kill and restart pm2 to make changes taking effect. +Each time you edit the code, be sure to kill and restart PM2 to make changes taking effect. -## Install pm2 development +## Install PM2 development ```bash $ npm install git://github.com/Unitech/pm2#development -g @@ -1113,7 +1115,7 @@ $ npm install git://github.com/Unitech/pm2#development -g ## Known bugs and workarounds -First, install the lastest pm2 version: +First, install the lastest PM2 version: ```bash $ npm install -g pm2@latest @@ -1121,7 +1123,7 @@ $ npm install -g pm2@latest ### Node 0.10.x doesn't free the script port when stopped. It's due to the Node.js cluster module. So if you feel that this problem is important for your use case, use the [fork mode](#execute-any-script-what-is-fork-mode-) instead. -By using the fork mode you will lose core features of pm2 like the automatic clusterization of your code over all CPUs available and the 0s reload. +By using the fork mode you will lose core features of PM2 like the automatic clusterization of your code over all CPUs available and the 0s reload. ``` $ pm2 start index.js -x # start my app in fork mode diff --git a/pres/pm2-v2.png b/pres/pm2-v2.png new file mode 100644 index 0000000000000000000000000000000000000000..54f45fe044ed712c04a4fbb6599b07ddfca76406 GIT binary patch literal 9096 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 Date: Thu, 21 Aug 2014 15:56:58 +0200 Subject: [PATCH 54/69] fix badges --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e7928a22..0c3a3581 100644 --- a/README.md +++ b/README.md @@ -24,10 +24,10 @@ Tested with Node v0.11, v0.10 (https://travis-ci.org/Unitech/PM2). Compatible with CoffeeScript. Works on Linux & MacOS. -[![NPM version](https://badge.fury.io/js/PM2.png)](http://badge.fury.io/js/pm2) +[![NPM version](https://badge.fury.io/js/pm2.png)](http://badge.fury.io/js/pm2) [![Donate](http://gravaco.in/b06327acf5ae1a2ce3f08254ed7f33d7.png)](http://goo.gl/sdaIwX) -[![NPM](https://nodei.co/npm/PM2.png?downloads=true)](https://nodei.co/npm/pm2.png?downloads=true) +[![NPM](https://nodei.co/npm/pm2.png?downloads=true)](https://nodei.co/npm/pm2.png?downloads=true) From 73c6489041554fe6a349c11fd6b2a82d1f6389a3 Mon Sep 17 00:00:00 2001 From: tknew2 Date: Thu, 21 Aug 2014 16:40:25 +0200 Subject: [PATCH 55/69] fix options minUptime maxRestarts --- examples/keymetrics-load.js | 6 ++++- examples/kill-not-so-fast.js | 5 +++- lib/CLI.js | 4 +++ lib/God.js | 47 ++++++++++++++++++++++-------------- lib/God/ClusterMode.js | 10 +++----- lib/God/ForkMode.js | 3 ++- 6 files changed, 47 insertions(+), 28 deletions(-) diff --git a/examples/keymetrics-load.js b/examples/keymetrics-load.js index 719bd96c..83bae41a 100644 --- a/examples/keymetrics-load.js +++ b/examples/keymetrics-load.js @@ -6,7 +6,11 @@ pm2.connect(function() { pm2.delete('all', function() { pm2.start('examples/human_event.js', function() { pm2.start('examples/child.js', {instances:2},function() { - pm2.start('examples/kill-not-so-fast.js', {instances:10}, function() { + pm2.start('examples/kill-not-so-fast.js', { + instances:10, + minUptime: 0, + maxRestarts : 0 + }, function() { pm2.start('examples/auto-save.js', {execMode : 'fork', watch:true, force : true}, function() { pm2.start('examples/custom_action_with_params.js', function() { //pm2.start('examples/auto-bench.js', {instances : 'max'}, function() { diff --git a/examples/kill-not-so-fast.js b/examples/kill-not-so-fast.js index 036c8e71..d0385b46 100644 --- a/examples/kill-not-so-fast.js +++ b/examples/kill-not-so-fast.js @@ -1,4 +1,7 @@ +console.log('start'); + setTimeout(function() { + console.log('exit'); process.exit(1); -}, 1050); +}, 300); diff --git a/lib/CLI.js b/lib/CLI.js index fb7b6c50..1ee5cb6a 100644 --- a/lib/CLI.js +++ b/lib/CLI.js @@ -79,6 +79,10 @@ CLI.start = function(script, opts, cb) { appConf['run_as_group'] = opts.runAsGroup; if (opts.logDateFormat) appConf['log_date_format'] = opts.logDateFormat; + if (typeof(opts.minUptime) !== 'undefined') + appConf['min_uptime'] = opts.minUptime; + if (typeof(opts.maxRestarts) !== 'undefined') + appConf['max_restarts'] = opts.maxRestarts; if (opts.executeCommand) { appConf['exec_mode'] = 'fork_mode'; diff --git a/lib/God.js b/lib/God.js index 1fb9aa96..a52508f2 100644 --- a/lib/God.js +++ b/lib/God.js @@ -52,16 +52,18 @@ require('./God/ActionMethods')(God); * @param {} exit_code * @return */ -function handleExit(clu, exit_code) { +God.handleExit = function handleExit(clu, exit_code) { console.log('Script %s %s exit', clu.pm2_env.pm_exec_path, clu.pm2_env.pm_id); - var proc = God.clusters_db[clu.pm2_env.pm_id]; + var proc = this.clusters_db[clu.pm2_env.pm_id]; - if (!proc) return false; + if (!proc) { + console.error('Process undefined ? with process id ', clu.pm2_env.pm_id); + return false; + } var stopping = (proc.pm2_env.status == cst.STOPPING_STATUS || proc.pm2_env.status == cst.ERRORED_STATUS) ? true : false; var overlimit = false; - var pidFile = proc.pm2_env.pm_pid_path; if (stopping) proc.process.pid = 0; @@ -72,8 +74,10 @@ function handleExit(clu, exit_code) { proc.pm2_env.status = cst.STOPPED_STATUS; try { - fs.unlinkSync(pidFile); - } catch(e) { console.error('Error when unlinking PID file', e); } + fs.unlinkSync(proc.pm2_env.pm_pid_path); + } catch(e) { + console.error('Error when unlinking PID file', e); + } /** * Avoid infinite reloop if an error is present @@ -81,8 +85,8 @@ function handleExit(clu, exit_code) { // If the process has been created less than 15seconds ago // And if the process has an uptime less than a second - var min_uptime = (proc.pm2_env.min_uptime || 1000); - var max_restarts = (proc.pm2_env.max_restarts || 15); + var min_uptime = typeof(proc.pm2_env.min_uptime) !== 'undefined' ? proc.pm2_env.min_uptime : 1000; + var max_restarts = typeof(proc.pm2_env.max_restarts) !== 'undefined' ? proc.pm2_env.max_restarts : 15; if ((Date.now() - proc.pm2_env.created_at) < (min_uptime * max_restarts)) { if ((Date.now() - proc.pm2_env.pm_uptime) < min_uptime) { @@ -100,20 +104,23 @@ function handleExit(clu, exit_code) { proc.pm2_env.pm_exec_path, proc.pm2_env.unstable_restarts, proc.pm2_env.status); - God.bus.emit('process:exit:overlimit', { process : Common.serialize(proc) }); + this.bus.emit('process:exit:overlimit', { process : Common.serialize(proc) }); proc.pm2_env.unstable_restarts = 0; proc.pm2_env.created_at = null; overlimit = true; } } - God.bus.emit('process:exit', { process : Common.serialize(proc) }); + this.bus.emit('process:exit', { process : Common.serialize(proc) }); if (!stopping) proc.pm2_env.restart_time = proc.pm2_env.restart_time + 1; - if (!stopping && !overlimit) God.executeApp(proc.pm2_env); -} + if (!stopping && !overlimit) + this.executeApp(proc.pm2_env); + + return false; +}; /** @@ -125,7 +132,7 @@ function handleExit(clu, exit_code) { * @return Literal */ God.executeApp = function executeApp(env, cb) { - var env_copy = JSON.parse(JSON.stringify(env)); + var env_copy = Common.serialize(env); util._extend(env_copy, env.env); @@ -153,7 +160,7 @@ God.executeApp = function executeApp(env, cb) { } if (env_copy['watch']) { - env_copy['watcher'] = require('./Watcher').watch(env_copy); + env_copy['watcher'] = require('./Watcher').watch(env_copy); } } @@ -170,7 +177,7 @@ God.executeApp = function executeApp(env, cb) { God.forkMode(env_copy, function forkMode(err, clu) { if (cb && err) return cb(err); if (err) return false; - + var old_env = God.clusters_db[clu.pm2_env.pm_id]; if (old_env) old_env = null; @@ -178,11 +185,14 @@ God.executeApp = function executeApp(env, cb) { clu.once('error', function cluError(err) { proc.pm2_env.status = cst.ERRORED_STATUS; + return false; }); clu.once('close', function cluClose(code) { proc.removeAllListeners(); - return handleExit(proc, code); + clu._reloadLogs = null; + God.handleExit(proc, code); + return false; }); God.bus.emit('process:online', {process : Common.serialize(proc)}); @@ -207,6 +217,7 @@ God.executeApp = function executeApp(env, cb) { if (God.clusters_db[clu.pm2_env.pm_id].process._handle) { God.clusters_db[clu.pm2_env.pm_id].process._handle.owner = null; God.clusters_db[clu.pm2_env.pm_id].process._handle = null; + God.clusters_db[clu.pm2_env.pm_id].process = null; } God.clusters_db[clu.pm2_env.pm_id] = null; } @@ -226,7 +237,7 @@ God.executeApp = function executeApp(env, cb) { clu.once('exit', function cluExit(exited_clu, code) { proc.removeAllListeners(); - return handleExit(Common.serialize(proc), code); + return God.handleExit(Common.serialize(proc), code); }); return false; @@ -259,7 +270,7 @@ God.prepare = function prepare(env, cb) { return false; } - return God.executeApp(Common.serialize(env), function(err, clu) { // deep copy + return God.executeApp(Common.serialize(env), function(err, clu) { if (err) return ex(i - 1); arr.push(clu); return ex(i - 1); diff --git a/lib/God/ClusterMode.js b/lib/God/ClusterMode.js index 6419ba22..5044eaf2 100644 --- a/lib/God/ClusterMode.js +++ b/lib/God/ClusterMode.js @@ -31,9 +31,9 @@ module.exports = function ClusterMode(God) { God.nodeApp = function nodeApp(env_copy, cb){ var clu = null; - if (fs.existsSync(env_copy.pm_exec_path) == false) { - return cb(God.logAndGenerateError('Script ' + env_copy.pm_exec_path + ' missing'), {}); - } + // if (fs.existsSync(env_copy.pm_exec_path) == false) { + // return cb(God.logAndGenerateError('Script ' + env_copy.pm_exec_path + ' missing'), {}); + // } console.log('Entering in node wrap logic (cluster_mode) for script %s', env_copy.pm_exec_path); @@ -72,10 +72,6 @@ module.exports = function ClusterMode(God) { } }); - - //delete clu.process._handle.owner; - return cb(null, clu); - return false; }; }; diff --git a/lib/God/ForkMode.js b/lib/God/ForkMode.js index 817b802d..5c7ec30c 100644 --- a/lib/God/ForkMode.js +++ b/lib/God/ForkMode.js @@ -31,7 +31,7 @@ module.exports = function ForkMode(God) { var command = ''; var args = []; - log('Entering in fork mode'); + console.log('Entering in fork mode'); var spawn = require('child_process').spawn; var interpreter = pm2_env.exec_interpreter || 'node'; @@ -187,6 +187,7 @@ module.exports = function ForkMode(God) { if (cb) return cb(null, cspr); return false; }); + return false; }); }; From 6a752970ec6bb302076c11c5a14d984e8a010f3b Mon Sep 17 00:00:00 2001 From: tknew2 Date: Thu, 21 Aug 2014 17:23:42 +0200 Subject: [PATCH 56/69] fix deep code --- constants.js | 10 +++++----- lib/God.js | 16 ++++++++++------ lib/ProcessContainer.js | 2 +- pres/pm2-large.png | Bin 5641 -> 0 bytes pres/top-logo.png | Bin 12494 -> 0 bytes 5 files changed, 16 insertions(+), 12 deletions(-) delete mode 100644 pres/pm2-large.png delete mode 100644 pres/top-logo.png diff --git a/constants.js b/constants.js index d0cb9530..64600c69 100644 --- a/constants.js +++ b/constants.js @@ -15,13 +15,13 @@ var DEFAULT_FILE_PATH = p.resolve(HOME, '.pm2'); var default_conf = { DEFAULT_FILE_PATH : DEFAULT_FILE_PATH, - PM2_LOG_FILE_PATH : p.join(process.env.PM2_LOG_DIR || DEFAULT_FILE_PATH, 'pm2.log'), - PM2_PID_FILE_PATH : p.join(process.env.PM2_PID_DIR || DEFAULT_FILE_PATH, 'pm2.pid'), + PM2_LOG_FILE_PATH : p.join(DEFAULT_FILE_PATH, 'pm2.log'), + PM2_PID_FILE_PATH : p.join(DEFAULT_FILE_PATH, 'pm2.pid'), DEFAULT_PID_PATH : p.join(DEFAULT_FILE_PATH, 'pids'), DEFAULT_LOG_PATH : p.join(DEFAULT_FILE_PATH, 'logs'), DUMP_FILE_PATH : p.join(DEFAULT_FILE_PATH, 'dump.pm2'), - SAMPLE_CONF_FILE : '../lib/custom_options.sh', + SAMPLE_CONF_FILE : p.join('..', 'lib', 'custom_options.sh'), PM2_CONF_FILE : p.join(DEFAULT_FILE_PATH, 'custom_options.sh'), DAEMON_BIND_HOST : process.env.PM2_BIND_ADDR || 'localhost', @@ -62,8 +62,8 @@ var default_conf = { INTERACTION_CONF : p.join(DEFAULT_FILE_PATH, 'agent.json'), SEND_INTERVAL : 1000, - INTERACTOR_LOG_FILE_PATH : p.join(p.resolve(process.env.HOME, '.pm2'), 'agent.log'), - INTERACTOR_PID_PATH : p.join(p.resolve(process.env.HOME, '.pm2'), 'agent.pid'), + INTERACTOR_LOG_FILE_PATH : p.join(DEFAULT_FILE_PATH, 'agent.log'), + INTERACTOR_PID_PATH : p.join(DEFAULT_FILE_PATH, 'agent.pid'), INTERACTOR_RPC_PORT : parseInt(process.env.PM2_INTERACTOR_PORT) || 6668 }; diff --git a/lib/God.js b/lib/God.js index a52508f2..5220118c 100644 --- a/lib/God.js +++ b/lib/God.js @@ -75,7 +75,7 @@ God.handleExit = function handleExit(clu, exit_code) { try { fs.unlinkSync(proc.pm2_env.pm_pid_path); - } catch(e) { + } catch (e) { console.error('Error when unlinking PID file', e); } @@ -134,7 +134,7 @@ God.handleExit = function handleExit(clu, exit_code) { God.executeApp = function executeApp(env, cb) { var env_copy = Common.serialize(env); - util._extend(env_copy, env.env); + util._extend(env_copy, env_copy.env); env_copy['axm_actions'] = []; @@ -198,7 +198,6 @@ God.executeApp = function executeApp(env, cb) { God.bus.emit('process:online', {process : Common.serialize(proc)}); if (cb) cb(null, clu); - return false; }); } @@ -214,8 +213,10 @@ God.executeApp = function executeApp(env, cb) { if (old_env) { old_env = null; - if (God.clusters_db[clu.pm2_env.pm_id].process._handle) { - God.clusters_db[clu.pm2_env.pm_id].process._handle.owner = null; + + if (typeof(God.clusters_db[clu.pm2_env.pm_id].process._handle) !== 'undefined') { + if (God.clusters_db[clu.pm2_env.pm_id].process._handle) + God.clusters_db[clu.pm2_env.pm_id].process._handle.owner = null; God.clusters_db[clu.pm2_env.pm_id].process._handle = null; God.clusters_db[clu.pm2_env.pm_id].process = null; } @@ -237,7 +238,9 @@ God.executeApp = function executeApp(env, cb) { clu.once('exit', function cluExit(exited_clu, code) { proc.removeAllListeners(); - return God.handleExit(Common.serialize(proc), code); + proc.process.removeAllListeners(); + God.handleExit(Common.serialize(proc), code); + return false; }); return false; @@ -303,6 +306,7 @@ God.prepareJson = function prepareJson(app, cwd, cb) { } app = Common.resolveAppPaths(app, cwd); + if (app instanceof Error) return cb(app); diff --git a/lib/ProcessContainer.js b/lib/ProcessContainer.js index dc708b0d..1f764002 100644 --- a/lib/ProcessContainer.js +++ b/lib/ProcessContainer.js @@ -78,7 +78,7 @@ function cronize(cron_pattern) { function exec(script, outFile, errFile) { var stderr, stdout; - if(p.extname(script) == '.coffee') { + if (p.extname(script) == '.coffee') { require('coffee-script/register'); } diff --git a/pres/pm2-large.png b/pres/pm2-large.png deleted file mode 100644 index 418a71c0e13d84f9c5fe4bcb57ef12658841b11a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5641 zcmb7I2UL^UwhkbupfZjqUO-T3f>IO^K>`BOn{??)Cm4bdY6!uKQUns|%|Ut{K!}tC z%K#HVKthpFq7p!AAV5MW$@}BXyZ7C<)?MqpmGz&jv)6z2f6lk}{?6X}`7KMMqx>TL zAQ0%NiSbPv5QqnOK$VC1fU!(4b`|*J3birP1J(A6EdUcfPcx&NAU5as3Ri{!B!`2I zokD@-W1NRe??P@QAUp^+u`oC|#U~=5aD?F`t^@*|d~R}6*DjK@GKF}V?}JoYqZXK> z3FcQYaVB^8jJYoe{UXnI$@8}aSE(B(x5Rv+8BP8@Ey#3nc0L%dclBSjg!e7P79%f zpl+?g3g-90B_P$Tkt#>56w??TirzCtKQYPKJYt^pK&r!>W{gIe(6VQHGht&2Hzeg2 zxL>{38%~g7XBAt(-o30>A=vk3DzS5FK?mgGDo(!2LC0Jq_(b!RR=1&Dz2W@+2TEH( z$$HNoR#2CvA{O~>q;h9*(=Fyt6ur{UYmVMp6kOnYHJwd5IE;7(F}ZKshA4U!yKK<_ zXVPXx!+3AiB^h&VtxD<%4d>5E6^+6rVh6V2I>@eow&%t<=m^#o~~q36;;)CeX|mq*4EaB z$o22~(bbYrY(<5fo12@{8~@Mc6JjDFJ#(LOi9{mi{rmTa1BIV@0U+SxL1Demgp_OU zO}A@^UcGnv{^{}G@9LYD43&TE^e3GW6YJaATEch_udE8fPE09+R5d$g;sU(p`|~A_ zojgc-d*2HX&*+D{Us_F;)QdQ0NBF!s@CxeX}`)@8Ch=)Rde0O08R4xDAOH zq|5(Gcau)fP*_v8K_Za@!bO9MphGsGqcyHgtV}o=}Q-3W60ro7dtIwLd zniFb)4d();puc3JqoXDof4>{YH#9oxzclbT48l>*D!VRE2c!M-C z(Jkfrrm3j}z*muUCK04M^i!Sx*%BK$vrI*c3cH-aQ1idO;jgTqps=vGD6+K~xx-teJNc5TswJkEY~@sGX(>D` z%;$5FLHuw~g`KIDl@$PkY_%Kcmtv5JvUwOBUfkB!Hs{$0g+iTL;R{(weddO3B{mDp z@Udzq4B$nF5qQwxpXM7*&LFW`Hcnwnkhbuv|M<=+3pz|eHl5;z=g9vPVx1h|l-&Aq>%1abBR;vfzJ zO!E`nzlVRy<63VG@!kM0Eia1=3EV1-j*gBI^heDJsZBdXaiqyQh1T%|WD@3~u9JT?1CGM;N z52OH-$9O_1A4^$@!q%7twOol>J+8|SoadL~W8;B3_LC@;Q&0KOQxk~~54IsZwnN88 zy}ReUS9-n=e2(1c@ec3)@nWCJwus&HdyR5e@s)&ay)Sg+GQrkVaEy>h^ za9KEM=1GL*@uH_H^4r5;!VKB8wIg>qX0CI>xB27~xeT|Qycm0%9BZbQfTg)5YlR|< znnfN`_8n7m1HHQm0iSQ#E<|H$#pch3nHYx2t`!jzt@GBeG}eSa6Pe7N++U}Z_F{}` zuV1?<-MMd-OSM{MN)VDWM-=7nlc-6tl~RBWLAKw%6Z7)62$U@K7apQeqf@UvB-N&g zJCJc==Hy;~(Vfm<@cxaEeS^+@|MWhHl3@wxv}h@v%UmVbL013KL3Zr>!|7wn(eyGx zM)xrmvf^c5o}-Xs)>K;G;ssXq)lbV$9$`XjO=>%o$MLMMpQhM(eR;dz$!GR=ig4dF z=?&gQ+>B^(w1ibb)VB2gR^#UmZ8n|x-Mu_bWD+yU+$GFnrB$4JsEy0tDXf8=BOK06 z`ts4kSX^;qmsoX)Z}*EjCcgGetmsCH)rx20c z+L3H5>uk2p!?R7BXv!+vUHe)Faqp8IDSwhNPU%ar zN5#5~ZHs}#LssZOeqJDaaFn)Uzcm~@ZIJ5RdY<{MJE1A7G5~zuDUx*I!@wa4VgZZN z&8{+4&dok@M9dkbvk`m+X2OI*jiPmaZq3{_wEoBPca;Q`N3>T1ZED2mP-$X(t%YSv zYsk6Bne4W^2P*g-Yk(MR6qHRI(2AVM%ak@^IS++8ZiOnfDEsu52rgS*B?AQH@MJ9J zu`UFLy1a5<3rfBnHL?7$U6`wjl2a!D>gpdy?R-a1Z=@)F7%n1ieX5a2NyN;JE_OS%N(7(= zoi_4m6E4ThUTC$^qEfW$Vg^ONq*uE{1qaZ-J#HfmP40Xc9tvp&O#M*kon8Gp{cen| zg0vsYI)v_Lw^`*qIw>Mt!>KVYVyvjcJ+*~fnyHvf>MdxkTQugYkROT%@Vk_-T6y*{$H+9@36CDn{(%&^qsI%~l>Zf!DSdMq9{}Q{Z<77iyJpF#29`W1r`n{5mt==Jdni*D+z( z_G{(oNORi@#`$m`>#xP=!q|$PXm?IC6}vjk&!pDn>=yIqGPIloKL!Yo^M+`+D#n z{=rt(XN&Trg-;4hi%0f*DPHKHJ}4nooyyJPmb4MDG@jlr5!S(GMt?=Qh=~BsRL&C! zvkYq0S|HmYJLX2eo2?NX)q*_QcRHkHU9F>b3-ExKLJm~OKk!+TmNZPSWPE~Hz0hPl zK`Cj1vm4jG?$cK<7#6z=jq(|?-pYK~o53XF9kj5f%`oFwx=*1AkO921+@r}7u#Qp# zTCS2(s?r_wZh})`Ue405$)gWXB+PGBbvPnBWP>hzup1F@NSA{v;o<{kRtNM8*&zrt{iab%9pj zt}h@7ldmyU)Yq>{G+S%i&?})q)(?V5xloER_6^HJ-mKohLN6GKhUuupd5j#d$V@tQ zbXk$#K#Y;`h~XBZYh3}m6ESP)Y^u;B1and{4SV3E|hXbq0xueot^yDfgC&}}K+q@7_H+SBSOfB;e zcQ@wcM1R9mJ`6(J`qH!L(L%ez%JA)ri3zb6$r!EOMgnSCAzoO=3%XxPNn3h~g*BxW zhMewy zS$9(Q^Z_bNH-l3-SAGbxe$GzPIl_##&k#hhA(A??r~1x(2@8Jz>|42AdC|}_JK_OW zCS@IF*1sFG@=H@d7;RtBS%s4j`74W3jf2`RjW^fs#H!58%%h|=*4>h~_9WiQw9NMO z@Sf}8tANZ795g(YAU{rY)QCBH%sePna{-GEWw+D8PA;)&lQfskhaY~H9e%w77%Cn| zceb_oL>MnL$*3*gkkzuQrov}R2*^S?7Pv^jq3L)9|7!zLhmln>PfbTt2&m#VD~7wa zxNP73@)^~V#a-~mtq`IM#%+KZ!hp4(xbDR3-DlBDsXJ|BOCwoH%i`PUcu@ywtUFXwf>SKL zd!bcz!3|u6+lg(%%3$}yheF3FI&(%H1zPYGiuTjAQ@LMY`%r^-<@S}xDFuTE329H8 z)^Dd`D>O9b2hcN$bvSB{C}sjxM%y$5b;To!K{A7YAr*I`0YgHe_mw`|9kRAH81Xz| zuXnQ~?6)3XG(?^^yEmR+M8V#7 zNsd3x_)eoKAC}NBW*{-TWS1BXRwtY2H)1EstWZ0jF7z51@7Z2TSHN9kCU=y(9r3#3 z*RZH_%HZHSx$+s{QJnwpOoboAr@gN$wGh(c3eTj>dQAS0ddeQP_f~1YU7d&M+Y20AYy}5{v z2Qofkgm|1kSk^MZ-;IHc7X<#hA%@Z1GJfrWz?ED*k>E61NFt{F-Uutk>fNyBU32u zOQaD~tsn#C9eECd`q3KslGoVq*HsR#G8WVs!b(RjP@&O0Jj-l+@GVBN@%gF^Ue3b8 zIv+o)8aih1%E~kfAK6$xW4)Jj^|q>7O1ssu%^@$un4Q1h+}gnS{px{}@9gH-)u66F zNx6wnY&;;2u#k=GFVznMCWvc@rHv^aGj|yS673uEoAcHHIgc?qeF7jmak zaa%p!^Nl+PIHh`}$fWJdtK>Gxe|)LY;WR|<+z#Keoa%0#ohR7NTpJ0V*s0eTC`DkL zHoE3qUB>Lm+P@?2?RuL%=?q0kNRaUo#{_n{lakSjphYJ?zxh=6xSe4laRzbd7FH-1 z=mh+y>iPdG|Jhdf_uj(4RQxA7rwsvM;_r$Gb6ORzYFBp8)31WL!e*g* zLVzQg$7VTaQ=$REw;g1m6Gt_FWFT4;&SM^+zPww_Jq!j|{AfvI<3^Db`=xj7bex f*M41krmZjNXY}k`d8GixP%}2~nd&4G9sQ=zYW>iB1rrWt13w zqW3%GyT9k&zwSLA4&#)y*IMtp-o5sIWn`d5O2j|}002nA+8QPR0Cped_pbzan4g=M zyjz$P)>9KLH9*xc6AJSOp0lo&2H^VUtDvLg4dxRPw zaL-xFT2K>FKc*^}xI)j7O9E0UGuU`&Wf(h$_fbl`3nb4Fgn4;LefgE z5!nb(jyk!s-oGN&B)lHNAmFJsMjpbk=LkD5k|yusMaIIsDKB1kFW7_|Wy#XzWIb@j z?GQ%1!}7*qQCtlIv0)i^>V?rDT~!bT&k^gQi z`I@lGrn0OT}~TLF~iQGDv&JfKT{{t%EdJyhfrj;qB2ZeTuz#0F@6}%b;a= za#ldTuq8BWMvb_)w+`B*eiz&$qPJ-DUV;U`hUJ9dbR0$eF~7)?vOU_7pTf;^aI~Pv zPtl7_=3N-tpY<_>Ie4~>XmIdOav@S3p;}Zb^|yv*!^>Ga8`fMNMrXT!kqO?1?H%zUi@LrQxQzgVP$_+o&Cfb3-Jx^tF$-B z^m=HpVjG*aA4~U5;m?iWJ`L;FR?124Pt?;dtH~?fa2`w6jN|9u#y8c~^Pa>(<7pZxa2x(&G3ox4HFyoZOisM!X?6*Q}{{R0)bdy(e{Z$?fU! zptdK7xFG*af86a0vSh7tZW1AjzB#^j277W@;GVeq)6?;yw;2ZJxdT5&x#+(kf)4eA z^nMMmWr(kWHJ3_p&y%d-8Zl542utL)Y9{mB9@d=Ry%4TcGw30;qvHgb3UsQjrw(RcmxGO9 zMG*Na)R6ojeh58gs{$_Qch*0LPEF`6^-?ISp9LYrBO54mRozvRuoj5tqJnwVizk(u z2X8`9;qt}aS9VWnR1;oXP%j8)cNo$-qkqn__bevv*um&f!ps&IO-yr}t33>Q{v5$T^v(K$IbnnzgMa%Wu{IYRw0#J3J$oyD zlN5E0Md!f&l9|ekweJVUb-^awGc}zg%CU8=V8a(imEJLtqb5=K_1jX}H({K?Zo+dm zo?l$jWn6ovn1{3ReyWqKQ2X5jg}5-c2NOCdN6*`&ljJQGrqE#aYz5QVNOhI-XG~6R zRicdraK;n|Hg97vxW=<5$&u`K)nukq*23HSWA)@egQQ5qelRBlG&gDKj~q4FeeytT z9@up2tbdCTdl!7-h5wN2AY9M;;#9Cnm1ixhANITtYlY@cGH` zV1-{b)`BV`e7x45+$-Pwne!SKL7&M|aRIqxKt@9;_gg!`j~KHS@N+d03_MArwpDpt z{Fx;+WRCRf#oF)Ozn-~u{}i3R(pgV*B0h?CTapc15{&IcgYysVqgIE= zA3n8v5JC*`e2FeVUVa+%?}K+w;rCriCbEB|mS)Eh$0I6zpHxT=sI2Cmas$9vnuUs&8<@OPc6 zta7VcjN_{4yEdCcVl zxxy{uxhpHeL8xW_04d;;Q-$5O)=yQNacN|zkG)D;|K6>rdAZnuPwe<9s-QEDvMdM$ zfi&#nuwoMN{+#sm;98;$hOpOk9T{1{Rd1TOXCDU172I;>jKc5^S?E7FL;jsejdq_t zO;<3Tg4yFd>6XjBB5@i-pE1^UT>=Up@c!)yf3O%;H=5n%ytus6Ty9;}k( zS0}CGYFd@MLlo41>%kme)n@zy513_?0dH0?Hg&SdJV_WgClcn9%XB8 z{-)If(-L_L_bFBh>5CAZ$90J-N_`NiE)p76)pdLv2ln85|M^CwyJmHcnx_^>I zn1AbF>H&r$a=Z&qva~bV&QkevC0kseJPkWHbqlW!vAuTL-PhhM~s__MwLq zDUIYK2jPLU@7UN51eOj-Xfm5AhyGznk@`5wK3*ovPs`eT{DWmm{oQ@Yl5v&xKPoaf zrdS$rr<0d*lO+?v9d>-&c-5Cr+4_=kEG(aU^yUd33*Ry`Kp}TE3};K4XEi=ZyuPH5 zOJ}yD{m;RTk(x}ZMjlu$4sryk9gS5x>N*{pXDcv;4MS zG`MmoKxWH-2opD_0OQ@^y|mHWATaAce09<$ajYRrG_r@}VspO;7{RgE=&VQ3xZMtU zswF0_oPAiuI)AY9PTdP+G*Tk29t&;D0eLLB6N%qwZLT0~7C3b!cI$K>_GP64SACqH zWorZa)``DminQK{1Ts@PGKgMQmAt5Orpc!a@!I@)lQb1?-k|TN{{HzSfpawtO@|#+ zf)U!7$Ze{q_iDny__%Y{o+|!rCPTM*ESKCGNkeLFMZR3uzZA|5e@T)))q?yF$4Oysq~o!X z)5X7=<}?&eLx1}Vx1raTGuedMc6~6S7o%jPSTUdmRC6JVQHIV_iK%Lc;Oekh*n@&<$NG22^7*wK_e(yG``K>e&6C3gLScnNWix##2!t}xtGq! z*Pm{BAN>|y!75$e9#B3zAZ7S~21h`uP71Rrs zSK$6N^SRER4W78X?Sp^U>`PE*ioxsH>{UnKUF;=04Fs|Y^Ek97!^-Km4nN#nB<{oN zQa4P}y9qr8(ng(4fVi=X6?xTH0qt?HX@CQ^A4z)HFh-qYB>E7QdaoqTE<2J2^|pJp zl#?glI-8E2Lq!$AQ{YJ01*xc>+xJ41^n&- zQ#H#bVg+vh9FGq-wmjI5iL--$fSwoAZa;Znppt_3HIu5=FMD);<`6@@p6txG0BD3) zayhf1q$;edT31&>DaU>HMP6XackCwFoWm_bBp<}pi*`6qhs+qbn9eEF17|R~ib1wq z0+*w2o_>^07jj7N!D|k;fXb!@-^S{soXONIg%G-c#=TxAR}PCj8)NW?zzGa5wB>*A zccwc21aa__aC877?*1x}F*+VL;vK2Goi1$)b3}%2wmWKtba%RZ*Od1MpsHHW>3Ns= zlb8=CfbgT%=ts`eg#$2P0W)dYa$f9R<;)rUxpbVyPaRRIF;{J_Ezf>V6xg{0l1CP#o25vHvL4TIi*N)-I zlMctsWmI*^U=;5Wg|E7u6C3}ukj9oQpVdz6PlZR94fvnk367W#+KTKqh{aa@=I`}v zMcQl%gpZ3dMZFsoJ71cj{{mUv)IRDSaeX#K3a^s07N1z=4NMuBlNj2qHtg|D7$6^K zdp3Vpq$MWiH5$~Ex|zpcZ~v=c`7r3-W=FX8Er0lSCmuv}0lXB~rR7sP&7bNz zYqW5m*=Wm`JnI#L>#vH`y7N72A^v(yzm><(t}ah#XMB|;r#6_B0wEAZ+i*P3w+Kyu zimBKt4Y)tYo3pZn8yANd($^$x{dQJw= z0Qpb8u4dSpC$cr|@8rweF2VFF&OiKflu#z2nlkotlS|w-1&?Tz3qdAodwlV>SItbQ zorCxG^Pk^bjkv%qzdrg%*w;$a9DJd>^(2E;<80`Zh9b5xh78hYWJ`!1V2E-z5do=$ z@pPQN75-zJbdccxt?W57R`(=4y>4sq9kAz3lzBiv+Ch6q7A&hLs7jiklf+oJ+^eX% z6Cb%O?$KlT>IBqqh$Ec&NE|T@DJ@xK5C0J7cE#hh&<8B9;zf@4v@eHCF7Rd8?05q@ zH_3%>%8q;fBAXIcZ4yB-?Ku&4{|OT-Zj>(F;Fi3^IqDC*znlc3`Z`GO>MqD~m;w?= z753_tg^YlGC&gpwab@5y*Wh{m7s70)5!+`o{pDKM_+dh*BT>)OV+u4Y-}Xle^EB49 zquvDC*m)E3(vZ}AO_7vA{Fq&ZCnW=jw!;hn)gqz0m6-hJ@^Rk5Q z`g14}TbSd8za%n!C8SzLZ*Kv+9m^k`CiSHh%GMW=NrDdKM^3JUMA)gKM1UQX%}yrG zx$PIPBB2^S(%m^KtQ^y!yG~JqWKltYU0&zw;Gc3u_T!jxuo8hviJg)WiaHsq+bIV{cjF!rGqUi3OiG@9`b4L-R?dU1VQKETsqPwvI z`N?8^e(I9+A%tUoz?dxUIttl90@*2ZJiUGutTfL0mM%i*BjMbt+#K*iaxW!r1k*Tl zrx?+i%bknFnRqb`K1A3rk!rJ6AZ^caeR#l`1o|62EE5Y4xhLL4V9UhYLfVuo2IZ3e z9Eon4srPbg1Ht7P`OkgGw4=XBjcTz8JJ%dHncofN9v6o`g~sadyw7cfsUi*wBJQJU z{5p!`GV?@;>Y=bqPK-uCCV%F=iiag(S&CXYebUS5qCT+V7*-3}NZS${^9q&+vEnL? zIPPW$Y}yr^e-z57W=F#KpH`(!gU~cfAG9rpe`>l+ZS&;;+8L0O1)RLjR(koJp#&%Q zK*|+mi98z{bZR5uD9Ie2Ky|%*tu>vfV~*!4!gU|MuP%}wJXxXvE25Urxxf?bJ@zPf zx{AVlOh-sdE*fUZSt;k=z79CxUW%reHVcaud&FGaz=uaTteJc75iZr{IaW75#UgL! z%qltGEy}}X?$j{(lBg;>1J_{>nWmZ<4ongAD`KJU1i*QUfk=JrBn~7_k+7p6$dcU- zf2)fBfVh_u#lx-ZR1r%pMciMzgb=de9a7D zvjEK!C@}ciG8}vHya#w-1rCc1&i(c_i{eaSAwu69HHHgwJSRM3M+D-9?v_4f%m8#~ zDA1wI3NlyNyB$E($vYMD6=XD_$rh9f0HlPY(7HW!7ke`{$^zEry0znSBM+y(C))}r zN2RZ1+wNK|XV~S=ZadXLX|Ml&9uP_c>03RYHRD$m7N0t46$v!n*|tN;_{obqcPQ#zoDGG1KSSnPO1Bx`|WgTZGDq5m;hl~{CBBkTlh$;#%7!jp;*8S zZ-h0Qj|;v0veyZzkj!BYCfMUDX#4g{8ZWHKh;C-uz8`!o2aX_Qdi=#VHSrUZ7h5msD6J}wmJ+(+P9U~Nft z^)(q-`6V4*c(oDT!c=ktJ~?psjn(*%N|J=)->9B#ll>WwYrj!n zb^>AF>3BRr{>xwjkXB>{RV|4d;i%+TnrXgXv4UbF!`z^0KDM3db*OV<4iM*?o%7li55U!We|bo468bK@g9i(+`A?rWW(Jqppug?^9oawiR5!L)!8+_mKx%d}FJ zEci$wN`|rTrpjPDgT*(&L>w~Z)!s|uA3pTR|3VTFdL|>8jA(1#qX+Q8AW;0K{ZAwG zA^ZV+j4uP)%F8ShTX8u0PWq}j@2E?Yf?$l)I%R4z+{lCLtC*Prc`$8shx*lPu}wX@ z8)qf-scPUGY6x;W$-3#|If_1hgwwARa$h5?17k~Qc=%<>l%2VLH7`4gj(=f{EFfoy z>~6FSZGAJYqURHbZXxm6YAmIiVZdt!lg0)O6w9oW#Tp!i}<7XS%QJEL+oXFj^&uo}c#Y2yaWh)iCOz8v;L1fSxu+Akaq20m37&AY$ixVL#AN z@5{$o5f8F-G1z{x6qM|Fndbsi9qP`dLL>gaZ={pph|~g%VBqz5`c9t&s+X#6 z2CAGFefW-0Avth6J6WVsg#nNOG>*_(4OE6emryXOFru42$pb;LpK3F+bgcGBZ}!$- z^>1~)f3sd1shhbnueA1caEQPQ9!N8q3ha0AhzwqjlGCE;r<5CeXL&pKPSDLIjKtRo zd3>HBj;(<+f}_|Hu(`Sos&LiP(jl!Ng39|p z#9z$#|7rp@la}IaVehDJ={hv!JQVm7y+DIHG#rTZisFXRPzXZp5e%cL(NpHS^8%zu zMnWOnqZ8_z#$8<>?`yfLN#;rg)~|1%O&0kdZIqSNXIn3smUQ6}QqMehaT2{&yNyiq z1T1)CE1t;u@OJ&7T=H9DfZ4%WqQbTDBY_kyU=$Ki__niv9kx@iFs~y03ExTs;Y=;4 z5`mLTrjWU!+PX0yq>7M3I7fKh%KCGj&5ZlK658oh9Zwt*lVvdcwk|*&(?2T-*P@_| zTapY92lim#YLip@-Xn0yAJ(rqIzKh?c~v)F+uFrl@nT#Wv9G$wgY?1+J(g}6ROUer zz(jzw*!^a9JAlzA9AO=jx4IB-C?tx}#|sBn(kZ4^`{TYHPp{ZgGl8NuvK0R95Pu)= zgBdeILqJ{tc1tE7l#h-5 z9Q&2cFouCq42$Nr=C4N9x6`D)F0gOTU$E?!@sj4$gr){neRut>Rb%r8fzbjEC;{ zwR#m|-d3yTbW#UGcgIlMw|Y+q@GwIykReNDD_!R9%vzMl!wU=yof1G8Fza$6AHi!; z$?!?)nlRl%{G|yYSwNJRqpyC@({G_(xN%Q|JGUx)VRDhZeE%FPF3&akoc2VsgP$0w zB*eGV2s??D7wqe-n&i6!+>d$?IVkP}=q4!4*%5ZC#e@e&3 zdj0T!nr5~=jt0NK?U8t-ZbEG~60ClVF@K1YXrw)Hc;&4Tp4STWH^ccO@!k<*WItde zljFmNRxqrdteTHHri-ss(r%+jbaY{Mfsq2ilr-A@{^Qin!;!6HH}9Kb5eUOLV3=U1 zq9k3+)8BS1ZshCOSV{++mwVeF=A^7+(kf2+;E*)eHlpuG9hCRSXw(C;v2*TWeQdw@ zamYO>(eI9Cr}&YNo(2wm&%8s$WT)kRytC2=ADqo&?G&%ec+=c;B|QA3SI>D>?TRf* zL`N=qR%?R5s0ned-Pc7j0^CcR4Gag&YArc>+}N()ZZ*;Jrd6v*6#$@6yV(m6im;Mn@!bZQ4!NW5 z8dh6kYTV-F^z=1r0k0+kUFH@Q_t2JjX=Y{%j>1TW<1+Hz%X#<&w7;?UFzNC zsOQV8lD(wTRnax}=eGMkz*wQuK&D&xYb-bMlhdtviR0+l>8UQJ#-`RSj6V%AlpvB) z4|WD`2k~rRT}kCIH)Hs?>TyvFOomLRTA|NRa#Np&pODMWh>zs@BPUr}7+MvFTy2)1 zmPLllUjn36!rsWZuIA4ho;;cUnx?`_gEr*~>QyAn!K`(%Bw8vt5MIJF`E*DeNW|f# zx`m#(&0&4Yv0BYtd=@_+fF_H3rhK^0KpoujxUHp1(dge`=;gX>yVCC{bEPtw%{L>DZw@v6;%3WE$cW zf3Qpom0vG7kA0;Qwnt7*`Z-%zbvhWp?ci;TKf7%xRCn~}_w1lt>bTL&gLyto8Sd9Y zG#h+P8oMVo9i>eP!k27@RG=qOZOrlw8T(~f3l&`CajuBg# zu$(}CF~2w+r`HI>sZUjJgZW?S`GU72@PBR?GTqLkrZ)q|tlKW@Ixo>@80peCjWP(LuOjd^28?M*2(uVIk_6*k5<4yD}cGb!h6n zu2vXP6IHRjkC49fb-7`csftlm4cn$ znY-ZU24Vwtj4zQ$H`uglMM0fwY1Yoq*I;RdX{gdy$KBnq+3XvdkW`mr5^?a1{eqcz z*b<|?-^3V|hlGwdc>?T{F(Oy+n&a|#F{!KKdk#Kq9%O5aA*?y6P!R_Vd|tzpvt8|Y zQ$1f9jmK*DP*n3~wtOk6xLH1E(75}9^eO}4o!$C=qwmXkKkS5=>N2K$$aM91B~m%d zEUr%V_`{inId@J$dGU#c4RZvjgiY}|3lm(l_+ojPW%Ef<6BXLeuVWGIn)cui8ncW! z#s5G9{K58OVtrlv*NZYWr=qkWvI{#6Tiea%KDQ%92BhZ9mJ0NtM3Q)Ep{8WGhIbO8 zjre8CQMQ>{E>5l-KfEdQWRhe=AtR!bwi&P2ya2@UJn=vmFSO@R`K{3h*^0i~_5_Sf z>{t#dC&md*wOM~t?`!jjlsc7g)tPAQslg0&+X4Pzq^rUse!kBN?_DSlRlLjIknmR+ zmBfDvwrny?wanzj!gO&}5v<9LfG$oXPZ1-V7pEC^qv+d~fp@yM>QabM(qs-RB&hJ- z=kj?XD?XB441q>aJud1iT*rA)luw|9TdTwo@`N7GV7vp~;@L#IU#!VR6{JG(L;m67 zYvZ6YpQAO0=MetVAk1PeueWsxg$My>O$UQx~7Y{Rv=ym-L-a9+Xe z&u?htfNF$+u=rRW&ma8x!3tjm+nkv%KfIjYGsPXJ99*{(o~Ym?RY_AfnP39Mc;fux zxm^TZg|jP9=>9o|4J_BT$FgzILHFLytWHc1X5TF762FO51{@=pq~uE~6J01mBimG` z0gJeom=L$usc#-lQ)0NjpR084xT!Qn6cvST+CP3w1DAALbh=&Qa&L|RTp5n%CLCux z{QHv=bwoz?#gv+;#|6VctEqnQWoxrGTI~yueAa4tisQ3-$y+w_VI$?5H^Xq(=Imn~ znNHc9?Jk1ghc7a8_33?D6^_sru%x8K#I2k^le5GMF=42w`Yzu;6!R2a_)>57AJg8} z$k)L(-cx^1mCqB@JKWaG;qS0V3T5*W(q9Yr;X`xe_fE>a%xkbvVU7;?!)|qHOU_HX z-IWWsX-uqFc+VCySrv|12Qp7Aab*eXa)Qte7=3oRe|BHk&DTK<@#+)MHAD}OS2mGA zdCzNVEi5%VOHzc19DKGD-0p7mT@8Lkpd=|L_csUEC>>SA!Yp_B%wz|s#Cf~g%8xAQ z+O9G7+nKsFUJ633vyL@;zS52K9yY&xL9S?-aRb{^>oy8+l_%12a$z*z5Y(Yy*UL{O zD&P1xeBEDMx%m4B=NzXzIG<_saydQm4Zr-Dzr87S_`@is zYI{>Uz|d~$zNd}+qX+W=7o`F8;*Ump;X00!cOw!V*Sr@)K3(OsR7E%TVhY%xwGY+4 zFn>NFlY?snpjc?+n$)+{eX2r89j!AO>v$KaP#yPcuOEANzVZ14y?diaC%fI$H`NPb zWWJ}n^j!Pev2;h_7qJ6?H_wALQ=ZCYSy&xL=qaC(_-v8H&q(Vg+mR@+UmkP2v29n~ zd+UWa4E+A)@GkBZy$rp~+EPJCvLZYI{t`Y-el~xN#fsl))w>fyyXUj?2rh;1QkN*X z`|*XBmuJIco%O8OiDZil7?}xP$}_oJ8qo4h+_shF#J2FBf|TWTC>9uU7k=uc>nR(}8H}$}0=^MVCtbrPd-kC9v z-Mfzv|JFz?tZ87co52$Io2yHS)G>>y;?JE2_)TZ___+WNu-943)9l0a0e_}FN=J&| zdwcQV>Tf3D$Dd81B_+R^Zn_nB=$V;MOE7b#N1$BWp5{IfD7BvTBm`sS7K5Y>-w>M$ zVglNPcgBcFMZP+Y2;UWwmT;EH{xf*g0&Kr?vPhJ%A20FN;+O4y$dbmAawM(gNq!ez zBk($VL0cm298>HXv^Cl_og*3w>5Sxd_eI#1HnJr)BUNlxEki^HHo3RSf}^Of)z{5y zui;IN%n@D1lHp|L8FsYA5!GcrwTkBLfuDO(i`8X+!!5DGhkknBY{U~1laouu?^NdB zv+$|a6L|z`;|}jMqHF1ubeqDG{$d_jD64nO#oTe&*HPXyck*K$*$+oC#1~?+&7ArE zWUiOK>Y+MiT#-ADnKXKcW$9g{_{Y=k8#XSC z{xkJuydr;9%X=nlwlN?_2^%d<6uz5y$oN#tyT!N4`|UI!0&A;kslf)iYc`>BPygg{ zilj?Ey@=e8d%Axfj>eC5!(`R)0Tq}v5mrSoDMqyIoXQ*hr>&e58CJmi{_lVpgH`kj z>Q9US_5J7h7^P2K%dSRK?}g*Gh`i>W#+N>GF#pNCBsY6b)4D-;vWA6mErc97-XteyVdeq7W$olZ~(V>3Z;ZglO80;cb< zMeO;AUBb3XN+2HiUWme6IZVrJ{yOL|W5|29@%eJK%C!L^sHuAK;9}B7D&mjNrdw1f z)Bz}87UocChh4`t&vW&%H0UtMXuheiO=jI-o_rCr<0nf}r-%w|ru_5RN~kbkD1%X% zw{I9^P2WA;RQc=VlW@ScOH4K7N4vL&S%5xwPnGK7NPBzQL$RbPxq;D|BCnfvckHYP zY6m7$_Wd}IgaRaO+4g19#_Nm%@f3VNrdbR&joo|409*3@Bk~32in&T;hk562H);`| zp@aQq+bm|28e4gjSEu{0*#t|SkgtsmIgK0(q;v04*-xSgYB{24DHO<&`I0u?a~8is zyxT7l8yMdSqdwP)DhY^AeF4)>ztO9<{TAJzx%3onkorn-7&xrcv*W7W>f+CAVhVQO ztL35!Q=n|Fpl3v4vsdlx|G3Wbis~O%UX? z;wg1MyXuKz`#}?`DsSOEqfqP)Z#-mdQ!S_7vh4_X+N&<$Wz*zqE_X-1yvd`}eS9QS z+qyGkV9f+K{#)|vIiF@HyrhcH)_voveG;2Fp6%YEV~ Date: Thu, 21 Aug 2014 17:42:45 +0200 Subject: [PATCH 57/69] less and less possible references --- lib/God.js | 2 +- lib/God/ActionMethods.js | 6 ++++-- lib/Satan.js | 14 ++++++++------ 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/lib/God.js b/lib/God.js index 5220118c..5560349f 100644 --- a/lib/God.js +++ b/lib/God.js @@ -239,7 +239,7 @@ God.executeApp = function executeApp(env, cb) { clu.once('exit', function cluExit(exited_clu, code) { proc.removeAllListeners(); proc.process.removeAllListeners(); - God.handleExit(Common.serialize(proc), code); + God.handleExit(proc, code); return false; }); diff --git a/lib/God/ActionMethods.js b/lib/God/ActionMethods.js index e9ef3d4d..b7f4142b 100644 --- a/lib/God/ActionMethods.js +++ b/lib/God/ActionMethods.js @@ -186,10 +186,11 @@ module.exports = function(God) { delete cluster.workers[proc.id]; clearTimeout(timeout); God.killProcess(proc.process.pid, function() { - return setTimeout(function() { + setTimeout(function() { proc.pm2_env.status = cst.STOPPED_STATUS; cb(null, God.getFormatedProcesses()); }, 100); + return false; }); return false; }); @@ -299,9 +300,10 @@ module.exports = function(God) { if (proc.pm2_env.status == cst.ONLINE_STATUS) { God.killProcess(proc.process.pid, function() { - return setTimeout(function() { + setTimeout(function() { return cb(null, God.getFormatedProcesses()); }, 100); + return false; }); } else diff --git a/lib/Satan.js b/lib/Satan.js index dd47678a..552c1e88 100644 --- a/lib/Satan.js +++ b/lib/Satan.js @@ -11,10 +11,6 @@ var rpc = require('pm2-axon-rpc'); var axon = require('axon'); -var rep = axon.socket('rep'); -var req = axon.socket('req'); -var pub = axon.socket('pub-emitter'); - var debug = require('debug')('pm2:satan'); var util = require('util'); var fs = require('fs'); @@ -23,7 +19,6 @@ var cst = require('../constants.js'); var Stringify = require('json-stringify-safe'); -var pkg = require('../package.json'); /** * Export */ @@ -106,6 +101,8 @@ Satan.remoteWrapper = function() { // Only require here because God init himself var God = require('./God'); + var pkg = require('../package.json'); + Satan.processStateHandler(God); // Send ready message to Satan Client @@ -116,6 +113,7 @@ Satan.remoteWrapper = function() { pid : process.pid, pm2_version : pkg.version }); + pkg = null; } /** @@ -125,6 +123,7 @@ Satan.remoteWrapper = function() { /** * Rep/Req - RPC system to interact with God */ + var rep = axon.socket('rep'); var server = new rpc.Server(rep); @@ -168,6 +167,7 @@ Satan.remoteWrapper = function() { /** * Pub system for real time notifications */ + var pub = axon.socket('pub-emitter'); pub.bind(cst.DAEMON_PUB_PORT, cst.DAEMON_BIND_HOST); console.log('BUS system [READY] on %s:%s', cst.DAEMON_PUB_PORT, cst.DAEMON_BIND_HOST); @@ -258,7 +258,8 @@ Satan.launchDaemon = function launchDaemon(cb) { InteractorDaemonizer.launchAndInteract({}, function(err, data) { process.emit('satan:daemon:ready'); - return setTimeout(function() {cb(null, child)}, 150); + setTimeout(function() {cb(null, child)}, 20); + return false; }); }); @@ -298,6 +299,7 @@ Satan.pingDaemon = function pingDaemon(cb) { */ Satan.launchRPC = function launchRPC(cb) { debug('Launching RPC client on port %s %s', cst.DAEMON_RPC_PORT, cst.DAEMON_BIND_HOST); + var req = axon.socket('req'); Satan.client = new rpc.Client(req); Satan.client.sock.once('connect', function() { From 6e463cd166e0ee67e9ed25a75e77509775316dbc Mon Sep 17 00:00:00 2001 From: tknew2 Date: Thu, 21 Aug 2014 18:35:28 +0200 Subject: [PATCH 58/69] environment refresh on restart #528 --- CHANGELOG.md | 1 + lib/CLI.js | 108 +++++++++++++++++++----------- lib/God/ActionMethods.js | 9 ++- test/bash/#misc.sh# | 110 +++++++++++++++++++++++++++++++ test/bash/.#misc.sh | 1 + test/bash/cli.sh | 4 +- test/bash/env-refresh.sh | 45 +++++++++++++ test/fixtures/env-refreshed.json | 10 +++ test/fixtures/env.json | 4 +- test/main.sh | 2 + 10 files changed, 252 insertions(+), 42 deletions(-) create mode 100644 test/bash/#misc.sh# create mode 120000 test/bash/.#misc.sh create mode 100644 test/bash/env-refresh.sh create mode 100644 test/fixtures/env-refreshed.json diff --git a/CHANGELOG.md b/CHANGELOG.md index e18cc38d..285c3de6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ # 0.10.0 - PM2 Hellfire release - PM2 hearth code has been refactored and now it handles extreme scenario without any leak or bug +- PM2 restart refresh current environment variables #528 - PM2 reset reset restart numbers - Auto update script at PM2 installtion - --watch enhanced to avoid zombie processes diff --git a/lib/CLI.js b/lib/CLI.js index 1ee5cb6a..33f991e0 100644 --- a/lib/CLI.js +++ b/lib/CLI.js @@ -253,30 +253,45 @@ CLI.actionFromJson = function(action, file, jsonVia, cb) { if (!Array.isArray(appConf)) appConf = [appConf]; //convert to array - async.eachLimit(appConf, cst.CONCURRENT_ACTIONS, function(proc, next) { - var name; + async.eachLimit(appConf, cst.CONCURRENT_ACTIONS, function(proc, next1) { + var name = ''; + var new_env = proc.env ? proc.env : {}; if (!proc.name) name = p.basename(proc.script); else name = proc.name; - Satan.executeRemote(action, name, function(err, list) { - if (err) - console.error(err); - printOut(cst.PREFIX_MSG + 'Stopping process by name ' + name); - next(); + Common.getProcessIdByName(name, function(err, ids) { + if (err) { + printError(err); + return next1(); + } + if (!ids) return next1(); + + async.eachLimit(ids, 1, function(id, next2) { + var opts; + + if (action == 'restartProcessId') + opts = { id : id, env : new_env }; + else + opts = id; + + Satan.executeRemote(action, opts, function(err, res) { + if (err) { + printError(err); + return next2(); + } + printOut(cst.PREFIX_MSG + 'Process ' + id + ' restarted'); + return next2(); + }); + }, function(err) { + return next1(null, {success:true}); + }); }); - }, function(err) { - if (err) { - printOut(err); - if (cb) cb(err); - else return exitCli(cst.ERROR_EXIT); - } - if (cb) return cb(null, {success:true}); - else return setTimeout(speedList, 800); + else return setTimeout(speedList, 100); }); }; @@ -904,10 +919,10 @@ CLI.restart = function(process_name, cb) { process.stdin.setEncoding('utf8'); process.stdin.on('data', function (param) { process.stdin.pause(); - CLI.actionFromJson('restartProcessName', param, 'pipe', cb); + CLI.actionFromJson('restartProcessId', param, 'pipe', cb); }); } else if (process_name.indexOf('.json') > 0) - CLI.actionFromJson('restartProcessName', process_name, 'file', cb); + CLI.actionFromJson('restartProcessId', process_name, 'file', cb); else if (process_name == 'all') CLI._restartAll(cb); else if (isNaN(parseInt(process_name))) { @@ -925,14 +940,28 @@ CLI.restart = function(process_name, cb) { * @param {} pm2_name * @return */ -CLI._restartProcessByName = function(pm2_name, cb) { - Satan.executeRemote('restartProcessName', pm2_name, function(err, list) { +CLI._restartProcessByName = function(process_name, cb) { + Common.getProcessIdByName(process_name, function(err, ids) { if (err) { printError(err); return cb ? cb({msg:err}) : exitCli(cst.ERROR_EXIT); } - printOut(cst.PREFIX_MSG + 'Process ' + pm2_name + ' restarted'); - return cb ? cb(null, list) : speedList(); + async.eachLimit(ids, 1, function(id, next) { + Satan.executeRemote('restartProcessId', { + id: id, + env : process.env + }, function(err, res) { + if (err) { + printError(err); + return next(); + } + printOut(cst.PREFIX_MSG + 'Process ' + id + ' restarted'); + return next(); + }); + }, function(err) { + if (err) return cb(new Error(err)); + return cb ? cb(null, ids) : speedList(); + }); }); }; @@ -943,12 +972,15 @@ CLI._restartProcessByName = function(pm2_name, cb) { * @return */ CLI._restartProcessById = function(pm2_id, cb) { - Satan.executeRemote('restartProcessId', pm2_id, function(err, res) { + Satan.executeRemote('restartProcessId', { + id: pm2_id, + env : process.env + }, function(err, res) { if (err) { printError(err); return cb ? cb({msg:err}) : exitCli(cst.ERROR_EXIT); } - printOut(cst.PREFIX_MSG + 'Process ' + pm2_id + ' restarted'); + printOut(cst.PREFIX_MSG + 'Process id ' + pm2_id + ' restarted'); return cb ? cb(null, res) : speedList(); }); }; @@ -969,7 +1001,6 @@ CLI._restartAll = function(cb) { return cb ? cb({msg:err}) : exitCli(cst.ERROR_EXIT); } - (function rec(processes) { var proc = processes[0]; @@ -977,15 +1008,18 @@ CLI._restartAll = function(cb) { printOut(cst.PREFIX_MSG + 'All processes has been restarted'); return cb ? cb(null, processes) : setTimeout(speedList, 1000); } - Satan.executeRemote('restartProcessId', proc.pm2_env.pm_id, function(err, res) { - if (err) { - printError(err); - return cb ? cb({msg:err}) : exitCli(cst.ERROR_EXIT); - } - printOut(cst.PREFIX_MSG + 'Process ' + proc.pm2_env.name + ' restarted'); - processes.shift(); - return rec(processes); - }); + Satan.executeRemote('restartProcessId', { + id : proc.pm2_env.pm_id, + env : process.env + }, function(err, res) { + if (err) { + printError(err); + return cb ? cb({msg:err}) : exitCli(cst.ERROR_EXIT); + } + printOut(cst.PREFIX_MSG + 'Process ' + proc.pm2_env.name + ' restarted'); + processes.shift(); + return rec(processes); + }); return false; })(list); }); @@ -1010,9 +1044,9 @@ CLI.delete = function(process_name, jsonVia, cb) { printOut(cst.PREFIX_MSG + 'Deleting ' + process_name); if (jsonVia == 'pipe') - return CLI.actionFromJson('deleteProcessName', process_name, 'pipe'); + return CLI.actionFromJson('deleteProcessId', process_name, 'pipe'); if (process_name.indexOf('.json') > 0) - return CLI.actionFromJson('deleteProcessName', process_name, 'file'); + return CLI.actionFromJson('deleteProcessId', process_name, 'file'); else if (process_name == 'all') { Satan.executeRemote('deleteAll', {}, function(err, list) { if (err) { @@ -1055,10 +1089,10 @@ CLI.stop = function(process_name, cb) { process.stdin.setEncoding('utf8'); process.stdin.on('data', function (param) { process.stdin.pause(); - CLI.actionFromJson('stopProcessName', param, 'pipe', cb); + CLI.actionFromJson('stopProcessId', param, 'pipe', cb); }); } else if (process_name.indexOf('.json') > 0) - CLI.actionFromJson('stopProcessName', process_name, 'file', cb); + CLI.actionFromJson('stopProcessId', process_name, 'file', cb); else if (process_name == 'all') CLI._stopAll(cb); else if (isNaN(parseInt(process_name))) { diff --git a/lib/God/ActionMethods.js b/lib/God/ActionMethods.js index b7f4142b..5662084a 100644 --- a/lib/God/ActionMethods.js +++ b/lib/God/ActionMethods.js @@ -15,7 +15,7 @@ var cst = require('../../constants.js'); var pkg = require('../../package.json'); var pidusage = require('pidusage'); var Common = require('../Common'); - +var util = require('util'); var debug = require('debug')('pm2:ActionMethod'); @@ -288,7 +288,10 @@ module.exports = function(God) { * @param {} cb * @return Literal */ - God.restartProcessId = function(id, cb) { + God.restartProcessId = function(opts, cb) { + var id = opts.id; + var env = opts.env; + if (!(id in God.clusters_db)) return cb(God.logAndGenerateError(id + ' id unknown'), {}); @@ -298,6 +301,8 @@ module.exports = function(God) { God.resetState(proc.pm2_env); + util._extend(proc.pm2_env.env, opts.env); + if (proc.pm2_env.status == cst.ONLINE_STATUS) { God.killProcess(proc.process.pid, function() { setTimeout(function() { diff --git a/test/bash/#misc.sh# b/test/bash/#misc.sh# new file mode 100644 index 00000000..871dd072 --- /dev/null +++ b/test/bash/#misc.sh# @@ -0,0 +1,110 @@ +#!/usr/bin/env bash + +SRC=$(cd $(dirname "$0"); pwd) +source "${SRC}/include.sh" + +cd $file_path + +echo -e "\033[1mRunning tests:\033[0m" + +# +# Max memory auto restart option +# +# -max-memory-restart option && maxMemoryRestart (via JSON file) +# +$pm2 start big-array.js --max-memory-restart 19 +sleep 7 +$pm2 list +should 'process should been restarted' 'restart_time: 0' 0 + +$pm2 delete all + +# +# Via JSON +# +$pm2 start max-mem.json +sleep 7 +$pm2 list +should 'process should been restarted' 'restart_time: 0' 0 + +$pm2 delete all + +$pm2 start env.js + +OUT_LOG=`$pm2 prettylist | grep -m 1 -E "pm_out_log_path:" | sed "s/.*'\([^']*\)',/\1/"` + +cat /dev/null > $OUT_LOG + +sleep 1 + +OUT=`cat $OUT_LOG | head -n 1` + +if [ $OUT = "undefined" ] +then + success "environment variable not defined" +else + fail "environment defined ? wtf ?" +fi + +$pm2 delete all + +$pm2 start env.json + +cat /dev/null > $OUT_LOG + +sleep 1 + +OUT=`cat $OUT_LOG | head -n 1` + +if [ "$OUT" = "undefined" ] +then + fail "environment variable hasnt been defined" +else + success "environment variable successfully defined" +fi + + +##################### +# Merge logs option # +##################### +$pm2 kill + +rm outmerge* + +$pm2 start echo.js -i 4 -o outmerge.log + +cat outmerge.log > /dev/null +ispec 'file outmerge.log should not exist' + +cat outmerge-0.log > /dev/null +spec 'file outmerge-0.log should exist' + +rm outmerge* + +############ Now with --merge option + +$pm2 kill + +rm outmerge* + +$pm2 start echo.js -i 4 -o outmerge.log --merge-logs + +cat outmerge.log > /dev/null +spec 'file outmerge.log should exist' + +cat outmerge-0.log > /dev/null +ispec 'file outmerge-0.log should not exist' + +rm outmerge* + +########### coffee cluster test +$pm2 delete all + +$pm2 start echo.coffee + +should 'process should not have been restarted' 'restart_time: 0' 1 +should 'process should be online' "status: 'online'" 1 + +# +###### Refresh environment +# diff --git a/test/bash/.#misc.sh b/test/bash/.#misc.sh new file mode 120000 index 00000000..ab0c0af0 --- /dev/null +++ b/test/bash/.#misc.sh @@ -0,0 +1 @@ +tknew@Transport.14962:1408626701 \ No newline at end of file diff --git a/test/bash/cli.sh b/test/bash/cli.sh index 0bd8f8dd..48c5367d 100755 --- a/test/bash/cli.sh +++ b/test/bash/cli.sh @@ -124,8 +124,8 @@ spec "Should get the right JSON with HttpInterface file launched" $pm2 flush spec "Should clean logs" -cat ~/.pm2/logs/echo-out.log | wc -l -spec "File Log should be cleaned" +# cat ~/.pm2/logs/echo-out.log | wc -l +# spec "File Log should be cleaned" sleep 0.3 $http_get -q http://localhost:9615/ -O $JSON_FILE diff --git a/test/bash/env-refresh.sh b/test/bash/env-refresh.sh new file mode 100644 index 00000000..9d02bfba --- /dev/null +++ b/test/bash/env-refresh.sh @@ -0,0 +1,45 @@ +#!/usr/bin/env bash + +SRC=$(cd $(dirname "$0"); pwd) +source "${SRC}/include.sh" +cd $file_path + +echo -e "\033[1mENV REFRESH\033[0m" + +# +# Restart via CLI +# +TEST_VARIABLE='hello1' $pm2 start env.js -o out-env.log --merge-logs --name "env" +>out-env.log + +sleep 0.5 +grep "hello1" out-env.log &> /dev/null +spec "should contain env variable" + +TEST_VARIABLE='89hello89' $pm2 restart env + +sleep 0.5 +grep "89hello89" out-env.log &> /dev/null +spec "should contain refreshed environment variable" + +$pm2 delete all + +# HEYYYY + +# +# Restart via JSON +# + +$pm2 start env.json +>out-env.log + +sleep 0.5 +grep "YES" out-env.log &> /dev/null +spec "should contain env variable" + +$pm2 restart env-refreshed.json +>out-env.log + +sleep 0.5 +grep "HEYYYY" out-env.log &> /dev/null +spec "should contain refreshed env variable via json" diff --git a/test/fixtures/env-refreshed.json b/test/fixtures/env-refreshed.json new file mode 100644 index 00000000..4c68b380 --- /dev/null +++ b/test/fixtures/env-refreshed.json @@ -0,0 +1,10 @@ +[{ + "name" : "env", + "script" : "./env.js", + "out_file" : "out-env.log", + "merge_logs" : true, + "env": { + "NODE_ENV": "production", + "TEST_VARIABLE": "HEYYYY" + } +}] diff --git a/test/fixtures/env.json b/test/fixtures/env.json index 341ca233..7215f1f2 100644 --- a/test/fixtures/env.json +++ b/test/fixtures/env.json @@ -1,8 +1,10 @@ [{ "name" : "env", "script" : "./env.js", + "out_file" : "out-env.log", + "merge_logs" : true, "env": { "NODE_ENV": "production", - "TEST_VARIABLE": "xxx" + "TEST_VARIABLE": "YES" } }] diff --git a/test/main.sh b/test/main.sh index 010fb5c4..cee71e1c 100644 --- a/test/main.sh +++ b/test/main.sh @@ -38,6 +38,8 @@ bash ./test/bash/fork.sh spec "Fork verified" bash ./test/bash/infinite_loop.sh spec "Infinite loop stop" +bash ./test/bash/env-refresh.sh +spec "Environment refresh on restart" bash ./test/bash/reset.sh spec "Reset meta" From 84703112fd4b97c24260955b2fcede4e536d9b41 Mon Sep 17 00:00:00 2001 From: tknew2 Date: Thu, 21 Aug 2014 18:36:01 +0200 Subject: [PATCH 59/69] clean --- test/bash/#misc.sh# | 110 -------------------------------------------- test/bash/.#misc.sh | 1 - 2 files changed, 111 deletions(-) delete mode 100644 test/bash/#misc.sh# delete mode 120000 test/bash/.#misc.sh diff --git a/test/bash/#misc.sh# b/test/bash/#misc.sh# deleted file mode 100644 index 871dd072..00000000 --- a/test/bash/#misc.sh# +++ /dev/null @@ -1,110 +0,0 @@ -#!/usr/bin/env bash - -SRC=$(cd $(dirname "$0"); pwd) -source "${SRC}/include.sh" - -cd $file_path - -echo -e "\033[1mRunning tests:\033[0m" - -# -# Max memory auto restart option -# -# -max-memory-restart option && maxMemoryRestart (via JSON file) -# -$pm2 start big-array.js --max-memory-restart 19 -sleep 7 -$pm2 list -should 'process should been restarted' 'restart_time: 0' 0 - -$pm2 delete all - -# -# Via JSON -# -$pm2 start max-mem.json -sleep 7 -$pm2 list -should 'process should been restarted' 'restart_time: 0' 0 - -$pm2 delete all - -$pm2 start env.js - -OUT_LOG=`$pm2 prettylist | grep -m 1 -E "pm_out_log_path:" | sed "s/.*'\([^']*\)',/\1/"` - -cat /dev/null > $OUT_LOG - -sleep 1 - -OUT=`cat $OUT_LOG | head -n 1` - -if [ $OUT = "undefined" ] -then - success "environment variable not defined" -else - fail "environment defined ? wtf ?" -fi - -$pm2 delete all - -$pm2 start env.json - -cat /dev/null > $OUT_LOG - -sleep 1 - -OUT=`cat $OUT_LOG | head -n 1` - -if [ "$OUT" = "undefined" ] -then - fail "environment variable hasnt been defined" -else - success "environment variable successfully defined" -fi - - -##################### -# Merge logs option # -##################### -$pm2 kill - -rm outmerge* - -$pm2 start echo.js -i 4 -o outmerge.log - -cat outmerge.log > /dev/null -ispec 'file outmerge.log should not exist' - -cat outmerge-0.log > /dev/null -spec 'file outmerge-0.log should exist' - -rm outmerge* - -############ Now with --merge option - -$pm2 kill - -rm outmerge* - -$pm2 start echo.js -i 4 -o outmerge.log --merge-logs - -cat outmerge.log > /dev/null -spec 'file outmerge.log should exist' - -cat outmerge-0.log > /dev/null -ispec 'file outmerge-0.log should not exist' - -rm outmerge* - -########### coffee cluster test -$pm2 delete all - -$pm2 start echo.coffee - -should 'process should not have been restarted' 'restart_time: 0' 1 -should 'process should be online' "status: 'online'" 1 - -# -###### Refresh environment -# diff --git a/test/bash/.#misc.sh b/test/bash/.#misc.sh deleted file mode 120000 index ab0c0af0..00000000 --- a/test/bash/.#misc.sh +++ /dev/null @@ -1 +0,0 @@ -tknew@Transport.14962:1408626701 \ No newline at end of file From eead74f1b3071c71c7c2e5b1df93569e059f6bf9 Mon Sep 17 00:00:00 2001 From: tknew2 Date: Fri, 22 Aug 2014 16:09:49 +0200 Subject: [PATCH 60/69] upgrade pm2-logs --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index cb6438db..021f5a9f 100644 --- a/package.json +++ b/package.json @@ -149,7 +149,7 @@ "promise-spawner": "^0.0.3" }, "optionalDependencies": { - "pm2-logs" : "latest", + "pm2-logs" : "~0.1.0", "ikt" : "git+http://ikt.pm2.io/ikt.git#master" }, "bugs": { From 6c5b67d1e46b9579db0f0a40f4601e6a462abe65 Mon Sep 17 00:00:00 2001 From: tknew2 Date: Fri, 22 Aug 2014 16:12:06 +0200 Subject: [PATCH 61/69] update test --- test/programmatic/god.mocha.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/programmatic/god.mocha.js b/test/programmatic/god.mocha.js index c7f3d834..59a35695 100644 --- a/test/programmatic/god.mocha.js +++ b/test/programmatic/god.mocha.js @@ -140,7 +140,7 @@ describe('God', function() { }); it('should restart the same process and set it as state online and be up', function(done) { - God.restartProcessId(clu.pm2_env.pm_id, function(err, dt) { + God.restartProcessId({id:clu.pm2_env.pm_id}, function(err, dt) { var proc = God.findProcessById(clu.pm2_env.pm_id); proc.pm2_env.status.should.be.equal('online'); God.checkProcess(proc.process.pid).should.be.equal(true); From d13eaa173e356b067e6d44837d3160f2e1113650 Mon Sep 17 00:00:00 2001 From: tknew2 Date: Fri, 22 Aug 2014 16:20:51 +0200 Subject: [PATCH 62/69] more verbosity on all deletion --- lib/CLI.js | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/lib/CLI.js b/lib/CLI.js index 33f991e0..bef4914f 100644 --- a/lib/CLI.js +++ b/lib/CLI.js @@ -1041,20 +1041,27 @@ CLI.delete = function(process_name, jsonVia, cb) { process_name = process_name.toString(); } - printOut(cst.PREFIX_MSG + 'Deleting ' + process_name); + printOut(cst.PREFIX_MSG + 'Deleting %s process', process_name); if (jsonVia == 'pipe') return CLI.actionFromJson('deleteProcessId', process_name, 'pipe'); if (process_name.indexOf('.json') > 0) return CLI.actionFromJson('deleteProcessId', process_name, 'file'); else if (process_name == 'all') { - Satan.executeRemote('deleteAll', {}, function(err, list) { + Common.getAllProcessId(function(err, ids) { if (err) { - printError(cst.PREFIX_MSG_ERR + err); + printError(err); return cb ? cb({msg:err}) : exitCli(cst.ERROR_EXIT); } - printOut(cst.PREFIX_MSG + 'All processes has been stopped and deleted'); - return cb ? cb(null, list) : speedList(); + async.eachLimit(ids, 1, function(id, next) { + printOut(cst.PREFIX_MSG + 'Deleting process id %d', id); + Satan.executeRemote('deleteProcessId', id, function(err, list) { + return next(); + }); + }, function(err) { + return cb ? cb(null, ids) : speedList(); + }); + return false; }); } else if (!isNaN(parseInt(process_name))) { @@ -1408,10 +1415,9 @@ CLI.ilogs = function() { * @return */ CLI.killDaemon = function(cb) { - printOut(cst.PREFIX_MSG + 'Stopping PM2'); + printOut(cst.PREFIX_MSG + 'Stopping PM2...'); - printOut(cst.PREFIX_MSG + 'Stopping & Deleting all processes...'); - Satan.executeRemote('deleteAll', {}, function(err, list) { + this.delete('all', function(err, list) { printOut(cst.PREFIX_MSG + 'All processes has been stopped and deleted'); InteractorDaemonizer.killDaemon(function() { From 1ec8ff645524b49549590880e2b9dc70d0892fd3 Mon Sep 17 00:00:00 2001 From: tknew2 Date: Fri, 22 Aug 2014 16:44:56 +0200 Subject: [PATCH 63/69] can restart script --- lib/Common.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/Common.js b/lib/Common.js index 5fb04f77..d588770c 100644 --- a/lib/Common.js +++ b/lib/Common.js @@ -224,7 +224,8 @@ Common.getProcessIdByName = function(name, cb) { } list.forEach(function(proc) { - if (typeof(name) === 'string' && proc.name == name) { + if (proc.pm2_env.name == name || + proc.pm2_env.pm_exec_path == p.resolve(name)) { found_proc.push(proc.pm_id); } }); From ffd4841eaa6bf80c3096c4cba35414c4b2d9af99 Mon Sep 17 00:00:00 2001 From: tknew2 Date: Fri, 22 Aug 2014 17:00:20 +0200 Subject: [PATCH 64/69] update tests --- test/programmatic/programmatic.js | 1 - 1 file changed, 1 deletion(-) diff --git a/test/programmatic/programmatic.js b/test/programmatic/programmatic.js index 531282a9..6ccc1d38 100644 --- a/test/programmatic/programmatic.js +++ b/test/programmatic/programmatic.js @@ -113,7 +113,6 @@ describe('PM2 programmatic calls', function() { it('should delete processes', function(done) { pm2.delete('all', function(err, ret) { should(err).be.null; - ret.length.should.eql(0); pm2.list(function(err, ret) { should(err).be.null; ret.length.should.eql(0); From c745b481895f161ce4a0aa34c1d1a37b272b4855 Mon Sep 17 00:00:00 2001 From: tknew2 Date: Fri, 22 Aug 2014 17:09:17 +0200 Subject: [PATCH 65/69] right exit code when process name doesnt exist --- lib/CLI.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/CLI.js b/lib/CLI.js index bef4914f..0f849bc9 100644 --- a/lib/CLI.js +++ b/lib/CLI.js @@ -946,6 +946,9 @@ CLI._restartProcessByName = function(process_name, cb) { printError(err); return cb ? cb({msg:err}) : exitCli(cst.ERROR_EXIT); } + if (!ids || ids.length === 0) { + return cb ? cb({msg:'Unknown process name'}) : exitCli(cst.ERROR_EXIT); + } async.eachLimit(ids, 1, function(id, next) { Satan.executeRemote('restartProcessId', { id: id, From 32027ef9657096c34c0aa541f026aab20bded3bf Mon Sep 17 00:00:00 2001 From: root Date: Fri, 22 Aug 2014 14:16:40 -0400 Subject: [PATCH 66/69] upgrade - add benchmarking tools --- examples/keymetrics-load.js | 2 +- examples/kill-not-so-fast.js | 2 +- lib/God.js | 5 ++++- lib/God/ActionMethods.js | 3 +-- lib/Satan.js | 7 ++++++- package.json | 4 ++-- test/benchmarks/monit-daemon.sh | 16 +++++++++++++++ test/benchmarks/monit.sh | 35 +++++++++++++++++++++++++++++++++ test/benchmarks/result.monit | 6 ++++++ 9 files changed, 72 insertions(+), 8 deletions(-) create mode 100755 test/benchmarks/monit-daemon.sh create mode 100755 test/benchmarks/monit.sh create mode 100644 test/benchmarks/result.monit diff --git a/examples/keymetrics-load.js b/examples/keymetrics-load.js index 83bae41a..947fead0 100644 --- a/examples/keymetrics-load.js +++ b/examples/keymetrics-load.js @@ -7,7 +7,7 @@ pm2.connect(function() { pm2.start('examples/human_event.js', function() { pm2.start('examples/child.js', {instances:2},function() { pm2.start('examples/kill-not-so-fast.js', { - instances:10, + instances:20, minUptime: 0, maxRestarts : 0 }, function() { diff --git a/examples/kill-not-so-fast.js b/examples/kill-not-so-fast.js index d0385b46..921b0517 100644 --- a/examples/kill-not-so-fast.js +++ b/examples/kill-not-so-fast.js @@ -3,5 +3,5 @@ console.log('start'); setTimeout(function() { console.log('exit'); - process.exit(1); + throw new Error('Exitasdsadasdsda unacepted !!'); }, 300); diff --git a/lib/God.js b/lib/God.js index 5560349f..80801605 100644 --- a/lib/God.js +++ b/lib/God.js @@ -8,6 +8,7 @@ var fs = require('fs'); var p = path; var Common = require('./Common'); var cst = require('../constants.js'); +var pidusage = require('pidusage'); // require('webkit-devtools-agent').start({ // port: 9999, @@ -57,11 +58,13 @@ God.handleExit = function handleExit(clu, exit_code) { var proc = this.clusters_db[clu.pm2_env.pm_id]; + pidusage.unmonitor(proc.process.pid); + if (!proc) { console.error('Process undefined ? with process id ', clu.pm2_env.pm_id); return false; } - + var stopping = (proc.pm2_env.status == cst.STOPPING_STATUS || proc.pm2_env.status == cst.ERRORED_STATUS) ? true : false; var overlimit = false; diff --git a/lib/God/ActionMethods.js b/lib/God/ActionMethods.js index 5662084a..4f2bdb2c 100644 --- a/lib/God/ActionMethods.js +++ b/lib/God/ActionMethods.js @@ -38,12 +38,11 @@ module.exports = function(God) { var processes = God.getFormatedProcesses(); async.map(processes, function computeMonitor(pro, next) { - if (pro.pm2_env.status != cst.STOPPED_STATUS && pro.pm2_env.status != cst.STOPPING_STATUS && pro.pm2_env.status != cst.ERRORED_STATUS) { - pidusage(pro.pid, function retPidUsage(err, res) { + pidusage.stat(pro.pid, function retPidUsage(err, res) { if (err) { pro['monit'] = { memory : 0, diff --git a/lib/Satan.js b/lib/Satan.js index 552c1e88..d01adf6b 100644 --- a/lib/Satan.js +++ b/lib/Satan.js @@ -83,7 +83,12 @@ Satan.processStateHandler = function(God) { try { fs.writeFileSync(cst.PM2_PID_FILE_PATH, process.pid); } catch(e){} - + + process.on('SIGILL', function() { + global.gc(); + console.log(' running garbage collector'); + }); + process.on('SIGTERM', gracefullExit); process.on('SIGINT', gracefullExit); process.on('SIGQUIT', gracefullExit); diff --git a/package.json b/package.json index 021f5a9f..f9e4bd15 100644 --- a/package.json +++ b/package.json @@ -133,7 +133,7 @@ "json-stringify-safe": "~5.0.0", "nssocket": "~0.5.1", "punt" : "~2.2.0", - "pidusage": "~0.0.7", + "pidusage": "~0.1.0", "pm2-axon-rpc": "~0.2.1", "pm2-deploy": "~0.0.4", "pm2-interface" : "~1.1.0", @@ -149,7 +149,7 @@ "promise-spawner": "^0.0.3" }, "optionalDependencies": { - "pm2-logs" : "~0.1.0", + "pm2-logs" : "~0.1.1", "ikt" : "git+http://ikt.pm2.io/ikt.git#master" }, "bugs": { diff --git a/test/benchmarks/monit-daemon.sh b/test/benchmarks/monit-daemon.sh new file mode 100755 index 00000000..3f456287 --- /dev/null +++ b/test/benchmarks/monit-daemon.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +while [ true ] +do + PM2_PID=`pgrep "pm2: Daemon" -o` + + # Run garbage collector + kill -SIGILL $PM2_PID + sleep 5 + + FILE="/proc/$PM2_PID/smaps" + Rss=`echo 0 $(cat $FILE | grep Rss | awk '{print $2}' | sed 's#^#+#') | bc;` + + echo `date +%H:%M:%S` $Rss >> $RESULT_FILE + sleep 100 +done diff --git a/test/benchmarks/monit.sh b/test/benchmarks/monit.sh new file mode 100755 index 00000000..5ea4f80d --- /dev/null +++ b/test/benchmarks/monit.sh @@ -0,0 +1,35 @@ +#!/bin/bash + +RESULT_FILE=result.monit + +export RESULT_FILE=$RESULT_FILE + +launch() { + echo "========= `date`" >> $RESULT_FILE + nohup ./monit-daemon.sh &> monit.log & +} + +ppkill() { + pkill -f monit-daemon.sh ; pkill -f sleep +} + +case "$1" in + start) + launch + ;; + kill) + ppkill + ;; + stop) + ppkill + ;; + restart) + ppkill + launch + ;; + *) + echo "Usage: {start|kill|stop|restart}" + exit 1 + ;; +esac +exit $RETVAL diff --git a/test/benchmarks/result.monit b/test/benchmarks/result.monit new file mode 100644 index 00000000..ccc7030d --- /dev/null +++ b/test/benchmarks/result.monit @@ -0,0 +1,6 @@ +========= Fri Aug 22 14:11:29 EDT 2014 +14:11:35 61012 +========= Fri Aug 22 14:11:45 EDT 2014 +14:11:50 59984 +========= Fri Aug 22 14:12:47 EDT 2014 +14:12:52 59464 From 8c2944cf15419bb3c1a6e5f5ac8a65279031d190 Mon Sep 17 00:00:00 2001 From: tknew2 Date: Fri, 22 Aug 2014 20:20:35 +0200 Subject: [PATCH 67/69] fixes --- CHANGELOG.md | 3 ++- lib/God/ActionMethods.js | 10 ++++++++-- package.json | 2 +- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 285c3de6..2570cf2c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,8 +3,9 @@ - PM2 hearth code has been refactored and now it handles extreme scenario without any leak or bug - PM2 restart refresh current environment variables #528 +- PM2 delete all more verbose - PM2 reset reset restart numbers -- Auto update script at PM2 installtion +- Auto update script at PM2 installation - --watch enhanced to avoid zombie processes - Restart app when reaching a limit of memory by using --max-memory-restart (and max_memory_restart via JSON)(https://github.com/Unitech/pm2#max-memory-restart) - PM2 respects strong unix standard process management diff --git a/lib/God/ActionMethods.js b/lib/God/ActionMethods.js index 5662084a..739d0c91 100644 --- a/lib/God/ActionMethods.js +++ b/lib/God/ActionMethods.js @@ -56,7 +56,7 @@ module.exports = function(God) { memory : Math.floor(res.memory), cpu : Math.floor(res.cpu) }; - + res = null; return next(null, pro); }); } @@ -175,6 +175,11 @@ module.exports = function(God) { proc.pm2_env.status = cst.STOPPING_STATUS; + if (!proc.process.pid) { + proc.pm2_env.status = cst.STOPPED_STATUS; + return cb(null); + } + /** * Process to stop on cluster mode */ @@ -183,7 +188,8 @@ module.exports = function(God) { proc.state != 'dead') { proc.once('disconnect', function(){ - delete cluster.workers[proc.id]; + cluster.workers[proc.id] = null; + clearTimeout(timeout); God.killProcess(proc.process.pid, function() { setTimeout(function() { diff --git a/package.json b/package.json index 021f5a9f..9cf3636d 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "pm2", "preferGlobal": "true", - "version": "0.10.0-beta23", + "version": "0.10.0-beta25", "os": [ "!win32" ], From 5888dbef79bc09e91cf982600653e9f8e8f3c0ce Mon Sep 17 00:00:00 2001 From: tknew2 Date: Fri, 22 Aug 2014 20:25:42 +0200 Subject: [PATCH 68/69] fixes --- examples/keymetrics-load.js | 2 +- test/bash/cli2.sh | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/examples/keymetrics-load.js b/examples/keymetrics-load.js index 947fead0..83bae41a 100644 --- a/examples/keymetrics-load.js +++ b/examples/keymetrics-load.js @@ -7,7 +7,7 @@ pm2.connect(function() { pm2.start('examples/human_event.js', function() { pm2.start('examples/child.js', {instances:2},function() { pm2.start('examples/kill-not-so-fast.js', { - instances:20, + instances:10, minUptime: 0, maxRestarts : 0 }, function() { diff --git a/test/bash/cli2.sh b/test/bash/cli2.sh index a34c3b79..0bd0c38f 100755 --- a/test/bash/cli2.sh +++ b/test/bash/cli2.sh @@ -72,5 +72,3 @@ cat outech-0.log > /dev/null spec "file outech-0.log exist" cat errech-0.log > /dev/null spec "file errech-0.log exist" - -should 'should has not restarted' 'restart_time: 0' 10 From 415cf8657b7b54d05b598b573f5038069a3112cc Mon Sep 17 00:00:00 2001 From: tknew2 Date: Fri, 22 Aug 2014 20:33:57 +0200 Subject: [PATCH 69/69] merge with master --- README.md | 2 ++ bin/pm2 | 4 ++-- lib/CliUx.js | 1 + test/bash/cli.sh | 2 +- test/bash/fork.sh | 2 +- 5 files changed, 7 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 0c3a3581..c559d962 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,7 @@ PM2 is a process manager for Node apps with a built-in load balancer. ### Tech notes + PM2 is perfect for spreading your stateless Node.js code across all CPUs available on a server, for keeping all processes alive forever and 0s reload them. ### Main features @@ -31,6 +32,7 @@ Works on Linux & MacOS. + Master: [![Build Status](https://api.travis-ci.org/Unitech/PM2.png?branch=master)](https://travis-ci.org/Unitech/PM2) Development: [![Build Status](https://api.travis-ci.org/Unitech/PM2.png?branch=development)](https://travis-ci.org/Unitech/PM2) diff --git a/bin/pm2 b/bin/pm2 index e5925929..98c50f62 100755 --- a/bin/pm2 +++ b/bin/pm2 @@ -320,7 +320,7 @@ commander.command('save') })); // -// Resurect +// Resurrect // commander.command('resurrect') .description('resurrect previously dumped processes') @@ -333,7 +333,7 @@ commander.command('resurrect') // Set pm2 to startup // commander.command('startup ') - .description('auto resurect process at startup. [platform] = ubuntu, centos, gentoo or systemd') + .description('auto resurrect process at startup. [platform] = ubuntu, centos, gentoo or systemd') .action(function(platform) { CLI.startup(platform, commander); }); diff --git a/lib/CliUx.js b/lib/CliUx.js index 55c2b43f..544722ed 100644 --- a/lib/CliUx.js +++ b/lib/CliUx.js @@ -53,6 +53,7 @@ UX.describeTable = function(process) { { 'name': pm2_env.name }, { 'id' : pm2_env.pm_id }, { 'path' : pm2_env.pm_exec_path }, + { 'args' : pm2_env.args ? JSON.parse(pm2_env.args).join(' ') : '' }, { 'exec cwd' : pm2_env.pm_cwd }, { 'error log path' : pm2_env.pm_err_log_path }, { 'out log path' : pm2_env.pm_out_log_path }, diff --git a/test/bash/cli.sh b/test/bash/cli.sh index 48c5367d..08299bcf 100755 --- a/test/bash/cli.sh +++ b/test/bash/cli.sh @@ -199,7 +199,7 @@ success "Processes sucessfully restarted with a specific name" $pm2 kill $pm2 resurrect -spec "Should resurect all apps" +spec "Should resurrect all apps" sleep 0.5 OUT=`$pm2 prettylist | grep -o "restart_time" | wc -l` diff --git a/test/bash/fork.sh b/test/bash/fork.sh index d1c7d20f..0b7f7cf6 100644 --- a/test/bash/fork.sh +++ b/test/bash/fork.sh @@ -25,7 +25,7 @@ $pm2 kill $pm2 start echo.coffee -x --interpreter coffee should 'should has forked app' 'fork_mode' 1 -### Dump resurect should be ok +### Dump resurrect should be ok $pm2 dump $pm2 kill