pandora
Version:
A powerful and lightweight application manager for Node.js applications powered by TypeScript.
172 lines (145 loc) • 4.84 kB
text/typescript
;
require('source-map-support').install();
import program = require('commander');
import {PROCESS_ERROR, FINISH_SHUTDOWN, PROCESS_READY, SHUTDOWN, PANDORA_PROCESS} from '../const';
import {ProcessRepresentation} from '../domain';
import assert = require('assert');
import {makeRequire} from 'pandora-dollar';
import {ScalableMaster} from './ScalableMaster';
import {SpawnWrapperUtils} from './SpawnWrapperUtils';
import {consoleLogger} from 'pandora-dollar';
import {CoreSDK} from 'pandora-core-sdk';
import {CoreSDKWithExtendedConfig} from '../util/CoreSDKWithExtendedConfig';
/**
* class ProcessBootstrap
* Bootstrap a worker process, handing all phases of an application stating
*/
export class ProcessBootstrap {
public master: ScalableMaster;
public processRepresentation: ProcessRepresentation;
protected coreSdk: CoreSDK;
constructor(processRepresentation: ProcessRepresentation) {
this.processRepresentation = processRepresentation;
if(this.processRepresentation.scale > 1) {
this.master = new ScalableMaster(processRepresentation);
return;
} else {
const {appName, appDir} = this.processRepresentation;
this.coreSdk = new CoreSDKWithExtendedConfig({
mode: 'worker',
appName, appDir
});
}
}
async start() {
if(this.master) {
await this.startAsMaster();
return;
}
await this.startAsWorker();
}
async stop() {
if(this.master) {
await this.master.stop();
return;
}
try {
await this.coreSdk.stop();
} catch(err) {
// ignore
}
SpawnWrapperUtils.unwrap();
}
async startAsMaster() {
await this.master.start();
}
async startAsWorker() {
SpawnWrapperUtils.setAsFirstLevel();
SpawnWrapperUtils.wrap();
process.env[PANDORA_PROCESS] = JSON.stringify(this.processRepresentation);
await this.coreSdk.start();
// Require the entryFile if there given it, pandora.fork() dep on it
if(this.processRepresentation.entryFile) {
const entryFileBaseDir = this.processRepresentation.entryFileBaseDir;
const ownRequire = entryFileBaseDir ? makeRequire(entryFileBaseDir) : require;
ownRequire(this.processRepresentation.entryFile);
}
}
/**
* A static method to handing the CLI
*/
static cmd() {
program
.allowUnknownOption()
.option('--params [params]')
.parse(process.argv);
let options;
try {
options = JSON.parse(program.params);
assert(options.appName, 'The field appName required by ProcessBootstrap');
assert(options.appDir, 'The field appDir required by ProcessBootstrap');
assert(options.processName, 'The field processName required by ProcessBootstrap');
} catch (err) {
err.message = `Invalid options "${program.params}", ${err.message}`;
consoleLogger.error(err);
if (process.send) {
process.send({action: PROCESS_ERROR, error: err});
}
return;
}
(<any> process).__pandoraOriginArgv = Array.from(process.argv);
Array.prototype.splice.apply(process.argv, [
2, process.argv.length - 2,
...(options.args || [])
]);
if(options.entryFile) {
process.argv[1] = options.entryFile;
}
const processBootstrap = new ProcessBootstrap(options);
process.on('message', (message) => {
if (message.action === SHUTDOWN) {
processBootstrap.stop().then(() => {
if (process.send) {
process.send({action: FINISH_SHUTDOWN});
}
}).catch(consoleLogger.error);
}
});
const onProcessTerm = (sig) => {
consoleLogger.info(`Process [name = ${options.processName}, pid = ${process.pid}] Receive a signal ${sig}, start to stopping, pid ${process.pid}`);
processBootstrap.stop().then(() => {
process.exit(0);
}).catch((err) => {
consoleLogger.error(err);
process.exit(1);
});
};
process.once('SIGQUIT', onProcessTerm.bind(null, 'SIGQUIT'));
process.once('SIGTERM', onProcessTerm.bind(null, 'SIGTERM'));
process.once('SIGINT', onProcessTerm.bind(null, 'SIGINT'));
processBootstrap.start().then(() => {
if (process.send) {
process.send({action: PROCESS_READY});
}
}).catch((err) => {
consoleLogger.error(err);
consoleLogger.error('An error occurred during the start of ProcessBootstrap.');
if (process.send) {
process.send({action: PROCESS_ERROR, error: err});
}
});
}
}
let cmdDid = false;
export function cmd() {
if (cmdDid) {
return;
}
cmdDid = true;
ProcessBootstrap.cmd();
}
// Handing CLI if this module be the main module
if (require.main === module || process.env.RUN_PROCESS_BOOTSTRAP_BY_FORCE) {
delete process.env.RUN_PROCESS_BOOTSTRAP_BY_FORCE;
cmd();
}