1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2025-03-13 19:28:55 +00:00

Merge pull request #1458 from kyverno/1457_wildcard_anchors

handle anchors for wildcard annotations
This commit is contained in:
Jim Bugwadia 2021-01-07 11:57:21 -08:00 committed by GitHub
commit ff246a81a1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 87 additions and 31 deletions

View file

@ -51,7 +51,7 @@ type NegationHandler struct {
//Handle process negation handler
func (nh NegationHandler) Handle(handler resourceElementHandler, resourceMap map[string]interface{}, originPattern interface{}, ac *common.AnchorKey) (string, error) {
anchorKey := commonAnchors.RemoveAnchor(nh.anchor)
anchorKey, _ := commonAnchors.RemoveAnchor(nh.anchor)
currentPath := nh.path + anchorKey + "/"
// if anchor is present in the resource then fail
if _, ok := resourceMap[anchorKey]; ok {
@ -80,7 +80,7 @@ type EqualityHandler struct {
//Handle processed condition anchor
func (eh EqualityHandler) Handle(handler resourceElementHandler, resourceMap map[string]interface{}, originPattern interface{}, ac *common.AnchorKey) (string, error) {
anchorKey := commonAnchors.RemoveAnchor(eh.anchor)
anchorKey, _ := commonAnchors.RemoveAnchor(eh.anchor)
currentPath := eh.path + anchorKey + "/"
// check if anchor is present in resource
if value, ok := resourceMap[anchorKey]; ok {
@ -144,7 +144,7 @@ type ConditionAnchorHandler struct {
//Handle processed condition anchor
func (ch ConditionAnchorHandler) Handle(handler resourceElementHandler, resourceMap map[string]interface{}, originPattern interface{}, ac *common.AnchorKey) (string, error) {
anchorKey := commonAnchors.RemoveAnchor(ch.anchor)
anchorKey, _ := commonAnchors.RemoveAnchor(ch.anchor)
currentPath := ch.path + anchorKey + "/"
// check if anchor is present in resource
if value, ok := resourceMap[anchorKey]; ok {
@ -178,7 +178,7 @@ type ExistenceHandler struct {
//Handle processes the existence anchor handler
func (eh ExistenceHandler) Handle(handler resourceElementHandler, resourceMap map[string]interface{}, originPattern interface{}, ac *common.AnchorKey) (string, error) {
// skip is used by existence anchor to not process further if condition is not satisfied
anchorKey := commonAnchors.RemoveAnchor(eh.anchor)
anchorKey, _ := commonAnchors.RemoveAnchor(eh.anchor)
currentPath := eh.path + anchorKey + "/"
// check if anchor is present in resource
if value, ok := resourceMap[anchorKey]; ok {

View file

@ -58,15 +58,22 @@ func IsExistenceAnchor(str string) bool {
return (str[:len(left)] == left && str[len(str)-len(right):] == right)
}
// RemoveAnchor remove anchor from the given key
func RemoveAnchor(key string) string {
// RemoveAnchor remove anchor from the given key. It returns
// the anchor-free tag value and the prefix of the anchor.
func RemoveAnchor(key string) (string, string) {
if IsConditionAnchor(key) {
return key[1 : len(key)-1]
return key[1 : len(key)-1], key[0:1]
}
if IsExistenceAnchor(key) || IsAddingAnchor(key) || IsEqualityAnchor(key) || IsNegationAnchor(key) {
return key[2 : len(key)-1]
return key[2 : len(key)-1], key[0:2]
}
return key
return key, ""
}
// AddAnchor adds an anchor with the supplied prefix.
// The suffix is assumed to be ")".
func AddAnchor(key, anchorPrefix string) string {
return anchorPrefix + key + ")"
}

View file

@ -104,7 +104,7 @@ func (ac *AnchorKey) CheckAnchorInResource(pattern interface{}, resource interfa
// Checks if anchor key has value in resource
func doesAnchorsKeyHasValue(key string, resource interface{}) bool {
akey := common.RemoveAnchor(key)
akey, _ := common.RemoveAnchor(key)
switch typed := resource.(type) {
case map[string]interface{}:
if _, ok := typed[akey]; ok {

View file

@ -61,13 +61,15 @@ func Mutate(policyContext *PolicyContext) (resp *response.EngineResponse) {
}
if err := MatchesResourceDescription(patchedResource, rule, policyContext.AdmissionInfo, excludeResource); err != nil {
logger.V(3).Info("resource not matched", "reason", err.Error())
logger.V(4).Info("rule not matched", "reason", err.Error())
continue
}
logger.V(3).Info("matched mutate rule")
// add configmap json data to context
if err := AddResourceToContext(logger, rule.Context, resCache, jsonContext); err != nil {
logger.V(4).Info("failed to add configmaps to context", "reason", err.Error())
logger.V(2).Info("failed to add configmaps to context", "reason", err.Error())
continue
}
@ -122,5 +124,5 @@ func endMutateResultResponse(logger logr.Logger, resp *response.EngineResponse,
}
resp.PolicyResponse.ProcessingTime = time.Since(startTime)
logger.V(4).Info("finished processing policy", "processingTime", resp.PolicyResponse.ProcessingTime.String(), "mutationRulesApplied", resp.PolicyResponse.RulesAppliedCount)
logger.V(5).Info("finished processing policy", "processingTime", resp.PolicyResponse.ProcessingTime.String(), "mutationRulesApplied", resp.PolicyResponse.RulesAppliedCount)
}

View file

@ -24,10 +24,10 @@ func Validate(policyContext *PolicyContext) (resp *response.EngineResponse) {
startTime := time.Now()
logger := buildLogger(policyContext)
logger.V(4).Info("start processing", "startTime", startTime)
logger.V(4).Info("start policy processing", "startTime", startTime)
defer func() {
buildResponse(logger, policyContext, resp, startTime)
logger.V(4).Info("finished processing", "processingTime", resp.PolicyResponse.ProcessingTime.String(), "validationRulesApplied", resp.PolicyResponse.RulesAppliedCount)
logger.V(4).Info("finished policy processing", "processingTime", resp.PolicyResponse.ProcessingTime.String(), "validationRulesApplied", resp.PolicyResponse.RulesAppliedCount)
}()
resp = validateResource(logger, policyContext)
@ -95,9 +95,11 @@ func validateResource(log logr.Logger, ctx *PolicyContext) *response.EngineRespo
continue
}
log = log.WithValues("rule", rule.Name)
// add configmap json data to context
if err := AddResourceToContext(log, rule.Context, ctx.ResourceCache, ctx.JSONContext); err != nil {
log.V(4).Info("cannot add configmaps to context", "reason", err.Error())
log.V(2).Info("failed to add configmaps to context", "reason", err.Error())
continue
}
@ -105,6 +107,8 @@ func validateResource(log logr.Logger, ctx *PolicyContext) *response.EngineRespo
continue
}
log.V(3).Info("matched validate rule")
// operate on the copy of the conditions, as we perform variable substitution
preconditionsCopy := copyConditions(rule.Conditions)

View file

@ -10,11 +10,13 @@ import (
// ReplaceInSelector replaces label selector keys and values containing
// wildcard characters with matching keys and values from the resource labels.
func ReplaceInSelector(labelSelector *metav1.LabelSelector, resourceLabels map[string]string) {
result := replaceWildcardsInMap(labelSelector.MatchLabels, resourceLabels)
result := replaceWildcardsInMapKeyValues(labelSelector.MatchLabels, resourceLabels)
labelSelector.MatchLabels = result
}
func replaceWildcardsInMap(patternMap map[string]string, resourceMap map[string]string) map[string]string {
// replaceWildcardsInMap will expand the "key" and "value" and will replace wildcard characters
// It also does not handle anchors as these are not expected in selectors
func replaceWildcardsInMapKeyValues(patternMap map[string]string, resourceMap map[string]string) map[string]string {
result := map[string]string{}
for k, v := range patternMap {
if hasWildcards(k) || hasWildcards(v) {
@ -61,7 +63,8 @@ func replaceWildCardChars(s string) string {
// ExpandInMetadata substitutes wildcard characters in map keys for metadata.labels and
// metadata.annotations that are present in a validation pattern. Values are not substituted
// here, as they are evaluated separately while processing the validation pattern.
// here, as they are evaluated separately while processing the validation pattern. Anchors
// on the tags (e.g. "=(kubernetes.io/*)" will be preserved when the values are expanded.
func ExpandInMetadata(patternMap, resourceMap map[string]interface{}) map[string]interface{} {
_, patternMetadata := getPatternValue("metadata", patternMap)
@ -90,7 +93,8 @@ func ExpandInMetadata(patternMap, resourceMap map[string]interface{}) map[string
func getPatternValue(tag string, pattern map[string]interface{}) (string, interface{}) {
for k, v := range pattern {
if commonAnchor.RemoveAnchor(k) == tag {
k2, _ := commonAnchor.RemoveAnchor(k)
if k2 == tag {
return k, v
}
}
@ -98,6 +102,7 @@ func getPatternValue(tag string, pattern map[string]interface{}) (string, interf
return "", nil
}
// expandWildcardsInTag
func expandWildcardsInTag(tag string, patternMetadata, resourceMetadata interface{}) (string, map[string]interface{}) {
patternKey, patternData := getValueAsStringMap(tag, patternMetadata)
if patternData == nil {
@ -109,17 +114,7 @@ func expandWildcardsInTag(tag string, patternMetadata, resourceMetadata interfac
return "", nil
}
results := map[string]interface{}{}
for k, v := range patternData {
if hasWildcards(k) {
anchorFreeKey := commonAnchor.RemoveAnchor(k)
matchK, _ := expandWildcards(anchorFreeKey, v, resourceData, false, false)
results[matchK] = v
} else {
results[k] = v
}
}
results := replaceWildcardsInMapKeys(patternData, resourceData)
return patternKey, results
}
@ -142,3 +137,24 @@ func getValueAsStringMap(key string, data interface{}) (string, map[string]strin
return patternKey, result
}
// replaceWildcardsInMapKeys will expand only the "key" and not replace wildcard characters in the key or values
// It also preserves anchors in keys
func replaceWildcardsInMapKeys(patternData, resourceData map[string]string) map[string]interface{} {
results := map[string]interface{}{}
for k, v := range patternData {
if hasWildcards(k) {
anchorFreeKey, anchorPrefix := commonAnchor.RemoveAnchor(k)
matchK, _ := expandWildcards(anchorFreeKey, v, resourceData, false, false)
if anchorPrefix != "" {
matchK = commonAnchor.AddAnchor(matchK, anchorPrefix)
}
results[matchK] = v
} else {
results[k] = v
}
}
return results
}

View file

@ -0,0 +1,27 @@
package wildcards
import (
"reflect"
"testing"
)
func TestExpandInMetadata(t *testing.T) {
//testExpand(t, map[string]string{"test/*": "*"}, map[string]string{},
// map[string]string{"test/0": "0"})
testExpand(t, map[string]string{"test/*": "*"}, map[string]string{"test/test": "test"},
map[string]interface{}{"test/test": "*"})
testExpand(t, map[string]string{"=(test/*)": "test"}, map[string]string{"test/test": "test"},
map[string]interface{}{"=(test/test)": "test"})
testExpand(t, map[string]string{"test/*": "*"}, map[string]string{"test/test1": "test1", "test/test2": "test2"},
map[string]interface{}{"test/test1": "*"})
}
func testExpand(t *testing.T, patternMap, resourceMap map[string]string, expectedMap map[string]interface{}) {
result := replaceWildcardsInMapKeys(patternMap, resourceMap)
if !reflect.DeepEqual(expectedMap, result) {
t.Errorf("expected %v but received %v", expectedMap, result)
}
}