diff options
Diffstat (limited to 'plugin/kubernetes')
-rw-r--r-- | plugin/kubernetes/README.md | 14 | ||||
-rw-r--r-- | plugin/kubernetes/handler_test.go | 7 | ||||
-rw-r--r-- | plugin/kubernetes/metadata.go | 59 | ||||
-rw-r--r-- | plugin/kubernetes/metadata_test.go | 126 |
4 files changed, 204 insertions, 2 deletions
diff --git a/plugin/kubernetes/README.md b/plugin/kubernetes/README.md index 22614db8e..68efdae0c 100644 --- a/plugin/kubernetes/README.md +++ b/plugin/kubernetes/README.md @@ -225,3 +225,17 @@ or the word "any"), then that label will match all values. The labels that acce *.service.default.svc.cluster.local. 5 IN A 192.168.25.15 ``` This response can be randomized using the `loadbalance` plugin + +## Metadata + +The kubernetes plugin will publish the following metadata, if the _metadata_ +plugin is also enabled: + + * kubernetes/endpoint: the endpoint name in the query + * kubernetes/kind: the resource kind (pod or svc) in the query + * kubernetes/namespace: the namespace in the query + * kubernetes/port-name: the port name in an SRV query + * kubernetes/protocol: the protocol in an SRV query + * kubernetes/service: the service name in the query + * kubernetes/client-namespace: the client pod's namespace, if `pods verified` mode is enabled + * kubernetes/client-pod-name: the client pod's name, if `pods verified` mode is enabled diff --git a/plugin/kubernetes/handler_test.go b/plugin/kubernetes/handler_test.go index e1a8212ca..0efd03c07 100644 --- a/plugin/kubernetes/handler_test.go +++ b/plugin/kubernetes/handler_test.go @@ -495,9 +495,12 @@ func (APIConnServeTest) EpIndexReverse(string) []*object.Endpoints { return nil func (APIConnServeTest) SvcIndexReverse(string) []*object.Service { return nil } func (APIConnServeTest) Modified() int64 { return time.Now().Unix() } -func (APIConnServeTest) PodIndex(string) []*object.Pod { +func (APIConnServeTest) PodIndex(ip string) []*object.Pod { + if ip != "10.240.0.1" { + return []*object.Pod{} + } a := []*object.Pod{ - {Namespace: "podns", PodIP: "10.240.0.1"}, // Remote IP set in test.ResponseWriter + {Namespace: "podns", Name: "foo", PodIP: "10.240.0.1"}, // Remote IP set in test.ResponseWriter } return a } diff --git a/plugin/kubernetes/metadata.go b/plugin/kubernetes/metadata.go new file mode 100644 index 000000000..323ae9e11 --- /dev/null +++ b/plugin/kubernetes/metadata.go @@ -0,0 +1,59 @@ +package kubernetes + +import ( + "context" + + "github.com/coredns/coredns/plugin/metadata" + "github.com/coredns/coredns/request" +) + +// Metadata implements the metadata.Provider interface. +func (k *Kubernetes) Metadata(ctx context.Context, state request.Request) context.Context { + // possible optimization: cache r so it doesn't need to be calculated again in ServeDNS + r, err := parseRequest(state) + if err != nil { + metadata.SetValueFunc(ctx, "kubernetes/parse-error", func() string { + return err.Error() + }) + return ctx + } + + metadata.SetValueFunc(ctx, "kubernetes/port-name", func() string { + return r.port + }) + + metadata.SetValueFunc(ctx, "kubernetes/protocol", func() string { + return r.protocol + }) + + metadata.SetValueFunc(ctx, "kubernetes/endpoint", func() string { + return r.endpoint + }) + + metadata.SetValueFunc(ctx, "kubernetes/service", func() string { + return r.service + }) + + metadata.SetValueFunc(ctx, "kubernetes/namespace", func() string { + return r.namespace + }) + + metadata.SetValueFunc(ctx, "kubernetes/kind", func() string { + return r.podOrSvc + }) + + pod := k.podWithIP(state.IP()) + if pod == nil { + return ctx + } + + metadata.SetValueFunc(ctx, "kubernetes/client-namespace", func() string { + return pod.Namespace + }) + + metadata.SetValueFunc(ctx, "kubernetes/client-pod-name", func() string { + return pod.Name + }) + + return ctx +} diff --git a/plugin/kubernetes/metadata_test.go b/plugin/kubernetes/metadata_test.go new file mode 100644 index 000000000..44f16f13e --- /dev/null +++ b/plugin/kubernetes/metadata_test.go @@ -0,0 +1,126 @@ +package kubernetes + +import ( + "context" + "testing" + + "github.com/coredns/coredns/plugin/metadata" + "github.com/coredns/coredns/plugin/test" + "github.com/coredns/coredns/request" + + "github.com/miekg/dns" +) + +var metadataCases = []struct { + Qname string + Qtype uint16 + RemoteIP string + Md map[string]string +}{ + { + Qname: "foo.bar.notapod.cluster.local.", Qtype: dns.TypeA, + Md: map[string]string{ + "kubernetes/parse-error": "invalid query name", + }, + }, + { + Qname: "10-240-0-1.podns.pod.cluster.local.", Qtype: dns.TypeA, + Md: map[string]string{ + "kubernetes/endpoint": "", + "kubernetes/kind": "pod", + "kubernetes/namespace": "podns", + "kubernetes/port-name": "*", + "kubernetes/protocol": "*", + "kubernetes/service": "10-240-0-1", + "kubernetes/client-namespace": "podns", + "kubernetes/client-pod-name": "foo", + }, + }, + { + Qname: "s.ns.svc.cluster.local.", Qtype: dns.TypeA, + Md: map[string]string{ + "kubernetes/endpoint": "", + "kubernetes/kind": "svc", + "kubernetes/namespace": "ns", + "kubernetes/port-name": "*", + "kubernetes/protocol": "*", + "kubernetes/service": "s", + "kubernetes/client-namespace": "podns", + "kubernetes/client-pod-name": "foo", + }, + }, + { + Qname: "s.ns.svc.cluster.local.", Qtype: dns.TypeA, + RemoteIP: "10.10.10.10", + Md: map[string]string{ + "kubernetes/endpoint": "", + "kubernetes/kind": "svc", + "kubernetes/namespace": "ns", + "kubernetes/port-name": "*", + "kubernetes/protocol": "*", + "kubernetes/service": "s", + }, + }, + { + Qname: "_http._tcp.s.ns.svc.cluster.local.", Qtype: dns.TypeSRV, + RemoteIP: "10.10.10.10", + Md: map[string]string{ + "kubernetes/endpoint": "", + "kubernetes/kind": "svc", + "kubernetes/namespace": "ns", + "kubernetes/port-name": "http", + "kubernetes/protocol": "tcp", + "kubernetes/service": "s", + }, + }, + { + Qname: "ep.s.ns.svc.cluster.local.", Qtype: dns.TypeA, + RemoteIP: "10.10.10.10", + Md: map[string]string{ + "kubernetes/endpoint": "ep", + "kubernetes/kind": "svc", + "kubernetes/namespace": "ns", + "kubernetes/port-name": "*", + "kubernetes/protocol": "*", + "kubernetes/service": "s", + }, + }, +} + +func mapsDiffer(a, b map[string]string) bool { + if len(a) != len(b) { + return true + } + + for k, va := range a { + vb, ok := b[k] + if !ok || va != vb { + return true + } + } + return false +} + +func TestMetadata(t *testing.T) { + k := New([]string{"cluster.local."}) + k.APIConn = &APIConnServeTest{} + + for i, tc := range metadataCases { + ctx := metadata.ContextWithMetadata(context.Background()) + state := request.Request{ + Req: &dns.Msg{Question: []dns.Question{{Name: tc.Qname, Qtype: tc.Qtype}}}, + Zone: "cluster.local.", + W: &test.ResponseWriter{RemoteIP: tc.RemoteIP}, + } + + k.Metadata(ctx, state) + + md := make(map[string]string) + for _, l := range metadata.Labels(ctx) { + md[l] = metadata.ValueFunc(ctx, l)() + } + if mapsDiffer(tc.Md, md) { + t.Errorf("case %d expected metadata %v and got %v", i, tc.Md, md) + } + } +} |