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  }