mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-07 03:03:59 +00:00
apiextensions: fix validation error for status.storedVersions
This commit is contained in:
parent
d4fde1e92a
commit
cfcbce31a3
@ -30,8 +30,13 @@ import (
|
||||
celgo "github.com/google/cel-go/cel"
|
||||
|
||||
"k8s.io/apiextensions-apiserver/pkg/apihelpers"
|
||||
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
|
||||
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
||||
apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
|
||||
structuralschema "k8s.io/apiextensions-apiserver/pkg/apiserver/schema"
|
||||
"k8s.io/apiextensions-apiserver/pkg/apiserver/schema/cel"
|
||||
structuraldefaulting "k8s.io/apiextensions-apiserver/pkg/apiserver/schema/defaulting"
|
||||
apiservervalidation "k8s.io/apiextensions-apiserver/pkg/apiserver/validation"
|
||||
apiequality "k8s.io/apimachinery/pkg/api/equality"
|
||||
genericvalidation "k8s.io/apimachinery/pkg/api/validation"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
@ -41,12 +46,6 @@ import (
|
||||
apiservercel "k8s.io/apiserver/pkg/cel"
|
||||
"k8s.io/apiserver/pkg/cel/environment"
|
||||
"k8s.io/apiserver/pkg/util/webhook"
|
||||
|
||||
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
|
||||
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
||||
apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
|
||||
structuralschema "k8s.io/apiextensions-apiserver/pkg/apiserver/schema"
|
||||
apiservervalidation "k8s.io/apiextensions-apiserver/pkg/apiserver/validation"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -219,7 +218,7 @@ func ValidateCustomResourceDefinitionStoredVersions(storedVersions []string, ver
|
||||
for _, v := range versions {
|
||||
_, ok := storedVersionsMap[v.Name]
|
||||
if v.Storage && !ok {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath, v, "must have the storage version "+v.Name))
|
||||
allErrs = append(allErrs, field.Invalid(fldPath, storedVersions, "must have the storage version "+v.Name))
|
||||
}
|
||||
if ok {
|
||||
delete(storedVersionsMap, v.Name)
|
||||
|
@ -26,8 +26,6 @@ import (
|
||||
|
||||
"github.com/google/cel-go/cel"
|
||||
|
||||
"k8s.io/utils/pointer"
|
||||
|
||||
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
|
||||
apiextensionsfuzzer "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/fuzzer"
|
||||
apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
|
||||
@ -42,12 +40,13 @@ import (
|
||||
"k8s.io/apimachinery/pkg/util/version"
|
||||
"k8s.io/apiserver/pkg/cel/environment"
|
||||
"k8s.io/apiserver/pkg/cel/library"
|
||||
"k8s.io/utils/pointer"
|
||||
)
|
||||
|
||||
type validationMatch struct {
|
||||
path *field.Path
|
||||
errorType field.ErrorType
|
||||
contains string
|
||||
path *field.Path
|
||||
errorType field.ErrorType
|
||||
containsString string
|
||||
}
|
||||
|
||||
func required(path ...string) validationMatch {
|
||||
@ -73,7 +72,12 @@ func forbidden(path ...string) validationMatch {
|
||||
}
|
||||
|
||||
func (v validationMatch) matches(err *field.Error) bool {
|
||||
return err.Type == v.errorType && err.Field == v.path.String() && strings.Contains(err.Error(), v.contains)
|
||||
return err.Type == v.errorType && err.Field == v.path.String() && strings.Contains(err.Error(), v.containsString)
|
||||
}
|
||||
|
||||
func (v validationMatch) contains(s string) validationMatch {
|
||||
v.containsString = s
|
||||
return v
|
||||
}
|
||||
|
||||
func strPtr(s string) *string { return &s }
|
||||
@ -9436,6 +9440,100 @@ func TestSchemaHasDefaults(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateCustomResourceDefinitionStoredVersions(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
versions []string
|
||||
storageVersion string
|
||||
storedVersions []string
|
||||
errors []validationMatch
|
||||
}{
|
||||
{
|
||||
name: "one version",
|
||||
versions: []string{"v1"},
|
||||
storageVersion: "v1",
|
||||
storedVersions: []string{"v1"},
|
||||
},
|
||||
{
|
||||
name: "no stored version",
|
||||
versions: []string{"v1"},
|
||||
storageVersion: "v1",
|
||||
storedVersions: []string{},
|
||||
errors: []validationMatch{
|
||||
invalid("status", "storedVersions").contains("Invalid value: []string{}: must have at least one stored version"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "many versions",
|
||||
versions: []string{"v1alpha", "v1beta1", "v1"},
|
||||
storageVersion: "v1",
|
||||
storedVersions: []string{"v1alpha", "v1"},
|
||||
},
|
||||
{
|
||||
name: "missing stored versions",
|
||||
versions: []string{"v1beta1", "v1"},
|
||||
storageVersion: "v1",
|
||||
storedVersions: []string{"v1alpha", "v1beta1", "v1"},
|
||||
errors: []validationMatch{
|
||||
invalidIndex(0, "status", "storedVersions").contains("Invalid value: \"v1alpha\": must appear in spec.versions"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "missing storage versions",
|
||||
versions: []string{"v1alpha", "v1beta1", "v1"},
|
||||
storageVersion: "v1",
|
||||
storedVersions: []string{"v1alpha", "v1beta1"},
|
||||
errors: []validationMatch{
|
||||
invalid("status", "storedVersions").contains("Invalid value: []string{\"v1alpha\", \"v1beta1\"}: must have the storage version v1"),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
crd := &apiextensions.CustomResourceDefinition{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "plural.group.com", ResourceVersion: "1"},
|
||||
Spec: apiextensions.CustomResourceDefinitionSpec{
|
||||
Group: "group.com",
|
||||
Scope: "Cluster",
|
||||
Names: apiextensions.CustomResourceDefinitionNames{Plural: "plural", Singular: "singular", Kind: "Plural", ListKind: "PluralList"},
|
||||
},
|
||||
Status: apiextensions.CustomResourceDefinitionStatus{StoredVersions: tc.storedVersions},
|
||||
}
|
||||
for _, version := range tc.versions {
|
||||
v := apiextensions.CustomResourceDefinitionVersion{Name: version}
|
||||
if tc.storageVersion == version {
|
||||
v.Storage = true
|
||||
}
|
||||
crd.Spec.Versions = append(crd.Spec.Versions, v)
|
||||
}
|
||||
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
errs := ValidateCustomResourceDefinitionStoredVersions(crd.Status.StoredVersions, crd.Spec.Versions, field.NewPath("status", "storedVersions"))
|
||||
seenErrs := make([]bool, len(errs))
|
||||
|
||||
for _, expectedError := range tc.errors {
|
||||
found := false
|
||||
for i, err := range errs {
|
||||
if expectedError.matches(err) && !seenErrs[i] {
|
||||
found = true
|
||||
seenErrs[i] = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
t.Errorf("expected %v at %v, got %v", expectedError.errorType, expectedError.path.String(), errs)
|
||||
}
|
||||
}
|
||||
for i, seen := range seenErrs {
|
||||
if !seen {
|
||||
t.Errorf("unexpected error: %v", errs[i])
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkSchemaHas(b *testing.B) {
|
||||
scheme := runtime.NewScheme()
|
||||
codecs := serializer.NewCodecFactory(scheme)
|
||||
|
Loading…
Reference in New Issue
Block a user