WIP: Parserequest2 cutback (#868)

* middleware/kubernetes: pull TXT out of parseRequest

Put the TXT handling one layer higher and remove it from parseRequest.
Also rename the podsvc field in there to podOrSvc. Now that it isn't
used anymore for TXT record (dns-version) that was put in there. We can
make this a boolean (in a future PR).

Make parseRequest get an optional Zone that is from state.Zone and use
that instead of its own code. Removed some tests and other smaller
cleanups.

Fixes #836

* add this reverse

* another check

* readd

* Rename to kPod and kService for some clarity
This commit is contained in:
Miek Gieben 2017-08-10 01:08:58 -07:00 committed by GitHub
parent fefc4374d7
commit 7e56cc74e5
9 changed files with 317 additions and 328 deletions

View file

@ -11,6 +11,7 @@ import (
"github.com/coredns/coredns/middleware"
"github.com/coredns/coredns/middleware/etcd/msg"
"github.com/coredns/coredns/middleware/pkg/dnsutil"
dnsstrings "github.com/coredns/coredns/middleware/pkg/strings"
"github.com/coredns/coredns/middleware/proxy"
"github.com/coredns/coredns/request"
@ -64,7 +65,8 @@ type endpoint struct {
port api.EndpointPort
}
type service struct {
// kService is a service as retrieved via the k8s API.
type kService struct {
name string
namespace string
addr string
@ -72,23 +74,13 @@ type service struct {
endpoints []endpoint
}
type pod struct {
// kPod is a pod as retrieved via the k8s API.
type kPod struct {
name string
namespace string
addr string
}
type recordRequest struct {
port string
protocol string
endpoint string
service string
namespace string
typeName string
zone string
federation string
}
var (
errNoItems = errors.New("no items found")
errNsNotExposed = errors.New("namespace is not exposed")
@ -100,12 +92,33 @@ var (
// Services implements the ServiceBackend interface.
func (k *Kubernetes) Services(state request.Request, exact bool, opt middleware.Options) (svcs []msg.Service, debug []msg.Service, err error) {
r, e := k.parseRequest(state.Name(), state.QType())
// We're looking again at types, which we've already done in ServeDNS, but there are some types k8s just can't answer.
switch state.QType() {
case dns.TypeTXT:
// 1 label + zone, label must be "dns-version"
t, err := dnsutil.TrimZone(state.Name(), state.Zone)
if err != nil {
return nil, nil, err
}
segs := dns.SplitDomainName(t)
if len(segs) != 1 {
return nil, nil, errors.New("servfail")
}
if segs[0] != "dns-version" {
return nil, nil, errInvalidRequest
}
svc := msg.Service{Text: DNSSchemaVersion, TTL: 28800, Key: msg.Path(state.QName(), "coredns")}
return []msg.Service{svc}, nil, nil
}
r, e := k.parseRequest(state.Name(), state.QType(), state.Zone)
if e != nil {
return nil, nil, e
}
switch state.Type() {
case "A", "AAAA", "CNAME":
switch state.QType() {
case dns.TypeA, dns.TypeAAAA, dns.TypeCNAME:
if state.Type() == "A" && isDefaultNS(state.Name(), r) {
// If this is an A request for "ns.dns", respond with a "fake" record for coredns.
// SOA records always use this hardcoded name
@ -118,7 +131,7 @@ func (k *Kubernetes) Services(state request.Request, exact bool, opt middleware.
return nil, nil, e
}
return s, nil, e // Haven't implemented debug queries yet.
case "SRV":
case dns.TypeSRV:
s, e := k.Entries(r)
// SRV for external services is not yet implemented, so remove those records
noext := []msg.Service{}
@ -128,13 +141,7 @@ func (k *Kubernetes) Services(state request.Request, exact bool, opt middleware.
}
}
return noext, nil, e
case "TXT":
if r.typeName == "dns-version" {
srv := k.recordsForTXT(r)
svcs = append(svcs, srv)
}
return svcs, nil, err
case "NS":
case dns.TypeNS:
srv := k.recordsForNS(r)
svcs = append(svcs, srv)
return svcs, nil, err
@ -142,11 +149,6 @@ func (k *Kubernetes) Services(state request.Request, exact bool, opt middleware.
return nil, nil, nil
}
func (k *Kubernetes) recordsForTXT(r recordRequest) msg.Service {
return msg.Service{Text: DNSSchemaVersion, TTL: 28800,
Key: msg.Path(strings.Join([]string{r.typeName, r.zone}, "."), "coredns")}
}
func (k *Kubernetes) recordsForNS(r recordRequest) msg.Service {
ns := k.coreDNSRecord()
return msg.Service{Host: ns.A.String(),
@ -234,99 +236,6 @@ func (k *Kubernetes) InitKubeCache() (err error) {
return err
}
func (k *Kubernetes) parseRequest(lowerCasedName string, qtype uint16) (r recordRequest, err error) {
// 3 Possible cases
// SRV Request: _port._protocol.service.namespace.[federation.]type.zone
// A Request (endpoint): endpoint.service.namespace.[federation.]type.zone
// A Request (service): service.namespace.[federation.]type.zone
// separate zone from rest of lowerCasedName
var segs []string
for _, z := range k.Zones {
if dns.IsSubDomain(z, lowerCasedName) {
r.zone = z
segs = dns.SplitDomainName(lowerCasedName)
segs = segs[:len(segs)-dns.CountLabel(r.zone)]
break
}
}
if r.zone == "" {
return r, errZoneNotFound
}
r.federation, segs = k.stripFederation(segs)
if qtype == dns.TypeNS {
return r, nil
}
if qtype == dns.TypeA && isDefaultNS(lowerCasedName, r) {
return r, nil
}
offset := 0
if 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
}
}
if (qtype == dns.TypeA || 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
}
if len(segs) == (offset + 3) {
r.service = segs[offset]
r.namespace = segs[offset+1]
r.typeName = segs[offset+2]
return r, nil
}
if len(segs) == 1 && qtype == dns.TypeTXT {
r.typeName = segs[0]
return r, nil
}
return r, errInvalidRequest
}
// Records not implemented, see Entries().
func (k *Kubernetes) Records(name string, exact bool) ([]msg.Service, error) {
return nil, fmt.Errorf("NOOP")
@ -375,7 +284,7 @@ func endpointHostname(addr api.EndpointAddress) string {
return ""
}
func (k *Kubernetes) getRecordsForK8sItems(services []service, pods []pod, r recordRequest) (records []msg.Service) {
func (k *Kubernetes) getRecordsForK8sItems(services []kService, pods []kPod, r recordRequest) (records []msg.Service) {
zonePath := msg.Path(r.zone, "coredns")
for _, svc := range services {
@ -435,7 +344,7 @@ func (k *Kubernetes) getRecordsForK8sItems(services []service, pods []pod, r rec
return records
}
func (k *Kubernetes) findPods(namespace, podname string) (pods []pod, err error) {
func (k *Kubernetes) findPods(namespace, podname string) (pods []kPod, err error) {
if k.PodMode == PodModeDisabled {
return pods, errPodsDisabled
}
@ -448,7 +357,7 @@ func (k *Kubernetes) findPods(namespace, podname string) (pods []pod, err error)
}
if k.PodMode == PodModeInsecure {
s := pod{name: podname, namespace: namespace, addr: ip}
s := kPod{name: podname, namespace: namespace, addr: ip}
pods = append(pods, s)
return pods, nil
}
@ -468,7 +377,7 @@ func (k *Kubernetes) findPods(namespace, podname string) (pods []pod, err error)
}
// check for matching ip and namespace
if ip == p.Status.PodIP && match(namespace, p.Namespace, nsWildcard) {
s := pod{name: podname, namespace: namespace, addr: ip}
s := kPod{name: podname, namespace: namespace, addr: ip}
pods = append(pods, s)
return pods, nil
}
@ -477,9 +386,9 @@ func (k *Kubernetes) findPods(namespace, podname string) (pods []pod, err error)
}
// get retrieves matching data from the cache.
func (k *Kubernetes) get(r recordRequest) (services []service, pods []pod, err error) {
func (k *Kubernetes) get(r recordRequest) (services []kService, pods []kPod, err error) {
switch {
case r.typeName == Pod:
case r.podOrSvc == Pod:
pods, err = k.findPods(r.namespace, r.service)
return nil, pods, err
default:
@ -488,9 +397,9 @@ func (k *Kubernetes) get(r recordRequest) (services []service, pods []pod, err e
}
}
func (k *Kubernetes) findServices(r recordRequest) ([]service, error) {
func (k *Kubernetes) findServices(r recordRequest) ([]kService, error) {
serviceList := k.APIConn.ServiceList()
var resultItems []service
var resultItems []kService
nsWildcard := wildcard(r.namespace)
serviceWildcard := wildcard(r.service)
@ -506,7 +415,7 @@ func (k *Kubernetes) findServices(r recordRequest) ([]service, error) {
if nsWildcard && (len(k.Namespaces) > 0) && (!dnsstrings.StringInSlice(svc.Namespace, k.Namespaces)) {
continue
}
s := service{name: svc.Name, namespace: svc.Namespace}
s := kService{name: svc.Name, namespace: svc.Namespace}
// Endpoint query or headless service
if svc.Spec.ClusterIP == api.ClusterIPNone || r.endpoint != "" {