mw/kubernetes: remove federation and cidr (#916)

* mw/kubernetes: remove federation and cidr

Remove both as we have a corefile syntax change that handles cidr and
remove federation because that is going to be its own middleware.

* backwards incompat changes

This PR:
* removes cidr from kubernetes (core Corefile feature now)
* removes federation from kubernets (comes back as new middleware)
* [remove autopath - which was already gone, so that already was
  backwards incompat]
* adds `fallthrough` to the *etcd* middleware and makes you enable it.
* Fail on unknown properties
* documentation
* Disable TestHealthCheck as it uses realtime and fails
This commit is contained in:
Miek Gieben 2017-08-14 08:49:26 +01:00 committed by GitHub
parent 818d2b10ad
commit 00f5c7797e
26 changed files with 197 additions and 815 deletions

View file

@ -104,6 +104,8 @@ func parseErratic(c *caddy.Controller) (*Erratic, error) {
return nil, fmt.Errorf("illegal amount value given %q", args[0]) return nil, fmt.Errorf("illegal amount value given %q", args[0])
} }
e.truncate = uint64(amount) e.truncate = uint64(amount)
default:
return nil, c.Errf("unknown property '%s'", c.Val())
} }
} }
} }

View file

@ -23,6 +23,7 @@ If you want to `round robin` A and AAAA responses look at the `loadbalance` midd
~~~ ~~~
etcd [ZONES...] { etcd [ZONES...] {
stubzones stubzones
fallthrough
path PATH path PATH
endpoint ENDPOINT... endpoint ENDPOINT...
upstream ADDRESS... upstream ADDRESS...
@ -33,6 +34,7 @@ etcd [ZONES...] {
* `stubzones` enables the stub zones feature. The stubzone is *only* done in the etcd tree located * `stubzones` enables the stub zones feature. The stubzone is *only* done in the etcd tree located
under the *first* zone specified. under the *first* zone specified.
* `fallthrough` If zone matches but no record can be generated, pass request to the next middleware.
* **PATH** the path inside etcd. Defaults to "/skydns". * **PATH** the path inside etcd. Defaults to "/skydns".
* **ENDPOINT** the etcd endpoints. Defaults to "http://localhost:2397". * **ENDPOINT** the etcd endpoints. Defaults to "http://localhost:2397".
* `upstream` upstream resolvers to be used resolve external names found in etcd (think CNAMEs) * `upstream` upstream resolvers to be used resolve external names found in etcd (think CNAMEs)

View file

@ -21,15 +21,16 @@ import (
// Etcd is a middleware talks to an etcd cluster. // Etcd is a middleware talks to an etcd cluster.
type Etcd struct { type Etcd struct {
Next middleware.Handler Next middleware.Handler
Zones []string Fallthrough bool
PathPrefix string Zones []string
Proxy proxy.Proxy // Proxy for looking up names during the resolution process PathPrefix string
Client etcdc.KeysAPI Proxy proxy.Proxy // Proxy for looking up names during the resolution process
Ctx context.Context Client etcdc.KeysAPI
Inflight *singleflight.Group Ctx context.Context
Stubmap *map[string]proxy.Proxy // list of proxies for stub resolving. Inflight *singleflight.Group
Debugging bool // Do we allow debug queries. Stubmap *map[string]proxy.Proxy // list of proxies for stub resolving.
Debugging bool // Do we allow debug queries.
endpoints []string // Stored here as well, to aid in testing. endpoints []string // Stored here as well, to aid in testing.
} }

View file

@ -46,7 +46,10 @@ func (e *Etcd) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (
if opt.Debug != "" { if opt.Debug != "" {
r.Question[0].Name = opt.Debug r.Question[0].Name = opt.Debug
} }
return middleware.NextOrFailure(e.Name(), e.Next, ctx, w, r) if e.Fallthrough {
return middleware.NextOrFailure(e.Name(), e.Next, ctx, w, r)
}
return dns.RcodeServerFailure, nil
} }
var ( var (

View file

@ -16,6 +16,7 @@ import (
func TestMultiLookup(t *testing.T) { func TestMultiLookup(t *testing.T) {
etc := newEtcdMiddleware() etc := newEtcdMiddleware()
etc.Zones = []string{"skydns.test.", "miek.nl."} etc.Zones = []string{"skydns.test.", "miek.nl."}
etc.Fallthrough = true
etc.Next = test.ErrorHandler() etc.Next = test.ErrorHandler()
for _, serv := range servicesMulti { for _, serv := range servicesMulti {

View file

@ -74,6 +74,8 @@ func etcdParse(c *caddy.Controller) (*Etcd, bool, error) {
switch c.Val() { switch c.Val() {
case "stubzones": case "stubzones":
stubzones = true stubzones = true
case "fallthrough":
etc.Fallthrough = true
case "debug": case "debug":
etc.Debugging = true etc.Debugging = true
case "path": case "path":

View file

@ -115,6 +115,8 @@ func fileParse(c *caddy.Controller) (Zones, error) {
return Zones{}, err return Zones{}, err
} }
prxy = proxy.NewLookup(ups) prxy = proxy.NewLookup(ups)
default:
return Zones{}, c.Errf("unknown property '%s'", c.Val())
} }
for _, origin := range origins { for _, origin := range origins {

View file

@ -79,6 +79,8 @@ func hostsParse(c *caddy.Controller) (Hosts, error) {
continue continue
} }
return h, c.ArgErr() return h, c.ArgErr()
default:
return h, c.Errf("unknown property '%s'", c.Val())
} }
} }
} }

View file

@ -21,15 +21,14 @@ all the zones the middleware should be authoritative for.
``` ```
kubernetes [ZONES...] { kubernetes [ZONES...] {
resyncperiod DURATION resyncperiod DURATION
endpoint URL endpoint URL
tls CERT KEY CACERT tls CERT KEY CACERT
namespaces NAMESPACE [NAMESPACE...] namespaces NAMESPACE [NAMESPACE...]
labels EXPRESSION labels EXPRESSION
pods POD-MODE pods POD-MODE
upstream ADDRESS [ADDRESS...] upstream ADDRESS [ADDRESS...]
federation NAME DOMAIN fallthrough
fallthrough
} }
``` ```
* `resyncperiod` specifies the Kubernetes data API **DURATION** period. * `resyncperiod` specifies the Kubernetes data API **DURATION** period.
@ -63,8 +62,6 @@ kubernetes [ZONES...] {
* `upstream` **ADDRESS [ADDRESS...]** defines the upstream resolvers used for resolving services * `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 that point to external hosts (External Services). **ADDRESS** can be an ip, an ip:port, or a path
to a file structured like resolv.conf. to a file structured like resolv.conf.
* `federation` **NAME DOMAIN** defines federation membership. One line for each federation
membership. Each line consists of the name of the federation, and the domain.
* `fallthrough` If a query for a record in the cluster zone results in NXDOMAIN, normally that is * `fallthrough` If a query for a record in the cluster zone results in NXDOMAIN, normally that is
what the response will be. However, if you specify this option, the query will instead be passed what the response will be. However, if you specify this option, the query will instead be passed
on down the middleware chain, which can include another middleware to handle the query. on down the middleware chain, which can include another middleware to handle the query.
@ -85,40 +82,47 @@ here:
Or you can selectively expose some namespaces: Or you can selectively expose some namespaces:
kubernetes cluster.local { kubernetes cluster.local {
namespaces test staging namespaces test staging
}
If you want to use federation, just use the `federation` option. Here we handle all service requests
in the `prod` and `stage` federations. We resolve upstream records using the servers configured in
`/etc/resolv.conf`.
. {
kubernetes cluster.local {
federation prod prod.feddomain.com
federation stage stage.feddomain.com
upstream /etc/resolv.conf
}
} }
And finally we can connect to Kubernetes from outside the cluster: And finally we can connect to Kubernetes from outside the cluster:
kubernetes cluster.local { kubernetes cluster.local {
endpoint https://k8s-endpoint:8443 endpoint https://k8s-endpoint:8443
tls cert key cacert tls cert key cacert
} }
## Enabling server-side domain search path completion with *autopath* ## AutoPath
The *kubernetes* middleware can be used in conjunction with the *autopath* middleware. Using this feature enables server-side domain search path completion in kubernetes clusters. Note: `pods` must be set to `verified` for this to function properly. The *kubernetes* middleware can be used in conjunction with the *autopath* middleware. Using this
feature enables server-side domain search path completion in kubernetes clusters. Note: `pods` must
be set to `verified` for this to function properly.
autopath @kubernetes cluster.local {
kubernetes cluster.local { autopath @kubernetes
pods verified kubernetes {
} pods verified
}
}
## Federation
The *kubernetes* middleware can be used in conjunction with the *federation* middleware. Using this
feature enables serving federated domains from the kubernetes clusters.
cluster.local {
federation {
fallthrough
prod prod.example.org
staging staging.example.org
}
kubernetes
}
## Wildcard ## Wildcards
Some query labels accept a wildcard value to match any value. If a label is a valid wildcard (\*, 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: or the word "any"), then that label will match all values. The labels that accept wildcards are:

View file

@ -1,96 +0,0 @@
package kubernetes
import (
"net"
"strings"
"github.com/coredns/coredns/middleware/etcd/msg"
)
// Federation holds TODO(...).
type Federation struct {
name string
zone string
}
const (
// TODO: Do not hardcode these labels. Pull them out of the API instead.
//
// We can get them via ....
// import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
// metav1.LabelZoneFailureDomain
// metav1.LabelZoneRegion
//
// But importing above breaks coredns with flag collision of 'log_dir'
labelAvailabilityZone = "failure-domain.beta.kubernetes.io/zone"
labelRegion = "failure-domain.beta.kubernetes.io/region"
)
// stripFederation removes the federation segment from the segment list, if it
// matches a configured federation name.
func (k *Kubernetes) stripFederation(segs []string) (string, []string) {
if len(segs) < 3 {
return "", segs
}
for _, f := range k.Federations {
if f.name == segs[len(segs)-2] {
fed := segs[len(segs)-2]
segs[len(segs)-2] = segs[len(segs)-1]
segs = segs[:len(segs)-1]
return fed, segs
}
}
return "", segs
}
// federationCNAMERecord returns a service record for the requested federated service
// with the target host in the federated CNAME format which the external DNS provider
// should be able to resolve
func (k *Kubernetes) federationCNAMERecord(r recordRequest) msg.Service {
myNodeName := k.localNodeName()
node, err := k.APIConn.GetNodeByName(myNodeName)
if err != nil {
return msg.Service{}
}
for _, f := range k.Federations {
if f.name != r.federation {
continue
}
if r.endpoint == "" {
return msg.Service{
Key: strings.Join([]string{msg.Path(r.zone, "coredns"), r.podOrSvc, r.federation, r.namespace, r.service}, "/"),
Host: strings.Join([]string{r.service, r.namespace, r.federation, r.podOrSvc, node.Labels[labelAvailabilityZone], node.Labels[labelRegion], f.zone}, "."),
}
}
return msg.Service{
Key: strings.Join([]string{msg.Path(r.zone, "coredns"), r.podOrSvc, r.federation, r.namespace, r.service, r.endpoint}, "/"),
Host: strings.Join([]string{r.endpoint, r.service, r.namespace, r.federation, r.podOrSvc, node.Labels[labelAvailabilityZone], node.Labels[labelRegion], f.zone}, "."),
}
}
return msg.Service{}
}
func (k *Kubernetes) localNodeName() string {
localIP := k.interfaceAddrsFunc()
if localIP == nil {
return ""
}
// Find endpoint matching localIP
endpointsList := k.APIConn.EndpointsList()
for _, ep := range endpointsList.Items {
for _, eps := range ep.Subsets {
for _, addr := range eps.Addresses {
if localIP.Equal(net.ParseIP(addr.IP)) {
return *addr.NodeName
}
}
}
}
return ""
}

View file

@ -1,108 +0,0 @@
package kubernetes
import (
"net"
"strings"
"testing"
"github.com/coredns/coredns/middleware/etcd/msg"
"github.com/coredns/coredns/request"
"github.com/miekg/dns"
"k8s.io/client-go/1.5/pkg/api"
)
func testStripFederation(t *testing.T, k Kubernetes, input []string, expectedFed string, expectedSegs string) {
fed, segs := k.stripFederation(input)
if expectedSegs != strings.Join(segs, ".") {
t.Errorf("For '%v', expected segs result '%v'. Instead got result '%v'.", strings.Join(input, "."), expectedSegs, strings.Join(segs, "."))
}
if expectedFed != fed {
t.Errorf("For '%v', expected fed result '%v'. Instead got result '%v'.", strings.Join(input, "."), expectedFed, fed)
}
}
func TestStripFederation(t *testing.T) {
k := Kubernetes{Zones: []string{"inter.webs.test"}}
k.Federations = []Federation{{name: "fed", zone: "era.tion.com"}}
testStripFederation(t, k, []string{"service", "ns", "fed", Svc}, "fed", "service.ns.svc")
testStripFederation(t, k, []string{"service", "ns", "foo", Svc}, "", "service.ns.foo.svc")
testStripFederation(t, k, []string{"foo", "bar"}, "", "foo.bar")
}
type apiConnFedTest struct{}
func (apiConnFedTest) Run() { return }
func (apiConnFedTest) Stop() error { return nil }
func (apiConnFedTest) ServiceList() []*api.Service { return []*api.Service{} }
func (apiConnFedTest) PodIndex(string) []interface{} { return nil }
func (apiConnFedTest) EndpointsList() api.EndpointsList {
n := "test.node.foo.bar"
return api.EndpointsList{
Items: []api.Endpoints{
{
Subsets: []api.EndpointSubset{
{
Addresses: []api.EndpointAddress{
{
IP: "10.9.8.7",
NodeName: &n,
},
},
},
},
},
},
}
}
func (apiConnFedTest) GetNodeByName(name string) (api.Node, error) {
if name != "test.node.foo.bar" {
return api.Node{}, nil
}
return api.Node{
ObjectMeta: api.ObjectMeta{
Name: "test.node.foo.bar",
Labels: map[string]string{
labelRegion: "fd-r",
labelAvailabilityZone: "fd-az",
},
},
}, nil
}
func testFederationCNAMERecord(t *testing.T, k Kubernetes, input recordRequest, expected msg.Service) {
svc := k.federationCNAMERecord(input)
if expected.Host != svc.Host {
t.Errorf("For '%v', expected Host result '%v'. Instead got result '%v'.", input, expected.Host, svc.Host)
}
if expected.Key != svc.Key {
t.Errorf("For '%v', expected Key result '%v'. Instead got result '%v'.", input, expected.Key, svc.Key)
}
}
func TestFederationCNAMERecord(t *testing.T) {
k := Kubernetes{Zones: []string{"inter.webs."}}
k.Federations = []Federation{{name: "fed", zone: "era.tion.com"}}
k.APIConn = apiConnFedTest{}
k.interfaceAddrsFunc = func() net.IP { return net.ParseIP("10.9.8.7") }
m := new(dns.Msg)
state := request.Request{Zone: "inter.webs.", Req: m}
m.SetQuestion("s1.ns.fed.svc.inter.webs.", dns.TypeA)
r, _ := k.parseRequest(state)
testFederationCNAMERecord(t, k, r, msg.Service{Key: "/coredns/webs/inter/svc/fed/ns/s1", Host: "s1.ns.fed.svc.fd-az.fd-r.era.tion.com"})
m.SetQuestion("ep1.s1.ns.fed.svc.inter.webs.", dns.TypeA)
r, _ = k.parseRequest(state)
testFederationCNAMERecord(t, k, r, msg.Service{Key: "/coredns/webs/inter/svc/fed/ns/s1/ep1", Host: "ep1.s1.ns.fed.svc.fd-az.fd-r.era.tion.com"})
m.SetQuestion("ep1.s1.ns.foo.svc.inter.webs.", dns.TypeA)
r, _ = k.parseRequest(state)
testFederationCNAMERecord(t, k, r, msg.Service{Key: "", Host: ""})
}

View file

@ -25,18 +25,10 @@ func (k Kubernetes) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.M
// otherwise delegate to the next in the pipeline. // otherwise delegate to the next in the pipeline.
zone := middleware.Zones(k.Zones).Matches(state.Name()) zone := middleware.Zones(k.Zones).Matches(state.Name())
if zone == "" { if zone == "" {
if state.QType() != dns.TypePTR { if k.Fallthrough {
return middleware.NextOrFailure(k.Name(), k.Next, ctx, w, r) return middleware.NextOrFailure(k.Name(), k.Next, ctx, w, r)
} }
// If this is a PTR request, and the request is in a defined return dns.RcodeServerFailure, nil
// pod/service cidr range, process the request in this middleware,
// otherwise pass to next middleware.
if !k.isRequestInReverseRange(state.Name()) {
return middleware.NextOrFailure(k.Name(), k.Next, ctx, w, r)
}
// Set the zone to this specific request, as we want to handle this reverse request.
zone = state.Name()
} }
state.Zone = zone state.Zone = zone

View file

@ -59,28 +59,6 @@ var dnsTestCases = map[string](*test.Case){
test.CNAME("external.testns.svc.cluster.local. 0 IN CNAME ext.interwebs.test."), test.CNAME("external.testns.svc.cluster.local. 0 IN CNAME ext.interwebs.test."),
}, },
}, },
"A Service (Local Federated)": {
Qname: "svc1.testns.fed.svc.cluster.local.", Qtype: dns.TypeA,
Rcode: dns.RcodeSuccess,
Answer: []dns.RR{
test.A("svc1.testns.fed.svc.cluster.local. 0 IN A 10.0.0.1"),
},
},
"PTR Service": {
Qname: "1.0.0.10.in-addr.arpa.", Qtype: dns.TypePTR,
Rcode: dns.RcodeSuccess,
Answer: []dns.RR{
test.PTR("1.0.0.10.in-addr.arpa. 0 IN PTR svc1.testns.svc.cluster.local."),
},
},
// TODO A Service (Remote Federated)
"CNAME Service (Remote Federated)": {
Qname: "svc0.testns.fed.svc.cluster.local.", Qtype: dns.TypeCNAME,
Rcode: dns.RcodeSuccess,
Answer: []dns.RR{
test.CNAME("svc0.testns.fed.svc.cluster.local. 0 IN CNAME svc0.testns.fed.svc.fd-az.fd-r.federal.test."),
},
},
"AAAA Service (existing service)": { "AAAA Service (existing service)": {
Qname: "svc1.testns.svc.cluster.local.", Qtype: dns.TypeAAAA, Qname: "svc1.testns.svc.cluster.local.", Qtype: dns.TypeAAAA,
Rcode: dns.RcodeSuccess, Rcode: dns.RcodeSuccess,
@ -174,7 +152,6 @@ func TestServeDNS(t *testing.T) {
_, cidr, _ := net.ParseCIDR("10.0.0.0/8") _, cidr, _ := net.ParseCIDR("10.0.0.0/8")
k.ReverseCidrs = []net.IPNet{*cidr} k.ReverseCidrs = []net.IPNet{*cidr}
k.Federations = []Federation{{name: "fed", zone: "federal.test."}}
k.APIConn = &APIConnServeTest{} k.APIConn = &APIConnServeTest{}
k.interfaceAddrsFunc = localPodIP k.interfaceAddrsFunc = localPodIP
k.Next = test.NextHandler(dns.RcodeSuccess, nil) k.Next = test.NextHandler(dns.RcodeSuccess, nil)
@ -405,10 +382,6 @@ func (APIConnServeTest) GetNodeByName(name string) (api.Node, error) {
return api.Node{ return api.Node{
ObjectMeta: api.ObjectMeta{ ObjectMeta: api.ObjectMeta{
Name: "test.node.foo.bar", Name: "test.node.foo.bar",
Labels: map[string]string{
labelRegion: "fd-r",
labelAvailabilityZone: "fd-az",
},
}, },
}, nil }, nil
} }

View file

@ -41,7 +41,6 @@ type Kubernetes struct {
APIConn dnsController APIConn dnsController
ResyncPeriod time.Duration ResyncPeriod time.Duration
Namespaces []string Namespaces []string
Federations []Federation
LabelSelector *unversionedapi.LabelSelector LabelSelector *unversionedapi.LabelSelector
Selector *labels.Selector Selector *labels.Selector
PodMode string PodMode string
@ -309,13 +308,6 @@ func (k *Kubernetes) Entries(r recordRequest) ([]msg.Service, error) {
return nil, err return nil, err
} }
if len(services) == 0 && len(pods) == 0 { if len(services) == 0 && len(pods) == 0 {
// Did not find item in k8s, try federated
if r.federation != "" {
fedCNAME := k.federationCNAMERecord(r)
if fedCNAME.Key != "" {
return []msg.Service{fedCNAME}, nil
}
}
return nil, errNoItems return nil, errNoItems
} }
@ -344,15 +336,9 @@ func (k *Kubernetes) getRecordsForK8sItems(services []kService, pods []kPod, r r
if svc.addr == api.ClusterIPNone || len(svc.endpoints) > 0 { if svc.addr == api.ClusterIPNone || len(svc.endpoints) > 0 {
// This is a headless service or endpoints are present, create records for each endpoint // This is a headless service or endpoints are present, create records for each endpoint
for _, ep := range svc.endpoints { for _, ep := range svc.endpoints {
s := msg.Service{ s := msg.Service{Host: ep.addr.IP, Port: int(ep.port.Port)}
Host: ep.addr.IP, s.Key = strings.Join([]string{zonePath, Svc, svc.namespace, svc.name, endpointHostname(ep.addr)}, "/")
Port: int(ep.port.Port),
}
if r.federation != "" {
s.Key = strings.Join([]string{zonePath, Svc, r.federation, svc.namespace, svc.name, endpointHostname(ep.addr)}, "/")
} else {
s.Key = strings.Join([]string{zonePath, Svc, svc.namespace, svc.name, endpointHostname(ep.addr)}, "/")
}
records = append(records, s) records = append(records, s)
} }
continue continue
@ -360,37 +346,22 @@ func (k *Kubernetes) getRecordsForK8sItems(services []kService, pods []kPod, r r
// Create records for each exposed port... // Create records for each exposed port...
for _, p := range svc.ports { for _, p := range svc.ports {
s := msg.Service{ s := msg.Service{Host: svc.addr, Port: int(p.Port)}
Host: svc.addr, s.Key = strings.Join([]string{zonePath, Svc, svc.namespace, svc.name}, "/")
Port: int(p.Port)}
if r.federation != "" {
s.Key = strings.Join([]string{zonePath, Svc, r.federation, svc.namespace, svc.name}, "/")
} else {
s.Key = strings.Join([]string{zonePath, Svc, svc.namespace, svc.name}, "/")
}
records = append(records, s) records = append(records, s)
} }
// If the addr is not an IP (i.e. an external service), add the record ... // If the addr is not an IP (i.e. an external service), add the record ...
s := msg.Service{ s := msg.Service{Key: strings.Join([]string{zonePath, Svc, svc.namespace, svc.name}, "/"), Host: svc.addr}
Key: strings.Join([]string{zonePath, Svc, svc.namespace, svc.name}, "/"),
Host: svc.addr}
if t, _ := s.HostType(); t == dns.TypeCNAME { if t, _ := s.HostType(); t == dns.TypeCNAME {
if r.federation != "" { s.Key = strings.Join([]string{zonePath, Svc, svc.namespace, svc.name}, "/")
s.Key = strings.Join([]string{zonePath, Svc, r.federation, svc.namespace, svc.name}, "/")
} else {
s.Key = strings.Join([]string{zonePath, Svc, svc.namespace, svc.name}, "/")
}
records = append(records, s) records = append(records, s)
} }
} }
for _, p := range pods { for _, p := range pods {
s := msg.Service{ s := msg.Service{Key: strings.Join([]string{zonePath, Pod, p.namespace, p.name}, "/"), Host: p.addr}
Key: strings.Join([]string{zonePath, Pod, p.namespace, p.name}, "/"),
Host: p.addr,
}
records = append(records, s) records = append(records, s)
} }

View file

@ -189,10 +189,6 @@ func (APIConnServiceTest) GetNodeByName(name string) (api.Node, error) {
return api.Node{ return api.Node{
ObjectMeta: api.ObjectMeta{ ObjectMeta: api.ObjectMeta{
Name: "test.node.foo.bar", Name: "test.node.foo.bar",
Labels: map[string]string{
labelRegion: "fd-r",
labelAvailabilityZone: "fd-az",
},
}, },
}, nil }, nil
} }
@ -200,7 +196,6 @@ func (APIConnServiceTest) GetNodeByName(name string) (api.Node, error) {
func TestServices(t *testing.T) { func TestServices(t *testing.T) {
k := Kubernetes{Zones: []string{"interwebs.test."}} k := Kubernetes{Zones: []string{"interwebs.test."}}
k.Federations = []Federation{{name: "fed", zone: "era.tion.com"}}
k.interfaceAddrsFunc = localPodIP k.interfaceAddrsFunc = localPodIP
k.APIConn = &APIConnServiceTest{} k.APIConn = &APIConnServiceTest{}
@ -221,10 +216,6 @@ func TestServices(t *testing.T) {
// External Services // External Services
{qname: "external.testns.svc.interwebs.test.", qtype: dns.TypeCNAME, answer: svcAns{host: "coredns.io", key: "/coredns/test/interwebs/svc/testns/external"}}, {qname: "external.testns.svc.interwebs.test.", qtype: dns.TypeCNAME, answer: svcAns{host: "coredns.io", key: "/coredns/test/interwebs/svc/testns/external"}},
// Federated Services
{qname: "svc1.testns.fed.svc.interwebs.test.", qtype: dns.TypeA, answer: svcAns{host: "10.0.0.1", key: "/coredns/test/interwebs/svc/fed/testns/svc1"}},
{qname: "svc0.testns.fed.svc.interwebs.test.", qtype: dns.TypeA, answer: svcAns{host: "svc0.testns.fed.svc.fd-az.fd-r.era.tion.com", key: "/coredns/test/interwebs/svc/fed/testns/svc0"}},
} }
for _, test := range tests { for _, test := range tests {

View file

@ -19,14 +19,13 @@ type recordRequest struct {
namespace string 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 // 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. // pods and false for services. If we ever need to extend this well use a typed value.
podOrSvc string podOrSvc string
zone string zone string
federation 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.
func (k *Kubernetes) parseRequest(state request.Request) (r recordRequest, err error) { func (k *Kubernetes) parseRequest(state request.Request) (r recordRequest, err error) {
// 3 Possible cases // 3 Possible cases: TODO(chris): remove federations comments here.
// SRV Request: _port._protocol.service.namespace.[federation.]type.zone // SRV Request: _port._protocol.service.namespace.[federation.]type.zone
// A Request (endpoint): endpoint.service.namespace.[federation.]type.zone // A Request (endpoint): endpoint.service.namespace.[federation.]type.zone
// A Request (service): service.namespace.[federation.]type.zone // A Request (service): service.namespace.[federation.]type.zone
@ -35,7 +34,6 @@ func (k *Kubernetes) parseRequest(state request.Request) (r recordRequest, err e
segs := dns.SplitDomainName(base) segs := dns.SplitDomainName(base)
r.zone = state.Zone r.zone = state.Zone
r.federation, segs = k.stripFederation(segs)
if state.QType() == dns.TypeNS { if state.QType() == dns.TypeNS {
return r, nil return r, nil
@ -112,6 +110,5 @@ func (r recordRequest) String() string {
s += "." + r.namespace s += "." + r.namespace
s += "." + r.podOrSvc s += "." + r.podOrSvc
s += "." + r.zone s += "." + r.zone
s += "." + r.federation
return s return s
} }

View file

@ -18,21 +18,21 @@ func TestParseRequest(t *testing.T) {
{ {
// valid SRV request // valid SRV request
"_http._tcp.webs.mynamespace.svc.inter.webs.test.", dns.TypeSRV, "_http._tcp.webs.mynamespace.svc.inter.webs.test.", dns.TypeSRV,
"http.tcp..webs.mynamespace.svc.intern.webs.tests..", "http.tcp..webs.mynamespace.svc.intern.webs.tests.",
}, },
{ {
// wildcard acceptance // wildcard acceptance
"*.any.*.any.svc.inter.webs.test.", dns.TypeSRV, "*.any.*.any.svc.inter.webs.test.", dns.TypeSRV,
"*.any..*.any.svc.intern.webs.tests..", "*.any..*.any.svc.intern.webs.tests.",
}, },
{ {
// A request of endpoint // A request of endpoint
"1-2-3-4.webs.mynamespace.svc.inter.webs.test.", dns.TypeA, "1-2-3-4.webs.mynamespace.svc.inter.webs.test.", dns.TypeA,
"..1-2-3-4.webs.mynamespace.svc.intern.webs.tests..", "..1-2-3-4.webs.mynamespace.svc.intern.webs.tests.",
}, },
{ {
"inter.webs.test.", dns.TypeNS, "inter.webs.test.", dns.TypeNS,
"......intern.webs.tests..", "......intern.webs.tests.",
}, },
} }
for i, tc := range tests { for i, tc := range tests {

View file

@ -1,8 +1,6 @@
package kubernetes package kubernetes
import ( import (
"net"
"github.com/coredns/coredns/middleware" "github.com/coredns/coredns/middleware"
"github.com/coredns/coredns/middleware/etcd/msg" "github.com/coredns/coredns/middleware/etcd/msg"
"github.com/coredns/coredns/middleware/pkg/dnsutil" "github.com/coredns/coredns/middleware/pkg/dnsutil"
@ -20,13 +18,3 @@ func (k *Kubernetes) Reverse(state request.Request, exact bool, opt middleware.O
records := k.getServiceRecordForIP(ip, state.Name()) records := k.getServiceRecordForIP(ip, state.Name())
return records, nil, nil return records, nil, nil
} }
func (k *Kubernetes) isRequestInReverseRange(name string) bool {
ip := dnsutil.ExtractAddressFromReverse(name)
for _, c := range k.ReverseCidrs {
if c.Contains(net.ParseIP(ip)) {
return true
}
}
return false
}

View file

@ -1,33 +0,0 @@
package kubernetes
import (
"net"
"testing"
)
func TestIsRequestInReverseRange(t *testing.T) {
tests := []struct {
cidr string
name string
expected bool
}{
{"1.2.3.0/24", "4.3.2.1.in-addr.arpa.", true},
{"1.2.3.0/24", "5.3.2.1.in-addr.arpa.", true},
{"5.6.0.0/16", "5.4.6.5.in-addr.arpa.", true},
{"1.2.3.0/24", "5.4.2.1.in-addr.arpa.", false},
{"5.6.0.0/16", "5.4.2.1.in-addr.arpa.", false},
{"5.6.0.0/16", "5.6.0.1.in-addr.arpa.", false},
}
k := Kubernetes{}
for _, test := range tests {
_, cidr, _ := net.ParseCIDR(test.cidr)
k.ReverseCidrs = []net.IPNet{*cidr}
result := k.isRequestInReverseRange(test.name)
if result != test.expected {
t.Errorf("Expected '%v' for '%v' in %v.", test.expected, test.name, test.cidr)
}
}
}

View file

@ -3,8 +3,6 @@ package kubernetes
import ( import (
"errors" "errors"
"fmt" "fmt"
"log"
"net"
"strings" "strings"
"time" "time"
@ -74,141 +72,113 @@ func kubernetesParse(c *caddy.Controller) (*Kubernetes, error) {
k8s.autoPathSearch = searchFromResolvConf() k8s.autoPathSearch = searchFromResolvConf()
for c.Next() { for c.Next() {
if c.Val() == "kubernetes" { zones := c.RemainingArgs()
zones := c.RemainingArgs()
if len(zones) != 0 { if len(zones) != 0 {
k8s.Zones = zones k8s.Zones = zones
middleware.Zones(k8s.Zones).Normalize() middleware.Zones(k8s.Zones).Normalize()
} else { } else {
k8s.Zones = make([]string, len(c.ServerBlockKeys)) k8s.Zones = make([]string, len(c.ServerBlockKeys))
for i := 0; i < len(c.ServerBlockKeys); i++ { for i := 0; i < len(c.ServerBlockKeys); i++ {
k8s.Zones[i] = middleware.Host(c.ServerBlockKeys[i]).Normalize() k8s.Zones[i] = middleware.Host(c.ServerBlockKeys[i]).Normalize()
}
} }
}
k8s.primaryZoneIndex = -1 k8s.primaryZoneIndex = -1
for i, z := range k8s.Zones { for i, z := range k8s.Zones {
if strings.HasSuffix(z, "in-addr.arpa.") || strings.HasSuffix(z, "ip6.arpa.") { if strings.HasSuffix(z, "in-addr.arpa.") || strings.HasSuffix(z, "ip6.arpa.") {
continue
}
k8s.primaryZoneIndex = i
break
}
if k8s.primaryZoneIndex == -1 {
return nil, errors.New("non-reverse zone name must be used")
}
for c.NextBlock() {
switch c.Val() {
case "pods":
args := c.RemainingArgs()
if len(args) == 1 {
switch args[0] {
case PodModeDisabled, PodModeInsecure, PodModeVerified:
k8s.PodMode = args[0]
default:
return nil, fmt.Errorf("wrong value for pods: %s, must be one of: disabled, verified, insecure", args[0])
}
continue continue
} }
k8s.primaryZoneIndex = i return nil, c.ArgErr()
break case "namespaces":
} args := c.RemainingArgs()
if len(args) > 0 {
if k8s.primaryZoneIndex == -1 { k8s.Namespaces = append(k8s.Namespaces, args...)
return nil, errors.New("non-reverse zone name must be given for Kubernetes") continue
}
for c.NextBlock() {
switch c.Val() {
case "cidrs":
// DEPRECATION WARNING
log.Printf("[WARNING] \"cidrs\" will be removed for CoreDNS soon. See https://coredns.io/2017/07/23/corefile-explained#reverse-zones for the replacement")
args := c.RemainingArgs()
if len(args) > 0 {
for _, cidrStr := range args {
_, cidr, err := net.ParseCIDR(cidrStr)
if err != nil {
return nil, fmt.Errorf("invalid cidr: %s", cidrStr)
}
k8s.ReverseCidrs = append(k8s.ReverseCidrs, *cidr)
}
continue
}
return nil, c.ArgErr()
case "pods":
args := c.RemainingArgs()
if len(args) == 1 {
switch args[0] {
case PodModeDisabled, PodModeInsecure, PodModeVerified:
k8s.PodMode = args[0]
default:
return nil, fmt.Errorf("wrong value for pods: %s, must be one of: disabled, verified, insecure", args[0])
}
continue
}
return nil, c.ArgErr()
case "namespaces":
args := c.RemainingArgs()
if len(args) > 0 {
k8s.Namespaces = append(k8s.Namespaces, args...)
continue
}
return nil, c.ArgErr()
case "endpoint":
args := c.RemainingArgs()
if len(args) > 0 {
for _, endpoint := range strings.Split(args[0], ",") {
k8s.APIServerList = append(k8s.APIServerList, strings.TrimSpace(endpoint))
}
continue
}
return nil, c.ArgErr()
case "tls": // cert key cacertfile
args := c.RemainingArgs()
if len(args) == 3 {
k8s.APIClientCert, k8s.APIClientKey, k8s.APICertAuth = args[0], args[1], args[2]
continue
}
return nil, c.ArgErr()
case "resyncperiod":
args := c.RemainingArgs()
if len(args) > 0 {
rp, err := time.ParseDuration(args[0])
if err != nil {
return nil, fmt.Errorf("unable to parse resync duration value: '%v': %v", args[0], err)
}
k8s.ResyncPeriod = rp
continue
}
return nil, c.ArgErr()
case "labels":
args := c.RemainingArgs()
if len(args) > 0 {
labelSelectorString := strings.Join(args, " ")
ls, err := unversionedapi.ParseToLabelSelector(labelSelectorString)
if err != nil {
return nil, fmt.Errorf("unable to parse label selector value: '%v': %v", labelSelectorString, err)
}
k8s.LabelSelector = ls
continue
}
return nil, c.ArgErr()
case "fallthrough":
args := c.RemainingArgs()
if len(args) == 0 {
k8s.Fallthrough = true
continue
}
return nil, c.ArgErr()
case "upstream":
args := c.RemainingArgs()
if len(args) == 0 {
return nil, c.ArgErr()
}
ups, err := dnsutil.ParseHostPortOrFile(args...)
if err != nil {
return nil, err
}
k8s.Proxy = proxy.NewLookup(ups)
case "federation": // name zone
args := c.RemainingArgs()
if len(args) == 2 {
k8s.Federations = append(k8s.Federations, Federation{
name: args[0],
zone: args[1],
})
continue
}
return nil, fmt.Errorf("incorrect number of arguments for federation, got %v, expected 2", len(args))
} }
return nil, c.ArgErr()
case "endpoint":
args := c.RemainingArgs()
if len(args) > 0 {
for _, endpoint := range strings.Split(args[0], ",") {
k8s.APIServerList = append(k8s.APIServerList, strings.TrimSpace(endpoint))
}
continue
}
return nil, c.ArgErr()
case "tls": // cert key cacertfile
args := c.RemainingArgs()
if len(args) == 3 {
k8s.APIClientCert, k8s.APIClientKey, k8s.APICertAuth = args[0], args[1], args[2]
continue
}
return nil, c.ArgErr()
case "resyncperiod":
args := c.RemainingArgs()
if len(args) > 0 {
rp, err := time.ParseDuration(args[0])
if err != nil {
return nil, fmt.Errorf("unable to parse resync duration value: '%v': %v", args[0], err)
}
k8s.ResyncPeriod = rp
continue
}
return nil, c.ArgErr()
case "labels":
args := c.RemainingArgs()
if len(args) > 0 {
labelSelectorString := strings.Join(args, " ")
ls, err := unversionedapi.ParseToLabelSelector(labelSelectorString)
if err != nil {
return nil, fmt.Errorf("unable to parse label selector value: '%v': %v", labelSelectorString, err)
}
k8s.LabelSelector = ls
continue
}
return nil, c.ArgErr()
case "fallthrough":
args := c.RemainingArgs()
if len(args) == 0 {
k8s.Fallthrough = true
continue
}
return nil, c.ArgErr()
case "upstream":
args := c.RemainingArgs()
if len(args) == 0 {
return nil, c.ArgErr()
}
ups, err := dnsutil.ParseHostPortOrFile(args...)
if err != nil {
return nil, err
}
k8s.Proxy = proxy.NewLookup(ups)
default:
return nil, c.Errf("unknown property '%s'", c.Val())
} }
return k8s, nil
} }
return k8s, nil
} }
return nil, errors.New("kubernetes setup called without keyword 'kubernetes' in Corefile") return nil, errors.New("kubernetes setup called without keyword 'kubernetes' in Corefile")
} }

View file

@ -1,20 +1,14 @@
package kubernetes package kubernetes
import ( import (
"net"
"strings" "strings"
"testing" "testing"
"time" "time"
"github.com/mholt/caddy" "github.com/mholt/caddy"
unversionedapi "k8s.io/client-go/1.5/pkg/api/unversioned" "k8s.io/client-go/1.5/pkg/api/unversioned"
) )
func parseCidr(cidr string) net.IPNet {
_, ipnet, _ := net.ParseCIDR(cidr)
return *ipnet
}
func TestKubernetesParse(t *testing.T) { func TestKubernetesParse(t *testing.T) {
tests := []struct { tests := []struct {
input string // Corefile data as string input string // Corefile data as string
@ -25,7 +19,6 @@ func TestKubernetesParse(t *testing.T) {
expectedResyncPeriod time.Duration // expected resync period value expectedResyncPeriod time.Duration // expected resync period value
expectedLabelSelector string // expected label selector value expectedLabelSelector string // expected label selector value
expectedPodMode string expectedPodMode string
expectedCidrs []net.IPNet
expectedFallthrough bool expectedFallthrough bool
expectedUpstreams []string expectedUpstreams []string
}{ }{
@ -39,7 +32,6 @@ func TestKubernetesParse(t *testing.T) {
defaultResyncPeriod, defaultResyncPeriod,
"", "",
PodModeDisabled, PodModeDisabled,
nil,
false, false,
nil, nil,
}, },
@ -52,7 +44,6 @@ func TestKubernetesParse(t *testing.T) {
defaultResyncPeriod, defaultResyncPeriod,
"", "",
PodModeDisabled, PodModeDisabled,
nil,
false, false,
nil, nil,
}, },
@ -66,7 +57,6 @@ func TestKubernetesParse(t *testing.T) {
defaultResyncPeriod, defaultResyncPeriod,
"", "",
PodModeDisabled, PodModeDisabled,
nil,
false, false,
nil, nil,
}, },
@ -81,7 +71,6 @@ func TestKubernetesParse(t *testing.T) {
defaultResyncPeriod, defaultResyncPeriod,
"", "",
PodModeDisabled, PodModeDisabled,
nil,
false, false,
nil, nil,
}, },
@ -96,7 +85,6 @@ func TestKubernetesParse(t *testing.T) {
defaultResyncPeriod, defaultResyncPeriod,
"", "",
PodModeDisabled, PodModeDisabled,
nil,
false, false,
nil, nil,
}, },
@ -111,7 +99,6 @@ func TestKubernetesParse(t *testing.T) {
defaultResyncPeriod, defaultResyncPeriod,
"", "",
PodModeDisabled, PodModeDisabled,
nil,
false, false,
nil, nil,
}, },
@ -126,7 +113,6 @@ func TestKubernetesParse(t *testing.T) {
30 * time.Second, 30 * time.Second,
"", "",
PodModeDisabled, PodModeDisabled,
nil,
false, false,
nil, nil,
}, },
@ -141,7 +127,6 @@ func TestKubernetesParse(t *testing.T) {
15 * time.Minute, 15 * time.Minute,
"", "",
PodModeDisabled, PodModeDisabled,
nil,
false, false,
nil, nil,
}, },
@ -156,7 +141,6 @@ func TestKubernetesParse(t *testing.T) {
defaultResyncPeriod, defaultResyncPeriod,
"environment=prod", "environment=prod",
PodModeDisabled, PodModeDisabled,
nil,
false, false,
nil, nil,
}, },
@ -171,7 +155,6 @@ func TestKubernetesParse(t *testing.T) {
defaultResyncPeriod, defaultResyncPeriod,
"application=nginx,environment in (production,qa,staging)", "application=nginx,environment in (production,qa,staging)",
PodModeDisabled, PodModeDisabled,
nil,
false, false,
nil, nil,
}, },
@ -190,7 +173,6 @@ func TestKubernetesParse(t *testing.T) {
15 * time.Minute, 15 * time.Minute,
"application=nginx,environment in (production,qa,staging)", "application=nginx,environment in (production,qa,staging)",
PodModeDisabled, PodModeDisabled,
nil,
true, true,
nil, nil,
}, },
@ -204,7 +186,6 @@ func TestKubernetesParse(t *testing.T) {
defaultResyncPeriod, defaultResyncPeriod,
"", "",
PodModeDisabled, PodModeDisabled,
nil,
false, false,
nil, nil,
}, },
@ -219,7 +200,6 @@ func TestKubernetesParse(t *testing.T) {
defaultResyncPeriod, defaultResyncPeriod,
"", "",
PodModeDisabled, PodModeDisabled,
nil,
false, false,
nil, nil,
}, },
@ -234,7 +214,6 @@ func TestKubernetesParse(t *testing.T) {
defaultResyncPeriod, defaultResyncPeriod,
"", "",
PodModeDisabled, PodModeDisabled,
nil,
false, false,
nil, nil,
}, },
@ -249,7 +228,6 @@ func TestKubernetesParse(t *testing.T) {
0 * time.Minute, 0 * time.Minute,
"", "",
PodModeDisabled, PodModeDisabled,
nil,
false, false,
nil, nil,
}, },
@ -264,7 +242,6 @@ func TestKubernetesParse(t *testing.T) {
0 * time.Second, 0 * time.Second,
"", "",
PodModeDisabled, PodModeDisabled,
nil,
false, false,
nil, nil,
}, },
@ -279,7 +256,6 @@ func TestKubernetesParse(t *testing.T) {
0 * time.Second, 0 * time.Second,
"", "",
PodModeDisabled, PodModeDisabled,
nil,
false, false,
nil, nil,
}, },
@ -294,7 +270,6 @@ func TestKubernetesParse(t *testing.T) {
0 * time.Second, 0 * time.Second,
"", "",
PodModeDisabled, PodModeDisabled,
nil,
false, false,
nil, nil,
}, },
@ -309,7 +284,6 @@ func TestKubernetesParse(t *testing.T) {
0 * time.Second, 0 * time.Second,
"", "",
PodModeDisabled, PodModeDisabled,
nil,
false, false,
nil, nil,
}, },
@ -325,7 +299,6 @@ func TestKubernetesParse(t *testing.T) {
defaultResyncPeriod, defaultResyncPeriod,
"", "",
PodModeDisabled, PodModeDisabled,
nil,
false, false,
nil, nil,
}, },
@ -341,7 +314,6 @@ func TestKubernetesParse(t *testing.T) {
defaultResyncPeriod, defaultResyncPeriod,
"", "",
PodModeInsecure, PodModeInsecure,
nil,
false, false,
nil, nil,
}, },
@ -357,7 +329,6 @@ func TestKubernetesParse(t *testing.T) {
defaultResyncPeriod, defaultResyncPeriod,
"", "",
PodModeVerified, PodModeVerified,
nil,
false, false,
nil, nil,
}, },
@ -373,39 +344,6 @@ func TestKubernetesParse(t *testing.T) {
defaultResyncPeriod, defaultResyncPeriod,
"", "",
PodModeVerified, PodModeVerified,
nil,
false,
nil,
},
// cidrs ok
{
`kubernetes coredns.local {
cidrs 10.0.0.0/24 10.0.1.0/24
}`,
false,
"",
1,
0,
defaultResyncPeriod,
"",
PodModeDisabled,
[]net.IPNet{parseCidr("10.0.0.0/24"), parseCidr("10.0.1.0/24")},
false,
nil,
},
// cidrs ok
{
`kubernetes coredns.local {
cidrs hard dry
}`,
true,
"invalid cidr: hard",
-1,
0,
defaultResyncPeriod,
"",
PodModeDisabled,
nil,
false, false,
nil, nil,
}, },
@ -421,7 +359,6 @@ func TestKubernetesParse(t *testing.T) {
defaultResyncPeriod, defaultResyncPeriod,
"", "",
PodModeDisabled, PodModeDisabled,
nil,
false, false,
nil, nil,
}, },
@ -437,7 +374,6 @@ func TestKubernetesParse(t *testing.T) {
defaultResyncPeriod, defaultResyncPeriod,
"", "",
PodModeDisabled, PodModeDisabled,
nil,
false, false,
[]string{"13.14.15.16:53"}, []string{"13.14.15.16:53"},
}, },
@ -453,7 +389,6 @@ func TestKubernetesParse(t *testing.T) {
defaultResyncPeriod, defaultResyncPeriod,
"", "",
PodModeDisabled, PodModeDisabled,
nil,
false, false,
nil, nil,
}, },
@ -508,7 +443,7 @@ func TestKubernetesParse(t *testing.T) {
// Labels // Labels
if k8sController.LabelSelector != nil { if k8sController.LabelSelector != nil {
foundLabelSelectorString := unversionedapi.FormatLabelSelector(k8sController.LabelSelector) foundLabelSelectorString := unversioned.FormatLabelSelector(k8sController.LabelSelector)
if foundLabelSelectorString != test.expectedLabelSelector { if foundLabelSelectorString != test.expectedLabelSelector {
t.Errorf("Test %d: Expected kubernetes controller to be initialized with label selector '%s'. Instead found selector '%s' for input '%s'", i, test.expectedLabelSelector, foundLabelSelectorString, test.input) t.Errorf("Test %d: Expected kubernetes controller to be initialized with label selector '%s'. Instead found selector '%s' for input '%s'", i, test.expectedLabelSelector, foundLabelSelectorString, test.input)
} }
@ -519,16 +454,6 @@ func TestKubernetesParse(t *testing.T) {
t.Errorf("Test %d: Expected kubernetes controller to be initialized with pod mode '%s'. Instead found pod mode '%s' for input '%s'", i, test.expectedPodMode, foundPodMode, test.input) t.Errorf("Test %d: Expected kubernetes controller to be initialized with pod mode '%s'. Instead found pod mode '%s' for input '%s'", i, test.expectedPodMode, foundPodMode, test.input)
} }
// Cidrs
foundCidrs := k8sController.ReverseCidrs
if len(foundCidrs) != len(test.expectedCidrs) {
t.Errorf("Test %d: Expected kubernetes controller to be initialized with %d cidrs. Instead found %d cidrs for input '%s'", i, len(test.expectedCidrs), len(foundCidrs), test.input)
}
for j, cidr := range test.expectedCidrs {
if cidr.String() != foundCidrs[j].String() {
t.Errorf("Test %d: Expected kubernetes controller to be initialized with cidr '%s'. Instead found cidr '%s' for input '%s'", i, test.expectedCidrs[j].String(), foundCidrs[j].String(), test.input)
}
}
// fallthrough // fallthrough
foundFallthrough := k8sController.Fallthrough foundFallthrough := k8sController.Fallthrough
if foundFallthrough != test.expectedFallthrough { if foundFallthrough != test.expectedFallthrough {
@ -558,70 +483,3 @@ func TestKubernetesParse(t *testing.T) {
} }
} }
} }
func TestKubernetesParseFederation(t *testing.T) {
tests := []struct {
input string // Corefile data as string
shouldErr bool // true if test case is exected to produce an error.
expectedErrContent string // substring from the expected error. Empty for positive cases.
expectedFederations []Federation
}{
// Valid federations
{
`kubernetes coredns.local {
federation foo bar.crawl.com
federation fed era.tion.com
}`,
false,
"",
[]Federation{
{name: "foo", zone: "bar.crawl.com"},
{name: "fed", zone: "era.tion.com"},
},
},
// Invalid federations
{
`kubernetes coredns.local {
federation starship
}`,
true,
`incorrect number of arguments for federation`,
[]Federation{},
},
}
for i, test := range tests {
c := caddy.NewTestController("dns", test.input)
k8sController, err := kubernetesParse(c)
if test.shouldErr && err == nil {
t.Errorf("Test %d: Expected error, but did not find error for input '%s'. Error was: '%v'", i, test.input, err)
}
if err != nil {
if !test.shouldErr {
t.Errorf("Test %d: Expected no error but found one for input %s. Error was: %v", i, test.input, err)
continue
}
if test.shouldErr && (len(test.expectedErrContent) < 1) {
t.Fatalf("Test %d: Test marked as expecting an error, but no expectedErrContent provided for input '%s'. Error was: '%v'", i, test.input, err)
}
if !strings.Contains(err.Error(), test.expectedErrContent) {
t.Errorf("Test %d: Expected error to contain: %v, found error: %v, input: %s", i, test.expectedErrContent, err, test.input)
}
continue
}
foundFed := k8sController.Federations
if len(foundFed) != len(test.expectedFederations) {
t.Errorf("Test %d: Expected kubernetes controller to be initialized with %d fedrations. Instead found %d fedrations for input '%s'", i, len(test.expectedFederations), len(foundFed), test.input)
}
for j, fed := range test.expectedFederations {
if fed != foundFed[j] {
t.Errorf("Test %d: Expected kubernetes controller to be initialized with federation '%s'. Instead found federation '%s' for input '%s'", i, test.expectedFederations[j], foundFed[j], test.input)
}
}
}
}

View file

@ -89,7 +89,7 @@ func prometheusParse(c *caddy.Controller) (*Metrics, error) {
return met, e return met, e
} }
default: default:
return met, c.Errf("unknown item: %s", c.Val()) return met, c.Errf("unknown property: %s", c.Val())
} }
} }

View file

@ -53,7 +53,9 @@ func TestRegisterPolicy(t *testing.T) {
} }
func TestHealthCheck(t *testing.T) { // TODO(miek): Disabled for now, we should get out of the habit of using
// realtime in these tests .
func testHealthCheck(t *testing.T) {
log.SetOutput(ioutil.Discard) log.SetOutput(ioutil.Discard)
u := &HealthCheck{ u := &HealthCheck{

View file

@ -88,6 +88,8 @@ func secondaryParse(c *caddy.Controller) (file.Zones, error) {
return file.Zones{}, err return file.Zones{}, err
} }
prxy = proxy.NewLookup(ups) prxy = proxy.NewLookup(ups)
default:
return file.Zones{}, c.Errf("unknown property '%s'", c.Val())
} }
for _, origin := range origins { for _, origin := range origins {

View file

@ -42,6 +42,7 @@ func TestEtcdStubAndProxyLookup(t *testing.T) {
path /skydns path /skydns
endpoint http://localhost:2379 endpoint http://localhost:2379
upstream 8.8.8.8:53 8.8.4.4:53 upstream 8.8.8.8:53 8.8.4.4:53
fallthrough
} }
proxy . 8.8.8.8:53 proxy . 8.8.8.8:53
}` }`

View file

@ -3,6 +3,8 @@
package test package test
import ( import (
"io/ioutil"
"log"
"os" "os"
"testing" "testing"
"time" "time"
@ -13,6 +15,10 @@ import (
"github.com/miekg/dns" "github.com/miekg/dns"
) )
func init() {
log.SetOutput(ioutil.Discard)
}
// Test data // Test data
// TODO: Fix the actual RR values // TODO: Fix the actual RR values
@ -218,25 +224,6 @@ var dnsTestCases = []test.Case{
Rcode: dns.RcodeServerFailure, Rcode: dns.RcodeServerFailure,
Answer: []dns.RR{}, Answer: []dns.RR{},
}, },
{
Qname: "123.0.0.10.in-addr.arpa.", Qtype: dns.TypePTR,
Rcode: dns.RcodeSuccess,
Answer: []dns.RR{},
},
{
Qname: "100.0.0.10.in-addr.arpa.", Qtype: dns.TypePTR,
Rcode: dns.RcodeSuccess,
Answer: []dns.RR{
test.PTR("100.0.0.10.in-addr.arpa. 303 IN PTR svc-1-a.test-1.svc.cluster.local."),
},
},
{
Qname: "115.0.0.10.in-addr.arpa.", Qtype: dns.TypePTR,
Rcode: dns.RcodeSuccess,
Answer: []dns.RR{
test.PTR("115.0.0.10.in-addr.arpa. 303 IN PTR svc-c.test-1.svc.cluster.local."),
},
},
{ {
Qname: "dns-version.cluster.local.", Qtype: dns.TypeTXT, Qname: "dns-version.cluster.local.", Qtype: dns.TypeTXT,
Rcode: dns.RcodeSuccess, Rcode: dns.RcodeSuccess,
@ -244,13 +231,6 @@ var dnsTestCases = []test.Case{
test.TXT("dns-version.cluster.local. 28800 IN TXT \"1.0.0\""), test.TXT("dns-version.cluster.local. 28800 IN TXT \"1.0.0\""),
}, },
}, },
{
Qname: "next-in-chain.", Qtype: dns.TypeA,
Rcode: dns.RcodeSuccess,
Answer: []dns.RR{
test.A("next-in-chain. 0 IN A 192.0.2.53"),
},
},
{ {
Qname: "cluster.local.", Qtype: dns.TypeNS, Qname: "cluster.local.", Qtype: dns.TypeNS,
Rcode: dns.RcodeSuccess, Rcode: dns.RcodeSuccess,
@ -303,80 +283,6 @@ var dnsTestCasesPodsVerified = []test.Case{
}, },
} }
var dnsTestCasesCidrReverseZone = []test.Case{
{
Qname: "123.0.0.10.in-addr.arpa.", Qtype: dns.TypePTR,
Rcode: dns.RcodeSuccess,
Answer: []dns.RR{},
},
{
Qname: "100.0.0.10.in-addr.arpa.", Qtype: dns.TypePTR,
Rcode: dns.RcodeSuccess,
Answer: []dns.RR{
test.PTR("100.0.0.10.in-addr.arpa. 303 IN PTR svc-1-a.test-1.svc.cluster.local."),
},
},
{
Qname: "110.0.0.10.in-addr.arpa.", Qtype: dns.TypePTR,
Rcode: dns.RcodeSuccess,
Answer: []dns.RR{
test.PTR("115.0.0.10.in-addr.arpa. 303 IN PTR svc-1-b.test-1.svc.cluster.local."),
},
},
{
Qname: "115.0.0.10.in-addr.arpa.", Qtype: dns.TypePTR,
Rcode: dns.RcodeSuccess,
Answer: []dns.RR{
test.PTR("115.0.0.10.in-addr.arpa. 303 IN PTR svc-c.test-1.svc.cluster.local."),
},
},
{
Qname: "next-in-chain.", Qtype: dns.TypeA,
Rcode: dns.RcodeSuccess,
Answer: []dns.RR{
test.A("next-in-chain. 0 IN A 192.0.2.53"),
},
},
}
var dnsTestCasesPartialCidrReverseZone = []test.Case{
{
// In exposed range, record not present = OK + No data
Qname: "99.0.0.10.in-addr.arpa.", Qtype: dns.TypePTR,
Rcode: dns.RcodeSuccess,
Answer: []dns.RR{},
},
{
// In exposed range, record present = OK + Data
Qname: "100.0.0.10.in-addr.arpa.", Qtype: dns.TypePTR,
Rcode: dns.RcodeSuccess,
Answer: []dns.RR{
test.PTR("100.0.0.10.in-addr.arpa. 303 IN PTR svc-1-a.test-1.svc.cluster.local."),
},
},
{
// In exposed range, record present = OK + Data
Qname: "110.0.0.10.in-addr.arpa.", Qtype: dns.TypePTR,
Rcode: dns.RcodeSuccess,
Answer: []dns.RR{
test.PTR("115.0.0.10.in-addr.arpa. 303 IN PTR svc-1-b.test-1.svc.cluster.local."),
},
},
{
// Out of exposed range, record present = pass to next middleware (not existing in test) = FAIL
Qname: "115.0.0.10.in-addr.arpa.", Qtype: dns.TypePTR,
Rcode: dns.RcodeServerFailure,
Answer: []dns.RR{},
},
{
Qname: "next-in-chain.", Qtype: dns.TypeA,
Rcode: dns.RcodeSuccess,
Answer: []dns.RR{
test.A("next-in-chain. 0 IN A 192.0.2.53"),
},
},
}
var dnsTestCasesAllNSExposed = []test.Case{ var dnsTestCasesAllNSExposed = []test.Case{
{ {
Qname: "svc-1-a.test-1.svc.cluster.local.", Qtype: dns.TypeA, Qname: "svc-1-a.test-1.svc.cluster.local.", Qtype: dns.TypeA,
@ -392,25 +298,6 @@ var dnsTestCasesAllNSExposed = []test.Case{
test.A("svc-c.test-1.svc.cluster.local. 303 IN A 10.0.0.120"), test.A("svc-c.test-1.svc.cluster.local. 303 IN A 10.0.0.120"),
}, },
}, },
{
Qname: "123.0.0.10.in-addr.arpa.", Qtype: dns.TypePTR,
Rcode: dns.RcodeSuccess,
Answer: []dns.RR{},
},
{
Qname: "100.0.0.10.in-addr.arpa.", Qtype: dns.TypePTR,
Rcode: dns.RcodeSuccess,
Answer: []dns.RR{
test.PTR("100.0.0.10.in-addr.arpa. 303 IN PTR svc-1-a.test-1.svc.cluster.local."),
},
},
{
Qname: "120.0.0.10.in-addr.arpa.", Qtype: dns.TypePTR,
Rcode: dns.RcodeSuccess,
Answer: []dns.RR{
test.PTR("120.0.0.10.in-addr.arpa. 303 IN PTR svc-c.test-2.svc.cluster.local."),
},
},
} }
var dnsTestCasesFallthrough = []test.Case{ var dnsTestCasesFallthrough = []test.Case{
@ -558,42 +445,11 @@ func TestKubernetesIntegrationPodsVerified(t *testing.T) {
doIntegrationTests(t, corefile, dnsTestCasesPodsVerified) doIntegrationTests(t, corefile, dnsTestCasesPodsVerified)
} }
func TestKubernetesIntegrationCidrReverseZone(t *testing.T) {
corefile :=
`.:0 {
kubernetes cluster.local {
endpoint http://localhost:8080
namespaces test-1
cidrs 10.0.0.0/24
}
erratic . {
drop 0
}
`
doIntegrationTests(t, corefile, dnsTestCasesCidrReverseZone)
}
func TestKubernetesIntegrationPartialCidrReverseZone(t *testing.T) {
corefile :=
`.:0 {
kubernetes cluster.local {
endpoint http://localhost:8080
namespaces test-1
cidrs 10.0.0.96/28 10.0.0.120/32
}
erratic . {
drop 0
}
`
doIntegrationTests(t, corefile, dnsTestCasesPartialCidrReverseZone)
}
func TestKubernetesIntegrationAllNSExposed(t *testing.T) { func TestKubernetesIntegrationAllNSExposed(t *testing.T) {
corefile := corefile :=
`.:0 { `.:0 {
kubernetes cluster.local { kubernetes cluster.local {
endpoint http://localhost:8080 endpoint http://localhost:8080
cidrs 10.0.0.0/24
} }
` `
doIntegrationTests(t, corefile, dnsTestCasesAllNSExposed) doIntegrationTests(t, corefile, dnsTestCasesAllNSExposed)
@ -615,7 +471,6 @@ func TestKubernetesIntegrationFallthrough(t *testing.T) {
file ` + dbfile + ` cluster.local file ` + dbfile + ` cluster.local
kubernetes cluster.local { kubernetes cluster.local {
endpoint http://localhost:8080 endpoint http://localhost:8080
cidrs 10.0.0.0/24
namespaces test-1 namespaces test-1
upstream ` + udp + ` upstream ` + udp + `
fallthrough fallthrough