Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

api: Add min ready seconds & availablereplicas for statefulsets #100842

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
10 changes: 10 additions & 0 deletions api/openapi-spec/swagger.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 13 additions & 0 deletions pkg/apis/apps/types.go
Expand Up @@ -157,6 +157,13 @@ type StatefulSetSpec struct {
// consists of all revisions not represented by a currently applied
// StatefulSetSpec version. The default value is 10.
RevisionHistoryLimit *int32

// Minimum number of seconds for which a newly created pod should be ready
// without any of its container crashing for it to be considered available.
// Defaults to 0 (pod will be considered available as soon as it is ready)
// This is an alpha field and requires enabling StatefulSetMinReadySeconds feature gate.
// +optional
MinReadySeconds int32
}

// StatefulSetStatus represents the current state of a StatefulSet.
Expand Down Expand Up @@ -196,6 +203,12 @@ type StatefulSetStatus struct {

// Represents the latest available observations of a statefulset's current state.
Conditions []StatefulSetCondition

// Total number of available pods (ready for at least minReadySeconds) targeted by this statefulset.
// This is an alpha field and requires enabling StatefulSetMinReadySeconds feature gate.
// Remove omitempty when graduating to beta
// +optional
AvailableReplicas int32
}

// StatefulSetConditionType describes the condition types of StatefulSets.
Expand Down
4 changes: 4 additions & 0 deletions pkg/apis/apps/v1/defaults_test.go
Expand Up @@ -208,6 +208,7 @@ func TestSetDefaultStatefulSet(t *testing.T) {
},
Spec: appsv1.StatefulSetSpec{
Replicas: &defaultReplicas,
MinReadySeconds: int32(0),
Template: defaultTemplate,
PodManagementPolicy: appsv1.OrderedReadyPodManagement,
UpdateStrategy: appsv1.StatefulSetUpdateStrategy{
Expand Down Expand Up @@ -235,6 +236,7 @@ func TestSetDefaultStatefulSet(t *testing.T) {
},
Spec: appsv1.StatefulSetSpec{
Replicas: &defaultReplicas,
MinReadySeconds: int32(0),
Template: defaultTemplate,
PodManagementPolicy: appsv1.OrderedReadyPodManagement,
UpdateStrategy: appsv1.StatefulSetUpdateStrategy{
Expand All @@ -257,6 +259,7 @@ func TestSetDefaultStatefulSet(t *testing.T) {
},
Spec: appsv1.StatefulSetSpec{
Replicas: &defaultReplicas,
MinReadySeconds: int32(0),
Template: defaultTemplate,
PodManagementPolicy: appsv1.ParallelPodManagement,
UpdateStrategy: appsv1.StatefulSetUpdateStrategy{
Expand Down Expand Up @@ -286,6 +289,7 @@ func TestSetDefaultStatefulSet(t *testing.T) {
},
Spec: appsv1.StatefulSetSpec{
Replicas: &defaultReplicas,
MinReadySeconds: int32(0),
Template: defaultTemplate,
PodManagementPolicy: appsv1.OrderedReadyPodManagement,
UpdateStrategy: appsv1.StatefulSetUpdateStrategy{
Expand Down
4 changes: 4 additions & 0 deletions pkg/apis/apps/v1/zz_generated.conversion.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion pkg/apis/apps/v1beta1/defaults.go
Expand Up @@ -60,7 +60,6 @@ func SetDefaults_StatefulSet(obj *appsv1beta1.StatefulSet) {
obj.Spec.UpdateStrategy.RollingUpdate.Partition = new(int32)
*obj.Spec.UpdateStrategy.RollingUpdate.Partition = 0
}

}

// SetDefaults_Deployment sets additional defaults compared to its counterpart
Expand Down
4 changes: 4 additions & 0 deletions pkg/apis/apps/v1beta1/zz_generated.conversion.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions pkg/apis/apps/v1beta2/defaults_test.go
Expand Up @@ -207,6 +207,7 @@ func TestSetDefaultStatefulSet(t *testing.T) {
},
Spec: appsv1beta2.StatefulSetSpec{
Replicas: &defaultReplicas,
MinReadySeconds: int32(0),
Template: defaultTemplate,
PodManagementPolicy: appsv1beta2.OrderedReadyPodManagement,
UpdateStrategy: appsv1beta2.StatefulSetUpdateStrategy{
Expand Down Expand Up @@ -234,6 +235,7 @@ func TestSetDefaultStatefulSet(t *testing.T) {
},
Spec: appsv1beta2.StatefulSetSpec{
Replicas: &defaultReplicas,
MinReadySeconds: int32(0),
Template: defaultTemplate,
PodManagementPolicy: appsv1beta2.OrderedReadyPodManagement,
UpdateStrategy: appsv1beta2.StatefulSetUpdateStrategy{
Expand All @@ -256,6 +258,7 @@ func TestSetDefaultStatefulSet(t *testing.T) {
},
Spec: appsv1beta2.StatefulSetSpec{
Replicas: &defaultReplicas,
MinReadySeconds: int32(0),
Template: defaultTemplate,
PodManagementPolicy: appsv1beta2.ParallelPodManagement,
UpdateStrategy: appsv1beta2.StatefulSetUpdateStrategy{
Expand Down
4 changes: 4 additions & 0 deletions pkg/apis/apps/v1beta2/zz_generated.conversion.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

26 changes: 25 additions & 1 deletion pkg/apis/apps/validation/validation.go
Expand Up @@ -109,6 +109,9 @@ func ValidateStatefulSetSpec(spec *apps.StatefulSetSpec, fldPath *field.Path, op
}

allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(spec.Replicas), fldPath.Child("replicas"))...)
if utilfeature.DefaultFeatureGate.Enabled(features.StatefulSetMinReadySeconds) {
allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(spec.MinReadySeconds), fldPath.Child("minReadySeconds"))...)
}
if spec.Selector == nil {
allErrs = append(allErrs, field.Required(fldPath.Child("selector"), ""))
} else {
Expand Down Expand Up @@ -152,11 +155,21 @@ func ValidateStatefulSetUpdate(statefulSet, oldStatefulSet *apps.StatefulSet) fi
newStatefulSetClone.Spec.Replicas = oldStatefulSet.Spec.Replicas // +k8s:verify-mutation:reason=clone
newStatefulSetClone.Spec.Template = oldStatefulSet.Spec.Template // +k8s:verify-mutation:reason=clone
newStatefulSetClone.Spec.UpdateStrategy = oldStatefulSet.Spec.UpdateStrategy // +k8s:verify-mutation:reason=clone
if utilfeature.DefaultFeatureGate.Enabled(features.StatefulSetMinReadySeconds) {
newStatefulSetClone.Spec.MinReadySeconds = oldStatefulSet.Spec.MinReadySeconds // +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', and 'updateStrategy' are forbidden"))
if utilfeature.DefaultFeatureGate.Enabled(features.StatefulSetMinReadySeconds) {
allErrs = append(allErrs, field.Forbidden(field.NewPath("spec"), "updates to statefulset spec for fields other than 'replicas', 'template', 'minReadySeconds' and 'updateStrategy' are forbidden"))
} else {
allErrs = append(allErrs, field.Forbidden(field.NewPath("spec"), "updates to statefulset spec for fields other than 'replicas', 'template' and 'updateStrategy' are forbidden"))
}
}

allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(statefulSet.Spec.Replicas), field.NewPath("spec", "replicas"))...)
if utilfeature.DefaultFeatureGate.Enabled(features.StatefulSetMinReadySeconds) {
allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(statefulSet.Spec.MinReadySeconds), field.NewPath("spec", "minReadySeconds"))...)
}
return allErrs
}

Expand All @@ -168,6 +181,9 @@ func ValidateStatefulSetStatus(status *apps.StatefulSetStatus, fieldPath *field.
allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.ReadyReplicas), fieldPath.Child("readyReplicas"))...)
allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.CurrentReplicas), fieldPath.Child("currentReplicas"))...)
allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.UpdatedReplicas), fieldPath.Child("updatedReplicas"))...)
if utilfeature.DefaultFeatureGate.Enabled(features.StatefulSetMinReadySeconds) {
allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.AvailableReplicas), fieldPath.Child("availableReplicas"))...)
}
if status.ObservedGeneration != nil {
allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(*status.ObservedGeneration), fieldPath.Child("observedGeneration"))...)
}
Expand All @@ -185,6 +201,14 @@ func ValidateStatefulSetStatus(status *apps.StatefulSetStatus, fieldPath *field.
if status.UpdatedReplicas > status.Replicas {
allErrs = append(allErrs, field.Invalid(fieldPath.Child("updatedReplicas"), status.UpdatedReplicas, msg))
}
if utilfeature.DefaultFeatureGate.Enabled(features.StatefulSetMinReadySeconds) {
if status.AvailableReplicas > status.Replicas {
allErrs = append(allErrs, field.Invalid(fieldPath.Child("availableReplicas"), status.AvailableReplicas, msg))
}
if status.AvailableReplicas > status.ReadyReplicas {
allErrs = append(allErrs, field.Invalid(fieldPath.Child("availableReplicas"), status.AvailableReplicas, "cannot be greater than readyReplicas"))
}
}

return allErrs
}
Expand Down