mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-22 03:11:40 +00:00
Merge pull request #89482 from renatoviana12/master
fixed percentage behaviour in instr
This commit is contained in:
commit
1a66eb7b8a
@ -465,7 +465,7 @@ func TestSetDefaultDeployment(t *testing.T) {
|
||||
func TestDefaultDeploymentAvailability(t *testing.T) {
|
||||
d := roundTrip(t, runtime.Object(&appsv1.Deployment{})).(*appsv1.Deployment)
|
||||
|
||||
maxUnavailable, err := intstr.GetValueFromIntOrPercent(d.Spec.Strategy.RollingUpdate.MaxUnavailable, int(*(d.Spec.Replicas)), false)
|
||||
maxUnavailable, err := intstr.GetScaledValueFromIntOrPercent(d.Spec.Strategy.RollingUpdate.MaxUnavailable, int(*(d.Spec.Replicas)), false)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
@ -187,7 +187,7 @@ func TestSetDefaultDeployment(t *testing.T) {
|
||||
func TestDefaultDeploymentAvailability(t *testing.T) {
|
||||
d := roundTrip(t, runtime.Object(&appsv1beta1.Deployment{})).(*appsv1beta1.Deployment)
|
||||
|
||||
maxUnavailable, err := intstr.GetValueFromIntOrPercent(d.Spec.Strategy.RollingUpdate.MaxUnavailable, int(*(d.Spec.Replicas)), false)
|
||||
maxUnavailable, err := intstr.GetScaledValueFromIntOrPercent(d.Spec.Strategy.RollingUpdate.MaxUnavailable, int(*(d.Spec.Replicas)), false)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
@ -435,7 +435,7 @@ func TestSetDefaultDeployment(t *testing.T) {
|
||||
func TestDefaultDeploymentAvailability(t *testing.T) {
|
||||
d := roundTrip(t, runtime.Object(&appsv1beta2.Deployment{})).(*appsv1beta2.Deployment)
|
||||
|
||||
maxUnavailable, err := intstr.GetValueFromIntOrPercent(d.Spec.Strategy.RollingUpdate.MaxUnavailable, int(*(d.Spec.Replicas)), false)
|
||||
maxUnavailable, err := intstr.GetScaledValueFromIntOrPercent(d.Spec.Strategy.RollingUpdate.MaxUnavailable, int(*(d.Spec.Replicas)), false)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
@ -415,7 +415,7 @@ func (dsc *DaemonSetsController) getUnavailableNumbers(ds *apps.DaemonSet, nodeL
|
||||
numUnavailable++
|
||||
}
|
||||
}
|
||||
maxUnavailable, err := intstrutil.GetValueFromIntOrPercent(ds.Spec.UpdateStrategy.RollingUpdate.MaxUnavailable, desiredNumberScheduled, true)
|
||||
maxUnavailable, err := intstrutil.GetScaledValueFromIntOrPercent(ds.Spec.UpdateStrategy.RollingUpdate.MaxUnavailable, desiredNumberScheduled, true)
|
||||
if err != nil {
|
||||
return -1, -1, fmt.Errorf("invalid value for MaxUnavailable: %v", err)
|
||||
}
|
||||
|
@ -817,7 +817,7 @@ func NewRSNewReplicas(deployment *apps.Deployment, allRSs []*apps.ReplicaSet, ne
|
||||
switch deployment.Spec.Strategy.Type {
|
||||
case apps.RollingUpdateDeploymentStrategyType:
|
||||
// Check if we can scale up.
|
||||
maxSurge, err := intstrutil.GetValueFromIntOrPercent(deployment.Spec.Strategy.RollingUpdate.MaxSurge, int(*(deployment.Spec.Replicas)), true)
|
||||
maxSurge, err := intstrutil.GetScaledValueFromIntOrPercent(deployment.Spec.Strategy.RollingUpdate.MaxSurge, int(*(deployment.Spec.Replicas)), true)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@ -881,11 +881,11 @@ func WaitForObservedDeployment(getDeploymentFunc func() (*apps.Deployment, error
|
||||
// 2 desired, max unavailable 0%, surge 1% - should scale new(+1), then old(-1), then new(+1), then old(-1)
|
||||
// 1 desired, max unavailable 0%, surge 1% - should scale new(+1), then old(-1)
|
||||
func ResolveFenceposts(maxSurge, maxUnavailable *intstrutil.IntOrString, desired int32) (int32, int32, error) {
|
||||
surge, err := intstrutil.GetValueFromIntOrPercent(intstrutil.ValueOrDefault(maxSurge, intstrutil.FromInt(0)), int(desired), true)
|
||||
surge, err := intstrutil.GetScaledValueFromIntOrPercent(intstrutil.ValueOrDefault(maxSurge, intstrutil.FromInt(0)), int(desired), true)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
unavailable, err := intstrutil.GetValueFromIntOrPercent(intstrutil.ValueOrDefault(maxUnavailable, intstrutil.FromInt(0)), int(desired), false)
|
||||
unavailable, err := intstrutil.GetScaledValueFromIntOrPercent(intstrutil.ValueOrDefault(maxUnavailable, intstrutil.FromInt(0)), int(desired), false)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
|
@ -599,7 +599,7 @@ func (dc *DisruptionController) getExpectedPodCount(pdb *policy.PodDisruptionBud
|
||||
return
|
||||
}
|
||||
var maxUnavailable int
|
||||
maxUnavailable, err = intstr.GetValueFromIntOrPercent(pdb.Spec.MaxUnavailable, int(expectedCount), true)
|
||||
maxUnavailable, err = intstr.GetScaledValueFromIntOrPercent(pdb.Spec.MaxUnavailable, int(expectedCount), true)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@ -618,7 +618,7 @@ func (dc *DisruptionController) getExpectedPodCount(pdb *policy.PodDisruptionBud
|
||||
}
|
||||
|
||||
var minAvailable int
|
||||
minAvailable, err = intstr.GetValueFromIntOrPercent(pdb.Spec.MinAvailable, int(expectedCount), true)
|
||||
minAvailable, err = intstr.GetScaledValueFromIntOrPercent(pdb.Spec.MinAvailable, int(expectedCount), true)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -135,6 +135,33 @@ func ValueOrDefault(intOrPercent *IntOrString, defaultValue IntOrString) *IntOrS
|
||||
return intOrPercent
|
||||
}
|
||||
|
||||
// GetScaledValueFromIntOrPercent is meant to replace GetValueFromIntOrPercent.
|
||||
// This method returns a scaled value from an IntOrString type. If the IntOrString
|
||||
// is a percentage string value it's treated as a percentage and scaled appropriately
|
||||
// in accordance to the total, if it's an int value it's treated as a a simple value and
|
||||
// if it is a string value which is either non-numeric or numeric but lacking a trailing '%' it returns an error.
|
||||
func GetScaledValueFromIntOrPercent(intOrPercent *IntOrString, total int, roundUp bool) (int, error) {
|
||||
if intOrPercent == nil {
|
||||
return 0, errors.New("nil value for IntOrString")
|
||||
}
|
||||
value, isPercent, err := getIntOrPercentValueSafely(intOrPercent)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("invalid value for IntOrString: %v", err)
|
||||
}
|
||||
if isPercent {
|
||||
if roundUp {
|
||||
value = int(math.Ceil(float64(value) * (float64(total)) / 100))
|
||||
} else {
|
||||
value = int(math.Floor(float64(value) * (float64(total)) / 100))
|
||||
}
|
||||
}
|
||||
return value, nil
|
||||
}
|
||||
|
||||
// GetValueFromIntOrPercent was deprecated in favor of
|
||||
// GetScaledValueFromIntOrPercent. This method was treating all int as a numeric value and all
|
||||
// strings with or without a percent symbol as a percentage value.
|
||||
// Deprecated
|
||||
func GetValueFromIntOrPercent(intOrPercent *IntOrString, total int, roundUp bool) (int, error) {
|
||||
if intOrPercent == nil {
|
||||
return 0, errors.New("nil value for IntOrString")
|
||||
@ -153,6 +180,8 @@ func GetValueFromIntOrPercent(intOrPercent *IntOrString, total int, roundUp bool
|
||||
return value, nil
|
||||
}
|
||||
|
||||
// getIntOrPercentValue is a legacy function and only meant to be called by GetValueFromIntOrPercent
|
||||
// For a more correct implementation call getIntOrPercentSafely
|
||||
func getIntOrPercentValue(intOrStr *IntOrString) (int, bool, error) {
|
||||
switch intOrStr.Type {
|
||||
case Int:
|
||||
@ -167,3 +196,25 @@ func getIntOrPercentValue(intOrStr *IntOrString) (int, bool, error) {
|
||||
}
|
||||
return 0, false, fmt.Errorf("invalid type: neither int nor percentage")
|
||||
}
|
||||
|
||||
func getIntOrPercentValueSafely(intOrStr *IntOrString) (int, bool, error) {
|
||||
switch intOrStr.Type {
|
||||
case Int:
|
||||
return intOrStr.IntValue(), false, nil
|
||||
case String:
|
||||
isPercent := false
|
||||
s := intOrStr.StrVal
|
||||
if strings.HasSuffix(s, "%") {
|
||||
isPercent = true
|
||||
s = strings.TrimSuffix(intOrStr.StrVal, "%")
|
||||
} else {
|
||||
return 0, false, fmt.Errorf("invalid type: string is not a percentage")
|
||||
}
|
||||
v, err := strconv.Atoi(s)
|
||||
if err != nil {
|
||||
return 0, false, fmt.Errorf("invalid value %q: %v", intOrStr.StrVal, err)
|
||||
}
|
||||
return int(v), isPercent, nil
|
||||
}
|
||||
return 0, false, fmt.Errorf("invalid type: neither int nor percentage")
|
||||
}
|
||||
|
@ -110,7 +110,79 @@ func TestIntOrStringMarshalJSONUnmarshalYAML(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetValueFromIntOrPercent(t *testing.T) {
|
||||
func TestGetIntFromIntOrString(t *testing.T) {
|
||||
tests := []struct {
|
||||
input IntOrString
|
||||
expectErr bool
|
||||
expectVal int
|
||||
expectPerc bool
|
||||
}{
|
||||
{
|
||||
input: FromInt(200),
|
||||
expectErr: false,
|
||||
expectVal: 200,
|
||||
expectPerc: false,
|
||||
},
|
||||
{
|
||||
input: FromString("200"),
|
||||
expectErr: true,
|
||||
expectPerc: false,
|
||||
},
|
||||
{
|
||||
input: FromString("30%0"),
|
||||
expectErr: true,
|
||||
expectPerc: false,
|
||||
},
|
||||
{
|
||||
input: FromString("40%"),
|
||||
expectErr: false,
|
||||
expectVal: 40,
|
||||
expectPerc: true,
|
||||
},
|
||||
{
|
||||
input: FromString("%"),
|
||||
expectErr: true,
|
||||
expectPerc: false,
|
||||
},
|
||||
{
|
||||
input: FromString("a%"),
|
||||
expectErr: true,
|
||||
expectPerc: false,
|
||||
},
|
||||
{
|
||||
input: FromString("a"),
|
||||
expectErr: true,
|
||||
expectPerc: false,
|
||||
},
|
||||
{
|
||||
input: FromString("40#"),
|
||||
expectErr: true,
|
||||
expectPerc: false,
|
||||
},
|
||||
{
|
||||
input: FromString("40%%"),
|
||||
expectErr: true,
|
||||
expectPerc: false,
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run("", func(t *testing.T) {
|
||||
value, isPercent, err := getIntOrPercentValueSafely(&test.input)
|
||||
if test.expectVal != value {
|
||||
t.Fatalf("expected value does not match, expected: %d, got: %d", test.expectVal, value)
|
||||
}
|
||||
if test.expectPerc != isPercent {
|
||||
t.Fatalf("expected percent does not match, expected: %t, got: %t", test.expectPerc, isPercent)
|
||||
}
|
||||
if test.expectErr != (err != nil) {
|
||||
t.Fatalf("expected error does not match, expected error: %v, got: %v", test.expectErr, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestGetIntFromIntOrPercent(t *testing.T) {
|
||||
tests := []struct {
|
||||
input IntOrString
|
||||
total int
|
||||
@ -156,11 +228,15 @@ func TestGetValueFromIntOrPercent(t *testing.T) {
|
||||
input: FromString("#%"),
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
input: FromString("90"),
|
||||
expectErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
t.Logf("test case %d", i)
|
||||
value, err := GetValueFromIntOrPercent(&test.input, test.total, test.roundUp)
|
||||
value, err := GetScaledValueFromIntOrPercent(&test.input, test.total, test.roundUp)
|
||||
if test.expectErr && err == nil {
|
||||
t.Errorf("expected error, but got none")
|
||||
continue
|
||||
@ -176,7 +252,7 @@ func TestGetValueFromIntOrPercent(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetValueFromIntOrPercentNil(t *testing.T) {
|
||||
_, err := GetValueFromIntOrPercent(nil, 0, false)
|
||||
_, err := GetScaledValueFromIntOrPercent(nil, 0, false)
|
||||
if err == nil {
|
||||
t.Errorf("expected error got none")
|
||||
}
|
||||
|
@ -208,11 +208,11 @@ func findOldReplicaSets(deployment *appsv1.Deployment, rsList []*appsv1.ReplicaS
|
||||
// 2 desired, max unavailable 0%, surge 1% - should scale new(+1), then old(-1), then new(+1), then old(-1)
|
||||
// 1 desired, max unavailable 0%, surge 1% - should scale new(+1), then old(-1)
|
||||
func ResolveFenceposts(maxSurge, maxUnavailable *intstrutil.IntOrString, desired int32) (int32, int32, error) {
|
||||
surge, err := intstrutil.GetValueFromIntOrPercent(intstrutil.ValueOrDefault(maxSurge, intstrutil.FromInt(0)), int(desired), true)
|
||||
surge, err := intstrutil.GetScaledValueFromIntOrPercent(intstrutil.ValueOrDefault(maxSurge, intstrutil.FromInt(0)), int(desired), true)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
unavailable, err := intstrutil.GetValueFromIntOrPercent(intstrutil.ValueOrDefault(maxUnavailable, intstrutil.FromInt(0)), int(desired), false)
|
||||
unavailable, err := intstrutil.GetScaledValueFromIntOrPercent(intstrutil.ValueOrDefault(maxUnavailable, intstrutil.FromInt(0)), int(desired), false)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
|
@ -755,7 +755,7 @@ func testProportionalScalingDeployment(f *framework.Framework) {
|
||||
framework.ExpectNoError(err)
|
||||
|
||||
// Checking state of first rollout's replicaset.
|
||||
maxUnavailable, err := intstr.GetValueFromIntOrPercent(deployment.Spec.Strategy.RollingUpdate.MaxUnavailable, int(*(deployment.Spec.Replicas)), false)
|
||||
maxUnavailable, err := intstr.GetScaledValueFromIntOrPercent(deployment.Spec.Strategy.RollingUpdate.MaxUnavailable, int(*(deployment.Spec.Replicas)), false)
|
||||
framework.ExpectNoError(err)
|
||||
|
||||
// First rollout's replicaset should have Deployment's (replicas - maxUnavailable) = 10 - 2 = 8 available replicas.
|
||||
@ -780,7 +780,7 @@ func testProportionalScalingDeployment(f *framework.Framework) {
|
||||
secondRS, err := deploymentutil.GetNewReplicaSet(deployment, c.AppsV1())
|
||||
framework.ExpectNoError(err)
|
||||
|
||||
maxSurge, err := intstr.GetValueFromIntOrPercent(deployment.Spec.Strategy.RollingUpdate.MaxSurge, int(*(deployment.Spec.Replicas)), false)
|
||||
maxSurge, err := intstr.GetScaledValueFromIntOrPercent(deployment.Spec.Strategy.RollingUpdate.MaxSurge, int(*(deployment.Spec.Replicas)), false)
|
||||
framework.ExpectNoError(err)
|
||||
|
||||
// Second rollout's replicaset should have 0 available replicas.
|
||||
|
Loading…
Reference in New Issue
Block a user