Merge pull request #5572 from ian-howell/fix/template-error-messages

fix(pkg/engine): Clean up template error messages
pull/5649/head
Matthew Fisher 5 years ago committed by GitHub
commit 5264ebd5aa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -17,6 +17,7 @@ limitations under the License.
package engine
import (
"fmt"
"path"
"sort"
"strings"
@ -150,51 +151,65 @@ func (e Engine) renderWithReferences(tpls, referenceTpls map[string]renderable)
// higher-level (in file system) templates over deeply nested templates.
keys := sortTemplates(tpls)
for _, fname := range keys {
r := tpls[fname]
if _, err := t.New(fname).Parse(r.tpl); err != nil {
return map[string]string{}, errors.Wrapf(err, "parse error in %q", fname)
for _, filename := range keys {
r := tpls[filename]
if _, err := t.New(filename).Parse(r.tpl); err != nil {
return map[string]string{}, parseTemplateError(filename, err)
}
}
// Adding the reference templates to the template context
// so they can be referenced in the tpl function
for fname, r := range referenceTpls {
if t.Lookup(fname) == nil {
if _, err := t.New(fname).Parse(r.tpl); err != nil {
return map[string]string{}, errors.Wrapf(err, "parse error in %q", fname)
for filename, r := range referenceTpls {
if t.Lookup(filename) == nil {
if _, err := t.New(filename).Parse(r.tpl); err != nil {
return map[string]string{}, parseTemplateError(filename, err)
}
}
}
rendered = make(map[string]string, len(keys))
for _, file := range keys {
for _, filename := range keys {
// Don't render partials. We don't care out the direct output of partials.
// They are only included from other templates.
if strings.HasPrefix(path.Base(file), "_") {
if strings.HasPrefix(path.Base(filename), "_") {
continue
}
// At render time, add information about the template that is being rendered.
vals := tpls[file].vals
vals["Template"] = chartutil.Values{"Name": file, "BasePath": tpls[file].basePath}
vals := tpls[filename].vals
vals["Template"] = chartutil.Values{"Name": filename, "BasePath": tpls[filename].basePath}
var buf strings.Builder
if err := t.ExecuteTemplate(&buf, file, vals); err != nil {
return map[string]string{}, errors.Wrapf(err, "render error in %q", file)
if err := t.ExecuteTemplate(&buf, filename, vals); err != nil {
return map[string]string{}, parseTemplateError(filename, err)
}
// Work around the issue where Go will emit "<no value>" even if Options(missing=zero)
// is set. Since missing=error will never get here, we do not need to handle
// the Strict case.
f := &chart.File{
Name: strings.ReplaceAll(file, "/templates", "/manifests"),
Name: strings.ReplaceAll(filename, "/templates", "/manifests"),
Data: []byte(strings.ReplaceAll(buf.String(), "<no value>", "")),
}
rendered[file] = string(f.Data)
rendered[filename] = string(f.Data)
}
return rendered, nil
}
func parseTemplateError(filename string, err error) error {
tokens := strings.Split(err.Error(), ": ")
if len(tokens) == 1 {
// This might happen if a non-templating error occurs
return fmt.Errorf("render error in (%s): %s", filename, err)
}
// The first token is "template"
// The second token is either "filename:lineno" or "filename:lineNo:columnNo"
location := tokens[1]
// The remaining tokens make up a stacktrace-like chain, ending with the relevant error
errMsg := tokens[len(tokens)-1]
return fmt.Errorf("render error at (%s): %s", string(location), errMsg)
}
func sortTemplates(tpls map[string]renderable) []string {
keys := make([]string, len(tpls))
i := 0

@ -187,6 +187,33 @@ func TestParallelRenderInternals(t *testing.T) {
wg.Wait()
}
func TestRenderErrors(t *testing.T) {
vals := chartutil.Values{"Values": map[string]interface{}{}}
tplsMissingRequired := map[string]renderable{
"missing_required": {tpl: `{{required "foo is required" .Values.foo}}`, vals: vals},
}
_, err := new(Engine).render(tplsMissingRequired)
if err == nil {
t.Fatalf("Expected failures while rendering: %s", err)
}
expected := `render error at (missing_required:1:2): foo is required`
if err.Error() != expected {
t.Errorf("Expected '%s', got %q", expected, err.Error())
}
tplsUndefinedFunction := map[string]renderable{
"undefined_function": {tpl: `{{foo}}`, vals: vals},
}
_, err = new(Engine).render(tplsUndefinedFunction)
if err == nil {
t.Fatalf("Expected failures while rendering: %s", err)
}
expected = `render error at (undefined_function:1): function "foo" not defined`
if err.Error() != expected {
t.Errorf("Expected '%s', got %q", expected, err.Error())
}
}
func TestAllTemplates(t *testing.T) {
ch1 := &chart.Chart{
Metadata: &chart.Metadata{Name: "ch1"},

Loading…
Cancel
Save