k8s.io/kubernetes@v1.29.3/pkg/controller/volume/attachdetach/attach_detach_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 attachdetach implements a controller to manage volume attach and detach 18 // operations. 19 package attachdetach 20 21 import ( 22 "context" 23 "fmt" 24 "net" 25 "time" 26 27 "k8s.io/klog/v2" 28 "k8s.io/mount-utils" 29 utilexec "k8s.io/utils/exec" 30 31 authenticationv1 "k8s.io/api/authentication/v1" 32 v1 "k8s.io/api/core/v1" 33 apierrors "k8s.io/apimachinery/pkg/api/errors" 34 "k8s.io/apimachinery/pkg/labels" 35 "k8s.io/apimachinery/pkg/types" 36 "k8s.io/apimachinery/pkg/util/runtime" 37 "k8s.io/apimachinery/pkg/util/wait" 38 utilfeature "k8s.io/apiserver/pkg/util/feature" 39 coreinformers "k8s.io/client-go/informers/core/v1" 40 storageinformersv1 "k8s.io/client-go/informers/storage/v1" 41 clientset "k8s.io/client-go/kubernetes" 42 "k8s.io/client-go/kubernetes/scheme" 43 v1core "k8s.io/client-go/kubernetes/typed/core/v1" 44 corelisters "k8s.io/client-go/listers/core/v1" 45 storagelistersv1 "k8s.io/client-go/listers/storage/v1" 46 kcache "k8s.io/client-go/tools/cache" 47 "k8s.io/client-go/tools/record" 48 "k8s.io/client-go/util/workqueue" 49 cloudprovider "k8s.io/cloud-provider" 50 csitrans "k8s.io/csi-translation-lib" 51 "k8s.io/kubernetes/pkg/controller/volume/attachdetach/cache" 52 "k8s.io/kubernetes/pkg/controller/volume/attachdetach/metrics" 53 "k8s.io/kubernetes/pkg/controller/volume/attachdetach/populator" 54 "k8s.io/kubernetes/pkg/controller/volume/attachdetach/reconciler" 55 "k8s.io/kubernetes/pkg/controller/volume/attachdetach/statusupdater" 56 "k8s.io/kubernetes/pkg/controller/volume/attachdetach/util" 57 "k8s.io/kubernetes/pkg/controller/volume/common" 58 "k8s.io/kubernetes/pkg/volume" 59 "k8s.io/kubernetes/pkg/volume/csi" 60 "k8s.io/kubernetes/pkg/volume/csimigration" 61 volumeutil "k8s.io/kubernetes/pkg/volume/util" 62 "k8s.io/kubernetes/pkg/volume/util/operationexecutor" 63 "k8s.io/kubernetes/pkg/volume/util/subpath" 64 "k8s.io/kubernetes/pkg/volume/util/volumepathhandler" 65 ) 66 67 // TimerConfig contains configuration of internal attach/detach timers and 68 // should be used only to speed up tests. DefaultTimerConfig is the suggested 69 // timer configuration for production. 70 type TimerConfig struct { 71 // ReconcilerLoopPeriod is the amount of time the reconciler loop waits 72 // between successive executions 73 ReconcilerLoopPeriod time.Duration 74 75 // ReconcilerMaxWaitForUnmountDuration is the maximum amount of time the 76 // attach detach controller will wait for a volume to be safely unmounted 77 // from its node. Once this time has expired, the controller will assume the 78 // node or kubelet are unresponsive and will detach the volume anyway. 79 ReconcilerMaxWaitForUnmountDuration time.Duration 80 81 // DesiredStateOfWorldPopulatorLoopSleepPeriod is the amount of time the 82 // DesiredStateOfWorldPopulator loop waits between successive executions 83 DesiredStateOfWorldPopulatorLoopSleepPeriod time.Duration 84 85 // DesiredStateOfWorldPopulatorListPodsRetryDuration is the amount of 86 // time the DesiredStateOfWorldPopulator loop waits between list pods 87 // calls. 88 DesiredStateOfWorldPopulatorListPodsRetryDuration time.Duration 89 } 90 91 // DefaultTimerConfig is the default configuration of Attach/Detach controller 92 // timers. 93 var DefaultTimerConfig = TimerConfig{ 94 ReconcilerLoopPeriod: 100 * time.Millisecond, 95 ReconcilerMaxWaitForUnmountDuration: 6 * time.Minute, 96 DesiredStateOfWorldPopulatorLoopSleepPeriod: 1 * time.Minute, 97 DesiredStateOfWorldPopulatorListPodsRetryDuration: 3 * time.Minute, 98 } 99 100 // AttachDetachController defines the operations supported by this controller. 101 type AttachDetachController interface { 102 Run(ctx context.Context) 103 GetDesiredStateOfWorld() cache.DesiredStateOfWorld 104 } 105 106 // NewAttachDetachController returns a new instance of AttachDetachController. 107 func NewAttachDetachController( 108 logger klog.Logger, 109 kubeClient clientset.Interface, 110 podInformer coreinformers.PodInformer, 111 nodeInformer coreinformers.NodeInformer, 112 pvcInformer coreinformers.PersistentVolumeClaimInformer, 113 pvInformer coreinformers.PersistentVolumeInformer, 114 csiNodeInformer storageinformersv1.CSINodeInformer, 115 csiDriverInformer storageinformersv1.CSIDriverInformer, 116 volumeAttachmentInformer storageinformersv1.VolumeAttachmentInformer, 117 cloud cloudprovider.Interface, 118 plugins []volume.VolumePlugin, 119 prober volume.DynamicPluginProber, 120 disableReconciliationSync bool, 121 reconcilerSyncDuration time.Duration, 122 timerConfig TimerConfig) (AttachDetachController, error) { 123 124 adc := &attachDetachController{ 125 kubeClient: kubeClient, 126 pvcLister: pvcInformer.Lister(), 127 pvcsSynced: pvcInformer.Informer().HasSynced, 128 pvLister: pvInformer.Lister(), 129 pvsSynced: pvInformer.Informer().HasSynced, 130 podLister: podInformer.Lister(), 131 podsSynced: podInformer.Informer().HasSynced, 132 podIndexer: podInformer.Informer().GetIndexer(), 133 nodeLister: nodeInformer.Lister(), 134 nodesSynced: nodeInformer.Informer().HasSynced, 135 cloud: cloud, 136 pvcQueue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "pvcs"), 137 } 138 139 adc.csiNodeLister = csiNodeInformer.Lister() 140 adc.csiNodeSynced = csiNodeInformer.Informer().HasSynced 141 142 adc.csiDriverLister = csiDriverInformer.Lister() 143 adc.csiDriversSynced = csiDriverInformer.Informer().HasSynced 144 145 adc.volumeAttachmentLister = volumeAttachmentInformer.Lister() 146 adc.volumeAttachmentSynced = volumeAttachmentInformer.Informer().HasSynced 147 148 if err := adc.volumePluginMgr.InitPlugins(plugins, prober, adc); err != nil { 149 return nil, fmt.Errorf("could not initialize volume plugins for Attach/Detach Controller: %w", err) 150 } 151 152 adc.broadcaster = record.NewBroadcaster() 153 recorder := adc.broadcaster.NewRecorder(scheme.Scheme, v1.EventSource{Component: "attachdetach-controller"}) 154 blkutil := volumepathhandler.NewBlockVolumePathHandler() 155 156 adc.desiredStateOfWorld = cache.NewDesiredStateOfWorld(&adc.volumePluginMgr) 157 adc.actualStateOfWorld = cache.NewActualStateOfWorld(&adc.volumePluginMgr) 158 adc.attacherDetacher = 159 operationexecutor.NewOperationExecutor(operationexecutor.NewOperationGenerator( 160 kubeClient, 161 &adc.volumePluginMgr, 162 recorder, 163 blkutil)) 164 adc.nodeStatusUpdater = statusupdater.NewNodeStatusUpdater( 165 kubeClient, nodeInformer.Lister(), adc.actualStateOfWorld) 166 167 // Default these to values in options 168 adc.reconciler = reconciler.NewReconciler( 169 timerConfig.ReconcilerLoopPeriod, 170 timerConfig.ReconcilerMaxWaitForUnmountDuration, 171 reconcilerSyncDuration, 172 disableReconciliationSync, 173 adc.desiredStateOfWorld, 174 adc.actualStateOfWorld, 175 adc.attacherDetacher, 176 adc.nodeStatusUpdater, 177 adc.nodeLister, 178 recorder) 179 180 csiTranslator := csitrans.New() 181 adc.intreeToCSITranslator = csiTranslator 182 adc.csiMigratedPluginManager = csimigration.NewPluginManager(csiTranslator, utilfeature.DefaultFeatureGate) 183 184 adc.desiredStateOfWorldPopulator = populator.NewDesiredStateOfWorldPopulator( 185 timerConfig.DesiredStateOfWorldPopulatorLoopSleepPeriod, 186 timerConfig.DesiredStateOfWorldPopulatorListPodsRetryDuration, 187 podInformer.Lister(), 188 adc.desiredStateOfWorld, 189 &adc.volumePluginMgr, 190 pvcInformer.Lister(), 191 pvInformer.Lister(), 192 adc.csiMigratedPluginManager, 193 adc.intreeToCSITranslator) 194 195 podInformer.Informer().AddEventHandler(kcache.ResourceEventHandlerFuncs{ 196 AddFunc: func(obj interface{}) { 197 adc.podAdd(logger, obj) 198 }, 199 UpdateFunc: func(oldObj, newObj interface{}) { 200 adc.podUpdate(logger, oldObj, newObj) 201 }, 202 DeleteFunc: func(obj interface{}) { 203 adc.podDelete(logger, obj) 204 }, 205 }) 206 207 // This custom indexer will index pods by its PVC keys. Then we don't need 208 // to iterate all pods every time to find pods which reference given PVC. 209 if err := common.AddPodPVCIndexerIfNotPresent(adc.podIndexer); err != nil { 210 return nil, fmt.Errorf("could not initialize attach detach controller: %w", err) 211 } 212 213 nodeInformer.Informer().AddEventHandler(kcache.ResourceEventHandlerFuncs{ 214 AddFunc: func(obj interface{}) { 215 adc.nodeAdd(logger, obj) 216 }, 217 UpdateFunc: func(oldObj, newObj interface{}) { 218 adc.nodeUpdate(logger, oldObj, newObj) 219 }, 220 DeleteFunc: func(obj interface{}) { 221 adc.nodeDelete(logger, obj) 222 }, 223 }) 224 225 pvcInformer.Informer().AddEventHandler(kcache.ResourceEventHandlerFuncs{ 226 AddFunc: func(obj interface{}) { 227 adc.enqueuePVC(obj) 228 }, 229 UpdateFunc: func(old, new interface{}) { 230 adc.enqueuePVC(new) 231 }, 232 }) 233 234 return adc, nil 235 } 236 237 type attachDetachController struct { 238 // kubeClient is the kube API client used by volumehost to communicate with 239 // the API server. 240 kubeClient clientset.Interface 241 242 // pvcLister is the shared PVC lister used to fetch and store PVC 243 // objects from the API server. It is shared with other controllers and 244 // therefore the PVC objects in its store should be treated as immutable. 245 pvcLister corelisters.PersistentVolumeClaimLister 246 pvcsSynced kcache.InformerSynced 247 248 // pvLister is the shared PV lister used to fetch and store PV objects 249 // from the API server. It is shared with other controllers and therefore 250 // the PV objects in its store should be treated as immutable. 251 pvLister corelisters.PersistentVolumeLister 252 pvsSynced kcache.InformerSynced 253 254 podLister corelisters.PodLister 255 podsSynced kcache.InformerSynced 256 podIndexer kcache.Indexer 257 258 nodeLister corelisters.NodeLister 259 nodesSynced kcache.InformerSynced 260 261 csiNodeLister storagelistersv1.CSINodeLister 262 csiNodeSynced kcache.InformerSynced 263 264 // csiDriverLister is the shared CSIDriver lister used to fetch and store 265 // CSIDriver objects from the API server. It is shared with other controllers 266 // and therefore the CSIDriver objects in its store should be treated as immutable. 267 csiDriverLister storagelistersv1.CSIDriverLister 268 csiDriversSynced kcache.InformerSynced 269 270 // volumeAttachmentLister is the shared volumeAttachment lister used to fetch and store 271 // VolumeAttachment objects from the API server. It is shared with other controllers 272 // and therefore the VolumeAttachment objects in its store should be treated as immutable. 273 volumeAttachmentLister storagelistersv1.VolumeAttachmentLister 274 volumeAttachmentSynced kcache.InformerSynced 275 276 // cloud provider used by volume host 277 cloud cloudprovider.Interface 278 279 // volumePluginMgr used to initialize and fetch volume plugins 280 volumePluginMgr volume.VolumePluginMgr 281 282 // desiredStateOfWorld is a data structure containing the desired state of 283 // the world according to this controller: i.e. what nodes the controller 284 // is managing, what volumes it wants be attached to these nodes, and which 285 // pods are scheduled to those nodes referencing the volumes. 286 // The data structure is populated by the controller using a stream of node 287 // and pod API server objects fetched by the informers. 288 desiredStateOfWorld cache.DesiredStateOfWorld 289 290 // actualStateOfWorld is a data structure containing the actual state of 291 // the world according to this controller: i.e. which volumes are attached 292 // to which nodes. 293 // The data structure is populated upon successful completion of attach and 294 // detach actions triggered by the controller and a periodic sync with 295 // storage providers for the "true" state of the world. 296 actualStateOfWorld cache.ActualStateOfWorld 297 298 // attacherDetacher is used to start asynchronous attach and operations 299 attacherDetacher operationexecutor.OperationExecutor 300 301 // reconciler is used to run an asynchronous periodic loop to reconcile the 302 // desiredStateOfWorld with the actualStateOfWorld by triggering attach 303 // detach operations using the attacherDetacher. 304 reconciler reconciler.Reconciler 305 306 // nodeStatusUpdater is used to update node status with the list of attached 307 // volumes 308 nodeStatusUpdater statusupdater.NodeStatusUpdater 309 310 // desiredStateOfWorldPopulator runs an asynchronous periodic loop to 311 // populate the current pods using podInformer. 312 desiredStateOfWorldPopulator populator.DesiredStateOfWorldPopulator 313 314 // broadcaster is broadcasting events 315 broadcaster record.EventBroadcaster 316 317 // pvcQueue is used to queue pvc objects 318 pvcQueue workqueue.RateLimitingInterface 319 320 // csiMigratedPluginManager detects in-tree plugins that have been migrated to CSI 321 csiMigratedPluginManager csimigration.PluginManager 322 323 // intreeToCSITranslator translates from in-tree volume specs to CSI 324 intreeToCSITranslator csimigration.InTreeToCSITranslator 325 } 326 327 func (adc *attachDetachController) Run(ctx context.Context) { 328 defer runtime.HandleCrash() 329 defer adc.pvcQueue.ShutDown() 330 331 // Start events processing pipeline. 332 adc.broadcaster.StartStructuredLogging(0) 333 adc.broadcaster.StartRecordingToSink(&v1core.EventSinkImpl{Interface: adc.kubeClient.CoreV1().Events("")}) 334 defer adc.broadcaster.Shutdown() 335 336 logger := klog.FromContext(ctx) 337 logger.Info("Starting attach detach controller") 338 defer logger.Info("Shutting down attach detach controller") 339 340 synced := []kcache.InformerSynced{adc.podsSynced, adc.nodesSynced, adc.pvcsSynced, adc.pvsSynced} 341 if adc.csiNodeSynced != nil { 342 synced = append(synced, adc.csiNodeSynced) 343 } 344 if adc.csiDriversSynced != nil { 345 synced = append(synced, adc.csiDriversSynced) 346 } 347 if adc.volumeAttachmentSynced != nil { 348 synced = append(synced, adc.volumeAttachmentSynced) 349 } 350 351 if !kcache.WaitForNamedCacheSync("attach detach", ctx.Done(), synced...) { 352 return 353 } 354 355 err := adc.populateActualStateOfWorld(logger) 356 if err != nil { 357 logger.Error(err, "Error populating the actual state of world") 358 } 359 err = adc.populateDesiredStateOfWorld(logger) 360 if err != nil { 361 logger.Error(err, "Error populating the desired state of world") 362 } 363 go adc.reconciler.Run(ctx) 364 go adc.desiredStateOfWorldPopulator.Run(ctx) 365 go wait.UntilWithContext(ctx, adc.pvcWorker, time.Second) 366 metrics.Register(adc.pvcLister, 367 adc.pvLister, 368 adc.podLister, 369 adc.actualStateOfWorld, 370 adc.desiredStateOfWorld, 371 &adc.volumePluginMgr, 372 adc.csiMigratedPluginManager, 373 adc.intreeToCSITranslator) 374 375 <-ctx.Done() 376 } 377 378 func (adc *attachDetachController) populateActualStateOfWorld(logger klog.Logger) error { 379 logger.V(5).Info("Populating ActualStateOfworld") 380 nodes, err := adc.nodeLister.List(labels.Everything()) 381 if err != nil { 382 return err 383 } 384 385 for _, node := range nodes { 386 nodeName := types.NodeName(node.Name) 387 for _, attachedVolume := range node.Status.VolumesAttached { 388 uniqueName := attachedVolume.Name 389 // The nil VolumeSpec is safe only in the case the volume is not in use by any pod. 390 // In such a case it should be detached in the first reconciliation cycle and the 391 // volume spec is not needed to detach a volume. If the volume is used by a pod, it 392 // its spec can be: this would happen during in the populateDesiredStateOfWorld which 393 // scans the pods and updates their volumes in the ActualStateOfWorld too. 394 err = adc.actualStateOfWorld.MarkVolumeAsAttached(logger, uniqueName, nil /* VolumeSpec */, nodeName, attachedVolume.DevicePath) 395 if err != nil { 396 logger.Error(err, "Failed to mark the volume as attached") 397 continue 398 } 399 adc.processVolumesInUse(logger, nodeName, node.Status.VolumesInUse) 400 adc.addNodeToDswp(node, types.NodeName(node.Name)) 401 } 402 } 403 err = adc.processVolumeAttachments(logger) 404 if err != nil { 405 logger.Error(err, "Failed to process volume attachments") 406 } 407 return err 408 } 409 410 func (adc *attachDetachController) getNodeVolumeDevicePath( 411 volumeName v1.UniqueVolumeName, nodeName types.NodeName) (string, error) { 412 var devicePath string 413 var found bool 414 node, err := adc.nodeLister.Get(string(nodeName)) 415 if err != nil { 416 return devicePath, err 417 } 418 for _, attachedVolume := range node.Status.VolumesAttached { 419 if volumeName == attachedVolume.Name { 420 devicePath = attachedVolume.DevicePath 421 found = true 422 break 423 } 424 } 425 if !found { 426 err = fmt.Errorf("Volume %s not found on node %s", volumeName, nodeName) 427 } 428 429 return devicePath, err 430 } 431 432 func (adc *attachDetachController) populateDesiredStateOfWorld(logger klog.Logger) error { 433 logger.V(5).Info("Populating DesiredStateOfworld") 434 435 pods, err := adc.podLister.List(labels.Everything()) 436 if err != nil { 437 return err 438 } 439 for _, pod := range pods { 440 podToAdd := pod 441 adc.podAdd(logger, podToAdd) 442 for _, podVolume := range podToAdd.Spec.Volumes { 443 nodeName := types.NodeName(podToAdd.Spec.NodeName) 444 // The volume specs present in the ActualStateOfWorld are nil, let's replace those 445 // with the correct ones found on pods. The present in the ASW with no corresponding 446 // pod will be detached and the spec is irrelevant. 447 volumeSpec, err := util.CreateVolumeSpec(logger, podVolume, podToAdd, nodeName, &adc.volumePluginMgr, adc.pvcLister, adc.pvLister, adc.csiMigratedPluginManager, adc.intreeToCSITranslator) 448 if err != nil { 449 logger.Error( 450 err, 451 "Error creating spec for volume of pod", 452 "pod", klog.KObj(podToAdd), 453 "volumeName", podVolume.Name) 454 continue 455 } 456 plugin, err := adc.volumePluginMgr.FindAttachablePluginBySpec(volumeSpec) 457 if err != nil || plugin == nil { 458 logger.V(10).Info( 459 "Skipping volume for pod: it does not implement attacher interface", 460 "pod", klog.KObj(podToAdd), 461 "volumeName", podVolume.Name, 462 "err", err) 463 continue 464 } 465 volumeName, err := volumeutil.GetUniqueVolumeNameFromSpec(plugin, volumeSpec) 466 if err != nil { 467 logger.Error( 468 err, 469 "Failed to find unique name for volume of pod", 470 "pod", klog.KObj(podToAdd), 471 "volumeName", podVolume.Name) 472 continue 473 } 474 attachState := adc.actualStateOfWorld.GetAttachState(volumeName, nodeName) 475 if attachState == cache.AttachStateAttached { 476 logger.V(10).Info("Volume is attached to node. Marking as attached in ActualStateOfWorld", 477 "node", klog.KRef("", string(nodeName)), 478 "volumeName", volumeName) 479 devicePath, err := adc.getNodeVolumeDevicePath(volumeName, nodeName) 480 if err != nil { 481 logger.Error(err, "Failed to find device path") 482 continue 483 } 484 err = adc.actualStateOfWorld.MarkVolumeAsAttached(logger, volumeName, volumeSpec, nodeName, devicePath) 485 if err != nil { 486 logger.Error(err, "Failed to update volume spec for node", "node", klog.KRef("", string(nodeName))) 487 } 488 } 489 } 490 } 491 492 return nil 493 } 494 495 func (adc *attachDetachController) podAdd(logger klog.Logger, obj interface{}) { 496 pod, ok := obj.(*v1.Pod) 497 if pod == nil || !ok { 498 return 499 } 500 if pod.Spec.NodeName == "" { 501 // Ignore pods without NodeName, indicating they are not scheduled. 502 return 503 } 504 505 volumeActionFlag := util.DetermineVolumeAction( 506 pod, 507 adc.desiredStateOfWorld, 508 true /* default volume action */) 509 510 util.ProcessPodVolumes(logger, pod, volumeActionFlag, /* addVolumes */ 511 adc.desiredStateOfWorld, &adc.volumePluginMgr, adc.pvcLister, adc.pvLister, adc.csiMigratedPluginManager, adc.intreeToCSITranslator) 512 } 513 514 // GetDesiredStateOfWorld returns desired state of world associated with controller 515 func (adc *attachDetachController) GetDesiredStateOfWorld() cache.DesiredStateOfWorld { 516 return adc.desiredStateOfWorld 517 } 518 519 func (adc *attachDetachController) podUpdate(logger klog.Logger, oldObj, newObj interface{}) { 520 pod, ok := newObj.(*v1.Pod) 521 if pod == nil || !ok { 522 return 523 } 524 if pod.Spec.NodeName == "" { 525 // Ignore pods without NodeName, indicating they are not scheduled. 526 return 527 } 528 529 volumeActionFlag := util.DetermineVolumeAction( 530 pod, 531 adc.desiredStateOfWorld, 532 true /* default volume action */) 533 534 util.ProcessPodVolumes(logger, pod, volumeActionFlag, /* addVolumes */ 535 adc.desiredStateOfWorld, &adc.volumePluginMgr, adc.pvcLister, adc.pvLister, adc.csiMigratedPluginManager, adc.intreeToCSITranslator) 536 } 537 538 func (adc *attachDetachController) podDelete(logger klog.Logger, obj interface{}) { 539 pod, ok := obj.(*v1.Pod) 540 if pod == nil || !ok { 541 return 542 } 543 544 util.ProcessPodVolumes(logger, pod, false, /* addVolumes */ 545 adc.desiredStateOfWorld, &adc.volumePluginMgr, adc.pvcLister, adc.pvLister, adc.csiMigratedPluginManager, adc.intreeToCSITranslator) 546 } 547 548 func (adc *attachDetachController) nodeAdd(logger klog.Logger, obj interface{}) { 549 node, ok := obj.(*v1.Node) 550 // TODO: investigate if nodeName is empty then if we can return 551 // kubernetes/kubernetes/issues/37777 552 if node == nil || !ok { 553 return 554 } 555 nodeName := types.NodeName(node.Name) 556 adc.nodeUpdate(logger, nil, obj) 557 // kubernetes/kubernetes/issues/37586 558 // This is to workaround the case when a node add causes to wipe out 559 // the attached volumes field. This function ensures that we sync with 560 // the actual status. 561 adc.actualStateOfWorld.SetNodeStatusUpdateNeeded(logger, nodeName) 562 } 563 564 func (adc *attachDetachController) nodeUpdate(logger klog.Logger, oldObj, newObj interface{}) { 565 node, ok := newObj.(*v1.Node) 566 // TODO: investigate if nodeName is empty then if we can return 567 if node == nil || !ok { 568 return 569 } 570 571 nodeName := types.NodeName(node.Name) 572 adc.addNodeToDswp(node, nodeName) 573 adc.processVolumesInUse(logger, nodeName, node.Status.VolumesInUse) 574 } 575 576 func (adc *attachDetachController) nodeDelete(logger klog.Logger, obj interface{}) { 577 node, ok := obj.(*v1.Node) 578 if node == nil || !ok { 579 return 580 } 581 582 nodeName := types.NodeName(node.Name) 583 if err := adc.desiredStateOfWorld.DeleteNode(nodeName); err != nil { 584 // This might happen during drain, but we still want it to appear in our logs 585 logger.Info("Error removing node from desired-state-of-world", "node", klog.KObj(node), "err", err) 586 } 587 588 adc.processVolumesInUse(logger, nodeName, node.Status.VolumesInUse) 589 } 590 591 func (adc *attachDetachController) enqueuePVC(obj interface{}) { 592 key, err := kcache.DeletionHandlingMetaNamespaceKeyFunc(obj) 593 if err != nil { 594 runtime.HandleError(fmt.Errorf("Couldn't get key for object %+v: %v", obj, err)) 595 return 596 } 597 adc.pvcQueue.Add(key) 598 } 599 600 // pvcWorker processes items from pvcQueue 601 func (adc *attachDetachController) pvcWorker(ctx context.Context) { 602 for adc.processNextItem(klog.FromContext(ctx)) { 603 } 604 } 605 606 func (adc *attachDetachController) processNextItem(logger klog.Logger) bool { 607 keyObj, shutdown := adc.pvcQueue.Get() 608 if shutdown { 609 return false 610 } 611 defer adc.pvcQueue.Done(keyObj) 612 613 if err := adc.syncPVCByKey(logger, keyObj.(string)); err != nil { 614 // Rather than wait for a full resync, re-add the key to the 615 // queue to be processed. 616 adc.pvcQueue.AddRateLimited(keyObj) 617 runtime.HandleError(fmt.Errorf("Failed to sync pvc %q, will retry again: %v", keyObj.(string), err)) 618 return true 619 } 620 621 // Finally, if no error occurs we Forget this item so it does not 622 // get queued again until another change happens. 623 adc.pvcQueue.Forget(keyObj) 624 return true 625 } 626 627 func (adc *attachDetachController) syncPVCByKey(logger klog.Logger, key string) error { 628 logger.V(5).Info("syncPVCByKey", "pvcKey", key) 629 namespace, name, err := kcache.SplitMetaNamespaceKey(key) 630 if err != nil { 631 logger.V(4).Info("Error getting namespace & name of pvc to get pvc from informer", "pvcKey", key, "err", err) 632 return nil 633 } 634 pvc, err := adc.pvcLister.PersistentVolumeClaims(namespace).Get(name) 635 if apierrors.IsNotFound(err) { 636 logger.V(4).Info("Error getting pvc from informer", "pvcKey", key, "err", err) 637 return nil 638 } 639 if err != nil { 640 return err 641 } 642 643 if pvc.Status.Phase != v1.ClaimBound || pvc.Spec.VolumeName == "" { 644 // Skip unbound PVCs. 645 return nil 646 } 647 648 objs, err := adc.podIndexer.ByIndex(common.PodPVCIndex, key) 649 if err != nil { 650 return err 651 } 652 for _, obj := range objs { 653 pod, ok := obj.(*v1.Pod) 654 if !ok { 655 continue 656 } 657 // we are only interested in active pods with nodeName set 658 if len(pod.Spec.NodeName) == 0 || volumeutil.IsPodTerminated(pod, pod.Status) { 659 continue 660 } 661 volumeActionFlag := util.DetermineVolumeAction( 662 pod, 663 adc.desiredStateOfWorld, 664 true /* default volume action */) 665 666 util.ProcessPodVolumes(logger, pod, volumeActionFlag, /* addVolumes */ 667 adc.desiredStateOfWorld, &adc.volumePluginMgr, adc.pvcLister, adc.pvLister, adc.csiMigratedPluginManager, adc.intreeToCSITranslator) 668 } 669 return nil 670 } 671 672 // processVolumesInUse processes the list of volumes marked as "in-use" 673 // according to the specified Node's Status.VolumesInUse and updates the 674 // corresponding volume in the actual state of the world to indicate that it is 675 // mounted. 676 func (adc *attachDetachController) processVolumesInUse( 677 logger klog.Logger, nodeName types.NodeName, volumesInUse []v1.UniqueVolumeName) { 678 logger.V(4).Info("processVolumesInUse for node", "node", klog.KRef("", string(nodeName))) 679 for _, attachedVolume := range adc.actualStateOfWorld.GetAttachedVolumesForNode(nodeName) { 680 mounted := false 681 for _, volumeInUse := range volumesInUse { 682 if attachedVolume.VolumeName == volumeInUse { 683 mounted = true 684 break 685 } 686 } 687 err := adc.actualStateOfWorld.SetVolumeMountedByNode(logger, attachedVolume.VolumeName, nodeName, mounted) 688 if err != nil { 689 logger.Info( 690 "SetVolumeMountedByNode returned an error", 691 "node", klog.KRef("", string(nodeName)), 692 "volumeName", attachedVolume.VolumeName, 693 "mounted", mounted, 694 "err", err) 695 } 696 } 697 } 698 699 // Process Volume-Attachment objects. 700 // Should be called only after populating attached volumes in the ASW. 701 // For each VA object, this function checks if its present in the ASW. 702 // If not, adds the volume to ASW as an "uncertain" attachment. 703 // In the reconciler, the logic checks if the volume is present in the DSW; 704 // 705 // if yes, the reconciler will attempt attach on the volume; 706 // if not (could be a dangling attachment), the reconciler will detach this volume. 707 func (adc *attachDetachController) processVolumeAttachments(logger klog.Logger) error { 708 vas, err := adc.volumeAttachmentLister.List(labels.Everything()) 709 if err != nil { 710 logger.Error(err, "Failed to list VolumeAttachment objects") 711 return err 712 } 713 for _, va := range vas { 714 nodeName := types.NodeName(va.Spec.NodeName) 715 pvName := va.Spec.Source.PersistentVolumeName 716 if pvName == nil { 717 // Currently VA objects are created for CSI volumes only. nil pvName is unexpected, generate a warning 718 logger.Info("Skipping the va as its pvName is nil", "node", klog.KRef("", string(nodeName)), "vaName", va.Name) 719 continue 720 } 721 pv, err := adc.pvLister.Get(*pvName) 722 if err != nil { 723 logger.Error(err, "Unable to lookup pv object", "PV", klog.KRef("", *pvName)) 724 continue 725 } 726 727 var plugin volume.AttachableVolumePlugin 728 volumeSpec := volume.NewSpecFromPersistentVolume(pv, false) 729 730 // Consult csiMigratedPluginManager first before querying the plugins registered during runtime in volumePluginMgr. 731 // In-tree plugins that provisioned PVs will not be registered anymore after migration to CSI, once the respective 732 // feature gate is enabled. 733 if inTreePluginName, err := adc.csiMigratedPluginManager.GetInTreePluginNameFromSpec(pv, nil); err == nil { 734 if adc.csiMigratedPluginManager.IsMigrationEnabledForPlugin(inTreePluginName) { 735 // PV is migrated and should be handled by the CSI plugin instead of the in-tree one 736 plugin, _ = adc.volumePluginMgr.FindAttachablePluginByName(csi.CSIPluginName) 737 // podNamespace is not needed here for Azurefile as the volumeName generated will be the same with or without podNamespace 738 volumeSpec, err = csimigration.TranslateInTreeSpecToCSI(volumeSpec, "" /* podNamespace */, adc.intreeToCSITranslator) 739 if err != nil { 740 logger.Error(err, "Failed to translate intree volumeSpec to CSI volumeSpec for volume", "node", klog.KRef("", string(nodeName)), "inTreePluginName", inTreePluginName, "vaName", va.Name, "PV", klog.KRef("", *pvName)) 741 continue 742 } 743 } 744 } 745 746 if plugin == nil { 747 plugin, err = adc.volumePluginMgr.FindAttachablePluginBySpec(volumeSpec) 748 if err != nil || plugin == nil { 749 // Currently VA objects are created for CSI volumes only. nil plugin is unexpected, generate a warning 750 logger.Info("Skipping processing the volume on node, no attacher interface found", "node", klog.KRef("", string(nodeName)), "PV", klog.KRef("", *pvName), "err", err) 751 continue 752 } 753 } 754 755 volumeName, err := volumeutil.GetUniqueVolumeNameFromSpec(plugin, volumeSpec) 756 if err != nil { 757 logger.Error(err, "Failed to find unique name for volume", "node", klog.KRef("", string(nodeName)), "vaName", va.Name, "PV", klog.KRef("", *pvName)) 758 continue 759 } 760 attachState := adc.actualStateOfWorld.GetAttachState(volumeName, nodeName) 761 if attachState == cache.AttachStateDetached { 762 logger.V(1).Info("Marking volume attachment as uncertain as volume is not attached", "node", klog.KRef("", string(nodeName)), "volumeName", volumeName, "attachState", attachState) 763 err = adc.actualStateOfWorld.MarkVolumeAsUncertain(logger, volumeName, volumeSpec, nodeName) 764 if err != nil { 765 logger.Error(err, "MarkVolumeAsUncertain fail to add the volume to ASW", "node", klog.KRef("", string(nodeName)), "volumeName", volumeName) 766 } 767 } 768 } 769 return nil 770 } 771 772 var _ volume.VolumeHost = &attachDetachController{} 773 var _ volume.AttachDetachVolumeHost = &attachDetachController{} 774 775 func (adc *attachDetachController) CSINodeLister() storagelistersv1.CSINodeLister { 776 return adc.csiNodeLister 777 } 778 779 func (adc *attachDetachController) CSIDriverLister() storagelistersv1.CSIDriverLister { 780 return adc.csiDriverLister 781 } 782 783 func (adc *attachDetachController) IsAttachDetachController() bool { 784 return true 785 } 786 787 func (adc *attachDetachController) VolumeAttachmentLister() storagelistersv1.VolumeAttachmentLister { 788 return adc.volumeAttachmentLister 789 } 790 791 // VolumeHost implementation 792 // This is an unfortunate requirement of the current factoring of volume plugin 793 // initializing code. It requires kubelet specific methods used by the mounting 794 // code to be implemented by all initializers even if the initializer does not 795 // do mounting (like this attach/detach controller). 796 // Issue kubernetes/kubernetes/issues/14217 to fix this. 797 func (adc *attachDetachController) GetPluginDir(podUID string) string { 798 return "" 799 } 800 801 func (adc *attachDetachController) GetVolumeDevicePluginDir(podUID string) string { 802 return "" 803 } 804 805 func (adc *attachDetachController) GetPodsDir() string { 806 return "" 807 } 808 809 func (adc *attachDetachController) GetPodVolumeDir(podUID types.UID, pluginName, volumeName string) string { 810 return "" 811 } 812 813 func (adc *attachDetachController) GetPodPluginDir(podUID types.UID, pluginName string) string { 814 return "" 815 } 816 817 func (adc *attachDetachController) GetPodVolumeDeviceDir(podUID types.UID, pluginName string) string { 818 return "" 819 } 820 821 func (adc *attachDetachController) GetKubeClient() clientset.Interface { 822 return adc.kubeClient 823 } 824 825 func (adc *attachDetachController) NewWrapperMounter(volName string, spec volume.Spec, pod *v1.Pod, opts volume.VolumeOptions) (volume.Mounter, error) { 826 return nil, fmt.Errorf("NewWrapperMounter not supported by Attach/Detach controller's VolumeHost implementation") 827 } 828 829 func (adc *attachDetachController) NewWrapperUnmounter(volName string, spec volume.Spec, podUID types.UID) (volume.Unmounter, error) { 830 return nil, fmt.Errorf("NewWrapperUnmounter not supported by Attach/Detach controller's VolumeHost implementation") 831 } 832 833 func (adc *attachDetachController) GetCloudProvider() cloudprovider.Interface { 834 return adc.cloud 835 } 836 837 func (adc *attachDetachController) GetMounter(pluginName string) mount.Interface { 838 return nil 839 } 840 841 func (adc *attachDetachController) GetHostName() string { 842 return "" 843 } 844 845 func (adc *attachDetachController) GetHostIP() (net.IP, error) { 846 return nil, fmt.Errorf("GetHostIP() not supported by Attach/Detach controller's VolumeHost implementation") 847 } 848 849 func (adc *attachDetachController) GetNodeAllocatable() (v1.ResourceList, error) { 850 return v1.ResourceList{}, nil 851 } 852 853 func (adc *attachDetachController) GetAttachedVolumesFromNodeStatus() (map[v1.UniqueVolumeName]string, error) { 854 return map[v1.UniqueVolumeName]string{}, nil 855 } 856 857 func (adc *attachDetachController) GetSecretFunc() func(namespace, name string) (*v1.Secret, error) { 858 return func(_, _ string) (*v1.Secret, error) { 859 return nil, fmt.Errorf("GetSecret unsupported in attachDetachController") 860 } 861 } 862 863 func (adc *attachDetachController) GetConfigMapFunc() func(namespace, name string) (*v1.ConfigMap, error) { 864 return func(_, _ string) (*v1.ConfigMap, error) { 865 return nil, fmt.Errorf("GetConfigMap unsupported in attachDetachController") 866 } 867 } 868 869 func (adc *attachDetachController) GetServiceAccountTokenFunc() func(_, _ string, _ *authenticationv1.TokenRequest) (*authenticationv1.TokenRequest, error) { 870 return func(_, _ string, _ *authenticationv1.TokenRequest) (*authenticationv1.TokenRequest, error) { 871 return nil, fmt.Errorf("GetServiceAccountToken unsupported in attachDetachController") 872 } 873 } 874 875 func (adc *attachDetachController) DeleteServiceAccountTokenFunc() func(types.UID) { 876 return func(types.UID) { 877 // nolint:logcheck 878 klog.ErrorS(nil, "DeleteServiceAccountToken unsupported in attachDetachController") 879 } 880 } 881 882 func (adc *attachDetachController) GetExec(pluginName string) utilexec.Interface { 883 return utilexec.New() 884 } 885 886 func (adc *attachDetachController) addNodeToDswp(node *v1.Node, nodeName types.NodeName) { 887 if _, exists := node.Annotations[volumeutil.ControllerManagedAttachAnnotation]; exists { 888 keepTerminatedPodVolumes := false 889 890 if t, ok := node.Annotations[volumeutil.KeepTerminatedPodVolumesAnnotation]; ok { 891 keepTerminatedPodVolumes = t == "true" 892 } 893 894 // Node specifies annotation indicating it should be managed by attach 895 // detach controller. Add it to desired state of world. 896 adc.desiredStateOfWorld.AddNode(nodeName, keepTerminatedPodVolumes) 897 } 898 } 899 900 func (adc *attachDetachController) GetNodeLabels() (map[string]string, error) { 901 return nil, fmt.Errorf("GetNodeLabels() unsupported in Attach/Detach controller") 902 } 903 904 func (adc *attachDetachController) GetNodeName() types.NodeName { 905 return "" 906 } 907 908 func (adc *attachDetachController) GetEventRecorder() record.EventRecorder { 909 return nil 910 } 911 912 func (adc *attachDetachController) GetSubpather() subpath.Interface { 913 // Subpaths not needed in attachdetach controller 914 return nil 915 } 916 917 func (adc *attachDetachController) GetCSIDriverLister() storagelistersv1.CSIDriverLister { 918 return adc.csiDriverLister 919 }