Merge pull request #177 from kitematic/setup-cancel

Install docker and boot2docker with Kitematic
This commit is contained in:
Jeffrey Morgan 2015-02-13 10:36:14 -08:00
commit e8f87e1ebe
10 changed files with 162 additions and 107 deletions

View File

@ -1,3 +1,7 @@
language: node_js language: node_js
node_js: node_js:
- 0.10 - 0.10
cache:
directories:
- node_modules
- resources

View File

@ -12,7 +12,7 @@ describe('SetupStore', function () {
pit('downloads virtualbox if it is not installed', function () { pit('downloads virtualbox if it is not installed', function () {
virtualBox.installed.mockReturnValue(false); virtualBox.installed.mockReturnValue(false);
setupUtil.download.mockReturnValue(Promise.resolve()); setupUtil.download.mockReturnValue(Promise.resolve());
return setupStore.steps().downloadVirtualBox.run().then(() => { return setupStore.steps().download.run().then(() => {
// TODO: make sure download was called with the right args // TODO: make sure download was called with the right args
expect(setupUtil.download).toBeCalled(); expect(setupUtil.download).toBeCalled();
}); });
@ -23,30 +23,18 @@ describe('SetupStore', function () {
virtualBox.version.mockReturnValue(Promise.resolve('4.3.16')); virtualBox.version.mockReturnValue(Promise.resolve('4.3.16'));
setupUtil.compareVersions.mockReturnValue(-1); setupUtil.compareVersions.mockReturnValue(-1);
setupUtil.download.mockReturnValue(Promise.resolve()); setupUtil.download.mockReturnValue(Promise.resolve());
return setupStore.steps().downloadVirtualBox.run().then(() => { return setupStore.steps().download.run().then(() => {
expect(setupUtil.download).toBeCalled(); expect(setupUtil.download).toBeCalled();
}); });
}); });
pit('skips download if virtualbox is already installed', function () {
virtualBox.installed.mockReturnValue(true);
virtualBox.version.mockReturnValue(Promise.resolve('4.3.20'));
setupUtil.download.mockClear();
setupUtil.download.mockReturnValue(Promise.resolve());
setupUtil.compareVersions.mockReturnValue(1);
return setupStore.steps().downloadVirtualBox.run().then(() => {
expect(setupUtil.download).not.toBeCalled();
});
});
}); });
describe('install step', function () { describe('install step', function () {
pit('installs virtualbox if it is not installed', function () { pit('installs virtualbox if it is not installed', function () {
virtualBox.installed.mockReturnValue(false); virtualBox.installed.mockReturnValue(false);
virtualBox.killall.mockReturnValue(Promise.resolve()); virtualBox.killall.mockReturnValue(Promise.resolve());
setupUtil.isSudo.mockReturnValue(Promise.resolve(false));
util.exec.mockReturnValue(Promise.resolve()); util.exec.mockReturnValue(Promise.resolve());
return setupStore.steps().installVirtualBox.run().then(() => { return setupStore.steps().install.run().then(() => {
// TODO: make sure that the right install command was executed // TODO: make sure that the right install command was executed
expect(util.exec).toBeCalled(); expect(util.exec).toBeCalled();
}); });
@ -56,29 +44,14 @@ describe('SetupStore', function () {
virtualBox.installed.mockReturnValue(true); virtualBox.installed.mockReturnValue(true);
virtualBox.version.mockReturnValue(Promise.resolve('4.3.16')); virtualBox.version.mockReturnValue(Promise.resolve('4.3.16'));
virtualBox.killall.mockReturnValue(Promise.resolve()); virtualBox.killall.mockReturnValue(Promise.resolve());
setupUtil.isSudo.mockReturnValue(Promise.resolve(false));
setupUtil.compareVersions.mockReturnValue(-1); setupUtil.compareVersions.mockReturnValue(-1);
util.exec.mockReturnValue(Promise.resolve()); util.exec.mockReturnValue(Promise.resolve());
return setupStore.steps().installVirtualBox.run().then(() => { return setupStore.steps().install.run().then(() => {
// TODO: make sure the right install command was executed // TODO: make sure the right install command was executed
expect(virtualBox.killall).toBeCalled(); expect(virtualBox.killall).toBeCalled();
expect(util.exec).toBeCalled(); expect(util.exec).toBeCalled();
}); });
}); });
pit('skips install if virtualbox is already installed', function () {
virtualBox.installed.mockReturnValue(true);
virtualBox.version.mockReturnValue(Promise.resolve('4.3.20'));
setupUtil.isSudo.mockReturnValue(Promise.resolve(false));
setupUtil.compareVersions.mockReturnValue(-1);
util.exec.mockReturnValue(Promise.resolve());
return setupStore.steps().installVirtualBox.run().then(() => {
virtualBox.killall.mockClear();
util.exec.mockClear();
expect(virtualBox.killall).not.toBeCalled();
expect(util.exec).not.toBeCalled();
});
});
}); });
describe('init step', function () { describe('init step', function () {
@ -86,7 +59,7 @@ describe('SetupStore', function () {
pit('inintializes the boot2docker vm if it does not exist', function () { pit('inintializes the boot2docker vm if it does not exist', function () {
boot2docker.exists.mockReturnValue(Promise.resolve(false)); boot2docker.exists.mockReturnValue(Promise.resolve(false));
boot2docker.init.mockReturnValue(Promise.resolve()); boot2docker.init.mockReturnValue(Promise.resolve());
return setupStore.steps().initBoot2Docker.run().then(() => { return setupStore.steps().init.run().then(() => {
expect(boot2docker.init).toBeCalled(); expect(boot2docker.init).toBeCalled();
}); });
}); });
@ -98,7 +71,7 @@ describe('SetupStore', function () {
boot2docker.stop.mockReturnValue(Promise.resolve()); boot2docker.stop.mockReturnValue(Promise.resolve());
boot2docker.upgrade.mockReturnValue(Promise.resolve()); boot2docker.upgrade.mockReturnValue(Promise.resolve());
setupUtil.compareVersions.mockReturnValue(-1); setupUtil.compareVersions.mockReturnValue(-1);
return setupStore.steps().initBoot2Docker.run().then(() => { return setupStore.steps().init.run().then(() => {
boot2docker.init.mockClear(); boot2docker.init.mockClear();
expect(boot2docker.init).not.toBeCalled(); expect(boot2docker.init).not.toBeCalled();
expect(boot2docker.upgrade).toBeCalled(); expect(boot2docker.upgrade).toBeCalled();
@ -111,7 +84,7 @@ describe('SetupStore', function () {
boot2docker.status.mockReturnValue(false); boot2docker.status.mockReturnValue(false);
boot2docker.waitstatus.mockReturnValue(Promise.resolve()); boot2docker.waitstatus.mockReturnValue(Promise.resolve());
boot2docker.start.mockReturnValue(Promise.resolve()); boot2docker.start.mockReturnValue(Promise.resolve());
return setupStore.steps().startBoot2Docker.run().then(() => { return setupStore.steps().start.run().then(() => {
expect(boot2docker.start).toBeCalled(); expect(boot2docker.start).toBeCalled();
}); });
}); });

View File

@ -17,7 +17,7 @@ var _logs = {};
var _streams = {}; var _streams = {};
var _muted = {}; var _muted = {};
var ContainerStore = assign(EventEmitter.prototype, { var ContainerStore = assign(Object.create(EventEmitter.prototype), {
CLIENT_CONTAINER_EVENT: 'client_container_event', CLIENT_CONTAINER_EVENT: 'client_container_event',
CLIENT_RECOMMENDED_EVENT: 'client_recommended_event', CLIENT_RECOMMENDED_EVENT: 'client_recommended_event',
SERVER_CONTAINER_EVENT: 'server_container_event', SERVER_CONTAINER_EVENT: 'server_container_event',

View File

@ -8,8 +8,10 @@ var docker = require('./Docker');
var router = require('./router'); var router = require('./router');
var boot2docker = require('./boot2docker'); var boot2docker = require('./boot2docker');
var ContainerStore = require('./ContainerStore'); var ContainerStore = require('./ContainerStore');
var SetupStore = require('./ContainerStore'); var SetupStore = require('./SetupStore');
var settingsjson; var settingsjson;
var Menu = require('./Menu');
try { try {
settingsjson = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'settings.json'), 'utf8')); settingsjson = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'settings.json'), 'utf8'));
} catch (err) { } catch (err) {
@ -34,6 +36,7 @@ bugsnag.appVersion = app.getVersion();
router.run(Handler => React.render(<Handler/>, document.body)); router.run(Handler => React.render(<Handler/>, document.body));
if (!window.location.hash.length || window.location.hash === '#/') { if (!window.location.hash.length || window.location.hash === '#/') {
SetupStore.run().then(boot2docker.ip).then(ip => { SetupStore.run().then(boot2docker.ip).then(ip => {
console.log(ip);
docker.setHost(ip); docker.setHost(ip);
ContainerStore.init(function (err) { ContainerStore.init(function (err) {
if (err) { console.log(err); } if (err) { console.log(err); }

View File

@ -1,8 +1,11 @@
var remote = require('remote'); var remote = require('remote');
var app = remote.require('app'); var app = remote.require('app');
var path = require('path');
var docker = require('./Docker');
var Menu = remote.require('menu'); var Menu = remote.require('menu');
var BrowserWindow = remote.require('browser-window'); var BrowserWindow = remote.require('browser-window');
var router = require('./Router'); var router = require('./Router');
var util = require('./Util');
// main.js // main.js
var template = [ var template = [
@ -59,6 +62,28 @@ var template = [
}, },
] ]
}, },
{
label: 'File',
submenu: [
{
label: 'New Container',
accelerator: 'Command+N',
selector: 'undo:'
},
{
type: 'separator'
},
{
label: 'Open Docker Terminal',
accelerator: 'Command+Shift+T',
click: function() {
var terminal = path.join(process.cwd(), 'resources', 'terminal');
var cmd = [terminal, `DOCKER_HOST=${'tcp://' + docker.host + ':2376'} DOCKER_CERT_PATH=${path.join(process.env[(process.platform === 'win32') ? 'USERPROFILE' : 'HOME'], '.boot2docker/certs/boot2docker-vm')} DOCKER_TLS_VERIFY=1 $SHELL`];
util.exec(cmd).then(() => {});
}
},
]
},
{ {
label: 'Edit', label: 'Edit',
submenu: [ submenu: [

View File

@ -83,7 +83,7 @@ var Setup = React.createClass({
<div className="content"> <div className="content">
<h4>Installation Cancelled</h4> <h4>Installation Cancelled</h4>
<h1>Couldn&#39;t Install VirtualBox</h1> <h1>Couldn&#39;t Install VirtualBox</h1>
<p>Kitematic did not receive the administrative privileges required to install VirtualBox.</p> <p>Kitematic did not receive the administrative privileges required to install VirtualBox &amp; Docker.</p>
<p>Please retry or download &amp; install VirutalBox manually from the <a onClick={this.handleOpenWebsite}>official Oracle website</a>.</p> <p>Please retry or download &amp; install VirutalBox manually from the <a onClick={this.handleOpenWebsite}>official Oracle website</a>.</p>
<button className="btn btn-action" onClick={this.handleRetry}>Retry</button> <button className="btn btn-action" onClick={this.handleRetry}>Retry</button>
</div> </div>

View File

@ -1,5 +1,4 @@
var EventEmitter = require('events').EventEmitter; var EventEmitter = require('events').EventEmitter;
var assign = require('object-assign');
var _ = require('underscore'); var _ = require('underscore');
var path = require('path'); var path = require('path');
var Promise = require('bluebird'); var Promise = require('bluebird');
@ -7,70 +6,60 @@ var boot2docker = require('./Boot2Docker');
var virtualBox = require('./VirtualBox'); var virtualBox = require('./VirtualBox');
var setupUtil = require('./SetupUtil'); var setupUtil = require('./SetupUtil');
var util = require('./Util'); var util = require('./Util');
var assign = require('object-assign');
var SUDO_PROMPT = 'Kitematic requires administrative privileges to install VirtualBox.';
var _currentStep = null; var _currentStep = null;
var _error = null; var _error = null;
var _cancelled = false; var _cancelled = false;
var _retryPromise = null; var _retryPromise = null;
var _requiredSteps = [];
var _steps = [{ var _steps = [{
name: 'downloadVirtualBox', name: 'download',
title: 'Downloading VirtualBox', title: 'Downloading VirtualBox',
message: 'VirtualBox is being downloaded. Kitematic requires VirtualBox to run containers.', message: 'VirtualBox is being downloaded. Kitematic requires VirtualBox to run containers.',
totalPercent: 35, totalPercent: 35,
percent: 0, percent: 0,
run: Promise.coroutine(function* (progressCallback) { run: Promise.coroutine(function* (progressCallback) {
var packagejson = util.packagejson(); var packagejson = util.packagejson();
if (virtualBox.installed()) {
var version = yield virtualBox.version();
if (setupUtil.compareVersions(version, packagejson['virtualbox-required-version']) >= 0) {
return;
}
}
var virtualBoxFile = `https://github.com/kitematic/virtualbox/releases/download/${packagejson['virtualbox-version']}/${packagejson['virtualbox-filename']}`; var virtualBoxFile = `https://github.com/kitematic/virtualbox/releases/download/${packagejson['virtualbox-version']}/${packagejson['virtualbox-filename']}`;
yield setupUtil.download(virtualBoxFile, path.join(setupUtil.supportDir(), packagejson['virtualbox-filename']), packagejson['virtualbox-checksum'], percent => { yield setupUtil.download(virtualBoxFile, path.join(util.supportDir(), packagejson['virtualbox-filename']), packagejson['virtualbox-checksum'], percent => {
progressCallback(percent); progressCallback(percent);
}); });
}) })
}, { }, {
name: 'installVirtualBox', name: 'install',
title: 'Installing VirtualBox', title: 'Installing Docker & VirtualBox',
message: "VirtualBox is being installed in the background. We may need you to type in your password to continue.", message: 'VirtualBox is being installed in the background. We may need you to type in your password to continue.',
totalPercent: 5, totalPercent: 5,
percent: 0, percent: 0,
seconds: 5, seconds: 5,
run: Promise.coroutine(function* () { run: Promise.coroutine(function* () {
var packagejson = util.packagejson(); var packagejson = util.packagejson();
if (virtualBox.installed()) { var base = util.copyBinariesCmd() + ' && ' + util.fixBinariesCmd();
var version = yield virtualBox.version(); if (!virtualBox.installed() || setupUtil.compareVersions(yield virtualBox.version(), packagejson['virtualbox-required-version']) < 0) {
if (setupUtil.compareVersions(version, packagejson['virtualbox-required-version']) >= 0) {
return;
}
yield virtualBox.killall(); yield virtualBox.killall();
base += ` && installer -pkg ${path.join(util.supportDir(), packagejson['virtualbox-filename'])} -target /`;
} }
var isSudo = yield setupUtil.isSudo(); var cmd = `${util.escapePath(path.join(util.resourceDir(), 'cocoasudo'))} --prompt="Kitematic requires administrative privileges to install VirtualBox." bash -c \"${base}\"`;
var iconPath = path.join(setupUtil.resourceDir(), 'kitematic.icns');
var sudoCmd = isSudo ? ['sudo'] : [path.join(setupUtil.resourceDir(), 'cocoasudo'), '--icon=' + iconPath, `--prompt=${SUDO_PROMPT}`];
sudoCmd.push.apply(sudoCmd, ['installer', '-pkg', path.join(setupUtil.supportDir(), packagejson['virtualbox-filename']), '-target', '/']);
try { try {
yield util.exec(sudoCmd); yield util.exec(cmd);
} catch (err) { } catch (err) {
throw null; throw null;
} }
}) })
}, { }, {
name: 'initBoot2Docker', name: 'init',
title: 'Setting up Docker VM', title: 'Setting up Docker VM',
message: "To run Docker containers on your computer, we are setting up a Linux virtual machine provided by boot2docker.", message: 'To run Docker containers on your computer, we are setting up a Linux virtual machine provided by boot2docker.',
totalPercent: 15, totalPercent: 15,
percent: 0, percent: 0,
seconds: 11, seconds: 11,
run: Promise.coroutine(function* (progressCallback) { run: Promise.coroutine(function* (progressCallback) {
setupUtil.simulateProgress(this.seconds, progressCallback);
yield virtualBox.vmdestroy('kitematic-vm'); yield virtualBox.vmdestroy('kitematic-vm');
var exists = yield boot2docker.exists(); var exists = yield boot2docker.exists();
if (!exists) { if (!exists) {
setupUtil.simulateProgress(this.seconds, progressCallback);
yield boot2docker.init(); yield boot2docker.init();
return; return;
} }
@ -81,21 +70,20 @@ var _steps = [{
var isoversion = boot2docker.isoversion(); var isoversion = boot2docker.isoversion();
if (!isoversion || setupUtil.compareVersions(isoversion, boot2docker.version()) < 0) { if (!isoversion || setupUtil.compareVersions(isoversion, boot2docker.version()) < 0) {
setupUtil.simulateProgress(this.seconds, progressCallback);
yield boot2docker.stop(); yield boot2docker.stop();
yield boot2docker.upgrade(); yield boot2docker.upgrade();
} }
}) })
}, { }, {
name: 'startBoot2Docker', name: 'start',
title: 'Starting Docker VM', title: 'Starting Docker VM',
message: "Kitematic is starting the boot2docker VM. This may take about a minute.", message: "Kitematic is starting the boot2docker VM. This may take about a minute.",
totalPercent: 45, totalPercent: 45,
percent: 0, percent: 0,
seconds: 35, seconds: 35,
run: function (progressCallback) { run: function (progressCallback) {
setupUtil.simulateProgress(this.seconds, progressCallback);
return boot2docker.waitstatus('saving').then(boot2docker.status).then(status => { return boot2docker.waitstatus('saving').then(boot2docker.status).then(status => {
setupUtil.simulateProgress(this.seconds, progressCallback);
if (status !== 'running') { if (status !== 'running') {
return boot2docker.start(); return boot2docker.start();
} }
@ -103,28 +91,29 @@ var _steps = [{
} }
}]; }];
var SetupStore = assign(EventEmitter.prototype, { var SetupStore = assign(Object.create(EventEmitter.prototype), {
PROGRESS_EVENT: 'setup_progress', PROGRESS_EVENT: 'setup_progress',
STEP_EVENT: 'setup_step', STEP_EVENT: 'setup_step',
ERROR_EVENT: 'setup_error', ERROR_EVENT: 'setup_error',
step: function () { step: function () {
return _currentStep || _steps[0]; return _currentStep;
}, },
steps: function () { steps: function () {
return _.indexBy(_steps, 'name'); return _.indexBy(_steps, 'name');
}, },
stepCount: function () { stepCount: function () {
return _steps.length; return _requiredSteps.length;
}, },
number: function () { number: function () {
return _.indexOf(_steps, _currentStep) + 1; return _.indexOf(_requiredSteps, _currentStep) + 1;
}, },
percent: function () { percent: function () {
var total = 0; var sofar = 0;
_.each(_steps, step => { var totalPercent = _requiredSteps.reduce((prev, step) => prev + step.totalPercent, 0);
total += step.totalPercent * step.percent / 100; _.each(_requiredSteps, step => {
sofar += step.totalPercent * step.percent / 100;
}); });
return Math.min(Math.round(total), 99); return Math.min(Math.round(100 * sofar / totalPercent), 99);
}, },
error: function () { error: function () {
return _error; return _error;
@ -141,13 +130,37 @@ var SetupStore = assign(EventEmitter.prototype, {
_retryPromise = Promise.defer(); _retryPromise = Promise.defer();
return _retryPromise.promise; return _retryPromise.promise;
}, },
run: Promise.coroutine(function* () { init: Promise.coroutine(function* () {
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();
required.init = !(yield boot2docker.exists()) || !isoversion || setupUtil.compareVersions(isoversion, boot2docker.version()) < 0;
required.start = required.init || (yield boot2docker.status()) !== 'running';
var exists = yield boot2docker.exists(); var exists = yield boot2docker.exists();
if (exists) { if (exists) {
this.steps().startBoot2Docker.seconds = 13; this.steps().start.seconds = 13;
} }
for (let step of _steps) { _requiredSteps = _steps.filter(function (step) {
return required[step.name];
});
}),
updateBinaries: function () {
if (setupUtil.needsBinaryFix()) {
return Promise.resolve();
}
if (setupUtil.shouldUpdateBinaries()) {
return util.exec(util.copyBinariesCmd());
}
return Promise.resolve();
},
run: Promise.coroutine(function* () {
yield this.init();
yield this.updateBinaries();
for (let step of _requiredSteps) {
_currentStep = step; _currentStep = step;
step.percent = 0; step.percent = 0;
while (true) { while (true) {
@ -163,6 +176,7 @@ var SetupStore = assign(EventEmitter.prototype, {
break; break;
} catch (err) { } catch (err) {
if (err) { if (err) {
console.log(err.stack);
_error = err; _error = err;
this.emit(this.ERROR_EVENT); this.emit(this.ERROR_EVENT);
} else { } else {

View File

@ -1,36 +1,33 @@
var _ = require('underscore'); var _ = require('underscore');
var crypto = require('crypto'); var crypto = require('crypto');
var exec = require('exec');
var fs = require('fs'); var fs = require('fs');
var path = require('path');
var request = require('request'); var request = require('request');
var progress = require('request-progress'); var progress = require('request-progress');
var path = require('path');
var Promise = require('bluebird'); var Promise = require('bluebird');
var util = require('./Util');
var SetupUtil = { var SetupUtil = {
supportDir: function () { needsBinaryFix: function () {
var dirs = ['Library', 'Application\ Support', 'Kitematic']; if (!fs.existsSync('/usr/local/bin/docker') && !fs.existsSync('/usr/local/bin/boot2docker')) {
var acc = process.env.HOME; return fs.statSync('/usr/local/bin').gid !== 80 || fs.statSync('/usr/local/bin').uid !== process.getuid();
dirs.forEach(function (d) { }
acc = path.join(acc, d);
if (!fs.existsSync(acc)) { if (fs.existsSync('/usr/local/bin/docker') && (fs.statSync('/usr/local/bin/docker').gid !== 80 || fs.statSync('/usr/local/bin/docker').uid !== process.getuid())) {
fs.mkdirSync(acc); return true;
} }
});
return acc; if (fs.existsSync('/usr/local/bin/boot2docker') && (fs.statSync('/usr/local/bin/boot2docker').gid !== 80 || fs.statSync('/usr/local/bin/boot2docker').uid !== process.getuid())) {
return true;
}
return false;
}, },
resourceDir: function () { shouldUpdateBinaries: function () {
return process.env.RESOURCES_PATH; var packagejson = util.packagejson();
}, return !fs.existsSync('/usr/local/bin/docker') ||
isSudo: function () { !fs.existsSync('/usr/local/bin/boot2docker') ||
return new Promise((resolve, reject) => { this.checksum('/usr/local/bin/boot2docker') !== this.checksum(path.join(util.resourceDir(), 'boot2docker-' + packagejson['boot2docker-version'])) ||
exec(['sudo', '-n', '-u', 'root', 'true'], (stderr, stdout, code) => { this.checksum('/usr/local/bin/docker') !== this.checksum(path.join(util.resourceDir(), 'docker-' + packagejson['docker-version']));
if (code) {
reject(stderr);
}
resolve(stderr.indexOf('a password is required') === -1);
});
});
}, },
simulateProgress: function (estimateSeconds, progress) { simulateProgress: function (estimateSeconds, progress) {
var times = _.range(0, estimateSeconds * 1000, 200); var times = _.range(0, estimateSeconds * 1000, 200);
@ -42,10 +39,13 @@ var SetupUtil = {
timers.push(timer); timers.push(timer);
}); });
}, },
checksum: function (filename) {
return crypto.createHash('sha256').update(fs.readFileSync(filename), 'utf8').digest('hex');
},
download: function (url, filename, checksum, percentCallback) { download: function (url, filename, checksum, percentCallback) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
if (fs.existsSync(filename)) { if (fs.existsSync(filename)) {
var existingChecksum = crypto.createHash('sha256').update(fs.readFileSync(filename), 'utf8').digest('hex'); var existingChecksum = this.checksum(filename);
if (existingChecksum === checksum) { if (existingChecksum === checksum) {
resolve(); resolve();
return; return;

View File

@ -4,9 +4,11 @@ var fs = require('fs');
var path = require('path'); var path = require('path');
module.exports = { module.exports = {
exec: function (args) { exec: function (args, options) {
options = options || {};
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
exec(args, (stderr, stdout, code) => { console.log(options);
exec(args, options, (stderr, stdout, code) => {
if (code) { if (code) {
reject(stderr); reject(stderr);
} }
@ -17,7 +19,42 @@ module.exports = {
home: function () { home: function () {
return process.env[(process.platform === 'win32') ? 'USERPROFILE' : 'HOME']; return process.env[(process.platform === 'win32') ? 'USERPROFILE' : 'HOME'];
}, },
supportDir: function () {
var dirs = ['Library', 'Application\ Support', 'Kitematic'];
var acc = process.env.HOME;
dirs.forEach(function (d) {
acc = path.join(acc, d);
if (!fs.existsSync(acc)) {
fs.mkdirSync(acc);
}
});
return acc;
},
resourceDir: function () {
return process.env.RESOURCES_PATH;
},
packagejson: function () { packagejson: function () {
return JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'package.json'), 'utf8')); return JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'package.json'), 'utf8'));
},
copycmd: function (src, dest) {
return ['rm', '-f', dest, '&&', 'cp', src, dest];
},
copyBinariesCmd: function () {
var packagejson = this.packagejson();
var cmd = [];
cmd.push.apply(cmd, this.copycmd(this.escapePath(path.join(this.resourceDir(), 'boot2docker-' + packagejson['boot2docker-version'])), '/usr/local/bin/boot2docker'));
cmd.push('&&');
cmd.push.apply(cmd, this.copycmd(this.escapePath(path.join(this.resourceDir(), 'docker-' + packagejson['docker-version'])), '/usr/local/bin/docker'));
return cmd.join(' ');
},
fixBinariesCmd: function () {
var cmd = [];
cmd.push.apply(cmd, ['chown', `${process.getuid()}:${80}`, this.escapePath(path.join('/usr/local/bin', 'boot2docker'))]);
cmd.push('&&');
cmd.push.apply(cmd, ['chown', `${process.getuid()}:${80}`, this.escapePath(path.join('/usr/local/bin', 'docker'))]);
return cmd.join(' ');
},
escapePath: function (str) {
return str.replace(/ /g, '\\ ').replace(/\(/g, '\\(').replace(/\)/g, '\\)');
} }
}; };

View File

@ -27,13 +27,12 @@ var VirtualBox = {
return util.exec(this.command() + ' list runningvms | sed -E \'s/.*\\{(.*)\\}/\\1/\' | xargs -L1 -I {} ' + this.command() + ' controlvm {} poweroff'); return util.exec(this.command() + ' list runningvms | sed -E \'s/.*\\{(.*)\\}/\\1/\' | xargs -L1 -I {} ' + this.command() + ' controlvm {} poweroff');
}, },
killall: function () { killall: function () {
if (!this.installed()) {
return Promise.reject('VirtualBox not installed.');
}
return this.poweroffall().then(() => { return this.poweroffall().then(() => {
return util.exec(['pkill', 'VirtualBox']); return util.exec(['pkill', 'VirtualBox']);
}).then(() => { }).then(() => {
return util.exec(['pkill', 'VBox']); return util.exec(['pkill', 'VBox']);
}).catch(err => {
}); });
}, },
vmstate: function (name) { vmstate: function (name) {