зеркало из https://github.com/docker/kitematic.git
0.5.3 setup stability, errors & retrying
This commit is contained in:
Родитель
bf0abc1d2c
Коммит
9b32a6a680
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "Kitematic",
|
"name": "Kitematic",
|
||||||
"version": "0.5.2",
|
"version": "0.5.3",
|
||||||
"author": "Kitematic",
|
"author": "Kitematic",
|
||||||
"description": "Simple Docker Container management for Mac OS X.",
|
"description": "Simple Docker Container management for Mac OS X.",
|
||||||
"homepage": "https://kitematic.com/",
|
"homepage": "https://kitematic.com/",
|
||||||
|
|
|
@ -28,7 +28,7 @@ var ContainerDetail = React.createClass({
|
||||||
<div className="details">
|
<div className="details">
|
||||||
<ContainerDetailsHeader container={this.props.container}/>
|
<ContainerDetailsHeader container={this.props.container}/>
|
||||||
<ContainerDetailsSubheader container={this.props.container} />
|
<ContainerDetailsSubheader container={this.props.container} />
|
||||||
<Router.RouteHandler container={this.props.container}/>
|
<Router.RouteHandler container={this.props.container} error={this.props.error}/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ var ContainerHomePreview = require('./ContainerHomePreview.react');
|
||||||
var ContainerHomeLogs = require('./ContainerHomeLogs.react');
|
var ContainerHomeLogs = require('./ContainerHomeLogs.react');
|
||||||
var ContainerHomeFolders = require('./ContainerHomeFolders.react');
|
var ContainerHomeFolders = require('./ContainerHomeFolders.react');
|
||||||
var ContainerUtil = require('./ContainerUtil');
|
var ContainerUtil = require('./ContainerUtil');
|
||||||
var webPorts = require('./Util').webPorts;
|
var util = require('./Util');
|
||||||
|
|
||||||
var resizeWindow = function () {
|
var resizeWindow = function () {
|
||||||
$('.left .wrapper').height(window.innerHeight - 240);
|
$('.left .wrapper').height(window.innerHeight - 240);
|
||||||
|
@ -27,6 +27,9 @@ var ContainerHome = React.createClass({
|
||||||
handleResize: function () {
|
handleResize: function () {
|
||||||
resizeWindow();
|
resizeWindow();
|
||||||
},
|
},
|
||||||
|
handleErrorClick: function () {
|
||||||
|
util.exec(['open', 'https://github.com/kitematic/kitematic/issues/new']);
|
||||||
|
},
|
||||||
componentWillReceiveProps: function () {
|
componentWillReceiveProps: function () {
|
||||||
this.init();
|
this.init();
|
||||||
},
|
},
|
||||||
|
@ -52,7 +55,7 @@ var ContainerHome = React.createClass({
|
||||||
this.setState({
|
this.setState({
|
||||||
ports: ports,
|
ports: ports,
|
||||||
defaultPort: _.find(_.keys(ports), function (port) {
|
defaultPort: _.find(_.keys(ports), function (port) {
|
||||||
return webPorts.indexOf(port) !== -1;
|
return util.webPorts.indexOf(port) !== -1;
|
||||||
}),
|
}),
|
||||||
progress: ContainerStore.progress(this.getParams().name),
|
progress: ContainerStore.progress(this.getParams().name),
|
||||||
blocked: ContainerStore.blocked(this.getParams().name)
|
blocked: ContainerStore.blocked(this.getParams().name)
|
||||||
|
@ -68,7 +71,14 @@ var ContainerHome = React.createClass({
|
||||||
},
|
},
|
||||||
render: function () {
|
render: function () {
|
||||||
var body;
|
var body;
|
||||||
if (this.props.container && this.props.container.State.Downloading) {
|
if (this.props.error) {
|
||||||
|
body = (
|
||||||
|
<div className="details-progress">
|
||||||
|
<h3>There was a problem connecting to the Docker Engine.<br/>Either the VirtualBox VM was removed, is not responding or Docker is not running inside of it. Try restarting Kitematic. If the issue persists, please <a onClick={this.handleErrorClick}>file a ticket on our GitHub repo.</a></h3>
|
||||||
|
<Radial progress={100} error={true} thick={true} transparent={true}/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
} else if (this.props.container && this.props.container.State.Downloading) {
|
||||||
if (this.state.progress) {
|
if (this.state.progress) {
|
||||||
body = (
|
body = (
|
||||||
<div className="details-progress">
|
<div className="details-progress">
|
||||||
|
|
|
@ -7,17 +7,20 @@ var docker = require('./Docker');
|
||||||
var metrics = require('./Metrics');
|
var metrics = require('./Metrics');
|
||||||
var registry = require('./Registry');
|
var registry = require('./Registry');
|
||||||
var LogStore = require('./LogStore');
|
var LogStore = require('./LogStore');
|
||||||
|
var bugsnag = require('bugsnag-js');
|
||||||
|
|
||||||
var _placeholders = {};
|
var _placeholders = {};
|
||||||
var _containers = {};
|
var _containers = {};
|
||||||
var _progress = {};
|
var _progress = {};
|
||||||
var _muted = {};
|
var _muted = {};
|
||||||
var _blocked = {};
|
var _blocked = {};
|
||||||
|
var _error = null;
|
||||||
|
|
||||||
var ContainerStore = assign(Object.create(EventEmitter.prototype), {
|
var ContainerStore = assign(Object.create(EventEmitter.prototype), {
|
||||||
CLIENT_CONTAINER_EVENT: 'client_container_event',
|
CLIENT_CONTAINER_EVENT: 'client_container_event',
|
||||||
SERVER_CONTAINER_EVENT: 'server_container_event',
|
SERVER_CONTAINER_EVENT: 'server_container_event',
|
||||||
SERVER_PROGRESS_EVENT: 'server_progress_event',
|
SERVER_PROGRESS_EVENT: 'server_progress_event',
|
||||||
|
SERVER_ERROR_EVENT: 'server_error_event',
|
||||||
_pullImage: function (repository, tag, callback, progressCallback, blockedCallback) {
|
_pullImage: function (repository, tag, callback, progressCallback, blockedCallback) {
|
||||||
registry.layers(repository, tag, (err, layerSizes) => {
|
registry.layers(repository, tag, (err, layerSizes) => {
|
||||||
|
|
||||||
|
@ -35,6 +38,10 @@ var ContainerStore = assign(Object.create(EventEmitter.prototype), {
|
||||||
|
|
||||||
var totalBytes = layersToDownload.map(function (s) { return s.size; }).reduce(function (pv, sv) { return pv + sv; }, 0);
|
var totalBytes = layersToDownload.map(function (s) { return s.size; }).reduce(function (pv, sv) { return pv + sv; }, 0);
|
||||||
docker.client().pull(repository + ':' + tag, (err, stream) => {
|
docker.client().pull(repository + ':' + tag, (err, stream) => {
|
||||||
|
if (err) {
|
||||||
|
callback(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
stream.setEncoding('utf8');
|
stream.setEncoding('utf8');
|
||||||
|
|
||||||
var layerProgress = layersToDownload.reduce(function (r, layer) {
|
var layerProgress = layersToDownload.reduce(function (r, layer) {
|
||||||
|
@ -50,7 +57,7 @@ var ContainerStore = assign(Object.create(EventEmitter.prototype), {
|
||||||
var data = JSON.parse(str);
|
var data = JSON.parse(str);
|
||||||
console.log(data);
|
console.log(data);
|
||||||
|
|
||||||
if (data.status === 'Pulling dependent layers') {
|
if (data.status === 'Pulling dependent layers' || data.status.indexOf('already being pulled by another client') !== -1) {
|
||||||
blockedCallback();
|
blockedCallback();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -153,7 +160,7 @@ var ContainerStore = assign(Object.create(EventEmitter.prototype), {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
_resumePulling: function () {
|
_resumePulling: function (callback) {
|
||||||
var downloading = _.filter(_.values(this.containers()), function (container) {
|
var downloading = _.filter(_.values(this.containers()), function (container) {
|
||||||
return container.State.Downloading;
|
return container.State.Downloading;
|
||||||
});
|
});
|
||||||
|
@ -163,12 +170,20 @@ var ContainerStore = assign(Object.create(EventEmitter.prototype), {
|
||||||
downloading.forEach(function (container) {
|
downloading.forEach(function (container) {
|
||||||
_progress[container.Name] = 99;
|
_progress[container.Name] = 99;
|
||||||
docker.client().pull(container.Config.Image, function (err, stream) {
|
docker.client().pull(container.Config.Image, function (err, stream) {
|
||||||
|
if (err) {
|
||||||
|
callback(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
stream.setEncoding('utf8');
|
stream.setEncoding('utf8');
|
||||||
stream.on('data', function () {});
|
stream.on('data', function () {});
|
||||||
stream.on('end', function () {
|
stream.on('end', function () {
|
||||||
delete _placeholders[container.Name];
|
delete _placeholders[container.Name];
|
||||||
localStorage.setItem('store.placeholders', JSON.stringify(_placeholders));
|
localStorage.setItem('store.placeholders', JSON.stringify(_placeholders));
|
||||||
self._createContainer(container.Name, {Image: container.Config.Image}, function () {
|
self._createContainer(container.Name, {Image: container.Config.Image}, err => {
|
||||||
|
if (err) {
|
||||||
|
callback(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
self.emit(self.SERVER_PROGRESS_EVENT, container.Name);
|
self.emit(self.SERVER_PROGRESS_EVENT, container.Name);
|
||||||
self.emit(self.CLIENT_CONTAINER_EVENT, container.Name);
|
self.emit(self.CLIENT_CONTAINER_EVENT, container.Name);
|
||||||
});
|
});
|
||||||
|
@ -176,13 +191,17 @@ var ContainerStore = assign(Object.create(EventEmitter.prototype), {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
_startListeningToEvents: function () {
|
_startListeningToEvents: function (callback) {
|
||||||
docker.client().getEvents(function (err, stream) {
|
docker.client().getEvents((err, stream) => {
|
||||||
|
if (err) {
|
||||||
|
callback(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (stream) {
|
if (stream) {
|
||||||
stream.setEncoding('utf8');
|
stream.setEncoding('utf8');
|
||||||
stream.on('data', this._dockerEvent.bind(this));
|
stream.on('data', this._dockerEvent.bind(this));
|
||||||
}
|
}
|
||||||
}.bind(this));
|
});
|
||||||
},
|
},
|
||||||
_dockerEvent: function (json) {
|
_dockerEvent: function (json) {
|
||||||
var data = JSON.parse(json);
|
var data = JSON.parse(json);
|
||||||
|
@ -200,7 +219,7 @@ var ContainerStore = assign(Object.create(EventEmitter.prototype), {
|
||||||
this.emit(this.SERVER_CONTAINER_EVENT, data.status);
|
this.emit(this.SERVER_CONTAINER_EVENT, data.status);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.fetchContainer(data.id, function (err) {
|
this.fetchContainer(data.id, err => {
|
||||||
if (err) {
|
if (err) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -209,13 +228,16 @@ var ContainerStore = assign(Object.create(EventEmitter.prototype), {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.emit(this.SERVER_CONTAINER_EVENT, container ? container.Name : null, data.status);
|
this.emit(this.SERVER_CONTAINER_EVENT, container ? container.Name : null, data.status);
|
||||||
}.bind(this));
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
init: function (callback) {
|
init: function (callback) {
|
||||||
// TODO: Load cached data from db on loading
|
// TODO: Load cached data from db on loading
|
||||||
this.fetchAllContainers(function (err) {
|
this.fetchAllContainers(err => {
|
||||||
if (err) {
|
if (err) {
|
||||||
|
_error = err;
|
||||||
|
this.emit(this.SERVER_ERROR_EVENT, err);
|
||||||
|
bugsnag.notify(err, 'Container Store failed to init', err);
|
||||||
callback(err);
|
callback(err);
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
|
@ -227,12 +249,20 @@ var ContainerStore = assign(Object.create(EventEmitter.prototype), {
|
||||||
localStorage.setItem('store.placeholders', JSON.stringify(_placeholders));
|
localStorage.setItem('store.placeholders', JSON.stringify(_placeholders));
|
||||||
}
|
}
|
||||||
this.emit(this.CLIENT_CONTAINER_EVENT);
|
this.emit(this.CLIENT_CONTAINER_EVENT);
|
||||||
this._resumePulling();
|
this._resumePulling(err => {
|
||||||
this._startListeningToEvents();
|
_error = err;
|
||||||
}.bind(this));
|
this.emit(this.SERVER_ERROR_EVENT, err);
|
||||||
|
bugsnag.notify(err, 'Container Store failed to resume pulling', err);
|
||||||
|
});
|
||||||
|
this._startListeningToEvents(err => {
|
||||||
|
_error = err;
|
||||||
|
this.emit(this.SERVER_ERROR_EVENT, err);
|
||||||
|
bugsnag.notify(err, 'Container Store failed to listen to events', err);
|
||||||
|
});
|
||||||
|
});
|
||||||
},
|
},
|
||||||
fetchContainer: function (id, callback) {
|
fetchContainer: function (id, callback) {
|
||||||
docker.client().getContainer(id).inspect(function (err, container) {
|
docker.client().getContainer(id).inspect((err, container) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
callback(err);
|
callback(err);
|
||||||
} else {
|
} else {
|
||||||
|
@ -245,11 +275,10 @@ var ContainerStore = assign(Object.create(EventEmitter.prototype), {
|
||||||
_containers[container.Name] = container;
|
_containers[container.Name] = container;
|
||||||
callback(null, container);
|
callback(null, container);
|
||||||
}
|
}
|
||||||
}.bind(this));
|
});
|
||||||
},
|
},
|
||||||
fetchAllContainers: function (callback) {
|
fetchAllContainers: function (callback) {
|
||||||
var self = this;
|
docker.client().listContainers({all: true}, (err, containers) => {
|
||||||
docker.client().listContainers({all: true}, function (err, containers) {
|
|
||||||
if (err) {
|
if (err) {
|
||||||
callback(err);
|
callback(err);
|
||||||
return;
|
return;
|
||||||
|
@ -260,8 +289,8 @@ var ContainerStore = assign(Object.create(EventEmitter.prototype), {
|
||||||
delete _containers[name];
|
delete _containers[name];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
async.each(containers, function (container, callback) {
|
async.each(containers, (container, callback) => {
|
||||||
self.fetchContainer(container.Id, function (err) {
|
this.fetchContainer(container.Id, function (err) {
|
||||||
callback(err);
|
callback(err);
|
||||||
});
|
});
|
||||||
}, function (err) {
|
}, function (err) {
|
||||||
|
@ -289,14 +318,28 @@ var ContainerStore = assign(Object.create(EventEmitter.prototype), {
|
||||||
|
|
||||||
_muted[containerName] = true;
|
_muted[containerName] = true;
|
||||||
_progress[containerName] = 0;
|
_progress[containerName] = 0;
|
||||||
this._pullImage(repository, tag, () => {
|
this._pullImage(repository, tag, err => {
|
||||||
|
if (err) {
|
||||||
|
_error = err;
|
||||||
|
this.emit(this.SERVER_ERROR_EVENT, err);
|
||||||
|
bugsnag.notify(err, 'Container Store failed to create container', err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_error = null;
|
||||||
_blocked[containerName] = false;
|
_blocked[containerName] = false;
|
||||||
if (!_placeholders[containerName]) {
|
if (!_placeholders[containerName]) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
delete _placeholders[containerName];
|
delete _placeholders[containerName];
|
||||||
localStorage.setItem('store.placeholders', JSON.stringify(_placeholders));
|
localStorage.setItem('store.placeholders', JSON.stringify(_placeholders));
|
||||||
this._createContainer(containerName, {Image: imageName}, () => {
|
this._createContainer(containerName, {Image: imageName}, err => {
|
||||||
|
if (err) {
|
||||||
|
console.log(err);
|
||||||
|
_error = err;
|
||||||
|
this.emit(this.SERVER_ERROR_EVENT, err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_error = null;
|
||||||
metrics.track('Container Finished Creating');
|
metrics.track('Container Finished Creating');
|
||||||
delete _progress[containerName];
|
delete _progress[containerName];
|
||||||
_muted[containerName] = false;
|
_muted[containerName] = false;
|
||||||
|
@ -321,10 +364,10 @@ var ContainerStore = assign(Object.create(EventEmitter.prototype), {
|
||||||
LogStore.rename(name, data.name);
|
LogStore.rename(name, data.name);
|
||||||
}
|
}
|
||||||
var fullData = assign(_containers[name], data);
|
var fullData = assign(_containers[name], data);
|
||||||
this._createContainer(name, fullData, function (err) {
|
this._createContainer(name, fullData, function () {
|
||||||
_muted[name] = false;
|
_muted[name] = false;
|
||||||
this.emit(this.CLIENT_CONTAINER_EVENT, name);
|
this.emit(this.CLIENT_CONTAINER_EVENT, name);
|
||||||
callback(err);
|
callback();
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
},
|
},
|
||||||
rename: function (name, newName, callback) {
|
rename: function (name, newName, callback) {
|
||||||
|
@ -417,6 +460,9 @@ var ContainerStore = assign(Object.create(EventEmitter.prototype), {
|
||||||
},
|
},
|
||||||
blocked: function (name) {
|
blocked: function (name) {
|
||||||
return !!_blocked[name];
|
return !!_blocked[name];
|
||||||
|
},
|
||||||
|
error: function () {
|
||||||
|
return _error;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -20,11 +20,13 @@ var Containers = React.createClass({
|
||||||
containers: ContainerStore.containers(),
|
containers: ContainerStore.containers(),
|
||||||
sorted: ContainerStore.sorted(),
|
sorted: ContainerStore.sorted(),
|
||||||
updateAvailable: false,
|
updateAvailable: false,
|
||||||
currentButtonLabel: ''
|
currentButtonLabel: '',
|
||||||
|
error: ContainerStore.error()
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
componentDidMount: function () {
|
componentDidMount: function () {
|
||||||
this.update();
|
this.update();
|
||||||
|
ContainerStore.on(ContainerStore.SERVER_ERROR_EVENT, this.updateError);
|
||||||
ContainerStore.on(ContainerStore.SERVER_CONTAINER_EVENT, this.update);
|
ContainerStore.on(ContainerStore.SERVER_CONTAINER_EVENT, this.update);
|
||||||
ContainerStore.on(ContainerStore.CLIENT_CONTAINER_EVENT, this.updateFromClient);
|
ContainerStore.on(ContainerStore.CLIENT_CONTAINER_EVENT, this.updateFromClient);
|
||||||
|
|
||||||
|
@ -52,6 +54,11 @@ var Containers = React.createClass({
|
||||||
this.transitionTo('containers');
|
this.transitionTo('containers');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
updateError: function (err) {
|
||||||
|
this.setState({
|
||||||
|
error: err
|
||||||
|
});
|
||||||
|
},
|
||||||
update: function (name, status) {
|
update: function (name, status) {
|
||||||
this.setState({
|
this.setState({
|
||||||
containers: ContainerStore.containers(),
|
containers: ContainerStore.containers(),
|
||||||
|
@ -175,7 +182,7 @@ var Containers = React.createClass({
|
||||||
<div className="sidebar-buttons-padding"></div>
|
<div className="sidebar-buttons-padding"></div>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
<Router.RouteHandler container={container}/>
|
<Router.RouteHandler container={container} error={this.state.error}/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -23,10 +23,7 @@ var ImageCard = React.createClass({
|
||||||
},
|
},
|
||||||
handleClick: function (name) {
|
handleClick: function (name) {
|
||||||
metrics.track('Created Container');
|
metrics.track('Created Container');
|
||||||
ContainerStore.create(name, this.state.chosenTag, function (err) {
|
ContainerStore.create(name, this.state.chosenTag, function () {
|
||||||
if (err) {
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
$(document.body).find('.new-container-item').parent().fadeOut();
|
$(document.body).find('.new-container-item').parent().fadeOut();
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
},
|
},
|
||||||
|
|
14
src/Main.js
14
src/Main.js
|
@ -6,13 +6,13 @@ var fs = require('fs');
|
||||||
var path = require('path');
|
var path = require('path');
|
||||||
var docker = require('./Docker');
|
var docker = require('./Docker');
|
||||||
var router = require('./Router');
|
var router = require('./Router');
|
||||||
var machine = require('./DockerMachine');
|
|
||||||
var ContainerStore = require('./ContainerStore');
|
var ContainerStore = require('./ContainerStore');
|
||||||
var SetupStore = require('./SetupStore');
|
var SetupStore = require('./SetupStore');
|
||||||
var metrics = require('./Metrics');
|
var metrics = require('./Metrics');
|
||||||
var template = require('./MenuTemplate');
|
var template = require('./MenuTemplate');
|
||||||
var util = require('./Util');
|
var util = require('./Util');
|
||||||
var Menu = remote.require('menu');
|
var Menu = remote.require('menu');
|
||||||
|
var bugsnag = require('bugsnag-js');
|
||||||
|
|
||||||
window.addEventListener('resize', function () {
|
window.addEventListener('resize', function () {
|
||||||
fs.writeFileSync(path.join(util.supportDir(), 'size'), JSON.stringify({
|
fs.writeFileSync(path.join(util.supportDir(), 'size'), JSON.stringify({
|
||||||
|
@ -38,7 +38,6 @@ if (process.env.NODE_ENV === 'development') {
|
||||||
head.appendChild(script);
|
head.appendChild(script);
|
||||||
}
|
}
|
||||||
|
|
||||||
var bugsnag = require('bugsnag-js');
|
|
||||||
bugsnag.apiKey = settingsjson.bugsnag;
|
bugsnag.apiKey = settingsjson.bugsnag;
|
||||||
bugsnag.autoNotify = true;
|
bugsnag.autoNotify = true;
|
||||||
bugsnag.releaseStage = process.env.NODE_ENV === 'development' ? 'development' : 'production';
|
bugsnag.releaseStage = process.env.NODE_ENV === 'development' ? 'development' : 'production';
|
||||||
|
@ -81,18 +80,25 @@ setInterval(function () {
|
||||||
}, 14400000);
|
}, 14400000);
|
||||||
|
|
||||||
router.run(Handler => React.render(<Handler/>, document.body));
|
router.run(Handler => React.render(<Handler/>, document.body));
|
||||||
SetupStore.run().then(machine.info).then(machine => {
|
SetupStore.setup().then(machine => {
|
||||||
|
console.log('setup complete');
|
||||||
|
console.log(machine);
|
||||||
docker.setup(machine.url, machine.name);
|
docker.setup(machine.url, machine.name);
|
||||||
Menu.setApplicationMenu(Menu.buildFromTemplate(template()));
|
Menu.setApplicationMenu(Menu.buildFromTemplate(template()));
|
||||||
|
ContainerStore.on(ContainerStore.SERVER_ERROR_EVENT, (err) => {
|
||||||
|
bugsnag.notify(err);
|
||||||
|
});
|
||||||
ContainerStore.init(function (err) {
|
ContainerStore.init(function (err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
|
console.log(err);
|
||||||
bugsnag.notify(err);
|
bugsnag.notify(err);
|
||||||
}
|
}
|
||||||
router.transitionTo('containers');
|
router.transitionTo('containers');
|
||||||
});
|
});
|
||||||
}).catch(err => {
|
}).catch(err => {
|
||||||
metrics.track('Setup Failed', {
|
metrics.track('Setup Failed', {
|
||||||
step: SetupStore.step(),
|
step: 'catch',
|
||||||
|
message: err.message
|
||||||
});
|
});
|
||||||
bugsnag.notify(err);
|
bugsnag.notify(err);
|
||||||
console.log(err);
|
console.log(err);
|
||||||
|
|
|
@ -3,7 +3,7 @@ var React = require('react/addons');
|
||||||
var Radial = React.createClass({
|
var Radial = React.createClass({
|
||||||
render: function () {
|
render: function () {
|
||||||
var percentage;
|
var percentage;
|
||||||
if ((this.props.progress !== null && this.props.progress !== undefined) && !this.props.spin) {
|
if ((this.props.progress !== null && this.props.progress !== undefined) && !this.props.spin && !this.props.error) {
|
||||||
percentage = (
|
percentage = (
|
||||||
<div className="percentage"></div>
|
<div className="percentage"></div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -6,13 +6,15 @@ var RetinaImage = require('react-retina-image');
|
||||||
var Header = require('./Header.react');
|
var Header = require('./Header.react');
|
||||||
var Util = require('./Util');
|
var Util = require('./Util');
|
||||||
var metrics = require('./Metrics');
|
var metrics = require('./Metrics');
|
||||||
|
var machine = require('./DockerMachine');
|
||||||
|
|
||||||
var Setup = React.createClass({
|
var Setup = React.createClass({
|
||||||
mixins: [ Router.Navigation ],
|
mixins: [ Router.Navigation ],
|
||||||
getInitialState: function () {
|
getInitialState: function () {
|
||||||
return {
|
return {
|
||||||
progress: 0,
|
progress: 0,
|
||||||
name: ''
|
name: '',
|
||||||
|
retrying: false
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
componentWillMount: function () {
|
componentWillMount: function () {
|
||||||
|
@ -28,10 +30,26 @@ var Setup = React.createClass({
|
||||||
SetupStore.removeListener(SetupStore.STEP_EVENT, this.update);
|
SetupStore.removeListener(SetupStore.STEP_EVENT, this.update);
|
||||||
SetupStore.removeListener(SetupStore.ERROR_EVENT, this.update);
|
SetupStore.removeListener(SetupStore.ERROR_EVENT, this.update);
|
||||||
},
|
},
|
||||||
handleRetry: function () {
|
handleCancelRetry: function () {
|
||||||
metrics.track('Setup Retried');
|
metrics.track('Setup Retried', {
|
||||||
|
from: 'cancel'
|
||||||
|
});
|
||||||
SetupStore.retry();
|
SetupStore.retry();
|
||||||
},
|
},
|
||||||
|
handleErrorRetry: function () {
|
||||||
|
this.setState({
|
||||||
|
retrying: true
|
||||||
|
});
|
||||||
|
metrics.track('Setup Retried', {
|
||||||
|
from: 'error'
|
||||||
|
});
|
||||||
|
machine.stop().finally(() => {
|
||||||
|
this.setState({
|
||||||
|
retrying: false
|
||||||
|
});
|
||||||
|
SetupStore.retry();
|
||||||
|
});
|
||||||
|
},
|
||||||
handleOpenWebsite: function () {
|
handleOpenWebsite: function () {
|
||||||
Util.exec(['open', 'https://www.virtualbox.org/wiki/Downloads']);
|
Util.exec(['open', 'https://www.virtualbox.org/wiki/Downloads']);
|
||||||
},
|
},
|
||||||
|
@ -110,21 +128,21 @@ var Setup = React.createClass({
|
||||||
<h1>We're Sorry!</h1>
|
<h1>We're Sorry!</h1>
|
||||||
<p>There seems to have been an unexpected error with Kitematic:</p>
|
<p>There seems to have been an unexpected error with Kitematic:</p>
|
||||||
<p className="error">{this.state.error}<br />{this.state.error.message}</p>
|
<p className="error">{this.state.error}<br />{this.state.error.message}</p>
|
||||||
|
<p><button className="btn btn-action" disabled={this.state.retrying} onClick={this.handleErrorRetry}>Retry Setup</button></p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
render: function () {
|
render: function () {
|
||||||
if (!SetupStore.step()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
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 {
|
} else if (SetupStore.step()) {
|
||||||
return this.renderStep();
|
return this.renderStep();
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -10,6 +10,7 @@ var util = require('./Util');
|
||||||
var assign = require('object-assign');
|
var assign = require('object-assign');
|
||||||
var metrics = require('./Metrics');
|
var metrics = require('./Metrics');
|
||||||
var bugsnag = require('bugsnag-js');
|
var bugsnag = require('bugsnag-js');
|
||||||
|
var rimraf = require('rimraf');
|
||||||
|
|
||||||
var _currentStep = null;
|
var _currentStep = null;
|
||||||
var _error = null;
|
var _error = null;
|
||||||
|
@ -59,26 +60,25 @@ var _steps = [{
|
||||||
message: 'To run Docker containers on your computer, Kitematic is starting a Linux virutal machine. This may take a minute...',
|
message: 'To run Docker containers on your computer, Kitematic is starting a Linux virutal machine. This may take a minute...',
|
||||||
totalPercent: 60,
|
totalPercent: 60,
|
||||||
percent: 0,
|
percent: 0,
|
||||||
seconds: 52,
|
seconds: 53,
|
||||||
run: Promise.coroutine(function* (progressCallback) {
|
run: Promise.coroutine(function* (progressCallback) {
|
||||||
setupUtil.simulateProgress(this.seconds, progressCallback);
|
setupUtil.simulateProgress(this.seconds, progressCallback);
|
||||||
yield virtualBox.vmdestroy('kitematic-vm');
|
yield virtualBox.vmdestroy('kitematic-vm');
|
||||||
var exists = yield machine.exists();
|
var exists = yield machine.exists();
|
||||||
if (!exists) {
|
if (!exists || (yield machine.state()) === 'Error') {
|
||||||
yield machine.create();
|
|
||||||
return;
|
|
||||||
} else if ((yield machine.state()) === 'Error') {
|
|
||||||
try {
|
try {
|
||||||
yield machine.rm();
|
yield machine.rm();
|
||||||
} catch (err) {}
|
yield machine.create();
|
||||||
yield machine.create();
|
} catch (err) {
|
||||||
|
rimraf.sync(path.join(util.home(), '.docker', 'machine', 'machines', machine.name()));
|
||||||
|
yield machine.create();
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var isoversion = machine.isoversion();
|
var isoversion = machine.isoversion();
|
||||||
var packagejson = util.packagejson();
|
var packagejson = util.packagejson();
|
||||||
if (!isoversion || setupUtil.compareVersions(isoversion, packagejson['docker-version']) < 0) {
|
if (!isoversion || setupUtil.compareVersions(isoversion, packagejson['docker-version']) < 0) {
|
||||||
console.log('upgrading');
|
|
||||||
yield machine.stop();
|
yield machine.stop();
|
||||||
yield machine.upgrade();
|
yield machine.upgrade();
|
||||||
}
|
}
|
||||||
|
@ -113,15 +113,21 @@ var SetupStore = assign(Object.create(EventEmitter.prototype), {
|
||||||
error: function () {
|
error: function () {
|
||||||
return _error;
|
return _error;
|
||||||
},
|
},
|
||||||
|
setError: function (error) {
|
||||||
|
_error = error;
|
||||||
|
this.emit(this.ERROR_EVENT);
|
||||||
|
},
|
||||||
cancelled: function () {
|
cancelled: function () {
|
||||||
return _cancelled;
|
return _cancelled;
|
||||||
},
|
},
|
||||||
retry: function () {
|
retry: function () {
|
||||||
_error = null;
|
_error = null;
|
||||||
_cancelled = false;
|
_cancelled = false;
|
||||||
_retryPromise.resolve();
|
if (_retryPromise) {
|
||||||
|
_retryPromise.resolve();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
wait: function () {
|
pause: function () {
|
||||||
_retryPromise = Promise.defer();
|
_retryPromise = Promise.defer();
|
||||||
return _retryPromise.promise;
|
return _retryPromise.promise;
|
||||||
},
|
},
|
||||||
|
@ -141,7 +147,7 @@ var SetupStore = assign(Object.create(EventEmitter.prototype), {
|
||||||
if (isoversion && setupUtil.compareVersions(isoversion, packagejson['docker-version']) < 0) {
|
if (isoversion && setupUtil.compareVersions(isoversion, packagejson['docker-version']) < 0) {
|
||||||
this.steps().init.seconds = 33;
|
this.steps().init.seconds = 33;
|
||||||
} else if (exists && (yield machine.state()) !== 'Error') {
|
} else if (exists && (yield machine.state()) !== 'Error') {
|
||||||
this.steps().init.seconds = 13;
|
this.steps().init.seconds = 23;
|
||||||
}
|
}
|
||||||
|
|
||||||
_requiredSteps = _steps.filter(function (step) {
|
_requiredSteps = _steps.filter(function (step) {
|
||||||
|
@ -196,12 +202,30 @@ var SetupStore = assign(Object.create(EventEmitter.prototype), {
|
||||||
_cancelled = true;
|
_cancelled = true;
|
||||||
this.emit(this.STEP_EVENT);
|
this.emit(this.STEP_EVENT);
|
||||||
}
|
}
|
||||||
yield this.wait();
|
yield this.pause();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
metrics.track('Finished Setup');
|
|
||||||
_currentStep = null;
|
_currentStep = null;
|
||||||
|
return yield machine.info();
|
||||||
|
}),
|
||||||
|
setup: Promise.coroutine(function * () {
|
||||||
|
while (true) {
|
||||||
|
var info = yield this.run();
|
||||||
|
console.log(info);
|
||||||
|
if (!info.url) {
|
||||||
|
metrics.track('Setup Failed', {
|
||||||
|
step: 'done',
|
||||||
|
message: 'Machine URL not set'
|
||||||
|
});
|
||||||
|
bugsnag.notify('SetupError', 'Machine url was not set', machine);
|
||||||
|
SetupStore.setError('Could not reach the Docker Engine inside the VirtualBox VM');
|
||||||
|
yield this.pause();
|
||||||
|
} else {
|
||||||
|
metrics.track('Finished Setup');
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ module.exports = {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
exec(args, options, (stderr, stdout, code) => {
|
exec(args, options, (stderr, stdout, code) => {
|
||||||
if (code) {
|
if (code) {
|
||||||
reject(stderr);
|
reject(stderr || args.join(' ').replace(this.home(), '') + 'returned non zero exit code');
|
||||||
}
|
}
|
||||||
resolve(stdout);
|
resolve(stdout);
|
||||||
});
|
});
|
||||||
|
|
|
@ -122,7 +122,7 @@
|
||||||
.details-progress {
|
.details-progress {
|
||||||
margin: 20% auto 0;
|
margin: 20% auto 0;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
width: 300px;
|
width: 400px;
|
||||||
h2 {
|
h2 {
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,4 +8,5 @@ pkill VBox
|
||||||
rm -rf ~/Library/Application\ Support/Kitematic/
|
rm -rf ~/Library/Application\ Support/Kitematic/
|
||||||
rm -rf ~/.docker
|
rm -rf ~/.docker
|
||||||
rm -rf ~/Library/VirtualBox/
|
rm -rf ~/Library/VirtualBox/
|
||||||
|
rm -rf ~/Kitematic
|
||||||
$DIR/VirtualBox_Uninstall.tool
|
$DIR/VirtualBox_Uninstall.tool
|
||||||
|
|
Загрузка…
Ссылка в новой задаче