From db4f3301229f4980c2521db13acfd7df7dd48008 Mon Sep 17 00:00:00 2001 From: Graham Reed Date: Thu, 8 Sep 2022 15:36:12 +0100 Subject: [PATCH] Speed up `tpl` - Use a clone of the current Template instead of re-creating everything from scratch - Needs to inject `include` so any defines in the tpl text can be seen. Signed-off-by: Graham Reed --- pkg/engine/engine.go | 49 +++++++++++++++++++++++++------------------- 1 file changed, 28 insertions(+), 21 deletions(-) diff --git a/pkg/engine/engine.go b/pkg/engine/engine.go index 00494f9d7..464a0427e 100644 --- a/pkg/engine/engine.go +++ b/pkg/engine/engine.go @@ -103,13 +103,10 @@ func warnWrap(warn string) string { return warnStartDelim + warn + warnEndDelim } -// initFunMap creates the Engine's FuncMap and adds context-specific functions. -func (e Engine) initFunMap(t *template.Template, referenceTpls map[string]renderable) { - funcMap := funcMap() - includedNames := make(map[string]int) - - // Add the 'include' function here so we can close over t. - funcMap["include"] = func(name string, data interface{}) (string, error) { +// 'include' needs to be defined in the scope of a 'tpl' template as +// well as regular file-loaded templates. +func includeFun(t *template.Template, includedNames map[string]int) func(string, interface{}) (string, error) { + return func(name string, data interface{}) (string, error) { var buf strings.Builder if v, ok := includedNames[name]; ok { if v > recursionMaxNums { @@ -123,32 +120,42 @@ func (e Engine) initFunMap(t *template.Template, referenceTpls map[string]render includedNames[name]-- return buf.String(), err } +} + +// initFunMap creates the Engine's FuncMap and adds context-specific functions. +func (e Engine) initFunMap(t *template.Template, referenceTpls map[string]renderable) { + funcMap := funcMap() + includedNames := make(map[string]int) + + // Add the 'include' function here so we can close over t. + funcMap["include"] = includeFun(t, includedNames) // Add the 'tpl' function here funcMap["tpl"] = func(tpl string, vals chartutil.Values) (string, error) { - basePath, err := vals.PathValue("Template.BasePath") + t, err := t.Clone() if err != nil { - return "", errors.Wrapf(err, "cannot retrieve Template.Basepath from values inside tpl function: %s", tpl) + return "", errors.Wrapf(err, "cannot clone template") } - templateName, err := vals.PathValue("Template.Name") - if err != nil { - return "", errors.Wrapf(err, "cannot retrieve Template.Name from values inside tpl function: %s", tpl) + // Re-inject 'include' so that it can close over our clone of t; + // this lets any 'define's inside tpl be 'include'd. + tplFuncMap := template.FuncMap{ + "include": includeFun(t, includedNames), } + t.Funcs(tplFuncMap) - templates := map[string]renderable{ - templateName.(string): { - tpl: tpl, - vals: vals, - basePath: basePath.(string), - }, + t, err = t.Parse(tpl) + if err != nil { + return "", errors.Wrapf(err, "cannot parse template %q", tpl) } - result, err := e.renderWithReferences(templates, referenceTpls) - if err != nil { + var buf strings.Builder + if err := t.Execute(&buf, vals); err != nil { return "", errors.Wrapf(err, "error during tpl function execution for %q", tpl) } - return result[templateName.(string)], nil + + // See comment in renderWithReferences explaining the hack. + return strings.ReplaceAll(buf.String(), "", ""), nil } // Add the `required` function here so we can use lintMode