UNPKG

zcatalyst-cli

Version:

Command Line Tool for CATALYST

484 lines (483 loc) 30.7 kB
'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;