Merge pull request #71167 from msau42/block-beta

Promote raw block volume support to beta
pull/58/head
k8s-ci-robot 2018-11-16 20:28:03 -08:00 committed by GitHub
commit ec2e767e59
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
42 changed files with 531 additions and 273 deletions

View File

@ -84268,7 +84268,7 @@
"type": "boolean"
},
"volumeDevices": {
"description": "volumeDevices is the list of block devices to be used by the container. This is an alpha feature and may change in the future.",
"description": "volumeDevices is the list of block devices to be used by the container. This is a beta feature.",
"type": "array",
"items": {
"$ref": "#/definitions/io.k8s.api.core.v1.VolumeDevice"
@ -86221,7 +86221,7 @@
"type": "string"
},
"volumeMode": {
"description": "volumeMode defines what type of volume is required by the claim. Value of Filesystem is implied when not included in claim spec. This is an alpha feature and may change in the future.",
"description": "volumeMode defines what type of volume is required by the claim. Value of Filesystem is implied when not included in claim spec. This is a beta feature.",
"type": "string"
},
"volumeName": {
@ -86437,7 +86437,7 @@
"$ref": "#/definitions/io.k8s.api.core.v1.StorageOSPersistentVolumeSource"
},
"volumeMode": {
"description": "volumeMode defines if a volume is intended to be used with a formatted filesystem or to remain in raw block state. Value of Filesystem is implied when not included in spec. This is an alpha feature and may change in the future.",
"description": "volumeMode defines if a volume is intended to be used with a formatted filesystem or to remain in raw block state. Value of Filesystem is implied when not included in spec. This is a beta feature.",
"type": "string"
},
"vsphereVolume": {

View File

@ -8227,7 +8227,7 @@
"items": {
"$ref": "v1.VolumeDevice"
},
"description": "volumeDevices is the list of block devices to be used by the container. This is an alpha feature and may change in the future."
"description": "volumeDevices is the list of block devices to be used by the container. This is a beta feature."
},
"livenessProbe": {
"$ref": "v1.Probe",
@ -9796,7 +9796,7 @@
},
"volumeMode": {
"$ref": "v1.PersistentVolumeMode",
"description": "volumeMode defines what type of volume is required by the claim. Value of Filesystem is implied when not included in claim spec. This is an alpha feature and may change in the future."
"description": "volumeMode defines what type of volume is required by the claim. Value of Filesystem is implied when not included in claim spec. This is a beta feature."
},
"dataSource": {
"$ref": "v1.TypedLocalObjectReference",

View File

@ -5739,7 +5739,7 @@
"items": {
"$ref": "v1.VolumeDevice"
},
"description": "volumeDevices is the list of block devices to be used by the container. This is an alpha feature and may change in the future."
"description": "volumeDevices is the list of block devices to be used by the container. This is a beta feature."
},
"livenessProbe": {
"$ref": "v1.Probe",
@ -6982,7 +6982,7 @@
},
"volumeMode": {
"$ref": "v1.PersistentVolumeMode",
"description": "volumeMode defines what type of volume is required by the claim. Value of Filesystem is implied when not included in claim spec. This is an alpha feature and may change in the future."
"description": "volumeMode defines what type of volume is required by the claim. Value of Filesystem is implied when not included in claim spec. This is a beta feature."
},
"dataSource": {
"$ref": "v1.TypedLocalObjectReference",

View File

@ -8227,7 +8227,7 @@
"items": {
"$ref": "v1.VolumeDevice"
},
"description": "volumeDevices is the list of block devices to be used by the container. This is an alpha feature and may change in the future."
"description": "volumeDevices is the list of block devices to be used by the container. This is a beta feature."
},
"livenessProbe": {
"$ref": "v1.Probe",
@ -9800,7 +9800,7 @@
},
"volumeMode": {
"$ref": "v1.PersistentVolumeMode",
"description": "volumeMode defines what type of volume is required by the claim. Value of Filesystem is implied when not included in claim spec. This is an alpha feature and may change in the future."
"description": "volumeMode defines what type of volume is required by the claim. Value of Filesystem is implied when not included in claim spec. This is a beta feature."
},
"dataSource": {
"$ref": "v1.TypedLocalObjectReference",

View File

@ -2923,7 +2923,7 @@
"items": {
"$ref": "v1.VolumeDevice"
},
"description": "volumeDevices is the list of block devices to be used by the container. This is an alpha feature and may change in the future."
"description": "volumeDevices is the list of block devices to be used by the container. This is a beta feature."
},
"livenessProbe": {
"$ref": "v1.Probe",

View File

@ -2978,7 +2978,7 @@
"items": {
"$ref": "v1.VolumeDevice"
},
"description": "volumeDevices is the list of block devices to be used by the container. This is an alpha feature and may change in the future."
"description": "volumeDevices is the list of block devices to be used by the container. This is a beta feature."
},
"livenessProbe": {
"$ref": "v1.Probe",

View File

@ -2978,7 +2978,7 @@
"items": {
"$ref": "v1.VolumeDevice"
},
"description": "volumeDevices is the list of block devices to be used by the container. This is an alpha feature and may change in the future."
"description": "volumeDevices is the list of block devices to be used by the container. This is a beta feature."
},
"livenessProbe": {
"$ref": "v1.Probe",

View File

@ -8923,7 +8923,7 @@
"items": {
"$ref": "v1.VolumeDevice"
},
"description": "volumeDevices is the list of block devices to be used by the container. This is an alpha feature and may change in the future."
"description": "volumeDevices is the list of block devices to be used by the container. This is a beta feature."
},
"livenessProbe": {
"$ref": "v1.Probe",

View File

@ -19987,7 +19987,7 @@
},
"volumeMode": {
"$ref": "v1.PersistentVolumeMode",
"description": "volumeMode defines what type of volume is required by the claim. Value of Filesystem is implied when not included in claim spec. This is an alpha feature and may change in the future."
"description": "volumeMode defines what type of volume is required by the claim. Value of Filesystem is implied when not included in claim spec. This is a beta feature."
},
"dataSource": {
"$ref": "v1.TypedLocalObjectReference",
@ -20320,7 +20320,7 @@
},
"volumeMode": {
"$ref": "v1.PersistentVolumeMode",
"description": "volumeMode defines if a volume is intended to be used with a formatted filesystem or to remain in raw block state. Value of Filesystem is implied when not included in spec. This is an alpha feature and may change in the future."
"description": "volumeMode defines if a volume is intended to be used with a formatted filesystem or to remain in raw block state. Value of Filesystem is implied when not included in spec. This is a beta feature."
},
"nodeAffinity": {
"$ref": "v1.VolumeNodeAffinity",
@ -22143,7 +22143,7 @@
"items": {
"$ref": "v1.VolumeDevice"
},
"description": "volumeDevices is the list of block devices to be used by the container. This is an alpha feature and may change in the future."
"description": "volumeDevices is the list of block devices to be used by the container. This is a beta feature."
},
"livenessProbe": {
"$ref": "v1.Probe",

View File

@ -1012,7 +1012,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">volumeMode</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">volumeMode defines what type of volume is required by the claim. Value of Filesystem is implied when not included in claim spec. This is an alpha feature and may change in the future.</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">volumeMode defines what type of volume is required by the claim. Value of Filesystem is implied when not included in claim spec. This is a beta feature.</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"><a href="#_v1_persistentvolumemode">v1.PersistentVolumeMode</a></p></td>
<td class="tableblock halign-left valign-top"></td>
@ -5718,7 +5718,7 @@ Examples:<br>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">volumeDevices</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">volumeDevices is the list of block devices to be used by the container. This is an alpha feature and may change in the future.</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">volumeDevices is the list of block devices to be used by the container. This is a beta feature.</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"><a href="#_v1_volumedevice">v1.VolumeDevice</a> array</p></td>
<td class="tableblock halign-left valign-top"></td>

View File

@ -1040,7 +1040,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">volumeMode</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">volumeMode defines what type of volume is required by the claim. Value of Filesystem is implied when not included in claim spec. This is an alpha feature and may change in the future.</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">volumeMode defines what type of volume is required by the claim. Value of Filesystem is implied when not included in claim spec. This is a beta feature.</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"><a href="#_v1_persistentvolumemode">v1.PersistentVolumeMode</a></p></td>
<td class="tableblock halign-left valign-top"></td>
@ -5853,7 +5853,7 @@ Examples:<br>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">volumeDevices</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">volumeDevices is the list of block devices to be used by the container. This is an alpha feature and may change in the future.</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">volumeDevices is the list of block devices to be used by the container. This is a beta feature.</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"><a href="#_v1_volumedevice">v1.VolumeDevice</a> array</p></td>
<td class="tableblock halign-left valign-top"></td>

View File

@ -987,7 +987,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">volumeMode</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">volumeMode defines what type of volume is required by the claim. Value of Filesystem is implied when not included in claim spec. This is an alpha feature and may change in the future.</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">volumeMode defines what type of volume is required by the claim. Value of Filesystem is implied when not included in claim spec. This is a beta feature.</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"><a href="#_v1_persistentvolumemode">v1.PersistentVolumeMode</a></p></td>
<td class="tableblock halign-left valign-top"></td>
@ -6187,7 +6187,7 @@ Examples:<br>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">volumeDevices</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">volumeDevices is the list of block devices to be used by the container. This is an alpha feature and may change in the future.</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">volumeDevices is the list of block devices to be used by the container. This is a beta feature.</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"><a href="#_v1_volumedevice">v1.VolumeDevice</a> array</p></td>
<td class="tableblock halign-left valign-top"></td>

View File

@ -4610,7 +4610,7 @@ Examples:<br>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">volumeDevices</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">volumeDevices is the list of block devices to be used by the container. This is an alpha feature and may change in the future.</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">volumeDevices is the list of block devices to be used by the container. This is a beta feature.</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"><a href="#_v1_volumedevice">v1.VolumeDevice</a> array</p></td>
<td class="tableblock halign-left valign-top"></td>

View File

@ -4761,7 +4761,7 @@ Examples:<br>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">volumeDevices</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">volumeDevices is the list of block devices to be used by the container. This is an alpha feature and may change in the future.</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">volumeDevices is the list of block devices to be used by the container. This is a beta feature.</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"><a href="#_v1_volumedevice">v1.VolumeDevice</a> array</p></td>
<td class="tableblock halign-left valign-top"></td>

View File

@ -4617,7 +4617,7 @@ Examples:<br>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">volumeDevices</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">volumeDevices is the list of block devices to be used by the container. This is an alpha feature and may change in the future.</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">volumeDevices is the list of block devices to be used by the container. This is a beta feature.</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"><a href="#_v1_volumedevice">v1.VolumeDevice</a> array</p></td>
<td class="tableblock halign-left valign-top"></td>

View File

@ -6565,7 +6565,7 @@ If PodSelector is also set, then the NetworkPolicyPeer as a whole selects the Po
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">volumeDevices</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">volumeDevices is the list of block devices to be used by the container. This is an alpha feature and may change in the future.</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">volumeDevices is the list of block devices to be used by the container. This is a beta feature.</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"><a href="#_v1_volumedevice">v1.VolumeDevice</a> array</p></td>
<td class="tableblock halign-left valign-top"></td>

View File

@ -689,7 +689,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">volumeMode</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">volumeMode defines what type of volume is required by the claim. Value of Filesystem is implied when not included in claim spec. This is an alpha feature and may change in the future.</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">volumeMode defines what type of volume is required by the claim. Value of Filesystem is implied when not included in claim spec. This is a beta feature.</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"><a href="#_v1_persistentvolumemode">v1.PersistentVolumeMode</a></p></td>
<td class="tableblock halign-left valign-top"></td>
@ -4282,7 +4282,7 @@ Examples:<br>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">volumeMode</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">volumeMode defines if a volume is intended to be used with a formatted filesystem or to remain in raw block state. Value of Filesystem is implied when not included in spec. This is an alpha feature and may change in the future.</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">volumeMode defines if a volume is intended to be used with a formatted filesystem or to remain in raw block state. Value of Filesystem is implied when not included in spec. This is a beta feature.</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"><a href="#_v1_persistentvolumemode">v1.PersistentVolumeMode</a></p></td>
<td class="tableblock halign-left valign-top"></td>
@ -10595,7 +10595,7 @@ More info: <a href="https://kubernetes.io/docs/concepts/workloads/pods/pod-lifec
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">volumeDevices</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">volumeDevices is the list of block devices to be used by the container. This is an alpha feature and may change in the future.</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">volumeDevices is the list of block devices to be used by the container. This is a beta feature.</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"><a href="#_v1_volumedevice">v1.VolumeDevice</a> array</p></td>
<td class="tableblock halign-left valign-top"></td>

View File

@ -403,11 +403,15 @@ var Funcs = func(codecs runtimeserializer.CodecFactory) []interface{} {
pv.Status.Message = c.RandString()
reclamationPolicies := []core.PersistentVolumeReclaimPolicy{core.PersistentVolumeReclaimRecycle, core.PersistentVolumeReclaimRetain}
pv.Spec.PersistentVolumeReclaimPolicy = reclamationPolicies[c.Rand.Intn(len(reclamationPolicies))]
volumeModes := []core.PersistentVolumeMode{core.PersistentVolumeFilesystem, core.PersistentVolumeBlock}
pv.Spec.VolumeMode = &volumeModes[c.Rand.Intn(len(volumeModes))]
},
func(pvc *core.PersistentVolumeClaim, c fuzz.Continue) {
c.FuzzNoCustom(pvc) // fuzz self without calling this function again
types := []core.PersistentVolumeClaimPhase{core.ClaimBound, core.ClaimPending, core.ClaimLost}
pvc.Status.Phase = types[c.Rand.Intn(len(types))]
volumeModes := []core.PersistentVolumeMode{core.PersistentVolumeFilesystem, core.PersistentVolumeBlock}
pvc.Spec.VolumeMode = &volumeModes[c.Rand.Intn(len(volumeModes))]
},
func(obj *core.AzureDiskVolumeSource, c fuzz.Continue) {
if obj.CachingMode == nil {

View File

@ -298,7 +298,7 @@ type PersistentVolumeSpec struct {
MountOptions []string
// volumeMode defines if a volume is intended to be used with a formatted filesystem
// or to remain in raw block state. Value of Filesystem is implied when not included in spec.
// This is an alpha feature and may change in the future.
// This is a beta feature.
// +optional
VolumeMode *PersistentVolumeMode
// NodeAffinity defines constraints that limit what nodes this volume can be accessed from.
@ -410,7 +410,7 @@ type PersistentVolumeClaimSpec struct {
StorageClassName *string
// volumeMode defines what type of volume is required by the claim.
// Value of Filesystem is implied when not included in claim spec.
// This is an alpha feature and may change in the future.
// This is a beta feature.
// +optional
VolumeMode *PersistentVolumeMode
// This field requires the VolumeSnapshotDataSource alpha feature gate to be
@ -1968,7 +1968,7 @@ type Container struct {
// +optional
VolumeMounts []VolumeMount
// volumeDevices is the list of block devices to be used by the container.
// This is an alpha feature and may change in the future.
// This is a beta feature.
// +optional
VolumeDevices []VolumeDevice
// +optional

View File

@ -27,8 +27,10 @@ import (
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/intstr"
utilfeature "k8s.io/apiserver/pkg/util/feature"
utilfeaturetesting "k8s.io/apiserver/pkg/util/feature/testing"
"k8s.io/kubernetes/pkg/api/legacyscheme"
corev1 "k8s.io/kubernetes/pkg/apis/core/v1"
"k8s.io/kubernetes/pkg/features"
utilpointer "k8s.io/utils/pointer"
// enforce that all types are installed
@ -803,6 +805,7 @@ func TestSetDefaultSecret(t *testing.T) {
}
func TestSetDefaultPersistentVolume(t *testing.T) {
defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.BlockVolume, false)()
pv := &v1.PersistentVolume{}
obj2 := roundTrip(t, runtime.Object(pv))
pv2 := obj2.(*v1.PersistentVolume)
@ -822,10 +825,7 @@ func TestSetDefaultPersistentVolume(t *testing.T) {
}
// When feature gate is enabled, field should be defaulted
err := utilfeature.DefaultFeatureGate.Set("BlockVolume=true")
if err != nil {
t.Fatalf("Failed to enable feature gate for BlockVolume: %v", err)
}
defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.BlockVolume, true)()
obj3 := roundTrip(t, runtime.Object(pv)).(*v1.PersistentVolume)
outputMode3 := obj3.Spec.VolumeMode
@ -834,15 +834,10 @@ func TestSetDefaultPersistentVolume(t *testing.T) {
} else if *outputMode3 != defaultMode {
t.Errorf("Expected VolumeMode to be defaulted to: %+v, got: %+v", defaultMode, outputMode3)
}
err = utilfeature.DefaultFeatureGate.Set("BlockVolume=false")
if err != nil {
t.Fatalf("Failed to disable feature gate for BlockVolume: %v", err)
}
}
func TestSetDefaultPersistentVolumeClaim(t *testing.T) {
defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.BlockVolume, false)()
pvc := &v1.PersistentVolumeClaim{}
obj2 := roundTrip(t, runtime.Object(pvc))
pvc2 := obj2.(*v1.PersistentVolumeClaim)
@ -859,10 +854,7 @@ func TestSetDefaultPersistentVolumeClaim(t *testing.T) {
}
// When feature gate is enabled, field should be defaulted
err := utilfeature.DefaultFeatureGate.Set("BlockVolume=true")
if err != nil {
t.Fatalf("Failed to enable feature gate for BlockVolume: %v", err)
}
defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.BlockVolume, true)()
obj3 := roundTrip(t, runtime.Object(pvc)).(*v1.PersistentVolumeClaim)
outputMode3 := obj3.Spec.VolumeMode
@ -871,11 +863,6 @@ func TestSetDefaultPersistentVolumeClaim(t *testing.T) {
} else if *outputMode3 != defaultMode {
t.Errorf("Expected VolumeMode to be defaulted to: %+v, got: %+v", defaultMode, outputMode3)
}
err = utilfeature.DefaultFeatureGate.Set("BlockVolume=false")
if err != nil {
t.Fatalf("Failed to disable feature gate for BlockVolume: %v", err)
}
}
func TestSetDefaulEndpointsProtocol(t *testing.T) {

View File

@ -66,9 +66,11 @@ func testVolume(name string, namespace string, spec core.PersistentVolumeSpec) *
func TestValidatePersistentVolumes(t *testing.T) {
validMode := core.PersistentVolumeFilesystem
invalidMode := core.PersistentVolumeMode("fakeVolumeMode")
scenarios := map[string]struct {
isExpectedFailure bool
volume *core.PersistentVolume
disableBlock bool
}{
"good-volume": {
isExpectedFailure: false,
@ -147,6 +149,22 @@ func TestValidatePersistentVolumes(t *testing.T) {
PersistentVolumeReclaimPolicy: core.PersistentVolumeReclaimRetain,
}),
},
"good-volume-with-volume-mode": {
isExpectedFailure: false,
volume: testVolume("foo", "", core.PersistentVolumeSpec{
Capacity: core.ResourceList{
core.ResourceName(core.ResourceStorage): resource.MustParse("10G"),
},
AccessModes: []core.PersistentVolumeAccessMode{core.ReadWriteOnce},
PersistentVolumeSource: core.PersistentVolumeSource{
HostPath: &core.HostPathVolumeSource{
Path: "/foo",
Type: newHostPathType(string(core.HostPathDirectory)),
},
},
VolumeMode: &validMode,
}),
},
"invalid-accessmode": {
isExpectedFailure: true,
volume: testVolume("foo", "", core.PersistentVolumeSpec{
@ -178,6 +196,22 @@ func TestValidatePersistentVolumes(t *testing.T) {
PersistentVolumeReclaimPolicy: "fakeReclaimPolicy",
}),
},
"invalid-volume-mode": {
isExpectedFailure: true,
volume: testVolume("foo", "", core.PersistentVolumeSpec{
Capacity: core.ResourceList{
core.ResourceName(core.ResourceStorage): resource.MustParse("10G"),
},
AccessModes: []core.PersistentVolumeAccessMode{core.ReadWriteOnce},
PersistentVolumeSource: core.PersistentVolumeSource{
HostPath: &core.HostPathVolumeSource{
Path: "/foo",
Type: newHostPathType(string(core.HostPathDirectory)),
},
},
VolumeMode: &invalidMode,
}),
},
"unexpected-namespace": {
isExpectedFailure: true,
volume: testVolume("foo", "unexpected-namespace", core.PersistentVolumeSpec{
@ -336,9 +370,8 @@ func TestValidatePersistentVolumes(t *testing.T) {
StorageClassName: "-invalid-",
}),
},
// VolumeMode alpha feature disabled
// TODO: remove when no longer alpha
"alpha disabled valid volume mode": {
"feature disabled valid volume mode": {
disableBlock: true,
isExpectedFailure: true,
volume: testVolume("foo", "", core.PersistentVolumeSpec{
Capacity: core.ResourceList{
@ -400,13 +433,16 @@ func TestValidatePersistentVolumes(t *testing.T) {
}
for name, scenario := range scenarios {
errs := ValidatePersistentVolume(scenario.volume)
if len(errs) == 0 && scenario.isExpectedFailure {
t.Errorf("Unexpected success for scenario: %s", name)
}
if len(errs) > 0 && !scenario.isExpectedFailure {
t.Errorf("Unexpected failure for scenario: %s - %+v", name, errs)
}
t.Run(name, func(t *testing.T) {
defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.BlockVolume, !scenario.disableBlock)()
errs := ValidatePersistentVolume(scenario.volume)
if len(errs) == 0 && scenario.isExpectedFailure {
t.Errorf("Unexpected success for scenario: %s", name)
}
if len(errs) > 0 && !scenario.isExpectedFailure {
t.Errorf("Unexpected failure for scenario: %s - %+v", name, errs)
}
})
}
}
@ -791,10 +827,12 @@ func testVolumeClaimStorageClassInAnnotationAndSpec(name, namespace, scNameInAnn
func TestValidatePersistentVolumeClaim(t *testing.T) {
invalidClassName := "-invalid-"
validClassName := "valid"
invalidMode := core.PersistentVolumeMode("fakeVolumeMode")
validMode := core.PersistentVolumeFilesystem
scenarios := map[string]struct {
isExpectedFailure bool
claim *core.PersistentVolumeClaim
disableBlock bool
}{
"good-claim": {
isExpectedFailure: false,
@ -817,6 +855,7 @@ func TestValidatePersistentVolumeClaim(t *testing.T) {
},
},
StorageClassName: &validClassName,
VolumeMode: &validMode,
}),
},
"invalid-claim-zero-capacity": {
@ -988,9 +1027,8 @@ func TestValidatePersistentVolumeClaim(t *testing.T) {
StorageClassName: &invalidClassName,
}),
},
// VolumeMode alpha feature disabled
// TODO: remove when no longer alpha
"disabled alpha valid volume mode": {
"feature disabled valid volume mode": {
disableBlock: true,
isExpectedFailure: true,
claim: testVolumeClaim("foo", "ns", core.PersistentVolumeClaimSpec{
Selector: &metav1.LabelSelector{
@ -1014,16 +1052,34 @@ func TestValidatePersistentVolumeClaim(t *testing.T) {
VolumeMode: &validMode,
}),
},
"invalid-volume-mode": {
isExpectedFailure: true,
claim: testVolumeClaim("foo", "ns", core.PersistentVolumeClaimSpec{
AccessModes: []core.PersistentVolumeAccessMode{
core.ReadWriteOnce,
core.ReadOnlyMany,
},
Resources: core.ResourceRequirements{
Requests: core.ResourceList{
core.ResourceName(core.ResourceStorage): resource.MustParse("10G"),
},
},
VolumeMode: &invalidMode,
}),
},
}
for name, scenario := range scenarios {
errs := ValidatePersistentVolumeClaim(scenario.claim)
if len(errs) == 0 && scenario.isExpectedFailure {
t.Errorf("Unexpected success for scenario: %s", name)
}
if len(errs) > 0 && !scenario.isExpectedFailure {
t.Errorf("Unexpected failure for scenario: %s - %+v", name, errs)
}
t.Run(name, func(t *testing.T) {
defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.BlockVolume, !scenario.disableBlock)()
errs := ValidatePersistentVolumeClaim(scenario.claim)
if len(errs) == 0 && scenario.isExpectedFailure {
t.Errorf("Unexpected success for scenario: %s", name)
}
if len(errs) > 0 && !scenario.isExpectedFailure {
t.Errorf("Unexpected failure for scenario: %s - %+v", name, errs)
}
})
}
}
@ -1106,15 +1162,17 @@ func TestAlphaPVVolumeModeUpdate(t *testing.T) {
}
for name, scenario := range scenarios {
// ensure we have a resource version specified for updates
toggleBlockVolumeFeature(scenario.enableBlock, t)
errs := ValidatePersistentVolumeUpdate(scenario.newPV, scenario.oldPV)
if len(errs) == 0 && scenario.isExpectedFailure {
t.Errorf("Unexpected success for scenario: %s", name)
}
if len(errs) > 0 && !scenario.isExpectedFailure {
t.Errorf("Unexpected failure for scenario: %s - %+v", name, errs)
}
t.Run(name, func(t *testing.T) {
// ensure we have a resource version specified for updates
defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.BlockVolume, scenario.enableBlock)()
errs := ValidatePersistentVolumeUpdate(scenario.newPV, scenario.oldPV)
if len(errs) == 0 && scenario.isExpectedFailure {
t.Errorf("Unexpected success for scenario: %s", name)
}
if len(errs) > 0 && !scenario.isExpectedFailure {
t.Errorf("Unexpected failure for scenario: %s - %+v", name, errs)
}
})
}
}
@ -1558,8 +1616,7 @@ func TestValidatePersistentVolumeClaimUpdate(t *testing.T) {
t.Run(name, func(t *testing.T) {
// ensure we have a resource version specified for updates
defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ExpandPersistentVolumes, scenario.enableResize)()
toggleBlockVolumeFeature(scenario.enableBlock, t)
defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.BlockVolume, scenario.enableBlock)()
scenario.oldClaim.ResourceVersion = "1"
scenario.newClaim.ResourceVersion = "1"
errs := ValidatePersistentVolumeClaimUpdate(scenario.newClaim, scenario.oldClaim)
@ -1573,23 +1630,6 @@ func TestValidatePersistentVolumeClaimUpdate(t *testing.T) {
}
}
func toggleBlockVolumeFeature(toggleFlag bool, t *testing.T) {
if toggleFlag {
// Enable alpha feature BlockVolume
err := utilfeature.DefaultFeatureGate.Set("BlockVolume=true")
if err != nil {
t.Errorf("Failed to enable feature gate for BlockVolume: %v", err)
return
}
} else {
err := utilfeature.DefaultFeatureGate.Set("BlockVolume=false")
if err != nil {
t.Errorf("Failed to disable feature gate for BlockVolume: %v", err)
return
}
}
}
func TestValidateKeyToPath(t *testing.T) {
testCases := []struct {
kp core.KeyToPath
@ -3880,13 +3920,9 @@ func TestAlphaHugePagesIsolation(t *testing.T) {
}
}
func TestAlphaPVCVolumeMode(t *testing.T) {
// Enable alpha feature BlockVolume for PVC
err := utilfeature.DefaultFeatureGate.Set("BlockVolume=true")
if err != nil {
t.Errorf("Failed to enable feature gate for BlockVolume: %v", err)
return
}
func TestPVCVolumeMode(t *testing.T) {
// Enable feature BlockVolume for PVC
defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.BlockVolume, true)()
block := core.PersistentVolumeBlock
file := core.PersistentVolumeFilesystem
@ -3917,13 +3953,9 @@ func TestAlphaPVCVolumeMode(t *testing.T) {
}
}
func TestAlphaPVVolumeMode(t *testing.T) {
// Enable alpha feature BlockVolume for PV
err := utilfeature.DefaultFeatureGate.Set("BlockVolume=true")
if err != nil {
t.Errorf("Failed to enable feature gate for BlockVolume: %v", err)
return
}
func TestPVVolumeMode(t *testing.T) {
// Enable feature BlockVolume for PVC
defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.BlockVolume, true)()
block := core.PersistentVolumeBlock
file := core.PersistentVolumeFilesystem
@ -5088,12 +5120,8 @@ func TestAlphaValidateVolumeDevices(t *testing.T) {
{Name: "abc-123", MountPath: "/this/path/exists"},
}
// enable Alpha BlockVolume
err1 := utilfeature.DefaultFeatureGate.Set("BlockVolume=true")
if err1 != nil {
t.Errorf("Failed to enable feature gate for BlockVolume: %v", err1)
return
}
// enable BlockVolume
defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.BlockVolume, true)()
// Success Cases:
// Validate normal success cases - only PVC volumeSource
if errs := ValidateVolumeDevices(successCase, GetVolumeMountMap(goodVolumeMounts), vols, field.NewPath("field")); len(errs) != 0 {
@ -5108,12 +5136,8 @@ func TestAlphaValidateVolumeDevices(t *testing.T) {
}
}
// disable Alpha BlockVolume
err2 := utilfeature.DefaultFeatureGate.Set("BlockVolume=false")
if err2 != nil {
t.Errorf("Failed to disable feature gate for BlockVolume: %v", err2)
return
}
// disable BlockVolume
defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.BlockVolume, false)()
if errs := ValidateVolumeDevices(disabledAlphaVolDevice, GetVolumeMountMap(goodVolumeMounts), vols, field.NewPath("field")); len(errs) == 0 {
t.Errorf("expected failure: %v", errs)
}

View File

@ -23,6 +23,8 @@ import (
storage "k8s.io/api/storage/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
utilfeature "k8s.io/apiserver/pkg/util/feature"
utilfeaturetesting "k8s.io/apiserver/pkg/util/feature/testing"
"k8s.io/kubernetes/pkg/features"
)
// Test single call to syncClaim and syncVolume methods.
@ -35,8 +37,6 @@ func TestSync(t *testing.T) {
"foo": "true",
"bar": "false",
}
modeBlock := v1.PersistentVolumeBlock
modeFile := v1.PersistentVolumeFilesystem
tests := []controllerTest{
// [Unit test set 1] User did not care which PV they get.
@ -589,9 +589,22 @@ func TestSync(t *testing.T) {
newClaimArray("claim13-5", "uid13-5", "1Gi", "volume13-5", v1.ClaimBound, nil, annBoundByController, annBindCompleted),
noevents, noerrors, testSyncClaim,
},
}
// All of these should bind as feature set is not enabled for BlockVolume
// meaning volumeMode will be ignored and dropped
runSyncTests(t, tests, []*storage.StorageClass{
{
ObjectMeta: metav1.ObjectMeta{Name: classWait},
VolumeBindingMode: &modeWait,
},
}, []*v1.Pod{})
}
func TestSyncBlockVolumeDisabled(t *testing.T) {
modeBlock := v1.PersistentVolumeBlock
modeFile := v1.PersistentVolumeFilesystem
// All of these should bind as feature set is not enabled for BlockVolume
// meaning volumeMode will be ignored and dropped
tests := []controllerTest{
{
// syncVolume binds a requested block claim to a block volume
"14-1 - binding to volumeMode block",
@ -639,6 +652,7 @@ func TestSync(t *testing.T) {
},
}
defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.BlockVolume, false)()
runSyncTests(t, tests, []*storage.StorageClass{
{
ObjectMeta: metav1.ObjectMeta{Name: classWait},
@ -647,7 +661,7 @@ func TestSync(t *testing.T) {
}, []*v1.Pod{})
}
func TestSyncAlphaBlockVolume(t *testing.T) {
func TestSyncBlockVolume(t *testing.T) {
modeBlock := v1.PersistentVolumeBlock
modeFile := v1.PersistentVolumeFilesystem
@ -827,12 +841,7 @@ func TestSyncAlphaBlockVolume(t *testing.T) {
},
}
err := utilfeature.DefaultFeatureGate.Set("BlockVolume=true")
if err != nil {
t.Errorf("Failed to enable feature gate for BlockVolume: %v", err)
return
}
defer utilfeature.DefaultFeatureGate.Set("BlockVolume=false")
defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.BlockVolume, true)()
runSyncTests(t, tests, []*storage.StorageClass{}, []*v1.Pod{})
}

View File

@ -38,6 +38,7 @@ import (
"k8s.io/apimachinery/pkg/util/diff"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/apimachinery/pkg/watch"
utilfeature "k8s.io/apiserver/pkg/util/feature"
"k8s.io/client-go/informers"
clientset "k8s.io/client-go/kubernetes"
"k8s.io/client-go/kubernetes/fake"
@ -48,6 +49,7 @@ import (
"k8s.io/client-go/tools/record"
"k8s.io/kubernetes/pkg/api/testapi"
"k8s.io/kubernetes/pkg/controller"
"k8s.io/kubernetes/pkg/features"
vol "k8s.io/kubernetes/pkg/volume"
"k8s.io/kubernetes/pkg/volume/util/recyclerclient"
)
@ -179,6 +181,12 @@ func (r *volumeReactor) React(action core.Action) (handled bool, ret runtime.Obj
return true, nil, fmt.Errorf("Cannot create volume %s: volume already exists", volume.Name)
}
// mimic apiserver defaulting
if volume.Spec.VolumeMode == nil && utilfeature.DefaultFeatureGate.Enabled(features.BlockVolume) {
volume.Spec.VolumeMode = new(v1.PersistentVolumeMode)
*volume.Spec.VolumeMode = v1.PersistentVolumeFilesystem
}
// Store the updated object to appropriate places.
r.volumes[volume.Name] = volume
r.changedObjects = append(r.changedObjects, volume)
@ -630,6 +638,7 @@ func newTestController(kubeClient clientset.Interface, informerFactory informers
// newVolume returns a new volume with given attributes
func newVolume(name, capacity, boundToClaimUID, boundToClaimName string, phase v1.PersistentVolumePhase, reclaimPolicy v1.PersistentVolumeReclaimPolicy, class string, annotations ...string) *v1.PersistentVolume {
fs := v1.PersistentVolumeFilesystem
volume := v1.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{
Name: name,
@ -645,6 +654,7 @@ func newVolume(name, capacity, boundToClaimUID, boundToClaimName string, phase v
AccessModes: []v1.PersistentVolumeAccessMode{v1.ReadWriteOnce, v1.ReadOnlyMany},
PersistentVolumeReclaimPolicy: reclaimPolicy,
StorageClassName: class,
VolumeMode: &fs,
},
Status: v1.PersistentVolumeStatus{
Phase: phase,
@ -740,6 +750,7 @@ func newVolumeArray(name, capacity, boundToClaimUID, boundToClaimName string, ph
// newClaim returns a new claim with given attributes
func newClaim(name, claimUID, capacity, boundToVolume string, phase v1.PersistentVolumeClaimPhase, class *string, annotations ...string) *v1.PersistentVolumeClaim {
fs := v1.PersistentVolumeFilesystem
claim := v1.PersistentVolumeClaim{
ObjectMeta: metav1.ObjectMeta{
Name: name,
@ -756,6 +767,7 @@ func newClaim(name, claimUID, capacity, boundToVolume string, phase v1.Persisten
},
VolumeName: boundToVolume,
StorageClassName: class,
VolumeMode: &fs,
},
Status: v1.PersistentVolumeClaimStatus{
Phase: phase,
@ -1226,6 +1238,7 @@ func (plugin *mockVolumePlugin) Provision(selectedNode *v1.Node, allowedTopologi
Phase: v1.VolumeAvailable,
},
}
pv.Spec.VolumeMode = plugin.provisionOptions.PVC.Spec.VolumeMode
}
plugin.provisionCallCounter++

View File

@ -158,13 +158,13 @@ func findMatchingVolume(
volumeQty := volume.Spec.Capacity[v1.ResourceStorage]
// check if volumeModes do not match (Alpha and feature gate protected)
isMisMatch, err := checkVolumeModeMisMatches(&claim.Spec, &volume.Spec)
// check if volumeModes do not match (feature gate protected)
isMismatch, err := checkVolumeModeMismatches(&claim.Spec, &volume.Spec)
if err != nil {
return nil, fmt.Errorf("error checking if volumeMode was a mismatch: %v", err)
}
// filter out mismatching volumeModes
if isMisMatch {
if isMismatch {
continue
}
@ -258,25 +258,24 @@ func findMatchingVolume(
return nil, nil
}
// checkVolumeModeMatches is a convenience method that checks volumeMode for PersistentVolume
// and PersistentVolumeClaims along with making sure that the Alpha feature gate BlockVolume is
// enabled.
// This is Alpha and could change in the future.
func checkVolumeModeMisMatches(pvcSpec *v1.PersistentVolumeClaimSpec, pvSpec *v1.PersistentVolumeSpec) (bool, error) {
if utilfeature.DefaultFeatureGate.Enabled(features.BlockVolume) {
if pvSpec.VolumeMode != nil && pvcSpec.VolumeMode != nil {
requestedVolumeMode := *pvcSpec.VolumeMode
pvVolumeMode := *pvSpec.VolumeMode
return requestedVolumeMode != pvVolumeMode, nil
} else {
// This also should retrun an error, this means that
// the defaulting has failed.
return true, fmt.Errorf("api defaulting for volumeMode failed")
}
} else {
// feature gate is disabled
// checkVolumeModeMismatches is a convenience method that checks volumeMode for PersistentVolume
// and PersistentVolumeClaims
func checkVolumeModeMismatches(pvcSpec *v1.PersistentVolumeClaimSpec, pvSpec *v1.PersistentVolumeSpec) (bool, error) {
if !utilfeature.DefaultFeatureGate.Enabled(features.BlockVolume) {
return false, nil
}
// In HA upgrades, we cannot guarantee that the apiserver is on a version >= controller-manager.
// So we default a nil volumeMode to filesystem
requestedVolumeMode := v1.PersistentVolumeFilesystem
if pvcSpec.VolumeMode != nil {
requestedVolumeMode = *pvcSpec.VolumeMode
}
pvVolumeMode := v1.PersistentVolumeFilesystem
if pvSpec.VolumeMode != nil {
pvVolumeMode = *pvSpec.VolumeMode
}
return requestedVolumeMode != pvVolumeMode, nil
}
// findBestMatchForClaim is a convenience method that finds a volume by the claim's AccessModes and requests for Storage

View File

@ -33,6 +33,7 @@ import (
)
func makePVC(size string, modfn func(*v1.PersistentVolumeClaim)) *v1.PersistentVolumeClaim {
fs := v1.PersistentVolumeFilesystem
pvc := v1.PersistentVolumeClaim{
ObjectMeta: metav1.ObjectMeta{
Name: "claim01",
@ -45,6 +46,7 @@ func makePVC(size string, modfn func(*v1.PersistentVolumeClaim)) *v1.PersistentV
v1.ResourceName(v1.ResourceStorage): resource.MustParse(size),
},
},
VolumeMode: &fs,
},
}
if modfn != nil {
@ -197,6 +199,7 @@ func TestMatchVolume(t *testing.T) {
}
func TestMatchingWithBoundVolumes(t *testing.T) {
fs := v1.PersistentVolumeFilesystem
volumeIndex := newPersistentVolumeOrderedIndex()
// two similar volumes, one is bound
pv1 := &v1.PersistentVolume{
@ -213,7 +216,8 @@ func TestMatchingWithBoundVolumes(t *testing.T) {
},
AccessModes: []v1.PersistentVolumeAccessMode{v1.ReadWriteOnce, v1.ReadOnlyMany},
// this one we're pretending is already bound
ClaimRef: &v1.ObjectReference{UID: "abc123"},
ClaimRef: &v1.ObjectReference{UID: "abc123"},
VolumeMode: &fs,
},
Status: v1.PersistentVolumeStatus{
Phase: v1.VolumeBound,
@ -233,6 +237,7 @@ func TestMatchingWithBoundVolumes(t *testing.T) {
GCEPersistentDisk: &v1.GCEPersistentDiskVolumeSource{},
},
AccessModes: []v1.PersistentVolumeAccessMode{v1.ReadWriteOnce, v1.ReadOnlyMany},
VolumeMode: &fs,
},
Status: v1.PersistentVolumeStatus{
Phase: v1.VolumeAvailable,
@ -254,6 +259,7 @@ func TestMatchingWithBoundVolumes(t *testing.T) {
v1.ResourceName(v1.ResourceStorage): resource.MustParse("1G"),
},
},
VolumeMode: &fs,
},
}
@ -328,6 +334,7 @@ func TestAllPossibleAccessModes(t *testing.T) {
}
func TestFindingVolumeWithDifferentAccessModes(t *testing.T) {
fs := v1.PersistentVolumeFilesystem
gce := &v1.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{UID: "001", Name: "gce"},
Spec: v1.PersistentVolumeSpec{
@ -337,6 +344,7 @@ func TestFindingVolumeWithDifferentAccessModes(t *testing.T) {
v1.ReadWriteOnce,
v1.ReadOnlyMany,
},
VolumeMode: &fs,
},
Status: v1.PersistentVolumeStatus{
Phase: v1.VolumeAvailable,
@ -351,6 +359,7 @@ func TestFindingVolumeWithDifferentAccessModes(t *testing.T) {
AccessModes: []v1.PersistentVolumeAccessMode{
v1.ReadWriteOnce,
},
VolumeMode: &fs,
},
Status: v1.PersistentVolumeStatus{
Phase: v1.VolumeAvailable,
@ -367,6 +376,7 @@ func TestFindingVolumeWithDifferentAccessModes(t *testing.T) {
v1.ReadOnlyMany,
v1.ReadWriteMany,
},
VolumeMode: &fs,
},
Status: v1.PersistentVolumeStatus{
Phase: v1.VolumeAvailable,
@ -381,6 +391,7 @@ func TestFindingVolumeWithDifferentAccessModes(t *testing.T) {
Spec: v1.PersistentVolumeClaimSpec{
AccessModes: []v1.PersistentVolumeAccessMode{v1.ReadWriteOnce},
Resources: v1.ResourceRequirements{Requests: v1.ResourceList{v1.ResourceName(v1.ResourceStorage): resource.MustParse("1G")}},
VolumeMode: &fs,
},
}
@ -440,6 +451,7 @@ func TestFindingVolumeWithDifferentAccessModes(t *testing.T) {
}
func createTestVolumes() []*v1.PersistentVolume {
fs := v1.PersistentVolumeFilesystem
// these volumes are deliberately out-of-order to test indexing and sorting
return []*v1.PersistentVolume{
{
@ -458,6 +470,7 @@ func createTestVolumes() []*v1.PersistentVolume {
v1.ReadWriteOnce,
v1.ReadOnlyMany,
},
VolumeMode: &fs,
},
Status: v1.PersistentVolumeStatus{
Phase: v1.VolumeAvailable,
@ -480,7 +493,8 @@ func createTestVolumes() []*v1.PersistentVolume {
v1.ReadOnlyMany,
},
// this one we're pretending is already bound
ClaimRef: &v1.ObjectReference{UID: "def456"},
ClaimRef: &v1.ObjectReference{UID: "def456"},
VolumeMode: &fs,
},
Status: v1.PersistentVolumeStatus{
Phase: v1.VolumeBound,
@ -503,6 +517,7 @@ func createTestVolumes() []*v1.PersistentVolume {
v1.ReadOnlyMany,
v1.ReadWriteMany,
},
VolumeMode: &fs,
},
Status: v1.PersistentVolumeStatus{
Phase: v1.VolumeAvailable,
@ -525,7 +540,8 @@ func createTestVolumes() []*v1.PersistentVolume {
v1.ReadOnlyMany,
},
// this one we're pretending is already bound
ClaimRef: &v1.ObjectReference{UID: "abc123"},
ClaimRef: &v1.ObjectReference{UID: "abc123"},
VolumeMode: &fs,
},
Status: v1.PersistentVolumeStatus{
Phase: v1.VolumeBound,
@ -548,6 +564,7 @@ func createTestVolumes() []*v1.PersistentVolume {
v1.ReadOnlyMany,
v1.ReadWriteMany,
},
VolumeMode: &fs,
},
Status: v1.PersistentVolumeStatus{
Phase: v1.VolumeAvailable,
@ -569,6 +586,7 @@ func createTestVolumes() []*v1.PersistentVolume {
v1.ReadWriteOnce,
v1.ReadOnlyMany,
},
VolumeMode: &fs,
},
Status: v1.PersistentVolumeStatus{
Phase: v1.VolumeAvailable,
@ -591,6 +609,7 @@ func createTestVolumes() []*v1.PersistentVolume {
v1.ReadOnlyMany,
v1.ReadWriteMany,
},
VolumeMode: &fs,
},
Status: v1.PersistentVolumeStatus{
Phase: v1.VolumeAvailable,
@ -614,6 +633,7 @@ func createTestVolumes() []*v1.PersistentVolume {
AccessModes: []v1.PersistentVolumeAccessMode{
v1.ReadWriteOnce,
},
VolumeMode: &fs,
},
Status: v1.PersistentVolumeStatus{
Phase: v1.VolumeAvailable,
@ -638,6 +658,7 @@ func createTestVolumes() []*v1.PersistentVolume {
v1.ReadWriteOnce,
},
StorageClassName: classSilver,
VolumeMode: &fs,
},
Status: v1.PersistentVolumeStatus{
Phase: v1.VolumeAvailable,
@ -659,6 +680,7 @@ func createTestVolumes() []*v1.PersistentVolume {
v1.ReadWriteOnce,
},
StorageClassName: classSilver,
VolumeMode: &fs,
},
Status: v1.PersistentVolumeStatus{
Phase: v1.VolumeAvailable,
@ -680,6 +702,7 @@ func createTestVolumes() []*v1.PersistentVolume {
v1.ReadWriteOnce,
},
StorageClassName: classGold,
VolumeMode: &fs,
},
Status: v1.PersistentVolumeStatus{
Phase: v1.VolumeAvailable,
@ -703,6 +726,7 @@ func createTestVolumes() []*v1.PersistentVolume {
v1.ReadWriteMany,
},
StorageClassName: classLarge,
VolumeMode: &fs,
},
Status: v1.PersistentVolumeStatus{
Phase: v1.VolumeAvailable,
@ -726,6 +750,7 @@ func createTestVolumes() []*v1.PersistentVolume {
v1.ReadWriteMany,
},
StorageClassName: classLarge,
VolumeMode: &fs,
},
Status: v1.PersistentVolumeStatus{
Phase: v1.VolumeAvailable,
@ -749,6 +774,7 @@ func createTestVolumes() []*v1.PersistentVolume {
},
StorageClassName: classWait,
NodeAffinity: getVolumeNodeAffinity("key1", "value1"),
VolumeMode: &fs,
},
Status: v1.PersistentVolumeStatus{
Phase: v1.VolumeAvailable,
@ -772,6 +798,7 @@ func createTestVolumes() []*v1.PersistentVolume {
},
StorageClassName: classWait,
NodeAffinity: getVolumeNodeAffinity("key1", "value1"),
VolumeMode: &fs,
},
Status: v1.PersistentVolumeStatus{
Phase: v1.VolumeAvailable,
@ -796,6 +823,7 @@ func createTestVolumes() []*v1.PersistentVolume {
StorageClassName: classWait,
ClaimRef: &v1.ObjectReference{Name: "claim02", Namespace: "myns"},
NodeAffinity: getVolumeNodeAffinity("key1", "value1"),
VolumeMode: &fs,
},
Status: v1.PersistentVolumeStatus{
Phase: v1.VolumeAvailable,
@ -819,6 +847,7 @@ func createTestVolumes() []*v1.PersistentVolume {
},
StorageClassName: classWait,
NodeAffinity: getVolumeNodeAffinity("key1", "value3"),
VolumeMode: &fs,
},
Status: v1.PersistentVolumeStatus{
Phase: v1.VolumeAvailable,
@ -842,6 +871,7 @@ func createTestVolumes() []*v1.PersistentVolume {
},
StorageClassName: classWait,
NodeAffinity: getVolumeNodeAffinity("key1", "value4"),
VolumeMode: &fs,
},
Status: v1.PersistentVolumeStatus{
Phase: v1.VolumePending,
@ -865,6 +895,7 @@ func createTestVolumes() []*v1.PersistentVolume {
},
StorageClassName: classWait,
NodeAffinity: getVolumeNodeAffinity("key1", "value4"),
VolumeMode: &fs,
},
Status: v1.PersistentVolumeStatus{
Phase: v1.VolumeFailed,
@ -888,6 +919,7 @@ func createTestVolumes() []*v1.PersistentVolume {
},
StorageClassName: classWait,
NodeAffinity: getVolumeNodeAffinity("key1", "value4"),
VolumeMode: &fs,
},
Status: v1.PersistentVolumeStatus{
Phase: v1.VolumeReleased,
@ -911,12 +943,14 @@ func createTestVolumes() []*v1.PersistentVolume {
},
StorageClassName: classWait,
NodeAffinity: getVolumeNodeAffinity("key1", "value4"),
VolumeMode: &fs,
},
},
}
}
func testVolume(name, size string) *v1.PersistentVolume {
fs := v1.PersistentVolumeFilesystem
return &v1.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{
Name: name,
@ -926,6 +960,7 @@ func testVolume(name, size string) *v1.PersistentVolume {
Capacity: v1.ResourceList{v1.ResourceName(v1.ResourceStorage): resource.MustParse(size)},
PersistentVolumeSource: v1.PersistentVolumeSource{HostPath: &v1.HostPathVolumeSource{}},
AccessModes: []v1.PersistentVolumeAccessMode{v1.ReadWriteOnce},
VolumeMode: &fs,
},
Status: v1.PersistentVolumeStatus{
Phase: v1.VolumeAvailable,
@ -1003,34 +1038,36 @@ func createVolumeModeFilesystemTestVolume() *v1.PersistentVolume {
}
}
func createVolumeModeNilTestVolume() *v1.PersistentVolume {
return &v1.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{
UID: "local-1",
Name: "nil-mode",
},
Spec: v1.PersistentVolumeSpec{
Capacity: v1.ResourceList{
v1.ResourceName(v1.ResourceStorage): resource.MustParse("10G"),
},
PersistentVolumeSource: v1.PersistentVolumeSource{
Local: &v1.LocalVolumeSource{},
},
AccessModes: []v1.PersistentVolumeAccessMode{
v1.ReadWriteOnce,
},
},
Status: v1.PersistentVolumeStatus{
Phase: v1.VolumeAvailable,
},
}
}
func createTestVolOrderedIndex(pv *v1.PersistentVolume) persistentVolumeOrderedIndex {
volFile := newPersistentVolumeOrderedIndex()
volFile.store.Add(pv)
return volFile
}
func toggleFeature(toggleFlag bool, featureName string, t *testing.T) {
var valueStr string
if toggleFlag {
// Enable feature
valueStr = featureName + "=true"
err := utilfeature.DefaultFeatureGate.Set(valueStr)
if err != nil {
t.Errorf("Failed to enable feature gate for %s: %v", featureName, err)
return
}
} else {
// Disable feature
valueStr = featureName + "=false"
err := utilfeature.DefaultFeatureGate.Set(valueStr)
if err != nil {
t.Errorf("Failed to disable feature gate for %s: %v", featureName, err)
return
}
}
}
func TestAlphaVolumeModeCheck(t *testing.T) {
func TestVolumeModeCheck(t *testing.T) {
blockMode := v1.PersistentVolumeBlock
filesystemMode := v1.PersistentVolumeFilesystem
@ -1038,55 +1075,85 @@ func TestAlphaVolumeModeCheck(t *testing.T) {
// If feature gate is enabled, VolumeMode will always be defaulted
// If feature gate is disabled, VolumeMode is dropped by API and ignored
scenarios := map[string]struct {
isExpectedMisMatch bool
isExpectedMismatch bool
vol *v1.PersistentVolume
pvc *v1.PersistentVolumeClaim
enableBlock bool
}{
"feature enabled - pvc block and pv filesystem": {
isExpectedMisMatch: true,
isExpectedMismatch: true,
vol: createVolumeModeFilesystemTestVolume(),
pvc: makeVolumeModePVC("8G", &blockMode, nil),
enableBlock: true,
},
"feature enabled - pvc filesystem and pv block": {
isExpectedMisMatch: true,
isExpectedMismatch: true,
vol: createVolumeModeBlockTestVolume(),
pvc: makeVolumeModePVC("8G", &filesystemMode, nil),
enableBlock: true,
},
"feature enabled - pvc block and pv block": {
isExpectedMisMatch: false,
isExpectedMismatch: false,
vol: createVolumeModeBlockTestVolume(),
pvc: makeVolumeModePVC("8G", &blockMode, nil),
enableBlock: true,
},
"feature enabled - pvc filesystem and pv filesystem": {
isExpectedMisMatch: false,
isExpectedMismatch: false,
vol: createVolumeModeFilesystemTestVolume(),
pvc: makeVolumeModePVC("8G", &filesystemMode, nil),
enableBlock: true,
},
"feature enabled - pvc filesystem and pv nil": {
isExpectedMismatch: false,
vol: createVolumeModeNilTestVolume(),
pvc: makeVolumeModePVC("8G", &filesystemMode, nil),
enableBlock: true,
},
"feature enabled - pvc nil and pv filesytem": {
isExpectedMismatch: false,
vol: createVolumeModeFilesystemTestVolume(),
pvc: makeVolumeModePVC("8G", nil, nil),
enableBlock: true,
},
"feature enabled - pvc nil and pv nil": {
isExpectedMismatch: false,
vol: createVolumeModeNilTestVolume(),
pvc: makeVolumeModePVC("8G", nil, nil),
enableBlock: true,
},
"feature enabled - pvc nil and pv block": {
isExpectedMismatch: true,
vol: createVolumeModeBlockTestVolume(),
pvc: makeVolumeModePVC("8G", nil, nil),
enableBlock: true,
},
"feature enabled - pvc block and pv nil": {
isExpectedMismatch: true,
vol: createVolumeModeNilTestVolume(),
pvc: makeVolumeModePVC("8G", &blockMode, nil),
enableBlock: true,
},
"feature disabled - pvc block and pv filesystem": {
isExpectedMisMatch: false,
isExpectedMismatch: false,
vol: createVolumeModeFilesystemTestVolume(),
pvc: makeVolumeModePVC("8G", &blockMode, nil),
enableBlock: false,
},
"feature disabled - pvc filesystem and pv block": {
isExpectedMisMatch: false,
isExpectedMismatch: false,
vol: createVolumeModeBlockTestVolume(),
pvc: makeVolumeModePVC("8G", &filesystemMode, nil),
enableBlock: false,
},
"feature disabled - pvc block and pv block": {
isExpectedMisMatch: false,
isExpectedMismatch: false,
vol: createVolumeModeBlockTestVolume(),
pvc: makeVolumeModePVC("8G", &blockMode, nil),
enableBlock: false,
},
"feature disabled - pvc filesystem and pv filesystem": {
isExpectedMisMatch: false,
isExpectedMismatch: false,
vol: createVolumeModeFilesystemTestVolume(),
pvc: makeVolumeModePVC("8G", &filesystemMode, nil),
enableBlock: false,
@ -1094,25 +1161,23 @@ func TestAlphaVolumeModeCheck(t *testing.T) {
}
for name, scenario := range scenarios {
toggleFeature(scenario.enableBlock, "BlockVolume", t)
expectedMisMatch, err := checkVolumeModeMisMatches(&scenario.pvc.Spec, &scenario.vol.Spec)
recover := utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.BlockVolume, scenario.enableBlock)
expectedMismatch, err := checkVolumeModeMismatches(&scenario.pvc.Spec, &scenario.vol.Spec)
if err != nil {
t.Errorf("Unexpected failure for checkVolumeModeMisMatches: %v", err)
t.Errorf("Unexpected failure for checkVolumeModeMismatches: %v", err)
}
// expected to match but either got an error or no returned pvmatch
if expectedMisMatch && !scenario.isExpectedMisMatch {
if expectedMismatch && !scenario.isExpectedMismatch {
t.Errorf("Unexpected failure for scenario, expected not to mismatch on modes but did: %s", name)
}
if !expectedMisMatch && scenario.isExpectedMisMatch {
if !expectedMismatch && scenario.isExpectedMismatch {
t.Errorf("Unexpected failure for scenario, did not mismatch on mode when expected to mismatch: %s", name)
}
recover()
}
// make sure feature gate is turned off
toggleFeature(false, "BlockVolume", t)
}
func TestAlphaFilteringVolumeModes(t *testing.T) {
func TestFilteringVolumeModes(t *testing.T) {
blockMode := v1.PersistentVolumeBlock
filesystemMode := v1.PersistentVolumeFilesystem
@ -1187,7 +1252,7 @@ func TestAlphaFilteringVolumeModes(t *testing.T) {
}
for name, scenario := range scenarios {
toggleFeature(scenario.enableBlock, "BlockVolume", t)
recover := utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.BlockVolume, scenario.enableBlock)
pvmatch, err := scenario.vol.findBestMatchForClaim(scenario.pvc, false)
// expected to match but either got an error or no returned pvmatch
if pvmatch == nil && scenario.isExpectedMatch {
@ -1203,13 +1268,12 @@ func TestAlphaFilteringVolumeModes(t *testing.T) {
if err != nil && !scenario.isExpectedMatch {
t.Errorf("Unexpected failure for scenario: %s - %+v", name, err)
}
recover()
}
// make sure feature gate is turned off
toggleFeature(false, "BlockVolume", t)
}
func TestStorageObjectInUseProtectionFiltering(t *testing.T) {
fs := v1.PersistentVolumeFilesystem
pv := &v1.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{
Name: "pv1",
@ -1219,6 +1283,7 @@ func TestStorageObjectInUseProtectionFiltering(t *testing.T) {
Capacity: v1.ResourceList{v1.ResourceName(v1.ResourceStorage): resource.MustParse("1G")},
PersistentVolumeSource: v1.PersistentVolumeSource{HostPath: &v1.HostPathVolumeSource{}},
AccessModes: []v1.PersistentVolumeAccessMode{v1.ReadWriteOnce},
VolumeMode: &fs,
},
Status: v1.PersistentVolumeStatus{
Phase: v1.VolumeAvailable,
@ -1237,6 +1302,7 @@ func TestStorageObjectInUseProtectionFiltering(t *testing.T) {
Spec: v1.PersistentVolumeClaimSpec{
AccessModes: []v1.PersistentVolumeAccessMode{v1.ReadWriteOnce},
Resources: v1.ResourceRequirements{Requests: v1.ResourceList{v1.ResourceName(v1.ResourceStorage): resource.MustParse("1G")}},
VolumeMode: &fs,
},
}
@ -1343,6 +1409,7 @@ func TestStorageObjectInUseProtectionFiltering(t *testing.T) {
}
func TestFindingPreboundVolumes(t *testing.T) {
fs := v1.PersistentVolumeFilesystem
claim := &v1.PersistentVolumeClaim{
ObjectMeta: metav1.ObjectMeta{
Name: "claim01",
@ -1352,6 +1419,7 @@ func TestFindingPreboundVolumes(t *testing.T) {
Spec: v1.PersistentVolumeClaimSpec{
AccessModes: []v1.PersistentVolumeAccessMode{v1.ReadWriteOnce},
Resources: v1.ResourceRequirements{Requests: v1.ResourceList{v1.ResourceName(v1.ResourceStorage): resource.MustParse("1Gi")}},
VolumeMode: &fs,
},
}
claimRef, err := ref.GetReference(scheme.Scheme, claim)

View File

@ -270,11 +270,11 @@ func checkVolumeSatisfyClaim(volume *v1.PersistentVolume, claim *v1.PersistentVo
return fmt.Errorf("storageClassName does not match")
}
isMisMatch, err := checkVolumeModeMisMatches(&claim.Spec, &volume.Spec)
isMismatch, err := checkVolumeModeMismatches(&claim.Spec, &volume.Spec)
if err != nil {
return fmt.Errorf("error checking volumeMode: %v", err)
}
if isMisMatch {
if isMismatch {
return fmt.Errorf("incompatible volumeMode")
}
@ -613,7 +613,7 @@ func (ctrl *PersistentVolumeController) syncVolume(volume *v1.PersistentVolume)
}
return nil
} else if claim.Spec.VolumeName == "" {
if isMisMatch, err := checkVolumeModeMisMatches(&claim.Spec, &volume.Spec); err != nil || isMisMatch {
if isMismatch, err := checkVolumeModeMismatches(&claim.Spec, &volume.Spec); err != nil || isMismatch {
// Binding for the volume won't be called in syncUnboundClaim,
// because findBestMatchForClaim won't return the volume due to volumeMode mismatch.
volumeMsg := fmt.Sprintf("Cannot bind PersistentVolume to requested PersistentVolumeClaim %q due to incompatible volumeMode.", claim.Name)

View File

@ -426,6 +426,7 @@ const (
)
func makeTestPVC(name, size, node string, pvcBoundState int, pvName, resourceVersion string, className *string) *v1.PersistentVolumeClaim {
fs := v1.PersistentVolumeFilesystem
pvc := &v1.PersistentVolumeClaim{
TypeMeta: metav1.TypeMeta{
Kind: "PersistentVolumeClaim",
@ -445,6 +446,7 @@ func makeTestPVC(name, size, node string, pvcBoundState int, pvName, resourceVer
},
},
StorageClassName: className,
VolumeMode: &fs,
},
}
@ -462,6 +464,7 @@ func makeTestPVC(name, size, node string, pvcBoundState int, pvName, resourceVer
}
func makeBadPVC() *v1.PersistentVolumeClaim {
fs := v1.PersistentVolumeFilesystem
return &v1.PersistentVolumeClaim{
ObjectMeta: metav1.ObjectMeta{
Name: "bad-pvc",
@ -477,11 +480,13 @@ func makeBadPVC() *v1.PersistentVolumeClaim {
},
},
StorageClassName: &waitClass,
VolumeMode: &fs,
},
}
}
func makeTestPV(name, node, capacity, version string, boundToPVC *v1.PersistentVolumeClaim, className string) *v1.PersistentVolume {
fs := v1.PersistentVolumeFilesystem
pv := &v1.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{
Name: name,
@ -492,6 +497,7 @@ func makeTestPV(name, node, capacity, version string, boundToPVC *v1.PersistentV
v1.ResourceName(v1.ResourceStorage): resource.MustParse(capacity),
},
StorageClassName: className,
VolumeMode: &fs,
},
Status: v1.PersistentVolumeStatus{
Phase: v1.VolumeAvailable,

View File

@ -217,6 +217,7 @@ const (
// owner: @screeley44
// alpha: v1.9
// beta: v1.13
//
// Enable Block volume support in containers.
BlockVolume utilfeature.Feature = "BlockVolume"
@ -433,7 +434,7 @@ var defaultKubernetesFeatureGates = map[utilfeature.Feature]utilfeature.FeatureS
CSIDriverRegistry: {Default: false, PreRelease: utilfeature.Alpha},
CSINodeInfo: {Default: false, PreRelease: utilfeature.Alpha},
CustomPodDNS: {Default: true, PreRelease: utilfeature.Beta},
BlockVolume: {Default: false, PreRelease: utilfeature.Alpha},
BlockVolume: {Default: true, PreRelease: utilfeature.Beta},
StorageObjectInUseProtection: {Default: true, PreRelease: utilfeature.GA},
ResourceLimitsPriorityFunction: {Default: false, PreRelease: utilfeature.Alpha},
SupportIPVSProxyMode: {Default: true, PreRelease: utilfeature.GA},

View File

@ -46,12 +46,14 @@ import (
func TestFindAndAddNewPods_FindAndRemoveDeletedPods(t *testing.T) {
// create dswp
mode := v1.PersistentVolumeFilesystem
pv := &v1.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{
Name: "dswp-test-volume-name",
},
Spec: v1.PersistentVolumeSpec{
ClaimRef: &v1.ObjectReference{Namespace: "ns", Name: "file-bound"},
ClaimRef: &v1.ObjectReference{Namespace: "ns", Name: "file-bound"},
VolumeMode: &mode,
},
}
pvc := &v1.PersistentVolumeClaim{
@ -453,6 +455,7 @@ func TestCreateVolumeSpec_Invalid_Block_VolumeMounts(t *testing.T) {
}
func TestCheckVolumeFSResize(t *testing.T) {
mode := v1.PersistentVolumeFilesystem
pv := &v1.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{
Name: "dswp-test-volume-name",
@ -461,6 +464,7 @@ func TestCheckVolumeFSResize(t *testing.T) {
PersistentVolumeSource: v1.PersistentVolumeSource{RBD: &v1.RBDPersistentVolumeSource{}},
Capacity: volumeCapacity(1),
ClaimRef: &v1.ObjectReference{Namespace: "ns", Name: "file-bound"},
VolumeMode: &mode,
},
}
pvc := &v1.PersistentVolumeClaim{

View File

@ -950,13 +950,15 @@ func Test_GenerateUnmapDeviceFunc_Plugin_Not_Found(t *testing.T) {
func Test_Run_Positive_VolumeFSResizeControllerAttachEnabled(t *testing.T) {
defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ExpandInUsePersistentVolumes, true)()
fs := v1.PersistentVolumeFilesystem
pv := &v1.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{
Name: "pv",
UID: "pvuid",
},
Spec: v1.PersistentVolumeSpec{
ClaimRef: &v1.ObjectReference{Name: "pvc"},
ClaimRef: &v1.ObjectReference{Name: "pvc"},
VolumeMode: &fs,
},
}
pvc := &v1.PersistentVolumeClaim{
@ -966,6 +968,7 @@ func Test_Run_Positive_VolumeFSResizeControllerAttachEnabled(t *testing.T) {
},
Spec: v1.PersistentVolumeClaimSpec{
VolumeName: "pv",
VolumeMode: &fs,
},
}
pod := &v1.Pod{

View File

@ -167,6 +167,7 @@ func TestGetExtraSupplementalGroupsForPod(t *testing.T) {
}
for _, tc := range cases {
fs := v1.PersistentVolumeFilesystem
pv := &v1.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{
Name: "pvA",
@ -183,6 +184,7 @@ func TestGetExtraSupplementalGroupsForPod(t *testing.T) {
ClaimRef: &v1.ObjectReference{
Name: claim.ObjectMeta.Name,
},
VolumeMode: &fs,
},
}
kubeClient := fake.NewSimpleClientset(node, pod, pv, claim)
@ -273,6 +275,7 @@ func createObjects() (*v1.Node, *v1.Pod, *v1.PersistentVolume, *v1.PersistentVol
},
},
}
fs := v1.PersistentVolumeFilesystem
pv := &v1.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{
Name: "pvA",
@ -286,6 +289,7 @@ func createObjects() (*v1.Node, *v1.Pod, *v1.PersistentVolume, *v1.PersistentVol
ClaimRef: &v1.ObjectReference{
Name: "claimA",
},
VolumeMode: &fs,
},
}
claim := &v1.PersistentVolumeClaim{

View File

@ -254,6 +254,7 @@ func TestPluginVolume(t *testing.T) {
func TestPluginPersistentVolume(t *testing.T) {
lun := int32(0)
fs := v1.PersistentVolumeFilesystem
vol := &v1.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{
Name: "vol1",
@ -266,6 +267,7 @@ func TestPluginPersistentVolume(t *testing.T) {
Lun: &lun,
},
},
VolumeMode: &fs,
},
}
doTestPlugin(t, volume.NewSpecFromPersistentVolume(vol, false))
@ -285,6 +287,7 @@ func TestPluginVolumeWWIDs(t *testing.T) {
}
func TestPluginPersistentVolumeWWIDs(t *testing.T) {
fs := v1.PersistentVolumeFilesystem
vol := &v1.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{
Name: "vol1",
@ -296,6 +299,7 @@ func TestPluginPersistentVolumeWWIDs(t *testing.T) {
FSType: "ext4",
},
},
VolumeMode: &fs,
},
}
doTestPlugin(t, volume.NewSpecFromPersistentVolume(vol, false))
@ -314,6 +318,7 @@ func TestPluginVolumeNoDiskInfo(t *testing.T) {
}
func TestPluginPersistentVolumeNoDiskInfo(t *testing.T) {
fs := v1.PersistentVolumeFilesystem
vol := &v1.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{
Name: "vol1",
@ -324,6 +329,7 @@ func TestPluginPersistentVolumeNoDiskInfo(t *testing.T) {
FSType: "ext4",
},
},
VolumeMode: &fs,
},
}
doTestPluginNilMounter(t, volume.NewSpecFromPersistentVolume(vol, false))
@ -337,6 +343,7 @@ func TestPersistentClaimReadOnlyFlag(t *testing.T) {
defer os.RemoveAll(tmpDir)
lun := int32(0)
fs := v1.PersistentVolumeFilesystem
pv := &v1.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{
Name: "pvA",
@ -352,6 +359,7 @@ func TestPersistentClaimReadOnlyFlag(t *testing.T) {
ClaimRef: &v1.ObjectReference{
Name: "claimA",
},
VolumeMode: &fs,
},
}
@ -362,6 +370,7 @@ func TestPersistentClaimReadOnlyFlag(t *testing.T) {
},
Spec: v1.PersistentVolumeClaimSpec{
VolumeName: "pvA",
VolumeMode: &fs,
},
Status: v1.PersistentVolumeClaimStatus{
Phase: v1.ClaimBound,

View File

@ -641,7 +641,7 @@ message Container {
repeated VolumeMount volumeMounts = 9;
// volumeDevices is the list of block devices to be used by the container.
// This is an alpha feature and may change in the future.
// This is a beta feature.
// +patchMergeKey=devicePath
// +patchStrategy=merge
// +optional
@ -2320,7 +2320,7 @@ message PersistentVolumeClaimSpec {
// volumeMode defines what type of volume is required by the claim.
// Value of Filesystem is implied when not included in claim spec.
// This is an alpha feature and may change in the future.
// This is a beta feature.
// +optional
optional string volumeMode = 6;
@ -2536,7 +2536,7 @@ message PersistentVolumeSpec {
// volumeMode defines if a volume is intended to be used with a formatted filesystem
// or to remain in raw block state. Value of Filesystem is implied when not included in spec.
// This is an alpha feature and may change in the future.
// This is a beta feature.
// +optional
optional string volumeMode = 8;

View File

@ -326,7 +326,7 @@ type PersistentVolumeSpec struct {
MountOptions []string `json:"mountOptions,omitempty" protobuf:"bytes,7,opt,name=mountOptions"`
// volumeMode defines if a volume is intended to be used with a formatted filesystem
// or to remain in raw block state. Value of Filesystem is implied when not included in spec.
// This is an alpha feature and may change in the future.
// This is a beta feature.
// +optional
VolumeMode *PersistentVolumeMode `json:"volumeMode,omitempty" protobuf:"bytes,8,opt,name=volumeMode,casttype=PersistentVolumeMode"`
// NodeAffinity defines constraints that limit what nodes this volume can be accessed from.
@ -455,7 +455,7 @@ type PersistentVolumeClaimSpec struct {
StorageClassName *string `json:"storageClassName,omitempty" protobuf:"bytes,5,opt,name=storageClassName"`
// volumeMode defines what type of volume is required by the claim.
// Value of Filesystem is implied when not included in claim spec.
// This is an alpha feature and may change in the future.
// This is a beta feature.
// +optional
VolumeMode *PersistentVolumeMode `json:"volumeMode,omitempty" protobuf:"bytes,6,opt,name=volumeMode,casttype=PersistentVolumeMode"`
// This field requires the VolumeSnapshotDataSource alpha feature gate to be
@ -2114,7 +2114,7 @@ type Container struct {
// +patchStrategy=merge
VolumeMounts []VolumeMount `json:"volumeMounts,omitempty" patchStrategy:"merge" patchMergeKey:"mountPath" protobuf:"bytes,9,rep,name=volumeMounts"`
// volumeDevices is the list of block devices to be used by the container.
// This is an alpha feature and may change in the future.
// This is a beta feature.
// +patchMergeKey=devicePath
// +patchStrategy=merge
// +optional

View File

@ -321,7 +321,7 @@ var map_Container = map[string]string{
"env": "List of environment variables to set in the container. Cannot be updated.",
"resources": "Compute Resources required by this container. Cannot be updated. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/",
"volumeMounts": "Pod volumes to mount into the container's filesystem. Cannot be updated.",
"volumeDevices": "volumeDevices is the list of block devices to be used by the container. This is an alpha feature and may change in the future.",
"volumeDevices": "volumeDevices is the list of block devices to be used by the container. This is a beta feature.",
"livenessProbe": "Periodic probe of container liveness. Container will be restarted if the probe fails. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes",
"readinessProbe": "Periodic probe of container service readiness. Container will be removed from service endpoints if the probe fails. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes",
"lifecycle": "Actions that the management system should take in response to container lifecycle events. Cannot be updated.",
@ -1222,7 +1222,7 @@ var map_PersistentVolumeClaimSpec = map[string]string{
"resources": "Resources represents the minimum resources the volume should have. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources",
"volumeName": "VolumeName is the binding reference to the PersistentVolume backing this claim.",
"storageClassName": "Name of the StorageClass required by the claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1",
"volumeMode": "volumeMode defines what type of volume is required by the claim. Value of Filesystem is implied when not included in claim spec. This is an alpha feature and may change in the future.",
"volumeMode": "volumeMode defines what type of volume is required by the claim. Value of Filesystem is implied when not included in claim spec. This is a beta feature.",
"dataSource": "This field requires the VolumeSnapshotDataSource alpha feature gate to be enabled and currently VolumeSnapshot is the only supported data source. If the provisioner can support VolumeSnapshot data source, it will create a new volume and data will be restored to the volume at the same time. If the provisioner does not support VolumeSnapshot data source, volume will not be created and the failure will be reported as an event. In the future, we plan to support more data source types and the behavior of the provisioner may change.",
}
@ -1300,7 +1300,7 @@ var map_PersistentVolumeSpec = map[string]string{
"persistentVolumeReclaimPolicy": "What happens to a persistent volume when released from its claim. Valid options are Retain (default for manually created PersistentVolumes), Delete (default for dynamically provisioned PersistentVolumes), and Recycle (deprecated). Recycle must be supported by the volume plugin underlying this PersistentVolume. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#reclaiming",
"storageClassName": "Name of StorageClass to which this persistent volume belongs. Empty value means that this volume does not belong to any StorageClass.",
"mountOptions": "A list of mount options, e.g. [\"ro\", \"soft\"]. Not validated - mount will simply fail if one is invalid. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes/#mount-options",
"volumeMode": "volumeMode defines if a volume is intended to be used with a formatted filesystem or to remain in raw block state. Value of Filesystem is implied when not included in spec. This is an alpha feature and may change in the future.",
"volumeMode": "volumeMode defines if a volume is intended to be used with a formatted filesystem or to remain in raw block state. Value of Filesystem is implied when not included in spec. This is a beta feature.",
"nodeAffinity": "NodeAffinity defines constraints that limit what nodes this volume can be accessed from. This field influences the scheduling of pods that use this volume.",
}

View File

@ -58,6 +58,7 @@ var upgradeTests = []upgrades.Test{
&apps.DaemonSetUpgradeTest{},
&upgrades.IngressUpgradeTest{},
&upgrades.AppArmorUpgradeTest{},
&storage.VolumeModeDowngradeTest{},
}
var gpuUpgradeTests = []upgrades.Test{

View File

@ -218,11 +218,7 @@ var _ = utils.SIGDescribe("PersistentVolumes-local ", func() {
if testVolType == GCELocalSSDVolumeType {
serialStr = " [Serial]"
}
alphaStr := ""
if testVolType == BlockLocalVolumeType {
alphaStr = " [Feature:BlockVolume]"
}
ctxString := fmt.Sprintf("[Volume type: %s]%v%v", testVolType, serialStr, alphaStr)
ctxString := fmt.Sprintf("[Volume type: %s]%v", testVolType, serialStr)
testMode := immediateMode
Context(ctxString, func() {

View File

@ -186,7 +186,7 @@ func testProvisioning(input *provisioningTestInput) {
TestDynamicProvisioning(input.testCase, input.cs, input.pvc, input.sc)
})
It("should create and delete block persistent volumes [Feature:BlockVolume]", func() {
It("should create and delete block persistent volumes", func() {
if !input.dInfo.IsBlockSupported {
framework.Skipf("Driver %q does not support BlockVolume - skipping", input.dInfo.Name)
}

View File

@ -47,8 +47,7 @@ var _ TestSuite = &volumeModeTestSuite{}
func InitVolumeModeTestSuite() TestSuite {
return &volumeModeTestSuite{
tsInfo: TestSuiteInfo{
name: "volumeMode",
featureTag: "[Feature:BlockVolume]",
name: "volumeMode",
testPatterns: []testpatterns.TestPattern{
testpatterns.FsVolModePreprovisionedPV,
testpatterns.FsVolModeDynamicPV,
@ -304,10 +303,10 @@ func testVolumeModeSuccessForPreprovisionedPV(input *volumeModeTestInput) {
Expect(err).NotTo(HaveOccurred())
By("Checking if persistent volume exists as expected volume mode")
checkVolumeModeOfPath(pod, input.volMode, "/mnt/volume1")
utils.CheckVolumeModeOfPath(pod, input.volMode, "/mnt/volume1")
By("Checking if read/write to persistent volume works properly")
checkReadWriteToPath(pod, input.volMode, "/mnt/volume1")
utils.CheckReadWriteToPath(pod, input.volMode, "/mnt/volume1")
})
// TODO(mkimuram): Add more tests
}
@ -366,10 +365,10 @@ func testVolumeModeSuccessForDynamicPV(input *volumeModeTestInput) {
Expect(err).NotTo(HaveOccurred())
By("Checking if persistent volume exists as expected volume mode")
checkVolumeModeOfPath(pod, input.volMode, "/mnt/volume1")
utils.CheckVolumeModeOfPath(pod, input.volMode, "/mnt/volume1")
By("Checking if read/write to persistent volume works properly")
checkReadWriteToPath(pod, input.volMode, "/mnt/volume1")
utils.CheckReadWriteToPath(pod, input.volMode, "/mnt/volume1")
})
// TODO(mkimuram): Add more tests
}
@ -401,45 +400,3 @@ func generateConfigsForPreprovisionedPVTest(scName string, volBindMode storagev1
return scConfig, pvConfig, pvcConfig
}
func checkVolumeModeOfPath(pod *v1.Pod, volMode v1.PersistentVolumeMode, path string) {
if volMode == v1.PersistentVolumeBlock {
// Check if block exists
utils.VerifyExecInPodSucceed(pod, fmt.Sprintf("test -b %s", path))
// Double check that it's not directory
utils.VerifyExecInPodFail(pod, fmt.Sprintf("test -d %s", path), 1)
} else {
// Check if directory exists
utils.VerifyExecInPodSucceed(pod, fmt.Sprintf("test -d %s", path))
// Double check that it's not block
utils.VerifyExecInPodFail(pod, fmt.Sprintf("test -b %s", path), 1)
}
}
func checkReadWriteToPath(pod *v1.Pod, volMode v1.PersistentVolumeMode, path string) {
if volMode == v1.PersistentVolumeBlock {
// random -> file1
utils.VerifyExecInPodSucceed(pod, "dd if=/dev/urandom of=/tmp/file1 bs=64 count=1")
// file1 -> dev (write to dev)
utils.VerifyExecInPodSucceed(pod, fmt.Sprintf("dd if=/tmp/file1 of=%s bs=64 count=1", path))
// dev -> file2 (read from dev)
utils.VerifyExecInPodSucceed(pod, fmt.Sprintf("dd if=%s of=/tmp/file2 bs=64 count=1", path))
// file1 == file2 (check contents)
utils.VerifyExecInPodSucceed(pod, "diff /tmp/file1 /tmp/file2")
// Clean up temp files
utils.VerifyExecInPodSucceed(pod, "rm -f /tmp/file1 /tmp/file2")
// Check that writing file to block volume fails
utils.VerifyExecInPodFail(pod, fmt.Sprintf("echo 'Hello world.' > %s/file1.txt", path), 1)
} else {
// text -> file1 (write to file)
utils.VerifyExecInPodSucceed(pod, fmt.Sprintf("echo 'Hello world.' > %s/file1.txt", path))
// grep file1 (read from file and check contents)
utils.VerifyExecInPodSucceed(pod, fmt.Sprintf("grep 'Hello world.' %s/file1.txt", path))
// Check that writing to directory as block volume fails
utils.VerifyExecInPodFail(pod, fmt.Sprintf("dd if=/dev/urandom of=%s bs=64 count=1", path), 1)
}
}

View File

@ -441,3 +441,45 @@ func PrivilegedTestPSPClusterRoleBinding(client clientset.Interface,
}
}
func CheckVolumeModeOfPath(pod *v1.Pod, volMode v1.PersistentVolumeMode, path string) {
if volMode == v1.PersistentVolumeBlock {
// Check if block exists
VerifyExecInPodSucceed(pod, fmt.Sprintf("test -b %s", path))
// Double check that it's not directory
VerifyExecInPodFail(pod, fmt.Sprintf("test -d %s", path), 1)
} else {
// Check if directory exists
VerifyExecInPodSucceed(pod, fmt.Sprintf("test -d %s", path))
// Double check that it's not block
VerifyExecInPodFail(pod, fmt.Sprintf("test -b %s", path), 1)
}
}
func CheckReadWriteToPath(pod *v1.Pod, volMode v1.PersistentVolumeMode, path string) {
if volMode == v1.PersistentVolumeBlock {
// random -> file1
VerifyExecInPodSucceed(pod, "dd if=/dev/urandom of=/tmp/file1 bs=64 count=1")
// file1 -> dev (write to dev)
VerifyExecInPodSucceed(pod, fmt.Sprintf("dd if=/tmp/file1 of=%s bs=64 count=1", path))
// dev -> file2 (read from dev)
VerifyExecInPodSucceed(pod, fmt.Sprintf("dd if=%s of=/tmp/file2 bs=64 count=1", path))
// file1 == file2 (check contents)
VerifyExecInPodSucceed(pod, "diff /tmp/file1 /tmp/file2")
// Clean up temp files
VerifyExecInPodSucceed(pod, "rm -f /tmp/file1 /tmp/file2")
// Check that writing file to block volume fails
VerifyExecInPodFail(pod, fmt.Sprintf("echo 'Hello world.' > %s/file1.txt", path), 1)
} else {
// text -> file1 (write to file)
VerifyExecInPodSucceed(pod, fmt.Sprintf("echo 'Hello world.' > %s/file1.txt", path))
// grep file1 (read from file and check contents)
VerifyExecInPodSucceed(pod, fmt.Sprintf("grep 'Hello world.' %s/file1.txt", path))
// Check that writing to directory as block volume fails
VerifyExecInPodFail(pod, fmt.Sprintf("dd if=/dev/urandom of=%s bs=64 count=1", path), 1)
}
}

View File

@ -7,12 +7,18 @@ load(
go_library(
name = "go_default_library",
srcs = ["persistent_volumes.go"],
srcs = [
"persistent_volumes.go",
"volume_mode.go",
],
importpath = "k8s.io/kubernetes/test/e2e/upgrades/storage",
deps = [
"//staging/src/k8s.io/api/core/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/errors:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/version:go_default_library",
"//test/e2e/framework:go_default_library",
"//test/e2e/storage/utils:go_default_library",
"//test/e2e/upgrades:go_default_library",
"//vendor/github.com/onsi/ginkgo:go_default_library",
"//vendor/github.com/onsi/gomega:go_default_library",

View File

@ -0,0 +1,125 @@
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package storage
import (
"fmt"
"time"
"k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/version"
"k8s.io/kubernetes/test/e2e/framework"
"k8s.io/kubernetes/test/e2e/storage/utils"
"k8s.io/kubernetes/test/e2e/upgrades"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
const devicePath = "/mnt/volume1"
// VolumeModeDowngradeTest tests that a VolumeMode Block PV is not mistakenly
// formatted and mounted like a nil/Filesystem PV after a downgrade to a version
// where the BlockVolume feature is disabled
type VolumeModeDowngradeTest struct {
pvSource *v1.PersistentVolumeSource
pv *v1.PersistentVolume
pvc *v1.PersistentVolumeClaim
pod *v1.Pod
}
func (VolumeModeDowngradeTest) Name() string {
return "[sig-storage] volume-mode-downgrade"
}
func (t *VolumeModeDowngradeTest) Skip(upgCtx upgrades.UpgradeContext) bool {
if !framework.ProviderIs("openstack", "gce", "aws", "gke", "vsphere", "azure") {
return true
}
// Only run when downgrading from >= 1.13 to < 1.13
blockVersion := version.MustParseSemantic("1.13.0-alpha.0")
if upgCtx.Versions[0].Version.LessThan(blockVersion) {
return true
}
if !upgCtx.Versions[1].Version.LessThan(blockVersion) {
return true
}
return false
}
// Setup creates a block pv and then verifies that a pod can consume it. The pod writes data to the volume.
func (t *VolumeModeDowngradeTest) Setup(f *framework.Framework) {
var err error
cs := f.ClientSet
ns := f.Namespace.Name
By("Creating a PVC")
block := v1.PersistentVolumeBlock
pvcConfig := framework.PersistentVolumeClaimConfig{
StorageClassName: nil,
VolumeMode: &block,
}
t.pvc = framework.MakePersistentVolumeClaim(pvcConfig, ns)
t.pvc, err = framework.CreatePVC(cs, ns, t.pvc)
Expect(err).NotTo(HaveOccurred())
err = framework.WaitForPersistentVolumeClaimPhase(v1.ClaimBound, cs, ns, t.pvc.Name, framework.Poll, framework.ClaimProvisionTimeout)
Expect(err).NotTo(HaveOccurred())
t.pvc, err = cs.CoreV1().PersistentVolumeClaims(t.pvc.Namespace).Get(t.pvc.Name, metav1.GetOptions{})
Expect(err).NotTo(HaveOccurred())
t.pv, err = cs.CoreV1().PersistentVolumes().Get(t.pvc.Spec.VolumeName, metav1.GetOptions{})
Expect(err).NotTo(HaveOccurred())
By("Consuming the PVC before downgrade")
t.pod, err = framework.CreateSecPod(cs, ns, []*v1.PersistentVolumeClaim{t.pvc}, false, "", false, false, framework.SELinuxLabel, nil, framework.PodStartTimeout)
Expect(err).NotTo(HaveOccurred())
By("Checking if PV exists as expected volume mode")
utils.CheckVolumeModeOfPath(t.pod, block, devicePath)
By("Checking if read/write to PV works properly")
utils.CheckReadWriteToPath(t.pod, block, devicePath)
}
// Test waits for the downgrade to complete, and then verifies that a pod can no
// longer consume the pv as it is not mapped nor mounted into the pod
func (t *VolumeModeDowngradeTest) Test(f *framework.Framework, done <-chan struct{}, upgrade upgrades.UpgradeType) {
By("Waiting for downgrade to finish")
<-done
By("Verifying that nothing exists at the device path in the pod")
utils.VerifyExecInPodFail(t.pod, fmt.Sprintf("test -e %s", devicePath), 1)
}
// Teardown cleans up any remaining resources.
func (t *VolumeModeDowngradeTest) Teardown(f *framework.Framework) {
By("Deleting the pod")
framework.ExpectNoError(framework.DeletePodWithWait(f, f.ClientSet, t.pod))
By("Deleting the PVC")
framework.ExpectNoError(f.ClientSet.CoreV1().PersistentVolumeClaims(t.pvc.Namespace).Delete(t.pvc.Name, nil))
By("Waiting for the PV to be deleted")
framework.ExpectNoError(framework.WaitForPersistentVolumeDeleted(f.ClientSet, t.pv.Name, 5*time.Second, 20*time.Minute))
}