mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-24 12:15:52 +00:00
CRD versioning validation and defaulting
This commit is contained in:
parent
10c48ae510
commit
8a39e5381c
@ -17,6 +17,7 @@ limitations under the License.
|
||||
package apiextensions
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
@ -116,3 +117,33 @@ func CRDRemoveFinalizer(crd *CustomResourceDefinition, needle string) {
|
||||
}
|
||||
crd.Finalizers = newFinalizers
|
||||
}
|
||||
|
||||
// HasServedCRDVersion returns true if `version` is in the list of CRD's versions and the Served flag is set.
|
||||
func HasServedCRDVersion(crd *CustomResourceDefinition, version string) bool {
|
||||
for _, v := range crd.Spec.Versions {
|
||||
if v.Name == version {
|
||||
return v.Served
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// GetCRDStorageVersion returns the storage version for given CRD.
|
||||
func GetCRDStorageVersion(crd *CustomResourceDefinition) (string, error) {
|
||||
for _, v := range crd.Spec.Versions {
|
||||
if v.Storage {
|
||||
return v.Name, nil
|
||||
}
|
||||
}
|
||||
// This should not happened if crd is valid
|
||||
return "", fmt.Errorf("invalid CustomResourceDefinition, no storage version")
|
||||
}
|
||||
|
||||
func IsStoredVersion(crd *CustomResourceDefinition, version string) bool {
|
||||
for _, v := range crd.Status.StoredVersions {
|
||||
if version == v {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
@ -31,6 +31,14 @@ func addDefaultingFuncs(scheme *runtime.Scheme) error {
|
||||
|
||||
func SetDefaults_CustomResourceDefinition(obj *CustomResourceDefinition) {
|
||||
SetDefaults_CustomResourceDefinitionSpec(&obj.Spec)
|
||||
if len(obj.Status.StoredVersions) == 0 {
|
||||
for _, v := range obj.Spec.Versions {
|
||||
if v.Storage {
|
||||
obj.Status.StoredVersions = append(obj.Status.StoredVersions, v.Name)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func SetDefaults_CustomResourceDefinitionSpec(obj *CustomResourceDefinitionSpec) {
|
||||
@ -43,4 +51,16 @@ func SetDefaults_CustomResourceDefinitionSpec(obj *CustomResourceDefinitionSpec)
|
||||
if len(obj.Names.ListKind) == 0 && len(obj.Names.Kind) > 0 {
|
||||
obj.Names.ListKind = obj.Names.Kind + "List"
|
||||
}
|
||||
// If there is no list of versions, create on using deprecated Version field.
|
||||
if len(obj.Versions) == 0 && len(obj.Version) != 0 {
|
||||
obj.Versions = []CustomResourceDefinitionVersion{{
|
||||
Name: obj.Version,
|
||||
Storage: true,
|
||||
Served: true,
|
||||
}}
|
||||
}
|
||||
// For backward compatibility set the version field to the first item in versions list.
|
||||
if len(obj.Version) == 0 && len(obj.Versions) != 0 {
|
||||
obj.Version = obj.Versions[0].Name
|
||||
}
|
||||
}
|
||||
|
@ -45,6 +45,7 @@ func ValidateCustomResourceDefinition(obj *apiextensions.CustomResourceDefinitio
|
||||
allErrs := genericvalidation.ValidateObjectMeta(&obj.ObjectMeta, false, nameValidationFn, field.NewPath("metadata"))
|
||||
allErrs = append(allErrs, ValidateCustomResourceDefinitionSpec(&obj.Spec, field.NewPath("spec"))...)
|
||||
allErrs = append(allErrs, ValidateCustomResourceDefinitionStatus(&obj.Status, field.NewPath("status"))...)
|
||||
allErrs = append(allErrs, ValidateCustomResourceDefinitionStoredVersions(obj.Status.StoredVersions, obj.Spec.Versions, field.NewPath("status").Child("storedVersions"))...)
|
||||
return allErrs
|
||||
}
|
||||
|
||||
@ -53,6 +54,34 @@ func ValidateCustomResourceDefinitionUpdate(obj, oldObj *apiextensions.CustomRes
|
||||
allErrs := genericvalidation.ValidateObjectMetaUpdate(&obj.ObjectMeta, &oldObj.ObjectMeta, field.NewPath("metadata"))
|
||||
allErrs = append(allErrs, ValidateCustomResourceDefinitionSpecUpdate(&obj.Spec, &oldObj.Spec, apiextensions.IsCRDConditionTrue(oldObj, apiextensions.Established), field.NewPath("spec"))...)
|
||||
allErrs = append(allErrs, ValidateCustomResourceDefinitionStatus(&obj.Status, field.NewPath("status"))...)
|
||||
allErrs = append(allErrs, ValidateCustomResourceDefinitionStoredVersions(obj.Status.StoredVersions, obj.Spec.Versions, field.NewPath("status").Child("storedVersions"))...)
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// ValidateCustomResourceDefinitionStoredVersions statically validates
|
||||
func ValidateCustomResourceDefinitionStoredVersions(storedVersions []string, versions []apiextensions.CustomResourceDefinitionVersion, fldPath *field.Path) field.ErrorList {
|
||||
if len(storedVersions) == 0 {
|
||||
return field.ErrorList{field.Invalid(fldPath, storedVersions, "must have at least one stored version")}
|
||||
}
|
||||
allErrs := field.ErrorList{}
|
||||
storedVersionsMap := map[string]int{}
|
||||
for i, v := range storedVersions {
|
||||
storedVersionsMap[v] = i
|
||||
}
|
||||
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))
|
||||
}
|
||||
if ok {
|
||||
delete(storedVersionsMap, v.Name)
|
||||
}
|
||||
}
|
||||
|
||||
for v, i := range storedVersionsMap {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Index(i), v, "must appear in spec.versions"))
|
||||
}
|
||||
|
||||
return allErrs
|
||||
}
|
||||
|
||||
@ -75,12 +104,6 @@ func ValidateCustomResourceDefinitionSpec(spec *apiextensions.CustomResourceDefi
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("group"), spec.Group, "should be a domain with at least one dot"))
|
||||
}
|
||||
|
||||
if len(spec.Version) == 0 {
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("version"), ""))
|
||||
} else if errs := validationutil.IsDNS1035Label(spec.Version); len(errs) > 0 {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("version"), spec.Version, strings.Join(errs, ",")))
|
||||
}
|
||||
|
||||
switch spec.Scope {
|
||||
case "":
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("scope"), ""))
|
||||
@ -89,6 +112,37 @@ func ValidateCustomResourceDefinitionSpec(spec *apiextensions.CustomResourceDefi
|
||||
allErrs = append(allErrs, field.NotSupported(fldPath.Child("scope"), spec.Scope, []string{string(apiextensions.ClusterScoped), string(apiextensions.NamespaceScoped)}))
|
||||
}
|
||||
|
||||
storageFlagCount := 0
|
||||
versionsMap := map[string]bool{}
|
||||
uniqueNames := true
|
||||
for i, version := range spec.Versions {
|
||||
if version.Storage {
|
||||
storageFlagCount++
|
||||
}
|
||||
if versionsMap[version.Name] {
|
||||
uniqueNames = false
|
||||
} else {
|
||||
versionsMap[version.Name] = true
|
||||
}
|
||||
if errs := validationutil.IsDNS1035Label(version.Name); len(errs) > 0 {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("versions").Index(i).Child("name"), spec.Versions[i].Name, strings.Join(errs, ",")))
|
||||
}
|
||||
}
|
||||
if !uniqueNames {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("versions"), spec.Versions, "must contain unique version names"))
|
||||
}
|
||||
if storageFlagCount != 1 {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("versions"), spec.Versions, "must have exactly one version marked as storage version"))
|
||||
}
|
||||
if len(spec.Version) != 0 {
|
||||
if errs := validationutil.IsDNS1035Label(spec.Version); len(errs) > 0 {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("version"), spec.Version, strings.Join(errs, ",")))
|
||||
}
|
||||
if len(spec.Versions) >= 1 && spec.Versions[0].Name != spec.Version {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("version"), spec.Version, "must match the first version in spec.versions"))
|
||||
}
|
||||
}
|
||||
|
||||
// in addition to the basic name restrictions, some names are required for spec, but not for status
|
||||
if len(spec.Names.Plural) == 0 {
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("names", "plural"), ""))
|
||||
@ -130,7 +184,6 @@ func ValidateCustomResourceDefinitionSpecUpdate(spec, oldSpec *apiextensions.Cus
|
||||
|
||||
if established {
|
||||
// these effect the storage and cannot be changed therefore
|
||||
allErrs = append(allErrs, genericvalidation.ValidateImmutableField(spec.Version, oldSpec.Version, fldPath.Child("version"))...)
|
||||
allErrs = append(allErrs, genericvalidation.ValidateImmutableField(spec.Scope, oldSpec.Scope, fldPath.Child("scope"))...)
|
||||
allErrs = append(allErrs, genericvalidation.ValidateImmutableField(spec.Names.Kind, oldSpec.Names.Kind, fldPath.Child("names", "kind"))...)
|
||||
}
|
||||
|
@ -19,10 +19,9 @@ package validation
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
|
||||
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
|
||||
)
|
||||
|
||||
type validationMatch struct {
|
||||
@ -51,11 +50,150 @@ func (v validationMatch) matches(err *field.Error) bool {
|
||||
}
|
||||
|
||||
func TestValidateCustomResourceDefinition(t *testing.T) {
|
||||
singleVersionList := []apiextensions.CustomResourceDefinitionVersion{
|
||||
{
|
||||
Name: "version",
|
||||
Served: true,
|
||||
Storage: true,
|
||||
},
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
resource *apiextensions.CustomResourceDefinition
|
||||
errors []validationMatch
|
||||
}{
|
||||
{
|
||||
name: "no_storage_version",
|
||||
resource: &apiextensions.CustomResourceDefinition{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "plural.group.com"},
|
||||
Spec: apiextensions.CustomResourceDefinitionSpec{
|
||||
Group: "group.com",
|
||||
Scope: apiextensions.ResourceScope("Cluster"),
|
||||
Names: apiextensions.CustomResourceDefinitionNames{
|
||||
Plural: "plural",
|
||||
Singular: "singular",
|
||||
Kind: "Plural",
|
||||
ListKind: "PluralList",
|
||||
},
|
||||
Versions: []apiextensions.CustomResourceDefinitionVersion{
|
||||
{
|
||||
Name: "version",
|
||||
Served: true,
|
||||
Storage: false,
|
||||
},
|
||||
{
|
||||
Name: "version2",
|
||||
Served: true,
|
||||
Storage: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
Status: apiextensions.CustomResourceDefinitionStatus{
|
||||
StoredVersions: []string{"version"},
|
||||
},
|
||||
},
|
||||
errors: []validationMatch{
|
||||
invalid("spec", "versions"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "multiple_storage_version",
|
||||
resource: &apiextensions.CustomResourceDefinition{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "plural.group.com"},
|
||||
Spec: apiextensions.CustomResourceDefinitionSpec{
|
||||
Group: "group.com",
|
||||
Scope: apiextensions.ResourceScope("Cluster"),
|
||||
Names: apiextensions.CustomResourceDefinitionNames{
|
||||
Plural: "plural",
|
||||
Singular: "singular",
|
||||
Kind: "Plural",
|
||||
ListKind: "PluralList",
|
||||
},
|
||||
Versions: []apiextensions.CustomResourceDefinitionVersion{
|
||||
{
|
||||
Name: "version",
|
||||
Served: true,
|
||||
Storage: true,
|
||||
},
|
||||
{
|
||||
Name: "version2",
|
||||
Served: true,
|
||||
Storage: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
Status: apiextensions.CustomResourceDefinitionStatus{
|
||||
StoredVersions: []string{"version"},
|
||||
},
|
||||
},
|
||||
errors: []validationMatch{
|
||||
invalid("spec", "versions"),
|
||||
invalid("status", "storedVersions"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "missing_storage_version_in_stored_versions",
|
||||
resource: &apiextensions.CustomResourceDefinition{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "plural.group.com"},
|
||||
Spec: apiextensions.CustomResourceDefinitionSpec{
|
||||
Group: "group.com",
|
||||
Scope: apiextensions.ResourceScope("Cluster"),
|
||||
Names: apiextensions.CustomResourceDefinitionNames{
|
||||
Plural: "plural",
|
||||
Singular: "singular",
|
||||
Kind: "Plural",
|
||||
ListKind: "PluralList",
|
||||
},
|
||||
Versions: []apiextensions.CustomResourceDefinitionVersion{
|
||||
{
|
||||
Name: "version",
|
||||
Served: true,
|
||||
Storage: false,
|
||||
},
|
||||
{
|
||||
Name: "version2",
|
||||
Served: true,
|
||||
Storage: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
Status: apiextensions.CustomResourceDefinitionStatus{
|
||||
StoredVersions: []string{"version"},
|
||||
},
|
||||
},
|
||||
errors: []validationMatch{
|
||||
invalid("status", "storedVersions"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "empty_stored_version",
|
||||
resource: &apiextensions.CustomResourceDefinition{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "plural.group.com"},
|
||||
Spec: apiextensions.CustomResourceDefinitionSpec{
|
||||
Group: "group.com",
|
||||
Scope: apiextensions.ResourceScope("Cluster"),
|
||||
Names: apiextensions.CustomResourceDefinitionNames{
|
||||
Plural: "plural",
|
||||
Singular: "singular",
|
||||
Kind: "Plural",
|
||||
ListKind: "PluralList",
|
||||
},
|
||||
Versions: []apiextensions.CustomResourceDefinitionVersion{
|
||||
{
|
||||
Name: "version",
|
||||
Served: true,
|
||||
Storage: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
Status: apiextensions.CustomResourceDefinitionStatus{
|
||||
StoredVersions: []string{},
|
||||
},
|
||||
},
|
||||
errors: []validationMatch{
|
||||
invalid("status", "storedVersions"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "mismatched name",
|
||||
resource: &apiextensions.CustomResourceDefinition{
|
||||
@ -68,8 +206,9 @@ func TestValidateCustomResourceDefinition(t *testing.T) {
|
||||
},
|
||||
},
|
||||
errors: []validationMatch{
|
||||
invalid("status", "storedVersions"),
|
||||
invalid("metadata", "name"),
|
||||
required("spec", "version"),
|
||||
invalid("spec", "versions"),
|
||||
required("spec", "scope"),
|
||||
required("spec", "names", "singular"),
|
||||
required("spec", "names", "kind"),
|
||||
@ -82,9 +221,10 @@ func TestValidateCustomResourceDefinition(t *testing.T) {
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "plural.group.com"},
|
||||
},
|
||||
errors: []validationMatch{
|
||||
invalid("status", "storedVersions"),
|
||||
invalid("metadata", "name"),
|
||||
invalid("spec", "versions"),
|
||||
required("spec", "group"),
|
||||
required("spec", "version"),
|
||||
required("spec", "scope"),
|
||||
required("spec", "names", "plural"),
|
||||
required("spec", "names", "singular"),
|
||||
@ -117,9 +257,9 @@ func TestValidateCustomResourceDefinition(t *testing.T) {
|
||||
},
|
||||
},
|
||||
errors: []validationMatch{
|
||||
invalid("status", "storedVersions"),
|
||||
invalid("metadata", "name"),
|
||||
invalid("spec", "group"),
|
||||
invalid("spec", "version"),
|
||||
unsupported("spec", "scope"),
|
||||
invalid("spec", "names", "plural"),
|
||||
invalid("spec", "names", "singular"),
|
||||
@ -131,6 +271,8 @@ func TestValidateCustomResourceDefinition(t *testing.T) {
|
||||
invalid("status", "acceptedNames", "kind"),
|
||||
invalid("status", "acceptedNames", "listKind"), // invalid format
|
||||
invalid("status", "acceptedNames", "listKind"), // kind == listKind
|
||||
invalid("spec", "versions"),
|
||||
invalid("spec", "version"),
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -138,8 +280,9 @@ func TestValidateCustomResourceDefinition(t *testing.T) {
|
||||
resource: &apiextensions.CustomResourceDefinition{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "plural.group"},
|
||||
Spec: apiextensions.CustomResourceDefinitionSpec{
|
||||
Group: "group.c(*&om",
|
||||
Version: "version",
|
||||
Group: "group.c(*&om",
|
||||
Version: "version",
|
||||
Versions: singleVersionList,
|
||||
Names: apiextensions.CustomResourceDefinitionNames{
|
||||
Plural: "plural",
|
||||
Singular: "singular",
|
||||
@ -154,6 +297,7 @@ func TestValidateCustomResourceDefinition(t *testing.T) {
|
||||
Kind: "matching",
|
||||
ListKind: "matching",
|
||||
},
|
||||
StoredVersions: []string{"version"},
|
||||
},
|
||||
},
|
||||
errors: []validationMatch{
|
||||
@ -169,9 +313,10 @@ func TestValidateCustomResourceDefinition(t *testing.T) {
|
||||
resource: &apiextensions.CustomResourceDefinition{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "plural.group.com"},
|
||||
Spec: apiextensions.CustomResourceDefinitionSpec{
|
||||
Group: "group.com",
|
||||
Version: "version",
|
||||
Scope: apiextensions.NamespaceScoped,
|
||||
Group: "group.com",
|
||||
Version: "version",
|
||||
Versions: singleVersionList,
|
||||
Scope: apiextensions.NamespaceScoped,
|
||||
Names: apiextensions.CustomResourceDefinitionNames{
|
||||
Plural: "plural",
|
||||
Singular: "singular",
|
||||
@ -187,6 +332,9 @@ func TestValidateCustomResourceDefinition(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
Status: apiextensions.CustomResourceDefinitionStatus{
|
||||
StoredVersions: []string{"version"},
|
||||
},
|
||||
},
|
||||
errors: []validationMatch{
|
||||
forbidden("spec", "validation", "openAPIV3Schema", "additionalProperties"),
|
||||
@ -197,9 +345,10 @@ func TestValidateCustomResourceDefinition(t *testing.T) {
|
||||
resource: &apiextensions.CustomResourceDefinition{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "plural.group.com"},
|
||||
Spec: apiextensions.CustomResourceDefinitionSpec{
|
||||
Group: "group.com",
|
||||
Version: "version",
|
||||
Scope: apiextensions.NamespaceScoped,
|
||||
Group: "group.com",
|
||||
Version: "version",
|
||||
Versions: singleVersionList,
|
||||
Scope: apiextensions.NamespaceScoped,
|
||||
Names: apiextensions.CustomResourceDefinitionNames{
|
||||
Plural: "plural",
|
||||
Singular: "singular",
|
||||
@ -217,6 +366,9 @@ func TestValidateCustomResourceDefinition(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
Status: apiextensions.CustomResourceDefinitionStatus{
|
||||
StoredVersions: []string{"version"},
|
||||
},
|
||||
},
|
||||
errors: []validationMatch{},
|
||||
},
|
||||
@ -266,7 +418,14 @@ func TestValidateCustomResourceDefinitionUpdate(t *testing.T) {
|
||||
Spec: apiextensions.CustomResourceDefinitionSpec{
|
||||
Group: "group.com",
|
||||
Version: "version",
|
||||
Scope: apiextensions.ResourceScope("Cluster"),
|
||||
Versions: []apiextensions.CustomResourceDefinitionVersion{
|
||||
{
|
||||
Name: "version",
|
||||
Served: true,
|
||||
Storage: true,
|
||||
},
|
||||
},
|
||||
Scope: apiextensions.ResourceScope("Cluster"),
|
||||
Names: apiextensions.CustomResourceDefinitionNames{
|
||||
Plural: "plural",
|
||||
Singular: "singular",
|
||||
@ -291,7 +450,14 @@ func TestValidateCustomResourceDefinitionUpdate(t *testing.T) {
|
||||
Spec: apiextensions.CustomResourceDefinitionSpec{
|
||||
Group: "group.com",
|
||||
Version: "version",
|
||||
Scope: apiextensions.ResourceScope("Cluster"),
|
||||
Versions: []apiextensions.CustomResourceDefinitionVersion{
|
||||
{
|
||||
Name: "version",
|
||||
Served: true,
|
||||
Storage: true,
|
||||
},
|
||||
},
|
||||
Scope: apiextensions.ResourceScope("Cluster"),
|
||||
Names: apiextensions.CustomResourceDefinitionNames{
|
||||
Plural: "plural",
|
||||
Singular: "singular",
|
||||
@ -306,6 +472,7 @@ func TestValidateCustomResourceDefinitionUpdate(t *testing.T) {
|
||||
Kind: "kind",
|
||||
ListKind: "listkind",
|
||||
},
|
||||
StoredVersions: []string{"version"},
|
||||
},
|
||||
},
|
||||
errors: []validationMatch{},
|
||||
@ -320,7 +487,14 @@ func TestValidateCustomResourceDefinitionUpdate(t *testing.T) {
|
||||
Spec: apiextensions.CustomResourceDefinitionSpec{
|
||||
Group: "group.com",
|
||||
Version: "version",
|
||||
Scope: apiextensions.ResourceScope("Cluster"),
|
||||
Versions: []apiextensions.CustomResourceDefinitionVersion{
|
||||
{
|
||||
Name: "version",
|
||||
Served: true,
|
||||
Storage: true,
|
||||
},
|
||||
},
|
||||
Scope: apiextensions.ResourceScope("Cluster"),
|
||||
Names: apiextensions.CustomResourceDefinitionNames{
|
||||
Plural: "plural",
|
||||
Singular: "singular",
|
||||
@ -348,7 +522,14 @@ func TestValidateCustomResourceDefinitionUpdate(t *testing.T) {
|
||||
Spec: apiextensions.CustomResourceDefinitionSpec{
|
||||
Group: "group.com",
|
||||
Version: "version",
|
||||
Scope: apiextensions.ResourceScope("Cluster"),
|
||||
Versions: []apiextensions.CustomResourceDefinitionVersion{
|
||||
{
|
||||
Name: "version",
|
||||
Served: true,
|
||||
Storage: true,
|
||||
},
|
||||
},
|
||||
Scope: apiextensions.ResourceScope("Cluster"),
|
||||
Names: apiextensions.CustomResourceDefinitionNames{
|
||||
Plural: "plural",
|
||||
Singular: "singular",
|
||||
@ -363,10 +544,91 @@ func TestValidateCustomResourceDefinitionUpdate(t *testing.T) {
|
||||
Kind: "kind",
|
||||
ListKind: "listkind",
|
||||
},
|
||||
StoredVersions: []string{"version"},
|
||||
},
|
||||
},
|
||||
errors: []validationMatch{},
|
||||
},
|
||||
{
|
||||
name: "version-deleted",
|
||||
old: &apiextensions.CustomResourceDefinition{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "plural.group.com",
|
||||
ResourceVersion: "42",
|
||||
},
|
||||
Spec: apiextensions.CustomResourceDefinitionSpec{
|
||||
Group: "group.com",
|
||||
Version: "version",
|
||||
Versions: []apiextensions.CustomResourceDefinitionVersion{
|
||||
{
|
||||
Name: "version",
|
||||
Served: true,
|
||||
Storage: true,
|
||||
},
|
||||
{
|
||||
Name: "version2",
|
||||
Served: true,
|
||||
Storage: false,
|
||||
},
|
||||
},
|
||||
Scope: apiextensions.ResourceScope("Cluster"),
|
||||
Names: apiextensions.CustomResourceDefinitionNames{
|
||||
Plural: "plural",
|
||||
Singular: "singular",
|
||||
Kind: "kind",
|
||||
ListKind: "listkind",
|
||||
},
|
||||
},
|
||||
Status: apiextensions.CustomResourceDefinitionStatus{
|
||||
AcceptedNames: apiextensions.CustomResourceDefinitionNames{
|
||||
Plural: "plural",
|
||||
Singular: "singular",
|
||||
Kind: "kind",
|
||||
ListKind: "listkind",
|
||||
},
|
||||
StoredVersions: []string{"version", "version2"},
|
||||
Conditions: []apiextensions.CustomResourceDefinitionCondition{
|
||||
{Type: apiextensions.Established, Status: apiextensions.ConditionTrue},
|
||||
},
|
||||
},
|
||||
},
|
||||
resource: &apiextensions.CustomResourceDefinition{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "plural.group.com",
|
||||
ResourceVersion: "42",
|
||||
},
|
||||
Spec: apiextensions.CustomResourceDefinitionSpec{
|
||||
Group: "group.com",
|
||||
Version: "version",
|
||||
Versions: []apiextensions.CustomResourceDefinitionVersion{
|
||||
{
|
||||
Name: "version",
|
||||
Served: true,
|
||||
Storage: true,
|
||||
},
|
||||
},
|
||||
Scope: apiextensions.ResourceScope("Cluster"),
|
||||
Names: apiextensions.CustomResourceDefinitionNames{
|
||||
Plural: "plural",
|
||||
Singular: "singular",
|
||||
Kind: "kind",
|
||||
ListKind: "listkind",
|
||||
},
|
||||
},
|
||||
Status: apiextensions.CustomResourceDefinitionStatus{
|
||||
AcceptedNames: apiextensions.CustomResourceDefinitionNames{
|
||||
Plural: "plural",
|
||||
Singular: "singular",
|
||||
Kind: "kind",
|
||||
ListKind: "listkind",
|
||||
},
|
||||
StoredVersions: []string{"version", "version2"},
|
||||
},
|
||||
},
|
||||
errors: []validationMatch{
|
||||
invalid("status", "storedVersions[1]"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "changes",
|
||||
old: &apiextensions.CustomResourceDefinition{
|
||||
@ -377,7 +639,14 @@ func TestValidateCustomResourceDefinitionUpdate(t *testing.T) {
|
||||
Spec: apiextensions.CustomResourceDefinitionSpec{
|
||||
Group: "group.com",
|
||||
Version: "version",
|
||||
Scope: apiextensions.ResourceScope("Cluster"),
|
||||
Versions: []apiextensions.CustomResourceDefinitionVersion{
|
||||
{
|
||||
Name: "version",
|
||||
Served: true,
|
||||
Storage: true,
|
||||
},
|
||||
},
|
||||
Scope: apiextensions.ResourceScope("Cluster"),
|
||||
Names: apiextensions.CustomResourceDefinitionNames{
|
||||
Plural: "plural",
|
||||
Singular: "singular",
|
||||
@ -405,7 +674,14 @@ func TestValidateCustomResourceDefinitionUpdate(t *testing.T) {
|
||||
Spec: apiextensions.CustomResourceDefinitionSpec{
|
||||
Group: "abc.com",
|
||||
Version: "version2",
|
||||
Scope: apiextensions.ResourceScope("Namespaced"),
|
||||
Versions: []apiextensions.CustomResourceDefinitionVersion{
|
||||
{
|
||||
Name: "version2",
|
||||
Served: true,
|
||||
Storage: true,
|
||||
},
|
||||
},
|
||||
Scope: apiextensions.ResourceScope("Namespaced"),
|
||||
Names: apiextensions.CustomResourceDefinitionNames{
|
||||
Plural: "plural2",
|
||||
Singular: "singular2",
|
||||
@ -420,6 +696,7 @@ func TestValidateCustomResourceDefinitionUpdate(t *testing.T) {
|
||||
Kind: "kind2",
|
||||
ListKind: "listkind2",
|
||||
},
|
||||
StoredVersions: []string{"version2"},
|
||||
},
|
||||
},
|
||||
errors: []validationMatch{
|
||||
@ -437,7 +714,14 @@ func TestValidateCustomResourceDefinitionUpdate(t *testing.T) {
|
||||
Spec: apiextensions.CustomResourceDefinitionSpec{
|
||||
Group: "group.com",
|
||||
Version: "version",
|
||||
Scope: apiextensions.ResourceScope("Cluster"),
|
||||
Versions: []apiextensions.CustomResourceDefinitionVersion{
|
||||
{
|
||||
Name: "version",
|
||||
Served: true,
|
||||
Storage: true,
|
||||
},
|
||||
},
|
||||
Scope: apiextensions.ResourceScope("Cluster"),
|
||||
Names: apiextensions.CustomResourceDefinitionNames{
|
||||
Plural: "plural",
|
||||
Singular: "singular",
|
||||
@ -465,7 +749,14 @@ func TestValidateCustomResourceDefinitionUpdate(t *testing.T) {
|
||||
Spec: apiextensions.CustomResourceDefinitionSpec{
|
||||
Group: "abc.com",
|
||||
Version: "version2",
|
||||
Scope: apiextensions.ResourceScope("Namespaced"),
|
||||
Versions: []apiextensions.CustomResourceDefinitionVersion{
|
||||
{
|
||||
Name: "version2",
|
||||
Served: true,
|
||||
Storage: true,
|
||||
},
|
||||
},
|
||||
Scope: apiextensions.ResourceScope("Namespaced"),
|
||||
Names: apiextensions.CustomResourceDefinitionNames{
|
||||
Plural: "plural2",
|
||||
Singular: "singular2",
|
||||
@ -480,11 +771,11 @@ func TestValidateCustomResourceDefinitionUpdate(t *testing.T) {
|
||||
Kind: "kind2",
|
||||
ListKind: "listkind2",
|
||||
},
|
||||
StoredVersions: []string{"version2"},
|
||||
},
|
||||
},
|
||||
errors: []validationMatch{
|
||||
immutable("spec", "group"),
|
||||
immutable("spec", "version"),
|
||||
immutable("spec", "scope"),
|
||||
immutable("spec", "names", "kind"),
|
||||
immutable("spec", "names", "plural"),
|
||||
|
Loading…
Reference in New Issue
Block a user