mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Fix DNS entry update issue
When an update is done to the container resolv.conf file and it was inheriting host entries, then we should not re-read the host entries when the container leaves and re-joins the endpoint. Signed-off-by: Jana Radhakrishnan <mrjana@docker.com>
This commit is contained in:
parent
4a3c7e1bb5
commit
d96e94897e
2 changed files with 130 additions and 10 deletions
|
@ -1,12 +1,15 @@
|
||||||
package libnetwork
|
package libnetwork
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/Sirupsen/logrus"
|
"github.com/Sirupsen/logrus"
|
||||||
|
"github.com/docker/docker/pkg/ioutils"
|
||||||
"github.com/docker/libnetwork/driverapi"
|
"github.com/docker/libnetwork/driverapi"
|
||||||
"github.com/docker/libnetwork/netutils"
|
"github.com/docker/libnetwork/netutils"
|
||||||
"github.com/docker/libnetwork/pkg/etchosts"
|
"github.com/docker/libnetwork/pkg/etchosts"
|
||||||
|
@ -513,7 +516,7 @@ func (ep *endpoint) updateParentHosts() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ep *endpoint) setupDNS() error {
|
func (ep *endpoint) updateDNS(resolvConf []byte) error {
|
||||||
ep.Lock()
|
ep.Lock()
|
||||||
container := ep.container
|
container := ep.container
|
||||||
network := ep.network
|
network := ep.network
|
||||||
|
@ -523,6 +526,77 @@ func (ep *endpoint) setupDNS() error {
|
||||||
return ErrNoContainer
|
return ErrNoContainer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hashFile := container.config.resolvConfPath + ".hash"
|
||||||
|
oldHash, err := ioutil.ReadFile(hashFile)
|
||||||
|
if err != nil {
|
||||||
|
if !os.IsNotExist(err) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
oldHash = []byte{}
|
||||||
|
}
|
||||||
|
|
||||||
|
resolvBytes, err := ioutil.ReadFile(container.config.resolvConfPath)
|
||||||
|
if err != nil {
|
||||||
|
if !os.IsNotExist(err) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
curHash, err := ioutils.HashData(bytes.NewReader(resolvBytes))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if string(oldHash) != "" && curHash != string(oldHash) {
|
||||||
|
// Seems the user has changed the container resolv.conf since the last time
|
||||||
|
// we checked so return without doing anything.
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// replace any localhost/127.* and remove IPv6 nameservers if IPv6 disabled.
|
||||||
|
resolvConf, _ = resolvconf.FilterResolvDNS(resolvConf, network.enableIPv6)
|
||||||
|
|
||||||
|
newHash, err := ioutils.HashData(bytes.NewReader(resolvConf))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// for atomic updates to these files, use temporary files with os.Rename:
|
||||||
|
dir := path.Dir(container.config.resolvConfPath)
|
||||||
|
tmpHashFile, err := ioutil.TempFile(dir, "hash")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
tmpResolvFile, err := ioutil.TempFile(dir, "resolv")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// write the updates to the temp files
|
||||||
|
if err = ioutil.WriteFile(tmpHashFile.Name(), []byte(newHash), 0644); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err = ioutil.WriteFile(tmpResolvFile.Name(), resolvConf, 0644); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// rename the temp files for atomic replace
|
||||||
|
if err = os.Rename(tmpHashFile.Name(), hashFile); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return os.Rename(tmpResolvFile.Name(), container.config.resolvConfPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ep *endpoint) setupDNS() error {
|
||||||
|
ep.Lock()
|
||||||
|
container := ep.container
|
||||||
|
ep.Unlock()
|
||||||
|
|
||||||
|
if container == nil {
|
||||||
|
return ErrNoContainer
|
||||||
|
}
|
||||||
|
|
||||||
if container.config.resolvConfPath == "" {
|
if container.config.resolvConfPath == "" {
|
||||||
container.config.resolvConfPath = defaultPrefix + "/" + container.id + "/resolv.conf"
|
container.config.resolvConfPath = defaultPrefix + "/" + container.id + "/resolv.conf"
|
||||||
}
|
}
|
||||||
|
@ -556,9 +630,7 @@ func (ep *endpoint) setupDNS() error {
|
||||||
return resolvconf.Build(container.config.resolvConfPath, dnsList, dnsSearchList)
|
return resolvconf.Build(container.config.resolvConfPath, dnsList, dnsSearchList)
|
||||||
}
|
}
|
||||||
|
|
||||||
// replace any localhost/127.* but always discard IPv6 entries for now.
|
return ep.updateDNS(resolvConf)
|
||||||
resolvConf, _ = resolvconf.FilterResolvDNS(resolvConf, network.enableIPv6)
|
|
||||||
return ioutil.WriteFile(ep.container.config.resolvConfPath, resolvConf, 0644)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// EndpointOptionGeneric function returns an option setter for a Generic option defined
|
// EndpointOptionGeneric function returns an option setter for a Generic option defined
|
||||||
|
|
|
@ -1017,13 +1017,17 @@ func TestEnableIPv6(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNoEnableIPv6(t *testing.T) {
|
func TestResolvConf(t *testing.T) {
|
||||||
if !netutils.IsRunningInContainer() {
|
if !netutils.IsRunningInContainer() {
|
||||||
defer netutils.SetupTestNetNS(t)()
|
defer netutils.SetupTestNetNS(t)()
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpResolvConf := []byte("search pommesfrites.fr\nnameserver 12.34.56.78\nnameserver 2001:4860:4860::8888")
|
tmpResolvConf1 := []byte("search pommesfrites.fr\nnameserver 12.34.56.78\nnameserver 2001:4860:4860::8888")
|
||||||
expectedResolvConf := []byte("search pommesfrites.fr\nnameserver 12.34.56.78\n")
|
expectedResolvConf1 := []byte("search pommesfrites.fr\nnameserver 12.34.56.78\n")
|
||||||
|
tmpResolvConf2 := []byte("search pommesfrites.fr\nnameserver 112.34.56.78\nnameserver 2001:4860:4860::8888")
|
||||||
|
expectedResolvConf2 := []byte("search pommesfrites.fr\nnameserver 112.34.56.78\n")
|
||||||
|
tmpResolvConf3 := []byte("search pommesfrites.fr\nnameserver 113.34.56.78\n")
|
||||||
|
|
||||||
//take a copy of resolv.conf for restoring after test completes
|
//take a copy of resolv.conf for restoring after test completes
|
||||||
resolvConfSystem, err := ioutil.ReadFile("/etc/resolv.conf")
|
resolvConfSystem, err := ioutil.ReadFile("/etc/resolv.conf")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -1046,7 +1050,7 @@ func TestNoEnableIPv6(t *testing.T) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := ioutil.WriteFile("/etc/resolv.conf", tmpResolvConf, 0644); err != nil {
|
if err := ioutil.WriteFile("/etc/resolv.conf", tmpResolvConf1, 0644); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1069,13 +1073,57 @@ func TestNoEnableIPv6(t *testing.T) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !bytes.Equal(content, expectedResolvConf) {
|
if !bytes.Equal(content, expectedResolvConf1) {
|
||||||
t.Fatalf("Expected %s, Got %s", string(expectedResolvConf), string(content))
|
t.Fatalf("Expected %s, Got %s", string(expectedResolvConf1), string(content))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = ep1.Leave(containerID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := ioutil.WriteFile("/etc/resolv.conf", tmpResolvConf2, 0644); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = ep1.Join(containerID,
|
||||||
|
libnetwork.JoinOptionResolvConfPath(resolvConfPath))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
content, err = ioutil.ReadFile(resolvConfPath)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !bytes.Equal(content, expectedResolvConf2) {
|
||||||
|
t.Fatalf("Expected %s, Got %s", string(expectedResolvConf2), string(content))
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := ioutil.WriteFile(resolvConfPath, tmpResolvConf3, 0644); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = ep1.Leave(containerID)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = ep1.Join(containerID,
|
||||||
|
libnetwork.JoinOptionResolvConfPath(resolvConfPath))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
content, err = ioutil.ReadFile(resolvConfPath)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !bytes.Equal(content, tmpResolvConf3) {
|
||||||
|
t.Fatalf("Expected %s, Got %s", string(tmpResolvConf3), string(content))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
Loading…
Add table
Reference in a new issue