From 935afab9254c7f4dd19e0f830ced1940dd7dec8c Mon Sep 17 00:00:00 2001 From: vince Date: Wed, 21 Feb 2018 14:41:17 +0100 Subject: [PATCH 01/32] feature: add mjs support --- lib/Common.js | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/lib/Common.js b/lib/Common.js index 2dd053aab..a8eeec1ff 100644 --- a/lib/Common.js +++ b/lib/Common.js @@ -247,15 +247,17 @@ Common.prepareAppConf = function(opts, app) { * @param {string} filename * @return {mixed} null if not conf file, json or yaml if conf */ -Common.isConfigFile = function(filename) { - if (typeof(filename) != 'string') +Common.isConfigFile = function (filename) { + if (typeof (filename) !== 'string') return null; - if (filename.indexOf('.json') != -1) + if (filename.indexOf('.json') !== -1) return 'json'; if (filename.indexOf('.yml') > -1 || filename.indexOf('.yaml') > -1) return 'yaml'; - if (filename.indexOf('.config.js') != -1) + if (filename.indexOf('.config.js') !== -1) return 'js'; + if (filename.indexOf('.config.mjs') !== -1) + return 'mjs'; return null; }; @@ -288,7 +290,7 @@ Common.parseConfig = function(confObj, filename) { filename.indexOf('.yaml') > -1) { return yamljs.parse(confObj.toString()); } - else if (filename.indexOf('.config.js') > -1) { + else if (filename.indexOf('.config.js') > -1 || filename.indexOf('.config.mjs') > -1) { var confPath = require.resolve(path.resolve(filename)); delete require.cache[confPath]; return require(confPath); From 0908e9afb29e76ccdef3cffdbd6e1575e31915d9 Mon Sep 17 00:00:00 2001 From: vince Date: Wed, 21 Feb 2018 16:38:00 +0100 Subject: [PATCH 02/32] feature: make "pull", "backward" and "forward" works with id and pid --- lib/API/Version.js | 36 ++++++++++++++++++++---------------- lib/Client.js | 22 ++++++++++++++++++++++ 2 files changed, 42 insertions(+), 16 deletions(-) diff --git a/lib/API/Version.js b/lib/API/Version.js index 8f5bb93ba..9a20bb209 100644 --- a/lib/API/Version.js +++ b/lib/API/Version.js @@ -21,11 +21,11 @@ module.exports = function(CLI) { printOut(cst.PREFIX_MSG + 'Updating repository for process name %s', process_name); - that.Client.getProcessByName(process_name, function(err, processes) { + that.Client.getProcessByNameOrId(process_name, function (err, processes) { - if (processes.length === 0) { - printError('No processes with this name: %s', process_name); - return cb ? cb({msg:'Process not found: '+process_name}) : that.exitCli(cst.ERROR_EXIT); + if (err || processes.length === 0) { + printError('No processes with this name or id : %s', process_name); + return cb ? cb({msg: 'Process not found: ' + process_name}) : that.exitCli(cst.ERROR_EXIT); } var proc = processes[0]; @@ -82,11 +82,11 @@ module.exports = function(CLI) { printOut(cst.PREFIX_MSG + 'Updating repository for process name %s', process_name); - that.Client.getProcessByName(process_name, function(err, processes) { + that.Client.getProcessByNameOrId(process_name, function (err, processes) { - if (processes.length === 0) { - printError('No processes with this name: %s', process_name); - return cb ? cb({msg:'Process not found: ' + process_name}) : that.exitCli(cst.ERROR_EXIT); + if (err || processes.length === 0) { + printError('No processes with this name or id : %s', process_name); + return cb ? cb({msg: 'Process not found: ' + process_name}) : that.exitCli(cst.ERROR_EXIT); } var proc = processes[0]; @@ -138,14 +138,16 @@ module.exports = function(CLI) { var that = this; printOut(cst.PREFIX_MSG + 'Downgrading to previous commit repository for process name %s', process_name); - that.Client.getProcessByName(process_name, function(err, processes) { + that.Client.getProcessByNameOrId(process_name, function (err, processes) { - if (processes.length === 0) { - printError('No processes with this name: %s', process_name); - return cb ? cb({msg:'Process not found: '+process_name}) : that.exitCli(cst.ERROR_EXIT); + if (err || processes.length === 0) { + printError('No processes with this name or id : %s', process_name); + return cb ? cb({msg: 'Process not found: ' + process_name}) : that.exitCli(cst.ERROR_EXIT); } var proc = processes[0]; + // in case user searched by id/pid + process_name = proc.name; if (proc.pm2_env.versioning === undefined || proc.pm2_env.versioning === null) @@ -194,14 +196,16 @@ module.exports = function(CLI) { var that = this; printOut(cst.PREFIX_MSG + 'Updating to next commit repository for process name %s', process_name); - that.Client.getProcessByName(process_name, function(err, processes) { + that.Client.getProcessByNameOrId(process_name, function (err, processes) { - if (processes.length === 0) { - printError('No processes with this name: %s', process_name); - return cb ? cb({msg:'Process not found: '+process_name}) : that.exitCli(cst.ERROR_EXIT); + if (err || processes.length === 0) { + printError('No processes with this name or id: %s', process_name); + return cb ? cb({msg: 'Process not found: ' + process_name}) : that.exitCli(cst.ERROR_EXIT); } var proc = processes[0]; + // in case user searched by id/pid + process_name = proc.name; if (proc.pm2_env.versioning) { vizion.next({folder: proc.pm2_env.versioning.repo_path}, function(err, meta) { if (err !== null) diff --git a/lib/Client.js b/lib/Client.js index 74103fd3b..0b7855157 100644 --- a/lib/Client.js +++ b/lib/Client.js @@ -717,3 +717,25 @@ Client.prototype.getProcessByName = function(name, cb) { return cb(null, found_proc); }); }; + +Client.prototype.getProcessByNameOrId = function (nameOrId, cb) { + var foundProc = []; + + this.executeRemote('getMonitorData', {}, function (err, list) { + if (err) { + Common.printError('Error retrieving process list: ' + err); + return cb(err); + } + + list.forEach(function (proc) { + if (proc.pm2_env.name === nameOrId || + proc.pm2_env.pm_exec_path === path.resolve(nameOrId) || + proc.pid === parseInt(nameOrId) || + proc.pm2_env.pm_id === parseInt(nameOrId)) { + foundProc.push(proc); + } + }); + + return cb(null, foundProc); + }); +}; From 84796956347ca638750fe89cb5545e2a90a0f2c2 Mon Sep 17 00:00:00 2001 From: vince Date: Mon, 26 Feb 2018 14:11:14 +0100 Subject: [PATCH 03/32] chore: upgrade module version and engine version --- package.json | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/package.json b/package.json index 1855d6c34..bb5a31fe9 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "preferGlobal": true, "version": "2.10.1", "engines": { - "node": ">=0.12" + "node": ">=4.0.0" }, "directories": { "bin": "./bin", @@ -161,17 +161,19 @@ "dependencies": { "async": "^2.5", "blessed": "^0.1.81", - "chalk": "^1.1", - "chokidar": "^2", + "chalk": "^2.3.1", + "chokidar": "^2.0.2", "cli-table-redemption": "^1.0.0", - "commander": "2.13.0", + "coffee-script": "^1.12.7", + "commander": "2.14.1", "cron": "^1.3", "debug": "^3.0", - "eventemitter2": "1.0.5", + "eventemitter2": "5.0.1", "fclone": "1.0.11", + "livescript": "^1.5.0", "mkdirp": "0.5.1", "moment": "^2.19", - "needle": "^2.1.0", + "needle": "^2.2.0", "nssocket": "0.6.0", "pidusage": "^1.2.0", "pm2-axon": "3.1.0", @@ -179,9 +181,9 @@ "pm2-deploy": "^0.3.9", "pm2-multimeter": "^0.1.2", "pmx": "^1.6", - "promptly": "2.2.0", + "promptly": "3.0.3", "semver": "^5.3", - "shelljs": "0.7.8", + "shelljs": "0.8.1", "source-map-support": "^0.5", "sprintf-js": "1.1.1", "v8-compile-cache": "^1.1.0", @@ -190,7 +192,7 @@ }, "devDependencies": { "mocha": "^3.5", - "should": "^11" + "should": "^13" }, "optionalDependencies": { "gkt": "https://tgz.pm2.io/gkt-1.0.0.tgz" From 1e0017325fc8cf658263fb4e02c7bf8912f422b3 Mon Sep 17 00:00:00 2001 From: vince Date: Mon, 26 Feb 2018 14:45:34 +0100 Subject: [PATCH 04/32] fix: #3456 use homedir() instead of process.env.HOME, make module installation work on windows --- lib/API/Modules/Modularizer.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/API/Modules/Modularizer.js b/lib/API/Modules/Modularizer.js index 7093324bf..e738255ed 100644 --- a/lib/API/Modules/Modularizer.js +++ b/lib/API/Modules/Modularizer.js @@ -6,6 +6,7 @@ var shelljs = require('shelljs'); var path = require('path'); var fs = require('fs'); +var os = require('os'); var async = require('async'); var p = path; var readline = require('readline'); @@ -185,7 +186,7 @@ Modularizer.installModule = function(CLI, module_name, opts, cb) { var install_path = path.join(cst.DEFAULT_MODULE_PATH, canonic_module_name); mkdirp(install_path, function() { - process.chdir(process.env.HOME); + process.chdir(os.homedir()); var install_instance = spawn(cst.IS_WINDOWS ? 'npm.cmd' : 'npm', ['install', module_name, '--loglevel=error', '--prefix', install_path ], { stdio : 'inherit', From 417fc19b2055313831ec0a515eb8ccd061ef8426 Mon Sep 17 00:00:00 2001 From: Robin Monnier Date: Mon, 26 Feb 2018 15:15:23 +0100 Subject: [PATCH 05/32] documentation for ecosystem file --- lib/API/schema.json | 285 +++++++++++++++++++++++++++----------------- 1 file changed, 177 insertions(+), 108 deletions(-) diff --git a/lib/API/schema.json b/lib/API/schema.json index 97400a3b7..3f1847eab 100644 --- a/lib/API/schema.json +++ b/lib/API/schema.json @@ -1,24 +1,89 @@ { "script": { "type": "string", + "default": "Required", "require": true, - "alias" : "exec" + "alias" : "exec", + "description": "Path of the script to launch" + }, + "name": { + "type": "string", + "default": "Script filename without extension (app for app.js)", + "description": "Process name in the process list" + }, + "cwd": { + "type": "string", + "default": "CWD of the current environment (ie from your shell)", + "description": "Current working directory to start the process with" }, "args": { "type": [ "array", "string" - ] + ], + "description": "Arguments to pass to the script" + }, + "interpreter": { + "type": "string", + "alias": "exec_interpreter", + "default": "node", + "description": "Interpreter absolute path" }, "node_args": { "type": [ "array", "string" ], - "alias": ["interpreterArgs", "interpreter_args"] + "alias": ["interpreter_args"], + "description": "Arguments to pass to the interpreter" }, - "name": { - "type": "string" + "output": { + "type": "string", + "alias": ["out", "out_file", "out_log"], + "default": "~/.pm2/logs/-out.log", + "description": "File path for stdout (each line is appended to this file)" + }, + "error": { + "type": "string", + "alias": ["error_file", "err", "err_file", "err_log"], + "default": "~/.pm2/logs/-error.err", + "description": "File path for stderr (each line is appended to this file)" + }, + "log": { + "type": [ + "boolean", + "string" + ], + "default": "/dev/null", + "alias": "log_file", + "description": "File path for combined stdout and stderr" + }, + "disable_logs": { + "type": "boolean", + "default": false, + "description": "Disable all logs storage" + }, + "log_type": { + "type": "string", + "description": "Define a specific log output type, possible values: json|" + }, + "log_date_format": { + "type": "string", + "description": "Format for log timestamps (eg YYYY-MM-DD HH:mm Z) in moment.js format" + }, + "env": { + "type": [ + "object", + "string" + ], + "description": "Specify environment variables to be injected in your app" + }, + "^env_\\S*$": { + "type": [ + "object", + "string" + ], + "description": "Specify environment variables to be injected when using --env " }, "max_memory_restart": { "type": [ @@ -27,124 +92,89 @@ ], "regex": "^\\d+(G|M|K)?$", "ext_type": "sbyte", - "desc": "it should be a NUMBER - byte, \"[NUMBER]G\"(Gigabyte), \"[NUMBER]M\"(Megabyte) or \"[NUMBER]K\"(Kilobyte)" - }, - "uid" : { - "type" : "string" + "desc": "it should be a NUMBER - byte, \"[NUMBER]G\"(Gigabyte), \"[NUMBER]M\"(Megabyte) or \"[NUMBER]K\"(Kilobyte)", + "description": "Restart the app if an amount of memory,is exceeded. Uses human-friendly format: 'K' for kilobytes, 'M' for megabytes, 'G' for gigabytes', etc. Eg 150M" }, - "gid" : { - "type" : "string" + "pid_file": { + "type": "string", + "alias": "pid", + "default": "~/.pm2/pids/app_name-id.pid", + "description": "File path where the pid of the started process is written by pm2" }, "restart_delay": { - "type" : "number" + "type" : "number", + "default": 0, + "description": "Time in ms to wait before restarting a crashing app" }, - "source_map_support" : { - "type": "boolean" + "source_map_support": { + "type": "boolean", + "default": true, + "description": "Enable or disable the source map support" }, - "wait_ready" : { - "type": "boolean" + "disable_source_map_support": { + "type": "boolean", + "default": false, + "description": "Enable or disable the source map support" }, - "disable_source_map_support" : { - "type": "boolean" + "wait_ready": { + "type": "boolean", + "default": false, + "description": "Make the process wait for process.send('ready')" }, "instances": { - "type": "number" + "type": "number", + "default": 1, + "description": "Number of instances to be started in cluster mode" }, "kill_timeout": { - "type": "number" + "type": "number", + "default": 1600, + "description": "Time in ms before sending the final SIGKILL signal after SIGINT" }, "listen_timeout": { - "type": "number" - }, - "port": { - "type": "number" - }, - "log_file": { - "type": [ - "boolean", - "string" - ], - "alias": "log" - }, - "error_file": { - "type": "string", - "alias": ["error", "err", "err_file", "err_log"] - }, - "log_type": { - "type": "string" - }, - "out_file": { - "type": "string", - "alias": ["output", "out", "out_log"] - }, - "pid_file": { - "type": "string", - "alias": "pid" + "type": "number", + "description": "Time in ms before forcing a reload if app is still not listening/has still note sent ready" }, "cron_restart": { "type": "string", - "alias": "cron" - }, - "cwd": { - "type": "string" + "alias": "cron", + "description": "A cron pattern to restart your app" }, "merge_logs": { "type": "boolean", - "alias" : "combine_logs" - }, - "vizion" : { - "type": "boolean", - "default" : true - }, - "pmx" : { - "type": "boolean", - "default" : true + "alias" : "combine_logs", + "default": false, + "description": "In cluster mode, merge each type of logs into a single file (instead of having one for each cluster)" }, - "automation" : { + "vizion": { "type": "boolean", - "default" : true + "default" : true, + "description": "Enable or disable the versioning control metadatas (vizion library)" }, - "autorestart" : { + "autorestart": { "type": "boolean", - "default" : true - }, - "treekill" : { - "type": "boolean", - "default" : true + "default" : true, + "description": "pm2 will not attempt to restart it following successful completion or process failure" }, "watch": { "type": [ "boolean", "array", "string" - ] + ], + "default": false, + "description": "Enable or disable the watch mode" }, "ignore_watch": { "type": [ "array", "string" - ] + ], + "description": "List of path to ignore (regex)" }, "watch_options": { - "type": "object" - }, - "env": { - "type": [ - "object", - "string" - ] - }, - "^env_\\S*$": { - "type": [ - "object", - "string" - ] - }, - "disable_logs" : { - "type": "boolean" - }, - "log_date_format": { - "type": "string" + "type": "object", + "description": "Object that will be used as an options with chokidar (refer to chokidar documentation)" }, "min_uptime": { "type": [ @@ -154,49 +184,88 @@ "regex": "^\\d+(h|m|s)?$", "desc": "it should be a NUMBER - milliseconds, \"[NUMBER]h\"(hours), \"[NUMBER]m\"(minutes) or \"[NUMBER]s\"(seconds)", "min": 100, - "ext_type": "stime" + "ext_type": "stime", + "default": 1000, + "description": "Minimum uptime of the app to be considered started (format is [0-9]+(h|m|s)?, for hours, minutes, seconds, default to ms)" }, "max_restarts": { "type": "number", - "min": 0 + "min": 0, + "default": 16, + "description": "Number of times a script is restarted when it exits in less than min_uptime" }, "exec_mode": { "type": "string", "regex": "^(cluster|fork)(_mode)?$", "alias": "executeCommand", - "desc": "it should be \"cluster\"(\"cluster_mode\") or \"fork\"(\"fork_mode\") only" - }, - "exec_interpreter": { - "type": "string", - "alias": "interpreter" - }, - "write": { - "type": "boolean" + "desc": "it should be \"cluster\"(\"cluster_mode\") or \"fork\"(\"fork_mode\") only", + "default": "fork", + "description": "Set the execution mode, possible values: fork|cluster" }, "force": { - "type": "boolean" + "type": "boolean", + "default": false, + "description": "pm2 will start a script even if it is already running (the script path is considered, not the name)" }, "append_env_to_name": { - "type": "boolean" + "type": "boolean", + "default": false, + "description": "Append the environment name to the app name" }, "post_update": { - "type": "array" + "type": "array", + "description": "List of commands executed after a pull/upgrade operation performed from Keymetrics dashboard" }, - "disable_trace": { + "trace": { "type": [ "boolean" - ] + ], + "default": false, + "description": "Enable or disable the transaction tracing" }, - "trace": { + "disable_trace": { "type": [ "boolean" - ] + ], + "default": true, + "description": "Enable or disable the transaction tracing" }, "increment_var": { - "type": "string" + "type": "string", + "description": "Specify the name of an environnement variable to inject which increments for each cluster" }, "instance_var": { "type": "string", - "default" : "NODE_APP_INSTANCE" + "default" : "NODE_APP_INSTANCE", + "description": "Rename the NODE_APP_INSTANCE environment variable" + }, + "pmx": { + "type": "boolean", + "default" : true, + "description": "Enable or disable the pmx injection" + }, + "automation": { + "type": "boolean", + "default" : true, + "description": "See --no-automation flag" + }, + "treekill": { + "type": "boolean", + "default" : true, + "description": "See --no-treekill flag" + }, + "port": { + "type": "number", + "description": "Shortcut to inject a PORT environment variable" + }, + "uid": { + "type" : "string", + "default": "Current user uid", + "description": "Set user id" + }, + "gid": { + "type" : "string", + "default": "Current user gid", + "description": "Set group id" } -} +} \ No newline at end of file From 13d6565c72e3596d05f87bfc8be15d3ee45fb279 Mon Sep 17 00:00:00 2001 From: vince Date: Mon, 26 Feb 2018 15:54:14 +0100 Subject: [PATCH 06/32] chore: remove coffee and livescript dependencies --- package.json | 2 -- 1 file changed, 2 deletions(-) diff --git a/package.json b/package.json index bb5a31fe9..14a819c32 100644 --- a/package.json +++ b/package.json @@ -164,13 +164,11 @@ "chalk": "^2.3.1", "chokidar": "^2.0.2", "cli-table-redemption": "^1.0.0", - "coffee-script": "^1.12.7", "commander": "2.14.1", "cron": "^1.3", "debug": "^3.0", "eventemitter2": "5.0.1", "fclone": "1.0.11", - "livescript": "^1.5.0", "mkdirp": "0.5.1", "moment": "^2.19", "needle": "^2.2.0", From 074a7a407a31b4d88442f5834d253d62f4e543b8 Mon Sep 17 00:00:00 2001 From: vince Date: Mon, 26 Feb 2018 16:02:10 +0100 Subject: [PATCH 07/32] chore: downgrade promptly --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 14a819c32..3bd207d6d 100644 --- a/package.json +++ b/package.json @@ -179,7 +179,7 @@ "pm2-deploy": "^0.3.9", "pm2-multimeter": "^0.1.2", "pmx": "^1.6", - "promptly": "3.0.3", + "promptly": "2.2.0", "semver": "^5.3", "shelljs": "0.8.1", "source-map-support": "^0.5", From 1650ec7f91f6f5102f80b7c34210b899841a4402 Mon Sep 17 00:00:00 2001 From: Robin Monnier Date: Mon, 26 Feb 2018 16:22:58 +0100 Subject: [PATCH 08/32] documentation for ecosystem file --- lib/API/schema.json | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/lib/API/schema.json b/lib/API/schema.json index 3f1847eab..22fbacace 100644 --- a/lib/API/schema.json +++ b/lib/API/schema.json @@ -1,19 +1,18 @@ { "script": { "type": "string", - "default": "Required", "require": true, "alias" : "exec", - "description": "Path of the script to launch" + "description": "Path of the script to launch, required field" }, "name": { "type": "string", - "default": "Script filename without extension (app for app.js)", + "default": "Script filename without the extension (app for app.js)", "description": "Process name in the process list" }, "cwd": { "type": "string", - "default": "CWD of the current environment (ie from your shell)", + "default": "CWD of the current environment (from your shell)", "description": "Current working directory to start the process with" }, "args": { @@ -56,7 +55,7 @@ ], "default": "/dev/null", "alias": "log_file", - "description": "File path for combined stdout and stderr" + "description": "File path for combined stdout and stderr (each line is appended to this file)" }, "disable_logs": { "type": "boolean", @@ -76,7 +75,7 @@ "object", "string" ], - "description": "Specify environment variables to be injected in your app" + "description": "Specify environment variables to be injected" }, "^env_\\S*$": { "type": [ @@ -93,7 +92,7 @@ "regex": "^\\d+(G|M|K)?$", "ext_type": "sbyte", "desc": "it should be a NUMBER - byte, \"[NUMBER]G\"(Gigabyte), \"[NUMBER]M\"(Megabyte) or \"[NUMBER]K\"(Kilobyte)", - "description": "Restart the app if an amount of memory,is exceeded. Uses human-friendly format: 'K' for kilobytes, 'M' for megabytes, 'G' for gigabytes', etc. Eg 150M" + "description": "Restart the app if an amount of memory is exceeded (format: 'K|G|' for KB, 'M' for MB, 'G' for GB, default to byte)" }, "pid_file": { "type": "string", @@ -119,7 +118,7 @@ "wait_ready": { "type": "boolean", "default": false, - "description": "Make the process wait for process.send('ready')" + "description": "Make the process wait for a process.send('ready')" }, "instances": { "type": "number", @@ -149,12 +148,12 @@ "vizion": { "type": "boolean", "default" : true, - "description": "Enable or disable the versioning control metadatas (vizion library)" + "description": "Enable or disable the versioning metadatas (vizion library)" }, "autorestart": { "type": "boolean", "default" : true, - "description": "pm2 will not attempt to restart it following successful completion or process failure" + "description": "Enable or disable auto restart after process failure" }, "watch": { "type": [ @@ -170,7 +169,7 @@ "array", "string" ], - "description": "List of path to ignore (regex)" + "description": "List of paths to ignore (regex)" }, "watch_options": { "type": "object", @@ -205,7 +204,7 @@ "force": { "type": "boolean", "default": false, - "description": "pm2 will start a script even if it is already running (the script path is considered, not the name)" + "description": "Start a script even if it is already running (only the script path is considered)" }, "append_env_to_name": { "type": "boolean", @@ -242,17 +241,17 @@ "pmx": { "type": "boolean", "default" : true, - "description": "Enable or disable the pmx injection" + "description": "Enable or disable pmx wrapping" }, "automation": { "type": "boolean", "default" : true, - "description": "See --no-automation flag" + "description": "Enable or disable pmx wrapping" }, "treekill": { "type": "boolean", "default" : true, - "description": "See --no-treekill flag" + "description": "Only kill the main process, not detached children" }, "port": { "type": "number", From beb6e48787c39c66569141d0fd8d090736114d23 Mon Sep 17 00:00:00 2001 From: vince Date: Mon, 26 Feb 2018 16:23:34 +0100 Subject: [PATCH 09/32] chore: drop 0.12 test on travis --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index d76fc89cc..25b0a3678 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,6 @@ node_js: - "4" - "6" - "8" - - "0.12" os: - linux before_install: From e3831f95c8d71f98e8840da37f7e883727eccd59 Mon Sep 17 00:00:00 2001 From: vince Date: Tue, 27 Feb 2018 10:52:12 +0100 Subject: [PATCH 10/32] refactor: transform API into class --- lib/API.js | 141 +++++++++++++++++++++++++++-------------------------- 1 file changed, 73 insertions(+), 68 deletions(-) diff --git a/lib/API.js b/lib/API.js index 59bc227f1..8e7e00869 100644 --- a/lib/API.js +++ b/lib/API.js @@ -45,85 +45,88 @@ var IMMUTABLE_MSG = chalk.bold.blue('Use --update-env to update environment vari * @param {String} [opts.secret_key=null] keymetrics bucket secret key * @param {String} [opts.machine_name=null] keymetrics instance name */ -var API = module.exports = function(opts) { - if (!opts) opts = {}; - var that = this; +class API { - this.daemon_mode = typeof(opts.daemon_mode) == 'undefined' ? true : opts.daemon_mode; - this.pm2_home = conf.PM2_ROOT_PATH; - this.public_key = process.env.KEYMETRICS_SECRET || opts.public_key || null; - this.secret_key = process.env.KEYMETRICS_PUBLIC || opts.secret_key || null; - this.machine_name = process.env.INSTANCE_NAME || opts.machine_name || null + constructor (opts) { + if (!opts) opts = {}; + var that = this; - /** - * CWD resolution - */ - this.cwd = process.cwd(); - if (opts.cwd) { - this.cwd = path.resolve(opts.cwd); - } + this.daemon_mode = typeof(opts.daemon_mode) == 'undefined' ? true : opts.daemon_mode; + this.pm2_home = conf.PM2_ROOT_PATH; + this.public_key = process.env.KEYMETRICS_SECRET || opts.public_key || null; + this.secret_key = process.env.KEYMETRICS_PUBLIC || opts.secret_key || null; + this.machine_name = process.env.INSTANCE_NAME || opts.machine_name || null - /** - * PM2 HOME resolution - */ - if (opts.pm2_home && opts.independent == true) - throw new Error('You cannot set a pm2_home and independent instance in same time'); + /** + * CWD resolution + */ + this.cwd = process.cwd(); + if (opts.cwd) { + this.cwd = path.resolve(opts.cwd); + } - if (opts.pm2_home) { - // Override default conf file - this.pm2_home = opts.pm2_home; - conf = util._extend(conf, path_structure(this.pm2_home)); - } - else if (opts.independent == true && conf.IS_WINDOWS === false) { - // Create an unique pm2 instance - var crypto = require('crypto'); - var random_file = crypto.randomBytes(8).toString('hex'); - this.pm2_home = path.join('/tmp', random_file); - - // If we dont explicitly tell to have a daemon - // It will go as in proc - if (typeof(opts.daemon_mode) == 'undefined') - this.daemon_mode = false; - conf = util._extend(conf, path_structure(this.pm2_home)); - } + /** + * PM2 HOME resolution + */ + if (opts.pm2_home && opts.independent == true) + throw new Error('You cannot set a pm2_home and independent instance in same time'); + + if (opts.pm2_home) { + // Override default conf file + this.pm2_home = opts.pm2_home; + conf = util._extend(conf, path_structure(this.pm2_home)); + } + else if (opts.independent == true && conf.IS_WINDOWS === false) { + // Create an unique pm2 instance + var crypto = require('crypto'); + var random_file = crypto.randomBytes(8).toString('hex'); + this.pm2_home = path.join('/tmp', random_file); + + // If we dont explicitly tell to have a daemon + // It will go as in proc + if (typeof(opts.daemon_mode) == 'undefined') + this.daemon_mode = false; + conf = util._extend(conf, path_structure(this.pm2_home)); + } - this._conf = conf; + this._conf = conf; - if (conf.IS_WINDOWS) { - // Weird fix, may need to be dropped - // @todo windows connoisseur double check - if (process.stdout._handle && process.stdout._handle.setBlocking) - process.stdout._handle.setBlocking(true); - } + if (conf.IS_WINDOWS) { + // Weird fix, may need to be dropped + // @todo windows connoisseur double check + if (process.stdout._handle && process.stdout._handle.setBlocking) + process.stdout._handle.setBlocking(true); + } - this.Client = new Client({ - pm2_home : that.pm2_home, - conf : this._conf, - secret_key : this.secret_key, - public_key : this.public_key, - daemon_mode : this.daemon_mode, - machine_name : this.machine_name - }); + this.Client = new Client({ + pm2_home: that.pm2_home, + conf: this._conf, + secret_key: this.secret_key, + public_key: this.public_key, + daemon_mode: this.daemon_mode, + machine_name: this.machine_name + }); - this.gl_interact_infos = null; - this.gl_is_km_linked = false; + this.gl_interact_infos = null; + this.gl_is_km_linked = false; - try { - var pid = fs.readFileSync(conf.INTERACTOR_PID_PATH); - pid = parseInt(pid.toString().trim()); - process.kill(pid, 0); - that.gl_is_km_linked = true; - } catch(e) { - that.gl_is_km_linked = false; - } + try { + var pid = fs.readFileSync(conf.INTERACTOR_PID_PATH); + pid = parseInt(pid.toString().trim()); + process.kill(pid, 0); + that.gl_is_km_linked = true; + } catch (e) { + that.gl_is_km_linked = false; + } - // For testing purposes - if (this.secret_key && process.env.NODE_ENV == 'local_test') - that.gl_is_km_linked = true; + // For testing purposes + if (this.secret_key && process.env.NODE_ENV == 'local_test') + that.gl_is_km_linked = true; - KMDaemon.getInteractInfo(this._conf, function(i_err, interact) { - that.gl_interact_infos = interact; - }); + KMDaemon.getInteractInfo(this._conf, function (i_err, interact) { + that.gl_interact_infos = interact; + }); + } }; @@ -1685,3 +1688,5 @@ API.prototype.deepUpdate = function(cb) { cb ? cb(null, {success:true}) : that.exitCli(conf.SUCCESS_EXIT); }); }; + +module.exports = API; From 9552bd61b72692beb620a91765ad440cdf6abefe Mon Sep 17 00:00:00 2001 From: vince Date: Tue, 27 Feb 2018 11:25:32 +0100 Subject: [PATCH 11/32] refactor: remove prototype from API and create method --- lib/API.js | 2542 ++++++++++++++++++++++++++-------------------------- 1 file changed, 1273 insertions(+), 1269 deletions(-) diff --git a/lib/API.js b/lib/API.js index 8e7e00869..c42cf8370 100644 --- a/lib/API.js +++ b/lib/API.js @@ -126,288 +126,274 @@ class API { KMDaemon.getInteractInfo(this._conf, function (i_err, interact) { that.gl_interact_infos = interact; }); + + this.gl_retry = 0; } -}; + /** + * Connect to PM2 + * Calling this command is now optional + * + * @param {Function} cb callback once pm2 is ready for commands + */ + connect (noDaemon, cb) { + var that = this; + this.start_timer = new Date(); + + if (typeof(cb) == 'undefined') { + cb = noDaemon; + noDaemon = false; + } else if (noDaemon === true) { + // Backward compatibility with PM2 1.x + this.Client.daemon_mode = false; + this.daemon_mode = false; + } -////////////////////////// -// Load all API methods // -////////////////////////// + this.Client.start(function(err, meta) { + if (err) + return cb(err); -require('./API/Extra.js')(API); -require('./API/Interaction.js')(API); -require('./API/Deploy.js')(API); -require('./API/Modules/Modules.js')(API); -require('./API/Keymetrics/cli-api.js')(API); -require('./API/Configuration.js')(API); -require('./API/Version.js')(API); -require('./API/Startup.js')(API); -require('./API/LogManagement.js')(API); -require('./API/Containerizer.js')(API); + if (meta.new_pm2_instance == false && that.daemon_mode === true) + return cb(err, meta); -/** - * Connect to PM2 - * Calling this command is now optional - * - * @param {Function} cb callback once pm2 is ready for commands - */ -API.prototype.connect = function(noDaemon, cb) { - var that = this; - this.start_timer = new Date(); - - if (typeof(cb) == 'undefined') { - cb = noDaemon; - noDaemon = false; - } else if (noDaemon === true) { - // Backward compatibility with PM2 1.x - this.Client.daemon_mode = false; - this.daemon_mode = false; + // If new pm2 instance has been popped + // Lauch all modules + Modularizer.launchAll(that, function(err_mod) { + return cb(err, meta); + }); + }); } - this.Client.start(function(err, meta) { - if (err) - return cb(err); - - if (meta.new_pm2_instance == false && that.daemon_mode === true) - return cb(err, meta); - - // If new pm2 instance has been popped - // Lauch all modules - Modularizer.launchAll(that, function(err_mod) { - return cb(err, meta); - }); - }); -} + /** + * Usefull when custom PM2 created with independent flag set to true + * This will cleanup the newly created instance + * by removing folder, killing PM2 and so on + * + * @param {Function} cb callback once cleanup is successfull + */ + destroy (cb) { + var exec = require('shelljs').exec; + var that = this; -/** - * Usefull when custom PM2 created with independent flag set to true - * This will cleanup the newly created instance - * by removing folder, killing PM2 and so on - * - * @param {Function} cb callback once cleanup is successfull - */ -API.prototype.destroy = function(cb) { - var exec = require('shelljs').exec; - var that = this; + debug('Killing and deleting current deamon'); - debug('Killing and deleting current deamon'); + this.killDaemon(function() { + var cmd = 'rm -rf ' + that.pm2_home; + var test_path = path.join(that.pm2_home, 'module_conf.json'); + var test_path_2 = path.join(that.pm2_home, 'pm2.pid'); - this.killDaemon(function() { - var cmd = 'rm -rf ' + that.pm2_home; - var test_path = path.join(that.pm2_home, 'module_conf.json'); - var test_path_2 = path.join(that.pm2_home, 'pm2.pid'); + if (that.pm2_home.indexOf('.pm2') > -1) + return cb(new Error('Destroy is not a allowed method on .pm2')); - if (that.pm2_home.indexOf('.pm2') > -1) - return cb(new Error('Destroy is not a allowed method on .pm2')); + if (fs.accessSync) { + fs.access(test_path, fs.R_OK, function(err) { + if (err) return cb(err); + debug('Deleting temporary folder %s', that.pm2_home); + exec(cmd, cb); + }); + return false; + } - if (fs.accessSync) { - fs.access(test_path, fs.R_OK, function(err) { - if (err) return cb(err); - debug('Deleting temporary folder %s', that.pm2_home); - exec(cmd, cb); + // Support for Node 0.10 + fs.exists(test_path, function(exist) { + if (exist) { + debug('Deleting temporary folder %s', that.pm2_home); + exec(cmd, cb); + } + return cb(null); }); - return false; - } - - // Support for Node 0.10 - fs.exists(test_path, function(exist) { - if (exist) { - debug('Deleting temporary folder %s', that.pm2_home); - exec(cmd, cb); - } - return cb(null); }); - }); -}; + } -/** - * Disconnect from PM2 instance - * This will allow your software to exit by itself - * - * @param {Function} [cb] optional callback once connection closed - */ -API.prototype.disconnect = API.prototype.close = function(cb) { - var that = this; + /** + * Disconnect from PM2 instance + * This will allow your software to exit by itself + * + * @param {Function} [cb] optional callback once connection closed + */ + disconnect (cb) { + var that = this; - if (!cb) cb = function() {}; + if (!cb) cb = function() {}; - this.Client.close(function(err, data) { - debug('The session lasted %ds', (new Date() - that.start_timer) / 1000); - return cb(err, data); - }); -}; + this.Client.close(function(err, data) { + debug('The session lasted %ds', (new Date() - that.start_timer) / 1000); + return cb(err, data); + }); + }; -/** - * Launch modules - * - * @param {Function} cb callback once pm2 has launched modules - */ -API.prototype.launchModules = function(cb) { - Modularizer.launchAll(this, cb); -}; + /** + * Launch modules + * + * @param {Function} cb callback once pm2 has launched modules + */ + launchModules (cb) { + Modularizer.launchAll(this, cb); + } -/** - * Enable bus allowing to retrieve various process event - * like logs, restarts, reloads - * - * @param {Function} cb callback called with 1st param err and 2nb param the bus - */ -API.prototype.launchBus = function(cb) { - this.Client.launchBus(cb); -}; + /** + * Enable bus allowing to retrieve various process event + * like logs, restarts, reloads + * + * @param {Function} cb callback called with 1st param err and 2nb param the bus + */ + launchBus (cb) { + this.Client.launchBus(cb); + } -/** - * Exit methods for API - * @param {Integer} code exit code for terminal - */ -API.prototype.exitCli = function(code) { - var that = this; - - // Do nothing if PM2 called programmatically (also in speedlist) - if (conf.PM2_PROGRAMMATIC && process.env.PM2_USAGE != 'CLI') return false; - - KMDaemon.disconnectRPC(function() { - that.Client.close(function() { - code = code || 0; - // Safe exits process after all streams are drained. - // file descriptor flag. - var fds = 0; - // exits process when stdout (1) and sdterr(2) are both drained. - function tryToExit() { - if ((fds & 1) && (fds & 2)) { - debug('This command took %ds to execute', (new Date() - that.start_timer) / 1000); - process.exit(code); + /** + * Exit methods for API + * @param {Integer} code exit code for terminal + */ + exitCli (code) { + var that = this; + + // Do nothing if PM2 called programmatically (also in speedlist) + if (conf.PM2_PROGRAMMATIC && process.env.PM2_USAGE != 'CLI') return false; + + KMDaemon.disconnectRPC(function() { + that.Client.close(function() { + code = code || 0; + // Safe exits process after all streams are drained. + // file descriptor flag. + var fds = 0; + // exits process when stdout (1) and sdterr(2) are both drained. + function tryToExit() { + if ((fds & 1) && (fds & 2)) { + debug('This command took %ds to execute', (new Date() - that.start_timer) / 1000); + process.exit(code); + } } - } - [process.stdout, process.stderr].forEach(function(std) { - var fd = std.fd; - if (!std.bufferSize) { - // bufferSize equals 0 means current stream is drained. - fds = fds | fd; - } else { - // Appends nothing to the std queue, but will trigger `tryToExit` event on `drain`. - std.write && std.write('', function() { + [process.stdout, process.stderr].forEach(function(std) { + var fd = std.fd; + if (!std.bufferSize) { + // bufferSize equals 0 means current stream is drained. fds = fds | fd; - tryToExit(); - }); - } - // Does not write anything more. - delete std.write; + } else { + // Appends nothing to the std queue, but will trigger `tryToExit` event on `drain`. + std.write && std.write('', function() { + fds = fds | fd; + tryToExit(); + }); + } + // Does not write anything more. + delete std.write; + }); + tryToExit(); }); - tryToExit(); }); - }); -}; + } //////////////////////////// // Application management // //////////////////////////// -/** - * Start a file or json with configuration - * @param {Object||String} cmd script to start or json - * @param {Function} cb called when application has been started - */ -API.prototype.start = function(cmd, opts, cb) { - if (typeof(opts) == "function") { - cb = opts; - opts = {}; - } - if (!opts) - opts = {}; + /** + * Start a file or json with configuration + * @param {Object||String} cmd script to start or json + * @param {Function} cb called when application has been started + */ + start (cmd, opts, cb) { + if (typeof(opts) == "function") { + cb = opts; + opts = {}; + } + if (!opts) + opts = {}; - var that = this; + var that = this; - if (util.isArray(opts.watch) && opts.watch.length === 0) - opts.watch = (opts.rawArgs ? !!~opts.rawArgs.indexOf('--watch') : !!~process.argv.indexOf('--watch')) || false; + if (util.isArray(opts.watch) && opts.watch.length === 0) + opts.watch = (opts.rawArgs ? !!~opts.rawArgs.indexOf('--watch') : !!~process.argv.indexOf('--watch')) || false; - if (Common.isConfigFile(cmd) || (typeof(cmd) === 'object')) - that._startJson(cmd, opts, 'restartProcessId', cb); - else { - that._startScript(cmd, opts, cb); + if (Common.isConfigFile(cmd) || (typeof(cmd) === 'object')) + that._startJson(cmd, opts, 'restartProcessId', cb); + else { + that._startScript(cmd, opts, cb); + } } -}; -/** - * Reset process counters - * - * @method resetMetaProcess - */ -API.prototype.reset = function(process_name, cb) { - var that = this; - - function processIds(ids, cb) { - async.eachLimit(ids, conf.CONCURRENT_ACTIONS, function(id, next) { - that.Client.executeRemote('resetMetaProcessId', id, function(err, res) { - if (err) console.error(err); - Common.printOut(conf.PREFIX_MSG + 'Resetting meta for process id %d', id); - return next(); + /** + * Reset process counters + * + * @method resetMetaProcess + */ + reset (process_name, cb) { + var that = this; + + function processIds(ids, cb) { + async.eachLimit(ids, conf.CONCURRENT_ACTIONS, function(id, next) { + that.Client.executeRemote('resetMetaProcessId', id, function(err, res) { + if (err) console.error(err); + Common.printOut(conf.PREFIX_MSG + 'Resetting meta for process id %d', id); + return next(); + }); + }, function(err) { + if (err) return cb(Common.retErr(err)); + return cb ? cb(null, {success:true}) : that.speedList(); }); - }, function(err) { - if (err) return cb(Common.retErr(err)); - return cb ? cb(null, {success:true}) : that.speedList(); - }); - } + } - if (process_name == 'all') { - that.Client.getAllProcessId(function(err, ids) { - if (err) { - Common.printError(err); - return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT); - } - return processIds(ids, cb); - }); - } - else if (isNaN(process_name)) { - that.Client.getProcessIdByName(process_name, function(err, ids) { - if (err) { - Common.printError(err); - return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT); - } - if (ids.length === 0) { - Common.printError('Unknown process name'); - return cb ? cb(new Error('Unknown process name')) : that.exitCli(conf.ERROR_EXIT); - } - return processIds(ids, cb); - }); - } else { - processIds([process_name], cb); + if (process_name == 'all') { + that.Client.getAllProcessId(function(err, ids) { + if (err) { + Common.printError(err); + return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT); + } + return processIds(ids, cb); + }); + } + else if (isNaN(process_name)) { + that.Client.getProcessIdByName(process_name, function(err, ids) { + if (err) { + Common.printError(err); + return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT); + } + if (ids.length === 0) { + Common.printError('Unknown process name'); + return cb ? cb(new Error('Unknown process name')) : that.exitCli(conf.ERROR_EXIT); + } + return processIds(ids, cb); + }); + } else { + processIds([process_name], cb); + } } -}; -/** - * Update daemonized PM2 Daemon - * - * @param {Function} cb callback when pm2 has been upgraded - */ -API.prototype.update = function(cb) { - var that = this; + /** + * Update daemonized PM2 Daemon + * + * @param {Function} cb callback when pm2 has been upgraded + */ + update (cb) { + var that = this; - Common.printOut('Be sure to have the latest version by doing `npm install pm2@latest -g` before doing this procedure.'); + Common.printOut('Be sure to have the latest version by doing `npm install pm2@latest -g` before doing this procedure.'); - // Dump PM2 processes - that.Client.executeRemote('notifyKillPM2', {}, function() {}); + // Dump PM2 processes + that.Client.executeRemote('notifyKillPM2', {}, function() {}); - that.getVersion(function(err, new_version) { - // If not linked to keymetrics, and update pm2 to latest, display motd.update - if (!that.gl_is_km_linked && !err && (pkg.version != new_version)) { - var dt = fs.readFileSync(path.join(__dirname, that._conf.KEYMETRICS_UPDATE)); - console.log(dt.toString()); - } + that.getVersion(function(err, new_version) { + // If not linked to keymetrics, and update pm2 to latest, display motd.update + if (!that.gl_is_km_linked && !err && (pkg.version != new_version)) { + var dt = fs.readFileSync(path.join(__dirname, that._conf.KEYMETRICS_UPDATE)); + console.log(dt.toString()); + } - that.dump(function(err) { - debug('Dumping successfull', err); - that.killDaemon(function() { - debug('------------------ Everything killed', arguments); - that.Client.launchDaemon({interactor:false}, function(err, child) { - that.Client.launchRPC(function() { - that.resurrect(function() { - Common.printOut(chalk.blue.bold('>>>>>>>>>> PM2 updated')); - Modularizer.launchAll(that, function() { - KMDaemon.launchAndInteract(that._conf, null, function(err, data, interactor_proc) { - // Interactor error can be skipped here - return cb ? cb(null, {success:true}) : that.speedList(); + that.dump(function(err) { + debug('Dumping successfull', err); + that.killDaemon(function() { + debug('------------------ Everything killed', arguments); + that.Client.launchDaemon({interactor:false}, function(err, child) { + that.Client.launchRPC(function() { + that.resurrect(function() { + Common.printOut(chalk.blue.bold('>>>>>>>>>> PM2 updated')); + Modularizer.launchAll(that, function() { + KMDaemon.launchAndInteract(that._conf, null, function(err, data, interactor_proc) { + // Interactor error can be skipped here + return cb ? cb(null, {success:true}) : that.speedList(); + }); }); }); }); @@ -415,1278 +401,1296 @@ API.prototype.update = function(cb) { }); }); }); - }); - return false; -}; + return false; + } -/** - * Graceful Reload an application - * - * @param {String} process_name Application Name or All - * @param {Object} opts Options - * @param {Function} cb Callback - */ -API.prototype.gracefulReload = function(process_name, opts, cb) { - var that = this; + /** + * Graceful Reload an application + * + * @param {String} process_name Application Name or All + * @param {Object} opts Options + * @param {Function} cb Callback + */ + gracefulReload (process_name, opts, cb) { + var that = this; - if (typeof(opts) == "function") { - cb = opts; - opts = {}; - } + if (typeof(opts) == "function") { + cb = opts; + opts = {}; + } - //Common.printOut(conf.PREFIX_MSG_WARNING + chalk.bold.yellow('Warning gracefulReload will be soon deprecated')); - //Common.printOut(conf.PREFIX_MSG_WARNING + chalk.bold.yellow('Use http://pm2.keymetrics.io/docs/usage/signals-clean-restart/ instead')); + //Common.printOut(conf.PREFIX_MSG_WARNING + chalk.bold.yellow('Warning gracefulReload will be soon deprecated')); + //Common.printOut(conf.PREFIX_MSG_WARNING + chalk.bold.yellow('Use http://pm2.keymetrics.io/docs/usage/signals-clean-restart/ instead')); - if (Common.isConfigFile(process_name)) - that._startJson(process_name, commander, 'softReloadProcessId'); - else { - if (opts && !opts.updateEnv) - Common.printOut(IMMUTABLE_MSG); - that._operate('softReloadProcessId', process_name, opts, cb); + if (Common.isConfigFile(process_name)) + that._startJson(process_name, commander, 'softReloadProcessId'); + else { + if (opts && !opts.updateEnv) + Common.printOut(IMMUTABLE_MSG); + that._operate('softReloadProcessId', process_name, opts, cb); + } } -}; -/** - * Reload an application - * - * @param {String} process_name Application Name or All - * @param {Object} opts Options - * @param {Function} cb Callback - */ -API.prototype.reload = function(process_name, opts, cb) { - var that = this; + /** + * Reload an application + * + * @param {String} process_name Application Name or All + * @param {Object} opts Options + * @param {Function} cb Callback + */ + reload (process_name, opts, cb) { + var that = this; - if (typeof(opts) == "function") { - cb = opts; - opts = {}; - } + if (typeof(opts) == "function") { + cb = opts; + opts = {}; + } - var delay = Common.lockReload(); + var delay = Common.lockReload(); - if (delay > 0 && opts.force != true) { - Common.printError(conf.PREFIX_MSG_ERR + 'Reload already in progress, please try again in ' + Math.floor((conf.RELOAD_LOCK_TIMEOUT - delay) / 1000) + ' seconds or use --force'); - return cb ? cb(new Error('Reload in progress')) : that.exitCli(conf.ERROR_EXIT); - } + if (delay > 0 && opts.force != true) { + Common.printError(conf.PREFIX_MSG_ERR + 'Reload already in progress, please try again in ' + Math.floor((conf.RELOAD_LOCK_TIMEOUT - delay) / 1000) + ' seconds or use --force'); + return cb ? cb(new Error('Reload in progress')) : that.exitCli(conf.ERROR_EXIT); + } - if (Common.isConfigFile(process_name)) - that._startJson(process_name, opts, 'reloadProcessId', function(err, apps) { - Common.unlockReload(); - if (err) - return cb ? cb(err) : that.exitCli(conf.ERROR_EXIT); - return cb ? cb(null, apps) : that.exitCli(conf.SUCCESS_EXIT);; - }); - else { - if (opts && !opts.updateEnv) - Common.printOut(IMMUTABLE_MSG); + if (Common.isConfigFile(process_name)) + that._startJson(process_name, opts, 'reloadProcessId', function(err, apps) { + Common.unlockReload(); + if (err) + return cb ? cb(err) : that.exitCli(conf.ERROR_EXIT); + return cb ? cb(null, apps) : that.exitCli(conf.SUCCESS_EXIT);; + }); + else { + if (opts && !opts.updateEnv) + Common.printOut(IMMUTABLE_MSG); - that._operate('reloadProcessId', process_name, opts, function(err, apps) { - Common.unlockReload(); + that._operate('reloadProcessId', process_name, opts, function(err, apps) { + Common.unlockReload(); - if (err) - return cb ? cb(err) : that.exitCli(conf.ERROR_EXIT); - return cb ? cb(null, apps) : that.exitCli(conf.SUCCESS_EXIT);; - }); + if (err) + return cb ? cb(err) : that.exitCli(conf.ERROR_EXIT); + return cb ? cb(null, apps) : that.exitCli(conf.SUCCESS_EXIT);; + }); + } } -}; -/** - * Restart process - * - * @param {String} cmd Application Name / Process id / JSON application file / 'all' - * @param {Object} opts Extra options to be updated - * @param {Function} cb Callback - */ -API.prototype.restart = function(cmd, opts, cb) { - if (typeof(opts) == "function") { - cb = opts; - opts = {}; - } - var that = this; - - if (typeof(cmd) === 'number') - cmd = cmd.toString(); - - if (cmd == "-") { - // Restart from PIPED JSON - process.stdin.resume(); - process.stdin.setEncoding('utf8'); - process.stdin.on('data', function (param) { - process.stdin.pause(); - that.actionFromJson('restartProcessId', param, opts, 'pipe', cb); - }); - } - else if (Common.isConfigFile(cmd) || typeof(cmd) === 'object') - that._startJson(cmd, opts, 'restartProcessId', cb); - else { - if (opts && !opts.updateEnv) - Common.printOut(IMMUTABLE_MSG); - that._operate('restartProcessId', cmd, opts, cb); - } -}; + /** + * Restart process + * + * @param {String} cmd Application Name / Process id / JSON application file / 'all' + * @param {Object} opts Extra options to be updated + * @param {Function} cb Callback + */ + restart (cmd, opts, cb) { + if (typeof(opts) == "function") { + cb = opts; + opts = {}; + } + var that = this; -/** - * Delete process - * - * @param {String} process_name Application Name / Process id / Application file / 'all' - * @param {Function} cb Callback - */ -API.prototype.delete = function(process_name, jsonVia, cb) { - var that = this; + if (typeof(cmd) === 'number') + cmd = cmd.toString(); - if (typeof(jsonVia) === "function") { - cb = jsonVia; - jsonVia = null; - } - if (typeof(process_name) === "number") { - process_name = process_name.toString(); + if (cmd == "-") { + // Restart from PIPED JSON + process.stdin.resume(); + process.stdin.setEncoding('utf8'); + process.stdin.on('data', function (param) { + process.stdin.pause(); + that.actionFromJson('restartProcessId', param, opts, 'pipe', cb); + }); + } + else if (Common.isConfigFile(cmd) || typeof(cmd) === 'object') + that._startJson(cmd, opts, 'restartProcessId', cb); + else { + if (opts && !opts.updateEnv) + Common.printOut(IMMUTABLE_MSG); + that._operate('restartProcessId', cmd, opts, cb); + } } - if (jsonVia == 'pipe') - return that.actionFromJson('deleteProcessId', process_name, commander, 'pipe', cb); - if (Common.isConfigFile(process_name)) - return that.actionFromJson('deleteProcessId', process_name, commander, 'file', cb); - else - that._operate('deleteProcessId', process_name, cb); -}; + /** + * Delete process + * + * @param {String} process_name Application Name / Process id / Application file / 'all' + * @param {Function} cb Callback + */ + delete (process_name, jsonVia, cb) { + var that = this; -/** - * Stop process - * - * @param {String} process_name Application Name / Process id / Application file / 'all' - * @param {Function} cb Callback - */ -API.prototype.stop = function(process_name, cb) { - var that = this; - - if (typeof(process_name) === 'number') - process_name = process_name.toString(); - - if (process_name == "-") { - process.stdin.resume(); - process.stdin.setEncoding('utf8'); - process.stdin.on('data', function (param) { - process.stdin.pause(); - that.actionFromJson('stopProcessId', param, commander, 'pipe', cb); - }); + if (typeof(jsonVia) === "function") { + cb = jsonVia; + jsonVia = null; + } + if (typeof(process_name) === "number") { + process_name = process_name.toString(); + } + + if (jsonVia == 'pipe') + return that.actionFromJson('deleteProcessId', process_name, commander, 'pipe', cb); + if (Common.isConfigFile(process_name)) + return that.actionFromJson('deleteProcessId', process_name, commander, 'file', cb); + else + that._operate('deleteProcessId', process_name, cb); } - else if (Common.isConfigFile(process_name)) - that.actionFromJson('stopProcessId', process_name, commander, 'file', cb); - else - that._operate('stopProcessId', process_name, cb); -}; -/** - * Get list of all processes managed - * - * @param {Function} cb Callback - */ -API.prototype.list = function(opts, cb) { - var that = this; + /** + * Stop process + * + * @param {String} process_name Application Name / Process id / Application file / 'all' + * @param {Function} cb Callback + */ + stop (process_name, cb) { + var that = this; + + if (typeof(process_name) === 'number') + process_name = process_name.toString(); - if (typeof(opts) == 'function') { - cb = opts; - opts = null; + if (process_name == "-") { + process.stdin.resume(); + process.stdin.setEncoding('utf8'); + process.stdin.on('data', function (param) { + process.stdin.pause(); + that.actionFromJson('stopProcessId', param, commander, 'pipe', cb); + }); + } + else if (Common.isConfigFile(process_name)) + that.actionFromJson('stopProcessId', process_name, commander, 'file', cb); + else + that._operate('stopProcessId', process_name, cb); } - that.Client.executeRemote('getMonitorData', {}, function(err, list) { - if (err) { - Common.printError(err); - return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT); + /** + * Get list of all processes managed + * + * @param {Function} cb Callback + */ + list (opts, cb) { + var that = this; + + if (typeof(opts) == 'function') { + cb = opts; + opts = null; } - if (opts && opts.rawArgs && opts.rawArgs.indexOf('--watch') > -1) { - var moment = require('moment'); - function show() { - process.stdout.write('\033[2J'); - process.stdout.write('\033[0f'); - console.log('Last refresh: ', moment().format('LTS')); - that.Client.executeRemote('getMonitorData', {}, function(err, list) { - UX.dispAsTable(list, null); - }); + that.Client.executeRemote('getMonitorData', {}, function(err, list) { + if (err) { + Common.printError(err); + return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT); } - show(); - setInterval(show, 900); - return false; - } + if (opts && opts.rawArgs && opts.rawArgs.indexOf('--watch') > -1) { + var moment = require('moment'); + function show() { + process.stdout.write('\\033[2J'); + process.stdout.write('\\033[0f'); + console.log('Last refresh: ', moment().format('LTS')); + that.Client.executeRemote('getMonitorData', {}, function(err, list) { + UX.dispAsTable(list, null); + }); + } - return cb ? cb(null, list) : that.speedList(); - }); -}; + show(); + setInterval(show, 900); + return false; + } -/** - * Kill Daemon - * - * @param {Function} cb Callback - */ -API.prototype.killDaemon = API.prototype.kill = function(cb) { - var that = this; + return cb ? cb(null, list) : that.speedList(); + }); + } - var semver = require('semver'); - Common.printOut(conf.PREFIX_MSG + 'Stopping PM2...'); + /** + * Kill Daemon + * + * @param {Function} cb Callback + */ + killDaemon (cb) { + var that = this; - that.Client.executeRemote('notifyKillPM2', {}, function() {}); + var semver = require('semver'); + Common.printOut(conf.PREFIX_MSG + 'Stopping PM2...'); - that.killAllModules(function() { - that._operate('deleteProcessId', 'all', function(err, list) { - Common.printOut(conf.PREFIX_MSG + 'All processes have been stopped and deleted'); - process.env.PM2_SILENT = 'false'; + that.Client.executeRemote('notifyKillPM2', {}, function() {}); - that.killInteract(function(err, data) { - that.Client.killDaemon(function(err, res) { - if (err) Common.printError(err); - Common.printOut(conf.PREFIX_MSG + 'PM2 stopped'); - return cb ? cb(err, res) : that.exitCli(conf.SUCCESS_EXIT); + that.killAllModules(function() { + that._operate('deleteProcessId', 'all', function(err, list) { + Common.printOut(conf.PREFIX_MSG + 'All processes have been stopped and deleted'); + process.env.PM2_SILENT = 'false'; + + that.killInteract(function(err, data) { + that.Client.killDaemon(function(err, res) { + if (err) Common.printError(err); + Common.printOut(conf.PREFIX_MSG + 'PM2 stopped'); + return cb ? cb(err, res) : that.exitCli(conf.SUCCESS_EXIT); + }); }); }); }); - }); -}; + } -///////////////////// -// Private methods // -///////////////////// + ///////////////////// + // Private methods // + ///////////////////// -/** - * Method to START / RESTART a script - * - * @private - * @param {string} script script name (will be resolved according to location) - */ -API.prototype._startScript = function(script, opts, cb) { - if (typeof opts == "function") { - cb = opts; - opts = {}; - } - var that = this; + /** + * Method to START / RESTART a script + * + * @private + * @param {string} script script name (will be resolved according to location) + */ + _startScript (script, opts, cb) { + if (typeof opts == "function") { + cb = opts; + opts = {}; + } + var that = this; - var app_conf = Config.transCMDToConf(opts); - var appConf = {}; + var app_conf = Config.transCMDToConf(opts); + var appConf = {}; - if (!!opts.executeCommand) - app_conf.exec_mode = 'fork'; - else if (opts.instances !== undefined) - app_conf.exec_mode = 'cluster'; - else - app_conf.exec_mode = 'fork'; + if (!!opts.executeCommand) + app_conf.exec_mode = 'fork'; + else if (opts.instances !== undefined) + app_conf.exec_mode = 'cluster'; + else + app_conf.exec_mode = 'fork'; - // Options set via environment variables - if (process.env.PM2_DEEP_MONITORING) - app_conf.deep_monitoring = true; + // Options set via environment variables + if (process.env.PM2_DEEP_MONITORING) + app_conf.deep_monitoring = true; - if (typeof app_conf.name == 'function'){ - delete app_conf.name; - } + if (typeof app_conf.name == 'function'){ + delete app_conf.name; + } - delete app_conf.args; + delete app_conf.args; - var argsIndex; + var argsIndex; - if (opts.rawArgs && (argsIndex = opts.rawArgs.indexOf('--')) >= 0) { - app_conf.args = opts.rawArgs.slice(argsIndex + 1); - } - else if (opts.scriptArgs) { - app_conf.args = opts.scriptArgs; - } + if (opts.rawArgs && (argsIndex = opts.rawArgs.indexOf('--')) >= 0) { + app_conf.args = opts.rawArgs.slice(argsIndex + 1); + } + else if (opts.scriptArgs) { + app_conf.args = opts.scriptArgs; + } - app_conf.script = script; + app_conf.script = script; - if ((appConf = Common.verifyConfs(app_conf)) instanceof Error) - return cb ? cb(Common.retErr(appConf)) : that.exitCli(conf.ERROR_EXIT); + if ((appConf = Common.verifyConfs(app_conf)) instanceof Error) + return cb ? cb(Common.retErr(appConf)) : that.exitCli(conf.ERROR_EXIT); - app_conf = appConf[0]; + app_conf = appConf[0]; - app_conf.username = Common.getCurrentUsername(); + app_conf.username = Common.getCurrentUsername(); - /** - * If -w option, write configuration to configuration.json file - */ - if (appConf.write) { - var dst_path = path.join(process.env.PWD || process.cwd(), app_conf.name + '-pm2.json'); - Common.printOut(conf.PREFIX_MSG + 'Writing configuration to', chalk.blue(dst_path)); - // pretty JSON - try { - fs.writeFileSync(dst_path, JSON.stringify(app_conf, null, 2)); - } catch (e) { - console.error(e.stack || e); + /** + * If -w option, write configuration to configuration.json file + */ + if (appConf.write) { + var dst_path = path.join(process.env.PWD || process.cwd(), app_conf.name + '-pm2.json'); + Common.printOut(conf.PREFIX_MSG + 'Writing configuration to', chalk.blue(dst_path)); + // pretty JSON + try { + fs.writeFileSync(dst_path, JSON.stringify(app_conf, null, 2)); + } catch (e) { + console.error(e.stack || e); + } } - } - /** - * If start start/restart application - */ - function restartExistingProcessName(cb) { - if (!isNaN(script) || + /** + * If start start/restart application + */ + function restartExistingProcessName(cb) { + if (!isNaN(script) || (typeof script === 'string' && script.indexOf('/') != -1) || (typeof script === 'string' && path.extname(script) !== '')) - return cb(null); - - if (script !== 'all') { - that.Client.getProcessIdByName(script, function(err, ids) { - if (err && cb) return cb(err); - if (ids.length > 0) { - that._operate('restartProcessId', script, opts, function(err, list) { - if (err) return cb(err); - Common.printOut(conf.PREFIX_MSG + 'Process successfully started'); - return cb(true, list); - }); - } - else return cb(null); - }); + return cb(null); + + if (script !== 'all') { + that.Client.getProcessIdByName(script, function(err, ids) { + if (err && cb) return cb(err); + if (ids.length > 0) { + that._operate('restartProcessId', script, opts, function(err, list) { + if (err) return cb(err); + Common.printOut(conf.PREFIX_MSG + 'Process successfully started'); + return cb(true, list); + }); + } + else return cb(null); + }); + } + else { + that._operate('restartProcessId', 'all', function(err, list) { + if (err) return cb(err); + Common.printOut(conf.PREFIX_MSG + 'Process successfully started'); + return cb(true, list); + }); + } } - else { - that._operate('restartProcessId', 'all', function(err, list) { + + function restartExistingProcessId(cb) { + if (isNaN(script)) return cb(null); + + that._operate('restartProcessId', script, opts, function(err, list) { if (err) return cb(err); Common.printOut(conf.PREFIX_MSG + 'Process successfully started'); return cb(true, list); }); } - } - - function restartExistingProcessId(cb) { - if (isNaN(script)) return cb(null); - that._operate('restartProcessId', script, opts, function(err, list) { - if (err) return cb(err); - Common.printOut(conf.PREFIX_MSG + 'Process successfully started'); - return cb(true, list); - }); - } - - /** - * Restart a process with the same full path - * Or start it - */ - function restartExistingProcessPath(cb) { - that.Client.executeRemote('getMonitorData', {}, function(err, procs) { - if (err) return cb ? cb(new Error(err)) : that.exitCli(conf.ERROR_EXIT); + /** + * Restart a process with the same full path + * Or start it + */ + function restartExistingProcessPath(cb) { + that.Client.executeRemote('getMonitorData', {}, function(err, procs) { + if (err) return cb ? cb(new Error(err)) : that.exitCli(conf.ERROR_EXIT); - var full_path = path.resolve(that.cwd, script); - var managed_script = null; + var full_path = path.resolve(that.cwd, script); + var managed_script = null; - procs.forEach(function(proc) { - if (proc.pm2_env.pm_exec_path == full_path && + procs.forEach(function(proc) { + if (proc.pm2_env.pm_exec_path == full_path && proc.pm2_env.name == app_conf.name) - managed_script = proc; - }); + managed_script = proc; + }); - if (managed_script && + if (managed_script && (managed_script.pm2_env.status == conf.STOPPED_STATUS || - managed_script.pm2_env.status == conf.STOPPING_STATUS || - managed_script.pm2_env.status == conf.ERRORED_STATUS)) { - // Restart process if stopped - var app_name = managed_script.pm2_env.name; + managed_script.pm2_env.status == conf.STOPPING_STATUS || + managed_script.pm2_env.status == conf.ERRORED_STATUS)) { + // Restart process if stopped + var app_name = managed_script.pm2_env.name; - that._operate('restartProcessId', app_name, opts, function(err, list) { - if (err) return cb ? cb(new Error(err)) : that.exitCli(conf.ERROR_EXIT); - Common.printOut(conf.PREFIX_MSG + 'Process successfully started'); - return cb(true, list); - }); - return false; - } - else if (managed_script && !opts.force) { - Common.printError(conf.PREFIX_MSG_ERR + 'Script already launched, add -f option to force re-execution'); - return cb(new Error('Script already launched')); - } + that._operate('restartProcessId', app_name, opts, function(err, list) { + if (err) return cb ? cb(new Error(err)) : that.exitCli(conf.ERROR_EXIT); + Common.printOut(conf.PREFIX_MSG + 'Process successfully started'); + return cb(true, list); + }); + return false; + } + else if (managed_script && !opts.force) { + Common.printError(conf.PREFIX_MSG_ERR + 'Script already launched, add -f option to force re-execution'); + return cb(new Error('Script already launched')); + } - var resolved_paths = null; + var resolved_paths = null; - try { - resolved_paths = Common.resolveAppAttributes({ - cwd : that.cwd, - pm2_home : that.pm2_home - }, app_conf); - } catch(e) { - Common.printError(e); - return cb(Common.retErr(e)); - } + try { + resolved_paths = Common.resolveAppAttributes({ + cwd : that.cwd, + pm2_home : that.pm2_home + }, app_conf); + } catch(e) { + Common.printError(e); + return cb(Common.retErr(e)); + } - Common.printOut(conf.PREFIX_MSG + 'Starting %s in %s (%d instance' + (resolved_paths.instances > 1 ? 's' : '') + ')', - resolved_paths.pm_exec_path, resolved_paths.exec_mode, resolved_paths.instances); + Common.printOut(conf.PREFIX_MSG + 'Starting %s in %s (%d instance' + (resolved_paths.instances > 1 ? 's' : '') + ')', + resolved_paths.pm_exec_path, resolved_paths.exec_mode, resolved_paths.instances); - if (!resolved_paths.env) resolved_paths.env = {}; + if (!resolved_paths.env) resolved_paths.env = {}; - // Set PM2 HOME in case of child process using PM2 API - resolved_paths.env['PM2_HOME'] = that.pm2_home; + // Set PM2 HOME in case of child process using PM2 API + resolved_paths.env['PM2_HOME'] = that.pm2_home; - var additional_env = Modularizer.getAdditionalConf(resolved_paths.name); - util._extend(resolved_paths.env, additional_env); + var additional_env = Modularizer.getAdditionalConf(resolved_paths.name); + util._extend(resolved_paths.env, additional_env); - // Is KM linked? - resolved_paths.km_link = that.gl_is_km_linked; + // Is KM linked? + resolved_paths.km_link = that.gl_is_km_linked; - that.Client.executeRemote('prepare', resolved_paths, function(err, data) { - if (err) { - Common.printError(conf.PREFIX_MSG_ERR + 'Error while launching application', err.stack || err); - return cb(Common.retErr(err)); - } + that.Client.executeRemote('prepare', resolved_paths, function(err, data) { + if (err) { + Common.printError(conf.PREFIX_MSG_ERR + 'Error while launching application', err.stack || err); + return cb(Common.retErr(err)); + } - Common.printOut(conf.PREFIX_MSG + 'Done.'); - return cb(true, data); + Common.printOut(conf.PREFIX_MSG + 'Done.'); + return cb(true, data); + }); + return false; }); - return false; - }); - } - - async.series([ - restartExistingProcessName, - restartExistingProcessId, - restartExistingProcessPath - ], function(err, data) { + } - if (err instanceof Error) - return cb ? cb(err) : that.exitCli(conf.ERROR_EXIT); + async.series([ + restartExistingProcessName, + restartExistingProcessId, + restartExistingProcessPath + ], function(err, data) { - var ret = {}; - data.forEach(function(_dt) { - if (_dt !== undefined) - ret = _dt; - }); + if (err instanceof Error) + return cb ? cb(err) : that.exitCli(conf.ERROR_EXIT); - return cb ? cb(null, ret) : that.speedList(); - }); -}; + var ret = {}; + data.forEach(function(_dt) { + if (_dt !== undefined) + ret = _dt; + }); -/** - * Method to start/restart/reload processes from a JSON file - * It will start app not started - * Can receive only option to skip applications - * - * @private - */ -API.prototype._startJson = function(file, opts, action, pipe, cb) { - var config = {}; - var appConf = {}; - var deployConf = {}; - var apps_info = []; - var that = this; - - if (typeof(cb) === 'undefined' && typeof(pipe) === 'function') { - cb = pipe; + return cb ? cb(null, ret) : that.speedList(); + }); } - if (typeof(file) === 'object') { - config = file; - } else if (pipe === 'pipe') { - config = Common.parseConfig(file, 'pipe'); - } else { - var data = null; + /** + * Method to start/restart/reload processes from a JSON file + * It will start app not started + * Can receive only option to skip applications + * + * @private + */ + _startJson (file, opts, action, pipe, cb) { + var config = {}; + var appConf = {}; + var deployConf = {}; + var apps_info = []; + var that = this; - var isAbsolute = false + if (typeof(cb) === 'undefined' && typeof(pipe) === 'function') { + cb = pipe; + } - //node 0.11 compatibility #2815 - if (typeof path.isAbsolute === 'function') { - isAbsolute = path.isAbsolute(file) + if (typeof(file) === 'object') { + config = file; + } else if (pipe === 'pipe') { + config = Common.parseConfig(file, 'pipe'); } else { - isAbsolute = require('./tools/IsAbsolute.js')(file) - } + var data = null; - var file_path = isAbsolute ? file : path.join(that.cwd, file); + var isAbsolute = false - debug('Resolved filepath %s', file_path); + //node 0.11 compatibility #2815 + if (typeof path.isAbsolute === 'function') { + isAbsolute = path.isAbsolute(file) + } else { + isAbsolute = require('./tools/IsAbsolute.js')(file) + } - try { - data = fs.readFileSync(file_path); - } catch(e) { - Common.printError(conf.PREFIX_MSG_ERR + 'File ' + file +' not found'); - return cb ? cb(Common.retErr(e)) : that.exitCli(conf.ERROR_EXIT); - } + var file_path = isAbsolute ? file : path.join(that.cwd, file); - try { - config = Common.parseConfig(data, file); - } catch(e) { - Common.printError(conf.PREFIX_MSG_ERR + 'File ' + file + ' malformated'); - console.error(e); - return cb ? cb(Common.retErr(e)) : that.exitCli(conf.ERROR_EXIT); - } - } + debug('Resolved filepath %s', file_path); - if (config.deploy) - deployConf = config.deploy; + try { + data = fs.readFileSync(file_path); + } catch(e) { + Common.printError(conf.PREFIX_MSG_ERR + 'File ' + file +' not found'); + return cb ? cb(Common.retErr(e)) : that.exitCli(conf.ERROR_EXIT); + } - if (config.apps) - appConf = config.apps; - else if (config.pm2) - appConf = config.pm2; - else - appConf = config; + try { + config = Common.parseConfig(data, file); + } catch(e) { + Common.printError(conf.PREFIX_MSG_ERR + 'File ' + file + ' malformated'); + console.error(e); + return cb ? cb(Common.retErr(e)) : that.exitCli(conf.ERROR_EXIT); + } + } - if (!Array.isArray(appConf)) - appConf = [appConf]; //convert to array + if (config.deploy) + deployConf = config.deploy; - if ((appConf = Common.verifyConfs(appConf)) instanceof Error) - return cb ? cb(appConf) : that.exitCli(conf.ERROR_EXIT); + if (config.apps) + appConf = config.apps; + else if (config.pm2) + appConf = config.pm2; + else + appConf = config; - process.env.PM2_JSON_PROCESSING = true; + if (!Array.isArray(appConf)) + appConf = [appConf]; //convert to array - // Get App list - var apps_name = []; - var proc_list = {}; + if ((appConf = Common.verifyConfs(appConf)) instanceof Error) + return cb ? cb(appConf) : that.exitCli(conf.ERROR_EXIT); - // Here we pick only the field we want from the CLI when starting a JSON - appConf.forEach(function(app) { - // --only - if (opts.only && opts.only != app.name) - return false; - // --watch - if (!app.watch && opts.watch && opts.watch === true) - app.watch = true; - // --ignore-watch - if (!app.ignore_watch && opts.ignore_watch) - app.ignore_watch = opts.ignore_watch; - // --instances - if (opts.instances && typeof(opts.instances) === 'number') - app.instances = opts.instances; - // --uid - if (opts.uid) - app.uid = opts.uid; - // --gid - if (opts.gid) - app.gid = opts.gid; - // Specific - if (app.append_env_to_name && opts.env) - app.name += ('-' + opts.env); - app.username = Common.getCurrentUsername(); - apps_name.push(app.name); - }); - - that.Client.executeRemote('getMonitorData', {}, function(err, raw_proc_list) { - if (err) { - Common.printError(err); - return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT); - } + process.env.PM2_JSON_PROCESSING = true; - /** - * Uniquify in memory process list - */ - raw_proc_list.forEach(function(proc) { - proc_list[proc.name] = proc; + // Get App list + var apps_name = []; + var proc_list = {}; + + // Here we pick only the field we want from the CLI when starting a JSON + appConf.forEach(function(app) { + // --only + if (opts.only && opts.only != app.name) + return false; + // --watch + if (!app.watch && opts.watch && opts.watch === true) + app.watch = true; + // --ignore-watch + if (!app.ignore_watch && opts.ignore_watch) + app.ignore_watch = opts.ignore_watch; + // --instances + if (opts.instances && typeof(opts.instances) === 'number') + app.instances = opts.instances; + // --uid + if (opts.uid) + app.uid = opts.uid; + // --gid + if (opts.gid) + app.gid = opts.gid; + // Specific + if (app.append_env_to_name && opts.env) + app.name += ('-' + opts.env); + app.username = Common.getCurrentUsername(); + apps_name.push(app.name); }); - /** - * Auto detect application already started - * and act on them depending on action - */ - async.eachLimit(Object.keys(proc_list), conf.CONCURRENT_ACTIONS, function(proc_name, next) { - // Skip app name (--only option) - if (apps_name.indexOf(proc_name) == -1) - return next(); + that.Client.executeRemote('getMonitorData', {}, function(err, raw_proc_list) { + if (err) { + Common.printError(err); + return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT); + } + + /** + * Uniquify in memory process list + */ + raw_proc_list.forEach(function(proc) { + proc_list[proc.name] = proc; + }); + + /** + * Auto detect application already started + * and act on them depending on action + */ + async.eachLimit(Object.keys(proc_list), conf.CONCURRENT_ACTIONS, function(proc_name, next) { + // Skip app name (--only option) + if (apps_name.indexOf(proc_name) == -1) + return next(); - if (!(action == 'reloadProcessId' || + if (!(action == 'reloadProcessId' || action == 'softReloadProcessId' || action == 'restartProcessId')) - throw new Error('Wrong action called'); + throw new Error('Wrong action called'); - var apps = appConf.filter(function(app) { - return app.name == proc_name; - }); + var apps = appConf.filter(function(app) { + return app.name == proc_name; + }); - var envs = apps.map(function(app){ - // Binds env_diff to env and returns it. - return Common.mergeEnvironmentVariables(app, opts.env, deployConf); - }); + var envs = apps.map(function(app){ + // Binds env_diff to env and returns it. + return Common.mergeEnvironmentVariables(app, opts.env, deployConf); + }); - // Assigns own enumerable properties of all - // Notice: if people use the same name in different apps, - // duplicated envs will be overrode by the last one - var env = envs.reduce(function(e1, e2){ - return util._extend(e1, e2); - }); + // Assigns own enumerable properties of all + // Notice: if people use the same name in different apps, + // duplicated envs will be overrode by the last one + var env = envs.reduce(function(e1, e2){ + return util._extend(e1, e2); + }); - // When we are processing JSON, allow to keep the new env by default - env.updateEnv = true; + // When we are processing JSON, allow to keep the new env by default + env.updateEnv = true; - // Pass `env` option - that._operate(action, proc_name, env, function(err, ret) { - if (err) Common.printError(err); + // Pass `env` option + that._operate(action, proc_name, env, function(err, ret) { + if (err) Common.printError(err); - // For return - apps_info = apps_info.concat(ret); + // For return + apps_info = apps_info.concat(ret); - that.Client.notifyGod(action, proc_name); - // And Remove from array to spy - apps_name.splice(apps_name.indexOf(proc_name), 1); - return next(); - }); + that.Client.notifyGod(action, proc_name); + // And Remove from array to spy + apps_name.splice(apps_name.indexOf(proc_name), 1); + return next(); + }); - }, function(err) { - if (err) return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT); - if (apps_name.length > 0 && action != 'start') - Common.printOut(conf.PREFIX_MSG_WARNING + 'Applications %s not running, starting...', apps_name.join(', ')); - // Start missing apps - return startApps(apps_name, function(err, apps) { - apps_info = apps_info.concat(apps); - return cb ? cb(err, apps_info) : that.speedList(err ? 1 : 0); + }, function(err) { + if (err) return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT); + if (apps_name.length > 0 && action != 'start') + Common.printOut(conf.PREFIX_MSG_WARNING + 'Applications %s not running, starting...', apps_name.join(', ')); + // Start missing apps + return startApps(apps_name, function(err, apps) { + apps_info = apps_info.concat(apps); + return cb ? cb(err, apps_info) : that.speedList(err ? 1 : 0); + }); }); + return false; }); - return false; - }); - function startApps(app_name_to_start, cb) { - var apps_to_start = []; - var apps_started = []; + function startApps(app_name_to_start, cb) { + var apps_to_start = []; + var apps_started = []; - appConf.forEach(function(app, i) { - if (app_name_to_start.indexOf(app.name) != -1) { - apps_to_start.push(appConf[i]); - } - }); + appConf.forEach(function(app, i) { + if (app_name_to_start.indexOf(app.name) != -1) { + apps_to_start.push(appConf[i]); + } + }); + + async.eachLimit(apps_to_start, conf.CONCURRENT_ACTIONS, function(app, next) { + if (opts.cwd) + app.cwd = opts.cwd; + if (opts.force_name) + app.name = opts.force_name; + if (opts.started_as_module) + app.pmx_module = true; + + var resolved_paths = null; + + // hardcode script name to use `serve` feature inside a process file + if (app.script === 'serve') { + app.script = path.resolve(__dirname, 'API', 'Serve.js') + } + + try { + resolved_paths = Common.resolveAppAttributes({ + cwd : that.cwd, + pm2_home : that.pm2_home + }, app); + } catch (e) { + return next(); + } + + if (!resolved_paths.env) resolved_paths.env = {}; + + // Set PM2 HOME in case of child process using PM2 API + resolved_paths.env['PM2_HOME'] = that.pm2_home; - async.eachLimit(apps_to_start, conf.CONCURRENT_ACTIONS, function(app, next) { - if (opts.cwd) - app.cwd = opts.cwd; - if (opts.force_name) - app.name = opts.force_name; - if (opts.started_as_module) - app.pmx_module = true; + var additional_env = Modularizer.getAdditionalConf(resolved_paths.name); + util._extend(resolved_paths.env, additional_env); - var resolved_paths = null; + resolved_paths.env = Common.mergeEnvironmentVariables(resolved_paths, opts.env, deployConf); - // hardcode script name to use `serve` feature inside a process file - if (app.script === 'serve') { - app.script = path.resolve(__dirname, 'API', 'Serve.js') + delete resolved_paths.env.current_conf; + + // Is KM linked? + resolved_paths.km_link = that.gl_is_km_linked; + + that.Client.executeRemote('prepare', resolved_paths, function(err, data) { + if (err) { + Common.printError(conf.PREFIX_MSG_ERR + 'Process failed to launch %s', err.message ? err.message : err); + return next(); + } + if (data.length === 0) { + Common.printError(conf.PREFIX_MSG_ERR + 'Process config loading failed', data); + return next(); + } + + Common.printOut(conf.PREFIX_MSG + 'App [%s] launched (%d instances)', data[0].pm2_env.name, data.length); + apps_started = apps_started.concat(data); + next(); + }); + + }, function(err) { + return cb ? cb(err || null, apps_started) : that.speedList(); + }); + return false; + } + } + + /** + * Apply a RPC method on the json file + * @private + * @method actionFromJson + * @param {string} action RPC Method + * @param {object} options + * @param {string|object} file file + * @param {string} jsonVia action type (=only 'pipe' ?) + * @param {Function} + */ + actionFromJson (action, file, opts, jsonVia, cb) { + var appConf = {}; + var ret_processes = []; + var that = this; + + //accept programmatic calls + if (typeof file == 'object') { + cb = typeof jsonVia == 'function' ? jsonVia : cb; + appConf = file; + } + else if (jsonVia == 'file') { + var data = null; + + try { + data = fs.readFileSync(file); + } catch(e) { + Common.printError(conf.PREFIX_MSG_ERR + 'File ' + file +' not found'); + return cb ? cb(Common.retErr(e)) : that.exitCli(conf.ERROR_EXIT); } try { - resolved_paths = Common.resolveAppAttributes({ - cwd : that.cwd, - pm2_home : that.pm2_home - }, app); - } catch (e) { - return next(); + appConf = Common.parseConfig(data, file); + } catch(e) { + Common.printError(conf.PREFIX_MSG_ERR + 'File ' + file + ' malformated'); + console.error(e); + return cb ? cb(Common.retErr(e)) : that.exitCli(conf.ERROR_EXIT); } + } else if (jsonVia == 'pipe') { + appConf = Common.parseConfig(file, 'pipe'); + } else { + Common.printError('Bad call to actionFromJson, jsonVia should be one of file, pipe'); + return that.exitCli(conf.ERROR_EXIT); + } - if (!resolved_paths.env) resolved_paths.env = {}; + // Backward compatibility + if (appConf.apps) + appConf = appConf.apps; - // Set PM2 HOME in case of child process using PM2 API - resolved_paths.env['PM2_HOME'] = that.pm2_home; + if (!Array.isArray(appConf)) + appConf = [appConf]; - var additional_env = Modularizer.getAdditionalConf(resolved_paths.name); - util._extend(resolved_paths.env, additional_env); + if ((appConf = Common.verifyConfs(appConf)) instanceof Error) + return cb ? cb(appConf) : that.exitCli(conf.ERROR_EXIT); - resolved_paths.env = Common.mergeEnvironmentVariables(resolved_paths, opts.env, deployConf); + async.eachLimit(appConf, conf.CONCURRENT_ACTIONS, function(proc, next1) { + var name = ''; + var new_env; - delete resolved_paths.env.current_conf; + if (!proc.name) + name = path.basename(proc.script); + else + name = proc.name; - // Is KM linked? - resolved_paths.km_link = that.gl_is_km_linked; + if (opts.only && opts.only != name) + return process.nextTick(next1); + + if (opts && opts.env) + new_env = Common.mergeEnvironmentVariables(proc, opts.env); + else + new_env = Common.mergeEnvironmentVariables(proc); - that.Client.executeRemote('prepare', resolved_paths, function(err, data) { + that.Client.getProcessIdByName(name, function(err, ids) { if (err) { - Common.printError(conf.PREFIX_MSG_ERR + 'Process failed to launch %s', err.message ? err.message : err); - return next(); - } - if (data.length === 0) { - Common.printError(conf.PREFIX_MSG_ERR + 'Process config loading failed', data); - return next(); + Common.printError(err); + return next1(); } + if (!ids) return next1(); - Common.printOut(conf.PREFIX_MSG + 'App [%s] launched (%d instances)', data[0].pm2_env.name, data.length); - apps_started = apps_started.concat(data); - next(); - }); + async.eachLimit(ids, conf.CONCURRENT_ACTIONS, function(id, next2) { + var opts = {}; + //stopProcessId could accept options to? + if (action == 'restartProcessId') { + opts = {id : id, env : new_env}; + } else { + opts = id; + } + + that.Client.executeRemote(action, opts, function(err, res) { + ret_processes.push(res); + if (err) { + Common.printError(err); + return next2(); + } + + if (action == 'restartProcessId') { + that.Client.notifyGod('restart', id); + } else if (action == 'deleteProcessId') { + that.Client.notifyGod('delete', id); + } else if (action == 'stopProcessId') { + that.Client.notifyGod('stop', id); + } + + Common.printOut(conf.PREFIX_MSG + '[%s](%d) \u2713', name, id); + return next2(); + }); + }, function(err) { + return next1(null, ret_processes); + }); + }); }, function(err) { - return cb ? cb(err || null, apps_started) : that.speedList(); + if (cb) return cb(null, ret_processes); + else return that.speedList(); }); - return false; } -}; -/** - * Apply a RPC method on the json file - * @private - * @method actionFromJson - * @param {string} action RPC Method - * @param {object} options - * @param {string|object} file file - * @param {string} jsonVia action type (=only 'pipe' ?) - * @param {Function} - */ -API.prototype.actionFromJson = function(action, file, opts, jsonVia, cb) { - var appConf = {}; - var ret_processes = []; - var that = this; - - //accept programmatic calls - if (typeof file == 'object') { - cb = typeof jsonVia == 'function' ? jsonVia : cb; - appConf = file; - } - else if (jsonVia == 'file') { - var data = null; - try { - data = fs.readFileSync(file); - } catch(e) { - Common.printError(conf.PREFIX_MSG_ERR + 'File ' + file +' not found'); - return cb ? cb(Common.retErr(e)) : that.exitCli(conf.ERROR_EXIT); - } + /** + * Main function to operate with PM2 daemon + * + * @param {String} action_name Name of action (restartProcessId, deleteProcessId, stopProcessId) + * @param {String} process_name can be 'all', a id integer or process name + * @param {Object} envs object with CLI options / environment + */ + _operate (action_name, process_name, envs, cb) { + var that = this; + var update_env = false; + var ret = []; - try { - appConf = Common.parseConfig(data, file); - } catch(e) { - Common.printError(conf.PREFIX_MSG_ERR + 'File ' + file + ' malformated'); - console.error(e); - return cb ? cb(Common.retErr(e)) : that.exitCli(conf.ERROR_EXIT); + // Make sure all options exist + if (!envs) + envs = {}; + + if (typeof(envs) == 'function'){ + cb = envs; + envs = {}; } - } else if (jsonVia == 'pipe') { - appConf = Common.parseConfig(file, 'pipe'); - } else { - Common.printError('Bad call to actionFromJson, jsonVia should be one of file, pipe'); - return that.exitCli(conf.ERROR_EXIT); - } - // Backward compatibility - if (appConf.apps) - appConf = appConf.apps; + // Set via env.update (JSON processing) + if (envs.updateEnv === true) + update_env = true; + + var concurrent_actions = envs.parallel || conf.CONCURRENT_ACTIONS; - if (!Array.isArray(appConf)) - appConf = [appConf]; + if (!process.env.PM2_JSON_PROCESSING || envs.commands) { + envs = that._handleAttributeUpdate(envs); + } - if ((appConf = Common.verifyConfs(appConf)) instanceof Error) - return cb ? cb(appConf) : that.exitCli(conf.ERROR_EXIT); + /** + * Set current updated configuration if not passed + */ + if (!envs.current_conf) { + var _conf = fclone(envs); + envs = { + current_conf : _conf + } - async.eachLimit(appConf, conf.CONCURRENT_ACTIONS, function(proc, next1) { - var name = ''; - var new_env; + // Is KM linked? + envs.current_conf.km_link = that.gl_is_km_linked; + } - if (!proc.name) - name = path.basename(proc.script); - else - name = proc.name; + /** + * Operate action on specific process id + */ + function processIds(ids, cb) { + Common.printOut(conf.PREFIX_MSG + 'Applying action %s on app [%s](ids: %s)', action_name, process_name, ids); - if (opts.only && opts.only != name) - return process.nextTick(next1); + if (action_name == 'deleteProcessId') + concurrent_actions = 10; - if (opts && opts.env) - new_env = Common.mergeEnvironmentVariables(proc, opts.env); - else - new_env = Common.mergeEnvironmentVariables(proc); + async.eachLimit(ids, concurrent_actions, function(id, next) { + var opts; - that.Client.getProcessIdByName(name, function(err, ids) { - if (err) { - Common.printError(err); - return next1(); - } - if (!ids) return next1(); + // These functions need extra param to be passed + if (action_name == 'restartProcessId' || + action_name == 'reloadProcessId' || + action_name == 'softReloadProcessId') { + var new_env = {}; + + if (update_env === true) { + if (conf.PM2_PROGRAMMATIC == true) + new_env = Common.safeExtend({}, process.env); + else + new_env = util._extend({}, process.env); - async.eachLimit(ids, conf.CONCURRENT_ACTIONS, function(id, next2) { - var opts = {}; + Object.keys(envs).forEach(function(k) { + new_env[k] = envs[k]; + }); + } + else { + new_env = envs; + } - //stopProcessId could accept options to? - if (action == 'restartProcessId') { - opts = {id : id, env : new_env}; - } else { + opts = { + id : id, + env : new_env + }; + } + else { opts = id; } - that.Client.executeRemote(action, opts, function(err, res) { - ret_processes.push(res); + that.Client.executeRemote(action_name, opts, function(err, res) { if (err) { - Common.printError(err); - return next2(); + Common.printError(conf.PREFIX_MSG_ERR + 'Process %s not found', id); + return next('Process not found'); } - if (action == 'restartProcessId') { + if (action_name == 'restartProcessId') { that.Client.notifyGod('restart', id); - } else if (action == 'deleteProcessId') { + } else if (action_name == 'deleteProcessId') { that.Client.notifyGod('delete', id); - } else if (action == 'stopProcessId') { + } else if (action_name == 'stopProcessId') { that.Client.notifyGod('stop', id); + } else if (action_name == 'reloadProcessId') { + that.Client.notifyGod('reload', id); + } else if (action_name == 'softReloadProcessId') { + that.Client.notifyGod('graceful reload', id); } - Common.printOut(conf.PREFIX_MSG + '[%s](%d) \u2713', name, id); - return next2(); + if (!Array.isArray(res)) + res = [res]; + + // Filter return + res.forEach(function(proc) { + Common.printOut(conf.PREFIX_MSG + '[%s](%d) \u2713', proc.pm2_env ? proc.pm2_env.name : process_name, id); + + if (!proc.pm2_env) return false; + + ret.push({ + name : proc.pm2_env.name, + pm_id : proc.pm2_env.pm_id, + status : proc.pm2_env.status, + restart_time : proc.pm2_env.restart_time, + pm2_env : { + name : proc.pm2_env.name, + pm_id : proc.pm2_env.pm_id, + status : proc.pm2_env.status, + restart_time : proc.pm2_env.restart_time, + env : proc.pm2_env.env + } + }); + }); + + return next(); }); }, function(err) { - return next1(null, ret_processes); + if (err) return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT); + return cb ? cb(null, ret) : that.speedList(); }); - }); - }, function(err) { - if (cb) return cb(null, ret_processes); - else return that.speedList(); - }); -}; - + } -/** - * Main function to operate with PM2 daemon - * - * @param {String} action_name Name of action (restartProcessId, deleteProcessId, stopProcessId) - * @param {String} process_name can be 'all', a id integer or process name - * @param {Object} envs object with CLI options / environment - */ -API.prototype._operate = function(action_name, process_name, envs, cb) { - var that = this; - var update_env = false; - var ret = []; - - // Make sure all options exist - if (!envs) - envs = {}; - - if (typeof(envs) == 'function'){ - cb = envs; - envs = {}; - } + if (process_name == 'all') { + that.Client.getAllProcessId(function(err, ids) { + if (err) { + Common.printError(err); + return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT); + } + if (!ids || ids.length === 0) { + Common.printError(conf.PREFIX_MSG_WARNING + 'No process found'); + return cb ? cb(new Error('process name not found')) : that.exitCli(conf.ERROR_EXIT); + } - // Set via env.update (JSON processing) - if (envs.updateEnv === true) - update_env = true; + return processIds(ids, cb); + }); + } + // operate using regex + else if (isNaN(process_name) && process_name[0] === '/' && process_name[process_name.length - 1] === '/') { + var regex = new RegExp(process_name.replace(/\//g, '')); - var concurrent_actions = envs.parallel || conf.CONCURRENT_ACTIONS; + that.Client.executeRemote('getMonitorData', {}, function(err, list) { + if (err) { + Common.printError('Error retrieving process list: ' + err); + return cb(err); + } + var found_proc = []; + list.forEach(function(proc) { + if (regex.test(proc.pm2_env.name)) { + found_proc.push(proc.pm_id); + } + }); - if (!process.env.PM2_JSON_PROCESSING || envs.commands) { - envs = that._handleAttributeUpdate(envs); - } + if (found_proc.length === 0) { + Common.printError(conf.PREFIX_MSG_WARNING + 'No process found'); + return cb ? cb(new Error('process name not found')) : that.exitCli(conf.ERROR_EXIT); + } - /** - * Set current updated configuration if not passed - */ - if (!envs.current_conf) { - var _conf = fclone(envs); - envs = { - current_conf : _conf + return processIds(found_proc, cb); + }); } + else if (isNaN(process_name)) { + /** + * We can not stop or delete a module but we can restart it + * to refresh configuration variable + */ + var allow_module_restart = action_name == 'restartProcessId' ? true : false; + + that.Client.getProcessIdByName(process_name, allow_module_restart, function(err, ids) { + if (err) { + Common.printError(err); + return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT); + } + if (!ids || ids.length === 0) { + Common.printError(conf.PREFIX_MSG_ERR + 'Process %s not found', process_name); + return cb ? cb(new Error('process name not found')) : that.exitCli(conf.ERROR_EXIT); + } + + /** + * Determine if the process to restart is a module + * if yes load configuration variables and merge with the current environment + */ + var additional_env = Modularizer.getAdditionalConf(process_name); + util._extend(envs, additional_env); - // Is KM linked? - envs.current_conf.km_link = that.gl_is_km_linked; + return processIds(ids, cb); + }); + } else { + // Check if application name as number is an app name + that.Client.getProcessIdByName(process_name, function(err, ids) { + if (ids.length > 0) + return processIds(ids, cb); + // Else operate on pm id + return processIds([process_name], cb); + }); + } } /** - * Operate action on specific process id + * Converts CamelCase Commander.js arguments + * to Underscore + * (nodeArgs -> node_args) */ - function processIds(ids, cb) { - Common.printOut(conf.PREFIX_MSG + 'Applying action %s on app [%s](ids: %s)', action_name, process_name, ids); - - if (action_name == 'deleteProcessId') - concurrent_actions = 10; - - async.eachLimit(ids, concurrent_actions, function(id, next) { - var opts; - - // These functions need extra param to be passed - if (action_name == 'restartProcessId' || - action_name == 'reloadProcessId' || - action_name == 'softReloadProcessId') { - var new_env = {}; - - if (update_env === true) { - if (conf.PM2_PROGRAMMATIC == true) - new_env = Common.safeExtend({}, process.env); - else - new_env = util._extend({}, process.env); + _handleAttributeUpdate (opts) { + var conf = Config.transCMDToConf(opts); + var that = this; - Object.keys(envs).forEach(function(k) { - new_env[k] = envs[k]; - }); - } - else { - new_env = envs; - } + if (typeof(conf.name) != 'string') + delete conf.name; - opts = { - id : id, - env : new_env - }; - } - else { - opts = id; - } + var argsIndex = 0; + if (opts.rawArgs && (argsIndex = opts.rawArgs.indexOf('--')) >= 0) { + conf.args = opts.rawArgs.slice(argsIndex + 1); + } - that.Client.executeRemote(action_name, opts, function(err, res) { - if (err) { - Common.printError(conf.PREFIX_MSG_ERR + 'Process %s not found', id); - return next('Process not found'); - } + var appConf = Common.verifyConfs(conf)[0]; - if (action_name == 'restartProcessId') { - that.Client.notifyGod('restart', id); - } else if (action_name == 'deleteProcessId') { - that.Client.notifyGod('delete', id); - } else if (action_name == 'stopProcessId') { - that.Client.notifyGod('stop', id); - } else if (action_name == 'reloadProcessId') { - that.Client.notifyGod('reload', id); - } else if (action_name == 'softReloadProcessId') { - that.Client.notifyGod('graceful reload', id); - } + if (appConf instanceof Error) { + Common.printError('Error while transforming CamelCase args to underscore'); + return appConf; + } - if (!Array.isArray(res)) - res = [res]; + if (argsIndex == -1) + delete appConf.args; + if (appConf.name == 'undefined') + delete appConf.name; - // Filter return - res.forEach(function(proc) { - Common.printOut(conf.PREFIX_MSG + '[%s](%d) \u2713', proc.pm2_env ? proc.pm2_env.name : process_name, id); + delete appConf.exec_mode; - if (!proc.pm2_env) return false; + if (util.isArray(appConf.watch) && appConf.watch.length === 0) { + if (!~opts.rawArgs.indexOf('--watch')) + delete appConf.watch + } - ret.push({ - name : proc.pm2_env.name, - pm_id : proc.pm2_env.pm_id, - status : proc.pm2_env.status, - restart_time : proc.pm2_env.restart_time, - pm2_env : { - name : proc.pm2_env.name, - pm_id : proc.pm2_env.pm_id, - status : proc.pm2_env.status, - restart_time : proc.pm2_env.restart_time, - env : proc.pm2_env.env - } - }); - }); + // Options set via environment variables + if (process.env.PM2_DEEP_MONITORING) + appConf.deep_monitoring = true; + + // Force deletion of defaults values set by commander + // to avoid overriding specified configuration by user + if (appConf.treekill === true) + delete appConf.treekill; + if (appConf.pmx === true) + delete appConf.pmx; + if (appConf.vizion === true) + delete appConf.vizion; + if (appConf.automation === true) + delete appConf.automation; + if (appConf.autorestart === true) + delete appConf.autorestart; - return next(); - }); - }, function(err) { - if (err) return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT); - return cb ? cb(null, ret) : that.speedList(); - }); + return appConf; } - if (process_name == 'all') { - that.Client.getAllProcessId(function(err, ids) { + getProcessIdByName (name, cb) { + var that = this; + + this.Client.getProcessIdByName(name, function(err, id) { if (err) { Common.printError(err); return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT); } - if (!ids || ids.length === 0) { - Common.printError(conf.PREFIX_MSG_WARNING + 'No process found'); - return cb ? cb(new Error('process name not found')) : that.exitCli(conf.ERROR_EXIT); - } - - return processIds(ids, cb); + console.log(id); + return cb ? cb(null, id) : that.exitCli(conf.SUCCESS_EXIT); }); } - // operate using regex - else if (isNaN(process_name) && process_name[0] === '/' && process_name[process_name.length - 1] === '/') { - var regex = new RegExp(process_name.replace(/\//g, '')); + + /** + * Description + * @method jlist + * @param {} debug + * @return + */ + jlist (debug) { + var that = this; that.Client.executeRemote('getMonitorData', {}, function(err, list) { if (err) { - Common.printError('Error retrieving process list: ' + err); - return cb(err); - } - var found_proc = []; - list.forEach(function(proc) { - if (regex.test(proc.pm2_env.name)) { - found_proc.push(proc.pm_id); - } - }); - - if (found_proc.length === 0) { - Common.printError(conf.PREFIX_MSG_WARNING + 'No process found'); - return cb ? cb(new Error('process name not found')) : that.exitCli(conf.ERROR_EXIT); + Common.printError(err); + that.exitCli(conf.ERROR_EXIT); } - return processIds(found_proc, cb); - }); - } - else if (isNaN(process_name)) { - /** - * We can not stop or delete a module but we can restart it - * to refresh configuration variable - */ - var allow_module_restart = action_name == 'restartProcessId' ? true : false; - - that.Client.getProcessIdByName(process_name, allow_module_restart, function(err, ids) { - if (err) { - Common.printError(err); - return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT); + if (debug) { + process.stdout.write(util.inspect(list, false, null, false)); } - if (!ids || ids.length === 0) { - Common.printError(conf.PREFIX_MSG_ERR + 'Process %s not found', process_name); - return cb ? cb(new Error('process name not found')) : that.exitCli(conf.ERROR_EXIT); + else { + process.stdout.write(JSON.stringify(list)); } - /** - * Determine if the process to restart is a module - * if yes load configuration variables and merge with the current environment - */ - var additional_env = Modularizer.getAdditionalConf(process_name); - util._extend(envs, additional_env); + that.exitCli(conf.SUCCESS_EXIT); - return processIds(ids, cb); - }); - } else { - // Check if application name as number is an app name - that.Client.getProcessIdByName(process_name, function(err, ids) { - if (ids.length > 0) - return processIds(ids, cb); - // Else operate on pm id - return processIds([process_name], cb); }); } -}; - -/** - * Converts CamelCase Commander.js arguments - * to Underscore - * (nodeArgs -> node_args) - */ -API.prototype._handleAttributeUpdate = function(opts) { - var conf = Config.transCMDToConf(opts); - var that = this; - - if (typeof(conf.name) != 'string') - delete conf.name; - - var argsIndex = 0; - if (opts.rawArgs && (argsIndex = opts.rawArgs.indexOf('--')) >= 0) { - conf.args = opts.rawArgs.slice(argsIndex + 1); - } - var appConf = Common.verifyConfs(conf)[0]; - - if (appConf instanceof Error) { - Common.printError('Error while transforming CamelCase args to underscore'); - return appConf; - } + /** + * Description + * @method speedList + * @return + */ + speedList (code) { + var that = this; - if (argsIndex == -1) - delete appConf.args; - if (appConf.name == 'undefined') - delete appConf.name; + // Do nothing if PM2 called programmatically and not called from CLI (also in exitCli) + if (conf.PM2_PROGRAMMATIC && process.env.PM2_USAGE != 'CLI') return false; - delete appConf.exec_mode; + that.Client.executeRemote('getMonitorData', {}, function(err, list) { + if (err) { + if (gl_retry == 0) { + gl_retry += 1; + return setTimeout(that.speedList.bind(that), 1400); + } + console.error('Error retrieving process list: %s.\nA process seems to be on infinite loop, retry in 5 seconds',err); + return that.exitCli(conf.ERROR_EXIT); + } + if (process.stdout.isTTY === false) { + UX.miniDisplay(list); + } + else if (commander.miniList && !commander.silent) + UX.miniDisplay(list); + else if (!commander.silent) { + if (that.gl_interact_infos) { + Common.printOut(chalk.green.bold('●') + ' Agent Online | Dashboard Access: ' + chalk.bold('https://app.keymetrics.io/#/r/%s') + ' | Server name: %s', that.gl_interact_infos.public_key, that.gl_interact_infos.machine_name); + } + UX.dispAsTable(list, commander); + Common.printOut(chalk.white.italic(' Use `pm2 show ` to get more details about an app')); + } - if (util.isArray(appConf.watch) && appConf.watch.length === 0) { - if (!~opts.rawArgs.indexOf('--watch')) - delete appConf.watch + if (that.Client.daemon_mode == false) { + Common.printOut('[--no-daemon] Continue to stream logs'); + Common.printOut('[--no-daemon] Exit on target PM2 exit pid=' + fs.readFileSync(conf.PM2_PID_FILE_PATH).toString()); + global._auto_exit = true; + return that.streamLogs('all', 0, false, 'HH:mm:ss', false); + } + else if (commander.attach === true) { + return that.streamLogs('all', 0, false, null, false); + } + else { + return that.exitCli(code ? code : conf.SUCCESS_EXIT); + } + }); } - // Options set via environment variables - if (process.env.PM2_DEEP_MONITORING) - appConf.deep_monitoring = true; - - // Force deletion of defaults values set by commander - // to avoid overriding specified configuration by user - if (appConf.treekill === true) - delete appConf.treekill; - if (appConf.pmx === true) - delete appConf.pmx; - if (appConf.vizion === true) - delete appConf.vizion; - if (appConf.automation === true) - delete appConf.automation; - if (appConf.autorestart === true) - delete appConf.autorestart; - - return appConf; -}; - -API.prototype.getProcessIdByName = function(name, cb) { - var that = this; + /** + * Scale up/down a process + * @method scale + */ + scale (app_name, number, cb) { + var that = this; - this.Client.getProcessIdByName(name, function(err, id) { - if (err) { - Common.printError(err); - return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT); + function addProcs(proc, value, cb) { + (function ex(proc, number) { + if (number-- === 0) return cb(); + Common.printOut(conf.PREFIX_MSG + 'Scaling up application'); + that.Client.executeRemote('duplicateProcessId', proc.pm2_env.pm_id, ex.bind(this, proc, number)); + })(proc, number); } - console.log(id); - return cb ? cb(null, id) : that.exitCli(conf.SUCCESS_EXIT); - }); -}; -/** - * Description - * @method jlist - * @param {} debug - * @return - */ -API.prototype.jlist = function(debug) { - var that = this; + function rmProcs(procs, value, cb) { + var i = 0; - that.Client.executeRemote('getMonitorData', {}, function(err, list) { - if (err) { - Common.printError(err); - that.exitCli(conf.ERROR_EXIT); + (function ex(procs, number) { + if (number++ === 0) return cb(); + that._operate('deleteProcessId', procs[i++].pm2_env.pm_id, ex.bind(this, procs, number)); + })(procs, number); } - if (debug) { - process.stdout.write(util.inspect(list, false, null, false)); - } - else { - process.stdout.write(JSON.stringify(list)); + function end() { + return cb ? cb(null, {success:true}) : that.speedList(); } - that.exitCli(conf.SUCCESS_EXIT); - - }); -}; - -var gl_retry = 0; + this.Client.getProcessByName(app_name, function(err, procs) { + if (err) { + Common.printError(err); + return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT); + } -/** - * Description - * @method speedList - * @return - */ -API.prototype.speedList = function(code) { - var that = this; + if (!procs || procs.length === 0) { + Common.printError(conf.PREFIX_MSG_ERR + 'Application %s not found', app_name); + return cb ? cb(new Error('App not found')) : that.exitCli(conf.ERROR_EXIT); + } - // Do nothing if PM2 called programmatically and not called from CLI (also in exitCli) - if (conf.PM2_PROGRAMMATIC && process.env.PM2_USAGE != 'CLI') return false; + var proc_number = procs.length; - that.Client.executeRemote('getMonitorData', {}, function(err, list) { - if (err) { - if (gl_retry == 0) { - gl_retry += 1; - return setTimeout(that.speedList.bind(that), 1400); + if (typeof(number) === 'string' && number.indexOf('+') >= 0) { + number = parseInt(number, 10); + return addProcs(procs[0], number, end); } - console.error('Error retrieving process list: %s.\nA process seems to be on infinite loop, retry in 5 seconds',err); - return that.exitCli(conf.ERROR_EXIT); - } - if (process.stdout.isTTY === false) { - UX.miniDisplay(list); - } - else if (commander.miniList && !commander.silent) - UX.miniDisplay(list); - else if (!commander.silent) { - if (that.gl_interact_infos) { - Common.printOut(chalk.green.bold('●') + ' Agent Online | Dashboard Access: ' + chalk.bold('https://app.keymetrics.io/#/r/%s') + ' | Server name: %s', that.gl_interact_infos.public_key, that.gl_interact_infos.machine_name); + else if (typeof(number) === 'string' && number.indexOf('-') >= 0) { + number = parseInt(number, 10); + return rmProcs(procs[0], number, end); } - UX.dispAsTable(list, commander); - Common.printOut(chalk.white.italic(' Use `pm2 show ` to get more details about an app')); - } - - if (that.Client.daemon_mode == false) { - Common.printOut('[--no-daemon] Continue to stream logs'); - Common.printOut('[--no-daemon] Exit on target PM2 exit pid=' + fs.readFileSync(conf.PM2_PID_FILE_PATH).toString()); - global._auto_exit = true; - return that.streamLogs('all', 0, false, 'HH:mm:ss', false); - } - else if (commander.attach === true) { - return that.streamLogs('all', 0, false, null, false); - } - else { - return that.exitCli(code ? code : conf.SUCCESS_EXIT); - } - }); -} - -/** - * Scale up/down a process - * @method scale - */ -API.prototype.scale = function(app_name, number, cb) { - var that = this; - - function addProcs(proc, value, cb) { - (function ex(proc, number) { - if (number-- === 0) return cb(); - Common.printOut(conf.PREFIX_MSG + 'Scaling up application'); - that.Client.executeRemote('duplicateProcessId', proc.pm2_env.pm_id, ex.bind(this, proc, number)); - })(proc, number); - } - - function rmProcs(procs, value, cb) { - var i = 0; - - (function ex(procs, number) { - if (number++ === 0) return cb(); - that._operate('deleteProcessId', procs[i++].pm2_env.pm_id, ex.bind(this, procs, number)); - })(procs, number); - } + else { + number = parseInt(number, 10); + number = number - proc_number; - function end() { - return cb ? cb(null, {success:true}) : that.speedList(); + if (number < 0) + return rmProcs(procs, number, end); + else if (number > 0) + return addProcs(procs[0], number, end); + else { + Common.printError(conf.PREFIX_MSG_ERR + 'Nothing to do'); + return cb ? cb(new Error('Same process number')) : that.exitCli(conf.ERROR_EXIT); + } + } + }); } - this.Client.getProcessByName(app_name, function(err, procs) { - if (err) { - Common.printError(err); - return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT); - } - - if (!procs || procs.length === 0) { - Common.printError(conf.PREFIX_MSG_ERR + 'Application %s not found', app_name); - return cb ? cb(new Error('App not found')) : that.exitCli(conf.ERROR_EXIT); - } - - var proc_number = procs.length; + /** + * Description + * @method describeProcess + * @param {} pm2_id + * @return + */ + describe (pm2_id, cb) { + var that = this; - if (typeof(number) === 'string' && number.indexOf('+') >= 0) { - number = parseInt(number, 10); - return addProcs(procs[0], number, end); - } - else if (typeof(number) === 'string' && number.indexOf('-') >= 0) { - number = parseInt(number, 10); - return rmProcs(procs[0], number, end); - } - else { - number = parseInt(number, 10); - number = number - proc_number; + var found_proc = []; - if (number < 0) - return rmProcs(procs, number, end); - else if (number > 0) - return addProcs(procs[0], number, end); - else { - Common.printError(conf.PREFIX_MSG_ERR + 'Nothing to do'); - return cb ? cb(new Error('Same process number')) : that.exitCli(conf.ERROR_EXIT); + that.Client.executeRemote('getMonitorData', {}, function(err, list) { + if (err) { + Common.printError('Error retrieving process list: ' + err); + that.exitCli(conf.ERROR_EXIT); } - } - }); -}; -/** - * Description - * @method describeProcess - * @param {} pm2_id - * @return - */ -API.prototype.describe = function(pm2_id, cb) { - var that = this; - - var found_proc = []; + list.forEach(function(proc) { + if ((!isNaN(pm2_id) && proc.pm_id == pm2_id) || + (typeof(pm2_id) === 'string' && proc.name == pm2_id)) { + found_proc.push(proc); + } + }); - that.Client.executeRemote('getMonitorData', {}, function(err, list) { - if (err) { - Common.printError('Error retrieving process list: ' + err); - that.exitCli(conf.ERROR_EXIT); - } + if (found_proc.length === 0) { + Common.printError(conf.PREFIX_MSG_WARNING + '%s doesn\'t exist', pm2_id); + return cb ? cb(null, []) : that.exitCli(conf.ERROR_EXIT); + } - list.forEach(function(proc) { - if ((!isNaN(pm2_id) && proc.pm_id == pm2_id) || - (typeof(pm2_id) === 'string' && proc.name == pm2_id)) { - found_proc.push(proc); + if (!cb) { + found_proc.forEach(function(proc) { + UX.describeTable(proc); + }); } + + return cb ? cb(null, found_proc) : that.exitCli(conf.SUCCESS_EXIT); }); + } - if (found_proc.length === 0) { - Common.printError(conf.PREFIX_MSG_WARNING + '%s doesn\'t exist', pm2_id); - return cb ? cb(null, []) : that.exitCli(conf.ERROR_EXIT); - } + /** + * API method to perform a deep update of PM2 + * @method deepUpdate + */ + deepUpdate (cb) { + var that = this; - if (!cb) { - found_proc.forEach(function(proc) { - UX.describeTable(proc); - }); - } + Common.printOut(conf.PREFIX_MSG + 'Updating PM2...'); - return cb ? cb(null, found_proc) : that.exitCli(conf.SUCCESS_EXIT); - }); + var exec = require('shelljs').exec; + var child = exec("npm i -g pm2@latest; pm2 update", {async : true}); + + child.stdout.on('end', function() { + Common.printOut(conf.PREFIX_MSG + 'PM2 successfully updated'); + cb ? cb(null, {success:true}) : that.exitCli(conf.SUCCESS_EXIT); + }); + } }; -/** - * API method to perform a deep update of PM2 - * @method deepUpdate - */ -API.prototype.deepUpdate = function(cb) { - var that = this; +API.prototype.close = API.prototype.disconnect; +API.prototype.kill = API.prototype.killDaemon; - Common.printOut(conf.PREFIX_MSG + 'Updating PM2...'); - var exec = require('shelljs').exec; - var child = exec("npm i -g pm2@latest; pm2 update", {async : true}); +////////////////////////// +// Load all API methods // +////////////////////////// + +require('./API/Extra.js')(API); +require('./API/Interaction.js')(API); +require('./API/Deploy.js')(API); +require('./API/Modules/Modules.js')(API); +require('./API/Keymetrics/cli-api.js')(API); +require('./API/Configuration.js')(API); +require('./API/Version.js')(API); +require('./API/Startup.js')(API); +require('./API/LogManagement.js')(API); +require('./API/Containerizer.js')(API); - child.stdout.on('end', function() { - Common.printOut(conf.PREFIX_MSG + 'PM2 successfully updated'); - cb ? cb(null, {success:true}) : that.exitCli(conf.SUCCESS_EXIT); - }); -}; module.exports = API; From 0cab8880ffa362cf27ab7d7b6a64d6b478dce7cd Mon Sep 17 00:00:00 2001 From: vince Date: Tue, 27 Feb 2018 12:28:43 +0100 Subject: [PATCH 12/32] refactor: drop some 0.x patch --- lib/API.js | 30 +++++------------------------- 1 file changed, 5 insertions(+), 25 deletions(-) diff --git a/lib/API.js b/lib/API.js index c42cf8370..7c7b997bd 100644 --- a/lib/API.js +++ b/lib/API.js @@ -185,22 +185,10 @@ class API { if (that.pm2_home.indexOf('.pm2') > -1) return cb(new Error('Destroy is not a allowed method on .pm2')); - if (fs.accessSync) { - fs.access(test_path, fs.R_OK, function(err) { - if (err) return cb(err); - debug('Deleting temporary folder %s', that.pm2_home); - exec(cmd, cb); - }); - return false; - } - - // Support for Node 0.10 - fs.exists(test_path, function(exist) { - if (exist) { - debug('Deleting temporary folder %s', that.pm2_home); - exec(cmd, cb); - } - return cb(null); + fs.access(test_path, fs.R_OK, function(err) { + if (err) return cb(err); + debug('Deleting temporary folder %s', that.pm2_home); + exec(cmd, cb); }); }); } @@ -858,15 +846,7 @@ class API { } else { var data = null; - var isAbsolute = false - - //node 0.11 compatibility #2815 - if (typeof path.isAbsolute === 'function') { - isAbsolute = path.isAbsolute(file) - } else { - isAbsolute = require('./tools/IsAbsolute.js')(file) - } - + var isAbsolute = path.isAbsolute(file) var file_path = isAbsolute ? file : path.join(that.cwd, file); debug('Resolved filepath %s', file_path); From 047aa494d5c4dd4342915766b54d673db0d5cdf1 Mon Sep 17 00:00:00 2001 From: vince Date: Tue, 27 Feb 2018 15:47:37 +0100 Subject: [PATCH 13/32] refactor: change safety var to const --- lib/API.js | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/lib/API.js b/lib/API.js index 7c7b997bd..60c69380d 100644 --- a/lib/API.js +++ b/lib/API.js @@ -4,14 +4,14 @@ * can be found in the LICENSE file. */ -var commander = require('commander'); -var fs = require('fs'); -var path = require('path'); -var async = require('async'); -var debug = require('debug')('pm2:cli'); -var util = require('util'); -var chalk = require('chalk'); -var fclone = require('fclone'); +const commander = require('commander'); +const fs = require('fs'); +const path = require('path'); +const async = require('async'); +const debug = require('debug')('pm2:cli'); +const util = require('util'); +const chalk = require('chalk'); +const fclone = require('fclone'); var conf = require('../constants.js'); var Client = require('./Client'); @@ -49,7 +49,7 @@ class API { constructor (opts) { if (!opts) opts = {}; - var that = this; + const that = this; this.daemon_mode = typeof(opts.daemon_mode) == 'undefined' ? true : opts.daemon_mode; this.pm2_home = conf.PM2_ROOT_PATH; @@ -78,8 +78,8 @@ class API { } else if (opts.independent == true && conf.IS_WINDOWS === false) { // Create an unique pm2 instance - var crypto = require('crypto'); - var random_file = crypto.randomBytes(8).toString('hex'); + const crypto = require('crypto'); + const random_file = crypto.randomBytes(8).toString('hex'); this.pm2_home = path.join('/tmp', random_file); // If we dont explicitly tell to have a daemon @@ -111,7 +111,7 @@ class API { this.gl_is_km_linked = false; try { - var pid = fs.readFileSync(conf.INTERACTOR_PID_PATH); + const pid = fs.readFileSync(conf.INTERACTOR_PID_PATH); pid = parseInt(pid.toString().trim()); process.kill(pid, 0); that.gl_is_km_linked = true; From 6d8f0dfae8106deb2fee0a7ae15b6ca9802a066d Mon Sep 17 00:00:00 2001 From: vince Date: Tue, 27 Feb 2018 16:15:41 +0100 Subject: [PATCH 14/32] refactor: create alias method instead of modify prototype --- lib/API.js | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/lib/API.js b/lib/API.js index 60c69380d..0c9519fd8 100644 --- a/lib/API.js +++ b/lib/API.js @@ -210,6 +210,14 @@ class API { }); }; + /** + * Alias on disconnect + * @param cb + */ + close (cb) { + this.disconnect(cb); + } + /** * Launch modules * @@ -617,6 +625,10 @@ class API { }); } + kill (cb) { + this.killDaemon(cb); + } + ///////////////////// // Private methods // ///////////////////// @@ -1653,9 +1665,6 @@ class API { } }; -API.prototype.close = API.prototype.disconnect; -API.prototype.kill = API.prototype.killDaemon; - ////////////////////////// // Load all API methods // From d322dd00de0f527224c027b4fec5e86f12fd69ed Mon Sep 17 00:00:00 2001 From: vince Date: Tue, 27 Feb 2018 16:53:40 +0100 Subject: [PATCH 15/32] refactor: add node 4.x support --- lib/API.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/API.js b/lib/API.js index 0c9519fd8..d950d1b06 100644 --- a/lib/API.js +++ b/lib/API.js @@ -3,6 +3,7 @@ * Use of this source code is governed by a license that * can be found in the LICENSE file. */ +'use strict'; const commander = require('commander'); const fs = require('fs'); From bb57c76d4191343925013d4353299092d80732c9 Mon Sep 17 00:00:00 2001 From: vince Date: Wed, 28 Feb 2018 18:39:40 +0100 Subject: [PATCH 16/32] refactor: drop gracefullreload --- README.md | 1 - bin/pm2 | 11 +------ examples/misc-examples/graceful-exit.js | 2 +- examples/sourcemap-auto-resolve/API.js | 27 ----------------- lib/API.js | 27 ----------------- lib/API/Version.js | 10 ------- lib/God/Reload.js | 2 +- lib/Interactor/RemoteActions/Pm2Actions.js | 1 - test/bash/app-config-update.sh | 11 +------ test/bash/gracefulReload.sh | 35 ---------------------- test/bash/gracefulReload2.sh | 28 ----------------- test/bash/gracefulReload3.sh | 22 -------------- test/bash/json-file.sh | 7 +---- test/bash/reload.sh | 2 -- test/fixtures/graceful-exit-no-listen.js | 2 +- test/fixtures/graceful-exit-send.js | 2 +- test/fixtures/graceful-exit.js | 2 +- test/interface/remote.mocha.js | 24 --------------- test/pm2_behavior_tests.sh | 6 ---- test/programmatic/signals.js | 15 ---------- 20 files changed, 8 insertions(+), 229 deletions(-) delete mode 100644 test/bash/gracefulReload.sh delete mode 100644 test/bash/gracefulReload2.sh delete mode 100644 test/bash/gracefulReload3.sh diff --git a/README.md b/README.md index 1ef3279ae..21a9dc853 100644 --- a/README.md +++ b/README.md @@ -293,7 +293,6 @@ $ pm2 reset [app-name] # Reset all counters $ pm2 stop all # Stop all apps $ pm2 stop 0 # Stop process with id 0 $ pm2 restart all # Restart all apps -$ pm2 gracefulReload all # Gracefully reload all apps in cluster mode $ pm2 delete all # Kill and delete all apps $ pm2 delete 0 # Delete app with id 0 diff --git a/bin/pm2 b/bin/pm2 index 05072f4bc..809177b03 100755 --- a/bin/pm2 +++ b/bin/pm2 @@ -155,7 +155,7 @@ function checkCompletion(){ return data.short; }), data); // array containing commands after which process name should be listed - var cmdProcess = ['stop', 'restart', 'scale', 'reload', 'gracefulReload', 'delete', 'reset', 'pull', 'forward', 'backward', 'logs', 'describe', 'desc', 'show']; + var cmdProcess = ['stop', 'restart', 'scale', 'reload', 'delete', 'reset', 'pull', 'forward', 'backward', 'logs', 'describe', 'desc', 'show']; if (cmdProcess.indexOf(data.prev) > -1) { pm2.list(function(err, list){ @@ -390,15 +390,6 @@ commander.command('reload ') pm2.reload(pm2_id, commander); }); -// -// Reload process(es) -// -commander.command('gracefulReload ') - .description('gracefully reload a process. Send a "shutdown" message to close all connections.') - .action(function(pm2_id) { - pm2.gracefulReload(pm2_id, commander); - }); - commander.command('id ') .description('get process id by name') .action(function(name) { diff --git a/examples/misc-examples/graceful-exit.js b/examples/misc-examples/graceful-exit.js index df593d447..a35fc3e85 100644 --- a/examples/misc-examples/graceful-exit.js +++ b/examples/misc-examples/graceful-exit.js @@ -2,7 +2,7 @@ /* * Example of graceful exit * - * $ pm2 gracefulReload all + * $ pm2 reload all */ process.on('message', function(msg) { diff --git a/examples/sourcemap-auto-resolve/API.js b/examples/sourcemap-auto-resolve/API.js index ed62bdecb..544138794 100644 --- a/examples/sourcemap-auto-resolve/API.js +++ b/examples/sourcemap-auto-resolve/API.js @@ -399,33 +399,6 @@ API.prototype.update = function(cb) { return false; }; -/** - * Graceful Reload an application - * - * @param {String} process_name Application Name or All - * @param {Object} opts Options - * @param {Function} cb Callback - */ -API.prototype.gracefulReload = function(process_name, opts, cb) { - var that = this; - - if (typeof(opts) == "function") { - cb = opts; - opts = {}; - } - - //Common.printOut(conf.PREFIX_MSG_WARNING + chalk.bold.yellow('Warning gracefulReload will be soon deprecated')); - //Common.printOut(conf.PREFIX_MSG_WARNING + chalk.bold.yellow('Use http://pm2.keymetrics.io/docs/usage/signals-clean-restart/ instead')); - - if (Common.isConfigFile(process_name)) - that._startJson(process_name, commander, 'softReloadProcessId'); - else { - if (opts && !opts.updateEnv) - Common.printOut(IMMUTABLE_MSG); - that._operate('softReloadProcessId', process_name, opts, cb); - } -}; - /** * Reload an application * diff --git a/lib/API.js b/lib/API.js index d950d1b06..0296c2d31 100644 --- a/lib/API.js +++ b/lib/API.js @@ -402,33 +402,6 @@ class API { return false; } - /** - * Graceful Reload an application - * - * @param {String} process_name Application Name or All - * @param {Object} opts Options - * @param {Function} cb Callback - */ - gracefulReload (process_name, opts, cb) { - var that = this; - - if (typeof(opts) == "function") { - cb = opts; - opts = {}; - } - - //Common.printOut(conf.PREFIX_MSG_WARNING + chalk.bold.yellow('Warning gracefulReload will be soon deprecated')); - //Common.printOut(conf.PREFIX_MSG_WARNING + chalk.bold.yellow('Use http://pm2.keymetrics.io/docs/usage/signals-clean-restart/ instead')); - - if (Common.isConfigFile(process_name)) - that._startJson(process_name, commander, 'softReloadProcessId'); - else { - if (opts && !opts.updateEnv) - Common.printOut(IMMUTABLE_MSG); - that._operate('softReloadProcessId', process_name, opts, cb); - } - } - /** * Reload an application * diff --git a/lib/API/Version.js b/lib/API/Version.js index 8f5bb93ba..a32de64f9 100644 --- a/lib/API/Version.js +++ b/lib/API/Version.js @@ -366,16 +366,6 @@ module.exports = function(CLI) { this._pull({process_name: process_name, action: 'reload'}, cb); }; - /** - * CLI method for updating a repository - * @method pullAndGracefulReload - * @param {string} process_name name of processes to pull - * @return - */ - CLI.prototype.pullAndGracefulReload = function (process_name, cb) { - this._pull({process_name: process_name, action: 'gracefulReload'}, cb); - }; - /** * CLI method for updating a repository to a specific commit id * @method pullCommitId diff --git a/lib/God/Reload.js b/lib/God/Reload.js index d55cad1cc..5472f3b4e 100644 --- a/lib/God/Reload.js +++ b/lib/God/Reload.js @@ -174,7 +174,7 @@ function hardReload(God, id, wait_msg, cb) { module.exports = function(God) { /** - * GracefulReload + * Reload * @method softReloadProcessId * @param {} id * @param {} cb diff --git a/lib/Interactor/RemoteActions/Pm2Actions.js b/lib/Interactor/RemoteActions/Pm2Actions.js index a6de7c611..b5edd93bb 100644 --- a/lib/Interactor/RemoteActions/Pm2Actions.js +++ b/lib/Interactor/RemoteActions/Pm2Actions.js @@ -21,7 +21,6 @@ var Password = require('../Password.js'); var PM2_REMOTE_METHOD_ALLOWED = { 'restart' : {}, 'reload' : {}, - 'gracefulReload' : {}, 'reset' : {}, 'scale' : {}, diff --git a/test/bash/app-config-update.sh b/test/bash/app-config-update.sh index 8ef0a532c..1807110eb 100644 --- a/test/bash/app-config-update.sh +++ b/test/bash/app-config-update.sh @@ -58,14 +58,10 @@ $pm2 reload app-config-update/echo.js --node-args="--harmony" $pm2 prettylist | grep "node_args: \[ '--harmony' \]" spec "Should application have one node argument" -$pm2 gracefulReload app-config-update/echo.js --node-args="--harmony" -$pm2 prettylist | grep "node_args: \[ '--harmony' \]" -spec "Should application have two node arguments" - $pm2 prettylist | grep "node_args" spec "Should have found parameter" # Now set node-args to null -$pm2 gracefulReload app-config-update/echo.js --node-args=null +$pm2 reload app-config-update/echo.js --node-args=null # Should not find node_args anymore $pm2 prettylist | grep "node_args" ispec "Should have deleted cli parameter when passing null" @@ -74,8 +70,3 @@ $pm2 reload echo --name="new-name" $pm2 reset all $pm2 restart new-name should 'should reload processes with new name' 'restart_time: 1' 1 - -$pm2 gracefulReload new-name --name="new-name-2" -$pm2 reset all -$pm2 restart new-name-2 -should 'should graceful reload processes with new name' 'restart_time: 1' 1 diff --git a/test/bash/gracefulReload.sh b/test/bash/gracefulReload.sh deleted file mode 100644 index e7ed9fce5..000000000 --- a/test/bash/gracefulReload.sh +++ /dev/null @@ -1,35 +0,0 @@ -#!/usr/bin/env bash - -SRC=$(cd $(dirname "$0"); pwd) -source "${SRC}/include.sh" - -cd $file_path - -echo "################## GRACEFUL RELOAD ###################" - -############### - -echo "Launching" -$pm2 start graceful-exit.js -i 4 --name="graceful" -o "grace.log" -e "grace-err.log" -should 'should start processes' 'online' 4 - -OUT_LOG=`$pm2 prettylist | grep -m 1 -E "pm_out_log_path:" | sed "s/.*'\([^']*\)',/\1/"` -cat /dev/null > $OUT_LOG - -#### Graceful reload all - -$pm2 gracefulReload all - -OUT=`grep "Finished closing connections" "$OUT_LOG" | wc -l` -[ $OUT -eq 1 ] || fail "Process not restarted gracefuly" -success "Process restarted gracefuly" - - -cat /dev/null > $OUT_LOG - -#### Graceful reload name -$pm2 gracefulReload graceful - -OUT=`grep "Finished closing connections" "$OUT_LOG" | wc -l` -[ $OUT -eq 1 ] || fail "Process not restarted gracefuly" -success "Process restarted gracefuly" diff --git a/test/bash/gracefulReload2.sh b/test/bash/gracefulReload2.sh deleted file mode 100644 index b7c25fdd4..000000000 --- a/test/bash/gracefulReload2.sh +++ /dev/null @@ -1,28 +0,0 @@ -#!/usr/bin/env bash - -SRC=$(cd $(dirname "$0"); pwd) -source "${SRC}/include.sh" - -cd $file_path - -echo "################## GRACEFUL RELOAD 2 ###################" - -echo "Launching" -$pm2 start graceful-exit-no-listen.js -i 2 --name="graceful2" -o "grace2.log" -e "grace-err2.log" -should 'should start processes' 'online' 2 - -OUT_LOG=`$pm2 prettylist | grep -m 1 -E "pm_out_log_path:" | sed "s/.*'\([^']*\)',/\1/"` -cat /dev/null > $OUT_LOG - -#### Graceful reload name -$pm2 gracefulReload graceful2 - -echo "PATH: $OUT_LOG" - -TEXT=$(cat $OUT_LOG) - -echo "TEXT: $TEXT" - -OUT=`grep "Finished closing connections" "$OUT_LOG" | wc -l` -[ $OUT -eq 1 ] || fail "Non-listening process not restarted gracefuly" -success "Non-listening process restarted gracefuly" diff --git a/test/bash/gracefulReload3.sh b/test/bash/gracefulReload3.sh deleted file mode 100644 index 307d74f6b..000000000 --- a/test/bash/gracefulReload3.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env bash - -SRC=$(cd $(dirname "$0"); pwd) -source "${SRC}/include.sh" - -cd $file_path - -echo "################## GRACEFUL RELOAD 3 ###################" - -echo "Launching" -$pm2 start graceful-exit-send.js -i 2 --name="graceful3" -o "grace3.log" -e "grace-err3.log" -should 'should start processes' 'online' 2 - -OUT_LOG=`$pm2 prettylist | grep -m 1 -E "pm_out_log_path:" | sed "s/.*'\([^']*\)',/\1/"` -cat /dev/null > $OUT_LOG - -#### Graceful reload name -$pm2 gracefulReload graceful3 - -OUT=`grep "Finished closing connections" "$OUT_LOG" | wc -l` -[ $OUT -eq 1 ] || fail "Process that sends 'online' not restarted gracefuly" -success "Process that sends 'online' restarted gracefuly" diff --git a/test/bash/json-file.sh b/test/bash/json-file.sh index 50c3ff352..08ce5543b 100644 --- a/test/bash/json-file.sh +++ b/test/bash/json-file.sh @@ -36,18 +36,13 @@ sleep 1 should 'should reload processes' 'online' 6 should 'should all script been restarted one time' 'restart_time: 2' 6 -$pm2 gracefulReload all.json -sleep 1 -should 'should graceful reload processes' 'online' 6 -should 'should all script been restarted one time' 'restart_time: 3' 6 - ## ## Smart restart ## $pm2 start all.json sleep 1 should 'should smart restart processes' 'online' 6 -should 'should all script been restarted one time' 'restart_time: 4' 6 +should 'should all script been restarted one time' 'restart_time: 3' 6 $pm2 stop all.json sleep 1 diff --git a/test/bash/reload.sh b/test/bash/reload.sh index d659a1b6c..88ad94165 100644 --- a/test/bash/reload.sh +++ b/test/bash/reload.sh @@ -50,8 +50,6 @@ $pm2 restart delayed_exit.js should 'should restart processes' 'restart_time: 1' 2 $pm2 reload delayed_exit.js should 'should restart processes' 'restart_time: 2' 2 -$pm2 gracefulReload delayed_exit.js -should 'should restart processes' 'restart_time: 3' 2 $pm2 kill $pm2 start child.js -i 4 diff --git a/test/fixtures/graceful-exit-no-listen.js b/test/fixtures/graceful-exit-no-listen.js index 37cfba2dc..814d3845c 100644 --- a/test/fixtures/graceful-exit-no-listen.js +++ b/test/fixtures/graceful-exit-no-listen.js @@ -2,7 +2,7 @@ /* * Example of graceful exit that does not listen * - * $ pm2 gracefulReload all + * $ pm2 reload all */ process.on('message', function(msg) { diff --git a/test/fixtures/graceful-exit-send.js b/test/fixtures/graceful-exit-send.js index 7142c3f5d..94d461f00 100644 --- a/test/fixtures/graceful-exit-send.js +++ b/test/fixtures/graceful-exit-send.js @@ -2,7 +2,7 @@ /* * Example of graceful exit that does not listen but sends 'online' * - * $ pm2 gracefulReload all + * $ pm2 reload all */ process.on('message', function(msg) { diff --git a/test/fixtures/graceful-exit.js b/test/fixtures/graceful-exit.js index 43e8212a9..5b1461a18 100644 --- a/test/fixtures/graceful-exit.js +++ b/test/fixtures/graceful-exit.js @@ -2,7 +2,7 @@ /* * Example of graceful exit * - * $ pm2 gracefulReload all + * $ pm2 reload all */ process.on('message', function(msg) { diff --git a/test/interface/remote.mocha.js b/test/interface/remote.mocha.js index d1ded5a12..87adbcd41 100644 --- a/test/interface/remote.mocha.js +++ b/test/interface/remote.mocha.js @@ -145,30 +145,6 @@ describe('REMOTE PM2 ACTIONS', function() { }); }); - it('should gracefulRELOAD', function(done) { - send_cmd.once('trigger:pm2:result', function(pck) { - /** - * Once remote command is finished... - */ - - should(pck.ret.err).be.null(); - - pm2.list(function(err, ret) { - ret.forEach(function(proc) { - proc.pm2_env.restart_time.should.eql(3); - }); - }); - - done(); - }); - - send_cmd.emit('cmd', { - _type : 'trigger:pm2:action', - method_name : 'gracefulReload', - parameters : {name : 'child' } - }); - }); - it('should RESET metadata', function(done) { send_cmd.once('trigger:pm2:result', function(pck) { /** diff --git a/test/pm2_behavior_tests.sh b/test/pm2_behavior_tests.sh index 6b1fa6306..9d212a11d 100644 --- a/test/pm2_behavior_tests.sh +++ b/test/pm2_behavior_tests.sh @@ -81,12 +81,6 @@ bash ./test/bash/right-exit-code.sh spec "Verification exit code" bash ./test/bash/log-reload.sh spec "Log reload" -bash ./test/bash/gracefulReload.sh -spec "gracefulReload system 1" -bash ./test/bash/gracefulReload2.sh -spec "gracefulReload system 2" -bash ./test/bash/gracefulReload3.sh -spec "gracefulReload system 3" bash ./test/bash/misc.sh spec "MISC features" bash ./test/bash/fork.sh diff --git a/test/programmatic/signals.js b/test/programmatic/signals.js index 6c9c93b30..fad65206e 100644 --- a/test/programmatic/signals.js +++ b/test/programmatic/signals.js @@ -162,21 +162,6 @@ describe('Signal kill (+delayed)', function() { }); }); - - it('should graceful reload script', function(done) { - setTimeout(function() { - pm2.list(function(err, list) { - list[0].pm2_env.status.should.eql('online'); - list[0].pm2_env.restart_time.should.eql(2); - done(); - }); - }, 1500); - - pm2.gracefulReload('delayed-sigint', function(err, app) { - //done(err); - }); - - }); }); describe('with 4000ms via kill_timeout (json/cli option)', function() { From fadf82a95e0adaa097c82599ed5092c0f1cc81d1 Mon Sep 17 00:00:00 2001 From: Chang Wang Date: Thu, 1 Mar 2018 21:39:54 -0500 Subject: [PATCH 17/32] Update downloads badge to point to graph of downloads --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1ef3279ae..386527097 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ npm version - + NPM Downloads From f2523f6a6b9d8b61ba6ace7b89a0353bee76360b Mon Sep 17 00:00:00 2001 From: vince Date: Fri, 2 Mar 2018 10:22:23 +0100 Subject: [PATCH 18/32] fix: #3485 fix issue when there is empty dump file --- lib/API/Startup.js | 26 +++++++++++++++++++++----- lib/God/ActionMethods.js | 19 ++++++++++++++----- 2 files changed, 35 insertions(+), 10 deletions(-) diff --git a/lib/API/Startup.js b/lib/API/Startup.js index 5249acc5d..5be3e3d63 100644 --- a/lib/API/Startup.js +++ b/lib/API/Startup.js @@ -371,13 +371,26 @@ module.exports = function(CLI) { * @return */ function fin(err) { + + // try to fix issues with empty dump file + // like #3485 + if (env_arr.length === 0) { + // if no process in list don't modify dump file + // process list should not be empty + if(cb) { + return cb(null, {success: true}); + } else { + Common.printOut(cst.PREFIX_MSG + 'Nothing to save !!!'); + Common.printOut(cst.PREFIX_MSG + 'In this case we keep old dump file. To clear dump file you can delete it manually !'); + that.exitCli(cst.SUCCESS_EXIT); + return; + } + } + // Back up dump file try { if (fs.existsSync(cst.DUMP_FILE_PATH)) { - if (fs.existsSync(cst.DUMP_BACKUP_FILE_PATH)) { - fs.unlinkSync(cst.DUMP_BACKUP_FILE_PATH); - } - fs.renameSync(cst.DUMP_FILE_PATH, cst.DUMP_BACKUP_FILE_PATH); + fs.copyFileSync(cst.DUMP_FILE_PATH, cst.DUMP_BACKUP_FILE_PATH); } } catch (e) { console.error(e.stack || e); @@ -390,8 +403,11 @@ module.exports = function(CLI) { } catch (e) { console.error(e.stack || e); try { - fs.unlinkSync(cst.DUMP_FILE_PATH); + // try to backup file + fs.copyFileSync(cst.DUMP_BACKUP_FILE_PATH, cst.DUMP_FILE_PATH); } catch (e) { + // don't keep broken file + fs.unlinkSync(cst.DUMP_FILE_PATH); console.error(e.stack || e); } Common.printOut(cst.PREFIX_MSG_ERR + 'Failed to save dump file in %s', cst.DUMP_FILE_PATH); diff --git a/lib/God/ActionMethods.js b/lib/God/ActionMethods.js index 65cb64157..6b577d69a 100644 --- a/lib/God/ActionMethods.js +++ b/lib/God/ActionMethods.js @@ -137,13 +137,19 @@ module.exports = function(God) { } function fin(err) { + + // try to fix issues with empty dump file + // like #3485 + if (process_list.length === 0) { + // if no process in list don't modify dump file + // process list should not be empty + return cb(null, {success:true, process_list: process_list}); + } + // Back up dump file try { if (fs.existsSync(cst.DUMP_FILE_PATH)) { - if (fs.existsSync(cst.DUMP_BACKUP_FILE_PATH)) { - fs.unlinkSync(cst.DUMP_BACKUP_FILE_PATH); - } - fs.renameSync(cst.DUMP_FILE_PATH, cst.DUMP_BACKUP_FILE_PATH); + fs.copyFileSync(cst.DUMP_FILE_PATH, cst.DUMP_BACKUP_FILE_PATH); } } catch (e) { console.error(e.stack || e); @@ -155,8 +161,11 @@ module.exports = function(God) { } catch (e) { console.error(e.stack || e); try { - fs.unlinkSync(cst.DUMP_FILE_PATH); + // try to backup file + fs.copyFileSync(cst.DUMP_BACKUP_FILE_PATH, cst.DUMP_FILE_PATH); } catch (e) { + // don't keep broken file + fs.unlinkSync(cst.DUMP_FILE_PATH); console.error(e.stack || e); } } From ced2835d2100dc7f5162295f7b51335374855495 Mon Sep 17 00:00:00 2001 From: vince Date: Fri, 2 Mar 2018 12:18:54 +0100 Subject: [PATCH 19/32] feature: #3485 add cleardump command --- bin/pm2 | 9 +++++++++ lib/API/Startup.js | 19 ++++++++++++++++++- lib/God/ActionMethods.js | 4 +++- test/programmatic/programmatic.js | 7 ++++++- 4 files changed, 36 insertions(+), 3 deletions(-) diff --git a/bin/pm2 b/bin/pm2 index 05072f4bc..3099bba4d 100755 --- a/bin/pm2 +++ b/bin/pm2 @@ -612,6 +612,15 @@ commander.command('dump') pm2.dump(); })); +// +// Delete dump file +// +commander.command('cleardump') + .description('Create empty dump file') + .action(failOnUnknown(function() { + pm2.clearDump(); + })); + // // Save processes to file // diff --git a/lib/API/Startup.js b/lib/API/Startup.js index 5be3e3d63..3064e6a62 100644 --- a/lib/API/Startup.js +++ b/lib/API/Startup.js @@ -404,7 +404,9 @@ module.exports = function(CLI) { console.error(e.stack || e); try { // try to backup file - fs.copyFileSync(cst.DUMP_BACKUP_FILE_PATH, cst.DUMP_FILE_PATH); + if(fs.existsSync(cst.DUMP_BACKUP_FILE_PATH)) { + fs.copyFileSync(cst.DUMP_BACKUP_FILE_PATH, cst.DUMP_FILE_PATH); + } } catch (e) { // don't keep broken file fs.unlinkSync(cst.DUMP_FILE_PATH); @@ -431,6 +433,21 @@ module.exports = function(CLI) { }); }; + /** + * Remove DUMP_FILE_PATH file and DUMP_BACKUP_FILE_PATH file + * @method dump + * @param {} cb + * @return + */ + CLI.prototype.clearDump = function(cb) { + fs.writeFileSync(cst.DUMP_FILE_PATH, JSON.stringify([])); + + if(cb && typeof cb === 'function') return cb(); + + Common.printOut(cst.PREFIX_MSG + 'Successfully created %s', cst.DUMP_FILE_PATH); + return this.exitCli(cst.SUCCESS_EXIT); + }; + /** * Resurrect processes * @method resurrect diff --git a/lib/God/ActionMethods.js b/lib/God/ActionMethods.js index 6b577d69a..37a6255cf 100644 --- a/lib/God/ActionMethods.js +++ b/lib/God/ActionMethods.js @@ -162,7 +162,9 @@ module.exports = function(God) { console.error(e.stack || e); try { // try to backup file - fs.copyFileSync(cst.DUMP_BACKUP_FILE_PATH, cst.DUMP_FILE_PATH); + if(fs.existsSync(cst.DUMP_BACKUP_FILE_PATH)) { + fs.copyFileSync(cst.DUMP_BACKUP_FILE_PATH, cst.DUMP_FILE_PATH); + } } catch (e) { // don't keep broken file fs.unlinkSync(cst.DUMP_FILE_PATH); diff --git a/test/programmatic/programmatic.js b/test/programmatic/programmatic.js index e71f0aecf..478e13618 100644 --- a/test/programmatic/programmatic.js +++ b/test/programmatic/programmatic.js @@ -18,7 +18,12 @@ describe('PM2 programmatic calls', function() { }); after(function(done) { - pm2.kill(done); + pm2.delete('all', function(err, ret) { + // clean dump file + pm2.clearDump(function(err) { + pm2.kill(done); + }); + }); }); before(function(done) { From 8951184688c720ded5b4b46bd5b393c3793f9b03 Mon Sep 17 00:00:00 2001 From: vince Date: Fri, 2 Mar 2018 15:25:15 +0100 Subject: [PATCH 20/32] fix: solve empty list when no process and try to update pm2 --- lib/API/Startup.js | 6 ++++++ lib/God/ActionMethods.js | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/lib/API/Startup.js b/lib/API/Startup.js index 3064e6a62..f77ca5167 100644 --- a/lib/API/Startup.js +++ b/lib/API/Startup.js @@ -375,6 +375,12 @@ module.exports = function(CLI) { // try to fix issues with empty dump file // like #3485 if (env_arr.length === 0) { + + // fix : if no dump file, no process, only module and after pm2 update + if (!fs.existsSync(cst.DUMP_FILE_PATH)) { + that.clearDump(function(){}); + } + // if no process in list don't modify dump file // process list should not be empty if(cb) { diff --git a/lib/God/ActionMethods.js b/lib/God/ActionMethods.js index 37a6255cf..1df6b159c 100644 --- a/lib/God/ActionMethods.js +++ b/lib/God/ActionMethods.js @@ -141,6 +141,12 @@ module.exports = function(God) { // try to fix issues with empty dump file // like #3485 if (process_list.length === 0) { + + // fix : if no dump file, no process, only module and after pm2 update + if (!fs.existsSync(cst.DUMP_FILE_PATH)) { + that.clearDump(function(){}); + } + // if no process in list don't modify dump file // process list should not be empty return cb(null, {success:true, process_list: process_list}); From 39f242985b7d0886a1f99f3dbe7a9145d60e66fe Mon Sep 17 00:00:00 2001 From: vince Date: Fri, 2 Mar 2018 17:18:23 +0100 Subject: [PATCH 21/32] feature: add inspect feature, on runtime --- bin/pm2 | 7 +++++++ lib/API/Extra.js | 20 +++++++++++++++++++- lib/ProcessContainerFork.js | 20 ++++++++++++++++++-- 3 files changed, 44 insertions(+), 3 deletions(-) diff --git a/bin/pm2 b/bin/pm2 index 809177b03..eebc8da68 100755 --- a/bin/pm2 +++ b/bin/pm2 @@ -396,6 +396,13 @@ commander.command('id ') pm2.getProcessIdByName(name); }); +// Inspect a process +commander.command('inspect ') + .description('inspect a process') + .action(function(cmd) { + pm2.inspect(cmd, commander); + }); + // // Stop and delete a process by name from database // diff --git a/lib/API/Extra.js b/lib/API/Extra.js index 00f445dde..6f70d5490 100644 --- a/lib/API/Extra.js +++ b/lib/API/Extra.js @@ -15,6 +15,7 @@ var fs = require('fs'); var fmt = require('../tools/fmt.js'); var moment = require('moment'); var pkg = require('../../package.json'); +const semver = require('semver'); module.exports = function(CLI) { @@ -38,7 +39,6 @@ module.exports = function(CLI) { */ CLI.prototype.report = function() { var that = this; - var semver = require('semver'); function reporting(cb) { @@ -639,4 +639,22 @@ module.exports = function(CLI) { launchMonitor(); }; + + CLI.prototype.inspect = function(app_name, cb) { + const that = this; + if(semver.satisfies(process.versions.node, '>= 8.0.0')) { + this.trigger(app_name, 'internal:inspect', function (err, res) { + if(res[0].data.return === '') { + Common.printOut(`Inspect disabled on ${app_name}`); + } else { + Common.printOut(`Inspect enabled on ${app_name} => go to chrome : chrome://inspect !!!`); + } + + that.exitCli(cst.SUCCESS_EXIT); + }); + } else { + Common.printOut('Inspect is available for node version >=8.x !'); + that.exitCli(cst.SUCCESS_EXIT); + } + }; }; diff --git a/lib/ProcessContainerFork.js b/lib/ProcessContainerFork.js index 6d1b0cebb..189851e9a 100644 --- a/lib/ProcessContainerFork.js +++ b/lib/ProcessContainerFork.js @@ -1,17 +1,33 @@ -/** + /** * Copyright 2013 the PM2 project authors. All rights reserved. * Use of this source code is governed by a license that * can be found in the LICENSE file. */ // Inject custom modules if (process.env.pmx !== 'false') { - require('pmx').init({ + const pmx = require('pmx'); + pmx.init({ transactions: (process.env.km_link === 'true' && (process.env.trace === 'true' || process.env.deep_monitoring === 'true')) || false, http: process.env.km_link === 'true' || false, v8: process.env.v8 === 'true' || process.env.deep_monitoring === 'true' || false, event_loop_dump: process.env.event_loop_inspector === 'true' || process.env.deep_monitoring === 'true' || false, deep_metrics: process.env.deep_monitoring === 'true' || false }); + + if(require('semver').satisfies(process.versions.node, '>= 8.0.0')) { + var url = ''; + pmx.action('internal:inspect', function(reply) { + const inspector = require('inspector'); + if(url === '') { + inspector.open(); + url = inspector.url(); + } else { + inspector.close(); + url = ''; + } + reply(url); + }); + } } if (typeof(process.env.source_map_support) != "undefined" && From 33d1fb3762840795db5921683806d6b62ff6dc8a Mon Sep 17 00:00:00 2001 From: vince Date: Fri, 2 Mar 2018 17:43:47 +0100 Subject: [PATCH 22/32] feature: add inspect feature, on runtime for cluster app --- lib/API/Extra.js | 2 +- lib/ProcessContainer.js | 10 +--------- lib/ProcessContainerFork.js | 26 +------------------------- lib/ProcessUtils.js | 29 +++++++++++++++++++++++++++++ 4 files changed, 32 insertions(+), 35 deletions(-) create mode 100644 lib/ProcessUtils.js diff --git a/lib/API/Extra.js b/lib/API/Extra.js index 6f70d5490..60ced15dd 100644 --- a/lib/API/Extra.js +++ b/lib/API/Extra.js @@ -644,7 +644,7 @@ module.exports = function(CLI) { const that = this; if(semver.satisfies(process.versions.node, '>= 8.0.0')) { this.trigger(app_name, 'internal:inspect', function (err, res) { - if(res[0].data.return === '') { + if(res && res[0].data.return === '') { Common.printOut(`Inspect disabled on ${app_name}`); } else { Common.printOut(`Inspect enabled on ${app_name} => go to chrome : chrome://inspect !!!`); diff --git a/lib/ProcessContainer.js b/lib/ProcessContainer.js index db9540dd6..0d739bac9 100644 --- a/lib/ProcessContainer.js +++ b/lib/ProcessContainer.js @@ -30,15 +30,7 @@ delete process.env.pm2_env; (function ProcessContainer() { var fs = require('fs'); - if (process.env.pmx !== 'false') { - require('pmx').init({ - transactions: (process.env.km_link === 'true' && (process.env.trace === 'true' || process.env.deep_monitoring === 'true')) || false, - http: process.env.km_link === 'true' || false, - v8: process.env.v8 === 'true' || process.env.deep_monitoring === 'true' || false, - event_loop_dump: process.env.event_loop_inspector === 'true' || process.env.deep_monitoring === 'true' || false, - deep_metrics: process.env.deep_monitoring === 'true' || false - }); - } + require('./ProcessUtils').injectModules(); var stdFile = pm2_env.pm_log_path; var outFile = pm2_env.pm_out_log_path; diff --git a/lib/ProcessContainerFork.js b/lib/ProcessContainerFork.js index 189851e9a..59c45d3fb 100644 --- a/lib/ProcessContainerFork.js +++ b/lib/ProcessContainerFork.js @@ -4,31 +4,7 @@ * can be found in the LICENSE file. */ // Inject custom modules -if (process.env.pmx !== 'false') { - const pmx = require('pmx'); - pmx.init({ - transactions: (process.env.km_link === 'true' && (process.env.trace === 'true' || process.env.deep_monitoring === 'true')) || false, - http: process.env.km_link === 'true' || false, - v8: process.env.v8 === 'true' || process.env.deep_monitoring === 'true' || false, - event_loop_dump: process.env.event_loop_inspector === 'true' || process.env.deep_monitoring === 'true' || false, - deep_metrics: process.env.deep_monitoring === 'true' || false - }); - - if(require('semver').satisfies(process.versions.node, '>= 8.0.0')) { - var url = ''; - pmx.action('internal:inspect', function(reply) { - const inspector = require('inspector'); - if(url === '') { - inspector.open(); - url = inspector.url(); - } else { - inspector.close(); - url = ''; - } - reply(url); - }); - } -} +require('./ProcessUtils').injectModules(); if (typeof(process.env.source_map_support) != "undefined" && process.env.source_map_support !== "false") { diff --git a/lib/ProcessUtils.js b/lib/ProcessUtils.js new file mode 100644 index 000000000..3e90117e3 --- /dev/null +++ b/lib/ProcessUtils.js @@ -0,0 +1,29 @@ +module.exports = { + injectModules: function() { + if (process.env.pmx !== 'false') { + const pmx = require('pmx'); + pmx.init({ + transactions: (process.env.km_link === 'true' && (process.env.trace === 'true' || process.env.deep_monitoring === 'true')) || false, + http: process.env.km_link === 'true' || false, + v8: process.env.v8 === 'true' || process.env.deep_monitoring === 'true' || false, + event_loop_dump: process.env.event_loop_inspector === 'true' || process.env.deep_monitoring === 'true' || false, + deep_metrics: process.env.deep_monitoring === 'true' || false + }); + + if(require('semver').satisfies(process.versions.node, '>= 8.0.0')) { + var url = ''; + pmx.action('internal:inspect', function(reply) { + const inspector = require('inspector'); + if(url === '') { + inspector.open(); + url = inspector.url(); + } else { + inspector.close(); + url = ''; + } + reply(url); + }); + } + } + } +}; From dacc654207cbe494af0d12a3f9f27c3b16541802 Mon Sep 17 00:00:00 2001 From: vince Date: Mon, 5 Mar 2018 10:49:32 +0100 Subject: [PATCH 23/32] fix: improve error message if action has failed --- lib/API/Extra.js | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/lib/API/Extra.js b/lib/API/Extra.js index 60ced15dd..c6c057ec1 100644 --- a/lib/API/Extra.js +++ b/lib/API/Extra.js @@ -644,10 +644,15 @@ module.exports = function(CLI) { const that = this; if(semver.satisfies(process.versions.node, '>= 8.0.0')) { this.trigger(app_name, 'internal:inspect', function (err, res) { - if(res && res[0].data.return === '') { - Common.printOut(`Inspect disabled on ${app_name}`); + + if(res && res[0]) { + if (res[0].data.return === '') { + Common.printOut(`Inspect disabled on ${app_name}`); + } else { + Common.printOut(`Inspect enabled on ${app_name} => go to chrome : chrome://inspect !!!`); + } } else { - Common.printOut(`Inspect enabled on ${app_name} => go to chrome : chrome://inspect !!!`); + Common.printOut(`Impossible to enabled inspect mode on ${app_name} !!!`); } that.exitCli(cst.SUCCESS_EXIT); From 5f78ecbf90f9f46a7feb2a169968e86b0ecac91e Mon Sep 17 00:00:00 2001 From: vince Date: Mon, 5 Mar 2018 11:14:11 +0100 Subject: [PATCH 24/32] chore: wording on error message --- lib/API/Extra.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/API/Extra.js b/lib/API/Extra.js index c6c057ec1..cf7a23fcf 100644 --- a/lib/API/Extra.js +++ b/lib/API/Extra.js @@ -652,7 +652,7 @@ module.exports = function(CLI) { Common.printOut(`Inspect enabled on ${app_name} => go to chrome : chrome://inspect !!!`); } } else { - Common.printOut(`Impossible to enabled inspect mode on ${app_name} !!!`); + Common.printOut(`Unable to activate inspect mode on ${app_name} !!!`); } that.exitCli(cst.SUCCESS_EXIT); From 97fd1010d005e59f2411042fa95891f9717fa8b7 Mon Sep 17 00:00:00 2001 From: vince Date: Mon, 5 Mar 2018 13:38:29 +0100 Subject: [PATCH 25/32] chore: fix issue with snapshot command + remove command forceGc --- bin/pm2 | 9 --------- lib/Daemon.js | 3 +-- lib/God/Methods.js | 17 ----------------- lib/Satan.js | 1 - 4 files changed, 1 insertion(+), 29 deletions(-) diff --git a/bin/pm2 b/bin/pm2 index 809177b03..55aeda641 100755 --- a/bin/pm2 +++ b/bin/pm2 @@ -882,15 +882,6 @@ commander.command('backward ') pm2.backward(pm2_name); }); -// -// Force PM2 to trigger garbage collection -// -commander.command('gc') - .description('force PM2 to trigger garbage collection') - .action(function() { - pm2.forceGc(); - }); - // // Perform a deep update of PM2 // diff --git a/lib/Daemon.js b/lib/Daemon.js index 26c9c98ef..c1d7019ff 100644 --- a/lib/Daemon.js +++ b/lib/Daemon.js @@ -153,7 +153,7 @@ Daemon.prototype.innerStart = function(cb) { var profiler; try { - profiler = require('v8-profiler'); + profiler = require('v8-profiler-node8'); } catch(e) { profiler = null; } @@ -231,7 +231,6 @@ Daemon.prototype.innerStart = function(cb) { notifyByProcessId : God.notifyByProcessId, notifyKillPM2 : God.notifyKillPM2, - forceGc : God.forceGc, monitor : God.monitor, unmonitor : God.unmonitor, diff --git a/lib/God/Methods.js b/lib/God/Methods.js index e7b8e9fa3..ea6753a2c 100644 --- a/lib/God/Methods.js +++ b/lib/God/Methods.js @@ -247,21 +247,4 @@ module.exports = function(God) { pm2_env.unstable_restarts = 0; }; - /** - * Description - * @method forcegc - * @return - */ - God.forceGc = function(opts, cb) { - if (global.gc) { - global.gc(); - debug('Garbage collection triggered successfully'); - if (cb) cb(null, {success: true}); - } - else { - debug('Garbage collection failed'); - if (cb) cb(null, {success: false}); - } - }; - }; diff --git a/lib/Satan.js b/lib/Satan.js index e6df06ba9..1fc6651e7 100644 --- a/lib/Satan.js +++ b/lib/Satan.js @@ -215,7 +215,6 @@ Satan.remoteWrapper = function() { killMe : God.killMe, notifyKillPM2 : God.notifyKillPM2, - forceGc : God.forceGc, findByFullPath : God.findByFullPath, From aae1d55e410c4dcfbbca83eaabbdf1a65d55f3aa Mon Sep 17 00:00:00 2001 From: vince Date: Mon, 5 Mar 2018 16:02:50 +0100 Subject: [PATCH 26/32] chore: revert PR #3496 --- lib/API/schema.json | 284 +++++++++++++++++--------------------------- 1 file changed, 108 insertions(+), 176 deletions(-) diff --git a/lib/API/schema.json b/lib/API/schema.json index 75db9a98b..4fb1e935b 100644 --- a/lib/API/schema.json +++ b/lib/API/schema.json @@ -2,87 +2,23 @@ "script": { "type": "string", "require": true, - "alias" : "exec", - "description": "Path of the script to launch, required field" - }, - "name": { - "type": "string", - "default": "Script filename without the extension (app for app.js)", - "description": "Process name in the process list" - }, - "cwd": { - "type": "string", - "default": "CWD of the current environment (from your shell)", - "description": "Current working directory to start the process with" + "alias" : "exec" }, "args": { "type": [ "array", "string" - ], - "description": "Arguments to pass to the script" - }, - "interpreter": { - "type": "string", - "alias": "exec_interpreter", - "default": "node", - "description": "Interpreter absolute path" + ] }, "node_args": { "type": [ "array", "string" ], - "alias": ["interpreter_args"], - "description": "Arguments to pass to the interpreter" - }, - "output": { - "type": "string", - "alias": ["out", "out_file", "out_log"], - "default": "~/.pm2/logs/-out.log", - "description": "File path for stdout (each line is appended to this file)" + "alias": ["interpreterArgs", "interpreter_args"] }, - "error": { - "type": "string", - "alias": ["error_file", "err", "err_file", "err_log"], - "default": "~/.pm2/logs/-error.err", - "description": "File path for stderr (each line is appended to this file)" - }, - "log": { - "type": [ - "boolean", - "string" - ], - "default": "/dev/null", - "alias": "log_file", - "description": "File path for combined stdout and stderr (each line is appended to this file)" - }, - "disable_logs": { - "type": "boolean", - "default": false, - "description": "Disable all logs storage" - }, - "log_type": { - "type": "string", - "description": "Define a specific log output type, possible values: json|" - }, - "log_date_format": { - "type": "string", - "description": "Format for log timestamps (eg YYYY-MM-DD HH:mm Z) in moment.js format" - }, - "env": { - "type": [ - "object", - "string" - ], - "description": "Specify environment variables to be injected" - }, - "^env_\\S*$": { - "type": [ - "object", - "string" - ], - "description": "Specify environment variables to be injected when using --env " + "name": { + "type": "string" }, "max_memory_restart": { "type": [ @@ -91,89 +27,124 @@ ], "regex": "^\\d+(G|M|K)?$", "ext_type": "sbyte", - "desc": "it should be a NUMBER - byte, \"[NUMBER]G\"(Gigabyte), \"[NUMBER]M\"(Megabyte) or \"[NUMBER]K\"(Kilobyte)", - "description": "Restart the app if an amount of memory is exceeded (format: 'K|G|' for KB, 'M' for MB, 'G' for GB, default to byte)" + "desc": "it should be a NUMBER - byte, \"[NUMBER]G\"(Gigabyte), \"[NUMBER]M\"(Megabyte) or \"[NUMBER]K\"(Kilobyte)" }, - "pid_file": { - "type": "string", - "alias": "pid", - "default": "~/.pm2/pids/app_name-id.pid", - "description": "File path where the pid of the started process is written by pm2" + "uid" : { + "type" : "string" + }, + "gid" : { + "type" : "string" }, "restart_delay": { - "type" : "number", - "default": 0, - "description": "Time in ms to wait before restarting a crashing app" + "type" : "number" }, - "source_map_support": { - "type": "boolean", - "default": true, - "description": "Enable or disable the source map support" + "source_map_support" : { + "type": "boolean" }, - "disable_source_map_support": { - "type": "boolean", - "default": false, - "description": "Enable or disable the source map support" + "wait_ready" : { + "type": "boolean" }, - "wait_ready": { - "type": "boolean", - "default": false, - "description": "Make the process wait for a process.send('ready')" + "disable_source_map_support" : { + "type": "boolean" }, "instances": { - "type": "number", - "default": 1, - "description": "Number of instances to be started in cluster mode" + "type": "number" }, "kill_timeout": { - "type": "number", - "default": 1600, - "description": "Time in ms before sending the final SIGKILL signal after SIGINT" + "type": "number" }, "listen_timeout": { - "type": "number", - "description": "Time in ms before forcing a reload if app is still not listening/has still note sent ready" + "type": "number" + }, + "port": { + "type": "number" + }, + "log_file": { + "type": [ + "boolean", + "string" + ], + "alias": "log" + }, + "error_file": { + "type": "string", + "alias": ["error", "err", "err_file", "err_log"] + }, + "log_type": { + "type": "string" + }, + "out_file": { + "type": "string", + "alias": ["output", "out", "out_log"] + }, + "pid_file": { + "type": "string", + "alias": "pid" }, "cron_restart": { "type": "string", - "alias": "cron", - "description": "A cron pattern to restart your app" + "alias": "cron" + }, + "cwd": { + "type": "string" }, "merge_logs": { "type": "boolean", - "alias" : "combine_logs", - "default": false, - "description": "In cluster mode, merge each type of logs into a single file (instead of having one for each cluster)" + "alias" : "combine_logs" + }, + "vizion" : { + "type": "boolean", + "default" : true }, - "vizion": { + "pmx" : { "type": "boolean", - "default" : true, - "description": "Enable or disable the versioning metadatas (vizion library)" + "default" : true + }, + "automation" : { + "type": "boolean", + "default" : true + }, + "autorestart" : { + "type": "boolean", + "default" : true }, - "autorestart": { + "treekill" : { "type": "boolean", - "default" : true, - "description": "Enable or disable auto restart after process failure" + "default" : true }, "watch": { "type": [ "boolean", "array", "string" - ], - "default": false, - "description": "Enable or disable the watch mode" + ] }, "ignore_watch": { "type": [ "array", "string" - ], - "description": "List of paths to ignore (regex)" + ] }, "watch_options": { - "type": "object", - "description": "Object that will be used as an options with chokidar (refer to chokidar documentation)" + "type": "object" + }, + "env": { + "type": [ + "object", + "string" + ] + }, + "^env_\\S*$": { + "type": [ + "object", + "string" + ] + }, + "disable_logs" : { + "type": "boolean" + }, + "log_date_format": { + "type": "string" }, "min_uptime": { "type": [ @@ -183,51 +154,43 @@ "regex": "^\\d+(h|m|s)?$", "desc": "it should be a NUMBER - milliseconds, \"[NUMBER]h\"(hours), \"[NUMBER]m\"(minutes) or \"[NUMBER]s\"(seconds)", "min": 100, - "ext_type": "stime", - "default": 1000, - "description": "Minimum uptime of the app to be considered started (format is [0-9]+(h|m|s)?, for hours, minutes, seconds, default to ms)" + "ext_type": "stime" }, "max_restarts": { "type": "number", - "min": 0, - "default": 16, - "description": "Number of times a script is restarted when it exits in less than min_uptime" + "min": 0 }, "exec_mode": { "type": "string", "regex": "^(cluster|fork)(_mode)?$", "alias": "executeCommand", - "desc": "it should be \"cluster\"(\"cluster_mode\") or \"fork\"(\"fork_mode\") only", - "default": "fork", - "description": "Set the execution mode, possible values: fork|cluster" + "desc": "it should be \"cluster\"(\"cluster_mode\") or \"fork\"(\"fork_mode\") only" + }, + "exec_interpreter": { + "type": "string", + "alias": "interpreter" + }, + "write": { + "type": "boolean" }, "force": { - "type": "boolean", - "default": false, - "description": "Start a script even if it is already running (only the script path is considered)" + "type": "boolean" }, "append_env_to_name": { - "type": "boolean", - "default": false, - "description": "Append the environment name to the app name" + "type": "boolean" }, "post_update": { - "type": "array", - "description": "List of commands executed after a pull/upgrade operation performed from Keymetrics dashboard" + "type": "array" }, - "trace": { + "disable_trace": { "type": [ "boolean" - ], - "default": false, - "description": "Enable or disable the transaction tracing" + ] }, - "disable_trace": { + "trace": { "type": [ "boolean" - ], - "default": true, - "description": "Enable or disable the transaction tracing" + ] }, "v8": { "type": [ @@ -245,45 +208,14 @@ ] }, "increment_var": { - "type": "string", - "description": "Specify the name of an environnement variable to inject which increments for each cluster" + "type": "string" }, "instance_var": { "type": "string", - "default" : "NODE_APP_INSTANCE", - "description": "Rename the NODE_APP_INSTANCE environment variable" - }, - "pmx": { - "type": "boolean", - "default" : true, - "description": "Enable or disable pmx wrapping" - }, - "automation": { - "type": "boolean", - "default" : true, - "description": "Enable or disable pmx wrapping" - }, - "treekill": { - "type": "boolean", - "default" : true, - "description": "Only kill the main process, not detached children" - }, - "port": { - "type": "number", - "description": "Shortcut to inject a PORT environment variable" - }, - "uid": { - "type" : "string", - "default": "Current user uid", - "description": "Set user id" - }, - "gid": { - "type" : "string", - "default": "Current user gid", - "description": "Set group id" + "default" : "NODE_APP_INSTANCE" }, "windowsHide": { "type": "boolean", "default" : true } -} \ No newline at end of file +} From bc07f43b115066f6077606df8f59379777f2a917 Mon Sep 17 00:00:00 2001 From: vince Date: Mon, 5 Mar 2018 17:11:36 +0100 Subject: [PATCH 27/32] fix: use polyfill module for copySync with node 4.x --- lib/API/Startup.js | 5 +++-- lib/God/ActionMethods.js | 5 +++-- package.json | 1 + 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/lib/API/Startup.js b/lib/API/Startup.js index f77ca5167..b96877fe8 100644 --- a/lib/API/Startup.js +++ b/lib/API/Startup.js @@ -7,6 +7,7 @@ var debug = require('debug')('pm2:cli:startup'); var chalk = require('chalk'); var path = require('path'); var fs = require('fs'); +const fsExtra = require('fs-extra'); var async = require('async'); var exec = require('child_process').exec; var Common = require('../Common.js'); @@ -396,7 +397,7 @@ module.exports = function(CLI) { // Back up dump file try { if (fs.existsSync(cst.DUMP_FILE_PATH)) { - fs.copyFileSync(cst.DUMP_FILE_PATH, cst.DUMP_BACKUP_FILE_PATH); + fsExtra.copySync(cst.DUMP_FILE_PATH, cst.DUMP_BACKUP_FILE_PATH); } } catch (e) { console.error(e.stack || e); @@ -411,7 +412,7 @@ module.exports = function(CLI) { try { // try to backup file if(fs.existsSync(cst.DUMP_BACKUP_FILE_PATH)) { - fs.copyFileSync(cst.DUMP_BACKUP_FILE_PATH, cst.DUMP_FILE_PATH); + fsExtra.copySync(cst.DUMP_BACKUP_FILE_PATH, cst.DUMP_FILE_PATH); } } catch (e) { // don't keep broken file diff --git a/lib/God/ActionMethods.js b/lib/God/ActionMethods.js index 1df6b159c..1556a1652 100644 --- a/lib/God/ActionMethods.js +++ b/lib/God/ActionMethods.js @@ -12,6 +12,7 @@ */ var fs = require('fs'); +const fsExtra = require('fs-extra'); var path = require('path'); var async = require('async'); var os = require('os'); @@ -155,7 +156,7 @@ module.exports = function(God) { // Back up dump file try { if (fs.existsSync(cst.DUMP_FILE_PATH)) { - fs.copyFileSync(cst.DUMP_FILE_PATH, cst.DUMP_BACKUP_FILE_PATH); + fsExtra.copySync(cst.DUMP_FILE_PATH, cst.DUMP_BACKUP_FILE_PATH); } } catch (e) { console.error(e.stack || e); @@ -169,7 +170,7 @@ module.exports = function(God) { try { // try to backup file if(fs.existsSync(cst.DUMP_BACKUP_FILE_PATH)) { - fs.copyFileSync(cst.DUMP_BACKUP_FILE_PATH, cst.DUMP_FILE_PATH); + fsExtra.copySync(cst.DUMP_BACKUP_FILE_PATH, cst.DUMP_FILE_PATH); } } catch (e) { // don't keep broken file diff --git a/package.json b/package.json index 3bd207d6d..4acaa133d 100644 --- a/package.json +++ b/package.json @@ -169,6 +169,7 @@ "debug": "^3.0", "eventemitter2": "5.0.1", "fclone": "1.0.11", + "fs-extra": "^5.0.0", "mkdirp": "0.5.1", "moment": "^2.19", "needle": "^2.2.0", From c3ae6aa8b45a5e8bcf408c07473d19379a10c53e Mon Sep 17 00:00:00 2001 From: vince Date: Fri, 2 Mar 2018 17:18:23 +0100 Subject: [PATCH 28/32] feature: add inspect feature, on runtime --- bin/pm2 | 7 +++++++ lib/API/Extra.js | 20 +++++++++++++++++++- lib/ProcessContainerFork.js | 20 ++++++++++++++++++-- 3 files changed, 44 insertions(+), 3 deletions(-) diff --git a/bin/pm2 b/bin/pm2 index 9bcae4e6b..840dc84c6 100755 --- a/bin/pm2 +++ b/bin/pm2 @@ -396,6 +396,13 @@ commander.command('id ') pm2.getProcessIdByName(name); }); +// Inspect a process +commander.command('inspect ') + .description('inspect a process') + .action(function(cmd) { + pm2.inspect(cmd, commander); + }); + // // Stop and delete a process by name from database // diff --git a/lib/API/Extra.js b/lib/API/Extra.js index 00f445dde..6f70d5490 100644 --- a/lib/API/Extra.js +++ b/lib/API/Extra.js @@ -15,6 +15,7 @@ var fs = require('fs'); var fmt = require('../tools/fmt.js'); var moment = require('moment'); var pkg = require('../../package.json'); +const semver = require('semver'); module.exports = function(CLI) { @@ -38,7 +39,6 @@ module.exports = function(CLI) { */ CLI.prototype.report = function() { var that = this; - var semver = require('semver'); function reporting(cb) { @@ -639,4 +639,22 @@ module.exports = function(CLI) { launchMonitor(); }; + + CLI.prototype.inspect = function(app_name, cb) { + const that = this; + if(semver.satisfies(process.versions.node, '>= 8.0.0')) { + this.trigger(app_name, 'internal:inspect', function (err, res) { + if(res[0].data.return === '') { + Common.printOut(`Inspect disabled on ${app_name}`); + } else { + Common.printOut(`Inspect enabled on ${app_name} => go to chrome : chrome://inspect !!!`); + } + + that.exitCli(cst.SUCCESS_EXIT); + }); + } else { + Common.printOut('Inspect is available for node version >=8.x !'); + that.exitCli(cst.SUCCESS_EXIT); + } + }; }; diff --git a/lib/ProcessContainerFork.js b/lib/ProcessContainerFork.js index 6d1b0cebb..189851e9a 100644 --- a/lib/ProcessContainerFork.js +++ b/lib/ProcessContainerFork.js @@ -1,17 +1,33 @@ -/** + /** * Copyright 2013 the PM2 project authors. All rights reserved. * Use of this source code is governed by a license that * can be found in the LICENSE file. */ // Inject custom modules if (process.env.pmx !== 'false') { - require('pmx').init({ + const pmx = require('pmx'); + pmx.init({ transactions: (process.env.km_link === 'true' && (process.env.trace === 'true' || process.env.deep_monitoring === 'true')) || false, http: process.env.km_link === 'true' || false, v8: process.env.v8 === 'true' || process.env.deep_monitoring === 'true' || false, event_loop_dump: process.env.event_loop_inspector === 'true' || process.env.deep_monitoring === 'true' || false, deep_metrics: process.env.deep_monitoring === 'true' || false }); + + if(require('semver').satisfies(process.versions.node, '>= 8.0.0')) { + var url = ''; + pmx.action('internal:inspect', function(reply) { + const inspector = require('inspector'); + if(url === '') { + inspector.open(); + url = inspector.url(); + } else { + inspector.close(); + url = ''; + } + reply(url); + }); + } } if (typeof(process.env.source_map_support) != "undefined" && From e1f7224d9bc0c627a42f813d651a06c71d43c67c Mon Sep 17 00:00:00 2001 From: vince Date: Fri, 2 Mar 2018 17:43:47 +0100 Subject: [PATCH 29/32] feature: add inspect feature, on runtime for cluster app --- lib/API/Extra.js | 2 +- lib/ProcessContainer.js | 10 +--------- lib/ProcessContainerFork.js | 26 +------------------------- lib/ProcessUtils.js | 29 +++++++++++++++++++++++++++++ 4 files changed, 32 insertions(+), 35 deletions(-) create mode 100644 lib/ProcessUtils.js diff --git a/lib/API/Extra.js b/lib/API/Extra.js index 6f70d5490..60ced15dd 100644 --- a/lib/API/Extra.js +++ b/lib/API/Extra.js @@ -644,7 +644,7 @@ module.exports = function(CLI) { const that = this; if(semver.satisfies(process.versions.node, '>= 8.0.0')) { this.trigger(app_name, 'internal:inspect', function (err, res) { - if(res[0].data.return === '') { + if(res && res[0].data.return === '') { Common.printOut(`Inspect disabled on ${app_name}`); } else { Common.printOut(`Inspect enabled on ${app_name} => go to chrome : chrome://inspect !!!`); diff --git a/lib/ProcessContainer.js b/lib/ProcessContainer.js index db9540dd6..0d739bac9 100644 --- a/lib/ProcessContainer.js +++ b/lib/ProcessContainer.js @@ -30,15 +30,7 @@ delete process.env.pm2_env; (function ProcessContainer() { var fs = require('fs'); - if (process.env.pmx !== 'false') { - require('pmx').init({ - transactions: (process.env.km_link === 'true' && (process.env.trace === 'true' || process.env.deep_monitoring === 'true')) || false, - http: process.env.km_link === 'true' || false, - v8: process.env.v8 === 'true' || process.env.deep_monitoring === 'true' || false, - event_loop_dump: process.env.event_loop_inspector === 'true' || process.env.deep_monitoring === 'true' || false, - deep_metrics: process.env.deep_monitoring === 'true' || false - }); - } + require('./ProcessUtils').injectModules(); var stdFile = pm2_env.pm_log_path; var outFile = pm2_env.pm_out_log_path; diff --git a/lib/ProcessContainerFork.js b/lib/ProcessContainerFork.js index 189851e9a..59c45d3fb 100644 --- a/lib/ProcessContainerFork.js +++ b/lib/ProcessContainerFork.js @@ -4,31 +4,7 @@ * can be found in the LICENSE file. */ // Inject custom modules -if (process.env.pmx !== 'false') { - const pmx = require('pmx'); - pmx.init({ - transactions: (process.env.km_link === 'true' && (process.env.trace === 'true' || process.env.deep_monitoring === 'true')) || false, - http: process.env.km_link === 'true' || false, - v8: process.env.v8 === 'true' || process.env.deep_monitoring === 'true' || false, - event_loop_dump: process.env.event_loop_inspector === 'true' || process.env.deep_monitoring === 'true' || false, - deep_metrics: process.env.deep_monitoring === 'true' || false - }); - - if(require('semver').satisfies(process.versions.node, '>= 8.0.0')) { - var url = ''; - pmx.action('internal:inspect', function(reply) { - const inspector = require('inspector'); - if(url === '') { - inspector.open(); - url = inspector.url(); - } else { - inspector.close(); - url = ''; - } - reply(url); - }); - } -} +require('./ProcessUtils').injectModules(); if (typeof(process.env.source_map_support) != "undefined" && process.env.source_map_support !== "false") { diff --git a/lib/ProcessUtils.js b/lib/ProcessUtils.js new file mode 100644 index 000000000..3e90117e3 --- /dev/null +++ b/lib/ProcessUtils.js @@ -0,0 +1,29 @@ +module.exports = { + injectModules: function() { + if (process.env.pmx !== 'false') { + const pmx = require('pmx'); + pmx.init({ + transactions: (process.env.km_link === 'true' && (process.env.trace === 'true' || process.env.deep_monitoring === 'true')) || false, + http: process.env.km_link === 'true' || false, + v8: process.env.v8 === 'true' || process.env.deep_monitoring === 'true' || false, + event_loop_dump: process.env.event_loop_inspector === 'true' || process.env.deep_monitoring === 'true' || false, + deep_metrics: process.env.deep_monitoring === 'true' || false + }); + + if(require('semver').satisfies(process.versions.node, '>= 8.0.0')) { + var url = ''; + pmx.action('internal:inspect', function(reply) { + const inspector = require('inspector'); + if(url === '') { + inspector.open(); + url = inspector.url(); + } else { + inspector.close(); + url = ''; + } + reply(url); + }); + } + } + } +}; From d9f44f170f115c2d6dfb6a7fe71dc31bd7fb66fb Mon Sep 17 00:00:00 2001 From: vince Date: Mon, 5 Mar 2018 10:49:32 +0100 Subject: [PATCH 30/32] fix: improve error message if action has failed --- lib/API/Extra.js | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/lib/API/Extra.js b/lib/API/Extra.js index 60ced15dd..c6c057ec1 100644 --- a/lib/API/Extra.js +++ b/lib/API/Extra.js @@ -644,10 +644,15 @@ module.exports = function(CLI) { const that = this; if(semver.satisfies(process.versions.node, '>= 8.0.0')) { this.trigger(app_name, 'internal:inspect', function (err, res) { - if(res && res[0].data.return === '') { - Common.printOut(`Inspect disabled on ${app_name}`); + + if(res && res[0]) { + if (res[0].data.return === '') { + Common.printOut(`Inspect disabled on ${app_name}`); + } else { + Common.printOut(`Inspect enabled on ${app_name} => go to chrome : chrome://inspect !!!`); + } } else { - Common.printOut(`Inspect enabled on ${app_name} => go to chrome : chrome://inspect !!!`); + Common.printOut(`Impossible to enabled inspect mode on ${app_name} !!!`); } that.exitCli(cst.SUCCESS_EXIT); From c251c8c97e6f18aae584cac6b7f3c83cf4f2de9c Mon Sep 17 00:00:00 2001 From: vince Date: Mon, 5 Mar 2018 11:14:11 +0100 Subject: [PATCH 31/32] chore: wording on error message --- lib/API/Extra.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/API/Extra.js b/lib/API/Extra.js index c6c057ec1..cf7a23fcf 100644 --- a/lib/API/Extra.js +++ b/lib/API/Extra.js @@ -652,7 +652,7 @@ module.exports = function(CLI) { Common.printOut(`Inspect enabled on ${app_name} => go to chrome : chrome://inspect !!!`); } } else { - Common.printOut(`Impossible to enabled inspect mode on ${app_name} !!!`); + Common.printOut(`Unable to activate inspect mode on ${app_name} !!!`); } that.exitCli(cst.SUCCESS_EXIT); From 65f60659139f46874882ca5a0c377dc2ad20ad9a Mon Sep 17 00:00:00 2001 From: Alexandre Strzelewicz Date: Wed, 7 Mar 2018 13:42:19 +0100 Subject: [PATCH 32/32] doc: move up docker integration --- README.md | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index b9de87c96..3b07141dd 100644 --- a/README.md +++ b/README.md @@ -70,6 +70,21 @@ Your app is now daemonized, monitored and kept alive forever. [More about Process Management](http://pm2.keymetrics.io/docs/usage/process-management/) +### Container Support + +With the drop-in replacement command for `node`, called `pm2-runtime`, run your Node.js application in a proper production environment. +We also offer an [officialy supported Docker image](https://hub.docker.com/r/keymetrics/pm2/). + +Using it is seamless: + +``` +FROM keymetrics/pm2:latest-alpine +[...] +CMD [ "pm2-runtime", "npm", "--", "start" ] +``` + +[Read More about the dedicated integration](http://pm2.keymetrics.io/docs/usage/docker-pm2-nodejs/) + ### Managing a Process Once applications are started you can manage them easily: @@ -130,21 +145,6 @@ Seamlessly supported by all major Node.js frameworks and any Node.js application [More informations about how PM2 make clustering easy](https://keymetrics.io/2015/03/26/pm2-clustering-made-easy/) -### Container Support - -With the drop-in replacement command for `node`, called `pm2-runtime`, run your Node.js application in a proper production environment. -We also offer an [officialy supported Docker image](https://hub.docker.com/r/keymetrics/pm2/). - -Using it is seamless: - -``` -FROM keymetrics/pm2:latest-alpine -[...] -CMD [ "pm2-runtime", "npm", "--", "start" ] -``` - -[Read More about the dedicated integration](http://pm2.keymetrics.io/docs/usage/docker-pm2-nodejs/) - ### Terminal Based Monitoring ![Monit](https://github.com/Unitech/pm2/raw/master/pres/pm2-monit.png)