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