Skip to content

Commit c05c8e9

Browse files
committed
GenericEphemeralVolume: feature gate, API, documentation
As explained in https://github.com/kubernetes/enhancements/tree/master/keps/sig-storage/1698-generic-ephemeral-volumes, CSI inline volumes are not suitable for more "normal" kinds of storage systems. For those a new approach is needed: "generic ephemeral inline volumes".
1 parent 896da22 commit c05c8e9

File tree

15 files changed

+743
-151
lines changed

15 files changed

+743
-151
lines changed

pkg/api/pod/util.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -431,6 +431,7 @@ func dropDisabledFields(
431431
dropDisabledProcMountField(podSpec, oldPodSpec)
432432

433433
dropDisabledCSIVolumeSourceAlphaFields(podSpec, oldPodSpec)
434+
dropDisabledEphemeralVolumeSourceAlphaFields(podSpec, oldPodSpec)
434435

435436
if !utilfeature.DefaultFeatureGate.Enabled(features.NonPreemptingPriority) &&
436437
!podPriorityInUse(oldPodSpec) {
@@ -499,6 +500,16 @@ func dropDisabledCSIVolumeSourceAlphaFields(podSpec, oldPodSpec *api.PodSpec) {
499500
}
500501
}
501502

503+
// dropDisabledEphemeralVolumeSourceAlphaFields removes disabled alpha fields from []EphemeralVolumeSource.
504+
// This should be called from PrepareForCreate/PrepareForUpdate for all pod specs resources containing a EphemeralVolumeSource
505+
func dropDisabledEphemeralVolumeSourceAlphaFields(podSpec, oldPodSpec *api.PodSpec) {
506+
if !utilfeature.DefaultFeatureGate.Enabled(features.GenericEphemeralVolume) && !csiInUse(oldPodSpec) {
507+
for i := range podSpec.Volumes {
508+
podSpec.Volumes[i].Ephemeral = nil
509+
}
510+
}
511+
}
512+
502513
func ephemeralContainersInUse(podSpec *api.PodSpec) bool {
503514
if podSpec == nil {
504515
return false

pkg/api/testing/backward_compatibility_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ func TestCompatibility_v1_PodSecurityContext(t *testing.T) {
159159
}
160160

161161
validator := func(obj runtime.Object) field.ErrorList {
162-
return validation.ValidatePodSpec(&(obj.(*api.Pod).Spec), field.NewPath("spec"))
162+
return validation.ValidatePodSpec(&(obj.(*api.Pod).Spec), &(obj.(*api.Pod).ObjectMeta), field.NewPath("spec"))
163163
}
164164

165165
for _, tc := range cases {

pkg/apis/core/fuzzer/fuzzer.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,14 @@ var Funcs = func(codecs runtimeserializer.CodecFactory) []interface{} {
263263
i.ISCSIInterface = "default"
264264
}
265265
},
266+
func(i *core.PersistentVolumeClaimSpec, c fuzz.Continue) {
267+
// Match defaulting in pkg/apis/core/v1/defaults.go.
268+
volumeMode := core.PersistentVolumeMode(c.RandString())
269+
if volumeMode == "" {
270+
volumeMode = core.PersistentVolumeFilesystem
271+
}
272+
i.VolumeMode = &volumeMode
273+
},
266274
func(d *core.DNSPolicy, c fuzz.Continue) {
267275
policies := []core.DNSPolicy{core.DNSClusterFirst, core.DNSDefault}
268276
*d = policies[c.Rand.Intn(len(policies))]

pkg/apis/core/types.go

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,33 @@ type VolumeSource struct {
157157
// CSI (Container Storage Interface) represents ephemeral storage that is handled by certain external CSI drivers (Beta feature).
158158
// +optional
159159
CSI *CSIVolumeSource
160+
// Ephemeral represents a volume that is handled by a cluster storage driver (Alpha feature).
161+
// The volume's lifecycle is tied to the pod that defines it - it will be created before the pod starts,
162+
// and deleted when the pod is removed.
163+
//
164+
// Use this if:
165+
// a) the volume is only needed while the pod runs,
166+
// b) features of normal volumes like restoring from snapshot or capacity
167+
// tracking are needed,
168+
// c) the storage driver is specified through a storage class, and
169+
// d) the storage driver supports dynamic volume provisioning through
170+
// a PersistentVolumeClaim (see EphemeralVolumeSource for more
171+
// information on the connection between this volume type
172+
// and PersistentVolumeClaim).
173+
//
174+
// Use PersistentVolumeClaim or one of the vendor-specific
175+
// APIs for volumes that persist for longer than the lifecycle
176+
// of an individual pod.
177+
//
178+
// Use CSI for light-weight local ephemeral volumes if the CSI driver is meant to
179+
// be used that way - see the documentation of the driver for
180+
// more information.
181+
//
182+
// A pod can use both types of ephemeral volumes and
183+
// persistent volumes at the same time.
184+
//
185+
// +optional
186+
Ephemeral *EphemeralVolumeSource
160187
}
161188

162189
// PersistentVolumeSource is similar to VolumeSource but meant for the administrator who creates PVs.
@@ -1670,6 +1697,53 @@ type CSIVolumeSource struct {
16701697
NodePublishSecretRef *LocalObjectReference
16711698
}
16721699

1700+
// EphemeralVolumeSource represents an ephemeral volume that is handled by a normal storage driver.
1701+
type EphemeralVolumeSource struct {
1702+
// VolumeClaimTemplate will be used to create a stand-alone PVC to provision the volume.
1703+
// The pod in which this EphemeralVolumeSource is embedded will be the
1704+
// owner of the PVC, i.e. the PVC will be deleted together with the
1705+
// pod. The name of the PVC will be `<pod name>-<volume name>` where
1706+
// `<volume name>` is the name from the `PodSpec.Volumes` array
1707+
// entry. Pod validation will reject the pod if the concatenated name
1708+
// is not valid for a PVC (for example, too long).
1709+
//
1710+
// An existing PVC with that name that is not owned by the pod
1711+
// will *not* be used for the pod to avoid using an unrelated
1712+
// volume by mistake. Starting the pod is then blocked until
1713+
// the unrelated PVC is removed. If such a pre-created PVC is
1714+
// meant to be used by the pod, the PVC has to updated with an
1715+
// owner reference to the pod once the pod exists. Normally
1716+
// this should not be necessary, but it may be useful when
1717+
// manually reconstructing a broken cluster.
1718+
//
1719+
// This field is read-only and no changes will be made by Kubernetes
1720+
// to the PVC after it has been created.
1721+
//
1722+
// Required, must not be nil.
1723+
VolumeClaimTemplate *PersistentVolumeClaimTemplate
1724+
1725+
// ReadOnly specifies a read-only configuration for the volume.
1726+
// Defaults to false (read/write).
1727+
// +optional
1728+
ReadOnly bool
1729+
}
1730+
1731+
// PersistentVolumeClaimTemplate is used to produce
1732+
// PersistentVolumeClaim objects as part of an EphemeralVolumeSource.
1733+
type PersistentVolumeClaimTemplate struct {
1734+
// ObjectMeta may contain labels and annotations that will be copied into the PVC
1735+
// when creating it. No other fields are allowed and will be rejected during
1736+
// validation.
1737+
// +optional
1738+
metav1.ObjectMeta
1739+
1740+
// Spec for the PersistentVolumeClaim. The entire content is
1741+
// copied unchanged into the PVC that gets created from this
1742+
// template. The same fields as in a PersistentVolumeClaim
1743+
// are also valid here.
1744+
Spec PersistentVolumeClaimSpec
1745+
}
1746+
16731747
// ContainerPort represents a network port in a single container
16741748
type ContainerPort struct {
16751749
// Optional: If specified, this must be an IANA_SVC_NAME Each named port

pkg/apis/core/v1/defaults.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -285,9 +285,11 @@ func SetDefaults_PersistentVolumeClaim(obj *v1.PersistentVolumeClaim) {
285285
if obj.Status.Phase == "" {
286286
obj.Status.Phase = v1.ClaimPending
287287
}
288-
if obj.Spec.VolumeMode == nil {
289-
obj.Spec.VolumeMode = new(v1.PersistentVolumeMode)
290-
*obj.Spec.VolumeMode = v1.PersistentVolumeFilesystem
288+
}
289+
func SetDefaults_PersistentVolumeClaimSpec(obj *v1.PersistentVolumeClaimSpec) {
290+
if obj.VolumeMode == nil {
291+
obj.VolumeMode = new(v1.PersistentVolumeMode)
292+
*obj.VolumeMode = v1.PersistentVolumeFilesystem
291293
}
292294
}
293295
func SetDefaults_ISCSIVolumeSource(obj *v1.ISCSIVolumeSource) {

pkg/apis/core/v1/defaults_test.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ func TestWorkloadDefaults(t *testing.T) {
142142
".Spec.Volumes[0].VolumeSource.DownwardAPI.DefaultMode": `420`,
143143
".Spec.Volumes[0].VolumeSource.DownwardAPI.Items[0].FieldRef.APIVersion": `"v1"`,
144144
".Spec.Volumes[0].VolumeSource.EmptyDir": `{}`,
145+
".Spec.Volumes[0].VolumeSource.Ephemeral.VolumeClaimTemplate.Spec.VolumeMode": `"Filesystem"`,
145146
".Spec.Volumes[0].VolumeSource.HostPath.Type": `""`,
146147
".Spec.Volumes[0].VolumeSource.ISCSI.ISCSIInterface": `"default"`,
147148
".Spec.Volumes[0].VolumeSource.Projected.DefaultMode": `420`,
@@ -265,6 +266,7 @@ func TestPodDefaults(t *testing.T) {
265266
".Spec.Volumes[0].VolumeSource.DownwardAPI.DefaultMode": `420`,
266267
".Spec.Volumes[0].VolumeSource.DownwardAPI.Items[0].FieldRef.APIVersion": `"v1"`,
267268
".Spec.Volumes[0].VolumeSource.EmptyDir": `{}`,
269+
".Spec.Volumes[0].VolumeSource.Ephemeral.VolumeClaimTemplate.Spec.VolumeMode": `"Filesystem"`,
268270
".Spec.Volumes[0].VolumeSource.HostPath.Type": `""`,
269271
".Spec.Volumes[0].VolumeSource.ISCSI.ISCSIInterface": `"default"`,
270272
".Spec.Volumes[0].VolumeSource.Projected.DefaultMode": `420`,
@@ -1375,6 +1377,58 @@ func TestSetDefaultPersistentVolumeClaim(t *testing.T) {
13751377
}
13761378
}
13771379

1380+
func TestSetDefaultEphemeral(t *testing.T) {
1381+
fsMode := v1.PersistentVolumeFilesystem
1382+
blockMode := v1.PersistentVolumeBlock
1383+
1384+
tests := []struct {
1385+
name string
1386+
volumeMode *v1.PersistentVolumeMode
1387+
expectedVolumeMode v1.PersistentVolumeMode
1388+
}{
1389+
{
1390+
name: "volume mode nil",
1391+
volumeMode: nil,
1392+
expectedVolumeMode: v1.PersistentVolumeFilesystem,
1393+
},
1394+
{
1395+
name: "volume mode filesystem",
1396+
volumeMode: &fsMode,
1397+
expectedVolumeMode: v1.PersistentVolumeFilesystem,
1398+
},
1399+
{
1400+
name: "volume mode block",
1401+
volumeMode: &blockMode,
1402+
expectedVolumeMode: v1.PersistentVolumeBlock,
1403+
},
1404+
}
1405+
1406+
for _, test := range tests {
1407+
pod := &v1.Pod{
1408+
Spec: v1.PodSpec{
1409+
Volumes: []v1.Volume{
1410+
{
1411+
VolumeSource: v1.VolumeSource{
1412+
Ephemeral: &v1.EphemeralVolumeSource{
1413+
VolumeClaimTemplate: &v1.PersistentVolumeClaimTemplate{
1414+
Spec: v1.PersistentVolumeClaimSpec{
1415+
VolumeMode: test.volumeMode,
1416+
},
1417+
},
1418+
},
1419+
},
1420+
},
1421+
},
1422+
},
1423+
}
1424+
obj1 := roundTrip(t, runtime.Object(pod))
1425+
pod1 := obj1.(*v1.Pod)
1426+
if *pod1.Spec.Volumes[0].VolumeSource.Ephemeral.VolumeClaimTemplate.Spec.VolumeMode != test.expectedVolumeMode {
1427+
t.Errorf("Test %s failed, Expected VolumeMode: %v, but got %v", test.name, test.volumeMode, *pod1.Spec.Volumes[0].VolumeSource.Ephemeral.VolumeClaimTemplate.Spec.VolumeMode)
1428+
}
1429+
}
1430+
}
1431+
13781432
func TestSetDefaultEndpointsProtocol(t *testing.T) {
13791433
in := &v1.Endpoints{Subsets: []v1.EndpointSubset{
13801434
{Ports: []v1.EndpointPort{{}, {Protocol: "UDP"}, {}}},

0 commit comments

Comments
 (0)