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:
parent
48c40ae1cd
commit
47dceabfc6
4 changed files with 66 additions and 22 deletions
|
@ -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.
|
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,
|
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
|
AAAA, SRV, and PTR records; To make it a proper DNS zone, it handles SOA and NS queries for the apex of the zone.
|
||||||
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`):
|
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 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
|
## Examples
|
||||||
|
|
||||||
Enable names under `example.org` to be resolved to in-cluster DNS addresses.
|
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
|
# See Also
|
||||||
|
|
||||||
For some background see [resolve external IP address](https://github.com/kubernetes/dns/issues/242).
|
For some background see [resolve external IP address](https://github.com/kubernetes/dns/issues/242).
|
||||||
|
|
|
@ -16,6 +16,7 @@ import (
|
||||||
|
|
||||||
"github.com/coredns/coredns/plugin"
|
"github.com/coredns/coredns/plugin"
|
||||||
"github.com/coredns/coredns/plugin/etcd/msg"
|
"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/plugin/pkg/upstream"
|
||||||
"github.com/coredns/coredns/request"
|
"github.com/coredns/coredns/request"
|
||||||
|
|
||||||
|
@ -39,6 +40,7 @@ type Externaler interface {
|
||||||
type External struct {
|
type External struct {
|
||||||
Next plugin.Handler
|
Next plugin.Handler
|
||||||
Zones []string
|
Zones []string
|
||||||
|
Fall fall.F
|
||||||
|
|
||||||
hostmaster string
|
hostmaster string
|
||||||
apex 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)
|
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
|
state.Zone = zone
|
||||||
for _, z := range e.Zones {
|
for _, z := range e.Zones {
|
||||||
// TODO(miek): save this in the External struct.
|
// 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
|
m.Authoritative = true
|
||||||
|
|
||||||
if len(svc) == 0 {
|
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.Rcode = rcode
|
||||||
m.Ns = []dns.RR{e.soa(state)}
|
m.Ns = []dns.RR{e.soa(state)}
|
||||||
w.WriteMsg(m)
|
w.WriteMsg(m)
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package external
|
package external
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/coredns/caddy"
|
"github.com/coredns/caddy"
|
||||||
|
@ -9,7 +10,9 @@ import (
|
||||||
"github.com/coredns/coredns/plugin/pkg/upstream"
|
"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 {
|
func setup(c *caddy.Controller) error {
|
||||||
e, err := parse(c)
|
e, err := parse(c)
|
||||||
|
@ -21,14 +24,18 @@ func setup(c *caddy.Controller) error {
|
||||||
c.OnStartup(func() error {
|
c.OnStartup(func() error {
|
||||||
m := dnsserver.GetConfig(c).Handler("kubernetes")
|
m := dnsserver.GetConfig(c).Handler("kubernetes")
|
||||||
if m == nil {
|
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
|
x, ok := m.(Externaler)
|
||||||
e.externalAddrFunc = x.ExternalAddress
|
if !ok {
|
||||||
e.externalServicesFunc = x.ExternalServices
|
return plugin.Error(pluginName, errors.New("kubernetes plugin does not implement the Externaler interface"))
|
||||||
e.externalSerialFunc = x.ExternalSerial
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
e.externalFunc = x.External
|
||||||
|
e.externalAddrFunc = x.ExternalAddress
|
||||||
|
e.externalServicesFunc = x.ExternalServices
|
||||||
|
e.externalSerialFunc = x.ExternalSerial
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -70,6 +77,8 @@ func parse(c *caddy.Controller) (*External, error) {
|
||||||
e.apex = args[0]
|
e.apex = args[0]
|
||||||
case "headless":
|
case "headless":
|
||||||
e.headless = true
|
e.headless = true
|
||||||
|
case "fallthrough":
|
||||||
|
e.Fall.SetZonesFromArgs(c.RemainingArgs())
|
||||||
default:
|
default:
|
||||||
return nil, c.Errf("unknown property '%s'", c.Val())
|
return nil, c.Errf("unknown property '%s'", c.Val())
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,24 +4,33 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/coredns/caddy"
|
"github.com/coredns/caddy"
|
||||||
|
"github.com/coredns/coredns/plugin/pkg/fall"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestSetup(t *testing.T) {
|
func TestSetup(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
input string
|
input string
|
||||||
shouldErr bool
|
shouldErr bool
|
||||||
expectedZone string
|
expectedZone string
|
||||||
expectedApex string
|
expectedApex string
|
||||||
expectedHeadless bool
|
expectedHeadless bool
|
||||||
|
expectedFallthrough fall.F
|
||||||
}{
|
}{
|
||||||
{`k8s_external`, false, "", "dns", false},
|
{`k8s_external`, false, "", "dns", false, fall.Zero},
|
||||||
{`k8s_external example.org`, false, "example.org.", "dns", false},
|
{`k8s_external example.org`, false, "example.org.", "dns", false, fall.Zero},
|
||||||
{`k8s_external example.org {
|
{`k8s_external example.org {
|
||||||
apex testdns
|
apex testdns
|
||||||
}`, false, "example.org.", "testdns", false},
|
}`, false, "example.org.", "testdns", false, fall.Zero},
|
||||||
{`k8s_external example.org {
|
{`k8s_external example.org {
|
||||||
headless
|
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 {
|
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)
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue