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

Merge pull request #116 from nirmata/wildcard_operator_fix

Wildcard operator fix
This commit is contained in:
shuting 2019-05-28 18:56:55 -07:00 committed by GitHub
commit 263218c729
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 610 additions and 321 deletions

View file

@ -66,7 +66,7 @@ func applyOverlay(resource, overlay interface{}, path string) ([]PatchBytes, err
for key, value := range typedOverlay {
if wrappedWithParentheses(key) {
key = key[1 : len(key)-1]
continue
}
currentPath := path + key + "/"
resourcePart, ok := typedResource[key]
@ -96,7 +96,7 @@ func applyOverlay(resource, overlay interface{}, path string) ([]PatchBytes, err
}
appliedPatches = append(appliedPatches, patches...)
case string, float64, int64:
case string, float64, int64, bool:
patch, err := replaceSubtree(overlay, path)
if err != nil {
return nil, err
@ -231,7 +231,7 @@ func skipArrayObject(object, anchors map[string]interface{}) bool {
return true
}
if value != pattern {
if !ValidateValueWithPattern(value, pattern) {
return true
}
}
@ -324,6 +324,8 @@ func prepareJSONValue(overlay interface{}) string {
return fmt.Sprintf("%f", typed)
case int64:
return fmt.Sprintf("%d", typed)
case bool:
return fmt.Sprintf("%t", typed)
default:
return ""
}

View file

@ -2,7 +2,6 @@ package engine
import (
"encoding/json"
"log"
"testing"
jsonpatch "github.com/evanphx/json-patch"
@ -220,7 +219,6 @@ func TestApplyOverlay_TestInsertToArray(t *testing.T) {
assert.Assert(t, decoded != nil)
patched, err := decoded.Apply(resourceRaw)
log.Fatalf("%s", patched)
assert.NilError(t, err)
assert.Assert(t, patched != nil)
}

303
pkg/engine/pattern.go Normal file
View file

@ -0,0 +1,303 @@
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
func ValidateValueWithPattern(value, pattern interface{}) bool {
switch typedPattern := pattern.(type) {
case bool:
typedValue, ok := value.(bool)
if !ok {
log.Printf("Expected bool, found %T", value)
return false
}
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 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
default:
log.Printf("Unknown type as pattern: %T\n", pattern)
return false
}
}
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 int64(typedValue) == pattern
}
log.Printf("Expected int, found float: %f\n", typedValue)
return false
default:
log.Printf("Expected int, found: %T\n", value)
return false
}
}
func validateValueWithFloatPattern(value interface{}, pattern float64) bool {
switch typedValue := value.(type) {
case int:
// check that float has no fraction
if pattern == math.Trunc(pattern) {
return int(pattern) == value
}
log.Printf("Expected float, found int: %d\n", typedValue)
return false
case float64:
return typedValue == pattern
default:
log.Printf("Expected float, found: %T\n", value)
return false
}
}
func validateValueWithNilPattern(value interface{}) bool {
switch typed := value.(type) {
case float64:
return typed == 0.0
case int:
return typed == 0
case int64:
return typed == 0
case string:
return typed == ""
case bool:
return typed == false
case nil:
return true
case map[string]interface{}, []interface{}:
log.Println("Maps and arrays could not be checked with nil pattern")
return false
default:
log.Printf("Unknown type as value when checking for nil pattern: %T\n", value)
return false
}
}
func validateValueWithStringPatterns(value interface{}, pattern string) bool {
statements := strings.Split(pattern, "|")
for _, statement := range statements {
statement = strings.Trim(statement, " ")
if validateValueWithStringPattern(value, statement) {
return true
}
}
return false
}
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)
}
return validateNumberWithStr(value, number, str, operator)
}
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 {
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, patternNumber, operator)
}
return validateNumber(value, patternNumber, operator)
}
func validateNumber(value, pattern interface{}, operator Operator) bool {
var floatPattern, floatValue float64
switch typed := value.(type) {
case string:
var err error
floatValue, err = strconv.ParseFloat(typed, 64)
if err != nil {
return false
}
case float64:
floatValue = typed
case int64:
floatValue = float64(typed)
case int:
floatValue = float64(typed)
default:
return false
}
switch typed := pattern.(type) {
case string:
var err error
floatPattern, err = strconv.ParseFloat(typed, 64)
if err != nil {
return false
}
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 len(pattern) < 2 {
return Equal
}
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 Equal
}
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
}

