From 4e21723a0c1f4fcb92df40c89b9c7b36e47a15e1 Mon Sep 17 00:00:00 2001 From: Chris Friesen Date: Mon, 4 Apr 2022 14:43:30 -0600 Subject: [PATCH] Wait for at least 1 replica for Deployments and Daemonsets The wait logic for Deployments and Daemonsets subtracts their maxUnavailable value from the amount of replicas they should have ready. This can lead to waiting for 0 pods to be ready. This change ensures at least 1 replica is ready, unless there are 0 desired replicas. Fixes #8660. This is based on https://github.com/helm/helm/pull/8671 by Sean Eagan which was never merged. Co-authored-by: Sean Eagan Signed-off-by: Sean Eagan Signed-off-by: Chris Friesen --- pkg/kube/ready.go | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/pkg/kube/ready.go b/pkg/kube/ready.go index 5d080d9bf..e46ee3e15 100644 --- a/pkg/kube/ready.go +++ b/pkg/kube/ready.go @@ -272,7 +272,17 @@ func (c *ReadyChecker) volumeReady(v *corev1.PersistentVolumeClaim) bool { } func (c *ReadyChecker) deploymentReady(rs *appsv1.ReplicaSet, dep *appsv1.Deployment) bool { + // If there are no desired replicas, there will be nothing to wait for. + if *dep.Spec.Replicas == 0 { + return true + } + + // Ensure the expected available replicas are ready. expectedReady := *dep.Spec.Replicas - deploymentutil.MaxUnavailable(*dep) + // Also ensure at least 1 replica is ready. + if expectedReady < 1 { + expectedReady = 1 + } if !(rs.Status.ReadyReplicas >= expectedReady) { c.log("Deployment is not ready: %s/%s. %d out of %d expected pods are ready", dep.Namespace, dep.Name, rs.Status.ReadyReplicas, expectedReady) return false @@ -286,11 +296,18 @@ func (c *ReadyChecker) daemonSetReady(ds *appsv1.DaemonSet) bool { return true } + // If there are no desired replicas, there will be nothing to wait for. + if ds.Status.DesiredNumberScheduled == 0 { + return true + } + // Make sure all the updated pods have been scheduled if ds.Status.UpdatedNumberScheduled != ds.Status.DesiredNumberScheduled { c.log("DaemonSet is not ready: %s/%s. %d out of %d expected pods have been scheduled", ds.Namespace, ds.Name, ds.Status.UpdatedNumberScheduled, ds.Status.DesiredNumberScheduled) return false } + + // Ensure the expected available replicas are ready. maxUnavailable, err := intstr.GetValueFromIntOrPercent(ds.Spec.UpdateStrategy.RollingUpdate.MaxUnavailable, int(ds.Status.DesiredNumberScheduled), true) if err != nil { // If for some reason the value is invalid, set max unavailable to the @@ -298,8 +315,11 @@ func (c *ReadyChecker) daemonSetReady(ds *appsv1.DaemonSet) bool { // `MaxUnavailable` function in deploymentutil maxUnavailable = int(ds.Status.DesiredNumberScheduled) } - expectedReady := int(ds.Status.DesiredNumberScheduled) - maxUnavailable + // Also ensure at least 1 replica is ready. + if expectedReady < 1 { + expectedReady = 1 + } if !(int(ds.Status.NumberReady) >= expectedReady) { c.log("DaemonSet is not ready: %s/%s. %d out of %d expected pods are ready", ds.Namespace, ds.Name, ds.Status.NumberReady, expectedReady) return false