From 7ab8fde4940c3f7c2a5363675f5fe3c7c4cee859 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Max=20Sch=C3=A4fer?= <40192999+MaxSchaefer@users.noreply.github.com> Date: Mon, 26 Jun 2023 01:36:26 +0200 Subject: [PATCH] feat(nameresolution/kubernetes): add ability for templating the dns name resolution (#2883) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Max Schäfer Co-authored-by: Yaron Schneider Co-authored-by: Alessandro (Ale) Segala <43508+ItalyPaleAle@users.noreply.github.com> --- nameresolution/kubernetes/kubernetes.go | 35 ++++++++++++++++-- nameresolution/kubernetes/kubernetes_test.go | 39 ++++++++++++++++++++ 2 files changed, 70 insertions(+), 4 deletions(-) diff --git a/nameresolution/kubernetes/kubernetes.go b/nameresolution/kubernetes/kubernetes.go index a91e6f0deb..1741a7c90a 100644 --- a/nameresolution/kubernetes/kubernetes.go +++ b/nameresolution/kubernetes/kubernetes.go @@ -14,7 +14,9 @@ limitations under the License. package kubernetes import ( + "bytes" "strconv" + "text/template" "github.com/dapr/components-contrib/nameresolution" "github.com/dapr/kit/config" @@ -24,11 +26,21 @@ import ( const ( DefaultClusterDomain = "cluster.local" ClusterDomainKey = "clusterDomain" + TemplateKey = "template" ) +func executeTemplateWithResolveRequest(tmpl *template.Template, req nameresolution.ResolveRequest) (string, error) { + var addr bytes.Buffer + if err := tmpl.Execute(&addr, req); err != nil { + return "", err + } + return addr.String(), nil +} + type resolver struct { logger logger.Logger clusterDomain string + tmpl *template.Template } // NewResolver creates Kubernetes name resolver. @@ -36,6 +48,7 @@ func NewResolver(logger logger.Logger) nameresolution.Resolver { return &resolver{ logger: logger, clusterDomain: DefaultClusterDomain, + tmpl: nil, } } @@ -45,14 +58,25 @@ func (k *resolver) Init(metadata nameresolution.Metadata) error { if err != nil { return err } - if config, ok := configInterface.(map[string]interface{}); ok { - clusterDomainPtr := config[ClusterDomainKey] - if clusterDomainPtr != nil { - clusterDomain, _ := clusterDomainPtr.(string) + + if cfg, ok := configInterface.(map[string]interface{}); ok { + clusterDomainAny := cfg[ClusterDomainKey] + tmplStrAny := cfg[TemplateKey] + + if clusterDomainAny != nil { + clusterDomain, _ := clusterDomainAny.(string) if clusterDomain != "" { k.clusterDomain = clusterDomain } } + + if tmplStrAny != nil { + tmplStr, _ := tmplStrAny.(string) + if tmplStr != "" { + k.tmpl = template.Must(template.New("kubernetes-template").Parse(tmplStr)) + k.logger.Debugf("using custom template %s", tmplStr) + } + } } return nil @@ -60,6 +84,9 @@ func (k *resolver) Init(metadata nameresolution.Metadata) error { // ResolveID resolves name to address in Kubernetes. func (k *resolver) ResolveID(req nameresolution.ResolveRequest) (string, error) { + if k.tmpl != nil { + return executeTemplateWithResolveRequest(k.tmpl, req) + } // Dapr requires this formatting for Kubernetes services return req.ID + "-dapr." + req.Namespace + ".svc." + k.clusterDomain + ":" + strconv.Itoa(req.Port), nil } diff --git a/nameresolution/kubernetes/kubernetes_test.go b/nameresolution/kubernetes/kubernetes_test.go index d450b5bebe..3028de6c69 100644 --- a/nameresolution/kubernetes/kubernetes_test.go +++ b/nameresolution/kubernetes/kubernetes_test.go @@ -48,3 +48,42 @@ func TestResolveWithCustomClusterDomain(t *testing.T) { assert.NoError(t, err) assert.Equal(t, expect, target) } + +func TestResolveWithTemplate(t *testing.T) { + resolver := NewResolver(logger.NewLogger("test")) + _ = resolver.Init(nameresolution.Metadata{ + Configuration: map[string]interface{}{ + "template": "{{.ID}}-{{.Namespace}}.internal:{{.Port}}", + }, + }) + + request := nameresolution.ResolveRequest{ID: "myid", Namespace: "abc", Port: 1234} + const expected = "myid-abc.internal:1234" + target, err := resolver.ResolveID(request) + + assert.NoError(t, err) + assert.Equal(t, target, expected) +} + +func TestResolveWithTemplateAndData(t *testing.T) { + resolver := NewResolver(logger.NewLogger("test")) + _ = resolver.Init(nameresolution.Metadata{ + Configuration: map[string]interface{}{ + "template": "{{.ID}}-{{.Data.region}}.internal:{{.Port}}", + }, + }) + + request := nameresolution.ResolveRequest{ + ID: "myid", + Namespace: "abc", + Port: 1234, + Data: map[string]string{ + "region": "myland", + }, + } + const expected = "myid-myland.internal:1234" + target, err := resolver.ResolveID(request) + + assert.NoError(t, err) + assert.Equal(t, target, expected) +}