1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2024-12-14 11:57:48 +00:00

Implemented validation across same yaml

This commit is contained in:
belyshevdenis 2019-06-20 18:21:55 +03:00
parent f6ebb056a9
commit 46cbbf9568
6 changed files with 732 additions and 33 deletions

6
.directory Normal file
View file

@ -0,0 +1,6 @@
[Dolphin]
Timestamp=2019,6,20,15,39,37
Version=3
[Settings]
HiddenFilesShown=true

View file

@ -23,7 +23,7 @@ func CreateAnchorHandler(anchor string, pattern interface{}, path string) Valida
// resourcePart must be an array of dictionaries
// patternPart must be a dictionary with anchors
type ValidationAnchorHandler interface {
Handle(resourcePart []interface{}, patternPart map[string]interface{}) result.RuleApplicationResult
Handle(resourcePart []interface{}, patternPart map[string]interface{}, originPattern interface{}) result.RuleApplicationResult
}
// NoAnchorValidationHandler just calls validateMap
@ -41,7 +41,7 @@ func NewNoAnchorValidationHandler(path string) ValidationAnchorHandler {
}
// Handle performs validation in context of NoAnchorValidationHandler
func (navh *NoAnchorValidationHandler) Handle(resourcePart []interface{}, patternPart map[string]interface{}) result.RuleApplicationResult {
func (navh *NoAnchorValidationHandler) Handle(resourcePart []interface{}, patternPart map[string]interface{}, originPattern interface{}) result.RuleApplicationResult {
handlingResult := result.NewRuleApplicationResult("")
for i, resourceElement := range resourcePart {
@ -53,7 +53,7 @@ func (navh *NoAnchorValidationHandler) Handle(resourcePart []interface{}, patter
return handlingResult
}
res := validateMap(typedResourceElement, patternPart, currentPath)
res := validateMap(typedResourceElement, patternPart, originPattern, currentPath)
handlingResult.MergeWith(&res)
}
@ -81,8 +81,8 @@ func NewConditionAnchorValidationHandler(anchor string, pattern interface{}, pat
}
// Handle performs validation in context of ConditionAnchorValidationHandler
func (cavh *ConditionAnchorValidationHandler) Handle(resourcePart []interface{}, patternPart map[string]interface{}) result.RuleApplicationResult {
_, handlingResult := handleConditionCases(resourcePart, patternPart, cavh.anchor, cavh.pattern, cavh.path)
func (cavh *ConditionAnchorValidationHandler) Handle(resourcePart []interface{}, patternPart map[string]interface{}, originPattern interface{}) result.RuleApplicationResult {
_, handlingResult := handleConditionCases(resourcePart, patternPart, cavh.anchor, cavh.pattern, cavh.path, originPattern)
return handlingResult
}
@ -110,8 +110,8 @@ func NewExistanceAnchorValidationHandler(anchor string, pattern interface{}, pat
}
// Handle performs validation in context of ExistanceAnchorValidationHandler
func (eavh *ExistanceAnchorValidationHandler) Handle(resourcePart []interface{}, patternPart map[string]interface{}) result.RuleApplicationResult {
anchoredEntries, handlingResult := handleConditionCases(resourcePart, patternPart, eavh.anchor, eavh.pattern, eavh.path)
func (eavh *ExistanceAnchorValidationHandler) Handle(resourcePart []interface{}, patternPart map[string]interface{}, originPattern interface{}) result.RuleApplicationResult {
anchoredEntries, handlingResult := handleConditionCases(resourcePart, patternPart, eavh.anchor, eavh.pattern, eavh.path, originPattern)
if 0 == anchoredEntries {
handlingResult.FailWithMessagef("Existance anchor %s used, but no suitable entries were found", eavh.anchor)
@ -134,7 +134,7 @@ func checkForAnchorCondition(anchor string, pattern interface{}, resourceMap map
// both () and ^() are checking conditions and have a lot of similar logic
// the only difference is that ^() requires existace of one element
// anchoredEntries var counts this occurences.
func handleConditionCases(resourcePart []interface{}, patternPart map[string]interface{}, anchor string, pattern interface{}, path string) (int, result.RuleApplicationResult) {
func handleConditionCases(resourcePart []interface{}, patternPart map[string]interface{}, anchor string, pattern interface{}, path string, originPattern interface{}) (int, result.RuleApplicationResult) {
handlingResult := result.NewRuleApplicationResult("")
anchoredEntries := 0
@ -152,7 +152,7 @@ func handleConditionCases(resourcePart []interface{}, patternPart map[string]int
}
anchoredEntries++
res := validateMap(typedResourceElement, patternPart, currentPath)
res := validateMap(typedResourceElement, patternPart, originPattern, currentPath)
handlingResult.MergeWith(&res)
}

View file

@ -28,6 +28,9 @@ const (
Less Operator = "<"
)
const relativePrefix Operator = "./"
const referenceSign Operator = "$()"
// ValidateValueWithPattern validates value with operators and wildcards
func ValidateValueWithPattern(value, pattern interface{}) bool {
switch typedPattern := pattern.(type) {

View file

@ -148,6 +148,28 @@ func isConditionAnchor(str string) bool {
return (str[0] == '(' && str[len(str)-1] == ')')
}
func getRawKeyIfWrappedWithAttributes(str string) string {
if len(str) < 2 {
return str
}
if str[0] == '(' && str[len(str)-1] == ')' {
return str[1 : len(str)-1]
} else if (str[0] == '$' || str[0] == '^' || str[0] == '+') && (str[1] == '(' && str[len(str)-1] == ')') {
return str[2 : len(str)-1]
} else {
return str
}
}
func isStringIsReference(str string) bool {
if len(str) < len(referenceSign) {
return false
}
return str[0] == '$' && str[1] == '(' && str[len(str)-1] == ')'
}
func isExistanceAnchor(str string) bool {
left := "^("
right := ")"

View file

@ -2,7 +2,11 @@ package engine
import (
"encoding/json"
"fmt"
"path/filepath"
"reflect"
"strconv"
"strings"
kubepolicy "github.com/nirmata/kyverno/pkg/apis/policy/v1alpha1"
"github.com/nirmata/kyverno/pkg/result"
@ -48,13 +52,13 @@ func Validate(policy kubepolicy.Policy, rawResource []byte, gvk metav1.GroupVers
// validateResourceWithPattern is a start of element-by-element validation process
// It assumes that validation is started from root, so "/" is passed
func validateResourceWithPattern(resource, pattern interface{}) result.RuleApplicationResult {
return validateResourceElement(resource, pattern, "/")
return validateResourceElement(resource, pattern, pattern, "/")
}
// validateResourceElement detects the element type (map, array, nil, string, int, bool, float)
// and calls corresponding handler
// Pattern tree and resource tree can have different structure. In this case validation fails
func validateResourceElement(resourceElement, patternElement interface{}, path string) result.RuleApplicationResult {
func validateResourceElement(resourceElement, patternElement, originPattern interface{}, path string) result.RuleApplicationResult {
res := result.NewRuleApplicationResult("")
// TODO: Move similar message templates to message package
@ -67,7 +71,7 @@ func validateResourceElement(resourceElement, patternElement interface{}, path s
return res
}
return validateMap(typedResourceElement, typedPatternElement, path)
return validateMap(typedResourceElement, typedPatternElement, originPattern, path)
// array
case []interface{}:
typedResourceElement, ok := resourceElement.([]interface{})
@ -76,9 +80,18 @@ func validateResourceElement(resourceElement, patternElement interface{}, path s
return res
}
return validateArray(typedResourceElement, typedPatternElement, path)
return validateArray(typedResourceElement, typedPatternElement, originPattern, path)
// elementary values
case string, float64, int, int64, bool, nil:
/*Analyze pattern */
if checkedPattern := reflect.ValueOf(patternElement); checkedPattern.Kind() == reflect.String {
if isStringIsReference(checkedPattern.String()) { //check for $ anchor
patternElement, res = actualizePattern(originPattern, checkedPattern.String(), path)
if result.Failed == res.Reason {
return res
}
}
}
if !ValidateValueWithPattern(resourceElement, patternElement) {
res.FailWithMessagef("Failed to validate value %v with pattern %v. Path: %s", resourceElement, patternElement, path)
}
@ -92,7 +105,7 @@ func validateResourceElement(resourceElement, patternElement interface{}, path s
// If validateResourceElement detects map element inside resource and pattern trees, it goes to validateMap
// For each element of the map we must detect the type again, so we pass these elements to validateResourceElement
func validateMap(resourceMap, patternMap map[string]interface{}, path string) result.RuleApplicationResult {
func validateMap(resourceMap, patternMap map[string]interface{}, origPattern interface{}, path string) result.RuleApplicationResult {
res := result.NewRuleApplicationResult("")
for key, patternElement := range patternMap {
@ -104,7 +117,7 @@ func validateMap(resourceMap, patternMap map[string]interface{}, path string) re
} else if patternElement == "*" && resourceMap[key] == nil {
res.FailWithMessagef("Field %s is not present", key)
} else {
elementResult := validateResourceElement(resourceMap[key], patternElement, path+key+"/")
elementResult := validateResourceElement(resourceMap[key], patternElement, origPattern, path+key+"/")
res.MergeWith(&elementResult)
}
}
@ -112,10 +125,7 @@ func validateMap(resourceMap, patternMap map[string]interface{}, path string) re
return res
}
// If validateResourceElement detects array element inside resource and pattern trees, it goes to validateArray
// Unlike the validateMap, we should check the array elements type on-site, because in case of maps, we should
// get anchors and check each array element with it.
func validateArray(resourceArray, patternArray []interface{}, path string) result.RuleApplicationResult {
func validateArray(resourceArray, patternArray []interface{}, originPattern interface{}, path string) result.RuleApplicationResult {
res := result.NewRuleApplicationResult("")
if 0 == len(patternArray) {
@ -126,13 +136,13 @@ func validateArray(resourceArray, patternArray []interface{}, path string) resul
case map[string]interface{}:
// This is special case, because maps in arrays can have anchors that must be
// processed with the special way affecting the entire array
arrayResult := validateArrayOfMaps(resourceArray, typedPatternElement, path)
arrayResult := validateArrayOfMaps(resourceArray, typedPatternElement, originPattern, path)
res.MergeWith(&arrayResult)
default:
// In all other cases - detect type and handle each array element with validateResourceElement
for i, patternElement := range patternArray {
currentPath := path + strconv.Itoa(i) + "/"
elementResult := validateResourceElement(resourceArray[i], patternElement, currentPath)
elementResult := validateResourceElement(resourceArray[i], patternElement, originPattern, currentPath)
res.MergeWith(&elementResult)
}
}
@ -140,11 +150,126 @@ func validateArray(resourceArray, patternArray []interface{}, path string) resul
return res
}
func actualizePattern(origPattern interface{}, referencePattern, absolutePath string) (interface{}, result.RuleApplicationResult) {
res := result.NewRuleApplicationResult("")
var foundValue interface{}
referencePattern = strings.Trim(referencePattern, "$()")
operator := getOperatorFromStringPattern(referencePattern)
referencePattern = referencePattern[len(operator):]
if len(referencePattern) == 0 {
res.FailWithMessagef("Expected path. Found empty reference")
return nil, res
}
actualPath := FormAbsolutePath(referencePattern, absolutePath)
valFromReference, res := getValueFromReference(origPattern, actualPath)
if result.Failed == res.Reason {
return nil, res
}
if operator == Equal { //if operator does not exist return raw value
return valFromReference, res
}
foundValue, res = valFromReferenceToString(valFromReference, string(operator))
return string(operator) + foundValue.(string), res
}
//Parse value to string
func valFromReferenceToString(value interface{}, operator string) (string, result.RuleApplicationResult) {
res := result.NewRuleApplicationResult("")
switch typed := value.(type) {
case string:
return typed, res
case int, int64:
return fmt.Sprintf("%d", value), res
case float64:
return fmt.Sprintf("%f", value), res
default:
res.FailWithMessagef("Incorrect expression. Operator %s does not match with value: %v", operator, value)
return "", res
}
}
func FormAbsolutePath(referencePath, absolutePath string) string {
if filepath.IsAbs(referencePath) {
return referencePath
}
return filepath.Join(absolutePath, referencePath)
}
//Prepares original pattern, path to value, and call traverse function
func getValueFromReference(origPattern interface{}, reference string) (interface{}, result.RuleApplicationResult) {
originalPatternMap := origPattern.(map[string]interface{})
reference = reference[1:len(reference)]
statements := strings.Split(reference, "/")
return getValueFromPattern(originalPatternMap, statements, 0)
}
func getValueFromPattern(patternMap map[string]interface{}, keys []string, currentKeyIndex int) (interface{}, result.RuleApplicationResult) {
res := result.NewRuleApplicationResult("")
for key, pattern := range patternMap {
rawKey := getRawKeyIfWrappedWithAttributes(key)
if rawKey == keys[len(keys)-1] && currentKeyIndex == len(keys)-1 {
return pattern, res
} else if rawKey != keys[currentKeyIndex] && currentKeyIndex != len(keys)-1 {
continue
}
switch typedPattern := pattern.(type) {
case []interface{}:
if keys[currentKeyIndex] == rawKey {
for i, value := range typedPattern {
resourceMap, ok := value.(map[string]interface{})
if !ok {
res.FailWithMessagef("Pattern and resource have different structures. Expected %T, found %T", pattern, value)
return nil, res
}
if keys[currentKeyIndex+1] == strconv.Itoa(i) {
return getValueFromPattern(resourceMap, keys, currentKeyIndex+2)
}
res.FailWithMessagef("Reference to non-existent place in the document")
}
}
res.FailWithMessagef("Reference to non-existent place in the document")
case map[string]interface{}:
if keys[currentKeyIndex] == rawKey {
return getValueFromPattern(typedPattern, keys, currentKeyIndex+1)
}
res.FailWithMessagef("Reference to non-existent place in the document")
case string, float64, int, int64, bool, nil:
continue
}
}
path := ""
/*for i := len(keys) - 1; i >= 0; i-- {
path = keys[i] + path + "/"
}*/
for _, elem := range keys {
path = "/" + elem + path
}
res.FailWithMessagef("No value found for specified reference: %s", path)
return nil, res
}
// validateArrayOfMaps gets anchors from pattern array map element, applies anchors logic
// and then validates each map due to the pattern
func validateArrayOfMaps(resourceMapArray []interface{}, patternMap map[string]interface{}, path string) result.RuleApplicationResult {
func validateArrayOfMaps(resourceMapArray []interface{}, patternMap map[string]interface{}, originPattern interface{}, path string) result.RuleApplicationResult {
anchor, pattern := getAnchorFromMap(patternMap)
handler := CreateAnchorHandler(anchor, pattern, path)
return handler.Handle(resourceMapArray, patternMap)
return handler.Handle(resourceMapArray, patternMap, originPattern)
}

View file

@ -289,7 +289,7 @@ func TestValidateMap(t *testing.T) {
json.Unmarshal(rawPattern, &pattern)
json.Unmarshal(rawMap, &resource)
res := validateMap(resource, pattern, "/")
res := validateMap(resource, pattern, pattern, "/")
assert.NilError(t, res.ToError())
}
@ -384,7 +384,7 @@ func TestValidateMap_AsteriskForInt(t *testing.T) {
json.Unmarshal(rawPattern, &pattern)
json.Unmarshal(rawMap, &resource)
res := validateMap(resource, pattern, "/")
res := validateMap(resource, pattern, pattern, "/")
assert.NilError(t, res.ToError())
}
@ -476,7 +476,7 @@ func TestValidateMap_AsteriskForMap(t *testing.T) {
json.Unmarshal(rawPattern, &pattern)
json.Unmarshal(rawMap, &resource)
res := validateMap(resource, pattern, "/")
res := validateMap(resource, pattern, pattern, "/")
assert.NilError(t, res.ToError())
}
@ -563,7 +563,7 @@ func TestValidateMap_AsteriskForArray(t *testing.T) {
json.Unmarshal(rawPattern, &pattern)
json.Unmarshal(rawMap, &resource)
res := validateMap(resource, pattern, "/")
res := validateMap(resource, pattern, pattern, "/")
assert.NilError(t, res.ToError())
}
@ -653,7 +653,7 @@ func TestValidateMap_AsteriskFieldIsMissing(t *testing.T) {
json.Unmarshal(rawPattern, &pattern)
json.Unmarshal(rawMap, &resource)
res := validateMap(resource, pattern, "/")
res := validateMap(resource, pattern, pattern, "/")
assert.Assert(t, res.ToError() != nil)
}
@ -743,7 +743,7 @@ func TestValidateMap_livenessProbeIsNull(t *testing.T) {
json.Unmarshal(rawPattern, &pattern)
json.Unmarshal(rawMap, &resource)
res := validateMap(resource, pattern, "/")
res := validateMap(resource, pattern, pattern, "/")
assert.NilError(t, res.ToError())
}
@ -832,7 +832,7 @@ func TestValidateMap_livenessProbeIsMissing(t *testing.T) {
json.Unmarshal(rawPattern, &pattern)
json.Unmarshal(rawMap, &resource)
res := validateMap(resource, pattern, "/")
res := validateMap(resource, pattern, pattern, "/")
assert.NilError(t, res.ToError())
}
@ -873,7 +873,7 @@ func TestValidateMapElement_TwoElementsInArrayOnePass(t *testing.T) {
json.Unmarshal(rawPattern, &pattern)
json.Unmarshal(rawMap, &resource)
res := validateResourceElement(resource, pattern, "/")
res := validateResourceElement(resource, pattern, pattern, "/")
assert.NilError(t, res.ToError())
}
@ -905,10 +905,553 @@ func TestValidateMapElement_OneElementInArrayPass(t *testing.T) {
json.Unmarshal(rawPattern, &pattern)
json.Unmarshal(rawMap, &resource)
res := validateResourceElement(resource, pattern, "/")
res := validateResourceElement(resource, pattern, pattern, "/")
assert.NilError(t, res.ToError())
}
func TestValidateMap_CorrectRelativePathInConfig(t *testing.T) {
rawPattern := []byte(`{
"spec":{
"containers":[
{
"name":"*",
"resources":{
"requests":{
"memory":"$(<=./../../limits/memory)"
},
"limits":{
"memory":"2048Mi"
}
}
}
]
}
}`)
rawMap := []byte(`{
"apiVersion":"apps/v1",
"kind":"Deployment",
"metadata":{
"name":"nginx-deployment",
"labels":{
"app":"nginx"
}
},
"spec":{
"containers":[
{
"name":"nirmata",
"resources":{
"requests":{
"memory":"1024Mi"
},
"limits":{
"memory":"2048Mi"
}
}
}
]
}
}`)
var pattern, resource interface{}
json.Unmarshal(rawPattern, &pattern)
json.Unmarshal(rawMap, &resource)
res := validateResourceElement(resource, pattern, pattern, "/")
assert.NilError(t, res.ToError())
}
func TestValidateMap_RelativePathDoesNotExists(t *testing.T) {
rawPattern := []byte(`{
"spec":{
"containers":[
{
"name":"*",
"resources":{
"requests":{
"memory":"$(./../somekey/somekey2/memory)"
},
"limits":{
"memory":"2048Mi"
}
}
}
]
}
}`)
rawMap := []byte(`{
"apiVersion":"apps/v1",
"kind":"Deployment",
"metadata":{
"name":"nginx-deployment",
"labels":{
"app":"nginx"
}
},
"spec":{
"containers":[
{
"name":"nirmata",
"resources":{
"requests":{
"memory":"1024Mi"
},
"limits":{
"memory":"2048Mi"
}
}
}
]
}
}`)
var pattern, resource interface{}
json.Unmarshal(rawPattern, &pattern)
json.Unmarshal(rawMap, &resource)
res := validateResourceElement(resource, pattern, pattern, "/")
assert.Assert(t, res.ToError() != nil)
}
func TestValidateMap_OnlyAnchorsInPath(t *testing.T) {
rawPattern := []byte(`{
"spec":{
"containers":[
{
"name":"*",
"resources":{
"requests":{
"memory":"$()"
},
"limits":{
"memory":"2048Mi"
}
}
}
]
}
}`)
rawMap := []byte(`{
"apiVersion":"apps/v1",
"kind":"Deployment",
"metadata":{
"name":"nginx-deployment",
"labels":{
"app":"nginx"
}
},
"spec":{
"containers":[
{
"name":"nirmata",
"resources":{
"requests":{
"memory":"1024Mi"
},
"limits":{
"memory":"2048Mi"
}
}
}
]
}
}`)
var pattern, resource interface{}
json.Unmarshal(rawPattern, &pattern)
json.Unmarshal(rawMap, &resource)
res := validateResourceElement(resource, pattern, pattern, "/")
assert.Assert(t, res.ToError() != nil)
}
func TestValidateMap_MalformedReferenceOnlyDolarMark(t *testing.T) {
rawPattern := []byte(`{
"spec":{
"containers":[
{
"name":"*",
"resources":{
"requests":{
"memory":"$"
},
"limits":{
"memory":"2048Mi"
}
}
}
]
}
}`)
rawMap := []byte(`{
"apiVersion":"apps/v1",
"kind":"Deployment",
"metadata":{
"name":"nginx-deployment",
"labels":{
"app":"nginx"
}
},
"spec":{
"containers":[
{
"name":"nirmata",
"resources":{
"requests":{
"memory":"1024Mi"
},
"limits":{
"memory":"2048Mi"
}
}
}
]
}
}`)
var pattern, resource interface{}
json.Unmarshal(rawPattern, &pattern)
json.Unmarshal(rawMap, &resource)
res := validateResourceElement(resource, pattern, pattern, "/")
assert.Assert(t, res.ToError() != nil)
}
func TestValidateMap_RelativePathWithParentheses(t *testing.T) {
rawPattern := []byte(`{
"spec":{
"containers":[
{
"name":"*",
"resources":{
"requests":{
"memory":"$(<=./../../lim(its/mem)ory)"
},
"lim(its":{
"mem)ory":"2048Mi"
}
}
}
]
}
}`)
rawMap := []byte(`{
"apiVersion":"apps/v1",
"kind":"Deployment",
"metadata":{
"name":"nginx-deployment",
"labels":{
"app":"nginx"
}
},
"spec":{
"containers":[
{
"name":"nirmata",
"resources":{
"requests":{
"memory":"1024Mi"
},
"lim(its":{
"mem)ory":"2048Mi"
}
}
}
]
}
}`)
var pattern, resource interface{}
json.Unmarshal(rawPattern, &pattern)
json.Unmarshal(rawMap, &resource)
res := validateResourceElement(resource, pattern, pattern, "/")
assert.NilError(t, res.ToError())
}
func TestValidateMap_MalformedPath(t *testing.T) {
rawPattern := []byte(`{
"spec":{
"containers":[
{
"name":"*",
"resources":{
"requests":{
"memory":"$(>2048)"
},
"limits":{
"memory":"2048Mi"
}
}
}
]
}
}`)
rawMap := []byte(`{
"apiVersion":"apps/v1",
"kind":"Deployment",
"metadata":{
"name":"nginx-deployment",
"labels":{
"app":"nginx"
}
},
"spec":{
"containers":[
{
"name":"nirmata",
"resources":{
"requests":{
"memory":"1024Mi"
},
"limits":{
"memory":"2048Mi"
}
}
}
]
}
}`)
var pattern, resource interface{}
json.Unmarshal(rawPattern, &pattern)
json.Unmarshal(rawMap, &resource)
res := validateResourceElement(resource, pattern, pattern, "/")
assert.Assert(t, res.ToError() != nil)
}
func TestValidateMap_AbosolutePathExists(t *testing.T) {
rawPattern := []byte(`{
"spec":{
"containers":[
{
"name":"*",
"resources":{
"requests":{
"memory":"$(<=/spec/containers/0/resources/limits/memory)"
},
"limits":{
"memory":"2048Mi"
}
}
}
]
}
}`)
rawMap := []byte(`{
"apiVersion":"apps/v1",
"kind":"Deployment",
"metadata":{
"name":"nginx-deployment",
"labels":{
"app":"nginx"
}
},
"spec":{
"containers":[
{
"name":"nirmata",
"resources":{
"requests":{
"memory":"1024Mi"
},
"limits":{
"memory":"2048Mi"
}
}
}
]
}
}`)
var pattern, resource interface{}
json.Unmarshal(rawPattern, &pattern)
json.Unmarshal(rawMap, &resource)
res := validateResourceElement(resource, pattern, pattern, "/")
assert.Assert(t, res.ToError() == nil)
}
func TestValidateMap_AbsolutePathToMetadata(t *testing.T) {
rawPattern := []byte(`{
"metadata":{
"labels":{
"app":"nirmata*"
}
},
"spec":{
"containers":[
{
"(name)":"$(/metadata/labels/app)",
"image":"nirmata.io*"
}
]
}
}`)
rawMap := []byte(`{
"apiVersion":"apps/v1",
"kind":"Deployment",
"metadata":{
"labels":{
"app":"nirmata*"
}
},
"spec":{
"containers":[
{
"resources":{
"requests":{
"memory":"1024Mi"
},
"limits":{
"memory":"2048Mi"
}
},
"name":"nirmata"
}
]
}
}`)
var pattern, resource interface{}
json.Unmarshal(rawPattern, &pattern)
json.Unmarshal(rawMap, &resource)
res := validateResourceElement(resource, pattern, pattern, "/")
assert.Assert(t, res.ToError() == nil)
}
func TestValidateMap_AbosolutePathDoesNotExists(t *testing.T) {
rawPattern := []byte(`{
"spec":{
"containers":[
{
"name":"*",
"resources":{
"requests":{
"memory":"$(<=/some/memory)"
},
"limits":{
"memory":"2048Mi"
}
}
}
]
}
}`)
rawMap := []byte(`{
"apiVersion":"apps/v1",
"kind":"Deployment",
"metadata":{
"name":"nginx-deployment",
"labels":{
"app":"nginx"
}
},
"spec":{
"containers":[
{
"name":"nirmata",
"resources":{
"requests":{
"memory":"1024Mi"
},
"limits":{
"memory":"2048Mi"
}
}
}
]
}
}`)
var pattern, resource interface{}
json.Unmarshal(rawPattern, &pattern)
json.Unmarshal(rawMap, &resource)
res := validateResourceElement(resource, pattern, pattern, "/")
assert.Assert(t, res.ToError() != nil)
}
func TestActualizePattern_GivenRelativePathThatExists(t *testing.T) {
absolutePath := "/spec/containers/0/resources/requests/memory"
referencePath := "$(<=./../../limits/memory)"
rawPattern := []byte(`{
"spec":{
"containers":[
{
"name":"*",
"resources":{
"requests":{
"memory":"$(<=./../../limits/memory)"
},
"limits":{
"memory":"2048Mi"
}
}
}
]
}
}`)
var pattern interface{}
json.Unmarshal(rawPattern, &pattern)
pattern, res := actualizePattern(pattern, referencePath, absolutePath)
assert.Assert(t, res.ToError() == nil)
}
func TestFormAbsolutePath_RelativePathExists(t *testing.T) {
absolutePath := "/spec/containers/0/resources/requests/memory"
referencePath := "./../../limits/memory"
expectedString := "/spec/containers/0/resources/limits/memory"
result := FormAbsolutePath(referencePath, absolutePath)
assert.Assert(t, result == expectedString)
}
func TestFormAbsolutePath_RelativePathWithBackToTopInTheBegining(t *testing.T) {
absolutePath := "/spec/containers/0/resources/requests/memory"
referencePath := "../../limits/memory"
expectedString := "/spec/containers/0/resources/limits/memory"
result := FormAbsolutePath(referencePath, absolutePath)
assert.Assert(t, result == expectedString)
}
func TestFormAbsolutePath_AbsolutePathExists(t *testing.T) {
absolutePath := "/spec/containers/0/resources/requests/memory"
referencePath := "/spec/containers/0/resources/limits/memory"
result := FormAbsolutePath(referencePath, absolutePath)
assert.Assert(t, result == referencePath)
}
func TestFormAbsolutePath_EmptyPath(t *testing.T) {
absolutePath := "/spec/containers/0/resources/requests/memory"
referencePath := ""
result := FormAbsolutePath(referencePath, absolutePath)
assert.Assert(t, result == absolutePath)
}
func TestValidateMapElement_OneElementInArrayNotPass(t *testing.T) {
rawPattern := []byte(`[
{
@ -937,7 +1480,7 @@ func TestValidateMapElement_OneElementInArrayNotPass(t *testing.T) {
json.Unmarshal(rawPattern, &pattern)
json.Unmarshal(rawMap, &resource)
res := validateResourceElement(resource, pattern, "/")
res := validateResourceElement(resource, pattern, pattern, "/")
assert.Assert(t, res.ToError() != nil)
}