Skip to content

Commit fe82462

Browse files
Zheaolik8s-publishing-bot
authored andcommitted
Add extra value validation for matchExpression field in LabelSelector
Kubernetes-commit: 0843c4dfcab93dd242044e51d6a2a21dc73d233e
1 parent 067949d commit fe82462

File tree

2 files changed

+118
-3
lines changed

2 files changed

+118
-3
lines changed

pkg/apis/meta/v1/validation/validation.go

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,19 +28,25 @@ import (
2828
"k8s.io/apimachinery/pkg/util/validation/field"
2929
)
3030

31-
func ValidateLabelSelector(ps *metav1.LabelSelector, fldPath *field.Path) field.ErrorList {
31+
// LabelSelectorValidationOptions is a struct that can be passed to ValidateLabelSelector to record the validate options
32+
type LabelSelectorValidationOptions struct {
33+
// Allow invalid label value in selector
34+
AllowInvalidLabelValueInSelector bool
35+
}
36+
37+
func ValidateLabelSelector(ps *metav1.LabelSelector, opts LabelSelectorValidationOptions, fldPath *field.Path) field.ErrorList {
3238
allErrs := field.ErrorList{}
3339
if ps == nil {
3440
return allErrs
3541
}
3642
allErrs = append(allErrs, ValidateLabels(ps.MatchLabels, fldPath.Child("matchLabels"))...)
3743
for i, expr := range ps.MatchExpressions {
38-
allErrs = append(allErrs, ValidateLabelSelectorRequirement(expr, fldPath.Child("matchExpressions").Index(i))...)
44+
allErrs = append(allErrs, ValidateLabelSelectorRequirement(expr, opts, fldPath.Child("matchExpressions").Index(i))...)
3945
}
4046
return allErrs
4147
}
4248

43-
func ValidateLabelSelectorRequirement(sr metav1.LabelSelectorRequirement, fldPath *field.Path) field.ErrorList {
49+
func ValidateLabelSelectorRequirement(sr metav1.LabelSelectorRequirement, opts LabelSelectorValidationOptions, fldPath *field.Path) field.ErrorList {
4450
allErrs := field.ErrorList{}
4551
switch sr.Operator {
4652
case metav1.LabelSelectorOpIn, metav1.LabelSelectorOpNotIn:
@@ -55,6 +61,13 @@ func ValidateLabelSelectorRequirement(sr metav1.LabelSelectorRequirement, fldPat
5561
allErrs = append(allErrs, field.Invalid(fldPath.Child("operator"), sr.Operator, "not a valid selector operator"))
5662
}
5763
allErrs = append(allErrs, ValidateLabelName(sr.Key, fldPath.Child("key"))...)
64+
if !opts.AllowInvalidLabelValueInSelector {
65+
for valueIndex, value := range sr.Values {
66+
for _, msg := range validation.IsValidLabelValue(value) {
67+
allErrs = append(allErrs, field.Invalid(fldPath.Child("values").Index(valueIndex), value, msg))
68+
}
69+
}
70+
}
5871
return allErrs
5972
}
6073

pkg/apis/meta/v1/validation/validation_test.go

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -427,6 +427,99 @@ func TestValidateConditions(t *testing.T) {
427427
}
428428
}
429429

430+
func TestLabelSelectorMatchExpression(t *testing.T) {
431+
// Success case
432+
testCases := []struct {
433+
name string
434+
labelSelector *metav1.LabelSelector
435+
wantErrorNumber int
436+
validateErrs func(t *testing.T, errs field.ErrorList)
437+
}{
438+
{
439+
name: "Valid LabelSelector",
440+
labelSelector: &metav1.LabelSelector{
441+
MatchExpressions: []metav1.LabelSelectorRequirement{
442+
{
443+
Key: "key",
444+
Operator: metav1.LabelSelectorOpIn,
445+
Values: []string{"value"},
446+
},
447+
},
448+
},
449+
wantErrorNumber: 0,
450+
validateErrs: nil,
451+
},
452+
{
453+
name: "MatchExpression's key name isn't valid",
454+
labelSelector: &metav1.LabelSelector{
455+
MatchExpressions: []metav1.LabelSelectorRequirement{
456+
{
457+
Key: "-key",
458+
Operator: metav1.LabelSelectorOpIn,
459+
Values: []string{"value"},
460+
},
461+
},
462+
},
463+
wantErrorNumber: 1,
464+
validateErrs: func(t *testing.T, errs field.ErrorList) {
465+
errMessage := "name part must consist of alphanumeric characters"
466+
if !partStringInErrorMessage(errs, errMessage) {
467+
t.Errorf("missing %q in\n%v", errMessage, errorsAsString(errs))
468+
}
469+
},
470+
},
471+
{
472+
name: "MatchExpression's operator isn't valid",
473+
labelSelector: &metav1.LabelSelector{
474+
MatchExpressions: []metav1.LabelSelectorRequirement{
475+
{
476+
Key: "key",
477+
Operator: "abc",
478+
Values: []string{"value"},
479+
},
480+
},
481+
},
482+
wantErrorNumber: 1,
483+
validateErrs: func(t *testing.T, errs field.ErrorList) {
484+
errMessage := "not a valid selector operator"
485+
if !partStringInErrorMessage(errs, errMessage) {
486+
t.Errorf("missing %q in\n%v", errMessage, errorsAsString(errs))
487+
}
488+
},
489+
},
490+
{
491+
name: "MatchExpression's value name isn't valid",
492+
labelSelector: &metav1.LabelSelector{
493+
MatchExpressions: []metav1.LabelSelectorRequirement{
494+
{
495+
Key: "key",
496+
Operator: metav1.LabelSelectorOpIn,
497+
Values: []string{"-value"},
498+
},
499+
},
500+
},
501+
wantErrorNumber: 1,
502+
validateErrs: func(t *testing.T, errs field.ErrorList) {
503+
errMessage := "a valid label must be an empty string or consist of"
504+
if !partStringInErrorMessage(errs, errMessage) {
505+
t.Errorf("missing %q in\n%v", errMessage, errorsAsString(errs))
506+
}
507+
},
508+
},
509+
}
510+
for index, testCase := range testCases {
511+
t.Run(testCase.name, func(t *testing.T) {
512+
allErrs := ValidateLabelSelector(testCase.labelSelector, LabelSelectorValidationOptions{false}, field.NewPath("labelSelector"))
513+
if len(allErrs) != testCase.wantErrorNumber {
514+
t.Errorf("case[%d]: expected failure", index)
515+
}
516+
if len(allErrs) >= 1 && testCase.validateErrs != nil {
517+
testCase.validateErrs(t, allErrs)
518+
}
519+
})
520+
}
521+
}
522+
430523
func hasError(errs field.ErrorList, needle string) bool {
431524
for _, curr := range errs {
432525
if curr.Error() == needle {
@@ -445,6 +538,15 @@ func hasPrefixError(errs field.ErrorList, prefix string) bool {
445538
return false
446539
}
447540

541+
func partStringInErrorMessage(errs field.ErrorList, prefix string) bool {
542+
for _, curr := range errs {
543+
if strings.Contains(curr.Error(), prefix) {
544+
return true
545+
}
546+
}
547+
return false
548+
}
549+
448550
func errorsAsString(errs field.ErrorList) string {
449551
messages := []string{}
450552
for _, curr := range errs {

0 commit comments

Comments
 (0)