@best/runner-remote
Version:
Best Runner (Headless)
232 lines • 9.31 kB
JavaScript
"use strict";
/*
* Copyright (c) 2019, salesforce.com, inc.
* All rights reserved.
* SPDX-License-Identifier: MIT
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
*/
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.RunnerRemote = void 0;
const debug_1 = __importDefault(require("debug"));
const path_1 = __importDefault(require("path"));
const socket_io_client_1 = require("socket.io-client");
const shared_1 = require("@best/shared");
const utils_1 = require("@best/utils");
const file_uploader_1 = __importDefault(require("./utils/file-uploader"));
const create_tar_1 = require("./utils/create-tar");
const { AGENT_REJECTION, BENCHMARK_UPLOAD_REQUEST, CONNECT_ERROR, CONNECT, DISCONNECT, ERROR, RECONNECT_FAILED } = shared_1.BEST_RPC;
const { BENCHMARK_END, BENCHMARK_ERROR, BENCHMARK_LOG, BENCHMARK_RESULTS, BENCHMARK_START, BENCHMARK_UPDATE } = shared_1.BEST_RPC;
const RPC_METHODS = [
AGENT_REJECTION,
BENCHMARK_END,
BENCHMARK_ERROR,
BENCHMARK_LOG,
BENCHMARK_RESULTS,
BENCHMARK_START,
BENCHMARK_UPDATE,
BENCHMARK_UPLOAD_REQUEST,
CONNECT_ERROR,
CONNECT,
DISCONNECT,
ERROR,
RECONNECT_FAILED,
];
const THROW_FUNCTION = function (err) {
throw new Error(err || 'unknown error');
};
const log_rpc = (0, debug_1.default)('runner-remote:rpc');
class RunnerRemote {
uri;
running = false;
uploader;
pendingBenchmarks;
isHub;
socket;
benchmarkBuilds;
runnerLogStream;
benchmarkResults = [];
uploadingBenchmark = false;
_onBenchmarkError = THROW_FUNCTION;
_onBenchmarksRunSuccess = THROW_FUNCTION;
constructor(benchmarksBuilds, runnerLogStream, config) {
const { isHub, options, specs, token, uri } = config;
const socketOptions = {
path: '/best',
reconnection: false,
autoConnect: false,
query: {
...options,
specs: JSON.stringify(specs),
jobs: benchmarksBuilds.length,
},
pfx: [],
};
if (token) {
socketOptions.query.authToken = token;
}
this.uri = uri;
this.isHub = isHub;
this.socket = (0, socket_io_client_1.io)(uri, (0, utils_1.proxifiedSocketOptions)(socketOptions));
this.benchmarkBuilds = benchmarksBuilds;
this.pendingBenchmarks = benchmarksBuilds.length;
this.runnerLogStream = runnerLogStream;
RPC_METHODS.forEach((methodName) => this.socket.on(methodName, this[methodName].bind(this)));
}
// -- Socket lifecycle ----------------------------------------------------------------------
[CONNECT]() {
log_rpc(`socket:connect`);
}
[CONNECT_ERROR]() {
log_rpc('socket:error');
this._triggerBenchmarkError(`Unable to connect to agent "${this.uri}" (socket:connect_error)`);
}
[DISCONNECT]() {
log_rpc('socket:disconnect');
this._triggerBenchmarkError('socket:disconnect');
}
[ERROR]() {
log_rpc('socket:error');
this._triggerBenchmarkError('socket:reconnect_failed');
}
[RECONNECT_FAILED]() {
log_rpc('reconnect_failed');
this._triggerBenchmarkError('socket:reconnect_failed');
}
// -- Specific Best RPC Commands ------------------------------------------------------------
[AGENT_REJECTION](reason) {
log_rpc(`agent_rejection: ${AGENT_REJECTION}`);
this._triggerBenchmarkError(reason);
}
[BENCHMARK_UPLOAD_REQUEST]() {
const benchmarkConfig = this.benchmarkBuilds.shift();
if (!benchmarkConfig) {
return this._triggerBenchmarkError('Agent is requesting more jobs than specified');
}
if (this.uploadingBenchmark) {
return this._triggerBenchmarkError('Already uploading a benchmark');
}
benchmarkConfig.isHub = this.isHub;
log_rpc(`${BENCHMARK_UPLOAD_REQUEST} - Sending: ${benchmarkConfig.benchmarkSignature}`);
this.socket.emit(shared_1.BEST_RPC.BENCHMARK_UPLOAD_RESPONSE, benchmarkConfig, async (_benchmarkSignature) => {
const { benchmarkName, benchmarkEntry, benchmarkRemoteEntry } = benchmarkConfig;
const bundleDirname = path_1.default.dirname(benchmarkRemoteEntry || benchmarkEntry);
const tarBundle = path_1.default.resolve(bundleDirname, `${benchmarkName}.tgz`);
try {
/*
* Unfortunately, because of the way Best was designed, the hub
* does a lot of unnecessary extracting and compressing of files,
* when it already has the `tar` it needs to send to the agent.
*
* Because of that, the more tests Best has to run, the slower things
* will be, and sometimes Best will even crash.
*
* The following is a band-aid fix until a more major refactoring \
* rearchitecting of Best is done.
*
* Ref: https://github.com/salesforce/best/commit/889191e84607afd93b206d64c68649b5c0905952
*/
if (!this.isHub) {
await (0, create_tar_1.createTarBundle)(bundleDirname, benchmarkName);
}
const uploader = await this._getUploaderInstance();
uploader.upload(tarBundle);
}
catch (err) {
return this._triggerBenchmarkError(err.message);
}
});
}
[BENCHMARK_RESULTS](results) {
this.benchmarkResults.push(...results);
this.pendingBenchmarks -= 1;
log_rpc(`${BENCHMARK_UPLOAD_REQUEST} - Received results, pending ${this.pendingBenchmarks}`);
if (this.pendingBenchmarks === 0) {
if (this.benchmarkBuilds.length === 0) {
this._triggerBenchmarkSucess();
}
else {
this._triggerBenchmarkError('Results missmatch: Agent has sent more jobs that benchmarks consumed...');
}
}
}
// -- Logger methods (must be side effect free) --------------------------------------------------------------------
[BENCHMARK_START](benchmarkSignature) {
this.runnerLogStream.onBenchmarkStart(benchmarkSignature);
}
[BENCHMARK_UPDATE](benchmarkSignature, state, runtimeOpts) {
this.runnerLogStream.updateBenchmarkProgress(benchmarkSignature, state, runtimeOpts);
}
[BENCHMARK_END](benchmarkSignature) {
this.runnerLogStream.onBenchmarkEnd(benchmarkSignature);
}
[BENCHMARK_ERROR](benchmarkSignature) {
this.runnerLogStream.onBenchmarkError(benchmarkSignature);
}
[BENCHMARK_LOG](msg) {
this.runnerLogStream.log(msg);
}
// -- Private --------------------------------------------------------------------
_getUploaderInstance() {
if (this.uploader) {
return Promise.resolve(this.uploader);
}
return new Promise((resolve, reject) => {
const uploader = new file_uploader_1.default(this.socket);
const cancelRejection = setTimeout(() => {
reject('[RUNNER_REMOTE] uploader:error | Unable to stablish connection for upload benchmarks');
}, 10000);
uploader.on('start', () => {
log_rpc('uploader:start');
this.uploadingBenchmark = true;
});
uploader.on('error', (err) => {
log_rpc('uploader:error');
this._triggerBenchmarkError(err);
});
uploader.on('complete', () => {
log_rpc('uploader:complete');
this.uploadingBenchmark = false;
});
uploader.on('ready', () => {
log_rpc('uploader:ready');
this.uploader = uploader;
clearTimeout(cancelRejection);
resolve(uploader);
});
});
}
_triggerBenchmarkSucess() {
if (this.running) {
this.running = false;
this.socket.disconnect();
this._onBenchmarksRunSuccess(this.benchmarkResults);
this._onBenchmarksRunSuccess = THROW_FUNCTION; // To catch side-effects and race conditions
}
}
_triggerBenchmarkError(error_msg) {
if (this.running) {
const error = typeof error_msg === 'string' ? new Error(error_msg) : error_msg;
this.running = false;
this._onBenchmarkError(error);
this._onBenchmarkError = THROW_FUNCTION; // To catch side-effects and race conditions
}
}
run() {
return new Promise((resolve, reject) => {
this._onBenchmarksRunSuccess = resolve;
this._onBenchmarkError = reject;
this.running = true;
this.socket.open();
});
}
interruptRunner() {
if (this.running) {
this.socket.disconnect();
}
}
}
exports.RunnerRemote = RunnerRemote;
//# sourceMappingURL=runner-remote.js.map