1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2025-03-06 16:06:56 +00:00
kyverno/pkg/engine/mutate/overlayCondition.go
shivkumar dudhani d327309d72 refactor logging
2020-03-17 16:25:34 -07:00

211 lines
7.8 KiB
Go
Executable file

package mutate
import (
"fmt"
"reflect"
"strconv"
"github.com/go-logr/logr"
"github.com/nirmata/kyverno/pkg/engine/anchor"
"github.com/nirmata/kyverno/pkg/engine/validate"
"sigs.k8s.io/controller-runtime/pkg/log"
)
func meetConditions(log logr.Logger, resource, overlay interface{}) (string, overlayError) {
return checkConditions(log, resource, overlay, "/")
}
// resource and overlay should be the same type
func checkConditions(log logr.Logger, resource, overlay interface{}, path string) (string, overlayError) {
// overlay has no anchor, return true
if !hasNestedAnchors(overlay) {
return "", overlayError{}
}
// resource item exists but has different type
// return false if anchor exists in overlay
// condition never be true in this case
if reflect.TypeOf(resource) != reflect.TypeOf(overlay) {
if hasNestedAnchors(overlay) {
log.V(4).Info(fmt.Sprintf("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))
}
return "", overlayError{}
}
switch typedOverlay := overlay.(type) {
case map[string]interface{}:
typedResource := resource.(map[string]interface{})
return checkConditionOnMap(typedResource, typedOverlay, path)
case []interface{}:
typedResource := resource.([]interface{})
return checkConditionOnArray(typedResource, typedOverlay, path)
default:
// anchor on non map/array is invalid:
// - anchor defined on values
log.Info("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)
// validate resource with conditions
if newPath, err := validateConditionAnchorMap(resourceMap, anchors, path); !reflect.DeepEqual(err, overlayError{}) {
return newPath, err
}
// 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) {
log.Log.V(4).Info("Mutate overlay pattern is empty", "path", path)
return "", overlayError{}
}
if reflect.TypeOf(resource[0]) != reflect.TypeOf(overlay[0]) {
log.Log.V(4).Info(fmt.Sprintf("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 {
// 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{}
}
// 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) {
if reflect.TypeOf(resource) != reflect.TypeOf(overlay) {
log.Log.V(4).Info("Found anchor on different types of element: overlay %T, resource %T", overlay, resource)
return path, newOverlayError(conditionFailure, fmt.Sprintf("Found anchor on different types of element: overlay %T, resource %T", overlay, resource))
}
switch typedOverlay := overlay.(type) {
case map[string]interface{}:
typedResource := resource.(map[string]interface{})
for key, overlayVal := range typedOverlay {
noAnchorKey := removeAnchor(key)
curPath := path + noAnchorKey + "/"
resourceVal, ok := typedResource[noAnchorKey]
if !ok {
return curPath, newOverlayError(conditionFailure, fmt.Sprintf("Field %s is not present", noAnchorKey))
}
if newPath, err := compareOverlay(resourceVal, overlayVal, curPath); !reflect.DeepEqual(err, overlayError{}) {
return newPath, err
}
}
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
}
}
}
case string, float64, int, int64, bool, nil:
if !validate.ValidateValueWithPattern(log.Log, resource, overlay) {
log.Log.V(4).Info(fmt.Sprintf("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))
}
default:
return path, newOverlayError(conditionFailure, fmt.Sprintf("Overlay has unknown type %T, value %v", overlay, overlay))
}
return "", overlayError{}
}
// validateNonAnchorOverlayMap validate anchor condition in overlay block without anchor
func validateNonAnchorOverlayMap(resourceMap, overlayWithoutAnchor map[string]interface{}, path string) (string, overlayError) {
// validate resource map (anchors could exist in resource)
for key, overlayValue := range overlayWithoutAnchor {
curPath := path + key + "/"
resourceValue, ok := resourceMap[key]
if !ok {
if !hasNestedAnchors(overlayValue) {
// policy: "(image)": "*:latest",
// "imagePullPolicy": "IfNotPresent",
// resource: "(image)": "*:latest",
// the above case should be allowed
continue
}
}
if newPath, err := checkConditions(log.Log, resourceValue, overlayValue, curPath); !reflect.DeepEqual(err, overlayError{}) {
return newPath, err
}
}
return "", overlayError{}
}
func checkConditionsOnArrayOfSameTypes(resource, overlay []interface{}, path string) (string, overlayError) {
switch overlay[0].(type) {
case map[string]interface{}:
return checkConditionsOnArrayOfMaps(resource, overlay, path)
default:
for i, overlayElement := range overlay {
curPath := path + strconv.Itoa(i) + "/"
path, err := checkConditions(log.Log, resource[i], overlayElement, curPath)
if !reflect.DeepEqual(err, overlayError{}) {
return path, err
}
}
}
return "", overlayError{}
}
func checkConditionsOnArrayOfMaps(resource, overlay []interface{}, path string) (string, overlayError) {
var newPath string
var err overlayError
for i, overlayElement := range overlay {
for _, resourceMap := range resource {
curPath := path + strconv.Itoa(i) + "/"
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{}
}
}
}
// report last error
return newPath, err
}