mirror of
https://github.com/Unitech/pm2.git
synced 2025-12-08 20:35:53 +00:00
580 lines
16 KiB
JavaScript
Executable File
580 lines
16 KiB
JavaScript
Executable File
#!/usr/bin/env node
|
|
|
|
'use strict';
|
|
|
|
var commander = require('commander');
|
|
var fs = require('fs');
|
|
var path = require('path');
|
|
var p = path;
|
|
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');
|
|
var platform = require('os').platform();
|
|
|
|
CLI.pm2Init();
|
|
|
|
commander.version(pkg.version)
|
|
.option('-v --version', 'get version')
|
|
.option('-s --silent', 'hide all messages', false)
|
|
.option('-m --mini-list', 'display a compacted list without formatting')
|
|
.option('-f --force', 'force actions')
|
|
.option('-n --name <name>', 'set a <name> for script')
|
|
.option('-i --instances <number>', 'launch [number] instances (for networked app)(load balanced)')
|
|
.option('-l --log [path]', 'specify entire log file (error and out are both included)')
|
|
.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')
|
|
.option('-c --cron <cron_pattern>', 'restart a running process based on a cron pattern')
|
|
.option('-w --write', 'write configuration in local folder')
|
|
.option('--interpreter <interpreter>', 'the interpreter pm2 should use for executing app (bash, python...)')
|
|
.option('--log-date-format <momentjs format>', 'add custom prefix timestamp to logs')
|
|
.option('--no-daemon', 'run pm2 daemon in the foreground if it doesn\'t exist already')
|
|
.option('--merge-logs', 'merge logs from different instances but keep error and out separated')
|
|
.option('--watch', 'watch application folder for changes')
|
|
.option('--ignore-watch <folders|files>', 'folder/files to be ignored watching, chould be a specific name or regex - e.g. --ignore-watch="test node_modules \"some scripts\""')
|
|
.option('--node-args <node_args>', 'space delimited arguments to pass to node in cluster mode - e.g. --node-args="--debug=7001 --trace-deprecation"')
|
|
.option('--no-color', 'skip colors')
|
|
.usage('[cmd] app');
|
|
|
|
commander.on('--help', function() {
|
|
console.log(' Basic Examples:');
|
|
console.log('');
|
|
console.log(' Start an app using all CPUs available + set a name :');
|
|
console.log(' $ pm2 start app.js -i max --name "api"');
|
|
console.log('');
|
|
console.log(' Restart the previous app launched, by name :');
|
|
console.log(' $ pm2 restart api');
|
|
console.log('');
|
|
console.log(' Stop the app :');
|
|
console.log(' $ pm2 stop api');
|
|
console.log('');
|
|
console.log(' Restart the app that is stopped :');
|
|
console.log(' $ pm2 restart api');
|
|
console.log('');
|
|
console.log(' Remove the app from the process list :');
|
|
console.log(' $ pm2 delete api');
|
|
console.log('');
|
|
console.log(' Kill daemon pm2 :');
|
|
console.log(' $ pm2 kill');
|
|
console.log('');
|
|
console.log(' Update pm2 :');
|
|
console.log(' $ npm install pm2@latest -g ; pm2 updatePM2');
|
|
console.log('');
|
|
console.log(' More examples in https://github.com/Unitech/pm2#usagefeatures');
|
|
console.log('');
|
|
console.log(' Deployment help:');
|
|
console.log('');
|
|
console.log(' $ pm2 deploy help');
|
|
console.log('');
|
|
console.log('');
|
|
});
|
|
|
|
if (process.argv.indexOf('-s') > -1) {
|
|
for(var key in console){
|
|
var code = key.charCodeAt(0);
|
|
if(code >= 97 && code <= 122){
|
|
console[key] = function(){};
|
|
}
|
|
}
|
|
}
|
|
|
|
function beginCommandProcessing() {
|
|
//
|
|
// Wait Satan is connected to God to launch parsing
|
|
//
|
|
// This message is triggered once the RPC Client is connected to the Daemon
|
|
// in file Satan.js, method Satan.launchRPC
|
|
//
|
|
debug('Got message from Satan as succesfully connected to PM2, now parsing arguments');
|
|
|
|
// Deprecate message for update as there is an automatic update procedure now
|
|
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);
|
|
});
|
|
}
|
|
|
|
if (process.argv.indexOf('--no-daemon') > -1) {
|
|
//
|
|
// Start daemon if it does not exist
|
|
//
|
|
// Function checks if --no-daemon option is present,
|
|
// and starts daemon in the same process if it does not exists
|
|
//
|
|
console.log('pm2 launched in no-daemon mode (you can add DEBUG="*" env variable to get more messages)');
|
|
Satan.pingDaemon(function(ab) {
|
|
if (ab == true) {
|
|
console.error('>> pm2 is already daemonized ! You should kill it before launching it in no-daemon mode'.red);
|
|
}
|
|
Satan.start(true, function() {
|
|
beginCommandProcessing();
|
|
});
|
|
});
|
|
}
|
|
else
|
|
Satan.start(false, function() {
|
|
beginCommandProcessing();
|
|
});
|
|
|
|
|
|
//
|
|
// Helper function to fail when unknown command arguments are passed
|
|
//
|
|
function failOnUnknown(fn) {
|
|
return function(arg) {
|
|
if (arguments.length > 1) {
|
|
console.log(cst.PREFIX_MSG + '\nUnknown command argument: ' + arg);
|
|
commander.outputHelp();
|
|
process.exit(cst.ERROR_EXIT);
|
|
}
|
|
return fn.apply(this, arguments);
|
|
};
|
|
}
|
|
|
|
//
|
|
// Start command
|
|
//
|
|
commander.command('start <file|json|stdin>')
|
|
.option('--watch', 'Watch folder for changes')
|
|
.description('start and daemonize an app')
|
|
.action(function(cmd) {
|
|
if (cmd == "-") {
|
|
process.stdin.resume();
|
|
process.stdin.setEncoding('utf8');
|
|
process.stdin.on('data', function (cmd) {
|
|
process.stdin.pause();
|
|
CLI.startJson(cmd, commander, 'pipe');
|
|
});
|
|
} else if (cmd.slice(-5) == '.json')
|
|
CLI.startJson(cmd, commander, 'file');
|
|
else
|
|
CLI.start(cmd, commander);
|
|
});
|
|
|
|
commander.command('deploy <file|environment>')
|
|
.description('deploy your json')
|
|
.action(function(cmd) {
|
|
CLI.deploy(cmd, commander);
|
|
});
|
|
|
|
commander.command('startOrRestart <json>')
|
|
.description('start or restart JSON file')
|
|
.action(function(file) {
|
|
CLI._jsonStartOrAction('restart', file, commander);
|
|
});
|
|
|
|
commander.command('startOrReload <json>')
|
|
.description('start or gracefully reload JSON file')
|
|
.action(function(file) {
|
|
CLI._jsonStartOrAction('reload', file, commander);
|
|
});
|
|
|
|
commander.command('startOrGracefulReload <json>')
|
|
.description('start or gracefully reload JSON file')
|
|
.action(function(file) {
|
|
CLI._jsonStartOrAction('gracefulReload', file, commander);
|
|
});
|
|
|
|
//
|
|
// Stop specific id
|
|
//
|
|
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>)')
|
|
.action(function(param) {
|
|
CLI.stop(param);
|
|
});
|
|
|
|
//
|
|
// Stop All processes
|
|
//
|
|
commander.command('restart <id|name|all|json|stdin>')
|
|
.option('--watch', 'Toggle watching folder for changes')
|
|
.description('restart a process')
|
|
.action(function(param) {
|
|
CLI.restart(param);
|
|
});
|
|
|
|
//
|
|
// Reload process(es)
|
|
//
|
|
commander.command('reload <name|all>')
|
|
.description('reload processes (note that its for app using HTTP/HTTPS)')
|
|
.action(function(pm2_id) {
|
|
CLI.reload(pm2_id);
|
|
});
|
|
|
|
//
|
|
// Reload process(es)
|
|
//
|
|
commander.command('gracefulReload <name|all>')
|
|
.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.')
|
|
// .action(function(pm2_id) {
|
|
// CLI.gracefulStop(pm2_id);
|
|
// });
|
|
|
|
//
|
|
// 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')
|
|
.action(function(name) {
|
|
if (name == "-") {
|
|
process.stdin.resume();
|
|
process.stdin.setEncoding('utf8');
|
|
process.stdin.on('data', function (param) {
|
|
process.stdin.pause();
|
|
CLI.delete(param, 'pipe');
|
|
});
|
|
} else
|
|
CLI.delete(name,'');
|
|
});
|
|
|
|
//
|
|
// Send system signal to process
|
|
//
|
|
commander.command('sendSignal <signal> <pm2_id|name>')
|
|
.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);
|
|
CLI.sendSignalToProcessName(signal, pm2_id);
|
|
} else {
|
|
console.log(cst.PREFIX_MSG + 'Sending signal to process id ' + pm2_id);
|
|
CLI.sendSignalToProcessId(signal, pm2_id);
|
|
}
|
|
});
|
|
|
|
//
|
|
// Stop and delete a process by name from database
|
|
//
|
|
commander.command('ping')
|
|
.description('ping pm2 daemon - if not up it will launch it')
|
|
.action(function() {
|
|
CLI.ping();
|
|
});
|
|
|
|
commander.command('updatePM2')
|
|
.description('update in-memory PM2 with local PM2')
|
|
.action(function() {
|
|
CLI.updatePM2()
|
|
});
|
|
|
|
commander.command('update')
|
|
.description('(alias) update in-memory PM2 with local PM2')
|
|
.action(function() {
|
|
CLI.updatePM2()
|
|
});
|
|
|
|
//
|
|
// Interact
|
|
//
|
|
commander.command('interact [secret_key|command] [public_key] [machine_name]')
|
|
.description('agent actions for keymetrics.io - command can be stop|info|delete|restart')
|
|
.action(function(secret_key, public_key, machine) {
|
|
if (secret_key == 'stop' || secret_key == 'kill') {
|
|
console.log(chalk.cyan('[Keymetrics.io]') + ' Stopping agent...');
|
|
CLI.killInteract(function() {
|
|
console.log(chalk.cyan('[Keymetrics.io]') + ' Stopped');
|
|
return process.exit(cst.SUCCESS_EXIT);
|
|
});
|
|
return false;
|
|
}
|
|
if (secret_key == 'info') {
|
|
console.log(chalk.cyan('[Keymetrics.io]') + ' Getting agent information...');
|
|
return CLI.infoInteract();
|
|
}
|
|
if (secret_key == 'delete') {
|
|
CLI.killInteract(function() {
|
|
try {
|
|
fs.unlinkSync(cst.INTERACTION_CONF);
|
|
} catch(e) { console.log(e.stack) }
|
|
console.log(chalk.cyan('[Keymetrics.io]') + ' Agent interaction ended');
|
|
return process.exit(cst.SUCCESS_EXIT);
|
|
});
|
|
return false;
|
|
}
|
|
if (secret_key == 'start' || secret_key == 'restart')
|
|
return CLI.interact(null, null, null);
|
|
if (secret_key && !public_key) {
|
|
console.error(chalk.cyan('[Keymetrics.io]') + ' Command [%s] unknown or missing public key', secret_key);
|
|
return process.exit(cst.ERROR_EXIT);
|
|
}
|
|
return CLI.interact(secret_key, public_key, machine);
|
|
});
|
|
|
|
//
|
|
// Web interface
|
|
//
|
|
commander.command('web')
|
|
.description('launch an health API on port ' + cst.WEB_INTERFACE)
|
|
.action(function() {
|
|
console.log('Launching web interface on port ' + cst.WEB_INTERFACE);
|
|
CLI.web();
|
|
});
|
|
|
|
//
|
|
// Save processes to file
|
|
//
|
|
commander.command('dump')
|
|
.description('dump all processes for resurrecting them later')
|
|
.action(failOnUnknown(function() {
|
|
console.log(cst.PREFIX_MSG + 'Dumping processes');
|
|
CLI.dump();
|
|
}));
|
|
|
|
commander.command('save')
|
|
.description('(alias) dump all processes for resurrecting them later')
|
|
.action(failOnUnknown(function() {
|
|
console.log(cst.PREFIX_MSG + 'Dumping processes');
|
|
CLI.dump();
|
|
}));
|
|
|
|
//
|
|
// Resurrect
|
|
//
|
|
commander.command('resurrect')
|
|
.description('resurrect previously dumped processes')
|
|
.action(failOnUnknown(function() {
|
|
console.log(cst.PREFIX_MSG + 'Resurrecting');
|
|
CLI.resurrect();
|
|
}));
|
|
|
|
//
|
|
// Set pm2 to startup
|
|
//
|
|
commander.command('startup [platform]')
|
|
.description('auto resurrect process at startup. [platform] = ubuntu, centos, gentoo or systemd')
|
|
.action(function(_platform, cmd) {
|
|
CLI.startup(_platform || platform, commander);
|
|
});
|
|
|
|
|
|
//
|
|
// Sample generate
|
|
//
|
|
commander.command('generate')
|
|
.description('generate an ecosystem.json configuration file')
|
|
.action(function(name) {
|
|
CLI.generateSample(name);
|
|
});
|
|
|
|
commander.command('ecosystem')
|
|
.description('generate an ecosystem.json configuration file')
|
|
.action(function(name) {
|
|
CLI.generateSample(name);
|
|
});
|
|
|
|
commander.command('reset <name|id|all>')
|
|
.description('reset counters for process')
|
|
.action(function(proc_id) {
|
|
CLI.reset(proc_id);
|
|
});
|
|
|
|
commander.command('describe <id>')
|
|
.description('describe all parameters of a process id')
|
|
.action(function(proc_id) {
|
|
CLI.describe(proc_id);
|
|
});
|
|
|
|
commander.command('desc <id>')
|
|
.description('(alias) describe all parameters of a process id')
|
|
.action(function(proc_id) {
|
|
CLI.describe(proc_id);
|
|
});
|
|
|
|
commander.command('info <id>')
|
|
.description('(alias) describe all parameters of a process id')
|
|
.action(function(proc_id) {
|
|
CLI.describe(proc_id);
|
|
});
|
|
|
|
commander.command('show <id>')
|
|
.description('(alias) describe all parameters of a process id')
|
|
.action(function(proc_id) {
|
|
CLI.describe(proc_id);
|
|
});
|
|
|
|
//
|
|
// List command
|
|
//
|
|
commander.command('list')
|
|
.description('list all processes')
|
|
.action(function() {
|
|
CLI.list()
|
|
});
|
|
|
|
commander.command('ls')
|
|
.description('(alias) list all processes')
|
|
.action(function() {
|
|
CLI.list()
|
|
});
|
|
|
|
commander.command('l')
|
|
.description('(alias) list all processes')
|
|
.action(function() {
|
|
CLI.list()
|
|
});
|
|
|
|
commander.command('status')
|
|
.description('(alias) list all processes')
|
|
.action(function() {
|
|
CLI.list()
|
|
});
|
|
|
|
|
|
// List in raw json
|
|
commander.command('jlist')
|
|
.description('list all processes in JSON format')
|
|
.action(function() {
|
|
CLI.jlist()
|
|
});
|
|
|
|
// List in prettified Json
|
|
commander.command('prettylist')
|
|
.description('print json in a prettified JSON')
|
|
.action(failOnUnknown(function() {
|
|
CLI.jlist(true);
|
|
}));
|
|
|
|
//
|
|
// Monitoring command
|
|
//
|
|
commander.command('monit')
|
|
.description('launch termcaps monitoring')
|
|
.action(function() {
|
|
CLI.monit()
|
|
});
|
|
|
|
commander.command('m')
|
|
.description('(alias) launch termcaps monitoring')
|
|
.action(function() {
|
|
CLI.monit()
|
|
});
|
|
|
|
|
|
//
|
|
// Flushing command
|
|
//
|
|
commander.command('flush')
|
|
.description('flush logs')
|
|
.action(failOnUnknown(function() {
|
|
CLI.flush();
|
|
}));
|
|
|
|
//
|
|
// Reload all logs
|
|
//
|
|
commander.command('reloadLogs')
|
|
.description('reload all logs')
|
|
.action(function() {
|
|
CLI.reloadLogs();
|
|
});
|
|
|
|
//
|
|
// Log streaming
|
|
//
|
|
commander.command('logs [id|name]')
|
|
.option('--lines <N>', 'output the last N lines, instead of the last 20')
|
|
.description('stream logs file. Default stream all logs')
|
|
.action(function(id, cmd) {
|
|
var line = 20;
|
|
if(typeof cmd.lines == 'number'){
|
|
line = cmd.lines;
|
|
}
|
|
CLI.streamLogs(id, line);
|
|
});
|
|
|
|
commander.command('ilogs')
|
|
.description('advanced interface to display logs')
|
|
.action(function(id) {
|
|
CLI.ilogs(id);
|
|
});
|
|
|
|
|
|
//
|
|
// Kill
|
|
//
|
|
commander.command('kill')
|
|
.description('kill daemon')
|
|
.action(failOnUnknown(function(arg) {
|
|
CLI.killDaemon(function() {
|
|
process.exit(cst.SUCCESS_EXIT);
|
|
});
|
|
}));
|
|
|
|
//
|
|
// Update repository for a given app
|
|
//
|
|
|
|
commander.command('pull <name> [commit_id]')
|
|
.description('updates repository for a given app')
|
|
.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);
|
|
});
|
|
|
|
//
|
|
// Update repository to the next commit for a given app
|
|
//
|
|
commander.command('forward <name>')
|
|
.description('updates repository to the next commit for a given app')
|
|
.action(function(pm2_name) {
|
|
CLI.forward(pm2_name);
|
|
});
|
|
|
|
//
|
|
// Downgrade repository to the previous commit for a given app
|
|
//
|
|
commander.command('backward <name>')
|
|
.description('downgrades repository to the previous commit for a given app')
|
|
.action(function(pm2_name) {
|
|
CLI.backward(pm2_name);
|
|
});
|
|
|
|
//
|
|
// Catch all
|
|
//
|
|
commander.command('*')
|
|
.action(function() {
|
|
console.log(cst.PREFIX_MSG + '\nCommand not found');
|
|
commander.outputHelp();
|
|
process.exit(cst.ERROR_EXIT);
|
|
});
|
|
|
|
//
|
|
// Display help
|
|
//
|
|
if (process.argv.length == 2) {
|
|
commander.parse(process.argv);
|
|
commander.outputHelp();
|
|
process.exit(cst.ERROR_EXIT);
|
|
}
|