k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/pkg/volume/plugins.go (about)

     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 volume
    18  
    19  import (
    20  	"fmt"
    21  	"net"
    22  	"strings"
    23  	"sync"
    24  
    25  	"k8s.io/apimachinery/pkg/util/sets"
    26  	"k8s.io/klog/v2"
    27  	"k8s.io/mount-utils"
    28  	"k8s.io/utils/exec"
    29  
    30  	authenticationv1 "k8s.io/api/authentication/v1"
    31  	v1 "k8s.io/api/core/v1"
    32  	"k8s.io/apimachinery/pkg/api/resource"
    33  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    34  	"k8s.io/apimachinery/pkg/types"
    35  	utilerrors "k8s.io/apimachinery/pkg/util/errors"
    36  	"k8s.io/apimachinery/pkg/util/validation"
    37  	"k8s.io/client-go/informers"
    38  	clientset "k8s.io/client-go/kubernetes"
    39  	storagelistersv1 "k8s.io/client-go/listers/storage/v1"
    40  	"k8s.io/client-go/tools/cache"
    41  	"k8s.io/client-go/tools/record"
    42  	"k8s.io/kubernetes/pkg/volume/util/hostutil"
    43  	"k8s.io/kubernetes/pkg/volume/util/recyclerclient"
    44  	"k8s.io/kubernetes/pkg/volume/util/subpath"
    45  )
    46  
    47  type ProbeOperation uint32
    48  type ProbeEvent struct {
    49  	Plugin     VolumePlugin // VolumePlugin that was added/updated/removed. if ProbeEvent.Op is 'ProbeRemove', Plugin should be nil
    50  	PluginName string
    51  	Op         ProbeOperation // The operation to the plugin
    52  }
    53  
    54  const (
    55  	// Common parameter which can be specified in StorageClass to specify the desired FSType
    56  	// Provisioners SHOULD implement support for this if they are block device based
    57  	// Must be a filesystem type supported by the host operating system.
    58  	// Ex. "ext4", "xfs", "ntfs". Default value depends on the provisioner
    59  	VolumeParameterFSType = "fstype"
    60  
    61  	ProbeAddOrUpdate ProbeOperation = 1 << iota
    62  	ProbeRemove
    63  )
    64  
    65  // VolumeOptions contains option information about a volume.
    66  type VolumeOptions struct {
    67  	// The attributes below are required by volume.Provisioner
    68  	// TODO: refactor all of this out of volumes when an admin can configure
    69  	// many kinds of provisioners.
    70  
    71  	// Reclamation policy for a persistent volume
    72  	PersistentVolumeReclaimPolicy v1.PersistentVolumeReclaimPolicy
    73  	// Mount options for a persistent volume
    74  	MountOptions []string
    75  	// Suggested PV.Name of the PersistentVolume to provision.
    76  	// This is a generated name guaranteed to be unique in Kubernetes cluster.
    77  	// If you choose not to use it as volume name, ensure uniqueness by either
    78  	// combining it with your value or create unique values of your own.
    79  	PVName string
    80  	// PVC is reference to the claim that lead to provisioning of a new PV.
    81  	// Provisioners *must* create a PV that would be matched by this PVC,
    82  	// i.e. with required capacity, accessMode, labels matching PVC.Selector and
    83  	// so on.
    84  	PVC *v1.PersistentVolumeClaim
    85  	// Volume provisioning parameters from StorageClass
    86  	Parameters map[string]string
    87  }
    88  
    89  // NodeResizeOptions contain options to be passed for node expansion.
    90  type NodeResizeOptions struct {
    91  	VolumeSpec *Spec
    92  
    93  	// DevicePath - location of actual device on the node. In case of CSI
    94  	// this just could be volumeID
    95  	DevicePath string
    96  
    97  	// DeviceMountPath location where device is mounted on the node. If volume type
    98  	// is attachable - this would be global mount path otherwise
    99  	// it would be location where volume was mounted for the pod
   100  	DeviceMountPath string
   101  
   102  	// DeviceStagingPath stores location where the volume is staged
   103  	DeviceStagePath string
   104  
   105  	NewSize resource.Quantity
   106  	OldSize resource.Quantity
   107  }
   108  
   109  type DynamicPluginProber interface {
   110  	Init() error
   111  
   112  	// aggregates events for successful drivers and errors for failed drivers
   113  	Probe() (events []ProbeEvent, err error)
   114  }
   115  
   116  // VolumePlugin is an interface to volume plugins that can be used on a
   117  // kubernetes node (e.g. by kubelet) to instantiate and manage volumes.
   118  type VolumePlugin interface {
   119  	// Init initializes the plugin.  This will be called exactly once
   120  	// before any New* calls are made - implementations of plugins may
   121  	// depend on this.
   122  	Init(host VolumeHost) error
   123  
   124  	// Name returns the plugin's name.  Plugins must use namespaced names
   125  	// such as "example.com/volume" and contain exactly one '/' character.
   126  	// The "kubernetes.io" namespace is reserved for plugins which are
   127  	// bundled with kubernetes.
   128  	GetPluginName() string
   129  
   130  	// GetVolumeName returns the name/ID to uniquely identifying the actual
   131  	// backing device, directory, path, etc. referenced by the specified volume
   132  	// spec.
   133  	// For Attachable volumes, this value must be able to be passed back to
   134  	// volume Detach methods to identify the device to act on.
   135  	// If the plugin does not support the given spec, this returns an error.
   136  	GetVolumeName(spec *Spec) (string, error)
   137  
   138  	// CanSupport tests whether the plugin supports a given volume
   139  	// specification from the API.  The spec pointer should be considered
   140  	// const.
   141  	CanSupport(spec *Spec) bool
   142  
   143  	// RequiresRemount returns true if this plugin requires mount calls to be
   144  	// reexecuted. Atomically updating volumes, like Downward API, depend on
   145  	// this to update the contents of the volume.
   146  	RequiresRemount(spec *Spec) bool
   147  
   148  	// NewMounter creates a new volume.Mounter from an API specification.
   149  	// Ownership of the spec pointer in *not* transferred.
   150  	// - spec: The v1.Volume spec
   151  	// - pod: The enclosing pod
   152  	NewMounter(spec *Spec, podRef *v1.Pod, opts VolumeOptions) (Mounter, error)
   153  
   154  	// NewUnmounter creates a new volume.Unmounter from recoverable state.
   155  	// - name: The volume name, as per the v1.Volume spec.
   156  	// - podUID: The UID of the enclosing pod
   157  	NewUnmounter(name string, podUID types.UID) (Unmounter, error)
   158  
   159  	// ConstructVolumeSpec constructs a volume spec based on the given volume name
   160  	// and volumePath. The spec may have incomplete information due to limited
   161  	// information from input. This function is used by volume manager to reconstruct
   162  	// volume spec by reading the volume directories from disk
   163  	ConstructVolumeSpec(volumeName, volumePath string) (ReconstructedVolume, error)
   164  
   165  	// SupportsMountOption returns true if volume plugins supports Mount options
   166  	// Specifying mount options in a volume plugin that doesn't support
   167  	// user specified mount options will result in error creating persistent volumes
   168  	SupportsMountOption() bool
   169  
   170  	// SupportsSELinuxContextMount returns true if volume plugins supports
   171  	// mount -o context=XYZ for a given volume.
   172  	SupportsSELinuxContextMount(spec *Spec) (bool, error)
   173  }
   174  
   175  // PersistentVolumePlugin is an extended interface of VolumePlugin and is used
   176  // by volumes that want to provide long term persistence of data
   177  type PersistentVolumePlugin interface {
   178  	VolumePlugin
   179  	// GetAccessModes describes the ways a given volume can be accessed/mounted.
   180  	GetAccessModes() []v1.PersistentVolumeAccessMode
   181  }
   182  
   183  // RecyclableVolumePlugin is an extended interface of VolumePlugin and is used
   184  // by persistent volumes that want to be recycled before being made available
   185  // again to new claims
   186  type RecyclableVolumePlugin interface {
   187  	VolumePlugin
   188  
   189  	// Recycle knows how to reclaim this
   190  	// resource after the volume's release from a PersistentVolumeClaim.
   191  	// Recycle will use the provided recorder to write any events that might be
   192  	// interesting to user. It's expected that caller will pass these events to
   193  	// the PV being recycled.
   194  	Recycle(pvName string, spec *Spec, eventRecorder recyclerclient.RecycleEventRecorder) error
   195  }
   196  
   197  // DeletableVolumePlugin is an extended interface of VolumePlugin and is used
   198  // by persistent volumes that want to be deleted from the cluster after their
   199  // release from a PersistentVolumeClaim.
   200  type DeletableVolumePlugin interface {
   201  	VolumePlugin
   202  	// NewDeleter creates a new volume.Deleter which knows how to delete this
   203  	// resource in accordance with the underlying storage provider after the
   204  	// volume's release from a claim
   205  	NewDeleter(logger klog.Logger, spec *Spec) (Deleter, error)
   206  }
   207  
   208  // ProvisionableVolumePlugin is an extended interface of VolumePlugin and is
   209  // used to create volumes for the cluster.
   210  type ProvisionableVolumePlugin interface {
   211  	VolumePlugin
   212  	// NewProvisioner creates a new volume.Provisioner which knows how to
   213  	// create PersistentVolumes in accordance with the plugin's underlying
   214  	// storage provider
   215  	NewProvisioner(logger klog.Logger, options VolumeOptions) (Provisioner, error)
   216  }
   217  
   218  // AttachableVolumePlugin is an extended interface of VolumePlugin and is used for volumes that require attachment
   219  // to a node before mounting.
   220  type AttachableVolumePlugin interface {
   221  	DeviceMountableVolumePlugin
   222  	NewAttacher() (Attacher, error)
   223  	NewDetacher() (Detacher, error)
   224  	// CanAttach tests if provided volume spec is attachable
   225  	CanAttach(spec *Spec) (bool, error)
   226  }
   227  
   228  // DeviceMountableVolumePlugin is an extended interface of VolumePlugin and is used
   229  // for volumes that requires mount device to a node before binding to volume to pod.
   230  type DeviceMountableVolumePlugin interface {
   231  	VolumePlugin
   232  	NewDeviceMounter() (DeviceMounter, error)
   233  	NewDeviceUnmounter() (DeviceUnmounter, error)
   234  	GetDeviceMountRefs(deviceMountPath string) ([]string, error)
   235  	// CanDeviceMount determines if device in volume.Spec is mountable
   236  	CanDeviceMount(spec *Spec) (bool, error)
   237  }
   238  
   239  // ExpandableVolumePlugin is an extended interface of VolumePlugin and is used for volumes that can be
   240  // expanded via control-plane ExpandVolumeDevice call.
   241  type ExpandableVolumePlugin interface {
   242  	VolumePlugin
   243  	ExpandVolumeDevice(spec *Spec, newSize resource.Quantity, oldSize resource.Quantity) (resource.Quantity, error)
   244  	RequiresFSResize() bool
   245  }
   246  
   247  // NodeExpandableVolumePlugin is an expanded interface of VolumePlugin and is used for volumes that
   248  // require expansion on the node via NodeExpand call.
   249  type NodeExpandableVolumePlugin interface {
   250  	VolumePlugin
   251  	RequiresFSResize() bool
   252  	// NodeExpand expands volume on given deviceMountPath and returns true if resize is successful.
   253  	NodeExpand(resizeOptions NodeResizeOptions) (bool, error)
   254  }
   255  
   256  // BlockVolumePlugin is an extend interface of VolumePlugin and is used for block volumes support.
   257  type BlockVolumePlugin interface {
   258  	VolumePlugin
   259  	// NewBlockVolumeMapper creates a new volume.BlockVolumeMapper from an API specification.
   260  	// Ownership of the spec pointer in *not* transferred.
   261  	// - spec: The v1.Volume spec
   262  	// - pod: The enclosing pod
   263  	NewBlockVolumeMapper(spec *Spec, podRef *v1.Pod, opts VolumeOptions) (BlockVolumeMapper, error)
   264  	// NewBlockVolumeUnmapper creates a new volume.BlockVolumeUnmapper from recoverable state.
   265  	// - name: The volume name, as per the v1.Volume spec.
   266  	// - podUID: The UID of the enclosing pod
   267  	NewBlockVolumeUnmapper(name string, podUID types.UID) (BlockVolumeUnmapper, error)
   268  	// ConstructBlockVolumeSpec constructs a volume spec based on the given
   269  	// podUID, volume name and a pod device map path.
   270  	// The spec may have incomplete information due to limited information
   271  	// from input. This function is used by volume manager to reconstruct
   272  	// volume spec by reading the volume directories from disk.
   273  	ConstructBlockVolumeSpec(podUID types.UID, volumeName, volumePath string) (*Spec, error)
   274  }
   275  
   276  // TODO(#14217)
   277  // As part of the Volume Host refactor we are starting to create Volume Hosts
   278  // for specific hosts. New methods for each specific host can be added here.
   279  // Currently consumers will do type assertions to get the specific type of Volume
   280  // Host; however, the end result should be that specific Volume Hosts are passed
   281  // to the specific functions they are needed in (instead of using a catch-all
   282  // VolumeHost interface)
   283  
   284  // KubeletVolumeHost is a Kubelet specific interface that plugins can use to access the kubelet.
   285  type KubeletVolumeHost interface {
   286  	// SetKubeletError lets plugins set an error on the Kubelet runtime status
   287  	// that will cause the Kubelet to post NotReady status with the error message provided
   288  	SetKubeletError(err error)
   289  
   290  	// GetInformerFactory returns the informer factory for CSIDriverLister
   291  	GetInformerFactory() informers.SharedInformerFactory
   292  	// CSIDriverLister returns the informer lister for the CSIDriver API Object
   293  	CSIDriverLister() storagelistersv1.CSIDriverLister
   294  	// CSIDriverSynced returns the informer synced for the CSIDriver API Object
   295  	CSIDriversSynced() cache.InformerSynced
   296  	// WaitForCacheSync is a helper function that waits for cache sync for CSIDriverLister
   297  	WaitForCacheSync() error
   298  	// Returns hostutil.HostUtils
   299  	GetHostUtil() hostutil.HostUtils
   300  
   301  	// Returns trust anchors from the named ClusterTrustBundle.
   302  	GetTrustAnchorsByName(name string, allowMissing bool) ([]byte, error)
   303  
   304  	// Returns trust anchors from the ClusterTrustBundles selected by signer
   305  	// name and label selector.
   306  	GetTrustAnchorsBySigner(signerName string, labelSelector *metav1.LabelSelector, allowMissing bool) ([]byte, error)
   307  }
   308  
   309  // AttachDetachVolumeHost is a AttachDetach Controller specific interface that plugins can use
   310  // to access methods on the Attach Detach Controller.
   311  type AttachDetachVolumeHost interface {
   312  	// CSINodeLister returns the informer lister for the CSINode API Object
   313  	CSINodeLister() storagelistersv1.CSINodeLister
   314  
   315  	// CSIDriverLister returns the informer lister for the CSIDriver API Object
   316  	CSIDriverLister() storagelistersv1.CSIDriverLister
   317  
   318  	// VolumeAttachmentLister returns the informer lister for the VolumeAttachment API Object
   319  	VolumeAttachmentLister() storagelistersv1.VolumeAttachmentLister
   320  	// IsAttachDetachController is an interface marker to strictly tie AttachDetachVolumeHost
   321  	// to the attachDetachController
   322  	IsAttachDetachController() bool
   323  }
   324  
   325  // VolumeHost is an interface that plugins can use to access the kubelet.
   326  type VolumeHost interface {
   327  	// GetPluginDir returns the absolute path to a directory under which
   328  	// a given plugin may store data.  This directory might not actually
   329  	// exist on disk yet.  For plugin data that is per-pod, see
   330  	// GetPodPluginDir().
   331  	GetPluginDir(pluginName string) string
   332  
   333  	// GetVolumeDevicePluginDir returns the absolute path to a directory
   334  	// under which a given plugin may store data.
   335  	// ex. plugins/kubernetes.io/{PluginName}/{DefaultKubeletVolumeDevicesDirName}/{volumePluginDependentPath}/
   336  	GetVolumeDevicePluginDir(pluginName string) string
   337  
   338  	// GetPodsDir returns the absolute path to a directory where all the pods
   339  	// information is stored
   340  	GetPodsDir() string
   341  
   342  	// GetPodVolumeDir returns the absolute path a directory which
   343  	// represents the named volume under the named plugin for the given
   344  	// pod.  If the specified pod does not exist, the result of this call
   345  	// might not exist.
   346  	GetPodVolumeDir(podUID types.UID, pluginName string, volumeName string) string
   347  
   348  	// GetPodPluginDir returns the absolute path to a directory under which
   349  	// a given plugin may store data for a given pod.  If the specified pod
   350  	// does not exist, the result of this call might not exist.  This
   351  	// directory might not actually exist on disk yet.
   352  	GetPodPluginDir(podUID types.UID, pluginName string) string
   353  
   354  	// GetPodVolumeDeviceDir returns the absolute path a directory which
   355  	// represents the named plugin for the given pod.
   356  	// If the specified pod does not exist, the result of this call
   357  	// might not exist.
   358  	// ex. pods/{podUid}/{DefaultKubeletVolumeDevicesDirName}/{escapeQualifiedPluginName}/
   359  	GetPodVolumeDeviceDir(podUID types.UID, pluginName string) string
   360  
   361  	// GetKubeClient returns a client interface
   362  	GetKubeClient() clientset.Interface
   363  
   364  	// NewWrapperMounter finds an appropriate plugin with which to handle
   365  	// the provided spec.  This is used to implement volume plugins which
   366  	// "wrap" other plugins.  For example, the "secret" volume is
   367  	// implemented in terms of the "emptyDir" volume.
   368  	NewWrapperMounter(volName string, spec Spec, pod *v1.Pod, opts VolumeOptions) (Mounter, error)
   369  
   370  	// NewWrapperUnmounter finds an appropriate plugin with which to handle
   371  	// the provided spec.  See comments on NewWrapperMounter for more
   372  	// context.
   373  	NewWrapperUnmounter(volName string, spec Spec, podUID types.UID) (Unmounter, error)
   374  
   375  	// Get mounter interface.
   376  	GetMounter(pluginName string) mount.Interface
   377  
   378  	// Returns the hostname of the host kubelet is running on
   379  	GetHostName() string
   380  
   381  	// Returns host IP or nil in the case of error.
   382  	GetHostIP() (net.IP, error)
   383  
   384  	// Returns node allocatable.
   385  	GetNodeAllocatable() (v1.ResourceList, error)
   386  
   387  	// Returns a function that returns a secret.
   388  	GetSecretFunc() func(namespace, name string) (*v1.Secret, error)
   389  
   390  	// Returns a function that returns a configmap.
   391  	GetConfigMapFunc() func(namespace, name string) (*v1.ConfigMap, error)
   392  
   393  	GetServiceAccountTokenFunc() func(namespace, name string, tr *authenticationv1.TokenRequest) (*authenticationv1.TokenRequest, error)
   394  
   395  	DeleteServiceAccountTokenFunc() func(podUID types.UID)
   396  
   397  	// Returns an interface that should be used to execute any utilities in volume plugins
   398  	GetExec(pluginName string) exec.Interface
   399  
   400  	// Returns the labels on the node
   401  	GetNodeLabels() (map[string]string, error)
   402  
   403  	// Returns the name of the node
   404  	GetNodeName() types.NodeName
   405  
   406  	GetAttachedVolumesFromNodeStatus() (map[v1.UniqueVolumeName]string, error)
   407  
   408  	// Returns the event recorder of kubelet.
   409  	GetEventRecorder() record.EventRecorder
   410  
   411  	// Returns an interface that should be used to execute subpath operations
   412  	GetSubpather() subpath.Interface
   413  }
   414  
   415  // VolumePluginMgr tracks registered plugins.
   416  type VolumePluginMgr struct {
   417  	mutex                     sync.RWMutex
   418  	plugins                   map[string]VolumePlugin
   419  	prober                    DynamicPluginProber
   420  	probedPlugins             map[string]VolumePlugin
   421  	loggedDeprecationWarnings sets.String
   422  	Host                      VolumeHost
   423  }
   424  
   425  // Spec is an internal representation of a volume.  All API volume types translate to Spec.
   426  type Spec struct {
   427  	Volume                          *v1.Volume
   428  	PersistentVolume                *v1.PersistentVolume
   429  	ReadOnly                        bool
   430  	InlineVolumeSpecForCSIMigration bool
   431  	Migrated                        bool
   432  }
   433  
   434  // Name returns the name of either Volume or PersistentVolume, one of which must not be nil.
   435  func (spec *Spec) Name() string {
   436  	switch {
   437  	case spec.Volume != nil:
   438  		return spec.Volume.Name
   439  	case spec.PersistentVolume != nil:
   440  		return spec.PersistentVolume.Name
   441  	default:
   442  		return ""
   443  	}
   444  }
   445  
   446  // IsKubeletExpandable returns true for volume types that can be expanded only by the node
   447  // and not the controller. Currently Flex volume is the only one in this category since
   448  // it is typically not installed on the controller
   449  func (spec *Spec) IsKubeletExpandable() bool {
   450  	switch {
   451  	case spec.Volume != nil:
   452  		return spec.Volume.FlexVolume != nil
   453  	case spec.PersistentVolume != nil:
   454  		return spec.PersistentVolume.Spec.FlexVolume != nil
   455  	default:
   456  		return false
   457  	}
   458  }
   459  
   460  // KubeletExpandablePluginName creates and returns a name for the plugin
   461  // this is used in context on the controller where the plugin lookup fails
   462  // as volume expansion on controller isn't supported, but a plugin name is
   463  // required
   464  func (spec *Spec) KubeletExpandablePluginName() string {
   465  	switch {
   466  	case spec.Volume != nil && spec.Volume.FlexVolume != nil:
   467  		return spec.Volume.FlexVolume.Driver
   468  	case spec.PersistentVolume != nil && spec.PersistentVolume.Spec.FlexVolume != nil:
   469  		return spec.PersistentVolume.Spec.FlexVolume.Driver
   470  	default:
   471  		return ""
   472  	}
   473  }
   474  
   475  // VolumeConfig is how volume plugins receive configuration.  An instance
   476  // specific to the plugin will be passed to the plugin's
   477  // ProbeVolumePlugins(config) func.  Reasonable defaults will be provided by
   478  // the binary hosting the plugins while allowing override of those default
   479  // values.  Those config values are then set to an instance of VolumeConfig
   480  // and passed to the plugin.
   481  //
   482  // Values in VolumeConfig are intended to be relevant to several plugins, but
   483  // not necessarily all plugins.  The preference is to leverage strong typing
   484  // in this struct.  All config items must have a descriptive but non-specific
   485  // name (i.e, RecyclerMinimumTimeout is OK but RecyclerMinimumTimeoutForNFS is
   486  // !OK).  An instance of config will be given directly to the plugin, so
   487  // config names specific to plugins are unneeded and wrongly expose plugins in
   488  // this VolumeConfig struct.
   489  //
   490  // OtherAttributes is a map of string values intended for one-off
   491  // configuration of a plugin or config that is only relevant to a single
   492  // plugin.  All values are passed by string and require interpretation by the
   493  // plugin. Passing config as strings is the least desirable option but can be
   494  // used for truly one-off configuration. The binary should still use strong
   495  // typing for this value when binding CLI values before they are passed as
   496  // strings in OtherAttributes.
   497  type VolumeConfig struct {
   498  	// RecyclerPodTemplate is pod template that understands how to scrub clean
   499  	// a persistent volume after its release. The template is used by plugins
   500  	// which override specific properties of the pod in accordance with that
   501  	// plugin. See NewPersistentVolumeRecyclerPodTemplate for the properties
   502  	// that are expected to be overridden.
   503  	RecyclerPodTemplate *v1.Pod
   504  
   505  	// RecyclerMinimumTimeout is the minimum amount of time in seconds for the
   506  	// recycler pod's ActiveDeadlineSeconds attribute. Added to the minimum
   507  	// timeout is the increment per Gi of capacity.
   508  	RecyclerMinimumTimeout int
   509  
   510  	// RecyclerTimeoutIncrement is the number of seconds added to the recycler
   511  	// pod's ActiveDeadlineSeconds for each Gi of capacity in the persistent
   512  	// volume. Example: 5Gi volume x 30s increment = 150s + 30s minimum = 180s
   513  	// ActiveDeadlineSeconds for recycler pod
   514  	RecyclerTimeoutIncrement int
   515  
   516  	// PVName is name of the PersistentVolume instance that is being recycled.
   517  	// It is used to generate unique recycler pod name.
   518  	PVName string
   519  
   520  	// OtherAttributes stores config as strings.  These strings are opaque to
   521  	// the system and only understood by the binary hosting the plugin and the
   522  	// plugin itself.
   523  	OtherAttributes map[string]string
   524  
   525  	// ProvisioningEnabled configures whether provisioning of this plugin is
   526  	// enabled or not. Currently used only in host_path plugin.
   527  	ProvisioningEnabled bool
   528  }
   529  
   530  // ReconstructedVolume contains information about a volume reconstructed by
   531  // ConstructVolumeSpec().
   532  type ReconstructedVolume struct {
   533  	// Spec is the volume spec of a mounted volume
   534  	Spec *Spec
   535  	// SELinuxMountContext is value of -o context=XYZ mount option.
   536  	// If empty, no such mount option is used.
   537  	SELinuxMountContext string
   538  }
   539  
   540  // NewSpecFromVolume creates an Spec from an v1.Volume
   541  func NewSpecFromVolume(vs *v1.Volume) *Spec {
   542  	return &Spec{
   543  		Volume: vs,
   544  	}
   545  }
   546  
   547  // NewSpecFromPersistentVolume creates an Spec from an v1.PersistentVolume
   548  func NewSpecFromPersistentVolume(pv *v1.PersistentVolume, readOnly bool) *Spec {
   549  	return &Spec{
   550  		PersistentVolume: pv,
   551  		ReadOnly:         readOnly,
   552  	}
   553  }
   554  
   555  // InitPlugins initializes each plugin.  All plugins must have unique names.
   556  // This must be called exactly once before any New* methods are called on any
   557  // plugins.
   558  func (pm *VolumePluginMgr) InitPlugins(plugins []VolumePlugin, prober DynamicPluginProber, host VolumeHost) error {
   559  	pm.mutex.Lock()
   560  	defer pm.mutex.Unlock()
   561  
   562  	pm.Host = host
   563  	pm.loggedDeprecationWarnings = sets.NewString()
   564  
   565  	if prober == nil {
   566  		// Use a dummy prober to prevent nil deference.
   567  		pm.prober = &dummyPluginProber{}
   568  	} else {
   569  		pm.prober = prober
   570  	}
   571  	if err := pm.prober.Init(); err != nil {
   572  		// Prober init failure should not affect the initialization of other plugins.
   573  		klog.ErrorS(err, "Error initializing dynamic plugin prober")
   574  		pm.prober = &dummyPluginProber{}
   575  	}
   576  
   577  	if pm.plugins == nil {
   578  		pm.plugins = map[string]VolumePlugin{}
   579  	}
   580  	if pm.probedPlugins == nil {
   581  		pm.probedPlugins = map[string]VolumePlugin{}
   582  	}
   583  
   584  	allErrs := []error{}
   585  	for _, plugin := range plugins {
   586  		name := plugin.GetPluginName()
   587  		if errs := validation.IsQualifiedName(name); len(errs) != 0 {
   588  			allErrs = append(allErrs, fmt.Errorf("volume plugin has invalid name: %q: %s", name, strings.Join(errs, ";")))
   589  			continue
   590  		}
   591  
   592  		if _, found := pm.plugins[name]; found {
   593  			allErrs = append(allErrs, fmt.Errorf("volume plugin %q was registered more than once", name))
   594  			continue
   595  		}
   596  		err := plugin.Init(host)
   597  		if err != nil {
   598  			klog.ErrorS(err, "Failed to load volume plugin", "pluginName", name)
   599  			allErrs = append(allErrs, err)
   600  			continue
   601  		}
   602  		pm.plugins[name] = plugin
   603  		klog.V(1).InfoS("Loaded volume plugin", "pluginName", name)
   604  	}
   605  	return utilerrors.NewAggregate(allErrs)
   606  }
   607  
   608  func (pm *VolumePluginMgr) initProbedPlugin(probedPlugin VolumePlugin) error {
   609  	name := probedPlugin.GetPluginName()
   610  	if errs := validation.IsQualifiedName(name); len(errs) != 0 {
   611  		return fmt.Errorf("volume plugin has invalid name: %q: %s", name, strings.Join(errs, ";"))
   612  	}
   613  
   614  	err := probedPlugin.Init(pm.Host)
   615  	if err != nil {
   616  		return fmt.Errorf("failed to load volume plugin %s, error: %s", name, err.Error())
   617  	}
   618  
   619  	klog.V(1).InfoS("Loaded volume plugin", "pluginName", name)
   620  	return nil
   621  }
   622  
   623  // FindPluginBySpec looks for a plugin that can support a given volume
   624  // specification.  If no plugins can support or more than one plugin can
   625  // support it, return error.
   626  func (pm *VolumePluginMgr) FindPluginBySpec(spec *Spec) (VolumePlugin, error) {
   627  	pm.mutex.RLock()
   628  	defer pm.mutex.RUnlock()
   629  
   630  	if spec == nil {
   631  		return nil, fmt.Errorf("could not find plugin because volume spec is nil")
   632  	}
   633  
   634  	var match VolumePlugin
   635  	matchedPluginNames := []string{}
   636  	for _, v := range pm.plugins {
   637  		if v.CanSupport(spec) {
   638  			match = v
   639  			matchedPluginNames = append(matchedPluginNames, v.GetPluginName())
   640  		}
   641  	}
   642  
   643  	pm.refreshProbedPlugins()
   644  	for _, plugin := range pm.probedPlugins {
   645  		if plugin.CanSupport(spec) {
   646  			match = plugin
   647  			matchedPluginNames = append(matchedPluginNames, plugin.GetPluginName())
   648  		}
   649  	}
   650  
   651  	if len(matchedPluginNames) == 0 {
   652  		return nil, fmt.Errorf("no volume plugin matched")
   653  	}
   654  	if len(matchedPluginNames) > 1 {
   655  		return nil, fmt.Errorf("multiple volume plugins matched: %s", strings.Join(matchedPluginNames, ","))
   656  	}
   657  
   658  	return match, nil
   659  }
   660  
   661  // FindPluginByName fetches a plugin by name. If no plugin is found, returns error.
   662  func (pm *VolumePluginMgr) FindPluginByName(name string) (VolumePlugin, error) {
   663  	pm.mutex.RLock()
   664  	defer pm.mutex.RUnlock()
   665  
   666  	var match VolumePlugin
   667  	if v, found := pm.plugins[name]; found {
   668  		match = v
   669  	}
   670  
   671  	pm.refreshProbedPlugins()
   672  	if plugin, found := pm.probedPlugins[name]; found {
   673  		if match != nil {
   674  			return nil, fmt.Errorf("multiple volume plugins matched: %s and %s", match.GetPluginName(), plugin.GetPluginName())
   675  		}
   676  		match = plugin
   677  	}
   678  
   679  	if match == nil {
   680  		return nil, fmt.Errorf("no volume plugin matched name: %s", name)
   681  	}
   682  	return match, nil
   683  }
   684  
   685  // Check if probedPlugin cache update is required.
   686  // If it is, initialize all probed plugins and replace the cache with them.
   687  func (pm *VolumePluginMgr) refreshProbedPlugins() {
   688  	events, err := pm.prober.Probe()
   689  
   690  	if err != nil {
   691  		klog.ErrorS(err, "Error dynamically probing plugins")
   692  	}
   693  
   694  	// because the probe function can return a list of valid plugins
   695  	// even when an error is present we still must add the plugins
   696  	// or they will be skipped because each event only fires once
   697  	for _, event := range events {
   698  		if event.Op == ProbeAddOrUpdate {
   699  			if err := pm.initProbedPlugin(event.Plugin); err != nil {
   700  				klog.ErrorS(err, "Error initializing dynamically probed plugin",
   701  					"pluginName", event.Plugin.GetPluginName())
   702  				continue
   703  			}
   704  			pm.probedPlugins[event.Plugin.GetPluginName()] = event.Plugin
   705  		} else if event.Op == ProbeRemove {
   706  			// Plugin is not available on ProbeRemove event, only PluginName
   707  			delete(pm.probedPlugins, event.PluginName)
   708  		} else {
   709  			klog.ErrorS(nil, "Unknown Operation on PluginName.",
   710  				"pluginName", event.Plugin.GetPluginName())
   711  		}
   712  	}
   713  }
   714  
   715  // FindPersistentPluginBySpec looks for a persistent volume plugin that can
   716  // support a given volume specification.  If no plugin is found, return an
   717  // error
   718  func (pm *VolumePluginMgr) FindPersistentPluginBySpec(spec *Spec) (PersistentVolumePlugin, error) {
   719  	volumePlugin, err := pm.FindPluginBySpec(spec)
   720  	if err != nil {
   721  		return nil, fmt.Errorf("could not find volume plugin for spec: %#v", spec)
   722  	}
   723  	if persistentVolumePlugin, ok := volumePlugin.(PersistentVolumePlugin); ok {
   724  		return persistentVolumePlugin, nil
   725  	}
   726  	return nil, fmt.Errorf("no persistent volume plugin matched")
   727  }
   728  
   729  // FindPersistentPluginByName fetches a persistent volume plugin by name.  If
   730  // no plugin is found, returns error.
   731  func (pm *VolumePluginMgr) FindPersistentPluginByName(name string) (PersistentVolumePlugin, error) {
   732  	volumePlugin, err := pm.FindPluginByName(name)
   733  	if err != nil {
   734  		return nil, err
   735  	}
   736  	if persistentVolumePlugin, ok := volumePlugin.(PersistentVolumePlugin); ok {
   737  		return persistentVolumePlugin, nil
   738  	}
   739  	return nil, fmt.Errorf("no persistent volume plugin matched")
   740  }
   741  
   742  // FindRecyclablePluginByName fetches a persistent volume plugin by name.  If
   743  // no plugin is found, returns error.
   744  func (pm *VolumePluginMgr) FindRecyclablePluginBySpec(spec *Spec) (RecyclableVolumePlugin, error) {
   745  	volumePlugin, err := pm.FindPluginBySpec(spec)
   746  	if err != nil {
   747  		return nil, err
   748  	}
   749  	if recyclableVolumePlugin, ok := volumePlugin.(RecyclableVolumePlugin); ok {
   750  		return recyclableVolumePlugin, nil
   751  	}
   752  	return nil, fmt.Errorf("no recyclable volume plugin matched")
   753  }
   754  
   755  // FindProvisionablePluginByName fetches  a persistent volume plugin by name.  If
   756  // no plugin is found, returns error.
   757  func (pm *VolumePluginMgr) FindProvisionablePluginByName(name string) (ProvisionableVolumePlugin, error) {
   758  	volumePlugin, err := pm.FindPluginByName(name)
   759  	if err != nil {
   760  		return nil, err
   761  	}
   762  	if provisionableVolumePlugin, ok := volumePlugin.(ProvisionableVolumePlugin); ok {
   763  		return provisionableVolumePlugin, nil
   764  	}
   765  	return nil, fmt.Errorf("no provisionable volume plugin matched")
   766  }
   767  
   768  // FindDeletablePluginBySpec fetches a persistent volume plugin by spec.  If
   769  // no plugin is found, returns error.
   770  func (pm *VolumePluginMgr) FindDeletablePluginBySpec(spec *Spec) (DeletableVolumePlugin, error) {
   771  	volumePlugin, err := pm.FindPluginBySpec(spec)
   772  	if err != nil {
   773  		return nil, err
   774  	}
   775  	if deletableVolumePlugin, ok := volumePlugin.(DeletableVolumePlugin); ok {
   776  		return deletableVolumePlugin, nil
   777  	}
   778  	return nil, fmt.Errorf("no deletable volume plugin matched")
   779  }
   780  
   781  // FindDeletablePluginByName fetches a persistent volume plugin by name.  If
   782  // no plugin is found, returns error.
   783  func (pm *VolumePluginMgr) FindDeletablePluginByName(name string) (DeletableVolumePlugin, error) {
   784  	volumePlugin, err := pm.FindPluginByName(name)
   785  	if err != nil {
   786  		return nil, err
   787  	}
   788  	if deletableVolumePlugin, ok := volumePlugin.(DeletableVolumePlugin); ok {
   789  		return deletableVolumePlugin, nil
   790  	}
   791  	return nil, fmt.Errorf("no deletable volume plugin matched")
   792  }
   793  
   794  // FindCreatablePluginBySpec fetches a persistent volume plugin by name.  If
   795  // no plugin is found, returns error.
   796  func (pm *VolumePluginMgr) FindCreatablePluginBySpec(spec *Spec) (ProvisionableVolumePlugin, error) {
   797  	volumePlugin, err := pm.FindPluginBySpec(spec)
   798  	if err != nil {
   799  		return nil, err
   800  	}
   801  	if provisionableVolumePlugin, ok := volumePlugin.(ProvisionableVolumePlugin); ok {
   802  		return provisionableVolumePlugin, nil
   803  	}
   804  	return nil, fmt.Errorf("no creatable volume plugin matched")
   805  }
   806  
   807  // FindAttachablePluginBySpec fetches a persistent volume plugin by spec.
   808  // Unlike the other "FindPlugin" methods, this does not return error if no
   809  // plugin is found.  All volumes require a mounter and unmounter, but not
   810  // every volume will have an attacher/detacher.
   811  func (pm *VolumePluginMgr) FindAttachablePluginBySpec(spec *Spec) (AttachableVolumePlugin, error) {
   812  	volumePlugin, err := pm.FindPluginBySpec(spec)
   813  	if err != nil {
   814  		return nil, err
   815  	}
   816  	if attachableVolumePlugin, ok := volumePlugin.(AttachableVolumePlugin); ok {
   817  		if canAttach, err := attachableVolumePlugin.CanAttach(spec); err != nil {
   818  			return nil, err
   819  		} else if canAttach {
   820  			return attachableVolumePlugin, nil
   821  		}
   822  	}
   823  	return nil, nil
   824  }
   825  
   826  // FindAttachablePluginByName fetches an attachable volume plugin by name.
   827  // Unlike the other "FindPlugin" methods, this does not return error if no
   828  // plugin is found.  All volumes require a mounter and unmounter, but not
   829  // every volume will have an attacher/detacher.
   830  func (pm *VolumePluginMgr) FindAttachablePluginByName(name string) (AttachableVolumePlugin, error) {
   831  	volumePlugin, err := pm.FindPluginByName(name)
   832  	if err != nil {
   833  		return nil, err
   834  	}
   835  	if attachablePlugin, ok := volumePlugin.(AttachableVolumePlugin); ok {
   836  		return attachablePlugin, nil
   837  	}
   838  	return nil, nil
   839  }
   840  
   841  // FindDeviceMountablePluginBySpec fetches a persistent volume plugin by spec.
   842  func (pm *VolumePluginMgr) FindDeviceMountablePluginBySpec(spec *Spec) (DeviceMountableVolumePlugin, error) {
   843  	volumePlugin, err := pm.FindPluginBySpec(spec)
   844  	if err != nil {
   845  		return nil, err
   846  	}
   847  	if deviceMountableVolumePlugin, ok := volumePlugin.(DeviceMountableVolumePlugin); ok {
   848  		if canMount, err := deviceMountableVolumePlugin.CanDeviceMount(spec); err != nil {
   849  			return nil, err
   850  		} else if canMount {
   851  			return deviceMountableVolumePlugin, nil
   852  		}
   853  	}
   854  	return nil, nil
   855  }
   856  
   857  // FindDeviceMountablePluginByName fetches a devicemountable volume plugin by name.
   858  func (pm *VolumePluginMgr) FindDeviceMountablePluginByName(name string) (DeviceMountableVolumePlugin, error) {
   859  	volumePlugin, err := pm.FindPluginByName(name)
   860  	if err != nil {
   861  		return nil, err
   862  	}
   863  	if deviceMountableVolumePlugin, ok := volumePlugin.(DeviceMountableVolumePlugin); ok {
   864  		return deviceMountableVolumePlugin, nil
   865  	}
   866  	return nil, nil
   867  }
   868  
   869  // FindExpandablePluginBySpec fetches a persistent volume plugin by spec.
   870  func (pm *VolumePluginMgr) FindExpandablePluginBySpec(spec *Spec) (ExpandableVolumePlugin, error) {
   871  	volumePlugin, err := pm.FindPluginBySpec(spec)
   872  	if err != nil {
   873  		if spec.IsKubeletExpandable() {
   874  			// for kubelet expandable volumes, return a noop plugin that
   875  			// returns success for expand on the controller
   876  			klog.V(4).InfoS("FindExpandablePluginBySpec -> returning noopExpandableVolumePluginInstance", "specName", spec.Name())
   877  			return &noopExpandableVolumePluginInstance{spec}, nil
   878  		}
   879  		klog.V(4).InfoS("FindExpandablePluginBySpec -> err", "specName", spec.Name(), "err", err)
   880  		return nil, err
   881  	}
   882  
   883  	if expandableVolumePlugin, ok := volumePlugin.(ExpandableVolumePlugin); ok {
   884  		return expandableVolumePlugin, nil
   885  	}
   886  	return nil, nil
   887  }
   888  
   889  // FindExpandablePluginBySpec fetches a persistent volume plugin by name.
   890  func (pm *VolumePluginMgr) FindExpandablePluginByName(name string) (ExpandableVolumePlugin, error) {
   891  	volumePlugin, err := pm.FindPluginByName(name)
   892  	if err != nil {
   893  		return nil, err
   894  	}
   895  
   896  	if expandableVolumePlugin, ok := volumePlugin.(ExpandableVolumePlugin); ok {
   897  		return expandableVolumePlugin, nil
   898  	}
   899  	return nil, nil
   900  }
   901  
   902  // FindMapperPluginBySpec fetches a block volume plugin by spec.
   903  func (pm *VolumePluginMgr) FindMapperPluginBySpec(spec *Spec) (BlockVolumePlugin, error) {
   904  	volumePlugin, err := pm.FindPluginBySpec(spec)
   905  	if err != nil {
   906  		return nil, err
   907  	}
   908  
   909  	if blockVolumePlugin, ok := volumePlugin.(BlockVolumePlugin); ok {
   910  		return blockVolumePlugin, nil
   911  	}
   912  	return nil, nil
   913  }
   914  
   915  // FindMapperPluginByName fetches a block volume plugin by name.
   916  func (pm *VolumePluginMgr) FindMapperPluginByName(name string) (BlockVolumePlugin, error) {
   917  	volumePlugin, err := pm.FindPluginByName(name)
   918  	if err != nil {
   919  		return nil, err
   920  	}
   921  
   922  	if blockVolumePlugin, ok := volumePlugin.(BlockVolumePlugin); ok {
   923  		return blockVolumePlugin, nil
   924  	}
   925  	return nil, nil
   926  }
   927  
   928  // FindNodeExpandablePluginBySpec fetches a persistent volume plugin by spec
   929  func (pm *VolumePluginMgr) FindNodeExpandablePluginBySpec(spec *Spec) (NodeExpandableVolumePlugin, error) {
   930  	volumePlugin, err := pm.FindPluginBySpec(spec)
   931  	if err != nil {
   932  		return nil, err
   933  	}
   934  	if fsResizablePlugin, ok := volumePlugin.(NodeExpandableVolumePlugin); ok {
   935  		return fsResizablePlugin, nil
   936  	}
   937  	return nil, nil
   938  }
   939  
   940  // FindNodeExpandablePluginByName fetches a persistent volume plugin by name
   941  func (pm *VolumePluginMgr) FindNodeExpandablePluginByName(name string) (NodeExpandableVolumePlugin, error) {
   942  	volumePlugin, err := pm.FindPluginByName(name)
   943  	if err != nil {
   944  		return nil, err
   945  	}
   946  
   947  	if fsResizablePlugin, ok := volumePlugin.(NodeExpandableVolumePlugin); ok {
   948  		return fsResizablePlugin, nil
   949  	}
   950  
   951  	return nil, nil
   952  }
   953  
   954  func (pm *VolumePluginMgr) Run(stopCh <-chan struct{}) {
   955  	kletHost, ok := pm.Host.(KubeletVolumeHost)
   956  	if ok {
   957  		// start informer for CSIDriver
   958  		informerFactory := kletHost.GetInformerFactory()
   959  		informerFactory.Start(stopCh)
   960  		informerFactory.WaitForCacheSync(stopCh)
   961  	}
   962  }
   963  
   964  // NewPersistentVolumeRecyclerPodTemplate creates a template for a recycler
   965  // pod.  By default, a recycler pod simply runs "rm -rf" on a volume and tests
   966  // for emptiness.  Most attributes of the template will be correct for most
   967  // plugin implementations.  The following attributes can be overridden per
   968  // plugin via configuration:
   969  //
   970  //  1. pod.Spec.Volumes[0].VolumeSource must be overridden.  Recycler
   971  //     implementations without a valid VolumeSource will fail.
   972  //  2. pod.GenerateName helps distinguish recycler pods by name.  Recommended.
   973  //     Default is "pv-recycler-".
   974  //  3. pod.Spec.ActiveDeadlineSeconds gives the recycler pod a maximum timeout
   975  //     before failing.  Recommended.  Default is 60 seconds.
   976  //
   977  // See HostPath and NFS for working recycler examples
   978  func NewPersistentVolumeRecyclerPodTemplate() *v1.Pod {
   979  	timeout := int64(60)
   980  	pod := &v1.Pod{
   981  		ObjectMeta: metav1.ObjectMeta{
   982  			GenerateName: "pv-recycler-",
   983  			Namespace:    metav1.NamespaceDefault,
   984  		},
   985  		Spec: v1.PodSpec{
   986  			ActiveDeadlineSeconds: &timeout,
   987  			RestartPolicy:         v1.RestartPolicyNever,
   988  			Volumes: []v1.Volume{
   989  				{
   990  					Name: "vol",
   991  					// IMPORTANT!  All plugins using this template MUST
   992  					// override pod.Spec.Volumes[0].VolumeSource Recycler
   993  					// implementations without a valid VolumeSource will fail.
   994  					VolumeSource: v1.VolumeSource{},
   995  				},
   996  			},
   997  			Containers: []v1.Container{
   998  				{
   999  					Name:    "pv-recycler",
  1000  					Image:   "registry.k8s.io/build-image/debian-base:bookworm-v1.0.2",
  1001  					Command: []string{"/bin/sh"},
  1002  					Args:    []string{"-c", "test -e /scrub && find /scrub -mindepth 1 -delete && test -z \"$(ls -A /scrub)\" || exit 1"},
  1003  					VolumeMounts: []v1.VolumeMount{
  1004  						{
  1005  							Name:      "vol",
  1006  							MountPath: "/scrub",
  1007  						},
  1008  					},
  1009  				},
  1010  			},
  1011  		},
  1012  	}
  1013  	return pod
  1014  }
  1015  
  1016  // Check validity of recycle pod template
  1017  // List of checks:
  1018  // - at least one volume is defined in the recycle pod template
  1019  // If successful, returns nil
  1020  // if unsuccessful, returns an error.
  1021  func ValidateRecyclerPodTemplate(pod *v1.Pod) error {
  1022  	if len(pod.Spec.Volumes) < 1 {
  1023  		return fmt.Errorf("does not contain any volume(s)")
  1024  	}
  1025  	return nil
  1026  }
  1027  
  1028  type dummyPluginProber struct{}
  1029  
  1030  func (*dummyPluginProber) Init() error                  { return nil }
  1031  func (*dummyPluginProber) Probe() ([]ProbeEvent, error) { return nil, nil }