pot-js
Version:
Process management module
408 lines (310 loc) • 12.1 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
var _events = require('events');
var _potLogger = require('pot-logger');
var _chalk = require('chalk');
var _chalk2 = _interopRequireDefault(_chalk);
var _delay = require('delay');
var _delay2 = _interopRequireDefault(_delay);
var _Connection = require('../Connection');
var _Connection2 = _interopRequireDefault(_Connection);
var _WorkerMonitor = require('./WorkerMonitor');
var _WorkerMonitor2 = _interopRequireDefault(_WorkerMonitor);
var _workspace = require('../utils/workspace');
var _workspace2 = _interopRequireDefault(_workspace);
var _watch = require('../utils/watch');
var _watch2 = _interopRequireDefault(_watch);
var _onSignalExit = require('../utils/onSignalExit');
var _onSignalExit2 = _interopRequireDefault(_onSignalExit);
var _createScriptRunner = require('../utils/createScriptRunner');
var _createScriptRunner2 = _interopRequireDefault(_createScriptRunner);
var _EnvVar = require('../utils/EnvVar');
var _getKey = require('../utils/getKey');
var _getKey2 = _interopRequireDefault(_getKey);
var _Errors = require('../utils/Errors');
var _Errors2 = _interopRequireDefault(_Errors);
var _ensureInstanceNumber = require('../utils/ensureInstanceNumber');
var _ensureInstanceNumber2 = _interopRequireDefault(_ensureInstanceNumber);
var _PidHelpers = require('../utils/PidHelpers');
var _SocketsHelpers = require('../utils/SocketsHelpers');
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; }
function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
class MasterMonitor extends _events.EventEmitter {
constructor(options) {
var _this;
_this = super();
this.currentWorkerMonitor = null;
const space = options.workspace,
logsDir = options.logsDir,
execPath = options.execPath,
spawnArgs = options.spawnArgs,
daemon = options.daemon,
env = options.env,
monitorProcessTitle = options.monitorProcessTitle,
cwd = options.baseDir,
production = options.production,
name = options.name,
events = options.events,
watchOptions = options.watch,
respawnOptions = _objectWithoutProperties(options, ['workspace', 'logsDir', 'execPath', 'spawnArgs', 'daemon', 'env', 'monitorProcessTitle', 'baseDir', 'production', 'name', 'events', 'watch']);
(0, _potLogger.setLoggers)(_extends({}, options, {
enable: !daemon || logsDir,
logsDir: logsDir || '.logs'
}));
_workspace2.default.set(space);
process.title = monitorProcessTitle;
this._workerMonitorOptions = _extends({
stdio: 'pipe'
}, respawnOptions, {
execPath,
execArgv: spawnArgs,
data: options,
env: function () {
const res = _extends({}, env);
if (!res.NODE_ENV) {
res.NODE_ENV = production ? 'production' : 'development';
}
res[_EnvVar.ENV_VAR_KEY] = JSON.stringify(options);
return res;
}()
});
const eventsLogger = (0, _potLogger.ensureLogger)('events', 'gray');
const runScript = (0, _createScriptRunner2.default)({ cwd, logger: eventsLogger });
this._runEvent = function (event) {
for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
args[_key - 1] = arguments[_key];
}
const hook = events[event];
if (hook) {
const prefix = [event].concat(args).filter(Boolean).join(' ');
eventsLogger.info(_chalk2.default.gray(`${prefix} - ${hook}`));
runScript(event, ...args);
}
};
this.workerMonitors = [];
const exit = (() => {
var _ref = _asyncToGenerator(function* () {
_potLogger.logger.debug('exit');
try {
const connection = yield _Connection2.default.getByName(name);
if (connection) {
yield connection.requestStopServer();
}
yield Promise.all(_this.workerMonitors.map((() => {
var _ref2 = _asyncToGenerator(function* (monitor) {
return monitor.stop();
});
return function (_x) {
return _ref2.apply(this, arguments);
};
})()));
} catch (err) {
_potLogger.logger.debug(err);
}
process.exit();
});
return function exit() {
return _ref.apply(this, arguments);
};
})();
process.on('uncaughtException', (() => {
var _ref3 = _asyncToGenerator(function* (err) {
_potLogger.logger.fatal(err);
yield exit();
});
return function (_x2) {
return _ref3.apply(this, arguments);
};
})());
(0, _onSignalExit2.default)(_asyncToGenerator(function* () {
(0, _potLogger.setLoggers)('logLevel', 'OFF');
yield exit();
}));
(0, _watch2.default)(_extends({ cwd }, watchOptions), _asyncToGenerator(function* () {
_potLogger.logger.trace('watch:restart');
process.emit('watch:restart');
const length = _this.workerMonitors.length;
const reloadDelay = length > 1 ? 2000 / length : 0;
for (const workerMonitor of _this.workerMonitors) {
yield workerMonitor.restart();
yield (0, _delay2.default)(reloadDelay);
}
}));
}
// will be set by server socket
spawn() {
var _arguments = arguments,
_this2 = this;
return _asyncToGenerator(function* () {
let options = _arguments.length > 0 && _arguments[0] !== undefined ? _arguments[0] : {};
const newInstances = (0, _ensureInstanceNumber2.default)(options.instances);
const EventTypes = _WorkerMonitor2.default.EventTypes;
const runEvent = _this2._runEvent;
const workerMonitors = new Array(newInstances).fill().map(function () {
return new _WorkerMonitor2.default(_this2._workerMonitorOptions);
});
const errors = new _Errors2.default();
const bootstraps = workerMonitors.map(function (workerMonitor) {
let displayName = workerMonitor.data.name;
workerMonitor.on(EventTypes.STOP, function () {
_potLogger.logger.warn(`"${displayName}" stopped`);
runEvent(EventTypes.STOP);
});
workerMonitor.on(EventTypes.CRASH, function () {
_potLogger.logger.fatal(`"${displayName}" crashed`);
runEvent(EventTypes.CRASH);
});
workerMonitor.on(EventTypes.SLEEP, function () {
_potLogger.logger.warn(`"${displayName}" sleeped`);
runEvent(EventTypes.SLEEP);
});
workerMonitor.on(EventTypes.SPAWN, function () {
runEvent(EventTypes.SPAWN);
});
workerMonitor.on(EventTypes.EXIT, (() => {
var _ref6 = _asyncToGenerator(function* (code, signal) {
_potLogger.logger.debug(`"${displayName}" exit with code "${code}", signal "${signal}"`);
runEvent(EventTypes.EXIT, code, signal);
});
return function (_x4, _x5) {
return _ref6.apply(this, arguments);
};
})());
workerMonitor.on(EventTypes.STDOUT, function (data) {
runEvent(EventTypes.STDOUT);
_potLogger.logger.info(data.toString().trim());
});
workerMonitor.on(EventTypes.STDERR, function (data) {
runEvent(EventTypes.STDERR);
_potLogger.logger.error(data.toString().trim());
});
workerMonitor.on(EventTypes.WARN, function (data) {
runEvent(EventTypes.WARN);
_potLogger.logger.warn(data.toString().trim());
});
workerMonitor.on(EventTypes.RESTART, _asyncToGenerator(function* () {
yield (0, _PidHelpers.writePid)(workerMonitor.data);
_potLogger.logger.info(`"${displayName}" restarted`);
runEvent(EventTypes.RESTART);
}));
return new Promise(function (resolve) {
workerMonitor.on(EventTypes.START, _asyncToGenerator(function* () {
try {
const workerMonitors = _this2.workerMonitors;
const numbers = workerMonitors.length ? workerMonitors.map(function (wm) {
return wm.id;
}) : [0];
workerMonitor.id = Math.max(...numbers) + 1;
workerMonitors.push(workerMonitor);
const options = workerMonitor.data,
id = workerMonitor.id;
_workspace2.default.set(options);
const key = (0, _getKey2.default)(workerMonitor);
const pidFile = yield (0, _PidHelpers.getPidFile)(key);
const socketPath = yield (0, _SocketsHelpers.getSocketPath)(key);
options.instanceId = id;
options.key = key;
options.pidFile = pidFile;
options.socketPath = socketPath;
options.displayName = options.name + (id ? ` #${id}` : '');
yield (0, _SocketsHelpers.startServer)(_this2, workerMonitor);
yield (0, _PidHelpers.writePid)(options);
workerMonitors.sort(function (a, b) {
return a.id - b.id;
});
displayName = workerMonitor.data.displayName;
_potLogger.logger.info(`"${displayName}" started`);
runEvent(EventTypes.START);
} catch (err) {
_potLogger.logger.debug(err);
errors.push(err);
}
resolve(workerMonitor.toJSON());
}));
workerMonitor.start();
});
});
const added = yield Promise.all(bootstraps);
const ok = bootstraps.length > errors.length;
return {
ok,
errors: errors.toJSON(),
added
};
})();
}
scale(number) {
var _this3 = this;
return _asyncToGenerator(function* () {
const delta = (0, _ensureInstanceNumber2.default)(number) - _this3.workerMonitors.length;
if (!delta) {
return { ok: true, errors: [] };
} else if (delta > 0) {
return _this3.spawn({ instances: delta });
} else {
const workerMonitors = _this3.workerMonitors;
const toRemove = workerMonitors.slice(workerMonitors.length + delta);
const errors = new _Errors2.default();
const removed = yield Promise.all(toRemove.map((() => {
var _ref9 = _asyncToGenerator(function* (workerMonitor) {
const state = workerMonitor.toJSON();
yield _this3.requestShutDown(workerMonitor).catch(function (err) {
return errors.push(err);
});
return state;
});
return function (_x6) {
return _ref9.apply(this, arguments);
};
})()));
return {
ok: !errors.length,
errors: errors.toJSON(),
removed
};
}
})();
}
state(newState) {
var _this4 = this;
return _asyncToGenerator(function* () {
const currentWorkerMonitor = _this4.currentWorkerMonitor;
if (currentWorkerMonitor) {
if (newState) {
Object.assign(currentWorkerMonitor.data, newState);
}
return currentWorkerMonitor.toJSON();
}
})();
}
restart() {
var _this5 = this;
return _asyncToGenerator(function* () {
const currentWorkerMonitor = _this5.currentWorkerMonitor;
if (currentWorkerMonitor) {
yield currentWorkerMonitor.restart();
return true;
}
return false;
})();
}
requestShutDown(workerMonitor) {
var _this6 = this;
return _asyncToGenerator(function* () {
yield workerMonitor.stop();
var _workerMonitor$toJSON = workerMonitor.toJSON();
const socketPath = _workerMonitor$toJSON.socketPath,
pidFile = _workerMonitor$toJSON.pidFile;
const workerMonitors = _this6.workerMonitors;
const index = workerMonitors.indexOf(workerMonitor);
workerMonitors.splice(index, 1);
yield Promise.all([(0, _SocketsHelpers.removeDomainSocketFile)(socketPath), (0, _PidHelpers.removePidFile)(pidFile)]);
if (!workerMonitors.length) process.exit(0);
})();
}
}
exports.default = MasterMonitor;