Merge pull request #334 from squeed/host-local-dns

ipam/host-local: add ResolvConf argument for DNS configuration
This commit is contained in:
Tom Denham 2017-01-12 17:06:04 -08:00 committed by GitHub
commit 530dd71e19
7 changed files with 169 additions and 7 deletions

View File

@ -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:

View File

@ -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
@ -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"
}
}
```

View File

@ -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:"-"`
}

View File

@ -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
}

View File

@ -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())
}

View File

@ -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",
@ -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",
@ -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)

View File

@ -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
@ -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()
}