aboutsummaryrefslogtreecommitdiff
path: root/middleware/kubernetes
diff options
context:
space:
mode:
Diffstat (limited to 'middleware/kubernetes')
-rw-r--r--middleware/kubernetes/controller.go56
-rw-r--r--middleware/kubernetes/handler.go3
-rw-r--r--middleware/kubernetes/kubernetes.go48
-rw-r--r--middleware/kubernetes/nametemplate/nametemplate.go5
-rw-r--r--middleware/kubernetes/setup.go140
-rw-r--r--middleware/kubernetes/setup_test.go390
6 files changed, 565 insertions, 77 deletions
diff --git a/middleware/kubernetes/controller.go b/middleware/kubernetes/controller.go
index 3fbea313e..5de16d61c 100644
--- a/middleware/kubernetes/controller.go
+++ b/middleware/kubernetes/controller.go
@@ -2,7 +2,6 @@ package kubernetes
import (
"fmt"
- "log"
"sync"
"time"
@@ -12,7 +11,7 @@ import (
"k8s.io/kubernetes/pkg/client/cache"
client "k8s.io/kubernetes/pkg/client/unversioned"
"k8s.io/kubernetes/pkg/controller/framework"
- "k8s.io/kubernetes/pkg/labels"
+ "k8s.io/kubernetes/pkg/labels"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/watch"
)
@@ -24,7 +23,7 @@ var (
type dnsController struct {
client *client.Client
- selector *labels.Selector
+ selector *labels.Selector
endpController *framework.Controller
svcController *framework.Controller
@@ -45,9 +44,9 @@ type dnsController struct {
// newDNSController creates a controller for coredns
func newdnsController(kubeClient *client.Client, resyncPeriod time.Duration, lselector *labels.Selector) *dnsController {
dns := dnsController{
- client: kubeClient,
- selector: lselector,
- stopCh: make(chan struct{}),
+ client: kubeClient,
+ selector: lselector,
+ stopCh: make(chan struct{}),
}
dns.endpLister.Store, dns.endpController = framework.NewInformer(
@@ -76,54 +75,54 @@ func newdnsController(kubeClient *client.Client, resyncPeriod time.Duration, lse
func serviceListFunc(c *client.Client, ns string, s *labels.Selector) func(api.ListOptions) (runtime.Object, error) {
return func(opts api.ListOptions) (runtime.Object, error) {
- if s != nil {
- opts.LabelSelector = *s
- }
+ if s != nil {
+ opts.LabelSelector = *s
+ }
return c.Services(ns).List(opts)
}
}
func serviceWatchFunc(c *client.Client, ns string, s *labels.Selector) func(options api.ListOptions) (watch.Interface, error) {
return func(options api.ListOptions) (watch.Interface, error) {
- if s != nil {
- options.LabelSelector = *s
- }
+ if s != nil {
+ options.LabelSelector = *s
+ }
return c.Services(ns).Watch(options)
}
}
func endpointsListFunc(c *client.Client, ns string, s *labels.Selector) func(api.ListOptions) (runtime.Object, error) {
return func(opts api.ListOptions) (runtime.Object, error) {
- if s != nil {
- opts.LabelSelector = *s
- }
+ if s != nil {
+ opts.LabelSelector = *s
+ }
return c.Endpoints(ns).List(opts)
}
}
func endpointsWatchFunc(c *client.Client, ns string, s *labels.Selector) func(options api.ListOptions) (watch.Interface, error) {
return func(options api.ListOptions) (watch.Interface, error) {
- if s != nil {
- options.LabelSelector = *s
- }
+ if s != nil {
+ options.LabelSelector = *s
+ }
return c.Endpoints(ns).Watch(options)
}
}
func namespaceListFunc(c *client.Client, s *labels.Selector) func(api.ListOptions) (runtime.Object, error) {
return func(opts api.ListOptions) (runtime.Object, error) {
- if s != nil {
- opts.LabelSelector = *s
- }
+ if s != nil {
+ opts.LabelSelector = *s
+ }
return c.Namespaces().List(opts)
}
}
func namespaceWatchFunc(c *client.Client, s *labels.Selector) func(options api.ListOptions) (watch.Interface, error) {
return func(options api.ListOptions) (watch.Interface, error) {
- if s != nil {
- options.LabelSelector = *s
- }
+ if s != nil {
+ options.LabelSelector = *s
+ }
return c.Namespaces().Watch(options)
}
}
@@ -140,7 +139,6 @@ func (dns *dnsController) Stop() error {
// Only try draining the workqueue if we haven't already.
if !dns.shutdown {
close(dns.stopCh)
- log.Println("shutting down controller queues")
dns.shutdown = true
return nil
@@ -151,14 +149,10 @@ func (dns *dnsController) Stop() error {
// Run starts the controller.
func (dns *dnsController) Run() {
- log.Println("[debug] Starting k8s notification controllers")
-
go dns.endpController.Run(dns.stopCh)
go dns.svcController.Run(dns.stopCh)
go dns.nsController.Run(dns.stopCh)
-
<-dns.stopCh
- log.Println("[debug] shutting down coredns controller")
}
func (dns *dnsController) GetNamespaceList() *api.NamespaceList {
@@ -203,12 +197,12 @@ func (dns *dnsController) GetServiceInNamespace(namespace string, servicename st
svcObj, svcExists, err := dns.svcLister.Store.GetByKey(svcKey)
if err != nil {
- log.Printf("error getting service %v from the cache: %v\n", svcKey, err)
+ // TODO(...): should return err here
return nil
}
if !svcExists {
- log.Printf("service %v does not exists\n", svcKey)
+ // TODO(...): should return err here
return nil
}
diff --git a/middleware/kubernetes/handler.go b/middleware/kubernetes/handler.go
index 05dfba934..1986820d5 100644
--- a/middleware/kubernetes/handler.go
+++ b/middleware/kubernetes/handler.go
@@ -2,7 +2,6 @@ package kubernetes
import (
"fmt"
- "log"
"strings"
"github.com/miekg/coredns/middleware"
@@ -12,8 +11,6 @@ import (
)
func (k Kubernetes) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
- log.Printf("[debug] here entering ServeDNS: ctx:%v dnsmsg:%v\n", ctx, r)
-
state := middleware.State{W: w, Req: r}
if state.QClass() != dns.ClassINET {
return dns.RcodeServerFailure, fmt.Errorf("can only deal with ClassINET")
diff --git a/middleware/kubernetes/kubernetes.go b/middleware/kubernetes/kubernetes.go
index 5e2a1bf53..59a044140 100644
--- a/middleware/kubernetes/kubernetes.go
+++ b/middleware/kubernetes/kubernetes.go
@@ -16,10 +16,10 @@ import (
"github.com/miekg/dns"
"k8s.io/kubernetes/pkg/api"
unversionedapi "k8s.io/kubernetes/pkg/api/unversioned"
- "k8s.io/kubernetes/pkg/labels"
unversionedclient "k8s.io/kubernetes/pkg/client/unversioned"
"k8s.io/kubernetes/pkg/client/unversioned/clientcmd"
clientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api"
+ "k8s.io/kubernetes/pkg/labels"
)
type Kubernetes struct {
@@ -32,10 +32,10 @@ type Kubernetes struct {
NameTemplate *nametemplate.NameTemplate
Namespaces []string
LabelSelector *unversionedapi.LabelSelector
- Selector *labels.Selector
+ Selector *labels.Selector
}
-func (g *Kubernetes) StartKubeCache() error {
+func (g *Kubernetes) InitKubeCache() error {
// For a custom api server or running outside a k8s cluster
// set URL in env.KUBERNETES_MASTER or set endpoint in Corefile
loadingRules := clientcmd.NewDefaultClientConfigLoadingRules()
@@ -46,7 +46,6 @@ func (g *Kubernetes) StartKubeCache() error {
clientConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, overrides)
config, err := clientConfig.ClientConfig()
if err != nil {
- log.Printf("[debug] error connecting to the client: %v", err)
return err
}
kubeClient, err := unversionedclient.New(config)
@@ -58,20 +57,17 @@ func (g *Kubernetes) StartKubeCache() error {
if g.LabelSelector == nil {
log.Printf("[INFO] Kubernetes middleware configured without a label selector. No label-based filtering will be performed.")
} else {
- var selector labels.Selector
+ var selector labels.Selector
selector, err = unversionedapi.LabelSelectorAsSelector(g.LabelSelector)
- g.Selector = &selector
- if err != nil {
- log.Printf("[ERROR] Unable to create Selector for LabelSelector '%s'.Error was: %s", g.LabelSelector, err)
- return err
- }
+ g.Selector = &selector
+ if err != nil {
+ log.Printf("[ERROR] Unable to create Selector for LabelSelector '%s'.Error was: %s", g.LabelSelector, err)
+ return err
+ }
log.Printf("[INFO] Kubernetes middleware configured with the label selector '%s'. Only kubernetes objects matching this label selector will be exposed.", unversionedapi.FormatLabelSelector(g.LabelSelector))
}
- log.Printf("[debug] Starting kubernetes middleware with k8s API resync period: %s", g.ResyncPeriod)
g.APIConn = newdnsController(kubeClient, g.ResyncPeriod, g.Selector)
- go g.APIConn.Run()
-
return err
}
@@ -115,7 +111,6 @@ func (g *Kubernetes) Records(name string, exact bool) ([]msg.Service, error) {
typeName string
)
- log.Printf("[debug] enter Records('%v', '%v')\n", name, exact)
zone, serviceSegments := g.getZoneForName(name)
// TODO: Implementation above globbed together segments for the serviceName if
@@ -137,30 +132,18 @@ func (g *Kubernetes) Records(name string, exact bool) ([]msg.Service, error) {
serviceName = util.WildcardStar
}
- log.Printf("[debug] published namespaces: %v\n", g.Namespaces)
-
- log.Printf("[debug] exact: %v\n", exact)
- log.Printf("[debug] zone: %v\n", zone)
- log.Printf("[debug] servicename: %v\n", serviceName)
- log.Printf("[debug] namespace: %v\n", namespace)
- log.Printf("[debug] typeName: %v\n", typeName)
- log.Printf("[debug] APIconn: %v\n", g.APIConn)
-
nsWildcard := util.SymbolContainsWildcard(namespace)
serviceWildcard := util.SymbolContainsWildcard(serviceName)
// Abort if the namespace does not contain a wildcard, and namespace is not published per CoreFile
// Case where namespace contains a wildcard is handled in Get(...) method.
if (!nsWildcard) && (len(g.Namespaces) > 0) && (!util.StringInSlice(namespace, g.Namespaces)) {
- log.Printf("[debug] Namespace '%v' is not published by Corefile\n", namespace)
return nil, nil
}
- log.Printf("before g.Get(namespace, nsWildcard, serviceName, serviceWildcard): %v %v %v %v", namespace, nsWildcard, serviceName, serviceWildcard)
+ log.Printf("[debug] before g.Get(namespace, nsWildcard, serviceName, serviceWildcard): %v %v %v %v", namespace, nsWildcard, serviceName, serviceWildcard)
k8sItems, err := g.Get(namespace, nsWildcard, serviceName, serviceWildcard)
- log.Printf("[debug] k8s items: %v\n", k8sItems)
if err != nil {
- log.Printf("[ERROR] Got error while looking up ServiceItems. Error is: %v\n", err)
return nil, err
}
if k8sItems == nil {
@@ -178,7 +161,6 @@ func (g *Kubernetes) getRecordsForServiceItems(serviceItems []api.Service, value
for _, item := range serviceItems {
clusterIP := item.Spec.ClusterIP
- log.Printf("[debug] clusterIP: %v\n", clusterIP)
// Create records by constructing record name from template...
//values.Namespace = item.Metadata.Namespace
@@ -188,13 +170,11 @@ func (g *Kubernetes) getRecordsForServiceItems(serviceItems []api.Service, value
// Create records for each exposed port...
for _, p := range item.Spec.Ports {
- log.Printf("[debug] port: %v\n", p.Port)
s := msg.Service{Host: clusterIP, Port: int(p.Port)}
records = append(records, s)
}
}
- log.Printf("[debug] records from getRecordsForServiceItems(): %v\n", records)
return records
}
@@ -202,13 +182,6 @@ func (g *Kubernetes) getRecordsForServiceItems(serviceItems []api.Service, value
func (g *Kubernetes) Get(namespace string, nsWildcard bool, servicename string, serviceWildcard bool) ([]api.Service, error) {
serviceList := g.APIConn.GetServiceList()
- /* TODO: Remove?
- if err != nil {
- log.Printf("[ERROR] Getting service list produced error: %v", err)
- return nil, err
- }
- */
-
var resultItems []api.Service
for _, item := range serviceList.Items {
@@ -216,7 +189,6 @@ func (g *Kubernetes) Get(namespace string, nsWildcard bool, servicename string,
// If namespace has a wildcard, filter results against Corefile namespace list.
// (Namespaces without a wildcard were filtered before the call to this function.)
if nsWildcard && (len(g.Namespaces) > 0) && (!util.StringInSlice(item.Namespace, g.Namespaces)) {
- log.Printf("[debug] Namespace '%v' is not published by Corefile\n", item.Namespace)
continue
}
resultItems = append(resultItems, item)
diff --git a/middleware/kubernetes/nametemplate/nametemplate.go b/middleware/kubernetes/nametemplate/nametemplate.go
index 5a34ae4ad..3e1ac4bb3 100644
--- a/middleware/kubernetes/nametemplate/nametemplate.go
+++ b/middleware/kubernetes/nametemplate/nametemplate.go
@@ -2,7 +2,6 @@ package nametemplate
import (
"errors"
- "log"
"strings"
"github.com/miekg/coredns/middleware/kubernetes/util"
@@ -87,17 +86,13 @@ func (t *NameTemplate) SetTemplate(s string) error {
if !elementPositionSet {
if strings.Contains(v, "{") {
err = errors.New("Record name template contains the unknown symbol '" + v + "'")
- log.Printf("[debug] %v\n", err)
return err
- } else {
- log.Printf("[debug] Template string has static element '%v'\n", v)
}
}
}
if err == nil && !t.IsValid() {
err = errors.New("Record name template does not pass NameTemplate validation")
- log.Printf("[debug] %v\n", err)
return err
}
diff --git a/middleware/kubernetes/setup.go b/middleware/kubernetes/setup.go
new file mode 100644
index 000000000..fc3c036b8
--- /dev/null
+++ b/middleware/kubernetes/setup.go
@@ -0,0 +1,140 @@
+package kubernetes
+
+import (
+ "errors"
+ "fmt"
+ "strings"
+ "time"
+
+ "github.com/miekg/coredns/core/dnsserver"
+ "github.com/miekg/coredns/middleware"
+ "github.com/miekg/coredns/middleware/kubernetes/nametemplate"
+
+ "github.com/mholt/caddy"
+ unversionedapi "k8s.io/kubernetes/pkg/api/unversioned"
+)
+
+func init() {
+ caddy.RegisterPlugin("kubernetes", caddy.Plugin{
+ ServerType: "dns",
+ Action: setup,
+ })
+}
+
+func setup(c *caddy.Controller) error {
+ kubernetes, err := kubernetesParse(c)
+ if err != nil {
+ return err
+ }
+
+ err = kubernetes.InitKubeCache()
+ if err != nil {
+ return err
+ }
+
+ // Register KubeCache start and stop functions with Caddy
+ c.OnStartup(func() error {
+ go kubernetes.APIConn.Run()
+ return nil
+ })
+
+ c.OnShutdown(func() error {
+ return kubernetes.APIConn.Stop()
+ })
+
+ dnsserver.GetConfig(c).AddMiddleware(func(next dnsserver.Handler) dnsserver.Handler {
+ kubernetes.Next = next
+ return kubernetes
+ })
+
+ return nil
+}
+
+func kubernetesParse(c *caddy.Controller) (Kubernetes, error) {
+ var err error
+ template := defaultNameTemplate
+
+ k8s := Kubernetes{ResyncPeriod: defaultResyncPeriod}
+ k8s.NameTemplate = new(nametemplate.NameTemplate)
+ k8s.NameTemplate.SetTemplate(template)
+
+ for c.Next() {
+ if c.Val() == "kubernetes" {
+ zones := c.RemainingArgs()
+
+ if len(zones) == 0 {
+ k8s.Zones = make([]string, len(c.ServerBlockKeys))
+ copy(k8s.Zones, c.ServerBlockKeys)
+ }
+
+ k8s.Zones = NormalizeZoneList(zones)
+ middleware.Zones(k8s.Zones).FullyQualify()
+
+ if k8s.Zones == nil || len(k8s.Zones) < 1 {
+ err = errors.New("Zone name must be provided for kubernetes middleware.")
+ return Kubernetes{}, err
+ }
+
+ for c.NextBlock() {
+ switch c.Val() {
+ case "template":
+ args := c.RemainingArgs()
+ if len(args) != 0 {
+ template := strings.Join(args, "")
+ err = k8s.NameTemplate.SetTemplate(template)
+ if err != nil {
+ return Kubernetes{}, err
+ }
+ } else {
+ return Kubernetes{}, c.ArgErr()
+ }
+ case "namespaces":
+ args := c.RemainingArgs()
+ if len(args) != 0 {
+ k8s.Namespaces = append(k8s.Namespaces, args...)
+ } else {
+ return Kubernetes{}, c.ArgErr()
+ }
+ case "endpoint":
+ args := c.RemainingArgs()
+ if len(args) != 0 {
+ k8s.APIEndpoint = args[0]
+ } else {
+ return Kubernetes{}, c.ArgErr()
+ }
+ case "resyncperiod":
+ args := c.RemainingArgs()
+ if len(args) != 0 {
+ k8s.ResyncPeriod, err = time.ParseDuration(args[0])
+ if err != nil {
+ err = errors.New(fmt.Sprintf("Unable to parse resync duration value. Value provided was '%v'. Example valid values: '15s', '5m', '1h'. Error was: %v", args[0], err))
+ return Kubernetes{}, err
+ }
+ } else {
+ return Kubernetes{}, c.ArgErr()
+ }
+ case "labels":
+ args := c.RemainingArgs()
+ if len(args) != 0 {
+ labelSelectorString := strings.Join(args, " ")
+ k8s.LabelSelector, err = unversionedapi.ParseToLabelSelector(labelSelectorString)
+ if err != nil {
+ err = errors.New(fmt.Sprintf("Unable to parse label selector. Value provided was '%v'. Error was: %v", labelSelectorString, err))
+ return Kubernetes{}, err
+ }
+ } else {
+ return Kubernetes{}, c.ArgErr()
+ }
+ }
+ }
+ return k8s, nil
+ }
+ }
+ err = errors.New("Kubernetes setup called without keyword 'kubernetes' in Corefile")
+ return Kubernetes{}, err
+}
+
+const (
+ defaultNameTemplate = "{service}.{namespace}.{zone}"
+ defaultResyncPeriod = 5 * time.Minute
+)
diff --git a/middleware/kubernetes/setup_test.go b/middleware/kubernetes/setup_test.go
new file mode 100644
index 000000000..6e0918a3b
--- /dev/null
+++ b/middleware/kubernetes/setup_test.go
@@ -0,0 +1,390 @@
+package kubernetes
+
+import (
+ "strings"
+ "testing"
+ "time"
+
+ "github.com/mholt/caddy"
+ unversionedapi "k8s.io/kubernetes/pkg/api/unversioned"
+)
+
+func TestKubernetesParse(t *testing.T) {
+ tests := []struct {
+ description string // Human-facing description of test case
+ 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.
+ expectedZoneCount int // expected count of defined zones.
+ expectedNTValid bool // NameTemplate to be initialized and valid
+ expectedNSCount int // expected count of namespaces.
+ expectedResyncPeriod time.Duration // expected resync period value
+ expectedLabelSelector string // expected label selector value
+ }{
+ // positive
+ {
+ "kubernetes keyword with one zone",
+ `kubernetes coredns.local`,
+ false,
+ "",
+ 1,
+ true,
+ 0,
+ defaultResyncPeriod,
+ "",
+ },
+ {
+ "kubernetes keyword with multiple zones",
+ `kubernetes coredns.local test.local`,
+ false,
+ "",
+ 2,
+ true,
+ 0,
+ defaultResyncPeriod,
+ "",
+ },
+ {
+ "kubernetes keyword with zone and empty braces",
+ `kubernetes coredns.local {
+}`,
+ false,
+ "",
+ 1,
+ true,
+ 0,
+ defaultResyncPeriod,
+ "",
+ },
+ {
+ "endpoint keyword with url",
+ `kubernetes coredns.local {
+ endpoint http://localhost:9090
+}`,
+ false,
+ "",
+ 1,
+ true,
+ 0,
+ defaultResyncPeriod,
+ "",
+ },
+ {
+ "template keyword with valid template",
+ `kubernetes coredns.local {
+ template {service}.{namespace}.{zone}
+}`,
+ false,
+ "",
+ 1,
+ true,
+ 0,
+ defaultResyncPeriod,
+ "",
+ },
+ {
+ "namespaces keyword with one namespace",
+ `kubernetes coredns.local {
+ namespaces demo
+}`,
+ false,
+ "",
+ 1,
+ true,
+ 1,
+ defaultResyncPeriod,
+ "",
+ },
+ {
+ "namespaces keyword with multiple namespaces",
+ `kubernetes coredns.local {
+ namespaces demo test
+}`,
+ false,
+ "",
+ 1,
+ true,
+ 2,
+ defaultResyncPeriod,
+ "",
+ },
+ {
+ "resync period in seconds",
+ `kubernetes coredns.local {
+ resyncperiod 30s
+}`,
+ false,
+ "",
+ 1,
+ true,
+ 0,
+ 30 * time.Second,
+ "",
+ },
+ {
+ "resync period in minutes",
+ `kubernetes coredns.local {
+ resyncperiod 15m
+}`,
+ false,
+ "",
+ 1,
+ true,
+ 0,
+ 15 * time.Minute,
+ "",
+ },
+ {
+ "basic label selector",
+ `kubernetes coredns.local {
+ labels environment=prod
+}`,
+ false,
+ "",
+ 1,
+ true,
+ 0,
+ defaultResyncPeriod,
+ "environment=prod",
+ },
+ {
+ "multi-label selector",
+ `kubernetes coredns.local {
+ labels environment in (production, staging, qa),application=nginx
+}`,
+ false,
+ "",
+ 1,
+ true,
+ 0,
+ defaultResyncPeriod,
+ "application=nginx,environment in (production,qa,staging)",
+ },
+ {
+ "fully specified valid config",
+ `kubernetes coredns.local test.local {
+ resyncperiod 15m
+ endpoint http://localhost:8080
+ template {service}.{namespace}.{zone}
+ namespaces demo test
+ labels environment in (production, staging, qa),application=nginx
+}`,
+ false,
+ "",
+ 2,
+ true,
+ 2,
+ 15 * time.Minute,
+ "application=nginx,environment in (production,qa,staging)",
+ },
+ // negative
+ {
+ "no kubernetes keyword",
+ "",
+ true,
+ "Kubernetes setup called without keyword 'kubernetes' in Corefile",
+ -1,
+ false,
+ -1,
+ defaultResyncPeriod,
+ "",
+ },
+ {
+ "kubernetes keyword without a zone",
+ `kubernetes`,
+ true,
+ "Zone name must be provided for kubernetes middleware",
+ -1,
+ true,
+ 0,
+ defaultResyncPeriod,
+ "",
+ },
+ {
+ "endpoint keyword without an endpoint value",
+ `kubernetes coredns.local {
+ endpoint
+}`,
+ true,
+ "Wrong argument count or unexpected line ending after 'endpoint'",
+ -1,
+ true,
+ -1,
+ defaultResyncPeriod,
+ "",
+ },
+ {
+ "template keyword without a template value",
+ `kubernetes coredns.local {
+ template
+}`,
+ true,
+ "Wrong argument count or unexpected line ending after 'template'",
+ -1,
+ false,
+ 0,
+ defaultResyncPeriod,
+ "",
+ },
+ {
+ "template keyword with an invalid template value",
+ `kubernetes coredns.local {
+ template {namespace}.{zone}
+}`,
+ true,
+ "Record name template does not pass NameTemplate validation",
+ -1,
+ false,
+ 0,
+ defaultResyncPeriod,
+ "",
+ },
+ {
+ "namespace keyword without a namespace value",
+ `kubernetes coredns.local {
+ namespaces
+}`,
+ true,
+ "Parse error: Wrong argument count or unexpected line ending after 'namespaces'",
+ -1,
+ true,
+ -1,
+ defaultResyncPeriod,
+ "",
+ },
+ {
+ "resyncperiod keyword without a duration value",
+ `kubernetes coredns.local {
+ resyncperiod
+}`,
+ true,
+ "Wrong argument count or unexpected line ending after 'resyncperiod'",
+ -1,
+ true,
+ 0,
+ 0 * time.Minute,
+ "",
+ },
+ {
+ "resync period no units",
+ `kubernetes coredns.local {
+ resyncperiod 15
+}`,
+ true,
+ "Unable to parse resync duration value. Value provided was ",
+ -1,
+ true,
+ 0,
+ 0 * time.Second,
+ "",
+ },
+ {
+ "resync period invalid",
+ `kubernetes coredns.local {
+ resyncperiod abc
+}`,
+ true,
+ "Unable to parse resync duration value. Value provided was ",
+ -1,
+ true,
+ 0,
+ 0 * time.Second,
+ "",
+ },
+ {
+ "labels with no selector value",
+ `kubernetes coredns.local {
+ labels
+}`,
+ true,
+ "Wrong argument count or unexpected line ending after 'labels'",
+ -1,
+ true,
+ 0,
+ 0 * time.Second,
+ "",
+ },
+ {
+ "labels with invalid selector value",
+ `kubernetes coredns.local {
+ labels environment in (production, qa
+}`,
+ true,
+ "Unable to parse label selector. Value provided was",
+ -1,
+ true,
+ 0,
+ 0 * time.Second,
+ "",
+ },
+ }
+
+ t.Logf("Parser test cases count: %v", len(tests))
+ for i, test := range tests {
+ c := caddy.NewTestController("dns", test.input)
+ k8sController, err := kubernetesParse(c)
+ t.Logf("setup test: %2v -- %v\n", i, test.description)
+ //t.Logf("controller: %v\n", k8sController)
+
+ 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 test.shouldErr && (test.expectedZoneCount >= 0) {
+ t.Errorf("Test %d: Test marked as expecting an error, but provides value for expectedZoneCount!=-1 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
+ }
+
+ // No error was raised, so validate initialization of k8sController
+ // Zones
+ foundZoneCount := len(k8sController.Zones)
+ if foundZoneCount != test.expectedZoneCount {
+ t.Errorf("Test %d: Expected kubernetes controller to be initialized with %d zones, instead found %d zones: '%v' for input '%s'", i, test.expectedZoneCount, foundZoneCount, k8sController.Zones, test.input)
+ }
+
+ // NameTemplate
+ if k8sController.NameTemplate == nil {
+ t.Errorf("Test %d: Expected kubernetes controller to be initialized with a NameTemplate. Instead found '%v' for input '%s'", i, k8sController.NameTemplate, test.input)
+ } else {
+ foundNTValid := k8sController.NameTemplate.IsValid()
+ if foundNTValid != test.expectedNTValid {
+ t.Errorf("Test %d: Expected NameTemplate validity to be '%v', instead found '%v' for input '%s'", i, test.expectedNTValid, foundNTValid, test.input)
+ }
+ }
+
+ // Namespaces
+ foundNSCount := len(k8sController.Namespaces)
+ if foundNSCount != test.expectedNSCount {
+ t.Errorf("Test %d: Expected kubernetes controller to be initialized with %d namespaces. Instead found %d namespaces: '%v' for input '%s'", i, test.expectedNSCount, foundNSCount, k8sController.Namespaces, test.input)
+ }
+
+ // ResyncPeriod
+ foundResyncPeriod := k8sController.ResyncPeriod
+ if foundResyncPeriod != test.expectedResyncPeriod {
+ t.Errorf("Test %d: Expected kubernetes controller to be initialized with resync period '%s'. Instead found period '%s' for input '%s'", i, test.expectedResyncPeriod, foundResyncPeriod, test.input)
+ }
+
+ // Labels
+ if k8sController.LabelSelector != nil {
+ foundLabelSelectorString := unversionedapi.FormatLabelSelector(k8sController.LabelSelector)
+ 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)
+ }
+ }
+ }
+}