Skip to content

Commit

Permalink
handle anchors for wildcard annotations
Browse files Browse the repository at this point in the history
  • Loading branch information
JimBugwadia committed Jan 7, 2021
1 parent fab777c commit 3a4592c
Show file tree
Hide file tree
Showing 7 changed files with 87 additions and 31 deletions.
8 changes: 4 additions & 4 deletions pkg/engine/anchor/anchor.go
Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down
17 changes: 12 additions & 5 deletions pkg/engine/anchor/common/common.go
Expand Up @@ -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 + ")"
}
2 changes: 1 addition & 1 deletion pkg/engine/common/anchorKey.go
Expand Up @@ -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 {
Expand Down
8 changes: 5 additions & 3 deletions pkg/engine/mutation.go
Expand Up @@ -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
}

Expand Down Expand Up @@ -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)
}
10 changes: 7 additions & 3 deletions pkg/engine/validation.go
Expand Up @@ -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)
Expand Down Expand Up @@ -95,16 +95,20 @@ 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
}

if !matches(log, rule, ctx) {
continue
}

log.V(3).Info("matched validate rule")

// operate on the copy of the conditions, as we perform variable substitution
preconditionsCopy := copyConditions(rule.Conditions)

Expand Down
46 changes: 31 additions & 15 deletions pkg/engine/wildcards/wildcards.go
Expand Up @@ -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) {
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -90,14 +93,16 @@ 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
}
}

return "", nil
}

// expandWildcardsInTag
func expandWildcardsInTag(tag string, patternMetadata, resourceMetadata interface{}) (string, map[string]interface{}) {
patternKey, patternData := getValueAsStringMap(tag, patternMetadata)
if patternData == nil {
Expand All @@ -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
}

Expand All @@ -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 changes: 27 additions & 0 deletions pkg/engine/wildcards/wildcards_test.go
@@ -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)
}
}

0 comments on commit 3a4592c

Please sign in to comment.