Stop Lint from breaking when using required

Have updated the required filter so that it doesn't break when linting a
chart. This work is based off #4221 and #4748 which didn't make it into
the v3 branch.

Signed-off-by: Thomas O'Donnell <andy.tom@gmail.com>
pull/5889/head
Thomas O'Donnell 6 years ago
parent c35dbb7aab
commit 213f714604

@ -18,6 +18,7 @@ package engine
import ( import (
"fmt" "fmt"
"log"
"path" "path"
"path/filepath" "path/filepath"
"sort" "sort"
@ -35,6 +36,8 @@ type Engine struct {
// If strict is enabled, template rendering will fail if a template references // If strict is enabled, template rendering will fail if a template references
// a value that was not passed in. // a value that was not passed in.
Strict bool Strict bool
// In LintMode, some 'required' template values may be missing, so don't fail
LintMode bool
} }
// Render takes a chart, optional values, and value overrides, and attempts to render the Go templates. // Render takes a chart, optional values, and value overrides, and attempts to render the Go templates.
@ -114,6 +117,29 @@ func (e Engine) initFunMap(t *template.Template, referenceTpls map[string]render
} }
return result[templateName.(string)], nil return result[templateName.(string)], nil
} }
// Add the `required` function here so we can use lintMode
funcMap["required"] = func(warn string, val interface{}) (interface{}, error) {
if val == nil {
if e.LintMode {
// Don't fail on missing required values when linting
log.Printf("[INFO] Missing required value: %s", warn)
return "", nil
}
return val, errors.Errorf(warn)
} else if _, ok := val.(string); ok {
if val == "" {
if e.LintMode {
// Don't fail on missing required values when linting
log.Printf("[INFO] Missing required value: %s", warn)
return "", nil
}
return val, errors.Errorf(warn)
}
}
return val, nil
}
t.Funcs(funcMap) t.Funcs(funcMap)
} }

@ -480,6 +480,33 @@ func TestAlterFuncMap_require(t *testing.T) {
if gotNum := out["conan/templates/bases"]; gotNum != expectNum { if gotNum := out["conan/templates/bases"]; gotNum != expectNum {
t.Errorf("Expected %q, got %q (%v)", expectNum, gotNum, out) t.Errorf("Expected %q, got %q (%v)", expectNum, gotNum, out)
} }
// test required without passing in needed values with lint mode on
// verifies lint replaces required with an empty string (should not fail)
lintValues := chartutil.Values{
"Values": chartutil.Values{
"who": "us",
},
"Chart": c.Metadata,
"Release": chartutil.Values{
"Name": "That 90s meme",
},
}
var e Engine
e.LintMode = true
out, err = e.Render(c, lintValues)
if err != nil {
t.Fatal(err)
}
expectStr = "All your base are belong to us"
if gotStr := out["conan/templates/quote"]; gotStr != expectStr {
t.Errorf("Expected %q, got %q (%v)", expectStr, gotStr, out)
}
expectNum = "All of them!"
if gotNum := out["conan/templates/bases"]; gotNum != expectNum {
t.Errorf("Expected %q, got %q (%v)", expectNum, gotNum, out)
}
} }
func TestAlterFuncMap_tpl(t *testing.T) { func TestAlterFuncMap_tpl(t *testing.T) {

@ -24,7 +24,6 @@ import (
"github.com/BurntSushi/toml" "github.com/BurntSushi/toml"
"github.com/Masterminds/sprig" "github.com/Masterminds/sprig"
"github.com/pkg/errors"
yaml "gopkg.in/yaml.v2" yaml "gopkg.in/yaml.v2"
) )
@ -54,13 +53,13 @@ func funcMap() template.FuncMap {
"fromYaml": fromYAML, "fromYaml": fromYAML,
"toJson": toJSON, "toJson": toJSON,
"fromJson": fromJSON, "fromJson": fromJSON,
"required": required,
// This is a placeholder for the "include" function, which is // This is a placeholder for the "include" function, which is
// late-bound to a template. By declaring it here, we preserve the // late-bound to a template. By declaring it here, we preserve the
// integrity of the linter. // integrity of the linter.
"include": func(string, interface{}) string { return "not implemented" }, "include": func(string, interface{}) string { return "not implemented" },
"tpl": func(string, interface{}) interface{} { return "not implemented" }, "tpl": func(string, interface{}) interface{} { return "not implemented" },
"required": func(string, interface{}) (interface{}, error) { return "not implemented", nil },
} }
for k, v := range extra { for k, v := range extra {
@ -70,17 +69,6 @@ func funcMap() template.FuncMap {
return f return f
} }
func required(warn string, val interface{}) (interface{}, error) {
if val == nil {
return val, errors.Errorf(warn)
} else if _, ok := val.(string); ok {
if val == "" {
return val, errors.Errorf(warn)
}
}
return val, nil
}
// toYAML takes an interface, marshals it to yaml, and returns a string. It will // toYAML takes an interface, marshals it to yaml, and returns a string. It will
// always return a string, even on marshal error (empty string). // always return a string, even on marshal error (empty string).
// //

@ -30,10 +30,6 @@ func TestFuncs(t *testing.T) {
tpl, expect string tpl, expect string
vars interface{} vars interface{}
}{{ }{{
tpl: `All {{ required "A valid 'bases' is required" .bases }} of them!`,
expect: `All 2 of them!`,
vars: map[string]interface{}{"bases": 2},
}, {
tpl: `{{ toYaml . }}`, tpl: `{{ toYaml . }}`,
expect: `foo: bar`, expect: `foo: bar`,
vars: map[string]interface{}{"foo": "bar"}, vars: map[string]interface{}{"foo": "bar"},

@ -68,6 +68,7 @@ func Templates(linter *support.Linter, values map[string]interface{}, namespace
} }
var e engine.Engine var e engine.Engine
e.Strict = strict e.Strict = strict
e.LintMode = true
renderedContentMap, err := e.Render(chart, valuesToRender) renderedContentMap, err := e.Render(chart, valuesToRender)
renderOk := linter.RunLinterRule(support.ErrorSev, path, err) renderOk := linter.RunLinterRule(support.ErrorSev, path, err)

Loading…
Cancel
Save