aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--middleware/kubernetes/README.md52
-rw-r--r--middleware/kubernetes/handler.go109
-rw-r--r--middleware/kubernetes/kubernetes.go71
-rw-r--r--middleware/kubernetes/kubernetes_test.go24
-rw-r--r--middleware/kubernetes/setup.go50
-rw-r--r--middleware/kubernetes/setup_test.go152
6 files changed, 407 insertions, 51 deletions
diff --git a/middleware/kubernetes/README.md b/middleware/kubernetes/README.md
index f0d47a1aa..0f2c18b1f 100644
--- a/middleware/kubernetes/README.md
+++ b/middleware/kubernetes/README.md
@@ -121,6 +121,58 @@ kubernetes coredns.local {
# Each line consists of the name of the federation, and the domain.
federation myfed foo.example.com
+ # autopath [NDOTS [RESPONSE [RESOLV-CONF]]
+ #
+ # Enables server side search path lookups for pods. When enabled, coredns
+ # will identify search path queries from pods and perform the remaining
+ # lookups in the path on the pod's behalf. The search path used mimics the
+ # resolv.conf search path deployed to pods. E.g.
+ #
+ # search ns1.svc.cluster.local svc.cluster.local cluster.local foo.com
+ #
+ # If no domains in the path produce an answer, a lookup on the bare question
+ # will be attempted.
+ #
+ # A successful response will contain a question section with the original
+ # question, and an answer section containing the record for the question that
+ # actually had an answer. This means that the question and answer will not
+ # match. For example:
+ #
+ # # host -v -t a google.com
+ # Trying "google.com.default.svc.cluster.local"
+ # ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 50957
+ # ;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0
+ #
+ # ;; QUESTION SECTION:
+ # ;google.com.default.svc.cluster.local. IN A
+ #
+ # ;; ANSWER SECTION:
+ # google.com. 175 IN A 216.58.194.206
+ #
+ #
+ # NDOTS (default: 0) This provides an adjustable threshold to
+ # prevent server side lookups from triggering. If the number of dots before
+ # the first search domain is less than this number, then the search path will
+ # not executed on the server side.
+ #
+ # RESPONSE (default: SERVFAIL) RESPONSE can be either NXDOMAIN, SERVFAIL or
+ # NOERROR. This option causes coredns to return the given response instead of
+ # NXDOMAIN when the all searches in the path produce no results. Setting this
+ # to SERVFAIL or NOERROR should prevent the client from fruitlessly continuing
+ # the client side searches in the path after the server already checked them.
+ #
+ # RESOLV-CONF (default: /etc/resolv.conf) If specified, coredns uses this
+ # file to get the host's search domains. CoreDNS performs a lookup on these
+ # domains if the in-cluster search domains in the path fail to produce an
+ # answer. If not specified, the values will be read from the local resolv.conf
+ # file (i.e the resolv.conf file in the pod containing coredns).
+ #
+ # Enabling autopath causes coredns to use more memory since it needs to
+ # maintain a watch on all pods. If autopath and "pods verified" mode are
+ # both enabled, they will share the same watch. I.e. enabling both options
+ # should have an equivalent memory impact of just one.
+ autopath 0 SERVFAIL /etc/resolv.conf
+
# fallthrough
#
# If a query for a record in the cluster zone results in NXDOMAIN,
diff --git a/middleware/kubernetes/handler.go b/middleware/kubernetes/handler.go
index ec1184198..f1a247923 100644
--- a/middleware/kubernetes/handler.go
+++ b/middleware/kubernetes/handler.go
@@ -2,9 +2,11 @@ package kubernetes
import (
"errors"
+ "strings"
"github.com/coredns/coredns/middleware"
"github.com/coredns/coredns/middleware/pkg/dnsutil"
+ "github.com/coredns/coredns/middleware/rewrite"
"github.com/coredns/coredns/request"
"github.com/miekg/dns"
@@ -39,37 +41,55 @@ func (k Kubernetes) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.M
zone = state.Name()
}
- var (
- records, extra []dns.RR
- err error
- )
- switch state.Type() {
- case "A":
- records, _, err = middleware.A(&k, zone, state, nil, middleware.Options{})
- case "AAAA":
- records, _, err = middleware.AAAA(&k, zone, state, nil, middleware.Options{})
- case "TXT":
- records, _, err = middleware.TXT(&k, zone, state, middleware.Options{})
- case "CNAME":
- records, _, err = middleware.CNAME(&k, zone, state, middleware.Options{})
- case "PTR":
- records, _, err = middleware.PTR(&k, zone, state, middleware.Options{})
- case "MX":
- records, extra, _, err = middleware.MX(&k, zone, state, middleware.Options{})
- case "SRV":
- records, extra, _, err = middleware.SRV(&k, zone, state, middleware.Options{})
- case "SOA":
- records, _, err = middleware.SOA(&k, zone, state, middleware.Options{})
- case "NS":
- if state.Name() == zone {
- records, extra, _, err = middleware.NS(&k, zone, state, middleware.Options{})
- break
+ records, extra, _, err := k.routeRequest(zone, state)
+
+ if k.AutoPath.Enabled && k.IsNameError(err) {
+ p := k.findPodWithIP(state.IP())
+ for p != nil {
+ name, path, ok := splitSearch(zone, state.QName(), p.Namespace)
+ if !ok {
+ break
+ }
+ if (dns.CountLabel(name) - 1) < k.AutoPath.NDots {
+ break
+ }
+ // Search "svc.cluster.local" and "cluster.local"
+ for i := 0; i < 2; i++ {
+ path = strings.Join(dns.SplitDomainName(path)[1:], ".")
+ state = state.NewWithQuestion(strings.Join([]string{name, path}, "."), state.QType())
+ records, extra, _, err = k.routeRequest(zone, state)
+ if !k.IsNameError(err) {
+ break
+ }
+ }
+ if !k.IsNameError(err) {
+ break
+ }
+ // Fallthrough with the host search path (if set)
+ wr := rewrite.NewResponseReverter(w, r)
+ for _, hostsearch := range k.AutoPath.HostSearchPath {
+ r = state.NewWithQuestion(strings.Join([]string{name, hostsearch}, "."), state.QType()).Req
+ rcode, nextErr := middleware.NextOrFailure(k.Name(), k.Next, ctx, wr, r)
+ if rcode == dns.RcodeSuccess {
+ return rcode, nextErr
+ }
+ }
+ // Search . in this middleware
+ state = state.NewWithQuestion(strings.Join([]string{name, "."}, ""), state.QType())
+ records, extra, _, err = k.routeRequest(zone, state)
+ if !k.IsNameError(err) {
+ break
+ }
+ // Search . in the next middleware
+ r = state.Req
+ rcode, nextErr := middleware.NextOrFailure(k.Name(), k.Next, ctx, wr, r)
+ if rcode == dns.RcodeNameError {
+ rcode = k.AutoPath.OnNXDOMAIN
+ }
+ return rcode, nextErr
}
- fallthrough
- default:
- // Do a fake A lookup, so we can distinguish between NODATA and NXDOMAIN
- _, _, err = middleware.A(&k, zone, state, nil, middleware.Options{})
}
+
if k.IsNameError(err) {
if k.Fallthrough {
return middleware.NextOrFailure(k.Name(), k.Next, ctx, w, r)
@@ -95,5 +115,36 @@ func (k Kubernetes) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.M
return dns.RcodeSuccess, nil
}
+func (k *Kubernetes) routeRequest(zone string, state request.Request) (records []dns.RR, extra []dns.RR, debug []dns.RR, err error) {
+ switch state.Type() {
+ case "A":
+ records, _, err = middleware.A(k, zone, state, nil, middleware.Options{})
+ case "AAAA":
+ records, _, err = middleware.AAAA(k, zone, state, nil, middleware.Options{})
+ case "TXT":
+ records, _, err = middleware.TXT(k, zone, state, middleware.Options{})
+ case "CNAME":
+ records, _, err = middleware.CNAME(k, zone, state, middleware.Options{})
+ case "PTR":
+ records, _, err = middleware.PTR(k, zone, state, middleware.Options{})
+ case "MX":
+ records, extra, _, err = middleware.MX(k, zone, state, middleware.Options{})
+ case "SRV":
+ records, extra, _, err = middleware.SRV(k, zone, state, middleware.Options{})
+ case "SOA":
+ records, _, err = middleware.SOA(k, zone, state, middleware.Options{})
+ case "NS":
+ if state.Name() == zone {
+ records, extra, _, err = middleware.NS(k, zone, state, middleware.Options{})
+ break
+ }
+ fallthrough
+ default:
+ // Do a fake A lookup, so we can distinguish between NODATA and NXDOMAIN
+ _, _, err = middleware.A(k, zone, state, nil, middleware.Options{})
+ }
+ return records, extra, nil, err
+}
+
// Name implements the Handler interface.
func (k Kubernetes) Name() string { return "kubernetes" }
diff --git a/middleware/kubernetes/kubernetes.go b/middleware/kubernetes/kubernetes.go
index d4a255935..a1e6bdb73 100644
--- a/middleware/kubernetes/kubernetes.go
+++ b/middleware/kubernetes/kubernetes.go
@@ -28,26 +28,35 @@ import (
// Kubernetes implements a middleware that connects to a Kubernetes cluster.
type Kubernetes struct {
- Next middleware.Handler
- Zones []string
- primaryZone int
- Proxy proxy.Proxy // Proxy for looking up names during the resolution process
- APIEndpoint string
- APICertAuth string
- APIClientCert string
- APIClientKey string
- APIConn dnsController
- ResyncPeriod time.Duration
- Namespaces []string
- Federations []Federation
- LabelSelector *unversionedapi.LabelSelector
- Selector *labels.Selector
- PodMode string
- ReverseCidrs []net.IPNet
- Fallthrough bool
+ Next middleware.Handler
+ Zones []string
+ primaryZone int
+ Proxy proxy.Proxy // Proxy for looking up names during the resolution process
+ APIEndpoint string
+ APICertAuth string
+ APIClientCert string
+ APIClientKey string
+ APIConn dnsController
+ ResyncPeriod time.Duration
+ Namespaces []string
+ Federations []Federation
+ LabelSelector *unversionedapi.LabelSelector
+ Selector *labels.Selector
+ PodMode string
+ ReverseCidrs []net.IPNet
+ Fallthrough bool
+ AutoPath
interfaceAddrs interfaceAddrser
}
+type AutoPath struct {
+ Enabled bool
+ NDots int
+ ResolvConfFile string
+ HostSearchPath []string
+ OnNXDOMAIN int
+}
+
const (
// PodModeDisabled is the default value where pod requests are ignored
PodModeDisabled = "disabled"
@@ -97,6 +106,7 @@ var errInvalidRequest = errors.New("invalid query name")
var errZoneNotFound = errors.New("zone not found")
var errAPIBadPodType = errors.New("expected type *api.Pod")
var errPodsDisabled = errors.New("pod records disabled")
+var errResolvConfReadErr = errors.New("resolv.conf read error")
// Services implements the ServiceBackend interface.
func (k *Kubernetes) Services(state request.Request, exact bool, opt middleware.Options) (svcs []msg.Service, debug []msg.Service, err error) {
@@ -183,7 +193,7 @@ func (k *Kubernetes) Lookup(state request.Request, name string, typ uint16) (*dn
// IsNameError implements the ServiceBackend interface.
func (k *Kubernetes) IsNameError(err error) bool {
- return err == errNoItems || err == errNsNotExposed || err == errInvalidRequest
+ return err == errNoItems || err == errNsNotExposed || err == errInvalidRequest || err == errZoneNotFound
}
// Debug implements the ServiceBackend interface.
@@ -245,7 +255,7 @@ func (k *Kubernetes) InitKubeCache() (err error) {
}
opts := dnsControlOpts{
- initPodCache: k.PodMode == PodModeVerified,
+ initPodCache: (k.PodMode == PodModeVerified || k.AutoPath.Enabled),
}
k.APIConn = newdnsController(kubeClient, k.ResyncPeriod, k.Selector, opts)
@@ -448,6 +458,21 @@ func ipFromPodName(podname string) string {
return strings.Replace(podname, "-", ":", -1)
}
+func (k *Kubernetes) findPodWithIP(ip string) (p *api.Pod) {
+ if k.PodMode != PodModeVerified {
+ return nil
+ }
+ objList := k.APIConn.PodIndex(ip)
+ for _, o := range objList {
+ p, ok := o.(*api.Pod)
+ if !ok {
+ return nil
+ }
+ return p
+ }
+ return nil
+}
+
func (k *Kubernetes) findPods(namespace, podname string) (pods []pod, err error) {
if k.PodMode == PodModeDisabled {
return pods, errPodsDisabled
@@ -634,3 +659,11 @@ func (k *Kubernetes) localPodIP() net.IP {
}
return nil
}
+
+func splitSearch(zone, question, namespace string) (name, search string, ok bool) {
+ search = strings.Join([]string{namespace, "svc", zone}, ".")
+ if dns.IsSubDomain(search, question) {
+ return question[:len(question)-len(search)-1], search, true
+ }
+ return "", "", false
+}
diff --git a/middleware/kubernetes/kubernetes_test.go b/middleware/kubernetes/kubernetes_test.go
index 2003513a4..8449101a5 100644
--- a/middleware/kubernetes/kubernetes_test.go
+++ b/middleware/kubernetes/kubernetes_test.go
@@ -480,3 +480,27 @@ func TestServices(t *testing.T) {
}
}
+
+func TestSplitSearchPath(t *testing.T) {
+ type testCase struct {
+ question string
+ namespace string
+ expectedName string
+ expectedSearch string
+ expectedOk bool
+ }
+ tests := []testCase{
+ {question: "test.blah.com", namespace: "ns1", expectedName: "", expectedSearch: "", expectedOk: false},
+ {question: "foo.com.ns2.svc.interwebs.nets", namespace: "ns1", expectedName: "", expectedSearch: "", expectedOk: false},
+ {question: "foo.com.svc.interwebs.nets", namespace: "ns1", expectedName: "", expectedSearch: "", expectedOk: false},
+ {question: "foo.com.ns1.svc.interwebs.nets", namespace: "ns1", expectedName: "foo.com", expectedSearch: "ns1.svc.interwebs.nets", expectedOk: true},
+ }
+ zone := "interwebs.nets"
+ for _, c := range tests {
+ name, search, ok := splitSearch(zone, c.question, c.namespace)
+ if c.expectedName != name || c.expectedSearch != search || c.expectedOk != ok {
+ t.Errorf("Case %v: Expected name'%v', search:'%v', ok:'%v'. Got name:'%v', search:'%v', ok:'%v'.", c.question, c.expectedName, c.expectedSearch, c.expectedOk, name, search, ok)
+ }
+ }
+
+}
diff --git a/middleware/kubernetes/setup.go b/middleware/kubernetes/setup.go
index 1f8a1f0e3..7e9060da4 100644
--- a/middleware/kubernetes/setup.go
+++ b/middleware/kubernetes/setup.go
@@ -4,6 +4,7 @@ import (
"errors"
"fmt"
"net"
+ "strconv"
"strings"
"time"
@@ -11,6 +12,7 @@ import (
"github.com/coredns/coredns/middleware"
"github.com/coredns/coredns/middleware/pkg/dnsutil"
"github.com/coredns/coredns/middleware/proxy"
+ "github.com/miekg/dns"
"github.com/mholt/caddy"
unversionedapi "k8s.io/client-go/1.5/pkg/api/unversioned"
@@ -187,7 +189,48 @@ func kubernetesParse(c *caddy.Controller) (*Kubernetes, error) {
continue
}
return nil, fmt.Errorf("incorrect number of arguments for federation, got %v, expected 2", len(args))
+ case "autopath": // name zone
+ args := c.RemainingArgs()
+ k8s.AutoPath = AutoPath{
+ NDots: defautNdots,
+ HostSearchPath: []string{},
+ ResolvConfFile: defaultResolvConfFile,
+ OnNXDOMAIN: defaultOnNXDOMAIN,
+ }
+ if len(args) > 3 {
+ return nil, fmt.Errorf("incorrect number of arguments for autopath, got %v, expected at most 3", len(args))
+ }
+ if len(args) > 0 {
+ ndots, err := strconv.Atoi(args[0])
+ if err != nil {
+ return nil, fmt.Errorf("invalid NDOTS argument for autopath, got '%v', expected an integer", ndots)
+ }
+ k8s.AutoPath.NDots = ndots
+ }
+ if len(args) > 1 {
+ switch args[1] {
+ case dns.RcodeToString[dns.RcodeNameError]:
+ k8s.AutoPath.OnNXDOMAIN = dns.RcodeNameError
+ case dns.RcodeToString[dns.RcodeSuccess]:
+ k8s.AutoPath.OnNXDOMAIN = dns.RcodeSuccess
+ case dns.RcodeToString[dns.RcodeServerFailure]:
+ k8s.AutoPath.OnNXDOMAIN = dns.RcodeServerFailure
+ default:
+ return nil, fmt.Errorf("invalid RESPONSE argument for autopath, got '%v', expected SERVFAIL, NOERROR, or NXDOMAIN", args[1])
+ }
+ }
+ if len(args) > 2 {
+ k8s.AutoPath.ResolvConfFile = args[2]
+ }
+ rc, err := dns.ClientConfigFromFile(k8s.AutoPath.ResolvConfFile)
+ if err != nil {
+ return nil, fmt.Errorf("error when parsing %v: %v", k8s.AutoPath.ResolvConfFile, err)
+ }
+ k8s.AutoPath.HostSearchPath = rc.Search
+ middleware.Zones(k8s.AutoPath.HostSearchPath).Normalize()
+ k8s.AutoPath.Enabled = true
+ continue
}
}
return k8s, nil
@@ -197,6 +240,9 @@ func kubernetesParse(c *caddy.Controller) (*Kubernetes, error) {
}
const (
- defaultResyncPeriod = 5 * time.Minute
- defaultPodMode = PodModeDisabled
+ defaultResyncPeriod = 5 * time.Minute
+ defaultPodMode = PodModeDisabled
+ defautNdots = 0
+ defaultResolvConfFile = "/etc/resolv.conf"
+ defaultOnNXDOMAIN = dns.RcodeServerFailure
)
diff --git a/middleware/kubernetes/setup_test.go b/middleware/kubernetes/setup_test.go
index 8414d76fd..c8e84a724 100644
--- a/middleware/kubernetes/setup_test.go
+++ b/middleware/kubernetes/setup_test.go
@@ -2,11 +2,16 @@ package kubernetes
import (
"net"
+ "os"
+ "reflect"
"strings"
"testing"
"time"
+ "github.com/coredns/coredns/middleware/test"
+
"github.com/mholt/caddy"
+ "github.com/miekg/dns"
unversionedapi "k8s.io/client-go/1.5/pkg/api/unversioned"
)
@@ -16,6 +21,13 @@ func parseCidr(cidr string) net.IPNet {
}
func TestKubernetesParse(t *testing.T) {
+ f, rm, err := test.TempFile(os.TempDir(), testResolveConf)
+ autoPathResolvConfFile := f
+ if err != nil {
+ t.Fatalf("Could not create resolv.conf TempFile: %s", err)
+ }
+ defer rm()
+
tests := []struct {
description string // Human-facing description of test case
input string // Corefile data as string
@@ -30,6 +42,7 @@ func TestKubernetesParse(t *testing.T) {
expectedFallthrough bool
expectedUpstreams []string
expectedFederations []Federation
+ expectedAutoPath AutoPath
}{
// positive
{
@@ -46,6 +59,7 @@ func TestKubernetesParse(t *testing.T) {
false,
nil,
[]Federation{},
+ AutoPath{},
},
{
"kubernetes keyword with multiple zones",
@@ -61,6 +75,7 @@ func TestKubernetesParse(t *testing.T) {
false,
nil,
[]Federation{},
+ AutoPath{},
},
{
"kubernetes keyword with zone and empty braces",
@@ -77,6 +92,7 @@ func TestKubernetesParse(t *testing.T) {
false,
nil,
[]Federation{},
+ AutoPath{},
},
{
"endpoint keyword with url",
@@ -94,6 +110,7 @@ func TestKubernetesParse(t *testing.T) {
false,
nil,
[]Federation{},
+ AutoPath{},
},
{
"namespaces keyword with one namespace",
@@ -111,6 +128,7 @@ func TestKubernetesParse(t *testing.T) {
false,
nil,
nil,
+ AutoPath{},
},
{
"namespaces keyword with multiple namespaces",
@@ -128,6 +146,7 @@ func TestKubernetesParse(t *testing.T) {
false,
nil,
[]Federation{},
+ AutoPath{},
},
{
"resync period in seconds",
@@ -145,6 +164,7 @@ func TestKubernetesParse(t *testing.T) {
false,
nil,
[]Federation{},
+ AutoPath{},
},
{
"resync period in minutes",
@@ -162,6 +182,7 @@ func TestKubernetesParse(t *testing.T) {
false,
nil,
[]Federation{},
+ AutoPath{},
},
{
"basic label selector",
@@ -179,6 +200,7 @@ func TestKubernetesParse(t *testing.T) {
false,
nil,
[]Federation{},
+ AutoPath{},
},
{
"multi-label selector",
@@ -196,6 +218,7 @@ func TestKubernetesParse(t *testing.T) {
false,
nil,
[]Federation{},
+ AutoPath{},
},
{
"fully specified valid config",
@@ -217,6 +240,7 @@ func TestKubernetesParse(t *testing.T) {
true,
nil,
[]Federation{},
+ AutoPath{},
},
// negative
{
@@ -233,6 +257,7 @@ func TestKubernetesParse(t *testing.T) {
false,
nil,
[]Federation{},
+ AutoPath{},
},
{
"kubernetes keyword without a zone",
@@ -248,6 +273,7 @@ func TestKubernetesParse(t *testing.T) {
false,
nil,
[]Federation{},
+ AutoPath{},
},
{
"endpoint keyword without an endpoint value",
@@ -265,6 +291,7 @@ func TestKubernetesParse(t *testing.T) {
false,
nil,
[]Federation{},
+ AutoPath{},
},
{
"namespace keyword without a namespace value",
@@ -282,6 +309,7 @@ func TestKubernetesParse(t *testing.T) {
false,
nil,
[]Federation{},
+ AutoPath{},
},
{
"resyncperiod keyword without a duration value",
@@ -299,6 +327,7 @@ func TestKubernetesParse(t *testing.T) {
false,
nil,
[]Federation{},
+ AutoPath{},
},
{
"resync period no units",
@@ -316,6 +345,7 @@ func TestKubernetesParse(t *testing.T) {
false,
nil,
[]Federation{},
+ AutoPath{},
},
{
"resync period invalid",
@@ -333,6 +363,7 @@ func TestKubernetesParse(t *testing.T) {
false,
nil,
[]Federation{},
+ AutoPath{},
},
{
"labels with no selector value",
@@ -350,6 +381,7 @@ func TestKubernetesParse(t *testing.T) {
false,
nil,
[]Federation{},
+ AutoPath{},
},
{
"labels with invalid selector value",
@@ -367,6 +399,7 @@ func TestKubernetesParse(t *testing.T) {
false,
nil,
[]Federation{},
+ AutoPath{},
},
// pods disabled
{
@@ -385,6 +418,7 @@ func TestKubernetesParse(t *testing.T) {
false,
nil,
[]Federation{},
+ AutoPath{},
},
// pods insecure
{
@@ -403,6 +437,7 @@ func TestKubernetesParse(t *testing.T) {
false,
nil,
[]Federation{},
+ AutoPath{},
},
// pods verified
{
@@ -421,6 +456,7 @@ func TestKubernetesParse(t *testing.T) {
false,
nil,
[]Federation{},
+ AutoPath{},
},
// pods invalid
{
@@ -439,6 +475,7 @@ func TestKubernetesParse(t *testing.T) {
false,
nil,
[]Federation{},
+ AutoPath{},
},
// cidrs ok
{
@@ -457,6 +494,7 @@ func TestKubernetesParse(t *testing.T) {
false,
nil,
[]Federation{},
+ AutoPath{},
},
// cidrs ok
{
@@ -475,6 +513,7 @@ func TestKubernetesParse(t *testing.T) {
false,
nil,
[]Federation{},
+ AutoPath{},
},
// fallthrough invalid
{
@@ -493,6 +532,7 @@ func TestKubernetesParse(t *testing.T) {
false,
nil,
[]Federation{},
+ AutoPath{},
},
// Valid upstream
{
@@ -511,6 +551,7 @@ func TestKubernetesParse(t *testing.T) {
false,
[]string{"13.14.15.16:53"},
[]Federation{},
+ AutoPath{},
},
// Invalid upstream
{
@@ -529,6 +570,7 @@ func TestKubernetesParse(t *testing.T) {
false,
nil,
[]Federation{},
+ AutoPath{},
},
// Valid federations
{
@@ -551,6 +593,7 @@ func TestKubernetesParse(t *testing.T) {
{name: "foo", zone: "bar.crawl.com"},
{name: "fed", zone: "era.tion.com"},
},
+ AutoPath{},
},
// Invalid federations
{
@@ -569,6 +612,104 @@ func TestKubernetesParse(t *testing.T) {
false,
nil,
[]Federation{},
+ AutoPath{},
+ },
+ // autopath
+ {
+ "valid autopath",
+ `kubernetes coredns.local {
+ autopath 1 NXDOMAIN ` + autoPathResolvConfFile + `
+}`,
+ false,
+ "",
+ 1,
+ 0,
+ defaultResyncPeriod,
+ "",
+ defaultPodMode,
+ nil,
+ false,
+ nil,
+ nil,
+ AutoPath{
+ Enabled: true,
+ NDots: 1,
+ HostSearchPath: []string{"bar.com.", "baz.com."},
+ ResolvConfFile: autoPathResolvConfFile,
+ OnNXDOMAIN: dns.RcodeNameError,
+ },
+ },
+ {
+ "invalid autopath RESPONSE",
+ `kubernetes coredns.local {
+ autopath 0 CRY
+}`,
+ true,
+ "invalid RESPONSE argument for autopath",
+ -1,
+ 0,
+ defaultResyncPeriod,
+ "",
+ defaultPodMode,
+ nil,
+ false,
+ nil,
+ nil,
+ AutoPath{},
+ },
+ {
+ "invalid autopath NDOTS",
+ `kubernetes coredns.local {
+ autopath polka
+}`,
+ true,
+ "invalid NDOTS argument for autopath",
+ -1,
+ 0,
+ defaultResyncPeriod,
+ "",
+ defaultPodMode,
+ nil,
+ false,
+ nil,
+ nil,
+ AutoPath{},
+ },
+ {
+ "invalid autopath RESOLV-CONF",
+ `kubernetes coredns.local {
+ autopath 1 NOERROR /wrong/path/to/resolv.conf
+}`,
+ true,
+ "error when parsing",
+ -1,
+ 0,
+ defaultResyncPeriod,
+ "",
+ defaultPodMode,
+ nil,
+ false,
+ nil,
+ nil,
+ AutoPath{},
+ },
+ {
+ "invalid autopath invalid option",
+ `kubernetes coredns.local {
+ autopath 1 SERVFAIL ` + autoPathResolvConfFile + ` foo
+}`,
+ true,
+ "incorrect number of arguments",
+ -1,
+ 0,
+ defaultResyncPeriod,
+ "",
+ defaultPodMode,
+ nil,
+ false,
+ nil,
+ nil,
+ AutoPath{},
},
}
@@ -669,6 +810,15 @@ func TestKubernetesParse(t *testing.T) {
}
}
-
+ // autopath
+ if !reflect.DeepEqual(test.expectedAutoPath, k8sController.AutoPath) {
+ t.Errorf("Test %d: Expected kubernetes controller to be initialized with autopath '%v'. Instead found autopath '%v' for input '%s'", i, test.expectedAutoPath, k8sController.AutoPath, test.input)
+ }
}
}
+
+const testResolveConf = `nameserver 1.2.3.4
+domain foo.com
+search bar.com baz.com
+options ndots:5
+`