mirror of
https://github.com/Unitech/pm2.git
synced 2025-12-08 20:35:53 +00:00
merge development with master
This commit is contained in:
commit
852ef05a2c
28
CHANGELOG.md
28
CHANGELOG.md
@ -1,4 +1,32 @@
|
||||
|
||||
# 0.10.0 - PM2 Hellfire release
|
||||
|
||||
- PM2 hearth code has been refactored and now it handles extreme scenario without any leak or bug
|
||||
- PM2 restart <json|id|name|all> refresh current environment variables #528
|
||||
- PM2 delete all more verbose
|
||||
- PM2 reset <all|id|name> reset restart numbers
|
||||
- Auto update script at PM2 installation
|
||||
- --watch enhanced to avoid zombie processes
|
||||
- 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
|
||||
|
||||
144
README.md
144
README.md
@ -1,10 +1,10 @@
|
||||

|
||||

|
||||
|
||||
pm2 is a process manager for Node apps with a built-in load balancer.
|
||||
PM2 is a process manager for Node apps with a built-in load balancer.
|
||||
|
||||
### Tech notes
|
||||
|
||||
pm2 is perfect for spreading your stateless Node.js code across all CPUs available on a server, for keeping all processes alive forever and 0s reload them.
|
||||
PM2 is perfect for spreading your stateless Node.js code across all CPUs available on a server, for keeping all processes alive forever and 0s reload them.
|
||||
|
||||
### Main features
|
||||
|
||||
@ -12,11 +12,12 @@ pm2 is perfect for spreading your stateless Node.js code across all CPUs availab
|
||||
- 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
|
||||
|
||||
Tested with Node v0.11, v0.10 (https://travis-ci.org/Unitech/pm2).
|
||||
Tested with Node v0.11, v0.10 (https://travis-ci.org/Unitech/PM2).
|
||||
|
||||
**The recommended Node.js version is v0.11.13**
|
||||
|
||||
@ -30,20 +31,21 @@ Works on Linux & MacOS.
|
||||
|
||||
<!-- ### Build Status -->
|
||||
|
||||
Master: [](https://travis-ci.org/Unitech/pm2)
|
||||
Master: [](https://travis-ci.org/Unitech/PM2)
|
||||
|
||||
Development: [](https://travis-ci.org/Unitech/PM2)
|
||||
|
||||
Development: [](https://travis-ci.org/Unitech/pm2)
|
||||
|
||||
## Monitoring dashboard
|
||||
|
||||

|
||||
|
||||
We're going to release a very nice product, a dashboard to monitor every part of your Node.js applications. Here are some links:
|
||||
We are developing a top-notch product: a dashboard to monitor each part of your Node.js applications. Here are some links:
|
||||
|
||||
- [Pitch + Survey](https://docs.google.com/forms/d/1FuCjIhrGg-ItxInq2nLreoe9GS-gZWJNkNWE0JJajw8/viewform) People who fill the survey will be eligible for free license
|
||||
- [Newsletter](http://signup.pm2.io/) Subscribe to be kept informed
|
||||
|
||||
Thanks in advance and we hope that you like pm2!
|
||||
Thanks in advance and we hope that you like PM2!
|
||||
|
||||
------
|
||||
|
||||
@ -56,18 +58,19 @@ Thanks in advance and we hope that you like pm2!
|
||||
- [Examples](#a3)
|
||||
- [Different ways to launch a process](#a667)
|
||||
- [Options](#a987)
|
||||
- [How to update pm2 ?](#update-pm2)
|
||||
- [How to update PM2 ?](#update-pm2)
|
||||
|
||||
### Features
|
||||
|
||||
- [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)
|
||||
- [Watch & Restart](#a890)
|
||||
- [Reloading without downtime](#a690)
|
||||
- [Make pm2 restart on server reboot](#a8)
|
||||
- [Make PM2 restart on server reboot](#a8)
|
||||
- [JSON app declaration](#a10)
|
||||
|
||||
### Deployment - ecosystem.json
|
||||
@ -88,14 +91,14 @@ Thanks in advance and we hope that you like pm2!
|
||||
- [Configuration file](#a989)
|
||||
- [Enabling Harmony ES6](#a66)
|
||||
- [CoffeeScript](#a19)
|
||||
- [Testing pm2 on your prod environment](#a149)
|
||||
- [Testing PM2 on your prod environment](#a149)
|
||||
- [JSON app via pipe](#a96)
|
||||
|
||||
### Knowledge
|
||||
|
||||
- [Stateless apps ?](#stateless-apps)
|
||||
- [Transitional state of apps](#a4)
|
||||
- [Setup pm2 on server: tutorial](#a89)
|
||||
- [Setup PM2 on server: tutorial](#a89)
|
||||
- [Logs and PID files](#a34)
|
||||
- [Execute any script: What is fork mode ?](#a23)
|
||||
|
||||
@ -113,15 +116,15 @@ Thanks in advance and we hope that you like pm2!
|
||||
<a name="a1"/>
|
||||
## Installation
|
||||
|
||||
The preferred Node version to run pm2, is the **0.11.10**
|
||||
The preferred Node version to run PM2 is **0.11.10**
|
||||
|
||||
The latest pm2 stable version is installable via NPM:
|
||||
The latest PM2 stable version is installable via NPM:
|
||||
|
||||
```bash
|
||||
$ npm install pm2@latest -g
|
||||
```
|
||||
|
||||
If the above fails:
|
||||
If the above fails use:
|
||||
|
||||
```bash
|
||||
$ npm install git://github.com/Unitech/pm2#master -g
|
||||
@ -179,6 +182,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
|
||||
@ -226,7 +230,7 @@ $ pm2 start -x echo.rb
|
||||
$ pm2 start -x echo.pl
|
||||
```
|
||||
|
||||
The not javascript languages will have to be run in [fork mode](#a23).
|
||||
Languages other than javascript have to be run in [fork mode](#a23).
|
||||
|
||||
<a name="a987"/>
|
||||
## Options
|
||||
@ -259,7 +263,7 @@ Options:
|
||||
```
|
||||
|
||||
<a name="update-pm2"/>
|
||||
## How to update pm2
|
||||
## How to update PM2
|
||||
|
||||
Install the latest pm2 version :
|
||||
|
||||
@ -267,7 +271,7 @@ Install the latest pm2 version :
|
||||
$ npm install pm2@latest -g
|
||||
```
|
||||
|
||||
Then update the in-memory pm2 :
|
||||
Then update the in-memory PM2 :
|
||||
|
||||
```bash
|
||||
$ pm2 updatePM2
|
||||
@ -278,7 +282,7 @@ $ pm2 updatePM2
|
||||
<a name="a4"/>
|
||||
## Transitional state of apps (important)
|
||||
|
||||
pm2 is a process manager, as said, pm2 can start, stop, restart and *delete* processes.
|
||||
PM2 is a process manager. PM2 can start, stop, restart and *delete* processes.
|
||||
|
||||
Start a process:
|
||||
|
||||
@ -293,7 +297,8 @@ Now let's say I need to stop the web-interface:
|
||||
$ pm2 stop web-interface
|
||||
```
|
||||
|
||||
As you can see **the process hasn't disappeared**. It is still there but now in `stopped` status.
|
||||
As you can see **the process hasn't disappeared**. It's still there but in `stopped` status.
|
||||
|
||||
|
||||
To restart it just do:
|
||||
|
||||
@ -301,8 +306,8 @@ To restart it just do:
|
||||
$ pm2 restart web-interface
|
||||
```
|
||||
|
||||
Now I want to **delete** the app from the pm2 process list.
|
||||
To do that:
|
||||
Now I want to **delete** the app from the PM2 process list.
|
||||
To do so:
|
||||
|
||||
```bash
|
||||
$ pm2 delete web-interface
|
||||
@ -327,6 +332,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
|
||||
|
||||
@ -363,7 +387,7 @@ $ pm2 ilogs
|
||||
|
||||
### Reloading all logs (SIGUSR2/Logrotate)
|
||||
|
||||
To reload all logs, you can send `SIGUSR2` to the pm2 process.
|
||||
To reload all logs, you can send `SIGUSR2` to the PM2 process.
|
||||
|
||||
You can also reload all logs via the command line with:
|
||||
|
||||
@ -389,6 +413,7 @@ $ pm2 start app.js --name "API" -i max
|
||||
|
||||
If your app is well-designed (**stateless**) you'll be able to **process many more queries**.
|
||||
|
||||
|
||||
Important concepts to make a Node.js app stateless:
|
||||
|
||||
- Sessions must not be stored in memory but shared via a database (Redis, Mongo, whatever)
|
||||
@ -448,13 +473,13 @@ Then use the command:
|
||||
$ pm2 gracefulReload [all|name]
|
||||
```
|
||||
|
||||
When pm2 starts a new process to replace an old one, it will wait for the new process to begin listening to a connection before sending the shutdown message to the old one. If a script does not need to listen to a connection, it can manually tell pm2 that the process has started up by calling `process.send('online')`.
|
||||
When PM2 starts a new process to replace an old one, it will wait for the new process to begin listening to a connection before sending the shutdown message to the old one. If a script does not need to listen to a connection, it can manually tell PM2 that the process has started up by calling `process.send('online')`.
|
||||
|
||||
<a name="a8"/>
|
||||
## Startup script
|
||||
|
||||
pm2 has the amazing ability to **generate startup scripts and configure them**.
|
||||
pm2 is also smart enough to **save all your process list** and to **bring back all your processes on restart**.
|
||||
PM2 has the amazing ability to **generate startup scripts and configure them**.
|
||||
PM2 is also smart enough to **save all your process list** and to **bring back all your processes on restart**.
|
||||
|
||||
```bash
|
||||
$ pm2 startup [ubuntu|centos|gentoo|systemd]
|
||||
@ -466,7 +491,7 @@ Once you have started the apps and want to keep them on server reboot do:
|
||||
$ pm2 save
|
||||
```
|
||||
|
||||
**Warning** It's tricky to make this feature work generically, so once pm2 has setup your startup script, reboot your server to make sure that pm2 has launched your apps!
|
||||
**Warning** It's tricky to make this feature work generically, so once PM2 has setup your startup script, reboot your server to make sure that PM2 has launched your apps!
|
||||
|
||||
### More information
|
||||
|
||||
@ -495,7 +520,7 @@ $ pm2 startup ubuntu -u www
|
||||
|
||||
### Related commands
|
||||
|
||||
Dump all processes status and environment managed by pm2:
|
||||
Dump all processes status and environment managed by PM2:
|
||||
```bash
|
||||
$ pm2 dump
|
||||
```
|
||||
@ -509,7 +534,7 @@ $ pm2 resurrect
|
||||
<a name="a890"/>
|
||||
## Watch & Restart
|
||||
|
||||
pm2 can automatically restart your app when a file changes in the current directory or its subdirectories:
|
||||
PM2 can automatically restart your app when a file changes in the current directory or its subdirectories:
|
||||
|
||||
```bash
|
||||
$ pm2 start app.js --watch
|
||||
@ -528,7 +553,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"/>
|
||||
@ -545,6 +569,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",
|
||||
@ -613,6 +638,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",
|
||||
@ -891,7 +917,7 @@ pm2.connect(function(err) {
|
||||
<a name="a77"/>
|
||||
# Special features
|
||||
|
||||
Launching pm2 without daemonizing itself:
|
||||
Launching PM2 without daemonizing itself:
|
||||
|
||||
```bash
|
||||
$ pm2 start app.js --no-daemon
|
||||
@ -915,7 +941,6 @@ PM2_BIND_ADDR
|
||||
PM2_API_PORT
|
||||
PM2_GRACEFUL_TIMEOUT
|
||||
PM2_MODIFY_REQUIRE
|
||||
PM2_NODE_OPTIONS
|
||||
```
|
||||
|
||||
|
||||
@ -928,36 +953,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
|
||||
|
||||
@ -991,7 +1001,7 @@ We recommend following the 12 factor convention : [http://12factor.net/](http://
|
||||
<a name="a34"/>
|
||||
## Log and PID files
|
||||
|
||||
By default, logs (error and output), pid files, dumps, and pm2 logs are located in `~/.pm2/`:
|
||||
By default, logs (error and output), pid files, dumps, and PM2 logs are located in `~/.pm2/`:
|
||||
|
||||
```
|
||||
.pm2/
|
||||
@ -1006,7 +1016,7 @@ By default, logs (error and output), pid files, dumps, and pm2 logs are located
|
||||
<a name="a23"/>
|
||||
## Execute any script: What is fork mode?
|
||||
|
||||
The default mode of pm2 consists of wrapping the code of your node application into the Node Cluster module. It's called the **cluster mode**.
|
||||
The default mode of PM2 consists of wrapping the code of your node application into the Node Cluster module. It's called the **cluster mode**.
|
||||
|
||||
There is also a more classical way to execute your app, like node-forever does, called the **fork mode**.
|
||||
|
||||
@ -1060,9 +1070,9 @@ echo $my_json | pm2 start -
|
||||
```
|
||||
|
||||
<a name="a149"/>
|
||||
## Is my production server ready for pm2?
|
||||
## Is my production server ready for PM2?
|
||||
|
||||
Just try the tests before using pm2 on your production server
|
||||
Just try the tests before using PM2 on your production server
|
||||
|
||||
```bash
|
||||
$ git clone https://github.com/Unitech/pm2.git
|
||||
@ -1086,7 +1096,7 @@ $ nvm alias default v0.11.10
|
||||
<a name="a27"/>
|
||||
## Contributing/Development mode
|
||||
|
||||
To hack pm2, it's pretty simple:
|
||||
To hack PM2, it's very simple:
|
||||
|
||||
```bash
|
||||
$ pm2 kill # kill the current pm2
|
||||
@ -1095,9 +1105,9 @@ $ cd pm2/
|
||||
$ DEBUG=* PM2_DEBUG=true ./bin/pm2 --no-daemon
|
||||
```
|
||||
|
||||
Each time you edit the code, be sure to kill and restart pm2 to make changes taking effect.
|
||||
Each time you edit the code, be sure to kill and restart PM2 to make changes taking effect.
|
||||
|
||||
## Install pm2 development
|
||||
## Install PM2 development
|
||||
|
||||
```bash
|
||||
$ npm install git://github.com/Unitech/pm2#development -g
|
||||
@ -1106,7 +1116,7 @@ $ npm install git://github.com/Unitech/pm2#development -g
|
||||
<a name="a21"/>
|
||||
## Known bugs and workarounds
|
||||
|
||||
First, install the lastest pm2 version:
|
||||
First, install the lastest PM2 version:
|
||||
|
||||
```bash
|
||||
$ npm install -g pm2@latest
|
||||
@ -1114,7 +1124,7 @@ $ npm install -g pm2@latest
|
||||
|
||||
### Node 0.10.x doesn't free the script port when stopped. It's due to the Node.js cluster module.
|
||||
So if you feel that this problem is important for your use case, use the [fork mode](#execute-any-script-what-is-fork-mode-) instead.
|
||||
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.
|
||||
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.
|
||||
|
||||
```
|
||||
$ pm2 start index.js -x # start my app in fork mode
|
||||
|
||||
181
bin/pm2
181
bin/pm2
@ -10,11 +10,41 @@ 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');
|
||||
var pkg = require('../package.json');
|
||||
|
||||
//
|
||||
// Init
|
||||
//
|
||||
(function init() {
|
||||
var exist = fs.existsSync(cst.DEFAULT_FILE_PATH);
|
||||
|
||||
if (!exist) {
|
||||
console.log('Initializing folder for pm2 on %s', cst.DEFAULT_FILE_PATH);
|
||||
fs.mkdirSync(cst.DEFAULT_FILE_PATH);
|
||||
fs.mkdirSync(cst.DEFAULT_LOG_PATH);
|
||||
fs.mkdirSync(cst.DEFAULT_PID_PATH);
|
||||
|
||||
/**
|
||||
* Create configuration file if not present
|
||||
*/
|
||||
fs.exists(cst.PM2_CONF_FILE, function(exist) {
|
||||
if (!exist) {
|
||||
console.log('Creating PM2 configuration file in %s', cst.PM2_CONF_FILE);
|
||||
fs
|
||||
.createReadStream(path.join(__dirname, cst.SAMPLE_CONF_FILE))
|
||||
.pipe(fs.createWriteStream(cst.PM2_CONF_FILE));
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
})();
|
||||
|
||||
|
||||
commander.version(pkg.version)
|
||||
.option('-v --version', 'get version')
|
||||
.option('-s --silent', 'hide all messages', false)
|
||||
@ -25,6 +55,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')
|
||||
@ -36,8 +67,8 @@ commander.version(pkg.version)
|
||||
.option('--merge-logs', 'merge logs from different instances but keep error and out separated')
|
||||
.option('--watch', 'watch application folder for changes')
|
||||
.option('--node-args <node_args>', "space delimited arguments to pass to node in cluster mode - e.g. --node-args=\"--debug=7001 --trace-deprecation\"",
|
||||
function(val) {
|
||||
return val.split(' ');
|
||||
function(val) {
|
||||
return val.split(' ');
|
||||
})
|
||||
.option('--run-as-user <run_as_user>', 'The user or uid to run a managed process as')
|
||||
.option('--run-as-group <run_as_group>', 'The group or gid to run a managed process as')
|
||||
@ -121,7 +152,7 @@ function failOnUnknown(fn) {
|
||||
//
|
||||
commander.command('start <file|json|stdin>')
|
||||
.option('--watch', 'Watch folder for changes')
|
||||
.description(' start and daemonize an app')
|
||||
.description('start and daemonize an app')
|
||||
.action(function(cmd) {
|
||||
if (cmd == "-") {
|
||||
process.stdin.resume();
|
||||
@ -137,13 +168,13 @@ commander.command('start <file|json|stdin>')
|
||||
});
|
||||
|
||||
commander.command('deploy <file|environment>')
|
||||
.description(' deploy your json')
|
||||
.description('deploy your json')
|
||||
.action(function(cmd) {
|
||||
CLI.deploy(cmd, commander);
|
||||
});
|
||||
|
||||
commander.command('startOrRestart <json>')
|
||||
.description(' start or restart JSON file')
|
||||
.description('start or restart JSON file')
|
||||
.action(function(file) {
|
||||
CLI._jsonStartOrAction('restart', file, commander);
|
||||
});
|
||||
@ -153,7 +184,7 @@ commander.command('startOrRestart <json>')
|
||||
//
|
||||
commander.command('stop <id|name|all|json|stdin>')
|
||||
.option('--watch', 'Stop watching folder for changes')
|
||||
.description(' stop a process (to start it again, do pm2 restart <app>)')
|
||||
.description('stop a process (to start it again, do pm2 restart <app>)')
|
||||
.action(function(param) {
|
||||
CLI.stop(param);
|
||||
});
|
||||
@ -163,7 +194,7 @@ commander.command('stop <id|name|all|json|stdin>')
|
||||
//
|
||||
commander.command('restart <id|name|all|json|stdin>')
|
||||
.option('--watch', 'Toggle watching folder for changes')
|
||||
.description(' restart a process')
|
||||
.description('restart a process')
|
||||
.action(function(param) {
|
||||
CLI.restart(param);
|
||||
});
|
||||
@ -172,7 +203,7 @@ commander.command('restart <id|name|all|json|stdin>')
|
||||
// Reload process(es)
|
||||
//
|
||||
commander.command('reload <name|all>')
|
||||
.description(' reload processes (note that its for app using HTTP/HTTPS)')
|
||||
.description('reload processes (note that its for app using HTTP/HTTPS)')
|
||||
.action(function(pm2_id) {
|
||||
CLI.reload(pm2_id);
|
||||
});
|
||||
@ -181,13 +212,13 @@ commander.command('reload <name|all>')
|
||||
// Reload process(es)
|
||||
//
|
||||
commander.command('gracefulReload <name|all>')
|
||||
.description(' gracefully reload a process. Send a "shutdown" message to close all connections.')
|
||||
.description('gracefully reload a process. Send a "shutdown" message to close all connections.')
|
||||
.action(function(pm2_id) {
|
||||
CLI.gracefulReload(pm2_id);
|
||||
});
|
||||
|
||||
// commander.command('gracefulStop <name|all>')
|
||||
// .description(' gracefully reload a process. Send a "shutdown" message to close all connections.')
|
||||
// .description('gracefully reload a process. Send a "shutdown" message to close all connections.')
|
||||
// .action(function(pm2_id) {
|
||||
// CLI.gracefulStop(pm2_id);
|
||||
// });
|
||||
@ -196,7 +227,7 @@ commander.command('gracefulReload <name|all>')
|
||||
// Stop and delete a process by name from database
|
||||
//
|
||||
commander.command('delete <name|id|script|all|json|stdin>')
|
||||
.description(' stop and delete a process from pm2 process list')
|
||||
.description('stop and delete a process from pm2 process list')
|
||||
.action(function(name) {
|
||||
if (name == "-") {
|
||||
process.stdin.resume();
|
||||
@ -213,7 +244,7 @@ commander.command('delete <name|id|script|all|json|stdin>')
|
||||
// Send system signal to process
|
||||
//
|
||||
commander.command('sendSignal <signal> <pm2_id|name>')
|
||||
.description(' send a system signal to the target process')
|
||||
.description('send a system signal to the target process')
|
||||
.action(function(signal, pm2_id) {
|
||||
if (isNaN(parseInt(pm2_id))) {
|
||||
console.log(cst.PREFIX_MSG + 'Sending signal to process name ' + pm2_id);
|
||||
@ -228,16 +259,16 @@ commander.command('sendSignal <signal> <pm2_id|name>')
|
||||
// Stop and delete a process by name from database
|
||||
//
|
||||
commander.command('ping')
|
||||
.description(' ping pm2 daemon - if not up it will launch it')
|
||||
.description('ping pm2 daemon - if not up it will launch it')
|
||||
.action(function() {
|
||||
CLI.ping();
|
||||
});
|
||||
CLI.ping();
|
||||
});
|
||||
|
||||
commander.command('updatePM2')
|
||||
.description(' updatePM2 after a npm update')
|
||||
.description('updatePM2 after a npm update')
|
||||
.action(function() {
|
||||
CLI.updatePM2()
|
||||
});
|
||||
CLI.updatePM2()
|
||||
});
|
||||
|
||||
//
|
||||
// Interact
|
||||
@ -245,20 +276,20 @@ commander.command('updatePM2')
|
||||
commander.command('interact [secret_key] [public_key] [machine_name]')
|
||||
.description('launch agent to interact with Keymetrics')
|
||||
.action(function(secret, public, machine) {
|
||||
CLI.interact(secret, public, machine);
|
||||
});
|
||||
CLI.interact(secret, public, machine);
|
||||
});
|
||||
|
||||
commander.command('killInteract')
|
||||
.description('stop agent')
|
||||
.action(function() {
|
||||
CLI.killInteract()
|
||||
});
|
||||
CLI.killInteract()
|
||||
});
|
||||
|
||||
commander.command('infoInteract')
|
||||
.description('get information about agent')
|
||||
.action(function() {
|
||||
CLI.infoInteract();
|
||||
});
|
||||
CLI.infoInteract();
|
||||
});
|
||||
|
||||
|
||||
//
|
||||
@ -323,17 +354,23 @@ commander.command('ecosystem')
|
||||
CLI.generateSample(name);
|
||||
});
|
||||
|
||||
commander.command('reset <name|id|all>')
|
||||
.description('reset counters for process')
|
||||
.action(function(proc_id) {
|
||||
CLI.resetMetaProcess(proc_id);
|
||||
});
|
||||
|
||||
commander.command('describe <id>')
|
||||
.description('describe all parameters of a process id')
|
||||
.action(function(proc_id) {
|
||||
CLI.describe(proc_id);
|
||||
});
|
||||
CLI.describe(proc_id);
|
||||
});
|
||||
|
||||
commander.command('desc <id>')
|
||||
.description('describe all parameters of a process id')
|
||||
.action(function(proc_id) {
|
||||
CLI.describe(proc_id);
|
||||
});
|
||||
CLI.describe(proc_id);
|
||||
});
|
||||
|
||||
//
|
||||
// List command
|
||||
@ -341,34 +378,34 @@ commander.command('desc <id>')
|
||||
commander.command('list')
|
||||
.description('list all processes')
|
||||
.action(function() {
|
||||
CLI.list()
|
||||
});
|
||||
CLI.list()
|
||||
});
|
||||
|
||||
commander.command('ls')
|
||||
.description('(alias) list all processes')
|
||||
.action(function() {
|
||||
CLI.list()
|
||||
});
|
||||
CLI.list()
|
||||
});
|
||||
|
||||
commander.command('l')
|
||||
.description('(alias) list all processes')
|
||||
.action(function() {
|
||||
CLI.list()
|
||||
});
|
||||
CLI.list()
|
||||
});
|
||||
|
||||
commander.command('status')
|
||||
.description('(alias) list all processes')
|
||||
.action(function() {
|
||||
CLI.list()
|
||||
});
|
||||
CLI.list()
|
||||
});
|
||||
|
||||
|
||||
// List in raw json
|
||||
commander.command('jlist')
|
||||
.description('list all processes in JSON format')
|
||||
.action(function() {
|
||||
CLI.jlist()
|
||||
});
|
||||
CLI.jlist()
|
||||
});
|
||||
|
||||
// List in prettified Json
|
||||
commander.command('prettylist')
|
||||
@ -383,14 +420,14 @@ commander.command('prettylist')
|
||||
commander.command('monit')
|
||||
.description('launch termcaps monitoring')
|
||||
.action(function() {
|
||||
CLI.monit()
|
||||
});
|
||||
CLI.monit()
|
||||
});
|
||||
|
||||
commander.command('m')
|
||||
.description('(alias) launch termcaps monitoring')
|
||||
.action(function() {
|
||||
CLI.monit()
|
||||
});
|
||||
CLI.monit()
|
||||
});
|
||||
|
||||
|
||||
//
|
||||
@ -464,53 +501,15 @@ if (process.argv.length == 2) {
|
||||
// in file Satan.js, method Satan.launchRPC
|
||||
//
|
||||
process.once('satan:client:ready', function() {
|
||||
CLI.getVersion(function(err, remote_version) {
|
||||
if (!err && (pkg.version != remote_version)) {
|
||||
console.log('');
|
||||
console.log(chalk.red.bold('>>>> In-memory PM2 is out-of-date, do:\n>>>> $ pm2 updatePM2'));
|
||||
console.log('In memory PM2 version:', chalk.blue.bold(remote_version));
|
||||
console.log('Local PM2 version:', chalk.blue.bold(pkg.version));
|
||||
console.log('');
|
||||
}
|
||||
commander.parse(process.argv);
|
||||
});
|
||||
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('');
|
||||
console.log(chalk.red.bold('>>>> In-memory PM2 is out-of-date, do:\n>>>> $ pm2 updatePM2'));
|
||||
console.log('In memory PM2 version:', chalk.blue.bold(remote_version));
|
||||
console.log('Local PM2 version:', chalk.blue.bold(pkg.version));
|
||||
console.log('');
|
||||
}
|
||||
commander.parse(process.argv);
|
||||
});
|
||||
});
|
||||
|
||||
//
|
||||
// Init
|
||||
//
|
||||
(function init() {
|
||||
fs.exists(cst.DEFAULT_FILE_PATH, function(exist) {
|
||||
if (!exist) {
|
||||
console.log('Initializing folder for pm2 on %s', cst.DEFAULT_FILE_PATH);
|
||||
fs.mkdirSync(cst.DEFAULT_FILE_PATH);
|
||||
fs.mkdirSync(cst.DEFAULT_LOG_PATH);
|
||||
fs.mkdirSync(cst.DEFAULT_PID_PATH);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Create configuration file if not present
|
||||
*/
|
||||
fs.exists(cst.PM2_CONF_FILE, function(exist) {
|
||||
if (!exist) {
|
||||
console.log('Creating PM2 configuration file in %s', cst.PM2_CONF_FILE);
|
||||
fs
|
||||
.createReadStream(path.join(__dirname, cst.SAMPLE_CONF_FILE))
|
||||
.pipe(fs.createWriteStream(cst.PM2_CONF_FILE));
|
||||
}
|
||||
});
|
||||
})();
|
||||
|
||||
(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) {}
|
||||
})();
|
||||
|
||||
14
constants.js
14
constants.js
@ -15,13 +15,13 @@ var DEFAULT_FILE_PATH = p.resolve(HOME, '.pm2');
|
||||
|
||||
var default_conf = {
|
||||
DEFAULT_FILE_PATH : DEFAULT_FILE_PATH,
|
||||
PM2_LOG_FILE_PATH : p.join(process.env.PM2_LOG_DIR || p.resolve(process.env.HOME, '.pm2'), 'pm2.log'),
|
||||
PM2_PID_FILE_PATH : p.join(process.env.PM2_PID_DIR || p.resolve(process.env.HOME, '.pm2'), 'pm2.pid'),
|
||||
PM2_LOG_FILE_PATH : p.join(DEFAULT_FILE_PATH, 'pm2.log'),
|
||||
PM2_PID_FILE_PATH : p.join(DEFAULT_FILE_PATH, 'pm2.pid'),
|
||||
DEFAULT_PID_PATH : p.join(DEFAULT_FILE_PATH, 'pids'),
|
||||
DEFAULT_LOG_PATH : p.join(DEFAULT_FILE_PATH, 'logs'),
|
||||
DUMP_FILE_PATH : p.join(DEFAULT_FILE_PATH, 'dump.pm2'),
|
||||
|
||||
SAMPLE_CONF_FILE : '../lib/custom_options.sh',
|
||||
SAMPLE_CONF_FILE : p.join('..', 'lib', 'custom_options.sh'),
|
||||
PM2_CONF_FILE : p.join(DEFAULT_FILE_PATH, 'custom_options.sh'),
|
||||
|
||||
DAEMON_BIND_HOST : process.env.PM2_BIND_ADDR || 'localhost',
|
||||
@ -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',
|
||||
@ -62,8 +62,8 @@ var default_conf = {
|
||||
INTERACTION_CONF : p.join(DEFAULT_FILE_PATH, 'agent.json'),
|
||||
SEND_INTERVAL : 1000,
|
||||
|
||||
INTERACTOR_LOG_FILE_PATH : p.join(p.resolve(process.env.HOME, '.pm2'), 'agent.log'),
|
||||
INTERACTOR_PID_PATH : p.join(p.resolve(process.env.HOME, '.pm2'), 'agent.pid'),
|
||||
INTERACTOR_LOG_FILE_PATH : p.join(DEFAULT_FILE_PATH, 'agent.log'),
|
||||
INTERACTOR_PID_PATH : p.join(DEFAULT_FILE_PATH, 'agent.pid'),
|
||||
|
||||
INTERACTOR_RPC_PORT : parseInt(process.env.PM2_INTERACTOR_PORT) || 6668
|
||||
};
|
||||
|
||||
@ -1,30 +0,0 @@
|
||||
|
||||
|
||||
// Expose action
|
||||
// And "touch" file every 1.4s to restart the file
|
||||
|
||||
var axm = require('axm');
|
||||
|
||||
function makeid() {
|
||||
var text = "";
|
||||
var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||
|
||||
for( var i=0; i < 5; i++ )
|
||||
text += possible.charAt(Math.floor(Math.random() * possible.length));
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
|
||||
axm.action('cmd:' + makeid(), {comment : 'Refresh main database'}, function(reply) {
|
||||
console.log('Refreshing');
|
||||
reply({success : true});
|
||||
});
|
||||
|
||||
setTimeout(function() {
|
||||
var fs = require('fs');
|
||||
|
||||
|
||||
var a = fs.readFileSync(__filename);
|
||||
fs.writeFileSync(__filename, a);
|
||||
}, 1400);
|
||||
@ -2,7 +2,22 @@
|
||||
var axm = require('axm');
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
axm.action('refresh:db2', {comment : 'Refresh main database'}, function(reply) {
|
||||
|
||||
axm.emit('user:register', {
|
||||
user : 'Alex registered',
|
||||
email : 'thorustor@gmail.com'
|
||||
});
|
||||
|
||||
reply({success : true});
|
||||
});
|
||||
|
||||
|
||||
|
||||
axm.action('hello', {comment : 'Refresh main database'}, function(reply) {
|
||||
console.log('Refreshing');
|
||||
reply({success : true});
|
||||
});
|
||||
|
||||
0
examples/echo.js
Normal file → Executable file
0
examples/echo.js
Normal file → Executable file
28
examples/keymetrics-load.js
Normal file
28
examples/keymetrics-load.js
Normal file
@ -0,0 +1,28 @@
|
||||
|
||||
|
||||
var pm2 = require('..');
|
||||
|
||||
pm2.connect(function() {
|
||||
pm2.delete('all', function() {
|
||||
pm2.start('examples/human_event.js', function() {
|
||||
pm2.start('examples/child.js', {instances:2},function() {
|
||||
pm2.start('examples/kill-not-so-fast.js', {
|
||||
instances:10,
|
||||
minUptime: 0,
|
||||
maxRestarts : 0
|
||||
}, function() {
|
||||
pm2.start('examples/auto-save.js', {execMode : 'fork', watch:true, force : true}, function() {
|
||||
pm2.start('examples/custom_action_with_params.js', function() {
|
||||
//pm2.start('examples/auto-bench.js', {instances : 'max'}, function() {
|
||||
pm2.start('examples/throw.js', {name:'auto-throw'}, function() {
|
||||
pm2.disconnect(function() { process.exit(1); });
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -4,20 +4,24 @@ var pm2 = require('..');
|
||||
pm2.connect(function() {
|
||||
pm2.delete('all', function() {
|
||||
pm2.start('examples/human_event.js', function() {
|
||||
pm2.start('examples/child.js', {instances:2},function() {
|
||||
pm2.start('examples/custom_action.js', function() {
|
||||
pm2.start('examples/custom_action_with_params.js', function() {
|
||||
pm2.start('examples/auto-save.js', {watch : true, name :'auto-save-modify'}, function() {
|
||||
pm2.start('examples/http-trace.js', {name:'trace'}, function() {
|
||||
//pm2.start('examples/auto-bench.js', {instances : 'max'}, function() {
|
||||
pm2.start('examples/throw.js', {name:'auto-throw'}, function() {
|
||||
pm2.disconnect(function() { process.exit(1); });
|
||||
pm2.start('examples/child.js', {instances:2},function() {
|
||||
pm2.start('examples/custom_action.js', function() {
|
||||
pm2.start('examples/custom_action.js', {execMode : 'fork', force : true}, function() {
|
||||
pm2.start('examples/auto-save.js', {execMode : 'fork', watch:true, force : true}, function() {
|
||||
pm2.start('examples/custom_action_with_params.js', function() {
|
||||
pm2.start('examples/auto-save.js', {watch : true,force:true, name :'auto-save-modify'}, function() {
|
||||
pm2.start('examples/http-trace.js', {name:'trace'}, function() {
|
||||
//pm2.start('examples/auto-bench.js', {instances : 'max'}, function() {
|
||||
pm2.start('examples/throw.js', {name:'auto-throw'}, function() {
|
||||
pm2.disconnect(function() { process.exit(1); });
|
||||
});
|
||||
//});
|
||||
});
|
||||
//});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
7
examples/kill-not-so-fast.js
Normal file
7
examples/kill-not-so-fast.js
Normal file
@ -0,0 +1,7 @@
|
||||
|
||||
console.log('start');
|
||||
|
||||
setTimeout(function() {
|
||||
console.log('exit');
|
||||
throw new Error('Exitasdsadasdsda unacepted !!');
|
||||
}, 300);
|
||||
328
lib/CLI.js
328
lib/CLI.js
@ -24,6 +24,8 @@ var exitCli = Common.exitCli;
|
||||
var printError = Common.printError;
|
||||
var printOut = Common.printOut;
|
||||
|
||||
var pm2 = require('..');
|
||||
|
||||
/**
|
||||
* Method to start a script
|
||||
* @method startFile
|
||||
@ -41,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)
|
||||
@ -72,6 +79,10 @@ CLI.start = function(script, opts, cb) {
|
||||
appConf['run_as_group'] = opts.runAsGroup;
|
||||
if (opts.logDateFormat)
|
||||
appConf['log_date_format'] = opts.logDateFormat;
|
||||
if (typeof(opts.minUptime) !== 'undefined')
|
||||
appConf['min_uptime'] = opts.minUptime;
|
||||
if (typeof(opts.maxRestarts) !== 'undefined')
|
||||
appConf['max_restarts'] = opts.maxRestarts;
|
||||
|
||||
if (opts.executeCommand) {
|
||||
appConf['exec_mode'] = 'fork_mode';
|
||||
@ -89,9 +100,16 @@ CLI.start = function(script, opts, cb) {
|
||||
appConf['exec_interpreter'] = 'node';
|
||||
}
|
||||
|
||||
// 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 (opts.execMode) {
|
||||
appConf['exec_mode'] = opts.execMode;
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
@ -195,7 +213,7 @@ CLI.deploy = function(file, commands, cb) {
|
||||
|
||||
Deploy.deployForEnv(json_conf.deploy, env, args, function(err, data) {
|
||||
if (err) {
|
||||
printError(err);
|
||||
printError('Deploy failed');
|
||||
return cb ? cb(err) : exitCli(cst.ERROR_EXIT);
|
||||
}
|
||||
printOut('--> Success');
|
||||
@ -235,30 +253,45 @@ CLI.actionFromJson = function(action, file, jsonVia, cb) {
|
||||
|
||||
if (!Array.isArray(appConf)) appConf = [appConf]; //convert to array
|
||||
|
||||
async.eachLimit(appConf, cst.CONCURRENT_ACTIONS, function(proc, next) {
|
||||
var name;
|
||||
async.eachLimit(appConf, cst.CONCURRENT_ACTIONS, function(proc, next1) {
|
||||
var name = '';
|
||||
var new_env = proc.env ? proc.env : {};
|
||||
|
||||
if (!proc.name)
|
||||
name = p.basename(proc.script);
|
||||
else
|
||||
name = proc.name;
|
||||
|
||||
Satan.executeRemote(action, name, function(err, list) {
|
||||
if (err)
|
||||
console.error(err);
|
||||
printOut(cst.PREFIX_MSG + 'Stopping process by name ' + name);
|
||||
next();
|
||||
Common.getProcessIdByName(name, function(err, ids) {
|
||||
if (err) {
|
||||
printError(err);
|
||||
return next1();
|
||||
}
|
||||
if (!ids) return next1();
|
||||
|
||||
async.eachLimit(ids, 1, function(id, next2) {
|
||||
var opts;
|
||||
|
||||
if (action == 'restartProcessId')
|
||||
opts = { id : id, env : new_env };
|
||||
else
|
||||
opts = id;
|
||||
|
||||
Satan.executeRemote(action, opts, function(err, res) {
|
||||
if (err) {
|
||||
printError(err);
|
||||
return next2();
|
||||
}
|
||||
printOut(cst.PREFIX_MSG + 'Process ' + id + ' restarted');
|
||||
return next2();
|
||||
});
|
||||
}, function(err) {
|
||||
return next1(null, {success:true});
|
||||
});
|
||||
});
|
||||
|
||||
}, function(err) {
|
||||
if (err) {
|
||||
printOut(err);
|
||||
if (cb) cb(err);
|
||||
else return exitCli(cst.ERROR_EXIT);
|
||||
}
|
||||
|
||||
if (cb) return cb(null, {success:true});
|
||||
else return setTimeout(speedList, 800);
|
||||
else return setTimeout(speedList, 100);
|
||||
});
|
||||
};
|
||||
|
||||
@ -458,6 +491,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,
|
||||
@ -476,22 +510,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.SUCCESS_EXIT);
|
||||
});
|
||||
};
|
||||
|
||||
@ -525,6 +545,46 @@ CLI.ping = function(cb) {
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Reset meta data
|
||||
* @method resetMetaProcess
|
||||
*/
|
||||
CLI.resetMetaProcess = function(process_name, cb) {
|
||||
function processIds(ids, cb) {
|
||||
async.eachLimit(ids, 4, function(id, next) {
|
||||
Satan.executeRemote('resetMetaProcessId', id, function(err, res) {
|
||||
if (err) console.error(err);
|
||||
printOut(cst.PREFIX_MSG + 'Reseting meta for process id %d', id);
|
||||
return next();
|
||||
});
|
||||
}, function(err) {
|
||||
if (err) return cb(new Error(err));
|
||||
return cb ? cb(null, {success:true}) : speedList();
|
||||
});
|
||||
};
|
||||
|
||||
if (process_name == 'all') {
|
||||
Common.getAllProcessId(function(err, ids) {
|
||||
if (err) {
|
||||
printError(err);
|
||||
return cb ? cb({msg:err}) : exitCli(cst.ERROR_EXIT);
|
||||
}
|
||||
return processIds(ids, cb);
|
||||
});
|
||||
}
|
||||
else if (isNaN(parseInt(process_name))) {
|
||||
Common.getProcessIdByName(process_name, function(err, ids) {
|
||||
if (err) {
|
||||
printError(err);
|
||||
return cb ? cb({msg:err}) : exitCli(cst.ERROR_EXIT);
|
||||
}
|
||||
return processIds(ids, cb);
|
||||
});
|
||||
} else {
|
||||
processIds([process_name], cb);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Resurrect processes
|
||||
* @method resurrect
|
||||
@ -564,29 +624,33 @@ 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');
|
||||
debug('Dumping successfull', err);
|
||||
CLI.killDaemon(function() {
|
||||
debug('Daemon killed');
|
||||
|
||||
CLI.killDaemon(function(err) {
|
||||
printOut(cst.PREFIX_MSG + '--- killed');
|
||||
Satan.launchDaemon(function(err, child) {
|
||||
Satan.launchDaemon(function(err, child) {
|
||||
Satan.launchRPC(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();
|
||||
CLI.resurrect(function() {
|
||||
printOut(chalk.blue.bold('>>>>>>>>>> PM2 updated'));
|
||||
return cb ? cb(null, {success:true}) : speedList();
|
||||
});
|
||||
});
|
||||
return false;
|
||||
|
||||
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -632,11 +696,12 @@ CLI.dump = function(cb) {
|
||||
* @return
|
||||
*/
|
||||
CLI.web = function(cb) {
|
||||
Satan.executeRemote('prepare', resolvePaths({
|
||||
script : p.resolve(p.dirname(module.filename), './HttpInterface.js'),
|
||||
name : 'Pm2Http' + cst.WEB_INTERFACE,
|
||||
exec_mode : 'fork_mode'
|
||||
}), function(err, proc) {
|
||||
var filepath = p.resolve(p.dirname(module.filename), 'HttpInterface.js');
|
||||
|
||||
CLI.start(filepath, {
|
||||
name : 'pm2-http-interface',
|
||||
execMode : 'fork_mode'
|
||||
}, function(err, proc) {
|
||||
if (err) {
|
||||
printError(cst.PREFIX_MSG_ERR + 'Error while launching application', err.stack || err);
|
||||
return cb ? cb({msg:err}) : speedList();
|
||||
@ -854,14 +919,14 @@ CLI.restart = function(process_name, cb) {
|
||||
process.stdin.setEncoding('utf8');
|
||||
process.stdin.on('data', function (param) {
|
||||
process.stdin.pause();
|
||||
CLI.actionFromJson('restartProcessName', param, 'pipe', cb);
|
||||
CLI.actionFromJson('restartProcessId', param, 'pipe', cb);
|
||||
});
|
||||
} else if (process_name.indexOf('.json') > 0)
|
||||
CLI.actionFromJson('restartProcessName', process_name, 'file', cb);
|
||||
CLI.actionFromJson('restartProcessId', process_name, 'file', cb);
|
||||
else if (process_name == 'all')
|
||||
CLI._restartAll(cb);
|
||||
else if (isNaN(parseInt(process_name))) {
|
||||
printError('Restarting process by name ' + process_name);
|
||||
printOut('Restarting process by name ' + process_name);
|
||||
CLI._restartProcessByName(process_name, cb);
|
||||
} else {
|
||||
printOut('Restarting process by id ' + process_name);
|
||||
@ -875,14 +940,31 @@ CLI.restart = function(process_name, cb) {
|
||||
* @param {} pm2_name
|
||||
* @return
|
||||
*/
|
||||
CLI._restartProcessByName = function(pm2_name, cb) {
|
||||
Satan.executeRemote('restartProcessName', pm2_name, function(err, list) {
|
||||
CLI._restartProcessByName = function(process_name, cb) {
|
||||
Common.getProcessIdByName(process_name, function(err, ids) {
|
||||
if (err) {
|
||||
printError(err);
|
||||
return cb ? cb({msg:err}) : exitCli(cst.ERROR_EXIT);
|
||||
}
|
||||
printOut(cst.PREFIX_MSG + 'Process ' + pm2_name + ' restarted');
|
||||
return cb ? cb(null, list) : speedList();
|
||||
if (!ids || ids.length === 0) {
|
||||
return cb ? cb({msg:'Unknown process name'}) : exitCli(cst.ERROR_EXIT);
|
||||
}
|
||||
async.eachLimit(ids, 1, function(id, next) {
|
||||
Satan.executeRemote('restartProcessId', {
|
||||
id: id,
|
||||
env : process.env
|
||||
}, function(err, res) {
|
||||
if (err) {
|
||||
printError(err);
|
||||
return next();
|
||||
}
|
||||
printOut(cst.PREFIX_MSG + 'Process ' + id + ' restarted');
|
||||
return next();
|
||||
});
|
||||
}, function(err) {
|
||||
if (err) return cb(new Error(err));
|
||||
return cb ? cb(null, ids) : speedList();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
@ -893,12 +975,15 @@ CLI._restartProcessByName = function(pm2_name, cb) {
|
||||
* @return
|
||||
*/
|
||||
CLI._restartProcessById = function(pm2_id, cb) {
|
||||
Satan.executeRemote('restartProcessId', pm2_id, function(err, res) {
|
||||
Satan.executeRemote('restartProcessId', {
|
||||
id: pm2_id,
|
||||
env : process.env
|
||||
}, function(err, res) {
|
||||
if (err) {
|
||||
printError(err);
|
||||
return cb ? cb({msg:err}) : exitCli(cst.ERROR_EXIT);
|
||||
}
|
||||
printOut(cst.PREFIX_MSG + 'Process ' + pm2_id + ' restarted');
|
||||
printOut(cst.PREFIX_MSG + 'Process id ' + pm2_id + ' restarted');
|
||||
return cb ? cb(null, res) : speedList();
|
||||
});
|
||||
};
|
||||
@ -915,27 +1000,29 @@ 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);
|
||||
}
|
||||
|
||||
|
||||
(function rec(processes) {
|
||||
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) {
|
||||
if (err) {
|
||||
printError(err);
|
||||
return cb ? cb({msg:err}) : exitCli(cst.ERROR_EXIT);
|
||||
}
|
||||
printOut(cst.PREFIX_MSG + 'Process ' + proc.pm2_env.name + ' restarted');
|
||||
processes.shift();
|
||||
return rec(processes);
|
||||
});
|
||||
Satan.executeRemote('restartProcessId', {
|
||||
id : proc.pm2_env.pm_id,
|
||||
env : process.env
|
||||
}, function(err, res) {
|
||||
if (err) {
|
||||
printError(err);
|
||||
return cb ? cb({msg:err}) : exitCli(cst.ERROR_EXIT);
|
||||
}
|
||||
printOut(cst.PREFIX_MSG + 'Process ' + proc.pm2_env.name + ' restarted');
|
||||
processes.shift();
|
||||
return rec(processes);
|
||||
});
|
||||
return false;
|
||||
})(list);
|
||||
});
|
||||
@ -957,35 +1044,42 @@ CLI.delete = function(process_name, jsonVia, cb) {
|
||||
process_name = process_name.toString();
|
||||
}
|
||||
|
||||
printOut(cst.PREFIX_MSG + 'Deleting %s process', process_name);
|
||||
|
||||
if (jsonVia == 'pipe')
|
||||
return CLI.actionFromJson('deleteProcessName', process_name, 'pipe');
|
||||
return CLI.actionFromJson('deleteProcessId', process_name, 'pipe');
|
||||
if (process_name.indexOf('.json') > 0)
|
||||
return CLI.actionFromJson('deleteProcessName', process_name, 'file');
|
||||
return CLI.actionFromJson('deleteProcessId', process_name, 'file');
|
||||
else if (process_name == 'all') {
|
||||
printOut(cst.PREFIX_MSG + 'Stopping and deleting all processes');
|
||||
Satan.executeRemote('deleteAll', {}, function(err, list) {
|
||||
Common.getAllProcessId(function(err, ids) {
|
||||
if (err) {
|
||||
printError(err);
|
||||
return cb ? cb({msg:err}) : exitCli(cst.ERROR_EXIT);
|
||||
}
|
||||
return cb ? cb(null, list) : speedList();
|
||||
async.eachLimit(ids, 1, function(id, next) {
|
||||
printOut(cst.PREFIX_MSG + 'Deleting process id %d', id);
|
||||
Satan.executeRemote('deleteProcessId', id, function(err, list) {
|
||||
return next();
|
||||
});
|
||||
}, function(err) {
|
||||
return cb ? cb(null, ids) : speedList();
|
||||
});
|
||||
return false;
|
||||
});
|
||||
}
|
||||
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();
|
||||
@ -998,21 +1092,22 @@ 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');
|
||||
process.stdin.on('data', function (param) {
|
||||
process.stdin.pause();
|
||||
CLI.actionFromJson('stopProcessName', param, 'pipe', cb);
|
||||
CLI.actionFromJson('stopProcessId', param, 'pipe', cb);
|
||||
});
|
||||
} else if (process_name.indexOf('.json') > 0)
|
||||
CLI.actionFromJson('stopProcessName', process_name, 'file', cb);
|
||||
CLI.actionFromJson('stopProcessId', process_name, 'file', cb);
|
||||
else if (process_name == 'all')
|
||||
CLI._stopAll(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);
|
||||
}
|
||||
};
|
||||
@ -1025,9 +1120,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();
|
||||
});
|
||||
};
|
||||
@ -1041,7 +1137,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);
|
||||
@ -1058,10 +1154,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();
|
||||
});
|
||||
};
|
||||
@ -1139,6 +1235,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);
|
||||
});
|
||||
};
|
||||
@ -1310,7 +1407,7 @@ CLI.ilogs = function() {
|
||||
});
|
||||
} catch(e) {
|
||||
printOut('pm2-logs module is not installed');
|
||||
fallbackLogStream(id);
|
||||
fallbackLogStream();
|
||||
}
|
||||
};
|
||||
|
||||
@ -1321,35 +1418,16 @@ 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);
|
||||
}
|
||||
this.delete('all', 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;
|
||||
@ -1408,6 +1486,7 @@ function speedList() {
|
||||
* @return
|
||||
*/
|
||||
function getInteractInfo(cb) {
|
||||
debug('Getting interaction info');
|
||||
InteractorDaemonizer.ping(function(online) {
|
||||
if (!online) {
|
||||
return cb({msg : 'offline'});
|
||||
@ -1417,7 +1496,10 @@ function getInteractInfo(cb) {
|
||||
if (err) {
|
||||
return cb(err);
|
||||
}
|
||||
return cb(null, infos);
|
||||
InteractorDaemonizer.disconnectRPC(function() {
|
||||
return cb(null, infos);
|
||||
});
|
||||
return false;
|
||||
});
|
||||
});
|
||||
return false;
|
||||
|
||||
@ -59,6 +59,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 },
|
||||
@ -84,6 +85,8 @@ UX.dispAsTable = function(list, interact_infos) {
|
||||
style : {'padding-left' : 1, head : ['cyan', 'bold'], border : ['white'], compact : true}
|
||||
});
|
||||
|
||||
if (!list)
|
||||
return console.log('list empty');
|
||||
list.forEach(function(l) {
|
||||
var obj = {};
|
||||
|
||||
|
||||
@ -11,7 +11,10 @@ var cst = require('../constants.js');
|
||||
var extItps = require('./interpreter.json');
|
||||
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)
|
||||
*/
|
||||
@ -33,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.
|
||||
@ -43,6 +51,7 @@ Common.resolveAppPaths = function(app, cwd, outputter) {
|
||||
util._extend(app.env, env);
|
||||
|
||||
app.env.pm_cwd = cwd;
|
||||
app.pm_cwd = cwd;
|
||||
|
||||
if (!app.exec_interpreter) {
|
||||
if (extItps[path.extname(app.script)]) {
|
||||
@ -60,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);
|
||||
}
|
||||
@ -71,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;
|
||||
@ -82,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;
|
||||
@ -90,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;
|
||||
@ -103,6 +123,10 @@ Common.resolveAppPaths = function(app, cwd, outputter) {
|
||||
return app;
|
||||
};
|
||||
|
||||
Common.deepCopy = Common.serialize = function serialize(data) {
|
||||
return JSON.parse(Stringify(data));
|
||||
};
|
||||
|
||||
/**
|
||||
* Description
|
||||
* @method validateApp
|
||||
@ -142,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);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
@ -169,3 +195,41 @@ Common.printOut = function() {
|
||||
if (process.env.PM2_SILENT) return false;
|
||||
return console.log.apply(console, arguments);
|
||||
};
|
||||
|
||||
|
||||
Common.getAllProcessId = function(cb) {
|
||||
var found_proc = [];
|
||||
|
||||
Satan.executeRemote('getMonitorData', {}, function(err, list) {
|
||||
if (err) {
|
||||
Common.printError('Error retrieving process list: ' + err);
|
||||
return cb(err);
|
||||
}
|
||||
|
||||
list.forEach(function(proc) {
|
||||
found_proc.push(proc.pm_id);
|
||||
});
|
||||
|
||||
return cb(null, found_proc);
|
||||
});
|
||||
};
|
||||
|
||||
Common.getProcessIdByName = function(name, cb) {
|
||||
var found_proc = [];
|
||||
|
||||
Satan.executeRemote('getMonitorData', {}, function(err, list) {
|
||||
if (err) {
|
||||
Common.printError('Error retrieving process list: ' + err);
|
||||
return cb(err);
|
||||
}
|
||||
|
||||
list.forEach(function(proc) {
|
||||
if (proc.pm2_env.name == name ||
|
||||
proc.pm2_env.pm_exec_path == p.resolve(name)) {
|
||||
found_proc.push(proc.pm_id);
|
||||
}
|
||||
});
|
||||
|
||||
return cb(null, found_proc);
|
||||
});
|
||||
};
|
||||
|
||||
186
lib/God.js
186
lib/God.js
@ -1,8 +1,3 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies
|
||||
*/
|
||||
|
||||
var cluster = require('cluster');
|
||||
var numCPUs = require('os').cpus() ? require('os').cpus().length : 1;
|
||||
@ -13,6 +8,14 @@ var fs = require('fs');
|
||||
var p = path;
|
||||
var Common = require('./Common');
|
||||
var cst = require('../constants.js');
|
||||
var pidusage = require('pidusage');
|
||||
|
||||
// require('webkit-devtools-agent').start({
|
||||
// port: 9999,
|
||||
// bind_to: '0.0.0.0',
|
||||
// ipc_port: 3333,
|
||||
// verbose: true
|
||||
// });
|
||||
|
||||
/**
|
||||
* Override cluster module configuration
|
||||
@ -28,10 +31,9 @@ var God = module.exports = {
|
||||
next_id : 0,
|
||||
clusters_db : {},
|
||||
bus : new EventEmitter2({
|
||||
wildcard: true,
|
||||
wildcard: false,
|
||||
delimiter: ':',
|
||||
newListener: false,
|
||||
maxListeners: 20
|
||||
maxListeners: 1000
|
||||
})
|
||||
};
|
||||
|
||||
@ -44,22 +46,6 @@ require('./God/ClusterMode.js')(God);
|
||||
require('./God/Reload')(God);
|
||||
require('./God/ActionMethods')(God);
|
||||
|
||||
/**
|
||||
* Forced entry to initialize cluster monitoring
|
||||
*/
|
||||
|
||||
(function initEngine() {
|
||||
cluster.on('online', function(clu) {
|
||||
console.log('%s - id%d worker online', clu.pm2_env.pm_exec_path, clu.pm2_env.pm_id);
|
||||
clu.pm2_env.status = cst.ONLINE_STATUS;
|
||||
God.bus.emit('process:online', { process : clu });
|
||||
});
|
||||
|
||||
cluster.on('exit', function(clu, code, signal) {
|
||||
handleExit(clu, code);
|
||||
});
|
||||
})();
|
||||
|
||||
/**
|
||||
* Handle logic when a process exit (Node or Fork)
|
||||
* @method handleExit
|
||||
@ -67,24 +53,34 @@ require('./God/ActionMethods')(God);
|
||||
* @param {} exit_code
|
||||
* @return
|
||||
*/
|
||||
function handleExit(clu, exit_code) {
|
||||
console.log('Script %s %s exited code %d', clu.pm2_env.pm_exec_path, clu.pm2_env.pm_id, exit_code);
|
||||
God.handleExit = function handleExit(clu, exit_code) {
|
||||
console.log('Script %s %s exit', clu.pm2_env.pm_exec_path, clu.pm2_env.pm_id);
|
||||
|
||||
var stopping = (clu.pm2_env.status == cst.STOPPING_STATUS || clu.pm2_env.status == cst.ERRORED_STATUS) ? true : false;
|
||||
var proc = this.clusters_db[clu.pm2_env.pm_id];
|
||||
|
||||
pidusage.unmonitor(proc.process.pid);
|
||||
|
||||
if (!proc) {
|
||||
console.error('Process undefined ? with process id ', clu.pm2_env.pm_id);
|
||||
return false;
|
||||
}
|
||||
|
||||
var stopping = (proc.pm2_env.status == cst.STOPPING_STATUS || proc.pm2_env.status == cst.ERRORED_STATUS) ? true : false;
|
||||
var overlimit = false;
|
||||
var pidFile = clu.pm2_env.pm_pid_path;
|
||||
|
||||
if (stopping) clu.process.pid = 0;
|
||||
if (stopping) proc.process.pid = 0;
|
||||
|
||||
if (clu.pm2_env.axm_actions) clu.pm2_env.axm_actions = [];
|
||||
if (proc.pm2_env.axm_actions) proc.pm2_env.axm_actions = [];
|
||||
|
||||
if (clu.pm2_env.status != cst.ERRORED_STATUS &&
|
||||
clu.pm2_env.status != cst.STOPPING_STATUS)
|
||||
clu.pm2_env.status = cst.STOPPED_STATUS;
|
||||
if (proc.pm2_env.status != cst.ERRORED_STATUS &&
|
||||
proc.pm2_env.status != cst.STOPPING_STATUS)
|
||||
proc.pm2_env.status = cst.STOPPED_STATUS;
|
||||
|
||||
try {
|
||||
fs.unlinkSync(pidFile);
|
||||
} catch(e) { console.error('Error when unlinking PID file', e); }
|
||||
fs.unlinkSync(proc.pm2_env.pm_pid_path);
|
||||
} catch (e) {
|
||||
console.error('Error when unlinking PID file', e);
|
||||
}
|
||||
|
||||
/**
|
||||
* Avoid infinite reloop if an error is present
|
||||
@ -92,37 +88,42 @@ function handleExit(clu, exit_code) {
|
||||
// If the process has been created less than 15seconds ago
|
||||
|
||||
// And if the process has an uptime less than a second
|
||||
var min_uptime = (clu.pm2_env.min_uptime || 1000);
|
||||
var max_restarts = (clu.pm2_env.max_restarts || 15);
|
||||
var min_uptime = typeof(proc.pm2_env.min_uptime) !== 'undefined' ? proc.pm2_env.min_uptime : 1000;
|
||||
var max_restarts = typeof(proc.pm2_env.max_restarts) !== 'undefined' ? proc.pm2_env.max_restarts : 15;
|
||||
|
||||
if ((Date.now() - clu.pm2_env.created_at) < (min_uptime * max_restarts)) {
|
||||
if ((Date.now() - clu.pm2_env.pm_uptime) < min_uptime) {
|
||||
if ((Date.now() - proc.pm2_env.created_at) < (min_uptime * max_restarts)) {
|
||||
if ((Date.now() - proc.pm2_env.pm_uptime) < min_uptime) {
|
||||
// Increment unstable restart
|
||||
clu.pm2_env.unstable_restarts += 1;
|
||||
proc.pm2_env.unstable_restarts += 1;
|
||||
}
|
||||
|
||||
if (clu.pm2_env.unstable_restarts >= max_restarts) {
|
||||
if (proc.pm2_env.unstable_restarts >= max_restarts) {
|
||||
// Too many unstable restart in less than 15 seconds
|
||||
// Set the process as 'ERRORED'
|
||||
// And stop to restart it
|
||||
clu.pm2_env.status = cst.ERRORED_STATUS;
|
||||
console.log('Script %s had too many unstable restarts (%d). Stopped.',
|
||||
clu.pm2_env.pm_exec_path,
|
||||
clu.pm2_env.unstable_restarts);
|
||||
God.bus.emit('process:exit:overlimit', { process : clu });
|
||||
clu.pm2_env.unstable_restarts = 0;
|
||||
clu.pm2_env.created_at = null;
|
||||
proc.pm2_env.status = cst.ERRORED_STATUS;
|
||||
|
||||
console.log('Script %s had too many unstable restarts (%d). Stopped. %j',
|
||||
proc.pm2_env.pm_exec_path,
|
||||
proc.pm2_env.unstable_restarts,
|
||||
proc.pm2_env.status);
|
||||
this.bus.emit('process:exit:overlimit', { process : Common.serialize(proc) });
|
||||
proc.pm2_env.unstable_restarts = 0;
|
||||
proc.pm2_env.created_at = null;
|
||||
overlimit = true;
|
||||
}
|
||||
}
|
||||
|
||||
God.bus.emit('process:exit', { process : clu });
|
||||
this.bus.emit('process:exit', { process : Common.serialize(proc) });
|
||||
|
||||
if (!stopping)
|
||||
clu.pm2_env.restart_time = clu.pm2_env.restart_time + 1;
|
||||
proc.pm2_env.restart_time = proc.pm2_env.restart_time + 1;
|
||||
|
||||
if (!stopping && !overlimit) God.executeApp(clu.pm2_env);
|
||||
}
|
||||
if (!stopping && !overlimit)
|
||||
this.executeApp(proc.pm2_env);
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
@ -133,10 +134,10 @@ function handleExit(clu, exit_code) {
|
||||
* @param {Function} cb
|
||||
* @return Literal
|
||||
*/
|
||||
God.executeApp = function(env, cb) {
|
||||
var env_copy = JSON.parse(JSON.stringify(env));
|
||||
God.executeApp = function executeApp(env, cb) {
|
||||
var env_copy = Common.serialize(env);
|
||||
|
||||
util._extend(env_copy, env.env);
|
||||
util._extend(env_copy, env_copy.env);
|
||||
|
||||
env_copy['axm_actions'] = [];
|
||||
|
||||
@ -162,7 +163,7 @@ God.executeApp = function(env, cb) {
|
||||
}
|
||||
|
||||
if (env_copy['watch']) {
|
||||
env_copy['watcher'] = require('./Watcher').watch(env_copy);
|
||||
env_copy['watcher'] = require('./Watcher').watch(env_copy);
|
||||
}
|
||||
}
|
||||
|
||||
@ -176,22 +177,30 @@ God.executeApp = function(env, cb) {
|
||||
/**
|
||||
* Fork mode logic
|
||||
*/
|
||||
God.forkMode(env_copy, function(err, clu) {
|
||||
God.forkMode(env_copy, function forkMode(err, clu) {
|
||||
if (cb && err) return cb(err);
|
||||
if (err) return false;
|
||||
|
||||
God.clusters_db[env_copy.pm_id] = clu;
|
||||
var old_env = God.clusters_db[clu.pm2_env.pm_id];
|
||||
if (old_env) old_env = null;
|
||||
|
||||
clu.once('error', function(err) {
|
||||
clu.pm2_env.status = cst.ERRORED_STATUS;
|
||||
var proc = God.clusters_db[env_copy.pm_id] = clu;
|
||||
|
||||
clu.once('error', function cluError(err) {
|
||||
proc.pm2_env.status = cst.ERRORED_STATUS;
|
||||
return false;
|
||||
});
|
||||
|
||||
clu.once('close', function(code) {
|
||||
handleExit(clu, code);
|
||||
clu.once('close', function cluClose(code) {
|
||||
proc.removeAllListeners();
|
||||
clu._reloadLogs = null;
|
||||
God.handleExit(proc, code);
|
||||
return false;
|
||||
});
|
||||
|
||||
God.bus.emit('process:online', {process : clu });
|
||||
God.bus.emit('process:online', {process : Common.serialize(proc)});
|
||||
|
||||
if (cb) cb(null, clu);
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
@ -199,11 +208,44 @@ God.executeApp = function(env, cb) {
|
||||
/**
|
||||
* Cluster mode logic (for NodeJS apps)
|
||||
*/
|
||||
God.nodeApp(env_copy, function(err, clu) {
|
||||
God.nodeApp(env_copy, function nodeApp(err, clu) {
|
||||
if (cb && err) return cb(err);
|
||||
if (err) return false;
|
||||
God.clusters_db[clu.pm2_env.pm_id] = clu;
|
||||
if (cb) cb(null, clu);
|
||||
|
||||
var old_env = God.clusters_db[clu.pm2_env.pm_id];
|
||||
|
||||
if (old_env) {
|
||||
old_env = null;
|
||||
|
||||
if (typeof(God.clusters_db[clu.pm2_env.pm_id].process._handle) !== 'undefined') {
|
||||
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].process = null;
|
||||
}
|
||||
God.clusters_db[clu.pm2_env.pm_id] = null;
|
||||
}
|
||||
|
||||
var proc = God.clusters_db[clu.pm2_env.pm_id] = clu;
|
||||
|
||||
clu.once('online', function cluOnline() {
|
||||
proc.pm2_env.status = cst.ONLINE_STATUS;
|
||||
|
||||
console.log('%s - id%d worker online', proc.pm2_env.pm_exec_path, proc.pm2_env.pm_id);
|
||||
|
||||
God.bus.emit('process:online', { process : Common.serialize(proc) });
|
||||
|
||||
if (cb) return cb(null, proc);
|
||||
return false;
|
||||
});
|
||||
|
||||
clu.once('exit', function cluExit(exited_clu, code) {
|
||||
proc.removeAllListeners();
|
||||
proc.process.removeAllListeners();
|
||||
God.handleExit(proc, code);
|
||||
return false;
|
||||
});
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
@ -220,7 +262,7 @@ God.executeApp = function(env, cb) {
|
||||
* @param {} cb
|
||||
* @return Literal
|
||||
*/
|
||||
God.prepare = function(env, cb) {
|
||||
God.prepare = function prepare(env, cb) {
|
||||
// If instances option is set (-i [arg])
|
||||
if (env.instances) {
|
||||
if (env.instances == 'max') env.instances = numCPUs;
|
||||
@ -233,7 +275,8 @@ God.prepare = function(env, cb) {
|
||||
if (cb != null) return cb(null, arr);
|
||||
return false;
|
||||
}
|
||||
return God.executeApp(JSON.parse(JSON.stringify(env)), function(err, clu) { // deep copy
|
||||
|
||||
return God.executeApp(Common.serialize(env), function(err, clu) {
|
||||
if (err) return ex(i - 1);
|
||||
arr.push(clu);
|
||||
return ex(i - 1);
|
||||
@ -242,7 +285,7 @@ God.prepare = function(env, cb) {
|
||||
}
|
||||
else {
|
||||
return God.executeApp(env, function(err, dt) {
|
||||
cb(err, [dt]);
|
||||
cb(err, [Common.serialize(dt)]);
|
||||
});
|
||||
}
|
||||
return false;
|
||||
@ -259,13 +302,14 @@ God.prepare = function(env, cb) {
|
||||
* @param cb {Function}
|
||||
* @return CallExpression
|
||||
*/
|
||||
God.prepareJson = function (app, cwd, cb) {
|
||||
God.prepareJson = function prepareJson(app, cwd, cb) {
|
||||
if (!cb) {
|
||||
cb = cwd;
|
||||
cwd = undefined;
|
||||
}
|
||||
|
||||
app = Common.resolveAppPaths(app, cwd);
|
||||
|
||||
if (app instanceof Error)
|
||||
return cb(app);
|
||||
|
||||
|
||||
@ -14,6 +14,10 @@ var p = path;
|
||||
var cst = require('../../constants.js');
|
||||
var pkg = require('../../package.json');
|
||||
var pidusage = require('pidusage');
|
||||
var Common = require('../Common');
|
||||
var util = require('util');
|
||||
|
||||
var debug = require('debug')('pm2:ActionMethod');
|
||||
|
||||
/**
|
||||
* Description
|
||||
@ -30,42 +34,43 @@ module.exports = function(God) {
|
||||
* @param {} cb
|
||||
* @return
|
||||
*/
|
||||
God.getMonitorData = function(env, cb) {
|
||||
God.getMonitorData = function getMonitorData(env, cb) {
|
||||
var processes = God.getFormatedProcesses();
|
||||
var arr = [];
|
||||
|
||||
async.mapLimit(processes, 6, function(pro, next) {
|
||||
|
||||
async.map(processes, function computeMonitor(pro, next) {
|
||||
if (pro.pm2_env.status != cst.STOPPED_STATUS &&
|
||||
pro.pm2_env.status != cst.STOPPING_STATUS &&
|
||||
pro.pm2_env.status != cst.ERRORED_STATUS) {
|
||||
|
||||
try {
|
||||
pidusage(pro.pid, function(err, res) {
|
||||
if (err)
|
||||
return next(err);
|
||||
|
||||
pidusage.stat(pro.pid, function retPidUsage(err, res) {
|
||||
if (err) {
|
||||
pro['monit'] = {
|
||||
memory : Math.floor(res.memory),
|
||||
cpu : Math.floor(res.cpu)
|
||||
memory : 0,
|
||||
cpu : 0
|
||||
};
|
||||
return next(null, pro);
|
||||
});
|
||||
}
|
||||
|
||||
} catch(e) {
|
||||
God.logAndGenerateError(e);
|
||||
pro['monit'] = {memory : 0, cpu : 0};
|
||||
pro['monit'] = {
|
||||
memory : Math.floor(res.memory),
|
||||
cpu : Math.floor(res.cpu)
|
||||
};
|
||||
res = null;
|
||||
return next(null, pro);
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
else {
|
||||
pro['monit'] = {memory : 0, cpu : 0};
|
||||
pro['monit'] = {
|
||||
memory : 0,
|
||||
cpu : 0
|
||||
};
|
||||
return next(null, pro);
|
||||
}
|
||||
return false;
|
||||
}, function(err, res) {
|
||||
}, function retMonitor(err, res) {
|
||||
if (err) return cb(God.logAndGenerateError(err), null);
|
||||
processes = null;
|
||||
|
||||
return cb(null, res);
|
||||
});
|
||||
|
||||
@ -78,7 +83,7 @@ module.exports = function(God) {
|
||||
* @param {} cb
|
||||
* @return
|
||||
*/
|
||||
God.getSystemData = function(env, cb) {
|
||||
God.getSystemData = function getSystemData(env, cb) {
|
||||
God.getMonitorData(env, function(err, processes) {
|
||||
cb(err, {
|
||||
system: {
|
||||
@ -142,7 +147,7 @@ module.exports = function(God) {
|
||||
God.startProcessId = function(id, cb) {
|
||||
if (!(id in God.clusters_db))
|
||||
return cb(God.logAndGenerateError(id + ' id unknown'), {});
|
||||
God.bus.emit('process:start', { process : God.clusters_db[id] });
|
||||
God.bus.emit('process:start', { process : Common.serialize(God.clusters_db[id]) });
|
||||
if (God.clusters_db[id].pm2_env.status == cst.ONLINE_STATUS)
|
||||
return cb(God.logAndGenerateError('process already online'), {});
|
||||
return God.executeApp(God.clusters_db[id].pm2_env, cb);
|
||||
@ -162,13 +167,18 @@ module.exports = function(God) {
|
||||
if (God.clusters_db[id].pm2_env.status == cst.STOPPED_STATUS)
|
||||
return cb(null, God.getFormatedProcesses());
|
||||
|
||||
God.bus.emit('process:stop', { process : God.clusters_db[id] });
|
||||
God.bus.emit('process:stop', { process : Common.serialize(God.clusters_db[id]) });
|
||||
|
||||
var proc = God.clusters_db[id];
|
||||
var timeout = null;
|
||||
|
||||
proc.pm2_env.status = cst.STOPPING_STATUS;
|
||||
|
||||
if (!proc.process.pid) {
|
||||
proc.pm2_env.status = cst.STOPPED_STATUS;
|
||||
return cb(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process to stop on cluster mode
|
||||
*/
|
||||
@ -177,13 +187,15 @@ module.exports = function(God) {
|
||||
proc.state != 'dead') {
|
||||
|
||||
proc.once('disconnect', function(){
|
||||
delete cluster.workers[proc.id];
|
||||
cluster.workers[proc.id] = null;
|
||||
|
||||
clearTimeout(timeout);
|
||||
God.killProcess(proc.process.pid, function() {
|
||||
return setTimeout(function() {
|
||||
setTimeout(function() {
|
||||
proc.pm2_env.status = cst.STOPPED_STATUS;
|
||||
cb(null, God.getFormatedProcesses());
|
||||
}, 100);
|
||||
return false;
|
||||
});
|
||||
return false;
|
||||
});
|
||||
@ -237,6 +249,20 @@ module.exports = function(God) {
|
||||
return false;
|
||||
};
|
||||
|
||||
God.resetMetaProcessId = function(id, cb) {
|
||||
if (!(id in God.clusters_db))
|
||||
return cb(God.logAndGenerateError(id + ' id unknown'), {});
|
||||
|
||||
if (!God.clusters_db[id] || !God.clusters_db[id].pm2_env)
|
||||
return cb(God.logAndGenerateError('Error when getting proc || proc.pm2_env'), {});
|
||||
|
||||
God.clusters_db[id].pm2_env.created_at = Date.now();
|
||||
God.clusters_db[id].pm2_env.unstable_restarts = 0;
|
||||
God.clusters_db[id].pm2_env.restart_time = 0;
|
||||
|
||||
return cb(null, God.getFormatedProcesses());
|
||||
};
|
||||
|
||||
/**
|
||||
* Delete a process by id
|
||||
* It will stop it and remove it from the database
|
||||
@ -247,10 +273,11 @@ module.exports = function(God) {
|
||||
*/
|
||||
God.deleteProcessId = function(id, cb) {
|
||||
if (God.clusters_db[id])
|
||||
God.bus.emit('process:delete', { process : God.clusters_db[id] });
|
||||
God.bus.emit('process:delete', { process : Common.serialize(God.clusters_db[id]) });
|
||||
|
||||
God.stopProcessId(id, function(err, dt) {
|
||||
if (err) return cb(God.logAndGenerateError(err), {});
|
||||
// ! transform to slow object
|
||||
delete God.clusters_db[id];
|
||||
return cb(null, God.getFormatedProcesses());
|
||||
});
|
||||
@ -266,21 +293,27 @@ module.exports = function(God) {
|
||||
* @param {} cb
|
||||
* @return Literal
|
||||
*/
|
||||
God.restartProcessId = function(id, cb) {
|
||||
God.restartProcessId = function(opts, cb) {
|
||||
var id = opts.id;
|
||||
var env = opts.env;
|
||||
|
||||
if (!(id in God.clusters_db))
|
||||
return cb(God.logAndGenerateError(id + ' id unknown'), {});
|
||||
|
||||
var proc = God.clusters_db[id];
|
||||
|
||||
God.bus.emit('process:restart', { process : proc });
|
||||
God.bus.emit('process:restart', { process : Common.serialize(proc) });
|
||||
|
||||
God.resetState(proc.pm2_env);
|
||||
|
||||
util._extend(proc.pm2_env.env, opts.env);
|
||||
|
||||
if (proc.pm2_env.status == cst.ONLINE_STATUS) {
|
||||
God.killProcess(proc.process.pid, function() {
|
||||
return setTimeout(function() {
|
||||
setTimeout(function() {
|
||||
return cb(null, God.getFormatedProcesses());
|
||||
}, 100);
|
||||
return false;
|
||||
});
|
||||
}
|
||||
else
|
||||
@ -351,12 +384,11 @@ module.exports = function(God) {
|
||||
|
||||
var proc = God.clusters_db[id];
|
||||
|
||||
God.bus.emit('process:send_signal', { process : proc });
|
||||
God.bus.emit('process:send_signal', { process : Common.serialize(proc) });
|
||||
|
||||
try {
|
||||
process.kill(God.clusters_db[id].process.pid, signal);
|
||||
} catch(e) {
|
||||
console.error(e);
|
||||
return cb(God.logAndGenerateError('Error when sending signal (signal unknown)'), {});
|
||||
}
|
||||
return cb(null, God.getFormatedProcesses());
|
||||
@ -408,6 +440,7 @@ module.exports = function(God) {
|
||||
|
||||
async.eachLimit(processes, cst.CONCURRENT_ACTIONS, function(proc, next) {
|
||||
God.stopProcessId(proc.pm2_env.pm_id, function() {
|
||||
// Slow object
|
||||
delete God.clusters_db[proc.pm2_env.pm_id];
|
||||
return next();
|
||||
});
|
||||
@ -432,7 +465,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();
|
||||
});
|
||||
@ -454,17 +489,17 @@ module.exports = function(God) {
|
||||
* @return
|
||||
*/
|
||||
God.killMe = function(env, cb) {
|
||||
God.deleteAll({}, function(err, processes) {
|
||||
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);
|
||||
};
|
||||
|
||||
|
||||
@ -542,10 +577,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);
|
||||
});
|
||||
|
||||
@ -10,6 +10,7 @@ var cluster = require('cluster');
|
||||
var fs = require('fs');
|
||||
var cst = require('../../constants.js');
|
||||
var util = require('util');
|
||||
var Common = require('../Common');
|
||||
|
||||
/**
|
||||
* Description
|
||||
@ -17,7 +18,7 @@ var util = require('util');
|
||||
* @param {} God
|
||||
* @return
|
||||
*/
|
||||
module.exports = function(God) {
|
||||
module.exports = function ClusterMode(God) {
|
||||
|
||||
/**
|
||||
* For Node apps - Cluster mode
|
||||
@ -27,55 +28,50 @@ module.exports = function(God) {
|
||||
* @param {} cb
|
||||
* @return Literal
|
||||
*/
|
||||
God.nodeApp = function(env_copy, cb){
|
||||
var clu;
|
||||
God.nodeApp = function nodeApp(env_copy, cb){
|
||||
var clu = null;
|
||||
|
||||
if (fs.existsSync(env_copy.pm_exec_path) == false) {
|
||||
console.error('Script ' + env_copy.pm_exec_path + ' missing');
|
||||
return cb(God.logAndGenerateError('Script ' + env_copy.pm_exec_path + ' missing'), {});
|
||||
}
|
||||
// if (fs.existsSync(env_copy.pm_exec_path) == false) {
|
||||
// return cb(God.logAndGenerateError('Script ' + env_copy.pm_exec_path + ' missing'), {});
|
||||
// }
|
||||
|
||||
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 {
|
||||
clu = cluster.fork(env_copy);
|
||||
} catch(e) { return God.logAndGenerateError(e); }
|
||||
} catch(e) {
|
||||
God.logAndGenerateError(e);
|
||||
return cb(e);
|
||||
}
|
||||
|
||||
clu.pm2_env = env_copy;
|
||||
God.clusters_db[env_copy.pm_id] = clu;
|
||||
|
||||
// Receive message from child
|
||||
clu.on('message', function(msg) {
|
||||
clu.on('message', function cluMessage(msg) {
|
||||
var proc_data = Common.serialize(clu);
|
||||
|
||||
switch (msg.type) {
|
||||
case 'process:exception':
|
||||
God.bus.emit('process:exception', {process : clu, data : msg, err : msg.err});
|
||||
God.bus.emit('process:exception', {process : proc_data, data : msg, err : msg.err});
|
||||
break;
|
||||
case 'log:out':
|
||||
God.bus.emit('log:out', {process : clu, data : msg.data});
|
||||
God.bus.emit('log:out', {process : proc_data, data : msg.data});
|
||||
break;
|
||||
case 'log:err':
|
||||
God.bus.emit('log:err', {process : clu, data : msg.data});
|
||||
God.bus.emit('log:err', {process : proc_data, data : msg.data});
|
||||
break;
|
||||
case 'human_event':
|
||||
God.bus.emit('human_event', {process : clu, data : util._extend(msg, {type:msg.name})});
|
||||
God.bus.emit('human_event', {process : proc_data, data : util._extend(msg, {type:msg.name})});
|
||||
break;
|
||||
default: // Permits to send message to external from the app
|
||||
God.bus.emit(msg.type ? msg.type : 'process:msg', {process : clu, data : msg });
|
||||
God.bus.emit(msg.type ? msg.type : 'process:msg', {process : proc_data, data : msg });
|
||||
}
|
||||
});
|
||||
|
||||
// Avoid circular dependency
|
||||
delete clu.process._handle.owner;
|
||||
|
||||
clu.once('online', function() {
|
||||
clu.pm2_env.status = cst.ONLINE_STATUS;
|
||||
if (cb) return cb(null, clu);
|
||||
return false;
|
||||
});
|
||||
return false;
|
||||
return cb(null, clu);
|
||||
};
|
||||
};
|
||||
|
||||
@ -11,14 +11,14 @@ var fs = require('fs');
|
||||
var cst = require('../../constants.js');
|
||||
var uidNumber = require('uid-number');
|
||||
var moment = require('moment');
|
||||
|
||||
var Common = require('../Common');
|
||||
/**
|
||||
* Description
|
||||
* @method exports
|
||||
* @param {} God
|
||||
* @return
|
||||
*/
|
||||
module.exports = function(God) {
|
||||
module.exports = function ForkMode(God) {
|
||||
/**
|
||||
* For all apps - FORK MODE
|
||||
* fork the app
|
||||
@ -27,10 +27,11 @@ module.exports = function(God) {
|
||||
* @param {} cb
|
||||
* @return
|
||||
*/
|
||||
God.forkMode = function(pm2_env, cb) {
|
||||
var command, args;
|
||||
God.forkMode = function forkMode(pm2_env, cb) {
|
||||
var command = '';
|
||||
var args = [];
|
||||
|
||||
log('Entering in fork mode');
|
||||
console.log('Entering in fork mode');
|
||||
var spawn = require('child_process').spawn;
|
||||
|
||||
var interpreter = pm2_env.exec_interpreter || 'node';
|
||||
@ -38,16 +39,26 @@ module.exports = function(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,93 +86,109 @@ module.exports = function(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){
|
||||
console.error(e.stack || e);
|
||||
if (cb) return cb(e);
|
||||
}
|
||||
if(e){
|
||||
God.logAndGenerateError(e);
|
||||
if (cb) return cb(e);
|
||||
}
|
||||
|
||||
try {
|
||||
var cspr = spawn(command, args, {
|
||||
env : pm2_env,
|
||||
detached : true,
|
||||
gid : gid,
|
||||
uid : uid,
|
||||
cwd : pm2_env.pm_cwd || process.cwd(),
|
||||
stdio : ['ipc', null, null]
|
||||
});
|
||||
} catch(e) {
|
||||
console.error(e.stack || e);
|
||||
if (cb) return cb(e);
|
||||
}
|
||||
|
||||
cspr.process = {};
|
||||
cspr.process.pid = cspr.pid;
|
||||
cspr.pm2_env = pm2_env;
|
||||
cspr.pm2_env.status = cst.ONLINE_STATUS;
|
||||
|
||||
cspr.stderr.on('data', function(data) {
|
||||
|
||||
var log_data = data.toString();
|
||||
if (pm2_env.log_date_format)
|
||||
log_data = moment().format(pm2_env.log_date_format) + ': ' + log_data;
|
||||
|
||||
stderr.write(log_data);
|
||||
|
||||
God.bus.emit('log:err', {
|
||||
process : cspr,
|
||||
data : data.toString()
|
||||
});
|
||||
try {
|
||||
var cspr = spawn(command, args, {
|
||||
env : pm2_env,
|
||||
detached : true,
|
||||
gid : gid,
|
||||
uid : uid,
|
||||
cwd : pm2_env.pm_cwd || process.cwd(),
|
||||
stdio : ['ipc', null, null]
|
||||
});
|
||||
} catch(e) {
|
||||
God.logAndGenerateError(e);
|
||||
if (cb) return cb(e);
|
||||
}
|
||||
|
||||
cspr.stdout.on('data', function(data) {
|
||||
|
||||
var log_data = data.toString();
|
||||
if (pm2_env.log_date_format)
|
||||
log_data = moment().format(pm2_env.log_date_format) + ': ' + log_data;
|
||||
cspr.process = {};
|
||||
cspr.process.pid = cspr.pid;
|
||||
cspr.pm2_env = pm2_env;
|
||||
cspr.pm2_env.status = cst.ONLINE_STATUS;
|
||||
|
||||
stdout.write(log_data);
|
||||
cspr.stderr.on('data', function forkErrData(data) {
|
||||
|
||||
God.bus.emit('log:out', {
|
||||
process : cspr,
|
||||
data : data.toString()
|
||||
});
|
||||
var log_data = data.toString();
|
||||
if (pm2_env.log_date_format)
|
||||
log_data = moment().format(pm2_env.log_date_format) + ': ' + log_data;
|
||||
|
||||
stderr.write(log_data);
|
||||
|
||||
God.bus.emit('log:err', {
|
||||
process : Common.serialize(cspr),
|
||||
data : data.toString()
|
||||
});
|
||||
|
||||
cspr.on('message', function(data) {
|
||||
God.bus.emit(data.type ? data.type : 'process:msg', {
|
||||
process : cspr,
|
||||
data : data
|
||||
});
|
||||
});
|
||||
|
||||
fs.writeFileSync(pidFile, cspr.pid);
|
||||
|
||||
cspr.once('close', function(status) {
|
||||
try {
|
||||
stderr.close();
|
||||
stdout.close();
|
||||
} catch(e) { console.error(e.stack || e);}
|
||||
});
|
||||
|
||||
cspr._reloadLogs = startLogging;
|
||||
|
||||
|
||||
cspr.unref();
|
||||
|
||||
if (cb) return cb(null, cspr);
|
||||
return false;
|
||||
});
|
||||
|
||||
cspr.stdout.on('data', function forkOutData(data) {
|
||||
|
||||
var log_data = data.toString();
|
||||
if (pm2_env.log_date_format)
|
||||
log_data = moment().format(pm2_env.log_date_format) + ': ' + log_data;
|
||||
|
||||
stdout.write(log_data);
|
||||
|
||||
God.bus.emit('log:out', {
|
||||
process : Common.serialize(cspr),
|
||||
data : data.toString()
|
||||
});
|
||||
});
|
||||
|
||||
cspr.on('message', function forkMessage(data) {
|
||||
God.bus.emit(data.type ? data.type : 'process:msg', {
|
||||
process : Common.serialize(cspr),
|
||||
data : data
|
||||
});
|
||||
});
|
||||
|
||||
fs.writeFileSync(pidFile, cspr.pid);
|
||||
|
||||
cspr.once('close', function forkClose(status) {
|
||||
try {
|
||||
stderr.close();
|
||||
stdout.close();
|
||||
} catch(e) { God.logAndGenerateError(e);}
|
||||
});
|
||||
|
||||
cspr._reloadLogs = startLogging;
|
||||
|
||||
cspr.unref();
|
||||
|
||||
if (cb) return cb(null, cspr);
|
||||
return false;
|
||||
});
|
||||
return false;
|
||||
});
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
* @project PM2
|
||||
*/
|
||||
var p = require('path');
|
||||
|
||||
var Common = require('../Common');
|
||||
/**
|
||||
* Description
|
||||
* @method exports
|
||||
@ -46,8 +46,8 @@ module.exports = function(God) {
|
||||
* @method getFormatedProcesses
|
||||
* @return arr
|
||||
*/
|
||||
God.getFormatedProcesses = function() {
|
||||
var db = God.clusters_db;
|
||||
God.getFormatedProcesses = function getFormatedProcesses() {
|
||||
var db = Common.serialize(God.clusters_db);
|
||||
var arr = [];
|
||||
|
||||
for (var key in db) {
|
||||
@ -60,6 +60,7 @@ module.exports = function(God) {
|
||||
});
|
||||
}
|
||||
}
|
||||
db = null;
|
||||
return arr;
|
||||
};
|
||||
|
||||
@ -69,7 +70,7 @@ module.exports = function(God) {
|
||||
* @param {} id
|
||||
* @return ConditionalExpression
|
||||
*/
|
||||
God.findProcessById = function(id) {
|
||||
God.findProcessById = function findProcessById(id) {
|
||||
return God.clusters_db[id] ? God.clusters_db[id] : null;
|
||||
};
|
||||
|
||||
@ -99,7 +100,7 @@ module.exports = function(God) {
|
||||
* @return
|
||||
*/
|
||||
God.findByScript = function(script, cb) {
|
||||
var db = God.clusters_db;
|
||||
var db = Common.serialize(God.clusters_db);
|
||||
var arr = [];
|
||||
|
||||
for (var key in db) {
|
||||
|
||||
@ -8,7 +8,7 @@
|
||||
|
||||
var async = require('async');
|
||||
var cst = require('../../constants.js');
|
||||
|
||||
var Common = require('../Common');
|
||||
/**
|
||||
* softReload will wait permission from process to exit
|
||||
* @method softReload
|
||||
@ -31,7 +31,7 @@ function softReload(God, id, cb) {
|
||||
var new_env = JSON.parse(JSON.stringify(old_worker.pm2_env));
|
||||
new_env.restart_time += 1;
|
||||
|
||||
God.bus.emit('process:start_soft_reload', { process : old_worker });
|
||||
God.bus.emit('process:start_soft_reload', { process : Common.serialize(old_worker) });
|
||||
|
||||
// Reset created_at and unstable_restarts
|
||||
God.resetState(new_env);
|
||||
@ -123,7 +123,7 @@ function hardReload(God, id, cb) {
|
||||
var new_env = JSON.parse(JSON.stringify(old_worker.pm2_env));
|
||||
new_env.restart_time += 1;
|
||||
|
||||
God.bus.emit('process:start_reload', { process : old_worker });
|
||||
God.bus.emit('process:start_reload', { process : Common.serialize(old_worker) });
|
||||
|
||||
// Reset created_at and unstable_restarts
|
||||
God.resetState(new_env);
|
||||
|
||||
@ -59,17 +59,3 @@ http.createServer(function (req, res) {
|
||||
return res.end();
|
||||
}
|
||||
}).listen(cst.WEB_INTERFACE);
|
||||
|
||||
|
||||
// var MicroDB = require("nodejs-microdb");
|
||||
|
||||
// var fdb = new MicroDB({
|
||||
// "file" : p.join(cst.DEFAULT_FILE_PATH, "monit.db")
|
||||
// });
|
||||
|
||||
// setInterval(function() {
|
||||
// Satan.executeRemote("list", {}, function(err, data_proc) {
|
||||
// console.log('adding');
|
||||
// fdb.add(data_proc);
|
||||
// });
|
||||
// }, 1000);
|
||||
|
||||
@ -6,12 +6,14 @@ var axon = require('axon');
|
||||
var debug = require('debug')('interface:driver'); // Interface
|
||||
var chalk = require('chalk');
|
||||
|
||||
var pkg = require('../../package.json');
|
||||
|
||||
var cst = require('../../constants.js');
|
||||
var Cipher = require('./Cipher.js');
|
||||
var Filter = require('./Filter.js');
|
||||
var ReverseInteractor = require('./ReverseInteractor.js');
|
||||
var PushInteractor = require('./PushInteractor.js');
|
||||
|
||||
var WatchDog = require('./WatchDog.js');
|
||||
|
||||
var Daemon = {
|
||||
connectToPM2 : function() {
|
||||
@ -20,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) {
|
||||
@ -51,7 +53,7 @@ var Daemon = {
|
||||
opts.PUBLIC_KEY = process.env.PM2_PUBLIC_KEY;
|
||||
opts.SECRET_KEY = process.env.PM2_SECRET_KEY;
|
||||
opts.REVERSE_INTERACT = JSON.parse(process.env.PM2_REVERSE_INTERACT);
|
||||
|
||||
opts.PM2_VERSION = pkg.version;
|
||||
if (!opts.MACHINE_NAME) {
|
||||
console.error('You must provide a PM2_MACHINE_NAME environment variable');
|
||||
process.exit(cst.ERROR_EXIT);
|
||||
@ -77,28 +79,6 @@ var Daemon = {
|
||||
|
||||
return opts;
|
||||
},
|
||||
wrapStd : function(cb) {
|
||||
var stdout = fs.createWriteStream(cst.INTERACTOR_LOG_FILE_PATH, {
|
||||
flags : 'a'
|
||||
});
|
||||
|
||||
stdout.on('open', function() {
|
||||
process.stderr.write = (function(write) {
|
||||
return function(string, encoding, fd) {
|
||||
stdout.write(new Date().toISOString() + ' : ' + string);
|
||||
};
|
||||
})(process.stderr.write);
|
||||
|
||||
process.stdout.write = (function(write) {
|
||||
return function(string, encoding, fd) {
|
||||
stdout.write(new Date().toISOString() + ' : ' + string);
|
||||
};
|
||||
})(process.stdout.write);
|
||||
|
||||
if (cb) return cb();
|
||||
return false;
|
||||
});
|
||||
},
|
||||
start : function() {
|
||||
var self = this;
|
||||
|
||||
@ -109,6 +89,10 @@ var Daemon = {
|
||||
self.activateRPC();
|
||||
self.opts.ipm2 = self.connectToPM2();
|
||||
|
||||
// WatchDog.start({
|
||||
// conf : self.opts
|
||||
// });
|
||||
|
||||
// Then connect to external services
|
||||
if (cst.DEBUG) {
|
||||
PushInteractor.start({
|
||||
@ -150,7 +134,5 @@ if (require.main === module) {
|
||||
console.log(chalk.cyan.bold('[Keymetrics.io]') + ' Launching agent');
|
||||
process.title = 'PM2: Keymetrics.io Agent';
|
||||
|
||||
Daemon.wrapStd(function() {
|
||||
Daemon.start();
|
||||
});
|
||||
Daemon.start();
|
||||
}
|
||||
|
||||
@ -1,21 +1,16 @@
|
||||
|
||||
var pkg = require('../../package.json');
|
||||
var os = require('os');
|
||||
|
||||
function Filter(machine_name) {
|
||||
this.machine_name = machine_name;
|
||||
this.pm2_version = pkg.version;
|
||||
var Filter = {};
|
||||
|
||||
Filter.getProcessID = function(machine_name, name, id) {
|
||||
return machine_name + ':' + name + ':' + id;
|
||||
};
|
||||
|
||||
Filter.prototype.getProcessID = function(name, id) {
|
||||
return this.machine_name + ':' + name + ':' + id;
|
||||
};
|
||||
|
||||
Filter.prototype.status = function(processes, conf) {
|
||||
Filter.status = function(processes, conf) {
|
||||
if (!processes) return null;
|
||||
|
||||
var filter_procs = [];
|
||||
var self = this;
|
||||
|
||||
processes.forEach(function(proc) {
|
||||
filter_procs.push({
|
||||
@ -32,7 +27,7 @@ Filter.prototype.status = function(processes, conf) {
|
||||
pm_id : proc.pm2_env.pm_id,
|
||||
cpu : Math.floor(proc.monit.cpu) || 0,
|
||||
memory : Math.floor(proc.monit.memory) || 0,
|
||||
process_id : self.getProcessID(proc.pm2_env.name, proc.pm2_env.pm_id),
|
||||
process_id : Filter.getProcessID(conf.MACHINE_NAME, proc.pm2_env.name, proc.pm2_env.pm_id),
|
||||
axm_actions : proc.pm2_env.axm_actions || []
|
||||
});
|
||||
});
|
||||
@ -40,7 +35,6 @@ Filter.prototype.status = function(processes, conf) {
|
||||
return {
|
||||
process : filter_procs,
|
||||
server : {
|
||||
pm2_version : self.pm2_version,
|
||||
loadavg : os.loadavg(),
|
||||
total_mem : os.totalmem(),
|
||||
free_mem : os.freemem(),
|
||||
@ -50,18 +44,19 @@ Filter.prototype.status = function(processes, conf) {
|
||||
type : os.type(),
|
||||
platform : os.platform(),
|
||||
arch : os.arch(),
|
||||
interaction : conf.REVERSE_INTERACT
|
||||
interaction : conf.REVERSE_INTERACT,
|
||||
pm2_version : conf.PM2_VERSION
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
Filter.prototype.monitoring = function(processes) {
|
||||
Filter.monitoring = function(processes, conf) {
|
||||
if (!processes) return null;
|
||||
var self = this;
|
||||
|
||||
var filter_procs = {};
|
||||
|
||||
processes.forEach(function(proc) {
|
||||
filter_procs[self.getProcessID(proc.pm2_env.name,proc.pm2_env.pm_id)] = [proc.monit.cpu, proc.monit.memory];
|
||||
filter_procs[Filter.getProcessID(conf.MACHINE_NAME, proc.pm2_env.name,proc.pm2_env.pm_id)] = [proc.monit.cpu, proc.monit.memory];
|
||||
});
|
||||
|
||||
return {
|
||||
@ -72,7 +67,7 @@ Filter.prototype.monitoring = function(processes) {
|
||||
};
|
||||
};
|
||||
|
||||
Filter.prototype.pruneProcessObj = function(process, machine_name) {
|
||||
Filter.pruneProcessObj = function(process, machine_name) {
|
||||
return {
|
||||
pm_id : process.pm2_env.pm_id,
|
||||
name : process.pm2_env.name,
|
||||
@ -81,7 +76,7 @@ Filter.prototype.pruneProcessObj = function(process, machine_name) {
|
||||
};
|
||||
};
|
||||
|
||||
Filter.prototype.processState = function(data) {
|
||||
Filter.processState = function(data) {
|
||||
var state = {
|
||||
state : data.process.pm2_env.status,
|
||||
name : data.process.pm2_env.name,
|
||||
|
||||
@ -7,7 +7,7 @@ var path = require('path');
|
||||
var util = require('util');
|
||||
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,7 +157,10 @@ function launchOrAttach(infos, cb) {
|
||||
InteractorDaemonizer.daemonize = function(infos, cb) {
|
||||
var InteractorJS = path.resolve(path.dirname(module.filename), 'Daemon.js');
|
||||
|
||||
var child = require('child_process').fork(InteractorJS, [], {
|
||||
var out = fs.openSync(cst.INTERACTOR_LOG_FILE_PATH, 'a'),
|
||||
err = fs.openSync(cst.INTERACTOR_LOG_FILE_PATH, 'a');
|
||||
|
||||
var child = require('child_process').spawn('node', [InteractorJS], {
|
||||
silent : false,
|
||||
detached : true,
|
||||
cwd : process.cwd(),
|
||||
@ -140,20 +170,26 @@ InteractorDaemonizer.daemonize = function(infos, cb) {
|
||||
PM2_PUBLIC_KEY : infos.public_key,
|
||||
PM2_REVERSE_INTERACT : infos.reverse_interact
|
||||
}, process.env),
|
||||
stdio : 'ignore'
|
||||
}, function(err, stdout, stderr) {
|
||||
if (err) return console.error(err);
|
||||
return console.log('Interactor daemonized');
|
||||
stdio : ['ipc', out, err]
|
||||
});
|
||||
|
||||
fs.writeFileSync(cst.INTERACTOR_PID_PATH, child.pid);
|
||||
|
||||
child.unref();
|
||||
|
||||
child.on('exit', function(msg) {
|
||||
debug('Error when launching Interactor, please check the agent logs');
|
||||
return cb(null, child);
|
||||
});
|
||||
|
||||
debug('Waiting for message');
|
||||
child.once('message', function(msg) {
|
||||
debug('Interactor ready');
|
||||
process.emit('interactor:daemon:ready');
|
||||
console.log(msg);
|
||||
console.log(chalk.cyan.bold('[Keymetrics.io]') + ' Log: %s | Conf: %s | PID: %s', cst.INTERACTOR_LOG_FILE_PATH,
|
||||
//console.log(msg);
|
||||
|
||||
child.disconnect();
|
||||
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);
|
||||
@ -258,20 +294,36 @@ 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);
|
||||
}
|
||||
|
||||
InteractorDaemonizer.getSetKeys({
|
||||
secret_key : opts.secret_key || null,
|
||||
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;
|
||||
|
||||
@ -13,9 +13,7 @@ var Cipher = require('./Cipher.js');
|
||||
var PushInteractor = module.exports = {
|
||||
start : function(p) {
|
||||
if (!p.port || !p.host) throw new Error('port or host not declared in PullInteractor');
|
||||
if (!p.conf || !p.conf.ipm2) throw new Error('ipm2 ĩs not initialized');
|
||||
|
||||
var self = this;
|
||||
if (!p.conf || !p.conf.ipm2) throw new Error('ipm2 is not initialized');
|
||||
|
||||
console.log(p.host + ':' + p.port);
|
||||
|
||||
@ -27,7 +25,6 @@ var PushInteractor = module.exports = {
|
||||
|
||||
this.conf = p.conf;
|
||||
this.ipm2 = p.conf.ipm2;
|
||||
this.filter = new Filter(this.conf.MACHINE_NAME);
|
||||
this.pm2_connected = false;
|
||||
this.send_buffer = [];
|
||||
this.http_transactions = [];
|
||||
@ -37,15 +34,18 @@ var PushInteractor = module.exports = {
|
||||
*/
|
||||
this.ipm2.on('ready', function() {
|
||||
console.log('[PUSH] Connected to PM2');
|
||||
self.pm2_connected = true;
|
||||
PushInteractor.pm2_connected = true;
|
||||
PushInteractor.startWorker();
|
||||
});
|
||||
|
||||
this.ipm2.on('reconnecting', function() {
|
||||
console.log('[PUSH] Reconnecting to PM2');
|
||||
self.pm2_connected = false;
|
||||
if (PushInteractor.timer_worker)
|
||||
clearInterval(PushInteractor.timer_worker);
|
||||
PushInteractor.pm2_connected = false;
|
||||
});
|
||||
|
||||
self.pm2_connected = true;
|
||||
PushInteractor.pm2_connected = true;
|
||||
|
||||
/**
|
||||
* Connect to AXM
|
||||
@ -56,25 +56,18 @@ var PushInteractor = module.exports = {
|
||||
* Start the chmilblik
|
||||
*/
|
||||
this.processEvents();
|
||||
this.startWorker();
|
||||
},
|
||||
/**
|
||||
* Send bufferized data at regular interval
|
||||
*/
|
||||
startWorker : function() {
|
||||
var self = this;
|
||||
|
||||
setInterval(function() {
|
||||
if (self.pm2_connected == false) return;
|
||||
|
||||
self.sendData();
|
||||
this.timer_worker = setInterval(function() {
|
||||
PushInteractor.sendData();
|
||||
}, cst.SEND_INTERVAL);
|
||||
},
|
||||
processEvents : function() {
|
||||
var self = this;
|
||||
|
||||
this.ipm2.bus.on('*', function(event, packet) {
|
||||
if (self.pm2_connected == false) return false;
|
||||
//if (PushInteractor.pm2_connected == false) return false;
|
||||
|
||||
if (packet.process && packet.process.pm2_env) {
|
||||
/**
|
||||
@ -82,8 +75,8 @@ var PushInteractor = module.exports = {
|
||||
*/
|
||||
if (event == 'axm:action' || event.match(/^log:/)) return false;
|
||||
|
||||
packet.process = self.filter.pruneProcessObj(packet.process, self.conf.MACHINE_NAME);
|
||||
self.bufferData(event, packet);
|
||||
packet.process = Filter.pruneProcessObj(packet.process, PushInteractor.conf.MACHINE_NAME);
|
||||
PushInteractor.bufferData(event, packet);
|
||||
}
|
||||
else {
|
||||
/**
|
||||
@ -97,28 +90,26 @@ var PushInteractor = module.exports = {
|
||||
});
|
||||
},
|
||||
bufferizeServerStatus : function(cb) {
|
||||
var self = this;
|
||||
|
||||
this.ipm2.rpc.getMonitorData({}, function(err, processes) {
|
||||
if (!processes) return console.error('Cant access to getMonitorData RPC PM2 method');
|
||||
|
||||
var ret;
|
||||
|
||||
if ((ret = self.filter.monitoring(processes))) {
|
||||
self.bufferData('monitoring', ret);
|
||||
if ((ret = Filter.monitoring(processes, PushInteractor.conf))) {
|
||||
PushInteractor.bufferData('monitoring', ret);
|
||||
}
|
||||
|
||||
if ((ret = self.filter.status(processes, self.conf))) {
|
||||
self.bufferData('status', ret);
|
||||
if ((ret = Filter.status(processes, PushInteractor.conf))) {
|
||||
PushInteractor.bufferData('status', ret);
|
||||
}
|
||||
|
||||
if (self.http_transactions && self.http_transactions.length > 0) {
|
||||
self.send_buffer.push({
|
||||
if (PushInteractor.http_transactions && PushInteractor.http_transactions.length > 0) {
|
||||
PushInteractor.send_buffer.push({
|
||||
event : 'http:transaction',
|
||||
transactions : self.http_transactions,
|
||||
server_name : self.conf.MACHINE_NAME
|
||||
transactions : PushInteractor.http_transactions,
|
||||
server_name : PushInteractor.conf.MACHINE_NAME
|
||||
});
|
||||
self.http_transactions = [];
|
||||
PushInteractor.http_transactions = [];
|
||||
}
|
||||
|
||||
if (cb) return cb();
|
||||
@ -131,72 +122,73 @@ var PushInteractor = module.exports = {
|
||||
* @return
|
||||
*/
|
||||
sendData : function() {
|
||||
var data = {};
|
||||
var self = this;
|
||||
|
||||
this.bufferizeServerStatus(function() {
|
||||
/**
|
||||
* Cipher data with AES256
|
||||
*/
|
||||
var data = {};
|
||||
|
||||
if (process.env.NODE_ENV && process.env.NODE_ENV == 'test') {
|
||||
data = {
|
||||
public_key : self.conf.PUBLIC_KEY,
|
||||
public_key : PushInteractor.conf.PUBLIC_KEY,
|
||||
sent_at : new Date(),
|
||||
data : {
|
||||
buffer : self.send_buffer,
|
||||
server_name : self.conf.MACHINE_NAME
|
||||
buffer : PushInteractor.send_buffer,
|
||||
server_name : PushInteractor.conf.MACHINE_NAME
|
||||
}
|
||||
};
|
||||
}
|
||||
else {
|
||||
|
||||
/**
|
||||
* Cipher data with AES256
|
||||
*/
|
||||
|
||||
var cipheredData = Cipher.cipherMessage(JSON.stringify({
|
||||
buffer : self.send_buffer,
|
||||
server_name : self.conf.MACHINE_NAME
|
||||
}), self.conf.SECRET_KEY);
|
||||
buffer : PushInteractor.send_buffer,
|
||||
server_name : PushInteractor.conf.MACHINE_NAME
|
||||
}), PushInteractor.conf.SECRET_KEY);
|
||||
|
||||
data = {
|
||||
public_key : self.conf.PUBLIC_KEY,
|
||||
sent_at : new Date(),
|
||||
public_key : PushInteractor.conf.PUBLIC_KEY,
|
||||
sent_at : Date.now(),
|
||||
data : cipheredData
|
||||
};
|
||||
}
|
||||
|
||||
self.udpSocket.send(JSON.stringify(data));
|
||||
PushInteractor.udpSocket.send(JSON.stringify(data));
|
||||
|
||||
debug('Buffer with length %d sent', self.send_buffer.length);
|
||||
self.send_buffer = [];
|
||||
debug('Buffer with length %d sent', PushInteractor.send_buffer.length);
|
||||
data = null;
|
||||
|
||||
PushInteractor.send_buffer = [];
|
||||
});
|
||||
},
|
||||
bufferData : function(event, packet) {
|
||||
var self = this;
|
||||
|
||||
if (packet.process && !packet.server) {
|
||||
|
||||
if (event == 'http:transaction') {
|
||||
packet.data.data.process_id = self.conf.MACHINE_NAME + ':' + packet.process.name + ':' + packet.process.pm_id;
|
||||
packet.data.data.process_id = Filter.getProcessID(PushInteractor.conf.MACHINE_NAME, packet.process.name, packet.process.pm_id);
|
||||
packet.data.data.process_name = packet.process.name;
|
||||
return self.http_transactions.push(packet.data.data);
|
||||
return PushInteractor.http_transactions.push(packet.data.data);
|
||||
}
|
||||
|
||||
self.send_buffer.push({
|
||||
at : new Date(),
|
||||
PushInteractor.send_buffer.push({
|
||||
at : Date.now(),
|
||||
event : event,
|
||||
data : packet.data || null,
|
||||
process : packet.process,
|
||||
process_id : self.conf.MACHINE_NAME + ':' + packet.process.name + ':' + packet.process.pm_id,
|
||||
process_id : Filter.getProcessID(PushInteractor.conf.MACHINE_NAME, packet.process.name, packet.process.pm_id),
|
||||
process_name : packet.process.name
|
||||
});
|
||||
}
|
||||
else {
|
||||
self.send_buffer.push({
|
||||
at : new Date(),
|
||||
PushInteractor.send_buffer.push({
|
||||
at : Date.now(),
|
||||
event : event,
|
||||
data : packet,
|
||||
server_name : self.conf.MACHINE_NAME
|
||||
server_name : PushInteractor.conf.MACHINE_NAME
|
||||
});
|
||||
}
|
||||
|
||||
debug('Event %s bufferized', event);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
8
lib/Interactor/Tools.js
Normal file
8
lib/Interactor/Tools.js
Normal file
@ -0,0 +1,8 @@
|
||||
|
||||
var Stringify = require('json-stringify-safe');
|
||||
|
||||
var Tools = {};
|
||||
|
||||
Tools.serialize = function(data) {
|
||||
return JSON.parse(Stringify(data));
|
||||
};
|
||||
72
lib/Interactor/WatchDog.js
Normal file
72
lib/Interactor/WatchDog.js
Normal file
@ -0,0 +1,72 @@
|
||||
|
||||
var pm2 = require('../..');
|
||||
var debug = require('debug')('interface:watchdog');
|
||||
|
||||
process.env.PM2_AGENT_ONLINE = true;
|
||||
|
||||
var WatchDog = module.exports = {
|
||||
start : function(p) {
|
||||
var self = this;
|
||||
this.ipm2 = p.conf.ipm2;
|
||||
this.relaunching = false;
|
||||
|
||||
/**
|
||||
* Handle PM2 connection state changes
|
||||
*/
|
||||
this.ipm2.on('ready', function() {
|
||||
console.log('[WATCHDOG] Connected to PM2');
|
||||
self.relaunching = false;
|
||||
self.autoDump();
|
||||
});
|
||||
|
||||
this.ipm2.on('reconnecting', function() {
|
||||
console.log('[WATCHDOG] PM2 is disconnected - Relaunching PM2');
|
||||
|
||||
if (self.relaunching === true) return console.log('[WATCHDOG] Already relaunching PM2');
|
||||
self.relaunching = true;
|
||||
|
||||
if (self.dump_interval)
|
||||
clearInterval(self.dump_interval);
|
||||
|
||||
return WatchDog.resurrect();
|
||||
});
|
||||
},
|
||||
resurrect : function() {
|
||||
var self = this;
|
||||
|
||||
console.log('[WATCHDOG] Trying to launch PM2 #1');
|
||||
|
||||
|
||||
pm2.connect(function() {
|
||||
console.log('[WATCHDOG] PM2 successfully launched. Resurrecting processes');
|
||||
|
||||
pm2.resurrect(function(err) {
|
||||
if (err) {
|
||||
self.relaunching = false;
|
||||
return console.error('[WATCHDOG] Error when resurrect');
|
||||
}
|
||||
console.log('PM2 has been resurrected');
|
||||
self.relaunching = false;
|
||||
pm2.disconnect(function() {});
|
||||
return false;
|
||||
});
|
||||
return false;
|
||||
});
|
||||
},
|
||||
autoDump : function() {
|
||||
var self = this;
|
||||
|
||||
this.dump_interval = setInterval(function() {
|
||||
pm2.connect(function() {
|
||||
if (self.relaunching == true) return false;
|
||||
|
||||
pm2.dump(function(err) {
|
||||
if (err) return console.error('[WATCHDOG] Error when dumping');
|
||||
debug('PM2 process list dumped');
|
||||
pm2.disconnect(function() {});
|
||||
return false;
|
||||
});
|
||||
});
|
||||
}, 5000);
|
||||
}
|
||||
};
|
||||
@ -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);
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
18
lib/Monit.js
18
lib/Monit.js
@ -10,8 +10,6 @@ var CliUx = require('./CliUx');
|
||||
|
||||
var debug = require('debug')('pm2:monit');
|
||||
|
||||
require('colors');
|
||||
|
||||
// Cst for light programs
|
||||
const RATIO_T1 = Math.floor(os.totalmem() / 500);
|
||||
// Cst for medium programs
|
||||
@ -45,7 +43,7 @@ Monit.reset = function(msg) {
|
||||
|
||||
if(msg) {
|
||||
this.multi.write(msg);
|
||||
}
|
||||
}
|
||||
|
||||
this.bars = {};
|
||||
|
||||
@ -111,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;
|
||||
@ -127,6 +125,8 @@ Monit.refresh = function(processes) {
|
||||
|
||||
Monit.addProcess = function(proc, i) {
|
||||
|
||||
require('colors');
|
||||
|
||||
if(proc.pm_id in this.bars) {
|
||||
return ;
|
||||
}
|
||||
@ -183,7 +183,7 @@ Monit.addProcesses = function(processes) {
|
||||
if(!processes) {
|
||||
processes = [];
|
||||
}
|
||||
|
||||
|
||||
this.reset();
|
||||
|
||||
var num = processes.length;
|
||||
@ -204,7 +204,7 @@ Monit.addProcesses = function(processes) {
|
||||
* @method drawRatio
|
||||
* @param {} bar_memory
|
||||
* @param {} memory
|
||||
* @return
|
||||
* @return
|
||||
*/
|
||||
Monit.drawRatio = function(bar_memory, memory) {
|
||||
var scale = 0;
|
||||
@ -225,12 +225,14 @@ Monit.drawRatio = function(bar_memory, memory) {
|
||||
* @return this
|
||||
*/
|
||||
Monit.updateBars = function(proc) {
|
||||
require('colors');
|
||||
|
||||
if (proc.pm2_env.status !== 'online' || proc.pm2_env.status !== this.bars[proc.pm_id].status) {
|
||||
this.bars[proc.pm_id].cpu.percent(0, proc.pm2_env.status.red);
|
||||
this.drawRatio(this.bars[proc.pm_id].memory, 0, proc.pm2_env.status.red);
|
||||
this.drawRatio(this.bars[proc.pm_id].memory, 0, proc.pm2_env.status.red);
|
||||
} else if (!proc.monit) {
|
||||
this.bars[proc.pm_id].cpu.percent(0, 'No data'.red);
|
||||
this.drawRatio(this.bars[proc.pm_id].memory, 0, 'No data'.red);
|
||||
this.drawRatio(this.bars[proc.pm_id].memory, 0, 'No data'.red);
|
||||
} else {
|
||||
this.bars[proc.pm_id].cpu.percent(proc.monit.cpu);
|
||||
this.drawRatio(this.bars[proc.pm_id].memory, proc.monit.memory);
|
||||
|
||||
@ -2,6 +2,10 @@
|
||||
// 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');
|
||||
@ -30,9 +34,6 @@ var cst = require('../constants');
|
||||
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);
|
||||
|
||||
@ -75,12 +76,9 @@ 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') {
|
||||
|
||||
if (p.extname(script) == '.coffee') {
|
||||
require('coffee-script/register');
|
||||
}
|
||||
|
||||
@ -195,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);
|
||||
|
||||
|
||||
144
lib/Satan.js
144
lib/Satan.js
@ -11,10 +11,6 @@
|
||||
|
||||
var rpc = require('pm2-axon-rpc');
|
||||
var axon = require('axon');
|
||||
var rep = axon.socket('rep');
|
||||
var req = axon.socket('req');
|
||||
var pub = axon.socket('pub-emitter');
|
||||
|
||||
var debug = require('debug')('pm2:satan');
|
||||
var util = require('util');
|
||||
var fs = require('fs');
|
||||
@ -23,7 +19,6 @@ var cst = require('../constants.js');
|
||||
|
||||
var Stringify = require('json-stringify-safe');
|
||||
|
||||
var pkg = require('../package.json');
|
||||
/**
|
||||
* Export
|
||||
*/
|
||||
@ -39,19 +34,19 @@ 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) {
|
||||
Satan.remoteWrapper();
|
||||
return cb ? cb(null) : false;
|
||||
}
|
||||
|
||||
// Daemonize
|
||||
return Satan.launchDaemon(function(err, child) {
|
||||
if (err) {
|
||||
@ -88,7 +83,12 @@ Satan.processStateHandler = function(God) {
|
||||
try {
|
||||
fs.writeFileSync(cst.PM2_PID_FILE_PATH, process.pid);
|
||||
} catch(e){}
|
||||
|
||||
|
||||
process.on('SIGILL', function() {
|
||||
global.gc();
|
||||
console.log(' running garbage collector');
|
||||
});
|
||||
|
||||
process.on('SIGTERM', gracefullExit);
|
||||
process.on('SIGINT', gracefullExit);
|
||||
process.on('SIGQUIT', gracefullExit);
|
||||
@ -106,38 +106,10 @@ Satan.remoteWrapper = function() {
|
||||
// Only require here because God init himself
|
||||
var God = require('./God');
|
||||
|
||||
var InteractorDaemonizer = require('./Interactor/InteractorDaemonizer.js');
|
||||
var pkg = require('../package.json');
|
||||
|
||||
Satan.processStateHandler(God);
|
||||
|
||||
if (process.env.SILENT == 'true') {
|
||||
// Redirect output to files
|
||||
var stdout = fs.createWriteStream(cst.PM2_LOG_FILE_PATH, {
|
||||
flags : 'a'
|
||||
});
|
||||
|
||||
/**
|
||||
* Description
|
||||
* @method write
|
||||
* @param {} string
|
||||
* @return
|
||||
*/
|
||||
process.stderr.write = function(string) {
|
||||
stdout.write(new Date().toISOString() + ' : ' + string);
|
||||
};
|
||||
|
||||
/**
|
||||
* Description
|
||||
* @method write
|
||||
* @param {} string
|
||||
* @return
|
||||
*/
|
||||
process.stdout.write = function(string) {
|
||||
stdout.write(new Date().toISOString() + ' : ' + string);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
// Send ready message to Satan Client
|
||||
if (typeof(process.send) === 'function') {
|
||||
process.send({
|
||||
@ -146,6 +118,7 @@ Satan.remoteWrapper = function() {
|
||||
pid : process.pid,
|
||||
pm2_version : pkg.version
|
||||
});
|
||||
pkg = null;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -155,6 +128,7 @@ Satan.remoteWrapper = function() {
|
||||
/**
|
||||
* Rep/Req - RPC system to interact with God
|
||||
*/
|
||||
var rep = axon.socket('rep');
|
||||
|
||||
var server = new rpc.Server(rep);
|
||||
|
||||
@ -173,6 +147,7 @@ Satan.remoteWrapper = function() {
|
||||
stopAll : God.stopAll,
|
||||
softReloadProcessId : God.softReloadProcessId,
|
||||
reloadProcessId : God.reloadProcessId,
|
||||
resetMetaProcessId : God.resetMetaProcessId,
|
||||
killMe : God.killMe,
|
||||
findByScript : God.findByScript,
|
||||
findByPort : God.findByPort,
|
||||
@ -197,6 +172,7 @@ Satan.remoteWrapper = function() {
|
||||
/**
|
||||
* Pub system for real time notifications
|
||||
*/
|
||||
var pub = axon.socket('pub-emitter');
|
||||
|
||||
pub.bind(cst.DAEMON_PUB_PORT, cst.DAEMON_BIND_HOST);
|
||||
console.log('BUS system [READY] on %s:%s', cst.DAEMON_PUB_PORT, cst.DAEMON_BIND_HOST);
|
||||
@ -205,7 +181,7 @@ Satan.remoteWrapper = function() {
|
||||
* Action treatment specifics
|
||||
* Attach actions to pm2_env.axm_actions variables (name + options)
|
||||
*/
|
||||
God.bus.on('axm:action', function(msg) {
|
||||
God.bus.on('axm:action', function axmActions(msg) {
|
||||
debug('Got new action', msg);
|
||||
var pm2_env = msg.process.pm2_env;
|
||||
var exists = false;
|
||||
@ -225,22 +201,16 @@ 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;
|
||||
});
|
||||
|
||||
/**
|
||||
* Launch Interactor
|
||||
*/
|
||||
InteractorDaemonizer.launchAndInteract({}, function(err, data) {
|
||||
// Then bind
|
||||
God.bus.onAny(function(data_v) {
|
||||
debug(this.event);
|
||||
// Avoid circular structure
|
||||
pub.emit(this.event, JSON.parse(Stringify(data_v)));
|
||||
});
|
||||
God.bus.onAny(function interactionBroadcast(data_v) {
|
||||
pub.emit(this.event, JSON.parse(Stringify(data_v)));
|
||||
});
|
||||
|
||||
};
|
||||
@ -259,31 +229,44 @@ Satan.remoteWrapper = function() {
|
||||
* @param {} cb
|
||||
* @return
|
||||
*/
|
||||
Satan.launchDaemon = function(cb) {
|
||||
Satan.launchDaemon = function launchDaemon(cb) {
|
||||
debug('Launching daemon');
|
||||
|
||||
var SatanJS = p.resolve(p.dirname(module.filename), 'Satan.js');
|
||||
var InteractorDaemonizer = require('./Interactor/InteractorDaemonizer.js');
|
||||
|
||||
var child = require('child_process').fork(SatanJS, [], {
|
||||
silent : false,
|
||||
var node_args = [];
|
||||
|
||||
var out = fs.openSync(cst.PM2_LOG_FILE_PATH, 'a'),
|
||||
err = fs.openSync(cst.PM2_LOG_FILE_PATH, 'a');
|
||||
|
||||
if (process.env.PM2_NODE_OPTIONS)
|
||||
node_args = node_args.concat(process.env.PM2_NODE_OPTIONS.split(' '));
|
||||
|
||||
node_args.push(SatanJS);
|
||||
|
||||
var child = require('child_process').spawn('node', node_args, {
|
||||
detached : true,
|
||||
cwd : process.cwd(),
|
||||
env : util._extend({
|
||||
'SILENT' : cst.DEBUG ? !cst.DEBUG : true,
|
||||
'HOME' : (process.env.PM2_HOME || process.env.HOME)
|
||||
}, process.env),
|
||||
stdio : 'ignore'
|
||||
}, function(err, stdout, stderr) {
|
||||
if (err) console.error(err);
|
||||
debug(arguments);
|
||||
stdio : ['ipc', out, err]
|
||||
});
|
||||
|
||||
child.unref();
|
||||
|
||||
child.once('message', function(msg) {
|
||||
process.emit('satan:daemon:ready');
|
||||
console.log(msg);
|
||||
return setTimeout(function() {cb(null, child)}, 150);
|
||||
debug('PM2 Daemon launched', msg);
|
||||
child.disconnect();
|
||||
|
||||
InteractorDaemonizer.launchAndInteract({}, function(err, data) {
|
||||
process.emit('satan:daemon:ready');
|
||||
setTimeout(function() {cb(null, child)}, 20);
|
||||
return false;
|
||||
});
|
||||
|
||||
});
|
||||
};
|
||||
|
||||
@ -294,11 +277,11 @@ Satan.launchDaemon = function(cb) {
|
||||
* @param {} cb
|
||||
* @return
|
||||
*/
|
||||
Satan.pingDaemon = function(cb) {
|
||||
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');
|
||||
@ -319,8 +302,9 @@ Satan.pingDaemon = function(cb) {
|
||||
* @method launchRPC
|
||||
* @return
|
||||
*/
|
||||
Satan.launchRPC = function(cb) {
|
||||
Satan.launchRPC = function launchRPC(cb) {
|
||||
debug('Launching RPC client on port %s %s', cst.DAEMON_RPC_PORT, cst.DAEMON_BIND_HOST);
|
||||
var req = axon.socket('req');
|
||||
Satan.client = new rpc.Client(req);
|
||||
|
||||
Satan.client.sock.once('connect', function() {
|
||||
@ -329,20 +313,22 @@ Satan.launchRPC = function(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);
|
||||
};
|
||||
|
||||
/**
|
||||
* Methods to close the RPC connection
|
||||
* @callback cb
|
||||
*/
|
||||
Satan.disconnectRPC = function(cb) {
|
||||
Satan.disconnectRPC = function disconnectRPC(cb) {
|
||||
debug('Disconnecting RPC');
|
||||
process.nextTick(function() {
|
||||
if (!Satan.client || !Satan.client.sock || !Satan.client.sock.close) return cb({
|
||||
msg : 'RPC connection to PM2 is not launched'
|
||||
});
|
||||
Satan.client.sock.close();
|
||||
return cb(null, {success:true});
|
||||
if (!Satan.client_sock || !Satan.client_sock.close)
|
||||
return cb({
|
||||
msg : 'RPC connection to PM2 is not launched'
|
||||
});
|
||||
Satan.client_sock.close();
|
||||
return cb ? cb(null, {success:true}) : false;
|
||||
});
|
||||
};
|
||||
|
||||
@ -352,7 +338,7 @@ Satan.disconnectRPC = function(cb) {
|
||||
* @param {} cb
|
||||
* @return
|
||||
*/
|
||||
Satan.getExposedMethods = function(cb) {
|
||||
Satan.getExposedMethods = function getExposedMethods(cb) {
|
||||
Satan.client.methods(cb);
|
||||
};
|
||||
|
||||
@ -364,7 +350,7 @@ Satan.getExposedMethods = function(cb) {
|
||||
* @param {} fn
|
||||
* @return
|
||||
*/
|
||||
Satan.executeRemote = function(method, env, fn) {
|
||||
Satan.executeRemote = function executeRemote(method, env, fn) {
|
||||
//stop watching when process is deleted
|
||||
if (method.indexOf('delete') !== -1) {
|
||||
Satan.stopWatch(method, env, fn);
|
||||
@ -380,6 +366,7 @@ Satan.executeRemote = function(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);
|
||||
};
|
||||
|
||||
@ -389,8 +376,15 @@ Satan.executeRemote = function(method, env, fn) {
|
||||
* @param {} fn
|
||||
* @return
|
||||
*/
|
||||
Satan.killDaemon = function(fn) {
|
||||
Satan.executeRemote('killMe', {}, fn);
|
||||
Satan.killDaemon = function killDaemon(fn) {
|
||||
Satan.executeRemote('killMe', {}, function() {
|
||||
Satan.disconnectRPC(function() {
|
||||
setTimeout(function() {
|
||||
return fn ? fn(null, {success:true}) : false;
|
||||
}, 200);
|
||||
return false;
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
@ -401,7 +395,7 @@ Satan.killDaemon = function(fn) {
|
||||
* @param {} fn
|
||||
* @return
|
||||
*/
|
||||
Satan.restartWatch = function(method, env, fn) {
|
||||
Satan.restartWatch = function restartWatch(method, env, fn) {
|
||||
Satan.client.call('restartWatch', method, env, fn);
|
||||
};
|
||||
|
||||
@ -413,7 +407,7 @@ Satan.restartWatch = function(method, env, fn) {
|
||||
* @param {} fn
|
||||
* @return
|
||||
*/
|
||||
Satan.stopWatch = function(method, env, fn) {
|
||||
Satan.stopWatch = function stopWatch(method, env, fn) {
|
||||
Satan.client.call('stopWatch', method, env, fn);
|
||||
};
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
48
package.json
48
package.json
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "pm2",
|
||||
"preferGlobal": "true",
|
||||
"version": "0.9.6",
|
||||
"version": "0.10.0-beta25",
|
||||
"os": [
|
||||
"!win32"
|
||||
],
|
||||
@ -67,28 +67,44 @@
|
||||
}
|
||||
],
|
||||
"homepage": "https://github.com/Unitech/pm2",
|
||||
"description": "Modern CLI process manager for Node apps with a builtin load-balancer",
|
||||
"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",
|
||||
"preinstall" : "bash ./scripts/preinstall.sh",
|
||||
"postinstall" : "bash ./scripts/postinstall.sh"
|
||||
},
|
||||
"keywords": [
|
||||
"cli",
|
||||
"fault tolerant",
|
||||
"sysadmin",
|
||||
"tools",
|
||||
"h24",
|
||||
"pm2",
|
||||
"logs",
|
||||
"log",
|
||||
"json",
|
||||
"express",
|
||||
"hapi",
|
||||
"kraken",
|
||||
"reload",
|
||||
"microservice",
|
||||
"programmatic",
|
||||
"harmony",
|
||||
"node-pm2",
|
||||
"production",
|
||||
"vitalsigns",
|
||||
"keymetrics",
|
||||
"deploy",
|
||||
"deployment",
|
||||
"daemon",
|
||||
"supervisor",
|
||||
"nodemon",
|
||||
"pm2.io",
|
||||
"ghost",
|
||||
"ghost production",
|
||||
"monitoring",
|
||||
"process manager",
|
||||
"forever",
|
||||
"forever-monitor",
|
||||
"keep process alive",
|
||||
"process configuration",
|
||||
"clustering",
|
||||
@ -104,35 +120,37 @@
|
||||
"dependencies": {
|
||||
"async": "~0.9.0",
|
||||
"axm": "~0.1.7",
|
||||
"axon": "~1.0.0",
|
||||
"chalk": "^0.4.0",
|
||||
"axon": "~2.0.0",
|
||||
"chalk": "~0.4.0",
|
||||
"chokidar": "~0.8.2",
|
||||
"cli-table": "~0.3.0",
|
||||
"coffee-script": "~1.7.1",
|
||||
"colors": "~0.6.2",
|
||||
"commander": "~2.2.0",
|
||||
"commander": "~2.3.0",
|
||||
"cron": "~1.0.4",
|
||||
"debug": "~1.0.2",
|
||||
"eventemitter2": "~0.4.13",
|
||||
"eventemitter2": "~0.4.14",
|
||||
"json-stringify-safe": "~5.0.0",
|
||||
"nssocket": "~0.5.1",
|
||||
"punt" : "~2.2.0",
|
||||
"pidusage": "~0.0.6",
|
||||
"pm2-axon-rpc": "~0.0.2",
|
||||
"pidusage": "~0.1.0",
|
||||
"pm2-axon-rpc": "~0.2.1",
|
||||
"pm2-deploy": "~0.0.4",
|
||||
"pm2-interface": "~0.1.3",
|
||||
"pm2-interface" : "~1.1.0",
|
||||
"pm2-multimeter": "~0.1.2",
|
||||
"pm2-rpc-fallback" : "~2.8.0",
|
||||
"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" : "~0.1.1",
|
||||
"ikt" : "git+http://ikt.pm2.io/ikt.git#master"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/Unitech/pm2/issues"
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 5.5 KiB |
BIN
pres/pm2-v2.png
Normal file
BIN
pres/pm2-v2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 8.9 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 12 KiB |
77
scripts/kill.js
Normal file
77
scripts/kill.js
Normal file
@ -0,0 +1,77 @@
|
||||
#!/bin/bash
|
||||
|
||||
':' // Hack to pass parameters to Node before running this file
|
||||
':' //; [ -f ~/.pm2/custom_options.sh ] && . ~/.pm2/custom_options.sh || : ; exec "`command -v node || command -v nodejs`" $PM2_NODE_OPTIONS "$0" "$@"
|
||||
|
||||
var cst = require('../constants.js');
|
||||
var fs = require('fs');
|
||||
var pm2 = require('..');
|
||||
|
||||
function killEverything() {
|
||||
var pm2_pid = null;
|
||||
var interactor_pid = null;
|
||||
|
||||
try {
|
||||
pm2_pid = fs.readFileSync(cst.PM2_PID_FILE_PATH);
|
||||
} catch(e) {
|
||||
console.log('PM2 pid file EEXIST');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
try {
|
||||
interactor_pid = fs.readFileSync(cst.INTERACTOR_PID_PATH);
|
||||
} catch(e) {
|
||||
}
|
||||
|
||||
if (interactor_pid) {
|
||||
try {
|
||||
console.log('Killing interactor');
|
||||
process.kill(interactor_pid);
|
||||
}
|
||||
catch (err) {
|
||||
}
|
||||
}
|
||||
|
||||
if (pm2_pid) {
|
||||
try {
|
||||
console.log('Killing PM2');
|
||||
process.kill(pm2_pid);
|
||||
}
|
||||
catch (err) {
|
||||
}
|
||||
}
|
||||
|
||||
setTimeout(function() {
|
||||
process.exit(0);
|
||||
}, 100);
|
||||
}
|
||||
|
||||
|
||||
var fallback = require('pm2-rpc-fallback').fallback;
|
||||
|
||||
fallback(cst, function(err, data) {
|
||||
if (err && err.online) {
|
||||
// Right RPC communcation
|
||||
console.log('Stopping PM2');
|
||||
pm2.connect(function() {
|
||||
pm2.updatePM2(function() {
|
||||
pm2.disconnect(function() {
|
||||
return process.exit(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
return false;
|
||||
}
|
||||
else if (err && err.offline) {
|
||||
console.log('PM2 already offline');
|
||||
return process.exit(0);
|
||||
}
|
||||
else if (err) {
|
||||
return killEverything();
|
||||
}
|
||||
if (data) {
|
||||
console.log('Killing old PM2');
|
||||
return killEverything();
|
||||
}
|
||||
return false;
|
||||
});
|
||||
20
scripts/ping.js
Normal file
20
scripts/ping.js
Normal file
@ -0,0 +1,20 @@
|
||||
|
||||
var cst = require('../constants.js');
|
||||
var fs = require('fs');
|
||||
|
||||
try {
|
||||
var pm2_pid = fs.readFileSync(cst.PM2_PID_FILE_PATH);
|
||||
} catch(e) {
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (pm2_pid) {
|
||||
try {
|
||||
process.kill(parseInt(pm2_pid), 0);
|
||||
console.log('PM2 online');
|
||||
process.exit(0);
|
||||
}
|
||||
catch (err) {
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
14
scripts/postinstall.sh
Normal file
14
scripts/postinstall.sh
Normal file
@ -0,0 +1,14 @@
|
||||
#!/bin/bash
|
||||
|
||||
`command -v node || command -v nodejs` ./scripts/ping.js
|
||||
if [ $? -eq 0 ]
|
||||
then
|
||||
bash ./scripts/kill.js
|
||||
if [ $? -eq 0 ]
|
||||
then
|
||||
./bin/pm2 resurrect
|
||||
fi
|
||||
exit 0;
|
||||
else
|
||||
exit 0;
|
||||
fi
|
||||
24
scripts/preinstall.sh
Normal file
24
scripts/preinstall.sh
Normal file
@ -0,0 +1,24 @@
|
||||
#!/bin/bash
|
||||
|
||||
#
|
||||
# Check if user is logged as root and that pm2 command is available
|
||||
#
|
||||
|
||||
if ( [ "$EUID" -eq 0 ] || [ "$USER" == "root" ] ) && ! ( env | grep "unsafe-perm" );
|
||||
then
|
||||
echo "##### PM2 INSTALLATION"
|
||||
echo "#"
|
||||
echo "#"
|
||||
echo "# As you run PM2 as root, to update PM2 automatically"
|
||||
echo "# you must add the --unsafe-perm flag."
|
||||
echo "#"
|
||||
echo "# $ npm install pm2 -g --unsafe-perm"
|
||||
echo "#"
|
||||
echo "# Else run the installation as a non root user"
|
||||
echo "#"
|
||||
echo "#"
|
||||
echo "#"
|
||||
echo "######"
|
||||
echo ""
|
||||
exit 1
|
||||
fi
|
||||
@ -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
|
||||
#
|
||||
@ -125,8 +124,8 @@ spec "Should get the right JSON with HttpInterface file launched"
|
||||
$pm2 flush
|
||||
spec "Should clean logs"
|
||||
|
||||
cat ~/.pm2/logs/echo-out.log | wc -l
|
||||
spec "File Log should be cleaned"
|
||||
# cat ~/.pm2/logs/echo-out.log | wc -l
|
||||
# spec "File Log should be cleaned"
|
||||
|
||||
sleep 0.3
|
||||
$http_get -q http://localhost:9615/ -O $JSON_FILE
|
||||
@ -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
|
||||
@ -153,8 +148,8 @@ 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 7 ] || fail "$1"
|
||||
success "$1"
|
||||
[ $OUT -eq 7 ] || fail "Error while wgeting data via web interface"
|
||||
success "Got data from interface"
|
||||
|
||||
|
||||
$pm2 list
|
||||
@ -211,6 +206,8 @@ OUT=`$pm2 prettylist | grep -o "restart_time" | wc -l`
|
||||
[ $OUT -eq 8 ] || fail "Not valid process number"
|
||||
success "Processes valid"
|
||||
|
||||
|
||||
|
||||
$pm2 delete all
|
||||
spec "Should delete 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,7 +69,6 @@ $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"
|
||||
should 'should has not restarted' 'restart_time: 0' 10
|
||||
spec "file errech-0.log exist"
|
||||
|
||||
45
test/bash/env-refresh.sh
Normal file
45
test/bash/env-refresh.sh
Normal file
@ -0,0 +1,45 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
SRC=$(cd $(dirname "$0"); pwd)
|
||||
source "${SRC}/include.sh"
|
||||
cd $file_path
|
||||
|
||||
echo -e "\033[1mENV REFRESH\033[0m"
|
||||
|
||||
#
|
||||
# Restart via CLI
|
||||
#
|
||||
TEST_VARIABLE='hello1' $pm2 start env.js -o out-env.log --merge-logs --name "env"
|
||||
>out-env.log
|
||||
|
||||
sleep 0.5
|
||||
grep "hello1" out-env.log &> /dev/null
|
||||
spec "should contain env variable"
|
||||
|
||||
TEST_VARIABLE='89hello89' $pm2 restart env
|
||||
|
||||
sleep 0.5
|
||||
grep "89hello89" out-env.log &> /dev/null
|
||||
spec "should contain refreshed environment variable"
|
||||
|
||||
$pm2 delete all
|
||||
|
||||
# HEYYYY
|
||||
|
||||
#
|
||||
# Restart via JSON
|
||||
#
|
||||
|
||||
$pm2 start env.json
|
||||
>out-env.log
|
||||
|
||||
sleep 0.5
|
||||
grep "YES" out-env.log &> /dev/null
|
||||
spec "should contain env variable"
|
||||
|
||||
$pm2 restart env-refreshed.json
|
||||
>out-env.log
|
||||
|
||||
sleep 0.5
|
||||
grep "HEYYYY" out-env.log &> /dev/null
|
||||
spec "should contain refreshed env variable via json"
|
||||
@ -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,22 +9,31 @@ $pm2 kill
|
||||
echo "################ HARMONY ES6"
|
||||
|
||||
$pm2 start harmony.js
|
||||
sleep 8
|
||||
$pm2 list
|
||||
should 'should fail when trying to launch pm2 without harmony option' 'errored' 1
|
||||
$pm2 list
|
||||
$pm2 kill
|
||||
|
||||
PM2_NODE_OPTIONS='--harmony' `pwd`/../../bin/pm2 start harmony.js
|
||||
sleep 2
|
||||
should 'should not fail when passing harmony option to V8' 'errored' 0
|
||||
$pm2 list
|
||||
$pm2 kill
|
||||
|
||||
should 'should FAIL when not passing harmony option to V8' 'restart_time: 0' 0
|
||||
$pm2 list
|
||||
$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
|
||||
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
|
||||
$pm2 kill
|
||||
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
|
||||
@ -79,10 +98,9 @@ ispec 'file outmerge-0.log should not exist'
|
||||
rm outmerge*
|
||||
|
||||
########### coffee cluster test
|
||||
$pm2 kill
|
||||
$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
|
||||
|
||||
|
||||
46
test/bash/reset.sh
Normal file
46
test/bash/reset.sh
Normal file
@ -0,0 +1,46 @@
|
||||
|
||||
#!/usr/bin/env bash
|
||||
|
||||
SRC=$(cd $(dirname "$0"); pwd)
|
||||
source "${SRC}/include.sh"
|
||||
|
||||
cd $file_path
|
||||
$pm2 kill
|
||||
|
||||
echo "################## RESET ###################"
|
||||
|
||||
#
|
||||
# BY ID
|
||||
#
|
||||
$pm2 start echo.js
|
||||
should 'should restarted be one for all' 'restart_time: 0' 1
|
||||
|
||||
$pm2 restart 0
|
||||
should 'should process restarted' 'restart_time: 1' 1
|
||||
|
||||
$pm2 reset 0
|
||||
should 'should process reseted' 'restart_time: 0' 1
|
||||
|
||||
#
|
||||
# BY NAME
|
||||
#
|
||||
$pm2 start echo.js -i 4 -f
|
||||
should 'should restarted be one for all' 'restart_time: 0' 5
|
||||
|
||||
$pm2 restart echo
|
||||
should 'should process restarted' 'restart_time: 1' 5
|
||||
|
||||
$pm2 reset echo
|
||||
should 'should process reseted' 'restart_time: 0' 5
|
||||
|
||||
|
||||
#
|
||||
# ALL
|
||||
#
|
||||
$pm2 restart all
|
||||
$pm2 restart all
|
||||
$pm2 restart all
|
||||
should 'should process restarted' 'restart_time: 3' 5
|
||||
|
||||
$pm2 reset all
|
||||
should 'should process reseted' 'restart_time: 0' 5
|
||||
16
test/benchmarks/monit-daemon.sh
Executable file
16
test/benchmarks/monit-daemon.sh
Executable file
@ -0,0 +1,16 @@
|
||||
#!/bin/bash
|
||||
|
||||
while [ true ]
|
||||
do
|
||||
PM2_PID=`pgrep "pm2: Daemon" -o`
|
||||
|
||||
# Run garbage collector
|
||||
kill -SIGILL $PM2_PID
|
||||
sleep 5
|
||||
|
||||
FILE="/proc/$PM2_PID/smaps"
|
||||
Rss=`echo 0 $(cat $FILE | grep Rss | awk '{print $2}' | sed 's#^#+#') | bc;`
|
||||
|
||||
echo `date +%H:%M:%S` $Rss >> $RESULT_FILE
|
||||
sleep 100
|
||||
done
|
||||
35
test/benchmarks/monit.sh
Executable file
35
test/benchmarks/monit.sh
Executable file
@ -0,0 +1,35 @@
|
||||
#!/bin/bash
|
||||
|
||||
RESULT_FILE=result.monit
|
||||
|
||||
export RESULT_FILE=$RESULT_FILE
|
||||
|
||||
launch() {
|
||||
echo "========= `date`" >> $RESULT_FILE
|
||||
nohup ./monit-daemon.sh &> monit.log &
|
||||
}
|
||||
|
||||
ppkill() {
|
||||
pkill -f monit-daemon.sh ; pkill -f sleep
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
launch
|
||||
;;
|
||||
kill)
|
||||
ppkill
|
||||
;;
|
||||
stop)
|
||||
ppkill
|
||||
;;
|
||||
restart)
|
||||
ppkill
|
||||
launch
|
||||
;;
|
||||
*)
|
||||
echo "Usage: {start|kill|stop|restart}"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
exit $RETVAL
|
||||
6
test/benchmarks/result.monit
Normal file
6
test/benchmarks/result.monit
Normal file
@ -0,0 +1,6 @@
|
||||
========= Fri Aug 22 14:11:29 EDT 2014
|
||||
14:11:35 61012
|
||||
========= Fri Aug 22 14:11:45 EDT 2014
|
||||
14:11:50 59984
|
||||
========= Fri Aug 22 14:12:47 EDT 2014
|
||||
14:12:52 59464
|
||||
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);
|
||||
10
test/fixtures/env-refreshed.json
vendored
Normal file
10
test/fixtures/env-refreshed.json
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
[{
|
||||
"name" : "env",
|
||||
"script" : "./env.js",
|
||||
"out_file" : "out-env.log",
|
||||
"merge_logs" : true,
|
||||
"env": {
|
||||
"NODE_ENV": "production",
|
||||
"TEST_VARIABLE": "HEYYYY"
|
||||
}
|
||||
}]
|
||||
4
test/fixtures/env.json
vendored
4
test/fixtures/env.json
vendored
@ -1,8 +1,10 @@
|
||||
[{
|
||||
"name" : "env",
|
||||
"script" : "./env.js",
|
||||
"out_file" : "out-env.log",
|
||||
"merge_logs" : true,
|
||||
"env": {
|
||||
"NODE_ENV": "production",
|
||||
"TEST_VARIABLE": "xxx"
|
||||
"TEST_VARIABLE": "YES"
|
||||
}
|
||||
}]
|
||||
|
||||
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 #########"
|
||||
@ -38,5 +38,9 @@ bash ./test/bash/fork.sh
|
||||
spec "Fork verified"
|
||||
bash ./test/bash/infinite_loop.sh
|
||||
spec "Infinite loop stop"
|
||||
bash ./test/bash/env-refresh.sh
|
||||
spec "Environment refresh on restart"
|
||||
bash ./test/bash/reset.sh
|
||||
spec "Reset meta"
|
||||
|
||||
$pm2 kill
|
||||
|
||||
@ -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();
|
||||
});
|
||||
});
|
||||
|
||||
@ -117,7 +140,7 @@ describe('God', function() {
|
||||
});
|
||||
|
||||
it('should restart the same process and set it as state online and be up', function(done) {
|
||||
God.restartProcessId(clu.pm2_env.pm_id, function(err, dt) {
|
||||
God.restartProcessId({id:clu.pm2_env.pm_id}, 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);
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -113,7 +113,6 @@ describe('PM2 programmatic calls', function() {
|
||||
it('should delete processes', function(done) {
|
||||
pm2.delete('all', function(err, ret) {
|
||||
should(err).be.null;
|
||||
ret.length.should.eql(0);
|
||||
pm2.list(function(err, ret) {
|
||||
should(err).be.null;
|
||||
ret.length.should.eql(0);
|
||||
@ -258,7 +257,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