diff --git a/pkg/engine/engine.go b/pkg/engine/engine.go index cb88088e6..73b8dabad 100644 --- a/pkg/engine/engine.go +++ b/pkg/engine/engine.go @@ -88,6 +88,28 @@ type renderable struct { vals chartutil.Values } +// alterFuncMap takes the Engine's FuncMap and adds context-specific functions. +// +// The resulting FuncMap is only valid for the passed-in template. +func (e *Engine) alterFuncMap(t *template.Template) template.FuncMap { + // Clone the func map because we are adding context-specific functions. + var funcMap template.FuncMap = map[string]interface{}{} + for k, v := range e.FuncMap { + funcMap[k] = v + } + + // Add the 'include' function here so we can close over t. + funcMap["include"] = func(name string, data interface{}) string { + buf := bytes.NewBuffer(nil) + if err := t.ExecuteTemplate(buf, name, data); err != nil { + buf.WriteString(err.Error()) + } + return buf.String() + } + + return funcMap +} + // render takes a map of templates/values and renders them. func (e *Engine) render(tpls map[string]renderable) (map[string]string, error) { // Basically, what we do here is start with an empty parent template and then @@ -105,10 +127,13 @@ func (e *Engine) render(tpls map[string]renderable) (map[string]string, error) { // but will still emit for others. We mitigate that later. t.Option("missingkey=zero") } + + funcMap := e.alterFuncMap(t) + files := []string{} for fname, r := range tpls { log.Printf("Preparing template %s", fname) - t = t.New(fname).Funcs(e.FuncMap) + 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) } diff --git a/pkg/engine/engine_test.go b/pkg/engine/engine_test.go index 03ee083dc..f680d2053 100644 --- a/pkg/engine/engine_test.go +++ b/pkg/engine/engine_test.go @@ -358,3 +358,33 @@ func TestRenderBuiltinValues(t *testing.T) { } } + +func TestAlterFuncMap(t *testing.T) { + c := &chart.Chart{ + Metadata: &chart.Metadata{Name: "conrad"}, + Templates: []*chart.Template{ + {Name: "quote", Data: []byte(`{{include "conrad/_partial" . | indent 2}} dead.`)}, + {Name: "_partial", Data: []byte(`{{.Release.Name}} - he`)}, + }, + Values: &chart.Config{Raw: ``}, + Dependencies: []*chart.Chart{}, + } + + v := chartutil.Values{ + "Values": &chart.Config{Raw: ""}, + "Chart": c.Metadata, + "Release": chartutil.Values{ + "Name": "Mistah Kurtz", + }, + } + + out, err := New().Render(c, v) + if err != nil { + t.Fatal(err) + } + + expect := " Mistah Kurtz - he dead." + if got := out["conrad/quote"]; got != expect { + t.Errorf("Expected %q, got %q (%v)", expect, got, out) + } +}