...

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

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

     1  /*
     2  Copyright 2014 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 nfs
    18  
    19  import (
    20  	"fmt"
    21  	"os"
    22  	"time"
    23  
    24  	netutil "k8s.io/utils/net"
    25  
    26  	"k8s.io/klog/v2"
    27  	"k8s.io/mount-utils"
    28  	utilstrings "k8s.io/utils/strings"
    29  
    30  	v1 "k8s.io/api/core/v1"
    31  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    32  	"k8s.io/apimachinery/pkg/types"
    33  	"k8s.io/kubernetes/pkg/volume"
    34  	"k8s.io/kubernetes/pkg/volume/util"
    35  	"k8s.io/kubernetes/pkg/volume/util/recyclerclient"
    36  )
    37  
    38  func getPath(uid types.UID, volName string, host volume.VolumeHost) string {
    39  	return host.GetPodVolumeDir(uid, utilstrings.EscapeQualifiedName(nfsPluginName), volName)
    40  }
    41  
    42  // ProbeVolumePlugins is the primary entrypoint for volume plugins.
    43  // This is the primary entrypoint for volume plugins.
    44  // The volumeConfig arg provides the ability to configure recycler behavior.  It is implemented as a pointer to allow nils.
    45  // The nfsPlugin is used to store the volumeConfig and give it, when needed, to the func that creates NFS Recyclers.
    46  // Tests that exercise recycling should not use this func but instead use ProbeRecyclablePlugins() to override default behavior.
    47  func ProbeVolumePlugins(volumeConfig volume.VolumeConfig) []volume.VolumePlugin {
    48  	return []volume.VolumePlugin{
    49  		&nfsPlugin{
    50  			host:   nil,
    51  			config: volumeConfig,
    52  		},
    53  	}
    54  }
    55  
    56  type nfsPlugin struct {
    57  	host   volume.VolumeHost
    58  	config volume.VolumeConfig
    59  }
    60  
    61  var _ volume.VolumePlugin = &nfsPlugin{}
    62  var _ volume.PersistentVolumePlugin = &nfsPlugin{}
    63  var _ volume.RecyclableVolumePlugin = &nfsPlugin{}
    64  
    65  const (
    66  	nfsPluginName  = "kubernetes.io/nfs"
    67  	unMountTimeout = time.Minute
    68  )
    69  
    70  func (plugin *nfsPlugin) Init(host volume.VolumeHost) error {
    71  	plugin.host = host
    72  	return nil
    73  }
    74  
    75  func (plugin *nfsPlugin) GetPluginName() string {
    76  	return nfsPluginName
    77  }
    78  
    79  func (plugin *nfsPlugin) GetVolumeName(spec *volume.Spec) (string, error) {
    80  	volumeSource, _, err := getVolumeSource(spec)
    81  	if err != nil {
    82  		return "", err
    83  	}
    84  
    85  	return fmt.Sprintf(
    86  		"%v/%v",
    87  		volumeSource.Server,
    88  		volumeSource.Path), nil
    89  }
    90  
    91  func (plugin *nfsPlugin) CanSupport(spec *volume.Spec) bool {
    92  	return (spec.PersistentVolume != nil && spec.PersistentVolume.Spec.NFS != nil) ||
    93  		(spec.Volume != nil && spec.Volume.NFS != nil)
    94  }
    95  
    96  func (plugin *nfsPlugin) RequiresRemount(spec *volume.Spec) bool {
    97  	return false
    98  }
    99  
   100  func (plugin *nfsPlugin) SupportsMountOption() bool {
   101  	return true
   102  }
   103  
   104  func (plugin *nfsPlugin) SupportsBulkVolumeVerification() bool {
   105  	return false
   106  }
   107  
   108  func (plugin *nfsPlugin) SupportsSELinuxContextMount(spec *volume.Spec) (bool, error) {
   109  	return false, nil
   110  }
   111  
   112  func (plugin *nfsPlugin) GetAccessModes() []v1.PersistentVolumeAccessMode {
   113  	return []v1.PersistentVolumeAccessMode{
   114  		v1.ReadWriteOnce,
   115  		v1.ReadOnlyMany,
   116  		v1.ReadWriteMany,
   117  	}
   118  }
   119  
   120  func (plugin *nfsPlugin) NewMounter(spec *volume.Spec, pod *v1.Pod, _ volume.VolumeOptions) (volume.Mounter, error) {
   121  	return plugin.newMounterInternal(spec, pod, plugin.host.GetMounter(plugin.GetPluginName()))
   122  }
   123  
   124  func (plugin *nfsPlugin) newMounterInternal(spec *volume.Spec, pod *v1.Pod, mounter mount.Interface) (volume.Mounter, error) {
   125  	source, readOnly, err := getVolumeSource(spec)
   126  	if err != nil {
   127  		return nil, err
   128  	}
   129  	return &nfsMounter{
   130  		nfs: &nfs{
   131  			volName:         spec.Name(),
   132  			mounter:         mounter,
   133  			pod:             pod,
   134  			plugin:          plugin,
   135  			MetricsProvider: volume.NewMetricsStatFS(getPath(pod.UID, spec.Name(), plugin.host)),
   136  		},
   137  		server:       getServerFromSource(source),
   138  		exportPath:   source.Path,
   139  		readOnly:     readOnly,
   140  		mountOptions: util.MountOptionFromSpec(spec),
   141  	}, nil
   142  }
   143  
   144  func (plugin *nfsPlugin) NewUnmounter(volName string, podUID types.UID) (volume.Unmounter, error) {
   145  	return plugin.newUnmounterInternal(volName, podUID, plugin.host.GetMounter(plugin.GetPluginName()))
   146  }
   147  
   148  func (plugin *nfsPlugin) newUnmounterInternal(volName string, podUID types.UID, mounter mount.Interface) (volume.Unmounter, error) {
   149  	return &nfsUnmounter{&nfs{
   150  		volName:         volName,
   151  		mounter:         mounter,
   152  		pod:             &v1.Pod{ObjectMeta: metav1.ObjectMeta{UID: podUID}},
   153  		plugin:          plugin,
   154  		MetricsProvider: volume.NewMetricsStatFS(getPath(podUID, volName, plugin.host)),
   155  	}}, nil
   156  }
   157  
   158  // Recycle recycles/scrubs clean an NFS volume.
   159  // Recycle blocks until the pod has completed or any error occurs.
   160  func (plugin *nfsPlugin) Recycle(pvName string, spec *volume.Spec, eventRecorder recyclerclient.RecycleEventRecorder) error {
   161  	if spec.PersistentVolume == nil || spec.PersistentVolume.Spec.NFS == nil {
   162  		return fmt.Errorf("spec.PersistentVolumeSource.NFS is nil")
   163  	}
   164  
   165  	pod := plugin.config.RecyclerPodTemplate
   166  	timeout := util.CalculateTimeoutForVolume(plugin.config.RecyclerMinimumTimeout, plugin.config.RecyclerTimeoutIncrement, spec.PersistentVolume)
   167  	// overrides
   168  	pod.Spec.ActiveDeadlineSeconds = &timeout
   169  	pod.GenerateName = "pv-recycler-nfs-"
   170  	pod.Spec.Volumes[0].VolumeSource = v1.VolumeSource{
   171  		NFS: &v1.NFSVolumeSource{
   172  			Server: spec.PersistentVolume.Spec.NFS.Server,
   173  			Path:   spec.PersistentVolume.Spec.NFS.Path,
   174  		},
   175  	}
   176  	return recyclerclient.RecycleVolumeByWatchingPodUntilCompletion(pvName, pod, plugin.host.GetKubeClient(), eventRecorder)
   177  }
   178  
   179  func (plugin *nfsPlugin) ConstructVolumeSpec(volumeName, mountPath string) (volume.ReconstructedVolume, error) {
   180  	nfsVolume := &v1.Volume{
   181  		Name: volumeName,
   182  		VolumeSource: v1.VolumeSource{
   183  			NFS: &v1.NFSVolumeSource{
   184  				Path: volumeName,
   185  			},
   186  		},
   187  	}
   188  	return volume.ReconstructedVolume{
   189  		Spec: volume.NewSpecFromVolume(nfsVolume),
   190  	}, nil
   191  }
   192  
   193  // NFS volumes represent a bare host file or directory mount of an NFS export.
   194  type nfs struct {
   195  	volName string
   196  	pod     *v1.Pod
   197  	mounter mount.Interface
   198  	plugin  *nfsPlugin
   199  	volume.MetricsProvider
   200  }
   201  
   202  func (nfsVolume *nfs) GetPath() string {
   203  	name := nfsPluginName
   204  	return nfsVolume.plugin.host.GetPodVolumeDir(nfsVolume.pod.UID, utilstrings.EscapeQualifiedName(name), nfsVolume.volName)
   205  }
   206  
   207  type nfsMounter struct {
   208  	*nfs
   209  	server       string
   210  	exportPath   string
   211  	readOnly     bool
   212  	mountOptions []string
   213  }
   214  
   215  var _ volume.Mounter = &nfsMounter{}
   216  
   217  func (nfsMounter *nfsMounter) GetAttributes() volume.Attributes {
   218  	return volume.Attributes{
   219  		ReadOnly:       nfsMounter.readOnly,
   220  		Managed:        false,
   221  		SELinuxRelabel: false,
   222  	}
   223  }
   224  
   225  // SetUp attaches the disk and bind mounts to the volume path.
   226  func (nfsMounter *nfsMounter) SetUp(mounterArgs volume.MounterArgs) error {
   227  	return nfsMounter.SetUpAt(nfsMounter.GetPath(), mounterArgs)
   228  }
   229  
   230  func (nfsMounter *nfsMounter) SetUpAt(dir string, mounterArgs volume.MounterArgs) error {
   231  	notMnt, err := mount.IsNotMountPoint(nfsMounter.mounter, dir)
   232  	klog.V(4).Infof("NFS mount set up: %s %v %v", dir, !notMnt, err)
   233  	if err != nil && !os.IsNotExist(err) {
   234  		return err
   235  	}
   236  	if !notMnt {
   237  		return nil
   238  	}
   239  	if err := os.MkdirAll(dir, 0750); err != nil {
   240  		return err
   241  	}
   242  	source := fmt.Sprintf("%s:%s", nfsMounter.server, nfsMounter.exportPath)
   243  	options := []string{}
   244  	if nfsMounter.readOnly {
   245  		options = append(options, "ro")
   246  	}
   247  	mountOptions := util.JoinMountOptions(nfsMounter.mountOptions, options)
   248  	err = nfsMounter.mounter.MountSensitiveWithoutSystemd(source, dir, "nfs", mountOptions, nil)
   249  	if err != nil {
   250  		notMnt, mntErr := mount.IsNotMountPoint(nfsMounter.mounter, dir)
   251  		if mntErr != nil {
   252  			klog.Errorf("IsNotMountPoint check failed: %v", mntErr)
   253  			return err
   254  		}
   255  		if !notMnt {
   256  			if mntErr = nfsMounter.mounter.Unmount(dir); mntErr != nil {
   257  				klog.Errorf("Failed to unmount: %v", mntErr)
   258  				return err
   259  			}
   260  			notMnt, mntErr := mount.IsNotMountPoint(nfsMounter.mounter, dir)
   261  			if mntErr != nil {
   262  				klog.Errorf("IsNotMountPoint check failed: %v", mntErr)
   263  				return err
   264  			}
   265  			if !notMnt {
   266  				// This is very odd, we don't expect it.  We'll try again next sync loop.
   267  				klog.Errorf("%s is still mounted, despite call to unmount().  Will try again next sync loop.", dir)
   268  				return err
   269  			}
   270  		}
   271  		os.Remove(dir)
   272  		return err
   273  	}
   274  	return nil
   275  }
   276  
   277  var _ volume.Unmounter = &nfsUnmounter{}
   278  
   279  type nfsUnmounter struct {
   280  	*nfs
   281  }
   282  
   283  func (c *nfsUnmounter) TearDown() error {
   284  	return c.TearDownAt(c.GetPath())
   285  }
   286  
   287  func (c *nfsUnmounter) TearDownAt(dir string) error {
   288  	// Use extensiveMountPointCheck to consult /proc/mounts. We can't use faster
   289  	// IsLikelyNotMountPoint (lstat()), since there may be root_squash on the
   290  	// NFS server and kubelet may not be able to do lstat/stat() there.
   291  	forceUnmounter, ok := c.mounter.(mount.MounterForceUnmounter)
   292  	if ok {
   293  		klog.V(4).Infof("Using force unmounter interface")
   294  		return mount.CleanupMountWithForce(dir, forceUnmounter, true /* extensiveMountPointCheck */, unMountTimeout)
   295  	}
   296  	return mount.CleanupMountPoint(dir, c.mounter, true /* extensiveMountPointCheck */)
   297  }
   298  
   299  func getVolumeSource(spec *volume.Spec) (*v1.NFSVolumeSource, bool, error) {
   300  	if spec.Volume != nil && spec.Volume.NFS != nil {
   301  		return spec.Volume.NFS, spec.Volume.NFS.ReadOnly, nil
   302  	} else if spec.PersistentVolume != nil &&
   303  		spec.PersistentVolume.Spec.NFS != nil {
   304  		return spec.PersistentVolume.Spec.NFS, spec.ReadOnly, nil
   305  	}
   306  
   307  	return nil, false, fmt.Errorf("Spec does not reference a NFS volume type")
   308  }
   309  
   310  func getServerFromSource(source *v1.NFSVolumeSource) string {
   311  	if netutil.IsIPv6String(source.Server) {
   312  		return fmt.Sprintf("[%s]", source.Server)
   313  	}
   314  	return source.Server
   315  }
   316  

View as plain text