mirror of
https://github.com/Unitech/pm2.git
synced 2025-12-08 20:35:53 +00:00
refactor communication protocol - upgrade axon & rpc
This commit is contained in:
commit
3f6323f957
23
CHANGELOG.md
23
CHANGELOG.md
@ -1,4 +1,27 @@
|
||||
|
||||
# 0.10.0 - PM2 Hellfire release
|
||||
|
||||
- PM2 hearth code has been refactored and now it handles extreme scenario without any leak or bug
|
||||
- Restart app when reaching a limit of memory by using --max-memory-restart (and max_memory_restart via JSON)(https://github.com/Unitech/pm2#max-memory-restart)
|
||||
- PM2 respects strong unix standard process management
|
||||
- Remove timestamps by default with pm2 logs
|
||||
- Coffeescript not enabled by default anymore (enhance memory usage)
|
||||
- PM2 Programmatic interface enhanced
|
||||
- PM2 hearth refactor
|
||||
- PM2 describe show node-args
|
||||
- node_args for V8 options is now available via JSON declaration
|
||||
- Watch system avoid ghost processes
|
||||
- Memory leak fixes
|
||||
- Better performance on interface
|
||||
- Fix tests
|
||||
- Enable PM2_NODE_OPTIONS and node-args for fork mode
|
||||
- Dependencies updated
|
||||
- Faster monitoring system
|
||||
- AXM actions unification
|
||||
- Socket errors handled
|
||||
- Watchdog via Agent - restart automatically PM2 with previous processes in case of crash
|
||||
- PM2_NODE_OPTIONS deprecation (use --node-args instead)
|
||||
|
||||
# 0.9.6 - 0.9.5 - 0.9.4
|
||||
|
||||
- Bash test auto exit when failure
|
||||
|
||||
@ -5,27 +5,33 @@
|
||||
When you got an issue by using pm2, you will fire an issue on [github](https://github.com/Unitech/pm2). We'll be glad to help or to fix it but the more data you give the most fast it would be resolved.
|
||||
Please try following these rules it will make the task easier for you and for us:
|
||||
|
||||
#### 1) Search through issues if it hasn't been resolved yet
|
||||
#### 2) Make sure that you provide following informations:
|
||||
#### 1. Search through issues if it hasn't been resolved yet
|
||||
#### 2. Make sure that you provide following informations:
|
||||
- pm2 version `pm2 --version`
|
||||
- nodejs version `node --version`
|
||||
- operating system
|
||||
|
||||
#### 3) Provide details about your issue:
|
||||
#### 3. Provide details about your issue:
|
||||
- What are the steps that brought me to the issue?
|
||||
- How may I reproduce this? (this isn't easy in some cases)
|
||||
- Are you using a cluster module? Are you trying to catch SIGTERM signals? With `code` if possible.
|
||||
|
||||
#### 4) Think global if your issue is too specific we might not be able to help
|
||||
#### 4. Think global
|
||||
If your issue is too specific we might not be able to help and stackoverflow might be a better place to seak for an answer
|
||||
|
||||
#### 5) Be clear and format issues with [markdown](http://daringfireball.net/projects/markdown/)
|
||||
#### 5. Be clear and format issues with [markdown](http://daringfireball.net/projects/markdown/)
|
||||
Note that we might understand english, german and french
|
||||
|
||||
#### 6) Use debugging functions:
|
||||
#### 6. Use debugging functions:
|
||||
|
||||
```DEBUG=pm2:* PM2_DEBUG=true ./bin/pm2 --no-daemon start my-buggy-thing.js```
|
||||
|
||||
If your issue is flagged as `need data` be sure that there won't be any upgrade unless we can have enough data to reproduce.
|
||||
|
||||
## Pull-Requests
|
||||
|
||||
@Todo
|
||||
1. Fork pm2
|
||||
2. Create a different branch to do your fixes/improvements if it's core-related.
|
||||
3. Please add unit tests! There are lots of tests take examples from there!
|
||||
4. Try to be as clear as possible in your commits
|
||||
5. Pull request on pm2 from your branch
|
||||
|
||||
90
README.md
90
README.md
@ -12,6 +12,7 @@ pm2 is perfect when you need to spread your stateless Node.js code across all CP
|
||||
- Script daemonization
|
||||
- 0s downtime reload for Node apps
|
||||
- Generate SystemV/SystemD startup scripts (Ubuntu, Centos...)
|
||||
- Set memory limit for process to restart
|
||||
- Pause unstable process (avoid infinite loop)
|
||||
- Restart on file change with `--watch`
|
||||
- Monitoring in console
|
||||
@ -62,6 +63,7 @@ Thanks in advance and we hope that you like pm2!
|
||||
|
||||
- [Transitional state of apps](#a4)
|
||||
- [Process listing](#a6)
|
||||
- [Automatic restart process based on memory](#max-memory-restart)
|
||||
- [Monitoring CPU/Memory usage](#a7)
|
||||
- [Logs management](#a9)
|
||||
- [Clustering](#a5)
|
||||
@ -179,6 +181,7 @@ $ pm2 delete all # Will remove all processes from pm2 list
|
||||
|
||||
# Misc
|
||||
|
||||
$ pm2 reset <process> # Reset meta data (restarted time...)
|
||||
$ pm2 updatePM2 # Update in memory pm2
|
||||
$ pm2 ping # Ensure pm2 daemon has been launched
|
||||
$ pm2 sendSignal SIGUSR2 my-app # Send system signal to script
|
||||
@ -219,13 +222,15 @@ For scripts in other languages:
|
||||
|
||||
```bash
|
||||
$ pm2 start echo.coffee
|
||||
$ pm2 start echo.php
|
||||
$ pm2 start echo.py
|
||||
$ pm2 start echo.sh
|
||||
$ pm2 start echo.rb
|
||||
$ pm2 start echo.pl
|
||||
$ pm2 start -x echo.php
|
||||
$ pm2 start -x echo.py
|
||||
$ pm2 start -x echo.sh
|
||||
$ pm2 start -x echo.rb
|
||||
$ pm2 start -x echo.pl
|
||||
```
|
||||
|
||||
The not javascript languages will have to be run in [fork mode](#a23).
|
||||
|
||||
<a name="a987"/>
|
||||
## Options
|
||||
|
||||
@ -325,6 +330,25 @@ To get more details about a specific process:
|
||||
$ pm2 describe 0
|
||||
```
|
||||
|
||||
<a name="max-memory-restart"/>
|
||||
## Automatic restart process based on memory
|
||||
|
||||
Value passed is in megaoctets. Internally it uses the V8 flag `--max-old-space-size=MEM` to make a process exit when memory exceed a certain amount of RAM used.
|
||||
|
||||
CLI:
|
||||
```bash
|
||||
$ pm2 start big-array.js --max-memory-restart 20
|
||||
```
|
||||
|
||||
JSON:
|
||||
```json
|
||||
{
|
||||
"name" : "max_mem",
|
||||
"script" : "big-array.js",
|
||||
"max_memory_restart" : "20"
|
||||
}
|
||||
```
|
||||
|
||||
<a name="a7"/>
|
||||
## Monitoring CPU/Memory usage
|
||||
|
||||
@ -526,7 +550,6 @@ To watch specifics paths, please use a JSON app declaration, `watch` can take a
|
||||
"watch": ["server", "client"],
|
||||
"ignoreWatch" : ["node_modules", "client/img"]
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
<a name="a10"/>
|
||||
@ -543,6 +566,7 @@ You can define parameters for your apps in `processes.json`:
|
||||
"log_date_format" : "YYYY-MM-DD HH:mm Z",
|
||||
"ignoreWatch" : ["[\\/\\\\]\\./", "node_modules"],
|
||||
"watch" : "true",
|
||||
"node_args" : "--harmony",
|
||||
"cwd" : "/this/is/a/path/to/start/script",
|
||||
"env": {
|
||||
"NODE_ENV": "production",
|
||||
@ -611,6 +635,7 @@ Note that if you execute `pm2 start node-app-2` again, it will spawn an addition
|
||||
"cwd" : "/srv/node-app/current",
|
||||
"args" : "['--toto=heya coco', '-d', '1']",
|
||||
"script" : "bin/app.js",
|
||||
"node_args" : "--harmony",
|
||||
"log_date_format" : "YYYY-MM-DD HH:mm Z",
|
||||
"error_file" : "/var/log/node-app/node-app.stderr.log",
|
||||
"out_file" : "log/node-app.stdout.log",
|
||||
@ -913,7 +938,6 @@ PM2_BIND_ADDR
|
||||
PM2_API_PORT
|
||||
PM2_GRACEFUL_TIMEOUT
|
||||
PM2_MODIFY_REQUIRE
|
||||
PM2_NODE_OPTIONS
|
||||
```
|
||||
|
||||
|
||||
@ -926,36 +950,21 @@ $ pm2 web
|
||||
<a name="a66"/>
|
||||
## Enabling Harmony ES6
|
||||
|
||||
### Enable by default for all processes
|
||||
|
||||
You can enable Harmony ES6 by setting `PM2_NODE_OPTIONS='--harmony'` environment variable option when you start pm2 (pm2 should not be already daemonized).
|
||||
|
||||
To pass this option by default, you can edit `~/.pm2/custom_options.sh` and add:
|
||||
|
||||
```bash
|
||||
export PM2_NODE_OPTIONS='--harmony'
|
||||
```
|
||||
|
||||
Then:
|
||||
|
||||
```bash
|
||||
$ pm2 dump
|
||||
$ pm2 exit
|
||||
$ pm2 resurrect
|
||||
```
|
||||
|
||||
If ES6 has been enabled you should see this message at the beginning of each pm2 command:
|
||||
|
||||
```
|
||||
● ES6 mode
|
||||
```
|
||||
|
||||
### Enable for specific processes
|
||||
|
||||
The `--node-args` option permit to launch script with V8 flags, so to enable harmony for a process just do this:
|
||||
```bash
|
||||
$ pm2 start my_app.js --node-args="--harmony"
|
||||
```
|
||||
|
||||
And with JSON declaration:
|
||||
|
||||
```bash
|
||||
[{
|
||||
"name" : "ES6",
|
||||
"script" : "es6.js",
|
||||
"node_args" : "--harmony"
|
||||
}]
|
||||
```
|
||||
|
||||
<a name="a19"/>
|
||||
## CoffeeScript
|
||||
|
||||
@ -1029,6 +1038,8 @@ $ pm2 start my-bash-script.sh -x --interpreter bash
|
||||
$ pm2 start my-python-script.py -x --interpreter python
|
||||
```
|
||||
|
||||
The interpreter is deduced from the file extension from the [following list](https://github.com/Unitech/pm2/blob/master/lib/interpreter.json).
|
||||
|
||||
<a name="a96"/>
|
||||
## JSON app configuration via pipe from stdout
|
||||
|
||||
@ -1123,6 +1134,13 @@ For more information about this, see [issue #74](https://github.com/Unitech/pm2/
|
||||
When using the cluster mode (by default) you can't use ports from 0 to 1024. If you really need to exec in this range use the [fork mode](#a23) with the `-x` parameter.
|
||||
By using the fork mode you will lose core features of pm2 like the automatic clusterization of your code over all CPUs available and the 0s reload.
|
||||
|
||||
### User tips from issues
|
||||
- [Vagrant and pm2 #289](https://github.com/Unitech/pm2/issues/289#issuecomment-42900019)
|
||||
- [Start the same app on different ports #322](https://github.com/Unitech/pm2/issues/322#issuecomment-46792733)
|
||||
- [Using ansible with pm2](https://github.com/Unitech/pm2/issues/88#issuecomment-49106686)
|
||||
- [Cron string as argument](https://github.com/Unitech/pm2/issues/496#issuecomment-49323861)
|
||||
- [Restart when process reaches a specific memory amount](https://github.com/Unitech/pm2/issues/141)
|
||||
|
||||
<a name="a20"/>
|
||||
## External resources and articles
|
||||
|
||||
@ -1142,12 +1160,6 @@ By using the fork mode you will lose core features of pm2 like the automatic clu
|
||||
- http://revdancatt.com/2013/09/17/node-day-1-getting-the-server-installing-node-and-pm2/
|
||||
- https://medium.com/tech-talk/e7c0b0e5ce3c
|
||||
|
||||
## Some tips
|
||||
- [Vagrant and pm2 #289](https://github.com/Unitech/pm2/issues/289#issuecomment-42900019)
|
||||
- [Start the same app on different ports #322](https://github.com/Unitech/pm2/issues/322#issuecomment-46792733)
|
||||
- [Using ansible with pm2](https://github.com/Unitech/pm2/issues/88#issuecomment-49106686)
|
||||
- [Cron string as argument](https://github.com/Unitech/pm2/issues/496#issuecomment-49323861)
|
||||
|
||||
## Contributors
|
||||
|
||||
```
|
||||
|
||||
16
bin/pm2
16
bin/pm2
@ -10,6 +10,7 @@ var util = require('util');
|
||||
var cronJob = require('cron').CronJob;
|
||||
var chalk = require('chalk');
|
||||
|
||||
var debug = require('debug')('pm2:cli');
|
||||
var Satan = require('../lib/Satan');
|
||||
var CLI = require('../lib/CLI');
|
||||
var cst = require('../constants.js');
|
||||
@ -25,6 +26,7 @@ commander.version(pkg.version)
|
||||
.option('-o --output <path>', 'specify out log file')
|
||||
.option('-e --error <path>', 'specify error log file')
|
||||
.option('-p --pid <pid>', 'specify pid file')
|
||||
.option('--max-memory-restart <memory>', 'specify max memory amount used to autorestart (in megaoctets)')
|
||||
.option('--env <environment_name>', 'specify environment to get specific env variables (for JSON declaration)')
|
||||
.option('-x --execute-command', 'execute a program using fork system')
|
||||
.option('-u --user <username>', 'define user when generating startup script')
|
||||
@ -470,6 +472,7 @@ if (process.argv.length == 2) {
|
||||
// in file Satan.js, method Satan.launchRPC
|
||||
//
|
||||
process.once('satan:client:ready', function() {
|
||||
debug('Got message from Satan as succesfully connected to PM2, now parsing arguments');
|
||||
CLI.getVersion(function(err, remote_version) {
|
||||
if (!err && (pkg.version != remote_version)) {
|
||||
console.log('');
|
||||
@ -507,16 +510,3 @@ process.once('satan:client:ready', function() {
|
||||
}
|
||||
});
|
||||
})();
|
||||
|
||||
(function testHarmony() {
|
||||
//
|
||||
// Harmony test
|
||||
//
|
||||
try {
|
||||
var assert = require('assert')
|
||||
, s = new Set();
|
||||
s.add('a');
|
||||
assert.ok(s.has('a'));
|
||||
console.log('● ES6 mode'.green);
|
||||
} catch(e) {}
|
||||
})();
|
||||
|
||||
@ -36,8 +36,8 @@ var default_conf = {
|
||||
DEBUG : process.env.PM2_DEBUG || false,
|
||||
WEB_INTERFACE : parseInt(process.env.PM2_API_PORT) || 9615,
|
||||
MODIFY_REQUIRE : process.env.PM2_MODIFY_REQUIRE || false,
|
||||
PREFIX_MSG : '\x1B[32mPM2 \x1B[39m',
|
||||
PREFIX_MSG_ERR : '\x1B[31mPM2 [ERROR] \x1B[39m',
|
||||
PREFIX_MSG : '\x1B[32m[PM2] \x1B[39m',
|
||||
PREFIX_MSG_ERR : '\x1B[31m[PM2] [ERROR] \x1B[39m',
|
||||
SAMPLE_FILE_PATH : '../lib/sample.json',
|
||||
|
||||
CENTOS_STARTUP_SCRIPT : '../lib/scripts/pm2-init-centos.sh',
|
||||
|
||||
150
lib/CLI.js
150
lib/CLI.js
@ -43,17 +43,22 @@ CLI.start = function(script, opts, cb) {
|
||||
opts = {};
|
||||
}
|
||||
|
||||
if (opts.nodeArgs)
|
||||
if (opts.nodeArgs) {
|
||||
//maintain backwards compat for space delimited string args
|
||||
if (Array.isArray(opts.nodeArgs)){
|
||||
appConf['nodeArgs'] = opts.nodeArgs;
|
||||
appConf['node_args'] = opts.nodeArgs;
|
||||
} else {
|
||||
appConf['nodeArgs'] = opts.nodeArgs.split(' ');
|
||||
appConf['node_args'] = opts.nodeArgs.split(' ');
|
||||
}
|
||||
} else {
|
||||
appConf.node_args = [];
|
||||
}
|
||||
if (opts.scriptArgs)
|
||||
appConf['args'] = JSON.stringify(opts.scriptArgs);
|
||||
if (opts.name)
|
||||
appConf['name'] = opts.name;
|
||||
if (opts.maxMemoryRestart)
|
||||
appConf.max_memory_restart = opts.maxMemoryRestart;
|
||||
if (opts.instances)
|
||||
appConf['instances'] = opts.instances;
|
||||
if (opts.error)
|
||||
@ -95,9 +100,12 @@ CLI.start = function(script, opts, cb) {
|
||||
appConf['exec_mode'] = opts.execMode;
|
||||
}
|
||||
|
||||
// if (appConf['exec_mode'] == 'cluster_mode' && process.version.match(/0.10/)) {
|
||||
// printOut(cst.PREFIX_MSG_ERR + ' [Warning], you\'re using the 0.10.x node version, it\'s prefered that you switch to fork mode by adding the -x parameter.');
|
||||
// }
|
||||
if (appConf['exec_mode'] == 'cluster_mode' &&
|
||||
process.version.match(/0.10/) &&
|
||||
(parseInt(appConf.instances) == 1 || !appConf.instances)) {
|
||||
//appConf['exec_mode'] = 'fork_mode';
|
||||
printOut('\x1B[31m[PM2] [WARNING] \x1B[39m you\'re using the 0.10.x node version, it\'s prefered that you switch to fork mode by adding the -x parameter. 0.11.x can use cluster_mode (default mode) efficiently.');
|
||||
}
|
||||
|
||||
// Script arguments
|
||||
|
||||
@ -464,6 +472,7 @@ CLI.startup = function(platform, opts, cb) {
|
||||
* @param {string} machine_name
|
||||
*/
|
||||
CLI.interact = function(secret_key, public_key, machine_name, cb) {
|
||||
debug('Launching interact');
|
||||
InteractorDaemonizer.launchAndInteract({
|
||||
secret_key : secret_key || null,
|
||||
public_key : public_key || null,
|
||||
@ -482,22 +491,8 @@ CLI.interact = function(secret_key, public_key, machine_name, cb) {
|
||||
* @method killInteract
|
||||
*/
|
||||
CLI.killInteract = function(cb) {
|
||||
InteractorDaemonizer.ping(function(online) {
|
||||
if (!online) {
|
||||
if (!cb) printError('Interactor not launched');
|
||||
return cb ? cb({msg:'Interactor not launched'}) : exitCli(cst.SUCCESS_EXIT);
|
||||
}
|
||||
InteractorDaemonizer.launchRPC(function() {
|
||||
InteractorDaemonizer.rpc.kill(function(err) {
|
||||
if (err) {
|
||||
if (!cb) printError(err);
|
||||
return cb ? cb({msg : err}) : exitCli(cst.ERROR_EXIT);
|
||||
}
|
||||
if (!cb) printOut('Interactor successfully killed');
|
||||
return cb ? cb(null, {msg : 'killed'}) : exitCli(cst.SUCCESS_EXIT);
|
||||
});
|
||||
});
|
||||
return false;
|
||||
InteractorDaemonizer.killDaemon(function(err) {
|
||||
return cb ? cb({msg:'Interactor not launched'}) : exitCli(cst.ERROR_EXIT);
|
||||
});
|
||||
};
|
||||
|
||||
@ -540,7 +535,7 @@ CLI.resetMetaProcess = function(process_name, cb) {
|
||||
async.eachLimit(ids, 4, function(id, next) {
|
||||
Satan.executeRemote('resetMetaProcessId', id, function(err, res) {
|
||||
if (err) console.error(err);
|
||||
printOut('Reseting meta for process id %d', id);
|
||||
printOut(cst.PREFIX_MSG + 'Reseting meta for process id %d', id);
|
||||
return next();
|
||||
});
|
||||
}, function(err) {
|
||||
@ -610,29 +605,35 @@ CLI.resurrect = function(cb) {
|
||||
CLI.updatePM2 = function(cb) {
|
||||
printOut('Be sure to haave the latest version by doing `npm install pm2@latest -g` before doing this procedure.');
|
||||
|
||||
InteractorDaemonizer.update(function() {
|
||||
|
||||
// Kill Daemon and disconnect RPC
|
||||
InteractorDaemonizer.killDaemon(function() {
|
||||
|
||||
// Dump PM2 processes
|
||||
CLI.dump(function(err) {
|
||||
printOut(cst.PREFIX_MSG + '--- dumped');
|
||||
|
||||
CLI.killDaemon(function(err) {
|
||||
printOut(cst.PREFIX_MSG + '--- killed');
|
||||
Satan.launchDaemon(function(err, child) {
|
||||
CLI.killDaemon(function() {
|
||||
Satan.disconnectRPC(function() {
|
||||
|
||||
printOut(cst.PREFIX_MSG + '--- resurrected');
|
||||
if (err) {
|
||||
printError(err);
|
||||
return cb ? cb({msg:err}) : exitCli(cst.ERROR_EXIT);
|
||||
}
|
||||
CLI.resurrect(function() {
|
||||
printOut(chalk.blue.bold('>>>>>>>>>> PM2 updated'));
|
||||
return cb ? cb(null, {success:true}) : speedList();
|
||||
Satan.launchDaemon(function(err, child) {
|
||||
Satan.launchRPC(function() {
|
||||
|
||||
CLI.resurrect(function() {
|
||||
printOut(chalk.blue.bold('>>>>>>>>>> PM2 updated'));
|
||||
return cb ? cb(null, {success:true}) : speedList();
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
return false;
|
||||
|
||||
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -680,7 +681,7 @@ CLI.dump = function(cb) {
|
||||
CLI.web = function(cb) {
|
||||
var filepath = p.resolve(p.dirname(module.filename), 'HttpInterface.js');
|
||||
|
||||
pm2.start(filepath, {
|
||||
CLI.start(filepath, {
|
||||
name : 'pm2-http-interface',
|
||||
execMode : 'fork_mode'
|
||||
}, function(err, proc) {
|
||||
@ -962,7 +963,7 @@ CLI._restartAll = function(cb) {
|
||||
return cb ? cb({msg:err}) : exitCli(cst.ERROR_EXIT);
|
||||
}
|
||||
if (list && list.length === 0) {
|
||||
printError('No process launched');
|
||||
printError(cst.PREFIX_MSG + 'No process launched');
|
||||
return cb ? cb({msg:err}) : exitCli(cst.ERROR_EXIT);
|
||||
}
|
||||
|
||||
@ -971,7 +972,7 @@ CLI._restartAll = function(cb) {
|
||||
var proc = processes[0];
|
||||
|
||||
if (proc == null) {
|
||||
printOut(cst.PREFIX_MSG + 'Process restarted...');
|
||||
printOut(cst.PREFIX_MSG + 'All processes has been restarted');
|
||||
return cb ? cb(null, processes) : setTimeout(speedList, 1000);
|
||||
}
|
||||
Satan.executeRemote('restartProcessId', proc.pm2_env.pm_id, function(err, res) {
|
||||
@ -1004,35 +1005,35 @@ CLI.delete = function(process_name, jsonVia, cb) {
|
||||
process_name = process_name.toString();
|
||||
}
|
||||
|
||||
printOut(cst.PREFIX_MSG + 'Deleting ' + process_name);
|
||||
|
||||
if (jsonVia == 'pipe')
|
||||
return CLI.actionFromJson('deleteProcessName', process_name, 'pipe');
|
||||
if (process_name.indexOf('.json') > 0)
|
||||
return CLI.actionFromJson('deleteProcessName', process_name, 'file');
|
||||
else if (process_name == 'all') {
|
||||
printOut(cst.PREFIX_MSG + 'Stopping and deleting all processes');
|
||||
Satan.executeRemote('deleteAll', {}, function(err, list) {
|
||||
if (err) {
|
||||
printError(err);
|
||||
printError(cst.PREFIX_MSG_ERR + err);
|
||||
return cb ? cb({msg:err}) : exitCli(cst.ERROR_EXIT);
|
||||
}
|
||||
printOut(cst.PREFIX_MSG + 'All processes has been stopped and deleted');
|
||||
return cb ? cb(null, list) : speedList();
|
||||
});
|
||||
}
|
||||
else if (!isNaN(parseInt(process_name))) {
|
||||
printOut('Stopping and deleting process by id : %s', process_name);
|
||||
Satan.executeRemote('deleteProcessId', process_name, function(err, list) {
|
||||
if (err) {
|
||||
printError(err);
|
||||
printError(cst.PREFIX_MSG_ERR + err);
|
||||
return cb ? cb({msg:err}) : exitCli(cst.ERROR_EXIT);
|
||||
}
|
||||
return cb ? cb(null, list) : speedList();
|
||||
});
|
||||
}
|
||||
else {
|
||||
printOut(cst.PREFIX_MSG + 'Stopping and deleting process by name %s', process_name);
|
||||
Satan.executeRemote('deleteProcessName', process_name, function(err, list) {
|
||||
if (err) {
|
||||
printError(err);
|
||||
printError(cst.PREFIX_MSG_ERR + err);
|
||||
return cb ? cb({msg:err}) : exitCli(cst.ERROR_EXIT);
|
||||
}
|
||||
return cb ? cb(null, list) : speedList();
|
||||
@ -1045,6 +1046,8 @@ CLI.stop = function(process_name, cb) {
|
||||
if (typeof(process_name) === 'number')
|
||||
process_name = process_name.toString();
|
||||
|
||||
printOut(cst.PREFIX_MSG + 'Stopping ' + process_name);
|
||||
|
||||
if (process_name == "-") {
|
||||
process.stdin.resume();
|
||||
process.stdin.setEncoding('utf8');
|
||||
@ -1059,7 +1062,6 @@ CLI.stop = function(process_name, cb) {
|
||||
else if (isNaN(parseInt(process_name))) {
|
||||
CLI._stopProcessName(process_name, cb);
|
||||
} else {
|
||||
printOut(cst.PREFIX_MSG + 'Stopping process by id ' + process_name);
|
||||
CLI._stopId(process_name, cb);
|
||||
}
|
||||
};
|
||||
@ -1072,9 +1074,10 @@ CLI.stop = function(process_name, cb) {
|
||||
CLI._stopAll = function(cb) {
|
||||
Satan.executeRemote('stopAll', {}, function(err, list) {
|
||||
if (err) {
|
||||
printError(err);
|
||||
printError(cst.PREFIX_MSG_ERR + err);
|
||||
return cb ? cb({msg:err}) : exitCli(cst.ERROR_EXIT);
|
||||
}
|
||||
printOut(cst.PREFIX_MSG + 'All processes stopped');
|
||||
return cb ? cb(null, list) : speedList();
|
||||
});
|
||||
};
|
||||
@ -1088,7 +1091,7 @@ CLI._stopAll = function(cb) {
|
||||
CLI._stopProcessName = function(name, cb) {
|
||||
Satan.executeRemote('stopProcessName', name, function(err, list) {
|
||||
if (err) {
|
||||
printError(err);
|
||||
printError(cst.PREFIX_MSG_ERR + err);
|
||||
return cb ? cb({msg:err}) : exitCli(cst.ERROR_EXIT);
|
||||
}
|
||||
printOut(cst.PREFIX_MSG + 'Stopping process by name ' + name);
|
||||
@ -1105,10 +1108,10 @@ CLI._stopProcessName = function(name, cb) {
|
||||
CLI._stopId = function(pm2_id, cb) {
|
||||
Satan.executeRemote('stopProcessId', pm2_id, function(err, list) {
|
||||
if (err) {
|
||||
printError(err);
|
||||
printError(cst.PREFIX_MSG_ERR + err);
|
||||
return cb ? cb({msg:err}) : exitCli(cst.ERROR_EXIT);
|
||||
}
|
||||
printOut(cst.PREFIX_MSG + ' Process stopped');
|
||||
printOut(cst.PREFIX_MSG + 'Process %d stopped', pm2_id);
|
||||
return cb ? cb(null, list) : speedList();
|
||||
});
|
||||
};
|
||||
@ -1186,6 +1189,7 @@ CLI.flush = function(cb) {
|
||||
fs.openSync(l.pm2_env.pm_out_log_path, 'w');
|
||||
fs.openSync(l.pm2_env.pm_err_log_path, 'w');
|
||||
});
|
||||
printOut(cst.PREFIX_MSG + 'Logs flushed');
|
||||
return cb ? cb(null, list) : exitCli(cst.SUCCESS_EXIT);
|
||||
});
|
||||
};
|
||||
@ -1357,7 +1361,7 @@ CLI.ilogs = function() {
|
||||
});
|
||||
} catch(e) {
|
||||
printOut('pm2-logs module is not installed');
|
||||
fallbackLogStream(id);
|
||||
fallbackLogStream();
|
||||
}
|
||||
};
|
||||
|
||||
@ -1368,35 +1372,17 @@ CLI.ilogs = function() {
|
||||
* @return
|
||||
*/
|
||||
CLI.killDaemon = function(cb) {
|
||||
printOut(cst.PREFIX_MSG + 'Killing pm2...');
|
||||
printOut(cst.PREFIX_MSG + 'Stopping PM2');
|
||||
|
||||
Satan.executeRemote('getMonitorData', {}, function(err, list) {
|
||||
if (err) {
|
||||
printError('Error retrieving process list: ' + err);
|
||||
return cb ? cb({msg : err}) : exitCli(cst.ERROR_EXIT);
|
||||
}
|
||||
printOut(cst.PREFIX_MSG + 'Stopping & Deleting all processes...');
|
||||
Satan.executeRemote('deleteAll', {}, function(err, list) {
|
||||
printOut(cst.PREFIX_MSG + 'All processes has been stopped and deleted');
|
||||
|
||||
async.eachLimit(list, 1, function(proc, next) {
|
||||
Satan.executeRemote('deleteProcessId', proc.pm2_env.pm_id, function(err, res) {
|
||||
if (err) {
|
||||
printError('Error : ' + err);
|
||||
}
|
||||
printOut(cst.PREFIX_MSG + 'Process %s stopped', proc.pm2_env.name);
|
||||
return next();
|
||||
});
|
||||
return false;
|
||||
}, function(err) {
|
||||
printOut(cst.PREFIX_MSG + 'All processes stopped');
|
||||
CLI.killInteract(function() {
|
||||
Satan.killDaemon(function(err, res) {
|
||||
if (err) {
|
||||
printError(err);
|
||||
if (cb) return cb({msg:err});
|
||||
else exitCli(cst.ERROR_EXIT);
|
||||
}
|
||||
console.info('PM2 stopped');
|
||||
return cb ? cb(null, res) : exitCli(cst.SUCCESS_EXIT);
|
||||
});
|
||||
InteractorDaemonizer.killDaemon(function() {
|
||||
Satan.killDaemon(function(err, res) {
|
||||
if (err) printError(err);
|
||||
printOut(cst.PREFIX_MSG + 'PM2 stopped');
|
||||
return cb ? cb(null) : exitCli(cst.SUCCESS_EXIT);
|
||||
});
|
||||
});
|
||||
return false;
|
||||
@ -1455,6 +1441,7 @@ function speedList() {
|
||||
* @return
|
||||
*/
|
||||
function getInteractInfo(cb) {
|
||||
debug('Getting interaction info');
|
||||
InteractorDaemonizer.ping(function(online) {
|
||||
if (!online) {
|
||||
return cb({msg : 'offline'});
|
||||
@ -1464,7 +1451,10 @@ function getInteractInfo(cb) {
|
||||
if (err) {
|
||||
return cb(err);
|
||||
}
|
||||
return cb(null, infos);
|
||||
InteractorDaemonizer.disconnectRPC(function() {
|
||||
return cb(null, infos);
|
||||
});
|
||||
return false;
|
||||
});
|
||||
});
|
||||
return false;
|
||||
|
||||
@ -58,6 +58,7 @@ UX.describeTable = function(process) {
|
||||
{ 'out log path' : pm2_env.pm_out_log_path },
|
||||
{ 'pid path' : pm2_env.pm_pid_path },
|
||||
{ 'mode' : pm2_env.exec_mode },
|
||||
{ 'node v8 arguments' : pm2_env.node_args },
|
||||
{ 'watch & reload' : pm2_env.watch ? chalk.green.bold('✔') : '✘' },
|
||||
{ 'interpreter' : pm2_env.exec_interpreter },
|
||||
{ 'restarts' : pm2_env.restart_time },
|
||||
|
||||
@ -14,6 +14,7 @@ var p = path;
|
||||
var Stringify = require('json-stringify-safe');
|
||||
|
||||
var Satan = require('./Satan.js');
|
||||
var InteractorDaemonizer = require('./Interactor/InteractorDaemonizer.js');
|
||||
/**
|
||||
* Common methods (used by CLI and God)
|
||||
*/
|
||||
@ -35,7 +36,12 @@ Common.resolveAppPaths = function(app, cwd, outputter) {
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
cwd = cwd || process.cwd();
|
||||
if (cwd && cwd[0] == '/')
|
||||
cwd = cwd;
|
||||
else if (cwd)
|
||||
cwd = p.resolve(process.cwd(), cwd);
|
||||
else
|
||||
cwd = process.cwd();
|
||||
|
||||
// Set current env by first adding the process environment and then extending/replacing it
|
||||
// with env specified on command-line or JSON file.
|
||||
@ -63,10 +69,24 @@ Common.resolveAppPaths = function(app, cwd, outputter) {
|
||||
app["pm_exec_path"] = path.resolve(cwd, app.script);
|
||||
delete app.script;
|
||||
|
||||
if (!app["name"]) {
|
||||
app["name"] = p.basename(app["pm_exec_path"]);
|
||||
if (app.node_args && !Array.isArray(app.node_args))
|
||||
app.node_args = app.node_args.split(' ');
|
||||
|
||||
if (!app.node_args)
|
||||
app.node_args = [];
|
||||
|
||||
if (app.max_memory_restart &&
|
||||
!isNaN(parseInt(app.max_memory_restart)) &&
|
||||
Array.isArray(app.node_args)) {
|
||||
app.node_args.push('--max-old-space-size=' + app.max_memory_restart);
|
||||
}
|
||||
|
||||
if (!app.name) {
|
||||
app.name = p.basename(app["pm_exec_path"]);
|
||||
}
|
||||
|
||||
var formated_app_name = app.name.replace(/[^a-zA-Z0-9\\.\\-]/g, '-');
|
||||
|
||||
if (fs.existsSync(app.pm_exec_path) == false) {
|
||||
return new Error('script not found : ' + app.pm_exec_path);
|
||||
}
|
||||
@ -74,10 +94,7 @@ Common.resolveAppPaths = function(app, cwd, outputter) {
|
||||
if (app.out_file)
|
||||
app["pm_out_log_path"] = path.resolve(cwd, app.out_file);
|
||||
else {
|
||||
if (!app.name) {
|
||||
return new Error('You havent specified log path, please specify at least a "name" field in the JSON');
|
||||
}
|
||||
app["pm_out_log_path"] = path.resolve(cst.DEFAULT_LOG_PATH, [app.name, '-out.log'].join(''));
|
||||
app["pm_out_log_path"] = path.resolve(cst.DEFAULT_LOG_PATH, [formated_app_name, '-out.log'].join(''));
|
||||
app.out_file = app["pm_out_log_path"];
|
||||
}
|
||||
delete app.out_file;
|
||||
@ -85,7 +102,7 @@ Common.resolveAppPaths = function(app, cwd, outputter) {
|
||||
if (app.error_file)
|
||||
app["pm_err_log_path"] = path.resolve(cwd, app.error_file);
|
||||
else {
|
||||
app["pm_err_log_path"] = path.resolve(cst.DEFAULT_LOG_PATH, [app.name, '-err.log'].join(''));
|
||||
app["pm_err_log_path"] = path.resolve(cst.DEFAULT_LOG_PATH, [formated_app_name, '-err.log'].join(''));
|
||||
app.error_file = app["pm_err_log_path"];
|
||||
}
|
||||
delete app.error_file;
|
||||
@ -93,7 +110,7 @@ Common.resolveAppPaths = function(app, cwd, outputter) {
|
||||
if (app.pid_file)
|
||||
app["pm_pid_path"] = path.resolve(cwd, app.pid_file);
|
||||
else {
|
||||
app["pm_pid_path"] = path.resolve(cst.DEFAULT_PID_PATH, [app.name, '.pid'].join(''));
|
||||
app["pm_pid_path"] = path.resolve(cst.DEFAULT_PID_PATH, [formated_app_name, '.pid'].join(''));
|
||||
app.pid_file = app["pm_pid_path"];
|
||||
}
|
||||
delete app.pid_file;
|
||||
@ -149,8 +166,10 @@ Common.validateApp = function(appConf, outputter) {
|
||||
* @return CallExpression
|
||||
*/
|
||||
Common.exitCli = function(code) {
|
||||
Satan.disconnectRPC(function() {
|
||||
return process.exit(code || 0);
|
||||
InteractorDaemonizer.disconnectRPC(function() {
|
||||
Satan.disconnectRPC(function() {
|
||||
return process.exit(code || 0);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@ -30,7 +30,7 @@ var God = module.exports = {
|
||||
next_id : 0,
|
||||
clusters_db : {},
|
||||
bus : new EventEmitter2({
|
||||
wildcard: true,
|
||||
wildcard: false,
|
||||
delimiter: ':',
|
||||
maxListeners: 1000
|
||||
})
|
||||
@ -169,7 +169,8 @@ God.executeApp = function executeApp(env, cb) {
|
||||
*/
|
||||
God.forkMode(env_copy, function forkMode(err, clu) {
|
||||
if (cb && err) return cb(err);
|
||||
|
||||
if (err) return false;
|
||||
|
||||
var old_env = God.clusters_db[clu.pm2_env.pm_id];
|
||||
if (old_env) old_env = null;
|
||||
|
||||
@ -203,6 +204,10 @@ God.executeApp = function executeApp(env, cb) {
|
||||
|
||||
if (old_env) {
|
||||
old_env = null;
|
||||
if (God.clusters_db[clu.pm2_env.pm_id].process._handle) {
|
||||
God.clusters_db[clu.pm2_env.pm_id].process._handle.owner = null;
|
||||
God.clusters_db[clu.pm2_env.pm_id].process._handle = null;
|
||||
}
|
||||
God.clusters_db[clu.pm2_env.pm_id] = null;
|
||||
}
|
||||
|
||||
|
||||
@ -16,6 +16,9 @@ var pkg = require('../../package.json');
|
||||
var pidusage = require('pidusage');
|
||||
var Common = require('../Common');
|
||||
|
||||
|
||||
var debug = require('debug')('pm2:ActionMethod');
|
||||
|
||||
/**
|
||||
* Description
|
||||
* @method exports
|
||||
@ -450,7 +453,9 @@ module.exports = function(God) {
|
||||
if (processes && processes.length === 0)
|
||||
return cb(God.logAndGenerateError('No processes launched'), {});
|
||||
|
||||
debug('Deleting all processes');
|
||||
async.eachLimit(processes, cst.CONCURRENT_ACTIONS, function(proc, next) {
|
||||
debug('Deleting process %s', proc.pm2_env.pm_id);
|
||||
God.deleteProcessId(proc.pm2_env.pm_id, function() {
|
||||
return next();
|
||||
});
|
||||
@ -472,15 +477,17 @@ module.exports = function(God) {
|
||||
* @return
|
||||
*/
|
||||
God.killMe = function(env, cb) {
|
||||
console.log('pm2 has been killed by command line');
|
||||
God.bus.emit('pm2:kill', {
|
||||
status : 'killed',
|
||||
msg : 'pm2 has been killed via CLI'
|
||||
});
|
||||
setTimeout(function() {
|
||||
cb(null, {msg : 'pm2 killed'});
|
||||
process.exit(cst.SUCCESS_EXIT);
|
||||
}, 800);
|
||||
console.log('PM2 has been killed by user command');
|
||||
God.bus.emit('pm2:kill', {
|
||||
status : 'killed',
|
||||
msg : 'pm2 has been killed via CLI'
|
||||
});
|
||||
|
||||
cb(null, {msg : 'pm2 killed'});
|
||||
|
||||
setTimeout(function() {
|
||||
process.exit(cst.SUCCESS_EXIT);
|
||||
}, 5);
|
||||
};
|
||||
|
||||
|
||||
@ -558,10 +565,16 @@ module.exports = function(God) {
|
||||
|
||||
processIds.forEach(function (id) {
|
||||
var cluster = God.clusters_db[id];
|
||||
|
||||
if (cluster.pm2_env.exec_mode == 'cluster_mode')
|
||||
cluster.send({type:'log:reload'});
|
||||
else
|
||||
cluster._reloadLogs(function() {
|
||||
else // Fork mode
|
||||
cluster._reloadLogs(function(err) {
|
||||
if (err) {
|
||||
God.logAndGenerateError(err);
|
||||
return cb(new Error(err));
|
||||
};
|
||||
return false;
|
||||
});
|
||||
console.log('Reloading logs for process id %d', id);
|
||||
});
|
||||
|
||||
@ -11,6 +11,7 @@ var fs = require('fs');
|
||||
var cst = require('../../constants.js');
|
||||
var util = require('util');
|
||||
var Common = require('../Common');
|
||||
|
||||
/**
|
||||
* Description
|
||||
* @method exports
|
||||
@ -36,8 +37,8 @@ module.exports = function ClusterMode(God) {
|
||||
|
||||
console.log('Entering in node wrap logic (cluster_mode) for script %s', env_copy.pm_exec_path);
|
||||
|
||||
if (env_copy.nodeArgs && Array.isArray(env_copy.nodeArgs)) {
|
||||
cluster.settings.execArgv = env_copy.nodeArgs;
|
||||
if (env_copy.node_args && Array.isArray(env_copy.node_args)) {
|
||||
cluster.settings.execArgv = env_copy.node_args;
|
||||
}
|
||||
|
||||
try {
|
||||
@ -71,8 +72,8 @@ module.exports = function ClusterMode(God) {
|
||||
}
|
||||
});
|
||||
|
||||
// Avoid circular dependency
|
||||
delete clu.process._handle.owner;
|
||||
|
||||
//delete clu.process._handle.owner;
|
||||
|
||||
return cb(null, clu);
|
||||
return false;
|
||||
|
||||
@ -28,7 +28,8 @@ module.exports = function ForkMode(God) {
|
||||
* @return
|
||||
*/
|
||||
God.forkMode = function forkMode(pm2_env, cb) {
|
||||
var command, args;
|
||||
var command = '';
|
||||
var args = [];
|
||||
|
||||
log('Entering in fork mode');
|
||||
var spawn = require('child_process').spawn;
|
||||
@ -38,16 +39,26 @@ module.exports = function ForkMode(God) {
|
||||
|
||||
if (interpreter !== 'none') {
|
||||
command = interpreter;
|
||||
args = [pm2_env.pm_exec_path];
|
||||
|
||||
if (pm2_env.node_args && Array.isArray(pm2_env.node_args)) {
|
||||
args = args.concat(pm2_env.node_args);
|
||||
}
|
||||
|
||||
if (process.env.PM2_NODE_OPTIONS) {
|
||||
args = args.concat(process.env.PM2_NODE_OPTIONS.split(' '));
|
||||
}
|
||||
|
||||
args.push(pm2_env.pm_exec_path);
|
||||
}
|
||||
else {
|
||||
command = pm2_env.pm_exec_path;
|
||||
args = [ ];
|
||||
}
|
||||
|
||||
// Concat args if present
|
||||
if (pm2_env.args)
|
||||
if (pm2_env.args) {
|
||||
args = args.concat(eval((pm2_env.args)));
|
||||
}
|
||||
|
||||
|
||||
var stdout, stderr;
|
||||
var outFile = pm2_env.pm_out_log_path;
|
||||
@ -75,15 +86,31 @@ module.exports = function ForkMode(God) {
|
||||
function startLogging(cb) {
|
||||
stdout = fs.createWriteStream(outFile, { flags : 'a' });
|
||||
|
||||
stdout.on('error', function(e) {
|
||||
God.logAndGenerateError(e);
|
||||
return cb(e);
|
||||
});
|
||||
|
||||
stdout.on('open', function() {
|
||||
stderr = fs.createWriteStream(errFile, { flags : 'a' });
|
||||
|
||||
stderr.on('error', function(e) {
|
||||
God.logAndGenerateError(e);
|
||||
return cb(e);
|
||||
});
|
||||
|
||||
stderr.on('open', function() {
|
||||
return cb();
|
||||
return cb(null);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
startLogging(function() {
|
||||
startLogging(function(err) {
|
||||
if (err) {
|
||||
God.logAndGenerateError(err);
|
||||
return cb(err);
|
||||
};
|
||||
|
||||
getugid(function(e, uid, gid){
|
||||
if(e){
|
||||
God.logAndGenerateError(e);
|
||||
|
||||
@ -60,6 +60,7 @@ module.exports = function(God) {
|
||||
});
|
||||
}
|
||||
}
|
||||
db = null;
|
||||
return arr;
|
||||
};
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
|
||||
var fs = require('fs');
|
||||
var ipm2 = require('../../pm2-interface');
|
||||
var rpc = require('axon-rpc');
|
||||
var ipm2 = require('pm2-interface');
|
||||
var rpc = require('pm2-axon-rpc');
|
||||
var axon = require('axon');
|
||||
var debug = require('debug')('interface:driver'); // Interface
|
||||
var chalk = require('chalk');
|
||||
@ -22,15 +22,15 @@ var Daemon = {
|
||||
activateRPC : function() {
|
||||
console.log('Launching Interactor exposure');
|
||||
|
||||
var self = this;
|
||||
var rep = axon.socket('rep');
|
||||
var self = this;
|
||||
var rep = axon.socket('rep');
|
||||
var daemon_server = new rpc.Server(rep);
|
||||
var sock = rep.bind(cst.INTERACTOR_RPC_PORT);
|
||||
|
||||
daemon_server.expose({
|
||||
kill : function(cb) {
|
||||
console.log('Killing interactor');
|
||||
cb();
|
||||
cb(null);
|
||||
setTimeout(function() { process.exit(cst.SUCCESS_EXIT) }, 50);
|
||||
},
|
||||
getInfos : function(cb) {
|
||||
@ -89,9 +89,9 @@ var Daemon = {
|
||||
self.activateRPC();
|
||||
self.opts.ipm2 = self.connectToPM2();
|
||||
|
||||
WatchDog.start({
|
||||
conf : self.opts
|
||||
});
|
||||
// WatchDog.start({
|
||||
// conf : self.opts
|
||||
// });
|
||||
|
||||
// Then connect to external services
|
||||
if (cst.DEBUG) {
|
||||
|
||||
@ -5,9 +5,9 @@ var fs = require('fs');
|
||||
var cst = require('../../constants.js');
|
||||
var path = require('path');
|
||||
var util = require('util');
|
||||
var rpc = require('axon-rpc');
|
||||
var rpc = require('pm2-axon-rpc');
|
||||
var Common = require('../Common');
|
||||
var debug = require('debug')('interface:daemon');
|
||||
var debug = require('debug')('pm2:interface:daemon');
|
||||
var axon = require('axon');
|
||||
var chalk = require('chalk');
|
||||
|
||||
@ -25,7 +25,7 @@ InteractorDaemonizer.ping = function(cb) {
|
||||
var req = axon.socket('req');
|
||||
var client = new rpc.Client(req);
|
||||
|
||||
debug('Trying to connect to Interactor daemon');
|
||||
debug('[PING INTERACTOR] Trying to connect to Interactor daemon');
|
||||
client.sock.once('reconnect attempt', function() {
|
||||
client.sock.close();
|
||||
debug('Interactor Daemon not launched');
|
||||
@ -39,6 +39,28 @@ InteractorDaemonizer.ping = function(cb) {
|
||||
req.connect(cst.INTERACTOR_RPC_PORT);
|
||||
};
|
||||
|
||||
InteractorDaemonizer.killDaemon = function(cb) {
|
||||
|
||||
debug('Killing interactor #1 ping');
|
||||
InteractorDaemonizer.ping(function(online) {
|
||||
debug('Interactor online', online);
|
||||
if (!online) {
|
||||
if (!cb) Common.printError('Interactor not launched');
|
||||
return cb ? cb({msg:'Interactor not launched'}) : Common.exitCli(cst.SUCCESS_EXIT);
|
||||
}
|
||||
|
||||
InteractorDaemonizer.launchRPC(function() {
|
||||
InteractorDaemonizer.rpc.kill(function(err) {
|
||||
if (err) Common.printError(err);
|
||||
setTimeout(function() {
|
||||
InteractorDaemonizer.disconnectRPC(cb);
|
||||
}, 100);
|
||||
});
|
||||
});
|
||||
return false;
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Description
|
||||
* @method launchRPC
|
||||
@ -84,9 +106,11 @@ InteractorDaemonizer.launchRPC = function(cb) {
|
||||
});
|
||||
};
|
||||
|
||||
generateMethods(function() {
|
||||
debug('Methods generated');
|
||||
cb();
|
||||
this.client.sock.once('connect', function() {
|
||||
generateMethods(function() {
|
||||
debug('Methods generated');
|
||||
cb();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
@ -101,19 +125,22 @@ InteractorDaemonizer.launchRPC = function(cb) {
|
||||
*/
|
||||
function launchOrAttach(infos, cb) {
|
||||
InteractorDaemonizer.ping(function(online) {
|
||||
if (online && !process.env.PM2_FORCE) {
|
||||
if (online) {
|
||||
debug('Interactor online, restarting it...');
|
||||
InteractorDaemonizer.launchRPC(function() {
|
||||
InteractorDaemonizer.rpc.kill(function(err) {
|
||||
InteractorDaemonizer.daemonize(infos, function() {
|
||||
return cb(true);
|
||||
InteractorDaemonizer.rpc.kill(function(err) {
|
||||
InteractorDaemonizer.daemonize(infos, function() {
|
||||
return cb(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
else
|
||||
else {
|
||||
debug('Interactor offline, launching it...');
|
||||
InteractorDaemonizer.daemonize(infos, function() {
|
||||
return cb(true);
|
||||
});
|
||||
}
|
||||
return false;
|
||||
});
|
||||
};
|
||||
@ -130,8 +157,6 @@ function launchOrAttach(infos, cb) {
|
||||
InteractorDaemonizer.daemonize = function(infos, cb) {
|
||||
var InteractorJS = path.resolve(path.dirname(module.filename), 'Daemon.js');
|
||||
|
||||
console.log('Launching interactor');
|
||||
|
||||
var out = fs.openSync(cst.INTERACTOR_LOG_FILE_PATH, 'a'),
|
||||
err = fs.openSync(cst.INTERACTOR_LOG_FILE_PATH, 'a');
|
||||
|
||||
@ -152,12 +177,19 @@ InteractorDaemonizer.daemonize = function(infos, cb) {
|
||||
|
||||
child.unref();
|
||||
|
||||
child.on('exit', function(msg) {
|
||||
console.error('Error when launching Interactor, please check the agent logs');
|
||||
return setTimeout(function() {cb(null, child)}, 100);
|
||||
});
|
||||
|
||||
debug('Waiting for message');
|
||||
child.once('message', function(msg) {
|
||||
debug('Interactor ready');
|
||||
process.emit('interactor:daemon:ready');
|
||||
console.log(msg);
|
||||
//console.log(msg);
|
||||
|
||||
child.disconnect();
|
||||
console.log(chalk.cyan.bold('[Keymetrics.io]') + ' Log: %s | Conf: %s | PID: %s', cst.INTERACTOR_LOG_FILE_PATH,
|
||||
console.log(chalk.cyan('[Keymetrics.io]') + ' Launched - Log: %s | Conf: %s | PID: %s', cst.INTERACTOR_LOG_FILE_PATH,
|
||||
cst.INTERACTION_CONF,
|
||||
cst.INTERACTOR_PID_PATH);
|
||||
return setTimeout(function() {cb(null, child)}, 100);
|
||||
@ -262,6 +294,20 @@ InteractorDaemonizer.getSetKeys = function(secret_key, public_key, machine_name,
|
||||
});
|
||||
};
|
||||
|
||||
InteractorDaemonizer.disconnectRPC = function(cb) {
|
||||
process.nextTick(function() {
|
||||
if (!InteractorDaemonizer.client_sock ||
|
||||
!InteractorDaemonizer.client_sock.close)
|
||||
return cb(null, {
|
||||
success : false,
|
||||
msg : 'RPC connection to Interactor Daemon is not launched'
|
||||
});
|
||||
debug('Closing RPC INTERACTOR');
|
||||
InteractorDaemonizer.client_sock.close();
|
||||
return cb ? cb(null, {success:true}) : false;
|
||||
});
|
||||
};
|
||||
|
||||
InteractorDaemonizer.launchAndInteract = function(opts, cb) {
|
||||
if (process.env.PM2_AGENT_ONLINE) {
|
||||
return process.nextTick(cb);
|
||||
@ -272,14 +318,12 @@ InteractorDaemonizer.launchAndInteract = function(opts, cb) {
|
||||
public_key : opts.public_key || null,
|
||||
machine_name : opts.machine_name || null
|
||||
}, function(err, data) {
|
||||
if (err)
|
||||
if (err) {
|
||||
debug('Cant get set keys');
|
||||
return cb ? cb({msg:'Error when getting / setting keys'}) : Common.exitCli(cst.ERROR_EXIT);
|
||||
|
||||
}
|
||||
|
||||
launchOrAttach(data, function(status) {
|
||||
|
||||
//Common.printOut(chalk.cyan.bold('[Keymetrics.io]') + ' Interactor successfully launched');
|
||||
|
||||
return cb ? cb(null, {success:true}) : Common.exitCli(cst.SUCCESS_EXIT);
|
||||
});
|
||||
return false;
|
||||
|
||||
@ -2,7 +2,6 @@
|
||||
// Display a file in streaming
|
||||
//
|
||||
var fs = require('fs');
|
||||
var dateFormat = require('./dateformat.js');
|
||||
|
||||
var colors = [
|
||||
'\x1B[34m', // blue
|
||||
@ -89,9 +88,6 @@ function print_data(odb, title, data) {
|
||||
|
||||
lines.forEach(function(l) {
|
||||
if (l)
|
||||
console.log(odb.color + '[%s %s]\x1B[39m %s',
|
||||
title,
|
||||
dateFormat(Date.now(), "isoDateTime"),
|
||||
l);
|
||||
console.log(odb.color + '[%s]\x1B[39m %s', title, l);
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
@ -109,7 +109,7 @@ Monit.refresh = function(processes) {
|
||||
|
||||
//this is to avoid a print issue when the process is restarted for example
|
||||
//we might also check for the pid but restarted|restarting will be rendered bad
|
||||
if(proc.pm2_env.status !== this.bars[proc.pm_id].status) {
|
||||
if(this.bars[proc.pm_id] && proc.pm2_env.status !== this.bars[proc.pm_id].status) {
|
||||
debug('bars for %s does not exists', proc.pm_id);
|
||||
this.addProcesses(processes);
|
||||
break;
|
||||
|
||||
@ -2,12 +2,14 @@
|
||||
// Child wrapper. Redirect output to files, assign pid & co.
|
||||
// by Strzelewicz Alexandre
|
||||
|
||||
// Rename process
|
||||
if (process.env.name != null)
|
||||
process.title = 'pm2: ' + process.env.name;
|
||||
|
||||
var fs = require('fs');
|
||||
var p = require('path');
|
||||
var cst = require('../constants');
|
||||
|
||||
require('coffee-script/register');
|
||||
|
||||
/**
|
||||
* Main entrance to wrap the desired code
|
||||
*/
|
||||
@ -32,9 +34,6 @@ require('coffee-script/register');
|
||||
if (process.env.args != null)
|
||||
process.argv = process.argv.concat(eval(process.env.args));
|
||||
|
||||
// Rename process
|
||||
if (process.env.name != null)
|
||||
process.title = 'pm2: ' + process.env.name;
|
||||
|
||||
exec(script, outFile, errFile);
|
||||
|
||||
@ -77,11 +76,12 @@ function cronize(cron_pattern) {
|
||||
* @return
|
||||
*/
|
||||
function exec(script, outFile, errFile) {
|
||||
// Change dir to fix process.cwd
|
||||
process.chdir(process.env.PWD || p.dirname(script));
|
||||
|
||||
var stderr, stdout;
|
||||
|
||||
if(p.extname(script) == '.coffee') {
|
||||
require('coffee-script/register');
|
||||
}
|
||||
|
||||
process.on('message', function (msg) {
|
||||
if (msg.type === 'log:reload') {
|
||||
stdout.end();
|
||||
@ -193,6 +193,10 @@ function exec(script, outFile, errFile) {
|
||||
process.setuid(process.env.run_as_user);
|
||||
}
|
||||
|
||||
// Change dir to fix process.cwd
|
||||
process.chdir(process.env.pm_cwd || process.env.PWD || p.dirname(script));
|
||||
|
||||
|
||||
// Get the script & exec
|
||||
require(script);
|
||||
|
||||
|
||||
36
lib/Satan.js
36
lib/Satan.js
@ -9,7 +9,7 @@
|
||||
* Dependencies
|
||||
*/
|
||||
|
||||
var rpc = require('axon-rpc');
|
||||
var rpc = require('pm2-axon-rpc');
|
||||
var axon = require('axon');
|
||||
var rep = axon.socket('rep');
|
||||
var req = axon.socket('req');
|
||||
@ -39,12 +39,13 @@ var Satan = module.exports = {};
|
||||
* @callback cb
|
||||
*/
|
||||
Satan.start = function(noDaemonMode, cb) {
|
||||
if (typeof(noDaemonMode) == "function") {
|
||||
if (typeof(noDaemonMode) == "function") {
|
||||
cb = noDaemonMode;
|
||||
noDaemonMode = false;
|
||||
}
|
||||
|
||||
Satan.pingDaemon(function(ab) {
|
||||
debug('PM2 alive: ' + ab);
|
||||
// If Daemon not alive
|
||||
if (ab == false) {
|
||||
if (noDaemonMode) {
|
||||
@ -195,10 +196,9 @@ Satan.remoteWrapper = function() {
|
||||
});
|
||||
}
|
||||
|
||||
if (exists === true)
|
||||
return true;
|
||||
else
|
||||
return God.clusters_db[pm2_env.pm_id].pm2_env.axm_actions.push(msg.data.data);
|
||||
if (exists === false)
|
||||
God.clusters_db[pm2_env.pm_id].pm2_env.axm_actions.push(msg.data.data);
|
||||
return false;
|
||||
});
|
||||
|
||||
/**
|
||||
@ -254,11 +254,13 @@ Satan.launchDaemon = function launchDaemon(cb) {
|
||||
|
||||
child.once('message', function(msg) {
|
||||
debug('PM2 Daemon launched', msg);
|
||||
child.disconnect();
|
||||
|
||||
InteractorDaemonizer.launchAndInteract({}, function(err, data) {
|
||||
process.emit('satan:daemon:ready');
|
||||
child.disconnect();
|
||||
return setTimeout(function() {cb(null, child)}, 150);
|
||||
});
|
||||
|
||||
});
|
||||
};
|
||||
|
||||
@ -273,7 +275,7 @@ Satan.pingDaemon = function pingDaemon(cb) {
|
||||
var req = axon.socket('req');
|
||||
var client = new rpc.Client(req);
|
||||
|
||||
debug('Trying to connect to server');
|
||||
debug('[PING PM2] Trying to connect to server');
|
||||
client.sock.once('reconnect attempt', function() {
|
||||
client.sock.close();
|
||||
debug('Daemon not launched');
|
||||
@ -304,7 +306,7 @@ Satan.launchRPC = function launchRPC(cb) {
|
||||
return cb ? cb(null) : false;
|
||||
});
|
||||
|
||||
req.connect(cst.DAEMON_RPC_PORT, cst.DAEMON_BIND_HOST);
|
||||
this.client_sock = req.connect(cst.DAEMON_RPC_PORT, cst.DAEMON_BIND_HOST);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -312,12 +314,13 @@ Satan.launchRPC = function launchRPC(cb) {
|
||||
* @callback cb
|
||||
*/
|
||||
Satan.disconnectRPC = function disconnectRPC(cb) {
|
||||
debug('Disconnecting RPC');
|
||||
process.nextTick(function() {
|
||||
if (!Satan.client || !Satan.client.sock || !Satan.client.sock.close)
|
||||
if (!Satan.client_sock || !Satan.client_sock.close)
|
||||
return cb({
|
||||
msg : 'RPC connection to PM2 is not launched'
|
||||
});
|
||||
Satan.client.sock.close();
|
||||
Satan.client_sock.close();
|
||||
return cb ? cb(null, {success:true}) : false;
|
||||
});
|
||||
};
|
||||
@ -356,6 +359,7 @@ Satan.executeRemote = function executeRemote(method, env, fn) {
|
||||
console.error('Did you forgot to call pm2.connect(function() { }) before interacting with PM2 ?');
|
||||
return process.exit(0);
|
||||
}
|
||||
debug('Calling daemont method pm2:%s', method);
|
||||
Satan.client.call(method, env, fn);
|
||||
};
|
||||
|
||||
@ -366,7 +370,15 @@ Satan.executeRemote = function executeRemote(method, env, fn) {
|
||||
* @return
|
||||
*/
|
||||
Satan.killDaemon = function killDaemon(fn) {
|
||||
Satan.executeRemote('killMe', {}, fn);
|
||||
debug('Killing interactor');
|
||||
Satan.executeRemote('killMe', {}, function() {
|
||||
Satan.disconnectRPC(function() {
|
||||
setTimeout(function() {
|
||||
return fn ? fn(null, {success:true}) : false;
|
||||
}, 20);
|
||||
return false;
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@ -1,189 +0,0 @@
|
||||
/*
|
||||
* Date Format 1.2.3
|
||||
* (c) 2007-2009 Steven Levithan <stevenlevithan.com>
|
||||
* MIT license
|
||||
*
|
||||
* Includes enhancements by Scott Trenda <scott.trenda.net>
|
||||
* and Kris Kowal <cixar.com/~kris.kowal/>
|
||||
*
|
||||
* Accepts a date, a mask, or a date and a mask.
|
||||
* Returns a formatted version of the given date.
|
||||
* The date defaults to the current date/time.
|
||||
* The mask defaults to dateFormat.masks.default.
|
||||
*/
|
||||
|
||||
var dateFormat = function () {
|
||||
/**
|
||||
* Description
|
||||
* @method getDayOfWeek
|
||||
* @param {} date
|
||||
* @return dow
|
||||
*/
|
||||
var token = /d{1,4}|m{1,4}|yy(?:yy)?|([HhMsTt])\1?|[LloSZWN]|"[^"]*"|'[^']*'/g,
|
||||
timezone = /\b(?:[PMCEA][SDP]T|(?:Pacific|Mountain|Central|Eastern|Atlantic) (?:Standard|Daylight|Prevailing) Time|(?:GMT|UTC)(?:[-+]\d{4})?)\b/g,
|
||||
timezoneClip = /[^-+\dA-Z]/g,
|
||||
pad = function (val, len) {
|
||||
val = String(val);
|
||||
len = len || 2;
|
||||
while (val.length < len) val = "0" + val;
|
||||
return val;
|
||||
},
|
||||
/**
|
||||
* Get the ISO 8601 week number
|
||||
* Based on comments from
|
||||
* http://techblog.procurios.nl/k/n618/news/view/33796/14863/Calculate-ISO-8601-week-and-year-in-javascript.html
|
||||
*/
|
||||
getWeek = function (date) {
|
||||
// Remove time components of date
|
||||
var targetThursday = new Date(date.getFullYear(), date.getMonth(), date.getDate());
|
||||
|
||||
// Change date to Thursday same week
|
||||
targetThursday.setDate(targetThursday.getDate() - ((targetThursday.getDay() + 6) % 7) + 3);
|
||||
|
||||
// Take January 4th as it is always in week 1 (see ISO 8601)
|
||||
var firstThursday = new Date(targetThursday.getFullYear(), 0, 4);
|
||||
|
||||
// Change date to Thursday same week
|
||||
firstThursday.setDate(firstThursday.getDate() - ((firstThursday.getDay() + 6) % 7) + 3);
|
||||
|
||||
// Check if daylight-saving-time-switch occured and correct for it
|
||||
var ds = targetThursday.getTimezoneOffset() - firstThursday.getTimezoneOffset();
|
||||
targetThursday.setHours(targetThursday.getHours() - ds);
|
||||
|
||||
// Number of weeks between target Thursday and first Thursday
|
||||
var weekDiff = (targetThursday - firstThursday) / (86400000*7);
|
||||
return 1 + weekDiff;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get ISO-8601 numeric representation of the day of the week
|
||||
* 1 (for Monday) through 7 (for Sunday)
|
||||
*/
|
||||
|
||||
getDayOfWeek = function(date){
|
||||
var dow = date.getDay();
|
||||
if(dow === 0) dow = 7;
|
||||
return dow;
|
||||
};
|
||||
|
||||
// Regexes and supporting functions are cached through closure
|
||||
return function (date, mask, utc, gmt) {
|
||||
var dF = dateFormat;
|
||||
|
||||
// You can't provide utc if you skip other args (use the "UTC:" mask prefix)
|
||||
if (arguments.length == 1 && Object.prototype.toString.call(date) == "[object String]" && !/\d/.test(date)) {
|
||||
mask = date;
|
||||
date = undefined;
|
||||
}
|
||||
|
||||
date = date || new Date;
|
||||
|
||||
if(!(date instanceof Date)) {
|
||||
date = new Date(date);
|
||||
}
|
||||
|
||||
if (isNaN(date)) {
|
||||
throw TypeError("Invalid date");
|
||||
}
|
||||
|
||||
mask = String(dF.masks[mask] || mask || dF.masks["default"]);
|
||||
|
||||
// Allow setting the utc/gmt argument via the mask
|
||||
var maskSlice = mask.slice(0, 4);
|
||||
if (maskSlice == "UTC:" || maskSlice == "GMT:") {
|
||||
mask = mask.slice(4);
|
||||
utc = true;
|
||||
if (maskSlice == "GMT:") {
|
||||
gmt = true;
|
||||
}
|
||||
}
|
||||
|
||||
var _ = utc ? "getUTC" : "get",
|
||||
d = date[_ + "Date"](),
|
||||
D = date[_ + "Day"](),
|
||||
m = date[_ + "Month"](),
|
||||
y = date[_ + "FullYear"](),
|
||||
H = date[_ + "Hours"](),
|
||||
M = date[_ + "Minutes"](),
|
||||
s = date[_ + "Seconds"](),
|
||||
L = date[_ + "Milliseconds"](),
|
||||
o = utc ? 0 : date.getTimezoneOffset(),
|
||||
W = getWeek(date),
|
||||
N = getDayOfWeek(date),
|
||||
flags = {
|
||||
d: d,
|
||||
dd: pad(d),
|
||||
ddd: dF.i18n.dayNames[D],
|
||||
dddd: dF.i18n.dayNames[D + 7],
|
||||
m: m + 1,
|
||||
mm: pad(m + 1),
|
||||
mmm: dF.i18n.monthNames[m],
|
||||
mmmm: dF.i18n.monthNames[m + 12],
|
||||
yy: String(y).slice(2),
|
||||
yyyy: y,
|
||||
h: H % 12 || 12,
|
||||
hh: pad(H % 12 || 12),
|
||||
H: H,
|
||||
HH: pad(H),
|
||||
M: M,
|
||||
MM: pad(M),
|
||||
s: s,
|
||||
ss: pad(s),
|
||||
l: pad(L, 3),
|
||||
L: pad(L > 99 ? Math.round(L / 10) : L),
|
||||
t: H < 12 ? "a" : "p",
|
||||
tt: H < 12 ? "am" : "pm",
|
||||
T: H < 12 ? "A" : "P",
|
||||
TT: H < 12 ? "AM" : "PM",
|
||||
Z: gmt ? "GMT" : utc ? "UTC" : (String(date).match(timezone) || [""]).pop().replace(timezoneClip, ""),
|
||||
o: (o > 0 ? "-" : "+") + pad(Math.floor(Math.abs(o) / 60) * 100 + Math.abs(o) % 60, 4),
|
||||
S: ["th", "st", "nd", "rd"][d % 10 > 3 ? 0 : (d % 100 - d % 10 != 10) * d % 10],
|
||||
W: W,
|
||||
N: N
|
||||
};
|
||||
|
||||
return mask.replace(token, function ($0) {
|
||||
return $0 in flags ? flags[$0] : $0.slice(1, $0.length - 1);
|
||||
});
|
||||
};
|
||||
}();
|
||||
|
||||
// Some common format strings
|
||||
dateFormat.masks = {
|
||||
"default": "ddd mmm dd yyyy HH:MM:ss",
|
||||
shortDate: "m/d/yy",
|
||||
mediumDate: "mmm d, yyyy",
|
||||
longDate: "mmmm d, yyyy",
|
||||
fullDate: "dddd, mmmm d, yyyy",
|
||||
shortTime: "h:MM TT",
|
||||
mediumTime: "h:MM:ss TT",
|
||||
longTime: "h:MM:ss TT Z",
|
||||
isoDate: "yyyy-mm-dd",
|
||||
isoTime: "HH:MM:ss",
|
||||
isoDateTime: "yyyy-mm-dd'T'HH:MM:ss",
|
||||
isoUtcDateTime: "UTC:yyyy-mm-dd'T'HH:MM:ss'Z'",
|
||||
expiresHeaderFormat: "ddd, dd mmm yyyy HH:MM:ss Z"
|
||||
};
|
||||
|
||||
// Internationalization strings
|
||||
dateFormat.i18n = {
|
||||
dayNames: [
|
||||
"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat",
|
||||
"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"
|
||||
],
|
||||
monthNames: [
|
||||
"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
|
||||
"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"
|
||||
]
|
||||
};
|
||||
|
||||
/*
|
||||
// For convenience...
|
||||
Date.prototype.format = function (mask, utc) {
|
||||
return dateFormat(this, mask, utc);
|
||||
};
|
||||
*/
|
||||
|
||||
if (typeof exports !== "undefined") {
|
||||
module.exports = dateFormat;
|
||||
}
|
||||
16
package.json
16
package.json
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "pm2",
|
||||
"preferGlobal": "true",
|
||||
"version": "0.10.0",
|
||||
"version": "0.10.1",
|
||||
"os": [
|
||||
"!win32"
|
||||
],
|
||||
@ -70,7 +70,7 @@
|
||||
"description": "Modern CLI process manager for Node apps with a builtin load-balancer. Perfectly designed for microservices architecture.",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "bash test/main.sh && NODE_ENV=test mocha"
|
||||
"test": "NODE_ENV=test bash test/index.sh && bash test/main.sh"
|
||||
},
|
||||
"keywords": [
|
||||
"cli",
|
||||
@ -119,7 +119,7 @@
|
||||
"async": "~0.9.0",
|
||||
"axm": "~0.1.7",
|
||||
"axon": "~2.0.0",
|
||||
"chalk": "^0.4.0",
|
||||
"chalk": "~0.4.0",
|
||||
"chokidar": "~0.8.2",
|
||||
"cli-table": "~0.3.0",
|
||||
"coffee-script": "~1.7.1",
|
||||
@ -132,20 +132,22 @@
|
||||
"nssocket": "~0.5.1",
|
||||
"punt" : "~2.2.0",
|
||||
"pidusage": "~0.0.7",
|
||||
"axon-rpc": "~0.0.3",
|
||||
"pm2-axon-rpc": "~0.2.0",
|
||||
"pm2-deploy": "~0.0.4",
|
||||
"pm2-interface" : "~1.1.0",
|
||||
"pm2-multimeter": "~0.1.2",
|
||||
"moment" : "~2.7.0",
|
||||
"uid-number": "0.0.5"
|
||||
"uid-number": "~0.0.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"mocha": "^1.20.1",
|
||||
"should": "^4.0.0",
|
||||
"better-assert": "^1.0.0",
|
||||
"promise-spawner": "0.0.3"
|
||||
"promise-spawner": "^0.0.3"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"pm2-logs" : "latest"
|
||||
"pm2-logs" : "latest",
|
||||
"ikst" : "~0.1.2"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/Unitech/pm2/issues"
|
||||
|
||||
3
pm2-interface/.gitignore
vendored
3
pm2-interface/.gitignore
vendored
@ -1,3 +0,0 @@
|
||||
node_modules
|
||||
*.log
|
||||
*.pid
|
||||
@ -1,13 +0,0 @@
|
||||
Copyright [2013] [Strzelewicz Alexandre]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
@ -1,147 +0,0 @@
|
||||
# pm2-interface (for pm2 version >= 0.6.0)
|
||||
|
||||
pm2-interface permits you to interact with [PM2](https://github.com/Unitech/pm2) the process manager for NodeJS.
|
||||
|
||||
You can **control all exposed methods** by the pm2 deamon [God](https://github.com/Unitech/pm2/blob/master/lib/God.js) and also **receive real time notifications** for example for a process who got an unexpectedException, who's starting/stopping.
|
||||
|
||||
## RPC methods
|
||||
|
||||
- `ipm2.rpc.prepareJson(json_app, cwd, fn)` send a JSON configuration to start app(s) in the cwd folder
|
||||
- `ipm2.rpc.getMonitorData({}, fn)` receive all related informations about supervised process (cpu/ram/pid...)
|
||||
- `ipm2.rpc.getSystemData({}, fn)` receive all data about process managed by pm2 and computer resources usage
|
||||
- `ipm2.rpc.startProcessId(integer, fn)` start a process by id (pm_id) who his state is stopped
|
||||
- `ipm2.rpc.stopProcessId(integer, fn)` stop a process by id (pm_id)
|
||||
- `ipm2.rpc.stopAll({}, fn)` stop all process
|
||||
- `ipm2.rpc.reload(data, fn)` reload all apps (only for networked apps)
|
||||
- `ipm2.rpc.killMe(data, fn)` kill pm2 daemon
|
||||
- `ipm2.rpc.findByScript(string, fn)` send you back the informations about a specific process
|
||||
- `ipm2.rpc.restartProcessId(integer, fn)` restart a process by id (pm_id)
|
||||
- `ipm2.rpc.restartProcessName(string, fn)` restart all processes who have the given name
|
||||
- `ipm2.rpc.deleteProcess(string, fn)` stop and delete all processes from the pm2 database
|
||||
- `ipm2.rpc.deleteAll(data, fn)` stop and delete all processes
|
||||
- `ipm2.rpc.msgProcess(opts, fn)` send msg `opts.msg` to process at `opts.id` or all processes with `opts.name`
|
||||
|
||||
## Notifications
|
||||
|
||||
- `process:online` when a process is started/restarted
|
||||
- `process:exit` when a process is exited
|
||||
- `process:exception` When a process has received an uncaughtException
|
||||
|
||||
**Advanced feature** : You can use `process.send({ type : 'my:message', data : {}})` in your Node apps. When you emit a message, they will be redirected to pm2 and sent back to the pm2-interface bus. This can be coupled with `rpc.msgProcess(opts, fn)` to allow 2-way communication between managed processes and pm2-interface - see second Example below.
|
||||
|
||||
> It should be noted that `process.send` will be undefined if there is no parent process. Therefore a check of `if (process.send)` may be advisable.
|
||||
|
||||
## Example
|
||||
|
||||
```javascript
|
||||
var ipm2 = require('pm2-interface')();
|
||||
|
||||
ipm2.on('ready', function() {
|
||||
console.log('Connected to pm2');
|
||||
|
||||
ipm2.bus.on('*', function(event, data){
|
||||
console.log(event, data.pm2_env.name);
|
||||
});
|
||||
|
||||
setTimeout(function() {
|
||||
ipm2.rpc.restartProcessId(0, function(err, dt) {
|
||||
console.log(dt);
|
||||
});
|
||||
}, 2000);
|
||||
|
||||
|
||||
ipm2.rpc.getMonitorData({}, function(err, dt) {
|
||||
console.log(dt);
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
## Example 2-way
|
||||
|
||||
in your process script
|
||||
```javascript
|
||||
if (send in process) {
|
||||
process.on("message", function (msg) {
|
||||
if ( "type" in msg && msg.type === "god:heap" ) {
|
||||
var heap = process.memoryUsage().heapUsed
|
||||
process.send({type:"process:heap", heap:heap})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
var myMemoryLeak = []
|
||||
|
||||
setInterval( function () {
|
||||
var object = {}
|
||||
for (var i = 0; i < 10000; i++) {
|
||||
object["key"+i] = Math.random().toString(36).substring(7)
|
||||
}
|
||||
|
||||
myMemoryLeak.push(object)
|
||||
|
||||
}, Math.round(Math.random()*2000))
|
||||
```
|
||||
in monitoring script
|
||||
```javascript
|
||||
var ipm2 = require('pm2-interface')()
|
||||
|
||||
ipm2.on('ready', function() {
|
||||
|
||||
console.log('Connected to pm2')
|
||||
|
||||
ipm2.bus.on('process:heap', function(data){
|
||||
console.log("process heap:", data)
|
||||
})
|
||||
|
||||
|
||||
setInterval( function () {
|
||||
var msg = {type:"god:heap"} // god: is arbitrary and used to distinguish incoming & outgoing msgs
|
||||
ipm2.rpc.msgProcess({name:"worker", msg:msg}, function (err, res) {
|
||||
if (err) console.log(err)
|
||||
else console.log(res)
|
||||
})
|
||||
}, 5000)
|
||||
})
|
||||
```
|
||||
Start pm2 and monitoring script + output:
|
||||
```shell
|
||||
pm2 start worker.js -i 3 --name worker
|
||||
node monitor.js
|
||||
|
||||
sent 3 messages # coming from the console.log(res)
|
||||
process heap: { pm_id: 0, msg: { type: 'process:heap', heap: 43416064 } }
|
||||
process heap: { pm_id: 1, msg: { type: 'process:heap', heap: 18373704 } }
|
||||
process heap: { pm_id: 2, msg: { type: 'process:heap', heap: 80734256 } }
|
||||
sent 3 messages
|
||||
process heap: { pm_id: 0, msg: { type: 'process:heap', heap: 61994096 } }
|
||||
process heap: { pm_id: 1, msg: { type: 'process:heap', heap: 22437400 } }
|
||||
process heap: { pm_id: 2, msg: { type: 'process:heap', heap: 116622432 } }
|
||||
sent 3 messages
|
||||
process heap: { pm_id: 0, msg: { type: 'process:heap', heap: 79641168 } }
|
||||
process heap: { pm_id: 1, msg: { type: 'process:heap', heap: 32260112 } }
|
||||
process heap: { pm_id: 2, msg: { type: 'process:heap', heap: 156047904 } }
|
||||
|
||||
pm2 delete all
|
||||
```
|
||||
|
||||
## Disconnect
|
||||
|
||||
Since pm2-interface interacts with PM2 via sockets, any script which uses pm2-interface will remain alive even when the node.js event loop is empty. `process.exit()` can be called to forcefully exit, or, if your script has finished making calls to PM2, you may call `ipm2.disconnect()` to disconnect the socket connections and allow node to exit automatically.
|
||||
|
||||
```javascript
|
||||
ipm2.on('ready', function() {
|
||||
|
||||
// ...
|
||||
|
||||
ipm2.disconnect();
|
||||
});
|
||||
```
|
||||
|
||||
> Calling `disconnect()` means "I am entirely done interacting with PM2." You will no longer be able to receive messages on `ipm2.bus` or send requests on `ipm2.rpc`. To reconnect you must completely start over with a new ipm2 object.
|
||||
|
||||
## Ideas
|
||||
|
||||
- Catching exceptions and fowarding them by mail
|
||||
- A web interface to control PM2
|
||||
|
||||
## Apache v2 License
|
||||
@ -1,12 +0,0 @@
|
||||
//
|
||||
// Modifying these values break tests and can break
|
||||
// pm2-interface intercommunication (because of ports)
|
||||
//
|
||||
|
||||
module.exports = {
|
||||
DAEMON_BIND_HOST : 'localhost',
|
||||
DAEMON_RPC_PORT : 6666, // RPC commands
|
||||
DAEMON_PUB_PORT : 6667, // Realtime events
|
||||
SUCCESS_EXIT : 0,
|
||||
ERROR_EXIT : 1
|
||||
};
|
||||
@ -1,48 +0,0 @@
|
||||
var ipm2 = require('..');
|
||||
|
||||
var ipm2a = ipm2({bind_host : 'localhost'});
|
||||
|
||||
|
||||
|
||||
ipm2a.on('ready', function() {
|
||||
console.log('Connected to pm2');
|
||||
|
||||
ipm2a.bus.on('*', function(event, data){
|
||||
console.log(event);
|
||||
});
|
||||
|
||||
// ipm2a.bus.on('log:err', function(event, data){
|
||||
// console.log(event, data);
|
||||
// });
|
||||
|
||||
// ipm2a.bus.on('log:out', function(event, data){
|
||||
// console.log(event, data);
|
||||
// });
|
||||
|
||||
|
||||
ipm2a.on('rpc_sock:reconnecting', function() {
|
||||
console.log('rpc_sock:reconnecting');
|
||||
});
|
||||
ipm2a.on('sub_sock:ready', function() {
|
||||
console.log('sub_sock:ready');
|
||||
});
|
||||
ipm2a.on('sub_sock:closed', function() {
|
||||
console.log('sub_sock:closed');
|
||||
});
|
||||
|
||||
ipm2a.on('sub_sock:reconnecting', function() {
|
||||
console.log('sub_sock:reconnecting');
|
||||
});
|
||||
|
||||
|
||||
// setTimeout(function() {
|
||||
// ipm2.rpc.restartProcessId(0, function(err, dt) {
|
||||
// console.log(dt);
|
||||
// });
|
||||
// }, 2000);
|
||||
|
||||
|
||||
// ipm2.rpc.getMonitorData({}, function(err, dt) {
|
||||
// console.log(dt);
|
||||
// });
|
||||
});
|
||||
@ -1,27 +0,0 @@
|
||||
|
||||
var ipm2 = require('..');
|
||||
|
||||
var ipm2a = ipm2({bind_host : 'localhost'});
|
||||
|
||||
|
||||
|
||||
ipm2a.on('ready', function() {
|
||||
console.log('Connected to pm2');
|
||||
|
||||
ipm2a.bus.on('*', function(event, data){
|
||||
console.log(event);
|
||||
});
|
||||
|
||||
setInterval(function() {
|
||||
|
||||
var msg = {type:"god:heap"};
|
||||
|
||||
ipm2a.rpc.msgProcess({name:"expose_method", msg:msg}, function (err, res) {
|
||||
if (err) console.log(err)
|
||||
else console.log(res)
|
||||
console.log(arguments);
|
||||
})
|
||||
|
||||
}, 2000);
|
||||
|
||||
});
|
||||
@ -1,3 +0,0 @@
|
||||
|
||||
module.exports = exports = require("./lib");
|
||||
|
||||
@ -1,123 +0,0 @@
|
||||
/**
|
||||
* Dependencies
|
||||
*/
|
||||
|
||||
var axon = require('axon');
|
||||
var cst = require('../constants.js');
|
||||
var sys = require('sys');
|
||||
var rpc = require('axon-rpc');
|
||||
var log = require('debug')('pm2:interface');
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
|
||||
/**
|
||||
* Export with conf
|
||||
*/
|
||||
module.exports = function(opts){
|
||||
var sub_port = opts && opts.sub_port || cst.DAEMON_PUB_PORT;
|
||||
var rpc_port = opts && opts.rpc_port || cst.DAEMON_RPC_PORT;
|
||||
var bind_host = opts && opts.bind_host || cst.DAEMON_BIND_HOST;
|
||||
|
||||
return new IPM2(sub_port, rpc_port, bind_host);
|
||||
};
|
||||
|
||||
/**
|
||||
* IPM2, Pm2 Interface
|
||||
*/
|
||||
|
||||
var IPM2 = function(sub_port, rpc_port, bind_host) {
|
||||
//if (!(this instanceof Bash)) return new Bash(opts);
|
||||
var self = this;
|
||||
|
||||
EventEmitter.call(this);
|
||||
|
||||
this.sub_port = sub_port;
|
||||
this.rpc_port = rpc_port;
|
||||
this.bind_host = bind_host;
|
||||
|
||||
var sub = axon.socket('sub-emitter');
|
||||
var sub_sock = this.sub_sock = sub.connect(sub_port, bind_host);
|
||||
this.bus = sub;
|
||||
|
||||
var req = axon.socket("req");
|
||||
var rpc_sock = this.rpc_sock = req.connect(rpc_port, bind_host);
|
||||
this.rpc_client = new rpc.Client(req);
|
||||
|
||||
this.rpc = {};
|
||||
|
||||
/**
|
||||
* Disconnect socket connections. This will allow Node to exit automatically.
|
||||
* Further calls to PM2 from this object will throw an error.
|
||||
*/
|
||||
this.disconnect = function () {
|
||||
self.sub_sock.close();
|
||||
self.rpc_sock.close();
|
||||
};
|
||||
|
||||
/**
|
||||
* Generate method by requesting exposed methods by PM2
|
||||
* You can now control/interact with PM2
|
||||
*/
|
||||
var generateMethods = function(cb) {
|
||||
log('Requesting and generating RPC methods');
|
||||
self.rpc_client.methods(function(err, methods) {
|
||||
Object.keys(methods).forEach(function(key) {
|
||||
var method_signature, md;
|
||||
method_signature = md = methods[key];
|
||||
|
||||
log('+-- Creating %s method', md.name);
|
||||
|
||||
(function(name) {
|
||||
self.rpc[name] = function() {
|
||||
log(name);
|
||||
var args = Array.prototype.slice.call(arguments);
|
||||
args.unshift(name);
|
||||
self.rpc_client.call.apply(self.rpc_client, args);
|
||||
};
|
||||
})(md.name);
|
||||
|
||||
});
|
||||
return cb();
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Waiting to connect to sub channel
|
||||
* and RPC
|
||||
*/
|
||||
|
||||
rpc_sock.on('connect', function() {
|
||||
log('rpc_sock:ready');
|
||||
self.emit('rpc_sock:ready');
|
||||
generateMethods(function() {
|
||||
self.emit('ready');
|
||||
});
|
||||
});
|
||||
|
||||
rpc_sock.on('close', function() {
|
||||
log('rpc_sock:closed');
|
||||
self.emit('close');
|
||||
});
|
||||
|
||||
rpc_sock.on('reconnect attempt', function() {
|
||||
log('rpc_sock:reconnecting');
|
||||
self.emit('reconnecting');
|
||||
});
|
||||
|
||||
sub_sock.on('connect', function() {
|
||||
log('sub_sock ready');
|
||||
self.emit('sub_sock:ready');
|
||||
});
|
||||
|
||||
sub_sock.on('close', function() {
|
||||
log('sub_sock:closed');
|
||||
self.emit('closed');
|
||||
});
|
||||
|
||||
sub_sock.on('reconnect attempt', function() {
|
||||
log('sub_sock:reconnecting');
|
||||
self.emit('reconnecting');
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
sys.inherits(IPM2, EventEmitter);
|
||||
@ -1,42 +0,0 @@
|
||||
{
|
||||
"name": "pm2-interface",
|
||||
"version": "0.1.4",
|
||||
"description": "Interact with pm2 via RPC and receive real time notifications",
|
||||
"author": {
|
||||
"name": "Strzelewicz Alexandre",
|
||||
"email": "as@unitech.io",
|
||||
"url": "http://unitech.io"
|
||||
},
|
||||
"engines" : {
|
||||
"node" : ">=0.8"
|
||||
},
|
||||
"main": "index.js",
|
||||
"dependencies" : {
|
||||
"axon" : "2.0.0",
|
||||
"axon-rpc" : "0.0.3",
|
||||
"debug" : "*"
|
||||
},
|
||||
"keywords": [
|
||||
"cli",
|
||||
"fault tolerant",
|
||||
"sysadmin",
|
||||
"tools",
|
||||
"pm2",
|
||||
"node-pm2",
|
||||
"monitoring",
|
||||
"process manager",
|
||||
"forever",
|
||||
"process configuration",
|
||||
"clustering",
|
||||
"cluster cli",
|
||||
"cluster",
|
||||
"cron",
|
||||
"devops",
|
||||
"dev ops"
|
||||
],
|
||||
"repository": {
|
||||
"type" : "git",
|
||||
"url" : "git://github.com/Unitech/pm2-interface.git"
|
||||
},
|
||||
"license": "Apache v2"
|
||||
}
|
||||
@ -7,9 +7,6 @@ echo -e "\033[1mRunning tests:\033[0m"
|
||||
|
||||
cd $file_path
|
||||
|
||||
$pm2 kill
|
||||
spec "kill daemon"
|
||||
|
||||
#
|
||||
# Different way to stop process
|
||||
#
|
||||
@ -24,6 +21,7 @@ success "$1"
|
||||
$pm2 stop 12412
|
||||
$pm2 stop 0
|
||||
|
||||
|
||||
OUT=`$pm2 prettylist | grep -o "stopped" | wc -l`
|
||||
[ $OUT -eq 1 ] || fail "$1"
|
||||
success "$1"
|
||||
@ -37,6 +35,7 @@ OUT=`$pm2 prettylist | grep -o "stopped" | wc -l`
|
||||
[ $OUT -eq 3 ] || fail "$1"
|
||||
success "$1"
|
||||
|
||||
|
||||
#
|
||||
# Describe process
|
||||
#
|
||||
@ -137,11 +136,7 @@ spec "Should get the right JSON with HttpInterface file launched"
|
||||
# Restart only one process
|
||||
#
|
||||
$pm2 restart 1
|
||||
sleep 0.3
|
||||
$http_get -q http://localhost:9615/ -O $JSON_FILE
|
||||
OUT=`cat $JSON_FILE | grep -o "restart_time\":1" | wc -l`
|
||||
[ $OUT -eq 1 ] || fail "$1"
|
||||
success "$1"
|
||||
should 'should has restarted process' 'restart_time: 1' 1
|
||||
|
||||
#
|
||||
# Restart all processes
|
||||
|
||||
@ -21,7 +21,7 @@ $pm2 restart echo.js
|
||||
spec "Should restart an app by script.js (TRANSITIONAL STATE)"
|
||||
|
||||
###############
|
||||
$pm2 kill
|
||||
$pm2 delete all
|
||||
|
||||
echo "---- BY_NAME Start an app, stop it, if state stopped and started, restart stopped app"
|
||||
|
||||
@ -33,30 +33,13 @@ $pm2 restart gege
|
||||
should 'should app be online once restart called' 'online' 1
|
||||
|
||||
###############
|
||||
$pm2 kill
|
||||
$pm2 delete all
|
||||
|
||||
echo "Start an app, start it one more time, if started, throw message"
|
||||
$pm2 start echo.js
|
||||
$pm2 start echo.js
|
||||
ispec "Should not re start app"
|
||||
|
||||
|
||||
###############
|
||||
$pm2 kill
|
||||
|
||||
cd ../..
|
||||
|
||||
echo "Change path try to exec"
|
||||
$pm2 start test/fixtures/echo.js
|
||||
should 'should app be online' 'online' 1
|
||||
$pm2 stop test/fixtures/echo.js
|
||||
should 'should app be stopped' 'stopped' 1
|
||||
$pm2 start test/fixtures/echo.js
|
||||
should 'should app be online' 'online' 1
|
||||
|
||||
cd -
|
||||
|
||||
|
||||
########### DELETED STUFF BY ID
|
||||
$pm2 kill
|
||||
|
||||
@ -65,14 +48,14 @@ $pm2 delete 0
|
||||
should 'should has been deleted process by id' "name: 'echo'" 0
|
||||
|
||||
########### DELETED STUFF BY NAME
|
||||
$pm2 kill
|
||||
$pm2 delete all
|
||||
|
||||
$pm2 start echo.js --name test
|
||||
$pm2 delete test
|
||||
should 'should has been deleted process by name' "name: 'test'" 0
|
||||
|
||||
########### DELETED STUFF BY SCRIPT
|
||||
$pm2 kill
|
||||
$pm2 delete all
|
||||
|
||||
$pm2 start echo.js
|
||||
$pm2 delete echo.js
|
||||
@ -86,8 +69,8 @@ $pm2 kill
|
||||
$pm2 start echo.js -o outech.log -e errech.log --name gmail -i 10
|
||||
sleep 0.5
|
||||
cat outech-0.log > /dev/null
|
||||
spec "file outech.log exist"
|
||||
spec "file outech-0.log exist"
|
||||
cat errech-0.log > /dev/null
|
||||
spec "file errech.log exist"
|
||||
spec "file errech-0.log exist"
|
||||
|
||||
should 'should has not restarted' 'restart_time: 0' 10
|
||||
|
||||
@ -6,8 +6,6 @@ source "${SRC}/include.sh"
|
||||
cd $file_path
|
||||
|
||||
########### Fork mode
|
||||
$pm2 kill
|
||||
|
||||
$pm2 start echo.js -x
|
||||
should 'should has forked app' 'fork_mode' 1
|
||||
|
||||
|
||||
@ -8,7 +8,6 @@ cd $file_path
|
||||
echo "################## GRACEFUL RELOAD ###################"
|
||||
|
||||
###############
|
||||
$pm2 kill
|
||||
|
||||
echo "Launching"
|
||||
$pm2 start graceful-exit.js -i 4 --name="graceful" -o "grace.log" -e "grace-err.log"
|
||||
|
||||
@ -8,7 +8,6 @@ cd $file_path
|
||||
echo "################## GRACEFUL RELOAD 3 ###################"
|
||||
|
||||
###############
|
||||
$pm2 kill
|
||||
|
||||
echo "Launching"
|
||||
$pm2 start graceful-exit-send.js -i 2 --name="graceful3" -o "grace3.log" -e "grace-err3.log"
|
||||
|
||||
@ -9,20 +9,31 @@ $pm2 kill
|
||||
echo "################ HARMONY ES6"
|
||||
|
||||
$pm2 start harmony.js
|
||||
sleep 8
|
||||
sleep 2
|
||||
$pm2 list
|
||||
should 'should fail when trying to launch pm2 without harmony option' 'errored' 1
|
||||
should 'should FAIL when not passing harmony option to V8' 'restart_time: 0' 0
|
||||
$pm2 list
|
||||
$pm2 kill
|
||||
|
||||
PM2_NODE_OPTIONS='--harmony' `pwd`/../../bin/pm2 start harmony.js
|
||||
sleep 4
|
||||
$pm2 list
|
||||
should 'should not fail when passing harmony option to V8' 'restart_time: 0' 1
|
||||
$pm2 kill
|
||||
$pm2 delete all
|
||||
|
||||
$pm2 start harmony.js --node-args="--harmony"
|
||||
sleep 8
|
||||
sleep 2
|
||||
$pm2 list
|
||||
should 'should not fail when passing node-args=harmony opts' 'errored' 0
|
||||
$pm2 kill
|
||||
should 'should not fail when passing node-args=harmony opts in CLUSTERMODE' 'restart_time: 0' 1
|
||||
$pm2 delete all
|
||||
|
||||
echo "################ HARMONY / NODEARGS ES6 FORK MODE"
|
||||
|
||||
$pm2 start harmony.js --node-args="--harmony" -x
|
||||
sleep 2
|
||||
$pm2 list
|
||||
should 'should not fail when passing node-args=harmony opts in FORKMODE' 'restart_time: 0' 1
|
||||
$pm2 delete all
|
||||
|
||||
echo "################## NODE ARGS VIA JSON"
|
||||
|
||||
$pm2 start harmony.json
|
||||
sleep 2
|
||||
$pm2 list
|
||||
should 'should not fail when passing harmony option to V8 via node_args in JSON files' 'restart_time: 0' 1
|
||||
|
||||
$pm2 delete all
|
||||
|
||||
@ -22,7 +22,7 @@ file_path="test/fixtures"
|
||||
$pm2 kill
|
||||
|
||||
# Determine wget / curl
|
||||
which wget
|
||||
which wget > /dev/null
|
||||
if [ $? -eq 0 ]
|
||||
then
|
||||
http_get="wget"
|
||||
@ -31,8 +31,6 @@ else
|
||||
exit 1;
|
||||
fi
|
||||
|
||||
echo $http_get
|
||||
|
||||
function fail {
|
||||
echo -e "######## \033[31m ✘ $1\033[0m"
|
||||
exit 1
|
||||
|
||||
@ -8,10 +8,6 @@ cd $file_path
|
||||
|
||||
echo "Starting infinite loop tests"
|
||||
|
||||
$pm2 kill
|
||||
|
||||
|
||||
|
||||
$pm2 start killtoofast.js --name unstable-process
|
||||
|
||||
echo -n "Waiting for process to restart too many times and pm2 to stop it"
|
||||
|
||||
@ -7,8 +7,27 @@ cd $file_path
|
||||
|
||||
echo -e "\033[1mRunning tests:\033[0m"
|
||||
|
||||
#
|
||||
# Max memory auto restart option
|
||||
#
|
||||
# -max-memory-restart option && maxMemoryRestart (via JSON file)
|
||||
#
|
||||
$pm2 start big-array.js --max-memory-restart 19
|
||||
sleep 7
|
||||
$pm2 list
|
||||
should 'process should been restarted' 'restart_time: 0' 0
|
||||
|
||||
$pm2 kill
|
||||
$pm2 delete all
|
||||
|
||||
#
|
||||
# Via JSON
|
||||
#
|
||||
$pm2 start max-mem.json
|
||||
sleep 7
|
||||
$pm2 list
|
||||
should 'process should been restarted' 'restart_time: 0' 0
|
||||
|
||||
$pm2 delete all
|
||||
|
||||
$pm2 start env.js
|
||||
|
||||
@ -27,7 +46,7 @@ else
|
||||
fail "environment defined ? wtf ?"
|
||||
fi
|
||||
|
||||
$pm2 kill
|
||||
$pm2 delete all
|
||||
|
||||
$pm2 start env.json
|
||||
|
||||
@ -37,7 +56,7 @@ sleep 1
|
||||
|
||||
OUT=`cat $OUT_LOG | head -n 1`
|
||||
|
||||
if [ $OUT = "undefined" ]
|
||||
if [ "$OUT" = "undefined" ]
|
||||
then
|
||||
fail "environment variable hasnt been defined"
|
||||
else
|
||||
@ -77,3 +96,11 @@ cat outmerge-0.log > /dev/null
|
||||
ispec 'file outmerge-0.log should not exist'
|
||||
|
||||
rm outmerge*
|
||||
|
||||
########### coffee cluster test
|
||||
$pm2 delete all
|
||||
|
||||
$pm2 start echo.coffee
|
||||
|
||||
should 'process should not have been restarted' 'restart_time: 0' 1
|
||||
should 'process should be online' "status: 'online'" 1
|
||||
|
||||
23
test/fixtures/big-array-es6.js
vendored
Normal file
23
test/fixtures/big-array-es6.js
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
|
||||
|
||||
var obj = {};
|
||||
var i = 0;
|
||||
|
||||
setInterval(function() {
|
||||
obj[i] = Array.apply(null, new Array(99999)).map(String.prototype.valueOf,"hi");
|
||||
i++;
|
||||
}, 2);
|
||||
|
||||
|
||||
(function testHarmony() {
|
||||
//
|
||||
// Harmony test
|
||||
//
|
||||
try {
|
||||
var assert = require('assert')
|
||||
, s = new Set();
|
||||
s.add('a');
|
||||
assert.ok(s.has('a'));
|
||||
console.log('● ES6 mode'.green);
|
||||
} catch(e) {}
|
||||
})();
|
||||
8
test/fixtures/big-array.js
vendored
Normal file
8
test/fixtures/big-array.js
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
|
||||
var obj = {};
|
||||
var i = 0;
|
||||
|
||||
setInterval(function() {
|
||||
obj[i] = Array.apply(null, new Array(99999)).map(String.prototype.valueOf,"hi");
|
||||
i++;
|
||||
}, 2);
|
||||
5
test/fixtures/harmony.json
vendored
Normal file
5
test/fixtures/harmony.json
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"name" : "ES6",
|
||||
"script" : "harmony.js",
|
||||
"node_args" : "--harmony"
|
||||
}
|
||||
5
test/fixtures/max-mem.json
vendored
Normal file
5
test/fixtures/max-mem.json
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"name" : "max_mem",
|
||||
"script" : "big-array.js",
|
||||
"max_memory_restart" : "19"
|
||||
}
|
||||
@ -1,7 +0,0 @@
|
||||
require('./programmatic/pm2Bus.mocha.js');
|
||||
require('./programmatic/god.mocha.js');
|
||||
require('./programmatic/monit.mocha.js');
|
||||
require('./programmatic/satan.mocha.js');
|
||||
require('./programmatic/programmatic.js');
|
||||
require('./programmatic/interactor.mocha.js');
|
||||
require('./programmatic/interactor.daemonizer.mocha.js');
|
||||
35
test/index.sh
Normal file
35
test/index.sh
Normal file
@ -0,0 +1,35 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
alias mocha='../node_modules/mocha/bin/mocha'
|
||||
pm2="`type -P node` `pwd`/bin/pm2"
|
||||
|
||||
|
||||
|
||||
function fail {
|
||||
echo -e "######## \033[31m ✘ $1\033[0m"
|
||||
$pm2 kill
|
||||
exit 1
|
||||
}
|
||||
|
||||
function success {
|
||||
echo -e "\033[32m------------> ✔ $1\033[0m"
|
||||
$pm2 kill
|
||||
}
|
||||
|
||||
function spec {
|
||||
[ $? -eq 0 ] || fail "$1"
|
||||
success "$1"
|
||||
}
|
||||
|
||||
$pm2 kill
|
||||
|
||||
mocha ./test/programmatic/god.mocha.js
|
||||
spec "God test"
|
||||
mocha ./test/programmatic/satan.mocha.js
|
||||
spec "Satan test"
|
||||
mocha ./test/programmatic/programmatic.js
|
||||
spec "Programmatic test"
|
||||
mocha ./test/programmatic/interactor.daemonizer.mocha.js
|
||||
spec "Interactor daemonizer test"
|
||||
|
||||
echo "########## PROGRAMMATIC TEST DONE #########"
|
||||
@ -4,22 +4,49 @@ var numCPUs = require('os').cpus().length;
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
var should = require('should');
|
||||
|
||||
var Common = require('../../lib/Common');
|
||||
/**
|
||||
* Description
|
||||
* @method getConf
|
||||
* @return AssignmentExpression
|
||||
*/
|
||||
function getConf() {
|
||||
return process_conf = {
|
||||
pm_exec_path : path.resolve(process.cwd(), 'test/fixtures/echo.js'),
|
||||
pm_err_log_path : path.resolve(process.cwd(), 'test/echoErr.log'),
|
||||
pm_out_log_path : path.resolve(process.cwd(), 'test/echoLog.log'),
|
||||
pm_pid_file : path.resolve(process.cwd(), 'test/echopid'),
|
||||
exec_mode : 'cluster_mode'
|
||||
};
|
||||
var a = Common.resolveAppPaths({
|
||||
script : path.resolve(process.cwd(), 'test/fixtures/echo.js'),
|
||||
name : 'echo',
|
||||
instances : 2
|
||||
});
|
||||
return a;
|
||||
}
|
||||
|
||||
function getConf2() {
|
||||
return Common.resolveAppPaths({
|
||||
script : path.resolve(process.cwd(), 'test/fixtures/child.js'),
|
||||
instances : 4,
|
||||
exec_mode : 'cluster_mode',
|
||||
name : 'child'
|
||||
});
|
||||
}
|
||||
|
||||
function getConf3() {
|
||||
return Common.resolveAppPaths({
|
||||
script : path.resolve(process.cwd(), 'test/fixtures/child.js'),
|
||||
instances : 10,
|
||||
exec_mode : 'cluster_mode',
|
||||
name : 'child'
|
||||
});
|
||||
}
|
||||
|
||||
function getConf4() {
|
||||
return Common.resolveAppPaths({
|
||||
script : path.resolve(process.cwd(), 'test/fixtures/args.js'),
|
||||
args : "['-d', '-a']",
|
||||
instances : '1',
|
||||
name : 'child'
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
describe('God', function() {
|
||||
before(function(done) {
|
||||
God.deleteAll({}, function(err, dt) {
|
||||
@ -52,18 +79,14 @@ describe('God', function() {
|
||||
});
|
||||
|
||||
it('should kill a process by name', function(done) {
|
||||
God.prepare({
|
||||
pm_exec_path : path.resolve(process.cwd(), 'test/fixtures/echo.js'),
|
||||
pm_err_log_path : path.resolve(process.cwd(), 'test/errLog.log'),
|
||||
pm_out_log_path : path.resolve(process.cwd(), 'test/outLog.log'),
|
||||
pm_pid_path : path.resolve(process.cwd(), 'test/child'),
|
||||
instances : 2
|
||||
}, function(err, procs) {
|
||||
God.getFormatedProcesses().length.should.equal(2);
|
||||
God.prepare(getConf(), function(err, procs) {
|
||||
God.getFormatedProcesses().length.should.equal(2);
|
||||
|
||||
God.stopProcessName('echo', function() {
|
||||
God.getFormatedProcesses().length.should.equal(2);
|
||||
God.deleteAll({}, done);
|
||||
God.deleteAll({}, function() {
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -82,9 +105,9 @@ describe('God', function() {
|
||||
God.prepare(getConf(), function(err, procs) {
|
||||
should(err).be.null;
|
||||
pid = procs[0].pid;
|
||||
procs[0].pm2_env.status.should.be.equal('online');
|
||||
God.getFormatedProcesses().length.should.equal(1);
|
||||
done();
|
||||
procs[0].pm2_env.status.should.be.equal('online');
|
||||
God.getFormatedProcesses().length.should.equal(2);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -102,8 +125,8 @@ describe('God', function() {
|
||||
clu = procs[0];
|
||||
|
||||
pid = clu.pid;
|
||||
procs[0].pm2_env.status.should.be.equal('online');
|
||||
done();
|
||||
procs[0].pm2_env.status.should.be.equal('online');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
@ -126,7 +149,7 @@ describe('God', function() {
|
||||
});
|
||||
|
||||
it('should stop this process by name and keep in db on state stopped', function(done) {
|
||||
God.stopProcessName(clu.name, function(err, dt) {
|
||||
God.stopProcessName(clu.pm2_env.name, function(err, dt) {
|
||||
var proc = God.findProcessById(clu.pm2_env.pm_id);
|
||||
proc.pm2_env.status.should.be.equal('stopped');
|
||||
God.checkProcess(proc.process.pid).should.be.equal(false);
|
||||
@ -135,7 +158,7 @@ describe('God', function() {
|
||||
});
|
||||
|
||||
it('should restart the same process by NAME and set it as state online and be up', function(done) {
|
||||
God.restartProcessName(clu.name, function(err, dt) {
|
||||
God.restartProcessName(clu.pm2_env.name, function(err, dt) {
|
||||
var proc = God.findProcessById(clu.pm2_env.pm_id);
|
||||
proc.pm2_env.status.should.be.equal('online');
|
||||
God.checkProcess(proc.process.pid).should.be.equal(true);
|
||||
@ -148,7 +171,7 @@ describe('God', function() {
|
||||
God.deleteProcessId(clu.pm2_env.pm_id, function(err, dt) {
|
||||
var proc = God.findProcessById(clu.pm2_env.pm_id);
|
||||
God.checkProcess(old_pid).should.be.equal(false);
|
||||
dt.length.should.be.equal(0);
|
||||
dt.length.should.be.equal(1);
|
||||
done();
|
||||
});
|
||||
});
|
||||
@ -158,7 +181,7 @@ describe('God', function() {
|
||||
pid = _clu[0].pid;
|
||||
_clu[0].pm2_env.status.should.be.equal('online');
|
||||
var old_pid = _clu[0].pid;
|
||||
God.deleteProcessName(_clu.name, function(err, dt) {
|
||||
God.deleteProcessName(_clu[0].pm2_env.name, function(err, dt) {
|
||||
setTimeout(function() {
|
||||
var proc = God.findProcessById(clu.pm2_env.pm_id);
|
||||
should(proc == null);
|
||||
@ -181,15 +204,7 @@ describe('God', function() {
|
||||
});
|
||||
|
||||
it('should launch app', function(done) {
|
||||
God.prepare({
|
||||
pm_exec_path : path.resolve(process.cwd(), 'test/fixtures/child.js'),
|
||||
pm_err_log_path : path.resolve(process.cwd(), 'test/errLog.log'),
|
||||
pm_out_log_path : path.resolve(process.cwd(), 'test/outLog.log'),
|
||||
pm_pid_path : path.resolve(process.cwd(), 'test/child'),
|
||||
instances : 4,
|
||||
exec_mode : 'cluster_mode',
|
||||
name : 'child'
|
||||
}, function(err, procs) {
|
||||
God.prepare(getConf2(), function(err, procs) {
|
||||
var processes = God.getFormatedProcesses();
|
||||
|
||||
setTimeout(function() {
|
||||
@ -234,29 +249,19 @@ describe('God', function() {
|
||||
});
|
||||
|
||||
it('should launch multiple processes depending on CPUs available', function(done) {
|
||||
God.prepare({
|
||||
pm_exec_path : path.resolve(process.cwd(), 'test/fixtures/echo.js'),
|
||||
pm_err_log_path : path.resolve(process.cwd(), 'test/errLog.log'),
|
||||
pm_out_log_path : path.resolve(process.cwd(), 'test/outLog.log'),
|
||||
pm_pid_path : path.resolve(process.cwd(), 'test/child'),
|
||||
exec_mode : 'cluster_mode',
|
||||
instances : 3
|
||||
}, function(err, procs) {
|
||||
God.getFormatedProcesses().length.should.equal(3);
|
||||
God.prepare(Common.resolveAppPaths({
|
||||
script : path.resolve(process.cwd(), 'test/fixtures/echo.js'),
|
||||
name : 'child',
|
||||
instances:3
|
||||
}), function(err, procs) {
|
||||
God.getFormatedProcesses().length.should.equal(3);
|
||||
procs.length.should.equal(3);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should start maximum processes depending on CPU numbers', function(done) {
|
||||
God.prepare({
|
||||
pm_exec_path : path.resolve(process.cwd(), 'test/fixtures/echo.js'),
|
||||
pm_err_log_path : path.resolve(process.cwd(), 'test/errLog.log'),
|
||||
pm_out_log_path : path.resolve(process.cwd(), 'test/outLog.log'),
|
||||
pm_pid_path : path.resolve(process.cwd(), 'test/child'),
|
||||
instances : 10,
|
||||
exec_mode : 'cluster_mode',
|
||||
}, function(err, procs) {
|
||||
God.prepare(getConf3(), function(err, procs) {
|
||||
God.getFormatedProcesses().length.should.equal(10);
|
||||
procs.length.should.equal(10);
|
||||
done();
|
||||
@ -264,15 +269,7 @@ describe('God', function() {
|
||||
});
|
||||
|
||||
it('should handle arguments', function(done) {
|
||||
God.prepare({
|
||||
pm_exec_path : path.resolve(process.cwd(), 'test/fixtures/args.js'),
|
||||
pm_err_log_path : path.resolve(process.cwd(), 'test/errLog.log'),
|
||||
pm_out_log_path : path.resolve(process.cwd(), 'test/outLog.log'),
|
||||
pm_pid_path : path.resolve(process.cwd(), 'test/child'),
|
||||
args : "['-d', '-a']",
|
||||
instances : '1',
|
||||
exec_mode : 'cluster_mode'
|
||||
}, function(err, procs) {
|
||||
God.prepare(getConf4(), function(err, procs) {
|
||||
setTimeout(function() {
|
||||
God.getFormatedProcesses()[0].pm2_env.restart_time.should.eql(0);
|
||||
done();
|
||||
|
||||
@ -4,7 +4,7 @@ var p = require('path')
|
||||
// , spawn = require('child_process').spawn
|
||||
, Spawner = require('promise-spawner')
|
||||
, async = require('async')
|
||||
|
||||
|
||||
, bin = p.join(root, '/bin/pm2')
|
||||
, pm2 = require(p.join(root, 'index.js'))
|
||||
, ids = []
|
||||
@ -16,13 +16,6 @@ var timeout = function(cb, time) {
|
||||
}
|
||||
|
||||
describe('Monitor', function() {
|
||||
this.timeout(0);
|
||||
|
||||
beforeEach(function(cb) {
|
||||
pm2.connect(function() {
|
||||
cb()
|
||||
})
|
||||
})
|
||||
|
||||
before(function(cb) {
|
||||
pm2.connect(function() {
|
||||
@ -32,6 +25,16 @@ describe('Monitor', function() {
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
after(function(cb) {
|
||||
pm2.killDaemon(function() {
|
||||
pm2.disconnect(function() {
|
||||
cb()
|
||||
})
|
||||
});
|
||||
})
|
||||
|
||||
|
||||
it('should start', function() {
|
||||
|
||||
var modifiers = {
|
||||
@ -56,7 +59,7 @@ describe('Monitor', function() {
|
||||
console.log(this.data.err)
|
||||
}
|
||||
|
||||
process.exit(code)
|
||||
process.exit(code)
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
@ -8,7 +8,7 @@ var Plan = require('../helpers/plan.js');
|
||||
var APPS = require('../helpers/apps.js');
|
||||
var Ipm2 = require('pm2-interface');
|
||||
|
||||
describe.skip('PM2 BUS / RPC', function() {
|
||||
describe('PM2 BUS / RPC', function() {
|
||||
var pm2;
|
||||
var ipm2;
|
||||
|
||||
|
||||
@ -258,7 +258,11 @@ describe('PM2 programmatic calls', function() {
|
||||
describe('start OR restart', function() {
|
||||
before(function(done) {
|
||||
pm2.delete('all', function(err, ret) {
|
||||
done();
|
||||
pm2.list(function(err, ret) {
|
||||
should(err).be.null;
|
||||
ret.length.should.eql(0);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@ -17,9 +17,7 @@ describe('Satan', function() {
|
||||
it('should start Satan interaction', function(done) {
|
||||
Satan.start(function(err) {
|
||||
should(err).be.null;
|
||||
pm2.delete('all', function(err, ret) {
|
||||
done();
|
||||
});
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user