mirror of
https://github.com/kyverno/kyverno.git
synced 2025-03-05 07:26:55 +00:00
Extend new operators (#2788)
* extending new operators Signed-off-by: anushkamittal20 <anumittal4641@gmail.com> * Changes in file names Signed-off-by: anushkamittal20 <anumittal4641@gmail.com> * tests added Signed-off-by: anushkamittal20 <anumittal4641@gmail.com> * removed print statements Signed-off-by: anushkamittal20 <anumittal4641@gmail.com> * Changes to reduce code redundancy Signed-off-by: anushkamittal20 <anumittal4641@gmail.com> * Minor corrections in anyin and allin Signed-off-by: anushkamittal20 <anumittal4641@gmail.com> * added correction for anynotin and allnotin Signed-off-by: anushkamittal20 <anumittal4641@gmail.com>
This commit is contained in:
parent
d126280184
commit
3089edafa4
11 changed files with 186 additions and 14 deletions
|
@ -1,4 +1,4 @@
|
|||
package validate
|
||||
package common
|
||||
|
||||
import (
|
||||
"fmt"
|
|
@ -1,4 +1,4 @@
|
|||
package validate
|
||||
package common
|
||||
|
||||
import (
|
||||
"fmt"
|
|
@ -1,4 +1,4 @@
|
|||
package validate
|
||||
package common
|
||||
|
||||
import (
|
||||
"encoding/json"
|
|
@ -7,7 +7,7 @@ import (
|
|||
|
||||
"github.com/go-logr/logr"
|
||||
commonAnchors "github.com/kyverno/kyverno/pkg/engine/anchor/common"
|
||||
"github.com/kyverno/kyverno/pkg/engine/validate"
|
||||
"github.com/kyverno/kyverno/pkg/engine/common"
|
||||
"sigs.k8s.io/controller-runtime/pkg/log"
|
||||
)
|
||||
|
||||
|
@ -144,7 +144,7 @@ func compareOverlay(resource, overlay interface{}, path string) (string, overlay
|
|||
}
|
||||
}
|
||||
case string, float64, int, int64, bool, nil:
|
||||
if !validate.ValidateValueWithPattern(log.Log, resource, overlay) {
|
||||
if !common.ValidateValueWithPattern(log.Log, resource, overlay) {
|
||||
log.Log.V(4).Info(fmt.Sprintf("Mutate rule: failed validating value %v with overlay %v", resource, overlay))
|
||||
return path, newOverlayError(conditionFailure, fmt.Sprintf("Failed validating value %v with overlay %v", resource, overlay))
|
||||
}
|
||||
|
|
|
@ -79,13 +79,13 @@ func validateResourceElement(log logr.Logger, resourceElement, patternElement, o
|
|||
switch resource := resourceElement.(type) {
|
||||
case []interface{}:
|
||||
for _, res := range resource {
|
||||
if !ValidateValueWithPattern(log, res, patternElement) {
|
||||
if !common.ValidateValueWithPattern(log, res, patternElement) {
|
||||
return path, fmt.Errorf("resource value '%v' does not match '%v' at path %s", resourceElement, patternElement, path)
|
||||
}
|
||||
}
|
||||
return "", nil
|
||||
default:
|
||||
if !ValidateValueWithPattern(log, resourceElement, patternElement) {
|
||||
if !common.ValidateValueWithPattern(log, resourceElement, patternElement) {
|
||||
return path, fmt.Errorf("resource value '%v' does not match '%v' at path %s", resourceElement, patternElement, path)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -291,6 +291,12 @@ func TestEvaluate(t *testing.T) {
|
|||
{kyverno.Condition{Key: []interface{}{"5*"}, Operator: kyverno.ConditionOperators["AnyIn"], Value: []interface{}{"1.1.1.1", "2.2.2.2", "3.3.3.3"}}, false},
|
||||
{kyverno.Condition{Key: []interface{}{"1.1.1.1", "2.2.2.2", "5.5.5.5"}, Operator: kyverno.ConditionOperators["AnyIn"], Value: []interface{}{"2*"}}, true},
|
||||
{kyverno.Condition{Key: []interface{}{"1.1.1.1", "2.2.2.2", "5.5.5.5"}, Operator: kyverno.ConditionOperators["AnyIn"], Value: []interface{}{"4*"}}, false},
|
||||
{kyverno.Condition{Key: []interface{}{"5"}, Operator: kyverno.ConditionOperators["AnyIn"], Value: "1-3"}, false},
|
||||
{kyverno.Condition{Key: "5", Operator: kyverno.ConditionOperators["AnyIn"], Value: "1-10"}, true},
|
||||
{kyverno.Condition{Key: 5, Operator: kyverno.ConditionOperators["AnyIn"], Value: "1-10"}, true},
|
||||
{kyverno.Condition{Key: []interface{}{1, 5}, Operator: kyverno.ConditionOperators["AnyIn"], Value: "7-10"}, false},
|
||||
{kyverno.Condition{Key: []interface{}{1, 5}, Operator: kyverno.ConditionOperators["AnyIn"], Value: "0-10"}, true},
|
||||
{kyverno.Condition{Key: []interface{}{1.002, 1.222}, Operator: kyverno.ConditionOperators["AnyIn"], Value: "1.001-10"}, true},
|
||||
|
||||
// All In
|
||||
{kyverno.Condition{Key: []interface{}{"1.1.1.1", "2.2.2.2"}, Operator: kyverno.ConditionOperators["AllIn"], Value: []interface{}{"1.1.1.1", "2.2.2.2", "3.3.3.3"}}, true},
|
||||
|
@ -305,6 +311,9 @@ func TestEvaluate(t *testing.T) {
|
|||
{kyverno.Condition{Key: []interface{}{"2.1.1.1", "2.2.2.2", "2.5.5.5"}, Operator: kyverno.ConditionOperators["AllIn"], Value: []interface{}{"2*"}}, true},
|
||||
{kyverno.Condition{Key: []interface{}{"1.1.1.1", "2.2.2.2", "5.5.5.5"}, Operator: kyverno.ConditionOperators["AllIn"], Value: []interface{}{"4*"}}, false},
|
||||
{kyverno.Condition{Key: []interface{}{"1.1.1.1", "5.5.5.5"}, Operator: kyverno.ConditionOperators["AllIn"], Value: "5.5.5.5"}, false},
|
||||
{kyverno.Condition{Key: 5, Operator: kyverno.ConditionOperators["AllIn"], Value: "1-10"}, true},
|
||||
{kyverno.Condition{Key: []interface{}{3, 2}, Operator: kyverno.ConditionOperators["AllIn"], Value: "1-10"}, true},
|
||||
{kyverno.Condition{Key: []interface{}{3, 2}, Operator: kyverno.ConditionOperators["AllIn"], Value: "5-10"}, false},
|
||||
|
||||
// All Not In
|
||||
{kyverno.Condition{Key: 1, Operator: kyverno.ConditionOperators["AllNotIn"], Value: []interface{}{1, 2, 3}}, false},
|
||||
|
@ -323,6 +332,10 @@ func TestEvaluate(t *testing.T) {
|
|||
{kyverno.Condition{Key: []interface{}{"1.1.1.1", "3.3.3.3", "5.5.5.5"}, Operator: kyverno.ConditionOperators["AllNotIn"], Value: []interface{}{"2*"}}, true},
|
||||
{kyverno.Condition{Key: []interface{}{"4.1.1.1", "4.2.2.2", "4.5.5.5"}, Operator: kyverno.ConditionOperators["AllNotIn"], Value: []interface{}{"4*"}}, false},
|
||||
{kyverno.Condition{Key: []interface{}{"1.1.1.1", "4.4.4.4"}, Operator: kyverno.ConditionOperators["AllNotIn"], Value: "2.2.2.2"}, true},
|
||||
{kyverno.Condition{Key: 5.5, Operator: kyverno.ConditionOperators["AllNotIn"], Value: "6-10"}, true},
|
||||
{kyverno.Condition{Key: "5", Operator: kyverno.ConditionOperators["AllNotIn"], Value: "1-6"}, false},
|
||||
{kyverno.Condition{Key: []interface{}{3, 2}, Operator: kyverno.ConditionOperators["AllNotIn"], Value: "5-10"}, true},
|
||||
{kyverno.Condition{Key: []interface{}{2, 6}, Operator: kyverno.ConditionOperators["AllNotIn"], Value: "5-10"}, false},
|
||||
|
||||
// Any Not In
|
||||
{kyverno.Condition{Key: 1, Operator: kyverno.ConditionOperators["AnyNotIn"], Value: []interface{}{1, 2, 3}}, false},
|
||||
|
@ -338,6 +351,9 @@ func TestEvaluate(t *testing.T) {
|
|||
{kyverno.Condition{Key: []interface{}{"1*", "3*", "5*"}, Operator: kyverno.ConditionOperators["AnyNotIn"], Value: []interface{}{"1.1.1.1", "2.2.2.2", "3.3.3.3"}}, true},
|
||||
{kyverno.Condition{Key: []interface{}{"1.1.1.1", "2.2.2.2", "5.5.5.5"}, Operator: kyverno.ConditionOperators["AnyNotIn"], Value: []interface{}{"2*"}}, true},
|
||||
{kyverno.Condition{Key: []interface{}{"2.2*"}, Operator: kyverno.ConditionOperators["AnyNotIn"], Value: []interface{}{"2.2.2.2"}}, false},
|
||||
{kyverno.Condition{Key: "5", Operator: kyverno.ConditionOperators["AnyNotIn"], Value: "1-3"}, true},
|
||||
{kyverno.Condition{Key: []interface{}{1, 5, 11}, Operator: kyverno.ConditionOperators["AnyNotIn"], Value: "0-10"}, true},
|
||||
{kyverno.Condition{Key: []interface{}{1, 5, 7}, Operator: kyverno.ConditionOperators["AnyNotIn"], Value: "0-10"}, false},
|
||||
}
|
||||
|
||||
ctx := context.NewContext()
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
|
||||
"github.com/go-logr/logr"
|
||||
"github.com/kyverno/kyverno/pkg/engine/context"
|
||||
"github.com/kyverno/kyverno/pkg/engine/operator"
|
||||
"github.com/minio/pkg/wildcard"
|
||||
)
|
||||
|
||||
|
@ -28,6 +29,8 @@ func (allin AllInHandler) Evaluate(key, value interface{}) bool {
|
|||
switch typedKey := key.(type) {
|
||||
case string:
|
||||
return allin.validateValueWithStringPattern(typedKey, value)
|
||||
case int, int32, int64, float32, float64:
|
||||
return allin.validateValueWithStringPattern(fmt.Sprint(typedKey), value)
|
||||
case []interface{}:
|
||||
var stringSlice []string
|
||||
for _, v := range typedKey {
|
||||
|
@ -41,7 +44,7 @@ func (allin AllInHandler) Evaluate(key, value interface{}) bool {
|
|||
}
|
||||
|
||||
func (allin AllInHandler) validateValueWithStringPattern(key string, value interface{}) (keyExists bool) {
|
||||
invalidType, keyExists := keyExistsInArray(key, value, allin.log)
|
||||
invalidType, keyExists := allKeyExistsInArray(key, value, allin.log)
|
||||
if invalidType {
|
||||
allin.log.Info("expected type []string", "value", value, "type", fmt.Sprintf("%T", value))
|
||||
return false
|
||||
|
@ -50,6 +53,49 @@ func (allin AllInHandler) validateValueWithStringPattern(key string, value inter
|
|||
return keyExists
|
||||
}
|
||||
|
||||
func allKeyExistsInArray(key string, value interface{}, log logr.Logger) (invalidType bool, keyExists bool) {
|
||||
switch valuesAvailable := value.(type) {
|
||||
|
||||
case []interface{}:
|
||||
for _, val := range valuesAvailable {
|
||||
if wildcard.Match(key, fmt.Sprint(val)) {
|
||||
return false, true
|
||||
}
|
||||
}
|
||||
|
||||
case string:
|
||||
if wildcard.Match(valuesAvailable, key) {
|
||||
return false, true
|
||||
}
|
||||
|
||||
operatorVariable := operator.GetOperatorFromStringPattern(fmt.Sprintf("%v", value))
|
||||
if operatorVariable == operator.InRange {
|
||||
return false, handleRange(key, value, log)
|
||||
}
|
||||
|
||||
var arr []string
|
||||
if json.Valid([]byte(valuesAvailable)) {
|
||||
if err := json.Unmarshal([]byte(valuesAvailable), &arr); err != nil {
|
||||
log.Error(err, "failed to unmarshal value to JSON string array", "key", key, "value", value)
|
||||
return true, false
|
||||
}
|
||||
} else {
|
||||
arr = append(arr, valuesAvailable)
|
||||
}
|
||||
|
||||
for _, val := range arr {
|
||||
if key == val {
|
||||
return false, true
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
return true, false
|
||||
}
|
||||
|
||||
return false, false
|
||||
}
|
||||
|
||||
func (allin AllInHandler) validateValueWithStringSetPattern(key []string, value interface{}) (keyExists bool) {
|
||||
invalidType, isAllIn := allSetExistsInArray(key, value, allin.log, false)
|
||||
if invalidType {
|
||||
|
@ -80,9 +126,36 @@ func allSetExistsInArray(key []string, value interface{}, log logr.Logger, allNo
|
|||
case string:
|
||||
|
||||
if len(key) == 1 && key[0] == valuesAvailable {
|
||||
if allNotIn {
|
||||
return false, false
|
||||
}
|
||||
return false, true
|
||||
}
|
||||
|
||||
operatorVariable := operator.GetOperatorFromStringPattern(fmt.Sprintf("%v", value))
|
||||
if operatorVariable == operator.InRange {
|
||||
if allNotIn {
|
||||
isAllNotInBool := true
|
||||
for _, k := range key {
|
||||
if handleRange(k, valuesAvailable, log) {
|
||||
isAllNotInBool = false
|
||||
}
|
||||
}
|
||||
return false, isAllNotInBool
|
||||
} else {
|
||||
isAllInCount := 0
|
||||
for _, k := range key {
|
||||
if handleRange(k, value, log) {
|
||||
isAllInCount++
|
||||
}
|
||||
}
|
||||
if isAllInCount == len(key) {
|
||||
return false, true
|
||||
}
|
||||
return false, false
|
||||
}
|
||||
}
|
||||
|
||||
var arr []string
|
||||
if json.Valid([]byte(valuesAvailable)) {
|
||||
if err := json.Unmarshal([]byte(valuesAvailable), &arr); err != nil {
|
||||
|
|
|
@ -41,7 +41,7 @@ func (allnin AllNotInHandler) Evaluate(key, value interface{}) bool {
|
|||
}
|
||||
|
||||
func (allnin AllNotInHandler) validateValueWithStringPattern(key string, value interface{}) bool {
|
||||
invalidType, keyExists := keyExistsInArray(key, value, allnin.log)
|
||||
invalidType, keyExists := allKeyExistsInArray(key, value, allnin.log)
|
||||
if invalidType {
|
||||
allnin.log.Info("expected type []string", "value", value, "type", fmt.Sprintf("%T", value))
|
||||
return false
|
||||
|
|
|
@ -3,9 +3,12 @@ package operator
|
|||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
"github.com/kyverno/kyverno/pkg/engine/common"
|
||||
"github.com/kyverno/kyverno/pkg/engine/context"
|
||||
"github.com/kyverno/kyverno/pkg/engine/operator"
|
||||
"github.com/minio/pkg/wildcard"
|
||||
)
|
||||
|
||||
|
@ -43,7 +46,7 @@ func (anyin AnyInHandler) Evaluate(key, value interface{}) bool {
|
|||
}
|
||||
|
||||
func (anyin AnyInHandler) validateValueWithStringPattern(key string, value interface{}) (keyExists bool) {
|
||||
invalidType, keyExists := keyExistsInArray(key, value, anyin.log)
|
||||
invalidType, keyExists := anyKeyExistsInArray(key, value, anyin.log)
|
||||
if invalidType {
|
||||
anyin.log.Info("expected type []string", "value", value, "type", fmt.Sprintf("%T", value))
|
||||
return false
|
||||
|
@ -52,6 +55,60 @@ func (anyin AnyInHandler) validateValueWithStringPattern(key string, value inter
|
|||
return keyExists
|
||||
}
|
||||
|
||||
// anykeyExistsInArray checks if the key exists in the array value
|
||||
// The value can be a string, an array of strings, or a JSON format
|
||||
// array of strings (e.g. ["val1", "val2", "val3"].
|
||||
func anyKeyExistsInArray(key string, value interface{}, log logr.Logger) (invalidType bool, keyExists bool) {
|
||||
switch valuesAvailable := value.(type) {
|
||||
|
||||
case []interface{}:
|
||||
for _, val := range valuesAvailable {
|
||||
if wildcard.Match(key, fmt.Sprint(val)) {
|
||||
return false, true
|
||||
}
|
||||
}
|
||||
|
||||
case string:
|
||||
if wildcard.Match(valuesAvailable, key) {
|
||||
return false, true
|
||||
}
|
||||
|
||||
operatorVariable := operator.GetOperatorFromStringPattern(fmt.Sprintf("%v", value))
|
||||
if operatorVariable == operator.InRange {
|
||||
return false, handleRange(key, value, log)
|
||||
}
|
||||
|
||||
var arr []string
|
||||
if json.Valid([]byte(valuesAvailable)) {
|
||||
if err := json.Unmarshal([]byte(valuesAvailable), &arr); err != nil {
|
||||
log.Error(err, "failed to unmarshal value to JSON string array", "key", key, "value", value)
|
||||
return true, false
|
||||
}
|
||||
} else {
|
||||
arr = append(arr, valuesAvailable)
|
||||
}
|
||||
|
||||
for _, val := range arr {
|
||||
if key == val {
|
||||
return false, true
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
return true, false
|
||||
}
|
||||
|
||||
return false, false
|
||||
}
|
||||
|
||||
func handleRange(key string, value interface{}, log logr.Logger) bool {
|
||||
if !common.ValidateValueWithPattern(log, key, value) {
|
||||
return false
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
func (anyin AnyInHandler) validateValueWithStringSetPattern(key []string, value interface{}) (keyExists bool) {
|
||||
invalidType, isAnyIn := anySetExistsInArray(key, value, anyin.log, false)
|
||||
if invalidType {
|
||||
|
@ -80,11 +137,37 @@ func anySetExistsInArray(key []string, value interface{}, log logr.Logger, anyNo
|
|||
return false, isAnyIn(key, valueSlice)
|
||||
|
||||
case string:
|
||||
|
||||
if len(key) == 1 && key[0] == valuesAvailable {
|
||||
if anyNotIn {
|
||||
return false, false
|
||||
}
|
||||
return false, true
|
||||
}
|
||||
|
||||
operatorVariable := operator.GetOperatorFromStringPattern(fmt.Sprintf("%v", value))
|
||||
if operatorVariable == operator.InRange {
|
||||
if anyNotIn {
|
||||
isAnyNotInBool := false
|
||||
stringForAnyNotIn := strings.Replace(valuesAvailable, "-", "!-", 1)
|
||||
for _, k := range key {
|
||||
if handleRange(k, stringForAnyNotIn, log) {
|
||||
isAnyNotInBool = true
|
||||
break
|
||||
}
|
||||
}
|
||||
return false, isAnyNotInBool
|
||||
} else {
|
||||
isAnyInBool := false
|
||||
for _, k := range key {
|
||||
if handleRange(k, value, log) {
|
||||
isAnyInBool = true
|
||||
break
|
||||
}
|
||||
}
|
||||
return false, isAnyInBool
|
||||
}
|
||||
}
|
||||
|
||||
var arr []string
|
||||
if json.Valid([]byte(valuesAvailable)) {
|
||||
if err := json.Unmarshal([]byte(valuesAvailable), &arr); err != nil {
|
||||
|
|
|
@ -41,7 +41,7 @@ func (anynin AnyNotInHandler) Evaluate(key, value interface{}) bool {
|
|||
}
|
||||
|
||||
func (anynin AnyNotInHandler) validateValueWithStringPattern(key string, value interface{}) bool {
|
||||
invalidType, keyExists := keyExistsInArray(key, value, anynin.log)
|
||||
invalidType, keyExists := anyKeyExistsInArray(key, value, anynin.log)
|
||||
if invalidType {
|
||||
anynin.log.Info("expected type []string", "value", value, "type", fmt.Sprintf("%T", value))
|
||||
return false
|
||||
|
|
|
@ -6,7 +6,7 @@ import (
|
|||
"strconv"
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
"github.com/kyverno/kyverno/pkg/engine/validate"
|
||||
"github.com/kyverno/kyverno/pkg/engine/common"
|
||||
"github.com/kyverno/kyverno/pkg/engine/wildcards"
|
||||
"sigs.k8s.io/controller-runtime/pkg/log"
|
||||
)
|
||||
|
@ -54,7 +54,7 @@ func validateResourceElement(log logr.Logger, resourceElement, patternElement, o
|
|||
return validateArray(log, typedResourceElement, typedPatternElement, originPattern, path)
|
||||
// elementary values
|
||||
case string, float64, int, int64, bool, nil:
|
||||
if !validate.ValidateValueWithPattern(log, resourceElement, patternElement) {
|
||||
if !common.ValidateValueWithPattern(log, resourceElement, patternElement) {
|
||||
return path, fmt.Errorf("value '%v' does not match '%v' at path %s", resourceElement, patternElement, path)
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue