From e803423cbeab1383e6ed5cb27eb35c10334702a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Charles-Edouard=20Br=C3=A9t=C3=A9ch=C3=A9?= Date: Tue, 17 Sep 2024 23:13:09 +0200 Subject: [PATCH] ast MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Charles-Edouard Brétéché --- pkg/engine/assert/expression.go | 7 +++++++ pkg/engine/assert/parse.go | 29 ++++++++++------------------- pkg/engine/assert/project.go | 6 +++++- 3 files changed, 22 insertions(+), 20 deletions(-) diff --git a/pkg/engine/assert/expression.go b/pkg/engine/assert/expression.go index 898fb5b5..fb517256 100644 --- a/pkg/engine/assert/expression.go +++ b/pkg/engine/assert/expression.go @@ -4,7 +4,9 @@ import ( "context" "reflect" "regexp" + "sync" + "github.com/jmespath-community/go-jmespath/pkg/parsing" reflectutils "github.com/kyverno/kyverno-json/pkg/utils/reflect" ) @@ -21,6 +23,7 @@ type expression struct { statement string binding string engine string + ast func() (parsing.ASTNode, error) } func parseExpressionRegex(_ context.Context, in string) *expression { @@ -54,6 +57,10 @@ func parseExpressionRegex(_ context.Context, in string) *expression { if expression.statement == "" { return nil } + expression.ast = sync.OnceValues(func() (parsing.ASTNode, error) { + parser := parsing.NewParser() + return parser.Parse(expression.statement) + }) return expression } diff --git a/pkg/engine/assert/parse.go b/pkg/engine/assert/parse.go index 736896f0..a34aae51 100644 --- a/pkg/engine/assert/parse.go +++ b/pkg/engine/assert/parse.go @@ -7,7 +7,6 @@ import ( "github.com/jmespath-community/go-jmespath/pkg/binding" jpbinding "github.com/jmespath-community/go-jmespath/pkg/binding" - "github.com/jmespath-community/go-jmespath/pkg/parsing" "github.com/kyverno/kyverno-json/pkg/engine/match" "github.com/kyverno/kyverno-json/pkg/engine/template" reflectutils "github.com/kyverno/kyverno-json/pkg/utils/reflect" @@ -142,11 +141,9 @@ func (n sliceNode) assert(ctx context.Context, path *field.Path, value any, bind // scalarNode is a terminal type of assertion. // it receives a value and compares it with an expected value. // the expected value can be the result of an expression. -type scalarNode struct { - project func(value any, bindings binding.Bindings, opts ...template.Option) (any, error) -} +type scalarNode func(value any, bindings binding.Bindings, opts ...template.Option) (any, error) -func newScalarNode(ctx context.Context, path *field.Path, rhs any) (Assertion, error) { +func newScalarNode(ctx context.Context, path *field.Path, rhs any) (scalarNode, error) { expression := parseExpression(ctx, rhs) // we only project if the expression uses the engine syntax // this is to avoid the case where the value is a map and the RHS is a string @@ -157,28 +154,22 @@ func newScalarNode(ctx context.Context, path *field.Path, rhs any) (Assertion, e if expression.binding != "" { return nil, field.Invalid(path, rhs, "binding is not supported on the RHS") } - parser := parsing.NewParser() - compiled, err := parser.Parse(expression.statement) + ast, err := expression.ast() if err != nil { return nil, field.InternalError(path, err) } - return &scalarNode{ - project: func(value any, bindings binding.Bindings, opts ...template.Option) (any, error) { - return template.ExecuteAST(ctx, compiled, value, bindings, opts...) - }, - }, nil - } else { - return &scalarNode{ - project: func(value any, bindings binding.Bindings, opts ...template.Option) (any, error) { - return rhs, nil - }, + return func(value any, bindings binding.Bindings, opts ...template.Option) (any, error) { + return template.ExecuteAST(ctx, ast, value, bindings, opts...) }, nil } + return func(value any, bindings binding.Bindings, opts ...template.Option) (any, error) { + return rhs, nil + }, nil } -func (n *scalarNode) assert(ctx context.Context, path *field.Path, value any, bindings binding.Bindings, opts ...template.Option) (field.ErrorList, error) { +func (n scalarNode) assert(ctx context.Context, path *field.Path, value any, bindings binding.Bindings, opts ...template.Option) (field.ErrorList, error) { var errs field.ErrorList - if rhs, err := n.project(value, bindings, opts...); err != nil { + if rhs, err := n(value, bindings, opts...); err != nil { return nil, field.InternalError(path, err) } else if match, err := match.Match(ctx, rhs, value); err != nil { return nil, field.InternalError(path, err) diff --git a/pkg/engine/assert/project.go b/pkg/engine/assert/project.go index 1a36f81d..cd057e22 100644 --- a/pkg/engine/assert/project.go +++ b/pkg/engine/assert/project.go @@ -21,7 +21,11 @@ func project(ctx context.Context, key any, value any, bindings binding.Bindings, expression := parseExpression(ctx, key) if expression != nil { if expression.engine != "" { - projected, err := template.Execute(ctx, expression.statement, value, bindings, opts...) + ast, err := expression.ast() + if err != nil { + return nil, err + } + projected, err := template.ExecuteAST(ctx, ast, value, bindings, opts...) if err != nil { return nil, err }