1
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
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
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
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