-
Notifications
You must be signed in to change notification settings - Fork 804
/
wildcards.go
161 lines (132 loc) · 4.52 KB
/
wildcards.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
package wildcards
import (
"strings"
commonAnchor "github.com/kyverno/kyverno/pkg/engine/anchor/common"
"github.com/minio/pkg/wildcard"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// 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 := replaceWildcardsInMapKeyValues(labelSelector.MatchLabels, resourceLabels)
labelSelector.MatchLabels = result
}
// 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) {
matchK, matchV := expandWildcards(k, v, resourceMap, true, true)
result[matchK] = matchV
} else {
result[k] = v
}
}
return result
}
func hasWildcards(s string) bool {
return strings.Contains(s, "*") || strings.Contains(s, "?")
}
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 !matchValue {
return k1, v1
} else if wildcard.Match(v, v1) {
return k1, v1
}
}
}
if replace {
k = replaceWildCardChars(k)
v = replaceWildCardChars(v)
}
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
}
// 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. 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)
if patternMetadata == nil {
return patternMap
}
resourceMetadata := resourceMap["metadata"]
if resourceMetadata == nil {
return patternMap
}
metadata := patternMetadata.(map[string]interface{})
labelsKey, labels := expandWildcardsInTag("labels", patternMetadata, resourceMetadata)
if labels != nil {
metadata[labelsKey] = labels
}
annotationsKey, annotations := expandWildcardsInTag("annotations", patternMetadata, resourceMetadata)
if annotations != nil {
metadata[annotationsKey] = annotations
}
return patternMap
}
func getPatternValue(tag string, pattern map[string]interface{}) (string, interface{}) {
for k, v := range pattern {
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 {
return "", nil
}
_, resourceData := getValueAsStringMap(tag, resourceMetadata)
if resourceData == nil {
return "", nil
}
results := replaceWildcardsInMapKeys(patternData, resourceData)
return patternKey, results
}
func getValueAsStringMap(key string, data interface{}) (string, map[string]string) {
if data == nil {
return "", nil
}
dataMap := data.(map[string]interface{})
patternKey, val := getPatternValue(key, dataMap)
if val == nil {
return "", nil
}
result := map[string]string{}
for k, v := range val.(map[string]interface{}) {
result[k] = v.(string)
}
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
}