mirror of
https://github.com/Unitech/pm2.git
synced 2025-12-08 20:35:53 +00:00
module system v3: independant folder for each module + retrocompat v2/v3
This commit is contained in:
parent
58369a5533
commit
b5372aa19e
5
bin/pm2
5
bin/pm2
@ -440,8 +440,11 @@ commander.command('update')
|
||||
*/
|
||||
commander.command('install <module|git:// url>')
|
||||
.alias('module:install')
|
||||
.option('--v1', 'install module in v1 manner (do not use it)')
|
||||
.description('install or update a module and run it forever')
|
||||
.action(function(plugin_name) {
|
||||
.action(function(plugin_name, opts) {
|
||||
if (opts.v1)
|
||||
commander.v1 = true;
|
||||
pm2.install(plugin_name, commander);
|
||||
});
|
||||
|
||||
|
||||
@ -14,19 +14,13 @@ var chalk = require('chalk');
|
||||
var Configuration = require('../../Configuration.js');
|
||||
var cst = require('../../../constants.js');
|
||||
var Common = require('../../Common');
|
||||
var UX = require('../CliUx.js');
|
||||
var Utility = require('../../Utility.js');
|
||||
var semver = require('semver');
|
||||
|
||||
var ModularizerV1 = require('./Modularizerv1.js');
|
||||
var Modularizer = module.exports = {};
|
||||
|
||||
var MODULE_CONF_PREFIX = 'module-db';
|
||||
var MODULE_CONF_PREFIX = 'module-db-v2';
|
||||
|
||||
function startModule(CLI, opts, cb) {
|
||||
/** SCRIPT
|
||||
* Open file and make the script detection smart
|
||||
*/
|
||||
|
||||
if (!opts.cmd) throw new Error('module package.json not defined');
|
||||
if (!opts.development_mode) opts.development_mode = false;
|
||||
|
||||
@ -51,16 +45,6 @@ function startModule(CLI, opts, cb) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that the module is valid
|
||||
* If not, delete
|
||||
*/
|
||||
if (isValidModule(package_json) === false) {
|
||||
if (!opts.development_mode) shelljs.rm('-rf', opts.proc_pabth);
|
||||
Common.printError(cst.PREFIX_MSG_MOD + 'Module uninstalled');
|
||||
return cb(new Error('Invalid module (script is missing)'));
|
||||
}
|
||||
|
||||
Common.extend(opts, {
|
||||
cwd : opts.proc_path,
|
||||
watch : opts.development_mode,
|
||||
@ -75,7 +59,7 @@ function startModule(CLI, opts, cb) {
|
||||
});
|
||||
};
|
||||
|
||||
function installModule(CLI, module_name, opts, cb) {
|
||||
Modularizer.installModule = function(CLI, module_name, opts, cb) {
|
||||
var proc_path = '',
|
||||
cmd = '',
|
||||
conf = {},
|
||||
@ -87,9 +71,9 @@ function installModule(CLI, module_name, opts, cb) {
|
||||
}
|
||||
|
||||
if (module_name == '.') {
|
||||
/**
|
||||
/*******************
|
||||
* Development mode
|
||||
*/
|
||||
*******************/
|
||||
Common.printOut(cst.PREFIX_MSG_MOD + 'Installing local module in DEVELOPMENT MODE with WATCH auto restart');
|
||||
development_mode = true;
|
||||
proc_path = process.cwd();
|
||||
@ -107,100 +91,94 @@ function installModule(CLI, module_name, opts, cb) {
|
||||
Common.printOut(cst.PREFIX_MSG_MOD + 'Module successfully installed and launched');
|
||||
return cb(null, dt);
|
||||
});
|
||||
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
/**
|
||||
* Production mode
|
||||
*/
|
||||
var cli = {};
|
||||
|
||||
if (semver.satisfies(process.version, '> 0.12') && shelljs.which('yarn')) {
|
||||
cli = {
|
||||
bin : 'yarn',
|
||||
cmd : 'add'
|
||||
}
|
||||
}
|
||||
else {
|
||||
cli = {
|
||||
bin : 'npm',
|
||||
cmd : 'install'
|
||||
}
|
||||
/******************
|
||||
* Production mode
|
||||
******************/
|
||||
Common.printOut(cst.PREFIX_MSG_MOD + 'Calling ' + chalk.bold.red('[NPM]') + ' to install ' + module_name + ' ...');
|
||||
|
||||
var canonic_module_name = Utility.getCanonicModuleName(module_name);
|
||||
var install_path = path.join(cst.DEFAULT_MODULE_PATH, canonic_module_name);
|
||||
|
||||
var install_instance = spawn(cst.IS_WINDOWS ? 'npm.cmd' : 'npm', ['install', module_name, '--loglevel=error', '--prefix', install_path ], {
|
||||
stdio : 'inherit',
|
||||
env: process.env,
|
||||
shell : true
|
||||
});
|
||||
|
||||
install_instance.on('close', finalize);
|
||||
|
||||
install_instance.on('error', function (err) {
|
||||
console.error(err.stack || err);
|
||||
});
|
||||
|
||||
function finalize(code) {
|
||||
if (code != 0) {
|
||||
// @todo Rollback
|
||||
return cb(new Error("Installation failed"));
|
||||
}
|
||||
|
||||
Common.printOut(cst.PREFIX_MSG_MOD + 'Calling ' + chalk.bold.red('[' + cli.bin.toUpperCase() + ']') + ' to install ' + module_name + ' ...');
|
||||
Common.printOut(cst.PREFIX_MSG_MOD + 'Module downloaded');
|
||||
|
||||
var install_instance = spawn(cst.IS_WINDOWS ? cli.bin + '.cmd' : cli.bin, [cli.cmd, module_name, '--loglevel=error'], {
|
||||
stdio : 'inherit',
|
||||
env: process.env,
|
||||
shell : true,
|
||||
cwd : cst.PM2_ROOT_PATH
|
||||
var proc_path = path.join(install_path, 'node_modules', canonic_module_name);
|
||||
var package_json_path = path.join(proc_path, 'package.json');
|
||||
|
||||
// Append default configuration to module configuration
|
||||
try {
|
||||
var conf = JSON.parse(fs.readFileSync(package_json_path).toString()).config;
|
||||
|
||||
if (conf) {
|
||||
Object.keys(conf).forEach(function(key) {
|
||||
Configuration.setSyncIfNotExist(canonic_module_name + ':' + key, conf[key]);
|
||||
});
|
||||
}
|
||||
} catch(e) {
|
||||
Common.printError(e);
|
||||
}
|
||||
|
||||
opts = Common.extend(opts, {
|
||||
cmd : package_json_path,
|
||||
development_mode : development_mode,
|
||||
proc_path : proc_path
|
||||
});
|
||||
|
||||
install_instance.on('close', finalize);
|
||||
Configuration.set(MODULE_CONF_PREFIX + ':' + canonic_module_name, {
|
||||
uid : opts.uid,
|
||||
gid : opts.gid
|
||||
}, function(err, data) {
|
||||
if (err) return cb(err);
|
||||
|
||||
install_instance.on('error', function (err) {
|
||||
console.error(err.stack || err);
|
||||
});
|
||||
startModule(CLI, opts, function(err, dt) {
|
||||
if (err) return cb(err);
|
||||
|
||||
function finalize(code) {
|
||||
if (code != 0) {
|
||||
return cb(new Error("Installation failed"));
|
||||
}
|
||||
if (process.env.PM2_PROGRAMMATIC === 'true')
|
||||
return cb(null, dt);
|
||||
|
||||
Common.printOut(cst.PREFIX_MSG_MOD + 'Module downloaded');
|
||||
|
||||
var canonic_module_name = Utility.getCanonicModuleName(module_name);
|
||||
|
||||
proc_path = p.join(cst.PM2_ROOT_PATH, 'node_modules', canonic_module_name);
|
||||
|
||||
cmd = p.join(proc_path, cst.DEFAULT_MODULE_JSON);
|
||||
|
||||
/**
|
||||
* Append default configuration to module configuration
|
||||
*/
|
||||
try {
|
||||
var conf = JSON.parse(fs.readFileSync(path.join(proc_path, 'package.json')).toString()).config;
|
||||
if (conf) {
|
||||
Object.keys(conf).forEach(function(key) {
|
||||
Configuration.setSyncIfNotExist(canonic_module_name + ':' + key, conf[key]);
|
||||
});
|
||||
}
|
||||
} catch(e) {
|
||||
Common.printError(e);
|
||||
}
|
||||
|
||||
opts = Common.extend(opts, {
|
||||
cmd : cmd,
|
||||
development_mode : development_mode,
|
||||
proc_path : proc_path
|
||||
});
|
||||
|
||||
Configuration.set(MODULE_CONF_PREFIX + ':' + canonic_module_name, {
|
||||
uid : opts.uid,
|
||||
gid : opts.gid
|
||||
}, function(err, data) {
|
||||
|
||||
startModule(CLI, opts, function(err, dt) {
|
||||
if (err) return cb(err);
|
||||
|
||||
if (process.env.PM2_PROGRAMMATIC === 'true')
|
||||
return cb(null, dt);
|
||||
|
||||
CLI.conf(canonic_module_name, function() {
|
||||
Common.printOut(cst.PREFIX_MSG_MOD + 'Module successfully installed and launched');
|
||||
Common.printOut(cst.PREFIX_MSG_MOD + 'Edit configuration via: `pm2 conf`');
|
||||
return cb(null, dt);
|
||||
});
|
||||
CLI.conf(canonic_module_name, function() {
|
||||
Common.printOut(cst.PREFIX_MSG_MOD + 'Module successfully installed and launched');
|
||||
Common.printOut(cst.PREFIX_MSG_MOD + 'Edit configuration via: `pm2 conf`');
|
||||
return cb(null, dt);
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function uninstallModule(CLI, module_name, cb) {
|
||||
var proc_path = p.join(cst.PM2_ROOT_PATH, 'node_modules', module_name);
|
||||
|
||||
Configuration.unsetSync(MODULE_CONF_PREFIX + ':' + module_name);
|
||||
try {
|
||||
// v1 uninstallation
|
||||
fs.statSync(proc_path)
|
||||
Configuration.unsetSync('module-db:' + module_name);
|
||||
} catch(e) {
|
||||
proc_path = p.join(cst.DEFAULT_MODULE_PATH, module_name, 'node_modules', module_name);
|
||||
Configuration.unsetSync(MODULE_CONF_PREFIX + ':' + module_name);
|
||||
}
|
||||
|
||||
|
||||
CLI.deleteModule(module_name, function(err, data) {
|
||||
if (err) {
|
||||
@ -222,117 +200,6 @@ function uninstallModule(CLI, module_name, cb) {
|
||||
});
|
||||
}
|
||||
|
||||
function installLangModule(module_name, cb) {
|
||||
var node_module_path = path.resolve(path.join(__dirname, '../../../'));
|
||||
|
||||
var cli = semver.satisfies(process.version, '> 0.12') && shelljs.which('yarn') ? 'yarn' : 'npm'
|
||||
Common.printOut(cst.PREFIX_MSG_MOD + 'Calling ' + chalk.bold.red('[' + cli.toUpperCase() + ']') + ' to install ' + module_name + ' ...');
|
||||
|
||||
var install_instance = spawn(cst.IS_WINDOWS ? 'npm.cmd' : 'npm', ['install', module_name, '--loglevel=error'], {
|
||||
stdio : 'inherit',
|
||||
env: process.env,
|
||||
shell : true,
|
||||
cwd : node_module_path
|
||||
});
|
||||
|
||||
install_instance.on('close', function(code) {
|
||||
if (code > 0)
|
||||
return cb(new Error('Module install failed'));
|
||||
return cb(null);
|
||||
});
|
||||
|
||||
install_instance.on('error', function (err) {
|
||||
console.error(err.stack || err);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* List modules on the old way
|
||||
*/
|
||||
var listModules = function() {
|
||||
var module_folder = p.join(cst.PM2_ROOT_PATH, 'node_modules');
|
||||
var ret = {};
|
||||
|
||||
shelljs.config.silent = true;
|
||||
var modules = shelljs.ls(module_folder);
|
||||
shelljs.config.silent = false;
|
||||
|
||||
modules.forEach(function(module_name) {
|
||||
if (module_name.indexOf('pm2-') > -1)
|
||||
ret[module_name] = {};
|
||||
});
|
||||
|
||||
return ret;
|
||||
};
|
||||
|
||||
/**
|
||||
* List modules based on internal database
|
||||
*/
|
||||
var listModulesV2 = Modularizer.listModules = function() {
|
||||
var config = Configuration.getSync(MODULE_CONF_PREFIX);
|
||||
|
||||
if (!config) {
|
||||
var modules_already_installed = listModules();
|
||||
|
||||
Object.keys(modules_already_installed).forEach(function(module_name) {
|
||||
Configuration.setSync(MODULE_CONF_PREFIX + ':' + module_name, {});
|
||||
});
|
||||
return modules_already_installed;
|
||||
}
|
||||
|
||||
return config;
|
||||
};
|
||||
|
||||
Modularizer.getAdditionalConf = function(app_name) {
|
||||
if (!app_name) throw new Error('No app_name defined');
|
||||
|
||||
var module_conf = Configuration.getAllSync();
|
||||
|
||||
var additional_env = {};
|
||||
|
||||
if (!module_conf[app_name]) {
|
||||
additional_env = {};
|
||||
additional_env[app_name] = {};
|
||||
}
|
||||
else {
|
||||
additional_env = Common.clone(module_conf[app_name]);
|
||||
additional_env[app_name] = JSON.stringify(module_conf[app_name]);
|
||||
}
|
||||
return additional_env;
|
||||
};
|
||||
|
||||
Modularizer.launchAll = function(CLI, cb) {
|
||||
var module_folder = p.join(cst.PM2_ROOT_PATH, 'node_modules');
|
||||
|
||||
var modules = listModulesV2();
|
||||
|
||||
async.eachLimit(Object.keys(modules), 1, function(module, next) {
|
||||
var pmod = p.join(module_folder, module, cst.DEFAULT_MODULE_JSON);
|
||||
|
||||
Common.printOut(cst.PREFIX_MSG_MOD + 'Starting module ' + module);
|
||||
|
||||
var opts = {};
|
||||
|
||||
if (modules[module] != true) {
|
||||
Common.extend(opts, modules[module]);
|
||||
}
|
||||
|
||||
Common.extend(opts, {
|
||||
cmd : pmod,
|
||||
development_mode : false,
|
||||
proc_path : p.join(module_folder, module)
|
||||
});
|
||||
|
||||
startModule(CLI, opts, function(err, dt) {
|
||||
if (err) console.error(err);
|
||||
return next();
|
||||
});
|
||||
|
||||
}, function() {
|
||||
return cb ? cb(null) : false;
|
||||
});
|
||||
};
|
||||
|
||||
Modularizer.install = function(CLI, module_name, opts, cb) {
|
||||
Common.printOut(cst.PREFIX_MSG_MOD + 'Installing module ' + module_name);
|
||||
|
||||
@ -341,7 +208,7 @@ Modularizer.install = function(CLI, module_name, opts, cb) {
|
||||
if (module_name == 'v8-profiler' || module_name == 'profiler') {
|
||||
installLangModule('v8-profiler-node8', function(err) {
|
||||
if (err) {
|
||||
Common.printError(cst.PREFIX_MSG_MOD_ERR + chalk.bold.green('Profling installation has FAILED (checkout previous logs)'));
|
||||
Common.printError(cst.PREFIX_MSG_MOD_ERR + chalk.bold.green('Profiling installation has FAILED (checkout previous logs)'));
|
||||
return cb(err);
|
||||
}
|
||||
|
||||
@ -386,25 +253,73 @@ Modularizer.install = function(CLI, module_name, opts, cb) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Update
|
||||
moduleExist(CLI, canonic_module_name, function(exists) {
|
||||
|
||||
if (moduleExist(canonic_module_name) === true) {
|
||||
/**
|
||||
* Update
|
||||
*/
|
||||
Common.printOut(cst.PREFIX_MSG_MOD + 'Module already installed. Updating.');
|
||||
if (exists) {
|
||||
Common.printOut(cst.PREFIX_MSG_MOD + 'Module already installed. Updating.');
|
||||
|
||||
uninstallModule(CLI, canonic_module_name, function(err) {
|
||||
return installModule(CLI, module_name, opts, cb);
|
||||
return uninstallModule(CLI, canonic_module_name, function(err) {
|
||||
return ModularizerV1.installModule(CLI, module_name, opts, cb);
|
||||
});
|
||||
}
|
||||
|
||||
// Install
|
||||
Modularizer.installModule(CLI, module_name, opts, cb);
|
||||
})
|
||||
|
||||
};
|
||||
|
||||
// Start V1 and V2 modules
|
||||
Modularizer.launchAll = function(CLI, cb) {
|
||||
ModularizerV1.launchModules(CLI, function() {
|
||||
Modularizer.launchModules(CLI, cb);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* List modules based on modules present in ~/.pm2/modules/ folder
|
||||
*/
|
||||
Modularizer.listModules = function() {
|
||||
return Configuration.getSync(MODULE_CONF_PREFIX);
|
||||
};
|
||||
|
||||
Modularizer.launchModules = function(CLI, cb) {
|
||||
var modules = Modularizer.listModules();
|
||||
|
||||
if (!modules) return cb();
|
||||
|
||||
async.eachLimit(Object.keys(modules), 1, function(module_name, next) {
|
||||
Common.printOut(cst.PREFIX_MSG_MOD + 'Starting module ' + module_name);
|
||||
|
||||
var install_path = path.join(cst.DEFAULT_MODULE_PATH, module_name);
|
||||
var proc_path = path.join(install_path, 'node_modules', module_name);
|
||||
var package_json_path = path.join(proc_path, 'package.json');
|
||||
|
||||
var opts = {};
|
||||
|
||||
// Merge with embedded configuration inside module_conf (uid, gid)
|
||||
Common.extend(opts, modules[module_name]);
|
||||
|
||||
// Merge meta data to start module properly
|
||||
Common.extend(opts, {
|
||||
// package.json path
|
||||
cmd : package_json_path,
|
||||
// starting mode
|
||||
development_mode : false,
|
||||
// process cwd
|
||||
proc_path : proc_path
|
||||
});
|
||||
|
||||
return false;
|
||||
}
|
||||
startModule(CLI, opts, function(err, dt) {
|
||||
if (err) console.error(err);
|
||||
return next();
|
||||
});
|
||||
|
||||
/**
|
||||
* Install
|
||||
*/
|
||||
installModule(CLI, module_name, opts, cb);
|
||||
};
|
||||
}, function() {
|
||||
return cb ? cb(null) : false;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Uninstall module
|
||||
@ -412,12 +327,11 @@ Modularizer.install = function(CLI, module_name, opts, cb) {
|
||||
Modularizer.uninstall = function(CLI, module_name, cb) {
|
||||
Common.printOut(cst.PREFIX_MSG_MOD + 'Uninstalling module ' + module_name);
|
||||
|
||||
//if (moduleExist(module_name) === false && module_name != '.') {
|
||||
//Common.printError(cst.PREFIX_MSG_MOD_ERR + 'Module unknown.');
|
||||
//return cb({msg:'Module unknown'});
|
||||
//}
|
||||
if (module_name == 'all') {
|
||||
var modules = listModulesV2();
|
||||
var modules = Modularizer.listModules();
|
||||
|
||||
if (!modules) return cb();
|
||||
|
||||
async.forEachLimit(Object.keys(modules), 1, function(module, next) {
|
||||
uninstallModule(CLI, module, next);
|
||||
}, cb);
|
||||
@ -428,6 +342,28 @@ Modularizer.uninstall = function(CLI, module_name, cb) {
|
||||
uninstallModule(CLI, canonic_module_name, cb);
|
||||
};
|
||||
|
||||
|
||||
// Expose old module installation method for testing purpose
|
||||
Modularizer.installModuleV1 = ModularizerV1.installModule;
|
||||
|
||||
Modularizer.getAdditionalConf = function(app_name) {
|
||||
if (!app_name) throw new Error('No app_name defined');
|
||||
|
||||
var module_conf = Configuration.getAllSync();
|
||||
|
||||
var additional_env = {};
|
||||
|
||||
if (!module_conf[app_name]) {
|
||||
additional_env = {};
|
||||
additional_env[app_name] = {};
|
||||
}
|
||||
else {
|
||||
additional_env = Common.clone(module_conf[app_name]);
|
||||
additional_env[app_name] = JSON.stringify(module_conf[app_name]);
|
||||
}
|
||||
return additional_env;
|
||||
};
|
||||
|
||||
/**
|
||||
* Publish a module
|
||||
*/
|
||||
@ -522,44 +458,45 @@ Modularizer.generateSample = function(app_name, cb) {
|
||||
rl.question(cst.PREFIX_MSG_MOD + "Module name: ", function(module_name) {
|
||||
samplize(module_name);
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
function isValidModule(conf) {
|
||||
var valid = true;
|
||||
function installLangModule(module_name, cb) {
|
||||
var node_module_path = path.resolve(path.join(__dirname, '../../../'));
|
||||
Common.printOut(cst.PREFIX_MSG_MOD + 'Calling ' + chalk.bold.red('[NPM]') + ' to install ' + module_name + ' ...');
|
||||
|
||||
if (!conf.apps) {
|
||||
Common.printError(cst.PREFIX_MSG_MOD_ERR + 'apps attribute indicating the script to launch is not defined in the package.json');
|
||||
return false;
|
||||
}
|
||||
var install_instance = spawn(cst.IS_WINDOWS ? 'npm.cmd' : 'npm', ['install', module_name, '--loglevel=error'], {
|
||||
stdio : 'inherit',
|
||||
env: process.env,
|
||||
shell : true,
|
||||
cwd : node_module_path
|
||||
});
|
||||
|
||||
if (Array.isArray(conf.apps)) {
|
||||
conf.apps.forEach(function(app) {
|
||||
if (!app.script)
|
||||
valid = false;
|
||||
});
|
||||
}
|
||||
else {
|
||||
if (!conf.apps.script)
|
||||
valid = false;
|
||||
}
|
||||
install_instance.on('close', function(code) {
|
||||
if (code > 0)
|
||||
return cb(new Error('Module install failed'));
|
||||
return cb(null);
|
||||
});
|
||||
|
||||
return valid;
|
||||
install_instance.on('error', function (err) {
|
||||
console.error(err.stack || err);
|
||||
});
|
||||
};
|
||||
|
||||
function moduleExist(module_name) {
|
||||
var modules = getModuleInstalled();
|
||||
function moduleExist(CLI, module_name, cb) {
|
||||
// If old module, force his deletion
|
||||
var modules_v1 = Configuration.getSync('module-db');
|
||||
|
||||
if (module_name.indexOf('/') > -1)
|
||||
module_name = module_name.split('/')[1];
|
||||
if (modules_v1) {
|
||||
modules_v1 = Object.keys(modules_v1);
|
||||
if (modules_v1.indexOf(module_name) > -1) {
|
||||
return uninstallModule(CLI, module_name, function() {
|
||||
cb(false);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return modules.indexOf(module_name) > -1 ? true : false;
|
||||
var modules = Configuration.getSync(MODULE_CONF_PREFIX);
|
||||
if (!modules) return cb(false);
|
||||
modules = Object.keys(modules);
|
||||
return cb(modules.indexOf(module_name) > -1 ? true : false);
|
||||
};
|
||||
|
||||
function getModuleInstalled() {
|
||||
shelljs.config.silent = true;
|
||||
var module_folder = p.join(cst.PM2_ROOT_PATH, 'node_modules');
|
||||
var modules = shelljs.ls('-A', module_folder);
|
||||
shelljs.config.silent = false;
|
||||
return modules;
|
||||
}
|
||||
|
||||
182
lib/API/Modules/Modularizerv1.js
Normal file
182
lib/API/Modules/Modularizerv1.js
Normal file
@ -0,0 +1,182 @@
|
||||
/**
|
||||
* Copyright 2013 the PM2 project authors. All rights reserved.
|
||||
* Use of this source code is governed by a license that
|
||||
* can be found in the LICENSE file.
|
||||
*/
|
||||
var shelljs = require('shelljs');
|
||||
var path = require('path');
|
||||
var fs = require('fs');
|
||||
var async = require('async');
|
||||
var p = path;
|
||||
var spawn = require('child_process').spawn;
|
||||
var chalk = require('chalk');
|
||||
var Configuration = require('../../Configuration.js');
|
||||
var cst = require('../../../constants.js');
|
||||
var Common = require('../../Common');
|
||||
var Utility = require('../../Utility.js');
|
||||
|
||||
var MODULE_CONF_PREFIX = 'module-db';
|
||||
|
||||
var Modularizer = module.exports = {};
|
||||
|
||||
function startModule(CLI, opts, cb) {
|
||||
/** SCRIPT
|
||||
* Open file and make the script detection smart
|
||||
*/
|
||||
|
||||
if (!opts.cmd) throw new Error('module package.json not defined');
|
||||
if (!opts.development_mode) opts.development_mode = false;
|
||||
|
||||
try {
|
||||
var package_json = require(opts.cmd);
|
||||
} catch(e) {
|
||||
Common.printError(e);
|
||||
return cb();
|
||||
}
|
||||
|
||||
/**
|
||||
* Script file detection
|
||||
* 1- *apps* field (default pm2 json configuration)
|
||||
* 2- *bin* field
|
||||
* 3- *main* field
|
||||
*/
|
||||
if (!package_json.apps) {
|
||||
package_json.apps = {};
|
||||
|
||||
if (package_json.bin) {
|
||||
var bin = Object.keys(package_json.bin)[0];
|
||||
|
||||
package_json.apps.script = package_json.bin[bin];
|
||||
}
|
||||
else if (package_json.main) {
|
||||
package_json.apps.script = package_json.main;
|
||||
}
|
||||
}
|
||||
|
||||
Common.extend(opts, {
|
||||
cwd : opts.proc_path,
|
||||
watch : opts.development_mode,
|
||||
force_name : package_json.name,
|
||||
started_as_module : true
|
||||
});
|
||||
|
||||
// Start the module
|
||||
CLI.start(package_json, opts, function(err, data) {
|
||||
if (err) return cb(err);
|
||||
return cb(null, data);
|
||||
});
|
||||
};
|
||||
|
||||
Modularizer.launchModules = function(CLI, cb) {
|
||||
var module_folder = p.join(cst.PM2_ROOT_PATH, 'node_modules');
|
||||
var modules = Configuration.getSync(MODULE_CONF_PREFIX);
|
||||
|
||||
if (!modules) return cb();
|
||||
|
||||
async.eachLimit(Object.keys(modules), 1, function(module, next) {
|
||||
var pmod = p.join(module_folder, module, cst.DEFAULT_MODULE_JSON);
|
||||
|
||||
Common.printOut(cst.PREFIX_MSG_MOD + 'Starting module ' + module);
|
||||
|
||||
var opts = {};
|
||||
|
||||
if (modules[module] != true) {
|
||||
Common.extend(opts, modules[module]);
|
||||
}
|
||||
|
||||
Common.extend(opts, {
|
||||
cmd : pmod,
|
||||
development_mode : false,
|
||||
proc_path : p.join(module_folder, module)
|
||||
});
|
||||
|
||||
startModule(CLI, opts, function(err, dt) {
|
||||
if (err) console.error(err);
|
||||
return next();
|
||||
});
|
||||
|
||||
}, function() {
|
||||
return cb ? cb(null) : false;
|
||||
});
|
||||
}
|
||||
|
||||
Modularizer.installModule = function(CLI, module_name, opts, cb) {
|
||||
var proc_path = '',
|
||||
cmd = '',
|
||||
conf = {},
|
||||
development_mode = false;
|
||||
|
||||
var cli = {
|
||||
bin : 'npm',
|
||||
cmd : 'install'
|
||||
}
|
||||
|
||||
Common.printOut(cst.PREFIX_MSG_MOD + 'Calling ' + chalk.bold.red('[' + cli.bin.toUpperCase() + ']') + ' to install ' + module_name + ' ...');
|
||||
|
||||
var install_instance = spawn(cst.IS_WINDOWS ? cli.bin + '.cmd' : cli.bin, [cli.cmd, module_name, '--loglevel=error'], {
|
||||
stdio : 'inherit',
|
||||
env: process.env,
|
||||
shell : true,
|
||||
cwd : cst.PM2_ROOT_PATH
|
||||
});
|
||||
|
||||
install_instance.on('close', finalize);
|
||||
|
||||
install_instance.on('error', function (err) {
|
||||
console.error(err.stack || err);
|
||||
});
|
||||
|
||||
function finalize(code) {
|
||||
if (code != 0) {
|
||||
return cb(new Error("Installation failed"));
|
||||
}
|
||||
|
||||
Common.printOut(cst.PREFIX_MSG_MOD + 'Module downloaded');
|
||||
|
||||
var canonic_module_name = Utility.getCanonicModuleName(module_name);
|
||||
|
||||
proc_path = p.join(cst.PM2_ROOT_PATH, 'node_modules', canonic_module_name);
|
||||
|
||||
cmd = p.join(proc_path, cst.DEFAULT_MODULE_JSON);
|
||||
|
||||
/**
|
||||
* Append default configuration to module configuration
|
||||
*/
|
||||
try {
|
||||
var conf = JSON.parse(fs.readFileSync(path.join(proc_path, 'package.json')).toString()).config;
|
||||
if (conf) {
|
||||
Object.keys(conf).forEach(function(key) {
|
||||
Configuration.setSyncIfNotExist(canonic_module_name + ':' + key, conf[key]);
|
||||
});
|
||||
}
|
||||
} catch(e) {
|
||||
Common.printError(e);
|
||||
}
|
||||
|
||||
opts = Common.extend(opts, {
|
||||
cmd : cmd,
|
||||
development_mode : development_mode,
|
||||
proc_path : proc_path
|
||||
});
|
||||
|
||||
Configuration.set(MODULE_CONF_PREFIX + ':' + canonic_module_name, {
|
||||
uid : opts.uid,
|
||||
gid : opts.gid,
|
||||
version : 0
|
||||
}, function(err, data) {
|
||||
|
||||
startModule(CLI, opts, function(err, dt) {
|
||||
if (err) return cb(err);
|
||||
|
||||
if (process.env.PM2_PROGRAMMATIC === 'true')
|
||||
return cb(null, dt);
|
||||
|
||||
CLI.conf(canonic_module_name, function() {
|
||||
Common.printOut(cst.PREFIX_MSG_MOD + 'Module successfully installed and launched');
|
||||
Common.printOut(cst.PREFIX_MSG_MOD + 'Edit configuration via: `pm2 conf`');
|
||||
return cb(null, dt);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -21,6 +21,7 @@ var Utility = require('../../Utility.js');
|
||||
var MODULE_CONF_PREFIX = 'module-db';
|
||||
|
||||
var Modularizer = require('./Modularizer.js');
|
||||
var ModularizerV1 = require('./Modularizerv1.js');
|
||||
|
||||
// Special module with post display
|
||||
function postDisplay(app, cb) {
|
||||
@ -78,6 +79,25 @@ module.exports = function(CLI) {
|
||||
opts = {};
|
||||
}
|
||||
|
||||
// Because for test, allow to install module in V1 way
|
||||
if (opts.v1) {
|
||||
Common.printOut('Installing the V1 way...');
|
||||
console.log(opts.uid, opts.gid, opts.v1);
|
||||
ModularizerV1.installModule(this, module_name, opts, function(err, data) {
|
||||
if (err) {
|
||||
Common.printError(cst.PREFIX_MSG_ERR + (err.message || err));
|
||||
return cb ? cb(Common.retErr(err)) : that.speedList(cst.ERROR_EXIT);
|
||||
}
|
||||
|
||||
// Check if special module with post_install display
|
||||
if (data && data[0] && data[0].pm2_env && data[0].pm2_env.PM2_EXTRA_DISPLAY) {
|
||||
return postDisplay.call(that, data[0].pm2_env, cb);
|
||||
}
|
||||
return cb ? cb(null, data) : that.speedList(cst.SUCCESS_EXIT);
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
Modularizer.install(this, module_name, opts, function(err, data) {
|
||||
if (err) {
|
||||
Common.printError(cst.PREFIX_MSG_ERR + (err.message || err));
|
||||
|
||||
2
paths.js
2
paths.js
@ -45,7 +45,7 @@ module.exports = function(PM2_HOME) {
|
||||
|
||||
DEFAULT_PID_PATH : p.resolve(PM2_HOME, 'pids'),
|
||||
DEFAULT_LOG_PATH : p.resolve(PM2_HOME, 'logs'),
|
||||
DEFAULT_MODULE_PATH : p.resolve(PM2_HOME, 'node_modules'),
|
||||
DEFAULT_MODULE_PATH : p.resolve(PM2_HOME, 'modules'),
|
||||
KM_ACCESS_TOKEN : p.resolve(PM2_HOME, 'km-access-token'),
|
||||
DUMP_FILE_PATH : p.resolve(PM2_HOME, 'dump.pm2'),
|
||||
DUMP_BACKUP_FILE_PATH : p.resolve(PM2_HOME, 'dump.pm2.bak'),
|
||||
|
||||
20
test/bash/module-retrocompat.sh
Normal file
20
test/bash/module-retrocompat.sh
Normal file
@ -0,0 +1,20 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
SRC=$(cd $(dirname "$0"); pwd)
|
||||
source "${SRC}/include.sh"
|
||||
|
||||
echo -e "\033[1mRunning tests:\033[0m"
|
||||
|
||||
cd $file_path
|
||||
|
||||
#
|
||||
# Re init module system
|
||||
#
|
||||
rm -rf ~/.pm2/node_modules
|
||||
rm -rf ~/.pm2/modules
|
||||
$pm2 kill
|
||||
#
|
||||
#
|
||||
#
|
||||
|
||||
$pm2 install pm2-probe --v1
|
||||
@ -24,7 +24,7 @@ function spec {
|
||||
success "$1"
|
||||
}
|
||||
|
||||
|
||||
$pm2 uninstall all
|
||||
|
||||
# if [ $TRAVIS ]
|
||||
# then
|
||||
@ -77,6 +77,8 @@ mocha --opts ./mocha.opts ./send_data_process.mocha.js
|
||||
spec "Send data to a process"
|
||||
mocha --opts ./mocha.opts ./modules.mocha.js
|
||||
spec "Module API testing"
|
||||
mocha --opts ./mocha.opts ./module_retrocompat.mocha.js
|
||||
spec "Module retrocompatibility system"
|
||||
|
||||
mocha --opts ./mocha.opts ./json_validation.mocha.js
|
||||
spec "JSON validation test"
|
||||
|
||||
104
test/programmatic/module_retrocompat.mocha.js
Normal file
104
test/programmatic/module_retrocompat.mocha.js
Normal file
@ -0,0 +1,104 @@
|
||||
|
||||
|
||||
var PM2 = require('../..');
|
||||
var should = require('should');
|
||||
var shelljs = require('shelljs');
|
||||
var path = require('path');
|
||||
var fs = require('fs');
|
||||
|
||||
describe('Modules programmatic testing', function() {
|
||||
var pm2;
|
||||
|
||||
// after(function(done) {
|
||||
// pm2.kill(done);
|
||||
// });
|
||||
|
||||
var MODULE_CONF_PATH;
|
||||
var MODULE_PATH;
|
||||
|
||||
it('should instanciate PM2', function() {
|
||||
pm2 = new PM2.custom({
|
||||
//independent : true,
|
||||
//daemon_mode : true
|
||||
});
|
||||
|
||||
MODULE_CONF_PATH = pm2._conf.PM2_MODULE_CONF_FILE;
|
||||
MODULE_PATH = pm2._conf.DEFAULT_MODULE_PATH;
|
||||
});
|
||||
|
||||
it('should cleanup paths', function() {
|
||||
fs.writeFileSync(MODULE_CONF_PATH, '{}');
|
||||
shelljs.rm('-r', MODULE_PATH);
|
||||
shelljs.rm('-r', path.join(pm2._conf.PM2_HOME, 'node_modules'));
|
||||
});
|
||||
|
||||
describe('Be able to manage old module system', function() {
|
||||
it('should install a module the old school way', function(done) {
|
||||
pm2.install('pm2-server-monit', { v1 : true}, function(err, apps) {
|
||||
var data = JSON.parse(fs.readFileSync(MODULE_CONF_PATH));
|
||||
should.exists(data['module-db']['pm2-server-monit']);
|
||||
fs.statSync(path.join(pm2._conf.PM2_HOME, 'node_modules', 'pm2-server-monit'));
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should be able to uninstall module', function(done) {
|
||||
pm2.uninstall('pm2-server-monit', function(err, apps) {
|
||||
var data = JSON.parse(fs.readFileSync(MODULE_CONF_PATH));
|
||||
should.not.exists(data['module-db']['pm2-server-monit']);
|
||||
try {
|
||||
fs.statSync(path.join(pm2._conf.PM2_HOME, 'node_modules', 'pm2-server-monit'));
|
||||
} catch(e) {
|
||||
if (!e) done(new Error('module must have been deleted...'));
|
||||
}
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Upgrade module to V2 management', function() {
|
||||
it('should install a module the old school way', function(done) {
|
||||
pm2.install('pm2-server-monit', { v1 : true}, function(err, apps) {
|
||||
var data = JSON.parse(fs.readFileSync(MODULE_CONF_PATH));
|
||||
should.exists(data['module-db']['pm2-server-monit']);
|
||||
fs.statSync(path.join(pm2._conf.PM2_HOME, 'node_modules', 'pm2-server-monit'));
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should update and still have module started', function(done) {
|
||||
pm2.update(function() {
|
||||
pm2.list(function(err, procs) {
|
||||
should(procs.length).eql(1);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should reinstall module in new school way', function(done) {
|
||||
pm2.install('pm2-server-monit', function(err, apps) {
|
||||
var data = JSON.parse(fs.readFileSync(MODULE_CONF_PATH));
|
||||
should.exists(data['module-db-v2']['pm2-server-monit']);
|
||||
should.not.exists(data['module-db']['pm2-server-monit']);
|
||||
try {
|
||||
fs.statSync(path.join(pm2._conf.PM2_HOME, 'node_modules', 'pm2-server-monit'));
|
||||
} catch(e) {
|
||||
if (!e)
|
||||
done(new Error('The old module has not been deleted...'));
|
||||
}
|
||||
|
||||
fs.statSync(path.join(MODULE_PATH, 'pm2-server-monit', 'node_modules', 'pm2-server-monit'));
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should update and still have module started', function(done) {
|
||||
pm2.update(function() {
|
||||
pm2.list(function(err, procs) {
|
||||
should(procs.length).eql(1);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
Loading…
x
Reference in New Issue
Block a user