k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/pkg/volume/util/operationexecutor/operation_generator.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 operationexecutor 18 19 import ( 20 "context" 21 goerrors "errors" 22 "fmt" 23 "os" 24 "path/filepath" 25 "strings" 26 "time" 27 28 "k8s.io/apimachinery/pkg/api/resource" 29 30 v1 "k8s.io/api/core/v1" 31 "k8s.io/apimachinery/pkg/api/errors" 32 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 33 "k8s.io/apimachinery/pkg/types" 34 utilfeature "k8s.io/apiserver/pkg/util/feature" 35 clientset "k8s.io/client-go/kubernetes" 36 "k8s.io/client-go/tools/record" 37 volerr "k8s.io/cloud-provider/volume/errors" 38 storagehelpers "k8s.io/component-helpers/storage/volume" 39 csitrans "k8s.io/csi-translation-lib" 40 "k8s.io/klog/v2" 41 v1helper "k8s.io/kubernetes/pkg/apis/core/v1/helper" 42 "k8s.io/kubernetes/pkg/features" 43 kevents "k8s.io/kubernetes/pkg/kubelet/events" 44 "k8s.io/kubernetes/pkg/volume" 45 "k8s.io/kubernetes/pkg/volume/util" 46 "k8s.io/kubernetes/pkg/volume/util/hostutil" 47 volumetypes "k8s.io/kubernetes/pkg/volume/util/types" 48 "k8s.io/kubernetes/pkg/volume/util/volumepathhandler" 49 ) 50 51 const ( 52 unknownVolumePlugin string = "UnknownVolumePlugin" 53 unknownAttachableVolumePlugin string = "UnknownAttachableVolumePlugin" 54 DetachOperationName string = "volume_detach" 55 VerifyControllerAttachedVolumeOpName string = "verify_controller_attached_volume" 56 ) 57 58 // InTreeToCSITranslator contains methods required to check migratable status 59 // and perform translations from InTree PVs and Inline to CSI 60 type InTreeToCSITranslator interface { 61 IsPVMigratable(pv *v1.PersistentVolume) bool 62 IsInlineMigratable(vol *v1.Volume) bool 63 IsMigratableIntreePluginByName(inTreePluginName string) bool 64 GetInTreePluginNameFromSpec(pv *v1.PersistentVolume, vol *v1.Volume) (string, error) 65 GetCSINameFromInTreeName(pluginName string) (string, error) 66 TranslateInTreePVToCSI(pv *v1.PersistentVolume) (*v1.PersistentVolume, error) 67 TranslateInTreeInlineVolumeToCSI(volume *v1.Volume, podNamespace string) (*v1.PersistentVolume, error) 68 } 69 70 var _ OperationGenerator = &operationGenerator{} 71 72 type operationGenerator struct { 73 // Used to fetch objects from the API server like Node in the 74 // VerifyControllerAttachedVolume operation. 75 kubeClient clientset.Interface 76 77 // volumePluginMgr is the volume plugin manager used to create volume 78 // plugin objects. 79 volumePluginMgr *volume.VolumePluginMgr 80 81 // recorder is used to record events in the API server 82 recorder record.EventRecorder 83 84 // blkUtil provides volume path related operations for block volume 85 blkUtil volumepathhandler.BlockVolumePathHandler 86 87 translator InTreeToCSITranslator 88 } 89 90 type inTreeResizeResponse struct { 91 pvc *v1.PersistentVolumeClaim 92 pv *v1.PersistentVolume 93 94 err error 95 // indicates that resize operation was called on underlying volume driver 96 // mainly useful for testing. 97 resizeCalled bool 98 } 99 100 // NewOperationGenerator is returns instance of operationGenerator 101 func NewOperationGenerator(kubeClient clientset.Interface, 102 volumePluginMgr *volume.VolumePluginMgr, 103 recorder record.EventRecorder, 104 blkUtil volumepathhandler.BlockVolumePathHandler) OperationGenerator { 105 106 return &operationGenerator{ 107 kubeClient: kubeClient, 108 volumePluginMgr: volumePluginMgr, 109 recorder: recorder, 110 blkUtil: blkUtil, 111 translator: csitrans.New(), 112 } 113 } 114 115 // OperationGenerator interface that extracts out the functions from operation_executor to make it dependency injectable 116 type OperationGenerator interface { 117 // Generates the MountVolume function needed to perform the mount of a volume plugin 118 GenerateMountVolumeFunc(waitForAttachTimeout time.Duration, volumeToMount VolumeToMount, actualStateOfWorldMounterUpdater ActualStateOfWorldMounterUpdater, isRemount bool) volumetypes.GeneratedOperations 119 120 // Generates the UnmountVolume function needed to perform the unmount of a volume plugin 121 GenerateUnmountVolumeFunc(volumeToUnmount MountedVolume, actualStateOfWorld ActualStateOfWorldMounterUpdater, podsDir string) (volumetypes.GeneratedOperations, error) 122 123 // Generates the AttachVolume function needed to perform attach of a volume plugin 124 GenerateAttachVolumeFunc(logger klog.Logger, volumeToAttach VolumeToAttach, actualStateOfWorld ActualStateOfWorldAttacherUpdater) volumetypes.GeneratedOperations 125 126 // Generates the DetachVolume function needed to perform the detach of a volume plugin 127 GenerateDetachVolumeFunc(logger klog.Logger, volumeToDetach AttachedVolume, verifySafeToDetach bool, actualStateOfWorld ActualStateOfWorldAttacherUpdater) (volumetypes.GeneratedOperations, error) 128 129 // Generates the VolumesAreAttached function needed to verify if volume plugins are attached 130 GenerateVolumesAreAttachedFunc(attachedVolumes []AttachedVolume, nodeName types.NodeName, actualStateOfWorld ActualStateOfWorldAttacherUpdater) (volumetypes.GeneratedOperations, error) 131 132 // Generates the UnMountDevice function needed to perform the unmount of a device 133 GenerateUnmountDeviceFunc(deviceToDetach AttachedVolume, actualStateOfWorld ActualStateOfWorldMounterUpdater, mounter hostutil.HostUtils) (volumetypes.GeneratedOperations, error) 134 135 // Generates the function needed to check if the attach_detach controller has attached the volume plugin 136 GenerateVerifyControllerAttachedVolumeFunc(logger klog.Logger, volumeToMount VolumeToMount, nodeName types.NodeName, actualStateOfWorld ActualStateOfWorldAttacherUpdater) (volumetypes.GeneratedOperations, error) 137 138 // Generates the MapVolume function needed to perform the map of a volume plugin 139 GenerateMapVolumeFunc(waitForAttachTimeout time.Duration, volumeToMount VolumeToMount, actualStateOfWorldMounterUpdater ActualStateOfWorldMounterUpdater) (volumetypes.GeneratedOperations, error) 140 141 // Generates the UnmapVolume function needed to perform the unmap of a volume plugin 142 GenerateUnmapVolumeFunc(volumeToUnmount MountedVolume, actualStateOfWorld ActualStateOfWorldMounterUpdater) (volumetypes.GeneratedOperations, error) 143 144 // Generates the UnmapDevice function needed to perform the unmap of a device 145 GenerateUnmapDeviceFunc(deviceToDetach AttachedVolume, actualStateOfWorld ActualStateOfWorldMounterUpdater, mounter hostutil.HostUtils) (volumetypes.GeneratedOperations, error) 146 147 // GetVolumePluginMgr returns volume plugin manager 148 GetVolumePluginMgr() *volume.VolumePluginMgr 149 150 // GetCSITranslator returns the CSI Translation Library 151 GetCSITranslator() InTreeToCSITranslator 152 153 GenerateExpandVolumeFunc(*v1.PersistentVolumeClaim, *v1.PersistentVolume) (volumetypes.GeneratedOperations, error) 154 155 GenerateExpandAndRecoverVolumeFunc(*v1.PersistentVolumeClaim, *v1.PersistentVolume, string) (volumetypes.GeneratedOperations, error) 156 157 // Generates the volume file system resize function, which can resize volume's file system to expected size without unmounting the volume. 158 // Along with volumeToMount and actualStateOfWorld, the function expects current size of volume on the node as an argument. The current 159 // size here always refers to capacity last recorded in actualStateOfWorld from pvc.Status.Capacity 160 GenerateExpandInUseVolumeFunc(volumeToMount VolumeToMount, actualStateOfWorld ActualStateOfWorldMounterUpdater, currentSize resource.Quantity) (volumetypes.GeneratedOperations, error) 161 } 162 163 type inTreeResizeOpts struct { 164 resizerName string 165 pvc *v1.PersistentVolumeClaim 166 pv *v1.PersistentVolume 167 volumeSpec *volume.Spec 168 volumePlugin volume.ExpandableVolumePlugin 169 } 170 171 type nodeResizeOperationOpts struct { 172 vmt VolumeToMount 173 pvc *v1.PersistentVolumeClaim 174 pv *v1.PersistentVolume 175 pluginResizeOpts volume.NodeResizeOptions 176 volumePlugin volume.NodeExpandableVolumePlugin 177 actualStateOfWorld ActualStateOfWorldMounterUpdater 178 } 179 180 func (og *operationGenerator) GenerateVolumesAreAttachedFunc( 181 attachedVolumes []AttachedVolume, 182 nodeName types.NodeName, 183 actualStateOfWorld ActualStateOfWorldAttacherUpdater) (volumetypes.GeneratedOperations, error) { 184 // volumesPerPlugin maps from a volume plugin to a list of volume specs which belong 185 // to this type of plugin 186 volumesPerPlugin := make(map[string][]*volume.Spec) 187 // volumeSpecMap maps from a volume spec to its unique volumeName which will be used 188 // when calling MarkVolumeAsDetached 189 volumeSpecMap := make(map[*volume.Spec]v1.UniqueVolumeName) 190 191 // Iterate each volume spec and put them into a map index by the pluginName 192 for _, volumeAttached := range attachedVolumes { 193 if volumeAttached.VolumeSpec == nil { 194 klog.Errorf("VerifyVolumesAreAttached.GenerateVolumesAreAttachedFunc: nil spec for volume %s", volumeAttached.VolumeName) 195 continue 196 } 197 volumePlugin, err := 198 og.volumePluginMgr.FindPluginBySpec(volumeAttached.VolumeSpec) 199 if err != nil || volumePlugin == nil { 200 klog.Errorf(volumeAttached.GenerateErrorDetailed("VolumesAreAttached.FindPluginBySpec failed", err).Error()) 201 continue 202 } 203 volumeSpecList, pluginExists := volumesPerPlugin[volumePlugin.GetPluginName()] 204 if !pluginExists { 205 volumeSpecList = []*volume.Spec{} 206 } 207 volumeSpecList = append(volumeSpecList, volumeAttached.VolumeSpec) 208 volumesPerPlugin[volumePlugin.GetPluginName()] = volumeSpecList 209 // Migration: VolumeSpecMap contains original VolumeName for use in ActualStateOfWorld 210 volumeSpecMap[volumeAttached.VolumeSpec] = volumeAttached.VolumeName 211 } 212 213 volumesAreAttachedFunc := func() volumetypes.OperationContext { 214 215 // For each volume plugin, pass the list of volume specs to VolumesAreAttached to check 216 // whether the volumes are still attached. 217 for pluginName, volumesSpecs := range volumesPerPlugin { 218 attachableVolumePlugin, err := 219 og.volumePluginMgr.FindAttachablePluginByName(pluginName) 220 if err != nil || attachableVolumePlugin == nil { 221 klog.Errorf( 222 "VolumeAreAttached.FindAttachablePluginBySpec failed for plugin %q with: %v", 223 pluginName, 224 err) 225 continue 226 } 227 228 volumeAttacher, newAttacherErr := attachableVolumePlugin.NewAttacher() 229 if newAttacherErr != nil { 230 klog.Errorf( 231 "VolumesAreAttached.NewAttacher failed for getting plugin %q with: %v", 232 pluginName, 233 newAttacherErr) 234 continue 235 } 236 237 attached, areAttachedErr := volumeAttacher.VolumesAreAttached(volumesSpecs, nodeName) 238 if areAttachedErr != nil { 239 klog.Errorf( 240 "VolumesAreAttached failed for checking on node %q with: %v", 241 nodeName, 242 areAttachedErr) 243 continue 244 } 245 246 for spec, check := range attached { 247 if !check { 248 actualStateOfWorld.MarkVolumeAsDetached(volumeSpecMap[spec], nodeName) 249 klog.V(1).Infof("VerifyVolumesAreAttached determined volume %q (spec.Name: %q) is no longer attached to node %q, therefore it was marked as detached.", 250 volumeSpecMap[spec], spec.Name(), nodeName) 251 } 252 } 253 } 254 255 // It is hard to differentiate migrated status for all volumes for verify_volumes_are_attached_per_node 256 return volumetypes.NewOperationContext(nil, nil, false) 257 } 258 259 return volumetypes.GeneratedOperations{ 260 OperationName: "verify_volumes_are_attached_per_node", 261 OperationFunc: volumesAreAttachedFunc, 262 CompleteFunc: util.OperationCompleteHook(util.GetFullQualifiedPluginNameForVolume("<n/a>", nil), "verify_volumes_are_attached_per_node"), 263 EventRecorderFunc: nil, // nil because we do not want to generate event on error 264 }, nil 265 } 266 267 func (og *operationGenerator) GenerateAttachVolumeFunc( 268 logger klog.Logger, 269 volumeToAttach VolumeToAttach, 270 actualStateOfWorld ActualStateOfWorldAttacherUpdater) volumetypes.GeneratedOperations { 271 272 attachVolumeFunc := func() volumetypes.OperationContext { 273 attachableVolumePlugin, err := 274 og.volumePluginMgr.FindAttachablePluginBySpec(volumeToAttach.VolumeSpec) 275 276 migrated := getMigratedStatusBySpec(volumeToAttach.VolumeSpec) 277 278 if err != nil || attachableVolumePlugin == nil { 279 eventErr, detailedErr := volumeToAttach.GenerateError("AttachVolume.FindAttachablePluginBySpec failed", err) 280 return volumetypes.NewOperationContext(eventErr, detailedErr, migrated) 281 } 282 283 volumeAttacher, newAttacherErr := attachableVolumePlugin.NewAttacher() 284 if newAttacherErr != nil { 285 eventErr, detailedErr := volumeToAttach.GenerateError("AttachVolume.NewAttacher failed", newAttacherErr) 286 return volumetypes.NewOperationContext(eventErr, detailedErr, migrated) 287 } 288 289 // Execute attach 290 devicePath, attachErr := volumeAttacher.Attach( 291 volumeToAttach.VolumeSpec, volumeToAttach.NodeName) 292 293 if attachErr != nil { 294 uncertainNode := volumeToAttach.NodeName 295 if derr, ok := attachErr.(*volerr.DanglingAttachError); ok { 296 uncertainNode = derr.CurrentNode 297 } 298 addErr := actualStateOfWorld.MarkVolumeAsUncertain( 299 logger, 300 volumeToAttach.VolumeName, 301 volumeToAttach.VolumeSpec, 302 uncertainNode) 303 if addErr != nil { 304 klog.Errorf("AttachVolume.MarkVolumeAsUncertain fail to add the volume %q to actual state with %s", volumeToAttach.VolumeName, addErr) 305 } 306 307 // On failure, return error. Caller will log and retry. 308 eventErr, detailedErr := volumeToAttach.GenerateError("AttachVolume.Attach failed", attachErr) 309 return volumetypes.NewOperationContext(eventErr, detailedErr, migrated) 310 } 311 312 // Successful attach event is useful for user debugging 313 simpleMsg, _ := volumeToAttach.GenerateMsg("AttachVolume.Attach succeeded", "") 314 for _, pod := range volumeToAttach.ScheduledPods { 315 og.recorder.Eventf(pod, v1.EventTypeNormal, kevents.SuccessfulAttachVolume, simpleMsg) 316 } 317 klog.Infof(volumeToAttach.GenerateMsgDetailed("AttachVolume.Attach succeeded", "")) 318 319 // Update actual state of world 320 addVolumeNodeErr := actualStateOfWorld.MarkVolumeAsAttached( 321 logger, v1.UniqueVolumeName(""), volumeToAttach.VolumeSpec, volumeToAttach.NodeName, devicePath) 322 if addVolumeNodeErr != nil { 323 // On failure, return error. Caller will log and retry. 324 eventErr, detailedErr := volumeToAttach.GenerateError("AttachVolume.MarkVolumeAsAttached failed", addVolumeNodeErr) 325 return volumetypes.NewOperationContext(eventErr, detailedErr, migrated) 326 } 327 328 return volumetypes.NewOperationContext(nil, nil, migrated) 329 } 330 331 eventRecorderFunc := func(err *error) { 332 if *err != nil { 333 for _, pod := range volumeToAttach.ScheduledPods { 334 og.recorder.Eventf(pod, v1.EventTypeWarning, kevents.FailedAttachVolume, (*err).Error()) 335 } 336 } 337 } 338 339 attachableVolumePluginName := unknownAttachableVolumePlugin 340 341 // Get attacher plugin 342 attachableVolumePlugin, err := 343 og.volumePluginMgr.FindAttachablePluginBySpec(volumeToAttach.VolumeSpec) 344 // It's ok to ignore the error, returning error is not expected from this function. 345 // If an error case occurred during the function generation, this error case(skipped one) will also trigger an error 346 // while the generated function is executed. And those errors will be handled during the execution of the generated 347 // function with a back off policy. 348 if err == nil && attachableVolumePlugin != nil { 349 attachableVolumePluginName = attachableVolumePlugin.GetPluginName() 350 } 351 352 return volumetypes.GeneratedOperations{ 353 OperationName: "volume_attach", 354 OperationFunc: attachVolumeFunc, 355 EventRecorderFunc: eventRecorderFunc, 356 CompleteFunc: util.OperationCompleteHook(util.GetFullQualifiedPluginNameForVolume(attachableVolumePluginName, volumeToAttach.VolumeSpec), "volume_attach"), 357 } 358 } 359 360 func (og *operationGenerator) GetVolumePluginMgr() *volume.VolumePluginMgr { 361 return og.volumePluginMgr 362 } 363 364 func (og *operationGenerator) GetCSITranslator() InTreeToCSITranslator { 365 return og.translator 366 } 367 368 func (og *operationGenerator) GenerateDetachVolumeFunc( 369 logger klog.Logger, 370 volumeToDetach AttachedVolume, 371 verifySafeToDetach bool, 372 actualStateOfWorld ActualStateOfWorldAttacherUpdater) (volumetypes.GeneratedOperations, error) { 373 var volumeName string 374 var attachableVolumePlugin volume.AttachableVolumePlugin 375 var pluginName string 376 var err error 377 378 if volumeToDetach.VolumeSpec != nil { 379 attachableVolumePlugin, err = findDetachablePluginBySpec(volumeToDetach.VolumeSpec, og.volumePluginMgr) 380 if err != nil || attachableVolumePlugin == nil { 381 return volumetypes.GeneratedOperations{}, volumeToDetach.GenerateErrorDetailed("DetachVolume.findDetachablePluginBySpec failed", err) 382 } 383 384 volumeName, err = 385 attachableVolumePlugin.GetVolumeName(volumeToDetach.VolumeSpec) 386 if err != nil { 387 return volumetypes.GeneratedOperations{}, volumeToDetach.GenerateErrorDetailed("DetachVolume.GetVolumeName failed", err) 388 } 389 } else { 390 // Get attacher plugin and the volumeName by splitting the volume unique name in case 391 // there's no VolumeSpec: this happens only on attach/detach controller crash recovery 392 // when a pod has been deleted during the controller downtime 393 pluginName, volumeName, err = util.SplitUniqueName(volumeToDetach.VolumeName) 394 if err != nil { 395 return volumetypes.GeneratedOperations{}, volumeToDetach.GenerateErrorDetailed("DetachVolume.SplitUniqueName failed", err) 396 } 397 398 attachableVolumePlugin, err = og.volumePluginMgr.FindAttachablePluginByName(pluginName) 399 if err != nil || attachableVolumePlugin == nil { 400 return volumetypes.GeneratedOperations{}, volumeToDetach.GenerateErrorDetailed("DetachVolume.FindAttachablePluginByName failed", err) 401 } 402 403 } 404 405 if pluginName == "" { 406 pluginName = attachableVolumePlugin.GetPluginName() 407 } 408 409 volumeDetacher, err := attachableVolumePlugin.NewDetacher() 410 if err != nil { 411 return volumetypes.GeneratedOperations{}, volumeToDetach.GenerateErrorDetailed("DetachVolume.NewDetacher failed", err) 412 } 413 414 detachVolumeFunc := func() volumetypes.OperationContext { 415 var err error 416 if verifySafeToDetach { 417 err = og.verifyVolumeIsSafeToDetach(volumeToDetach) 418 } 419 if err == nil { 420 err = volumeDetacher.Detach(volumeName, volumeToDetach.NodeName) 421 } 422 423 migrated := getMigratedStatusBySpec(volumeToDetach.VolumeSpec) 424 425 if err != nil { 426 // On failure, mark the volume as uncertain. Attach() must succeed before adding the volume back 427 // to node status as attached. 428 uncertainError := actualStateOfWorld.MarkVolumeAsUncertain( 429 logger, volumeToDetach.VolumeName, volumeToDetach.VolumeSpec, volumeToDetach.NodeName) 430 if uncertainError != nil { 431 klog.Errorf("DetachVolume.MarkVolumeAsUncertain failed to add the volume %q to actual state after detach error: %s", volumeToDetach.VolumeName, uncertainError) 432 } 433 eventErr, detailedErr := volumeToDetach.GenerateError("DetachVolume.Detach failed", err) 434 return volumetypes.NewOperationContext(eventErr, detailedErr, migrated) 435 } 436 437 klog.Infof(volumeToDetach.GenerateMsgDetailed("DetachVolume.Detach succeeded", "")) 438 439 // Update actual state of world 440 actualStateOfWorld.MarkVolumeAsDetached( 441 volumeToDetach.VolumeName, volumeToDetach.NodeName) 442 443 return volumetypes.NewOperationContext(nil, nil, migrated) 444 } 445 446 return volumetypes.GeneratedOperations{ 447 OperationName: DetachOperationName, 448 OperationFunc: detachVolumeFunc, 449 CompleteFunc: util.OperationCompleteHook(util.GetFullQualifiedPluginNameForVolume(pluginName, volumeToDetach.VolumeSpec), DetachOperationName), 450 EventRecorderFunc: nil, // nil because we do not want to generate event on error 451 }, nil 452 } 453 454 func (og *operationGenerator) GenerateMountVolumeFunc( 455 waitForAttachTimeout time.Duration, 456 volumeToMount VolumeToMount, 457 actualStateOfWorld ActualStateOfWorldMounterUpdater, 458 isRemount bool) volumetypes.GeneratedOperations { 459 460 volumePluginName := unknownVolumePlugin 461 volumePlugin, err := 462 og.volumePluginMgr.FindPluginBySpec(volumeToMount.VolumeSpec) 463 if err == nil && volumePlugin != nil { 464 volumePluginName = volumePlugin.GetPluginName() 465 } 466 467 mountVolumeFunc := func() volumetypes.OperationContext { 468 // Get mounter plugin 469 volumePlugin, err := og.volumePluginMgr.FindPluginBySpec(volumeToMount.VolumeSpec) 470 471 migrated := getMigratedStatusBySpec(volumeToMount.VolumeSpec) 472 473 if err != nil || volumePlugin == nil { 474 eventErr, detailedErr := volumeToMount.GenerateError("MountVolume.FindPluginBySpec failed", err) 475 return volumetypes.NewOperationContext(eventErr, detailedErr, migrated) 476 } 477 478 affinityErr := checkNodeAffinity(og, volumeToMount) 479 if affinityErr != nil { 480 eventErr, detailedErr := volumeToMount.GenerateError("MountVolume.NodeAffinity check failed", affinityErr) 481 return volumetypes.NewOperationContext(eventErr, detailedErr, migrated) 482 } 483 484 volumeMounter, newMounterErr := volumePlugin.NewMounter( 485 volumeToMount.VolumeSpec, 486 volumeToMount.Pod, 487 volume.VolumeOptions{}) 488 if newMounterErr != nil { 489 eventErr, detailedErr := volumeToMount.GenerateError("MountVolume.NewMounter initialization failed", newMounterErr) 490 return volumetypes.NewOperationContext(eventErr, detailedErr, migrated) 491 } 492 493 mountCheckError := checkMountOptionSupport(og, volumeToMount, volumePlugin) 494 if mountCheckError != nil { 495 eventErr, detailedErr := volumeToMount.GenerateError("MountVolume.MountOptionSupport check failed", mountCheckError) 496 return volumetypes.NewOperationContext(eventErr, detailedErr, migrated) 497 } 498 499 // Enforce ReadWriteOncePod access mode if it is the only one present. This is also enforced during scheduling. 500 if actualStateOfWorld.IsVolumeMountedElsewhere(volumeToMount.VolumeName, volumeToMount.PodName) && 501 // Because we do not know what access mode the pod intends to use if there are multiple. 502 len(volumeToMount.VolumeSpec.PersistentVolume.Spec.AccessModes) == 1 && 503 v1helper.ContainsAccessMode(volumeToMount.VolumeSpec.PersistentVolume.Spec.AccessModes, v1.ReadWriteOncePod) { 504 505 err = goerrors.New("volume uses the ReadWriteOncePod access mode and is already in use by another pod") 506 eventErr, detailedErr := volumeToMount.GenerateError("MountVolume.SetUp failed", err) 507 return volumetypes.NewOperationContext(eventErr, detailedErr, migrated) 508 } 509 510 // Get attacher, if possible 511 attachableVolumePlugin, _ := 512 og.volumePluginMgr.FindAttachablePluginBySpec(volumeToMount.VolumeSpec) 513 var volumeAttacher volume.Attacher 514 if attachableVolumePlugin != nil { 515 volumeAttacher, _ = attachableVolumePlugin.NewAttacher() 516 } 517 518 // get deviceMounter, if possible 519 deviceMountableVolumePlugin, _ := og.volumePluginMgr.FindDeviceMountablePluginBySpec(volumeToMount.VolumeSpec) 520 var volumeDeviceMounter volume.DeviceMounter 521 if deviceMountableVolumePlugin != nil { 522 volumeDeviceMounter, _ = deviceMountableVolumePlugin.NewDeviceMounter() 523 } 524 525 var fsGroup *int64 526 var fsGroupChangePolicy *v1.PodFSGroupChangePolicy 527 if podSc := volumeToMount.Pod.Spec.SecurityContext; podSc != nil { 528 if podSc.FSGroup != nil { 529 fsGroup = podSc.FSGroup 530 } 531 if podSc.FSGroupChangePolicy != nil { 532 fsGroupChangePolicy = podSc.FSGroupChangePolicy 533 } 534 } 535 536 devicePath := volumeToMount.DevicePath 537 if volumeAttacher != nil { 538 // Wait for attachable volumes to finish attaching 539 klog.InfoS(volumeToMount.GenerateMsgDetailed("MountVolume.WaitForAttach entering", fmt.Sprintf("DevicePath %q", volumeToMount.DevicePath)), "pod", klog.KObj(volumeToMount.Pod)) 540 541 devicePath, err = volumeAttacher.WaitForAttach( 542 volumeToMount.VolumeSpec, devicePath, volumeToMount.Pod, waitForAttachTimeout) 543 if err != nil { 544 // On failure, return error. Caller will log and retry. 545 eventErr, detailedErr := volumeToMount.GenerateError("MountVolume.WaitForAttach failed", err) 546 return volumetypes.NewOperationContext(eventErr, detailedErr, migrated) 547 } 548 549 klog.InfoS(volumeToMount.GenerateMsgDetailed("MountVolume.WaitForAttach succeeded", fmt.Sprintf("DevicePath %q", devicePath)), "pod", klog.KObj(volumeToMount.Pod)) 550 } 551 552 var resizeError error 553 resizeOptions := volume.NodeResizeOptions{ 554 DevicePath: devicePath, 555 } 556 557 if volumeDeviceMounter != nil && actualStateOfWorld.GetDeviceMountState(volumeToMount.VolumeName) != DeviceGloballyMounted { 558 deviceMountPath, err := 559 volumeDeviceMounter.GetDeviceMountPath(volumeToMount.VolumeSpec) 560 if err != nil { 561 // On failure, return error. Caller will log and retry. 562 eventErr, detailedErr := volumeToMount.GenerateError("MountVolume.GetDeviceMountPath failed", err) 563 return volumetypes.NewOperationContext(eventErr, detailedErr, migrated) 564 } 565 566 // Mount device to global mount path 567 err = volumeDeviceMounter.MountDevice( 568 volumeToMount.VolumeSpec, 569 devicePath, 570 deviceMountPath, 571 volume.DeviceMounterArgs{FsGroup: fsGroup, SELinuxLabel: volumeToMount.SELinuxLabel}, 572 ) 573 if err != nil { 574 og.checkForFailedMount(volumeToMount, err) 575 og.markDeviceErrorState(volumeToMount, devicePath, deviceMountPath, err, actualStateOfWorld) 576 // On failure, return error. Caller will log and retry. 577 eventErr, detailedErr := volumeToMount.GenerateError("MountVolume.MountDevice failed", err) 578 return volumetypes.NewOperationContext(eventErr, detailedErr, migrated) 579 } 580 581 klog.InfoS(volumeToMount.GenerateMsgDetailed("MountVolume.MountDevice succeeded", fmt.Sprintf("device mount path %q", deviceMountPath)), "pod", klog.KObj(volumeToMount.Pod)) 582 583 // Update actual state of world to reflect volume is globally mounted 584 markDeviceMountedErr := actualStateOfWorld.MarkDeviceAsMounted( 585 volumeToMount.VolumeName, devicePath, deviceMountPath, volumeToMount.SELinuxLabel) 586 if markDeviceMountedErr != nil { 587 // On failure, return error. Caller will log and retry. 588 eventErr, detailedErr := volumeToMount.GenerateError("MountVolume.MarkDeviceAsMounted failed", markDeviceMountedErr) 589 return volumetypes.NewOperationContext(eventErr, detailedErr, migrated) 590 } 591 // set staging path for volume expansion 592 resizeOptions.DeviceStagePath = deviceMountPath 593 } 594 595 if volumeDeviceMounter != nil && resizeOptions.DeviceStagePath == "" { 596 deviceStagePath, err := volumeDeviceMounter.GetDeviceMountPath(volumeToMount.VolumeSpec) 597 if err != nil { 598 // On failure, return error. Caller will log and retry. 599 eventErr, detailedErr := volumeToMount.GenerateError("MountVolume.GetDeviceMountPath failed for expansion", err) 600 return volumetypes.NewOperationContext(eventErr, detailedErr, migrated) 601 } 602 resizeOptions.DeviceStagePath = deviceStagePath 603 } 604 605 // Execute mount 606 mountErr := volumeMounter.SetUp(volume.MounterArgs{ 607 FsUser: util.FsUserFrom(volumeToMount.Pod), 608 FsGroup: fsGroup, 609 DesiredSize: volumeToMount.DesiredSizeLimit, 610 FSGroupChangePolicy: fsGroupChangePolicy, 611 SELinuxLabel: volumeToMount.SELinuxLabel, 612 }) 613 // Update actual state of world 614 markOpts := MarkVolumeOpts{ 615 PodName: volumeToMount.PodName, 616 PodUID: volumeToMount.Pod.UID, 617 VolumeName: volumeToMount.VolumeName, 618 Mounter: volumeMounter, 619 OuterVolumeSpecName: volumeToMount.OuterVolumeSpecName, 620 VolumeGidVolume: volumeToMount.VolumeGidValue, 621 VolumeSpec: volumeToMount.VolumeSpec, 622 VolumeMountState: VolumeMounted, 623 SELinuxMountContext: volumeToMount.SELinuxLabel, 624 } 625 if mountErr != nil { 626 og.checkForFailedMount(volumeToMount, mountErr) 627 og.markVolumeErrorState(volumeToMount, markOpts, mountErr, actualStateOfWorld) 628 // On failure, return error. Caller will log and retry. 629 eventErr, detailedErr := volumeToMount.GenerateError("MountVolume.SetUp failed", mountErr) 630 return volumetypes.NewOperationContext(eventErr, detailedErr, migrated) 631 } 632 633 detailedMsg := volumeToMount.GenerateMsgDetailed("MountVolume.SetUp succeeded", "") 634 verbosity := klog.Level(1) 635 if isRemount { 636 verbosity = klog.Level(4) 637 } 638 klog.V(verbosity).InfoS(detailedMsg, "pod", klog.KObj(volumeToMount.Pod)) 639 resizeOptions.DeviceMountPath = volumeMounter.GetPath() 640 641 _, resizeError = og.expandVolumeDuringMount(volumeToMount, actualStateOfWorld, resizeOptions) 642 if resizeError != nil { 643 klog.Errorf("MountVolume.NodeExpandVolume failed with %v", resizeError) 644 eventErr, detailedErr := volumeToMount.GenerateError("MountVolume.Setup failed while expanding volume", resizeError) 645 // At this point, MountVolume.Setup already succeeded, we should add volume into actual state 646 // so that reconciler can clean up volume when needed. However, volume resize failed, 647 // we should not mark the volume as mounted to avoid pod starts using it. 648 // Considering the above situations, we mark volume as uncertain here so that reconciler will trigger 649 // volume tear down when pod is deleted, and also makes sure pod will not start using it. 650 if err := actualStateOfWorld.MarkVolumeMountAsUncertain(markOpts); err != nil { 651 klog.Errorf(volumeToMount.GenerateErrorDetailed("MountVolume.MarkVolumeMountAsUncertain failed", err).Error()) 652 } 653 return volumetypes.NewOperationContext(eventErr, detailedErr, migrated) 654 } 655 656 // record total time it takes to mount a volume. This is end to end time that includes waiting for volume to attach, node to be update 657 // plugin call to succeed 658 mountRequestTime := volumeToMount.MountRequestTime 659 totalTimeTaken := time.Since(mountRequestTime).Seconds() 660 util.RecordOperationLatencyMetric(util.GetFullQualifiedPluginNameForVolume(volumePluginName, volumeToMount.VolumeSpec), "overall_volume_mount", totalTimeTaken) 661 662 markVolMountedErr := actualStateOfWorld.MarkVolumeAsMounted(markOpts) 663 if markVolMountedErr != nil { 664 // On failure, return error. Caller will log and retry. 665 eventErr, detailedErr := volumeToMount.GenerateError("MountVolume.MarkVolumeAsMounted failed", markVolMountedErr) 666 return volumetypes.NewOperationContext(eventErr, detailedErr, migrated) 667 } 668 return volumetypes.NewOperationContext(nil, nil, migrated) 669 } 670 671 eventRecorderFunc := func(err *error) { 672 if *err != nil { 673 og.recorder.Eventf(volumeToMount.Pod, v1.EventTypeWarning, kevents.FailedMountVolume, (*err).Error()) 674 } 675 } 676 677 return volumetypes.GeneratedOperations{ 678 OperationName: "volume_mount", 679 OperationFunc: mountVolumeFunc, 680 EventRecorderFunc: eventRecorderFunc, 681 CompleteFunc: util.OperationCompleteHook(util.GetFullQualifiedPluginNameForVolume(volumePluginName, volumeToMount.VolumeSpec), "volume_mount"), 682 } 683 } 684 685 func (og *operationGenerator) checkForFailedMount(volumeToMount VolumeToMount, mountError error) { 686 pv := volumeToMount.VolumeSpec.PersistentVolume 687 if pv == nil { 688 return 689 } 690 691 if volumetypes.IsFilesystemMismatchError(mountError) { 692 simpleMsg, _ := volumeToMount.GenerateMsg("MountVolume failed", mountError.Error()) 693 og.recorder.Eventf(pv, v1.EventTypeWarning, kevents.FailedMountOnFilesystemMismatch, simpleMsg) 694 } 695 } 696 697 func (og *operationGenerator) markDeviceErrorState(volumeToMount VolumeToMount, devicePath, deviceMountPath string, mountError error, actualStateOfWorld ActualStateOfWorldMounterUpdater) { 698 if volumetypes.IsOperationFinishedError(mountError) && 699 actualStateOfWorld.GetDeviceMountState(volumeToMount.VolumeName) == DeviceMountUncertain { 700 701 if actualStateOfWorld.IsVolumeDeviceReconstructed(volumeToMount.VolumeName) { 702 klog.V(2).InfoS("MountVolume.markDeviceErrorState leaving volume uncertain", "volumeName", volumeToMount.VolumeName) 703 return 704 } 705 706 // Only devices which were uncertain can be marked as unmounted 707 markDeviceUnmountError := actualStateOfWorld.MarkDeviceAsUnmounted(volumeToMount.VolumeName) 708 if markDeviceUnmountError != nil { 709 klog.Errorf(volumeToMount.GenerateErrorDetailed("MountDevice.MarkDeviceAsUnmounted failed", markDeviceUnmountError).Error()) 710 } 711 return 712 } 713 714 if volumetypes.IsUncertainProgressError(mountError) && 715 actualStateOfWorld.GetDeviceMountState(volumeToMount.VolumeName) == DeviceNotMounted { 716 // only devices which are not mounted can be marked as uncertain. We do not want to mark a device 717 // which was previously marked as mounted here as uncertain. 718 markDeviceUncertainError := actualStateOfWorld.MarkDeviceAsUncertain(volumeToMount.VolumeName, devicePath, deviceMountPath, volumeToMount.SELinuxLabel) 719 if markDeviceUncertainError != nil { 720 klog.Errorf(volumeToMount.GenerateErrorDetailed("MountDevice.MarkDeviceAsUncertain failed", markDeviceUncertainError).Error()) 721 } 722 } 723 724 } 725 726 func (og *operationGenerator) markVolumeErrorState(volumeToMount VolumeToMount, markOpts MarkVolumeOpts, mountError error, actualStateOfWorld ActualStateOfWorldMounterUpdater) { 727 if volumetypes.IsOperationFinishedError(mountError) && 728 actualStateOfWorld.GetVolumeMountState(volumeToMount.VolumeName, markOpts.PodName) == VolumeMountUncertain { 729 // if volume was previously reconstructed we are not going to change its state as unmounted even 730 // if mount operation fails. 731 if actualStateOfWorld.IsVolumeReconstructed(volumeToMount.VolumeName, volumeToMount.PodName) { 732 klog.V(3).InfoS("MountVolume.markVolumeErrorState leaving volume uncertain", "volumeName", volumeToMount.VolumeName) 733 return 734 } 735 736 t := actualStateOfWorld.MarkVolumeAsUnmounted(volumeToMount.PodName, volumeToMount.VolumeName) 737 if t != nil { 738 klog.Errorf(volumeToMount.GenerateErrorDetailed("MountVolume.MarkVolumeAsUnmounted failed", t).Error()) 739 } 740 return 741 742 } 743 744 if volumetypes.IsUncertainProgressError(mountError) && 745 actualStateOfWorld.GetVolumeMountState(volumeToMount.VolumeName, markOpts.PodName) == VolumeNotMounted { 746 t := actualStateOfWorld.MarkVolumeMountAsUncertain(markOpts) 747 if t != nil { 748 klog.Errorf(volumeToMount.GenerateErrorDetailed("MountVolume.MarkVolumeMountAsUncertain failed", t).Error()) 749 } 750 } 751 } 752 753 func (og *operationGenerator) GenerateUnmountVolumeFunc( 754 volumeToUnmount MountedVolume, 755 actualStateOfWorld ActualStateOfWorldMounterUpdater, 756 podsDir string) (volumetypes.GeneratedOperations, error) { 757 // Get mountable plugin 758 volumePlugin, err := og.volumePluginMgr.FindPluginByName(volumeToUnmount.PluginName) 759 if err != nil || volumePlugin == nil { 760 return volumetypes.GeneratedOperations{}, volumeToUnmount.GenerateErrorDetailed("UnmountVolume.FindPluginByName failed", err) 761 } 762 volumeUnmounter, newUnmounterErr := volumePlugin.NewUnmounter( 763 volumeToUnmount.InnerVolumeSpecName, volumeToUnmount.PodUID) 764 if newUnmounterErr != nil { 765 return volumetypes.GeneratedOperations{}, volumeToUnmount.GenerateErrorDetailed("UnmountVolume.NewUnmounter failed", newUnmounterErr) 766 } 767 768 unmountVolumeFunc := func() volumetypes.OperationContext { 769 subpather := og.volumePluginMgr.Host.GetSubpather() 770 771 migrated := getMigratedStatusBySpec(volumeToUnmount.VolumeSpec) 772 773 // Remove all bind-mounts for subPaths 774 podDir := filepath.Join(podsDir, string(volumeToUnmount.PodUID)) 775 if err := subpather.CleanSubPaths(podDir, volumeToUnmount.InnerVolumeSpecName); err != nil { 776 eventErr, detailedErr := volumeToUnmount.GenerateError("error cleaning subPath mounts", err) 777 return volumetypes.NewOperationContext(eventErr, detailedErr, migrated) 778 } 779 780 // Execute unmount 781 unmountErr := volumeUnmounter.TearDown() 782 if unmountErr != nil { 783 // Mark the volume as uncertain, so SetUp is called for new pods. Teardown may be already in progress. 784 opts := MarkVolumeOpts{ 785 PodName: volumeToUnmount.PodName, 786 PodUID: volumeToUnmount.PodUID, 787 VolumeName: volumeToUnmount.VolumeName, 788 OuterVolumeSpecName: volumeToUnmount.OuterVolumeSpecName, 789 VolumeGidVolume: volumeToUnmount.VolumeGidValue, 790 VolumeSpec: volumeToUnmount.VolumeSpec, 791 VolumeMountState: VolumeMountUncertain, 792 } 793 markMountUncertainErr := actualStateOfWorld.MarkVolumeMountAsUncertain(opts) 794 if markMountUncertainErr != nil { 795 // There is nothing else we can do. Hope that UnmountVolume will be re-tried shortly. 796 klog.Errorf(volumeToUnmount.GenerateErrorDetailed("UnmountVolume.MarkVolumeMountAsUncertain failed", markMountUncertainErr).Error()) 797 } 798 799 // On failure, return error. Caller will log and retry. 800 eventErr, detailedErr := volumeToUnmount.GenerateError("UnmountVolume.TearDown failed", unmountErr) 801 return volumetypes.NewOperationContext(eventErr, detailedErr, migrated) 802 } 803 804 klog.Infof( 805 "UnmountVolume.TearDown succeeded for volume %q (OuterVolumeSpecName: %q) pod %q (UID: %q). InnerVolumeSpecName %q. PluginName %q, VolumeGidValue %q", 806 volumeToUnmount.VolumeName, 807 volumeToUnmount.OuterVolumeSpecName, 808 volumeToUnmount.PodName, 809 volumeToUnmount.PodUID, 810 volumeToUnmount.InnerVolumeSpecName, 811 volumeToUnmount.PluginName, 812 volumeToUnmount.VolumeGidValue) 813 814 // Update actual state of world 815 markVolMountedErr := actualStateOfWorld.MarkVolumeAsUnmounted( 816 volumeToUnmount.PodName, volumeToUnmount.VolumeName) 817 if markVolMountedErr != nil { 818 // On failure, just log and exit 819 klog.Errorf(volumeToUnmount.GenerateErrorDetailed("UnmountVolume.MarkVolumeAsUnmounted failed", markVolMountedErr).Error()) 820 } 821 822 return volumetypes.NewOperationContext(nil, nil, migrated) 823 } 824 825 return volumetypes.GeneratedOperations{ 826 OperationName: "volume_unmount", 827 OperationFunc: unmountVolumeFunc, 828 CompleteFunc: util.OperationCompleteHook(util.GetFullQualifiedPluginNameForVolume(volumePlugin.GetPluginName(), volumeToUnmount.VolumeSpec), "volume_unmount"), 829 EventRecorderFunc: nil, // nil because we do not want to generate event on error 830 }, nil 831 } 832 833 func (og *operationGenerator) GenerateUnmountDeviceFunc( 834 deviceToDetach AttachedVolume, 835 actualStateOfWorld ActualStateOfWorldMounterUpdater, 836 hostutil hostutil.HostUtils) (volumetypes.GeneratedOperations, error) { 837 // Get DeviceMounter plugin 838 deviceMountableVolumePlugin, err := 839 og.volumePluginMgr.FindDeviceMountablePluginByName(deviceToDetach.PluginName) 840 if err != nil || deviceMountableVolumePlugin == nil { 841 return volumetypes.GeneratedOperations{}, deviceToDetach.GenerateErrorDetailed("UnmountDevice.FindDeviceMountablePluginByName failed", err) 842 } 843 844 volumeDeviceUnmounter, err := deviceMountableVolumePlugin.NewDeviceUnmounter() 845 if err != nil { 846 return volumetypes.GeneratedOperations{}, deviceToDetach.GenerateErrorDetailed("UnmountDevice.NewDeviceUnmounter failed", err) 847 } 848 849 volumeDeviceMounter, err := deviceMountableVolumePlugin.NewDeviceMounter() 850 if err != nil { 851 return volumetypes.GeneratedOperations{}, deviceToDetach.GenerateErrorDetailed("UnmountDevice.NewDeviceMounter failed", err) 852 } 853 854 unmountDeviceFunc := func() volumetypes.OperationContext { 855 856 migrated := getMigratedStatusBySpec(deviceToDetach.VolumeSpec) 857 858 //deviceMountPath := deviceToDetach.DeviceMountPath 859 deviceMountPath, err := 860 volumeDeviceMounter.GetDeviceMountPath(deviceToDetach.VolumeSpec) 861 if err != nil { 862 // On failure other than "does not exist", return error. Caller will log and retry. 863 if !strings.Contains(err.Error(), "does not exist") { 864 eventErr, detailedErr := deviceToDetach.GenerateError("GetDeviceMountPath failed", err) 865 return volumetypes.NewOperationContext(eventErr, detailedErr, migrated) 866 } 867 // If the mount path could not be found, don't fail the unmount, but instead log a warning and proceed, 868 // using the value from deviceToDetach.DeviceMountPath, so that the device can be marked as unmounted 869 deviceMountPath = deviceToDetach.DeviceMountPath 870 klog.Warningf(deviceToDetach.GenerateMsgDetailed(fmt.Sprintf( 871 "GetDeviceMountPath failed, but unmount operation will proceed using deviceMountPath=%s: %v", deviceMountPath, err), "")) 872 } 873 refs, err := deviceMountableVolumePlugin.GetDeviceMountRefs(deviceMountPath) 874 875 if err != nil || util.HasMountRefs(deviceMountPath, refs) { 876 if err == nil { 877 err = fmt.Errorf("the device mount path %q is still mounted by other references %v", deviceMountPath, refs) 878 } 879 eventErr, detailedErr := deviceToDetach.GenerateError("GetDeviceMountRefs check failed", err) 880 return volumetypes.NewOperationContext(eventErr, detailedErr, migrated) 881 } 882 // Execute unmount 883 unmountDeviceErr := volumeDeviceUnmounter.UnmountDevice(deviceMountPath) 884 if unmountDeviceErr != nil { 885 // Mark the device as uncertain, so MountDevice is called for new pods. UnmountDevice may be already in progress. 886 markDeviceUncertainErr := actualStateOfWorld.MarkDeviceAsUncertain(deviceToDetach.VolumeName, deviceToDetach.DevicePath, deviceMountPath, deviceToDetach.SELinuxMountContext) 887 if markDeviceUncertainErr != nil { 888 // There is nothing else we can do. Hope that UnmountDevice will be re-tried shortly. 889 klog.Errorf(deviceToDetach.GenerateErrorDetailed("UnmountDevice.MarkDeviceAsUncertain failed", markDeviceUncertainErr).Error()) 890 } 891 892 // On failure, return error. Caller will log and retry. 893 eventErr, detailedErr := deviceToDetach.GenerateError("UnmountDevice failed", unmountDeviceErr) 894 return volumetypes.NewOperationContext(eventErr, detailedErr, migrated) 895 } 896 // Before logging that UnmountDevice succeeded and moving on, 897 // use hostutil.PathIsDevice to check if the path is a device, 898 // if so use hostutil.DeviceOpened to check if the device is in use anywhere 899 // else on the system. Retry if it returns true. 900 deviceOpened, deviceOpenedErr := isDeviceOpened(deviceToDetach, hostutil) 901 if deviceOpenedErr != nil { 902 return volumetypes.NewOperationContext(nil, deviceOpenedErr, migrated) 903 } 904 // The device is still in use elsewhere. Caller will log and retry. 905 if deviceOpened { 906 // Mark the device as uncertain, so MountDevice is called for new pods. 907 markDeviceUncertainErr := actualStateOfWorld.MarkDeviceAsUncertain(deviceToDetach.VolumeName, deviceToDetach.DevicePath, deviceMountPath, deviceToDetach.SELinuxMountContext) 908 if markDeviceUncertainErr != nil { 909 // There is nothing else we can do. Hope that UnmountDevice will be re-tried shortly. 910 klog.Errorf(deviceToDetach.GenerateErrorDetailed("UnmountDevice.MarkDeviceAsUncertain failed", markDeviceUncertainErr).Error()) 911 } 912 eventErr, detailedErr := deviceToDetach.GenerateError( 913 "UnmountDevice failed", 914 goerrors.New("the device is in use when it was no longer expected to be in use")) 915 return volumetypes.NewOperationContext(eventErr, detailedErr, migrated) 916 } 917 918 klog.Info(deviceToDetach.GenerateMsgDetailed("UnmountDevice succeeded", "")) 919 920 // Update actual state of world 921 markDeviceUnmountedErr := actualStateOfWorld.MarkDeviceAsUnmounted( 922 deviceToDetach.VolumeName) 923 if markDeviceUnmountedErr != nil { 924 // On failure, return error. Caller will log and retry. 925 eventErr, detailedErr := deviceToDetach.GenerateError("MarkDeviceAsUnmounted failed", markDeviceUnmountedErr) 926 return volumetypes.NewOperationContext(eventErr, detailedErr, migrated) 927 } 928 929 return volumetypes.NewOperationContext(nil, nil, migrated) 930 } 931 932 return volumetypes.GeneratedOperations{ 933 OperationName: "unmount_device", 934 OperationFunc: unmountDeviceFunc, 935 CompleteFunc: util.OperationCompleteHook(util.GetFullQualifiedPluginNameForVolume(deviceMountableVolumePlugin.GetPluginName(), deviceToDetach.VolumeSpec), "unmount_device"), 936 EventRecorderFunc: nil, // nil because we do not want to generate event on error 937 }, nil 938 } 939 940 // GenerateMapVolumeFunc marks volume as mounted based on following steps. 941 // If plugin is attachable, call WaitForAttach() and then mark the device 942 // as mounted. On next step, SetUpDevice is called without dependent of 943 // plugin type, but this method mainly is targeted for none attachable plugin. 944 // After setup is done, create symbolic links on both global map path and pod 945 // device map path. Once symbolic links are created, take fd lock by 946 // loopback for the device to avoid silent volume replacement. This lock 947 // will be released once no one uses the device. 948 // If all steps are completed, the volume is marked as mounted. 949 func (og *operationGenerator) GenerateMapVolumeFunc( 950 waitForAttachTimeout time.Duration, 951 volumeToMount VolumeToMount, 952 actualStateOfWorld ActualStateOfWorldMounterUpdater) (volumetypes.GeneratedOperations, error) { 953 954 // Get block volume mapper plugin 955 blockVolumePlugin, err := 956 og.volumePluginMgr.FindMapperPluginBySpec(volumeToMount.VolumeSpec) 957 if err != nil { 958 return volumetypes.GeneratedOperations{}, volumeToMount.GenerateErrorDetailed("MapVolume.FindMapperPluginBySpec failed", err) 959 } 960 961 if blockVolumePlugin == nil { 962 return volumetypes.GeneratedOperations{}, volumeToMount.GenerateErrorDetailed("MapVolume.FindMapperPluginBySpec failed to find BlockVolumeMapper plugin. Volume plugin is nil.", nil) 963 } 964 965 affinityErr := checkNodeAffinity(og, volumeToMount) 966 if affinityErr != nil { 967 eventErr, detailedErr := volumeToMount.GenerateError("MapVolume.NodeAffinity check failed", affinityErr) 968 og.recorder.Eventf(volumeToMount.Pod, v1.EventTypeWarning, kevents.FailedMountVolume, eventErr.Error()) 969 return volumetypes.GeneratedOperations{}, detailedErr 970 } 971 blockVolumeMapper, newMapperErr := blockVolumePlugin.NewBlockVolumeMapper( 972 volumeToMount.VolumeSpec, 973 volumeToMount.Pod, 974 volume.VolumeOptions{}) 975 if newMapperErr != nil { 976 eventErr, detailedErr := volumeToMount.GenerateError("MapVolume.NewBlockVolumeMapper initialization failed", newMapperErr) 977 og.recorder.Eventf(volumeToMount.Pod, v1.EventTypeWarning, kevents.FailedMapVolume, eventErr.Error()) 978 return volumetypes.GeneratedOperations{}, detailedErr 979 } 980 981 // Get attacher, if possible 982 attachableVolumePlugin, _ := 983 og.volumePluginMgr.FindAttachablePluginBySpec(volumeToMount.VolumeSpec) 984 var volumeAttacher volume.Attacher 985 if attachableVolumePlugin != nil { 986 volumeAttacher, _ = attachableVolumePlugin.NewAttacher() 987 } 988 989 mapVolumeFunc := func() (operationContext volumetypes.OperationContext) { 990 var devicePath string 991 var stagingPath string 992 993 migrated := getMigratedStatusBySpec(volumeToMount.VolumeSpec) 994 995 // Enforce ReadWriteOncePod access mode. This is also enforced during scheduling. 996 if actualStateOfWorld.IsVolumeMountedElsewhere(volumeToMount.VolumeName, volumeToMount.PodName) && 997 // Because we do not know what access mode the pod intends to use if there are multiple. 998 len(volumeToMount.VolumeSpec.PersistentVolume.Spec.AccessModes) == 1 && 999 v1helper.ContainsAccessMode(volumeToMount.VolumeSpec.PersistentVolume.Spec.AccessModes, v1.ReadWriteOncePod) { 1000 1001 err = goerrors.New("volume uses the ReadWriteOncePod access mode and is already in use by another pod") 1002 eventErr, detailedErr := volumeToMount.GenerateError("MapVolume.SetUpDevice failed", err) 1003 return volumetypes.NewOperationContext(eventErr, detailedErr, migrated) 1004 } 1005 1006 // Set up global map path under the given plugin directory using symbolic link 1007 globalMapPath, err := 1008 blockVolumeMapper.GetGlobalMapPath(volumeToMount.VolumeSpec) 1009 if err != nil { 1010 // On failure, return error. Caller will log and retry. 1011 eventErr, detailedErr := volumeToMount.GenerateError("MapVolume.GetGlobalMapPath failed", err) 1012 return volumetypes.NewOperationContext(eventErr, detailedErr, migrated) 1013 } 1014 if volumeAttacher != nil { 1015 // Wait for attachable volumes to finish attaching 1016 klog.InfoS(volumeToMount.GenerateMsgDetailed("MapVolume.WaitForAttach entering", fmt.Sprintf("DevicePath %q", volumeToMount.DevicePath)), "pod", klog.KObj(volumeToMount.Pod)) 1017 1018 devicePath, err = volumeAttacher.WaitForAttach( 1019 volumeToMount.VolumeSpec, volumeToMount.DevicePath, volumeToMount.Pod, waitForAttachTimeout) 1020 if err != nil { 1021 // On failure, return error. Caller will log and retry. 1022 eventErr, detailedErr := volumeToMount.GenerateError("MapVolume.WaitForAttach failed", err) 1023 return volumetypes.NewOperationContext(eventErr, detailedErr, migrated) 1024 } 1025 1026 klog.InfoS(volumeToMount.GenerateMsgDetailed("MapVolume.WaitForAttach succeeded", fmt.Sprintf("DevicePath %q", devicePath)), "pod", klog.KObj(volumeToMount.Pod)) 1027 1028 } 1029 // Call SetUpDevice if blockVolumeMapper implements CustomBlockVolumeMapper 1030 if customBlockVolumeMapper, ok := blockVolumeMapper.(volume.CustomBlockVolumeMapper); ok && actualStateOfWorld.GetDeviceMountState(volumeToMount.VolumeName) != DeviceGloballyMounted { 1031 var mapErr error 1032 stagingPath, mapErr = customBlockVolumeMapper.SetUpDevice() 1033 if mapErr != nil { 1034 og.markDeviceErrorState(volumeToMount, devicePath, globalMapPath, mapErr, actualStateOfWorld) 1035 // On failure, return error. Caller will log and retry. 1036 eventErr, detailedErr := volumeToMount.GenerateError("MapVolume.SetUpDevice failed", mapErr) 1037 return volumetypes.NewOperationContext(eventErr, detailedErr, migrated) 1038 } 1039 } 1040 1041 // Update actual state of world to reflect volume is globally mounted 1042 markedDevicePath := devicePath 1043 markDeviceMappedErr := actualStateOfWorld.MarkDeviceAsMounted( 1044 volumeToMount.VolumeName, markedDevicePath, globalMapPath, "") 1045 if markDeviceMappedErr != nil { 1046 // On failure, return error. Caller will log and retry. 1047 eventErr, detailedErr := volumeToMount.GenerateError("MapVolume.MarkDeviceAsMounted failed", markDeviceMappedErr) 1048 return volumetypes.NewOperationContext(eventErr, detailedErr, migrated) 1049 } 1050 1051 markVolumeOpts := MarkVolumeOpts{ 1052 PodName: volumeToMount.PodName, 1053 PodUID: volumeToMount.Pod.UID, 1054 VolumeName: volumeToMount.VolumeName, 1055 BlockVolumeMapper: blockVolumeMapper, 1056 OuterVolumeSpecName: volumeToMount.OuterVolumeSpecName, 1057 VolumeGidVolume: volumeToMount.VolumeGidValue, 1058 VolumeSpec: volumeToMount.VolumeSpec, 1059 VolumeMountState: VolumeMounted, 1060 } 1061 1062 // Call MapPodDevice if blockVolumeMapper implements CustomBlockVolumeMapper 1063 if customBlockVolumeMapper, ok := blockVolumeMapper.(volume.CustomBlockVolumeMapper); ok { 1064 // Execute driver specific map 1065 pluginDevicePath, mapErr := customBlockVolumeMapper.MapPodDevice() 1066 if mapErr != nil { 1067 // On failure, return error. Caller will log and retry. 1068 og.markVolumeErrorState(volumeToMount, markVolumeOpts, mapErr, actualStateOfWorld) 1069 eventErr, detailedErr := volumeToMount.GenerateError("MapVolume.MapPodDevice failed", mapErr) 1070 return volumetypes.NewOperationContext(eventErr, detailedErr, migrated) 1071 } 1072 1073 // From now on, the volume is mapped. Mark it as uncertain on error, 1074 // so it is is unmapped when corresponding pod is deleted. 1075 defer func() { 1076 if operationContext.EventErr != nil { 1077 errText := operationContext.EventErr.Error() 1078 og.markVolumeErrorState(volumeToMount, markVolumeOpts, volumetypes.NewUncertainProgressError(errText), actualStateOfWorld) 1079 } 1080 }() 1081 1082 // if pluginDevicePath is provided, assume attacher may not provide device 1083 // or attachment flow uses SetupDevice to get device path 1084 if len(pluginDevicePath) != 0 { 1085 devicePath = pluginDevicePath 1086 } 1087 if len(devicePath) == 0 { 1088 eventErr, detailedErr := volumeToMount.GenerateError("MapVolume failed", goerrors.New("device path of the volume is empty")) 1089 return volumetypes.NewOperationContext(eventErr, detailedErr, migrated) 1090 } 1091 } 1092 1093 // When kubelet is containerized, devicePath may be a symlink at a place unavailable to 1094 // kubelet, so evaluate it on the host and expect that it links to a device in /dev, 1095 // which will be available to containerized kubelet. If still it does not exist, 1096 // AttachFileDevice will fail. If kubelet is not containerized, eval it anyway. 1097 kvh, ok := og.GetVolumePluginMgr().Host.(volume.KubeletVolumeHost) 1098 if !ok { 1099 eventErr, detailedErr := volumeToMount.GenerateError("MapVolume type assertion error", fmt.Errorf("volume host does not implement KubeletVolumeHost interface")) 1100 return volumetypes.NewOperationContext(eventErr, detailedErr, migrated) 1101 } 1102 hu := kvh.GetHostUtil() 1103 devicePath, err = hu.EvalHostSymlinks(devicePath) 1104 if err != nil { 1105 eventErr, detailedErr := volumeToMount.GenerateError("MapVolume.EvalHostSymlinks failed", err) 1106 return volumetypes.NewOperationContext(eventErr, detailedErr, migrated) 1107 } 1108 1109 // Update actual state of world with the devicePath again, if devicePath has changed from markedDevicePath 1110 // TODO: This can be improved after #82492 is merged and ASW has state. 1111 if markedDevicePath != devicePath { 1112 markDeviceMappedErr := actualStateOfWorld.MarkDeviceAsMounted( 1113 volumeToMount.VolumeName, devicePath, globalMapPath, "") 1114 if markDeviceMappedErr != nil { 1115 // On failure, return error. Caller will log and retry. 1116 eventErr, detailedErr := volumeToMount.GenerateError("MapVolume.MarkDeviceAsMounted failed", markDeviceMappedErr) 1117 return volumetypes.NewOperationContext(eventErr, detailedErr, migrated) 1118 } 1119 } 1120 1121 // Execute common map 1122 volumeMapPath, volName := blockVolumeMapper.GetPodDeviceMapPath() 1123 mapErr := util.MapBlockVolume(og.blkUtil, devicePath, globalMapPath, volumeMapPath, volName, volumeToMount.Pod.UID) 1124 if mapErr != nil { 1125 // On failure, return error. Caller will log and retry. 1126 eventErr, detailedErr := volumeToMount.GenerateError("MapVolume.MapBlockVolume failed", mapErr) 1127 return volumetypes.NewOperationContext(eventErr, detailedErr, migrated) 1128 } 1129 1130 // Device mapping for global map path succeeded 1131 simpleMsg, detailedMsg := volumeToMount.GenerateMsg("MapVolume.MapPodDevice succeeded", fmt.Sprintf("globalMapPath %q", globalMapPath)) 1132 verbosity := klog.Level(4) 1133 og.recorder.Eventf(volumeToMount.Pod, v1.EventTypeNormal, kevents.SuccessfulMountVolume, simpleMsg) 1134 klog.V(verbosity).InfoS(detailedMsg, "pod", klog.KObj(volumeToMount.Pod)) 1135 1136 // Device mapping for pod device map path succeeded 1137 simpleMsg, detailedMsg = volumeToMount.GenerateMsg("MapVolume.MapPodDevice succeeded", fmt.Sprintf("volumeMapPath %q", volumeMapPath)) 1138 verbosity = klog.Level(1) 1139 og.recorder.Eventf(volumeToMount.Pod, v1.EventTypeNormal, kevents.SuccessfulMountVolume, simpleMsg) 1140 klog.V(verbosity).InfoS(detailedMsg, "pod", klog.KObj(volumeToMount.Pod)) 1141 1142 resizeOptions := volume.NodeResizeOptions{ 1143 DevicePath: devicePath, 1144 DeviceStagePath: stagingPath, 1145 } 1146 _, resizeError := og.expandVolumeDuringMount(volumeToMount, actualStateOfWorld, resizeOptions) 1147 if resizeError != nil { 1148 klog.Errorf("MapVolume.NodeExpandVolume failed with %v", resizeError) 1149 eventErr, detailedErr := volumeToMount.GenerateError("MapVolume.MarkVolumeAsMounted failed while expanding volume", resizeError) 1150 // At this point, MountVolume.Setup already succeeded, we should add volume into actual state 1151 // so that reconciler can clean up volume when needed. However, if nodeExpandVolume failed, 1152 // we should not mark the volume as mounted to avoid pod starts using it. 1153 // Considering the above situations, we mark volume as uncertain here so that reconciler will trigger 1154 // volume tear down when pod is deleted, and also makes sure pod will not start using it. 1155 if err := actualStateOfWorld.MarkVolumeMountAsUncertain(markVolumeOpts); err != nil { 1156 klog.Errorf(volumeToMount.GenerateErrorDetailed("MountVolume.MarkVolumeMountAsUncertain failed", err).Error()) 1157 } 1158 return volumetypes.NewOperationContext(eventErr, detailedErr, migrated) 1159 } 1160 1161 markVolMountedErr := actualStateOfWorld.MarkVolumeAsMounted(markVolumeOpts) 1162 if markVolMountedErr != nil { 1163 // On failure, return error. Caller will log and retry. 1164 eventErr, detailedErr := volumeToMount.GenerateError("MapVolume.MarkVolumeAsMounted failed", markVolMountedErr) 1165 return volumetypes.NewOperationContext(eventErr, detailedErr, migrated) 1166 } 1167 1168 return volumetypes.NewOperationContext(nil, nil, migrated) 1169 } 1170 1171 eventRecorderFunc := func(err *error) { 1172 if *err != nil { 1173 og.recorder.Eventf(volumeToMount.Pod, v1.EventTypeWarning, kevents.FailedMapVolume, (*err).Error()) 1174 } 1175 } 1176 1177 return volumetypes.GeneratedOperations{ 1178 OperationName: "map_volume", 1179 OperationFunc: mapVolumeFunc, 1180 EventRecorderFunc: eventRecorderFunc, 1181 CompleteFunc: util.OperationCompleteHook(util.GetFullQualifiedPluginNameForVolume(blockVolumePlugin.GetPluginName(), volumeToMount.VolumeSpec), "map_volume"), 1182 }, nil 1183 } 1184 1185 // GenerateUnmapVolumeFunc marks volume as unmonuted based on following steps. 1186 // Remove symbolic links from pod device map path dir and global map path dir. 1187 // Once those cleanups are done, remove pod device map path dir. 1188 // If all steps are completed, the volume is marked as unmounted. 1189 func (og *operationGenerator) GenerateUnmapVolumeFunc( 1190 volumeToUnmount MountedVolume, 1191 actualStateOfWorld ActualStateOfWorldMounterUpdater) (volumetypes.GeneratedOperations, error) { 1192 1193 // Get block volume unmapper plugin 1194 blockVolumePlugin, err := 1195 og.volumePluginMgr.FindMapperPluginByName(volumeToUnmount.PluginName) 1196 if err != nil { 1197 return volumetypes.GeneratedOperations{}, volumeToUnmount.GenerateErrorDetailed("UnmapVolume.FindMapperPluginByName failed", err) 1198 } 1199 if blockVolumePlugin == nil { 1200 return volumetypes.GeneratedOperations{}, volumeToUnmount.GenerateErrorDetailed("UnmapVolume.FindMapperPluginByName failed to find BlockVolumeMapper plugin. Volume plugin is nil.", nil) 1201 } 1202 blockVolumeUnmapper, newUnmapperErr := blockVolumePlugin.NewBlockVolumeUnmapper( 1203 volumeToUnmount.InnerVolumeSpecName, volumeToUnmount.PodUID) 1204 if newUnmapperErr != nil { 1205 return volumetypes.GeneratedOperations{}, volumeToUnmount.GenerateErrorDetailed("UnmapVolume.NewUnmapper failed", newUnmapperErr) 1206 } 1207 1208 unmapVolumeFunc := func() volumetypes.OperationContext { 1209 1210 migrated := getMigratedStatusBySpec(volumeToUnmount.VolumeSpec) 1211 1212 // pods/{podUid}/volumeDevices/{escapeQualifiedPluginName}/{volumeName} 1213 podDeviceUnmapPath, volName := blockVolumeUnmapper.GetPodDeviceMapPath() 1214 // plugins/kubernetes.io/{PluginName}/volumeDevices/{volumePluginDependentPath}/{podUID} 1215 globalUnmapPath, err := blockVolumeUnmapper.GetGlobalMapPath(volumeToUnmount.VolumeSpec) 1216 if err != nil { 1217 // On failure, return error. Caller will log and retry. 1218 eventErr, detailedErr := volumeToUnmount.GenerateError("UnmapVolume.GetGlobalMapPath failed", err) 1219 return volumetypes.NewOperationContext(eventErr, detailedErr, migrated) 1220 } 1221 1222 // Mark the device as uncertain to make sure kubelet calls UnmapDevice again in all the "return err" 1223 // cases below. The volume is marked as fully un-mapped at the end of this function, when everything 1224 // succeeds. 1225 markVolumeOpts := MarkVolumeOpts{ 1226 PodName: volumeToUnmount.PodName, 1227 PodUID: volumeToUnmount.PodUID, 1228 VolumeName: volumeToUnmount.VolumeName, 1229 OuterVolumeSpecName: volumeToUnmount.OuterVolumeSpecName, 1230 VolumeGidVolume: volumeToUnmount.VolumeGidValue, 1231 VolumeSpec: volumeToUnmount.VolumeSpec, 1232 VolumeMountState: VolumeMountUncertain, 1233 } 1234 markVolumeUncertainErr := actualStateOfWorld.MarkVolumeMountAsUncertain(markVolumeOpts) 1235 if markVolumeUncertainErr != nil { 1236 // On failure, return error. Caller will log and retry. 1237 eventErr, detailedErr := volumeToUnmount.GenerateError("UnmapVolume.MarkDeviceAsUncertain failed", markVolumeUncertainErr) 1238 return volumetypes.NewOperationContext(eventErr, detailedErr, migrated) 1239 } 1240 1241 // Execute common unmap 1242 unmapErr := util.UnmapBlockVolume(og.blkUtil, globalUnmapPath, podDeviceUnmapPath, volName, volumeToUnmount.PodUID) 1243 if unmapErr != nil { 1244 // On failure, return error. Caller will log and retry. 1245 eventErr, detailedErr := volumeToUnmount.GenerateError("UnmapVolume.UnmapBlockVolume failed", unmapErr) 1246 return volumetypes.NewOperationContext(eventErr, detailedErr, migrated) 1247 } 1248 1249 // Call UnmapPodDevice if blockVolumeUnmapper implements CustomBlockVolumeUnmapper 1250 if customBlockVolumeUnmapper, ok := blockVolumeUnmapper.(volume.CustomBlockVolumeUnmapper); ok { 1251 // Execute plugin specific unmap 1252 unmapErr = customBlockVolumeUnmapper.UnmapPodDevice() 1253 if unmapErr != nil { 1254 // On failure, return error. Caller will log and retry. 1255 eventErr, detailedErr := volumeToUnmount.GenerateError("UnmapVolume.UnmapPodDevice failed", unmapErr) 1256 return volumetypes.NewOperationContext(eventErr, detailedErr, migrated) 1257 } 1258 } 1259 1260 klog.Infof( 1261 "UnmapVolume succeeded for volume %q (OuterVolumeSpecName: %q) pod %q (UID: %q). InnerVolumeSpecName %q. PluginName %q, VolumeGidValue %q", 1262 volumeToUnmount.VolumeName, 1263 volumeToUnmount.OuterVolumeSpecName, 1264 volumeToUnmount.PodName, 1265 volumeToUnmount.PodUID, 1266 volumeToUnmount.InnerVolumeSpecName, 1267 volumeToUnmount.PluginName, 1268 volumeToUnmount.VolumeGidValue) 1269 1270 // Update actual state of world 1271 markVolUnmountedErr := actualStateOfWorld.MarkVolumeAsUnmounted( 1272 volumeToUnmount.PodName, volumeToUnmount.VolumeName) 1273 if markVolUnmountedErr != nil { 1274 // On failure, just log and exit 1275 klog.Errorf(volumeToUnmount.GenerateErrorDetailed("UnmapVolume.MarkVolumeAsUnmounted failed", markVolUnmountedErr).Error()) 1276 } 1277 1278 return volumetypes.NewOperationContext(nil, nil, migrated) 1279 } 1280 1281 return volumetypes.GeneratedOperations{ 1282 OperationName: "unmap_volume", 1283 OperationFunc: unmapVolumeFunc, 1284 CompleteFunc: util.OperationCompleteHook(util.GetFullQualifiedPluginNameForVolume(blockVolumePlugin.GetPluginName(), volumeToUnmount.VolumeSpec), "unmap_volume"), 1285 EventRecorderFunc: nil, // nil because we do not want to generate event on error 1286 }, nil 1287 } 1288 1289 // GenerateUnmapDeviceFunc marks device as unmounted based on following steps. 1290 // Check under globalMapPath dir if there isn't pod's symbolic links in it. 1291 // If symbolic link isn't there, the device isn't referenced from Pods. 1292 // Call plugin TearDownDevice to clean-up device connection, stored data under 1293 // globalMapPath, these operations depend on plugin implementation. 1294 // Once TearDownDevice is completed, remove globalMapPath dir. 1295 // After globalMapPath is removed, fd lock by loopback for the device can 1296 // be released safely because no one can consume the device at this point. 1297 // At last, device open status will be checked just in case. 1298 // If all steps are completed, the device is marked as unmounted. 1299 func (og *operationGenerator) GenerateUnmapDeviceFunc( 1300 deviceToDetach AttachedVolume, 1301 actualStateOfWorld ActualStateOfWorldMounterUpdater, 1302 hostutil hostutil.HostUtils) (volumetypes.GeneratedOperations, error) { 1303 1304 blockVolumePlugin, err := 1305 og.volumePluginMgr.FindMapperPluginByName(deviceToDetach.PluginName) 1306 if err != nil { 1307 return volumetypes.GeneratedOperations{}, deviceToDetach.GenerateErrorDetailed("UnmapDevice.FindMapperPluginByName failed", err) 1308 } 1309 1310 if blockVolumePlugin == nil { 1311 return volumetypes.GeneratedOperations{}, deviceToDetach.GenerateErrorDetailed("UnmapDevice.FindMapperPluginByName failed to find BlockVolumeMapper plugin. Volume plugin is nil.", nil) 1312 } 1313 1314 blockVolumeUnmapper, newUnmapperErr := blockVolumePlugin.NewBlockVolumeUnmapper( 1315 deviceToDetach.VolumeSpec.Name(), 1316 "" /* podUID */) 1317 if newUnmapperErr != nil { 1318 return volumetypes.GeneratedOperations{}, deviceToDetach.GenerateErrorDetailed("UnmapDevice.NewUnmapper failed", newUnmapperErr) 1319 } 1320 1321 unmapDeviceFunc := func() volumetypes.OperationContext { 1322 migrated := getMigratedStatusBySpec(deviceToDetach.VolumeSpec) 1323 // Search under globalMapPath dir if all symbolic links from pods have been removed already. 1324 // If symbolic links are there, pods may still refer the volume. 1325 globalMapPath := deviceToDetach.DeviceMountPath 1326 refs, err := og.blkUtil.GetDeviceBindMountRefs(deviceToDetach.DevicePath, globalMapPath) 1327 if err != nil { 1328 if os.IsNotExist(err) { 1329 // Looks like SetupDevice did not complete. Fall through to TearDownDevice and mark the device as unmounted. 1330 refs = nil 1331 } else { 1332 eventErr, detailedErr := deviceToDetach.GenerateError("UnmapDevice.GetDeviceBindMountRefs check failed", err) 1333 return volumetypes.NewOperationContext(eventErr, detailedErr, migrated) 1334 } 1335 } 1336 if len(refs) > 0 { 1337 err = fmt.Errorf("the device %q is still referenced from other Pods %v", globalMapPath, refs) 1338 eventErr, detailedErr := deviceToDetach.GenerateError("UnmapDevice failed", err) 1339 return volumetypes.NewOperationContext(eventErr, detailedErr, migrated) 1340 } 1341 1342 // Mark the device as uncertain to make sure kubelet calls UnmapDevice again in all the "return err" 1343 // cases below. The volume is marked as fully un-mapped at the end of this function, when everything 1344 // succeeds. 1345 markDeviceUncertainErr := actualStateOfWorld.MarkDeviceAsUncertain( 1346 deviceToDetach.VolumeName, deviceToDetach.DevicePath, globalMapPath, "" /* seLinuxMountContext */) 1347 if markDeviceUncertainErr != nil { 1348 // On failure, return error. Caller will log and retry. 1349 eventErr, detailedErr := deviceToDetach.GenerateError("UnmapDevice.MarkDeviceAsUncertain failed", markDeviceUncertainErr) 1350 return volumetypes.NewOperationContext(eventErr, detailedErr, migrated) 1351 } 1352 1353 // Call TearDownDevice if blockVolumeUnmapper implements CustomBlockVolumeUnmapper 1354 if customBlockVolumeUnmapper, ok := blockVolumeUnmapper.(volume.CustomBlockVolumeUnmapper); ok { 1355 // Execute tear down device 1356 unmapErr := customBlockVolumeUnmapper.TearDownDevice(globalMapPath, deviceToDetach.DevicePath) 1357 if unmapErr != nil { 1358 // On failure, return error. Caller will log and retry. 1359 eventErr, detailedErr := deviceToDetach.GenerateError("UnmapDevice.TearDownDevice failed", unmapErr) 1360 return volumetypes.NewOperationContext(eventErr, detailedErr, migrated) 1361 } 1362 } 1363 1364 // Plugin finished TearDownDevice(). Now globalMapPath dir and plugin's stored data 1365 // on the dir are unnecessary, clean up it. 1366 removeMapPathErr := og.blkUtil.RemoveMapPath(globalMapPath) 1367 if removeMapPathErr != nil { 1368 // On failure, return error. Caller will log and retry. 1369 eventErr, detailedErr := deviceToDetach.GenerateError("UnmapDevice.RemoveMapPath failed", removeMapPathErr) 1370 return volumetypes.NewOperationContext(eventErr, detailedErr, migrated) 1371 } 1372 1373 // Before logging that UnmapDevice succeeded and moving on, 1374 // use hostutil.PathIsDevice to check if the path is a device, 1375 // if so use hostutil.DeviceOpened to check if the device is in use anywhere 1376 // else on the system. Retry if it returns true. 1377 deviceOpened, deviceOpenedErr := isDeviceOpened(deviceToDetach, hostutil) 1378 if deviceOpenedErr != nil { 1379 return volumetypes.NewOperationContext(nil, deviceOpenedErr, migrated) 1380 } 1381 // The device is still in use elsewhere. Caller will log and retry. 1382 if deviceOpened { 1383 eventErr, detailedErr := deviceToDetach.GenerateError( 1384 "UnmapDevice failed", 1385 fmt.Errorf("the device is in use when it was no longer expected to be in use")) 1386 return volumetypes.NewOperationContext(eventErr, detailedErr, migrated) 1387 } 1388 1389 klog.Infof(deviceToDetach.GenerateMsgDetailed("UnmapDevice succeeded", "")) 1390 1391 // Update actual state of world 1392 markDeviceUnmountedErr := actualStateOfWorld.MarkDeviceAsUnmounted( 1393 deviceToDetach.VolumeName) 1394 if markDeviceUnmountedErr != nil { 1395 // On failure, return error. Caller will log and retry. 1396 eventErr, detailedErr := deviceToDetach.GenerateError("MarkDeviceAsUnmounted failed", markDeviceUnmountedErr) 1397 return volumetypes.NewOperationContext(eventErr, detailedErr, migrated) 1398 } 1399 1400 return volumetypes.NewOperationContext(nil, nil, migrated) 1401 } 1402 1403 return volumetypes.GeneratedOperations{ 1404 OperationName: "unmap_device", 1405 OperationFunc: unmapDeviceFunc, 1406 CompleteFunc: util.OperationCompleteHook(util.GetFullQualifiedPluginNameForVolume(blockVolumePlugin.GetPluginName(), deviceToDetach.VolumeSpec), "unmap_device"), 1407 EventRecorderFunc: nil, // nil because we do not want to generate event on error 1408 }, nil 1409 } 1410 1411 func (og *operationGenerator) GenerateVerifyControllerAttachedVolumeFunc( 1412 logger klog.Logger, 1413 volumeToMount VolumeToMount, 1414 nodeName types.NodeName, 1415 actualStateOfWorld ActualStateOfWorldAttacherUpdater) (volumetypes.GeneratedOperations, error) { 1416 volumePlugin, err := 1417 og.volumePluginMgr.FindPluginBySpec(volumeToMount.VolumeSpec) 1418 if err != nil || volumePlugin == nil { 1419 return volumetypes.GeneratedOperations{}, volumeToMount.GenerateErrorDetailed("VerifyControllerAttachedVolume.FindPluginBySpec failed", err) 1420 } 1421 1422 // For attachable volume types, lets check if volume is attached by reading from node lister. 1423 // This would avoid exponential back-off and creation of goroutine unnecessarily. We still 1424 // verify status of attached volume by directly reading from API server later on.This is necessarily 1425 // to ensure any race conditions because of cached state in the informer. 1426 if volumeToMount.PluginIsAttachable { 1427 cachedAttachedVolumes, _ := og.volumePluginMgr.Host.GetAttachedVolumesFromNodeStatus() 1428 if cachedAttachedVolumes != nil { 1429 _, volumeFound := cachedAttachedVolumes[volumeToMount.VolumeName] 1430 if !volumeFound { 1431 return volumetypes.GeneratedOperations{}, NewMountPreConditionFailedError(fmt.Sprintf("volume %s is not yet in node's status", volumeToMount.VolumeName)) 1432 } 1433 } 1434 } 1435 1436 verifyControllerAttachedVolumeFunc := func() volumetypes.OperationContext { 1437 migrated := getMigratedStatusBySpec(volumeToMount.VolumeSpec) 1438 claimSize := actualStateOfWorld.GetClaimSize(volumeToMount.VolumeName) 1439 1440 // only fetch claimSize if it was not set previously 1441 if volumeToMount.VolumeSpec.PersistentVolume != nil && claimSize == nil && !volumeToMount.VolumeSpec.InlineVolumeSpecForCSIMigration { 1442 pv := volumeToMount.VolumeSpec.PersistentVolume 1443 pvc, err := og.kubeClient.CoreV1().PersistentVolumeClaims(pv.Spec.ClaimRef.Namespace).Get(context.TODO(), pv.Spec.ClaimRef.Name, metav1.GetOptions{}) 1444 if err != nil { 1445 eventErr, detailedErr := volumeToMount.GenerateError("VerifyControllerAttachedVolume fetching pvc failed", err) 1446 return volumetypes.NewOperationContext(eventErr, detailedErr, migrated) 1447 } 1448 pvcStatusSize := pvc.Status.Capacity.Storage() 1449 if pvcStatusSize != nil { 1450 claimSize = pvcStatusSize 1451 } 1452 } 1453 1454 if !volumeToMount.PluginIsAttachable { 1455 // If the volume does not implement the attacher interface, it is 1456 // assumed to be attached and the actual state of the world is 1457 // updated accordingly. 1458 1459 addVolumeNodeErr := actualStateOfWorld.MarkVolumeAsAttached( 1460 logger, volumeToMount.VolumeName, volumeToMount.VolumeSpec, nodeName, "" /* devicePath */) 1461 if addVolumeNodeErr != nil { 1462 // On failure, return error. Caller will log and retry. 1463 eventErr, detailedErr := volumeToMount.GenerateError("VerifyControllerAttachedVolume.MarkVolumeAsAttachedByUniqueVolumeName failed", addVolumeNodeErr) 1464 return volumetypes.NewOperationContext(eventErr, detailedErr, migrated) 1465 } 1466 actualStateOfWorld.InitializeClaimSize(logger, volumeToMount.VolumeName, claimSize) 1467 return volumetypes.NewOperationContext(nil, nil, migrated) 1468 } 1469 1470 if !volumeToMount.ReportedInUse { 1471 // If the given volume has not yet been added to the list of 1472 // VolumesInUse in the node's volume status, do not proceed, return 1473 // error. Caller will log and retry. The node status is updated 1474 // periodically by kubelet, so it may take as much as 10 seconds 1475 // before this clears. 1476 // Issue #28141 to enable on demand status updates. 1477 eventErr, detailedErr := volumeToMount.GenerateError("Volume has not been added to the list of VolumesInUse in the node's volume status", nil) 1478 return volumetypes.NewOperationContext(eventErr, detailedErr, migrated) 1479 } 1480 1481 // Fetch current node object 1482 node, fetchErr := og.kubeClient.CoreV1().Nodes().Get(context.TODO(), string(nodeName), metav1.GetOptions{}) 1483 if fetchErr != nil { 1484 // On failure, return error. Caller will log and retry. 1485 eventErr, detailedErr := volumeToMount.GenerateError("VerifyControllerAttachedVolume failed fetching node from API server", fetchErr) 1486 return volumetypes.NewOperationContext(eventErr, detailedErr, migrated) 1487 } 1488 1489 for _, attachedVolume := range node.Status.VolumesAttached { 1490 if attachedVolume.Name == volumeToMount.VolumeName { 1491 addVolumeNodeErr := actualStateOfWorld.MarkVolumeAsAttached( 1492 logger, v1.UniqueVolumeName(""), volumeToMount.VolumeSpec, nodeName, attachedVolume.DevicePath) 1493 klog.InfoS(volumeToMount.GenerateMsgDetailed("Controller attach succeeded", fmt.Sprintf("device path: %q", attachedVolume.DevicePath)), "pod", klog.KObj(volumeToMount.Pod)) 1494 if addVolumeNodeErr != nil { 1495 // On failure, return error. Caller will log and retry. 1496 eventErr, detailedErr := volumeToMount.GenerateError("VerifyControllerAttachedVolume.MarkVolumeAsAttached failed", addVolumeNodeErr) 1497 return volumetypes.NewOperationContext(eventErr, detailedErr, migrated) 1498 } 1499 actualStateOfWorld.InitializeClaimSize(logger, volumeToMount.VolumeName, claimSize) 1500 return volumetypes.NewOperationContext(nil, nil, migrated) 1501 } 1502 } 1503 1504 // Volume not attached, return error. Caller will log and retry. 1505 eventErr, detailedErr := volumeToMount.GenerateError("Volume not attached according to node status", nil) 1506 return volumetypes.NewOperationContext(eventErr, detailedErr, migrated) 1507 } 1508 1509 return volumetypes.GeneratedOperations{ 1510 OperationName: VerifyControllerAttachedVolumeOpName, 1511 OperationFunc: verifyControllerAttachedVolumeFunc, 1512 CompleteFunc: util.OperationCompleteHook(util.GetFullQualifiedPluginNameForVolume(volumePlugin.GetPluginName(), volumeToMount.VolumeSpec), "verify_controller_attached_volume"), 1513 EventRecorderFunc: nil, // nil because we do not want to generate event on error 1514 }, nil 1515 1516 } 1517 1518 func (og *operationGenerator) verifyVolumeIsSafeToDetach( 1519 volumeToDetach AttachedVolume) error { 1520 // Fetch current node object 1521 node, fetchErr := og.kubeClient.CoreV1().Nodes().Get(context.TODO(), string(volumeToDetach.NodeName), metav1.GetOptions{}) 1522 if fetchErr != nil { 1523 if errors.IsNotFound(fetchErr) { 1524 klog.Warningf(volumeToDetach.GenerateMsgDetailed("Node not found on API server. DetachVolume will skip safe to detach check", "")) 1525 return nil 1526 } 1527 1528 // On failure, return error. Caller will log and retry. 1529 return volumeToDetach.GenerateErrorDetailed("DetachVolume failed fetching node from API server", fetchErr) 1530 } 1531 1532 for _, inUseVolume := range node.Status.VolumesInUse { 1533 if inUseVolume == volumeToDetach.VolumeName { 1534 return volumeToDetach.GenerateErrorDetailed( 1535 "DetachVolume failed", 1536 fmt.Errorf("volume is still in use by node, according to Node status")) 1537 } 1538 } 1539 1540 // Volume is not marked as in use by node 1541 klog.Infof(volumeToDetach.GenerateMsgDetailed("Verified volume is safe to detach", "")) 1542 return nil 1543 } 1544 1545 func (og *operationGenerator) GenerateExpandVolumeFunc( 1546 pvc *v1.PersistentVolumeClaim, 1547 pv *v1.PersistentVolume) (volumetypes.GeneratedOperations, error) { 1548 1549 volumeSpec := volume.NewSpecFromPersistentVolume(pv, false) 1550 1551 volumePlugin, err := og.volumePluginMgr.FindExpandablePluginBySpec(volumeSpec) 1552 if err != nil { 1553 return volumetypes.GeneratedOperations{}, fmt.Errorf("error finding plugin for expanding volume: %q with error %v", util.GetPersistentVolumeClaimQualifiedName(pvc), err) 1554 } 1555 1556 if volumePlugin == nil { 1557 return volumetypes.GeneratedOperations{}, fmt.Errorf("can not find plugin for expanding volume: %q", util.GetPersistentVolumeClaimQualifiedName(pvc)) 1558 } 1559 1560 expandVolumeFunc := func() volumetypes.OperationContext { 1561 migrated := false 1562 1563 newSize := pvc.Spec.Resources.Requests[v1.ResourceStorage] 1564 statusSize := pvc.Status.Capacity[v1.ResourceStorage] 1565 pvSize := pv.Spec.Capacity[v1.ResourceStorage] 1566 if pvSize.Cmp(newSize) < 0 { 1567 updatedSize, expandErr := volumePlugin.ExpandVolumeDevice( 1568 volumeSpec, 1569 newSize, 1570 statusSize) 1571 if expandErr != nil { 1572 detailedErr := fmt.Errorf("error expanding volume %q of plugin %q: %v", util.GetPersistentVolumeClaimQualifiedName(pvc), volumePlugin.GetPluginName(), expandErr) 1573 return volumetypes.NewOperationContext(detailedErr, detailedErr, migrated) 1574 } 1575 1576 klog.Infof("ExpandVolume succeeded for volume %s", util.GetPersistentVolumeClaimQualifiedName(pvc)) 1577 1578 newSize = updatedSize 1579 // k8s doesn't have transactions, we can't guarantee that after updating PV - updating PVC will be 1580 // successful, that is why all PVCs for which pvc.Spec.Size > pvc.Status.Size must be reprocessed 1581 // until they reflect user requested size in pvc.Status.Size 1582 _, updateErr := util.UpdatePVSize(pv, newSize, og.kubeClient) 1583 if updateErr != nil { 1584 detailedErr := fmt.Errorf("error updating PV spec capacity for volume %q with : %v", util.GetPersistentVolumeClaimQualifiedName(pvc), updateErr) 1585 return volumetypes.NewOperationContext(detailedErr, detailedErr, migrated) 1586 } 1587 1588 klog.Infof("ExpandVolume.UpdatePV succeeded for volume %s", util.GetPersistentVolumeClaimQualifiedName(pvc)) 1589 } 1590 1591 fsVolume, _ := util.CheckVolumeModeFilesystem(volumeSpec) 1592 // No Cloudprovider resize needed, lets mark resizing as done 1593 // Rest of the volume expand controller code will assume PVC as *not* resized until pvc.Status.Size 1594 // reflects user requested size. 1595 if !volumePlugin.RequiresFSResize() || !fsVolume { 1596 klog.V(4).Infof("Controller resizing done for PVC %s", util.GetPersistentVolumeClaimQualifiedName(pvc)) 1597 _, err := util.MarkResizeFinished(pvc, newSize, og.kubeClient) 1598 if err != nil { 1599 detailedErr := fmt.Errorf("error marking pvc %s as resized : %v", util.GetPersistentVolumeClaimQualifiedName(pvc), err) 1600 return volumetypes.NewOperationContext(detailedErr, detailedErr, migrated) 1601 } 1602 successMsg := fmt.Sprintf("ExpandVolume succeeded for volume %s", util.GetPersistentVolumeClaimQualifiedName(pvc)) 1603 og.recorder.Eventf(pvc, v1.EventTypeNormal, kevents.VolumeResizeSuccess, successMsg) 1604 } else { 1605 _, err := util.MarkForFSResize(pvc, og.kubeClient) 1606 if err != nil { 1607 detailedErr := fmt.Errorf("error updating pvc %s condition for fs resize : %v", util.GetPersistentVolumeClaimQualifiedName(pvc), err) 1608 klog.Warning(detailedErr) 1609 return volumetypes.NewOperationContext(nil, nil, migrated) 1610 } 1611 oldCapacity := pvc.Status.Capacity[v1.ResourceStorage] 1612 err = util.AddAnnPreResizeCapacity(pv, oldCapacity, og.kubeClient) 1613 if err != nil { 1614 detailedErr := fmt.Errorf("error updating pv %s annotation (%s) with pre-resize capacity %s: %v", pv.ObjectMeta.Name, util.AnnPreResizeCapacity, oldCapacity.String(), err) 1615 klog.Warning(detailedErr) 1616 return volumetypes.NewOperationContext(nil, nil, migrated) 1617 } 1618 1619 } 1620 return volumetypes.NewOperationContext(nil, nil, migrated) 1621 } 1622 1623 eventRecorderFunc := func(err *error) { 1624 if *err != nil { 1625 og.recorder.Eventf(pvc, v1.EventTypeWarning, kevents.VolumeResizeFailed, (*err).Error()) 1626 } 1627 } 1628 1629 return volumetypes.GeneratedOperations{ 1630 OperationName: "expand_volume", 1631 OperationFunc: expandVolumeFunc, 1632 EventRecorderFunc: eventRecorderFunc, 1633 CompleteFunc: util.OperationCompleteHook(util.GetFullQualifiedPluginNameForVolume(volumePlugin.GetPluginName(), volumeSpec), "expand_volume"), 1634 }, nil 1635 } 1636 1637 func (og *operationGenerator) GenerateExpandAndRecoverVolumeFunc( 1638 pvc *v1.PersistentVolumeClaim, 1639 pv *v1.PersistentVolume, resizerName string) (volumetypes.GeneratedOperations, error) { 1640 1641 volumeSpec := volume.NewSpecFromPersistentVolume(pv, false) 1642 1643 volumePlugin, err := og.volumePluginMgr.FindExpandablePluginBySpec(volumeSpec) 1644 if err != nil { 1645 return volumetypes.GeneratedOperations{}, fmt.Errorf("error finding plugin for expanding volume: %q with error %v", util.GetPersistentVolumeClaimQualifiedName(pvc), err) 1646 } 1647 1648 if volumePlugin == nil { 1649 return volumetypes.GeneratedOperations{}, fmt.Errorf("can not find plugin for expanding volume: %q", util.GetPersistentVolumeClaimQualifiedName(pvc)) 1650 } 1651 1652 expandVolumeFunc := func() volumetypes.OperationContext { 1653 resizeOpts := inTreeResizeOpts{ 1654 pvc: pvc, 1655 pv: pv, 1656 resizerName: resizerName, 1657 volumePlugin: volumePlugin, 1658 volumeSpec: volumeSpec, 1659 } 1660 migrated := false 1661 resp := og.expandAndRecoverFunction(resizeOpts) 1662 if resp.err != nil { 1663 return volumetypes.NewOperationContext(resp.err, resp.err, migrated) 1664 } 1665 return volumetypes.NewOperationContext(nil, nil, migrated) 1666 } 1667 1668 eventRecorderFunc := func(err *error) { 1669 if *err != nil { 1670 og.recorder.Eventf(pvc, v1.EventTypeWarning, kevents.VolumeResizeFailed, (*err).Error()) 1671 } 1672 } 1673 1674 return volumetypes.GeneratedOperations{ 1675 OperationName: "expand_volume", 1676 OperationFunc: expandVolumeFunc, 1677 EventRecorderFunc: eventRecorderFunc, 1678 CompleteFunc: util.OperationCompleteHook(util.GetFullQualifiedPluginNameForVolume(volumePlugin.GetPluginName(), volumeSpec), "expand_volume"), 1679 }, nil 1680 } 1681 1682 func (og *operationGenerator) expandAndRecoverFunction(resizeOpts inTreeResizeOpts) inTreeResizeResponse { 1683 pvc := resizeOpts.pvc 1684 pv := resizeOpts.pv 1685 resizerName := resizeOpts.resizerName 1686 volumePlugin := resizeOpts.volumePlugin 1687 volumeSpec := resizeOpts.volumeSpec 1688 1689 pvcSpecSize := pvc.Spec.Resources.Requests[v1.ResourceStorage] 1690 pvcStatusSize := pvc.Status.Capacity[v1.ResourceStorage] 1691 pvSize := pv.Spec.Capacity[v1.ResourceStorage] 1692 1693 resizeResponse := inTreeResizeResponse{ 1694 pvc: pvc, 1695 pv: pv, 1696 resizeCalled: false, 1697 } 1698 1699 // by default we are expanding to fulfill size requested in pvc.Spec.Resources 1700 newSize := pvcSpecSize 1701 1702 var resizeStatus v1.ClaimResourceStatus 1703 if status, ok := pvc.Status.AllocatedResourceStatuses[v1.ResourceStorage]; ok { 1704 resizeStatus = status 1705 } 1706 1707 var allocatedSize *resource.Quantity 1708 t, ok := pvc.Status.AllocatedResources[v1.ResourceStorage] 1709 if ok { 1710 allocatedSize = &t 1711 } 1712 var err error 1713 1714 if pvSize.Cmp(pvcSpecSize) < 0 { 1715 // pv is not of requested size yet and hence will require expanding 1716 1717 switch resizeStatus { 1718 case v1.PersistentVolumeClaimControllerResizeInProgress, 1719 v1.PersistentVolumeClaimNodeResizePending, 1720 v1.PersistentVolumeClaimNodeResizeInProgress, 1721 v1.PersistentVolumeClaimNodeResizeFailed: 1722 if allocatedSize != nil { 1723 newSize = *allocatedSize 1724 } 1725 default: 1726 newSize = pvcSpecSize 1727 } 1728 } else { 1729 // PV has already been expanded and hence we can be here for following reasons: 1730 // 1. If expansion is pending on the node and this was just a spurious update event 1731 // we don't need to do anything and let kubelet handle it. 1732 // 2. It could be that - although we successfully expanded the volume, we failed to 1733 // record our work in API objects, in which case - we should resume resizing operation 1734 // and let API objects be updated. 1735 // 3. Controller successfully expanded the volume, but expansion is failing on the node 1736 // and before kubelet can retry failed node expansion - controller must verify if it is 1737 // safe to do so. 1738 // 4. While expansion was still pending on the node, user reduced the pvc size. 1739 switch resizeStatus { 1740 case v1.PersistentVolumeClaimNodeResizeInProgress, 1741 v1.PersistentVolumeClaimNodeResizePending: 1742 // we don't need to do any work. We could be here because of a spurious update event. 1743 // This is case #1 1744 return resizeResponse 1745 case v1.PersistentVolumeClaimNodeResizeFailed: 1746 // This is case#3 1747 pvc, err = og.markForPendingNodeExpansion(pvc, pv) 1748 resizeResponse.pvc = pvc 1749 resizeResponse.err = err 1750 return resizeResponse 1751 case v1.PersistentVolumeClaimControllerResizeInProgress, 1752 v1.PersistentVolumeClaimControllerResizeFailed: 1753 // This is case#2 or it could also be case#4 when user manually shrunk the PVC 1754 // after expanding it. 1755 if allocatedSize != nil { 1756 newSize = *allocatedSize 1757 } 1758 default: 1759 // It is impossible for ResizeStatus to be "" and allocatedSize to be not nil but somehow 1760 // if we do end up in this state, it is safest to resume expansion to last recorded size in 1761 // allocatedSize variable. 1762 if resizeStatus == "" && allocatedSize != nil { 1763 newSize = *allocatedSize 1764 } else { 1765 newSize = pvcSpecSize 1766 } 1767 } 1768 } 1769 1770 pvc, err = util.MarkControllerReisizeInProgress(pvc, resizerName, newSize, og.kubeClient) 1771 if err != nil { 1772 msg := fmt.Errorf("error updating pvc %s with resize in progress: %v", util.GetPersistentVolumeClaimQualifiedName(pvc), err) 1773 resizeResponse.err = msg 1774 resizeResponse.pvc = pvc 1775 return resizeResponse 1776 } 1777 1778 updatedSize, err := volumePlugin.ExpandVolumeDevice(volumeSpec, newSize, pvcStatusSize) 1779 resizeResponse.resizeCalled = true 1780 1781 if err != nil { 1782 msg := fmt.Errorf("error expanding pvc %s: %v", util.GetPersistentVolumeClaimQualifiedName(pvc), err) 1783 resizeResponse.err = msg 1784 resizeResponse.pvc = pvc 1785 return resizeResponse 1786 } 1787 1788 // update PV size 1789 var updateErr error 1790 pv, updateErr = util.UpdatePVSize(pv, updatedSize, og.kubeClient) 1791 // if updating PV failed, we are going to leave the PVC in ControllerExpansionInProgress state, so as expansion can be retried to previously set allocatedSize value. 1792 if updateErr != nil { 1793 msg := fmt.Errorf("error updating pv for pvc %s: %v", util.GetPersistentVolumeClaimQualifiedName(pvc), updateErr) 1794 resizeResponse.err = msg 1795 return resizeResponse 1796 } 1797 resizeResponse.pv = pv 1798 1799 fsVolume, _ := util.CheckVolumeModeFilesystem(volumeSpec) 1800 1801 if !volumePlugin.RequiresFSResize() || !fsVolume { 1802 pvc, err = util.MarkResizeFinished(pvc, updatedSize, og.kubeClient) 1803 if err != nil { 1804 msg := fmt.Errorf("error marking pvc %s as resized: %v", util.GetPersistentVolumeClaimQualifiedName(pvc), err) 1805 resizeResponse.err = msg 1806 return resizeResponse 1807 } 1808 resizeResponse.pvc = pvc 1809 successMsg := fmt.Sprintf("ExpandVolume succeeded for volume %s", util.GetPersistentVolumeClaimQualifiedName(pvc)) 1810 og.recorder.Eventf(pvc, v1.EventTypeNormal, kevents.VolumeResizeSuccess, successMsg) 1811 } else { 1812 pvc, err = og.markForPendingNodeExpansion(pvc, pv) 1813 resizeResponse.pvc = pvc 1814 if err != nil { 1815 msg := fmt.Errorf("error marking pvc %s for node expansion: %v", util.GetPersistentVolumeClaimQualifiedName(pvc), err) 1816 resizeResponse.err = msg 1817 return resizeResponse 1818 } 1819 } 1820 return resizeResponse 1821 } 1822 1823 func (og *operationGenerator) markForPendingNodeExpansion(pvc *v1.PersistentVolumeClaim, pv *v1.PersistentVolume) (*v1.PersistentVolumeClaim, error) { 1824 var err error 1825 pvc, err = util.MarkForFSResize(pvc, og.kubeClient) 1826 if err != nil { 1827 msg := fmt.Errorf("error marking pvc %s for node expansion: %v", util.GetPersistentVolumeClaimQualifiedName(pvc), err) 1828 return pvc, msg 1829 } 1830 // store old PVC capacity in pv, so as if PVC gets deleted while node expansion was pending 1831 // we can restore size of pvc from PV annotation and still perform expansion on the node 1832 oldCapacity := pvc.Status.Capacity[v1.ResourceStorage] 1833 err = util.AddAnnPreResizeCapacity(pv, oldCapacity, og.kubeClient) 1834 if err != nil { 1835 detailedErr := fmt.Errorf("error updating pv %s annotation (%s) with pre-resize capacity %s: %v", pv.ObjectMeta.Name, util.AnnPreResizeCapacity, oldCapacity.String(), err) 1836 klog.Warning(detailedErr) 1837 return pvc, detailedErr 1838 } 1839 return pvc, nil 1840 } 1841 1842 func (og *operationGenerator) GenerateExpandInUseVolumeFunc( 1843 volumeToMount VolumeToMount, 1844 actualStateOfWorld ActualStateOfWorldMounterUpdater, currentSize resource.Quantity) (volumetypes.GeneratedOperations, error) { 1845 1846 volumePlugin, err := 1847 og.volumePluginMgr.FindPluginBySpec(volumeToMount.VolumeSpec) 1848 if err != nil || volumePlugin == nil { 1849 return volumetypes.GeneratedOperations{}, volumeToMount.GenerateErrorDetailed("NodeExpandVolume.FindPluginBySpec failed", err) 1850 } 1851 1852 fsResizeFunc := func() volumetypes.OperationContext { 1853 var resizeDone bool 1854 var eventErr, detailedErr error 1855 migrated := false 1856 1857 if currentSize.IsZero() || volumeToMount.DesiredPersistentVolumeSize.IsZero() { 1858 err := fmt.Errorf("current or new size of the volume is not set") 1859 eventErr, detailedErr = volumeToMount.GenerateError("NodeExpandvolume.expansion failed", err) 1860 return volumetypes.NewOperationContext(eventErr, detailedErr, migrated) 1861 } 1862 1863 resizeOptions := volume.NodeResizeOptions{ 1864 VolumeSpec: volumeToMount.VolumeSpec, 1865 DevicePath: volumeToMount.DevicePath, 1866 OldSize: currentSize, 1867 NewSize: volumeToMount.DesiredPersistentVolumeSize, 1868 } 1869 fsVolume, err := util.CheckVolumeModeFilesystem(volumeToMount.VolumeSpec) 1870 if err != nil { 1871 eventErr, detailedErr = volumeToMount.GenerateError("NodeExpandvolume.CheckVolumeModeFilesystem failed", err) 1872 return volumetypes.NewOperationContext(eventErr, detailedErr, migrated) 1873 } 1874 1875 if fsVolume { 1876 volumeMounter, newMounterErr := volumePlugin.NewMounter( 1877 volumeToMount.VolumeSpec, 1878 volumeToMount.Pod, 1879 volume.VolumeOptions{}) 1880 if newMounterErr != nil { 1881 eventErr, detailedErr = volumeToMount.GenerateError("NodeExpandVolume.NewMounter initialization failed", newMounterErr) 1882 return volumetypes.NewOperationContext(eventErr, detailedErr, migrated) 1883 } 1884 1885 resizeOptions.DeviceMountPath = volumeMounter.GetPath() 1886 1887 deviceMountableVolumePlugin, _ := og.volumePluginMgr.FindDeviceMountablePluginBySpec(volumeToMount.VolumeSpec) 1888 var volumeDeviceMounter volume.DeviceMounter 1889 if deviceMountableVolumePlugin != nil { 1890 volumeDeviceMounter, _ = deviceMountableVolumePlugin.NewDeviceMounter() 1891 } 1892 1893 if volumeDeviceMounter != nil { 1894 deviceStagePath, err := volumeDeviceMounter.GetDeviceMountPath(volumeToMount.VolumeSpec) 1895 if err != nil { 1896 eventErr, detailedErr = volumeToMount.GenerateError("NodeExpandVolume.GetDeviceMountPath failed", err) 1897 return volumetypes.NewOperationContext(eventErr, detailedErr, migrated) 1898 } 1899 resizeOptions.DeviceStagePath = deviceStagePath 1900 } 1901 } else { 1902 // Get block volume mapper plugin 1903 blockVolumePlugin, err := 1904 og.volumePluginMgr.FindMapperPluginBySpec(volumeToMount.VolumeSpec) 1905 if err != nil { 1906 eventErr, detailedErr = volumeToMount.GenerateError("MapVolume.FindMapperPluginBySpec failed", err) 1907 return volumetypes.NewOperationContext(eventErr, detailedErr, migrated) 1908 } 1909 1910 if blockVolumePlugin == nil { 1911 eventErr, detailedErr = volumeToMount.GenerateError("MapVolume.FindMapperPluginBySpec failed to find BlockVolumeMapper plugin. Volume plugin is nil.", nil) 1912 return volumetypes.NewOperationContext(eventErr, detailedErr, migrated) 1913 } 1914 1915 blockVolumeMapper, newMapperErr := blockVolumePlugin.NewBlockVolumeMapper( 1916 volumeToMount.VolumeSpec, 1917 volumeToMount.Pod, 1918 volume.VolumeOptions{}) 1919 if newMapperErr != nil { 1920 eventErr, detailedErr = volumeToMount.GenerateError("MapVolume.NewBlockVolumeMapper initialization failed", newMapperErr) 1921 return volumetypes.NewOperationContext(eventErr, detailedErr, migrated) 1922 } 1923 1924 // if plugin supports custom mappers lets add DeviceStagePath 1925 if customBlockVolumeMapper, ok := blockVolumeMapper.(volume.CustomBlockVolumeMapper); ok { 1926 resizeOptions.DeviceStagePath = customBlockVolumeMapper.GetStagingPath() 1927 } 1928 } 1929 1930 // if we are doing online expansion then volume is already published 1931 resizeDone, eventErr, detailedErr = og.doOnlineExpansion(volumeToMount, actualStateOfWorld, resizeOptions) 1932 if eventErr != nil || detailedErr != nil { 1933 return volumetypes.NewOperationContext(eventErr, detailedErr, migrated) 1934 } 1935 if resizeDone { 1936 return volumetypes.NewOperationContext(nil, nil, migrated) 1937 } 1938 // This is a placeholder error - we should NEVER reach here. 1939 err = fmt.Errorf("volume resizing failed for unknown reason") 1940 eventErr, detailedErr = volumeToMount.GenerateError("NodeExpandVolume.NodeExpandVolume failed to resize volume", err) 1941 return volumetypes.NewOperationContext(eventErr, detailedErr, migrated) 1942 } 1943 1944 eventRecorderFunc := func(err *error) { 1945 if *err != nil { 1946 og.recorder.Eventf(volumeToMount.Pod, v1.EventTypeWarning, kevents.VolumeResizeFailed, (*err).Error()) 1947 } 1948 } 1949 1950 return volumetypes.GeneratedOperations{ 1951 OperationName: "volume_fs_resize", 1952 OperationFunc: fsResizeFunc, 1953 EventRecorderFunc: eventRecorderFunc, 1954 CompleteFunc: util.OperationCompleteHook(util.GetFullQualifiedPluginNameForVolume(volumePlugin.GetPluginName(), volumeToMount.VolumeSpec), "volume_fs_resize"), 1955 }, nil 1956 } 1957 1958 func (og *operationGenerator) doOnlineExpansion(volumeToMount VolumeToMount, 1959 actualStateOfWorld ActualStateOfWorldMounterUpdater, 1960 resizeOptions volume.NodeResizeOptions) (bool, error, error) { 1961 1962 resizeDone, err := og.nodeExpandVolume(volumeToMount, actualStateOfWorld, resizeOptions) 1963 if err != nil { 1964 e1, e2 := volumeToMount.GenerateError("NodeExpandVolume.NodeExpandVolume failed", err) 1965 klog.Errorf(e2.Error()) 1966 return false, e1, e2 1967 } 1968 if resizeDone { 1969 markingDone := actualStateOfWorld.MarkVolumeAsResized(volumeToMount.VolumeName, &resizeOptions.NewSize) 1970 if !markingDone { 1971 // On failure, return error. Caller will log and retry. 1972 genericFailureError := fmt.Errorf("unable to mark volume as resized") 1973 e1, e2 := volumeToMount.GenerateError("NodeExpandVolume.MarkVolumeAsResized failed", genericFailureError) 1974 return false, e1, e2 1975 } 1976 return true, nil, nil 1977 } 1978 return false, nil, nil 1979 } 1980 1981 func (og *operationGenerator) expandVolumeDuringMount(volumeToMount VolumeToMount, actualStateOfWorld ActualStateOfWorldMounterUpdater, rsOpts volume.NodeResizeOptions) (bool, error) { 1982 supportsExpansion, expandablePlugin := og.checkIfSupportsNodeExpansion(volumeToMount) 1983 if supportsExpansion { 1984 pv := volumeToMount.VolumeSpec.PersistentVolume 1985 pvc, err := og.kubeClient.CoreV1().PersistentVolumeClaims(pv.Spec.ClaimRef.Namespace).Get(context.TODO(), pv.Spec.ClaimRef.Name, metav1.GetOptions{}) 1986 if err != nil { 1987 // Return error rather than leave the file system un-resized, caller will log and retry 1988 return false, fmt.Errorf("mountVolume.NodeExpandVolume get PVC failed : %v", err) 1989 } 1990 1991 pvcStatusCap := pvc.Status.Capacity[v1.ResourceStorage] 1992 pvSpecCap := pv.Spec.Capacity[v1.ResourceStorage] 1993 if pvcStatusCap.Cmp(pvSpecCap) < 0 { 1994 if volumeToMount.VolumeSpec.ReadOnly { 1995 simpleMsg, detailedMsg := volumeToMount.GenerateMsg("MountVolume.NodeExpandVolume failed", "requested read-only file system") 1996 klog.Warningf(detailedMsg) 1997 og.recorder.Eventf(volumeToMount.Pod, v1.EventTypeWarning, kevents.FileSystemResizeFailed, simpleMsg) 1998 og.recorder.Eventf(pvc, v1.EventTypeWarning, kevents.FileSystemResizeFailed, simpleMsg) 1999 return true, nil 2000 } 2001 2002 rsOpts.NewSize = pvSpecCap 2003 rsOpts.OldSize = pvcStatusCap 2004 resizeOp := nodeResizeOperationOpts{ 2005 vmt: volumeToMount, 2006 pvc: pvc, 2007 pv: pv, 2008 pluginResizeOpts: rsOpts, 2009 volumePlugin: expandablePlugin, 2010 actualStateOfWorld: actualStateOfWorld, 2011 } 2012 if og.checkForRecoveryFromExpansion(pvc, volumeToMount) { 2013 nodeExpander := newNodeExpander(resizeOp, og.kubeClient, og.recorder) 2014 resizeFinished, err, _ := nodeExpander.expandOnPlugin() 2015 return resizeFinished, err 2016 } else { 2017 return og.legacyCallNodeExpandOnPlugin(resizeOp) 2018 } 2019 } 2020 } 2021 return true, nil 2022 } 2023 2024 func (og *operationGenerator) checkIfSupportsNodeExpansion(volumeToMount VolumeToMount) (bool, volume.NodeExpandableVolumePlugin) { 2025 if volumeToMount.VolumeSpec != nil && 2026 volumeToMount.VolumeSpec.InlineVolumeSpecForCSIMigration { 2027 klog.V(4).Infof("This volume %s is a migrated inline volume and is not resizable", volumeToMount.VolumeName) 2028 return false, nil 2029 } 2030 2031 // Get expander, if possible 2032 expandableVolumePlugin, _ := 2033 og.volumePluginMgr.FindNodeExpandablePluginBySpec(volumeToMount.VolumeSpec) 2034 if expandableVolumePlugin != nil && 2035 expandableVolumePlugin.RequiresFSResize() && 2036 volumeToMount.VolumeSpec.PersistentVolume != nil { 2037 return true, expandableVolumePlugin 2038 } 2039 return false, nil 2040 } 2041 2042 func (og *operationGenerator) nodeExpandVolume( 2043 volumeToMount VolumeToMount, 2044 actualStateOfWorld ActualStateOfWorldMounterUpdater, 2045 rsOpts volume.NodeResizeOptions) (bool, error) { 2046 2047 supportsExpansion, expandableVolumePlugin := og.checkIfSupportsNodeExpansion(volumeToMount) 2048 2049 if supportsExpansion { 2050 // lets use sizes handed over to us by caller for comparison 2051 if rsOpts.NewSize.Cmp(rsOpts.OldSize) > 0 { 2052 pv := volumeToMount.VolumeSpec.PersistentVolume 2053 pvc, err := og.kubeClient.CoreV1().PersistentVolumeClaims(pv.Spec.ClaimRef.Namespace).Get(context.TODO(), pv.Spec.ClaimRef.Name, metav1.GetOptions{}) 2054 if err != nil { 2055 // Return error rather than leave the file system un-resized, caller will log and retry 2056 return false, fmt.Errorf("mountVolume.NodeExpandVolume get PVC failed : %v", err) 2057 } 2058 2059 if volumeToMount.VolumeSpec.ReadOnly { 2060 simpleMsg, detailedMsg := volumeToMount.GenerateMsg("MountVolume.NodeExpandVolume failed", "requested read-only file system") 2061 klog.Warningf(detailedMsg) 2062 og.recorder.Eventf(volumeToMount.Pod, v1.EventTypeWarning, kevents.FileSystemResizeFailed, simpleMsg) 2063 og.recorder.Eventf(pvc, v1.EventTypeWarning, kevents.FileSystemResizeFailed, simpleMsg) 2064 return true, nil 2065 } 2066 resizeOp := nodeResizeOperationOpts{ 2067 vmt: volumeToMount, 2068 pvc: pvc, 2069 pv: pv, 2070 pluginResizeOpts: rsOpts, 2071 volumePlugin: expandableVolumePlugin, 2072 actualStateOfWorld: actualStateOfWorld, 2073 } 2074 2075 if og.checkForRecoveryFromExpansion(pvc, volumeToMount) { 2076 nodeExpander := newNodeExpander(resizeOp, og.kubeClient, og.recorder) 2077 resizeFinished, err, _ := nodeExpander.expandOnPlugin() 2078 return resizeFinished, err 2079 } else { 2080 return og.legacyCallNodeExpandOnPlugin(resizeOp) 2081 } 2082 } 2083 } 2084 return true, nil 2085 } 2086 2087 func (og *operationGenerator) checkForRecoveryFromExpansion(pvc *v1.PersistentVolumeClaim, volumeToMount VolumeToMount) bool { 2088 resizeStatus := pvc.Status.AllocatedResourceStatuses[v1.ResourceStorage] 2089 allocatedResource := pvc.Status.AllocatedResources 2090 featureGateStatus := utilfeature.DefaultFeatureGate.Enabled(features.RecoverVolumeExpansionFailure) 2091 2092 if !featureGateStatus { 2093 return false 2094 } 2095 2096 // Even though RecoverVolumeExpansionFailure feature gate is enabled, it appears that we are running with older version 2097 // of resize controller, which will not populate allocatedResource and resizeStatus. This can happen because of version skew 2098 // and hence we are going to keep expanding using older logic. 2099 if resizeStatus == "" && allocatedResource == nil { 2100 _, detailedMsg := volumeToMount.GenerateMsg("MountVolume.NodeExpandVolume running with", "older external resize controller") 2101 klog.Warningf(detailedMsg) 2102 return false 2103 } 2104 return true 2105 } 2106 2107 // legacyCallNodeExpandOnPlugin is old version of calling node expansion on plugin, which does not support 2108 // recovery from volume expansion failure 2109 // TODO: Removing this code when RecoverVolumeExpansionFailure feature goes GA. 2110 func (og *operationGenerator) legacyCallNodeExpandOnPlugin(resizeOp nodeResizeOperationOpts) (bool, error) { 2111 pvc := resizeOp.pvc 2112 volumeToMount := resizeOp.vmt 2113 rsOpts := resizeOp.pluginResizeOpts 2114 actualStateOfWorld := resizeOp.actualStateOfWorld 2115 expandableVolumePlugin := resizeOp.volumePlugin 2116 2117 pvcStatusCap := pvc.Status.Capacity[v1.ResourceStorage] 2118 2119 nodeName := volumeToMount.Pod.Spec.NodeName 2120 2121 var err error 2122 2123 // File system resize was requested, proceed 2124 klog.V(4).InfoS(volumeToMount.GenerateMsgDetailed("MountVolume.NodeExpandVolume entering", fmt.Sprintf("DevicePath %q", volumeToMount.DevicePath)), "pod", klog.KObj(volumeToMount.Pod)) 2125 2126 rsOpts.VolumeSpec = volumeToMount.VolumeSpec 2127 2128 _, resizeErr := expandableVolumePlugin.NodeExpand(rsOpts) 2129 if resizeErr != nil { 2130 // This is a workaround for now, until RecoverFromVolumeExpansionFailure feature goes GA. 2131 // If RecoverFromVolumeExpansionFailure feature is enabled, we will not ever hit this state, because 2132 // we will wait for VolumeExpansionPendingOnNode before trying to expand volume in kubelet. 2133 if volumetypes.IsOperationNotSupportedError(resizeErr) { 2134 klog.V(4).InfoS(volumeToMount.GenerateMsgDetailed("MountVolume.NodeExpandVolume failed", "NodeExpandVolume not supported"), "pod", klog.KObj(volumeToMount.Pod)) 2135 return true, nil 2136 } 2137 2138 // if driver returned FailedPrecondition error that means 2139 // volume expansion should not be retried on this node but 2140 // expansion operation should not block mounting 2141 if volumetypes.IsFailedPreconditionError(resizeErr) { 2142 actualStateOfWorld.MarkForInUseExpansionError(volumeToMount.VolumeName) 2143 klog.Errorf(volumeToMount.GenerateErrorDetailed("MountVolume.NodeExapndVolume failed", resizeErr).Error()) 2144 return true, nil 2145 } 2146 return false, resizeErr 2147 } 2148 2149 simpleMsg, detailedMsg := volumeToMount.GenerateMsg("MountVolume.NodeExpandVolume succeeded", nodeName) 2150 og.recorder.Eventf(volumeToMount.Pod, v1.EventTypeNormal, kevents.FileSystemResizeSuccess, simpleMsg) 2151 og.recorder.Eventf(pvc, v1.EventTypeNormal, kevents.FileSystemResizeSuccess, simpleMsg) 2152 klog.InfoS(detailedMsg, "pod", klog.KObj(volumeToMount.Pod)) 2153 2154 // if PVC already has new size, there is no need to update it. 2155 if pvcStatusCap.Cmp(rsOpts.NewSize) >= 0 { 2156 return true, nil 2157 } 2158 2159 // File system resize succeeded, now update the PVC's Capacity to match the PV's 2160 _, err = util.MarkFSResizeFinished(pvc, rsOpts.NewSize, og.kubeClient) 2161 if err != nil { 2162 // On retry, NodeExpandVolume will be called again but do nothing 2163 return false, fmt.Errorf("mountVolume.NodeExpandVolume update PVC status failed : %v", err) 2164 } 2165 return true, nil 2166 } 2167 2168 func checkMountOptionSupport(og *operationGenerator, volumeToMount VolumeToMount, plugin volume.VolumePlugin) error { 2169 mountOptions := util.MountOptionFromSpec(volumeToMount.VolumeSpec) 2170 2171 if len(mountOptions) > 0 && !plugin.SupportsMountOption() { 2172 return fmt.Errorf("mount options are not supported for this volume type") 2173 } 2174 return nil 2175 } 2176 2177 // checkNodeAffinity looks at the PV node affinity, and checks if the node has the same corresponding labels 2178 // This ensures that we don't mount a volume that doesn't belong to this node 2179 func checkNodeAffinity(og *operationGenerator, volumeToMount VolumeToMount) error { 2180 pv := volumeToMount.VolumeSpec.PersistentVolume 2181 if pv != nil { 2182 nodeLabels, err := og.volumePluginMgr.Host.GetNodeLabels() 2183 if err != nil { 2184 return err 2185 } 2186 err = storagehelpers.CheckNodeAffinity(pv, nodeLabels) 2187 if err != nil { 2188 return err 2189 } 2190 } 2191 return nil 2192 } 2193 2194 // isDeviceOpened checks the device status if the device is in use anywhere else on the system 2195 func isDeviceOpened(deviceToDetach AttachedVolume, hostUtil hostutil.HostUtils) (bool, error) { 2196 isDevicePath, devicePathErr := hostUtil.PathIsDevice(deviceToDetach.DevicePath) 2197 var deviceOpened bool 2198 var deviceOpenedErr error 2199 if !isDevicePath && devicePathErr == nil || 2200 (devicePathErr != nil && strings.Contains(devicePathErr.Error(), "does not exist")) { 2201 // not a device path or path doesn't exist 2202 //TODO: refer to #36092 2203 klog.V(3).Infof("The path isn't device path or doesn't exist. Skip checking device path: %s", deviceToDetach.DevicePath) 2204 deviceOpened = false 2205 } else if devicePathErr != nil { 2206 return false, deviceToDetach.GenerateErrorDetailed("PathIsDevice failed", devicePathErr) 2207 } else { 2208 deviceOpened, deviceOpenedErr = hostUtil.DeviceOpened(deviceToDetach.DevicePath) 2209 if deviceOpenedErr != nil { 2210 return false, deviceToDetach.GenerateErrorDetailed("DeviceOpened failed", deviceOpenedErr) 2211 } 2212 } 2213 return deviceOpened, nil 2214 } 2215 2216 // findDetachablePluginBySpec is a variant of VolumePluginMgr.FindAttachablePluginByName() function. 2217 // The difference is that it bypass the CanAttach() check for CSI plugin, i.e. it assumes all CSI plugin supports detach. 2218 // The intention here is that a CSI plugin volume can end up in an Uncertain state, so that a detach 2219 // operation will help it to detach no matter it actually has the ability to attach/detach. 2220 func findDetachablePluginBySpec(spec *volume.Spec, pm *volume.VolumePluginMgr) (volume.AttachableVolumePlugin, error) { 2221 volumePlugin, err := pm.FindPluginBySpec(spec) 2222 if err != nil { 2223 return nil, err 2224 } 2225 if attachableVolumePlugin, ok := volumePlugin.(volume.AttachableVolumePlugin); ok { 2226 if attachableVolumePlugin.GetPluginName() == "kubernetes.io/csi" { 2227 return attachableVolumePlugin, nil 2228 } 2229 if canAttach, err := attachableVolumePlugin.CanAttach(spec); err != nil { 2230 return nil, err 2231 } else if canAttach { 2232 return attachableVolumePlugin, nil 2233 } 2234 } 2235 return nil, nil 2236 } 2237 2238 func getMigratedStatusBySpec(spec *volume.Spec) bool { 2239 migrated := false 2240 if spec != nil { 2241 migrated = spec.Migrated 2242 } 2243 return migrated 2244 }