1
0
Fork 0
mirror of https://github.com/portainer/portainer.git synced 2025-07-22 23:09:41 +02:00

feat(containers): added support for port range mappings when deploying containers (#3194)

* feat(containers): added support for port range mappings when deploying containers

* feat(containers): added placeholders to port publishing input fields

* feat(containers): added a tooltip to the manual network port publishing

* feat(containers): improved the code consistency
This commit is contained in:
Mattias Edlund 2019-10-15 18:13:57 +09:00 committed by xAt0mZ
parent f67e866e7e
commit accca0f2a6
3 changed files with 195 additions and 42 deletions

View file

@ -1,5 +1,66 @@
import _ from 'lodash-es';
import splitargs from 'splitargs/src/splitargs'
const portPattern = /^([1-9]|[1-5]?[0-9]{2,4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])$/m;
function parsePort(port) {
if (portPattern.test(port)) {
return parseInt(port);
} else {
return 0;
}
}
function parsePortRange(portRange) {
if (typeof portRange !== 'string') {
portRange = portRange.toString();
}
// Split the range and convert to integers
const stringPorts = _.split(portRange, '-', 2);
const intPorts = _.map(stringPorts, parsePort);
// If it's not a range, we still make sure that we return two ports (start & end)
if (intPorts.length == 1) {
intPorts.push(intPorts[0]);
}
return intPorts;
}
function isValidPortRange(portRange) {
if (typeof portRange === 'string') {
portRange = parsePortRange();
}
return Array.isArray(portRange) && portRange.length === 2 &&
portRange[0] > 0 && portRange[1] >= portRange[0];
}
function createPortRange(portRangeText, port) {
if (typeof portRangeText !== 'string') {
portRangeText = portRangeText.toString();
}
let hostIp = null;
const colonIndex = portRangeText.indexOf(':');
if (colonIndex >= 0) {
hostIp = portRangeText.substr(0, colonIndex);
portRangeText = portRangeText.substr(colonIndex + 1);
}
port = (typeof port === 'number' ? port : parsePort(port));
const portRange = parsePortRange(portRangeText);
const startPort = Math.min(portRange[0], port);
const endPort = Math.max(portRange[1], port);
if (hostIp) {
return hostIp + ':' + startPort + '-' + endPort;
} else {
return startPort + '-' + endPort;
}
}
angular.module('portainer.docker')
.factory('ContainerHelper', [function ContainerHelperFactory() {
'use strict';
@ -54,5 +115,124 @@ angular.module('portainer.docker')
return config;
};
helper.preparePortBindings = function(portBindings) {
const bindings = {};
_.forEach(portBindings, (portBinding) => {
if (!portBinding.containerPort) {
return;
}
let hostPort = portBinding.hostPort;
const containerPortRange = parsePortRange(portBinding.containerPort);
if (!isValidPortRange(containerPortRange)) {
throw new Error('Invalid port specification: ' + portBinding.containerPort);
}
const startPort = containerPortRange[0];
const endPort = containerPortRange[1];
let hostIp = undefined;
let startHostPort = 0;
let endHostPort = 0;
if (hostPort) {
if (hostPort.indexOf(':') > -1) {
const hostAndPort = _.split(hostPort, ':');
hostIp = hostAndPort[0];
hostPort = hostAndPort[1];
}
const hostPortRange = parsePortRange(hostPort);
if (!isValidPortRange(hostPortRange)) {
throw new Error('Invalid port specification: ' + hostPort);
}
startHostPort = hostPortRange[0];
endHostPort = hostPortRange[1];
if (endPort !== startPort && (endPort - startPort) !== (endHostPort - startHostPort)) {
throw new Error('Invalid port specification: ' + hostPort);
}
}
for (let i = 0; i <= (endPort - startPort); i++) {
const containerPort = (startPort + i).toString();
if (startHostPort > 0) {
hostPort = (startHostPort + i).toString();
}
if (startPort === endPort && startHostPort !== endHostPort) {
hostPort += '-' + endHostPort.toString();
}
const bindKey = containerPort + '/' + portBinding.protocol;
bindings[bindKey] = [{ HostIp: hostIp, HostPort: hostPort }];
}
});
return bindings;
};
helper.sortAndCombinePorts = function(portBindings) {
const bindings = [];
const portBindingKeys = _.keys(portBindings);
// Group the port bindings by protocol
const portBindingKeysByProtocol = _.groupBy(portBindingKeys, (portKey) => {
return _.split(portKey, '/')[1];
});
_.forEach(portBindingKeysByProtocol, (portBindingKeys, protocol) => {
// Group the port bindings by host IP
const portBindingKeysByHostIp = _.groupBy(portBindingKeys, (portKey) => {
const portBinding = portBindings[portKey][0];
return portBinding.HostIp || '';
});
_.forEach(portBindingKeysByHostIp, (portBindingKeys) => {
// Sort by host port
const sortedPortBindingKeys = _.orderBy(portBindingKeys, (portKey) => {
return parseInt(_.split(portKey, '/')[0]);
});
let previousHostPort = -1;
let previousContainerPort = -1;
_.forEach(sortedPortBindingKeys, (portKey) => {
const portKeySplit = _.split(portKey, '/');
const containerPort = parseInt(portKeySplit[0]);
const portBinding = portBindings[portKey][0];
const hostPort = parsePort(portBinding.HostPort);
// We only combine single ports, and skip the host port ranges on one container port
if (hostPort > 0) {
// If we detect consecutive ports, we create a range of them
if (bindings.length > 0 && previousHostPort === (hostPort - 1) && previousContainerPort === (containerPort - 1)) {
bindings[bindings.length-1].hostPort = createPortRange(bindings[bindings.length-1].hostPort, hostPort);
bindings[bindings.length-1].containerPort = createPortRange(bindings[bindings.length-1].containerPort, containerPort);
previousHostPort = hostPort;
previousContainerPort = containerPort;
return;
}
previousHostPort = hostPort;
previousContainerPort = containerPort;
} else {
previousHostPort = -1;
previousContainerPort = -1;
}
let bindingHostPort = portBinding.HostPort.toString();
if (portBinding.HostIp) {
bindingHostPort = portBinding.HostIp + ':' + bindingHostPort;
}
const binding = {
hostPort: bindingHostPort,
containerPort: containerPort,
protocol: protocol
};
bindings.push(binding);
});
});
});
return bindings;
};
return helper;
}]);