diff --git a/daemon/container_unix.go b/daemon/container_unix.go index d5ea7afd97..d6766f73e9 100644 --- a/daemon/container_unix.go +++ b/daemon/container_unix.go @@ -615,10 +615,6 @@ func (container *Container) buildPortMapInfo(ep libnetwork.Endpoint, networkSett return networkSettings, nil } - if mac, ok := driverInfo[netlabel.MacAddress]; ok { - networkSettings.MacAddress = mac.(net.HardwareAddr).String() - } - networkSettings.Ports = nat.PortMap{} if expData, ok := driverInfo[netlabel.ExposedPorts]; ok { @@ -652,7 +648,7 @@ func (container *Container) buildPortMapInfo(ep libnetwork.Endpoint, networkSett return networkSettings, nil } -func (container *Container) buildEndpointInfo(ep libnetwork.Endpoint, networkSettings *network.Settings) (*network.Settings, error) { +func (container *Container) buildEndpointInfo(n libnetwork.Network, ep libnetwork.Endpoint, networkSettings *network.Settings) (*network.Settings, error) { if ep == nil { return nil, derr.ErrorCodeEmptyEndpoint } @@ -667,36 +663,70 @@ func (container *Container) buildEndpointInfo(ep libnetwork.Endpoint, networkSet return networkSettings, nil } + networkSettings.Networks[n.Name()].EndpointID = ep.ID() + iface := epInfo.Iface() if iface == nil { return networkSettings, nil } + if networkSettings.EndpointID == "" { + networkSettings.EndpointID = ep.ID() + } if iface.Address() != nil { ones, _ := iface.Address().Mask.Size() - networkSettings.IPAddress = iface.Address().IP.String() - networkSettings.IPPrefixLen = ones + if networkSettings.IPAddress == "" || networkSettings.IPPrefixLen == 0 { + networkSettings.IPAddress = iface.Address().IP.String() + networkSettings.IPPrefixLen = ones + } + networkSettings.Networks[n.Name()].IPAddress = iface.Address().IP.String() + networkSettings.Networks[n.Name()].IPPrefixLen = ones } if iface.AddressIPv6() != nil && iface.AddressIPv6().IP.To16() != nil { onesv6, _ := iface.AddressIPv6().Mask.Size() - networkSettings.GlobalIPv6Address = iface.AddressIPv6().IP.String() - networkSettings.GlobalIPv6PrefixLen = onesv6 + if networkSettings.GlobalIPv6Address == "" || networkSettings.GlobalIPv6PrefixLen == 0 { + networkSettings.GlobalIPv6Address = iface.AddressIPv6().IP.String() + networkSettings.GlobalIPv6PrefixLen = onesv6 + } + networkSettings.Networks[n.Name()].GlobalIPv6Address = iface.AddressIPv6().IP.String() + networkSettings.Networks[n.Name()].GlobalIPv6PrefixLen = onesv6 + } + + driverInfo, err := ep.DriverInfo() + if err != nil { + return nil, err + } + + if driverInfo == nil { + // It is not an error for epInfo to be nil + return networkSettings, nil + } + if mac, ok := driverInfo[netlabel.MacAddress]; ok { + if networkSettings.MacAddress == "" { + networkSettings.MacAddress = mac.(net.HardwareAddr).String() + } + networkSettings.Networks[n.Name()].MacAddress = mac.(net.HardwareAddr).String() } return networkSettings, nil } -func (container *Container) updateJoinInfo(ep libnetwork.Endpoint) error { +func (container *Container) updateJoinInfo(n libnetwork.Network, ep libnetwork.Endpoint) error { epInfo := ep.Info() if epInfo == nil { // It is not an error to get an empty endpoint info return nil } - - container.NetworkSettings.Gateway = epInfo.Gateway().String() + if container.NetworkSettings.Gateway == "" { + container.NetworkSettings.Gateway = epInfo.Gateway().String() + } + container.NetworkSettings.Networks[n.Name()].Gateway = epInfo.Gateway().String() if epInfo.GatewayIPv6().To16() != nil { - container.NetworkSettings.IPv6Gateway = epInfo.GatewayIPv6().String() + if container.NetworkSettings.IPv6Gateway == "" { + container.NetworkSettings.IPv6Gateway = epInfo.GatewayIPv6().String() + } + container.NetworkSettings.Networks[n.Name()].IPv6Gateway = epInfo.GatewayIPv6().String() } return nil @@ -704,11 +734,10 @@ func (container *Container) updateJoinInfo(ep libnetwork.Endpoint) error { func (container *Container) updateNetworkSettings(n libnetwork.Network) error { if container.NetworkSettings == nil { - container.NetworkSettings = &network.Settings{Networks: []string{}} + container.NetworkSettings = &network.Settings{Networks: make(map[string]*network.EndpointSettings)} } - settings := container.NetworkSettings - for _, s := range settings.Networks { + for s := range container.NetworkSettings.Networks { sn, err := container.daemon.FindNetwork(s) if err != nil { continue @@ -727,7 +756,7 @@ func (container *Container) updateNetworkSettings(n libnetwork.Network) error { return runconfig.ErrConflictNoNetwork } } - settings.Networks = append(settings.Networks, n.Name()) + container.NetworkSettings.Networks[n.Name()] = new(network.EndpointSettings) return nil } @@ -738,7 +767,7 @@ func (container *Container) updateEndpointNetworkSettings(n libnetwork.Network, return err } - networkSettings, err = container.buildEndpointInfo(ep, networkSettings) + networkSettings, err = container.buildEndpointInfo(n, ep, networkSettings) if err != nil { return err } @@ -769,7 +798,7 @@ func (container *Container) updateNetwork() error { // Find if container is connected to the default bridge network var n libnetwork.Network - for _, name := range container.NetworkSettings.Networks { + for name := range container.NetworkSettings.Networks { sn, err := container.daemon.FindNetwork(name) if err != nil { continue @@ -899,9 +928,8 @@ func createNetwork(controller libnetwork.NetworkController, dnet string, driver } func (container *Container) allocateNetwork() error { - settings := container.NetworkSettings.Networks updateSettings := false - if settings == nil { + if len(container.NetworkSettings.Networks) == 0 { mode := container.hostConfig.NetworkMode controller := container.daemon.netController if container.Config.NetworkDisabled || mode.IsContainer() { @@ -912,11 +940,12 @@ func (container *Container) allocateNetwork() error { if mode.IsDefault() { networkName = controller.Config().Daemon.DefaultNetwork } - settings = []string{networkName} + container.NetworkSettings.Networks = make(map[string]*network.EndpointSettings) + container.NetworkSettings.Networks[networkName] = new(network.EndpointSettings) updateSettings = true } - for _, n := range settings { + for n := range container.NetworkSettings.Networks { if err := container.connectToNetwork(n, updateSettings); err != nil { return err } @@ -1015,7 +1044,7 @@ func (container *Container) connectToNetwork(idOrName string, updateSettings boo return err } - if err := container.updateJoinInfo(ep); err != nil { + if err := container.updateJoinInfo(n, ep); err != nil { return derr.ErrorCodeJoinInfo.WithArgs(err) } @@ -1142,6 +1171,9 @@ func (container *Container) releaseNetwork() { sid := container.NetworkSettings.SandboxID networks := container.NetworkSettings.Networks + for n := range networks { + networks[n] = &network.EndpointSettings{} + } container.NetworkSettings = &network.Settings{Networks: networks} @@ -1199,19 +1231,18 @@ func (container *Container) disconnectFromNetwork(n libnetwork.Network) error { return fmt.Errorf("endpoint delete failed for container %s on network %s: %v", container.ID, n.Name(), err) } - networks := container.NetworkSettings.Networks - for i, s := range networks { - sn, err := container.daemon.FindNetwork(s) - if err != nil { - continue - } - if sn.Name() == n.Name() { - networks = append(networks[:i], networks[i+1:]...) - container.NetworkSettings.Networks = networks - break - } - } + if container.NetworkSettings.EndpointID == container.NetworkSettings.Networks[n.Name()].EndpointID { + container.NetworkSettings.EndpointID = "" + container.NetworkSettings.Gateway = "" + container.NetworkSettings.GlobalIPv6Address = "" + container.NetworkSettings.GlobalIPv6PrefixLen = 0 + container.NetworkSettings.IPAddress = "" + container.NetworkSettings.IPPrefixLen = 0 + container.NetworkSettings.IPv6Gateway = "" + container.NetworkSettings.MacAddress = "" + } + delete(container.NetworkSettings.Networks, n.Name()) return nil } diff --git a/daemon/network/settings.go b/daemon/network/settings.go index 4ade435413..ecfecc60c6 100644 --- a/daemon/network/settings.go +++ b/daemon/network/settings.go @@ -26,22 +26,34 @@ type IPAMConfig struct { // TODO Windows. Many of these fields can be factored out., type Settings struct { Bridge string - EndpointID string + EndpointID string // this is for backward compatibility SandboxID string - Gateway string - GlobalIPv6Address string - GlobalIPv6PrefixLen int + Gateway string // this is for backward compatibility + GlobalIPv6Address string // this is for backward compatibility + GlobalIPv6PrefixLen int // this is for backward compatibility HairpinMode bool - IPAddress string - IPPrefixLen int - IPv6Gateway string + IPAddress string // this is for backward compatibility + IPPrefixLen int // this is for backward compatibility + IPv6Gateway string // this is for backward compatibility LinkLocalIPv6Address string LinkLocalIPv6PrefixLen int - MacAddress string - Networks []string + MacAddress string // this is for backward compatibility + Networks map[string]*EndpointSettings Ports nat.PortMap SandboxKey string SecondaryIPAddresses []Address SecondaryIPv6Addresses []Address IsAnonymousEndpoint bool } + +// EndpointSettings stores the network endpoint details +type EndpointSettings struct { + EndpointID string + Gateway string + IPAddress string + IPPrefixLen int + IPv6Gateway string + GlobalIPv6Address string + GlobalIPv6PrefixLen int + MacAddress string +} diff --git a/integration-cli/docker_api_network_test.go b/integration-cli/docker_api_network_test.go index f76796b778..2180cd10e4 100644 --- a/integration-cli/docker_api_network_test.go +++ b/integration-cli/docker_api_network_test.go @@ -54,7 +54,7 @@ func (s *DockerSuite) TestApiNetworkInspect(c *check.C) { // run a container and attach it to the default bridge network out, _ := dockerCmd(c, "run", "-d", "--name", "test", "busybox", "top") containerID := strings.TrimSpace(out) - containerIP := findContainerIP(c, "test") + containerIP := findContainerIP(c, "test", "bridge") // inspect default bridge network again and make sure the container is connected nr = getNetworkResource(c, nr.ID) @@ -122,7 +122,7 @@ func (s *DockerSuite) TestApiNetworkConnectDisconnect(c *check.C) { // check if container IP matches network inspect ip, _, err := net.ParseCIDR(nr.Containers[containerID].IPv4Address) c.Assert(err, checker.IsNil) - containerIP := findContainerIP(c, "test") + containerIP := findContainerIP(c, "test", "testnetwork") c.Assert(ip.String(), checker.Equals, containerIP) // disconnect container from the network diff --git a/integration-cli/docker_api_stats_test.go b/integration-cli/docker_api_stats_test.go index cf204b92f1..4b302dacbc 100644 --- a/integration-cli/docker_api_stats_test.go +++ b/integration-cli/docker_api_stats_test.go @@ -85,7 +85,7 @@ func (s *DockerSuite) TestApiStatsNetworkStats(c *check.C) { c.Assert(waitRun(id), checker.IsNil) // Retrieve the container address - contIP := findContainerIP(c, id) + contIP := findContainerIP(c, id, "bridge") numPings := 10 var preRxPackets uint64 diff --git a/integration-cli/docker_cli_network_unix_test.go b/integration-cli/docker_cli_network_unix_test.go index fca94b219e..d4a7eb337f 100644 --- a/integration-cli/docker_cli_network_unix_test.go +++ b/integration-cli/docker_cli_network_unix_test.go @@ -284,7 +284,7 @@ func (s *DockerNetworkSuite) TestDockerNetworkConnectDisconnect(c *check.C) { // check if container IP matches network inspect ip, _, err := net.ParseCIDR(nr.Containers[containerID].IPv4Address) c.Assert(err, check.IsNil) - containerIP := findContainerIP(c, "test") + containerIP := findContainerIP(c, "test", "test") c.Assert(ip.String(), checker.Equals, containerIP) // disconnect container from the network diff --git a/integration-cli/docker_utils.go b/integration-cli/docker_utils.go index 01c53efc09..67627aff93 100644 --- a/integration-cli/docker_utils.go +++ b/integration-cli/docker_utils.go @@ -782,13 +782,13 @@ func dockerCmdInDirWithTimeout(timeout time.Duration, path string, args ...strin return integration.DockerCmdInDirWithTimeout(dockerBinary, timeout, path, args...) } -func findContainerIP(c *check.C, id string, vargs ...string) string { - out, _ := dockerCmd(c, "inspect", "--format='{{ .NetworkSettings.IPAddress }}'", id) +func findContainerIP(c *check.C, id string, network string) string { + out, _ := dockerCmd(c, "inspect", fmt.Sprintf("--format='{{ .NetworkSettings.Networks.%s.IPAddress }}'", network), id) return strings.Trim(out, " \r\n'") } func (d *Daemon) findContainerIP(id string) string { - return findContainerIP(d.c, id, "--host", d.sock()) + return findContainerIP(d.c, id, "--host") } func getContainerCount() (int, error) {