mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-23 19:56:01 +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) {
|
func TestDefaultDeploymentAvailability(t *testing.T) {
|
||||||
d := roundTrip(t, runtime.Object(&appsv1.Deployment{})).(*appsv1.Deployment)
|
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 {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error: %v", err)
|
t.Fatalf("unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -187,7 +187,7 @@ func TestSetDefaultDeployment(t *testing.T) {
|
|||||||
func TestDefaultDeploymentAvailability(t *testing.T) {
|
func TestDefaultDeploymentAvailability(t *testing.T) {
|
||||||
d := roundTrip(t, runtime.Object(&appsv1beta1.Deployment{})).(*appsv1beta1.Deployment)
|
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 {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error: %v", err)
|
t.Fatalf("unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -435,7 +435,7 @@ func TestSetDefaultDeployment(t *testing.T) {
|
|||||||
func TestDefaultDeploymentAvailability(t *testing.T) {
|
func TestDefaultDeploymentAvailability(t *testing.T) {
|
||||||
d := roundTrip(t, runtime.Object(&appsv1beta2.Deployment{})).(*appsv1beta2.Deployment)
|
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 {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error: %v", err)
|
t.Fatalf("unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -415,7 +415,7 @@ func (dsc *DaemonSetsController) getUnavailableNumbers(ds *apps.DaemonSet, nodeL
|
|||||||
numUnavailable++
|
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 {
|
if err != nil {
|
||||||
return -1, -1, fmt.Errorf("invalid value for MaxUnavailable: %v", err)
|
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 {
|
switch deployment.Spec.Strategy.Type {
|
||||||
case apps.RollingUpdateDeploymentStrategyType:
|
case apps.RollingUpdateDeploymentStrategyType:
|
||||||
// Check if we can scale up.
|
// 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 {
|
if err != nil {
|
||||||
return 0, err
|
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)
|
// 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)
|
// 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) {
|
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 {
|
if err != nil {
|
||||||
return 0, 0, err
|
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 {
|
if err != nil {
|
||||||
return 0, 0, err
|
return 0, 0, err
|
||||||
}
|
}
|
||||||
|
@ -599,7 +599,7 @@ func (dc *DisruptionController) getExpectedPodCount(pdb *policy.PodDisruptionBud
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
var maxUnavailable int
|
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 {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -618,7 +618,7 @@ func (dc *DisruptionController) getExpectedPodCount(pdb *policy.PodDisruptionBud
|
|||||||
}
|
}
|
||||||
|
|
||||||
var minAvailable int
|
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 {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -135,6 +135,33 @@ func ValueOrDefault(intOrPercent *IntOrString, defaultValue IntOrString) *IntOrS
|
|||||||
return intOrPercent
|
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) {
|
func GetValueFromIntOrPercent(intOrPercent *IntOrString, total int, roundUp bool) (int, error) {
|
||||||
if intOrPercent == nil {
|
if intOrPercent == nil {
|
||||||
return 0, errors.New("nil value for IntOrString")
|
return 0, errors.New("nil value for IntOrString")
|
||||||
@ -153,6 +180,8 @@ func GetValueFromIntOrPercent(intOrPercent *IntOrString, total int, roundUp bool
|
|||||||
return value, nil
|
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) {
|
func getIntOrPercentValue(intOrStr *IntOrString) (int, bool, error) {
|
||||||
switch intOrStr.Type {
|
switch intOrStr.Type {
|
||||||
case Int:
|
case Int:
|
||||||
@ -167,3 +196,25 @@ func getIntOrPercentValue(intOrStr *IntOrString) (int, bool, error) {
|
|||||||
}
|
}
|
||||||
return 0, false, fmt.Errorf("invalid type: neither int nor percentage")
|
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 {
|
tests := []struct {
|
||||||
input IntOrString
|
input IntOrString
|
||||||
total int
|
total int
|
||||||
@ -156,11 +228,15 @@ func TestGetValueFromIntOrPercent(t *testing.T) {
|
|||||||
input: FromString("#%"),
|
input: FromString("#%"),
|
||||||
expectErr: true,
|
expectErr: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
input: FromString("90"),
|
||||||
|
expectErr: true,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, test := range tests {
|
for i, test := range tests {
|
||||||
t.Logf("test case %d", i)
|
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 {
|
if test.expectErr && err == nil {
|
||||||
t.Errorf("expected error, but got none")
|
t.Errorf("expected error, but got none")
|
||||||
continue
|
continue
|
||||||
@ -176,7 +252,7 @@ func TestGetValueFromIntOrPercent(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestGetValueFromIntOrPercentNil(t *testing.T) {
|
func TestGetValueFromIntOrPercentNil(t *testing.T) {
|
||||||
_, err := GetValueFromIntOrPercent(nil, 0, false)
|
_, err := GetScaledValueFromIntOrPercent(nil, 0, false)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Errorf("expected error got none")
|
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)
|
// 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)
|
// 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) {
|
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 {
|
if err != nil {
|
||||||
return 0, 0, err
|
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 {
|
if err != nil {
|
||||||
return 0, 0, err
|
return 0, 0, err
|
||||||
}
|
}
|
||||||
|
@ -755,7 +755,7 @@ func testProportionalScalingDeployment(f *framework.Framework) {
|
|||||||
framework.ExpectNoError(err)
|
framework.ExpectNoError(err)
|
||||||
|
|
||||||
// Checking state of first rollout's replicaset.
|
// 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)
|
framework.ExpectNoError(err)
|
||||||
|
|
||||||
// First rollout's replicaset should have Deployment's (replicas - maxUnavailable) = 10 - 2 = 8 available replicas.
|
// 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())
|
secondRS, err := deploymentutil.GetNewReplicaSet(deployment, c.AppsV1())
|
||||||
framework.ExpectNoError(err)
|
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)
|
framework.ExpectNoError(err)
|
||||||
|
|
||||||
// Second rollout's replicaset should have 0 available replicas.
|
// Second rollout's replicaset should have 0 available replicas.
|
||||||
|
Loading…
Reference in New Issue
Block a user