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:
parent
9df6b46b20
commit
604c0408c6
11 changed files with 447 additions and 2304 deletions
File diff suppressed because it is too large
Load diff
|
@ -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)
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue