1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2024-12-14 11:57:48 +00:00

Refactored operator tests to use test cases (#2620)

Identified several situations where operators didn't return the expected result (mainly around different types) so fixed those to make all tests cases pass.

Signed-off-by: Marcus Noble <github@marcusnoble.co.uk>
This commit is contained in:
Marcus Noble 2021-10-29 09:54:51 +01:00 committed by GitHub
parent 9df6b46b20
commit 604c0408c6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 447 additions and 2304 deletions

File diff suppressed because it is too large Load diff

View file

@ -31,7 +31,7 @@ func (allin AllInHandler) Evaluate(key, value interface{}) bool {
case []interface{}:
var stringSlice []string
for _, v := range typedKey {
stringSlice = append(stringSlice, v.(string))
stringSlice = append(stringSlice, fmt.Sprint(v))
}
return allin.validateValueWithStringSetPattern(stringSlice, value)
default:
@ -70,11 +70,7 @@ func allSetExistsInArray(key []string, value interface{}, log logr.Logger, allNo
case []interface{}:
var valueSlice []string
for _, val := range valuesAvailable {
v, ok := val.(string)
if !ok {
return true, false
}
valueSlice = append(valueSlice, v)
valueSlice = append(valueSlice, fmt.Sprint(val))
}
if allNotIn {
return false, isAllNotIn(key, valueSlice)

View file

@ -26,10 +26,12 @@ func (allnin AllNotInHandler) Evaluate(key, value interface{}) bool {
switch typedKey := key.(type) {
case string:
return allnin.validateValueWithStringPattern(typedKey, value)
case int, int32, int64, float32, float64:
return allnin.validateValueWithStringPattern(fmt.Sprint(typedKey), value)
case []interface{}:
var stringSlice []string
for _, v := range typedKey {
stringSlice = append(stringSlice, v.(string))
stringSlice = append(stringSlice, fmt.Sprint(v))
}
return allnin.validateValueWithStringSetPattern(stringSlice, value)
default:

View file

@ -28,10 +28,12 @@ func (anyin AnyInHandler) Evaluate(key, value interface{}) bool {
switch typedKey := key.(type) {
case string:
return anyin.validateValueWithStringPattern(typedKey, value)
case int, int32, int64, float32, float64:
return anyin.validateValueWithStringPattern(fmt.Sprint(typedKey), value)
case []interface{}:
var stringSlice []string
for _, v := range typedKey {
stringSlice = append(stringSlice, v.(string))
stringSlice = append(stringSlice, fmt.Sprint(v))
}
return anyin.validateValueWithStringSetPattern(stringSlice, value)
default:
@ -70,11 +72,7 @@ func anySetExistsInArray(key []string, value interface{}, log logr.Logger, anyNo
case []interface{}:
var valueSlice []string
for _, val := range valuesAvailable {
v, ok := val.(string)
if !ok {
return true, false
}
valueSlice = append(valueSlice, v)
valueSlice = append(valueSlice, fmt.Sprint(val))
}
if anyNotIn {
return false, isAnyNotIn(key, valueSlice)

View file

@ -26,10 +26,12 @@ func (anynin AnyNotInHandler) Evaluate(key, value interface{}) bool {
switch typedKey := key.(type) {
case string:
return anynin.validateValueWithStringPattern(typedKey, value)
case int, int32, int64, float32, float64:
return anynin.validateValueWithStringPattern(fmt.Sprint(typedKey), value)
case []interface{}:
var stringSlice []string
for _, v := range typedKey {
stringSlice = append(stringSlice, v.(string))
stringSlice = append(stringSlice, fmt.Sprint(v))
}
return anynin.validateValueWithStringSetPattern(stringSlice, value)
default:

View file

@ -7,6 +7,7 @@ import (
"strconv"
"github.com/minio/pkg/wildcard"
"k8s.io/apimachinery/pkg/api/resource"
"github.com/go-logr/logr"
"github.com/kyverno/kyverno/pkg/engine/context"
@ -67,6 +68,26 @@ func (eh EqualHandler) validateValueWithMapPattern(key map[string]interface{}, v
}
func (eh EqualHandler) validateValueWithStringPattern(key string, value interface{}) bool {
// We need to check duration first as it's the only type that can be compared to a different type.
durationKey, durationValue, err := parseDuration(key, value)
if err == nil {
return durationKey.Seconds() == durationValue.Seconds()
}
// Attempt to extract resource quantity from string.
resourceKey, err := resource.ParseQuantity(key)
if err == nil {
switch typedValue := value.(type) {
case string:
resourceValue, err := resource.ParseQuantity(typedValue)
if err != nil {
eh.log.Error(fmt.Errorf("parse error: "), "Failed to parse value type doesn't match key type")
return false
}
return resourceKey.Equal(resourceValue)
}
}
if val, ok := value.(string); ok {
return wildcard.Match(val, key)
}

View file

@ -30,6 +30,8 @@ func (in InHandler) Evaluate(key, value interface{}) bool {
switch typedKey := key.(type) {
case string:
return in.validateValueWithStringPattern(typedKey, value)
case int, int32, int64, float32, float64:
return in.validateValueWithStringPattern(fmt.Sprint(typedKey), value)
case []interface{}:
var stringSlice []string
for _, v := range typedKey {
@ -60,18 +62,12 @@ func keyExistsInArray(key string, value interface{}, log logr.Logger) (invalidTy
case []interface{}:
for _, val := range valuesAvailable {
v, ok := val.(string)
if !ok {
return true, false
}
if ok && wildcard.Match(key, v) {
if wildcard.Match(key, fmt.Sprint(val)) {
return false, true
}
}
case string:
if wildcard.Match(valuesAvailable, key) {
return false, true
}

View file

@ -7,6 +7,7 @@ import (
"strconv"
"github.com/minio/pkg/wildcard"
"k8s.io/apimachinery/pkg/api/resource"
"github.com/go-logr/logr"
"github.com/kyverno/kyverno/pkg/engine/context"
@ -55,7 +56,7 @@ func (neh NotEqualHandler) validateValueWithSlicePattern(key []interface{}, valu
return !reflect.DeepEqual(key, val)
}
neh.log.Info("Expected type []interface{}", "value", value, "type", fmt.Sprintf("%T", value))
return false
return true
}
func (neh NotEqualHandler) validateValueWithMapPattern(key map[string]interface{}, value interface{}) bool {
@ -63,16 +64,36 @@ func (neh NotEqualHandler) validateValueWithMapPattern(key map[string]interface{
return !reflect.DeepEqual(key, val)
}
neh.log.Info("Expected type map[string]interface{}", "value", value, "type", fmt.Sprintf("%T", value))
return false
return true
}
func (neh NotEqualHandler) validateValueWithStringPattern(key string, value interface{}) bool {
// We need to check duration first as it's the only type that can be compared to a different type.
durationKey, durationValue, err := parseDuration(key, value)
if err == nil {
return durationKey.Seconds() != durationValue.Seconds()
}
// Attempt to extract resource quantity from string.
resourceKey, err := resource.ParseQuantity(key)
if err == nil {
switch typedValue := value.(type) {
case string:
resourceValue, err := resource.ParseQuantity(typedValue)
if err != nil {
neh.log.Error(fmt.Errorf("parse error: "), "Failed to parse value type doesn't match key type")
return false
}
return !resourceKey.Equal(resourceValue)
}
}
if val, ok := value.(string); ok {
return !wildcard.Match(val, key)
}
neh.log.Info("Expected type string", "value", value, "type", fmt.Sprintf("%T", value))
return false
return true
}
func (neh NotEqualHandler) validateValueWithFloatPattern(key float64, value interface{}) bool {
@ -96,21 +117,21 @@ func (neh NotEqualHandler) validateValueWithFloatPattern(key float64, value inte
float64Num, err := strconv.ParseFloat(typedValue, 64)
if err != nil {
neh.log.Error(err, "Failed to parse float64 from string")
return false
return true
}
return float64Num != key
default:
neh.log.Info("Expected type float", "value", value, "type", fmt.Sprintf("%T", value))
return false
return true
}
return false
return true
}
func (neh NotEqualHandler) validateValueWithBoolPattern(key bool, value interface{}) bool {
typedValue, ok := value.(bool)
if !ok {
neh.log.Info("Expected type bool", "value", value, "type", fmt.Sprintf("%T", value))
return false
return true
}
return key != typedValue
}
@ -133,11 +154,11 @@ func (neh NotEqualHandler) validateValueWithIntPattern(key int64, value interfac
int64Num, err := strconv.ParseInt(typedValue, 10, 64)
if err != nil {
neh.log.Error(err, "Failed to parse int64 from string")
return false
return true
}
return int64Num != key
default:
neh.log.Info("Expected type int", "value", value, "type", fmt.Sprintf("%T", value))
return false
return true
}
}

View file

@ -27,6 +27,8 @@ func (nin NotInHandler) Evaluate(key, value interface{}) bool {
switch typedKey := key.(type) {
case string:
return nin.validateValueWithStringPattern(typedKey, value)
case int, int32, int64, float32, float64:
return nin.validateValueWithStringPattern(fmt.Sprint(typedKey), value)
case []interface{}:
var stringSlice []string
for _, v := range typedKey {

View file

@ -3,7 +3,6 @@ package operator
import (
"fmt"
"strconv"
"time"
"github.com/go-logr/logr"
kyverno "github.com/kyverno/kyverno/pkg/api/kyverno/v1"
@ -38,8 +37,16 @@ func compareByCondition(key float64, value float64, op kyverno.ConditionOperator
return key <= value
case kyverno.LessThan:
return key < value
case kyverno.Equals:
return key == value
case kyverno.Equal:
return key == value
case kyverno.NotEquals:
return key != value
case kyverno.NotEqual:
return key != value
default:
(*log).Info(fmt.Sprintf("Expected operator, one of [GreaterThanOrEquals, GreaterThan, LessThanOrEquals, LessThan], found %s", op))
(*log).Info(fmt.Sprintf("Expected operator, one of [GreaterThanOrEquals, GreaterThan, LessThanOrEquals, LessThan, Equals, NotEquals], found %s", op))
return false
}
}
@ -69,6 +76,10 @@ func (noh NumericOperatorHandler) validateValueWithIntPattern(key int64, value i
case float64:
return compareByCondition(float64(key), typedValue, noh.condition, &noh.log)
case string:
durationKey, durationValue, err := parseDuration(key, value)
if err == nil {
return compareByCondition(float64(durationKey.Seconds()), float64(durationValue.Seconds()), noh.condition, &noh.log)
}
// extract float64 and (if that fails) then, int64 from the string
float64val, err := strconv.ParseFloat(typedValue, 64)
if err == nil {
@ -95,6 +106,10 @@ func (noh NumericOperatorHandler) validateValueWithFloatPattern(key float64, val
case float64:
return compareByCondition(key, typedValue, noh.condition, &noh.log)
case string:
durationKey, durationValue, err := parseDuration(key, value)
if err == nil {
return compareByCondition(float64(durationKey.Seconds()), float64(durationValue.Seconds()), noh.condition, &noh.log)
}
float64val, err := strconv.ParseFloat(typedValue, 64)
if err == nil {
return compareByCondition(key, float64val, noh.condition, &noh.log)
@ -128,7 +143,7 @@ func (noh NumericOperatorHandler) validateValueWithResourcePattern(key resource.
func (noh NumericOperatorHandler) validateValueWithStringPattern(key string, value interface{}) bool {
// We need to check duration first as it's the only type that can be compared to a different type
durationKey, durationValue, err := noh.parseDuration(key, value)
durationKey, durationValue, err := parseDuration(key, value)
if err == nil {
return compareByCondition(float64(durationKey.Seconds()), float64(durationValue.Seconds()), noh.condition, &noh.log)
}
@ -152,67 +167,6 @@ func (noh NumericOperatorHandler) validateValueWithStringPattern(key string, val
return false
}
func (noh NumericOperatorHandler) parseDuration(key, value interface{}) (*time.Duration, *time.Duration, error) {
var keyDuration *time.Duration
var valueDuration *time.Duration
var err error
// We need to first ensure at least one of the values is actually a duration string
switch typedKey := key.(type) {
case string:
duration, err := time.ParseDuration(typedKey)
if err == nil && key != "0" {
keyDuration = &duration
}
}
switch typedValue := value.(type) {
case string:
duration, err := time.ParseDuration(typedValue)
if err == nil && value != "0" {
valueDuration = &duration
}
}
if keyDuration == nil && valueDuration == nil {
return keyDuration, valueDuration, fmt.Errorf("neither value is a duration")
}
if keyDuration == nil {
var duration time.Duration
switch typedKey := key.(type) {
case int:
duration = time.Duration(typedKey) * time.Second
case int64:
duration = time.Duration(typedKey) * time.Second
case float64:
duration = time.Duration(typedKey) * time.Second
default:
return keyDuration, valueDuration, fmt.Errorf("no valid duration value")
}
keyDuration = &duration
}
if valueDuration == nil {
var duration time.Duration
switch typedValue := value.(type) {
case int:
duration = time.Duration(typedValue) * time.Second
case int64:
duration = time.Duration(typedValue) * time.Second
case float64:
duration = time.Duration(typedValue) * time.Second
default:
return keyDuration, valueDuration, fmt.Errorf("no valid duration value")
}
valueDuration = &duration
}
return keyDuration, valueDuration, err
}
// the following functions are unreachable because the key is strictly supposed to be numeric
// still the following functions are just created to make NumericOperatorHandler struct implement OperatorHandler interface
func (noh NumericOperatorHandler) validateValueWithBoolPattern(key bool, value interface{}) bool {

View file

@ -1,7 +1,9 @@
package operator
import (
"fmt"
"strings"
"time"
"github.com/go-logr/logr"
kyverno "github.com/kyverno/kyverno/pkg/api/kyverno/v1"
@ -74,3 +76,64 @@ func CreateOperatorHandler(log logr.Logger, ctx context.EvalInterface, op kyvern
return nil
}
func parseDuration(key, value interface{}) (*time.Duration, *time.Duration, error) {
var keyDuration *time.Duration
var valueDuration *time.Duration
var err error
// We need to first ensure at least one of the values is actually a duration string.
switch typedKey := key.(type) {
case string:
duration, err := time.ParseDuration(typedKey)
if err == nil && key != "0" {
keyDuration = &duration
}
}
switch typedValue := value.(type) {
case string:
duration, err := time.ParseDuration(typedValue)
if err == nil && value != "0" {
valueDuration = &duration
}
}
if keyDuration == nil && valueDuration == nil {
return keyDuration, valueDuration, fmt.Errorf("neither value is a duration")
}
if keyDuration == nil {
var duration time.Duration
switch typedKey := key.(type) {
case int:
duration = time.Duration(typedKey) * time.Second
case int64:
duration = time.Duration(typedKey) * time.Second
case float64:
duration = time.Duration(typedKey) * time.Second
default:
return keyDuration, valueDuration, fmt.Errorf("no valid duration value")
}
keyDuration = &duration
}
if valueDuration == nil {
var duration time.Duration
switch typedValue := value.(type) {
case int:
duration = time.Duration(typedValue) * time.Second
case int64:
duration = time.Duration(typedValue) * time.Second
case float64:
duration = time.Duration(typedValue) * time.Second
default:
return keyDuration, valueDuration, fmt.Errorf("no valid duration value")
}
valueDuration = &duration
}
return keyDuration, valueDuration, err
}