diff --git a/pkg/labels/selector.go b/pkg/labels/selector.go index 0845d82305f..c35da49ac3a 100644 --- a/pkg/labels/selector.go +++ b/pkg/labels/selector.go @@ -20,6 +20,8 @@ import ( "fmt" "sort" "strings" + + "github.com/GoogleCloudPlatform/kubernetes/pkg/util" ) // Selector represents a label selector. @@ -105,6 +107,48 @@ func (t andTerm) String() string { return strings.Join(terms, ",") } +// Operator represents a key's relationship +// to a set of values in a Requirement. +// TODO: Should also represent key's existence. +type Operator int + +const ( + IN Operator = iota + 1 + NOT_IN +) + +// LabelSelector only not named 'Selector' due +// to name conflict until Selector is deprecated. +type LabelSelector struct { + Requirements []Requirement +} + +type Requirement struct { + key string + operator Operator + strValues util.StringSet +} + +func (r *Requirement) Matches(ls Labels) bool { + switch r.operator { + case IN: + return r.strValues.Has(ls.Get(r.key)) + case NOT_IN: + return !r.strValues.Has(ls.Get(r.key)) + default: + return false + } +} + +func (sg *LabelSelector) Matches(ls Labels) bool { + for _, req := range sg.Requirements { + if !req.Matches(ls) { + return false + } + } + return true +} + func try(selectorPiece, op string) (lhs, rhs string, ok bool) { pieces := strings.Split(selectorPiece, op) if len(pieces) == 2 { diff --git a/pkg/labels/selector_test.go b/pkg/labels/selector_test.go index 6c5b81296e3..5539d71ea84 100644 --- a/pkg/labels/selector_test.go +++ b/pkg/labels/selector_test.go @@ -18,6 +18,8 @@ package labels import ( "testing" + + "github.com/GoogleCloudPlatform/kubernetes/pkg/util" ) func TestSelectorParse(t *testing.T) { @@ -163,3 +165,57 @@ func TestSetIsEmpty(t *testing.T) { t.Errorf("Nested andTerm should not be empty") } } + +func expectMatchRequirement(t *testing.T, req Requirement, ls Set) { + if !req.Matches(ls) { + t.Errorf("Wanted '%+v' to match '%s', but it did not.\n", req, ls) + } +} + +func expectNoMatchRequirement(t *testing.T, req Requirement, ls Set) { + if req.Matches(ls) { + t.Errorf("Wanted '%+v' to not match '%s', but it did.", req, ls) + } +} + +func TestRequirementMatches(t *testing.T) { + s := Set{"x": "foo", "y": "baz"} + a := Requirement{key: "x", operator: IN, strValues: util.NewStringSet("foo")} + b := Requirement{key: "x", operator: NOT_IN, strValues: util.NewStringSet("beta")} + c := Requirement{key: "y", operator: IN, strValues: nil} + d := Requirement{key: "y", strValues: util.NewStringSet("foo")} + expectMatchRequirement(t, a, s) + expectMatchRequirement(t, b, s) + expectNoMatchRequirement(t, c, s) + expectNoMatchRequirement(t, d, s) +} + +func expectMatchLabSelector(t *testing.T, lsel LabelSelector, s Set) { + if !lsel.Matches(s) { + t.Errorf("Wanted '%+v' to match '%s', but it did not.\n", lsel, s) + } +} + +func expectNoMatchLabSelector(t *testing.T, lsel LabelSelector, s Set) { + if lsel.Matches(s) { + t.Errorf("Wanted '%+v' to not match '%s', but it did.\n", lsel, s) + } +} + +func TestLabelSelectorMatches(t *testing.T) { + s := Set{"x": "foo", "y": "baz"} + allMatch := LabelSelector{ + Requirements: []Requirement{ + {key: "x", operator: IN, strValues: util.NewStringSet("foo")}, + {key: "y", operator: NOT_IN, strValues: util.NewStringSet("alpha")}, + }, + } + singleNonMatch := LabelSelector{ + Requirements: []Requirement{ + {key: "x", operator: IN, strValues: util.NewStringSet("foo")}, + {key: "y", operator: IN, strValues: util.NewStringSet("alpha")}, + }, + } + expectMatchLabSelector(t, allMatch, s) + expectNoMatchLabSelector(t, singleNonMatch, s) +}