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:
parent
f6ebb056a9
commit
46cbbf9568
6 changed files with 732 additions and 33 deletions
6
.directory
Normal file
6
.directory
Normal file
|
@ -0,0 +1,6 @@
|
|||
[Dolphin]
|
||||
Timestamp=2019,6,20,15,39,37
|
||||
Version=3
|
||||
|
||||
[Settings]
|
||||
HiddenFilesShown=true
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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 := ")"
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue