plugin/kubernetes: Add upstream @self and loop count (#1484)

* add upstream @self and loop count

* 1st round of feedback

* allow argless upstream

* update test

* readmes

* feedback
This commit is contained in:
Chris O'Haver
2018-02-14 15:11:26 -05:00
committed by Miek Gieben
parent ee8084a08f
commit 71ee323651
15 changed files with 177 additions and 58 deletions

View File

@@ -13,8 +13,8 @@ CoreDNS running the kubernetes plugin can be used as a replacement of kube-dns i
cluster. See the [deployment](https://github.com/coredns/deployment) repository for details on [how
to deploy CoreDNS in Kubernetes](https://github.com/coredns/deployment/tree/master/kubernetes).
[stubDomains](http://blog.kubernetes.io/2017/04/configuring-private-dns-zones-upstream-nameservers-kubernetes.html)
are implemented via the *proxy* plugin.
[stubDomains and upstreamNameservers](http://blog.kubernetes.io/2017/04/configuring-private-dns-zones-upstream-nameservers-kubernetes.html)
are implemented via the *proxy* plugin and kubernetes *upstream*. See example below.
## Syntax
@@ -36,7 +36,7 @@ kubernetes [ZONES...] {
labels EXPRESSION
pods POD-MODE
endpoint_pod_names
upstream ADDRESS...
upstream [ADDRESS...]
ttl TTL
fallthrough [ZONES...]
}
@@ -80,8 +80,9 @@ kubernetes [ZONES...] {
follows: Use the hostname of the endpoint, or if hostname is not set, use the
pod name of the pod targeted by the endpoint. If there is no pod targeted by
the endpoint, use the dashed IP address form.
* `upstream` **ADDRESS [ADDRESS...]** defines the upstream resolvers used for resolving services
that point to external hosts (External Services). **ADDRESS** can be an IP, an IP:port, or a path
* `upstream` [**ADDRESS**...] defines the upstream resolvers used for resolving services
that point to external hosts (aka External Services aka CNAMEs). If no **ADDRESS** is given, CoreDNS
will resolve External Services against itself. **ADDRESS** can be an IP, an IP:port, or a path
to a file structured like resolv.conf.
* `ttl` allows you to set a custom TTL for responses. The default (and allowed minimum) is to use
5 seconds, the maximum is capped at 3600 seconds.
@@ -129,24 +130,33 @@ kubernetes cluster.local {
}
~~~
Here we use the *proxy* plugin to implement stubDomains that forwards `example.org` and
`example.com` to another nameserver.
## stubDomains and upstreamNameservers
Here we use the *proxy* plugin to implement a stubDomain that forwards `example.local` to the nameserver `10.100.0.10:53`.
The *upstream* option in kubernetes means that ExternalName services (CNAMEs) will be resolved using the respective proxy.
Also configured is an upstreamNameserver `8.8.8.8:53` that will be used for resolving names that do not fall in `cluster.local`
or `example.local`.
~~~ txt
cluster.local {
kubernetes {
endpoint https://k8s-endpoint:8443
tls cert key cacert
.:53 {
kubernetes cluster.local {
upstream
}
}
example.org {
proxy . 8.8.8.8:53
}
example.com {
proxy example.local 10.100.0.10:53
proxy . 8.8.8.8:53
}
~~~
The configuration above represents the following Kube-DNS stubDomains and upstreamNameservers configuration.
~~~ txt
stubDomains: |
{“example.local”: [“10.100.0.10:53”]}
upstreamNameservers: |
[“8.8.8.8:53”]
~~~
## AutoPath
The *kubernetes* plugin can be used in conjunction with the *autopath* plugin. Using this

View File

@@ -11,7 +11,8 @@ import (
// ServeDNS implements the plugin.Handler interface.
func (k Kubernetes) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
state := request.Request{W: w, Req: r}
opt := plugin.Options{}
state := request.Request{W: w, Req: r, Context: ctx}
m := new(dns.Msg)
m.SetReply(r)
@@ -32,24 +33,24 @@ func (k Kubernetes) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.M
switch state.QType() {
case dns.TypeA:
records, err = plugin.A(&k, zone, state, nil, plugin.Options{})
records, err = plugin.A(&k, zone, state, nil, opt)
case dns.TypeAAAA:
records, err = plugin.AAAA(&k, zone, state, nil, plugin.Options{})
records, err = plugin.AAAA(&k, zone, state, nil, opt)
case dns.TypeTXT:
records, err = plugin.TXT(&k, zone, state, plugin.Options{})
records, err = plugin.TXT(&k, zone, state, opt)
case dns.TypeCNAME:
records, err = plugin.CNAME(&k, zone, state, plugin.Options{})
records, err = plugin.CNAME(&k, zone, state, opt)
case dns.TypePTR:
records, err = plugin.PTR(&k, zone, state, plugin.Options{})
records, err = plugin.PTR(&k, zone, state, opt)
case dns.TypeMX:
records, extra, err = plugin.MX(&k, zone, state, plugin.Options{})
records, extra, err = plugin.MX(&k, zone, state, opt)
case dns.TypeSRV:
records, extra, err = plugin.SRV(&k, zone, state, plugin.Options{})
records, extra, err = plugin.SRV(&k, zone, state, opt)
case dns.TypeSOA:
records, err = plugin.SOA(&k, zone, state, plugin.Options{})
records, err = plugin.SOA(&k, zone, state, opt)
case dns.TypeNS:
if state.Name() == zone {
records, extra, err = plugin.NS(&k, zone, state, plugin.Options{})
records, extra, err = plugin.NS(&k, zone, state, opt)
break
}
fallthrough
@@ -57,21 +58,21 @@ func (k Kubernetes) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.M
k.Transfer(ctx, state)
default:
// Do a fake A lookup, so we can distinguish between NODATA and NXDOMAIN
_, err = plugin.A(&k, zone, state, nil, plugin.Options{})
_, err = plugin.A(&k, zone, state, nil, opt)
}
if k.IsNameError(err) {
if k.Fall.Through(state.Name()) {
return plugin.NextOrFailure(k.Name(), k.Next, ctx, w, r)
}
return plugin.BackendError(&k, zone, dns.RcodeNameError, state, nil /* err */, plugin.Options{})
return plugin.BackendError(&k, zone, dns.RcodeNameError, state, nil /* err */, opt)
}
if err != nil {
return dns.RcodeServerFailure, err
}
if len(records) == 0 {
return plugin.BackendError(&k, zone, dns.RcodeSuccess, state, nil, plugin.Options{})
return plugin.BackendError(&k, zone, dns.RcodeSuccess, state, nil, opt)
}
m.Answer = append(m.Answer, records...)

View File

@@ -14,7 +14,7 @@ import (
"github.com/coredns/coredns/plugin/pkg/dnsutil"
"github.com/coredns/coredns/plugin/pkg/fall"
"github.com/coredns/coredns/plugin/pkg/healthcheck"
"github.com/coredns/coredns/plugin/proxy"
"github.com/coredns/coredns/plugin/pkg/upstream"
"github.com/coredns/coredns/request"
"github.com/miekg/dns"
@@ -31,7 +31,7 @@ import (
type Kubernetes struct {
Next plugin.Handler
Zones []string
Proxy proxy.Proxy // Proxy for looking up names during the resolution process
Upstream upstream.Upstream
APIServerList []string
APIProxy *apiProxy
APICertAuth string
@@ -59,7 +59,6 @@ func New(zones []string) *Kubernetes {
k.Namespaces = make(map[string]bool)
k.interfaceAddrsFunc = func() net.IP { return net.ParseIP("127.0.0.1") }
k.podMode = podModeDisabled
k.Proxy = proxy.Proxy{}
k.ttl = defaultTTL
return k
@@ -146,7 +145,7 @@ func (k *Kubernetes) primaryZone() string { return k.Zones[k.primaryZoneIndex] }
// 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)
return k.Upstream.Lookup(state, name, typ)
}
// IsNameError implements the ServiceBackend interface.

View File

@@ -9,10 +9,9 @@ import (
"github.com/coredns/coredns/core/dnsserver"
"github.com/coredns/coredns/plugin"
"github.com/coredns/coredns/plugin/pkg/dnsutil"
"github.com/coredns/coredns/plugin/pkg/parse"
"github.com/coredns/coredns/plugin/proxy"
"github.com/coredns/coredns/plugin/pkg/upstream"
"github.com/mholt/caddy"
"github.com/miekg/dns"
meta "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -195,14 +194,11 @@ func ParseStanza(c *caddy.Controller) (*Kubernetes, error) {
k8s.Fall.SetZonesFromArgs(c.RemainingArgs())
case "upstream":
args := c.RemainingArgs()
if len(args) == 0 {
return nil, c.ArgErr()
}
ups, err := dnsutil.ParseHostPortOrFile(args...)
u, err := upstream.NewUpstream(args)
if err != nil {
return nil, err
}
k8s.Proxy = proxy.NewLookup(ups)
k8s.Upstream = u
case "ttl":
args := c.RemainingArgs()
if len(args) == 0 {

View File

@@ -7,6 +7,7 @@ import (
"github.com/coredns/coredns/plugin/pkg/fall"
"github.com/coredns/coredns/plugin/proxy"
"github.com/mholt/caddy"
meta "k8s.io/apimachinery/pkg/apis/meta/v1"
)
@@ -463,7 +464,10 @@ kubernetes cluster.local`,
t.Errorf("Test %d: Expected kubernetes controller to be initialized with fallthrough '%v'. Instead found fallthrough '%v' for input '%s'", i, test.expectedFallthrough, k8sController.Fall, test.input)
}
// upstream
foundUpstreams := k8sController.Proxy.Upstreams
var foundUpstreams *[]proxy.Upstream
if k8sController.Upstream.Forward != nil {
foundUpstreams = k8sController.Upstream.Forward.Upstreams
}
if test.expectedUpstreams == nil {
if foundUpstreams != nil {
t.Errorf("Test %d: Expected kubernetes controller to not be initialized with upstreams for input '%s'", i, test.input)