mirror of https://github.com/docker/docs.git
WIP upgrade
This commit is contained in:
parent
a44f20168d
commit
9dc6a03047
|
|
@ -229,7 +229,9 @@ module.exports = function (grunt) {
|
||||||
babel: {
|
babel: {
|
||||||
options: {
|
options: {
|
||||||
sourceMap: 'inline',
|
sourceMap: 'inline',
|
||||||
blacklist: 'regenerator'
|
blacklist: 'regenerator',
|
||||||
|
stage: 1,
|
||||||
|
optional: ['asyncToGenerator']
|
||||||
},
|
},
|
||||||
dist: {
|
dist: {
|
||||||
files: [{
|
files: [{
|
||||||
|
|
|
||||||
|
|
@ -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);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
38
package.json
38
package.json
|
|
@ -24,47 +24,43 @@
|
||||||
],
|
],
|
||||||
"docker-version": "1.8.2",
|
"docker-version": "1.8.2",
|
||||||
"docker-machine-version": "0.4.1",
|
"docker-machine-version": "0.4.1",
|
||||||
"electron-version": "0.27.2",
|
"electron-version": "0.33.6",
|
||||||
"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",
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"alt": "^0.16.2",
|
"alt": "^0.17.4",
|
||||||
"ansi-to-html": "0.3.0",
|
"ansi-to-html": "0.3.0",
|
||||||
"any-promise": "^0.1.0",
|
"any-promise": "^0.1.0",
|
||||||
"async": "^0.9.0",
|
"async": "^1.4.2",
|
||||||
"bluebird": "^2.9.24",
|
"bluebird": "^2.9.24",
|
||||||
"bugsnag-js": "^2.4.7",
|
"bugsnag-js": "^2.4.7",
|
||||||
"classnames": "^1.2.0",
|
"classnames": "^2.1.5",
|
||||||
"coveralls": "^2.11.2",
|
"coveralls": "^2.11.2",
|
||||||
"deep-extend": "^0.4.0",
|
"deep-extend": "^0.4.0",
|
||||||
"dockerode": "^2.1.4",
|
"dockerode": "^2.1.4",
|
||||||
"exec": "0.2.0",
|
"exec": "0.2.1",
|
||||||
|
"history": "^1.12.0",
|
||||||
"install": "^0.1.8",
|
"install": "^0.1.8",
|
||||||
"jquery": "^2.1.3",
|
"jquery": "^2.1.3",
|
||||||
"mixpanel": "kitematic/mixpanel-node",
|
"mixpanel": "kitematic/mixpanel-node",
|
||||||
"mkdirp": "^0.5.0",
|
"mkdirp": "^0.5.0",
|
||||||
"node-uuid": "^1.4.3",
|
"node-uuid": "^1.4.3",
|
||||||
"npm": "^2.9.1",
|
"npm": "^3.3.5",
|
||||||
"object-assign": "^2.0.0",
|
"object-assign": "^4.0.1",
|
||||||
"osx-release": "^1.1.0",
|
"osx-release": "^1.1.0",
|
||||||
"parseUri": "^1.2.3-2",
|
"parseUri": "^1.2.3-2",
|
||||||
"react": "^0.13.1",
|
"react": "^0.13.1",
|
||||||
"react-bootstrap": "^0.20.3",
|
"react-bootstrap": "^0.26.1",
|
||||||
"react-retina-image": "^1.1.2",
|
"react-retina-image": "^1.1.2",
|
||||||
"react-router": "^0.13.3",
|
"react-router": "^1.0.0-rc1",
|
||||||
"request": "^2.55.0",
|
"request": "^2.55.0",
|
||||||
"request-progress": "^0.3.1",
|
"request-progress": "^0.3.1",
|
||||||
"rimraf": "^2.3.2",
|
"rimraf": "^2.3.2",
|
||||||
"underscore": "^1.8.3",
|
"underscore": "^1.8.3",
|
||||||
"validator": "^3.39.0"
|
"validator": "^4.1.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"babel": "^5.1.10",
|
"babel": "^5.8.23",
|
||||||
"babel-jest": "^5.2.0",
|
"babel-jest": "^5.2.0",
|
||||||
"electron-prebuilt": "^0.27.3",
|
"electron-prebuilt": "^0.33.6",
|
||||||
"eslint": "^1.3.1",
|
"eslint": "^1.3.1",
|
||||||
"eslint-plugin-react": "^3.3.0",
|
"eslint-plugin-react": "^3.3.0",
|
||||||
"grunt": "^0.4.5",
|
"grunt": "^0.4.5",
|
||||||
|
|
@ -79,7 +75,7 @@
|
||||||
"grunt-curl": "^2.2.0",
|
"grunt-curl": "^2.2.0",
|
||||||
"grunt-download-electron": "^2.1.1",
|
"grunt-download-electron": "^2.1.1",
|
||||||
"grunt-electron": "^2.0.0",
|
"grunt-electron": "^2.0.0",
|
||||||
"grunt-electron-installer": "^0.37.0",
|
"grunt-electron-installer": "^1.0.4",
|
||||||
"grunt-if-missing": "^1.0.0",
|
"grunt-if-missing": "^1.0.0",
|
||||||
"grunt-newer": "^1.1.1",
|
"grunt-newer": "^1.1.1",
|
||||||
"grunt-plistbuddy": "^0.1.1",
|
"grunt-plistbuddy": "^0.1.1",
|
||||||
|
|
@ -88,13 +84,13 @@
|
||||||
"grunt-rename": "^0.1.4",
|
"grunt-rename": "^0.1.4",
|
||||||
"grunt-shell": "^1.1.2",
|
"grunt-shell": "^1.1.2",
|
||||||
"grunt-shell-spawn": "^0.3.8",
|
"grunt-shell-spawn": "^0.3.8",
|
||||||
"jest-cli": "^0.4.5",
|
"jest-cli": "^0.5.8",
|
||||||
"jsxhint": "^0.14.0",
|
"jsxhint": "^0.15.1",
|
||||||
"load-grunt-tasks": "^3.2.0",
|
"load-grunt-tasks": "^3.2.0",
|
||||||
"minimist": "^1.1.1",
|
"minimist": "^1.1.1",
|
||||||
"react-tools": "^0.13.1",
|
"react-tools": "^0.13.1",
|
||||||
"run-sequence": "^1.0.2",
|
"run-sequence": "^1.0.2",
|
||||||
"shell-escape": "^0.2.0",
|
"shell-escape": "^0.2.0",
|
||||||
"source-map-support": "^0.2.10"
|
"source-map-support": "^0.3.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
import alt from '../alt';
|
||||||
|
|
||||||
|
class SetupActions {
|
||||||
|
constructor () {
|
||||||
|
this.generateActions(
|
||||||
|
'progress',
|
||||||
|
'error'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default alt.createActions(SetupActions);
|
||||||
39
src/app.js
39
src/app.js
|
|
@ -9,15 +9,16 @@ import metrics from './utils/MetricsUtil';
|
||||||
import template from './menutemplate';
|
import template from './menutemplate';
|
||||||
import webUtil from './utils/WebUtil';
|
import webUtil from './utils/WebUtil';
|
||||||
import hubUtil from './utils/HubUtil';
|
import hubUtil from './utils/HubUtil';
|
||||||
var urlUtil = require('./utils/URLUtil');
|
import setupUtil from './utils/SetupUtil';
|
||||||
var app = remote.require('app');
|
|
||||||
import request from 'request';
|
import request from 'request';
|
||||||
import docker from './utils/DockerUtil';
|
import docker from './utils/DockerUtil';
|
||||||
import hub from './utils/HubUtil';
|
import hub from './utils/HubUtil';
|
||||||
import Router from 'react-router';
|
import Router from 'react-router';
|
||||||
|
import createHashHistory from 'history/lib/createHashHistory'
|
||||||
import routes from './routes';
|
import routes from './routes';
|
||||||
import routerContainer from './router';
|
import routerContainer from './router';
|
||||||
import repositoryActions from './actions/RepositoryActions';
|
import repositoryActions from './actions/RepositoryActions';
|
||||||
|
var app = remote.require('app');
|
||||||
|
|
||||||
hubUtil.init();
|
hubUtil.init();
|
||||||
|
|
||||||
|
|
@ -40,19 +41,16 @@ setInterval(function () {
|
||||||
metrics.track('app heartbeat');
|
metrics.track('app heartbeat');
|
||||||
}, 14400000);
|
}, 14400000);
|
||||||
|
|
||||||
var router = Router.create({
|
let history = createHashHistory()
|
||||||
routes: routes
|
React.render(<Router history={history}>{routes}</Router>, document.body)
|
||||||
});
|
|
||||||
router.run(Handler => React.render(<Handler/>, document.body));
|
|
||||||
routerContainer.set(router);
|
|
||||||
|
|
||||||
SetupStore.setup().then(() => {
|
setupUtil.setup().then(() => {
|
||||||
Menu.setApplicationMenu(Menu.buildFromTemplate(template()));
|
Menu.setApplicationMenu(Menu.buildFromTemplate(template()));
|
||||||
docker.init();
|
docker.init();
|
||||||
if (!hub.prompted() && !hub.loggedin()) {
|
if (!hub.prompted() && !hub.loggedin()) {
|
||||||
router.transitionTo('login');
|
history.replaceState(null, '/account/login');
|
||||||
} else {
|
} else {
|
||||||
router.transitionTo('search');
|
history.replaceState(null, '/containers/new');
|
||||||
}
|
}
|
||||||
}).catch(err => {
|
}).catch(err => {
|
||||||
metrics.track('Setup Failed', {
|
metrics.track('Setup Failed', {
|
||||||
|
|
@ -67,24 +65,3 @@ ipc.on('application:quitting', () => {
|
||||||
machine.stop();
|
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
|
|
||||||
};
|
|
||||||
|
|
|
||||||
|
|
@ -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 () {
|
app.on('ready', function () {
|
||||||
var mainWindow = new BrowserWindow({
|
var mainWindow = new BrowserWindow({
|
||||||
width: size.width || 800,
|
width: size.width || 800,
|
||||||
|
|
@ -106,18 +100,6 @@ app.on('ready', function () {
|
||||||
mainWindow.show();
|
mainWindow.show();
|
||||||
mainWindow.focus();
|
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') {
|
if (process.env.NODE_ENV !== 'development') {
|
||||||
autoUpdater.setFeedUrl('https://updates.kitematic.com/releases/latest?version=' + app.getVersion() + '&beta=' + !!settingsjson.beta + '&platform=' + os.platform());
|
autoUpdater.setFeedUrl('https://updates.kitematic.com/releases/latest?version=' + app.getVersion() + '&beta=' + !!settingsjson.beta + '&platform=' + os.platform());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,10 +10,6 @@ import shell from 'shell';
|
||||||
import machine from '../utils/DockerMachineUtil';
|
import machine from '../utils/DockerMachineUtil';
|
||||||
|
|
||||||
var Containers = React.createClass({
|
var Containers = React.createClass({
|
||||||
contextTypes: {
|
|
||||||
router: React.PropTypes.func
|
|
||||||
},
|
|
||||||
|
|
||||||
getInitialState: function () {
|
getInitialState: function () {
|
||||||
return {
|
return {
|
||||||
sidebarOffset: 0,
|
sidebarOffset: 0,
|
||||||
|
|
@ -52,7 +48,9 @@ var Containers = React.createClass({
|
||||||
let containers = containerStore.getState().containers;
|
let containers = containerStore.getState().containers;
|
||||||
let sorted = this.sorted(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) {
|
if (containerStore.getState().pending) {
|
||||||
this.context.router.transitionTo('pull');
|
this.context.router.transitionTo('pull');
|
||||||
} else if (name && !containers[name]) {
|
} else if (name && !containers[name]) {
|
||||||
|
|
@ -151,7 +149,7 @@ var Containers = React.createClass({
|
||||||
sidebarHeaderClass += ' sep';
|
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 (
|
return (
|
||||||
<div className="containers">
|
<div className="containers">
|
||||||
<Header />
|
<Header />
|
||||||
|
|
|
||||||
|
|
@ -1,79 +1,43 @@
|
||||||
import React from 'react/addons';
|
import React from 'react/addons';
|
||||||
import Router from 'react-router';
|
import Router from 'react-router';
|
||||||
import Radial from './Radial.react.js';
|
import Radial from './Radial.react.js';
|
||||||
import SetupStore from '../stores/SetupStore';
|
|
||||||
import RetinaImage from 'react-retina-image';
|
import RetinaImage from 'react-retina-image';
|
||||||
import Header from './Header.react';
|
import Header from './Header.react';
|
||||||
import Util from '../utils/Util';
|
import Util from '../utils/Util';
|
||||||
import metrics from '../utils/MetricsUtil';
|
import metrics from '../utils/MetricsUtil';
|
||||||
|
import setupStore from '../stores/SetupStore';
|
||||||
|
|
||||||
var Setup = React.createClass({
|
var Setup = React.createClass({
|
||||||
mixins: [ Router.Navigation ],
|
mixins: [Router.Navigation],
|
||||||
|
|
||||||
getInitialState: function () {
|
getInitialState: function () {
|
||||||
return {
|
return setupStore.getState();
|
||||||
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);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
componentDidMount: function () {
|
componentDidMount: function () {
|
||||||
this.update();
|
setupStore.listen(this.update);
|
||||||
},
|
},
|
||||||
|
|
||||||
componentDidUnmount: function () {
|
componentDidUnmount: function () {
|
||||||
SetupStore.removeListener(SetupStore.PROGRESS_EVENT, this.update);
|
setupStore.unlisten(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']);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
update: function () {
|
update: function () {
|
||||||
this.setState({
|
this.setState(setupStore.getState());
|
||||||
progress: SetupStore.percent(),
|
|
||||||
step: SetupStore.step(),
|
|
||||||
error: SetupStore.error(),
|
|
||||||
cancelled: SetupStore.cancelled()
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
|
|
||||||
renderContents: function () {
|
renderContents: function () {
|
||||||
var img = 'virtualbox.png';
|
|
||||||
if (SetupStore.step().name === 'init' || SetupStore.step().name === 'start') {
|
|
||||||
img = 'boot2docker.png';
|
|
||||||
}
|
|
||||||
return (
|
return (
|
||||||
<div className="contents">
|
<div className="contents">
|
||||||
<RetinaImage src={img} checkIfRetinaImgExists={false}/>
|
<RetinaImage src="boot2docker.png" checkIfRetinaImgExists={false}/>
|
||||||
<div className="detail">
|
<div className="detail">
|
||||||
<Radial progress={this.state.progress} thick={true} gray={true}/>
|
<Radial progress={this.state.progress} thick={true} gray={true}/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
renderStep: function () {
|
|
||||||
|
renderProgress: function () {
|
||||||
return (
|
return (
|
||||||
<div className="setup">
|
<div className="setup">
|
||||||
<Header hideLogin={true}/>
|
<Header hideLogin={true}/>
|
||||||
|
|
@ -83,9 +47,8 @@ var Setup = React.createClass({
|
||||||
</div>
|
</div>
|
||||||
<div className="desc">
|
<div className="desc">
|
||||||
<div className="content">
|
<div className="content">
|
||||||
<h4>Step {SetupStore.number()} out of {SetupStore.stepCount()}</h4>
|
<h1>{this.state.title}</h1>
|
||||||
<h1>{SetupStore.step().title}</h1>
|
<p>{this.state.message}</p>
|
||||||
<p>{SetupStore.step().message}</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -141,15 +104,14 @@ var Setup = React.createClass({
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
render: function () {
|
render: function () {
|
||||||
if (this.state.cancelled) {
|
if (this.state.cancelled) {
|
||||||
return this.renderCancelled();
|
return this.renderCancelled();
|
||||||
} else if (this.state.error) {
|
} else if (this.state.error) {
|
||||||
return this.renderError();
|
return this.renderError();
|
||||||
} else if (SetupStore.step()) {
|
|
||||||
return this.renderStep();
|
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return this.renderProgress();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ var metrics = require('./utils/MetricsUtil');
|
||||||
var machine = require('./utils/DockerMachineUtil');
|
var machine = require('./utils/DockerMachineUtil');
|
||||||
var dialog = remote.require('dialog');
|
var dialog = remote.require('dialog');
|
||||||
import docker from './utils/DockerUtil';
|
import docker from './utils/DockerUtil';
|
||||||
|
import Router from 'react-router';
|
||||||
|
|
||||||
// main.js
|
// main.js
|
||||||
var MenuTemplate = function () {
|
var MenuTemplate = function () {
|
||||||
|
|
@ -21,7 +22,7 @@ var MenuTemplate = function () {
|
||||||
metrics.track('Opened About', {
|
metrics.track('Opened About', {
|
||||||
from: 'menu'
|
from: 'menu'
|
||||||
});
|
});
|
||||||
router.get().transitionTo('about');
|
Router.transitionTo('about');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -35,7 +36,7 @@ var MenuTemplate = function () {
|
||||||
metrics.track('Opened Preferences', {
|
metrics.track('Opened Preferences', {
|
||||||
from: 'menu'
|
from: 'menu'
|
||||||
});
|
});
|
||||||
router.get().transitionTo('preferences');
|
Router.transitionTo('preferences');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -16,45 +16,42 @@ import Preferences from './components/Preferences.react';
|
||||||
import About from './components/About.react';
|
import About from './components/About.react';
|
||||||
import NewContainerSearch from './components/NewContainerSearch.react';
|
import NewContainerSearch from './components/NewContainerSearch.react';
|
||||||
import NewContainerPull from './components/NewContainerPull.react';
|
import NewContainerPull from './components/NewContainerPull.react';
|
||||||
import Router from 'react-router';
|
import {Router, IndexRoute, Route, Link} from 'react-router'
|
||||||
|
|
||||||
var Route = Router.Route;
|
|
||||||
var DefaultRoute = Router.DefaultRoute;
|
|
||||||
var RouteHandler = Router.RouteHandler;
|
|
||||||
|
|
||||||
var App = React.createClass({
|
var App = React.createClass({
|
||||||
render: function () {
|
render: function () {
|
||||||
return (
|
return (
|
||||||
<RouteHandler/>
|
<div>
|
||||||
|
{this.props.children}
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
var routes = (
|
var routes = (
|
||||||
<Route name="app" path="/" handler={App}>
|
<Route path="/" component={App}>
|
||||||
<Route name="account" path="account" handler={Account}>
|
<IndexRoute component={Setup}/>
|
||||||
<Route name="signup" path="signup" handler={AccountSignup}/>
|
<Route path="account" component={Account}>
|
||||||
<Route name="login" path="login" handler={AccountLogin}/>
|
<Route path="signup" component={AccountSignup}/>
|
||||||
|
<Route path="login" component={AccountLogin}/>
|
||||||
</Route>
|
</Route>
|
||||||
<Route name="containers" path="containers" handler={Containers}>
|
<Route path="containers" component={Containers}>
|
||||||
<Route name="container" path="details/:name" handler={ContainerDetails}>
|
<Route path="details/:name" component={ContainerDetails}>
|
||||||
<DefaultRoute name="containerHome" handler={ContainerHome} />
|
<IndexRoute component={ContainerHome} />
|
||||||
<Route name="containerLogs" path="logs" handler={ContainerLogs}/>
|
<Route path="logs" component={ContainerLogs}/>
|
||||||
<Route name="containerSettings" path="settings" handler={ContainerSettings}>
|
<Route path="settings" component={ContainerSettings}>
|
||||||
<Route name="containerSettingsGeneral" path="general" handler={ContainerSettingsGeneral}/>
|
<Route path="general" component={ContainerSettingsGeneral}/>
|
||||||
<Route name="containerSettingsPorts" path="ports" handler={ContainerSettingsPorts}/>
|
<Route path="ports" component={ContainerSettingsPorts}/>
|
||||||
<Route name="containerSettingsVolumes" path="volumes" handler={ContainerSettingsVolumes}/>
|
<Route path="volumes" component={ContainerSettingsVolumes}/>
|
||||||
<Route name="containerSettingsAdvanced" path="advanced" handler={ContainerSettingsAdvanced}/>
|
<Route path="advanced" component={ContainerSettingsAdvanced}/>
|
||||||
</Route>
|
</Route>
|
||||||
</Route>
|
</Route>
|
||||||
<Route name="new" path="new">
|
<Route name="new" path="new">
|
||||||
<DefaultRoute name="search" handler={NewContainerSearch}/>
|
<IndexRoute component={NewContainerSearch}/>
|
||||||
<Route name="pull" path="pull" handler={NewContainerPull}></Route>
|
|
||||||
</Route>
|
</Route>
|
||||||
<Route name="preferences" path="preferences" handler={Preferences}/>
|
<Route path="preferences" component={Preferences}/>
|
||||||
<Route name="about" path="about" handler={About}/>
|
<Route path="about" component={About}/>
|
||||||
</Route>
|
</Route>
|
||||||
<DefaultRoute name="setup" handler={Setup}/>
|
|
||||||
</Route>
|
</Route>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,240 +1,22 @@
|
||||||
import {EventEmitter} from 'events';
|
import alt from '../alt';
|
||||||
import _ from 'underscore';
|
import setupActions from '../actions/SetupActions';
|
||||||
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';
|
|
||||||
|
|
||||||
var _currentStep = null;
|
class SetupStore {
|
||||||
var _error = null;
|
constructor () {
|
||||||
var _cancelled = false;
|
this.bindActions(setupActions);
|
||||||
var _retryPromise = null;
|
this.title = '';
|
||||||
var _requiredSteps = [];
|
this.message = '';
|
||||||
|
this.percent = 0;
|
||||||
var _steps = [{
|
this.error = null;
|
||||||
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);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}, {
|
|
||||||
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();
|
error ({error}) {
|
||||||
var packagejson = util.packagejson();
|
this.setState({error});
|
||||||
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();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}];
|
|
||||||
|
|
||||||
var SetupStore = assign(Object.create(EventEmitter.prototype), {
|
progress ({progress}) {
|
||||||
PROGRESS_EVENT: 'setup_progress',
|
this.setState({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();
|
|
||||||
|
|
||||||
required.download = vboxNeedsInstall && (!fs.existsSync(vboxfile) || setupUtil.checksum(vboxfile) !== virtualBox.checksum());
|
export default alt.createStore(SetupStore);
|
||||||
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;
|
|
||||||
|
|
|
||||||
|
|
@ -1,113 +1,81 @@
|
||||||
import _ from 'underscore';
|
import _ from 'underscore';
|
||||||
import crypto from 'crypto';
|
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import request from 'request';
|
|
||||||
import progress from 'request-progress';
|
|
||||||
import Promise from 'bluebird';
|
import Promise from 'bluebird';
|
||||||
import util from './Util';
|
import util from './Util';
|
||||||
import resources from './ResourcesUtil';
|
import bugsnag from 'bugsnag-js';
|
||||||
var virtualBox = require ('./VirtualBoxUtil');
|
import virtualBox from './VirtualBoxUtil';
|
||||||
|
import setupActions from '../actions/SetupActions';
|
||||||
|
import metrics from './MetricsUtil';
|
||||||
|
import machine from './DockerMachineUtil';
|
||||||
|
import docker from './DockerUtil';
|
||||||
|
|
||||||
var SetupUtil = {
|
let _retryPromise = null;
|
||||||
needsBinaryFix() {
|
|
||||||
return this.pathDoesNotExistOrDenied(util.binsPath()) ||
|
export default {
|
||||||
(fs.existsSync(util.dockerBinPath()) && this.pathDenied(util.dockerBinPath())) ||
|
simulateProgress (estimateSeconds, progress) {
|
||||||
(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) {
|
|
||||||
var times = _.range(0, estimateSeconds * 1000, 200);
|
var times = _.range(0, estimateSeconds * 1000, 200);
|
||||||
var timers = [];
|
var timers = [];
|
||||||
_.each(times, time => {
|
_.each(times, time => {
|
||||||
var timer = setTimeout(() => {
|
var timer = setTimeout(() => {
|
||||||
progress(100 * time / (estimateSeconds * 1000));
|
setupActions.progress(100 * time / (estimateSeconds * 1000));
|
||||||
}, time);
|
}, time);
|
||||||
timers.push(timer);
|
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 => {
|
retry (removeVM) {
|
||||||
if (percentCallback) {
|
if (removeVM) {
|
||||||
percentCallback(state.percent);
|
machine.rm().finally(() => {
|
||||||
}
|
console.log('machine removed');
|
||||||
}).on('error', err => {
|
_retryPromise.resolve();
|
||||||
reject(err);
|
|
||||||
}).pipe(fs.createWriteStream(filename)).on('error', err => {
|
|
||||||
reject(err);
|
|
||||||
}).on('close', err => {
|
|
||||||
if (err) {
|
|
||||||
reject(err);
|
|
||||||
}
|
|
||||||
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;
|
|
||||||
|
|
|
||||||
|
|
@ -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;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
@ -10,15 +10,6 @@ var VirtualBox = {
|
||||||
return '/Applications/VirtualBox.app/Contents/MacOS/VBoxManage';
|
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 () {
|
installed: function () {
|
||||||
if(util.isWindows()) {
|
if(util.isWindows()) {
|
||||||
return fs.existsSync('C:\\Program Files\\Oracle\\VirtualBox\\VBoxManage.exe') && fs.existsSync('C:\\Program Files\\Oracle\\VirtualBox\\VirtualBox.exe');
|
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) {
|
vmExists: function (name) {
|
||||||
return new Promise((resolve, reject) => {
|
return util.exec([this.command(), 'showvminfo', name]).then(() => {
|
||||||
util.exec([this.command(), 'showvminfo', name, '--machinereadable']).then(stdout => {
|
return true;
|
||||||
var match = stdout.match(/VMState="(\w+)"/);
|
}).catch((err) => {
|
||||||
if (!match) {
|
return false;
|
||||||
reject('Could not parse VMState');
|
|
||||||
}
|
|
||||||
resolve(match[1]);
|
|
||||||
}).catch(reject);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue