Browse code

Remove attachable network on swarm leave

- When the node leaves the cluster, if any user run
container(s) is connected to the swarm network,
then daemon needs to detach the container(s) and
remove the network.

Signed-off-by: Alessandro Boch <aboch@docker.com>
(cherry picked from commit 3cedca5d532958ffc007d9b62cc871d3d113f054)
Signed-off-by: Victor Vieux <vieux@docker.com>

Alessandro Boch authored on 2017/01/14 13:14:03
Showing 5 changed files
... ...
@@ -351,7 +351,7 @@ func (c *Cluster) startNewNode(conf nodeStartConfig) (*node, error) {
351 351
 	c.actualLocalAddr = actualLocalAddr // not saved
352 352
 	c.saveState(conf)
353 353
 
354
-	c.config.Backend.SetClusterProvider(c)
354
+	c.config.Backend.DaemonJoinsCluster(c)
355 355
 	go func() {
356 356
 		err := detectLockedError(n.Err(ctx))
357 357
 		if err != nil {
... ...
@@ -724,6 +724,7 @@ func (c *Cluster) Leave(force bool) error {
724 724
 	if err := c.clearState(); err != nil {
725 725
 		return err
726 726
 	}
727
+
727 728
 	return nil
728 729
 }
729 730
 
... ...
@@ -751,7 +752,7 @@ func (c *Cluster) clearState() error {
751 751
 	if err := os.MkdirAll(c.root, 0700); err != nil {
752 752
 		return err
753 753
 	}
754
-	c.config.Backend.SetClusterProvider(nil)
754
+	c.config.Backend.DaemonLeavesCluster()
755 755
 	return nil
756 756
 }
757 757
 
... ...
@@ -47,7 +47,8 @@ type Backend interface {
47 47
 	VolumeCreate(name, driverName string, opts, labels map[string]string) (*types.Volume, error)
48 48
 	Containers(config *types.ContainerListOptions) ([]*types.Container, error)
49 49
 	SetNetworkBootstrapKeys([]*networktypes.EncryptionKey) error
50
-	SetClusterProvider(provider cluster.Provider)
50
+	DaemonJoinsCluster(provider cluster.Provider)
51
+	DaemonLeavesCluster()
51 52
 	IsSwarmCompatible() error
52 53
 	SubscribeToEvents(since, until time.Time, filter filters.Args) ([]events.Message, chan interface{})
53 54
 	UnsubscribeFromEvents(listener chan interface{})
... ...
@@ -446,8 +446,22 @@ func (daemon *Daemon) registerLink(parent, child *container.Container, alias str
446 446
 	return nil
447 447
 }
448 448
 
449
-// SetClusterProvider sets a component for querying the current cluster state.
450
-func (daemon *Daemon) SetClusterProvider(clusterProvider cluster.Provider) {
449
+// DaemonJoinsCluster informs the daemon has joined the cluster and provides
450
+// the handler to query the cluster component
451
+func (daemon *Daemon) DaemonJoinsCluster(clusterProvider cluster.Provider) {
452
+	daemon.setClusterProvider(clusterProvider)
453
+}
454
+
455
+// DaemonLeavesCluster informs the daemon has left the cluster
456
+func (daemon *Daemon) DaemonLeavesCluster() {
457
+	// Daemon is in charge of removing the attachable networks with
458
+	// connected containers when the node leaves the swarm
459
+	daemon.clearAttachableNetworks()
460
+	daemon.setClusterProvider(nil)
461
+}
462
+
463
+// setClusterProvider sets a component for querying the current cluster state.
464
+func (daemon *Daemon) setClusterProvider(clusterProvider cluster.Provider) {
451 465
 	daemon.clusterProvider = clusterProvider
452 466
 	daemon.netController.SetClusterProvider(clusterProvider)
453 467
 }
... ...
@@ -468,3 +468,31 @@ func (daemon *Daemon) deleteNetwork(networkID string, dynamic bool) error {
468 468
 func (daemon *Daemon) GetNetworks() []libnetwork.Network {
469 469
 	return daemon.getAllNetworks()
470 470
 }
471
+
472
+// clearAttachableNetworks removes the attachable networks
473
+// after disconnecting any connected container
474
+func (daemon *Daemon) clearAttachableNetworks() {
475
+	for _, n := range daemon.GetNetworks() {
476
+		if !n.Info().Attachable() {
477
+			continue
478
+		}
479
+		for _, ep := range n.Endpoints() {
480
+			epInfo := ep.Info()
481
+			if epInfo == nil {
482
+				continue
483
+			}
484
+			sb := epInfo.Sandbox()
485
+			if sb == nil {
486
+				continue
487
+			}
488
+			containerID := sb.ContainerID()
489
+			if err := daemon.DisconnectContainerFromNetwork(containerID, n.ID(), true); err != nil {
490
+				logrus.Warnf("Failed to disconnect container %s from swarm network %s on cluster leave: %v",
491
+					containerID, n.Name(), err)
492
+			}
493
+		}
494
+		if err := daemon.DeleteManagedNetwork(n.ID()); err != nil {
495
+			logrus.Warnf("Failed to remove swarm network %s on cluster leave: %v", n.Name(), err)
496
+		}
497
+	}
498
+}
... ...
@@ -393,6 +393,33 @@ func (s *DockerSwarmSuite) TestOverlayAttachable(c *check.C) {
393 393
 	c.Assert(strings.TrimSpace(out), checker.Equals, "true")
394 394
 }
395 395
 
396
+func (s *DockerSwarmSuite) TestOverlayAttachableOnSwarmLeave(c *check.C) {
397
+	d := s.AddDaemon(c, true, true)
398
+
399
+	// Create an attachable swarm network
400
+	nwName := "attovl"
401
+	out, err := d.Cmd("network", "create", "-d", "overlay", "--attachable", nwName)
402
+	c.Assert(err, checker.IsNil, check.Commentf(out))
403
+
404
+	// Connect a container to the network
405
+	out, err = d.Cmd("run", "-d", "--network", nwName, "--name", "c1", "busybox", "top")
406
+	c.Assert(err, checker.IsNil, check.Commentf(out))
407
+
408
+	// Leave the swarm
409
+	err = d.Leave(true)
410
+	c.Assert(err, checker.IsNil)
411
+
412
+	// Check the container is disconnected
413
+	out, err = d.Cmd("inspect", "c1", "--format", "{{.NetworkSettings.Networks."+nwName+"}}")
414
+	c.Assert(err, checker.IsNil)
415
+	c.Assert(strings.TrimSpace(out), checker.Equals, "<no value>")
416
+
417
+	// Check the network is gone
418
+	out, err = d.Cmd("network", "ls", "--format", "{{.Name}}")
419
+	c.Assert(err, checker.IsNil)
420
+	c.Assert(out, checker.Not(checker.Contains), nwName)
421
+}
422
+
396 423
 func (s *DockerSwarmSuite) TestSwarmRemoveInternalNetwork(c *check.C) {
397 424
 	d := s.AddDaemon(c, true, true)
398 425