...

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

Documentation: k8s.io/kubernetes/pkg/volume

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

View as plain text