Enable connection pooler for replica
- Refactor code for connection pooler deployment and services - Refactor sync code for connection pooler - Rename EnableConnectionPooler to EnableMasterConnectionPooler - Update yamls and tests
This commit is contained in:
		
							parent
							
								
									83ddd5c85b
								
							
						
					
					
						commit
						fb49376085
					
				| 
						 | 
					@ -185,7 +185,9 @@ spec:
 | 
				
			||||||
              # Note: usernames specified here as database owners must be declared in the users key of the spec key.
 | 
					              # Note: usernames specified here as database owners must be declared in the users key of the spec key.
 | 
				
			||||||
            dockerImage:
 | 
					            dockerImage:
 | 
				
			||||||
              type: string
 | 
					              type: string
 | 
				
			||||||
            enableConnectionPooler:
 | 
					            enableMasterConnectionPooler:
 | 
				
			||||||
 | 
					              type: boolean
 | 
				
			||||||
 | 
					            enableReplicaConnectionPooler:
 | 
				
			||||||
                type: boolean
 | 
					                type: boolean
 | 
				
			||||||
            enableLogicalBackup:
 | 
					            enableLogicalBackup:
 | 
				
			||||||
              type: boolean
 | 
					              type: boolean
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -140,7 +140,7 @@ These parameters are grouped directly under  the `spec` key in the manifest.
 | 
				
			||||||
  is `false`, then no volume will be mounted no matter how operator was
 | 
					  is `false`, then no volume will be mounted no matter how operator was
 | 
				
			||||||
  configured (so you can override the operator configuration). Optional.
 | 
					  configured (so you can override the operator configuration). Optional.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
* **enableConnectionPooler**
 | 
					* **enableMasterConnectionPooler**
 | 
				
			||||||
  Tells the operator to create a connection pooler with a database. If this
 | 
					  Tells the operator to create a connection pooler with a database. If this
 | 
				
			||||||
  field is true, a connection pooler deployment will be created even if
 | 
					  field is true, a connection pooler deployment will be created even if
 | 
				
			||||||
  `connectionPooler` section is empty. Optional, not set by default.
 | 
					  `connectionPooler` section is empty. Optional, not set by default.
 | 
				
			||||||
| 
						 | 
					@ -387,7 +387,7 @@ CPU and memory limits for the sidecar container.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Parameters are grouped under the `connectionPooler` top-level key and specify
 | 
					Parameters are grouped under the `connectionPooler` top-level key and specify
 | 
				
			||||||
configuration for connection pooler. If this section is not empty, a connection
 | 
					configuration for connection pooler. If this section is not empty, a connection
 | 
				
			||||||
pooler will be created for a database even if `enableConnectionPooler` is not
 | 
					pooler will be created for a database even if `enableMasterConnectionPooler` is not
 | 
				
			||||||
present.
 | 
					present.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
* **numberOfInstances**
 | 
					* **numberOfInstances**
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -736,7 +736,7 @@ manifest:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```yaml
 | 
					```yaml
 | 
				
			||||||
spec:
 | 
					spec:
 | 
				
			||||||
  enableConnectionPooler: true
 | 
					  enableMasterConnectionPooler: true
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
This will tell the operator to create a connection pooler with default
 | 
					This will tell the operator to create a connection pooler with default
 | 
				
			||||||
| 
						 | 
					@ -772,7 +772,7 @@ spec:
 | 
				
			||||||
        memory: 100Mi
 | 
					        memory: 100Mi
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
The `enableConnectionPooler` flag is not required when the `connectionPooler`
 | 
					The `enableMasterConnectionPooler` flag is not required when the `connectionPooler`
 | 
				
			||||||
section is present in the manifest. But, it can be used to disable/remove the
 | 
					section is present in the manifest. But, it can be used to disable/remove the
 | 
				
			||||||
pooler while keeping its configuration.
 | 
					pooler while keeping its configuration.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -99,7 +99,7 @@ class EndToEndTestCase(unittest.TestCase):
 | 
				
			||||||
                'postgresqls', 'acid-minimal-cluster',
 | 
					                'postgresqls', 'acid-minimal-cluster',
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    'spec': {
 | 
					                    'spec': {
 | 
				
			||||||
                        'enableConnectionPooler': True,
 | 
					                        'enableMasterConnectionPooler': True,
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                })
 | 
					                })
 | 
				
			||||||
            k8s.wait_for_pod_start(pod_selector)
 | 
					            k8s.wait_for_pod_start(pod_selector)
 | 
				
			||||||
