...

Source file src/k8s.io/apiextensions-apiserver/pkg/registry/customresourcedefinition/strategy_test.go

Documentation: k8s.io/apiextensions-apiserver/pkg/registry/customresourcedefinition

     1  /*
     2  Copyright 2017 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package customresourcedefinition
    18  
    19  import (
    20  	"context"
    21  	"testing"
    22  
    23  	"github.com/google/go-cmp/cmp"
    24  
    25  	"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
    26  	"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
    27  	"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/validation"
    28  	apiextensionsfeatures "k8s.io/apiextensions-apiserver/pkg/features"
    29  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    30  	"k8s.io/apimachinery/pkg/util/validation/field"
    31  	utilfeature "k8s.io/apiserver/pkg/util/feature"
    32  	featuregatetesting "k8s.io/component-base/featuregate/testing"
    33  	"k8s.io/utils/pointer"
    34  	"k8s.io/utils/ptr"
    35  )
    36  
    37  func strPtr(in string) *string {
    38  	return &in
    39  }
    40  
    41  func TestValidateAPIApproval(t *testing.T) {
    42  	okFn := func(t *testing.T, errors field.ErrorList) {
    43  		t.Helper()
    44  		if len(errors) > 0 {
    45  			t.Fatal(errors)
    46  		}
    47  	}
    48  
    49  	tests := []struct {
    50  		name string
    51  
    52  		group              string
    53  		annotationValue    string
    54  		oldAnnotationValue *string
    55  		validateError      func(t *testing.T, errors field.ErrorList)
    56  	}{
    57  		{
    58  			name:            "ignore non-k8s group",
    59  			group:           "other.io",
    60  			annotationValue: "invalid",
    61  			validateError:   okFn,
    62  		},
    63  		{
    64  			name:            "invalid annotation create",
    65  			group:           "sigs.k8s.io",
    66  			annotationValue: "invalid",
    67  			validateError: func(t *testing.T, errors field.ErrorList) {
    68  				t.Helper()
    69  				if len(errors) == 0 {
    70  					t.Fatal("expected errors, got none")
    71  				}
    72  				if e, a := `metadata.annotations[api-approved.kubernetes.io]: Invalid value: "invalid": protected groups must have approval annotation "api-approved.kubernetes.io" with either a URL or a reason starting with "unapproved", see https://github.com/kubernetes/enhancements/pull/1111`, errors.ToAggregate().Error(); e != a {
    73  					t.Fatal(errors)
    74  				}
    75  			},
    76  		},
    77  		{
    78  			name:               "invalid annotation update",
    79  			group:              "sigs.k8s.io",
    80  			annotationValue:    "invalid",
    81  			oldAnnotationValue: strPtr("invalid"),
    82  			validateError:      okFn,
    83  		},
    84  		{
    85  			name:               "invalid annotation to missing",
    86  			group:              "sigs.k8s.io",
    87  			annotationValue:    "",
    88  			oldAnnotationValue: strPtr("invalid"),
    89  			validateError: func(t *testing.T, errors field.ErrorList) {
    90  				t.Helper()
    91  				if len(errors) == 0 {
    92  					t.Fatal("expected errors, got none")
    93  				}
    94  				if e, a := `metadata.annotations[api-approved.kubernetes.io]: Required value: protected groups must have approval annotation "api-approved.kubernetes.io", see https://github.com/kubernetes/enhancements/pull/1111`, errors.ToAggregate().Error(); e != a {
    95  					t.Fatal(errors)
    96  				}
    97  			},
    98  		},
    99  		{
   100  			name:               "missing to invalid annotation",
   101  			group:              "sigs.k8s.io",
   102  			annotationValue:    "invalid",
   103  			oldAnnotationValue: strPtr(""),
   104  			validateError: func(t *testing.T, errors field.ErrorList) {
   105  				t.Helper()
   106  				if len(errors) == 0 {
   107  					t.Fatal("expected errors, got none")
   108  				}
   109  				if e, a := `metadata.annotations[api-approved.kubernetes.io]: Invalid value: "invalid": protected groups must have approval annotation "api-approved.kubernetes.io" with either a URL or a reason starting with "unapproved", see https://github.com/kubernetes/enhancements/pull/1111`, errors.ToAggregate().Error(); e != a {
   110  					t.Fatal(errors)
   111  				}
   112  			},
   113  		},
   114  		{
   115  			name:            "missing annotation",
   116  			group:           "sigs.k8s.io",
   117  			annotationValue: "",
   118  			validateError: func(t *testing.T, errors field.ErrorList) {
   119  				t.Helper()
   120  				if len(errors) == 0 {
   121  					t.Fatal("expected errors, got none")
   122  				}
   123  				if e, a := `metadata.annotations[api-approved.kubernetes.io]: Required value: protected groups must have approval annotation "api-approved.kubernetes.io", see https://github.com/kubernetes/enhancements/pull/1111`, errors.ToAggregate().Error(); e != a {
   124  					t.Fatal(errors)
   125  				}
   126  			},
   127  		},
   128  		{
   129  			name:               "missing annotation update",
   130  			group:              "sigs.k8s.io",
   131  			annotationValue:    "",
   132  			oldAnnotationValue: strPtr(""),
   133  			validateError:      okFn,
   134  		},
   135  		{
   136  			name:            "url",
   137  			group:           "sigs.k8s.io",
   138  			annotationValue: "https://github.com/kubernetes/kubernetes/pull/79724",
   139  			validateError:   okFn,
   140  		},
   141  		{
   142  			name:            "unapproved",
   143  			group:           "sigs.k8s.io",
   144  			annotationValue: "unapproved, other reason",
   145  			validateError:   okFn,
   146  		},
   147  	}
   148  
   149  	for _, test := range tests {
   150  		t.Run(test.name, func(t *testing.T) {
   151  			crd := &apiextensions.CustomResourceDefinition{
   152  				ObjectMeta: metav1.ObjectMeta{Name: "foos." + test.group, Annotations: map[string]string{v1beta1.KubeAPIApprovedAnnotation: test.annotationValue}, ResourceVersion: "1"},
   153  				Spec: apiextensions.CustomResourceDefinitionSpec{
   154  					Group:    test.group,
   155  					Scope:    apiextensions.NamespaceScoped,
   156  					Version:  "v1",
   157  					Versions: []apiextensions.CustomResourceDefinitionVersion{{Name: "v1", Storage: true, Served: true}},
   158  					Names:    apiextensions.CustomResourceDefinitionNames{Plural: "foos", Singular: "foo", Kind: "Foo", ListKind: "FooList"},
   159  					Validation: &apiextensions.CustomResourceValidation{
   160  						OpenAPIV3Schema: &apiextensions.JSONSchemaProps{Type: "object", XPreserveUnknownFields: pointer.BoolPtr(true)},
   161  					},
   162  				},
   163  				Status: apiextensions.CustomResourceDefinitionStatus{
   164  					StoredVersions: []string{"v1"},
   165  				},
   166  			}
   167  			var oldCRD *apiextensions.CustomResourceDefinition
   168  			if test.oldAnnotationValue != nil {
   169  				oldCRD = &apiextensions.CustomResourceDefinition{
   170  					ObjectMeta: metav1.ObjectMeta{Name: "foos." + test.group, Annotations: map[string]string{v1beta1.KubeAPIApprovedAnnotation: *test.oldAnnotationValue}, ResourceVersion: "1"},
   171  					Spec: apiextensions.CustomResourceDefinitionSpec{
   172  						Group:    test.group,
   173  						Scope:    apiextensions.NamespaceScoped,
   174  						Version:  "v1",
   175  						Versions: []apiextensions.CustomResourceDefinitionVersion{{Name: "v1", Storage: true, Served: true}},
   176  						Names:    apiextensions.CustomResourceDefinitionNames{Plural: "foos", Singular: "foo", Kind: "Foo", ListKind: "FooList"},
   177  						Validation: &apiextensions.CustomResourceValidation{
   178  							OpenAPIV3Schema: &apiextensions.JSONSchemaProps{Type: "object", XPreserveUnknownFields: pointer.BoolPtr(true)},
   179  						},
   180  					},
   181  					Status: apiextensions.CustomResourceDefinitionStatus{
   182  						StoredVersions: []string{"v1"},
   183  					},
   184  				}
   185  			}
   186  
   187  			var actual field.ErrorList
   188  			ctx := context.TODO()
   189  			if oldCRD == nil {
   190  				actual = validation.ValidateCustomResourceDefinition(ctx, crd)
   191  			} else {
   192  				actual = validation.ValidateCustomResourceDefinitionUpdate(ctx, crd, oldCRD)
   193  			}
   194  			test.validateError(t, actual)
   195  		})
   196  	}
   197  }
   198  
   199  // TestDropDisabledFields tests if the drop functionality is working fine or not with feature gate switch
   200  func TestDropDisabledFields(t *testing.T) {
   201  	testCases := []struct {
   202  		name                   string
   203  		enableRatcheting       bool
   204  		enableSelectableFields bool
   205  		crd                    *apiextensions.CustomResourceDefinition
   206  		oldCRD                 *apiextensions.CustomResourceDefinition
   207  		expectedCRD            *apiextensions.CustomResourceDefinition
   208  	}{
   209  		{
   210  			name:             "Ratcheting, For creation, FG disabled, no OptionalOldSelf, no field drop",
   211  			enableRatcheting: false,
   212  			crd:              &apiextensions.CustomResourceDefinition{},
   213  			oldCRD:           nil,
   214  			expectedCRD:      &apiextensions.CustomResourceDefinition{},
   215  		},
   216  		{
   217  			name:             "Ratcheting, For creation, FG disabled, set OptionalOldSelf, drop OptionalOldSelf",
   218  			enableRatcheting: false,
   219  			crd: &apiextensions.CustomResourceDefinition{
   220  				ObjectMeta: metav1.ObjectMeta{Name: "foos.sigs.k8s.io", Annotations: map[string]string{v1beta1.KubeAPIApprovedAnnotation: "valid"}, ResourceVersion: "1"},
   221  				Spec: apiextensions.CustomResourceDefinitionSpec{
   222  					Validation: &apiextensions.CustomResourceValidation{
   223  						OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
   224  							Type: "object",
   225  							XValidations: apiextensions.ValidationRules{
   226  								{
   227  									Rule:            "size(self) > 0",
   228  									Message:         "openAPIV3Schema should contain more than 0 element.",
   229  									OptionalOldSelf: ptr.To(true),
   230  								},
   231  							},
   232  							Properties: map[string]apiextensions.JSONSchemaProps{
   233  								"subRule": {
   234  									Type: "object",
   235  									XValidations: apiextensions.ValidationRules{
   236  										{
   237  											Rule:            "isTest == true",
   238  											Message:         "isTest should be true.",
   239  											OptionalOldSelf: ptr.To(true),
   240  										},
   241  									},
   242  									Properties: map[string]apiextensions.JSONSchemaProps{
   243  										"isTest": {
   244  											Type: "boolean",
   245  										},
   246  									},
   247  								},
   248  							},
   249  						},
   250  					},
   251  				},
   252  			},
   253  			oldCRD: nil,
   254  			expectedCRD: &apiextensions.CustomResourceDefinition{
   255  				ObjectMeta: metav1.ObjectMeta{Name: "foos.sigs.k8s.io", Annotations: map[string]string{v1beta1.KubeAPIApprovedAnnotation: "valid"}, ResourceVersion: "1"},
   256  				Spec: apiextensions.CustomResourceDefinitionSpec{
   257  					Validation: &apiextensions.CustomResourceValidation{
   258  						OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
   259  							Type: "object",
   260  							XValidations: apiextensions.ValidationRules{
   261  								{
   262  									Rule:    "size(self) > 0",
   263  									Message: "openAPIV3Schema should contain more than 0 element.",
   264  								},
   265  							},
   266  							Properties: map[string]apiextensions.JSONSchemaProps{
   267  								"subRule": {
   268  									Type: "object",
   269  									XValidations: apiextensions.ValidationRules{
   270  										{
   271  											Rule:    "isTest == true",
   272  											Message: "isTest should be true.",
   273  										},
   274  									},
   275  									Properties: map[string]apiextensions.JSONSchemaProps{
   276  										"isTest": {
   277  											Type: "boolean",
   278  										},
   279  									},
   280  								},
   281  							},
   282  						},
   283  					},
   284  				},
   285  			},
   286  		},
   287  		{
   288  			name:             "Ratcheting, For creation, FG enabled, set OptionalOldSelf, update with OptionalOldSelf",
   289  			enableRatcheting: true,
   290  			crd: &apiextensions.CustomResourceDefinition{
   291  				ObjectMeta: metav1.ObjectMeta{Name: "foos.sigs.k8s.io", Annotations: map[string]string{v1beta1.KubeAPIApprovedAnnotation: "valid"}, ResourceVersion: "1"},
   292  				Spec: apiextensions.CustomResourceDefinitionSpec{
   293  					Validation: &apiextensions.CustomResourceValidation{
   294  						OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
   295  							Type: "object",
   296  							XValidations: apiextensions.ValidationRules{
   297  								{
   298  									Rule:            "size(self) > 0",
   299  									Message:         "openAPIV3Schema should contain more than 0 element.",
   300  									OptionalOldSelf: ptr.To(true),
   301  								},
   302  							},
   303  							Properties: map[string]apiextensions.JSONSchemaProps{
   304  								"subRule": {
   305  									Type: "object",
   306  									XValidations: apiextensions.ValidationRules{
   307  										{
   308  											Rule:            "isTest == true",
   309  											Message:         "isTest should be true.",
   310  											OptionalOldSelf: ptr.To(true),
   311  										},
   312  									},
   313  									Properties: map[string]apiextensions.JSONSchemaProps{
   314  										"isTest": {
   315  											Type: "boolean",
   316  										},
   317  									},
   318  								},
   319  							},
   320  						},
   321  					},
   322  				},
   323  			},
   324  			oldCRD: nil,
   325  			expectedCRD: &apiextensions.CustomResourceDefinition{
   326  				ObjectMeta: metav1.ObjectMeta{Name: "foos.sigs.k8s.io", Annotations: map[string]string{v1beta1.KubeAPIApprovedAnnotation: "valid"}, ResourceVersion: "1"},
   327  				Spec: apiextensions.CustomResourceDefinitionSpec{
   328  					Validation: &apiextensions.CustomResourceValidation{
   329  						OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
   330  							Type: "object",
   331  							XValidations: apiextensions.ValidationRules{
   332  								{
   333  									Rule:            "size(self) > 0",
   334  									Message:         "openAPIV3Schema should contain more than 0 element.",
   335  									OptionalOldSelf: ptr.To(true),
   336  								},
   337  							},
   338  							Properties: map[string]apiextensions.JSONSchemaProps{
   339  								"subRule": {
   340  									Type: "object",
   341  									XValidations: apiextensions.ValidationRules{
   342  										{
   343  											Rule:            "isTest == true",
   344  											Message:         "isTest should be true.",
   345  											OptionalOldSelf: ptr.To(true),
   346  										},
   347  									},
   348  									Properties: map[string]apiextensions.JSONSchemaProps{
   349  										"isTest": {
   350  											Type: "boolean",
   351  										},
   352  									},
   353  								},
   354  							},
   355  						},
   356  					},
   357  				},
   358  			},
   359  		},
   360  		{
   361  			name:             "Ratcheting, For update, FG disabled, oldCRD OptionalOldSelf in use, don't drop OptionalOldSelfs",
   362  			enableRatcheting: false,
   363  			crd: &apiextensions.CustomResourceDefinition{
   364  				ObjectMeta: metav1.ObjectMeta{Name: "foos.sigs.k8s.io", Annotations: map[string]string{v1beta1.KubeAPIApprovedAnnotation: "valid"}, ResourceVersion: "1"},
   365  				Spec: apiextensions.CustomResourceDefinitionSpec{
   366  					Validation: &apiextensions.CustomResourceValidation{
   367  						OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
   368  							Type: "object",
   369  							XValidations: apiextensions.ValidationRules{
   370  								{
   371  									Rule:            "size(self) > 0",
   372  									Message:         "openAPIV3Schema should contain more than 0 element.",
   373  									OptionalOldSelf: ptr.To(true),
   374  								},
   375  							},
   376  							Properties: map[string]apiextensions.JSONSchemaProps{
   377  								"subRule": {
   378  									Type: "object",
   379  									XValidations: apiextensions.ValidationRules{
   380  										{
   381  											Rule:            "isTest == true",
   382  											Message:         "isTest should be true.",
   383  											OptionalOldSelf: ptr.To(true),
   384  										},
   385  									},
   386  									Properties: map[string]apiextensions.JSONSchemaProps{
   387  										"isTest": {
   388  											Type: "boolean",
   389  										},
   390  									},
   391  								},
   392  							},
   393  						},
   394  					},
   395  				},
   396  			},
   397  			oldCRD: &apiextensions.CustomResourceDefinition{
   398  				ObjectMeta: metav1.ObjectMeta{Name: "foos.sigs.k8s.io", Annotations: map[string]string{v1beta1.KubeAPIApprovedAnnotation: "valid"}, ResourceVersion: "1"},
   399  				Spec: apiextensions.CustomResourceDefinitionSpec{
   400  					Validation: &apiextensions.CustomResourceValidation{
   401  						OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
   402  							Type: "object",
   403  							Properties: map[string]apiextensions.JSONSchemaProps{
   404  								"otherRule": {
   405  									Type: "object",
   406  									XValidations: apiextensions.ValidationRules{
   407  										{
   408  											Rule:            "self.isTest == true",
   409  											Message:         "isTest should be true.",
   410  											OptionalOldSelf: ptr.To(true),
   411  										},
   412  									},
   413  								},
   414  							},
   415  						},
   416  					},
   417  				},
   418  			},
   419  			expectedCRD: &apiextensions.CustomResourceDefinition{
   420  				ObjectMeta: metav1.ObjectMeta{Name: "foos.sigs.k8s.io", Annotations: map[string]string{v1beta1.KubeAPIApprovedAnnotation: "valid"}, ResourceVersion: "1"},
   421  				Spec: apiextensions.CustomResourceDefinitionSpec{
   422  					Validation: &apiextensions.CustomResourceValidation{
   423  						OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
   424  							Type: "object",
   425  							XValidations: apiextensions.ValidationRules{
   426  								{
   427  									Rule:            "size(self) > 0",
   428  									Message:         "openAPIV3Schema should contain more than 0 element.",
   429  									OptionalOldSelf: ptr.To(true),
   430  								},
   431  							},
   432  							Properties: map[string]apiextensions.JSONSchemaProps{
   433  								"subRule": {
   434  									Type: "object",
   435  									XValidations: apiextensions.ValidationRules{
   436  										{
   437  											Rule:            "isTest == true",
   438  											Message:         "isTest should be true.",
   439  											OptionalOldSelf: ptr.To(true),
   440  										},
   441  									},
   442  									Properties: map[string]apiextensions.JSONSchemaProps{
   443  										"isTest": {
   444  											Type: "boolean",
   445  										},
   446  									},
   447  								},
   448  							},
   449  						},
   450  					},
   451  				},
   452  			},
   453  		},
   454  		{
   455  			name:             "Ratcheting, For update, FG disabled, oldCRD OptionalOldSelf in use, but different from new, don't drop OptionalOldSelfs",
   456  			enableRatcheting: false,
   457  			crd: &apiextensions.CustomResourceDefinition{
   458  				ObjectMeta: metav1.ObjectMeta{Name: "foos.sigs.k8s.io", Annotations: map[string]string{v1beta1.KubeAPIApprovedAnnotation: "valid"}, ResourceVersion: "1"},
   459  				Spec: apiextensions.CustomResourceDefinitionSpec{
   460  					Validation: &apiextensions.CustomResourceValidation{
   461  						OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
   462  							Type: "object",
   463  							XValidations: apiextensions.ValidationRules{
   464  								{
   465  									Rule:            "size(self) > 0",
   466  									Message:         "openAPIV3Schema should contain more than 0 element.",
   467  									OptionalOldSelf: ptr.To(true),
   468  								},
   469  							},
   470  							Properties: map[string]apiextensions.JSONSchemaProps{
   471  								"subRule": {
   472  									Type: "object",
   473  									XValidations: apiextensions.ValidationRules{
   474  										{
   475  											Rule:            "isTest == true",
   476  											Message:         "isTest should be true.",
   477  											OptionalOldSelf: ptr.To(true),
   478  										},
   479  									},
   480  									Properties: map[string]apiextensions.JSONSchemaProps{
   481  										"isTest": {
   482  											Type: "boolean",
   483  										},
   484  									},
   485  								},
   486  							},
   487  						},
   488  					},
   489  				},
   490  			},
   491  			oldCRD: &apiextensions.CustomResourceDefinition{
   492  				ObjectMeta: metav1.ObjectMeta{Name: "foos.sigs.k8s.io", Annotations: map[string]string{v1beta1.KubeAPIApprovedAnnotation: "valid"}, ResourceVersion: "1"},
   493  				Spec: apiextensions.CustomResourceDefinitionSpec{
   494  					Validation: &apiextensions.CustomResourceValidation{
   495  						OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
   496  							Type: "object",
   497  							Properties: map[string]apiextensions.JSONSchemaProps{
   498  								"subRule": {
   499  									Type: "object",
   500  									XValidations: apiextensions.ValidationRules{
   501  										{
   502  											Rule:            "isTest == true",
   503  											Message:         "isTest should be true.",
   504  											OptionalOldSelf: ptr.To(true),
   505  										},
   506  									},
   507  								},
   508  							},
   509  						},
   510  					},
   511  				},
   512  			},
   513  			expectedCRD: &apiextensions.CustomResourceDefinition{
   514  				ObjectMeta: metav1.ObjectMeta{Name: "foos.sigs.k8s.io", Annotations: map[string]string{v1beta1.KubeAPIApprovedAnnotation: "valid"}, ResourceVersion: "1"},
   515  				Spec: apiextensions.CustomResourceDefinitionSpec{
   516  					Validation: &apiextensions.CustomResourceValidation{
   517  						OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
   518  							Type: "object",
   519  							XValidations: apiextensions.ValidationRules{
   520  								{
   521  									Rule:            "size(self) > 0",
   522  									Message:         "openAPIV3Schema should contain more than 0 element.",
   523  									OptionalOldSelf: ptr.To(true),
   524  								},
   525  							},
   526  							Properties: map[string]apiextensions.JSONSchemaProps{
   527  								"subRule": {
   528  									Type: "object",
   529  									XValidations: apiextensions.ValidationRules{
   530  										{
   531  											Rule:            "isTest == true",
   532  											Message:         "isTest should be true.",
   533  											OptionalOldSelf: ptr.To(true),
   534  										},
   535  									},
   536  									Properties: map[string]apiextensions.JSONSchemaProps{
   537  										"isTest": {
   538  											Type: "boolean",
   539  										},
   540  									},
   541  								},
   542  							},
   543  						},
   544  					},
   545  				},
   546  			},
   547  		},
   548  		{
   549  			name:             "Ratcheting, For update, FG disabled, oldCRD has no OptionalOldSelf, drop OptionalOldSelf",
   550  			enableRatcheting: false,
   551  			crd: &apiextensions.CustomResourceDefinition{
   552  				ObjectMeta: metav1.ObjectMeta{Name: "foos.sigs.k8s.io", Annotations: map[string]string{v1beta1.KubeAPIApprovedAnnotation: "valid"}, ResourceVersion: "1"},
   553  				Spec: apiextensions.CustomResourceDefinitionSpec{
   554  					Validation: &apiextensions.CustomResourceValidation{
   555  						OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
   556  							Type: "object",
   557  							XValidations: apiextensions.ValidationRules{
   558  								{
   559  									Rule:            "size(self) > 0",
   560  									Message:         "openAPIV3Schema should contain more than 0 element.",
   561  									OptionalOldSelf: ptr.To(true),
   562  								},
   563  							},
   564  						},
   565  					},
   566  				},
   567  			},
   568  			oldCRD: &apiextensions.CustomResourceDefinition{
   569  				ObjectMeta: metav1.ObjectMeta{Name: "foos.sigs.k8s.io", Annotations: map[string]string{v1beta1.KubeAPIApprovedAnnotation: "valid"}, ResourceVersion: "1"},
   570  				Spec: apiextensions.CustomResourceDefinitionSpec{
   571  					Validation: &apiextensions.CustomResourceValidation{
   572  						OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
   573  							Type: "object",
   574  						},
   575  					},
   576  				},
   577  			},
   578  			expectedCRD: &apiextensions.CustomResourceDefinition{
   579  				ObjectMeta: metav1.ObjectMeta{Name: "foos.sigs.k8s.io", Annotations: map[string]string{v1beta1.KubeAPIApprovedAnnotation: "valid"}, ResourceVersion: "1"},
   580  				Spec: apiextensions.CustomResourceDefinitionSpec{
   581  					Validation: &apiextensions.CustomResourceValidation{
   582  						OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
   583  							Type: "object",
   584  							XValidations: apiextensions.ValidationRules{
   585  								{
   586  									Rule:    "size(self) > 0",
   587  									Message: "openAPIV3Schema should contain more than 0 element.",
   588  								},
   589  							},
   590  						},
   591  					},
   592  				},
   593  			},
   594  		},
   595  		{
   596  			name:             "Ratcheting, For update, FG enabled, oldCRD has optionalOldSelf, updated to newCRD",
   597  			enableRatcheting: true,
   598  			crd: &apiextensions.CustomResourceDefinition{
   599  				ObjectMeta: metav1.ObjectMeta{Name: "foos.sigs.k8s.io", Annotations: map[string]string{v1beta1.KubeAPIApprovedAnnotation: "valid"}, ResourceVersion: "1"},
   600  				Spec: apiextensions.CustomResourceDefinitionSpec{
   601  					Validation: &apiextensions.CustomResourceValidation{
   602  						OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
   603  							Type: "object",
   604  							XValidations: apiextensions.ValidationRules{
   605  								{
   606  									Rule:            "size(self) > 0",
   607  									Message:         "openAPIV3Schema should contain more than 0 element.",
   608  									OptionalOldSelf: ptr.To(true),
   609  								},
   610  							},
   611  						},
   612  					},
   613  				},
   614  			},
   615  			oldCRD: &apiextensions.CustomResourceDefinition{
   616  				ObjectMeta: metav1.ObjectMeta{Name: "foos.sigs.k8s.io", Annotations: map[string]string{v1beta1.KubeAPIApprovedAnnotation: "valid"}, ResourceVersion: "1"},
   617  				Spec: apiextensions.CustomResourceDefinitionSpec{
   618  					Validation: &apiextensions.CustomResourceValidation{
   619  						OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
   620  							Type: "object",
   621  							XValidations: apiextensions.ValidationRules{
   622  								{
   623  									Rule:            "old data",
   624  									Message:         "old data",
   625  									OptionalOldSelf: ptr.To(true),
   626  								},
   627  							},
   628  						},
   629  					},
   630  				},
   631  			},
   632  			expectedCRD: &apiextensions.CustomResourceDefinition{
   633  				ObjectMeta: metav1.ObjectMeta{Name: "foos.sigs.k8s.io", Annotations: map[string]string{v1beta1.KubeAPIApprovedAnnotation: "valid"}, ResourceVersion: "1"},
   634  				Spec: apiextensions.CustomResourceDefinitionSpec{
   635  					Validation: &apiextensions.CustomResourceValidation{
   636  						OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
   637  							Type: "object",
   638  							XValidations: apiextensions.ValidationRules{
   639  								{
   640  									Rule:            "size(self) > 0",
   641  									Message:         "openAPIV3Schema should contain more than 0 element.",
   642  									OptionalOldSelf: ptr.To(true),
   643  								},
   644  							},
   645  						},
   646  					},
   647  				},
   648  			},
   649  		},
   650  		{
   651  			name:             "Ratcheting, For update, FG enabled, oldCRD has no OptionalOldSelf, updated to newCRD",
   652  			enableRatcheting: true,
   653  			crd: &apiextensions.CustomResourceDefinition{
   654  				ObjectMeta: metav1.ObjectMeta{Name: "foos.sigs.k8s.io", Annotations: map[string]string{v1beta1.KubeAPIApprovedAnnotation: "valid"}, ResourceVersion: "1"},
   655  				Spec: apiextensions.CustomResourceDefinitionSpec{
   656  					Validation: &apiextensions.CustomResourceValidation{
   657  						OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
   658  							Type: "object",
   659  							XValidations: apiextensions.ValidationRules{
   660  								{
   661  									Rule:            "size(self) > 0",
   662  									Message:         "openAPIV3Schema should contain more than 0 element.",
   663  									OptionalOldSelf: ptr.To(true),
   664  								},
   665  							},
   666  						},
   667  					},
   668  				},
   669  			},
   670  			oldCRD: &apiextensions.CustomResourceDefinition{
   671  				ObjectMeta: metav1.ObjectMeta{Name: "foos.sigs.k8s.io", Annotations: map[string]string{v1beta1.KubeAPIApprovedAnnotation: "valid"}, ResourceVersion: "1"},
   672  				Spec: apiextensions.CustomResourceDefinitionSpec{
   673  					Validation: &apiextensions.CustomResourceValidation{
   674  						OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
   675  							Type: "object",
   676  						},
   677  					},
   678  				},
   679  			},
   680  			expectedCRD: &apiextensions.CustomResourceDefinition{
   681  				ObjectMeta: metav1.ObjectMeta{Name: "foos.sigs.k8s.io", Annotations: map[string]string{v1beta1.KubeAPIApprovedAnnotation: "valid"}, ResourceVersion: "1"},
   682  				Spec: apiextensions.CustomResourceDefinitionSpec{
   683  					Validation: &apiextensions.CustomResourceValidation{
   684  						OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
   685  							Type: "object",
   686  							XValidations: apiextensions.ValidationRules{
   687  								{
   688  									Rule:            "size(self) > 0",
   689  									Message:         "openAPIV3Schema should contain more than 0 element.",
   690  									OptionalOldSelf: ptr.To(true),
   691  								},
   692  							},
   693  						},
   694  					},
   695  				},
   696  			},
   697  		},
   698  		// SelectableFields
   699  		{
   700  			name:                   "SelectableFields, For create, FG disabled, SelectableFields in update, dropped",
   701  			enableSelectableFields: false,
   702  			crd: &apiextensions.CustomResourceDefinition{
   703  				ObjectMeta: metav1.ObjectMeta{Name: "foos.sigs.k8s.io", Annotations: map[string]string{v1beta1.KubeAPIApprovedAnnotation: "valid"}, ResourceVersion: "1"},
   704  				Spec: apiextensions.CustomResourceDefinitionSpec{
   705  					Validation: &apiextensions.CustomResourceValidation{
   706  						OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
   707  							Type: "object",
   708  							Properties: map[string]apiextensions.JSONSchemaProps{
   709  								"field": {
   710  									Type: "string",
   711  								},
   712  							},
   713  						},
   714  					},
   715  					SelectableFields: []apiextensions.SelectableField{
   716  						{
   717  							JSONPath: ".field",
   718  						},
   719  					},
   720  				},
   721  			},
   722  			expectedCRD: &apiextensions.CustomResourceDefinition{
   723  				ObjectMeta: metav1.ObjectMeta{Name: "foos.sigs.k8s.io", Annotations: map[string]string{v1beta1.KubeAPIApprovedAnnotation: "valid"}, ResourceVersion: "1"},
   724  				Spec: apiextensions.CustomResourceDefinitionSpec{
   725  					Validation: &apiextensions.CustomResourceValidation{
   726  						OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
   727  							Type: "object",
   728  							Properties: map[string]apiextensions.JSONSchemaProps{
   729  								"field": {
   730  									Type: "string",
   731  								},
   732  							},
   733  						},
   734  					},
   735  				},
   736  			},
   737  		},
   738  		{
   739  			name:                   "SelectableFields, For create, FG enabled, no SelectableFields in update, no drop",
   740  			enableSelectableFields: true,
   741  			crd: &apiextensions.CustomResourceDefinition{
   742  				ObjectMeta: metav1.ObjectMeta{Name: "foos.sigs.k8s.io", Annotations: map[string]string{v1beta1.KubeAPIApprovedAnnotation: "valid"}, ResourceVersion: "1"},
   743  				Spec: apiextensions.CustomResourceDefinitionSpec{
   744  					Validation: &apiextensions.CustomResourceValidation{
   745  						OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
   746  							Type: "object",
   747  							Properties: map[string]apiextensions.JSONSchemaProps{
   748  								"field": {
   749  									Type: "string",
   750  								},
   751  							},
   752  						},
   753  					},
   754  				},
   755  			},
   756  			expectedCRD: &apiextensions.CustomResourceDefinition{
   757  				ObjectMeta: metav1.ObjectMeta{Name: "foos.sigs.k8s.io", Annotations: map[string]string{v1beta1.KubeAPIApprovedAnnotation: "valid"}, ResourceVersion: "1"},
   758  				Spec: apiextensions.CustomResourceDefinitionSpec{
   759  					Validation: &apiextensions.CustomResourceValidation{
   760  						OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
   761  							Type: "object",
   762  							Properties: map[string]apiextensions.JSONSchemaProps{
   763  								"field": {
   764  									Type: "string",
   765  								},
   766  							},
   767  						},
   768  					},
   769  				},
   770  			},
   771  		},
   772  		{
   773  			name:                   "SelectableFields, For create, FG enabled, SelectableFields in update, no drop",
   774  			enableSelectableFields: true,
   775  			crd: &apiextensions.CustomResourceDefinition{
   776  				ObjectMeta: metav1.ObjectMeta{Name: "foos.sigs.k8s.io", Annotations: map[string]string{v1beta1.KubeAPIApprovedAnnotation: "valid"}, ResourceVersion: "1"},
   777  				Spec: apiextensions.CustomResourceDefinitionSpec{
   778  					Validation: &apiextensions.CustomResourceValidation{
   779  						OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
   780  							Type: "object",
   781  							Properties: map[string]apiextensions.JSONSchemaProps{
   782  								"field": {
   783  									Type: "string",
   784  								},
   785  							},
   786  						},
   787  					},
   788  					SelectableFields: []apiextensions.SelectableField{
   789  						{
   790  							JSONPath: ".field",
   791  						},
   792  					},
   793  				},
   794  			},
   795  			expectedCRD: &apiextensions.CustomResourceDefinition{
   796  				ObjectMeta: metav1.ObjectMeta{Name: "foos.sigs.k8s.io", Annotations: map[string]string{v1beta1.KubeAPIApprovedAnnotation: "valid"}, ResourceVersion: "1"},
   797  				Spec: apiextensions.CustomResourceDefinitionSpec{
   798  					Validation: &apiextensions.CustomResourceValidation{
   799  						OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
   800  							Type: "object",
   801  							Properties: map[string]apiextensions.JSONSchemaProps{
   802  								"field": {
   803  									Type: "string",
   804  								},
   805  							},
   806  						},
   807  					},
   808  					SelectableFields: []apiextensions.SelectableField{
   809  						{
   810  							JSONPath: ".field",
   811  						},
   812  					},
   813  				},
   814  			},
   815  		},
   816  		{
   817  			name:                   "SelectableFields, For update, FG disabled, oldCRD has SelectableFields, SelectableFields in update, no drop",
   818  			enableSelectableFields: false,
   819  			crd: &apiextensions.CustomResourceDefinition{
   820  				ObjectMeta: metav1.ObjectMeta{Name: "foos.sigs.k8s.io", Annotations: map[string]string{v1beta1.KubeAPIApprovedAnnotation: "valid"}, ResourceVersion: "1"},
   821  				Spec: apiextensions.CustomResourceDefinitionSpec{
   822  					Validation: &apiextensions.CustomResourceValidation{
   823  						OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
   824  							Type: "object",
   825  							Properties: map[string]apiextensions.JSONSchemaProps{
   826  								"field1": {
   827  									Type: "string",
   828  								},
   829  								"field2": {
   830  									Type: "string",
   831  								},
   832  							},
   833  						},
   834  					},
   835  					SelectableFields: []apiextensions.SelectableField{
   836  						{
   837  							JSONPath: ".field1",
   838  						},
   839  						{
   840  							JSONPath: ".field2",
   841  						},
   842  					},
   843  				},
   844  			},
   845  			oldCRD: &apiextensions.CustomResourceDefinition{
   846  				ObjectMeta: metav1.ObjectMeta{Name: "foos.sigs.k8s.io", Annotations: map[string]string{v1beta1.KubeAPIApprovedAnnotation: "valid"}, ResourceVersion: "1"},
   847  				Spec: apiextensions.CustomResourceDefinitionSpec{
   848  					Validation: &apiextensions.CustomResourceValidation{
   849  						OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
   850  							Type: "object",
   851  							Properties: map[string]apiextensions.JSONSchemaProps{
   852  								"field1": {
   853  									Type: "string",
   854  								},
   855  							},
   856  						},
   857  					},
   858  					SelectableFields: []apiextensions.SelectableField{
   859  						{
   860  							JSONPath: ".field1",
   861  						},
   862  					},
   863  				},
   864  			},
   865  			expectedCRD: &apiextensions.CustomResourceDefinition{
   866  				ObjectMeta: metav1.ObjectMeta{Name: "foos.sigs.k8s.io", Annotations: map[string]string{v1beta1.KubeAPIApprovedAnnotation: "valid"}, ResourceVersion: "1"},
   867  				Spec: apiextensions.CustomResourceDefinitionSpec{
   868  					Validation: &apiextensions.CustomResourceValidation{
   869  						OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
   870  							Type: "object",
   871  							Properties: map[string]apiextensions.JSONSchemaProps{
   872  								"field1": {
   873  									Type: "string",
   874  								},
   875  								"field2": {
   876  									Type: "string",
   877  								},
   878  							},
   879  						},
   880  					},
   881  					SelectableFields: []apiextensions.SelectableField{
   882  						{
   883  							JSONPath: ".field1",
   884  						},
   885  						{
   886  							JSONPath: ".field2",
   887  						},
   888  					},
   889  				},
   890  			},
   891  		},
   892  		{
   893  			name:                   "SelectableFields, For update, FG disabled, oldCRD does not have SelectableFields, no SelectableFields in update, no drop",
   894  			enableSelectableFields: false,
   895  			crd: &apiextensions.CustomResourceDefinition{
   896  				ObjectMeta: metav1.ObjectMeta{Name: "foos.sigs.k8s.io", Annotations: map[string]string{v1beta1.KubeAPIApprovedAnnotation: "valid"}, ResourceVersion: "1"},
   897  				Spec: apiextensions.CustomResourceDefinitionSpec{
   898  					Validation: &apiextensions.CustomResourceValidation{
   899  						OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
   900  							Type: "object",
   901  							Properties: map[string]apiextensions.JSONSchemaProps{
   902  								"field1": {
   903  									Type: "string",
   904  								},
   905  								"field2": {
   906  									Type: "string",
   907  								},
   908  							},
   909  						},
   910  					},
   911  				},
   912  			},
   913  			oldCRD: &apiextensions.CustomResourceDefinition{
   914  				ObjectMeta: metav1.ObjectMeta{Name: "foos.sigs.k8s.io", Annotations: map[string]string{v1beta1.KubeAPIApprovedAnnotation: "valid"}, ResourceVersion: "1"},
   915  				Spec: apiextensions.CustomResourceDefinitionSpec{
   916  					Validation: &apiextensions.CustomResourceValidation{
   917  						OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
   918  							Type: "object",
   919  							Properties: map[string]apiextensions.JSONSchemaProps{
   920  								"field1": {
   921  									Type: "string",
   922  								},
   923  							},
   924  						},
   925  					},
   926  				},
   927  			},
   928  			expectedCRD: &apiextensions.CustomResourceDefinition{
   929  				ObjectMeta: metav1.ObjectMeta{Name: "foos.sigs.k8s.io", Annotations: map[string]string{v1beta1.KubeAPIApprovedAnnotation: "valid"}, ResourceVersion: "1"},
   930  				Spec: apiextensions.CustomResourceDefinitionSpec{
   931  					Validation: &apiextensions.CustomResourceValidation{
   932  						OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
   933  							Type: "object",
   934  							Properties: map[string]apiextensions.JSONSchemaProps{
   935  								"field1": {
   936  									Type: "string",
   937  								},
   938  								"field2": {
   939  									Type: "string",
   940  								},
   941  							},
   942  						},
   943  					},
   944  				},
   945  			},
   946  		},
   947  		{
   948  			name:                   "SelectableFields, For update, FG disabled, oldCRD does not have SelectableFields, SelectableFields in update, dropped",
   949  			enableSelectableFields: false,
   950  			crd: &apiextensions.CustomResourceDefinition{
   951  				ObjectMeta: metav1.ObjectMeta{Name: "foos.sigs.k8s.io", Annotations: map[string]string{v1beta1.KubeAPIApprovedAnnotation: "valid"}, ResourceVersion: "1"},
   952  				Spec: apiextensions.CustomResourceDefinitionSpec{
   953  					Validation: &apiextensions.CustomResourceValidation{
   954  						OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
   955  							Type: "object",
   956  							Properties: map[string]apiextensions.JSONSchemaProps{
   957  								"field1": {
   958  									Type: "string",
   959  								},
   960  								"field2": {
   961  									Type: "string",
   962  								},
   963  							},
   964  						},
   965  					},
   966  					SelectableFields: []apiextensions.SelectableField{
   967  						{
   968  							JSONPath: ".field1",
   969  						},
   970  						{
   971  							JSONPath: ".field2",
   972  						},
   973  					},
   974  				},
   975  			},
   976  			oldCRD: &apiextensions.CustomResourceDefinition{
   977  				ObjectMeta: metav1.ObjectMeta{Name: "foos.sigs.k8s.io", Annotations: map[string]string{v1beta1.KubeAPIApprovedAnnotation: "valid"}, ResourceVersion: "1"},
   978  				Spec: apiextensions.CustomResourceDefinitionSpec{
   979  					Validation: &apiextensions.CustomResourceValidation{
   980  						OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
   981  							Type: "object",
   982  							Properties: map[string]apiextensions.JSONSchemaProps{
   983  								"field1": {
   984  									Type: "string",
   985  								},
   986  							},
   987  						},
   988  					},
   989  				},
   990  			},
   991  			expectedCRD: &apiextensions.CustomResourceDefinition{
   992  				ObjectMeta: metav1.ObjectMeta{Name: "foos.sigs.k8s.io", Annotations: map[string]string{v1beta1.KubeAPIApprovedAnnotation: "valid"}, ResourceVersion: "1"},
   993  				Spec: apiextensions.CustomResourceDefinitionSpec{
   994  					Validation: &apiextensions.CustomResourceValidation{
   995  						OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
   996  							Type: "object",
   997  							Properties: map[string]apiextensions.JSONSchemaProps{
   998  								"field1": {
   999  									Type: "string",
  1000  								},
  1001  								"field2": {
  1002  									Type: "string",
  1003  								},
  1004  							},
  1005  						},
  1006  					},
  1007  				},
  1008  			},
  1009  		},
  1010  		{
  1011  			name:                   "SelectableFields, For update, FG enabled, oldCRD has SelectableFields, SelectableFields in update, no drop",
  1012  			enableSelectableFields: true,
  1013  			crd: &apiextensions.CustomResourceDefinition{
  1014  				ObjectMeta: metav1.ObjectMeta{Name: "foos.sigs.k8s.io", Annotations: map[string]string{v1beta1.KubeAPIApprovedAnnotation: "valid"}, ResourceVersion: "1"},
  1015  				Spec: apiextensions.CustomResourceDefinitionSpec{
  1016  					Validation: &apiextensions.CustomResourceValidation{
  1017  						OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
  1018  							Type: "object",
  1019  							Properties: map[string]apiextensions.JSONSchemaProps{
  1020  								"field1": {
  1021  									Type: "string",
  1022  								},
  1023  								"field2": {
  1024  									Type: "string",
  1025  								},
  1026  							},
  1027  						},
  1028  					},
  1029  					SelectableFields: []apiextensions.SelectableField{
  1030  						{
  1031  							JSONPath: ".field1",
  1032  						},
  1033  						{
  1034  							JSONPath: ".field2",
  1035  						},
  1036  					},
  1037  				},
  1038  			},
  1039  			oldCRD: &apiextensions.CustomResourceDefinition{
  1040  				ObjectMeta: metav1.ObjectMeta{Name: "foos.sigs.k8s.io", Annotations: map[string]string{v1beta1.KubeAPIApprovedAnnotation: "valid"}, ResourceVersion: "1"},
  1041  				Spec: apiextensions.CustomResourceDefinitionSpec{
  1042  					Validation: &apiextensions.CustomResourceValidation{
  1043  						OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
  1044  							Type: "object",
  1045  							Properties: map[string]apiextensions.JSONSchemaProps{
  1046  								"field1": {
  1047  									Type: "string",
  1048  								},
  1049  							},
  1050  						},
  1051  					},
  1052  					SelectableFields: []apiextensions.SelectableField{
  1053  						{
  1054  							JSONPath: ".field1",
  1055  						},
  1056  					},
  1057  				},
  1058  			},
  1059  			expectedCRD: &apiextensions.CustomResourceDefinition{
  1060  				ObjectMeta: metav1.ObjectMeta{Name: "foos.sigs.k8s.io", Annotations: map[string]string{v1beta1.KubeAPIApprovedAnnotation: "valid"}, ResourceVersion: "1"},
  1061  				Spec: apiextensions.CustomResourceDefinitionSpec{
  1062  					Validation: &apiextensions.CustomResourceValidation{
  1063  						OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
  1064  							Type: "object",
  1065  							Properties: map[string]apiextensions.JSONSchemaProps{
  1066  								"field1": {
  1067  									Type: "string",
  1068  								},
  1069  								"field2": {
  1070  									Type: "string",
  1071  								},
  1072  							},
  1073  						},
  1074  					},
  1075  					SelectableFields: []apiextensions.SelectableField{
  1076  						{
  1077  							JSONPath: ".field1",
  1078  						},
  1079  						{
  1080  							JSONPath: ".field2",
  1081  						},
  1082  					},
  1083  				},
  1084  			},
  1085  		},
  1086  		{
  1087  			name:                   "SelectableFields, For update, FG enabled, oldCRD does not have SelectableFields, SelectableFields in update, no drop",
  1088  			enableSelectableFields: true,
  1089  			crd: &apiextensions.CustomResourceDefinition{
  1090  				ObjectMeta: metav1.ObjectMeta{Name: "foos.sigs.k8s.io", Annotations: map[string]string{v1beta1.KubeAPIApprovedAnnotation: "valid"}, ResourceVersion: "1"},
  1091  				Spec: apiextensions.CustomResourceDefinitionSpec{
  1092  					Validation: &apiextensions.CustomResourceValidation{
  1093  						OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
  1094  							Type: "object",
  1095  							Properties: map[string]apiextensions.JSONSchemaProps{
  1096  								"field1": {
  1097  									Type: "string",
  1098  								},
  1099  								"field2": {
  1100  									Type: "string",
  1101  								},
  1102  							},
  1103  						},
  1104  					},
  1105  					SelectableFields: []apiextensions.SelectableField{
  1106  						{
  1107  							JSONPath: ".field1",
  1108  						},
  1109  						{
  1110  							JSONPath: ".field2",
  1111  						},
  1112  					},
  1113  				},
  1114  			},
  1115  			oldCRD: &apiextensions.CustomResourceDefinition{
  1116  				ObjectMeta: metav1.ObjectMeta{Name: "foos.sigs.k8s.io", Annotations: map[string]string{v1beta1.KubeAPIApprovedAnnotation: "valid"}, ResourceVersion: "1"},
  1117  				Spec: apiextensions.CustomResourceDefinitionSpec{
  1118  					Validation: &apiextensions.CustomResourceValidation{
  1119  						OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
  1120  							Type: "object",
  1121  							Properties: map[string]apiextensions.JSONSchemaProps{
  1122  								"field1": {
  1123  									Type: "string",
  1124  								},
  1125  							},
  1126  						},
  1127  					},
  1128  					SelectableFields: []apiextensions.SelectableField{
  1129  						{
  1130  							JSONPath: ".field1",
  1131  						},
  1132  					},
  1133  				},
  1134  			},
  1135  			expectedCRD: &apiextensions.CustomResourceDefinition{
  1136  				ObjectMeta: metav1.ObjectMeta{Name: "foos.sigs.k8s.io", Annotations: map[string]string{v1beta1.KubeAPIApprovedAnnotation: "valid"}, ResourceVersion: "1"},
  1137  				Spec: apiextensions.CustomResourceDefinitionSpec{
  1138  					Validation: &apiextensions.CustomResourceValidation{
  1139  						OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
  1140  							Type: "object",
  1141  							Properties: map[string]apiextensions.JSONSchemaProps{
  1142  								"field1": {
  1143  									Type: "string",
  1144  								},
  1145  								"field2": {
  1146  									Type: "string",
  1147  								},
  1148  							},
  1149  						},
  1150  					},
  1151  					SelectableFields: []apiextensions.SelectableField{
  1152  						{
  1153  							JSONPath: ".field1",
  1154  						},
  1155  						{
  1156  							JSONPath: ".field2",
  1157  						},
  1158  					},
  1159  				},
  1160  			},
  1161  		},
  1162  		{
  1163  			name:                   "pre-version SelectableFields, For update, FG disabled, oldCRD does not have SelectableFields, SelectableFields in update, dropped",
  1164  			enableSelectableFields: false,
  1165  			crd: &apiextensions.CustomResourceDefinition{
  1166  				ObjectMeta: metav1.ObjectMeta{Name: "foos.sigs.k8s.io", Annotations: map[string]string{v1beta1.KubeAPIApprovedAnnotation: "valid"}, ResourceVersion: "1"},
  1167  				Spec: apiextensions.CustomResourceDefinitionSpec{
  1168  					Versions: []apiextensions.CustomResourceDefinitionVersion{
  1169  						{
  1170  							Name: "v1",
  1171  							Schema: &apiextensions.CustomResourceValidation{
  1172  								OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
  1173  									Type: "object",
  1174  									Properties: map[string]apiextensions.JSONSchemaProps{
  1175  										"field1": {
  1176  											Type: "string",
  1177  										},
  1178  										"field2": {
  1179  											Type: "string",
  1180  										},
  1181  									},
  1182  								},
  1183  							},
  1184  							SelectableFields: []apiextensions.SelectableField{
  1185  								{
  1186  									JSONPath: ".field1",
  1187  								},
  1188  								{
  1189  									JSONPath: ".field2",
  1190  								},
  1191  							},
  1192  						},
  1193  						{
  1194  							Name: "v2",
  1195  							Schema: &apiextensions.CustomResourceValidation{
  1196  								OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
  1197  									Type: "object",
  1198  									Properties: map[string]apiextensions.JSONSchemaProps{
  1199  										"field3": {
  1200  											Type: "string",
  1201  										},
  1202  										"field4": {
  1203  											Type: "string",
  1204  										},
  1205  									},
  1206  								},
  1207  							},
  1208  							SelectableFields: []apiextensions.SelectableField{
  1209  								{
  1210  									JSONPath: ".field3",
  1211  								},
  1212  								{
  1213  									JSONPath: ".field4",
  1214  								},
  1215  							},
  1216  						},
  1217  					},
  1218  				},
  1219  			},
  1220  			oldCRD: &apiextensions.CustomResourceDefinition{
  1221  				ObjectMeta: metav1.ObjectMeta{Name: "foos.sigs.k8s.io", Annotations: map[string]string{v1beta1.KubeAPIApprovedAnnotation: "valid"}, ResourceVersion: "1"},
  1222  				Spec: apiextensions.CustomResourceDefinitionSpec{
  1223  					Versions: []apiextensions.CustomResourceDefinitionVersion{
  1224  						{
  1225  							Name: "v1",
  1226  							Schema: &apiextensions.CustomResourceValidation{
  1227  								OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
  1228  									Type: "object",
  1229  									Properties: map[string]apiextensions.JSONSchemaProps{
  1230  										"field1": {
  1231  											Type: "string",
  1232  										},
  1233  										"field2": {
  1234  											Type: "string",
  1235  										},
  1236  									},
  1237  								},
  1238  							},
  1239  						},
  1240  						{
  1241  							Name: "v2",
  1242  							Schema: &apiextensions.CustomResourceValidation{
  1243  								OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
  1244  									Type: "object",
  1245  									Properties: map[string]apiextensions.JSONSchemaProps{
  1246  										"field3": {
  1247  											Type: "string",
  1248  										},
  1249  										"field4": {
  1250  											Type: "string",
  1251  										},
  1252  									},
  1253  								},
  1254  							},
  1255  						},
  1256  					},
  1257  				},
  1258  			},
  1259  			expectedCRD: &apiextensions.CustomResourceDefinition{
  1260  				ObjectMeta: metav1.ObjectMeta{Name: "foos.sigs.k8s.io", Annotations: map[string]string{v1beta1.KubeAPIApprovedAnnotation: "valid"}, ResourceVersion: "1"},
  1261  				Spec: apiextensions.CustomResourceDefinitionSpec{
  1262  					Versions: []apiextensions.CustomResourceDefinitionVersion{
  1263  						{
  1264  							Name: "v1",
  1265  							Schema: &apiextensions.CustomResourceValidation{
  1266  								OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
  1267  									Type: "object",
  1268  									Properties: map[string]apiextensions.JSONSchemaProps{
  1269  										"field1": {
  1270  											Type: "string",
  1271  										},
  1272  										"field2": {
  1273  											Type: "string",
  1274  										},
  1275  									},
  1276  								},
  1277  							},
  1278  						},
  1279  						{
  1280  							Name: "v2",
  1281  							Schema: &apiextensions.CustomResourceValidation{
  1282  								OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
  1283  									Type: "object",
  1284  									Properties: map[string]apiextensions.JSONSchemaProps{
  1285  										"field3": {
  1286  											Type: "string",
  1287  										},
  1288  										"field4": {
  1289  											Type: "string",
  1290  										},
  1291  									},
  1292  								},
  1293  							},
  1294  						},
  1295  					},
  1296  				},
  1297  			},
  1298  		},
  1299  	}
  1300  	for _, tc := range testCases {
  1301  		t.Run(tc.name, func(t *testing.T) {
  1302  			defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, apiextensionsfeatures.CRDValidationRatcheting, tc.enableRatcheting)()
  1303  			defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, apiextensionsfeatures.CustomResourceFieldSelectors, tc.enableSelectableFields)()
  1304  			old := tc.oldCRD.DeepCopy()
  1305  
  1306  			dropDisabledFields(tc.crd, tc.oldCRD)
  1307  
  1308  			// old crd should never be changed
  1309  			if diff := cmp.Diff(tc.oldCRD, old); diff != "" {
  1310  				t.Fatalf("old crd changed from %v to %v\n%v", tc.oldCRD, old, diff)
  1311  			}
  1312  
  1313  			if diff := cmp.Diff(tc.expectedCRD, tc.crd); diff != "" {
  1314  				t.Fatalf("unexpected crd: %v\n%v", tc.crd, diff)
  1315  			}
  1316  		})
  1317  	}
  1318  }
  1319  

View as plain text