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

View File

@ -20,6 +20,7 @@ import (
"fmt" "fmt"
"k8s.io/kubernetes/pkg/labels" "k8s.io/kubernetes/pkg/labels"
"k8s.io/kubernetes/pkg/selection"
"k8s.io/kubernetes/pkg/util/sets" "k8s.io/kubernetes/pkg/util/sets"
) )
@ -35,23 +36,23 @@ func LabelSelectorAsSelector(ps *LabelSelector) (labels.Selector, error) {
} }
selector := labels.NewSelector() selector := labels.NewSelector()
for k, v := range ps.MatchLabels { 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 { if err != nil {
return nil, err return nil, err
} }
selector = selector.Add(*r) selector = selector.Add(*r)
} }
for _, expr := range ps.MatchExpressions { for _, expr := range ps.MatchExpressions {
var op labels.Operator var op selection.Operator
switch expr.Operator { switch expr.Operator {
case LabelSelectorOpIn: case LabelSelectorOpIn:
op = labels.InOperator op = selection.In
case LabelSelectorOpNotIn: case LabelSelectorOpNotIn:
op = labels.NotInOperator op = selection.NotIn
case LabelSelectorOpExists: case LabelSelectorOpExists:
op = labels.ExistsOperator op = selection.Exists
case LabelSelectorOpDoesNotExist: case LabelSelectorOpDoesNotExist:
op = labels.DoesNotExistOperator op = selection.DoesNotExist
default: default:
return nil, fmt.Errorf("%q is not a valid pod selector operator", expr.Operator) 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 { for _, req := range reqs {
var op LabelSelectorOperator var op LabelSelectorOperator
switch req.Operator() { switch req.Operator() {
case labels.EqualsOperator, labels.DoubleEqualsOperator: case selection.Equals, selection.DoubleEquals:
vals := req.Values() vals := req.Values()
if vals.Len() != 1 { if vals.Len() != 1 {
return nil, fmt.Errorf("equals operator must have exactly one value") 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 labelSelector.MatchLabels[req.Key()] = val
continue continue
case labels.InOperator: case selection.In:
op = LabelSelectorOpIn op = LabelSelectorOpIn
case labels.NotInOperator: case selection.NotIn:
op = LabelSelectorOpNotIn op = LabelSelectorOpNotIn
case labels.ExistsOperator: case selection.Exists:
op = LabelSelectorOpExists op = LabelSelectorOpExists
case labels.DoesNotExistOperator: case selection.DoesNotExist:
op = LabelSelectorOpDoesNotExist 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 // 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()) return nil, fmt.Errorf("%q isn't supported in label selectors", req.Operator())
default: 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" "fmt"
"sort" "sort"
"strings" "strings"
"k8s.io/kubernetes/pkg/selection"
) )
// Selector represents a field selector. // 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. // applied to the entire selector, or an error if fn returns an error.
Transform(fn TransformFunc) (Selector, 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 returns a human readable string that represents this selector.
String() string String() string
} }
@ -75,6 +81,14 @@ func (t *hasTerm) Transform(fn TransformFunc) (Selector, error) {
return &hasTerm{field, value}, nil 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 { func (t *hasTerm) String() string {
return fmt.Sprintf("%v=%v", t.field, t.value) 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 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 { func (t *notHasTerm) String() string {
return fmt.Sprintf("%v!=%v", t.field, t.value) 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 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 { func (t andTerm) String() string {
var terms []string var terms []string
for _, q := range t { for _, q := range t {

View File

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

View File

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

View File

@ -35,6 +35,7 @@ import (
"k8s.io/kubernetes/pkg/labels" "k8s.io/kubernetes/pkg/labels"
"k8s.io/kubernetes/pkg/registry/generic" "k8s.io/kubernetes/pkg/registry/generic"
"k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/selection"
"k8s.io/kubernetes/pkg/storage" "k8s.io/kubernetes/pkg/storage"
etcdstorage "k8s.io/kubernetes/pkg/storage/etcd" etcdstorage "k8s.io/kubernetes/pkg/storage/etcd"
"k8s.io/kubernetes/pkg/storage/etcd/etcdtest" "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 { func matchPodName(names ...string) *generic.SelectionPredicate {
// Note: even if pod name is a field, we have to use labels, // Note: even if pod name is a field, we have to use labels,
// because field selector doesn't support "IN" operator. // 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 { if err != nil {
panic("Labels requirement must validate successfully") 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"
)