diff --git a/lib/API/Log.js b/lib/API/Log.js index 1e1c552c0..37ad02974 100644 --- a/lib/API/Log.js +++ b/lib/API/Log.js @@ -194,34 +194,42 @@ Log.devStream = function(Client, id, raw, timestamp, exclusive) { }); }; -Log.jsonStream = function(Client, app_name) { +Log.jsonStream = function(Client, id) { var that = this; Client.launchBus(function(err, bus) { if (err) console.error(err); bus.on('process:event', function(packet) { - console.log(JSON.stringify({ + process.stdout.write(JSON.stringify({ timestamp : moment(packet.at), type : 'process_event', status : packet.event, app_name : packet.process.name })); + process.stdout.write('\n'); }); bus.on('log:*', function(type, packet) { - if (app_name != 'all' && app_name && app_name != packet.process.name) return false; + if (id !== 'all' + && packet.process.name != id + && packet.process.pm_id != id) + return; + + if (type === 'PM2') + return; if (typeof(packet.data) == 'string') packet.data = packet.data.replace(/(\r\n|\n|\r)/gm,''); - console.log(JSON.stringify({ + process.stdout.write(JSON.stringify({ message : packet.data, timestamp : moment(packet.at), type : type, process_id : packet.process.pm_id, app_name : packet.process.name })); + process.stdout.write('\n'); }); }); }; diff --git a/lib/Client.js b/lib/Client.js index ab4c6fb7f..74103fd3b 100644 --- a/lib/Client.js +++ b/lib/Client.js @@ -87,7 +87,7 @@ Client.prototype.start = function(cb) { that.launchRPC(function(err, meta) { return cb(null, { daemon_mode : that.conf.daemon_mode, - new_pm2_instance : false, + new_pm2_instance : true, rpc_socket_file : that.rpc_socket_file, pub_socket_file : that.pub_socket_file, pm2_home : that.pm2_home diff --git a/lib/God.js b/lib/God.js index a7dd8c3d5..dc0019972 100644 --- a/lib/God.js +++ b/lib/God.js @@ -452,7 +452,7 @@ God.finalizeProcedure = function finalizeProcedure(proc) { !proc.pm2_env || proc.pm2_env.status == cst.STOPPED_STATUS || proc.pm2_env.status == cst.STOPPING_STATUS) { - return console.error('Proc is not defined anymore or is being killed'); + return console.error('Cancelling versioning data parsing'); } proc.pm2_env.vizion_running = false; diff --git a/lib/binaries/Runtime4Docker.js b/lib/binaries/Runtime4Docker.js index 53ee13456..0094f063d 100644 --- a/lib/binaries/Runtime4Docker.js +++ b/lib/binaries/Runtime4Docker.js @@ -1,7 +1,7 @@ 'use strict'; /** - * Specialized PM2 CLI for Docker + * Specialized PM2 CLI for Containers */ var commander = require('commander'); var debug = require('debug')('pm2:cli'); @@ -10,20 +10,26 @@ var Log = require('../../lib/API/Log'); var cst = require('../../constants.js'); var pkg = require('../../package.json'); var path = require('path'); -var pm2; -// Do not print banner process.env.PM2_DISCRETE_MODE = true; commander.version(pkg.version) - .description('pm2-docker is a drop-in replacement node.js binary with some interesting production features') + .description('pm2-runtime is a drop-in replacement Node.js binary for containers') .option('-i --instances ', 'launch [number] of processes automatically load-balanced. Increase overall performances and performance stability.') .option('--secret [key]', '[MONITORING] keymetrics secret key') + .option('--no-autorestart', 'start an app without automatic restart') + .option('--node-args ', 'space delimited arguments to pass to node in cluster mode - e.g. --node-args="--debug=7001 --trace-deprecation"') + .option('-n --name ', 'set a for script') + .option('--max-memory-restart ', 'specify max memory amount used to autorestart (in octet or use syntax like 100M)') + .option('-c --cron ', 'restart a running process based on a cron pattern') + .option('--interpreter ', 'the interpreter pm2 should use for executing app (bash, python...)') .option('--public [key]', '[MONITORING] keymetrics public key') .option('--machine-name [name]', '[MONITORING] keymetrics machine name') - .option('--raw', 'raw log output') - .option('--json', 'output logs in json format') + .option('--trace', 'enable transaction tracing with km') + .option('--v8', 'enable v8 data collecting') .option('--format', 'output logs formated like key=val') + .option('--formatted', 'formatted log output |id|app|log') + .option('--json', 'output logs in json format') .option('--delay ', 'delay start of configuration file by ', 0) .option('--web [port]', 'launch process web api on [port] (default to 9615)') .option('--only ', 'only act on one application of configuration') @@ -32,38 +38,18 @@ commander.version(pkg.version) .option('--watch', 'watch and restart application on file change') .option('--error ', 'error log file destination (default disabled)', '/dev/null') .option('--output ', 'output log file destination (default disabled)', '/dev/null') + .allowUnknownOption() .usage('app.js'); -function start(cmd, opts) { - pm2 = new PM2.custom({ - pm2_home : process.env.PM2_HOME || path.join(process.env.HOME, '.pm2'), - secret_key : process.env.KEYMETRICS_SECRET || commander.secret, - public_key : process.env.KEYMETRICS_PUBLIC || commander.public, - machine_name : process.env.INSTANCE_NAME || commander.machineName, - daemon_mode : false - }); - - if (opts.web) { - var port = opts.web === true ? cst.WEB_PORT : opts.web; - pm2.web(port); - } - - run(cmd, opts, function() { - if (commander.autoExit) - autoExit(); - }); -} - commander.command('*') .action(function(cmd){ - start(cmd, commander); + Runtime.instanciate(cmd); }); -// @todo need to allow passing same option than pm2 start commander.command('start ') .description('start an application or json ecosystem file') .action(function(cmd) { - start(cmd, commander); + Runtime.instanciate(cmd); }); if (process.argv.length == 2) { @@ -71,105 +57,127 @@ if (process.argv.length == 2) { process.exit(1); } -var autoExitIndex = process.argv.indexOf('--auto-exit'); -if (autoExitIndex > -1) { - console.warn( - "Warning: --auto-exit has been removed, as it's now the default behavior" + - "; if you want to disable it, use the new --no-auto-exit flag." - ); - - process.argv.splice(autoExitIndex, 1); -} - -commander.parse(process.argv); +var Runtime = { + pm2 : null, + instanciate : function(cmd) { + this.pm2 = new PM2.custom({ + pm2_home : process.env.PM2_HOME || path.join(process.env.HOME, '.pm2'), + secret_key : process.env.KEYMETRICS_SECRET || commander.secret, + public_key : process.env.KEYMETRICS_PUBLIC || commander.public, + machine_name : process.env.INSTANCE_NAME || commander.machineName, + daemon_mode : process.env.PM2_RUNTIME_DEBUG || false + }); -process.on('SIGINT', function() { - exitPM2(); -}); - -process.on('SIGTERM', function() { - exitPM2(); -}); - -function run(cmd, opts, cb) { - var needRaw = commander.raw; - var timestamp = commander.timestamp; - - if (commander.json === true) - Log.jsonStream(pm2.Client, 'all'); - else if (commander.format === true) - Log.formatStream(pm2.Client, 'all', false, 'YYYY-MM-DD-HH:mm:ssZZ'); - else - Log.stream(pm2.Client, 'all', needRaw, timestamp, true); - - function exec() { - pm2.start(cmd, opts, function(err, obj) { - if (err) { - console.error(err.message || err); - return exitPM2(); - } - if (obj && obj.length == 0) { - console.error('Failed to start application %s', cmd); - return exitPM2(); + this.pm2.connect(function(err, pm2_meta) { + if (pm2_meta.new_pm2_instance == false) { + console.warn('[WARN] PM2 Daemon is already running') } - // ack start - cb(); - if (process.env.PM2_RUNTIME_DEBUG) - pm2.disconnect(function() {}); + process.on('SIGINT', function() { + Runtime.exit(); + }); - }); - } - setTimeout(exec.bind(this), opts.delay * 1000); -} + process.on('SIGTERM', function() { + Runtime.exit(); + }); -function exitPM2() { - pm2.kill(function() { - process.exit(0); - }); -} + Runtime.startLogStreaming(); + Runtime.startApp(cmd, function(err) { + if (err) { + console.error(err.message || err); + return Runtime.exit(); + } + }); + }); + }, + + /** + * Log Streaming Management + */ + startLogStreaming : function() { + if (commander.json === true) + Log.jsonStream(this.pm2.Client, 'all'); + else if (commander.format === true) + Log.formatStream(this.pm2.Client, 'all', false, 'YYYY-MM-DD-HH:mm:ssZZ'); + else + Log.stream(this.pm2.Client, 'all', !commander.formatted, commander.timestamp, true); + }, + + /** + * Application Startup + */ + startApp : function(cmd, cb) { + function exec() { + this.pm2.start(cmd, commander, function(err, obj) { + if (err) + return cb(err); + if (obj && obj.length == 0) + return cb(new Error('Failed to start application')) + + if (commander.web) { + var port = commander.web === true ? cst.WEB_PORT : commander.web; + Runtime.pm2.web(port); + } -/** - * Exit current PM2 instance if 0 app is online - * function activated via --auto-exit - */ -function autoExit() { - var interval = 3000; - var aliveInterval = interval * 1.5; - - setTimeout(function () { - var alive = false - var aliveTimer = setTimeout(function () { - if (!alive) { - console.error('PM2 Daemon is dead'); - process.exit(1); - } - }, aliveInterval); + if (commander.autoExit) + Runtime.autoExitWorker(); - pm2.list(function (err, apps) { - if (err) { - console.log('pm2.list got error') - console.error(err); - exitPM2(); - } + // For Testing purpose (allow to auto exit CLI) + if (process.env.PM2_RUNTIME_DEBUG) + Runtime.pm2.disconnect(function() {}); - clearTimeout(aliveTimer); - alive = true; + return cb(null, obj); + }); + } + // via --delay option + setTimeout(exec.bind(this), commander.delay * 1000); + }, + + /** + * Exit runtime mgmt + */ + exit : function() { + if (!this.pm2) return process.exit(1); + + this.pm2.kill(function() { + process.exit(0); + }); + }, + + /** + * Exit current PM2 instance if 0 app is online + * function activated via --auto-exit + */ + autoExitWorker : function() { + var interval = 1000; + + var timer = setTimeout(function () { + Runtime.pm2.list(function (err, apps) { + if (err) { + console.error('Could not run pm2 list'); + return Runtime.autoExitWorker(); + } - var appOnline = 0; + var appOnline = 0; - apps.forEach(function (app) { - if (app.pm2_env.status === cst.ONLINE_STATUS || + apps.forEach(function (app) { + if (app.pm2_env.status === cst.ONLINE_STATUS || app.pm2_env.status === cst.LAUNCHING_STATUS) { - appOnline++; + appOnline++; + } + }); + + if (appOnline === 0) { + console.log('0 application online, exiting'); + return Runtime.exit(); } + + Runtime.autoExitWorker(); }); + }, interval); - if (appOnline === 0) { - console.log('0 application online, exiting'); - exitPM2(); - } - autoExit(); - }); - }, interval); + timer.unref(); + } } + +commander.parse(process.argv); diff --git a/test/bash/cli-actions-2.sh b/test/bash/cli-actions-2.sh index 7cab8b283..02b50fc58 100644 --- a/test/bash/cli-actions-2.sh +++ b/test/bash/cli-actions-2.sh @@ -30,6 +30,13 @@ should 'should app be online once restart called' 'online' 1 $pm2 delete all +############## PID +$pm2 start 001-test.js --name "test" +should 'should app be online' 'online' 1 +$pm2 pid > /tmp/pid-tmp +$pm2 pid test +$pm2 delete all + ############### echo "Start application with filename starting with a numeric" diff --git a/test/bash/pm2-runtime.sh b/test/bash/pm2-runtime.sh index c58e55c84..f07131a35 100644 --- a/test/bash/pm2-runtime.sh +++ b/test/bash/pm2-runtime.sh @@ -3,27 +3,54 @@ SRC=$(cd $(dirname "$0"); pwd) source "${SRC}/include.sh" -export PM2_HOME="$HOME/.pm3" - -pm2runtime="`type -P node` `pwd`/bin/pm2-runtime" +pm2_runtime="`type -P node` `pwd`/bin/pm2-runtime" export PM2_RUNTIME_DEBUG='true' cd $file_path/pm2-dev -$pm2 delete all +# +# Simple start with 4 apps +# +$pm2 kill -# Test with js -$pm2runtime app.js -i 4 +$pm2_runtime app.js -i 4 should 'should have started 4 apps' 'online' 4 -$pm2 delete all +$pm2 kill +# # Test with json and args -$pm2runtime app.json +# +$pm2_runtime app.json should 'should have started 1 apps' 'online' 1 $pm2 prettylist | grep "watch: \[ 'server', 'client' \]" spec "Should application have two watch arguments" $pm2 prettylist | grep "ignore_watch: \[ 'node_modules', 'client/img' \]" spec "Should application have two ignore_watch arguments" $pm2 kill + +# Restore default behavior for exit checks +unset PM2_RUNTIME_DEBUG + +# +# --no-autorestart checks +# +# $pm2_runtime app.js --no-autorestart +# PID_PM2=$! +# $pm2 pid app +# echo "OK" +# PID=`cat /tmp/pid` +# echo $PID +# kill $PID +# sleep 3 +# pgrep "PM2" +# ispec "PM2 runtime should be killed because no app is running" + +# +# Auto Exit Worker +# +$pm2_runtime exited_app.js 2> /dev/null +sleep 1 +pgrep "PM2" +ispec "PM2 runtime should be killed because no app is running"