Procházet zdrojové kódy

Merge pull request #18906 from coolljt0725/connect_to_created

Support network connect/disconnect to stopped container
Sebastiaan van Stijn před 9 roky
rodič
revize
301627c677

+ 53 - 30
daemon/container_operations_unix.go

@@ -696,13 +696,43 @@ func cleanOperationalData(es *networktypes.EndpointSettings) {
 	es.MacAddress = ""
 	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
 // ConnectToNetwork connects a container to a network
 func (daemon *Daemon) ConnectToNetwork(container *container.Container, idOrName string, endpointConfig *networktypes.EndpointSettings) error {
 func (daemon *Daemon) ConnectToNetwork(container *container.Container, idOrName string, endpointConfig *networktypes.EndpointSettings) error {
 	if !container.Running {
 	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 {
 	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)
@@ -711,37 +741,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) {
 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) {
 	if !containertypes.NetworkMode(idOrName).IsUserDefined() && hasUserDefinedIPAddress(endpointConfig) {
 		return runconfig.ErrUnsupportedNetworkAndIP
 		return runconfig.ErrUnsupportedNetworkAndIP
 	}
 	}
 
 
-	if containertypes.NetworkMode(idOrName).IsBridge() &&
-		daemon.configStore.DisableBridge {
-		container.Config.NetworkDisabled = true
-		return nil
-	}
-
 	controller := daemon.netController
 	controller := daemon.netController
 
 
-	n, err := daemon.FindNetwork(idOrName)
-	if err != nil {
-		return err
-	}
-
 	if err := validateNetworkingConfig(n, endpointConfig); err != nil {
 	if err := validateNetworkingConfig(n, endpointConfig); err != nil {
 		return err
 		return err
 	}
 	}
 
 
-	if updateSettings {
-		if err := daemon.updateNetworkSettings(container, n); err != nil {
-			return err
-		}
-	}
-
 	if endpointConfig != nil {
 	if endpointConfig != nil {
 		container.NetworkSettings.Networks[n.Name()] = endpointConfig
 		container.NetworkSettings.Networks[n.Name()] = endpointConfig
 	}
 	}
@@ -805,16 +822,22 @@ 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) error {
 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() {
 	if container.HostConfig.NetworkMode.IsHost() && containertypes.NetworkMode(n.Type()).IsHost() {
 		return runconfig.ErrConflictHostNetwork
 		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 {
 	if err := container.ToDiskLocking(); err != nil {

+ 1 - 1
docs/reference/commandline/network_connect.md

@@ -16,7 +16,7 @@ parent = "smn_cli"
 
 
       --help             Print usage
       --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
 or by ID. Once connected, the container can communicate with other containers in
 the same network.
 the same network.
 
 

+ 2 - 3
docs/userguide/networking/work-with-networks.md

@@ -311,9 +311,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
 ## Disconnecting containers
 
 

+ 9 - 0
errors/daemon.go

@@ -46,6 +46,15 @@ var (
 		HTTPStatusCode: http.StatusInternalServerError,
 		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
 	// ErrorCodePausedContainer is generated when we attempt to attach a
 	// container but its paused.
 	// container but its paused.
 	ErrorCodePausedContainer = errcode.Register(errGroup, errcode.ErrorDescriptor{
 	ErrorCodePausedContainer = errcode.Register(errGroup, errcode.ErrorDescriptor{

+ 38 - 6
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(nr.Name, checker.Equals, "test")
 	c.Assert(len(nr.Containers), checker.Equals, 0)
 	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")
 	dockerCmd(c, "network", "rm", "test")
 	assertNwNotAvailable(c, "test")
 	assertNwNotAvailable(c, "test")
 }
 }
@@ -938,7 +933,44 @@ func (s *DockerNetworkSuite) TestDockerNetworkRestartWithMulipleNetworks(c *chec
 	networks, err := inspectField("foo", "NetworkSettings.Networks")
 	networks, err := inspectField("foo", "NetworkSettings.Networks")
 	c.Assert(err, checker.IsNil)
 	c.Assert(err, checker.IsNil)
 	c.Assert(networks, checker.Contains, "bridge", check.Commentf("Should contain 'bridge' network"))
 	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) {
 func (s *DockerNetworkSuite) TestDockerNetworkConnectPreferredIP(c *check.C) {

+ 1 - 1
man/docker-network-connect.1.md

@@ -11,7 +11,7 @@ NETWORK CONTAINER
 
 
 # DESCRIPTION
 # 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
 or by ID. Once connected, the container can communicate with other containers in
 the same network.
 the same network.
 
 

+ 1 - 1
man/docker-network-disconnect.1.md

@@ -11,7 +11,7 @@ NETWORK CONTAINER
 
 
 # DESCRIPTION
 # 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
 ```bash
   $ docker network disconnect multi-host-network container1
   $ docker network disconnect multi-host-network container1