Skip to content

Commit

Permalink
lazy binding
Browse files Browse the repository at this point in the history
Signed-off-by: Charles-Edouard Brétéché <[email protected]>
  • Loading branch information
eddycharly committed Oct 2, 2023
1 parent ad54eab commit 7b1ae4d
Show file tree
Hide file tree
Showing 9 changed files with 57 additions and 44 deletions.
10 changes: 0 additions & 10 deletions config/crds/json.kyverno.io_policies.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -52,16 +52,6 @@ spec:
description: Variable defines an arbitrary JMESPath context
variable that can be defined inline.
properties:
default:
description: Default is an optional arbitrary JSON
object that the variable may take if the JMESPath
expression evaluates to nil
type: object
x-kubernetes-preserve-unknown-fields: true
jmesPath:
description: JMESPath is an optional JMESPath Expression
that can be used to transform the variable.
type: string
value:
description: Value is any arbitrary JSON object representable
in YAML or JSON form.
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -302,4 +302,4 @@ require (
sigs.k8s.io/yaml v1.3.0 // indirect
)

replace github.com/jmespath-community/go-jmespath => github.com/eddycharly/go-community-jmespath v1.1.1-0.20230929121033-cb50a1973e28
replace github.com/jmespath-community/go-jmespath => github.com/eddycharly/go-community-jmespath v1.1.1-0.20231002150022-0274ad918d4c
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -413,8 +413,8 @@ github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
github.com/ebitengine/purego v0.6.0-alpha h1:7skbuQ4040gPjIt6Dmvreeyjuuy+xIK1CW5cssLg0+o=
github.com/ebitengine/purego v0.6.0-alpha/go.mod h1:ah1In8AOtksoNK6yk5z1HTJeUkC1Ez4Wk2idgGslMwQ=
github.com/eddycharly/go-community-jmespath v1.1.1-0.20230929121033-cb50a1973e28 h1:i3+LhieWZ+/ZUKlO7VNWuAsdzu2dIepsqT8Qg7z0X/w=
github.com/eddycharly/go-community-jmespath v1.1.1-0.20230929121033-cb50a1973e28/go.mod h1:4gOyFJsR/Gk+05RgTKYrifT7tBPWD8Lubtb5jRrfy9I=
github.com/eddycharly/go-community-jmespath v1.1.1-0.20231002150022-0274ad918d4c h1:nWV1tE2FXHvhtoJLBoq9E8hswfGr1ufj64PH2jal8+I=
github.com/eddycharly/go-community-jmespath v1.1.1-0.20231002150022-0274ad918d4c/go.mod h1:4gOyFJsR/Gk+05RgTKYrifT7tBPWD8Lubtb5jRrfy9I=
github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
Expand Down
9 changes: 0 additions & 9 deletions pkg/apis/v1alpha1/policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,15 +63,6 @@ type Variable struct {
// +kubebuilder:pruning:PreserveUnknownFields
// +kubebuilder:validation:Schemaless
Value interface{} `json:"value,omitempty"`

// JMESPath is an optional JMESPath Expression that can be used to transform the variable.
JMESPath string `json:"jmesPath,omitempty"`

// Default is an optional arbitrary JSON object that the variable may take if the JMESPath expression evaluates to nil
// +kubebuilder:validation:Type=object
// +kubebuilder:pruning:PreserveUnknownFields
// +kubebuilder:validation:Schemaless
Default interface{} `json:"default,omitempty"`
}

