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)
|
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].AuthSecret = nil
|
||||||
c.ConnectionPooler[role].Deployment = nil
|
c.ConnectionPooler[role].Deployment = nil
|
||||||
c.ConnectionPooler[role].Service = nil
|
c.ConnectionPooler[role].Service = nil
|
||||||
|
c.ConnectionPooler[role].ConfigMap = nil
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -799,6 +818,40 @@ func (c *Cluster) deleteConnectionPoolerSecret() (err error) {
|
||||||
return nil
|
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
|
// Perform actual patching of a connection pooler deployment, assuming that all
|
||||||
// the check were already done before.
|
// the check were already done before.
|
||||||
func updateConnectionPoolerDeployment(KubeClient k8sutil.KubernetesClient, newDeployment *appsv1.Deployment, doUpdate bool) (*appsv1.Deployment, error) {
|
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
|
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
|
// next the pooler deployment
|
||||||
deployment, err = c.KubeClient.
|
deployment, err = c.KubeClient.
|
||||||
Deployments(c.Namespace).
|
Deployments(c.Namespace).
|
||||||
|
|
@ -1206,7 +1267,10 @@ func (c *Cluster) syncConnectionPoolerWorker(oldSpec, newSpec *acidv1.Postgresql
|
||||||
syncReason = append(syncReason, specReason...)
|
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{}
|
deletedPodAnnotations := []string{}
|
||||||
if changed, reason := c.compareAnnotations(deployment.Spec.Template.Annotations, newPodAnnotations, &deletedPodAnnotations); changed {
|
if changed, reason := c.compareAnnotations(deployment.Spec.Template.Annotations, newPodAnnotations, &deletedPodAnnotations); changed {
|
||||||
specSync = true
|
specSync = true
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@ func newFakeK8sPoolerTestClient() (k8sutil.KubernetesClient, *fake.Clientset) {
|
||||||
DeploymentsGetter: clientSet.AppsV1(),
|
DeploymentsGetter: clientSet.AppsV1(),
|
||||||
ServicesGetter: clientSet.CoreV1(),
|
ServicesGetter: clientSet.CoreV1(),
|
||||||
SecretsGetter: clientSet.CoreV1(),
|
SecretsGetter: clientSet.CoreV1(),
|
||||||
|
ConfigMapsGetter: clientSet.CoreV1(),
|
||||||
}, clientSet
|
}, clientSet
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package cluster
|
package cluster
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
|
@ -9,6 +10,7 @@ import (
|
||||||
"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"
|
v1 "k8s.io/api/core/v1"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newGenerateConfigCluster() *Cluster {
|
func newGenerateConfigCluster() *Cluster {
|
||||||
|
|
@ -182,3 +184,49 @@ func TestPoolerPodTemplateGeneratedConfigOff(t *testing.T) {
|
||||||
t.Errorf("did not expect checksum annotation when GenerateConfig is off")
|
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