283
pkg/engine/pattern_test.go Normal file
View file

@ -0,0 +1,283 @@
package engine
import (
"encoding/json"
"testing"
"gotest.tools/assert"
)
func TestValidateValueWithPattern_Bool(t *testing.T) {
assert.Assert(t, ValidateValueWithPattern(true, true))
assert.Assert(t, !ValidateValueWithPattern(true, false))
assert.Assert(t, !ValidateValueWithPattern(false, true))
assert.Assert(t, ValidateValueWithPattern(false, false))
}
func TestValidateValueWithPattern_BoolInJson(t *testing.T) {
rawPattern := []byte(`
{
"key": true
}
`)
rawValue := []byte(`
{
"key": true
}
`)
var pattern, value map[string]interface{}
err := json.Unmarshal(rawPattern, &pattern)
assert.Assert(t, err)
err = json.Unmarshal(rawValue, &value)
assert.Assert(t, err)
assert.Assert(t, ValidateValueWithPattern(value["key"], pattern["key"]))
}
func TestValidateValueWithPattern_NullPatternStringValue(t *testing.T) {
rawPattern := []byte(`
{
"key": null
}
`)
rawValue := []byte(`
{
"key": "value"
}
`)
var pattern, value map[string]interface{}
err := json.Unmarshal(rawPattern, &pattern)
assert.Assert(t, err)
err = json.Unmarshal(rawValue, &value)
assert.Assert(t, err)
assert.Assert(t, !ValidateValueWithPattern(value["key"], pattern["key"]))
}
func TestValidateValueWithPattern_NullPatternDefaultString(t *testing.T) {
rawPattern := []byte(`
{
"key": null
}
`)
rawValue := []byte(`
{
"key": ""
}
`)
var pattern, value map[string]interface{}
err := json.Unmarshal(rawPattern, &pattern)
assert.Assert(t, err)
err = json.Unmarshal(rawValue, &value)
assert.Assert(t, err)
assert.Assert(t, ValidateValueWithPattern(value["key"], pattern["key"]))
}
func TestValidateValueWithPattern_NullPatternDefaultFloat(t *testing.T) {
rawPattern := []byte(`
{
"key": null
}
`)
rawValue := []byte(`
{
"key": 0.0
}
`)
var pattern, value map[string]interface{}
err := json.Unmarshal(rawPattern, &pattern)
assert.Assert(t, err)
err = json.Unmarshal(rawValue, &value)
assert.Assert(t, err)
assert.Assert(t, ValidateValueWithPattern(value["key"], pattern["key"]))
}
func TestValidateValueWithPattern_NullPatternDefaultInt(t *testing.T) {
rawPattern := []byte(`
{
"key": null
}
`)
rawValue := []byte(`
{
"key": 0
}
`)
var pattern, value map[string]interface{}
err := json.Unmarshal(rawPattern, &pattern)
assert.Assert(t, err)
err = json.Unmarshal(rawValue, &value)
assert.Assert(t, err)
assert.Assert(t, ValidateValueWithPattern(value["key"], pattern["key"]))
}
func TestValidateValueWithPattern_NullPatternDefaultBool(t *testing.T) {
rawPattern := []byte(`
{
"key": null
}
`)
rawValue := []byte(`
{
"key": false
}
`)
var pattern, value map[string]interface{}
err := json.Unmarshal(rawPattern, &pattern)
assert.Assert(t, err)
err = json.Unmarshal(rawValue, &value)
assert.Assert(t, err)
assert.Assert(t, ValidateValueWithPattern(value["key"], pattern["key"]))
}
func TestValidateValueWithPattern_StringsLogicalOr(t *testing.T) {
pattern := "192.168.88.1 | 10.100.11.*"
value := "10.100.11.54"
assert.Assert(t, ValidateValueWithPattern(value, pattern))
}
func TestValidateValueWithNilPattern_NullPatternStringValue(t *testing.T) {
assert.Assert(t, !validateValueWithNilPattern("value"))
}
func TestValidateValueWithNilPattern_NullPatternDefaultString(t *testing.T) {
assert.Assert(t, validateValueWithNilPattern(""))
}
func TestValidateValueWithNilPattern_NullPatternDefaultFloat(t *testing.T) {
assert.Assert(t, validateValueWithNilPattern(0.0))
}
func TestValidateValueWithNilPattern_NullPatternFloat(t *testing.T) {
assert.Assert(t, !validateValueWithNilPattern(0.1))
}
func TestValidateValueWithNilPattern_NullPatternDefaultInt(t *testing.T) {
assert.Assert(t, validateValueWithNilPattern(0))
}
func TestValidateValueWithNilPattern_NullPatternInt(t *testing.T) {
assert.Assert(t, !validateValueWithNilPattern(1))
}
func TestValidateValueWithNilPattern_NullPatternDefaultBool(t *testing.T) {
assert.Assert(t, validateValueWithNilPattern(false))
}
func TestValidateValueWithNilPattern_NullPatternTrueBool(t *testing.T) {
assert.Assert(t, !validateValueWithNilPattern(true))
}
func TestValidateValueWithFloatPattern_FloatValue(t *testing.T) {
assert.Assert(t, validateValueWithFloatPattern(7.9914, 7.9914))
}
func TestValidateValueWithFloatPattern_FloatValueNotPass(t *testing.T) {
assert.Assert(t, !validateValueWithFloatPattern(7.9914, 7.99141))
}
func TestValidateValueWithFloatPattern_FloatPatternWithoutFractionIntValue(t *testing.T) {
assert.Assert(t, validateValueWithFloatPattern(7, 7.000000))
}
func TestValidateValueWithFloatPattern_FloatPatternWithoutFraction(t *testing.T) {
assert.Assert(t, validateValueWithFloatPattern(7.000000, 7.000000))
}
func TestValidateValueWithIntPattern_FloatValueWithoutFraction(t *testing.T) {
assert.Assert(t, validateValueWithFloatPattern(7.000000, 7))
}
func TestValidateValueWithIntPattern_FloatValueWitFraction(t *testing.T) {
assert.Assert(t, !validateValueWithFloatPattern(7.000001, 7))
}
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))
}
func TestGetOperatorFromStringPattern_OneChar(t *testing.T) {
assert.Equal(t, getOperatorFromStringPattern("f"), Equal)
}
func TestGetOperatorFromStringPattern_EmptyString(t *testing.T) {
assert.Equal(t, getOperatorFromStringPattern(""), Equal)
}
func TestGetOperatorFromStringPattern_OnlyOperator(t *testing.T) {
assert.Equal(t, getOperatorFromStringPattern(">="), MoreEqual)
}

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 with %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, bool, nil:
if !ValidateValueWithPattern(resourcePart, patternPart) {
return fmt.Errorf("Failed validate %v with %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-*"}`)
@ -322,6 +158,17 @@ func TestGetAnchorsFromMap_ThereAreNoAnchors(t *testing.T) {
assert.Assert(t, len(actualMap) == 0)
}
func TestValidateMap(t *testing.T) {
rawPattern := []byte(`{ "spec": { "template": { "spec": { "containers": [ { "name": "?*", "resources": { "requests": { "cpu": "<4|8" } } } ] } } } }`)
rawMap := []byte(`{ "apiVersion": "apps/v1", "kind": "Deployment", "metadata": { "name": "nginx-deployment", "labels": { "app": "nginx" } }, "spec": { "replicas": 3, "selector": { "matchLabels": { "app": "nginx" } }, "template": { "metadata": { "labels": { "app": "nginx" } }, "spec": { "securityContext": { "runAsNonRoot": true }, "containers": [ { "name": "nginx", "image": "https://nirmata/nginx:latest", "imagePullPolicy": "Always", "readinessProbe": { "exec": { "command": [ "cat", "/tmp/healthy" ] }, "initialDelaySeconds": 5, "periodSeconds": 10 }, "livenessProbe": { "tcpSocket": { "port": 8080 }, "initialDelaySeconds": 15, "periodSeconds": 11 }, "resources": { "limits": { "memory": "2Gi", "cpu": 8 }, "requests": { "memory": "512Mi", "cpu": "8" } }, "ports": [ { "containerPort": 80 } ] } ] } } } }`)
var pattern, resource interface{}
json.Unmarshal(rawPattern, &pattern)
json.Unmarshal(rawMap, &resource)
assert.NilError(t, validateMap(resource, pattern))
}
func TestValidateMapElement_TwoElementsInArrayOnePass(t *testing.T) {
rawPattern := []byte(`[ { "(name)": "nirmata-*", "object": [ { "(key1)": "value*", "key2": "value*" } ] } ]`)
rawMap := []byte(`[ { "name": "nirmata-1", "object": [ { "key1": "value1", "key2": "value2" } ] }, { "name": "nirmata-1", "object": [ { "key1": "not_value", "key2": "not_value" } ] } ]`)
@ -356,7 +203,7 @@ func TestValidateMapElement_OneElementInArrayNotPass(t *testing.T) {
}
func TestValidate_ServiceTest(t *testing.T) {
rawPolicy := []byte(`{ "apiVersion": "kyverno.nirmata.io/v1alpha1", "kind": "Policy", "metadata": { "name": "policy-service" }, "spec": { "rules": [ { "name": "ps1", "resource": { "kind": "Service", "name": "game-service*" }, "mutate": { "patches": [ { "path": "/metadata/labels/isMutated", "op": "add", "value": "true" }, { "path": "/metadata/labels/secretLabel", "op": "replace", "value": "weKnow" }, { "path": "/metadata/labels/originalLabel", "op": "remove" }, { "path": "/spec/selector/app", "op": "replace", "value": "mutedApp" } ] }, "validate": { "message": "This resource is broken", "pattern": { "spec": { "ports": [ { "name": "hs", "protocol": 32 } ] } } } } ] } }`)
rawPolicy := []byte(`{ "apiVersion": "kyverno.nirmata.io/v1alpha1", "kind": "Policy", "metadata": { "name": "policy-service" }, "spec": { "rules": [ { "name": "ps1", "resource": { "kinds": [ "Service" ], "name": "game-service*" }, "mutate": { "patches": [ { "path": "/metadata/labels/isMutated", "op": "add", "value": "true" }, { "path": "/metadata/labels/secretLabel", "op": "replace", "value": "weKnow" }, { "path": "/metadata/labels/originalLabel", "op": "remove" }, { "path": "/spec/selector/app", "op": "replace", "value": "mutedApp" } ] }, "validate": { "message": "This resource is broken", "pattern": { "spec": { "ports": [ { "name": "hs", "protocol": 32 } ] } } } } ] } }`)
rawResource := []byte(`{ "kind": "Service", "apiVersion": "v1", "metadata": { "name": "game-service", "labels": { "originalLabel": "isHere", "secretLabel": "thisIsMySecret" } }, "spec": { "selector": { "app": "MyApp" }, "ports": [ { "name": "http", "protocol": "TCP", "port": 80, "targetPort": 9376 } ] } }`)
var policy kubepolicy.Policy
@ -370,7 +217,7 @@ func TestValidate_ServiceTest(t *testing.T) {
}
func TestValidate_MapHasFloats(t *testing.T) {
rawPolicy := []byte(`{ "apiVersion": "kyverno.nirmata.io/v1alpha1", "kind": "Policy", "metadata": { "name": "policy-deployment-changed" }, "spec": { "rules": [ { "name": "First policy v2", "resource": { "kind": "Deployment", "name": "nginx-*" }, "mutate": { "patches": [ { "path": "/metadata/labels/isMutated", "op": "add", "value": "true" }, { "path": "/metadata/labels/app", "op": "replace", "value": "nginx_is_mutated" } ] }, "validate": { "message": "replicas number is wrong", "pattern": { "metadata": { "labels": { "app": "*" } }, "spec": { "replicas": 3 } } } } ] } }`)
rawPolicy := []byte(`{ "apiVersion": "kyverno.nirmata.io/v1alpha1", "kind": "Policy", "metadata": { "name": "policy-deployment-changed" }, "spec": { "rules": [ { "name": "First policy v2", "resource": { "kinds": [ "Deployment" ], "name": "nginx-*" }, "mutate": { "patches": [ { "path": "/metadata/labels/isMutated", "op": "add", "value": "true" }, { "path": "/metadata/labels/app", "op": "replace", "value": "nginx_is_mutated" } ] }, "validate": { "message": "replicas number is wrong", "pattern": { "metadata": { "labels": { "app": "*" } }, "spec": { "replicas": 3 } } } } ] } }`)
rawResource := []byte(`{ "apiVersion": "apps/v1", "kind": "Deployment", "metadata": { "name": "nginx-deployment", "labels": { "app": "nginx" } }, "spec": { "replicas": 3, "selector": { "matchLabels": { "app": "nginx" } }, "template": { "metadata": { "labels": { "app": "nginx" } }, "spec": { "containers": [ { "name": "nginx", "image": "nginx:1.7.9", "ports": [ { "containerPort": 80 } ] } ] } } } }`)
var policy kubepolicy.Policy