1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2025-03-06 16:06:56 +00:00
kyverno/pkg/engine/overlayCondition.go

208 lines
7.6 KiB
Go
Raw Normal View History

2019-07-26 12:01:09 -07:00
package engine
import (
"fmt"
2019-07-26 12:01:09 -07:00
"reflect"
"strconv"
2019-07-26 12:01:09 -07:00
"github.com/golang/glog"
2019-10-21 14:22:31 -07:00
"github.com/nirmata/kyverno/pkg/engine/anchor"
2019-07-26 12:01:09 -07:00
)
func meetConditions(resource, overlay interface{}) (string, overlayError) {
return checkConditions(resource, overlay, "/")
}
// resource and overlay should be the same type
func checkConditions(resource, overlay interface{}, path string) (string, overlayError) {
2019-07-26 12:01:09 -07:00
// overlay has no anchor, return true
if !hasNestedAnchors(overlay) {
return "", overlayError{}
2019-07-26 12:01:09 -07:00
}
// resource item exists but has different type
// return false if anchor exists in overlay
// conditon never be true in this case
if reflect.TypeOf(resource) != reflect.TypeOf(overlay) {
if hasNestedAnchors(overlay) {
glog.V(4).Infof("Found anchor on different types of element at path %s: overlay %T, resource %T", path, overlay, resource)
return path, newOverlayError(conditionFailure,
fmt.Sprintf("Found anchor on different types of element at path %s: overlay %T %v, resource %T %v", path, overlay, overlay, resource, resource))
2019-07-26 12:01:09 -07:00
}
return "", overlayError{}
2019-07-26 12:01:09 -07:00
}
switch typedOverlay := overlay.(type) {
case map[string]interface{}:
typedResource := resource.(map[string]interface{})
return checkConditionOnMap(typedResource, typedOverlay, path)
2019-07-26 12:01:09 -07:00
case []interface{}:
typedResource := resource.([]interface{})
return checkConditionOnArray(typedResource, typedOverlay, path)
2019-07-26 12:01:09 -07:00
default:
2019-10-31 20:38:24 -07:00
// anchor on non map/array is invalid:
// - anchor defined on values
glog.Warningln("Found invalid conditional anchor: anchor defined on values")
return "", overlayError{}
}
}
func checkConditionOnMap(resourceMap, overlayMap map[string]interface{}, path string) (string, overlayError) {
anchors, overlayWithoutAnchor := getAnchorAndElementsFromMap(overlayMap)
2019-11-05 11:04:43 -08:00
// validate resource with conditions
if newPath, err := validateConditionAnchorMap(resourceMap, anchors, path); !reflect.DeepEqual(err, overlayError{}) {
return newPath, err
2019-07-26 12:01:09 -07:00
}
2019-11-05 11:04:43 -08:00
// traverse overlay pattern to further validate conditions
if newPath, err := validateNonAnchorOverlayMap(resourceMap, overlayWithoutAnchor, path); !reflect.DeepEqual(err, overlayError{}) {
return newPath, err
}
// empty overlayMap
return "", overlayError{}
}
func checkConditionOnArray(resource, overlay []interface{}, path string) (string, overlayError) {
if 0 == len(overlay) {
glog.Infof("Mutate overlay pattern is empty, path %s", path)
return "", overlayError{}
}
if reflect.TypeOf(resource[0]) != reflect.TypeOf(overlay[0]) {
glog.V(4).Infof("Overlay array and resource array have elements of different types: %T and %T", overlay[0], resource[0])
return path, newOverlayError(conditionFailure,
fmt.Sprintf("Overlay array and resource array have elements of different types: %T and %T", overlay[0], resource[0]))
}
return checkConditionsOnArrayOfSameTypes(resource, overlay, path)
}
func validateConditionAnchorMap(resourceMap, anchors map[string]interface{}, path string) (string, overlayError) {
for key, overlayValue := range anchors {
// skip if key does not have condition anchor
if !anchor.IsConditionAnchor(key) {
continue
}
// validate condition anchor map
noAnchorKey := removeAnchor(key)
curPath := path + noAnchorKey + "/"
if resourceValue, ok := resourceMap[noAnchorKey]; ok {
2019-11-05 11:04:43 -08:00
// compare entire resourceValue block
// return immediately on err since condition fails on this block
if newPath, err := compareOverlay(resourceValue, overlayValue, curPath); !reflect.DeepEqual(err, overlayError{}) {
return newPath, err
}
} else {
// noAnchorKey doesn't exist in resource
return curPath, newOverlayError(conditionNotPresent, fmt.Sprintf("resource field is not present %s", noAnchorKey))
}
}
return "", overlayError{}
2019-07-26 12:01:09 -07:00
}
2019-10-31 20:38:24 -07:00
// compareOverlay compare values in anchormap and resourcemap
// i.e. check if B1 == B2
// overlay - (A): B1
// resource - A: B2
func compareOverlay(resource, overlay interface{}, path string) (string, overlayError) {
2019-10-31 20:38:24 -07:00
if reflect.TypeOf(resource) != reflect.TypeOf(overlay) {
glog.V(4).Infof("Found anchor on different types of element: overlay %T, resource %T\nSkip processing overlay.", overlay, resource)
return path, newOverlayError(conditionFailure, fmt.Sprintf("Found anchor on different types of element: overlay %T, resource %T\nSkip processing overlay.", overlay, resource))
2019-10-31 20:38:24 -07:00
}
switch typedOverlay := overlay.(type) {
case map[string]interface{}:
typedResource := resource.(map[string]interface{})
for key, overlayVal := range typedOverlay {
noAnchorKey := removeAnchor(key)
curPath := path + noAnchorKey + "/"
2019-10-31 20:38:24 -07:00
resourceVal, ok := typedResource[noAnchorKey]
if !ok {
return curPath, newOverlayError(conditionFailure, fmt.Sprintf("field %s is not present", noAnchorKey))
2019-10-31 20:38:24 -07:00
}
if newPath, err := compareOverlay(resourceVal, overlayVal, curPath); !reflect.DeepEqual(err, overlayError{}) {
return newPath, err
2019-10-31 20:38:24 -07:00
}
}
case []interface{}:
typedResource := resource.([]interface{})
for _, overlayElement := range typedOverlay {
for _, resourceElement := range typedResource {
if newPath, err := compareOverlay(resourceElement, overlayElement, path); !reflect.DeepEqual(err, overlayError{}) {
return newPath, err
2019-10-31 20:38:24 -07:00
}
}
}
case string, float64, int, int64, bool, nil:
if !ValidateValueWithPattern(resource, overlay) {
glog.V(4).Infof("Mutate rule: failed validating value %v with overlay %v", resource, overlay)
return path, newOverlayError(conditionFailure, fmt.Sprintf("failed validating value %v with overlay %v", resource, overlay))
2019-07-26 12:01:09 -07:00
}
2019-10-31 20:38:24 -07:00
default:
return path, newOverlayError(conditionFailure, fmt.Sprintf("overlay has unknown type %T, value %v", overlay, overlay))
2019-10-31 20:38:24 -07:00
}
return "", overlayError{}
2019-10-31 20:38:24 -07:00
}
2019-07-26 12:01:09 -07:00
2019-11-05 11:04:43 -08:00
// validateNonAnchorOverlayMap validate anchor condition in overlay block without anchor
func validateNonAnchorOverlayMap(resourceMap, overlayWithoutAnchor map[string]interface{}, path string) (string, overlayError) {
2019-10-31 20:38:24 -07:00
// validate resource map (anchors could exist in resource)
for key, overlayValue := range overlayWithoutAnchor {
curPath := path + key + "/"
2019-10-31 20:38:24 -07:00
resourceValue, ok := resourceMap[key]
if !ok {
// policy: "(image)": "*:latest",
// "imagePullPolicy": "IfNotPresent",
// resource: "(image)": "*:latest",
// the above case should be allowed
continue
}
if newPath, err := checkConditions(resourceValue, overlayValue, curPath); !reflect.DeepEqual(err, overlayError{}) {
return newPath, err
2019-10-31 20:38:24 -07:00
}
}
return "", overlayError{}
2019-07-26 12:01:09 -07:00
}
func checkConditionsOnArrayOfSameTypes(resource, overlay []interface{}, path string) (string, overlayError) {
2019-07-26 12:01:09 -07:00
switch overlay[0].(type) {
case map[string]interface{}:
return checkConditionsOnArrayOfMaps(resource, overlay, path)
2019-07-26 12:01:09 -07:00
default:
for i, overlayElement := range overlay {
curPath := path + strconv.Itoa(i) + "/"
path, err := checkConditions(resource[i], overlayElement, curPath)
if !reflect.DeepEqual(err, overlayError{}) {
return path, err
}
}
2019-07-26 12:01:09 -07:00
}
return "", overlayError{}
2019-07-26 12:01:09 -07:00
}
func checkConditionsOnArrayOfMaps(resource, overlay []interface{}, path string) (string, overlayError) {
2019-11-05 11:04:43 -08:00
var newPath string
var err overlayError
2019-11-05 11:04:43 -08:00
for i, overlayElement := range overlay {
2019-10-31 20:38:24 -07:00
for _, resourceMap := range resource {
curPath := path + strconv.Itoa(i) + "/"
2019-11-05 11:04:43 -08:00
newPath, err = checkConditionOnMap(resourceMap.(map[string]interface{}), overlayElement.(map[string]interface{}), curPath)
// when resource has multiple same blocks of the overlay block
// return true if there is one resource block meet the overlay pattern
// reference: TestMeetConditions_AtleastOneExist
if reflect.DeepEqual(err, overlayError{}) {
return "", overlayError{}
2019-07-26 15:54:42 -07:00
}
2019-07-26 12:01:09 -07:00
}
}
2019-11-05 11:04:43 -08:00
// report last error
return newPath, err
2019-07-26 12:01:09 -07:00
}