terra-toolkit
Version:
Utilities to help when developing terra modules.
362 lines (303 loc) • 11 kB
JavaScript
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
var _child_process = require("child_process");
var _retry = _interopRequireDefault(require("async/retry"));
var _http = _interopRequireDefault(require("http"));
var _path = _interopRequireDefault(require("path"));
var _services = _interopRequireDefault(require("../../../config/wdio/services.default-config"));
var _logger = _interopRequireDefault(require("../../../scripts/utils/logger"));
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) { symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); } keys.push.apply(keys, symbols); } return keys; }
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { (0, _defineProperty2.default)(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
var context = '[Terra-Toolkit:selenium-docker]';
/**
* Webdriver.io SeleniumDockerService
* provides standalone chrome selenium docker automation.
*/
var SeleniumDockerService = /*#__PURE__*/function () {
function SeleniumDockerService() {
(0, _classCallCheck2.default)(this, SeleniumDockerService);
this.getSeleniumStatus = this.getSeleniumStatus.bind(this);
}
/**
* Start up docker container before all workers get launched.
* @param {Object} config wdio configuration object
* @param {Array.<Object>} capabilities list of capabilities details
*/
(0, _createClass2.default)(SeleniumDockerService, [{
key: "onPrepare",
value: function () {
var _onPrepare = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee(config) {
var dockerInfo;
return _regenerator.default.wrap(function _callee$(_context) {
while (1) {
switch (_context.prev = _context.next) {
case 0:
this.config = _objectSpread(_objectSpread({}, _services.default.seleniumDocker), config.seleniumDocker || {});
this.host = config.host;
this.port = config.port;
this.path = config.path;
if (!this.config.enabled) {
_context.next = 22;
break;
}
_context.next = 7;
return this.getDockerInfo();
case 7:
dockerInfo = _context.sent;
if (!(dockerInfo.Swarm.LocalNodeState !== 'active')) {
_context.next = 11;
break;
}
_context.next = 11;
return this.initSwarm();
case 11:
_context.next = 13;
return this.getStack();
case 13:
if (!_context.sent) {
_context.next = 18;
break;
}
_context.next = 16;
return this.removeStack();
case 16:
_context.next = 18;
return this.ensureNetworkRemoved();
case 18:
_context.next = 20;
return this.deployStack();
case 20:
_context.next = 22;
return this.ensureSelenium();
case 22:
case "end":
return _context.stop();
}
}
}, _callee, this);
}));
function onPrepare(_x) {
return _onPrepare.apply(this, arguments);
}
return onPrepare;
}()
/**
* Clean up docker container after all workers got shut down and the process is about to exit.
*/
}, {
key: "onComplete",
value: function () {
var _onComplete = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee2() {
return _regenerator.default.wrap(function _callee2$(_context2) {
while (1) {
switch (_context2.prev = _context2.next) {
case 0:
if (!this.config.enabled) {
_context2.next = 5;
break;
}
_context2.next = 3;
return this.removeStack();
case 3:
_context2.next = 5;
return this.ensureNetworkRemoved();
case 5:
case "end":
return _context2.stop();
}
}
}, _callee2, this);
}));
function onComplete() {
return _onComplete.apply(this, arguments);
}
return onComplete;
}()
/**
* Waits for selenium to startup and be ready within the configured container.
* @return {Promise}
*/
}, {
key: "ensureSelenium",
value: function ensureSelenium() {
var _this = this;
return new Promise(function (resolve, reject) {
_logger.default.log('Ensuring selenium status is ready', {
context: context
});
(0, _retry.default)({
times: _this.config.retries,
interval: _this.config.retryInterval
}, _this.getSeleniumStatus, function (err, result) {
if (err) {
reject(_logger.default.error(JSON.stringify(err), {
context: context
}));
} else {
resolve(result);
}
});
});
}
/**
* Gets the stack information.
* @return {Promise} which resolves to a string representing the stack, or null if none exists.
*/
}, {
key: "getStack",
value: function getStack() {
return new Promise(function (resolve) {
(0, _child_process.exec)('docker stack ls | grep wdio', function (error, stdout) {
resolve(stdout);
});
});
}
/**
* Gets information about the docker environment.
* @return {Promise} which resolves to a JSON object describing the docker environment.
*/
}, {
key: "getDockerInfo",
value: function getDockerInfo() {
return this.execute('docker info --format "{{json .}}"').then(function (result) {
return JSON.parse(result);
});
}
/**
* Gets the stack default network.
* @return {Promise} which resolves to a string representing the network, or null if none exists.
*/
}, {
key: "getNetwork",
value: function getNetwork() {
return this.execute('docker network ls --filter name=wdio --format "{{.ID}}: {{.Driver}}"');
}
/**
* Initializes the docker swarm. See https://docs.docker.com/engine/reference/commandline/swarm_init/#related-commands
* @return {Promise}
*/
}, {
key: "initSwarm",
value: function initSwarm() {
_logger.default.log('Initializing docker swarm', {
context: context
});
return this.execute('docker swarm init');
}
/**
* Deploys the docker selenium hub stack
* @return {Promise}
*/
}, {
key: "deployStack",
value: function deployStack() {
_logger.default.log('Deploying docker selenium stack', {
context: context
});
return this.execute("docker stack deploy --compose-file ".concat(this.config.composeFile, " wdio"));
}
/**
* Stops the docker stack
* @return {Promise}
*/
}, {
key: "removeStack",
value: function removeStack() {
_logger.default.log('Removing docker selenium stack', {
context: context
});
return this.execute('docker stack rm wdio');
}
/**
* Executes an arbitrary command and returns a promise.
* @param {String} command - The command to execute
* @return {Promise}
*/
}, {
key: "execute",
value: function execute(command) {
return new Promise(function (resolve, reject) {
(0, _child_process.exec)(command, function (error, stdout) {
if (error) {
reject(error);
} else {
resolve(stdout);
}
});
});
}
/**
* Ensures the stack default network is removed.
* @return {Promise}
*/
}, {
key: "ensureNetworkRemoved",
value: function ensureNetworkRemoved() {
var _this2 = this;
return new Promise(function (resolve, reject) {
(0, _retry.default)({
times: 1000,
interval: 10
}, function (callback) {
// If there is a network, it will register as an error in the callback
_this2.getNetwork().then(callback).catch(callback);
}, function (err, result) {
if (err) {
reject(err);
} else {
resolve(result);
}
});
});
}
/**
* Gets the status of the selenium server.
* @param {function} callback taking (err, result).
*/
}, {
key: "getSeleniumStatus",
value: function getSeleniumStatus(callback) {
_http.default.get({
host: this.host,
port: this.port,
path: _path.default.posix.join(this.path || '/wd/hub', 'status')
}, function (res) {
var statusCode = res.statusCode;
if (statusCode !== 200) {
callback('Request failed');
return;
}
res.setEncoding('utf8');
var rawData = '';
res.on('data', function (chunk) {
rawData += chunk;
});
res.on('end', function () {
try {
var status = JSON.parse(rawData);
if (status.value && status.value.ready) {
callback(null, status);
} else {
callback(status);
}
} catch (e) {
callback("Request failed: ".concat(e.message));
}
});
}).on('error', function (e) {
callback("Request failed: ".concat(e.message));
});
}
}]);
return SeleniumDockerService;
}();
exports.default = SeleniumDockerService;