@zappinginc/zm2 6.0.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.local.json +8 -0
- package/.gitattributes +4 -0
- package/.mocharc.js +14 -0
- package/CHANGELOG.md +2416 -0
- package/CLAUDE.md +84 -0
- package/CONTRIBUTING.md +124 -0
- package/GNU-AGPL-3.0.txt +665 -0
- package/LICENSE +1 -0
- package/README.md +248 -0
- package/bin/zm2 +3 -0
- package/bin/zm2-dev +3 -0
- package/bin/zm2-docker +3 -0
- package/bin/zm2-runtime +3 -0
- package/bin/zm2-windows +3 -0
- package/bin/zm2.ps1 +3 -0
- package/bun.lock +421 -0
- package/constants.js +114 -0
- package/index.js +13 -0
- package/lib/API/Configuration.js +212 -0
- package/lib/API/Containerizer.js +335 -0
- package/lib/API/Dashboard.js +459 -0
- package/lib/API/Deploy.js +117 -0
- package/lib/API/Extra.js +775 -0
- package/lib/API/ExtraMgmt/Docker.js +30 -0
- package/lib/API/Log.js +315 -0
- package/lib/API/LogManagement.js +371 -0
- package/lib/API/Modules/LOCAL.js +122 -0
- package/lib/API/Modules/Modularizer.js +148 -0
- package/lib/API/Modules/NPM.js +445 -0
- package/lib/API/Modules/TAR.js +362 -0
- package/lib/API/Modules/flagExt.js +46 -0
- package/lib/API/Modules/index.js +120 -0
- package/lib/API/Monit.js +247 -0
- package/lib/API/Serve.js +343 -0
- package/lib/API/Startup.js +629 -0
- package/lib/API/UX/helpers.js +213 -0
- package/lib/API/UX/index.js +9 -0
- package/lib/API/UX/pm2-describe.js +193 -0
- package/lib/API/UX/pm2-ls-minimal.js +31 -0
- package/lib/API/UX/pm2-ls.js +483 -0
- package/lib/API/Version.js +382 -0
- package/lib/API/interpreter.json +12 -0
- package/lib/API/pm2-plus/PM2IO.js +372 -0
- package/lib/API/pm2-plus/auth-strategies/CliAuth.js +288 -0
- package/lib/API/pm2-plus/auth-strategies/WebAuth.js +187 -0
- package/lib/API/pm2-plus/helpers.js +97 -0
- package/lib/API/pm2-plus/link.js +126 -0
- package/lib/API/pm2-plus/pres/motd +16 -0
- package/lib/API/pm2-plus/pres/motd.update +26 -0
- package/lib/API/pm2-plus/pres/welcome +28 -0
- package/lib/API/pm2-plus/process-selector.js +52 -0
- package/lib/API/schema.json +379 -0
- package/lib/API.js +1931 -0
- package/lib/Client.js +776 -0
- package/lib/Common.js +911 -0
- package/lib/Configuration.js +304 -0
- package/lib/Daemon.js +456 -0
- package/lib/Event.js +37 -0
- package/lib/God/ActionMethods.js +909 -0
- package/lib/God/ClusterMode.js +97 -0
- package/lib/God/ForkMode.js +297 -0
- package/lib/God/Methods.js +265 -0
- package/lib/God/Reload.js +240 -0
- package/lib/God.js +632 -0
- package/lib/HttpInterface.js +76 -0
- package/lib/ProcessContainer.js +305 -0
- package/lib/ProcessContainerBun.js +360 -0
- package/lib/ProcessContainerFork.js +42 -0
- package/lib/ProcessContainerForkBun.js +33 -0
- package/lib/ProcessUtils.js +55 -0
- package/lib/TreeKill.js +118 -0
- package/lib/Utility.js +430 -0
- package/lib/VersionCheck.js +46 -0
- package/lib/Watcher.js +117 -0
- package/lib/Worker.js +169 -0
- package/lib/binaries/CLI.js +1041 -0
- package/lib/binaries/DevCLI.js +183 -0
- package/lib/binaries/Runtime.js +101 -0
- package/lib/binaries/Runtime4Docker.js +192 -0
- package/lib/completion.js +229 -0
- package/lib/completion.sh +40 -0
- package/lib/motd +36 -0
- package/lib/templates/Dockerfiles/Dockerfile-java.tpl +7 -0
- package/lib/templates/Dockerfiles/Dockerfile-nodejs.tpl +8 -0
- package/lib/templates/Dockerfiles/Dockerfile-ruby.tpl +7 -0
- package/lib/templates/ecosystem-es.tpl +24 -0
- package/lib/templates/ecosystem-simple-es.tpl +8 -0
- package/lib/templates/ecosystem-simple.tpl +6 -0
- package/lib/templates/ecosystem.tpl +22 -0
- package/lib/templates/init-scripts/launchd.tpl +35 -0
- package/lib/templates/init-scripts/openrc.tpl +52 -0
- package/lib/templates/init-scripts/pm2-init-amazon.sh +86 -0
- package/lib/templates/init-scripts/rcd-openbsd.tpl +41 -0
- package/lib/templates/init-scripts/rcd.tpl +44 -0
- package/lib/templates/init-scripts/smf.tpl +43 -0
- package/lib/templates/init-scripts/systemd-online.tpl +22 -0
- package/lib/templates/init-scripts/systemd.tpl +22 -0
- package/lib/templates/init-scripts/upstart.tpl +103 -0
- package/lib/templates/logrotate.d/pm2 +10 -0
- package/lib/templates/sample-apps/http-server/README.md +14 -0
- package/lib/templates/sample-apps/http-server/api.js +9 -0
- package/lib/templates/sample-apps/http-server/ecosystem.config.js +14 -0
- package/lib/templates/sample-apps/http-server/package.json +11 -0
- package/lib/templates/sample-apps/pm2-plus-metrics-actions/README.md +45 -0
- package/lib/templates/sample-apps/pm2-plus-metrics-actions/custom-metrics.js +66 -0
- package/lib/templates/sample-apps/pm2-plus-metrics-actions/ecosystem.config.js +12 -0
- package/lib/templates/sample-apps/pm2-plus-metrics-actions/package.json +11 -0
- package/lib/templates/sample-apps/python-app/README.md +4 -0
- package/lib/templates/sample-apps/python-app/echo.py +7 -0
- package/lib/templates/sample-apps/python-app/ecosystem.config.js +12 -0
- package/lib/templates/sample-apps/python-app/package.json +11 -0
- package/lib/tools/Config.js +248 -0
- package/lib/tools/IsAbsolute.js +20 -0
- package/lib/tools/copydirSync.js +101 -0
- package/lib/tools/deleteFolderRecursive.js +19 -0
- package/lib/tools/find-package-json.js +74 -0
- package/lib/tools/fmt.js +72 -0
- package/lib/tools/isbinaryfile.js +94 -0
- package/lib/tools/json5.js +752 -0
- package/lib/tools/open.js +63 -0
- package/lib/tools/passwd.js +58 -0
- package/lib/tools/promise.min.js +1 -0
- package/lib/tools/sexec.js +55 -0
- package/lib/tools/treeify.js +113 -0
- package/lib/tools/which.js +120 -0
- package/lib/tools/xdg-open +861 -0
- package/package.json +219 -0
- package/paths.js +93 -0
- package/pm2 +11 -0
- package/preinstall.js +24 -0
- package/run.sh +9 -0
- package/types/index.d.ts +722 -0
- package/types/tsconfig.json +14 -0
package/lib/API.js
ADDED
|
@@ -0,0 +1,1931 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright 2013-2022 the ZM2 project authors. All rights reserved.
|
|
3
|
+
* Use of this source code is governed by a license that
|
|
4
|
+
* can be found in the LICENSE file.
|
|
5
|
+
*/
|
|
6
|
+
'use strict';
|
|
7
|
+
|
|
8
|
+
const commander = require('commander');
|
|
9
|
+
const fs = require('fs');
|
|
10
|
+
const path = require('path');
|
|
11
|
+
const eachLimit = require('async/eachLimit');
|
|
12
|
+
const series = require('async/series');
|
|
13
|
+
const debug = require('debug')('zm2:cli');
|
|
14
|
+
const util = require('util');
|
|
15
|
+
const chalk = require('ansis');
|
|
16
|
+
const fclone = require('fclone');
|
|
17
|
+
|
|
18
|
+
var DockerMgmt = require('./API/ExtraMgmt/Docker.js')
|
|
19
|
+
var conf = require('../constants.js');
|
|
20
|
+
var Client = require('./Client');
|
|
21
|
+
var Common = require('./Common');
|
|
22
|
+
var KMDaemon = require('@pm2/agent/src/InteractorClient');
|
|
23
|
+
var Config = require('./tools/Config');
|
|
24
|
+
var Modularizer = require('./API/Modules/Modularizer.js');
|
|
25
|
+
var path_structure = require('../paths.js');
|
|
26
|
+
var UX = require('./API/UX');
|
|
27
|
+
var pkg = require('../package.json');
|
|
28
|
+
var hf = require('./API/Modules/flagExt.js');
|
|
29
|
+
var Configuration = require('./Configuration.js');
|
|
30
|
+
const sexec = require('./tools/sexec.js')
|
|
31
|
+
|
|
32
|
+
var IMMUTABLE_MSG = chalk.bold.blue('Use --update-env to update environment variables');
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Main Function to be imported
|
|
36
|
+
* can be aliased to ZM2
|
|
37
|
+
*
|
|
38
|
+
* To use it when ZM2 is installed as a module:
|
|
39
|
+
*
|
|
40
|
+
* var ZM2 = require('zm2');
|
|
41
|
+
*
|
|
42
|
+
* var zm2 = ZM2(<opts>);
|
|
43
|
+
*
|
|
44
|
+
*
|
|
45
|
+
* @param {Object} opts
|
|
46
|
+
* @param {String} [opts.cwd=<current>] override zm2 cwd for starting scripts
|
|
47
|
+
* @param {String} [opts.pm2_home=[<paths.js>]] zm2 directory for log, pids, socket files
|
|
48
|
+
* @param {Boolean} [opts.independent=false] unique ZM2 instance (random pm2_home)
|
|
49
|
+
* @param {Boolean} [opts.daemon_mode=true] should be called in the same process or not
|
|
50
|
+
* @param {String} [opts.public_key=null] zm2 plus bucket public key
|
|
51
|
+
* @param {String} [opts.secret_key=null] zm2 plus bucket secret key
|
|
52
|
+
* @param {String} [opts.machine_name=null] zm2 plus instance name
|
|
53
|
+
*/
|
|
54
|
+
class API {
|
|
55
|
+
|
|
56
|
+
constructor (opts) {
|
|
57
|
+
if (!opts) opts = {};
|
|
58
|
+
var that = this;
|
|
59
|
+
|
|
60
|
+
this.daemon_mode = typeof(opts.daemon_mode) == 'undefined' ? true : opts.daemon_mode;
|
|
61
|
+
this.pm2_home = conf.PM2_ROOT_PATH;
|
|
62
|
+
this.public_key = conf.PUBLIC_KEY || opts.public_key || null;
|
|
63
|
+
this.secret_key = conf.SECRET_KEY || opts.secret_key || null;
|
|
64
|
+
this.machine_name = conf.MACHINE_NAME || opts.machine_name || null
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* CWD resolution
|
|
68
|
+
*/
|
|
69
|
+
this.cwd = process.cwd();
|
|
70
|
+
if (opts.cwd) {
|
|
71
|
+
this.cwd = path.resolve(opts.cwd);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* ZM2 HOME resolution
|
|
76
|
+
*/
|
|
77
|
+
if (opts.pm2_home && opts.independent == true)
|
|
78
|
+
throw new Error('You cannot set a zm2_home and independent instance in same time');
|
|
79
|
+
|
|
80
|
+
if (opts.pm2_home) {
|
|
81
|
+
// Override default conf file
|
|
82
|
+
this.pm2_home = opts.pm2_home;
|
|
83
|
+
conf = Object.assign(conf, path_structure(this.pm2_home));
|
|
84
|
+
}
|
|
85
|
+
else if (opts.independent == true && conf.IS_WINDOWS === false) {
|
|
86
|
+
// Create an unique zm2 instance
|
|
87
|
+
const crypto = require('crypto');
|
|
88
|
+
var random_file = crypto.randomBytes(8).toString('hex');
|
|
89
|
+
this.pm2_home = path.join('/tmp', random_file);
|
|
90
|
+
|
|
91
|
+
// If we dont explicitly tell to have a daemon
|
|
92
|
+
// It will go as in proc
|
|
93
|
+
if (typeof(opts.daemon_mode) == 'undefined')
|
|
94
|
+
this.daemon_mode = false;
|
|
95
|
+
conf = Object.assign(conf, path_structure(this.pm2_home));
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
this._conf = conf;
|
|
99
|
+
|
|
100
|
+
if (conf.IS_WINDOWS) {
|
|
101
|
+
// Weird fix, may need to be dropped
|
|
102
|
+
// @todo windows connoisseur double check
|
|
103
|
+
if (process.stdout._handle && process.stdout._handle.setBlocking)
|
|
104
|
+
process.stdout._handle.setBlocking(true);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
this.Client = new Client({
|
|
108
|
+
pm2_home: that.pm2_home,
|
|
109
|
+
conf: this._conf,
|
|
110
|
+
secret_key: this.secret_key,
|
|
111
|
+
public_key: this.public_key,
|
|
112
|
+
daemon_mode: this.daemon_mode,
|
|
113
|
+
machine_name: this.machine_name
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
this.pm2_configuration = Configuration.getSync('pm2') || {}
|
|
117
|
+
|
|
118
|
+
this.gl_interact_infos = null;
|
|
119
|
+
this.gl_is_km_linked = false;
|
|
120
|
+
|
|
121
|
+
try {
|
|
122
|
+
var pid = fs.readFileSync(conf.INTERACTOR_PID_PATH);
|
|
123
|
+
pid = parseInt(pid.toString().trim());
|
|
124
|
+
process.kill(pid, 0);
|
|
125
|
+
that.gl_is_km_linked = true;
|
|
126
|
+
} catch (e) {
|
|
127
|
+
that.gl_is_km_linked = false;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// For testing purposes
|
|
131
|
+
if (this.secret_key && process.env.NODE_ENV == 'local_test')
|
|
132
|
+
that.gl_is_km_linked = true;
|
|
133
|
+
|
|
134
|
+
KMDaemon.ping(this._conf, function(err, result) {
|
|
135
|
+
if (!err && result === true) {
|
|
136
|
+
fs.readFile(conf.INTERACTION_CONF, (err, _conf) => {
|
|
137
|
+
if (!err) {
|
|
138
|
+
try {
|
|
139
|
+
that.gl_interact_infos = JSON.parse(_conf.toString())
|
|
140
|
+
} catch(e) {
|
|
141
|
+
var json5 = require('./tools/json5.js')
|
|
142
|
+
try {
|
|
143
|
+
that.gl_interact_infos = json5.parse(_conf.toString())
|
|
144
|
+
} catch(e) {
|
|
145
|
+
console.error(e)
|
|
146
|
+
that.gl_interact_infos = null
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
})
|
|
151
|
+
}
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
this.gl_retry = 0;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Connect to ZM2
|
|
159
|
+
* Calling this command is now optional
|
|
160
|
+
*
|
|
161
|
+
* @param {Function} cb callback once zm2 is ready for commands
|
|
162
|
+
*/
|
|
163
|
+
connect (noDaemon, cb) {
|
|
164
|
+
var that = this;
|
|
165
|
+
this.start_timer = new Date();
|
|
166
|
+
|
|
167
|
+
if (typeof(cb) == 'undefined') {
|
|
168
|
+
cb = noDaemon;
|
|
169
|
+
noDaemon = false;
|
|
170
|
+
} else if (noDaemon === true) {
|
|
171
|
+
// Backward compatibility with ZM2 1.x
|
|
172
|
+
this.Client.daemon_mode = false;
|
|
173
|
+
this.daemon_mode = false;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
this.Client.start(function(err, meta) {
|
|
177
|
+
if (err)
|
|
178
|
+
return cb(err);
|
|
179
|
+
|
|
180
|
+
if (meta.new_pm2_instance == false && that.daemon_mode === true)
|
|
181
|
+
return cb(err, meta);
|
|
182
|
+
|
|
183
|
+
that.launchSysMonitoring(() => {})
|
|
184
|
+
// If new zm2 instance has been popped
|
|
185
|
+
// Launch all modules
|
|
186
|
+
that.launchAll(that, function(err_mod) {
|
|
187
|
+
return cb(err, meta);
|
|
188
|
+
});
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Usefull when custom ZM2 created with independent flag set to true
|
|
194
|
+
* This will cleanup the newly created instance
|
|
195
|
+
* by removing folder, killing ZM2 and so on
|
|
196
|
+
*
|
|
197
|
+
* @param {Function} cb callback once cleanup is successfull
|
|
198
|
+
*/
|
|
199
|
+
destroy (cb) {
|
|
200
|
+
var that = this;
|
|
201
|
+
|
|
202
|
+
debug('Killing and deleting current deamon');
|
|
203
|
+
|
|
204
|
+
this.killDaemon(function() {
|
|
205
|
+
var cmd = 'rm -rf ' + that.pm2_home;
|
|
206
|
+
var test_path = path.join(that.pm2_home, 'module_conf.json');
|
|
207
|
+
var test_path_2 = path.join(that.pm2_home, 'pm2.pid');
|
|
208
|
+
|
|
209
|
+
if (that.pm2_home.indexOf('.pm2') > -1)
|
|
210
|
+
return cb(new Error('Destroy is not a allowed method on .pm2'));
|
|
211
|
+
|
|
212
|
+
fs.access(test_path, fs.constants.R_OK, function(err) {
|
|
213
|
+
if (err) return cb(err);
|
|
214
|
+
debug('Deleting temporary folder %s', that.pm2_home);
|
|
215
|
+
sexec(cmd, cb);
|
|
216
|
+
});
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Disconnect from ZM2 instance
|
|
222
|
+
* This will allow your software to exit by itself
|
|
223
|
+
*
|
|
224
|
+
* @param {Function} [cb] optional callback once connection closed
|
|
225
|
+
*/
|
|
226
|
+
disconnect (cb) {
|
|
227
|
+
var that = this;
|
|
228
|
+
|
|
229
|
+
if (!cb) cb = function() {};
|
|
230
|
+
|
|
231
|
+
this.Client.close(function(err, data) {
|
|
232
|
+
debug('The session lasted %ds', (new Date() - that.start_timer) / 1000);
|
|
233
|
+
return cb(err, data);
|
|
234
|
+
});
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Alias on disconnect
|
|
239
|
+
* @param cb
|
|
240
|
+
*/
|
|
241
|
+
close (cb) {
|
|
242
|
+
this.disconnect(cb);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Launch modules
|
|
247
|
+
*
|
|
248
|
+
* @param {Function} cb callback once zm2 has launched modules
|
|
249
|
+
*/
|
|
250
|
+
launchModules (cb) {
|
|
251
|
+
this.launchAll(this, cb);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Enable bus allowing to retrieve various process event
|
|
256
|
+
* like logs, restarts, reloads
|
|
257
|
+
*
|
|
258
|
+
* @param {Function} cb callback called with 1st param err and 2nb param the bus
|
|
259
|
+
*/
|
|
260
|
+
launchBus (cb) {
|
|
261
|
+
this.Client.launchBus(cb);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Exit methods for API
|
|
266
|
+
* @param {Integer} code exit code for terminal
|
|
267
|
+
*/
|
|
268
|
+
exitCli (code) {
|
|
269
|
+
var that = this;
|
|
270
|
+
|
|
271
|
+
// Do nothing if ZM2 called programmatically (also in speedlist)
|
|
272
|
+
if (conf.PM2_PROGRAMMATIC && (process.env.ZM2_USAGE || process.env.PM2_USAGE) != 'CLI') return false;
|
|
273
|
+
|
|
274
|
+
KMDaemon.disconnectRPC(function() {
|
|
275
|
+
that.Client.close(function() {
|
|
276
|
+
code = code || 0;
|
|
277
|
+
// Safe exits process after all streams are drained.
|
|
278
|
+
// file descriptor flag.
|
|
279
|
+
var fds = 0;
|
|
280
|
+
// exits process when stdout (1) and sdterr(2) are both drained.
|
|
281
|
+
function tryToExit() {
|
|
282
|
+
if ((fds & 1) && (fds & 2)) {
|
|
283
|
+
debug('This command took %ds to execute', (new Date() - that.start_timer) / 1000);
|
|
284
|
+
process.exit(code);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
[process.stdout, process.stderr].forEach(function(std) {
|
|
289
|
+
var fd = std.fd;
|
|
290
|
+
if (!std.bufferSize) {
|
|
291
|
+
// bufferSize equals 0 means current stream is drained.
|
|
292
|
+
fds = fds | fd;
|
|
293
|
+
} else {
|
|
294
|
+
// Appends nothing to the std queue, but will trigger `tryToExit` event on `drain`.
|
|
295
|
+
std.write && std.write('', function() {
|
|
296
|
+
fds = fds | fd;
|
|
297
|
+
tryToExit();
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
// Does not write anything more.
|
|
301
|
+
delete std.write;
|
|
302
|
+
});
|
|
303
|
+
tryToExit();
|
|
304
|
+
});
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
////////////////////////////
|
|
309
|
+
// Application management //
|
|
310
|
+
////////////////////////////
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Start a file or json with configuration
|
|
314
|
+
* @param {Object||String} cmd script to start or json
|
|
315
|
+
* @param {Function} cb called when application has been started
|
|
316
|
+
*/
|
|
317
|
+
start (cmd, opts, cb) {
|
|
318
|
+
if (typeof(opts) == "function") {
|
|
319
|
+
cb = opts;
|
|
320
|
+
opts = {};
|
|
321
|
+
}
|
|
322
|
+
if (!opts) opts = {};
|
|
323
|
+
|
|
324
|
+
var that = this;
|
|
325
|
+
if (Array.isArray(opts.watch) && opts.watch.length === 0)
|
|
326
|
+
opts.watch = (opts.rawArgs ? !!~opts.rawArgs.indexOf('--watch') : !!~process.argv.indexOf('--watch')) || false;
|
|
327
|
+
|
|
328
|
+
if (Common.isConfigFile(cmd) || (typeof(cmd) === 'object')) {
|
|
329
|
+
that._startJson(cmd, opts, 'restartProcessId', (err, procs) => {
|
|
330
|
+
return cb ? cb(err, procs) : this.speedList()
|
|
331
|
+
})
|
|
332
|
+
}
|
|
333
|
+
else {
|
|
334
|
+
that._startScript(cmd, opts, (err, procs) => {
|
|
335
|
+
return cb ? cb(err, procs) : this.speedList(0)
|
|
336
|
+
})
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* Reset process counters
|
|
342
|
+
*
|
|
343
|
+
* @method resetMetaProcess
|
|
344
|
+
*/
|
|
345
|
+
reset (process_name, cb) {
|
|
346
|
+
var that = this;
|
|
347
|
+
|
|
348
|
+
function processIds(ids, cb) {
|
|
349
|
+
eachLimit(ids, conf.CONCURRENT_ACTIONS, function(id, next) {
|
|
350
|
+
that.Client.executeRemote('resetMetaProcessId', id, function(err, res) {
|
|
351
|
+
if (err) console.error(err);
|
|
352
|
+
Common.printOut(conf.PREFIX_MSG + 'Resetting meta for process id %d', id);
|
|
353
|
+
return next();
|
|
354
|
+
});
|
|
355
|
+
}, function(err) {
|
|
356
|
+
if (err) return cb(Common.retErr(err));
|
|
357
|
+
return cb ? cb(null, {success:true}) : that.speedList();
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
if (process_name == 'all') {
|
|
362
|
+
that.Client.getAllProcessId(function(err, ids) {
|
|
363
|
+
if (err) {
|
|
364
|
+
Common.printError(err);
|
|
365
|
+
return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT);
|
|
366
|
+
}
|
|
367
|
+
return processIds(ids, cb);
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
else if (isNaN(process_name)) {
|
|
371
|
+
that.Client.getProcessIdByName(process_name, function(err, ids) {
|
|
372
|
+
if (err) {
|
|
373
|
+
Common.printError(err);
|
|
374
|
+
return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT);
|
|
375
|
+
}
|
|
376
|
+
if (ids.length === 0) {
|
|
377
|
+
Common.printError('Unknown process name');
|
|
378
|
+
return cb ? cb(new Error('Unknown process name')) : that.exitCli(conf.ERROR_EXIT);
|
|
379
|
+
}
|
|
380
|
+
return processIds(ids, cb);
|
|
381
|
+
});
|
|
382
|
+
} else {
|
|
383
|
+
processIds([process_name], cb);
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
/**
|
|
388
|
+
* Update daemonized ZM2 Daemon
|
|
389
|
+
*
|
|
390
|
+
* @param {Function} cb callback when zm2 has been upgraded
|
|
391
|
+
*/
|
|
392
|
+
update (cb) {
|
|
393
|
+
var that = this;
|
|
394
|
+
|
|
395
|
+
Common.printOut('Be sure to have the latest version by doing `npm install zm2@latest -g` before doing this procedure.');
|
|
396
|
+
|
|
397
|
+
// Dump ZM2 processes
|
|
398
|
+
that.Client.executeRemote('notifyKillPM2', {}, function() {});
|
|
399
|
+
|
|
400
|
+
that.getVersion(function(err, new_version) {
|
|
401
|
+
// If not linked to ZM2 plus, and update ZM2 to latest, display motd.update
|
|
402
|
+
if (!that.gl_is_km_linked && !err && (pkg.version != new_version)) {
|
|
403
|
+
var dt = fs.readFileSync(path.join(__dirname, that._conf.PM2_UPDATE));
|
|
404
|
+
console.log(dt.toString());
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
that.dump(function(err) {
|
|
408
|
+
that.killDaemon(function() {
|
|
409
|
+
that.Client.launchDaemon({interactor:false}, function(err, child) {
|
|
410
|
+
that.Client.launchRPC(function() {
|
|
411
|
+
that.resurrect(function() {
|
|
412
|
+
Common.printOut(chalk.blue.bold('>>>>>>>>>> ZM2 updated'));
|
|
413
|
+
that.launchSysMonitoring(() => {})
|
|
414
|
+
that.launchAll(that, function() {
|
|
415
|
+
KMDaemon.launchAndInteract(that._conf, {
|
|
416
|
+
pm2_version: pkg.version
|
|
417
|
+
}, function(err, data, interactor_proc) {
|
|
418
|
+
})
|
|
419
|
+
setTimeout(() => {
|
|
420
|
+
return cb ? cb(null, {success:true}) : that.speedList();
|
|
421
|
+
}, 250)
|
|
422
|
+
});
|
|
423
|
+
});
|
|
424
|
+
});
|
|
425
|
+
});
|
|
426
|
+
});
|
|
427
|
+
});
|
|
428
|
+
});
|
|
429
|
+
|
|
430
|
+
return false;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
/**
|
|
434
|
+
* Reload an application
|
|
435
|
+
*
|
|
436
|
+
* @param {String} process_name Application Name or All
|
|
437
|
+
* @param {Object} opts Options
|
|
438
|
+
* @param {Function} cb Callback
|
|
439
|
+
*/
|
|
440
|
+
reload (process_name, opts, cb) {
|
|
441
|
+
var that = this;
|
|
442
|
+
|
|
443
|
+
if (typeof(opts) == "function") {
|
|
444
|
+
cb = opts;
|
|
445
|
+
opts = {};
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
var delay = Common.lockReload();
|
|
449
|
+
if (delay > 0 && opts.force != true) {
|
|
450
|
+
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');
|
|
451
|
+
return cb ? cb(new Error('Reload in progress')) : that.exitCli(conf.ERROR_EXIT);
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
if (Common.isConfigFile(process_name))
|
|
455
|
+
that._startJson(process_name, opts, 'reloadProcessId', function(err, apps) {
|
|
456
|
+
Common.unlockReload();
|
|
457
|
+
if (err)
|
|
458
|
+
return cb ? cb(err) : that.exitCli(conf.ERROR_EXIT);
|
|
459
|
+
return cb ? cb(null, apps) : that.exitCli(conf.SUCCESS_EXIT);
|
|
460
|
+
});
|
|
461
|
+
else {
|
|
462
|
+
if (opts && opts.env) {
|
|
463
|
+
var err = 'Using --env [env] without passing the ecosystem.config.js does not work'
|
|
464
|
+
Common.err(err);
|
|
465
|
+
Common.unlockReload();
|
|
466
|
+
return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT);
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
if (opts && !opts.updateEnv)
|
|
470
|
+
Common.printOut(IMMUTABLE_MSG);
|
|
471
|
+
|
|
472
|
+
that._operate('reloadProcessId', process_name, opts, function(err, apps) {
|
|
473
|
+
Common.unlockReload();
|
|
474
|
+
|
|
475
|
+
if (err)
|
|
476
|
+
return cb ? cb(err) : that.exitCli(conf.ERROR_EXIT);
|
|
477
|
+
return cb ? cb(null, apps) : that.exitCli(conf.SUCCESS_EXIT);
|
|
478
|
+
});
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
/**
|
|
483
|
+
* Restart process
|
|
484
|
+
*
|
|
485
|
+
* @param {String} cmd Application Name / Process id / JSON application file / 'all'
|
|
486
|
+
* @param {Object} opts Extra options to be updated
|
|
487
|
+
* @param {Function} cb Callback
|
|
488
|
+
*/
|
|
489
|
+
restart (cmd, opts, cb) {
|
|
490
|
+
if (typeof(opts) == "function") {
|
|
491
|
+
cb = opts;
|
|
492
|
+
opts = {};
|
|
493
|
+
}
|
|
494
|
+
var that = this;
|
|
495
|
+
|
|
496
|
+
if (typeof(cmd) === 'number')
|
|
497
|
+
cmd = cmd.toString();
|
|
498
|
+
|
|
499
|
+
if (cmd == "-") {
|
|
500
|
+
// Restart from PIPED JSON
|
|
501
|
+
process.stdin.resume();
|
|
502
|
+
process.stdin.setEncoding('utf8');
|
|
503
|
+
process.stdin.on('data', function (param) {
|
|
504
|
+
process.stdin.pause();
|
|
505
|
+
that.actionFromJson('restartProcessId', param, opts, 'pipe', cb);
|
|
506
|
+
});
|
|
507
|
+
}
|
|
508
|
+
else if (Common.isConfigFile(cmd) || typeof(cmd) === 'object')
|
|
509
|
+
that._startJson(cmd, opts, 'restartProcessId', cb);
|
|
510
|
+
else {
|
|
511
|
+
if (opts && opts.env) {
|
|
512
|
+
var err = 'Using --env [env] without passing the ecosystem.config.js does not work'
|
|
513
|
+
Common.err(err);
|
|
514
|
+
return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT);
|
|
515
|
+
}
|
|
516
|
+
if (opts && !opts.updateEnv)
|
|
517
|
+
Common.printOut(IMMUTABLE_MSG);
|
|
518
|
+
that._operate('restartProcessId', cmd, opts, cb);
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
/**
|
|
523
|
+
* Delete process
|
|
524
|
+
*
|
|
525
|
+
* @param {String} process_name Application Name / Process id / Application file / 'all'
|
|
526
|
+
* @param {Function} cb Callback
|
|
527
|
+
*/
|
|
528
|
+
delete (process_name, jsonVia, cb) {
|
|
529
|
+
var that = this;
|
|
530
|
+
|
|
531
|
+
if (typeof(jsonVia) === "function") {
|
|
532
|
+
cb = jsonVia;
|
|
533
|
+
jsonVia = null;
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
if (typeof(process_name) === "number") {
|
|
537
|
+
process_name = process_name.toString();
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
if (jsonVia == 'pipe')
|
|
541
|
+
return that.actionFromJson('deleteProcessId', process_name, commander, 'pipe', (err, procs) => {
|
|
542
|
+
return cb ? cb(err, procs) : this.speedList()
|
|
543
|
+
});
|
|
544
|
+
if (Common.isConfigFile(process_name))
|
|
545
|
+
return that.actionFromJson('deleteProcessId', process_name, commander, 'file', (err, procs) => {
|
|
546
|
+
return cb ? cb(err, procs) : this.speedList()
|
|
547
|
+
});
|
|
548
|
+
else {
|
|
549
|
+
that._operate('deleteProcessId', process_name, (err, procs) => {
|
|
550
|
+
return cb ? cb(err, procs) : this.speedList()
|
|
551
|
+
});
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
/**
|
|
556
|
+
* Stop process
|
|
557
|
+
*
|
|
558
|
+
* @param {String} process_name Application Name / Process id / Application file / 'all'
|
|
559
|
+
* @param {Function} cb Callback
|
|
560
|
+
*/
|
|
561
|
+
stop (process_name, cb) {
|
|
562
|
+
var that = this;
|
|
563
|
+
|
|
564
|
+
if (typeof(process_name) === 'number')
|
|
565
|
+
process_name = process_name.toString();
|
|
566
|
+
|
|
567
|
+
if (process_name == "-") {
|
|
568
|
+
process.stdin.resume();
|
|
569
|
+
process.stdin.setEncoding('utf8');
|
|
570
|
+
process.stdin.on('data', function (param) {
|
|
571
|
+
process.stdin.pause();
|
|
572
|
+
that.actionFromJson('stopProcessId', param, commander, 'pipe', (err, procs) => {
|
|
573
|
+
return cb ? cb(err, procs) : this.speedList()
|
|
574
|
+
})
|
|
575
|
+
});
|
|
576
|
+
}
|
|
577
|
+
else if (Common.isConfigFile(process_name))
|
|
578
|
+
that.actionFromJson('stopProcessId', process_name, commander, 'file', (err, procs) => {
|
|
579
|
+
return cb ? cb(err, procs) : this.speedList()
|
|
580
|
+
});
|
|
581
|
+
else
|
|
582
|
+
that._operate('stopProcessId', process_name, (err, procs) => {
|
|
583
|
+
return cb ? cb(err, procs) : this.speedList()
|
|
584
|
+
});
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
/**
|
|
588
|
+
* Get list of all processes managed
|
|
589
|
+
*
|
|
590
|
+
* @param {Function} cb Callback
|
|
591
|
+
*/
|
|
592
|
+
list (opts, cb) {
|
|
593
|
+
var that = this;
|
|
594
|
+
|
|
595
|
+
if (typeof(opts) == 'function') {
|
|
596
|
+
cb = opts;
|
|
597
|
+
opts = null;
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
that.Client.executeRemote('getMonitorData', {}, function(err, list) {
|
|
601
|
+
if (err) {
|
|
602
|
+
Common.printError(err);
|
|
603
|
+
return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT);
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
if (opts && opts.rawArgs && opts.rawArgs.indexOf('--watch') > -1) {
|
|
607
|
+
var dayjs = require('dayjs');
|
|
608
|
+
function show() {
|
|
609
|
+
process.stdout.write('\x1b[2J');
|
|
610
|
+
process.stdout.write('\x1b[0f');
|
|
611
|
+
console.log('Last refresh: ', dayjs().format());
|
|
612
|
+
that.Client.executeRemote('getMonitorData', {}, function(err, list) {
|
|
613
|
+
UX.list(list, null);
|
|
614
|
+
});
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
show();
|
|
618
|
+
setInterval(show, 900);
|
|
619
|
+
return false;
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
return cb ? cb(null, list) : that.speedList(null);
|
|
623
|
+
});
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
/**
|
|
627
|
+
* Kill Daemon
|
|
628
|
+
*
|
|
629
|
+
* @param {Function} cb Callback
|
|
630
|
+
*/
|
|
631
|
+
killDaemon (cb) {
|
|
632
|
+
process.env.ZM2_STATUS = 'stopping'
|
|
633
|
+
|
|
634
|
+
var that = this;
|
|
635
|
+
|
|
636
|
+
that.Client.executeRemote('notifyKillPM2', {}, function() {});
|
|
637
|
+
|
|
638
|
+
that._operate('deleteProcessId', 'all', function(err, list) {
|
|
639
|
+
Common.printOut(conf.PREFIX_MSG + '[v] All Applications Stopped');
|
|
640
|
+
process.env.ZM2_SILENT = 'false';
|
|
641
|
+
|
|
642
|
+
that.killAgent(function(err, data) {
|
|
643
|
+
if (!err) {
|
|
644
|
+
Common.printOut(conf.PREFIX_MSG + '[v] Agent Stopped');
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
that.Client.killDaemon(function(err, res) {
|
|
648
|
+
if (err) Common.printError(err);
|
|
649
|
+
Common.printOut(conf.PREFIX_MSG + '[v] ZM2 Daemon Stopped');
|
|
650
|
+
return cb ? cb(err, res) : that.exitCli(conf.SUCCESS_EXIT);
|
|
651
|
+
});
|
|
652
|
+
|
|
653
|
+
});
|
|
654
|
+
})
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
kill (cb) {
|
|
658
|
+
this.killDaemon(cb);
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
/////////////////////
|
|
662
|
+
// Private methods //
|
|
663
|
+
/////////////////////
|
|
664
|
+
|
|
665
|
+
/**
|
|
666
|
+
* Method to START / RESTART a script
|
|
667
|
+
*
|
|
668
|
+
* @private
|
|
669
|
+
* @param {string} script script name (will be resolved according to location)
|
|
670
|
+
*/
|
|
671
|
+
_startScript (script, opts, cb) {
|
|
672
|
+
if (typeof opts == "function") {
|
|
673
|
+
cb = opts;
|
|
674
|
+
opts = {};
|
|
675
|
+
}
|
|
676
|
+
var that = this;
|
|
677
|
+
|
|
678
|
+
/**
|
|
679
|
+
* Commander.js tricks
|
|
680
|
+
*/
|
|
681
|
+
var app_conf = Config.filterOptions(opts);
|
|
682
|
+
var appConf = {};
|
|
683
|
+
|
|
684
|
+
if (typeof app_conf.name == 'function')
|
|
685
|
+
delete app_conf.name;
|
|
686
|
+
|
|
687
|
+
delete app_conf.args;
|
|
688
|
+
|
|
689
|
+
// Retrieve arguments via -- <args>
|
|
690
|
+
var argsIndex;
|
|
691
|
+
|
|
692
|
+
if (opts.rawArgs && (argsIndex = opts.rawArgs.indexOf('--')) >= 0)
|
|
693
|
+
app_conf.args = opts.rawArgs.slice(argsIndex + 1);
|
|
694
|
+
else if (opts.scriptArgs)
|
|
695
|
+
app_conf.args = opts.scriptArgs;
|
|
696
|
+
|
|
697
|
+
app_conf.script = script;
|
|
698
|
+
if(!app_conf.namespace)
|
|
699
|
+
app_conf.namespace = 'default';
|
|
700
|
+
|
|
701
|
+
if ((appConf = Common.verifyConfs(app_conf)) instanceof Error) {
|
|
702
|
+
Common.err(appConf)
|
|
703
|
+
return cb ? cb(Common.retErr(appConf)) : that.exitCli(conf.ERROR_EXIT);
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
app_conf = appConf[0];
|
|
707
|
+
|
|
708
|
+
if (opts.watchDelay) {
|
|
709
|
+
if (typeof opts.watchDelay === "string" && opts.watchDelay.indexOf("ms") !== -1)
|
|
710
|
+
app_conf.watch_delay = parseInt(opts.watchDelay);
|
|
711
|
+
else {
|
|
712
|
+
app_conf.watch_delay = parseFloat(opts.watchDelay) * 1000;
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
var mas = [];
|
|
717
|
+
if(typeof opts.ext != 'undefined')
|
|
718
|
+
hf.make_available_extension(opts, mas); // for -e flag
|
|
719
|
+
mas.length > 0 ? app_conf.ignore_watch = mas : 0;
|
|
720
|
+
|
|
721
|
+
/**
|
|
722
|
+
* If -w option, write configuration to configuration.json file
|
|
723
|
+
*/
|
|
724
|
+
if (app_conf.write) {
|
|
725
|
+
var dst_path = path.join(process.env.PWD || process.cwd(), app_conf.name + '-zm2.json');
|
|
726
|
+
Common.printOut(conf.PREFIX_MSG + 'Writing configuration to', chalk.blue(dst_path));
|
|
727
|
+
// pretty JSON
|
|
728
|
+
try {
|
|
729
|
+
fs.writeFileSync(dst_path, JSON.stringify(app_conf, null, 2));
|
|
730
|
+
} catch (e) {
|
|
731
|
+
console.error(e.stack || e);
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
series([
|
|
736
|
+
restartExistingProcessName,
|
|
737
|
+
restartExistingNameSpace,
|
|
738
|
+
restartExistingProcessId,
|
|
739
|
+
restartExistingProcessPathOrStartNew
|
|
740
|
+
], function(err, data) {
|
|
741
|
+
if (err instanceof Error)
|
|
742
|
+
return cb ? cb(err) : that.exitCli(conf.ERROR_EXIT);
|
|
743
|
+
|
|
744
|
+
var ret = {};
|
|
745
|
+
|
|
746
|
+
data.forEach(function(_dt) {
|
|
747
|
+
if (_dt !== undefined)
|
|
748
|
+
ret = _dt;
|
|
749
|
+
});
|
|
750
|
+
|
|
751
|
+
return cb ? cb(null, ret) : that.speedList();
|
|
752
|
+
});
|
|
753
|
+
|
|
754
|
+
/**
|
|
755
|
+
* If start <app_name> start/restart application
|
|
756
|
+
*/
|
|
757
|
+
function restartExistingProcessName(cb) {
|
|
758
|
+
if (!isNaN(script) ||
|
|
759
|
+
(typeof script === 'string' && script.indexOf('/') != -1) ||
|
|
760
|
+
(typeof script === 'string' && path.extname(script) !== ''))
|
|
761
|
+
return cb(null);
|
|
762
|
+
|
|
763
|
+
that.Client.getProcessIdByName(script, function(err, ids) {
|
|
764
|
+
if (err && cb) return cb(err);
|
|
765
|
+
if (ids.length > 0) {
|
|
766
|
+
that._operate('restartProcessId', script, opts, function(err, list) {
|
|
767
|
+
if (err) return cb(err);
|
|
768
|
+
Common.printOut(conf.PREFIX_MSG + 'Process successfully started');
|
|
769
|
+
return cb(true, list);
|
|
770
|
+
});
|
|
771
|
+
}
|
|
772
|
+
else return cb(null);
|
|
773
|
+
});
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
/**
|
|
777
|
+
* If start <namespace> start/restart namespace
|
|
778
|
+
*/
|
|
779
|
+
function restartExistingNameSpace(cb) {
|
|
780
|
+
if (!isNaN(script) ||
|
|
781
|
+
(typeof script === 'string' && script.indexOf('/') != -1) ||
|
|
782
|
+
(typeof script === 'string' && path.extname(script) !== ''))
|
|
783
|
+
return cb(null);
|
|
784
|
+
|
|
785
|
+
if (script !== 'all') {
|
|
786
|
+
that.Client.getProcessIdsByNamespace(script, function (err, ids) {
|
|
787
|
+
if (err && cb) return cb(err);
|
|
788
|
+
if (ids.length > 0) {
|
|
789
|
+
that._operate('restartProcessId', script, opts, function (err, list) {
|
|
790
|
+
if (err) return cb(err);
|
|
791
|
+
Common.printOut(conf.PREFIX_MSG + 'Process successfully started');
|
|
792
|
+
return cb(true, list);
|
|
793
|
+
});
|
|
794
|
+
}
|
|
795
|
+
else return cb(null);
|
|
796
|
+
});
|
|
797
|
+
}
|
|
798
|
+
else {
|
|
799
|
+
that._operate('restartProcessId', 'all', function(err, list) {
|
|
800
|
+
if (err) return cb(err);
|
|
801
|
+
Common.printOut(conf.PREFIX_MSG + 'Process successfully started');
|
|
802
|
+
return cb(true, list);
|
|
803
|
+
});
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
function restartExistingProcessId(cb) {
|
|
808
|
+
if (isNaN(script)) return cb(null);
|
|
809
|
+
|
|
810
|
+
that._operate('restartProcessId', script, opts, function(err, list) {
|
|
811
|
+
if (err) return cb(err);
|
|
812
|
+
Common.printOut(conf.PREFIX_MSG + 'Process successfully started');
|
|
813
|
+
return cb(true, list);
|
|
814
|
+
});
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
/**
|
|
818
|
+
* Restart a process with the same full path
|
|
819
|
+
* Or start it
|
|
820
|
+
*/
|
|
821
|
+
function restartExistingProcessPathOrStartNew(cb) {
|
|
822
|
+
that.Client.executeRemote('getMonitorData', {}, function(err, procs) {
|
|
823
|
+
if (err) return cb ? cb(new Error(err)) : that.exitCli(conf.ERROR_EXIT);
|
|
824
|
+
|
|
825
|
+
var full_path = path.resolve(that.cwd, script);
|
|
826
|
+
var managed_script = null;
|
|
827
|
+
|
|
828
|
+
procs.forEach(function(proc) {
|
|
829
|
+
if (proc.pm2_env.pm_exec_path == full_path &&
|
|
830
|
+
proc.pm2_env.name == app_conf.name)
|
|
831
|
+
managed_script = proc;
|
|
832
|
+
});
|
|
833
|
+
|
|
834
|
+
if (managed_script &&
|
|
835
|
+
(managed_script.pm2_env.status == conf.STOPPED_STATUS ||
|
|
836
|
+
managed_script.pm2_env.status == conf.STOPPING_STATUS ||
|
|
837
|
+
managed_script.pm2_env.status == conf.ERRORED_STATUS)) {
|
|
838
|
+
// Restart process if stopped
|
|
839
|
+
var app_name = managed_script.pm2_env.name;
|
|
840
|
+
|
|
841
|
+
that._operate('restartProcessId', app_name, opts, function(err, list) {
|
|
842
|
+
if (err) return cb ? cb(new Error(err)) : that.exitCli(conf.ERROR_EXIT);
|
|
843
|
+
Common.printOut(conf.PREFIX_MSG + 'Process successfully started');
|
|
844
|
+
return cb(true, list);
|
|
845
|
+
});
|
|
846
|
+
return false;
|
|
847
|
+
}
|
|
848
|
+
else if (managed_script && !opts.force) {
|
|
849
|
+
Common.err('Script already launched, add -f option to force re-execution');
|
|
850
|
+
return cb(new Error('Script already launched'));
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
var resolved_paths = null;
|
|
854
|
+
|
|
855
|
+
try {
|
|
856
|
+
resolved_paths = Common.resolveAppAttributes({
|
|
857
|
+
cwd : that.cwd,
|
|
858
|
+
pm2_home : that.pm2_home
|
|
859
|
+
}, app_conf);
|
|
860
|
+
} catch(e) {
|
|
861
|
+
Common.err(e.message);
|
|
862
|
+
return cb(Common.retErr(e));
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
Common.printOut(conf.PREFIX_MSG + 'Starting %s in %s (%d instance' + (resolved_paths.instances > 1 ? 's' : '') + ')',
|
|
866
|
+
resolved_paths.pm_exec_path, resolved_paths.exec_mode, resolved_paths.instances);
|
|
867
|
+
|
|
868
|
+
if (!resolved_paths.env) resolved_paths.env = {};
|
|
869
|
+
|
|
870
|
+
// Set ZM2 HOME in case of child process using ZM2 API
|
|
871
|
+
resolved_paths.env['ZM2_HOME'] = that.pm2_home;
|
|
872
|
+
resolved_paths.env['PM2_HOME'] = that.pm2_home;
|
|
873
|
+
|
|
874
|
+
var additional_env = Modularizer.getAdditionalConf(resolved_paths.name);
|
|
875
|
+
Object.assign(resolved_paths.env, additional_env);
|
|
876
|
+
|
|
877
|
+
// Is KM linked?
|
|
878
|
+
resolved_paths.km_link = that.gl_is_km_linked;
|
|
879
|
+
|
|
880
|
+
that.Client.executeRemote('prepare', resolved_paths, function(err, data) {
|
|
881
|
+
if (err) {
|
|
882
|
+
Common.printError(conf.PREFIX_MSG_ERR + 'Error while launching application', err.stack || err);
|
|
883
|
+
return cb(Common.retErr(err));
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
Common.printOut(conf.PREFIX_MSG + 'Done.');
|
|
887
|
+
return cb(true, data);
|
|
888
|
+
});
|
|
889
|
+
return false;
|
|
890
|
+
});
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
/**
|
|
895
|
+
* Method to start/restart/reload processes from a JSON file
|
|
896
|
+
* It will start app not started
|
|
897
|
+
* Can receive only option to skip applications
|
|
898
|
+
*
|
|
899
|
+
* @private
|
|
900
|
+
*/
|
|
901
|
+
_startJson (file, opts, action, pipe, cb) {
|
|
902
|
+
var config = {};
|
|
903
|
+
var appConf = {};
|
|
904
|
+
var staticConf = [];
|
|
905
|
+
var deployConf = {};
|
|
906
|
+
var apps_info = [];
|
|
907
|
+
var that = this;
|
|
908
|
+
|
|
909
|
+
/**
|
|
910
|
+
* Get File configuration
|
|
911
|
+
*/
|
|
912
|
+
if (typeof(cb) === 'undefined' && typeof(pipe) === 'function') {
|
|
913
|
+
cb = pipe;
|
|
914
|
+
}
|
|
915
|
+
if (typeof(file) === 'object') {
|
|
916
|
+
config = file;
|
|
917
|
+
} else if (pipe === 'pipe') {
|
|
918
|
+
config = Common.parseConfig(file, 'pipe');
|
|
919
|
+
} else {
|
|
920
|
+
var data = null;
|
|
921
|
+
|
|
922
|
+
var isAbsolute = path.isAbsolute(file)
|
|
923
|
+
var file_path = isAbsolute ? file : path.join(that.cwd, file);
|
|
924
|
+
|
|
925
|
+
debug('Resolved filepath %s', file_path);
|
|
926
|
+
|
|
927
|
+
try {
|
|
928
|
+
data = fs.readFileSync(file_path);
|
|
929
|
+
} catch(e) {
|
|
930
|
+
Common.printError(conf.PREFIX_MSG_ERR + 'File ' + file +' not found');
|
|
931
|
+
return cb ? cb(Common.retErr(e)) : that.exitCli(conf.ERROR_EXIT);
|
|
932
|
+
}
|
|
933
|
+
|
|
934
|
+
try {
|
|
935
|
+
config = Common.parseConfig(data, file);
|
|
936
|
+
} catch(e) {
|
|
937
|
+
Common.printError(conf.PREFIX_MSG_ERR + 'File ' + file + ' malformated');
|
|
938
|
+
console.error(e);
|
|
939
|
+
return cb ? cb(Common.retErr(e)) : that.exitCli(conf.ERROR_EXIT);
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
/**
|
|
944
|
+
* Alias some optional fields
|
|
945
|
+
*/
|
|
946
|
+
if (config.deploy)
|
|
947
|
+
deployConf = config.deploy;
|
|
948
|
+
if (config.static)
|
|
949
|
+
staticConf = config.static;
|
|
950
|
+
if (config.apps)
|
|
951
|
+
appConf = config.apps;
|
|
952
|
+
else if (config.pm2)
|
|
953
|
+
appConf = config.pm2;
|
|
954
|
+
else
|
|
955
|
+
appConf = config;
|
|
956
|
+
if (!Array.isArray(appConf))
|
|
957
|
+
appConf = [appConf];
|
|
958
|
+
|
|
959
|
+
if ((appConf = Common.verifyConfs(appConf)) instanceof Error)
|
|
960
|
+
return cb ? cb(appConf) : that.exitCli(conf.ERROR_EXIT);
|
|
961
|
+
|
|
962
|
+
process.env.ZM2_JSON_PROCESSING = true;
|
|
963
|
+
|
|
964
|
+
// Get App list
|
|
965
|
+
var apps_name = [];
|
|
966
|
+
var proc_list = {};
|
|
967
|
+
|
|
968
|
+
// Add statics to apps
|
|
969
|
+
staticConf.forEach(function(serve) {
|
|
970
|
+
appConf.push({
|
|
971
|
+
name: serve.name ? serve.name : `static-page-server-${serve.port}`,
|
|
972
|
+
script: path.resolve(__dirname, 'API', 'Serve.js'),
|
|
973
|
+
env: {
|
|
974
|
+
ZM2_SERVE_PORT: serve.port,
|
|
975
|
+
ZM2_SERVE_HOST: serve.host,
|
|
976
|
+
ZM2_SERVE_PATH: serve.path,
|
|
977
|
+
ZM2_SERVE_SPA: serve.spa,
|
|
978
|
+
ZM2_SERVE_DIRECTORY: serve.directory,
|
|
979
|
+
ZM2_SERVE_BASIC_AUTH: serve.basic_auth !== undefined,
|
|
980
|
+
ZM2_SERVE_BASIC_AUTH_USERNAME: serve.basic_auth ? serve.basic_auth.username : null,
|
|
981
|
+
ZM2_SERVE_BASIC_AUTH_PASSWORD: serve.basic_auth ? serve.basic_auth.password : null,
|
|
982
|
+
ZM2_SERVE_MONITOR: serve.monitor
|
|
983
|
+
}
|
|
984
|
+
});
|
|
985
|
+
});
|
|
986
|
+
|
|
987
|
+
// Here we pick only the field we want from the CLI when starting a JSON
|
|
988
|
+
appConf.forEach(function(app) {
|
|
989
|
+
if (!app.env) { app.env = {}; }
|
|
990
|
+
app.env.io = app.io;
|
|
991
|
+
// --only <app>
|
|
992
|
+
if (opts.only) {
|
|
993
|
+
var apps = opts.only.split(/,| /)
|
|
994
|
+
if (apps.indexOf(app.name) == -1)
|
|
995
|
+
return false
|
|
996
|
+
}
|
|
997
|
+
// Namespace
|
|
998
|
+
if (!app.namespace) {
|
|
999
|
+
if (opts.namespace)
|
|
1000
|
+
app.namespace = opts.namespace;
|
|
1001
|
+
else
|
|
1002
|
+
app.namespace = 'default';
|
|
1003
|
+
}
|
|
1004
|
+
// --watch
|
|
1005
|
+
if (!app.watch && opts.watch && opts.watch === true)
|
|
1006
|
+
app.watch = true;
|
|
1007
|
+
// --ignore-watch
|
|
1008
|
+
if (!app.ignore_watch && opts.ignore_watch)
|
|
1009
|
+
app.ignore_watch = opts.ignore_watch;
|
|
1010
|
+
if (opts.install_url)
|
|
1011
|
+
app.install_url = opts.install_url;
|
|
1012
|
+
// --instances <nb>
|
|
1013
|
+
if (opts.instances && typeof(opts.instances) === 'number')
|
|
1014
|
+
app.instances = opts.instances;
|
|
1015
|
+
// --uid <user>
|
|
1016
|
+
if (opts.uid)
|
|
1017
|
+
app.uid = opts.uid;
|
|
1018
|
+
// --gid <user>
|
|
1019
|
+
if (opts.gid)
|
|
1020
|
+
app.gid = opts.gid;
|
|
1021
|
+
// Specific
|
|
1022
|
+
if (app.append_env_to_name && opts.env)
|
|
1023
|
+
app.name += ('-' + opts.env);
|
|
1024
|
+
if (opts.name_prefix && app.name.indexOf(opts.name_prefix) == -1)
|
|
1025
|
+
app.name = `${opts.name_prefix}:${app.name}`
|
|
1026
|
+
|
|
1027
|
+
app.username = Common.getCurrentUsername();
|
|
1028
|
+
apps_name.push(app.name);
|
|
1029
|
+
});
|
|
1030
|
+
|
|
1031
|
+
that.Client.executeRemote('getMonitorData', {}, function(err, raw_proc_list) {
|
|
1032
|
+
if (err) {
|
|
1033
|
+
Common.printError(err);
|
|
1034
|
+
return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT);
|
|
1035
|
+
}
|
|
1036
|
+
|
|
1037
|
+
/**
|
|
1038
|
+
* Uniquify in memory process list
|
|
1039
|
+
*/
|
|
1040
|
+
raw_proc_list.forEach(function(proc) {
|
|
1041
|
+
proc_list[proc.name] = proc;
|
|
1042
|
+
});
|
|
1043
|
+
|
|
1044
|
+
/**
|
|
1045
|
+
* Auto detect application already started
|
|
1046
|
+
* and act on them depending on action
|
|
1047
|
+
*/
|
|
1048
|
+
eachLimit(Object.keys(proc_list), conf.CONCURRENT_ACTIONS, function(proc_name, next) {
|
|
1049
|
+
// Skip app name (--only option)
|
|
1050
|
+
if (apps_name.indexOf(proc_name) == -1)
|
|
1051
|
+
return next();
|
|
1052
|
+
|
|
1053
|
+
if (!(action == 'reloadProcessId' ||
|
|
1054
|
+
action == 'softReloadProcessId' ||
|
|
1055
|
+
action == 'restartProcessId'))
|
|
1056
|
+
throw new Error('Wrong action called');
|
|
1057
|
+
|
|
1058
|
+
var apps = appConf.filter(function(app) {
|
|
1059
|
+
return app.name == proc_name;
|
|
1060
|
+
});
|
|
1061
|
+
|
|
1062
|
+
var envs = apps.map(function(app){
|
|
1063
|
+
// Binds env_diff to env and returns it.
|
|
1064
|
+
return Common.mergeEnvironmentVariables(app, opts.env, deployConf);
|
|
1065
|
+
});
|
|
1066
|
+
|
|
1067
|
+
// Assigns own enumerable properties of all
|
|
1068
|
+
// Notice: if people use the same name in different apps,
|
|
1069
|
+
// duplicated envs will be overrode by the last one
|
|
1070
|
+
var env = envs.reduce(function(e1, e2){
|
|
1071
|
+
return Object.assign(e1, e2);
|
|
1072
|
+
});
|
|
1073
|
+
|
|
1074
|
+
// When we are processing JSON, allow to keep the new env by default
|
|
1075
|
+
env.updateEnv = true;
|
|
1076
|
+
|
|
1077
|
+
// Pass `env` option
|
|
1078
|
+
that._operate(action, proc_name, env, function(err, ret) {
|
|
1079
|
+
if (err) Common.printError(err);
|
|
1080
|
+
|
|
1081
|
+
// For return
|
|
1082
|
+
apps_info = apps_info.concat(ret);
|
|
1083
|
+
|
|
1084
|
+
that.Client.notifyGod(action, proc_name);
|
|
1085
|
+
// And Remove from array to spy
|
|
1086
|
+
apps_name.splice(apps_name.indexOf(proc_name), 1);
|
|
1087
|
+
return next();
|
|
1088
|
+
});
|
|
1089
|
+
|
|
1090
|
+
}, function(err) {
|
|
1091
|
+
if (err) return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT);
|
|
1092
|
+
if (apps_name.length > 0 && action != 'start')
|
|
1093
|
+
Common.printOut(conf.PREFIX_MSG_WARNING + 'Applications %s not running, starting...', apps_name.join(', '));
|
|
1094
|
+
// Start missing apps
|
|
1095
|
+
return startApps(apps_name, function(err, apps) {
|
|
1096
|
+
apps_info = apps_info.concat(apps);
|
|
1097
|
+
return cb ? cb(err, apps_info) : that.speedList(err ? 1 : 0);
|
|
1098
|
+
});
|
|
1099
|
+
});
|
|
1100
|
+
return false;
|
|
1101
|
+
});
|
|
1102
|
+
|
|
1103
|
+
function startApps(app_name_to_start, cb) {
|
|
1104
|
+
var apps_to_start = [];
|
|
1105
|
+
var apps_started = [];
|
|
1106
|
+
var apps_errored = [];
|
|
1107
|
+
|
|
1108
|
+
appConf.forEach(function(app, i) {
|
|
1109
|
+
if (app_name_to_start.indexOf(app.name) != -1) {
|
|
1110
|
+
apps_to_start.push(appConf[i]);
|
|
1111
|
+
}
|
|
1112
|
+
});
|
|
1113
|
+
|
|
1114
|
+
eachLimit(apps_to_start, conf.CONCURRENT_ACTIONS, function(app, next) {
|
|
1115
|
+
if (opts.cwd)
|
|
1116
|
+
app.cwd = opts.cwd;
|
|
1117
|
+
if (opts.force_name)
|
|
1118
|
+
app.name = opts.force_name;
|
|
1119
|
+
if (opts.started_as_module)
|
|
1120
|
+
app.pmx_module = true;
|
|
1121
|
+
|
|
1122
|
+
var resolved_paths = null;
|
|
1123
|
+
|
|
1124
|
+
// hardcode script name to use `serve` feature inside a process file
|
|
1125
|
+
if (app.script === 'serve') {
|
|
1126
|
+
app.script = path.resolve(__dirname, 'API', 'Serve.js')
|
|
1127
|
+
}
|
|
1128
|
+
|
|
1129
|
+
try {
|
|
1130
|
+
resolved_paths = Common.resolveAppAttributes({
|
|
1131
|
+
cwd : that.cwd,
|
|
1132
|
+
pm2_home : that.pm2_home
|
|
1133
|
+
}, app);
|
|
1134
|
+
} catch (e) {
|
|
1135
|
+
apps_errored.push(e)
|
|
1136
|
+
Common.err(`Error: ${e.message}`)
|
|
1137
|
+
return next();
|
|
1138
|
+
}
|
|
1139
|
+
|
|
1140
|
+
if (!resolved_paths.env) resolved_paths.env = {};
|
|
1141
|
+
|
|
1142
|
+
// Set ZM2 HOME in case of child process using ZM2 API
|
|
1143
|
+
resolved_paths.env['ZM2_HOME'] = that.pm2_home;
|
|
1144
|
+
resolved_paths.env['PM2_HOME'] = that.pm2_home;
|
|
1145
|
+
|
|
1146
|
+
var additional_env = Modularizer.getAdditionalConf(resolved_paths.name);
|
|
1147
|
+
Object.assign(resolved_paths.env, additional_env);
|
|
1148
|
+
|
|
1149
|
+
resolved_paths.env = Common.mergeEnvironmentVariables(resolved_paths, opts.env, deployConf);
|
|
1150
|
+
|
|
1151
|
+
delete resolved_paths.env.current_conf;
|
|
1152
|
+
|
|
1153
|
+
// Is KM linked?
|
|
1154
|
+
resolved_paths.km_link = that.gl_is_km_linked;
|
|
1155
|
+
|
|
1156
|
+
if (resolved_paths.wait_ready) {
|
|
1157
|
+
Common.warn(`App ${resolved_paths.name} has option 'wait_ready' set, waiting for app to be ready...`)
|
|
1158
|
+
}
|
|
1159
|
+
that.Client.executeRemote('prepare', resolved_paths, function(err, data) {
|
|
1160
|
+
if (err) {
|
|
1161
|
+
Common.printError(conf.PREFIX_MSG_ERR + 'Process failed to launch %s', err.message ? err.message : err);
|
|
1162
|
+
return next();
|
|
1163
|
+
}
|
|
1164
|
+
if (data.length === 0) {
|
|
1165
|
+
Common.printError(conf.PREFIX_MSG_ERR + 'Process config loading failed', data);
|
|
1166
|
+
return next();
|
|
1167
|
+
}
|
|
1168
|
+
|
|
1169
|
+
Common.printOut(conf.PREFIX_MSG + 'App [%s] launched (%d instances)', data[0].pm2_env.name, data.length);
|
|
1170
|
+
apps_started = apps_started.concat(data);
|
|
1171
|
+
next();
|
|
1172
|
+
});
|
|
1173
|
+
|
|
1174
|
+
}, function(err) {
|
|
1175
|
+
var final_error = err || apps_errored.length > 0 ? apps_errored : null
|
|
1176
|
+
return cb ? cb(final_error, apps_started) : that.speedList();
|
|
1177
|
+
});
|
|
1178
|
+
return false;
|
|
1179
|
+
}
|
|
1180
|
+
}
|
|
1181
|
+
|
|
1182
|
+
/**
|
|
1183
|
+
* Apply a RPC method on the json file
|
|
1184
|
+
* @private
|
|
1185
|
+
* @method actionFromJson
|
|
1186
|
+
* @param {string} action RPC Method
|
|
1187
|
+
* @param {object} options
|
|
1188
|
+
* @param {string|object} file file
|
|
1189
|
+
* @param {string} jsonVia action type (=only 'pipe' ?)
|
|
1190
|
+
* @param {Function}
|
|
1191
|
+
*/
|
|
1192
|
+
actionFromJson (action, file, opts, jsonVia, cb) {
|
|
1193
|
+
var appConf = {};
|
|
1194
|
+
var ret_processes = [];
|
|
1195
|
+
var that = this;
|
|
1196
|
+
|
|
1197
|
+
//accept programmatic calls
|
|
1198
|
+
if (typeof file == 'object') {
|
|
1199
|
+
cb = typeof jsonVia == 'function' ? jsonVia : cb;
|
|
1200
|
+
appConf = file;
|
|
1201
|
+
}
|
|
1202
|
+
else if (jsonVia == 'file') {
|
|
1203
|
+
var data = null;
|
|
1204
|
+
|
|
1205
|
+
try {
|
|
1206
|
+
data = fs.readFileSync(file);
|
|
1207
|
+
} catch(e) {
|
|
1208
|
+
Common.printError(conf.PREFIX_MSG_ERR + 'File ' + file +' not found');
|
|
1209
|
+
return cb ? cb(Common.retErr(e)) : that.exitCli(conf.ERROR_EXIT);
|
|
1210
|
+
}
|
|
1211
|
+
|
|
1212
|
+
try {
|
|
1213
|
+
appConf = Common.parseConfig(data, file);
|
|
1214
|
+
} catch(e) {
|
|
1215
|
+
Common.printError(conf.PREFIX_MSG_ERR + 'File ' + file + ' malformated');
|
|
1216
|
+
console.error(e);
|
|
1217
|
+
return cb ? cb(Common.retErr(e)) : that.exitCli(conf.ERROR_EXIT);
|
|
1218
|
+
}
|
|
1219
|
+
} else if (jsonVia == 'pipe') {
|
|
1220
|
+
appConf = Common.parseConfig(file, 'pipe');
|
|
1221
|
+
} else {
|
|
1222
|
+
Common.printError('Bad call to actionFromJson, jsonVia should be one of file, pipe');
|
|
1223
|
+
return that.exitCli(conf.ERROR_EXIT);
|
|
1224
|
+
}
|
|
1225
|
+
|
|
1226
|
+
// Backward compatibility
|
|
1227
|
+
if (appConf.apps)
|
|
1228
|
+
appConf = appConf.apps;
|
|
1229
|
+
|
|
1230
|
+
if (!Array.isArray(appConf))
|
|
1231
|
+
appConf = [appConf];
|
|
1232
|
+
|
|
1233
|
+
if ((appConf = Common.verifyConfs(appConf)) instanceof Error)
|
|
1234
|
+
return cb ? cb(appConf) : that.exitCli(conf.ERROR_EXIT);
|
|
1235
|
+
|
|
1236
|
+
eachLimit(appConf, conf.CONCURRENT_ACTIONS, function(proc, next1) {
|
|
1237
|
+
var name = '';
|
|
1238
|
+
var new_env;
|
|
1239
|
+
|
|
1240
|
+
if (!proc.name)
|
|
1241
|
+
name = path.basename(proc.script);
|
|
1242
|
+
else
|
|
1243
|
+
name = proc.name;
|
|
1244
|
+
|
|
1245
|
+
if (opts.only && opts.only != name)
|
|
1246
|
+
return process.nextTick(next1);
|
|
1247
|
+
|
|
1248
|
+
if (opts && opts.env)
|
|
1249
|
+
new_env = Common.mergeEnvironmentVariables(proc, opts.env);
|
|
1250
|
+
else
|
|
1251
|
+
new_env = Common.mergeEnvironmentVariables(proc);
|
|
1252
|
+
|
|
1253
|
+
that.Client.getProcessIdByName(name, function(err, ids) {
|
|
1254
|
+
if (err) {
|
|
1255
|
+
Common.printError(err);
|
|
1256
|
+
return next1();
|
|
1257
|
+
}
|
|
1258
|
+
if (!ids) return next1();
|
|
1259
|
+
|
|
1260
|
+
eachLimit(ids, conf.CONCURRENT_ACTIONS, function(id, next2) {
|
|
1261
|
+
var opts = {};
|
|
1262
|
+
|
|
1263
|
+
//stopProcessId could accept options to?
|
|
1264
|
+
if (action == 'restartProcessId') {
|
|
1265
|
+
opts = {id : id, env : new_env};
|
|
1266
|
+
} else {
|
|
1267
|
+
opts = id;
|
|
1268
|
+
}
|
|
1269
|
+
|
|
1270
|
+
that.Client.executeRemote(action, opts, function(err, res) {
|
|
1271
|
+
ret_processes.push(res);
|
|
1272
|
+
if (err) {
|
|
1273
|
+
Common.printError(err);
|
|
1274
|
+
return next2();
|
|
1275
|
+
}
|
|
1276
|
+
|
|
1277
|
+
if (action == 'restartProcessId') {
|
|
1278
|
+
that.Client.notifyGod('restart', id);
|
|
1279
|
+
} else if (action == 'deleteProcessId') {
|
|
1280
|
+
that.Client.notifyGod('delete', id);
|
|
1281
|
+
} else if (action == 'stopProcessId') {
|
|
1282
|
+
that.Client.notifyGod('stop', id);
|
|
1283
|
+
}
|
|
1284
|
+
|
|
1285
|
+
Common.printOut(conf.PREFIX_MSG + '[%s](%d) \u2713', name, id);
|
|
1286
|
+
return next2();
|
|
1287
|
+
});
|
|
1288
|
+
}, function(err) {
|
|
1289
|
+
return next1(null, ret_processes);
|
|
1290
|
+
});
|
|
1291
|
+
});
|
|
1292
|
+
}, function(err) {
|
|
1293
|
+
if (cb) return cb(null, ret_processes);
|
|
1294
|
+
else return that.speedList();
|
|
1295
|
+
});
|
|
1296
|
+
}
|
|
1297
|
+
|
|
1298
|
+
|
|
1299
|
+
/**
|
|
1300
|
+
* Main function to operate with ZM2 daemon
|
|
1301
|
+
*
|
|
1302
|
+
* @param {String} action_name Name of action (restartProcessId, deleteProcessId, stopProcessId)
|
|
1303
|
+
* @param {String} process_name can be 'all', a id integer or process name
|
|
1304
|
+
* @param {Object} envs object with CLI options / environment
|
|
1305
|
+
*/
|
|
1306
|
+
_operate (action_name, process_name, envs, cb) {
|
|
1307
|
+
var that = this;
|
|
1308
|
+
var update_env = false;
|
|
1309
|
+
var ret = [];
|
|
1310
|
+
|
|
1311
|
+
// Make sure all options exist
|
|
1312
|
+
if (!envs)
|
|
1313
|
+
envs = {};
|
|
1314
|
+
|
|
1315
|
+
if (typeof(envs) == 'function'){
|
|
1316
|
+
cb = envs;
|
|
1317
|
+
envs = {};
|
|
1318
|
+
}
|
|
1319
|
+
|
|
1320
|
+
// Set via env.update (JSON processing)
|
|
1321
|
+
if (envs.updateEnv === true)
|
|
1322
|
+
update_env = true;
|
|
1323
|
+
|
|
1324
|
+
var concurrent_actions = envs.parallel || conf.CONCURRENT_ACTIONS;
|
|
1325
|
+
|
|
1326
|
+
if (!(process.env.ZM2_JSON_PROCESSING || process.env.PM2_JSON_PROCESSING) || envs.commands) {
|
|
1327
|
+
envs = that._handleAttributeUpdate(envs);
|
|
1328
|
+
}
|
|
1329
|
+
|
|
1330
|
+
/**
|
|
1331
|
+
* Set current updated configuration if not passed
|
|
1332
|
+
*/
|
|
1333
|
+
if (!envs.current_conf) {
|
|
1334
|
+
var _conf = fclone(envs);
|
|
1335
|
+
envs = {
|
|
1336
|
+
current_conf : _conf
|
|
1337
|
+
}
|
|
1338
|
+
|
|
1339
|
+
// Is KM linked?
|
|
1340
|
+
envs.current_conf.km_link = that.gl_is_km_linked;
|
|
1341
|
+
}
|
|
1342
|
+
|
|
1343
|
+
/**
|
|
1344
|
+
* Operate action on specific process id
|
|
1345
|
+
*/
|
|
1346
|
+
function processIds(ids, cb) {
|
|
1347
|
+
Common.printOut(conf.PREFIX_MSG + 'Applying action %s on app [%s](ids: %s)', action_name, process_name, ids);
|
|
1348
|
+
|
|
1349
|
+
if (ids.length <= 2)
|
|
1350
|
+
concurrent_actions = 1;
|
|
1351
|
+
|
|
1352
|
+
if (action_name == 'deleteProcessId')
|
|
1353
|
+
concurrent_actions = 10;
|
|
1354
|
+
|
|
1355
|
+
eachLimit(ids, concurrent_actions, function(id, next) {
|
|
1356
|
+
var opts;
|
|
1357
|
+
|
|
1358
|
+
// These functions need extra param to be passed
|
|
1359
|
+
if (action_name == 'restartProcessId' ||
|
|
1360
|
+
action_name == 'reloadProcessId' ||
|
|
1361
|
+
action_name == 'softReloadProcessId') {
|
|
1362
|
+
var new_env = {};
|
|
1363
|
+
|
|
1364
|
+
if (update_env === true) {
|
|
1365
|
+
if (conf.PM2_PROGRAMMATIC == true)
|
|
1366
|
+
new_env = Common.safeExtend({}, process.env);
|
|
1367
|
+
else
|
|
1368
|
+
new_env = Object.assign({}, process.env);
|
|
1369
|
+
|
|
1370
|
+
Object.keys(envs).forEach(function(k) {
|
|
1371
|
+
new_env[k] = envs[k];
|
|
1372
|
+
});
|
|
1373
|
+
}
|
|
1374
|
+
else {
|
|
1375
|
+
new_env = envs;
|
|
1376
|
+
}
|
|
1377
|
+
|
|
1378
|
+
opts = {
|
|
1379
|
+
id : id,
|
|
1380
|
+
env : new_env
|
|
1381
|
+
};
|
|
1382
|
+
}
|
|
1383
|
+
else {
|
|
1384
|
+
opts = id;
|
|
1385
|
+
}
|
|
1386
|
+
|
|
1387
|
+
that.Client.executeRemote(action_name, opts, function(err, res) {
|
|
1388
|
+
if (err) {
|
|
1389
|
+
Common.printError(conf.PREFIX_MSG_ERR + 'Process %s not found', id);
|
|
1390
|
+
return next(`Process ${id} not found`);
|
|
1391
|
+
}
|
|
1392
|
+
|
|
1393
|
+
if (action_name == 'restartProcessId') {
|
|
1394
|
+
that.Client.notifyGod('restart', id);
|
|
1395
|
+
} else if (action_name == 'deleteProcessId') {
|
|
1396
|
+
that.Client.notifyGod('delete', id);
|
|
1397
|
+
} else if (action_name == 'stopProcessId') {
|
|
1398
|
+
that.Client.notifyGod('stop', id);
|
|
1399
|
+
} else if (action_name == 'reloadProcessId') {
|
|
1400
|
+
that.Client.notifyGod('reload', id);
|
|
1401
|
+
} else if (action_name == 'softReloadProcessId') {
|
|
1402
|
+
that.Client.notifyGod('graceful reload', id);
|
|
1403
|
+
}
|
|
1404
|
+
|
|
1405
|
+
if (!Array.isArray(res))
|
|
1406
|
+
res = [res];
|
|
1407
|
+
|
|
1408
|
+
// Filter return
|
|
1409
|
+
res.forEach(function(proc) {
|
|
1410
|
+
Common.printOut(conf.PREFIX_MSG + '[%s](%d) \u2713', proc.pm2_env ? proc.pm2_env.name : process_name, id);
|
|
1411
|
+
|
|
1412
|
+
if (action_name == 'stopProcessId' && proc.pm2_env && proc.pm2_env.cron_restart) {
|
|
1413
|
+
Common.warn(`App ${chalk.bold(proc.pm2_env.name)} stopped but CRON RESTART is still UP ${proc.pm2_env.cron_restart}`)
|
|
1414
|
+
}
|
|
1415
|
+
|
|
1416
|
+
if (!proc.pm2_env) return false;
|
|
1417
|
+
|
|
1418
|
+
ret.push({
|
|
1419
|
+
name : proc.pm2_env.name,
|
|
1420
|
+
namespace: proc.pm2_env.namespace,
|
|
1421
|
+
pm_id : proc.pm2_env.pm_id,
|
|
1422
|
+
status : proc.pm2_env.status,
|
|
1423
|
+
restart_time : proc.pm2_env.restart_time,
|
|
1424
|
+
pm2_env : {
|
|
1425
|
+
name : proc.pm2_env.name,
|
|
1426
|
+
namespace: proc.pm2_env.namespace,
|
|
1427
|
+
pm_id : proc.pm2_env.pm_id,
|
|
1428
|
+
status : proc.pm2_env.status,
|
|
1429
|
+
restart_time : proc.pm2_env.restart_time,
|
|
1430
|
+
env : proc.pm2_env.env
|
|
1431
|
+
}
|
|
1432
|
+
});
|
|
1433
|
+
});
|
|
1434
|
+
|
|
1435
|
+
return next();
|
|
1436
|
+
});
|
|
1437
|
+
}, function(err) {
|
|
1438
|
+
if (err) return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT);
|
|
1439
|
+
return cb ? cb(null, ret) : that.speedList();
|
|
1440
|
+
});
|
|
1441
|
+
}
|
|
1442
|
+
|
|
1443
|
+
if (process_name == 'all') {
|
|
1444
|
+
// When using shortcuts like 'all', do not delete modules
|
|
1445
|
+
var fn
|
|
1446
|
+
|
|
1447
|
+
if (process.env.ZM2_STATUS == 'stopping' || process.env.PM2_STATUS == 'stopping')
|
|
1448
|
+
that.Client.getAllProcessId(function(err, ids) {
|
|
1449
|
+
reoperate(err, ids)
|
|
1450
|
+
});
|
|
1451
|
+
else
|
|
1452
|
+
that.Client.getAllProcessIdWithoutModules(function(err, ids) {
|
|
1453
|
+
reoperate(err, ids)
|
|
1454
|
+
});
|
|
1455
|
+
|
|
1456
|
+
function reoperate(err, ids) {
|
|
1457
|
+
if (err) {
|
|
1458
|
+
Common.printError(err);
|
|
1459
|
+
return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT);
|
|
1460
|
+
}
|
|
1461
|
+
if (!ids || ids.length === 0) {
|
|
1462
|
+
Common.printError(conf.PREFIX_MSG_WARNING + 'No process found');
|
|
1463
|
+
return cb ? cb(new Error('process name not found')) : that.exitCli(conf.ERROR_EXIT);
|
|
1464
|
+
}
|
|
1465
|
+
return processIds(ids, cb);
|
|
1466
|
+
}
|
|
1467
|
+
}
|
|
1468
|
+
// operate using regex
|
|
1469
|
+
else if (isNaN(process_name) && process_name[0] === '/' && process_name[process_name.length - 1] === '/') {
|
|
1470
|
+
var regex = new RegExp(process_name.replace(/\//g, ''));
|
|
1471
|
+
|
|
1472
|
+
that.Client.executeRemote('getMonitorData', {}, function(err, list) {
|
|
1473
|
+
if (err) {
|
|
1474
|
+
Common.printError('Error retrieving process list: ' + err);
|
|
1475
|
+
return cb(err);
|
|
1476
|
+
}
|
|
1477
|
+
var found_proc = [];
|
|
1478
|
+
list.forEach(function(proc) {
|
|
1479
|
+
if (regex.test(proc.pm2_env.name)) {
|
|
1480
|
+
found_proc.push(proc.pm_id);
|
|
1481
|
+
}
|
|
1482
|
+
});
|
|
1483
|
+
|
|
1484
|
+
if (found_proc.length === 0) {
|
|
1485
|
+
Common.printError(conf.PREFIX_MSG_WARNING + 'No process found');
|
|
1486
|
+
return cb ? cb(new Error('process name not found')) : that.exitCli(conf.ERROR_EXIT);
|
|
1487
|
+
}
|
|
1488
|
+
|
|
1489
|
+
return processIds(found_proc, cb);
|
|
1490
|
+
});
|
|
1491
|
+
}
|
|
1492
|
+
else if (isNaN(process_name)) {
|
|
1493
|
+
/**
|
|
1494
|
+
* We can not stop or delete a module but we can restart it
|
|
1495
|
+
* to refresh configuration variable
|
|
1496
|
+
*/
|
|
1497
|
+
var allow_module_restart = action_name == 'restartProcessId' ? true : false;
|
|
1498
|
+
|
|
1499
|
+
that.Client.getProcessIdByName(process_name, allow_module_restart, function (err, ids) {
|
|
1500
|
+
if (err) {
|
|
1501
|
+
Common.printError(err);
|
|
1502
|
+
return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT);
|
|
1503
|
+
}
|
|
1504
|
+
if (ids && ids.length > 0) {
|
|
1505
|
+
/**
|
|
1506
|
+
* Determine if the process to restart is a module
|
|
1507
|
+
* if yes load configuration variables and merge with the current environment
|
|
1508
|
+
*/
|
|
1509
|
+
var additional_env = Modularizer.getAdditionalConf(process_name);
|
|
1510
|
+
Object.assign(envs, additional_env);
|
|
1511
|
+
return processIds(ids, cb);
|
|
1512
|
+
}
|
|
1513
|
+
|
|
1514
|
+
that.Client.getProcessIdsByNamespace(process_name, allow_module_restart, function (err, ns_process_ids) {
|
|
1515
|
+
if (err) {
|
|
1516
|
+
Common.printError(err);
|
|
1517
|
+
return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT);
|
|
1518
|
+
}
|
|
1519
|
+
if (!ns_process_ids || ns_process_ids.length === 0) {
|
|
1520
|
+
Common.printError(conf.PREFIX_MSG_ERR + 'Process or Namespace %s not found', process_name);
|
|
1521
|
+
return cb ? cb(new Error('process or namespace not found')) : that.exitCli(conf.ERROR_EXIT);
|
|
1522
|
+
}
|
|
1523
|
+
|
|
1524
|
+
/**
|
|
1525
|
+
* Determine if the process to restart is a module
|
|
1526
|
+
* if yes load configuration variables and merge with the current environment
|
|
1527
|
+
*/
|
|
1528
|
+
var ns_additional_env = Modularizer.getAdditionalConf(process_name);
|
|
1529
|
+
Object.assign(envs, ns_additional_env);
|
|
1530
|
+
return processIds(ns_process_ids, cb);
|
|
1531
|
+
});
|
|
1532
|
+
});
|
|
1533
|
+
} else {
|
|
1534
|
+
if (that.pm2_configuration.docker == "true" ||
|
|
1535
|
+
that.pm2_configuration.docker == true) {
|
|
1536
|
+
// Docker/Systemd process interaction detection
|
|
1537
|
+
that.Client.executeRemote('getMonitorData', {}, (err, proc_list) => {
|
|
1538
|
+
var higher_id = 0
|
|
1539
|
+
proc_list.forEach(p => { p.pm_id > higher_id ? higher_id = p.pm_id : null })
|
|
1540
|
+
|
|
1541
|
+
// Is Docker/Systemd
|
|
1542
|
+
if (process_name > higher_id)
|
|
1543
|
+
return DockerMgmt.processCommand(that, higher_id, process_name, action_name, (err) => {
|
|
1544
|
+
if (err) {
|
|
1545
|
+
Common.printError(conf.PREFIX_MSG_ERR + (err.message ? err.message : err));
|
|
1546
|
+
return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT);
|
|
1547
|
+
}
|
|
1548
|
+
|
|
1549
|
+
return cb ? cb(null, ret) : that.speedList();
|
|
1550
|
+
})
|
|
1551
|
+
|
|
1552
|
+
// Check if application name as number is an app name
|
|
1553
|
+
that.Client.getProcessIdByName(process_name, function(err, ids) {
|
|
1554
|
+
if (ids.length > 0)
|
|
1555
|
+
return processIds(ids, cb);
|
|
1556
|
+
|
|
1557
|
+
// Check if application name as number is an namespace
|
|
1558
|
+
that.Client.getProcessIdsByNamespace(process_name, function(err, ns_process_ids) {
|
|
1559
|
+
if (ns_process_ids.length > 0)
|
|
1560
|
+
return processIds(ns_process_ids, cb);
|
|
1561
|
+
// Else operate on pm id
|
|
1562
|
+
return processIds([process_name], cb);
|
|
1563
|
+
});
|
|
1564
|
+
});
|
|
1565
|
+
})
|
|
1566
|
+
}
|
|
1567
|
+
else {
|
|
1568
|
+
// Check if application name as number is an app name
|
|
1569
|
+
that.Client.getProcessIdByName(process_name, function(err, ids) {
|
|
1570
|
+
if (ids.length > 0)
|
|
1571
|
+
return processIds(ids, cb);
|
|
1572
|
+
|
|
1573
|
+
// Check if application name as number is an namespace
|
|
1574
|
+
that.Client.getProcessIdsByNamespace(process_name, function(err, ns_process_ids) {
|
|
1575
|
+
if (ns_process_ids.length > 0)
|
|
1576
|
+
return processIds(ns_process_ids, cb);
|
|
1577
|
+
// Else operate on pm id
|
|
1578
|
+
return processIds([process_name], cb);
|
|
1579
|
+
});
|
|
1580
|
+
});
|
|
1581
|
+
}
|
|
1582
|
+
}
|
|
1583
|
+
}
|
|
1584
|
+
|
|
1585
|
+
/**
|
|
1586
|
+
* Converts CamelCase Commander.js arguments
|
|
1587
|
+
* to Underscore
|
|
1588
|
+
* (nodeArgs -> node_args)
|
|
1589
|
+
*/
|
|
1590
|
+
_handleAttributeUpdate (opts) {
|
|
1591
|
+
var conf = Config.filterOptions(opts);
|
|
1592
|
+
var that = this;
|
|
1593
|
+
|
|
1594
|
+
if (typeof(conf.name) != 'string')
|
|
1595
|
+
delete conf.name;
|
|
1596
|
+
|
|
1597
|
+
var argsIndex = 0;
|
|
1598
|
+
if (opts.rawArgs && (argsIndex = opts.rawArgs.indexOf('--')) >= 0) {
|
|
1599
|
+
conf.args = opts.rawArgs.slice(argsIndex + 1);
|
|
1600
|
+
}
|
|
1601
|
+
|
|
1602
|
+
var appConf = Common.verifyConfs(conf)[0];
|
|
1603
|
+
|
|
1604
|
+
if (appConf instanceof Error) {
|
|
1605
|
+
Common.printError('Error while transforming CamelCase args to underscore');
|
|
1606
|
+
return appConf;
|
|
1607
|
+
}
|
|
1608
|
+
|
|
1609
|
+
if (argsIndex == -1)
|
|
1610
|
+
delete appConf.args;
|
|
1611
|
+
if (appConf.name == 'undefined')
|
|
1612
|
+
delete appConf.name;
|
|
1613
|
+
|
|
1614
|
+
delete appConf.exec_mode;
|
|
1615
|
+
|
|
1616
|
+
if (Array.isArray(appConf.watch) && appConf.watch.length === 0) {
|
|
1617
|
+
if (!~opts.rawArgs.indexOf('--watch'))
|
|
1618
|
+
delete appConf.watch
|
|
1619
|
+
}
|
|
1620
|
+
|
|
1621
|
+
// Options set via environment variables
|
|
1622
|
+
if (process.env.ZM2_DEEP_MONITORING || process.env.PM2_DEEP_MONITORING)
|
|
1623
|
+
appConf.deep_monitoring = true;
|
|
1624
|
+
|
|
1625
|
+
// Force deletion of defaults values set by commander
|
|
1626
|
+
// to avoid overriding specified configuration by user
|
|
1627
|
+
if (appConf.treekill === true)
|
|
1628
|
+
delete appConf.treekill;
|
|
1629
|
+
if (appConf.pmx === true)
|
|
1630
|
+
delete appConf.pmx;
|
|
1631
|
+
if (appConf.vizion === true)
|
|
1632
|
+
delete appConf.vizion;
|
|
1633
|
+
if (appConf.automation === true)
|
|
1634
|
+
delete appConf.automation;
|
|
1635
|
+
if (appConf.autostart === true)
|
|
1636
|
+
delete appConf.autostart;
|
|
1637
|
+
if (appConf.autorestart === true)
|
|
1638
|
+
delete appConf.autorestart;
|
|
1639
|
+
|
|
1640
|
+
return appConf;
|
|
1641
|
+
}
|
|
1642
|
+
|
|
1643
|
+
getProcessIdByName (name, cb) {
|
|
1644
|
+
var that = this;
|
|
1645
|
+
|
|
1646
|
+
this.Client.getProcessIdByName(name, function(err, id) {
|
|
1647
|
+
if (err) {
|
|
1648
|
+
Common.printError(err);
|
|
1649
|
+
return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT);
|
|
1650
|
+
}
|
|
1651
|
+
console.log(id);
|
|
1652
|
+
return cb ? cb(null, id) : that.exitCli(conf.SUCCESS_EXIT);
|
|
1653
|
+
});
|
|
1654
|
+
}
|
|
1655
|
+
|
|
1656
|
+
/**
|
|
1657
|
+
* Description
|
|
1658
|
+
* @method jlist
|
|
1659
|
+
* @param {} debug
|
|
1660
|
+
* @return
|
|
1661
|
+
*/
|
|
1662
|
+
jlist (debug) {
|
|
1663
|
+
var that = this;
|
|
1664
|
+
|
|
1665
|
+
that.Client.executeRemote('getMonitorData', {}, function(err, list) {
|
|
1666
|
+
if (err) {
|
|
1667
|
+
Common.printError(err);
|
|
1668
|
+
return that.exitCli(conf.ERROR_EXIT);
|
|
1669
|
+
}
|
|
1670
|
+
|
|
1671
|
+
if (debug) {
|
|
1672
|
+
process.stdout.write(util.inspect(list, false, null, false));
|
|
1673
|
+
}
|
|
1674
|
+
else {
|
|
1675
|
+
process.stdout.write(JSON.stringify(list));
|
|
1676
|
+
}
|
|
1677
|
+
|
|
1678
|
+
that.exitCli(conf.SUCCESS_EXIT);
|
|
1679
|
+
|
|
1680
|
+
});
|
|
1681
|
+
}
|
|
1682
|
+
|
|
1683
|
+
/**
|
|
1684
|
+
* Display system information
|
|
1685
|
+
* @method slist
|
|
1686
|
+
* @return
|
|
1687
|
+
*/
|
|
1688
|
+
slist (tree) {
|
|
1689
|
+
this.Client.executeRemote('getSystemData', {}, (err, sys_infos) => {
|
|
1690
|
+
if (err) {
|
|
1691
|
+
Common.err(err)
|
|
1692
|
+
return this.exitCli(conf.ERROR_EXIT)
|
|
1693
|
+
}
|
|
1694
|
+
|
|
1695
|
+
if (tree === true) {
|
|
1696
|
+
var treeify = require('./tools/treeify.js')
|
|
1697
|
+
console.log(treeify.asTree(sys_infos, true))
|
|
1698
|
+
}
|
|
1699
|
+
else
|
|
1700
|
+
process.stdout.write(util.inspect(sys_infos, false, null, false))
|
|
1701
|
+
this.exitCli(conf.SUCCESS_EXIT)
|
|
1702
|
+
})
|
|
1703
|
+
}
|
|
1704
|
+
|
|
1705
|
+
/**
|
|
1706
|
+
* Description
|
|
1707
|
+
* @method speedList
|
|
1708
|
+
* @return
|
|
1709
|
+
*/
|
|
1710
|
+
speedList (code, apps_acted) {
|
|
1711
|
+
var that = this;
|
|
1712
|
+
var systemdata = null
|
|
1713
|
+
var acted = []
|
|
1714
|
+
|
|
1715
|
+
if ((code != 0 && code != null)) {
|
|
1716
|
+
return that.exitCli(code ? code : conf.SUCCESS_EXIT);
|
|
1717
|
+
}
|
|
1718
|
+
|
|
1719
|
+
if (apps_acted && apps_acted.length > 0) {
|
|
1720
|
+
apps_acted.forEach(proc => {
|
|
1721
|
+
acted.push(proc.pm2_env ? proc.pm2_env.pm_id : proc.pm_id)
|
|
1722
|
+
})
|
|
1723
|
+
}
|
|
1724
|
+
|
|
1725
|
+
// Do nothing if ZM2 called programmatically and not called from CLI (also in exitCli)
|
|
1726
|
+
if ((conf.PM2_PROGRAMMATIC && (process.env.ZM2_USAGE || process.env.PM2_USAGE) != 'CLI'))
|
|
1727
|
+
return false;
|
|
1728
|
+
|
|
1729
|
+
return that.Client.executeRemote('getMonitorData', {}, (err, proc_list) => {
|
|
1730
|
+
doList(err, proc_list)
|
|
1731
|
+
})
|
|
1732
|
+
|
|
1733
|
+
function doList(err, list) {
|
|
1734
|
+
if (err) {
|
|
1735
|
+
if (that.gl_retry == 0) {
|
|
1736
|
+
that.gl_retry += 1;
|
|
1737
|
+
return setTimeout(that.speedList.bind(that), 1400);
|
|
1738
|
+
}
|
|
1739
|
+
console.error('Error retrieving process list: %s.\nA process seems to be on infinite loop, retry in 5 seconds',err);
|
|
1740
|
+
return that.exitCli(conf.ERROR_EXIT);
|
|
1741
|
+
}
|
|
1742
|
+
if (process.stdout.isTTY === false) {
|
|
1743
|
+
UX.list_min(list);
|
|
1744
|
+
}
|
|
1745
|
+
else if (commander.miniList && !commander.silent)
|
|
1746
|
+
UX.list_min(list);
|
|
1747
|
+
else if (!commander.silent) {
|
|
1748
|
+
if (that.gl_interact_infos) {
|
|
1749
|
+
var dashboard_url = `https://app.pm2.io/#/r/${that.gl_interact_infos.public_key}`
|
|
1750
|
+
|
|
1751
|
+
if (that.gl_interact_infos.info_node != 'https://root.keymetrics.io') {
|
|
1752
|
+
dashboard_url = `${that.gl_interact_infos.info_node}/#/r/${that.gl_interact_infos.public_key}`
|
|
1753
|
+
}
|
|
1754
|
+
|
|
1755
|
+
Common.printOut('%s ZM2+ activated | Instance Name: %s | Dash: %s',
|
|
1756
|
+
chalk.green.bold('⇆'),
|
|
1757
|
+
chalk.bold(that.gl_interact_infos.machine_name),
|
|
1758
|
+
chalk.bold(dashboard_url))
|
|
1759
|
+
}
|
|
1760
|
+
UX.list(list, commander);
|
|
1761
|
+
//Common.printOut(chalk.white.italic(' Use `zm2 show <id|name>` to get more details about an app'));
|
|
1762
|
+
}
|
|
1763
|
+
|
|
1764
|
+
if (that.Client.daemon_mode == false) {
|
|
1765
|
+
Common.printOut('[--no-daemon] Continue to stream logs');
|
|
1766
|
+
Common.printOut('[--no-daemon] Exit on target ZM2 exit pid=' + fs.readFileSync(conf.PM2_PID_FILE_PATH).toString());
|
|
1767
|
+
global._auto_exit = true;
|
|
1768
|
+
return that.streamLogs('all', 0, false, 'HH:mm:ss', false);
|
|
1769
|
+
}
|
|
1770
|
+
// if (process.stdout.isTTY) if looking for start logs
|
|
1771
|
+
else if (!process.env.TRAVIS && process.env.NODE_ENV != 'test' && acted.length > 0 && (commander.attach === true)) {
|
|
1772
|
+
Common.info(`Log streaming apps id: ${chalk.cyan(acted.join(' '))}, exit with Ctrl-C or will exit in 10secs`)
|
|
1773
|
+
|
|
1774
|
+
// setTimeout(() => {
|
|
1775
|
+
// Common.info(`Log streaming exited automatically, run 'zm2 logs' to continue watching logs`)
|
|
1776
|
+
// return that.exitCli(code ? code : conf.SUCCESS_EXIT);
|
|
1777
|
+
// }, 10000)
|
|
1778
|
+
|
|
1779
|
+
return acted.forEach((proc_name) => {
|
|
1780
|
+
that.streamLogs(proc_name, 0, false, null, false);
|
|
1781
|
+
})
|
|
1782
|
+
}
|
|
1783
|
+
else {
|
|
1784
|
+
return that.exitCli(code ? code : conf.SUCCESS_EXIT);
|
|
1785
|
+
}
|
|
1786
|
+
}
|
|
1787
|
+
}
|
|
1788
|
+
|
|
1789
|
+
/**
|
|
1790
|
+
* Scale up/down a process
|
|
1791
|
+
* @method scale
|
|
1792
|
+
*/
|
|
1793
|
+
scale (app_name, number, cb) {
|
|
1794
|
+
var that = this;
|
|
1795
|
+
|
|
1796
|
+
function addProcs(proc, value, cb) {
|
|
1797
|
+
(function ex(proc, number) {
|
|
1798
|
+
if (number-- === 0) return cb();
|
|
1799
|
+
Common.printOut(conf.PREFIX_MSG + 'Scaling up application');
|
|
1800
|
+
that.Client.executeRemote('duplicateProcessId', proc.pm2_env.pm_id, ex.bind(this, proc, number));
|
|
1801
|
+
})(proc, number);
|
|
1802
|
+
}
|
|
1803
|
+
|
|
1804
|
+
function rmProcs(procs, value, cb) {
|
|
1805
|
+
var i = 0;
|
|
1806
|
+
|
|
1807
|
+
(function ex(procs, number) {
|
|
1808
|
+
if (number++ === 0) return cb();
|
|
1809
|
+
that._operate('deleteProcessId', procs[i++].pm2_env.pm_id, ex.bind(this, procs, number));
|
|
1810
|
+
})(procs, number);
|
|
1811
|
+
}
|
|
1812
|
+
|
|
1813
|
+
function end() {
|
|
1814
|
+
return cb ? cb(null, {success:true}) : that.speedList();
|
|
1815
|
+
}
|
|
1816
|
+
|
|
1817
|
+
this.Client.getProcessByName(app_name, function(err, procs) {
|
|
1818
|
+
if (err) {
|
|
1819
|
+
Common.printError(err);
|
|
1820
|
+
return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT);
|
|
1821
|
+
}
|
|
1822
|
+
|
|
1823
|
+
if (!procs || procs.length === 0) {
|
|
1824
|
+
Common.printError(conf.PREFIX_MSG_ERR + 'Application %s not found', app_name);
|
|
1825
|
+
return cb ? cb(new Error('App not found')) : that.exitCli(conf.ERROR_EXIT);
|
|
1826
|
+
}
|
|
1827
|
+
|
|
1828
|
+
var proc_number = procs.length;
|
|
1829
|
+
|
|
1830
|
+
if (typeof(number) === 'string' && number.indexOf('+') >= 0) {
|
|
1831
|
+
number = parseInt(number, 10);
|
|
1832
|
+
return addProcs(procs[0], number, end);
|
|
1833
|
+
}
|
|
1834
|
+
else if (typeof(number) === 'string' && number.indexOf('-') >= 0) {
|
|
1835
|
+
number = parseInt(number, 10);
|
|
1836
|
+
return rmProcs(procs[0], number, end);
|
|
1837
|
+
}
|
|
1838
|
+
else {
|
|
1839
|
+
number = parseInt(number, 10);
|
|
1840
|
+
number = number - proc_number;
|
|
1841
|
+
|
|
1842
|
+
if (number < 0)
|
|
1843
|
+
return rmProcs(procs, number, end);
|
|
1844
|
+
else if (number > 0)
|
|
1845
|
+
return addProcs(procs[0], number, end);
|
|
1846
|
+
else {
|
|
1847
|
+
Common.printError(conf.PREFIX_MSG_ERR + 'Nothing to do');
|
|
1848
|
+
return cb ? cb(new Error('Same process number')) : that.exitCli(conf.ERROR_EXIT);
|
|
1849
|
+
}
|
|
1850
|
+
}
|
|
1851
|
+
});
|
|
1852
|
+
}
|
|
1853
|
+
|
|
1854
|
+
/**
|
|
1855
|
+
* Description
|
|
1856
|
+
* @method describeProcess
|
|
1857
|
+
* @param {} pm2_id
|
|
1858
|
+
* @return
|
|
1859
|
+
*/
|
|
1860
|
+
describe (pm2_id, cb) {
|
|
1861
|
+
var that = this;
|
|
1862
|
+
|
|
1863
|
+
var found_proc = [];
|
|
1864
|
+
|
|
1865
|
+
that.Client.executeRemote('getMonitorData', {}, function(err, list) {
|
|
1866
|
+
if (err) {
|
|
1867
|
+
Common.printError('Error retrieving process list: ' + err);
|
|
1868
|
+
that.exitCli(conf.ERROR_EXIT);
|
|
1869
|
+
}
|
|
1870
|
+
|
|
1871
|
+
list.forEach(function(proc) {
|
|
1872
|
+
if ((!isNaN(pm2_id) && proc.pm_id == pm2_id) ||
|
|
1873
|
+
(typeof(pm2_id) === 'string' && proc.name == pm2_id)) {
|
|
1874
|
+
found_proc.push(proc);
|
|
1875
|
+
}
|
|
1876
|
+
});
|
|
1877
|
+
|
|
1878
|
+
if (found_proc.length === 0) {
|
|
1879
|
+
Common.printError(conf.PREFIX_MSG_WARNING + '%s doesn\'t exist', pm2_id);
|
|
1880
|
+
return cb ? cb(null, []) : that.exitCli(conf.ERROR_EXIT);
|
|
1881
|
+
}
|
|
1882
|
+
|
|
1883
|
+
if (!cb) {
|
|
1884
|
+
found_proc.forEach(function(proc) {
|
|
1885
|
+
UX.describe(proc);
|
|
1886
|
+
});
|
|
1887
|
+
}
|
|
1888
|
+
|
|
1889
|
+
return cb ? cb(null, found_proc) : that.exitCli(conf.SUCCESS_EXIT);
|
|
1890
|
+
});
|
|
1891
|
+
}
|
|
1892
|
+
|
|
1893
|
+
/**
|
|
1894
|
+
* API method to perform a deep update of ZM2
|
|
1895
|
+
* @method deepUpdate
|
|
1896
|
+
*/
|
|
1897
|
+
deepUpdate (cb) {
|
|
1898
|
+
var that = this;
|
|
1899
|
+
|
|
1900
|
+
Common.printOut(conf.PREFIX_MSG + 'Updating ZM2...');
|
|
1901
|
+
|
|
1902
|
+
var child = sexec("npm i -g zm2@latest; zm2 update");
|
|
1903
|
+
|
|
1904
|
+
child.stdout.on('end', function() {
|
|
1905
|
+
Common.printOut(conf.PREFIX_MSG + 'ZM2 successfully updated');
|
|
1906
|
+
cb ? cb(null, {success:true}) : that.exitCli(conf.SUCCESS_EXIT);
|
|
1907
|
+
});
|
|
1908
|
+
}
|
|
1909
|
+
};
|
|
1910
|
+
|
|
1911
|
+
|
|
1912
|
+
//////////////////////////
|
|
1913
|
+
// Load all API methods //
|
|
1914
|
+
//////////////////////////
|
|
1915
|
+
|
|
1916
|
+
require('./API/Extra.js')(API);
|
|
1917
|
+
require('./API/Deploy.js')(API);
|
|
1918
|
+
require('./API/Modules/index.js')(API);
|
|
1919
|
+
|
|
1920
|
+
require('./API/pm2-plus/link.js')(API);
|
|
1921
|
+
require('./API/pm2-plus/process-selector.js')(API);
|
|
1922
|
+
require('./API/pm2-plus/helpers.js')(API);
|
|
1923
|
+
|
|
1924
|
+
require('./API/Configuration.js')(API);
|
|
1925
|
+
require('./API/Version.js')(API);
|
|
1926
|
+
require('./API/Startup.js')(API);
|
|
1927
|
+
require('./API/LogManagement.js')(API);
|
|
1928
|
+
require('./API/Containerizer.js')(API);
|
|
1929
|
+
|
|
1930
|
+
|
|
1931
|
+
module.exports = API;
|