@@ -33,19 +33,31 @@ import (
3333 "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
3434 apierrors "k8s.io/apimachinery/pkg/api/errors"
3535 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
36+ "k8s.io/apimachinery/pkg/runtime"
3637 "k8s.io/apimachinery/pkg/runtime/schema"
3738 "k8s.io/apimachinery/pkg/util/sets"
3839 "k8s.io/apimachinery/pkg/util/wait"
3940 k8syaml "k8s.io/apimachinery/pkg/util/yaml"
41+ "k8s.io/client-go/kubernetes/scheme"
4042 "k8s.io/client-go/rest"
4143 "k8s.io/client-go/util/retry"
4244 "k8s.io/utils/pointer"
4345 "sigs.k8s.io/controller-runtime/pkg/client"
46+ "sigs.k8s.io/controller-runtime/pkg/webhook/conversion"
4447 "sigs.k8s.io/yaml"
4548)
4649
4750// CRDInstallOptions are the options for installing CRDs
4851type CRDInstallOptions struct {
52+ // Scheme is used to determine if conversion webhooks should be enabled
53+ // for a particular CRD / object.
54+ //
55+ // Conversion webhooks are going to be enabled if an object in the scheme
56+ // implements Hub and Spoke conversions.
57+ //
58+ // If nil, scheme.Scheme is used.
59+ Scheme * runtime.Scheme
60+
4961 // Paths is a list of paths to the directories or files containing CRDs
5062 Paths []string
5163
@@ -86,7 +98,7 @@ func InstallCRDs(config *rest.Config, options CRDInstallOptions) ([]client.Objec
8698 return nil , err
8799 }
88100
89- if err := modifyConversionWebhooks (options .CRDs , options .WebhookOptions ); err != nil {
101+ if err := modifyConversionWebhooks (options .CRDs , options .Scheme , options . WebhookOptions ); err != nil {
90102 return nil , err
91103 }
92104
@@ -118,6 +130,9 @@ func readCRDFiles(options *CRDInstallOptions) error {
118130
119131// defaultCRDOptions sets the default values for CRDs
120132func defaultCRDOptions (o * CRDInstallOptions ) {
133+ if o .Scheme == nil {
134+ o .Scheme = scheme .Scheme
135+ }
121136 if o .MaxTime == 0 {
122137 o .MaxTime = defaultMaxWait
123138 }
@@ -356,11 +371,30 @@ func renderCRDs(options *CRDInstallOptions) ([]client.Object, error) {
356371 return res , nil
357372}
358373
359- func modifyConversionWebhooks (crds []client.Object , webhookOptions WebhookInstallOptions ) error {
374+ // modifyConversionWebhooks takes all the registered CustomResourceDefinitions and applies modifications
375+ // to conditionally enable webhooks if the type is registered within the scheme.
376+ //
377+ // The complexity of this function is high mostly due to all the edge cases that we need to handle:
378+ // CRDv1beta1, CRDv1, and their unstructured counterpart.
379+ //
380+ // We should be able to simplify this code once we drop support for v1beta1 and standardize around the typed CRDv1 object.
381+ func modifyConversionWebhooks (crds []client.Object , scheme * runtime.Scheme , webhookOptions WebhookInstallOptions ) error { //nolint:gocyclo
360382 if len (webhookOptions .LocalServingCAData ) == 0 {
361383 return nil
362384 }
363385
386+ // Determine all registered convertible types.
387+ convertibles := map [schema.GroupKind ]struct {}{}
388+ for gvk := range scheme .AllKnownTypes () {
389+ obj , err := scheme .New (gvk )
390+ if err != nil {
391+ return err
392+ }
393+ if ok , err := conversion .IsConvertible (scheme , obj ); ok && err == nil {
394+ convertibles [gvk .GroupKind ()] = struct {}{}
395+ }
396+ }
397+
364398 // generate host port.
365399 hostPort , err := webhookOptions .generateHostPort ()
366400 if err != nil {
@@ -371,10 +405,19 @@ func modifyConversionWebhooks(crds []client.Object, webhookOptions WebhookInstal
371405 for _ , crd := range crds {
372406 switch c := crd .(type ) {
373407 case * apiextensionsv1beta1.CustomResourceDefinition :
408+ // Continue if we're preserving unknown fields.
409+ //
374410 // preserveUnknownFields defaults to true if `nil` in v1beta1.
375411 if c .Spec .PreserveUnknownFields == nil || * c .Spec .PreserveUnknownFields {
376412 continue
377413 }
414+ // Continue if the GroupKind isn't registered as being convertible.
415+ if _ , ok := convertibles [schema.GroupKind {
416+ Group : c .Spec .Group ,
417+ Kind : c .Spec .Names .Kind ,
418+ }]; ! ok {
419+ continue
420+ }
378421 c .Spec .Conversion .Strategy = apiextensionsv1beta1 .WebhookConverter
379422 c .Spec .Conversion .WebhookClientConfig .Service = nil
380423 c .Spec .Conversion .WebhookClientConfig = & apiextensionsv1beta1.WebhookClientConfig {
@@ -383,9 +426,17 @@ func modifyConversionWebhooks(crds []client.Object, webhookOptions WebhookInstal
383426 CABundle : webhookOptions .LocalServingCAData ,
384427 }
385428 case * apiextensionsv1.CustomResourceDefinition :
429+ // Continue if we're preserving unknown fields.
386430 if c .Spec .PreserveUnknownFields {
387431 continue
388432 }
433+ // Continue if the GroupKind isn't registered as being convertible.
434+ if _ , ok := convertibles [schema.GroupKind {
435+ Group : c .Spec .Group ,
436+ Kind : c .Spec .Names .Kind ,
437+ }]; ! ok {
438+ continue
439+ }
389440 c .Spec .Conversion .Strategy = apiextensionsv1 .WebhookConverter
390441 c .Spec .Conversion .Webhook .ClientConfig .Service = nil
391442 c .Spec .Conversion .Webhook .ClientConfig = & apiextensionsv1.WebhookClientConfig {
@@ -401,8 +452,32 @@ func modifyConversionWebhooks(crds []client.Object, webhookOptions WebhookInstal
401452
402453 switch c .GroupVersionKind ().Version {
403454 case "v1beta1" :
455+ // Continue if we're preserving unknown fields.
456+ //
404457 // preserveUnknownFields defaults to true if `nil` in v1beta1.
405- if preserve , found , _ := unstructured .NestedBool (c .Object , "spec" , "preserveUnknownFields" ); preserve || ! found {
458+ if preserve , found , err := unstructured .NestedBool (c .Object , "spec" , "preserveUnknownFields" ); preserve || ! found {
459+ continue
460+ } else if err != nil {
461+ return err
462+ }
463+
464+ // Continue if the GroupKind isn't registered as being convertible.
465+ group , found , err := unstructured .NestedString (c .Object , "spec" , "group" )
466+ if ! found {
467+ continue
468+ } else if err != nil {
469+ return err
470+ }
471+ kind , found , err := unstructured .NestedString (c .Object , "spec" , "names" , "kind" )
472+ if ! found {
473+ continue
474+ } else if err != nil {
475+ return err
476+ }
477+ if _ , ok := convertibles [schema.GroupKind {
478+ Group : group ,
479+ Kind : kind ,
480+ }]; ! ok {
406481 continue
407482 }
408483
@@ -428,7 +503,29 @@ func modifyConversionWebhooks(crds []client.Object, webhookOptions WebhookInstal
428503 return err
429504 }
430505 case "v1" :
431- if preserve , _ , _ := unstructured .NestedBool (c .Object , "spec" , "preserveUnknownFields" ); preserve {
506+ if preserve , _ , err := unstructured .NestedBool (c .Object , "spec" , "preserveUnknownFields" ); preserve {
507+ continue
508+ } else if err != nil {
509+ return err
510+ }
511+
512+ // Continue if the GroupKind isn't registered as being convertible.
513+ group , found , err := unstructured .NestedString (c .Object , "spec" , "group" )
514+ if ! found {
515+ continue
516+ } else if err != nil {
517+ return err
518+ }
519+ kind , found , err := unstructured .NestedString (c .Object , "spec" , "names" , "kind" )
520+ if ! found {
521+ continue
522+ } else if err != nil {
523+ return err
524+ }
525+ if _ , ok := convertibles [schema.GroupKind {
526+ Group : group ,
527+ Kind : kind ,
528+ }]; ! ok {
432529 continue
433530 }
434531
0 commit comments