feat(pooler): mount generated config and override command/args
This commit is contained in:
parent
be3fbdaec8
commit
8641fbd9de
|
|
@ -34,6 +34,7 @@ type ConnectionPoolerObjects struct {
|
||||||
AuthSecret *v1.Secret
|
AuthSecret *v1.Secret
|
||||||
Deployment *appsv1.Deployment
|
Deployment *appsv1.Deployment
|
||||||
Service *v1.Service
|
Service *v1.Service
|
||||||
|
ConfigMap *v1.ConfigMap
|
||||||
Name string
|
Name string
|
||||||
ClusterName string
|
ClusterName string
|
||||||
Namespace string
|
Namespace string
|
||||||
|
|
@ -466,6 +467,33 @@ func (c *Cluster) generateConnectionPoolerPodTemplate(role PostgresRole) (
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// When enabled, mount an operator-generated pgbouncer.ini and override the
|
||||||
|
// container command/args (e.g. for images like the Chainguard FIPS pgbouncer
|
||||||
|
// whose entrypoint is the bare binary with no config-rendering wrapper).
|
||||||
|
if c.OpConfig.ConnectionPooler.GenerateConfig {
|
||||||
|
configVolumeName := fmt.Sprintf("%s-config", c.connectionPoolerName(role))
|
||||||
|
poolerVolumes = append(poolerVolumes, v1.Volume{
|
||||||
|
Name: configVolumeName,
|
||||||
|
VolumeSource: v1.VolumeSource{
|
||||||
|
ConfigMap: &v1.ConfigMapVolumeSource{
|
||||||
|
LocalObjectReference: v1.LocalObjectReference{
|
||||||
|
Name: configVolumeName,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
volumeMounts = append(volumeMounts, v1.VolumeMount{
|
||||||
|
Name: configVolumeName,
|
||||||
|
MountPath: c.OpConfig.ConnectionPooler.ConfigPath,
|
||||||
|
SubPath: pgBouncerConfigFileName,
|
||||||
|
ReadOnly: true,
|
||||||
|
})
|
||||||
|
if len(c.OpConfig.ConnectionPooler.Command) > 0 {
|
||||||
|
poolerContainer.Command = c.OpConfig.ConnectionPooler.Command
|
||||||
|
}
|
||||||
|
poolerContainer.Args = c.OpConfig.ConnectionPooler.Args
|
||||||
|
}
|
||||||
|
|
||||||
poolerContainer.Env = envVars
|
poolerContainer.Env = envVars
|
||||||
poolerContainer.VolumeMounts = volumeMounts
|
poolerContainer.VolumeMounts = volumeMounts
|
||||||
tolerationsSpec := tolerations(&spec.Tolerations, c.OpConfig.PodToleration)
|
tolerationsSpec := tolerations(&spec.Tolerations, c.OpConfig.PodToleration)
|
||||||
|
|
@ -483,11 +511,16 @@ func (c *Cluster) generateConnectionPoolerPodTemplate(role PostgresRole) (
|
||||||
securityContext.FSGroup = effectiveFSGroup
|
securityContext.FSGroup = effectiveFSGroup
|
||||||
}
|
}
|
||||||
|
|
||||||
|
podAnnotations, annErr := c.connectionPoolerPodAnnotations(role)
|
||||||
|
if annErr != nil {
|
||||||
|
return nil, annErr
|
||||||
|
}
|
||||||
|
|
||||||
podTemplate := &v1.PodTemplateSpec{
|
podTemplate := &v1.PodTemplateSpec{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Labels: c.connectionPoolerLabels(role, true).MatchLabels,
|
Labels: c.connectionPoolerLabels(role, true).MatchLabels,
|
||||||
Namespace: c.Namespace,
|
Namespace: c.Namespace,
|
||||||
Annotations: c.annotationsSet(c.generatePodAnnotations(spec)),
|
Annotations: podAnnotations,
|
||||||
},
|
},
|
||||||
Spec: v1.PodSpec{
|
Spec: v1.PodSpec{
|
||||||
TerminationGracePeriodSeconds: &gracePeriod,
|
TerminationGracePeriodSeconds: &gracePeriod,
|
||||||
|
|
|
||||||
|
|
@ -167,3 +167,20 @@ func (c *Cluster) generateConnectionPoolerConfigMap(role PostgresRole) (*v1.Conf
|
||||||
},
|
},
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// connectionPoolerPodAnnotations returns the pooler pod annotations, adding the
|
||||||
|
// config checksum when generated config is enabled so config changes roll pods.
|
||||||
|
func (c *Cluster) connectionPoolerPodAnnotations(role PostgresRole) (map[string]string, error) {
|
||||||
|
annotations := c.annotationsSet(c.generatePodAnnotations(&c.Spec))
|
||||||
|
if c.OpConfig.ConnectionPooler.GenerateConfig {
|
||||||
|
checksum, err := c.connectionPoolerConfigChecksum(role)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if annotations == nil {
|
||||||
|
annotations = map[string]string{}
|
||||||
|
}
|
||||||
|
annotations[poolerConfigChecksumAnnotation] = checksum
|
||||||
|
}
|
||||||
|
return annotations, nil
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"github.com/zalando/postgres-operator/pkg/util"
|
"github.com/zalando/postgres-operator/pkg/util"
|
||||||
"github.com/zalando/postgres-operator/pkg/util/config"
|
"github.com/zalando/postgres-operator/pkg/util/config"
|
||||||
"github.com/zalando/postgres-operator/pkg/util/k8sutil"
|
"github.com/zalando/postgres-operator/pkg/util/k8sutil"
|
||||||
|
v1 "k8s.io/api/core/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newGenerateConfigCluster() *Cluster {
|
func newGenerateConfigCluster() *Cluster {
|
||||||
|
|
@ -127,3 +128,57 @@ func TestGenerateConnectionPoolerConfigMap(t *testing.T) {
|
||||||
t.Errorf("config map should have owner references")
|
t.Errorf("config map should have owner references")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func findVolumeMount(mounts []v1.VolumeMount, path string) *v1.VolumeMount {
|
||||||
|
for i := range mounts {
|
||||||
|
if mounts[i].MountPath == path {
|
||||||
|
return &mounts[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPoolerPodTemplateGeneratedConfigOn(t *testing.T) {
|
||||||
|
cluster := newGenerateConfigCluster()
|
||||||
|
|
||||||
|
tmpl, err := cluster.generateConnectionPoolerPodTemplate(Master)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
container := tmpl.Spec.Containers[0]
|
||||||
|
|
||||||
|
mount := findVolumeMount(container.VolumeMounts, "/etc/pgbouncer/pgbouncer.ini")
|
||||||
|
if mount == nil {
|
||||||
|
t.Fatalf("expected a volume mount at /etc/pgbouncer/pgbouncer.ini")
|
||||||
|
}
|
||||||
|
if mount.SubPath != "pgbouncer.ini" {
|
||||||
|
t.Errorf("expected subPath pgbouncer.ini, got %q", mount.SubPath)
|
||||||
|
}
|
||||||
|
if len(container.Args) != 1 || container.Args[0] != "/etc/pgbouncer/pgbouncer.ini" {
|
||||||
|
t.Errorf("expected args [/etc/pgbouncer/pgbouncer.ini], got %#v", container.Args)
|
||||||
|
}
|
||||||
|
if _, ok := tmpl.Annotations[poolerConfigChecksumAnnotation]; !ok {
|
||||||
|
t.Errorf("expected checksum annotation on pod template")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPoolerPodTemplateGeneratedConfigOff(t *testing.T) {
|
||||||
|
cluster := newGenerateConfigCluster()
|
||||||
|
cluster.OpConfig.ConnectionPooler.GenerateConfig = false
|
||||||
|
|
||||||
|
tmpl, err := cluster.generateConnectionPoolerPodTemplate(Master)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
container := tmpl.Spec.Containers[0]
|
||||||
|
|
||||||
|
if findVolumeMount(container.VolumeMounts, "/etc/pgbouncer/pgbouncer.ini") != nil {
|
||||||
|
t.Errorf("did not expect config mount when GenerateConfig is off")
|
||||||
|
}
|
||||||
|
if len(container.Args) != 0 {
|
||||||
|
t.Errorf("did not expect args when GenerateConfig is off, got %#v", container.Args)
|
||||||
|
}
|
||||||
|
if _, ok := tmpl.Annotations[poolerConfigChecksumAnnotation]; ok {
|
||||||
|
t.Errorf("did not expect checksum annotation when GenerateConfig is off")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue