From ae038d1e51fd4dc883971ac59712e893fbcee51b Mon Sep 17 00:00:00 2001 From: Unitech Date: Thu, 20 Nov 2014 11:39:30 +0100 Subject: [PATCH 01/10] throttle tests --- test/bash/include.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/bash/include.sh b/test/bash/include.sh index 4966d594e..712fe243f 100644 --- a/test/bash/include.sh +++ b/test/bash/include.sh @@ -44,11 +44,13 @@ function success { } function spec { + sleep 0.3 [ $? -eq 0 ] || fail "$1" success "$1" } function ispec { + sleep 0.3 [ $? -ne 0 ] || fail "$1" success "$1" } From 253640f792abf4e6ce6d09510b1e274d986bcd5b Mon Sep 17 00:00:00 2001 From: Unitech Date: Thu, 20 Nov 2014 11:48:23 +0100 Subject: [PATCH 02/10] keep return --- test/bash/include.sh | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/test/bash/include.sh b/test/bash/include.sh index 712fe243f..4f0298d4f 100644 --- a/test/bash/include.sh +++ b/test/bash/include.sh @@ -44,14 +44,16 @@ function success { } function spec { + RET=$? sleep 0.3 - [ $? -eq 0 ] || fail "$1" + [ $RET -eq 0 ] || fail "$1" success "$1" } function ispec { + RET=$? sleep 0.3 - [ $? -ne 0 ] || fail "$1" + [ $RET -ne 0 ] || fail "$1" success "$1" } From 13d5baf6ba547394ba2ec69d42d67e6b2ec31969 Mon Sep 17 00:00:00 2001 From: Unitech Date: Thu, 20 Nov 2014 16:35:10 +0100 Subject: [PATCH 03/10] bash fix --- test/bash/misc.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/bash/misc.sh b/test/bash/misc.sh index 49da688db..073678cca 100644 --- a/test/bash/misc.sh +++ b/test/bash/misc.sh @@ -40,7 +40,7 @@ sleep 1 OUT=`cat $OUT_LOG | head -n 1` -if [ $OUT = "undefined" ] +if [ $OUT="undefined" ] then success "environment variable not defined" else From 21306731e4993dea7e6f3b104ea25e15f7384878 Mon Sep 17 00:00:00 2001 From: Unitech Date: Fri, 21 Nov 2014 12:04:43 +0100 Subject: [PATCH 04/10] Move startLogging method into Utility.js to avoid memory overhead from Common.js --- lib/Common.js | 56 +------------------------------------- lib/God/ForkMode.js | 5 ++-- lib/ProcessContainer.js | 14 +++++----- lib/Utility.js | 59 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 69 insertions(+), 65 deletions(-) diff --git a/lib/Common.js b/lib/Common.js index c47f4b680..21badf91f 100644 --- a/lib/Common.js +++ b/lib/Common.js @@ -18,67 +18,13 @@ var Stringify = require('json-stringify-safe'); var Satan = require('./Satan.js'); var InteractorDaemonizer = require('./Interactor/InteractorDaemonizer.js'); + /** * Common methods (used by CLI and God) */ var Common = module.exports; -/** - * Start log outgoing messages - * @method startLogging - * @param {} callback - * @return - */ -Common.startLogging = function(stds, callback) { - // Make sure directories of `logs` and `pids` exist. - try { - ['logs', 'pids'].forEach(function(n){ - (function(_path){ - !fs.existsSync(_path) && fs.mkdirSync(_path, '0755'); - })(path.resolve(cst.PM2_ROOT_PATH, n)) - }); - }catch(err){ - return callback(new Error('can not create directories (logs/pids):' + err.message)); - } - - // waterfall. - var flows = []; - // types of stdio, should be sorted as `std(entire log)`, `out`, `err`. - var types = Object.keys(stds).sort(function(x, y){ - return -x.charCodeAt(0) + y.charCodeAt(0); - }); - - // Create write streams. - (function createWS(io){ - if(io.length != 1){ - return; - } - io = io[0]; - - // If `std` is a Stream type, try next `std`. - // compatible with `pm2 reloadLogs` - if(typeof stds[io] == 'object' && !isNaN(stds[io].fd)){ - return createWS(types.splice(0, 1)); - } - - flows.push(function(next){ - var file = stds[io]; - stds[io] = fs.createWriteStream(file, {flags: 'a'}) - .on('error', function(err){ - next(err); - }) - .on('open', function(){ - next(); - }); - stds[io]._file = file; - }); - createWS(types.splice(0, 1)); - })(types.splice(0, 1)); - - async.waterfall(flows, callback); -} - /** * Resolve app paths and replace missing values with defaults. * @method resolveAppPaths diff --git a/lib/God/ForkMode.js b/lib/God/ForkMode.js index 6974ecd4d..b9d0f67a1 100644 --- a/lib/God/ForkMode.js +++ b/lib/God/ForkMode.js @@ -11,7 +11,6 @@ var fs = require('fs'); var cst = require('../../constants.js'); var moment = require('moment'); var Common = require('../Common'); - var Utility = require('../Utility.js'); /** @@ -73,7 +72,7 @@ module.exports = function ForkMode(God) { stds.std = pm2_env.pm_log_path; } - Common.startLogging(stds, function(err, result) { + Utility.startLogging(stds, function(err, result) { if (err) { God.logAndGenerateError(err); return cb(err); @@ -169,7 +168,7 @@ module.exports = function ForkMode(God) { stds[k] = stds[k]._file; } cspr.removeAllListeners(); - Common.startLogging(stds, cb); + Utility.startLogging(stds, cb); }; cspr.unref(); diff --git a/lib/ProcessContainer.js b/lib/ProcessContainer.js index 7b799dbbc..a0f7725bf 100644 --- a/lib/ProcessContainer.js +++ b/lib/ProcessContainer.js @@ -6,11 +6,11 @@ if (process.env.name != null) process.title = 'PM2 v' + process.env._pm2_version + ': ' + process.env.name; -var fs = require('fs'); -var p = require('path'); -var cst = require('../constants'); -var Common = require('./Common'); -var axm = require('axm'); +var fs = require('fs'); +var p = require('path'); +var cst = require('../constants'); +var Utility = require('./Utility.js'); +var axm = require('axm'); /** * Main entrance to wrap the desired code @@ -88,7 +88,7 @@ function exec(script, stds) { stds[k].close(); stds[k] = stds[k]._file; } - Common.startLogging(stds, function (err) { + Utility.startLogging(stds, function (err) { if(err){ console.error('Failed to reload logs:', err.stack); }else { @@ -103,7 +103,7 @@ function exec(script, stds) { if (process.env.log_date_format) moment = require('moment'); - Common.startLogging(stds, function (err) { + Utility.startLogging(stds, function (err) { if (err) { process.send({ type : 'process:exception', diff --git a/lib/Utility.js b/lib/Utility.js index 61d9a7641..be7a84e34 100644 --- a/lib/Utility.js +++ b/lib/Utility.js @@ -1,5 +1,9 @@ var Stringify = require('json-stringify-safe'); +var fs = require('fs'); +var path = require('path'); +var cst = require('../constants.js'); +var async = require('async'); var Utility = module.exports = { getDate : function() { @@ -7,5 +11,60 @@ var Utility = module.exports = { }, serialize : function(data) { return JSON.parse(Stringify(data)); + }, + startLogging : function(stds, callback) { + /** + * Start log outgoing messages + * @method startLogging + * @param {} callback + * @return + */ + // Make sure directories of `logs` and `pids` exist. + try { + ['logs', 'pids'].forEach(function(n){ + (function(_path){ + !fs.existsSync(_path) && fs.mkdirSync(_path, '0755'); + })(path.resolve(cst.PM2_ROOT_PATH, n)); + }); + }catch(err){ + return callback(new Error('can not create directories (logs/pids):' + err.message)); + } + + // waterfall. + var flows = []; + // types of stdio, should be sorted as `std(entire log)`, `out`, `err`. + var types = Object.keys(stds).sort(function(x, y){ + return -x.charCodeAt(0) + y.charCodeAt(0); + }); + + // Create write streams. + (function createWS(io){ + if(io.length != 1){ + return false; + } + io = io[0]; + + // If `std` is a Stream type, try next `std`. + // compatible with `pm2 reloadLogs` + if(typeof stds[io] == 'object' && !isNaN(stds[io].fd)){ + return createWS(types.splice(0, 1)); + } + + flows.push(function(next){ + var file = stds[io]; + + stds[io] = fs.createWriteStream(file, {flags: 'a'}) + .on('error', function(err){ + next(err); + }) + .on('open', function(){ + next(); + }); + stds[io]._file = file; + }); + return createWS(types.splice(0, 1)); + })(types.splice(0, 1)); + + async.waterfall(flows, callback); } }; From 636fd99c42fa895851e866e11798ad558c449609 Mon Sep 17 00:00:00 2001 From: Unitech Date: Fri, 21 Nov 2014 12:09:18 +0100 Subject: [PATCH 05/10] update isbinaryfile --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e487e5552..19ed3d372 100644 --- a/package.json +++ b/package.json @@ -144,7 +144,7 @@ "punt" : "2.2.0", "shelljs" : "0.3.0", - "isbinaryfile" : "^2.0.2" + "isbinaryfile" : "~2.0.3" }, "devDependencies": { "mocha" : "^1.20.1", From 197781ed57e24063403e3f06d13b94b16ef08ab4 Mon Sep 17 00:00:00 2001 From: Unitech Date: Fri, 21 Nov 2014 14:46:13 +0100 Subject: [PATCH 06/10] check reload logs and avoid cb if error --- lib/God/ActionMethods.js | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/lib/God/ActionMethods.js b/lib/God/ActionMethods.js index 8c1a0e5b7..032c9f075 100644 --- a/lib/God/ActionMethods.js +++ b/lib/God/ActionMethods.js @@ -226,6 +226,7 @@ module.exports = function(God) { proc.disconnect(); } catch (e) { // Fallback on disconnect method fail + console.log('Could not disconnect process', e.stack || e); clearTimeout(timeout); proc.removeListener('disconnect', onDisconnect); if (proc && proc.process && proc.process.pid) { @@ -491,18 +492,20 @@ 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 // Fork mode + console.log('Reloading logs for process id %d', id); + + if (cluster.pm2_env.exec_mode == 'cluster_mode') { + cluster.send({ + type:'log:reload' + }); + } + else if (cluster._reloadLogs) { cluster._reloadLogs(function(err) { - if (err) { - God.logAndGenerateError(err); - return cb(new Error(err)); - }; - return false; + if (err) God.logAndGenerateError(err); }); - console.log('Reloading logs for process id %d', id); + } }); + return cb(null, {}); }; From c0143cc5dae29758f44988c5fd9d160cf9d7922e Mon Sep 17 00:00:00 2001 From: Joni Shkurti Date: Fri, 21 Nov 2014 14:54:00 +0100 Subject: [PATCH 07/10] Update ActionMethods.js --- lib/God/ActionMethods.js | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/lib/God/ActionMethods.js b/lib/God/ActionMethods.js index 032c9f075..ad4e0607b 100644 --- a/lib/God/ActionMethods.js +++ b/lib/God/ActionMethods.js @@ -494,15 +494,19 @@ module.exports = function(God) { console.log('Reloading logs for process id %d', id); - if (cluster.pm2_env.exec_mode == 'cluster_mode') { - cluster.send({ - type:'log:reload' - }); - } - else if (cluster._reloadLogs) { - cluster._reloadLogs(function(err) { - if (err) God.logAndGenerateError(err); - }); + if (cluster && + cluster.pm2_env) { + if (cluster.send && + cluster.pm2_env.exec_mode == 'cluster_mode') { + cluster.send({ + type:'log:reload' + }); + } + else if (cluster._reloadLogs) { + cluster._reloadLogs(function(err) { + if (err) God.logAndGenerateError(err); + }); + } } }); From 402190264f92afb845f41ac671331a91b708c18f Mon Sep 17 00:00:00 2001 From: jshkurti Date: Fri, 21 Nov 2014 15:46:09 +0100 Subject: [PATCH 08/10] CLI Feature added: pm2 pull [commit_id] // goes to commit_id if specified --- bin/pm2 | 13 ++++++-- lib/CLI.js | 12 ++++++- lib/tools/VersionManagement.js | 60 ++++++++++++++++++++++++++++++++++ test/bash/pull.sh | 4 +++ 4 files changed, 85 insertions(+), 4 deletions(-) diff --git a/bin/pm2 b/bin/pm2 index 9774649b3..076a88bde 100755 --- a/bin/pm2 +++ b/bin/pm2 @@ -515,10 +515,17 @@ commander.command('kill') // // Update repository for a given app // -commander.command('pull ') + +commander.command('pull [commit id]') .description('updates repository for a given app') - .action(function(pm2_name) { - CLI.pullAndRestart(pm2_name); + .action(function(pm2_name, commit_id) { + if (commit_id !== undefined) { + CLI.pullCommitId( + {pm2_name: pm2_name, + commit_id: commit_id}); + } + else + CLI.pullAndRestart(pm2_name); }); // diff --git a/lib/CLI.js b/lib/CLI.js index 46d6495f1..741a631e1 100644 --- a/lib/CLI.js +++ b/lib/CLI.js @@ -1393,7 +1393,7 @@ CLI.infoInteract = function(cb) { }); }; -var Version = require('./tools/VersionManagement'); +var Version = require('./tools/VersionManagement.js'); /** * CLI method for updating a repository @@ -1425,6 +1425,16 @@ CLI.pullAndGracefulReload = function (process_name, cb) { Version._pull({process_name: process_name, action: 'gracefulReload'}, cb); } +/** + * CLI method for updating a repository to a specific commit id + * @method pullCommitId + * @param {object} opts + * @return + */ +CLI.pullCommitId = function (opts, cb) { + Version.pullCommitId(opts.pm2_name, opts.commit_id); +} + /** * CLI method for downgrading a repository to the previous commit (older) * @method backward diff --git a/lib/tools/VersionManagement.js b/lib/tools/VersionManagement.js index 7785c3550..d8e9d5dfa 100644 --- a/lib/tools/VersionManagement.js +++ b/lib/tools/VersionManagement.js @@ -72,6 +72,66 @@ Methods._pull = function(opts, cb) { }); }; +/** + * CLI method for updating a repository to a specific commit id + * @method pullCommitId + * @param {string} process_name + * @param {string} commit_id + * @return + */ +Methods.pullCommitId = function(process_name, commit_id, cb) { + var reload_type = 'restart'; + + printOut(cst.PREFIX_MSG + 'Updating repository for process name %s', process_name); + + Common.getProcessByName(process_name, function(err, processes) { + + if (processes.length === 0) { + printError('No processes with this name: %s', process_name); + return cb ? cb({msg:'Process not found: '+process_name}) : exitCli(cst.ERROR_EXIT); + } + + var proc = processes[0]; + if (proc.pm2_env.versioning) { + vizion.isUpToDate({folder: proc.pm2_env.versioning.repo_path}, + function(err, meta) { + if (err !== null) + return cb ? cb({msg:err}) : exitCli(cst.ERROR_EXIT); + vizion.revertTo( + {revision: commit_id, + folder: proc.pm2_env.versioning.repo_path}, + function(err2, meta2) { + if (!err2 && meta2.success) { + getPostUpdateCmds(proc.pm2_env.versioning.repo_path, process_name, + function (command_list) { + execCommands(proc.pm2_env.versioning.repo_path, command_list, function(err) { + if (err !== null) + { + printError(err); + return cb ? cb({msg:err}) : exitCli(cst.ERROR_EXIT); + } + else { + printOut(cst.PREFIX_MSG + 'Process successfully updated %s', process_name); + printOut(cst.PREFIX_MSG + 'Current commit %s', commit_id); + return CLI[reload_type](process_name, cb); + } + }); + }); + } + else { + printOut(cst.PREFIX_MSG + 'Already up-to-date or an error occured: %s', process_name); + return cb ? cb(null, {success:meta.success}) : exitCli(cst.SUCCESS_EXIT); + } + }); + }); + } + else { + printOut(cst.PREFIX_MSG + 'No versioning system found for process %s', process_name); + return cb ? cb(null, {success:false}) : exitCli(cst.SUCCESS_EXIT); + } + }); +}; + /** * CLI method for downgrading a repository to the previous commit (older) * @method backward diff --git a/test/bash/pull.sh b/test/bash/pull.sh index 960ea801e..c8feca257 100644 --- a/test/bash/pull.sh +++ b/test/bash/pull.sh @@ -75,6 +75,10 @@ OUT=`$pm2 jlist | egrep -oh '"ahead":true' | wc -c` [ $OUT -eq 13 ] || fail "Worker: ahead flag should be true" success "Worker: ahead flag should be true" +OUT=`$pm2 pull app 83dfc32383a84e146005d8981bcae2c52a5b123b | egrep -oh 'Current commit 83dfc32383a84e146005d8981bcae2c52a5b123b' | wc -c` +[ $OUT -eq 56 ] || fail "Commit ID should be correct" +success "Commit ID should be correct" + $pm2 kill cd .. rm -rf ./app-playground From d3d2bcc179514b48358b49ae6cdc8358be3f6e9f Mon Sep 17 00:00:00 2001 From: Joni Shkurti Date: Fri, 21 Nov 2014 16:38:02 +0100 Subject: [PATCH 09/10] Update PULL.md --- doc/PULL.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/doc/PULL.md b/doc/PULL.md index 65756d453..ebd63fec2 100644 --- a/doc/PULL.md +++ b/doc/PULL.md @@ -26,9 +26,10 @@ Switches your local repository to the next (more recent) commit if there is one. ```bash -$ pm2 pull +$ pm2 pull [commit ID] ``` -Updates your local repository to the most recent remote commit for the current branch. +Updates your local repository to the most recent remote commit for the current branch +or to the optional specified commit ID. From c2e3581cb5aebcd5495177fdc9349fadc27fb9c3 Mon Sep 17 00:00:00 2001 From: jshkurti Date: Fri, 21 Nov 2014 16:53:14 +0100 Subject: [PATCH 10/10] Worker: restart process if using 0 memory and status equals ONLINE --- bin/pm2 | 2 +- lib/Worker.js | 26 +++++++++++++++++++++----- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/bin/pm2 b/bin/pm2 index 076a88bde..54d424991 100755 --- a/bin/pm2 +++ b/bin/pm2 @@ -516,7 +516,7 @@ commander.command('kill') // Update repository for a given app // -commander.command('pull [commit id]') +commander.command('pull [commit_id]') .description('updates repository for a given app') .action(function(pm2_name, commit_id) { if (commit_id !== undefined) { diff --git a/lib/Worker.js b/lib/Worker.js index 437dda26f..79ea1b847 100644 --- a/lib/Worker.js +++ b/lib/Worker.js @@ -20,12 +20,12 @@ module.exports = function(God) { if (!(proc && proc.pm2_env && - proc_key.monit && - proc_key.monit.memory !== undefined && - proc.pm2_env.max_memory_restart !== undefined)) + proc_key.monit)) return cb(); - if (proc.pm2_env.max_memory_restart < (proc_key.monit.memory / (1024*1024))) { + if (proc_key.monit.memory !== undefined && + proc.pm2_env.max_memory_restart !== undefined && + proc.pm2_env.max_memory_restart < (proc_key.monit.memory / (1024*1024))) { console.log('[PM2][WORKER] Process %s restarted because it exceeds --max-memory-restart value', proc.pm2_env.pm_id); God.restartProcessId({ @@ -37,8 +37,24 @@ module.exports = function(God) { return cb(); }); } - else + else if (proc.pm2_env.status !== undefined && + proc_key.monit.memory !== undefined && + proc.pm2_env.status === cst.ONLINE_STATUS && + proc_key.monit.memory === 0) { + console.log('[PM2][WORKER] Process %s restarted because it uses 0 memory and has ONLINE status', + proc.pm2_env.pm_id); + God.restartProcessId({ + id: proc.pm2_env.pm_id, + env: proc.pm2_env.env + }, function(err, data) { + if (err) + console.error(err.stack || err); + return cb(); + }); + } + else { return cb(); + } }; var versioningRefresh = function(proc_key, cb) {