diff --git a/Gruntfile.js b/Gruntfile.js
index 9b02a0811b..7624bb252b 100644
--- a/Gruntfile.js
+++ b/Gruntfile.js
@@ -229,7 +229,9 @@ module.exports = function (grunt) {
babel: {
options: {
sourceMap: 'inline',
- blacklist: 'regenerator'
+ blacklist: 'regenerator',
+ stage: 1,
+ optional: ['asyncToGenerator']
},
dist: {
files: [{
diff --git a/__tests__/URLUtil-test.js b/__tests__/URLUtil-test.js
deleted file mode 100644
index 2c1212523a..0000000000
--- a/__tests__/URLUtil-test.js
+++ /dev/null
@@ -1,65 +0,0 @@
-jest.dontMock('../src/utils/URLUtil');
-jest.dontMock('parseUri');
-var urlUtil = require('../src/utils/URLUtil');
-var util = require('../src/utils/Util');
-
-describe('URLUtil', function () {
- beforeEach(() => {
- util.compareVersions.mockClear();
- util.isOfficialRepo.mockClear();
- });
-
- it('does nothing if the url is undefined', () => {
- util.compareVersions.mockReturnValue(1);
- util.isOfficialRepo.mockReturnValue(true);
- expect(urlUtil.openUrl()).toBe(false);
- });
-
- it('does nothing if the flags object is undefined', () => {
- util.compareVersions.mockReturnValue(1);
- util.isOfficialRepo.mockReturnValue(true);
- expect(urlUtil.openUrl('docker://repository/run/redis')).toBe(false);
- });
-
- it('does nothing if the url enabled flag is falsy', () => {
- util.compareVersions.mockReturnValue(1);
- util.isOfficialRepo.mockReturnValue(true);
- expect(urlUtil.openUrl('docker://repository/run/redis', {dockerURLEnabledVersion: undefined})).toBe(false);
- });
-
- it('does nothing if the url enabled flag version is higher than the app version', () => {
- util.compareVersions.mockReturnValue(-1);
- util.isOfficialRepo.mockReturnValue(true);
- expect(urlUtil.openUrl('docker://repository/run/redis', {dockerURLEnabledVersion: '0.5.19'}, '0.5.18')).toBe(false);
- });
-
- it('does nothing if the type is not in the whitelist', () => {
- util.compareVersions.mockReturnValue(1);
- util.isOfficialRepo.mockReturnValue(true);
- expect(urlUtil.openUrl('docker://badtype/run/redis', {dockerURLEnabledVersion: '0.5.19'}, '0.5.18')).toBe(false);
- });
-
- it('does nothing if the method is not in the whitelist', () => {
- util.compareVersions.mockReturnValue(1);
- util.isOfficialRepo.mockReturnValue(true);
- expect(urlUtil.openUrl('docker://repository/badmethod/redis', {dockerURLEnabledVersion: '0.5.19'}, '0.5.18')).toBe(false);
- });
-
- it('does nothing if protocol is not docker:', () => {
- util.compareVersions.mockReturnValue(1);
- util.isOfficialRepo.mockReturnValue(true);
- expect(urlUtil.openUrl('facetime://')).toBe(false);
- });
-
- it('does nothing if repo is not official', () => {
- util.compareVersions.mockReturnValue(1);
- util.isOfficialRepo.mockReturnValue(false);
- expect(urlUtil.openUrl('docker://repository/run/not/official', {dockerURLEnabledVersion: '0.5.19'}, '0.5.20')).toBe(false);
- });
-
- it('returns true if type and method are correct', () => {
- util.compareVersions.mockReturnValue(1);
- util.isOfficialRepo.mockReturnValue(true);
- expect(urlUtil.openUrl('docker://repository/run/redis', {dockerURLEnabledVersion: '0.5.19'}, '0.5.20')).toBe(true);
- });
-});
diff --git a/package.json b/package.json
index 71a485fbc4..35a26545af 100644
--- a/package.json
+++ b/package.json
@@ -24,47 +24,43 @@
],
"docker-version": "1.8.2",
"docker-machine-version": "0.4.1",
- "electron-version": "0.27.2",
- "virtualbox-version": "5.0.4",
- "virtualbox-filename": "VirtualBox-5.0.4.pkg",
- "virtualbox-filename-win": "VirtualBox-5.0.4.exe",
- "virtualbox-checksum": "4597b2ebdf7a334a3a4028e2285f62cf4cf157477e33fa5f433d944da54737d6",
- "virtualbox-checksum-win": "17fe9943eae33d1d23d37160fd862b7c5db0eef8cb48225cf143244d0e934f94",
+ "electron-version": "0.33.6",
"dependencies": {
- "alt": "^0.16.2",
+ "alt": "^0.17.4",
"ansi-to-html": "0.3.0",
"any-promise": "^0.1.0",
- "async": "^0.9.0",
+ "async": "^1.4.2",
"bluebird": "^2.9.24",
"bugsnag-js": "^2.4.7",
- "classnames": "^1.2.0",
+ "classnames": "^2.1.5",
"coveralls": "^2.11.2",
"deep-extend": "^0.4.0",
"dockerode": "^2.1.4",
- "exec": "0.2.0",
+ "exec": "0.2.1",
+ "history": "^1.12.0",
"install": "^0.1.8",
"jquery": "^2.1.3",
"mixpanel": "kitematic/mixpanel-node",
"mkdirp": "^0.5.0",
"node-uuid": "^1.4.3",
- "npm": "^2.9.1",
- "object-assign": "^2.0.0",
+ "npm": "^3.3.5",
+ "object-assign": "^4.0.1",
"osx-release": "^1.1.0",
"parseUri": "^1.2.3-2",
"react": "^0.13.1",
- "react-bootstrap": "^0.20.3",
+ "react-bootstrap": "^0.26.1",
"react-retina-image": "^1.1.2",
- "react-router": "^0.13.3",
+ "react-router": "^1.0.0-rc1",
"request": "^2.55.0",
"request-progress": "^0.3.1",
"rimraf": "^2.3.2",
"underscore": "^1.8.3",
- "validator": "^3.39.0"
+ "validator": "^4.1.0"
},
"devDependencies": {
- "babel": "^5.1.10",
+ "babel": "^5.8.23",
"babel-jest": "^5.2.0",
- "electron-prebuilt": "^0.27.3",
+ "electron-prebuilt": "^0.33.6",
"eslint": "^1.3.1",
"eslint-plugin-react": "^3.3.0",
"grunt": "^0.4.5",
@@ -79,7 +75,7 @@
"grunt-curl": "^2.2.0",
"grunt-download-electron": "^2.1.1",
"grunt-electron": "^2.0.0",
- "grunt-electron-installer": "^0.37.0",
+ "grunt-electron-installer": "^1.0.4",
"grunt-if-missing": "^1.0.0",
"grunt-newer": "^1.1.1",
"grunt-plistbuddy": "^0.1.1",
@@ -88,13 +84,13 @@
"grunt-rename": "^0.1.4",
"grunt-shell": "^1.1.2",
"grunt-shell-spawn": "^0.3.8",
- "jest-cli": "^0.4.5",
- "jsxhint": "^0.14.0",
+ "jest-cli": "^0.5.8",
+ "jsxhint": "^0.15.1",
"load-grunt-tasks": "^3.2.0",
"minimist": "^1.1.1",
"react-tools": "^0.13.1",
"run-sequence": "^1.0.2",
"shell-escape": "^0.2.0",
- "source-map-support": "^0.2.10"
+ "source-map-support": "^0.3.2"
}
}
diff --git a/src/actions/SetupActions.js b/src/actions/SetupActions.js
new file mode 100644
index 0000000000..a9102b8d66
--- /dev/null
+++ b/src/actions/SetupActions.js
@@ -0,0 +1,12 @@
+import alt from '../alt';
+
+class SetupActions {
+ constructor () {
+ this.generateActions(
+ 'progress',
+ 'error'
+ );
+ }
+}
+
+export default alt.createActions(SetupActions);
diff --git a/src/app.js b/src/app.js
index a902b06b2f..7c2c2bfe38 100644
--- a/src/app.js
+++ b/src/app.js
@@ -9,15 +9,16 @@ import metrics from './utils/MetricsUtil';
import template from './menutemplate';
import webUtil from './utils/WebUtil';
import hubUtil from './utils/HubUtil';
-var urlUtil = require('./utils/URLUtil');
-var app = remote.require('app');
+import setupUtil from './utils/SetupUtil';
import request from 'request';
import docker from './utils/DockerUtil';
import hub from './utils/HubUtil';
import Router from 'react-router';
+import createHashHistory from 'history/lib/createHashHistory'
import routes from './routes';
import routerContainer from './router';
import repositoryActions from './actions/RepositoryActions';
+var app = remote.require('app');
hubUtil.init();
@@ -40,19 +41,16 @@ setInterval(function () {
metrics.track('app heartbeat');
}, 14400000);
-var router = Router.create({
- routes: routes
-});
-router.run(Handler => React.render(, document.body));
-routerContainer.set(router);
+let history = createHashHistory()
+React.render({routes}, document.body)
-SetupStore.setup().then(() => {
+setupUtil.setup().then(() => {
Menu.setApplicationMenu(Menu.buildFromTemplate(template()));
docker.init();
if (!hub.prompted() && !hub.loggedin()) {
- router.transitionTo('login');
+ history.replaceState(null, '/account/login');
} else {
- router.transitionTo('search');
+ history.replaceState(null, '/containers/new');
}
}).catch(err => {
metrics.track('Setup Failed', {
@@ -67,24 +65,3 @@ ipc.on('application:quitting', () => {
machine.stop();
}
});
-
-// Event fires when the app receives a docker:// URL such as
-// docker://repository/run/redis
-ipc.on('application:open-url', opts => {
- request.get('https://kitematic.com/flags.json', (err, response, body) => {
- if (err || response.statusCode !== 200) {
- return;
- }
-
- var flags = JSON.parse(body);
- if (!flags) {
- return;
- }
-
- urlUtil.openUrl(opts.url, flags, app.getVersion());
- });
-});
-
-module.exports = {
- router: router
-};
diff --git a/src/browser.js b/src/browser.js
index 9ff97d3011..61d5f74273 100644
--- a/src/browser.js
+++ b/src/browser.js
@@ -42,12 +42,6 @@ if (process.platform === 'win32') {
}
}
-var openURL = null;
-app.on('open-url', function (event, url) {
- event.preventDefault();
- openURL = url;
-});
-
app.on('ready', function () {
var mainWindow = new BrowserWindow({
width: size.width || 800,
@@ -106,18 +100,6 @@ app.on('ready', function () {
mainWindow.show();
mainWindow.focus();
- if (openURL) {
- mainWindow.webContents.send('application:open-url', {
- url: openURL
- });
- }
- app.on('open-url', function (event, url) {
- event.preventDefault();
- mainWindow.webContents.send('application:open-url', {
- url: url
- });
- });
-
if (process.env.NODE_ENV !== 'development') {
autoUpdater.setFeedUrl('https://updates.kitematic.com/releases/latest?version=' + app.getVersion() + '&beta=' + !!settingsjson.beta + '&platform=' + os.platform());
}
diff --git a/src/components/Containers.react.js b/src/components/Containers.react.js
index b7058b28df..492d76cba7 100644
--- a/src/components/Containers.react.js
+++ b/src/components/Containers.react.js
@@ -10,10 +10,6 @@ import shell from 'shell';
import machine from '../utils/DockerMachineUtil';
var Containers = React.createClass({
- contextTypes: {
- router: React.PropTypes.func
- },
-
getInitialState: function () {
return {
sidebarOffset: 0,
@@ -52,7 +48,9 @@ var Containers = React.createClass({
let containers = containerStore.getState().containers;
let sorted = this.sorted(containerStore.getState().containers);
- let name = this.context.router.getCurrentParams().name;
+ console.log(this.props);
+
+ let name = this.props.params.name;
if (containerStore.getState().pending) {
this.context.router.transitionTo('pull');
} else if (name && !containers[name]) {
@@ -151,7 +149,7 @@ var Containers = React.createClass({
sidebarHeaderClass += ' sep';
}
- var container = this.context.router.getCurrentParams().name ? this.state.containers[this.context.router.getCurrentParams().name] : {};
+ var container = this.props.params ? this.state.containers[ this.props.params] : {};
return (
diff --git a/src/components/Setup.react.js b/src/components/Setup.react.js
index 0d42839562..f54702cb8b 100644
--- a/src/components/Setup.react.js
+++ b/src/components/Setup.react.js
@@ -1,79 +1,43 @@
import React from 'react/addons';
import Router from 'react-router';
import Radial from './Radial.react.js';
-import SetupStore from '../stores/SetupStore';
import RetinaImage from 'react-retina-image';
import Header from './Header.react';
import Util from '../utils/Util';
import metrics from '../utils/MetricsUtil';
+import setupStore from '../stores/SetupStore';
var Setup = React.createClass({
- mixins: [ Router.Navigation ],
+ mixins: [Router.Navigation],
+
getInitialState: function () {
- return {
- progress: 0,
- name: '',
- };
- },
- componentWillMount: function () {
- SetupStore.on(SetupStore.PROGRESS_EVENT, this.update);
- SetupStore.on(SetupStore.STEP_EVENT, this.update);
- SetupStore.on(SetupStore.ERROR_EVENT, this.update);
+ return setupStore.getState();
},
+
componentDidMount: function () {
- this.update();
+ setupStore.listen(this.update);
},
+
componentDidUnmount: function () {
- SetupStore.removeListener(SetupStore.PROGRESS_EVENT, this.update);
- SetupStore.removeListener(SetupStore.STEP_EVENT, this.update);
- SetupStore.removeListener(SetupStore.ERROR_EVENT, this.update);
- },
- handleCancelRetry: function () {
- metrics.track('Setup Retried', {
- from: 'cancel'
- });
- SetupStore.retry();
- },
- handleErrorRetry: function () {
- metrics.track('Setup Retried', {
- from: 'error',
- removeVM: false
- });
- SetupStore.retry(false);
- },
- handleErrorRemoveRetry: function () {
- metrics.track('Setup Retried', {
- from: 'error',
- removeVM: true
- });
- SetupStore.retry(true);
- },
- handleOpenWebsite: function () {
- Util.exec(['open', 'https://www.virtualbox.org/wiki/Downloads']);
+ setupStore.unlisten(this.update);
},
+
update: function () {
- this.setState({
- progress: SetupStore.percent(),
- step: SetupStore.step(),
- error: SetupStore.error(),
- cancelled: SetupStore.cancelled()
- });
+ this.setState(setupStore.getState());
},
+
renderContents: function () {
- var img = 'virtualbox.png';
- if (SetupStore.step().name === 'init' || SetupStore.step().name === 'start') {
- img = 'boot2docker.png';
- }
return (
);
},
- renderStep: function () {
+
+ renderProgress: function () {
return (
@@ -83,9 +47,8 @@ var Setup = React.createClass({
-
Step {SetupStore.number()} out of {SetupStore.stepCount()}
-
{SetupStore.step().title}
-
{SetupStore.step().message}
+
{this.state.title}
+
{this.state.message}
@@ -141,15 +104,14 @@ var Setup = React.createClass({
);
},
+
render: function () {
if (this.state.cancelled) {
return this.renderCancelled();
} else if (this.state.error) {
return this.renderError();
- } else if (SetupStore.step()) {
- return this.renderStep();
} else {
- return false;
+ return this.renderProgress();
}
}
});
diff --git a/src/menutemplate.js b/src/menutemplate.js
index 0f9eda4092..adbf090123 100644
--- a/src/menutemplate.js
+++ b/src/menutemplate.js
@@ -8,6 +8,7 @@ var metrics = require('./utils/MetricsUtil');
var machine = require('./utils/DockerMachineUtil');
var dialog = remote.require('dialog');
import docker from './utils/DockerUtil';
+import Router from 'react-router';
// main.js
var MenuTemplate = function () {
@@ -21,7 +22,7 @@ var MenuTemplate = function () {
metrics.track('Opened About', {
from: 'menu'
});
- router.get().transitionTo('about');
+ Router.transitionTo('about');
}
},
{
@@ -35,7 +36,7 @@ var MenuTemplate = function () {
metrics.track('Opened Preferences', {
from: 'menu'
});
- router.get().transitionTo('preferences');
+ Router.transitionTo('preferences');
}
},
{
diff --git a/src/routes.js b/src/routes.js
index b8f628fbad..3cb21e9cbd 100644
--- a/src/routes.js
+++ b/src/routes.js
@@ -16,45 +16,42 @@ import Preferences from './components/Preferences.react';
import About from './components/About.react';
import NewContainerSearch from './components/NewContainerSearch.react';
import NewContainerPull from './components/NewContainerPull.react';
-import Router from 'react-router';
-
-var Route = Router.Route;
-var DefaultRoute = Router.DefaultRoute;
-var RouteHandler = Router.RouteHandler;
+import {Router, IndexRoute, Route, Link} from 'react-router'
var App = React.createClass({
render: function () {
return (
-
+
+ {this.props.children}
+
);
}
});
var routes = (
-
-
-
-
+
+
+
+
+
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
-
-
+
-
-
+
+
-
);
diff --git a/src/stores/SetupStore.js b/src/stores/SetupStore.js
index 9d8418bbe9..67fbd18164 100644
--- a/src/stores/SetupStore.js
+++ b/src/stores/SetupStore.js
@@ -1,240 +1,22 @@
-import {EventEmitter} from 'events';
-import _ from 'underscore';
-import path from 'path';
-import fs from 'fs';
-import Promise from 'bluebird';
-import machine from '../utils/DockerMachineUtil';
-import virtualBox from '../utils/VirtualBoxUtil';
-import setupUtil from '../utils/SetupUtil';
-import util from '../utils/Util';
-import assign from 'object-assign';
-import metrics from '../utils/MetricsUtil';
-import bugsnag from 'bugsnag-js';
-import docker from '../utils/DockerUtil';
+import alt from '../alt';
+import setupActions from '../actions/SetupActions';
-var _currentStep = null;
-var _error = null;
-var _cancelled = false;
-var _retryPromise = null;
-var _requiredSteps = [];
-
-var _steps = [{
- name: 'download',
- title: 'Downloading VirtualBox',
- message: 'VirtualBox is being downloaded. Kitematic requires VirtualBox to run containers.',
- totalPercent: 35,
- percent: 0,
- run: function (progressCallback) {
- return setupUtil.download(virtualBox.url(), path.join(util.supportDir(), virtualBox.filename()), virtualBox.checksum(), percent => {
- progressCallback(percent);
- });
+class SetupStore {
+ constructor () {
+ this.bindActions(setupActions);
+ this.title = '';
+ this.message = '';
+ this.percent = 0;
+ this.error = null;
}
-}, {
- name: 'install',
- title: 'Installing VirtualBox & Docker',
- message: 'VirtualBox & Docker are being installed or upgraded in the background. We may need you to type in your password to continue.',
- totalPercent: 5,
- percent: 0,
- seconds: 5,
- run: Promise.coroutine(function* (progressCallback) {
- if (!virtualBox.installed()) {
- yield virtualBox.killall();
- progressCallback(50); // TODO: detect when the installation has started so we can simulate progress
- try {
- if (util.isWindows()) {
- yield util.exec([path.join(util.supportDir(), virtualBox.filename()), '-msiparams', 'REBOOT=ReallySuppress', 'LIMITUI=INSTALLUILEVEL_PROGRESSONLY']);
- } else {
- yield util.exec(setupUtil.macSudoCmd(setupUtil.installVirtualBoxCmd()));
- }
- } catch (err) {
- throw null;
- }
- } else if (!util.isWindows() && !virtualBox.active()) {
- yield util.exec(setupUtil.macSudoCmd(util.escapePath('/Library/Application Support/VirtualBox/LaunchDaemons/VirtualBoxStartup.sh') + ' restart'));
- }
- })
-}, {
- name: 'init',
- title: 'Starting Docker VM',
- message: 'To run Docker containers on your computer, Kitematic is starting a Linux virtual machine. This may take a minute...',
- totalPercent: 60,
- percent: 0,
- seconds: 110,
- run: Promise.coroutine(function* (progressCallback) {
- setupUtil.simulateProgress(this.seconds, progressCallback);
- var exists = yield machine.exists();
- if (!exists || (yield machine.state()) === 'Error') {
- if (exists && (yield machine.state()) === 'Error') {
- yield machine.rm();
- }
- yield machine.create();
- return;
- }
- var isoversion = machine.isoversion();
- var packagejson = util.packagejson();
- var packagejsonVersion = packagejson['docker-version'].split('-')[0];
- if (!isoversion || util.compareVersions(isoversion, packagejsonVersion) < 0) {
- yield machine.start();
- yield machine.upgrade();
- }
- if ((yield machine.state()) !== 'Running') {
- yield machine.start();
- }
- })
-}];
+ error ({error}) {
+ this.setState({error});
+ }
-var SetupStore = assign(Object.create(EventEmitter.prototype), {
- PROGRESS_EVENT: 'setup_progress',
- STEP_EVENT: 'setup_step',
- ERROR_EVENT: 'setup_error',
- step: function () {
- return _currentStep;
- },
- steps: function () {
- return _.indexBy(_steps, 'name');
- },
- stepCount: function () {
- return _requiredSteps.length;
- },
- number: function () {
- return _.indexOf(_requiredSteps, _currentStep) + 1;
- },
- percent: function () {
- var sofar = 0;
- var totalPercent = _requiredSteps.reduce((prev, step) => prev + step.totalPercent, 0);
- _.each(_requiredSteps, step => {
- sofar += step.totalPercent * step.percent / 100;
- });
- return Math.min(Math.round(100 * sofar / totalPercent), 99);
- },
- error: function () {
- return _error;
- },
- cancelled: function () {
- return _cancelled;
- },
- retry: function (remove) {
- _error = null;
- _cancelled = false;
- if (!_retryPromise) {
- return;
- }
- this.emit(this.ERROR_EVENT);
- if (remove) {
- machine.rm().finally(() => {
- _retryPromise.resolve();
- });
- } else {
- machine.stop().finally(() => {
- _retryPromise.resolve();
- });
- }
- },
- setError: function (error) {
- _error = error;
- this.emit(this.ERROR_EVENT);
- },
- pause: function () {
- _retryPromise = Promise.defer();
- return _retryPromise.promise;
- },
- requiredSteps: Promise.coroutine(function* () {
- if (_requiredSteps.length) {
- return Promise.resolve(_requiredSteps);
- }
- var packagejson = util.packagejson();
- var isoversion = machine.isoversion();
- var required = {};
- var vboxfile = path.join(util.supportDir(), virtualBox.filename());
- var vboxNeedsInstall = !virtualBox.installed();
+ progress ({progress}) {
+ this.setState({progress})
+ }
+}
- required.download = vboxNeedsInstall && (!fs.existsSync(vboxfile) || setupUtil.checksum(vboxfile) !== virtualBox.checksum());
- required.install = vboxNeedsInstall || (!util.isWindows() && !virtualBox.active());
- required.init = required.install || !(yield machine.exists()) || (yield machine.state()) !== 'Running' || !isoversion || util.compareVersions(isoversion, packagejson['docker-version']) < 0;
-
- var exists = yield machine.exists();
- if (isoversion && util.compareVersions(isoversion, packagejson['docker-version']) < 0) {
- this.steps().init.seconds = 33;
- } else if (exists && (yield machine.state()) === 'Saved') {
- this.steps().init.seconds = 8;
- } else if (exists && (yield machine.state()) !== 'Error') {
- this.steps().init.seconds = 23;
- }
-
- _requiredSteps = _steps.filter(function (step) {
- return required[step.name];
- });
- return Promise.resolve(_requiredSteps);
- }),
- run: Promise.coroutine(function* () {
- metrics.track('Started Setup', {
- virtualbox: virtualBox.installed() ? yield virtualBox.version() : 'Not Installed'
- });
- var steps = yield this.requiredSteps();
- for (let step of steps) {
- _currentStep = step;
- step.percent = 0;
- while (true) {
- try {
- this.emit(this.STEP_EVENT);
- yield step.run(percent => {
- if (_currentStep) {
- step.percent = percent;
- this.emit(this.PROGRESS_EVENT);
- }
- });
- metrics.track('Setup Completed Step', {
- name: step.name
- });
- step.percent = 100;
- break;
- } catch (err) {
- if (err) {
- throw err;
- } else {
- metrics.track('Setup Cancelled');
- _cancelled = true;
- this.emit(this.STEP_EVENT);
- }
- yield this.pause();
- }
- }
- }
- _currentStep = null;
- return yield machine.ip();
- }),
- setup: Promise.coroutine(function * () {
- while (true) {
- try {
- var ip = yield this.run();
- if (!ip || !ip.length) {
- throw {
- message: 'Machine IP could not be fetched. Please retry the setup. If this fails please file a ticket on our GitHub repo.',
- machine: yield machine.info(),
- ip: ip
- };
- }
- docker.setup(ip, machine.name());
- yield docker.waitForConnection();
- metrics.track('Setup Finished');
- break;
- } catch (err) {
- err.message = util.removeSensitiveData(err.message);
- metrics.track('Setup Failed', {
- step: _currentStep,
- });
- console.log(err);
- bugsnag.notify('SetupError', err.message, {
- error: err,
- output: err.message
- }, 'info');
- _error = err;
- this.emit(this.ERROR_EVENT);
- yield this.pause();
- }
- }
- })
-});
-
-module.exports = SetupStore;
+export default alt.createStore(SetupStore);
diff --git a/src/utils/SetupUtil.js b/src/utils/SetupUtil.js
index 80b0edc6f2..fccb76ffc8 100644
--- a/src/utils/SetupUtil.js
+++ b/src/utils/SetupUtil.js
@@ -1,113 +1,81 @@
import _ from 'underscore';
-import crypto from 'crypto';
import fs from 'fs';
import path from 'path';
-import request from 'request';
-import progress from 'request-progress';
import Promise from 'bluebird';
import util from './Util';
-import resources from './ResourcesUtil';
-var virtualBox = require ('./VirtualBoxUtil');
+import bugsnag from 'bugsnag-js';
+import virtualBox from './VirtualBoxUtil';
+import setupActions from '../actions/SetupActions';
+import metrics from './MetricsUtil';
+import machine from './DockerMachineUtil';
+import docker from './DockerUtil';
-var SetupUtil = {
- needsBinaryFix() {
- return this.pathDoesNotExistOrDenied(util.binsPath()) ||
- (fs.existsSync(util.dockerBinPath()) && this.pathDenied(util.dockerBinPath())) ||
- (fs.existsSync(util.dockerMachineBinPath()) && this.pathDenied(util.dockerMachineBinPath()));
- },
- pathDoesNotExistOrDenied: function (path) {
- if(util.isWindows()) {
- return (!fs.existsSync(path));
- } else {
- return (!fs.existsSync(path) || this.pathDenied(path));
- }
- },
- pathDenied: function (path) {
- return fs.statSync(path).gid !== 80 || fs.statSync(path).uid !== process.getuid();
- },
- shouldUpdateBinaries: function () {
- return !fs.existsSync(util.dockerBinPath()) ||
- !fs.existsSync(util.dockerMachineBinPath()) ||
- !fs.existsSync(util.dockerComposeBinPath()) ||
- this.checksum(util.dockerBinPath()) !== this.checksum(resources.docker()) ||
- this.checksum(util.dockerMachineBinPath()) !== this.checksum(resources.dockerMachine()) ||
- this.checksum(util.dockerComposeBinPath()) !== this.checksum(resources.dockerCompose());
- },
- copycmd: function (src, dest) {
- return ['rm', '-f', dest, '&&', 'cp', src, dest];
- },
- copyBinariesCmd: function () {
- var cmd = ['mkdir', '-p', '/usr/local/bin'];
- cmd.push('&&');
- cmd.push.apply(cmd, this.copycmd(util.escapePath(resources.dockerMachine()), '/usr/local/bin/docker-machine'));
- cmd.push('&&');
- cmd.push.apply(cmd, this.copycmd(util.escapePath(resources.docker()), '/usr/local/bin/docker'));
- cmd.push('&&');
- cmd.push.apply(cmd, this.copycmd(util.escapePath(resources.dockerCompose()), '/usr/local/bin/docker-compose'));
- return cmd.join(' ');
- },
- fixBinariesCmd: function () {
- var cmd = [];
- cmd.push.apply(cmd, ['chown', `${process.getuid()}:${80}`, path.join('/usr/local/bin')]);
- cmd.push('&&');
- cmd.push.apply(cmd, ['chown', `${process.getuid()}:${80}`, path.join('/usr/local/bin', 'docker-machine')]);
- cmd.push('&&');
- cmd.push.apply(cmd, ['chown', `${process.getuid()}:${80}`, path.join('/usr/local/bin', 'docker')]);
- cmd.push('&&');
- cmd.push.apply(cmd, ['chown', `${process.getuid()}:${80}`, path.join('/usr/local/bin', 'docker-compose')]);
- return cmd.join(' ');
- },
- installVirtualBoxCmd: function () {
- if(util.isWindows()) {
- return `powershell.exe -ExecutionPolicy unrestricted -Command "Start-Process \\\"${path.join(util.supportDir(), virtualBox.filename())}\\\" -ArgumentList \\\"--silent --msiparams REBOOT=ReallySuppress\\\" -Verb runAs -Wait"`;
- } else {
- return `installer -pkg ${util.escapePath(path.join(util.supportDir(), virtualBox.filename()))} -target /`;
- }
- },
- macSudoCmd: function (cmd) {
- return `${util.escapePath(resources.macsudo())} -p "Kitematic requires administrative privileges to install and/or start VirtualBox." sh -c \"${cmd}\"`;
- },
- simulateProgress(estimateSeconds, progress) {
+let _retryPromise = null;
+
+export default {
+ simulateProgress (estimateSeconds, progress) {
var times = _.range(0, estimateSeconds * 1000, 200);
var timers = [];
_.each(times, time => {
var timer = setTimeout(() => {
- progress(100 * time / (estimateSeconds * 1000));
+ setupActions.progress(100 * time / (estimateSeconds * 1000));
}, time);
timers.push(timer);
});
},
- checksum(filename) {
- return crypto.createHash('sha256').update(fs.readFileSync(filename), 'utf8').digest('hex');
- },
- download(url, filename, checksum, percentCallback) {
- return new Promise((resolve, reject) => {
- if (fs.existsSync(filename)) {
- var existingChecksum = this.checksum(filename);
- if (existingChecksum === checksum) {
- resolve();
- return;
- } else {
- fs.unlinkSync(filename);
- }
- }
- progress(request({ uri: url, rejectUnauthorized: false }), { throttle: 10 }).on('progress', state => {
- if (percentCallback) {
- percentCallback(state.percent);
- }
- }).on('error', err => {
- reject(err);
- }).pipe(fs.createWriteStream(filename)).on('error', err => {
- reject(err);
- }).on('close', err => {
- if (err) {
- reject(err);
- }
- resolve();
+ retry (removeVM) {
+ if (removeVM) {
+ machine.rm().finally(() => {
+ console.log('machine removed');
+ _retryPromise.resolve();
});
- });
+ } else {
+ _retryPromise.resolve();
+ }
+ },
+
+ pause () {
+ _retryPromise = Promise.defer();
+ return _retryPromise.promise;
+ },
+
+ async setup () {
+ metrics.track('Started Setup');
+ while (true) {
+ try {
+ if (!util.isWindows() && !virtualBox.active()) {
+ await util.exec(setupUtil.macSudoCmd(util.escapePath('/Library/Application Support/VirtualBox/LaunchDaemons/VirtualBoxStartup.sh') + ' restart'));
+ }
+
+ let exists = await virtualBox.vmExists('default');
+ if (!exists) {
+ this.simulateProgress(60, progress => setupActions.progress(progress));
+ await machine.rm();
+ await machine.create();
+ } else {
+ let state = await machine.state();
+ if (state === 'Saved') {
+ this.simulateProgress(10, progress => setupActions.progress(progress));
+ } else {
+ this.simulateProgress(25, progress => setupActions.progress(progress));
+ }
+ await machine.start();
+ }
+
+ let ip = await machine.ip();
+ docker.setup(ip, machine.name());
+ await docker.waitForConnection();
+ break;
+ } catch (err) {
+ setupActions.error(err);
+ bugsnag.notify('SetupError', err.message, {
+ error: err,
+ output: err.message
+ }, 'info');
+ await this.pause();
+ }
+ }
+ metrics.track('Setup Finished');
}
};
-
-module.exports = SetupUtil;
diff --git a/src/utils/URLUtil.js b/src/utils/URLUtil.js
deleted file mode 100644
index 68b102fd22..0000000000
--- a/src/utils/URLUtil.js
+++ /dev/null
@@ -1,60 +0,0 @@
-import util from './Util';
-import parseUri from 'parseUri';
-import containerServerActions from '../actions/ContainerServerActions';
-
-module.exports = {
- TYPE_WHITELIST: ['repository'],
- METHOD_WHITELIST: ['run'],
- openUrl: function (url, flags, appVersion) {
- if (!url || !flags || !flags.dockerURLEnabledVersion || !appVersion) {
- return false;
- }
-
- // Make sure this feature is enabled via the feature flag
- if (util.compareVersions(appVersion, flags.dockerURLEnabledVersion) < 0) {
- return false;
- }
-
- var parser = parseUri(url);
-
- if (parser.protocol !== 'docker') {
- return false;
- }
-
- // Get the type of object we're operating on, e.g. 'repository'
- var type = parser.host;
-
- if (this.TYPE_WHITELIST.indexOf(type) === -1) {
- return false;
- }
-
- // Separate the path into [run', 'redis']
- var tokens = parser.path.replace('/', '').split('/');
-
- // Get the method trying to be executed, e.g. 'run'
- var method = tokens[0];
-
- if (this.METHOD_WHITELIST.indexOf(method) === -1) {
- return false;
- }
-
- // Get the repository namespace and repo name, e.g. 'redis' or 'myusername/myrepo'
- var repo = tokens.slice(1).join('/');
-
- // Only accept official repos for now (one component)
- if (tokens > 1) {
- return false;
- }
-
- // Only accept official repos for now
- if (!util.isOfficialRepo(repo)) {
- return false;
- }
-
- if (type === 'repository' && method === 'run') {
- containerServerActions.pending({repo, tag: 'latest'});
- return true;
- }
- return false;
- }
-};
diff --git a/src/utils/VirtualBoxUtil.js b/src/utils/VirtualBoxUtil.js
index 338717676a..20eff5ec08 100644
--- a/src/utils/VirtualBoxUtil.js
+++ b/src/utils/VirtualBoxUtil.js
@@ -10,15 +10,6 @@ var VirtualBox = {
return '/Applications/VirtualBox.app/Contents/MacOS/VBoxManage';
}
},
- filename: function () {
- return util.isWindows() ? util.packagejson()['virtualbox-filename-win'] : util.packagejson()['virtualbox-filename'];
- },
- checksum: function () {
- return util.isWindows() ? util.packagejson()['virtualbox-checksum-win'] : util.packagejson()['virtualbox-checksum'];
- },
- url: function () {
- return `https://github.com/kitematic/virtualbox/releases/download/${util.packagejson()['virtualbox-version']}/${this.filename()}`;
- },
installed: function () {
if(util.isWindows()) {
return fs.existsSync('C:\\Program Files\\Oracle\\VirtualBox\\VBoxManage.exe') && fs.existsSync('C:\\Program Files\\Oracle\\VirtualBox\\VirtualBox.exe');
@@ -68,15 +59,11 @@ var VirtualBox = {
});
}
},
- vmstate: function (name) {
- return new Promise((resolve, reject) => {
- util.exec([this.command(), 'showvminfo', name, '--machinereadable']).then(stdout => {
- var match = stdout.match(/VMState="(\w+)"/);
- if (!match) {
- reject('Could not parse VMState');
- }
- resolve(match[1]);
- }).catch(reject);
+ vmExists: function (name) {
+ return util.exec([this.command(), 'showvminfo', name]).then(() => {
+ return true;
+ }).catch((err) => {
+ return false;
});
}
};