Skip to content
This repository was archived by the owner on Feb 8, 2021. It is now read-only.

Commit 9cc1910

Browse files
committed
kubectl: preserve availability when maxUnavailability is not 100%
1 parent 5f86dd1 commit 9cc1910

File tree

2 files changed

+27
-103
lines changed

2 files changed

+27
-103
lines changed

pkg/kubectl/rolling_updater.go

Lines changed: 2 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ import (
2020
goerrors "errors"
2121
"fmt"
2222
"io"
23-
"math"
2423
"strconv"
2524
"strings"
2625
"time"
@@ -198,12 +197,12 @@ func (r *RollingUpdater) Update(config *RollingUpdaterConfig) error {
198197
oldRc.Name, originalReplicasAnnotation, oldRc.Annotations[originalReplicasAnnotation])
199198
}
200199
// The maximum pods which can go unavailable during the update.
201-
maxUnavailable, err := extractMaxValue(config.MaxUnavailable, "maxUnavailable", desired)
200+
maxUnavailable, err := intstr.GetValueFromIntOrPercent(&config.MaxUnavailable, desired, false)
202201
if err != nil {
203202
return err
204203
}
205204
// The maximum scaling increment.
206-
maxSurge, err := extractMaxValue(config.MaxSurge, "maxSurge", desired)
205+
maxSurge, err := intstr.GetValueFromIntOrPercent(&config.MaxSurge, desired, true)
207206
if err != nil {
208207
return err
209208
}
@@ -486,29 +485,6 @@ func (r *RollingUpdater) cleanupWithClients(oldRc, newRc *api.ReplicationControl
486485
}
487486
}
488487

489-
// func extractMaxValue is a helper to extract config max values as either
490-
// absolute numbers or based on percentages of the given value.
491-
func extractMaxValue(field intstr.IntOrString, name string, value int) (int, error) {
492-
switch field.Type {
493-
case intstr.Int:
494-
if field.IntVal < 0 {
495-
return 0, fmt.Errorf("%s must be >= 0", name)
496-
}
497-
return field.IntValue(), nil
498-
case intstr.String:
499-
s := strings.Replace(field.StrVal, "%", "", -1)
500-
v, err := strconv.Atoi(s)
501-
if err != nil {
502-
return 0, fmt.Errorf("invalid %s value %q: %v", name, field.StrVal, err)
503-
}
504-
if v < 0 {
505-
return 0, fmt.Errorf("%s must be >= 0", name)
506-
}
507-
return int(math.Ceil(float64(value) * (float64(v)) / 100)), nil
508-
}
509-
return 0, fmt.Errorf("invalid kind %q for %s", field.Type, name)
510-
}
511-
512488
func Rename(c client.ReplicationControllersNamespacer, rc *api.ReplicationController, newName string) error {
513489
oldName := rc.Name
514490
rc.Name = newName

pkg/kubectl/rolling_updater_test.go

Lines changed: 25 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -430,20 +430,21 @@ Scaling foo-v1 down to 0
430430
Scaling foo-v2 up to 10
431431
`,
432432
}, {
433-
name: "1->1 10/0 fast readiness",
433+
name: "1->1 25/25 maintain minimum availability",
434434
oldRc: oldRc(1, 1),
435435
newRc: newRc(0, 1),
436436
newRcExists: false,
437-
maxUnavail: intstr.FromString("10%"),
438-
maxSurge: intstr.FromString("0%"),
437+
maxUnavail: intstr.FromString("25%"),
438+
maxSurge: intstr.FromString("25%"),
439439
expected: []interface{}{
440-
down{oldReady: 1, newReady: 0, to: 0},
441440
up{1},
441+
down{oldReady: 1, newReady: 0, noop: true},
442+
down{oldReady: 1, newReady: 1, to: 0},
442443
},
443444
output: `Created foo-v2
444-
Scaling up foo-v2 from 0 to 1, scaling down foo-v1 from 1 to 0 (keep 0 pods available, don't exceed 1 pods)
445-
Scaling foo-v1 down to 0
445+
Scaling up foo-v2 from 0 to 1, scaling down foo-v1 from 1 to 0 (keep 1 pods available, don't exceed 2 pods)
446446
Scaling foo-v2 up to 1
447+
Scaling foo-v1 down to 0
447448
`,
448449
}, {
449450
name: "1->1 0/10 delayed readiness",
@@ -475,7 +476,7 @@ Scaling foo-v1 down to 0
475476
down{oldReady: 1, newReady: 1, to: 0},
476477
},
477478
output: `Created foo-v2
478-
Scaling up foo-v2 from 0 to 1, scaling down foo-v1 from 1 to 0 (keep 0 pods available, don't exceed 2 pods)
479+
Scaling up foo-v2 from 0 to 1, scaling down foo-v1 from 1 to 0 (keep 1 pods available, don't exceed 2 pods)
479480
Scaling foo-v2 up to 1
480481
Scaling foo-v1 down to 0
481482
`,
@@ -655,6 +656,23 @@ Scaling foo-v1 down to 1
655656
Scaling foo-v2 up to 1
656657
Scaling foo-v1 down to 0
657658
Scaling foo-v2 up to 2
659+
`,
660+
},
661+
{
662+
name: "1->1 100%/0 allow maxUnavailability",
663+
oldRc: oldRc(1, 1),
664+
newRc: newRc(0, 1),
665+
newRcExists: false,
666+
maxUnavail: intstr.FromString("100%"),
667+
maxSurge: intstr.FromInt(0),
668+
expected: []interface{}{
669+
down{oldReady: 1, newReady: 0, to: 0},
670+
up{1},
671+
},
672+
output: `Created foo-v2
673+
Scaling up foo-v2 from 0 to 1, scaling down foo-v1 from 1 to 0 (keep 0 pods available, don't exceed 1 pods)
674+
Scaling foo-v1 down to 0
675+
Scaling foo-v2 up to 1
658676
`,
659677
},
660678
}
@@ -1571,73 +1589,3 @@ func TestRollingUpdater_readyPods(t *testing.T) {
15711589
}
15721590
}
15731591
}
1574-
1575-
func TestRollingUpdater_extractMaxValue(t *testing.T) {
1576-
tests := []struct {
1577-
field intstr.IntOrString
1578-
original int
1579-
expected int
1580-
valid bool
1581-
}{
1582-
{
1583-
field: intstr.FromInt(1),
1584-
original: 100,
1585-
expected: 1,
1586-
valid: true,
1587-
},
1588-
{
1589-
field: intstr.FromInt(0),
1590-
original: 100,
1591-
expected: 0,
1592-
valid: true,
1593-
},
1594-
{
1595-
field: intstr.FromInt(-1),
1596-
original: 100,
1597-
valid: false,
1598-
},
1599-
{
1600-
field: intstr.FromString("10%"),
1601-
original: 100,
1602-
expected: 10,
1603-
valid: true,
1604-
},
1605-
{
1606-
field: intstr.FromString("100%"),
1607-
original: 100,
1608-
expected: 100,
1609-
valid: true,
1610-
},
1611-
{
1612-
field: intstr.FromString("200%"),
1613-
original: 100,
1614-
expected: 200,
1615-
valid: true,
1616-
},
1617-
{
1618-
field: intstr.FromString("0%"),
1619-
original: 100,
1620-
expected: 0,
1621-
valid: true,
1622-
},
1623-
{
1624-
field: intstr.FromString("-1%"),
1625-
original: 100,
1626-
valid: false,
1627-
},
1628-
}
1629-
1630-
for i, test := range tests {
1631-
t.Logf("evaluating test %d", i)
1632-
max, err := extractMaxValue(test.field, "field", test.original)
1633-
if test.valid && err != nil {
1634-
t.Fatalf("unexpected error: %v", err)
1635-
}
1636-
if !test.valid && err == nil {
1637-
t.Fatalf("expected an error")
1638-
}
1639-
if e, a := test.expected, max; e != a {
1640-
t.Fatalf("expected max %d, got %d", e, a)
1641-
}
1642-
}
1643-
}

0 commit comments

Comments
 (0)