https://github.com/docker/libnetwork/pull/810 provides the more complete
solution for moving the Port-mapping ownership away from endpoint and
into Sandbox. But, this PR makes the best use of existing libnetwork
design and get a step closer to the gaol.
Signed-off-by: Madhu Venugopal <madhu@docker.com>
| ... | ... |
@@ -129,18 +129,26 @@ func (container *Container) buildPortMapInfo(ep libnetwork.Endpoint) error {
|
| 129 | 129 |
return derr.ErrorCodeEmptyNetwork |
| 130 | 130 |
} |
| 131 | 131 |
|
| 132 |
+ if len(networkSettings.Ports) == 0 {
|
|
| 133 |
+ pm, err := getEndpointPortMapInfo(ep) |
|
| 134 |
+ if err != nil {
|
|
| 135 |
+ return err |
|
| 136 |
+ } |
|
| 137 |
+ networkSettings.Ports = pm |
|
| 138 |
+ } |
|
| 139 |
+ return nil |
|
| 140 |
+} |
|
| 141 |
+ |
|
| 142 |
+func getEndpointPortMapInfo(ep libnetwork.Endpoint) (nat.PortMap, error) {
|
|
| 143 |
+ pm := nat.PortMap{}
|
|
| 132 | 144 |
driverInfo, err := ep.DriverInfo() |
| 133 | 145 |
if err != nil {
|
| 134 |
- return err |
|
| 146 |
+ return pm, err |
|
| 135 | 147 |
} |
| 136 | 148 |
|
| 137 | 149 |
if driverInfo == nil {
|
| 138 | 150 |
// It is not an error for epInfo to be nil |
| 139 |
- return nil |
|
| 140 |
- } |
|
| 141 |
- |
|
| 142 |
- if networkSettings.Ports == nil {
|
|
| 143 |
- networkSettings.Ports = nat.PortMap{}
|
|
| 151 |
+ return pm, nil |
|
| 144 | 152 |
} |
| 145 | 153 |
|
| 146 | 154 |
if expData, ok := driverInfo[netlabel.ExposedPorts]; ok {
|
| ... | ... |
@@ -148,30 +156,45 @@ func (container *Container) buildPortMapInfo(ep libnetwork.Endpoint) error {
|
| 148 | 148 |
for _, tp := range exposedPorts {
|
| 149 | 149 |
natPort, err := nat.NewPort(tp.Proto.String(), strconv.Itoa(int(tp.Port))) |
| 150 | 150 |
if err != nil {
|
| 151 |
- return derr.ErrorCodeParsingPort.WithArgs(tp.Port, err) |
|
| 151 |
+ return pm, derr.ErrorCodeParsingPort.WithArgs(tp.Port, err) |
|
| 152 | 152 |
} |
| 153 |
- networkSettings.Ports[natPort] = nil |
|
| 153 |
+ pm[natPort] = nil |
|
| 154 | 154 |
} |
| 155 | 155 |
} |
| 156 | 156 |
} |
| 157 | 157 |
|
| 158 | 158 |
mapData, ok := driverInfo[netlabel.PortMap] |
| 159 | 159 |
if !ok {
|
| 160 |
- return nil |
|
| 160 |
+ return pm, nil |
|
| 161 | 161 |
} |
| 162 | 162 |
|
| 163 | 163 |
if portMapping, ok := mapData.([]types.PortBinding); ok {
|
| 164 | 164 |
for _, pp := range portMapping {
|
| 165 | 165 |
natPort, err := nat.NewPort(pp.Proto.String(), strconv.Itoa(int(pp.Port))) |
| 166 | 166 |
if err != nil {
|
| 167 |
- return err |
|
| 167 |
+ return pm, err |
|
| 168 | 168 |
} |
| 169 | 169 |
natBndg := nat.PortBinding{HostIP: pp.HostIP.String(), HostPort: strconv.Itoa(int(pp.HostPort))}
|
| 170 |
- networkSettings.Ports[natPort] = append(networkSettings.Ports[natPort], natBndg) |
|
| 170 |
+ pm[natPort] = append(pm[natPort], natBndg) |
|
| 171 | 171 |
} |
| 172 | 172 |
} |
| 173 | 173 |
|
| 174 |
- return nil |
|
| 174 |
+ return pm, nil |
|
| 175 |
+} |
|
| 176 |
+ |
|
| 177 |
+func getSandboxPortMapInfo(sb libnetwork.Sandbox) nat.PortMap {
|
|
| 178 |
+ pm := nat.PortMap{}
|
|
| 179 |
+ if sb == nil {
|
|
| 180 |
+ return pm |
|
| 181 |
+ } |
|
| 182 |
+ |
|
| 183 |
+ for _, ep := range sb.Endpoints() {
|
|
| 184 |
+ pm, _ = getEndpointPortMapInfo(ep) |
|
| 185 |
+ if len(pm) > 0 {
|
|
| 186 |
+ break |
|
| 187 |
+ } |
|
| 188 |
+ } |
|
| 189 |
+ return pm |
|
| 175 | 190 |
} |
| 176 | 191 |
|
| 177 | 192 |
// BuildEndpointInfo sets endpoint-related fields on container.NetworkSettings based on the provided network and endpoint. |
| ... | ... |
@@ -265,7 +288,7 @@ func (container *Container) BuildJoinOptions(n libnetwork.Network) ([]libnetwork |
| 265 | 265 |
} |
| 266 | 266 |
|
| 267 | 267 |
// BuildCreateEndpointOptions builds endpoint options from a given network. |
| 268 |
-func (container *Container) BuildCreateEndpointOptions(n libnetwork.Network, epConfig *network.EndpointSettings) ([]libnetwork.EndpointOption, error) {
|
|
| 268 |
+func (container *Container) BuildCreateEndpointOptions(n libnetwork.Network, epConfig *network.EndpointSettings, sb libnetwork.Sandbox) ([]libnetwork.EndpointOption, error) {
|
|
| 269 | 269 |
var ( |
| 270 | 270 |
portSpecs = make(nat.PortSet) |
| 271 | 271 |
bindings = make(nat.PortMap) |
| ... | ... |
@@ -294,10 +317,29 @@ func (container *Container) BuildCreateEndpointOptions(n libnetwork.Network, epC |
| 294 | 294 |
createOptions = append(createOptions, libnetwork.CreateOptionDisableResolution()) |
| 295 | 295 |
} |
| 296 | 296 |
|
| 297 |
- // Other configs are applicable only for the endpoint in the network |
|
| 297 |
+ // configs that are applicable only for the endpoint in the network |
|
| 298 | 298 |
// to which container was connected to on docker run. |
| 299 |
- if n.Name() != container.HostConfig.NetworkMode.NetworkName() && |
|
| 300 |
- !(n.Name() == "bridge" && container.HostConfig.NetworkMode.IsDefault()) {
|
|
| 299 |
+ // Ideally all these network-specific endpoint configurations must be moved under |
|
| 300 |
+ // container.NetworkSettings.Networks[n.Name()] |
|
| 301 |
+ if n.Name() == container.HostConfig.NetworkMode.NetworkName() || |
|
| 302 |
+ (n.Name() == "bridge" && container.HostConfig.NetworkMode.IsDefault()) {
|
|
| 303 |
+ if container.Config.MacAddress != "" {
|
|
| 304 |
+ mac, err := net.ParseMAC(container.Config.MacAddress) |
|
| 305 |
+ if err != nil {
|
|
| 306 |
+ return nil, err |
|
| 307 |
+ } |
|
| 308 |
+ |
|
| 309 |
+ genericOption := options.Generic{
|
|
| 310 |
+ netlabel.MacAddress: mac, |
|
| 311 |
+ } |
|
| 312 |
+ |
|
| 313 |
+ createOptions = append(createOptions, libnetwork.EndpointOptionGeneric(genericOption)) |
|
| 314 |
+ } |
|
| 315 |
+ } |
|
| 316 |
+ |
|
| 317 |
+ // Port-mapping rules belong to the container & applicable only to non-internal networks |
|
| 318 |
+ portmaps := getSandboxPortMapInfo(sb) |
|
| 319 |
+ if n.Info().Internal() || len(portmaps) > 0 {
|
|
| 301 | 320 |
return createOptions, nil |
| 302 | 321 |
} |
| 303 | 322 |
|
| ... | ... |
@@ -357,19 +399,6 @@ func (container *Container) BuildCreateEndpointOptions(n libnetwork.Network, epC |
| 357 | 357 |
libnetwork.CreateOptionPortMapping(pbList), |
| 358 | 358 |
libnetwork.CreateOptionExposedPorts(exposeList)) |
| 359 | 359 |
|
| 360 |
- if container.Config.MacAddress != "" {
|
|
| 361 |
- mac, err := net.ParseMAC(container.Config.MacAddress) |
|
| 362 |
- if err != nil {
|
|
| 363 |
- return nil, err |
|
| 364 |
- } |
|
| 365 |
- |
|
| 366 |
- genericOption := options.Generic{
|
|
| 367 |
- netlabel.MacAddress: mac, |
|
| 368 |
- } |
|
| 369 |
- |
|
| 370 |
- createOptions = append(createOptions, libnetwork.EndpointOptionGeneric(genericOption)) |
|
| 371 |
- } |
|
| 372 |
- |
|
| 373 | 360 |
return createOptions, nil |
| 374 | 361 |
} |
| 375 | 362 |
|
| ... | ... |
@@ -775,7 +775,8 @@ func (daemon *Daemon) connectToNetwork(container *container.Container, idOrName |
| 775 | 775 |
|
| 776 | 776 |
controller := daemon.netController |
| 777 | 777 |
|
| 778 |
- createOptions, err := container.BuildCreateEndpointOptions(n, endpointConfig) |
|
| 778 |
+ sb := daemon.getNetworkSandbox(container) |
|
| 779 |
+ createOptions, err := container.BuildCreateEndpointOptions(n, endpointConfig, sb) |
|
| 779 | 780 |
if err != nil {
|
| 780 | 781 |
return err |
| 781 | 782 |
} |
| ... | ... |
@@ -801,7 +802,6 @@ func (daemon *Daemon) connectToNetwork(container *container.Container, idOrName |
| 801 | 801 |
return err |
| 802 | 802 |
} |
| 803 | 803 |
|
| 804 |
- sb := daemon.getNetworkSandbox(container) |
|
| 805 | 804 |
if sb == nil {
|
| 806 | 805 |
options, err := daemon.buildSandboxOptions(container, n) |
| 807 | 806 |
if err != nil {
|
| ... | ... |
@@ -293,3 +293,24 @@ func (s *DockerSuite) TestPortExposeHostBinding(c *check.C) {
|
| 293 | 293 |
// Port is still bound after the Container is removed |
| 294 | 294 |
c.Assert(err, checker.NotNil, check.Commentf("out: %s", out))
|
| 295 | 295 |
} |
| 296 |
+ |
|
| 297 |
+func (s *DockerSuite) TestPortBindingOnSandbox(c *check.C) {
|
|
| 298 |
+ testRequires(c, DaemonIsLinux, NotUserNamespace) |
|
| 299 |
+ dockerCmd(c, "network", "create", "--internal", "-d", "bridge", "internal-net") |
|
| 300 |
+ dockerCmd(c, "run", "--net", "internal-net", "-d", "--name", "c1", |
|
| 301 |
+ "-p", "8080:8080", "busybox", "nc", "-l", "-p", "8080") |
|
| 302 |
+ c.Assert(waitRun("c1"), check.IsNil)
|
|
| 303 |
+ |
|
| 304 |
+ _, _, err := dockerCmdWithError("run", "--net=host", "busybox", "nc", "localhost", "8080")
|
|
| 305 |
+ c.Assert(err, check.NotNil, |
|
| 306 |
+ check.Commentf("Port mapping on internal network is expected to fail"))
|
|
| 307 |
+ |
|
| 308 |
+ // Connect container to another normal bridge network |
|
| 309 |
+ dockerCmd(c, "network", "create", "-d", "bridge", "foo-net") |
|
| 310 |
+ dockerCmd(c, "network", "connect", "foo-net", "c1") |
|
| 311 |
+ |
|
| 312 |
+ _, _, err = dockerCmdWithError("run", "--net=host", "busybox", "nc", "localhost", "8080")
|
|
| 313 |
+ c.Assert(err, check.IsNil, |
|
| 314 |
+ check.Commentf("Port mapping on the new network is expected to succeed"))
|
|
| 315 |
+ |
|
| 316 |
+} |