Skip to content

Commit 7709b76

Browse files
stttsk8s-publishing-bot
authored andcommitted
apiextensions: fix validation error for status.storedVersions
Kubernetes-commit: cfcbce31a39d87e0ba30db995397e11eae334605
1 parent b69767a commit 7709b76

File tree

2 files changed

+110
-13
lines changed

2 files changed

+110
-13
lines changed

pkg/apis/apiextensions/validation/validation.go

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,13 @@ import (
3030
celgo "github.com/google/cel-go/cel"
3131

3232
"k8s.io/apiextensions-apiserver/pkg/apihelpers"
33+
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
34+
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
35+
apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
36+
structuralschema "k8s.io/apiextensions-apiserver/pkg/apiserver/schema"
3337
"k8s.io/apiextensions-apiserver/pkg/apiserver/schema/cel"
3438
structuraldefaulting "k8s.io/apiextensions-apiserver/pkg/apiserver/schema/defaulting"
39+
apiservervalidation "k8s.io/apiextensions-apiserver/pkg/apiserver/validation"
3540
apiequality "k8s.io/apimachinery/pkg/api/equality"
3641
genericvalidation "k8s.io/apimachinery/pkg/api/validation"
3742
"k8s.io/apimachinery/pkg/util/sets"
@@ -41,12 +46,6 @@ import (
4146
apiservercel "k8s.io/apiserver/pkg/cel"
4247
"k8s.io/apiserver/pkg/cel/environment"
4348
"k8s.io/apiserver/pkg/util/webhook"
44-
45-
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
46-
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
47-
apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
48-
structuralschema "k8s.io/apiextensions-apiserver/pkg/apiserver/schema"
49-
apiservervalidation "k8s.io/apiextensions-apiserver/pkg/apiserver/validation"
5049
)
5150

5251
var (
@@ -219,7 +218,7 @@ func ValidateCustomResourceDefinitionStoredVersions(storedVersions []string, ver
219218
for _, v := range versions {
220219
_, ok := storedVersionsMap[v.Name]
221220
if v.Storage && !ok {
222-
allErrs = append(allErrs, field.Invalid(fldPath, v, "must have the storage version "+v.Name))
221+
allErrs = append(allErrs, field.Invalid(fldPath, storedVersions, "must have the storage version "+v.Name))
223222
}
224223
if ok {
225224
delete(storedVersionsMap, v.Name)

pkg/apis/apiextensions/validation/validation_test.go

Lines changed: 104 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,6 @@ import (
2626

2727
"github.com/google/cel-go/cel"
2828

29-
"k8s.io/utils/pointer"
30-
3129
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
3230
apiextensionsfuzzer "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/fuzzer"
3331
apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
@@ -42,12 +40,13 @@ import (
4240
"k8s.io/apimachinery/pkg/util/version"
4341
"k8s.io/apiserver/pkg/cel/environment"
4442
"k8s.io/apiserver/pkg/cel/library"
43+
"k8s.io/utils/pointer"
4544
)
4645

4746
type validationMatch struct {
48-
path *field.Path
49-
errorType field.ErrorType
50-
contains string
47+
path *field.Path
48+
errorType field.ErrorType
49+
containsString string
5150
}
5251

5352
func required(path ...string) validationMatch {
@@ -73,7 +72,12 @@ func forbidden(path ...string) validationMatch {
7372
}
7473

7574
func (v validationMatch) matches(err *field.Error) bool {
76-
return err.Type == v.errorType && err.Field == v.path.String() && strings.Contains(err.Error(), v.contains)
75+
return err.Type == v.errorType && err.Field == v.path.String() && strings.Contains(err.Error(), v.containsString)
76+
}
77+
78+
func (v validationMatch) contains(s string) validationMatch {
79+
v.containsString = s
80+
return v
7781
}
7882

7983
func strPtr(s string) *string { return &s }
@@ -9436,6 +9440,100 @@ func TestSchemaHasDefaults(t *testing.T) {
94369440
}
94379441
}
94389442

9443+
func TestValidateCustomResourceDefinitionStoredVersions(t *testing.T) {
9444+
tests := []struct {
9445+
name string
9446+
versions []string
9447+
storageVersion string
9448+
storedVersions []string
9449+
errors []validationMatch
9450+
}{
9451+
{
9452+
name: "one version",
9453+
versions: []string{"v1"},
9454+
storageVersion: "v1",
9455+
storedVersions: []string{"v1"},
9456+
},
9457+
{
9458+
name: "no stored version",
9459+
versions: []string{"v1"},
9460+
storageVersion: "v1",
9461+
storedVersions: []string{},
9462+
errors: []validationMatch{
9463+
invalid("status", "storedVersions").contains("Invalid value: []string{}: must have at least one stored version"),
9464+
},
9465+
},
9466+
{
9467+
name: "many versions",
9468+
versions: []string{"v1alpha", "v1beta1", "v1"},
9469+
storageVersion: "v1",
9470+
storedVersions: []string{"v1alpha", "v1"},
9471+
},
9472+
{
9473+
name: "missing stored versions",
9474+
versions: []string{"v1beta1", "v1"},
9475+
storageVersion: "v1",
9476+
storedVersions: []string{"v1alpha", "v1beta1", "v1"},
9477+
errors: []validationMatch{
9478+
invalidIndex(0, "status", "storedVersions").contains("Invalid value: \"v1alpha\": must appear in spec.versions"),
9479+
},
9480+
},
9481+
{
9482+
name: "missing storage versions",
9483+
versions: []string{"v1alpha", "v1beta1", "v1"},
9484+
storageVersion: "v1",
9485+
storedVersions: []string{"v1alpha", "v1beta1"},
9486+
errors: []validationMatch{
9487+
invalid("status", "storedVersions").contains("Invalid value: []string{\"v1alpha\", \"v1beta1\"}: must have the storage version v1"),
9488+
},
9489+
},
9490+
}
9491+
9492+
for _, tc := range tests {
9493+
crd := &apiextensions.CustomResourceDefinition{
9494+
ObjectMeta: metav1.ObjectMeta{Name: "plural.group.com", ResourceVersion: "1"},
9495+
Spec: apiextensions.CustomResourceDefinitionSpec{
9496+
Group: "group.com",
9497+
Scope: "Cluster",
9498+
Names: apiextensions.CustomResourceDefinitionNames{Plural: "plural", Singular: "singular", Kind: "Plural", ListKind: "PluralList"},
9499+
},
9500+
Status: apiextensions.CustomResourceDefinitionStatus{StoredVersions: tc.storedVersions},
9501+
}
9502+
for _, version := range tc.versions {
9503+
v := apiextensions.CustomResourceDefinitionVersion{Name: version}
9504+
if tc.storageVersion == version {
9505+
v.Storage = true
9506+
}
9507+
crd.Spec.Versions = append(crd.Spec.Versions, v)
9508+
}
9509+
9510+
t.Run(tc.name, func(t *testing.T) {
9511+
errs := ValidateCustomResourceDefinitionStoredVersions(crd.Status.StoredVersions, crd.Spec.Versions, field.NewPath("status", "storedVersions"))
9512+
seenErrs := make([]bool, len(errs))
9513+
9514+
for _, expectedError := range tc.errors {
9515+
found := false
9516+
for i, err := range errs {
9517+
if expectedError.matches(err) && !seenErrs[i] {
9518+
found = true
9519+
seenErrs[i] = true
9520+
break
9521+
}
9522+
}
9523+
9524+
if !found {
9525+
t.Errorf("expected %v at %v, got %v", expectedError.errorType, expectedError.path.String(), errs)
9526+
}
9527+
}
9528+
for i, seen := range seenErrs {
9529+
if !seen {
9530+
t.Errorf("unexpected error: %v", errs[i])
9531+
}
9532+
}
9533+
})
9534+
}
9535+
}
9536+
94399537
func BenchmarkSchemaHas(b *testing.B) {
94409538
scheme := runtime.NewScheme()
94419539
codecs := serializer.NewCodecFactory(scheme)

0 commit comments

Comments
 (0)