- 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>
... | ... |
@@ -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 |
|