diff --git a/staging/src/k8s.io/apimachinery/pkg/labels/selector.go b/staging/src/k8s.io/apimachinery/pkg/labels/selector.go index c2c74e005f1..2f8e1e2b0c4 100644 --- a/staging/src/k8s.io/apimachinery/pkg/labels/selector.go +++ b/staging/src/k8s.io/apimachinery/pkg/labels/selector.go @@ -54,6 +54,11 @@ type Selector interface { // Make a deep copy of the selector. DeepCopySelector() Selector + + // RequiresExactMatch allows a caller to introspect whether a given selector + // requires a single specific label to be set, and if so returns the value it + // requires. + RequiresExactMatch(label string) (value string, found bool) } // Everything returns a selector that matches all labels. @@ -63,12 +68,13 @@ func Everything() Selector { type nothingSelector struct{} -func (n nothingSelector) Matches(_ Labels) bool { return false } -func (n nothingSelector) Empty() bool { return false } -func (n nothingSelector) String() string { return "" } -func (n nothingSelector) Add(_ ...Requirement) Selector { return n } -func (n nothingSelector) Requirements() (Requirements, bool) { return nil, false } -func (n nothingSelector) DeepCopySelector() Selector { return n } +func (n nothingSelector) Matches(_ Labels) bool { return false } +func (n nothingSelector) Empty() bool { return false } +func (n nothingSelector) String() string { return "" } +func (n nothingSelector) Add(_ ...Requirement) Selector { return n } +func (n nothingSelector) Requirements() (Requirements, bool) { return nil, false } +func (n nothingSelector) DeepCopySelector() Selector { return n } +func (n nothingSelector) RequiresExactMatch(label string) (value string, found bool) { return "", false } // Nothing returns a selector that matches no labels func Nothing() Selector { @@ -358,6 +364,23 @@ func (lsel internalSelector) String() string { return strings.Join(reqs, ",") } +// RequiresExactMatch introspect whether a given selector requires a single specific field +// to be set, and if so returns the value it requires. +func (lsel internalSelector) RequiresExactMatch(label string) (value string, found bool) { + for ix := range lsel { + if lsel[ix].key == label { + switch lsel[ix].operator { + case selection.Equals, selection.DoubleEquals, selection.In: + if len(lsel[ix].strValues) == 1 { + return lsel[ix].strValues[0], true + } + } + return "", false + } + } + return "", false +} + // Token represents constant definition for lexer token type Token int diff --git a/staging/src/k8s.io/apimachinery/pkg/labels/selector_test.go b/staging/src/k8s.io/apimachinery/pkg/labels/selector_test.go index 64b121fa804..e93055094c6 100644 --- a/staging/src/k8s.io/apimachinery/pkg/labels/selector_test.go +++ b/staging/src/k8s.io/apimachinery/pkg/labels/selector_test.go @@ -630,3 +630,81 @@ func BenchmarkSelectorFromValidatedSet(b *testing.B) { } } } + +func TestRequiresExactMatch(t *testing.T) { + testCases := []struct { + name string + sel Selector + label string + expectedFound bool + expectedValue string + }{ + { + name: "keyInOperatorExactMatch", + sel: internalSelector{Requirement{"key", selection.In, []string{"value"}}}, + label: "key", + expectedFound: true, + expectedValue: "value", + }, + { + name: "keyInOperatorNotExactMatch", + sel: internalSelector{Requirement{"key", selection.In, []string{"value", "value2"}}}, + label: "key", + expectedFound: false, + expectedValue: "", + }, + { + name: "keyInOperatorNotExactMatch", + sel: internalSelector{ + Requirement{"key", selection.In, []string{"value", "value1"}}, + Requirement{"key2", selection.In, []string{"value2"}}, + }, + label: "key2", + expectedFound: true, + expectedValue: "value2", + }, + { + name: "keyEqualOperatorExactMatch", + sel: internalSelector{Requirement{"key", selection.Equals, []string{"value"}}}, + label: "key", + expectedFound: true, + expectedValue: "value", + }, + { + name: "keyDoubleEqualOperatorExactMatch", + sel: internalSelector{Requirement{"key", selection.DoubleEquals, []string{"value"}}}, + label: "key", + expectedFound: true, + expectedValue: "value", + }, + { + name: "keyNotEqualOperatorExactMatch", + sel: internalSelector{Requirement{"key", selection.NotEquals, []string{"value"}}}, + label: "key", + expectedFound: false, + expectedValue: "", + }, + { + name: "keyEqualOperatorExactMatchFirst", + sel: internalSelector{ + Requirement{"key", selection.In, []string{"value"}}, + Requirement{"key2", selection.In, []string{"value2"}}, + }, + label: "key", + expectedFound: true, + expectedValue: "value", + }, + } + for _, ts := range testCases { + t.Run(ts.name, func(t *testing.T) { + value, found := ts.sel.RequiresExactMatch(ts.label) + if found != ts.expectedFound { + t.Errorf("Expected match %v, found %v", ts.expectedFound, found) + } + if found && value != ts.expectedValue { + t.Errorf("Expected value %v, found %v", ts.expectedValue, value) + } + + }) + } +}