mirror of https://github.com/k3s-io/k3s
use SecretObject to reference iSCSI CHAP secret
Signed-off-by: Huamin Chen <hchen@redhat.com>pull/6/head
parent
02f803cc02
commit
bb34a0b7ef
|
@ -88,9 +88,17 @@ func VisitPVSecretNames(pv *api.PersistentVolume, visitor Visitor) bool {
|
|||
}
|
||||
}
|
||||
case source.ISCSI != nil:
|
||||
if source.ISCSI.SecretRef != nil && !visitor(getClaimRefNamespace(pv), source.ISCSI.SecretRef.Name) {
|
||||
if source.ISCSI.SecretRef != nil {
|
||||
// previously persisted PV objects use claimRef namespace
|
||||
ns := getClaimRefNamespace(pv)
|
||||
if len(source.ISCSI.SecretRef.Namespace) > 0 {
|
||||
// use the secret namespace if namespace is set
|
||||
ns = source.ISCSI.SecretRef.Namespace
|
||||
}
|
||||
if !visitor(ns, source.ISCSI.SecretRef.Name) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
case source.StorageOS != nil:
|
||||
if source.StorageOS.SecretRef != nil && !visitor(source.StorageOS.SecretRef.Namespace, source.StorageOS.SecretRef.Name) {
|
||||
return false
|
||||
|
|
|
@ -93,8 +93,15 @@ func TestPVSecrets(t *testing.T) {
|
|||
{Spec: api.PersistentVolumeSpec{
|
||||
ClaimRef: &api.ObjectReference{Namespace: "claimrefns", Name: "claimrefname"},
|
||||
PersistentVolumeSource: api.PersistentVolumeSource{
|
||||
ISCSI: &api.ISCSIVolumeSource{
|
||||
SecretRef: &api.LocalObjectReference{
|
||||
ISCSI: &api.ISCSIPersistentVolumeSource{
|
||||
SecretRef: &api.SecretReference{
|
||||
Name: "Spec.PersistentVolumeSource.ISCSI.SecretRef",
|
||||
Namespace: "iscsi"}}}}},
|
||||
{Spec: api.PersistentVolumeSpec{
|
||||
ClaimRef: &api.ObjectReference{Namespace: "claimrefns", Name: "claimrefname"},
|
||||
PersistentVolumeSource: api.PersistentVolumeSource{
|
||||
ISCSI: &api.ISCSIPersistentVolumeSource{
|
||||
SecretRef: &api.SecretReference{
|
||||
Name: "Spec.PersistentVolumeSource.ISCSI.SecretRef"}}}}},
|
||||
{Spec: api.PersistentVolumeSpec{
|
||||
ClaimRef: &api.ObjectReference{Namespace: "claimrefns", Name: "claimrefname"},
|
||||
|
@ -161,6 +168,7 @@ func TestPVSecrets(t *testing.T) {
|
|||
"claimrefns/Spec.PersistentVolumeSource.ScaleIO.SecretRef",
|
||||
"scaleions/Spec.PersistentVolumeSource.ScaleIO.SecretRef",
|
||||
"claimrefns/Spec.PersistentVolumeSource.ISCSI.SecretRef",
|
||||
"iscsi/Spec.PersistentVolumeSource.ISCSI.SecretRef",
|
||||
"storageosns/Spec.PersistentVolumeSource.StorageOS.SecretRef",
|
||||
)
|
||||
if missingNames := expectedNamespacedNames.Difference(extractedNamesWithNamespace); len(missingNames) > 0 {
|
||||
|
|
|
@ -245,6 +245,12 @@ var Funcs = func(codecs runtimeserializer.CodecFactory) []interface{} {
|
|||
i.ISCSIInterface = "default"
|
||||
}
|
||||
},
|
||||
func(i *core.ISCSIPersistentVolumeSource, c fuzz.Continue) {
|
||||
i.ISCSIInterface = c.RandString()
|
||||
if i.ISCSIInterface == "" {
|
||||
i.ISCSIInterface = "default"
|
||||
}
|
||||
},
|
||||
func(d *core.DNSPolicy, c fuzz.Continue) {
|
||||
policies := []core.DNSPolicy{core.DNSClusterFirst, core.DNSDefault}
|
||||
*d = policies[c.Rand.Intn(len(policies))]
|
||||
|
|
|
@ -347,10 +347,10 @@ type PersistentVolumeSource struct {
|
|||
// Quobyte represents a Quobyte mount on the host that shares a pod's lifetime
|
||||
// +optional
|
||||
Quobyte *QuobyteVolumeSource
|
||||
// ISCSIVolumeSource represents an ISCSI resource that is attached to a
|
||||
// ISCSIPersistentVolumeSource represents an ISCSI resource that is attached to a
|
||||
// kubelet's host machine and then exposed to the pod.
|
||||
// +optional
|
||||
ISCSI *ISCSIVolumeSource
|
||||
ISCSI *ISCSIPersistentVolumeSource
|
||||
// FlexVolume represents a generic volume resource that is
|
||||
// provisioned/attached using an exec based plugin. This is an alpha feature and may change in future.
|
||||
// +optional
|
||||
|
@ -793,6 +793,54 @@ type ISCSIVolumeSource struct {
|
|||
InitiatorName *string
|
||||
}
|
||||
|
||||
// ISCSIPersistentVolumeSource represents an ISCSI disk.
|
||||
// ISCSI volumes can only be mounted as read/write once.
|
||||
// ISCSI volumes support ownership management and SELinux relabeling.
|
||||
type ISCSIPersistentVolumeSource struct {
|
||||
// Required: iSCSI target portal
|
||||
// the portal is either an IP or ip_addr:port if port is other than default (typically TCP ports 860 and 3260)
|
||||
// +optional
|
||||
TargetPortal string
|
||||
// Required: target iSCSI Qualified Name
|
||||
// +optional
|
||||
IQN string
|
||||
// Required: iSCSI target lun number
|
||||
// +optional
|
||||
Lun int32
|
||||
// Optional: Defaults to 'default' (tcp). iSCSI interface name that uses an iSCSI transport.
|
||||
// +optional
|
||||
ISCSIInterface string
|
||||
// Filesystem type to mount.
|
||||
// Must be a filesystem type supported by the host operating system.
|
||||
// Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified.
|
||||
// TODO: how do we prevent errors in the filesystem from compromising the machine
|
||||
// +optional
|
||||
FSType string
|
||||
// Optional: Defaults to false (read/write). ReadOnly here will force
|
||||
// the ReadOnly setting in VolumeMounts.
|
||||
// +optional
|
||||
ReadOnly bool
|
||||
// Optional: list of iSCSI target portal ips for high availability.
|
||||
// the portal is either an IP or ip_addr:port if port is other than default (typically TCP ports 860 and 3260)
|
||||
// +optional
|
||||
Portals []string
|
||||
// Optional: whether support iSCSI Discovery CHAP authentication
|
||||
// +optional
|
||||
DiscoveryCHAPAuth bool
|
||||
// Optional: whether support iSCSI Session CHAP authentication
|
||||
// +optional
|
||||
SessionCHAPAuth bool
|
||||
// Optional: CHAP secret for iSCSI target and initiator authentication.
|
||||
// The secret is used if either DiscoveryCHAPAuth or SessionCHAPAuth is true
|
||||
// +optional
|
||||
SecretRef *SecretReference
|
||||
// Optional: Custom initiator name per volume.
|
||||
// If initiatorName is specified with iscsiInterface simultaneously, new iSCSI interface
|
||||
// <target portal>:<volume name> will be created for the connection.
|
||||
// +optional
|
||||
InitiatorName *string
|
||||
}
|
||||
|
||||
// Represents a Fibre Channel volume.
|
||||
// Fibre Channel volumes can only be mounted as read/write once.
|
||||
// Fibre Channel volumes support ownership management and SELinux relabeling.
|
||||
|
|
|
@ -249,6 +249,11 @@ func SetDefaults_ISCSIVolumeSource(obj *v1.ISCSIVolumeSource) {
|
|||
obj.ISCSIInterface = "default"
|
||||
}
|
||||
}
|
||||
func SetDefaults_ISCSIPersistentVolumeSource(obj *v1.ISCSIPersistentVolumeSource) {
|
||||
if obj.ISCSIInterface == "" {
|
||||
obj.ISCSIInterface = "default"
|
||||
}
|
||||
}
|
||||
func SetDefaults_AzureDiskVolumeSource(obj *v1.AzureDiskVolumeSource) {
|
||||
if obj.CachingMode == nil {
|
||||
obj.CachingMode = new(v1.AzureDataDiskCachingMode)
|
||||
|
|
|
@ -712,6 +712,46 @@ func validateGitRepoVolumeSource(gitRepo *core.GitRepoVolumeSource, fldPath *fie
|
|||
}
|
||||
|
||||
func validateISCSIVolumeSource(iscsi *core.ISCSIVolumeSource, fldPath *field.Path) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
if len(iscsi.TargetPortal) == 0 {
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("targetPortal"), ""))
|
||||
}
|
||||
if len(iscsi.IQN) == 0 {
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("iqn"), ""))
|
||||
} else {
|
||||
if !strings.HasPrefix(iscsi.IQN, "iqn") && !strings.HasPrefix(iscsi.IQN, "eui") && !strings.HasPrefix(iscsi.IQN, "naa") {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("iqn"), iscsi.IQN, "must be valid format starting with iqn, eui, or naa"))
|
||||
} else if strings.HasPrefix(iscsi.IQN, "iqn") && !iscsiInitiatorIqnRegex.MatchString(iscsi.IQN) {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("iqn"), iscsi.IQN, "must be valid format"))
|
||||
} else if strings.HasPrefix(iscsi.IQN, "eui") && !iscsiInitiatorEuiRegex.MatchString(iscsi.IQN) {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("iqn"), iscsi.IQN, "must be valid format"))
|
||||
} else if strings.HasPrefix(iscsi.IQN, "naa") && !iscsiInitiatorNaaRegex.MatchString(iscsi.IQN) {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("iqn"), iscsi.IQN, "must be valid format"))
|
||||
}
|
||||
}
|
||||
if iscsi.Lun < 0 || iscsi.Lun > 255 {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("lun"), iscsi.Lun, validation.InclusiveRangeError(0, 255)))
|
||||
}
|
||||
if (iscsi.DiscoveryCHAPAuth || iscsi.SessionCHAPAuth) && iscsi.SecretRef == nil {
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("secretRef"), ""))
|
||||
}
|
||||
if iscsi.InitiatorName != nil {
|
||||
initiator := *iscsi.InitiatorName
|
||||
if !strings.HasPrefix(initiator, "iqn") && !strings.HasPrefix(initiator, "eui") && !strings.HasPrefix(initiator, "naa") {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("initiatorname"), initiator, "must be valid format starting with iqn, eui, or naa"))
|
||||
}
|
||||
if strings.HasPrefix(initiator, "iqn") && !iscsiInitiatorIqnRegex.MatchString(initiator) {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("initiatorname"), initiator, "must be valid format"))
|
||||
} else if strings.HasPrefix(initiator, "eui") && !iscsiInitiatorEuiRegex.MatchString(initiator) {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("initiatorname"), initiator, "must be valid format"))
|
||||
} else if strings.HasPrefix(initiator, "naa") && !iscsiInitiatorNaaRegex.MatchString(initiator) {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("initiatorname"), initiator, "must be valid format"))
|
||||
}
|
||||
}
|
||||
return allErrs
|
||||
}
|
||||
|
||||
func validateISCSIPersistentVolumeSource(iscsi *core.ISCSIPersistentVolumeSource, fldPath *field.Path) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
if len(iscsi.TargetPortal) == 0 {
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("targetPortal"), ""))
|
||||
|
@ -735,6 +775,11 @@ func validateISCSIVolumeSource(iscsi *core.ISCSIVolumeSource, fldPath *field.Pat
|
|||
if (iscsi.DiscoveryCHAPAuth || iscsi.SessionCHAPAuth) && iscsi.SecretRef == nil {
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("secretRef"), ""))
|
||||
}
|
||||
if iscsi.SecretRef != nil {
|
||||
if len(iscsi.SecretRef.Name) == 0 {
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("secretRef", "name"), ""))
|
||||
}
|
||||
}
|
||||
if iscsi.InitiatorName != nil {
|
||||
initiator := *iscsi.InitiatorName
|
||||
if !strings.HasPrefix(initiator, "iqn") && !strings.HasPrefix(initiator, "eui") && !strings.HasPrefix(initiator, "naa") {
|
||||
|
@ -1517,7 +1562,7 @@ func ValidatePersistentVolume(pv *core.PersistentVolume) field.ErrorList {
|
|||
allErrs = append(allErrs, field.Forbidden(specPath.Child("iscsi"), "may not specify more than 1 volume type"))
|
||||
} else {
|
||||
numVolumes++
|
||||
allErrs = append(allErrs, validateISCSIVolumeSource(pv.Spec.ISCSI, specPath.Child("iscsi"))...)
|
||||
allErrs = append(allErrs, validateISCSIPersistentVolumeSource(pv.Spec.ISCSI, specPath.Child("iscsi"))...)
|
||||
}
|
||||
if pv.Spec.ISCSI.InitiatorName != nil && len(pv.ObjectMeta.Name+":"+pv.Spec.ISCSI.TargetPortal) > 64 {
|
||||
tooLongErr := "Total length of <volume name>:<iscsi.targetPortal> must be under 64 characters if iscsi.initiatorName is specified."
|
||||
|
|
|
@ -856,6 +856,26 @@ func printISCSIVolumeSource(iscsi *api.ISCSIVolumeSource, w PrefixWriter) {
|
|||
iscsi.TargetPortal, iscsi.IQN, iscsi.Lun, iscsi.ISCSIInterface, iscsi.FSType, iscsi.ReadOnly, iscsi.Portals, iscsi.DiscoveryCHAPAuth, iscsi.SessionCHAPAuth, iscsi.SecretRef, initiator)
|
||||
}
|
||||
|
||||
func printISCSIPersistentVolumeSource(iscsi *api.ISCSIPersistentVolumeSource, w PrefixWriter) {
|
||||
initiatorName := "<none>"
|
||||
if iscsi.InitiatorName != nil {
|
||||
initiatorName = *iscsi.InitiatorName
|
||||
}
|
||||
w.Write(LEVEL_2, "Type:\tISCSI (an ISCSI Disk resource that is attached to a kubelet's host machine and then exposed to the pod)\n"+
|
||||
" TargetPortal:\t%v\n"+
|
||||
" IQN:\t%v\n"+
|
||||
" Lun:\t%v\n"+
|
||||
" ISCSIInterface\t%v\n"+
|
||||
" FSType:\t%v\n"+
|
||||
" ReadOnly:\t%v\n"+
|
||||
" Portals:\t%v\n"+
|
||||
" DiscoveryCHAPAuth:\t%v\n"+
|
||||
" SessionCHAPAuth:\t%v\n"+
|
||||
" SecretRef:\t%v\n"+
|
||||
" InitiatorName:\t%v\n",
|
||||
iscsi.TargetPortal, iscsi.IQN, iscsi.Lun, iscsi.ISCSIInterface, iscsi.FSType, iscsi.ReadOnly, iscsi.Portals, iscsi.DiscoveryCHAPAuth, iscsi.SessionCHAPAuth, iscsi.SecretRef, initiatorName)
|
||||
}
|
||||
|
||||
func printGlusterfsVolumeSource(glusterfs *api.GlusterfsVolumeSource, w PrefixWriter) {
|
||||
w.Write(LEVEL_2, "Type:\tGlusterfs (a Glusterfs mount on the host that shares a pod's lifetime)\n"+
|
||||
" EndpointsName:\t%v\n"+
|
||||
|
@ -1131,7 +1151,7 @@ func describePersistentVolume(pv *api.PersistentVolume, events *api.EventList) (
|
|||
case pv.Spec.NFS != nil:
|
||||
printNFSVolumeSource(pv.Spec.NFS, w)
|
||||
case pv.Spec.ISCSI != nil:
|
||||
printISCSIVolumeSource(pv.Spec.ISCSI, w)
|
||||
printISCSIPersistentVolumeSource(pv.Spec.ISCSI, w)
|
||||
case pv.Spec.Glusterfs != nil:
|
||||
printGlusterfsVolumeSource(pv.Spec.Glusterfs, w)
|
||||
case pv.Spec.RBD != nil:
|
||||
|
|
|
@ -853,7 +853,7 @@ func TestPersistentVolumeDescriber(t *testing.T) {
|
|||
ObjectMeta: metav1.ObjectMeta{Name: "bar"},
|
||||
Spec: api.PersistentVolumeSpec{
|
||||
PersistentVolumeSource: api.PersistentVolumeSource{
|
||||
ISCSI: &api.ISCSIVolumeSource{},
|
||||
ISCSI: &api.ISCSIPersistentVolumeSource{},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
@ -23,6 +23,7 @@ go_library(
|
|||
"//pkg/volume/util:go_default_library",
|
||||
"//vendor/github.com/golang/glog:go_default_library",
|
||||
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/types:go_default_library",
|
||||
],
|
||||
)
|
||||
|
|
|
@ -24,6 +24,7 @@ import (
|
|||
|
||||
"github.com/golang/glog"
|
||||
"k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/kubernetes/pkg/util/mount"
|
||||
"k8s.io/kubernetes/pkg/volume"
|
||||
|
@ -100,7 +101,7 @@ func (attacher *iscsiAttacher) MountDevice(spec *volume.Spec, devicePath string,
|
|||
return err
|
||||
}
|
||||
}
|
||||
volumeSource, readOnly, err := getVolumeSource(spec)
|
||||
readOnly, fsType, err := getISCSIVolumeInfo(spec)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -112,7 +113,7 @@ func (attacher *iscsiAttacher) MountDevice(spec *volume.Spec, devicePath string,
|
|||
if notMnt {
|
||||
diskMounter := &mount.SafeFormatAndMount{Interface: mounter, Exec: attacher.host.GetExec(iscsiPluginName)}
|
||||
mountOptions := volume.MountOptionFromSpec(spec, options...)
|
||||
err = diskMounter.FormatAndMount(devicePath, deviceMountPath, volumeSource.FSType, mountOptions)
|
||||
err = diskMounter.FormatAndMount(devicePath, deviceMountPath, fsType, mountOptions)
|
||||
if err != nil {
|
||||
os.Remove(deviceMountPath)
|
||||
return err
|
||||
|
@ -159,29 +160,75 @@ func (detacher *iscsiDetacher) UnmountDevice(deviceMountPath string) error {
|
|||
func (attacher *iscsiAttacher) volumeSpecToMounter(spec *volume.Spec, host volume.VolumeHost, pod *v1.Pod) (*iscsiDiskMounter, error) {
|
||||
var secret map[string]string
|
||||
var bkportal []string
|
||||
iscsi, readOnly, err := getVolumeSource(spec)
|
||||
readOnly, fsType, err := getISCSIVolumeInfo(spec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Obtain secret for AttachDisk
|
||||
if iscsi.SecretRef != nil && pod != nil {
|
||||
if secret, err = volumeutil.GetSecretForPod(pod, iscsi.SecretRef.Name, host.GetKubeClient()); err != nil {
|
||||
glog.Errorf("Couldn't get secret from %v/%v", pod.Namespace, iscsi.SecretRef)
|
||||
if pod != nil {
|
||||
chapDiscovery, err := getISCSIDiscoveryCHAPInfo(spec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
chapSession, err := getISCSISessionCHAPInfo(spec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
lun := strconv.Itoa(int(iscsi.Lun))
|
||||
portal := portalMounter(iscsi.TargetPortal)
|
||||
if chapDiscovery || chapSession {
|
||||
secretName, secretNamespace, err := getISCSISecretNameAndNamespace(spec, pod.Namespace)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(secretNamespace) == 0 || len(secretName) == 0 {
|
||||
return nil, fmt.Errorf("CHAP enabled but secret name or namespace is empty")
|
||||
}
|
||||
// if secret is provided, retrieve it
|
||||
kubeClient := host.GetKubeClient()
|
||||
if kubeClient == nil {
|
||||
return nil, fmt.Errorf("Cannot get kube client")
|
||||
}
|
||||
secretObj, err := kubeClient.Core().Secrets(secretNamespace).Get(secretName, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Couldn't get secret %v/%v error: %v", secretNamespace, secretName, err)
|
||||
return nil, err
|
||||
}
|
||||
secret = make(map[string]string)
|
||||
for name, data := range secretObj.Data {
|
||||
glog.V(6).Infof("retrieving CHAP secret name: %s", name)
|
||||
secret[name] = string(data)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
tp, portals, iqn, lunStr, err := getISCSITargetInfo(spec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
lun := strconv.Itoa(int(lunStr))
|
||||
portal := portalMounter(tp)
|
||||
bkportal = append(bkportal, portal)
|
||||
for _, tp := range iscsi.Portals {
|
||||
bkportal = append(bkportal, portalMounter(string(tp)))
|
||||
for _, p := range portals {
|
||||
bkportal = append(bkportal, portalMounter(string(p)))
|
||||
}
|
||||
iface := iscsi.ISCSIInterface
|
||||
exec := attacher.host.GetExec(iscsiPluginName)
|
||||
|
||||
iface, initiatorNamePtr, err := getISCSIInitiatorInfo(spec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var initiatorName string
|
||||
if iscsi.InitiatorName != nil {
|
||||
initiatorName = *iscsi.InitiatorName
|
||||
if initiatorNamePtr != nil {
|
||||
initiatorName = *initiatorNamePtr
|
||||
}
|
||||
chapDiscovery, err := getISCSIDiscoveryCHAPInfo(spec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
chapSession, err := getISCSISessionCHAPInfo(spec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
exec := attacher.host.GetExec(iscsiPluginName)
|
||||
|
||||
return &iscsiDiskMounter{
|
||||
iscsiDisk: &iscsiDisk{
|
||||
|
@ -190,15 +237,15 @@ func (attacher *iscsiAttacher) volumeSpecToMounter(spec *volume.Spec, host volum
|
|||
},
|
||||
VolName: spec.Name(),
|
||||
Portals: bkportal,
|
||||
Iqn: iscsi.IQN,
|
||||
Iqn: iqn,
|
||||
lun: lun,
|
||||
Iface: iface,
|
||||
chap_discovery: iscsi.DiscoveryCHAPAuth,
|
||||
chap_session: iscsi.SessionCHAPAuth,
|
||||
chap_discovery: chapDiscovery,
|
||||
chap_session: chapSession,
|
||||
secret: secret,
|
||||
InitiatorName: initiatorName,
|
||||
manager: &ISCSIUtil{}},
|
||||
fsType: iscsi.FSType,
|
||||
fsType: fsType,
|
||||
readOnly: readOnly,
|
||||
mounter: &mount.SafeFormatAndMount{Interface: host.GetMounter(iscsiPluginName), Exec: exec},
|
||||
exec: exec,
|
||||
|
|
|
@ -23,6 +23,7 @@ import (
|
|||
|
||||
"github.com/golang/glog"
|
||||
"k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/kubernetes/pkg/util/mount"
|
||||
utilstrings "k8s.io/kubernetes/pkg/util/strings"
|
||||
|
@ -56,16 +57,12 @@ func (plugin *iscsiPlugin) GetPluginName() string {
|
|||
}
|
||||
|
||||
func (plugin *iscsiPlugin) GetVolumeName(spec *volume.Spec) (string, error) {
|
||||
volumeSource, _, err := getVolumeSource(spec)
|
||||
tp, _, iqn, lun, err := getISCSITargetInfo(spec)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return fmt.Sprintf(
|
||||
"%v:%v:%v",
|
||||
volumeSource.TargetPortal,
|
||||
volumeSource.IQN,
|
||||
volumeSource.Lun), nil
|
||||
return fmt.Sprintf("%v:%v:%v", tp, iqn, lun), nil
|
||||
}
|
||||
|
||||
func (plugin *iscsiPlugin) CanSupport(spec *volume.Spec) bool {
|
||||
|
@ -98,41 +95,80 @@ func (plugin *iscsiPlugin) GetAccessModes() []v1.PersistentVolumeAccessMode {
|
|||
func (plugin *iscsiPlugin) NewMounter(spec *volume.Spec, pod *v1.Pod, _ volume.VolumeOptions) (volume.Mounter, error) {
|
||||
// Inject real implementations here, test through the internal function.
|
||||
var secret map[string]string
|
||||
source, _, err := getVolumeSource(spec)
|
||||
if pod == nil {
|
||||
return nil, fmt.Errorf("nil pod")
|
||||
}
|
||||
chapDiscover, err := getISCSIDiscoveryCHAPInfo(spec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
chapSession, err := getISCSISessionCHAPInfo(spec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if chapDiscover || chapSession {
|
||||
secretName, secretNamespace, err := getISCSISecretNameAndNamespace(spec, pod.Namespace)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if source.SecretRef != nil {
|
||||
if secret, err = ioutil.GetSecretForPod(pod, source.SecretRef.Name, plugin.host.GetKubeClient()); err != nil {
|
||||
glog.Errorf("Couldn't get secret from %v/%v", pod.Namespace, source.SecretRef)
|
||||
if len(secretName) > 0 && len(secretNamespace) > 0 {
|
||||
// if secret is provideded, retrieve it
|
||||
kubeClient := plugin.host.GetKubeClient()
|
||||
if kubeClient == nil {
|
||||
return nil, fmt.Errorf("Cannot get kube client")
|
||||
}
|
||||
secretObj, err := kubeClient.Core().Secrets(secretNamespace).Get(secretName, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Couldn't get secret %v/%v error: %v", secretNamespace, secretName, err)
|
||||
return nil, err
|
||||
}
|
||||
secret = make(map[string]string)
|
||||
for name, data := range secretObj.Data {
|
||||
glog.V(4).Infof("retrieving CHAP secret name: %s", name)
|
||||
secret[name] = string(data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return plugin.newMounterInternal(spec, pod.UID, &ISCSIUtil{}, plugin.host.GetMounter(plugin.GetPluginName()), plugin.host.GetExec(plugin.GetPluginName()), secret)
|
||||
}
|
||||
|
||||
func (plugin *iscsiPlugin) newMounterInternal(spec *volume.Spec, podUID types.UID, manager diskManager, mounter mount.Interface, exec mount.Exec, secret map[string]string) (volume.Mounter, error) {
|
||||
// iscsi volumes used directly in a pod have a ReadOnly flag set by the pod author.
|
||||
// iscsi volumes used as a PersistentVolume gets the ReadOnly flag indirectly through the persistent-claim volume used to mount the PV
|
||||
iscsi, readOnly, err := getVolumeSource(spec)
|
||||
readOnly, fsType, err := getISCSIVolumeInfo(spec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tp, portals, iqn, lunStr, err := getISCSITargetInfo(spec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
lun := strconv.Itoa(int(iscsi.Lun))
|
||||
portal := portalMounter(iscsi.TargetPortal)
|
||||
lun := strconv.Itoa(int(lunStr))
|
||||
portal := portalMounter(tp)
|
||||
var bkportal []string
|
||||
bkportal = append(bkportal, portal)
|
||||
for _, tp := range iscsi.Portals {
|
||||
bkportal = append(bkportal, portalMounter(string(tp)))
|
||||
for _, p := range portals {
|
||||
bkportal = append(bkportal, portalMounter(string(p)))
|
||||
}
|
||||
|
||||
iface, initiatorNamePtr, err := getISCSIInitiatorInfo(spec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
iface := iscsi.ISCSIInterface
|
||||
|
||||
var initiatorName string
|
||||
if iscsi.InitiatorName != nil {
|
||||
initiatorName = *iscsi.InitiatorName
|
||||
if initiatorNamePtr != nil {
|
||||
initiatorName = *initiatorNamePtr
|
||||
}
|
||||
chapDiscovery, err := getISCSIDiscoveryCHAPInfo(spec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
chapSession, err := getISCSISessionCHAPInfo(spec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &iscsiDiskMounter{
|
||||
|
@ -140,16 +176,16 @@ func (plugin *iscsiPlugin) newMounterInternal(spec *volume.Spec, podUID types.UI
|
|||
podUID: podUID,
|
||||
VolName: spec.Name(),
|
||||
Portals: bkportal,
|
||||
Iqn: iscsi.IQN,
|
||||
Iqn: iqn,
|
||||
lun: lun,
|
||||
Iface: iface,
|
||||
chap_discovery: iscsi.DiscoveryCHAPAuth,
|
||||
chap_session: iscsi.SessionCHAPAuth,
|
||||
chap_discovery: chapDiscovery,
|
||||
chap_session: chapSession,
|
||||
secret: secret,
|
||||
InitiatorName: initiatorName,
|
||||
manager: manager,
|
||||
plugin: plugin},
|
||||
fsType: iscsi.FSType,
|
||||
fsType: fsType,
|
||||
readOnly: readOnly,
|
||||
mounter: &mount.SafeFormatAndMount{Interface: mounter, Exec: exec},
|
||||
exec: exec,
|
||||
|
@ -277,13 +313,87 @@ func portalMounter(portal string) string {
|
|||
return portal
|
||||
}
|
||||
|
||||
func getVolumeSource(spec *volume.Spec) (*v1.ISCSIVolumeSource, bool, error) {
|
||||
// get iSCSI volume info: readOnly and fstype
|
||||
func getISCSIVolumeInfo(spec *volume.Spec) (bool, string, error) {
|
||||
// for volume source, readonly is in volume spec
|
||||
// for PV, readonly is in PV spec
|
||||
if spec.Volume != nil && spec.Volume.ISCSI != nil {
|
||||
return spec.Volume.ISCSI, spec.Volume.ISCSI.ReadOnly, nil
|
||||
return spec.Volume.ISCSI.ReadOnly, spec.Volume.ISCSI.FSType, nil
|
||||
} else if spec.PersistentVolume != nil &&
|
||||
spec.PersistentVolume.Spec.ISCSI != nil {
|
||||
return spec.PersistentVolume.Spec.ISCSI, spec.ReadOnly, nil
|
||||
return spec.ReadOnly, spec.PersistentVolume.Spec.ISCSI.FSType, nil
|
||||
}
|
||||
|
||||
return nil, false, fmt.Errorf("Spec does not reference an ISCSI volume type")
|
||||
return false, "", fmt.Errorf("Spec does not reference an ISCSI volume type")
|
||||
}
|
||||
|
||||
// get iSCSI target info: target portal, portals, iqn, and lun
|
||||
func getISCSITargetInfo(spec *volume.Spec) (string, []string, string, int32, error) {
|
||||
if spec.Volume != nil && spec.Volume.ISCSI != nil {
|
||||
return spec.Volume.ISCSI.TargetPortal, spec.Volume.ISCSI.Portals, spec.Volume.ISCSI.IQN, spec.Volume.ISCSI.Lun, nil
|
||||
} else if spec.PersistentVolume != nil &&
|
||||
spec.PersistentVolume.Spec.ISCSI != nil {
|
||||
return spec.PersistentVolume.Spec.ISCSI.TargetPortal, spec.PersistentVolume.Spec.ISCSI.Portals, spec.PersistentVolume.Spec.ISCSI.IQN, spec.PersistentVolume.Spec.ISCSI.Lun, nil
|
||||
}
|
||||
|
||||
return "", nil, "", 0, fmt.Errorf("Spec does not reference an ISCSI volume type")
|
||||
}
|
||||
|
||||
// get iSCSI initiator info: iface and initiator name
|
||||
func getISCSIInitiatorInfo(spec *volume.Spec) (string, *string, error) {
|
||||
if spec.Volume != nil && spec.Volume.ISCSI != nil {
|
||||
return spec.Volume.ISCSI.ISCSIInterface, spec.Volume.ISCSI.InitiatorName, nil
|
||||
} else if spec.PersistentVolume != nil &&
|
||||
spec.PersistentVolume.Spec.ISCSI != nil {
|
||||
return spec.PersistentVolume.Spec.ISCSI.ISCSIInterface, spec.PersistentVolume.Spec.ISCSI.InitiatorName, nil
|
||||
}
|
||||
|
||||
return "", nil, fmt.Errorf("Spec does not reference an ISCSI volume type")
|
||||
}
|
||||
|
||||
// get iSCSI Discovery CHAP boolean
|
||||
func getISCSIDiscoveryCHAPInfo(spec *volume.Spec) (bool, error) {
|
||||
if spec.Volume != nil && spec.Volume.ISCSI != nil {
|
||||
return spec.Volume.ISCSI.DiscoveryCHAPAuth, nil
|
||||
} else if spec.PersistentVolume != nil &&
|
||||
spec.PersistentVolume.Spec.ISCSI != nil {
|
||||
return spec.PersistentVolume.Spec.ISCSI.DiscoveryCHAPAuth, nil
|
||||
}
|
||||
|
||||
return false, fmt.Errorf("Spec does not reference an ISCSI volume type")
|
||||
}
|
||||
|
||||
// get iSCSI Session CHAP boolean
|
||||
func getISCSISessionCHAPInfo(spec *volume.Spec) (bool, error) {
|
||||
if spec.Volume != nil && spec.Volume.ISCSI != nil {
|
||||
return spec.Volume.ISCSI.SessionCHAPAuth, nil
|
||||
} else if spec.PersistentVolume != nil &&
|
||||
spec.PersistentVolume.Spec.ISCSI != nil {
|
||||
return spec.PersistentVolume.Spec.ISCSI.SessionCHAPAuth, nil
|
||||
}
|
||||
|
||||
return false, fmt.Errorf("Spec does not reference an ISCSI volume type")
|
||||
}
|
||||
|
||||
// get iSCSI CHAP Secret info: secret name and namespace
|
||||
func getISCSISecretNameAndNamespace(spec *volume.Spec, defaultSecretNamespace string) (string, string, error) {
|
||||
if spec.Volume != nil && spec.Volume.ISCSI != nil {
|
||||
if spec.Volume.ISCSI.SecretRef != nil {
|
||||
return spec.Volume.ISCSI.SecretRef.Name, defaultSecretNamespace, nil
|
||||
}
|
||||
return "", "", nil
|
||||
} else if spec.PersistentVolume != nil &&
|
||||
spec.PersistentVolume.Spec.ISCSI != nil {
|
||||
secretRef := spec.PersistentVolume.Spec.ISCSI.SecretRef
|
||||
secretNs := defaultSecretNamespace
|
||||
if secretRef != nil {
|
||||
if len(secretRef.Namespace) != 0 {
|
||||
secretNs = secretRef.Namespace
|
||||
}
|
||||
return secretRef.Name, secretNs, nil
|
||||
}
|
||||
return "", "", nil
|
||||
}
|
||||
|
||||
return "", "", fmt.Errorf("Spec does not reference an ISCSI volume type")
|
||||
}
|
||||
|
|
|
@ -205,7 +205,7 @@ func TestPluginPersistentVolume(t *testing.T) {
|
|||
},
|
||||
Spec: v1.PersistentVolumeSpec{
|
||||
PersistentVolumeSource: v1.PersistentVolumeSource{
|
||||
ISCSI: &v1.ISCSIVolumeSource{
|
||||
ISCSI: &v1.ISCSIPersistentVolumeSource{
|
||||
TargetPortal: "127.0.0.1:3260",
|
||||
IQN: "iqn.2014-12.server:storage.target01",
|
||||
FSType: "ext4",
|
||||
|
@ -230,7 +230,7 @@ func TestPersistentClaimReadOnlyFlag(t *testing.T) {
|
|||
},
|
||||
Spec: v1.PersistentVolumeSpec{
|
||||
PersistentVolumeSource: v1.PersistentVolumeSource{
|
||||
ISCSI: &v1.ISCSIVolumeSource{
|
||||
ISCSI: &v1.ISCSIPersistentVolumeSource{
|
||||
TargetPortal: "127.0.0.1:3260",
|
||||
IQN: "iqn.2014-12.server:storage.target01",
|
||||
FSType: "ext4",
|
||||
|
@ -283,3 +283,146 @@ func TestPortalMounter(t *testing.T) {
|
|||
t.Errorf("wrong portal: %s", portal)
|
||||
}
|
||||
}
|
||||
|
||||
type testcase struct {
|
||||
name string
|
||||
defaultNs string
|
||||
spec *volume.Spec
|
||||
// Expected return of the test
|
||||
expectedName string
|
||||
expectedNs string
|
||||
expectedIface string
|
||||
expectedError error
|
||||
}
|
||||
|
||||
func TestGetSecretNameAndNamespaceForPV(t *testing.T) {
|
||||
tests := []testcase{
|
||||
{
|
||||
name: "persistent volume source",
|
||||
defaultNs: "default",
|
||||
spec: &volume.Spec{
|
||||
PersistentVolume: &v1.PersistentVolume{
|
||||
Spec: v1.PersistentVolumeSpec{
|
||||
PersistentVolumeSource: v1.PersistentVolumeSource{
|
||||
ISCSI: &v1.ISCSIPersistentVolumeSource{
|
||||
TargetPortal: "127.0.0.1:3260",
|
||||
IQN: "iqn.2014-12.server:storage.target01",
|
||||
FSType: "ext4",
|
||||
Lun: 0,
|
||||
SecretRef: &v1.SecretReference{
|
||||
Name: "name",
|
||||
Namespace: "ns",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedName: "name",
|
||||
expectedNs: "ns",
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
name: "persistent volume source without namespace",
|
||||
defaultNs: "default",
|
||||
spec: &volume.Spec{
|
||||
PersistentVolume: &v1.PersistentVolume{
|
||||
Spec: v1.PersistentVolumeSpec{
|
||||
PersistentVolumeSource: v1.PersistentVolumeSource{
|
||||
ISCSI: &v1.ISCSIPersistentVolumeSource{
|
||||
TargetPortal: "127.0.0.1:3260",
|
||||
IQN: "iqn.2014-12.server:storage.target01",
|
||||
FSType: "ext4",
|
||||
Lun: 0,
|
||||
SecretRef: &v1.SecretReference{
|
||||
Name: "name",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedName: "name",
|
||||
expectedNs: "default",
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
name: "pod volume source",
|
||||
defaultNs: "default",
|
||||
spec: &volume.Spec{
|
||||
Volume: &v1.Volume{
|
||||
VolumeSource: v1.VolumeSource{
|
||||
ISCSI: &v1.ISCSIVolumeSource{
|
||||
TargetPortal: "127.0.0.1:3260",
|
||||
IQN: "iqn.2014-12.server:storage.target01",
|
||||
FSType: "ext4",
|
||||
Lun: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedName: "",
|
||||
expectedNs: "",
|
||||
expectedError: nil,
|
||||
},
|
||||
}
|
||||
for _, testcase := range tests {
|
||||
resultName, resultNs, err := getISCSISecretNameAndNamespace(testcase.spec, testcase.defaultNs)
|
||||
if err != testcase.expectedError || resultName != testcase.expectedName || resultNs != testcase.expectedNs {
|
||||
t.Errorf("%s failed: expected err=%v ns=%q name=%q, got %v/%q/%q", testcase.name, testcase.expectedError, testcase.expectedNs, testcase.expectedName,
|
||||
err, resultNs, resultName)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestGetISCSIInitiatorInfo(t *testing.T) {
|
||||
tests := []testcase{
|
||||
{
|
||||
name: "persistent volume source",
|
||||
spec: &volume.Spec{
|
||||
PersistentVolume: &v1.PersistentVolume{
|
||||
Spec: v1.PersistentVolumeSpec{
|
||||
PersistentVolumeSource: v1.PersistentVolumeSource{
|
||||
ISCSI: &v1.ISCSIPersistentVolumeSource{
|
||||
TargetPortal: "127.0.0.1:3260",
|
||||
IQN: "iqn.2014-12.server:storage.target01",
|
||||
FSType: "ext4",
|
||||
Lun: 0,
|
||||
ISCSIInterface: "tcp",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedIface: "tcp",
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
name: "pod volume source",
|
||||
spec: &volume.Spec{
|
||||
Volume: &v1.Volume{
|
||||
VolumeSource: v1.VolumeSource{
|
||||
ISCSI: &v1.ISCSIVolumeSource{
|
||||
TargetPortal: "127.0.0.1:3260",
|
||||
IQN: "iqn.2014-12.server:storage.target01",
|
||||
FSType: "ext4",
|
||||
Lun: 0,
|
||||
ISCSIInterface: "tcp",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedIface: "tcp",
|
||||
expectedError: nil,
|
||||
},
|
||||
}
|
||||
for _, testcase := range tests {
|
||||
resultIface, _, err := getISCSIInitiatorInfo(testcase.spec)
|
||||
if err != testcase.expectedError || resultIface != testcase.expectedIface {
|
||||
t.Errorf("%s failed: expected err=%v iface=%s, got %v/%s", testcase.name, testcase.expectedError, testcase.expectedIface,
|
||||
err, resultIface)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -402,7 +402,7 @@ type PersistentVolumeSource struct {
|
|||
// ISCSI represents an ISCSI Disk resource that is attached to a
|
||||
// kubelet's host machine and then exposed to the pod. Provisioned by an admin.
|
||||
// +optional
|
||||
ISCSI *ISCSIVolumeSource `json:"iscsi,omitempty" protobuf:"bytes,7,opt,name=iscsi"`
|
||||
ISCSI *ISCSIPersistentVolumeSource `json:"iscsi,omitempty" protobuf:"bytes,7,opt,name=iscsi"`
|
||||
// Cinder represents a cinder volume attached and mounted on kubelets host machine
|
||||
// More info: https://releases.k8s.io/HEAD/examples/mysql-cinder-pd/README.md
|
||||
// +optional
|
||||
|
@ -1236,14 +1236,15 @@ type NFSVolumeSource struct {
|
|||
// ISCSI volumes can only be mounted as read/write once.
|
||||
// ISCSI volumes support ownership management and SELinux relabeling.
|
||||
type ISCSIVolumeSource struct {
|
||||
// iSCSI target portal. The portal is either an IP or ip_addr:port if the port
|
||||
// iSCSI Target Portal. The Portal is either an IP or ip_addr:port if the port
|
||||
// is other than default (typically TCP ports 860 and 3260).
|
||||
TargetPortal string `json:"targetPortal" protobuf:"bytes,1,opt,name=targetPortal"`
|
||||
// Target iSCSI Qualified Name.
|
||||
IQN string `json:"iqn" protobuf:"bytes,2,opt,name=iqn"`
|
||||
// iSCSI target lun number.
|
||||
// iSCSI Target Lun number.
|
||||
Lun int32 `json:"lun" protobuf:"varint,3,opt,name=lun"`
|
||||
// Optional: Defaults to 'default' (tcp). iSCSI interface name that uses an iSCSI transport.
|
||||
// iSCSI Interface Name that uses an iSCSI transport.
|
||||
// Defaults to 'default' (tcp).
|
||||
// +optional
|
||||
ISCSIInterface string `json:"iscsiInterface,omitempty" protobuf:"bytes,4,opt,name=iscsiInterface"`
|
||||
// Filesystem type of the volume that you want to mount.
|
||||
|
@ -1257,7 +1258,7 @@ type ISCSIVolumeSource struct {
|
|||
// Defaults to false.
|
||||
// +optional
|
||||
ReadOnly bool `json:"readOnly,omitempty" protobuf:"varint,6,opt,name=readOnly"`
|
||||
// iSCSI target portal List. The portal is either an IP or ip_addr:port if the port
|
||||
// iSCSI Target Portal List. The portal is either an IP or ip_addr:port if the port
|
||||
// is other than default (typically TCP ports 860 and 3260).
|
||||
// +optional
|
||||
Portals []string `json:"portals,omitempty" protobuf:"bytes,7,opt,name=portals"`
|
||||
|
@ -1267,10 +1268,56 @@ type ISCSIVolumeSource struct {
|
|||
// whether support iSCSI Session CHAP authentication
|
||||
// +optional
|
||||
SessionCHAPAuth bool `json:"chapAuthSession,omitempty" protobuf:"varint,11,opt,name=chapAuthSession"`
|
||||
// CHAP secret for iSCSI target and initiator authentication
|
||||
// CHAP Secret for iSCSI target and initiator authentication
|
||||
// +optional
|
||||
SecretRef *LocalObjectReference `json:"secretRef,omitempty" protobuf:"bytes,10,opt,name=secretRef"`
|
||||
// Custom iSCSI initiator name.
|
||||
// Custom iSCSI Initiator Name.
|
||||
// If initiatorName is specified with iscsiInterface simultaneously, new iSCSI interface
|
||||
// <target portal>:<volume name> will be created for the connection.
|
||||
// +optional
|
||||
InitiatorName *string `json:"initiatorName,omitempty" protobuf:"bytes,12,opt,name=initiatorName"`
|
||||
}
|
||||
|
||||
// ISCSIPersistentVolumeSource represents an ISCSI disk.
|
||||
// ISCSI volumes can only be mounted as read/write once.
|
||||
// ISCSI volumes support ownership management and SELinux relabeling.
|
||||
type ISCSIPersistentVolumeSource struct {
|
||||
// iSCSI Target Portal. The Portal is either an IP or ip_addr:port if the port
|
||||
// is other than default (typically TCP ports 860 and 3260).
|
||||
TargetPortal string `json:"targetPortal" protobuf:"bytes,1,opt,name=targetPortal"`
|
||||
// Target iSCSI Qualified Name.
|
||||
IQN string `json:"iqn" protobuf:"bytes,2,opt,name=iqn"`
|
||||
// iSCSI Target Lun number.
|
||||
Lun int32 `json:"lun" protobuf:"varint,3,opt,name=lun"`
|
||||
// iSCSI Interface Name that uses an iSCSI transport.
|
||||
// Defaults to 'default' (tcp).
|
||||
// +optional
|
||||
ISCSIInterface string `json:"iscsiInterface,omitempty" protobuf:"bytes,4,opt,name=iscsiInterface"`
|
||||
// Filesystem type of the volume that you want to mount.
|
||||
// Tip: Ensure that the filesystem type is supported by the host operating system.
|
||||
// Examples: "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified.
|
||||
// More info: https://kubernetes.io/docs/concepts/storage/volumes#iscsi
|
||||
// TODO: how do we prevent errors in the filesystem from compromising the machine
|
||||
// +optional
|
||||
FSType string `json:"fsType,omitempty" protobuf:"bytes,5,opt,name=fsType"`
|
||||
// ReadOnly here will force the ReadOnly setting in VolumeMounts.
|
||||
// Defaults to false.
|
||||
// +optional
|
||||
ReadOnly bool `json:"readOnly,omitempty" protobuf:"varint,6,opt,name=readOnly"`
|
||||
// iSCSI Target Portal List. The Portal is either an IP or ip_addr:port if the port
|
||||
// is other than default (typically TCP ports 860 and 3260).
|
||||
// +optional
|
||||
Portals []string `json:"portals,omitempty" protobuf:"bytes,7,opt,name=portals"`
|
||||
// whether support iSCSI Discovery CHAP authentication
|
||||
// +optional
|
||||
DiscoveryCHAPAuth bool `json:"chapAuthDiscovery,omitempty" protobuf:"varint,8,opt,name=chapAuthDiscovery"`
|
||||
// whether support iSCSI Session CHAP authentication
|
||||
// +optional
|
||||
SessionCHAPAuth bool `json:"chapAuthSession,omitempty" protobuf:"varint,11,opt,name=chapAuthSession"`
|
||||
// CHAP Secret for iSCSI target and initiator authentication
|
||||
// +optional
|
||||
SecretRef *SecretReference `json:"secretRef,omitempty" protobuf:"bytes,10,opt,name=secretRef"`
|
||||
// Custom iSCSI Initiator Name.
|
||||
// If initiatorName is specified with iscsiInterface simultaneously, new iSCSI interface
|
||||
// <target portal>:<volume name> will be created for the connection.
|
||||
// +optional
|
||||
|
|
Loading…
Reference in New Issue