Adding implementation of KEP-3335, StatefulSetSlice

This commit is contained in:
Peter Schuurman
2022-09-26 18:34:03 -07:00
parent 2b7b5245ea
commit 7b3d77a41a
37 changed files with 1804 additions and 507 deletions

View File

@@ -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.
@@ -128,6 +130,12 @@ func ValidateStatefulSetSpec(spec *apps.StatefulSetSpec, fldPath *field.Path, op
allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(spec.Replicas), fldPath.Child("replicas"))...)
allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(spec.MinReadySeconds), fldPath.Child("minReadySeconds"))...)
if utilfeature.DefaultFeatureGate.Enabled(features.StatefulSetSlice) {
if spec.Ordinals != nil {
replicaStartOrdinal := spec.Ordinals.Start
allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(replicaStartOrdinal), fldPath.Child("ordinals.start"))...)
}
}
if spec.Selector == nil {
allErrs = append(allErrs, field.Required(fldPath.Child("selector"), ""))
@@ -177,10 +185,17 @@ func ValidateStatefulSetUpdate(statefulSet, oldStatefulSet *apps.StatefulSet, op
newStatefulSetClone.Spec.Template = oldStatefulSet.Spec.Template // +k8s:verify-mutation:reason=clone
newStatefulSetClone.Spec.UpdateStrategy = oldStatefulSet.Spec.UpdateStrategy // +k8s:verify-mutation:reason=clone
newStatefulSetClone.Spec.MinReadySeconds = oldStatefulSet.Spec.MinReadySeconds // +k8s:verify-mutation:reason=clone
if utilfeature.DefaultFeatureGate.Enabled(features.StatefulSetSlice) {
newStatefulSetClone.Spec.Ordinals = oldStatefulSet.Spec.Ordinals // +k8s:verify-mutation:reason=clone
}
newStatefulSetClone.Spec.PersistentVolumeClaimRetentionPolicy = oldStatefulSet.Spec.PersistentVolumeClaimRetentionPolicy // +k8s:verify-mutation:reason=clone
if !apiequality.Semantic.DeepEqual(newStatefulSetClone.Spec, oldStatefulSet.Spec) {
allErrs = append(allErrs, field.Forbidden(field.NewPath("spec"), "updates to statefulset spec for fields other than 'replicas', 'template', 'updateStrategy', 'persistentVolumeClaimRetentionPolicy' and 'minReadySeconds' are forbidden"))
if utilfeature.DefaultFeatureGate.Enabled(features.StatefulSetSlice) {
allErrs = append(allErrs, field.Forbidden(field.NewPath("spec"), "updates to statefulset spec for fields other than 'replicas', 'ordinals', 'template', 'updateStrategy', 'persistentVolumeClaimRetentionPolicy' and 'minReadySeconds' are forbidden"))
} else {
allErrs = append(allErrs, field.Forbidden(field.NewPath("spec"), "updates to statefulset spec for fields other than 'replicas', 'template', 'updateStrategy', 'persistentVolumeClaimRetentionPolicy' and 'minReadySeconds' are forbidden"))
}
}
return allErrs

View File

@@ -86,6 +86,8 @@ func TestValidateStatefulSet(t *testing.T) {
const enableStatefulSetAutoDeletePVC = "[enable StatefulSetAutoDeletePVC]"
const enableStatefulSetSlice = "[enable StatefulSetSlice]"
type testCase struct {
name string
set apps.StatefulSet
@@ -193,6 +195,20 @@ func TestValidateStatefulSet(t *testing.T) {
},
},
},
{
name: "ordinals.start positive value " + enableStatefulSetSlice,
set: apps.StatefulSet{
ObjectMeta: metav1.ObjectMeta{Name: "abc-123", Namespace: metav1.NamespaceDefault},
Spec: apps.StatefulSetSpec{
PodManagementPolicy: apps.ParallelPodManagement,
UpdateStrategy: apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType},
Selector: &metav1.LabelSelector{MatchLabels: validLabels},
Template: validPodTemplate.Template,
Replicas: 3,
Ordinals: &apps.StatefulSetOrdinals{Start: 2},
},
},
},
}
errorCases := []testCase{
@@ -635,6 +651,23 @@ func TestValidateStatefulSet(t *testing.T) {
field.Invalid(field.NewPath("spec", "updateStrategy", "rollingUpdate", "maxUnavailable"), nil, ""),
},
},
{
name: "invalid ordinals.start " + enableStatefulSetSlice,
set: apps.StatefulSet{
ObjectMeta: metav1.ObjectMeta{Name: "abc-123", Namespace: metav1.NamespaceDefault},
Spec: apps.StatefulSetSpec{
PodManagementPolicy: apps.ParallelPodManagement,
UpdateStrategy: apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType},
Selector: &metav1.LabelSelector{MatchLabels: validLabels},
Template: validPodTemplate.Template,
Replicas: 3,
Ordinals: &apps.StatefulSetOrdinals{Start: -2},
},
},
errs: field.ErrorList{
field.Invalid(field.NewPath("spec", "ordinals.start"), nil, ""),
},
},
}
cmpOpts := []cmp.Option{cmpopts.IgnoreFields(field.Error{}, "BadValue", "Detail"), cmpopts.SortSlices(func(a, b *field.Error) bool { return a.Error() < b.Error() })}
@@ -651,6 +684,9 @@ func TestValidateStatefulSet(t *testing.T) {
if strings.Contains(name, enableStatefulSetAutoDeletePVC) {
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.StatefulSetAutoDeletePVC, true)()
}
if strings.Contains(name, enableStatefulSetSlice) {
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.StatefulSetSlice, true)()
}
errs := ValidateStatefulSet(&testCase.set, pod.GetValidationOptionsFromPodTemplate(&testCase.set.Spec.Template, nil))
wantErrs := testCase.errs