diff --git a/src/components/ContainerSettingsPorts.react.js b/src/components/ContainerSettingsPorts.react.js index 83fbd198e8..b3c75e1d92 100644 --- a/src/components/ContainerSettingsPorts.react.js +++ b/src/components/ContainerSettingsPorts.react.js @@ -5,6 +5,7 @@ import ContainerUtil from '../utils/ContainerUtil'; import containerActions from '../actions/ContainerActions'; import containerStore from '../stores/ContainerStore'; import metrics from '../utils/MetricsUtil'; +import docker from '../utils/DockerUtil'; import {webPorts} from '../utils/Util'; import {DropdownButton, MenuItem} from 'react-bootstrap'; @@ -13,8 +14,18 @@ var ContainerSettingsPorts = React.createClass({ router: React.PropTypes.func }, getInitialState: function () { + var ports = ContainerUtil.ports(this.props.container); + var initialPorts = this.props.container.InitialPorts; + ports[''] = { + ip: docker.host, + url: '', + port: '', + portType: 'tcp', + error: null + }; return { - ports: ContainerUtil.ports(this.props.container) + ports: ports, + initialPorts: initialPorts }; }, handleViewLink: function (url) { @@ -23,32 +34,67 @@ var ContainerSettingsPorts = React.createClass({ }); shell.openExternal(url); }, - handleChangePort: function(key, e) { - let ports = this.state.ports; - let port = e.target.value; - - // save updated port - ports[key] = _.extend(ports[key], { - url: 'http://' + ports[key]['ip'] + ':' + port, - port: port, - error: null - }); + createEmptyPort: function (ports) { + ports[''] = { + ip: docker.host, + url: '', + port: '', + portType: 'tcp' + }; + document.getElementById('portKey').value = ''; + document.getElementById('portValue').value = ''; + }, + addPort: function () { + var portKey = document.getElementById('portKey').value; + var portValue = document.getElementById('portValue').value; + var portTypeValue = document.getElementById('portType').textContent; + var ports = this.state.ports; + if (portKey !== '') { + ports[portKey] = { + ip: docker.host, + url: docker.host + ':' + portValue, + port: portValue, + portType: portTypeValue.trim(), + error: null + }; + this.checkPort(ports, portKey, portKey); + if (ports[portKey].error === null) { + this.createEmptyPort(ports); + } + } + return ports; + }, + handleAddPort: function (e) { + var ports = this.addPort(); + this.setState({ports: ports}); + metrics.track('Added Pending Port'); + }, + checkPort: function (ports, port, key) { // basic validation, if number is integer, if its in range, if there // is no collision with ports of other containers and also if there is no // collision with ports for current container const otherContainers = _.filter(_.values(containerStore.getState().containers), c => c.Name !== this.props.container.Name); const otherPorts = _.flatten(otherContainers.map(container => { - return _.values(container.NetworkSettings.Ports).map(hosts => hosts.map(host => { - return {port: host.HostPort, name: container.Name} - })); + try { + return _.values(container.NetworkSettings.Ports).map(hosts => hosts.map(host => { + return {port: host.HostPort, name: container.Name}; + }) + ); + }catch (err) { + + } })).reduce((prev, pair) => { - prev[pair.port] = pair.name; + try { + prev[pair.port] = pair.name; + }catch (err) { + + } return prev; }, {}); const duplicates = _.filter(ports, (v, i) => { - return (i != key && _.isEqual(v.port, port)); + return (i !== key && _.isEqual(v.port, port)); }); if (!port.match(/^[0-9]+$/g)) { @@ -56,12 +102,41 @@ var ContainerSettingsPorts = React.createClass({ } else if (port <= 0 || port > 65535) { ports[key].error = 'Needs to be in range <1,65535>.'; } else if (otherPorts[port]) { - ports[key].error = 'Collision with container "'+ otherPorts[port] +'"'; + ports[key].error = 'Collision with container "' + otherPorts[port] + '"'; } else if (duplicates.length > 0) { ports[key].error = 'Collision with another port in this container.'; - } else if (port == 22 || port == 2376) { + } else if (port === 22 || port === 2376) { ports[key].error = 'Ports 22 and 2376 are reserved ports for Kitematic/Docker.'; } + }, + handleChangePort: function (key, e) { + let ports = this.state.ports; + let port = e.target.value; + // save updated port + ports[key] = _.extend(ports[key], { + url: 'http://' + ports[key].ip + ':' + port, + port: port, + error: null + }); + this.checkPort(ports, port, key); + + this.setState({ports: ports}); + }, + handleChangePortKey: function (key, e) { + let ports = this.state.ports; + let portKey = e.target.value; + + // save updated port + var currentPort = ports[key]; + + delete ports[key]; + ports[portKey] = currentPort; + + this.setState({ports: ports}); + }, + handleRemovePort: function (key, e) { + let ports = this.state.ports; + delete ports[key]; this.setState({ports: ports}); }, handleChangePortType: function (key, portType) { @@ -77,19 +152,38 @@ var ContainerSettingsPorts = React.createClass({ }); this.setState({ports: ports}); }, + isInitialPort: function (key, ports) { + for (var idx in ports) { + if (ports.hasOwnProperty(idx)) { + var p = idx.split('/'); + if (p.length > 0) { + if (p[0] === key) { + return true; + } + } + } + } + return false; + }, handleSave: function () { - let exposedPorts = {}; - let portBindings = _.reduce(this.state.ports, (res, value, key) => { - res[key + '/' + value.portType] = [{ - HostPort: value.port - }]; - exposedPorts[key] = {}; + let ports = this.state.ports; + ports = this.addPort(); + this.setState({ports: ports}); + let bindings = _.reduce(ports, (res, value, key) => { + if (key !== '') { + res[key + '/' + value.portType] = [{ + HostPort: value.port + }]; + } return res; }, {}); - let hostConfig = _.extend(this.props.container.HostConfig, {PortBindings: portBindings}); + containerActions.update(this.props.container.Name, { + NetworkSettings: { + Ports: bindings + } + }); - containerActions.update(this.props.container.Name, {ExposedPorts: exposedPorts, HostConfig: hostConfig}); }, render: function () { if (!this.props.container) { @@ -102,24 +196,37 @@ var ContainerSettingsPorts = React.createClass({ var key = pair[0]; var {ip, port, url, portType, error} = pair[1]; isValid = (error) ? false : isValid; - let ipLink = (this.props.container.State.Running && !this.props.container.State.Paused && !this.props.container.State.ExitCode && !this.props.container.State.Restarting) ? ({ip}):({ip}); + let ipLink = (this.props.container.State.Running && !this.props.container.State.Paused && !this.props.container.State.ExitCode && !this.props.container.State.Restarting) ? ({ip}) : ({ip}); + var icon = ''; + var portKey = ''; + var portValue = ''; + if (key === '') { + icon = ; + portKey = ; + portValue = ; + }else { + if (this.isInitialPort(key, this.state.initialPorts)) { + icon = ; + }else { + icon = ; + } + portKey = ; + portValue = ; + } return ( - {key} + {portKey} {ipLink}: - + {portValue} - + TCP UDP + {icon} {error} ); diff --git a/src/utils/DockerUtil.js b/src/utils/DockerUtil.js index a06765d330..ea6f799cfb 100644 --- a/src/utils/DockerUtil.js +++ b/src/utils/DockerUtil.js @@ -85,6 +85,7 @@ export default { startContainer (name) { let container = this.client.getContainer(name); + container.start((error) => { if (error) { containerServerActions.error({name, error}); @@ -117,7 +118,7 @@ export default { if (!containerData.HostConfig || (containerData.HostConfig && !containerData.HostConfig.PortBindings)) { containerData.PublishAllPorts = true; } - + if (image.Config.Cmd) { containerData.Cmd = image.Config.Cmd; } else if (!image.Config.Entrypoint) { @@ -148,6 +149,14 @@ export default { containerServerActions.error({name: id, error}); } else { container.Name = container.Name.replace('/', ''); + this.client.getImage(container.Image).inspect((error, image) => { + if (error) { + containerServerActions.error({name, error}); + return; + } + container.InitialPorts = image.Config.ExposedPorts; + }); + containerServerActions.updated({container}); } }); @@ -165,6 +174,13 @@ export default { return; } container.Name = container.Name.replace('/', ''); + this.client.getImage(container.Image).inspect((error, image) => { + if (error) { + containerServerActions.error({name, error}); + return; + } + container.InitialPorts = image.Config.ExposedPorts; + }); callback(null, container); }); }, (err, containers) => {