Browse Source

Support container disconnect for non-existing network

There are cases such as migrating from classic overlay network to the
swarm-mode networking (without kv-store), such a mechanism to allow
disconnecting a container even when a network isnt available will be
useful.

Signed-off-by: Madhu Venugopal <madhu@docker.com>
Madhu Venugopal 8 years ago
parent
commit
05a3f2666e

+ 1 - 1
api/server/router/network/backend.go

@@ -15,6 +15,6 @@ type Backend interface {
 	GetNetworks() []libnetwork.Network
 	CreateNetwork(nc types.NetworkCreateRequest) (*types.NetworkCreateResponse, error)
 	ConnectContainerToNetwork(containerName, networkName string, endpointConfig *network.EndpointSettings) error
-	DisconnectContainerFromNetwork(containerName string, network libnetwork.Network, force bool) error
+	DisconnectContainerFromNetwork(containerName string, networkName string, force bool) error
 	DeleteNetwork(name string) error
 }

+ 3 - 6
api/server/router/network/network_routes.go

@@ -143,17 +143,14 @@ func (n *networkRouter) postNetworkDisconnect(ctx context.Context, w http.Respon
 		return err
 	}
 
-	nw, err := n.backend.FindNetwork(vars["id"])
-	if err != nil {
-		return err
-	}
+	nw, _ := n.backend.FindNetwork(vars["id"])
 
