246 lines
7.6 KiB
Go
246 lines
7.6 KiB
Go
package cluster
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
"testing"
|
|
|
|
acidv1 "github.com/zalando/postgres-operator/pkg/apis/acid.zalan.do/v1"
|
|
"github.com/zalando/postgres-operator/pkg/util"
|
|
"github.com/zalando/postgres-operator/pkg/util/config"
|
|
"github.com/zalando/postgres-operator/pkg/util/k8sutil"
|
|
v1 "k8s.io/api/core/v1"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
)
|
|
|
|
func newGenerateConfigCluster() *Cluster {
|
|
maxDB := int32(60)
|
|
instances := int32(2)
|
|
cluster := New(
|
|
Config{OpConfig: config.Config{
|
|
ConnectionPooler: config.ConnectionPooler{
|
|
User: "pooler",
|
|
Schema: "pooler",
|
|
Mode: "transaction",
|
|
MaxDBConnections: &maxDB,
|
|
NumberOfInstances: &instances,
|
|
GenerateConfig: true,
|
|
AuthType: "scram-sha-256",
|
|
ConfigPath: "/etc/pgbouncer/pgbouncer.ini",
|
|
Args: []string{"/etc/pgbouncer/pgbouncer.ini"},
|
|
},
|
|
Resources: config.Resources{
|
|
EnableOwnerReferences: util.True(),
|
|
},
|
|
}},
|
|
k8sutil.NewMockKubernetesClient(), acidv1.Postgresql{}, logger, eventRecorder)
|
|
cluster.Name = "acid-test"
|
|
cluster.Namespace = "default"
|
|
cluster.Spec = acidv1.PostgresSpec{ConnectionPooler: &acidv1.ConnectionPooler{}}
|
|
return cluster
|
|
}
|
|
|
|
func TestGeneratePgBouncerIni(t *testing.T) {
|
|
cluster := newGenerateConfigCluster()
|
|
|
|
ini, err := cluster.generatePgBouncerIni(Master)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
|
|
for _, want := range []string{
|
|
"[databases]",
|
|
"[pgbouncer]",
|
|
"pool_mode = transaction",
|
|
"auth_type = scram-sha-256",
|
|
"auth_file = /etc/pgbouncer/userlist.txt",
|
|
"auth_query = SELECT * FROM pooler.user_lookup($1)",
|
|
"server_tls_sslmode = require",
|
|
"default_pool_size = 15",
|
|
"max_db_connections = 30",
|
|
} {
|
|
if !strings.Contains(ini, want) {
|
|
t.Errorf("rendered ini missing %q\n---\n%s", want, ini)
|
|
}
|
|
}
|
|
|
|
if strings.Contains(ini, "client_tls_cert_file") {
|
|
t.Errorf("did not expect client_tls_cert_file without spec.TLS\n%s", ini)
|
|
}
|
|
}
|
|
|
|
func TestGeneratePgBouncerIniWithTLS(t *testing.T) {
|
|
cluster := newGenerateConfigCluster()
|
|
cluster.Spec.TLS = &acidv1.TLSDescription{SecretName: "pg-tls"}
|
|
|
|
ini, err := cluster.generatePgBouncerIni(Master)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
for _, want := range []string{
|
|
"client_tls_sslmode = require",
|
|
"client_tls_key_file = /tls/tls.key",
|
|
"client_tls_cert_file = /tls/tls.crt",
|
|
} {
|
|
if !strings.Contains(ini, want) {
|
|
t.Errorf("rendered ini missing %q\n---\n%s", want, ini)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestConnectionPoolerConfigChecksumStability(t *testing.T) {
|
|
cluster := newGenerateConfigCluster()
|
|
|
|
sum1, err := cluster.connectionPoolerConfigChecksum(Master)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
sum2, err := cluster.connectionPoolerConfigChecksum(Master)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if sum1 != sum2 {
|
|
t.Errorf("checksum not stable: %q != %q", sum1, sum2)
|
|
}
|
|
|
|
cluster.OpConfig.ConnectionPooler.AuthType = "md5"
|
|
sum3, err := cluster.connectionPoolerConfigChecksum(Master)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if sum1 == sum3 {
|
|
t.Errorf("checksum should change when config changes")
|
|
}
|
|
}
|
|
|
|
func TestGenerateConnectionPoolerConfigMap(t *testing.T) {
|
|
cluster := newGenerateConfigCluster()
|
|
|
|
cm, err := cluster.generateConnectionPoolerConfigMap(Master)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if cm.Name != cluster.connectionPoolerName(Master)+"-config" {
|
|
t.Errorf("unexpected config map name %q", cm.Name)
|
|
}
|
|
if _, ok := cm.Data["pgbouncer.ini"]; !ok {
|
|
t.Errorf("config map missing pgbouncer.ini key, got %#v", cm.Data)
|
|
}
|
|
if len(cm.OwnerReferences) == 0 {
|
|
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")
|
|
}
|
|
}
|
|
|
|
func TestSyncConnectionPoolerConfigMap(t *testing.T) {
|
|
client, _ := newFakeK8sPoolerTestClient()
|
|
maxDB := int32(60)
|
|
instances := int32(2)
|
|
pg := acidv1.Postgresql{
|
|
ObjectMeta: metav1.ObjectMeta{Name: "acid-test", Namespace: "default"},
|
|
Spec: acidv1.PostgresSpec{
|
|
EnableConnectionPooler: boolToPointer(true),
|
|
ConnectionPooler: &acidv1.ConnectionPooler{},
|
|
},
|
|
}
|
|
cluster := New(
|
|
Config{OpConfig: config.Config{
|
|
ConnectionPooler: config.ConnectionPooler{
|
|
User: "pooler", Schema: "pooler", Mode: "transaction",
|
|
MaxDBConnections: &maxDB, NumberOfInstances: &instances,
|
|
GenerateConfig: true, AuthType: "scram-sha-256",
|
|
ConfigPath: "/etc/pgbouncer/pgbouncer.ini",
|
|
Args: []string{"/etc/pgbouncer/pgbouncer.ini"},
|
|
},
|
|
}},
|
|
client, pg, logger, eventRecorder)
|
|
cluster.Name = "acid-test"
|
|
cluster.Namespace = "default"
|
|
cluster.Spec = pg.Spec
|
|
cluster.ConnectionPooler = map[PostgresRole]*ConnectionPoolerObjects{
|
|
Master: {Name: cluster.connectionPoolerName(Master), Namespace: "default", Role: Master},
|
|
}
|
|
|
|
if err := cluster.syncConnectionPoolerConfigMap(Master); err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
name := cluster.connectionPoolerName(Master) + "-config"
|
|
cm, err := client.ConfigMaps("default").Get(context.TODO(), name, metav1.GetOptions{})
|
|
if err != nil {
|
|
t.Fatalf("config map not created: %v", err)
|
|
}
|
|
if _, ok := cm.Data["pgbouncer.ini"]; !ok {
|
|
t.Errorf("config map missing pgbouncer.ini")
|
|
}
|
|
|
|
if err := cluster.syncConnectionPoolerConfigMap(Master); err != nil {
|
|
t.Fatalf("unexpected error on resync: %v", err)
|
|
}
|
|
|
|
// drift -> update branch: change a config input that alters the rendered ini
|
|
cluster.OpConfig.ConnectionPooler.AuthType = "md5"
|
|
if err := cluster.syncConnectionPoolerConfigMap(Master); err != nil {
|
|
t.Fatalf("unexpected error on drift resync: %v", err)
|
|
}
|
|
cm, err = client.ConfigMaps("default").Get(context.TODO(), name, metav1.GetOptions{})
|
|
if err != nil {
|
|
t.Fatalf("config map not found after update: %v", err)
|
|
}
|
|
if !strings.Contains(cm.Data["pgbouncer.ini"], "auth_type = md5") {
|
|
t.Errorf("expected updated config map to contain auth_type = md5, got:\n%s", cm.Data["pgbouncer.ini"])
|
|
}
|
|
}
|