Merge pull request #48789 from mtanino/issue/47311

Automatic merge from submit-queue (batch tested with PRs 50806, 48789, 49922, 49935, 50438)

iSCSI volume plugin: iSCSI initiatorname support

**What this PR does / why we need it**:

This PR adds iSCSI initiatorname parameter to ISCSIVolumeSource to enable automatic configuration of initiator name per volume. This would allow for more fine grained configuration, and remove the need to configure the initiator name on the host by administrator.

**Which issue this PR fixes**: fixes #47311

**Special notes for your reviewer**:

/cc @rootfs @jsafrane @msau42

**Release note**:

```
iSCSI volume plugin: Support customization of iSCSI initiator name per volume
```
pull/6/head
Kubernetes Submit Queue 2017-08-22 19:45:29 -07:00 committed by GitHub
commit d235fa3f1c
36 changed files with 1548 additions and 858 deletions

View File

@ -59432,6 +59432,10 @@
"description": "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",
"type": "string"
},
"initiatorName": {
"description": "Custom iSCSI initiator name. If initiatorName is specified with iscsiInterface simultaneously, new iSCSI interface \u003ctarget portal\u003e:\u003cvolume name\u003e will be created for the connection.",
"type": "string"
},
"iqn": {
"description": "Target iSCSI Qualified Name.",
"type": "string"

View File

@ -4349,6 +4349,10 @@
"secretRef": {
"$ref": "v1.LocalObjectReference",
"description": "CHAP secret for iSCSI target and initiator authentication"
},
"initiatorName": {
"type": "string",
"description": "Custom iSCSI initiator name. If initiatorName is specified with iscsiInterface simultaneously, new iSCSI interface \u003ctarget portal\u003e:\u003cvolume name\u003e will be created for the connection."
}
}
},

View File

@ -6487,6 +6487,10 @@
"secretRef": {
"$ref": "v1.LocalObjectReference",
"description": "CHAP secret for iSCSI target and initiator authentication"
},
"initiatorName": {
"type": "string",
"description": "Custom iSCSI initiator name. If initiatorName is specified with iscsiInterface simultaneously, new iSCSI interface \u003ctarget portal\u003e:\u003cvolume name\u003e will be created for the connection."
}
}
},

View File

@ -1931,6 +1931,10 @@
"secretRef": {
"$ref": "v1.LocalObjectReference",
"description": "CHAP secret for iSCSI target and initiator authentication"
},
"initiatorName": {
"type": "string",
"description": "Custom iSCSI initiator name. If initiatorName is specified with iscsiInterface simultaneously, new iSCSI interface \u003ctarget portal\u003e:\u003cvolume name\u003e will be created for the connection."
}
}
},

View File

@ -1986,6 +1986,10 @@
"secretRef": {
"$ref": "v1.LocalObjectReference",
"description": "CHAP secret for iSCSI target and initiator authentication"
},
"initiatorName": {
"type": "string",
"description": "Custom iSCSI initiator name. If initiatorName is specified with iscsiInterface simultaneously, new iSCSI interface \u003ctarget portal\u003e:\u003cvolume name\u003e will be created for the connection."
}
}
},

View File

@ -1986,6 +1986,10 @@
"secretRef": {
"$ref": "v1.LocalObjectReference",
"description": "CHAP secret for iSCSI target and initiator authentication"
},
"initiatorName": {
"type": "string",
"description": "Custom iSCSI initiator name. If initiatorName is specified with iscsiInterface simultaneously, new iSCSI interface \u003ctarget portal\u003e:\u003cvolume name\u003e will be created for the connection."
}
}
},

View File

@ -7041,6 +7041,10 @@
"secretRef": {
"$ref": "v1.LocalObjectReference",
"description": "CHAP secret for iSCSI target and initiator authentication"
},
"initiatorName": {
"type": "string",
"description": "Custom iSCSI initiator name. If initiatorName is specified with iscsiInterface simultaneously, new iSCSI interface \u003ctarget portal\u003e:\u003cvolume name\u003e will be created for the connection."
}
}
},

View File

@ -1787,6 +1787,10 @@
"secretRef": {
"$ref": "v1.LocalObjectReference",
"description": "CHAP secret for iSCSI target and initiator authentication"
},
"initiatorName": {
"type": "string",
"description": "Custom iSCSI initiator name. If initiatorName is specified with iscsiInterface simultaneously, new iSCSI interface \u003ctarget portal\u003e:\u003cvolume name\u003e will be created for the connection."
}
}
},

View File

@ -19153,6 +19153,10 @@
"secretRef": {
"$ref": "v1.LocalObjectReference",
"description": "CHAP secret for iSCSI target and initiator authentication"
},
"initiatorName": {
"type": "string",
"description": "Custom iSCSI initiator name. If initiatorName is specified with iscsiInterface simultaneously, new iSCSI interface \u003ctarget portal\u003e:\u003cvolume name\u003e will be created for the connection."
}
}
},

View File

@ -2012,6 +2012,13 @@ When an object is created, the system will populate this list with the current s
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_v1_localobjectreference">v1.LocalObjectReference</a></p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">initiatorName</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Custom iSCSI initiator name. If initiatorName is specified with iscsiInterface simultaneously, new iSCSI interface &lt;target portal&gt;:&lt;volume name&gt; will be created for the connection.</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">string</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
</tbody>
</table>

View File

@ -2110,6 +2110,13 @@ When an object is created, the system will populate this list with the current s
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_v1_localobjectreference">v1.LocalObjectReference</a></p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">initiatorName</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Custom iSCSI initiator name. If initiatorName is specified with iscsiInterface simultaneously, new iSCSI interface &lt;target portal&gt;:&lt;volume name&gt; will be created for the connection.</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">string</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
</tbody>
</table>

View File

@ -1629,6 +1629,13 @@ When an object is created, the system will populate this list with the current s
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_v1_localobjectreference">v1.LocalObjectReference</a></p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">initiatorName</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Custom iSCSI initiator name. If initiatorName is specified with iscsiInterface simultaneously, new iSCSI interface &lt;target portal&gt;:&lt;volume name&gt; will be created for the connection.</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">string</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
</tbody>
</table>