-	if nw.Info().Dynamic() {
+	if nw != nil && nw.Info().Dynamic() {
 		err := fmt.Errorf("operation not supported for swarm scoped networks")
 		return errors.NewRequestForbiddenError(err)
 	}
 
-	return n.backend.DisconnectContainerFromNetwork(disconnect.Container, nw, disconnect.Force)
+	return n.backend.DisconnectContainerFromNetwork(disconnect.Container, vars["id"], disconnect.Force)
 }
 
 func (n *networkRouter) deleteNetwork(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {

+ 7 - 2
daemon/container_operations.go

@@ -618,8 +618,13 @@ func (daemon *Daemon) connectToNetwork(container *container.Container, idOrName
 	return nil
 }
 
-// ForceEndpointDelete deletes an endpoing from a network forcefully
-func (daemon *Daemon) ForceEndpointDelete(name string, n libnetwork.Network) error {
+// ForceEndpointDelete deletes an endpoint from a network forcefully
+func (daemon *Daemon) ForceEndpointDelete(name string, networkName string) error {
+	n, err := daemon.FindNetwork(networkName)
+	if err != nil {
+		return err
+	}
+
 	ep, err := n.EndpointByName(name)
 	if err != nil {
 		return err

+ 1 - 2
daemon/container_operations_solaris.go

@@ -7,7 +7,6 @@ import (
 
 	"github.com/docker/docker/container"
 	networktypes "github.com/docker/engine-api/types/network"
-	"github.com/docker/libnetwork"
 )
 
 func (daemon *Daemon) setupLinkedContainers(container *container.Container) ([]string, error) {
@@ -25,7 +24,7 @@ func (daemon *Daemon) getSize(container *container.Container) (int64, int64) {
 }
 
 // DisconnectFromNetwork disconnects a container from the network
-func (daemon *Daemon) DisconnectFromNetwork(container *container.Container, n libnetwork.Network, force bool) error {
+func (daemon *Daemon) DisconnectFromNetwork(container *container.Container, networkName string, force bool) error {
 	return fmt.Errorf("Solaris does not support disconnecting a running container from a network")
 }
 

+ 18 - 14
daemon/container_operations_unix.go

@@ -21,7 +21,6 @@ import (
 	"github.com/docker/docker/runconfig"
 	containertypes "github.com/docker/engine-api/types/container"
 	networktypes "github.com/docker/engine-api/types/network"
-	"github.com/docker/libnetwork"
 	"github.com/opencontainers/runc/libcontainer/configs"
 	"github.com/opencontainers/runc/libcontainer/devices"
 	"github.com/opencontainers/runc/libcontainer/label"
@@ -124,33 +123,38 @@ func (daemon *Daemon) ConnectToNetwork(container *container.Container, idOrName
 }
 
 // DisconnectFromNetwork disconnects container from network n.
-func (daemon *Daemon) DisconnectFromNetwork(container *container.Container, n libnetwork.Network, force bool) error {
-	if container.HostConfig.NetworkMode.IsHost() && containertypes.NetworkMode(n.Type()).IsHost() {
-		return runconfig.ErrConflictHostNetwork
-	}
-	if !container.Running {
+func (daemon *Daemon) DisconnectFromNetwork(container *container.Container, networkName string, force bool) error {
+	n, err := daemon.FindNetwork(networkName)
+	if !container.Running || (err != nil && force) {
 		if container.RemovalInProgress || container.Dead {
 			return errRemovalContainer(container.ID)
 		}
-		if _, ok := container.NetworkSettings.Networks[n.Name()]; ok {
-			delete(container.NetworkSettings.Networks, n.Name())
-		} else {
-			return fmt.Errorf("container %s is not connected to the network %s", container.ID, n.Name())
+		if _, ok := container.NetworkSettings.Networks[networkName]; !ok {
+			return fmt.Errorf("container %s is not connected to the network %s", container.ID, networkName)
 		}
-	} else {
+		delete(container.NetworkSettings.Networks, networkName)
+	} else if err == nil {
+		if container.HostConfig.NetworkMode.IsHost() && containertypes.NetworkMode(n.Type()).IsHost() {
+			return runconfig.ErrConflictHostNetwork
+		}
+
 		if err := disconnectFromNetwork(container, n, false); err != nil {
 			return err
 		}
+	} else {
+		return err
 	}
 
 	if err := container.ToDiskLocking(); err != nil {
 		return fmt.Errorf("Error saving container to disk: %v", err)
 	}
 
-	attributes := map[string]string{
-		"container": container.ID,
+	if n != nil {
+		attributes := map[string]string{
+			"container": container.ID,
+		}
+		daemon.LogNetworkEventWithAttributes(n, "disconnect", attributes)
 	}
-	daemon.LogNetworkEventWithAttributes(n, "disconnect", attributes)
 	return nil
 }
 

+ 1 - 2
daemon/container_operations_windows.go

@@ -7,7 +7,6 @@ import (
 
 	"github.com/docker/docker/container"
 	networktypes "github.com/docker/engine-api/types/network"
-	"github.com/docker/libnetwork"
 )
 
 func (daemon *Daemon) setupLinkedContainers(container *container.Container) ([]string, error) {
@@ -20,7 +19,7 @@ func (daemon *Daemon) ConnectToNetwork(container *container.Container, idOrName
 }
 
 // DisconnectFromNetwork disconnects container from a network.
-func (daemon *Daemon) DisconnectFromNetwork(container *container.Container, n libnetwork.Network, force bool) error {
+func (daemon *Daemon) DisconnectFromNetwork(container *container.Container, networkName string, force bool) error {
 	return fmt.Errorf("Windows does not support disconnecting a running container from a network")
 }
 

+ 3 - 3
daemon/network.go

@@ -316,15 +316,15 @@ func (daemon *Daemon) ConnectContainerToNetwork(containerName, networkName strin
 
 // DisconnectContainerFromNetwork disconnects the given container from
 // the given network. If either cannot be found, an err is returned.
-func (daemon *Daemon) DisconnectContainerFromNetwork(containerName string, network libnetwork.Network, force bool) error {
+func (daemon *Daemon) DisconnectContainerFromNetwork(containerName string, networkName string, force bool) error {
 	container, err := daemon.GetContainer(containerName)
 	if err != nil {
 		if force {
-			return daemon.ForceEndpointDelete(containerName, network)
+			return daemon.ForceEndpointDelete(containerName, networkName)
 		}
 		return err
 	}
-	return daemon.DisconnectFromNetwork(container, network, force)
+	return daemon.DisconnectFromNetwork(container, networkName, force)
 }
 
 // GetNetworkDriverList returns the list of plugins drivers

+ 16 - 0
integration-cli/docker_cli_network_unix_test.go

@@ -1274,6 +1274,22 @@ func (s *DockerNetworkSuite) TestDockerNetworkConnectDisconnectToStoppedContaine
 
 }
 
+func (s *DockerNetworkSuite) TestDockerNetworkDisconnectContainerNonexistingNetwork(c *check.C) {
+	dockerCmd(c, "network", "create", "test")
+	dockerCmd(c, "run", "--net=test", "-d", "--name=foo", "busybox", "top")
+	networks := inspectField(c, "foo", "NetworkSettings.Networks")
+	c.Assert(networks, checker.Contains, "test", check.Commentf("Should contain 'test' network"))
+
+	// Stop container and remove network
+	dockerCmd(c, "stop", "foo")
+	dockerCmd(c, "network", "rm", "test")
+
+	// Test disconnecting stopped container from nonexisting network
+	dockerCmd(c, "network", "disconnect", "-f", "test", "foo")
+	networks = inspectField(c, "foo", "NetworkSettings.Networks")
+	c.Assert(networks, checker.Not(checker.Contains), "test", check.Commentf("Should not contain 'test' network"))
+}
+
 func (s *DockerNetworkSuite) TestDockerNetworkConnectPreferredIP(c *check.C) {
 	// create two networks
 	dockerCmd(c, "network", "create", "--ipv6", "--subnet=172.28.0.0/16", "--subnet=2001:db8:1234::/64", "n0")