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:
parent
818d2b10ad
commit
00f5c7797e
26 changed files with 197 additions and 815 deletions
|
@ -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())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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.
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 (
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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":
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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 ""
|
|
||||||
}
|
|
|
@ -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: ""})
|
|
||||||
}
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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")
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -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())
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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{
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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
|
||||||
}`
|
}`
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Add table
Reference in a new issue