k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/pkg/volume/csi/csi_plugin.go (about) 1 /* 2 Copyright 2017 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 csi 18 19 import ( 20 "context" 21 "errors" 22 "fmt" 23 "os" 24 "path/filepath" 25 "strings" 26 "time" 27 28 "k8s.io/klog/v2" 29 30 authenticationv1 "k8s.io/api/authentication/v1" 31 api "k8s.io/api/core/v1" 32 storage "k8s.io/api/storage/v1" 33 apierrors "k8s.io/apimachinery/pkg/api/errors" 34 meta "k8s.io/apimachinery/pkg/apis/meta/v1" 35 "k8s.io/apimachinery/pkg/types" 36 utilruntime "k8s.io/apimachinery/pkg/util/runtime" 37 utilversion "k8s.io/apimachinery/pkg/util/version" 38 "k8s.io/apimachinery/pkg/util/wait" 39 utilfeature "k8s.io/apiserver/pkg/util/feature" 40 clientset "k8s.io/client-go/kubernetes" 41 storagelisters "k8s.io/client-go/listers/storage/v1" 42 csitranslationplugins "k8s.io/csi-translation-lib/plugins" 43 "k8s.io/kubernetes/pkg/features" 44 "k8s.io/kubernetes/pkg/kubelet/util" 45 "k8s.io/kubernetes/pkg/volume" 46 "k8s.io/kubernetes/pkg/volume/csi/nodeinfomanager" 47 volumetypes "k8s.io/kubernetes/pkg/volume/util/types" 48 ) 49 50 const ( 51 // CSIPluginName is the name of the in-tree CSI Plugin 52 CSIPluginName = "kubernetes.io/csi" 53 54 csiTimeout = 2 * time.Minute 55 volNameSep = "^" 56 volDataFileName = "vol_data.json" 57 fsTypeBlockName = "block" 58 59 // CsiResyncPeriod is default resync period duration 60 // TODO: increase to something useful 61 CsiResyncPeriod = time.Minute 62 ) 63 64 type csiPlugin struct { 65 host volume.VolumeHost 66 csiDriverLister storagelisters.CSIDriverLister 67 serviceAccountTokenGetter func(namespace, name string, tr *authenticationv1.TokenRequest) (*authenticationv1.TokenRequest, error) 68 volumeAttachmentLister storagelisters.VolumeAttachmentLister 69 } 70 71 // ProbeVolumePlugins returns implemented plugins 72 func ProbeVolumePlugins() []volume.VolumePlugin { 73 p := &csiPlugin{ 74 host: nil, 75 } 76 return []volume.VolumePlugin{p} 77 } 78 79 // volume.VolumePlugin methods 80 var _ volume.VolumePlugin = &csiPlugin{} 81 82 // RegistrationHandler is the handler which is fed to the pluginwatcher API. 83 type RegistrationHandler struct { 84 } 85 86 // TODO (verult) consider using a struct instead of global variables 87 // csiDrivers map keep track of all registered CSI drivers on the node and their 88 // corresponding sockets 89 var csiDrivers = &DriversStore{} 90 91 var nim nodeinfomanager.Interface 92 93 // PluginHandler is the plugin registration handler interface passed to the 94 // pluginwatcher module in kubelet 95 var PluginHandler = &RegistrationHandler{} 96 97 // ValidatePlugin is called by kubelet's plugin watcher upon detection 98 // of a new registration socket opened by CSI Driver registrar side car. 99 func (h *RegistrationHandler) ValidatePlugin(pluginName string, endpoint string, versions []string) error { 100 klog.Infof(log("Trying to validate a new CSI Driver with name: %s endpoint: %s versions: %s", 101 pluginName, endpoint, strings.Join(versions, ","))) 102 103 _, err := h.validateVersions("ValidatePlugin", pluginName, endpoint, versions) 104 if err != nil { 105 return fmt.Errorf("validation failed for CSI Driver %s at endpoint %s: %v", pluginName, endpoint, err) 106 } 107 108 return err 109 } 110 111 // RegisterPlugin is called when a plugin can be registered 112 func (h *RegistrationHandler) RegisterPlugin(pluginName string, endpoint string, versions []string, pluginClientTimeout *time.Duration) error { 113 klog.Infof(log("Register new plugin with name: %s at endpoint: %s", pluginName, endpoint)) 114 115 highestSupportedVersion, err := h.validateVersions("RegisterPlugin", pluginName, endpoint, versions) 116 if err != nil { 117 return err 118 } 119 120 // Storing endpoint of newly registered CSI driver into the map, where CSI driver name will be the key 121 // all other CSI components will be able to get the actual socket of CSI drivers by its name. 122 csiDrivers.Set(pluginName, Driver{ 123 endpoint: endpoint, 124 highestSupportedVersion: highestSupportedVersion, 125 }) 126 127 // Get node info from the driver. 128 csi, err := newCsiDriverClient(csiDriverName(pluginName)) 129 if err != nil { 130 return err 131 } 132 133 var timeout time.Duration 134 if pluginClientTimeout == nil { 135 timeout = csiTimeout 136 } else { 137 timeout = *pluginClientTimeout 138 } 139 140 ctx, cancel := context.WithTimeout(context.Background(), timeout) 141 defer cancel() 142 143 driverNodeID, maxVolumePerNode, accessibleTopology, err := csi.NodeGetInfo(ctx) 144 if err != nil { 145 if unregErr := unregisterDriver(pluginName); unregErr != nil { 146 klog.Error(log("registrationHandler.RegisterPlugin failed to unregister plugin due to previous error: %v", unregErr)) 147 } 148 return err 149 } 150 151 err = nim.InstallCSIDriver(pluginName, driverNodeID, maxVolumePerNode, accessibleTopology) 152 if err != nil { 153 if unregErr := unregisterDriver(pluginName); unregErr != nil { 154 klog.Error(log("registrationHandler.RegisterPlugin failed to unregister plugin due to previous error: %v", unregErr)) 155 } 156 return err 157 } 158 159 return nil 160 } 161 162 func (h *RegistrationHandler) validateVersions(callerName, pluginName string, endpoint string, versions []string) (*utilversion.Version, error) { 163 if len(versions) == 0 { 164 return nil, errors.New(log("%s for CSI driver %q failed. Plugin returned an empty list for supported versions", callerName, pluginName)) 165 } 166 167 // Validate version 168 // CSI currently only has version 0.x and 1.x (see https://github.com/container-storage-interface/spec/releases). 169 // Therefore any driver claiming version 2.x+ is ignored as an unsupported versions. 170 // Future 1.x versions of CSI are supposed to be backwards compatible so this version of Kubernetes will work with any 1.x driver 171 // (or 0.x), but it may not work with 2.x drivers (because 2.x does not have to be backwards compatible with 1.x). 172 // CSI v0.x is no longer supported as of Kubernetes v1.17 in accordance with deprecation policy set out in Kubernetes v1.13. 173 newDriverHighestVersion, err := utilversion.HighestSupportedVersion(versions) 174 if err != nil { 175 return nil, errors.New(log("%s for CSI driver %q failed. None of the versions specified %q are supported. err=%v", callerName, pluginName, versions, err)) 176 } 177 178 existingDriver, driverExists := csiDrivers.Get(pluginName) 179 if driverExists { 180 if !existingDriver.highestSupportedVersion.LessThan(newDriverHighestVersion) { 181 return nil, errors.New(log("%s for CSI driver %q failed. Another driver with the same name is already registered with a higher supported version: %q", callerName, pluginName, existingDriver.highestSupportedVersion)) 182 } 183 } 184 185 return newDriverHighestVersion, nil 186 } 187 188 // DeRegisterPlugin is called when a plugin removed its socket, signaling 189 // it is no longer available 190 func (h *RegistrationHandler) DeRegisterPlugin(pluginName string) { 191 klog.Info(log("registrationHandler.DeRegisterPlugin request for plugin %s", pluginName)) 192 if err := unregisterDriver(pluginName); err != nil { 193 klog.Error(log("registrationHandler.DeRegisterPlugin failed: %v", err)) 194 } 195 } 196 197 func (p *csiPlugin) Init(host volume.VolumeHost) error { 198 p.host = host 199 200 csiClient := host.GetKubeClient() 201 if csiClient == nil { 202 klog.Warning(log("kubeclient not set, assuming standalone kubelet")) 203 } else { 204 // set CSIDriverLister and volumeAttachmentLister 205 adcHost, ok := host.(volume.AttachDetachVolumeHost) 206 if ok { 207 p.csiDriverLister = adcHost.CSIDriverLister() 208 if p.csiDriverLister == nil { 209 klog.Error(log("CSIDriverLister not found on AttachDetachVolumeHost")) 210 } 211 p.volumeAttachmentLister = adcHost.VolumeAttachmentLister() 212 if p.volumeAttachmentLister == nil { 213 klog.Error(log("VolumeAttachmentLister not found on AttachDetachVolumeHost")) 214 } 215 } 216 kletHost, ok := host.(volume.KubeletVolumeHost) 217 if ok { 218 p.csiDriverLister = kletHost.CSIDriverLister() 219 if p.csiDriverLister == nil { 220 klog.Error(log("CSIDriverLister not found on KubeletVolumeHost")) 221 } 222 p.serviceAccountTokenGetter = host.GetServiceAccountTokenFunc() 223 if p.serviceAccountTokenGetter == nil { 224 klog.Error(log("ServiceAccountTokenGetter not found on KubeletVolumeHost")) 225 } 226 // We don't run the volumeAttachmentLister in the kubelet context 227 p.volumeAttachmentLister = nil 228 } 229 } 230 231 var migratedPlugins = map[string](func() bool){ 232 csitranslationplugins.GCEPDInTreePluginName: func() bool { 233 return true 234 }, 235 csitranslationplugins.AWSEBSInTreePluginName: func() bool { 236 return true 237 }, 238 csitranslationplugins.CinderInTreePluginName: func() bool { 239 return true 240 }, 241 csitranslationplugins.AzureDiskInTreePluginName: func() bool { 242 return true 243 }, 244 csitranslationplugins.AzureFileInTreePluginName: func() bool { 245 return true 246 }, 247 csitranslationplugins.VSphereInTreePluginName: func() bool { 248 return true 249 }, 250 csitranslationplugins.PortworxVolumePluginName: func() bool { 251 return utilfeature.DefaultFeatureGate.Enabled(features.CSIMigrationPortworx) 252 }, 253 } 254 255 // Initializing the label management channels 256 nim = nodeinfomanager.NewNodeInfoManager(host.GetNodeName(), host, migratedPlugins) 257 258 // This function prevents Kubelet from posting Ready status until CSINode 259 // is both installed and initialized 260 if err := initializeCSINode(host); err != nil { 261 return errors.New(log("failed to initialize CSINode: %v", err)) 262 } 263 264 return nil 265 } 266 267 func initializeCSINode(host volume.VolumeHost) error { 268 kvh, ok := host.(volume.KubeletVolumeHost) 269 if !ok { 270 klog.V(4).Info("Cast from VolumeHost to KubeletVolumeHost failed. Skipping CSINode initialization, not running on kubelet") 271 return nil 272 } 273 kubeClient := host.GetKubeClient() 274 if kubeClient == nil { 275 // Kubelet running in standalone mode. Skip CSINode initialization 276 klog.Warning("Skipping CSINode initialization, kubelet running in standalone mode") 277 return nil 278 } 279 280 kvh.SetKubeletError(errors.New("CSINode is not yet initialized")) 281 282 go func() { 283 defer utilruntime.HandleCrash() 284 285 // First wait indefinitely to talk to Kube APIServer 286 nodeName := host.GetNodeName() 287 err := waitForAPIServerForever(kubeClient, nodeName) 288 if err != nil { 289 klog.Fatalf("Failed to initialize CSINode while waiting for API server to report ok: %v", err) 290 } 291 292 // Backoff parameters tuned to retry over 140 seconds. Will fail and restart the Kubelet 293 // after max retry steps. 294 initBackoff := wait.Backoff{ 295 Steps: 6, 296 Duration: 15 * time.Millisecond, 297 Factor: 6.0, 298 Jitter: 0.1, 299 } 300 err = wait.ExponentialBackoff(initBackoff, func() (bool, error) { 301 klog.V(4).Infof("Initializing migrated drivers on CSINode") 302 err := nim.InitializeCSINodeWithAnnotation() 303 if err != nil { 304 kvh.SetKubeletError(fmt.Errorf("failed to initialize CSINode: %v", err)) 305 klog.Errorf("Failed to initialize CSINode: %v", err) 306 return false, nil 307 } 308 309 // Successfully initialized drivers, allow Kubelet to post Ready 310 kvh.SetKubeletError(nil) 311 return true, nil 312 }) 313 if err != nil { 314 // 2 releases after CSIMigration and all CSIMigrationX (where X is a volume plugin) 315 // are permanently enabled the apiserver/controllers can assume that the kubelet is 316 // using CSI for all Migrated volume plugins. Then all the CSINode initialization 317 // code can be dropped from Kubelet. 318 // Kill the Kubelet process and allow it to restart to retry initialization 319 klog.Fatalf("Failed to initialize CSINode after retrying: %v", err) 320 } 321 }() 322 return nil 323 } 324 325 func (p *csiPlugin) GetPluginName() string { 326 return CSIPluginName 327 } 328 329 // GetvolumeName returns a concatenated string of CSIVolumeSource.Driver<volNameSe>CSIVolumeSource.VolumeHandle 330 // That string value is used in Detach() to extract driver name and volumeName. 331 func (p *csiPlugin) GetVolumeName(spec *volume.Spec) (string, error) { 332 csi, err := getPVSourceFromSpec(spec) 333 if err != nil { 334 return "", errors.New(log("plugin.GetVolumeName failed to extract volume source from spec: %v", err)) 335 } 336 337 // return driverName<separator>volumeHandle 338 return fmt.Sprintf("%s%s%s", csi.Driver, volNameSep, csi.VolumeHandle), nil 339 } 340 341 func (p *csiPlugin) CanSupport(spec *volume.Spec) bool { 342 // TODO (vladimirvivien) CanSupport should also take into account 343 // the availability/registration of specified Driver in the volume source 344 if spec == nil { 345 return false 346 } 347 return (spec.PersistentVolume != nil && spec.PersistentVolume.Spec.CSI != nil) || 348 (spec.Volume != nil && spec.Volume.CSI != nil) 349 } 350 351 func (p *csiPlugin) RequiresRemount(spec *volume.Spec) bool { 352 if p.csiDriverLister == nil { 353 return false 354 } 355 driverName, err := GetCSIDriverName(spec) 356 if err != nil { 357 klog.V(5).Info(log("Failed to mark %q as republish required, err: %v", spec.Name(), err)) 358 return false 359 } 360 csiDriver, err := p.getCSIDriver(driverName) 361 if err != nil { 362 klog.V(5).Info(log("Failed to mark %q as republish required, err: %v", spec.Name(), err)) 363 return false 364 } 365 return *csiDriver.Spec.RequiresRepublish 366 } 367 368 func (p *csiPlugin) NewMounter( 369 spec *volume.Spec, 370 pod *api.Pod, 371 _ volume.VolumeOptions) (volume.Mounter, error) { 372 373 volSrc, pvSrc, err := getSourceFromSpec(spec) 374 if err != nil { 375 return nil, err 376 } 377 378 var ( 379 driverName string 380 volumeHandle string 381 readOnly bool 382 ) 383 384 switch { 385 case volSrc != nil: 386 volumeHandle = makeVolumeHandle(string(pod.UID), spec.Name()) 387 driverName = volSrc.Driver 388 if volSrc.ReadOnly != nil { 389 readOnly = *volSrc.ReadOnly 390 } 391 case pvSrc != nil: 392 driverName = pvSrc.Driver 393 volumeHandle = pvSrc.VolumeHandle 394 readOnly = spec.ReadOnly 395 default: 396 return nil, errors.New(log("volume source not found in volume.Spec")) 397 } 398 399 volumeLifecycleMode, err := p.getVolumeLifecycleMode(spec) 400 if err != nil { 401 return nil, err 402 } 403 404 k8s := p.host.GetKubeClient() 405 if k8s == nil { 406 return nil, errors.New(log("failed to get a kubernetes client")) 407 } 408 409 kvh, ok := p.host.(volume.KubeletVolumeHost) 410 if !ok { 411 return nil, errors.New(log("cast from VolumeHost to KubeletVolumeHost failed")) 412 } 413 414 mounter := &csiMountMgr{ 415 plugin: p, 416 k8s: k8s, 417 spec: spec, 418 pod: pod, 419 podUID: pod.UID, 420 driverName: csiDriverName(driverName), 421 volumeLifecycleMode: volumeLifecycleMode, 422 volumeID: volumeHandle, 423 specVolumeID: spec.Name(), 424 readOnly: readOnly, 425 kubeVolHost: kvh, 426 } 427 mounter.csiClientGetter.driverName = csiDriverName(driverName) 428 429 dir := mounter.GetPath() 430 mounter.MetricsProvider = NewMetricsCsi(volumeHandle, dir, csiDriverName(driverName)) 431 klog.V(4).Info(log("mounter created successfully")) 432 return mounter, nil 433 } 434 435 func (p *csiPlugin) NewUnmounter(specName string, podUID types.UID) (volume.Unmounter, error) { 436 klog.V(4).Infof(log("setting up unmounter for [name=%v, podUID=%v]", specName, podUID)) 437 438 kvh, ok := p.host.(volume.KubeletVolumeHost) 439 if !ok { 440 return nil, errors.New(log("cast from VolumeHost to KubeletVolumeHost failed")) 441 } 442 443 unmounter := &csiMountMgr{ 444 plugin: p, 445 podUID: podUID, 446 specVolumeID: specName, 447 kubeVolHost: kvh, 448 } 449 450 // load volume info from file 451 dir := unmounter.GetPath() 452 dataDir := filepath.Dir(dir) // dropoff /mount at end 453 data, err := loadVolumeData(dataDir, volDataFileName) 454 if err != nil { 455 return nil, errors.New(log("unmounter failed to load volume data file [%s]: %v", dir, err)) 456 } 457 unmounter.driverName = csiDriverName(data[volDataKey.driverName]) 458 unmounter.volumeID = data[volDataKey.volHandle] 459 unmounter.csiClientGetter.driverName = unmounter.driverName 460 461 return unmounter, nil 462 } 463 464 func (p *csiPlugin) ConstructVolumeSpec(volumeName, mountPath string) (volume.ReconstructedVolume, error) { 465 klog.V(4).Info(log("plugin.ConstructVolumeSpec [pv.Name=%v, path=%v]", volumeName, mountPath)) 466 467 volData, err := loadVolumeData(mountPath, volDataFileName) 468 if err != nil { 469 return volume.ReconstructedVolume{}, errors.New(log("plugin.ConstructVolumeSpec failed loading volume data using [%s]: %v", mountPath, err)) 470 } 471 klog.V(4).Info(log("plugin.ConstructVolumeSpec extracted [%#v]", volData)) 472 473 var ret volume.ReconstructedVolume 474 if utilfeature.DefaultFeatureGate.Enabled(features.SELinuxMountReadWriteOncePod) { 475 ret.SELinuxMountContext = volData[volDataKey.seLinuxMountContext] 476 } 477 478 // If mode is VolumeLifecycleEphemeral, use constructVolSourceSpec 479 // to construct volume source spec. If mode is VolumeLifecyclePersistent, 480 // use constructPVSourceSpec to construct volume construct pv source spec. 481 if storage.VolumeLifecycleMode(volData[volDataKey.volumeLifecycleMode]) == storage.VolumeLifecycleEphemeral { 482 ret.Spec = p.constructVolSourceSpec(volData[volDataKey.specVolID], volData[volDataKey.driverName]) 483 return ret, nil 484 } 485 486 ret.Spec = p.constructPVSourceSpec(volData[volDataKey.specVolID], volData[volDataKey.driverName], volData[volDataKey.volHandle]) 487 return ret, nil 488 } 489 490 // constructVolSourceSpec constructs volume.Spec with CSIVolumeSource 491 func (p *csiPlugin) constructVolSourceSpec(volSpecName, driverName string) *volume.Spec { 492 vol := &api.Volume{ 493 Name: volSpecName, 494 VolumeSource: api.VolumeSource{ 495 CSI: &api.CSIVolumeSource{ 496 Driver: driverName, 497 }, 498 }, 499 } 500 return volume.NewSpecFromVolume(vol) 501 } 502 503 // constructPVSourceSpec constructs volume.Spec with CSIPersistentVolumeSource 504 func (p *csiPlugin) constructPVSourceSpec(volSpecName, driverName, volumeHandle string) *volume.Spec { 505 fsMode := api.PersistentVolumeFilesystem 506 pv := &api.PersistentVolume{ 507 ObjectMeta: meta.ObjectMeta{ 508 Name: volSpecName, 509 }, 510 Spec: api.PersistentVolumeSpec{ 511 PersistentVolumeSource: api.PersistentVolumeSource{ 512 CSI: &api.CSIPersistentVolumeSource{ 513 Driver: driverName, 514 VolumeHandle: volumeHandle, 515 }, 516 }, 517 VolumeMode: &fsMode, 518 }, 519 } 520 return volume.NewSpecFromPersistentVolume(pv, false) 521 } 522 523 func (p *csiPlugin) SupportsMountOption() bool { 524 // TODO (vladimirvivien) use CSI VolumeCapability.MountVolume.mount_flags 525 // to probe for the result for this method 526 // (bswartz) Until the CSI spec supports probing, our only option is to 527 // make plugins register their support for mount options or lack thereof 528 // directly with kubernetes. 529 return true 530 } 531 532 func (p *csiPlugin) SupportsSELinuxContextMount(spec *volume.Spec) (bool, error) { 533 if utilfeature.DefaultFeatureGate.Enabled(features.SELinuxMountReadWriteOncePod) { 534 driver, err := GetCSIDriverName(spec) 535 if err != nil { 536 return false, err 537 } 538 csiDriver, err := p.getCSIDriver(driver) 539 if err != nil { 540 if apierrors.IsNotFound(err) { 541 return false, nil 542 } 543 return false, err 544 } 545 if csiDriver.Spec.SELinuxMount != nil { 546 return *csiDriver.Spec.SELinuxMount, nil 547 } 548 return false, nil 549 } 550 return false, nil 551 } 552 553 // volume.AttachableVolumePlugin methods 554 var _ volume.AttachableVolumePlugin = &csiPlugin{} 555 556 var _ volume.DeviceMountableVolumePlugin = &csiPlugin{} 557 558 func (p *csiPlugin) NewAttacher() (volume.Attacher, error) { 559 return p.newAttacherDetacher() 560 } 561 562 func (p *csiPlugin) NewDeviceMounter() (volume.DeviceMounter, error) { 563 return p.NewAttacher() 564 } 565 566 func (p *csiPlugin) NewDetacher() (volume.Detacher, error) { 567 return p.newAttacherDetacher() 568 } 569 570 func (p *csiPlugin) CanAttach(spec *volume.Spec) (bool, error) { 571 volumeLifecycleMode, err := p.getVolumeLifecycleMode(spec) 572 if err != nil { 573 return false, err 574 } 575 576 if volumeLifecycleMode == storage.VolumeLifecycleEphemeral { 577 klog.V(5).Info(log("plugin.CanAttach = false, ephemeral mode detected for spec %v", spec.Name())) 578 return false, nil 579 } 580 581 pvSrc, err := getCSISourceFromSpec(spec) 582 if err != nil { 583 return false, err 584 } 585 586 driverName := pvSrc.Driver 587 588 skipAttach, err := p.skipAttach(driverName) 589 if err != nil { 590 return false, err 591 } 592 593 return !skipAttach, nil 594 } 595 596 // CanDeviceMount returns true if the spec supports device mount 597 func (p *csiPlugin) CanDeviceMount(spec *volume.Spec) (bool, error) { 598 volumeLifecycleMode, err := p.getVolumeLifecycleMode(spec) 599 if err != nil { 600 return false, err 601 } 602 603 if volumeLifecycleMode == storage.VolumeLifecycleEphemeral { 604 klog.V(5).Info(log("plugin.CanDeviceMount skipped ephemeral mode detected for spec %v", spec.Name())) 605 return false, nil 606 } 607 608 // Persistent volumes support device mount. 609 return true, nil 610 } 611 612 func (p *csiPlugin) NewDeviceUnmounter() (volume.DeviceUnmounter, error) { 613 return p.NewDetacher() 614 } 615 616 func (p *csiPlugin) GetDeviceMountRefs(deviceMountPath string) ([]string, error) { 617 m := p.host.GetMounter(p.GetPluginName()) 618 return m.GetMountRefs(deviceMountPath) 619 } 620 621 // BlockVolumePlugin methods 622 var _ volume.BlockVolumePlugin = &csiPlugin{} 623 624 func (p *csiPlugin) NewBlockVolumeMapper(spec *volume.Spec, podRef *api.Pod, opts volume.VolumeOptions) (volume.BlockVolumeMapper, error) { 625 pvSource, err := getCSISourceFromSpec(spec) 626 if err != nil { 627 return nil, err 628 } 629 readOnly, err := getReadOnlyFromSpec(spec) 630 if err != nil { 631 return nil, err 632 } 633 634 klog.V(4).Info(log("setting up block mapper for [volume=%v,driver=%v]", pvSource.VolumeHandle, pvSource.Driver)) 635 636 k8s := p.host.GetKubeClient() 637 if k8s == nil { 638 return nil, errors.New(log("failed to get a kubernetes client")) 639 } 640 641 mapper := &csiBlockMapper{ 642 k8s: k8s, 643 plugin: p, 644 volumeID: pvSource.VolumeHandle, 645 driverName: csiDriverName(pvSource.Driver), 646 readOnly: readOnly, 647 spec: spec, 648 specName: spec.Name(), 649 pod: podRef, 650 podUID: podRef.UID, 651 } 652 mapper.csiClientGetter.driverName = csiDriverName(pvSource.Driver) 653 654 // Save volume info in pod dir 655 dataDir := getVolumeDeviceDataDir(spec.Name(), p.host) 656 657 if err := os.MkdirAll(dataDir, 0750); err != nil { 658 return nil, errors.New(log("failed to create data dir %s: %v", dataDir, err)) 659 } 660 klog.V(4).Info(log("created path successfully [%s]", dataDir)) 661 662 blockPath, err := mapper.GetGlobalMapPath(spec) 663 if err != nil { 664 return nil, errors.New(log("failed to get device path: %v", err)) 665 } 666 667 mapper.MetricsProvider = NewMetricsCsi(pvSource.VolumeHandle, blockPath+"/"+string(podRef.UID), csiDriverName(pvSource.Driver)) 668 669 // persist volume info data for teardown 670 node := string(p.host.GetNodeName()) 671 attachID := getAttachmentName(pvSource.VolumeHandle, pvSource.Driver, node) 672 volData := map[string]string{ 673 volDataKey.specVolID: spec.Name(), 674 volDataKey.volHandle: pvSource.VolumeHandle, 675 volDataKey.driverName: pvSource.Driver, 676 volDataKey.nodeName: node, 677 volDataKey.attachmentID: attachID, 678 } 679 680 err = saveVolumeData(dataDir, volDataFileName, volData) 681 defer func() { 682 // Only if there was an error and volume operation was considered 683 // finished, we should remove the directory. 684 if err != nil && volumetypes.IsOperationFinishedError(err) { 685 // attempt to cleanup volume mount dir. 686 if err = removeMountDir(p, dataDir); err != nil { 687 klog.Error(log("attacher.MountDevice failed to remove mount dir after error [%s]: %v", dataDir, err)) 688 } 689 } 690 }() 691 if err != nil { 692 errorMsg := log("csi.NewBlockVolumeMapper failed to save volume info data: %v", err) 693 klog.Error(errorMsg) 694 return nil, errors.New(errorMsg) 695 } 696 697 return mapper, nil 698 } 699 700 func (p *csiPlugin) NewBlockVolumeUnmapper(volName string, podUID types.UID) (volume.BlockVolumeUnmapper, error) { 701 klog.V(4).Infof(log("setting up block unmapper for [Spec=%v, podUID=%v]", volName, podUID)) 702 unmapper := &csiBlockMapper{ 703 plugin: p, 704 podUID: podUID, 705 specName: volName, 706 } 707 708 // load volume info from file 709 dataDir := getVolumeDeviceDataDir(unmapper.specName, p.host) 710 data, err := loadVolumeData(dataDir, volDataFileName) 711 if err != nil { 712 return nil, errors.New(log("unmapper failed to load volume data file [%s]: %v", dataDir, err)) 713 } 714 unmapper.driverName = csiDriverName(data[volDataKey.driverName]) 715 unmapper.volumeID = data[volDataKey.volHandle] 716 unmapper.csiClientGetter.driverName = unmapper.driverName 717 718 return unmapper, nil 719 } 720 721 func (p *csiPlugin) ConstructBlockVolumeSpec(podUID types.UID, specVolName, mapPath string) (*volume.Spec, error) { 722 klog.V(4).Infof("plugin.ConstructBlockVolumeSpec [podUID=%s, specVolName=%s, path=%s]", string(podUID), specVolName, mapPath) 723 724 dataDir := getVolumeDeviceDataDir(specVolName, p.host) 725 volData, err := loadVolumeData(dataDir, volDataFileName) 726 if err != nil { 727 return nil, errors.New(log("plugin.ConstructBlockVolumeSpec failed loading volume data using [%s]: %v", mapPath, err)) 728 } 729 730 klog.V(4).Info(log("plugin.ConstructBlockVolumeSpec extracted [%#v]", volData)) 731 732 blockMode := api.PersistentVolumeBlock 733 pv := &api.PersistentVolume{ 734 ObjectMeta: meta.ObjectMeta{ 735 Name: volData[volDataKey.specVolID], 736 }, 737 Spec: api.PersistentVolumeSpec{ 738 PersistentVolumeSource: api.PersistentVolumeSource{ 739 CSI: &api.CSIPersistentVolumeSource{ 740 Driver: volData[volDataKey.driverName], 741 VolumeHandle: volData[volDataKey.volHandle], 742 }, 743 }, 744 VolumeMode: &blockMode, 745 }, 746 } 747 748 return volume.NewSpecFromPersistentVolume(pv, false), nil 749 } 750 751 // skipAttach looks up CSIDriver object associated with driver name 752 // to determine if driver requires attachment volume operation 753 func (p *csiPlugin) skipAttach(driver string) (bool, error) { 754 csiDriver, err := p.getCSIDriver(driver) 755 if err != nil { 756 if apierrors.IsNotFound(err) { 757 // Don't skip attach if CSIDriver does not exist 758 return false, nil 759 } 760 return false, err 761 } 762 if csiDriver.Spec.AttachRequired != nil && *csiDriver.Spec.AttachRequired == false { 763 return true, nil 764 } 765 return false, nil 766 } 767 768 func (p *csiPlugin) getCSIDriver(driver string) (*storage.CSIDriver, error) { 769 kletHost, ok := p.host.(volume.KubeletVolumeHost) 770 if ok { 771 if err := kletHost.WaitForCacheSync(); err != nil { 772 return nil, err 773 } 774 } 775 776 if p.csiDriverLister == nil { 777 return nil, errors.New("CSIDriver lister does not exist") 778 } 779 csiDriver, err := p.csiDriverLister.Get(driver) 780 return csiDriver, err 781 } 782 783 // getVolumeLifecycleMode returns the mode for the specified spec: {persistent|ephemeral}. 784 // 1) If mode cannot be determined, it will default to "persistent". 785 // 2) If Mode cannot be resolved to either {persistent | ephemeral}, an error is returned 786 // See https://github.com/kubernetes/enhancements/blob/master/keps/sig-storage/596-csi-inline-volumes/README.md 787 func (p *csiPlugin) getVolumeLifecycleMode(spec *volume.Spec) (storage.VolumeLifecycleMode, error) { 788 // 1) if volume.Spec.Volume.CSI != nil -> mode is ephemeral 789 // 2) if volume.Spec.PersistentVolume.Spec.CSI != nil -> persistent 790 volSrc, _, err := getSourceFromSpec(spec) 791 if err != nil { 792 return "", err 793 } 794 795 if volSrc != nil { 796 return storage.VolumeLifecycleEphemeral, nil 797 } 798 return storage.VolumeLifecyclePersistent, nil 799 } 800 801 func (p *csiPlugin) getPublishContext(client clientset.Interface, handle, driver, nodeName string) (map[string]string, error) { 802 skip, err := p.skipAttach(driver) 803 if err != nil { 804 return nil, err 805 } 806 if skip { 807 return nil, nil 808 } 809 810 attachID := getAttachmentName(handle, driver, nodeName) 811 812 // search for attachment by VolumeAttachment.Spec.Source.PersistentVolumeName 813 attachment, err := client.StorageV1().VolumeAttachments().Get(context.TODO(), attachID, meta.GetOptions{}) 814 if err != nil { 815 return nil, err // This err already has enough context ("VolumeAttachment xyz not found") 816 } 817 818 if attachment == nil { 819 err = errors.New("no existing VolumeAttachment found") 820 return nil, err 821 } 822 return attachment.Status.AttachmentMetadata, nil 823 } 824 825 func (p *csiPlugin) newAttacherDetacher() (*csiAttacher, error) { 826 k8s := p.host.GetKubeClient() 827 if k8s == nil { 828 return nil, errors.New(log("unable to get kubernetes client from host")) 829 } 830 831 return &csiAttacher{ 832 plugin: p, 833 k8s: k8s, 834 watchTimeout: csiTimeout, 835 }, nil 836 } 837 838 // podInfoEnabled check CSIDriver enabled pod info flag 839 func (p *csiPlugin) podInfoEnabled(driverName string) (bool, error) { 840 csiDriver, err := p.getCSIDriver(driverName) 841 if err != nil { 842 if apierrors.IsNotFound(err) { 843 klog.V(4).Infof(log("CSIDriver %q not found, not adding pod information", driverName)) 844 return false, nil 845 } 846 return false, err 847 } 848 849 // if PodInfoOnMount is not set or false we do not set pod attributes 850 if csiDriver.Spec.PodInfoOnMount == nil || *csiDriver.Spec.PodInfoOnMount == false { 851 klog.V(4).Infof(log("CSIDriver %q does not require pod information", driverName)) 852 return false, nil 853 } 854 return true, nil 855 } 856 857 func unregisterDriver(driverName string) error { 858 csiDrivers.Delete(driverName) 859 860 if err := nim.UninstallCSIDriver(driverName); err != nil { 861 return errors.New(log("Error uninstalling CSI driver: %v", err)) 862 } 863 864 return nil 865 } 866 867 // waitForAPIServerForever waits forever to get a CSINode instance as a proxy 868 // for a healthy APIServer 869 func waitForAPIServerForever(client clientset.Interface, nodeName types.NodeName) error { 870 var lastErr error 871 // Served object is discarded so no risk to have stale object with benefit to 872 // reduce the load on APIServer and etcd. 873 opts := meta.GetOptions{} 874 util.FromApiserverCache(&opts) 875 err := wait.PollImmediateInfinite(time.Second, func() (bool, error) { 876 // Get a CSINode from API server to make sure 1) kubelet can reach API server 877 // and 2) it has enough permissions. Kubelet may have restricted permissions 878 // when it's bootstrapping TLS. 879 // https://kubernetes.io/docs/reference/command-line-tools-reference/kubelet-tls-bootstrapping/ 880 _, lastErr = client.StorageV1().CSINodes().Get(context.TODO(), string(nodeName), opts) 881 if lastErr == nil || apierrors.IsNotFound(lastErr) { 882 // API server contacted 883 return true, nil 884 } 885 klog.V(2).Infof("Failed to contact API server when waiting for CSINode publishing: %s", lastErr) 886 return false, nil 887 }) 888 if err != nil { 889 // In theory this is unreachable, but just in case: 890 return fmt.Errorf("%v: %v", err, lastErr) 891 } 892 893 return nil 894 }