From 0969aa9bf93975a181a93c67883e0ffc6ceff6e9 Mon Sep 17 00:00:00 2001 From: Shuting Zhao Date: Fri, 13 Dec 2019 13:17:22 -0800 Subject: [PATCH 1/2] implement quantity comparison --- pkg/engine/pattern.go | 83 ++++++++++++++++++++------------------ pkg/engine/pattern_test.go | 48 +++++++++++++--------- pkg/engine/utils.go | 20 ++++----- 3 files changed, 79 insertions(+), 72 deletions(-) diff --git a/pkg/engine/pattern.go b/pkg/engine/pattern.go index 6e3ae0039e..0bcfb36fa0 100644 --- a/pkg/engine/pattern.go +++ b/pkg/engine/pattern.go @@ -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 diff --git a/pkg/engine/pattern_test.go b/pkg/engine/pattern_test.go index 2ce21ddde2..c9e0fd9a3a 100644 --- a/pkg/engine/pattern_test.go +++ b/pkg/engine/pattern_test.go @@ -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)) + assert.Assert(t, !validateNumberWithStr(1024, "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)) } -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) { diff --git a/pkg/engine/utils.go b/pkg/engine/utils.go index 51d00d4b06..47f74db0a5 100644 --- a/pkg/engine/utils.go +++ b/pkg/engine/utils.go @@ -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) } } From 5ced2409a38fcb80be4a3cfa47c7a02480fb1123 Mon Sep 17 00:00:00 2001 From: Shuting Zhao Date: Fri, 13 Dec 2019 13:30:24 -0800 Subject: [PATCH 2/2] update test --- pkg/engine/pattern_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/engine/pattern_test.go b/pkg/engine/pattern_test.go index c9e0fd9a3a..f9c3d171c7 100644 --- a/pkg/engine/pattern_test.go +++ b/pkg/engine/pattern_test.go @@ -257,7 +257,6 @@ func TestValidateNumberWithStr_LessFloatAndInt(t *testing.T) { func TestValidateQuantity_InvalidQuantity(t *testing.T) { assert.Assert(t, !validateNumberWithStr("1024Gi", "", Equal)) assert.Assert(t, !validateNumberWithStr("gii", "1024Gi", Equal)) - assert.Assert(t, !validateNumberWithStr(1024, "1024Gi", Equal)) } func TestValidateQuantity_Equal(t *testing.T) { @@ -266,6 +265,7 @@ func TestValidateQuantity_Equal(t *testing.T) { 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 TestValidateQuantity_Operation(t *testing.T) {