From d2f192821ef190387da854ecd91c7f8885651f7c Mon Sep 17 00:00:00 2001 From: Nick Ethier Date: Tue, 2 Feb 2021 22:54:23 -0500 Subject: [PATCH 1/2] drivers/docker: support mapping multiple host ports to the same container port --- drivers/docker/driver_default.go | 4 ++-- drivers/docker/driver_windows.go | 4 ++-- drivers/docker/ports.go | 32 +++++++++++++++++++++++--------- drivers/docker/ports_test.go | 19 +++++++++++++++++++ 4 files changed, 46 insertions(+), 13 deletions(-) create mode 100644 drivers/docker/ports_test.go diff --git a/drivers/docker/driver_default.go b/drivers/docker/driver_default.go index 0460b193b..295706bd9 100644 --- a/drivers/docker/driver_default.go +++ b/drivers/docker/driver_default.go @@ -7,8 +7,8 @@ import ( docker "github.com/fsouza/go-dockerclient" ) -func getPortBinding(ip string, port string) []docker.PortBinding { - return []docker.PortBinding{{HostIP: ip, HostPort: port}} +func getPortBinding(ip string, port string) docker.PortBinding { + return docker.PortBinding{HostIP: ip, HostPort: port} } func tweakCapabilities(basics, adds, drops []string) ([]string, error) { diff --git a/drivers/docker/driver_windows.go b/drivers/docker/driver_windows.go index 70c11bbaa..1e0364a5f 100644 --- a/drivers/docker/driver_windows.go +++ b/drivers/docker/driver_windows.go @@ -3,8 +3,8 @@ package docker import docker "github.com/fsouza/go-dockerclient" //Currently Windows containers don't support host ip in port binding. -func getPortBinding(ip string, port string) []docker.PortBinding { - return []docker.PortBinding{{HostIP: "", HostPort: port}} +func getPortBinding(ip string, port string) docker.PortBinding { + return docker.PortBinding{HostIP: "", HostPort: port} } func tweakCapabilities(basics, adds, drops []string) ([]string, error) { diff --git a/drivers/docker/ports.go b/drivers/docker/ports.go index e3df4cdef..e8a8488ff 100644 --- a/drivers/docker/ports.go +++ b/drivers/docker/ports.go @@ -8,6 +8,9 @@ import ( "github.com/hashicorp/nomad/helper/pluginutils/hclutils" ) +// publishedPorts is a utility struct to keep track of the port bindings to publish. +// After calling add for each port, the publishedPorts and exposedPorts fields can be +// used in the docker container and host configs type publishedPorts struct { logger hclog.Logger publishedPorts map[docker.Port][]docker.PortBinding @@ -22,7 +25,7 @@ func newPublishedPorts(logger hclog.Logger) *publishedPorts { } } -// adds the port to the structures the Docker API expects for declaring mapped ports +// addMapped adds the port to the structures the Docker API expects for declaring mapped ports func (p *publishedPorts) addMapped(label, ip string, port int, portMap hclutils.MapStrInt) { // By default we will map the allocated port 1:1 to the container containerPortInt := port @@ -35,18 +38,29 @@ func (p *publishedPorts) addMapped(label, ip string, port int, portMap hclutils. p.add(label, ip, port, containerPortInt) } +// add adds a port binding for the given port mapping func (p *publishedPorts) add(label, ip string, port, to int) { + // if to is not set, use the port value per default docker functionality if to == 0 { to = port } - hostPortStr := strconv.Itoa(port) - containerPort := docker.Port(strconv.Itoa(to)) - p.publishedPorts[containerPort+"/tcp"] = getPortBinding(ip, hostPortStr) - p.publishedPorts[containerPort+"/udp"] = getPortBinding(ip, hostPortStr) - p.logger.Debug("allocated static port", "ip", ip, "port", port) + // two docker port bindings are created for each port for tcp and udp + cPortTCP := docker.Port(strconv.Itoa(to) + "/tcp") + cPortUDP := docker.Port(strconv.Itoa(to) + "/udp") + binding := getPortBinding(ip, strconv.Itoa(port)) - p.exposedPorts[containerPort+"/tcp"] = struct{}{} - p.exposedPorts[containerPort+"/udp"] = struct{}{} - p.logger.Debug("exposed port", "port", port) + if _, ok := p.publishedPorts[cPortTCP]; !ok { + // initialize both tcp and udp binding slices since they are always created together + p.publishedPorts[cPortTCP] = []docker.PortBinding{} + p.publishedPorts[cPortUDP] = []docker.PortBinding{} + } + + p.publishedPorts[cPortTCP] = append(p.publishedPorts[cPortTCP], binding) + p.publishedPorts[cPortUDP] = append(p.publishedPorts[cPortUDP], binding) + p.logger.Debug("allocated static port", "ip", ip, "port", port, "label", label) + + p.exposedPorts[cPortTCP] = struct{}{} + p.exposedPorts[cPortUDP] = struct{}{} + p.logger.Debug("exposed port", "port", port, "label", label) } diff --git a/drivers/docker/ports_test.go b/drivers/docker/ports_test.go new file mode 100644 index 000000000..2500f19ff --- /dev/null +++ b/drivers/docker/ports_test.go @@ -0,0 +1,19 @@ +package docker + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/hashicorp/nomad/helper/testlog" +) + +func TestPublishedPorts_add(t *testing.T) { + p := newPublishedPorts(testlog.HCLogger(t)) + p.add("label", "10.0.0.1", 1234, 80) + p.add("label", "10.0.0.1", 5678, 80) + for _, bindings := range p.publishedPorts { + require.Len(t, bindings, 2) + } + require.Len(t, p.exposedPorts, 2) +} From 05ea452d2c785b59551e2ad7abf5928a016470a9 Mon Sep 17 00:00:00 2001 From: Nick Ethier Date: Tue, 2 Feb 2021 23:00:03 -0500 Subject: [PATCH 2/2] add changelog item --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d55860333..8143ffd89 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ BUG FIXES: * consul: Fixed a bug where failing tasks with group services would only cause the allocation to restart once instead of respecting the `restart` field. [[GH-9869](https://github.com/hashicorp/nomad/issues/9869)] * consul/connect: Fixed a bug where gateway proxy connection default timeout not set [[GH-9851](https://github.com/hashicorp/nomad/pull/9851)] * consul/connect: Fixed a bug preventing more than one connect gateway per Nomad client [[GH-9849](https://github.com/hashicorp/nomad/pull/9849)] + * drivers/docker: Fixed a bug preventing multiple ports to be mapped to the same container port [[GH-9951](https://github.com/hashicorp/nomad/issues/9951)] * scheduler: Fixed a bug where shared ports were not persisted during inplace updates for service jobs. [[GH-9830](https://github.com/hashicorp/nomad/issues/9830)] * scheduler: Fixed a bug where job statuses and summaries where duplicated and miscalculated when registering a job. [[GH-9768](https://github.com/hashicorp/nomad/issues/9768)] * scheduler (Enterprise): Fixed a bug where the deprecated network `mbits` field was being considered as part of quota enforcement. [[GH-9920](https://github.com/hashicorp/nomad/issues/9920)]