@@ -14,6 +14,7 @@ import (
1414 "k8s.io/apimachinery/pkg/api/meta"
1515 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1616 "k8s.io/apimachinery/pkg/labels"
17+ "k8s.io/apimachinery/pkg/selection"
1718 "k8s.io/apimachinery/pkg/types"
1819 "k8s.io/utils/ptr"
1920 kclient "sigs.k8s.io/controller-runtime/pkg/client"
@@ -73,12 +74,107 @@ func ReconcileLeaseTimeFields(beginTime, endTime **metav1.Time, duration **metav
7374 return nil
7475}
7576
77+ // ParseLabelSelector parses a label selector string and converts it to metav1.LabelSelector.
78+ // This function supports the != operator by first parsing with labels.Parse() which supports
79+ // all label selector syntax including !=, then converting to metav1.LabelSelector format.
80+ func ParseLabelSelector (selectorStr string ) (* metav1.LabelSelector , error ) {
81+ // First, try to parse using labels.Parse() which supports != operator
82+ parsedSelector , err := labels .Parse (selectorStr )
83+ if err != nil {
84+ return nil , fmt .Errorf ("failed to parse label selector: %w" , err )
85+ }
86+
87+ // Extract requirements from the parsed selector
88+ requirements , selectable := parsedSelector .Requirements ()
89+ if ! selectable {
90+ return & metav1.LabelSelector {}, nil
91+ }
92+
93+ // Convert requirements to metav1.LabelSelector format
94+ matchLabels := make (map [string ]string )
95+ var matchExpressions []metav1.LabelSelectorRequirement
96+
97+ // Track NotEquals requirements by key so we can combine them into NotIn
98+ notEqualsByKey := make (map [string ][]string )
99+
100+ for _ , req := range requirements {
101+ key := req .Key ()
102+ operator := req .Operator ()
103+ values := req .ValuesUnsorted ()
104+
105+ switch operator {
106+ case selection .Equals :
107+ // For exact match with single value, use matchLabels
108+ if len (values ) == 1 {
109+ // Check if we already have an equality requirement for this key with a different value
110+ if existingValue , exists := matchLabels [key ]; exists && existingValue != values [0 ] {
111+ return nil , fmt .Errorf ("invalid selector: label %s cannot have multiple equality requirements with different values (%s and %s)" , key , existingValue , values [0 ])
112+ }
113+ matchLabels [key ] = values [0 ]
114+ } else {
115+ // Multiple values should use In operator
116+ matchExpressions = append (matchExpressions , metav1.LabelSelectorRequirement {
117+ Key : key ,
118+ Operator : metav1 .LabelSelectorOpIn ,
119+ Values : values ,
120+ })
121+ }
122+ case selection .NotEquals :
123+ // Accumulate != requirements for the same key to combine into NotIn
124+ if len (values ) != 1 {
125+ return nil , fmt .Errorf ("invalid selector: != operator requires exactly one value" )
126+ }
127+ notEqualsByKey [key ] = append (notEqualsByKey [key ], values [0 ])
128+ case selection .In :
129+ matchExpressions = append (matchExpressions , metav1.LabelSelectorRequirement {
130+ Key : key ,
131+ Operator : metav1 .LabelSelectorOpIn ,
132+ Values : values ,
133+ })
134+ case selection .NotIn :
135+ matchExpressions = append (matchExpressions , metav1.LabelSelectorRequirement {
136+ Key : key ,
137+ Operator : metav1 .LabelSelectorOpNotIn ,
138+ Values : values ,
139+ })
140+ case selection .Exists :
141+ matchExpressions = append (matchExpressions , metav1.LabelSelectorRequirement {
142+ Key : key ,
143+ Operator : metav1 .LabelSelectorOpExists ,
144+ Values : []string {},
145+ })
146+ case selection .DoesNotExist :
147+ matchExpressions = append (matchExpressions , metav1.LabelSelectorRequirement {
148+ Key : key ,
149+ Operator : metav1 .LabelSelectorOpDoesNotExist ,
150+ Values : []string {},
151+ })
152+ default :
153+ return nil , fmt .Errorf ("unsupported label selector operator: %v" , operator )
154+ }
155+ }
156+
157+ // Convert accumulated NotEquals requirements to NotIn expressions
158+ for key , vals := range notEqualsByKey {
159+ matchExpressions = append (matchExpressions , metav1.LabelSelectorRequirement {
160+ Key : key ,
161+ Operator : metav1 .LabelSelectorOpNotIn ,
162+ Values : vals ,
163+ })
164+ }
165+
166+ return & metav1.LabelSelector {
167+ MatchLabels : matchLabels ,
168+ MatchExpressions : matchExpressions ,
169+ }, nil
170+ }
171+
76172func LeaseFromProtobuf (
77173 req * cpb.Lease ,
78174 key types.NamespacedName ,
79175 clientRef corev1.LocalObjectReference ,
80176) (* Lease , error ) {
81- selector , err := metav1 . ParseToLabelSelector (req .Selector )
177+ selector , err := ParseLabelSelector (req .Selector )
82178 if err != nil {
83179 return nil , err
84180 }
0 commit comments