knative.dev/pkg@v0.0.0-20260602142205-ac97e43f6622/webhook/resourcesemantics/conversion/controller.go (about) 1 /* 2 Copyright 2020 The Knative 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 conversion 18 19 import ( 20 "context" 21 22 "k8s.io/apimachinery/pkg/runtime" 23 "k8s.io/apimachinery/pkg/runtime/schema" 24 "k8s.io/apimachinery/pkg/types" 25 "k8s.io/client-go/tools/cache" 26 "knative.dev/pkg/apis" 27 apixclient "knative.dev/pkg/client/injection/apiextensions/client" 28 crdinformer "knative.dev/pkg/client/injection/apiextensions/informers/apiextensions/v1/customresourcedefinition" 29 "knative.dev/pkg/controller" 30 secretinformer "knative.dev/pkg/injection/clients/namespacedkube/informers/core/v1/secret" 31 "knative.dev/pkg/logging" 32 pkgreconciler "knative.dev/pkg/reconciler" 33 "knative.dev/pkg/system" 34 "knative.dev/pkg/webhook" 35 ) 36 37 // ConvertibleObject defines the functionality our API types 38 // are required to implement in order to be convertible from 39 // one version to another 40 // 41 // Optionally if the object implements apis.Defaultable the 42 // ConversionController will apply defaults before returning 43 // the response 44 type ConvertibleObject interface { 45 // ConvertTo(ctx, to) 46 // ConvertFrom(ctx, from) 47 apis.Convertible 48 49 // DeepCopyObject() 50 // GetObjectKind() => SetGroupVersionKind(gvk) 51 runtime.Object 52 } 53 54 // GroupKindConversion specifies how a specific Kind for a given 55 // group should be converted 56 type GroupKindConversion struct { 57 // DefinitionName specifies the CustomResourceDefinition that should 58 // be reconciled with by the controller. 59 // 60 // The conversion webhook configuration will be updated 61 // when the CA bundle changes 62 DefinitionName string 63 64 // HubVersion specifies which version of the CustomResource supports 65 // conversions to and from all types 66 // 67 // It is expected that the Zygotes map contains an entry for the 68 // specified HubVersion 69 HubVersion string 70 71 // Zygotes contains a map of version strings (ie. v1, v2) to empty 72 // ConvertibleObject objects 73 // 74 // During a conversion request these zygotes will be deep copied 75 // and manipulated using the apis.Convertible interface 76 Zygotes map[string]ConvertibleObject 77 } 78 79 // NewConversionController returns a K8s controller that will 80 // will reconcile CustomResourceDefinitions and update their 81 // conversion webhook attributes such as path & CA bundle. 82 // 83 // Additionally the controller's Reconciler implements 84 // webhook.ConversionController for the purposes of converting 85 // resources between different versions 86 func NewConversionController( 87 ctx context.Context, 88 path string, 89 kinds map[schema.GroupKind]GroupKindConversion, 90 withContext func(context.Context) context.Context, 91 ) *controller.Impl { 92 opts := []OptionFunc{ 93 WithPath(path), 94 WithWrapContext(withContext), 95 WithKinds(kinds), 96 } 97 98 return newController(ctx, opts...) 99 } 100 101 func newController(ctx context.Context, optsFunc ...OptionFunc) *controller.Impl { 102 secretInformer := secretinformer.Get(ctx) 103 crdInformer := crdinformer.Get(ctx) 104 client := apixclient.Get(ctx) 105 woptions := webhook.GetOptions(ctx) 106 107 opts := &options{} 108 109 for _, f := range optsFunc { 110 f(opts) 111 } 112 113 r := &reconciler{ 114 LeaderAwareFuncs: pkgreconciler.LeaderAwareFuncs{ 115 // Have this reconciler enqueue our types whenever it becomes leader. 116 PromoteFunc: func(bkt pkgreconciler.Bucket, enq func(pkgreconciler.Bucket, types.NamespacedName)) error { 117 for _, gkc := range opts.kinds { 118 name := gkc.DefinitionName 119 enq(bkt, types.NamespacedName{Name: name}) 120 } 121 return nil 122 }, 123 }, 124 125 kinds: opts.kinds, 126 path: opts.path, 127 secretName: woptions.SecretName, 128 withContext: opts.wc, 129 130 client: client, 131 secretLister: secretInformer.Lister(), 132 crdLister: crdInformer.Lister(), 133 } 134 135 logger := logging.FromContext(ctx) 136 controllerOptions := woptions.ControllerOptions 137 if controllerOptions == nil { 138 const queueName = "ConversionWebhook" 139 controllerOptions = &controller.ControllerOptions{WorkQueueName: queueName, Logger: logger.Named(queueName)} 140 } 141 c := controller.NewContext(ctx, r, *controllerOptions) 142 143 // Reconciler when the named CRDs change. 144 for _, gkc := range opts.kinds { 145 name := gkc.DefinitionName 146 147 crdInformer.Informer().AddEventHandler(cache.FilteringResourceEventHandler{ 148 FilterFunc: controller.FilterWithName(name), 149 Handler: controller.HandleAll(c.Enqueue), 150 }) 151 152 sentinel := c.EnqueueSentinel(types.NamespacedName{Name: name}) 153 154 // Reconcile when the cert bundle changes. 155 secretInformer.Informer().AddEventHandler(cache.FilteringResourceEventHandler{ 156 FilterFunc: controller.FilterWithNameAndNamespace(system.Namespace(), woptions.SecretName), 157 Handler: controller.HandleAll(sentinel), 158 }) 159 } 160 161 return c 162 }