feat(pooler): create/sync/delete generated config map in lifecycle
This commit is contained in:
parent
c5e0b43a36
commit
e79c7d3ca3
|
|
@ -773,9 +773,28 @@ func (c *Cluster) deleteConnectionPooler(role PostgresRole) (err error) {
|
|||
c.logger.Infof("connection pooler auth secret %s has been deleted for role %s", authSecret.Name, role)
|
||||
}
|
||||
|
||||
// Repeat the same for the generated config map
|
||||
configMap := c.ConnectionPooler[role].ConfigMap
|
||||
if configMap == nil {
|
||||
c.logger.Debug("no connection pooler config map object to delete")
|
||||
} else {
|
||||
err = c.KubeClient.
|
||||
ConfigMaps(c.Namespace).
|
||||
Delete(context.TODO(), configMap.Name, options)
|
||||
|
||||
if k8sutil.ResourceNotFound(err) {
|
||||
c.logger.Debugf("connection pooler config map %s for role %s has already been deleted", configMap.Name, role)
|
||||
} else if err != nil {
|
||||
return fmt.Errorf("could not delete connection pooler config map: %v", err)
|
||||
}
|
||||
|
||||
c.logger.Infof("connection pooler config map %s has been deleted for role %s", configMap.Name, role)
|
||||
}
|
||||
|
||||
c.ConnectionPooler[role].AuthSecret = nil
|
||||
c.ConnectionPooler[role].Deployment = nil
|
||||
c.ConnectionPooler[role].Service = nil
|
||||
c.ConnectionPooler[role].ConfigMap = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -799,6 +818,40 @@ func (c *Cluster) deleteConnectionPoolerSecret() (err error) {
|
|||
return nil
|
||||
}
|
||||
|
||||
// syncConnectionPoolerConfigMap reconciles the operator-generated pgbouncer
|
||||
// config map for the given role: create if missing, update on drift.
|
||||
func (c *Cluster) syncConnectionPoolerConfigMap(role PostgresRole) error {
|
||||
desired, err := c.generateConnectionPoolerConfigMap(role)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not generate connection pooler config map: %v", err)
|
||||
}
|
||||
|
||||
existing, err := c.KubeClient.ConfigMaps(c.Namespace).Get(context.TODO(), desired.Name, metav1.GetOptions{})
|
||||
if k8sutil.ResourceNotFound(err) {
|
||||
created, cErr := c.KubeClient.ConfigMaps(c.Namespace).Create(context.TODO(), desired, metav1.CreateOptions{})
|
||||
if cErr != nil {
|
||||
return fmt.Errorf("could not create connection pooler config map: %v", cErr)
|
||||
}
|
||||
c.ConnectionPooler[role].ConfigMap = created
|
||||
return nil
|
||||
} else if err != nil {
|
||||
return fmt.Errorf("could not get connection pooler config map: %v", err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(existing.Data, desired.Data) {
|
||||
desired.ResourceVersion = existing.ResourceVersion
|
||||
updated, uErr := c.KubeClient.ConfigMaps(c.Namespace).Update(context.TODO(), desired, metav1.UpdateOptions{})
|
||||
if uErr != nil {
|
||||
return fmt.Errorf("could not update connection pooler config map: %v", uErr)
|
||||
}
|
||||
c.ConnectionPooler[role].ConfigMap = updated
|
||||
return nil
|
||||
}
|
||||
|
||||
c.ConnectionPooler[role].ConfigMap = existing
|
||||
return nil
|
||||
}
|
||||
|
||||
// Perform actual patching of a connection pooler deployment, assuming that all
|
||||
// the check were already done before.
|
||||
func updateConnectionPoolerDeployment(KubeClient k8sutil.KubernetesClient, newDeployment *appsv1.Deployment, doUpdate bool) (*appsv1.Deployment, error) {
|
||||
|
|
@ -1146,6 +1199,14 @@ func (c *Cluster) syncConnectionPoolerWorker(oldSpec, newSpec *acidv1.Postgresql
|
|||
c.ConnectionPooler[role].AuthSecret = authSecret
|
||||
}
|
||||
|
||||
// reconcile the generated pgbouncer config map before the deployment so the
|
||||
// mounted config exists when pods start
|
||||
if c.OpConfig.ConnectionPooler.GenerateConfig {
|
||||
if cmErr := c.syncConnectionPoolerConfigMap(role); cmErr != nil {
|
||||
return NoSync, cmErr
|
||||
}
|
||||
}
|
||||
|
||||
// next the pooler deployment
|
||||
deployment, err = c.KubeClient.
|
||||
Deployments(c.Namespace).
|
||||
|
|
@ -1206,7 +1267,10 @@ func (c *Cluster) syncConnectionPoolerWorker(oldSpec, newSpec *acidv1.Postgresql
|
|||
syncReason = append(syncReason, specReason...)
|
||||
}
|
||||
|
||||
newPodAnnotations := c.annotationsSet(c.generatePodAnnotations(&c.Spec))
|
||||
newPodAnnotations, annErr := c.connectionPoolerPodAnnotations(role)
|
||||
if annErr != nil {
|
||||
return nil, fmt.Errorf("could not generate pod annotations for connection pooler: %v", annErr)
|
||||
}
|
||||
deletedPodAnnotations := []string{}
|
||||
if changed, reason := c.compareAnnotations(deployment.Spec.Template.Annotations, newPodAnnotations, &deletedPodAnnotations); changed {
|
||||
specSync = true
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ func newFakeK8sPoolerTestClient() (k8sutil.KubernetesClient, *fake.Clientset) {
|
|||
DeploymentsGetter: clientSet.AppsV1(),
|
||||
ServicesGetter: clientSet.CoreV1(),
|
||||
SecretsGetter: clientSet.CoreV1(),
|
||||
ConfigMapsGetter: clientSet.CoreV1(),
|
||||
}, clientSet
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package cluster
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
|
|
@ -9,6 +10,7 @@ import (
|
|||
"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 {
|
||||
|
|
@ -182,3 +184,49 @@ func TestPoolerPodTemplateGeneratedConfigOff(t *testing.T) {
|
|||
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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue