diff --git a/daemon/container_unix.go b/daemon/container_unix.go index 722b25dc18..f3ae95dcf2 100644 --- a/daemon/container_unix.go +++ b/daemon/container_unix.go @@ -392,32 +392,34 @@ func (container *Container) buildHostnameFile() error { return ioutil.WriteFile(container.HostnamePath, []byte(container.Config.Hostname+"\n"), 0644) } -func (container *Container) buildJoinOptions() ([]libnetwork.EndpointOption, error) { +func (container *Container) buildSandboxOptions() ([]libnetwork.SandboxOption, error) { var ( - joinOptions []libnetwork.EndpointOption + sboxOptions []libnetwork.SandboxOption err error dns []string dnsSearch []string ) - joinOptions = append(joinOptions, libnetwork.JoinOptionHostname(container.Config.Hostname), - libnetwork.JoinOptionDomainname(container.Config.Domainname)) + sboxOptions = append(sboxOptions, libnetwork.OptionHostname(container.Config.Hostname), + libnetwork.OptionDomainname(container.Config.Domainname)) if container.hostConfig.NetworkMode.IsHost() { - joinOptions = append(joinOptions, libnetwork.JoinOptionUseDefaultSandbox()) + sboxOptions = append(sboxOptions, libnetwork.OptionUseDefaultSandbox()) + sboxOptions = append(sboxOptions, libnetwork.OptionOriginHostsPath("/etc/hosts")) + sboxOptions = append(sboxOptions, libnetwork.OptionOriginResolvConfPath("/etc/resolv.conf")) } container.HostsPath, err = container.getRootResourcePath("hosts") if err != nil { return nil, err } - joinOptions = append(joinOptions, libnetwork.JoinOptionHostsPath(container.HostsPath)) + sboxOptions = append(sboxOptions, libnetwork.OptionHostsPath(container.HostsPath)) container.ResolvConfPath, err = container.getRootResourcePath("resolv.conf") if err != nil { return nil, err } - joinOptions = append(joinOptions, libnetwork.JoinOptionResolvConfPath(container.ResolvConfPath)) + sboxOptions = append(sboxOptions, libnetwork.OptionResolvConfPath(container.ResolvConfPath)) if len(container.hostConfig.DNS) > 0 { dns = container.hostConfig.DNS @@ -426,7 +428,7 @@ func (container *Container) buildJoinOptions() ([]libnetwork.EndpointOption, err } for _, d := range dns { - joinOptions = append(joinOptions, libnetwork.JoinOptionDNS(d)) + sboxOptions = append(sboxOptions, libnetwork.OptionDNS(d)) } if len(container.hostConfig.DNSSearch) > 0 { @@ -436,7 +438,7 @@ func (container *Container) buildJoinOptions() ([]libnetwork.EndpointOption, err } for _, ds := range dnsSearch { - joinOptions = append(joinOptions, libnetwork.JoinOptionDNSSearch(ds)) + sboxOptions = append(sboxOptions, libnetwork.OptionDNSSearch(ds)) } if container.NetworkSettings.SecondaryIPAddresses != nil { @@ -446,7 +448,7 @@ func (container *Container) buildJoinOptions() ([]libnetwork.EndpointOption, err } for _, a := range container.NetworkSettings.SecondaryIPAddresses { - joinOptions = append(joinOptions, libnetwork.JoinOptionExtraHost(name, a.Addr)) + sboxOptions = append(sboxOptions, libnetwork.OptionExtraHost(name, a.Addr)) } } @@ -465,7 +467,7 @@ func (container *Container) buildJoinOptions() ([]libnetwork.EndpointOption, err if alias != child.Name[1:] { aliasList = aliasList + " " + child.Name[1:] } - joinOptions = append(joinOptions, libnetwork.JoinOptionExtraHost(aliasList, child.NetworkSettings.IPAddress)) + sboxOptions = append(sboxOptions, libnetwork.OptionExtraHost(aliasList, child.NetworkSettings.IPAddress)) if child.NetworkSettings.EndpointID != "" { childEndpoints = append(childEndpoints, child.NetworkSettings.EndpointID) } @@ -474,7 +476,7 @@ func (container *Container) buildJoinOptions() ([]libnetwork.EndpointOption, err for _, extraHost := range container.hostConfig.ExtraHosts { // allow IPv6 addresses in extra hosts; only split on first ":" parts := strings.SplitN(extraHost, ":", 2) - joinOptions = append(joinOptions, libnetwork.JoinOptionExtraHost(parts[0], parts[1])) + sboxOptions = append(sboxOptions, libnetwork.OptionExtraHost(parts[0], parts[1])) } refs := container.daemon.containerGraph().RefPaths(container.ID) @@ -490,7 +492,7 @@ func (container *Container) buildJoinOptions() ([]libnetwork.EndpointOption, err if c != nil && !container.daemon.configStore.DisableBridge && container.hostConfig.NetworkMode.IsPrivate() { logrus.Debugf("Update /etc/hosts of %s for alias %s with ip %s", c.ID, ref.Name, container.NetworkSettings.IPAddress) - joinOptions = append(joinOptions, libnetwork.JoinOptionParentUpdate(c.NetworkSettings.EndpointID, ref.Name, container.NetworkSettings.IPAddress)) + sboxOptions = append(sboxOptions, libnetwork.OptionParentUpdate(c.ID, ref.Name, container.NetworkSettings.IPAddress)) if c.NetworkSettings.EndpointID != "" { parentEndpoints = append(parentEndpoints, c.NetworkSettings.EndpointID) } @@ -504,9 +506,9 @@ func (container *Container) buildJoinOptions() ([]libnetwork.EndpointOption, err }, } - joinOptions = append(joinOptions, libnetwork.JoinOptionGeneric(linkOptions)) + sboxOptions = append(sboxOptions, libnetwork.OptionGeneric(linkOptions)) - return joinOptions, nil + return sboxOptions, nil } func (container *Container) buildPortMapInfo(n libnetwork.Network, ep libnetwork.Endpoint, networkSettings *network.Settings) (*network.Settings, error) { @@ -630,12 +632,10 @@ func (container *Container) updateJoinInfo(ep libnetwork.Endpoint) error { container.NetworkSettings.IPv6Gateway = epInfo.GatewayIPv6().String() } - container.NetworkSettings.SandboxKey = epInfo.SandboxKey() - return nil } -func (container *Container) updateNetworkSettings(n libnetwork.Network, ep libnetwork.Endpoint) error { +func (container *Container) updateEndpointNetworkSettings(n libnetwork.Network, ep libnetwork.Endpoint) error { networkSettings := &network.Settings{NetworkID: n.ID(), EndpointID: ep.ID()} networkSettings, err := container.buildPortMapInfo(n, ep, networkSettings) @@ -656,35 +656,30 @@ func (container *Container) updateNetworkSettings(n libnetwork.Network, ep libne return nil } +func (container *Container) updateSandboxNetworkSettings(sb libnetwork.Sandbox) error { + container.NetworkSettings.SandboxID = sb.ID() + container.NetworkSettings.SandboxKey = sb.Key() + return nil +} + // UpdateNetwork is used to update the container's network (e.g. when linked containers // get removed/unlinked). func (container *Container) updateNetwork() error { - n, err := container.daemon.netController.NetworkByID(container.NetworkSettings.NetworkID) + ctrl := container.daemon.netController + sid := container.NetworkSettings.SandboxID + + sb, err := ctrl.SandboxByID(sid) if err != nil { - return fmt.Errorf("error locating network id %s: %v", container.NetworkSettings.NetworkID, err) + return fmt.Errorf("error locating sandbox id %s: %v", sid, err) } - ep, err := n.EndpointByID(container.NetworkSettings.EndpointID) - if err != nil { - return fmt.Errorf("error locating endpoint id %s: %v", container.NetworkSettings.EndpointID, err) - } - - if err := ep.Leave(container.ID); err != nil { - return fmt.Errorf("endpoint leave failed: %v", err) - - } - - joinOptions, err := container.buildJoinOptions() + options, err := container.buildSandboxOptions() if err != nil { return fmt.Errorf("Update network failed: %v", err) } - if err := ep.Join(container.ID, joinOptions...); err != nil { - return fmt.Errorf("endpoint join failed: %v", err) - } - - if err := container.updateJoinInfo(ep); err != nil { - return fmt.Errorf("Updating join info failed: %v", err) + if err := sb.Refresh(options...); err != nil { + return fmt.Errorf("Update network failed: Failure in refresh sandbox %s: %v", sid, err) } return nil @@ -871,6 +866,7 @@ func (container *Container) allocateNetwork() error { func (container *Container) configureNetwork(networkName, service, networkDriver string, canCreateNetwork bool) error { controller := container.daemon.netController + n, err := controller.NetworkByName(networkName) if err != nil { if _, ok := err.(libnetwork.ErrNoSuchNetwork); !ok || !canCreateNetwork { @@ -899,16 +895,32 @@ func (container *Container) configureNetwork(networkName, service, networkDriver } } - if err := container.updateNetworkSettings(n, ep); err != nil { + if err := container.updateEndpointNetworkSettings(n, ep); err != nil { return err } - joinOptions, err := container.buildJoinOptions() - if err != nil { - return err + var sb libnetwork.Sandbox + controller.WalkSandboxes(func(s libnetwork.Sandbox) bool { + if s.ContainerID() == container.ID { + sb = s + return true + } + return false + }) + if sb == nil { + options, err := container.buildSandboxOptions() + if err != nil { + return err + } + sb, err = controller.NewSandbox(container.ID, options...) + if err != nil { + return err + } } - if err := ep.Join(container.ID, joinOptions...); err != nil { + container.updateSandboxNetworkSettings(sb) + + if err := ep.Join(sb); err != nil { return err } @@ -1038,12 +1050,19 @@ func (container *Container) releaseNetwork() { return } + sid := container.NetworkSettings.SandboxID eid := container.NetworkSettings.EndpointID nid := container.NetworkSettings.NetworkID container.NetworkSettings = &network.Settings{} - if nid == "" || eid == "" { + if sid == "" || nid == "" || eid == "" { + return + } + + sb, err := container.daemon.netController.SandboxByID(sid) + if err != nil { + logrus.Errorf("error locating sandbox id %s: %v", sid, err) return } @@ -1059,17 +1078,9 @@ func (container *Container) releaseNetwork() { return } - switch { - case container.hostConfig.NetworkMode.IsHost(): - if err := ep.Leave(container.ID); err != nil { - logrus.Errorf("Error leaving endpoint id %s for container %s: %v", eid, container.ID, err) - return - } - default: - if err := container.daemon.netController.LeaveAll(container.ID); err != nil { - logrus.Errorf("Leave all failed for %s: %v", container.ID, err) - return - } + if err := sb.Delete(); err != nil { + logrus.Errorf("Error deleting sandbox id %s for container %s: %v", sid, container.ID, err) + return } // In addition to leaving all endpoints, delete implicitly created endpoint diff --git a/daemon/network/settings.go b/daemon/network/settings.go index df4822f6c3..725b399c31 100644 --- a/daemon/network/settings.go +++ b/daemon/network/settings.go @@ -13,6 +13,7 @@ type Address struct { type Settings struct { Bridge string EndpointID string + SandboxID string Gateway string GlobalIPv6Address string GlobalIPv6PrefixLen int diff --git a/daemon/stats.go b/daemon/stats.go index b31b8ba993..aa356633b7 100644 --- a/daemon/stats.go +++ b/daemon/stats.go @@ -6,7 +6,7 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/daemon/execdriver" - "github.com/docker/libnetwork/sandbox" + "github.com/docker/libnetwork/osl" "github.com/opencontainers/runc/libcontainer" ) @@ -86,16 +86,12 @@ func (daemon *Daemon) getNetworkStats(name string) ([]*libcontainer.NetworkInter return list, err } - nw, err := daemon.netController.NetworkByID(c.NetworkSettings.NetworkID) - if err != nil { - return list, err - } - ep, err := nw.EndpointByID(c.NetworkSettings.EndpointID) + sb, err := daemon.netController.SandboxByID(c.NetworkSettings.SandboxID) if err != nil { return list, err } - stats, err := ep.Statistics() + stats, err := sb.Statistics() if err != nil { return list, err } @@ -108,7 +104,7 @@ func (daemon *Daemon) getNetworkStats(name string) ([]*libcontainer.NetworkInter return list, nil } -func convertLnNetworkStats(name string, stats *sandbox.InterfaceStatistics) *libcontainer.NetworkInterface { +func convertLnNetworkStats(name string, stats *osl.InterfaceStatistics) *libcontainer.NetworkInterface { n := &libcontainer.NetworkInterface{Name: name} n.RxBytes = stats.RxBytes n.RxPackets = stats.RxPackets diff --git a/integration-cli/docker_cli_daemon_test.go b/integration-cli/docker_cli_daemon_test.go index dea981edbb..46843c596e 100644 --- a/integration-cli/docker_cli_daemon_test.go +++ b/integration-cli/docker_cli_daemon_test.go @@ -1524,19 +1524,23 @@ func (s *DockerDaemonSuite) TestDaemonRestartCleanupNetns(c *check.C) { if err != nil { c.Fatal(out, err) } + + // Get sandbox key via inspect + out, err = s.d.Cmd("inspect", "--format", "'{{.NetworkSettings.SandboxKey}}'", "netns") + if err != nil { + c.Fatalf("Error inspecting container: %s, %v", out, err) + } + fileName := strings.Trim(out, " \r\n'") + if out, err := s.d.Cmd("stop", "netns"); err != nil { c.Fatal(out, err) } - // Construct netns file name from container id - out = strings.TrimSpace(out) - nsFile := out[:12] - // Test if the file still exists - out, _, err = runCommandWithOutput(exec.Command("stat", "-c", "%n", "/var/run/docker/netns/"+nsFile)) + out, _, err = runCommandWithOutput(exec.Command("stat", "-c", "%n", fileName)) out = strings.TrimSpace(out) c.Assert(err, check.IsNil, check.Commentf("Output: %s", out)) - c.Assert(out, check.Equals, "/var/run/docker/netns/"+nsFile, check.Commentf("Output: %s", out)) + c.Assert(out, check.Equals, fileName, check.Commentf("Output: %s", out)) // Remove the container and restart the daemon if out, err := s.d.Cmd("rm", "netns"); err != nil { @@ -1548,10 +1552,9 @@ func (s *DockerDaemonSuite) TestDaemonRestartCleanupNetns(c *check.C) { } // Test again and see now the netns file does not exist - out, _, err = runCommandWithOutput(exec.Command("stat", "-c", "%n", "/var/run/docker/netns/"+nsFile)) + out, _, err = runCommandWithOutput(exec.Command("stat", "-c", "%n", fileName)) out = strings.TrimSpace(out) c.Assert(err, check.Not(check.IsNil), check.Commentf("Output: %s", out)) - // c.Assert(out, check.Equals, "", check.Commentf("Output: %s", out)) } // tests regression detailed in #13964 where DOCKER_TLS_VERIFY env is ignored diff --git a/integration-cli/docker_cli_run_test.go b/integration-cli/docker_cli_run_test.go index 64da7c2c29..dab5edf748 100644 --- a/integration-cli/docker_cli_run_test.go +++ b/integration-cli/docker_cli_run_test.go @@ -865,7 +865,7 @@ func (s *DockerSuite) TestRunDnsDefaultOptions(c *check.C) { // check that the actual defaults are appended to the commented out // localhost resolver (which should be preserved) // NOTE: if we ever change the defaults from google dns, this will break - expected := "#nameserver 127.0.2.1\n\nnameserver 8.8.8.8\nnameserver 8.8.4.4" + expected := "#nameserver 127.0.2.1\n\nnameserver 8.8.8.8\nnameserver 8.8.4.4\n" if actual != expected { c.Fatalf("expected resolv.conf be: %q, but was: %q", expected, actual) } @@ -880,7 +880,7 @@ func (s *DockerSuite) TestRunDnsOptions(c *check.C) { } actual := strings.Replace(strings.Trim(out, "\r\n"), "\n", " ", -1) - if actual != "nameserver 127.0.0.1 search mydomain" { + if actual != "search mydomain nameserver 127.0.0.1" { c.Fatalf("expected 'nameserver 127.0.0.1 search mydomain', but says: %q", actual) } @@ -1001,7 +1001,7 @@ func (s *DockerSuite) TestRunNonRootUserResolvName(c *check.C) { func (s *DockerSuite) TestRunResolvconfUpdate(c *check.C) { testRequires(c, SameHostDaemon) - tmpResolvConf := []byte("search pommesfrites.fr\nnameserver 12.34.56.78") + tmpResolvConf := []byte("search pommesfrites.fr\nnameserver 12.34.56.78\n") tmpLocalhostResolvConf := []byte("nameserver 127.0.0.1") //take a copy of resolv.conf for restoring after test completes @@ -1131,7 +1131,7 @@ func (s *DockerSuite) TestRunResolvconfUpdate(c *check.C) { c.Fatal(err) } - expected := "\nnameserver 8.8.8.8\nnameserver 8.8.4.4" + expected := "\nnameserver 8.8.8.8\nnameserver 8.8.4.4\n" if !bytes.Equal(containerResolv, []byte(expected)) { c.Fatalf("Container does not have cleaned/replaced DNS in resolv.conf; expected %q, got %q", expected, string(containerResolv)) }