UNPKG

@tableau/taco-toolkit

Version:
298 lines 24.5 kB
"use strict"; 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 __generator = (this && this.__generator) || function (thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (g && (g = 0, op[0] && (_ = 0)), _) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.startConnector = void 0; var child_process_1 = __importDefault(require("child_process")); var fs_1 = __importDefault(require("fs")); var path_1 = __importDefault(require("path")); var superagent_1 = __importDefault(require("superagent")); var chalk_1 = __importDefault(require("chalk")); var status_code_1 = require("../../../shared/errors/status-code"); var error_source_1 = require("../../../shared/errors/error-source"); var error_codes_1 = require("../../../shared/errors/error-codes"); var command_1 = require("../../taco-commands/utils/command"); var constants_1 = require("../../taco-commands/utils/constants"); var toolkit_paths_1 = require("../../taco-commands/utils/toolkit-paths"); var connector_1 = require("../../taco-commands/utils/connector"); var connector_json_1 = require("../../taco-commands/utils/connector-json"); var logging_1 = require("../../taco-commands/utils/logging"); var PORT_FILE_CHECK_INTERVAL = 500; var PORT_FILE_WAIT_TIMEOUT = 30000; var CALLER_ID = 'taco-start'; var CONNECTION_ID = 'simulator'; var PORT_FILE_NAME = '.eps'; var epsProcess; /** * Starting EPS with loading a connector at current path. * * TODO: this code path is only for connector authors developments. * We should make this as a dev server. */ function startConnector(config, state) { return __awaiter(this, void 0, void 0, function () { var spinner, debugMode, tacoRootPath, portFilePath, logPath, error_1, connectionState, error_2, connectionInfo; return __generator(this, function (_a) { switch (_a.label) { case 0: spinner = state.spinner; debugMode = config.debugMode; tacoRootPath = (0, command_1.resolvePath)(config.tacoRootPath); (0, connector_1.exitIfNotTacoRoot)(tacoRootPath); spinner.start('Starting EPS...'); portFilePath = getEPSPortFilePath(tacoRootPath); logPath = (0, logging_1.getLogPath)(tacoRootPath); _a.label = 1; case 1: _a.trys.push([1, 3, , 4]); return [4 /*yield*/, startEps(toolkit_paths_1.EPS_MAIN_PATH, portFilePath, true, logPath)]; case 2: _a.sent(); return [3 /*break*/, 4]; case 3: error_1 = _a.sent(); spinner.fail('Failed to start EPS'); (0, logging_1.logFatalError)({ statusCode: status_code_1.StatusCode.Cancelled, errorSource: error_source_1.ErrorSources.Client, errorCode: error_codes_1.ErrorCodes.Q1XA447W, message: error_1.toString(), error: error_1, }); terminateEPSAndExit(); return [3 /*break*/, 4]; case 4: spinner.success('EPS started'); spinner.start("Loading connector: ".concat(tacoRootPath)); _a.label = 5; case 5: _a.trys.push([5, 7, , 8]); return [4 /*yield*/, launchConnector(tacoRootPath, debugMode)]; case 6: connectionState = _a.sent(); return [3 /*break*/, 8]; case 7: error_2 = _a.sent(); spinner.fail('Error occurred while loading the connector'); (0, logging_1.logFatalError)({ statusCode: status_code_1.StatusCode.Cancelled, errorSource: error_source_1.ErrorSources.Client, errorCode: error_codes_1.ErrorCodes.OD3MMIGV, message: error_2.toString(), error: error_2, }); terminateEPSAndExit(); return [3 /*break*/, 8]; case 8: if (isCanceledConnector(connectionState)) { spinner.warn('Connector loading is canceled. Terminating EPS...'); terminateEPSAndExit(); } connectionInfo = connectionState.info; spinner.success("Successfully loaded connector '".concat(connectionInfo.connectorName, "'")); displayConnectionInfo(connectionInfo, logPath); return [2 /*return*/]; } }); }); } exports.startConnector = startConnector; function startEps(epsExecutablePath, epsPortFilePath, debug, logDir) { var _a, _b; return __awaiter(this, void 0, void 0, function () { var forkParams; return __generator(this, function (_c) { switch (_c.label) { case 0: if (!fs_1.default.existsSync(epsExecutablePath)) { throw new Error("Failed to find EPS executable file: ".concat(epsExecutablePath)); } // Delete the existing port file, so we won't read the wrong eps info from last time starting eps if (fs_1.default.existsSync(epsPortFilePath)) { logging_1.logger.debug("Found stale EPS port file. Deleting ".concat(epsPortFilePath)); fs_1.default.unlinkSync(epsPortFilePath); } forkParams = [ "--caller-pid=".concat(process.pid), "--caller-id=".concat(CALLER_ID), "--port-file=".concat(epsPortFilePath), "--log-dir=".concat(logDir), '--feature-flags=EPSSupportgRPC:off,EPSStaticTacoUnpack:off', ]; // TODO make the simulator log level configurable // if(logLevel){ // forkParams.push(`--log-level ${logLevel}`) // } if (debug) { forkParams.push('--inspect-brk'); } logging_1.logger.debug("Launching EPS with ".concat(forkParams)); epsProcess = child_process_1.default.fork(epsExecutablePath, forkParams, { detached: false, silent: false, stdio: 'pipe', }); (_a = epsProcess.stdout) === null || _a === void 0 ? void 0 : _a.on('data', function (msg) { logging_1.logger.debug(Buffer.from(msg).toString()); }); (_b = epsProcess.stderr) === null || _b === void 0 ? void 0 : _b.on('data', function (msg) { logging_1.logger.error("Error occured in EPS.\n".concat(Buffer.from(msg).toString())); }); return [4 /*yield*/, waitForEPSStart(epsPortFilePath, PORT_FILE_CHECK_INTERVAL, PORT_FILE_WAIT_TIMEOUT)]; case 1: return [2 /*return*/, _c.sent()]; } }); }); } function waitForEPSStart(epsPortFilePath, interval, timeout) { return new Promise(function (resolve, reject) { var startTime = Date.now(); var intervalId = setInterval(function () { if (fs_1.default.existsSync(epsPortFilePath)) { clearInterval(intervalId); logging_1.logger.debug("Found EPS port file '".concat(epsPortFilePath, "'")); resolve(); } else if (Date.now() - startTime > timeout) { clearInterval(intervalId); reject(new Error("Timed out! Failed to locate EPS port file after ".concat(timeout, " ms at path: ").concat(epsPortFilePath))); } }, interval); }); } function launchConnector(tacoRootPath, debug) { return __awaiter(this, void 0, void 0, function () { var connectorJsonPath, connectorJson, connectorName, _a, epsInstancePort, epsInstanceId, url, response, _b, extractorId, extractorPort; return __generator(this, function (_c) { switch (_c.label) { case 0: connectorJsonPath = (0, connector_1.getConnectorJsonPath)(tacoRootPath); connectorJson = (0, connector_json_1.getConnectorJson)(connectorJsonPath); connectorName = (0, connector_json_1.getConnectorClassName)(connectorJson); _a = getEPSInstanceInfo(tacoRootPath), epsInstancePort = _a.epsInstancePort, epsInstanceId = _a.epsInstanceId; url = "http://localhost:".concat(epsInstancePort, "/launch/").concat(connectorName, "?debug=").concat(debug); logging_1.logger.debug("Loading connector with url: ".concat(url)); return [4 /*yield*/, superagent_1.default .post(url) .set('connection-id', CONNECTION_ID) .set('caller-id', CALLER_ID) .set('eps-instance-id', epsInstanceId) .set('plugin-path', tacoRootPath)]; case 1: response = _c.sent(); if (isCanceledResponse(response)) { return [2 /*return*/, { state: 'canceled', }]; } _b = response.body, extractorId = _b.extractorId, extractorPort = _b.port; if (!extractorId || !extractorPort) { throw new Error("Received invalid extractor info from ".concat(url, "\n") + // prettier "Response body: ".concat(JSON.stringify(response.body))); } return [2 /*return*/, { state: 'success', info: { extractorId: extractorId, extractorPort: extractorPort, epsInstanceId: epsInstanceId, epsInstancePort: epsInstancePort, connectorName: connectorName, }, }]; } }); }); } function isCanceledResponse(response) { var body = response.body; if (typeof body === 'object' && body !== null) { return body.status === 'canceled'; } return false; } function terminateEPSAndExit() { logging_1.logger.debug('Shutting down EPS'); if (epsProcess !== undefined) { try { epsProcess.kill('SIGKILL'); } catch (error) { (0, logging_1.logFatalError)({ statusCode: status_code_1.StatusCode.Cancelled, errorSource: error_source_1.ErrorSources.Client, errorCode: error_codes_1.ErrorCodes.KY5XRFYV, message: "Unable to terminate EPS process: ".concat(error), error: error, }); process.exit(constants_1.TACO_COMMAND_ERROR_EXIT_CODE); } } logging_1.logger.info('EPS terminated'); return process.exit(constants_1.TACO_COMMAND_ERROR_EXIT_CODE); } function getEPSPortFilePath(tacoRootPath) { return path_1.default.join(tacoRootPath, PORT_FILE_NAME); } function getEPSInstanceInfo(tacoRootPath) { var epsFilePath = getEPSPortFilePath(tacoRootPath); var lines = fs_1.default.readFileSync(epsFilePath, 'utf-8').split(/\r?\n/); return { epsInstancePort: lines[0], epsInstanceId: lines[1], }; } function isCanceledConnector(connectorState) { return connectorState.state === 'canceled'; } function displayConnectionInfo(connectionInfo, serverLogPath) { var extractorPort = connectionInfo.extractorPort, extractorId = connectionInfo.extractorId, epsInstanceId = connectionInfo.epsInstanceId; console.info("\nYou may now fetch metadata and data from the following urls with the headers:\n" + " Tables: ".concat(chalk_1.default.cyan("http://localhost:".concat(extractorPort, "/tables")), "\n") + " Metadata: ".concat(chalk_1.default.cyan("http://localhost:".concat(extractorPort, "/tables/<TABLE_NAME>/metadata")), "\n") + " Data: ".concat(chalk_1.default.cyan("http://localhost:".concat(extractorPort, "/tables/<TABLE_NAME>/rows")))); console.info("\n".concat(chalk_1.default.green('Request Headers:'), "\n") + " extractor-id: ".concat(extractorId, "\n") + " eps-instance-id: ".concat(epsInstanceId, "\n") + " caller-id: ".concat(CALLER_ID, "\n") + " connection-id: ".concat(CONNECTION_ID)); console.info("\nServer logs are located at: ".concat(serverLogPath, "\n")); } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3RhcnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvY2xpL3RhY28tc2NyaXB0cy9zdGFydC9zdGFydC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFBQSxnRUFBMkQ7QUFDM0QsMENBQW1CO0FBQ25CLDhDQUF1QjtBQUN2QiwwREFBbUM7QUFDbkMsZ0RBQXlCO0FBRXpCLGtFQUErRDtBQUMvRCxvRUFBa0U7QUFDbEUsa0VBQStEO0FBQy9ELDZEQUErRTtBQUMvRSxpRUFBa0Y7QUFDbEYseUVBQXVFO0FBQ3ZFLGlFQUE2RjtBQUM3RiwyRUFBa0c7QUFDbEcsNkRBQXFGO0FBRXJGLElBQU0sd0JBQXdCLEdBQUcsR0FBRyxDQUFBO0FBQ3BDLElBQU0sc0JBQXNCLEdBQUcsS0FBTSxDQUFBO0FBQ3JDLElBQU0sU0FBUyxHQUFHLFlBQVksQ0FBQTtBQUM5QixJQUFNLGFBQWEsR0FBRyxXQUFXLENBQUE7QUFDakMsSUFBTSxjQUFjLEdBQUcsTUFBTSxDQUFBO0FBRTdCLElBQUksVUFBb0MsQ0FBQTtBQStCeEM7Ozs7O0dBS0c7QUFDSCxTQUFzQixjQUFjLENBQUMsTUFBbUIsRUFBRSxLQUFxQjs7Ozs7O29CQUNyRSxPQUFPLEdBQUssS0FBSyxRQUFWLENBQVU7b0JBQ2pCLFNBQVMsR0FBSyxNQUFNLFVBQVgsQ0FBVztvQkFFdEIsWUFBWSxHQUFHLElBQUEscUJBQVcsRUFBQyxNQUFNLENBQUMsWUFBWSxDQUFDLENBQUE7b0JBQ3JELElBQUEsNkJBQWlCLEVBQUMsWUFBWSxDQUFDLENBQUE7b0JBRS9CLE9BQU8sQ0FBQyxLQUFLLENBQUMsaUJBQWlCLENBQUMsQ0FBQTtvQkFDMUIsWUFBWSxHQUFHLGtCQUFrQixDQUFDLFlBQVksQ0FBQyxDQUFBO29CQUMvQyxPQUFPLEdBQUcsSUFBQSxvQkFBVSxFQUFDLFlBQVksQ0FBQyxDQUFBOzs7O29CQUd0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