mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-27 05:27:21 +00:00
apimachinery/pkg/labels: add SelectorFromSet
While rambling again about how unsafe labels.SelectorFromSet is as it just returns an empty selector that matches everything when it encounters a parsing error, I noticed that we do not even have a safe alternate. This commit fixes that by adding a `ValidatedSelectorFromSet` func that either returns a Selector or an error. It also changes SelectorFromSet to use SelectorFromValidatedSet under the hood, so invalid Sets are send to the server and rejected there, rather than silently doing the wrong thing by using am empty Selector.
This commit is contained in:
parent
ed00f42848
commit
bec6f08c58
@ -57,14 +57,22 @@ func (ls Set) Get(label string) string {
|
|||||||
return ls[label]
|
return ls[label]
|
||||||
}
|
}
|
||||||
|
|
||||||
// AsSelector converts labels into a selectors.
|
// AsSelector converts labels into a selectors. It does not
|
||||||
|
// perform any validation, which means the server will reject
|
||||||
|
// the request if the Set contains invalid values.
|
||||||
func (ls Set) AsSelector() Selector {
|
func (ls Set) AsSelector() Selector {
|
||||||
return SelectorFromSet(ls)
|
return SelectorFromSet(ls)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AsValidatedSelector converts labels into a selectors.
|
||||||
|
// The Set is validated client-side, which allows to catch errors early.
|
||||||
|
func (ls Set) AsValidatedSelector() (Selector, error) {
|
||||||
|
return ValidatedSelectorFromSet(ls)
|
||||||
|
}
|
||||||
|
|
||||||
// AsSelectorPreValidated converts labels into a selector, but
|
// AsSelectorPreValidated converts labels into a selector, but
|
||||||
// assumes that labels are already validated and thus don't
|
// assumes that labels are already validated and thus doesn't
|
||||||
// preform any validation.
|
// perform any validation.
|
||||||
// According to our measurements this is significantly faster
|
// According to our measurements this is significantly faster
|
||||||
// in codepaths that matter at high scale.
|
// in codepaths that matter at high scale.
|
||||||
func (ls Set) AsSelectorPreValidated() Selector {
|
func (ls Set) AsSelectorPreValidated() Selector {
|
||||||
|
@ -869,23 +869,30 @@ func validateLabelValue(k, v string) error {
|
|||||||
|
|
||||||
// SelectorFromSet returns a Selector which will match exactly the given Set. A
|
// SelectorFromSet returns a Selector which will match exactly the given Set. A
|
||||||
// nil and empty Sets are considered equivalent to Everything().
|
// nil and empty Sets are considered equivalent to Everything().
|
||||||
|
// It does not perform any validation, which means the server will reject
|
||||||
|
// the request if the Set contains invalid values.
|
||||||
func SelectorFromSet(ls Set) Selector {
|
func SelectorFromSet(ls Set) Selector {
|
||||||
|
return SelectorFromValidatedSet(ls)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidatedSelectorFromSet returns a Selector which will match exactly the given Set. A
|
||||||
|
// nil and empty Sets are considered equivalent to Everything().
|
||||||
|
// The Set is validated client-side, which allows to catch errors early.
|
||||||
|
func ValidatedSelectorFromSet(ls Set) (Selector, error) {
|
||||||
if ls == nil || len(ls) == 0 {
|
if ls == nil || len(ls) == 0 {
|
||||||
return internalSelector{}
|
return internalSelector{}, nil
|
||||||
}
|
}
|
||||||
requirements := make([]Requirement, 0, len(ls))
|
requirements := make([]Requirement, 0, len(ls))
|
||||||
for label, value := range ls {
|
for label, value := range ls {
|
||||||
r, err := NewRequirement(label, selection.Equals, []string{value})
|
r, err := NewRequirement(label, selection.Equals, []string{value})
|
||||||
if err == nil {
|
if err != nil {
|
||||||
requirements = append(requirements, *r)
|
return nil, err
|
||||||
} else {
|
|
||||||
//TODO: double check errors when input comes from serialization?
|
|
||||||
return internalSelector{}
|
|
||||||
}
|
}
|
||||||
|
requirements = append(requirements, *r)
|
||||||
}
|
}
|
||||||
// sort to have deterministic string representation
|
// sort to have deterministic string representation
|
||||||
sort.Sort(ByKey(requirements))
|
sort.Sort(ByKey(requirements))
|
||||||
return internalSelector(requirements)
|
return internalSelector(requirements), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SelectorFromValidatedSet returns a Selector which will match exactly the given Set.
|
// SelectorFromValidatedSet returns a Selector which will match exactly the given Set.
|
||||||
|
@ -17,6 +17,7 @@ limitations under the License.
|
|||||||
package labels
|
package labels
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
@ -708,3 +709,33 @@ func TestRequiresExactMatch(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestValidatedSelectorFromSet(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
input Set
|
||||||
|
expectedSelector internalSelector
|
||||||
|
expectedError error
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Simple Set, no error",
|
||||||
|
input: Set{"key": "val"},
|
||||||
|
expectedSelector: internalSelector([]Requirement{{key: "key", operator: selection.Equals, strValues: []string{"val"}}}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Invalid Set, value too long",
|
||||||
|
input: Set{"Key": "axahm2EJ8Phiephe2eixohbee9eGeiyees1thuozi1xoh0GiuH3diewi8iem7Nui"},
|
||||||
|
expectedError: fmt.Errorf(`invalid label value: "axahm2EJ8Phiephe2eixohbee9eGeiyees1thuozi1xoh0GiuH3diewi8iem7Nui": at key: "Key": must be no more than 63 characters`),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range tests {
|
||||||
|
selector, err := ValidatedSelectorFromSet(tc.input)
|
||||||
|
if !reflect.DeepEqual(err, tc.expectedError) {
|
||||||
|
t.Fatalf("expected error %v, got error %v", tc.expectedError, err)
|
||||||
|
}
|
||||||
|
if err == nil && !reflect.DeepEqual(selector, tc.expectedSelector) {
|
||||||
|
t.Errorf("expected selector %v, got selector %v", tc.expectedSelector, selector)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user