k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/pkg/volume/iscsi/iscsi.go (about) 1 /* 2 Copyright 2015 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 iscsi 18 19 import ( 20 "context" 21 "fmt" 22 "os" 23 "path/filepath" 24 "strconv" 25 "strings" 26 27 utilfeature "k8s.io/apiserver/pkg/util/feature" 28 "k8s.io/klog/v2" 29 "k8s.io/kubernetes/pkg/features" 30 "k8s.io/mount-utils" 31 utilexec "k8s.io/utils/exec" 32 "k8s.io/utils/io" 33 "k8s.io/utils/keymutex" 34 utilstrings "k8s.io/utils/strings" 35 36 v1 "k8s.io/api/core/v1" 37 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 38 "k8s.io/apimachinery/pkg/types" 39 "k8s.io/kubernetes/pkg/volume" 40 ioutil "k8s.io/kubernetes/pkg/volume/util" 41 "k8s.io/kubernetes/pkg/volume/util/volumepathhandler" 42 ) 43 44 // ProbeVolumePlugins is the primary entrypoint for volume plugins. 45 func ProbeVolumePlugins() []volume.VolumePlugin { 46 return []volume.VolumePlugin{&iscsiPlugin{}} 47 } 48 49 type iscsiPlugin struct { 50 host volume.VolumeHost 51 targetLocks keymutex.KeyMutex 52 } 53 54 var _ volume.VolumePlugin = &iscsiPlugin{} 55 var _ volume.PersistentVolumePlugin = &iscsiPlugin{} 56 var _ volume.BlockVolumePlugin = &iscsiPlugin{} 57 58 const ( 59 iscsiPluginName = "kubernetes.io/iscsi" 60 ) 61 62 func (plugin *iscsiPlugin) Init(host volume.VolumeHost) error { 63 plugin.host = host 64 plugin.targetLocks = keymutex.NewHashed(0) 65 return nil 66 } 67 68 func (plugin *iscsiPlugin) GetPluginName() string { 69 return iscsiPluginName 70 } 71 72 func (plugin *iscsiPlugin) GetVolumeName(spec *volume.Spec) (string, error) { 73 tp, _, iqn, lun, err := getISCSITargetInfo(spec) 74 if err != nil { 75 return "", err 76 } 77 78 return fmt.Sprintf("%v:%v:%v", tp, iqn, lun), nil 79 } 80 81 func (plugin *iscsiPlugin) CanSupport(spec *volume.Spec) bool { 82 return (spec.Volume != nil && spec.Volume.ISCSI != nil) || (spec.PersistentVolume != nil && spec.PersistentVolume.Spec.ISCSI != nil) 83 } 84 85 func (plugin *iscsiPlugin) RequiresRemount(spec *volume.Spec) bool { 86 return false 87 } 88 89 func (plugin *iscsiPlugin) SupportsMountOption() bool { 90 return true 91 } 92 93 func (plugin *iscsiPlugin) SupportsSELinuxContextMount(spec *volume.Spec) (bool, error) { 94 return true, nil 95 } 96 97 func (plugin *iscsiPlugin) GetAccessModes() []v1.PersistentVolumeAccessMode { 98 return []v1.PersistentVolumeAccessMode{ 99 v1.ReadWriteOnce, 100 v1.ReadOnlyMany, 101 } 102 } 103 104 func (plugin *iscsiPlugin) NewMounter(spec *volume.Spec, pod *v1.Pod, _ volume.VolumeOptions) (volume.Mounter, error) { 105 if pod == nil { 106 return nil, fmt.Errorf("nil pod") 107 } 108 secret, err := createSecretMap(spec, plugin, pod.Namespace) 109 if err != nil { 110 return nil, err 111 } 112 return plugin.newMounterInternal(spec, pod.UID, &ISCSIUtil{}, plugin.host.GetMounter(plugin.GetPluginName()), plugin.host.GetExec(plugin.GetPluginName()), secret) 113 } 114 115 func (plugin *iscsiPlugin) newMounterInternal(spec *volume.Spec, podUID types.UID, manager diskManager, mounter mount.Interface, exec utilexec.Interface, secret map[string]string) (volume.Mounter, error) { 116 readOnly, fsType, err := getISCSIVolumeInfo(spec) 117 if err != nil { 118 return nil, err 119 } 120 iscsiDisk, err := createISCSIDisk(spec, podUID, plugin, manager, secret) 121 if err != nil { 122 return nil, err 123 } 124 125 if iscsiDisk != nil { 126 127 //Add volume metrics 128 iscsiDisk.MetricsProvider = volume.NewMetricsStatFS(iscsiDisk.GetPath()) 129 } 130 return &iscsiDiskMounter{ 131 iscsiDisk: iscsiDisk, 132 fsType: fsType, 133 readOnly: readOnly, 134 mounter: &mount.SafeFormatAndMount{Interface: mounter, Exec: exec}, 135 exec: exec, 136 deviceUtil: ioutil.NewDeviceHandler(ioutil.NewIOHandler()), 137 mountOptions: ioutil.MountOptionFromSpec(spec), 138 }, nil 139 } 140 141 // NewBlockVolumeMapper creates a new volume.BlockVolumeMapper from an API specification. 142 func (plugin *iscsiPlugin) NewBlockVolumeMapper(spec *volume.Spec, pod *v1.Pod, _ volume.VolumeOptions) (volume.BlockVolumeMapper, error) { 143 // If this is called via GenerateUnmapDeviceFunc(), pod is nil. 144 // Pass empty string as dummy uid since uid isn't used in the case. 145 var uid types.UID 146 var secret map[string]string 147 var err error 148 if pod != nil { 149 uid = pod.UID 150 secret, err = createSecretMap(spec, plugin, pod.Namespace) 151 if err != nil { 152 return nil, err 153 } 154 } 155 return plugin.newBlockVolumeMapperInternal(spec, uid, &ISCSIUtil{}, plugin.host.GetMounter(plugin.GetPluginName()), plugin.host.GetExec(plugin.GetPluginName()), secret) 156 } 157 158 func (plugin *iscsiPlugin) newBlockVolumeMapperInternal(spec *volume.Spec, podUID types.UID, manager diskManager, mounter mount.Interface, exec utilexec.Interface, secret map[string]string) (volume.BlockVolumeMapper, error) { 159 readOnly, _, err := getISCSIVolumeInfo(spec) 160 if err != nil { 161 return nil, err 162 } 163 iscsiDisk, err := createISCSIDisk(spec, podUID, plugin, manager, secret) 164 if err != nil { 165 return nil, err 166 } 167 mapper := &iscsiDiskMapper{ 168 iscsiDisk: iscsiDisk, 169 readOnly: readOnly, 170 exec: exec, 171 deviceUtil: ioutil.NewDeviceHandler(ioutil.NewIOHandler()), 172 } 173 174 blockPath, err := mapper.GetGlobalMapPath(spec) 175 if err != nil { 176 return nil, fmt.Errorf("failed to get device path: %v", err) 177 } 178 mapper.MetricsProvider = volume.NewMetricsBlock(filepath.Join(blockPath, string(podUID))) 179 180 return mapper, nil 181 } 182 183 func (plugin *iscsiPlugin) NewUnmounter(volName string, podUID types.UID) (volume.Unmounter, error) { 184 return plugin.newUnmounterInternal(volName, podUID, &ISCSIUtil{}, plugin.host.GetMounter(plugin.GetPluginName()), plugin.host.GetExec(plugin.GetPluginName())) 185 } 186 187 func (plugin *iscsiPlugin) newUnmounterInternal(volName string, podUID types.UID, manager diskManager, mounter mount.Interface, exec utilexec.Interface) (volume.Unmounter, error) { 188 return &iscsiDiskUnmounter{ 189 iscsiDisk: &iscsiDisk{ 190 podUID: podUID, 191 VolName: volName, 192 manager: manager, 193 plugin: plugin, 194 MetricsProvider: volume.NewMetricsStatFS(plugin.host.GetPodVolumeDir(podUID, utilstrings.EscapeQualifiedName(iscsiPluginName), volName)), 195 }, 196 mounter: mounter, 197 exec: exec, 198 deviceUtil: ioutil.NewDeviceHandler(ioutil.NewIOHandler()), 199 }, nil 200 } 201 202 // NewBlockVolumeUnmapper creates a new volume.BlockVolumeUnmapper from recoverable state. 203 func (plugin *iscsiPlugin) NewBlockVolumeUnmapper(volName string, podUID types.UID) (volume.BlockVolumeUnmapper, error) { 204 return plugin.newUnmapperInternal(volName, podUID, &ISCSIUtil{}, plugin.host.GetExec(plugin.GetPluginName())) 205 } 206 207 func (plugin *iscsiPlugin) newUnmapperInternal(volName string, podUID types.UID, manager diskManager, exec utilexec.Interface) (volume.BlockVolumeUnmapper, error) { 208 return &iscsiDiskUnmapper{ 209 iscsiDisk: &iscsiDisk{ 210 podUID: podUID, 211 VolName: volName, 212 manager: manager, 213 plugin: plugin, 214 }, 215 exec: exec, 216 deviceUtil: ioutil.NewDeviceHandler(ioutil.NewIOHandler()), 217 }, nil 218 } 219 220 func (plugin *iscsiPlugin) ConstructVolumeSpec(volumeName, mountPath string) (volume.ReconstructedVolume, error) { 221 // Find globalPDPath from pod volume directory(mountPath) 222 var globalPDPath string 223 mounter := plugin.host.GetMounter(plugin.GetPluginName()) 224 // Try really hard to get the global mount of the volume, an error returned from here would 225 // leave the global mount still mounted, while marking the volume as unused. 226 // The volume can then be mounted on several nodes, resulting in volume 227 // corruption. 228 paths, err := ioutil.GetReliableMountRefs(mounter, mountPath) 229 if io.IsInconsistentReadError(err) { 230 klog.Errorf("Failed to read mount refs from /proc/mounts for %s: %s", mountPath, err) 231 klog.Errorf("Kubelet cannot unmount volume at %s, please unmount it and all mounts of the same device manually.", mountPath) 232 return volume.ReconstructedVolume{}, err 233 } 234 if err != nil { 235 return volume.ReconstructedVolume{}, err 236 } 237 238 for _, path := range paths { 239 if strings.Contains(path, plugin.host.GetPluginDir(iscsiPluginName)) { 240 globalPDPath = path 241 break 242 } 243 } 244 // Couldn't fetch globalPDPath 245 if len(globalPDPath) == 0 { 246 return volume.ReconstructedVolume{}, fmt.Errorf("couldn't fetch globalPDPath. failed to obtain volume spec") 247 } 248 249 // Obtain iscsi disk configurations from globalPDPath 250 device, _, err := extractDeviceAndPrefix(globalPDPath) 251 if err != nil { 252 return volume.ReconstructedVolume{}, err 253 } 254 bkpPortal, iqn, err := extractPortalAndIqn(device) 255 if err != nil { 256 return volume.ReconstructedVolume{}, err 257 } 258 arr := strings.Split(device, "-lun-") 259 if len(arr) < 2 { 260 return volume.ReconstructedVolume{}, fmt.Errorf("failed to retrieve lun from globalPDPath: %v", globalPDPath) 261 } 262 lun, err := strconv.Atoi(arr[1]) 263 if err != nil { 264 return volume.ReconstructedVolume{}, err 265 } 266 iface, _ := extractIface(globalPDPath) 267 iscsiVolume := &v1.Volume{ 268 Name: volumeName, 269 VolumeSource: v1.VolumeSource{ 270 ISCSI: &v1.ISCSIVolumeSource{ 271 TargetPortal: bkpPortal, 272 IQN: iqn, 273 Lun: int32(lun), 274 ISCSIInterface: iface, 275 }, 276 }, 277 } 278 279 var mountContext string 280 if utilfeature.DefaultFeatureGate.Enabled(features.SELinuxMountReadWriteOncePod) { 281 kvh, ok := plugin.host.(volume.KubeletVolumeHost) 282 if !ok { 283 return volume.ReconstructedVolume{}, fmt.Errorf("plugin volume host does not implement KubeletVolumeHost interface") 284 } 285 hu := kvh.GetHostUtil() 286 mountContext, err = hu.GetSELinuxMountContext(mountPath) 287 if err != nil { 288 return volume.ReconstructedVolume{}, err 289 } 290 } 291 292 return volume.ReconstructedVolume{ 293 Spec: volume.NewSpecFromVolume(iscsiVolume), 294 SELinuxMountContext: mountContext, 295 }, nil 296 } 297 298 func (plugin *iscsiPlugin) ConstructBlockVolumeSpec(podUID types.UID, volumeName, mapPath string) (*volume.Spec, error) { 299 pluginDir := plugin.host.GetVolumeDevicePluginDir(iscsiPluginName) 300 blkutil := volumepathhandler.NewBlockVolumePathHandler() 301 globalMapPathUUID, err := blkutil.FindGlobalMapPathUUIDFromPod(pluginDir, mapPath, podUID) 302 if err != nil { 303 return nil, err 304 } 305 klog.V(5).Infof("globalMapPathUUID: %v, err: %v", globalMapPathUUID, err) 306 // Retrieve volume information from globalMapPathUUID 307 // globalMapPathUUID example: 308 // plugins/kubernetes.io/{PluginName}/{DefaultKubeletVolumeDevicesDirName}/{volumePluginDependentPath}/{pod uuid} 309 // plugins/kubernetes.io/iscsi/volumeDevices/iface-default/192.168.0.10:3260-iqn.2017-05.com.example:test-lun-0/{pod uuid} 310 globalMapPath := filepath.Dir(globalMapPathUUID) 311 return getVolumeSpecFromGlobalMapPath(volumeName, globalMapPath) 312 } 313 314 type iscsiDisk struct { 315 VolName string 316 podUID types.UID 317 Portals []string 318 Iqn string 319 Lun string 320 InitIface string 321 Iface string 322 chapDiscovery bool 323 chapSession bool 324 secret map[string]string `datapolicy:"token"` 325 InitiatorName string 326 plugin *iscsiPlugin 327 // Utility interface that provides API calls to the provider to attach/detach disks. 328 manager diskManager 329 volume.MetricsProvider 330 } 331 332 func (iscsi *iscsiDisk) GetPath() string { 333 name := iscsiPluginName 334 // safe to use PodVolumeDir now: volume teardown occurs before pod is cleaned up 335 return iscsi.plugin.host.GetPodVolumeDir(iscsi.podUID, utilstrings.EscapeQualifiedName(name), iscsi.VolName) 336 } 337 338 func (iscsi *iscsiDisk) iscsiGlobalMapPath(spec *volume.Spec) (string, error) { 339 mounter, err := volumeSpecToMounter(spec, iscsi.plugin.host, iscsi.plugin.targetLocks, nil /* pod */) 340 if err != nil { 341 klog.Warningf("failed to get iscsi mounter: %v", err) 342 return "", err 343 } 344 return iscsi.manager.MakeGlobalVDPDName(*mounter.iscsiDisk), nil 345 } 346 347 func (iscsi *iscsiDisk) iscsiPodDeviceMapPath() (string, string) { 348 name := iscsiPluginName 349 return iscsi.plugin.host.GetPodVolumeDeviceDir(iscsi.podUID, utilstrings.EscapeQualifiedName(name)), iscsi.VolName 350 } 351 352 type iscsiDiskMounter struct { 353 *iscsiDisk 354 readOnly bool 355 fsType string 356 volumeMode v1.PersistentVolumeMode 357 mounter *mount.SafeFormatAndMount 358 exec utilexec.Interface 359 deviceUtil ioutil.DeviceUtil 360 mountOptions []string 361 mountedWithSELinuxContext bool 362 } 363 364 var _ volume.Mounter = &iscsiDiskMounter{} 365 366 func (b *iscsiDiskMounter) GetAttributes() volume.Attributes { 367 return volume.Attributes{ 368 ReadOnly: b.readOnly, 369 Managed: !b.readOnly, 370 SELinuxRelabel: !b.mountedWithSELinuxContext, 371 } 372 } 373 374 func (b *iscsiDiskMounter) SetUp(mounterArgs volume.MounterArgs) error { 375 return b.SetUpAt(b.GetPath(), mounterArgs) 376 } 377 378 func (b *iscsiDiskMounter) SetUpAt(dir string, mounterArgs volume.MounterArgs) error { 379 // diskSetUp checks mountpoints and prevent repeated calls 380 err := diskSetUp(b.manager, *b, dir, b.mounter, mounterArgs.FsGroup, mounterArgs.FSGroupChangePolicy) 381 if err != nil { 382 klog.Errorf("iscsi: failed to setup") 383 } 384 385 if utilfeature.DefaultFeatureGate.Enabled(features.SELinuxMountReadWriteOncePod) { 386 // The volume must have been mounted in MountDevice with -o context. 387 // TODO: extract from mount table in GetAttributes() to be sure? 388 b.mountedWithSELinuxContext = mounterArgs.SELinuxLabel != "" 389 } 390 return err 391 } 392 393 type iscsiDiskUnmounter struct { 394 *iscsiDisk 395 mounter mount.Interface 396 exec utilexec.Interface 397 deviceUtil ioutil.DeviceUtil 398 } 399 400 var _ volume.Unmounter = &iscsiDiskUnmounter{} 401 402 // Unmounts the bind mount, and detaches the disk only if the disk 403 // resource was the last reference to that disk on the kubelet. 404 func (c *iscsiDiskUnmounter) TearDown() error { 405 return c.TearDownAt(c.GetPath()) 406 } 407 408 func (c *iscsiDiskUnmounter) TearDownAt(dir string) error { 409 return mount.CleanupMountPoint(dir, c.mounter, false) 410 } 411 412 // Block Volumes Support 413 type iscsiDiskMapper struct { 414 *iscsiDisk 415 readOnly bool 416 exec utilexec.Interface 417 deviceUtil ioutil.DeviceUtil 418 } 419 420 var _ volume.BlockVolumeMapper = &iscsiDiskMapper{} 421 422 type iscsiDiskUnmapper struct { 423 *iscsiDisk 424 exec utilexec.Interface 425 deviceUtil ioutil.DeviceUtil 426 volume.MetricsNil 427 } 428 429 // SupportsMetrics returns true for SupportsMetrics as it initializes the 430 // MetricsProvider. 431 func (idm *iscsiDiskMapper) SupportsMetrics() bool { 432 return true 433 } 434 435 var _ volume.BlockVolumeUnmapper = &iscsiDiskUnmapper{} 436 var _ volume.CustomBlockVolumeUnmapper = &iscsiDiskUnmapper{} 437 438 // Even though iSCSI plugin has attacher/detacher implementation, iSCSI plugin 439 // needs volume detach operation during TearDownDevice(). This method is only 440 // chance that operations are done on kubelet node during volume teardown sequences. 441 func (c *iscsiDiskUnmapper) TearDownDevice(mapPath, _ string) error { 442 err := c.manager.DetachBlockISCSIDisk(*c, mapPath) 443 if err != nil { 444 return fmt.Errorf("iscsi: failed to detach disk: %s\nError: %v", mapPath, err) 445 } 446 klog.V(4).Infof("iscsi: %q is unmounted, deleting the directory", mapPath) 447 err = os.RemoveAll(mapPath) 448 if err != nil { 449 return fmt.Errorf("iscsi: failed to delete the directory: %s\nError: %v", mapPath, err) 450 } 451 klog.V(4).Infof("iscsi: successfully detached disk: %s", mapPath) 452 return nil 453 } 454 455 func (c *iscsiDiskUnmapper) UnmapPodDevice() error { 456 return nil 457 } 458 459 // GetGlobalMapPath returns global map path and error 460 // path: plugins/kubernetes.io/{PluginName}/volumeDevices/{ifaceName}/{portal-some_iqn-lun-lun_id} 461 func (iscsi *iscsiDisk) GetGlobalMapPath(spec *volume.Spec) (string, error) { 462 return iscsi.iscsiGlobalMapPath(spec) 463 } 464 465 // GetPodDeviceMapPath returns pod device map path and volume name 466 // path: pods/{podUid}/volumeDevices/kubernetes.io~iscsi 467 // volumeName: pv0001 468 func (iscsi *iscsiDisk) GetPodDeviceMapPath() (string, string) { 469 return iscsi.iscsiPodDeviceMapPath() 470 } 471 472 func portalMounter(portal string) string { 473 if !strings.Contains(portal, ":") { 474 portal = portal + ":3260" 475 } 476 return portal 477 } 478 479 // get iSCSI volume info: readOnly and fstype 480 func getISCSIVolumeInfo(spec *volume.Spec) (bool, string, error) { 481 // for volume source, readonly is in volume spec 482 // for PV, readonly is in PV spec. PV gets the ReadOnly flag indirectly through the PVC source 483 if spec.Volume != nil && spec.Volume.ISCSI != nil { 484 return spec.Volume.ISCSI.ReadOnly, spec.Volume.ISCSI.FSType, nil 485 } else if spec.PersistentVolume != nil && 486 spec.PersistentVolume.Spec.ISCSI != nil { 487 return spec.ReadOnly, spec.PersistentVolume.Spec.ISCSI.FSType, nil 488 } 489 490 return false, "", fmt.Errorf("Spec does not reference an ISCSI volume type") 491 } 492 493 // get iSCSI target info: target portal, portals, iqn, and lun 494 func getISCSITargetInfo(spec *volume.Spec) (string, []string, string, int32, error) { 495 if spec.Volume != nil && spec.Volume.ISCSI != nil { 496 return spec.Volume.ISCSI.TargetPortal, spec.Volume.ISCSI.Portals, spec.Volume.ISCSI.IQN, spec.Volume.ISCSI.Lun, nil 497 } else if spec.PersistentVolume != nil && 498 spec.PersistentVolume.Spec.ISCSI != nil { 499 return spec.PersistentVolume.Spec.ISCSI.TargetPortal, spec.PersistentVolume.Spec.ISCSI.Portals, spec.PersistentVolume.Spec.ISCSI.IQN, spec.PersistentVolume.Spec.ISCSI.Lun, nil 500 } 501 502 return "", nil, "", 0, fmt.Errorf("Spec does not reference an ISCSI volume type") 503 } 504 505 // get iSCSI initiator info: iface and initiator name 506 func getISCSIInitiatorInfo(spec *volume.Spec) (string, *string, error) { 507 if spec.Volume != nil && spec.Volume.ISCSI != nil { 508 return spec.Volume.ISCSI.ISCSIInterface, spec.Volume.ISCSI.InitiatorName, nil 509 } else if spec.PersistentVolume != nil && 510 spec.PersistentVolume.Spec.ISCSI != nil { 511 return spec.PersistentVolume.Spec.ISCSI.ISCSIInterface, spec.PersistentVolume.Spec.ISCSI.InitiatorName, nil 512 } 513 514 return "", nil, fmt.Errorf("Spec does not reference an ISCSI volume type") 515 } 516 517 // get iSCSI Discovery CHAP boolean 518 func getISCSIDiscoveryCHAPInfo(spec *volume.Spec) (bool, error) { 519 if spec.Volume != nil && spec.Volume.ISCSI != nil { 520 return spec.Volume.ISCSI.DiscoveryCHAPAuth, nil 521 } else if spec.PersistentVolume != nil && 522 spec.PersistentVolume.Spec.ISCSI != nil { 523 return spec.PersistentVolume.Spec.ISCSI.DiscoveryCHAPAuth, nil 524 } 525 526 return false, fmt.Errorf("Spec does not reference an ISCSI volume type") 527 } 528 529 // get iSCSI Session CHAP boolean 530 func getISCSISessionCHAPInfo(spec *volume.Spec) (bool, error) { 531 if spec.Volume != nil && spec.Volume.ISCSI != nil { 532 return spec.Volume.ISCSI.SessionCHAPAuth, nil 533 } else if spec.PersistentVolume != nil && 534 spec.PersistentVolume.Spec.ISCSI != nil { 535 return spec.PersistentVolume.Spec.ISCSI.SessionCHAPAuth, nil 536 } 537 538 return false, fmt.Errorf("Spec does not reference an ISCSI volume type") 539 } 540 541 // get iSCSI CHAP Secret info: secret name and namespace 542 func getISCSISecretNameAndNamespace(spec *volume.Spec, defaultSecretNamespace string) (string, string, error) { 543 if spec.Volume != nil && spec.Volume.ISCSI != nil { 544 if spec.Volume.ISCSI.SecretRef != nil { 545 return spec.Volume.ISCSI.SecretRef.Name, defaultSecretNamespace, nil 546 } 547 return "", "", nil 548 } else if spec.PersistentVolume != nil && 549 spec.PersistentVolume.Spec.ISCSI != nil { 550 secretRef := spec.PersistentVolume.Spec.ISCSI.SecretRef 551 secretNs := defaultSecretNamespace 552 if secretRef != nil { 553 if len(secretRef.Namespace) != 0 { 554 secretNs = secretRef.Namespace 555 } 556 return secretRef.Name, secretNs, nil 557 } 558 return "", "", nil 559 } 560 561 return "", "", fmt.Errorf("Spec does not reference an ISCSI volume type") 562 } 563 564 func createISCSIDisk(spec *volume.Spec, podUID types.UID, plugin *iscsiPlugin, manager diskManager, secret map[string]string) (*iscsiDisk, error) { 565 tp, portals, iqn, lunStr, err := getISCSITargetInfo(spec) 566 if err != nil { 567 return nil, err 568 } 569 570 lun := strconv.Itoa(int(lunStr)) 571 portal := portalMounter(tp) 572 var bkportal []string 573 bkportal = append(bkportal, portal) 574 for _, p := range portals { 575 bkportal = append(bkportal, portalMounter(string(p))) 576 } 577 578 iface, initiatorNamePtr, err := getISCSIInitiatorInfo(spec) 579 if err != nil { 580 return nil, err 581 } 582 583 var initiatorName string 584 if initiatorNamePtr != nil { 585 initiatorName = *initiatorNamePtr 586 } 587 chapDiscovery, err := getISCSIDiscoveryCHAPInfo(spec) 588 if err != nil { 589 return nil, err 590 } 591 chapSession, err := getISCSISessionCHAPInfo(spec) 592 if err != nil { 593 return nil, err 594 } 595 596 initIface := iface 597 if initiatorName != "" { 598 iface = bkportal[0] + ":" + spec.Name() 599 } 600 601 return &iscsiDisk{ 602 podUID: podUID, 603 VolName: spec.Name(), 604 Portals: bkportal, 605 Iqn: iqn, 606 Lun: lun, 607 InitIface: initIface, 608 Iface: iface, 609 chapDiscovery: chapDiscovery, 610 chapSession: chapSession, 611 secret: secret, 612 InitiatorName: initiatorName, 613 manager: manager, 614 plugin: plugin}, nil 615 } 616 617 func createSecretMap(spec *volume.Spec, plugin *iscsiPlugin, namespace string) (map[string]string, error) { 618 var secret map[string]string 619 chapDiscover, err := getISCSIDiscoveryCHAPInfo(spec) 620 if err != nil { 621 return nil, err 622 } 623 chapSession, err := getISCSISessionCHAPInfo(spec) 624 if err != nil { 625 return nil, err 626 } 627 if chapDiscover || chapSession { 628 secretName, secretNamespace, err := getISCSISecretNameAndNamespace(spec, namespace) 629 if err != nil { 630 return nil, err 631 } 632 633 if len(secretName) > 0 && len(secretNamespace) > 0 { 634 // if secret is provideded, retrieve it 635 kubeClient := plugin.host.GetKubeClient() 636 if kubeClient == nil { 637 return nil, fmt.Errorf("cannot get kube client") 638 } 639 secretObj, err := kubeClient.CoreV1().Secrets(secretNamespace).Get(context.TODO(), secretName, metav1.GetOptions{}) 640 if err != nil { 641 err = fmt.Errorf("couldn't get secret %v/%v error: %w", secretNamespace, secretName, err) 642 return nil, err 643 } 644 secret = make(map[string]string) 645 for name, data := range secretObj.Data { 646 klog.V(4).Infof("retrieving CHAP secret name: %s", name) 647 secret[name] = string(data) 648 } 649 } 650 } 651 return secret, err 652 } 653 654 func createPersistentVolumeFromISCSIPVSource(volumeName string, iscsi v1.ISCSIPersistentVolumeSource) *v1.PersistentVolume { 655 block := v1.PersistentVolumeBlock 656 return &v1.PersistentVolume{ 657 ObjectMeta: metav1.ObjectMeta{ 658 Name: volumeName, 659 }, 660 Spec: v1.PersistentVolumeSpec{ 661 PersistentVolumeSource: v1.PersistentVolumeSource{ 662 ISCSI: &iscsi, 663 }, 664 VolumeMode: &block, 665 }, 666 } 667 } 668 669 func getVolumeSpecFromGlobalMapPath(volumeName, globalMapPath string) (*volume.Spec, error) { 670 // Retrieve volume spec information from globalMapPath 671 // globalMapPath example: 672 // plugins/kubernetes.io/{PluginName}/{DefaultKubeletVolumeDevicesDirName}/{volumePluginDependentPath} 673 // plugins/kubernetes.io/iscsi/volumeDevices/iface-default/192.168.0.10:3260-iqn.2017-05.com.example:test-lun-0 674 675 // device: 192.168.0.10:3260-iqn.2017-05.com.example:test-lun-0 676 device, _, err := extractDeviceAndPrefix(globalMapPath) 677 if err != nil { 678 return nil, err 679 } 680 bkpPortal, iqn, err := extractPortalAndIqn(device) 681 if err != nil { 682 return nil, err 683 } 684 arr := strings.Split(device, "-lun-") 685 if len(arr) < 2 { 686 return nil, fmt.Errorf("failed to retrieve lun from globalMapPath: %v", globalMapPath) 687 } 688 lun, err := strconv.Atoi(arr[1]) 689 if err != nil { 690 return nil, err 691 } 692 iface, found := extractIface(globalMapPath) 693 if !found { 694 return nil, fmt.Errorf("failed to retrieve iface from globalMapPath: %v", globalMapPath) 695 } 696 iscsiPV := createPersistentVolumeFromISCSIPVSource(volumeName, 697 v1.ISCSIPersistentVolumeSource{ 698 TargetPortal: bkpPortal, 699 IQN: iqn, 700 Lun: int32(lun), 701 ISCSIInterface: iface, 702 }, 703 ) 704 klog.V(5).Infof("ConstructBlockVolumeSpec: TargetPortal: %v, IQN: %v, Lun: %v, ISCSIInterface: %v", 705 iscsiPV.Spec.PersistentVolumeSource.ISCSI.TargetPortal, 706 iscsiPV.Spec.PersistentVolumeSource.ISCSI.IQN, 707 iscsiPV.Spec.PersistentVolumeSource.ISCSI.Lun, 708 iscsiPV.Spec.PersistentVolumeSource.ISCSI.ISCSIInterface, 709 ) 710 return volume.NewSpecFromPersistentVolume(iscsiPV, false), nil 711 }