1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927 |
- /**
- * 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;
|