diff --git a/pkg/engine/engine.go b/pkg/engine/engine.go index 3d348fb41..595ec2c19 100644 --- a/pkg/engine/engine.go +++ b/pkg/engine/engine.go @@ -37,7 +37,8 @@ type Engine struct { FuncMap template.FuncMap // If strict is enabled, template rendering will fail if a template references // a value that was not passed in. - Strict bool + Strict bool + CurrentTemplates map[string]renderable } // New creates a new Go template Engine instance. @@ -65,7 +66,9 @@ func New() *Engine { // - "include": This is late-bound in Engine.Render(). The version // included in the FuncMap is a placeholder. // - "required": This is late-bound in Engine.Render(). The version -// included in thhe FuncMap is a placeholder. +// included in the FuncMap is a placeholder. +// - "tpl": This is late-bound in Engine.Render(). The version +// included in the FuncMap is a placeholder. func FuncMap() template.FuncMap { f := sprig.TxtFuncMap() delete(f, "env") @@ -84,6 +87,7 @@ func FuncMap() template.FuncMap { // integrity of the linter. "include": func(string, interface{}) string { return "not implemented" }, "required": func(string, interface{}) interface{} { return "not implemented" }, + "tpl": func(string, interface{}) interface{} { return "not implemented" }, } for k, v := range extra { @@ -115,6 +119,7 @@ func FuncMap() template.FuncMap { func (e *Engine) Render(chrt *chart.Chart, values chartutil.Values) (map[string]string, error) { // Render the charts tmap := allTemplates(chrt, values) + e.CurrentTemplates = tmap return e.render(tmap) } @@ -159,6 +164,23 @@ func (e *Engine) alterFuncMap(t *template.Template) template.FuncMap { return val, nil } + // Add the 'tpl' function here + funcMap["tpl"] = func(tpl string, vals chartutil.Values) (string, error) { + r := renderable{ + tpl: tpl, + vals: vals, + } + + templates := map[string]renderable{} + templates["aaa_template"] = r + + result, err := e.render(templates) + if err != nil { + return "", fmt.Errorf("Error during tpl function execution for %q: %s", tpl, err.Error()) + } + return result["aaa_template"], nil + } + return funcMap } @@ -187,7 +209,7 @@ func (e *Engine) render(tpls map[string]renderable) (map[string]string, error) { keys := sortTemplates(tpls) files := []string{} - //for fname, r := range tpls { + for _, fname := range keys { r := tpls[fname] t = t.New(fname).Funcs(funcMap) @@ -197,6 +219,17 @@ func (e *Engine) render(tpls map[string]renderable) (map[string]string, error) { files = append(files, fname) } + // Adding the engine's currentTemplates to the template context + // so they can be referenced in the tpl function + for fname, r := range e.CurrentTemplates { + if t.Lookup(fname) == nil { + t = t.New(fname).Funcs(funcMap) + if _, err := t.Parse(r.tpl); err != nil { + return map[string]string{}, fmt.Errorf("parse error in %q: %s", fname, err) + } + } + } + rendered := make(map[string]string, len(files)) var buf bytes.Buffer for _, file := range files { diff --git a/pkg/engine/engine_test.go b/pkg/engine/engine_test.go index 88118c420..7da4a9103 100644 --- a/pkg/engine/engine_test.go +++ b/pkg/engine/engine_test.go @@ -80,7 +80,7 @@ func TestFuncMap(t *testing.T) { } // Test for Engine-specific template functions. - expect := []string{"include", "required", "toYaml", "fromYaml", "toToml", "toJson", "fromJson"} + expect := []string{"include", "required", "tpl", "toYaml", "fromYaml", "toToml", "toJson", "fromJson"} for _, f := range expect { if _, ok := fns[f]; !ok { t.Errorf("Expected add-on function %q", f) @@ -476,4 +476,91 @@ func TestAlterFuncMap(t *testing.T) { t.Errorf("Expected %q, got %q (%v)", expectNum, gotNum, outReq) } + tplChart := &chart.Chart{ + Metadata: &chart.Metadata{Name: "TplFunction"}, + Templates: []*chart.Template{ + {Name: "templates/base", Data: []byte(`Evaluate tpl {{tpl "Value: {{ .Values.value}}" .}}`)}, + }, + Values: &chart.Config{Raw: ``}, + Dependencies: []*chart.Chart{}, + } + + tplValues := chartutil.Values{ + "Values": chartutil.Values{ + "value": "myvalue", + }, + "Chart": tplChart.Metadata, + "Release": chartutil.Values{ + "Name": "TestRelease", + }, + } + + outTpl, err := New().Render(tplChart, tplValues) + if err != nil { + t.Fatal(err) + } + + expectTplStr := "Evaluate tpl Value: myvalue" + if gotStrTpl := outTpl["TplFunction/templates/base"]; gotStrTpl != expectTplStr { + t.Errorf("Expected %q, got %q (%v)", expectTplStr, gotStrTpl, outTpl) + } + + tplChartWithFunction := &chart.Chart{ + Metadata: &chart.Metadata{Name: "TplFunction"}, + Templates: []*chart.Template{ + {Name: "templates/base", Data: []byte(`Evaluate tpl {{tpl "Value: {{ .Values.value | quote}}" .}}`)}, + }, + Values: &chart.Config{Raw: ``}, + Dependencies: []*chart.Chart{}, + } + + tplValuesWithFunction := chartutil.Values{ + "Values": chartutil.Values{ + "value": "myvalue", + }, + "Chart": tplChartWithFunction.Metadata, + "Release": chartutil.Values{ + "Name": "TestRelease", + }, + } + + outTplWithFunction, err := New().Render(tplChartWithFunction, tplValuesWithFunction) + if err != nil { + t.Fatal(err) + } + + expectTplStrWithFunction := "Evaluate tpl Value: \"myvalue\"" + if gotStrTplWithFunction := outTplWithFunction["TplFunction/templates/base"]; gotStrTplWithFunction != expectTplStrWithFunction { + t.Errorf("Expected %q, got %q (%v)", expectTplStrWithFunction, gotStrTplWithFunction, outTplWithFunction) + } + + tplChartWithInclude := &chart.Chart{ + Metadata: &chart.Metadata{Name: "TplFunction"}, + Templates: []*chart.Template{ + {Name: "templates/base", Data: []byte(`{{ tpl "{{include ` + "`" + `TplFunction/templates/_partial` + "`" + ` . | quote }}" .}}`)}, + {Name: "templates/_partial", Data: []byte(`{{.Release.Name}}`)}, + }, + Values: &chart.Config{Raw: ``}, + Dependencies: []*chart.Chart{}, + } + tplValueWithInclude := chartutil.Values{ + "Values": chartutil.Values{ + "value": "myvalue", + }, + "Chart": tplChartWithInclude.Metadata, + "Release": chartutil.Values{ + "Name": "TestRelease", + }, + } + + outTplWithInclude, err := New().Render(tplChartWithInclude, tplValueWithInclude) + if err != nil { + t.Fatal(err) + } + + expectedTplStrWithInclude := "\"TestRelease\"" + if gotStrTplWithInclude := outTplWithInclude["TplFunction/templates/base"]; gotStrTplWithInclude != expectedTplStrWithInclude { + t.Errorf("Expected %q, got %q (%v)", expectedTplStrWithInclude, gotStrTplWithInclude, outTplWithInclude) + } + }