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