diff --git a/daemon/network.go b/daemon/network.go index 834e5c7c65..bfc7a75d83 100644 --- a/daemon/network.go +++ b/daemon/network.go @@ -1005,24 +1005,16 @@ func getPortMapInfo(sb *libnetwork.Sandbox) nat.PortMap { } for _, ep := range sb.Endpoints() { - pm = getEndpointPortMapInfo(ep) - if len(pm) > 0 { - break - } + getEndpointPortMapInfo(pm, ep) } return pm } -func getEndpointPortMapInfo(ep *libnetwork.Endpoint) nat.PortMap { - pm := nat.PortMap{} - driverInfo, err := ep.DriverInfo() - if err != nil { - return pm - } - +func getEndpointPortMapInfo(pm nat.PortMap, ep *libnetwork.Endpoint) { + driverInfo, _ := ep.DriverInfo() if driverInfo == nil { // It is not an error for epInfo to be nil - return pm + return } if expData, ok := driverInfo[netlabel.ExposedPorts]; ok { @@ -1033,14 +1025,16 @@ func getEndpointPortMapInfo(ep *libnetwork.Endpoint) nat.PortMap { log.G(context.TODO()).Errorf("invalid exposed port %s: %v", tp.String(), err) continue } - pm[natPort] = nil + if _, ok := pm[natPort]; !ok { + pm[natPort] = nil + } } } } mapData, ok := driverInfo[netlabel.PortMap] if !ok { - return pm + return } if portMapping, ok := mapData.([]lntypes.PortBinding); ok { @@ -1059,8 +1053,6 @@ func getEndpointPortMapInfo(ep *libnetwork.Endpoint) nat.PortMap { pm[natPort] = append(pm[natPort], natBndg) } } - - return pm } // buildEndpointInfo sets endpoint-related fields on container.NetworkSettings based on the provided network and endpoint. diff --git a/integration/network/bridge/bridge_linux_test.go b/integration/network/bridge/bridge_linux_test.go index 3b89e02232..c1ebcc408a 100644 --- a/integration/network/bridge/bridge_linux_test.go +++ b/integration/network/bridge/bridge_linux_test.go @@ -454,3 +454,42 @@ func TestPublishedPortAlreadyInUse(t *testing.T) { assert.NilError(t, err) assert.Check(t, is.Equal(inspect.State.Status, "created")) } + +// TestAllPortMappingsAreReturned check that dual-stack ports mapped through +// different networks are correctly reported as dual-stakc. +// +// Regression test for https://github.com/moby/moby/issues/49654. +func TestAllPortMappingsAreReturned(t *testing.T) { + ctx := setupTest(t) + + d := daemon.New(t) + d.StartWithBusybox(ctx, t, "--userland-proxy=false") + defer d.Stop(t) + + apiClient := d.NewClientT(t) + defer apiClient.Close() + + nwV4 := network.CreateNoError(ctx, t, apiClient, "testnetv4") + defer network.RemoveNoError(ctx, t, apiClient, nwV4) + + nwV6 := network.CreateNoError(ctx, t, apiClient, "testnetv6", + network.WithIPv4(false), + network.WithIPv6()) + defer network.RemoveNoError(ctx, t, apiClient, nwV6) + + ctrID := ctr.Run(ctx, t, apiClient, + ctr.WithExposedPorts("80/tcp", "81/tcp"), + ctr.WithPortMap(nat.PortMap{"80/tcp": {{HostPort: "8000"}}}), + ctr.WithEndpointSettings("testnetv4", &networktypes.EndpointSettings{}), + ctr.WithEndpointSettings("testnetv6", &networktypes.EndpointSettings{})) + defer ctr.Remove(ctx, t, apiClient, ctrID, containertypes.RemoveOptions{Force: true}) + + inspect := ctr.Inspect(ctx, t, apiClient, ctrID) + assert.DeepEqual(t, inspect.NetworkSettings.Ports, nat.PortMap{ + "80/tcp": []nat.PortBinding{ + {HostIP: "0.0.0.0", HostPort: "8000"}, + {HostIP: "::", HostPort: "8000"}, + }, + "81/tcp": nil, + }) +}