Browse code

Merge pull request #30897 from yongtang/30242-network-duplicate-names

Update `FindNetwork` to address network name duplications

Yong Tang authored on 2018/01/10 22:30:58
Showing 13 changed files
... ...
@@ -12,11 +12,11 @@ import (
12 12
 // Backend is all the methods that need to be implemented
13 13
 // to provide network specific functionality.
14 14
 type Backend interface {
15
-	FindNetwork(idName string) (libnetwork.Network, error)
15
+	FindUniqueNetwork(idName string) (libnetwork.Network, error)
16 16
 	GetNetworks() []libnetwork.Network
17 17
 	CreateNetwork(nc types.NetworkCreateRequest) (*types.NetworkCreateResponse, error)
18 18
 	ConnectContainerToNetwork(containerName, networkName string, endpointConfig *network.EndpointSettings) error
19 19
 	DisconnectContainerFromNetwork(containerName string, networkName string, force bool) error
20
-	DeleteNetwork(name string) error
20
+	DeleteNetwork(networkID string) error
21 21
 	NetworksPrune(ctx context.Context, pruneFilters filters.Args) (*types.NetworksPruneReport, error)
22 22
 }
... ...
@@ -2,6 +2,7 @@ package network
2 2
 
3 3
 import (
4 4
 	"encoding/json"
5
+	"fmt"
5 6
 	"net/http"
6 7
 	"strconv"
7 8
 	"strings"
... ...
@@ -288,7 +289,12 @@ func (n *networkRouter) postNetworkConnect(ctx context.Context, w http.ResponseW
288 288
 		return err
289 289
 	}
290 290
 
291
-	return n.backend.ConnectContainerToNetwork(connect.Container, vars["id"], connect.EndpointConfig)
291
+	// Always make sure there is no ambiguity with respect to the network ID/name
292
+	nw, err := n.backend.FindUniqueNetwork(vars["id"])
293
+	if err != nil {
294
+		return err
295
+	}
296
+	return n.backend.ConnectContainerToNetwork(connect.Container, nw.ID(), connect.EndpointConfig)
292 297
 }
293 298
 
294 299
 func (n *networkRouter) postNetworkDisconnect(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
... ...
@@ -312,15 +318,19 @@ func (n *networkRouter) deleteNetwork(ctx context.Context, w http.ResponseWriter
312 312
 	if err := httputils.ParseForm(r); err != nil {
313 313
 		return err
314 314
 	}
315
-	if _, err := n.cluster.GetNetwork(vars["id"]); err == nil {
316
-		if err = n.cluster.RemoveNetwork(vars["id"]); err != nil {
315
+
316
+	nw, err := n.findUniqueNetwork(vars["id"])
317
+	if err != nil {
318
+		return err
319
+	}
320
+	if nw.Scope == "swarm" {
321
+		if err = n.cluster.RemoveNetwork(nw.ID); err != nil {
322
+			return err
323
+		}
324
+	} else {
325
+		if err := n.backend.DeleteNetwork(nw.ID); err != nil {
317 326
 			return err
318 327
 		}
319
-		w.WriteHeader(http.StatusNoContent)
320
-		return nil
321
-	}
322
-	if err := n.backend.DeleteNetwork(vars["id"]); err != nil {
323
-		return err
324 328
 	}
325 329
 	w.WriteHeader(http.StatusNoContent)
326 330
 	return nil
... ...
@@ -518,3 +528,79 @@ func (n *networkRouter) postNetworksPrune(ctx context.Context, w http.ResponseWr
518 518
 	}
519 519
 	return httputils.WriteJSON(w, http.StatusOK, pruneReport)
520 520
 }
521
+
522
+// findUniqueNetwork will search network across different scopes (both local and swarm).
523
+// NOTE: This findUniqueNetwork is different from FindUniqueNetwork in the daemon.
524
+// In case multiple networks have duplicate names, return error.
525
+// First find based on full ID, return immediately once one is found.
526
+// If a network appears both in swarm and local, assume it is in local first
527
+// For full name and partial ID, save the result first, and process later
528
+// in case multiple records was found based on the same term
529
+// TODO (yongtang): should we wrap with version here for backward compatibility?
530
+func (n *networkRouter) findUniqueNetwork(term string) (types.NetworkResource, error) {
531
+	listByFullName := map[string]types.NetworkResource{}
532
+	listByPartialID := map[string]types.NetworkResource{}
533
+
534
+	nw := n.backend.GetNetworks()
535
+	for _, network := range nw {
536
+		if network.ID() == term {
537
+			return *n.buildDetailedNetworkResources(network, false), nil
538
+
539
+		}
540
+		if network.Name() == term && !network.Info().Ingress() {
541
+			// No need to check the ID collision here as we are still in
542
+			// local scope and the network ID is unique in this scope.
543
+			listByFullName[network.ID()] = *n.buildDetailedNetworkResources(network, false)
544
+		}
545
+		if strings.HasPrefix(network.ID(), term) {
546
+			// No need to check the ID collision here as we are still in
547
+			// local scope and the network ID is unique in this scope.
548
+			listByPartialID[network.ID()] = *n.buildDetailedNetworkResources(network, false)
549
+		}
550
+	}
551
+
552
+	nr, _ := n.cluster.GetNetworks()
553
+	for _, network := range nr {
554
+		if network.ID == term {
555
+			return network, nil
556
+		}
557
+		if network.Name == term {
558
+			// Check the ID collision as we are in swarm scope here, and
559
+			// the map (of the listByFullName) may have already had a
560
+			// network with the same ID (from local scope previously)
561
+			if _, ok := listByFullName[network.ID]; !ok {
562
+				listByFullName[network.ID] = network
563
+			}
564
+		}
565
+		if strings.HasPrefix(network.ID, term) {
566
+			// Check the ID collision as we are in swarm scope here, and
567
+			// the map (of the listByPartialID) may have already had a
568
+			// network with the same ID (from local scope previously)
569
+			if _, ok := listByPartialID[network.ID]; !ok {
570
+				listByPartialID[network.ID] = network
571
+			}
572
+		}
573
+	}
574
+
575
+	// Find based on full name, returns true only if no duplicates
576
+	if len(listByFullName) == 1 {
577
+		for _, v := range listByFullName {
578
+			return v, nil
579
+		}
580
+	}
581
+	if len(listByFullName) > 1 {
582
+		return types.NetworkResource{}, fmt.Errorf("network %s is ambiguous (%d matches found based on name)", term, len(listByFullName))
583
+	}
584
+
585
+	// Find based on partial ID, returns true only if no duplicates
586
+	if len(listByPartialID) == 1 {
587
+		for _, v := range listByPartialID {
588
+			return v, nil
589
+		}
590
+	}
591
+	if len(listByPartialID) > 1 {
592
+		return types.NetworkResource{}, fmt.Errorf("network %s is ambiguous (%d matches found based on ID prefix)", term, len(listByPartialID))
593
+	}
594
+
595
+	return types.NetworkResource{}, libnetwork.ErrNoSuchNetwork(term)
596
+}
... ...
@@ -27,8 +27,8 @@ import (
27 27
 // Backend defines the executor component for a swarm agent.
28 28
 type Backend interface {
29 29
 	CreateManagedNetwork(clustertypes.NetworkCreateRequest) error
30
-	DeleteManagedNetwork(name string) error
31
-	FindNetwork(idName string) (libnetwork.Network, error)
30
+	DeleteManagedNetwork(networkID string) error
31
+	FindUniqueNetwork(idName string) (libnetwork.Network, error)
32 32
 	SetupIngress(clustertypes.NetworkCreateRequest, string) (<-chan struct{}, error)
33 33
 	ReleaseIngress() (<-chan struct{}, error)
34 34
 	PullImage(ctx context.Context, image, tag, platform string, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error
... ...
@@ -143,8 +143,8 @@ func (c *containerAdapter) pullImage(ctx context.Context) error {
143 143
 }
144 144
 
145 145
 func (c *containerAdapter) createNetworks(ctx context.Context) error {
146
-	for _, network := range c.container.networks() {
147
-		ncr, err := c.container.networkCreateRequest(network)
146
+	for name := range c.container.networksAttachments {
147
+		ncr, err := c.container.networkCreateRequest(name)
148 148
 		if err != nil {
149 149
 			return err
150 150
 		}
... ...
@@ -162,15 +162,15 @@ func (c *containerAdapter) createNetworks(ctx context.Context) error {
162 162
 }
163 163
 
164 164
 func (c *containerAdapter) removeNetworks(ctx context.Context) error {
165
-	for _, nid := range c.container.networks() {
166
-		if err := c.backend.DeleteManagedNetwork(nid); err != nil {
165
+	for name, v := range c.container.networksAttachments {
166
+		if err := c.backend.DeleteManagedNetwork(v.Network.ID); err != nil {
167 167
 			switch err.(type) {
168 168
 			case *libnetwork.ActiveEndpointsError:
169 169
 				continue
170 170
 			case libnetwork.ErrNoSuchNetwork:
171 171
 				continue
172 172
 			default:
173
-				log.G(ctx).Errorf("network %s remove failed: %v", nid, err)
173
+				log.G(ctx).Errorf("network %s remove failed: %v", name, err)
174 174
 				return err
175 175
 			}
176 176
 		}
... ...
@@ -507,7 +507,7 @@ func getEndpointConfig(na *api.NetworkAttachment, b executorpkg.Backend) *networ
507 507
 		DriverOpts: na.DriverAttachmentOpts,
508 508
 	}
509 509
 	if v, ok := na.Network.Spec.Annotations.Labels["com.docker.swarm.predefined"]; ok && v == "true" {
510
-		if ln, err := b.FindNetwork(na.Network.Spec.Annotations.Name); err == nil {
510
+		if ln, err := b.FindUniqueNetwork(na.Network.Spec.Annotations.Name); err == nil {
511 511
 			n.NetworkID = ln.ID()
512 512
 		}
513 513
 	}
... ...
@@ -575,19 +575,6 @@ func (c *containerConfig) serviceConfig() *clustertypes.ServiceConfig {
575 575
 	return svcCfg
576 576
 }
577 577
 
578
-// networks returns a list of network names attached to the container. The
579
-// returned name can be used to lookup the corresponding network create
580
-// options.
581
-func (c *containerConfig) networks() []string {
582
-	var networks []string
583
-
584
-	for name := range c.networksAttachments {
585
-		networks = append(networks, name)
586
-	}
587
-
588
-	return networks
589
-}
590
-
591 578
 func (c *containerConfig) networkCreateRequest(name string) (clustertypes.NetworkCreateRequest, error) {
592 579
 	na, ok := c.networksAttachments[name]
593 580
 	if !ok {
... ...
@@ -292,7 +292,7 @@ func (c *Cluster) populateNetworkID(ctx context.Context, client swarmapi.Control
292 292
 	for i, n := range networks {
293 293
 		apiNetwork, err := getNetwork(ctx, client, n.Target)
294 294
 		if err != nil {
295
-			ln, _ := c.config.Backend.FindNetwork(n.Target)
295
+			ln, _ := c.config.Backend.FindUniqueNetwork(n.Target)
296 296
 			if ln != nil && runconfig.IsPreDefinedNetwork(ln.Name()) {
297 297
 				// Need to retrieve the corresponding predefined swarm network
298 298
 				// and use its id for the request.
... ...
@@ -251,8 +251,8 @@ func (daemon *Daemon) updateNetworkSettings(container *container.Container, n li
251 251
 		return runconfig.ErrConflictHostNetwork
252 252
 	}
253 253
 
254
-	for s := range container.NetworkSettings.Networks {
255
-		sn, err := daemon.FindNetwork(s)
254
+	for s, v := range container.NetworkSettings.Networks {
255
+		sn, err := daemon.FindUniqueNetwork(getNetworkID(s, v.EndpointSettings))
256 256
 		if err != nil {
257 257
 			continue
258 258
 		}
... ...
@@ -308,8 +308,8 @@ func (daemon *Daemon) updateNetwork(container *container.Container) error {
308 308
 
309 309
 	// Find if container is connected to the default bridge network
310 310
 	var n libnetwork.Network
311
-	for name := range container.NetworkSettings.Networks {
312
-		sn, err := daemon.FindNetwork(name)
311
+	for name, v := range container.NetworkSettings.Networks {
312
+		sn, err := daemon.FindUniqueNetwork(getNetworkID(name, v.EndpointSettings))
313 313
 		if err != nil {
314 314
 			continue
315 315
 		}
... ...
@@ -339,7 +339,7 @@ func (daemon *Daemon) updateNetwork(container *container.Container) error {
339 339
 }
340 340
 
341 341
 func (daemon *Daemon) findAndAttachNetwork(container *container.Container, idOrName string, epConfig *networktypes.EndpointSettings) (libnetwork.Network, *networktypes.NetworkingConfig, error) {
342
-	n, err := daemon.FindNetwork(idOrName)
342
+	n, err := daemon.FindUniqueNetwork(getNetworkID(idOrName, epConfig))
343 343
 	if err != nil {
344 344
 		// We should always be able to find the network for a
345 345
 		// managed container.
... ...
@@ -377,16 +377,16 @@ func (daemon *Daemon) findAndAttachNetwork(container *container.Container, idOrN
377 377
 		// trigger attachment in the swarm cluster manager.
378 378
 		if daemon.clusterProvider != nil {
379 379
 			var err error
380
-			config, err = daemon.clusterProvider.AttachNetwork(idOrName, container.ID, addresses)
380
+			config, err = daemon.clusterProvider.AttachNetwork(getNetworkID(idOrName, epConfig), container.ID, addresses)
381 381
 			if err != nil {
382 382
 				return nil, nil, err
383 383
 			}
384 384
 		}
385 385
 
386
-		n, err = daemon.FindNetwork(idOrName)
386
+		n, err = daemon.FindUniqueNetwork(getNetworkID(idOrName, epConfig))
387 387
 		if err != nil {
388 388
 			if daemon.clusterProvider != nil {
389
-				if err := daemon.clusterProvider.DetachNetwork(idOrName, container.ID); err != nil {
389
+				if err := daemon.clusterProvider.DetachNetwork(getNetworkID(idOrName, epConfig), container.ID); err != nil {
390 390
 					logrus.Warnf("Could not rollback attachment for container %s to network %s: %v", container.ID, idOrName, err)
391 391
 				}
392 392
 			}
... ...
@@ -437,7 +437,7 @@ func (daemon *Daemon) updateContainerNetworkSettings(container *container.Contai
437 437
 	if mode.IsUserDefined() {
438 438
 		var err error
439 439
 
440
-		n, err = daemon.FindNetwork(networkName)
440
+		n, err = daemon.FindUniqueNetwork(networkName)
441 441
 		if err == nil {
442 442
 			networkName = n.Name()
443 443
 		}
... ...
@@ -797,7 +797,7 @@ func (daemon *Daemon) connectToNetwork(container *container.Container, idOrName
797 797
 
798 798
 // ForceEndpointDelete deletes an endpoint from a network forcefully
799 799
 func (daemon *Daemon) ForceEndpointDelete(name string, networkName string) error {
800
-	n, err := daemon.FindNetwork(networkName)
800
+	n, err := daemon.FindUniqueNetwork(networkName)
801 801
 	if err != nil {
802 802
 		return err
803 803
 	}
... ...
@@ -949,7 +949,7 @@ func (daemon *Daemon) releaseNetwork(container *container.Container) {
949 949
 
950 950
 	var networks []libnetwork.Network
951 951
 	for n, epSettings := range settings {
952
-		if nw, err := daemon.FindNetwork(n); err == nil {
952
+		if nw, err := daemon.FindUniqueNetwork(getNetworkID(n, epSettings.EndpointSettings)); err == nil {
953 953
 			networks = append(networks, nw)
954 954
 		}
955 955
 
... ...
@@ -993,7 +993,7 @@ func (daemon *Daemon) ConnectToNetwork(container *container.Container, idOrName
993 993
 			return errRemovalContainer(container.ID)
994 994
 		}
995 995
 
996
-		n, err := daemon.FindNetwork(idOrName)
996
+		n, err := daemon.FindUniqueNetwork(idOrName)
997 997
 		if err == nil && n != nil {
998 998
 			if err := daemon.updateNetworkConfig(container, n, endpointConfig, true); err != nil {
999 999
 				return err
... ...
@@ -1016,7 +1016,7 @@ func (daemon *Daemon) ConnectToNetwork(container *container.Container, idOrName
1016 1016
 
1017 1017
 // DisconnectFromNetwork disconnects container from network n.
1018 1018
 func (daemon *Daemon) DisconnectFromNetwork(container *container.Container, networkName string, force bool) error {
1019
-	n, err := daemon.FindNetwork(networkName)
1019
+	n, err := daemon.FindUniqueNetwork(networkName)
1020 1020
 	container.Lock()
1021 1021
 	defer container.Unlock()
1022 1022
 
... ...
@@ -1087,3 +1087,12 @@ func (daemon *Daemon) DeactivateContainerServiceBinding(containerName string) er
1087 1087
 	}
1088 1088
 	return sb.DisableService()
1089 1089
 }
1090
+
1091
+func getNetworkID(name string, endpointSettings *networktypes.EndpointSettings) string {
1092
+	// We only want to prefer NetworkID for user defined networks.
1093
+	// For systems like bridge, none, etc. the name is preferred (otherwise restart may cause issues)
1094
+	if containertypes.NetworkMode(name).IsUserDefined() && endpointSettings != nil && endpointSettings.NetworkID != "" {
1095
+		return endpointSettings.NetworkID
1096
+	}
1097
+	return name
1098
+}
... ...
@@ -170,7 +170,7 @@ func (daemon *Daemon) initializeNetworkingPaths(container *container.Container,
170 170
 
171 171
 	if nc.NetworkSettings != nil {
172 172
 		for n := range nc.NetworkSettings.Networks {
173
-			sn, err := daemon.FindNetwork(n)
173
+			sn, err := daemon.FindUniqueNetwork(n)
174 174
 			if err != nil {
175 175
 				continue
176 176
 			}
... ...
@@ -317,7 +317,7 @@ func TestValidateContainerIsolation(t *testing.T) {
317 317
 
318 318
 func TestFindNetworkErrorType(t *testing.T) {
319 319
 	d := Daemon{}
320
-	_, err := d.FindNetwork("fakeNet")
320
+	_, err := d.FindUniqueNetwork("fakeNet")
321 321
 	_, ok := errors.Cause(err).(libnetwork.ErrNoSuchNetwork)
322 322
 	if !errdefs.IsNotFound(err) || !ok {
323 323
 		assert.Fail(t, "The FindNetwork method MUST always return an error that implements the NotFound interface and is ErrNoSuchNetwork")
... ...
@@ -226,20 +226,3 @@ func translateContainerdStartErr(cmd string, setExitCode func(int), err error) e
226 226
 	// TODO: it would be nice to get some better errors from containerd so we can return better errors here
227 227
 	return retErr
228 228
 }
229
-
230
-// TODO: cpuguy83 take care of it once the new library is ready
231
-type errNotFound struct{ error }
232
-
233
-func (errNotFound) NotFound() {}
234
-
235
-func (e errNotFound) Cause() error {
236
-	return e.error
237
-}
238
-
239
-// notFound is a helper to create an error of the class with the same name from any error type
240
-func notFound(err error) error {
241
-	if err == nil {
242
-		return nil
243
-	}
244
-	return errNotFound{err}
245
-}
... ...
@@ -29,36 +29,36 @@ func (daemon *Daemon) NetworkControllerEnabled() bool {
29 29
 	return daemon.netController != nil
30 30
 }
31 31
 
32
-// FindNetwork function finds a network for a given string that can represent network name or id
33
-func (daemon *Daemon) FindNetwork(idName string) (libnetwork.Network, error) {
34
-	// 1. match by full ID.
35
-	n, err := daemon.GetNetworkByID(idName)
36
-	if err == nil || !isNoSuchNetworkError(err) {
37
-		return n, err
38
-	}
39
-
40
-	// 2. match by full name
41
-	n, err = daemon.GetNetworkByName(idName)
42
-	if err == nil || !isNoSuchNetworkError(err) {
43
-		return n, err
44
-	}
45
-
46
-	// 3. match by ID prefix
47
-	list := daemon.GetNetworksByIDPrefix(idName)
48
-	if len(list) == 0 {
49
-		// Be very careful to change the error type here, the libnetwork.ErrNoSuchNetwork error is used by the controller
50
-		// to retry the creation of the network as managed through the swarm manager
51
-		return nil, errors.WithStack(notFound(libnetwork.ErrNoSuchNetwork(idName)))
52
-	}
53
-	if len(list) > 1 {
54
-		return nil, errors.WithStack(invalidIdentifier(idName))
32
+// FindUniqueNetwork returns a network based on:
33
+// 1. Full ID
34
+// 2. Full Name
35
+// 3. Partial ID
36
+// as long as there is no ambiguity
37
+func (daemon *Daemon) FindUniqueNetwork(term string) (libnetwork.Network, error) {
38
+	listByFullName := []libnetwork.Network{}
39
+	listByPartialID := []libnetwork.Network{}
40
+	for _, nw := range daemon.GetNetworks() {
41
+		if nw.ID() == term {
42
+			return nw, nil
43
+		}
44
+		if nw.Name() == term {
45
+			listByFullName = append(listByFullName, nw)
46
+		}
47
+		if strings.HasPrefix(nw.ID(), term) {
48
+			listByPartialID = append(listByPartialID, nw)
49
+		}
55 50
 	}
56
-	return list[0], nil
57
-}
58
-
59
-func isNoSuchNetworkError(err error) bool {
60
-	_, ok := err.(libnetwork.ErrNoSuchNetwork)
61
-	return ok
51
+	switch {
52
+	case len(listByFullName) == 1:
53
+		return listByFullName[0], nil
54
+	case len(listByFullName) > 1:
55
+		return nil, fmt.Errorf("network %s is ambiguous (%d matches found based on name)", term, len(listByFullName))
56
+	case len(listByPartialID) == 1:
57
+		return listByPartialID[0], nil
58
+	case len(listByPartialID) > 1:
59
+		return nil, fmt.Errorf("network %s is ambiguous (%d matches found based on ID prefix)", term, len(listByPartialID))
60
+	}
61
+	return nil, libnetwork.ErrNoSuchNetwork(term)
62 62
 }
63 63
 
64 64
 // GetNetworkByID function returns a network whose ID matches the given ID.
... ...
@@ -104,7 +104,11 @@ func (daemon *Daemon) GetNetworksByIDPrefix(partialID string) []libnetwork.Netwo
104 104
 
105 105
 // getAllNetworks returns a list containing all networks
106 106
 func (daemon *Daemon) getAllNetworks() []libnetwork.Network {
107
-	return daemon.netController.Networks()
107
+	c := daemon.netController
108
+	if c == nil {
109
+		return nil
110
+	}
111
+	return c.Networks()
108 112
 }
109 113
 
110 114
 type ingressJob struct {
... ...
@@ -274,7 +278,9 @@ func (daemon *Daemon) createNetwork(create types.NetworkCreateRequest, id string
274 274
 		// check if user defined CheckDuplicate, if set true, return err
275 275
 		// otherwise prepare a warning message
276 276
 		if create.CheckDuplicate {
277
-			return nil, libnetwork.NetworkNameError(create.Name)
277
+			if !agent || nw.Info().Dynamic() {
278
+				return nil, libnetwork.NetworkNameError(create.Name)
279
+			}
278 280
 		}
279 281
 		warning = fmt.Sprintf("Network with name %s (id : %s) already exists", nw.Name(), nw.ID())
280 282
 	}
... ...
@@ -464,25 +470,56 @@ func (daemon *Daemon) GetNetworkDriverList() []string {
464 464
 }
465 465
 
466 466
 // DeleteManagedNetwork deletes an agent network.
467
+// The requirement of networkID is enforced.
467 468
 func (daemon *Daemon) DeleteManagedNetwork(networkID string) error {
468
-	return daemon.deleteNetwork(networkID, true)
469
+	n, err := daemon.GetNetworkByID(networkID)
470
+	if err != nil {
471
+		return err
472
+	}
473
+	return daemon.deleteNetwork(n, true)
469 474
 }
470 475
 
471 476
 // DeleteNetwork destroys a network unless it's one of docker's predefined networks.
472 477
 func (daemon *Daemon) DeleteNetwork(networkID string) error {
473
-	return daemon.deleteNetwork(networkID, false)
474
-}
475
-
476
-func (daemon *Daemon) deleteNetwork(networkID string, dynamic bool) error {
477
-	nw, err := daemon.FindNetwork(networkID)
478
+	n, err := daemon.GetNetworkByID(networkID)
478 479
 	if err != nil {
479 480
 		return err
480 481
 	}
482
+	return daemon.deleteNetwork(n, false)
483
+}
481 484
 
482
-	if nw.Info().Ingress() {
483
-		return nil
485
+func (daemon *Daemon) deleteLoadBalancerSandbox(n libnetwork.Network) {
486
+	controller := daemon.netController
487
+
488
+	//The only endpoint left should be the LB endpoint (nw.Name() + "-endpoint")
489
+	endpoints := n.Endpoints()
490
+	if len(endpoints) == 1 {
491
+		sandboxName := n.Name() + "-sbox"
492
+
493
+		info := endpoints[0].Info()
494
+		if info != nil {
495
+			sb := info.Sandbox()
496
+			if sb != nil {
497
+				if err := sb.DisableService(); err != nil {
498
+					logrus.Warnf("Failed to disable service on sandbox %s: %v", sandboxName, err)
499
+					//Ignore error and attempt to delete the load balancer endpoint
500
+				}
501
+			}
502
+		}
503
+
504
+		if err := endpoints[0].Delete(true); err != nil {
505
+			logrus.Warnf("Failed to delete endpoint %s (%s) in %s: %v", endpoints[0].Name(), endpoints[0].ID(), sandboxName, err)
506
+			//Ignore error and attempt to delete the sandbox.
507
+		}
508
+
509
+		if err := controller.SandboxDestroy(sandboxName); err != nil {
510
+			logrus.Warnf("Failed to delete %s sandbox: %v", sandboxName, err)
511
+			//Ignore error and attempt to delete the network.
512
+		}
484 513
 	}
514
+}
485 515
 
516
+func (daemon *Daemon) deleteNetwork(nw libnetwork.Network, dynamic bool) error {
486 517
 	if runconfig.IsPreDefinedNetwork(nw.Name()) && !dynamic {
487 518
 		err := fmt.Errorf("%s is a pre-defined network and cannot be removed", nw.Name())
488 519
 		return notAllowedError{err}
... ...
@@ -159,7 +159,7 @@ func (daemon *Daemon) createSpec(c *container.Container) (*specs.Spec, error) {
159 159
 	gwHNSID := ""
160 160
 	if c.NetworkSettings != nil {
161 161
 		for n := range c.NetworkSettings.Networks {
162
-			sn, err := daemon.FindNetwork(n)
162
+			sn, err := daemon.FindUniqueNetwork(n)
163 163
 			if err != nil {
164 164
 				continue
165 165
 			}
... ...
@@ -11,6 +11,7 @@ import (
11 11
 	"github.com/docker/docker/client"
12 12
 	"github.com/docker/docker/integration-cli/request"
13 13
 	"github.com/gotestyourself/gotestyourself/poll"
14
+	"github.com/stretchr/testify/assert"
14 15
 	"github.com/stretchr/testify/require"
15 16
 	"golang.org/x/net/context"
16 17
 )
... ...
@@ -80,6 +81,68 @@ func TestCreateServiceMultipleTimes(t *testing.T) {
80 80
 	poll.WaitOn(t, networkIsRemoved(client, overlayID), poll.WithTimeout(1*time.Minute), poll.WithDelay(10*time.Second))
81 81
 }
82 82
 
83
+func TestCreateWithDuplicateNetworkNames(t *testing.T) {
84
+	defer setupTest(t)()
85
+	d := newSwarm(t)
86
+	defer d.Stop(t)
87
+	client, err := request.NewClientForHost(d.Sock())
88
+	require.NoError(t, err)
89
+
90
+	name := "foo"
91
+	networkCreate := types.NetworkCreate{
92
+		CheckDuplicate: false,
93
+		Driver:         "bridge",
94
+	}
95
+
96
+	n1, err := client.NetworkCreate(context.Background(), name, networkCreate)
97
+	require.NoError(t, err)
98
+
99
+	n2, err := client.NetworkCreate(context.Background(), name, networkCreate)
100
+	require.NoError(t, err)
101
+
102
+	// Dupliates with name but with different driver
103
+	networkCreate.Driver = "overlay"
104
+	n3, err := client.NetworkCreate(context.Background(), name, networkCreate)
105
+	require.NoError(t, err)
106
+
107
+	// Create Service with the same name
108
+	var instances uint64 = 1
109
+	serviceSpec := swarmServiceSpec("top", instances)
110
+
111
+	serviceSpec.TaskTemplate.Networks = append(serviceSpec.TaskTemplate.Networks, swarm.NetworkAttachmentConfig{Target: name})
112
+
113
+	service, err := client.ServiceCreate(context.Background(), serviceSpec, types.ServiceCreateOptions{})
114
+	require.NoError(t, err)
115
+
116
+	poll.WaitOn(t, serviceRunningTasksCount(client, service.ID, instances))
117
+
118
+	resp, _, err := client.ServiceInspectWithRaw(context.Background(), service.ID, types.ServiceInspectOptions{})
119
+	require.NoError(t, err)
120
+	assert.Equal(t, n3.ID, resp.Spec.TaskTemplate.Networks[0].Target)
121
+
122
+	// Remove Service
123
+	err = client.ServiceRemove(context.Background(), service.ID)
124
+	require.NoError(t, err)
125
+
126
+	// Make sure task has been destroyed.
127
+	poll.WaitOn(t, serviceIsRemoved(client, service.ID))
128
+
129
+	// Remove networks
130
+	err = client.NetworkRemove(context.Background(), n3.ID)
131
+	require.NoError(t, err)
132
+
133
+	err = client.NetworkRemove(context.Background(), n2.ID)
134
+	require.NoError(t, err)
135
+
136
+	err = client.NetworkRemove(context.Background(), n1.ID)
137
+	require.NoError(t, err)
138
+
139
+	// Make sure networks have been destroyed.
140
+	poll.WaitOn(t, networkIsRemoved(client, n3.ID), poll.WithTimeout(1*time.Minute), poll.WithDelay(10*time.Second))
141
+	poll.WaitOn(t, networkIsRemoved(client, n2.ID), poll.WithTimeout(1*time.Minute), poll.WithDelay(10*time.Second))
142
+	poll.WaitOn(t, networkIsRemoved(client, n1.ID), poll.WithTimeout(1*time.Minute), poll.WithDelay(10*time.Second))
143
+}
144
+
83 145
 func swarmServiceSpec(name string, replicas uint64) swarm.ServiceSpec {
84 146
 	return swarm.ServiceSpec{
85 147
 		Annotations: swarm.Annotations{