Browse code

Move port-mapping ownership closer to Sandbox (from Endpoint)

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>

Madhu Venugopal authored on 2016/01/26 12:55:18
Showing 3 changed files
... ...
@@ -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
+}