API/api.medcify.app/node_modules/pm2/lib/API.js

1928 lines
59 KiB
JavaScript
Raw Normal View History

2022-09-26 06:11:44 +00:00
/**
* Copyright 2013-2022 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.
*/
'use strict';
const commander = require('commander');
const fs = require('fs');
const path = require('path');
const eachLimit = require('async/eachLimit');
const series = require('async/series');
const debug = require('debug')('pm2:cli');
const util = require('util');
const chalk = require('chalk');
const fclone = require('fclone');
var DockerMgmt = require('./API/ExtraMgmt/Docker.js')
var conf = require('../constants.js');
var Client = require('./Client');
var Common = require('./Common');
var KMDaemon = require('@pm2/agent/src/InteractorClient');
var Config = require('./tools/Config');
var Modularizer = require('./API/Modules/Modularizer.js');
var path_structure = require('../paths.js');
var UX = require('./API/UX');
var pkg = require('../package.json');
var hf = require('./API/Modules/flagExt.js');
var Configuration = require('./Configuration.js');
const sexec = require('./tools/sexec.js')
var IMMUTABLE_MSG = chalk.bold.blue('Use --update-env to update environment variables');
/**
* Main Function to be imported
* can be aliased to PM2
*
* To use it when PM2 is installed as a module:
*
* var PM2 = require('pm2');
*
* var pm2 = PM2(<opts>);
*
*
* @param {Object} opts
* @param {String} [opts.cwd=<current>] override pm2 cwd for starting scripts
* @param {String} [opts.pm2_home=[<paths.js>]] pm2 directory for log, pids, socket files
* @param {Boolean} [opts.independent=false] unique PM2 instance (random pm2_home)
* @param {Boolean} [opts.daemon_mode=true] should be called in the same process or not
* @param {String} [opts.public_key=null] pm2 plus bucket public key
* @param {String} [opts.secret_key=null] pm2 plus bucket secret key
* @param {String} [opts.machine_name=null] pm2 plus instance name
*/
class API {
constructor (opts) {
if (!opts) opts = {};
var that = this;
this.daemon_mode = typeof(opts.daemon_mode) == 'undefined' ? true : opts.daemon_mode;
this.pm2_home = conf.PM2_ROOT_PATH;
this.public_key = conf.PUBLIC_KEY || opts.public_key || null;
this.secret_key = conf.SECRET_KEY || opts.secret_key || null;
this.machine_name = conf.MACHINE_NAME || opts.machine_name || null
/**
* CWD resolution
*/
this.cwd = process.cwd();
if (opts.cwd) {
this.cwd = path.resolve(opts.cwd);
}
/**
* 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 = Object.assign(conf, path_structure(this.pm2_home));
}
else if (opts.independent == true && conf.IS_WINDOWS === false) {
// Create an unique pm2 instance
const 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 = Object.assign(conf, path_structure(this.pm2_home));
}
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);
}
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.pm2_configuration = Configuration.getSync('pm2') || {}
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;
}
// For testing purposes
if (this.secret_key && process.env.NODE_ENV == 'local_test')
that.gl_is_km_linked = true;
KMDaemon.ping(this._conf, function(err, result) {
if (!err && result === true) {
fs.readFile(conf.INTERACTION_CONF, (err, _conf) => {
if (!err) {
try {
that.gl_interact_infos = JSON.parse(_conf.toString())
} catch(e) {
var json5 = require('./tools/json5.js')
try {
that.gl_interact_infos = json5.parse(_conf.toString())
} catch(e) {
console.error(e)
that.gl_interact_infos = null
}
}
}
})
}
})
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;
}
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);
that.launchSysMonitoring(() => {})
// If new pm2 instance has been popped
// Lauch all modules
that.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 that = this;
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');
if (that.pm2_home.indexOf('.pm2') > -1)
return cb(new Error('Destroy is not a allowed method on .pm2'));
fs.access(test_path, fs.R_OK, function(err) {
if (err) return cb(err);
debug('Deleting temporary folder %s', that.pm2_home);
sexec(cmd, cb);
});
});
}
/**
* 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() {};
this.Client.close(function(err, data) {
debug('The session lasted %ds', (new Date() - that.start_timer) / 1000);
return cb(err, data);
});
};
/**
* Alias on disconnect
* @param cb
*/
close (cb) {
this.disconnect(cb);
}
/**
* Launch modules
*
* @param {Function} cb callback once pm2 has launched modules
*/
launchModules (cb) {
this.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
*/
launchBus (cb) {
this.Client.launchBus(cb);
}
/**
* 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() {
fds = fds | fd;
tryToExit();
});
}
// Does not write anything more.
delete std.write;
});
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
*/
start (cmd, opts, cb) {
if (typeof(opts) == "function") {
cb = opts;
opts = {};
}
if (!opts) opts = {};
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 (Common.isConfigFile(cmd) || (typeof(cmd) === 'object')) {
that._startJson(cmd, opts, 'restartProcessId', (err, procs) => {
return cb ? cb(err, procs) : this.speedList()
})
}
else {
that._startScript(cmd, opts, (err, procs) => {
return cb ? cb(err, procs) : this.speedList(0)
})
}
}
/**
* Reset process counters
*
* @method resetMetaProcess
*/
reset (process_name, cb) {
var that = this;
function processIds(ids, cb) {
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();
});
}
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
*/
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.');
// Dump PM2 processes
that.Client.executeRemote('notifyKillPM2', {}, function() {});
that.getVersion(function(err, new_version) {
// If not linked to PM2 plus, 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.PM2_UPDATE));
console.log(dt.toString());
}
that.dump(function(err) {
that.killDaemon(function() {
that.Client.launchDaemon({interactor:false}, function(err, child) {
that.Client.launchRPC(function() {
that.resurrect(function() {
Common.printOut(chalk.blue.bold('>>>>>>>>>> PM2 updated'));
that.launchSysMonitoring(() => {})
that.launchAll(that, function() {
KMDaemon.launchAndInteract(that._conf, {
pm2_version: pkg.version
}, function(err, data, interactor_proc) {
})
setTimeout(() => {
return cb ? cb(null, {success:true}) : that.speedList();
}, 250)
});
});
});
});
});
});
});
return false;
}
/**
* 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 = {};
}
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 (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.env) {
var err = 'Using --env [env] without passing the ecosystem.config.js does not work'
Common.err(err);
Common.unlockReload();
return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT);
}
if (opts && !opts.updateEnv)
Common.printOut(IMMUTABLE_MSG);
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);
});
}
}
/**
* 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;
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.env) {
var err = 'Using --env [env] without passing the ecosystem.config.js does not work'
Common.err(err);
return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT);
}
if (opts && !opts.updateEnv)
Common.printOut(IMMUTABLE_MSG);
that._operate('restartProcessId', cmd, opts, 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;
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', (err, procs) => {
return cb ? cb(err, procs) : this.speedList()
});
if (Common.isConfigFile(process_name))
return that.actionFromJson('deleteProcessId', process_name, commander, 'file', (err, procs) => {
return cb ? cb(err, procs) : this.speedList()
});
else {
that._operate('deleteProcessId', process_name, (err, procs) => {
return cb ? cb(err, procs) : this.speedList()
});
}
}
/**
* 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 (process_name == "-") {
process.stdin.resume();
process.stdin.setEncoding('utf8');
process.stdin.on('data', function (param) {
process.stdin.pause();
that.actionFromJson('stopProcessId', param, commander, 'pipe', (err, procs) => {
return cb ? cb(err, procs) : this.speedList()
})
});
}
else if (Common.isConfigFile(process_name))
that.actionFromJson('stopProcessId', process_name, commander, 'file', (err, procs) => {
return cb ? cb(err, procs) : this.speedList()
});
else
that._operate('stopProcessId', process_name, (err, procs) => {
return cb ? cb(err, procs) : this.speedList()
});
}
/**
* Get list of all processes managed
*
* @param {Function} cb Callback
*/
list (opts, cb) {
var that = this;
if (typeof(opts) == 'function') {
cb = opts;
opts = null;
}
that.Client.executeRemote('getMonitorData', {}, function(err, list) {
if (err) {
Common.printError(err);
return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT);
}
if (opts && opts.rawArgs && opts.rawArgs.indexOf('--watch') > -1) {
var dayjs = require('dayjs');
function show() {
process.stdout.write('\x1b[2J');
process.stdout.write('\x1b[0f');
console.log('Last refresh: ', dayjs().format());
that.Client.executeRemote('getMonitorData', {}, function(err, list) {
UX.list(list, null);
});
}
show();
setInterval(show, 900);
return false;
}
return cb ? cb(null, list) : that.speedList(null);
});
}
/**
* Kill Daemon
*
* @param {Function} cb Callback
*/
killDaemon (cb) {
process.env.PM2_STATUS = 'stopping'
var that = this;
that.Client.executeRemote('notifyKillPM2', {}, function() {});
that._operate('deleteProcessId', 'all', function(err, list) {
Common.printOut(conf.PREFIX_MSG + '[v] All Applications Stopped');
process.env.PM2_SILENT = 'false';
that.killAgent(function(err, data) {
if (!err) {
Common.printOut(conf.PREFIX_MSG + '[v] Agent Stopped');
}
that.Client.killDaemon(function(err, res) {
if (err) Common.printError(err);
Common.printOut(conf.PREFIX_MSG + '[v] PM2 Daemon Stopped');
return cb ? cb(err, res) : that.exitCli(conf.SUCCESS_EXIT);
});
});
})
}
kill (cb) {
this.killDaemon(cb);
}
/////////////////////
// Private methods //
/////////////////////
/**
* 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;
/**
* Commander.js tricks
*/
var app_conf = Config.filterOptions(opts);
var appConf = {};
if (typeof app_conf.name == 'function')
delete app_conf.name;
delete app_conf.args;
// Retrieve arguments via -- <args>
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;
app_conf.script = script;
if(!app_conf.namespace)
app_conf.namespace = 'default';
if ((appConf = Common.verifyConfs(app_conf)) instanceof Error) {
Common.err(appConf)
return cb ? cb(Common.retErr(appConf)) : that.exitCli(conf.ERROR_EXIT);
}
app_conf = appConf[0];
if (opts.watchDelay) {
if (typeof opts.watchDelay === "string" && opts.watchDelay.indexOf("ms") !== -1)
app_conf.watch_delay = parseInt(opts.watchDelay);
else {
app_conf.watch_delay = parseFloat(opts.watchDelay) * 1000;
}
}
var mas = [];
if(typeof opts.ext != 'undefined')
hf.make_available_extension(opts, mas); // for -e flag
mas.length > 0 ? app_conf.ignore_watch = mas : 0;
/**
* If -w option, write configuration to configuration.json file
*/
if (app_conf.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);
}
}
series([
restartExistingProcessName,
restartExistingNameSpace,
restartExistingProcessId,
restartExistingProcessPathOrStartNew
], function(err, data) {
if (err instanceof Error)
return cb ? cb(err) : that.exitCli(conf.ERROR_EXIT);
var ret = {};
data.forEach(function(_dt) {
if (_dt !== undefined)
ret = _dt;
});
return cb ? cb(null, ret) : that.speedList();
});
/**
* If start <app_name> start/restart application
*/
function restartExistingProcessName(cb) {
if (!isNaN(script) ||
(typeof script === 'string' && script.indexOf('/') != -1) ||
(typeof script === 'string' && path.extname(script) !== ''))
return cb(null);
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);
});
}
/**
* If start <namespace> start/restart namespace
*/
function restartExistingNameSpace(cb) {
if (!isNaN(script) ||
(typeof script === 'string' && script.indexOf('/') != -1) ||
(typeof script === 'string' && path.extname(script) !== ''))
return cb(null);
if (script !== 'all') {
that.Client.getProcessIdsByNamespace(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);
});
}
}
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 restartExistingProcessPathOrStartNew(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;
procs.forEach(function(proc) {
if (proc.pm2_env.pm_exec_path == full_path &&
proc.pm2_env.name == app_conf.name)
managed_script = proc;
});
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;
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.err('Script already launched, add -f option to force re-execution');
return cb(new Error('Script already launched'));
}
var resolved_paths = null;
try {
resolved_paths = Common.resolveAppAttributes({
cwd : that.cwd,
pm2_home : that.pm2_home
}, app_conf);
} catch(e) {
Common.err(e.message);
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);
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;
var additional_env = Modularizer.getAdditionalConf(resolved_paths.name);
Object.assign(resolved_paths.env, additional_env);
// 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));
}
Common.printOut(conf.PREFIX_MSG + 'Done.');
return cb(true, data);
});
return false;
});
}
}
/**
* 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 staticConf = [];
var deployConf = {};
var apps_info = [];
var that = this;
/**
* Get File configuration
*/
if (typeof(cb) === 'undefined' && typeof(pipe) === 'function') {
cb = pipe;
}
if (typeof(file) === 'object') {
config = file;
} else if (pipe === 'pipe') {
config = Common.parseConfig(file, 'pipe');
} else {
var data = null;
var isAbsolute = path.isAbsolute(file)
var file_path = isAbsolute ? file : path.join(that.cwd, file);
debug('Resolved filepath %s', file_path);
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);
}
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);
}
}
/**
* Alias some optional fields
*/
if (config.deploy)
deployConf = config.deploy;
if (config.static)
staticConf = config.static;
if (config.apps)
appConf = config.apps;
else if (config.pm2)
appConf = config.pm2;
else
appConf = config;
if (!Array.isArray(appConf))
appConf = [appConf];
if ((appConf = Common.verifyConfs(appConf)) instanceof Error)
return cb ? cb(appConf) : that.exitCli(conf.ERROR_EXIT);
process.env.PM2_JSON_PROCESSING = true;
// Get App list
var apps_name = [];
var proc_list = {};
// Add statics to apps
staticConf.forEach(function(serve) {
appConf.push({
name: serve.name ? serve.name : `static-page-server-${serve.port}`,
script: path.resolve(__dirname, 'API', 'Serve.js'),
env: {
PM2_SERVE_PORT: serve.port,
PM2_SERVE_HOST: serve.host,
PM2_SERVE_PATH: serve.path,
PM2_SERVE_SPA: serve.spa,
PM2_SERVE_DIRECTORY: serve.directory,
PM2_SERVE_BASIC_AUTH: serve.basic_auth !== undefined,
PM2_SERVE_BASIC_AUTH_USERNAME: serve.basic_auth ? serve.basic_auth.username : null,
PM2_SERVE_BASIC_AUTH_PASSWORD: serve.basic_auth ? serve.basic_auth.password : null,
PM2_SERVE_MONITOR: serve.monitor
}
});
});
// Here we pick only the field we want from the CLI when starting a JSON
appConf.forEach(function(app) {
if (!app.env) { app.env = {}; }
app.env.io = app.io;
// --only <app>
if (opts.only) {
var apps = opts.only.split(/,| /)
if (apps.indexOf(app.name) == -1)
return false
}
// Namespace
if (!app.namespace) {
if (opts.namespace)
app.namespace = opts.namespace;
else
app.namespace = 'default';
}
// --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;
if (opts.install_url)
app.install_url = opts.install_url;
// --instances <nb>
if (opts.instances && typeof(opts.instances) === 'number')
app.instances = opts.instances;
// --uid <user>
if (opts.uid)
app.uid = opts.uid;
// --gid <user>
if (opts.gid)
app.gid = opts.gid;
// Specific
if (app.append_env_to_name && opts.env)
app.name += ('-' + opts.env);
if (opts.name_prefix && app.name.indexOf(opts.name_prefix) == -1)
app.name = `${opts.name_prefix}:${app.name}`
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);
}
/**
* 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
*/
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' ||
action == 'softReloadProcessId' ||
action == 'restartProcessId'))
throw new Error('Wrong action called');
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);
});
// 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 Object.assign(e1, e2);
});
// 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);
// 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();
});
}, 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;
});
function startApps(app_name_to_start, cb) {
var apps_to_start = [];
var apps_started = [];
var apps_errored = [];
appConf.forEach(function(app, i) {
if (app_name_to_start.indexOf(app.name) != -1) {
apps_to_start.push(appConf[i]);
}
});
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) {
apps_errored.push(e)
Common.err(`Error: ${e.message}`)
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;
var additional_env = Modularizer.getAdditionalConf(resolved_paths.name);
Object.assign(resolved_paths.env, additional_env);
resolved_paths.env = Common.mergeEnvironmentVariables(resolved_paths, opts.env, deployConf);
delete resolved_paths.env.current_conf;
// Is KM linked?
resolved_paths.km_link = that.gl_is_km_linked;
if (resolved_paths.wait_ready) {
Common.warn(`App ${resolved_paths.name} has option 'wait_ready' set, waiting for app to be ready...`)
}
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) {
var final_error = err || apps_errored.length > 0 ? apps_errored : null
return cb ? cb(final_error, 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 {
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);
}
// Backward compatibility
if (appConf.apps)
appConf = appConf.apps;
if (!Array.isArray(appConf))
appConf = [appConf];
if ((appConf = Common.verifyConfs(appConf)) instanceof Error)
return cb ? cb(appConf) : that.exitCli(conf.ERROR_EXIT);
eachLimit(appConf, conf.CONCURRENT_ACTIONS, function(proc, next1) {
var name = '';
var new_env;
if (!proc.name)
name = path.basename(proc.script);
else
name = proc.name;
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.getProcessIdByName(name, function(err, ids) {
if (err) {
Common.printError(err);
return next1();
}
if (!ids) return next1();
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) {
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
*/
_operate (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 = {};
}
// Set via env.update (JSON processing)
if (envs.updateEnv === true)
update_env = true;
var concurrent_actions = envs.parallel || conf.CONCURRENT_ACTIONS;
if (!process.env.PM2_JSON_PROCESSING || envs.commands) {
envs = that._handleAttributeUpdate(envs);
}
/**
* Set current updated configuration if not passed
*/
if (!envs.current_conf) {
var _conf = fclone(envs);
envs = {
current_conf : _conf
}
// Is KM linked?
envs.current_conf.km_link = that.gl_is_km_linked;
}
/**
* 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 (ids.length <= 2)
concurrent_actions = 1;
if (action_name == 'deleteProcessId')
concurrent_actions = 10;
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 = Object.assign({}, process.env);
Object.keys(envs).forEach(function(k) {
new_env[k] = envs[k];
});
}
else {
new_env = envs;
}
opts = {
id : id,
env : new_env
};
}
else {
opts = id;
}
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 ${id} not found`);
}
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 (!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 (action_name == 'stopProcessId' && proc.pm2_env && proc.pm2_env.cron_restart) {
Common.warn(`App ${chalk.bold(proc.pm2_env.name)} stopped but CRON RESTART is still UP ${proc.pm2_env.cron_restart}`)
}
if (!proc.pm2_env) return false;
ret.push({
name : proc.pm2_env.name,
namespace: proc.pm2_env.namespace,
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,
namespace: proc.pm2_env.namespace,
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) {
if (err) return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT);
return cb ? cb(null, ret) : that.speedList();
});
}
if (process_name == 'all') {
// When using shortcuts like 'all', do not delete modules
var fn
if (process.env.PM2_STATUS == 'stopping')
that.Client.getAllProcessId(function(err, ids) {
reoperate(err, ids)
});
else
that.Client.getAllProcessIdWithoutModules(function(err, ids) {
reoperate(err, ids)
});
function reoperate(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);
}
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, ''));
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);
}
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) {
/**
* 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);
Object.assign(envs, additional_env);
return processIds(ids, cb);
}
that.Client.getProcessIdsByNamespace(process_name, allow_module_restart, function (err, ns_process_ids) {
if (err) {
Common.printError(err);
return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT);
}
if (!ns_process_ids || ns_process_ids.length === 0) {
Common.printError(conf.PREFIX_MSG_ERR + 'Process or Namespace %s not found', process_name);
return cb ? cb(new Error('process or namespace 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 ns_additional_env = Modularizer.getAdditionalConf(process_name);
Object.assign(envs, ns_additional_env);
return processIds(ns_process_ids, cb);
});
});
} else {
if (that.pm2_configuration.docker == "true" ||
that.pm2_configuration.docker == true) {
// Docker/Systemd process interaction detection
that.Client.executeRemote('getMonitorData', {}, (err, proc_list) => {
var higher_id = 0
proc_list.forEach(p => { p.pm_id > higher_id ? higher_id = p.pm_id : null })
// Is Docker/Systemd
if (process_name > higher_id)
return DockerMgmt.processCommand(that, higher_id, process_name, action_name, (err) => {
if (err) {
Common.printError(conf.PREFIX_MSG_ERR + (err.message ? err.message : err));
return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT);
}
return cb ? cb(null, ret) : that.speedList();
})
// 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);
// Check if application name as number is an namespace
that.Client.getProcessIdsByNamespace(process_name, function(err, ns_process_ids) {
if (ns_process_ids.length > 0)
return processIds(ns_process_ids, cb);
// Else operate on pm id
return processIds([process_name], 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);
// Check if application name as number is an namespace
that.Client.getProcessIdsByNamespace(process_name, function(err, ns_process_ids) {
if (ns_process_ids.length > 0)
return processIds(ns_process_ids, cb);
// Else operate on pm id
return processIds([process_name], cb);
});
});
}
}
}
/**
* Converts CamelCase Commander.js arguments
* to Underscore
* (nodeArgs -> node_args)
*/
_handleAttributeUpdate (opts) {
var conf = Config.filterOptions(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;
}
if (argsIndex == -1)
delete appConf.args;
if (appConf.name == 'undefined')
delete appConf.name;
delete appConf.exec_mode;
if (util.isArray(appConf.watch) && appConf.watch.length === 0) {
if (!~opts.rawArgs.indexOf('--watch'))
delete appConf.watch
}
// 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;
}
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);
}
console.log(id);
return cb ? cb(null, id) : that.exitCli(conf.SUCCESS_EXIT);
});
}
/**
* Description
* @method jlist
* @param {} debug
* @return
*/
jlist (debug) {
var that = this;
that.Client.executeRemote('getMonitorData', {}, function(err, list) {
if (err) {
Common.printError(err);
return that.exitCli(conf.ERROR_EXIT);
}
if (debug) {
process.stdout.write(util.inspect(list, false, null, false));
}
else {
process.stdout.write(JSON.stringify(list));
}
that.exitCli(conf.SUCCESS_EXIT);
});
}
/**
* Display system information
* @method slist
* @return
*/
slist (tree) {
this.Client.executeRemote('getSystemData', {}, (err, sys_infos) => {
if (err) {
Common.err(err)
return this.exitCli(conf.ERROR_EXIT)
}
if (tree === true) {
var treeify = require('./tools/treeify.js')
console.log(treeify.asTree(sys_infos, true))
}
else
process.stdout.write(util.inspect(sys_infos, false, null, false))
this.exitCli(conf.SUCCESS_EXIT)
})
}
/**
* Description
* @method speedList
* @return
*/
speedList (code, apps_acted) {
var that = this;
var systemdata = null
var acted = []
if ((code != 0 && code != null)) {
return that.exitCli(code ? code : conf.SUCCESS_EXIT);
}
if (apps_acted && apps_acted.length > 0) {
apps_acted.forEach(proc => {
acted.push(proc.pm2_env ? proc.pm2_env.pm_id : proc.pm_id)
})
}
// 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;
return that.Client.executeRemote('getMonitorData', {}, (err, proc_list) => {
doList(err, proc_list)
})
function doList(err, list) {
if (err) {
if (that.gl_retry == 0) {
that.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.list_min(list);
}
else if (commander.miniList && !commander.silent)
UX.list_min(list);
else if (!commander.silent) {
if (that.gl_interact_infos) {
var dashboard_url = `https://app.pm2.io/#/r/${that.gl_interact_infos.public_key}`
if (that.gl_interact_infos.info_node != 'https://root.keymetrics.io') {
dashboard_url = `${that.gl_interact_infos.info_node}/#/r/${that.gl_interact_infos.public_key}`
}
Common.printOut('%s PM2+ activated | Instance Name: %s | Dash: %s',
chalk.green.bold('⇆'),
chalk.bold(that.gl_interact_infos.machine_name),
chalk.bold(dashboard_url))
}
UX.list(list, commander);
//Common.printOut(chalk.white.italic(' Use `pm2 show <id|name>` 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);
}
// if (process.stdout.isTTY) if looking for start logs
else if (!process.env.TRAVIS && process.env.NODE_ENV != 'test' && acted.length > 0 && (commander.attach === true)) {
Common.info(`Log streaming apps id: ${chalk.cyan(acted.join(' '))}, exit with Ctrl-C or will exit in 10secs`)
// setTimeout(() => {
// Common.info(`Log streaming exited automatically, run 'pm2 logs' to continue watching logs`)
// return that.exitCli(code ? code : conf.SUCCESS_EXIT);
// }, 10000)
return acted.forEach((proc_name) => {
that.streamLogs(proc_name, 0, false, null, false);
})
}
else {
return that.exitCli(code ? code : conf.SUCCESS_EXIT);
}
}
}
/**
* Scale up/down a process
* @method scale
*/
scale (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);
}
function end() {
return cb ? cb(null, {success:true}) : that.speedList();
}
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;
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;
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);
}
}
});
}
/**
* Description
* @method describeProcess
* @param {} pm2_id
* @return
*/
describe (pm2_id, cb) {
var that = this;
var found_proc = [];
that.Client.executeRemote('getMonitorData', {}, function(err, list) {
if (err) {
Common.printError('Error retrieving process list: ' + err);
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 (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);
}
if (!cb) {
found_proc.forEach(function(proc) {
UX.describe(proc);
});
}
return cb ? cb(null, found_proc) : that.exitCli(conf.SUCCESS_EXIT);
});
}
/**
* API method to perform a deep update of PM2
* @method deepUpdate
*/
deepUpdate (cb) {
var that = this;
Common.printOut(conf.PREFIX_MSG + 'Updating PM2...');
var child = sexec("npm i -g pm2@latest; pm2 update");
child.stdout.on('end', function() {
Common.printOut(conf.PREFIX_MSG + 'PM2 successfully updated');
cb ? cb(null, {success:true}) : that.exitCli(conf.SUCCESS_EXIT);
});
}
};
//////////////////////////
// Load all API methods //
//////////////////////////
require('./API/Extra.js')(API);
require('./API/Deploy.js')(API);
require('./API/Modules/index.js')(API);
require('./API/pm2-plus/link.js')(API);
require('./API/pm2-plus/process-selector.js')(API);
require('./API/pm2-plus/helpers.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);
module.exports = API;