Merge 3e93fae514 into bbc3eab7e0
This commit is contained in:
commit
7963b4b671
1
Makefile
1
Makefile
|
|
@ -76,6 +76,7 @@ $(GENERATED_CRDS): $(GENERATED)
|
|||
@$(SED) -i -e 's/listKind: PostgresqlList/listKind: postgresqlList/' manifests/postgresql.crd.yaml
|
||||
@hack/adjust_postgresql_crd.sh
|
||||
@mv manifests/acid.zalan.do_operatorconfigurations.yaml manifests/operatorconfiguration.crd.yaml
|
||||
@hack/adjust_operatorconfiguration_crd.sh
|
||||
@mv manifests/acid.zalan.do_postgresteams.yaml manifests/postgresteam.crd.yaml
|
||||
@cp manifests/postgresql.crd.yaml pkg/apis/acid.zalan.do/v1/postgresql.crd.yaml
|
||||
@cp manifests/postgresql.crd.yaml charts/postgres-operator/crds/postgresqls.yaml
|
||||
|
|
|
|||
|
|
@ -114,9 +114,15 @@ spec:
|
|||
format: int32
|
||||
minimum: 1
|
||||
type: integer
|
||||
connection_pooler_pod_security_context:
|
||||
type: object
|
||||
x-kubernetes-preserve-unknown-fields: true
|
||||
connection_pooler_schema:
|
||||
default: pooler
|
||||
type: string
|
||||
connection_pooler_security_context:
|
||||
type: object
|
||||
x-kubernetes-preserve-unknown-fields: true
|
||||
connection_pooler_user:
|
||||
default: pooler
|
||||
type: string
|
||||
|
|
@ -664,8 +670,7 @@ spec:
|
|||
type: object
|
||||
maintenance_windows:
|
||||
items:
|
||||
pattern: ^\ *((Mon|Tue|Wed|Thu|Fri|Sat|Sun):(2[0-3]|[01]?\d):([0-5]?\d)|(2[0-3]|[01]?\d):([0-5]?\d))-((2[0-3]|[01]?\d):([0-5]?\d)|(2[0-3]|[01]?\d):([0-5]?\d))\
|
||||
*$
|
||||
pattern: '^\ *((Mon|Tue|Wed|Thu|Fri|Sat|Sun):(2[0-3]|[01]?\d):([0-5]?\d)|(2[0-3]|[01]?\d):([0-5]?\d))-((2[0-3]|[01]?\d):([0-5]?\d)|(2[0-3]|[01]?\d):([0-5]?\d))\ *$'
|
||||
type: string
|
||||
type: array
|
||||
major_version_upgrade:
|
||||
|
|
|
|||
|
|
@ -475,6 +475,24 @@ configConnectionPooler:
|
|||
connection_pooler_default_memory_request: 100Mi
|
||||
connection_pooler_default_cpu_limit: "1"
|
||||
connection_pooler_default_memory_limit: 100Mi
|
||||
# Override the pooler pod- and container-level securityContext. Useful for hardened
|
||||
# images whose pgbouncer user/UID differs from the operator default (100/101) and which
|
||||
# own /etc/pgbouncer, or to satisfy restricted Pod Security Standards. When unset the
|
||||
# operator keeps its defaults (pod RunAsUser/RunAsGroup 100/101, container
|
||||
# allowPrivilegeEscalation=false).
|
||||
# connection_pooler_pod_security_context:
|
||||
# runAsUser: 100
|
||||
# runAsGroup: 101
|
||||
# runAsNonRoot: true
|
||||
# fsGroup: 101
|
||||
# seccompProfile:
|
||||
# type: RuntimeDefault
|
||||
# connection_pooler_security_context:
|
||||
# allowPrivilegeEscalation: false
|
||||
# readOnlyRootFilesystem: true
|
||||
# capabilities:
|
||||
# drop:
|
||||
# - ALL
|
||||
|
||||
configPatroni:
|
||||
# enable Patroni DCS failsafe_mode feature
|
||||
|
|
|
|||
|
|
@ -1107,3 +1107,48 @@ operator being able to provide some reasonable defaults.
|
|||
**connection_pooler_default_cpu_limit**
|
||||
**connection_pooler_default_memory_limit**
|
||||
Default resource configuration for connection pooler deployment.
|
||||
|
||||
* **connection_pooler_pod_security_context**
|
||||
Pod-level `securityContext` applied to the connection pooler deployment. The
|
||||
value is a standard Kubernetes
|
||||
[PodSecurityContext](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#podsecuritycontext-v1-core)
|
||||
object. Only fields you set are overridden; unset fields keep the operator
|
||||
defaults (`runAsUser: 100`, `runAsGroup: 101`, and the cluster's `fsGroup`).
|
||||
Settable **only** via the `OperatorConfiguration` CRD (not the ConfigMap).
|
||||
Defaults to not set.
|
||||
|
||||
* **connection_pooler_security_context**
|
||||
Container-level `securityContext` applied to the pgbouncer container. The
|
||||
value is a standard Kubernetes
|
||||
[SecurityContext](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#securitycontext-v1-core)
|
||||
object. Only fields you set are overridden; when `allowPrivilegeEscalation`
|
||||
is unset it defaults to `false`. Settable **only** via the
|
||||
`OperatorConfiguration` CRD (not the ConfigMap). Defaults to not set.
|
||||
|
||||
These two parameters are useful when running a hardened pooler image whose
|
||||
pgbouncer user/UID differs from the operator default (100/101), or to satisfy
|
||||
restricted [Pod Security Standards](https://kubernetes.io/docs/concepts/security/pod-security-standards/).
|
||||
Because they are object-valued they cannot be expressed in the flat operator
|
||||
ConfigMap; configure them through the `OperatorConfiguration` CRD:
|
||||
|
||||
```yaml
|
||||
apiVersion: "acid.zalan.do/v1"
|
||||
kind: OperatorConfiguration
|
||||
metadata:
|
||||
name: postgresql-operator-default-configuration
|
||||
configuration:
|
||||
connection_pooler:
|
||||
connection_pooler_pod_security_context:
|
||||
runAsUser: 100
|
||||
runAsGroup: 101
|
||||
runAsNonRoot: true
|
||||
fsGroup: 101
|
||||
seccompProfile:
|
||||
type: RuntimeDefault
|
||||
connection_pooler_security_context:
|
||||
allowPrivilegeEscalation: false
|
||||
readOnlyRootFilesystem: true
|
||||
capabilities:
|
||||
drop:
|
||||
- ALL
|
||||
```
|
||||
|
|
|
|||
|
|
@ -0,0 +1,22 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
# Hack to adjust the generated operatorconfiguration CRD YAML file and add
|
||||
# missing field settings which can not be expressed via kubebuilder markers.
|
||||
#
|
||||
# Injections:
|
||||
#
|
||||
# * type: string and pattern for the maintenance_windows items.
|
||||
# The MaintenanceWindow type marshals to/from a string (see marshal.go) but
|
||||
# the field is declared `+kubebuilder:validation:Schemaless` /
|
||||
# `+kubebuilder:validation:Type=array`, so controller-gen emits a bare
|
||||
# `type: array` without the required `items` schema. A structural CRD must
|
||||
# specify `items` for every array, so the generated CRD is rejected with:
|
||||
# spec.validation.openAPIV3Schema.properties[configuration]
|
||||
# .properties[maintenance_windows].items: Required value: must be specified
|
||||
|
||||
file="${1:-"manifests/operatorconfiguration.crd.yaml"}"
|
||||
|
||||
sed -i '/^[[:space:]]*maintenance_windows:$/{
|
||||
# Capture the indentation
|
||||
s/^\([[:space:]]*\)maintenance_windows:$/\1maintenance_windows:\n\1 items:\n\1 pattern: '\''^\\ *((Mon|Tue|Wed|Thu|Fri|Sat|Sun):(2[0-3]|[01]?\\d):([0-5]?\\d)|(2[0-3]|[01]?\\d):([0-5]?\\d))-((2[0-3]|[01]?\\d):([0-5]?\\d)|(2[0-3]|[01]?\\d):([0-5]?\\d))\\ *$'\''\n\1 type: string/
|
||||
}' "$file"
|
||||
|
|
@ -664,8 +664,7 @@ spec:
|
|||
type: object
|
||||
maintenance_windows:
|
||||
items:
|
||||
pattern: ^\ *((Mon|Tue|Wed|Thu|Fri|Sat|Sun):(2[0-3]|[01]?\d):([0-5]?\d)|(2[0-3]|[01]?\d):([0-5]?\d))-((2[0-3]|[01]?\d):([0-5]?\d)|(2[0-3]|[01]?\d):([0-5]?\d))\
|
||||
*$
|
||||
pattern: '^\ *((Mon|Tue|Wed|Thu|Fri|Sat|Sun):(2[0-3]|[01]?\d):([0-5]?\d)|(2[0-3]|[01]?\d):([0-5]?\d))-((2[0-3]|[01]?\d):([0-5]?\d)|(2[0-3]|[01]?\d):([0-5]?\d))\ *$'
|
||||
type: string
|
||||
type: array
|
||||
major_version_upgrade:
|
||||
|
|
|
|||
|
|
@ -345,6 +345,14 @@ type ConnectionPoolerConfiguration struct {
|
|||
DefaultCPULimit string `json:"connection_pooler_default_cpu_limit,omitempty"`
|
||||
// +kubebuilder:validation:Pattern=`^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$`
|
||||
DefaultMemoryLimit string `json:"connection_pooler_default_memory_limit,omitempty"`
|
||||
// +kubebuilder:validation:XPreserveUnknownFields
|
||||
// +kubebuilder:validation:Type=object
|
||||
// +kubebuilder:validation:Schemaless
|
||||
PodSecurityContext *v1.PodSecurityContext `json:"connection_pooler_pod_security_context,omitempty"`
|
||||
// +kubebuilder:validation:XPreserveUnknownFields
|
||||
// +kubebuilder:validation:Type=object
|
||||
// +kubebuilder:validation:Schemaless
|
||||
SecurityContext *v1.SecurityContext `json:"connection_pooler_security_context,omitempty"`
|
||||
}
|
||||
|
||||
// OperatorLogicalBackupConfiguration defines configuration for logical backup
|
||||
|
|
|
|||
|
|
@ -664,8 +664,7 @@ spec:
|
|||
type: object
|
||||
maintenance_windows:
|
||||
items:
|
||||
pattern: ^\ *((Mon|Tue|Wed|Thu|Fri|Sat|Sun):(2[0-3]|[01]?\d):([0-5]?\d)|(2[0-3]|[01]?\d):([0-5]?\d))-((2[0-3]|[01]?\d):([0-5]?\d)|(2[0-3]|[01]?\d):([0-5]?\d))\
|
||||
*$
|
||||
pattern: '^\ *((Mon|Tue|Wed|Thu|Fri|Sat|Sun):(2[0-3]|[01]?\d):([0-5]?\d)|(2[0-3]|[01]?\d):([0-5]?\d))-((2[0-3]|[01]?\d):([0-5]?\d)|(2[0-3]|[01]?\d):([0-5]?\d))\ *$'
|
||||
type: string
|
||||
type: array
|
||||
major_version_upgrade:
|
||||
|
|
|
|||
|
|
@ -142,6 +142,16 @@ func (in *ConnectionPoolerConfiguration) DeepCopyInto(out *ConnectionPoolerConfi
|
|||
*out = new(int32)
|
||||
**out = **in
|
||||
}
|
||||
if in.PodSecurityContext != nil {
|
||||
in, out := &in.PodSecurityContext, &out.PodSecurityContext
|
||||
*out = new(corev1.PodSecurityContext)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.SecurityContext != nil {
|
||||
in, out := &in.SecurityContext, &out.SecurityContext
|
||||
*out = new(corev1.SecurityContext)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -29,6 +29,51 @@ import (
|
|||
var poolerRunAsUser = int64(100)
|
||||
var poolerRunAsGroup = int64(101)
|
||||
|
||||
// generateConnectionPoolerPodSecurityContext returns the pod-level securityContext for the
|
||||
// connection pooler. When the operator configuration provides one it is used as the base
|
||||
// (deep-copied); otherwise an empty context is used. The historical defaults (RunAsUser/RunAsGroup
|
||||
// 100/101) and the spilo FSGroup are applied only for fields the configuration leaves unset, so
|
||||
// behavior is unchanged when no override is configured.
|
||||
func (c *Cluster) generateConnectionPoolerPodSecurityContext(spec *acidv1.PostgresSpec) *v1.PodSecurityContext {
|
||||
var securityContext v1.PodSecurityContext
|
||||
if c.OpConfig.ConnectionPooler.PodSecurityContext != nil {
|
||||
securityContext = *c.OpConfig.ConnectionPooler.PodSecurityContext.DeepCopy()
|
||||
}
|
||||
|
||||
if securityContext.RunAsUser == nil {
|
||||
securityContext.RunAsUser = &poolerRunAsUser
|
||||
}
|
||||
if securityContext.RunAsGroup == nil {
|
||||
securityContext.RunAsGroup = &poolerRunAsGroup
|
||||
}
|
||||
|
||||
if securityContext.FSGroup == nil {
|
||||
effectiveFSGroup := c.OpConfig.Resources.SpiloFSGroup
|
||||
if spec.SpiloFSGroup != nil {
|
||||
effectiveFSGroup = spec.SpiloFSGroup
|
||||
}
|
||||
if effectiveFSGroup != nil {
|
||||
securityContext.FSGroup = effectiveFSGroup
|
||||
}
|
||||
}
|
||||
|
||||
return &securityContext
|
||||
}
|
||||
|
||||
// generateConnectionPoolerContainerSecurityContext returns the container-level securityContext for
|
||||
// the pooler. A configured context is used as the base (deep-copied); AllowPrivilegeEscalation
|
||||
// defaults to false when left unset, preserving prior behavior.
|
||||
func (c *Cluster) generateConnectionPoolerContainerSecurityContext() *v1.SecurityContext {
|
||||
var securityContext v1.SecurityContext
|
||||
if c.OpConfig.ConnectionPooler.SecurityContext != nil {
|
||||
securityContext = *c.OpConfig.ConnectionPooler.SecurityContext.DeepCopy()
|
||||
}
|
||||
if securityContext.AllowPrivilegeEscalation == nil {
|
||||
securityContext.AllowPrivilegeEscalation = util.False()
|
||||
}
|
||||
return &securityContext
|
||||
}
|
||||
|
||||
// ConnectionPoolerObjects K8s objects that are belong to connection pooler
|
||||
type ConnectionPoolerObjects struct {
|
||||
AuthSecret *v1.Secret
|
||||
|
|
@ -383,9 +428,7 @@ func (c *Cluster) generateConnectionPoolerPodTemplate(role PostgresRole) (
|
|||
},
|
||||
},
|
||||
},
|
||||
SecurityContext: &v1.SecurityContext{
|
||||
AllowPrivilegeEscalation: util.False(),
|
||||
},
|
||||
SecurityContext: c.generateConnectionPoolerContainerSecurityContext(),
|
||||
}
|
||||
|
||||
var poolerVolumes []v1.Volume
|
||||
|
|
@ -444,19 +487,7 @@ func (c *Cluster) generateConnectionPoolerPodTemplate(role PostgresRole) (
|
|||
poolerContainer.Env = envVars
|
||||
poolerContainer.VolumeMounts = volumeMounts
|
||||
tolerationsSpec := tolerations(&spec.Tolerations, c.OpConfig.PodToleration)
|
||||
securityContext := v1.PodSecurityContext{}
|
||||
|
||||
// determine the User, Group and FSGroup for the pooler pod
|
||||
securityContext.RunAsUser = &poolerRunAsUser
|
||||
securityContext.RunAsGroup = &poolerRunAsGroup
|
||||
|
||||
effectiveFSGroup := c.OpConfig.Resources.SpiloFSGroup
|
||||
if spec.SpiloFSGroup != nil {
|
||||
effectiveFSGroup = spec.SpiloFSGroup
|
||||
}
|
||||
if effectiveFSGroup != nil {
|
||||
securityContext.FSGroup = effectiveFSGroup
|
||||
}
|
||||
securityContext := c.generateConnectionPoolerPodSecurityContext(spec)
|
||||
|
||||
podTemplate := &v1.PodTemplateSpec{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
|
|
@ -469,7 +500,7 @@ func (c *Cluster) generateConnectionPoolerPodTemplate(role PostgresRole) (
|
|||
Containers: []v1.Container{poolerContainer},
|
||||
Tolerations: tolerationsSpec,
|
||||
Volumes: poolerVolumes,
|
||||
SecurityContext: &securityContext,
|
||||
SecurityContext: securityContext,
|
||||
ServiceAccountName: c.OpConfig.PodServiceAccountName,
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1064,6 +1064,137 @@ func TestPoolerTLS(t *testing.T) {
|
|||
assert.Contains(t, poolerContainer.Env, v1.EnvVar{Name: "CONNECTION_POOLER_CLIENT_CA_FILE", Value: "/tls/ca.crt"})
|
||||
}
|
||||
|
||||
// poolerSecurityContextOpConfig builds a minimal OpConfig usable for exercising the
|
||||
// connection pooler securityContext behavior. Callers tweak the returned config's
|
||||
// ConnectionPooler / Resources before passing it to syncedPoolerDeployment.
|
||||
func poolerSecurityContextOpConfig() config.Config {
|
||||
return config.Config{
|
||||
PodManagementPolicy: "ordered_ready",
|
||||
ProtectedRoles: []string{"admin"},
|
||||
Auth: config.Auth{
|
||||
SuperUsername: superUserName,
|
||||
ReplicationUsername: replicationUserName,
|
||||
},
|
||||
Resources: config.Resources{
|
||||
ClusterLabels: map[string]string{"application": "spilo"},
|
||||
ClusterNameLabel: "cluster-name",
|
||||
DefaultCPURequest: "300m",
|
||||
DefaultCPULimit: "300m",
|
||||
DefaultMemoryRequest: "300Mi",
|
||||
DefaultMemoryLimit: "300Mi",
|
||||
PodRoleLabel: "spilo-role",
|
||||
},
|
||||
ConnectionPooler: config.ConnectionPooler{
|
||||
ConnectionPoolerDefaultCPURequest: "100m",
|
||||
ConnectionPoolerDefaultCPULimit: "100m",
|
||||
ConnectionPoolerDefaultMemoryRequest: "100Mi",
|
||||
ConnectionPoolerDefaultMemoryLimit: "100Mi",
|
||||
},
|
||||
PodServiceAccountName: "postgres-pod",
|
||||
}
|
||||
}
|
||||
|
||||
// syncedPoolerDeployment provisions a pooler with the given OpConfig and returns the
|
||||
// resulting master pooler Deployment.
|
||||
func syncedPoolerDeployment(t *testing.T, opConfig config.Config) *appsv1.Deployment {
|
||||
t.Helper()
|
||||
client, _ := newFakeK8sPoolerTestClient()
|
||||
clusterName := "acid-test-cluster"
|
||||
namespace := "default"
|
||||
|
||||
pg := acidv1.Postgresql{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: clusterName, Namespace: namespace},
|
||||
Spec: acidv1.PostgresSpec{
|
||||
TeamID: "myapp", NumberOfInstances: 1,
|
||||
EnableConnectionPooler: util.True(),
|
||||
Resources: &acidv1.Resources{
|
||||
ResourceRequests: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("1"), Memory: k8sutil.StringToPointer("10")},
|
||||
ResourceLimits: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("1"), Memory: k8sutil.StringToPointer("10")},
|
||||
},
|
||||
Volume: acidv1.Volume{Size: "1G"},
|
||||
},
|
||||
}
|
||||
|
||||
cluster := New(Config{OpConfig: opConfig}, client, pg, logger, eventRecorder)
|
||||
|
||||
_, err := cluster.createStatefulSet()
|
||||
assert.NoError(t, err)
|
||||
|
||||
cluster.ConnectionPooler = map[PostgresRole]*ConnectionPoolerObjects{
|
||||
Master: {
|
||||
Name: cluster.connectionPoolerName(Master),
|
||||
ClusterName: clusterName,
|
||||
Namespace: namespace,
|
||||
Role: Master,
|
||||
},
|
||||
}
|
||||
|
||||
_, err = cluster.syncConnectionPoolerWorker(nil, &pg, Master)
|
||||
assert.NoError(t, err)
|
||||
|
||||
deploy, err := client.Deployments(namespace).Get(context.TODO(), cluster.connectionPoolerName(Master), metav1.GetOptions{})
|
||||
assert.NoError(t, err)
|
||||
return deploy
|
||||
}
|
||||
|
||||
func TestConnectionPoolerDefaultSecurityContext(t *testing.T) {
|
||||
deploy := syncedPoolerDeployment(t, poolerSecurityContextOpConfig())
|
||||
|
||||
podSc := deploy.Spec.Template.Spec.SecurityContext
|
||||
assert.NotNil(t, podSc, "pod securityContext should be set")
|
||||
assert.Equal(t, int64(100), *podSc.RunAsUser, "defaults to RunAsUser 100 for backward compatibility")
|
||||
assert.Equal(t, int64(101), *podSc.RunAsGroup, "defaults to RunAsGroup 101 for backward compatibility")
|
||||
|
||||
containerSc := deploy.Spec.Template.Spec.Containers[constants.ConnectionPoolerContainer].SecurityContext
|
||||
assert.NotNil(t, containerSc, "container securityContext should be set")
|
||||
assert.Equal(t, false, *containerSc.AllowPrivilegeEscalation, "defaults AllowPrivilegeEscalation to false")
|
||||
}
|
||||
|
||||
func TestConnectionPoolerPodSecurityContextOverride(t *testing.T) {
|
||||
runAsUser := int64(1000001)
|
||||
runAsGroup := int64(1000001)
|
||||
spiloFSGroup := int64(103)
|
||||
|
||||
opConfig := poolerSecurityContextOpConfig()
|
||||
// FSGroup comes from the spilo config and must still apply when the override leaves it nil.
|
||||
opConfig.Resources.SpiloFSGroup = &spiloFSGroup
|
||||
opConfig.ConnectionPooler.PodSecurityContext = &v1.PodSecurityContext{
|
||||
RunAsUser: &runAsUser,
|
||||
RunAsGroup: &runAsGroup,
|
||||
RunAsNonRoot: util.True(),
|
||||
SeccompProfile: &v1.SeccompProfile{
|
||||
Type: v1.SeccompProfileTypeRuntimeDefault,
|
||||
},
|
||||
}
|
||||
|
||||
deploy := syncedPoolerDeployment(t, opConfig)
|
||||
podSc := deploy.Spec.Template.Spec.SecurityContext
|
||||
|
||||
assert.Equal(t, runAsUser, *podSc.RunAsUser, "honors configured RunAsUser")
|
||||
assert.Equal(t, runAsGroup, *podSc.RunAsGroup, "honors configured RunAsGroup")
|
||||
assert.Equal(t, true, *podSc.RunAsNonRoot, "honors configured RunAsNonRoot")
|
||||
assert.Equal(t, v1.SeccompProfileTypeRuntimeDefault, podSc.SeccompProfile.Type, "honors configured SeccompProfile")
|
||||
assert.Equal(t, spiloFSGroup, *podSc.FSGroup, "still applies spilo FSGroup when override leaves it nil")
|
||||
}
|
||||
|
||||
func TestConnectionPoolerContainerSecurityContextOverride(t *testing.T) {
|
||||
opConfig := poolerSecurityContextOpConfig()
|
||||
opConfig.ConnectionPooler.SecurityContext = &v1.SecurityContext{
|
||||
AllowPrivilegeEscalation: util.False(),
|
||||
ReadOnlyRootFilesystem: util.True(),
|
||||
Capabilities: &v1.Capabilities{
|
||||
Drop: []v1.Capability{"ALL"},
|
||||
},
|
||||
}
|
||||
|
||||
deploy := syncedPoolerDeployment(t, opConfig)
|
||||
containerSc := deploy.Spec.Template.Spec.Containers[constants.ConnectionPoolerContainer].SecurityContext
|
||||
|
||||
assert.Equal(t, true, *containerSc.ReadOnlyRootFilesystem, "honors configured ReadOnlyRootFilesystem")
|
||||
assert.Equal(t, false, *containerSc.AllowPrivilegeEscalation, "keeps AllowPrivilegeEscalation false")
|
||||
assert.Equal(t, []v1.Capability{"ALL"}, containerSc.Capabilities.Drop, "honors dropped capabilities")
|
||||
}
|
||||
|
||||
func TestConnectionPoolerServiceSpec(t *testing.T) {
|
||||
testName := "Test connection pooler service spec generation"
|
||||
var cluster = New(
|
||||
|
|
|
|||
|
|
@ -285,5 +285,10 @@ func (c *Controller) importConfigurationFromCRD(fromCRD *acidv1.OperatorConfigur
|
|||
fromCRD.ConnectionPooler.MaxDBConnections,
|
||||
k8sutil.Int32ToPointer(constants.ConnectionPoolerMaxDBConnections))
|
||||
|
||||
// Security contexts are nil unless explicitly configured; the cluster package falls back
|
||||
// to the historical pooler defaults when they are not set.
|
||||
result.ConnectionPooler.PodSecurityContext = fromCRD.ConnectionPooler.PodSecurityContext
|
||||
result.ConnectionPooler.SecurityContext = fromCRD.ConnectionPooler.SecurityContext
|
||||
|
||||
return result
|
||||
}
|
||||
|
|
|
|||
|
|
@ -165,6 +165,13 @@ type ConnectionPooler struct {
|
|||
ConnectionPoolerDefaultMemoryRequest string `name:"connection_pooler_default_memory_request"`
|
||||
ConnectionPoolerDefaultCPULimit string `name:"connection_pooler_default_cpu_limit"`
|
||||
ConnectionPoolerDefaultMemoryLimit string `name:"connection_pooler_default_memory_limit"`
|
||||
// PodSecurityContext and SecurityContext let operators override the pooler
|
||||
// pod- and container-level security contexts. They are populated only from the
|
||||
// OperatorConfiguration CRD (name:"-" excludes them from the ConfigMap decoder,
|
||||
// mirroring LivenessProbe). When nil, the operator falls back to the historical
|
||||
// defaults (pod RunAsUser/RunAsGroup 100/101, container AllowPrivilegeEscalation=false).
|
||||
PodSecurityContext *v1.PodSecurityContext `name:"-"`
|
||||
SecurityContext *v1.SecurityContext `name:"-"`
|
||||
}
|
||||
|
||||
// Config describes operator config
|
||||
|
|
|
|||
Loading…
Reference in New Issue