Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions cmd/schema-tweak/overrides.go
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,9 @@ func revSpecOverrides(prefixPath string) []entry {
}, {
name: "image",
flag: config.FeaturePodSpecVolumesImage,
}, {
name: "ephemeral",
flag: config.FeaturePodSpecVolumesEphemeral,
}},
}, {
path: "volumes.secret",
Expand Down
5 changes: 5 additions & 0 deletions config/core/300-resources/configuration.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1180,6 +1180,11 @@ spec:
This is accessible behind a feature flag - kubernetes.podspec-volumes-emptydir
type: object
x-kubernetes-preserve-unknown-fields: true
ephemeral:
description: |-
This is accessible behind a feature flag - kubernetes.podspec-volumes-ephemeral
type: object
x-kubernetes-preserve-unknown-fields: true
Comment on lines +1183 to +1187
Copy link
Copy Markdown
Member

@linkvt linkvt May 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just learned that we should extend the following code to get these changes instead of manually applying them:

}, {
name: "csi",
flag: config.FeaturePodSpecVolumesCSI,
}, {
name: "image",
flag: config.FeaturePodSpecVolumesImage,
}},

(CI failed for this reason)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I missed that. I'll get that updated.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added this and the related test is passing now.

hostPath:
description: |-
This is accessible behind a feature flag - kubernetes.podspec-volumes-hostpath
Expand Down
5 changes: 5 additions & 0 deletions config/core/300-resources/revision.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1156,6 +1156,11 @@ spec:
This is accessible behind a feature flag - kubernetes.podspec-volumes-emptydir
type: object
x-kubernetes-preserve-unknown-fields: true
ephemeral:
description: |-
This is accessible behind a feature flag - kubernetes.podspec-volumes-ephemeral
type: object
x-kubernetes-preserve-unknown-fields: true
hostPath:
description: |-
This is accessible behind a feature flag - kubernetes.podspec-volumes-hostpath
Expand Down
5 changes: 5 additions & 0 deletions config/core/300-resources/service.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1198,6 +1198,11 @@ spec:
This is accessible behind a feature flag - kubernetes.podspec-volumes-emptydir
type: object
x-kubernetes-preserve-unknown-fields: true
ephemeral:
description: |-
This is accessible behind a feature flag - kubernetes.podspec-volumes-ephemeral
type: object
x-kubernetes-preserve-unknown-fields: true
hostPath:
description: |-
This is accessible behind a feature flag - kubernetes.podspec-volumes-hostpath
Expand Down
8 changes: 7 additions & 1 deletion config/core/configmaps/features.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ metadata:
app.kubernetes.io/component: controller
app.kubernetes.io/version: devel
annotations:
knative.dev/example-checksum: "bee75b26"
knative.dev/example-checksum: "424df6ce"
data:
_example: |-
################################
Expand Down Expand Up @@ -207,6 +207,12 @@ data:
# 2. Disabled: disabling HostPath volume support
kubernetes.podspec-volumes-hostpath: "disabled"

# Controls whether volume support for generic ephemeral volumes is enabled or not.
# See https://kubernetes.io/docs/concepts/storage/ephemeral-volumes/#generic-ephemeral-volumes
# 1. Enabled: enabling generic ephemeral volume support
# 2. Disabled: disabling generic ephemeral volume support
kubernetes.podspec-volumes-ephemeral: "disabled"

# Controls whether volume support for CSI is enabled or not.
# 1. Enabled: enabling CSI volume support
# 2. Disabled: disabling CSI volume support
Expand Down
4 changes: 4 additions & 0 deletions pkg/apis/config/features.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ const (
FeaturePodSpecShareProcessNamespace = "kubernetes.podspec-shareprocessnamespace"
FeaturePodSpecTolerations = "kubernetes.podspec-tolerations"
FeaturePodSpecTopologySpreadConstraints = "kubernetes.podspec-topologyspreadconstraints"
FeaturePodSpecVolumesEphemeral = "kubernetes.podspec-volumes-ephemeral"
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should probably also add a test for this new feature as there are tests in features_test.go for the other Volumes

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, I'll add that.

FeaturePodSpecVolumesImage = "kubernetes.podspec-volumes-image"
)

