mirror of https://github.com/docker/docs.git
Error handling, updating state
This commit is contained in:
parent
239c54e2ad
commit
32f129c5bd
|
@ -65,12 +65,15 @@
|
|||
"bugsnag-js": "^2.4.7",
|
||||
"classnames": "^1.2.0",
|
||||
"coveralls": "^2.11.2",
|
||||
"deep-extend": "^0.4.0",
|
||||
"dockerode": "^2.1.1",
|
||||
"exec": "0.2.0",
|
||||
"install": "^0.1.8",
|
||||
"jquery": "^2.1.3",
|
||||
"mixpanel": "0.2.0",
|
||||
"mkdirp": "^0.5.0",
|
||||
"node-uuid": "^1.4.3",
|
||||
"npm": "^2.9.1",
|
||||
"object-assign": "^2.0.0",
|
||||
"parseUri": "^1.2.3-2",
|
||||
"react": "^0.13.1",
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import alt from '../alt';
|
||||
import dockerUtil from '../utils/DockerUtil';
|
||||
|
||||
class ContainerServerActions {
|
||||
class ContainerActions {
|
||||
start (name) {
|
||||
this.dispatch({name});
|
||||
dockerUtil.start(name);
|
||||
|
@ -12,7 +12,6 @@ class ContainerServerActions {
|
|||
dockerUtil.destroy(name);
|
||||
}
|
||||
|
||||
// TODO: don't require all container data for this method
|
||||
rename (name, newName) {
|
||||
this.dispatch({name, newName});
|
||||
dockerUtil.rename(name, newName);
|
||||
|
@ -24,7 +23,7 @@ class ContainerServerActions {
|
|||
}
|
||||
|
||||
update (name, container) {
|
||||
this.dispatch({container});
|
||||
this.dispatch({name, container});
|
||||
dockerUtil.updateContainer(name, container);
|
||||
}
|
||||
|
||||
|
@ -37,4 +36,4 @@ class ContainerServerActions {
|
|||
}
|
||||
}
|
||||
|
||||
export default alt.createActions(ContainerServerActions);
|
||||
export default alt.createActions(ContainerActions);
|
||||
|
|
|
@ -8,9 +8,10 @@ class ContainerServerActions {
|
|||
'destroyed',
|
||||
'error',
|
||||
'muted',
|
||||
'unmuted',
|
||||
'progress',
|
||||
'pending',
|
||||
'progress',
|
||||
'started',
|
||||
'unmuted',
|
||||
'updated',
|
||||
'waiting'
|
||||
);
|
||||
|
|
|
@ -12,14 +12,19 @@ var ContainerDetails = React.createClass({
|
|||
},
|
||||
|
||||
render: function () {
|
||||
if (!this.props.container) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let ports = containerUtil.ports(this.props.container);
|
||||
let defaultPort = _.find(_.keys(ports), port => {
|
||||
return util.webPorts.indexOf(port) !== -1;
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="details">
|
||||
<ContainerDetailsHeader {...this.props} defaultPort={defaultPort} ports={ports}/>
|
||||
<ContainerDetailsSubheader {...this.props}/>
|
||||
<ContainerDetailsSubheader {...this.props} defaultPort={defaultPort} ports={ports}/>
|
||||
<Router.RouteHandler {...this.props} defaultPort={defaultPort} ports={ports}/>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -7,7 +7,9 @@ var ContainerDetailsHeader = React.createClass({
|
|||
return false;
|
||||
}
|
||||
|
||||
if (this.props.container.State.Running && !this.props.container.State.Paused && !this.props.container.State.ExitCode && !this.props.container.State.Restarting) {
|
||||
if (this.props.container.State.Updating) {
|
||||
state = <span className="status downloading">UPDATING</span>;
|
||||
} else if (this.props.container.State.Running && !this.props.container.State.Paused && !this.props.container.State.ExitCode && !this.props.container.State.Restarting) {
|
||||
state = <span className="status running">RUNNING</span>;
|
||||
} else if (this.props.container.State.Restarting) {
|
||||
state = <span className="status restarting">RESTARTING</span>;
|
||||
|
|
|
@ -20,31 +20,31 @@ var ContainerDetailsSubheader = React.createClass({
|
|||
if (!this.props.container) {
|
||||
return false;
|
||||
}
|
||||
return (!this.props.container.State.Running || !this.props.defaultPort);
|
||||
return (!this.props.container.State.Running || !this.props.defaultPort || this.props.container.State.Updating);
|
||||
},
|
||||
disableRestart: function () {
|
||||
if (!this.props.container) {
|
||||
return false;
|
||||
}
|
||||
return (this.props.container.State.Downloading || this.props.container.State.Restarting);
|
||||
return (this.props.container.State.Downloading || this.props.container.State.Restarting || this.props.container.State.Updating);
|
||||
},
|
||||
disableStop: function () {
|
||||
if (!this.props.container) {
|
||||
return false;
|
||||
}
|
||||
return (this.props.container.State.Downloading || this.props.container.State.ExitCode || !this.props.container.State.Running);
|
||||
return (this.props.container.State.Downloading || this.props.container.State.ExitCode || !this.props.container.State.Running || this.props.container.State.Updating);
|
||||
},
|
||||
disableStart: function () {
|
||||
if (!this.props.container) {
|
||||
return false;
|
||||
}
|
||||
return (this.props.container.State.Downloading || this.props.container.State.Running);
|
||||
return (this.props.container.State.Downloading || this.props.container.State.Running || this.props.container.State.Updating);
|
||||
},
|
||||
disableTerminal: function () {
|
||||
if (!this.props.container) {
|
||||
return false;
|
||||
}
|
||||
return (!this.props.container.State.Running);
|
||||
return (!this.props.container.State.Running || this.props.container.State.Updating);
|
||||
},
|
||||
disableTab: function () {
|
||||
if (!this.props.container) {
|
||||
|
|
|
@ -35,12 +35,16 @@ var ContainerHome = React.createClass({
|
|||
},
|
||||
|
||||
render: function () {
|
||||
if (!this.props.container) {
|
||||
return;
|
||||
}
|
||||
|
||||
let body;
|
||||
if (this.props.error) {
|
||||
if (this.props.container.Error) {
|
||||
body = (
|
||||
<div className="details-progress">
|
||||
<h3>An error occurred:</h3>
|
||||
<h2>{this.props.error.statusCode} {this.props.error.reason} - {this.props.error.json}</h2>
|
||||
<h2>{this.props.container.Error.message}</h2>
|
||||
<h3>If you feel that this error is invalid, please <a onClick={this.handleErrorClick}>file a ticket on our GitHub repo.</a></h3>
|
||||
<Radial progress={100} error={true} thick={true} transparent={true}/>
|
||||
</div>
|
||||
|
|
|
@ -22,6 +22,14 @@ module.exports = React.createClass({
|
|||
LogStore.on(LogStore.SERVER_LOGS_EVENT, this.update);
|
||||
LogStore.fetch(this.props.container.Name);
|
||||
},
|
||||
|
||||
componentWillReceiveProps: function (nextProps) {
|
||||
if (this.props.container && nextProps.container && this.props.container.Name !== nextProps.container.Name) {
|
||||
LogStore.detach(this.props.container.Name);
|
||||
LogStore.fetch(nextProps.container.Name);
|
||||
}
|
||||
},
|
||||
|
||||
componentWillUnmount: function() {
|
||||
if (!this.props.container) {
|
||||
return;
|
||||
|
|
|
@ -16,7 +16,6 @@ var ContainerSettingsGeneral = React.createClass({
|
|||
getInitialState: function () {
|
||||
let env = ContainerUtil.env(this.props.container) || [];
|
||||
env.push(['', '']);
|
||||
console.log(env);
|
||||
return {
|
||||
slugName: null,
|
||||
nameError: null,
|
||||
|
@ -28,9 +27,7 @@ var ContainerSettingsGeneral = React.createClass({
|
|||
if (nextState.slugName !== this.state.slugName || nextState.nameError !== this.state.nameError) {
|
||||
return true;
|
||||
}
|
||||
if (nextState.env.length === this.state.env.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
|
@ -195,7 +192,7 @@ var ContainerSettingsGeneral = React.createClass({
|
|||
}
|
||||
|
||||
return (
|
||||
<div key={index + ':' + key + '=' + val} className="keyval-row">
|
||||
<div className="keyval-row">
|
||||
<input type="text" className="key line" defaultValue={key} onChange={this.handleChangeEnvKey.bind(this, index)}></input>
|
||||
<input type="text" className="val line" defaultValue={val} onChange={this.handleChangeEnvVal.bind(this, index)}></input>
|
||||
{icon}
|
||||
|
@ -215,7 +212,7 @@ var ContainerSettingsGeneral = React.createClass({
|
|||
<div className="env-vars">
|
||||
{vars}
|
||||
</div>
|
||||
<a className="btn btn-action" onClick={this.handleSaveEnvVars}>Save</a>
|
||||
<a className="btn btn-action" disabled={this.props.container.State.Updating} onClick={this.handleSaveEnvVars}>Save</a>
|
||||
</div>
|
||||
<div className="settings-section">
|
||||
<h3>Delete Container</h3>
|
||||
|
|
|
@ -22,7 +22,7 @@ var ContainerSettingsVolumes = React.createClass({
|
|||
return pair[1] + ':' + pair[0];
|
||||
});
|
||||
|
||||
containerActions.update(this.props.container.Name, {Binds: binds});
|
||||
containerActions.update(this.props.container.Name, {Binds: binds, Volumes: volumes});
|
||||
}
|
||||
});
|
||||
},
|
||||
|
@ -35,7 +35,7 @@ var ContainerSettingsVolumes = React.createClass({
|
|||
var binds = _.pairs(volumes).map(function (pair) {
|
||||
return pair[1] + ':' + pair[0];
|
||||
});
|
||||
containerActions.update(this.props.container.Name, {Binds: binds});
|
||||
containerActions.update(this.props.container.Name, {Binds: binds, Volumes: volumes});
|
||||
},
|
||||
handleOpenVolumeClick: function (path) {
|
||||
metrics.track('Opened Volume Directory', {
|
||||
|
@ -45,24 +45,24 @@ var ContainerSettingsVolumes = React.createClass({
|
|||
},
|
||||
render: function () {
|
||||
if (!this.props.container) {
|
||||
return (<div></div>);
|
||||
return false;
|
||||
}
|
||||
var self = this;
|
||||
var volumes = _.map(self.props.container.Volumes, function (val, key) {
|
||||
|
||||
var volumes = _.map(this.props.container.Volumes, (val, key) => {
|
||||
if (!val || val.indexOf(process.env.HOME) === -1) {
|
||||
val = (
|
||||
<span>
|
||||
<a className="value-right">No Folder</a>
|
||||
<a className="btn btn-action small" onClick={self.handleChooseVolumeClick.bind(self, key)}>Change</a>
|
||||
<a className="btn btn-action small" onClick={self.handleRemoveVolumeClick.bind(self, key)}>Remove</a>
|
||||
<a className="btn btn-action small" disabled={this.props.container.State.Updating} onClick={this.handleChooseVolumeClick.bind(this, key)}>Change</a>
|
||||
<a className="btn btn-action small" disabled={this.props.container.State.Updating} onClick={this.handleRemoveVolumeClick.bind(this, key)}>Remove</a>
|
||||
</span>
|
||||
);
|
||||
} else {
|
||||
val = (
|
||||
<span>
|
||||
<a className="value-right" onClick={self.handleOpenVolumeClick.bind(self, val)}>{val.replace(process.env.HOME, '~')}</a>
|
||||
<a className="btn btn-action small" onClick={self.handleChooseVolumeClick.bind(self, key)}>Change</a>
|
||||
<a className="btn btn-action small" onClick={self.handleRemoveVolumeClick.bind(self, key)}>Remove</a>
|
||||
<a className="value-right" onClick={this.handleOpenVolumeClick.bind(this, val)}>{val.replace(process.env.HOME, '~')}</a>
|
||||
<a className="btn btn-action small" disabled={this.props.container.State.Updating} onClick={this.handleChooseVolumeClick.bind(this, key)}>Change</a>
|
||||
<a className="btn btn-action small" disabled={this.props.container.State.Updating} onClick={this.handleRemoveVolumeClick.bind(this, key)}>Remove</a>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -195,7 +195,7 @@ var Containers = React.createClass({
|
|||
<div className="sidebar-buttons-padding"></div>
|
||||
</section>
|
||||
</div>
|
||||
<Router.RouteHandler pending={this.state.pending} containers={this.state.containers} container={container} error={this.state.error}/>
|
||||
<Router.RouteHandler pending={this.state.pending} containers={this.state.containers} container={container}/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import _ from 'underscore';
|
||||
import deepExtend from 'deep-extend';
|
||||
import alt from '../alt';
|
||||
import containerServerActions from '../actions/ContainerServerActions';
|
||||
import containerActions from '../actions/ContainerActions';
|
||||
|
@ -9,13 +10,18 @@ class ContainerStore {
|
|||
this.bindActions(containerServerActions);
|
||||
this.containers = {};
|
||||
|
||||
// Blacklist of containers to avoid updating
|
||||
this.muted = {};
|
||||
|
||||
// Pending container to create
|
||||
this.pending = null;
|
||||
}
|
||||
|
||||
error ({name, error}) {
|
||||
let containers = this.containers;
|
||||
if (containers[name]) {
|
||||
containers[name].Error = error;
|
||||
}
|
||||
this.setState({containers});
|
||||
}
|
||||
|
||||
start ({name}) {
|
||||
let containers = this.containers;
|
||||
if (containers[name]) {
|
||||
|
@ -24,6 +30,15 @@ class ContainerStore {
|
|||
}
|
||||
}
|
||||
|
||||
started ({name}) {
|
||||
let containers = this.containers;
|
||||
if (containers[name]) {
|
||||
containers[name].State.Starting = false;
|
||||
containers[name].State.Updating = false;
|
||||
this.setState({containers});
|
||||
}
|
||||
}
|
||||
|
||||
stop ({name}) {
|
||||
let containers = this.containers;
|
||||
if (containers[name]) {
|
||||
|
@ -36,6 +51,11 @@ class ContainerStore {
|
|||
let containers = this.containers;
|
||||
let data = containers[name];
|
||||
data.Name = newName;
|
||||
|
||||
if (data.State) {
|
||||
data.State.Updating = true;
|
||||
}
|
||||
|
||||
containers[newName] = data;
|
||||
delete containers[name];
|
||||
this.setState({containers});
|
||||
|
@ -44,23 +64,32 @@ class ContainerStore {
|
|||
added ({container}) {
|
||||
let containers = this.containers;
|
||||
containers[container.Name] = container;
|
||||
delete this.muted[container.Name];
|
||||
this.setState({containers});
|
||||
}
|
||||
|
||||
update ({name, container}) {
|
||||
let containers = this.containers;
|
||||
if (containers[name] && containers[name].State && containers[name].State.Updating) {
|
||||
return;
|
||||
}
|
||||
|
||||
deepExtend(containers[name], container);
|
||||
|
||||
if (containers[name].State) {
|
||||
containers[name].State.Updating = true;
|
||||
}
|
||||
|
||||
this.setState({containers});
|
||||
}
|
||||
|
||||
updated ({container}) {
|
||||
if (this.muted[container.Name]) {
|
||||
let containers = this.containers;
|
||||
if (!containers[container.Name] || containers[container.Name].State.Updating) {
|
||||
return;
|
||||
}
|
||||
|
||||
let containers = this.containers;
|
||||
if (!containers[container.Name]) {
|
||||
return;
|
||||
}
|
||||
containers[container.Name] = container;
|
||||
if (container.State.Running) {
|
||||
delete container.State.Starting;
|
||||
}
|
||||
|
||||
this.setState({containers});
|
||||
}
|
||||
|
||||
|
@ -87,20 +116,17 @@ class ContainerStore {
|
|||
let container = _.find(_.values(this.containers), container => {
|
||||
return container.Id === name || container.Name === name;
|
||||
});
|
||||
if (container && !this.muted[container.Name]) {
|
||||
|
||||
if (container && container.State && container.State.Updating) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (container) {
|
||||
delete containers[container.Name];
|
||||
this.setState({containers});
|
||||
}
|
||||
}
|
||||
|
||||
muted ({name}) {
|
||||
this.muted[name] = true;
|
||||
}
|
||||
|
||||
unmuted ({name}) {
|
||||
this.muted[name] = false;
|
||||
}
|
||||
|
||||
waiting({name, waiting}) {
|
||||
let containers = this.containers;
|
||||
if (containers[name]) {
|
||||
|
@ -109,14 +135,6 @@ class ContainerStore {
|
|||
this.setState({containers});
|
||||
}
|
||||
|
||||
error ({name, error}) {
|
||||
let containers = this.containers;
|
||||
if (containers[name]) {
|
||||
containers[name].Error = error;
|
||||
}
|
||||
this.setState({containers});
|
||||
}
|
||||
|
||||
pending ({repo, tag}) {
|
||||
let pending = {repo, tag};
|
||||
this.setState({pending});
|
||||
|
@ -127,10 +145,10 @@ class ContainerStore {
|
|||
}
|
||||
|
||||
static generateName (repo) {
|
||||
let base = _.last(repo.split('/'));
|
||||
let count = 1;
|
||||
let name = base;
|
||||
let names = _.keys(this.getState().containers);
|
||||
const base = _.last(repo.split('/'));
|
||||
const names = _.keys(this.getState().containers);
|
||||
var count = 1;
|
||||
var name = base;
|
||||
while (true) {
|
||||
if (names.indexOf(name) === -1) {
|
||||
return name;
|
||||
|
|
|
@ -79,7 +79,7 @@ export default {
|
|||
containerServerActions.error({name, error});
|
||||
return;
|
||||
}
|
||||
containerServerActions.unmuted({name});
|
||||
containerServerActions.started({name, error});
|
||||
this.fetchContainer(name);
|
||||
});
|
||||
},
|
||||
|
@ -184,9 +184,6 @@ export default {
|
|||
updateContainer (name, data) {
|
||||
let existing = this.client.getContainer(name);
|
||||
existing.inspect((error, existingData) => {
|
||||
if (error) {
|
||||
return;
|
||||
}
|
||||
if (error) {
|
||||
containerServerActions.error({name, error});
|
||||
return;
|
||||
|
@ -202,7 +199,6 @@ export default {
|
|||
}
|
||||
|
||||
var fullData = _.extend(existingData, data);
|
||||
containerServerActions.muted({name});
|
||||
this.createContainer(name, fullData);
|
||||
});
|
||||
},
|
||||
|
@ -213,7 +209,6 @@ export default {
|
|||
containerServerActions.error({name, error});
|
||||
return;
|
||||
}
|
||||
this.fetchAllContainers();
|
||||
var oldPath = path.join(util.home(), 'Kitematic', name);
|
||||
var newPath = path.join(util.home(), 'Kitematic', newName);
|
||||
|
||||
|
@ -223,7 +218,6 @@ export default {
|
|||
containerServerActions.error({newName, error});
|
||||
}
|
||||
rimraf(newPath, () => {
|
||||
console.log('removed');
|
||||
if (fs.existsSync(oldPath)) {
|
||||
fs.renameSync(oldPath, newPath);
|
||||
}
|
||||
|
@ -295,9 +289,7 @@ export default {
|
|||
containerServerActions.destroyed({id: name});
|
||||
var volumePath = path.join(util.home(), 'Kitematic', name);
|
||||
if (fs.existsSync(volumePath)) {
|
||||
rimraf(volumePath, function (err) {
|
||||
console.log(err);
|
||||
});
|
||||
rimraf(volumePath, () => {});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue