Sync due to defaults

Since we can miss it while checking only spec
This commit is contained in:
Dmitrii Dolgov 2020-03-02 16:32:15 +01:00
parent 3e98832703
commit 037d7120ef
6 changed files with 106 additions and 4 deletions

View File

@ -1206,7 +1206,7 @@ func (c *Cluster) deletePatroniClusterConfigMaps() error {
// Test if two connection pool configuration needs to be synced. For simplicity
// compare not the actual K8S objects, but the configuration itself and request
// sync if there is any difference.
func (c *Cluster) needSyncConnPoolDeployments(oldSpec, newSpec *acidv1.ConnectionPool) (sync bool, reasons []string) {
func (c *Cluster) needSyncConnPoolSpecs(oldSpec, newSpec *acidv1.ConnectionPool) (sync bool, reasons []string) {
reasons = []string{}
sync = false
@ -1228,3 +1228,69 @@ func (c *Cluster) needSyncConnPoolDeployments(oldSpec, newSpec *acidv1.Connectio
return sync, reasons
}
func syncResources(a, b *v1.ResourceRequirements) bool {
for _, res := range []v1.ResourceName{
v1.ResourceCPU,
v1.ResourceMemory,
} {
if !a.Limits[res].Equal(b.Limits[res]) ||
!a.Requests[res].Equal(b.Requests[res]) {
return true
}
}
return false
}
// Check if we need to synchronize connection pool deploymend due to new
// defaults, that are different from what we see in the DeploymentSpec
func (c *Cluster) needSyncConnPoolDefaults(
spec *acidv1.ConnectionPool,
deployment *appsv1.Deployment) (sync bool, reasons []string) {
reasons = []string{}
sync = false
config := c.OpConfig.ConnectionPool
podTemplate := deployment.Spec.Template
poolContainer := podTemplate.Spec.Containers[constants.ConnPoolContainer]
if spec.NumberOfInstances == nil &&
deployment.Spec.Replicas != config.NumberOfInstances {
sync = true
msg := fmt.Sprintf("NumberOfInstances is different (%d vs %d)",
deployment.Spec.Replicas, config.NumberOfInstances)
reasons = append(reasons, msg)
}
if spec.DockerImage == "" &&
poolContainer.Image != config.Image {
sync = true
msg := fmt.Sprintf("DockerImage is different (%s vs %s)",
poolContainer.Image, config.Image)
reasons = append(reasons, msg)
}
expectedResources, err := generateResourceRequirements(spec.Resources,
c.makeDefaultConnPoolResources())
// An error to generate expected resources means something is not quite
// right, but for the purpose of robustness do not panic here, just report
// and ignore resources comparison (in the worst case there will be no
// updates for new resource values).
if err == nil && syncResources(&poolContainer.Resources, expectedResources) {
sync = true
msg := fmt.Sprintf("Resources are different (%+v vs %+v)",
poolContainer.Resources, expectedResources)
reasons = append(reasons, msg)
}
if err != nil {
c.logger.Warningf("Cannot generate expected resources, %v", err)
}
return sync, reasons
}

View File

@ -1754,7 +1754,7 @@ func (c *Cluster) generateConnPoolPodTemplate(spec *acidv1.PostgresSpec) (
secretSelector := func(key string) *v1.SecretKeySelector {
return &v1.SecretKeySelector{
LocalObjectReference: v1.LocalObjectReference{
Name: c.credentialSecretName(c.OpConfig.SuperUsername),
Name: c.credentialSecretName(c.OpConfig.ConnectionPool.User),
},
Key: key,
}

View File

@ -702,8 +702,10 @@ func (c *Cluster) syncConnectionPoolWorker(oldSpec, newSpec *acidv1.Postgresql)
// actual synchronization
oldConnPool := oldSpec.Spec.ConnectionPool
newConnPool := newSpec.Spec.ConnectionPool
sync, reason := c.needSyncConnPoolDeployments(oldConnPool, newConnPool)
if sync {
specSync, specReason := c.needSyncConnPoolSpecs(oldConnPool, newConnPool)
defaultsSync, defaultsReason := c.needSyncConnPoolDefaults(newConnPool, deployment)
reason := append(specReason, defaultsReason...)
if specSync || defaultsSync {
c.logger.Infof("Update connection pool deployment %s, reason: %s",
c.connPoolName(), reason)

View File

@ -88,6 +88,11 @@ func TestConnPoolSynchronization(t *testing.T) {
Service: &v1.Service{},
}
clusterNewDefaultsMock := *cluster
clusterNewDefaultsMock.KubeClient = k8sutil.NewMockKubernetesClient()
cluster.OpConfig.ConnectionPool.Image = "pooler:2.0"
cluster.OpConfig.ConnectionPool.NumberOfInstances = int32ToPointer(2)
tests := []struct {
subTest string
oldSpec *acidv1.Postgresql
@ -166,6 +171,21 @@ func TestConnPoolSynchronization(t *testing.T) {
cluster: &clusterMock,
check: deploymentUpdated,
},
{
subTest: "update image from changed defaults",
oldSpec: &acidv1.Postgresql{
Spec: acidv1.PostgresSpec{
ConnectionPool: &acidv1.ConnectionPool{},
},
},
newSpec: &acidv1.Postgresql{
Spec: acidv1.PostgresSpec{
ConnectionPool: &acidv1.ConnectionPool{},
},
},
cluster: &clusterNewDefaultsMock,
check: deploymentUpdated,
},
}
for _, tt := range tests {
err := tt.cluster.syncConnectionPool(tt.oldSpec, tt.newSpec)

View File

@ -10,4 +10,6 @@ const (
ConnectionPoolDefaultCpuLimit = "100m"
ConnectionPoolDefaultMemoryRequest = "100Mi"
ConnectionPoolDefaultMemoryLimit = "100Mi"
ConnPoolContainer = 0
)

View File

@ -300,6 +300,18 @@ func (mock *mockDeployment) Get(name string, opts metav1.GetOptions) (*apiappsv1
ObjectMeta: metav1.ObjectMeta{
Name: "test-deployment",
},
Spec: apiappsv1.DeploymentSpec{
Replicas: int32ToPointer(1),
Template: v1.PodTemplateSpec{
Spec: v1.PodSpec{
Containers: []v1.Container{
v1.Container{
Image: "pooler:1.0",
},
},
},
},
},
}, nil
}