'use strict';

Object.defineProperty(exports, "__esModule", {
    value: true
});
exports.ExecutorDocker = undefined;

var _regenerator = require('babel-runtime/regenerator');

var _regenerator2 = _interopRequireDefault(_regenerator);

var _promise = require('babel-runtime/core-js/promise');

var _promise2 = _interopRequireDefault(_promise);

var _asyncToGenerator2 = require('babel-runtime/helpers/asyncToGenerator');

var _asyncToGenerator3 = _interopRequireDefault(_asyncToGenerator2);

var _getPrototypeOf = require('babel-runtime/core-js/object/get-prototype-of');

var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf);

var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck');

var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);

var _createClass2 = require('babel-runtime/helpers/createClass');

var _createClass3 = _interopRequireDefault(_createClass2);

var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn');

var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2);

var _inherits2 = require('babel-runtime/helpers/inherits');

var _inherits3 = _interopRequireDefault(_inherits2);

var _Executor2 = require('../agent/Executor');

var _AgentUtils = require('../agent/AgentUtils');

var _dockerode = require('dockerode');

var _dockerode2 = _interopRequireDefault(_dockerode);

var _stream = require('stream');

var _stream2 = _interopRequireDefault(_stream);

var _fs = require('fs');

var _fs2 = _interopRequireDefault(_fs);

var _appRootPath = require('app-root-path');

var _appRootPath2 = _interopRequireDefault(_appRootPath);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

var log = _AgentUtils.AgentUtils.logger();

/**
 * Sample .githubTaskManager.json task config
 *
 * see: https://github.com/wyvern8/github-task-manager/wiki/Structure-of-.githubTaskManager.json
 *
 {
   "executor": "Docker",
   "context": "run ls in latest alpine",
   "options": {
     "image": "alpine:latest",
     "command": ["/bin/ls", "-ltr", "/bin"],
     "env: [
        "myvar=myval",
        "var2=val2"
     ]
   }
 }
 */

