github.com/jingruilea/kubeedge@v1.2.0-beta.0.0.20200410162146-4bb8902b3879/edge/pkg/edged/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  @CHANGELOG
    17  KubeEdge Authors: To create mini-kubelet for edge deployment scenario,
    18  this file is derived from kubernetes v1.15.3,
    19  and the full file path is k8s.io/kubernetes/pkg/volume/csi/csi_mounter.go
    20  and make some modifications including:
    21  1. remove podAttributes in SetUpAt function.
    22  */
    23  
    24  package csi
    25  
    26  import (
    27  	"context"
    28  	"crypto/sha256"
    29  	"fmt"
    30  	"os"
    31  	"path"
    32  	"path/filepath"
    33  
    34  	api "k8s.io/api/core/v1"
    35  	apierrs "k8s.io/apimachinery/pkg/api/errors"
    36  	"k8s.io/apimachinery/pkg/types"
    37  	utilfeature "k8s.io/apiserver/pkg/util/feature"
    38  	"k8s.io/client-go/kubernetes"
    39  	"k8s.io/klog"
    40  	"k8s.io/kubernetes/pkg/features"
    41  	"k8s.io/kubernetes/pkg/volume"
    42  	utilstrings "k8s.io/utils/strings"
    43  )
    44  
    45  //TODO (vladimirvivien) move this in a central loc later
    46  var (
    47  	volDataKey = struct {
    48  		specVolID,
    49  		volHandle,
    50  		driverName,
    51  		nodeName,
    52  		attachmentID,
    53  		driverMode string
    54  	}{
    55  		"specVolID",
    56  		"volumeHandle",
    57  		"driverName",
    58  		"nodeName",
    59  		"attachmentID",
    60  		"driverMode",
    61  	}
    62  )
    63  
    64  type csiMountMgr struct {
    65  	csiClientGetter
    66  	k8s             kubernetes.Interface
    67  	plugin          *csiPlugin
    68  	driverName      csiDriverName
    69  	driverMode      driverMode
    70  	volumeID        string
    71  	specVolumeID    string
    72  	readOnly        bool
    73  	supportsSELinux bool
    74  	spec            *volume.Spec
    75  	pod             *api.Pod
    76  	podUID          types.UID
    77  	options         volume.VolumeOptions
    78  	publishContext  map[string]string
    79  	kubeVolHost     volume.KubeletVolumeHost
    80  	volume.MetricsProvider
    81  }
    82  
    83  // volume.Volume methods
    84  var _ volume.Volume = &csiMountMgr{}
    85  
    86  func (c *csiMountMgr) GetPath() string {
    87  	dir := filepath.Join(getTargetPath(c.podUID, c.specVolumeID, c.plugin.host), "/mount")
    88  	klog.V(4).Info(log("mounter.GetPath generated [%s]", dir))
    89  	return dir
    90  }
    91  
    92  func getTargetPath(uid types.UID, specVolumeID string, host volume.VolumeHost) string {
    93  	specVolID := utilstrings.EscapeQualifiedName(specVolumeID)
    94  	return host.GetPodVolumeDir(uid, utilstrings.EscapeQualifiedName(CSIPluginName), specVolID)
    95  }
    96  
    97  // volume.Mounter methods
    98  var _ volume.Mounter = &csiMountMgr{}
    99  
   100  func (c *csiMountMgr) CanMount() error {
   101  	return nil
   102  }
   103  
   104  func (c *csiMountMgr) SetUp(mounterArgs volume.MounterArgs) error {
   105  	return c.SetUpAt(c.GetPath(), mounterArgs)
   106  }
   107  
   108  func (c *csiMountMgr) SetUpAt(dir string, mounterArgs volume.MounterArgs) error {
   109  	klog.V(4).Infof(log("Mounter.SetUpAt(%s)", dir))
   110  
   111  	mounted, err := isDirMounted(c.plugin, dir)
   112  	if err != nil {
   113  		klog.Error(log("mounter.SetUpAt failed while checking mount status for dir [%s]", dir))
   114  		return err
   115  	}
   116  
   117  	if mounted {
   118  		klog.V(4).Info(log("mounter.SetUpAt skipping mount, dir already mounted [%s]", dir))
   119  		return nil
   120  	}
   121  
   122  	csi, err := c.csiClientGetter.Get()
   123  	if err != nil {
   124  		klog.Error(log("mounter.SetUpAt failed to get CSI client: %v", err))
   125  		return err
   126  	}
   127  	ctx, cancel := context.WithTimeout(context.Background(), csiTimeout)
   128  	defer cancel()
   129  
   130  	volSrc, pvSrc, err := getSourceFromSpec(c.spec)
   131  	if err != nil {
   132  		klog.Error(log("mounter.SetupAt failed to get CSI persistent source: %v", err))
   133  		return err
   134  	}
   135  
   136  	driverName := c.driverName
   137  	volumeHandle := c.volumeID
   138  	readOnly := c.readOnly
   139  	accessMode := api.ReadWriteOnce
   140  
   141  	var (
   142  		fsType             string
   143  		volAttribs         map[string]string
   144  		nodePublishSecrets map[string]string
   145  		publishContext     map[string]string
   146  		mountOptions       []string
   147  		deviceMountPath    string
   148  		secretRef          *api.SecretReference
   149  	)
   150  
   151  	switch {
   152  	case volSrc != nil:
   153  		if !utilfeature.DefaultFeatureGate.Enabled(features.CSIInlineVolume) {
   154  			return fmt.Errorf("CSIInlineVolume feature required")
   155  		}
   156  		if c.driverMode != ephemeralDriverMode {
   157  			return fmt.Errorf("unexpected driver mode: %s", c.driverMode)
   158  		}
   159  		if volSrc.FSType != nil {
   160  			fsType = *volSrc.FSType
   161  		}
   162  
   163  		volAttribs = volSrc.VolumeAttributes
   164  
   165  		if volSrc.NodePublishSecretRef != nil {
   166  			secretName := volSrc.NodePublishSecretRef.Name
   167  			ns := c.pod.Namespace
   168  			secretRef = &api.SecretReference{Name: secretName, Namespace: ns}
   169  		}
   170  	case pvSrc != nil:
   171  		if c.driverMode != persistentDriverMode {
   172  			return fmt.Errorf("unexpected driver mode: %s", c.driverMode)
   173  		}
   174  
   175  		fsType = pvSrc.FSType
   176  
   177  		volAttribs = pvSrc.VolumeAttributes
   178  
   179  		if pvSrc.NodePublishSecretRef != nil {
   180  			secretRef = pvSrc.NodePublishSecretRef
   181  		}
   182  
   183  		//TODO (vladimirvivien) implement better AccessModes mapping between k8s and CSI
   184  		if c.spec.PersistentVolume.Spec.AccessModes != nil {
   185  			accessMode = c.spec.PersistentVolume.Spec.AccessModes[0]
   186  		}
   187  
   188  		mountOptions = c.spec.PersistentVolume.Spec.MountOptions
   189  
   190  		// Check for STAGE_UNSTAGE_VOLUME set and populate deviceMountPath if so
   191  		stageUnstageSet, err := csi.NodeSupportsStageUnstage(ctx)
   192  		if err != nil {
   193  			klog.Error(log("mounter.SetUpAt failed to check for STAGE_UNSTAGE_VOLUME capabilty: %v", err))
   194  			return err
   195  		}
   196  
   197  		if stageUnstageSet {
   198  			deviceMountPath, err = makeDeviceMountPath(c.plugin, c.spec)
   199  			if err != nil {
   200  				klog.Error(log("mounter.SetUpAt failed to make device mount path: %v", err))
   201  				return err
   202  			}
   203  		}
   204  
   205  		// search for attachment by VolumeAttachment.Spec.Source.PersistentVolumeName
   206  		if c.publishContext == nil {
   207  			nodeName := string(c.plugin.host.GetNodeName())
   208  			c.publishContext, err = c.plugin.getPublishContext(c.k8s, volumeHandle, string(driverName), nodeName)
   209  			if err != nil {
   210  				return err
   211  			}
   212  			publishContext = c.publishContext
   213  		}
   214  
   215  	default:
   216  		return fmt.Errorf("volume source not found in volume.Spec")
   217  	}
   218  
   219  	// create target_dir before call to NodePublish
   220  	if err := os.MkdirAll(dir, 0750); err != nil {
   221  		klog.Error(log("mouter.SetUpAt failed to create dir %#v:  %v", dir, err))
   222  		return err
   223  	}
   224  	klog.V(4).Info(log("created target path successfully [%s]", dir))
   225  
   226  	nodePublishSecrets = map[string]string{}
   227  	if secretRef != nil {
   228  		nodePublishSecrets, err = getCredentialsFromSecret(c.k8s, secretRef)
   229  		if err != nil {
   230  			return fmt.Errorf("fetching NodePublishSecretRef %s/%s failed: %v",
   231  				secretRef.Namespace, secretRef.Name, err)
   232  		}
   233  
   234  	}
   235  
   236  	err = csi.NodePublishVolume(
   237  		ctx,
   238  		volumeHandle,
   239  		readOnly,
   240  		deviceMountPath,
   241  		dir,
   242  		accessMode,
   243  		publishContext,
   244  		volAttribs,
   245  		nodePublishSecrets,
   246  		fsType,
   247  		mountOptions,
   248  	)
   249  
   250  	if err != nil {
   251  		klog.Errorf(log("mounter.SetupAt failed: %v", err))
   252  		if removeMountDirErr := removeMountDir(c.plugin, dir); removeMountDirErr != nil {
   253  			klog.Error(log("mounter.SetupAt failed to remove mount dir after a NodePublish() error [%s]: %v", dir, removeMountDirErr))
   254  		}
   255  		return err
   256  	}
   257  
   258  	c.supportsSELinux, err = c.kubeVolHost.GetHostUtil().GetSELinuxSupport(dir)
   259  	if err != nil {
   260  		klog.V(2).Info(log("error checking for SELinux support: %s", err))
   261  	}
   262  
   263  	// apply volume ownership
   264  	// The following logic is derived from https://github.com/kubernetes/kubernetes/issues/66323
   265  	// if fstype is "", then skip fsgroup (could be indication of non-block filesystem)
   266  	// if fstype is provided and pv.AccessMode == ReadWriteOnly, then apply fsgroup
   267  
   268  	err = c.applyFSGroup(fsType, mounterArgs.FsGroup)
   269  	if err != nil {
   270  		// attempt to rollback mount.
   271  		fsGrpErr := fmt.Errorf("applyFSGroup failed for vol %s: %v", c.volumeID, err)
   272  		if unpubErr := csi.NodeUnpublishVolume(ctx, c.volumeID, dir); unpubErr != nil {
   273  			klog.Error(log("NodeUnpublishVolume failed for [%s]: %v", c.volumeID, unpubErr))
   274  			return fsGrpErr
   275  		}
   276  
   277  		if unmountErr := removeMountDir(c.plugin, dir); unmountErr != nil {
   278  			klog.Error(log("removeMountDir failed for [%s]: %v", dir, unmountErr))
   279  			return fsGrpErr
   280  		}
   281  		return fsGrpErr
   282  	}
   283  
   284  	klog.V(4).Infof(log("mounter.SetUp successfully requested NodePublish [%s]", dir))
   285  	return nil
   286  }
   287  
   288  func (c *csiMountMgr) podAttributes() (map[string]string, error) {
   289  	if !utilfeature.DefaultFeatureGate.Enabled(features.CSIDriverRegistry) {
   290  		return nil, nil
   291  	}
   292  
   293  	kletHost, ok := c.plugin.host.(volume.KubeletVolumeHost)
   294  	if ok {
   295  		kletHost.WaitForCacheSync()
   296  	}
   297  
   298  	if c.plugin.csiDriverLister == nil {
   299  		return nil, fmt.Errorf("CSIDriverLister not found")
   300  	}
   301  
   302  	csiDriver, err := c.plugin.csiDriverLister.Get(string(c.driverName))
   303  	if err != nil {
   304  		if apierrs.IsNotFound(err) {
   305  			klog.V(4).Infof(log("CSIDriver %q not found, not adding pod information", c.driverName))
   306  			return nil, nil
   307  		}
   308  		return nil, err
   309  	}
   310  
   311  	// if PodInfoOnMount is not set or false we do not set pod attributes
   312  	if csiDriver.Spec.PodInfoOnMount == nil || *csiDriver.Spec.PodInfoOnMount == false {
   313  		klog.V(4).Infof(log("CSIDriver %q does not require pod information", c.driverName))
   314  		return nil, nil
   315  	}
   316  
   317  	attrs := map[string]string{
   318  		"csi.storage.k8s.io/pod.name":            c.pod.Name,
   319  		"csi.storage.k8s.io/pod.namespace":       c.pod.Namespace,
   320  		"csi.storage.k8s.io/pod.uid":             string(c.pod.UID),
   321  		"csi.storage.k8s.io/serviceAccount.name": c.pod.Spec.ServiceAccountName,
   322  	}
   323  	klog.V(4).Infof(log("CSIDriver %q requires pod information", c.driverName))
   324  	return attrs, nil
   325  }
   326  
   327  func (c *csiMountMgr) GetAttributes() volume.Attributes {
   328  	path := c.GetPath()
   329  	supportSelinux, err := c.kubeVolHost.GetHostUtil().GetSELinuxSupport(path)
   330  	if err != nil {
   331  		klog.V(2).Info(log("error checking for SELinux support: %s", err))
   332  		// Best guess
   333  		supportSelinux = false
   334  	}
   335  
   336  	return volume.Attributes{
   337  		ReadOnly:        c.readOnly,
   338  		Managed:         !c.readOnly,
   339  		SupportsSELinux: supportSelinux,
   340  	}
   341  }
   342  
   343  // volume.Unmounter methods
   344  var _ volume.Unmounter = &csiMountMgr{}
   345  
   346  func (c *csiMountMgr) TearDown() error {
   347  	return c.TearDownAt(c.GetPath())
   348  }
   349  func (c *csiMountMgr) TearDownAt(dir string) error {
   350  	klog.V(4).Infof(log("Unmounter.TearDown(%s)", dir))
   351  
   352  	volID := c.volumeID
   353  	csi, err := c.csiClientGetter.Get()
   354  	if err != nil {
   355  		klog.Error(log("mounter.SetUpAt failed to get CSI client: %v", err))
   356  		return err
   357  	}
   358  
   359  	ctx, cancel := context.WithTimeout(context.Background(), csiTimeout)
   360  	defer cancel()
   361  
   362  	if err := csi.NodeUnpublishVolume(ctx, volID, dir); err != nil {
   363  		klog.Errorf(log("mounter.TearDownAt failed: %v", err))
   364  		return err
   365  	}
   366  
   367  	// clean mount point dir
   368  	if err := removeMountDir(c.plugin, dir); err != nil {
   369  		klog.Error(log("mounter.TearDownAt failed to clean mount dir [%s]: %v", dir, err))
   370  		return err
   371  	}
   372  	klog.V(4).Infof(log("mounter.TearDownAt successfully unmounted dir [%s]", dir))
   373  
   374  	return nil
   375  }
   376  
   377  // applyFSGroup applies the volume ownership it derives its logic
   378  // from https://github.com/kubernetes/kubernetes/issues/66323
   379  // 1) if fstype is "", then skip fsgroup (could be indication of non-block filesystem)
   380  // 2) if fstype is provided and pv.AccessMode == ReadWriteOnly and !c.spec.ReadOnly then apply fsgroup
   381  func (c *csiMountMgr) applyFSGroup(fsType string, fsGroup *int64) error {
   382  	if fsGroup != nil {
   383  		if fsType == "" {
   384  			klog.V(4).Info(log("mounter.SetupAt WARNING: skipping fsGroup, fsType not provided"))
   385  			return nil
   386  		}
   387  
   388  		accessModes := c.spec.PersistentVolume.Spec.AccessModes
   389  		if c.spec.PersistentVolume.Spec.AccessModes == nil {
   390  			klog.V(4).Info(log("mounter.SetupAt WARNING: skipping fsGroup, access modes not provided"))
   391  			return nil
   392  		}
   393  		if !hasReadWriteOnce(accessModes) {
   394  			klog.V(4).Info(log("mounter.SetupAt WARNING: skipping fsGroup, only support ReadWriteOnce access mode"))
   395  			return nil
   396  		}
   397  
   398  		if c.readOnly {
   399  			klog.V(4).Info(log("mounter.SetupAt WARNING: skipping fsGroup, volume is readOnly"))
   400  			return nil
   401  		}
   402  
   403  		err := volume.SetVolumeOwnership(c, fsGroup)
   404  		if err != nil {
   405  			return err
   406  		}
   407  
   408  		klog.V(4).Info(log("mounter.SetupAt fsGroup [%d] applied successfully to %s", *fsGroup, c.volumeID))
   409  	}
   410  
   411  	return nil
   412  }
   413  
   414  // isDirMounted returns the !notMounted result from IsLikelyNotMountPoint check
   415  func isDirMounted(plug *csiPlugin, dir string) (bool, error) {
   416  	mounter := plug.host.GetMounter(plug.GetPluginName())
   417  	notMnt, err := mounter.IsLikelyNotMountPoint(dir)
   418  	if err != nil && !os.IsNotExist(err) {
   419  		klog.Error(log("isDirMounted IsLikelyNotMountPoint test failed for dir [%v]", dir))
   420  		return false, err
   421  	}
   422  	return !notMnt, nil
   423  }
   424  
   425  // removeMountDir cleans the mount dir when dir is not mounted and removed the volume data file in dir
   426  func removeMountDir(plug *csiPlugin, mountPath string) error {
   427  	klog.V(4).Info(log("removing mount path [%s]", mountPath))
   428  
   429  	mnt, err := isDirMounted(plug, mountPath)
   430  	if err != nil {
   431  		return err
   432  	}
   433  	if !mnt {
   434  		klog.V(4).Info(log("dir not mounted, deleting it [%s]", mountPath))
   435  		if err := os.Remove(mountPath); err != nil && !os.IsNotExist(err) {
   436  			klog.Error(log("failed to remove dir [%s]: %v", mountPath, err))
   437  			return err
   438  		}
   439  		// remove volume data file as well
   440  		volPath := path.Dir(mountPath)
   441  		dataFile := filepath.Join(volPath, volDataFileName)
   442  		klog.V(4).Info(log("also deleting volume info data file [%s]", dataFile))
   443  		if err := os.Remove(dataFile); err != nil && !os.IsNotExist(err) {
   444  			klog.Error(log("failed to delete volume data file [%s]: %v", dataFile, err))
   445  			return err
   446  		}
   447  		// remove volume path
   448  		klog.V(4).Info(log("deleting volume path [%s]", volPath))
   449  		if err := os.Remove(volPath); err != nil && !os.IsNotExist(err) {
   450  			klog.Error(log("failed to delete volume path [%s]: %v", volPath, err))
   451  			return err
   452  		}
   453  	}
   454  	return nil
   455  }
   456  
   457  // makeVolumeHandle returns csi-<sha256(podUID,volSourceSpecName)>
   458  func makeVolumeHandle(podUID, volSourceSpecName string) string {
   459  	result := sha256.Sum256([]byte(fmt.Sprintf("%s%s", podUID, volSourceSpecName)))
   460  	return fmt.Sprintf("csi-%x", result)
   461  }