Skip to content

Commit

Permalink
fix comparison
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 Sep 29, 2023
1 parent f9a94b6 commit 39b4fcb
Show file tree
Hide file tree
Showing 8 changed files with 206 additions and 81 deletions.
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -301,3 +301,5 @@ require (
sigs.k8s.io/structured-merge-diff/v4 v4.3.0 // indirect
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
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -413,6 +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/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 Expand Up @@ -880,8 +882,6 @@ github.com/jingyugao/rowserrcheck v0.0.0-20210315055705-d907ca737bb1/go.mod h1:T
github.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8=
github.com/jinzhu/copier v0.4.0/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg=
github.com/jirfag/go-printf-func-name v0.0.0-20200119135958-7558a9eaa5af/go.mod h1:HEWGJkRDzjJY2sqdDwxccsGicWEf9BQOZsq2tV+xzM0=
github.com/jmespath-community/go-jmespath v1.1.1 h1:bFikPhsi/FdmlZhVgSCd2jj1e7G/rw+zyQfyg5UF+L4=
github.com/jmespath-community/go-jmespath v1.1.1/go.mod h1:4gOyFJsR/Gk+05RgTKYrifT7tBPWD8Lubtb5jRrfy9I=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
Expand Down
13 changes: 13 additions & 0 deletions pkg/commands/root_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,16 @@ func Test_FooBar(t *testing.T) {
err := cmd.Execute()
assert.NoError(t, err)
}

func Test_Scripted(t *testing.T) {
cmd := NewRootCommand()
assert.NotNil(t, cmd)
cmd.SetArgs([]string{
"--payload",
"../../testdata/scripted/payload.yaml",
"--policy",
"../../testdata/scripted/policy.yaml",
})
err := cmd.Execute()
assert.NoError(t, err)
}
213 changes: 149 additions & 64 deletions pkg/engine/match/match.go
Original file line number Diff line number Diff line change
@@ -1,122 +1,207 @@
package match

import (
"fmt"
"reflect"

"github.com/eddycharly/json-kyverno/pkg/apis/v1alpha1"
"github.com/kyverno/kyverno/pkg/utils/wildcard"
)

func MatchResources(match *v1alpha1.MatchResources, actual interface{}, options ...option) bool {
func MatchResources(match *v1alpha1.MatchResources, actual interface{}, options ...option) (bool, error) {
if match == nil || (len(match.Any) == 0 && len(match.All) == 0) {
return false
return false, nil
}
if len(match.Any) != 0 {
if !MatchAny(match.Any, actual, options...) {
return false
if match, err := MatchAny(match.Any, actual, options...); err != nil {
return false, err
} else if !match {
return false, nil
}
}
if len(match.All) != 0 {
if !MatchAll(match.All, actual, options...) {
return false
if match, err := MatchAll(match.All, actual, options...); err != nil {
return false, err
} else if !match {
return false, nil
}
}
return true
return true, nil
}

func MatchAny(filters v1alpha1.ResourceFilters, actual interface{}, options ...option) bool {
func MatchAny(filters v1alpha1.ResourceFilters, actual interface{}, options ...option) (bool, error) {
for _, filter := range filters {
if Match(filter.Resource, actual, options...) {
return true
if match, err := Match(filter.Resource, actual, options...); err != nil {
return false, err
} else if match {
return true, nil
}
}
return false
return false, nil
}

func MatchAll(filters v1alpha1.ResourceFilters, actual interface{}, options ...option) bool {
func MatchAll(filters v1alpha1.ResourceFilters, actual interface{}, options ...option) (bool, error) {
for _, filter := range filters {
if !Match(filter.Resource, actual, options...) {
return false
if match, err := Match(filter.Resource, actual, options...); err != nil {
return false, err
} else if !match {
return false, nil
}
}
return true
return true, nil
}

func Match(expected, actual interface{}, options ...option) bool {
func Match(expected, actual interface{}, options ...option) (bool, error) {
return match(expected, actual, newMatchOptions(options...))
}

func match(expected, actual interface{}, options matchOptions) bool {
func match(expected, actual interface{}, options matchOptions) (bool, error) {
if options.template != nil && reflect.TypeOf(expected).Kind() == reflect.String {
expected = options.template.Interface(reflect.ValueOf(expected).String())
}
// if reflect.TypeOf(expected) != reflect.TypeOf(actual) {
// return false
// }
switch reflect.TypeOf(expected).Kind() {
case reflect.String:
if options.wildcard {
if reflect.TypeOf(actual).Kind() != reflect.String {
return false
}
return wildcard.Match(reflect.ValueOf(expected).String(), reflect.ValueOf(actual).String())
}
case reflect.Slice:
if reflect.TypeOf(actual).Kind() != reflect.Slice {
return false
}
if reflect.ValueOf(expected).Len() != reflect.ValueOf(actual).Len() {
return false
result, err := options.template.Interface(reflect.ValueOf(expected).String(), actual)
if err != nil {
return false, err
}
for i := 0; i < reflect.ValueOf(expected).Len(); i++ {
if !match(reflect.ValueOf(expected).Index(i).Interface(), reflect.ValueOf(actual).Index(i).Interface(), options) {
return false
expected = result
}
if expected != nil {
switch reflect.TypeOf(expected).Kind() {
case reflect.String:
if options.wildcard {
if reflect.TypeOf(actual).Kind() != reflect.String {
return false, fmt.Errorf("invalid actual value, must be a string, found %s", reflect.TypeOf(actual).Kind())
}
return wildcard.Match(reflect.ValueOf(expected).String(), reflect.ValueOf(actual).String()), nil
}
}
return true
case reflect.Map:
if reflect.TypeOf(actual).Kind() != reflect.Map {
return false
}
iter := reflect.ValueOf(expected).MapRange()
for iter.Next() {
actualValue := reflect.ValueOf(actual).MapIndex(iter.Key())
if !actualValue.IsValid() {
return false
case reflect.Slice:
if reflect.TypeOf(actual).Kind() != reflect.Slice {
return false, fmt.Errorf("invalid actual value, must be a slice, found %s", reflect.TypeOf(actual).Kind())
}
if !match(iter.Value().Interface(), actualValue.Interface(), options) {
return false
if reflect.ValueOf(expected).Len() != reflect.ValueOf(actual).Len() {
return false, nil
}
for i := 0; i < reflect.ValueOf(expected).Len(); i++ {
if inner, err := match(reflect.ValueOf(expected).Index(i).Interface(), reflect.ValueOf(actual).Index(i).Interface(), options); err != nil {
return false, err
} else if !inner {
return false, nil
}
}
return true, nil
case reflect.Map:
keyType := reflect.TypeOf(expected).Key()
if options.template != nil && keyType.Kind() == reflect.String {
iter := reflect.ValueOf(expected).MapRange()
for iter.Next() {
key := iter.Key().String()
result, err := options.template.Interface(key, actual)
if err != nil {
return false, err
}
switch actualKey := result.(type) {
case string:
if actualKey == key {
if reflect.TypeOf(actual).Kind() != reflect.Map {
return false, fmt.Errorf("invalid actual value, must be a map, found %s", reflect.TypeOf(actual).Kind())
}
actualValue := reflect.ValueOf(actual).MapIndex(iter.Key())
if !actualValue.IsValid() {
return false, nil
}
if inner, err := match(iter.Value().Interface(), actualValue.Interface(), options); err != nil {
return false, err
} else if !inner {
return false, nil
}
} else {
if inner, err := match(iter.Value().Interface(), actualKey, options); err != nil {
return false, err
} else if !inner {
return false, nil
}
}
default:
if inner, err := match(iter.Value().Interface(), actualKey, options); err != nil {
return false, err
} else if !inner {
return false, nil
}
}
}
} else {
if reflect.TypeOf(actual).Kind() != reflect.Map {
return false, fmt.Errorf("invalid actual value, must be a map, found %s", reflect.TypeOf(actual).Kind())
}
iter := reflect.ValueOf(expected).MapRange()
for iter.Next() {
actualValue := reflect.ValueOf(actual).MapIndex(iter.Key())
if !actualValue.IsValid() {
return false, nil
}
if inner, err := match(iter.Value().Interface(), actualValue.Interface(), options); err != nil {
return false, err
} else if !inner {
return false, nil
}
}
}
return true, nil
}
return true
}
return matchScalar(expected, actual)
}

func matchScalar(expected, actual interface{}) bool {
func getKind(value interface{}) reflect.Kind {
if value == nil {
return reflect.Invalid
}
return reflect.TypeOf(value).Kind()
}

func toNumber(value reflect.Value) (float64, bool) {
if value.CanFloat() {
return value.Float(), true
}
if value.CanInt() {
return float64(value.Int()), true
}
if value.CanUint() {
return float64(value.Uint()), true
}
return 0, false
}

func matchScalar(expected, actual interface{}) (bool, error) {
if actual == nil && expected == nil {
return true, nil
}
if actual == expected {
return true, nil
}
// if they are the same type we can use reflect.DeepEqual
if reflect.TypeOf(expected) == reflect.TypeOf(actual) {
return reflect.DeepEqual(expected, actual)
return reflect.DeepEqual(expected, actual), nil
}
e := reflect.ValueOf(expected)
a := reflect.ValueOf(actual)
if !a.IsValid() && !e.IsValid() {
return true
}
if a.IsZero() && e.IsZero() {
return true
return true, nil
}
if a.CanComplex() && e.CanComplex() {
return a.Complex() == e.Complex()
return a.Complex() == e.Complex(), nil
}
if a.CanFloat() && e.CanFloat() {
return a.Float() == e.Float()
return a.Float() == e.Float(), nil
}
if a.CanInt() && e.CanInt() {
return a.Int() == e.Int()
return a.Int() == e.Int(), nil
}
if a.CanUint() && e.CanUint() {
return a.Uint() == e.Uint()
return a.Uint() == e.Uint(), nil
}
if a, ok := toNumber(a); ok {
if e, ok := toNumber(e); ok {
return a == e, nil
}
}
return false
return false, fmt.Errorf("types are not comparable, %s - %s", getKind(expected), getKind(actual))
}
22 changes: 11 additions & 11 deletions pkg/engine/template/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ var (
)

type Template interface {
String(string) string
Interface(string) interface{}
String(string, interface{}) string
Interface(string, interface{}) (interface{}, error)
}

type template struct {
Expand All @@ -40,11 +40,11 @@ func New(data interface{}, context ...v1alpha1.ContextEntry) Template {
}
}

func (t *template) String(in string) string {
func (t *template) String(in string, data interface{}) string {
groups := variable.FindAllStringSubmatch(in, -1)
for _, group := range groups {
statement := strings.TrimSpace(group[1])
result, err := t.jp(statement)
result, err := t.jp(statement, data)
if err != nil {
in = strings.ReplaceAll(in, group[0], fmt.Sprintf("ERR (%s - %s)", statement, err))
} else if result == nil {
Expand All @@ -58,18 +58,18 @@ func (t *template) String(in string) string {
return in
}

func (t *template) Interface(in string) interface{} {
func (t *template) Interface(in string, data interface{}) (interface{}, error) {
if inline.MatchString(in) {
in = strings.TrimPrefix(in, "{{")
in = strings.TrimSuffix(in, "}}")
statement := strings.TrimSpace(in)
result, err := t.jp(statement)
result, err := t.jp(statement, data)
if err != nil {
return nil
return nil, err
}
return result
return result, nil
} else {
return t.String(in)
return t.String(in, data), nil
}
}

Expand All @@ -82,10 +82,10 @@ func Execute(statement string, data interface{}) (interface{}, error) {
return interpreter.Execute(compiled, data)
}

func (t *template) jp(statement string) (interface{}, error) {
func (t *template) jp(statement string, data interface{}) (interface{}, error) {
compiled, err := parser.Parse(statement)
if err != nil {
return nil, err
}
return t.interpreter.Execute(compiled, t.data)
return t.interpreter.Execute(compiled, data)
}
Loading

0 comments on commit 39b4fcb

Please sign in to comment.