From e4d34057b65c6f13bbae73264f14999e87804e2a Mon Sep 17 00:00:00 2001 From: Liang Deng <283304489@qq.com> Date: Wed, 7 Aug 2024 15:18:47 +0800 Subject: [PATCH] feat: Supports upper-layer modification of the InstanceSet's UpdateStrategy Signed-off-by: Liang Deng <283304489@qq.com> --- apis/apps/v1alpha1/cluster_types.go | 24 +++-- apis/apps/v1alpha1/component_types.go | 7 ++ apis/apps/v1alpha1/zz_generated.deepcopy.go | 27 ++++- apis/workloads/v1alpha1/instanceset_types.go | 74 ++++++++++--- .../v1alpha1/zz_generated.deepcopy.go | 55 +++++++++- .../bases/apps.kubeblocks.io_clusters.yaml | 100 +++++++++++++----- .../bases/apps.kubeblocks.io_components.yaml | 37 +++++++ .../workloads.kubeblocks.io_instancesets.yaml | 39 ++++--- .../apps/transformer_cluster_component.go | 1 + .../apps/transformer_component_workload.go | 23 +--- .../crds/apps.kubeblocks.io_clusters.yaml | 100 +++++++++++++----- .../crds/apps.kubeblocks.io_components.yaml | 37 +++++++ .../workloads.kubeblocks.io_instancesets.yaml | 39 ++++--- pkg/controller/builder/builder_component.go | 5 + .../builder/builder_instance_set.go | 8 +- .../builder/builder_instance_set_test.go | 12 +-- pkg/controller/component/component.go | 1 + pkg/controller/component/its_convertor.go | 24 ++--- .../component/synthesize_component.go | 1 + pkg/controller/component/type.go | 1 + pkg/controller/factory/builder_test.go | 8 +- .../instanceset/reconciler_update.go | 8 +- .../instanceset/reconciler_update_test.go | 10 +- .../instanceset/revision_util_test.go | 4 +- pkg/controller/instanceset/update_plan.go | 6 +- .../instanceset/update_plan_test.go | 10 +- pkg/testutil/apps/instance_set_factoy.go | 5 +- 27 files changed, 471 insertions(+), 195 deletions(-) diff --git a/apis/apps/v1alpha1/cluster_types.go b/apis/apps/v1alpha1/cluster_types.go index 6de87bef8045..f5212865b4c1 100644 --- a/apis/apps/v1alpha1/cluster_types.go +++ b/apis/apps/v1alpha1/cluster_types.go @@ -394,6 +394,19 @@ type UserResourceRefs struct { ConfigMapRefs []ConfigMapRef `json:"configMapRefs,omitempty"` } +// InstanceUpdateStrategy indicates the strategy that the InstanceSet +// controller will use to perform updates. It includes any additional parameters +// necessary to perform the update for the indicated strategy. +type InstanceUpdateStrategy struct { + // Type indicates the type of the InstanceUpdateStrategy. + // Default is RollingUpdate. + // +optional + Type workloads.InstanceSetUpdateStrategyType `json:"type,omitempty"` + // RollingUpdate is used to communicate parameters when Type is RollingUpdateStatefulSetStrategyType. + // +optional + RollingUpdate *workloads.RollingUpdateStrategy `json:"rollingUpdate,omitempty"` +} + // InstanceTemplate allows customization of individual replica configurations in a Component. type InstanceTemplate struct { // Name specifies the unique name of the instance Pod created using this InstanceTemplate. @@ -790,15 +803,12 @@ type ClusterComponentSpec struct { // +optional ServiceAccountName string `json:"serviceAccountName,omitempty"` - // Defines the update strategy for the Component. + // Indicates the InstanceUpdateStrategy that will be + // employed to update Pods in the InstanceSet when a revision is made to + // Template. // - // Deprecated since v0.9. - // This field is maintained for backward compatibility and its use is discouraged. - // Existing usage should be updated to the current preferred approach to avoid compatibility issues in future releases. - // - // +kubebuilder:deprecatedversion:warning="This field has been deprecated since 0.9.0" // +optional - UpdateStrategy *UpdateStrategy `json:"updateStrategy,omitempty"` + InstanceUpdateStrategy InstanceUpdateStrategy `json:"instanceUpdateStrategy,omitempty"` // Controls the concurrency of pods during initial scale up, when replacing pods on nodes, // or when scaling down. It only used when `PodManagementPolicy` is set to `Parallel`. diff --git a/apis/apps/v1alpha1/component_types.go b/apis/apps/v1alpha1/component_types.go index f90aa925e01d..b2b0effa8b71 100644 --- a/apis/apps/v1alpha1/component_types.go +++ b/apis/apps/v1alpha1/component_types.go @@ -163,6 +163,13 @@ type ComponentSpec struct { // +optional ServiceAccountName string `json:"serviceAccountName,omitempty"` + // Indicates the InstanceUpdateStrategy that will be + // employed to update Pods in the InstanceSet when a revision is made to + // Template. + // + // +optional + InstanceUpdateStrategy InstanceUpdateStrategy `json:"instanceUpdateStrategy,omitempty"` + // Controls the concurrency of pods during initial scale up, when replacing pods on nodes, // or when scaling down. It only used when `PodManagementPolicy` is set to `Parallel`. // The default Concurrency is 100%. diff --git a/apis/apps/v1alpha1/zz_generated.deepcopy.go b/apis/apps/v1alpha1/zz_generated.deepcopy.go index 5083370bbb77..91985d1db767 100644 --- a/apis/apps/v1alpha1/zz_generated.deepcopy.go +++ b/apis/apps/v1alpha1/zz_generated.deepcopy.go @@ -585,11 +585,7 @@ func (in *ClusterComponentSpec) DeepCopyInto(out *ClusterComponentSpec) { *out = new(Issuer) (*in).DeepCopyInto(*out) } - if in.UpdateStrategy != nil { - in, out := &in.UpdateStrategy, &out.UpdateStrategy - *out = new(UpdateStrategy) - **out = **in - } + in.InstanceUpdateStrategy.DeepCopyInto(&out.InstanceUpdateStrategy) if in.ParallelPodManagementConcurrency != nil { in, out := &in.ParallelPodManagementConcurrency, &out.ParallelPodManagementConcurrency *out = new(intstr.IntOrString) @@ -1788,6 +1784,7 @@ func (in *ComponentSpec) DeepCopyInto(out *ComponentSpec) { *out = make([]string, len(*in)) copy(*out, *in) } + in.InstanceUpdateStrategy.DeepCopyInto(&out.InstanceUpdateStrategy) if in.ParallelPodManagementConcurrency != nil { in, out := &in.ParallelPodManagementConcurrency, &out.ParallelPodManagementConcurrency *out = new(intstr.IntOrString) @@ -3249,6 +3246,26 @@ func (in *InstanceTemplate) DeepCopy() *InstanceTemplate { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *InstanceUpdateStrategy) DeepCopyInto(out *InstanceUpdateStrategy) { + *out = *in + if in.RollingUpdate != nil { + in, out := &in.RollingUpdate, &out.RollingUpdate + *out = new(workloadsv1alpha1.RollingUpdateStrategy) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InstanceUpdateStrategy. +func (in *InstanceUpdateStrategy) DeepCopy() *InstanceUpdateStrategy { + if in == nil { + return nil + } + out := new(InstanceUpdateStrategy) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *InstanceVolumeClaimTemplate) DeepCopyInto(out *InstanceVolumeClaimTemplate) { *out = *in diff --git a/apis/workloads/v1alpha1/instanceset_types.go b/apis/workloads/v1alpha1/instanceset_types.go index c10cb08f8462..a75cff6c6c24 100644 --- a/apis/workloads/v1alpha1/instanceset_types.go +++ b/apis/workloads/v1alpha1/instanceset_types.go @@ -75,6 +75,66 @@ type SchedulingPolicy struct { TopologySpreadConstraints []corev1.TopologySpreadConstraint `json:"topologySpreadConstraints,omitempty"` } +// InstanceSetUpdateStrategyType is a string enumeration type that enumerates +// all possible update strategies for the InstanceSet controller. +// +enum +type InstanceSetUpdateStrategyType string + +const ( + // RollingUpdateInstanceSetStrategyType indicates that update will be + // applied to all Pods in the InstanceSet with respect to the InstanceSet + // ordering constraints. When a scale operation is performed with this + // strategy, new Pods will be created from the specification version indicated + // by the InstanceSet's updateRevision. + RollingUpdateInstanceSetStrategyType InstanceSetUpdateStrategyType = "RollingUpdate" + // OnDeleteInstanceSetStrategyType triggers the legacy behavior. Version + // tracking and ordered rolling restarts are disabled. Pods are recreated + // when they are manually deleted. When a scale + // operation is performed with this strategy, specification version indicated + // by the InstanceSet's currentRevision. + OnDeleteInstanceSetStrategyType InstanceSetUpdateStrategyType = "OnDelete" +) + +// InstanceUpdateStrategy indicates the strategy that the InstanceSet +// controller will use to perform updates. It includes any additional parameters +// necessary to perform the update for the indicated strategy. +type InstanceUpdateStrategy struct { + // Type indicates the type of the InstanceUpdateStrategy. + // Default is RollingUpdate. + // +optional + Type InstanceSetUpdateStrategyType `json:"type,omitempty"` + // RollingUpdate is used to communicate parameters when Type is RollingUpdateStatefulSetStrategyType. + // +optional + RollingUpdate *RollingUpdateStrategy `json:"rollingUpdate,omitempty"` + // Members(Pods) update strategy. + // + // - serial: update Members one by one that guarantee minimum component unavailable time. + // - bestEffortParallel: update Members in parallel that guarantee minimum component un-writable time. + // - parallel: force parallel + // + // +kubebuilder:validation:Enum={Serial,BestEffortParallel,Parallel} + // +optional + MemberUpdateStrategy *MemberUpdateStrategy `json:"memberUpdateStrategy,omitempty"` +} + +// RollingUpdateStrategy is used to communicate parameter for RollingUpdateInstanceSetStrategyType. +type RollingUpdateStrategy struct { + // Partition indicates the ordinal at which the InstanceSet 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. + // +optional + Partition *int32 `json:"partition,omitempty"` + // 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. 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 `json:"maxUnavailable,omitempty"` +} + // Range represents a range with a start and an end value. // It is used to define a continuous segment. type Range struct { @@ -326,10 +386,10 @@ type InstanceSetSpec struct { // Indicates the StatefulSetUpdateStrategy that will be // employed to update Pods in the InstanceSet when a revision is made to // Template. - // UpdateStrategy.Type will be set to appsv1.OnDeleteStatefulSetStrategyType if MemberUpdateStrategy is not nil + // UpdateStrategy.Type will be set to OnDeleteInstanceSetStrategyType if UpdateStrategy.MemberUpdateStrategy is not nil // // Note: This field will be removed in future version. - UpdateStrategy appsv1.StatefulSetUpdateStrategy `json:"updateStrategy,omitempty"` + UpdateStrategy InstanceUpdateStrategy `json:"updateStrategy,omitempty"` // A list of roles defined in the system. // @@ -346,16 +406,6 @@ type InstanceSetSpec struct { // +optional MembershipReconfiguration *MembershipReconfiguration `json:"membershipReconfiguration,omitempty"` - // Members(Pods) update strategy. - // - // - serial: update Members one by one that guarantee minimum component unavailable time. - // - bestEffortParallel: update Members in parallel that guarantee minimum component un-writable time. - // - parallel: force parallel - // - // +kubebuilder:validation:Enum={Serial,BestEffortParallel,Parallel} - // +optional - MemberUpdateStrategy *MemberUpdateStrategy `json:"memberUpdateStrategy,omitempty"` - // Indicates that the InstanceSet is paused, meaning the reconciliation of this InstanceSet object will be paused. // +optional Paused bool `json:"paused,omitempty"` diff --git a/apis/workloads/v1alpha1/zz_generated.deepcopy.go b/apis/workloads/v1alpha1/zz_generated.deepcopy.go index e8f6d3babb61..ae555f5e0315 100644 --- a/apis/workloads/v1alpha1/zz_generated.deepcopy.go +++ b/apis/workloads/v1alpha1/zz_generated.deepcopy.go @@ -211,11 +211,6 @@ func (in *InstanceSetSpec) DeepCopyInto(out *InstanceSetSpec) { *out = new(MembershipReconfiguration) (*in).DeepCopyInto(*out) } - if in.MemberUpdateStrategy != nil { - in, out := &in.MemberUpdateStrategy, &out.MemberUpdateStrategy - *out = new(MemberUpdateStrategy) - **out = **in - } if in.Credential != nil { in, out := &in.Credential, &out.Credential *out = new(Credential) @@ -374,6 +369,31 @@ func (in *InstanceTemplateStatus) DeepCopy() *InstanceTemplateStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *InstanceUpdateStrategy) DeepCopyInto(out *InstanceUpdateStrategy) { + *out = *in + if in.RollingUpdate != nil { + in, out := &in.RollingUpdate, &out.RollingUpdate + *out = new(RollingUpdateStrategy) + (*in).DeepCopyInto(*out) + } + if in.MemberUpdateStrategy != nil { + in, out := &in.MemberUpdateStrategy, &out.MemberUpdateStrategy + *out = new(MemberUpdateStrategy) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InstanceUpdateStrategy. +func (in *InstanceUpdateStrategy) DeepCopy() *InstanceUpdateStrategy { + if in == nil { + return nil + } + out := new(InstanceUpdateStrategy) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *MemberStatus) DeepCopyInto(out *MemberStatus) { *out = *in @@ -516,6 +536,31 @@ func (in *RoleProbe) DeepCopy() *RoleProbe { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RollingUpdateStrategy) DeepCopyInto(out *RollingUpdateStrategy) { + *out = *in + if in.Partition != nil { + in, out := &in.Partition, &out.Partition + *out = new(int32) + **out = **in + } + if in.MaxUnavailable != nil { + in, out := &in.MaxUnavailable, &out.MaxUnavailable + *out = new(intstr.IntOrString) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RollingUpdateStrategy. +func (in *RollingUpdateStrategy) DeepCopy() *RollingUpdateStrategy { + if in == nil { + return nil + } + out := new(RollingUpdateStrategy) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *SchedulingPolicy) DeepCopyInto(out *SchedulingPolicy) { *out = *in diff --git a/config/crd/bases/apps.kubeblocks.io_clusters.yaml b/config/crd/bases/apps.kubeblocks.io_clusters.yaml index 0abe99e9e9af..8a991d582593 100644 --- a/config/crd/bases/apps.kubeblocks.io_clusters.yaml +++ b/config/crd/bases/apps.kubeblocks.io_clusters.yaml @@ -655,6 +655,43 @@ spec: - name type: object type: array + instanceUpdateStrategy: + description: |- + Indicates the InstanceUpdateStrategy that will be + employed to update Pods in the InstanceSet when a revision is made to + Template. + properties: + rollingUpdate: + description: RollingUpdate is used to communicate parameters + when Type is RollingUpdateStatefulSetStrategyType. + properties: + maxUnavailable: + anyOf: + - type: integer + - type: string + 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. 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. + x-kubernetes-int-or-string: true + partition: + description: |- + Partition indicates the ordinal at which the InstanceSet 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 + type: object + type: + description: |- + Type indicates the type of the InstanceUpdateStrategy. + Default is RollingUpdate. + type: string + type: object instances: description: |- Allows for the customization of configuration values for each instance within a Component. @@ -5379,19 +5416,6 @@ spec: type: object type: array x-kubernetes-preserve-unknown-fields: true - updateStrategy: - description: |- - Defines the update strategy for the Component. - - - Deprecated since v0.9. - This field is maintained for backward compatibility and its use is discouraged. - Existing usage should be updated to the current preferred approach to avoid compatibility issues in future releases. - enum: - - Serial - - BestEffortParallel - - Parallel - type: string userResourceRefs: description: |- Allows users to specify custom ConfigMaps and Secrets to be mounted as volumes @@ -9293,6 +9317,43 @@ spec: - name type: object type: array + instanceUpdateStrategy: + description: |- + Indicates the InstanceUpdateStrategy that will be + employed to update Pods in the InstanceSet when a revision is made to + Template. + properties: + rollingUpdate: + description: RollingUpdate is used to communicate parameters + when Type is RollingUpdateStatefulSetStrategyType. + properties: + maxUnavailable: + anyOf: + - type: integer + - type: string + 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. 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. + x-kubernetes-int-or-string: true + partition: + description: |- + Partition indicates the ordinal at which the InstanceSet 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 + type: object + type: + description: |- + Type indicates the type of the InstanceUpdateStrategy. + Default is RollingUpdate. + type: string + type: object instances: description: |- Allows for the customization of configuration values for each instance within a Component. @@ -14080,19 +14141,6 @@ spec: type: object type: array x-kubernetes-preserve-unknown-fields: true - updateStrategy: - description: |- - Defines the update strategy for the Component. - - - Deprecated since v0.9. - This field is maintained for backward compatibility and its use is discouraged. - Existing usage should be updated to the current preferred approach to avoid compatibility issues in future releases. - enum: - - Serial - - BestEffortParallel - - Parallel - type: string userResourceRefs: description: |- Allows users to specify custom ConfigMaps and Secrets to be mounted as volumes diff --git a/config/crd/bases/apps.kubeblocks.io_components.yaml b/config/crd/bases/apps.kubeblocks.io_components.yaml index f34bcbc6a439..d5bb1d02879d 100644 --- a/config/crd/bases/apps.kubeblocks.io_components.yaml +++ b/config/crd/bases/apps.kubeblocks.io_components.yaml @@ -401,6 +401,43 @@ spec: - name type: object type: array + instanceUpdateStrategy: + description: |- + Indicates the InstanceUpdateStrategy that will be + employed to update Pods in the InstanceSet when a revision is made to + Template. + properties: + rollingUpdate: + description: RollingUpdate is used to communicate parameters when + Type is RollingUpdateStatefulSetStrategyType. + properties: + maxUnavailable: + anyOf: + - type: integer + - type: string + 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. 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. + x-kubernetes-int-or-string: true + partition: + description: |- + Partition indicates the ordinal at which the InstanceSet 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 + type: object + type: + description: |- + Type indicates the type of the InstanceUpdateStrategy. + Default is RollingUpdate. + type: string + type: object instances: description: |- Allows for the customization of configuration values for each instance within a Component. diff --git a/config/crd/bases/workloads.kubeblocks.io_instancesets.yaml b/config/crd/bases/workloads.kubeblocks.io_instancesets.yaml index 3e86dca55a8d..335c5fd3c3ba 100644 --- a/config/crd/bases/workloads.kubeblocks.io_instancesets.yaml +++ b/config/crd/bases/workloads.kubeblocks.io_instancesets.yaml @@ -3714,19 +3714,6 @@ spec: x-kubernetes-list-map-keys: - name x-kubernetes-list-type: map - memberUpdateStrategy: - description: |- - Members(Pods) update strategy. - - - - serial: update Members one by one that guarantee minimum component unavailable time. - - bestEffortParallel: update Members in parallel that guarantee minimum component un-writable time. - - parallel: force parallel - enum: - - Serial - - BestEffortParallel - - Parallel - type: string membershipReconfiguration: description: Provides actions to do membership dynamic reconfiguration. properties: @@ -11883,11 +11870,24 @@ spec: Indicates the StatefulSetUpdateStrategy that will be employed to update Pods in the InstanceSet when a revision is made to Template. - UpdateStrategy.Type will be set to appsv1.OnDeleteStatefulSetStrategyType if MemberUpdateStrategy is not nil + UpdateStrategy.Type will be set to OnDeleteInstanceSetStrategyType if UpdateStrategy.MemberUpdateStrategy is not nil Note: This field will be removed in future version. properties: + memberUpdateStrategy: + description: |- + Members(Pods) update strategy. + + + - serial: update Members one by one that guarantee minimum component unavailable time. + - bestEffortParallel: update Members in parallel that guarantee minimum component un-writable time. + - parallel: force parallel + enum: + - Serial + - BestEffortParallel + - Parallel + type: string rollingUpdate: description: RollingUpdate is used to communicate parameters when Type is RollingUpdateStatefulSetStrategyType. @@ -11900,14 +11900,13 @@ spec: 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. + Defaults to 1. 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. x-kubernetes-int-or-string: true partition: description: |- - Partition indicates the ordinal at which the StatefulSet should be partitioned + Partition indicates the ordinal at which the InstanceSet 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. @@ -11916,7 +11915,7 @@ spec: type: object type: description: |- - Type indicates the type of the StatefulSetUpdateStrategy. + Type indicates the type of the InstanceUpdateStrategy. Default is RollingUpdate. type: string type: object diff --git a/controllers/apps/transformer_cluster_component.go b/controllers/apps/transformer_cluster_component.go index bbdd59980750..a80e13867675 100644 --- a/controllers/apps/transformer_cluster_component.go +++ b/controllers/apps/transformer_cluster_component.go @@ -218,6 +218,7 @@ func copyAndMergeComponent(oldCompObj, newCompObj *appsv1alpha1.Component) *apps // compObjCopy.Spec.Monitor = compProto.Spec.Monitor compObjCopy.Spec.EnabledLogs = compProto.Spec.EnabledLogs compObjCopy.Spec.ServiceAccountName = compProto.Spec.ServiceAccountName + compObjCopy.Spec.InstanceUpdateStrategy = compProto.Spec.InstanceUpdateStrategy compObjCopy.Spec.ParallelPodManagementConcurrency = compProto.Spec.ParallelPodManagementConcurrency compObjCopy.Spec.PodUpdatePolicy = compProto.Spec.PodUpdatePolicy compObjCopy.Spec.Affinity = compProto.Spec.Affinity diff --git a/controllers/apps/transformer_component_workload.go b/controllers/apps/transformer_component_workload.go index 98890ba9d1c4..0ec9aa040b6a 100644 --- a/controllers/apps/transformer_component_workload.go +++ b/controllers/apps/transformer_component_workload.go @@ -31,7 +31,6 @@ import ( corev1 "k8s.io/api/core/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/types" - "k8s.io/apimachinery/pkg/util/intstr" "k8s.io/apimachinery/pkg/util/sets" "sigs.k8s.io/controller-runtime/pkg/client" @@ -263,22 +262,6 @@ func copyAndMergeITS(oldITS, newITS *workloads.InstanceSet, synthesizeComp *comp } } - updateUpdateStrategy := func(itsObj, itsProto *workloads.InstanceSet) { - var objMaxUnavailable *intstr.IntOrString - if itsObj.Spec.UpdateStrategy.RollingUpdate != nil { - objMaxUnavailable = itsObj.Spec.UpdateStrategy.RollingUpdate.MaxUnavailable - } - itsObj.Spec.UpdateStrategy = itsProto.Spec.UpdateStrategy - if objMaxUnavailable == nil && itsObj.Spec.UpdateStrategy.RollingUpdate != nil { - // HACK: This field is alpha-level (since v1.24) and is only honored by servers that enable the - // MaxUnavailableStatefulSet feature. - // When we get a nil MaxUnavailable from k8s, we consider that the field is not supported by the server, - // and set the MaxUnavailable as nil explicitly to avoid the workload been updated unexpectedly. - // Ref: https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#maximum-unavailable-pods - itsObj.Spec.UpdateStrategy.RollingUpdate.MaxUnavailable = nil - } - } - // be compatible with existed cluster updateService := func(itsObj, itsProto *workloads.InstanceSet) *corev1.Service { if itsProto.Spec.Service != nil { @@ -324,7 +307,6 @@ func copyAndMergeITS(oldITS, newITS *workloads.InstanceSet, synthesizeComp *comp itsObjCopy.Spec.Roles = itsProto.Spec.Roles itsObjCopy.Spec.RoleProbe = itsProto.Spec.RoleProbe itsObjCopy.Spec.MembershipReconfiguration = itsProto.Spec.MembershipReconfiguration - itsObjCopy.Spec.MemberUpdateStrategy = itsProto.Spec.MemberUpdateStrategy itsObjCopy.Spec.Credential = itsProto.Spec.Credential itsObjCopy.Spec.Instances = itsProto.Spec.Instances itsObjCopy.Spec.OfflineInstances = itsProto.Spec.OfflineInstances @@ -332,10 +314,7 @@ func copyAndMergeITS(oldITS, newITS *workloads.InstanceSet, synthesizeComp *comp itsObjCopy.Spec.VolumeClaimTemplates = itsProto.Spec.VolumeClaimTemplates itsObjCopy.Spec.ParallelPodManagementConcurrency = itsProto.Spec.ParallelPodManagementConcurrency itsObjCopy.Spec.PodUpdatePolicy = itsProto.Spec.PodUpdatePolicy - - if itsProto.Spec.UpdateStrategy.Type != "" || itsProto.Spec.UpdateStrategy.RollingUpdate != nil { - updateUpdateStrategy(itsObjCopy, itsProto) - } + itsObjCopy.Spec.UpdateStrategy = itsProto.Spec.UpdateStrategy intctrlutil.ResolvePodSpecDefaultFields(oldITS.Spec.Template.Spec, &itsObjCopy.Spec.Template.Spec) delayUpdateInstanceSetSystemFields(oldITS.Spec, &itsObjCopy.Spec) diff --git a/deploy/helm/crds/apps.kubeblocks.io_clusters.yaml b/deploy/helm/crds/apps.kubeblocks.io_clusters.yaml index 0abe99e9e9af..8a991d582593 100644 --- a/deploy/helm/crds/apps.kubeblocks.io_clusters.yaml +++ b/deploy/helm/crds/apps.kubeblocks.io_clusters.yaml @@ -655,6 +655,43 @@ spec: - name type: object type: array + instanceUpdateStrategy: + description: |- + Indicates the InstanceUpdateStrategy that will be + employed to update Pods in the InstanceSet when a revision is made to + Template. + properties: + rollingUpdate: + description: RollingUpdate is used to communicate parameters + when Type is RollingUpdateStatefulSetStrategyType. + properties: + maxUnavailable: + anyOf: + - type: integer + - type: string + 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. 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. + x-kubernetes-int-or-string: true + partition: + description: |- + Partition indicates the ordinal at which the InstanceSet 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 + type: object + type: + description: |- + Type indicates the type of the InstanceUpdateStrategy. + Default is RollingUpdate. + type: string + type: object instances: description: |- Allows for the customization of configuration values for each instance within a Component. @@ -5379,19 +5416,6 @@ spec: type: object type: array x-kubernetes-preserve-unknown-fields: true - updateStrategy: - description: |- - Defines the update strategy for the Component. - - - Deprecated since v0.9. - This field is maintained for backward compatibility and its use is discouraged. - Existing usage should be updated to the current preferred approach to avoid compatibility issues in future releases. - enum: - - Serial - - BestEffortParallel - - Parallel - type: string userResourceRefs: description: |- Allows users to specify custom ConfigMaps and Secrets to be mounted as volumes @@ -9293,6 +9317,43 @@ spec: - name type: object type: array + instanceUpdateStrategy: + description: |- + Indicates the InstanceUpdateStrategy that will be + employed to update Pods in the InstanceSet when a revision is made to + Template. + properties: + rollingUpdate: + description: RollingUpdate is used to communicate parameters + when Type is RollingUpdateStatefulSetStrategyType. + properties: + maxUnavailable: + anyOf: + - type: integer + - type: string + 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. 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. + x-kubernetes-int-or-string: true + partition: + description: |- + Partition indicates the ordinal at which the InstanceSet 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 + type: object + type: + description: |- + Type indicates the type of the InstanceUpdateStrategy. + Default is RollingUpdate. + type: string + type: object instances: description: |- Allows for the customization of configuration values for each instance within a Component. @@ -14080,19 +14141,6 @@ spec: type: object type: array x-kubernetes-preserve-unknown-fields: true - updateStrategy: - description: |- - Defines the update strategy for the Component. - - - Deprecated since v0.9. - This field is maintained for backward compatibility and its use is discouraged. - Existing usage should be updated to the current preferred approach to avoid compatibility issues in future releases. - enum: - - Serial - - BestEffortParallel - - Parallel - type: string userResourceRefs: description: |- Allows users to specify custom ConfigMaps and Secrets to be mounted as volumes diff --git a/deploy/helm/crds/apps.kubeblocks.io_components.yaml b/deploy/helm/crds/apps.kubeblocks.io_components.yaml index f34bcbc6a439..d5bb1d02879d 100644 --- a/deploy/helm/crds/apps.kubeblocks.io_components.yaml +++ b/deploy/helm/crds/apps.kubeblocks.io_components.yaml @@ -401,6 +401,43 @@ spec: - name type: object type: array + instanceUpdateStrategy: + description: |- + Indicates the InstanceUpdateStrategy that will be + employed to update Pods in the InstanceSet when a revision is made to + Template. + properties: + rollingUpdate: + description: RollingUpdate is used to communicate parameters when + Type is RollingUpdateStatefulSetStrategyType. + properties: + maxUnavailable: + anyOf: + - type: integer + - type: string + 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. 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. + x-kubernetes-int-or-string: true + partition: + description: |- + Partition indicates the ordinal at which the InstanceSet 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 + type: object + type: + description: |- + Type indicates the type of the InstanceUpdateStrategy. + Default is RollingUpdate. + type: string + type: object instances: description: |- Allows for the customization of configuration values for each instance within a Component. diff --git a/deploy/helm/crds/workloads.kubeblocks.io_instancesets.yaml b/deploy/helm/crds/workloads.kubeblocks.io_instancesets.yaml index 3e86dca55a8d..335c5fd3c3ba 100644 --- a/deploy/helm/crds/workloads.kubeblocks.io_instancesets.yaml +++ b/deploy/helm/crds/workloads.kubeblocks.io_instancesets.yaml @@ -3714,19 +3714,6 @@ spec: x-kubernetes-list-map-keys: - name x-kubernetes-list-type: map - memberUpdateStrategy: - description: |- - Members(Pods) update strategy. - - - - serial: update Members one by one that guarantee minimum component unavailable time. - - bestEffortParallel: update Members in parallel that guarantee minimum component un-writable time. - - parallel: force parallel - enum: - - Serial - - BestEffortParallel - - Parallel - type: string membershipReconfiguration: description: Provides actions to do membership dynamic reconfiguration. properties: @@ -11883,11 +11870,24 @@ spec: Indicates the StatefulSetUpdateStrategy that will be employed to update Pods in the InstanceSet when a revision is made to Template. - UpdateStrategy.Type will be set to appsv1.OnDeleteStatefulSetStrategyType if MemberUpdateStrategy is not nil + UpdateStrategy.Type will be set to OnDeleteInstanceSetStrategyType if UpdateStrategy.MemberUpdateStrategy is not nil Note: This field will be removed in future version. properties: + memberUpdateStrategy: + description: |- + Members(Pods) update strategy. + + + - serial: update Members one by one that guarantee minimum component unavailable time. + - bestEffortParallel: update Members in parallel that guarantee minimum component un-writable time. + - parallel: force parallel + enum: + - Serial + - BestEffortParallel + - Parallel + type: string rollingUpdate: description: RollingUpdate is used to communicate parameters when Type is RollingUpdateStatefulSetStrategyType. @@ -11900,14 +11900,13 @@ spec: 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. + Defaults to 1. 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. x-kubernetes-int-or-string: true partition: description: |- - Partition indicates the ordinal at which the StatefulSet should be partitioned + Partition indicates the ordinal at which the InstanceSet 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. @@ -11916,7 +11915,7 @@ spec: type: object type: description: |- - Type indicates the type of the StatefulSetUpdateStrategy. + Type indicates the type of the InstanceUpdateStrategy. Default is RollingUpdate. type: string type: object diff --git a/pkg/controller/builder/builder_component.go b/pkg/controller/builder/builder_component.go index a4be30cc1112..2151ee7ebaaf 100644 --- a/pkg/controller/builder/builder_component.go +++ b/pkg/controller/builder/builder_component.go @@ -82,6 +82,11 @@ func (builder *ComponentBuilder) SetServiceAccountName(serviceAccountName string return builder } +func (builder *ComponentBuilder) SetInstanceUpdateStrategy(instanceUpdateStrategy appsv1alpha1.InstanceUpdateStrategy) *ComponentBuilder { + builder.get().Spec.InstanceUpdateStrategy = instanceUpdateStrategy + return builder +} + func (builder *ComponentBuilder) SetParallelPodManagementConcurrency(parallelPodManagementConcurrency *intstr.IntOrString) *ComponentBuilder { builder.get().Spec.ParallelPodManagementConcurrency = parallelPodManagementConcurrency return builder diff --git a/pkg/controller/builder/builder_instance_set.go b/pkg/controller/builder/builder_instance_set.go index d05696011a88..ab77dab43860 100644 --- a/pkg/controller/builder/builder_instance_set.go +++ b/pkg/controller/builder/builder_instance_set.go @@ -118,12 +118,12 @@ func (builder *InstanceSetBuilder) SetPodUpdatePolicy(policy workloads.PodUpdate return builder } -func (builder *InstanceSetBuilder) SetUpdateStrategy(strategy apps.StatefulSetUpdateStrategy) *InstanceSetBuilder { +func (builder *InstanceSetBuilder) SetUpdateStrategy(strategy workloads.InstanceUpdateStrategy) *InstanceSetBuilder { builder.get().Spec.UpdateStrategy = strategy return builder } -func (builder *InstanceSetBuilder) SetUpdateStrategyType(strategyType apps.StatefulSetUpdateStrategyType) *InstanceSetBuilder { +func (builder *InstanceSetBuilder) SetUpdateStrategyType(strategyType workloads.InstanceSetUpdateStrategyType) *InstanceSetBuilder { builder.get().Spec.UpdateStrategy.Type = strategyType return builder } @@ -166,9 +166,9 @@ func (builder *InstanceSetBuilder) SetMembershipReconfiguration(reconfiguration } func (builder *InstanceSetBuilder) SetMemberUpdateStrategy(strategy *workloads.MemberUpdateStrategy) *InstanceSetBuilder { - builder.get().Spec.MemberUpdateStrategy = strategy + builder.get().Spec.UpdateStrategy.MemberUpdateStrategy = strategy if strategy != nil { - builder.SetUpdateStrategyType(apps.OnDeleteStatefulSetStrategyType) + builder.SetUpdateStrategyType(workloads.OnDeleteInstanceSetStrategyType) } return builder } diff --git a/pkg/controller/builder/builder_instance_set_test.go b/pkg/controller/builder/builder_instance_set_test.go index cc1ddbc29284..275bc3c5f4cb 100644 --- a/pkg/controller/builder/builder_instance_set_test.go +++ b/pkg/controller/builder/builder_instance_set_test.go @@ -108,14 +108,14 @@ var _ = Describe("instance_set builder", func() { }, } partition, maxUnavailable := int32(3), intstr.FromInt(2) - strategy := apps.StatefulSetUpdateStrategy{ - Type: apps.RollingUpdateStatefulSetStrategyType, - RollingUpdate: &apps.RollingUpdateStatefulSetStrategy{ + strategy := workloads.InstanceUpdateStrategy{ + Type: workloads.RollingUpdateInstanceSetStrategyType, + RollingUpdate: &workloads.RollingUpdateStrategy{ Partition: &partition, MaxUnavailable: &maxUnavailable, }, } - strategyType := apps.OnDeleteStatefulSetStrategyType + strategyType := workloads.OnDeleteInstanceSetStrategyType delay := int32(10) roleProbe := workloads.RoleProbe{InitialDelaySeconds: delay} actions := []workloads.Action{ @@ -213,8 +213,8 @@ var _ = Describe("instance_set builder", func() { Expect(its.Spec.RoleProbe.CustomHandler).Should(HaveLen(2)) Expect(its.Spec.RoleProbe.CustomHandler[0]).Should(Equal(actions[0])) Expect(its.Spec.RoleProbe.CustomHandler[1]).Should(Equal(action)) - Expect(its.Spec.MemberUpdateStrategy).ShouldNot(BeNil()) - Expect(*its.Spec.MemberUpdateStrategy).Should(Equal(memberUpdateStrategy)) + Expect(its.Spec.UpdateStrategy.MemberUpdateStrategy).ShouldNot(BeNil()) + Expect(*its.Spec.UpdateStrategy.MemberUpdateStrategy).Should(Equal(memberUpdateStrategy)) Expect(its.Spec.Service).ShouldNot(BeNil()) Expect(its.Spec.Service).Should(BeEquivalentTo(service)) Expect(its.Spec.Paused).Should(Equal(paused)) diff --git a/pkg/controller/component/component.go b/pkg/controller/component/component.go index 705086b597c4..62b9f3c27612 100644 --- a/pkg/controller/component/component.go +++ b/pkg/controller/component/component.go @@ -90,6 +90,7 @@ func BuildComponent(cluster *appsv1alpha1.Cluster, compSpec *appsv1alpha1.Cluste SetReplicas(compSpec.Replicas). SetResources(compSpec.Resources). SetServiceAccountName(compSpec.ServiceAccountName). + SetInstanceUpdateStrategy(compSpec.InstanceUpdateStrategy). SetParallelPodManagementConcurrency(compSpec.ParallelPodManagementConcurrency). SetPodUpdatePolicy(compSpec.PodUpdatePolicy). SetVolumeClaimTemplates(compSpec.VolumeClaimTemplates). diff --git a/pkg/controller/component/its_convertor.go b/pkg/controller/component/its_convertor.go index 969e80f1d534..7669563da928 100644 --- a/pkg/controller/component/its_convertor.go +++ b/pkg/controller/component/its_convertor.go @@ -47,7 +47,6 @@ func BuildWorkloadFrom(synthesizeComp *SynthesizedComponent, protoITS *workloads "roleprobe": &itsRoleProbeConvertor{}, "credential": &itsCredentialConvertor{}, "membershipreconfiguration": &itsMembershipReconfigurationConvertor{}, - "memberupdatestrategy": &itsMemberUpdateStrategyConvertor{}, "podmanagementpolicy": &itsPodManagementPolicyConvertor{}, "parallelpodmanagementconcurrency": &itsParallelPodManagementConcurrencyConvertor{}, "podupdatepolicy": &itsPodUpdatePolicyConvertor{}, @@ -79,17 +78,6 @@ type itsCredentialConvertor struct{} // itsMembershipReconfigurationConvertor is an implementation of the convertor interface, used to convert the given object into InstanceSet.Spec.MembershipReconfiguration. type itsMembershipReconfigurationConvertor struct{} -// itsMemberUpdateStrategyConvertor is an implementation of the convertor interface, used to convert the given object into InstanceSet.Spec.MemberUpdateStrategy. -type itsMemberUpdateStrategyConvertor struct{} - -func (c *itsMemberUpdateStrategyConvertor) convert(args ...any) (any, error) { - synthesizeComp, err := parseITSConvertorArgs(args...) - if err != nil { - return nil, err - } - return getMemberUpdateStrategy(synthesizeComp), nil -} - // itsPodManagementPolicyConvertor is an implementation of the convertor interface, used to convert the given object into InstanceSet.Spec.PodManagementPolicy. type itsPodManagementPolicyConvertor struct{} @@ -144,11 +132,15 @@ func (c *itsUpdateStrategyConvertor) convert(args ...any) (any, error) { if err != nil { return nil, err } - if getMemberUpdateStrategy(synthesizedComp) != nil { - // appsv1.OnDeleteStatefulSetStrategyType is the default value if member update strategy is set. - return appsv1.StatefulSetUpdateStrategy{}, nil + if memberUpdateStrategy := getMemberUpdateStrategy(synthesizedComp); memberUpdateStrategy != nil { + return workloads.InstanceUpdateStrategy{ + MemberUpdateStrategy: memberUpdateStrategy, + }, nil } - return nil, nil + return workloads.InstanceUpdateStrategy{ + Type: synthesizedComp.InstanceUpdateStrategy.Type, + RollingUpdate: synthesizedComp.InstanceUpdateStrategy.RollingUpdate, + }, nil } // itsInstancesConvertor converts component instanceTemplate to ITS instanceTemplate diff --git a/pkg/controller/component/synthesize_component.go b/pkg/controller/component/synthesize_component.go index 239c13587bb2..28824c94f0de 100644 --- a/pkg/controller/component/synthesize_component.go +++ b/pkg/controller/component/synthesize_component.go @@ -179,6 +179,7 @@ func buildSynthesizedComponent(reqCtx intctrlutil.RequestCtx, OfflineInstances: comp.Spec.OfflineInstances, DisableExporter: comp.Spec.DisableExporter, Stop: comp.Spec.Stop, + InstanceUpdateStrategy: comp.Spec.InstanceUpdateStrategy, PodManagementPolicy: compDef.Spec.PodManagementPolicy, ParallelPodManagementConcurrency: comp.Spec.ParallelPodManagementConcurrency, PodUpdatePolicy: comp.Spec.PodUpdatePolicy, diff --git a/pkg/controller/component/type.go b/pkg/controller/component/type.go index cf611905e692..9f74ee73a90c 100644 --- a/pkg/controller/component/type.go +++ b/pkg/controller/component/type.go @@ -61,6 +61,7 @@ type SynthesizedComponent struct { Labels map[string]string `json:"labels,omitempty"` Annotations map[string]string `json:"annotations,omitempty"` UpdateStrategy *v1alpha1.UpdateStrategy `json:"updateStrategy,omitempty"` + InstanceUpdateStrategy v1alpha1.InstanceUpdateStrategy `json:"instanceUpdateStrategy,omitempty"` PodManagementPolicy *appsv1.PodManagementPolicyType `json:"podManagementPolicy,omitempty"` ParallelPodManagementConcurrency *intstr.IntOrString `json:"parallelPodManagementConcurrency,omitempty"` PodUpdatePolicy *workloads.PodUpdatePolicyType `json:"podUpdatePolicy,omitempty"` diff --git a/pkg/controller/factory/builder_test.go b/pkg/controller/factory/builder_test.go index 059a2c34918c..5ded06fc5921 100644 --- a/pkg/controller/factory/builder_test.go +++ b/pkg/controller/factory/builder_test.go @@ -206,8 +206,8 @@ var _ = Describe("builder", func() { Expect(its.Spec.RoleProbe).ShouldNot(BeNil()) // test member update strategy - Expect(its.Spec.MemberUpdateStrategy).ShouldNot(BeNil()) - Expect(*its.Spec.MemberUpdateStrategy).Should(BeEquivalentTo(workloads.SerialUpdateStrategy)) + Expect(its.Spec.UpdateStrategy.MemberUpdateStrategy).ShouldNot(BeNil()) + Expect(*its.Spec.UpdateStrategy.MemberUpdateStrategy).Should(BeEquivalentTo(workloads.SerialUpdateStrategy)) By("set workload type to Consensus") clusterDef.Spec.ComponentDefs[0].WorkloadType = appsv1alpha1.Consensus @@ -227,8 +227,8 @@ var _ = Describe("builder", func() { Expect(its.Spec.RoleProbe).ShouldNot(BeNil()) // test member update strategy - Expect(its.Spec.MemberUpdateStrategy).ShouldNot(BeNil()) - Expect(*its.Spec.MemberUpdateStrategy).Should(BeEquivalentTo(workloads.BestEffortParallelUpdateStrategy)) + Expect(its.Spec.UpdateStrategy.MemberUpdateStrategy).ShouldNot(BeNil()) + Expect(*its.Spec.UpdateStrategy.MemberUpdateStrategy).Should(BeEquivalentTo(workloads.BestEffortParallelUpdateStrategy)) }) It("builds BackupJob correctly", func() { diff --git a/pkg/controller/instanceset/reconciler_update.go b/pkg/controller/instanceset/reconciler_update.go index 38d5a38d405e..e454db9237c7 100644 --- a/pkg/controller/instanceset/reconciler_update.go +++ b/pkg/controller/instanceset/reconciler_update.go @@ -93,7 +93,7 @@ func (r *updateReconciler) Reconcile(tree *kubebuilderx.ObjectTree) (kubebuilder // 3. do update // do nothing if UpdateStrategyType is 'OnDelete' - if its.Spec.UpdateStrategy.Type == apps.OnDeleteStatefulSetStrategyType { + if its.Spec.UpdateStrategy.Type == workloads.OnDeleteInstanceSetStrategyType { return kubebuilderx.Continue, nil } @@ -189,7 +189,7 @@ func buildBlockedCondition(its *workloads.InstanceSet, message string) *metav1.C } func getInstanceSetForUpdatePlan(its *workloads.InstanceSet) *workloads.InstanceSet { - if its.Spec.MemberUpdateStrategy != nil { + if its.Spec.UpdateStrategy.MemberUpdateStrategy != nil { return its } itsForPlan := its.DeepCopy() @@ -197,11 +197,11 @@ func getInstanceSetForUpdatePlan(its *workloads.InstanceSet) *workloads.Instance if its.Spec.PodManagementPolicy == apps.ParallelPodManagement { updateStrategy = workloads.ParallelUpdateStrategy } - itsForPlan.Spec.MemberUpdateStrategy = &updateStrategy + itsForPlan.Spec.UpdateStrategy.MemberUpdateStrategy = &updateStrategy return itsForPlan } -func parsePartitionNMaxUnavailable(rollingUpdate *apps.RollingUpdateStatefulSetStrategy, replicas int) (int, int, error) { +func parsePartitionNMaxUnavailable(rollingUpdate *workloads.RollingUpdateStrategy, replicas int) (int, int, error) { partition := replicas maxUnavailable := 1 if rollingUpdate == nil { diff --git a/pkg/controller/instanceset/reconciler_update_test.go b/pkg/controller/instanceset/reconciler_update_test.go index 8ed437941b19..a28d6a181a01 100644 --- a/pkg/controller/instanceset/reconciler_update_test.go +++ b/pkg/controller/instanceset/reconciler_update_test.go @@ -157,8 +157,8 @@ var _ = Describe("update reconciler test", func() { Expect(ok).Should(BeTrue()) partition := int32(3) maxUnavailable := intstr.FromInt32(2) - root.Spec.UpdateStrategy = appsv1.StatefulSetUpdateStrategy{ - RollingUpdate: &appsv1.RollingUpdateStatefulSetStrategy{ + root.Spec.UpdateStrategy = workloads.InstanceUpdateStrategy{ + RollingUpdate: &workloads.RollingUpdateStrategy{ Partition: &partition, MaxUnavailable: &maxUnavailable, }, @@ -175,8 +175,8 @@ var _ = Describe("update reconciler test", func() { Expect(err).Should(BeNil()) root, ok = partitionTree.GetRoot().(*workloads.InstanceSet) Expect(ok).Should(BeTrue()) - root.Spec.UpdateStrategy = appsv1.StatefulSetUpdateStrategy{ - RollingUpdate: &appsv1.RollingUpdateStatefulSetStrategy{ + root.Spec.UpdateStrategy = workloads.InstanceUpdateStrategy{ + RollingUpdate: &workloads.RollingUpdateStrategy{ Partition: &partition, MaxUnavailable: &maxUnavailable, }, @@ -199,7 +199,7 @@ var _ = Describe("update reconciler test", func() { Expect(err).Should(BeNil()) root, ok = onDeleteTree.GetRoot().(*workloads.InstanceSet) Expect(ok).Should(BeTrue()) - root.Spec.UpdateStrategy.Type = appsv1.OnDeleteStatefulSetStrategyType + root.Spec.UpdateStrategy.Type = workloads.OnDeleteInstanceSetStrategyType res, err = reconciler.Reconcile(onDeleteTree) Expect(err).Should(BeNil()) Expect(res).Should(Equal(kubebuilderx.Continue)) diff --git a/pkg/controller/instanceset/revision_util_test.go b/pkg/controller/instanceset/revision_util_test.go index 07b8545b7b3d..67d961097819 100644 --- a/pkg/controller/instanceset/revision_util_test.go +++ b/pkg/controller/instanceset/revision_util_test.go @@ -88,7 +88,6 @@ var _ = Describe("revision util test", func() { } } }, - "memberUpdateStrategy": "Serial", "podManagementPolicy": "Parallel", "replicas": 1, "roleProbe": { @@ -904,7 +903,8 @@ var _ = Describe("revision util test", func() { } }, "updateStrategy": { - "type": "OnDelete" + "type": "OnDelete", + "memberUpdateStrategy": "Serial" }, "volumeClaimTemplates": [ { diff --git a/pkg/controller/instanceset/update_plan.go b/pkg/controller/instanceset/update_plan.go index 2d7c0ede9c99..abe3fed45f7e 100644 --- a/pkg/controller/instanceset/update_plan.go +++ b/pkg/controller/instanceset/update_plan.go @@ -97,7 +97,7 @@ func (p *realUpdatePlan) planWalkFunc(vertex graph.Vertex) error { // This change may lead to false alarms, as when all replicas are temporarily unavailable for some reason, // the system will update them without waiting for their roles to be elected and probed. This cloud // potentially hide some uncertain risks. - serialUpdate := p.its.Spec.MemberUpdateStrategy != nil && *p.its.Spec.MemberUpdateStrategy == workloads.SerialUpdateStrategy + serialUpdate := p.its.Spec.UpdateStrategy.MemberUpdateStrategy != nil && *p.its.Spec.UpdateStrategy.MemberUpdateStrategy == workloads.SerialUpdateStrategy hasRoleProbed := len(p.its.Status.MembersStatus) > 0 if !serialUpdate || hasRoleProbed { return ErrWait @@ -121,7 +121,7 @@ func (p *realUpdatePlan) build() { root := &model.ObjectVertex{} p.dag.AddVertex(root) - if p.its.Spec.MemberUpdateStrategy == nil { + if p.its.Spec.UpdateStrategy.MemberUpdateStrategy == nil { return } @@ -129,7 +129,7 @@ func (p *realUpdatePlan) build() { SortPods(p.pods, rolePriorityMap, false) // generate plan by MemberUpdateStrategy - switch *p.its.Spec.MemberUpdateStrategy { + switch *p.its.Spec.UpdateStrategy.MemberUpdateStrategy { case workloads.SerialUpdateStrategy: p.buildSerialUpdatePlan() case workloads.ParallelUpdateStrategy: diff --git a/pkg/controller/instanceset/update_plan_test.go b/pkg/controller/instanceset/update_plan_test.go index ad1032f5438a..d13da7506bfe 100644 --- a/pkg/controller/instanceset/update_plan_test.go +++ b/pkg/controller/instanceset/update_plan_test.go @@ -122,7 +122,7 @@ var _ = Describe("update plan test.", func() { It("should work well in a serial plan", func() { By("build a serial plan") strategy := workloads.SerialUpdateStrategy - its.Spec.MemberUpdateStrategy = &strategy + its.Spec.UpdateStrategy.MemberUpdateStrategy = &strategy expectedPlan := [][]*corev1.Pod{ {pod4}, {pod2}, @@ -138,7 +138,7 @@ var _ = Describe("update plan test.", func() { It("should work well in a serial plan when pod has no role", func() { By("build a serial plan") strategy := workloads.SerialUpdateStrategy - its.Spec.MemberUpdateStrategy = &strategy + its.Spec.UpdateStrategy.MemberUpdateStrategy = &strategy expectedPlan := [][]*corev1.Pod{ {pod4}, {pod2}, @@ -154,7 +154,7 @@ var _ = Describe("update plan test.", func() { It("should work well in a parallel plan", func() { By("build a parallel plan") strategy := workloads.ParallelUpdateStrategy - its.Spec.MemberUpdateStrategy = &strategy + its.Spec.UpdateStrategy.MemberUpdateStrategy = &strategy expectedPlan := [][]*corev1.Pod{ {pod0, pod1, pod2, pod3, pod4, pod5, pod6}, } @@ -164,7 +164,7 @@ var _ = Describe("update plan test.", func() { It("should work well in a best effort parallel", func() { By("build a best effort parallel plan") strategy := workloads.BestEffortParallelUpdateStrategy - its.Spec.MemberUpdateStrategy = &strategy + its.Spec.UpdateStrategy.MemberUpdateStrategy = &strategy expectedPlan := [][]*corev1.Pod{ {pod2, pod3, pod4, pod6}, {pod1}, @@ -177,7 +177,7 @@ var _ = Describe("update plan test.", func() { It("should work well with role-less and heterogeneous pods", func() { By("build a serial plan with role-less and heterogeneous pods") strategy := workloads.SerialUpdateStrategy - its.Spec.MemberUpdateStrategy = &strategy + its.Spec.UpdateStrategy.MemberUpdateStrategy = &strategy its.Spec.Roles = nil for _, pod := range []*corev1.Pod{pod0, pod1, pod2, pod3, pod4, pod5, pod6} { labels := pod.Labels diff --git a/pkg/testutil/apps/instance_set_factoy.go b/pkg/testutil/apps/instance_set_factoy.go index 62ddd938d94c..28a3003dfaff 100644 --- a/pkg/testutil/apps/instance_set_factoy.go +++ b/pkg/testutil/apps/instance_set_factoy.go @@ -20,7 +20,6 @@ along with this program. If not, see . package apps import ( - appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -60,8 +59,8 @@ func NewInstanceSetFactory(namespace, name string, clusterName string, component }, }, }, - UpdateStrategy: appsv1.StatefulSetUpdateStrategy{ - Type: appsv1.OnDeleteStatefulSetStrategyType, + UpdateStrategy: workloads.InstanceUpdateStrategy{ + Type: workloads.OnDeleteInstanceSetStrategyType, }, }, }, f)