From 1445c62ed15e97a8fedc678eb32980cf4b4fd27d Mon Sep 17 00:00:00 2001 From: Jeffrey Morgan Date: Sun, 15 Feb 2015 16:54:21 -0800 Subject: [PATCH] Setup fixes & unit tests --- __tests__/README.md | 1 + __tests__/SetupStore-test.js | 25 +++++++--- browser/main.js | 1 + resources/cocoasudo | Bin 47704 -> 0 bytes resources/macsudo | Bin 0 -> 13916 bytes src/ContainerStore.js | 90 ++++++++++------------------------- src/Setup.react.js | 2 +- src/SetupStore.js | 34 +++++++------ src/SetupUtil.js | 11 +++++ 9 files changed, 75 insertions(+), 89 deletions(-) create mode 100644 __tests__/README.md delete mode 100755 resources/cocoasudo create mode 100755 resources/macsudo diff --git a/__tests__/README.md b/__tests__/README.md new file mode 100644 index 0000000000..02f5488031 --- /dev/null +++ b/__tests__/README.md @@ -0,0 +1 @@ +### Manual tests diff --git a/__tests__/SetupStore-test.js b/__tests__/SetupStore-test.js index ee4fc5f071..2df2918ce1 100644 --- a/__tests__/SetupStore-test.js +++ b/__tests__/SetupStore-test.js @@ -13,7 +13,6 @@ describe('SetupStore', function () { virtualBox.installed.mockReturnValue(false); setupUtil.download.mockReturnValue(Promise.resolve()); return setupStore.steps().download.run().then(() => { - // TODO: make sure download was called with the right args expect(setupUtil.download).toBeCalled(); }); }); @@ -30,26 +29,38 @@ describe('SetupStore', function () { }); describe('install step', function () { + util.exec.mockReturnValue(Promise.resolve()); + util.copyBinariesCmd.mockReturnValue('copycmd'); + util.fixBinariesCmd.mockReturnValue('fixcmd'); + virtualBox.killall.mockReturnValue(Promise.resolve()); + setupUtil.installVirtualBoxCmd.mockReturnValue('installvb'); + setupUtil.macSudoCmd.mockImplementation(cmd => 'macsudo ' + cmd); + pit('installs virtualbox if it is not installed', function () { virtualBox.installed.mockReturnValue(false); - virtualBox.killall.mockReturnValue(Promise.resolve()); util.exec.mockReturnValue(Promise.resolve()); return setupStore.steps().install.run().then(() => { - // TODO: make sure that the right install command was executed - expect(util.exec).toBeCalled(); + expect(virtualBox.killall).toBeCalled(); + expect(util.exec).toBeCalledWith('macsudo copycmd && fixcmd && installvbcmd'); }); }); pit('installs virtualbox if it is installed but has an outdated version', function () { virtualBox.installed.mockReturnValue(true); virtualBox.version.mockReturnValue(Promise.resolve('4.3.16')); - virtualBox.killall.mockReturnValue(Promise.resolve()); setupUtil.compareVersions.mockReturnValue(-1); util.exec.mockReturnValue(Promise.resolve()); return setupStore.steps().install.run().then(() => { - // TODO: make sure the right install command was executed expect(virtualBox.killall).toBeCalled(); - expect(util.exec).toBeCalled(); + expect(util.exec).toBeCalledWith('macsudo copycmd && fixcmd && installvbcmd'); + }); + }); + + pit('only installs binaries if virtualbox is installed', function () { + virtualBox.installed.mockReturnValue(true); + setupUtil.compareVersions.mockReturnValue(0); + return setupStore.steps().install.run().then(() => { + expect(util.exec).toBeCalledWith('macsudo copycmd && fixcmd'); }); }); }); diff --git a/browser/main.js b/browser/main.js index 1a3a6a4852..04354c9407 100644 --- a/browser/main.js +++ b/browser/main.js @@ -16,6 +16,7 @@ try { process.env.NODE_PATH = __dirname + '/../node_modules'; process.env.RESOURCES_PATH = __dirname + '/../resources'; process.chdir(path.join(__dirname, '..')); +process.env.PATH = '/usr/local/bin:' + process.env.PATH; if (argv.integration) { process.env.TEST_TYPE = 'integration'; diff --git a/resources/cocoasudo b/resources/cocoasudo deleted file mode 100755 index ccf0bf8aa1979901080b7b8378ee966848ffc510..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 47704 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 literal 0 HcmV?d00001 diff --git a/src/ContainerStore.js b/src/ContainerStore.js index 1ec6b04b87..92fb1375aa 100644 --- a/src/ContainerStore.js +++ b/src/ContainerStore.js @@ -11,6 +11,7 @@ var ContainerUtil = require('./ContainerUtil'); var convert = new Convert(); var _recommended = []; +var _placeholders = {}; var _containers = {}; var _progress = {}; var _logs = {}; @@ -23,32 +24,8 @@ var ContainerStore = assign(Object.create(EventEmitter.prototype), { SERVER_CONTAINER_EVENT: 'server_container_event', SERVER_PROGRESS_EVENT: 'server_progress_event', SERVER_LOGS_EVENT: 'server_logs_event', - _pullScratchImage: function (callback) { - var image = docker.client().getImage('scratch:latest'); - image.inspect(function (err, data) { - if (!data) { - docker.client().pull('scratch:latest', function (err, stream) { - if (err) { - callback(err); - return; - } - stream.setEncoding('utf8'); - stream.on('data', function () {}); - stream.on('end', function () { - callback(); - }); - }); - } else { - callback(); - } - }); - }, _pullImage: function (repository, tag, callback, progressCallback) { registry.layers(repository, tag, function (err, layerSizes) { - - // TODO: Support v2 registry API - // TODO: clean this up- It's messy to work with pulls from both the v1 and v2 registry APIs - // Use the per-layer pull progress % to update the total progress. docker.client().listImages({all: 1}, function(err, images) { var existingIds = new Set(images.map(function (image) { return image.Id.slice(0, 12); @@ -162,29 +139,14 @@ var ContainerStore = assign(Object.create(EventEmitter.prototype), { }); }, _createPlaceholderContainer: function (imageName, name, callback) { - var self = this; - this._pullScratchImage(function (err) { - if (err) { - callback(err); - return; + if (_placeholders[name]) { + delete _placeholders[name]; + } + _placeholders[name] = { + State: { } - docker.client().createContainer({ - Image: 'scratch:latest', - Tty: false, - Env: [ - 'KITEMATIC_DOWNLOADING=true', - 'KITEMATIC_DOWNLOADING_IMAGE=' + imageName - ], - Cmd: 'placeholder', - name: name - }, function (err) { - if (err) { - callback(err); - return; - } - self.fetchContainer(name, callback); - }); - }); + }; + return _placeholders[name]; }, _generateName: function (repository) { var base = _.last(repository.split('/')); @@ -386,28 +348,24 @@ var ContainerStore = assign(Object.create(EventEmitter.prototype), { var self = this; var imageName = repository + ':' + tag; var containerName = this._generateName(repository); - // Pull image - self._createPlaceholderContainer(imageName, containerName, function (err, container) { - if (err) { - callback(err); - return; - } - _containers[containerName] = container; - self.emit(self.CLIENT_CONTAINER_EVENT, containerName, 'create'); - _muted[containerName] = true; - _progress[containerName] = 0; - self._pullImage(repository, tag, function () { - self._createContainer(containerName, {Image: imageName}, function () { - delete _progress[containerName]; - _muted[containerName] = false; - self.emit(self.CLIENT_CONTAINER_EVENT, containerName); - }); - }, function (progress) { - _progress[containerName] = progress; - self.emit(self.SERVER_PROGRESS_EVENT, containerName); + + // Create placeholder container + var container = this._createPlaceholderContainer(imageName, containerName); + _containers[containerName] = container; + self.emit(self.CLIENT_CONTAINER_EVENT, containerName, 'create'); + _muted[containerName] = true; + _progress[containerName] = 0; + self._pullImage(repository, tag, function () { + self._createContainer(containerName, {Image: imageName}, function () { + delete _progress[containerName]; + _muted[containerName] = false; + self.emit(self.CLIENT_CONTAINER_EVENT, containerName); }); - callback(null, containerName); + }, function (progress) { + _progress[containerName] = progress; + self.emit(self.SERVER_PROGRESS_EVENT, containerName); }); + callback(null, containerName); }, updateContainer: function (name, data, callback) { _muted[name] = true; diff --git a/src/Setup.react.js b/src/Setup.react.js index bf8b430f64..ea48750a43 100644 --- a/src/Setup.react.js +++ b/src/Setup.react.js @@ -43,7 +43,7 @@ var Setup = React.createClass({ }, renderContents: function () { var img = 'virtualbox.png'; - if (SetupStore.step().name.indexOf('Boot2Docker') !== -1) { + if (SetupStore.step().name.indexOf('start') !== -1 || SetupStore.step().name.indexOf('init') !== -1) { img = 'boot2docker.png'; } return ( diff --git a/src/SetupStore.js b/src/SetupStore.js index d5af39b9d4..e94b39243c 100644 --- a/src/SetupStore.js +++ b/src/SetupStore.js @@ -1,6 +1,7 @@ var EventEmitter = require('events').EventEmitter; var _ = require('underscore'); var path = require('path'); +var fs = require('fs'); var Promise = require('bluebird'); var boot2docker = require('./Boot2Docker'); var virtualBox = require('./VirtualBox'); @@ -20,13 +21,12 @@ var _steps = [{ message: 'VirtualBox is being downloaded. Kitematic requires VirtualBox to run containers.', totalPercent: 35, percent: 0, - run: Promise.coroutine(function* (progressCallback) { + run: function (progressCallback) { var packagejson = util.packagejson(); - var virtualBoxFile = `https://github.com/kitematic/virtualbox/releases/download/${packagejson['virtualbox-version']}/${packagejson['virtualbox-filename']}`; - yield setupUtil.download(virtualBoxFile, path.join(util.supportDir(), packagejson['virtualbox-filename']), packagejson['virtualbox-checksum'], percent => { + return setupUtil.download(setupUtil.virtualBoxUrl(), path.join(util.supportDir(), packagejson['virtualbox-filename']), packagejson['virtualbox-checksum'], percent => { progressCallback(percent); }); - }) + } }, { name: 'install', title: 'Installing Docker & VirtualBox', @@ -34,16 +34,16 @@ var _steps = [{ totalPercent: 5, percent: 0, seconds: 5, - run: Promise.coroutine(function* () { + run: Promise.coroutine(function* (progressCallback) { var packagejson = util.packagejson(); - var base = util.copyBinariesCmd() + ' && ' + util.fixBinariesCmd(); + var cmd = util.copyBinariesCmd() + ' && ' + util.fixBinariesCmd(); if (!virtualBox.installed() || setupUtil.compareVersions(yield virtualBox.version(), packagejson['virtualbox-required-version']) < 0) { yield virtualBox.killall(); - base += ` && installer -pkg ${path.join(util.supportDir(), packagejson['virtualbox-filename'])} -target /`; + cmd += ' && ' + setupUtil.installVirtualBoxCmd(); } - var cmd = `${util.escapePath(path.join(util.resourceDir(), 'cocoasudo'))} --prompt="Kitematic requires administrative privileges to install VirtualBox." bash -c \"${base}\"`; try { - yield util.exec(cmd); + progressCallback(50); // TODO: detect when the installation has started so we can simulate progress + yield util.exec(setupUtil.macSudoCmd(cmd)); } catch (err) { throw null; } @@ -130,12 +130,16 @@ var SetupStore = assign(Object.create(EventEmitter.prototype), { _retryPromise = Promise.defer(); return _retryPromise.promise; }, - init: Promise.coroutine(function* () { + requiredSteps: Promise.coroutine(function* () { + if (_requiredSteps.length) { + return Promise.resolve(_requiredSteps); + } var packagejson = util.packagejson(); var isoversion = boot2docker.isoversion(); var required = {}; - required.download = !virtualBox.installed() || setupUtil.compareVersions(yield virtualBox.version(), packagejson['virtualbox-required-version']) < 0; - required.install = required.download || setupUtil.needsBinaryFix(); + var vboxfile = path.join(util.supportDir(), packagejson['virtualbox-filename']); + required.download = !virtualBox.installed() && (!fs.existsSync(vboxfile) || setupUtil.checksum(vboxfile) !== packagejson['virtualbox-checksum']); + required.install = !virtualBox.installed() || setupUtil.needsBinaryFix(); required.init = !(yield boot2docker.exists()) || !isoversion || setupUtil.compareVersions(isoversion, boot2docker.version()) < 0; required.start = required.init || (yield boot2docker.status()) !== 'running'; @@ -147,6 +151,7 @@ var SetupStore = assign(Object.create(EventEmitter.prototype), { _requiredSteps = _steps.filter(function (step) { return required[step.name]; }); + return Promise.resolve(_requiredSteps); }), updateBinaries: function () { if (setupUtil.needsBinaryFix()) { @@ -158,9 +163,9 @@ var SetupStore = assign(Object.create(EventEmitter.prototype), { return Promise.resolve(); }, run: Promise.coroutine(function* () { - yield this.init(); yield this.updateBinaries(); - for (let step of _requiredSteps) { + var steps = yield this.requiredSteps(); + for (let step of steps) { _currentStep = step; step.percent = 0; while (true) { @@ -176,7 +181,6 @@ var SetupStore = assign(Object.create(EventEmitter.prototype), { break; } catch (err) { if (err) { - console.log(err.stack); _error = err; this.emit(this.ERROR_EVENT); } else { diff --git a/src/SetupUtil.js b/src/SetupUtil.js index c18b0cab99..a2597bffd4 100644 --- a/src/SetupUtil.js +++ b/src/SetupUtil.js @@ -29,6 +29,17 @@ var SetupUtil = { this.checksum('/usr/local/bin/boot2docker') !== this.checksum(path.join(util.resourceDir(), 'boot2docker-' + packagejson['boot2docker-version'])) || this.checksum('/usr/local/bin/docker') !== this.checksum(path.join(util.resourceDir(), 'docker-' + packagejson['docker-version'])); }, + installVirtualBoxCmd: function () { + var packagejson = util.packagejson(); + return `installer -pkg ${util.escapePath(path.join(util.supportDir(), packagejson['virtualbox-filename']))} -target /`; + }, + virtualBoxUrl: function () { + var packagejson = util.packagejson(); + return `https://github.com/kitematic/virtualbox/releases/download/${packagejson['virtualbox-version']}/${packagejson['virtualbox-filename']}`; + }, + macSudoCmd: function (cmd) { + return `${util.escapePath(path.join(util.resourceDir(), 'macsudo'))} -p "Kitematic requires administrative privileges to install VirtualBox." sh -c \"${cmd}\"`; + }, simulateProgress: function (estimateSeconds, progress) { var times = _.range(0, estimateSeconds * 1000, 200); var timers = [];