Browse code

Merge pull request #18906 from coolljt0725/connect_to_created

Support network connect/disconnect to stopped container

Sebastiaan van Stijn authored on 2016/01/13 00:06:31
Showing 7 changed files
... ...
@@ -696,13 +696,43 @@ func cleanOperationalData(es *networktypes.EndpointSettings) {
696 696
 	es.MacAddress = ""
697 697
 }
698 698
 
699
+func (daemon *Daemon) updateNetworkConfig(container *container.Container, idOrName string, updateSettings bool) (libnetwork.Network, error) {
700
+	if container.HostConfig.NetworkMode.IsContainer() {
701
+		return nil, runconfig.ErrConflictSharedNetwork
702
+	}
703
+
704
+	if containertypes.NetworkMode(idOrName).IsBridge() &&
705
+		daemon.configStore.DisableBridge {
706
+		container.Config.NetworkDisabled = true
707
+		return nil, nil
708
+	}
709
+
710
+	n, err := daemon.FindNetwork(idOrName)
711
+	if err != nil {
712
+		return nil, err
713
+	}
714
+
715
+	if updateSettings {
716
+		if err := daemon.updateNetworkSettings(container, n); err != nil {
717
+			return nil, err
718
+		}
719
+	}
720
+	return n, nil
721
+}
722
+
699 723
 // ConnectToNetwork connects a container to a network
