...

Source file src/k8s.io/kubernetes/pkg/volume/iscsi/iscsi.go

Documentation: k8s.io/kubernetes/pkg/volume/iscsi

     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  }
   716  

View as plain text