aboutsummaryrefslogtreecommitdiff
path: root/middleware/kubernetes/parse.go
diff options
context:
space:
mode:
authorGravatar Miek Gieben <miek@miek.nl> 2017-08-22 20:44:42 +0100
committerGravatar Yong Tang <yong.tang.github@outlook.com> 2017-08-22 12:44:42 -0700
commit6a4e69eb9fd0aa02c47d74d743ae4fce292dcbb7 (patch)
treee07ebb0b33dd61c190af6b98a079cf45b91ac279 /middleware/kubernetes/parse.go
parent60d5e71a1aa3e7ff75e385600a214063a7b17d01 (diff)
downloadcoredns-6a4e69eb9fd0aa02c47d74d743ae4fce292dcbb7.tar.gz
coredns-6a4e69eb9fd0aa02c47d74d743ae4fce292dcbb7.tar.zst
coredns-6a4e69eb9fd0aa02c47d74d743ae4fce292dcbb7.zip
mw/kubernetes: Rewrite parseRequest and Readability improvements (#939)
* mw/kubernetes: rewrite parseRequest Stop looking at the qtype in parseRequest and make k.Namespace a map. Fallout from this is that pkg/strings as it is not used anymore. Also add a few helper functions to make unexposed namespaces easier to see in the code. Add wildcard tests to the middleware tests. * Fix tests Add a whole bunch of comments to document what we are trying to do. * This is now answered * up coverage * duh * Update testcase * Make it nodata
Diffstat (limited to 'middleware/kubernetes/parse.go')
-rw-r--r--middleware/kubernetes/parse.go121
1 files changed, 64 insertions, 57 deletions
diff --git a/middleware/kubernetes/parse.go b/middleware/kubernetes/parse.go
index 700dc654a..4b09b15f9 100644
--- a/middleware/kubernetes/parse.go
+++ b/middleware/kubernetes/parse.go
@@ -13,80 +13,87 @@ type recordRequest struct {
port string
// The protocol is usually _udp or _tcp (if set), and comes from the protocol part of a well formed
// SRV record.
- protocol string
- endpoint string
- service string
+ protocol string
+ endpoint string
+ // The servicename used in Kubernetes.
+ service string
+ // The namespace used in Kubernetes.
namespace string
- // A each name can be for a pod or a service, here we track what we've seen. This value is true for
- // pods and false for services. If we ever need to extend this well use a typed value.
+ // A each name can be for a pod or a service, here we track what we've seen, either "pod" or "service".
podOrSvc string
}
-// parseRequest parses the qname to find all the elements we need for querying k8s.
+// parseRequest parses the qname to find all the elements we need for querying k8s. Anything
+// that is not parsed will have the wildcard "*" value (except r.endpoint).
+// Potential underscores are stripped from _port and _protocol.
func (k *Kubernetes) parseRequest(state request.Request) (r recordRequest, err error) {
// 3 Possible cases:
- // o SRV Request: _port._protocol.service.namespace.type.zone
- // o A Request (endpoint): endpoint.service.namespace.type.zone
- // o A Request (service): service.namespace.type.zone
- // Federations are handled in the federation middleware.
+ // 1. _port._protocol.service.namespace.pod|svc.zone
+ // 2. (endpoint): endpoint.service.namespace.pod|svc.zone
+ // 3. (service): service.namespace.pod|svc.zone
+ //
+ // Federations are handled in the federation middleware. And aren't parsed here.
base, _ := dnsutil.TrimZone(state.Name(), state.Zone)
segs := dns.SplitDomainName(base)
- offset := 0
- if state.QType() == dns.TypeSRV {
- // The kubernetes peer-finder expects queries with empty port and service to resolve
- // If neither is specified, treat it as a wildcard
- if len(segs) == 3 {
- r.port = "*"
- r.service = "*"
- offset = 0
- } else {
- if len(segs) != 5 {
- return r, errInvalidRequest
- }
- // This is a SRV style request, get first two elements as port and
- // protocol, stripping leading underscores if present.
- if segs[0][0] == '_' {
- r.port = segs[0][1:]
- } else {
- r.port = segs[0]
- if !wildcard(r.port) {
- return r, errInvalidRequest
- }
- }
- if segs[1][0] == '_' {
- r.protocol = segs[1][1:]
- if r.protocol != "tcp" && r.protocol != "udp" {
- return r, errInvalidRequest
- }
- } else {
- r.protocol = segs[1]
- if !wildcard(r.protocol) {
- return r, errInvalidRequest
- }
- }
- if r.port == "" || r.protocol == "" {
- return r, errInvalidRequest
- }
- offset = 2
- }
+ r.port = "*"
+ r.protocol = "*"
+ r.service = "*"
+ r.namespace = "*"
+ // r.endpoint is the odd one out, we need to know if it has been set or not. If it is
+ // empty we should skip the endpoint check in k.get(). Hence we cannot set if to "*".
+
+ // 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
+ r.podOrSvc = segs[last]
+ if r.podOrSvc != Pod && r.podOrSvc != Svc {
+ return r, errInvalidRequest
}
- if (state.QType() == dns.TypeA || state.QType() == dns.TypeAAAA) && len(segs) == 4 {
- // This is an endpoint A/AAAA record request. Get first element as endpoint.
- r.endpoint = segs[0]
- offset = 1
+ last--
+ if last < 0 {
+ return r, nil
}
- if len(segs) == (offset + 3) {
- r.service = segs[offset]
- r.namespace = segs[offset+1]
- r.podOrSvc = segs[offset+2]
+ r.namespace = segs[last]
+ last--
+ if last < 0 {
+ return r, nil
+ }
+ r.service = segs[last]
+ last--
+ if last < 0 {
return r, nil
}
- return r, errInvalidRequest
+ // Becuase of ambiquity we check the labels left: 1: an endpoint. 2: port and protocol.
+ // Anything else is a query that is too long to answer and can safely be delegated to return an nxdomain.
+ switch last {
+
+ case 0: // endpoint only
+ r.endpoint = segs[last]
+ case 1: // service and port
+ r.protocol = stripUnderscore(segs[last])
+ r.port = stripUnderscore(segs[last-1])
+
+ default: // too long
+ return r, errInvalidRequest
+ }
+
+ return r, nil
+}
+
+// stripUnderscore removes a prefixed underscore from s.
+func stripUnderscore(s string) string {
+ if s[0] != '_' {
+ return s
+ }
+ return s[1:]
}
// String return a string representation of r, it just returns all fields concatenated with dots.