zcatalyst-cli
Version:
Command Line Tool for CATALYST
484 lines (483 loc) • 30.7 kB
JavaScript
'use strict';
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
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 });
const path_1 = require("path");
const http_1 = __importStar(require("http"));
const error_1 = __importDefault(require("../../error"));
const fn_utils_1 = require("../../fn-utils");
const credential_1 = __importDefault(require("../../internal/credential"));
const port_resolver_1 = __importDefault(require("../../port-resolver"));
const userConfig_1 = __importDefault(require("../../userConfig"));
const runtime_store_1 = __importDefault(require("../../runtime-store"));
const constants_1 = require("../../util_modules/constants");
const runtime_1 = __importDefault(require("../../util_modules/constants/lib/runtime"));
const fs_1 = require("../../util_modules/fs");
const js_1 = require("../../util_modules/js");
const logger_1 = require("../../util_modules/logger");
const project_1 = require("../../util_modules/project");
const shell_1 = require("../../util_modules/shell");
const http_functions_1 = require("../dependencies/http-functions");
const events_1 = __importDefault(require("events"));
const ensure_java_userconfig_1 = require("../../fn-utils/lib/ensure-java-userconfig");
const server_1 = require("../../util_modules/server");
class LocalFunction {
constructor(repl, target) {
var _a, _b, _c;
this.localFnEvents = new events_1.default();
this.killed = false;
this._call = (shell = true) => (rawData = {}) => {
let data;
try {
if (shell) {
if (typeof rawData === 'string') {
const content = fs_1.SYNC.readJSONFile((0, path_1.resolve)(runtime_store_1.default.get('cwd'), rawData));
if (content === undefined) {
return new error_1.default('content is undefined for provided file path');
}
rawData = content;
}
switch (this.target.type) {
case constants_1.FN_TYPE.cron: {
data = { data: rawData };
break;
}
case constants_1.FN_TYPE.event:
if (!('event_bus_details' in rawData) && !('events' in rawData)) {
(0, logger_1.info)('For event function kindly use event:generate command to generate the input json.');
(0, logger_1.info)('You can also provide event details as per documentation.');
return new error_1.default('Event bus details missing');
}
data = rawData;
break;
default: {
data = rawData;
}
}
}
else {
data = rawData;
}
}
catch (e) {
(0, logger_1.info)('the input must be in the form of json or a path to a file containing json data.');
return new error_1.default('Invalid input');
}
return (() => __awaiter(this, void 0, void 0, function* () {
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q;
const projectRoot = runtime_store_1.default.get('project.root');
const accessToken = yield credential_1.default.getAccessToken();
const slaveOptions = [];
const debugPort = runtime_store_1.default.get('context.port.debug.' + constants_1.FN_TYPE.basic, null);
yield fs_1.ASYNC.ensureFile(this.responseFile, true);
yield fs_1.ASYNC.ensureFile(this.metaFile, true);
if (this.target.type === constants_1.FN_TYPE.job) {
const jobData = data;
const jobMetaDetails = {
headers: Object.assign(Object.assign({}, (_b = (_a = jobData === null || jobData === void 0 ? void 0 : jobData.job_details) === null || _a === void 0 ? void 0 : _a.job_meta_details) === null || _b === void 0 ? void 0 : _b.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'
}),
params: ((_d = (_c = jobData === null || jobData === void 0 ? void 0 : jobData.job_details) === null || _c === void 0 ? void 0 : _c.job_meta_details) === null || _d === void 0 ? void 0 : _d.params) || {},
jobpool_details: ((_f = (_e = jobData === null || jobData === void 0 ? void 0 : jobData.job_details) === null || _e === void 0 ? void 0 : _e.job_meta_details) === null || _f === void 0 ? void 0 : _f.jobpool_details) || {
type: 'Function',
project_details: {
project_name: (0, project_1.getProjectName)(),
id: (0, project_1.getProjectId)()
}
}
};
jobData.job_details = Object.assign(Object.assign({}, jobData.job_details), { job_meta_details: jobMetaDetails });
jobData.capacity = jobData.capacity || {
memory: '256'
};
data = jobData;
}
const slaveFnTarget = { index: this.target.index, name: this.target.name };
if ((_g = this.target.stack) === null || _g === void 0 ? void 0 : _g.startsWith(runtime_1.default.language.node.value)) {
if (debugPort !== null) {
slaveOptions.push('--inspect-brk=' + debugPort);
}
slaveOptions.push(this.nodeInvoker);
slaveOptions.push(JSON.stringify(slaveFnTarget));
slaveOptions.push(JSON.stringify(data));
slaveOptions.push(JSON.stringify({
'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)()
}));
slaveOptions.push(JSON.stringify({
'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'
}));
slaveOptions.push(JSON.stringify((0, path_1.join)(projectRoot, constants_1.FOLDERNAME.build)));
this.slave = (0, shell_1.spawn)('node', slaveOptions, {
cwd: (0, path_1.join)(projectRoot, constants_1.FOLDERNAME.build, constants_1.FOLDERNAME.functions, this.target.name),
stdio: 'pipe',
env: Object.assign({ X_ZOHO_CATALYST_IS_LOCAL: 'true', X_ZOHO_CATALYST_FUNCTION_LOADED: 'true', X_ZOHO_CATALYST_ACCOUNTS_URL: constants_1.ORIGIN.auth, X_ZOHO_CATALYST_CONSOLE_URL: constants_1.ORIGIN.admin, X_ZOHO_CATALYST_RESOURCE_ID: (_h = this.target) === null || _h === void 0 ? void 0 : _h.id, X_ZOHO_STRATUS_RESOURCE_SUFFIX: constants_1.ORIGIN.stratusSuffix, CATALYST_PORTAL_DOMAIN: constants_1.ORIGIN.iamPortal, CATALYST_PROJECT_TIMEZONE: (0, project_1.getProjectTimezone)(Intl.DateTimeFormat().resolvedOptions().timeZone) }, this.target.env_var)
}).RAW();
}
else if ((_j = this.target.stack) === null || _j === void 0 ? void 0 : _j.startsWith(runtime_1.default.language.java.value)) {
const javaInvokerDir = (0, path_1.parse)(this.javaInvoker).dir;
slaveOptions.push('-cp');
slaveOptions.push(javaInvokerDir +
fn_utils_1.fnUtils.java.classPathSep +
(0, path_1.join)(javaInvokerDir, 'lib', '*'));
if (debugPort !== null) {
slaveOptions.push('-Xdebug');
slaveOptions.push('-Xrunjdwp:transport=dt_socket,address=' +
debugPort +
',server=y,suspend=y');
}
slaveOptions.push((0, path_1.basename)(this.javaInvoker));
slaveOptions.push(javaInvokerDir);
slaveOptions.push(JSON.stringify(slaveFnTarget));
slaveOptions.push(JSON.stringify(data));
slaveOptions.push(JSON.stringify({
'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)()
}));
slaveOptions.push(JSON.stringify({
'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'
}));
const configKey = `${this.target.stack}.bin`;
const userConfigCmd = userConfig_1.default.get(configKey);
const spawnCommand = (0, ensure_java_userconfig_1.getJavaSpawnCommand)(userConfigCmd, 'java', this.target.stack);
this.slave = (0, shell_1.spawn)(spawnCommand, slaveOptions, {
cwd: (0, path_1.join)(projectRoot, constants_1.FOLDERNAME.build, constants_1.FOLDERNAME.functions, this.target.name),
stdio: 'pipe',
env: Object.assign({ X_ZOHO_CATALYST_IS_LOCAL: 'true', X_ZOHO_CATALYST_FUNCTION_LOADED: 'true', X_ZOHO_CATALYST_ACCOUNTS_URL: constants_1.ORIGIN.auth, X_ZOHO_CATALYST_CONSOLE_URL: constants_1.ORIGIN.admin, X_ZOHO_CATALYST_RESOURCE_ID: (_k = this.target) === null || _k === void 0 ? void 0 : _k.id, X_ZOHO_STRATUS_RESOURCE_SUFFIX: constants_1.ORIGIN.stratusSuffix, CATALYST_PORTAL_DOMAIN: constants_1.ORIGIN.iamPortal, CATALYST_PROJECT_TIMEZONE: (0, project_1.getProjectTimezone)(Intl.DateTimeFormat().resolvedOptions().timeZone) }, this.target.env_var)
}).RAW();
}
else if ((_l = this.target.stack) === null || _l === void 0 ? void 0 : _l.startsWith(runtime_1.default.language.python.value)) {
const stackVersion = (_m = this.target.stack) === null || _m === void 0 ? void 0 : _m.replace('python_', '');
const runtimesDir = constants_1.ENVPATH.runtimes.data;
const httpPort = parseInt(runtime_store_1.default.get('context.port.http.' + constants_1.REMOTE_REF.functions.type[constants_1.FN_TYPE.basic]), 10);
const runTimePort = yield port_resolver_1.default.getFreePort(httpPort, 20, false);
yield fs_1.ASYNC.ensureFile(this.responseFile, true);
yield fs_1.ASYNC.ensureFile(this.metaFile, true);
slaveOptions.push('-u');
slaveOptions.push((0, path_1.join)(runtimesDir, runtime_1.default.language.python.value, `zcatalyst_runtime_${stackVersion.replace('_', '')}`, 'main.py'));
this.slave = (0, shell_1.spawn)(userConfig_1.default.get(`python${stackVersion}.bin`), slaveOptions, {
cwd: (0, path_1.join)(projectRoot, constants_1.FOLDERNAME.build, constants_1.FOLDERNAME.functions, this.target.name),
stdio: 'pipe',
env: Object.assign({ X_ZOHO_CATALYST_IS_LOCAL: 'true', X_ZOHO_CATALYST_FUNCTION_LOADED: 'true', X_ZOHO_CATALYST_ACCOUNTS_URL: constants_1.ORIGIN.auth, X_ZOHO_CATALYST_CONSOLE_URL: constants_1.ORIGIN.admin, X_ZOHO_CATALYST_RESOURCE_ID: (_o = this.target) === null || _o === void 0 ? void 0 : _o.id, X_ZOHO_STRATUS_RESOURCE_SUFFIX: constants_1.ORIGIN.stratusSuffix, CATALYST_PORTAL_DOMAIN: constants_1.ORIGIN.iamPortal, X_ZOHO_CATALYST_CODE_LOCATION: (0, path_1.join)(projectRoot, constants_1.FOLDERNAME.build, constants_1.FOLDERNAME.functions, this.target.name), X_ZOHO_CATALYST_SERVER_LISTEN_PORT: runTimePort.toString(), X_ZOHO_DATA_URL: `http://localhost:${runTimePort}/data`, X_ZOHO_CALLBACK_URL: `http://localhost:${runTimePort}/callback`, CATALYST_PROJECT_TIMEZONE: (0, project_1.getProjectTimezone)(Intl.DateTimeFormat().resolvedOptions().timeZone), X_ZOHO_ADMIN_CRED_TOKEN: 'dummy', X_ZOHO_PROJECT_SECRET_KEY: 'dummy', X_ZOHO_CATALYST_ORG: (0, project_1.getEnvId)(), X_ZOHO_CATALYST_ENVIRONMENT: 'Local', X_ZOHO_JOBMETA_JOBID: 'dummy', CATALYST_FUNCTION_TYPE: constants_1.REMOTE_REF.functions.type[this.target.type] }, this.target.env_var)
}).RAW();
this.slave.once('spawn', () => __awaiter(this, void 0, void 0, function* () {
var _r;
const jsonData = data;
jsonData.timestamp = Date.now();
const writeResponse = (response, status) => {
fs_1.SYNC.writeFile(this.responseFile, response);
fs_1.SYNC.writeFile(this.metaFile, JSON.stringify({ response: { statusCode: status } }));
};
if (this.target.type === constants_1.FN_TYPE.job) {
const jobDetailsServer = new http_1.Server((serverReq, serverRes) => __awaiter(this, void 0, void 0, function* () {
var _s, _t, _u;
if ((_s = serverReq.url) === null || _s === void 0 ? void 0 : _s.includes('data')) {
serverRes.writeHead(200);
serverRes.write(JSON.stringify({
data: jsonData
}));
serverRes.end();
}
else if ((_t = serverReq.url) === null || _t === void 0 ? void 0 : _t.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())));
});
switch (reqData.job_status) {
case 200: {
writeResponse('SUCCESS', 200);
break;
}
case 532: {
writeResponse('CODE_EXCEPTION', 532);
break;
}
case 530: {
writeResponse('FAILURE', 530);
break;
}
case 500: {
writeResponse('INTERNAL_SERVER_ERROR', 500);
break;
}
}
serverRes.writeHead(200);
serverRes.end();
return;
}
else {
(0, logger_1.debug)(`Invalid request: (${serverReq.method}) ${serverReq.url}`);
writeResponse('INTERNAL_SERVER_ERROR', 500);
(_u = this.slave) === null || _u === void 0 ? void 0 : _u.kill('SIGINT');
return;
}
}));
const jobConnDestroyer = new server_1.ConnectionDestroyer(jobDetailsServer);
jobDetailsServer.listen(runTimePort, () => {
(0, logger_1.debug)('server listening on port :' + runTimePort);
});
(_r = this.slave) === null || _r === void 0 ? void 0 : _r.once('exit', () => {
jobConnDestroyer.destroy();
});
return;
}
const reqJson = JSON.stringify(jsonData);
yield (0, http_functions_1.checkIfRuntimeServerRunning)(runTimePort.toString());
const req = http_1.default
.request(`http://127.0.0.1:${runTimePort}`, {
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',
'Content-Length': reqJson.length
}
}, (resp) => {
var _a;
if ([
constants_1.REMOTE_REF.functions.type.cron,
constants_1.REMOTE_REF.functions.type.event
].includes(this.target.type + '')) {
switch (resp.statusCode) {
case 200: {
writeResponse('SUCCESS', 200);
break;
}
case 532: {
writeResponse('CODE_EXCEPTION', 532);
break;
}
case 530: {
writeResponse('FAILURE', 530);
break;
}
case 500: {
writeResponse('INTERNAL_SERVER_ERROR', 500);
break;
}
}
(_a = this.slave) === null || _a === void 0 ? void 0 : _a.kill('SIGTERM');
return;
}
resp.on('data', (data) => {
const respStr = data.toString();
fs_1.SYNC.writeFile(this.responseFile, respStr);
});
resp.on('close', () => {
var _a;
const metaJson = {
response: { statusCode: resp.statusCode }
};
fs_1.SYNC.writeFile(this.metaFile, JSON.stringify(metaJson));
(_a = this.slave) === null || _a === void 0 ? void 0 : _a.kill('SIGTERM');
});
})
.on('error', (err) => {
var _a;
(0, logger_1.debug)(err);
(_a = this.slave) === null || _a === void 0 ? void 0 : _a.kill('SIGTERM');
});
req.write(reqJson);
req.end();
}));
}
if (this.slave === null) {
throw new error_1.default('Slave listening started before initializing', {
exit: 2
});
}
(_p = this.slave.stdout) === null || _p === void 0 ? void 0 : _p.on('data', (message) => {
(0, shell_1.clearLine)(process.stdout);
(0, logger_1.info)(message.toString());
});
(_q = this.slave.stderr) === null || _q === void 0 ? void 0 : _q.on('data', (message) => {
const errorStr = js_1.JS.trim(message.toString());
(0, shell_1.clearLine)(process.stdout);
(0, logger_1.info)(errorStr);
});
this.slave.on('exit', (code, sig) => __awaiter(this, void 0, void 0, function* () {
(0, logger_1.debug)(`local fn slave exit: ${code}, ${sig}`);
if (code === 0) {
(0, logger_1.info)(`[CLI] Function ${this.target.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 (this.target.type) {
case constants_1.FN_TYPE.basic:
(0, logger_1.info)(`[response - ${this.target.name}] ${response + ''}`);
(0, logger_1.info)(`[status - ${this.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.target.name}] ${response || 'Unknown'}`);
if (!response) {
(0, logger_1.info)(`[CLI] Make sure to close the ${this.target.name} (${this.target.type}) function.`);
}
this.localFnEvents.emit('response', {
status: meta.statusCode
});
break;
case constants_1.FN_TYPE.integration:
(0, logger_1.info)(`[response - ${this.target.name}] ${response + ''}`);
try {
this.localFnEvents.emit('response', JSON.parse(response || ''));
}
catch (er) {
(0, logger_1.debug)('Invalid integration response: ', er);
}
break;
}
}
else {
(code === 130 || sig === 'SIGINT') &&
(0, logger_1.info)(`[CLI] Function ${this.target.name} interrupted`);
(code === 143 ||
sig === 'SIGTERM' ||
sig === 'SIGQUIT' ||
sig === 'SIGKILL') &&
(0, logger_1.info)(`[CLI] Function ${this.target.name} process killed`);
this.localFnEvents.listenerCount('error') > 0 &&
this.localFnEvents.emit('error', new error_1.default(`Function(${this.target.name}) process exited with ${code ? 'status: ' + code : 'signal: ' + sig}`, {
skipHelp: true
}));
}
this.repl.showPrompt();
}));
this.slave.on('error', (err) => {
this.localFnEvents.emit('error', err);
});
return this.slave;
}))();
};
if (target.type === undefined ||
!Object.values(constants_1.FN_TYPE).includes(target.type)) {
throw new error_1.default('target type is not defined', { exit: 2 });
}
this.repl = repl;
this.watcher = target.watcher;
this.target = js_1.JS.omit(target, ['zip_stream', 'watcher']);
this.slave = null;
this.nodeInvoker = (0, path_1.normalize)((0, path_1.join)(__dirname, './invoker', target.type, 'node.mjs'));
const projectRoot = runtime_store_1.default.get('project.root');
this.javaInvoker = (0, path_1.join)(projectRoot, constants_1.FOLDERNAME.build, '.catalyst', target.stack, 'Java' + target.type + 'Invoker');
if ((_a = target.stack) === null || _a === void 0 ? void 0 : _a.startsWith(runtime_1.default.language.java.value)) {
fn_utils_1.fnUtils.java.ensureJavaInvoker(this.javaInvoker, (0, path_1.normalize)((0, path_1.join)(__dirname, './invoker', target.type, 'java', 'Java' + target.type + 'Invoker.java')), target);
}
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');
this.call = this._call.bind(this);
(_b = this.watcher) === null || _b === void 0 ? void 0 : _b.on('preparing', () => {
if (!this.repl.paused) {
this.repl.pause();
}
});
(_c = this.watcher) === null || _c === void 0 ? void 0 : _c.on('compiled', () => {
(0, logger_1.labeled)(`functions[${target.name}]`, 'ready!').MESSAGE();
if (this.repl.paused) {
repl.resume();
repl.showPrompt();
}
setTimeout(() => {
var _a;
(_a = this.watcher) === null || _a === void 0 ? void 0 : _a.emit('next');
}, 1000);
});
}
shutdown() {
return __awaiter(this, void 0, void 0, function* () {
return new Promise((res) => {
if (this.slave) {
this.killed = true;
this.slave.kill('SIGTERM');
}
if (this.watcher) {
this.watcher.close().then(res).catch(res);
}
else {
res();
}
});
});
}
once(event, fn) {
this.localFnEvents.once(event, fn);
return this;
}
removeListener(event, fn) {
this.localFnEvents.removeListener(event, fn);
return this;
}
}
exports.default = LocalFunction;