700 724
 func (daemon *Daemon) ConnectToNetwork(container *container.Container, idOrName string, endpointConfig *networktypes.EndpointSettings) error {
701 725
 	if !container.Running {
702
-		return derr.ErrorCodeNotRunning.WithArgs(container.ID)
703
-	}
704
-	if err := daemon.connectToNetwork(container, idOrName, endpointConfig, true); err != nil {
705
-		return err
726
+		if container.RemovalInProgress || container.Dead {
727
+			return derr.ErrorCodeRemovalContainer.WithArgs(container.ID)
728
+		}
729
+		if _, err := daemon.updateNetworkConfig(container, idOrName, true); err != nil {
730
+			return err
731
+		}
732
+	} else {
733
+		if err := daemon.connectToNetwork(container, idOrName, endpointConfig, true); err != nil {
734
+			return err
735
+		}
706 736
 	}
707 737
 	if err := container.ToDiskLocking(); err != nil {
708 738
 		return fmt.Errorf("Error saving container to disk: %v", err)
... ...
@@ -711,37 +741,24 @@ func (daemon *Daemon) ConnectToNetwork(container *container.Container, idOrName
711 711
 }
712 712
 
713 713
 func (daemon *Daemon) connectToNetwork(container *container.Container, idOrName string, endpointConfig *networktypes.EndpointSettings, updateSettings bool) (err error) {
714
-	if container.HostConfig.NetworkMode.IsContainer() {
715
-		return runconfig.ErrConflictSharedNetwork
714
+	n, err := daemon.updateNetworkConfig(container, idOrName, updateSettings)
715
+	if err != nil {
716
+		return err
717
+	}
718
+	if n == nil {
719
+		return nil
716 720
 	}
717 721
 
718 722
 	if !containertypes.NetworkMode(idOrName).IsUserDefined() && hasUserDefinedIPAddress(endpointConfig) {
719 723
 		return runconfig.ErrUnsupportedNetworkAndIP
720 724
 	}
721 725
 
722
-	if containertypes.NetworkMode(idOrName).IsBridge() &&
723
-		daemon.configStore.DisableBridge {
724
-		container.Config.NetworkDisabled = true
725
-		return nil
726
-	}
727
-
728 726
 	controller := daemon.netController
729 727
 
730
-	n, err := daemon.FindNetwork(idOrName)
731
-	if err != nil {
732
-		return err
733
-	}
734
-
735 728
 	if err := validateNetworkingConfig(n, endpointConfig); err != nil {
736 729
 		return err
737 730
 	}
738 731
 
739
-	if updateSettings {
740
-		if err := daemon.updateNetworkSettings(container, n); err != nil {
741
-			return err
742
-		}
743
-	}
744
-
745 732
 	if endpointConfig != nil {
746 733
 		container.NetworkSettings.Networks[n.Name()] = endpointConfig
747 734
 	}
... ...
@@ -805,16 +822,22 @@ func (daemon *Daemon) connectToNetwork(container *container.Container, idOrName
805 805
 
806 806
 // DisconnectFromNetwork disconnects container from network n.
807 807
 func (daemon *Daemon) DisconnectFromNetwork(container *container.Container, n libnetwork.Network) error {
808
-	if !container.Running {
809
-		return derr.ErrorCodeNotRunning.WithArgs(container.ID)
810
-	}
811
-
812 808
 	if container.HostConfig.NetworkMode.IsHost() && containertypes.NetworkMode(n.Type()).IsHost() {
813 809
 		return runconfig.ErrConflictHostNetwork
814 810
 	}
815
-
816
-	if err := disconnectFromNetwork(container, n); err != nil {
817
-		return err
811
+	if !container.Running {
812
+		if container.RemovalInProgress || container.Dead {
813
+			return derr.ErrorCodeRemovalContainer.WithArgs(container.ID)
814
+		}
815
+		if _, ok := container.NetworkSettings.Networks[n.Name()]; ok {
816
+			delete(container.NetworkSettings.Networks, n.Name())
817
+		} else {
818
+			return fmt.Errorf("container %s is not connected to the network %s", container.ID, n.Name())
819
+		}
820
+	} else {
821
+		if err := disconnectFromNetwork(container, n); err != nil {
822
+			return err
823
+		}
818 824
 	}
819 825
 
820 826
 	if err := container.ToDiskLocking(); err != nil {
... ...
@@ -16,7 +16,7 @@ parent = "smn_cli"
16 16
 
17 17
       --help             Print usage
18 18
 
19
-Connects a running container to a network. You can connect a container by name
19
+Connects a container to a network. You can connect a container by name
20 20
 or by ID. Once connected, the container can communicate with other containers in
21 21
 the same network.
22 22
 
... ...
@@ -311,9 +311,8 @@ PING 172.17.0.2 (172.17.0.2): 56 data bytes
311 311
 
312 312
 ```
313 313
 
314
-To connect a container to a network, the container must be running. If you stop
315
-a container and inspect a network it belongs to, you won't see that container.
316
-The `docker network inspect` command only shows running containers.
314
+You can connect both running and non-running containers to a network. However,
315
+`docker network inspect` only displays information on running containers.
317 316
 
318 317
 ## Disconnecting containers
319 318
 
... ...
@@ -46,6 +46,15 @@ var (
46 46
 		HTTPStatusCode: http.StatusInternalServerError,
47 47
 	})
48 48
 
49
+	// ErrorCodeRemovalContainer is generated when we attempt to connect or disconnect a
50
+	// container but it's marked for removal.
51
+	ErrorCodeRemovalContainer = errcode.Register(errGroup, errcode.ErrorDescriptor{
52
+		Value:          "REMOVALCONTAINER",
53
+		Message:        "Container %s is marked for removal and cannot be connected or disconnected to the network",
54
+		Description:    "The specified container is marked for removal and cannot be connected or disconnected to the network",
55
+		HTTPStatusCode: http.StatusInternalServerError,
56
+	})
57
+
49 58
 	// ErrorCodePausedContainer is generated when we attempt to attach a
50 59
 	// container but its paused.
51 60
 	ErrorCodePausedContainer = errcode.Register(errGroup, errcode.ErrorDescriptor{
... ...
@@ -448,11 +448,6 @@ func (s *DockerNetworkSuite) TestDockerNetworkConnectDisconnect(c *check.C) {
448 448
 	c.Assert(nr.Name, checker.Equals, "test")
449 449
 	c.Assert(len(nr.Containers), checker.Equals, 0)
450 450
 
451
-	// check if network connect fails for inactive containers
452
-	dockerCmd(c, "stop", containerID)
453
-	_, _, err = dockerCmdWithError("network", "connect", "test", containerID)
454
-	c.Assert(err, check.NotNil)
455
-
456 451
 	dockerCmd(c, "network", "rm", "test")
457 452
 	assertNwNotAvailable(c, "test")
458 453
 }
... ...
@@ -938,7 +933,44 @@ func (s *DockerNetworkSuite) TestDockerNetworkRestartWithMulipleNetworks(c *chec
938 938
 	networks, err := inspectField("foo", "NetworkSettings.Networks")
939 939
 	c.Assert(err, checker.IsNil)
940 940
 	c.Assert(networks, checker.Contains, "bridge", check.Commentf("Should contain 'bridge' network"))
941
-	c.Assert(networks, checker.Contains, "test", check.Commentf("Should contain 'test' netwokr"))
941
+	c.Assert(networks, checker.Contains, "test", check.Commentf("Should contain 'test' network"))
942
+}
943
+
944
+func (s *DockerNetworkSuite) TestDockerNetworkConnectDisconnectToStoppedContainer(c *check.C) {
945
+	dockerCmd(c, "network", "create", "test")
946
+	dockerCmd(c, "create", "--name=foo", "busybox", "top")
947
+	dockerCmd(c, "network", "connect", "test", "foo")
948
+	networks, err := inspectField("foo", "NetworkSettings.Networks")
949
+	c.Assert(err, checker.IsNil)
950
+	c.Assert(networks, checker.Contains, "test", check.Commentf("Should contain 'test' network"))
951
+
952
+	// Restart docker daemon to test the config has persisted to disk
953
+	s.d.Restart()
954
+	networks, err = inspectField("foo", "NetworkSettings.Networks")
955
+	c.Assert(err, checker.IsNil)
956
+	c.Assert(networks, checker.Contains, "test", check.Commentf("Should contain 'test' network"))
957
+
958
+	// start the container and test if we can ping it from another container in the same network
959
+	dockerCmd(c, "start", "foo")
960
+	c.Assert(waitRun("foo"), checker.IsNil)
961
+	ip, err := inspectField("foo", "NetworkSettings.Networks.test.IPAddress")
962
+	ip = strings.TrimSpace(ip)
963
+	dockerCmd(c, "run", "--net=test", "busybox", "sh", "-c", fmt.Sprintf("ping -c 1 %s", ip))
964
+
965
+	dockerCmd(c, "stop", "foo")
966
+
967
+	// Test disconnect
968
+	dockerCmd(c, "network", "disconnect", "test", "foo")
969
+	networks, err = inspectField("foo", "NetworkSettings.Networks")
970
+	c.Assert(err, checker.IsNil)
971
+	c.Assert(networks, checker.Not(checker.Contains), "test", check.Commentf("Should not contain 'test' network"))
972
+
973
+	// Restart docker daemon to test the config has persisted to disk
974
+	s.d.Restart()
975
+	networks, err = inspectField("foo", "NetworkSettings.Networks")
976
+	c.Assert(err, checker.IsNil)
977
+	c.Assert(networks, checker.Not(checker.Contains), "test", check.Commentf("Should not contain 'test' network"))
978
+
942 979
 }
943 980
 
944 981
 func (s *DockerNetworkSuite) TestDockerNetworkConnectPreferredIP(c *check.C) {
... ...
@@ -11,7 +11,7 @@ NETWORK CONTAINER
11 11
 
12 12
 # DESCRIPTION
13 13
 
14
-Connects a running container to a network. You can connect a container by name
14
+Connects a container to a network. You can connect a container by name
15 15
 or by ID. Once connected, the container can communicate with other containers in
16 16
 the same network.
17 17
 
... ...
@@ -11,7 +11,7 @@ NETWORK CONTAINER
11 11
 
12 12
 # DESCRIPTION
13 13
 
14
-Disconnects a container from a network. The container must be running to disconnect it from the network.
14
+Disconnects a container from a network.
15 15
 
16 16
 ```bash
17 17
   $ docker network disconnect multi-host-network container1