This commit is contained in:
murtll 2025-10-21 15:02:43 +02:00 committed by GitHub
commit ed18d8258b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 113 additions and 8 deletions

View File

@ -173,6 +173,12 @@ spec:
pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$' pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$'
schema: schema:
type: string type: string
sidecars:
type: array
nullable: true
items:
type: object
x-kubernetes-preserve-unknown-fields: true
user: user:
type: string type: string
databases: databases:

View File

@ -511,7 +511,8 @@ properties of the persistent storage that stores Postgres data.
Those parameters are defined under the `sidecars` key. They consist of a list Those parameters are defined under the `sidecars` key. They consist of a list
of dictionaries, each defining one sidecar (an extra container running of dictionaries, each defining one sidecar (an extra container running
along the main Postgres container on the same pod). The following keys can be along the main Postgres container on the same pod). Same way sidecars can be
defined for the pooler, using `connectionPooler.sidecars` key. The following keys can be
defined in the sidecar dictionary: defined in the sidecar dictionary:
* **name** * **name**
@ -601,6 +602,9 @@ for both master and replica pooler services (if `enableReplicaConnectionPooler`
* **resources** * **resources**
Resource configuration for connection pooler deployment. Resource configuration for connection pooler deployment.
* **sidecars**
Extra containers to run alongside with PGBouncer container in the same pod.
## Custom TLS certificates ## Custom TLS certificates
Those parameters are grouped under the `tls` top-level key. Note, you have to Those parameters are grouped under the `tls` top-level key. Note, you have to

View File

@ -171,6 +171,12 @@ spec:
pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$' pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$'
schema: schema:
type: string type: string
sidecars:
type: array
nullable: true
items:
type: object
x-kubernetes-preserve-unknown-fields: true
user: user:
type: string type: string
databases: databases:

View File

@ -275,6 +275,16 @@ var PostgresCRDResourceValidation = apiextv1.CustomResourceValidation{
"schema": { "schema": {
Type: "string", Type: "string",
}, },
"sidecars": {
Type: "array",
Nullable: true,
Items: &apiextv1.JSONSchemaPropsOrArray{
Schema: &apiextv1.JSONSchemaProps{
Type: "object",
XPreserveUnknownFields: util.True(),
},
},
},
"user": { "user": {
Type: "string", Type: "string",
}, },

View File

@ -248,6 +248,7 @@ type ConnectionPooler struct {
Mode string `json:"mode,omitempty"` Mode string `json:"mode,omitempty"`
DockerImage string `json:"dockerImage,omitempty"` DockerImage string `json:"dockerImage,omitempty"`
MaxDBConnections *int32 `json:"maxDBConnections,omitempty"` MaxDBConnections *int32 `json:"maxDBConnections,omitempty"`
Sidecars []Sidecar `json:"sidecars,omitempty"`
*Resources `json:"resources,omitempty"` *Resources `json:"resources,omitempty"`
} }

View File

@ -111,6 +111,13 @@ func (in *ConnectionPooler) DeepCopyInto(out *ConnectionPooler) {
*out = new(int32) *out = new(int32)
**out = **in **out = **in
} }
if in.Sidecars != nil {
in, out := &in.Sidecars, &out.Sidecars
*out = make([]Sidecar, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
if in.Resources != nil { if in.Resources != nil {
in, out := &in.Resources, &out.Resources in, out := &in.Resources, &out.Resources
*out = new(Resources) *out = new(Resources)

View File

@ -343,6 +343,18 @@ func (c *Cluster) generateConnectionPoolerPodTemplate(role PostgresRole) (
}, },
} }
sidecars := []v1.Container{}
if connectionPoolerSpec.Sidecars != nil && len(connectionPoolerSpec.Sidecars) > 0 {
sidecars, err = c.generateSidecarContainers(
connectionPoolerSpec.Sidecars,
makeDefaultConnectionPoolerResources(&c.OpConfig),
0,
)
if err != nil {
return nil, fmt.Errorf("could not generate pooler sidecar containers: %v", err)
}
}
// If the cluster has custom TLS certificates configured, we do the following: // If the cluster has custom TLS certificates configured, we do the following:
// 1. Add environment variables to tell pgBouncer where to find the TLS certificates // 1. Add environment variables to tell pgBouncer where to find the TLS certificates
// 2. Reference the secret in a volume // 2. Reference the secret in a volume
@ -404,7 +416,7 @@ func (c *Cluster) generateConnectionPoolerPodTemplate(role PostgresRole) (
}, },
Spec: v1.PodSpec{ Spec: v1.PodSpec{
TerminationGracePeriodSeconds: &gracePeriod, TerminationGracePeriodSeconds: &gracePeriod,
Containers: []v1.Container{poolerContainer}, Containers: append([]v1.Container{poolerContainer}, sidecars...),
Tolerations: tolerationsSpec, Tolerations: tolerationsSpec,
Volumes: poolerVolumes, Volumes: poolerVolumes,
SecurityContext: &securityContext, SecurityContext: &securityContext,

View File

@ -869,6 +869,65 @@ func TestConnectionPoolerDeploymentSpec(t *testing.T) {
} }
} }
func TestConnectionPoolerSidecars (t *testing.T) {
var cluster = New(
Config{
OpConfig: config.Config{
ProtectedRoles: []string{"admin"},
Auth: config.Auth{
SuperUsername: superUserName,
ReplicationUsername: replicationUserName,
},
ConnectionPooler: config.ConnectionPooler{
ConnectionPoolerDefaultCPURequest: "100m",
ConnectionPoolerDefaultCPULimit: "100m",
ConnectionPoolerDefaultMemoryRequest: "100Mi",
ConnectionPoolerDefaultMemoryLimit: "100Mi",
},
},
}, k8sutil.KubernetesClient{}, acidv1.Postgresql{}, logger, eventRecorder)
cluster.Statefulset = &appsv1.StatefulSet{
ObjectMeta: metav1.ObjectMeta{
Name: "test-sts",
},
}
cluster.ConnectionPooler = map[PostgresRole]*ConnectionPoolerObjects{
Master: {
Deployment: nil,
Service: nil,
LookupFunction: true,
Name: "",
Role: Master,
},
}
cluster.Spec = acidv1.PostgresSpec{
ConnectionPooler: &acidv1.ConnectionPooler{
Sidecars: []acidv1.Sidecar{
acidv1.Sidecar{
Name: "sidecar",
DockerImage: "image",
Env: []v1.EnvVar{
{
Name: "SOME_VAR",
Value: "some-value",
},
},
},
},
},
}
deployment, err := cluster.generateConnectionPoolerDeployment(cluster.ConnectionPooler[Master])
assert.NoError(t, err)
containers := deployment.Spec.Template.Spec.Containers
assert.Equal(t, 2, len(containers), "wrong number of containers")
assert.Equal(t, "sidecar", containers[1].Name, "wrong name of sidecar")
assert.Equal(t, "image", containers[1].Image, "wrong image of sidecar")
assert.Equal(t, "SOME_VAR", containers[1].Env[0].Name, "wrong name of env var in sidecar")
assert.Equal(t, "some-value", containers[1].Env[0].Value, "wrong value of env var in sidecar")
}
func testServiceAccount(cluster *Cluster, podSpec *v1.PodTemplateSpec, role PostgresRole) error { func testServiceAccount(cluster *Cluster, podSpec *v1.PodTemplateSpec, role PostgresRole) error {
poolerServiceAccount := podSpec.Spec.ServiceAccountName poolerServiceAccount := podSpec.Spec.ServiceAccountName