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