1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2025-04-08 10:04:25 +00:00

add annotation wildcard support

This commit is contained in:
Jim Bugwadia 2020-12-02 12:25:56 -08:00
parent 76b6974fc2
commit 59ba4fe3ac
3 changed files with 53 additions and 16 deletions

View file

@ -201,15 +201,15 @@ type ResourceDescription struct {
// +optional
Namespaces []string `json:"namespaces,omitempty" yaml:"namespaces,omitempty"`
// Annotations is a map of annotations (string key-value pairs). Annotation values
// supports wildcard characters "*" (matches zero or many characters) and
// "?" (at least one character).
// Annotations is a map of annotations (key-value pairs of type string). Annotation keys
// and values support the wildcard characters "*" (matches zero or many characters) and
// "?" (matches at least one character).
// +optional
Annotations map[string]string `json:"annotations,omitempty" yaml:"annotations,omitempty"`
// Selector is a label selector. Label keys and values in `matchLabels` support the wildcard
// characters `*` (matches zero or many characters) and `?` (matches one character).
// This feature allows writing label selectors like ["storage.k8s.io/*": "*"]. Note that
// Wildcards allows writing label selectors like ["storage.k8s.io/*": "*"]. Note that
// using ["*" : "*"] matches any key and value but does not match an empty label set.
// +optional
Selector *metav1.LabelSelector `json:"selector,omitempty" yaml:"selector,omitempty"`

View file

@ -54,19 +54,29 @@ func checkNameSpace(namespaces []string, resourceNameSpace string) bool {
}
func checkAnnotations(annotations map[string]string, resourceAnnotations map[string]string) bool {
if len(annotations) == 0 {
return true
}
for k, v := range annotations {
if len(resourceAnnotations) == 0 {
return false
match := false
for k1, v1 := range resourceAnnotations {
if wildcard.Match(k, k1) && wildcard.Match(v, v1) {
match = true
break
}
}
if resourceAnnotations[k] != v {
if match == false {
return false
}
}
return true
}
func checkSelector(labelSelector *metav1.LabelSelector, resourceLabels map[string]string) (bool, error) {
replaceWildCardsInSelector(labelSelector, resourceLabels)
replaceWildcardsInSelector(labelSelector, resourceLabels)
selector, err := metav1.LabelSelectorAsSelector(labelSelector)
if err != nil {
log.Log.Error(err, "failed to build label selector")
@ -80,13 +90,13 @@ func checkSelector(labelSelector *metav1.LabelSelector, resourceLabels map[strin
return false, nil
}
// replaceWildCardsInSelector replaces label selector keys containing
// wildcard characters with matching keys from the resource labels.
func replaceWildCardsInSelector(labelSelector *metav1.LabelSelector, resourceLabels map[string]string) {
// replaceWildcardsInSelector replaces label selector keys and values containing
// wildcard characters with matching keys and values from the resource labels.
func replaceWildcardsInSelector(labelSelector *metav1.LabelSelector, resourceLabels map[string]string) {
result := map[string]string{}
for k, v := range labelSelector.MatchLabels {
if containsWildCard(k) || containsWildCard(v) {
matchK, matchV := expandWildCards(k, v, resourceLabels)
if containsWildcards(k) || containsWildcards(v) {
matchK, matchV := expandWildcards(k, v, resourceLabels)
result[matchK] = matchV
} else {
result[k] = v
@ -96,11 +106,11 @@ func replaceWildCardsInSelector(labelSelector *metav1.LabelSelector, resourceLab
labelSelector.MatchLabels = result
}
func containsWildCard(s string) bool {
func containsWildcards(s string) bool {
return strings.Contains(s, "*") || strings.Contains(s, "?")
}
func expandWildCards(k, v string, labels map[string]string) (key string, val string) {
func expandWildcards(k, v string, labels map[string]string) (key string, val string) {
for k1, v1 := range labels {
if wildcard.Match(k, k1) {
if wildcard.Match(v, v1) {
@ -114,10 +124,11 @@ func expandWildCards(k, v string, labels map[string]string) (key string, val str
return k, v
}
// replaceWildCardChars will replace '*' and '?' characters which are not
// supported by Kubernetes with a '0'.
func replaceWildCardChars(s string) string {
s = strings.Replace(s, "*", "0", -1)
s = strings.Replace(s, "?", "0", -1)
return s
}

View file

@ -526,3 +526,29 @@ func testSelector(t *testing.T, s *metav1.LabelSelector, l map[string]string, ma
t.Errorf("select %v -> labels %v: expected %v received %v", s.MatchLabels, l, match, res)
}
}
func TestWildCardAnnotation(t *testing.T) {
// test single annotation values
testAnnotationMatch(t, map[string]string{}, map[string]string{}, true)
testAnnotationMatch(t, map[string]string{"test/*": "*"}, map[string]string{}, false)
testAnnotationMatch(t, map[string]string{"test/*": "*"}, map[string]string{"tes1/test": "*"}, false)
testAnnotationMatch(t, map[string]string{"test/*": "*"}, map[string]string{"test/test": "*"}, true)
testAnnotationMatch(t, map[string]string{"test/*": "*"}, map[string]string{"test/bar": "foo"}, true)
testAnnotationMatch(t, map[string]string{"test/b*": "*"}, map[string]string{"test/bar": "foo"}, true)
// test multiple annotation values
testAnnotationMatch(t, map[string]string{"test/b*": "*", "test2/*": "*"},
map[string]string{"test/bar": "foo"}, false)
testAnnotationMatch(t, map[string]string{"test/b*": "*", "test2/*": "*"},
map[string]string{"test/bar": "foo", "test2/123": "bar"}, true)
testAnnotationMatch(t, map[string]string{"test/b*": "*", "test2/*": "*"},
map[string]string{"test/bar": "foo", "test2/123": "bar", "test3/123": "bar2"}, true)
}
func testAnnotationMatch(t *testing.T, policy map[string]string, resource map[string]string, match bool) {
res := checkAnnotations(policy, resource)
if res != match {
t.Errorf("annotations %v -> labels %v: expected %v received %v", policy, resource, match, res)
}
}