zcatalyst-cli
Version:
Command Line Tool for CATALYST
293 lines (292 loc) • 14.3 kB
JavaScript
;
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.FnHandler = void 0;
const fs_1 = require("../../../util_modules/fs");
const constants_1 = require("../../../util_modules/constants");
const http_1 = require("http");
const logger_1 = require("../../../util_modules/logger");
const server_1 = require("../../../util_modules/server");
const project_1 = require("../../../util_modules/project");
const error_1 = __importDefault(require("../../../error"));
const path_1 = require("path");
const utils_1 = require("@zcatalyst/container-plugin/out/utils");
class FnHandler {
constructor(fn, localFnEvents) {
this.closed = false;
this.fn = fn;
this.stack = (() => {
var _a, _b, _c;
if ((_a = fn === null || fn === void 0 ? void 0 : fn.target.stack) === null || _a === void 0 ? void 0 : _a.startsWith(constants_1.RUNTIME.language.node.value)) {
return 'node';
}
if ((_b = fn === null || fn === void 0 ? void 0 : fn.target.stack) === null || _b === void 0 ? void 0 : _b.startsWith(constants_1.RUNTIME.language.java.value)) {
return 'java';
}
if ((_c = fn === null || fn === void 0 ? void 0 : fn.target.stack) === null || _c === void 0 ? void 0 : _c.startsWith(constants_1.RUNTIME.language.python.value)) {
return 'python';
}
throw new error_1.default('Invalid stack for execute command: ' + fn.target.stack, {
exit: 2
});
})();
this.localFnEvents = localFnEvents;
const projectRoot = (0, project_1.getProjectRoot)();
this.responseFile = (0, path_1.join)(projectRoot, constants_1.FOLDERNAME.build, '.catalyst', 'user_res_body');
this.metaFile = (0, path_1.join)(projectRoot, constants_1.FOLDERNAME.build, '.catalyst', 'user_meta.json');
}
writeResponse(response, status) {
fs_1.SYNC.writeFile(this.responseFile, response);
fs_1.SYNC.writeFile(this.metaFile, JSON.stringify({ response: { statusCode: status } }));
}
responseProcessor(statusCode, message) {
switch (statusCode) {
case 200: {
this.writeResponse('SUCCESS', 200);
break;
}
case 532: {
this.writeResponse('CODE_EXCEPTION', 532);
break;
}
case 530: {
this.writeResponse('FAILURE', 530);
break;
}
case 500: {
this.writeResponse('INTERNAL_SERVER_ERROR', 500);
break;
}
default: {
throw new error_1.default('INVALID STATUS CODE', {
context: {
statusCode: statusCode,
statusMessage: message
}
});
}
}
}
processFlowHandler(data, listenPort) {
return __awaiter(this, void 0, void 0, function* () {
return new Promise((resolve, reject) => {
const jobDetailsServer = new http_1.Server((serverReq, serverRes) => __awaiter(this, void 0, void 0, function* () {
var _a, _b, _c;
(0, logger_1.debug)(`[JOB_SERVER] [${serverReq.method}] ${serverReq.url}`);
if ((_a = serverReq.url) === null || _a === void 0 ? void 0 : _a.includes('/ruok')) {
serverRes.writeHead(200);
serverRes.write('Iam Ok!');
serverRes.end();
}
else if ((_b = serverReq.url) === null || _b === void 0 ? void 0 : _b.includes('data')) {
serverRes.writeHead(200);
serverRes.write(JSON.stringify({
data
}));
serverRes.end();
}
else if ((_c = serverReq.url) === null || _c === void 0 ? void 0 : _c.includes('callback')) {
const reqData = yield new Promise((_resolve) => {
const data = [];
serverReq.on('data', (chunk) => data.push(chunk));
serverReq.on('end', () => _resolve(JSON.parse(Buffer.concat(data).toString())));
});
this.responseProcessor(reqData.job_status);
serverRes.writeHead(200);
serverRes.write(JSON.stringify({}));
serverRes.end();
}
else {
(0, logger_1.debug)(`Invalid request: (${serverReq.method}) ${serverReq.url}`);
this.writeResponse('INTERNAL_SERVER_ERROR', 500);
reject(`Invalid request: (${serverReq.method}) ${serverReq.url}`);
}
}));
const jobConnDestroyer = new server_1.ConnectionDestroyer(jobDetailsServer);
jobDetailsServer.listen(listenPort, '127.0.0.1', () => {
(0, logger_1.debug)('job server listening on port :' + listenPort);
resolve({
kill: () => {
jobConnDestroyer.destroy();
}
});
});
jobDetailsServer.on('error', reject);
});
});
}
fnRequestHandler(_a) {
return __awaiter(this, arguments, void 0, function* ({ httpPort, data = {}, accessToken }) {
data.project_details = Object.assign({ id: (0, project_1.getProjectId)(), project_name: (0, project_1.getProjectName)() }, (data.project_details || {}));
const reqJson = JSON.stringify(data);
return new Promise((resolve, reject) => {
const req = (0, http_1.request)(`http://127.0.0.1:${httpPort}`, {
headers: {
'x-zc-projectid': (0, project_1.getProjectId)(),
'x-zc-project-domain': (0, project_1.getDomainPrefix)() + '.' + constants_1.ORIGIN.app.replace('https://', ''),
'x-zc-project-key': (0, project_1.getDomainKey)(),
'x-zc-environment': (0, project_1.getEnvName)(),
'x-zc-user-cred-type': 'token',
'x-zc-user-cred-token': accessToken,
'x-zc-admin-cred-type': 'token',
'x-zc-admin-cred-token': accessToken,
'x-zc-user-type': 'admin',
'x-zc-instance-id': 'local',
'Content-Length': reqJson.length
}
}, (resp) => {
var _a;
if ([constants_1.REMOTE_REF.functions.type.cron, constants_1.REMOTE_REF.functions.type.event].includes(((_a = this.fn.target) === null || _a === void 0 ? void 0 : _a.type) + '')) {
try {
this.responseProcessor(resp.statusCode || -1, resp.statusMessage);
return resolve();
}
catch (er) {
reject(er);
}
}
resp.on('data', (data) => {
const respStr = data.toString();
fs_1.SYNC.writeFile(this.responseFile, respStr);
});
resp.on('close', () => {
const metaJson = {
response: { statusCode: resp.statusCode }
};
fs_1.SYNC.writeFile(this.metaFile, JSON.stringify(metaJson));
resolve();
});
}).on('error', (err) => {
if ('code' in err && err.code === 'ECONNRESET') {
(0, logger_1.debug)('ECONNRESET - when sending request to container function');
return;
}
reject(new error_1.default('Error when trying to send the request to container function', { original: err, exit: 2, skipHelp: true }));
});
req.write(reqJson);
req.end();
});
});
}
fnResponseHandler(code, sig) {
return __awaiter(this, void 0, void 0, function* () {
var _a, _b, _c, _d, _e;
if (code === 0) {
(0, logger_1.info)();
(0, logger_1.info)(`[CLI] Function ${(_a = this.fn.target) === null || _a === void 0 ? void 0 : _a.name} execution complete`);
(0, logger_1.info)();
const response = yield fs_1.ASYNC.readFile(this.responseFile);
let meta;
try {
meta = JSON.parse((yield fs_1.ASYNC.readFile(this.metaFile))).response;
}
catch (err) {
meta = {};
}
switch ((_b = this.fn.target) === null || _b === void 0 ? void 0 : _b.type) {
case constants_1.FN_TYPE.basic:
(0, logger_1.info)(`[response - ${this.fn.target.name}] ${response + ''}`);
(0, logger_1.info)(`[status - ${this.fn.target.name}] ${(meta.statusCode || 200) + ''}`);
this.localFnEvents.emit('response', {
data: response,
status: meta.statsCode
});
break;
case constants_1.FN_TYPE.cron:
case constants_1.FN_TYPE.job:
case constants_1.FN_TYPE.event:
(0, logger_1.info)(`[status - ${this.fn.target.name}] ${response || 'Unknown'}`);
if (!response) {
(0, logger_1.info)();
(0, logger_1.info)(`[CLI] Make sure to close the ${this.fn.target.name} (${this.fn.target.type}) function.`);
(0, logger_1.info)();
}
this.localFnEvents.emit('response', {
status: meta.statusCode
});
break;
case constants_1.FN_TYPE.integration:
(0, logger_1.info)(`[response - ${this.fn.target.name}] ${response + ''}`);
try {
this.localFnEvents.emit('response', JSON.parse(response || ''));
}
catch (er) {
(0, logger_1.debug)('Invalid integration response: ', er);
}
break;
}
}
else {
if (code === 130 || sig === 'SIGINT') {
(0, logger_1.info)();
(0, logger_1.info)(`[CLI] Function ${(_c = this.fn.target) === null || _c === void 0 ? void 0 : _c.name} interrupted`);
(0, logger_1.info)();
}
if (code === 143 || sig === 'SIGTERM' || sig === 'SIGQUIT' || sig === 'SIGKILL') {
(0, logger_1.info)();
(0, logger_1.info)(`[CLI] Function ${(_d = this.fn.target) === null || _d === void 0 ? void 0 : _d.name} process killed`);
(0, logger_1.info)();
}
this.localFnEvents.listenerCount('error') > 0 &&
this.localFnEvents.emit('error', new error_1.default(`Function(${(_e = this.fn.target) === null || _e === void 0 ? void 0 : _e.name}) process exited with ${code ? 'status: ' + code : 'signal: ' + sig}`, {
skipHelp: true
}));
}
});
}
shutdown() {
return __awaiter(this, void 0, void 0, function* () {
return new Promise((res) => {
this.closed = true;
this.kill();
if (this.watcher) {
this.watcher.close().then(res).catch(res);
}
else {
res();
}
});
});
}
kill() {
var _a;
if (!this.slave || ((_a = this.slave) === null || _a === void 0 ? void 0 : _a.killed)) {
return;
}
(0, logger_1.info)();
(0, logger_1.info)('Terminating Function');
if (this.slave instanceof utils_1.ContainerEvents) {
this.slave.emit('close', this.fn);
return;
}
let isChildExited = false;
this.slave.kill('SIGTERM');
this.slave.once('exit', () => {
isChildExited = true;
});
setTimeout(() => {
if (!this.slave || this.slave instanceof utils_1.ContainerEvents) {
return;
}
if (!isChildExited) {
(0, logger_1.debug)(`Emitting SIGKILL to PID: ${this.slave.pid}`);
this.slave.kill('SIGKILL');
}
else {
(0, logger_1.debug)(`Fn child process kill confirmed PID: ${this.slave.pid}`);
}
}, 500);
}
}
exports.FnHandler = FnHandler;