fix(engine): Fix template rendering thread safety issue (#4828)

Remove the engine `currentTemplates` field which was shared state
across threads and thus not thread safe, and instead just pass these
reference templates as parameters down recursively.

Closes #4819

Signed-off-by: Sean Eagan <sean.eagan@att.com>
pull/4882/head
Sean Eagan 6 years ago committed by Matthew Fisher
parent 4a49abb81e
commit 6635bff38f

@ -38,8 +38,7 @@ 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
CurrentTemplates map[string]renderable
Strict bool
// In LintMode, some 'required' template values may be missing, so don't fail
LintMode bool
}
@ -122,7 +121,6 @@ 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)
}
@ -139,7 +137,7 @@ type renderable struct {
// 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 {
func (e *Engine) alterFuncMap(t *template.Template, referenceTpls map[string]renderable) 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 {
@ -198,7 +196,7 @@ func (e *Engine) alterFuncMap(t *template.Template) template.FuncMap {
templates[templateName.(string)] = r
result, err := e.render(templates)
result, err := e.renderWithReferences(templates, referenceTpls)
if err != nil {
return "", fmt.Errorf("Error during tpl function execution for %q: %s", tpl, err.Error())
}
@ -210,6 +208,12 @@ func (e *Engine) alterFuncMap(t *template.Template) template.FuncMap {
// render takes a map of templates/values and renders them.
func (e *Engine) render(tpls map[string]renderable) (rendered map[string]string, err error) {
return e.renderWithReferences(tpls, tpls)
}
// renderWithReferences takes a map of templates/values to render, and a map of
// templates which can be referenced within them.
func (e *Engine) renderWithReferences(tpls map[string]renderable, referenceTpls map[string]renderable) (rendered map[string]string, err error) {
// Basically, what we do here is start with an empty parent template and then
// build up a list of templates -- one for each file. Once all of the templates
// have been parsed, we loop through again and execute every template.
@ -231,7 +235,7 @@ func (e *Engine) render(tpls map[string]renderable) (rendered map[string]string,
t.Option("missingkey=zero")
}
funcMap := e.alterFuncMap(t)
funcMap := e.alterFuncMap(t, referenceTpls)
// We want to parse the templates in a predictable order. The order favors
// higher-level (in file system) templates over deeply nested templates.
@ -248,9 +252,9 @@ func (e *Engine) render(tpls map[string]renderable) (rendered map[string]string,
files = append(files, fname)
}
// Adding the engine's currentTemplates to the template context
// Adding the reference templates to the template context
// so they can be referenced in the tpl function
for fname, r := range e.CurrentTemplates {
for fname, r := range referenceTpls {
if t.Lookup(fname) == nil {
t = t.New(fname).Funcs(funcMap)
if _, err := t.Parse(r.tpl); err != nil {

Loading…
Cancel
Save