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>
This commit is contained in:
Madhu Venugopal 2016-08-26 13:08:28 -07:00
parent 91ec7fa811
commit 05a3f2666e
8 changed files with 50 additions and 30 deletions

View file

@ -15,6 +15,6 @@ type Backend interface {
GetNetworks() []libnetwork.Network GetNetworks() []libnetwork.Network
CreateNetwork(nc types.NetworkCreateRequest) (*types.NetworkCreateResponse, error) CreateNetwork(nc types.NetworkCreateRequest) (*types.NetworkCreateResponse, error)
ConnectContainerToNetwork(containerName, networkName string, endpointConfig *network.EndpointSettings) 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 DeleteNetwork(name string) error
} }

View file

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

View file

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

View file

@ -7,7 +7,6 @@ import (
"github.com/docker/docker/container" "github.com/docker/docker/container"
networktypes "github.com/docker/engine-api/types/network" networktypes "github.com/docker/engine-api/types/network"
"github.com/docker/libnetwork"
) )
func (daemon *Daemon) setupLinkedContainers(container *container.Container) ([]string, error) { 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 // 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") return fmt.Errorf("Solaris does not support disconnecting a running container from a network")
} }

View file

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

View file

@ -7,7 +7,6 @@ import (
"github.com/docker/docker/container" "github.com/docker/docker/container"
networktypes "github.com/docker/engine-api/types/network" networktypes "github.com/docker/engine-api/types/network"
"github.com/docker/libnetwork"
) )
func (daemon *Daemon) setupLinkedContainers(container *container.Container) ([]string, error) { 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. // 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") return fmt.Errorf("Windows does not support disconnecting a running container from a network")
} }

View file

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

View file

@ -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) { func (s *DockerNetworkSuite) TestDockerNetworkConnectPreferredIP(c *check.C) {
// create two networks // create two networks
dockerCmd(c, "network", "create", "--ipv6", "--subnet=172.28.0.0/16", "--subnet=2001:db8:1234::/64", "n0") dockerCmd(c, "network", "create", "--ipv6", "--subnet=172.28.0.0/16", "--subnet=2001:db8:1234::/64", "n0")