plugins.go 41 KB

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