mirror of https://github.com/docker/docs.git
Last flux changes for containers
This commit is contained in:
parent
7989d48253
commit
0e5ce4cc2e
|
@ -24,10 +24,17 @@ class ContainerServerActions {
|
|||
}
|
||||
|
||||
update (name, container) {
|
||||
console.log(container);
|
||||
this.dispatch({container});
|
||||
dockerUtil.updateContainer(name, container);
|
||||
}
|
||||
|
||||
clearPending () {
|
||||
this.dispatch();
|
||||
}
|
||||
|
||||
run (name, repo, tag) {
|
||||
dockerUtil.run(name, repo, tag);
|
||||
}
|
||||
}
|
||||
|
||||
export default alt.createActions(ContainerServerActions);
|
||||
|
|
|
@ -10,6 +10,7 @@ class ContainerServerActions {
|
|||
'muted',
|
||||
'unmuted',
|
||||
'progress',
|
||||
'pending',
|
||||
'updated',
|
||||
'waiting'
|
||||
);
|
||||
|
|
|
@ -5,8 +5,6 @@ var Radial = require('./Radial.react');
|
|||
var ContainerHomePreview = require('./ContainerHomePreview.react');
|
||||
var ContainerHomeLogs = require('./ContainerHomeLogs.react');
|
||||
var ContainerHomeFolders = require('./ContainerHomeFolders.react');
|
||||
var containerUtil = require('../utils/ContainerUtil');
|
||||
var util = require ('../utils/Util');
|
||||
var shell = require('shell');
|
||||
|
||||
var ContainerHome = React.createClass({
|
||||
|
@ -100,7 +98,7 @@ var ContainerHome = React.createClass({
|
|||
if (_.keys(this.props.ports) > 0) {
|
||||
right = (
|
||||
<div className="right">
|
||||
<ContainerHomePreview />
|
||||
<ContainerHomePreview ports={this.props.ports} defaultPort={this.props.defaultPort} />
|
||||
<ContainerHomeFolders container={this.props.container} />
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -70,6 +70,7 @@ var ContainerHomePreview = React.createClass({
|
|||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
preview = (
|
||||
<div className="web-preview wrapper">
|
||||
<h4>IP & Ports</h4>
|
||||
|
|
|
@ -1,13 +1,10 @@
|
|||
var $ = require('jquery');
|
||||
var React = require('react/addons');
|
||||
var React = require('react');
|
||||
var Router = require('react-router');
|
||||
var ContainerStore = require('../stores/ContainerStore');
|
||||
var metrics = require('../utils/MetricsUtil');
|
||||
|
||||
var ContainerListNewItem = React.createClass({
|
||||
contextTypes: {
|
||||
router: React.PropTypes.func
|
||||
},
|
||||
mixins: [Router.Navigation, Router.State],
|
||||
handleItemMouseEnter: function () {
|
||||
var $action = $(this.getDOMNode()).find('.action');
|
||||
$action.show();
|
||||
|
@ -16,22 +13,20 @@ var ContainerListNewItem = React.createClass({
|
|||
var $action = $(this.getDOMNode()).find('.action');
|
||||
$action.hide();
|
||||
},
|
||||
handleDelete: function () {
|
||||
var self = this;
|
||||
handleDelete: function (event) {
|
||||
metrics.track('Deleted Container', {
|
||||
from: 'list',
|
||||
type: 'new'
|
||||
});
|
||||
var containers = ContainerStore.sorted();
|
||||
$(self.getDOMNode()).fadeOut(300, () => {
|
||||
if (containers.length > 0) {
|
||||
var name = containers[0].Name;
|
||||
this.context.router.transitionTo('containerHome', {name: name});
|
||||
}
|
||||
});
|
||||
|
||||
if (this.props.containers.length > 0 && this.getRoutes()[this.getRoutes().length - 2].name === 'new') {
|
||||
var name = this.props.containers[0].Name;
|
||||
this.transitionTo('containerHome', {name});
|
||||
}
|
||||
$(this.getDOMNode()).fadeOut(300);
|
||||
event.preventDefault();
|
||||
},
|
||||
render: function () {
|
||||
var self = this;
|
||||
var action;
|
||||
if (this.props.containers.length > 0) {
|
||||
action = (
|
||||
|
@ -42,7 +37,7 @@ var ContainerListNewItem = React.createClass({
|
|||
}
|
||||
return (
|
||||
<Router.Link to="new">
|
||||
<li className="new-container-item" onMouseEnter={self.handleItemMouseEnter} onMouseLeave={self.handleItemMouseLeave}>
|
||||
<li className="new-container-item" onMouseEnter={this.handleItemMouseEnter} onMouseLeave={this.handleItemMouseLeave}>
|
||||
<div className="state state-new"></div>
|
||||
<div className="info">
|
||||
<div className="name">
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
var _ = require('underscore');
|
||||
var $ = require('jquery');
|
||||
var React = require('react/addons');
|
||||
var remote = require('remote');
|
||||
var metrics = require('../utils/MetricsUtil');
|
||||
|
@ -8,22 +7,31 @@ var ContainerUtil = require('../utils/ContainerUtil');
|
|||
var containerActions = require('../actions/ContainerActions');
|
||||
|
||||
var ContainerSettingsGeneral = React.createClass({
|
||||
mixins: [React.addons.LinkedStateMixin],
|
||||
|
||||
contextTypes: {
|
||||
router: React.PropTypes.func
|
||||
},
|
||||
|
||||
getInitialState: function () {
|
||||
let env = ContainerUtil.env(this.props.container) || [];
|
||||
env.push(['', '']);
|
||||
console.log(env);
|
||||
return {
|
||||
slugName: null,
|
||||
nameError: null,
|
||||
pendingEnv: ContainerUtil.env(this.props.container) || {}
|
||||
env: env
|
||||
};
|
||||
},
|
||||
|
||||
willReceiveProps: function () {
|
||||
this.setState({
|
||||
pendingEnv: ContainerUtil.env(this.props.container) || {}
|
||||
});
|
||||
shouldComponentUpdate: function (nextProps, nextState) {
|
||||
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;
|
||||
},
|
||||
|
||||
handleNameChange: function (e) {
|
||||
|
@ -78,39 +86,54 @@ var ContainerSettingsGeneral = React.createClass({
|
|||
metrics.track('Changed Container Name');
|
||||
},
|
||||
|
||||
handleSaveEnvVar: function () {
|
||||
var $rows = $('.env-vars .keyval-row');
|
||||
var envVarList = [];
|
||||
$rows.each(function () {
|
||||
var key = $(this).find('.key').val();
|
||||
var val = $(this).find('.val').val();
|
||||
if (!key.length || !val.length) {
|
||||
return;
|
||||
}
|
||||
envVarList.push(key + '=' + val);
|
||||
});
|
||||
handleSaveEnvVars: function () {
|
||||
metrics.track('Saved Environment Variables');
|
||||
containerActions.update(this.props.container.Name, {Env: envVarList});
|
||||
let list = [];
|
||||
_.each(this.state.env, kvp => {
|
||||
let [key, value] = kvp;
|
||||
if ((key && key.length) || (value && value.length)) {
|
||||
list.push(key + '=' + value);
|
||||
}
|
||||
});
|
||||
containerActions.update(this.props.container.Name, {Env: list});
|
||||
},
|
||||
|
||||
handleAddPendingEnvVar: function () {
|
||||
var newKey = $('#new-env-key').val();
|
||||
var newVal = $('#new-env-val').val();
|
||||
var newEnv = {};
|
||||
newEnv[newKey] = newVal;
|
||||
handleChangeEnvKey: function (index, event) {
|
||||
let env = _.map(this.state.env, _.clone);
|
||||
env[index][0] = event.target.value;
|
||||
this.setState({
|
||||
pendingEnv: _.extend(this.state.pendingEnv, newEnv)
|
||||
env: env
|
||||
});
|
||||
},
|
||||
|
||||
handleChangeEnvVal: function (index, event) {
|
||||
let env = _.map(this.state.env, _.clone);
|
||||
env[index][1] = event.target.value;
|
||||
this.setState({
|
||||
env: env
|
||||
});
|
||||
},
|
||||
|
||||
handleAddEnvVar: function () {
|
||||
let env = _.map(this.state.env, _.clone);
|
||||
env.push(['', '']);
|
||||
this.setState({
|
||||
env: env
|
||||
});
|
||||
$('#new-env-key').val('');
|
||||
$('#new-env-val').val('');
|
||||
metrics.track('Added Pending Environment Variable');
|
||||
},
|
||||
|
||||
handleRemovePendingEnvVar: function (key) {
|
||||
var newEnv = _.omit(this.state.env, key);
|
||||
handleRemoveEnvVar: function (index) {
|
||||
let env = _.map(this.state.env, _.clone);
|
||||
env.splice(index, 1);
|
||||
if (env.length === 0) {
|
||||
env.push(['', '']);
|
||||
}
|
||||
|
||||
this.setState({
|
||||
env: newEnv
|
||||
env: env
|
||||
});
|
||||
|
||||
metrics.track('Removed Environment Variable');
|
||||
},
|
||||
|
||||
|
@ -131,8 +154,9 @@ var ContainerSettingsGeneral = React.createClass({
|
|||
|
||||
render: function () {
|
||||
if (!this.props.container) {
|
||||
return (<div></div>);
|
||||
return false;
|
||||
}
|
||||
|
||||
var willBeRenamedAs;
|
||||
var btnSaveName = (
|
||||
<a className="btn btn-action" onClick={this.handleSaveContainerName} disabled="disabled">Save</a>
|
||||
|
@ -149,7 +173,8 @@ var ContainerSettingsGeneral = React.createClass({
|
|||
<p><strong>{this.state.nameError}</strong></p>
|
||||
);
|
||||
}
|
||||
var rename = (
|
||||
|
||||
let rename = (
|
||||
<div className="settings-section">
|
||||
<h3>Container Name</h3>
|
||||
<div className="container-name">
|
||||
|
@ -159,15 +184,25 @@ var ContainerSettingsGeneral = React.createClass({
|
|||
{btnSaveName}
|
||||
</div>
|
||||
);
|
||||
var pendingEnvVars = _.map(this.state.pendingEnv, (val, key) => {
|
||||
|
||||
let vars = _.map(this.state.env, (kvp, index) => {
|
||||
let [key, val] = kvp;
|
||||
let icon;
|
||||
if (index === this.state.env.length - 1) {
|
||||
icon = <a onClick={this.handleAddEnvVar} className="only-icon btn btn-positive small"><span className="icon icon-add-1"></span></a>;
|
||||
} else {
|
||||
icon = <a onClick={this.handleRemoveEnvVar.bind(this, index)} className="only-icon btn btn-action small"><span className="icon icon-cross"></span></a>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div key={key} className="keyval-row">
|
||||
<input type="text" className="key line" defaultValue={key}></input>
|
||||
<input type="text" className="val line" defaultValue={val}></input>
|
||||
<a onClick={this.handleRemovePendingEnvVar.bind(this, key)} className="only-icon btn btn-action small"><span className="icon icon-cross"></span></a>
|
||||
<div key={index + ':' + key + '=' + val} 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}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="settings-panel">
|
||||
{rename}
|
||||
|
@ -178,14 +213,9 @@ var ContainerSettingsGeneral = React.createClass({
|
|||
<div className="label-val">VALUE</div>
|
||||
</div>
|
||||
<div className="env-vars">
|
||||
{pendingEnvVars}
|
||||
<div className="keyval-row">
|
||||
<input id="new-env-key" type="text" className="key line"></input>
|
||||
<input id="new-env-val" type="text" className="val line"></input>
|
||||
<a onClick={this.handleAddPendingEnvVar} className="only-icon btn btn-positive small"><span className="icon icon-add-1"></span></a>
|
||||
</div>
|
||||
{vars}
|
||||
</div>
|
||||
<a className="btn btn-action" onClick={this.handleSaveEnvVar}>Save</a>
|
||||
<a className="btn btn-action" onClick={this.handleSaveEnvVars}>Save</a>
|
||||
</div>
|
||||
<div className="settings-section">
|
||||
<h3>Delete Container</h3>
|
||||
|
|
|
@ -4,12 +4,12 @@ var remote = require('remote');
|
|||
var dialog = remote.require('dialog');
|
||||
var shell = require('shell');
|
||||
var metrics = require('../utils/MetricsUtil');
|
||||
var ContainerStore = require('../stores/ContainerStore');
|
||||
var containerActions = require('../actions/ContainerActions');
|
||||
|
||||
var ContainerSettingsVolumes = React.createClass({
|
||||
handleChooseVolumeClick: function (dockerVol) {
|
||||
var self = this;
|
||||
dialog.showOpenDialog({properties: ['openDirectory', 'createDirectory']}, function (filenames) {
|
||||
dialog.showOpenDialog({properties: ['openDirectory', 'createDirectory']}, (filenames) => {
|
||||
if (!filenames) {
|
||||
return;
|
||||
}
|
||||
|
@ -21,11 +21,8 @@ var ContainerSettingsVolumes = React.createClass({
|
|||
var binds = _.pairs(volumes).map(function (pair) {
|
||||
return pair[1] + ':' + pair[0];
|
||||
});
|
||||
ContainerStore.updateContainer(self.props.container.Name, {
|
||||
Binds: binds
|
||||
}, function (err) {
|
||||
if (err) { console.log(err); }
|
||||
});
|
||||
|
||||
containerActions.update(this.props.container.Name, {Binds: binds});
|
||||
}
|
||||
});
|
||||
},
|
||||
|
@ -38,11 +35,7 @@ var ContainerSettingsVolumes = React.createClass({
|
|||
var binds = _.pairs(volumes).map(function (pair) {
|
||||
return pair[1] + ':' + pair[0];
|
||||
});
|
||||
ContainerStore.updateContainer(this.props.container.Name, {
|
||||
Binds: binds
|
||||
}, function (err) {
|
||||
if (err) { console.log(err); }
|
||||
});
|
||||
containerActions.update(this.props.container.Name, {Binds: binds});
|
||||
},
|
||||
handleOpenVolumeClick: function (path) {
|
||||
metrics.track('Opened Volume Directory', {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
var $ = require('jquery');
|
||||
var _ = require('underscore');
|
||||
var React = require('react/addons');
|
||||
var React = require('react');
|
||||
var Router = require('react-router');
|
||||
var containerStore = require('../stores/ContainerStore');
|
||||
var ContainerList = require('./ContainerList.react');
|
||||
|
@ -62,7 +62,9 @@ var Containers = React.createClass({
|
|||
});
|
||||
|
||||
let name = this.context.router.getCurrentParams().name;
|
||||
if (name && !containers[name]) {
|
||||
if (containerStore.getState().pending) {
|
||||
this.context.router.transitionTo('pull');
|
||||
} else if (name && !containers[name]) {
|
||||
if (sorted.length) {
|
||||
this.context.router.transitionTo('containerHome', {name: sorted[0].Name});
|
||||
} else {
|
||||
|
@ -72,7 +74,8 @@ var Containers = React.createClass({
|
|||
|
||||
this.setState({
|
||||
containers: containers,
|
||||
sorted: sorted
|
||||
sorted: sorted,
|
||||
pending: containerStore.getState().pending
|
||||
});
|
||||
},
|
||||
|
||||
|
|
|
@ -1,14 +1,16 @@
|
|||
var $ = require('jquery');
|
||||
var React = require('react/addons');
|
||||
var Router = require('react-router');
|
||||
var RetinaImage = require('react-retina-image');
|
||||
var metrics = require('../utils/MetricsUtil');
|
||||
var OverlayTrigger = require('react-bootstrap').OverlayTrigger;
|
||||
var Tooltip = require('react-bootstrap').Tooltip;
|
||||
var util = require('../utils/Util');
|
||||
var dockerUtil = require('../utils/DockerUtil');
|
||||
var containerActions = require('../actions/ContainerActions');
|
||||
var containerStore = require('../stores/ContainerStore');
|
||||
|
||||
var ImageCard = React.createClass({
|
||||
mixins: [Router.Navigation],
|
||||
getInitialState: function () {
|
||||
return {
|
||||
tags: [],
|
||||
|
@ -28,7 +30,8 @@ var ImageCard = React.createClass({
|
|||
from: 'search'
|
||||
});
|
||||
let name = containerStore.generateName(repository);
|
||||
dockerUtil.run(name, repository, this.state.chosenTag);
|
||||
containerActions.run(name, repository, this.state.chosenTag);
|
||||
this.transitionTo('containerHome', {name});
|
||||
},
|
||||
handleTagOverlayClick: function (name) {
|
||||
var $tagOverlay = $(this.getDOMNode()).find('.tag-overlay');
|
||||
|
|
|
@ -1,30 +1,32 @@
|
|||
var React = require('react/addons');
|
||||
var Router = require('react-router');
|
||||
var shell = require('shell');
|
||||
var ContainerStore = require('../stores/ContainerStore');
|
||||
var containerActions = require('../actions/ContainerActions');
|
||||
var containerStore = require('../stores/ContainerStore');
|
||||
var metrics = require('../utils/MetricsUtil');
|
||||
|
||||
module.exports = React.createClass({
|
||||
mixins: [Router.Navigation],
|
||||
handleOpenClick: function () {
|
||||
var repo = this.props.pending.repository;
|
||||
var repo = this.props.pending.repo;
|
||||
if (repo.indexOf('/') === -1) {
|
||||
shell.openExternal(`https://registry.hub.docker.com/_/${this.props.pending.repository}`);
|
||||
shell.openExternal(`https://registry.hub.docker.com/_/${this.props.pending.repo}`);
|
||||
} else {
|
||||
shell.openExternal(`https://registry.hub.docker.com/u/${this.props.pending.repository}`);
|
||||
shell.openExternal(`https://registry.hub.docker.com/u/${this.props.pending.repo}`);
|
||||
}
|
||||
},
|
||||
handleCancelClick: function () {
|
||||
metrics.track('Canceled Click-To-Pull');
|
||||
ContainerStore.clearPending();
|
||||
containerActions.clearPending();
|
||||
this.context.router.transitionTo('new');
|
||||
},
|
||||
handleConfirmClick: function () {
|
||||
metrics.track('Created Container', {
|
||||
from: 'click-to-pull'
|
||||
});
|
||||
ContainerStore.clearPending();
|
||||
ContainerStore.create(this.props.pending.repository, this.props.pending.tag, function () {});
|
||||
containerActions.clearPending();
|
||||
let name = containerStore.generateName(this.props.pending.repo);
|
||||
containerActions.run(name, this.props.pending.repo, this.props.pending.tag);
|
||||
},
|
||||
render: function () {
|
||||
if (!this.props.pending) {
|
||||
|
@ -34,7 +36,7 @@ module.exports = React.createClass({
|
|||
<div className="details">
|
||||
<div className="new-container-pull">
|
||||
<div className="content">
|
||||
<h1>You're about to download and run <a onClick={this.handleOpenClick}>{this.props.pending.repository}:{this.props.pending.tag}</a>.</h1>
|
||||
<h1>You're about to download and run <a onClick={this.handleOpenClick}>{this.props.pending.repo}:{this.props.pending.tag}</a>.</h1>
|
||||
<h1>Please confirm to create the container.</h1>
|
||||
<div className="buttons">
|
||||
<a className="btn btn-action" onClick={this.handleCancelClick}>Cancel</a> <a onClick={this.handleConfirmClick} className="btn btn-action">Confirm</a>
|
||||
|
|
|
@ -11,6 +11,9 @@ class ContainerStore {
|
|||
|
||||
// Blacklist of containers to avoid updating
|
||||
this.muted = {};
|
||||
|
||||
// Pending container to create
|
||||
this.pending = null;
|
||||
}
|
||||
|
||||
start ({name}) {
|
||||
|
@ -106,7 +109,7 @@ class ContainerStore {
|
|||
this.setState({containers});
|
||||
}
|
||||
|
||||
error ({ name, error }) {
|
||||
error ({name, error}) {
|
||||
let containers = this.containers;
|
||||
if (containers[name]) {
|
||||
containers[name].Error = error;
|
||||
|
@ -114,6 +117,15 @@ class ContainerStore {
|
|||
this.setState({containers});
|
||||
}
|
||||
|
||||
pending ({repo, tag}) {
|
||||
let pending = {repo, tag};
|
||||
this.setState({pending});
|
||||
}
|
||||
|
||||
clearPending () {
|
||||
this.setState({pending: null});
|
||||
}
|
||||
|
||||
static generateName (repo) {
|
||||
let base = _.last(repo.split('/'));
|
||||
let count = 1;
|
||||
|
|
|
@ -4,13 +4,13 @@ var docker = require('../utils/DockerUtil');
|
|||
var ContainerUtil = {
|
||||
env: function (container) {
|
||||
if (!container || !container.Config || !container.Config.Env) {
|
||||
return {};
|
||||
return [];
|
||||
}
|
||||
return _.object(container.Config.Env.map(function (env) {
|
||||
return _.map(container.Config.Env, env => {
|
||||
var i = env.indexOf('=');
|
||||
var splits = [env.slice(0, i), env.slice(i + 1)];
|
||||
return splits;
|
||||
}));
|
||||
});
|
||||
},
|
||||
|
||||
// TODO: inject host here instead of requiring Docker
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
var util = require('./Util');
|
||||
var parseUri = require('parseUri');
|
||||
var containerStore = require('../stores/ContainerStore');
|
||||
var containerServerActions = require('../actions/ContainerServerActions');
|
||||
|
||||
module.exports = {
|
||||
TYPE_WHITELIST: ['repository'],
|
||||
|
@ -52,7 +52,7 @@ module.exports = {
|
|||
}
|
||||
|
||||
if (type === 'repository' && method === 'run') {
|
||||
containerStore.setPending(repo, 'latest');
|
||||
containerServerActions.pending({repo, tag: 'latest'});
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
|
Loading…
Reference in New Issue