k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/pkg/controller/certificates/certificate_controller.go (about)

     1  /*
     2  Copyright 2016 The Kubernetes 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 certificates implements an abstract controller that is useful for
    18  // building controllers that manage CSRs
    19  package certificates
    20  
    21  import (
    22  	"context"
    23  	"fmt"
    24  	"time"
    25  
    26  	"golang.org/x/time/rate"
    27  
    28  	certificates "k8s.io/api/certificates/v1"
    29  	"k8s.io/apimachinery/pkg/api/errors"
    30  	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
    31  	"k8s.io/apimachinery/pkg/util/wait"
    32  	certificatesinformers "k8s.io/client-go/informers/certificates/v1"
    33  	clientset "k8s.io/client-go/kubernetes"
    34  	certificateslisters "k8s.io/client-go/listers/certificates/v1"
    35  	"k8s.io/client-go/tools/cache"
    36  	"k8s.io/client-go/util/workqueue"
    37  	"k8s.io/klog/v2"
    38  	"k8s.io/kubernetes/pkg/controller"
    39  )
    40  
    41  type CertificateController struct {
    42  	// name is an identifier for this particular controller instance.
    43  	name string
    44  
    45  	kubeClient clientset.Interface
    46  
    47  	csrLister  certificateslisters.CertificateSigningRequestLister
    48  	csrsSynced cache.InformerSynced
    49  
    50  	handler func(context.Context, *certificates.CertificateSigningRequest) error
    51  
    52  	queue workqueue.TypedRateLimitingInterface[string]
    53  }
    54  
    55  func NewCertificateController(
    56  	ctx context.Context,
    57  	name string,
    58  	kubeClient clientset.Interface,
    59  	csrInformer certificatesinformers.CertificateSigningRequestInformer,
    60  	handler func(context.Context, *certificates.CertificateSigningRequest) error,
    61  ) *CertificateController {
    62  	logger := klog.FromContext(ctx)
    63  	cc := &CertificateController{
    64  		name:       name,
    65  		kubeClient: kubeClient,
    66  		queue: workqueue.NewTypedRateLimitingQueueWithConfig(
    67  			workqueue.NewTypedMaxOfRateLimiter[string](
    68  				workqueue.NewTypedItemExponentialFailureRateLimiter[string](200*time.Millisecond, 1000*time.Second),
    69  				// 10 qps, 100 bucket size.  This is only for retry speed and its only the overall factor (not per item)
    70  				&workqueue.TypedBucketRateLimiter[string]{Limiter: rate.NewLimiter(rate.Limit(10), 100)},
    71  			),
    72  			workqueue.TypedRateLimitingQueueConfig[string]{
    73  				Name: "certificate",
    74  			},
    75  		),
    76  		handler: handler,
    77  	}
    78  
    79  	// Manage the addition/update of certificate requests
    80  	csrInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
    81  		AddFunc: func(obj interface{}) {
    82  			csr := obj.(*certificates.CertificateSigningRequest)
    83  			logger.V(4).Info("Adding certificate request", "csr", csr.Name)
    84  			cc.enqueueCertificateRequest(obj)
    85  		},
    86  		UpdateFunc: func(old, new interface{}) {
    87  			oldCSR := old.(*certificates.CertificateSigningRequest)
    88  			logger.V(4).Info("Updating certificate request", "old", oldCSR.Name)
    89  			cc.enqueueCertificateRequest(new)
    90  		},
    91  		DeleteFunc: func(obj interface{}) {
    92  			csr, ok := obj.(*certificates.CertificateSigningRequest)
    93  			if !ok {
    94  				tombstone, ok := obj.(cache.DeletedFinalStateUnknown)
    95  				if !ok {
    96  					logger.V(2).Info("Couldn't get object from tombstone", "object", obj)
    97  					return
    98  				}
    99  				csr, ok = tombstone.Obj.(*certificates.CertificateSigningRequest)
   100  				if !ok {
   101  					logger.V(2).Info("Tombstone contained object that is not a CSR", "object", obj)
   102  					return
   103  				}
   104  			}
   105  			logger.V(4).Info("Deleting certificate request", "csr", csr.Name)
   106  			cc.enqueueCertificateRequest(obj)
   107  		},
   108  	})
   109  	cc.csrLister = csrInformer.Lister()
   110  	cc.csrsSynced = csrInformer.Informer().HasSynced
   111  	return cc
   112  }
   113  
   114  // Run the main goroutine responsible for watching and syncing jobs.
   115  func (cc *CertificateController) Run(ctx context.Context, workers int) {
   116  	defer utilruntime.HandleCrash()
   117  	defer cc.queue.ShutDown()
   118  
   119  	logger := klog.FromContext(ctx)
   120  	logger.Info("Starting certificate controller", "name", cc.name)
   121  	defer logger.Info("Shutting down certificate controller", "name", cc.name)
   122  
   123  	if !cache.WaitForNamedCacheSync(fmt.Sprintf("certificate-%s", cc.name), ctx.Done(), cc.csrsSynced) {
   124  		return
   125  	}
   126  
   127  	for i := 0; i < workers; i++ {
   128  		go wait.UntilWithContext(ctx, cc.worker, time.Second)
   129  	}
   130  
   131  	<-ctx.Done()
   132  }
   133  
   134  // worker runs a thread that dequeues CSRs, handles them, and marks them done.
   135  func (cc *CertificateController) worker(ctx context.Context) {
   136  	for cc.processNextWorkItem(ctx) {
   137  	}
   138  }
   139  
   140  // processNextWorkItem deals with one key off the queue.  It returns false when it's time to quit.
   141  func (cc *CertificateController) processNextWorkItem(ctx context.Context) bool {
   142  	cKey, quit := cc.queue.Get()
   143  	if quit {
   144  		return false
   145  	}
   146  	defer cc.queue.Done(cKey)
   147  
   148  	if err := cc.syncFunc(ctx, cKey); err != nil {
   149  		cc.queue.AddRateLimited(cKey)
   150  		if _, ignorable := err.(ignorableError); !ignorable {
   151  			utilruntime.HandleError(fmt.Errorf("Sync %v failed with : %v", cKey, err))
   152  		} else {
   153  			klog.FromContext(ctx).V(4).Info("Sync certificate request failed", "csr", cKey, "err", err)
   154  		}
   155  		return true
   156  	}
   157  
   158  	cc.queue.Forget(cKey)
   159  	return true
   160  
   161  }
   162  
   163  func (cc *CertificateController) enqueueCertificateRequest(obj interface{}) {
   164  	key, err := controller.KeyFunc(obj)
   165  	if err != nil {
   166  		utilruntime.HandleError(fmt.Errorf("Couldn't get key for object %+v: %v", obj, err))
   167  		return
   168  	}
   169  	cc.queue.Add(key)
   170  }
   171  
   172  func (cc *CertificateController) syncFunc(ctx context.Context, key string) error {
   173  	logger := klog.FromContext(ctx)
   174  	startTime := time.Now()
   175  	defer func() {
   176  		logger.V(4).Info("Finished syncing certificate request", "csr", key, "elapsedTime", time.Since(startTime))
   177  	}()
   178  	csr, err := cc.csrLister.Get(key)
   179  	if errors.IsNotFound(err) {
   180  		logger.V(3).Info("csr has been deleted", "csr", key)
   181  		return nil
   182  	}
   183  	if err != nil {
   184  		return err
   185  	}
   186  
   187  	if len(csr.Status.Certificate) > 0 {
   188  		// no need to do anything because it already has a cert
   189  		return nil
   190  	}
   191  
   192  	// need to operate on a copy so we don't mutate the csr in the shared cache
   193  	csr = csr.DeepCopy()
   194  	return cc.handler(ctx, csr)
   195  }
   196  
   197  // IgnorableError returns an error that we shouldn't handle (i.e. log) because
   198  // it's spammy and usually user error. Instead we will log these errors at a
   199  // higher log level. We still need to throw these errors to signal that the
   200  // sync should be retried.
   201  func IgnorableError(s string, args ...interface{}) ignorableError {
   202  	return ignorableError(fmt.Sprintf(s, args...))
   203  }
   204  
   205  type ignorableError string
   206  
   207  func (e ignorableError) Error() string {
   208  	return string(e)
   209  }