diff --git a/pkg/action/action.go b/pkg/action/action.go index 937b42537..e91054a28 100644 --- a/pkg/action/action.go +++ b/pkg/action/action.go @@ -25,6 +25,7 @@ import ( "path" "path/filepath" "strings" + "text/template" "github.com/pkg/errors" "k8s.io/apimachinery/pkg/api/meta" @@ -80,6 +81,9 @@ type Configuration struct { // Capabilities describes the capabilities of the Kubernetes cluster. Capabilities *chartutil.Capabilities + // CustomTemplateFuncs is defined by users to provide custom template funcs + CustomTemplateFuncs template.FuncMap + // HookOutputFunc called with container name and returns and expects writer that will receive the log output. HookOutputFunc func(namespace, pod, container string) io.Writer } @@ -118,10 +122,14 @@ func (cfg *Configuration) renderResources(ch *chart.Chart, values chartutil.Valu } e := engine.New(restConfig) e.EnableDNS = enableDNS + e.CustomTemplateFuncs = cfg.CustomTemplateFuncs + files, err2 = e.Render(ch, values) } else { var e engine.Engine e.EnableDNS = enableDNS + e.CustomTemplateFuncs = cfg.CustomTemplateFuncs + files, err2 = e.Render(ch, values) } diff --git a/pkg/engine/engine.go b/pkg/engine/engine.go index 7235b026a..0b0933def 100644 --- a/pkg/engine/engine.go +++ b/pkg/engine/engine.go @@ -44,6 +44,8 @@ type Engine struct { clientProvider *ClientProvider // EnableDNS tells the engine to allow DNS lookups when rendering templates EnableDNS bool + // CustomTemplateFuncs is defined by users to provide custom template funcs + CustomTemplateFuncs template.FuncMap } // New creates a new instance of Engine using the passed in rest config. @@ -244,6 +246,11 @@ func (e Engine) initFunMap(t *template.Template) { } } + // Set custom template funcs + for k, v := range e.CustomTemplateFuncs { + funcMap[k] = v + } + t.Funcs(funcMap) } diff --git a/pkg/engine/engine_test.go b/pkg/engine/engine_test.go index a54e99cad..68e0158fa 100644 --- a/pkg/engine/engine_test.go +++ b/pkg/engine/engine_test.go @@ -1300,3 +1300,63 @@ func TestRenderTplMissingKeyString(t *testing.T) { t.Fatal(err) } } + +func TestRenderCustomTemplateFuncs(t *testing.T) { + // Create a chart with two templates that use custom functions + c := &chart.Chart{ + Metadata: &chart.Metadata{Name: "CustomFunc"}, + Templates: []*chart.File{ + { + Name: "templates/manifest", + Data: []byte(`{{exclaim .Values.message}}`), + }, + { + Name: "templates/override", + Data: []byte(`{{ upper .Values.message }}`), + }, + }, + } + v := chartutil.Values{ + "Values": chartutil.Values{ + "message": "hello", + }, + "Chart": c.Metadata, + "Release": chartutil.Values{ + "Name": "TestRelease", + }, + } + + // Define a custom template function "exclaim" that appends "!!!" to a string and override "upper" function + customFuncs := template.FuncMap{ + "exclaim": func(input string) string { + return input + "!!!" + }, + "upper": func(s string) string { + return "custom:" + s + }, + } + + // Create an engine instance and set the CustomTemplateFuncs. + e := new(Engine) + e.CustomTemplateFuncs = customFuncs + + // Render the chart. + out, err := e.Render(c, v) + if err != nil { + t.Fatal(err) + } + + // Expected output should be "hello!!!". + expected := "hello!!!" + key := "CustomFunc/templates/manifest" + if rendered, ok := out[key]; !ok || rendered != expected { + t.Errorf("Expected %q, got %q", expected, rendered) + } + + // Verify that the rendered template used the custom "upper" function. + expected = "custom:hello" + key = "CustomFunc/templates/override" + if rendered, ok := out[key]; !ok || rendered != expected { + t.Errorf("Expected %q, got %q", expected, rendered) + } +}