Merge pull request #81965 from roycaihw/crd-feature-gates-to-ga

Bump the CRD feature gates to GA
This commit is contained in:
Kubernetes Prow Robot 2019-08-28 14:44:44 -07:00 committed by GitHub
commit f3828b776b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 298 additions and 418 deletions

View File

@ -575,10 +575,10 @@ var defaultKubernetesFeatureGates = map[featuregate.Feature]featuregate.FeatureS
// inherited features from apiextensions-apiserver, relisted here to get a conflict if it is changed // inherited features from apiextensions-apiserver, relisted here to get a conflict if it is changed
// unintentionally on either side: // unintentionally on either side:
apiextensionsfeatures.CustomResourceValidation: {Default: true, PreRelease: featuregate.Beta}, apiextensionsfeatures.CustomResourceValidation: {Default: true, PreRelease: featuregate.GA, LockToDefault: true},
apiextensionsfeatures.CustomResourceSubresources: {Default: true, PreRelease: featuregate.Beta}, apiextensionsfeatures.CustomResourceSubresources: {Default: true, PreRelease: featuregate.GA, LockToDefault: true},
apiextensionsfeatures.CustomResourceWebhookConversion: {Default: true, PreRelease: featuregate.Beta}, apiextensionsfeatures.CustomResourceWebhookConversion: {Default: true, PreRelease: featuregate.GA, LockToDefault: true},
apiextensionsfeatures.CustomResourcePublishOpenAPI: {Default: true, PreRelease: featuregate.Beta}, apiextensionsfeatures.CustomResourcePublishOpenAPI: {Default: true, PreRelease: featuregate.GA, LockToDefault: true},
apiextensionsfeatures.CustomResourceDefaulting: {Default: true, PreRelease: featuregate.Beta}, apiextensionsfeatures.CustomResourceDefaulting: {Default: true, PreRelease: featuregate.Beta},
// features that enable backwards compatibility but are scheduled to be removed // features that enable backwards compatibility but are scheduled to be removed

View File

@ -31,12 +31,15 @@ const (
// owner: @sttts, @nikhita // owner: @sttts, @nikhita
// alpha: v1.8 // alpha: v1.8
// beta: v1.9 // beta: v1.9
// GA: v1.16
// //
// CustomResourceValidation is a list of validation methods for CustomResources // CustomResourceValidation is a list of validation methods for CustomResources
CustomResourceValidation featuregate.Feature = "CustomResourceValidation" CustomResourceValidation featuregate.Feature = "CustomResourceValidation"
// owner: @roycaihw, @sttts // owner: @roycaihw, @sttts
// alpha: v1.14 // alpha: v1.14
// beta: v1.15
// GA: v1.16
// //
// CustomResourcePublishOpenAPI enables publishing of CRD OpenAPI specs. // CustomResourcePublishOpenAPI enables publishing of CRD OpenAPI specs.
CustomResourcePublishOpenAPI featuregate.Feature = "CustomResourcePublishOpenAPI" CustomResourcePublishOpenAPI featuregate.Feature = "CustomResourcePublishOpenAPI"
@ -44,12 +47,15 @@ const (
// owner: @sttts, @nikhita // owner: @sttts, @nikhita
// alpha: v1.10 // alpha: v1.10
// beta: v1.11 // beta: v1.11
// GA: v1.16
// //
// CustomResourceSubresources defines the subresources for CustomResources // CustomResourceSubresources defines the subresources for CustomResources
CustomResourceSubresources featuregate.Feature = "CustomResourceSubresources" CustomResourceSubresources featuregate.Feature = "CustomResourceSubresources"
// owner: @mbohlool, @roycaihw // owner: @mbohlool, @roycaihw
// alpha: v1.13 // alpha: v1.13
// beta: v1.15
// GA: v1.16
// //
// CustomResourceWebhookConversion defines the webhook conversion for Custom Resources. // CustomResourceWebhookConversion defines the webhook conversion for Custom Resources.
CustomResourceWebhookConversion featuregate.Feature = "CustomResourceWebhookConversion" CustomResourceWebhookConversion featuregate.Feature = "CustomResourceWebhookConversion"
@ -69,9 +75,9 @@ func init() {
// To add a new feature, define a key for it above and add it here. The features will be // To add a new feature, define a key for it above and add it here. The features will be
// available throughout Kubernetes binaries. // available throughout Kubernetes binaries.
var defaultKubernetesFeatureGates = map[featuregate.Feature]featuregate.FeatureSpec{ var defaultKubernetesFeatureGates = map[featuregate.Feature]featuregate.FeatureSpec{
CustomResourceValidation: {Default: true, PreRelease: featuregate.Beta}, CustomResourceValidation: {Default: true, PreRelease: featuregate.GA, LockToDefault: true},
CustomResourceSubresources: {Default: true, PreRelease: featuregate.Beta}, CustomResourceSubresources: {Default: true, PreRelease: featuregate.GA, LockToDefault: true},
CustomResourceWebhookConversion: {Default: true, PreRelease: featuregate.Beta}, CustomResourceWebhookConversion: {Default: true, PreRelease: featuregate.GA, LockToDefault: true},
CustomResourcePublishOpenAPI: {Default: true, PreRelease: featuregate.Beta}, CustomResourcePublishOpenAPI: {Default: true, PreRelease: featuregate.GA, LockToDefault: true},
CustomResourceDefaulting: {Default: true, PreRelease: featuregate.Beta}, CustomResourceDefaulting: {Default: true, PreRelease: featuregate.Beta},
} }

View File

@ -55,13 +55,10 @@ go_test(
"//staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions:go_default_library", "//staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions:go_default_library",
"//staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1:go_default_library", "//staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1:go_default_library",
"//staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/validation:go_default_library", "//staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/validation:go_default_library",
"//staging/src/k8s.io/apiextensions-apiserver/pkg/features:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/diff:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/diff:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/validation/field:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/validation/field:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
"//staging/src/k8s.io/component-base/featuregate/testing:go_default_library",
"//vendor/k8s.io/utils/pointer:go_default_library", "//vendor/k8s.io/utils/pointer:go_default_library",
], ],
) )

View File

@ -24,456 +24,333 @@ import (
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions" "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/validation" "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/validation"
apiextensionsfeatures "k8s.io/apiextensions-apiserver/pkg/features"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/diff" "k8s.io/apimachinery/pkg/util/diff"
"k8s.io/apimachinery/pkg/util/validation/field" "k8s.io/apimachinery/pkg/util/validation/field"
utilfeature "k8s.io/apiserver/pkg/util/feature"
featuregatetesting "k8s.io/component-base/featuregate/testing"
"k8s.io/utils/pointer" "k8s.io/utils/pointer"
) )
func TestDropDisableFieldsCustomResourceDefinition(t *testing.T) { func TestDropDisableFieldsCustomResourceDefinition(t *testing.T) {
t.Log("testing unversioned validation..") t.Log("testing unversioned validation..")
for _, validationEnabled := range []bool{true, false} { crdWithUnversionedValidation := func() *apiextensions.CustomResourceDefinition {
crdWithUnversionedValidation := func() *apiextensions.CustomResourceDefinition { // crd with non-versioned validation
// crd with non-versioned validation return &apiextensions.CustomResourceDefinition{
return &apiextensions.CustomResourceDefinition{ Spec: apiextensions.CustomResourceDefinitionSpec{
Spec: apiextensions.CustomResourceDefinitionSpec{ Validation: &apiextensions.CustomResourceValidation{
Validation: &apiextensions.CustomResourceValidation{ OpenAPIV3Schema: &apiextensions.JSONSchemaProps{},
OpenAPIV3Schema: &apiextensions.JSONSchemaProps{},
},
}, },
}
}
crdWithoutUnversionedValidation := func() *apiextensions.CustomResourceDefinition {
// crd with non-versioned validation
return &apiextensions.CustomResourceDefinition{
Spec: apiextensions.CustomResourceDefinitionSpec{},
}
}
crdInfos := []struct {
name string
hasCRValidation bool
crd func() *apiextensions.CustomResourceDefinition
}{
{
name: "has unversioned validation",
hasCRValidation: true,
crd: crdWithUnversionedValidation,
},
{
name: "doesn't have unversioned validation",
hasCRValidation: false,
crd: crdWithoutUnversionedValidation,
},
{
name: "nil",
hasCRValidation: false,
crd: func() *apiextensions.CustomResourceDefinition { return nil },
}, },
} }
for _, oldCRDInfo := range crdInfos { }
for _, newCRDInfo := range crdInfos { crdWithoutUnversionedValidation := func() *apiextensions.CustomResourceDefinition {
oldCRDHasValidation, oldCRD := oldCRDInfo.hasCRValidation, oldCRDInfo.crd() // crd with non-versioned validation
newCRDHasValidation, newCRD := newCRDInfo.hasCRValidation, newCRDInfo.crd() return &apiextensions.CustomResourceDefinition{
if newCRD == nil { Spec: apiextensions.CustomResourceDefinitionSpec{},
continue }
} }
t.Run(fmt.Sprintf("validation feature enabled=%v, old CRD %v, new CRD %v", validationEnabled, oldCRDInfo.name, newCRDInfo.name), crdInfos := []struct {
func(t *testing.T) { name string
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, apiextensionsfeatures.CustomResourceValidation, validationEnabled)() crd func() *apiextensions.CustomResourceDefinition
var oldCRDSpec *apiextensions.CustomResourceDefinitionSpec }{
if oldCRD != nil { {
oldCRDSpec = &oldCRD.Spec name: "has unversioned validation",
} crd: crdWithUnversionedValidation,
dropDisabledFields(&newCRD.Spec, oldCRDSpec) },
// old CRD should never be changed {
if !reflect.DeepEqual(oldCRD, oldCRDInfo.crd()) { name: "doesn't have unversioned validation",
t.Errorf("old crd changed: %v", diff.ObjectReflectDiff(oldCRD, oldCRDInfo.crd())) crd: crdWithoutUnversionedValidation,
} },
switch { {
case validationEnabled || oldCRDHasValidation: name: "nil",
if !reflect.DeepEqual(newCRD, newCRDInfo.crd()) { crd: func() *apiextensions.CustomResourceDefinition { return nil },
t.Errorf("new crd changed: %v", diff.ObjectReflectDiff(newCRD, newCRDInfo.crd())) },
} }
case newCRDHasValidation: for _, oldCRDInfo := range crdInfos {
if reflect.DeepEqual(newCRD, newCRDInfo.crd()) { for _, newCRDInfo := range crdInfos {
t.Errorf("new crd was not changed") oldCRD := oldCRDInfo.crd()
} newCRD := newCRDInfo.crd()
if !reflect.DeepEqual(newCRD, crdWithoutUnversionedValidation()) { if newCRD == nil {
t.Errorf("new crd had unversioned validation: %v", diff.ObjectReflectDiff(newCRD, crdWithoutUnversionedValidation())) continue
}
default:
if !reflect.DeepEqual(newCRD, newCRDInfo.crd()) {
t.Errorf("new crd changed: %v", diff.ObjectReflectDiff(newCRD, newCRDInfo.crd()))
}
}
},
)
} }
t.Run(fmt.Sprintf("old CRD %v, new CRD %v", oldCRDInfo.name, newCRDInfo.name),
func(t *testing.T) {
var oldCRDSpec *apiextensions.CustomResourceDefinitionSpec
if oldCRD != nil {
oldCRDSpec = &oldCRD.Spec
}
dropDisabledFields(&newCRD.Spec, oldCRDSpec)
// old CRD should never be changed
if !reflect.DeepEqual(oldCRD, oldCRDInfo.crd()) {
t.Errorf("old crd changed: %v", diff.ObjectReflectDiff(oldCRD, oldCRDInfo.crd()))
}
if !reflect.DeepEqual(newCRD, newCRDInfo.crd()) {
t.Errorf("new crd changed: %v", diff.ObjectReflectDiff(newCRD, newCRDInfo.crd()))
}
},
)
} }
} }
t.Log("testing unversioned subresources...") t.Log("testing unversioned subresources...")
for _, validationEnabled := range []bool{true, false} { crdWithUnversionedSubresources := func() *apiextensions.CustomResourceDefinition {
crdWithUnversionedSubresources := func() *apiextensions.CustomResourceDefinition { // crd with unversioned subresources
// crd with unversioned subresources return &apiextensions.CustomResourceDefinition{
return &apiextensions.CustomResourceDefinition{ Spec: apiextensions.CustomResourceDefinitionSpec{
Spec: apiextensions.CustomResourceDefinitionSpec{ Subresources: &apiextensions.CustomResourceSubresources{},
Subresources: &apiextensions.CustomResourceSubresources{}, },
}
}
crdWithoutUnversionedSubresources := func() *apiextensions.CustomResourceDefinition {
// crd without unversioned subresources
return &apiextensions.CustomResourceDefinition{
Spec: apiextensions.CustomResourceDefinitionSpec{},
}
}
crdInfos = []struct {
name string
crd func() *apiextensions.CustomResourceDefinition
}{
{
name: "has unversioned subresources",
crd: crdWithUnversionedSubresources,
},
{
name: "doesn't have unversioned subresources",
crd: crdWithoutUnversionedSubresources,
},
{
name: "nil",
crd: func() *apiextensions.CustomResourceDefinition { return nil },
},
}
for _, oldCRDInfo := range crdInfos {
for _, newCRDInfo := range crdInfos {
oldCRD := oldCRDInfo.crd()
newCRD := newCRDInfo.crd()
if newCRD == nil {
continue
}
t.Run(fmt.Sprintf("old CRD %v, new CRD %v", oldCRDInfo.name, newCRDInfo.name),
func(t *testing.T) {
var oldCRDSpec *apiextensions.CustomResourceDefinitionSpec
if oldCRD != nil {
oldCRDSpec = &oldCRD.Spec
}
dropDisabledFields(&newCRD.Spec, oldCRDSpec)
// old CRD should never be changed
if !reflect.DeepEqual(oldCRD, oldCRDInfo.crd()) {
t.Errorf("old crd changed: %v", diff.ObjectReflectDiff(oldCRD, oldCRDInfo.crd()))
}
if !reflect.DeepEqual(newCRD, newCRDInfo.crd()) {
t.Errorf("new crd changed: %v", diff.ObjectReflectDiff(newCRD, newCRDInfo.crd()))
}
}, },
} )
}
crdWithoutUnversionedSubresources := func() *apiextensions.CustomResourceDefinition {
// crd without unversioned subresources
return &apiextensions.CustomResourceDefinition{
Spec: apiextensions.CustomResourceDefinitionSpec{},
}
}
crdInfos := []struct {
name string
hasCRSubresources bool
crd func() *apiextensions.CustomResourceDefinition
}{
{
name: "has unversioned subresources",
hasCRSubresources: true,
crd: crdWithUnversionedSubresources,
},
{
name: "doesn't have unversioned subresources",
hasCRSubresources: false,
crd: crdWithoutUnversionedSubresources,
},
{
name: "nil",
hasCRSubresources: false,
crd: func() *apiextensions.CustomResourceDefinition { return nil },
},
}
for _, oldCRDInfo := range crdInfos {
for _, newCRDInfo := range crdInfos {
oldCRDHasSubresources, oldCRD := oldCRDInfo.hasCRSubresources, oldCRDInfo.crd()
newCRDHasSubresources, newCRD := newCRDInfo.hasCRSubresources, newCRDInfo.crd()
if newCRD == nil {
continue
}
t.Run(fmt.Sprintf("subresources feature enabled=%v, old CRD %v, new CRD %v", validationEnabled, oldCRDInfo.name, newCRDInfo.name),
func(t *testing.T) {
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, apiextensionsfeatures.CustomResourceSubresources, validationEnabled)()
var oldCRDSpec *apiextensions.CustomResourceDefinitionSpec
if oldCRD != nil {
oldCRDSpec = &oldCRD.Spec
}
dropDisabledFields(&newCRD.Spec, oldCRDSpec)
// old CRD should never be changed
if !reflect.DeepEqual(oldCRD, oldCRDInfo.crd()) {
t.Errorf("old crd changed: %v", diff.ObjectReflectDiff(oldCRD, oldCRDInfo.crd()))
}
switch {
case validationEnabled || oldCRDHasSubresources:
if !reflect.DeepEqual(newCRD, newCRDInfo.crd()) {
t.Errorf("new crd changed: %v", diff.ObjectReflectDiff(newCRD, newCRDInfo.crd()))
}
case newCRDHasSubresources:
if reflect.DeepEqual(newCRD, newCRDInfo.crd()) {
t.Errorf("new crd was not changed")
}
if !reflect.DeepEqual(newCRD, crdWithoutUnversionedSubresources()) {
t.Errorf("new crd had unversioned subresources: %v", diff.ObjectReflectDiff(newCRD, crdWithoutUnversionedSubresources()))
}
default:
if !reflect.DeepEqual(newCRD, newCRDInfo.crd()) {
t.Errorf("new crd changed: %v", diff.ObjectReflectDiff(newCRD, newCRDInfo.crd()))
}
}
},
)
}
} }
} }
t.Log("testing versioned validation..") t.Log("testing versioned validation..")
for _, conversionEnabled := range []bool{true, false} { crdWithVersionedValidation := func() *apiextensions.CustomResourceDefinition {
for _, validationEnabled := range []bool{true, false} { // crd with versioned validation
crdWithVersionedValidation := func() *apiextensions.CustomResourceDefinition { return &apiextensions.CustomResourceDefinition{
// crd with versioned validation Spec: apiextensions.CustomResourceDefinitionSpec{
return &apiextensions.CustomResourceDefinition{ Versions: []apiextensions.CustomResourceDefinitionVersion{
Spec: apiextensions.CustomResourceDefinitionSpec{ {
Versions: []apiextensions.CustomResourceDefinitionVersion{ Name: "v1",
{ Schema: &apiextensions.CustomResourceValidation{
Name: "v1", OpenAPIV3Schema: &apiextensions.JSONSchemaProps{},
Schema: &apiextensions.CustomResourceValidation{
OpenAPIV3Schema: &apiextensions.JSONSchemaProps{},
},
},
}, },
}, },
} },
} },
crdWithoutVersionedValidation := func() *apiextensions.CustomResourceDefinition { }
// crd with versioned validation }
return &apiextensions.CustomResourceDefinition{ crdWithoutVersionedValidation := func() *apiextensions.CustomResourceDefinition {
Spec: apiextensions.CustomResourceDefinitionSpec{ // crd with versioned validation
Versions: []apiextensions.CustomResourceDefinitionVersion{ return &apiextensions.CustomResourceDefinition{
{ Spec: apiextensions.CustomResourceDefinitionSpec{
Name: "v1", Versions: []apiextensions.CustomResourceDefinitionVersion{
}, {
}, Name: "v1",
}, },
} },
},
}
}
crdInfos = []struct {
name string
crd func() *apiextensions.CustomResourceDefinition
}{
{
name: "has versioned validation",
crd: crdWithVersionedValidation,
},
{
name: "doesn't have versioned validation",
crd: crdWithoutVersionedValidation,
},
{
name: "nil",
crd: func() *apiextensions.CustomResourceDefinition { return nil },
},
}
for _, oldCRDInfo := range crdInfos {
for _, newCRDInfo := range crdInfos {
oldCRD := oldCRDInfo.crd()
newCRD := newCRDInfo.crd()
if newCRD == nil {
continue
} }
crdInfos := []struct { t.Run(fmt.Sprintf("old CRD %v, new CRD %v", oldCRDInfo.name, newCRDInfo.name),
name string func(t *testing.T) {
hasCRValidation bool var oldCRDSpec *apiextensions.CustomResourceDefinitionSpec
crd func() *apiextensions.CustomResourceDefinition if oldCRD != nil {
}{ oldCRDSpec = &oldCRD.Spec
{
name: "has versioned validation",
hasCRValidation: true,
crd: crdWithVersionedValidation,
},
{
name: "doesn't have versioned validation",
hasCRValidation: false,
crd: crdWithoutVersionedValidation,
},
{
name: "nil",
hasCRValidation: false,
crd: func() *apiextensions.CustomResourceDefinition { return nil },
},
}
for _, oldCRDInfo := range crdInfos {
for _, newCRDInfo := range crdInfos {
oldCRDHasValidation, oldCRD := oldCRDInfo.hasCRValidation, oldCRDInfo.crd()
newCRDHasValidation, newCRD := newCRDInfo.hasCRValidation, newCRDInfo.crd()
if newCRD == nil {
continue
} }
t.Run(fmt.Sprintf("validation feature enabled=%v, old CRD %v, new CRD %v", validationEnabled, oldCRDInfo.name, newCRDInfo.name), dropDisabledFields(&newCRD.Spec, oldCRDSpec)
func(t *testing.T) { // old CRD should never be changed
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, apiextensionsfeatures.CustomResourceValidation, validationEnabled)() if !reflect.DeepEqual(oldCRD, oldCRDInfo.crd()) {
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, apiextensionsfeatures.CustomResourceWebhookConversion, conversionEnabled)() t.Errorf("old crd changed: %v", diff.ObjectReflectDiff(oldCRD, oldCRDInfo.crd()))
var oldCRDSpec *apiextensions.CustomResourceDefinitionSpec }
if oldCRD != nil { if !reflect.DeepEqual(newCRD, newCRDInfo.crd()) {
oldCRDSpec = &oldCRD.Spec t.Errorf("new crd changed: %v", diff.ObjectReflectDiff(newCRD, newCRDInfo.crd()))
} }
dropDisabledFields(&newCRD.Spec, oldCRDSpec) },
// old CRD should never be changed )
if !reflect.DeepEqual(oldCRD, oldCRDInfo.crd()) {
t.Errorf("old crd changed: %v", diff.ObjectReflectDiff(oldCRD, oldCRDInfo.crd()))
}
switch {
case (conversionEnabled && validationEnabled) || oldCRDHasValidation:
if !reflect.DeepEqual(newCRD, newCRDInfo.crd()) {
t.Errorf("new crd changed: %v", diff.ObjectReflectDiff(newCRD, newCRDInfo.crd()))
}
case !conversionEnabled && !oldCRDHasValidation:
if !reflect.DeepEqual(newCRD, crdWithoutVersionedValidation()) {
t.Errorf("new crd was not changed")
}
case newCRDHasValidation:
if reflect.DeepEqual(newCRD, newCRDInfo.crd()) {
t.Errorf("new crd was not changed")
}
if !reflect.DeepEqual(newCRD, crdWithoutVersionedValidation()) {
t.Errorf("new crd had unversioned validation: %v", diff.ObjectReflectDiff(newCRD, crdWithoutVersionedValidation()))
}
default:
if !reflect.DeepEqual(newCRD, newCRDInfo.crd()) {
t.Errorf("new crd changed: %v", diff.ObjectReflectDiff(newCRD, newCRDInfo.crd()))
}
}
},
)
}
}
} }
} }
t.Log("testing versioned subresources w/ conversion enabled..") t.Log("testing versioned subresources w/ conversion enabled..")
for _, conversionEnabled := range []bool{true, false} { crdWithVersionedSubresources := func() *apiextensions.CustomResourceDefinition {
for _, validationEnabled := range []bool{true, false} { // crd with versioned subresources
crdWithVersionedSubresources := func() *apiextensions.CustomResourceDefinition { return &apiextensions.CustomResourceDefinition{
// crd with versioned subresources Spec: apiextensions.CustomResourceDefinitionSpec{
return &apiextensions.CustomResourceDefinition{ Versions: []apiextensions.CustomResourceDefinitionVersion{
Spec: apiextensions.CustomResourceDefinitionSpec{ {
Versions: []apiextensions.CustomResourceDefinitionVersion{ Name: "v1",
{ Subresources: &apiextensions.CustomResourceSubresources{},
Name: "v1",
Subresources: &apiextensions.CustomResourceSubresources{},
},
},
}, },
} },
} },
crdWithoutVersionedSubresources := func() *apiextensions.CustomResourceDefinition { }
// crd without versioned subresources }
return &apiextensions.CustomResourceDefinition{ crdWithoutVersionedSubresources := func() *apiextensions.CustomResourceDefinition {
Spec: apiextensions.CustomResourceDefinitionSpec{ // crd without versioned subresources
Versions: []apiextensions.CustomResourceDefinitionVersion{ return &apiextensions.CustomResourceDefinition{
{ Spec: apiextensions.CustomResourceDefinitionSpec{
Name: "v1", Versions: []apiextensions.CustomResourceDefinitionVersion{
}, {
}, Name: "v1",
}, },
} },
},
}
}
crdInfos = []struct {
name string
crd func() *apiextensions.CustomResourceDefinition
}{
{
name: "has versioned subresources",
crd: crdWithVersionedSubresources,
},
{
name: "doesn't have versioned subresources",
crd: crdWithoutVersionedSubresources,
},
{
name: "nil",
crd: func() *apiextensions.CustomResourceDefinition { return nil },
},
}
for _, oldCRDInfo := range crdInfos {
for _, newCRDInfo := range crdInfos {
oldCRD := oldCRDInfo.crd()
newCRD := newCRDInfo.crd()
if newCRD == nil {
continue
} }
crdInfos := []struct { t.Run(fmt.Sprintf("old CRD %v, new CRD %v", oldCRDInfo.name, newCRDInfo.name),
name string func(t *testing.T) {
hasCRSubresources bool var oldCRDSpec *apiextensions.CustomResourceDefinitionSpec
crd func() *apiextensions.CustomResourceDefinition if oldCRD != nil {
}{ oldCRDSpec = &oldCRD.Spec
{
name: "has versioned subresources",
hasCRSubresources: true,
crd: crdWithVersionedSubresources,
},
{
name: "doesn't have versioned subresources",
hasCRSubresources: false,
crd: crdWithoutVersionedSubresources,
},
{
name: "nil",
hasCRSubresources: false,
crd: func() *apiextensions.CustomResourceDefinition { return nil },
},
}
for _, oldCRDInfo := range crdInfos {
for _, newCRDInfo := range crdInfos {
oldCRDHasSubresources, oldCRD := oldCRDInfo.hasCRSubresources, oldCRDInfo.crd()
newCRDHasSubresources, newCRD := newCRDInfo.hasCRSubresources, newCRDInfo.crd()
if newCRD == nil {
continue
} }
t.Run(fmt.Sprintf("subresources feature enabled=%v, old CRD %v, new CRD %v", validationEnabled, oldCRDInfo.name, newCRDInfo.name), dropDisabledFields(&newCRD.Spec, oldCRDSpec)
func(t *testing.T) { // old CRD should never be changed
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, apiextensionsfeatures.CustomResourceSubresources, validationEnabled)() if !reflect.DeepEqual(oldCRD, oldCRDInfo.crd()) {
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, apiextensionsfeatures.CustomResourceWebhookConversion, conversionEnabled)() t.Errorf("old crd changed: %v", diff.ObjectReflectDiff(oldCRD, oldCRDInfo.crd()))
var oldCRDSpec *apiextensions.CustomResourceDefinitionSpec }
if oldCRD != nil { if !reflect.DeepEqual(newCRD, newCRDInfo.crd()) {
oldCRDSpec = &oldCRD.Spec t.Errorf("new crd changed: %v", diff.ObjectReflectDiff(newCRD, newCRDInfo.crd()))
} }
dropDisabledFields(&newCRD.Spec, oldCRDSpec) },
// old CRD should never be changed )
if !reflect.DeepEqual(oldCRD, oldCRDInfo.crd()) {
t.Errorf("old crd changed: %v", diff.ObjectReflectDiff(oldCRD, oldCRDInfo.crd()))
}
switch {
case (conversionEnabled && validationEnabled) || oldCRDHasSubresources:
if !reflect.DeepEqual(newCRD, newCRDInfo.crd()) {
t.Errorf("new crd changed: %v", diff.ObjectReflectDiff(newCRD, newCRDInfo.crd()))
}
case !conversionEnabled && !oldCRDHasSubresources:
if !reflect.DeepEqual(newCRD, crdWithoutVersionedSubresources()) {
t.Errorf("new crd was not changed")
}
case newCRDHasSubresources:
if reflect.DeepEqual(newCRD, newCRDInfo.crd()) {
t.Errorf("new crd was not changed")
}
if !reflect.DeepEqual(newCRD, crdWithoutVersionedSubresources()) {
t.Errorf("new crd had versioned subresources: %v", diff.ObjectReflectDiff(newCRD, crdWithoutVersionedSubresources()))
}
default:
if !reflect.DeepEqual(newCRD, newCRDInfo.crd()) {
t.Errorf("new crd changed: %v", diff.ObjectReflectDiff(newCRD, newCRDInfo.crd()))
}
}
},
)
}
}
} }
} }
t.Log("testing conversion webhook..") t.Log("testing conversion webhook..")
for _, validationEnabled := range []bool{true, false} { crdWithUnversionedConversionWebhook := func() *apiextensions.CustomResourceDefinition {
crdWithUnversionedConversionWebhook := func() *apiextensions.CustomResourceDefinition { // crd with conversion webhook
// crd with conversion webhook return &apiextensions.CustomResourceDefinition{
return &apiextensions.CustomResourceDefinition{ Spec: apiextensions.CustomResourceDefinitionSpec{
Spec: apiextensions.CustomResourceDefinitionSpec{ Conversion: &apiextensions.CustomResourceConversion{
Conversion: &apiextensions.CustomResourceConversion{ WebhookClientConfig: &apiextensions.WebhookClientConfig{},
WebhookClientConfig: &apiextensions.WebhookClientConfig{},
},
}, },
}
}
crdWithoutUnversionedConversionWebhook := func() *apiextensions.CustomResourceDefinition {
// crd with conversion webhook
return &apiextensions.CustomResourceDefinition{
Spec: apiextensions.CustomResourceDefinitionSpec{
Conversion: &apiextensions.CustomResourceConversion{},
},
}
}
crdInfos := []struct {
name string
hasCRConversionWebhook bool
crd func() *apiextensions.CustomResourceDefinition
}{
{
name: "has conversion webhook",
hasCRConversionWebhook: true,
crd: crdWithUnversionedConversionWebhook,
}, },
{
name: "doesn't have conversion webhook",
hasCRConversionWebhook: false,
crd: crdWithoutUnversionedConversionWebhook,
},
{
name: "nil",
hasCRConversionWebhook: false,
crd: func() *apiextensions.CustomResourceDefinition { return nil },
},
}
for _, oldCRDInfo := range crdInfos {
for _, newCRDInfo := range crdInfos {
oldCRDHasConversionWebhook, oldCRD := oldCRDInfo.hasCRConversionWebhook, oldCRDInfo.crd()
newCRDHasConversionWebhook, newCRD := newCRDInfo.hasCRConversionWebhook, newCRDInfo.crd()
if newCRD == nil {
continue
}
t.Run(fmt.Sprintf("subresources feature enabled=%v, old CRD %v, new CRD %v", validationEnabled, oldCRDInfo.name, newCRDInfo.name),
func(t *testing.T) {
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, apiextensionsfeatures.CustomResourceWebhookConversion, validationEnabled)()
var oldCRDSpec *apiextensions.CustomResourceDefinitionSpec
if oldCRD != nil {
oldCRDSpec = &oldCRD.Spec
}
dropDisabledFields(&newCRD.Spec, oldCRDSpec)
// old CRD should never be changed
if !reflect.DeepEqual(oldCRD, oldCRDInfo.crd()) {
t.Errorf("old crd changed: %v", diff.ObjectReflectDiff(oldCRD, oldCRDInfo.crd()))
}
switch {
case validationEnabled || oldCRDHasConversionWebhook:
if !reflect.DeepEqual(newCRD, newCRDInfo.crd()) {
t.Errorf("new crd changed: %v", diff.ObjectReflectDiff(newCRD, newCRDInfo.crd()))
}
case newCRDHasConversionWebhook:
if reflect.DeepEqual(newCRD, newCRDInfo.crd()) {
t.Errorf("new crd was not changed")
}
if !reflect.DeepEqual(newCRD, crdWithoutUnversionedConversionWebhook()) {
t.Errorf("new crd had webhook conversion: %v", diff.ObjectReflectDiff(newCRD, crdWithoutUnversionedConversionWebhook()))
}
default:
if !reflect.DeepEqual(newCRD, newCRDInfo.crd()) {
t.Errorf("new crd changed: %v", diff.ObjectReflectDiff(newCRD, newCRDInfo.crd()))
}
}
},
)
}
} }
} }
crdWithoutUnversionedConversionWebhook := func() *apiextensions.CustomResourceDefinition {
// crd with conversion webhook
return &apiextensions.CustomResourceDefinition{
Spec: apiextensions.CustomResourceDefinitionSpec{
Conversion: &apiextensions.CustomResourceConversion{},
},
}
}
crdInfos = []struct {
name string
crd func() *apiextensions.CustomResourceDefinition
}{
{
name: "has conversion webhook",
crd: crdWithUnversionedConversionWebhook,
},
{
name: "doesn't have conversion webhook",
crd: crdWithoutUnversionedConversionWebhook,
},
{
name: "nil",
crd: func() *apiextensions.CustomResourceDefinition { return nil },
},
}
for _, oldCRDInfo := range crdInfos {
for _, newCRDInfo := range crdInfos {
oldCRD := oldCRDInfo.crd()
newCRD := newCRDInfo.crd()
if newCRD == nil {
continue
}
t.Run(fmt.Sprintf("old CRD %v, new CRD %v", oldCRDInfo.name, newCRDInfo.name),
func(t *testing.T) {
var oldCRDSpec *apiextensions.CustomResourceDefinitionSpec
if oldCRD != nil {
oldCRDSpec = &oldCRD.Spec
}
dropDisabledFields(&newCRD.Spec, oldCRDSpec)
// old CRD should never be changed
if !reflect.DeepEqual(oldCRD, oldCRDInfo.crd()) {
t.Errorf("old crd changed: %v", diff.ObjectReflectDiff(oldCRD, oldCRDInfo.crd()))
}
if !reflect.DeepEqual(newCRD, newCRDInfo.crd()) {
t.Errorf("new crd changed: %v", diff.ObjectReflectDiff(newCRD, newCRDInfo.crd()))
}
},
)
}
}
} }
func strPtr(in string) *string { func strPtr(in string) *string {