github.com/jingruilea/kubeedge@v1.2.0-beta.0.0.20200410162146-4bb8902b3879/edge/pkg/edged/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 @CHANGELOG 17 KubeEdge Authors: To create mini-kubelet for edge deployment scenario, 18 this file is derived from kubernetes v1.15.3, 19 and the full file path is k8s.io/kubernetes/pkg/volume/csi/csi_plugin.go 20 and make some modifications including: 21 1. remove BlockVolumePlugin. 22 2. empty skipAttach function. 23 3. add NewController function. 24 */ 25 26 package csi 27 28 import ( 29 "context" 30 "errors" 31 "fmt" 32 "os" 33 "path" 34 "sort" 35 "strings" 36 "time" 37 38 api "k8s.io/api/core/v1" 39 meta "k8s.io/apimachinery/pkg/apis/meta/v1" 40 "k8s.io/apimachinery/pkg/types" 41 utilruntime "k8s.io/apimachinery/pkg/util/runtime" 42 utilversion "k8s.io/apimachinery/pkg/util/version" 43 "k8s.io/apimachinery/pkg/util/wait" 44 utilfeature "k8s.io/apiserver/pkg/util/feature" 45 clientset "k8s.io/client-go/kubernetes" 46 storagelisters "k8s.io/client-go/listers/storage/v1beta1" 47 csitranslationplugins "k8s.io/csi-translation-lib/plugins" 48 "k8s.io/klog" 49 "k8s.io/kubernetes/pkg/features" 50 "k8s.io/kubernetes/pkg/volume" 51 52 "github.com/kubeedge/kubeedge/edge/pkg/edged/volume/csi/nodeinfomanager" 53 ) 54 55 const ( 56 // CSIPluginName is the name of the in-tree CSI Plugin 57 CSIPluginName = "kubernetes.io/csi" 58 59 // TODO (vladimirvivien) implement a more dynamic way to discover 60 // the unix domain socket path for each installed csi driver. 61 // TODO (vladimirvivien) would be nice to name socket with a .sock extension 62 // for consistency. 63 csiAddrTemplate = "/var/lib/kubelet/plugins/%v/csi.sock" 64 csiTimeout = 2 * time.Minute 65 volNameSep = "^" 66 volDataFileName = "vol_data.json" 67 fsTypeBlockName = "block" 68 69 // TODO: increase to something useful 70 csiResyncPeriod = time.Minute 71 ) 72 73 var deprecatedSocketDirVersions = []string{"0.1.0", "0.2.0", "0.3.0", "0.4.0"} 74 75 type csiPlugin struct { 76 host volume.VolumeHost 77 blockEnabled bool 78 csiDriverLister storagelisters.CSIDriverLister 79 } 80 81 //TODO (vladimirvivien) add this type to storage api 82 type driverMode string 83 84 const persistentDriverMode driverMode = "persistent" 85 const ephemeralDriverMode driverMode = "ephemeral" 86 87 // ProbeVolumePlugins returns implemented plugins 88 func ProbeVolumePlugins() []volume.VolumePlugin { 89 p := &csiPlugin{ 90 host: nil, 91 blockEnabled: utilfeature.DefaultFeatureGate.Enabled(features.CSIBlockVolume), 92 } 93 return []volume.VolumePlugin{p} 94 } 95 96 // volume.VolumePlugin methods 97 var _ volume.VolumePlugin = &csiPlugin{} 98 99 // RegistrationHandler is the handler which is fed to the pluginwatcher API. 100 type RegistrationHandler struct { 101 } 102 103 // TODO (verult) consider using a struct instead of global variables 104 // csiDrivers map keep track of all registered CSI drivers on the node and their 105 // corresponding sockets 106 var csiDrivers = &DriversStore{} 107 108 var nim nodeinfomanager.Interface 109 110 // PluginHandler is the plugin registration handler interface passed to the 111 // pluginwatcher module in kubelet 112 var PluginHandler = &RegistrationHandler{} 113 114 // ValidatePlugin is called by kubelet's plugin watcher upon detection 115 // of a new registration socket opened by CSI Driver registrar side car. 116 func (h *RegistrationHandler) ValidatePlugin(pluginName string, endpoint string, versions []string) error { 117 klog.Infof(log("Trying to validate a new CSI Driver with name: %s endpoint: %s versions: %s", 118 pluginName, endpoint, strings.Join(versions, ","))) 119 120 _, err := h.validateVersions("ValidatePlugin", pluginName, endpoint, versions) 121 if err != nil { 122 return fmt.Errorf("validation failed for CSI Driver %s at endpoint %s: %v", pluginName, endpoint, err) 123 } 124 125 return err 126 } 127 128 // RegisterPlugin is called when a plugin can be registered 129 func (h *RegistrationHandler) RegisterPlugin(pluginName string, endpoint string, versions []string) error { 130 klog.Infof(log("Register new plugin with name: %s at endpoint: %s", pluginName, endpoint)) 131 132 highestSupportedVersion, err := h.validateVersions("RegisterPlugin", pluginName, endpoint, versions) 133 if err != nil { 134 return err 135 } 136 137 // Storing endpoint of newly registered CSI driver into the map, where CSI driver name will be the key 138 // all other CSI components will be able to get the actual socket of CSI drivers by its name. 139 csiDrivers.Set(pluginName, Driver{ 140 endpoint: endpoint, 141 highestSupportedVersion: highestSupportedVersion, 142 }) 143 144 // Get node info from the driver. 145 csi, err := newCsiDriverClient(csiDriverName(pluginName)) 146 if err != nil { 147 return err 148 } 149 150 ctx, cancel := context.WithTimeout(context.Background(), csiTimeout) 151 defer cancel() 152 153 driverNodeID, maxVolumePerNode, accessibleTopology, err := csi.NodeGetInfo(ctx) 154 if err != nil { 155 if unregErr := unregisterDriver(pluginName); unregErr != nil { 156 klog.Error(log("registrationHandler.RegisterPlugin failed to unregister plugin due to previous error: %v", unregErr)) 157 } 158 return err 159 } 160 161 err = nim.InstallCSIDriver(pluginName, driverNodeID, maxVolumePerNode, accessibleTopology) 162 if err != nil { 163 if unregErr := unregisterDriver(pluginName); unregErr != nil { 164 klog.Error(log("registrationHandler.RegisterPlugin failed to unregister plugin due to previous error: %v", unregErr)) 165 } 166 return err 167 } 168 169 return nil 170 } 171 172 func (h *RegistrationHandler) validateVersions(callerName, pluginName string, endpoint string, versions []string) (*utilversion.Version, error) { 173 if len(versions) == 0 { 174 err := fmt.Errorf("%s for CSI driver %q failed. Plugin returned an empty list for supported versions", callerName, pluginName) 175 klog.Error(err) 176 return nil, err 177 } 178 179 // Validate version 180 newDriverHighestVersion, err := highestSupportedVersion(versions) 181 if err != nil { 182 err := fmt.Errorf("%s for CSI driver %q failed. None of the versions specified %q are supported. err=%v", callerName, pluginName, versions, err) 183 klog.Error(err) 184 return nil, err 185 } 186 187 existingDriver, driverExists := csiDrivers.Get(pluginName) 188 if driverExists { 189 if !existingDriver.highestSupportedVersion.LessThan(newDriverHighestVersion) { 190 err := fmt.Errorf("%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) 191 klog.Error(err) 192 return nil, err 193 } 194 } 195 196 return newDriverHighestVersion, nil 197 } 198 199 // DeRegisterPlugin is called when a plugin removed its socket, signaling 200 // it is no longer available 201 func (h *RegistrationHandler) DeRegisterPlugin(pluginName string) { 202 klog.V(4).Info(log("registrationHandler.DeRegisterPlugin request for plugin %s", pluginName)) 203 if err := unregisterDriver(pluginName); err != nil { 204 klog.Error(log("registrationHandler.DeRegisterPlugin failed: %v", err)) 205 } 206 } 207 208 func (p *csiPlugin) Init(host volume.VolumeHost) error { 209 p.host = host 210 211 if utilfeature.DefaultFeatureGate.Enabled(features.CSIDriverRegistry) { 212 csiClient := host.GetKubeClient() 213 if csiClient == nil { 214 klog.Warning(log("kubeclient not set, assuming standalone kubelet")) 215 } else { 216 // set CSIDriverLister 217 adcHost, ok := host.(volume.AttachDetachVolumeHost) 218 if ok { 219 p.csiDriverLister = adcHost.CSIDriverLister() 220 if p.csiDriverLister == nil { 221 klog.Error(log("CSIDriverLister not found on AttachDetachVolumeHost")) 222 } 223 } 224 kletHost, ok := host.(volume.KubeletVolumeHost) 225 if ok { 226 p.csiDriverLister = kletHost.CSIDriverLister() 227 if p.csiDriverLister == nil { 228 klog.Error(log("CSIDriverLister not found on KubeletVolumeHost")) 229 } 230 } 231 } 232 } 233 234 var migratedPlugins = map[string](func() bool){ 235 csitranslationplugins.GCEPDInTreePluginName: func() bool { 236 return utilfeature.DefaultFeatureGate.Enabled(features.CSIMigration) && utilfeature.DefaultFeatureGate.Enabled(features.CSIMigrationGCE) 237 }, 238 csitranslationplugins.AWSEBSInTreePluginName: func() bool { 239 return utilfeature.DefaultFeatureGate.Enabled(features.CSIMigration) && utilfeature.DefaultFeatureGate.Enabled(features.CSIMigrationAWS) 240 }, 241 csitranslationplugins.CinderInTreePluginName: func() bool { 242 return utilfeature.DefaultFeatureGate.Enabled(features.CSIMigration) && utilfeature.DefaultFeatureGate.Enabled(features.CSIMigrationOpenStack) 243 }, 244 } 245 246 // Initializing the label management channels 247 nim = nodeinfomanager.NewNodeInfoManager(host.GetNodeName(), host, migratedPlugins) 248 249 // TODO: Evaluate the feature releated to csi 250 /*if utilfeature.DefaultFeatureGate.Enabled(features.CSINodeInfo) && 251 utilfeature.DefaultFeatureGate.Enabled(features.CSIMigration) { 252 // This function prevents Kubelet from posting Ready status until CSINodeInfo 253 // is both installed and initialized 254 if err := initializeCSINode(host); err != nil { 255 return fmt.Errorf("failed to initialize CSINodeInfo: %v", err) 256 } 257 }*/ 258 259 return nil 260 } 261 262 func initializeCSINode(host volume.VolumeHost) error { 263 kvh, ok := host.(volume.KubeletVolumeHost) 264 if !ok { 265 klog.V(4).Info("Cast from VolumeHost to KubeletVolumeHost failed. Skipping CSINodeInfo initialization, not running on kubelet") 266 return nil 267 } 268 kubeClient := host.GetKubeClient() 269 if kubeClient == nil { 270 // Kubelet running in standalone mode. Skip CSINodeInfo initialization 271 klog.Warning("Skipping CSINodeInfo initialization, kubelet running in standalone mode") 272 return nil 273 } 274 275 kvh.SetKubeletError(errors.New("CSINodeInfo is not yet initialized")) 276 277 go func() { 278 defer utilruntime.HandleCrash() 279 280 // Backoff parameters tuned to retry over 140 seconds. Will fail and restart the Kubelet 281 // after max retry steps. 282 initBackoff := wait.Backoff{ 283 Steps: 6, 284 Duration: 15 * time.Millisecond, 285 Factor: 6.0, 286 Jitter: 0.1, 287 } 288 err := wait.ExponentialBackoff(initBackoff, func() (bool, error) { 289 klog.V(4).Infof("Initializing migrated drivers on CSINodeInfo") 290 err := nim.InitializeCSINodeWithAnnotation() 291 if err != nil { 292 kvh.SetKubeletError(fmt.Errorf("Failed to initialize CSINodeInfo: %v", err)) 293 klog.Errorf("Failed to initialize CSINodeInfo: %v", err) 294 return false, nil 295 } 296 297 // Successfully initialized drivers, allow Kubelet to post Ready 298 kvh.SetKubeletError(nil) 299 return true, nil 300 }) 301 if err != nil { 302 // 2 releases after CSIMigration and all CSIMigrationX (where X is a volume plugin) 303 // are permanently enabled the apiserver/controllers can assume that the kubelet is 304 // using CSI for all Migrated volume plugins. Then all the CSINode initialization 305 // code can be dropped from Kubelet. 306 // Kill the Kubelet process and allow it to restart to retry initialization 307 klog.Fatalf("Failed to initialize CSINodeInfo after retrying") 308 } 309 }() 310 return nil 311 } 312 313 func (p *csiPlugin) GetPluginName() string { 314 return CSIPluginName 315 } 316 317 // GetvolumeName returns a concatenated string of CSIVolumeSource.Driver<volNameSe>CSIVolumeSource.VolumeHandle 318 // That string value is used in Detach() to extract driver name and volumeName. 319 func (p *csiPlugin) GetVolumeName(spec *volume.Spec) (string, error) { 320 csi, err := getPVSourceFromSpec(spec) 321 if err != nil { 322 klog.Error(log("plugin.GetVolumeName failed to extract volume source from spec: %v", err)) 323 return "", err 324 } 325 326 // return driverName<separator>volumeHandle 327 return fmt.Sprintf("%s%s%s", csi.Driver, volNameSep, csi.VolumeHandle), nil 328 } 329 330 func (p *csiPlugin) CanSupport(spec *volume.Spec) bool { 331 // TODO (vladimirvivien) CanSupport should also take into account 332 // the availability/registration of specified Driver in the volume source 333 if spec == nil { 334 return false 335 } 336 if utilfeature.DefaultFeatureGate.Enabled(features.CSIInlineVolume) { 337 return (spec.PersistentVolume != nil && spec.PersistentVolume.Spec.CSI != nil) || 338 (spec.Volume != nil && spec.Volume.CSI != nil) 339 } 340 341 return spec.PersistentVolume != nil && spec.PersistentVolume.Spec.CSI != nil 342 } 343 344 func (p *csiPlugin) IsMigratedToCSI() bool { 345 return false 346 } 347 348 func (p *csiPlugin) RequiresRemount() bool { 349 return false 350 } 351 352 func (p *csiPlugin) NewMounter( 353 spec *volume.Spec, 354 pod *api.Pod, 355 _ volume.VolumeOptions) (volume.Mounter, error) { 356 357 volSrc, pvSrc, err := getSourceFromSpec(spec) 358 if err != nil { 359 return nil, err 360 } 361 362 var ( 363 driverName string 364 volumeHandle string 365 readOnly bool 366 ) 367 368 switch { 369 case volSrc != nil && utilfeature.DefaultFeatureGate.Enabled(features.CSIInlineVolume): 370 volumeHandle = makeVolumeHandle(string(pod.UID), spec.Name()) 371 driverName = volSrc.Driver 372 if volSrc.ReadOnly != nil { 373 readOnly = *volSrc.ReadOnly 374 } 375 case pvSrc != nil: 376 driverName = pvSrc.Driver 377 volumeHandle = pvSrc.VolumeHandle 378 readOnly = spec.ReadOnly 379 default: 380 return nil, fmt.Errorf("volume source not found in volume.Spec") 381 } 382 383 driverMode, err := p.getDriverMode(spec) 384 if err != nil { 385 return nil, err 386 } 387 388 k8s := p.host.GetKubeClient() 389 if k8s == nil { 390 klog.Error(log("failed to get a kubernetes client")) 391 return nil, errors.New("failed to get a Kubernetes client") 392 } 393 394 kvh, ok := p.host.(volume.KubeletVolumeHost) 395 if !ok { 396 return nil, errors.New(log("cast from VolumeHost to KubeletVolumeHost failed")) 397 } 398 399 mounter := &csiMountMgr{ 400 plugin: p, 401 k8s: k8s, 402 spec: spec, 403 pod: pod, 404 podUID: pod.UID, 405 driverName: csiDriverName(driverName), 406 driverMode: driverMode, 407 volumeID: volumeHandle, 408 specVolumeID: spec.Name(), 409 readOnly: readOnly, 410 kubeVolHost: kvh, 411 } 412 mounter.csiClientGetter.driverName = csiDriverName(driverName) 413 414 // Save volume info in pod dir 415 dir := mounter.GetPath() 416 dataDir := path.Dir(dir) // dropoff /mount at end 417 418 if err := os.MkdirAll(dataDir, 0750); err != nil { 419 klog.Error(log("failed to create dir %#v: %v", dataDir, err)) 420 return nil, err 421 } 422 klog.V(4).Info(log("created path successfully [%s]", dataDir)) 423 424 // persist volume info data for teardown 425 node := string(p.host.GetNodeName()) 426 volData := map[string]string{ 427 volDataKey.specVolID: spec.Name(), 428 volDataKey.volHandle: volumeHandle, 429 volDataKey.driverName: driverName, 430 volDataKey.nodeName: node, 431 volDataKey.driverMode: string(driverMode), 432 } 433 434 attachID := getAttachmentName(volumeHandle, driverName, node) 435 volData[volDataKey.attachmentID] = attachID 436 437 if err := saveVolumeData(dataDir, volDataFileName, volData); err != nil { 438 klog.Error(log("failed to save volume info data: %v", err)) 439 if err := os.RemoveAll(dataDir); err != nil { 440 klog.Error(log("failed to remove dir after error [%s]: %v", dataDir, err)) 441 return nil, err 442 } 443 return nil, err 444 } 445 446 klog.V(4).Info(log("mounter created successfully")) 447 448 return mounter, nil 449 } 450 451 func (p *csiPlugin) NewUnmounter(specName string, podUID types.UID) (volume.Unmounter, error) { 452 klog.V(4).Infof(log("setting up unmounter for [name=%v, podUID=%v]", specName, podUID)) 453 454 kvh, ok := p.host.(volume.KubeletVolumeHost) 455 if !ok { 456 return nil, errors.New(log("cast from VolumeHost to KubeletVolumeHost failed")) 457 } 458 459 unmounter := &csiMountMgr{ 460 plugin: p, 461 podUID: podUID, 462 specVolumeID: specName, 463 kubeVolHost: kvh, 464 } 465 466 // load volume info from file 467 dir := unmounter.GetPath() 468 dataDir := path.Dir(dir) // dropoff /mount at end 469 data, err := loadVolumeData(dataDir, volDataFileName) 470 if err != nil { 471 klog.Error(log("unmounter failed to load volume data file [%s]: %v", dir, err)) 472 return nil, err 473 } 474 unmounter.driverName = csiDriverName(data[volDataKey.driverName]) 475 unmounter.volumeID = data[volDataKey.volHandle] 476 unmounter.csiClientGetter.driverName = unmounter.driverName 477 478 return unmounter, nil 479 } 480 481 func (p *csiPlugin) ConstructVolumeSpec(volumeName, mountPath string) (*volume.Spec, error) { 482 klog.V(4).Info(log("plugin.ConstructVolumeSpec [pv.Name=%v, path=%v]", volumeName, mountPath)) 483 484 volData, err := loadVolumeData(mountPath, volDataFileName) 485 if err != nil { 486 klog.Error(log("plugin.ConstructVolumeSpec failed loading volume data using [%s]: %v", mountPath, err)) 487 return nil, err 488 } 489 490 klog.V(4).Info(log("plugin.ConstructVolumeSpec extracted [%#v]", volData)) 491 492 var spec *volume.Spec 493 inlineEnabled := utilfeature.DefaultFeatureGate.Enabled(features.CSIInlineVolume) 494 495 if inlineEnabled { 496 mode := driverMode(volData[volDataKey.driverMode]) 497 switch { 498 case mode == ephemeralDriverMode: 499 spec = p.constructVolSourceSpec(volData[volDataKey.specVolID], volData[volDataKey.driverName]) 500 501 case mode == persistentDriverMode: 502 fallthrough 503 default: 504 spec = p.constructPVSourceSpec(volData[volDataKey.specVolID], volData[volDataKey.driverName], volData[volDataKey.volHandle]) 505 } 506 } else { 507 spec = p.constructPVSourceSpec(volData[volDataKey.specVolID], volData[volDataKey.driverName], volData[volDataKey.volHandle]) 508 } 509 510 return spec, nil 511 } 512 513 // constructVolSourceSpec constructs volume.Spec with CSIVolumeSource 514 func (p *csiPlugin) constructVolSourceSpec(volSpecName, driverName string) *volume.Spec { 515 vol := &api.Volume{ 516 Name: volSpecName, 517 VolumeSource: api.VolumeSource{ 518 CSI: &api.CSIVolumeSource{ 519 Driver: driverName, 520 }, 521 }, 522 } 523 return volume.NewSpecFromVolume(vol) 524 } 525 526 //constructPVSourceSpec constructs volume.Spec with CSIPersistentVolumeSource 527 func (p *csiPlugin) constructPVSourceSpec(volSpecName, driverName, volumeHandle string) *volume.Spec { 528 fsMode := api.PersistentVolumeFilesystem 529 pv := &api.PersistentVolume{ 530 ObjectMeta: meta.ObjectMeta{ 531 Name: volSpecName, 532 }, 533 Spec: api.PersistentVolumeSpec{ 534 PersistentVolumeSource: api.PersistentVolumeSource{ 535 CSI: &api.CSIPersistentVolumeSource{ 536 Driver: driverName, 537 VolumeHandle: volumeHandle, 538 }, 539 }, 540 VolumeMode: &fsMode, 541 }, 542 } 543 return volume.NewSpecFromPersistentVolume(pv, false) 544 } 545 546 func (p *csiPlugin) SupportsMountOption() bool { 547 // TODO (vladimirvivien) use CSI VolumeCapability.MountVolume.mount_flags 548 // to probe for the result for this method 549 // (bswartz) Until the CSI spec supports probing, our only option is to 550 // make plugins register their support for mount options or lack thereof 551 // directly with kubernetes. 552 return true 553 } 554 555 func (p *csiPlugin) SupportsBulkVolumeVerification() bool { 556 return false 557 } 558 559 // volume.AttachableVolumePlugin methods 560 var _ volume.AttachableVolumePlugin = &csiPlugin{} 561 562 var _ volume.DeviceMountableVolumePlugin = &csiPlugin{} 563 564 func (p *csiPlugin) NewAttacher() (volume.Attacher, error) { 565 return p.newAttacherDetacher() 566 } 567 568 func (p *csiPlugin) NewDeviceMounter() (volume.DeviceMounter, error) { 569 return p.NewAttacher() 570 } 571 572 func (p *csiPlugin) NewDetacher() (volume.Detacher, error) { 573 return p.newAttacherDetacher() 574 } 575 576 func (p *csiPlugin) CanAttach(spec *volume.Spec) (bool, error) { 577 driverMode, err := p.getDriverMode(spec) 578 if err != nil { 579 return false, err 580 } 581 582 if driverMode == ephemeralDriverMode { 583 klog.V(5).Info(log("plugin.CanAttach = false, ephemeral mode detected for spec %v", spec.Name())) 584 return false, nil 585 } 586 587 pvSrc, err := getCSISourceFromSpec(spec) 588 if err != nil { 589 return false, err 590 } 591 592 driverName := pvSrc.Driver 593 594 skipAttach, err := p.skipAttach(driverName) 595 if err != nil { 596 return false, err 597 } 598 599 return !skipAttach, nil 600 } 601 602 // CanDeviceMount returns true if the spec supports device mount 603 func (p *csiPlugin) CanDeviceMount(spec *volume.Spec) (bool, error) { 604 driverMode, err := p.getDriverMode(spec) 605 if err != nil { 606 return false, err 607 } 608 609 if driverMode == ephemeralDriverMode { 610 klog.V(5).Info(log("plugin.CanDeviceMount skipped ephemeral mode detected for spec %v", spec.Name())) 611 return false, nil 612 } 613 614 return true, nil 615 } 616 617 func (p *csiPlugin) NewDeviceUnmounter() (volume.DeviceUnmounter, error) { 618 return p.NewDetacher() 619 } 620 621 func (p *csiPlugin) GetDeviceMountRefs(deviceMountPath string) ([]string, error) { 622 m := p.host.GetMounter(p.GetPluginName()) 623 return m.GetMountRefs(deviceMountPath) 624 } 625 626 // skipAttach looks up CSIDriver object associated with driver name 627 // to determine if driver requires attachment volume operation 628 func (p *csiPlugin) skipAttach(driver string) (bool, error) { 629 return false, nil 630 } 631 632 // getDriverMode returns the driver mode for the specified spec: {persistent|ephemeral}. 633 // 1) If mode cannot be determined, it will default to "persistent". 634 // 2) If Mode cannot be resolved to either {persistent | ephemeral}, an error is returned 635 // See https://github.com/kubernetes/enhancements/blob/master/keps/sig-storage/20190122-csi-inline-volumes.md 636 func (p *csiPlugin) getDriverMode(spec *volume.Spec) (driverMode, error) { 637 // TODO (vladimirvivien) ultimately, mode will be retrieved from CSIDriver.Spec.Mode. 638 // However, in alpha version, mode is determined by the volume source: 639 // 1) if volume.Spec.Volume.CSI != nil -> mode is ephemeral 640 // 2) if volume.Spec.PersistentVolume.Spec.CSI != nil -> persistent 641 volSrc, _, err := getSourceFromSpec(spec) 642 if err != nil { 643 return "", err 644 } 645 646 if volSrc != nil && utilfeature.DefaultFeatureGate.Enabled(features.CSIInlineVolume) { 647 return ephemeralDriverMode, nil 648 } 649 return persistentDriverMode, nil 650 } 651 652 func (p *csiPlugin) getPublishContext(client clientset.Interface, handle, driver, nodeName string) (map[string]string, error) { 653 skip, err := p.skipAttach(driver) 654 if err != nil { 655 return nil, err 656 } 657 if skip { 658 return nil, nil 659 } 660 661 attachID := getAttachmentName(handle, driver, nodeName) 662 663 // search for attachment by VolumeAttachment.Spec.Source.PersistentVolumeName 664 attachment, err := client.StorageV1().VolumeAttachments().Get(attachID, meta.GetOptions{}) 665 if err != nil { 666 return nil, err // This err already has enough context ("VolumeAttachment xyz not found") 667 } 668 669 if attachment == nil { 670 err = errors.New("no existing VolumeAttachment found") 671 return nil, err 672 } 673 return attachment.Status.AttachmentMetadata, nil 674 } 675 676 func (p *csiPlugin) newAttacherDetacher() (*csiAttacher, error) { 677 k8s := p.host.GetKubeClient() 678 if k8s == nil { 679 klog.Error(log("unable to get kubernetes client from host")) 680 return nil, errors.New("unable to get Kubernetes client") 681 } 682 683 return &csiAttacher{ 684 plugin: p, 685 k8s: k8s, 686 waitSleepTime: 1 * time.Second, 687 }, nil 688 } 689 690 // NewController returns a csi controller instance 691 func NewController() *Controller { 692 return &Controller{} 693 } 694 695 func unregisterDriver(driverName string) error { 696 csiDrivers.Delete(driverName) 697 698 if err := nim.UninstallCSIDriver(driverName); err != nil { 699 klog.Errorf("Error uninstalling CSI driver: %v", err) 700 return err 701 } 702 703 return nil 704 } 705 706 // Return the highest supported version 707 func highestSupportedVersion(versions []string) (*utilversion.Version, error) { 708 if len(versions) == 0 { 709 return nil, fmt.Errorf("CSI driver reporting empty array for supported versions") 710 } 711 712 // Sort by lowest to highest version 713 sort.Slice(versions, func(i, j int) bool { 714 parsedVersionI, err := utilversion.ParseGeneric(versions[i]) 715 if err != nil { 716 // Push bad values to the bottom 717 return true 718 } 719 720 parsedVersionJ, err := utilversion.ParseGeneric(versions[j]) 721 if err != nil { 722 // Push bad values to the bottom 723 return false 724 } 725 726 return parsedVersionI.LessThan(parsedVersionJ) 727 }) 728 729 for i := len(versions) - 1; i >= 0; i-- { 730 highestSupportedVersion, err := utilversion.ParseGeneric(versions[i]) 731 if err != nil { 732 return nil, err 733 } 734 735 if highestSupportedVersion.Major() <= 1 { 736 return highestSupportedVersion, nil 737 } 738 } 739 740 return nil, fmt.Errorf("None of the CSI versions reported by this driver are supported") 741 } 742 743 // Only drivers that implement CSI 0.x are allowed to use deprecated socket dir. 744 func isDeprecatedSocketDirAllowed(versions []string) bool { 745 for _, version := range versions { 746 if isV0Version(version) { 747 return true 748 } 749 } 750 751 return false 752 } 753 754 func isV0Version(version string) bool { 755 parsedVersion, err := utilversion.ParseGeneric(version) 756 if err != nil { 757 return false 758 } 759 760 return parsedVersion.Major() == 0 761 }