diff --git a/plugin/kubernetes/README.md b/plugin/kubernetes/README.md index 2ffd7d1bb..8d66af8da 100644 --- a/plugin/kubernetes/README.md +++ b/plugin/kubernetes/README.md @@ -196,27 +196,6 @@ packet received by CoreDNS must be the IP address of the Pod that sent the reque } } -## Wildcards - -**NOTE: Wildcard queries are deprecated** and will no longer be supported in the next minor release. - -Some query labels accept a wildcard value to match any value. If a label is a valid wildcard (\*, -or the word "any"), then that label will match all values. The labels that accept wildcards are: - - * _endpoint_ in an `A` record request: _endpoint_.service.namespace.svc.zone, e.g., `*.nginx.ns.svc.cluster.local` - * _service_ in an `A` record request: _service_.namespace.svc.zone, e.g., `*.ns.svc.cluster.local` - * _namespace_ in an `A` record request: service._namespace_.svc.zone, e.g., `nginx.*.svc.cluster.local` - * _port and/or protocol_ in an `SRV` request: __port_.__protocol_.service.namespace.svc.zone., - e.g., `_http.*.service.ns.svc.cluster.local` - * multiple wildcards are allowed in a single query, e.g., `A` Request `*.*.svc.zone.` or `SRV` request `*.*.*.*.svc.zone.` - - For example, wildcards can be used to resolve all Endpoints for a Service as `A` records. e.g.: `*.service.ns.svc.myzone.local` will return the Endpoint IPs in the Service `service` in namespace `default`: - -``` -*.service.default.svc.cluster.local. 5 IN A 192.168.10.10 -*.service.default.svc.cluster.local. 5 IN A 192.168.25.15 -``` - ## Metadata The kubernetes plugin will publish the following metadata, if the *metadata* diff --git a/plugin/kubernetes/external.go b/plugin/kubernetes/external.go index 42495a430..77fa0ef58 100644 --- a/plugin/kubernetes/external.go +++ b/plugin/kubernetes/external.go @@ -24,11 +24,7 @@ func (k *Kubernetes) External(state request.Request) ([]msg.Service, int) { // We are dealing with a fairly normal domain name here, but we still need to have the service // and the namespace: // service.namespace. - // - // for service (and SRV) you can also say _tcp, and port (i.e. _http), we need those be picked - // up, unless they are not specified, then we use an internal wildcard. - port := "*" - protocol := "*" + var port, protocol string namespace := segs[last] if !k.namespaceExposed(namespace) { return nil, dns.RcodeNameError @@ -69,7 +65,7 @@ func (k *Kubernetes) External(state request.Request) ([]msg.Service, int) { for _, ip := range svc.ExternalIPs { for _, p := range svc.Ports { - if !(match(port, p.Name) && match(protocol, string(p.Protocol))) { + if !(matchPortAndProtocol(port, p.Name, protocol, string(p.Protocol))) { continue } rcode = dns.RcodeSuccess diff --git a/plugin/kubernetes/external_test.go b/plugin/kubernetes/external_test.go index 9444a999e..5b66579fc 100644 --- a/plugin/kubernetes/external_test.go +++ b/plugin/kubernetes/external_test.go @@ -31,9 +31,6 @@ var extCases = []struct { {Host: "1:2::5", Port: 80, TTL: 5, Key: "/c/org/example/testns/svc1"}, }, }, - { - Qname: "*._not-udp-or-tcp.svc1.testns.example.com.", Rcode: dns.RcodeSuccess, - }, { Qname: "_http._tcp.svc1.testns.example.com.", Rcode: dns.RcodeSuccess, Msg: []msg.Service{ diff --git a/plugin/kubernetes/handler_test.go b/plugin/kubernetes/handler_test.go index 709d831b3..1d39ba827 100644 --- a/plugin/kubernetes/handler_test.go +++ b/plugin/kubernetes/handler_test.go @@ -30,14 +30,6 @@ var dnsTestCases = []test.Case{ test.A("svcempty.testns.svc.cluster.local. 5 IN A 10.0.0.1"), }, }, - // A Service (wildcard) - { - Qname: "svc1.*.svc.cluster.local.", Qtype: dns.TypeA, - Rcode: dns.RcodeSuccess, - Answer: []dns.RR{ - test.A("svc1.*.svc.cluster.local. 5 IN A 10.0.0.1"), - }, - }, { Qname: "svc1.testns.svc.cluster.local.", Qtype: dns.TypeSRV, Rcode: dns.RcodeSuccess, @@ -56,42 +48,6 @@ var dnsTestCases = []test.Case{ Answer: []dns.RR{test.SRV("svc6.testns.svc.cluster.local. 5 IN SRV 0 100 80 svc6.testns.svc.cluster.local.")}, Extra: []dns.RR{test.AAAA("svc6.testns.svc.cluster.local. 5 IN AAAA 1234:abcd::1")}, }, - // SRV Service (wildcard) - { - Qname: "svc1.*.svc.cluster.local.", Qtype: dns.TypeSRV, - Rcode: dns.RcodeSuccess, - Answer: []dns.RR{test.SRV("svc1.*.svc.cluster.local. 5 IN SRV 0 100 80 svc1.testns.svc.cluster.local.")}, - Extra: []dns.RR{test.A("svc1.testns.svc.cluster.local. 5 IN A 10.0.0.1")}, - }, - { - Qname: "svcempty.*.svc.cluster.local.", Qtype: dns.TypeSRV, - Rcode: dns.RcodeSuccess, - Answer: []dns.RR{test.SRV("svcempty.*.svc.cluster.local. 5 IN SRV 0 100 80 svcempty.testns.svc.cluster.local.")}, - Extra: []dns.RR{test.A("svcempty.testns.svc.cluster.local. 5 IN A 10.0.0.1")}, - }, - // SRV Service (wildcards) - { - Qname: "*.any.svc1.*.svc.cluster.local.", Qtype: dns.TypeSRV, - Rcode: dns.RcodeSuccess, - Answer: []dns.RR{test.SRV("*.any.svc1.*.svc.cluster.local. 5 IN SRV 0 100 80 svc1.testns.svc.cluster.local.")}, - Extra: []dns.RR{test.A("svc1.testns.svc.cluster.local. 5 IN A 10.0.0.1")}, - }, - // A Service (wildcards) - { - Qname: "*.any.svc1.*.svc.cluster.local.", Qtype: dns.TypeA, - Rcode: dns.RcodeSuccess, - Answer: []dns.RR{ - test.A("*.any.svc1.*.svc.cluster.local. 5 IN A 10.0.0.1"), - }, - }, - // SRV Service Not udp/tcp - { - Qname: "*._not-udp-or-tcp.svc1.testns.svc.cluster.local.", Qtype: dns.TypeSRV, - Rcode: dns.RcodeNameError, - Ns: []dns.RR{ - test.SOA("cluster.local. 5 IN SOA ns.dns.cluster.local. hostmaster.cluster.local. 1499347823 7200 1800 86400 5"), - }, - }, // SRV Service { Qname: "_http._tcp.svc1.testns.svc.cluster.local.", Qtype: dns.TypeSRV, @@ -195,14 +151,6 @@ var dnsTestCases = []test.Case{ test.A("hdls1.testns.svc.cluster.local. 5 IN A 172.0.0.5"), }, }, - // SRV Service (Headless and portless) - { - Qname: "*.*.hdlsprtls.testns.svc.cluster.local.", Qtype: dns.TypeSRV, - Rcode: dns.RcodeSuccess, - Ns: []dns.RR{ - test.SOA("cluster.local. 5 IN SOA ns.dns.cluster.local. hostmaster.cluster.local. 1499347823 7200 1800 86400 5"), - }, - }, // AAAA { Qname: "5678-abcd--2.hdls1.testns.svc.cluster.local", Qtype: dns.TypeAAAA, @@ -406,7 +354,6 @@ var dnsTestCases = []test.Case{ } func TestServeDNS(t *testing.T) { - k := New([]string{"cluster.local."}) k.APIConn = &APIConnServeTest{} k.Next = test.NextHandler(dns.RcodeSuccess, nil) @@ -434,11 +381,11 @@ func TestServeDNS(t *testing.T) { // Before sorting, make sure that CNAMES do not appear after their target records if err := test.CNAMEOrder(resp); err != nil { - t.Error(err) + t.Errorf("Test %d, %v",i, err) } if err := test.SortAndCheck(resp, tc); err != nil { - t.Error(err) + t.Errorf("Test %d, %v",i, err) } } } diff --git a/plugin/kubernetes/kubernetes.go b/plugin/kubernetes/kubernetes.go index ad7453351..67d549329 100644 --- a/plugin/kubernetes/kubernetes.go +++ b/plugin/kubernetes/kubernetes.go @@ -360,7 +360,7 @@ func (k *Kubernetes) Records(ctx context.Context, state request.Request, exact b return nil, errNoItems } - if !wildcard(r.namespace) && !k.namespaceExposed(r.namespace) { + if !k.namespaceExposed(r.namespace) { return nil, errNsNotExposed } @@ -395,7 +395,7 @@ func (k *Kubernetes) findPods(r recordRequest, zone string) (pods []msg.Service, } namespace := r.namespace - if !wildcard(namespace) && !k.namespaceExposed(namespace) { + if !k.namespaceExposed(namespace) { return nil, errNoItems } @@ -403,7 +403,7 @@ func (k *Kubernetes) findPods(r recordRequest, zone string) (pods []msg.Service, // handle empty pod name if podname == "" { - if k.namespaceExposed(namespace) || wildcard(namespace) { + if k.namespaceExposed(namespace) { // NODATA return nil, nil } @@ -420,7 +420,7 @@ func (k *Kubernetes) findPods(r recordRequest, zone string) (pods []msg.Service, } if k.podMode == podModeInsecure { - if !wildcard(namespace) && !k.namespaceExposed(namespace) { // no wildcard, but namespace does not exist + if !k.namespaceExposed(namespace) { // namespace does not exist return nil, errNoItems } @@ -434,19 +434,8 @@ func (k *Kubernetes) findPods(r recordRequest, zone string) (pods []msg.Service, // PodModeVerified err = errNoItems - if wildcard(podname) && !wildcard(namespace) { - // If namespace exists, err should be nil, so that we return NODATA instead of NXDOMAIN - if k.namespaceExposed(namespace) { - err = nil - } - } for _, p := range k.APIConn.PodIndex(ip) { - // If namespace has a wildcard, filter results against Corefile namespace list. - if wildcard(namespace) && !k.namespaceExposed(p.Namespace) { - continue - } - // check for matching ip and namespace if ip == p.PodIP && match(namespace, p.Namespace) { s := msg.Service{Key: strings.Join([]string{zonePath, Pod, namespace, podname}, "/"), Host: ip, TTL: k.ttl} @@ -460,13 +449,13 @@ func (k *Kubernetes) findPods(r recordRequest, zone string) (pods []msg.Service, // findServices returns the services matching r from the cache. func (k *Kubernetes) findServices(r recordRequest, zone string) (services []msg.Service, err error) { - if !wildcard(r.namespace) && !k.namespaceExposed(r.namespace) { + if !k.namespaceExposed(r.namespace) { return nil, errNoItems } // handle empty service name if r.service == "" { - if k.namespaceExposed(r.namespace) || wildcard(r.namespace) { + if k.namespaceExposed(r.namespace) { // NODATA return nil, nil } @@ -475,12 +464,6 @@ func (k *Kubernetes) findServices(r recordRequest, zone string) (services []msg. } err = errNoItems - if wildcard(r.service) && !wildcard(r.namespace) { - // If namespace exists, err should be nil, so that we return NODATA instead of NXDOMAIN - if k.namespaceExposed(r.namespace) { - err = nil - } - } var ( endpointsListFunc func() []*object.Endpoints @@ -488,14 +471,11 @@ func (k *Kubernetes) findServices(r recordRequest, zone string) (services []msg. serviceList []*object.Service ) - if wildcard(r.service) || wildcard(r.namespace) { - serviceList = k.APIConn.ServiceList() - endpointsListFunc = func() []*object.Endpoints { return k.APIConn.EndpointsList() } - } else { - idx := object.ServiceKey(r.service, r.namespace) - serviceList = k.APIConn.SvcIndex(idx) - endpointsListFunc = func() []*object.Endpoints { return k.APIConn.EpIndex(idx) } - } + + idx := object.ServiceKey(r.service, r.namespace) + serviceList = k.APIConn.SvcIndex(idx) + endpointsListFunc = func() []*object.Endpoints { return k.APIConn.EpIndex(idx) } + zonePath := msg.Path(zone, coredns) for _, svc := range serviceList { @@ -503,12 +483,6 @@ func (k *Kubernetes) findServices(r recordRequest, zone string) (services []msg. continue } - // If request namespace is a wildcard, filter results against Corefile namespace list. - // (Namespaces without a wildcard were filtered before the call to this function.) - if wildcard(r.namespace) && !k.namespaceExposed(svc.Namespace) { - continue - } - // If "ignore empty_service" option is set and no endpoints exist, return NXDOMAIN unless // it's a headless or externalName service (covered below). if k.opts.ignoreEmptyService && svc.Type != api.ServiceTypeExternalName && !svc.Headless() { // serve NXDOMAIN if no endpoint is able to answer @@ -558,7 +532,7 @@ func (k *Kubernetes) findServices(r recordRequest, zone string) (services []msg. } for _, p := range eps.Ports { - if !(match(r.port, p.Name) && match(r.protocol, p.Protocol)) { + if !(matchPortAndProtocol(r.port, p.Name, r.protocol, p.Protocol)) { continue } s := msg.Service{Host: addr.IP, Port: int(p.Port), TTL: k.ttl} @@ -576,7 +550,7 @@ func (k *Kubernetes) findServices(r recordRequest, zone string) (services []msg. // ClusterIP service for _, p := range svc.Ports { - if !(match(r.port, p.Name) && match(r.protocol, string(p.Protocol))) { + if !(matchPortAndProtocol(r.port, p.Name, r.protocol, string(p.Protocol))) { continue } @@ -598,20 +572,14 @@ func (k *Kubernetes) Serial(state request.Request) uint32 { return uint32(k.APIC // MinTTL returns the minimal TTL. func (k *Kubernetes) MinTTL(state request.Request) uint32 { return k.ttl } -// match checks if a and b are equal taking wildcards into account. +// match checks if a and b are equal. func match(a, b string) bool { - if wildcard(a) { - return true - } - if wildcard(b) { - return true - } return strings.EqualFold(a, b) } -// wildcard checks whether s contains a wildcard value defined as "*" or "any". -func wildcard(s string) bool { - return s == "*" || s == "any" +// matchPortAndProtocol matches port and protocol, permitting the the 'a' inputs to be wild +func matchPortAndProtocol(aPort, bPort, aProtocol, bProtocol string) bool { + return (match(aPort, bPort) || aPort == "") && (match(aProtocol, bProtocol) || aProtocol == "") } const coredns = "c" // used as a fake key prefix in msg.Service diff --git a/plugin/kubernetes/kubernetes_test.go b/plugin/kubernetes/kubernetes_test.go index 0d5e6b637..ca19043fb 100644 --- a/plugin/kubernetes/kubernetes_test.go +++ b/plugin/kubernetes/kubernetes_test.go @@ -14,27 +14,6 @@ import ( meta "k8s.io/apimachinery/pkg/apis/meta/v1" ) -func TestWildcard(t *testing.T) { - var tests = []struct { - s string - expected bool - }{ - {"mynamespace", false}, - {"*", true}, - {"any", true}, - {"my*space", false}, - {"*space", false}, - {"myname*", false}, - } - - for _, te := range tests { - got := wildcard(te.s) - if got != te.expected { - t.Errorf("Expected Wildcard result '%v' for example '%v', got '%v'.", te.expected, te.s, got) - } - } -} - func TestEndpointHostname(t *testing.T) { var tests = []struct { ip string diff --git a/plugin/kubernetes/metadata_test.go b/plugin/kubernetes/metadata_test.go index 224e84553..009c53375 100644 --- a/plugin/kubernetes/metadata_test.go +++ b/plugin/kubernetes/metadata_test.go @@ -29,8 +29,8 @@ var metadataCases = []struct { "kubernetes/endpoint": "", "kubernetes/kind": "pod", "kubernetes/namespace": "podns", - "kubernetes/port-name": "*", - "kubernetes/protocol": "*", + "kubernetes/port-name": "", + "kubernetes/protocol": "", "kubernetes/service": "10-240-0-1", }, }, @@ -40,8 +40,8 @@ var metadataCases = []struct { "kubernetes/endpoint": "", "kubernetes/kind": "svc", "kubernetes/namespace": "ns", - "kubernetes/port-name": "*", - "kubernetes/protocol": "*", + "kubernetes/port-name": "", + "kubernetes/protocol": "", "kubernetes/service": "s", }, }, @@ -52,8 +52,8 @@ var metadataCases = []struct { "kubernetes/endpoint": "", "kubernetes/kind": "svc", "kubernetes/namespace": "ns", - "kubernetes/port-name": "*", - "kubernetes/protocol": "*", + "kubernetes/port-name": "", + "kubernetes/protocol": "", "kubernetes/service": "s", }, }, @@ -76,8 +76,8 @@ var metadataCases = []struct { "kubernetes/endpoint": "ep", "kubernetes/kind": "svc", "kubernetes/namespace": "ns", - "kubernetes/port-name": "*", - "kubernetes/protocol": "*", + "kubernetes/port-name": "", + "kubernetes/protocol": "", "kubernetes/service": "s", }, }, diff --git a/plugin/kubernetes/parse.go b/plugin/kubernetes/parse.go index a7cd36ba6..adef4a024 100644 --- a/plugin/kubernetes/parse.go +++ b/plugin/kubernetes/parse.go @@ -38,18 +38,6 @@ func parseRequest(name, zone string) (r recordRequest, err error) { } segs := dns.SplitDomainName(base) - r.port = "*" - r.protocol = "*" - // for r.name, r.namespace and r.endpoint, we need to know if they have been set or not... - // For endpoint: if empty we should skip the endpoint check in k.get(). Hence we cannot set if to "*". - // For name: myns.svc.cluster.local != *.myns.svc.cluster.local - // For namespace: svc.cluster.local != *.svc.cluster.local - - // start at the right and fill out recordRequest with the bits we find, so we look for - // pod|svc.namespace.service and then either - // * endpoint - // *_protocol._port - last := len(segs) - 1 if last < 0 { return r, nil diff --git a/plugin/kubernetes/parse_test.go b/plugin/kubernetes/parse_test.go index 68cff668b..4b8000b02 100644 --- a/plugin/kubernetes/parse_test.go +++ b/plugin/kubernetes/parse_test.go @@ -15,10 +15,8 @@ func TestParseRequest(t *testing.T) { }{ // valid SRV request {"_http._tcp.webs.mynamespace.svc.inter.webs.tests.", "http.tcp..webs.mynamespace.svc"}, - // wildcard acceptance - {"*.any.*.any.svc.inter.webs.tests.", "*.any..*.any.svc"}, // A request of endpoint - {"1-2-3-4.webs.mynamespace.svc.inter.webs.tests.", "*.*.1-2-3-4.webs.mynamespace.svc"}, + {"1-2-3-4.webs.mynamespace.svc.inter.webs.tests.", "..1-2-3-4.webs.mynamespace.svc"}, // bare zone {"inter.webs.tests.", "....."}, // bare svc type