UNPKG

container.ts

Version:
125 lines 5.58 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); /// <reference types="node" /> const assert = require("assert"); const path = require("path"); const childProcess = require("child_process"); const Observable_1 = require("rxjs/Observable"); require("rxjs/add/observable/of"); require("rxjs/add/observable/fromEvent"); require("rxjs/add/operator/switchMap"); require("rxjs/add/operator/take"); require("rxjs/add/operator/takeUntil"); const container_1 = require("../../container"); const ChildProcess_1 = require("../process/ChildProcess"); // TODO: Validation library. exports.ENV_SCRIPTS_PATH = "SCRIPTS_PATH"; exports.ENV_SCRIPTS_NAME = "SCRIPTS_NAME"; /** Spawned script process interface. */ class ScriptProcess { constructor(_scripts, _target, _id, _process, _options = {}) { this._scripts = _scripts; this._target = _target; this._id = _id; this._process = _process; this._options = _options; this._identifier = 0; this.scripts.debug(`fork '${_target}.${_id}'`); // Accumulate multiple callback arguments into array. const accumulator = (...args) => args; // Listen for process exit, reduce code/signal for next argument. this._exit = Observable_1.Observable.fromEvent(_process, "exit", accumulator) .take(1) .switchMap((args) => { const [code, signal] = args; const value = (typeof code === "number") ? code : signal; return Observable_1.Observable.of(value || 1); }); this._exit.subscribe((code) => this.scripts.debug(`exit '${_target}.${_id}' '${code}'`)); // Listen for process error, forward to scripts logger. Observable_1.Observable.fromEvent(_process, "error") .takeUntil(this._exit) .subscribe((error) => this.scripts.log.error(error)); // Listen for and handle process messages. this._message = Observable_1.Observable.fromEvent(_process, "message") .takeUntil(this._exit); this._message .subscribe((message) => this.handleMessage(message)); } get scripts() { return this._scripts; } get target() { return this._target; } get id() { return this._id; } get process() { return this._process; } get options() { return this._options; } get exit() { return this._exit; } get message() { return this._message; } /** Incrementing counter for unique identifiers. */ get identifier() { return ++this._identifier; } /** Send message to child process. */ send(type, data) { this.process.send({ type, data }); } /** Make call to module.method in child process. */ call(target, method, options = {}) { const timeout = options.timeout || ChildProcess_1.ChildProcess.DEFAULT_TIMEOUT; const args = options.args || []; const id = this.identifier; this.scripts.debug(`call '${this.target}.${this.id}.${target}.${method}' '${id}'`); // Send call request to child process. const sendData = { id, target, method, args }; this.send(ChildProcess_1.EProcessMessageType.CallRequest, sendData); return ChildProcess_1.ChildProcess.handleCallResponse(this.message, id, args, timeout); } /** Handle messages received from child process. */ handleMessage(message) { switch (message.type) { // Send received log and metric messages to container. case ChildProcess_1.EProcessMessageType.Log: { const data = message.data; this.scripts.container.sendLog(data.level, data.message, data.metadata, data.args); break; } case ChildProcess_1.EProcessMessageType.Metric: { const data = message.data; this.scripts.container.sendMetric(data.type, data.name, data.value, data.tags); break; } // Call request received from child. case ChildProcess_1.EProcessMessageType.CallRequest: { ChildProcess_1.ChildProcess.handleCallRequest(this, this.scripts.container, message.data); break; } } } } exports.ScriptProcess = ScriptProcess; /** Node.js scripts interface. */ class Scripts extends container_1.ContainerModule { get path() { return this._path; } constructor(name, opts) { super(name, opts); // Get scripts directory path from environment. const scriptsPath = this.environment.get(exports.ENV_SCRIPTS_PATH); assert(scriptsPath != null, "Scripts path is undefined"); this._path = path.resolve(scriptsPath); this.debug(`path '${this.path}'`); } /** Spawn new Node.js process using script file. */ fork(target, options = {}) { const filePath = path.resolve(this.path, target); const forkArgs = options.args || []; const forkEnv = this.environment.copy(); const identifier = this.identifier; // Use container environment when spawning processes. // Override name value to prepend application namespace. const name = `${this.namespace}.${target}.${identifier}`; forkEnv.set(exports.ENV_SCRIPTS_NAME, name); const forkOptions = { env: forkEnv.variables, }; const process = childProcess.fork(filePath, forkArgs, forkOptions); return new ScriptProcess(this, target, identifier, process, options); } } exports.Scripts = Scripts; //# sourceMappingURL=Scripts.js.map