diff --git a/pkg/kubectl/rolling_updater.go b/pkg/kubectl/rolling_updater.go index 7266ff4cfb8..6a6d579b5d1 100644 --- a/pkg/kubectl/rolling_updater.go +++ b/pkg/kubectl/rolling_updater.go @@ -208,13 +208,20 @@ func (r *RollingUpdater) Update(config *RollingUpdaterConfig) error { if err != nil { return err } - // Further validation. - if maxUnavailable == 0 && maxSurge == 0 { + // Validate maximums. + if desired > 0 && maxUnavailable == 0 && maxSurge == 0 { return fmt.Errorf("one of maxSurge or maxUnavailable must be specified") } // The minumum pods which must remain available througout the update // calculated for internal convenience. - minAvailable := original - maxUnavailable + minAvailable := int(math.Max(float64(0), float64(desired-maxUnavailable))) + // If the desired new scale is 0, then the max unavailable is necessarily + // the effective scale of the old RC regardless of the configuration + // (equivalent to 100% maxUnavailable). + if desired == 0 { + maxUnavailable = original + minAvailable = 0 + } fmt.Fprintf(out, "Scaling up %s from %d to %d, scaling down %s from %d to 0 (keep %d pods available, don't exceed %d pods)\n", newRc.Name, newRc.Spec.Replicas, desired, oldRc.Name, oldRc.Spec.Replicas, minAvailable, original+maxSurge) diff --git a/pkg/kubectl/rolling_updater_test.go b/pkg/kubectl/rolling_updater_test.go index f9834336b1c..982baad670b 100644 --- a/pkg/kubectl/rolling_updater_test.go +++ b/pkg/kubectl/rolling_updater_test.go @@ -539,7 +539,7 @@ Scaling foo-v1 down to 0 down{oldReady: 10, newReady: 20, to: 0}, }, output: `Created foo-v2 -Scaling up foo-v2 from 0 to 20, scaling down foo-v1 from 10 to 0 (keep 10 pods available, don't exceed 70 pods) +Scaling up foo-v2 from 0 to 20, scaling down foo-v1 from 10 to 0 (keep 20 pods available, don't exceed 70 pods) Scaling foo-v2 up to 20 Scaling foo-v1 down to 0 `, @@ -557,6 +557,83 @@ Scaling foo-v1 down to 0 output: `Continuing update with existing controller foo-v2. Scaling up foo-v2 from 1 to 1, scaling down foo-v1 from 1 to 0 (keep 1 pods available, don't exceed 2 pods) Scaling foo-v1 down to 0 +`, + }, + { + name: "3->0 1/1 desired 0 (absolute values)", + oldRc: oldRc(3, 3), + newRc: newRc(0, 0), + newRcExists: true, + maxUnavail: intstr.FromInt(1), + maxSurge: intstr.FromInt(1), + expected: []interface{}{ + down{oldReady: 3, newReady: 0, to: 0}, + }, + output: `Continuing update with existing controller foo-v2. +Scaling up foo-v2 from 0 to 0, scaling down foo-v1 from 3 to 0 (keep 0 pods available, don't exceed 4 pods) +Scaling foo-v1 down to 0 +`, + }, + { + name: "3->0 10/10 desired 0 (percentages)", + oldRc: oldRc(3, 3), + newRc: newRc(0, 0), + newRcExists: true, + maxUnavail: intstr.FromString("10%"), + maxSurge: intstr.FromString("10%"), + expected: []interface{}{ + down{oldReady: 3, newReady: 0, to: 0}, + }, + output: `Continuing update with existing controller foo-v2. +Scaling up foo-v2 from 0 to 0, scaling down foo-v1 from 3 to 0 (keep 0 pods available, don't exceed 3 pods) +Scaling foo-v1 down to 0 +`, + }, + { + name: "3->0 10/10 desired 0 (create new RC)", + oldRc: oldRc(3, 3), + newRc: newRc(0, 0), + newRcExists: false, + maxUnavail: intstr.FromString("10%"), + maxSurge: intstr.FromString("10%"), + expected: []interface{}{ + down{oldReady: 3, newReady: 0, to: 0}, + }, + output: `Created foo-v2 +Scaling up foo-v2 from 0 to 0, scaling down foo-v1 from 3 to 0 (keep 0 pods available, don't exceed 3 pods) +Scaling foo-v1 down to 0 +`, + }, + { + name: "0->0 1/1 desired 0 (absolute values)", + oldRc: oldRc(0, 0), + newRc: newRc(0, 0), + newRcExists: true, + maxUnavail: intstr.FromInt(1), + maxSurge: intstr.FromInt(1), + expected: []interface{}{ + down{oldReady: 0, newReady: 0, to: 0}, + }, + output: `Continuing update with existing controller foo-v2. +Scaling up foo-v2 from 0 to 0, scaling down foo-v1 from 0 to 0 (keep 0 pods available, don't exceed 1 pods) +`, + }, { + name: "30->2 50%/0", + oldRc: oldRc(30, 30), + newRc: newRc(0, 2), + newRcExists: false, + maxUnavail: intstr.FromString("50%"), + maxSurge: intstr.FromInt(0), + expected: []interface{}{ + down{oldReady: 30, newReady: 0, to: 1}, + up{2}, + down{oldReady: 1, newReady: 2, to: 0}, + }, + output: `Created foo-v2 +Scaling up foo-v2 from 0 to 2, scaling down foo-v1 from 30 to 0 (keep 1 pods available, don't exceed 30 pods) +Scaling foo-v1 down to 1 +Scaling foo-v2 up to 2 +Scaling foo-v1 down to 0 `, }, }