type MatchResources struct {
Expand Down
10 changes: 0 additions & 10 deletions pkg/data/crds/json.kyverno.io_policies.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -52,16 +52,6 @@ spec:
description: Variable defines an arbitrary JMESPath context
variable that can be defined inline.
properties:
default:
description: Default is an optional arbitrary JSON
object that the variable may take if the JMESPath
expression evaluates to nil
type: object
x-kubernetes-preserve-unknown-fields: true
jmesPath:
description: JMESPath is an optional JMESPath Expression
that can be used to transform the variable.
type: string
value:
description: Value is any arbitrary JSON object representable
in YAML or JSON form.
Expand Down
10 changes: 5 additions & 5 deletions pkg/engine/assert/map.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,30 +5,30 @@ import (
"reflect"

reflectutils "github.com/eddycharly/json-kyverno/pkg/utils/reflect"
"github.com/jmespath-community/go-jmespath/pkg/binding"
jpbinding "github.com/jmespath-community/go-jmespath/pkg/binding"
"k8s.io/apimachinery/pkg/util/validation/field"
)

// mapNode is the assertion type represented by a map.
// it is reponsible for projecting the analysed resource and passing the result to the descendant
type mapNode map[interface{}]Assertion

func (n mapNode) assert(path *field.Path, value interface{}, bindings binding.Bindings) (field.ErrorList, error) {
func (n mapNode) assert(path *field.Path, value interface{}, bindings jpbinding.Bindings) (field.ErrorList, error) {
var errs field.ErrorList
for k, v := range n {
projected, foreach, binding, err := project(k, value, bindings)
if err != nil {
return nil, field.InternalError(path.Child(fmt.Sprint(k)), err)
} else {
if binding != "" {
bindings = bindings.Register("$"+binding, projected)
bindings = bindings.Register("$"+binding, jpbinding.NewBinding(projected))
}
if foreach != "" {
projectedKind := reflectutils.GetKind(projected)
if projectedKind == reflect.Slice {
valueOf := reflect.ValueOf(projected)
for i := 0; i < valueOf.Len(); i++ {
bindings := bindings.Register("$"+foreach, i)
bindings := bindings.Register("$"+foreach, jpbinding.NewBinding(i))
if _errs, err := v.assert(path.Child(fmt.Sprint(k)).Index(i), valueOf.Index(i).Interface(), bindings); err != nil {
return nil, err
} else {
Expand All @@ -39,7 +39,7 @@ func (n mapNode) assert(path *field.Path, value interface{}, bindings binding.Bi
iter := reflect.ValueOf(projected).MapRange()
for iter.Next() {
key := iter.Key().Interface()
bindings := bindings.Register("$"+foreach, key)
bindings := bindings.Register("$"+foreach, jpbinding.NewBinding(key))
if _errs, err := v.assert(path.Child(fmt.Sprint(k)).Key(fmt.Sprint(key)), iter.Value().Interface(), bindings); err != nil {
return nil, err
} else {
Expand Down
38 changes: 38 additions & 0 deletions pkg/engine/template/binding.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package template

import (
"sync"

"github.com/jmespath-community/go-jmespath/pkg/binding"
)

type resolverFunc = func() (interface{}, error)

type lazyBinding struct {
resolver resolverFunc
}

func (b *lazyBinding) Value() (interface{}, error) {
return b.resolver()
}

func NewLazyBinding(resolver resolverFunc) binding.Binding {
binding := &lazyBinding{}
lock := &sync.Mutex{}
binding.resolver = func() (interface{}, error) {
lock.Lock()
defer lock.Unlock()
value, err := resolver()
binding.resolver = func() (interface{}, error) {
return value, err
}
return binding.resolver()
}
return binding
}

func NewLazyBindingWithValue(value interface{}) binding.Binding {
return NewLazyBinding(func() (interface{}, error) {
return value, nil
})
}
12 changes: 6 additions & 6 deletions pkg/json-engine/engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
"github.com/eddycharly/json-kyverno/pkg/engine/builder"
"github.com/eddycharly/json-kyverno/pkg/engine/match"
"github.com/eddycharly/json-kyverno/pkg/engine/template"
"github.com/jmespath-community/go-jmespath/pkg/binding"
jpbinding "github.com/jmespath-community/go-jmespath/pkg/binding"
)

type JsonEngineRequest struct {
Expand Down Expand Up @@ -54,12 +54,12 @@ func New() engine.Engine[JsonEngineRequest, JsonEngineResponse] {
Rule: r.Rule,
Resource: r.Resource,
}
bindings := binding.NewBindings()
bindings = bindings.Register("$resource", r.Resource)
bindings = bindings.Register("$rule", r.Rule)
bindings = bindings.Register("$policy", r.Policy)
bindings := jpbinding.NewBindings()
bindings = bindings.Register("$resource", jpbinding.NewBinding(r.Resource))
bindings = bindings.Register("$rule", jpbinding.NewBinding(r.Rule))
bindings = bindings.Register("$policy", jpbinding.NewBinding(r.Policy))
for _, entry := range r.Rule.Context {
bindings = bindings.Register("$"+entry.Name, entry.Variable.Value)
bindings = bindings.Register("$"+entry.Name, template.NewLazyBindingWithValue(entry.Variable.Value))
}
errs, err := assert.Assert(r.Rule.Validation.Pattern, r.Resource, bindings)
if err != nil {
Expand Down
6 changes: 5 additions & 1 deletion testdata/pod-no-latest/policy.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ metadata:
spec:
rules:
- name: pod-no-latest
context:
- name: tag
variable:
value: :latest
match:
any:
- resource:
Expand All @@ -18,7 +22,7 @@ spec:
# an image tag is required
(contains($foo, ':')): true
# using a mutable image tag e.g. 'latest' is not allowed
(ends_with($foo, ':latest')): false
(ends_with($foo, $tag)): false
~.containers@foo:
image:
# an image tag is required
Expand Down

0 comments on commit 7b1ae4d

Please sign in to comment.