...

Source file src/k8s.io/kubernetes/pkg/volume/local/local.go

Documentation: k8s.io/kubernetes/pkg/volume/local

     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  }
   706  

View as plain text