knative.dev/pkg@v0.0.0-20260602142205-ac97e43f6622/webhook/resourcesemantics/conversion/reconciler.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  	"fmt"
    22  
    23  	"go.uber.org/zap"
    24  	apixv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
    25  	apixclient "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
    26  	apixlisters "k8s.io/apiextensions-apiserver/pkg/client/listers/apiextensions/v1"
    27  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    28  	"k8s.io/apimachinery/pkg/runtime/schema"
    29  	"k8s.io/apimachinery/pkg/types"
    30  	corelisters "k8s.io/client-go/listers/core/v1"
    31  	"knative.dev/pkg/controller"
    32  	"knative.dev/pkg/kmp"
    33  	"knative.dev/pkg/logging"
    34  	"knative.dev/pkg/ptr"
    35  	pkgreconciler "knative.dev/pkg/reconciler"
    36  	"knative.dev/pkg/system"
    37  	"knative.dev/pkg/webhook"
    38  	certresources "knative.dev/pkg/webhook/certificates/resources"
    39  )
    40  
    41  type reconciler struct {
    42  	pkgreconciler.LeaderAwareFuncs
    43  
    44  	kinds       map[schema.GroupKind]GroupKindConversion
    45  	path        string
    46  	secretName  string
    47  	withContext func(context.Context) context.Context
    48  
    49  	secretLister corelisters.SecretLister
    50  	crdLister    apixlisters.CustomResourceDefinitionLister
    51  	client       apixclient.Interface
    52  }
    53  
    54  var (
    55  	_ webhook.ConversionController = (*reconciler)(nil)
    56  	_ controller.Reconciler        = (*reconciler)(nil)
    57  	_ pkgreconciler.LeaderAware    = (*reconciler)(nil)
    58  )
    59  
    60  // Path implements webhook.ConversionController
    61  func (r *reconciler) Path() string {
    62  	return r.path
    63  }
    64  
    65  // Reconciler implements controller.Reconciler
    66  func (r *reconciler) Reconcile(ctx context.Context, key string) error {
    67  	logger := logging.FromContext(ctx)
    68  
    69  	if !r.IsLeaderFor(types.NamespacedName{Name: key}) {
    70  		return controller.NewSkipKey(key)
    71  	}
    72  
    73  	// Look up the webhook secret, and fetch the CA cert bundle.
    74  	secret, err := r.secretLister.Secrets(system.Namespace()).Get(r.secretName)
    75  	if err != nil {
    76  		logger.Errorw("Error fetching secret", zap.Error(err))
    77  		return err
    78  	}
    79  
    80  	cacert, ok := secret.Data[certresources.CACert]
    81  	if !ok {
    82  		return fmt.Errorf("secret %q is missing %q key", r.secretName, certresources.CACert)
    83  	}
    84  
    85  	return r.reconcileCRD(ctx, cacert, key)
    86  }
    87  
    88  func (r *reconciler) reconcileCRD(ctx context.Context, cacert []byte, key string) error {
    89  	logger := logging.FromContext(ctx)
    90  
    91  	configuredCRD, err := r.crdLister.Get(key)
    92  	if err != nil {
    93  		return fmt.Errorf("error retrieving crd: %w", err)
    94  	}
    95  
    96  	crd := configuredCRD.DeepCopy()
    97  
    98  	if crd.Spec.Conversion == nil ||
    99  		crd.Spec.Conversion.Strategy != apixv1.WebhookConverter ||
   100  		crd.Spec.Conversion.Webhook.ClientConfig == nil ||
   101  		crd.Spec.Conversion.Webhook.ClientConfig.Service == nil {
   102  		return fmt.Errorf("custom resource %q isn't configured for webhook conversion", key)
   103  	}
   104  
   105  	crd.Spec.Conversion.Webhook.ClientConfig.CABundle = cacert
   106  	crd.Spec.Conversion.Webhook.ClientConfig.Service.Path = ptr.String(r.path)
   107  
   108  	if ok, err := kmp.SafeEqual(configuredCRD, crd); err != nil {
   109  		return fmt.Errorf("error diffing custom resource definitions: %w", err)
   110  	} else if !ok {
   111  		logger.Infof("updating CRD")
   112  		crdClient := r.client.ApiextensionsV1().CustomResourceDefinitions()
   113  		if _, err := crdClient.Update(ctx, crd, metav1.UpdateOptions{}); err != nil {
   114  			return fmt.Errorf("failed to update webhook: %w", err)
   115  		}
   116  	} else {
   117  		logger.Info("CRD is up to date")
   118  	}
   119  
   120  	return nil
   121  }