2019-07-26 12:01:09 -07:00
package engine
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-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-11 21:03:34 -08:00
continue
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-11-06 16:16:29 -08:00
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 )
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-11-06 16:16:29 -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 :
if ! 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-11-06 16:16:29 -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-11-06 16:16:29 -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 {
// policy: "(image)": "*:latest",
// "imagePullPolicy": "IfNotPresent",
// resource: "(image)": "*:latest",
// the above case should be allowed
continue
}
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
}