remove wildcard query functionality (#5019)
Signed-off-by: Chris O'Haver <cohaver@infoblox.com>
This commit is contained in:
parent
40a526b27f
commit
abaf938623
9 changed files with 30 additions and 178 deletions
|
@ -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*
|
||||
|
|
|
@ -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.<base>
|
||||
//
|
||||
// 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
|
||||
|
|
|
@ -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{
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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",
|
||||
},
|
||||
},
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue