k8s.io/kubernetes@v1.29.3/pkg/volume/local/local.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 local 18 19 import ( 20 "fmt" 21 "os" 22 "path/filepath" 23 "runtime" 24 "strings" 25 26 "k8s.io/klog/v2" 27 28 v1 "k8s.io/api/core/v1" 29 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 30 "k8s.io/apimachinery/pkg/types" 31 "k8s.io/client-go/tools/record" 32 "k8s.io/kubernetes/pkg/kubelet/events" 33 "k8s.io/kubernetes/pkg/volume" 34 "k8s.io/kubernetes/pkg/volume/util" 35 "k8s.io/kubernetes/pkg/volume/util/hostutil" 36 "k8s.io/kubernetes/pkg/volume/validation" 37 "k8s.io/mount-utils" 38 "k8s.io/utils/keymutex" 39 utilstrings "k8s.io/utils/strings" 40 ) 41 42 const ( 43 defaultFSType = "ext4" 44 ) 45 46 // ProbeVolumePlugins is the primary entrypoint for volume plugins. 47 func ProbeVolumePlugins() []volume.VolumePlugin { 48 return []volume.VolumePlugin{&localVolumePlugin{}} 49 } 50 51 type localVolumePlugin struct { 52 host volume.VolumeHost 53 volumeLocks keymutex.KeyMutex 54 recorder record.EventRecorder 55 } 56 57 var _ volume.VolumePlugin = &localVolumePlugin{} 58 var _ volume.PersistentVolumePlugin = &localVolumePlugin{} 59 var _ volume.BlockVolumePlugin = &localVolumePlugin{} 60 var _ volume.NodeExpandableVolumePlugin = &localVolumePlugin{} 61 62 const ( 63 localVolumePluginName = "kubernetes.io/local-volume" 64 ) 65 66 func (plugin *localVolumePlugin) Init(host volume.VolumeHost) error { 67 plugin.host = host 68 plugin.volumeLocks = keymutex.NewHashed(0) 69 plugin.recorder = host.GetEventRecorder() 70 return nil 71 } 72 73 func (plugin *localVolumePlugin) GetPluginName() string { 74 return localVolumePluginName 75 } 76 77 func (plugin *localVolumePlugin) GetVolumeName(spec *volume.Spec) (string, error) { 78 // This volume is only supported as a PersistentVolumeSource, so the PV name is unique 79 return spec.Name(), nil 80 } 81 82 func (plugin *localVolumePlugin) CanSupport(spec *volume.Spec) bool { 83 // This volume is only supported as a PersistentVolumeSource 84 return (spec.PersistentVolume != nil && spec.PersistentVolume.Spec.Local != nil) 85 } 86 87 func (plugin *localVolumePlugin) RequiresRemount(spec *volume.Spec) bool { 88 return false 89 } 90 91 func (plugin *localVolumePlugin) SupportsMountOption() bool { 92 return true 93 } 94 95 func (plugin *localVolumePlugin) SupportsBulkVolumeVerification() bool { 96 return false 97 } 98 99 func (plugin *localVolumePlugin) SupportsSELinuxContextMount(spec *volume.Spec) (bool, error) { 100 return false, nil 101 } 102 103 func (plugin *localVolumePlugin) GetAccessModes() []v1.PersistentVolumeAccessMode { 104 // The current meaning of AccessMode is how many nodes can attach to it, not how many pods can mount it 105 return []v1.PersistentVolumeAccessMode{ 106 v1.ReadWriteOnce, 107 } 108 } 109 110 func getVolumeSource(spec *volume.Spec) (*v1.LocalVolumeSource, bool, error) { 111 if spec.PersistentVolume != nil && spec.PersistentVolume.Spec.Local != nil { 112 return spec.PersistentVolume.Spec.Local, spec.ReadOnly, nil 113 } 114 115 return nil, false, fmt.Errorf("Spec does not reference a Local volume type") 116 } 117 118 func (plugin *localVolumePlugin) NewMounter(spec *volume.Spec, pod *v1.Pod, _ volume.VolumeOptions) (volume.Mounter, error) { 119 _, readOnly, err := getVolumeSource(spec) 120 if err != nil { 121 return nil, err 122 } 123 124 globalLocalPath, err := plugin.getGlobalLocalPath(spec) 125 if err != nil { 126 return nil, err 127 } 128 129 kvh, ok := plugin.host.(volume.KubeletVolumeHost) 130 if !ok { 131 return nil, fmt.Errorf("plugin volume host does not implement KubeletVolumeHost interface") 132 } 133 134 return &localVolumeMounter{ 135 localVolume: &localVolume{ 136 pod: pod, 137 podUID: pod.UID, 138 volName: spec.Name(), 139 mounter: plugin.host.GetMounter(plugin.GetPluginName()), 140 hostUtil: kvh.GetHostUtil(), 141 plugin: plugin, 142 globalPath: globalLocalPath, 143 MetricsProvider: volume.NewMetricsStatFS(plugin.host.GetPodVolumeDir(pod.UID, utilstrings.EscapeQualifiedName(localVolumePluginName), spec.Name())), 144 }, 145 mountOptions: util.MountOptionFromSpec(spec), 146 readOnly: readOnly, 147 }, nil 148 149 } 150 151 func (plugin *localVolumePlugin) NewUnmounter(volName string, podUID types.UID) (volume.Unmounter, error) { 152 return &localVolumeUnmounter{ 153 localVolume: &localVolume{ 154 podUID: podUID, 155 volName: volName, 156 mounter: plugin.host.GetMounter(plugin.GetPluginName()), 157 plugin: plugin, 158 }, 159 }, nil 160 } 161 162 func (plugin *localVolumePlugin) NewBlockVolumeMapper(spec *volume.Spec, pod *v1.Pod, 163 _ volume.VolumeOptions) (volume.BlockVolumeMapper, error) { 164 volumeSource, readOnly, err := getVolumeSource(spec) 165 if err != nil { 166 return nil, err 167 } 168 169 mapper := &localVolumeMapper{ 170 localVolume: &localVolume{ 171 podUID: pod.UID, 172 volName: spec.Name(), 173 globalPath: volumeSource.Path, 174 plugin: plugin, 175 }, 176 readOnly: readOnly, 177 } 178 179 blockPath, err := mapper.GetGlobalMapPath(spec) 180 if err != nil { 181 return nil, fmt.Errorf("failed to get device path: %v", err) 182 } 183 mapper.MetricsProvider = volume.NewMetricsBlock(filepath.Join(blockPath, string(pod.UID))) 184 185 return mapper, nil 186 } 187 188 func (plugin *localVolumePlugin) NewBlockVolumeUnmapper(volName string, 189 podUID types.UID) (volume.BlockVolumeUnmapper, error) { 190 return &localVolumeUnmapper{ 191 localVolume: &localVolume{ 192 podUID: podUID, 193 volName: volName, 194 plugin: plugin, 195 }, 196 }, nil 197 } 198 199 // TODO: check if no path and no topology constraints are ok 200 func (plugin *localVolumePlugin) ConstructVolumeSpec(volumeName, mountPath string) (volume.ReconstructedVolume, error) { 201 fs := v1.PersistentVolumeFilesystem 202 // The main purpose of reconstructed volume is to clean unused mount points 203 // and directories. 204 // For filesystem volume with directory source, no global mount path is 205 // needed to clean. Empty path is ok. 206 // For filesystem volume with block source, we should resolve to its device 207 // path if global mount path exists. 208 var path string 209 mounter := plugin.host.GetMounter(plugin.GetPluginName()) 210 refs, err := mounter.GetMountRefs(mountPath) 211 if err != nil { 212 return volume.ReconstructedVolume{}, err 213 } 214 baseMountPath := plugin.generateBlockDeviceBaseGlobalPath() 215 for _, ref := range refs { 216 if mount.PathWithinBase(ref, baseMountPath) { 217 // If the global mount for block device exists, the source is block 218 // device. 219 // The resolved device path may not be the exact same as path in 220 // local PV object if symbolic link is used. However, it's the true 221 // source and can be used in reconstructed volume. 222 path, _, err = mount.GetDeviceNameFromMount(mounter, ref) 223 if err != nil { 224 return volume.ReconstructedVolume{}, err 225 } 226 klog.V(4).Infof("local: reconstructing volume %q (pod volume mount: %q) with device %q", volumeName, mountPath, path) 227 break 228 } 229 } 230 localVolume := &v1.PersistentVolume{ 231 ObjectMeta: metav1.ObjectMeta{ 232 Name: volumeName, 233 }, 234 Spec: v1.PersistentVolumeSpec{ 235 PersistentVolumeSource: v1.PersistentVolumeSource{ 236 Local: &v1.LocalVolumeSource{ 237 Path: path, 238 }, 239 }, 240 VolumeMode: &fs, 241 }, 242 } 243 return volume.ReconstructedVolume{ 244 Spec: volume.NewSpecFromPersistentVolume(localVolume, false), 245 }, nil 246 } 247 248 func (plugin *localVolumePlugin) ConstructBlockVolumeSpec(podUID types.UID, volumeName, 249 mapPath string) (*volume.Spec, error) { 250 block := v1.PersistentVolumeBlock 251 252 localVolume := &v1.PersistentVolume{ 253 ObjectMeta: metav1.ObjectMeta{ 254 Name: volumeName, 255 }, 256 Spec: v1.PersistentVolumeSpec{ 257 PersistentVolumeSource: v1.PersistentVolumeSource{ 258 Local: &v1.LocalVolumeSource{ 259 // Not needed because we don't need to detach local device from the host. 260 Path: "", 261 }, 262 }, 263 VolumeMode: &block, 264 }, 265 } 266 267 return volume.NewSpecFromPersistentVolume(localVolume, false), nil 268 } 269 270 func (plugin *localVolumePlugin) generateBlockDeviceBaseGlobalPath() string { 271 return filepath.Join(plugin.host.GetPluginDir(localVolumePluginName), util.MountsInGlobalPDPath) 272 } 273 274 func (plugin *localVolumePlugin) getGlobalLocalPath(spec *volume.Spec) (string, error) { 275 if spec.PersistentVolume.Spec.Local == nil || len(spec.PersistentVolume.Spec.Local.Path) == 0 { 276 return "", fmt.Errorf("local volume source is nil or local path is not set") 277 } 278 279 kvh, ok := plugin.host.(volume.KubeletVolumeHost) 280 if !ok { 281 return "", fmt.Errorf("plugin volume host does not implement KubeletVolumeHost interface") 282 } 283 284 fileType, err := kvh.GetHostUtil().GetFileType(spec.PersistentVolume.Spec.Local.Path) 285 if err != nil { 286 return "", err 287 } 288 switch fileType { 289 case hostutil.FileTypeDirectory: 290 return spec.PersistentVolume.Spec.Local.Path, nil 291 case hostutil.FileTypeBlockDev: 292 return filepath.Join(plugin.generateBlockDeviceBaseGlobalPath(), spec.Name()), nil 293 default: 294 return "", fmt.Errorf("only directory and block device are supported") 295 } 296 } 297 298 var _ volume.DeviceMountableVolumePlugin = &localVolumePlugin{} 299 300 type deviceMounter struct { 301 plugin *localVolumePlugin 302 mounter *mount.SafeFormatAndMount 303 hostUtil hostutil.HostUtils 304 } 305 306 var _ volume.DeviceMounter = &deviceMounter{} 307 308 func (plugin *localVolumePlugin) CanDeviceMount(spec *volume.Spec) (bool, error) { 309 return true, nil 310 } 311 312 func (plugin *localVolumePlugin) NewDeviceMounter() (volume.DeviceMounter, error) { 313 kvh, ok := plugin.host.(volume.KubeletVolumeHost) 314 if !ok { 315 return nil, fmt.Errorf("plugin volume host does not implement KubeletVolumeHost interface") 316 } 317 return &deviceMounter{ 318 plugin: plugin, 319 mounter: util.NewSafeFormatAndMountFromHost(plugin.GetPluginName(), plugin.host), 320 hostUtil: kvh.GetHostUtil(), 321 }, nil 322 } 323 324 func (dm *deviceMounter) mountLocalBlockDevice(spec *volume.Spec, devicePath string, deviceMountPath string) error { 325 klog.V(4).Infof("local: mounting device %s to %s", devicePath, deviceMountPath) 326 notMnt, err := dm.mounter.IsLikelyNotMountPoint(deviceMountPath) 327 if err != nil { 328 if os.IsNotExist(err) { 329 if err := os.MkdirAll(deviceMountPath, 0750); err != nil { 330 return err 331 } 332 notMnt = true 333 } else { 334 return err 335 } 336 } 337 if !notMnt { 338 return nil 339 } 340 fstype, err := getVolumeSourceFSType(spec) 341 if err != nil { 342 return err 343 } 344 345 ro, err := getVolumeSourceReadOnly(spec) 346 if err != nil { 347 return err 348 } 349 options := []string{} 350 if ro { 351 options = append(options, "ro") 352 } 353 mountOptions := util.MountOptionFromSpec(spec, options...) 354 err = dm.mounter.FormatAndMount(devicePath, deviceMountPath, fstype, mountOptions) 355 if err != nil { 356 if rmErr := os.Remove(deviceMountPath); rmErr != nil { 357 klog.Warningf("local: failed to remove %s: %v", deviceMountPath, rmErr) 358 } 359 return fmt.Errorf("local: failed to mount device %s at %s (fstype: %s), error %w", devicePath, deviceMountPath, fstype, err) 360 } 361 klog.V(3).Infof("local: successfully mount device %s at %s (fstype: %s)", devicePath, deviceMountPath, fstype) 362 return nil 363 } 364 365 func (dm *deviceMounter) MountDevice(spec *volume.Spec, devicePath string, deviceMountPath string, _ volume.DeviceMounterArgs) error { 366 if spec.PersistentVolume.Spec.Local == nil || len(spec.PersistentVolume.Spec.Local.Path) == 0 { 367 return fmt.Errorf("local volume source is nil or local path is not set") 368 } 369 fileType, err := dm.hostUtil.GetFileType(spec.PersistentVolume.Spec.Local.Path) 370 if err != nil { 371 return err 372 } 373 374 switch fileType { 375 case hostutil.FileTypeBlockDev: 376 // local volume plugin does not implement AttachableVolumePlugin interface, so set devicePath to Path in PV spec directly 377 return dm.mountLocalBlockDevice(spec, spec.PersistentVolume.Spec.Local.Path, deviceMountPath) 378 case hostutil.FileTypeDirectory: 379 // if the given local volume path is of already filesystem directory, return directly 380 return nil 381 default: 382 return fmt.Errorf("only directory and block device are supported") 383 } 384 } 385 386 func (plugin *localVolumePlugin) RequiresFSResize() bool { 387 return true 388 } 389 390 func (plugin *localVolumePlugin) NodeExpand(resizeOptions volume.NodeResizeOptions) (bool, error) { 391 fsVolume, err := util.CheckVolumeModeFilesystem(resizeOptions.VolumeSpec) 392 if err != nil { 393 return false, fmt.Errorf("error checking VolumeMode: %v", err) 394 } 395 if !fsVolume { 396 return true, nil 397 } 398 399 localDevicePath := resizeOptions.VolumeSpec.PersistentVolume.Spec.Local.Path 400 401 kvh, ok := plugin.host.(volume.KubeletVolumeHost) 402 if !ok { 403 return false, fmt.Errorf("plugin volume host does not implement KubeletVolumeHost interface") 404 } 405 406 fileType, err := kvh.GetHostUtil().GetFileType(localDevicePath) 407 if err != nil { 408 return false, err 409 } 410 411 switch fileType { 412 case hostutil.FileTypeBlockDev: 413 _, err = util.GenericResizeFS(plugin.host, plugin.GetPluginName(), localDevicePath, resizeOptions.DeviceMountPath) 414 if err != nil { 415 return false, err 416 } 417 return true, nil 418 case hostutil.FileTypeDirectory: 419 // if the given local volume path is of already filesystem directory, return directly because 420 // we do not want to prevent mount operation from succeeding. 421 klog.InfoS("Expansion of directory based local volumes is NO-OP", "localVolumePath", localDevicePath) 422 return true, nil 423 default: 424 return false, fmt.Errorf("only directory and block device are supported") 425 } 426 } 427 428 func getVolumeSourceFSType(spec *volume.Spec) (string, error) { 429 if spec.PersistentVolume != nil && 430 spec.PersistentVolume.Spec.Local != nil { 431 if spec.PersistentVolume.Spec.Local.FSType != nil { 432 return *spec.PersistentVolume.Spec.Local.FSType, nil 433 } 434 // if the FSType is not set in local PV spec, setting it to default ("ext4") 435 return defaultFSType, nil 436 } 437 438 return "", fmt.Errorf("spec does not reference a Local volume type") 439 } 440 441 func getVolumeSourceReadOnly(spec *volume.Spec) (bool, error) { 442 if spec.PersistentVolume != nil && 443 spec.PersistentVolume.Spec.Local != nil { 444 // local volumes used as a PersistentVolume gets the ReadOnly flag indirectly through 445 // the persistent-claim volume used to mount the PV 446 return spec.ReadOnly, nil 447 } 448 449 return false, fmt.Errorf("spec does not reference a Local volume type") 450 } 451 452 func (dm *deviceMounter) GetDeviceMountPath(spec *volume.Spec) (string, error) { 453 return dm.plugin.getGlobalLocalPath(spec) 454 } 455 456 func (plugin *localVolumePlugin) NewDeviceUnmounter() (volume.DeviceUnmounter, error) { 457 return &deviceMounter{ 458 plugin: plugin, 459 mounter: util.NewSafeFormatAndMountFromHost(plugin.GetPluginName(), plugin.host), 460 }, nil 461 } 462 463 func (plugin *localVolumePlugin) GetDeviceMountRefs(deviceMountPath string) ([]string, error) { 464 mounter := plugin.host.GetMounter(plugin.GetPluginName()) 465 return mounter.GetMountRefs(deviceMountPath) 466 } 467 468 var _ volume.DeviceUnmounter = &deviceMounter{} 469 470 func (dm *deviceMounter) UnmountDevice(deviceMountPath string) error { 471 // If the local PV is a block device, 472 // The deviceMountPath is generated to the format like :/var/lib/kubelet/plugins/kubernetes.io/local-volume/mounts/localpv.spec.Name; 473 // If it is a filesystem directory, then the deviceMountPath is set directly to pvSpec.Local.Path 474 // We only need to unmount block device here, so we need to check if the deviceMountPath passed here 475 // has base mount path: /var/lib/kubelet/plugins/kubernetes.io/local-volume/mounts 476 basemountPath := dm.plugin.generateBlockDeviceBaseGlobalPath() 477 if mount.PathWithinBase(deviceMountPath, basemountPath) { 478 return mount.CleanupMountPoint(deviceMountPath, dm.mounter, false) 479 } 480 481 return nil 482 } 483 484 // Local volumes represent a local directory on a node. 485 // The directory at the globalPath will be bind-mounted to the pod's directory 486 type localVolume struct { 487 volName string 488 pod *v1.Pod 489 podUID types.UID 490 // Global path to the volume 491 globalPath string 492 // Mounter interface that provides system calls to mount the global path to the pod local path. 493 mounter mount.Interface 494 hostUtil hostutil.HostUtils 495 plugin *localVolumePlugin 496 volume.MetricsProvider 497 } 498 499 func (l *localVolume) GetPath() string { 500 return l.plugin.host.GetPodVolumeDir(l.podUID, utilstrings.EscapeQualifiedName(localVolumePluginName), l.volName) 501 } 502 503 type localVolumeMounter struct { 504 *localVolume 505 readOnly bool 506 mountOptions []string 507 } 508 509 var _ volume.Mounter = &localVolumeMounter{} 510 511 func (m *localVolumeMounter) GetAttributes() volume.Attributes { 512 return volume.Attributes{ 513 ReadOnly: m.readOnly, 514 Managed: !m.readOnly, 515 SELinuxRelabel: true, 516 } 517 } 518 519 // SetUp bind mounts the directory to the volume path 520 func (m *localVolumeMounter) SetUp(mounterArgs volume.MounterArgs) error { 521 return m.SetUpAt(m.GetPath(), mounterArgs) 522 } 523 524 // SetUpAt bind mounts the directory to the volume path and sets up volume ownership 525 func (m *localVolumeMounter) SetUpAt(dir string, mounterArgs volume.MounterArgs) error { 526 m.plugin.volumeLocks.LockKey(m.globalPath) 527 defer m.plugin.volumeLocks.UnlockKey(m.globalPath) 528 529 if m.globalPath == "" { 530 return fmt.Errorf("LocalVolume volume %q path is empty", m.volName) 531 } 532 533 err := validation.ValidatePathNoBacksteps(m.globalPath) 534 if err != nil { 535 return fmt.Errorf("invalid path: %s %v", m.globalPath, err) 536 } 537 538 notMnt, err := mount.IsNotMountPoint(m.mounter, dir) 539 klog.V(4).Infof("LocalVolume mount setup: PodDir(%s) VolDir(%s) Mounted(%t) Error(%v), ReadOnly(%t)", dir, m.globalPath, !notMnt, err, m.readOnly) 540 if err != nil && !os.IsNotExist(err) { 541 klog.Errorf("cannot validate mount point: %s %v", dir, err) 542 return err 543 } 544 545 if !notMnt { 546 return nil 547 } 548 refs, err := m.mounter.GetMountRefs(m.globalPath) 549 if mounterArgs.FsGroup != nil { 550 if err != nil { 551 klog.Errorf("cannot collect mounting information: %s %v", m.globalPath, err) 552 return err 553 } 554 555 // Only count mounts from other pods 556 refs = m.filterPodMounts(refs) 557 if len(refs) > 0 { 558 fsGroupNew := int64(*mounterArgs.FsGroup) 559 _, fsGroupOld, err := m.hostUtil.GetOwner(m.globalPath) 560 if err != nil { 561 return fmt.Errorf("failed to check fsGroup for %s (%v)", m.globalPath, err) 562 } 563 if fsGroupNew != fsGroupOld { 564 m.plugin.recorder.Eventf(m.pod, v1.EventTypeWarning, events.WarnAlreadyMountedVolume, "The requested fsGroup is %d, but the volume %s has GID %d. The volume may not be shareable.", fsGroupNew, m.volName, fsGroupOld) 565 } 566 } 567 568 } 569 570 if runtime.GOOS != "windows" { 571 // skip below MkdirAll for windows since the "bind mount" logic is implemented differently in mount_wiondows.go 572 if err := os.MkdirAll(dir, 0750); err != nil { 573 klog.Errorf("mkdir failed on disk %s (%v)", dir, err) 574 return err 575 } 576 } 577 // Perform a bind mount to the full path to allow duplicate mounts of the same volume. 578 options := []string{"bind"} 579 if m.readOnly { 580 options = append(options, "ro") 581 } 582 mountOptions := util.JoinMountOptions(options, m.mountOptions) 583 584 klog.V(4).Infof("attempting to mount %s", dir) 585 globalPath := util.MakeAbsolutePath(runtime.GOOS, m.globalPath) 586 err = m.mounter.MountSensitiveWithoutSystemd(globalPath, dir, "", mountOptions, nil) 587 if err != nil { 588 klog.Errorf("Mount of volume %s failed: %v", dir, err) 589 notMnt, mntErr := mount.IsNotMountPoint(m.mounter, dir) 590 if mntErr != nil { 591 klog.Errorf("IsNotMountPoint check failed: %v", mntErr) 592 return err 593 } 594 if !notMnt { 595 if mntErr = m.mounter.Unmount(dir); mntErr != nil { 596 klog.Errorf("Failed to unmount: %v", mntErr) 597 return err 598 } 599 notMnt, mntErr = mount.IsNotMountPoint(m.mounter, dir) 600 if mntErr != nil { 601 klog.Errorf("IsNotMountPoint check failed: %v", mntErr) 602 return err 603 } 604 if !notMnt { 605 // This is very odd, we don't expect it. We'll try again next sync loop. 606 klog.Errorf("%s is still mounted, despite call to unmount(). Will try again next sync loop.", dir) 607 return err 608 } 609 } 610 if rmErr := os.Remove(dir); rmErr != nil { 611 klog.Warningf("failed to remove %s: %v", dir, rmErr) 612 } 613 return err 614 } 615 if !m.readOnly { 616 // Volume owner will be written only once on the first volume mount 617 if len(refs) == 0 { 618 return volume.SetVolumeOwnership(m, dir, mounterArgs.FsGroup, mounterArgs.FSGroupChangePolicy, util.FSGroupCompleteHook(m.plugin, nil)) 619 } 620 } 621 return nil 622 } 623 624 // filterPodMounts only returns mount paths inside the kubelet pod directory 625 func (m *localVolumeMounter) filterPodMounts(refs []string) []string { 626 filtered := []string{} 627 for _, r := range refs { 628 if strings.HasPrefix(r, m.plugin.host.GetPodsDir()+string(os.PathSeparator)) { 629 filtered = append(filtered, r) 630 } 631 } 632 return filtered 633 } 634 635 type localVolumeUnmounter struct { 636 *localVolume 637 } 638 639 var _ volume.Unmounter = &localVolumeUnmounter{} 640 641 // TearDown unmounts the bind mount 642 func (u *localVolumeUnmounter) TearDown() error { 643 return u.TearDownAt(u.GetPath()) 644 } 645 646 // TearDownAt unmounts the bind mount 647 func (u *localVolumeUnmounter) TearDownAt(dir string) error { 648 klog.V(4).Infof("Unmounting volume %q at path %q\n", u.volName, dir) 649 return mount.CleanupMountPoint(dir, u.mounter, true) /* extensiveMountPointCheck = true */ 650 } 651 652 // localVolumeMapper implements the BlockVolumeMapper interface for local volumes. 653 type localVolumeMapper struct { 654 *localVolume 655 readOnly bool 656 } 657 658 var _ volume.BlockVolumeMapper = &localVolumeMapper{} 659 var _ volume.CustomBlockVolumeMapper = &localVolumeMapper{} 660 661 // SetUpDevice prepares the volume to the node by the plugin specific way. 662 func (m *localVolumeMapper) SetUpDevice() (string, error) { 663 return "", nil 664 } 665 666 // MapPodDevice provides physical device path for the local PV. 667 func (m *localVolumeMapper) MapPodDevice() (string, error) { 668 globalPath := util.MakeAbsolutePath(runtime.GOOS, m.globalPath) 669 klog.V(4).Infof("MapPodDevice returning path %s", globalPath) 670 return globalPath, nil 671 } 672 673 // GetStagingPath returns 674 func (m *localVolumeMapper) GetStagingPath() string { 675 return "" 676 } 677 678 // SupportsMetrics returns true for SupportsMetrics as it initializes the 679 // MetricsProvider. 680 func (m *localVolumeMapper) SupportsMetrics() bool { 681 return true 682 } 683 684 // localVolumeUnmapper implements the BlockVolumeUnmapper interface for local volumes. 685 type localVolumeUnmapper struct { 686 *localVolume 687 volume.MetricsNil 688 } 689 690 var _ volume.BlockVolumeUnmapper = &localVolumeUnmapper{} 691 692 // GetGlobalMapPath returns global map path and error. 693 // path: plugins/kubernetes.io/kubernetes.io/local-volume/volumeDevices/{volumeName} 694 func (l *localVolume) GetGlobalMapPath(spec *volume.Spec) (string, error) { 695 return filepath.Join(l.plugin.host.GetVolumeDevicePluginDir(utilstrings.EscapeQualifiedName(localVolumePluginName)), 696 l.volName), nil 697 } 698 699 // GetPodDeviceMapPath returns pod device map path and volume name. 700 // path: pods/{podUid}/volumeDevices/kubernetes.io~local-volume 701 // volName: local-pv-ff0d6d4 702 func (l *localVolume) GetPodDeviceMapPath() (string, string) { 703 return l.plugin.host.GetPodVolumeDeviceDir(l.podUID, 704 utilstrings.EscapeQualifiedName(localVolumePluginName)), l.volName 705 }