Improve the pod moving behavior during the Kubernetes cluster upgrade. (#281)

* Improve the pod moving behavior during the Kubernetes cluster upgrade.

Fix an issue of not waiting for at least one replica to become ready
(if the Statefulset indicates there are replicas) when moving the master
pod off the decomissioned node. Resolves the first part of #279.

Small fixes to error messages.

* Eliminate a race condition during the swithover.

When the operator initiates the failover (switchover) that fails and
then retries it for a second time it may happen that the previous
waitForPodChannel is still active. As a result, the operator subscribes
to the former master pod two times, causing a panic.

The problem was that the original code didn't bother to cancel the
waitForPodLalbel for the new master pod in the case when the failover
fails. This commit fixes it by adding a stop channel to that function.

Code review by @zerg-junior
This commit is contained in:
Oleksii Kliukin
2018-05-03 10:20:24 +02:00
committed by GitHub
parent ebff820fcc
commit fe47f9ebea
5 changed files with 71 additions and 29 deletions
+42 -16
View File
@@ -225,7 +225,7 @@ func (c *Cluster) getTeamMembers() ([]string, error) {
return teamInfo.Members, nil
}
func (c *Cluster) waitForPodLabel(podEvents chan spec.PodEvent, role *PostgresRole) (*v1.Pod, error) {
func (c *Cluster) waitForPodLabel(podEvents chan spec.PodEvent, stopChan chan struct{}, role *PostgresRole) (*v1.Pod, error) {
timeout := time.After(c.OpConfig.PodLabelWaitTimeout)
for {
select {
@@ -241,6 +241,8 @@ func (c *Cluster) waitForPodLabel(podEvents chan spec.PodEvent, role *PostgresRo
}
case <-timeout:
return nil, fmt.Errorf("pod label wait timeout")
case <-stopChan:
return nil, fmt.Errorf("pod label wait cancelled")
}
}
}
@@ -278,7 +280,10 @@ func (c *Cluster) waitStatefulsetReady() error {
})
}
func (c *Cluster) waitPodLabelsReady() error {
func (c *Cluster) _waitPodLabelsReady(anyReplica bool) error {
var (
podsNumber int
)
ls := c.labelsSet(false)
namespace := c.Namespace
@@ -295,35 +300,56 @@ func (c *Cluster) waitPodLabelsReady() error {
c.OpConfig.PodRoleLabel: string(Replica),
}).String(),
}
pods, err := c.KubeClient.Pods(namespace).List(listOptions)
if err != nil {
return err
podsNumber = 1
if !anyReplica {
pods, err := c.KubeClient.Pods(namespace).List(listOptions)
if err != nil {
return err
}
podsNumber = len(pods.Items)
c.logger.Debugf("Waiting for %d pods to become ready", podsNumber)
} else {
c.logger.Debugf("Waiting for any replica pod to become ready")
}
podsNumber := len(pods.Items)
err = retryutil.Retry(c.OpConfig.ResourceCheckInterval, c.OpConfig.ResourceCheckTimeout,
err := retryutil.Retry(c.OpConfig.ResourceCheckInterval, c.OpConfig.ResourceCheckTimeout,
func() (bool, error) {
masterPods, err2 := c.KubeClient.Pods(namespace).List(masterListOption)
if err2 != nil {
return false, err2
masterCount := 0
if !anyReplica {
masterPods, err2 := c.KubeClient.Pods(namespace).List(masterListOption)
if err2 != nil {
return false, err2
}
if len(masterPods.Items) > 1 {
return false, fmt.Errorf("too many masters (%d pods with the master label found)",
len(masterPods.Items))
}
masterCount = len(masterPods.Items)
}
replicaPods, err2 := c.KubeClient.Pods(namespace).List(replicaListOption)
if err2 != nil {
return false, err2
}
if len(masterPods.Items) > 1 {
return false, fmt.Errorf("too many masters")
}
if len(replicaPods.Items) == podsNumber {
replicaCount := len(replicaPods.Items)
if anyReplica && replicaCount > 0 {
c.logger.Debugf("Found %d running replica pods", replicaCount)
return true, nil
}
return len(masterPods.Items)+len(replicaPods.Items) == podsNumber, nil
return masterCount+replicaCount >= podsNumber, nil
})
return err
}
func (c *Cluster) waitForAnyReplicaLabelReady() error {
return c._waitPodLabelsReady(true)
}
func (c *Cluster) waitForAllPodsLabelReady() error {
return c._waitPodLabelsReady(false)
}
func (c *Cluster) waitStatefulsetPodsReady() error {
c.setProcessName("waiting for the pods of the statefulset")
// TODO: wait for the first Pod only
@@ -332,7 +358,7 @@ func (c *Cluster) waitStatefulsetPodsReady() error {
}
// TODO: wait only for master
if err := c.waitPodLabelsReady(); err != nil {
if err := c.waitForAllPodsLabelReady(); err != nil {
return fmt.Errorf("pod labels error: %v", err)
}