ServiceBackend interface (#369)

* Add ServiceBackend interface

This adds a ServiceBackend interface that is shared between etcd/etcd3
(later) and kubernetes, leading to a massive reduction in code. When
returning the specific records from their backend.

Fixes #273
This commit is contained in:
Miek Gieben 2016-10-30 15:54:16 +00:00 committed by GitHub
parent 81d5baee28
commit 27d893cf33
15 changed files with 273 additions and 503 deletions

View file

@ -3,6 +3,7 @@ package kubernetes
import (
"errors"
"fmt"
"log"
"strings"
"time"
@ -11,8 +12,9 @@ import (
"github.com/miekg/coredns/middleware/etcd/msg"
"github.com/miekg/coredns/middleware/kubernetes/nametemplate"
"github.com/miekg/coredns/middleware/pkg/dnsutil"
dns_strings "github.com/miekg/coredns/middleware/pkg/strings"
dnsstrings "github.com/miekg/coredns/middleware/pkg/strings"
"github.com/miekg/coredns/middleware/proxy"
"github.com/miekg/coredns/request"
"github.com/miekg/dns"
"k8s.io/kubernetes/pkg/api"
@ -41,6 +43,28 @@ type Kubernetes struct {
Selector *labels.Selector
}
// Services implements the ServiceBackend interface.
func (k *Kubernetes) Services(state request.Request, exact bool, opt middleware.Options) ([]msg.Service, []msg.Service, error) {
s, e := k.Records(state.Name(), exact)
return s, nil, e // Haven't implemented debug queries yet.
}
// Lookup implements the ServiceBackend interface.
func (k *Kubernetes) Lookup(state request.Request, name string, typ uint16) (*dns.Msg, error) {
return k.Proxy.Lookup(state, name, typ)
}
// IsNameError implements the ServiceBackend interface.
// TODO(infoblox): implement!
func (k *Kubernetes) IsNameError(err error) bool {
return false
}
// Debug implements the ServiceBackend interface.
func (k *Kubernetes) Debug() string {
return "debug"
}
func (k *Kubernetes) getClientConfig() (*restclient.Config, error) {
// For a custom api server or running outside a k8s cluster
// set URL in env.KUBERNETES_MASTER or set endpoint in Corefile
@ -73,7 +97,6 @@ func (k *Kubernetes) getClientConfig() (*restclient.Config, error) {
}
// InitKubeCache initializes a new Kubernetes cache.
// TODO(miek): is this correct?
func (k *Kubernetes) InitKubeCache() error {
config, err := k.getClientConfig()
@ -83,21 +106,24 @@ func (k *Kubernetes) InitKubeCache() error {
kubeClient, err := clientset_generated.NewForConfig(config)
if err != nil {
log.Printf("[ERROR] Failed to create kubernetes notification controller: %v", err)
return err
return fmt.Errorf("Failed to create kubernetes notification controller: %v", err)
}
if k.LabelSelector == nil {
log.Printf("[INFO] Kubernetes middleware configured without a label selector. No label-based filtering will be performed.")
} else {
if k.LabelSelector != nil {
var selector labels.Selector
selector, err = unversionedapi.LabelSelectorAsSelector(k.LabelSelector)
k.Selector = &selector
if err != nil {
log.Printf("[ERROR] Unable to create Selector for LabelSelector '%s'.Error was: %s", k.LabelSelector, err)
return err
return fmt.Errorf("Unable to create Selector for LabelSelector '%s'.Error was: %s", k.LabelSelector, err)
}
}
if k.LabelSelector == nil {
log.Printf("[INFO] Kubernetes middleware configured without a label selector. No label-based filtering will be performed.")
} else {
log.Printf("[INFO] Kubernetes middleware configured with the label selector '%s'. Only kubernetes objects matching this label selector will be exposed.", unversionedapi.FormatLabelSelector(k.LabelSelector))
}
k.APIConn = newdnsController(kubeClient, k.ResyncPeriod, k.Selector)
return err
@ -125,12 +151,11 @@ func (k *Kubernetes) getZoneForName(name string) (string, []string) {
return zone, serviceSegments
}
// Records looks up services in kubernetes.
// If exact is true, it will lookup just
// this name. This is used when find matches when completing SRV lookups
// Records looks up services in kubernetes. If exact is true, it will lookup
// just this name. This is used when find matches when completing SRV lookups
// for instance.
func (k *Kubernetes) Records(name string, exact bool) ([]msg.Service, error) {
// TODO: refector this.
// TODO: refactor this.
// Right now NamespaceFromSegmentArray do not supports PRE queries
ip := dnsutil.ExtractAddressFromReverse(name)
if ip != "" {
@ -169,7 +194,7 @@ func (k *Kubernetes) Records(name string, exact bool) ([]msg.Service, error) {
// Abort if the namespace does not contain a wildcard, and namespace is not published per CoreFile
// Case where namespace contains a wildcard is handled in Get(...) method.
if (!nsWildcard) && (len(k.Namespaces) > 0) && (!dns_strings.StringInSlice(namespace, k.Namespaces)) {
if (!nsWildcard) && (len(k.Namespaces) > 0) && (!dnsstrings.StringInSlice(namespace, k.Namespaces)) {
return nil, nil
}
@ -219,7 +244,7 @@ func (k *Kubernetes) Get(namespace string, nsWildcard bool, servicename string,
if symbolMatches(namespace, item.Namespace, nsWildcard) && symbolMatches(servicename, item.Name, serviceWildcard) {
// If namespace has a wildcard, filter results against Corefile namespace list.
// (Namespaces without a wildcard were filtered before the call to this function.)
if nsWildcard && (len(k.Namespaces) > 0) && (!dns_strings.StringInSlice(item.Namespace, k.Namespaces)) {
if nsWildcard && (len(k.Namespaces) > 0) && (!dnsstrings.StringInSlice(item.Namespace, k.Namespaces)) {
continue
}
resultItems = append(resultItems, item)
@ -242,11 +267,6 @@ func symbolMatches(queryString string, candidateString string, wildcard bool) bo
return result
}
// kubernetesNameError checks if the error is ErrorCodeKeyNotFound from kubernetes.
func isKubernetesNameError(err error) bool {
return false
}
func (k *Kubernetes) getServiceRecordForIP(ip, name string) []msg.Service {
svcList, err := k.APIConn.svcLister.List(labels.Everything())
if err != nil {