plugins.go 41 KB

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