UNPKG

pandora

Version:

A powerful and lightweight application manager for Node.js applications powered by TypeScript.

156 lines 6.51 kB
'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); const child_process_1 = require("child_process"); const const_1 = require("../const"); const path_1 = require("path"); const pandora_dollar_1 = require("pandora-dollar"); const pathProcessBootstrap = require.resolve('./ProcessBootstrap'); /** * Class ApplicationHandler */ class ProcessHandler { // TODO: make nodejsStdout is required constructor(processRepresentation) { this.startCount = 0; this.state = const_1.State.pending; this.processRepresentation = processRepresentation; } get appName() { return this.processRepresentation.appName; } get processName() { return this.processRepresentation.processName; } get appDir() { return this.processRepresentation.appDir; } get pid() { return this.forkedProcess && this.forkedProcess.pid; } /** * Start application through fork * @return {Promise<void>} */ async start() { const args = ['--params', JSON.stringify(this.processRepresentation)] .concat(this.processRepresentation.args || []); this.startCount++; await this.doFork(args); } doFork(args) { const representation = this.processRepresentation; const execArgv = process.execArgv.slice(0); // Handing typeScript file,just for testing if (/\.ts$/.test(module.filename) && execArgv.indexOf('ts-node/register') === -1) { execArgv.push('-r', 'ts-node/register', '-r', 'nyc-ts-patch'); } const userExecArgv = representation.execArgv; if (userExecArgv && userExecArgv.length) { execArgv.push.apply(execArgv, userExecArgv); } const env = Object.assign(Object.assign(Object.assign({ [const_1.PANDORA_HOME]: path_1.join(__dirname, '../../') }, process.env), representation.env), { [const_1.PANDORA_CWD]: process.cwd(), // require.main === module maybe be 'false' after patched spawn wrap in mocha ??? not very sure, but keep it for safe RUN_PROCESS_BOOTSTRAP_BY_FORCE: true }); return new Promise((resolve, reject) => { // Fork it const forkedProcess = child_process_1.fork(pathProcessBootstrap, args, { cwd: representation.appDir, execArgv, detached: true, stdio: ['ignore', process.stdout, process.stderr, 'ipc'], env }); forkedProcess.once('message', (message) => { if (message.action === const_1.PROCESS_READY) { const msg = `Process [name = ${this.processRepresentation.processName}, pid = ${forkedProcess.pid}] Started successfully!`; this.state = const_1.State.complete; pandora_dollar_1.consoleLogger.info(msg); resolve(); } else if (message.action === const_1.PROCESS_ERROR) { this.stop().catch((err) => { const msg = `Process [name = ${this.processRepresentation.processName}, pid = ${forkedProcess.pid}] Start error!`; pandora_dollar_1.consoleLogger.error(err); pandora_dollar_1.consoleLogger.error(msg); reject(new Error(msg)); }); } }); // Here just to distinguish normal exits and exceptional exits, exceptional exits need to restart forkedProcess.once('exit', (code, signal) => { const msg = `Process [name = ${this.processRepresentation.processName}, pid = ${forkedProcess.pid}] Exit with code ${code} and signal ${signal}`; pandora_dollar_1.consoleLogger.info(msg); switch (this.state) { case const_1.State.complete: // Restart it automatically when it exceptional exits after it start successful this.start().catch(err => { pandora_dollar_1.consoleLogger.error('Restart application error'); pandora_dollar_1.consoleLogger.error(err); }); break; case const_1.State.pending: default: const err = new Error(`Start failed! Run command [ pandora log ${this.appName} ] to get more information`); reject(err); break; } }); this.forkedProcess = forkedProcess; }); } /** * Stop application through kill * @return {Promise<void>} */ stop() { if (this.state === const_1.State.stopped) { return Promise.resolve(); } this.state = const_1.State.stopped; const forkedProcess = this.forkedProcess; this.forkedProcess = null; return new Promise((resolve) => { const timer = setTimeout(() => { forkedProcess.kill('SIGKILL'); setTimeout(resolve, 2000); }, const_1.SHUTDOWN_TIMEOUT); forkedProcess.once('exit', () => { clearTimeout(timer); resolve(); }); forkedProcess.kill('SIGTERM'); }); } /** * Reload application through process message * @param processName * @return {Promise<void>} */ reload(processName) { if (processName !== this.processName && processName != null) { return; } if (this.processRepresentation.scale === 1) { return this.stop().then(this.start.bind(this)); } return new Promise((resolve, reject) => { this.forkedProcess.once('message', (message) => { if (message.action === const_1.RELOAD_SUCCESS) { resolve(); } if (message.action === const_1.RELOAD_ERROR) { reject(message.error); } }); this.forkedProcess.send({ action: const_1.RELOAD, name: processName, }); setTimeout(() => { reject(new Error('Reload Timeout')); }, const_1.RELOAD_TIMEOUT); }); } } exports.ProcessHandler = ProcessHandler; //# sourceMappingURL=ProcessHandler.js.map