Skip to content
This repository has been archived by the owner on Jun 11, 2021. It is now read-only.

Add custom volumes as an option #4401

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 95 additions & 15 deletions src/components/ContainerSettingsVolumes.react.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,28 @@ import metrics from '../utils/MetricsUtil';
import containerActions from '../actions/ContainerActions';

var ContainerSettingsVolumes = React.createClass({
getInitialState: function () {
let container = this.props.container;

if (!container) {
return false;
}
let mounts = _.clone(container.Mounts);

mounts.push({
Destination: undefined,
Mode: 'rw',
Propagation: 'rpirvate',
RW: true,
Source: undefined,
Type: 'bind'
});

return {
containerName: container.Name,
mounts
}
},
handleChooseVolumeClick: function (dockerVol) {
dialog.showOpenDialog({properties: ['openDirectory', 'createDirectory']}, (filenames) => {
if (!filenames) {
Expand All @@ -28,43 +50,37 @@ var ContainerSettingsVolumes = React.createClass({

metrics.track('Choose Directory for Volume');

let mounts = _.clone(this.props.container.Mounts);
let mounts = _.clone(this.state.mounts);
_.each(mounts, m => {
if (m.Destination === dockerVol) {
m.Source = util.windowsToLinuxPath(directory);
m.Driver = null;
}
});

let binds = mounts.map(m => {
return m.Source + ':' + m.Destination;
this.setState({
mounts
});

let hostConfig = _.extend(this.props.container.HostConfig, {Binds: binds});

containerActions.update(this.props.container.Name, {Mounts: mounts, HostConfig: hostConfig});
});
},
handleRemoveVolumeClick: function (dockerVol) {
metrics.track('Removed Volume Directory', {
from: 'settings'
});

let mounts = _.clone(this.props.container.Mounts);
let mounts = _.clone(this.state.mounts);
_.each(mounts, m => {
if (m.Destination === dockerVol) {
m.Source = null;
m.Driver = 'local';
}
});

let binds = mounts.map(m => {
return m.Source + ':' + m.Destination;
this.setState({
mounts
});

let hostConfig = _.extend(this.props.container.HostConfig, {Binds: binds});

containerActions.update(this.props.container.Name, {Mounts: mounts, HostConfig: hostConfig});
},
handleOpenVolumeClick: function (path) {
metrics.track('Opened Volume Directory', {
Expand All @@ -76,13 +92,62 @@ var ContainerSettingsVolumes = React.createClass({
shell.showItemInFolder(path);
}
},
handleAddVolume: function () {
let mounts = _.clone(this.state.mounts);

// undefined is clearer when reading the code
mounts.push({
Destination: undefined,
Mode: 'rw',
Propagation: 'rpirvate',
RW: true,
Source: undefined,
Type: 'bind'
});

this.setState({
mounts
});

metrics.track('Adding Pending Volume')
},
handleRemoveVolume: function (index) {
let mounts = this.state.mounts.filter((val, idx) => idx !== index);

this.setState({
mounts
});

metrics.track('Removed Volume')
},
handleDestinationChanged: function (index, event) {
let mounts = _.clone(this.state.mounts);
mounts[index].Destination = event.target.value;

this.setState({
mounts
});
},
handleSaveVolumes: function() {
let mounts = this.state.mounts;
let binds = mounts.filter(m => {
aaomidi marked this conversation as resolved.
Show resolved Hide resolved
// Filter out everything that is empty/null
return !(!m.Destination || !m.Source || m.Destination === '' || m.Source === '');
}).map(m => {
return m.Source + ':' + m.Destination;
});

let hostConfig = _.extend(this.props.container.HostConfig, {Binds: binds});

containerActions.update(this.props.container.Name, {Mounts: mounts, HostConfig: hostConfig});
},
render: function () {
if (!this.props.container) {
return false;
}

var homeDir = util.isWindows() ? util.windowsToLinuxPath(util.home()) : util.home();
var mounts = _.map(this.props.container.Mounts, (m, i) => {
var mounts = _.map(this.state.mounts, (m, index) => {
let source = m.Source, destination = m.Destination;
if (!m.Source || (!util.isNative() && m.Source.indexOf(homeDir) === -1) || (m.Source.indexOf('/var/lib/docker/volumes') !== -1)) {
source = (
Expand All @@ -94,14 +159,26 @@ var ContainerSettingsVolumes = React.createClass({
<a className="value-right" onClick={this.handleOpenVolumeClick.bind(this, source)}>{local.replace(process.env.HOME, '~')}</a>
);
}

let icon;
if (index === this.state.mounts.length - 1) {
icon = <a onClick={this.handleAddVolume} className="only-icon btn btn-positive small"><span
className="icon icon-add"></span></a>;
} else {
icon = <a onClick={this.handleRemoveVolume.bind(this, index)} className="only-icon btn btn-action small"><span
className="icon icon-delete"></span></a>;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should probably take this piece of code out of this function

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, I'm not sure where to place it. Could I leave this one up to you? I was following the same code style as the other ContainerSetting pages:

if (index === this.state.env.length - 1) {
icon = <a onClick={this.handleAddEnvVar} className="only-icon btn btn-positive small"><span className="icon icon-add"></span></a>;
} else {
icon = <a onClick={this.handleRemoveEnvVar.bind(this, index)} className="only-icon btn btn-action small"><span className="icon icon-delete"></span></a>;
}

return (
<tr>
<td>{destination}</td>
<td>
<input type="text" className="destination line" defaultValue={destination}
onChange={this.handleDestinationChanged.bind(this, index)}></input>
</td>
<td>{source}</td>
<td>
<a className="btn btn-action small" disabled={this.props.container.State.Updating} onClick={this.handleChooseVolumeClick.bind(this, destination)}>Change</a>
<a className="btn btn-action small" disabled={this.props.container.State.Updating} onClick={this.handleRemoveVolumeClick.bind(this, destination)}>Remove</a>
</td>
<td>{icon}</td>
</tr>
);
});
Expand All @@ -121,6 +198,9 @@ var ContainerSettingsVolumes = React.createClass({
{mounts}
</tbody>
</table>
<div className="settings-section">
<a className="btn btn-action" disabled={this.props.container.State.Updating} onClick={this.handleSaveVolumes}>Save</a>
</div>
</div>
</div>
);
Expand Down