From 2fb38e49b6837eb4cda927df220a867936e0bda0 Mon Sep 17 00:00:00 2001 From: Andreas Neumann Date: Fri, 7 Feb 2020 15:48:18 +0100 Subject: [PATCH] Add RenderVerifier for templates (#1327) * Add package verifier that renders the template to detect invalid go template expressions Signed-off-by: Andreas Neumann --- pkg/engine/renderer/engine.go | 6 +-- pkg/engine/renderer/engine_test.go | 4 +- pkg/engine/task/render.go | 2 +- pkg/kudoctl/cmd/verify/verify.go | 1 + .../verifier/template/verify_render.go | 48 +++++++++++++++++++ .../verifier/template/verify_render_test.go | 32 +++++++++++++ 6 files changed, 87 insertions(+), 6 deletions(-) create mode 100644 pkg/kudoctl/packages/verifier/template/verify_render.go create mode 100644 pkg/kudoctl/packages/verifier/template/verify_render_test.go diff --git a/pkg/engine/renderer/engine.go b/pkg/engine/renderer/engine.go index 7856cbf67..6eccb158e 100644 --- a/pkg/engine/renderer/engine.go +++ b/pkg/engine/renderer/engine.go @@ -56,15 +56,15 @@ func (e Engine) Template(name string) *template.Template { // Render creates a fully rendered template based on a set of values. It parses these in strict mode, // returning errors when keys are missing. -func (e *Engine) Render(tpl string, vals map[string]interface{}) (string, error) { +func (e *Engine) Render(tplName string, tpl string, vals map[string]interface{}) (string, error) { var buf bytes.Buffer - t := e.Template("tpl") + t := e.Template(tplName) if _, err := t.Parse(tpl); err != nil { return "", fmt.Errorf("error parsing template: %s", err) } - if err := t.ExecuteTemplate(&buf, "tpl", vals); err != nil { + if err := t.ExecuteTemplate(&buf, tplName, vals); err != nil { return "", fmt.Errorf("error rendering template: %s", err) } diff --git a/pkg/engine/renderer/engine_test.go b/pkg/engine/renderer/engine_test.go index 23fabd7e7..b43bb299a 100644 --- a/pkg/engine/renderer/engine_test.go +++ b/pkg/engine/renderer/engine_test.go @@ -37,7 +37,7 @@ func TestRender(t *testing.T) { "Params": params, } - rendered, err := engine.Render(test.template, vals) + rendered, err := engine.Render(test.name, test.template, vals) if err != nil { t.Errorf("error rendering template: %s", err) } @@ -54,7 +54,7 @@ func TestUnsafeFuncs(t *testing.T) { unsafeFuncs := []string{"env", "expandenv", "base", "dir", "clean", "ext", "isAbs"} for _, fun := range unsafeFuncs { - _, err := engine.Render(fmt.Sprintf("{{ \"foo\" | %s }}", fun), nil) + _, err := engine.Render("tpl", fmt.Sprintf("{{ \"foo\" | %s }}", fun), nil) if err == nil { t.Errorf("expected error for %s, got none", fun) diff --git a/pkg/engine/task/render.go b/pkg/engine/task/render.go index 4a888281b..3bf1f5b84 100644 --- a/pkg/engine/task/render.go +++ b/pkg/engine/task/render.go @@ -31,7 +31,7 @@ func render(resourceNames []string, ctx Context) (map[string]string, error) { return nil, fmt.Errorf("error finding resource named %s", rn) } - rendered, err := engine.Render(resource, configs) + rendered, err := engine.Render(rn, resource, configs) if err != nil { return nil, fmt.Errorf("error expanding template %s: %w", rn, err) } diff --git a/pkg/kudoctl/cmd/verify/verify.go b/pkg/kudoctl/cmd/verify/verify.go index 936d1a184..6a2e64f3d 100644 --- a/pkg/kudoctl/cmd/verify/verify.go +++ b/pkg/kudoctl/cmd/verify/verify.go @@ -18,6 +18,7 @@ var verifiers = []verifier.PackageVerifier{ task.ReferenceVerifier{}, template.ParametersVerifier{}, template.ReferenceVerifier{}, + template.RenderVerifier{}, } // PackageFiles verifies operator package files diff --git a/pkg/kudoctl/packages/verifier/template/verify_render.go b/pkg/kudoctl/packages/verifier/template/verify_render.go new file mode 100644 index 000000000..c6cd97374 --- /dev/null +++ b/pkg/kudoctl/packages/verifier/template/verify_render.go @@ -0,0 +1,48 @@ +package template + +import ( + "github.com/kudobuilder/kudo/pkg/engine/renderer" + "github.com/kudobuilder/kudo/pkg/kudoctl/packages" + "github.com/kudobuilder/kudo/pkg/kudoctl/packages/verifier" +) + +var _ verifier.PackageVerifier = &RenderVerifier{} + +// RenderVerifier checks that all templates are compilable and contain valid golang template syntax +type RenderVerifier struct{} + +func (RenderVerifier) Verify(pf *packages.Files) verifier.Result { + res := verifier.NewResult() + res.Merge(templateCompilable(pf)) + return res +} + +func templateCompilable(pf *packages.Files) verifier.Result { + params := make(map[string]string) + for _, p := range pf.Params.Parameters { + params[p.Name] = "default" + } + + configs := make(map[string]interface{}) + configs["OperatorName"] = "OperatorName" + configs["Name"] = "Name" + configs["Namespace"] = "Namespace" + configs["Params"] = params + configs["Pipes"] = make(map[string]string) + configs["PlanName"] = "PlanName" + configs["PhaseName"] = "PhaseName" + configs["StepName"] = "StepName" + configs["AppVersion"] = "AppVersion" + + res := verifier.NewResult() + + engine := renderer.New() + for k, v := range pf.Templates { + _, err := engine.Render(k, v, configs) + if err != nil { + res.AddErrors(err.Error()) + } + } + + return res +} diff --git a/pkg/kudoctl/packages/verifier/template/verify_render_test.go b/pkg/kudoctl/packages/verifier/template/verify_render_test.go new file mode 100644 index 000000000..32af1a543 --- /dev/null +++ b/pkg/kudoctl/packages/verifier/template/verify_render_test.go @@ -0,0 +1,32 @@ +package template + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/kudobuilder/kudo/pkg/apis/kudo/v1beta1" + "github.com/kudobuilder/kudo/pkg/kudoctl/packages" +) + +func TestTemplateRenderVerifier(t *testing.T) { + params := make([]v1beta1.Parameter, 0) + paramFile := packages.ParamsFile{Parameters: params} + templates := make(map[string]string) + templates["foo.yaml"] = ` +{{ if eq }} +{{ end }} +` + operator := packages.OperatorFile{} + pf := packages.Files{ + Templates: templates, + Operator: &operator, + Params: ¶mFile, + } + verifier := RenderVerifier{} + res := verifier.Verify(&pf) + + assert.Equal(t, 0, len(res.Warnings)) + assert.Equal(t, 1, len(res.Errors)) + assert.Equal(t, `error rendering template: template: foo.yaml:2:6: executing "foo.yaml" at : wrong number of args for eq: want at least 1 got 0`, res.Errors[0]) +}