k8s.io/kubernetes@v1.29.3/pkg/volume/csi/csi_mounter.go (about) 1 /* 2 Copyright 2017 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 csi 18 19 import ( 20 "crypto/sha256" 21 "encoding/json" 22 "errors" 23 "fmt" 24 "os" 25 "path/filepath" 26 27 authenticationv1 "k8s.io/api/authentication/v1" 28 api "k8s.io/api/core/v1" 29 storage "k8s.io/api/storage/v1" 30 apierrors "k8s.io/apimachinery/pkg/api/errors" 31 "k8s.io/apimachinery/pkg/types" 32 utilfeature "k8s.io/apiserver/pkg/util/feature" 33 "k8s.io/client-go/kubernetes" 34 "k8s.io/klog/v2" 35 "k8s.io/kubernetes/pkg/features" 36 "k8s.io/kubernetes/pkg/volume" 37 "k8s.io/kubernetes/pkg/volume/util" 38 volumetypes "k8s.io/kubernetes/pkg/volume/util/types" 39 "k8s.io/mount-utils" 40 utilstrings "k8s.io/utils/strings" 41 ) 42 43 // TODO (vladimirvivien) move this in a central loc later 44 var ( 45 volDataKey = struct { 46 specVolID, 47 volHandle, 48 driverName, 49 nodeName, 50 attachmentID, 51 volumeLifecycleMode, 52 seLinuxMountContext string 53 }{ 54 "specVolID", 55 "volumeHandle", 56 "driverName", 57 "nodeName", 58 "attachmentID", 59 "volumeLifecycleMode", 60 "seLinuxMountContext", 61 } 62 ) 63 64 type csiMountMgr struct { 65 csiClientGetter 66 k8s kubernetes.Interface 67 plugin *csiPlugin 68 driverName csiDriverName 69 volumeLifecycleMode storage.VolumeLifecycleMode 70 volumeID string 71 specVolumeID string 72 readOnly bool 73 needSELinuxRelabel bool 74 spec *volume.Spec 75 pod *api.Pod 76 podUID types.UID 77 publishContext map[string]string 78 kubeVolHost volume.KubeletVolumeHost 79 volume.MetricsProvider 80 } 81 82 // volume.Volume methods 83 var _ volume.Volume = &csiMountMgr{} 84 85 func (c *csiMountMgr) GetPath() string { 86 dir := GetCSIMounterPath(filepath.Join(getTargetPath(c.podUID, c.specVolumeID, c.plugin.host))) 87 klog.V(4).Info(log("mounter.GetPath generated [%s]", dir)) 88 return dir 89 } 90 91 func getTargetPath(uid types.UID, specVolumeID string, host volume.VolumeHost) string { 92 specVolID := utilstrings.EscapeQualifiedName(specVolumeID) 93 return host.GetPodVolumeDir(uid, utilstrings.EscapeQualifiedName(CSIPluginName), specVolID) 94 } 95 96 // volume.Mounter methods 97 var _ volume.Mounter = &csiMountMgr{} 98 99 func (c *csiMountMgr) SetUp(mounterArgs volume.MounterArgs) error { 100 return c.SetUpAt(c.GetPath(), mounterArgs) 101 } 102 103 func (c *csiMountMgr) SetUpAt(dir string, mounterArgs volume.MounterArgs) error { 104 klog.V(4).Infof(log("Mounter.SetUpAt(%s)", dir)) 105 106 csi, err := c.csiClientGetter.Get() 107 if err != nil { 108 // Treat the absence of the CSI driver as a transient error 109 // See https://github.com/kubernetes/kubernetes/issues/120268 110 return volumetypes.NewTransientOperationFailure(log("mounter.SetUpAt failed to get CSI client: %v", err)) 111 } 112 113 ctx, cancel := createCSIOperationContext(c.spec, csiTimeout) 114 defer cancel() 115 116 volSrc, pvSrc, err := getSourceFromSpec(c.spec) 117 if err != nil { 118 return errors.New(log("mounter.SetupAt failed to get CSI persistent source: %v", err)) 119 } 120 121 // Check CSIDriver.Spec.Mode to ensure that the CSI driver 122 // supports the current volumeLifecycleMode. 123 if err := c.supportsVolumeLifecycleMode(); err != nil { 124 return volumetypes.NewTransientOperationFailure(log("mounter.SetupAt failed to check volume lifecycle mode: %s", err)) 125 } 126 127 fsGroupPolicy, err := c.getFSGroupPolicy() 128 if err != nil { 129 return volumetypes.NewTransientOperationFailure(log("mounter.SetupAt failed to check fsGroup policy: %s", err)) 130 } 131 132 driverName := c.driverName 133 volumeHandle := c.volumeID 134 readOnly := c.readOnly 135 accessMode := api.ReadWriteOnce 136 137 var ( 138 fsType string 139 volAttribs map[string]string 140 nodePublishSecrets map[string]string 141 publishContext map[string]string 142 mountOptions []string 143 deviceMountPath string 144 secretRef *api.SecretReference 145 ) 146 147 switch { 148 case volSrc != nil: 149 if c.volumeLifecycleMode != storage.VolumeLifecycleEphemeral { 150 return fmt.Errorf("unexpected volume mode: %s", c.volumeLifecycleMode) 151 } 152 if volSrc.FSType != nil { 153 fsType = *volSrc.FSType 154 } 155 156 volAttribs = volSrc.VolumeAttributes 157 158 if volSrc.NodePublishSecretRef != nil { 159 secretName := volSrc.NodePublishSecretRef.Name 160 ns := c.pod.Namespace 161 secretRef = &api.SecretReference{Name: secretName, Namespace: ns} 162 } 163 case pvSrc != nil: 164 if c.volumeLifecycleMode != storage.VolumeLifecyclePersistent { 165 return fmt.Errorf("unexpected driver mode: %s", c.volumeLifecycleMode) 166 } 167 168 fsType = pvSrc.FSType 169 170 volAttribs = pvSrc.VolumeAttributes 171 172 if pvSrc.NodePublishSecretRef != nil { 173 secretRef = pvSrc.NodePublishSecretRef 174 } 175 176 //TODO (vladimirvivien) implement better AccessModes mapping between k8s and CSI 177 if c.spec.PersistentVolume.Spec.AccessModes != nil { 178 accessMode = c.spec.PersistentVolume.Spec.AccessModes[0] 179 } 180 181 mountOptions = c.spec.PersistentVolume.Spec.MountOptions 182 183 // Check for STAGE_UNSTAGE_VOLUME set and populate deviceMountPath if so 184 stageUnstageSet, err := csi.NodeSupportsStageUnstage(ctx) 185 if err != nil { 186 return errors.New(log("mounter.SetUpAt failed to check for STAGE_UNSTAGE_VOLUME capability: %v", err)) 187 } 188 189 if stageUnstageSet { 190 deviceMountPath, err = makeDeviceMountPath(c.plugin, c.spec) 191 if err != nil { 192 return errors.New(log("mounter.SetUpAt failed to make device mount path: %v", err)) 193 } 194 } 195 196 // search for attachment by VolumeAttachment.Spec.Source.PersistentVolumeName 197 if c.publishContext == nil { 198 nodeName := string(c.plugin.host.GetNodeName()) 199 c.publishContext, err = c.plugin.getPublishContext(c.k8s, volumeHandle, string(driverName), nodeName) 200 if err != nil { 201 // we could have a transient error associated with fetching publish context 202 return volumetypes.NewTransientOperationFailure(log("mounter.SetUpAt failed to fetch publishContext: %v", err)) 203 } 204 publishContext = c.publishContext 205 } 206 207 default: 208 return fmt.Errorf("volume source not found in volume.Spec") 209 } 210 211 // create target_dir before call to NodePublish 212 parentDir := filepath.Dir(dir) 213 if err := os.MkdirAll(parentDir, 0750); err != nil { 214 return errors.New(log("mounter.SetUpAt failed to create dir %#v: %v", parentDir, err)) 215 } 216 klog.V(4).Info(log("created target path successfully [%s]", parentDir)) 217 218 nodePublishSecrets = map[string]string{} 219 if secretRef != nil { 220 nodePublishSecrets, err = getCredentialsFromSecret(c.k8s, secretRef) 221 if err != nil { 222 return volumetypes.NewTransientOperationFailure(fmt.Sprintf("fetching NodePublishSecretRef %s/%s failed: %v", 223 secretRef.Namespace, secretRef.Name, err)) 224 } 225 226 } 227 228 // Inject pod information into volume_attributes 229 podInfoEnabled, err := c.plugin.podInfoEnabled(string(c.driverName)) 230 if err != nil { 231 return volumetypes.NewTransientOperationFailure(log("mounter.SetUpAt failed to assemble volume attributes: %v", err)) 232 } 233 if podInfoEnabled { 234 volAttribs = mergeMap(volAttribs, getPodInfoAttrs(c.pod, c.volumeLifecycleMode)) 235 } 236 237 // Inject pod service account token into volume attributes 238 serviceAccountTokenAttrs, err := c.podServiceAccountTokenAttrs() 239 if err != nil { 240 return volumetypes.NewTransientOperationFailure(log("mounter.SetUpAt failed to get service accoount token attributes: %v", err)) 241 } 242 volAttribs = mergeMap(volAttribs, serviceAccountTokenAttrs) 243 244 driverSupportsCSIVolumeMountGroup := false 245 var nodePublishFSGroupArg *int64 246 driverSupportsCSIVolumeMountGroup, err = csi.NodeSupportsVolumeMountGroup(ctx) 247 if err != nil { 248 return volumetypes.NewTransientOperationFailure(log("mounter.SetUpAt failed to determine if the node service has VOLUME_MOUNT_GROUP capability: %v", err)) 249 } 250 251 if driverSupportsCSIVolumeMountGroup { 252 klog.V(3).Infof("Driver %s supports applying FSGroup (has VOLUME_MOUNT_GROUP node capability). Delegating FSGroup application to the driver through NodePublishVolume.", c.driverName) 253 nodePublishFSGroupArg = mounterArgs.FsGroup 254 } 255 256 var selinuxLabelMount bool 257 if utilfeature.DefaultFeatureGate.Enabled(features.SELinuxMountReadWriteOncePod) { 258 support, err := c.plugin.SupportsSELinuxContextMount(c.spec) 259 if err != nil { 260 return errors.New(log("failed to query for SELinuxMount support: %s", err)) 261 } 262 if support && mounterArgs.SELinuxLabel != "" { 263 mountOptions = util.AddSELinuxMountOption(mountOptions, mounterArgs.SELinuxLabel) 264 selinuxLabelMount = true 265 } 266 } 267 268 // Save volume info in pod dir 269 // persist volume info data for teardown 270 nodeName := string(c.plugin.host.GetNodeName()) 271 volData := map[string]string{ 272 volDataKey.specVolID: c.spec.Name(), 273 volDataKey.volHandle: volumeHandle, 274 volDataKey.driverName: string(c.driverName), 275 volDataKey.nodeName: nodeName, 276 volDataKey.volumeLifecycleMode: string(c.volumeLifecycleMode), 277 volDataKey.attachmentID: getAttachmentName(volumeHandle, string(c.driverName), nodeName), 278 } 279 280 if utilfeature.DefaultFeatureGate.Enabled(features.SELinuxMountReadWriteOncePod) && selinuxLabelMount { 281 volData[volDataKey.seLinuxMountContext] = mounterArgs.SELinuxLabel 282 } 283 284 err = saveVolumeData(parentDir, volDataFileName, volData) 285 defer func() { 286 // Only if there was an error and volume operation was considered 287 // finished, we should remove the directory. 288 if err != nil && volumetypes.IsOperationFinishedError(err) { 289 // attempt to cleanup volume mount dir 290 if removeerr := removeMountDir(c.plugin, dir); removeerr != nil { 291 klog.Error(log("mounter.SetUpAt failed to remove mount dir after error [%s]: %v", dir, removeerr)) 292 } 293 } 294 }() 295 if err != nil { 296 errorMsg := log("mounter.SetUpAt failed to save volume info data: %v", err) 297 klog.Error(errorMsg) 298 return volumetypes.NewTransientOperationFailure(errorMsg) 299 } 300 301 err = csi.NodePublishVolume( 302 ctx, 303 volumeHandle, 304 readOnly, 305 deviceMountPath, 306 dir, 307 accessMode, 308 publishContext, 309 volAttribs, 310 nodePublishSecrets, 311 fsType, 312 mountOptions, 313 nodePublishFSGroupArg, 314 ) 315 316 if err != nil { 317 // If operation finished with error then we can remove the mount directory. 318 if volumetypes.IsOperationFinishedError(err) { 319 if removeMountDirErr := removeMountDir(c.plugin, dir); removeMountDirErr != nil { 320 klog.Error(log("mounter.SetupAt failed to remove mount dir after a NodePublish() error [%s]: %v", dir, removeMountDirErr)) 321 } 322 } 323 return err 324 } 325 326 if !selinuxLabelMount { 327 c.needSELinuxRelabel, err = c.kubeVolHost.GetHostUtil().GetSELinuxSupport(dir) 328 if err != nil { 329 // The volume is mounted. Return UncertainProgressError, so kubelet will unmount it when user deletes the pod. 330 return volumetypes.NewUncertainProgressError(fmt.Sprintf("error checking for SELinux support: %s", err)) 331 } 332 } 333 334 if !driverSupportsCSIVolumeMountGroup && c.supportsFSGroup(fsType, mounterArgs.FsGroup, fsGroupPolicy) { 335 // Driver doesn't support applying FSGroup. Kubelet must apply it instead. 336 337 // fullPluginName helps to distinguish different driver from csi plugin 338 err := volume.SetVolumeOwnership(c, dir, mounterArgs.FsGroup, mounterArgs.FSGroupChangePolicy, util.FSGroupCompleteHook(c.plugin, c.spec)) 339 if err != nil { 340 // At this point mount operation is successful: 341 // 1. Since volume can not be used by the pod because of invalid permissions, we must return error 342 // 2. Since mount is successful, we must record volume as mounted in uncertain state, so it can be 343 // cleaned up. 344 return volumetypes.NewUncertainProgressError(fmt.Sprintf("applyFSGroup failed for vol %s: %v", c.volumeID, err)) 345 } 346 klog.V(4).Info(log("mounter.SetupAt fsGroup [%d] applied successfully to %s", *mounterArgs.FsGroup, c.volumeID)) 347 } 348 349 klog.V(4).Infof(log("mounter.SetUp successfully requested NodePublish [%s]", dir)) 350 return nil 351 } 352 353 func (c *csiMountMgr) podServiceAccountTokenAttrs() (map[string]string, error) { 354 if c.plugin.serviceAccountTokenGetter == nil { 355 return nil, errors.New("ServiceAccountTokenGetter is nil") 356 } 357 358 csiDriver, err := c.plugin.csiDriverLister.Get(string(c.driverName)) 359 if err != nil { 360 if apierrors.IsNotFound(err) { 361 klog.V(5).Infof(log("CSIDriver %q not found, not adding service account token information", c.driverName)) 362 return nil, nil 363 } 364 return nil, err 365 } 366 367 if len(csiDriver.Spec.TokenRequests) == 0 { 368 return nil, nil 369 } 370 371 outputs := map[string]authenticationv1.TokenRequestStatus{} 372 for _, tokenRequest := range csiDriver.Spec.TokenRequests { 373 audience := tokenRequest.Audience 374 audiences := []string{audience} 375 if audience == "" { 376 audiences = []string{} 377 } 378 tr, err := c.plugin.serviceAccountTokenGetter(c.pod.Namespace, c.pod.Spec.ServiceAccountName, &authenticationv1.TokenRequest{ 379 Spec: authenticationv1.TokenRequestSpec{ 380 Audiences: audiences, 381 ExpirationSeconds: tokenRequest.ExpirationSeconds, 382 BoundObjectRef: &authenticationv1.BoundObjectReference{ 383 APIVersion: "v1", 384 Kind: "Pod", 385 Name: c.pod.Name, 386 UID: c.pod.UID, 387 }, 388 }, 389 }) 390 if err != nil { 391 return nil, err 392 } 393 394 outputs[audience] = tr.Status 395 } 396 397 klog.V(4).Infof(log("Fetched service account token attrs for CSIDriver %q", c.driverName)) 398 tokens, _ := json.Marshal(outputs) 399 return map[string]string{ 400 "csi.storage.k8s.io/serviceAccount.tokens": string(tokens), 401 }, nil 402 } 403 404 func (c *csiMountMgr) GetAttributes() volume.Attributes { 405 return volume.Attributes{ 406 ReadOnly: c.readOnly, 407 Managed: !c.readOnly, 408 SELinuxRelabel: c.needSELinuxRelabel, 409 } 410 } 411 412 // volume.Unmounter methods 413 var _ volume.Unmounter = &csiMountMgr{} 414 415 func (c *csiMountMgr) TearDown() error { 416 return c.TearDownAt(c.GetPath()) 417 } 418 func (c *csiMountMgr) TearDownAt(dir string) error { 419 klog.V(4).Infof(log("Unmounter.TearDownAt(%s)", dir)) 420 421 volID := c.volumeID 422 csi, err := c.csiClientGetter.Get() 423 if err != nil { 424 // Treat the absence of the CSI driver as a transient error 425 // See https://github.com/kubernetes/kubernetes/issues/120268 426 return volumetypes.NewTransientOperationFailure(log("Unmounter.TearDownAt failed to get CSI client: %v", err)) 427 } 428 429 // Could not get spec info on whether this is a migrated operation because c.spec is nil 430 ctx, cancel := createCSIOperationContext(c.spec, csiTimeout) 431 defer cancel() 432 433 if err := csi.NodeUnpublishVolume(ctx, volID, dir); err != nil { 434 return errors.New(log("Unmounter.TearDownAt failed: %v", err)) 435 } 436 437 // Deprecation: Removal of target_path provided in the NodePublish RPC call 438 // (in this case location `dir`) MUST be done by the CSI plugin according 439 // to the spec. This will no longer be done directly as part of TearDown 440 // by the kubelet in the future. Kubelet will only be responsible for 441 // removal of json data files it creates and parent directories. 442 if err := removeMountDir(c.plugin, dir); err != nil { 443 return errors.New(log("Unmounter.TearDownAt failed to clean mount dir [%s]: %v", dir, err)) 444 } 445 klog.V(4).Infof(log("Unmounter.TearDownAt successfully unmounted dir [%s]", dir)) 446 447 return nil 448 } 449 450 func (c *csiMountMgr) supportsFSGroup(fsType string, fsGroup *int64, driverPolicy storage.FSGroupPolicy) bool { 451 if fsGroup == nil || driverPolicy == storage.NoneFSGroupPolicy || c.readOnly { 452 return false 453 } 454 455 if driverPolicy == storage.FileFSGroupPolicy { 456 return true 457 } 458 459 if fsType == "" { 460 klog.V(4).Info(log("mounter.SetupAt WARNING: skipping fsGroup, fsType not provided")) 461 return false 462 } 463 464 if c.spec.PersistentVolume != nil { 465 if c.spec.PersistentVolume.Spec.AccessModes == nil { 466 klog.V(4).Info(log("mounter.SetupAt WARNING: skipping fsGroup, access modes not provided")) 467 return false 468 } 469 if !hasReadWriteOnce(c.spec.PersistentVolume.Spec.AccessModes) { 470 klog.V(4).Info(log("mounter.SetupAt WARNING: skipping fsGroup, only support ReadWriteOnce access mode")) 471 return false 472 } 473 return true 474 } else if c.spec.Volume != nil && c.spec.Volume.CSI != nil { 475 // Inline CSI volumes are always mounted with RWO AccessMode by SetUpAt 476 return true 477 } 478 479 klog.V(4).Info(log("mounter.SetupAt WARNING: skipping fsGroup, unsupported volume type")) 480 return false 481 } 482 483 // getFSGroupPolicy returns if the CSI driver supports a volume in the given mode. 484 // An error indicates that it isn't supported and explains why. 485 func (c *csiMountMgr) getFSGroupPolicy() (storage.FSGroupPolicy, error) { 486 // Retrieve CSIDriver. It's not an error if that isn't 487 // possible (we don't have the lister if CSIDriverRegistry is 488 // disabled) or the driver isn't found (CSIDriver is 489 // optional) 490 var csiDriver *storage.CSIDriver 491 driver := string(c.driverName) 492 if c.plugin.csiDriverLister != nil { 493 c, err := c.plugin.getCSIDriver(driver) 494 if err != nil && !apierrors.IsNotFound(err) { 495 // Some internal error. 496 return storage.ReadWriteOnceWithFSTypeFSGroupPolicy, err 497 } 498 csiDriver = c 499 } 500 501 // If the csiDriver isn't defined, return the default behavior 502 if csiDriver == nil { 503 return storage.ReadWriteOnceWithFSTypeFSGroupPolicy, nil 504 } 505 // If the csiDriver exists but the fsGroupPolicy isn't defined, return an error 506 if csiDriver.Spec.FSGroupPolicy == nil || *csiDriver.Spec.FSGroupPolicy == "" { 507 return storage.ReadWriteOnceWithFSTypeFSGroupPolicy, errors.New(log("expected valid fsGroupPolicy, received nil value or empty string")) 508 } 509 return *csiDriver.Spec.FSGroupPolicy, nil 510 } 511 512 // supportsVolumeMode checks whether the CSI driver supports a volume in the given mode. 513 // An error indicates that it isn't supported and explains why. 514 func (c *csiMountMgr) supportsVolumeLifecycleMode() error { 515 // Retrieve CSIDriver. It's not an error if that isn't 516 // possible (we don't have the lister if CSIDriverRegistry is 517 // disabled) or the driver isn't found (CSIDriver is 518 // optional), but then only persistent volumes are supported. 519 var csiDriver *storage.CSIDriver 520 driver := string(c.driverName) 521 if c.plugin.csiDriverLister != nil { 522 c, err := c.plugin.getCSIDriver(driver) 523 if err != nil && !apierrors.IsNotFound(err) { 524 // Some internal error. 525 return err 526 } 527 csiDriver = c 528 } 529 530 // The right response depends on whether we have information 531 // about the driver and the volume mode. 532 switch { 533 case csiDriver == nil && c.volumeLifecycleMode == storage.VolumeLifecyclePersistent: 534 // No information, but that's okay for persistent volumes (and only those). 535 return nil 536 case csiDriver == nil: 537 return fmt.Errorf("volume mode %q not supported by driver %s (no CSIDriver object)", c.volumeLifecycleMode, driver) 538 case containsVolumeMode(csiDriver.Spec.VolumeLifecycleModes, c.volumeLifecycleMode): 539 // Explicitly listed. 540 return nil 541 default: 542 return fmt.Errorf("volume mode %q not supported by driver %s (only supports %q)", c.volumeLifecycleMode, driver, csiDriver.Spec.VolumeLifecycleModes) 543 } 544 } 545 546 // containsVolumeMode checks whether the given volume mode is listed. 547 func containsVolumeMode(modes []storage.VolumeLifecycleMode, mode storage.VolumeLifecycleMode) bool { 548 for _, m := range modes { 549 if m == mode { 550 return true 551 } 552 } 553 return false 554 } 555 556 // isDirMounted returns the !notMounted result from IsLikelyNotMountPoint check 557 func isDirMounted(plug *csiPlugin, dir string) (bool, error) { 558 mounter := plug.host.GetMounter(plug.GetPluginName()) 559 notMnt, err := mounter.IsLikelyNotMountPoint(dir) 560 if err != nil && !os.IsNotExist(err) { 561 klog.Error(log("isDirMounted IsLikelyNotMountPoint test failed for dir [%v]", dir)) 562 return false, err 563 } 564 return !notMnt, nil 565 } 566 567 func isCorruptedDir(dir string) bool { 568 _, pathErr := mount.PathExists(dir) 569 return pathErr != nil && mount.IsCorruptedMnt(pathErr) 570 } 571 572 // removeMountDir cleans the mount dir when dir is not mounted and removed the volume data file in dir 573 func removeMountDir(plug *csiPlugin, mountPath string) error { 574 klog.V(4).Info(log("removing mount path [%s]", mountPath)) 575 576 mnt, err := isDirMounted(plug, mountPath) 577 if err != nil { 578 return err 579 } 580 if !mnt { 581 klog.V(4).Info(log("dir not mounted, deleting it [%s]", mountPath)) 582 if err := os.Remove(mountPath); err != nil && !os.IsNotExist(err) { 583 return errors.New(log("failed to remove dir [%s]: %v", mountPath, err)) 584 } 585 // remove volume data file as well 586 volPath := filepath.Dir(mountPath) 587 dataFile := filepath.Join(volPath, volDataFileName) 588 klog.V(4).Info(log("also deleting volume info data file [%s]", dataFile)) 589 if err := os.Remove(dataFile); err != nil && !os.IsNotExist(err) { 590 return errors.New(log("failed to delete volume data file [%s]: %v", dataFile, err)) 591 } 592 // remove volume path 593 klog.V(4).Info(log("deleting volume path [%s]", volPath)) 594 if err := os.Remove(volPath); err != nil && !os.IsNotExist(err) { 595 return errors.New(log("failed to delete volume path [%s]: %v", volPath, err)) 596 } 597 } 598 return nil 599 } 600 601 // makeVolumeHandle returns csi-<sha256(podUID,volSourceSpecName)> 602 func makeVolumeHandle(podUID, volSourceSpecName string) string { 603 result := sha256.Sum256([]byte(fmt.Sprintf("%s%s", podUID, volSourceSpecName))) 604 return fmt.Sprintf("csi-%x", result) 605 } 606 607 func mergeMap(first, second map[string]string) map[string]string { 608 if first == nil { 609 return second 610 } 611 for k, v := range second { 612 first[k] = v 613 } 614 return first 615 }