...

Source file src/k8s.io/kubernetes/pkg/controlplane/controller/apiserverleasegc/gc_controller.go

Documentation: k8s.io/kubernetes/pkg/controlplane/controller/apiserverleasegc

     1  /*
     2  Copyright 2020 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 apiserverleasegc
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"time"
    23  
    24  	v1 "k8s.io/api/coordination/v1"
    25  	"k8s.io/apimachinery/pkg/api/errors"
    26  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    27  	"k8s.io/apimachinery/pkg/labels"
    28  	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
    29  	"k8s.io/apimachinery/pkg/util/wait"
    30  	informers "k8s.io/client-go/informers/coordination/v1"
    31  	"k8s.io/client-go/kubernetes"
    32  	listers "k8s.io/client-go/listers/coordination/v1"
    33  	"k8s.io/client-go/tools/cache"
    34  
    35  	"k8s.io/klog/v2"
    36  )
    37  
    38  // Controller deletes expired apiserver leases.
    39  type Controller struct {
    40  	kubeclientset kubernetes.Interface
    41  
    42  	leaseLister   listers.LeaseLister
    43  	leaseInformer cache.SharedIndexInformer
    44  	leasesSynced  cache.InformerSynced
    45  
    46  	leaseNamespace string
    47  
    48  	gcCheckPeriod time.Duration
    49  }
    50  
    51  // NewAPIServerLeaseGC creates a new Controller.
    52  func NewAPIServerLeaseGC(clientset kubernetes.Interface, gcCheckPeriod time.Duration, leaseNamespace, leaseLabelSelector string) *Controller {
    53  	// we construct our own informer because we need such a small subset of the information available.
    54  	// Just one namespace with label selection.
    55  	leaseInformer := informers.NewFilteredLeaseInformer(
    56  		clientset,
    57  		leaseNamespace,
    58  		0,
    59  		cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc},
    60  		func(listOptions *metav1.ListOptions) {
    61  			listOptions.LabelSelector = leaseLabelSelector
    62  		})
    63  	return &Controller{
    64  		kubeclientset:  clientset,
    65  		leaseLister:    listers.NewLeaseLister(leaseInformer.GetIndexer()),
    66  		leaseInformer:  leaseInformer,
    67  		leasesSynced:   leaseInformer.HasSynced,
    68  		leaseNamespace: leaseNamespace,
    69  		gcCheckPeriod:  gcCheckPeriod,
    70  	}
    71  }
    72  
    73  // Run starts one worker.
    74  func (c *Controller) Run(stopCh <-chan struct{}) {
    75  	defer utilruntime.HandleCrash()
    76  	defer klog.Infof("Shutting down apiserver lease garbage collector")
    77  
    78  	klog.Infof("Starting apiserver lease garbage collector")
    79  
    80  	// we have a personal informer that is narrowly scoped, start it.
    81  	go c.leaseInformer.Run(stopCh)
    82  
    83  	if !cache.WaitForCacheSync(stopCh, c.leasesSynced) {
    84  		utilruntime.HandleError(fmt.Errorf("timed out waiting for caches to sync"))
    85  		return
    86  	}
    87  
    88  	go wait.Until(c.gc, c.gcCheckPeriod, stopCh)
    89  
    90  	<-stopCh
    91  }
    92  
    93  func (c *Controller) gc() {
    94  	leases, err := c.leaseLister.Leases(c.leaseNamespace).List(labels.Everything())
    95  	if err != nil {
    96  		klog.ErrorS(err, "Error while listing apiserver leases")
    97  		return
    98  	}
    99  	for _, lease := range leases {
   100  		// evaluate lease from cache
   101  		if !isLeaseExpired(lease) {
   102  			continue
   103  		}
   104  		// double check latest lease from apiserver before deleting
   105  		lease, err := c.kubeclientset.CoordinationV1().Leases(c.leaseNamespace).Get(context.TODO(), lease.Name, metav1.GetOptions{})
   106  		if err != nil && !errors.IsNotFound(err) {
   107  			klog.ErrorS(err, "Error getting lease")
   108  			continue
   109  		}
   110  		if errors.IsNotFound(err) || lease == nil {
   111  			// In an HA cluster, this can happen if the lease was deleted
   112  			// by the same GC controller in another apiserver, which is legit.
   113  			// We don't expect other components to delete the lease.
   114  			klog.V(4).InfoS("Cannot find apiserver lease", "err", err)
   115  			continue
   116  		}
   117  		// evaluate lease from apiserver
   118  		if !isLeaseExpired(lease) {
   119  			continue
   120  		}
   121  		if err := c.kubeclientset.CoordinationV1().Leases(c.leaseNamespace).Delete(
   122  			context.TODO(), lease.Name, metav1.DeleteOptions{}); err != nil {
   123  			if errors.IsNotFound(err) {
   124  				// In an HA cluster, this can happen if the lease was deleted
   125  				// by the same GC controller in another apiserver, which is legit.
   126  				// We don't expect other components to delete the lease.
   127  				klog.V(4).InfoS("Apiserver lease is gone already", "err", err)
   128  			} else {
   129  				klog.ErrorS(err, "Error deleting lease")
   130  			}
   131  		}
   132  	}
   133  }
   134  
   135  func isLeaseExpired(lease *v1.Lease) bool {
   136  	currentTime := time.Now()
   137  	// Leases created by the apiserver lease controller should have non-nil renew time
   138  	// and lease duration set. Leases without these fields set are invalid and should
   139  	// be GC'ed.
   140  	return lease.Spec.RenewTime == nil ||
   141  		lease.Spec.LeaseDurationSeconds == nil ||
   142  		lease.Spec.RenewTime.Add(time.Duration(*lease.Spec.LeaseDurationSeconds)*time.Second).Before(currentTime)
   143  }
   144  

View as plain text