Allow access to Pod field `shareProcessNamespace`. (#14454)

* Allow access to Pod field `shareProcessNamespace`.

Feature is guarded by feature flag "kubernetes.podspec-shareprocessnamespace".
Fixes #14137.

* Add missing test for shareProcessNamespace
This commit is contained in:
Roland Huß 2023-09-28 21:50:20 +02:00 committed by GitHub
parent eea9f67ff8
commit 05e349fa97
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 300 additions and 231 deletions

View File

@ -629,6 +629,10 @@ spec:
serviceAccountName:
description: 'ServiceAccountName is the name of the ServiceAccount to use to run this pod. More info: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/'
type: string
shareProcessNamespace:
description: This is accessible behind a feature flag - kubernetes.podspec-shareproccessnamespace
type: boolean
x-kubernetes-preserve-unknown-fields: true
timeoutSeconds:
description: TimeoutSeconds is the maximum duration in seconds that the request instance is allowed to respond to a request. If unspecified, a system default will be provided.
type: integer

View File

@ -608,6 +608,10 @@ spec:
serviceAccountName:
description: 'ServiceAccountName is the name of the ServiceAccount to use to run this pod. More info: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/'
type: string
shareProcessNamespace:
description: This is accessible behind a feature flag - kubernetes.podspec-shareproccessnamespace
type: boolean
x-kubernetes-preserve-unknown-fields: true
timeoutSeconds:
description: TimeoutSeconds is the maximum duration in seconds that the request instance is allowed to respond to a request. If unspecified, a system default will be provided.
type: integer

View File

@ -633,6 +633,10 @@ spec:
serviceAccountName:
description: 'ServiceAccountName is the name of the ServiceAccount to use to run this pod. More info: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/'
type: string
shareProcessNamespace:
description: This is accessible behind a feature flag - kubernetes.podspec-shareproccessnamespace
type: boolean
x-kubernetes-preserve-unknown-fields: true
timeoutSeconds:
description: TimeoutSeconds is the maximum duration in seconds that the request instance is allowed to respond to a request. If unspecified, a system default will be provided.
type: integer

View File

@ -22,7 +22,7 @@ metadata:
app.kubernetes.io/component: controller
app.kubernetes.io/version: devel
annotations:
knative.dev/example-checksum: "eb70e734"
knative.dev/example-checksum: "f2fc138e"
data:
_example: |-
################################
@ -126,6 +126,12 @@ data:
# See: https://knative.dev/docs/serving/feature-flags/#kubernetes-security-context
kubernetes.podspec-securitycontext: "disabled"
# Indicated whether sharing the process namespace via ShareProcessNamespace pod spec is allowed.
# This can be especially useful for sharing data from images directly between sidecars
#
# See: https://knative.dev/docs/serving/configuration/feature-flags/#kubernetes-share-process-namespace
kubernetes.podspec-shareprocessnamespace: "disabled"
# Indicates whether Kubernetes PriorityClassName support is enabled
#
# WARNING: Cannot safely be disabled once enabled.

View File

@ -69,6 +69,7 @@ k8s.io/api/core/v1.PodSpec:
- RuntimeClassName
- SchedulerName
- SecurityContext
- ShareProcessNamespace
- Tolerations
- TopologySpreadConstraints
fieldOverrides:
@ -130,9 +131,15 @@ k8s.io/api/core/v1.PodSpec:
SecurityContext:
description: "This is accessible behind a feature flag - kubernetes.podspec-securitycontext"
additionalMarkers:
# Part of a feature flag - so we want to omit the schema and preserve unknown fields
- kubebuilder:validation:DropProperties
- kubebuilder:pruning:PreserveUnknownFields
# Part of a feature flag - so we want to omit the schema and preserve unknown fields
- kubebuilder:validation:DropProperties
- kubebuilder:pruning:PreserveUnknownFields
ShareProcessNamespace:
description: "This is accessible behind a feature flag - kubernetes.podspec-shareproccessnamespace"
additionalMarkers:
# Part of a feature flag - so we want to omit the schema and preserve unknown fields
- kubebuilder:validation:DropProperties
- kubebuilder:pruning:PreserveUnknownFields
Tolerations:
description: "This is accessible behind a feature flag - kubernetes.podspec-tolerations"
itemOverride:

View File

@ -59,6 +59,7 @@ func defaultFeaturesConfig() *Features {
PodSpecNodeSelector: Disabled,
PodSpecRuntimeClassName: Disabled,
PodSpecSecurityContext: Disabled,
PodSpecShareProcessNamespace: Disabled,
PodSpecPriorityClassName: Disabled,
PodSpecSchedulerName: Disabled,
ContainerSpecAddCapabilities: Disabled,
@ -91,6 +92,7 @@ func NewFeaturesConfigFromMap(data map[string]string) (*Features, error) {
asFlag("kubernetes.podspec-nodeselector", &nc.PodSpecNodeSelector),
asFlag("kubernetes.podspec-runtimeclassname", &nc.PodSpecRuntimeClassName),
asFlag("kubernetes.podspec-securitycontext", &nc.PodSpecSecurityContext),
asFlag("kubernetes.podspec-shareprocessnamespace", &nc.PodSpecShareProcessNamespace),
asFlag("kubernetes.podspec-priorityclassname", &nc.PodSpecPriorityClassName),
asFlag("kubernetes.podspec-schedulername", &nc.PodSpecSchedulerName),
asFlag("kubernetes.containerspec-addcapabilities", &nc.ContainerSpecAddCapabilities),
@ -127,6 +129,7 @@ type Features struct {
PodSpecNodeSelector Flag
PodSpecRuntimeClassName Flag
PodSpecSecurityContext Flag
PodSpecShareProcessNamespace Flag
PodSpecPriorityClassName Flag
PodSpecSchedulerName Flag
ContainerSpecAddCapabilities Flag

View File

@ -67,6 +67,7 @@ func TestFeaturesConfiguration(t *testing.T) {
PodSpecNodeSelector: Enabled,
PodSpecRuntimeClassName: Enabled,
PodSpecSecurityContext: Enabled,
PodSpecShareProcessNamespace: Enabled,
PodSpecTolerations: Enabled,
PodSpecPriorityClassName: Enabled,
PodSpecSchedulerName: Enabled,
@ -85,6 +86,7 @@ func TestFeaturesConfiguration(t *testing.T) {
"kubernetes.podspec-nodeselector": "Enabled",
"kubernetes.podspec-runtimeclassname": "Enabled",
"kubernetes.podspec-securitycontext": "Enabled",
"kubernetes.podspec-shareprocessnamespace": "Enabled",
"kubernetes.podspec-tolerations": "Enabled",
"kubernetes.podspec-priorityclassname": "Enabled",
"kubernetes.podspec-schedulername": "Enabled",
@ -328,232 +330,251 @@ func TestFeaturesConfiguration(t *testing.T) {
data: map[string]string{
"kubernetes.podspec-securitycontext": "Disabled",
},
}, {
name: "kubernetes.containerspec-addcapabilities Disabled",
wantErr: false,
wantFeatures: defaultWith(&Features{
ContainerSpecAddCapabilities: Disabled,
}),
data: map[string]string{
"kubernetes.containerspec-addcapabilities": "Disabled",
},
}, {
name: "kubernetes.containerspec-addcapabilities Enabled",
wantErr: false,
wantFeatures: defaultWith(&Features{
ContainerSpecAddCapabilities: Enabled,
}),
data: map[string]string{
"kubernetes.containerspec-addcapabilities": "Enabled",
},
}, {
name: "kubernetes.containerspec-addcapabilities Allowed",
wantErr: false,
wantFeatures: defaultWith(&Features{
ContainerSpecAddCapabilities: Allowed,
}),
data: map[string]string{
"kubernetes.containerspec-addcapabilities": "Allowed",
},
}, {
name: "tag-header-based-routing Allowed",
wantErr: false,
wantFeatures: defaultWith(&Features{
TagHeaderBasedRouting: Allowed,
}),
data: map[string]string{
"tag-header-based-routing": "Allowed",
},
}, {
name: "tag-header-based-routing Enabled",
wantErr: false,
wantFeatures: defaultWith(&Features{
TagHeaderBasedRouting: Enabled,
}),
data: map[string]string{
"tag-header-based-routing": "Enabled",
},
}, {
name: "kubernetes.podspec-volumes-emptyDir Disabled",
wantErr: false,
wantFeatures: defaultWith(&Features{
PodSpecVolumesEmptyDir: Disabled,
}),
data: map[string]string{
"kubernetes.podspec-volumes-emptydir": "Disabled",
},
}, {
name: "kubernetes.podspec-volumes-emptyDir Enabled",
wantErr: false,
wantFeatures: defaultWith(&Features{
PodSpecVolumesEmptyDir: Enabled,
}),
data: map[string]string{
"kubernetes.podspec-volumes-emptydir": "Enabled",
},
}, {
name: "kubernetes.podspec-persistent-volume-claim Disabled",
wantErr: false,
wantFeatures: defaultWith(&Features{
PodSpecPersistentVolumeClaim: Disabled,
}),
data: map[string]string{
"kubernetes.podspec-persistent-volume-claim": "Disabled",
},
}, {
name: "kubernetes.podspec-persistent-volume-claim Enabled",
wantErr: false,
wantFeatures: defaultWith(&Features{
PodSpecPersistentVolumeClaim: Enabled,
}),
data: map[string]string{
"kubernetes.podspec-persistent-volume-claim": "Enabled",
},
}, {
name: "kubernetes.podspec-persistent-volume-write Disabled",
wantErr: false,
wantFeatures: defaultWith(&Features{
PodSpecPersistentVolumeWrite: Disabled,
}),
data: map[string]string{
"kubernetes.podspec-persistent-volume-write": "Disabled",
},
}, {
name: "kubernetes.podspec-persistent-volume-claim Enabled",
wantErr: false,
wantFeatures: defaultWith(&Features{
PodSpecPersistentVolumeWrite: Enabled,
}),
data: map[string]string{
"kubernetes.podspec-persistent-volume-write": "Enabled",
},
}, {
name: "kubernetes.podspec-init-containers Disabled",
wantErr: false,
wantFeatures: defaultWith(&Features{
PodSpecInitContainers: Disabled,
}),
data: map[string]string{
"kubernetes.podspec-init-containers": "Disabled",
},
}, {
name: "kubernetes.podspec-init-container Enabled",
wantErr: false,
wantFeatures: defaultWith(&Features{
PodSpecInitContainers: Enabled,
}),
data: map[string]string{
"kubernetes.podspec-init-containers": "Enabled",
},
}, {
name: "kubernetes.podspec-priorityclassname Allowed",
wantErr: false,
wantFeatures: defaultWith(&Features{
PodSpecPriorityClassName: Allowed,
}),
data: map[string]string{
"kubernetes.podspec-priorityclassname": "Allowed",
},
}, {
name: "kubernetes.podspec-priorityclassname Enabled",
wantErr: false,
wantFeatures: defaultWith(&Features{
PodSpecPriorityClassName: Enabled,
}),
data: map[string]string{
"kubernetes.podspec-priorityclassname": "Enabled",
},
}, {
name: "kubernetes.podspec-priorityclassname Disabled",
wantErr: false,
wantFeatures: defaultWith(&Features{
PodSpecPriorityClassName: Disabled,
}),
data: map[string]string{
"kubernetes.podspec-priorityclassname": "Disabled",
},
}, {
name: "kubernetes.podspec-schedulername Allowed",
wantErr: false,
wantFeatures: defaultWith(&Features{
PodSpecSchedulerName: Allowed,
}),
data: map[string]string{
"kubernetes.podspec-schedulername": "Allowed",
},
}, {
name: "kubernetes.podspec-schedulername Enabled",
wantErr: false,
wantFeatures: defaultWith(&Features{
PodSpecSchedulerName: Enabled,
}),
data: map[string]string{
"kubernetes.podspec-schedulername": "Enabled",
},
}, {
name: "kubernetes.podspec-schedulername Disabled",
wantErr: false,
wantFeatures: defaultWith(&Features{
PodSpecSchedulerName: Disabled,
}),
data: map[string]string{
"kubernetes.podspec-schedulername": "Disabled",
},
}, {
name: "kubernetes.podspec-dnspolicy Allowed",
wantErr: false,
wantFeatures: defaultWith(&Features{
PodSpecDNSPolicy: Allowed,
}),
data: map[string]string{
"kubernetes.podspec-dnspolicy": "Allowed",
},
}, {
name: "kubernetes.podspec-dnspolicy Enabled",
wantErr: false,
wantFeatures: defaultWith(&Features{
PodSpecDNSPolicy: Enabled,
}),
data: map[string]string{
"kubernetes.podspec-dnspolicy": "Enabled",
},
}, {
name: "kubernetes.podspec-dnspolicy Disabled",
wantErr: false,
wantFeatures: defaultWith(&Features{
PodSpecDNSPolicy: Disabled,
}),
data: map[string]string{
"kubernetes.podspec-dnspolicy": "Disabled",
},
}, {
name: "kubernetes.podspec-dnsconfig Allowed",
wantErr: false,
wantFeatures: defaultWith(&Features{
PodSpecDNSConfig: Allowed,
}),
data: map[string]string{
"kubernetes.podspec-dnsconfig": "Allowed",
},
}, {
name: "kubernetes.podspec-dnsconfig Enabled",
wantErr: false,
wantFeatures: defaultWith(&Features{
PodSpecDNSConfig: Enabled,
}),
data: map[string]string{
"kubernetes.podspec-dnsconfig": "Enabled",
},
}, {
name: "kubernetes.podspec-dnsconfig Disabled",
wantErr: false,
wantFeatures: defaultWith(&Features{
PodSpecDNSConfig: Disabled,
}),
data: map[string]string{
"kubernetes.podspec-dnsconfig": "Disabled",
},
}}
},
{
name: "shared process namespace Allowed",
wantErr: false,
wantFeatures: defaultWith(&Features{
PodSpecShareProcessNamespace: Allowed,
}),
data: map[string]string{
"kubernetes.podspec-shareprocessnamespace": "Allowed",
},
}, {
name: "shared process namespace Disabled",
wantErr: false,
wantFeatures: defaultWith(&Features{
PodSpecShareProcessNamespace: Disabled,
}),
data: map[string]string{
"kubernetes.podspec-shareprocessnamespace": "Disabled",
},
}, {
name: "kubernetes.containerspec-addcapabilities Disabled",
wantErr: false,
wantFeatures: defaultWith(&Features{
ContainerSpecAddCapabilities: Disabled,
}),
data: map[string]string{
"kubernetes.containerspec-addcapabilities": "Disabled",
},
}, {
name: "kubernetes.containerspec-addcapabilities Enabled",
wantErr: false,
wantFeatures: defaultWith(&Features{
ContainerSpecAddCapabilities: Enabled,
}),
data: map[string]string{
"kubernetes.containerspec-addcapabilities": "Enabled",
},
}, {
name: "kubernetes.containerspec-addcapabilities Allowed",
wantErr: false,
wantFeatures: defaultWith(&Features{
ContainerSpecAddCapabilities: Allowed,
}),
data: map[string]string{
"kubernetes.containerspec-addcapabilities": "Allowed",
},
}, {
name: "tag-header-based-routing Allowed",
wantErr: false,
wantFeatures: defaultWith(&Features{
TagHeaderBasedRouting: Allowed,
}),
data: map[string]string{
"tag-header-based-routing": "Allowed",
},
}, {
name: "tag-header-based-routing Enabled",
wantErr: false,
wantFeatures: defaultWith(&Features{
TagHeaderBasedRouting: Enabled,
}),
data: map[string]string{
"tag-header-based-routing": "Enabled",
},
}, {
name: "kubernetes.podspec-volumes-emptyDir Disabled",
wantErr: false,
wantFeatures: defaultWith(&Features{
PodSpecVolumesEmptyDir: Disabled,
}),
data: map[string]string{
"kubernetes.podspec-volumes-emptydir": "Disabled",
},
}, {
name: "kubernetes.podspec-volumes-emptyDir Enabled",
wantErr: false,
wantFeatures: defaultWith(&Features{
PodSpecVolumesEmptyDir: Enabled,
}),
data: map[string]string{
"kubernetes.podspec-volumes-emptydir": "Enabled",
},
}, {
name: "kubernetes.podspec-persistent-volume-claim Disabled",
wantErr: false,
wantFeatures: defaultWith(&Features{
PodSpecPersistentVolumeClaim: Disabled,
}),
data: map[string]string{
"kubernetes.podspec-persistent-volume-claim": "Disabled",
},
}, {
name: "kubernetes.podspec-persistent-volume-claim Enabled",
wantErr: false,
wantFeatures: defaultWith(&Features{
PodSpecPersistentVolumeClaim: Enabled,
}),
data: map[string]string{
"kubernetes.podspec-persistent-volume-claim": "Enabled",
},
}, {
name: "kubernetes.podspec-persistent-volume-write Disabled",
wantErr: false,
wantFeatures: defaultWith(&Features{
PodSpecPersistentVolumeWrite: Disabled,
}),
data: map[string]string{
"kubernetes.podspec-persistent-volume-write": "Disabled",
},
}, {
name: "kubernetes.podspec-persistent-volume-claim Enabled",
wantErr: false,
wantFeatures: defaultWith(&Features{
PodSpecPersistentVolumeWrite: Enabled,
}),
data: map[string]string{
"kubernetes.podspec-persistent-volume-write": "Enabled",
},
}, {
name: "kubernetes.podspec-init-containers Disabled",
wantErr: false,
wantFeatures: defaultWith(&Features{
PodSpecInitContainers: Disabled,
}),
data: map[string]string{
"kubernetes.podspec-init-containers": "Disabled",
},
}, {
name: "kubernetes.podspec-init-container Enabled",
wantErr: false,
wantFeatures: defaultWith(&Features{
PodSpecInitContainers: Enabled,
}),
data: map[string]string{
"kubernetes.podspec-init-containers": "Enabled",
},
}, {
name: "kubernetes.podspec-priorityclassname Allowed",
wantErr: false,
wantFeatures: defaultWith(&Features{
PodSpecPriorityClassName: Allowed,
}),
data: map[string]string{
"kubernetes.podspec-priorityclassname": "Allowed",
},
}, {
name: "kubernetes.podspec-priorityclassname Enabled",
wantErr: false,
wantFeatures: defaultWith(&Features{
PodSpecPriorityClassName: Enabled,
}),
data: map[string]string{
"kubernetes.podspec-priorityclassname": "Enabled",
},
}, {
name: "kubernetes.podspec-priorityclassname Disabled",
wantErr: false,
wantFeatures: defaultWith(&Features{
PodSpecPriorityClassName: Disabled,
}),
data: map[string]string{
"kubernetes.podspec-priorityclassname": "Disabled",
},
}, {
name: "kubernetes.podspec-schedulername Allowed",
wantErr: false,
wantFeatures: defaultWith(&Features{
PodSpecSchedulerName: Allowed,
}),
data: map[string]string{
"kubernetes.podspec-schedulername": "Allowed",
},
}, {
name: "kubernetes.podspec-schedulername Enabled",
wantErr: false,
wantFeatures: defaultWith(&Features{
PodSpecSchedulerName: Enabled,
}),
data: map[string]string{
"kubernetes.podspec-schedulername": "Enabled",
},
}, {
name: "kubernetes.podspec-schedulername Disabled",
wantErr: false,
wantFeatures: defaultWith(&Features{
PodSpecSchedulerName: Disabled,
}),
data: map[string]string{
"kubernetes.podspec-schedulername": "Disabled",
},
}, {
name: "kubernetes.podspec-dnspolicy Allowed",
wantErr: false,
wantFeatures: defaultWith(&Features{
PodSpecDNSPolicy: Allowed,
}),
data: map[string]string{
"kubernetes.podspec-dnspolicy": "Allowed",
},
}, {
name: "kubernetes.podspec-dnspolicy Enabled",
wantErr: false,
wantFeatures: defaultWith(&Features{
PodSpecDNSPolicy: Enabled,
}),
data: map[string]string{
"kubernetes.podspec-dnspolicy": "Enabled",
},
}, {
name: "kubernetes.podspec-dnspolicy Disabled",
wantErr: false,
wantFeatures: defaultWith(&Features{
PodSpecDNSPolicy: Disabled,
}),
data: map[string]string{
"kubernetes.podspec-dnspolicy": "Disabled",
},
}, {
name: "kubernetes.podspec-dnsconfig Allowed",
wantErr: false,
wantFeatures: defaultWith(&Features{
PodSpecDNSConfig: Allowed,
}),
data: map[string]string{
"kubernetes.podspec-dnsconfig": "Allowed",
},
}, {
name: "kubernetes.podspec-dnsconfig Enabled",
wantErr: false,
wantFeatures: defaultWith(&Features{
PodSpecDNSConfig: Enabled,
}),
data: map[string]string{
"kubernetes.podspec-dnsconfig": "Enabled",
},
}, {
name: "kubernetes.podspec-dnsconfig Disabled",
wantErr: false,
wantFeatures: defaultWith(&Features{
PodSpecDNSConfig: Disabled,
}),
data: map[string]string{
"kubernetes.podspec-dnsconfig": "Disabled",
},
}}
for _, tt := range configTests {
t.Run(tt.name, func(t *testing.T) {

View File

@ -245,6 +245,9 @@ func PodSpecMask(ctx context.Context, in *corev1.PodSpec) *corev1.PodSpec {
// This is further validated in ValidatePodSecurityContext.
out.SecurityContext = in.SecurityContext
}
if cfg.Features.PodSpecShareProcessNamespace != config.Disabled {
out.ShareProcessNamespace = in.ShareProcessNamespace
}
if cfg.Features.PodSpecPriorityClassName != config.Disabled {
out.PriorityClassName = in.PriorityClassName
}
@ -270,7 +273,6 @@ func PodSpecMask(ctx context.Context, in *corev1.PodSpec) *corev1.PodSpec {
out.HostNetwork = false
out.HostPID = false
out.HostIPC = false
out.ShareProcessNamespace = nil
out.Hostname = ""
out.Subdomain = ""
out.Priority = nil

View File

@ -150,6 +150,13 @@ func withPodSpecSchedulerNameEnabled() configOption {
}
}
func withPodSpecProcessNamespaceEnabled() configOption {
return func(cfg *config.Config) *config.Config {
cfg.Features.PodSpecShareProcessNamespace = config.Enabled
return cfg
}
}
func withPodSpecInitContainersEnabled() configOption {
return func(cfg *config.Config) *config.Config {
cfg.Features.PodSpecInitContainers = config.Enabled
@ -1135,6 +1142,7 @@ func TestPodSpecMultiContainerValidation(t *testing.T) {
func TestPodSpecFeatureValidation(t *testing.T) {
runtimeClassName := "test"
shareProcessNamespace := true
featureData := []struct {
name string
@ -1287,6 +1295,16 @@ func TestPodSpecFeatureValidation(t *testing.T) {
Paths: []string{"schedulerName"},
},
cfgOpts: []configOption{withPodSpecSchedulerNameEnabled()},
}, {
name: "ShareProcessNamespace",
featureSpec: corev1.PodSpec{
ShareProcessNamespace: &shareProcessNamespace,
},
err: &apis.FieldError{
Message: "must not set the field(s)",
Paths: []string{"shareProcessNamespace"},
},
cfgOpts: []configOption{withPodSpecProcessNamespaceEnabled()},
}}
featureTests := []struct {