testingbot-tunnel-launcher
Version:
A wrapper around TestingBot's Tunnel
236 lines (190 loc) • 6.83 kB
JavaScript
;
var async = require('async');
var _require = require('child_process'),
exec = _require.exec;
var fs = require('fs');
var os = require('os');
var path = require('path');
var spawn = require('child_process').spawn;
var downloader = require('./downloader');
var tunnelLocation = void 0;
var activeTunnel = void 0;
var started = false;
function logger(msg) {
console.log.apply(console, arguments);
}
function download(options, callback) {
tunnelLocation = path.normalize(path.join(__dirname, '../testingbot-tunnel.jar'));
var url = 'https://testingbot.com/tunnel/testingbot-tunnel.jar';
if (options.tunnelVersion) {
tunnelLocation = path.normalize(path.join(__dirname, '../testingbot-tunnel-' + options.tunnelVersion + '.jar'));
url = 'https://testingbot.com/tunnel/testingbot-tunnel-' + options.tunnelVersion + '.jar';
}
try {
var exists = fs.existsSync(tunnelLocation);
if (exists) {
return exec('java -jar ' + tunnelLocation + ' -h', function (error, stdout, stderr) {
if (error || stderr) {
logger('Found a cached testingbot-tunnel.jar file, but it might be corrupt. Redownloading.');
downloadTunnel(url, tunnelLocation, callback);
} else {
callback(null);
}
});
}
} catch (ignore) {}
return downloadTunnel(url, tunnelLocation, callback);
}
function downloadTunnel(url, tunnelLocation, callback) {
downloader.get(url, { fileName: 'testingbot-tunnel', destination: tunnelLocation }, function (err) {
if (err) {
return callback(new Error('Could not download the tunnel from TestingBot - please check your connection. ' + err.message));
}
return callback(null);
});
}
function createArgs(options) {
var args = [];
args.push('-jar');
args.push(tunnelLocation);
var optionMapping = {
'tunnelIdentifier': 'tunnel-identifier',
'noBump': 'nobump',
'noCache': 'nocache'
};
if (options.apiKey) {
args.push(options.apiKey);
}
if (options.apiSecret) {
args.push(options.apiSecret);
}
for (var option in options) {
if (option === 'apiKey' || option === 'apiSecret' || option === 'verbose' || option === 'tunnelVersion') {
continue;
}
var optionName = optionMapping[option] || option;
if (options[option] && typeof options[option] === 'string') {
args.push('--' + optionName);
args.push(options[option]);
} else if (options[option]) {
args.push('--' + optionName);
}
}
return args;
}
function run(options, callback) {
if (!fs.existsSync(tunnelLocation)) {
return callback(new Error('Tunnel jar file is not present in ' + tunnelLocation));
}
var checkJava = spawn('java');
checkJava.on('error', function (err) {
return callback(new Error('Java might not be installed, necessary to use testingbot-tunnel ' + err.message));
});
var onReady = function onReady() {
started = true;
logger('Tunnel is ready');
callback(null, activeTunnel);
};
var onError = function onError(waitCounter) {
var errorMessage = 'Tunnel failed to launch in ' + waitCounter + ' seconds.';
logger(errorMessage);
callback(new Error(errorMessage), null);
};
var readyFile = path.join(os.tmpdir(), 'testingbot.ready');
function checkReadyFile() {
var waitCounter = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;
fs.access(readyFile, fs.constants.F_OK, function (error) {
if (!error) {
clearInterval(readyFileChecker);
return onReady();
}
if (waitCounter > 90) {
clearInterval(readyFileChecker);
return onError(waitCounter);
}
waitCounter += 1;
});
}
var readyFileChecker = setInterval(function () {
return checkReadyFile();
}, 1000);
var args = createArgs(options);
try {
if (fs.statSync(readyFile).isFile()) {
logger('Tunnel Readyfile already exists, removing');
fs.unlinkSync(readyFile);
}
} catch (ignore) {}
args.push('-f');
args.push(readyFile);
if (options.verbose) {
logger('Starting tunnel with options', args);
}
activeTunnel = spawn('java', args, {});
activeTunnel.stderr.on('data', function (data) {
data = data.toString().trim();
if (options.verbose && data !== '') {
logger(data);
}
if (data.indexOf('is available for download') > -1) {
logger(data);
}
if (data.indexOf('401 Unauthorized') > -1) {
activeTunnel.error = 'Invalid credentials. Please supply the correct key/secret obtained from TestingBot.com';
activeTunnel.close();
} else if (data.indexOf('minutes left') > -1) {
activeTunnel.error = 'You do not have any minutes left. Please upgrade your account at TestingBot.com';
activeTunnel.close();
}
});
activeTunnel.stdout.on('data', function (data) {
data = data.toString().trim();
if (options.verbose && data !== '') {
logger(data);
}
});
activeTunnel.close = function (closeCallback) {
if (closeCallback) {
if (!activeTunnel) {
closeCallback();
} else {
activeTunnel.on('close', function () {
closeCallback();
});
}
}
activeTunnel.kill('SIGINT');
};
activeTunnel.on('exit', function (code, signal) {
if (options.verbose) {
logger('Closing TestingBot Tunnel');
}
if (!started) {
callback(new Error(activeTunnel.error ? activeTunnel.error : 'Could not start TestingBot Tunnel. Exit code ' + code + ' signal: ' + signal));
}
started = false;
activeTunnel = null;
});
}
function killTunnel(callback) {
if (!callback) {
callback = function callback() {};
}
if (!activeTunnel) {
return callback(new Error('no active tunnel'));
}
activeTunnel.kill('SIGINT');
callback(null);
}
function downloadAndRun(options, callback) {
if (!options) {
options = {};
}
if (!callback) {
callback = function callback() {};
}
async.waterfall([async.apply(download, options), async.apply(run, options)], callback);
}
module.exports = downloadAndRun;
module.exports.kill = killTunnel;
module.exports.createArgs = createArgs;