k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/pkg/controller/volume/pvprotection/pv_protection_controller.go (about) 1 /* 2 Copyright 2018 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 pvprotection 18 19 import ( 20 "context" 21 "fmt" 22 "time" 23 24 v1 "k8s.io/api/core/v1" 25 apierrors "k8s.io/apimachinery/pkg/api/errors" 26 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 27 utilruntime "k8s.io/apimachinery/pkg/util/runtime" 28 "k8s.io/apimachinery/pkg/util/wait" 29 coreinformers "k8s.io/client-go/informers/core/v1" 30 clientset "k8s.io/client-go/kubernetes" 31 corelisters "k8s.io/client-go/listers/core/v1" 32 "k8s.io/client-go/tools/cache" 33 "k8s.io/client-go/util/workqueue" 34 "k8s.io/klog/v2" 35 "k8s.io/kubernetes/pkg/controller/volume/protectionutil" 36 "k8s.io/kubernetes/pkg/util/slice" 37 volumeutil "k8s.io/kubernetes/pkg/volume/util" 38 ) 39 40 // Controller is controller that removes PVProtectionFinalizer 41 // from PVs that are not bound to PVCs. 42 type Controller struct { 43 client clientset.Interface 44 45 pvLister corelisters.PersistentVolumeLister 46 pvListerSynced cache.InformerSynced 47 48 queue workqueue.TypedRateLimitingInterface[string] 49 } 50 51 // NewPVProtectionController returns a new *Controller. 52 func NewPVProtectionController(logger klog.Logger, pvInformer coreinformers.PersistentVolumeInformer, cl clientset.Interface) *Controller { 53 e := &Controller{ 54 client: cl, 55 queue: workqueue.NewTypedRateLimitingQueueWithConfig( 56 workqueue.DefaultTypedControllerRateLimiter[string](), 57 workqueue.TypedRateLimitingQueueConfig[string]{Name: "pvprotection"}, 58 ), 59 } 60 61 e.pvLister = pvInformer.Lister() 62 e.pvListerSynced = pvInformer.Informer().HasSynced 63 pvInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ 64 AddFunc: func(obj interface{}) { 65 e.pvAddedUpdated(logger, obj) 66 }, 67 UpdateFunc: func(old, new interface{}) { 68 e.pvAddedUpdated(logger, new) 69 }, 70 }) 71 72 return e 73 } 74 75 // Run runs the controller goroutines. 76 func (c *Controller) Run(ctx context.Context, workers int) { 77 defer utilruntime.HandleCrash() 78 defer c.queue.ShutDown() 79 80 logger := klog.FromContext(ctx) 81 logger.Info("Starting PV protection controller") 82 defer logger.Info("Shutting down PV protection controller") 83 84 if !cache.WaitForNamedCacheSync("PV protection", ctx.Done(), c.pvListerSynced) { 85 return 86 } 87 88 for i := 0; i < workers; i++ { 89 go wait.UntilWithContext(ctx, c.runWorker, time.Second) 90 } 91 92 <-ctx.Done() 93 } 94 95 func (c *Controller) runWorker(ctx context.Context) { 96 for c.processNextWorkItem(ctx) { 97 } 98 } 99 100 // processNextWorkItem deals with one pvcKey off the queue. It returns false when it's time to quit. 101 func (c *Controller) processNextWorkItem(ctx context.Context) bool { 102 pvKey, quit := c.queue.Get() 103 if quit { 104 return false 105 } 106 defer c.queue.Done(pvKey) 107 108 pvName := pvKey 109 110 err := c.processPV(ctx, pvName) 111 if err == nil { 112 c.queue.Forget(pvKey) 113 return true 114 } 115 116 utilruntime.HandleError(fmt.Errorf("PV %v failed with : %v", pvKey, err)) 117 c.queue.AddRateLimited(pvKey) 118 119 return true 120 } 121 122 func (c *Controller) processPV(ctx context.Context, pvName string) error { 123 logger := klog.FromContext(ctx) 124 logger.V(4).Info("Processing PV", "PV", klog.KRef("", pvName)) 125 startTime := time.Now() 126 defer func() { 127 logger.V(4).Info("Finished processing PV", "PV", klog.KRef("", pvName), "cost", time.Since(startTime)) 128 }() 129 130 pv, err := c.pvLister.Get(pvName) 131 if apierrors.IsNotFound(err) { 132 logger.V(4).Info("PV not found, ignoring", "PV", klog.KRef("", pvName)) 133 return nil 134 } 135 if err != nil { 136 return err 137 } 138 139 if protectionutil.IsDeletionCandidate(pv, volumeutil.PVProtectionFinalizer) { 140 // PV should be deleted. Check if it's used and remove finalizer if 141 // it's not. 142 isUsed := c.isBeingUsed(pv) 143 if !isUsed { 144 return c.removeFinalizer(ctx, pv) 145 } 146 logger.V(4).Info("Keeping PV because it is being used", "PV", klog.KRef("", pvName)) 147 } 148 149 if protectionutil.NeedToAddFinalizer(pv, volumeutil.PVProtectionFinalizer) { 150 // PV is not being deleted -> it should have the finalizer. The 151 // finalizer should be added by admission plugin, this is just to add 152 // the finalizer to old PVs that were created before the admission 153 // plugin was enabled. 154 return c.addFinalizer(ctx, pv) 155 } 156 return nil 157 } 158 159 func (c *Controller) addFinalizer(ctx context.Context, pv *v1.PersistentVolume) error { 160 pvClone := pv.DeepCopy() 161 pvClone.ObjectMeta.Finalizers = append(pvClone.ObjectMeta.Finalizers, volumeutil.PVProtectionFinalizer) 162 _, err := c.client.CoreV1().PersistentVolumes().Update(ctx, pvClone, metav1.UpdateOptions{}) 163 logger := klog.FromContext(ctx) 164 if err != nil { 165 logger.V(3).Info("Error adding protection finalizer to PV", "PV", klog.KObj(pv), "err", err) 166 return err 167 } 168 logger.V(3).Info("Added protection finalizer to PV", "PV", klog.KObj(pv)) 169 return nil 170 } 171 172 func (c *Controller) removeFinalizer(ctx context.Context, pv *v1.PersistentVolume) error { 173 pvClone := pv.DeepCopy() 174 pvClone.ObjectMeta.Finalizers = slice.RemoveString(pvClone.ObjectMeta.Finalizers, volumeutil.PVProtectionFinalizer, nil) 175 _, err := c.client.CoreV1().PersistentVolumes().Update(ctx, pvClone, metav1.UpdateOptions{}) 176 logger := klog.FromContext(ctx) 177 if err != nil { 178 logger.V(3).Info("Error removing protection finalizer from PV", "PV", klog.KObj(pv), "err", err) 179 return err 180 } 181 logger.V(3).Info("Removed protection finalizer from PV", "PV", klog.KObj(pv)) 182 return nil 183 } 184 185 func (c *Controller) isBeingUsed(pv *v1.PersistentVolume) bool { 186 // check if PV is being bound to a PVC by its status 187 // the status will be updated by PV controller 188 if pv.Status.Phase == v1.VolumeBound { 189 // the PV is being used now 190 return true 191 } 192 193 return false 194 } 195 196 // pvAddedUpdated reacts to pv added/updated events 197 func (c *Controller) pvAddedUpdated(logger klog.Logger, obj interface{}) { 198 pv, ok := obj.(*v1.PersistentVolume) 199 if !ok { 200 utilruntime.HandleError(fmt.Errorf("PV informer returned non-PV object: %#v", obj)) 201 return 202 } 203 logger.V(4).Info("Got event on PV", "PV", klog.KObj(pv)) 204 205 if protectionutil.NeedToAddFinalizer(pv, volumeutil.PVProtectionFinalizer) || protectionutil.IsDeletionCandidate(pv, volumeutil.PVProtectionFinalizer) { 206 c.queue.Add(pv.Name) 207 } 208 }