From 79d4f0f56ec84922184e25c0263807158b6fb76b Mon Sep 17 00:00:00 2001 From: Lei Jitang Date: Mon, 11 Jan 2016 20:13:39 -0500 Subject: [PATCH] Add docker network connect/disconnect to non-running container Signed-off-by: Lei Jitang --- daemon/container_operations_unix.go | 83 ++++++++++++------- docs/reference/commandline/network_connect.md | 2 +- .../networking/work-with-networks.md | 5 +- errors/daemon.go | 9 ++ .../docker_cli_network_unix_test.go | 44 ++++++++-- man/docker-network-connect.1.md | 2 +- man/docker-network-disconnect.1.md | 2 +- 7 files changed, 105 insertions(+), 42 deletions(-) diff --git a/daemon/container_operations_unix.go b/daemon/container_operations_unix.go index 6bf30cfb09..dd370cff41 100644 --- a/daemon/container_operations_unix.go +++ b/daemon/container_operations_unix.go @@ -708,13 +708,43 @@ func cleanOperationalData(es *networktypes.EndpointSettings) { es.MacAddress = "" } +func (daemon *Daemon) updateNetworkConfig(container *container.Container, idOrName string, updateSettings bool) (libnetwork.Network, error) { + if container.HostConfig.NetworkMode.IsContainer() { + return nil, runconfig.ErrConflictSharedNetwork + } + + if containertypes.NetworkMode(idOrName).IsBridge() && + daemon.configStore.DisableBridge { + container.Config.NetworkDisabled = true + return nil, nil + } + + n, err := daemon.FindNetwork(idOrName) + if err != nil { + return nil, err + } + + if updateSettings { + if err := daemon.updateNetworkSettings(container, n); err != nil { + return nil, err + } + } + return n, nil +} + // ConnectToNetwork connects a container to a network func (daemon *Daemon) ConnectToNetwork(container *container.Container, idOrName string, endpointConfig *networktypes.EndpointSettings) error { if !container.Running { - return derr.ErrorCodeNotRunning.WithArgs(container.ID) - } - if err := daemon.connectToNetwork(container, idOrName, endpointConfig, true); err != nil { - return err + if container.RemovalInProgress || container.Dead { + return derr.ErrorCodeRemovalContainer.WithArgs(container.ID) + } + if _, err := daemon.updateNetworkConfig(container, idOrName, true); err != nil { + return err + } + } else { + if err := daemon.connectToNetwork(container, idOrName, endpointConfig, true); err != nil { + return err + } } if err := container.ToDiskLocking(); err != nil { return fmt.Errorf("Error saving container to disk: %v", err) @@ -723,37 +753,24 @@ func (daemon *Daemon) ConnectToNetwork(container *container.Container, idOrName } func (daemon *Daemon) connectToNetwork(container *container.Container, idOrName string, endpointConfig *networktypes.EndpointSettings, updateSettings bool) (err error) { - if container.HostConfig.NetworkMode.IsContainer() { - return runconfig.ErrConflictSharedNetwork + n, err := daemon.updateNetworkConfig(container, idOrName, updateSettings) + if err != nil { + return err + } + if n == nil { + return nil } if !containertypes.NetworkMode(idOrName).IsUserDefined() && hasUserDefinedIPAddress(endpointConfig) { return runconfig.ErrUnsupportedNetworkAndIP } - if containertypes.NetworkMode(idOrName).IsBridge() && - daemon.configStore.DisableBridge { - container.Config.NetworkDisabled = true - return nil - } - controller := daemon.netController - n, err := daemon.FindNetwork(idOrName) - if err != nil { - return err - } - if err := validateNetworkingConfig(n, endpointConfig); err != nil { return err } - if updateSettings { - if err := daemon.updateNetworkSettings(container, n); err != nil { - return err - } - } - if endpointConfig != nil { container.NetworkSettings.Networks[n.Name()] = endpointConfig } @@ -817,16 +834,22 @@ func (daemon *Daemon) connectToNetwork(container *container.Container, idOrName // DisconnectFromNetwork disconnects container from network n. func (daemon *Daemon) DisconnectFromNetwork(container *container.Container, n libnetwork.Network) error { - if !container.Running { - return derr.ErrorCodeNotRunning.WithArgs(container.ID) - } - if container.HostConfig.NetworkMode.IsHost() && containertypes.NetworkMode(n.Type()).IsHost() { return runconfig.ErrConflictHostNetwork } - - if err := disconnectFromNetwork(container, n); err != nil { - return err + if !container.Running { + if container.RemovalInProgress || container.Dead { + return derr.ErrorCodeRemovalContainer.WithArgs(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()) + } + } else { + if err := disconnectFromNetwork(container, n); err != nil { + return err + } } if err := container.ToDiskLocking(); err != nil { diff --git a/docs/reference/commandline/network_connect.md b/docs/reference/commandline/network_connect.md index dbecda6ddb..866526effa 100644 --- a/docs/reference/commandline/network_connect.md +++ b/docs/reference/commandline/network_connect.md @@ -16,7 +16,7 @@ parent = "smn_cli" --help Print usage -Connects a running container to a network. You can connect a container by name +Connects a container to a network. You can connect a container by name or by ID. Once connected, the container can communicate with other containers in the same network. diff --git a/docs/userguide/networking/work-with-networks.md b/docs/userguide/networking/work-with-networks.md index 5762559a19..605c20b149 100644 --- a/docs/userguide/networking/work-with-networks.md +++ b/docs/userguide/networking/work-with-networks.md @@ -327,9 +327,8 @@ PING 172.17.0.2 (172.17.0.2): 56 data bytes ``` -To connect a container to a network, the container must be running. If you stop -a container and inspect a network it belongs to, you won't see that container. -The `docker network inspect` command only shows running containers. +You can connect both running and non-running containers to a network. However, +`docker network inspect` only displays information on running containers. ## Disconnecting containers diff --git a/errors/daemon.go b/errors/daemon.go index 1cb0da562f..278e712275 100644 --- a/errors/daemon.go +++ b/errors/daemon.go @@ -46,6 +46,15 @@ var ( HTTPStatusCode: http.StatusInternalServerError, }) + // ErrorCodeRemovalContainer is generated when we attempt to connect or disconnect a + // container but it's marked for removal. + ErrorCodeRemovalContainer = errcode.Register(errGroup, errcode.ErrorDescriptor{ + Value: "REMOVALCONTAINER", + Message: "Container %s is marked for removal and cannot be connected or disconnected to the network", + Description: "The specified container is marked for removal and cannot be connected or disconnected to the network", + HTTPStatusCode: http.StatusInternalServerError, + }) + // ErrorCodePausedContainer is generated when we attempt to attach a // container but its paused. ErrorCodePausedContainer = errcode.Register(errGroup, errcode.ErrorDescriptor{ diff --git a/integration-cli/docker_cli_network_unix_test.go b/integration-cli/docker_cli_network_unix_test.go index 967d44cc22..cd7fed0288 100644 --- a/integration-cli/docker_cli_network_unix_test.go +++ b/integration-cli/docker_cli_network_unix_test.go @@ -448,11 +448,6 @@ func (s *DockerNetworkSuite) TestDockerNetworkConnectDisconnect(c *check.C) { c.Assert(nr.Name, checker.Equals, "test") c.Assert(len(nr.Containers), checker.Equals, 0) - // check if network connect fails for inactive containers - dockerCmd(c, "stop", containerID) - _, _, err = dockerCmdWithError("network", "connect", "test", containerID) - c.Assert(err, check.NotNil) - dockerCmd(c, "network", "rm", "test") assertNwNotAvailable(c, "test") } @@ -960,7 +955,44 @@ func (s *DockerNetworkSuite) TestDockerNetworkRestartWithMulipleNetworks(c *chec networks, err := inspectField("foo", "NetworkSettings.Networks") c.Assert(err, checker.IsNil) c.Assert(networks, checker.Contains, "bridge", check.Commentf("Should contain 'bridge' network")) - c.Assert(networks, checker.Contains, "test", check.Commentf("Should contain 'test' netwokr")) + c.Assert(networks, checker.Contains, "test", check.Commentf("Should contain 'test' network")) +} + +func (s *DockerNetworkSuite) TestDockerNetworkConnectDisconnectToStoppedContainer(c *check.C) { + dockerCmd(c, "network", "create", "test") + dockerCmd(c, "create", "--name=foo", "busybox", "top") + dockerCmd(c, "network", "connect", "test", "foo") + networks, err := inspectField("foo", "NetworkSettings.Networks") + c.Assert(err, checker.IsNil) + c.Assert(networks, checker.Contains, "test", check.Commentf("Should contain 'test' network")) + + // Restart docker daemon to test the config has persisted to disk + s.d.Restart() + networks, err = inspectField("foo", "NetworkSettings.Networks") + c.Assert(err, checker.IsNil) + c.Assert(networks, checker.Contains, "test", check.Commentf("Should contain 'test' network")) + + // start the container and test if we can ping it from another container in the same network + dockerCmd(c, "start", "foo") + c.Assert(waitRun("foo"), checker.IsNil) + ip, err := inspectField("foo", "NetworkSettings.Networks.test.IPAddress") + ip = strings.TrimSpace(ip) + dockerCmd(c, "run", "--net=test", "busybox", "sh", "-c", fmt.Sprintf("ping -c 1 %s", ip)) + + dockerCmd(c, "stop", "foo") + + // Test disconnect + dockerCmd(c, "network", "disconnect", "test", "foo") + networks, err = inspectField("foo", "NetworkSettings.Networks") + c.Assert(err, checker.IsNil) + c.Assert(networks, checker.Not(checker.Contains), "test", check.Commentf("Should not contain 'test' network")) + + // Restart docker daemon to test the config has persisted to disk + s.d.Restart() + networks, err = inspectField("foo", "NetworkSettings.Networks") + c.Assert(err, checker.IsNil) + c.Assert(networks, checker.Not(checker.Contains), "test", check.Commentf("Should not contain 'test' network")) + } func (s *DockerNetworkSuite) TestDockerNetworkConnectPreferredIP(c *check.C) { diff --git a/man/docker-network-connect.1.md b/man/docker-network-connect.1.md index 6a77bfef46..e61874c80a 100644 --- a/man/docker-network-connect.1.md +++ b/man/docker-network-connect.1.md @@ -11,7 +11,7 @@ NETWORK CONTAINER # DESCRIPTION -Connects a running container to a network. You can connect a container by name +Connects a container to a network. You can connect a container by name or by ID. Once connected, the container can communicate with other containers in the same network. diff --git a/man/docker-network-disconnect.1.md b/man/docker-network-disconnect.1.md index 81b0387d85..bfe85ad23a 100644 --- a/man/docker-network-disconnect.1.md +++ b/man/docker-network-disconnect.1.md @@ -11,7 +11,7 @@ NETWORK CONTAINER # DESCRIPTION -Disconnects a container from a network. The container must be running to disconnect it from the network. +Disconnects a container from a network. ```bash $ docker network disconnect multi-host-network container1