mirror of
				https://github.com/coredns/coredns.git
				synced 2025-10-29 01:04:15 -04:00 
			
		
		
		
	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:
		| @@ -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) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user