View File

@ -1670,6 +1670,13 @@ When an object is created, the system will populate this list with the current s
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_v1_localobjectreference">v1.LocalObjectReference</a></p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">initiatorName</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Custom iSCSI initiator name. If initiatorName is specified with iscsiInterface simultaneously, new iSCSI interface &lt;target portal&gt;:&lt;volume name&gt; will be created for the connection.</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">string</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
</tbody>
</table>

View File

@ -1629,6 +1629,13 @@ When an object is created, the system will populate this list with the current s
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_v1_localobjectreference">v1.LocalObjectReference</a></p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">initiatorName</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Custom iSCSI initiator name. If initiatorName is specified with iscsiInterface simultaneously, new iSCSI interface &lt;target portal&gt;:&lt;volume name&gt; will be created for the connection.</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">string</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
</tbody>
</table>

View File

@ -2524,6 +2524,13 @@ When an object is created, the system will populate this list with the current s
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_v1_localobjectreference">v1.LocalObjectReference</a></p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">initiatorName</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Custom iSCSI initiator name. If initiatorName is specified with iscsiInterface simultaneously, new iSCSI interface &lt;target portal&gt;:&lt;volume name&gt; will be created for the connection.</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">string</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
</tbody>
</table>

View File

@ -2147,6 +2147,13 @@ When an object is created, the system will populate this list with the current s
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_v1_localobjectreference">v1.LocalObjectReference</a></p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">initiatorName</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Custom iSCSI initiator name. If initiatorName is specified with iscsiInterface simultaneously, new iSCSI interface &lt;target portal&gt;:&lt;volume name&gt; will be created for the connection.</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">string</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
</tbody>
</table>

View File

@ -2713,6 +2713,13 @@ When an object is created, the system will populate this list with the current s
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_v1_localobjectreference">v1.LocalObjectReference</a></p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">initiatorName</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Custom iSCSI initiator name. If initiatorName is specified with iscsiInterface simultaneously, new iSCSI interface &lt;target portal&gt;:&lt;volume name&gt; will be created for the connection.</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">string</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
</tbody>
</table>

View File

@ -10630,6 +10630,10 @@
"description": "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",
"type": "string"
},
"initiatorName": {
"description": "Custom iSCSI initiator name. If initiatorName is specified with iscsiInterface simultaneously, new iSCSI interface \u003ctarget portal\u003e:\u003cvolume name\u003e will be created for the connection.",
"type": "string"
},
"iqn": {
"description": "Target iSCSI Qualified Name.",
"type": "string"

View File

@ -5400,6 +5400,10 @@
"secretRef": {
"$ref": "v1.LocalObjectReference",
"description": "CHAP secret for iSCSI target and initiator authentication"
},
"initiatorName": {
"type": "string",
"description": "Custom iSCSI initiator name. If initiatorName is specified with iscsiInterface simultaneously, new iSCSI interface \u003ctarget portal\u003e:\u003cvolume name\u003e will be created for the connection."
}
}
},

View File

@ -2361,6 +2361,13 @@ When an object is created, the system will populate this list with the current s
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_v1_localobjectreference">v1.LocalObjectReference</a></p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">initiatorName</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Custom iSCSI initiator name. If initiatorName is specified with iscsiInterface simultaneously, new iSCSI interface &lt;target portal&gt;:&lt;volume name&gt; will be created for the connection.</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">string</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
</tbody>
</table>

View File

