kubernetes: Add zone filtering to fallthrough (#1353)
* Add zone filtering to fallthrough * Doh. gofmt * Update documentation
This commit is contained in:
parent
75a8a17da4
commit
84ebbbc722
8 changed files with 95 additions and 44 deletions
|
@ -71,6 +71,9 @@ reverse cases and **all other** request are handled by the backing plugin. This
|
||||||
"fallthrough" does. To keep things explicit we've opted that plugins implement such behavior
|
"fallthrough" does. To keep things explicit we've opted that plugins implement such behavior
|
||||||
should implement a `fallthrough` keyword.
|
should implement a `fallthrough` keyword.
|
||||||
|
|
||||||
|
The `fallthrough` directive should optionally accept a list of zones. Only queries for records
|
||||||
|
in one of those zones should be allowed to fallthrough.
|
||||||
|
|
||||||
## Qualifying for main repo
|
## Qualifying for main repo
|
||||||
|
|
||||||
Plugins for CoreDNS can live out-of-tree, `plugin.cfg` defaults to CoreDNS' repo but other
|
Plugins for CoreDNS can live out-of-tree, `plugin.cfg` defaults to CoreDNS' repo but other
|
||||||
|
|
|
@ -38,7 +38,7 @@ kubernetes [ZONES...] {
|
||||||
endpoint_pod_names
|
endpoint_pod_names
|
||||||
upstream ADDRESS...
|
upstream ADDRESS...
|
||||||
ttl TTL
|
ttl TTL
|
||||||
fallthrough
|
fallthrough [ZONES...]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -85,9 +85,12 @@ kubernetes [ZONES...] {
|
||||||
to a file structured like resolv.conf.
|
to a file structured like resolv.conf.
|
||||||
* `ttl` allows you to set a custom TTL for responses. The default (and allowed minimum) is to use
|
* `ttl` allows you to set a custom TTL for responses. The default (and allowed minimum) is to use
|
||||||
5 seconds, the maximum is capped at 3600 seconds.
|
5 seconds, the maximum is capped at 3600 seconds.
|
||||||
* `fallthrough` If a query for a record in the cluster zone results in NXDOMAIN, normally that is
|
* `fallthrough` **[ZONES...]** If a query for a record in the zones for which the plugin is authoritative
|
||||||
what the response will be. However, if you specify this option, the query will instead be passed
|
results in NXDOMAIN, normally that is what the response will be. However, if you specify this option,
|
||||||
on down the plugin chain, which can include another plugin to handle the query.
|
the query will instead be passed on down the plugin chain, which can include another plugin to handle
|
||||||
|
the query. If **[ZONES...]** is omitted, then fallthrough happens for all zones for which the plugin
|
||||||
|
is authoritative. If specific zones are listed (for example `in-addr.arpa` and `ip6.arpa`), then only
|
||||||
|
queries for those zones will be subject to fallthrough.
|
||||||
|
|
||||||
## Health
|
## Health
|
||||||
|
|
||||||
|
|
|
@ -59,7 +59,7 @@ func (k Kubernetes) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.M
|
||||||
}
|
}
|
||||||
|
|
||||||
if k.IsNameError(err) {
|
if k.IsNameError(err) {
|
||||||
if k.Fallthrough {
|
if plugin.Fallthrough(k.Fallthrough, state.Name()) {
|
||||||
return plugin.NextOrFailure(k.Name(), k.Next, ctx, w, r)
|
return plugin.NextOrFailure(k.Name(), k.Next, ctx, w, r)
|
||||||
}
|
}
|
||||||
return plugin.BackendError(&k, zone, dns.RcodeNameError, state, nil /* err */, plugin.Options{})
|
return plugin.BackendError(&k, zone, dns.RcodeNameError, state, nil /* err */, plugin.Options{})
|
||||||
|
|
|
@ -40,7 +40,7 @@ type Kubernetes struct {
|
||||||
Namespaces map[string]bool
|
Namespaces map[string]bool
|
||||||
podMode string
|
podMode string
|
||||||
endpointNameMode bool
|
endpointNameMode bool
|
||||||
Fallthrough bool
|
Fallthrough *[]string // nil = disabled, empty = all zones, o/w zones
|
||||||
ttl uint32
|
ttl uint32
|
||||||
|
|
||||||
primaryZoneIndex int
|
primaryZoneIndex int
|
||||||
|
|
|
@ -172,12 +172,8 @@ func kubernetesParse(c *caddy.Controller) (*Kubernetes, dnsControlOpts, error) {
|
||||||
}
|
}
|
||||||
return nil, opts, c.ArgErr()
|
return nil, opts, c.ArgErr()
|
||||||
case "fallthrough":
|
case "fallthrough":
|
||||||
args := c.RemainingArgs()
|
zones := c.RemainingArgs()
|
||||||
if len(args) == 0 {
|
k8s.Fallthrough = &zones
|
||||||
k8s.Fallthrough = true
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
return nil, opts, c.ArgErr()
|
|
||||||
case "upstream":
|
case "upstream":
|
||||||
args := c.RemainingArgs()
|
args := c.RemainingArgs()
|
||||||
if len(args) == 0 {
|
if len(args) == 0 {
|
||||||
|
|
|
@ -19,7 +19,7 @@ func TestKubernetesParse(t *testing.T) {
|
||||||
expectedResyncPeriod time.Duration // expected resync period value
|
expectedResyncPeriod time.Duration // expected resync period value
|
||||||
expectedLabelSelector string // expected label selector value
|
expectedLabelSelector string // expected label selector value
|
||||||
expectedPodMode string
|
expectedPodMode string
|
||||||
expectedFallthrough bool
|
expectedFallthrough *[]string
|
||||||
expectedUpstreams []string
|
expectedUpstreams []string
|
||||||
}{
|
}{
|
||||||
// positive
|
// positive
|
||||||
|
@ -32,7 +32,7 @@ func TestKubernetesParse(t *testing.T) {
|
||||||
defaultResyncPeriod,
|
defaultResyncPeriod,
|
||||||
"",
|
"",
|
||||||
podModeDisabled,
|
podModeDisabled,
|
||||||
false,
|
nil,
|
||||||
nil,
|
nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -44,7 +44,7 @@ func TestKubernetesParse(t *testing.T) {
|
||||||
defaultResyncPeriod,
|
defaultResyncPeriod,
|
||||||
"",
|
"",
|
||||||
podModeDisabled,
|
podModeDisabled,
|
||||||
false,
|
nil,
|
||||||
nil,
|
nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -57,7 +57,7 @@ func TestKubernetesParse(t *testing.T) {
|
||||||
defaultResyncPeriod,
|
defaultResyncPeriod,
|
||||||
"",
|
"",
|
||||||
podModeDisabled,
|
podModeDisabled,
|
||||||
false,
|
nil,
|
||||||
nil,
|
nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -71,7 +71,7 @@ func TestKubernetesParse(t *testing.T) {
|
||||||
defaultResyncPeriod,
|
defaultResyncPeriod,
|
||||||
"",
|
"",
|
||||||
podModeDisabled,
|
podModeDisabled,
|
||||||
false,
|
nil,
|
||||||
nil,
|
nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -85,7 +85,7 @@ func TestKubernetesParse(t *testing.T) {
|
||||||
defaultResyncPeriod,
|
defaultResyncPeriod,
|
||||||
"",
|
"",
|
||||||
podModeDisabled,
|
podModeDisabled,
|
||||||
false,
|
nil,
|
||||||
nil,
|
nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -99,7 +99,7 @@ func TestKubernetesParse(t *testing.T) {
|
||||||
defaultResyncPeriod,
|
defaultResyncPeriod,
|
||||||
"",
|
"",
|
||||||
podModeDisabled,
|
podModeDisabled,
|
||||||
false,
|
nil,
|
||||||
nil,
|
nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -113,7 +113,7 @@ func TestKubernetesParse(t *testing.T) {
|
||||||
30 * time.Second,
|
30 * time.Second,
|
||||||
"",
|
"",
|
||||||
podModeDisabled,
|
podModeDisabled,
|
||||||
false,
|
nil,
|
||||||
nil,
|
nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -127,7 +127,7 @@ func TestKubernetesParse(t *testing.T) {
|
||||||
15 * time.Minute,
|
15 * time.Minute,
|
||||||
"",
|
"",
|
||||||
podModeDisabled,
|
podModeDisabled,
|
||||||
false,
|
nil,
|
||||||
nil,
|
nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -141,7 +141,7 @@ func TestKubernetesParse(t *testing.T) {
|
||||||
defaultResyncPeriod,
|
defaultResyncPeriod,
|
||||||
"environment=prod",
|
"environment=prod",
|
||||||
podModeDisabled,
|
podModeDisabled,
|
||||||
false,
|
nil,
|
||||||
nil,
|
nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -155,7 +155,7 @@ func TestKubernetesParse(t *testing.T) {
|
||||||
defaultResyncPeriod,
|
defaultResyncPeriod,
|
||||||
"application=nginx,environment in (production,qa,staging)",
|
"application=nginx,environment in (production,qa,staging)",
|
||||||
podModeDisabled,
|
podModeDisabled,
|
||||||
false,
|
nil,
|
||||||
nil,
|
nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -173,7 +173,7 @@ func TestKubernetesParse(t *testing.T) {
|
||||||
15 * time.Minute,
|
15 * time.Minute,
|
||||||
"application=nginx,environment in (production,qa,staging)",
|
"application=nginx,environment in (production,qa,staging)",
|
||||||
podModeDisabled,
|
podModeDisabled,
|
||||||
true,
|
&[]string{},
|
||||||
nil,
|
nil,
|
||||||
},
|
},
|
||||||
// negative
|
// negative
|
||||||
|
@ -188,7 +188,7 @@ func TestKubernetesParse(t *testing.T) {
|
||||||
defaultResyncPeriod,
|
defaultResyncPeriod,
|
||||||
"",
|
"",
|
||||||
podModeDisabled,
|
podModeDisabled,
|
||||||
false,
|
nil,
|
||||||
nil,
|
nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -202,7 +202,7 @@ func TestKubernetesParse(t *testing.T) {
|
||||||
defaultResyncPeriod,
|
defaultResyncPeriod,
|
||||||
"",
|
"",
|
||||||
podModeDisabled,
|
podModeDisabled,
|
||||||
false,
|
nil,
|
||||||
nil,
|
nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -216,7 +216,7 @@ func TestKubernetesParse(t *testing.T) {
|
||||||
0 * time.Minute,
|
0 * time.Minute,
|
||||||
"",
|
"",
|
||||||
podModeDisabled,
|
podModeDisabled,
|
||||||
false,
|
nil,
|
||||||
nil,
|
nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -230,7 +230,7 @@ func TestKubernetesParse(t *testing.T) {
|
||||||
0 * time.Second,
|
0 * time.Second,
|
||||||
"",
|
"",
|
||||||
podModeDisabled,
|
podModeDisabled,
|
||||||
false,
|
nil,
|
||||||
nil,
|
nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -244,7 +244,7 @@ func TestKubernetesParse(t *testing.T) {
|
||||||
0 * time.Second,
|
0 * time.Second,
|
||||||
"",
|
"",
|
||||||
podModeDisabled,
|
podModeDisabled,
|
||||||
false,
|
nil,
|
||||||
nil,
|
nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -258,7 +258,7 @@ func TestKubernetesParse(t *testing.T) {
|
||||||
0 * time.Second,
|
0 * time.Second,
|
||||||
"",
|
"",
|
||||||
podModeDisabled,
|
podModeDisabled,
|
||||||
false,
|
nil,
|
||||||
nil,
|
nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -272,7 +272,7 @@ func TestKubernetesParse(t *testing.T) {
|
||||||
0 * time.Second,
|
0 * time.Second,
|
||||||
"",
|
"",
|
||||||
podModeDisabled,
|
podModeDisabled,
|
||||||
false,
|
nil,
|
||||||
nil,
|
nil,
|
||||||
},
|
},
|
||||||
// pods disabled
|
// pods disabled
|
||||||
|
@ -287,7 +287,7 @@ func TestKubernetesParse(t *testing.T) {
|
||||||
defaultResyncPeriod,
|
defaultResyncPeriod,
|
||||||
"",
|
"",
|
||||||
podModeDisabled,
|
podModeDisabled,
|
||||||
false,
|
nil,
|
||||||
nil,
|
nil,
|
||||||
},
|
},
|
||||||
// pods insecure
|
// pods insecure
|
||||||
|
@ -302,7 +302,7 @@ func TestKubernetesParse(t *testing.T) {
|
||||||
defaultResyncPeriod,
|
defaultResyncPeriod,
|
||||||
"",
|
"",
|
||||||
podModeInsecure,
|
podModeInsecure,
|
||||||
false,
|
nil,
|
||||||
nil,
|
nil,
|
||||||
},
|
},
|
||||||
// pods verified
|
// pods verified
|
||||||
|
@ -317,7 +317,7 @@ func TestKubernetesParse(t *testing.T) {
|
||||||
defaultResyncPeriod,
|
defaultResyncPeriod,
|
||||||
"",
|
"",
|
||||||
podModeVerified,
|
podModeVerified,
|
||||||
false,
|
nil,
|
||||||
nil,
|
nil,
|
||||||
},
|
},
|
||||||
// pods invalid
|
// pods invalid
|
||||||
|
@ -332,22 +332,22 @@ func TestKubernetesParse(t *testing.T) {
|
||||||
defaultResyncPeriod,
|
defaultResyncPeriod,
|
||||||
"",
|
"",
|
||||||
podModeVerified,
|
podModeVerified,
|
||||||
false,
|
nil,
|
||||||
nil,
|
nil,
|
||||||
},
|
},
|
||||||
// fallthrough invalid
|
// fallthrough with zones
|
||||||
{
|
{
|
||||||
`kubernetes coredns.local {
|
`kubernetes coredns.local {
|
||||||
fallthrough junk
|
fallthrough ip6.arpa inaddr.arpa foo.com
|
||||||
}`,
|
}`,
|
||||||
true,
|
false,
|
||||||
"rong argument count",
|
"rong argument count",
|
||||||
-1,
|
1,
|
||||||
0,
|
0,
|
||||||
defaultResyncPeriod,
|
defaultResyncPeriod,
|
||||||
"",
|
"",
|
||||||
podModeDisabled,
|
podModeDisabled,
|
||||||
false,
|
&[]string{"ip6.arpa", "inaddr.arpa", "foo.com"},
|
||||||
nil,
|
nil,
|
||||||
},
|
},
|
||||||
// Valid upstream
|
// Valid upstream
|
||||||
|
@ -362,7 +362,7 @@ func TestKubernetesParse(t *testing.T) {
|
||||||
defaultResyncPeriod,
|
defaultResyncPeriod,
|
||||||
"",
|
"",
|
||||||
podModeDisabled,
|
podModeDisabled,
|
||||||
false,
|
nil,
|
||||||
[]string{"13.14.15.16:53"},
|
[]string{"13.14.15.16:53"},
|
||||||
},
|
},
|
||||||
// Invalid upstream
|
// Invalid upstream
|
||||||
|
@ -377,7 +377,7 @@ func TestKubernetesParse(t *testing.T) {
|
||||||
defaultResyncPeriod,
|
defaultResyncPeriod,
|
||||||
"",
|
"",
|
||||||
podModeDisabled,
|
podModeDisabled,
|
||||||
false,
|
nil,
|
||||||
nil,
|
nil,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -444,9 +444,23 @@ func TestKubernetesParse(t *testing.T) {
|
||||||
|
|
||||||
// fallthrough
|
// fallthrough
|
||||||
foundFallthrough := k8sController.Fallthrough
|
foundFallthrough := k8sController.Fallthrough
|
||||||
if foundFallthrough != test.expectedFallthrough {
|
if foundFallthrough != nil {
|
||||||
|
failed := false
|
||||||
|
if test.expectedFallthrough == nil {
|
||||||
|
failed = true
|
||||||
|
} else if len(*foundFallthrough) != len(*test.expectedFallthrough) {
|
||||||
|
failed = true
|
||||||
|
} else {
|
||||||
|
for i := range *foundFallthrough {
|
||||||
|
if (*foundFallthrough)[i] != (*test.expectedFallthrough)[i] {
|
||||||
|
failed = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if failed {
|
||||||
t.Errorf("Test %d: Expected kubernetes controller to be initialized with fallthrough '%v'. Instead found fallthrough '%v' for input '%s'", i, test.expectedFallthrough, foundFallthrough, test.input)
|
t.Errorf("Test %d: Expected kubernetes controller to be initialized with fallthrough '%v'. Instead found fallthrough '%v' for input '%s'", i, test.expectedFallthrough, foundFallthrough, test.input)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// upstream
|
// upstream
|
||||||
foundUpstreams := k8sController.Proxy.Upstreams
|
foundUpstreams := k8sController.Proxy.Upstreams
|
||||||
if test.expectedUpstreams == nil {
|
if test.expectedUpstreams == nil {
|
||||||
|
|
|
@ -83,6 +83,21 @@ func NextOrFailure(name string, next Handler, ctx context.Context, w dns.Respons
|
||||||
return dns.RcodeServerFailure, Error(name, errors.New("no next plugin found"))
|
return dns.RcodeServerFailure, Error(name, errors.New("no next plugin found"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fallthrough handles the fallthrough logic used in plugins that support it
|
||||||
|
func Fallthrough(ftzones *[]string, qname string) bool {
|
||||||
|
if ftzones == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if len(*ftzones) == 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
zone := Zones(*ftzones).Matches(qname)
|
||||||
|
if zone != "" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// ClientWrite returns true if the response has been written to the client.
|
// ClientWrite returns true if the response has been written to the client.
|
||||||
// Each plugin to adhire to this protocol.
|
// Each plugin to adhire to this protocol.
|
||||||
func ClientWrite(rcode int) bool {
|
func ClientWrite(rcode int) bool {
|
||||||
|
|
|
@ -1 +1,21 @@
|
||||||
package plugin
|
package plugin
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestFallthrough(t *testing.T) {
|
||||||
|
if Fallthrough(nil, "foo.com.") {
|
||||||
|
t.Errorf("Expected false, got true for nil fallthrough")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !Fallthrough(&[]string{}, "foo.net.") {
|
||||||
|
t.Errorf("Expected true, got false for all zone fallthrough")
|
||||||
|
}
|
||||||
|
|
||||||
|
if Fallthrough(&[]string{"foo.com", "bar.com"}, "foo.net") {
|
||||||
|
t.Errorf("Expected false, got true for non-matching fallthrough zone")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !Fallthrough(&[]string{"foo.com.", "bar.com."}, "bar.com.") {
|
||||||
|
t.Errorf("Expected true, got false for matching fallthrough zone")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue