From 80b1fa000e1b8df7ca6744aa8385ed61978ab324 Mon Sep 17 00:00:00 2001 From: Meir Fischer Date: Thu, 31 Jul 2014 00:29:42 -0400 Subject: [PATCH 1/2] structured message for selector; matching functionality --- pkg/labels/selector.go | 48 +++++++++++++++++++++++++++++++++ pkg/labels/selector_test.go | 54 +++++++++++++++++++++++++++++++++++++ 2 files changed, 102 insertions(+) diff --git a/pkg/labels/selector.go b/pkg/labels/selector.go index 0845d82305f..65b8e2c3f7a 100644 --- a/pkg/labels/selector.go +++ b/pkg/labels/selector.go @@ -105,6 +105,54 @@ func (t andTerm) String() string { return strings.Join(terms, ",") } +type Comparator int + +const ( + IN Comparator = iota + 1 + NOT_IN +) + +// only not named 'Selector' due to name +// conflict until Selector is deprecated +type LabelSelector struct { + Requirements []Requirement +} + +type Requirement struct { + key string + comparator Comparator + strValues []string +} + +func (r *Requirement) containsStr(value string) bool { + for _, x := range r.strValues { + if value == x { + return true + } + } + return false +} + +func (r *Requirement) Matches(ls Labels) bool { + switch r.comparator { + case IN: + return r.containsStr(ls.Get(r.key)) + case NOT_IN: + return !r.containsStr(ls.Get(r.key)) + default: + return false + } +} + +func (sg *LabelSelector) Matches(ls Labels) bool { + for _, req := range sg.Requirements { + if sat := req.Matches(ls); !sat { + 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..67a9de100f0 100644 --- a/pkg/labels/selector_test.go +++ b/pkg/labels/selector_test.go @@ -163,3 +163,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", comparator: IN, strValues: []string{"foo"}} + b := Requirement{key: "x", comparator: NOT_IN, strValues: []string{"beta"}} + c := Requirement{key: "y", comparator: IN, strValues: nil} + d := Requirement{key: "y", strValues: []string{"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", comparator: IN, strValues: []string{"foo"}}, + {key: "y", comparator: NOT_IN, strValues: []string{"alpha"}}, + }, + } + singleNonMatch := LabelSelector{ + Requirements: []Requirement{ + {key: "x", comparator: IN, strValues: []string{"foo"}}, + {key: "y", comparator: IN, strValues: []string{"alpha"}}, + }, + } + expectMatchLabSelector(t, allMatch, s) + expectNoMatchLabSelector(t, singleNonMatch, s) +} From 9076e78654123a686b46b0c271ee19777951a113 Mon Sep 17 00:00:00 2001 From: Meir Fischer Date: Thu, 31 Jul 2014 23:56:37 -0400 Subject: [PATCH 2/2] change strValues's type to util.StringSet; rename Comparator to Operator; small syntax fix --- pkg/labels/selector.go | 36 ++++++++++++++++-------------------- pkg/labels/selector_test.go | 18 ++++++++++-------- 2 files changed, 26 insertions(+), 28 deletions(-) diff --git a/pkg/labels/selector.go b/pkg/labels/selector.go index 65b8e2c3f7a..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,40 +107,34 @@ func (t andTerm) String() string { return strings.Join(terms, ",") } -type Comparator int +// 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 Comparator = iota + 1 + IN Operator = iota + 1 NOT_IN ) -// only not named 'Selector' due to name -// conflict until Selector is deprecated +// LabelSelector only not named 'Selector' due +// to name conflict until Selector is deprecated. type LabelSelector struct { Requirements []Requirement } type Requirement struct { - key string - comparator Comparator - strValues []string -} - -func (r *Requirement) containsStr(value string) bool { - for _, x := range r.strValues { - if value == x { - return true - } - } - return false + key string + operator Operator + strValues util.StringSet } func (r *Requirement) Matches(ls Labels) bool { - switch r.comparator { + switch r.operator { case IN: - return r.containsStr(ls.Get(r.key)) + return r.strValues.Has(ls.Get(r.key)) case NOT_IN: - return !r.containsStr(ls.Get(r.key)) + return !r.strValues.Has(ls.Get(r.key)) default: return false } @@ -146,7 +142,7 @@ func (r *Requirement) Matches(ls Labels) bool { func (sg *LabelSelector) Matches(ls Labels) bool { for _, req := range sg.Requirements { - if sat := req.Matches(ls); !sat { + if !req.Matches(ls) { return false } } diff --git a/pkg/labels/selector_test.go b/pkg/labels/selector_test.go index 67a9de100f0..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) { @@ -178,10 +180,10 @@ func expectNoMatchRequirement(t *testing.T, req Requirement, ls Set) { func TestRequirementMatches(t *testing.T) { s := Set{"x": "foo", "y": "baz"} - a := Requirement{key: "x", comparator: IN, strValues: []string{"foo"}} - b := Requirement{key: "x", comparator: NOT_IN, strValues: []string{"beta"}} - c := Requirement{key: "y", comparator: IN, strValues: nil} - d := Requirement{key: "y", strValues: []string{"foo"}} + 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) @@ -204,14 +206,14 @@ func TestLabelSelectorMatches(t *testing.T) { s := Set{"x": "foo", "y": "baz"} allMatch := LabelSelector{ Requirements: []Requirement{ - {key: "x", comparator: IN, strValues: []string{"foo"}}, - {key: "y", comparator: NOT_IN, strValues: []string{"alpha"}}, + {key: "x", operator: IN, strValues: util.NewStringSet("foo")}, + {key: "y", operator: NOT_IN, strValues: util.NewStringSet("alpha")}, }, } singleNonMatch := LabelSelector{ Requirements: []Requirement{ - {key: "x", comparator: IN, strValues: []string{"foo"}}, - {key: "y", comparator: IN, strValues: []string{"alpha"}}, + {key: "x", operator: IN, strValues: util.NewStringSet("foo")}, + {key: "y", operator: IN, strValues: util.NewStringSet("alpha")}, }, } expectMatchLabSelector(t, allMatch, s)