Expand All @@ -102,6 +103,7 @@ func defaultFeaturesConfig() *Features {
PodSpecVolumesHostPath: Disabled,
PodSpecVolumesMountPropagation: Disabled,
PodSpecVolumesCSI: Disabled,
PodSpecVolumesEphemeral: Disabled,
PodSpecVolumesImage: Disabled,
PodSpecPersistentVolumeClaim: Disabled,
PodSpecPersistentVolumeWrite: Disabled,
Expand Down Expand Up @@ -141,6 +143,7 @@ func NewFeaturesConfigFromMap(data map[string]string) (*Features, error) {
asFlag(FeaturePodSpecHostPID, &nc.PodSpecHostPID),
asFlag(FeaturePodSpecHostPath, &nc.PodSpecVolumesHostPath),
asFlag(FeaturePodSpecVolumesCSI, &nc.PodSpecVolumesCSI),
asFlag(FeaturePodSpecVolumesEphemeral, &nc.PodSpecVolumesEphemeral),
asFlag(FeaturePodSpecVolumesImage, &nc.PodSpecVolumesImage),
asFlag(FeaturePodSpecInitContainers, &nc.PodSpecInitContainers),
asFlag(FeaturePodSpecVolumesMountPropagation, &nc.PodSpecVolumesMountPropagation),
Expand Down Expand Up @@ -187,6 +190,7 @@ type Features struct {
PodSpecVolumesHostPath Flag
PodSpecVolumesMountPropagation Flag
PodSpecVolumesCSI Flag
PodSpecVolumesEphemeral Flag
PodSpecVolumesImage Flag
PodSpecInitContainers Flag
PodSpecPersistentVolumeClaim Flag
Expand Down
18 changes: 18 additions & 0 deletions pkg/apis/config/features_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -480,6 +480,24 @@ func TestFeaturesConfiguration(t *testing.T) {
data: map[string]string{
"kubernetes.podspec-volumes-csi": "Enabled",
},
}, {
name: "kubernetes.podspec-volumes-ephemeral Disabled",
wantErr: false,
wantFeatures: defaultWith(&Features{
PodSpecVolumesEphemeral: Disabled,
}),
data: map[string]string{
"kubernetes.podspec-volumes-ephemeral": "Disabled",
},
}, {
name: "kubernetes.podspec-volumes-ephemeral Enabled",
wantErr: false,
wantFeatures: defaultWith(&Features{
PodSpecVolumesEphemeral: Enabled,
}),
data: map[string]string{
"kubernetes.podspec-volumes-ephemeral": "Enabled",
},
}, {
name: "kubernetes.podspec-persistent-volume-claim Disabled",
wantErr: false,
Expand Down
4 changes: 4 additions & 0 deletions pkg/apis/serving/fieldmask.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,10 @@ func VolumeSourceMask(ctx context.Context, in *corev1.VolumeSource) *corev1.Volu
out.CSI = in.CSI
}

if cfg.Features.PodSpecVolumesEphemeral != config.Disabled {
out.Ephemeral = in.Ephemeral
}

if cfg.Features.PodSpecVolumesImage != config.Disabled {
out.Image = in.Image
}
Expand Down
13 changes: 12 additions & 1 deletion pkg/apis/serving/k8s_validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,10 @@ func validateVolume(ctx context.Context, volume corev1.Volume) *apis.FieldError
errs = errs.Also(&apis.FieldError{Message: fmt.Sprintf("CSI volume support is disabled, "+
"but found CSI volume %s", volume.Name)})
}
if volume.Ephemeral != nil && features.PodSpecVolumesEphemeral != config.Enabled {
errs = errs.Also(&apis.FieldError{Message: fmt.Sprintf("Ephemeral volume support is disabled, "+
"but found Ephemeral volume %s", volume.Name)})
}
errs = errs.Also(apis.CheckDisallowedFields(volume, *VolumeMask(ctx, &volume)))
if volume.Name == "" {
errs = apis.ErrMissingField("name")
Expand Down Expand Up @@ -182,6 +186,10 @@ func validateVolume(ctx context.Context, volume corev1.Volume) *apis.FieldError
specified = append(specified, "csi")
}

if vs.Ephemeral != nil {
specified = append(specified, "ephemeral")
}

if vs.Image != nil {
specified = append(specified, "image")
errs = errs.Also(validateImageVolumeSource(vs.Image).ViaField("image"))
Expand All @@ -202,6 +210,9 @@ func validateVolume(ctx context.Context, volume corev1.Volume) *apis.FieldError
if cfg.Features.PodSpecVolumesCSI == config.Enabled {
fieldPaths = append(fieldPaths, "csi")
}
if cfg.Features.PodSpecVolumesEphemeral == config.Enabled {
fieldPaths = append(fieldPaths, "ephemeral")
}
if cfg.Features.PodSpecVolumesImage == config.Enabled {
fieldPaths = append(fieldPaths, "image")
}
Expand Down Expand Up @@ -717,7 +728,7 @@ func validateVolumeMounts(ctx context.Context, mounts []corev1.VolumeMount, volu
}
seenMountPath.Insert(path.Clean(vm.MountPath))

shouldCheckReadOnlyVolume := volumes[vm.Name].EmptyDir == nil && volumes[vm.Name].PersistentVolumeClaim == nil
shouldCheckReadOnlyVolume := volumes[vm.Name].EmptyDir == nil && volumes[vm.Name].PersistentVolumeClaim == nil && volumes[vm.Name].Ephemeral == nil
if shouldCheckReadOnlyVolume && !vm.ReadOnly {
errs = errs.Also((&apis.FieldError{
Message: "volume mount should be readOnly for this type of volume",
Expand Down
46 changes: 46 additions & 0 deletions pkg/apis/serving/k8s_validation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,13 @@ func withPodSpecVolumesCSIEnabled() configOption {
}
}

func withPodSpecVolumesEphemeralEnabled() configOption {
return func(cfg *config.Config) *config.Config {
cfg.Features.PodSpecVolumesEphemeral = config.Enabled
return cfg
}
}

func withPodSpecVolumesImageEnabled() configOption {
return func(cfg *config.Config) *config.Config {
cfg.Features.PodSpecVolumesImage = config.Enabled
Expand Down Expand Up @@ -3492,6 +3499,45 @@ func TestVolumeValidation(t *testing.T) {
},
cfgOpts: []configOption{withPodSpecVolumesImageEnabled()},
want: apis.ErrMissingOneOf("secret", "configMap", "projected", "emptyDir", "image"),
}, {
name: "valid ephemeral volume with feature enabled",
v: corev1.Volume{
Name: "foo",
VolumeSource: corev1.VolumeSource{
Ephemeral: &corev1.EphemeralVolumeSource{
VolumeClaimTemplate: &corev1.PersistentVolumeClaimTemplate{
Spec: corev1.PersistentVolumeClaimSpec{
AccessModes: []corev1.PersistentVolumeAccessMode{corev1.ReadWriteOnce},
},
},
},
},
},
cfgOpts: []configOption{withPodSpecVolumesEphemeralEnabled()},
}, {
name: "ephemeral volume with feature disabled",
v: corev1.Volume{
Name: "foo",
VolumeSource: corev1.VolumeSource{
Ephemeral: &corev1.EphemeralVolumeSource{
VolumeClaimTemplate: &corev1.PersistentVolumeClaimTemplate{
Spec: corev1.PersistentVolumeClaimSpec{
AccessModes: []corev1.PersistentVolumeAccessMode{corev1.ReadWriteOnce},
},
},
},
},
},
want: (&apis.FieldError{
Message: `Ephemeral volume support is disabled, but found Ephemeral volume foo`,
}).Also(&apis.FieldError{Message: "must not set the field(s)", Paths: []string{"ephemeral"}}),
}, {
name: "missing ephemeral volume when required",
v: corev1.Volume{
Name: "foo",
},
cfgOpts: []configOption{withPodSpecVolumesEphemeralEnabled()},
want: apis.ErrMissingOneOf("secret", "configMap", "projected", "emptyDir", "ephemeral"),
}}

for _, test := range tests {
Expand Down
2 changes: 1 addition & 1 deletion pkg/apis/serving/v1/revision_defaults.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ func (rs *RevisionSpec) applyDefault(ctx context.Context, container *corev1.Cont

vNames := make(sets.Set[string])
for _, v := range rs.PodSpec.Volumes {
if v.EmptyDir != nil || v.PersistentVolumeClaim != nil {
if v.EmptyDir != nil || v.PersistentVolumeClaim != nil || v.Ephemeral != nil {
vNames.Insert(v.Name)
}
}
Expand Down
Loading