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:
commit
ff246a81a1
7 changed files with 87 additions and 31 deletions
|
@ -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 {
|
||||
|
|
|
@ -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 + ")"
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
27
pkg/engine/wildcards/wildcards_test.go
Normal file
27
pkg/engine/wildcards/wildcards_test.go
Normal 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)
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue