diff --git a/config/core/configmaps/features.yaml b/config/core/configmaps/features.yaml index c01121a1e4eb..287a4be8c3e5 100644 --- a/config/core/configmaps/features.yaml +++ b/config/core/configmaps/features.yaml @@ -20,7 +20,7 @@ metadata: labels: serving.knative.dev/release: devel annotations: - knative.dev/example-checksum: "983ddf13" + knative.dev/example-checksum: "c0b41539" data: _example: | ################################ @@ -62,6 +62,10 @@ data: # attaching the following metadata annotation: "features.knative.dev/podspec-dryrun":"enabled". kubernetes.podspec-dryrun: "allowed" + # When set to "enabled" or "allowed" this feature allows end-users to set + # the Pod's RuntimeClassName. + kubernetes.podspec-runtimeclassname: "disabled" + # This feature allows end-users to set a subset of fields on the Pod's SecurityContext # in addition to expanding the allowable fields within a Container's SecurityContext. # diff --git a/pkg/apis/config/features.go b/pkg/apis/config/features.go index 10bde135f6d2..4174ff112790 100644 --- a/pkg/apis/config/features.go +++ b/pkg/apis/config/features.go @@ -40,15 +40,16 @@ const ( func defaultFeaturesConfig() *Features { return &Features{ - MultiContainer: Enabled, - PodSpecAffinity: Disabled, - PodSpecFieldRef: Disabled, - PodSpecDryRun: Allowed, - PodSpecNodeSelector: Disabled, - PodSpecSecurityContext: Disabled, - PodSpecTolerations: Disabled, - ResponsiveRevisionGC: Disabled, - TagHeaderBasedRouting: Disabled, + MultiContainer: Enabled, + PodSpecAffinity: Disabled, + PodSpecDryRun: Allowed, + PodSpecFieldRef: Disabled, + PodSpecNodeSelector: Disabled, + PodSpecRuntimeClassName: Disabled, + PodSpecSecurityContext: Disabled, + PodSpecTolerations: Disabled, + ResponsiveRevisionGC: Disabled, + TagHeaderBasedRouting: Disabled, } } @@ -59,9 +60,10 @@ func NewFeaturesConfigFromMap(data map[string]string) (*Features, error) { if err := cm.Parse(data, asFlag("multi-container", &nc.MultiContainer), asFlag("kubernetes.podspec-affinity", &nc.PodSpecAffinity), - asFlag("kubernetes.podspec-fieldref", &nc.PodSpecFieldRef), asFlag("kubernetes.podspec-dryrun", &nc.PodSpecDryRun), + asFlag("kubernetes.podspec-fieldref", &nc.PodSpecFieldRef), asFlag("kubernetes.podspec-nodeselector", &nc.PodSpecNodeSelector), + asFlag("kubernetes.podspec-runtimeclassname", &nc.PodSpecRuntimeClassName), asFlag("kubernetes.podspec-securitycontext", &nc.PodSpecSecurityContext), asFlag("kubernetes.podspec-tolerations", &nc.PodSpecTolerations), asFlag("responsive-revision-gc", &nc.ResponsiveRevisionGC), @@ -78,15 +80,16 @@ func NewFeaturesConfigFromConfigMap(config *corev1.ConfigMap) (*Features, error) // Features specifies which features are allowed by the webhook. type Features struct { - MultiContainer Flag - PodSpecAffinity Flag - PodSpecFieldRef Flag - PodSpecDryRun Flag - PodSpecNodeSelector Flag - PodSpecTolerations Flag - PodSpecSecurityContext Flag - ResponsiveRevisionGC Flag - TagHeaderBasedRouting Flag + MultiContainer Flag + PodSpecAffinity Flag + PodSpecDryRun Flag + PodSpecFieldRef Flag + PodSpecNodeSelector Flag + PodSpecRuntimeClassName Flag + PodSpecSecurityContext Flag + PodSpecTolerations Flag + ResponsiveRevisionGC Flag + TagHeaderBasedRouting Flag } // asFlag parses the value at key as a Flag into the target, if it exists. diff --git a/pkg/apis/config/features_test.go b/pkg/apis/config/features_test.go index f43e059d58f6..587040546262 100644 --- a/pkg/apis/config/features_test.go +++ b/pkg/apis/config/features_test.go @@ -59,24 +59,26 @@ func TestFeaturesConfiguration(t *testing.T) { name: "features Enabled", wantErr: false, wantFeatures: defaultWith(&Features{ - MultiContainer: Enabled, - PodSpecAffinity: Enabled, - PodSpecDryRun: Enabled, - PodSpecNodeSelector: Enabled, - PodSpecSecurityContext: Enabled, - PodSpecTolerations: Enabled, - ResponsiveRevisionGC: Enabled, - TagHeaderBasedRouting: Enabled, + MultiContainer: Enabled, + PodSpecAffinity: Enabled, + PodSpecDryRun: Enabled, + PodSpecNodeSelector: Enabled, + PodSpecRuntimeClassName: Enabled, + PodSpecSecurityContext: Enabled, + PodSpecTolerations: Enabled, + ResponsiveRevisionGC: Enabled, + TagHeaderBasedRouting: Enabled, }), data: map[string]string{ - "multi-container": "Enabled", - "kubernetes.podspec-affinity": "Enabled", - "kubernetes.podspec-dryrun": "Enabled", - "kubernetes.podspec-nodeselector": "Enabled", - "kubernetes.podspec-securitycontext": "Enabled", - "kubernetes.podspec-tolerations": "Enabled", - "responsive-revision-gc": "Enabled", - "tag-header-based-routing": "Enabled", + "multi-container": "Enabled", + "kubernetes.podspec-affinity": "Enabled", + "kubernetes.podspec-dryrun": "Enabled", + "kubernetes.podspec-nodeselector": "Enabled", + "kubernetes.podspec-runtimeclassname": "Enabled", + "kubernetes.podspec-securitycontext": "Enabled", + "kubernetes.podspec-tolerations": "Enabled", + "responsive-revision-gc": "Enabled", + "tag-header-based-routing": "Enabled", }, }, { name: "multi-container Allowed", @@ -186,6 +188,33 @@ func TestFeaturesConfiguration(t *testing.T) { data: map[string]string{ "kubernetes.podspec-nodeselector": "Disabled", }, + }, { + name: "kubernetes.podspec-runtimeclassname Allowed", + wantErr: false, + wantFeatures: defaultWith(&Features{ + PodSpecRuntimeClassName: Allowed, + }), + data: map[string]string{ + "kubernetes.podspec-runtimeclassname": "Allowed", + }, + }, { + name: "kubernetes.podspec-runtimeclassname Enabled", + wantErr: false, + wantFeatures: defaultWith(&Features{ + PodSpecRuntimeClassName: Enabled, + }), + data: map[string]string{ + "kubernetes.podspec-runtimeclassname": "Enabled", + }, + }, { + name: "kubernetes.podspec-runtimeclassname Disabled", + wantErr: false, + wantFeatures: defaultWith(&Features{ + PodSpecRuntimeClassName: Disabled, + }), + data: map[string]string{ + "kubernetes.podspec-runtimeclassname": "Disabled", + }, }, { name: "kubernetes.podspec-tolerations Allowed", wantErr: false, diff --git a/pkg/apis/serving/fieldmask.go b/pkg/apis/serving/fieldmask.go index aa3ee98b1abe..f345031f55f6 100644 --- a/pkg/apis/serving/fieldmask.go +++ b/pkg/apis/serving/fieldmask.go @@ -161,6 +161,9 @@ func PodSpecMask(ctx context.Context, in *corev1.PodSpec) *corev1.PodSpec { if cfg.Features.PodSpecNodeSelector != config.Disabled { out.NodeSelector = in.NodeSelector } + if cfg.Features.PodSpecRuntimeClassName != config.Disabled { + out.RuntimeClassName = in.RuntimeClassName + } if cfg.Features.PodSpecTolerations != config.Disabled { out.Tolerations = in.Tolerations } @@ -189,7 +192,6 @@ func PodSpecMask(ctx context.Context, in *corev1.PodSpec) *corev1.PodSpec { out.Priority = nil out.DNSConfig = nil out.ReadinessGates = nil - out.RuntimeClassName = nil return out } diff --git a/pkg/apis/serving/k8s_validation_test.go b/pkg/apis/serving/k8s_validation_test.go index dfd0643c460b..b51e507865e7 100644 --- a/pkg/apis/serving/k8s_validation_test.go +++ b/pkg/apis/serving/k8s_validation_test.go @@ -69,6 +69,13 @@ func withPodSpecTolerationsEnabled() configOption { } } +func withPodSpecRuntimeClassNameEnabled() configOption { + return func(cfg *config.Config) *config.Config { + cfg.Features.PodSpecRuntimeClassName = config.Enabled + return cfg + } +} + func withPodSpecSecurityContextEnabled() configOption { return func(cfg *config.Config) *config.Config { cfg.Features.PodSpecSecurityContext = config.Enabled @@ -543,6 +550,8 @@ func TestPodSpecMultiContainerValidation(t *testing.T) { } func TestPodSpecFeatureValidation(t *testing.T) { + runtimeClassName := "test" + featureData := []struct { name string featureSpec corev1.PodSpec @@ -597,6 +606,16 @@ func TestPodSpecFeatureValidation(t *testing.T) { Paths: []string{"tolerations"}, }, cfgOpts: []configOption{withPodSpecTolerationsEnabled()}, + }, { + name: "RuntimeClassName", + featureSpec: corev1.PodSpec{ + RuntimeClassName: &runtimeClassName, + }, + err: &apis.FieldError{ + Message: "must not set the field(s)", + Paths: []string{"runtimeClassName"}, + }, + cfgOpts: []configOption{withPodSpecRuntimeClassNameEnabled()}, }, { name: "PodSpecSecurityContext", featureSpec: corev1.PodSpec{