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  }