plugin/k8s_extenral: Supports fallthrough option (#5959)

* Add fallthrough option to k8s_external plugin to allow transitioning control to the next plugin if the domain is not found
* Exit on start up if required plugin is not present.

Signed-off-by: vanceli <vanceli@tencent.com>

---------

Signed-off-by: vanceli <vanceli@tencent.com>
Co-authored-by: vanceli <vanceli@tencent.com>
This commit is contained in:
Vancl 2023-03-24 20:52:44 +08:00 committed by GitHub
parent 48c40ae1cd
commit 47dceabfc6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 66 additions and 22 deletions

View file

@ -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).

View file

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

View file

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

View file

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