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