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
6 changes: 5 additions & 1 deletion config/core/configmaps/features.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ metadata:
labels:
serving.knative.dev/release: devel
annotations:
knative.dev/example-checksum: "983ddf13"
knative.dev/example-checksum: "c0b41539"
data:
_example: |
################################
Expand Down Expand Up @@ -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.
#
Expand Down
41 changes: 22 additions & 19 deletions pkg/apis/config/features.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}
}

Expand All @@ -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),
Expand All @@ -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.
Expand Down
61 changes: 45 additions & 16 deletions pkg/apis/config/features_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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,
Expand Down
4 changes: 3 additions & 1 deletion pkg/apis/serving/fieldmask.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Copy link
Contributor

Choose a reason for hiding this comment

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

worth adding a test for this, along same lines as the one for PodSpecTolerations?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

out.RuntimeClassName = in.RuntimeClassName
}
if cfg.Features.PodSpecTolerations != config.Disabled {
out.Tolerations = in.Tolerations
}
Expand Down Expand Up @@ -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
}
Expand Down
19 changes: 19 additions & 0 deletions pkg/apis/serving/k8s_validation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -543,6 +550,8 @@ func TestPodSpecMultiContainerValidation(t *testing.T) {
}

func TestPodSpecFeatureValidation(t *testing.T) {
runtimeClassName := "test"
Copy link
Member

Choose a reason for hiding this comment

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

I assume there is no good way to validate the values passed (besides podspec dry run?), since it varies so much vendor to vendor...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I suppose the controller could validate that the RuntimeClass with that name exists and add to the knative service's status and emit an event if it doesn't. Currently the controller will fail to create pods and users will see that the service didn't create and see the detailed reason in events already so I'm not sure it would be a much better UX.


featureData := []struct {
name string
featureSpec corev1.PodSpec
Expand Down Expand Up @@ -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{
Expand Down