UNPKG

ysb-sauce-tunnel

Version:

A wrapper around the Sauce Labs tunnel jar

162 lines (143 loc) 4.18 kB
/* npm */ var chalk = require('chalk'); var request = require('request').defaults({jar:false}); var split = require('split'); /* core */ var util = require('util'); var os = require('os'); var path = require('path'); var proc = require('child_process'); var EventEmitter = require('events').EventEmitter; var binaries = { 'darwin': 'sc', 'linux': 'sc', 'linux32': 'sc', 'win32': 'sc.exe' }; module.exports = SauceTunnel; function SauceTunnel(user, key, identifier, tunneled, extraFlags) { EventEmitter.call(this); this.user = user; this.key = key; this.identifier = identifier || 'Tunnel'+new Date().getTime(); this.tunneled = (tunneled == null) ? true : tunneled; this.baseUrl = ["https://", this.user, ':', this.key, '@saucelabs.com', '/rest/v1/', this.user].join(""); this.extraFlags = extraFlags; this.id = null; } util.inherits(SauceTunnel, EventEmitter); SauceTunnel.prototype.openTunnel = function(callback) { var me = this; // win32, darwin or linux var platform = os.platform(); // Special case: 32bit linux? platform += (platform === 'linux' && os.arch() === 'ia32') ? '32' : ''; var executable = binaries[platform]; if (!executable) { console.log("Platform:"+platform+" is not supported"); throw new Error(platform + ' platform is not supported'); } var args = ['-u', this.user, '-k', this.key]; if (this.identifier) { args.push("-i", this.identifier); } if (this.extraFlags) { args = args.concat(this.extraFlags); } var cmd = path.join(__dirname, 'vendor', platform, 'bin/', executable); console.log("CMD:"+cmd); console.log("ARGS:"+args); this.proc = proc.spawn(cmd, args,{ shell: true }); callback.called = false; this.proc.stdout.pipe(split()).on('data', function(data) { if (!data.match(/^\[-u,/g)) { console.log("DATA:"+data); me.emit('verbose:debug', data); } if (data.match(/Sauce Connect is up, you may start your tests/)) { me.emit('verbose:ok', '=> Sauce Labs Tunnel established'); if (!callback.called) { callback.called = true; callback(true); } } var match = data.match(/Tunnel ID\: ([a-z0-9]{32})/); if (match) { me.id = match[1]; } }); this.proc.stderr.pipe(split()).on('data', function(data) { me.emit('log:error', data); }); var self = this; this.proc.on('exit', function(code) { me.emit('verbose:ok', 'Sauce Labs Tunnel disconnected ', code); if (!callback.called) { callback.called = true; callback(false); } }); }; SauceTunnel.prototype.getTunnels = function(callback) { request({ url: this.baseUrl + '/tunnels', json: true }, function(err, resp, body) { callback(body); }); }; SauceTunnel.prototype.killTunnel = function(callback) { if (!this.tunneled) { return callback(); } this.emit('verbose:debug', 'Trying to kill tunnel'); request({ method: "DELETE", url: this.baseUrl + "/tunnels/" + this.id, json: true }, function (err, resp, body) { if (!err && resp.statusCode === 200) { this.emit('verbose:debug', 'Tunnel Closed'); } else { this.emit('log:error', 'Error closing tunnel'); } callback(err); }.bind(this)); }; SauceTunnel.prototype.start = function(callback) { var me = this; if (!this.tunneled) { return callback(true); } this.emit('verbose:writeln', chalk.inverse("=> Sauce Labs trying to open tunnel")); this.openTunnel(function(status) { callback(status); }); }; SauceTunnel.prototype.stop = function (callback) { var self = this, callbackArg; this.proc.on('exit', function () { callback(callbackArg); }); this.killTunnel(function (err) { // When deleting the tunnel via the REST API succeeds, then Sauce Connect exits automatically. // Otherwise kill the process. Don't care with the tunnel, it will time out. if (err) { callbackArg = err; self.proc.kill(); } }); }; SauceTunnel.prototype.kill = function(callback) { if (this.proc) { this.proc.on('exit', function () { callback(); }); this.proc.kill(); } else { callback(); } };