Skip to content

Commit

Permalink
Merge pull request #82162 from krmayankk/maxun
Browse files Browse the repository at this point in the history
API: maxUnavailable for StatefulSet
  • Loading branch information
k8s-ci-robot committed Mar 29, 2022
2 parents 874d4bf + 2733b66 commit f85ff4b
Show file tree
Hide file tree
Showing 48 changed files with 1,775 additions and 484 deletions.
6 changes: 5 additions & 1 deletion api/openapi-spec/swagger.json

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

6 changes: 5 additions & 1 deletion api/openapi-spec/v3/apis__apps__v1_openapi.json
Expand Up @@ -746,8 +746,12 @@
"io.k8s.api.apps.v1.RollingUpdateStatefulSetStrategy": {
"description": "RollingUpdateStatefulSetStrategy is used to communicate parameter for RollingUpdateStatefulSetStrategyType.",
"properties": {
"maxUnavailable": {
"$ref": "#/components/schemas/io.k8s.apimachinery.pkg.util.intstr.IntOrString",
"description": "The maximum number of pods that can be unavailable during the update. Value can be an absolute number (ex: 5) or a percentage of desired pods (ex: 10%). Absolute number is calculated from percentage by rounding up. This can not be 0. Defaults to 1. This field is alpha-level and is only honored by servers that enable the MaxUnavailableStatefulSet feature. The field applies to all pods in the range 0 to Replicas-1. That means if there is any unavailable pod in the range 0 to Replicas-1, it will be counted towards MaxUnavailable."
},
"partition": {
"description": "Partition indicates the ordinal at which the StatefulSet should be partitioned. Default value is 0.",
"description": "Partition indicates the ordinal at which the StatefulSet should be partitioned for updates. During a rolling update, all pods from ordinal Replicas-1 to Partition are updated. All pods from ordinal Partition-1 to 0 remain untouched. This is helpful in being able to do a canary based deployment. The default value is 0.",
"format": "int32",
"type": "integer"
}
Expand Down
15 changes: 13 additions & 2 deletions pkg/apis/apps/types.go
Expand Up @@ -92,9 +92,20 @@ const (

// RollingUpdateStatefulSetStrategy is used to communicate parameter for RollingUpdateStatefulSetStrategyType.
type RollingUpdateStatefulSetStrategy struct {
// Partition indicates the ordinal at which the StatefulSet should be
// partitioned.
// Partition indicates the ordinal at which the StatefulSet should be partitioned
// for updates. During a rolling update, all pods from ordinal Replicas-1 to
// Partition are updated. All pods from ordinal Partition-1 to 0 remain untouched.
// This is helpful in being able to do a canary based deployment. The default value is 0.
Partition int32
// The maximum number of pods that can be unavailable during the update.
// Value can be an absolute number (ex: 5) or a percentage of desired pods (ex: 10%).
// Absolute number is calculated from percentage by rounding up. This can not be 0.
// Defaults to 1. This field is alpha-level and is only honored by servers that enable the
// MaxUnavailableStatefulSet feature. The field applies to all pods in the range 0 to
// Replicas-1. That means if there is any unavailable pod in the range 0 to Replicas-1, it
// will be counted towards MaxUnavailable.
// +optional
MaxUnavailable *intstr.IntOrString
}

// PersistentVolumeClaimRetentionPolicyType is a string enumeration of the policies that will determine
Expand Down
16 changes: 12 additions & 4 deletions pkg/apis/apps/v1/defaults.go
Expand Up @@ -113,10 +113,18 @@ func SetDefaults_StatefulSet(obj *appsv1.StatefulSet) {
}

if obj.Spec.UpdateStrategy.Type == appsv1.RollingUpdateStatefulSetStrategyType &&
obj.Spec.UpdateStrategy.RollingUpdate != nil &&
obj.Spec.UpdateStrategy.RollingUpdate.Partition == nil {
obj.Spec.UpdateStrategy.RollingUpdate.Partition = new(int32)
*obj.Spec.UpdateStrategy.RollingUpdate.Partition = 0
obj.Spec.UpdateStrategy.RollingUpdate != nil {

if obj.Spec.UpdateStrategy.RollingUpdate.Partition == nil {
obj.Spec.UpdateStrategy.RollingUpdate.Partition = new(int32)
*obj.Spec.UpdateStrategy.RollingUpdate.Partition = 0
}
if utilfeature.DefaultFeatureGate.Enabled(features.MaxUnavailableStatefulSet) {
if obj.Spec.UpdateStrategy.RollingUpdate.MaxUnavailable == nil {
maxUnavailable := intstr.FromInt(1)
obj.Spec.UpdateStrategy.RollingUpdate.MaxUnavailable = &maxUnavailable
}
}
}

if utilfeature.DefaultFeatureGate.Enabled(features.StatefulSetAutoDeletePVC) {
Expand Down
171 changes: 167 additions & 4 deletions pkg/apis/apps/v1/defaults_test.go
Expand Up @@ -175,6 +175,15 @@ func TestSetDefaultDaemonSetSpec(t *testing.T) {
}
}

func getMaxUnavailable(maxUnavailable int) *intstr.IntOrString {
maxUnavailableIntOrStr := intstr.FromInt(maxUnavailable)
return &maxUnavailableIntOrStr
}

func getPartition(partition int32) *int32 {
return &partition
}

func TestSetDefaultStatefulSet(t *testing.T) {
defaultLabels := map[string]string{"foo": "bar"}
var defaultPartition int32 = 0
Expand All @@ -196,10 +205,11 @@ func TestSetDefaultStatefulSet(t *testing.T) {
}

tests := []struct {
name string
original *appsv1.StatefulSet
expected *appsv1.StatefulSet
enablePVCDeletionPolicy bool
name string
original *appsv1.StatefulSet
expected *appsv1.StatefulSet
enablePVCDeletionPolicy bool
enableMaxUnavailablePolicy bool
}{
{
name: "labels and default update strategy",
Expand Down Expand Up @@ -439,12 +449,165 @@ func TestSetDefaultStatefulSet(t *testing.T) {
},
enablePVCDeletionPolicy: false,
},
{
name: "MaxUnavailable disabled, with maxUnavailable not specified",
original: &appsv1.StatefulSet{
Spec: appsv1.StatefulSetSpec{
Template: defaultTemplate,
},
},
expected: &appsv1.StatefulSet{
ObjectMeta: metav1.ObjectMeta{
Labels: defaultLabels,
},
Spec: appsv1.StatefulSetSpec{
Replicas: &defaultReplicas,
Template: defaultTemplate,
PodManagementPolicy: appsv1.OrderedReadyPodManagement,
UpdateStrategy: appsv1.StatefulSetUpdateStrategy{
Type: appsv1.RollingUpdateStatefulSetStrategyType,
RollingUpdate: &appsv1.RollingUpdateStatefulSetStrategy{
Partition: getPartition(0),
},
},
RevisionHistoryLimit: utilpointer.Int32Ptr(10),
},
},
enableMaxUnavailablePolicy: false,
},
{
name: "MaxUnavailable disabled, with default maxUnavailable specified",
original: &appsv1.StatefulSet{
Spec: appsv1.StatefulSetSpec{
Template: defaultTemplate,
UpdateStrategy: appsv1.StatefulSetUpdateStrategy{
RollingUpdate: &appsv1.RollingUpdateStatefulSetStrategy{
Partition: &defaultPartition,
MaxUnavailable: getMaxUnavailable(1),
},
},
},
},
expected: &appsv1.StatefulSet{
ObjectMeta: metav1.ObjectMeta{
Labels: defaultLabels,
},
Spec: appsv1.StatefulSetSpec{
Replicas: &defaultReplicas,
Template: defaultTemplate,
PodManagementPolicy: appsv1.OrderedReadyPodManagement,
UpdateStrategy: appsv1.StatefulSetUpdateStrategy{
Type: appsv1.RollingUpdateStatefulSetStrategyType,
RollingUpdate: &appsv1.RollingUpdateStatefulSetStrategy{
Partition: getPartition(0),
MaxUnavailable: getMaxUnavailable(1),
},
},
RevisionHistoryLimit: utilpointer.Int32Ptr(10),
},
},
enableMaxUnavailablePolicy: false,
},
{
name: "MaxUnavailable disabled, with non default maxUnavailable specified",
original: &appsv1.StatefulSet{
Spec: appsv1.StatefulSetSpec{
Template: defaultTemplate,
UpdateStrategy: appsv1.StatefulSetUpdateStrategy{
RollingUpdate: &appsv1.RollingUpdateStatefulSetStrategy{
Partition: &notTheDefaultPartition,
MaxUnavailable: getMaxUnavailable(3),
},
},
},
},
expected: &appsv1.StatefulSet{
ObjectMeta: metav1.ObjectMeta{
Labels: defaultLabels,
},
Spec: appsv1.StatefulSetSpec{
Replicas: &defaultReplicas,
Template: defaultTemplate,
PodManagementPolicy: appsv1.OrderedReadyPodManagement,
UpdateStrategy: appsv1.StatefulSetUpdateStrategy{
Type: appsv1.RollingUpdateStatefulSetStrategyType,
RollingUpdate: &appsv1.RollingUpdateStatefulSetStrategy{
Partition: getPartition(42),
MaxUnavailable: getMaxUnavailable(3),
},
},
RevisionHistoryLimit: utilpointer.Int32Ptr(10),
},
},
enableMaxUnavailablePolicy: false,
},
{
name: "MaxUnavailable enabled, with no maxUnavailable specified",
original: &appsv1.StatefulSet{
Spec: appsv1.StatefulSetSpec{
Template: defaultTemplate,
},
},
expected: &appsv1.StatefulSet{
ObjectMeta: metav1.ObjectMeta{
Labels: defaultLabels,
},
Spec: appsv1.StatefulSetSpec{
Replicas: &defaultReplicas,
Template: defaultTemplate,
PodManagementPolicy: appsv1.OrderedReadyPodManagement,
UpdateStrategy: appsv1.StatefulSetUpdateStrategy{
Type: appsv1.RollingUpdateStatefulSetStrategyType,
RollingUpdate: &appsv1.RollingUpdateStatefulSetStrategy{
Partition: getPartition(0),
MaxUnavailable: getMaxUnavailable(1),
},
},
RevisionHistoryLimit: utilpointer.Int32Ptr(10),
},
},
enableMaxUnavailablePolicy: true,
},
{
name: "MaxUnavailable enabled, with non default maxUnavailable specified",
original: &appsv1.StatefulSet{
Spec: appsv1.StatefulSetSpec{
Template: defaultTemplate,
UpdateStrategy: appsv1.StatefulSetUpdateStrategy{
RollingUpdate: &appsv1.RollingUpdateStatefulSetStrategy{
Partition: &notTheDefaultPartition,
MaxUnavailable: getMaxUnavailable(3),
},
},
},
},
expected: &appsv1.StatefulSet{
ObjectMeta: metav1.ObjectMeta{
Labels: defaultLabels,
},
Spec: appsv1.StatefulSetSpec{
Replicas: &defaultReplicas,
Template: defaultTemplate,
PodManagementPolicy: appsv1.OrderedReadyPodManagement,
UpdateStrategy: appsv1.StatefulSetUpdateStrategy{
Type: appsv1.RollingUpdateStatefulSetStrategyType,
RollingUpdate: &appsv1.RollingUpdateStatefulSetStrategy{
Partition: getPartition(42),
MaxUnavailable: getMaxUnavailable(3),
},
},
RevisionHistoryLimit: utilpointer.Int32Ptr(10),
},
},
enableMaxUnavailablePolicy: true,
},
}

for _, test := range tests {
test := test
t.Run(test.name, func(t *testing.T) {
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.StatefulSetAutoDeletePVC, test.enablePVCDeletionPolicy)()
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.MaxUnavailableStatefulSet, test.enableMaxUnavailablePolicy)()

obj2 := roundTrip(t, runtime.Object(test.original))
got, ok := obj2.(*appsv1.StatefulSet)
Expand Down
3 changes: 3 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.

16 changes: 12 additions & 4 deletions pkg/apis/apps/v1beta1/defaults.go
Expand Up @@ -70,10 +70,18 @@ func SetDefaults_StatefulSet(obj *appsv1beta1.StatefulSet) {
*obj.Spec.RevisionHistoryLimit = 10
}
if obj.Spec.UpdateStrategy.Type == appsv1beta1.RollingUpdateStatefulSetStrategyType &&
obj.Spec.UpdateStrategy.RollingUpdate != nil &&
obj.Spec.UpdateStrategy.RollingUpdate.Partition == nil {
obj.Spec.UpdateStrategy.RollingUpdate.Partition = new(int32)
*obj.Spec.UpdateStrategy.RollingUpdate.Partition = 0
obj.Spec.UpdateStrategy.RollingUpdate != nil {

if obj.Spec.UpdateStrategy.RollingUpdate.Partition == nil {
obj.Spec.UpdateStrategy.RollingUpdate.Partition = new(int32)
*obj.Spec.UpdateStrategy.RollingUpdate.Partition = 0
}
if utilfeature.DefaultFeatureGate.Enabled(features.MaxUnavailableStatefulSet) {
if obj.Spec.UpdateStrategy.RollingUpdate.MaxUnavailable == nil {
maxUnavailable := intstr.FromInt(1)
obj.Spec.UpdateStrategy.RollingUpdate.MaxUnavailable = &maxUnavailable
}
}
}
}

Expand Down
3 changes: 3 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.

22 changes: 16 additions & 6 deletions pkg/apis/apps/v1beta2/defaults.go
Expand Up @@ -63,15 +63,25 @@ func SetDefaults_StatefulSet(obj *appsv1beta2.StatefulSet) {
if obj.Spec.UpdateStrategy.Type == "" {
obj.Spec.UpdateStrategy.Type = appsv1beta2.RollingUpdateStatefulSetStrategyType

// UpdateStrategy.RollingUpdate will take default values below.
obj.Spec.UpdateStrategy.RollingUpdate = &appsv1beta2.RollingUpdateStatefulSetStrategy{}
if obj.Spec.UpdateStrategy.RollingUpdate == nil {
// UpdateStrategy.RollingUpdate will take default values below.
obj.Spec.UpdateStrategy.RollingUpdate = &appsv1beta2.RollingUpdateStatefulSetStrategy{}
}
}

if obj.Spec.UpdateStrategy.Type == appsv1beta2.RollingUpdateStatefulSetStrategyType &&
obj.Spec.UpdateStrategy.RollingUpdate != nil &&
obj.Spec.UpdateStrategy.RollingUpdate.Partition == nil {
obj.Spec.UpdateStrategy.RollingUpdate.Partition = new(int32)
*obj.Spec.UpdateStrategy.RollingUpdate.Partition = 0
obj.Spec.UpdateStrategy.RollingUpdate != nil {

if obj.Spec.UpdateStrategy.RollingUpdate.Partition == nil {
obj.Spec.UpdateStrategy.RollingUpdate.Partition = new(int32)
*obj.Spec.UpdateStrategy.RollingUpdate.Partition = 0
}
if utilfeature.DefaultFeatureGate.Enabled(features.MaxUnavailableStatefulSet) {
if obj.Spec.UpdateStrategy.RollingUpdate.MaxUnavailable == nil {
maxUnavailable := intstr.FromInt(1)
obj.Spec.UpdateStrategy.RollingUpdate.MaxUnavailable = &maxUnavailable
}
}
}

if utilfeature.DefaultFeatureGate.Enabled(features.StatefulSetAutoDeletePVC) {
Expand Down

0 comments on commit f85ff4b

Please sign in to comment.