mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-09-20 17:38:50 +00:00
validation: Handle presence of MaxSurge on DaemonSet
When the maxsurge daemonset gate is disabled, the registry and validation must properly handle stripping the field. In the special case where that would leave the MaxUnavailable field set to 0, we must set it to 1 which is the default value.
This commit is contained in:
@@ -14,6 +14,7 @@ go_library(
|
||||
"//pkg/apis/apps:go_default_library",
|
||||
"//pkg/apis/core:go_default_library",
|
||||
"//pkg/apis/core/validation:go_default_library",
|
||||
"//pkg/features:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/api/equality:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/api/validation:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
@@ -22,6 +23,7 @@ go_library(
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/intstr:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/validation:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/validation/field:go_default_library",
|
||||
"//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
|
@@ -28,9 +28,11 @@ import (
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
"k8s.io/apimachinery/pkg/util/validation"
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
"k8s.io/kubernetes/pkg/apis/apps"
|
||||
api "k8s.io/kubernetes/pkg/apis/core"
|
||||
apivalidation "k8s.io/kubernetes/pkg/apis/core/validation"
|
||||
"k8s.io/kubernetes/pkg/features"
|
||||
)
|
||||
|
||||
// ValidateStatefulSetName can be used to check whether the given StatefulSet name is valid.
|
||||
@@ -343,14 +345,35 @@ func ValidateDaemonSetSpec(spec *apps.DaemonSetSpec, fldPath *field.Path, opts a
|
||||
|
||||
// ValidateRollingUpdateDaemonSet validates a given RollingUpdateDaemonSet.
|
||||
func ValidateRollingUpdateDaemonSet(rollingUpdate *apps.RollingUpdateDaemonSet, fldPath *field.Path) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
allErrs = append(allErrs, ValidatePositiveIntOrPercent(rollingUpdate.MaxUnavailable, fldPath.Child("maxUnavailable"))...)
|
||||
if getIntOrPercentValue(rollingUpdate.MaxUnavailable) == 0 {
|
||||
// MaxUnavailable cannot be 0.
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("maxUnavailable"), rollingUpdate.MaxUnavailable, "cannot be 0"))
|
||||
var allErrs field.ErrorList
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.DaemonSetUpdateSurge) {
|
||||
// Validate both fields are positive ints or have a percentage value
|
||||
allErrs = append(allErrs, ValidatePositiveIntOrPercent(rollingUpdate.MaxUnavailable, fldPath.Child("maxUnavailable"))...)
|
||||
allErrs = append(allErrs, ValidatePositiveIntOrPercent(rollingUpdate.MaxSurge, fldPath.Child("maxSurge"))...)
|
||||
|
||||
// Validate that MaxUnavailable and MaxSurge are not more than 100%.
|
||||
allErrs = append(allErrs, IsNotMoreThan100Percent(rollingUpdate.MaxUnavailable, fldPath.Child("maxUnavailable"))...)
|
||||
allErrs = append(allErrs, IsNotMoreThan100Percent(rollingUpdate.MaxSurge, fldPath.Child("maxSurge"))...)
|
||||
|
||||
// Validate exactly one of MaxSurge or MaxUnavailable is non-zero
|
||||
hasUnavailable := getIntOrPercentValue(rollingUpdate.MaxUnavailable) != 0
|
||||
hasSurge := getIntOrPercentValue(rollingUpdate.MaxSurge) != 0
|
||||
switch {
|
||||
case hasUnavailable && hasSurge:
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("maxSurge"), rollingUpdate.MaxSurge, "may not be set when maxUnavailable is non-zero"))
|
||||
case !hasUnavailable && !hasSurge:
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("maxUnavailable"), "cannot be 0 when maxSurge is 0"))
|
||||
}
|
||||
|
||||
} else {
|
||||
allErrs = append(allErrs, ValidatePositiveIntOrPercent(rollingUpdate.MaxUnavailable, fldPath.Child("maxUnavailable"))...)
|
||||
if getIntOrPercentValue(rollingUpdate.MaxUnavailable) == 0 {
|
||||
// MaxUnavailable cannot be 0.
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("maxUnavailable"), rollingUpdate.MaxUnavailable, "cannot be 0"))
|
||||
}
|
||||
// Validate that MaxUnavailable is not more than 100%.
|
||||
allErrs = append(allErrs, IsNotMoreThan100Percent(rollingUpdate.MaxUnavailable, fldPath.Child("maxUnavailable"))...)
|
||||
}
|
||||
// Validate that MaxUnavailable is not more than 100%.
|
||||
allErrs = append(allErrs, IsNotMoreThan100Percent(rollingUpdate.MaxUnavailable, fldPath.Child("maxUnavailable"))...)
|
||||
return allErrs
|
||||
}
|
||||
|
||||
|
@@ -2971,3 +2971,182 @@ func TestValidateReplicaSet(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDaemonSetUpdateMaxSurge(t *testing.T) {
|
||||
testCases := map[string]struct {
|
||||
ds *apps.RollingUpdateDaemonSet
|
||||
enableSurge bool
|
||||
expectError bool
|
||||
}{
|
||||
"invalid: unset": {
|
||||
ds: &apps.RollingUpdateDaemonSet{},
|
||||
expectError: true,
|
||||
},
|
||||
"invalid: zero percent": {
|
||||
ds: &apps.RollingUpdateDaemonSet{
|
||||
MaxUnavailable: intstr.FromString("0%"),
|
||||
},
|
||||
expectError: true,
|
||||
},
|
||||
"invalid: zero": {
|
||||
ds: &apps.RollingUpdateDaemonSet{
|
||||
MaxUnavailable: intstr.FromInt(0),
|
||||
},
|
||||
expectError: true,
|
||||
},
|
||||
"valid: one": {
|
||||
ds: &apps.RollingUpdateDaemonSet{
|
||||
MaxUnavailable: intstr.FromInt(1),
|
||||
},
|
||||
},
|
||||
"valid: one percent": {
|
||||
ds: &apps.RollingUpdateDaemonSet{
|
||||
MaxUnavailable: intstr.FromString("1%"),
|
||||
},
|
||||
},
|
||||
"valid: 100%": {
|
||||
ds: &apps.RollingUpdateDaemonSet{
|
||||
MaxUnavailable: intstr.FromString("100%"),
|
||||
},
|
||||
},
|
||||
"invalid: greater than 100%": {
|
||||
ds: &apps.RollingUpdateDaemonSet{
|
||||
MaxUnavailable: intstr.FromString("101%"),
|
||||
},
|
||||
expectError: true,
|
||||
},
|
||||
|
||||
"valid: surge and unavailable set": {
|
||||
ds: &apps.RollingUpdateDaemonSet{
|
||||
MaxUnavailable: intstr.FromString("1%"),
|
||||
MaxSurge: intstr.FromString("1%"),
|
||||
},
|
||||
},
|
||||
|
||||
"invalid: surge enabled, unavailable zero percent": {
|
||||
ds: &apps.RollingUpdateDaemonSet{
|
||||
MaxUnavailable: intstr.FromString("0%"),
|
||||
},
|
||||
enableSurge: true,
|
||||
expectError: true,
|
||||
},
|
||||
"invalid: surge enabled, unavailable zero": {
|
||||
ds: &apps.RollingUpdateDaemonSet{
|
||||
MaxUnavailable: intstr.FromInt(0),
|
||||
},
|
||||
enableSurge: true,
|
||||
expectError: true,
|
||||
},
|
||||
"valid: surge enabled, unavailable one": {
|
||||
ds: &apps.RollingUpdateDaemonSet{
|
||||
MaxUnavailable: intstr.FromInt(1),
|
||||
},
|
||||
enableSurge: true,
|
||||
},
|
||||
"valid: surge enabled, unavailable one percent": {
|
||||
ds: &apps.RollingUpdateDaemonSet{
|
||||
MaxUnavailable: intstr.FromString("1%"),
|
||||
},
|
||||
enableSurge: true,
|
||||
},
|
||||
"valid: surge enabled, unavailable 100%": {
|
||||
ds: &apps.RollingUpdateDaemonSet{
|
||||
MaxUnavailable: intstr.FromString("100%"),
|
||||
},
|
||||
enableSurge: true,
|
||||
},
|
||||
"invalid: surge enabled, unavailable greater than 100%": {
|
||||
ds: &apps.RollingUpdateDaemonSet{
|
||||
MaxUnavailable: intstr.FromString("101%"),
|
||||
},
|
||||
enableSurge: true,
|
||||
expectError: true,
|
||||
},
|
||||
|
||||
"invalid: surge enabled, surge zero percent": {
|
||||
ds: &apps.RollingUpdateDaemonSet{
|
||||
MaxSurge: intstr.FromString("0%"),
|
||||
},
|
||||
enableSurge: true,
|
||||
expectError: true,
|
||||
},
|
||||
"invalid: surge enabled, surge zero": {
|
||||
ds: &apps.RollingUpdateDaemonSet{
|
||||
MaxSurge: intstr.FromInt(0),
|
||||
},
|
||||
enableSurge: true,
|
||||
expectError: true,
|
||||
},
|
||||
"valid: surge enabled, surge one": {
|
||||
ds: &apps.RollingUpdateDaemonSet{
|
||||
MaxSurge: intstr.FromInt(1),
|
||||
},
|
||||
enableSurge: true,
|
||||
},
|
||||
"valid: surge enabled, surge one percent": {
|
||||
ds: &apps.RollingUpdateDaemonSet{
|
||||
MaxSurge: intstr.FromString("1%"),
|
||||
},
|
||||
enableSurge: true,
|
||||
},
|
||||
"valid: surge enabled, surge 100%": {
|
||||
ds: &apps.RollingUpdateDaemonSet{
|
||||
MaxSurge: intstr.FromString("100%"),
|
||||
},
|
||||
enableSurge: true,
|
||||
},
|
||||
"invalid: surge enabled, surge greater than 100%": {
|
||||
ds: &apps.RollingUpdateDaemonSet{
|
||||
MaxSurge: intstr.FromString("101%"),
|
||||
},
|
||||
enableSurge: true,
|
||||
expectError: true,
|
||||
},
|
||||
|
||||
"invalid: surge enabled, surge and unavailable set": {
|
||||
ds: &apps.RollingUpdateDaemonSet{
|
||||
MaxUnavailable: intstr.FromString("1%"),
|
||||
MaxSurge: intstr.FromString("1%"),
|
||||
},
|
||||
enableSurge: true,
|
||||
expectError: true,
|
||||
},
|
||||
|
||||
"invalid: surge enabled, surge and unavailable zero percent": {
|
||||
ds: &apps.RollingUpdateDaemonSet{
|
||||
MaxUnavailable: intstr.FromString("0%"),
|
||||
MaxSurge: intstr.FromString("0%"),
|
||||
},
|
||||
enableSurge: true,
|
||||
expectError: true,
|
||||
},
|
||||
"invalid: surge enabled, surge and unavailable zero": {
|
||||
ds: &apps.RollingUpdateDaemonSet{
|
||||
MaxUnavailable: intstr.FromInt(0),
|
||||
MaxSurge: intstr.FromInt(0),
|
||||
},
|
||||
enableSurge: true,
|
||||
expectError: true,
|
||||
},
|
||||
"invalid: surge enabled, surge and unavailable mixed zero": {
|
||||
ds: &apps.RollingUpdateDaemonSet{
|
||||
MaxUnavailable: intstr.FromInt(0),
|
||||
MaxSurge: intstr.FromString("0%"),
|
||||
},
|
||||
enableSurge: true,
|
||||
expectError: true,
|
||||
},
|
||||
}
|
||||
for tcName, tc := range testCases {
|
||||
t.Run(tcName, func(t *testing.T) {
|
||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DaemonSetUpdateSurge, tc.enableSurge)()
|
||||
errs := ValidateRollingUpdateDaemonSet(tc.ds, field.NewPath("spec", "updateStrategy", "rollingUpdate"))
|
||||
if tc.expectError && len(errs) == 0 {
|
||||
t.Errorf("Unexpected success")
|
||||
}
|
||||
if !tc.expectError && len(errs) != 0 {
|
||||
t.Errorf("Unexpected error(s): %v", errs)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user