github.com/argoproj/argo-cd/v2@v2.10.9/applicationset/utils/selector.go (about) 1 package utils 2 3 import ( 4 "fmt" 5 v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 6 "k8s.io/apimachinery/pkg/labels" 7 "k8s.io/apimachinery/pkg/selection" 8 "k8s.io/apimachinery/pkg/util/validation" 9 "k8s.io/apimachinery/pkg/util/validation/field" 10 "k8s.io/klog/v2" 11 "sort" 12 "strconv" 13 "strings" 14 ) 15 16 var ( 17 unaryOperators = []string{ 18 string(selection.Exists), string(selection.DoesNotExist), 19 } 20 binaryOperators = []string{ 21 string(selection.In), string(selection.NotIn), 22 string(selection.Equals), string(selection.DoubleEquals), string(selection.NotEquals), 23 string(selection.GreaterThan), string(selection.LessThan), 24 } 25 validRequirementOperators = append(binaryOperators, unaryOperators...) 26 ) 27 28 // Selector represents a label selector. 29 type Selector interface { 30 // Matches returns true if this selector matches the given set of labels. 31 Matches(labels.Labels) bool 32 33 // Add adds requirements to the Selector 34 Add(r ...Requirement) Selector 35 } 36 37 type internalSelector []Requirement 38 39 // ByKey sorts requirements by key to obtain deterministic parser 40 type ByKey []Requirement 41 42 func (a ByKey) Len() int { return len(a) } 43 44 func (a ByKey) Swap(i, j int) { a[i], a[j] = a[j], a[i] } 45 46 func (a ByKey) Less(i, j int) bool { return a[i].key < a[j].key } 47 48 // Matches for a internalSelector returns true if all 49 // its Requirements match the input Labels. If any 50 // Requirement does not match, false is returned. 51 func (s internalSelector) Matches(l labels.Labels) bool { 52 for ix := range s { 53 if matches := s[ix].Matches(l); !matches { 54 return false 55 } 56 } 57 return true 58 } 59 60 // Add adds requirements to the selector. It copies the current selector returning a new one 61 func (s internalSelector) Add(reqs ...Requirement) Selector { 62 ret := make(internalSelector, 0, len(s)+len(reqs)) 63 ret = append(ret, s...) 64 ret = append(ret, reqs...) 65 sort.Sort(ByKey(ret)) 66 return ret 67 } 68 69 type nothingSelector struct{} 70 71 func (n nothingSelector) Matches(l labels.Labels) bool { 72 return false 73 } 74 75 func (n nothingSelector) Add(r ...Requirement) Selector { 76 return n 77 } 78 79 // Nothing returns a selector that matches no labels 80 func nothing() Selector { 81 return nothingSelector{} 82 } 83 84 // Everything returns a selector that matches all labels. 85 func everything() Selector { 86 return internalSelector{} 87 } 88 89 // LabelSelectorAsSelector converts the LabelSelector api type into a struct that implements 90 // labels.Selector 91 // Note: This function should be kept in sync with the selector methods in pkg/labels/selector.go 92 func LabelSelectorAsSelector(ps *v1.LabelSelector) (Selector, error) { 93 if ps == nil { 94 return nothing(), nil 95 } 96 if len(ps.MatchLabels)+len(ps.MatchExpressions) == 0 { 97 return everything(), nil 98 } 99 requirements := make([]Requirement, 0, len(ps.MatchLabels)+len(ps.MatchExpressions)) 100 for k, v := range ps.MatchLabels { 101 r, err := newRequirement(k, selection.Equals, []string{v}) 102 if err != nil { 103 return nil, err 104 } 105 requirements = append(requirements, *r) 106 } 107 for _, expr := range ps.MatchExpressions { 108 var op selection.Operator 109 switch expr.Operator { 110 case v1.LabelSelectorOpIn: 111 op = selection.In 112 case v1.LabelSelectorOpNotIn: 113 op = selection.NotIn 114 case v1.LabelSelectorOpExists: 115 op = selection.Exists 116 case v1.LabelSelectorOpDoesNotExist: 117 op = selection.DoesNotExist 118 default: 119 return nil, fmt.Errorf("%q is not a valid pod selector operator", expr.Operator) 120 } 121 r, err := newRequirement(expr.Key, op, append([]string(nil), expr.Values...)) 122 if err != nil { 123 return nil, err 124 } 125 requirements = append(requirements, *r) 126 } 127 selector := newSelector() 128 selector = selector.Add(requirements...) 129 return selector, nil 130 } 131 132 // NewSelector returns a nil selector 133 func newSelector() Selector { 134 return internalSelector(nil) 135 } 136 137 func validateLabelKey(k string, path *field.Path) *field.Error { 138 if errs := validation.IsQualifiedName(k); len(errs) != 0 { 139 return field.Invalid(path, k, strings.Join(errs, "; ")) 140 } 141 return nil 142 } 143 144 // NewRequirement is the constructor for a Requirement. 145 // If any of these rules is violated, an error is returned: 146 // (1) The operator can only be In, NotIn, Equals, DoubleEquals, Gt, Lt, NotEquals, Exists, or DoesNotExist. 147 // (2) If the operator is In or NotIn, the values set must be non-empty. 148 // (3) If the operator is Equals, DoubleEquals, or NotEquals, the values set must contain one value. 149 // (4) If the operator is Exists or DoesNotExist, the value set must be empty. 150 // (5) If the operator is Gt or Lt, the values set must contain only one value, which will be interpreted as an integer. 151 // (6) The key is invalid due to its length, or sequence 152 // 153 // of characters. See validateLabelKey for more details. 154 // 155 // The empty string is a valid value in the input values set. 156 // Returned error, if not nil, is guaranteed to be an aggregated field.ErrorList 157 func newRequirement(key string, op selection.Operator, vals []string, opts ...field.PathOption) (*Requirement, error) { 158 var allErrs field.ErrorList 159 path := field.ToPath(opts...) 160 if err := validateLabelKey(key, path.Child("key")); err != nil { 161 allErrs = append(allErrs, err) 162 } 163 164 valuePath := path.Child("values") 165 switch op { 166 case selection.In, selection.NotIn: 167 if len(vals) == 0 { 168 allErrs = append(allErrs, field.Invalid(valuePath, vals, "for 'in', 'notin' operators, values set can't be empty")) 169 } 170 case selection.Equals, selection.DoubleEquals, selection.NotEquals: 171 if len(vals) != 1 { 172 allErrs = append(allErrs, field.Invalid(valuePath, vals, "exact-match compatibility requires one single value")) 173 } 174 case selection.Exists, selection.DoesNotExist: 175 if len(vals) != 0 { 176 allErrs = append(allErrs, field.Invalid(valuePath, vals, "values set must be empty for exists and does not exist")) 177 } 178 case selection.GreaterThan, selection.LessThan: 179 if len(vals) != 1 { 180 allErrs = append(allErrs, field.Invalid(valuePath, vals, "for 'Gt', 'Lt' operators, exactly one value is required")) 181 } 182 for i := range vals { 183 if _, err := strconv.ParseInt(vals[i], 10, 64); err != nil { 184 allErrs = append(allErrs, field.Invalid(valuePath.Index(i), vals[i], "for 'Gt', 'Lt' operators, the value must be an integer")) 185 } 186 } 187 default: 188 allErrs = append(allErrs, field.NotSupported(path.Child("operator"), op, validRequirementOperators)) 189 } 190 191 return &Requirement{key: key, operator: op, strValues: vals}, allErrs.ToAggregate() 192 } 193 194 // Requirement contains values, a key, and an operator that relates the key and values. 195 // The zero value of Requirement is invalid. 196 // Requirement implements both set based match and exact match 197 // Requirement should be initialized via NewRequirement constructor for creating a valid Requirement. 198 // +k8s:deepcopy-gen=true 199 type Requirement struct { 200 key string 201 operator selection.Operator 202 // In the majority of cases we have at most one value here. 203 // It is generally faster to operate on a single-element slice 204 // than on a single-element map, so we have a slice here. 205 strValues []string 206 } 207 208 func (r *Requirement) hasValue(value string) bool { 209 for i := range r.strValues { 210 if r.strValues[i] == value { 211 return true 212 } 213 } 214 return false 215 } 216 217 func (r *Requirement) Matches(ls labels.Labels) bool { 218 switch r.operator { 219 case selection.In, selection.Equals, selection.DoubleEquals: 220 if !ls.Has(r.key) { 221 return false 222 } 223 return r.hasValue(ls.Get(r.key)) 224 case selection.NotIn, selection.NotEquals: 225 if !ls.Has(r.key) { 226 return true 227 } 228 return !r.hasValue(ls.Get(r.key)) 229 case selection.Exists: 230 return ls.Has(r.key) 231 case selection.DoesNotExist: 232 return !ls.Has(r.key) 233 case selection.GreaterThan, selection.LessThan: 234 if !ls.Has(r.key) { 235 return false 236 } 237 lsValue, err := strconv.ParseInt(ls.Get(r.key), 10, 64) 238 if err != nil { 239 klog.V(10).Infof("ParseInt failed for value %+v in label %+v, %+v", ls.Get(r.key), ls, err) 240 return false 241 } 242 243 // There should be only one strValue in r.strValues, and can be converted to an integer. 244 if len(r.strValues) != 1 { 245 klog.V(10).Infof("Invalid values count %+v of requirement %#v, for 'Gt', 'Lt' operators, exactly one value is required", len(r.strValues), r) 246 return false 247 } 248 249 var rValue int64 250 for i := range r.strValues { 251 rValue, err = strconv.ParseInt(r.strValues[i], 10, 64) 252 if err != nil { 253 klog.V(10).Infof("ParseInt failed for value %+v in requirement %#v, for 'Gt', 'Lt' operators, the value must be an integer", r.strValues[i], r) 254 return false 255 } 256 } 257 return (r.operator == selection.GreaterThan && lsValue > rValue) || (r.operator == selection.LessThan && lsValue < rValue) 258 default: 259 return false 260 } 261 }