@ -709,6 +709,11 @@ type ISCSIVolumeSource struct {
// The secret is used if either DiscoveryCHAPAuth or SessionCHAPAuth is true
// +optional
SecretRef *LocalObjectReference
// 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.

View File

@ -1870,6 +1870,7 @@ func autoConvert_v1_ISCSIVolumeSource_To_api_ISCSIVolumeSource(in *v1.ISCSIVolum
out.DiscoveryCHAPAuth = in.DiscoveryCHAPAuth
out.SessionCHAPAuth = in.SessionCHAPAuth
out.SecretRef = (*api.LocalObjectReference)(unsafe.Pointer(in.SecretRef))
out.InitiatorName = (*string)(unsafe.Pointer(in.InitiatorName))
return nil
}
@ -1889,6 +1890,7 @@ func autoConvert_api_ISCSIVolumeSource_To_v1_ISCSIVolumeSource(in *api.ISCSIVolu
out.DiscoveryCHAPAuth = in.DiscoveryCHAPAuth
out.SessionCHAPAuth = in.SessionCHAPAuth
out.SecretRef = (*v1.LocalObjectReference)(unsafe.Pointer(in.SecretRef))
out.InitiatorName = (*string)(unsafe.Pointer(in.InitiatorName))
return nil
}

View File

@ -69,6 +69,10 @@ var volumeModeErrorMsg string = "must be a number between 0 and 0777 (octal), bo
// BannedOwners is a black list of object that are not allowed to be owners.
var BannedOwners = genericvalidation.BannedOwners
var iscsiInitiatorIqnRegex = regexp.MustCompile(`iqn\.\d{4}-\d{2}\.([[:alnum:]-.]+)(:[^,;*&$|\s]+)$`)
var iscsiInitiatorEuiRegex = regexp.MustCompile(`^eui.[[:alnum:]]{16}$`)
var iscsiInitiatorNaaRegex = regexp.MustCompile(`^naa.[[:alnum:]]{32}$`)
// ValidateHasLabel requires that metav1.ObjectMeta has a Label with key and expectedValue
func ValidateHasLabel(meta metav1.ObjectMeta, fldPath *field.Path, key, expectedValue string) field.ErrorList {
allErrs := field.ErrorList{}
@ -358,7 +362,7 @@ func ValidateVolumes(volumes []api.Volume, fldPath *field.Path) (sets.String, fi
for i, vol := range volumes {
idxPath := fldPath.Index(i)
namePath := idxPath.Child("name")
el := validateVolumeSource(&vol.VolumeSource, idxPath)
el := validateVolumeSource(&vol.VolumeSource, idxPath, vol.Name)
if len(vol.Name) == 0 {
el = append(el, field.Required(namePath, ""))
} else {
@ -377,7 +381,7 @@ func ValidateVolumes(volumes []api.Volume, fldPath *field.Path) (sets.String, fi
return allNames, allErrs
}
func validateVolumeSource(source *api.VolumeSource, fldPath *field.Path) field.ErrorList {
func validateVolumeSource(source *api.VolumeSource, fldPath *field.Path, volName string) field.ErrorList {
numVolumes := 0
allErrs := field.ErrorList{}
if source.EmptyDir != nil {
@ -444,6 +448,10 @@ func validateVolumeSource(source *api.VolumeSource, fldPath *field.Path) field.E
numVolumes++
allErrs = append(allErrs, validateISCSIVolumeSource(source.ISCSI, fldPath.Child("iscsi"))...)
}
if source.ISCSI.InitiatorName != nil && len(volName+":"+source.ISCSI.TargetPortal) > 64 {
tooLongErr := "Total length of <volume name>:<iscsi.targetPortal> must be under 64 characters if iscsi.initiatorName is specified."
allErrs = append(allErrs, field.Invalid(fldPath.Child("name"), volName, tooLongErr))
}
}
if source.Glusterfs != nil {
if numVolumes > 0 {
@ -636,6 +644,16 @@ func validateISCSIVolumeSource(iscsi *api.ISCSIVolumeSource, fldPath *field.Path
}
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"))
} 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)))
@ -643,6 +661,19 @@ func validateISCSIVolumeSource(iscsi *api.ISCSIVolumeSource, fldPath *field.Path
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"))
}
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
}
@ -1292,6 +1323,10 @@ func ValidatePersistentVolume(pv *api.PersistentVolume) field.ErrorList {
numVolumes++
allErrs = append(allErrs, validateISCSIVolumeSource(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."
allErrs = append(allErrs, field.Invalid(metaPath.Child("name"), pv.ObjectMeta.Name, tooLongErr))
}
}
if pv.Spec.Cinder != nil {
if numVolumes > 0 {

View File

@ -1025,6 +1025,8 @@ func newInt32(val int) *int32 {
// type on its own, but we want to also make sure that the logic works through
// the one-of wrapper, so we just do it all in one place.
func TestValidateVolumes(t *testing.T) {
validInitiatorName := "iqn.2015-02.example.com:init"
invalidInitiatorName := "2015-02.example.com:init"
testCases := []struct {
name string
vol api.Volume
@ -1268,6 +1270,36 @@ func TestValidateVolumes(t *testing.T) {
},
},
},
{
name: "valid IQN: eui format",
vol: api.Volume{
Name: "iscsi",
VolumeSource: api.VolumeSource{
ISCSI: &api.ISCSIVolumeSource{
TargetPortal: "127.0.0.1",
IQN: "eui.0123456789ABCDEF",
Lun: 1,
FSType: "ext4",
ReadOnly: false,
},
},
},
},
{
name: "valid IQN: naa format",
vol: api.Volume{
Name: "iscsi",
VolumeSource: api.VolumeSource{
ISCSI: &api.ISCSIVolumeSource{
TargetPortal: "127.0.0.1",
IQN: "naa.62004567BA64678D0123456789ABCDEF",
Lun: 1,
FSType: "ext4",
ReadOnly: false,
},
},
},
},
{
name: "empty portal",
vol: api.Volume{
@ -1302,6 +1334,91 @@ func TestValidateVolumes(t *testing.T) {
errtype: field.ErrorTypeRequired,
errfield: "iscsi.iqn",
},
{
name: "invalid IQN: iqn format",
vol: api.Volume{
Name: "iscsi",
VolumeSource: api.VolumeSource{
ISCSI: &api.ISCSIVolumeSource{
TargetPortal: "127.0.0.1",
IQN: "iqn.2015-02.example.com:test;ls;",
Lun: 1,
FSType: "ext4",
ReadOnly: false,
},
},
},
errtype: field.ErrorTypeInvalid,
errfield: "iscsi.iqn",
},
{
name: "invalid IQN: eui format",
vol: api.Volume{
Name: "iscsi",
VolumeSource: api.VolumeSource{
ISCSI: &api.ISCSIVolumeSource{
TargetPortal: "127.0.0.1",
IQN: "eui.0123456789ABCDEFGHIJ",
Lun: 1,
FSType: "ext4",
ReadOnly: false,
},
},
},
errtype: field.ErrorTypeInvalid,
errfield: "iscsi.iqn",
},
{
name: "invalid IQN: naa format",
vol: api.Volume{
Name: "iscsi",
VolumeSource: api.VolumeSource{
ISCSI: &api.ISCSIVolumeSource{
TargetPortal: "127.0.0.1",
IQN: "naa.62004567BA_4-78D.123456789ABCDEF",
Lun: 1,
FSType: "ext4",
ReadOnly: false,
},
},
},
errtype: field.ErrorTypeInvalid,
errfield: "iscsi.iqn",
},
{
name: "valid initiatorName",
vol: api.Volume{
Name: "iscsi",
VolumeSource: api.VolumeSource{
ISCSI: &api.ISCSIVolumeSource{
TargetPortal: "127.0.0.1",
IQN: "iqn.2015-02.example.com:test",
Lun: 1,
InitiatorName: &validInitiatorName,
FSType: "ext4",
ReadOnly: false,
},
},
},
},
{
name: "invalid initiatorName",
vol: api.Volume{
Name: "iscsi",
VolumeSource: api.VolumeSource{
ISCSI: &api.ISCSIVolumeSource{
TargetPortal: "127.0.0.1",
IQN: "iqn.2015-02.example.com:test",
Lun: 1,
InitiatorName: &invalidInitiatorName,
FSType: "ext4",
ReadOnly: false,
},
},
},
errtype: field.ErrorTypeInvalid,
errfield: "iscsi.initiatorname",
},
{
name: "empty secret",
vol: api.Volume{
@ -2475,7 +2592,7 @@ func TestAlphaLocalStorageCapacityIsolation(t *testing.T) {
return
}
for _, tc := range testCases {
if errs := validateVolumeSource(&tc, field.NewPath("spec")); len(errs) != 0 {
if errs := validateVolumeSource(&tc, field.NewPath("spec"), "tmpvol"); len(errs) != 0 {
t.Errorf("expected success: %v", errs)
}
}
@ -2486,7 +2603,7 @@ func TestAlphaLocalStorageCapacityIsolation(t *testing.T) {
return
}
for _, tc := range testCases {
if errs := validateVolumeSource(&tc, field.NewPath("spec")); len(errs) == 0 {
if errs := validateVolumeSource(&tc, field.NewPath("spec"), "tmpvol"); len(errs) == 0 {
t.Errorf("expected failure: %v", errs)
}
}

View File

@ -2333,6 +2333,15 @@ func (in *ISCSIVolumeSource) DeepCopyInto(out *ISCSIVolumeSource) {
**out = **in
}
}
if in.InitiatorName != nil {
in, out := &in.InitiatorName, &out.InitiatorName
if *in == nil {
*out = nil
} else {
*out = new(string)
**out = **in
}
}
return
}

View File

@ -42,6 +42,8 @@ go_test(
"//vendor/k8s.io/apimachinery/pkg/types:go_default_library",
"//vendor/k8s.io/client-go/kubernetes/fake:go_default_library",
"//vendor/k8s.io/client-go/util/testing:go_default_library",
"//vendor/k8s.io/utils/exec:go_default_library",
"//vendor/k8s.io/utils/exec/testing:go_default_library",
],
)

View File

@ -35,7 +35,6 @@ type diskManager interface {
// utility to mount a disk based filesystem
func diskSetUp(manager diskManager, b iscsiDiskMounter, volPath string, mounter mount.Interface, fsGroup *int64) error {
globalPDPath := manager.MakeGlobalPDName(*b.iscsiDisk)
// TODO: handle failed mounts here.
notMnt, err := mounter.IsLikelyNotMountPoint(volPath)
@ -60,6 +59,7 @@ func diskSetUp(manager diskManager, b iscsiDiskMounter, volPath string, mounter
if b.readOnly {
options = append(options, "ro")
}
globalPDPath := manager.MakeGlobalPDName(*b.iscsiDisk)
mountOptions := volume.JoinMountOptions(b.mountOptions, options)
err = mounter.Mount(globalPDPath, volPath, "", mountOptions)
if err != nil {

View File

@ -132,6 +132,11 @@ func (plugin *iscsiPlugin) newMounterInternal(spec *volume.Spec, podUID types.UI
}
iface := iscsi.ISCSIInterface
var initiatorName string
if iscsi.InitiatorName != nil {
initiatorName = *iscsi.InitiatorName
}
return &iscsiDiskMounter{
iscsiDisk: &iscsiDisk{
podUID: podUID,
@ -143,6 +148,7 @@ func (plugin *iscsiPlugin) newMounterInternal(spec *volume.Spec, podUID types.UI
chap_discovery: iscsi.DiscoveryCHAPAuth,
chap_session: iscsi.SessionCHAPAuth,
secret: secret,
InitiatorName: initiatorName,
manager: manager,
plugin: plugin},
fsType: iscsi.FSType,
@ -198,6 +204,7 @@ type iscsiDisk struct {
chap_discovery bool
chap_session bool
secret map[string]string
InitiatorName string
plugin *iscsiPlugin
// Utility interface that provides API calls to the provider to attach/detach disks.
manager diskManager

View File

@ -205,6 +205,20 @@ func (util *ISCSIUtil) AttachDisk(b iscsiDiskMounter) error {
iscsiTransport = extractTransportname(string(out))
bkpPortal := b.Portals
// create new iface and copy parameters from pre-configured iface to the created iface
if b.InitiatorName != "" {
// new iface name is <target portal>:<volume name>
newIface := bkpPortal[0] + ":" + b.volName
err = cloneIface(b, newIface)
if err != nil {
glog.Errorf("iscsi: failed to clone iface: %s error: %v", b.Iface, err)
return err
}
// update iface name
b.Iface = newIface
}
for _, tp := range bkpPortal {
// Rescan sessions to discover newly mapped LUNs. Do not specify the interface when rescanning
// to avoid establishing additional sessions to the same target.
@ -268,6 +282,8 @@ func (util *ISCSIUtil) AttachDisk(b iscsiDiskMounter) error {
}
if len(devicePaths) == 0 {
// delete cloned iface
b.plugin.execCommand("iscsiadm", []string{"-m", "iface", "-I", b.Iface, "-o", "delete"})
glog.Errorf("iscsi: failed to get any path for iscsi disk, last err seen:\n%v", lastErr)
return fmt.Errorf("failed to get any path for iscsi disk, last err seen:\n%v", lastErr)
}
@ -331,12 +347,13 @@ func (util *ISCSIUtil) DetachDisk(c iscsiDiskUnmounter, mntPath string) error {
refCount, err := getDevicePrefixRefCount(c.mounter, prefix)
if err == nil && refCount == 0 {
var bkpPortal []string
var iqn, iface string
var iqn, iface, initiatorName string
found := true
// load iscsi disk config from json file
if err := util.loadISCSI(c.iscsiDisk, mntPath); err == nil {
bkpPortal, iqn, iface = c.iscsiDisk.Portals, c.iscsiDisk.Iqn, c.iscsiDisk.Iface
initiatorName = c.iscsiDisk.InitiatorName
} else {
// If the iscsi disk config is not found, fall back to the original behavior.
// This portal/iqn/iface is no longer referenced, log out.
@ -351,7 +368,12 @@ func (util *ISCSIUtil) DetachDisk(c iscsiDiskUnmounter, mntPath string) error {
// Logout may fail as no session may exist for the portal/IQN on the specified interface.
iface, found = extractIface(mntPath)
}
for _, portal := range removeDuplicate(bkpPortal) {
portals := removeDuplicate(bkpPortal)
if len(portals) == 0 {
return fmt.Errorf("iscsi detach disk: failed to detach iscsi disk. Couldn't get connected portals from configurations.")
}
for _, portal := range portals {
logout := []string{"-m", "node", "-p", portal, "-T", iqn, "--logout"}
delete := []string{"-m", "node", "-p", portal, "-T", iqn, "-o", "delete"}
if found {
@ -370,6 +392,16 @@ func (util *ISCSIUtil) DetachDisk(c iscsiDiskUnmounter, mntPath string) error {
glog.Errorf("iscsi: failed to delete node record Error: %s", string(out))
}
}
// Delete the iface after all sessions have logged out
// If the iface is not created via iscsi plugin, skip to delete
if initiatorName != "" && found && iface == (portals[0]+":"+c.volName) {
delete := []string{"-m", "iface", "-I", iface, "-o", "delete"}
out, err := c.plugin.execCommand("iscsiadm", delete)
if err != nil {
glog.Errorf("iscsi: failed to delete iface Error: %s", string(out))
}
}
}
}
return nil
@ -448,3 +480,57 @@ func removeDuplicate(s []string) []string {
s = s[:len(m)]
return s
}
func parseIscsiadmShow(output string) (map[string]string, error) {
params := make(map[string]string)
slice := strings.Split(output, "\n")
for _, line := range slice {
if !strings.HasPrefix(line, "iface.") || strings.Contains(line, "<empty>") {
continue
}
iface := strings.Fields(line)
if len(iface) != 3 || iface[1] != "=" {
return nil, fmt.Errorf("Error: invalid iface setting: %v", iface)
}
// iscsi_ifacename is immutable once the iface is created
if iface[0] == "iface.iscsi_ifacename" {
continue
}
params[iface[0]] = iface[2]
}
return params, nil
}
func cloneIface(b iscsiDiskMounter, newIface string) error {
var lastErr error
// get pre-configured iface records
out, err := b.plugin.execCommand("iscsiadm", []string{"-m", "iface", "-I", b.Iface, "-o", "show"})
if err != nil {
lastErr = fmt.Errorf("iscsi: failed to show iface records: %s (%v)", string(out), err)
return lastErr
}
// parse obtained records
params, err := parseIscsiadmShow(string(out))
if err != nil {
lastErr = fmt.Errorf("iscsi: failed to parse iface records: %s (%v)", string(out), err)
return lastErr
}
// update initiatorname
params["iface.initiatorname"] = b.InitiatorName
// create new iface
out, err = b.plugin.execCommand("iscsiadm", []string{"-m", "iface", "-I", newIface, "-o", "new"})
if err != nil {
lastErr = fmt.Errorf("iscsi: failed to create new iface: %s (%v)", string(out), err)
return lastErr
}
// update new iface records
for key, val := range params {
_, err = b.plugin.execCommand("iscsiadm", []string{"-m", "iface", "-I", newIface, "-o", "update", "-n", key, "-v", val})
if err != nil {
b.plugin.execCommand("iscsiadm", []string{"-m", "iface", "-I", newIface, "-o", "delete"})
lastErr = fmt.Errorf("iscsi: failed to update iface records: %s (%v). iface(%s) will be used", string(out), err, b.Iface)
break
}
}
return lastErr
}

View File

@ -17,12 +17,16 @@ limitations under the License.
package iscsi
import (
"errors"
"os"
"path/filepath"
"reflect"
"testing"
"k8s.io/kubernetes/pkg/util/mount"
"k8s.io/kubernetes/pkg/volume"
"k8s.io/utils/exec"
fakeexec "k8s.io/utils/exec/testing"
)
func TestGetDevicePrefixRefCount(t *testing.T) {
@ -183,3 +187,177 @@ func TestWaitForPathToExist(t *testing.T) {
t.Errorf("waitForPathToExist: wrong code path called for %s", devicePath[1])
}
}
func TestParseIscsiadmShow(t *testing.T) {
fakeIscsiadmOutput1 := "# BEGIN RECORD 2.0-873\n" +
"iface.iscsi_ifacename = default\n" +
"iface.transport_name = tcp\n" +
"iface.initiatorname = <empty>\n" +
"iface.mtu = 0\n" +
"# END RECORD"
fakeIscsiadmOutput2 := "# BEGIN RECORD 2.0-873\n" +
"iface.iscsi_ifacename = default\n" +
"iface.transport_name = cxgb4i\n" +
"iface.initiatorname = <empty>\n" +
"iface.mtu = 0\n" +
"# END RECORD"
fakeIscsiadmOutput3 := "# BEGIN RECORD 2.0-873\n" +
"iface.iscsi_ifacename = custom\n" +
"iface.transport_name = <empty>\n" +
"iface.initiatorname = <empty>\n" +
"iface.mtu = 0\n" +
"# END RECORD"
fakeIscsiadmOutput4 := "iface.iscsi_ifacename=error"
fakeIscsiadmOutput5 := "iface.iscsi_ifacename + error"
expectedIscsiadmOutput1 := map[string]string{
"iface.transport_name": "tcp",
"iface.mtu": "0"}
expectedIscsiadmOutput2 := map[string]string{
"iface.transport_name": "cxgb4i",
"iface.mtu": "0"}
expectedIscsiadmOutput3 := map[string]string{
"iface.mtu": "0"}
params, _ := parseIscsiadmShow(fakeIscsiadmOutput1)
if !reflect.DeepEqual(params, expectedIscsiadmOutput1) {
t.Errorf("parseIscsiadmShow: Fail to parse iface record: %s", params)
}
params, _ = parseIscsiadmShow(fakeIscsiadmOutput2)
if !reflect.DeepEqual(params, expectedIscsiadmOutput2) {
t.Errorf("parseIscsiadmShow: Fail to parse iface record: %s", params)
}
params, _ = parseIscsiadmShow(fakeIscsiadmOutput3)
if !reflect.DeepEqual(params, expectedIscsiadmOutput3) {
t.Errorf("parseIscsiadmShow: Fail to parse iface record: %s", params)
}
_, err := parseIscsiadmShow(fakeIscsiadmOutput4)
if err == nil {
t.Errorf("parseIscsiadmShow: Fail to handle invalid record: iface %s", fakeIscsiadmOutput4)
}
_, err = parseIscsiadmShow(fakeIscsiadmOutput5)
if err == nil {
t.Errorf("parseIscsiadmShow: Fail to handle invalid record: iface %s", fakeIscsiadmOutput5)
}
}
func TestClonedIface(t *testing.T) {
fcmd := fakeexec.FakeCmd{
CombinedOutputScript: []fakeexec.FakeCombinedOutputAction{
// iscsiadm -m iface -I <iface> -o show
func() ([]byte, error) {
return []byte("iface.ipaddress = <empty>\niface.transport_name = tcp\niface.initiatorname = <empty>\n"), nil
},
// iscsiadm -m iface -I <newIface> -o new
func() ([]byte, error) { return []byte("New interface 192.168.1.10:pv0001 added"), nil },
// iscsiadm -m iface -I <newIface> -o update -n <key> -v <val>
func() ([]byte, error) { return []byte(""), nil },
func() ([]byte, error) { return []byte(""), nil },
},
}
fexec := fakeexec.FakeExec{
CommandScript: []fakeexec.FakeCommandAction{
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
},
}
plugins := []volume.VolumePlugin{
&iscsiPlugin{
host: nil,
exe: &fexec,
},
}
plugin := plugins[0]
fakeMounter := iscsiDiskMounter{
iscsiDisk: &iscsiDisk{
plugin: plugin.(*iscsiPlugin)},
}
newIface := "192.168.1.10:pv0001"
cloneIface(fakeMounter, newIface)
if fcmd.CombinedOutputCalls != 4 {
t.Errorf("expected 4 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls)
}
}
func TestClonedIfaceShowError(t *testing.T) {
fcmd := fakeexec.FakeCmd{
CombinedOutputScript: []fakeexec.FakeCombinedOutputAction{
// iscsiadm -m iface -I <iface> -o show, return test error
func() ([]byte, error) { return []byte(""), errors.New("test error") },
},
}
fexec := fakeexec.FakeExec{
CommandScript: []fakeexec.FakeCommandAction{
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
},
}
plugins := []volume.VolumePlugin{
&iscsiPlugin{
host: nil,
exe: &fexec,
},
}
plugin := plugins[0]
fakeMounter := iscsiDiskMounter{
iscsiDisk: &iscsiDisk{
plugin: plugin.(*iscsiPlugin)},
}
newIface := "192.168.1.10:pv0001"
cloneIface(fakeMounter, newIface)
if fcmd.CombinedOutputCalls != 1 {
t.Errorf("expected 1 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls)
}
}
func TestClonedIfaceUpdateError(t *testing.T) {
fcmd := fakeexec.FakeCmd{
CombinedOutputScript: []fakeexec.FakeCombinedOutputAction{
// iscsiadm -m iface -I <iface> -o show
func() ([]byte, error) {
return []byte("iface.ipaddress = <empty>\niface.transport_name = tcp\niface.initiatorname = <empty>\n"), nil
},
// iscsiadm -m iface -I <newIface> -o new
func() ([]byte, error) { return []byte("New interface 192.168.1.10:pv0001 added"), nil },
// iscsiadm -m iface -I <newIface> -o update -n <key> -v <val>
func() ([]byte, error) { return []byte(""), nil },
func() ([]byte, error) { return []byte(""), errors.New("test error") },
// iscsiadm -m iface -I <newIface> -o delete
func() ([]byte, error) { return []byte(""), nil },
},
}
fexec := fakeexec.FakeExec{
CommandScript: []fakeexec.FakeCommandAction{
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
},
}
plugins := []volume.VolumePlugin{
&iscsiPlugin{
host: nil,
exe: &fexec,
},
}
plugin := plugins[0]
fakeMounter := iscsiDiskMounter{
iscsiDisk: &iscsiDisk{
plugin: plugin.(*iscsiPlugin)},
}
newIface := "192.168.1.10:pv0001"
cloneIface(fakeMounter, newIface)
if fcmd.CombinedOutputCalls != 5 {
t.Errorf("expected 5 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls)
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1274,6 +1274,12 @@ message ISCSIVolumeSource {
// CHAP secret for iSCSI target and initiator authentication
// +optional
optional LocalObjectReference secretRef = 10;
// 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
optional string initiatorName = 12;
}
// Maps a string key to a path within a volume.

View File

@ -16506,7 +16506,7 @@ func (x *ISCSIVolumeSource) CodecEncodeSelf(e *codec1978.Encoder) {
} else {
yysep2 := !z.EncBinary()
yy2arr2 := z.EncBasicHandle().StructToArray
var yyq2 [10]bool
var yyq2 [11]bool
_, _, _ = yysep2, yyq2, yy2arr2
const yyr2 bool = false
yyq2[3] = x.ISCSIInterface != ""
@ -16516,9 +16516,10 @@ func (x *ISCSIVolumeSource) CodecEncodeSelf(e *codec1978.Encoder) {
yyq2[7] = x.DiscoveryCHAPAuth != false
yyq2[8] = x.SessionCHAPAuth != false
yyq2[9] = x.SecretRef != nil
yyq2[10] = x.InitiatorName != nil
var yynn2 int
if yyr2 || yy2arr2 {
r.EncodeArrayStart(10)
r.EncodeArrayStart(11)
} else {
yynn2 = 3
for _, b := range yyq2 {
@ -16767,6 +16768,41 @@ func (x *ISCSIVolumeSource) CodecEncodeSelf(e *codec1978.Encoder) {
}
}
}
if yyr2 || yy2arr2 {
z.EncSendContainerState(codecSelfer_containerArrayElem1234)
if yyq2[10] {
if x.InitiatorName == nil {
r.EncodeNil()
} else {
yy34 := *x.InitiatorName
yym35 := z.EncBinary()
_ = yym35
if false {
} else {
r.EncodeString(codecSelferC_UTF81234, string(yy34))
}
}
} else {
r.EncodeNil()
}
} else {
if yyq2[10] {
z.EncSendContainerState(codecSelfer_containerMapKey1234)
r.EncodeString(codecSelferC_UTF81234, string("initiatorName"))
z.EncSendContainerState(codecSelfer_containerMapValue1234)
if x.InitiatorName == nil {
r.EncodeNil()
} else {
yy36 := *x.InitiatorName
yym37 := z.EncBinary()
_ = yym37
if false {
} else {
r.EncodeString(codecSelferC_UTF81234, string(yy36))
}
}
}
}
if yyr2 || yy2arr2 {
z.EncSendContainerState(codecSelfer_containerArrayEnd1234)
} else {
@ -16947,6 +16983,22 @@ func (x *ISCSIVolumeSource) codecDecodeSelfFromMap(l int, d *codec1978.Decoder)
}
x.SecretRef.CodecDecodeSelf(d)
}
case "initiatorName":
if r.TryDecodeAsNil() {
if x.InitiatorName != nil {
x.InitiatorName = nil
}
} else {
if x.InitiatorName == nil {
x.InitiatorName = new(string)
}
yym24 := z.DecBinary()
_ = yym24
if false {
} else {
*((*string)(x.InitiatorName)) = r.DecodeString()
}
}
default:
z.DecStructFieldNotFound(-1, yys3)
} // end switch yys3
@ -16958,16 +17010,16 @@ func (x *ISCSIVolumeSource) codecDecodeSelfFromArray(l int, d *codec1978.Decoder
var h codecSelfer1234
z, r := codec1978.GenHelperDecoder(d)
_, _, _ = h, z, r
var yyj23 int
var yyb23 bool
var yyhl23 bool = l >= 0
yyj23++
if yyhl23 {
yyb23 = yyj23 > l
var yyj25 int
var yyb25 bool
var yyhl25 bool = l >= 0
yyj25++
if yyhl25 {
yyb25 = yyj25 > l
} else {
yyb23 = r.CheckBreak()
yyb25 = r.CheckBreak()
}
if yyb23 {
if yyb25 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@ -16975,29 +17027,7 @@ func (x *ISCSIVolumeSource) codecDecodeSelfFromArray(l int, d *codec1978.Decoder
if r.TryDecodeAsNil() {
x.TargetPortal = ""
} else {
yyv24 := &x.TargetPortal
yym25 := z.DecBinary()
_ = yym25
if false {
} else {
*((*string)(yyv24)) = r.DecodeString()
}
}
yyj23++
if yyhl23 {
yyb23 = yyj23 > l
} else {
yyb23 = r.CheckBreak()
}
if yyb23 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
z.DecSendContainerState(codecSelfer_containerArrayElem1234)
if r.TryDecodeAsNil() {
x.IQN = ""
} else {
yyv26 := &x.IQN
yyv26 := &x.TargetPortal
yym27 := z.DecBinary()
_ = yym27
if false {
@ -17005,13 +17035,35 @@ func (x *ISCSIVolumeSource) codecDecodeSelfFromArray(l int, d *codec1978.Decoder
*((*string)(yyv26)) = r.DecodeString()
}
}
yyj23++
if yyhl23 {
yyb23 = yyj23 > l
yyj25++
if yyhl25 {
yyb25 = yyj25 > l
} else {
yyb23 = r.CheckBreak()
yyb25 = r.CheckBreak()
}
if yyb23 {
if yyb25 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
z.DecSendContainerState(codecSelfer_containerArrayElem1234)
if r.TryDecodeAsNil() {
x.IQN = ""
} else {
yyv28 := &x.IQN
yym29 := z.DecBinary()
_ = yym29
if false {
} else {
*((*string)(yyv28)) = r.DecodeString()
}
}
yyj25++
if yyhl25 {
yyb25 = yyj25 > l
} else {
yyb25 = r.CheckBreak()
}
if yyb25 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@ -17019,21 +17071,21 @@ func (x *ISCSIVolumeSource) codecDecodeSelfFromArray(l int, d *codec1978.Decoder
if r.TryDecodeAsNil() {
x.Lun = 0
} else {
yyv28 := &x.Lun
yym29 := z.DecBinary()
_ = yym29
yyv30 := &x.Lun
yym31 := z.DecBinary()
_ = yym31
if false {
} else {
*((*int32)(yyv28)) = int32(r.DecodeInt(32))
*((*int32)(yyv30)) = int32(r.DecodeInt(32))
}
}
yyj23++
if yyhl23 {
yyb23 = yyj23 > l
yyj25++
if yyhl25 {
yyb25 = yyj25 > l
} else {
yyb23 = r.CheckBreak()
yyb25 = r.CheckBreak()
}
if yyb23 {
if yyb25 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@ -17041,29 +17093,7 @@ func (x *ISCSIVolumeSource) codecDecodeSelfFromArray(l int, d *codec1978.Decoder
if r.TryDecodeAsNil() {
x.ISCSIInterface = ""
} else {
yyv30 := &x.ISCSIInterface
yym31 := z.DecBinary()
_ = yym31
if false {
} else {
*((*string)(yyv30)) = r.DecodeString()
}
}
yyj23++
if yyhl23 {
yyb23 = yyj23 > l
} else {
yyb23 = r.CheckBreak()
}
if yyb23 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
z.DecSendContainerState(codecSelfer_containerArrayElem1234)
if r.TryDecodeAsNil() {
x.FSType = ""
} else {
yyv32 := &x.FSType
yyv32 := &x.ISCSIInterface
yym33 := z.DecBinary()
_ = yym33
if false {
@ -17071,13 +17101,35 @@ func (x *ISCSIVolumeSource) codecDecodeSelfFromArray(l int, d *codec1978.Decoder
*((*string)(yyv32)) = r.DecodeString()
}
}
yyj23++
if yyhl23 {
yyb23 = yyj23 > l
yyj25++
if yyhl25 {
yyb25 = yyj25 > l
} else {
yyb23 = r.CheckBreak()
yyb25 = r.CheckBreak()
}
if yyb23 {
if yyb25 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
z.DecSendContainerState(codecSelfer_containerArrayElem1234)
if r.TryDecodeAsNil() {
x.FSType = ""
} else {
yyv34 := &x.FSType
yym35 := z.DecBinary()
_ = yym35
if false {
} else {
*((*string)(yyv34)) = r.DecodeString()
}
}
yyj25++
if yyhl25 {
yyb25 = yyj25 > l
} else {
yyb25 = r.CheckBreak()
}
if yyb25 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@ -17085,21 +17137,21 @@ func (x *ISCSIVolumeSource) codecDecodeSelfFromArray(l int, d *codec1978.Decoder
if r.TryDecodeAsNil() {
x.ReadOnly = false
} else {
yyv34 := &x.ReadOnly
yym35 := z.DecBinary()
_ = yym35
yyv36 := &x.ReadOnly
yym37 := z.DecBinary()
_ = yym37
if false {
} else {
*((*bool)(yyv34)) = r.DecodeBool()
*((*bool)(yyv36)) = r.DecodeBool()
}
}
yyj23++
if yyhl23 {
yyb23 = yyj23 > l
yyj25++
if yyhl25 {
yyb25 = yyj25 > l
} else {
yyb23 = r.CheckBreak()
yyb25 = r.CheckBreak()
}
if yyb23 {
if yyb25 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@ -17107,21 +17159,21 @@ func (x *ISCSIVolumeSource) codecDecodeSelfFromArray(l int, d *codec1978.Decoder
if r.TryDecodeAsNil() {
x.Portals = nil
} else {
yyv36 := &x.Portals
yym37 := z.DecBinary()
_ = yym37
yyv38 := &x.Portals
yym39 := z.DecBinary()
_ = yym39
if false {
} else {
z.F.DecSliceStringX(yyv36, false, d)
z.F.DecSliceStringX(yyv38, false, d)
}
}
yyj23++
if yyhl23 {
yyb23 = yyj23 > l
yyj25++
if yyhl25 {
yyb25 = yyj25 > l
} else {
yyb23 = r.CheckBreak()
yyb25 = r.CheckBreak()
}
if yyb23 {
if yyb25 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@ -17129,29 +17181,7 @@ func (x *ISCSIVolumeSource) codecDecodeSelfFromArray(l int, d *codec1978.Decoder
if r.TryDecodeAsNil() {
x.DiscoveryCHAPAuth = false
} else {
yyv38 := &x.DiscoveryCHAPAuth
yym39 := z.DecBinary()
_ = yym39
if false {
} else {
*((*bool)(yyv38)) = r.DecodeBool()
}
}
yyj23++
if yyhl23 {
yyb23 = yyj23 > l
} else {
yyb23 = r.CheckBreak()
}
if yyb23 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
z.DecSendContainerState(codecSelfer_containerArrayElem1234)
if r.TryDecodeAsNil() {
x.SessionCHAPAuth = false
} else {
yyv40 := &x.SessionCHAPAuth
yyv40 := &x.DiscoveryCHAPAuth
yym41 := z.DecBinary()
_ = yym41
if false {
@ -17159,13 +17189,35 @@ func (x *ISCSIVolumeSource) codecDecodeSelfFromArray(l int, d *codec1978.Decoder
*((*bool)(yyv40)) = r.DecodeBool()
}
}
yyj23++
if yyhl23 {
yyb23 = yyj23 > l
yyj25++
if yyhl25 {
yyb25 = yyj25 > l
} else {
yyb23 = r.CheckBreak()
yyb25 = r.CheckBreak()
}
if yyb23 {
if yyb25 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
z.DecSendContainerState(codecSelfer_containerArrayElem1234)
if r.TryDecodeAsNil() {
x.SessionCHAPAuth = false
} else {
yyv42 := &x.SessionCHAPAuth
yym43 := z.DecBinary()
_ = yym43
if false {
} else {
*((*bool)(yyv42)) = r.DecodeBool()
}
}
yyj25++
if yyhl25 {
yyb25 = yyj25 > l
} else {
yyb25 = r.CheckBreak()
}
if yyb25 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
@ -17180,18 +17232,44 @@ func (x *ISCSIVolumeSource) codecDecodeSelfFromArray(l int, d *codec1978.Decoder
}
x.SecretRef.CodecDecodeSelf(d)
}
for {
yyj23++
if yyhl23 {
yyb23 = yyj23 > l
} else {
yyb23 = r.CheckBreak()
yyj25++
if yyhl25 {
yyb25 = yyj25 > l
} else {
yyb25 = r.CheckBreak()
}
if yyb25 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
z.DecSendContainerState(codecSelfer_containerArrayElem1234)
if r.TryDecodeAsNil() {
if x.InitiatorName != nil {
x.InitiatorName = nil
}
if yyb23 {
} else {
if x.InitiatorName == nil {
x.InitiatorName = new(string)
}
yym46 := z.DecBinary()
_ = yym46
if false {
} else {
*((*string)(x.InitiatorName)) = r.DecodeString()
}
}
for {
yyj25++
if yyhl25 {
yyb25 = yyj25 > l
} else {
yyb25 = r.CheckBreak()
}
if yyb25 {
break
}
z.DecSendContainerState(codecSelfer_containerArrayElem1234)
z.DecStructFieldNotFound(yyj23-1, "")
z.DecStructFieldNotFound(yyj25-1, "")
}
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
}

View File

@ -1095,6 +1095,11 @@ type ISCSIVolumeSource struct {
// CHAP secret for iSCSI target and initiator authentication
// +optional
SecretRef *LocalObjectReference `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
InitiatorName *string `json:"initiatorName,omitempty" protobuf:"bytes,12,opt,name=initiatorName"`
}
// Represents a Fibre Channel volume.

View File

@ -674,6 +674,7 @@ var map_ISCSIVolumeSource = map[string]string{
"chapAuthDiscovery": "whether support iSCSI Discovery CHAP authentication",
"chapAuthSession": "whether support iSCSI Session CHAP authentication",
"secretRef": "CHAP secret for iSCSI target and initiator authentication",
"initiatorName": "Custom iSCSI initiator name. If initiatorName is specified with iscsiInterface simultaneously, new iSCSI interface <target portal>:<volume name> will be created for the connection.",
}
func (ISCSIVolumeSource) SwaggerDoc() map[string]string {

View File

@ -2333,6 +2333,15 @@ func (in *ISCSIVolumeSource) DeepCopyInto(out *ISCSIVolumeSource) {
**out = **in
}
}
if in.InitiatorName != nil {
in, out := &in.InitiatorName, &out.InitiatorName
if *in == nil {
*out = nil
} else {
*out = new(string)
**out = **in
}
}
return
}