| 
						 | 
					@ -141,7 +141,7 @@ class EndToEndTestCase(unittest.TestCase):
 | 
				
			||||||
                'postgresqls', 'acid-minimal-cluster',
 | 
					                'postgresqls', 'acid-minimal-cluster',
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    'spec': {
 | 
					                    'spec': {
 | 
				
			||||||
                        'enableConnectionPooler': False,
 | 
					                        'enableMasterConnectionPooler': False,
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                })
 | 
					                })
 | 
				
			||||||
            k8s.wait_for_pods_to_stop(pod_selector)
 | 
					            k8s.wait_for_pods_to_stop(pod_selector)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -18,8 +18,8 @@ spec:
 | 
				
			||||||
    - createdb
 | 
					    - createdb
 | 
				
			||||||
  enableMasterLoadBalancer: false
 | 
					  enableMasterLoadBalancer: false
 | 
				
			||||||
  enableReplicaLoadBalancer: false
 | 
					  enableReplicaLoadBalancer: false
 | 
				
			||||||
  enableConnectionPooler: true  # not needed when connectionPooler section is present (see below)
 | 
					  enableMasterConnectionPooler: true  # not needed when connectionPooler section is present (see below)
 | 
				
			||||||
  #enableReplicaConnectionPooler: true # set to enable connectionPooler for replica endpoints
 | 
					  enableReplicaConnectionPooler: true # set to enable connectionPooler for replica endpoints
 | 
				
			||||||
  allowedSourceRanges:  # load balancers' source ranges for both master and replica services
 | 
					  allowedSourceRanges:  # load balancers' source ranges for both master and replica services
 | 
				
			||||||
  - 127.0.0.1/32
 | 
					  - 127.0.0.1/32
 | 
				
			||||||
  databases:
 | 
					  databases:
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -181,7 +181,9 @@ spec:
 | 
				
			||||||
              # Note: usernames specified here as database owners must be declared in the users key of the spec key.
 | 
					              # Note: usernames specified here as database owners must be declared in the users key of the spec key.
 | 
				
			||||||
            dockerImage:
 | 
					            dockerImage:
 | 
				
			||||||
              type: string
 | 
					              type: string
 | 
				
			||||||
            enableConnectionPooler:
 | 
					            enableMasterConnectionPooler:
 | 
				
			||||||
 | 
					              type: boolean
 | 
				
			||||||
 | 
					            enableReplicaConnectionPooler:
 | 
				
			||||||
                type: boolean
 | 
					                type: boolean
 | 
				
			||||||
            enableLogicalBackup:
 | 
					            enableLogicalBackup:
 | 
				
			||||||
              type: boolean
 | 
					              type: boolean
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -259,7 +259,7 @@ var PostgresCRDResourceValidation = apiextv1beta1.CustomResourceValidation{
 | 
				
			||||||
					"dockerImage": {
 | 
										"dockerImage": {
 | 
				
			||||||
						Type: "string",
 | 
											Type: "string",
 | 
				
			||||||
					},
 | 
										},
 | 
				
			||||||
					"enableConnectionPooler": {
 | 
										"enableMasterConnectionPooler": {
 | 
				
			||||||
						Type: "boolean",
 | 
											Type: "boolean",
 | 
				
			||||||
					},
 | 
										},
 | 
				
			||||||
					"enableReplicaConnectionPooler": {
 | 
										"enableReplicaConnectionPooler": {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -29,7 +29,7 @@ type PostgresSpec struct {
 | 
				
			||||||
	Patroni         `json:"patroni,omitempty"`
 | 
						Patroni         `json:"patroni,omitempty"`
 | 
				
			||||||
	Resources       `json:"resources,omitempty"`
 | 
						Resources       `json:"resources,omitempty"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	EnableConnectionPooler        *bool             `json:"enableConnectionPooler,omitempty"`
 | 
						EnableMasterConnectionPooler  *bool             `json:"enableMasterConnectionPooler,omitempty"`
 | 
				
			||||||
	EnableReplicaConnectionPooler *bool             `json:"enableReplicaConnectionPooler,omitempty"`
 | 
						EnableReplicaConnectionPooler *bool             `json:"enableReplicaConnectionPooler,omitempty"`
 | 
				
			||||||
	ConnectionPooler              *ConnectionPooler `json:"connectionPooler,omitempty"`
 | 
						ConnectionPooler              *ConnectionPooler `json:"connectionPooler,omitempty"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -56,6 +56,7 @@ type Config struct {
 | 
				
			||||||
// K8S objects that are belongs to a connection pooler
 | 
					// K8S objects that are belongs to a connection pooler
 | 
				
			||||||
type ConnectionPoolerObjects struct {
 | 
					type ConnectionPoolerObjects struct {
 | 
				
			||||||
	Deployment     *appsv1.Deployment
 | 
						Deployment     *appsv1.Deployment
 | 
				
			||||||
 | 
						ReplDeployment *appsv1.Deployment
 | 
				
			||||||
	Service        *v1.Service
 | 
						Service        *v1.Service
 | 
				
			||||||
	ReplService    *v1.Service
 | 
						ReplService    *v1.Service
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -719,7 +719,7 @@ func TestInitSystemUsers(t *testing.T) {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// cluster with connection pooler
 | 
						// cluster with connection pooler
 | 
				
			||||||
	cl.Spec.EnableConnectionPooler = boolToPointer(true)
 | 
						cl.Spec.EnableMasterConnectionPooler = boolToPointer(true)
 | 
				
			||||||
	cl.initSystemUsers()
 | 
						cl.initSystemUsers()
 | 
				
			||||||
	if _, exist := cl.systemUsers[constants.ConnectionPoolerUserKeyName]; !exist {
 | 
						if _, exist := cl.systemUsers[constants.ConnectionPoolerUserKeyName]; !exist {
 | 
				
			||||||
		t.Errorf("%s, connection pooler user is not present", testName)
 | 
							t.Errorf("%s, connection pooler user is not present", testName)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -462,7 +462,7 @@ func (c *Cluster) execCreateOrAlterExtension(extName, schemaName, statement, doi
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Creates a connection pool credentials lookup function in every database to
 | 
					// Creates a connection pool credentials lookup function in every database to
 | 
				
			||||||
// perform remote authentification.
 | 
					// perform remote authentication.
 | 
				
			||||||
func (c *Cluster) installLookupFunction(poolerSchema, poolerUser string) error {
 | 
					func (c *Cluster) installLookupFunction(poolerSchema, poolerUser string) error {
 | 
				
			||||||
	var stmtBytes bytes.Buffer
 | 
						var stmtBytes bytes.Buffer
 | 
				
			||||||
	c.logger.Info("Installing lookup function")
 | 
						c.logger.Info("Installing lookup function")
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -75,8 +75,12 @@ func (c *Cluster) statefulSetName() string {
 | 
				
			||||||
	return c.Name
 | 
						return c.Name
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (c *Cluster) connectionPoolerName() string {
 | 
					func (c *Cluster) connectionPoolerName(role PostgresRole) string {
 | 
				
			||||||
	return c.Name + "-pooler"
 | 
						name := c.Name + "-pooler"
 | 
				
			||||||
 | 
						if role == Replica {
 | 
				
			||||||
 | 
							name = name + "-repl"
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return name
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (c *Cluster) endpointName(role PostgresRole) string {
 | 
					func (c *Cluster) endpointName(role PostgresRole) string {
 | 
				
			||||||
| 
						 | 
					@ -2085,7 +2089,7 @@ func (c *Cluster) getConnectionPoolerEnvVars(spec *acidv1.PostgresSpec) []v1.Env
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return []v1.EnvVar{
 | 
						return []v1.EnvVar{
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			Name:  "CONNECTION_POOLER_MASTER_PORT",
 | 
								Name:  "CONNECTION_POOLER_PORT",
 | 
				
			||||||
			Value: fmt.Sprint(pgPort),
 | 
								Value: fmt.Sprint(pgPort),
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
| 
						 | 
					@ -2115,7 +2119,7 @@ func (c *Cluster) getConnectionPoolerEnvVars(spec *acidv1.PostgresSpec) []v1.Env
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (c *Cluster) generateConnectionPoolerPodTemplate(spec *acidv1.PostgresSpec) (
 | 
					func (c *Cluster) generateConnectionPoolerPodTemplate(spec *acidv1.PostgresSpec, role PostgresRole) (
 | 
				
			||||||
	*v1.PodTemplateSpec, error) {
 | 
						*v1.PodTemplateSpec, error) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	gracePeriod := int64(c.OpConfig.PodTerminateGracePeriod.Seconds())
 | 
						gracePeriod := int64(c.OpConfig.PodTerminateGracePeriod.Seconds())
 | 
				
			||||||
| 
						 | 
					@ -2151,11 +2155,11 @@ func (c *Cluster) generateConnectionPoolerPodTemplate(spec *acidv1.PostgresSpec)
 | 
				
			||||||
	envVars := []v1.EnvVar{
 | 
						envVars := []v1.EnvVar{
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			Name:  "PGHOST",
 | 
								Name:  "PGHOST",
 | 
				
			||||||
			Value: c.serviceAddress(Master),
 | 
								Value: c.serviceAddress(role),
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			Name:  "PGPORT",
 | 
								Name:  "PGPORT",
 | 
				
			||||||
			Value: c.servicePort(Master),
 | 
								Value: c.servicePort(role),
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			Name: "PGUSER",
 | 
								Name: "PGUSER",
 | 
				
			||||||
| 
						 | 
					@ -2237,7 +2241,7 @@ func (c *Cluster) ownerReferences() []metav1.OwnerReference {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (c *Cluster) generateConnectionPoolerDeployment(spec *acidv1.PostgresSpec) (
 | 
					func (c *Cluster) generateConnectionPoolerDeployment(spec *acidv1.PostgresSpec, role PostgresRole) (
 | 
				
			||||||
	*appsv1.Deployment, error) {
 | 
						*appsv1.Deployment, error) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// there are two ways to enable connection pooler, either to specify a
 | 
						// there are two ways to enable connection pooler, either to specify a
 | 
				
			||||||
| 
						 | 
					@ -2250,7 +2254,7 @@ func (c *Cluster) generateConnectionPoolerDeployment(spec *acidv1.PostgresSpec)
 | 
				
			||||||
		spec.ConnectionPooler = &acidv1.ConnectionPooler{}
 | 
							spec.ConnectionPooler = &acidv1.ConnectionPooler{}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	podTemplate, err := c.generateConnectionPoolerPodTemplate(spec)
 | 
						podTemplate, err := c.generateConnectionPoolerPodTemplate(spec, role)
 | 
				
			||||||
	numberOfInstances := spec.ConnectionPooler.NumberOfInstances
 | 
						numberOfInstances := spec.ConnectionPooler.NumberOfInstances
 | 
				
			||||||
	if numberOfInstances == nil {
 | 
						if numberOfInstances == nil {
 | 
				
			||||||
		numberOfInstances = util.CoalesceInt32(
 | 
							numberOfInstances = util.CoalesceInt32(
 | 
				
			||||||
| 
						 | 
					@ -2269,9 +2273,12 @@ func (c *Cluster) generateConnectionPoolerDeployment(spec *acidv1.PostgresSpec)
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var name string
 | 
				
			||||||
 | 
						name = c.connectionPoolerName(role)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	deployment := &appsv1.Deployment{
 | 
						deployment := &appsv1.Deployment{
 | 
				
			||||||
		ObjectMeta: metav1.ObjectMeta{
 | 
							ObjectMeta: metav1.ObjectMeta{
 | 
				
			||||||
			Name:        c.connectionPoolerName(),
 | 
								Name:        name,
 | 
				
			||||||
			Namespace:   c.Namespace,
 | 
								Namespace:   c.Namespace,
 | 
				
			||||||
			Labels:      c.connectionPoolerLabelsSelector().MatchLabels,
 | 
								Labels:      c.connectionPoolerLabelsSelector().MatchLabels,
 | 
				
			||||||
			Annotations: map[string]string{},
 | 
								Annotations: map[string]string{},
 | 
				
			||||||
| 
						 | 
					@ -2293,7 +2300,53 @@ func (c *Cluster) generateConnectionPoolerDeployment(spec *acidv1.PostgresSpec)
 | 
				
			||||||
	return deployment, nil
 | 
						return deployment, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (c *Cluster) generateConnectionPoolerService(spec *acidv1.PostgresSpec) *v1.Service {
 | 
					/* func (c *Cluster) generateReplicaConnectionPoolerDeployment(spec *acidv1.PostgresSpec) (
 | 
				
			||||||
 | 
						*appsv1.Deployment, error) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						podTemplate, err := c.generateConnectionPoolerPodTemplate(spec, Replica)
 | 
				
			||||||
 | 
						numberOfInstances := spec.ConnectionPooler.NumberOfInstances
 | 
				
			||||||
 | 
						if numberOfInstances == nil {
 | 
				
			||||||
 | 
							numberOfInstances = util.CoalesceInt32(
 | 
				
			||||||
 | 
								c.OpConfig.ConnectionPooler.NumberOfInstances,
 | 
				
			||||||
 | 
								k8sutil.Int32ToPointer(1))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if *numberOfInstances < constants.ConnectionPoolerMinInstances {
 | 
				
			||||||
 | 
							msg := "Adjusted number of connection pooler instances from %d to %d"
 | 
				
			||||||
 | 
							c.logger.Warningf(msg, numberOfInstances, constants.ConnectionPoolerMinInstances)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							*numberOfInstances = constants.ConnectionPoolerMinInstances
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						deployment := &appsv1.Deployment{
 | 
				
			||||||
 | 
							ObjectMeta: metav1.ObjectMeta{
 | 
				
			||||||
 | 
								Name:        c.connectionPoolerName() + "-repl",
 | 
				
			||||||
 | 
								Namespace:   c.Namespace,
 | 
				
			||||||
 | 
								Labels:      c.connectionPoolerLabelsSelector().MatchLabels,
 | 
				
			||||||
 | 
								Annotations: map[string]string{},
 | 
				
			||||||
 | 
								// make StatefulSet object its owner to represent the dependency.
 | 
				
			||||||
 | 
								// By itself StatefulSet is being deleted with "Orphaned"
 | 
				
			||||||
 | 
								// propagation policy, which means that it's deletion will not
 | 
				
			||||||
 | 
								// clean up this deployment, but there is a hope that this object
 | 
				
			||||||
 | 
								// will be garbage collected if something went wrong and operator
 | 
				
			||||||
 | 
								// didn't deleted it.
 | 
				
			||||||
 | 
								OwnerReferences: c.ownerReferences(),
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							Spec: appsv1.DeploymentSpec{
 | 
				
			||||||
 | 
								Replicas: numberOfInstances,
 | 
				
			||||||
 | 
								Selector: c.connectionPoolerLabelsSelector(),
 | 
				
			||||||
 | 
								Template: *podTemplate,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return deployment, nil
 | 
				
			||||||
 | 
					} */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *Cluster) generateConnectionPoolerService(spec *acidv1.PostgresSpec, role PostgresRole) *v1.Service {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// there are two ways to enable connection pooler, either to specify a
 | 
						// there are two ways to enable connection pooler, either to specify a
 | 
				
			||||||
	// connectionPooler section or enableConnectionPooler. In the second case
 | 
						// connectionPooler section or enableConnectionPooler. In the second case
 | 
				
			||||||
| 
						 | 
					@ -2304,24 +2357,26 @@ func (c *Cluster) generateConnectionPoolerService(spec *acidv1.PostgresSpec) *v1
 | 
				
			||||||
	if spec.ConnectionPooler == nil {
 | 
						if spec.ConnectionPooler == nil {
 | 
				
			||||||
		spec.ConnectionPooler = &acidv1.ConnectionPooler{}
 | 
							spec.ConnectionPooler = &acidv1.ConnectionPooler{}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						name := c.connectionPoolerName(role)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	serviceSpec := v1.ServiceSpec{
 | 
						serviceSpec := v1.ServiceSpec{
 | 
				
			||||||
		Ports: []v1.ServicePort{
 | 
							Ports: []v1.ServicePort{
 | 
				
			||||||
			{
 | 
								{
 | 
				
			||||||
				Name:       c.connectionPoolerName(),
 | 
									Name:       name,
 | 
				
			||||||
				Port:       pgPort,
 | 
									Port:       pgPort,
 | 
				
			||||||
				TargetPort: intstr.IntOrString{StrVal: c.servicePort(Master)},
 | 
									TargetPort: intstr.IntOrString{StrVal: c.servicePort(role)},
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		Type: v1.ServiceTypeClusterIP,
 | 
							Type: v1.ServiceTypeClusterIP,
 | 
				
			||||||
		Selector: map[string]string{
 | 
					 | 
				
			||||||
			"connection-pooler-repl": c.connectionPoolerName(),
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						if role == Replica {
 | 
				
			||||||
 | 
							serviceSpec.Selector = c.roleLabelsSet(false, Replica)
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							serviceSpec.Selector = map[string]string{"connection-pooler": name}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	service := &v1.Service{
 | 
						service := &v1.Service{
 | 
				
			||||||
		ObjectMeta: metav1.ObjectMeta{
 | 
							ObjectMeta: metav1.ObjectMeta{
 | 
				
			||||||
			Name:        c.connectionPoolerName(),
 | 
								Name:        name,
 | 
				
			||||||
			Namespace:   c.Namespace,
 | 
								Namespace:   c.Namespace,
 | 
				
			||||||
			Labels:      c.connectionPoolerLabelsSelector().MatchLabels,
 | 
								Labels:      c.connectionPoolerLabelsSelector().MatchLabels,
 | 
				
			||||||
			Annotations: map[string]string{},
 | 
								Annotations: map[string]string{},
 | 
				
			||||||
| 
						 | 
					@ -2339,7 +2394,7 @@ func (c *Cluster) generateConnectionPoolerService(spec *acidv1.PostgresSpec) *v1
 | 
				
			||||||
	return service
 | 
						return service
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (c *Cluster) generateReplicaConnectionPoolerService(spec *acidv1.PostgresSpec) *v1.Service {
 | 
					/* func (c *Cluster) generateReplicaConnectionPoolerService(spec *acidv1.PostgresSpec) *v1.Service {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	replicaserviceSpec := v1.ServiceSpec{
 | 
						replicaserviceSpec := v1.ServiceSpec{
 | 
				
			||||||
		Ports: []v1.ServicePort{
 | 
							Ports: []v1.ServicePort{
 | 
				
			||||||
| 
						 | 
					@ -2350,9 +2405,7 @@ func (c *Cluster) generateReplicaConnectionPoolerService(spec *acidv1.PostgresSp
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		Type:     v1.ServiceTypeClusterIP,
 | 
							Type:     v1.ServiceTypeClusterIP,
 | 
				
			||||||
		Selector: map[string]string{
 | 
							Selector: c.roleLabelsSet(false, Replica),
 | 
				
			||||||
			"connection-pooler-repl": c.connectionPoolerName() + "-repl",
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	service := &v1.Service{
 | 
						service := &v1.Service{
 | 
				
			||||||
| 
						 | 
					@ -2373,7 +2426,7 @@ func (c *Cluster) generateReplicaConnectionPoolerService(spec *acidv1.PostgresSp
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return service
 | 
						return service
 | 
				
			||||||
}
 | 
					} */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func ensurePath(file string, defaultDir string, defaultFile string) string {
 | 
					func ensurePath(file string, defaultDir string, defaultFile string) string {
 | 
				
			||||||
	if file == "" {
 | 
						if file == "" {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1090,7 +1090,7 @@ func TestConnectionPoolerPodSpec(t *testing.T) {
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	for _, tt := range tests {
 | 
						for _, tt := range tests {
 | 
				
			||||||
		podSpec, err := tt.cluster.generateConnectionPoolerPodTemplate(tt.spec)
 | 
							podSpec, err := tt.cluster.generateConnectionPoolerPodTemplate(tt.spec, Master)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if err != tt.expected && err.Error() != tt.expected.Error() {
 | 
							if err != tt.expected && err.Error() != tt.expected.Error() {
 | 
				
			||||||
			t.Errorf("%s [%s]: Could not generate pod template,\n %+v, expected\n %+v",
 | 
								t.Errorf("%s [%s]: Could not generate pod template,\n %+v, expected\n %+v",
 | 
				
			||||||
| 
						 | 
					@ -1192,7 +1192,7 @@ func TestConnectionPoolerDeploymentSpec(t *testing.T) {
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	for _, tt := range tests {
 | 
						for _, tt := range tests {
 | 
				
			||||||
		deployment, err := tt.cluster.generateConnectionPoolerDeployment(tt.spec)
 | 
							deployment, err := tt.cluster.generateConnectionPoolerDeployment(tt.spec, Master)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if err != tt.expected && err.Error() != tt.expected.Error() {
 | 
							if err != tt.expected && err.Error() != tt.expected.Error() {
 | 
				
			||||||
			t.Errorf("%s [%s]: Could not generate deployment spec,\n %+v, expected\n %+v",
 | 
								t.Errorf("%s [%s]: Could not generate deployment spec,\n %+v, expected\n %+v",
 | 
				
			||||||
| 
						 | 
					@ -1221,9 +1221,9 @@ func testServiceOwnwerReference(cluster *Cluster, service *v1.Service) error {
 | 
				
			||||||
func testServiceSelector(cluster *Cluster, service *v1.Service) error {
 | 
					func testServiceSelector(cluster *Cluster, service *v1.Service) error {
 | 
				
			||||||
	selector := service.Spec.Selector
 | 
						selector := service.Spec.Selector
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if selector["connection-pooler"] != cluster.connectionPoolerName() {
 | 
						if selector["connection-pooler"] != cluster.connectionPoolerName(Master) {
 | 
				
			||||||
		return fmt.Errorf("Selector is incorrect, got %s, expected %s",
 | 
							return fmt.Errorf("Selector is incorrect, got %s, expected %s",
 | 
				
			||||||
			selector["connection-pooler"], cluster.connectionPoolerName())
 | 
								selector["connection-pooler"], cluster.connectionPoolerName(Master))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
| 
						 | 
					@ -1289,7 +1289,7 @@ func TestConnectionPoolerServiceSpec(t *testing.T) {
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	for _, tt := range tests {
 | 
						for _, tt := range tests {
 | 
				
			||||||
		service := tt.cluster.generateConnectionPoolerService(tt.spec)
 | 
							service := tt.cluster.generateConnectionPoolerService(tt.spec, Master)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if err := tt.check(cluster, service); err != nil {
 | 
							if err := tt.check(cluster, service); err != nil {
 | 
				
			||||||
			t.Errorf("%s [%s]: Service spec is incorrect, %+v",
 | 
								t.Errorf("%s [%s]: Service spec is incorrect, %+v",
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -126,8 +126,8 @@ func (c *Cluster) createConnectionPooler(lookup InstallFunction) (*ConnectionPoo
 | 
				
			||||||
		msg = "could not prepare database for connection pooler: %v"
 | 
							msg = "could not prepare database for connection pooler: %v"
 | 
				
			||||||
		return nil, fmt.Errorf(msg, err)
 | 
							return nil, fmt.Errorf(msg, err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						if c.Spec.EnableMasterConnectionPooler != nil || c.ConnectionPooler != nil {
 | 
				
			||||||
	deploymentSpec, err := c.generateConnectionPoolerDeployment(&c.Spec)
 | 
							deploymentSpec, err := c.generateConnectionPoolerDeployment(&c.Spec, Master)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			msg = "could not generate deployment for connection pooler: %v"
 | 
								msg = "could not generate deployment for connection pooler: %v"
 | 
				
			||||||
			return nil, fmt.Errorf(msg, err)
 | 
								return nil, fmt.Errorf(msg, err)
 | 
				
			||||||
| 
						 | 
					@ -144,7 +144,7 @@ func (c *Cluster) createConnectionPooler(lookup InstallFunction) (*ConnectionPoo
 | 
				
			||||||
			return nil, err
 | 
								return nil, err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	serviceSpec := c.generateConnectionPoolerService(&c.Spec)
 | 
							serviceSpec := c.generateConnectionPoolerService(&c.Spec, Master)
 | 
				
			||||||
		service, err := c.KubeClient.
 | 
							service, err := c.KubeClient.
 | 
				
			||||||
			Services(serviceSpec.Namespace).
 | 
								Services(serviceSpec.Namespace).
 | 
				
			||||||
			Create(context.TODO(), serviceSpec, metav1.CreateOptions{})
 | 
								Create(context.TODO(), serviceSpec, metav1.CreateOptions{})
 | 
				
			||||||
| 
						 | 
					@ -152,29 +152,47 @@ func (c *Cluster) createConnectionPooler(lookup InstallFunction) (*ConnectionPoo
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return nil, err
 | 
								return nil, err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							c.ConnectionPooler = &ConnectionPoolerObjects{
 | 
				
			||||||
 | 
								Deployment: deployment,
 | 
				
			||||||
 | 
								Service:    service,
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							c.logger.Debugf("created new connection pooler %q, uid: %q",
 | 
				
			||||||
 | 
								util.NameFromMeta(deployment.ObjectMeta), deployment.UID)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if c.Spec.EnableReplicaConnectionPooler != nil && *c.Spec.EnableReplicaConnectionPooler == true {
 | 
						if c.Spec.EnableReplicaConnectionPooler != nil && *c.Spec.EnableReplicaConnectionPooler == true {
 | 
				
			||||||
		replServiceSpec := c.generateReplicaConnectionPoolerService(&c.Spec)
 | 
							repldeploymentSpec, err := c.generateConnectionPoolerDeployment(&c.Spec, Replica)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								msg = "could not generate deployment for connection pooler: %v"
 | 
				
			||||||
 | 
								return nil, fmt.Errorf(msg, err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// client-go does retry 10 times (with NoBackoff by default) when the API
 | 
				
			||||||
 | 
							// believe a request can be retried and returns Retry-After header. This
 | 
				
			||||||
 | 
							// should be good enough to not think about it here.
 | 
				
			||||||
 | 
							repldeployment, err := c.KubeClient.
 | 
				
			||||||
 | 
								Deployments(repldeploymentSpec.Namespace).
 | 
				
			||||||
 | 
								Create(context.TODO(), repldeploymentSpec, metav1.CreateOptions{})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return nil, err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							replServiceSpec := c.generateConnectionPoolerService(&c.Spec, Replica)
 | 
				
			||||||
		replService, err := c.KubeClient.
 | 
							replService, err := c.KubeClient.
 | 
				
			||||||
			Services(serviceSpec.Namespace).
 | 
								Services(replServiceSpec.Namespace).
 | 
				
			||||||
			Create(context.TODO(), replServiceSpec, metav1.CreateOptions{})
 | 
								Create(context.TODO(), replServiceSpec, metav1.CreateOptions{})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return nil, err
 | 
								return nil, err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		c.ConnectionPooler = &ConnectionPoolerObjects{
 | 
							c.ConnectionPooler = &ConnectionPoolerObjects{
 | 
				
			||||||
			Deployment:  deployment,
 | 
								ReplDeployment: repldeployment,
 | 
				
			||||||
			Service:     service,
 | 
					 | 
				
			||||||
			ReplService:    replService,
 | 
								ReplService:    replService,
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	} else {
 | 
							c.logger.Debugf("created new connection pooler for replica %q, uid: %q",
 | 
				
			||||||
		c.ConnectionPooler = &ConnectionPoolerObjects{
 | 
								util.NameFromMeta(repldeployment.ObjectMeta), repldeployment.UID)
 | 
				
			||||||
			Deployment: deployment,
 | 
					 | 
				
			||||||
			Service:    service,
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	c.logger.Debugf("created new connection pooler %q, uid: %q",
 | 
					 | 
				
			||||||
		util.NameFromMeta(deployment.ObjectMeta), deployment.UID)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return c.ConnectionPooler, nil
 | 
						return c.ConnectionPooler, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -192,7 +210,7 @@ func (c *Cluster) deleteConnectionPooler() (err error) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Clean up the deployment object. If deployment resource we've remembered
 | 
						// Clean up the deployment object. If deployment resource we've remembered
 | 
				
			||||||
	// is somehow empty, try to delete based on what would we generate
 | 
						// is somehow empty, try to delete based on what would we generate
 | 
				
			||||||
	deploymentName := c.connectionPoolerName()
 | 
						deploymentName := c.connectionPoolerName(Master)
 | 
				
			||||||
	deployment := c.ConnectionPooler.Deployment
 | 
						deployment := c.ConnectionPooler.Deployment
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if deployment != nil {
 | 
						if deployment != nil {
 | 
				
			||||||
| 
						 | 
					@ -217,7 +235,7 @@ func (c *Cluster) deleteConnectionPooler() (err error) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Repeat the same for the service object
 | 
						// Repeat the same for the service object
 | 
				
			||||||
	service := c.ConnectionPooler.Service
 | 
						service := c.ConnectionPooler.Service
 | 
				
			||||||
	serviceName := c.connectionPoolerName()
 | 
						serviceName := c.connectionPoolerName(Master)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if service != nil {
 | 
						if service != nil {
 | 
				
			||||||
		serviceName = service.Name
 | 
							serviceName = service.Name
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -97,7 +97,7 @@ func TestNeedConnectionPooler(t *testing.T) {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	cluster.Spec = acidv1.PostgresSpec{
 | 
						cluster.Spec = acidv1.PostgresSpec{
 | 
				
			||||||
		EnableConnectionPooler: boolToPointer(true),
 | 
							EnableMasterConnectionPooler: boolToPointer(true),
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if !cluster.needConnectionPooler() {
 | 
						if !cluster.needConnectionPooler() {
 | 
				
			||||||
| 
						 | 
					@ -106,7 +106,7 @@ func TestNeedConnectionPooler(t *testing.T) {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	cluster.Spec = acidv1.PostgresSpec{
 | 
						cluster.Spec = acidv1.PostgresSpec{
 | 
				
			||||||
		EnableConnectionPooler: boolToPointer(false),
 | 
							EnableMasterConnectionPooler: boolToPointer(false),
 | 
				
			||||||
		ConnectionPooler:             &acidv1.ConnectionPooler{},
 | 
							ConnectionPooler:             &acidv1.ConnectionPooler{},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -116,7 +116,7 @@ func TestNeedConnectionPooler(t *testing.T) {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	cluster.Spec = acidv1.PostgresSpec{
 | 
						cluster.Spec = acidv1.PostgresSpec{
 | 
				
			||||||
		EnableConnectionPooler: boolToPointer(true),
 | 
							EnableMasterConnectionPooler: boolToPointer(true),
 | 
				
			||||||
		ConnectionPooler:             &acidv1.ConnectionPooler{},
 | 
							ConnectionPooler:             &acidv1.ConnectionPooler{},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -922,34 +922,18 @@ func (c *Cluster) syncConnectionPooler(oldSpec,
 | 
				
			||||||
func (c *Cluster) syncConnectionPoolerWorker(oldSpec, newSpec *acidv1.Postgresql) (
 | 
					func (c *Cluster) syncConnectionPoolerWorker(oldSpec, newSpec *acidv1.Postgresql) (
 | 
				
			||||||
	SyncReason, error) {
 | 
						SyncReason, error) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	deployment, err := c.KubeClient.
 | 
						masterdeployment, err := c.checkAndCreateConnectionPoolerDeployment(Master, newSpec)
 | 
				
			||||||
		Deployments(c.Namespace).
 | 
					 | 
				
			||||||
		Get(context.TODO(), c.connectionPoolerName(), metav1.GetOptions{})
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if err != nil && k8sutil.ResourceNotFound(err) {
 | 
					 | 
				
			||||||
		msg := "Deployment %s for connection pooler synchronization is not found, create it"
 | 
					 | 
				
			||||||
		c.logger.Warningf(msg, c.connectionPoolerName())
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		deploymentSpec, err := c.generateConnectionPoolerDeployment(&newSpec.Spec)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
			msg = "could not generate deployment for connection pooler: %v"
 | 
							msg := "could not get connection pooler deployment to sync: %v"
 | 
				
			||||||
		return NoSync, fmt.Errorf(msg, err)
 | 
							return NoSync, fmt.Errorf(msg, err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						replicadeployment, err := c.checkAndCreateConnectionPoolerDeployment(Replica, newSpec)
 | 
				
			||||||
		deployment, err := c.KubeClient.
 | 
					 | 
				
			||||||
			Deployments(deploymentSpec.Namespace).
 | 
					 | 
				
			||||||
			Create(context.TODO(), deploymentSpec, metav1.CreateOptions{})
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
			return NoSync, err
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		c.ConnectionPooler.Deployment = deployment
 | 
					 | 
				
			||||||
	} else if err != nil {
 | 
					 | 
				
			||||||
		msg := "could not get connection pooler deployment to sync: %v"
 | 
							msg := "could not get connection pooler deployment to sync: %v"
 | 
				
			||||||
		return NoSync, fmt.Errorf(msg, err)
 | 
							return NoSync, fmt.Errorf(msg, err)
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		c.ConnectionPooler.Deployment = deployment
 | 
							c.ConnectionPooler.Deployment = masterdeployment
 | 
				
			||||||
 | 
							c.ConnectionPooler.ReplDeployment = replicadeployment
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// actual synchronization
 | 
							// actual synchronization
 | 
				
			||||||
		oldConnectionPooler := oldSpec.Spec.ConnectionPooler
 | 
							oldConnectionPooler := oldSpec.Spec.ConnectionPooler
 | 
				
			||||||
| 
						 | 
					@ -968,19 +952,63 @@ func (c *Cluster) syncConnectionPoolerWorker(oldSpec, newSpec *acidv1.Postgresql
 | 
				
			||||||
			newConnectionPooler = &acidv1.ConnectionPooler{}
 | 
								newConnectionPooler = &acidv1.ConnectionPooler{}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		c.logger.Infof("Old: %+v, New %+v", oldConnectionPooler, newConnectionPooler)
 | 
							c.logger.Infof("Old: %+v, New: %+v", oldConnectionPooler, newConnectionPooler)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		specSync, specReason := c.needSyncConnectionPoolerSpecs(oldConnectionPooler, newConnectionPooler)
 | 
							specSync, specReason := c.needSyncConnectionPoolerSpecs(oldConnectionPooler, newConnectionPooler)
 | 
				
			||||||
		defaultsSync, defaultsReason := c.needSyncConnectionPoolerDefaults(newConnectionPooler, deployment)
 | 
							defaultsSync, defaultsReason := c.needSyncConnectionPoolerDefaults(newConnectionPooler, masterdeployment)
 | 
				
			||||||
		reason := append(specReason, defaultsReason...)
 | 
							reason := append(specReason, defaultsReason...)
 | 
				
			||||||
		if specSync || defaultsSync {
 | 
							if specSync || defaultsSync {
 | 
				
			||||||
			c.logger.Infof("Update connection pooler deployment %s, reason: %+v",
 | 
								newmasterdeployment, err := c.UpdateConnectionPoolerDeploymentSub(Master, reason[:], newSpec)
 | 
				
			||||||
				c.connectionPoolerName(), reason)
 | 
								if err != nil {
 | 
				
			||||||
 | 
									return reason, err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								c.ConnectionPooler.Deployment = newmasterdeployment
 | 
				
			||||||
 | 
								return reason, nil
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			newDeploymentSpec, err := c.generateConnectionPoolerDeployment(&newSpec.Spec)
 | 
							defaultsSync, defaultsReason = c.needSyncConnectionPoolerDefaults(newConnectionPooler, replicadeployment)
 | 
				
			||||||
 | 
							reason = append(specReason, defaultsReason...)
 | 
				
			||||||
 | 
							if specSync || defaultsSync {
 | 
				
			||||||
 | 
								newreplicadeployment, err := c.UpdateConnectionPoolerDeploymentSub(Replica, reason[:], newSpec)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									return reason, err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								c.ConnectionPooler.Deployment = newreplicadeployment
 | 
				
			||||||
 | 
								return reason, nil
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						newAnnotations := c.AnnotationsToPropagate(c.ConnectionPooler.Deployment.Annotations)
 | 
				
			||||||
 | 
						if newAnnotations != nil {
 | 
				
			||||||
 | 
							c.updateConnectionPoolerAnnotations(newAnnotations)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						masterservice, err := c.checkAndCreateConnectionPoolerService(Master, newSpec)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							msg := "could not get connection pooler service to sync: %v"
 | 
				
			||||||
 | 
							return NoSync, fmt.Errorf(msg, err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						replicaservice, err := c.checkAndCreateConnectionPoolerService(Replica, newSpec)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							msg := "could not get connection pooler service to sync: %v"
 | 
				
			||||||
 | 
							return NoSync, fmt.Errorf(msg, err)
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							// Service updates are not supported and probably not that useful anyway
 | 
				
			||||||
 | 
							c.ConnectionPooler.Service = masterservice
 | 
				
			||||||
 | 
							c.ConnectionPooler.ReplService = replicaservice
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return NoSync, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *Cluster) UpdateConnectionPoolerDeploymentSub(role PostgresRole, reason []string, newSpec *acidv1.Postgresql) (*appsv1.Deployment, error) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						c.logger.Infof("Update connection pooler deployment %s, reason: %+v",
 | 
				
			||||||
 | 
							c.connectionPoolerName(role), reason)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						newDeploymentSpec, err := c.generateConnectionPoolerDeployment(&newSpec.Spec, role)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		msg := "could not generate deployment for connection pooler: %v"
 | 
							msg := "could not generate deployment for connection pooler: %v"
 | 
				
			||||||
				return reason, fmt.Errorf(msg, err)
 | 
							return nil, fmt.Errorf(msg, err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	oldDeploymentSpec := c.ConnectionPooler.Deployment
 | 
						oldDeploymentSpec := c.ConnectionPooler.Deployment
 | 
				
			||||||
| 
						 | 
					@ -990,44 +1018,59 @@ func (c *Cluster) syncConnectionPoolerWorker(oldSpec, newSpec *acidv1.Postgresql
 | 
				
			||||||
		newDeploymentSpec)
 | 
							newDeploymentSpec)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
				return reason, err
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return deployment, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *Cluster) checkAndCreateConnectionPoolerDeployment(role PostgresRole, newSpec *acidv1.Postgresql) (*appsv1.Deployment, error) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						deployment, err := c.KubeClient.
 | 
				
			||||||
 | 
							Deployments(c.Namespace).
 | 
				
			||||||
 | 
							Get(context.TODO(), c.connectionPoolerName(role), metav1.GetOptions{})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err != nil && k8sutil.ResourceNotFound(err) {
 | 
				
			||||||
 | 
							msg := "Deployment %s for connection pooler synchronization is not found, create it"
 | 
				
			||||||
 | 
							c.logger.Warningf(msg, c.connectionPoolerName(role))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							deploymentSpec, err := c.generateConnectionPoolerDeployment(&newSpec.Spec, role)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								msg = "could not generate deployment for connection pooler: %v"
 | 
				
			||||||
 | 
								return nil, fmt.Errorf(msg, err)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			c.ConnectionPooler.Deployment = deployment
 | 
							deployment, err := c.KubeClient.
 | 
				
			||||||
			return reason, nil
 | 
								Deployments(deploymentSpec.Namespace).
 | 
				
			||||||
		}
 | 
								Create(context.TODO(), deploymentSpec, metav1.CreateOptions{})
 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	newAnnotations := c.AnnotationsToPropagate(c.ConnectionPooler.Deployment.Annotations)
 | 
							if err != nil {
 | 
				
			||||||
	if newAnnotations != nil {
 | 
								return nil, err
 | 
				
			||||||
		c.updateConnectionPoolerAnnotations(newAnnotations)
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							return deployment, nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return deployment, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *Cluster) checkAndCreateConnectionPoolerService(role PostgresRole, newSpec *acidv1.Postgresql) (*v1.Service, error) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	service, err := c.KubeClient.
 | 
						service, err := c.KubeClient.
 | 
				
			||||||
		Services(c.Namespace).
 | 
							Services(c.Namespace).
 | 
				
			||||||
		Get(context.TODO(), c.connectionPoolerName(), metav1.GetOptions{})
 | 
							Get(context.TODO(), c.connectionPoolerName(role), metav1.GetOptions{})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if err != nil && k8sutil.ResourceNotFound(err) {
 | 
						if err != nil && k8sutil.ResourceNotFound(err) {
 | 
				
			||||||
		msg := "Service %s for connection pooler synchronization is not found, create it"
 | 
							msg := "Service %s for connection pooler synchronization is not found, create it"
 | 
				
			||||||
		c.logger.Warningf(msg, c.connectionPoolerName())
 | 
							c.logger.Warningf(msg, c.connectionPoolerName(role))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		serviceSpec := c.generateConnectionPoolerService(&newSpec.Spec)
 | 
							serviceSpec := c.generateConnectionPoolerService(&newSpec.Spec, role)
 | 
				
			||||||
		service, err := c.KubeClient.
 | 
							service, err := c.KubeClient.
 | 
				
			||||||
			Services(serviceSpec.Namespace).
 | 
								Services(serviceSpec.Namespace).
 | 
				
			||||||
			Create(context.TODO(), serviceSpec, metav1.CreateOptions{})
 | 
								Create(context.TODO(), serviceSpec, metav1.CreateOptions{})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return NoSync, err
 | 
								return nil, err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		c.ConnectionPooler.Service = service
 | 
							return service, nil
 | 
				
			||||||
	} else if err != nil {
 | 
					 | 
				
			||||||
		msg := "could not get connection pooler service to sync: %v"
 | 
					 | 
				
			||||||
		return NoSync, fmt.Errorf(msg, err)
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		// Service updates are not supported and probably not that useful anyway
 | 
					 | 
				
			||||||
		c.ConnectionPooler.Service = service
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						return service, nil
 | 
				
			||||||
	return NoSync, nil
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -139,7 +139,7 @@ func TestConnectionPoolerSynchronization(t *testing.T) {
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			newSpec: &acidv1.Postgresql{
 | 
								newSpec: &acidv1.Postgresql{
 | 
				
			||||||
				Spec: acidv1.PostgresSpec{
 | 
									Spec: acidv1.PostgresSpec{
 | 
				
			||||||
					EnableConnectionPooler: boolToPointer(true),
 | 
										EnableMasterConnectionPooler: boolToPointer(true),
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			cluster:          clusterMissingObjects,
 | 
								cluster:          clusterMissingObjects,
 | 
				
			||||||
| 
						 | 
					@ -232,13 +232,13 @@ func TestConnectionPoolerSynchronization(t *testing.T) {
 | 
				
			||||||
			subTest: "there is no sync from nil to an empty spec",
 | 
								subTest: "there is no sync from nil to an empty spec",
 | 
				
			||||||
			oldSpec: &acidv1.Postgresql{
 | 
								oldSpec: &acidv1.Postgresql{
 | 
				
			||||||
				Spec: acidv1.PostgresSpec{
 | 
									Spec: acidv1.PostgresSpec{
 | 
				
			||||||
					EnableConnectionPooler: boolToPointer(true),
 | 
										EnableMasterConnectionPooler: boolToPointer(true),
 | 
				
			||||||
					ConnectionPooler:             nil,
 | 
										ConnectionPooler:             nil,
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			newSpec: &acidv1.Postgresql{
 | 
								newSpec: &acidv1.Postgresql{
 | 
				
			||||||
				Spec: acidv1.PostgresSpec{
 | 
									Spec: acidv1.PostgresSpec{
 | 
				
			||||||
					EnableConnectionPooler: boolToPointer(true),
 | 
										EnableMasterConnectionPooler: boolToPointer(true),
 | 
				
			||||||
					ConnectionPooler:             &acidv1.ConnectionPooler{},
 | 
										ConnectionPooler:             &acidv1.ConnectionPooler{},
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -424,7 +424,7 @@ func (c *Cluster) connectionPoolerLabelsSelector() *metav1.LabelSelector {
 | 
				
			||||||
	connectionPoolerLabels := labels.Set(map[string]string{})
 | 
						connectionPoolerLabels := labels.Set(map[string]string{})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	extraLabels := labels.Set(map[string]string{
 | 
						extraLabels := labels.Set(map[string]string{
 | 
				
			||||||
		"connection-pooler": c.connectionPoolerName(),
 | 
							"connection-pooler": c.connectionPoolerName(Master),
 | 
				
			||||||
		"application":       "db-connection-pooler",
 | 
							"application":       "db-connection-pooler",
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -520,11 +520,16 @@ func (c *Cluster) patroniKubernetesUseConfigMaps() bool {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (c *Cluster) needConnectionPoolerWorker(spec *acidv1.PostgresSpec) bool {
 | 
					func (c *Cluster) needConnectionPoolerWorker(spec *acidv1.PostgresSpec) bool {
 | 
				
			||||||
	if spec.EnableConnectionPooler == nil {
 | 
						if spec.EnableMasterConnectionPooler != nil {
 | 
				
			||||||
 | 
							return *spec.EnableMasterConnectionPooler
 | 
				
			||||||
 | 
						} else if spec.EnableReplicaConnectionPooler != nil {
 | 
				
			||||||
 | 
							return *spec.EnableReplicaConnectionPooler
 | 
				
			||||||
 | 
						} else if spec.ConnectionPooler == nil {
 | 
				
			||||||
		return spec.ConnectionPooler != nil
 | 
							return spec.ConnectionPooler != nil
 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		return *spec.EnableConnectionPooler
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						// if the connectionPooler section is there, then we enable even though the
 | 
				
			||||||
 | 
						// flags are not there
 | 
				
			||||||
 | 
						return true
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (c *Cluster) needConnectionPooler() bool {
 | 
					func (c *Cluster) needConnectionPooler() bool {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -607,16 +607,27 @@ def update_postgresql(namespace: str, cluster: str):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        spec['volume'] = {'size': size}
 | 
					        spec['volume'] = {'size': size}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if 'enableConnectionPooler' in postgresql['spec']:
 | 
					    if 'enableMasterConnectionPooler' in postgresql['spec']:
 | 
				
			||||||
        cp = postgresql['spec']['enableConnectionPooler']
 | 
					        cp = postgresql['spec']['enableMasterConnectionPooler']
 | 
				
			||||||
        if not cp:
 | 
					        if not cp:
 | 
				
			||||||
            if 'enableConnectionPooler' in o['spec']:
 | 
					            if 'enableMasterConnectionPooler' in o['spec']:
 | 
				
			||||||
                del o['spec']['enableConnectionPooler']
 | 
					                del o['spec']['enableMasterConnectionPooler']
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            spec['enableConnectionPooler'] = True
 | 
					            spec['enableMasterConnectionPooler'] = True
 | 
				
			||||||
    else:
 | 
					    else:
 | 
				
			||||||
        if 'enableConnectionPooler' in o['spec']:
 | 
					        if 'enableMasterConnectionPooler' in o['spec']:
 | 
				
			||||||
            del o['spec']['enableConnectionPooler']
 | 
					            del o['spec']['enableMasterConnectionPooler']
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if 'enableReplicaConnectionPooler' in postgresql['spec']:
 | 
				
			||||||
 | 
					        cp = postgresql['spec']['enableReplicaConnectionPooler']
 | 
				
			||||||
 | 
					        if not cp:
 | 
				
			||||||
 | 
					            if 'enableReplicaConnectionPooler' in o['spec']:
 | 
				
			||||||
 | 
					                del o['spec']['enableReplicaConnectionPooler']
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            spec['enableReplicaConnectionPooler'] = True
 | 
				
			||||||
 | 
					    else:
 | 
				
			||||||
 | 
					        if 'enableReplicaConnectionPooler' in o['spec']:
 | 
				
			||||||
 | 
					            del o['spec']['enableReplicaConnectionPooler']
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if 'enableReplicaLoadBalancer' in postgresql['spec']:
 | 
					    if 'enableReplicaLoadBalancer' in postgresql['spec']:
 | 
				
			||||||
        rlb = postgresql['spec']['enableReplicaLoadBalancer']
 | 
					        rlb = postgresql['spec']['enableReplicaLoadBalancer']
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue