1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2024-12-15 17:51:20 +00:00

I have finished implementing patterns

This commit is contained in:
Denis Belyshev 2019-05-27 18:07:24 +03:00
parent ac736bbf84
commit 435a19152a
5 changed files with 262 additions and 324 deletions

View file

@ -231,7 +231,7 @@ func skipArrayObject(object, anchors map[string]interface{}) bool {
return true
}
if value != pattern {
if !ValidateValueWithPattern(value, pattern) {
return true
}
}

View file

@ -1,9 +1,32 @@
package engine
import (
"fmt"
"log"
"math"
"regexp"
"strconv"
"strings"
"github.com/minio/minio/pkg/wildcard"
)
// Operator is string alias that represents selection operators enum
type Operator string
const (
// Equal stands for ==
Equal Operator = ""
// MoreEqual stands for >=
MoreEqual Operator = ">="
// LessEqual stands for <=
LessEqual Operator = "<="
// NotEqual stands for !
NotEqual Operator = "!"
// More stands for >
More Operator = ">"
// Less stands for <
Less Operator = "<"
)
// ValidateValueWithPattern validates value with operators and wildcards
@ -17,30 +40,34 @@ func ValidateValueWithPattern(value, pattern interface{}) bool {
}
return typedPattern == typedValue
case int:
return validateValueWithIntPattern(value, int64(typedPattern))
case int64:
return validateValueWithIntPattern(value, typedPattern)
case float64:
return validateValueWithFloatPattern(value, typedPattern)
case string:
return validateValueWithStringPattern(value, typedPattern)
return validateValueWithStringPatterns(value, typedPattern)
case nil:
return validateValueWithNilPattern(value)
case map[string]interface{}, []interface{}:
log.Println("Maps and arrays as patterns are not supported")
return false
case nil:
return validateValueWithNilPattern(value)
default:
log.Printf("Unknown type as pattern: %T\n", pattern)
return false
}
}
func validateValueWithIntPattern(value interface{}, pattern int) bool {
func validateValueWithIntPattern(value interface{}, pattern int64) bool {
switch typedValue := value.(type) {
case int:
return int64(typedValue) == pattern
case int64:
return typedValue == pattern
case float64:
// check that float has no fraction
if typedValue == math.Trunc(typedValue) {
return int(typedValue) == pattern
return int64(typedValue) == pattern
}
log.Printf("Expected int, found float: %f\n", typedValue)
@ -75,6 +102,8 @@ func validateValueWithNilPattern(value interface{}) bool {
return typed == 0.0
case int:
return typed == 0
case int64:
return typed == 0
case string:
return typed == ""
case bool:
@ -90,10 +119,11 @@ func validateValueWithNilPattern(value interface{}) bool {
}
}
func validateValueWithStringPattern(value interface{}, pattern string) bool {
func validateValueWithStringPatterns(value interface{}, pattern string) bool {
statements := strings.Split(pattern, "|")
for statement := range statements {
if checkSingleStatement(value, statement) {
for _, statement := range statements {
statement = strings.Trim(statement, " ")
if validateValueWithStringPattern(value, statement) {
return true
}
}
@ -101,6 +131,164 @@ func validateValueWithStringPattern(value interface{}, pattern string) bool {
return false
}
func checkSingleStatement(value, pattern interface{}) bool {
func validateValueWithStringPattern(value interface{}, pattern string) bool {
operator := getOperatorFromStringPattern(pattern)
pattern = pattern[len(operator):]
number, str := getNumberAndStringPartsFromPattern(pattern)
if "" == number {
return validateString(value, str, operator)
}
validateNumberWithStr(value, number, str, operator)
return true
}
func validateString(value interface{}, pattern string, operator Operator) bool {
if NotEqual == operator || Equal == operator {
strValue, ok := value.(string)
if !ok {
log.Printf("Expected string, found %T\n", value)
return false
}
wildcardResult := wildcard.Match(pattern, strValue)
if NotEqual == operator {
return !wildcardResult
}
return wildcardResult
}
log.Println("Operators >, >=, <, <= are not applicable to strings")
return false
}
func validateNumberWithStr(value interface{}, patternNumber, patternStr string, operator Operator) bool {
patternParsedNumber, err := parseNumber(patternNumber)
if err != nil {
return false
}
if "" != patternStr {
typedValue, ok := value.(string)
if !ok {
log.Printf("Number must have suffix: %s", patternStr)
return false
}
valueNumber, valueStr := getNumberAndStringPartsFromPattern(typedValue)
if !wildcard.Match(patternStr, valueStr) {
log.Printf("Suffix %s has not passed wildcard check: %s", valueStr, patternStr)
return false
}
valueParsedNumber, err := parseNumber(valueNumber)
if err != nil {
return false
}
return validateNumber(valueParsedNumber, patternParsedNumber, operator)
}
return validateNumber(value, patternParsedNumber, operator)
}
func validateNumber(value, pattern interface{}, operator Operator) bool {
var floatPattern, floatValue float64
switch typed := value.(type) {
case float64:
floatValue = typed
case int64:
floatValue = float64(typed)
case int:
floatValue = float64(typed)
default:
return false
}
switch typed := pattern.(type) {
case float64:
floatPattern = typed
case int64:
floatPattern = float64(typed)
case int:
floatPattern = float64(typed)
default:
return false
}
switch operator {
case Equal:
return floatValue == floatPattern
case NotEqual:
return floatValue != floatPattern
case More:
return floatValue > floatPattern
case MoreEqual:
return floatValue >= floatPattern
case Less:
return floatValue < floatPattern
case LessEqual:
return floatValue <= floatPattern
}
return false
}
func getOperatorFromStringPattern(pattern string) Operator {
if pattern[:len(MoreEqual)] == string(MoreEqual) {
return MoreEqual
}
if pattern[:len(LessEqual)] == string(LessEqual) {
return LessEqual
}
if pattern[:len(More)] == string(More) {
return More
}
if pattern[:len(Less)] == string(Less) {
return Less
}
if pattern[:len(NotEqual)] == string(NotEqual) {
return NotEqual
}
return ""
}
func getNumberAndStringPartsFromPattern(pattern string) (number, str string) {
regexpStr := `^(\d*(\.\d+)?)(.*)`
re := regexp.MustCompile(regexpStr)
matches := re.FindAllStringSubmatch(pattern, -1)
match := matches[0]
return match[1], match[3]
}
func checkForWildcard(value, pattern string) error {
if !wildcard.Match(pattern, value) {
return fmt.Errorf("wildcard check has failed. Pattern: \"%s\". Value: \"%s\"", pattern, value)
}
return nil
}
func parseNumber(number string) (interface{}, error) {
var err error
if floatValue, err := strconv.ParseFloat(number, 64); err == nil {
return floatValue, nil
}
if intValue, err := strconv.ParseInt(number, 10, 64); err == nil {
return intValue, nil
}
return nil, err
}

View file

@ -211,3 +211,61 @@ func TestValidateValueWithIntPattern_FloatValueWitFraction(t *testing.T) {
func TestValidateValueWithIntPattern_NotPass(t *testing.T) {
assert.Assert(t, !validateValueWithFloatPattern(8, 7))
}
func TestGetNumberAndStringPartsFromPattern_NumberAndString(t *testing.T) {
number, str := getNumberAndStringPartsFromPattern("1024Gi")
assert.Equal(t, number, "1024")
assert.Equal(t, str, "Gi")
}
func TestGetNumberAndStringPartsFromPattern_OnlyNumber(t *testing.T) {
number, str := getNumberAndStringPartsFromPattern("1024")
assert.Equal(t, number, "1024")
assert.Equal(t, str, "")
}
func TestGetNumberAndStringPartsFromPattern_OnlyString(t *testing.T) {
number, str := getNumberAndStringPartsFromPattern("Gi")
assert.Equal(t, number, "")
assert.Equal(t, str, "Gi")
}
func TestGetNumberAndStringPartsFromPattern_StringFirst(t *testing.T) {
number, str := getNumberAndStringPartsFromPattern("Gi1024")
assert.Equal(t, number, "")
assert.Equal(t, str, "Gi1024")
}
func TestGetNumberAndStringPartsFromPattern_Empty(t *testing.T) {
number, str := getNumberAndStringPartsFromPattern("")
assert.Equal(t, number, "")
assert.Equal(t, str, "")
}
func TestValidateNumber_EqualTwoFloats(t *testing.T) {
assert.Assert(t, validateNumber(7.0, 7.000, Equal))
}
func TestValidateNumber_LessFloatAndInt(t *testing.T) {
assert.Assert(t, validateNumber(7, 7.00001, Less))
assert.Assert(t, validateNumber(7, 7.00001, NotEqual))
assert.Assert(t, !validateNumber(7, 7.0000, NotEqual))
assert.Assert(t, !validateNumber(6, 6.000000001, More))
}
func TestValidateNumberWithStr_Equal(t *testing.T) {
assert.Assert(t, validateNumberWithStr("1024Gi", "1024", "Gi", Equal))
}
func TestValidateNumberWithStr_More(t *testing.T) {
assert.Assert(t, !validateNumberWithStr("512Gi", "1024", "Gi", More))
}
func TestValidateNumberWithStr_MoreAndWildCard(t *testing.T) {
assert.Assert(t, validateNumberWithStr("2048Gi", "1024", "G?", More))
}
func TestValidateNumberWithStr_NoStr(t *testing.T) {
assert.Assert(t, validateNumberWithStr(2048, "1024", "", More))
}

View file

@ -4,26 +4,11 @@ import (
"encoding/json"
"fmt"
"log"
"strconv"
"strings"
"github.com/minio/minio/pkg/wildcard"
kubepolicy "github.com/nirmata/kyverno/pkg/apis/policy/v1alpha1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// Operator is string alias that represents selection operators enum
type Operator string
const (
MoreEqual Operator = ">="
LessEqual Operator = "<="
NotEqual Operator = "!="
More Operator = ">"
Less Operator = "<"
)
// TODO: Refactor using State pattern
// TODO: Return Events and pass all checks to get all validation errors (not )
@ -114,8 +99,8 @@ func validateArray(resourcePart, patternPart interface{}) error {
}
default:
for _, value := range resourceArray {
if err := checkSingleValue(value, patternArray[0]); err != nil {
return err
if !ValidateValueWithPattern(value, patternArray[0]) {
return fmt.Errorf("Failed validate %v wit %v", value, patternArray[0])
}
}
}
@ -139,33 +124,9 @@ func validateMapElement(resourcePart, patternPart interface{}) error {
}
return validateArray(array, pattern)
case string:
return checkSingleValue(resourcePart, patternPart)
case float64:
switch num := resourcePart.(type) {
case float64:
if num != pattern {
return fmt.Errorf("%f not equal %f", num, pattern)
}
case int64:
if float64(num) != pattern {
return fmt.Errorf("%d not equal %f", num, pattern)
}
default:
return fmt.Errorf("expected %T, found %T", patternPart, resourcePart)
}
case int64:
switch num := resourcePart.(type) {
case float64:
if num != float64(pattern) {
return fmt.Errorf("%f not equal %d", num, pattern)
}
case int64:
if float64(num) != float64(num) {
return fmt.Errorf("%d not equal %d", num, pattern)
}
default:
return fmt.Errorf("expected %T, found %T", patternPart, resourcePart)
case string, float64, int, int64:
if !ValidateValueWithPattern(resourcePart, patternPart) {
return fmt.Errorf("Failed validate %v wit %v", resourcePart, patternPart)
}
default:
return fmt.Errorf("validating error: unknown type in map: %T", patternPart)
@ -183,7 +144,7 @@ func skipValidatingObject(object, anchors map[string]interface{}) bool {
return true
}
if err := checkSingleValue(value, pattern); err != nil {
if !ValidateValueWithPattern(value, pattern) {
return true
}
}
@ -191,111 +152,6 @@ func skipValidatingObject(object, anchors map[string]interface{}) bool {
return false
}
func checkSingleValue(value, pattern interface{}) error {
switch typedPattern := pattern.(type) {
case string:
switch typedValue := value.(type) {
case string:
return checkForWildcard(typedValue, typedPattern)
case float64:
return checkForOperator(typedValue, typedPattern)
case int:
return checkForOperator(float64(typedValue), typedPattern)
default:
return fmt.Errorf("expected string or numerical type, found %T, pattern: %s", value, typedPattern)
}
case float64:
num, ok := value.(float64)
if !ok {
return fmt.Errorf("expected float, found %T", value)
}
if typedPattern != num {
return fmt.Errorf("value %f is not equal to pattern %f", value, typedPattern)
}
case int:
num, ok := value.(int)
if !ok {
return fmt.Errorf("expected int, found %T", value)
}
if typedPattern != num {
return fmt.Errorf("value %d is not equal to pattern %d", num, typedPattern)
}
default:
return fmt.Errorf("expected pattern (string or numerical type), found %T", pattern)
}
return nil
}
func checkForWildcard(value, pattern string) error {
if !wildcard.Match(pattern, value) {
return fmt.Errorf("wildcard check has failed. Pattern: \"%s\". Value: \"%s\"", pattern, value)
}
return nil
}
func checkForOperator(value float64, pattern string) error {
operators := strings.Split(pattern, "|")
for _, operator := range operators {
operator = strings.Replace(operator, " ", "", -1)
// At least one success - return nil
if checkSingleOperator(value, operator) {
return nil
}
}
return fmt.Errorf("operator check has failed. Pattern: \"%s\". Value: \"%f\"", pattern, value)
}
func checkSingleOperator(value float64, pattern string) bool {
if operatorVal, err := strconv.ParseFloat(pattern, 64); err == nil {
return value == operatorVal
}
if len(pattern) < 2 {
fmt.Printf("Validating error: operator can't have less than 2 characters: %s\n", pattern)
return false
}
if operatorVal, ok := parseOperator(MoreEqual, pattern); ok {
return value >= operatorVal
}
if operatorVal, ok := parseOperator(LessEqual, pattern); ok {
return value <= operatorVal
}
if operatorVal, ok := parseOperator(More, pattern); ok {
return value > operatorVal
}
if operatorVal, ok := parseOperator(Less, pattern); ok {
return value < operatorVal
}
if operatorVal, ok := parseOperator(NotEqual, pattern); ok {
return value != operatorVal
}
fmt.Printf("Validating error: unknown operator: %s\n", pattern)
return false
}
func parseOperator(operator Operator, pattern string) (float64, bool) {
if pattern[:len(operator)] == string(operator) {
if value, err := strconv.ParseFloat(pattern[len(operator):len(pattern)], 64); err == nil {
return value, true
}
}
return 0.0, false
}
func wrappedWithParentheses(str string) bool {
if len(str) < 2 {
return false

View file

@ -86,170 +86,6 @@ func TestCheckForWildcard_QuestionMark(t *testing.T) {
assert.Assert(t, checkForWildcard(value, pattern) != nil)
}
func TestCheckSingleValue_CheckInt(t *testing.T) {
pattern := 89
value := 89
assert.NilError(t, checkSingleValue(value, pattern))
value = 202
assert.Assert(t, checkSingleValue(value, pattern) != nil)
}
func TestCheckSingleValue_CheckFloat(t *testing.T) {
pattern := 89.9091
value := 89.9091
assert.NilError(t, checkSingleValue(value, pattern))
value = 89.9092
assert.Assert(t, checkSingleValue(value, pattern) != nil)
}
func TestCheckSingleValue_CheckOperatorMoreEqual(t *testing.T) {
pattern := " >= 89 "
value := 89
assert.NilError(t, checkSingleValue(value, pattern))
pattern = ">=10.0001"
floatValue := 89.901
assert.NilError(t, checkSingleValue(floatValue, pattern))
}
func TestCheckSingleValue_CheckOperatorMoreEqualFail(t *testing.T) {
pattern := " >= 90 "
value := 89
assert.Assert(t, checkSingleValue(value, pattern) != nil)
pattern = ">=910.0001"
floatValue := 89.901
assert.Assert(t, checkSingleValue(floatValue, pattern) != nil)
}
func TestCheckSingleValue_CheckOperatorLessEqual(t *testing.T) {
pattern := " <= 1 "
value := 1
assert.NilError(t, checkSingleValue(value, pattern))
pattern = "<=10.0001"
floatValue := 1.901
assert.NilError(t, checkSingleValue(floatValue, pattern))
}
func TestCheckSingleValue_CheckOperatorLessEqualFail(t *testing.T) {
pattern := " <= 0.1558 "
value := 1
assert.Assert(t, checkSingleValue(value, pattern) != nil)
pattern = "<=10.0001"
floatValue := 12.901
assert.Assert(t, checkSingleValue(floatValue, pattern) != nil)
}
func TestCheckSingleValue_CheckOperatorMore(t *testing.T) {
pattern := " > 10 "
value := 89
assert.NilError(t, checkSingleValue(value, pattern))
pattern = ">10.0001"
floatValue := 89.901
assert.NilError(t, checkSingleValue(floatValue, pattern))
}
func TestCheckSingleValue_CheckOperatorMoreFail(t *testing.T) {
pattern := " > 89 "
value := 89
assert.Assert(t, checkSingleValue(value, pattern) != nil)
pattern = ">910.0001"
floatValue := 89.901
assert.Assert(t, checkSingleValue(floatValue, pattern) != nil)
}
func TestCheckSingleValue_CheckOperatorLess(t *testing.T) {
pattern := " < 10 "
value := 9
assert.NilError(t, checkSingleValue(value, pattern))
pattern = "<10.0001"
floatValue := 9.901
assert.NilError(t, checkSingleValue(floatValue, pattern))
}
func TestCheckSingleValue_CheckOperatorLessFail(t *testing.T) {
pattern := " < 10 "
value := 10
assert.Assert(t, checkSingleValue(value, pattern) != nil)
pattern = "<10.0001"
floatValue := 19.901
assert.Assert(t, checkSingleValue(floatValue, pattern) != nil)
}
func TestCheckSingleValue_CheckOperatorNotEqual(t *testing.T) {
pattern := " != 10 "
value := 9.99999
assert.NilError(t, checkSingleValue(value, pattern))
pattern = "!=10.0001"
floatValue := 10.0000
assert.NilError(t, checkSingleValue(floatValue, pattern))
}
func TestCheckSingleValue_CheckOperatorNotEqualFail(t *testing.T) {
pattern := " != 9.99999 "
value := 9.99999
assert.Assert(t, checkSingleValue(value, pattern) != nil)
pattern = "!=10"
floatValue := 10
assert.Assert(t, checkSingleValue(floatValue, pattern) != nil)
}
func TestCheckSingleValue_CheckOperatorEqual(t *testing.T) {
pattern := " 10.000001 "
value := 10.000001
assert.NilError(t, checkSingleValue(value, pattern))
pattern = "10.000000"
floatValue := 10
assert.NilError(t, checkSingleValue(floatValue, pattern))
}
func TestCheckSingleValue_CheckOperatorEqualFail(t *testing.T) {
pattern := " 10.000000 "
value := 10.000001
assert.Assert(t, checkSingleValue(value, pattern) != nil)
pattern = "10.000001"
floatValue := 10
assert.Assert(t, checkSingleValue(floatValue, pattern) != nil)
}
func TestCheckSingleValue_CheckSeveralOperators(t *testing.T) {
pattern := " <-1 | 10.000001 "
value := 10.000001
assert.NilError(t, checkSingleValue(value, pattern))
value = -30
assert.NilError(t, checkSingleValue(value, pattern))
value = 5
assert.Assert(t, checkSingleValue(value, pattern) != nil)
}
func TestCheckSingleValue_CheckWildcard(t *testing.T) {
pattern := "nirmata_*"
value := "nirmata_awesome"
assert.NilError(t, checkSingleValue(value, pattern))
pattern = "nirmata_*"
value = "spasex_awesome"
assert.Assert(t, checkSingleValue(value, pattern) != nil)
pattern = "g?t"
value = "git"
assert.NilError(t, checkSingleValue(value, pattern))
}
func TestSkipArrayObject_OneAnchor(t *testing.T) {
rawAnchors := []byte(`{"(name)": "nirmata-*"}`)