aboutsummaryrefslogtreecommitdiff
path: root/plugin/kubernetes
diff options
context:
space:
mode:
Diffstat (limited to 'plugin/kubernetes')
-rw-r--r--plugin/kubernetes/README.md11
-rw-r--r--plugin/kubernetes/kubernetes.go36
-rw-r--r--plugin/kubernetes/kubernetes_test.go18
-rw-r--r--plugin/kubernetes/reverse.go2
-rw-r--r--plugin/kubernetes/setup.go7
-rw-r--r--plugin/kubernetes/setup_test.go63
6 files changed, 114 insertions, 23 deletions
diff --git a/plugin/kubernetes/README.md b/plugin/kubernetes/README.md
index a2eabe97f..358b0b307 100644
--- a/plugin/kubernetes/README.md
+++ b/plugin/kubernetes/README.md
@@ -31,6 +31,7 @@ kubernetes [ZONES...] {
namespaces NAMESPACE...
labels EXPRESSION
pods POD-MODE
+ endpoint_pod_names
upstream ADDRESS...
ttl TTL
fallthrough
@@ -65,6 +66,16 @@ kubernetes [ZONES...] {
option requires substantially more memory than in insecure mode, since it will maintain a watch
on all pods.
+* `endpoint_pod_names` Use the pod name of the pod targeted by the endpoint as
+ the endpoint name in A records, e.g.
+ `endpoint-name.my-service.namespace.svc.cluster.local. in A 1.2.3.4`
+ By default, the endpoint-name name selection is as follows: Use the hostname
+ of the endpoint, or if hostname is not set, use the dashed form of the endpoint
+ ip address (e.g. `1-2-3-4.my-service.namespace.svc.cluster.local.`)
+ If this directive is included, then name selection for endpoints changes as
+ follows: Use the hostname of the endpoint, or if hostname is not set, use the
+ pod name of the pod targeted by the endpoint. If there is no pod targeted by
+ the endpoint, use the dashed ip address form.
* `upstream` **ADDRESS [ADDRESS...]** defines the upstream resolvers used for resolving services
that point to external hosts (External Services). **ADDRESS** can be an ip, an ip:port, or a path
to a file structured like resolv.conf.
diff --git a/plugin/kubernetes/kubernetes.go b/plugin/kubernetes/kubernetes.go
index 23b8f1b00..ad89e1686 100644
--- a/plugin/kubernetes/kubernetes.go
+++ b/plugin/kubernetes/kubernetes.go
@@ -28,19 +28,20 @@ import (
// Kubernetes implements a plugin that connects to a Kubernetes cluster.
type Kubernetes struct {
- Next plugin.Handler
- Zones []string
- Proxy proxy.Proxy // Proxy for looking up names during the resolution process
- APIServerList []string
- APIProxy *apiProxy
- APICertAuth string
- APIClientCert string
- APIClientKey string
- APIConn dnsController
- Namespaces map[string]bool
- podMode string
- Fallthrough bool
- ttl uint32
+ Next plugin.Handler
+ Zones []string
+ Proxy proxy.Proxy // Proxy for looking up names during the resolution process
+ APIServerList []string
+ APIProxy *apiProxy
+ APICertAuth string
+ APIClientCert string
+ APIClientKey string
+ APIConn dnsController
+ Namespaces map[string]bool
+ podMode string
+ endpointNameMode bool
+ Fallthrough bool
+ ttl uint32
primaryZoneIndex int
interfaceAddrsFunc func() net.IP
@@ -276,10 +277,13 @@ func (k *Kubernetes) Records(state request.Request, exact bool) ([]msg.Service,
return services, err
}
-func endpointHostname(addr api.EndpointAddress) string {
+func endpointHostname(addr api.EndpointAddress, endpointNameMode bool) string {
if addr.Hostname != "" {
return strings.ToLower(addr.Hostname)
}
+ if endpointNameMode && addr.TargetRef != nil && addr.TargetRef.Name != "" {
+ return addr.TargetRef.Name
+ }
if strings.Contains(addr.IP, ".") {
return strings.Replace(addr.IP, ".", "-", -1)
}
@@ -375,7 +379,7 @@ func (k *Kubernetes) findServices(r recordRequest, zone string) (services []msg.
// See comments in parse.go parseRequest about the endpoint handling.
if r.endpoint != "" {
- if !match(r.endpoint, endpointHostname(addr)) {
+ if !match(r.endpoint, endpointHostname(addr, k.endpointNameMode)) {
continue
}
}
@@ -385,7 +389,7 @@ func (k *Kubernetes) findServices(r recordRequest, zone string) (services []msg.
continue
}
s := msg.Service{Host: addr.IP, Port: int(p.Port), TTL: k.ttl}
- s.Key = strings.Join([]string{zonePath, Svc, svc.Namespace, svc.Name, endpointHostname(addr)}, "/")
+ s.Key = strings.Join([]string{zonePath, Svc, svc.Namespace, svc.Name, endpointHostname(addr, k.endpointNameMode)}, "/")
err = nil
diff --git a/plugin/kubernetes/kubernetes_test.go b/plugin/kubernetes/kubernetes_test.go
index ae0bb13e5..de8c3f025 100644
--- a/plugin/kubernetes/kubernetes_test.go
+++ b/plugin/kubernetes/kubernetes_test.go
@@ -34,15 +34,21 @@ func TestWildcard(t *testing.T) {
func TestEndpointHostname(t *testing.T) {
var tests = []struct {
- ip string
- hostname string
- expected string
+ ip string
+ hostname string
+ expected string
+ podName string
+ endpointNameMode bool
}{
- {"10.11.12.13", "", "10-11-12-13"},
- {"10.11.12.13", "epname", "epname"},
+ {"10.11.12.13", "", "10-11-12-13", "", false},
+ {"10.11.12.13", "epname", "epname", "", false},
+ {"10.11.12.13", "", "10-11-12-13", "hello-abcde", false},
+ {"10.11.12.13", "epname", "epname", "hello-abcde", false},
+ {"10.11.12.13", "epname", "epname", "hello-abcde", true},
+ {"10.11.12.13", "", "hello-abcde", "hello-abcde", true},
}
for _, test := range tests {
- result := endpointHostname(api.EndpointAddress{IP: test.ip, Hostname: test.hostname})
+ result := endpointHostname(api.EndpointAddress{IP: test.ip, Hostname: test.hostname, TargetRef: &api.ObjectReference{Name: test.podName}}, test.endpointNameMode)
if result != test.expected {
t.Errorf("Expected endpoint name for (ip:%v hostname:%v) to be '%v', but got '%v'", test.ip, test.hostname, test.expected, result)
}
diff --git a/plugin/kubernetes/reverse.go b/plugin/kubernetes/reverse.go
index a67f59a5f..13cc78b8f 100644
--- a/plugin/kubernetes/reverse.go
+++ b/plugin/kubernetes/reverse.go
@@ -42,7 +42,7 @@ func (k *Kubernetes) serviceRecordForIP(ip, name string) []msg.Service {
for _, eps := range ep.Subsets {
for _, addr := range eps.Addresses {
if addr.IP == ip {
- domain := strings.Join([]string{endpointHostname(addr), ep.ObjectMeta.Name, ep.ObjectMeta.Namespace, Svc, k.primaryZone()}, ".")
+ domain := strings.Join([]string{endpointHostname(addr, k.endpointNameMode), ep.ObjectMeta.Name, ep.ObjectMeta.Namespace, Svc, k.primaryZone()}, ".")
return []msg.Service{{Host: domain}}
}
}
diff --git a/plugin/kubernetes/setup.go b/plugin/kubernetes/setup.go
index 13e6c810f..fd63d6ff6 100644
--- a/plugin/kubernetes/setup.go
+++ b/plugin/kubernetes/setup.go
@@ -104,6 +104,13 @@ func kubernetesParse(c *caddy.Controller) (*Kubernetes, dnsControlOpts, error) {
for c.NextBlock() {
switch c.Val() {
+ case "endpoint_pod_names":
+ args := c.RemainingArgs()
+ if len(args) > 0 {
+ return nil, opts, c.ArgErr()
+ }
+ k8s.endpointNameMode = true
+ continue
case "pods":
args := c.RemainingArgs()
if len(args) == 1 {
diff --git a/plugin/kubernetes/setup_test.go b/plugin/kubernetes/setup_test.go
index 3f15ead06..224c168a1 100644
--- a/plugin/kubernetes/setup_test.go
+++ b/plugin/kubernetes/setup_test.go
@@ -471,3 +471,66 @@ func TestKubernetesParse(t *testing.T) {
}
}
}
+
+func TestKubernetesEndpointsParse(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.
+ expectedEndpointMode bool
+ }{
+ // valid endpoints mode
+ {
+ `kubernetes coredns.local {
+ endpoint_pod_names
+}`,
+ false,
+ "",
+ true,
+ },
+ // endpoints invalid
+ {
+ `kubernetes coredns.local {
+ endpoint_pod_names giant_seed
+}`,
+ true,
+ "rong argument count or unexpected",
+ false,
+ },
+ // endpoint not set
+ {
+ `kubernetes coredns.local {
+}`,
+ false,
+ "",
+ false,
+ },
+ }
+
+ 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 !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
+ }
+
+ // Endpoints
+ foundEndpointNameMode := k8sController.endpointNameMode
+ if foundEndpointNameMode != test.expectedEndpointMode {
+ t.Errorf("Test %d: Expected kubernetes controller to be initialized with endpoints mode '%v'. Instead found endpoints mode '%v' for input '%s'", i, test.expectedEndpointMode, foundEndpointNameMode, test.input)
+ }
+ }
+}