2020-01-07 17:06:17 -08:00
|
|
|
package mutate
|
2019-07-26 12:01:09 -07:00
|
|
|
|
|
|
|
import (
|
2019-11-01 11:14:58 -07:00
|
|
|
"fmt"
|
2019-07-26 12:01:09 -07:00
|
|
|
"reflect"
|
2019-11-01 11:14:58 -07:00
|
|
|
"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-12-30 17:08:50 -08:00
|
|
|
"github.com/nirmata/kyverno/pkg/engine/validate"
|
2019-07-26 12:01:09 -07:00
|
|
|
)
|
|
|
|
|
2019-11-06 16:16:29 -08:00
|
|
|
func meetConditions(resource, overlay interface{}) (string, overlayError) {
|
2019-11-01 11:14:58 -07:00
|
|
|
return checkConditions(resource, overlay, "/")
|
|
|
|
}
|
|
|
|
|
|
|
|
// resource and overlay should be the same type
|
2019-11-06 16:16:29 -08:00
|
|
|
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) {
|
2019-11-06 16:16:29 -08:00
|
|
|
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) {
|
2019-11-01 11:14:58 -07:00
|
|
|
glog.V(4).Infof("Found anchor on different types of element at path %s: overlay %T, resource %T", path, overlay, resource)
|
2019-11-06 16:16:29 -08:00
|
|
|
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-11-01 11:14:58 -07:00
|
|
|
|
2019-07-26 12:01:09 -07:00
|
|
|
}
|
2019-11-06 16:16:29 -08:00
|
|
|
return "", overlayError{}
|
2019-07-26 12:01:09 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
switch typedOverlay := overlay.(type) {
|
|
|
|
case map[string]interface{}:
|
|
|
|
typedResource := resource.(map[string]interface{})
|
2019-11-01 11:14:58 -07:00
|
|
|
return checkConditionOnMap(typedResource, typedOverlay, path)
|
2019-07-26 12:01:09 -07:00
|
|
|
case []interface{}:
|
|
|
|
typedResource := resource.([]interface{})
|
2019-11-01 11:14:58 -07:00
|
|
|
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
|
2019-11-04 19:23:48 -08:00
|
|
|
glog.Warningln("Found invalid conditional anchor: anchor defined on values")
|
2019-11-06 16:16:29 -08:00
|
|
|
return "", overlayError{}
|
2019-11-01 11:14:58 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-06 16:16:29 -08:00
|
|
|
func checkConditionOnMap(resourceMap, overlayMap map[string]interface{}, path string) (string, overlayError) {
|
2019-11-01 11:14:58 -07:00
|
|
|
anchors, overlayWithoutAnchor := getAnchorAndElementsFromMap(overlayMap)
|
|
|
|
|
2019-11-05 11:04:43 -08:00
|
|
|
// validate resource with conditions
|
2019-11-06 16:16:29 -08:00
|
|
|
if newPath, err := validateConditionAnchorMap(resourceMap, anchors, path); !reflect.DeepEqual(err, overlayError{}) {
|
2019-11-01 11:14:58 -07:00
|
|
|
return newPath, err
|
2019-07-26 12:01:09 -07:00
|
|
|
}
|
2019-11-01 11:14:58 -07:00
|
|
|
|
2019-11-05 11:04:43 -08:00
|
|
|
// traverse overlay pattern to further validate conditions
|
2019-11-06 16:16:29 -08:00
|
|
|
if newPath, err := validateNonAnchorOverlayMap(resourceMap, overlayWithoutAnchor, path); !reflect.DeepEqual(err, overlayError{}) {
|
2019-11-01 11:14:58 -07:00
|
|
|
return newPath, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// empty overlayMap
|
2019-11-06 16:16:29 -08:00
|
|
|
return "", overlayError{}
|
2019-11-01 11:14:58 -07:00
|
|
|
}
|
|
|
|
|
2019-11-06 16:16:29 -08:00
|
|
|
func checkConditionOnArray(resource, overlay []interface{}, path string) (string, overlayError) {
|
2019-11-01 11:14:58 -07:00
|
|
|
if 0 == len(overlay) {
|
|
|
|
glog.Infof("Mutate overlay pattern is empty, path %s", path)
|
2019-11-06 16:16:29 -08:00
|
|
|
return "", overlayError{}
|
2019-11-01 11:14:58 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
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])
|
2019-11-06 16:16:29 -08:00
|
|
|
return path, newOverlayError(conditionFailure,
|
|
|
|
fmt.Sprintf("Overlay array and resource array have elements of different types: %T and %T", overlay[0], resource[0]))
|
2019-11-01 11:14:58 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
return checkConditionsOnArrayOfSameTypes(resource, overlay, path)
|
|
|
|
}
|
|
|
|
|
2019-11-06 16:16:29 -08:00
|
|
|
func validateConditionAnchorMap(resourceMap, anchors map[string]interface{}, path string) (string, overlayError) {
|
2019-11-01 11:14:58 -07:00
|
|
|
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
|
2019-11-06 16:16:29 -08:00
|
|
|
if newPath, err := compareOverlay(resourceValue, overlayValue, curPath); !reflect.DeepEqual(err, overlayError{}) {
|
2019-11-01 11:14:58 -07:00
|
|
|
return newPath, err
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// noAnchorKey doesn't exist in resource
|
2019-11-27 19:40:47 -08:00
|
|
|
return curPath, newOverlayError(conditionNotPresent, fmt.Sprintf("resource field is not present %s", noAnchorKey))
|
2019-11-01 11:14:58 -07:00
|
|
|
}
|
|
|
|
}
|
2019-11-06 16:16:29 -08:00
|
|
|
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
|
2019-11-06 16:16:29 -08:00
|
|
|
func compareOverlay(resource, overlay interface{}, path string) (string, overlayError) {
|
2019-10-31 20:38:24 -07:00
|
|
|
if reflect.TypeOf(resource) != reflect.TypeOf(overlay) {
|
2019-12-09 19:28:34 -08:00
|
|
|
glog.V(4).Infof("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))
|
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)
|
2019-11-01 11:14:58 -07:00
|
|
|
curPath := path + noAnchorKey + "/"
|
2019-10-31 20:38:24 -07:00
|
|
|
resourceVal, ok := typedResource[noAnchorKey]
|
|
|
|
if !ok {
|
2019-12-09 19:28:34 -08:00
|
|
|
return curPath, newOverlayError(conditionFailure, fmt.Sprintf("Field %s is not present", noAnchorKey))
|
2019-10-31 20:38:24 -07:00
|
|
|
}
|
2019-11-06 16:16:29 -08:00
|
|
|
if newPath, err := compareOverlay(resourceVal, overlayVal, curPath); !reflect.DeepEqual(err, overlayError{}) {
|
2019-11-01 11:14:58 -07:00
|
|
|
return newPath, err
|
2019-10-31 20:38:24 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
case []interface{}:
|
|
|
|
typedResource := resource.([]interface{})
|
|
|
|
for _, overlayElement := range typedOverlay {
|
|
|
|
for _, resourceElement := range typedResource {
|
2019-11-06 16:16:29 -08:00
|
|
|
if newPath, err := compareOverlay(resourceElement, overlayElement, path); !reflect.DeepEqual(err, overlayError{}) {
|
2019-11-01 11:14:58 -07:00
|
|
|
return newPath, err
|
2019-10-31 20:38:24 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
case string, float64, int, int64, bool, nil:
|
2019-12-30 17:08:50 -08:00
|
|
|
if !validate.ValidateValueWithPattern(resource, overlay) {
|
2019-11-01 11:14:58 -07:00
|
|
|
glog.V(4).Infof("Mutate rule: failed validating value %v with overlay %v", resource, overlay)
|
2019-12-09 19:28:34 -08:00
|
|
|
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:
|
2019-12-09 19:28:34 -08:00
|
|
|
return path, newOverlayError(conditionFailure, fmt.Sprintf("Overlay has unknown type %T, value %v", overlay, overlay))
|
2019-10-31 20:38:24 -07:00
|
|
|
}
|
|
|
|
|
2019-11-06 16:16:29 -08: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
|
2019-11-06 16:16:29 -08:00
|
|
|
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 {
|
2019-11-01 11:14:58 -07:00
|
|
|
curPath := path + key + "/"
|
2019-10-31 20:38:24 -07:00
|
|
|
resourceValue, ok := resourceMap[key]
|
|
|
|
if !ok {
|
2019-11-27 16:07:15 -08:00
|
|
|
if !hasNestedAnchors(overlayValue) {
|
|
|
|
// policy: "(image)": "*:latest",
|
|
|
|
// "imagePullPolicy": "IfNotPresent",
|
|
|
|
// resource: "(image)": "*:latest",
|
|
|
|
// the above case should be allowed
|
|
|
|
continue
|
|
|
|
}
|
2019-10-31 20:38:24 -07:00
|
|
|
}
|
2019-11-06 16:16:29 -08:00
|
|
|
if newPath, err := checkConditions(resourceValue, overlayValue, curPath); !reflect.DeepEqual(err, overlayError{}) {
|
2019-11-01 11:14:58 -07:00
|
|
|
return newPath, err
|
2019-10-31 20:38:24 -07:00
|
|
|
}
|
|
|
|
}
|
2019-11-06 16:16:29 -08:00
|
|
|
return "", overlayError{}
|
2019-07-26 12:01:09 -07:00
|
|
|
}
|
|
|
|
|
2019-11-06 16:16:29 -08: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{}:
|
2019-11-01 11:14:58 -07:00
|
|
|
return checkConditionsOnArrayOfMaps(resource, overlay, path)
|
2019-07-26 12:01:09 -07:00
|
|
|
default:
|
2019-11-01 11:14:58 -07:00
|
|
|
for i, overlayElement := range overlay {
|
|
|
|
curPath := path + strconv.Itoa(i) + "/"
|
|
|
|
path, err := checkConditions(resource[i], overlayElement, curPath)
|
2019-11-06 16:16:29 -08:00
|
|
|
if !reflect.DeepEqual(err, overlayError{}) {
|
2019-11-01 11:14:58 -07:00
|
|
|
return path, err
|
|
|
|
}
|
|
|
|
}
|
2019-07-26 12:01:09 -07:00
|
|
|
}
|
2019-11-06 16:16:29 -08:00
|
|
|
return "", overlayError{}
|
2019-07-26 12:01:09 -07:00
|
|
|
}
|
|
|
|
|
2019-11-06 16:16:29 -08:00
|
|
|
func checkConditionsOnArrayOfMaps(resource, overlay []interface{}, path string) (string, overlayError) {
|
2019-11-05 11:04:43 -08:00
|
|
|
var newPath string
|
2019-11-06 16:16:29 -08:00
|
|
|
var err overlayError
|
2019-11-05 11:04:43 -08:00
|
|
|
|
2019-11-01 11:14:58 -07:00
|
|
|
for i, overlayElement := range overlay {
|
2019-10-31 20:38:24 -07:00
|
|
|
for _, resourceMap := range resource {
|
2019-11-01 11:14:58 -07:00
|
|
|
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
|
2019-11-06 16:16:29 -08:00
|
|
|
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
|
|
|
}
|