diff --git a/plugin/k8s_external/README.md b/plugin/k8s_external/README.md index 1cf5eca71..893a13158 100644 --- a/plugin/k8s_external/README.md +++ b/plugin/k8s_external/README.md @@ -10,8 +10,7 @@ This plugin allows an additional zone to resolve the external IP address(es) of service and headless services. This plugin is only useful if the *kubernetes* plugin is also loaded. The plugin uses an external zone to resolve in-cluster IP addresses. It only handles queries for A, -AAAA, SRV, and PTR records; all others result in NODATA responses. To make it a proper DNS zone, it handles -SOA and NS queries for the apex of the zone. +AAAA, SRV, and PTR records; To make it a proper DNS zone, it handles SOA and NS queries for the apex of the zone. By default the apex of the zone will look like the following (assuming the zone used is `example.org`): @@ -67,6 +66,14 @@ k8s_external [ZONE...] { * if there is a headless service with external IPs set, external IPs will be resolved +If the queried domain does not exist, you can fall through to next plugin by adding the `fallthrough` option. + +~~~ +k8s_external [ZONE...] { + fallthrough [ZONE...] +} +~~~ + ## Examples Enable names under `example.org` to be resolved to in-cluster DNS addresses. @@ -106,6 +113,18 @@ zone transfers. Notifies are not supported. } ~~~ +With the `fallthrough` option, if the queried domain does not exist, it will be passed to the next plugin that matches the zone. + +~~~ +. { + kubernetes cluster.local + k8s_external example.org { + fallthrough + } + forward . 8.8.8.8 +} +~~~ + # See Also For some background see [resolve external IP address](https://github.com/kubernetes/dns/issues/242). diff --git a/plugin/k8s_external/external.go b/plugin/k8s_external/external.go index 2cbf88555..e56784ad7 100644 --- a/plugin/k8s_external/external.go +++ b/plugin/k8s_external/external.go @@ -16,6 +16,7 @@ import ( "github.com/coredns/coredns/plugin" "github.com/coredns/coredns/plugin/etcd/msg" + "github.com/coredns/coredns/plugin/pkg/fall" "github.com/coredns/coredns/plugin/pkg/upstream" "github.com/coredns/coredns/request" @@ -39,6 +40,7 @@ type Externaler interface { type External struct { Next plugin.Handler Zones []string + Fall fall.F hostmaster string apex string @@ -68,10 +70,6 @@ func (e *External) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Ms return plugin.NextOrFailure(e.Name(), e.Next, ctx, w, r) } - if e.externalFunc == nil { - return plugin.NextOrFailure(e.Name(), e.Next, ctx, w, r) - } - state.Zone = zone for _, z := range e.Zones { // TODO(miek): save this in the External struct. @@ -93,6 +91,10 @@ func (e *External) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Ms m.Authoritative = true if len(svc) == 0 { + if e.Fall.Through(state.Name()) && rcode == dns.RcodeNameError { + return plugin.NextOrFailure(e.Name(), e.Next, ctx, w, r) + } + m.Rcode = rcode m.Ns = []dns.RR{e.soa(state)} w.WriteMsg(m) diff --git a/plugin/k8s_external/setup.go b/plugin/k8s_external/setup.go index dbb1372e5..f42f7de23 100644 --- a/plugin/k8s_external/setup.go +++ b/plugin/k8s_external/setup.go @@ -1,6 +1,7 @@ package external import ( + "errors" "strconv" "github.com/coredns/caddy" @@ -9,7 +10,9 @@ import ( "github.com/coredns/coredns/plugin/pkg/upstream" ) -func init() { plugin.Register("k8s_external", setup) } +const pluginName = "k8s_external" + +func init() { plugin.Register(pluginName, setup) } func setup(c *caddy.Controller) error { e, err := parse(c) @@ -21,14 +24,18 @@ func setup(c *caddy.Controller) error { c.OnStartup(func() error { m := dnsserver.GetConfig(c).Handler("kubernetes") if m == nil { - return nil + return plugin.Error(pluginName, errors.New("kubernetes plugin not loaded")) } - if x, ok := m.(Externaler); ok { - e.externalFunc = x.External - e.externalAddrFunc = x.ExternalAddress - e.externalServicesFunc = x.ExternalServices - e.externalSerialFunc = x.ExternalSerial + + x, ok := m.(Externaler) + if !ok { + return plugin.Error(pluginName, errors.New("kubernetes plugin does not implement the Externaler interface")) } + + e.externalFunc = x.External + e.externalAddrFunc = x.ExternalAddress + e.externalServicesFunc = x.ExternalServices + e.externalSerialFunc = x.ExternalSerial return nil }) @@ -70,6 +77,8 @@ func parse(c *caddy.Controller) (*External, error) { e.apex = args[0] case "headless": e.headless = true + case "fallthrough": + e.Fall.SetZonesFromArgs(c.RemainingArgs()) default: return nil, c.Errf("unknown property '%s'", c.Val()) } diff --git a/plugin/k8s_external/setup_test.go b/plugin/k8s_external/setup_test.go index 351b35a4f..8814554ef 100644 --- a/plugin/k8s_external/setup_test.go +++ b/plugin/k8s_external/setup_test.go @@ -4,24 +4,33 @@ import ( "testing" "github.com/coredns/caddy" + "github.com/coredns/coredns/plugin/pkg/fall" ) func TestSetup(t *testing.T) { tests := []struct { - input string - shouldErr bool - expectedZone string - expectedApex string - expectedHeadless bool + input string + shouldErr bool + expectedZone string + expectedApex string + expectedHeadless bool + expectedFallthrough fall.F }{ - {`k8s_external`, false, "", "dns", false}, - {`k8s_external example.org`, false, "example.org.", "dns", false}, + {`k8s_external`, false, "", "dns", false, fall.Zero}, + {`k8s_external example.org`, false, "example.org.", "dns", false, fall.Zero}, {`k8s_external example.org { apex testdns -}`, false, "example.org.", "testdns", false}, +}`, false, "example.org.", "testdns", false, fall.Zero}, {`k8s_external example.org { headless -}`, false, "example.org.", "dns", true}, +}`, false, "example.org.", "dns", true, fall.Zero}, + {`k8s_external example.org { + fallthrough +}`, false, "example.org.", "dns", false, fall.Root}, + {`k8s_external example.org { + fallthrough ip6.arpa inaddr.arpa foo.com +}`, false, "example.org.", "dns", false, + fall.F{Zones: []string{"ip6.arpa.", "inaddr.arpa.", "foo.com."}}}, } for i, test := range tests { @@ -53,5 +62,10 @@ func TestSetup(t *testing.T) { t.Errorf("Test %d, expected headless %q for input %s, got: %v", i, test.expectedApex, test.input, e.headless) } } + if !test.shouldErr { + if !e.Fall.Equal(test.expectedFallthrough) { + t.Errorf("Test %d, expected to be initialized with fallthrough %q for input %s, got: %v", i, test.expectedFallthrough, test.input, e.Fall) + } + } } }