Skip to content

Commit

Permalink
Merge pull request kyverno#1361 from kyverno/1332_wildcard_keys_in_pa…
Browse files Browse the repository at this point in the history
…tterns

1332 wildcard keys in patterns
  • Loading branch information
JimBugwadia authored Dec 5, 2020
2 parents 13a9a47 + 1c2262b commit c953398
Show file tree
Hide file tree
Showing 5 changed files with 69 additions and 46 deletions.
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@ github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logr/logr v0.1.0 h1:M1Tv3VzNlEHg6uyACnRdtrploV2P7wZqH8BoQMtz0cg=
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
github.com/go-logr/zapr v0.1.0 h1:h+WVe9j6HAA01niTJPA/kKH0i7e0rLZBCwauQFcRE54=
github.com/go-logr/zapr v0.1.0/go.mod h1:tabnROwaDl0UNxkVeFRbY8bwB37GwRv0P8lg6aAiEnk=
github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8=
github.com/go-ole/go-ole v1.2.4 h1:nNBDSCOigTSiarFpYE9J/KtEA1IOW4CNeqT9TQDqCxI=
Expand Down Expand Up @@ -685,10 +686,13 @@ go.starlark.net v0.0.0-20190528202925-30ae18b8564f/go.mod h1:c1/X6cHgvdXj6pUlmWK
go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5/go.mod h1:nmDLcffg48OtT/PSW0Hg7FvpRQsQh5OSqIylirxKC7o=
go.uber.org/atomic v0.0.0-20181018215023-8dc6146f7569/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/multierr v0.0.0-20180122172545-ddea229ff1df/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/zap v0.0.0-20180814183419-67bc79d13d15/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181106171534-e4dc69e5b2fd/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
Expand Down
2 changes: 1 addition & 1 deletion pkg/engine/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ func doesResourceMatchConditionBlock(conditionBlock kyverno.ResourceDescription,
var errs []error
if len(conditionBlock.Kinds) > 0 {
if !checkKind(conditionBlock.Kinds, resource.GetKind()) {
errs = append(errs, fmt.Errorf("kind does not match"))
errs = append(errs, fmt.Errorf("kind does not match %v", conditionBlock.Kinds))
}
}
if conditionBlock.Name != "" {
Expand Down
2 changes: 0 additions & 2 deletions pkg/engine/validate/validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,7 @@ func validateResourceElement(log logr.Logger, resourceElement, patternElement, o
// For each element of the map we must detect the type again, so we pass these elements to validateResourceElement
func validateMap(log logr.Logger, resourceMap, patternMap map[string]interface{}, origPattern interface{}, path string, ac *common.AnchorKey) (string, error) {

//
patternMap = wildcards.ExpandInMetadata(patternMap, resourceMap)

// check if there is anchor in pattern
// Phase 1 : Evaluate all the anchors
// Phase 2 : Evaluate non-anchors
Expand Down
48 changes: 28 additions & 20 deletions pkg/engine/validate/validate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1360,53 +1360,61 @@ func TestValidateMapElement_OneElementInArrayNotPass(t *testing.T) {
func TestValidateMapWildcardKeys(t *testing.T) {
pattern := []byte(`{"metadata" : {"annotations": {"test/*": "value1"}}}`)
resource := []byte(`{"metadata" : {"annotations": {"test/bar": "value1"}}}`)
testValidationPattern(t, pattern, resource, "", true)
testValidationPattern(t, "1", pattern, resource, "", true)

pattern = []byte(`{"metadata" : {"annotations": {"test/b??": "v*"}}}`)
resource = []byte(`{"metadata" : {"annotations": {"test/bar": "value1"}}}`)
testValidationPattern(t, pattern, resource, "", true)
testValidationPattern(t, "2", pattern, resource, "", true)

pattern = []byte(`{}`)
resource = []byte(`{"metadata" : {"annotations": {"test/bar": "value1"}}}`)
testValidationPattern(t, pattern, resource, "", true)
testValidationPattern(t, "3", pattern, resource, "", true)

pattern = []byte(`{"metadata" : {"annotations": {"test/b??": "v*"}}}`)
resource = []byte(`{"metadata" : {"labels": {"test/bar": "value1"}}}`)
testValidationPattern(t, pattern, resource, "/metadata/annotations/", false)
testValidationPattern(t, "4", pattern, resource, "/metadata/annotations/", false)

pattern = []byte(`{"metadata" : {"labels": {"*/test": "foo"}}}`)
resource = []byte(`{"metadata" : {"labels": {"foo/test": "foo"}}}`)
testValidationPattern(t, pattern, resource, "", true)
testValidationPattern(t, "5", pattern, resource, "", true)

pattern = []byte(`{"metadata" : {"labels": {"foo/123*": "bar"}}}`)
resource = []byte(`{"metadata" : {"labels": {"foo/12?": "bar", "foo/123": "bar"}}}`)
testValidationPattern(t, pattern, resource, "", true)
pattern = []byte(`{"metadata" : {"labels": {"foo/a*": "bar"}}}`)
resource = []byte(`{"metadata" : {"labels": {"foo/aa?": "bar", "foo/789": "bar"}}}`)
testValidationPattern(t, "6", pattern, resource, "", true)

pattern = []byte(`{"metadata" : {"labels": {"foo/123*": "bar"}}}`)
resource = []byte(`{"metadata" : {"labels": {"foo/12?": "bar", "foo/123": "bar2"}}}`)
testValidationPattern(t, pattern, resource, "/metadata/labels/foo/123*/", false)
pattern = []byte(`{"metadata" : {"labels": {"foo/ABC*": "bar"}}}`)
resource = []byte(`{"metadata" : {"labels": {"foo/AB?": "bar", "foo/ABC": "bar2"}}}`)
testValidationPattern(t, "7", pattern, resource, "/metadata/labels/foo/ABC/", false)

pattern = []byte(`{"metadata" : {"labels": {"foo/1*": "bar", "foo/4*": "bar2"}}}`)
resource = []byte(`{"metadata" : {"labels": {"foo/123": "bar", "foo/456": "bar2"}}}`)
testValidationPattern(t, pattern, resource, "", true)
pattern = []byte(`{"=(metadata)" : {"=(labels)": {"foo/P*": "bar", "foo/Q*": "bar2"}}}`)
resource = []byte(`{"metadata" : {"labels": {"foo/PQR": "bar", "foo/QR": "bar2"}}}`)
testValidationPattern(t, "8", pattern, resource, "", true)

pattern = []byte(`{"metadata" : {"labels": {"foo/1*": "bar", "foo/4*": "bar2"}}}`)
pattern = []byte(`{"metadata" : {"labels": {"foo/1*": "bar"}}}`)
resource = []byte(`{"metadata" : {"labels": {"foo/123": "bar222"}}}`)
testValidationPattern(t, "9", pattern, resource, "/metadata/labels/foo/123/", false)

pattern = []byte(`{"metadata" : {"labels": {"foo/X*": "bar", "foo/A*": "bar2"}}}`)
resource = []byte(`{"metadata" : {"labels": {"foo/XYZ": "bar"}}}`)
testValidationPattern(t, "10", pattern, resource, "/metadata/labels/foo/A*/", false)

pattern = []byte(`{"=(metadata)" : {"=(labels)": {"foo/1*": "bar", "foo/4*": "bar2"}}}`)
resource = []byte(`{"metadata" : {"labels": {"foo/123": "bar"}}}`)
testValidationPattern(t, pattern, resource, "/metadata/labels/foo/4*/", false)
testValidationPattern(t, "11", pattern, resource, "/metadata/labels/foo/4*/", false)
}

func testValidationPattern(t *testing.T, patternBytes []byte, resourceBytes []byte, path string, nilErr bool) {
func testValidationPattern(t *testing.T, num string, patternBytes []byte, resourceBytes []byte, path string, nilErr bool) {
var pattern, resource interface{}
err := json.Unmarshal(patternBytes, &pattern)
assert.NilError(t, err)
err = json.Unmarshal(resourceBytes, &resource)
assert.NilError(t, err)

p, err := validateResourceElement(log.Log, resource, pattern, pattern, "/", common.NewAnchorMap())
assert.Equal(t, p, path)
assert.Equal(t, p, path, num)
if nilErr {
assert.NilError(t, err)
assert.NilError(t, err, num)
} else {
assert.Assert(t, err != nil)
assert.Assert(t, err != nil, num)
}
}
59 changes: 36 additions & 23 deletions pkg/engine/wildcards/wildcards.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ func replaceWildcardsInMap(patternMap map[string]string, resourceMap map[string]
result := map[string]string{}
for k, v := range patternMap {
if hasWildcards(k) || hasWildcards(v) {
matchK, matchV := expandWildcards(k, v, resourceMap, true)
matchK, matchV := expandWildcards(k, v, resourceMap, true, true)
result[matchK] = matchV
} else {
result[k] = v
Expand All @@ -32,10 +32,12 @@ func hasWildcards(s string) bool {
return strings.Contains(s, "*") || strings.Contains(s, "?")
}

func expandWildcards(k, v string, resourceMap map[string]string, replace bool) (key string, val string) {
func expandWildcards(k, v string, resourceMap map[string]string, matchValue, replace bool) (key string, val string) {
for k1, v1 := range resourceMap {
if wildcard.Match(k, k1) {
if wildcard.Match(v, v1) {
if !matchValue {
return k1, v1
} else if wildcard.Match(v, v1) {
return k1, v1
}
}
Expand All @@ -62,7 +64,7 @@ func replaceWildCardChars(s string) string {
// here, as they are evaluated separately while processing the validation pattern.
func ExpandInMetadata(patternMap, resourceMap map[string]interface{}) map[string]interface{} {

patternMetadata := patternMap["metadata"]
_, patternMetadata := getPatternValue("metadata", patternMap)
if patternMetadata == nil {
return patternMap
}
Expand All @@ -73,59 +75,70 @@ func ExpandInMetadata(patternMap, resourceMap map[string]interface{}) map[string
}

metadata := patternMetadata.(map[string]interface{})
labels := expandWildcardsInTag("labels", patternMetadata, resourceMetadata)
labelsKey, labels := expandWildcardsInTag("labels", patternMetadata, resourceMetadata)
if labels != nil {
metadata["labels"] = labels
metadata[labelsKey] = labels
}

annotations := expandWildcardsInTag("annotations", patternMetadata, resourceMetadata)
annotationsKey, annotations := expandWildcardsInTag("annotations", patternMetadata, resourceMetadata)
if annotations != nil {
metadata["annotations"] = annotations
metadata[annotationsKey] = annotations
}

return patternMap
}

func expandWildcardsInTag(tag string, patternMetadata, resourceMetadata interface{}) map[string]interface{} {
patternData := getValueAsStringMap(tag, patternMetadata)
func getPatternValue(tag string, pattern map[string]interface{}) (string, interface{}) {
for k, v := range pattern {
if commonAnchor.RemoveAnchor(k) == tag {
return k, v
}
}

return "", nil
}

func expandWildcardsInTag(tag string, patternMetadata, resourceMetadata interface{}) (string, map[string]interface{}) {
patternKey, patternData := getValueAsStringMap(tag, patternMetadata)
if patternData == nil {
return nil
return "", nil
}

resourceData := getValueAsStringMap(tag, resourceMetadata)
_, resourceData := getValueAsStringMap(tag, resourceMetadata)
if resourceData == nil {
return nil
return "", nil
}

results := map[string]interface{}{}
for k, v := range patternData {
if hasWildcards(k) {
newKey := commonAnchor.RemoveAnchor(k)
matchK, _ := expandWildcards(newKey, v, resourceData, false)
matchK = strings.Replace(k, newKey, matchK, 1)
anchorFreeKey := commonAnchor.RemoveAnchor(k)
matchK, _ := expandWildcards(anchorFreeKey, v, resourceData, false, false)
results[matchK] = v
} else {
results[k] = v
}
}

return results
return patternKey, results
}

func getValueAsStringMap(key string, dataMap interface{}) map[string]string {
if dataMap == nil {
return nil
func getValueAsStringMap(key string, data interface{}) (string, map[string]string) {
if data == nil {
return "", nil
}

val := dataMap.(map[string]interface{})[key]
dataMap := data.(map[string]interface{})
patternKey, val := getPatternValue(key, dataMap)

if val == nil {
return nil
return "", nil
}

result := map[string]string{}
for k, v := range val.(map[string]interface{}) {
result[k] = v.(string)
}

return result
return patternKey, result
}

0 comments on commit c953398

Please sign in to comment.