123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362 |
- var Configuration = require('../../Configuration.js');
- var cst = require('../../../constants.js');
- var Common = require('../../Common');
- var forEachLimit = require('async/forEachLimit');
- const sexec = require('../../tools/sexec.js');
- const deleteFolderRecursive = require('../../tools/deleteFolderRecursive.js');
- var path = require('path');
- var fs = require('fs');
- var os = require('os');
- var spawn = require('child_process').spawn;
- var exec = require('child_process').exec;
- var execSync = require('child_process').execSync;
- module.exports = {
- install,
- uninstall,
- start,
- publish,
- package
- }
- /**
- * Module management to manage tarball packages
- *
- * pm2 install http.tar.gz
- * pm2 uninstall http
- *
- * - the first and only folder in the tarball must be called module (tar zcvf http module/)
- * - a package.json must be present with attribute "name", "version" and "pm2" to declare apps to run
- */
- function install(PM2, module_filepath, opts, cb) {
- // Remote file retrieval
- if (module_filepath.includes('http') === true) {
- var target_file = module_filepath.split('/').pop()
- var target_filepath = path.join(os.tmpdir(), target_file)
- opts.install_url = module_filepath
- return retrieveRemote(module_filepath, target_filepath, (err) => {
- if (err) {
- Common.errMod(err)
- process.exit(1)
- }
- installLocal(PM2, target_filepath, opts, cb)
- })
- }
- // Local install
- installLocal(PM2, module_filepath, opts, cb)
- }
- function retrieveRemote(url, dest, cb) {
- Common.logMod(`Retrieving remote package ${url}...`)
- var wget = spawn('wget', [url, '-O', dest, '-q'], {
- stdio : 'inherit',
- env: process.env,
- windowsHide: true,
- shell : true
- })
- wget.on('error', (err) => {
- console.error(err.stack || err)
- })
- wget.on('close', (code) => {
- if (code !== 0)
- return cb(new Error('Could not download'))
- return cb(null)
- })
- }
- function installLocal(PM2, module_filepath, opts, cb) {
- Common.logMod(`Installing package ${module_filepath}`)
- // Get module name by unpacking the module/package.json only and read the name attribute
- getModuleName(module_filepath, function(err, module_name) {
- if (err) return cb(err)
- Common.logMod(`Module name is ${module_name}`)
- Common.logMod(`Depackaging module...`)
- var install_path = path.join(cst.DEFAULT_MODULE_PATH, module_name);
- require('mkdirp').sync(install_path)
- var install_instance = spawn('tar', ['zxf', module_filepath, '-C', install_path, '--strip-components 1'], {
- stdio : 'inherit',
- env: process.env,
- shell : true
- })
- install_instance.on('close', function(code) {
- Common.logMod(`Module depackaged in ${install_path}`)
- if (code == 0)
- return runInstall(PM2, install_path, module_name, opts, cb)
- return PM2.exitCli(1)
- });
- install_instance.on('error', function (err) {
- console.error(err.stack || err);
- });
- })
- }
- function deleteModulePath(module_name) {
- var sanitized = module_name.replace(/\./g, '')
- deleteFolderRecursive(path.join(cst.DEFAULT_MODULE_PATH, module_name));
- }
- function runInstall(PM2, target_path, module_name, opts, cb) {
- var config_file = path.join(target_path, 'package.json')
- var conf
- try {
- conf = require(config_file)
- module_name = conf.name
- } catch(e) {
- Common.errMod(new Error('Cannot find package.json file with name attribute at least'));
- }
- // Force with the name in the package.json
- opts.started_as_module = true
- opts.cwd = target_path
- if (needPrefix(conf))
- opts.name_prefix = module_name
- if (opts.install) {
- Common.logMod(`Running YARN install...`)
- sexec(`cd ${target_path} ; yarn install`, {silent: false}, function(code) {
- // Start apps under "apps" or "pm2" attribute
- Common.logMod(`Starting ${target_path}`)
- PM2.start(conf, opts, function(err, data) {
- if (err) return cb(err)
- Configuration.setSync(`${cst.MODULE_CONF_PREFIX_TAR}:${module_name}`, {
- source: 'tarball',
- install_url: opts.install_url,
- installed_at: Date.now()
- })
- Common.logMod(`Module INSTALLED and STARTED`)
- return cb(null, 'Module installed & Started')
- })
- })
- }
- else {
- PM2.start(conf, opts, function(err, data) {
- if (err) return cb(err)
- Configuration.setSync(`${cst.MODULE_CONF_PREFIX_TAR}:${module_name}`, {
- source: 'tarball',
- install_url: opts.install_url,
- installed_at: Date.now()
- })
- Common.logMod(`Module INSTALLED and STARTED`)
- return cb(null, 'Module installed & Started')
- })
- }
- }
- function start(PM2, module_name, cb) {
- var module_path = path.join(cst.DEFAULT_MODULE_PATH, module_name);
- Common.printOut(cst.PREFIX_MSG_MOD + 'Starting TAR module ' + module_name);
- var package_json_path = path.join(module_path, 'package.json');
- var module_conf = Configuration.getSync(`${cst.MODULE_CONF_PREFIX_TAR}:${module_name}`)
- try {
- var conf = require(package_json_path)
- } catch(e) {
- Common.printError(`Could not find package.json as ${package_json_path}`)
- return cb()
- }
- var opts = {};
- opts.started_as_module = true
- opts.cwd = module_path
- if (module_conf.install_url)
- opts.install_url = module_conf.install_url
- if (needPrefix(conf))
- opts.name_prefix = module_name
- PM2.start(conf, opts, function(err, data) {
- if (err) {
- Common.printError(`Could not start ${module_name} ${module_path}`)
- return cb()
- }
- Common.printOut(`${cst.PREFIX_MSG_MOD} Module ${module_name} STARTED`)
- return cb();
- })
- }
- /**
- * Retrieve from module package.json the name of each application
- * delete process and delete folder
- */
- function uninstall(PM2, module_name, cb) {
- var module_path = path.join(cst.DEFAULT_MODULE_PATH, module_name);
- Common.logMod(`Removing ${module_name} from auto startup`)
- try {
- var pkg = require(path.join(module_path, 'package.json'))
- } catch(e) {
- Common.errMod('Could not retrieve module package.json');
- return cb(e)
- }
- var apps = pkg.apps || pkg.pm2
- apps = [].concat(apps);
- /**
- * Some time a module can have multiple processes
- */
- forEachLimit(apps, 1, (app, next) => {
- var app_name
- if (!app.name) {
- Common.renderApplicationName(app)
- }
- if (apps.length > 1)
- app_name = `${module_name}:${app.name}`
- else if (apps.length == 1 && pkg.name != apps[0].name)
- app_name = `${module_name}:${app.name}`
- else
- app_name = app.name
- PM2._operate('deleteProcessId', app_name, () => {
- deleteModulePath(module_name)
- next()
- })
- }, () => {
- Configuration.unsetSync(`${cst.MODULE_CONF_PREFIX_TAR}:${module_name}`)
- cb(null)
- })
- }
- /**
- * Uncompress only module/package.json and retrieve the "name" attribute in the package.json
- */
- function getModuleName(module_filepath, cb) {
- var tmp_folder = path.join(os.tmpdir(), cst.MODULE_BASEFOLDER)
- var install_instance = spawn('tar', ['zxf', module_filepath, '-C', os.tmpdir(), `${cst.MODULE_BASEFOLDER}/package.json`], {
- stdio : 'inherit',
- env: process.env,
- shell : true
- })
- install_instance.on('close', function(code) {
- try {
- var pkg = JSON.parse(fs.readFileSync(path.join(tmp_folder, `package.json`)))
- return cb(null, pkg.name)
- } catch(e) {
- return cb(e)
- }
- });
- }
- function package(module_path, target_path, cb) {
- var base_folder = path.dirname(module_path)
- var module_folder_name = path.basename(module_path)
- var pkg = require(path.join(module_path, 'package.json'))
- var pkg_name = `${module_folder_name}-v${pkg.version.replace(/\./g, '-')}.tar.gz`
- var target_fullpath = path.join(target_path, pkg_name)
- var cmd = `tar zcf ${target_fullpath} -C ${base_folder} --transform 's,${module_folder_name},module,' ${module_folder_name}`
- Common.logMod(`Gziping ${module_path} to ${target_fullpath}`)
- var tar = exec(cmd, (err, sto, ste) => {
- if (err) {
- console.log(sto.toString().trim())
- console.log(ste.toString().trim())
- }
- })
- tar.on('close', function (code) {
- cb(code == 0 ? null : code, {
- package_name: pkg_name,
- path: target_fullpath
- })
- })
- }
- function publish(PM2, folder, cb) {
- var target_folder = folder ? path.resolve(folder) : process.cwd()
- try {
- var pkg = JSON.parse(fs.readFileSync(path.join(target_folder, 'package.json')).toString())
- } catch(e) {
- Common.errMod(`${process.cwd()} module does not contain any package.json`)
- process.exit(1)
- }
- if (!pkg.name) throw new Error('Attribute name should be present')
- if (!pkg.version) throw new Error('Attribute version should be present')
- if (!pkg.pm2 && !pkg.apps) throw new Error('Attribute apps should be present')
- var current_path = target_folder
- var module_name = path.basename(current_path)
- var target_path = os.tmpdir()
- Common.logMod(`Starting publishing procedure for ${module_name}@${pkg.version}`)
- package(current_path, target_path, (err, res) => {
- if (err) {
- Common.errMod('Can\'t package, exiting')
- process.exit(1)
- }
- Common.logMod(`Package [${pkg.name}] created in path ${res.path}`)
- var data = {
- module_data: {
- file: res.path,
- content_type: 'content/gzip'
- },
- id: pkg.name,
- name: pkg.name,
- version: pkg.version
- };
- var uri = `${PM2.pm2_configuration.registry}/api/v1/modules`
- Common.logMod(`Sending Package to remote ${pkg.name} ${uri}`)
- require('needle')
- .post(uri, data, { multipart: true }, function(err, res, body) {
- if (err) {
- Common.errMod(err)
- process.exit(1)
- }
- if (res.statusCode !== 200) {
- Common.errMod(`${pkg.name}-${pkg.version}: ${res.body.msg}`)
- process.exit(1)
- }
- Common.logMod(`Module ${module_name} published under version ${pkg.version}`)
- process.exit(0)
- })
- })
- }
- function needPrefix(conf) {
- if ((conf.apps && conf.apps.length > 1) ||
- (conf.pm2 && conf.pm2.length > 1) ||
- (conf.apps.length == 1 && conf.name != conf.apps[0].name))
- return true
- return false
- }
|