Skip to content

Commit

Permalink
Add DNS 'options' support
Browse files Browse the repository at this point in the history
This is needed to expose DNS options like 'ndots' into containers.

moby/moby#14069

Signed-off-by: Tim Hockin <thockin@google.com>
  • Loading branch information
thockin committed Aug 28, 2015
1 parent b1992c8 commit 0d76052
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 9 deletions.
29 changes: 26 additions & 3 deletions resolvconf/resolvconf.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ var (
nsIPv6Regexp = regexp.MustCompile(`(?m)^nameserver\s+` + ipv6Address + `\s*\n*`)
nsRegexp = regexp.MustCompile(`^\s*nameserver\s*((` + ipv4Address + `)|(` + ipv6Address + `))\s*$`)
searchRegexp = regexp.MustCompile(`^\s*search\s*(([^\s]+\s*)*)$`)
optionsRegexp = regexp.MustCompile(`^\s*options\s*(([^\s]+\s*)*)$`)
)

var lastModified struct {
Expand Down Expand Up @@ -165,10 +166,25 @@ func GetSearchDomains(resolvConf []byte) []string {
return domains
}

// GetOptions returns options (if any) listed in /etc/resolv.conf
// If more than one options line is encountered, only the contents of the last
// one is returned.
func GetOptions(resolvConf []byte) []string {
options := []string{}
for _, line := range getLines(resolvConf, []byte("#")) {
match := optionsRegexp.FindSubmatch(line)
if match == nil {
continue
}
options = strings.Fields(string(match[1]))
}
return options
}

// Build writes a configuration file to path containing a "nameserver" entry
// for every element in dns, and a "search" entry for every element in
// dnsSearch.
func Build(path string, dns, dnsSearch []string) (string, error) {
// for every element in dns, a "search" entry for every element in
// dnsSearch, and an "options" entry for every element in dnsOptions.
func Build(path string, dns, dnsSearch, dnsOptions []string) (string, error) {
content := bytes.NewBuffer(nil)
if len(dnsSearch) > 0 {
if searchString := strings.Join(dnsSearch, " "); strings.Trim(searchString, " ") != "." {
Expand All @@ -182,6 +198,13 @@ func Build(path string, dns, dnsSearch []string) (string, error) {
return "", err
}
}
if len(dnsOptions) > 0 {
if optsString := strings.Join(dnsOptions, " "); strings.Trim(optsString, " ") != "" {
if _, err := content.WriteString("options " + optsString + "\n"); err != nil {
return "", err
}
}
}

hash, err := ioutils.HashData(bytes.NewReader(content.Bytes()))
if err != nil {
Expand Down
59 changes: 55 additions & 4 deletions resolvconf/resolvconf_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,32 @@ nameserver 4.30.20.100`: {"foo.example.com", "example.com"},
}
}

func TestGetOptions(t *testing.T) {
for resolv, result := range map[string][]string{
`options opt1`: {"opt1"},
`options opt1 # ignored`: {"opt1"},
` options opt1 `: {"opt1"},
` options opt1 # ignored`: {"opt1"},
`options opt1 opt2 opt3`: {"opt1", "opt2", "opt3"},
`options opt1 opt2 opt3 # ignored`: {"opt1", "opt2", "opt3"},
` options opt1 opt2 opt3 `: {"opt1", "opt2", "opt3"},
` options opt1 opt2 opt3 # ignored`: {"opt1", "opt2", "opt3"},
``: {},
`# ignored`: {},
`nameserver 1.2.3.4`: {},
`nameserver 1.2.3.4
options opt1 opt2 opt3`: {"opt1", "opt2", "opt3"},
`nameserver 1.2.3.4
options opt1 opt2
options opt3 opt4`: {"opt3", "opt4"},
} {
test := GetOptions([]byte(resolv))
if !strSlicesEqual(test, result) {
t.Fatalf("Wrong options string {%s} should be %v. Input: %s", test, result, resolv)
}
}
}

func strSlicesEqual(a, b []string) bool {
if len(a) != len(b) {
return false
Expand All @@ -119,7 +145,7 @@ func TestBuild(t *testing.T) {
}
defer os.Remove(file.Name())

_, err = Build(file.Name(), []string{"ns1", "ns2", "ns3"}, []string{"search1"})
_, err = Build(file.Name(), []string{"ns1", "ns2", "ns3"}, []string{"search1"}, []string{"opt1"})
if err != nil {
t.Fatal(err)
}
Expand All @@ -129,7 +155,7 @@ func TestBuild(t *testing.T) {
t.Fatal(err)
}

if expected := "search search1\nnameserver ns1\nnameserver ns2\nnameserver ns3\n"; !bytes.Contains(content, []byte(expected)) {
if expected := "search search1\nnameserver ns1\nnameserver ns2\nnameserver ns3\noptions opt1\n"; !bytes.Contains(content, []byte(expected)) {
t.Fatalf("Expected to find '%s' got '%s'", expected, content)
}
}
Expand All @@ -141,7 +167,7 @@ func TestBuildWithZeroLengthDomainSearch(t *testing.T) {
}
defer os.Remove(file.Name())

_, err = Build(file.Name(), []string{"ns1", "ns2", "ns3"}, []string{"."})
_, err = Build(file.Name(), []string{"ns1", "ns2", "ns3"}, []string{"."}, []string{"opt1"})
if err != nil {
t.Fatal(err)
}
Expand All @@ -151,7 +177,32 @@ func TestBuildWithZeroLengthDomainSearch(t *testing.T) {
t.Fatal(err)
}

if expected := "nameserver ns1\nnameserver ns2\nnameserver ns3\n"; !bytes.Contains(content, []byte(expected)) {
if expected := "nameserver ns1\nnameserver ns2\nnameserver ns3\noptions opt1\n"; !bytes.Contains(content, []byte(expected)) {
t.Fatalf("Expected to find '%s' got '%s'", expected, content)
}
if notExpected := "search ."; bytes.Contains(content, []byte(notExpected)) {
t.Fatalf("Expected to not find '%s' got '%s'", notExpected, content)
}
}

func TestBuildWithNoOptions(t *testing.T) {
file, err := ioutil.TempFile("", "")
if err != nil {
t.Fatal(err)
}
defer os.Remove(file.Name())

_, err = Build(file.Name(), []string{"ns1", "ns2", "ns3"}, []string{"search1"}, []string{})
if err != nil {
t.Fatal(err)
}

content, err := ioutil.ReadFile(file.Name())
if err != nil {
t.Fatal(err)
}

if expected := "search search1\nnameserver ns1\nnameserver ns2\nnameserver ns3\n"; !bytes.Contains(content, []byte(expected)) {
t.Fatalf("Expected to find '%s' got '%s'", expected, content)
}
if notExpected := "search ."; bytes.Contains(content, []byte(notExpected)) {
Expand Down
17 changes: 15 additions & 2 deletions sandbox.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ type resolvConfPathConfig struct {
resolvConfHashFile string
dnsList []string
dnsSearchList []string
dnsOptionsList []string
}

type containerConfig struct {
Expand Down Expand Up @@ -406,17 +407,21 @@ func (sb *sandbox) setupDNS() error {
}
dnsList := resolvconf.GetNameservers(resolvConf)
dnsSearchList := resolvconf.GetSearchDomains(resolvConf)
dnsOptionsList := resolvconf.GetOptions(resolvConf)

if len(sb.config.dnsList) > 0 || len(sb.config.dnsSearchList) > 0 {
if len(sb.config.dnsList) > 0 || len(sb.config.dnsSearchList) > 0 || len(dnsOptionsList) > 0 {
if len(sb.config.dnsList) > 0 {
dnsList = sb.config.dnsList
}
if len(sb.config.dnsSearchList) > 0 {
dnsSearchList = sb.config.dnsSearchList
}
if len(sb.config.dnsOptionsList) > 0 {
dnsOptionsList = sb.config.dnsOptionsList
}
}

hash, err := resolvconf.Build(sb.config.resolvConfPath, dnsList, dnsSearchList)
hash, err := resolvconf.Build(sb.config.resolvConfPath, dnsList, dnsSearchList, dnsOptionsList)
if err != nil {
return err
}
Expand Down Expand Up @@ -580,6 +585,14 @@ func OptionDNSSearch(search string) SandboxOption {
}
}

// OptionDNSOptions function returns an option setter for dns options entry option to
// be passed to container Create method.
func OptionDNSOptions(options string) SandboxOption {
return func(sb *sandbox) {
sb.config.dnsOptionsList = append(sb.config.dnsOptionsList, options)
}
}

// OptionUseDefaultSandbox function returns an option setter for using default sandbox to
// be passed to container Create method.
func OptionUseDefaultSandbox() SandboxOption {
Expand Down

0 comments on commit 0d76052

Please sign in to comment.