UNPKG

wave-engine

Version:

[![MIT License](https://img.shields.io/badge/license-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![Latest Release](https://img.shields.io/npm/v/wave-engine.svg)](https://www.npmjs.com/package/wave-engine)

394 lines (393 loc) 18.6 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; const crypto_1 = require("crypto"); const high5_1 = require("hcloud-sdk/lib/interfaces/high5"); const execution_1 = require("hcloud-sdk/lib/interfaces/high5/space/execution"); const os_1 = __importDefault(require("os")); const client_1 = __importDefault(require("../debug/client")); const StreamCancelledError_1 = __importDefault(require("../errors/StreamCancelledError")); const MacOSMemory_1 = require("../helpers/osHelper/MacOSMemory"); const MacOSVersion_1 = require("../helpers/osHelper/MacOSVersion"); const WindowsVersion_1 = require("../helpers/osHelper/WindowsVersion"); const StreamResult_1 = require("../models/StreamResult"); const StreamSingleNodeResult_1 = require("../models/StreamSingleNodeResult"); const NodeExecutor_1 = __importDefault(require("./NodeExecutor")); class StreamRunner { static DEBUG_TIMEOUT = 10 * 60 * 1000; agentInfo; executionPackage; streamResult; executionStateHelper; extraCatalogLocations; catalogPath; debug; debugClient; currentNode; cleanupFns = []; executionStack = []; constructor(executionPackage, streamResult, catalogPath, agentInfo) { this.executionPackage = executionPackage; this.catalogPath = catalogPath; this.setAgentInfo(agentInfo); this.streamResult = streamResult ?? StreamResult_1.StreamResult.create({ payload: this.executionPackage.payload, uuid: (0, crypto_1.randomUUID)(), failed: false, nodeResults: [], startTimestamp: Date.now(), runner: this, }); if (executionPackage.debug) { this.debug = DebugState.CONTINUE; this.debugClient = new client_1.default(); } } async process(dry, executionStateHelper, nextNodeUUID, additionalConnectorRoot, extraCatalogLocations) { this.streamResult.dry = dry; this.streamResult.startTimestamp = Date.now(); this.executionStateHelper = executionStateHelper; if (extraCatalogLocations) { this.extraCatalogLocations = extraCatalogLocations; } if (this.executionPackage.debug) { return this.processDebug(dry); } nextNodeUUID = nextNodeUUID ?? this.executionPackage.design.startNode; const cleanupFns = []; while (nextNodeUUID !== undefined && this.executionStateHelper.getOutcome() !== execution_1.High5ExecutionOutcome.CANCELED) { await this.setMacOSUsedMem(); const nodeExecutionResult = await new NodeExecutor_1.default(nextNodeUUID, this.executionPackage, this.streamResult).process(dry, this.executionStateHelper, this, additionalConnectorRoot, this.catalogPath, this.extraCatalogLocations); executionStateHelper.updateNodeResult(nodeExecutionResult.nodeResult); nextNodeUUID = undefined; if (nodeExecutionResult.node) { if (nodeExecutionResult.nodeResult.failed) { nextNodeUUID = nodeExecutionResult.node.onFail; } else { nextNodeUUID = nodeExecutionResult.node.onSuccess; } } if (typeof nodeExecutionResult.cleanupFn === "function") { cleanupFns.push(nodeExecutionResult.cleanupFn); } } for (let i = cleanupFns.length - 1; i >= 0; i--) { await cleanupFns[i](); } this.streamResult.failed = (this.streamResult.nodeResults?.[this.streamResult.nodeResults.length - 1]?.failed && !additionalConnectorRoot) ?? false; this.streamResult.endTimestamp = Date.now(); if (this.streamResult.failed) { executionStateHelper.updateStateAndOutcome({ state: execution_1.High5ExecutionState.COMPLETED, outcome: execution_1.High5ExecutionOutcome.FAILED, }); } return this.streamResult.lean(); } async processDebug(dry, startNode) { if (!startNode) { startNode = this.executionPackage.design.nodes.find(n => n.uuid === this.executionPackage.design.startNode); this.currentNode = startNode; } this.executionStateHelper.on("cancelled", () => { this.debugClient.cancel(); }); nodeExecutionLoop: while (this.currentNode !== undefined && this.executionStateHelper.getOutcome() !== execution_1.High5ExecutionOutcome.CANCELED) { const nodeResult = StreamSingleNodeResult_1.StreamSingleNodeResult.create({ uuid: (0, crypto_1.randomUUID)(), nodeUuid: this.currentNode.uuid, name: this.currentNode.name, failed: false, startTimestamp: Date.now(), streamNode: this.currentNode, }); this.streamResult.nodeResults.push(nodeResult); if (this.currentNode.bypass) { nodeResult.bypassed = this.currentNode.bypass; this.executionStateHelper.updateNodeResult(nodeResult); this.executionStack.push({ node: this.currentNode, log: nodeResult }); const nextNodeUUID = nodeResult.failed ? this.currentNode.onFail : this.currentNode.onSuccess; this.currentNode = this.executionPackage.design.nodes.find(n => n.uuid === nextNodeUUID); continue; } const nodeExecutor = NodeExecutor_1.default.create(this.currentNode, this, nodeResult); const executableNode = await nodeExecutor.prepare(this.catalogPath, this.extraCatalogLocations); this.executionStateHelper.updateNodeResult(nodeResult); nodeResult.executableNode = executableNode; const nodeSpec = executableNode.getNodeSpecification(); if ((0, high5_1.isStreamNodeSpecificationV1)(nodeSpec) || (0, high5_1.isStreamNodeSpecificationV2)(nodeSpec) || (0, high5_1.isStreamNodeSpecificationV3)(nodeSpec)) { this.executionStateHelper.addRunningNode({ uuid: this.currentNode.uuid, name: nodeSpec.name, progress: 0, message: "", }); } else { nodeExecutor.error(new Error(`Unrecognized node specification version ${nodeSpec.specVersion}`)); return this.streamResult.lean(); } debugLoop: for (; this.executionStateHelper.getOutcome() !== execution_1.High5ExecutionOutcome.CANCELED;) { let command = undefined; try { if (this.debug === DebugState.STEP || (this.debug === DebugState.CONTINUE && this.currentNode.breakpoint)) { nodeResult.waiting = true; this.executionStateHelper.updateNodeResult(nodeResult); this.executionStateHelper.updateState(execution_1.High5ExecutionState.WAITING); command = await this.debugClient.wait(StreamRunner.DEBUG_TIMEOUT); } else { break debugLoop; } } catch (err) { if (err instanceof StreamCancelledError_1.default) { break nodeExecutionLoop; } else { throw err; } } finally { nodeResult.waiting = false; } this.executionStateHelper.updateState(execution_1.High5ExecutionState.RUNNING); if (command !== undefined) { switch (command.type) { case high5_1.CommandType.CONTINUE: this.debug = DebugState.CONTINUE; break debugLoop; case high5_1.CommandType.STEP_FORWARD: this.debug = DebugState.STEP; break debugLoop; case high5_1.CommandType.STEP_BACK: { this.debug = DebugState.STEP; const prevExec = this.executionStack.pop(); if (!prevExec) { continue debugLoop; } this.executionStateHelper.removeRunningNode(this.currentNode.uuid); this.executionStateHelper.removeNodeResult(nodeResult.uuid); this.removeNodeResult(nodeResult.uuid); this.currentNode = prevExec.node; this.executionStateHelper.removeNodeResult(prevExec.log.uuid); this.removeNodeResult(prevExec.log.uuid); continue nodeExecutionLoop; } case high5_1.CommandType.SET_VALUE: this.setNodeResultValue(command.uuid, command.key, command.value); break; case high5_1.CommandType.REPLACE_NODE: if (startNode?.uuid === command.node.uuid) { startNode = command.node; } this.setStreamNode(command.node); if (command.node.uuid === this.currentNode.uuid) { this.executionStateHelper.removeRunningNode(this.currentNode.uuid); this.executionStateHelper.removeNodeResult(nodeResult.uuid); this.removeNodeResult(nodeResult.uuid); this.currentNode = command.node; continue nodeExecutionLoop; } continue debugLoop; case high5_1.CommandType.RESTART: { this.debug = DebugState.CONTINUE; this.executionStateHelper.removeRunningNode(this.currentNode.uuid); for (const res of this.streamResult.nodeResults) { this.executionStateHelper.removeNodeResult(res.uuid); } this.clearNodeResults(); this.executionStack = []; this.currentNode = startNode; continue nodeExecutionLoop; } default: this.executionStateHelper.setStreamMessage(`received unknown debug command ${command.type}`); break; } } } const cleanupFn = await nodeExecutor.execute(dry); this.executionStateHelper.removeRunningNode(this.currentNode.uuid); this.executionStateHelper.updateNodeResult(nodeResult); if (typeof cleanupFn === "function") { this.cleanupFns.unshift(cleanupFn); } this.executionStack.push({ node: this.currentNode, log: nodeResult }); const nextNodeUUID = nodeResult.failed ? this.currentNode.onFail : this.currentNode.onSuccess; this.currentNode = this.executionPackage.design.nodes.find(n => n.uuid === nextNodeUUID); } await this.cleanup(); this.streamResult.failed = this.streamResult.nodeResults?.[this.streamResult.nodeResults.length - 1]?.failed ?? false; this.streamResult.endTimestamp = Date.now(); if (this.streamResult.failed) { this.executionStateHelper.updateStateAndOutcome({ state: execution_1.High5ExecutionState.COMPLETED, outcome: execution_1.High5ExecutionOutcome.FAILED, }); } return this.streamResult.lean(); } setStreamNode(node) { const idx = this.executionPackage.design.nodes.findIndex(n => n.uuid === node.uuid); if (idx === -1) { throw new Error("can only replace node by node with same UUID"); } this.executionPackage.design.nodes[idx] = node; } removeNodeResult(uuid) { const idx = this.streamResult.nodeResults.findIndex(nr => nr.uuid === uuid); if (idx !== -1) { this.streamResult.nodeResults.splice(idx, 1); } } clearNodeResults() { this.streamResult.nodeResults = []; } setNodeResultValue(uuid, key, value) { const nodeResult = this.streamResult.nodeResults.find(nr => nr.uuid === uuid); if (!nodeResult) { return; } const firstDotIdx = key.indexOf("."); if (firstDotIdx === -1) { return; } const type = key.slice(0, firstDotIdx); const name = key.slice(firstDotIdx + 1); let coll; switch (type.toUpperCase()) { case "INPUT": { if (nodeResult.inputs === undefined) { const spec = nodeResult.executableNode.getNodeSpecification(); if (((0, high5_1.isStreamNodeSpecificationV1)(spec) || (0, high5_1.isStreamNodeSpecificationV2)(spec)) && spec.inputs !== undefined && spec.inputs.some(i => i.name.toLowerCase() === name.toLowerCase())) { nodeResult.inputs = [ { name, value: undefined, originalValue: undefined, error: false, errorMessage: "", type: spec.inputs.find(i => i.name.toLowerCase() === name.toLowerCase()).type, }, ]; } } } coll = nodeResult.inputs; break; case "OUTPUT": { if (nodeResult.outputs === undefined) { const spec = nodeResult.executableNode.getNodeSpecification(); if (((0, high5_1.isStreamNodeSpecificationV1)(spec) || (0, high5_1.isStreamNodeSpecificationV2)(spec)) && spec.outputs !== undefined && spec.outputs.some(i => i.name.toLowerCase() === name.toLowerCase())) { nodeResult.outputs = [ { name, value: undefined, type: spec.outputs.find(i => i.name.toLowerCase() === name.toLowerCase()).type, }, ]; } } } coll = nodeResult.outputs; break; default: return; } const elem = coll.find(e => e.name.toLowerCase() === name.toLowerCase()); if (elem) { elem.value = value; this.executionStateHelper.updateNodeResult(nodeResult); } } setAgentInfo(agentInfo) { let osPlatform, osRelease, osVersion; if (os_1.default.platform() === "darwin") { const { platform, version, release } = (0, MacOSVersion_1.getMacOSVersion)(); osPlatform = platform; osRelease = release; osVersion = version; } else if (os_1.default.platform() === "win32") { const { platform, version, release } = (0, WindowsVersion_1.getWindowsVersion)(); osPlatform = platform; osRelease = release; osVersion = version; } else { osPlatform = os_1.default.platform(); osRelease = os_1.default.release(); osVersion = os_1.default.version(); } const allNics = os_1.default.networkInterfaces(); const filteredNics = {}; for (const interfaceName of Object.keys(allNics)) { const addresses = allNics[interfaceName]; if (!addresses) { continue; } const validAddresses = addresses.filter(addr => !addr.internal && addr.mac !== "00:00:00:00:00:00"); if (validAddresses.length > 0) { filteredNics[interfaceName] = validAddresses; } } this.agentInfo = { os: { type: os_1.default.type(), platform: osPlatform, release: osRelease, version: osVersion, }, cpu: os_1.default.cpus()[0]?.model, cpuArchitecture: os_1.default.arch(), hostname: os_1.default.hostname(), memory: { total: os_1.default.totalmem(), used: 0, }, connectionUptime: process.uptime() * 1000, nics: filteredNics, installerVersion: process.env.INSTALLER_VERSION, ip: agentInfo?.ip, version: agentInfo?.version, }; } async setMacOSUsedMem() { if (os_1.default.platform() === "darwin") { const freeMem = await (0, MacOSMemory_1.getMacOSFreeMem)(); this.agentInfo.memory.used = os_1.default.totalmem() - freeMem; } else { this.agentInfo.memory.used = os_1.default.totalmem() - os_1.default.freemem(); } } async cleanup() { for (const fn of this.cleanupFns) { try { await fn(); } catch (ignored) { } } } } exports.default = StreamRunner; var DebugState; (function (DebugState) { DebugState[DebugState["NEXT"] = 0] = "NEXT"; DebugState[DebugState["CONTINUE"] = 1] = "CONTINUE"; DebugState[DebugState["STEP"] = 2] = "STEP"; })(DebugState || (DebugState = {}));