diff --git a/libnetwork/endpoint.go b/libnetwork/endpoint.go index 822f88bd3e..8debc99ece 100644 --- a/libnetwork/endpoint.go +++ b/libnetwork/endpoint.go @@ -498,11 +498,14 @@ func (ep *endpoint) sbJoin(sb *sandbox, options ...EndpointOption) (err error) { } if doUpdateHostsFile(n, sb) { - address := "" - if ip := ep.getFirstInterfaceAddress(); ip != nil { - address = ip.String() + var addresses []string + if ip := ep.getFirstInterfaceIPv4Address(); ip != nil { + addresses = append(addresses, ip.String()) } - if err = sb.updateHostsFile(address); err != nil { + if ip := ep.getFirstInterfaceIPv6Address(); ip != nil { + addresses = append(addresses, ip.String()) + } + if err = sb.updateHostsFile(addresses); err != nil { return err } } @@ -912,7 +915,7 @@ func (ep *endpoint) getSandbox() (*sandbox, bool) { return ps, ok } -func (ep *endpoint) getFirstInterfaceAddress() net.IP { +func (ep *endpoint) getFirstInterfaceIPv4Address() net.IP { ep.Lock() defer ep.Unlock() @@ -923,6 +926,17 @@ func (ep *endpoint) getFirstInterfaceAddress() net.IP { return nil } +func (ep *endpoint) getFirstInterfaceIPv6Address() net.IP { + ep.Lock() + defer ep.Unlock() + + if ep.iface.addrv6 != nil { + return ep.iface.addrv6.IP + } + + return nil +} + // EndpointOptionGeneric function returns an option setter for a Generic option defined // in a Dictionary of Key-Value pair func EndpointOptionGeneric(generic map[string]interface{}) EndpointOption { diff --git a/libnetwork/endpoint_test.go b/libnetwork/endpoint_test.go new file mode 100644 index 0000000000..675c7a8c17 --- /dev/null +++ b/libnetwork/endpoint_test.go @@ -0,0 +1,76 @@ +// +build !windows + +package libnetwork + +import ( + "io/ioutil" + "os" + "testing" + + "github.com/docker/libnetwork/ipamapi" + "github.com/docker/libnetwork/osl" + "github.com/docker/libnetwork/testutils" +) + +func TestHostsEntries(t *testing.T) { + if !testutils.IsRunningInContainer() { + defer testutils.SetupTestOSContext(t)() + } + + expectedHostsFile := `127.0.0.1 localhost +::1 localhost ip6-localhost ip6-loopback +fe00::0 ip6-localnet +ff00::0 ip6-mcastprefix +ff02::1 ip6-allnodes +ff02::2 ip6-allrouters +192.168.222.2 somehost.example.com somehost +fe90::2 somehost.example.com somehost +` + + opts := []NetworkOption{NetworkOptionEnableIPv6(true), NetworkOptionIpam(ipamapi.DefaultIPAM, "", + []*IpamConf{{PreferredPool: "192.168.222.0/24", Gateway: "192.168.222.1"}}, + []*IpamConf{{PreferredPool: "fe90::/64", Gateway: "fe90::1"}}, + nil)} + + c, nws := getTestEnv(t, opts) + ctrlr := c.(*controller) + + hostsFile, err := ioutil.TempFile("", "") + if err != nil { + t.Fatal(err) + } + defer os.Remove(hostsFile.Name()) + + sbx, err := ctrlr.NewSandbox("sandbox1", OptionHostsPath(hostsFile.Name()), OptionHostname("somehost.example.com")) + if err != nil { + t.Fatal(err) + } + + ep1, err := nws[0].CreateEndpoint("ep1") + if err != nil { + t.Fatal(err) + } + + if err := ep1.Join(sbx, JoinOptionPriority(ep1, 1)); err != nil { + t.Fatal(err) + } + + data, err := ioutil.ReadFile(hostsFile.Name()) + if err != nil { + t.Fatal(err) + } + + if string(data) != expectedHostsFile { + t.Fatalf("expected the hosts file to read:\n%q\nbut instead got the following:\n%q\n", expectedHostsFile, string(data)) + } + + if err := sbx.Delete(); err != nil { + t.Fatal(err) + } + + if len(ctrlr.sandboxes) != 0 { + t.Fatalf("controller sandboxes is not empty. len = %d", len(ctrlr.sandboxes)) + } + + osl.GC() +} diff --git a/libnetwork/sandbox_dns_unix.go b/libnetwork/sandbox_dns_unix.go index f43b5d6035..08bf017326 100644 --- a/libnetwork/sandbox_dns_unix.go +++ b/libnetwork/sandbox_dns_unix.go @@ -98,8 +98,8 @@ func (sb *sandbox) buildHostsFile() error { return etchosts.Build(sb.config.hostsPath, "", sb.config.hostName, sb.config.domainName, extraContent) } -func (sb *sandbox) updateHostsFile(ifaceIP string) error { - if ifaceIP == "" { +func (sb *sandbox) updateHostsFile(ifaceIPs []string) error { + if ifaceIPs == nil || len(ifaceIPs) == 0 { return nil } @@ -120,7 +120,10 @@ func (sb *sandbox) updateHostsFile(ifaceIP string) error { mhost = fmt.Sprintf("%s %s", fqdn, parts[0]) } - extraContent := []etchosts.Record{{Hosts: mhost, IP: ifaceIP}} + var extraContent []etchosts.Record + for _, ip := range ifaceIPs { + extraContent = append(extraContent, etchosts.Record{Hosts: mhost, IP: ip}) + } sb.addHostsEntries(extraContent) return nil diff --git a/libnetwork/sandbox_dns_windows.go b/libnetwork/sandbox_dns_windows.go index e1ca73edef..d30bc7eabc 100644 --- a/libnetwork/sandbox_dns_windows.go +++ b/libnetwork/sandbox_dns_windows.go @@ -18,7 +18,7 @@ func (sb *sandbox) setupResolutionFiles() error { func (sb *sandbox) restorePath() { } -func (sb *sandbox) updateHostsFile(ifaceIP string) error { +func (sb *sandbox) updateHostsFile(ifaceIP []string) error { return nil }