var ExecutorDocker = exports.ExecutorDocker = function (_Executor) {
    (0, _inherits3.default)(ExecutorDocker, _Executor);

    function ExecutorDocker(eventData) {
        (0, _classCallCheck3.default)(this, ExecutorDocker);

        var _this = (0, _possibleConstructorReturn3.default)(this, (ExecutorDocker.__proto__ || (0, _getPrototypeOf2.default)(ExecutorDocker)).call(this, eventData));

        _this.options = _this.getOptions();
        return _this;
    }

    (0, _createClass3.default)(ExecutorDocker, [{
        key: 'validateImage',
        value: function validateImage(image) {
            var valid = false;
            var imageList = this.options.GTM_DOCKER_IMAGE_WHITELIST ? this.options.GTM_DOCKER_IMAGE_WHITELIST.split(',') : [];

            imageList = this.options.GTM_DOCKER_IMAGE_WHITELIST_FILE ? _fs2.default.readFileSync(_appRootPath2.default + '/' + this.options.GTM_DOCKER_IMAGE_WHITELIST_FILE, 'utf-8').toString().split('\n') : imageList;

            if (imageList && imageList.length > 0) {
                imageList.forEach(function (imagePattern) {
                    var pattern = new RegExp(imagePattern.trim());
                    if (pattern.test(image)) {
                        log.info('matched whitelist image pattern \'' + imagePattern.trim() + '\'');
                        valid = true;
                    }
                });
            }
            return valid;
        }
    }, {
        key: 'executeTask',
        value: function () {
            var _ref = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee(task) {
                var image, command, env, message, resultSummary, _message, _resultSummary, docker, containerLogs, pullImage;

                return _regenerator2.default.wrap(function _callee$(_context) {
                    while (1) {
                        switch (_context.prev = _context.next) {
                            case 0:
                                pullImage = function pullImage(image) {
                                    return new _promise2.default(function (resolve, reject) {
                                        docker.pull(image, function (err, stream) {
                                            if (err) {
                                                return reject(err);
                                            }

                                            docker.modem.followProgress(stream, onFinished, onProgress);

                                            function onFinished(err, output) {
                                                if (err) {
                                                    return reject(err);
                                                }
                                                return resolve(output);
                                            }

                                            function onProgress(event) {
                                                log.info(event);
                                            }
                                        });
                                    });
                                };

                                containerLogs = function containerLogs(container) {
                                    var logBuffer = [];

                                    // create a single stream for stdin and stdout
                                    var logStream = new _stream2.default.PassThrough();
                                    logStream.on('data', function (chunk) {
                                        logBuffer.push(chunk.toString('utf8')); // todo find a better way
                                        if (logBuffer.length % 100 === 0) {
                                            log.info(logBuffer.reverse().join(''));
                                            logBuffer = [];
                                        }
                                    });

                                    container.logs({
                                        follow: true,
                                        stdout: true,
                                        stderr: true
                                    }, function (err, stream) {
                                        if (err) {
                                            return log.error(err.message);
                                        }
                                        container.modem.demuxStream(stream, logStream, logStream);
                                        stream.on('end', function () {
                                            log.info(logBuffer.reverse().join(''));
                                            logBuffer = [];
                                            logStream.end('!stop!');
                                        });
                                    });
                                };

                                image = task.options.image;
                                command = task.options.command;
                                env = task.options.env || [];

                                if (!(command && (!this.options.GTM_DOCKER_COMMANDS_ALLOWED || this.options.GTM_DOCKER_COMMANDS_ALLOWED !== 'true'))) {
                                    _context.next = 11;
                                    break;
                                }

                                message = 'docker image commands are not allowed with the current configuration.';

                                log.error(message);
                                resultSummary = {
                                    passed: false,
                                    url: 'https://github.com/apocas/dockerode',
                                    message: message
                                };


                                task.results = resultSummary;

                                return _context.abrupt('return', _promise2.default.reject(task));

                            case 11:
                                if (this.validateImage(image)) {
                                    _context.next = 17;
                                    break;
                                }

                                _message = 'image \'' + image + '\' is not whitelisted.';

                                log.error(_message);
                                _resultSummary = {
                                    passed: false,
                                    url: 'https://github.com/apocas/dockerode',
                                    message: _message
                                };


                                task.results = _resultSummary;

                                return _context.abrupt('return', _promise2.default.reject(task));

                            case 17:

                                log.info('Starting local docker container \'' + image + '\' to run: ' + command.join(' '));

                                docker = new _dockerode2.default();

                                /**
                                 * Get logs from running container
                                 */

                                return _context.abrupt('return', pullImage(image).then(function () {
                                    return docker.createContainer({
                                        Image: image,
                                        Cmd: command,
                                        Env: env
                                    });
                                }).then(function (container) {
                                    return container.start({});
                                }).then(function (container) {
                                    return containerLogs(container);
                                }).then(function () {
                                    var resultSummary = {
                                        passed: true,
                                        url: 'https://github.com/apocas/dockerode',
                                        message: 'execution completed.'
                                    };

                                    task.results = resultSummary;

                                    return _promise2.default.resolve(task); // todo handle results
                                }).catch(function (e) {
                                    log.error(e.message);
                                    var resultSummary = {
                                        passed: false,
                                        url: 'https://github.com/apocas/dockerode',
                                        message: e.message
                                    };

                                    task.results = resultSummary;
                                    return _promise2.default.reject(task);
                                }));

                            case 20:
                            case 'end':
                                return _context.stop();
                        }
                    }
                }, _callee, this);
            }));

            function executeTask(_x) {
                return _ref.apply(this, arguments);
            }

            return executeTask;
        }()
    }]);
    return ExecutorDocker;
}(_Executor2.Executor);

_Executor2.Executor.register('Docker', ExecutorDocker);