github.com/jingruilea/kubeedge@v1.2.0-beta.0.0.20200410162146-4bb8902b3879/edge/pkg/edged/volume/csi/csi_plugin.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_plugin.go
    20  and make some modifications including:
    21  1. remove BlockVolumePlugin.
    22  2. empty skipAttach function.
    23  3. add NewController function.
    24  */
    25  
    26  package csi
    27  
    28  import (
    29  	"context"
    30  	"errors"
    31  	"fmt"
    32  	"os"
    33  	"path"
    34  	"sort"
    35  	"strings"
    36  	"time"
    37  
    38  	api "k8s.io/api/core/v1"
    39  	meta "k8s.io/apimachinery/pkg/apis/meta/v1"
    40  	"k8s.io/apimachinery/pkg/types"
    41  	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
    42  	utilversion "k8s.io/apimachinery/pkg/util/version"
    43  	"k8s.io/apimachinery/pkg/util/wait"
    44  	utilfeature "k8s.io/apiserver/pkg/util/feature"
    45  	clientset "k8s.io/client-go/kubernetes"
    46  	storagelisters "k8s.io/client-go/listers/storage/v1beta1"
    47  	csitranslationplugins "k8s.io/csi-translation-lib/plugins"
    48  	"k8s.io/klog"
    49  	"k8s.io/kubernetes/pkg/features"
    50  	"k8s.io/kubernetes/pkg/volume"
    51  
    52  	"github.com/kubeedge/kubeedge/edge/pkg/edged/volume/csi/nodeinfomanager"
    53  )
    54  
    55  const (
    56  	// CSIPluginName is the name of the in-tree CSI Plugin
    57  	CSIPluginName = "kubernetes.io/csi"
    58  
    59  	// TODO (vladimirvivien) implement a more dynamic way to discover
    60  	// the unix domain socket path for each installed csi driver.
    61  	// TODO (vladimirvivien) would be nice to name socket with a .sock extension
    62  	// for consistency.
    63  	csiAddrTemplate = "/var/lib/kubelet/plugins/%v/csi.sock"
    64  	csiTimeout      = 2 * time.Minute
    65  	volNameSep      = "^"
    66  	volDataFileName = "vol_data.json"
    67  	fsTypeBlockName = "block"
    68  
    69  	// TODO: increase to something useful
    70  	csiResyncPeriod = time.Minute
    71  )
    72  
    73  var deprecatedSocketDirVersions = []string{"0.1.0", "0.2.0", "0.3.0", "0.4.0"}
    74  
    75  type csiPlugin struct {
    76  	host            volume.VolumeHost
    77  	blockEnabled    bool
    78  	csiDriverLister storagelisters.CSIDriverLister
    79  }
    80  
    81  //TODO (vladimirvivien) add this type to storage api
    82  type driverMode string
    83  
    84  const persistentDriverMode driverMode = "persistent"
    85  const ephemeralDriverMode driverMode = "ephemeral"
    86  
    87  // ProbeVolumePlugins returns implemented plugins
    88  func ProbeVolumePlugins() []volume.VolumePlugin {
    89  	p := &csiPlugin{
    90  		host:         nil,
    91  		blockEnabled: utilfeature.DefaultFeatureGate.Enabled(features.CSIBlockVolume),
    92  	}
    93  	return []volume.VolumePlugin{p}
    94  }
    95  
    96  // volume.VolumePlugin methods
    97  var _ volume.VolumePlugin = &csiPlugin{}
    98  
    99  // RegistrationHandler is the handler which is fed to the pluginwatcher API.
   100  type RegistrationHandler struct {
   101  }
   102  
   103  // TODO (verult) consider using a struct instead of global variables
   104  // csiDrivers map keep track of all registered CSI drivers on the node and their
   105  // corresponding sockets
   106  var csiDrivers = &DriversStore{}
   107  
   108  var nim nodeinfomanager.Interface
   109  
   110  // PluginHandler is the plugin registration handler interface passed to the
   111  // pluginwatcher module in kubelet
   112  var PluginHandler = &RegistrationHandler{}
   113  
   114  // ValidatePlugin is called by kubelet's plugin watcher upon detection
   115  // of a new registration socket opened by CSI Driver registrar side car.
   116  func (h *RegistrationHandler) ValidatePlugin(pluginName string, endpoint string, versions []string) error {
   117  	klog.Infof(log("Trying to validate a new CSI Driver with name: %s endpoint: %s versions: %s",
   118  		pluginName, endpoint, strings.Join(versions, ",")))
   119  
   120  	_, err := h.validateVersions("ValidatePlugin", pluginName, endpoint, versions)
   121  	if err != nil {
   122  		return fmt.Errorf("validation failed for CSI Driver %s at endpoint %s: %v", pluginName, endpoint, err)
   123  	}
   124  
   125  	return err
   126  }
   127  
   128  // RegisterPlugin is called when a plugin can be registered
   129  func (h *RegistrationHandler) RegisterPlugin(pluginName string, endpoint string, versions []string) error {
   130  	klog.Infof(log("Register new plugin with name: %s at endpoint: %s", pluginName, endpoint))
   131  
   132  	highestSupportedVersion, err := h.validateVersions("RegisterPlugin", pluginName, endpoint, versions)
   133  	if err != nil {
   134  		return err
   135  	}
   136  
   137  	// Storing endpoint of newly registered CSI driver into the map, where CSI driver name will be the key
   138  	// all other CSI components will be able to get the actual socket of CSI drivers by its name.
   139  	csiDrivers.Set(pluginName, Driver{
   140  		endpoint:                endpoint,
   141  		highestSupportedVersion: highestSupportedVersion,
   142  	})
   143  
   144  	// Get node info from the driver.
   145  	csi, err := newCsiDriverClient(csiDriverName(pluginName))
   146  	if err != nil {
   147  		return err
   148  	}
   149  
   150  	ctx, cancel := context.WithTimeout(context.Background(), csiTimeout)
   151  	defer cancel()
   152  
   153  	driverNodeID, maxVolumePerNode, accessibleTopology, err := csi.NodeGetInfo(ctx)
   154  	if err != nil {
   155  		if unregErr := unregisterDriver(pluginName); unregErr != nil {
   156  			klog.Error(log("registrationHandler.RegisterPlugin failed to unregister plugin due to previous error: %v", unregErr))
   157  		}
   158  		return err
   159  	}
   160  
   161  	err = nim.InstallCSIDriver(pluginName, driverNodeID, maxVolumePerNode, accessibleTopology)
   162  	if err != nil {
   163  		if unregErr := unregisterDriver(pluginName); unregErr != nil {
   164  			klog.Error(log("registrationHandler.RegisterPlugin failed to unregister plugin due to previous error: %v", unregErr))
   165  		}
   166  		return err
   167  	}
   168  
   169  	return nil
   170  }
   171  
   172  func (h *RegistrationHandler) validateVersions(callerName, pluginName string, endpoint string, versions []string) (*utilversion.Version, error) {
   173  	if len(versions) == 0 {
   174  		err := fmt.Errorf("%s for CSI driver %q failed. Plugin returned an empty list for supported versions", callerName, pluginName)
   175  		klog.Error(err)
   176  		return nil, err
   177  	}
   178  
   179  	// Validate version
   180  	newDriverHighestVersion, err := highestSupportedVersion(versions)
   181  	if err != nil {
   182  		err := fmt.Errorf("%s for CSI driver %q failed. None of the versions specified %q are supported. err=%v", callerName, pluginName, versions, err)
   183  		klog.Error(err)
   184  		return nil, err
   185  	}
   186  
   187  	existingDriver, driverExists := csiDrivers.Get(pluginName)
   188  	if driverExists {
   189  		if !existingDriver.highestSupportedVersion.LessThan(newDriverHighestVersion) {
   190  			err := fmt.Errorf("%s for CSI driver %q failed. Another driver with the same name is already registered with a higher supported version: %q", callerName, pluginName, existingDriver.highestSupportedVersion)
   191  			klog.Error(err)
   192  			return nil, err
   193  		}
   194  	}
   195  
   196  	return newDriverHighestVersion, nil
   197  }
   198  
   199  // DeRegisterPlugin is called when a plugin removed its socket, signaling
   200  // it is no longer available
   201  func (h *RegistrationHandler) DeRegisterPlugin(pluginName string) {
   202  	klog.V(4).Info(log("registrationHandler.DeRegisterPlugin request for plugin %s", pluginName))
   203  	if err := unregisterDriver(pluginName); err != nil {
   204  		klog.Error(log("registrationHandler.DeRegisterPlugin failed: %v", err))
   205  	}
   206  }
   207  
   208  func (p *csiPlugin) Init(host volume.VolumeHost) error {
   209  	p.host = host
   210  
   211  	if utilfeature.DefaultFeatureGate.Enabled(features.CSIDriverRegistry) {
   212  		csiClient := host.GetKubeClient()
   213  		if csiClient == nil {
   214  			klog.Warning(log("kubeclient not set, assuming standalone kubelet"))
   215  		} else {
   216  			// set CSIDriverLister
   217  			adcHost, ok := host.(volume.AttachDetachVolumeHost)
   218  			if ok {
   219  				p.csiDriverLister = adcHost.CSIDriverLister()
   220  				if p.csiDriverLister == nil {
   221  					klog.Error(log("CSIDriverLister not found on AttachDetachVolumeHost"))
   222  				}
   223  			}
   224  			kletHost, ok := host.(volume.KubeletVolumeHost)
   225  			if ok {
   226  				p.csiDriverLister = kletHost.CSIDriverLister()
   227  				if p.csiDriverLister == nil {
   228  					klog.Error(log("CSIDriverLister not found on KubeletVolumeHost"))
   229  				}
   230  			}
   231  		}
   232  	}
   233  
   234  	var migratedPlugins = map[string](func() bool){
   235  		csitranslationplugins.GCEPDInTreePluginName: func() bool {
   236  			return utilfeature.DefaultFeatureGate.Enabled(features.CSIMigration) && utilfeature.DefaultFeatureGate.Enabled(features.CSIMigrationGCE)
   237  		},
   238  		csitranslationplugins.AWSEBSInTreePluginName: func() bool {
   239  			return utilfeature.DefaultFeatureGate.Enabled(features.CSIMigration) && utilfeature.DefaultFeatureGate.Enabled(features.CSIMigrationAWS)
   240  		},
   241  		csitranslationplugins.CinderInTreePluginName: func() bool {
   242  			return utilfeature.DefaultFeatureGate.Enabled(features.CSIMigration) && utilfeature.DefaultFeatureGate.Enabled(features.CSIMigrationOpenStack)
   243  		},
   244  	}
   245  
   246  	// Initializing the label management channels
   247  	nim = nodeinfomanager.NewNodeInfoManager(host.GetNodeName(), host, migratedPlugins)
   248  
   249  	// TODO: Evaluate the feature releated to csi
   250  	/*if utilfeature.DefaultFeatureGate.Enabled(features.CSINodeInfo) &&
   251  		utilfeature.DefaultFeatureGate.Enabled(features.CSIMigration) {
   252  		// This function prevents Kubelet from posting Ready status until CSINodeInfo
   253  		// is both installed and initialized
   254  		if err := initializeCSINode(host); err != nil {
   255  			return fmt.Errorf("failed to initialize CSINodeInfo: %v", err)
   256  		}
   257  	}*/
   258  
   259  	return nil
   260  }
   261  
   262  func initializeCSINode(host volume.VolumeHost) error {
   263  	kvh, ok := host.(volume.KubeletVolumeHost)
   264  	if !ok {
   265  		klog.V(4).Info("Cast from VolumeHost to KubeletVolumeHost failed. Skipping CSINodeInfo initialization, not running on kubelet")
   266  		return nil
   267  	}
   268  	kubeClient := host.GetKubeClient()
   269  	if kubeClient == nil {
   270  		// Kubelet running in standalone mode. Skip CSINodeInfo initialization
   271  		klog.Warning("Skipping CSINodeInfo initialization, kubelet running in standalone mode")
   272  		return nil
   273  	}
   274  
   275  	kvh.SetKubeletError(errors.New("CSINodeInfo is not yet initialized"))
   276  
   277  	go func() {
   278  		defer utilruntime.HandleCrash()
   279  
   280  		// Backoff parameters tuned to retry over 140 seconds. Will fail and restart the Kubelet
   281  		// after max retry steps.
   282  		initBackoff := wait.Backoff{
   283  			Steps:    6,
   284  			Duration: 15 * time.Millisecond,
   285  			Factor:   6.0,
   286  			Jitter:   0.1,
   287  		}
   288  		err := wait.ExponentialBackoff(initBackoff, func() (bool, error) {
   289  			klog.V(4).Infof("Initializing migrated drivers on CSINodeInfo")
   290  			err := nim.InitializeCSINodeWithAnnotation()
   291  			if err != nil {
   292  				kvh.SetKubeletError(fmt.Errorf("Failed to initialize CSINodeInfo: %v", err))
   293  				klog.Errorf("Failed to initialize CSINodeInfo: %v", err)
   294  				return false, nil
   295  			}
   296  
   297  			// Successfully initialized drivers, allow Kubelet to post Ready
   298  			kvh.SetKubeletError(nil)
   299  			return true, nil
   300  		})
   301  		if err != nil {
   302  			// 2 releases after CSIMigration and all CSIMigrationX (where X is a volume plugin)
   303  			// are permanently enabled the apiserver/controllers can assume that the kubelet is
   304  			// using CSI for all Migrated volume plugins. Then all the CSINode initialization
   305  			// code can be dropped from Kubelet.
   306  			// Kill the Kubelet process and allow it to restart to retry initialization
   307  			klog.Fatalf("Failed to initialize CSINodeInfo after retrying")
   308  		}
   309  	}()
   310  	return nil
   311  }
   312  
   313  func (p *csiPlugin) GetPluginName() string {
   314  	return CSIPluginName
   315  }
   316  
   317  // GetvolumeName returns a concatenated string of CSIVolumeSource.Driver<volNameSe>CSIVolumeSource.VolumeHandle
   318  // That string value is used in Detach() to extract driver name and volumeName.
   319  func (p *csiPlugin) GetVolumeName(spec *volume.Spec) (string, error) {
   320  	csi, err := getPVSourceFromSpec(spec)
   321  	if err != nil {
   322  		klog.Error(log("plugin.GetVolumeName failed to extract volume source from spec: %v", err))
   323  		return "", err
   324  	}
   325  
   326  	// return driverName<separator>volumeHandle
   327  	return fmt.Sprintf("%s%s%s", csi.Driver, volNameSep, csi.VolumeHandle), nil
   328  }
   329  
   330  func (p *csiPlugin) CanSupport(spec *volume.Spec) bool {
   331  	// TODO (vladimirvivien) CanSupport should also take into account
   332  	// the availability/registration of specified Driver in the volume source
   333  	if spec == nil {
   334  		return false
   335  	}
   336  	if utilfeature.DefaultFeatureGate.Enabled(features.CSIInlineVolume) {
   337  		return (spec.PersistentVolume != nil && spec.PersistentVolume.Spec.CSI != nil) ||
   338  			(spec.Volume != nil && spec.Volume.CSI != nil)
   339  	}
   340  
   341  	return spec.PersistentVolume != nil && spec.PersistentVolume.Spec.CSI != nil
   342  }
   343  
   344  func (p *csiPlugin) IsMigratedToCSI() bool {
   345  	return false
   346  }
   347  
   348  func (p *csiPlugin) RequiresRemount() bool {
   349  	return false
   350  }
   351  
   352  func (p *csiPlugin) NewMounter(
   353  	spec *volume.Spec,
   354  	pod *api.Pod,
   355  	_ volume.VolumeOptions) (volume.Mounter, error) {
   356  
   357  	volSrc, pvSrc, err := getSourceFromSpec(spec)
   358  	if err != nil {
   359  		return nil, err
   360  	}
   361  
   362  	var (
   363  		driverName   string
   364  		volumeHandle string
   365  		readOnly     bool
   366  	)
   367  
   368  	switch {
   369  	case volSrc != nil && utilfeature.DefaultFeatureGate.Enabled(features.CSIInlineVolume):
   370  		volumeHandle = makeVolumeHandle(string(pod.UID), spec.Name())
   371  		driverName = volSrc.Driver
   372  		if volSrc.ReadOnly != nil {
   373  			readOnly = *volSrc.ReadOnly
   374  		}
   375  	case pvSrc != nil:
   376  		driverName = pvSrc.Driver
   377  		volumeHandle = pvSrc.VolumeHandle
   378  		readOnly = spec.ReadOnly
   379  	default:
   380  		return nil, fmt.Errorf("volume source not found in volume.Spec")
   381  	}
   382  
   383  	driverMode, err := p.getDriverMode(spec)
   384  	if err != nil {
   385  		return nil, err
   386  	}
   387  
   388  	k8s := p.host.GetKubeClient()
   389  	if k8s == nil {
   390  		klog.Error(log("failed to get a kubernetes client"))
   391  		return nil, errors.New("failed to get a Kubernetes client")
   392  	}
   393  
   394  	kvh, ok := p.host.(volume.KubeletVolumeHost)
   395  	if !ok {
   396  		return nil, errors.New(log("cast from VolumeHost to KubeletVolumeHost failed"))
   397  	}
   398  
   399  	mounter := &csiMountMgr{
   400  		plugin:       p,
   401  		k8s:          k8s,
   402  		spec:         spec,
   403  		pod:          pod,
   404  		podUID:       pod.UID,
   405  		driverName:   csiDriverName(driverName),
   406  		driverMode:   driverMode,
   407  		volumeID:     volumeHandle,
   408  		specVolumeID: spec.Name(),
   409  		readOnly:     readOnly,
   410  		kubeVolHost:  kvh,
   411  	}
   412  	mounter.csiClientGetter.driverName = csiDriverName(driverName)
   413  
   414  	// Save volume info in pod dir
   415  	dir := mounter.GetPath()
   416  	dataDir := path.Dir(dir) // dropoff /mount at end
   417  
   418  	if err := os.MkdirAll(dataDir, 0750); err != nil {
   419  		klog.Error(log("failed to create dir %#v:  %v", dataDir, err))
   420  		return nil, err
   421  	}
   422  	klog.V(4).Info(log("created path successfully [%s]", dataDir))
   423  
   424  	// persist volume info data for teardown
   425  	node := string(p.host.GetNodeName())
   426  	volData := map[string]string{
   427  		volDataKey.specVolID:  spec.Name(),
   428  		volDataKey.volHandle:  volumeHandle,
   429  		volDataKey.driverName: driverName,
   430  		volDataKey.nodeName:   node,
   431  		volDataKey.driverMode: string(driverMode),
   432  	}
   433  
   434  	attachID := getAttachmentName(volumeHandle, driverName, node)
   435  	volData[volDataKey.attachmentID] = attachID
   436  
   437  	if err := saveVolumeData(dataDir, volDataFileName, volData); err != nil {
   438  		klog.Error(log("failed to save volume info data: %v", err))
   439  		if err := os.RemoveAll(dataDir); err != nil {
   440  			klog.Error(log("failed to remove dir after error [%s]: %v", dataDir, err))
   441  			return nil, err
   442  		}
   443  		return nil, err
   444  	}
   445  
   446  	klog.V(4).Info(log("mounter created successfully"))
   447  
   448  	return mounter, nil
   449  }
   450  
   451  func (p *csiPlugin) NewUnmounter(specName string, podUID types.UID) (volume.Unmounter, error) {
   452  	klog.V(4).Infof(log("setting up unmounter for [name=%v, podUID=%v]", specName, podUID))
   453  
   454  	kvh, ok := p.host.(volume.KubeletVolumeHost)
   455  	if !ok {
   456  		return nil, errors.New(log("cast from VolumeHost to KubeletVolumeHost failed"))
   457  	}
   458  
   459  	unmounter := &csiMountMgr{
   460  		plugin:       p,
   461  		podUID:       podUID,
   462  		specVolumeID: specName,
   463  		kubeVolHost:  kvh,
   464  	}
   465  
   466  	// load volume info from file
   467  	dir := unmounter.GetPath()
   468  	dataDir := path.Dir(dir) // dropoff /mount at end
   469  	data, err := loadVolumeData(dataDir, volDataFileName)
   470  	if err != nil {
   471  		klog.Error(log("unmounter failed to load volume data file [%s]: %v", dir, err))
   472  		return nil, err
   473  	}
   474  	unmounter.driverName = csiDriverName(data[volDataKey.driverName])
   475  	unmounter.volumeID = data[volDataKey.volHandle]
   476  	unmounter.csiClientGetter.driverName = unmounter.driverName
   477  
   478  	return unmounter, nil
   479  }
   480  
   481  func (p *csiPlugin) ConstructVolumeSpec(volumeName, mountPath string) (*volume.Spec, error) {
   482  	klog.V(4).Info(log("plugin.ConstructVolumeSpec [pv.Name=%v, path=%v]", volumeName, mountPath))
   483  
   484  	volData, err := loadVolumeData(mountPath, volDataFileName)
   485  	if err != nil {
   486  		klog.Error(log("plugin.ConstructVolumeSpec failed loading volume data using [%s]: %v", mountPath, err))
   487  		return nil, err
   488  	}
   489  
   490  	klog.V(4).Info(log("plugin.ConstructVolumeSpec extracted [%#v]", volData))
   491  
   492  	var spec *volume.Spec
   493  	inlineEnabled := utilfeature.DefaultFeatureGate.Enabled(features.CSIInlineVolume)
   494  
   495  	if inlineEnabled {
   496  		mode := driverMode(volData[volDataKey.driverMode])
   497  		switch {
   498  		case mode == ephemeralDriverMode:
   499  			spec = p.constructVolSourceSpec(volData[volDataKey.specVolID], volData[volDataKey.driverName])
   500  
   501  		case mode == persistentDriverMode:
   502  			fallthrough
   503  		default:
   504  			spec = p.constructPVSourceSpec(volData[volDataKey.specVolID], volData[volDataKey.driverName], volData[volDataKey.volHandle])
   505  		}
   506  	} else {
   507  		spec = p.constructPVSourceSpec(volData[volDataKey.specVolID], volData[volDataKey.driverName], volData[volDataKey.volHandle])
   508  	}
   509  
   510  	return spec, nil
   511  }
   512  
   513  // constructVolSourceSpec constructs volume.Spec with CSIVolumeSource
   514  func (p *csiPlugin) constructVolSourceSpec(volSpecName, driverName string) *volume.Spec {
   515  	vol := &api.Volume{
   516  		Name: volSpecName,
   517  		VolumeSource: api.VolumeSource{
   518  			CSI: &api.CSIVolumeSource{
   519  				Driver: driverName,
   520  			},
   521  		},
   522  	}
   523  	return volume.NewSpecFromVolume(vol)
   524  }
   525  
   526  //constructPVSourceSpec constructs volume.Spec with CSIPersistentVolumeSource
   527  func (p *csiPlugin) constructPVSourceSpec(volSpecName, driverName, volumeHandle string) *volume.Spec {
   528  	fsMode := api.PersistentVolumeFilesystem
   529  	pv := &api.PersistentVolume{
   530  		ObjectMeta: meta.ObjectMeta{
   531  			Name: volSpecName,
   532  		},
   533  		Spec: api.PersistentVolumeSpec{
   534  			PersistentVolumeSource: api.PersistentVolumeSource{
   535  				CSI: &api.CSIPersistentVolumeSource{
   536  					Driver:       driverName,
   537  					VolumeHandle: volumeHandle,
   538  				},
   539  			},
   540  			VolumeMode: &fsMode,
   541  		},
   542  	}
   543  	return volume.NewSpecFromPersistentVolume(pv, false)
   544  }
   545  
   546  func (p *csiPlugin) SupportsMountOption() bool {
   547  	// TODO (vladimirvivien) use CSI VolumeCapability.MountVolume.mount_flags
   548  	// to probe for the result for this method
   549  	// (bswartz) Until the CSI spec supports probing, our only option is to
   550  	// make plugins register their support for mount options or lack thereof
   551  	// directly with kubernetes.
   552  	return true
   553  }
   554  
   555  func (p *csiPlugin) SupportsBulkVolumeVerification() bool {
   556  	return false
   557  }
   558  
   559  // volume.AttachableVolumePlugin methods
   560  var _ volume.AttachableVolumePlugin = &csiPlugin{}
   561  
   562  var _ volume.DeviceMountableVolumePlugin = &csiPlugin{}
   563  
   564  func (p *csiPlugin) NewAttacher() (volume.Attacher, error) {
   565  	return p.newAttacherDetacher()
   566  }
   567  
   568  func (p *csiPlugin) NewDeviceMounter() (volume.DeviceMounter, error) {
   569  	return p.NewAttacher()
   570  }
   571  
   572  func (p *csiPlugin) NewDetacher() (volume.Detacher, error) {
   573  	return p.newAttacherDetacher()
   574  }
   575  
   576  func (p *csiPlugin) CanAttach(spec *volume.Spec) (bool, error) {
   577  	driverMode, err := p.getDriverMode(spec)
   578  	if err != nil {
   579  		return false, err
   580  	}
   581  
   582  	if driverMode == ephemeralDriverMode {
   583  		klog.V(5).Info(log("plugin.CanAttach = false, ephemeral mode detected for spec %v", spec.Name()))
   584  		return false, nil
   585  	}
   586  
   587  	pvSrc, err := getCSISourceFromSpec(spec)
   588  	if err != nil {
   589  		return false, err
   590  	}
   591  
   592  	driverName := pvSrc.Driver
   593  
   594  	skipAttach, err := p.skipAttach(driverName)
   595  	if err != nil {
   596  		return false, err
   597  	}
   598  
   599  	return !skipAttach, nil
   600  }
   601  
   602  // CanDeviceMount returns true if the spec supports device mount
   603  func (p *csiPlugin) CanDeviceMount(spec *volume.Spec) (bool, error) {
   604  	driverMode, err := p.getDriverMode(spec)
   605  	if err != nil {
   606  		return false, err
   607  	}
   608  
   609  	if driverMode == ephemeralDriverMode {
   610  		klog.V(5).Info(log("plugin.CanDeviceMount skipped ephemeral mode detected for spec %v", spec.Name()))
   611  		return false, nil
   612  	}
   613  
   614  	return true, nil
   615  }
   616  
   617  func (p *csiPlugin) NewDeviceUnmounter() (volume.DeviceUnmounter, error) {
   618  	return p.NewDetacher()
   619  }
   620  
   621  func (p *csiPlugin) GetDeviceMountRefs(deviceMountPath string) ([]string, error) {
   622  	m := p.host.GetMounter(p.GetPluginName())
   623  	return m.GetMountRefs(deviceMountPath)
   624  }
   625  
   626  // skipAttach looks up CSIDriver object associated with driver name
   627  // to determine if driver requires attachment volume operation
   628  func (p *csiPlugin) skipAttach(driver string) (bool, error) {
   629  	return false, nil
   630  }
   631  
   632  // getDriverMode returns the driver mode for the specified spec: {persistent|ephemeral}.
   633  // 1) If mode cannot be determined, it will default to "persistent".
   634  // 2) If Mode cannot be resolved to either {persistent | ephemeral}, an error is returned
   635  // See https://github.com/kubernetes/enhancements/blob/master/keps/sig-storage/20190122-csi-inline-volumes.md
   636  func (p *csiPlugin) getDriverMode(spec *volume.Spec) (driverMode, error) {
   637  	// TODO (vladimirvivien) ultimately, mode will be retrieved from CSIDriver.Spec.Mode.
   638  	// However, in alpha version, mode is determined by the volume source:
   639  	// 1) if volume.Spec.Volume.CSI != nil -> mode is ephemeral
   640  	// 2) if volume.Spec.PersistentVolume.Spec.CSI != nil -> persistent
   641  	volSrc, _, err := getSourceFromSpec(spec)
   642  	if err != nil {
   643  		return "", err
   644  	}
   645  
   646  	if volSrc != nil && utilfeature.DefaultFeatureGate.Enabled(features.CSIInlineVolume) {
   647  		return ephemeralDriverMode, nil
   648  	}
   649  	return persistentDriverMode, nil
   650  }
   651  
   652  func (p *csiPlugin) getPublishContext(client clientset.Interface, handle, driver, nodeName string) (map[string]string, error) {
   653  	skip, err := p.skipAttach(driver)
   654  	if err != nil {
   655  		return nil, err
   656  	}
   657  	if skip {
   658  		return nil, nil
   659  	}
   660  
   661  	attachID := getAttachmentName(handle, driver, nodeName)
   662  
   663  	// search for attachment by VolumeAttachment.Spec.Source.PersistentVolumeName
   664  	attachment, err := client.StorageV1().VolumeAttachments().Get(attachID, meta.GetOptions{})
   665  	if err != nil {
   666  		return nil, err // This err already has enough context ("VolumeAttachment xyz not found")
   667  	}
   668  
   669  	if attachment == nil {
   670  		err = errors.New("no existing VolumeAttachment found")
   671  		return nil, err
   672  	}
   673  	return attachment.Status.AttachmentMetadata, nil
   674  }
   675  
   676  func (p *csiPlugin) newAttacherDetacher() (*csiAttacher, error) {
   677  	k8s := p.host.GetKubeClient()
   678  	if k8s == nil {
   679  		klog.Error(log("unable to get kubernetes client from host"))
   680  		return nil, errors.New("unable to get Kubernetes client")
   681  	}
   682  
   683  	return &csiAttacher{
   684  		plugin:        p,
   685  		k8s:           k8s,
   686  		waitSleepTime: 1 * time.Second,
   687  	}, nil
   688  }
   689  
   690  // NewController returns a csi controller instance
   691  func NewController() *Controller {
   692  	return &Controller{}
   693  }
   694  
   695  func unregisterDriver(driverName string) error {
   696  	csiDrivers.Delete(driverName)
   697  
   698  	if err := nim.UninstallCSIDriver(driverName); err != nil {
   699  		klog.Errorf("Error uninstalling CSI driver: %v", err)
   700  		return err
   701  	}
   702  
   703  	return nil
   704  }
   705  
   706  // Return the highest supported version
   707  func highestSupportedVersion(versions []string) (*utilversion.Version, error) {
   708  	if len(versions) == 0 {
   709  		return nil, fmt.Errorf("CSI driver reporting empty array for supported versions")
   710  	}
   711  
   712  	// Sort by lowest to highest version
   713  	sort.Slice(versions, func(i, j int) bool {
   714  		parsedVersionI, err := utilversion.ParseGeneric(versions[i])
   715  		if err != nil {
   716  			// Push bad values to the bottom
   717  			return true
   718  		}
   719  
   720  		parsedVersionJ, err := utilversion.ParseGeneric(versions[j])
   721  		if err != nil {
   722  			// Push bad values to the bottom
   723  			return false
   724  		}
   725  
   726  		return parsedVersionI.LessThan(parsedVersionJ)
   727  	})
   728  
   729  	for i := len(versions) - 1; i >= 0; i-- {
   730  		highestSupportedVersion, err := utilversion.ParseGeneric(versions[i])
   731  		if err != nil {
   732  			return nil, err
   733  		}
   734  
   735  		if highestSupportedVersion.Major() <= 1 {
   736  			return highestSupportedVersion, nil
   737  		}
   738  	}
   739  
   740  	return nil, fmt.Errorf("None of the CSI versions reported by this driver are supported")
   741  }
   742  
   743  // Only drivers that implement CSI 0.x are allowed to use deprecated socket dir.
   744  func isDeprecatedSocketDirAllowed(versions []string) bool {
   745  	for _, version := range versions {
   746  		if isV0Version(version) {
   747  			return true
   748  		}
   749  	}
   750  
   751  	return false
   752  }
   753  
   754  func isV0Version(version string) bool {
   755  	parsedVersion, err := utilversion.ParseGeneric(version)
   756  	if err != nil {
   757  		return false
   758  	}
   759  
   760  	return parsedVersion.Major() == 0
   761  }