1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2025-03-31 03:45:17 +00:00

Merge pull request #558 from nirmata/428_quantity

implement quantity comparison
This commit is contained in:
shuting 2019-12-16 15:53:09 -08:00 committed by GitHub
commit 4149d706e8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 79 additions and 72 deletions

View file

@ -7,8 +7,8 @@ import (
"strings"
"github.com/golang/glog"
"github.com/minio/minio/pkg/wildcard"
apiresource "k8s.io/apimachinery/pkg/api/resource"
)
// Operator is string alias that represents selection operators enum
@ -32,6 +32,14 @@ const (
const relativePrefix Operator = "./"
const referenceSign Operator = "$()"
type quantity int
const (
equal quantity = 0
lessThan quantity = -1
greaterThan quantity = 1
)
// ValidateValueWithPattern validates value with operators and wildcards
func ValidateValueWithPattern(value, pattern interface{}) bool {
switch typedPattern := pattern.(type) {
@ -187,7 +195,7 @@ func validateValueWithStringPattern(value interface{}, pattern string) bool {
return validateString(value, str, operator)
}
return validateNumberWithStr(value, number, str, operator)
return validateNumberWithStr(value, pattern, operator)
}
// Handler for string values
@ -212,53 +220,50 @@ func validateString(value interface{}, pattern string, operator Operator) bool {
return false
}
// validateNumberWithStr applies wildcard to suffix and operator to numerical part
func validateNumberWithStr(value interface{}, patternNumber, patternStr string, operator Operator) bool {
// pattern has suffix
if "" != patternStr {
typedValue, ok := value.(string)
if !ok {
glog.Warningf("Number must have suffix: %s", patternStr)
return false
}
valueNumber, valueStr := getNumberAndStringPartsFromPattern(typedValue)
if !wildcard.Match(patternStr, valueStr) {
glog.Warningf("Suffix %s has not passed wildcard check: %s", valueStr, patternStr)
return false
}
return validateNumber(valueNumber, patternNumber, operator)
// validateNumberWithStr compares quantity if pattern type is quantity
// or a wildcard match to pattern string
func validateNumberWithStr(value interface{}, pattern string, operator Operator) bool {
typedValue, err := convertToString(value)
if err != nil {
glog.Warning(err)
return false
}
return validateNumber(value, patternNumber, operator)
patternQuan, err := apiresource.ParseQuantity(pattern)
// 1. nil error - quantity comparison
if err == nil {
valueQuan, err := apiresource.ParseQuantity(typedValue)
if err != nil {
glog.Warningf("Invalid quantity in resource %s, err: %v\n", typedValue, err)
return false
}
return compareQuantity(valueQuan, patternQuan, operator)
}
// 2. wildcard match
if !wildcard.Match(pattern, typedValue) {
glog.Warningf("Value '%s' has not passed wildcard check: %s", typedValue, pattern)
return false
}
return true
}
// validateNumber compares two numbers with operator
func validateNumber(value, pattern interface{}, operator Operator) bool {
floatPattern, err := convertToFloat(pattern)
if err != nil {
return false
}
floatValue, err := convertToFloat(value)
if err != nil {
return false
}
func compareQuantity(value, pattern apiresource.Quantity, operator Operator) bool {
result := value.Cmp(pattern)
switch operator {
case Equal:
return floatValue == floatPattern
return result == int(equal)
case NotEqual:
return floatValue != floatPattern
return result != int(equal)
case More:
return floatValue > floatPattern
case MoreEqual:
return floatValue >= floatPattern
return result == int(greaterThan)
case Less:
return floatValue < floatPattern
return result == int(lessThan)
case MoreEqual:
return (result == int(equal)) || (result == int(greaterThan))
case LessEqual:
return floatValue <= floatPattern
return (result == int(equal)) || (result == int(lessThan))
}
return false

View file

@ -152,6 +152,10 @@ func TestValidateValueWithPattern_StringsLogicalOr(t *testing.T) {
assert.Assert(t, ValidateValueWithPattern(value, pattern))
}
func TestValidateValueWithPattern_EqualTwoFloats(t *testing.T) {
assert.Assert(t, ValidateValueWithPattern(7.0, 7.000))
}
func TestValidateValueWithNilPattern_NullPatternStringValue(t *testing.T) {
assert.Assert(t, !validateValueWithNilPattern("value"))
}
@ -242,32 +246,36 @@ func TestGetNumberAndStringPartsFromPattern_Empty(t *testing.T) {
assert.Equal(t, str, "")
}
func TestValidateNumber_EqualTwoFloats(t *testing.T) {
assert.Assert(t, validateNumber(7.0, 7.000, Equal))
func TestValidateNumberWithStr_LessFloatAndInt(t *testing.T) {
assert.Assert(t, validateNumberWithStr(7.00001, "7.000001", More))
assert.Assert(t, validateNumberWithStr(7.00001, "7", NotEqual))
assert.Assert(t, validateNumberWithStr(7.0000, "7", Equal))
assert.Assert(t, !validateNumberWithStr(6.000000001, "6", Less))
}
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 TestValidateQuantity_InvalidQuantity(t *testing.T) {
assert.Assert(t, !validateNumberWithStr("1024Gi", "", Equal))
assert.Assert(t, !validateNumberWithStr("gii", "1024Gi", Equal))
}
func TestValidateNumberWithStr_Equal(t *testing.T) {
assert.Assert(t, validateNumberWithStr("1024Gi", "1024", "Gi", Equal))
func TestValidateQuantity_Equal(t *testing.T) {
assert.Assert(t, validateNumberWithStr("1024Gi", "1024Gi", Equal))
assert.Assert(t, validateNumberWithStr("1024Mi", "1Gi", Equal))
assert.Assert(t, validateNumberWithStr("0.2", "200m", Equal))
assert.Assert(t, validateNumberWithStr("500", "500", Equal))
assert.Assert(t, !validateNumberWithStr("2048", "1024", Equal))
assert.Assert(t, validateNumberWithStr(1024, "1024", 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))
func TestValidateQuantity_Operation(t *testing.T) {
assert.Assert(t, validateNumberWithStr("1Gi", "1000Mi", More))
assert.Assert(t, validateNumberWithStr("1G", "1Gi", Less))
assert.Assert(t, validateNumberWithStr("500m", "0.5", MoreEqual))
assert.Assert(t, validateNumberWithStr("1", "500m", MoreEqual))
assert.Assert(t, validateNumberWithStr("0.5", ".5", LessEqual))
assert.Assert(t, validateNumberWithStr("0.2", ".5", LessEqual))
assert.Assert(t, validateNumberWithStr("0.2", ".5", NotEqual))
}
func TestGetOperatorFromStringPattern_OneChar(t *testing.T) {

View file

@ -318,25 +318,19 @@ func removeAnchor(key string) string {
return key
}
// convertToFloat converts string and any other value to float64
func convertToFloat(value interface{}) (float64, error) {
// convertToString converts value to string
func convertToString(value interface{}) (string, error) {
switch typed := value.(type) {
case string:
var err error
floatValue, err := strconv.ParseFloat(typed, 64)
if err != nil {
return 0, err
}
return floatValue, nil
return string(typed), nil
case float64:
return typed, nil
return fmt.Sprintf("%f", typed), nil
case int64:
return float64(typed), nil
return strconv.FormatInt(typed, 10), nil
case int:
return float64(typed), nil
return strconv.Itoa(typed), nil
default:
return 0, fmt.Errorf("Could not convert %T to float64", value)
return "", fmt.Errorf("Could not convert %T to string", value)
}
}