mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-01 07:47:56 +00:00
More efficient selector
This commit is contained in:
parent
d7e70bd448
commit
9c91da7a22
@ -417,7 +417,7 @@ func NodeSelectorRequirementsAsSelector(nsm []NodeSelectorRequirement) (labels.S
|
|||||||
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)
|
||||||
}
|
}
|
||||||
r, err := labels.NewRequirement(expr.Key, op, sets.NewString(expr.Values...))
|
r, err := labels.NewRequirement(expr.Key, op, expr.Values)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,6 @@ import (
|
|||||||
|
|
||||||
"k8s.io/kubernetes/pkg/labels"
|
"k8s.io/kubernetes/pkg/labels"
|
||||||
"k8s.io/kubernetes/pkg/selection"
|
"k8s.io/kubernetes/pkg/selection"
|
||||||
"k8s.io/kubernetes/pkg/util/sets"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// LabelSelectorAsSelector converts the LabelSelector api type into a struct that implements
|
// LabelSelectorAsSelector converts the LabelSelector api type into a struct that implements
|
||||||
@ -36,7 +35,7 @@ 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, selection.Equals, sets.NewString(v))
|
r, err := labels.NewRequirement(k, selection.Equals, []string{v})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -56,7 +55,7 @@ func LabelSelectorAsSelector(ps *LabelSelector) (labels.Selector, error) {
|
|||||||
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)
|
||||||
}
|
}
|
||||||
r, err := labels.NewRequirement(expr.Key, op, sets.NewString(expr.Values...))
|
r, err := labels.NewRequirement(expr.Key, op, append([]string(nil), expr.Values...))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -91,9 +91,12 @@ func (a ByKey) Less(i, j int) bool { return a[i].key < a[j].key }
|
|||||||
// Requirement implements both set based match and exact match
|
// Requirement implements both set based match and exact match
|
||||||
// Requirement should be 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 selection.Operator
|
operator selection.Operator
|
||||||
strValues sets.String
|
// In huge majority of cases we have at most one value here.
|
||||||
|
// It is generally faster to operate on a single-element slice
|
||||||
|
// than on a single-element map, so we have a slice here.
|
||||||
|
strValues []string
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewRequirement is the constructor for a Requirement.
|
// NewRequirement is the constructor for a Requirement.
|
||||||
@ -107,7 +110,7 @@ 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 selection.Operator, vals sets.String) (*Requirement, error) {
|
func NewRequirement(key string, op selection.Operator, vals []string) (*Requirement, error) {
|
||||||
if err := validateLabelKey(key); err != nil {
|
if err := validateLabelKey(key); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -128,8 +131,8 @@ func NewRequirement(key string, op selection.Operator, vals sets.String) (*Requi
|
|||||||
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")
|
||||||
}
|
}
|
||||||
for val := range vals {
|
for i := range vals {
|
||||||
if _, err := strconv.ParseInt(val, 10, 64); err != nil {
|
if _, err := strconv.ParseInt(vals[i], 10, 64); err != nil {
|
||||||
return nil, fmt.Errorf("for 'Gt', 'Lt' operators, the value must be an integer")
|
return nil, fmt.Errorf("for 'Gt', 'Lt' operators, the value must be an integer")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -137,14 +140,24 @@ func NewRequirement(key string, op selection.Operator, vals sets.String) (*Requi
|
|||||||
return nil, fmt.Errorf("operator '%v' is not recognized", op)
|
return nil, fmt.Errorf("operator '%v' is not recognized", op)
|
||||||
}
|
}
|
||||||
|
|
||||||
for v := range vals {
|
for i := range vals {
|
||||||
if err := validateLabelValue(v); err != nil {
|
if err := validateLabelValue(vals[i]); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
sort.Strings(vals)
|
||||||
return &Requirement{key: key, operator: op, strValues: vals}, nil
|
return &Requirement{key: key, operator: op, strValues: vals}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *Requirement) hasValue(value string) bool {
|
||||||
|
for i := range r.strValues {
|
||||||
|
if r.strValues[i] == value {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// Matches returns true if the Requirement matches the input Labels.
|
// Matches returns true if the Requirement matches the input Labels.
|
||||||
// There is a match in the following cases:
|
// There is a match in the following cases:
|
||||||
// (1) The operator is Exists and Labels has the Requirement's key.
|
// (1) The operator is Exists and Labels has the Requirement's key.
|
||||||
@ -162,12 +175,12 @@ func (r *Requirement) Matches(ls Labels) bool {
|
|||||||
if !ls.Has(r.key) {
|
if !ls.Has(r.key) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return r.strValues.Has(ls.Get(r.key))
|
return r.hasValue(ls.Get(r.key))
|
||||||
case selection.NotIn, selection.NotEquals:
|
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.hasValue(ls.Get(r.key))
|
||||||
case selection.Exists:
|
case selection.Exists:
|
||||||
return ls.Has(r.key)
|
return ls.Has(r.key)
|
||||||
case selection.DoesNotExist:
|
case selection.DoesNotExist:
|
||||||
@ -189,10 +202,10 @@ func (r *Requirement) Matches(ls Labels) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var rValue int64
|
var rValue int64
|
||||||
for strValue := range r.strValues {
|
for i := range r.strValues {
|
||||||
rValue, err = strconv.ParseInt(strValue, 10, 64)
|
rValue, err = strconv.ParseInt(r.strValues[i], 10, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
glog.V(10).Infof("ParseInt failed for value %+v in requirement %#v, for 'Gt', 'Lt' operators, the value must be an integer", strValue, r)
|
glog.V(10).Infof("ParseInt failed for value %+v in requirement %#v, for 'Gt', 'Lt' operators, the value must be an integer", r.strValues[i], r)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -210,8 +223,8 @@ func (r *Requirement) Operator() selection.Operator {
|
|||||||
}
|
}
|
||||||
func (r *Requirement) Values() sets.String {
|
func (r *Requirement) Values() sets.String {
|
||||||
ret := sets.String{}
|
ret := sets.String{}
|
||||||
for k := range r.strValues {
|
for i := range r.strValues {
|
||||||
ret.Insert(k)
|
ret.Insert(r.strValues[i])
|
||||||
}
|
}
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
@ -258,9 +271,9 @@ func (r *Requirement) String() string {
|
|||||||
buffer.WriteString("(")
|
buffer.WriteString("(")
|
||||||
}
|
}
|
||||||
if len(r.strValues) == 1 {
|
if len(r.strValues) == 1 {
|
||||||
buffer.WriteString(r.strValues.List()[0])
|
buffer.WriteString(r.strValues[0])
|
||||||
} else { // only > 1 since == 0 prohibited by NewRequirement
|
} else { // only > 1 since == 0 prohibited by NewRequirement
|
||||||
buffer.WriteString(strings.Join(r.strValues.List(), ","))
|
buffer.WriteString(strings.Join(r.strValues, ","))
|
||||||
}
|
}
|
||||||
|
|
||||||
switch r.operator {
|
switch r.operator {
|
||||||
@ -561,7 +574,7 @@ func (p *Parser) parseRequirement() (*Requirement, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if operator == selection.Exists || operator == selection.DoesNotExist { // 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, []string{})
|
||||||
}
|
}
|
||||||
operator, err = p.parseOperator()
|
operator, err = p.parseOperator()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -577,7 +590,7 @@ func (p *Parser) parseRequirement() (*Requirement, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return NewRequirement(key, operator, values)
|
return NewRequirement(key, operator, values.List())
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -784,7 +797,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, selection.Equals, sets.NewString(value)); err != nil {
|
if r, err := NewRequirement(label, selection.Equals, []string{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 {
|
||||||
@ -805,7 +818,7 @@ func SelectorFromValidatedSet(ls Set) Selector {
|
|||||||
}
|
}
|
||||||
var requirements internalSelector
|
var requirements internalSelector
|
||||||
for label, value := range ls {
|
for label, value := range ls {
|
||||||
requirements = append(requirements, Requirement{key: label, operator: selection.Equals, strValues: sets.NewString(value)})
|
requirements = append(requirements, Requirement{key: label, operator: selection.Equals, strValues: []string{value}})
|
||||||
}
|
}
|
||||||
// sort to have deterministic string representation
|
// sort to have deterministic string representation
|
||||||
sort.Sort(ByKey(requirements))
|
sort.Sort(ByKey(requirements))
|
||||||
|
@ -320,7 +320,7 @@ func TestRequirementConstructor(t *testing.T) {
|
|||||||
{strings.Repeat("a", 254), selection.Exists, 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.List()); err == nil && !rc.Success {
|
||||||
t.Errorf("expected error with key:%#v op:%v vals:%v, got no error", rc.Key, rc.Op, rc.Vals)
|
t.Errorf("expected error with key:%#v op:%v vals:%v, got no error", rc.Key, rc.Op, rc.Vals)
|
||||||
} else if err != nil && rc.Success {
|
} else if err != nil && rc.Success {
|
||||||
t.Errorf("expected no error with key:%#v op:%v vals:%v, got:%v", rc.Key, rc.Op, rc.Vals, err)
|
t.Errorf("expected no error with key:%#v op:%v vals:%v, got:%v", rc.Key, rc.Op, rc.Vals, err)
|
||||||
@ -525,7 +525,7 @@ func TestSetSelectorParser(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func getRequirement(key string, op selection.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.List())
|
||||||
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)
|
||||||
return Requirement{}
|
return Requirement{}
|
||||||
@ -548,22 +548,22 @@ func TestAdd(t *testing.T) {
|
|||||||
"key",
|
"key",
|
||||||
selection.In,
|
selection.In,
|
||||||
[]string{"value"},
|
[]string{"value"},
|
||||||
internalSelector{Requirement{"key", selection.In, sets.NewString("value")}},
|
internalSelector{Requirement{"key", selection.In, []string{"value"}}},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"keyEqualsOperator",
|
"keyEqualsOperator",
|
||||||
internalSelector{Requirement{"key", selection.In, sets.NewString("value")}},
|
internalSelector{Requirement{"key", selection.In, []string{"value"}}},
|
||||||
"key2",
|
"key2",
|
||||||
selection.Equals,
|
selection.Equals,
|
||||||
[]string{"value2"},
|
[]string{"value2"},
|
||||||
internalSelector{
|
internalSelector{
|
||||||
Requirement{"key", selection.In, sets.NewString("value")},
|
Requirement{"key", selection.In, []string{"value"}},
|
||||||
Requirement{"key2", selection.Equals, sets.NewString("value2")},
|
Requirement{"key2", selection.Equals, []string{"value2"}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, ts := range testCases {
|
for _, ts := range testCases {
|
||||||
req, err := NewRequirement(ts.key, ts.operator, sets.NewString(ts.values...))
|
req, err := NewRequirement(ts.key, ts.operator, ts.values)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("%s - Unable to create labels.Requirement", ts.name)
|
t.Errorf("%s - Unable to create labels.Requirement", ts.name)
|
||||||
}
|
}
|
||||||
|
@ -43,7 +43,6 @@ import (
|
|||||||
etcdtesting "k8s.io/kubernetes/pkg/storage/etcd/testing"
|
etcdtesting "k8s.io/kubernetes/pkg/storage/etcd/testing"
|
||||||
"k8s.io/kubernetes/pkg/storage/storagebackend/factory"
|
"k8s.io/kubernetes/pkg/storage/storagebackend/factory"
|
||||||
storagetesting "k8s.io/kubernetes/pkg/storage/testing"
|
storagetesting "k8s.io/kubernetes/pkg/storage/testing"
|
||||||
"k8s.io/kubernetes/pkg/util/sets"
|
|
||||||
"k8s.io/kubernetes/pkg/util/validation/field"
|
"k8s.io/kubernetes/pkg/util/validation/field"
|
||||||
"k8s.io/kubernetes/pkg/util/wait"
|
"k8s.io/kubernetes/pkg/util/wait"
|
||||||
)
|
)
|
||||||
@ -109,7 +108,7 @@ func NewTestGenericStoreRegistry(t *testing.T) (factory.DestroyFunc, *Store) {
|
|||||||
func matchPodName(names ...string) storage.SelectionPredicate {
|
func matchPodName(names ...string) storage.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", selection.In, sets.NewString(names...))
|
l, err := labels.NewRequirement("name", selection.In, names)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic("Labels requirement must validate successfully")
|
panic("Labels requirement must validate successfully")
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user