make labels, fields expose selectable requirements

pull/6/head
Hongchao Deng 2016-08-15 15:36:37 -07:00
parent 9030a3234f
commit 1871a22039
8 changed files with 259 additions and 165 deletions

View File

@ -30,6 +30,7 @@ import (
"k8s.io/kubernetes/pkg/fields"
"k8s.io/kubernetes/pkg/labels"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/selection"
"k8s.io/kubernetes/pkg/types"
"k8s.io/kubernetes/pkg/util/sets"
@ -383,20 +384,20 @@ func NodeSelectorRequirementsAsSelector(nsm []NodeSelectorRequirement) (labels.S
}
selector := labels.NewSelector()
for _, expr := range nsm {
var op labels.Operator
var op selection.Operator
switch expr.Operator {
case NodeSelectorOpIn:
op = labels.InOperator
op = selection.In
case NodeSelectorOpNotIn:
op = labels.NotInOperator
op = selection.NotIn
case NodeSelectorOpExists:
op = labels.ExistsOperator
op = selection.Exists
case NodeSelectorOpDoesNotExist:
op = labels.DoesNotExistOperator
op = selection.DoesNotExist
case NodeSelectorOpGt:
op = labels.GreaterThanOperator
op = selection.GreaterThan
case NodeSelectorOpLt:
op = labels.LessThanOperator
op = selection.LessThan
default:
return nil, fmt.Errorf("%q is not a valid node selector operator", expr.Operator)
}

View File

@ -20,6 +20,7 @@ import (
"fmt"
"k8s.io/kubernetes/pkg/labels"
"k8s.io/kubernetes/pkg/selection"
"k8s.io/kubernetes/pkg/util/sets"
)
@ -35,23 +36,23 @@ func LabelSelectorAsSelector(ps *LabelSelector) (labels.Selector, error) {
}
selector := labels.NewSelector()
for k, v := range ps.MatchLabels {
r, err := labels.NewRequirement(k, labels.EqualsOperator, sets.NewString(v))
r, err := labels.NewRequirement(k, selection.Equals, sets.NewString(v))
if err != nil {
return nil, err
}
selector = selector.Add(*r)
}
for _, expr := range ps.MatchExpressions {
var op labels.Operator
var op selection.Operator
switch expr.Operator {
case LabelSelectorOpIn:
op = labels.InOperator
op = selection.In
case LabelSelectorOpNotIn:
op = labels.NotInOperator
op = selection.NotIn
case LabelSelectorOpExists:
op = labels.ExistsOperator
op = selection.Exists
case LabelSelectorOpDoesNotExist:
op = labels.DoesNotExistOperator
op = selection.DoesNotExist
default:
return nil, fmt.Errorf("%q is not a valid pod selector operator", expr.Operator)
}
@ -108,7 +109,7 @@ func ParseToLabelSelector(selector string) (*LabelSelector, error) {
for _, req := range reqs {
var op LabelSelectorOperator
switch req.Operator() {
case labels.EqualsOperator, labels.DoubleEqualsOperator:
case selection.Equals, selection.DoubleEquals:
vals := req.Values()
if vals.Len() != 1 {
return nil, fmt.Errorf("equals operator must have exactly one value")
@ -119,15 +120,15 @@ func ParseToLabelSelector(selector string) (*LabelSelector, error) {
}
labelSelector.MatchLabels[req.Key()] = val
continue
case labels.InOperator:
case selection.In:
op = LabelSelectorOpIn
case labels.NotInOperator:
case selection.NotIn:
op = LabelSelectorOpNotIn
case labels.ExistsOperator:
case selection.Exists:
op = LabelSelectorOpExists
case labels.DoesNotExistOperator:
case selection.DoesNotExist:
op = LabelSelectorOpDoesNotExist
case labels.GreaterThanOperator, labels.LessThanOperator:
case selection.GreaterThan, selection.LessThan:
// Adding a separate case for these operators to indicate that this is deliberate
return nil, fmt.Errorf("%q isn't supported in label selectors", req.Operator())
default:

View File

@ -0,0 +1,30 @@
/*
Copyright 2016 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package fields
import "k8s.io/kubernetes/pkg/selection"
// Requirements is AND of all requirements.
type Requirements []Requirement
// Requirement contains a field, a value, and an operator that relates the field and value.
// This is currently for reading internal selection information of field selector.
type Requirement struct {
Operator selection.Operator
Field string
Value string
}

View File

@ -20,6 +20,8 @@ import (
"fmt"
"sort"
"strings"
"k8s.io/kubernetes/pkg/selection"
)
// Selector represents a field selector.
@ -39,6 +41,10 @@ type Selector interface {
// applied to the entire selector, or an error if fn returns an error.
Transform(fn TransformFunc) (Selector, error)
// Requirements converts this interface to Requirements to expose
// more detailed selection information.
Requirements() Requirements
// String returns a human readable string that represents this selector.
String() string
}
@ -75,6 +81,14 @@ func (t *hasTerm) Transform(fn TransformFunc) (Selector, error) {
return &hasTerm{field, value}, nil
}
func (t *hasTerm) Requirements() Requirements {
return []Requirement{{
Field: t.field,
Operator: selection.Equals,
Value: t.value,
}}
}
func (t *hasTerm) String() string {
return fmt.Sprintf("%v=%v", t.field, t.value)
}
@ -103,6 +117,14 @@ func (t *notHasTerm) Transform(fn TransformFunc) (Selector, error) {
return &notHasTerm{field, value}, nil
}
func (t *notHasTerm) Requirements() Requirements {
return []Requirement{{
Field: t.field,
Operator: selection.NotEquals,
Value: t.value,
}}
}
func (t *notHasTerm) String() string {
return fmt.Sprintf("%v!=%v", t.field, t.value)
}
@ -157,6 +179,15 @@ func (t andTerm) Transform(fn TransformFunc) (Selector, error) {
return andTerm(next), nil
}
func (t andTerm) Requirements() Requirements {
reqs := make([]Requirement, 0, len(t))
for _, s := range []Selector(t) {
rs := s.Requirements()
reqs = append(reqs, rs...)
}
return reqs
}
func (t andTerm) String() string {
var terms []string
for _, q := range t {

View File

@ -24,10 +24,14 @@ import (
"strings"
"github.com/golang/glog"
"k8s.io/kubernetes/pkg/selection"
"k8s.io/kubernetes/pkg/util/sets"
"k8s.io/kubernetes/pkg/util/validation"
)
// Requirements is AND of all requirements.
type Requirements []Requirement
// Selector represents a label selector.
type Selector interface {
// Matches returns true if this selector matches the given set of labels.
@ -41,6 +45,12 @@ type Selector interface {
// Add adds requirements to the Selector
Add(r ...Requirement) Selector
// Requirements converts this interface into Requirements to expose
// more detailed selection information.
// If there are querying parameters, it will return converted requirements and selectable=true.
// If this selector doesn't want to select anything, it will return selectable=false.
Requirements() (requirements Requirements, selectable bool)
}
// Everything returns a selector that matches all labels.
@ -50,32 +60,17 @@ 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 "<null>" }
func (n nothingSelector) Add(_ ...Requirement) Selector { return n }
func (n nothingSelector) Matches(_ Labels) bool { return false }
func (n nothingSelector) Empty() bool { return false }
func (n nothingSelector) String() string { return "<null>" }
func (n nothingSelector) Add(_ ...Requirement) Selector { return n }
func (n nothingSelector) Requirements() (Requirements, bool) { return nil, false }
// Nothing returns a selector that matches no labels
func Nothing() Selector {
return nothingSelector{}
}
// Operator represents a key's relationship
// to a set of values in a Requirement.
type Operator string
const (
DoesNotExistOperator Operator = "!"
EqualsOperator Operator = "="
DoubleEqualsOperator Operator = "=="
InOperator Operator = "in"
NotEqualsOperator Operator = "!="
NotInOperator Operator = "notin"
ExistsOperator Operator = "exists"
GreaterThanOperator Operator = "gt"
LessThanOperator Operator = "lt"
)
func NewSelector() Selector {
return internalSelector(nil)
}
@ -91,14 +86,13 @@ func (a ByKey) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a ByKey) Less(i, j int) bool { return a[i].key < a[j].key }
// Requirement is a selector that contains values, a key
// and an operator that relates the key and values. The zero
// value of Requirement is invalid.
// Requirement contains values, a key, and an operator that relates the key and values.
// The zero value of Requirement is invalid.
// Requirement implements both set based match and exact match
// Requirement is initialized via NewRequirement constructor for creating a valid Requirement.
// Requirement should be initialized via NewRequirement constructor for creating a valid Requirement.
type Requirement struct {
key string
operator Operator
operator selection.Operator
strValues sets.String
}
@ -113,24 +107,24 @@ type Requirement struct {
// of characters. See validateLabelKey for more details.
//
// The empty string is a valid value in the input values set.
func NewRequirement(key string, op Operator, vals sets.String) (*Requirement, error) {
func NewRequirement(key string, op selection.Operator, vals sets.String) (*Requirement, error) {
if err := validateLabelKey(key); err != nil {
return nil, err
}
switch op {
case InOperator, NotInOperator:
case selection.In, selection.NotIn:
if len(vals) == 0 {
return nil, fmt.Errorf("for 'in', 'notin' operators, values set can't be empty")
}
case EqualsOperator, DoubleEqualsOperator, NotEqualsOperator:
case selection.Equals, selection.DoubleEquals, selection.NotEquals:
if len(vals) != 1 {
return nil, fmt.Errorf("exact-match compatibility requires one single value")
}
case ExistsOperator, DoesNotExistOperator:
case selection.Exists, selection.DoesNotExist:
if len(vals) != 0 {
return nil, fmt.Errorf("values set must be empty for exists and does not exist")
}
case GreaterThanOperator, LessThanOperator:
case selection.GreaterThan, selection.LessThan:
if len(vals) != 1 {
return nil, fmt.Errorf("for 'Gt', 'Lt' operators, exactly one value is required")
}
@ -164,21 +158,21 @@ func NewRequirement(key string, op Operator, vals sets.String) (*Requirement, er
// the Requirement's key and the corresponding value satisfies mathematical inequality.
func (r *Requirement) Matches(ls Labels) bool {
switch r.operator {
case InOperator, EqualsOperator, DoubleEqualsOperator:
case selection.In, selection.Equals, selection.DoubleEquals:
if !ls.Has(r.key) {
return false
}
return r.strValues.Has(ls.Get(r.key))
case NotInOperator, NotEqualsOperator:
case selection.NotIn, selection.NotEquals:
if !ls.Has(r.key) {
return true
}
return !r.strValues.Has(ls.Get(r.key))
case ExistsOperator:
case selection.Exists:
return ls.Has(r.key)
case DoesNotExistOperator:
case selection.DoesNotExist:
return !ls.Has(r.key)
case GreaterThanOperator, LessThanOperator:
case selection.GreaterThan, selection.LessThan:
if !ls.Has(r.key) {
return false
}
@ -202,7 +196,7 @@ func (r *Requirement) Matches(ls Labels) bool {
return false
}
}
return (r.operator == GreaterThanOperator && lsValue > rValue) || (r.operator == LessThanOperator && lsValue < rValue)
return (r.operator == selection.GreaterThan && lsValue > rValue) || (r.operator == selection.LessThan && lsValue < rValue)
default:
return false
}
@ -211,7 +205,7 @@ func (r *Requirement) Matches(ls Labels) bool {
func (r *Requirement) Key() string {
return r.key
}
func (r *Requirement) Operator() Operator {
func (r *Requirement) Operator() selection.Operator {
return r.operator
}
func (r *Requirement) Values() sets.String {
@ -235,32 +229,32 @@ func (lsel internalSelector) Empty() bool {
// returned. See NewRequirement for creating a valid Requirement.
func (r *Requirement) String() string {
var buffer bytes.Buffer
if r.operator == DoesNotExistOperator {
if r.operator == selection.DoesNotExist {
buffer.WriteString("!")
}
buffer.WriteString(r.key)
switch r.operator {
case EqualsOperator:
case selection.Equals:
buffer.WriteString("=")
case DoubleEqualsOperator:
case selection.DoubleEquals:
buffer.WriteString("==")
case NotEqualsOperator:
case selection.NotEquals:
buffer.WriteString("!=")
case InOperator:
case selection.In:
buffer.WriteString(" in ")
case NotInOperator:
case selection.NotIn:
buffer.WriteString(" notin ")
case GreaterThanOperator:
case selection.GreaterThan:
buffer.WriteString(">")
case LessThanOperator:
case selection.LessThan:
buffer.WriteString("<")
case ExistsOperator, DoesNotExistOperator:
case selection.Exists, selection.DoesNotExist:
return buffer.String()
}
switch r.operator {
case InOperator, NotInOperator:
case selection.In, selection.NotIn:
buffer.WriteString("(")
}
if len(r.strValues) == 1 {
@ -270,7 +264,7 @@ func (r *Requirement) String() string {
}
switch r.operator {
case InOperator, NotInOperator:
case selection.In, selection.NotIn:
buffer.WriteString(")")
}
return buffer.String()
@ -301,6 +295,8 @@ func (lsel internalSelector) Matches(l Labels) bool {
return true
}
func (lsel internalSelector) Requirements() (Requirements, bool) { return Requirements(lsel), true }
// String returns a comma-separated string of all
// the internalSelector Requirements' human-readable strings.
func (lsel internalSelector) String() string {
@ -564,7 +560,7 @@ func (p *Parser) parseRequirement() (*Requirement, error) {
if err != nil {
return nil, err
}
if operator == ExistsOperator || operator == DoesNotExistOperator { // operator found lookahead set checked
if operator == selection.Exists || operator == selection.DoesNotExist { // operator found lookahead set checked
return NewRequirement(key, operator, nil)
}
operator, err = p.parseOperator()
@ -573,9 +569,9 @@ func (p *Parser) parseRequirement() (*Requirement, error) {
}
var values sets.String
switch operator {
case InOperator, NotInOperator:
case selection.In, selection.NotIn:
values, err = p.parseValues()
case EqualsOperator, DoubleEqualsOperator, NotEqualsOperator, GreaterThanOperator, LessThanOperator:
case selection.Equals, selection.DoubleEquals, selection.NotEquals, selection.GreaterThan, selection.LessThan:
values, err = p.parseExactValue()
}
if err != nil {
@ -588,11 +584,11 @@ func (p *Parser) parseRequirement() (*Requirement, error) {
// parseKeyAndInferOperator parse literals.
// in case of no operator '!, in, notin, ==, =, !=' are found
// the 'exists' operator is inferred
func (p *Parser) parseKeyAndInferOperator() (string, Operator, error) {
var operator Operator
func (p *Parser) parseKeyAndInferOperator() (string, selection.Operator, error) {
var operator selection.Operator
tok, literal := p.consume(Values)
if tok == DoesNotExistToken {
operator = DoesNotExistOperator
operator = selection.DoesNotExist
tok, literal = p.consume(Values)
}
if tok != IdentifierToken {
@ -603,8 +599,8 @@ func (p *Parser) parseKeyAndInferOperator() (string, Operator, error) {
return "", "", err
}
if t, _ := p.lookahead(Values); t == EndOfStringToken || t == CommaToken {
if operator != DoesNotExistOperator {
operator = ExistsOperator
if operator != selection.DoesNotExist {
operator = selection.Exists
}
}
return literal, operator, nil
@ -612,24 +608,24 @@ func (p *Parser) parseKeyAndInferOperator() (string, Operator, error) {
// parseOperator return operator and eventually matchType
// matchType can be exact
func (p *Parser) parseOperator() (op Operator, err error) {
func (p *Parser) parseOperator() (op selection.Operator, err error) {
tok, lit := p.consume(KeyAndOperator)
switch tok {
// DoesNotExistToken shouldn't be here because it's a unary operator, not a binary operator
case InToken:
op = InOperator
op = selection.In
case EqualsToken:
op = EqualsOperator
op = selection.Equals
case DoubleEqualsToken:
op = DoubleEqualsOperator
op = selection.DoubleEquals
case GreaterThanToken:
op = GreaterThanOperator
op = selection.GreaterThan
case LessThanToken:
op = LessThanOperator
op = selection.LessThan
case NotInToken:
op = NotInOperator
op = selection.NotIn
case NotEqualsToken:
op = NotEqualsOperator
op = selection.NotEquals
default:
return "", fmt.Errorf("found '%s', expected: '=', '!=', '==', 'in', notin'", lit)
}
@ -788,7 +784,7 @@ func SelectorFromSet(ls Set) Selector {
}
var requirements internalSelector
for label, value := range ls {
if r, err := NewRequirement(label, EqualsOperator, sets.NewString(value)); err != nil {
if r, err := NewRequirement(label, selection.Equals, sets.NewString(value)); err != nil {
//TODO: double check errors when input comes from serialization?
return internalSelector{}
} else {

View File

@ -21,6 +21,7 @@ import (
"strings"
"testing"
"k8s.io/kubernetes/pkg/selection"
"k8s.io/kubernetes/pkg/util/sets"
)
@ -300,23 +301,23 @@ func TestParserLookahead(t *testing.T) {
func TestRequirementConstructor(t *testing.T) {
requirementConstructorTests := []struct {
Key string
Op Operator
Op selection.Operator
Vals sets.String
Success bool
}{
{"x", InOperator, nil, false},
{"x", NotInOperator, sets.NewString(), false},
{"x", InOperator, sets.NewString("foo"), true},
{"x", NotInOperator, sets.NewString("foo"), true},
{"x", ExistsOperator, nil, true},
{"x", DoesNotExistOperator, nil, true},
{"1foo", InOperator, sets.NewString("bar"), true},
{"1234", InOperator, sets.NewString("bar"), true},
{"y", GreaterThanOperator, sets.NewString("1"), true},
{"z", LessThanOperator, sets.NewString("6"), true},
{"foo", GreaterThanOperator, sets.NewString("bar"), false},
{"barz", LessThanOperator, sets.NewString("blah"), false},
{strings.Repeat("a", 254), ExistsOperator, nil, false}, //breaks DNS rule that len(key) <= 253
{"x", selection.In, nil, false},
{"x", selection.NotIn, sets.NewString(), false},
{"x", selection.In, sets.NewString("foo"), true},
{"x", selection.NotIn, sets.NewString("foo"), true},
{"x", selection.Exists, nil, true},
{"x", selection.DoesNotExist, nil, true},
{"1foo", selection.In, sets.NewString("bar"), true},
{"1234", selection.In, sets.NewString("bar"), true},
{"y", selection.GreaterThan, sets.NewString("1"), true},
{"z", selection.LessThan, sets.NewString("6"), true},
{"foo", selection.GreaterThan, sets.NewString("bar"), false},
{"barz", selection.LessThan, sets.NewString("blah"), false},
{strings.Repeat("a", 254), selection.Exists, nil, false}, //breaks DNS rule that len(key) <= 253
}
for _, rc := range requirementConstructorTests {
if _, err := NewRequirement(rc.Key, rc.Op, rc.Vals); err == nil && !rc.Success {
@ -336,34 +337,34 @@ func TestToString(t *testing.T) {
}{
{&internalSelector{
getRequirement("x", InOperator, sets.NewString("abc", "def"), t),
getRequirement("y", NotInOperator, sets.NewString("jkl"), t),
getRequirement("z", ExistsOperator, nil, t)},
getRequirement("x", selection.In, sets.NewString("abc", "def"), t),
getRequirement("y", selection.NotIn, sets.NewString("jkl"), t),
getRequirement("z", selection.Exists, nil, t)},
"x in (abc,def),y notin (jkl),z", true},
{&internalSelector{
getRequirement("x", NotInOperator, sets.NewString("abc", "def"), t),
getRequirement("y", NotEqualsOperator, sets.NewString("jkl"), t),
getRequirement("z", DoesNotExistOperator, nil, t)},
getRequirement("x", selection.NotIn, sets.NewString("abc", "def"), t),
getRequirement("y", selection.NotEquals, sets.NewString("jkl"), t),
getRequirement("z", selection.DoesNotExist, nil, t)},
"x notin (abc,def),y!=jkl,!z", true},
{&internalSelector{
getRequirement("x", InOperator, sets.NewString("abc", "def"), t),
getRequirement("x", selection.In, sets.NewString("abc", "def"), t),
req}, // adding empty req for the trailing ','
"x in (abc,def),", false},
{&internalSelector{
getRequirement("x", NotInOperator, sets.NewString("abc"), t),
getRequirement("y", InOperator, sets.NewString("jkl", "mno"), t),
getRequirement("z", NotInOperator, sets.NewString(""), t)},
getRequirement("x", selection.NotIn, sets.NewString("abc"), t),
getRequirement("y", selection.In, sets.NewString("jkl", "mno"), t),
getRequirement("z", selection.NotIn, sets.NewString(""), t)},
"x notin (abc),y in (jkl,mno),z notin ()", true},
{&internalSelector{
getRequirement("x", EqualsOperator, sets.NewString("abc"), t),
getRequirement("y", DoubleEqualsOperator, sets.NewString("jkl"), t),
getRequirement("z", NotEqualsOperator, sets.NewString("a"), t),
getRequirement("z", ExistsOperator, nil, t)},
getRequirement("x", selection.Equals, sets.NewString("abc"), t),
getRequirement("y", selection.DoubleEquals, sets.NewString("jkl"), t),
getRequirement("z", selection.NotEquals, sets.NewString("a"), t),
getRequirement("z", selection.Exists, nil, t)},
"x=abc,y==jkl,z!=a,z", true},
{&internalSelector{
getRequirement("x", GreaterThanOperator, sets.NewString("2"), t),
getRequirement("y", LessThanOperator, sets.NewString("8"), t),
getRequirement("z", ExistsOperator, nil, t)},
getRequirement("x", selection.GreaterThan, sets.NewString("2"), t),
getRequirement("y", selection.LessThan, sets.NewString("8"), t),
getRequirement("z", selection.Exists, nil, t)},
"x>2,y<8,z", true},
}
for _, ts := range toStringTests {
@ -386,33 +387,33 @@ func TestRequirementSelectorMatching(t *testing.T) {
req,
}, false},
{Set{"x": "foo", "y": "baz"}, &internalSelector{
getRequirement("x", InOperator, sets.NewString("foo"), t),
getRequirement("y", NotInOperator, sets.NewString("alpha"), t),
getRequirement("x", selection.In, sets.NewString("foo"), t),
getRequirement("y", selection.NotIn, sets.NewString("alpha"), t),
}, true},
{Set{"x": "foo", "y": "baz"}, &internalSelector{
getRequirement("x", InOperator, sets.NewString("foo"), t),
getRequirement("y", InOperator, sets.NewString("alpha"), t),
getRequirement("x", selection.In, sets.NewString("foo"), t),
getRequirement("y", selection.In, sets.NewString("alpha"), t),
}, false},
{Set{"y": ""}, &internalSelector{
getRequirement("x", NotInOperator, sets.NewString(""), t),
getRequirement("y", ExistsOperator, nil, t),
getRequirement("x", selection.NotIn, sets.NewString(""), t),
getRequirement("y", selection.Exists, nil, t),
}, true},
{Set{"y": ""}, &internalSelector{
getRequirement("x", DoesNotExistOperator, nil, t),
getRequirement("y", ExistsOperator, nil, t),
getRequirement("x", selection.DoesNotExist, nil, t),
getRequirement("y", selection.Exists, nil, t),
}, true},
{Set{"y": ""}, &internalSelector{
getRequirement("x", NotInOperator, sets.NewString(""), t),
getRequirement("y", DoesNotExistOperator, nil, t),
getRequirement("x", selection.NotIn, sets.NewString(""), t),
getRequirement("y", selection.DoesNotExist, nil, t),
}, false},
{Set{"y": "baz"}, &internalSelector{
getRequirement("x", InOperator, sets.NewString(""), t),
getRequirement("x", selection.In, sets.NewString(""), t),
}, false},
{Set{"z": "2"}, &internalSelector{
getRequirement("z", GreaterThanOperator, sets.NewString("1"), t),
getRequirement("z", selection.GreaterThan, sets.NewString("1"), t),
}, true},
{Set{"z": "v2"}, &internalSelector{
getRequirement("z", GreaterThanOperator, sets.NewString("1"), t),
getRequirement("z", selection.GreaterThan, sets.NewString("1"), t),
}, false},
}
for _, lsm := range labelSelectorMatchingTests {
@ -431,80 +432,80 @@ func TestSetSelectorParser(t *testing.T) {
}{
{"", NewSelector(), true, true},
{"\rx", internalSelector{
getRequirement("x", ExistsOperator, nil, t),
getRequirement("x", selection.Exists, nil, t),
}, true, true},
{"this-is-a-dns.domain.com/key-with-dash", internalSelector{
getRequirement("this-is-a-dns.domain.com/key-with-dash", ExistsOperator, nil, t),
getRequirement("this-is-a-dns.domain.com/key-with-dash", selection.Exists, nil, t),
}, true, true},
{"this-is-another-dns.domain.com/key-with-dash in (so,what)", internalSelector{
getRequirement("this-is-another-dns.domain.com/key-with-dash", InOperator, sets.NewString("so", "what"), t),
getRequirement("this-is-another-dns.domain.com/key-with-dash", selection.In, sets.NewString("so", "what"), t),
}, true, true},
{"0.1.2.domain/99 notin (10.10.100.1, tick.tack.clock)", internalSelector{
getRequirement("0.1.2.domain/99", NotInOperator, sets.NewString("10.10.100.1", "tick.tack.clock"), t),
getRequirement("0.1.2.domain/99", selection.NotIn, sets.NewString("10.10.100.1", "tick.tack.clock"), t),
}, true, true},
{"foo in (abc)", internalSelector{
getRequirement("foo", InOperator, sets.NewString("abc"), t),
getRequirement("foo", selection.In, sets.NewString("abc"), t),
}, true, true},
{"x notin\n (abc)", internalSelector{
getRequirement("x", NotInOperator, sets.NewString("abc"), t),
getRequirement("x", selection.NotIn, sets.NewString("abc"), t),
}, true, true},
{"x notin \t (abc,def)", internalSelector{
getRequirement("x", NotInOperator, sets.NewString("abc", "def"), t),
getRequirement("x", selection.NotIn, sets.NewString("abc", "def"), t),
}, true, true},
{"x in (abc,def)", internalSelector{
getRequirement("x", InOperator, sets.NewString("abc", "def"), t),
getRequirement("x", selection.In, sets.NewString("abc", "def"), t),
}, true, true},
{"x in (abc,)", internalSelector{
getRequirement("x", InOperator, sets.NewString("abc", ""), t),
getRequirement("x", selection.In, sets.NewString("abc", ""), t),
}, true, true},
{"x in ()", internalSelector{
getRequirement("x", InOperator, sets.NewString(""), t),
getRequirement("x", selection.In, sets.NewString(""), t),
}, true, true},
{"x notin (abc,,def),bar,z in (),w", internalSelector{
getRequirement("bar", ExistsOperator, nil, t),
getRequirement("w", ExistsOperator, nil, t),
getRequirement("x", NotInOperator, sets.NewString("abc", "", "def"), t),
getRequirement("z", InOperator, sets.NewString(""), t),
getRequirement("bar", selection.Exists, nil, t),
getRequirement("w", selection.Exists, nil, t),
getRequirement("x", selection.NotIn, sets.NewString("abc", "", "def"), t),
getRequirement("z", selection.In, sets.NewString(""), t),
}, true, true},
{"x,y in (a)", internalSelector{
getRequirement("y", InOperator, sets.NewString("a"), t),
getRequirement("x", ExistsOperator, nil, t),
getRequirement("y", selection.In, sets.NewString("a"), t),
getRequirement("x", selection.Exists, nil, t),
}, false, true},
{"x=a", internalSelector{
getRequirement("x", EqualsOperator, sets.NewString("a"), t),
getRequirement("x", selection.Equals, sets.NewString("a"), t),
}, true, true},
{"x>1", internalSelector{
getRequirement("x", GreaterThanOperator, sets.NewString("1"), t),
getRequirement("x", selection.GreaterThan, sets.NewString("1"), t),
}, true, true},
{"x<7", internalSelector{
getRequirement("x", LessThanOperator, sets.NewString("7"), t),
getRequirement("x", selection.LessThan, sets.NewString("7"), t),
}, true, true},
{"x=a,y!=b", internalSelector{
getRequirement("x", EqualsOperator, sets.NewString("a"), t),
getRequirement("y", NotEqualsOperator, sets.NewString("b"), t),
getRequirement("x", selection.Equals, sets.NewString("a"), t),
getRequirement("y", selection.NotEquals, sets.NewString("b"), t),
}, true, true},
{"x=a,y!=b,z in (h,i,j)", internalSelector{
getRequirement("x", EqualsOperator, sets.NewString("a"), t),
getRequirement("y", NotEqualsOperator, sets.NewString("b"), t),
getRequirement("z", InOperator, sets.NewString("h", "i", "j"), t),
getRequirement("x", selection.Equals, sets.NewString("a"), t),
getRequirement("y", selection.NotEquals, sets.NewString("b"), t),
getRequirement("z", selection.In, sets.NewString("h", "i", "j"), t),
}, true, true},
{"x=a||y=b", internalSelector{}, false, false},
{"x,,y", nil, true, false},
{",x,y", nil, true, false},
{"x nott in (y)", nil, true, false},
{"x notin ( )", internalSelector{
getRequirement("x", NotInOperator, sets.NewString(""), t),
getRequirement("x", selection.NotIn, sets.NewString(""), t),
}, true, true},
{"x notin (, a)", internalSelector{
getRequirement("x", NotInOperator, sets.NewString("", "a"), t),
getRequirement("x", selection.NotIn, sets.NewString("", "a"), t),
}, true, true},
{"a in (xyz),", nil, true, false},
{"a in (xyz)b notin ()", nil, true, false},
{"a ", internalSelector{
getRequirement("a", ExistsOperator, nil, t),
getRequirement("a", selection.Exists, nil, t),
}, true, true},
{"a in (x,y,notin, z,in)", internalSelector{
getRequirement("a", InOperator, sets.NewString("in", "notin", "x", "y", "z"), t),
getRequirement("a", selection.In, sets.NewString("in", "notin", "x", "y", "z"), t),
}, true, true}, // operator 'in' inside list of identifiers
{"a in (xyz abc)", nil, false, false}, // no comma
{"a notin(", nil, true, false}, // bad formed
@ -523,7 +524,7 @@ func TestSetSelectorParser(t *testing.T) {
}
}
func getRequirement(key string, op Operator, vals sets.String, t *testing.T) Requirement {
func getRequirement(key string, op selection.Operator, vals sets.String, t *testing.T) Requirement {
req, err := NewRequirement(key, op, vals)
if err != nil {
t.Errorf("NewRequirement(%v, %v, %v) resulted in error:%v", key, op, vals, err)
@ -537,7 +538,7 @@ func TestAdd(t *testing.T) {
name string
sel Selector
key string
operator Operator
operator selection.Operator
values []string
refSelector Selector
}{
@ -545,19 +546,19 @@ func TestAdd(t *testing.T) {
"keyInOperator",
internalSelector{},
"key",
InOperator,
selection.In,
[]string{"value"},
internalSelector{Requirement{"key", InOperator, sets.NewString("value")}},
internalSelector{Requirement{"key", selection.In, sets.NewString("value")}},
},
{
"keyEqualsOperator",
internalSelector{Requirement{"key", InOperator, sets.NewString("value")}},
internalSelector{Requirement{"key", selection.In, sets.NewString("value")}},
"key2",
EqualsOperator,
selection.Equals,
[]string{"value2"},
internalSelector{
Requirement{"key", InOperator, sets.NewString("value")},
Requirement{"key2", EqualsOperator, sets.NewString("value2")},
Requirement{"key", selection.In, sets.NewString("value")},
Requirement{"key2", selection.Equals, sets.NewString("value2")},
},
},
}

View File

@ -35,6 +35,7 @@ import (
"k8s.io/kubernetes/pkg/labels"
"k8s.io/kubernetes/pkg/registry/generic"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/selection"
"k8s.io/kubernetes/pkg/storage"
etcdstorage "k8s.io/kubernetes/pkg/storage/etcd"
"k8s.io/kubernetes/pkg/storage/etcd/etcdtest"
@ -88,7 +89,7 @@ func NewTestGenericStoreRegistry(t *testing.T) (*etcdtesting.EtcdTestServer, *St
func matchPodName(names ...string) *generic.SelectionPredicate {
// Note: even if pod name is a field, we have to use labels,
// because field selector doesn't support "IN" operator.
l, err := labels.NewRequirement("name", labels.InOperator, sets.NewString(names...))
l, err := labels.NewRequirement("name", selection.In, sets.NewString(names...))
if err != nil {
panic("Labels requirement must validate successfully")
}

33
pkg/selection/operator.go Normal file
View File

@ -0,0 +1,33 @@
/*
Copyright 2016 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package selection
// Operator represents a key/field's relationship to value(s).
// See labels.Requirement and fields.Requirement for more details.
type Operator string
const (
DoesNotExist Operator = "!"
Equals Operator = "="
DoubleEquals Operator = "=="
In Operator = "in"
NotEquals Operator = "!="
NotIn Operator = "notin"
Exists Operator = "exists"
GreaterThan Operator = "gt"
LessThan Operator = "lt"
)