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