Fix setup & don't show build containers

This commit is contained in:
Jeffrey Morgan 2015-01-26 23:48:33 -05:00
parent bde27af83f
commit d9ec2203e4
12 changed files with 105 additions and 48 deletions

View File

@ -2,10 +2,12 @@ var _ = require('underscore');
var $ = require('jquery'); var $ = require('jquery');
var React = require('react/addons'); var React = require('react/addons');
var Router = require('react-router'); var Router = require('react-router');
var exec = require('exec');
var remote = require('remote');
var dialog = remote.require('dialog');
var ContainerStore = require('./ContainerStore'); var ContainerStore = require('./ContainerStore');
var ContainerUtil = require('./ContainerUtil'); var ContainerUtil = require('./ContainerUtil');
var docker = require('./docker'); var docker = require('./docker');
var exec = require('exec');
var boot2docker = require('./boot2docker'); var boot2docker = require('./boot2docker');
var ProgressBar = require('react-bootstrap/ProgressBar'); var ProgressBar = require('react-bootstrap/ProgressBar');
@ -29,10 +31,6 @@ var ContainerDetails = React.createClass({
}; };
}, },
componentWillReceiveProps: function () { componentWillReceiveProps: function () {
if (this.state.page === this.PAGE_SETTINGS) {
}
console.log(this.props.container);
this.init(); this.init();
}, },
componentWillMount: function () { componentWillMount: function () {
@ -155,11 +153,16 @@ var ContainerDetails = React.createClass({
}); });
}, },
handleDeleteContainer: function () { handleDeleteContainer: function () {
var container = this.props.container; dialog.showMessageBox({
var name = container.Name.replace('/', ''); message: 'Are you sure you want to delete this container?',
ContainerStore.remove(name, function (err) { buttons: ['Delete', 'Cancel']
console.error(err); }, function (index) {
}); if (index === 0) {
ContainerStore.remove(this.props.container.Name, function (err) {
console.error(err);
});
}
}.bind(this));
}, },
render: function () { render: function () {
var self = this; var self = this;
@ -223,7 +226,7 @@ var ContainerDetails = React.createClass({
} else { } else {
if (this.state.page === this.PAGE_LOGS) { if (this.state.page === this.PAGE_LOGS) {
body = ( body = (
<div className="details-panel"> <div className="details-panel details-logs">
<div className="logs"> <div className="logs">
{logs} {logs}
</div> </div>
@ -281,7 +284,7 @@ var ContainerDetails = React.createClass({
'btn-action': true, 'btn-action': true,
'only-icon': true, 'only-icon': true,
'active': this.state.page === this.PAGE_LOGS, 'active': this.state.page === this.PAGE_LOGS,
disabled: !this.props.container.State.Running disabled: this.props.container.State.Downloading
}); });
var gearButtonClass = React.addons.classSet({ var gearButtonClass = React.addons.classSet({
@ -289,7 +292,7 @@ var ContainerDetails = React.createClass({
'btn-action': true, 'btn-action': true,
'only-icon': true, 'only-icon': true,
'active': this.state.page === this.PAGE_SETTINGS, 'active': this.state.page === this.PAGE_SETTINGS,
disabled: !this.props.container.State.Running disabled: this.props.container.State.Downloading
}); });
return ( return (

View File

@ -3,13 +3,14 @@ var async = require('async');
var assign = require('object-assign'); var assign = require('object-assign');
var Stream = require('stream'); var Stream = require('stream');
var Convert = require('ansi-to-html'); var Convert = require('ansi-to-html');
var convert = new Convert();
var docker = require('./docker'); var docker = require('./docker');
var registry = require('./registry'); var registry = require('./registry');
var ContainerUtil = require('./ContainerUtil'); var ContainerUtil = require('./ContainerUtil');
var $ = require('jquery'); var $ = require('jquery');
var _ = require('underscore'); var _ = require('underscore');
var convert = new Convert();
var _recommended = []; var _recommended = [];
var _containers = {}; var _containers = {};
var _progress = {}; var _progress = {};
@ -120,15 +121,19 @@ var ContainerStore = assign(EventEmitter.prototype, {
callback(err, null); callback(err, null);
return; return;
} }
container.start({ if (containerData.State && !containerData.State.Running) {
PublishAllPorts: true
}, function (err) {
if (err) {
callback(err);
return;
}
self.fetchContainer(name, callback); self.fetchContainer(name, callback);
}); } else {
container.start({
PublishAllPorts: true
}, function (err) {
if (err) {
callback(err);
return;
}
self.fetchContainer(name, callback);
});
}
}); });
}); });
}); });
@ -204,15 +209,18 @@ var ContainerStore = assign(EventEmitter.prototype, {
// If the event is delete, remove the container // If the event is delete, remove the container
if (data.status === 'destroy') { if (data.status === 'destroy') {
var container = _.findWhere(_.values(_containers), {Id: data.id}); var container = _.findWhere(_.values(_containers), {Id: data.id});
if (_muted[container.Name]) { if (!container || _muted[container.Name]) {
return; return;
} }
delete _containers[container.Name]; delete _containers[container.Name];
this.emit(this.SERVER_CONTAINER_EVENT, container.Name, data.status); this.emit(this.SERVER_CONTAINER_EVENT, container.Name, data.status);
} else { } else {
this.fetchContainer(data.id, function (err) { this.fetchContainer(data.id, function (err) {
if (err) {
return;
}
var container = _.findWhere(_.values(_containers), {Id: data.id}); var container = _.findWhere(_.values(_containers), {Id: data.id});
if (_muted[container.Name]) { if (!container || _muted[container.Name]) {
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);
@ -236,6 +244,10 @@ var ContainerStore = assign(EventEmitter.prototype, {
if (err) { if (err) {
callback(err); callback(err);
} else { } else {
if (container.Config.Image === container.Image.slice(0, 12) || container.Config.Image === container.Image) {
callback();
return;
}
// Fix leading slash in container names // Fix leading slash in container names
container.Name = container.Name.replace('/', ''); container.Name = container.Name.replace('/', '');
@ -278,7 +290,6 @@ var ContainerStore = assign(EventEmitter.prototype, {
async.map(recommended, function (repository, callback) { async.map(recommended, function (repository, callback) {
$.get('https://registry.hub.docker.com/v1/search?q=' + repository, function (data) { $.get('https://registry.hub.docker.com/v1/search?q=' + repository, function (data) {
var results = data.results; var results = data.results;
console.log(repository, data);
callback(null, _.find(results, function (r) { callback(null, _.find(results, function (r) {
return r.name === repository; return r.name === repository;
})); }));
@ -296,8 +307,9 @@ var ContainerStore = assign(EventEmitter.prototype, {
fetchLogs: function (name, callback) { fetchLogs: function (name, callback) {
if (_logs[name]) { if (_logs[name]) {
callback(); callback();
} else {
_logs[name] = [];
} }
_logs[name] = [];
var index = 0; var index = 0;
var self = this; var self = this;
docker.client().getContainer(name).logs({ docker.client().getContainer(name).logs({
@ -316,11 +328,11 @@ var ContainerStore = assign(EventEmitter.prototype, {
var time = buf.substr(0,buf.indexOf(' ')); var time = buf.substr(0,buf.indexOf(' '));
var msg = buf.substr(buf.indexOf(' ')+1); var msg = buf.substr(buf.indexOf(' ')+1);
_logs[name].push(convert.toHtml(self._escapeHTML(msg))); _logs[name].push(convert.toHtml(self._escapeHTML(msg)));
self.emit(self.SERVER_LOGS_EVENT, name);
} }
index += 1; index += 1;
}); });
stream.on('end', function (buf) { stream.on('end', function (buf) {
self.emit(self.SERVER_LOGS_EVENT, name);
callback(); callback();
docker.client().getContainer(name).logs({ docker.client().getContainer(name).logs({
follow: true, follow: true,
@ -329,6 +341,9 @@ var ContainerStore = assign(EventEmitter.prototype, {
timestamps: true, timestamps: true,
tail: 0 tail: 0
}, function (err, stream) { }, function (err, stream) {
if (err) {
return;
}
stream.setEncoding('utf8'); stream.setEncoding('utf8');
stream.on('data', function (buf) { stream.on('data', function (buf) {
// Every other message is a header // Every other message is a header
@ -357,10 +372,13 @@ var ContainerStore = assign(EventEmitter.prototype, {
self._createPlaceholderContainer(imageName, containerName, function (err, container) { self._createPlaceholderContainer(imageName, containerName, function (err, container) {
_containers[containerName] = container; _containers[containerName] = container;
self.emit(self.CLIENT_CONTAINER_EVENT, containerName, 'create'); self.emit(self.CLIENT_CONTAINER_EVENT, containerName, 'create');
_muted[containerName] = true;
_progress[containerName] = 0; _progress[containerName] = 0;
self._pullImage(repository, tag, function () { self._pullImage(repository, tag, function () {
self._createContainer(containerName, {Image: imageName}, function (err, container) { self._createContainer(containerName, {Image: imageName}, function (err, container) {
delete _progress[containerName]; delete _progress[containerName];
_muted[containerName] = false;
self.emit(self.CLIENT_CONTAINER_EVENT, containerName);
}); });
}, function (progress) { }, function (progress) {
_progress[containerName] = progress; _progress[containerName] = progress;
@ -382,7 +400,6 @@ var ContainerStore = assign(EventEmitter.prototype, {
var fullData = assign(_containers[name], data); var fullData = assign(_containers[name], data);
this._createContainer(name, fullData, function (err) { this._createContainer(name, fullData, function (err) {
this.emit(this.CLIENT_CONTAINER_EVENT, name); this.emit(this.CLIENT_CONTAINER_EVENT, name);
console.log(err);
_muted[name] = false; _muted[name] = false;
}.bind(this)); }.bind(this));
}, },

View File

@ -52,7 +52,6 @@ var Containers = React.createClass({
sorted: ContainerStore.sorted() sorted: ContainerStore.sorted()
}); });
if (status === 'create') { if (status === 'create') {
console.log('transition');
this.transitionTo('container', {name: name}); this.transitionTo('container', {name: name});
} }
}, },

View File

@ -12,7 +12,8 @@ var Radial = React.createClass({
} }
var classes = React.addons.classSet({ var classes = React.addons.classSet({
'radial-progress': true, 'radial-progress': true,
'radial-spinner': this.props.spin 'radial-spinner': this.props.spin,
'radial-negative': this.props.error
}); });
return ( return (
<div className={classes} data-progress={this.props.progress}> <div className={classes} data-progress={this.props.progress}>

View File

@ -14,9 +14,10 @@ var ContainerStore = require('./ContainerStore.js');
var setupSteps = [ var setupSteps = [
{ {
run: function (callback, progressCallback) { run: function (callback, progressCallback) {
console.log(util.supportDir());
var installed = virtualbox.installed(); var installed = virtualbox.installed();
if (!installed) { if (!installed) {
util.download('https://s3.amazonaws.com/kite-installer/' + virtualbox.INSTALLER_FILENAME, path.join(process.cwd(), 'resources', virtualbox.INSTALLER_FILENAME), virtualbox.INSTALLER_CHECKSUM, function (err) { util.download('https://s3.amazonaws.com/kite-installer/' + virtualbox.INSTALLER_FILENAME, path.join(util.supportDir(), virtualbox.INSTALLER_FILENAME), virtualbox.INSTALLER_CHECKSUM, function (err) {
if (err) {callback(err); return;} if (err) {callback(err); return;}
virtualbox.install(function (err) { virtualbox.install(function (err) {
if (!virtualbox.installed()) { if (!virtualbox.installed()) {
@ -144,15 +145,24 @@ var Setup = React.createClass({
var radial; var radial;
if (this.state.progress) { if (this.state.progress) {
radial = <Radial progress={this.state.progress}/>; radial = <Radial progress={this.state.progress}/>;
} else { } else if (this.state.error) {
radial = <Radial spin="true" progress="92"/>; radial = <Radial error={true} spin="true" progress="100"/>;
}
if (this.state.error) {
return (
<div className="setup">
{radial}
<p className="error">Error: {this.state.error}</p>
</div>
);
} else {
return (
<div className="setup">
{radial}
<p>{this.state.message}</p>
</div>
);
} }
return (
<div className="setup">
{radial}
<p>{this.state.message}</p>
</div>
);
}, },
componentWillMount: function () { componentWillMount: function () {
this.setState({}); this.setState({});
@ -160,10 +170,12 @@ var Setup = React.createClass({
componentDidMount: function () { componentDidMount: function () {
var self = this; var self = this;
this.setup(function (err) { this.setup(function (err) {
boot2docker.ip(function (err, ip) { if (!err) {
docker.setHost(ip); boot2docker.ip(function (err, ip) {
self.transitionTo('containers'); docker.setHost(ip);
}); self.transitionTo('containers');
});
}
}); });
}, },
setup: function (callback) { setup: function (callback) {
@ -188,7 +200,7 @@ var Setup = React.createClass({
// if any of the steps fail // if any of the steps fail
console.log('Kitematic setup failed at step ' + currentStep); console.log('Kitematic setup failed at step ' + currentStep);
console.log(err); console.log(err);
self.setState({error: err}); self.setState({error: err.message});
callback(err); callback(err);
} else { } else {
// Setup Finished // Setup Finished

View File

@ -54,7 +54,13 @@ var Boot2Docker = {
return path.join(process.cwd(), 'resources', 'boot2docker-' + this.version()); return path.join(process.cwd(), 'resources', 'boot2docker-' + this.version());
}, },
exists: function (callback) { exists: function (callback) {
cmdExec([Boot2Docker.command(), 'info'], callback); cmdExec([Boot2Docker.command(), 'info'], function (err, out) {
if (err) {
callback(null, false);
} else {
callback(null, true);
}
});
}, },
status: function (callback) { status: function (callback) {
cmdExec([Boot2Docker.command(), 'status'], function (err, out) { cmdExec([Boot2Docker.command(), 'status'], function (err, out) {

View File

@ -24,7 +24,7 @@ Bugsnag.releaseStage = process.env.NODE_ENV === 'development' ? 'development' :
Bugsnag.notifyReleaseStages = []; Bugsnag.notifyReleaseStages = [];
Bugsnag.appVersion = app.getVersion(); Bugsnag.appVersion = app.getVersion();
if (window.location.hash === '#/') { if (!window.location.hash.length || window.location.hash === '#/') {
router.run(function (Handler) { router.run(function (Handler) {
React.render(<Handler/>, document.body); React.render(<Handler/>, document.body);
}); });

View File

@ -61,6 +61,7 @@
overflow-y: scroll; overflow-y: scroll;
overflow-x: hidden; overflow-x: hidden;
box-sizing: border-box; box-sizing: border-box;
max-width: 280px;
&.sep { &.sep {
border-top: 1px solid #eee; border-top: 1px solid #eee;

View File

@ -3,6 +3,9 @@
text-align: center; text-align: center;
p { p {
&.error {
color: @brand-danger;
}
margin-top: 20px; margin-top: 20px;
} }
} }

View File

@ -6,9 +6,23 @@ var progress = require('request-progress');
var exec = require('exec'); var exec = require('exec');
var Util = { var Util = {
supportDir: function (callback) {
var dirs = ['Application\ Support', 'Kitematic'];
var acc = process.env.HOME;
dirs.forEach(function (d) {
acc = path.join(acc, d);
if (!fs.existsSync(acc)) {
fs.mkdirSync(acc);
}
});
return acc;
},
download: function (url, filename, checksum, callback, progressCallback) { download: function (url, filename, checksum, callback, progressCallback) {
var doDownload = function () { var doDownload = function () {
progress(request(url), { progress(request({
uri: url,
rejectUnauthorized: false
}), {
throttle: 250 throttle: 250
}).on('progress', function (state) { }).on('progress', function (state) {
progressCallback(state.percent); progressCallback(state.percent);

View File

@ -2,6 +2,7 @@ var fs = require('fs');
var exec = require('exec'); var exec = require('exec');
var path = require('path'); var path = require('path');
var async = require('async'); var async = require('async');
var util = require('./util');
var VirtualBox = { var VirtualBox = {
REQUIRED_VERSION: '4.3.18', REQUIRED_VERSION: '4.3.18',
@ -16,7 +17,7 @@ var VirtualBox = {
}, },
install: function (callback) { install: function (callback) {
// -W waits for the process to close before finishing. // -W waits for the process to close before finishing.
exec('open -W ' + path.join(process.cwd(), 'resources', this.INSTALLER_FILENAME).replace(' ', '\\ '), function (stderr, stdout, code) { exec('open -W ' + path.join(util.supportDir(), this.INSTALLER_FILENAME).replace(' ', '\\ '), function (stderr, stdout, code) {
if (code) { if (code) {
callback(stderr); callback(stderr);
return; return;

View File