Browse code

Update `FindUniqueNetwork` to address network name duplications

This fix is part of the effort to address 30242 where
issue arise because of the fact that multiple networks
may share the same name (within or across local/swarm scopes).

The focus of this fix is to allow creation of service
when a network in local scope has the same name as the
service network.

An integration test has been added.

This fix fixes 30242.

Signed-off-by: Yong Tang <yong.tang.github@outlook.com>

Yong Tang authored on 2017/11/01 04:46:53
Showing 9 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 differnt from FindUniqueNetwork from 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 {
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
+}
... ...
@@ -29,31 +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
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)
57 62
 }
58 63
 
59 64
 func isNoSuchNetworkError(err error) bool {
... ...
@@ -274,7 +279,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 +471,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}
... ...
@@ -2137,3 +2137,76 @@ func (s *DockerSwarmSuite) TestSwarmClusterEventsConfig(c *check.C) {
2137 2137
 	// filtered by config
2138 2138
 	waitForEvent(c, d, t1, "-f type=config", "config remove "+id, defaultRetryCount)
2139 2139
 }
2140
+
2141
+func (s *DockerSwarmSuite) TestServiceCreateWithDuplicateNetworkNames(c *check.C) {
2142
+	d := s.AddDaemon(c, true, true)
2143
+
2144
+	name := "foo"
2145
+	networkCreateRequest := types.NetworkCreateRequest{
2146
+		Name: name,
2147
+		NetworkCreate: types.NetworkCreate{
2148
+			CheckDuplicate: false,
2149
+			Driver:         "bridge",
2150
+		},
2151
+	}
2152
+
2153
+	// Create networks with the same name, 2 in local scope and 1 in swarm scope
2154
+	var n1 types.NetworkCreateResponse
2155
+	status, body, err := d.SockRequest("POST", "/networks/create", networkCreateRequest)
2156
+	c.Assert(err, checker.IsNil, check.Commentf(string(body)))
2157
+	c.Assert(status, checker.Equals, http.StatusCreated, check.Commentf(string(body)))
2158
+	c.Assert(json.Unmarshal(body, &n1), checker.IsNil)
2159
+
2160
+	var n2 types.NetworkCreateResponse
2161
+	status, body, err = d.SockRequest("POST", "/networks/create", networkCreateRequest)
2162
+	c.Assert(err, checker.IsNil, check.Commentf(string(body)))
2163
+	c.Assert(status, checker.Equals, http.StatusCreated, check.Commentf(string(body)))
2164
+	c.Assert(json.Unmarshal(body, &n2), checker.IsNil)
2165
+
2166
+	var n3 types.NetworkCreateResponse
2167
+	// Dupliates with name but with different driver
2168
+	networkCreateRequest.NetworkCreate.Driver = "overlay"
2169
+	status, body, err = d.SockRequest("POST", "/networks/create", networkCreateRequest)
2170
+	c.Assert(err, checker.IsNil, check.Commentf(string(body)))
2171
+	c.Assert(status, checker.Equals, http.StatusCreated, check.Commentf(string(body)))
2172
+	c.Assert(json.Unmarshal(body, &n3), checker.IsNil)
2173
+
2174
+	// Create Service with the same name
2175
+	d.CreateService(c, simpleTestService, func(s *swarm.Service) {
2176
+		s.Spec.Name = "top"
2177
+		s.Spec.TaskTemplate.Networks = []swarm.NetworkAttachmentConfig{
2178
+			{Target: name},
2179
+		}
2180
+	})
2181
+
2182
+	// make sure task has been deployed.
2183
+	waitAndAssert(c, defaultReconciliationTimeout, d.CheckActiveContainerCount, checker.Equals, 1)
2184
+
2185
+	result := icmd.RunCmd(d.Command("ps", "-a", "-q"))
2186
+	result.Assert(c, icmd.Success)
2187
+	containers := strings.Split(strings.TrimSpace(result.Stdout()), "\n")
2188
+	c.Assert(len(containers), checker.Equals, 1)
2189
+
2190
+	result = icmd.RunCmd(d.Command("inspect", "--format", `{{.NetworkSettings.Networks.foo.NetworkID}}`, containers[0]))
2191
+	result.Assert(c, icmd.Success)
2192
+	c.Assert(strings.TrimSpace(result.Stdout()), checker.Equals, n3.ID)
2193
+
2194
+	// Remove Service
2195
+	result = icmd.RunCmd(d.Command("service", "rm", "top"))
2196
+	result.Assert(c, icmd.Success)
2197
+
2198
+	// make sure task has been destroyed.
2199
+	waitAndAssert(c, defaultReconciliationTimeout, d.CheckActiveContainerCount, checker.Equals, 0)
2200
+
2201
+	result = icmd.RunCmd(d.Command("network", "rm", n1.ID))
2202
+	result.Assert(c, icmd.Success)
2203
+	c.Assert(strings.TrimSpace(result.Stdout()), checker.Equals, n1.ID)
2204
+
2205
+	result = icmd.RunCmd(d.Command("network", "rm", n2.ID))
2206
+	result.Assert(c, icmd.Success)
2207
+	c.Assert(strings.TrimSpace(result.Stdout()), checker.Equals, n2.ID)
2208
+
2209
+	result = icmd.RunCmd(d.Command("network", "rm", n3.ID))
2210
+	result.Assert(c, icmd.Success)
2211
+	c.Assert(strings.TrimSpace(result.Stdout()), checker.Equals, n3.ID)
2212
+}