k8s.io/kubernetes@v1.29.3/pkg/controller/volume/persistentvolume/pv_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 persistentvolume 18 19 import ( 20 "context" 21 "fmt" 22 "reflect" 23 "strings" 24 "time" 25 26 utilfeature "k8s.io/apiserver/pkg/util/feature" 27 "k8s.io/kubernetes/pkg/features" 28 "k8s.io/kubernetes/pkg/util/slice" 29 30 v1 "k8s.io/api/core/v1" 31 storage "k8s.io/api/storage/v1" 32 apierrors "k8s.io/apimachinery/pkg/api/errors" 33 "k8s.io/apimachinery/pkg/api/resource" 34 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 35 "k8s.io/apimachinery/pkg/util/sets" 36 clientset "k8s.io/client-go/kubernetes" 37 "k8s.io/client-go/kubernetes/scheme" 38 corelisters "k8s.io/client-go/listers/core/v1" 39 storagelisters "k8s.io/client-go/listers/storage/v1" 40 "k8s.io/client-go/tools/cache" 41 "k8s.io/client-go/tools/record" 42 ref "k8s.io/client-go/tools/reference" 43 "k8s.io/client-go/util/workqueue" 44 cloudprovider "k8s.io/cloud-provider" 45 volerr "k8s.io/cloud-provider/volume/errors" 46 storagehelpers "k8s.io/component-helpers/storage/volume" 47 "k8s.io/kubernetes/pkg/controller/volume/common" 48 "k8s.io/kubernetes/pkg/controller/volume/events" 49 "k8s.io/kubernetes/pkg/controller/volume/persistentvolume/metrics" 50 "k8s.io/kubernetes/pkg/util/goroutinemap" 51 "k8s.io/kubernetes/pkg/util/goroutinemap/exponentialbackoff" 52 vol "k8s.io/kubernetes/pkg/volume" 53 "k8s.io/kubernetes/pkg/volume/util" 54 "k8s.io/kubernetes/pkg/volume/util/recyclerclient" 55 volumetypes "k8s.io/kubernetes/pkg/volume/util/types" 56 57 "k8s.io/klog/v2" 58 ) 59 60 // ================================================================== 61 // PLEASE DO NOT ATTEMPT TO SIMPLIFY THIS CODE. 62 // KEEP THE SPACE SHUTTLE FLYING. 63 // ================================================================== 64 // 65 // This controller is intentionally written in a very verbose style. You will 66 // notice: 67 // 68 // 1. Every 'if' statement has a matching 'else' (exception: simple error 69 // checks for a client API call) 70 // 2. Things that may seem obvious are commented explicitly 71 // 72 // We call this style 'space shuttle style'. Space shuttle style is meant to 73 // ensure that every branch and condition is considered and accounted for - 74 // the same way code is written at NASA for applications like the space 75 // shuttle. 76 // 77 // Originally, the work of this controller was split amongst three 78 // controllers. This controller is the result a large effort to simplify the 79 // PV subsystem. During that effort, it became clear that we needed to ensure 80 // that every single condition was handled and accounted for in the code, even 81 // if it resulted in no-op code branches. 82 // 83 // As a result, the controller code may seem overly verbose, commented, and 84 // 'branchy'. However, a large amount of business knowledge and context is 85 // recorded here in order to ensure that future maintainers can correctly 86 // reason through the complexities of the binding behavior. For that reason, 87 // changes to this file should preserve and add to the space shuttle style. 88 // 89 // ================================================================== 90 // PLEASE DO NOT ATTEMPT TO SIMPLIFY THIS CODE. 91 // KEEP THE SPACE SHUTTLE FLYING. 92 // ================================================================== 93 94 // Design: 95 // 96 // The fundamental key to this design is the bi-directional "pointer" between 97 // PersistentVolumes (PVs) and PersistentVolumeClaims (PVCs), which is 98 // represented here as pvc.Spec.VolumeName and pv.Spec.ClaimRef. The bi- 99 // directionality is complicated to manage in a transactionless system, but 100 // without it we can't ensure sane behavior in the face of different forms of 101 // trouble. For example, a rogue HA controller instance could end up racing 102 // and making multiple bindings that are indistinguishable, resulting in 103 // potential data loss. 104 // 105 // This controller is designed to work in active-passive high availability 106 // mode. It *could* work also in active-active HA mode, all the object 107 // transitions are designed to cope with this, however performance could be 108 // lower as these two active controllers will step on each other toes 109 // frequently. 110 // 111 // This controller supports pre-bound (by the creator) objects in both 112 // directions: a PVC that wants a specific PV or a PV that is reserved for a 113 // specific PVC. 114 // 115 // The binding is two-step process. PV.Spec.ClaimRef is modified first and 116 // PVC.Spec.VolumeName second. At any point of this transaction, the PV or PVC 117 // can be modified by user or other controller or completely deleted. Also, 118 // two (or more) controllers may try to bind different volumes to different 119 // claims at the same time. The controller must recover from any conflicts 120 // that may arise from these conditions. 121 122 // CloudVolumeCreatedForClaimNamespaceTag is a name of a tag attached to a real volume in cloud (e.g. AWS EBS or GCE PD) 123 // with namespace of a persistent volume claim used to create this volume. 124 const CloudVolumeCreatedForClaimNamespaceTag = "kubernetes.io/created-for/pvc/namespace" 125 126 // CloudVolumeCreatedForClaimNameTag is a name of a tag attached to a real volume in cloud (e.g. AWS EBS or GCE PD) 127 // with name of a persistent volume claim used to create this volume. 128 const CloudVolumeCreatedForClaimNameTag = "kubernetes.io/created-for/pvc/name" 129 130 // CloudVolumeCreatedForVolumeNameTag is a name of a tag attached to a real volume in cloud (e.g. AWS EBS or GCE PD) 131 // with name of appropriate Kubernetes persistent volume . 132 const CloudVolumeCreatedForVolumeNameTag = "kubernetes.io/created-for/pv/name" 133 134 // Number of retries when we create a PV object for a provisioned volume. 135 const createProvisionedPVRetryCount = 5 136 137 // Interval between retries when we create a PV object for a provisioned volume. 138 const createProvisionedPVInterval = 10 * time.Second 139 140 // CSINameTranslator can get the CSI Driver name based on the in-tree plugin name 141 type CSINameTranslator interface { 142 GetCSINameFromInTreeName(pluginName string) (string, error) 143 } 144 145 // CSIMigratedPluginManager keeps track of CSI migration status of a plugin 146 type CSIMigratedPluginManager interface { 147 IsMigrationEnabledForPlugin(pluginName string) bool 148 } 149 150 // PersistentVolumeController is a controller that synchronizes 151 // PersistentVolumeClaims and PersistentVolumes. It starts two 152 // cache.Controllers that watch PersistentVolume and PersistentVolumeClaim 153 // changes. 154 type PersistentVolumeController struct { 155 volumeLister corelisters.PersistentVolumeLister 156 volumeListerSynced cache.InformerSynced 157 claimLister corelisters.PersistentVolumeClaimLister 158 claimListerSynced cache.InformerSynced 159 classLister storagelisters.StorageClassLister 160 classListerSynced cache.InformerSynced 161 podLister corelisters.PodLister 162 podListerSynced cache.InformerSynced 163 podIndexer cache.Indexer 164 NodeLister corelisters.NodeLister 165 NodeListerSynced cache.InformerSynced 166 167 kubeClient clientset.Interface 168 eventBroadcaster record.EventBroadcaster 169 eventRecorder record.EventRecorder 170 cloud cloudprovider.Interface 171 volumePluginMgr vol.VolumePluginMgr 172 enableDynamicProvisioning bool 173 clusterName string 174 resyncPeriod time.Duration 175 176 // Cache of the last known version of volumes and claims. This cache is 177 // thread safe as long as the volumes/claims there are not modified, they 178 // must be cloned before any modification. These caches get updated both by 179 // "xxx added/updated/deleted" events from etcd and by the controller when 180 // it saves newer version to etcd. 181 // Why local cache: binding a volume to a claim generates 4 events, roughly 182 // in this order (depends on goroutine ordering): 183 // - volume.Spec update 184 // - volume.Status update 185 // - claim.Spec update 186 // - claim.Status update 187 // With these caches, the controller can check that it has already saved 188 // volume.Status and claim.Spec+Status and does not need to do anything 189 // when e.g. volume.Spec update event arrives before all the other events. 190 // Without this cache, it would see the old version of volume.Status and 191 // claim in the informers (it has not been updated from API server events 192 // yet) and it would try to fix these objects to be bound together. 193 // Any write to API server would fail with version conflict - these objects 194 // have been already written. 195 volumes persistentVolumeOrderedIndex 196 claims cache.Store 197 198 // Work queues of claims and volumes to process. Every queue should have 199 // exactly one worker thread, especially syncClaim() is not reentrant. 200 // Two syncClaims could bind two different claims to the same volume or one 201 // claim to two volumes. The controller would recover from this (due to 202 // version errors in API server and other checks in this controller), 203 // however overall speed of multi-worker controller would be lower than if 204 // it runs single thread only. 205 claimQueue *workqueue.Type 206 volumeQueue *workqueue.Type 207 208 // Map of scheduled/running operations. 209 runningOperations goroutinemap.GoRoutineMap 210 211 // For testing only: hook to call before an asynchronous operation starts. 212 // Not used when set to nil. 213 preOperationHook func(operationName string) 214 215 createProvisionedPVRetryCount int 216 createProvisionedPVInterval time.Duration 217 218 // operationTimestamps caches start timestamp of operations 219 // (currently provision + binding/deletion) for metric recording. 220 // Detailed lifecycle/key for each operation 221 // 1. provision + binding 222 // key: claimKey 223 // start time: user has NOT provide any volume ref in the claim AND 224 // there is no existing volume found for the claim, 225 // "provisionClaim" is called with a valid plugin/external provisioner 226 // to provision a volume 227 // end time: after a volume has been provisioned and bound to the claim successfully 228 // the corresponding timestamp entry will be deleted from cache 229 // abort: claim has not been bound to a volume yet but a claim deleted event 230 // has been received from API server 231 // 2. deletion 232 // key: volumeName 233 // start time: when "reclaimVolume" process a volume with reclaim policy 234 // set to be "PersistentVolumeReclaimDelete" 235 // end time: after a volume deleted event has been received from API server 236 // the corresponding timestamp entry will be deleted from cache 237 // abort: N.A. 238 operationTimestamps metrics.OperationStartTimeCache 239 240 translator CSINameTranslator 241 csiMigratedPluginManager CSIMigratedPluginManager 242 } 243 244 // syncClaim is the main controller method to decide what to do with a claim. 245 // It's invoked by appropriate cache.Controller callbacks when a claim is 246 // created, updated or periodically synced. We do not differentiate between 247 // these events. 248 // For easier readability, it was split into syncUnboundClaim and syncBoundClaim 249 // methods. 250 func (ctrl *PersistentVolumeController) syncClaim(ctx context.Context, claim *v1.PersistentVolumeClaim) error { 251 logger := klog.FromContext(ctx) 252 logger.V(4).Info("Synchronizing PersistentVolumeClaim", "PVC", klog.KObj(claim), "claimStatus", getClaimStatusForLogging(claim)) 253 254 // Set correct "migrated-to" annotations on PVC and update in API server if 255 // necessary 256 newClaim, err := ctrl.updateClaimMigrationAnnotations(ctx, claim) 257 if err != nil { 258 // Nothing was saved; we will fall back into the same 259 // condition in the next call to this method 260 return err 261 } 262 claim = newClaim 263 264 if !metav1.HasAnnotation(claim.ObjectMeta, storagehelpers.AnnBindCompleted) { 265 return ctrl.syncUnboundClaim(ctx, claim) 266 } else { 267 return ctrl.syncBoundClaim(ctx, claim) 268 } 269 } 270 271 // checkVolumeSatisfyClaim checks if the volume requested by the claim satisfies the requirements of the claim 272 func checkVolumeSatisfyClaim(volume *v1.PersistentVolume, claim *v1.PersistentVolumeClaim) error { 273 requestedQty := claim.Spec.Resources.Requests[v1.ResourceName(v1.ResourceStorage)] 274 requestedSize := requestedQty.Value() 275 276 // check if PV's DeletionTimeStamp is set, if so, return error. 277 if volume.ObjectMeta.DeletionTimestamp != nil { 278 return fmt.Errorf("the volume is marked for deletion %q", volume.Name) 279 } 280 281 volumeQty := volume.Spec.Capacity[v1.ResourceStorage] 282 volumeSize := volumeQty.Value() 283 if volumeSize < requestedSize { 284 return fmt.Errorf("requested PV is too small") 285 } 286 287 requestedClass := storagehelpers.GetPersistentVolumeClaimClass(claim) 288 if storagehelpers.GetPersistentVolumeClass(volume) != requestedClass { 289 return fmt.Errorf("storageClassName does not match") 290 } 291 292 if storagehelpers.CheckVolumeModeMismatches(&claim.Spec, &volume.Spec) { 293 return fmt.Errorf("incompatible volumeMode") 294 } 295 296 if !storagehelpers.CheckAccessModes(claim, volume) { 297 return fmt.Errorf("incompatible accessMode") 298 } 299 300 return nil 301 } 302 303 // emitEventForUnboundDelayBindingClaim generates informative event for claim 304 // if it's in delay binding mode and not bound yet. 305 func (ctrl *PersistentVolumeController) emitEventForUnboundDelayBindingClaim(claim *v1.PersistentVolumeClaim) error { 306 reason := events.WaitForFirstConsumer 307 message := "waiting for first consumer to be created before binding" 308 podNames, err := ctrl.findNonScheduledPodsByPVC(claim) 309 if err != nil { 310 return err 311 } 312 if len(podNames) > 0 { 313 reason = events.WaitForPodScheduled 314 if len(podNames) > 1 { 315 // Although only one pod is taken into account in 316 // volume scheduling, more than one pods can reference 317 // the PVC at the same time. We can't know which pod is 318 // used in scheduling, all pods are included. 319 message = fmt.Sprintf("waiting for pods %s to be scheduled", strings.Join(podNames, ",")) 320 } else { 321 message = fmt.Sprintf("waiting for pod %s to be scheduled", podNames[0]) 322 } 323 } 324 ctrl.eventRecorder.Event(claim, v1.EventTypeNormal, reason, message) 325 return nil 326 } 327 328 // syncUnboundClaim is the main controller method to decide what to do with an 329 // unbound claim. 330 func (ctrl *PersistentVolumeController) syncUnboundClaim(ctx context.Context, claim *v1.PersistentVolumeClaim) error { 331 // This is a new PVC that has not completed binding 332 // OBSERVATION: pvc is "Pending" 333 logger := klog.FromContext(ctx) 334 if claim.Spec.VolumeName == "" { 335 // User did not care which PV they get. 336 delayBinding, err := storagehelpers.IsDelayBindingMode(claim, ctrl.classLister) 337 if err != nil { 338 return err 339 } 340 341 // [Unit test set 1] 342 volume, err := ctrl.volumes.findBestMatchForClaim(claim, delayBinding) 343 if err != nil { 344 logger.V(2).Info("Synchronizing unbound PersistentVolumeClaim, Error finding PV for claim", "PVC", klog.KObj(claim), "err", err) 345 return fmt.Errorf("error finding PV for claim %q: %w", claimToClaimKey(claim), err) 346 } 347 if volume == nil { 348 logger.V(4).Info("Synchronizing unbound PersistentVolumeClaim, no volume found", "PVC", klog.KObj(claim)) 349 // No PV could be found 350 // OBSERVATION: pvc is "Pending", will retry 351 352 logger.V(4).Info("Attempting to assign storage class to unbound PersistentVolumeClaim", "PVC", klog.KObj(claim)) 353 updated, err := ctrl.assignDefaultStorageClass(ctx, claim) 354 if err != nil { 355 metrics.RecordRetroactiveStorageClassMetric(false) 356 return fmt.Errorf("can't update PersistentVolumeClaim[%q]: %w", claimToClaimKey(claim), err) 357 } 358 if updated { 359 logger.V(4).Info("PersistentVolumeClaim update successful, restarting claim sync", "PVC", klog.KObj(claim)) 360 metrics.RecordRetroactiveStorageClassMetric(true) 361 return nil 362 } 363 364 switch { 365 case delayBinding && !storagehelpers.IsDelayBindingProvisioning(claim): 366 if err = ctrl.emitEventForUnboundDelayBindingClaim(claim); err != nil { 367 return err 368 } 369 case storagehelpers.GetPersistentVolumeClaimClass(claim) != "": 370 if err = ctrl.provisionClaim(ctx, claim); err != nil { 371 return err 372 } 373 return nil 374 default: 375 ctrl.eventRecorder.Event(claim, v1.EventTypeNormal, events.FailedBinding, "no persistent volumes available for this claim and no storage class is set") 376 } 377 378 // Mark the claim as Pending and try to find a match in the next 379 // periodic syncClaim 380 if _, err = ctrl.updateClaimStatus(ctx, claim, v1.ClaimPending, nil); err != nil { 381 return err 382 } 383 return nil 384 } else /* pv != nil */ { 385 // Found a PV for this claim 386 // OBSERVATION: pvc is "Pending", pv is "Available" 387 claimKey := claimToClaimKey(claim) 388 logger.V(4).Info("Synchronizing unbound PersistentVolumeClaim, volume found", "PVC", klog.KObj(claim), "volumeName", volume.Name, "volumeStatus", getVolumeStatusForLogging(volume)) 389 if err = ctrl.bind(ctx, volume, claim); err != nil { 390 // On any error saving the volume or the claim, subsequent 391 // syncClaim will finish the binding. 392 // record count error for provision if exists 393 // timestamp entry will remain in cache until a success binding has happened 394 metrics.RecordMetric(claimKey, &ctrl.operationTimestamps, err) 395 return err 396 } 397 // OBSERVATION: claim is "Bound", pv is "Bound" 398 // if exists a timestamp entry in cache, record end to end provision latency and clean up cache 399 // End of the provision + binding operation lifecycle, cache will be cleaned by "RecordMetric" 400 // [Unit test 12-1, 12-2, 12-4] 401 metrics.RecordMetric(claimKey, &ctrl.operationTimestamps, nil) 402 return nil 403 } 404 } else /* pvc.Spec.VolumeName != nil */ { 405 // [Unit test set 2] 406 // User asked for a specific PV. 407 logger.V(4).Info("Synchronizing unbound PersistentVolumeClaim, volume requested", "PVC", klog.KObj(claim), "volumeName", claim.Spec.VolumeName) 408 obj, found, err := ctrl.volumes.store.GetByKey(claim.Spec.VolumeName) 409 if err != nil { 410 return err 411 } 412 if !found { 413 // User asked for a PV that does not exist. 414 // OBSERVATION: pvc is "Pending" 415 // Retry later. 416 logger.V(4).Info("Synchronizing unbound PersistentVolumeClaim, volume requested and not found, will try again next time", "PVC", klog.KObj(claim), "volumeName", claim.Spec.VolumeName) 417 if _, err = ctrl.updateClaimStatus(ctx, claim, v1.ClaimPending, nil); err != nil { 418 return err 419 } 420 return nil 421 } else { 422 volume, ok := obj.(*v1.PersistentVolume) 423 if !ok { 424 return fmt.Errorf("cannot convert object from volume cache to volume %q!?: %+v", claim.Spec.VolumeName, obj) 425 } 426 logger.V(4).Info("Synchronizing unbound PersistentVolumeClaim, volume requested and found", "PVC", klog.KObj(claim), "volumeName", claim.Spec.VolumeName, "volumeStatus", getVolumeStatusForLogging(volume)) 427 if volume.Spec.ClaimRef == nil { 428 // User asked for a PV that is not claimed 429 // OBSERVATION: pvc is "Pending", pv is "Available" 430 logger.V(4).Info("Synchronizing unbound PersistentVolumeClaim, volume is unbound, binding", "PVC", klog.KObj(claim)) 431 if err = checkVolumeSatisfyClaim(volume, claim); err != nil { 432 logger.V(4).Info("Can't bind the claim to volume", "volumeName", volume.Name, "err", err) 433 // send an event 434 msg := fmt.Sprintf("Cannot bind to requested volume %q: %s", volume.Name, err) 435 ctrl.eventRecorder.Event(claim, v1.EventTypeWarning, events.VolumeMismatch, msg) 436 // volume does not satisfy the requirements of the claim 437 if _, err = ctrl.updateClaimStatus(ctx, claim, v1.ClaimPending, nil); err != nil { 438 return err 439 } 440 } else if err = ctrl.bind(ctx, volume, claim); err != nil { 441 // On any error saving the volume or the claim, subsequent 442 // syncClaim will finish the binding. 443 return err 444 } 445 // OBSERVATION: pvc is "Bound", pv is "Bound" 446 return nil 447 } else if storagehelpers.IsVolumeBoundToClaim(volume, claim) { 448 // User asked for a PV that is claimed by this PVC 449 // OBSERVATION: pvc is "Pending", pv is "Bound" 450 logger.V(4).Info("Synchronizing unbound PersistentVolumeClaim, volume already bound, finishing the binding", "PVC", klog.KObj(claim)) 451 452 // Finish the volume binding by adding claim UID. 453 if err = ctrl.bind(ctx, volume, claim); err != nil { 454 return err 455 } 456 // OBSERVATION: pvc is "Bound", pv is "Bound" 457 return nil 458 } else { 459 // User asked for a PV that is claimed by someone else 460 // OBSERVATION: pvc is "Pending", pv is "Bound" 461 if !metav1.HasAnnotation(claim.ObjectMeta, storagehelpers.AnnBoundByController) { 462 logger.V(4).Info("Synchronizing unbound PersistentVolumeClaim, volume already bound to different claim by user, will retry later", "PVC", klog.KObj(claim)) 463 claimMsg := fmt.Sprintf("volume %q already bound to a different claim.", volume.Name) 464 ctrl.eventRecorder.Event(claim, v1.EventTypeWarning, events.FailedBinding, claimMsg) 465 // User asked for a specific PV, retry later 466 if _, err = ctrl.updateClaimStatus(ctx, claim, v1.ClaimPending, nil); err != nil { 467 return err 468 } 469 return nil 470 } else { 471 // This should never happen because someone had to remove 472 // AnnBindCompleted annotation on the claim. 473 logger.V(4).Info("Synchronizing unbound PersistentVolumeClaim, volume already bound to different claim by controller, THIS SHOULD NEVER HAPPEN", "PVC", klog.KObj(claim), "boundClaim", klog.KRef(volume.Spec.ClaimRef.Namespace, volume.Spec.ClaimRef.Name)) 474 claimMsg := fmt.Sprintf("volume %q already bound to a different claim.", volume.Name) 475 ctrl.eventRecorder.Event(claim, v1.EventTypeWarning, events.FailedBinding, claimMsg) 476 477 return fmt.Errorf("invalid binding of claim %q to volume %q: volume already claimed by %q", claimToClaimKey(claim), claim.Spec.VolumeName, claimrefToClaimKey(volume.Spec.ClaimRef)) 478 } 479 } 480 } 481 } 482 } 483 484 // syncBoundClaim is the main controller method to decide what to do with a 485 // bound claim. 486 func (ctrl *PersistentVolumeController) syncBoundClaim(ctx context.Context, claim *v1.PersistentVolumeClaim) error { 487 // HasAnnotation(pvc, storagehelpers.AnnBindCompleted) 488 // This PVC has previously been bound 489 // OBSERVATION: pvc is not "Pending" 490 // [Unit test set 3] 491 492 logger := klog.FromContext(ctx) 493 494 if claim.Spec.VolumeName == "" { 495 // Claim was bound before but not any more. 496 if _, err := ctrl.updateClaimStatusWithEvent(ctx, claim, v1.ClaimLost, nil, v1.EventTypeWarning, "ClaimLost", "Bound claim has lost reference to PersistentVolume. Data on the volume is lost!"); err != nil { 497 return err 498 } 499 return nil 500 } 501 obj, found, err := ctrl.volumes.store.GetByKey(claim.Spec.VolumeName) 502 if err != nil { 503 return err 504 } 505 if !found { 506 // Claim is bound to a non-existing volume. 507 if _, err = ctrl.updateClaimStatusWithEvent(ctx, claim, v1.ClaimLost, nil, v1.EventTypeWarning, "ClaimLost", "Bound claim has lost its PersistentVolume. Data on the volume is lost!"); err != nil { 508 return err 509 } 510 return nil 511 } else { 512 volume, ok := obj.(*v1.PersistentVolume) 513 if !ok { 514 return fmt.Errorf("cannot convert object from volume cache to volume %q!?: %#v", claim.Spec.VolumeName, obj) 515 } 516 517 logger.V(4).Info("Synchronizing bound PersistentVolumeClaim, volume found", "PVC", klog.KObj(claim), "volumeName", claim.Spec.VolumeName, "volumeStatus", getVolumeStatusForLogging(volume)) 518 if volume.Spec.ClaimRef == nil { 519 // Claim is bound but volume has come unbound. 520 // Or, a claim was bound and the controller has not received updated 521 // volume yet. We can't distinguish these cases. 522 // Bind the volume again and set all states to Bound. 523 logger.V(4).Info("Synchronizing bound PersistentVolumeClaim, volume is unbound, fixing", "PVC", klog.KObj(claim)) 524 if err = ctrl.bind(ctx, volume, claim); err != nil { 525 // Objects not saved, next syncPV or syncClaim will try again 526 return err 527 } 528 return nil 529 } else if volume.Spec.ClaimRef.UID == claim.UID { 530 // All is well 531 // NOTE: syncPV can handle this so it can be left out. 532 // NOTE: bind() call here will do nothing in most cases as 533 // everything should be already set. 534 logger.V(4).Info("Synchronizing bound PersistentVolumeClaim, claim is already correctly bound", "PVC", klog.KObj(claim)) 535 if err = ctrl.bind(ctx, volume, claim); err != nil { 536 // Objects not saved, next syncPV or syncClaim will try again 537 return err 538 } 539 return nil 540 } else { 541 // Claim is bound but volume has a different claimant. 542 // Set the claim phase to 'Lost', which is a terminal 543 // phase. 544 if _, err = ctrl.updateClaimStatusWithEvent(ctx, claim, v1.ClaimLost, nil, v1.EventTypeWarning, "ClaimMisbound", "Two claims are bound to the same volume, this one is bound incorrectly"); err != nil { 545 return err 546 } 547 return nil 548 } 549 } 550 } 551 552 // syncVolume is the main controller method to decide what to do with a volume. 553 // It's invoked by appropriate cache.Controller callbacks when a volume is 554 // created, updated or periodically synced. We do not differentiate between 555 // these events. 556 func (ctrl *PersistentVolumeController) syncVolume(ctx context.Context, volume *v1.PersistentVolume) error { 557 logger := klog.FromContext(ctx) 558 logger.V(4).Info("Synchronizing PersistentVolume", "volumeName", volume.Name, "volumeStatus", getVolumeStatusForLogging(volume)) 559 // Set correct "migrated-to" annotations and modify finalizers on PV and update in API server if 560 // necessary 561 newVolume, err := ctrl.updateVolumeMigrationAnnotationsAndFinalizers(ctx, volume) 562 if err != nil { 563 // Nothing was saved; we will fall back into the same 564 // condition in the next call to this method 565 return err 566 } 567 volume = newVolume 568 569 // [Unit test set 4] 570 if volume.Spec.ClaimRef == nil { 571 // Volume is unused 572 logger.V(4).Info("Synchronizing PersistentVolume, volume is unused", "volumeName", volume.Name) 573 if _, err := ctrl.updateVolumePhase(ctx, volume, v1.VolumeAvailable, ""); err != nil { 574 // Nothing was saved; we will fall back into the same 575 // condition in the next call to this method 576 return err 577 } 578 return nil 579 } else /* pv.Spec.ClaimRef != nil */ { 580 // Volume is bound to a claim. 581 if volume.Spec.ClaimRef.UID == "" { 582 // The PV is reserved for a PVC; that PVC has not yet been 583 // bound to this PV; the PVC sync will handle it. 584 logger.V(4).Info("Synchronizing PersistentVolume, volume is pre-bound to claim", "PVC", klog.KRef(volume.Spec.ClaimRef.Namespace, volume.Spec.ClaimRef.Name), "volumeName", volume.Name) 585 if _, err := ctrl.updateVolumePhase(ctx, volume, v1.VolumeAvailable, ""); err != nil { 586 // Nothing was saved; we will fall back into the same 587 // condition in the next call to this method 588 return err 589 } 590 return nil 591 } 592 logger.V(4).Info("Synchronizing PersistentVolume, volume is bound to claim", "PVC", klog.KRef(volume.Spec.ClaimRef.Namespace, volume.Spec.ClaimRef.Name), "volumeName", volume.Name) 593 // Get the PVC by _name_ 594 var claim *v1.PersistentVolumeClaim 595 claimName := claimrefToClaimKey(volume.Spec.ClaimRef) 596 obj, found, err := ctrl.claims.GetByKey(claimName) 597 if err != nil { 598 return err 599 } 600 if !found { 601 // If the PV was created by an external PV provisioner or 602 // bound by external PV binder (e.g. kube-scheduler), it's 603 // possible under heavy load that the corresponding PVC is not synced to 604 // controller local cache yet. So we need to double-check PVC in 605 // 1) informer cache 606 // 2) apiserver if not found in informer cache 607 // to make sure we will not reclaim a PV wrongly. 608 // Note that only non-released and non-failed volumes will be 609 // updated to Released state when PVC does not exist. 610 if volume.Status.Phase != v1.VolumeReleased && volume.Status.Phase != v1.VolumeFailed { 611 obj, err = ctrl.claimLister.PersistentVolumeClaims(volume.Spec.ClaimRef.Namespace).Get(volume.Spec.ClaimRef.Name) 612 if err != nil && !apierrors.IsNotFound(err) { 613 return err 614 } 615 found = !apierrors.IsNotFound(err) 616 if !found { 617 obj, err = ctrl.kubeClient.CoreV1().PersistentVolumeClaims(volume.Spec.ClaimRef.Namespace).Get(ctx, volume.Spec.ClaimRef.Name, metav1.GetOptions{}) 618 if err != nil && !apierrors.IsNotFound(err) { 619 return err 620 } 621 found = !apierrors.IsNotFound(err) 622 } 623 } 624 } 625 if !found { 626 logger.V(4).Info("Synchronizing PersistentVolume, claim not found", "PVC", klog.KRef(volume.Spec.ClaimRef.Namespace, volume.Spec.ClaimRef.Name), "volumeName", volume.Name) 627 // Fall through with claim = nil 628 } else { 629 var ok bool 630 claim, ok = obj.(*v1.PersistentVolumeClaim) 631 if !ok { 632 return fmt.Errorf("cannot convert object from volume cache to volume %q!?: %#v", claim.Spec.VolumeName, obj) 633 } 634 logger.V(4).Info("Synchronizing PersistentVolume, claim found", "PVC", klog.KRef(volume.Spec.ClaimRef.Namespace, volume.Spec.ClaimRef.Name), "claimStatus", getClaimStatusForLogging(claim), "volumeName", volume.Name) 635 } 636 if claim != nil && claim.UID != volume.Spec.ClaimRef.UID { 637 // The claim that the PV was pointing to was deleted, and another 638 // with the same name created. 639 // in some cases, the cached claim is not the newest, and the volume.Spec.ClaimRef.UID is newer than cached. 640 // so we should double check by calling apiserver and get the newest claim, then compare them. 641 logger.V(4).Info("Maybe cached claim is not the newest one, we should fetch it from apiserver", "PVC", klog.KRef(volume.Spec.ClaimRef.Namespace, volume.Spec.ClaimRef.Name)) 642 643 claim, err = ctrl.kubeClient.CoreV1().PersistentVolumeClaims(volume.Spec.ClaimRef.Namespace).Get(ctx, volume.Spec.ClaimRef.Name, metav1.GetOptions{}) 644 if err != nil && !apierrors.IsNotFound(err) { 645 return err 646 } else if claim != nil { 647 // Treat the volume as bound to a missing claim. 648 if claim.UID != volume.Spec.ClaimRef.UID { 649 logger.V(4).Info("Synchronizing PersistentVolume, claim has a newer UID than pv.ClaimRef, the old one must have been deleted", "PVC", klog.KRef(volume.Spec.ClaimRef.Namespace, volume.Spec.ClaimRef.Name), "volumeName", volume.Name) 650 claim = nil 651 } else { 652 logger.V(4).Info("Synchronizing PersistentVolume, claim has a same UID with pv.ClaimRef", "PVC", klog.KRef(volume.Spec.ClaimRef.Namespace, volume.Spec.ClaimRef.Name), "volumeName", volume.Name) 653 } 654 } 655 } 656 657 if claim == nil { 658 // If we get into this block, the claim must have been deleted; 659 // NOTE: reclaimVolume may either release the PV back into the pool or 660 // recycle it or do nothing (retain) 661 662 // Do not overwrite previous Failed state - let the user see that 663 // something went wrong, while we still re-try to reclaim the 664 // volume. 665 if volume.Status.Phase != v1.VolumeReleased && volume.Status.Phase != v1.VolumeFailed { 666 // Also, log this only once: 667 logger.V(2).Info("Volume is released and reclaim policy will be executed", "volumeName", volume.Name, "reclaimPolicy", volume.Spec.PersistentVolumeReclaimPolicy) 668 if volume, err = ctrl.updateVolumePhase(ctx, volume, v1.VolumeReleased, ""); err != nil { 669 // Nothing was saved; we will fall back into the same condition 670 // in the next call to this method 671 return err 672 } 673 } 674 if err = ctrl.reclaimVolume(ctx, volume); err != nil { 675 // Release failed, we will fall back into the same condition 676 // in the next call to this method 677 return err 678 } 679 if volume.Spec.PersistentVolumeReclaimPolicy == v1.PersistentVolumeReclaimRetain { 680 // volume is being retained, it references a claim that does not exist now. 681 logger.V(4).Info("PersistentVolume references a claim that is not found", "PVC", klog.KRef(volume.Spec.ClaimRef.Namespace, volume.Spec.ClaimRef.Name), "claimUID", volume.Spec.ClaimRef.UID, "volumeName", volume.Name) 682 } 683 return nil 684 } else if claim.Spec.VolumeName == "" { 685 if storagehelpers.CheckVolumeModeMismatches(&claim.Spec, &volume.Spec) { 686 // Binding for the volume won't be called in syncUnboundClaim, 687 // because findBestMatchForClaim won't return the volume due to volumeMode mismatch. 688 volumeMsg := fmt.Sprintf("Cannot bind PersistentVolume to requested PersistentVolumeClaim %q due to incompatible volumeMode.", claim.Name) 689 ctrl.eventRecorder.Event(volume, v1.EventTypeWarning, events.VolumeMismatch, volumeMsg) 690 claimMsg := fmt.Sprintf("Cannot bind PersistentVolume %q to requested PersistentVolumeClaim due to incompatible volumeMode.", volume.Name) 691 ctrl.eventRecorder.Event(claim, v1.EventTypeWarning, events.VolumeMismatch, claimMsg) 692 // Skipping syncClaim 693 return nil 694 } 695 696 if metav1.HasAnnotation(volume.ObjectMeta, storagehelpers.AnnBoundByController) { 697 // The binding is not completed; let PVC sync handle it 698 logger.V(4).Info("Synchronizing PersistentVolume, volume not bound yet, waiting for syncClaim to fix it", "volumeName", volume.Name) 699 } else { 700 // Dangling PV; try to re-establish the link in the PVC sync 701 logger.V(4).Info("Synchronizing PersistentVolume, volume was bound and got unbound (by user?), waiting for syncClaim to fix it", "volumeName", volume.Name) 702 } 703 // In both cases, the volume is Bound and the claim is Pending. 704 // Next syncClaim will fix it. To speed it up, we enqueue the claim 705 // into the controller, which results in syncClaim to be called 706 // shortly (and in the right worker goroutine). 707 // This speeds up binding of provisioned volumes - provisioner saves 708 // only the new PV and it expects that next syncClaim will bind the 709 // claim to it. 710 ctrl.claimQueue.Add(claimToClaimKey(claim)) 711 return nil 712 } else if claim.Spec.VolumeName == volume.Name { 713 // Volume is bound to a claim properly, update status if necessary 714 logger.V(4).Info("Synchronizing PersistentVolume, all is bound", "volumeName", volume.Name) 715 if _, err = ctrl.updateVolumePhase(ctx, volume, v1.VolumeBound, ""); err != nil { 716 // Nothing was saved; we will fall back into the same 717 // condition in the next call to this method 718 return err 719 } 720 return nil 721 } else { 722 // Volume is bound to a claim, but the claim is bound elsewhere 723 if metav1.HasAnnotation(volume.ObjectMeta, storagehelpers.AnnDynamicallyProvisioned) && volume.Spec.PersistentVolumeReclaimPolicy == v1.PersistentVolumeReclaimDelete { 724 // This volume was dynamically provisioned for this claim. The 725 // claim got bound elsewhere, and thus this volume is not 726 // needed. Delete it. 727 // Mark the volume as Released for external deleters and to let 728 // the user know. Don't overwrite existing Failed status! 729 if volume.Status.Phase != v1.VolumeReleased && volume.Status.Phase != v1.VolumeFailed { 730 // Also, log this only once: 731 logger.V(2).Info("Dynamically provisioned volume is released and it will be deleted", "volumeName", volume.Name) 732 if volume, err = ctrl.updateVolumePhase(ctx, volume, v1.VolumeReleased, ""); err != nil { 733 // Nothing was saved; we will fall back into the same condition 734 // in the next call to this method 735 return err 736 } 737 } 738 if err = ctrl.reclaimVolume(ctx, volume); err != nil { 739 // Deletion failed, we will fall back into the same condition 740 // in the next call to this method 741 return err 742 } 743 return nil 744 } else { 745 // Volume is bound to a claim, but the claim is bound elsewhere 746 // and it's not dynamically provisioned. 747 if metav1.HasAnnotation(volume.ObjectMeta, storagehelpers.AnnBoundByController) { 748 // This is part of the normal operation of the controller; the 749 // controller tried to use this volume for a claim but the claim 750 // was fulfilled by another volume. We did this; fix it. 751 logger.V(4).Info("Synchronizing PersistentVolume, volume is bound by controller to a claim that is bound to another volume, unbinding", "volumeName", volume.Name) 752 if err = ctrl.unbindVolume(ctx, volume); err != nil { 753 return err 754 } 755 return nil 756 } else { 757 // The PV must have been created with this ptr; leave it alone. 758 logger.V(4).Info("Synchronizing PersistentVolume, volume is bound by user to a claim that is bound to another volume, waiting for the claim to get unbound", "volumeName", volume.Name) 759 // This just updates the volume phase and clears 760 // volume.Spec.ClaimRef.UID. It leaves the volume pre-bound 761 // to the claim. 762 if err = ctrl.unbindVolume(ctx, volume); err != nil { 763 return err 764 } 765 return nil 766 } 767 } 768 } 769 } 770 } 771 772 // updateClaimStatus saves new claim.Status to API server. 773 // Parameters: 774 // 775 // claim - claim to update 776 // phase - phase to set 777 // volume - volume which Capacity is set into claim.Status.Capacity 778 func (ctrl *PersistentVolumeController) updateClaimStatus(ctx context.Context, claim *v1.PersistentVolumeClaim, phase v1.PersistentVolumeClaimPhase, volume *v1.PersistentVolume) (*v1.PersistentVolumeClaim, error) { 779 logger := klog.FromContext(ctx) 780 logger.V(4).Info("Updating PersistentVolumeClaim status", "PVC", klog.KObj(claim), "setPhase", phase) 781 782 dirty := false 783 784 claimClone := claim.DeepCopy() 785 if claim.Status.Phase != phase { 786 claimClone.Status.Phase = phase 787 dirty = true 788 } 789 790 if volume == nil { 791 // Need to reset AccessModes and Capacity 792 if claim.Status.AccessModes != nil { 793 claimClone.Status.AccessModes = nil 794 dirty = true 795 } 796 if claim.Status.Capacity != nil { 797 claimClone.Status.Capacity = nil 798 dirty = true 799 } 800 } else { 801 // Need to update AccessModes and Capacity 802 if !reflect.DeepEqual(claim.Status.AccessModes, volume.Spec.AccessModes) { 803 claimClone.Status.AccessModes = volume.Spec.AccessModes 804 dirty = true 805 } 806 807 // Update Capacity if the claim is becoming Bound, not if it was already. 808 // A discrepancy can be intentional to mean that the PVC filesystem size 809 // doesn't match the PV block device size, so don't clobber it 810 if claim.Status.Phase != phase { 811 volumeCap, ok := volume.Spec.Capacity[v1.ResourceStorage] 812 if !ok { 813 return nil, fmt.Errorf("PersistentVolume %q is without a storage capacity", volume.Name) 814 } 815 claimCap, ok := claim.Status.Capacity[v1.ResourceStorage] 816 // If PV has a resize annotation, set the claim's request capacity 817 if metav1.HasAnnotation(volume.ObjectMeta, util.AnnPreResizeCapacity) { 818 logger.V(2).Info("Volume requires filesystem resize: setting pvc status capacity", "PVC", klog.KObj(claim), "volumeName", volume.Name, "statusCapacity", volume.ObjectMeta.Annotations[util.AnnPreResizeCapacity]) 819 preQty, err := resource.ParseQuantity(volume.ObjectMeta.Annotations[util.AnnPreResizeCapacity]) 820 if err != nil { 821 logger.Info("Parsing pre-resize-capacity from PV failed", "volumeName", volume.Name, "err", err) 822 preQty = volume.Spec.Capacity[v1.ResourceStorage] 823 } 824 if claimClone.Status.Capacity == nil { 825 claimClone.Status.Capacity = make(map[v1.ResourceName]resource.Quantity) 826 } 827 claimClone.Status.Capacity[v1.ResourceStorage] = preQty 828 dirty = true 829 } else if !ok || volumeCap.Cmp(claimCap) != 0 { 830 claimClone.Status.Capacity = volume.Spec.Capacity 831 dirty = true 832 } 833 } 834 } 835 836 if !dirty { 837 // Nothing to do. 838 logger.V(4).Info("Updating PersistentVolumeClaim status, phase already set", "PVC", klog.KObj(claim), "phase", phase) 839 return claim, nil 840 } 841 842 newClaim, err := ctrl.kubeClient.CoreV1().PersistentVolumeClaims(claimClone.Namespace).UpdateStatus(ctx, claimClone, metav1.UpdateOptions{}) 843 if err != nil { 844 logger.V(4).Info("Updating PersistentVolumeClaim status, set phase failed", "PVC", klog.KObj(claim), "phase", phase, "err", err) 845 return newClaim, err 846 } 847 _, err = ctrl.storeClaimUpdate(logger, newClaim) 848 if err != nil { 849 logger.V(4).Info("Updating PersistentVolumeClaim status: cannot update internal cache", "PVC", klog.KObj(claim), "err", err) 850 return newClaim, err 851 } 852 logger.V(2).Info("Claim entered phase", "PVC", klog.KObj(claim), "phase", phase) 853 return newClaim, nil 854 } 855 856 // updateClaimStatusWithEvent saves new claim.Status to API server and emits 857 // given event on the claim. It saves the status and emits the event only when 858 // the status has actually changed from the version saved in API server. 859 // Parameters: 860 // 861 // claim - claim to update 862 // phase - phase to set 863 // volume - volume which Capacity is set into claim.Status.Capacity 864 // eventtype, reason, message - event to send, see EventRecorder.Event() 865 func (ctrl *PersistentVolumeController) updateClaimStatusWithEvent(ctx context.Context, claim *v1.PersistentVolumeClaim, phase v1.PersistentVolumeClaimPhase, volume *v1.PersistentVolume, eventtype, reason, message string) (*v1.PersistentVolumeClaim, error) { 866 logger := klog.FromContext(ctx) 867 logger.V(4).Info("Updating updateClaimStatusWithEvent", "PVC", klog.KObj(claim), "setPhase", phase) 868 if claim.Status.Phase == phase { 869 // Nothing to do. 870 logger.V(4).Info("Updating updateClaimStatusWithEvent, phase already set", "PVC", klog.KObj(claim), "phase", phase) 871 return claim, nil 872 } 873 874 newClaim, err := ctrl.updateClaimStatus(ctx, claim, phase, volume) 875 if err != nil { 876 return nil, err 877 } 878 879 // Emit the event only when the status change happens, not every time 880 // syncClaim is called. 881 logger.V(3).Info("Claim changed status", "PVC", klog.KObj(claim), "phase", phase, "message", message) 882 ctrl.eventRecorder.Event(newClaim, eventtype, reason, message) 883 884 return newClaim, nil 885 } 886 887 // updateVolumePhase saves new volume phase to API server. 888 func (ctrl *PersistentVolumeController) updateVolumePhase(ctx context.Context, volume *v1.PersistentVolume, phase v1.PersistentVolumePhase, message string) (*v1.PersistentVolume, error) { 889 logger := klog.FromContext(ctx) 890 logger.V(4).Info("Updating PersistentVolume", "volumeName", volume.Name, "setPhase", phase) 891 if volume.Status.Phase == phase { 892 // Nothing to do. 893 logger.V(4).Info("Updating PersistentVolume: phase already set", "volumeName", volume.Name, "phase", phase) 894 return volume, nil 895 } 896 897 volumeClone := volume.DeepCopy() 898 volumeClone.Status.Phase = phase 899 volumeClone.Status.Message = message 900 901 newVol, err := ctrl.kubeClient.CoreV1().PersistentVolumes().UpdateStatus(ctx, volumeClone, metav1.UpdateOptions{}) 902 if err != nil { 903 logger.V(4).Info("Updating PersistentVolume: set phase failed", "volumeName", volume.Name, "phase", phase, "err", err) 904 return newVol, err 905 } 906 _, err = ctrl.storeVolumeUpdate(logger, newVol) 907 if err != nil { 908 logger.V(4).Info("Updating PersistentVolume: cannot update internal cache", "volumeName", volume.Name, "err", err) 909 return newVol, err 910 } 911 logger.V(2).Info("Volume entered phase", "volumeName", volume.Name, "phase", phase) 912 return newVol, err 913 } 914 915 // updateVolumePhaseWithEvent saves new volume phase to API server and emits 916 // given event on the volume. It saves the phase and emits the event only when 917 // the phase has actually changed from the version saved in API server. 918 func (ctrl *PersistentVolumeController) updateVolumePhaseWithEvent(ctx context.Context, volume *v1.PersistentVolume, phase v1.PersistentVolumePhase, eventtype, reason, message string) (*v1.PersistentVolume, error) { 919 logger := klog.FromContext(ctx) 920 logger.V(4).Info("Updating updateVolumePhaseWithEvent", "volumeName", volume.Name, "setPhase", phase) 921 if volume.Status.Phase == phase { 922 // Nothing to do. 923 logger.V(4).Info("Updating updateVolumePhaseWithEvent: phase already set", "volumeName", volume.Name, "phase", phase) 924 return volume, nil 925 } 926 927 newVol, err := ctrl.updateVolumePhase(ctx, volume, phase, message) 928 if err != nil { 929 return nil, err 930 } 931 932 // Emit the event only when the status change happens, not every time 933 // syncClaim is called. 934 logger.V(3).Info("Volume changed status", "volumeName", volume.Name, "changedPhase", phase, "message", message) 935 ctrl.eventRecorder.Event(newVol, eventtype, reason, message) 936 937 return newVol, nil 938 } 939 940 // assignDefaultStorageClass updates the claim storage class if there is any, the claim is updated to the API server. 941 // Ignores claims that already have a storage class. 942 // TODO: if resync is ever changed to a larger period, we might need to change how we set the default class on existing unbound claims 943 func (ctrl *PersistentVolumeController) assignDefaultStorageClass(ctx context.Context, claim *v1.PersistentVolumeClaim) (bool, error) { 944 logger := klog.FromContext(ctx) 945 946 if storagehelpers.PersistentVolumeClaimHasClass(claim) { 947 // The user asked for a class. 948 return false, nil 949 } 950 951 class, err := util.GetDefaultClass(ctrl.classLister) 952 if err != nil { 953 return false, err 954 } else if class == nil { 955 logger.V(4).Info("Can not assign storage class to PersistentVolumeClaim: default storage class not found", "PVC", klog.KObj(claim)) 956 return false, nil 957 } 958 959 logger.V(4).Info("Assigning StorageClass to PersistentVolumeClaim", "PVC", klog.KObj(claim), "storageClassName", class.Name) 960 claim.Spec.StorageClassName = &class.Name 961 _, err = ctrl.kubeClient.CoreV1().PersistentVolumeClaims(claim.GetNamespace()).Update(ctx, claim, metav1.UpdateOptions{}) 962 if err != nil { 963 return false, err 964 } 965 966 logger.V(4).Info("Successfully assigned StorageClass to PersistentVolumeClaim", "PVC", klog.KObj(claim), "storageClassName", class.Name) 967 return true, nil 968 } 969 970 // bindVolumeToClaim modifies given volume to be bound to a claim and saves it to 971 // API server. The claim is not modified in this method! 972 func (ctrl *PersistentVolumeController) bindVolumeToClaim(ctx context.Context, volume *v1.PersistentVolume, claim *v1.PersistentVolumeClaim) (*v1.PersistentVolume, error) { 973 logger := klog.FromContext(ctx) 974 logger.V(4).Info("Updating PersistentVolume: binding to claim", "PVC", klog.KObj(claim), "volumeName", volume.Name) 975 976 volumeClone, dirty, err := storagehelpers.GetBindVolumeToClaim(volume, claim) 977 if err != nil { 978 return nil, err 979 } 980 981 // Save the volume only if something was changed 982 if dirty { 983 return ctrl.updateBindVolumeToClaim(ctx, volumeClone, true) 984 } 985 986 logger.V(4).Info("Updating PersistentVolume: already bound to claim", "PVC", klog.KObj(claim), "volumeName", volume.Name) 987 return volume, nil 988 } 989 990 // updateBindVolumeToClaim modifies given volume to be bound to a claim and saves it to 991 // API server. The claim is not modified in this method! 992 func (ctrl *PersistentVolumeController) updateBindVolumeToClaim(ctx context.Context, volumeClone *v1.PersistentVolume, updateCache bool) (*v1.PersistentVolume, error) { 993 logger := klog.FromContext(ctx) 994 logger.V(2).Info("Claim bound to volume", "PVC", klog.KRef(volumeClone.Spec.ClaimRef.Namespace, volumeClone.Spec.ClaimRef.Name), "volumeName", volumeClone.Name) 995 newVol, err := ctrl.kubeClient.CoreV1().PersistentVolumes().Update(ctx, volumeClone, metav1.UpdateOptions{}) 996 if err != nil { 997 logger.V(4).Info("Updating PersistentVolume: binding to claim failed", "PVC", klog.KRef(volumeClone.Spec.ClaimRef.Namespace, volumeClone.Spec.ClaimRef.Name), "volumeName", volumeClone.Name, "err", err) 998 return newVol, err 999 } 1000 if updateCache { 1001 _, err = ctrl.storeVolumeUpdate(logger, newVol) 1002 if err != nil { 1003 logger.V(4).Info("Updating PersistentVolume: cannot update internal cache", "volumeName", volumeClone.Name, "err", err) 1004 return newVol, err 1005 } 1006 } 1007 logger.V(4).Info("Updating PersistentVolume: bound to claim", "PVC", klog.KRef(volumeClone.Spec.ClaimRef.Namespace, volumeClone.Spec.ClaimRef.Name), "volumeName", newVol.Name) 1008 return newVol, nil 1009 } 1010 1011 // bindClaimToVolume modifies the given claim to be bound to a volume and 1012 // saves it to API server. The volume is not modified in this method! 1013 func (ctrl *PersistentVolumeController) bindClaimToVolume(ctx context.Context, claim *v1.PersistentVolumeClaim, volume *v1.PersistentVolume) (*v1.PersistentVolumeClaim, error) { 1014 logger := klog.FromContext(ctx) 1015 logger.V(4).Info("Updating PersistentVolumeClaim: binding to volume", "PVC", klog.KObj(claim), "volumeName", volume.Name) 1016 1017 dirty := false 1018 1019 // Check if the claim was already bound (either by controller or by user) 1020 shouldBind := false 1021 if volume.Name != claim.Spec.VolumeName { 1022 shouldBind = true 1023 } 1024 1025 // The claim from method args can be pointing to watcher cache. We must not 1026 // modify these, therefore create a copy. 1027 claimClone := claim.DeepCopy() 1028 1029 if shouldBind { 1030 dirty = true 1031 // Bind the claim to the volume 1032 claimClone.Spec.VolumeName = volume.Name 1033 1034 // Set AnnBoundByController if it is not set yet 1035 if !metav1.HasAnnotation(claimClone.ObjectMeta, storagehelpers.AnnBoundByController) { 1036 metav1.SetMetaDataAnnotation(&claimClone.ObjectMeta, storagehelpers.AnnBoundByController, "yes") 1037 } 1038 } 1039 1040 // Set AnnBindCompleted if it is not set yet 1041 if !metav1.HasAnnotation(claimClone.ObjectMeta, storagehelpers.AnnBindCompleted) { 1042 metav1.SetMetaDataAnnotation(&claimClone.ObjectMeta, storagehelpers.AnnBindCompleted, "yes") 1043 dirty = true 1044 } 1045 1046 if dirty { 1047 logger.V(2).Info("Volume bound to claim", "PVC", klog.KObj(claim), "volumeName", volume.Name) 1048 newClaim, err := ctrl.kubeClient.CoreV1().PersistentVolumeClaims(claim.Namespace).Update(ctx, claimClone, metav1.UpdateOptions{}) 1049 if err != nil { 1050 logger.V(4).Info("Updating PersistentVolumeClaim: binding to volume failed", "PVC", klog.KObj(claim), "volumeName", volume.Name, "err", err) 1051 return newClaim, err 1052 } 1053 _, err = ctrl.storeClaimUpdate(logger, newClaim) 1054 if err != nil { 1055 logger.V(4).Info("Updating PersistentVolumeClaim: cannot update internal cache", "PVC", klog.KObj(claim), "err", err) 1056 return newClaim, err 1057 } 1058 logger.V(4).Info("Updating PersistentVolumeClaim: bound to volume", "PVC", klog.KObj(claim), "volumeName", volume.Name) 1059 return newClaim, nil 1060 } 1061 1062 logger.V(4).Info("Updating PersistentVolumeClaim: already bound to volume", "PVC", klog.KObj(claim), "volumeName", volume.Name) 1063 return claim, nil 1064 } 1065 1066 // bind saves binding information both to the volume and the claim and marks 1067 // both objects as Bound. Volume is saved first. 1068 // It returns on first error, it's up to the caller to implement some retry 1069 // mechanism. 1070 func (ctrl *PersistentVolumeController) bind(ctx context.Context, volume *v1.PersistentVolume, claim *v1.PersistentVolumeClaim) error { 1071 var err error 1072 // use updateClaim/updatedVolume to keep the original claim/volume for 1073 // logging in error cases. 1074 var updatedClaim *v1.PersistentVolumeClaim 1075 var updatedVolume *v1.PersistentVolume 1076 1077 logger := klog.FromContext(ctx) 1078 logger.V(4).Info("Binding volume to claim", "PVC", klog.KObj(claim), "volumeName", volume.Name) 1079 1080 if updatedVolume, err = ctrl.bindVolumeToClaim(ctx, volume, claim); err != nil { 1081 logger.V(3).Info("Error binding volume to claim: failed saving the volume", "PVC", klog.KObj(claim), "volumeName", volume.Name, "err", err) 1082 return err 1083 } 1084 volume = updatedVolume 1085 1086 if updatedVolume, err = ctrl.updateVolumePhase(ctx, volume, v1.VolumeBound, ""); err != nil { 1087 logger.V(3).Info("Error binding volume to claim: failed saving the volume status", "PVC", klog.KObj(claim), "volumeName", volume.Name, "err", err) 1088 return err 1089 } 1090 volume = updatedVolume 1091 1092 if updatedClaim, err = ctrl.bindClaimToVolume(ctx, claim, volume); err != nil { 1093 logger.V(3).Info("Error binding volume to claim: failed saving the claim", "PVC", klog.KObj(claim), "volumeName", volume.Name, "err", err) 1094 return err 1095 } 1096 claim = updatedClaim 1097 1098 if updatedClaim, err = ctrl.updateClaimStatus(ctx, claim, v1.ClaimBound, volume); err != nil { 1099 logger.V(3).Info("Error binding volume to claim: failed saving the claim status", "PVC", klog.KObj(claim), "volumeName", volume.Name, "err", err) 1100 return err 1101 } 1102 claim = updatedClaim 1103 1104 logger.V(4).Info("Volume bound to claim", "PVC", klog.KObj(claim), "volumeName", volume.Name) 1105 logger.V(4).Info("Volume status after binding", "volumeName", volume.Name, "volumeStatus", getVolumeStatusForLogging(volume)) 1106 logger.V(4).Info("Claim status after binding", "PVC", klog.KObj(claim), "claimStatus", getClaimStatusForLogging(claim)) 1107 return nil 1108 } 1109 1110 // unbindVolume rolls back previous binding of the volume. This may be necessary 1111 // when two controllers bound two volumes to single claim - when we detect this, 1112 // only one binding succeeds and the second one must be rolled back. 1113 // This method updates both Spec and Status. 1114 // It returns on first error, it's up to the caller to implement some retry 1115 // mechanism. 1116 func (ctrl *PersistentVolumeController) unbindVolume(ctx context.Context, volume *v1.PersistentVolume) error { 1117 logger := klog.FromContext(ctx) 1118 logger.V(4).Info("Updating PersistentVolume: rolling back binding from claim", "PVC", klog.KRef(volume.Spec.ClaimRef.Namespace, volume.Spec.ClaimRef.Name), "volumeName", volume.Name) 1119 1120 // Save the PV only when any modification is necessary. 1121 volumeClone := volume.DeepCopy() 1122 1123 if metav1.HasAnnotation(volume.ObjectMeta, storagehelpers.AnnBoundByController) { 1124 // The volume was bound by the controller. 1125 volumeClone.Spec.ClaimRef = nil 1126 delete(volumeClone.Annotations, storagehelpers.AnnBoundByController) 1127 if len(volumeClone.Annotations) == 0 { 1128 // No annotations look better than empty annotation map (and it's easier 1129 // to test). 1130 volumeClone.Annotations = nil 1131 } 1132 } else { 1133 // The volume was pre-bound by user. Clear only the binding UID. 1134 volumeClone.Spec.ClaimRef.UID = "" 1135 } 1136 1137 newVol, err := ctrl.kubeClient.CoreV1().PersistentVolumes().Update(ctx, volumeClone, metav1.UpdateOptions{}) 1138 if err != nil { 1139 logger.V(4).Info("Updating PersistentVolume: rollback failed", "volumeName", volume.Name, "err", err) 1140 return err 1141 } 1142 _, err = ctrl.storeVolumeUpdate(logger, newVol) 1143 if err != nil { 1144 logger.V(4).Info("Updating PersistentVolume: cannot update internal cache", "volumeName", volume.Name, "err", err) 1145 return err 1146 } 1147 logger.V(4).Info("Updating PersistentVolume: rolled back", "volumeName", newVol.Name) 1148 1149 // Update the status 1150 _, err = ctrl.updateVolumePhase(ctx, newVol, v1.VolumeAvailable, "") 1151 return err 1152 } 1153 1154 // reclaimVolume implements volume.Spec.PersistentVolumeReclaimPolicy and 1155 // starts appropriate reclaim action. 1156 func (ctrl *PersistentVolumeController) reclaimVolume(ctx context.Context, volume *v1.PersistentVolume) error { 1157 logger := klog.FromContext(ctx) 1158 if migrated := volume.Annotations[storagehelpers.AnnMigratedTo]; len(migrated) > 0 { 1159 // PV is Migrated. The PV controller should stand down and the external 1160 // provisioner will handle this PV 1161 return nil 1162 } 1163 switch volume.Spec.PersistentVolumeReclaimPolicy { 1164 case v1.PersistentVolumeReclaimRetain: 1165 logger.V(4).Info("ReclaimVolume: policy is Retain, nothing to do", "volumeName", volume.Name) 1166 1167 case v1.PersistentVolumeReclaimRecycle: 1168 logger.V(4).Info("ReclaimVolume: policy is Recycle", "volumeName", volume.Name) 1169 opName := fmt.Sprintf("recycle-%s[%s]", volume.Name, string(volume.UID)) 1170 ctrl.scheduleOperation(logger, opName, func() error { 1171 ctrl.recycleVolumeOperation(ctx, volume) 1172 return nil 1173 }) 1174 1175 case v1.PersistentVolumeReclaimDelete: 1176 logger.V(4).Info("ReclaimVolume: policy is Delete", "volumeName", volume.Name) 1177 opName := fmt.Sprintf("delete-%s[%s]", volume.Name, string(volume.UID)) 1178 // create a start timestamp entry in cache for deletion operation if no one exists with 1179 // key = volume.Name, pluginName = provisionerName, operation = "delete" 1180 ctrl.operationTimestamps.AddIfNotExist(volume.Name, ctrl.getProvisionerNameFromVolume(volume), "delete") 1181 ctrl.scheduleOperation(logger, opName, func() error { 1182 _, err := ctrl.deleteVolumeOperation(ctx, volume) 1183 if err != nil { 1184 // only report error count to "volume_operation_total_errors" 1185 // latency reporting will happen when the volume get finally 1186 // deleted and a volume deleted event is captured 1187 metrics.RecordMetric(volume.Name, &ctrl.operationTimestamps, err) 1188 } 1189 return err 1190 }) 1191 1192 default: 1193 // Unknown PersistentVolumeReclaimPolicy 1194 if _, err := ctrl.updateVolumePhaseWithEvent(ctx, volume, v1.VolumeFailed, v1.EventTypeWarning, "VolumeUnknownReclaimPolicy", "Volume has unrecognized PersistentVolumeReclaimPolicy"); err != nil { 1195 return err 1196 } 1197 } 1198 return nil 1199 } 1200 1201 // recycleVolumeOperation recycles a volume. This method is running in 1202 // standalone goroutine and already has all necessary locks. 1203 func (ctrl *PersistentVolumeController) recycleVolumeOperation(ctx context.Context, volume *v1.PersistentVolume) { 1204 logger := klog.FromContext(ctx) 1205 logger.V(4).Info("RecycleVolumeOperation started", "volumeName", volume.Name) 1206 1207 // This method may have been waiting for a volume lock for some time. 1208 // Previous recycleVolumeOperation might just have saved an updated version, 1209 // so read current volume state now. 1210 newVolume, err := ctrl.kubeClient.CoreV1().PersistentVolumes().Get(ctx, volume.Name, metav1.GetOptions{}) 1211 if err != nil { 1212 logger.V(3).Info("Error reading persistent volume", "volumeName", volume.Name, "err", err) 1213 return 1214 } 1215 needsReclaim, err := ctrl.isVolumeReleased(logger, newVolume) 1216 if err != nil { 1217 logger.V(3).Info("Error reading claim for volume", "volumeName", volume.Name, "err", err) 1218 return 1219 } 1220 if !needsReclaim { 1221 logger.V(3).Info("Volume no longer needs recycling, skipping", "volumeName", volume.Name) 1222 return 1223 } 1224 pods, used, err := ctrl.isVolumeUsed(newVolume) 1225 if err != nil { 1226 logger.V(3).Info("Can't recycle volume", "volumeName", volume.Name, "err", err) 1227 return 1228 } 1229 1230 // Verify the claim is in cache: if so, then it is a different PVC with the same name 1231 // since the volume is known to be released at this moment. The new (cached) PVC must use 1232 // a different PV -- we checked that the PV is unused in isVolumeReleased. 1233 // So the old PV is safe to be recycled. 1234 claimName := claimrefToClaimKey(volume.Spec.ClaimRef) 1235 _, claimCached, err := ctrl.claims.GetByKey(claimName) 1236 if err != nil { 1237 logger.V(3).Info("Error getting the claim from cache", "PVC", klog.KRef(volume.Spec.ClaimRef.Namespace, volume.Spec.ClaimRef.Name)) 1238 return 1239 } 1240 1241 if used && !claimCached { 1242 msg := fmt.Sprintf("Volume is used by pods: %s", strings.Join(pods, ",")) 1243 logger.V(3).Info("Can't recycle volume", "volumeName", volume.Name, "msg", msg) 1244 ctrl.eventRecorder.Event(volume, v1.EventTypeNormal, events.VolumeFailedRecycle, msg) 1245 return 1246 } 1247 1248 // Use the newest volume copy, this will save us from version conflicts on 1249 // saving. 1250 volume = newVolume 1251 1252 // Find a plugin. 1253 spec := vol.NewSpecFromPersistentVolume(volume, false) 1254 plugin, err := ctrl.volumePluginMgr.FindRecyclablePluginBySpec(spec) 1255 if err != nil { 1256 // No recycler found. Emit an event and mark the volume Failed. 1257 if _, err = ctrl.updateVolumePhaseWithEvent(ctx, volume, v1.VolumeFailed, v1.EventTypeWarning, events.VolumeFailedRecycle, "No recycler plugin found for the volume!"); err != nil { 1258 logger.V(4).Info("RecycleVolumeOperation: failed to mark volume as failed", "volumeName", volume.Name, "err", err) 1259 // Save failed, retry on the next deletion attempt 1260 return 1261 } 1262 // Despite the volume being Failed, the controller will retry recycling 1263 // the volume in every syncVolume() call. 1264 return 1265 } 1266 1267 // Plugin found 1268 recorder := ctrl.newRecyclerEventRecorder(volume) 1269 1270 if err = plugin.Recycle(volume.Name, spec, recorder); err != nil { 1271 // Recycler failed 1272 strerr := fmt.Sprintf("Recycle failed: %s", err) 1273 if _, err = ctrl.updateVolumePhaseWithEvent(ctx, volume, v1.VolumeFailed, v1.EventTypeWarning, events.VolumeFailedRecycle, strerr); err != nil { 1274 logger.V(4).Info("RecycleVolumeOperation: failed to mark volume as failed", "volumeName", volume.Name, "err", err) 1275 // Save failed, retry on the next deletion attempt 1276 return 1277 } 1278 // Despite the volume being Failed, the controller will retry recycling 1279 // the volume in every syncVolume() call. 1280 return 1281 } 1282 1283 logger.V(2).Info("Volume recycled", "volumeName", volume.Name) 1284 // Send an event 1285 ctrl.eventRecorder.Event(volume, v1.EventTypeNormal, events.VolumeRecycled, "Volume recycled") 1286 // Make the volume available again 1287 if err = ctrl.unbindVolume(ctx, volume); err != nil { 1288 // Oops, could not save the volume and therefore the controller will 1289 // recycle the volume again on next update. We _could_ maintain a cache 1290 // of "recently recycled volumes" and avoid unnecessary recycling, this 1291 // is left out as future optimization. 1292 logger.V(3).Info("RecycleVolumeOperation: failed to make recycled volume 'Available', we will recycle the volume again", "volumeName", volume.Name, "err", err) 1293 return 1294 } 1295 } 1296 1297 // deleteVolumeOperation deletes a volume. This method is running in standalone 1298 // goroutine and already has all necessary locks. 1299 func (ctrl *PersistentVolumeController) deleteVolumeOperation(ctx context.Context, volume *v1.PersistentVolume) (string, error) { 1300 logger := klog.FromContext(ctx) 1301 logger.V(4).Info("DeleteVolumeOperation started", "volumeName", volume.Name) 1302 1303 // This method may have been waiting for a volume lock for some time. 1304 // Previous deleteVolumeOperation might just have saved an updated version, so 1305 // read current volume state now. 1306 newVolume, err := ctrl.kubeClient.CoreV1().PersistentVolumes().Get(ctx, volume.Name, metav1.GetOptions{}) 1307 if err != nil { 1308 logger.V(3).Info("Error reading persistent volume", "volumeName", volume.Name, "err", err) 1309 return "", nil 1310 } 1311 1312 if !utilfeature.DefaultFeatureGate.Enabled(features.HonorPVReclaimPolicy) { 1313 if newVolume.GetDeletionTimestamp() != nil { 1314 logger.V(3).Info("Volume is already being deleted", "volumeName", volume.Name) 1315 return "", nil 1316 } 1317 } 1318 needsReclaim, err := ctrl.isVolumeReleased(logger, newVolume) 1319 if err != nil { 1320 logger.V(3).Info("Error reading claim for volume", "volumeName", volume.Name, "err", err) 1321 return "", nil 1322 } 1323 if !needsReclaim { 1324 logger.V(3).Info("Volume no longer needs deletion, skipping", "volumeName", volume.Name) 1325 return "", nil 1326 } 1327 1328 pluginName, deleted, err := ctrl.doDeleteVolume(ctx, volume) 1329 if err != nil { 1330 // Delete failed, update the volume and emit an event. 1331 logger.V(3).Info("Deletion of volume failed", "volumeName", volume.Name, "err", err) 1332 if volerr.IsDeletedVolumeInUse(err) { 1333 // The plugin needs more time, don't mark the volume as Failed 1334 // and send Normal event only 1335 ctrl.eventRecorder.Event(volume, v1.EventTypeNormal, events.VolumeDelete, err.Error()) 1336 } else { 1337 // The plugin failed, mark the volume as Failed and send Warning 1338 // event 1339 if _, err := ctrl.updateVolumePhaseWithEvent(ctx, volume, v1.VolumeFailed, v1.EventTypeWarning, events.VolumeFailedDelete, err.Error()); err != nil { 1340 logger.V(4).Info("DeleteVolumeOperation: failed to mark volume as failed", "volumeName", volume.Name, "err", err) 1341 // Save failed, retry on the next deletion attempt 1342 return pluginName, err 1343 } 1344 } 1345 1346 // Despite the volume being Failed, the controller will retry deleting 1347 // the volume in every syncVolume() call. 1348 return pluginName, err 1349 } 1350 if !deleted { 1351 // The volume waits for deletion by an external plugin. Do nothing. 1352 return pluginName, nil 1353 } 1354 1355 logger.V(4).Info("DeleteVolumeOperation: success", "volumeName", volume.Name) 1356 // Delete the volume 1357 if err = ctrl.kubeClient.CoreV1().PersistentVolumes().Delete(ctx, volume.Name, metav1.DeleteOptions{}); err != nil { 1358 // Oops, could not delete the volume and therefore the controller will 1359 // try to delete the volume again on next update. We _could_ maintain a 1360 // cache of "recently deleted volumes" and avoid unnecessary deletion, 1361 // this is left out as future optimization. 1362 logger.V(3).Info("Failed to delete volume from database", "volumeName", volume.Name, "err", err) 1363 return pluginName, nil 1364 } 1365 return pluginName, nil 1366 } 1367 1368 // isVolumeReleased returns true if given volume is released and can be recycled 1369 // or deleted, based on its retain policy. I.e. the volume is bound to a claim 1370 // and the claim does not exist or exists and is bound to different volume. 1371 func (ctrl *PersistentVolumeController) isVolumeReleased(logger klog.Logger, volume *v1.PersistentVolume) (bool, error) { 1372 // A volume needs reclaim if it has ClaimRef and appropriate claim does not 1373 // exist. 1374 if volume.Spec.ClaimRef == nil { 1375 logger.V(4).Info("isVolumeReleased: ClaimRef is nil", "volumeName", volume.Name) 1376 return false, nil 1377 } 1378 if volume.Spec.ClaimRef.UID == "" { 1379 // This is a volume bound by user and the controller has not finished 1380 // binding to the real claim yet. 1381 logger.V(4).Info("isVolumeReleased: ClaimRef is not bound", "volumeName", volume.Name) 1382 return false, nil 1383 } 1384 1385 var claim *v1.PersistentVolumeClaim 1386 claimName := claimrefToClaimKey(volume.Spec.ClaimRef) 1387 obj, found, err := ctrl.claims.GetByKey(claimName) 1388 if err != nil { 1389 return false, err 1390 } 1391 if !found { 1392 // Fall through with claim = nil 1393 } else { 1394 var ok bool 1395 claim, ok = obj.(*v1.PersistentVolumeClaim) 1396 if !ok { 1397 return false, fmt.Errorf("cannot convert object from claim cache to claim!?: %#v", obj) 1398 } 1399 } 1400 if claim != nil && claim.UID == volume.Spec.ClaimRef.UID { 1401 // the claim still exists and has the right UID 1402 1403 if len(claim.Spec.VolumeName) > 0 && claim.Spec.VolumeName != volume.Name { 1404 // the claim is bound to another PV, this PV *is* released 1405 return true, nil 1406 } 1407 1408 logger.V(4).Info("isVolumeReleased: ClaimRef is still valid, volume is not released", "volumeName", volume.Name) 1409 return false, nil 1410 } 1411 1412 logger.V(2).Info("isVolumeReleased: volume is released", "volumeName", volume.Name) 1413 return true, nil 1414 } 1415 1416 func (ctrl *PersistentVolumeController) findPodsByPVCKey(key string) ([]*v1.Pod, error) { 1417 pods := []*v1.Pod{} 1418 objs, err := ctrl.podIndexer.ByIndex(common.PodPVCIndex, key) 1419 if err != nil { 1420 return pods, err 1421 } 1422 for _, obj := range objs { 1423 pod, ok := obj.(*v1.Pod) 1424 if !ok { 1425 continue 1426 } 1427 pods = append(pods, pod) 1428 } 1429 return pods, err 1430 } 1431 1432 // isVolumeUsed returns list of active pods that use given PV. 1433 func (ctrl *PersistentVolumeController) isVolumeUsed(pv *v1.PersistentVolume) ([]string, bool, error) { 1434 if pv.Spec.ClaimRef == nil { 1435 return nil, false, nil 1436 } 1437 podNames := sets.NewString() 1438 pvcKey := fmt.Sprintf("%s/%s", pv.Spec.ClaimRef.Namespace, pv.Spec.ClaimRef.Name) 1439 pods, err := ctrl.findPodsByPVCKey(pvcKey) 1440 if err != nil { 1441 return nil, false, fmt.Errorf("error finding pods by pvc %q: %s", pvcKey, err) 1442 } 1443 for _, pod := range pods { 1444 if util.IsPodTerminated(pod, pod.Status) { 1445 continue 1446 } 1447 podNames.Insert(pod.Namespace + "/" + pod.Name) 1448 } 1449 return podNames.List(), podNames.Len() != 0, nil 1450 } 1451 1452 // findNonScheduledPodsByPVC returns list of non-scheduled active pods that reference given PVC. 1453 func (ctrl *PersistentVolumeController) findNonScheduledPodsByPVC(pvc *v1.PersistentVolumeClaim) ([]string, error) { 1454 pvcKey := fmt.Sprintf("%s/%s", pvc.Namespace, pvc.Name) 1455 pods, err := ctrl.findPodsByPVCKey(pvcKey) 1456 if err != nil { 1457 return nil, err 1458 } 1459 podNames := []string{} 1460 for _, pod := range pods { 1461 if util.IsPodTerminated(pod, pod.Status) { 1462 continue 1463 } 1464 if len(pod.Spec.NodeName) == 0 { 1465 podNames = append(podNames, pod.Name) 1466 } 1467 } 1468 return podNames, nil 1469 } 1470 1471 // doDeleteVolume finds appropriate delete plugin and deletes given volume, returning 1472 // the volume plugin name. Also, it returns 'true', when the volume was deleted and 1473 // 'false' when the volume cannot be deleted because the deleter is external. No 1474 // error should be reported in this case. 1475 func (ctrl *PersistentVolumeController) doDeleteVolume(ctx context.Context, volume *v1.PersistentVolume) (string, bool, error) { 1476 logger := klog.FromContext(ctx) 1477 logger.V(4).Info("doDeleteVolume", "volumeName", volume.Name) 1478 var err error 1479 1480 plugin, err := ctrl.findDeletablePlugin(volume) 1481 if err != nil { 1482 return "", false, err 1483 } 1484 if plugin == nil { 1485 // External deleter is requested, do nothing 1486 logger.V(3).Info("External deleter for volume requested, ignoring", "volumeName", volume.Name) 1487 return "", false, nil 1488 } 1489 1490 // Plugin found 1491 pluginName := plugin.GetPluginName() 1492 logger.V(5).Info("Found a deleter plugin for volume", "pluginName", pluginName, "volumeName", volume.Name) 1493 spec := vol.NewSpecFromPersistentVolume(volume, false) 1494 deleter, err := plugin.NewDeleter(logger, spec) 1495 if err != nil { 1496 // Cannot create deleter 1497 return pluginName, false, fmt.Errorf("failed to create deleter for volume %q: %w", volume.Name, err) 1498 } 1499 1500 opComplete := util.OperationCompleteHook(pluginName, "volume_delete") 1501 err = deleter.Delete() 1502 opComplete(volumetypes.CompleteFuncParam{Err: &err}) 1503 if err != nil { 1504 // Deleter failed 1505 return pluginName, false, err 1506 } 1507 logger.V(2).Info("Volume deleted", "volumeName", volume.Name) 1508 // Remove in-tree delete finalizer on the PV as the volume has been deleted from the underlying storage 1509 if utilfeature.DefaultFeatureGate.Enabled(features.HonorPVReclaimPolicy) { 1510 err = ctrl.removeDeletionProtectionFinalizer(ctx, volume) 1511 if err != nil { 1512 return pluginName, true, err 1513 } 1514 } 1515 return pluginName, true, nil 1516 } 1517 1518 func (ctrl *PersistentVolumeController) removeDeletionProtectionFinalizer(ctx context.Context, volume *v1.PersistentVolume) error { 1519 var err error 1520 pvUpdateNeeded := false 1521 // Retrieve latest version 1522 logger := klog.FromContext(ctx) 1523 newVolume, err := ctrl.kubeClient.CoreV1().PersistentVolumes().Get(ctx, volume.Name, metav1.GetOptions{}) 1524 if err != nil { 1525 logger.Error(err, "Error reading persistent volume", "volumeName", volume.Name) 1526 return err 1527 } 1528 volume = newVolume 1529 volumeClone := volume.DeepCopy() 1530 pvFinalizers := volumeClone.Finalizers 1531 if pvFinalizers != nil && slice.ContainsString(pvFinalizers, storagehelpers.PVDeletionInTreeProtectionFinalizer, nil) { 1532 pvUpdateNeeded = true 1533 pvFinalizers = slice.RemoveString(pvFinalizers, storagehelpers.PVDeletionInTreeProtectionFinalizer, nil) 1534 } 1535 if pvUpdateNeeded { 1536 volumeClone.SetFinalizers(pvFinalizers) 1537 _, err = ctrl.kubeClient.CoreV1().PersistentVolumes().Update(ctx, volumeClone, metav1.UpdateOptions{}) 1538 if err != nil { 1539 return fmt.Errorf("persistent volume controller can't update finalizer: %v", err) 1540 } 1541 _, err = ctrl.storeVolumeUpdate(logger, volumeClone) 1542 if err != nil { 1543 return fmt.Errorf("persistent Volume Controller can't anneal migration finalizer: %v", err) 1544 } 1545 logger.V(2).Info("PV in-tree protection finalizer removed from volume", "volumeName", volume.Name) 1546 } 1547 return nil 1548 } 1549 1550 // provisionClaim starts new asynchronous operation to provision a claim if 1551 // provisioning is enabled. 1552 func (ctrl *PersistentVolumeController) provisionClaim(ctx context.Context, claim *v1.PersistentVolumeClaim) error { 1553 if !ctrl.enableDynamicProvisioning { 1554 return nil 1555 } 1556 logger := klog.FromContext(ctx) 1557 logger.V(4).Info("provisionClaim: started", "PVC", klog.KObj(claim)) 1558 opName := fmt.Sprintf("provision-%s[%s]", claimToClaimKey(claim), string(claim.UID)) 1559 plugin, storageClass, err := ctrl.findProvisionablePlugin(claim) 1560 // findProvisionablePlugin does not return err for external provisioners 1561 if err != nil { 1562 ctrl.eventRecorder.Event(claim, v1.EventTypeWarning, events.ProvisioningFailed, err.Error()) 1563 logger.Error(err, "Error finding provisioning plugin for claim", "PVC", klog.KObj(claim)) 1564 // failed to find the requested provisioning plugin, directly return err for now. 1565 // controller will retry the provisioning in every syncUnboundClaim() call 1566 // retain the original behavior of returning nil from provisionClaim call 1567 return nil 1568 } 1569 ctrl.scheduleOperation(logger, opName, func() error { 1570 // create a start timestamp entry in cache for provision operation if no one exists with 1571 // key = claimKey, pluginName = provisionerName, operation = "provision" 1572 claimKey := claimToClaimKey(claim) 1573 ctrl.operationTimestamps.AddIfNotExist(claimKey, ctrl.getProvisionerName(plugin, storageClass), "provision") 1574 var err error 1575 if plugin == nil { 1576 _, err = ctrl.provisionClaimOperationExternal(ctx, claim, storageClass) 1577 } else { 1578 _, err = ctrl.provisionClaimOperation(ctx, claim, plugin, storageClass) 1579 } 1580 // if error happened, record an error count metric 1581 // timestamp entry will remain in cache until a success binding has happened 1582 if err != nil { 1583 metrics.RecordMetric(claimKey, &ctrl.operationTimestamps, err) 1584 } 1585 return err 1586 }) 1587 return nil 1588 } 1589 1590 // provisionClaimOperation provisions a volume. This method is running in 1591 // standalone goroutine and already has all necessary locks. 1592 func (ctrl *PersistentVolumeController) provisionClaimOperation( 1593 ctx context.Context, 1594 claim *v1.PersistentVolumeClaim, 1595 plugin vol.ProvisionableVolumePlugin, 1596 storageClass *storage.StorageClass) (string, error) { 1597 claimClass := storagehelpers.GetPersistentVolumeClaimClass(claim) 1598 logger := klog.FromContext(ctx) 1599 logger.V(4).Info("provisionClaimOperation started", "PVC", klog.KObj(claim), "storageClassName", claimClass) 1600 1601 // called from provisionClaim(), in this case, plugin MUST NOT be nil 1602 // NOTE: checks on plugin/storageClass has been saved 1603 pluginName := plugin.GetPluginName() 1604 if pluginName != "kubernetes.io/csi" && claim.Spec.DataSource != nil { 1605 // Only CSI plugin can have a DataSource. Fail the operation 1606 // if Datasource in Claim is not nil and it is not a CSI plugin, 1607 strerr := fmt.Sprintf("plugin %q is not a CSI plugin. Only CSI plugin can provision a claim with a datasource", pluginName) 1608 logger.V(2).Info(strerr) 1609 ctrl.eventRecorder.Event(claim, v1.EventTypeWarning, events.ProvisioningFailed, strerr) 1610 return pluginName, fmt.Errorf(strerr) 1611 1612 } 1613 provisionerName := storageClass.Provisioner 1614 logger.V(4).Info("provisionClaimOperation", "PVC", klog.KObj(claim), "pluginName", pluginName, "provisionerName", provisionerName) 1615 1616 // Add provisioner annotation to be consistent with external provisioner workflow 1617 newClaim, err := ctrl.setClaimProvisioner(ctx, claim, provisionerName) 1618 if err != nil { 1619 // Save failed, the controller will retry in the next sync 1620 logger.V(2).Info("Error saving claim", "PVC", klog.KObj(claim), "err", err) 1621 return pluginName, err 1622 } 1623 claim = newClaim 1624 1625 // internal provisioning 1626 1627 // A previous provisionClaimOperation may just have finished while we were waiting for 1628 // the locks. Check that PV (with deterministic name) hasn't been provisioned 1629 // yet. 1630 1631 pvName := ctrl.getProvisionedVolumeNameForClaim(claim) 1632 volume, err := ctrl.kubeClient.CoreV1().PersistentVolumes().Get(ctx, pvName, metav1.GetOptions{}) 1633 if err != nil && !apierrors.IsNotFound(err) { 1634 logger.V(3).Info("Error reading persistent volume", "PV", klog.KRef("", pvName), "err", err) 1635 return pluginName, err 1636 } 1637 if err == nil && volume != nil { 1638 // Volume has been already provisioned, nothing to do. 1639 logger.V(4).Info("provisionClaimOperation: volume already exists, skipping", "PVC", klog.KObj(claim)) 1640 return pluginName, err 1641 } 1642 1643 // Prepare a claimRef to the claim early (to fail before a volume is 1644 // provisioned) 1645 claimRef, err := ref.GetReference(scheme.Scheme, claim) 1646 if err != nil { 1647 logger.V(3).Info("Unexpected error getting claim reference", "err", err) 1648 return pluginName, err 1649 } 1650 1651 // Gather provisioning options 1652 tags := make(map[string]string) 1653 tags[CloudVolumeCreatedForClaimNamespaceTag] = claim.Namespace 1654 tags[CloudVolumeCreatedForClaimNameTag] = claim.Name 1655 tags[CloudVolumeCreatedForVolumeNameTag] = pvName 1656 1657 options := vol.VolumeOptions{ 1658 PersistentVolumeReclaimPolicy: *storageClass.ReclaimPolicy, 1659 MountOptions: storageClass.MountOptions, 1660 CloudTags: &tags, 1661 ClusterName: ctrl.clusterName, 1662 PVName: pvName, 1663 PVC: claim, 1664 Parameters: storageClass.Parameters, 1665 } 1666 1667 // Refuse to provision if the plugin doesn't support mount options, creation 1668 // of PV would be rejected by validation anyway 1669 if !plugin.SupportsMountOption() && len(options.MountOptions) > 0 { 1670 strerr := fmt.Sprintf("Mount options are not supported by the provisioner but StorageClass %q has mount options %v", storageClass.Name, options.MountOptions) 1671 logger.V(2).Info("Mount options are not supported by the provisioner but claim's StorageClass has mount options", "PVC", klog.KObj(claim), "storageClassName", storageClass.Name, "options", options.MountOptions) 1672 ctrl.eventRecorder.Event(claim, v1.EventTypeWarning, events.ProvisioningFailed, strerr) 1673 return pluginName, fmt.Errorf("provisioner %q doesn't support mount options", plugin.GetPluginName()) 1674 } 1675 1676 // Provision the volume 1677 provisioner, err := plugin.NewProvisioner(logger, options) 1678 if err != nil { 1679 strerr := fmt.Sprintf("Failed to create provisioner: %v", err) 1680 logger.V(2).Info("Failed to create provisioner for claim with StorageClass", "PVC", klog.KObj(claim), "storageClassName", storageClass.Name, "err", err) 1681 ctrl.eventRecorder.Event(claim, v1.EventTypeWarning, events.ProvisioningFailed, strerr) 1682 return pluginName, err 1683 } 1684 1685 var selectedNode *v1.Node = nil 1686 if nodeName, ok := claim.Annotations[storagehelpers.AnnSelectedNode]; ok { 1687 selectedNode, err = ctrl.NodeLister.Get(nodeName) 1688 if err != nil { 1689 strerr := fmt.Sprintf("Failed to get target node: %v", err) 1690 logger.V(3).Info("Unexpected error getting target node for claim", "node", klog.KRef("", nodeName), "PVC", klog.KObj(claim), "err", err) 1691 ctrl.eventRecorder.Event(claim, v1.EventTypeWarning, events.ProvisioningFailed, strerr) 1692 return pluginName, err 1693 } 1694 } 1695 allowedTopologies := storageClass.AllowedTopologies 1696 1697 opComplete := util.OperationCompleteHook(plugin.GetPluginName(), "volume_provision") 1698 volume, err = provisioner.Provision(selectedNode, allowedTopologies) 1699 opComplete(volumetypes.CompleteFuncParam{Err: &err}) 1700 if err != nil { 1701 // Other places of failure have nothing to do with VolumeScheduling, 1702 // so just let controller retry in the next sync. We'll only call func 1703 // rescheduleProvisioning here when the underlying provisioning actually failed. 1704 ctrl.rescheduleProvisioning(ctx, claim) 1705 1706 strerr := fmt.Sprintf("Failed to provision volume with StorageClass %q: %v", storageClass.Name, err) 1707 logger.V(2).Info("Failed to provision volume for claim with StorageClass", "PVC", klog.KObj(claim), "storageClassName", storageClass.Name, "err", err) 1708 ctrl.eventRecorder.Event(claim, v1.EventTypeWarning, events.ProvisioningFailed, strerr) 1709 return pluginName, err 1710 } 1711 1712 logger.V(3).Info("Volume for claim created", "PVC", klog.KObj(claim), "volumeName", volume.Name) 1713 1714 // Create Kubernetes PV object for the volume. 1715 if volume.Name == "" { 1716 volume.Name = pvName 1717 } 1718 // Bind it to the claim 1719 volume.Spec.ClaimRef = claimRef 1720 volume.Status.Phase = v1.VolumeBound 1721 volume.Spec.StorageClassName = claimClass 1722 1723 // Add AnnBoundByController (used in deleting the volume) 1724 metav1.SetMetaDataAnnotation(&volume.ObjectMeta, storagehelpers.AnnBoundByController, "yes") 1725 metav1.SetMetaDataAnnotation(&volume.ObjectMeta, storagehelpers.AnnDynamicallyProvisioned, plugin.GetPluginName()) 1726 1727 if utilfeature.DefaultFeatureGate.Enabled(features.HonorPVReclaimPolicy) { 1728 if volume.Spec.PersistentVolumeReclaimPolicy == v1.PersistentVolumeReclaimDelete { 1729 // Add In-Tree protection finalizer here only when the reclaim policy is `Delete` 1730 volume.SetFinalizers([]string{storagehelpers.PVDeletionInTreeProtectionFinalizer}) 1731 } 1732 } 1733 1734 // Try to create the PV object several times 1735 for i := 0; i < ctrl.createProvisionedPVRetryCount; i++ { 1736 logger.V(4).Info("provisionClaimOperation: trying to save volume", "PVC", klog.KObj(claim), "volumeName", volume.Name) 1737 var newVol *v1.PersistentVolume 1738 if newVol, err = ctrl.kubeClient.CoreV1().PersistentVolumes().Create(ctx, volume, metav1.CreateOptions{}); err == nil || apierrors.IsAlreadyExists(err) { 1739 // Save succeeded. 1740 if err != nil { 1741 logger.V(3).Info("Volume for claim already exists, reusing", "PVC", klog.KObj(claim), "volumeName", volume.Name) 1742 err = nil 1743 } else { 1744 logger.V(3).Info("Volume for claim saved", "PVC", klog.KObj(claim), "volumeName", volume.Name) 1745 1746 _, updateErr := ctrl.storeVolumeUpdate(logger, newVol) 1747 if updateErr != nil { 1748 // We will get an "volume added" event soon, this is not a big error 1749 logger.V(4).Info("provisionClaimOperation: cannot update internal cache", "volumeName", volume.Name, "err", updateErr) 1750 } 1751 } 1752 break 1753 } 1754 // Save failed, try again after a while. 1755 logger.V(3).Info("Failed to save volume for claim", "PVC", klog.KObj(claim), "volumeName", volume.Name, "err", err) 1756 time.Sleep(ctrl.createProvisionedPVInterval) 1757 } 1758 1759 if err != nil { 1760 // Save failed. Now we have a storage asset outside of Kubernetes, 1761 // but we don't have appropriate PV object for it. 1762 // Emit some event here and try to delete the storage asset several 1763 // times. 1764 strerr := fmt.Sprintf("Error creating provisioned PV object for claim %s: %v. Deleting the volume.", claimToClaimKey(claim), err) 1765 logger.V(3).Info(strerr) 1766 ctrl.eventRecorder.Event(claim, v1.EventTypeWarning, events.ProvisioningFailed, strerr) 1767 1768 var deleteErr error 1769 var deleted bool 1770 for i := 0; i < ctrl.createProvisionedPVRetryCount; i++ { 1771 _, deleted, deleteErr = ctrl.doDeleteVolume(ctx, volume) 1772 if deleteErr == nil && deleted { 1773 // Delete succeeded 1774 logger.V(4).Info("provisionClaimOperation: cleaning volume succeeded", "PVC", klog.KObj(claim), "volumeName", volume.Name) 1775 break 1776 } 1777 if !deleted { 1778 // This is unreachable code, the volume was provisioned by an 1779 // internal plugin and therefore there MUST be an internal 1780 // plugin that deletes it. 1781 logger.Error(nil, "Error finding internal deleter for volume plugin", "plugin", plugin.GetPluginName()) 1782 break 1783 } 1784 // Delete failed, try again after a while. 1785 logger.V(3).Info("Failed to delete volume", "volumeName", volume.Name, "err", deleteErr) 1786 time.Sleep(ctrl.createProvisionedPVInterval) 1787 } 1788 1789 if deleteErr != nil { 1790 // Delete failed several times. There is an orphaned volume and there 1791 // is nothing we can do about it. 1792 strerr := fmt.Sprintf("Error cleaning provisioned volume for claim %s: %v. Please delete manually.", claimToClaimKey(claim), deleteErr) 1793 logger.V(2).Info(strerr) 1794 ctrl.eventRecorder.Event(claim, v1.EventTypeWarning, events.ProvisioningCleanupFailed, strerr) 1795 } 1796 } else { 1797 logger.V(2).Info("Volume provisioned for claim", "PVC", klog.KObj(claim), "volumeName", volume.Name) 1798 msg := fmt.Sprintf("Successfully provisioned volume %s using %s", volume.Name, plugin.GetPluginName()) 1799 ctrl.eventRecorder.Event(claim, v1.EventTypeNormal, events.ProvisioningSucceeded, msg) 1800 } 1801 return pluginName, nil 1802 } 1803 1804 // provisionClaimOperationExternal provisions a volume using external provisioner async-ly 1805 // This method will be running in a standalone go-routine scheduled in "provisionClaim" 1806 func (ctrl *PersistentVolumeController) provisionClaimOperationExternal( 1807 ctx context.Context, 1808 claim *v1.PersistentVolumeClaim, 1809 storageClass *storage.StorageClass) (string, error) { 1810 claimClass := storagehelpers.GetPersistentVolumeClaimClass(claim) 1811 logger := klog.FromContext(ctx) 1812 logger.V(4).Info("provisionClaimOperationExternal started", "PVC", klog.KObj(claim), "storageClassName", claimClass) 1813 // Set provisionerName to external provisioner name by setClaimProvisioner 1814 var err error 1815 provisionerName := storageClass.Provisioner 1816 if ctrl.csiMigratedPluginManager.IsMigrationEnabledForPlugin(storageClass.Provisioner) { 1817 // update the provisioner name to use the migrated CSI plugin name 1818 provisionerName, err = ctrl.translator.GetCSINameFromInTreeName(storageClass.Provisioner) 1819 if err != nil { 1820 strerr := fmt.Sprintf("error getting CSI name for In tree plugin %s: %v", storageClass.Provisioner, err) 1821 logger.V(2).Info(strerr) 1822 ctrl.eventRecorder.Event(claim, v1.EventTypeWarning, events.ProvisioningFailed, strerr) 1823 return provisionerName, err 1824 } 1825 } 1826 // Add provisioner annotation so external provisioners know when to start 1827 newClaim, err := ctrl.setClaimProvisioner(ctx, claim, provisionerName) 1828 if err != nil { 1829 // Save failed, the controller will retry in the next sync 1830 strerr := fmt.Sprintf("Error saving claim: %v", err) 1831 logger.V(2).Info("Error saving claim", "PVC", klog.KObj(claim), "err", err) 1832 ctrl.eventRecorder.Event(claim, v1.EventTypeWarning, events.ProvisioningFailed, strerr) 1833 return provisionerName, err 1834 } 1835 claim = newClaim 1836 msg := fmt.Sprintf("Waiting for a volume to be created either by the external provisioner '%s' "+ 1837 "or manually by the system administrator. If volume creation is delayed, please verify that "+ 1838 "the provisioner is running and correctly registered.", provisionerName) 1839 // External provisioner has been requested for provisioning the volume 1840 // Report an event and wait for external provisioner to finish 1841 ctrl.eventRecorder.Event(claim, v1.EventTypeNormal, events.ExternalProvisioning, msg) 1842 logger.V(3).Info("provisionClaimOperationExternal provisioning claim", "PVC", klog.KObj(claim), "msg", msg) 1843 // return provisioner name here for metric reporting 1844 return provisionerName, nil 1845 } 1846 1847 // rescheduleProvisioning signal back to the scheduler to retry dynamic provisioning 1848 // by removing the AnnSelectedNode annotation 1849 func (ctrl *PersistentVolumeController) rescheduleProvisioning(ctx context.Context, claim *v1.PersistentVolumeClaim) { 1850 if _, ok := claim.Annotations[storagehelpers.AnnSelectedNode]; !ok { 1851 // Provisioning not triggered by the scheduler, skip 1852 return 1853 } 1854 1855 // The claim from method args can be pointing to watcher cache. We must not 1856 // modify these, therefore create a copy. 1857 newClaim := claim.DeepCopy() 1858 delete(newClaim.Annotations, storagehelpers.AnnSelectedNode) 1859 // Try to update the PVC object 1860 logger := klog.FromContext(ctx) 1861 if _, err := ctrl.kubeClient.CoreV1().PersistentVolumeClaims(newClaim.Namespace).Update(ctx, newClaim, metav1.UpdateOptions{}); err != nil { 1862 logger.V(4).Info("Failed to delete annotation 'storagehelpers.AnnSelectedNode' for PersistentVolumeClaim", "PVC", klog.KObj(newClaim), "err", err) 1863 return 1864 } 1865 if _, err := ctrl.storeClaimUpdate(logger, newClaim); err != nil { 1866 // We will get an "claim updated" event soon, this is not a big error 1867 logger.V(4).Info("Updating PersistentVolumeClaim: cannot update internal cache", "PVC", klog.KObj(newClaim), "err", err) 1868 } 1869 } 1870 1871 // getProvisionedVolumeNameForClaim returns PV.Name for the provisioned volume. 1872 // The name must be unique. 1873 func (ctrl *PersistentVolumeController) getProvisionedVolumeNameForClaim(claim *v1.PersistentVolumeClaim) string { 1874 return "pvc-" + string(claim.UID) 1875 } 1876 1877 // scheduleOperation starts given asynchronous operation on given volume. It 1878 // makes sure the operation is already not running. 1879 func (ctrl *PersistentVolumeController) scheduleOperation(logger klog.Logger, operationName string, operation func() error) { 1880 logger.V(4).Info("scheduleOperation", "operationName", operationName) 1881 1882 // Poke test code that an operation is just about to get started. 1883 if ctrl.preOperationHook != nil { 1884 ctrl.preOperationHook(operationName) 1885 } 1886 1887 err := ctrl.runningOperations.Run(operationName, operation) 1888 if err != nil { 1889 switch { 1890 case goroutinemap.IsAlreadyExists(err): 1891 logger.V(4).Info("Operation is already running, skipping", "operationName", operationName) 1892 case exponentialbackoff.IsExponentialBackoff(err): 1893 logger.V(4).Info("Operation postponed due to exponential backoff", "operationName", operationName) 1894 default: 1895 logger.Error(err, "Error scheduling operation", "operationName", operationName) 1896 } 1897 } 1898 } 1899 1900 // newRecyclerEventRecorder returns a RecycleEventRecorder that sends all events 1901 // to given volume. 1902 func (ctrl *PersistentVolumeController) newRecyclerEventRecorder(volume *v1.PersistentVolume) recyclerclient.RecycleEventRecorder { 1903 return func(eventtype, message string) { 1904 ctrl.eventRecorder.Eventf(volume, eventtype, events.RecyclerPod, "Recycler pod: %s", message) 1905 } 1906 } 1907 1908 // findProvisionablePlugin finds a provisioner plugin for a given claim. 1909 // It returns either the provisioning plugin or nil when an external 1910 // provisioner is requested. 1911 func (ctrl *PersistentVolumeController) findProvisionablePlugin(claim *v1.PersistentVolumeClaim) (vol.ProvisionableVolumePlugin, *storage.StorageClass, error) { 1912 // provisionClaim() which leads here is never called with claimClass=="", we 1913 // can save some checks. 1914 claimClass := storagehelpers.GetPersistentVolumeClaimClass(claim) 1915 class, err := ctrl.classLister.Get(claimClass) 1916 if err != nil { 1917 return nil, nil, err 1918 } 1919 1920 // Find a plugin for the class 1921 if ctrl.csiMigratedPluginManager.IsMigrationEnabledForPlugin(class.Provisioner) { 1922 // CSI migration scenario - do not depend on in-tree plugin 1923 return nil, class, nil 1924 } 1925 plugin, err := ctrl.volumePluginMgr.FindProvisionablePluginByName(class.Provisioner) 1926 if err != nil { 1927 if !strings.HasPrefix(class.Provisioner, "kubernetes.io/") { 1928 // External provisioner is requested, do not report error 1929 return nil, class, nil 1930 } 1931 return nil, class, err 1932 } 1933 return plugin, class, nil 1934 } 1935 1936 // findDeletablePlugin finds a deleter plugin for a given volume. It returns 1937 // either the deleter plugin or nil when an external deleter is requested. 1938 func (ctrl *PersistentVolumeController) findDeletablePlugin(volume *v1.PersistentVolume) (vol.DeletableVolumePlugin, error) { 1939 // Find a plugin. Try to find the same plugin that provisioned the volume 1940 var plugin vol.DeletableVolumePlugin 1941 if metav1.HasAnnotation(volume.ObjectMeta, storagehelpers.AnnDynamicallyProvisioned) { 1942 provisionPluginName := volume.Annotations[storagehelpers.AnnDynamicallyProvisioned] 1943 if provisionPluginName != "" { 1944 plugin, err := ctrl.volumePluginMgr.FindDeletablePluginByName(provisionPluginName) 1945 if err != nil { 1946 if !strings.HasPrefix(provisionPluginName, "kubernetes.io/") { 1947 // External provisioner is requested, do not report error 1948 return nil, nil 1949 } 1950 return nil, err 1951 } 1952 return plugin, nil 1953 } 1954 } 1955 1956 // The plugin that provisioned the volume was not found or the volume 1957 // was not dynamically provisioned. Try to find a plugin by spec. 1958 spec := vol.NewSpecFromPersistentVolume(volume, false) 1959 plugin, err := ctrl.volumePluginMgr.FindDeletablePluginBySpec(spec) 1960 if err != nil { 1961 // No deleter found. Emit an event and mark the volume Failed. 1962 return nil, fmt.Errorf("error getting deleter volume plugin for volume %q: %w", volume.Name, err) 1963 } 1964 return plugin, nil 1965 } 1966 1967 // obtain provisioner/deleter name for a volume 1968 func (ctrl *PersistentVolumeController) getProvisionerNameFromVolume(volume *v1.PersistentVolume) string { 1969 plugin, err := ctrl.findDeletablePlugin(volume) 1970 if err != nil { 1971 return "N/A" 1972 } 1973 if plugin != nil { 1974 return plugin.GetPluginName() 1975 } 1976 // If reached here, Either an external provisioner was used for provisioning 1977 // or a plugin has been migrated to CSI. 1978 // If an external provisioner was used, i.e., plugin == nil, instead of using 1979 // the AnnDynamicallyProvisioned annotation value, use the storageClass's Provisioner 1980 // field to avoid explosion of the metric in the cases like local storage provisioner 1981 // tagging a volume with arbitrary provisioner names 1982 storageClass := storagehelpers.GetPersistentVolumeClass(volume) 1983 class, err := ctrl.classLister.Get(storageClass) 1984 if err != nil { 1985 return "N/A" 1986 } 1987 if ctrl.csiMigratedPluginManager.IsMigrationEnabledForPlugin(class.Provisioner) { 1988 provisionerName, err := ctrl.translator.GetCSINameFromInTreeName(class.Provisioner) 1989 if err != nil { 1990 return "N/A" 1991 } 1992 return provisionerName 1993 } 1994 return class.Provisioner 1995 } 1996 1997 // obtain plugin/external provisioner name from plugin and storage class for timestamp logging purposes 1998 func (ctrl *PersistentVolumeController) getProvisionerName(plugin vol.ProvisionableVolumePlugin, storageClass *storage.StorageClass) string { 1999 // non CSI-migrated in-tree plugin, returns the plugin's name 2000 if plugin != nil { 2001 return plugin.GetPluginName() 2002 } 2003 if ctrl.csiMigratedPluginManager.IsMigrationEnabledForPlugin(storageClass.Provisioner) { 2004 // get the name of the CSI plugin that the in-tree storage class 2005 // provisioner has migrated to 2006 provisionerName, err := ctrl.translator.GetCSINameFromInTreeName(storageClass.Provisioner) 2007 if err != nil { 2008 return "N/A" 2009 } 2010 return provisionerName 2011 } 2012 return storageClass.Provisioner 2013 }