|
|
|
@ -21,6 +21,7 @@ import (
|
|
|
|
|
"log"
|
|
|
|
|
"path"
|
|
|
|
|
"path/filepath"
|
|
|
|
|
"regexp"
|
|
|
|
|
"sort"
|
|
|
|
|
"strings"
|
|
|
|
|
"text/template"
|
|
|
|
@ -80,6 +81,14 @@ type renderable struct {
|
|
|
|
|
basePath string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const warnStartDelim = "HELM_ERR_START"
|
|
|
|
|
const warnEndDelim = "HELM_ERR_END"
|
|
|
|
|
var warnRegex = regexp.MustCompile(warnStartDelim + `(.*)` + warnEndDelim)
|
|
|
|
|
|
|
|
|
|
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()
|
|
|
|
@ -126,7 +135,7 @@ func (e Engine) initFunMap(t *template.Template, referenceTpls map[string]render
|
|
|
|
|
log.Printf("[INFO] Missing required value: %s", warn)
|
|
|
|
|
return "", nil
|
|
|
|
|
}
|
|
|
|
|
return val, errors.Errorf(warn)
|
|
|
|
|
return val, errors.Errorf(warnWrap(warn))
|
|
|
|
|
} else if _, ok := val.(string); ok {
|
|
|
|
|
if val == "" {
|
|
|
|
|
if e.LintMode {
|
|
|
|
@ -134,7 +143,7 @@ func (e Engine) initFunMap(t *template.Template, referenceTpls map[string]render
|
|
|
|
|
log.Printf("[INFO] Missing required value: %s", warn)
|
|
|
|
|
return "", nil
|
|
|
|
|
}
|
|
|
|
|
return val, errors.Errorf(warn)
|
|
|
|
|
return val, errors.Errorf(warnWrap(warn))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return val, nil
|
|
|
|
@ -181,7 +190,7 @@ func (e Engine) renderWithReferences(tpls, referenceTpls map[string]renderable)
|
|
|
|
|
for _, filename := range keys {
|
|
|
|
|
r := tpls[filename]
|
|
|
|
|
if _, err := t.New(filename).Parse(r.tpl); err != nil {
|
|
|
|
|
return map[string]string{}, parseTemplateError(filename, err)
|
|
|
|
|
return map[string]string{}, cleanupParseError(filename, err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -190,7 +199,7 @@ func (e Engine) renderWithReferences(tpls, referenceTpls map[string]renderable)
|
|
|
|
|
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)
|
|
|
|
|
return map[string]string{}, cleanupParseError(filename, err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -207,7 +216,7 @@ func (e Engine) renderWithReferences(tpls, referenceTpls map[string]renderable)
|
|
|
|
|
vals["Template"] = chartutil.Values{"Name": filename, "BasePath": tpls[filename].basePath}
|
|
|
|
|
var buf strings.Builder
|
|
|
|
|
if err := t.ExecuteTemplate(&buf, filename, vals); err != nil {
|
|
|
|
|
return map[string]string{}, parseTemplateError(filename, err)
|
|
|
|
|
return map[string]string{}, cleanupExecError(filename, err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Work around the issue where Go will emit "<no value>" even if Options(missing=zero)
|
|
|
|
@ -223,18 +232,41 @@ func (e Engine) renderWithReferences(tpls, referenceTpls map[string]renderable)
|
|
|
|
|
return rendered, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func parseTemplateError(filename string, err error) error {
|
|
|
|
|
func cleanupParseError(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)
|
|
|
|
|
return fmt.Errorf("parse 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)
|
|
|
|
|
return fmt.Errorf("parse error at (%s): %s", string(location), errMsg)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func cleanupExecError(filename string, err error) error {
|
|
|
|
|
if _, isExecError := err.(template.ExecError); !isExecError {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tokens := strings.SplitN(err.Error(), ": ", 3)
|
|
|
|
|
if len(tokens) != 3 {
|
|
|
|
|
// This might happen if a non-templating error occurs
|
|
|
|
|
return fmt.Errorf("execution 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]
|
|
|
|
|
|
|
|
|
|
parts := warnRegex.FindStringSubmatch(tokens[2])
|
|
|
|
|
if len(parts) >= 2 {
|
|
|
|
|
return fmt.Errorf("execution error at (%s): %s", string(location), parts[1])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func sortTemplates(tpls map[string]renderable) []string {
|
|
|
|
|