Skip to content

Commit

Permalink
ipam/host-local: add ResolvConf argument for DNS configuration
Browse files Browse the repository at this point in the history
This adds the option `resolvConf` to the host-local IPAM configuration.
If specified, the plugin will try to parse the file as a resolv.conf(5)
type file and return it in the DNS response.
  • Loading branch information
squeed authored and Casey Callendrello committed Jan 11, 2017
1 parent 4406607 commit 5cde14c
Show file tree
Hide file tree
Showing 7 changed files with 169 additions and 7 deletions.
1 change: 1 addition & 0 deletions Documentation/host-local.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ It stores the state locally on the host filesystem, therefore ensuring uniquenes
* `rangeEnd` (string, optional): IP inside of "subnet" with which to end allocating addresses. Defaults to ".254" IP inside of the "subnet" block.
* `gateway` (string, optional): IP inside of "subnet" to designate as the gateway. Defaults to ".1" IP inside of the "subnet" block.
* `routes` (string, optional): list of routes to add to the container namespace. Each route is a dictionary with "dst" and optional "gw" fields. If "gw" is omitted, value of "gateway" will be used.
* `resolvConf` (string, optional): Path to a `resolv.conf` on the host to parse and return as the DNS configuration

## Supported arguments
The following [CNI_ARGS](https://github.com/containernetworking/cni/blob/master/SPEC.md#parameters) are supported:
Expand Down
6 changes: 4 additions & 2 deletions plugins/ipam/host-local/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# host-local IP address manager

host-local IPAM allocates IPv4 and IPv6 addresses out of a specified address range.
host-local IPAM allocates IPv4 and IPv6 addresses out of a specified address range. Optionally,
it can include a DNS configuration from a `resolv.conf` file on the host.

## Usage

Expand Down Expand Up @@ -65,7 +66,8 @@ f81d4fae-7dec-11d0-a765-00a0c91e6bf6
"rangeEnd": "3ffe:ffff:0:01ff::0020",
"routes": [
{ "dst": "3ffe:ffff:0:01ff::1/64" }
]
],
"resolvConf": "/etc/resolv.conf"
}
}
```
Expand Down
1 change: 1 addition & 0 deletions plugins/ipam/host-local/backend/allocator/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ type IPAMConfig struct {
Gateway net.IP `json:"gateway"`
Routes []types.Route `json:"routes"`
DataDir string `json:"dataDir"`
ResolvConf string `json:"resolvConf"`
Args *IPAMArgs `json:"-"`
}

Expand Down
64 changes: 64 additions & 0 deletions plugins/ipam/host-local/dns.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// Copyright 2016 CNI authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
"bufio"
"os"
"strings"

"github.com/containernetworking/cni/pkg/types"
)

// parseResolvConf parses an existing resolv.conf in to a DNS struct
func parseResolvConf(filename string) (*types.DNS, error) {
fp, err := os.Open(filename)
if err != nil {
return nil, err
}

dns := types.DNS{}
scanner := bufio.NewScanner(fp)
for scanner.Scan() {
line := scanner.Text()
line = strings.TrimSpace(line)

// Skip comments, empty lines
if len(line) == 0 || line[0] == '#' || line[0] == ';' {
continue
}

fields := strings.Fields(line)
if len(fields) < 2 {
continue
}
switch fields[0] {
case "nameserver":
dns.Nameservers = append(dns.Nameservers, fields[1])
case "domain":
dns.Domain = fields[1]
case "search":
dns.Search = append(dns.Search, fields[1:]...)
case "options":
dns.Options = append(dns.Options, fields[1:]...)
}
}

if err := scanner.Err(); err != nil {
return nil, err
}

return &dns, nil
}
80 changes: 80 additions & 0 deletions plugins/ipam/host-local/dns_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// Copyright 2016 CNI authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
"io/ioutil"
"os"

"github.com/containernetworking/cni/pkg/types"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)

var _ = Describe("parsing resolv.conf", func() {
It("parses a simple resolv.conf file", func() {
contents := `
nameserver 192.0.2.0
nameserver 192.0.2.1
`
dns, err := parse(contents)
Expect(err).NotTo(HaveOccurred())
Expect(*dns).Should(Equal(types.DNS{Nameservers: []string{"192.0.2.0", "192.0.2.1"}}))
})
It("ignores comments", func() {
dns, err := parse(`
nameserver 192.0.2.0
;nameserver 192.0.2.1
`)
Expect(err).NotTo(HaveOccurred())
Expect(*dns).Should(Equal(types.DNS{Nameservers: []string{"192.0.2.0"}}))
})
It("parses all fields", func() {
dns, err := parse(`
nameserver 192.0.2.0
nameserver 192.0.2.2
domain example.com
;nameserver comment
#nameserver comment
search example.net example.org
search example.gov
options one two three
options four
`)
Expect(err).NotTo(HaveOccurred())
Expect(*dns).Should(Equal(types.DNS{
Nameservers: []string{"192.0.2.0", "192.0.2.2"},
Domain: "example.com",
Search: []string{"example.net", "example.org", "example.gov"},
Options: []string{"one", "two", "three", "four"},
}))
})
})

func parse(contents string) (*types.DNS, error) {
f, err := ioutil.TempFile("", "host_local_resolv")
defer f.Close()
defer os.Remove(f.Name())

if err != nil {
return nil, err
}

if _, err := f.WriteString(contents); err != nil {
return nil, err
}

return parseResolvConf(f.Name())
}
10 changes: 8 additions & 2 deletions plugins/ipam/host-local/host_local_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ var _ = Describe("host-local Operations", func() {
Expect(err).NotTo(HaveOccurred())
defer os.RemoveAll(tmpDir)

err = ioutil.WriteFile(filepath.Join(tmpDir, "resolv.conf"), []byte("nameserver 192.0.2.3"), 0644)
Expect(err).NotTo(HaveOccurred())

conf := fmt.Sprintf(`{
"cniVersion": "0.2.0",
"name": "mynet",
Expand All @@ -46,9 +49,10 @@ var _ = Describe("host-local Operations", func() {
"ipam": {
"type": "host-local",
"subnet": "10.1.2.0/24",
"dataDir": "%s"
"dataDir": "%s",
"resolvConf": "%s/resolv.conf"
}
}`, tmpDir)
}`, tmpDir, tmpDir)

args := &skel.CmdArgs{
ContainerID: "dummy",
Expand Down Expand Up @@ -80,6 +84,8 @@ var _ = Describe("host-local Operations", func() {
Expect(err).NotTo(HaveOccurred())
Expect(string(contents)).To(Equal("10.1.2.2"))

Expect(result.DNS).To(Equal(types.DNS{Nameservers: []string{"192.0.2.3"}}))

// Release the IP
err = testutils.CmdDelWithResult(nspath, ifname, func() error {
return cmdDel(args)
Expand Down
14 changes: 11 additions & 3 deletions plugins/ipam/host-local/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,16 @@ func cmdAdd(args *skel.CmdArgs) error {
return err
}

r := types.Result{}

if ipamConf.ResolvConf != "" {
dns, err := parseResolvConf(ipamConf.ResolvConf)
if err != nil {
return err
}
r.DNS = *dns
}

store, err := disk.New(ipamConf.Name, ipamConf.DataDir)
if err != nil {
return err
Expand All @@ -48,10 +58,8 @@ func cmdAdd(args *skel.CmdArgs) error {
if err != nil {
return err
}
r.IP4 = ipConf

r := &types.Result{
IP4: ipConf,
}
return r.Print()
}

Expand Down

0 comments on commit 5cde14c

Please sign in to comment.