mirror of
				https://github.com/coredns/coredns.git
				synced 2025-10-30 09:43:17 -04:00 
			
		
		
		
	Add fallthrough support for Kubernetes (#626)
* Add fallthrough support for Kubernetes This enables registering other services in the same zone as Kubernetes services. This also re-orders the middleware chain so that Kubernetes comes before other types, in order to make this work out-of-the-box. * Remove extra line
This commit is contained in:
		| @@ -26,11 +26,11 @@ var directives = []string{ | ||||
| 	"loadbalance", | ||||
| 	"dnssec", | ||||
| 	"reverse", | ||||
| 	"kubernetes", | ||||
| 	"file", | ||||
| 	"auto", | ||||
| 	"secondary", | ||||
| 	"etcd", | ||||
| 	"kubernetes", | ||||
| 	"proxy", | ||||
| 	"whoami", | ||||
| 	"erratic", | ||||
|   | ||||
| @@ -34,11 +34,11 @@ | ||||
| 120:loadbalance:loadbalance | ||||
| 130:dnssec:dnssec | ||||
| 140:reverse:reverse | ||||
| 150:file:file | ||||
| 160:auto:auto | ||||
| 170:secondary:secondary | ||||
| 180:etcd:etcd | ||||
| 190:kubernetes:kubernetes | ||||
| 150:kubernetes:kubernetes | ||||
| 160:file:file | ||||
| 170:auto:auto | ||||
| 180:secondary:secondary | ||||
| 190:etcd:etcd | ||||
| 200:proxy:proxy | ||||
| 210:whoami:whoami | ||||
| 220:erratic:erratic | ||||
|   | ||||
| @@ -71,6 +71,9 @@ func (k Kubernetes) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.M | ||||
| 		_, _, err = middleware.A(&k, zone, state, nil, middleware.Options{}) | ||||
| 	} | ||||
| 	if k.IsNameError(err) { | ||||
| 		if k.Fallthrough { | ||||
| 			return middleware.NextOrFailure(k.Name(), k.Next, ctx, w, r) | ||||
| 		} | ||||
| 		// Make err nil when returning here, so we don't log spam for NXDOMAIN. | ||||
| 		return middleware.BackendError(&k, zone, dns.RcodeNameError, state, nil /*debug*/, nil /* err */, middleware.Options{}) | ||||
| 	} | ||||
|   | ||||
| @@ -43,6 +43,7 @@ type Kubernetes struct { | ||||
| 	Selector      *labels.Selector | ||||
| 	PodMode       string | ||||
| 	ReverseCidrs  []net.IPNet | ||||
| 	Fallthrough   bool | ||||
| } | ||||
|  | ||||
| const ( | ||||
|   | ||||
| @@ -155,6 +155,13 @@ func kubernetesParse(c *caddy.Controller) (*Kubernetes, error) { | ||||
| 						continue | ||||
| 					} | ||||
| 					return nil, c.ArgErr() | ||||
| 				case "fallthrough": | ||||
| 					args := c.RemainingArgs() | ||||
| 					if len(args) == 0 { | ||||
| 						k8s.Fallthrough = true | ||||
| 						continue | ||||
| 					} | ||||
| 					return nil, c.ArgErr() | ||||
| 				} | ||||
| 			} | ||||
| 			return k8s, nil | ||||
|   | ||||
| @@ -27,6 +27,7 @@ func TestKubernetesParse(t *testing.T) { | ||||
| 		expectedLabelSelector string        // expected label selector value | ||||
| 		expectedPodMode       string | ||||
| 		expectedCidrs         []net.IPNet | ||||
| 		expectedFallthrough   bool | ||||
| 	}{ | ||||
| 		// positive | ||||
| 		{ | ||||
| @@ -40,6 +41,7 @@ func TestKubernetesParse(t *testing.T) { | ||||
| 			"", | ||||
| 			defaultPodMode, | ||||
| 			nil, | ||||
| 			false, | ||||
| 		}, | ||||
| 		{ | ||||
| 			"kubernetes keyword with multiple zones", | ||||
| @@ -52,6 +54,7 @@ func TestKubernetesParse(t *testing.T) { | ||||
| 			"", | ||||
| 			defaultPodMode, | ||||
| 			nil, | ||||
| 			false, | ||||
| 		}, | ||||
| 		{ | ||||
| 			"kubernetes keyword with zone and empty braces", | ||||
| @@ -65,6 +68,7 @@ func TestKubernetesParse(t *testing.T) { | ||||
| 			"", | ||||
| 			defaultPodMode, | ||||
| 			nil, | ||||
| 			false, | ||||
| 		}, | ||||
| 		{ | ||||
| 			"endpoint keyword with url", | ||||
| @@ -79,6 +83,7 @@ func TestKubernetesParse(t *testing.T) { | ||||
| 			"", | ||||
| 			defaultPodMode, | ||||
| 			nil, | ||||
| 			false, | ||||
| 		}, | ||||
| 		{ | ||||
| 			"namespaces keyword with one namespace", | ||||
| @@ -93,6 +98,7 @@ func TestKubernetesParse(t *testing.T) { | ||||
| 			"", | ||||
| 			defaultPodMode, | ||||
| 			nil, | ||||
| 			false, | ||||
| 		}, | ||||
| 		{ | ||||
| 			"namespaces keyword with multiple namespaces", | ||||
| @@ -107,6 +113,7 @@ func TestKubernetesParse(t *testing.T) { | ||||
| 			"", | ||||
| 			defaultPodMode, | ||||
| 			nil, | ||||
| 			false, | ||||
| 		}, | ||||
| 		{ | ||||
| 			"resync period in seconds", | ||||
| @@ -121,6 +128,7 @@ func TestKubernetesParse(t *testing.T) { | ||||
| 			"", | ||||
| 			defaultPodMode, | ||||
| 			nil, | ||||
| 			false, | ||||
| 		}, | ||||
| 		{ | ||||
| 			"resync period in minutes", | ||||
| @@ -135,6 +143,7 @@ func TestKubernetesParse(t *testing.T) { | ||||
| 			"", | ||||
| 			defaultPodMode, | ||||
| 			nil, | ||||
| 			false, | ||||
| 		}, | ||||
| 		{ | ||||
| 			"basic label selector", | ||||
| @@ -149,6 +158,7 @@ func TestKubernetesParse(t *testing.T) { | ||||
| 			"environment=prod", | ||||
| 			defaultPodMode, | ||||
| 			nil, | ||||
| 			false, | ||||
| 		}, | ||||
| 		{ | ||||
| 			"multi-label selector", | ||||
| @@ -163,6 +173,7 @@ func TestKubernetesParse(t *testing.T) { | ||||
| 			"application=nginx,environment in (production,qa,staging)", | ||||
| 			defaultPodMode, | ||||
| 			nil, | ||||
| 			false, | ||||
| 		}, | ||||
| 		{ | ||||
| 			"fully specified valid config", | ||||
| @@ -171,6 +182,7 @@ func TestKubernetesParse(t *testing.T) { | ||||
| 	endpoint http://localhost:8080 | ||||
| 	namespaces demo test | ||||
|     labels environment in (production, staging, qa),application=nginx | ||||
|     fallthrough | ||||
| }`, | ||||
| 			false, | ||||
| 			"", | ||||
| @@ -180,6 +192,7 @@ func TestKubernetesParse(t *testing.T) { | ||||
| 			"application=nginx,environment in (production,qa,staging)", | ||||
| 			defaultPodMode, | ||||
| 			nil, | ||||
| 			true, | ||||
| 		}, | ||||
| 		// negative | ||||
| 		{ | ||||
| @@ -193,6 +206,7 @@ func TestKubernetesParse(t *testing.T) { | ||||
| 			"", | ||||
| 			defaultPodMode, | ||||
| 			nil, | ||||
| 			false, | ||||
| 		}, | ||||
| 		{ | ||||
| 			"kubernetes keyword without a zone", | ||||
| @@ -205,6 +219,7 @@ func TestKubernetesParse(t *testing.T) { | ||||
| 			"", | ||||
| 			defaultPodMode, | ||||
| 			nil, | ||||
| 			false, | ||||
| 		}, | ||||
| 		{ | ||||
| 			"endpoint keyword without an endpoint value", | ||||
| @@ -219,6 +234,7 @@ func TestKubernetesParse(t *testing.T) { | ||||
| 			"", | ||||
| 			defaultPodMode, | ||||
| 			nil, | ||||
| 			false, | ||||
| 		}, | ||||
| 		{ | ||||
| 			"namespace keyword without a namespace value", | ||||
| @@ -233,6 +249,7 @@ func TestKubernetesParse(t *testing.T) { | ||||
| 			"", | ||||
| 			defaultPodMode, | ||||
| 			nil, | ||||
| 			false, | ||||
| 		}, | ||||
| 		{ | ||||
| 			"resyncperiod keyword without a duration value", | ||||
| @@ -247,6 +264,7 @@ func TestKubernetesParse(t *testing.T) { | ||||
| 			"", | ||||
| 			defaultPodMode, | ||||
| 			nil, | ||||
| 			false, | ||||
| 		}, | ||||
| 		{ | ||||
| 			"resync period no units", | ||||
| @@ -261,6 +279,7 @@ func TestKubernetesParse(t *testing.T) { | ||||
| 			"", | ||||
| 			defaultPodMode, | ||||
| 			nil, | ||||
| 			false, | ||||
| 		}, | ||||
| 		{ | ||||
| 			"resync period invalid", | ||||
| @@ -275,6 +294,7 @@ func TestKubernetesParse(t *testing.T) { | ||||
| 			"", | ||||
| 			defaultPodMode, | ||||
| 			nil, | ||||
| 			false, | ||||
| 		}, | ||||
| 		{ | ||||
| 			"labels with no selector value", | ||||
| @@ -289,6 +309,7 @@ func TestKubernetesParse(t *testing.T) { | ||||
| 			"", | ||||
| 			defaultPodMode, | ||||
| 			nil, | ||||
| 			false, | ||||
| 		}, | ||||
| 		{ | ||||
| 			"labels with invalid selector value", | ||||
| @@ -303,6 +324,7 @@ func TestKubernetesParse(t *testing.T) { | ||||
| 			"", | ||||
| 			defaultPodMode, | ||||
| 			nil, | ||||
| 			false, | ||||
| 		}, | ||||
| 		// pods disabled | ||||
| 		{ | ||||
| @@ -318,6 +340,7 @@ func TestKubernetesParse(t *testing.T) { | ||||
| 			"", | ||||
| 			PodModeDisabled, | ||||
| 			nil, | ||||
| 			false, | ||||
| 		}, | ||||
| 		// pods insecure | ||||
| 		{ | ||||
| @@ -333,6 +356,7 @@ func TestKubernetesParse(t *testing.T) { | ||||
| 			"", | ||||
| 			PodModeInsecure, | ||||
| 			nil, | ||||
| 			false, | ||||
| 		}, | ||||
| 		// pods verified | ||||
| 		{ | ||||
| @@ -348,6 +372,7 @@ func TestKubernetesParse(t *testing.T) { | ||||
| 			"", | ||||
| 			PodModeVerified, | ||||
| 			nil, | ||||
| 			false, | ||||
| 		}, | ||||
| 		// pods invalid | ||||
| 		{ | ||||
| @@ -363,6 +388,7 @@ func TestKubernetesParse(t *testing.T) { | ||||
| 			"", | ||||
| 			PodModeVerified, | ||||
| 			nil, | ||||
| 			false, | ||||
| 		}, | ||||
| 		// cidrs ok | ||||
| 		{ | ||||
| @@ -378,6 +404,7 @@ func TestKubernetesParse(t *testing.T) { | ||||
| 			"", | ||||
| 			defaultPodMode, | ||||
| 			[]net.IPNet{parseCidr("10.0.0.0/24"), parseCidr("10.0.1.0/24")}, | ||||
| 			false, | ||||
| 		}, | ||||
| 		// cidrs ok | ||||
| 		{ | ||||
| @@ -393,6 +420,23 @@ func TestKubernetesParse(t *testing.T) { | ||||
| 			"", | ||||
| 			defaultPodMode, | ||||
| 			nil, | ||||
| 			false, | ||||
| 		}, | ||||
| 		// fallthrough invalid | ||||
| 		{ | ||||
| 			"Extra params for fallthrough", | ||||
| 			`kubernetes coredns.local { | ||||
| 	fallthrough junk | ||||
| }`, | ||||
| 			true, | ||||
| 			"Wrong argument count", | ||||
| 			-1, | ||||
| 			0, | ||||
| 			defaultResyncPeriod, | ||||
| 			"", | ||||
| 			defaultPodMode, | ||||
| 			nil, | ||||
| 			false, | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| @@ -466,6 +510,11 @@ func TestKubernetesParse(t *testing.T) { | ||||
| 				t.Errorf("Test %d: Expected kubernetes controller to be initialized with cidr '%s'. Instead found cidr '%s' for input '%s'", i, test.expectedCidrs[j].String(), foundCidrs[j].String(), test.input) | ||||
| 			} | ||||
| 		} | ||||
| 		// fallthrough | ||||
| 		foundFallthrough := k8sController.Fallthrough | ||||
| 		if foundFallthrough != test.expectedFallthrough { | ||||
| 			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) | ||||
| 		} | ||||
|  | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -3,6 +3,7 @@ | ||||
| package test | ||||
|  | ||||
| import ( | ||||
| 	"os" | ||||
| 	"testing" | ||||
| 	"time" | ||||
|  | ||||
| @@ -372,6 +373,23 @@ var dnsTestCasesAllNSExposed = []test.Case{ | ||||
| 	}, | ||||
| } | ||||
|  | ||||
| var dnsTestCasesFallthrough = []test.Case{ | ||||
| 	{ | ||||
| 		Qname: "f.b.svc.cluster.local.", Qtype: dns.TypeA, | ||||
| 		Rcode: dns.RcodeSuccess, | ||||
| 		Answer: []dns.RR{ | ||||
| 			test.A("f.b.svc.cluster.local.      303    IN      A       10.10.10.11"), | ||||
| 		}, | ||||
| 	}, | ||||
| 	{ | ||||
| 		Qname: "foo.cluster.local.", Qtype: dns.TypeA, | ||||
| 		Rcode: dns.RcodeSuccess, | ||||
| 		Answer: []dns.RR{ | ||||
| 			test.A("foo.cluster.local.      303    IN      A       10.10.10.10"), | ||||
| 		}, | ||||
| 	}, | ||||
| } | ||||
|  | ||||
| func createTestServer(t *testing.T, corefile string) (*caddy.Instance, string) { | ||||
| 	server, err := CoreDNSServer(corefile) | ||||
| 	if err != nil { | ||||
| @@ -424,9 +442,6 @@ func TestKubernetesIntegration(t *testing.T) { | ||||
| 		`.:0 { | ||||
|     kubernetes cluster.local 0.0.10.in-addr.arpa { | ||||
|                 endpoint http://localhost:8080 | ||||
| 		#endpoint https://kubernetes/  | ||||
| 		#tls admin.pem admin-key.pem ca.pem | ||||
| 		#tls k8s_auth/client2.crt k8s_auth/client2.key k8s_auth/ca2.crt | ||||
| 		namespaces test-1 | ||||
| 		pods disabled | ||||
|     } | ||||
| @@ -501,3 +516,41 @@ func TestKubernetesIntegrationAllNSExposed(t *testing.T) { | ||||
| ` | ||||
| 	doIntegrationTests(t, corefile, dnsTestCasesAllNSExposed) | ||||
| } | ||||
|  | ||||
| func TestKubernetesIntegrationFallthrough(t *testing.T) { | ||||
| 	dbfile, rmFunc, err := TempFile(os.TempDir(), clusterLocal) | ||||
| 	if err != nil { | ||||
| 		t.Fatalf("Could not create TempFile for fallthrough: %s", err) | ||||
| 	} | ||||
| 	defer rmFunc() | ||||
| 	corefile := | ||||
| 		`.:0 { | ||||
|     file ` + dbfile + ` cluster.local | ||||
|     kubernetes cluster.local { | ||||
|                 endpoint http://localhost:8080 | ||||
| 		cidrs 10.0.0.0/24 | ||||
| 		namespaces test-1 | ||||
| 		fallthrough | ||||
|     } | ||||
|     erratic { | ||||
| 	drop 0 | ||||
|     } | ||||
| ` | ||||
| 	cases := append(dnsTestCases, dnsTestCasesFallthrough...) | ||||
| 	doIntegrationTests(t, corefile, cases) | ||||
| } | ||||
|  | ||||
| const clusterLocal = `; cluster.local test file for fallthrough | ||||
| cluster.local.		IN	SOA	sns.dns.icann.org. noc.dns.icann.org. 2015082541 7200 3600 1209600 3600 | ||||
| cluster.local.		IN	NS	b.iana-servers.net. | ||||
| cluster.local.		IN	NS	a.iana-servers.net. | ||||
| cluster.local.		IN	A	127.0.0.1 | ||||
| cluster.local.		IN	A	127.0.0.2 | ||||
| foo.cluster.local.      IN      A	10.10.10.10 | ||||
| f.b.svc.cluster.local.  IN      A	10.10.10.11 | ||||
| *.w.cluster.local.      IN      TXT     "Wildcard" | ||||
| a.b.svc.cluster.local.  IN      TXT     "Not a wildcard" | ||||
| cname.cluster.local.    IN      CNAME   www.example.net. | ||||
|  | ||||
| service.namespace.svc.cluster.local.    IN      SRV     8080 10 10 cluster.local. | ||||
| ` | ||||
|   | ||||
		Reference in New Issue
	
	Block a user