@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/God.js
ADDED
|
@@ -0,0 +1,632 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright 2013-2022 the PM2 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
|
+
|
|
7
|
+
/******************************
|
|
8
|
+
* ______ _______ ______
|
|
9
|
+
* | __ \ | |__ |
|
|
10
|
+
* | __/ | __|
|
|
11
|
+
* |___| |__|_|__|______|
|
|
12
|
+
*
|
|
13
|
+
* Main Daemon side file
|
|
14
|
+
*
|
|
15
|
+
******************************/
|
|
16
|
+
|
|
17
|
+
var cluster = require('cluster');
|
|
18
|
+
var numCPUs = require('os').cpus() ? require('os').cpus().length : 1;
|
|
19
|
+
var path = require('path');
|
|
20
|
+
var EventEmitter2 = require('eventemitter2').EventEmitter2;
|
|
21
|
+
var fs = require('fs');
|
|
22
|
+
var vizion = require('vizion');
|
|
23
|
+
var debug = require('debug')('zm2:god');
|
|
24
|
+
var Utility = require('./Utility');
|
|
25
|
+
var cst = require('../constants.js');
|
|
26
|
+
var timesLimit = require('async/timesLimit');
|
|
27
|
+
var Configuration = require('./Configuration.js');
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Override cluster module configuration
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
if (cst.IS_BUN == true) {
|
|
34
|
+
cluster.setupMaster({
|
|
35
|
+
windowsHide: true,
|
|
36
|
+
exec : path.resolve(path.dirname(module.filename), 'ProcessContainerBun.js')
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
cluster.setupMaster({
|
|
41
|
+
windowsHide: true,
|
|
42
|
+
exec : path.resolve(path.dirname(module.filename), 'ProcessContainer.js')
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Expose God
|
|
48
|
+
*/
|
|
49
|
+
var God = module.exports = {
|
|
50
|
+
next_id : 0,
|
|
51
|
+
clusters_db : {},
|
|
52
|
+
configuration: {},
|
|
53
|
+
started_at : Date.now(),
|
|
54
|
+
system_infos_proc: null,
|
|
55
|
+
system_infos: null,
|
|
56
|
+
bus : new EventEmitter2({
|
|
57
|
+
wildcard: true,
|
|
58
|
+
delimiter: ':',
|
|
59
|
+
maxListeners: 1000
|
|
60
|
+
})
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
Utility.overrideConsole(God.bus);
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Populate God namespace
|
|
67
|
+
*/
|
|
68
|
+
require('./Event.js')(God);
|
|
69
|
+
require('./God/Methods.js')(God);
|
|
70
|
+
require('./God/ForkMode.js')(God);
|
|
71
|
+
require('./God/ClusterMode.js')(God);
|
|
72
|
+
require('./God/Reload')(God);
|
|
73
|
+
require('./God/ActionMethods')(God);
|
|
74
|
+
require('./Watcher')(God);
|
|
75
|
+
|
|
76
|
+
God.init = function() {
|
|
77
|
+
require('./Worker.js')(this)
|
|
78
|
+
God.system_infos_proc = null
|
|
79
|
+
|
|
80
|
+
this.configuration = Configuration.getSync('pm2')
|
|
81
|
+
|
|
82
|
+
setTimeout(function() {
|
|
83
|
+
God.Worker.start()
|
|
84
|
+
}, 500)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
God.writeExitSeparator = function(pm2_env, code, signal) {
|
|
88
|
+
try {
|
|
89
|
+
var exit_sep = `[PM2][${new Date().toISOString()}] app exited`
|
|
90
|
+
if (code)
|
|
91
|
+
exit_sep += `itself with exit code: ${code}`
|
|
92
|
+
if (signal)
|
|
93
|
+
exit_sep += `by an external signal: ${signal}`
|
|
94
|
+
exit_sep += '\n'
|
|
95
|
+
|
|
96
|
+
if (pm2_env.pm_out_log_path)
|
|
97
|
+
fs.writeFileSync(pm2_env.pm_out_log_path, exit_sep)
|
|
98
|
+
if (pm2_env.pm_err_log_path)
|
|
99
|
+
fs.writeFileSync(pm2_env.pm_err_log_path, exit_sep)
|
|
100
|
+
if (pm2_env.pm_log_path)
|
|
101
|
+
fs.writeFileSync(pm2_env.pm_log_path, exit_sep)
|
|
102
|
+
} catch(e) {
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Init new process
|
|
108
|
+
*/
|
|
109
|
+
God.prepare = function prepare (env, cb) {
|
|
110
|
+
// generate a new unique id for each processes
|
|
111
|
+
env.env.unique_id = Utility.generateUUID()
|
|
112
|
+
|
|
113
|
+
// if the app is standalone, no multiple instance
|
|
114
|
+
if (typeof env.instances === 'undefined') {
|
|
115
|
+
env.vizion_running = false;
|
|
116
|
+
if (env.env && env.env.vizion_running) env.env.vizion_running = false;
|
|
117
|
+
|
|
118
|
+
if (env.status == cst.STOPPED_STATUS) {
|
|
119
|
+
env.pm_id = God.getNewId()
|
|
120
|
+
var clu = {
|
|
121
|
+
pm2_env : env,
|
|
122
|
+
process: {
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
God.clusters_db[env.pm_id] = clu
|
|
126
|
+
God.registerCron(env)
|
|
127
|
+
return cb(null, [ God.clusters_db[env.pm_id] ])
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return God.executeApp(env, function (err, clu) {
|
|
131
|
+
if (err) return cb(err);
|
|
132
|
+
God.notify('start', clu, true);
|
|
133
|
+
return cb(null, [ Utility.clone(clu) ]);
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// find how many replicate the user want
|
|
138
|
+
env.instances = parseInt(env.instances);
|
|
139
|
+
if (env.instances === 0) {
|
|
140
|
+
env.instances = numCPUs;
|
|
141
|
+
} else if (env.instances < 0) {
|
|
142
|
+
env.instances += numCPUs;
|
|
143
|
+
}
|
|
144
|
+
if (env.instances <= 0) {
|
|
145
|
+
env.instances = 1;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
timesLimit(env.instances, 1, function (n, next) {
|
|
149
|
+
env.vizion_running = false;
|
|
150
|
+
if (env.env && env.env.vizion_running) {
|
|
151
|
+
env.env.vizion_running = false;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
God.injectVariables(env, function inject (err, _env) {
|
|
155
|
+
if (err) return next(err);
|
|
156
|
+
return God.executeApp(Utility.clone(_env), function (err, clu) {
|
|
157
|
+
if (err) return next(err);
|
|
158
|
+
God.notify('start', clu, true);
|
|
159
|
+
// here call next wihtout an array because
|
|
160
|
+
// async.times aggregate the result into an array
|
|
161
|
+
return next(null, Utility.clone(clu));
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
}, cb);
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Launch the specified script (present in env)
|
|
169
|
+
* @api private
|
|
170
|
+
* @method executeApp
|
|
171
|
+
* @param {Mixed} env
|
|
172
|
+
* @param {Function} cb
|
|
173
|
+
* @return Literal
|
|
174
|
+
*/
|
|
175
|
+
God.executeApp = function executeApp(env, cb) {
|
|
176
|
+
var env_copy = Utility.clone(env);
|
|
177
|
+
|
|
178
|
+
Utility.extend(env_copy, env_copy.env);
|
|
179
|
+
|
|
180
|
+
env_copy['status'] = env.autostart ? cst.LAUNCHING_STATUS : cst.STOPPED_STATUS;
|
|
181
|
+
env_copy['pm_uptime'] = Date.now();
|
|
182
|
+
env_copy['axm_actions'] = [];
|
|
183
|
+
env_copy['axm_monitor'] = {};
|
|
184
|
+
env_copy['axm_options'] = {};
|
|
185
|
+
env_copy['axm_dynamic'] = {};
|
|
186
|
+
env_copy['vizion_running'] =
|
|
187
|
+
env_copy['vizion_running'] !== undefined ? env_copy['vizion_running'] : false;
|
|
188
|
+
|
|
189
|
+
if (!env_copy.created_at)
|
|
190
|
+
env_copy['created_at'] = Date.now();
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Enter here when it's the first time that the process is created
|
|
194
|
+
* 1 - Assign a new id
|
|
195
|
+
* 2 - Reset restart time and unstable_restarts
|
|
196
|
+
* 3 - Assign a log file name depending on the id
|
|
197
|
+
* 4 - If watch option is set, look for changes
|
|
198
|
+
*/
|
|
199
|
+
if (env_copy['pm_id'] === undefined) {
|
|
200
|
+
env_copy['pm_id'] = God.getNewId();
|
|
201
|
+
env_copy['restart_time'] = 0;
|
|
202
|
+
env_copy['unstable_restarts'] = 0;
|
|
203
|
+
|
|
204
|
+
// add -pm_id to pid file
|
|
205
|
+
env_copy.pm_pid_path = env_copy.pm_pid_path.replace(/-[0-9]+\.pid$|\.pid$/g, '-' + env_copy['pm_id'] + '.pid');
|
|
206
|
+
|
|
207
|
+
// If merge option, dont separate the logs
|
|
208
|
+
if (!env_copy['merge_logs']) {
|
|
209
|
+
['', '_out', '_err'].forEach(function(k){
|
|
210
|
+
var key = 'pm' + k + '_log_path';
|
|
211
|
+
env_copy[key] && (env_copy[key] = env_copy[key].replace(/-[0-9]+\.log$|\.log$/g, '-' + env_copy['pm_id'] + '.log'));
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Initiate watch file
|
|
216
|
+
if (env_copy['watch']) {
|
|
217
|
+
God.watch.enable(env_copy);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
God.registerCron(env_copy)
|
|
222
|
+
|
|
223
|
+
if (env_copy['autostart'] === false) {
|
|
224
|
+
var clu = {pm2_env: env_copy, process: {pid: 0}};
|
|
225
|
+
God.clusters_db[env_copy.pm_id] = clu;
|
|
226
|
+
return cb(null, clu);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
var cb_called = false
|
|
230
|
+
|
|
231
|
+
/** Callback when application is launched */
|
|
232
|
+
var readyCb = function ready(proc) {
|
|
233
|
+
cb_called = true
|
|
234
|
+
|
|
235
|
+
proc.pm2_env.version = Utility.findPackageVersion(proc.pm2_env.pm_exec_path || proc.pm2_env.cwd);
|
|
236
|
+
// If vizion enabled run versioning retrieval system
|
|
237
|
+
if (cst.ENABLE_GIT_PARSING === true &&
|
|
238
|
+
proc.pm2_env.vizion !== false && proc.pm2_env.vizion !== "false") {
|
|
239
|
+
God.finalizeProcedure(proc);
|
|
240
|
+
}
|
|
241
|
+
else
|
|
242
|
+
God.notify('online', proc);
|
|
243
|
+
|
|
244
|
+
if (proc.pm2_env.status !== cst.ERRORED_STATUS)
|
|
245
|
+
proc.pm2_env.status = cst.ONLINE_STATUS
|
|
246
|
+
|
|
247
|
+
console.log(`App [${proc.pm2_env.name}:${proc.pm2_env.pm_id}] online`);
|
|
248
|
+
if (cb) cb(null, proc);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
if (env_copy.exec_mode === 'cluster_mode') {
|
|
252
|
+
/**
|
|
253
|
+
* Cluster mode logic (for NodeJS apps)
|
|
254
|
+
*/
|
|
255
|
+
God.nodeApp(env_copy, function nodeApp(err, clu) {
|
|
256
|
+
if (cb && err) return cb(err);
|
|
257
|
+
if (err) return false;
|
|
258
|
+
|
|
259
|
+
var old_env = God.clusters_db[clu.pm2_env.pm_id];
|
|
260
|
+
|
|
261
|
+
if (old_env) {
|
|
262
|
+
old_env = null;
|
|
263
|
+
God.clusters_db[clu.pm2_env.pm_id] = null;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
God.clusters_db[clu.pm2_env.pm_id] = clu;
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
if (cst.IS_BUN) {
|
|
270
|
+
// When starting an app that does not listen on a port
|
|
271
|
+
// Bun do not call 'online' event
|
|
272
|
+
// This is a temporary workaround
|
|
273
|
+
var a = setTimeout(() => {
|
|
274
|
+
if (clu.pm2_env)
|
|
275
|
+
God.clusters_db[clu.pm2_env.pm_id].state = 'online'
|
|
276
|
+
return readyCb(clu)
|
|
277
|
+
}, 500)
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
clu.once('error', function(err) {
|
|
281
|
+
if (cst.IS_BUN)
|
|
282
|
+
clearTimeout(a)
|
|
283
|
+
|
|
284
|
+
console.error(err.stack || err);
|
|
285
|
+
try {
|
|
286
|
+
clu.destroy && clu.destroy();
|
|
287
|
+
}
|
|
288
|
+
catch (e) {
|
|
289
|
+
console.error(e.stack || e);
|
|
290
|
+
God.handleExit(clu, cst.ERROR_EXIT);
|
|
291
|
+
}
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
clu.once('disconnect', function() {
|
|
295
|
+
if (cst.IS_BUN)
|
|
296
|
+
clearTimeout(a)
|
|
297
|
+
|
|
298
|
+
console.log('App name:%s id:%s disconnected', clu.pm2_env.name, clu.pm2_env.pm_id);
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
clu.once('exit', function cluExit(code, signal) {
|
|
302
|
+
if (cst.IS_BUN) {
|
|
303
|
+
clearTimeout(a)
|
|
304
|
+
if (cb_called == false)
|
|
305
|
+
readyCb(clu);
|
|
306
|
+
}
|
|
307
|
+
//God.writeExitSeparator(clu.pm2_env, code, signal)
|
|
308
|
+
|
|
309
|
+
God.handleExit(clu, code || 0, signal || 'SIGINT');
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
return clu.once('online', function () {
|
|
313
|
+
if (cst.IS_BUN) {
|
|
314
|
+
clearTimeout(a);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
if (!clu.pm2_env.wait_ready)
|
|
318
|
+
return readyCb(clu);
|
|
319
|
+
|
|
320
|
+
// Timeout if the ready message has not been sent before listen_timeout
|
|
321
|
+
var ready_timeout = setTimeout(function() {
|
|
322
|
+
God.bus.removeListener('process:msg', listener)
|
|
323
|
+
return readyCb(clu)
|
|
324
|
+
}, clu.pm2_env.listen_timeout || cst.GRACEFUL_LISTEN_TIMEOUT);
|
|
325
|
+
|
|
326
|
+
var listener = function (packet) {
|
|
327
|
+
if (packet.raw === 'ready' &&
|
|
328
|
+
packet.process.name === clu.pm2_env.name &&
|
|
329
|
+
packet.process.pm_id === clu.pm2_env.pm_id) {
|
|
330
|
+
clearTimeout(ready_timeout);
|
|
331
|
+
God.bus.removeListener('process:msg', listener)
|
|
332
|
+
return readyCb(clu)
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
God.bus.on('process:msg', listener);
|
|
337
|
+
});
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
else {
|
|
341
|
+
/**
|
|
342
|
+
* Fork mode logic
|
|
343
|
+
*/
|
|
344
|
+
God.forkMode(env_copy, function forkMode(err, clu) {
|
|
345
|
+
if (cb && err) return cb(err);
|
|
346
|
+
if (err) return false;
|
|
347
|
+
|
|
348
|
+
var old_env = God.clusters_db[clu.pm2_env.pm_id];
|
|
349
|
+
if (old_env) old_env = null;
|
|
350
|
+
|
|
351
|
+
God.clusters_db[env_copy.pm_id] = clu;
|
|
352
|
+
|
|
353
|
+
clu.once('error', function cluError(err) {
|
|
354
|
+
console.error(err.stack || err);
|
|
355
|
+
try {
|
|
356
|
+
clu.kill && clu.kill();
|
|
357
|
+
}
|
|
358
|
+
catch (e) {
|
|
359
|
+
console.error(e.stack || e);
|
|
360
|
+
God.handleExit(clu, cst.ERROR_EXIT);
|
|
361
|
+
}
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
clu.once('exit', function cluClose(code, signal) {
|
|
365
|
+
//God.writeExitSeparator(clu.pm2_env, code, signal)
|
|
366
|
+
|
|
367
|
+
if (clu.connected === true)
|
|
368
|
+
clu.disconnect && clu.disconnect();
|
|
369
|
+
clu._reloadLogs = null;
|
|
370
|
+
return God.handleExit(clu, code || 0, signal);
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
if (!clu.pm2_env.wait_ready)
|
|
374
|
+
return readyCb(clu);
|
|
375
|
+
|
|
376
|
+
// Timeout if the ready message has not been sent before listen_timeout
|
|
377
|
+
var ready_timeout = setTimeout(function() {
|
|
378
|
+
God.bus.removeListener('process:msg', listener)
|
|
379
|
+
return readyCb(clu)
|
|
380
|
+
}, clu.pm2_env.listen_timeout || cst.GRACEFUL_LISTEN_TIMEOUT);
|
|
381
|
+
|
|
382
|
+
var listener = function (packet) {
|
|
383
|
+
if (packet.raw === 'ready' &&
|
|
384
|
+
packet.process.name === clu.pm2_env.name &&
|
|
385
|
+
packet.process.pm_id === clu.pm2_env.pm_id) {
|
|
386
|
+
clearTimeout(ready_timeout);
|
|
387
|
+
God.bus.removeListener('process:msg', listener)
|
|
388
|
+
return readyCb(clu)
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
God.bus.on('process:msg', listener);
|
|
392
|
+
});
|
|
393
|
+
}
|
|
394
|
+
return false;
|
|
395
|
+
};
|
|
396
|
+
|
|
397
|
+
/**
|
|
398
|
+
* Handle logic when a process exit (Node or Fork)
|
|
399
|
+
* @method handleExit
|
|
400
|
+
* @param {} clu
|
|
401
|
+
* @param {} exit_code
|
|
402
|
+
* @return
|
|
403
|
+
*/
|
|
404
|
+
God.handleExit = function handleExit(clu, exit_code, kill_signal) {
|
|
405
|
+
console.log(`App [${clu.pm2_env.name}:${clu.pm2_env.pm_id}] exited with code [${exit_code}] via signal [${kill_signal || 'SIGINT'}]`)
|
|
406
|
+
|
|
407
|
+
var proc = this.clusters_db[clu.pm2_env.pm_id];
|
|
408
|
+
|
|
409
|
+
if (!proc) {
|
|
410
|
+
console.error('Process undefined ? with process id ', clu.pm2_env.pm_id);
|
|
411
|
+
return false;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
var stopExitCodes = proc.pm2_env.stop_exit_codes !== undefined && proc.pm2_env.stop_exit_codes !== null ? proc.pm2_env.stop_exit_codes : [];
|
|
415
|
+
if (!Array.isArray(stopExitCodes)) {
|
|
416
|
+
stopExitCodes = [stopExitCodes];
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
var stopping = (proc.pm2_env.status == cst.STOPPING_STATUS
|
|
420
|
+
|| proc.pm2_env.status == cst.STOPPED_STATUS
|
|
421
|
+
|| proc.pm2_env.status == cst.ERRORED_STATUS)
|
|
422
|
+
|| (proc.pm2_env.autorestart === false || proc.pm2_env.autorestart === "false")
|
|
423
|
+
|| (stopExitCodes.map((strOrNum) => typeof strOrNum === 'string' ? parseInt(strOrNum, 10) : strOrNum)
|
|
424
|
+
.includes(exit_code));
|
|
425
|
+
|
|
426
|
+
var overlimit = false;
|
|
427
|
+
|
|
428
|
+
if (stopping) proc.process.pid = 0;
|
|
429
|
+
|
|
430
|
+
// Reset probes and actions
|
|
431
|
+
if (proc.pm2_env.axm_actions) proc.pm2_env.axm_actions = [];
|
|
432
|
+
if (proc.pm2_env.axm_monitor) proc.pm2_env.axm_monitor = {};
|
|
433
|
+
|
|
434
|
+
if (proc.pm2_env.status != cst.ERRORED_STATUS &&
|
|
435
|
+
proc.pm2_env.status != cst.STOPPING_STATUS)
|
|
436
|
+
proc.pm2_env.status = cst.STOPPED_STATUS;
|
|
437
|
+
|
|
438
|
+
if (proc.pm2_env.pm_id.toString().indexOf('_old_') !== 0) {
|
|
439
|
+
try {
|
|
440
|
+
fs.unlinkSync(proc.pm2_env.pm_pid_path);
|
|
441
|
+
} catch (e) {
|
|
442
|
+
debug('Error when unlinking pid file', e);
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
/**
|
|
447
|
+
* Avoid infinite reloop if an error is present
|
|
448
|
+
*/
|
|
449
|
+
// If the process has been created less than 15seconds ago
|
|
450
|
+
|
|
451
|
+
// And if the process has an uptime less than a second
|
|
452
|
+
var min_uptime = typeof(proc.pm2_env.min_uptime) !== 'undefined' ? proc.pm2_env.min_uptime : 1000;
|
|
453
|
+
var max_restarts = typeof(proc.pm2_env.max_restarts) !== 'undefined' ? proc.pm2_env.max_restarts : 16;
|
|
454
|
+
|
|
455
|
+
if ((Date.now() - proc.pm2_env.created_at) < (min_uptime * max_restarts)) {
|
|
456
|
+
if ((Date.now() - proc.pm2_env.pm_uptime) < min_uptime) {
|
|
457
|
+
// Increment unstable restart
|
|
458
|
+
proc.pm2_env.unstable_restarts += 1;
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
|
|
463
|
+
if (proc.pm2_env.unstable_restarts >= max_restarts) {
|
|
464
|
+
// Too many unstable restart in less than 15 seconds
|
|
465
|
+
// Set the process as 'ERRORED'
|
|
466
|
+
// And stop restarting it
|
|
467
|
+
proc.pm2_env.status = cst.ERRORED_STATUS;
|
|
468
|
+
proc.process.pid = 0;
|
|
469
|
+
|
|
470
|
+
console.log('Script %s had too many unstable restarts (%d). Stopped. %j',
|
|
471
|
+
proc.pm2_env.pm_exec_path,
|
|
472
|
+
proc.pm2_env.unstable_restarts,
|
|
473
|
+
proc.pm2_env.status);
|
|
474
|
+
|
|
475
|
+
God.notify('restart overlimit', proc);
|
|
476
|
+
|
|
477
|
+
proc.pm2_env.unstable_restarts = 0;
|
|
478
|
+
proc.pm2_env.created_at = null;
|
|
479
|
+
overlimit = true;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
if (typeof(exit_code) !== 'undefined') proc.pm2_env.exit_code = exit_code;
|
|
483
|
+
|
|
484
|
+
God.notify('exit', proc);
|
|
485
|
+
|
|
486
|
+
if (God.pm2_being_killed) {
|
|
487
|
+
//console.log('[HandleExit] PM2 is being killed, stopping restart procedure...');
|
|
488
|
+
return false;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
var restart_delay = 0;
|
|
492
|
+
|
|
493
|
+
if (proc.pm2_env.restart_delay !== undefined &&
|
|
494
|
+
!isNaN(parseInt(proc.pm2_env.restart_delay))) {
|
|
495
|
+
proc.pm2_env.status = cst.WAITING_RESTART;
|
|
496
|
+
restart_delay = parseInt(proc.pm2_env.restart_delay);
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
if (proc.pm2_env.exp_backoff_restart_delay !== undefined &&
|
|
500
|
+
!isNaN(parseInt(proc.pm2_env.exp_backoff_restart_delay))) {
|
|
501
|
+
proc.pm2_env.status = cst.WAITING_RESTART;
|
|
502
|
+
if (!proc.pm2_env.prev_restart_delay) {
|
|
503
|
+
proc.pm2_env.prev_restart_delay = proc.pm2_env.exp_backoff_restart_delay
|
|
504
|
+
restart_delay = proc.pm2_env.exp_backoff_restart_delay
|
|
505
|
+
}
|
|
506
|
+
else {
|
|
507
|
+
proc.pm2_env.prev_restart_delay = Math.floor(Math.min(15000, proc.pm2_env.prev_restart_delay * 1.5))
|
|
508
|
+
restart_delay = proc.pm2_env.prev_restart_delay
|
|
509
|
+
}
|
|
510
|
+
console.log(`App [${clu.pm2_env.name}:${clu.pm2_env.pm_id}] will restart in ${restart_delay}ms`)
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
if (!stopping && !overlimit) {
|
|
514
|
+
//make this property unenumerable
|
|
515
|
+
Object.defineProperty(proc.pm2_env, 'restart_task', {configurable: true, writable: true});
|
|
516
|
+
proc.pm2_env.restart_task = setTimeout(function() {
|
|
517
|
+
proc.pm2_env.restart_time += 1;
|
|
518
|
+
God.executeApp(proc.pm2_env);
|
|
519
|
+
}, restart_delay);
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
return false;
|
|
523
|
+
};
|
|
524
|
+
|
|
525
|
+
/**
|
|
526
|
+
* @method finalizeProcedure
|
|
527
|
+
* @param proc {Object}
|
|
528
|
+
* @return
|
|
529
|
+
*/
|
|
530
|
+
God.finalizeProcedure = function finalizeProcedure(proc) {
|
|
531
|
+
var last_path = '';
|
|
532
|
+
var current_path = proc.pm2_env.cwd || path.dirname(proc.pm2_env.pm_exec_path);
|
|
533
|
+
var proc_id = proc.pm2_env.pm_id;
|
|
534
|
+
|
|
535
|
+
proc.pm2_env.version = Utility.findPackageVersion(proc.pm2_env.pm_exec_path || proc.pm2_env.cwd);
|
|
536
|
+
|
|
537
|
+
if (proc.pm2_env.vizion_running === true) {
|
|
538
|
+
debug('Vizion is already running for proc id: %d, skipping this round', proc_id);
|
|
539
|
+
return God.notify('online', proc);
|
|
540
|
+
}
|
|
541
|
+
proc.pm2_env.vizion_running = true;
|
|
542
|
+
|
|
543
|
+
vizion.analyze({folder : current_path}, function recur_path(err, meta){
|
|
544
|
+
var proc = God.clusters_db[proc_id];
|
|
545
|
+
|
|
546
|
+
if (err)
|
|
547
|
+
debug(err.stack || err);
|
|
548
|
+
|
|
549
|
+
if (!proc ||
|
|
550
|
+
!proc.pm2_env ||
|
|
551
|
+
proc.pm2_env.status == cst.STOPPED_STATUS ||
|
|
552
|
+
proc.pm2_env.status == cst.STOPPING_STATUS ||
|
|
553
|
+
proc.pm2_env.status == cst.ERRORED_STATUS) {
|
|
554
|
+
return console.error('Cancelling versioning data parsing');
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
proc.pm2_env.vizion_running = false;
|
|
558
|
+
|
|
559
|
+
if (!err) {
|
|
560
|
+
proc.pm2_env.versioning = meta;
|
|
561
|
+
proc.pm2_env.versioning.repo_path = current_path;
|
|
562
|
+
God.notify('online', proc);
|
|
563
|
+
}
|
|
564
|
+
else if (err && current_path === last_path) {
|
|
565
|
+
proc.pm2_env.versioning = null;
|
|
566
|
+
God.notify('online', proc);
|
|
567
|
+
}
|
|
568
|
+
else {
|
|
569
|
+
last_path = current_path;
|
|
570
|
+
current_path = path.dirname(current_path);
|
|
571
|
+
proc.pm2_env.vizion_running = true;
|
|
572
|
+
vizion.analyze({folder : current_path}, recur_path);
|
|
573
|
+
}
|
|
574
|
+
return false;
|
|
575
|
+
});
|
|
576
|
+
};
|
|
577
|
+
|
|
578
|
+
/**
|
|
579
|
+
* Inject variables into processes
|
|
580
|
+
* @param {Object} env environnement to be passed to the process
|
|
581
|
+
* @param {Function} cb invoked with <err, env>
|
|
582
|
+
*/
|
|
583
|
+
God.injectVariables = function injectVariables (env, cb) {
|
|
584
|
+
// allow to override the key of NODE_APP_INSTANCE if wanted
|
|
585
|
+
var instanceKey = process.env.PM2_PROCESS_INSTANCE_VAR || env.instance_var;
|
|
586
|
+
|
|
587
|
+
// we need to find the last NODE_APP_INSTANCE used
|
|
588
|
+
var instances = Object.keys(God.clusters_db)
|
|
589
|
+
.map(function (procId) {
|
|
590
|
+
return God.clusters_db[procId];
|
|
591
|
+
}).filter(function (proc) {
|
|
592
|
+
return proc.pm2_env.name === env.name &&
|
|
593
|
+
typeof proc.pm2_env[instanceKey] !== 'undefined';
|
|
594
|
+
}).map(function (proc) {
|
|
595
|
+
return proc.pm2_env[instanceKey];
|
|
596
|
+
}).sort(function (a, b) {
|
|
597
|
+
return b - a;
|
|
598
|
+
});
|
|
599
|
+
// default to last one + 1
|
|
600
|
+
var instanceNumber = typeof instances[0] === 'undefined' ? 0 : instances[0] + 1;
|
|
601
|
+
// but try to find a one available
|
|
602
|
+
for (var i = 0; i < instances.length; i++) {
|
|
603
|
+
if (instances.indexOf(i) === -1) {
|
|
604
|
+
instanceNumber = i;
|
|
605
|
+
break;
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
env[instanceKey] = instanceNumber;
|
|
609
|
+
|
|
610
|
+
// if using increment_var, we need to increment it
|
|
611
|
+
if (env.increment_var) {
|
|
612
|
+
var lastIncrement = Object.keys(God.clusters_db)
|
|
613
|
+
.map(function (procId) {
|
|
614
|
+
return God.clusters_db[procId];
|
|
615
|
+
}).filter(function (proc) {
|
|
616
|
+
return proc.pm2_env.name === env.name &&
|
|
617
|
+
typeof proc.pm2_env[env.increment_var] !== 'undefined';
|
|
618
|
+
}).map(function (proc) {
|
|
619
|
+
return Number(proc.pm2_env[env.increment_var]);
|
|
620
|
+
}).sort(function (a, b) {
|
|
621
|
+
return b - a;
|
|
622
|
+
})[0];
|
|
623
|
+
// inject a incremental variable
|
|
624
|
+
var defaut = Number(env.env[env.increment_var]) || 0;
|
|
625
|
+
env[env.increment_var] = typeof lastIncrement === 'undefined' ? defaut : lastIncrement + 1;
|
|
626
|
+
env.env[env.increment_var] = env[env.increment_var];
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
return cb(null, env);
|
|
630
|
+
};
|
|
631
|
+
|
|
632
|
+
God.init()
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright 2013-2022 the PM2 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
|
+
var http = require('http');
|
|
7
|
+
var os = require('os');
|
|
8
|
+
var pm2 = require('../index.js');
|
|
9
|
+
var urlT = require('url');
|
|
10
|
+
var cst = require('../constants.js');
|
|
11
|
+
|
|
12
|
+
// Default, attach to default local PM2
|
|
13
|
+
|
|
14
|
+
pm2.connect(function() {
|
|
15
|
+
startWebServer(pm2);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
function startWebServer(pm2) {
|
|
19
|
+
http.createServer(function (req, res) {
|
|
20
|
+
// Add CORS headers to allow browsers to fetch data directly
|
|
21
|
+
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
22
|
+
res.setHeader('Access-Control-Allow-Headers', 'Cache-Control, Pragma, Origin, Authorization, Content-Type, X-Requested-With');
|
|
23
|
+
res.setHeader('Access-Control-Allow-Methods', 'GET');
|
|
24
|
+
|
|
25
|
+
// We always send json
|
|
26
|
+
res.setHeader('Content-Type','application/json');
|
|
27
|
+
|
|
28
|
+
var path = urlT.parse(req.url).pathname;
|
|
29
|
+
|
|
30
|
+
if (path == '/') {
|
|
31
|
+
// Main monit route
|
|
32
|
+
pm2.list(function(err, list) {
|
|
33
|
+
if (err) {
|
|
34
|
+
return res.send(err);
|
|
35
|
+
}
|
|
36
|
+
var data = {
|
|
37
|
+
system_info: { hostname: os.hostname(),
|
|
38
|
+
uptime: os.uptime()
|
|
39
|
+
},
|
|
40
|
+
monit: { loadavg: os.loadavg(),
|
|
41
|
+
total_mem: os.totalmem(),
|
|
42
|
+
free_mem: os.freemem(),
|
|
43
|
+
cpu: os.cpus(),
|
|
44
|
+
interfaces: os.networkInterfaces()
|
|
45
|
+
},
|
|
46
|
+
processes: list
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
if (cst.WEB_STRIP_ENV_VARS === true) {
|
|
50
|
+
for (var i = data.processes.length - 1; i >= 0; i--) {
|
|
51
|
+
var proc = data.processes[i];
|
|
52
|
+
|
|
53
|
+
// Strip important environment variables
|
|
54
|
+
if (typeof proc.pm2_env === 'undefined' && typeof proc.pm2_env.env === 'undefined') return;
|
|
55
|
+
|
|
56
|
+
delete proc.pm2_env.env;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
res.statusCode = 200;
|
|
61
|
+
res.write(JSON.stringify(data));
|
|
62
|
+
return res.end();
|
|
63
|
+
|
|
64
|
+
})
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
// 404
|
|
68
|
+
res.statusCode = 404;
|
|
69
|
+
res.write(JSON.stringify({err : '404'}));
|
|
70
|
+
return res.end();
|
|
71
|
+
}
|
|
72
|
+
}).listen(process.env.PM2_WEB_PORT || cst.WEB_PORT, cst.WEB_IPADDR, function() {
|
|
73
|
+
console.log('Web interface listening on %s:%s', cst.WEB_IPADDR, cst.WEB_PORT);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
}
|