UNPKG

@applitools/execution-grid-tunnel

Version:

Allows user to run tests with exection-grid and navigate to private hosts and ips

131 lines (106 loc) 3.9 kB
const { tunnelId, stringifyConfig, socks5ProxyPort, stringifyPortRange, configFileRootPath, binPath, binArgs = '-c', logFilePath, heartbeatTimeout = 15000, startTunnelTimeoutThreshold = 10000, stopProcessTimeout = 5000, } = process.env const os = require('os') const path = require('path') const {promises: fs, unlinkSync} = require('fs') const {SubProcess} = require('teen_process') const nodeCleanup = require('node-cleanup') const {createIniFileFromJson, findFreePort} = require('../utils') const TUNNEL_STATUS = require('./tunnel-status') const {convertFrpcOutputToTunnelStatus} = require('./convert-frpc-output-to-tunnel-status.js') let selfKillTimeoutId let initTimeoutId const _startTunnel = async () => { const config = JSON.parse(stringifyConfig) const {min, max} = JSON.parse(stringifyPortRange) if (typeof socks5ProxyPort !== 'undefined') { config[tunnelId].local_port = socks5ProxyPort delete config[tunnelId].plugin } else { const localPort = await findFreePort(min, max) config[tunnelId].local_port = localPort } const configFilePath = path.resolve(configFileRootPath, `${tunnelId}.ini`) await createIniFileFromJson({path: configFilePath, content: config}) const proc = new SubProcess(binPath, [binArgs, configFilePath], { stdio: 'pipe', //shell: os.platform() !== 'win32', // TODO: understand why it causes to problems detached: false, windowsHide: true }) proc.on('stream-line', (line) => { if (!process.send) return const status = convertFrpcOutputToTunnelStatus(line) if (status) { const data = { status, ...(status === TUNNEL_STATUS.INIT_ERROR ? {error: line} : {}), } process.send(data) } // TODO: Run tunnels in many case!!!!! if (status === TUNNEL_STATUS.RUNNING || status === TUNNEL_STATUS.INIT_ERROR) { initTimeoutId && clearTimeout(initTimeoutId) initTimeoutId = undefined // set timeout to heartbeatTimeout after init clearTimeout(selfKillTimeoutId) startSelfKillTimeout(heartbeatTimeout) } logFilePath && fs.appendFile(logFilePath, `${line}\r\n`).catch(console.log) }) proc.on('die', () => { process.send && process.send({status: TUNNEL_STATUS.STOPPED}) initTimeoutId && clearTimeout(initTimeoutId) initTimeoutId = undefined process.exit() }) // Closing frpc if doesn't get heart bit from parent const startSelfKillTimeout = (timeout) => { selfKillTimeoutId = setTimeout(async () => { logFilePath && fs .appendFile(logFilePath, `TunnelId ${tunnelId}: self-kill is called \r\n`) .catch(console.log) console.log(`TunnelId ${tunnelId}: self-kill is called`) proc.isRunning && (await proc.stop()) process.exit(1) }, timeout) } process.on('message', ({status}) => { if (status !== 'ok') { return } if (selfKillTimeoutId !== undefined) { clearTimeout(selfKillTimeoutId) } startSelfKillTimeout(heartbeatTimeout) }) // In some cases when frpc connection fails, it tries to reconnect forever. // In that line we send error to frpc controller after startTunnelTimeoutThreshold initTimeoutId = setTimeout(() => { process.send({status: TUNNEL_STATUS.INIT_TIMEOUT_ERROR}) }, startTunnelTimeoutThreshold) nodeCleanup((exitCode, signal) => { process.send({status: TUNNEL_STATUS.STOPPED}) selfKillTimeoutId && clearTimeout(selfKillTimeoutId) initTimeoutId && clearTimeout(initTimeoutId) //TODO: should we check if proc.isRunning before run proc.stop proc.stop(signal, stopProcessTimeout).catch(console.log) unlinkSync(configFilePath) }) await proc.start() // set selfkill in case execution-grid-tunnel crashes before init startSelfKillTimeout(parseInt(startTunnelTimeoutThreshold) + parseInt(heartbeatTimeout)) } _startTunnel()