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>

Alessandro Boch authored on 2017/01/14 13:14:03
Showing 5 changed files
... ...
@@ -244,7 +244,7 @@ func (c *Cluster) newNodeRunner(conf nodeStartConfig) (*nodeRunner, error) {
244 244
 		return nil, err
245 245
 	}
246 246
 
247
-	c.config.Backend.SetClusterProvider(c)
247
+	c.config.Backend.DaemonJoinsCluster(c)
248 248
 
249 249
 	return nr, nil
250 250
 }
... ...
@@ -562,7 +562,7 @@ func (c *Cluster) Leave(force bool) error {
562 562
 	if err := clearPersistentState(c.root); err != nil {
563 563
 		return err
564 564
 	}
565
-	c.config.Backend.SetClusterProvider(nil)
565
+	c.config.Backend.DaemonLeavesCluster()
566 566
 	return nil
567 567
 }
568 568
 
... ...
@@ -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{})
... ...
@@ -433,8 +433,22 @@ func (daemon *Daemon) registerLink(parent, child *container.Container, alias str
433 433
 	return nil
434 434
 }
435 435
 
436
-// SetClusterProvider sets a component for querying the current cluster state.
437
-func (daemon *Daemon) SetClusterProvider(clusterProvider cluster.Provider) {
436
+// DaemonJoinsCluster informs the daemon has joined the cluster and provides
437
+// the handler to query the cluster component
438
+func (daemon *Daemon) DaemonJoinsCluster(clusterProvider cluster.Provider) {
439
+	daemon.setClusterProvider(clusterProvider)
440
+}
441
+
442
+// DaemonLeavesCluster informs the daemon has left the cluster
443
+func (daemon *Daemon) DaemonLeavesCluster() {
444
+	// Daemon is in charge of removing the attachable networks with
445
+	// connected containers when the node leaves the swarm
446
+	daemon.clearAttachableNetworks()
447
+	daemon.setClusterProvider(nil)
448
+}
449
+
450
+// setClusterProvider sets a component for querying the current cluster state.
451
+func (daemon *Daemon) setClusterProvider(clusterProvider cluster.Provider) {
438 452
 	daemon.clusterProvider = clusterProvider
439 453
 	daemon.netController.SetClusterProvider(clusterProvider)
440 454
 }
... ...
@@ -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
+}
... ...
@@ -358,6 +358,33 @@ func (s *DockerSwarmSuite) TestOverlayAttachable(c *check.C) {
358 358
 	c.Assert(strings.TrimSpace(out), checker.Equals, "true")
359 359
 }
360 360
 
361
+func (s *DockerSwarmSuite) TestOverlayAttachableOnSwarmLeave(c *check.C) {
362
+	d := s.AddDaemon(c, true, true)
363
+
364
+	// Create an attachable swarm network
365
+	nwName := "attovl"
366
+	out, err := d.Cmd("network", "create", "-d", "overlay", "--attachable", nwName)
367
+	c.Assert(err, checker.IsNil, check.Commentf(out))
368
+
369
+	// Connect a container to the network
370
+	out, err = d.Cmd("run", "-d", "--network", nwName, "--name", "c1", "busybox", "top")
371
+	c.Assert(err, checker.IsNil, check.Commentf(out))
372
+
373
+	// Leave the swarm
374
+	err = d.Leave(true)
375
+	c.Assert(err, checker.IsNil)
376
+
377
+	// Check the container is disconnected
378
+	out, err = d.Cmd("inspect", "c1", "--format", "{{.NetworkSettings.Networks."+nwName+"}}")
379
+	c.Assert(err, checker.IsNil)
380
+	c.Assert(strings.TrimSpace(out), checker.Equals, "<no value>")
381
+
382
+	// Check the network is gone
383
+	out, err = d.Cmd("network", "ls", "--format", "{{.Name}}")
384
+	c.Assert(err, checker.IsNil)
385
+	c.Assert(out, checker.Not(checker.Contains), nwName)
386
+}
387
+
361 388
 func (s *DockerSwarmSuite) TestSwarmRemoveInternalNetwork(c *check.C) {
362 389
 	d := s.AddDaemon(c, true, true)
363 390