mirror of
https://github.com/kyverno/kyverno.git
synced 2024-12-14 11:57:48 +00:00
8298a9a858
Signed-off-by: Liang Deng <283304489@qq.com> Co-authored-by: shuting <shuting@nirmata.com>
275 lines
9 KiB
Go
275 lines
9 KiB
Go
package anchor
|
|
|
|
import (
|
|
"fmt"
|
|
"strconv"
|
|
|
|
"github.com/go-logr/logr"
|
|
"github.com/kyverno/kyverno/pkg/logging"
|
|
)
|
|
|
|
type resourceElementHandler = func(
|
|
log logr.Logger,
|
|
resourceElement interface{},
|
|
patternElement interface{},
|
|
originPattern interface{},
|
|
path string,
|
|
ac *AnchorMap,
|
|
) (string, error)
|
|
|
|
// ValidationHandler for element processes
|
|
type ValidationHandler interface {
|
|
Handle(
|
|
handler resourceElementHandler,
|
|
resourceMap map[string]interface{},
|
|
originPattern interface{},
|
|
ac *AnchorMap,
|
|
) (string, error)
|
|
}
|
|
|
|
// CreateElementHandler factory to process elements
|
|
func CreateElementHandler(element string, pattern interface{}, path string) ValidationHandler {
|
|
if anchor := Parse(element); anchor != nil {
|
|
switch {
|
|
case IsCondition(anchor):
|
|
return newConditionAnchorHandler(anchor, pattern, path)
|
|
case IsGlobal(anchor):
|
|
return newGlobalAnchorHandler(anchor, pattern, path)
|
|
case IsExistence(anchor):
|
|
return newExistenceHandler(anchor, pattern, path)
|
|
case IsEquality(anchor):
|
|
return newEqualityHandler(anchor, pattern, path)
|
|
case IsNegation(anchor):
|
|
return newNegationHandler(anchor, pattern, path)
|
|
}
|
|
}
|
|
return newDefaultHandler(element, pattern, path)
|
|
}
|
|
|
|
// negationHandler provides handler for check if the tag in anchor is not defined
|
|
type negationHandler struct {
|
|
anchor Anchor
|
|
pattern interface{}
|
|
path string
|
|
}
|
|
|
|
// newNegationHandler returns instance of negation handler
|
|
func newNegationHandler(anchor Anchor, pattern interface{}, path string) ValidationHandler {
|
|
return negationHandler{
|
|
anchor: anchor,
|
|
pattern: pattern,
|
|
path: path,
|
|
}
|
|
}
|
|
|
|
// Handle process negation handler
|
|
func (nh negationHandler) Handle(handler resourceElementHandler, resourceMap map[string]interface{}, originPattern interface{}, ac *AnchorMap) (string, error) {
|
|
anchorKey := nh.anchor.Key()
|
|
currentPath := nh.path + anchorKey + "/"
|
|
// if anchor is present in the resource then fail
|
|
if _, ok := resourceMap[anchorKey]; ok {
|
|
// no need to process elements in value as key cannot be present in resource
|
|
ac.AnchorError = newNegationAnchorError(fmt.Sprintf("%s is not allowed", currentPath))
|
|
return currentPath, ac.AnchorError
|
|
}
|
|
// key is not defined in the resource
|
|
return "", nil
|
|
}
|
|
|
|
// equalityHandler provides handler for non anchor element
|
|
type equalityHandler struct {
|
|
anchor Anchor
|
|
pattern interface{}
|
|
path string
|
|
}
|
|
|
|
// newEqualityHandler returens instance of equality handler
|
|
func newEqualityHandler(anchor Anchor, pattern interface{}, path string) ValidationHandler {
|
|
return equalityHandler{
|
|
anchor: anchor,
|
|
pattern: pattern,
|
|
path: path,
|
|
}
|
|
}
|
|
|
|
// Handle processed equality anchor
|
|
func (eh equalityHandler) Handle(handler resourceElementHandler, resourceMap map[string]interface{}, originPattern interface{}, ac *AnchorMap) (string, error) {
|
|
anchorKey := eh.anchor.Key()
|
|
currentPath := eh.path + anchorKey + "/"
|
|
// check if anchor is present in resource
|
|
if value, ok := resourceMap[anchorKey]; ok {
|
|
// validate the values of the pattern
|
|
returnPath, err := handler(logging.GlobalLogger(), value, eh.pattern, originPattern, currentPath, ac)
|
|
if err != nil {
|
|
return returnPath, err
|
|
}
|
|
return "", nil
|
|
}
|
|
return "", nil
|
|
}
|
|
|
|
// defaultHandler provides handler for non anchor element
|
|
type defaultHandler struct {
|
|
element string
|
|
pattern interface{}
|
|
path string
|
|
}
|
|
|
|
// newDefaultHandler returns handler for non anchor elements
|
|
func newDefaultHandler(element string, pattern interface{}, path string) ValidationHandler {
|
|
return defaultHandler{
|
|
element: element,
|
|
pattern: pattern,
|
|
path: path,
|
|
}
|
|
}
|
|
|
|
// Handle process non anchor element
|
|
func (dh defaultHandler) Handle(handler resourceElementHandler, resourceMap map[string]interface{}, originPattern interface{}, ac *AnchorMap) (string, error) {
|
|
currentPath := dh.path + dh.element + "/"
|
|
if dh.pattern == "*" && resourceMap[dh.element] != nil {
|
|
return "", nil
|
|
} else if dh.pattern == "*" && resourceMap[dh.element] == nil {
|
|
return dh.path, fmt.Errorf("%s/%s not found", dh.path, dh.element)
|
|
} else {
|
|
path, err := handler(logging.GlobalLogger(), resourceMap[dh.element], dh.pattern, originPattern, currentPath, ac)
|
|
if err != nil {
|
|
return path, err
|
|
}
|
|
}
|
|
return "", nil
|
|
}
|
|
|
|
// conditionAnchorHandler provides handler for condition anchor
|
|
type conditionAnchorHandler struct {
|
|
anchor Anchor
|
|
pattern interface{}
|
|
path string
|
|
}
|
|
|
|
// newConditionAnchorHandler returns an instance of condition acnhor handler
|
|
func newConditionAnchorHandler(anchor Anchor, pattern interface{}, path string) ValidationHandler {
|
|
return conditionAnchorHandler{
|
|
anchor: anchor,
|
|
pattern: pattern,
|
|
path: path,
|
|
}
|
|
}
|
|
|
|
// Handle processed condition anchor
|
|
func (ch conditionAnchorHandler) Handle(handler resourceElementHandler, resourceMap map[string]interface{}, originPattern interface{}, ac *AnchorMap) (string, error) {
|
|
anchorKey := ch.anchor.Key()
|
|
currentPath := ch.path + anchorKey + "/"
|
|
// check if anchor is present in resource
|
|
if value, ok := resourceMap[anchorKey]; ok {
|
|
// validate the values of the pattern
|
|
returnPath, err := handler(logging.GlobalLogger(), value, ch.pattern, originPattern, currentPath, ac)
|
|
if err != nil {
|
|
ac.AnchorError = newConditionalAnchorError(err.Error())
|
|
return returnPath, ac.AnchorError
|
|
}
|
|
return "", nil
|
|
} else {
|
|
msg := "conditional anchor key doesn't exist in the resource"
|
|
return currentPath, newConditionalAnchorError(msg)
|
|
}
|
|
}
|
|
|
|
// globalAnchorHandler provides handler for global condition anchor
|
|
type globalAnchorHandler struct {
|
|
anchor Anchor
|
|
pattern interface{}
|
|
path string
|
|
}
|
|
|
|
// newGlobalAnchorHandler returns an instance of condition acnhor handler
|
|
func newGlobalAnchorHandler(anchor Anchor, pattern interface{}, path string) ValidationHandler {
|
|
return globalAnchorHandler{
|
|
anchor: anchor,
|
|
pattern: pattern,
|
|
path: path,
|
|
}
|
|
}
|
|
|
|
// Handle processed global condition anchor
|
|
func (gh globalAnchorHandler) Handle(handler resourceElementHandler, resourceMap map[string]interface{}, originPattern interface{}, ac *AnchorMap) (string, error) {
|
|
anchorKey := gh.anchor.Key()
|
|
currentPath := gh.path + anchorKey + "/"
|
|
// check if anchor is present in resource
|
|
if value, ok := resourceMap[anchorKey]; ok {
|
|
// validate the values of the pattern
|
|
returnPath, err := handler(logging.GlobalLogger(), value, gh.pattern, originPattern, currentPath, ac)
|
|
if err != nil {
|
|
ac.AnchorError = newGlobalAnchorError(err.Error())
|
|
return returnPath, ac.AnchorError
|
|
}
|
|
return "", nil
|
|
}
|
|
return "", nil
|
|
}
|
|
|
|
// existenceHandler provides handlers to process exitence anchor handler
|
|
type existenceHandler struct {
|
|
anchor Anchor
|
|
pattern interface{}
|
|
path string
|
|
}
|
|
|
|
// newExistenceHandler returns existence handler
|
|
func newExistenceHandler(anchor Anchor, pattern interface{}, path string) ValidationHandler {
|
|
return existenceHandler{
|
|
anchor: anchor,
|
|
pattern: pattern,
|
|
path: path,
|
|
}
|
|
}
|
|
|
|
// Handle processes the existence anchor handler
|
|
func (eh existenceHandler) Handle(handler resourceElementHandler, resourceMap map[string]interface{}, originPattern interface{}, ac *AnchorMap) (string, error) {
|
|
// skip is used by existence anchor to not process further if condition is not satisfied
|
|
anchorKey := eh.anchor.Key()
|
|
currentPath := eh.path + anchorKey + "/"
|
|
// check if anchor is present in resource
|
|
if value, ok := resourceMap[anchorKey]; ok {
|
|
// Existence anchor can only exist on resource value type of list
|
|
switch typedResource := value.(type) {
|
|
case []interface{}:
|
|
typedPattern, ok := eh.pattern.([]interface{})
|
|
if !ok {
|
|
return currentPath, fmt.Errorf("invalid pattern type %T: Pattern has to be of list to compare against resource", eh.pattern)
|
|
}
|
|
// loop all item in the pattern array
|
|
errorPath := ""
|
|
var err error
|
|
for _, patternMap := range typedPattern {
|
|
typedPatternMap, ok := patternMap.(map[string]interface{})
|
|
if !ok {
|
|
return currentPath, fmt.Errorf("invalid pattern type %T: Pattern has to be of type map to compare against items in resource", eh.pattern)
|
|
}
|
|
errorPath, err = validateExistenceListResource(handler, typedResource, typedPatternMap, originPattern, currentPath, ac)
|
|
if err != nil {
|
|
return errorPath, err
|
|
}
|
|
}
|
|
return errorPath, err
|
|
default:
|
|
return currentPath, fmt.Errorf("invalid resource type %T: Existence ^ () anchor can be used only on list/array type resource", value)
|
|
}
|
|
}
|
|
return "", nil
|
|
}
|
|
|
|
func validateExistenceListResource(handler resourceElementHandler, resourceList []interface{}, patternMap map[string]interface{}, originPattern interface{}, path string, ac *AnchorMap) (string, error) {
|
|
// the idea is all the element in the pattern array should be present atleast once in the resource list
|
|
// if non satisfy then throw an error
|
|
for i, resourceElement := range resourceList {
|
|
currentPath := path + strconv.Itoa(i) + "/"
|
|
_, err := handler(logging.GlobalLogger(), resourceElement, patternMap, originPattern, currentPath, ac)
|
|
if err == nil {
|
|
// condition is satisfied, dont check further
|
|
return "", nil
|
|
}
|
|
}
|
|
// none of the existence checks worked, so thats a failure sceanario
|
|
return path, fmt.Errorf("existence anchor validation failed at path %s", path)
|
|
}
|