Add namespace_labels
configuration for kubernetes plugin (#2707)
This commit is contained in:
parent
43c3e0ab68
commit
a3dd8cdf8d
9 changed files with 278 additions and 40 deletions
|
@ -56,6 +56,11 @@ kubernetes [ZONES...] {
|
||||||
* `kubeconfig` **KUBECONFIG** **CONTEXT** authenticates the connection to a remote k8s cluster using a kubeconfig file. It supports TLS, username and password, or token-based authentication. This option is ignored if connecting in-cluster (i.e., the endpoint is not specified).
|
* `kubeconfig` **KUBECONFIG** **CONTEXT** authenticates the connection to a remote k8s cluster using a kubeconfig file. It supports TLS, username and password, or token-based authentication. This option is ignored if connecting in-cluster (i.e., the endpoint is not specified).
|
||||||
* `namespaces` **NAMESPACE [NAMESPACE...]** only exposes the k8s namespaces listed.
|
* `namespaces` **NAMESPACE [NAMESPACE...]** only exposes the k8s namespaces listed.
|
||||||
If this option is omitted all namespaces are exposed
|
If this option is omitted all namespaces are exposed
|
||||||
|
* `namespace_labels` **EXPRESSION** only expose the records for Kubernetes namespaces that match this label selector.
|
||||||
|
The label selector syntax is described in the
|
||||||
|
[Kubernetes User Guide - Labels](http://kubernetes.io/docs/user-guide/labels/). An example that
|
||||||
|
only exposes namespaces labeled as "istio-injection=enabled", would use:
|
||||||
|
`labels istio-injection=enabled`.
|
||||||
* `labels` **EXPRESSION** only exposes the records for Kubernetes objects that match this label selector.
|
* `labels` **EXPRESSION** only exposes the records for Kubernetes objects that match this label selector.
|
||||||
The label selector syntax is described in the
|
The label selector syntax is described in the
|
||||||
[Kubernetes User Guide - Labels](https://kubernetes.io/docs/user-guide/labels/). An example that
|
[Kubernetes User Guide - Labels](https://kubernetes.io/docs/user-guide/labels/). An example that
|
||||||
|
|
|
@ -54,6 +54,7 @@ type dnsControl struct {
|
||||||
client kubernetes.Interface
|
client kubernetes.Interface
|
||||||
|
|
||||||
selector labels.Selector
|
selector labels.Selector
|
||||||
|
namespaceSelector labels.Selector
|
||||||
|
|
||||||
svcController cache.Controller
|
svcController cache.Controller
|
||||||
podController cache.Controller
|
podController cache.Controller
|
||||||
|
@ -81,9 +82,12 @@ type dnsControlOpts struct {
|
||||||
initEndpointsCache bool
|
initEndpointsCache bool
|
||||||
resyncPeriod time.Duration
|
resyncPeriod time.Duration
|
||||||
ignoreEmptyService bool
|
ignoreEmptyService bool
|
||||||
|
|
||||||
// Label handling.
|
// Label handling.
|
||||||
labelSelector *meta.LabelSelector
|
labelSelector *meta.LabelSelector
|
||||||
selector labels.Selector
|
selector labels.Selector
|
||||||
|
namespaceLabelSelector *meta.LabelSelector
|
||||||
|
namespaceSelector labels.Selector
|
||||||
|
|
||||||
zones []string
|
zones []string
|
||||||
endpointNameMode bool
|
endpointNameMode bool
|
||||||
|
@ -94,6 +98,7 @@ func newdnsController(kubeClient kubernetes.Interface, opts dnsControlOpts) *dns
|
||||||
dns := dnsControl{
|
dns := dnsControl{
|
||||||
client: kubeClient,
|
client: kubeClient,
|
||||||
selector: opts.selector,
|
selector: opts.selector,
|
||||||
|
namespaceSelector: opts.namespaceSelector,
|
||||||
stopCh: make(chan struct{}),
|
stopCh: make(chan struct{}),
|
||||||
zones: opts.zones,
|
zones: opts.zones,
|
||||||
endpointNameMode: opts.endpointNameMode,
|
endpointNameMode: opts.endpointNameMode,
|
||||||
|
@ -140,10 +145,12 @@ func newdnsController(kubeClient kubernetes.Interface, opts dnsControlOpts) *dns
|
||||||
|
|
||||||
dns.nsLister, dns.nsController = cache.NewInformer(
|
dns.nsLister, dns.nsController = cache.NewInformer(
|
||||||
&cache.ListWatch{
|
&cache.ListWatch{
|
||||||
ListFunc: namespaceListFunc(dns.client, dns.selector),
|
ListFunc: namespaceListFunc(dns.client, dns.namespaceSelector),
|
||||||
WatchFunc: namespaceWatchFunc(dns.client, dns.selector),
|
WatchFunc: namespaceWatchFunc(dns.client, dns.namespaceSelector),
|
||||||
},
|
},
|
||||||
&api.Namespace{}, opts.resyncPeriod, cache.ResourceEventHandlerFuncs{})
|
&api.Namespace{},
|
||||||
|
opts.resyncPeriod,
|
||||||
|
cache.ResourceEventHandlerFuncs{})
|
||||||
|
|
||||||
return &dns
|
return &dns
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,7 +30,7 @@ func (k *Kubernetes) External(state request.Request) ([]msg.Service, int) {
|
||||||
port := "*"
|
port := "*"
|
||||||
protocol := "*"
|
protocol := "*"
|
||||||
namespace := segs[last]
|
namespace := segs[last]
|
||||||
if !k.namespaceExposed(namespace) || !k.namespace(namespace) {
|
if !k.namespaceExposed(namespace) {
|
||||||
return nil, dns.RcodeNameError
|
return nil, dns.RcodeNameError
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ package kubernetes
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -380,6 +381,59 @@ func TestServeDNS(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var nsTestCases = []test.Case{
|
||||||
|
// A Service for an "exposed" namespace that "does exist"
|
||||||
|
{
|
||||||
|
Qname: "svc1.testns.svc.cluster.local.", Qtype: dns.TypeA,
|
||||||
|
Rcode: dns.RcodeSuccess,
|
||||||
|
Answer: []dns.RR{
|
||||||
|
test.A("svc1.testns.svc.cluster.local. 5 IN A 10.0.0.1"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// A service for an "exposed" namespace that "doesn't exist"
|
||||||
|
{
|
||||||
|
Qname: "svc1.nsnoexist.svc.cluster.local.", Qtype: dns.TypeA,
|
||||||
|
Rcode: dns.RcodeNameError,
|
||||||
|
Ns: []dns.RR{
|
||||||
|
test.SOA("cluster.local. 300 IN SOA ns.dns.cluster.local. hostmaster.cluster.local. 1551484803 7200 1800 86400 30"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestServeNamespaceDNS(t *testing.T) {
|
||||||
|
k := New([]string{"cluster.local."})
|
||||||
|
k.APIConn = &APIConnServeTest{}
|
||||||
|
k.Next = test.NextHandler(dns.RcodeSuccess, nil)
|
||||||
|
// if no namespaces are explicitly exposed, then they are all implicitly exposed
|
||||||
|
k.Namespaces = map[string]struct{}{}
|
||||||
|
ctx := context.TODO()
|
||||||
|
|
||||||
|
for i, tc := range nsTestCases {
|
||||||
|
r := tc.Msg()
|
||||||
|
|
||||||
|
w := dnstest.NewRecorder(&test.ResponseWriter{})
|
||||||
|
|
||||||
|
_, err := k.ServeDNS(ctx, w, r)
|
||||||
|
if err != tc.Error {
|
||||||
|
t.Errorf("Test %d expected no error, got %v", i, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if tc.Error != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
resp := w.Msg
|
||||||
|
if resp == nil {
|
||||||
|
t.Fatalf("Test %d, got nil message and no error for %q", i, r.Question[0].Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Before sorting, make sure that CNAMES do not appear after their target records
|
||||||
|
test.CNAMEOrder(resp)
|
||||||
|
|
||||||
|
test.SortAndCheck(resp, tc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var notSyncedTestCases = []test.Case{
|
var notSyncedTestCases = []test.Case{
|
||||||
{
|
{
|
||||||
// We should get ServerFailure instead of NameError for missing records when we kubernetes hasn't synced
|
// We should get ServerFailure instead of NameError for missing records when we kubernetes hasn't synced
|
||||||
|
@ -627,6 +681,9 @@ func (APIConnServeTest) GetNamespaceByName(name string) (*api.Namespace, error)
|
||||||
if name == "pod-nons" { // handler_pod_verified_test.go uses this for non-existent namespace.
|
if name == "pod-nons" { // handler_pod_verified_test.go uses this for non-existent namespace.
|
||||||
return &api.Namespace{}, nil
|
return &api.Namespace{}, nil
|
||||||
}
|
}
|
||||||
|
if name == "nsnoexist" {
|
||||||
|
return nil, fmt.Errorf("namespace not found")
|
||||||
|
}
|
||||||
return &api.Namespace{
|
return &api.Namespace{
|
||||||
ObjectMeta: meta.ObjectMeta{
|
ObjectMeta: meta.ObjectMeta{
|
||||||
Name: name,
|
Name: name,
|
||||||
|
|
|
@ -217,6 +217,15 @@ func (k *Kubernetes) InitKubeCache() (err error) {
|
||||||
k.opts.selector = selector
|
k.opts.selector = selector
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if k.opts.namespaceLabelSelector != nil {
|
||||||
|
var selector labels.Selector
|
||||||
|
selector, err = meta.LabelSelectorAsSelector(k.opts.namespaceLabelSelector)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to create Selector for LabelSelector '%s': %q", k.opts.namespaceLabelSelector, err)
|
||||||
|
}
|
||||||
|
k.opts.namespaceSelector = selector
|
||||||
|
}
|
||||||
|
|
||||||
k.opts.initPodCache = k.podMode == podModeVerified
|
k.opts.initPodCache = k.podMode == podModeVerified
|
||||||
|
|
||||||
k.opts.zones = k.Zones
|
k.opts.zones = k.Zones
|
||||||
|
@ -302,13 +311,15 @@ func (k *Kubernetes) findPods(r recordRequest, zone string) (pods []msg.Service,
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace := r.namespace
|
namespace := r.namespace
|
||||||
|
if !wildcard(namespace) && !k.namespaceExposed(namespace) {
|
||||||
|
return nil, errNoItems
|
||||||
|
}
|
||||||
|
|
||||||
podname := r.service
|
podname := r.service
|
||||||
zonePath := msg.Path(zone, coredns)
|
|
||||||
ip := ""
|
|
||||||
|
|
||||||
// handle empty pod name
|
// handle empty pod name
|
||||||
if podname == "" {
|
if podname == "" {
|
||||||
if k.namespace(namespace) || wildcard(namespace) {
|
if k.namespaceExposed(namespace) || wildcard(namespace) {
|
||||||
// NODATA
|
// NODATA
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
@ -316,6 +327,8 @@ func (k *Kubernetes) findPods(r recordRequest, zone string) (pods []msg.Service,
|
||||||
return nil, errNoItems
|
return nil, errNoItems
|
||||||
}
|
}
|
||||||
|
|
||||||
|
zonePath := msg.Path(zone, coredns)
|
||||||
|
ip := ""
|
||||||
if strings.Count(podname, "-") == 3 && !strings.Contains(podname, "--") {
|
if strings.Count(podname, "-") == 3 && !strings.Contains(podname, "--") {
|
||||||
ip = strings.Replace(podname, "-", ".", -1)
|
ip = strings.Replace(podname, "-", ".", -1)
|
||||||
} else {
|
} else {
|
||||||
|
@ -323,7 +336,7 @@ func (k *Kubernetes) findPods(r recordRequest, zone string) (pods []msg.Service,
|
||||||
}
|
}
|
||||||
|
|
||||||
if k.podMode == podModeInsecure {
|
if k.podMode == podModeInsecure {
|
||||||
if !wildcard(namespace) && !k.namespace(namespace) { // no wildcard, but namespace does not exist
|
if !wildcard(namespace) && !k.namespaceExposed(namespace) { // no wildcard, but namespace does not exist
|
||||||
return nil, errNoItems
|
return nil, errNoItems
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -338,8 +351,8 @@ func (k *Kubernetes) findPods(r recordRequest, zone string) (pods []msg.Service,
|
||||||
// PodModeVerified
|
// PodModeVerified
|
||||||
err = errNoItems
|
err = errNoItems
|
||||||
if wildcard(podname) && !wildcard(namespace) {
|
if wildcard(podname) && !wildcard(namespace) {
|
||||||
// If namespace exist, err should be nil, so that we return nodata instead of NXDOMAIN
|
// If namespace exists, err should be nil, so that we return NODATA instead of NXDOMAIN
|
||||||
if k.namespace(namespace) {
|
if k.namespaceExposed(namespace) {
|
||||||
err = nil
|
err = nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -368,12 +381,24 @@ func (k *Kubernetes) findPods(r recordRequest, zone string) (pods []msg.Service,
|
||||||
|
|
||||||
// findServices returns the services matching r from the cache.
|
// findServices returns the services matching r from the cache.
|
||||||
func (k *Kubernetes) findServices(r recordRequest, zone string) (services []msg.Service, err error) {
|
func (k *Kubernetes) findServices(r recordRequest, zone string) (services []msg.Service, err error) {
|
||||||
zonePath := msg.Path(zone, coredns)
|
if !wildcard(r.namespace) && !k.namespaceExposed(r.namespace) {
|
||||||
|
return nil, errNoItems
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle empty service name
|
||||||
|
if r.service == "" {
|
||||||
|
if k.namespaceExposed(r.namespace) || wildcard(r.namespace) {
|
||||||
|
// NODATA
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
// NXDOMAIN
|
||||||
|
return nil, errNoItems
|
||||||
|
}
|
||||||
|
|
||||||
err = errNoItems
|
err = errNoItems
|
||||||
if wildcard(r.service) && !wildcard(r.namespace) {
|
if wildcard(r.service) && !wildcard(r.namespace) {
|
||||||
// If namespace exist, err should be nil, so that we return nodata instead of NXDOMAIN
|
// If namespace exists, err should be nil, so that we return NODATA instead of NXDOMAIN
|
||||||
if k.namespace(r.namespace) {
|
if k.namespaceExposed(r.namespace) {
|
||||||
err = nil
|
err = nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -384,16 +409,6 @@ func (k *Kubernetes) findServices(r recordRequest, zone string) (services []msg.
|
||||||
serviceList []*object.Service
|
serviceList []*object.Service
|
||||||
)
|
)
|
||||||
|
|
||||||
// handle empty service name
|
|
||||||
if r.service == "" {
|
|
||||||
if k.namespace(r.namespace) || wildcard(r.namespace) {
|
|
||||||
// NODATA
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
// NXDOMAIN
|
|
||||||
return nil, errNoItems
|
|
||||||
}
|
|
||||||
|
|
||||||
if wildcard(r.service) || wildcard(r.namespace) {
|
if wildcard(r.service) || wildcard(r.namespace) {
|
||||||
serviceList = k.APIConn.ServiceList()
|
serviceList = k.APIConn.ServiceList()
|
||||||
endpointsListFunc = func() []*object.Endpoints { return k.APIConn.EndpointsList() }
|
endpointsListFunc = func() []*object.Endpoints { return k.APIConn.EndpointsList() }
|
||||||
|
@ -403,12 +418,13 @@ func (k *Kubernetes) findServices(r recordRequest, zone string) (services []msg.
|
||||||
endpointsListFunc = func() []*object.Endpoints { return k.APIConn.EpIndex(idx) }
|
endpointsListFunc = func() []*object.Endpoints { return k.APIConn.EpIndex(idx) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
zonePath := msg.Path(zone, coredns)
|
||||||
for _, svc := range serviceList {
|
for _, svc := range serviceList {
|
||||||
if !(match(r.namespace, svc.Namespace) && match(r.service, svc.Name)) {
|
if !(match(r.namespace, svc.Namespace) && match(r.service, svc.Name)) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// If namespace has a wildcard, filter results against Corefile namespace list.
|
// If request namespace is a wildcard, filter results against Corefile namespace list.
|
||||||
// (Namespaces without a wildcard were filtered before the call to this function.)
|
// (Namespaces without a wildcard were filtered before the call to this function.)
|
||||||
if wildcard(r.namespace) && !k.namespaceExposed(svc.Namespace) {
|
if wildcard(r.namespace) && !k.namespaceExposed(svc.Namespace) {
|
||||||
continue
|
continue
|
||||||
|
|
|
@ -1,20 +1,27 @@
|
||||||
package kubernetes
|
package kubernetes
|
||||||
|
|
||||||
// namespace checks if namespace n exists in this cluster. This returns true
|
// filteredNamespaceExists checks if namespace exists in this cluster
|
||||||
// even for non exposed namespaces, see namespaceExposed.
|
// according to any `namespace_labels` plugin configuration specified.
|
||||||
func (k *Kubernetes) namespace(n string) bool {
|
// Returns true even for namespaces not exposed by plugin configuration,
|
||||||
ns, err := k.APIConn.GetNamespaceByName(n)
|
// see namespaceExposed.
|
||||||
|
func (k *Kubernetes) filteredNamespaceExists(namespace string) bool {
|
||||||
|
ns, err := k.APIConn.GetNamespaceByName(namespace)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return ns.ObjectMeta.Name == n
|
return ns.ObjectMeta.Name == namespace
|
||||||
}
|
}
|
||||||
|
|
||||||
// namespaceExposed returns true when the namespace is exposed.
|
// configuredNamespace returns true when the namespace is exposed through the plugin
|
||||||
func (k *Kubernetes) namespaceExposed(namespace string) bool {
|
// `namespaces` configuration.
|
||||||
|
func (k *Kubernetes) configuredNamespace(namespace string) bool {
|
||||||
_, ok := k.Namespaces[namespace]
|
_, ok := k.Namespaces[namespace]
|
||||||
if len(k.Namespaces) > 0 && !ok {
|
if len(k.Namespaces) > 0 && !ok {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (k *Kubernetes) namespaceExposed(namespace string) bool {
|
||||||
|
return k.configuredNamespace(namespace) && k.filteredNamespaceExists(namespace)
|
||||||
|
}
|
||||||
|
|
72
plugin/kubernetes/namespace_test.go
Normal file
72
plugin/kubernetes/namespace_test.go
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
package kubernetes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestFilteredNamespaceExists(t *testing.T) {
|
||||||
|
tests := []struct{
|
||||||
|
expected bool
|
||||||
|
kubernetesNamespaces map[string]struct{}
|
||||||
|
testNamespace string
|
||||||
|
}{
|
||||||
|
{true, map[string]struct{}{}, "foobar" },
|
||||||
|
{false, map[string]struct{}{}, "nsnoexist" },
|
||||||
|
}
|
||||||
|
|
||||||
|
k := Kubernetes{}
|
||||||
|
k.APIConn = &APIConnServeTest{}
|
||||||
|
for i, test := range tests {
|
||||||
|
k.Namespaces = test.kubernetesNamespaces
|
||||||
|
actual := k.filteredNamespaceExists(test.testNamespace)
|
||||||
|
if actual != test.expected {
|
||||||
|
t.Errorf("Test %d failed. Filtered namespace %s was expected to exist", i, test.testNamespace)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNamespaceExposed(t *testing.T) {
|
||||||
|
tests := []struct{
|
||||||
|
expected bool
|
||||||
|
kubernetesNamespaces map[string]struct{}
|
||||||
|
testNamespace string
|
||||||
|
}{
|
||||||
|
{true, map[string]struct{}{ "foobar": {} }, "foobar" },
|
||||||
|
{false, map[string]struct{}{ "foobar": {} }, "nsnoexist" },
|
||||||
|
{true, map[string]struct{}{}, "foobar" },
|
||||||
|
{true, map[string]struct{}{}, "nsnoexist" },
|
||||||
|
}
|
||||||
|
|
||||||
|
k := Kubernetes{}
|
||||||
|
k.APIConn = &APIConnServeTest{}
|
||||||
|
for i, test := range tests {
|
||||||
|
k.Namespaces = test.kubernetesNamespaces
|
||||||
|
actual := k.configuredNamespace(test.testNamespace)
|
||||||
|
if actual != test.expected {
|
||||||
|
t.Errorf("Test %d failed. Namespace %s was expected to be exposed", i, test.testNamespace)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNamespaceValid(t *testing.T) {
|
||||||
|
tests := []struct{
|
||||||
|
expected bool
|
||||||
|
kubernetesNamespaces map[string]struct{}
|
||||||
|
testNamespace string
|
||||||
|
}{
|
||||||
|
{true, map[string]struct{}{ "foobar": {} }, "foobar" },
|
||||||
|
{false, map[string]struct{}{ "foobar": {} }, "nsnoexist" },
|
||||||
|
{true, map[string]struct{}{}, "foobar" },
|
||||||
|
{false, map[string]struct{}{}, "nsnoexist" },
|
||||||
|
}
|
||||||
|
|
||||||
|
k := Kubernetes{}
|
||||||
|
k.APIConn = &APIConnServeTest{}
|
||||||
|
for i, test := range tests {
|
||||||
|
k.Namespaces = test.kubernetesNamespaces
|
||||||
|
actual := k.namespaceExposed(test.testNamespace)
|
||||||
|
if actual != test.expected {
|
||||||
|
t.Errorf("Test %d failed. Namespace %s was expected to be valid", i, test.testNamespace)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -234,6 +234,18 @@ func ParseStanza(c *caddy.Controller) (*Kubernetes, error) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
return nil, c.ArgErr()
|
return nil, c.ArgErr()
|
||||||
|
case "namespace_labels":
|
||||||
|
args := c.RemainingArgs()
|
||||||
|
if len(args) > 0 {
|
||||||
|
namespaceLabelSelectorString := strings.Join(args, " ")
|
||||||
|
nls, err := meta.ParseToLabelSelector(namespaceLabelSelectorString)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to parse namespace_label selector value: '%v': %v", namespaceLabelSelectorString, err)
|
||||||
|
}
|
||||||
|
k8s.opts.namespaceLabelSelector = nls
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return nil, c.ArgErr()
|
||||||
case "fallthrough":
|
case "fallthrough":
|
||||||
k8s.Fall.SetZonesFromArgs(c.RemainingArgs())
|
k8s.Fall.SetZonesFromArgs(c.RemainingArgs())
|
||||||
case "upstream":
|
case "upstream":
|
||||||
|
@ -293,6 +305,10 @@ func ParseStanza(c *caddy.Controller) (*Kubernetes, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(k8s.Namespaces) != 0 && k8s.opts.namespaceLabelSelector != nil {
|
||||||
|
return nil, c.Errf("namespaces and namespace_labels cannot both be set")
|
||||||
|
}
|
||||||
|
|
||||||
return k8s, nil
|
return k8s, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,15 +13,16 @@ import (
|
||||||
|
|
||||||
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
|
||||||
shouldErr bool // true if test case is expected to produce an error.
|
shouldErr bool // true if test case is expected to produce an error.
|
||||||
expectedErrContent string // substring from the expected error. Empty for positive cases.
|
expectedErrContent string // substring from the expected error. Empty for positive cases.
|
||||||
expectedZoneCount int // expected count of defined zones.
|
expectedZoneCount int // expected count of defined zones.
|
||||||
expectedNSCount int // expected count of namespaces.
|
expectedNSCount int // expected count of namespaces.
|
||||||
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
|
expectedNamespaceLabelSelector string // expected namespace label selector value
|
||||||
expectedFallthrough fall.F
|
expectedPodMode string
|
||||||
|
expectedFallthrough fall.F
|
||||||
}{
|
}{
|
||||||
// positive
|
// positive
|
||||||
{
|
{
|
||||||
|
@ -32,6 +33,7 @@ func TestKubernetesParse(t *testing.T) {
|
||||||
0,
|
0,
|
||||||
defaultResyncPeriod,
|
defaultResyncPeriod,
|
||||||
"",
|
"",
|
||||||
|
"",
|
||||||
podModeDisabled,
|
podModeDisabled,
|
||||||
fall.Zero,
|
fall.Zero,
|
||||||
},
|
},
|
||||||
|
@ -43,6 +45,7 @@ func TestKubernetesParse(t *testing.T) {
|
||||||
0,
|
0,
|
||||||
defaultResyncPeriod,
|
defaultResyncPeriod,
|
||||||
"",
|
"",
|
||||||
|
"",
|
||||||
podModeDisabled,
|
podModeDisabled,
|
||||||
fall.Zero,
|
fall.Zero,
|
||||||
},
|
},
|
||||||
|
@ -55,6 +58,7 @@ func TestKubernetesParse(t *testing.T) {
|
||||||
0,
|
0,
|
||||||
defaultResyncPeriod,
|
defaultResyncPeriod,
|
||||||
"",
|
"",
|
||||||
|
"",
|
||||||
podModeDisabled,
|
podModeDisabled,
|
||||||
fall.Zero,
|
fall.Zero,
|
||||||
},
|
},
|
||||||
|
@ -68,6 +72,7 @@ func TestKubernetesParse(t *testing.T) {
|
||||||
0,
|
0,
|
||||||
defaultResyncPeriod,
|
defaultResyncPeriod,
|
||||||
"",
|
"",
|
||||||
|
"",
|
||||||
podModeDisabled,
|
podModeDisabled,
|
||||||
fall.Zero,
|
fall.Zero,
|
||||||
},
|
},
|
||||||
|
@ -81,6 +86,7 @@ func TestKubernetesParse(t *testing.T) {
|
||||||
1,
|
1,
|
||||||
defaultResyncPeriod,
|
defaultResyncPeriod,
|
||||||
"",
|
"",
|
||||||
|
"",
|
||||||
podModeDisabled,
|
podModeDisabled,
|
||||||
fall.Zero,
|
fall.Zero,
|
||||||
},
|
},
|
||||||
|
@ -94,6 +100,7 @@ func TestKubernetesParse(t *testing.T) {
|
||||||
2,
|
2,
|
||||||
defaultResyncPeriod,
|
defaultResyncPeriod,
|
||||||
"",
|
"",
|
||||||
|
"",
|
||||||
podModeDisabled,
|
podModeDisabled,
|
||||||
fall.Zero,
|
fall.Zero,
|
||||||
},
|
},
|
||||||
|
@ -107,6 +114,7 @@ func TestKubernetesParse(t *testing.T) {
|
||||||
0,
|
0,
|
||||||
30 * time.Second,
|
30 * time.Second,
|
||||||
"",
|
"",
|
||||||
|
"",
|
||||||
podModeDisabled,
|
podModeDisabled,
|
||||||
fall.Zero,
|
fall.Zero,
|
||||||
},
|
},
|
||||||
|
@ -120,6 +128,7 @@ func TestKubernetesParse(t *testing.T) {
|
||||||
0,
|
0,
|
||||||
15 * time.Minute,
|
15 * time.Minute,
|
||||||
"",
|
"",
|
||||||
|
"",
|
||||||
podModeDisabled,
|
podModeDisabled,
|
||||||
fall.Zero,
|
fall.Zero,
|
||||||
},
|
},
|
||||||
|
@ -133,6 +142,7 @@ func TestKubernetesParse(t *testing.T) {
|
||||||
0,
|
0,
|
||||||
defaultResyncPeriod,
|
defaultResyncPeriod,
|
||||||
"environment=prod",
|
"environment=prod",
|
||||||
|
"",
|
||||||
podModeDisabled,
|
podModeDisabled,
|
||||||
fall.Zero,
|
fall.Zero,
|
||||||
},
|
},
|
||||||
|
@ -146,6 +156,36 @@ func TestKubernetesParse(t *testing.T) {
|
||||||
0,
|
0,
|
||||||
defaultResyncPeriod,
|
defaultResyncPeriod,
|
||||||
"application=nginx,environment in (production,qa,staging)",
|
"application=nginx,environment in (production,qa,staging)",
|
||||||
|
"",
|
||||||
|
podModeDisabled,
|
||||||
|
fall.Zero,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
`kubernetes coredns.local {
|
||||||
|
namespace_labels istio-injection=enabled
|
||||||
|
}`,
|
||||||
|
false,
|
||||||
|
"",
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
defaultResyncPeriod,
|
||||||
|
"",
|
||||||
|
"istio-injection=enabled",
|
||||||
|
podModeDisabled,
|
||||||
|
fall.Zero,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
`kubernetes coredns.local {
|
||||||
|
namespaces foo bar
|
||||||
|
namespace_labels istio-injection=enabled
|
||||||
|
}`,
|
||||||
|
true,
|
||||||
|
"Error during parsing: namespaces and namespace_labels cannot both be set",
|
||||||
|
-1,
|
||||||
|
0,
|
||||||
|
defaultResyncPeriod,
|
||||||
|
"",
|
||||||
|
"istio-injection=enabled",
|
||||||
podModeDisabled,
|
podModeDisabled,
|
||||||
fall.Zero,
|
fall.Zero,
|
||||||
},
|
},
|
||||||
|
@ -163,6 +203,7 @@ func TestKubernetesParse(t *testing.T) {
|
||||||
2,
|
2,
|
||||||
15 * time.Minute,
|
15 * time.Minute,
|
||||||
"application=nginx,environment in (production,qa,staging)",
|
"application=nginx,environment in (production,qa,staging)",
|
||||||
|
"",
|
||||||
podModeDisabled,
|
podModeDisabled,
|
||||||
fall.Root,
|
fall.Root,
|
||||||
},
|
},
|
||||||
|
@ -177,6 +218,7 @@ func TestKubernetesParse(t *testing.T) {
|
||||||
-1,
|
-1,
|
||||||
defaultResyncPeriod,
|
defaultResyncPeriod,
|
||||||
"",
|
"",
|
||||||
|
"",
|
||||||
podModeDisabled,
|
podModeDisabled,
|
||||||
fall.Zero,
|
fall.Zero,
|
||||||
},
|
},
|
||||||
|
@ -190,6 +232,7 @@ func TestKubernetesParse(t *testing.T) {
|
||||||
-1,
|
-1,
|
||||||
defaultResyncPeriod,
|
defaultResyncPeriod,
|
||||||
"",
|
"",
|
||||||
|
"",
|
||||||
podModeDisabled,
|
podModeDisabled,
|
||||||
fall.Zero,
|
fall.Zero,
|
||||||
},
|
},
|
||||||
|
@ -203,6 +246,7 @@ func TestKubernetesParse(t *testing.T) {
|
||||||
0,
|
0,
|
||||||
0 * time.Minute,
|
0 * time.Minute,
|
||||||
"",
|
"",
|
||||||
|
"",
|
||||||
podModeDisabled,
|
podModeDisabled,
|
||||||
fall.Zero,
|
fall.Zero,
|
||||||
},
|
},
|
||||||
|
@ -216,6 +260,7 @@ func TestKubernetesParse(t *testing.T) {
|
||||||
0,
|
0,
|
||||||
0 * time.Second,
|
0 * time.Second,
|
||||||
"",
|
"",
|
||||||
|
"",
|
||||||
podModeDisabled,
|
podModeDisabled,
|
||||||
fall.Zero,
|
fall.Zero,
|
||||||
},
|
},
|
||||||
|
@ -229,6 +274,7 @@ func TestKubernetesParse(t *testing.T) {
|
||||||
0,
|
0,
|
||||||
0 * time.Second,
|
0 * time.Second,
|
||||||
"",
|
"",
|
||||||
|
"",
|
||||||
podModeDisabled,
|
podModeDisabled,
|
||||||
fall.Zero,
|
fall.Zero,
|
||||||
},
|
},
|
||||||
|
@ -242,6 +288,7 @@ func TestKubernetesParse(t *testing.T) {
|
||||||
0,
|
0,
|
||||||
0 * time.Second,
|
0 * time.Second,
|
||||||
"",
|
"",
|
||||||
|
"",
|
||||||
podModeDisabled,
|
podModeDisabled,
|
||||||
fall.Zero,
|
fall.Zero,
|
||||||
},
|
},
|
||||||
|
@ -255,6 +302,7 @@ func TestKubernetesParse(t *testing.T) {
|
||||||
0,
|
0,
|
||||||
0 * time.Second,
|
0 * time.Second,
|
||||||
"",
|
"",
|
||||||
|
"",
|
||||||
podModeDisabled,
|
podModeDisabled,
|
||||||
fall.Zero,
|
fall.Zero,
|
||||||
},
|
},
|
||||||
|
@ -269,6 +317,7 @@ func TestKubernetesParse(t *testing.T) {
|
||||||
0,
|
0,
|
||||||
defaultResyncPeriod,
|
defaultResyncPeriod,
|
||||||
"",
|
"",
|
||||||
|
"",
|
||||||
podModeDisabled,
|
podModeDisabled,
|
||||||
fall.Zero,
|
fall.Zero,
|
||||||
},
|
},
|
||||||
|
@ -283,6 +332,7 @@ func TestKubernetesParse(t *testing.T) {
|
||||||
0,
|
0,
|
||||||
defaultResyncPeriod,
|
defaultResyncPeriod,
|
||||||
"",
|
"",
|
||||||
|
"",
|
||||||
podModeInsecure,
|
podModeInsecure,
|
||||||
fall.Zero,
|
fall.Zero,
|
||||||
},
|
},
|
||||||
|
@ -297,6 +347,7 @@ func TestKubernetesParse(t *testing.T) {
|
||||||
0,
|
0,
|
||||||
defaultResyncPeriod,
|
defaultResyncPeriod,
|
||||||
"",
|
"",
|
||||||
|
"",
|
||||||
podModeVerified,
|
podModeVerified,
|
||||||
fall.Zero,
|
fall.Zero,
|
||||||
},
|
},
|
||||||
|
@ -311,6 +362,7 @@ func TestKubernetesParse(t *testing.T) {
|
||||||
0,
|
0,
|
||||||
defaultResyncPeriod,
|
defaultResyncPeriod,
|
||||||
"",
|
"",
|
||||||
|
"",
|
||||||
podModeVerified,
|
podModeVerified,
|
||||||
fall.Zero,
|
fall.Zero,
|
||||||
},
|
},
|
||||||
|
@ -325,6 +377,7 @@ func TestKubernetesParse(t *testing.T) {
|
||||||
0,
|
0,
|
||||||
defaultResyncPeriod,
|
defaultResyncPeriod,
|
||||||
"",
|
"",
|
||||||
|
"",
|
||||||
podModeDisabled,
|
podModeDisabled,
|
||||||
fall.F{Zones: []string{"ip6.arpa.", "inaddr.arpa.", "foo.com."}},
|
fall.F{Zones: []string{"ip6.arpa.", "inaddr.arpa.", "foo.com."}},
|
||||||
},
|
},
|
||||||
|
@ -339,6 +392,7 @@ func TestKubernetesParse(t *testing.T) {
|
||||||
0,
|
0,
|
||||||
defaultResyncPeriod,
|
defaultResyncPeriod,
|
||||||
"",
|
"",
|
||||||
|
"",
|
||||||
podModeDisabled,
|
podModeDisabled,
|
||||||
fall.Zero,
|
fall.Zero,
|
||||||
},
|
},
|
||||||
|
@ -352,6 +406,7 @@ kubernetes cluster.local`,
|
||||||
0,
|
0,
|
||||||
defaultResyncPeriod,
|
defaultResyncPeriod,
|
||||||
"",
|
"",
|
||||||
|
"",
|
||||||
podModeDisabled,
|
podModeDisabled,
|
||||||
fall.Zero,
|
fall.Zero,
|
||||||
},
|
},
|
||||||
|
@ -365,6 +420,7 @@ kubernetes cluster.local`,
|
||||||
0,
|
0,
|
||||||
defaultResyncPeriod,
|
defaultResyncPeriod,
|
||||||
"",
|
"",
|
||||||
|
"",
|
||||||
podModeDisabled,
|
podModeDisabled,
|
||||||
fall.Zero,
|
fall.Zero,
|
||||||
},
|
},
|
||||||
|
@ -378,6 +434,7 @@ kubernetes cluster.local`,
|
||||||
0,
|
0,
|
||||||
defaultResyncPeriod,
|
defaultResyncPeriod,
|
||||||
"",
|
"",
|
||||||
|
"",
|
||||||
podModeDisabled,
|
podModeDisabled,
|
||||||
fall.Zero,
|
fall.Zero,
|
||||||
},
|
},
|
||||||
|
@ -391,6 +448,7 @@ kubernetes cluster.local`,
|
||||||
0,
|
0,
|
||||||
defaultResyncPeriod,
|
defaultResyncPeriod,
|
||||||
"",
|
"",
|
||||||
|
"",
|
||||||
podModeDisabled,
|
podModeDisabled,
|
||||||
fall.Zero,
|
fall.Zero,
|
||||||
},
|
},
|
||||||
|
|
Loading…
Add table
Reference in a new issue