From f15db24f8814c3c96460e2574760bfbb068f2a0e Mon Sep 17 00:00:00 2001 From: free6om Date: Wed, 4 Dec 2024 16:13:36 +0800 Subject: [PATCH 01/11] feat: support InstanceUpdateStrategy --- apis/apps/v1/cluster_types.go | 5 +++++ apis/apps/v1/component_types.go | 6 ++++++ apis/apps/v1/types.go | 22 ++++++++++++++++++++++ 3 files changed, 33 insertions(+) diff --git a/apis/apps/v1/cluster_types.go b/apis/apps/v1/cluster_types.go index e1867dc74c8..fa8fcba2bab 100644 --- a/apis/apps/v1/cluster_types.go +++ b/apis/apps/v1/cluster_types.go @@ -426,6 +426,11 @@ type ClusterComponentSpec struct { // +optional ParallelPodManagementConcurrency *intstr.IntOrString `json:"parallelPodManagementConcurrency,omitempty"` + // Controls how the instances should be updated when a new ServiceVersion specified. + // + // +optional + InstanceUpdateStrategy *InstanceUpdateStrategy `json:"instanceUpdateStrategy,omitempty"` + // PodUpdatePolicy indicates how pods should be updated // // - `StrictInPlace` indicates that only allows in-place upgrades. diff --git a/apis/apps/v1/component_types.go b/apis/apps/v1/component_types.go index a0f9117f700..f416970c517 100644 --- a/apis/apps/v1/component_types.go +++ b/apis/apps/v1/component_types.go @@ -189,6 +189,12 @@ type ComponentSpec struct { // +optional ServiceAccountName string `json:"serviceAccountName,omitempty"` + // Indicates the InstanceUpdateStrategy that will be + // employed to update instances in the InstanceSet when a new ServiceVersion is specified. + // + // +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/v1/types.go b/apis/apps/v1/types.go index 0cc99cfc120..47dc1f6edd5 100644 --- a/apis/apps/v1/types.go +++ b/apis/apps/v1/types.go @@ -18,6 +18,7 @@ package v1 import ( corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/util/intstr" ) const ( @@ -661,3 +662,24 @@ type InstanceTemplate struct { // +optional VolumeClaimTemplates []ClusterComponentVolumeClaimTemplate `json:"volumeClaimTemplates,omitempty"` } + +// InstanceUpdateStrategy defines how instances of a component should be updated when a new ServiceVersion specified. +type InstanceUpdateStrategy struct { + // Indicates the number of instances that should be updated during a rolling update. + // The remaining instances will remain untouched. This is helpful in defining how many instances + // should participate in the update process. + // Value can be an absolute number (ex: 5) or a percentage of desired instances (ex: 10%). + // Absolute number is calculated from percentage by rounding up. + // The default value is ComponentSpec.Replicas (i.e., update all instances). + // + // +optional + Replicas *intstr.IntOrString `json:"replicas,omitempty"` + + // The maximum number of instances that can be unavailable during the update. + // Value can be an absolute number (ex: 5) or a percentage of desired instances (ex: 10%). + // Absolute number is calculated from percentage by rounding up. This can not be 0. + // Defaults to 1. The field applies to all instances. That means if there is any unavailable pod, + // it will be counted towards MaxUnavailable. + // +optional + MaxUnavailable *intstr.IntOrString `json:"maxUnavailable,omitempty"` +} From 95b47a114eefd4ecfc0c4b00d17b45748d64058e Mon Sep 17 00:00:00 2001 From: free6om Date: Wed, 4 Dec 2024 16:42:09 +0800 Subject: [PATCH 02/11] rename to RollingUpdate --- apis/apps/v1/cluster_types.go | 4 ++-- apis/apps/v1/component_types.go | 5 ++--- apis/apps/v1/types.go | 4 ++-- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/apis/apps/v1/cluster_types.go b/apis/apps/v1/cluster_types.go index fa8fcba2bab..b2b3eb16cc0 100644 --- a/apis/apps/v1/cluster_types.go +++ b/apis/apps/v1/cluster_types.go @@ -426,10 +426,10 @@ type ClusterComponentSpec struct { // +optional ParallelPodManagementConcurrency *intstr.IntOrString `json:"parallelPodManagementConcurrency,omitempty"` - // Controls how the instances should be updated when a new ServiceVersion specified. + // Provides fine-grained control over the RollingUpdate process when a new ServiceVersion specified. // // +optional - InstanceUpdateStrategy *InstanceUpdateStrategy `json:"instanceUpdateStrategy,omitempty"` + RollingUpdate *RollingUpdate `json:"rollingUpdate,omitempty"` // PodUpdatePolicy indicates how pods should be updated // diff --git a/apis/apps/v1/component_types.go b/apis/apps/v1/component_types.go index f416970c517..ec8a8cfd53f 100644 --- a/apis/apps/v1/component_types.go +++ b/apis/apps/v1/component_types.go @@ -189,11 +189,10 @@ type ComponentSpec struct { // +optional ServiceAccountName string `json:"serviceAccountName,omitempty"` - // Indicates the InstanceUpdateStrategy that will be - // employed to update instances in the InstanceSet when a new ServiceVersion is specified. + // Provides fine-grained control over the RollingUpdate process when a new ServiceVersion specified. // // +optional - InstanceUpdateStrategy *InstanceUpdateStrategy `json:"instanceUpdateStrategy,omitempty"` + RollingUpdate *RollingUpdate `json:"rollingUpdate,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/v1/types.go b/apis/apps/v1/types.go index 47dc1f6edd5..e64ca586fb2 100644 --- a/apis/apps/v1/types.go +++ b/apis/apps/v1/types.go @@ -663,8 +663,8 @@ type InstanceTemplate struct { VolumeClaimTemplates []ClusterComponentVolumeClaimTemplate `json:"volumeClaimTemplates,omitempty"` } -// InstanceUpdateStrategy defines how instances of a component should be updated when a new ServiceVersion specified. -type InstanceUpdateStrategy struct { +// RollingUpdate defines fine-grained control over the RollingUpdate process when a new ServiceVersion specified. +type RollingUpdate struct { // Indicates the number of instances that should be updated during a rolling update. // The remaining instances will remain untouched. This is helpful in defining how many instances // should participate in the update process. From 02208d3bac87b25c6e6af26682028335b5a83795 Mon Sep 17 00:00:00 2001 From: free6om Date: Thu, 5 Dec 2024 14:30:22 +0800 Subject: [PATCH 03/11] move&refactor all update related fields into a single struct --- apis/apps/v1/cluster_types.go | 16 +--- apis/apps/v1/component_types.go | 15 +--- apis/apps/v1/componentdefinition_types.go | 49 +--------- apis/apps/v1/shardingdefinition_types.go | 4 +- apis/apps/v1/types.go | 90 +++++++++++++++++-- apis/apps/v1/zz_generated.deepcopy.go | 10 +-- apis/apps/v1alpha1/cluster_conversion.go | 8 +- .../componentdefinition_controller_test.go | 10 +-- .../apps/shardingdefinition_controller.go | 4 +- .../shardingdefinition_controller_test.go | 8 +- pkg/controller/builder/builder_component.go | 2 +- .../builder/builder_component_definition.go | 2 +- pkg/controller/component/its_convertor.go | 6 +- pkg/controller/component/type.go | 22 ++--- .../apps/componentdefinition_factory.go | 2 +- pkg/testutil/apps/constant.go | 2 +- .../apps/shardingdefinition_factory.go | 4 +- 17 files changed, 130 insertions(+), 124 deletions(-) diff --git a/apis/apps/v1/cluster_types.go b/apis/apps/v1/cluster_types.go index b2b3eb16cc0..f30673a1393 100644 --- a/apis/apps/v1/cluster_types.go +++ b/apis/apps/v1/cluster_types.go @@ -426,22 +426,10 @@ type ClusterComponentSpec struct { // +optional ParallelPodManagementConcurrency *intstr.IntOrString `json:"parallelPodManagementConcurrency,omitempty"` - // Provides fine-grained control over the RollingUpdate process when a new ServiceVersion specified. + // Provides fine-grained control over the spec update process of all instances. // // +optional - RollingUpdate *RollingUpdate `json:"rollingUpdate,omitempty"` - - // PodUpdatePolicy indicates how pods should be updated - // - // - `StrictInPlace` indicates that only allows in-place upgrades. - // Any attempt to modify other fields will be rejected. - // - `PreferInPlace` indicates that we will first attempt an in-place upgrade of the Pod. - // If that fails, it will fall back to the ReCreate, where pod will be recreated. - // Default value is "PreferInPlace" - // - // +kubebuilder:validation:Enum={StrictInPlace,PreferInPlace} - // +optional - PodUpdatePolicy *PodUpdatePolicyType `json:"podUpdatePolicy,omitempty"` + UpdateStrategy *UpdateStrategy `json:"updateStrategy,omitempty"` // Allows for the customization of configuration values for each instance within a Component. // An instance represent a single replica (Pod and associated K8s resources like PVCs, Services, and ConfigMaps). diff --git a/apis/apps/v1/component_types.go b/apis/apps/v1/component_types.go index ec8a8cfd53f..26d9c96854b 100644 --- a/apis/apps/v1/component_types.go +++ b/apis/apps/v1/component_types.go @@ -189,10 +189,10 @@ type ComponentSpec struct { // +optional ServiceAccountName string `json:"serviceAccountName,omitempty"` - // Provides fine-grained control over the RollingUpdate process when a new ServiceVersion specified. + // Provides fine-grained control over the spec update process of all instances. // // +optional - RollingUpdate *RollingUpdate `json:"rollingUpdate,omitempty"` + UpdateStrategy *UpdateStrategy `json:"updateStrategy,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`. @@ -201,17 +201,6 @@ type ComponentSpec struct { // +optional ParallelPodManagementConcurrency *intstr.IntOrString `json:"parallelPodManagementConcurrency,omitempty"` - // PodUpdatePolicy indicates how pods should be updated - // - // - `StrictInPlace` indicates that only allows in-place upgrades. - // Any attempt to modify other fields will be rejected. - // - `PreferInPlace` indicates that we will first attempt an in-place upgrade of the Pod. - // If that fails, it will fall back to the ReCreate, where pod will be recreated. - // Default value is "PreferInPlace" - // - // +optional - PodUpdatePolicy *PodUpdatePolicyType `json:"podUpdatePolicy,omitempty"` - // Specifies the scheduling policy for the Component. // // +optional diff --git a/apis/apps/v1/componentdefinition_types.go b/apis/apps/v1/componentdefinition_types.go index 4299b460b86..b0a714c5542 100644 --- a/apis/apps/v1/componentdefinition_types.go +++ b/apis/apps/v1/componentdefinition_types.go @@ -444,21 +444,10 @@ type ComponentDefinitionSpec struct { // +optional MinReadySeconds int32 `json:"minReadySeconds,omitempty"` - // Specifies the concurrency strategy for updating multiple instances of the Component. - // Available strategies: + // Specifies fine-grained control over the spec update process of all instances. // - // - `Serial`: Updates replicas one at a time, ensuring minimal downtime by waiting for each replica to become ready - // before updating the next. - // - `Parallel`: Updates all replicas simultaneously, optimizing for speed but potentially reducing availability - // during the update. - // - `BestEffortParallel`: Updates replicas concurrently with a limit on simultaneous updates to ensure a minimum - // number of operational replicas for maintaining quorum. - // For example, in a 5-replica component, updating a maximum of 2 replicas simultaneously keeps - // at least 3 operational for quorum. - // - // This field is immutable and defaults to 'Serial'. + // This field is immutable. // - // +kubebuilder:default=Serial // +optional UpdateStrategy *UpdateStrategy `json:"updateStrategy,omitempty"` @@ -1482,40 +1471,6 @@ type ReplicaRole struct { Votable bool `json:"votable,omitempty"` } -// UpdateStrategy defines the update strategy for cluster components. This strategy determines how updates are applied -// across the cluster. -// The available strategies are `Serial`, `BestEffortParallel`, and `Parallel`. -// -// +enum -// +kubebuilder:validation:Enum={Serial,BestEffortParallel,Parallel} -type UpdateStrategy string - -const ( - // SerialStrategy indicates that updates are applied one at a time in a sequential manner. - // The operator waits for each replica to be updated and ready before proceeding to the next one. - // This ensures that only one replica is unavailable at a time during the update process. - SerialStrategy UpdateStrategy = "Serial" - - // ParallelStrategy indicates that updates are applied simultaneously to all Pods of a Component. - // The replicas are updated in parallel, with the operator updating all replicas concurrently. - // This strategy provides the fastest update time but may lead to a period of reduced availability or - // capacity during the update process. - ParallelStrategy UpdateStrategy = "Parallel" - - // BestEffortParallelStrategy indicates that the replicas are updated in parallel, with the operator making - // a best-effort attempt to update as many replicas as possible concurrently - // while maintaining the component's availability. - // Unlike the `Parallel` strategy, the `BestEffortParallel` strategy aims to ensure that a minimum number - // of replicas remain available during the update process to maintain the component's quorum and functionality. - // - // For example, consider a component with 5 replicas. To maintain the component's availability and quorum, - // the operator may allow a maximum of 2 replicas to be simultaneously updated. This ensures that at least - // 3 replicas (a quorum) remain available and functional during the update process. - // - // The `BestEffortParallel` strategy strikes a balance between update speed and component availability. - BestEffortParallelStrategy UpdateStrategy = "BestEffortParallel" -) - // ComponentLifecycleActions defines a collection of Actions for customizing the behavior of a Component. type ComponentLifecycleActions struct { // Specifies the hook to be executed after a component's creation. diff --git a/apis/apps/v1/shardingdefinition_types.go b/apis/apps/v1/shardingdefinition_types.go index 6c1adf14cb2..b3f88fb4691 100644 --- a/apis/apps/v1/shardingdefinition_types.go +++ b/apis/apps/v1/shardingdefinition_types.go @@ -75,7 +75,7 @@ type ShardingDefinitionSpec struct { // // +kubebuilder:default=Serial // +optional - ProvisionStrategy *UpdateStrategy `json:"provisionStrategy,omitempty"` + ProvisionStrategy *UpdateConcurrency `json:"provisionStrategy,omitempty"` // Specifies the strategy for updating shards of the sharding. Only `Serial` and `Parallel` are supported. // @@ -83,7 +83,7 @@ type ShardingDefinitionSpec struct { // // +kubebuilder:default=Serial // +optional - UpdateStrategy *UpdateStrategy `json:"updateStrategy,omitempty"` + UpdateStrategy *UpdateConcurrency `json:"updateStrategy,omitempty"` // Defines a set of hooks and procedures that customize the behavior of a sharding throughout its lifecycle. // diff --git a/apis/apps/v1/types.go b/apis/apps/v1/types.go index e64ca586fb2..6369e87b4bb 100644 --- a/apis/apps/v1/types.go +++ b/apis/apps/v1/types.go @@ -457,16 +457,16 @@ type ClusterComponentConfigSource struct { // - Local file } -type PodUpdatePolicyType string +type InstanceUpdatePolicyType string const ( - // StrictInPlacePodUpdatePolicyType indicates that only allows in-place upgrades. - // Any attempt to modify other fields will be rejected. - StrictInPlacePodUpdatePolicyType PodUpdatePolicyType = "StrictInPlace" + // StrictInPlaceInstanceUpdatePolicyType indicates that only allows in-place update. + // Any attempt to modify other fields that not support in-place update will be rejected. + StrictInPlaceInstanceUpdatePolicyType InstanceUpdatePolicyType = "StrictInPlace" - // PreferInPlacePodUpdatePolicyType indicates that we will first attempt an in-place upgrade of the Pod. - // If that fails, it will fall back to the ReCreate, where pod will be recreated. - PreferInPlacePodUpdatePolicyType PodUpdatePolicyType = "PreferInPlace" + // PreferInPlaceInstanceUpdatePolicyType indicates that we will first attempt an in-place update of the instance. + // If that fails, it will fall back to the ReCreate, where instance will be recreated. + PreferInPlaceInstanceUpdatePolicyType InstanceUpdatePolicyType = "PreferInPlace" ) type SchedulingPolicy struct { @@ -663,7 +663,27 @@ type InstanceTemplate struct { VolumeClaimTemplates []ClusterComponentVolumeClaimTemplate `json:"volumeClaimTemplates,omitempty"` } -// RollingUpdate defines fine-grained control over the RollingUpdate process when a new ServiceVersion specified. +// UpdateStrategy defines fine-grained control over the spec update process of all instances. +type UpdateStrategy struct { + // Indicates how instances should be updated. + // + // - `StrictInPlace` indicates that only allows in-place update. + // Any attempt to modify other fields that not support in-place update will be rejected. + // - `PreferInPlace` indicates that we will first attempt an in-place update of the instance. + // If that fails, it will fall back to the ReCreate, where instance will be recreated. + // Default value is "PreferInPlace". + // + // +kubebuilder:validation:Enum={StrictInPlace,PreferInPlace} + // +optional + InstanceUpdatePolicy *InstanceUpdatePolicyType `json:"instanceUpdatePolicy,omitempty"` + + // Specifies how the rolling update should be applied. + // + // +optional + RollingUpdate *RollingUpdate `json:"rollingUpdate,omitempty"` +} + +// RollingUpdate specifies how the rolling update should be applied. type RollingUpdate struct { // Indicates the number of instances that should be updated during a rolling update. // The remaining instances will remain untouched. This is helpful in defining how many instances @@ -675,11 +695,65 @@ type RollingUpdate struct { // +optional Replicas *intstr.IntOrString `json:"replicas,omitempty"` + // Specifies the concurrency level for updating instances during a rolling update. + // Available levels: + // + // - `Serial`: Updates instances one at a time, ensuring minimal downtime by waiting for each instance to become ready + // before updating the next. + // - `Parallel`: Updates all instances simultaneously, optimizing for speed but potentially reducing availability + // during the update. + // - `BestEffortParallel`: Updates instances concurrently with a limit on simultaneous updates to ensure a minimum + // number of operational replicas for maintaining quorum. + // For example, in a 5-instances setup, updating a maximum of 2 instances simultaneously keeps + // at least 3 operational for quorum. + // + // Defaults to 'Serial'. + // + // +kubebuilder:validation:Enum={Serial,Parallel,BestEffortParallel} + // +kubebuilder:default=Serial + // +optional + UpdateConcurrency *UpdateConcurrency `json:"updateConcurrency,omitempty"` + // The maximum number of instances that can be unavailable during the update. // Value can be an absolute number (ex: 5) or a percentage of desired instances (ex: 10%). // Absolute number is calculated from percentage by rounding up. This can not be 0. // Defaults to 1. The field applies to all instances. That means if there is any unavailable pod, // it will be counted towards MaxUnavailable. + // // +optional MaxUnavailable *intstr.IntOrString `json:"maxUnavailable,omitempty"` } + +// UpdateConcurrency defines the update concurrency level for cluster components. This concurrency level determines how updates are applied +// across the cluster. +// The available concurrency levels are `Serial`, `BestEffortParallel`, and `Parallel`. +// +// +enum +// +kubebuilder:validation:Enum={Serial,BestEffortParallel,Parallel} +type UpdateConcurrency string + +const ( + // SerialConcurrency indicates that updates are applied one at a time in a sequential manner. + // The operator waits for each replica to be updated and ready before proceeding to the next one. + // This ensures that only one replica is unavailable at a time during the update process. + SerialConcurrency UpdateConcurrency = "Serial" + + // ParallelConcurrency indicates that updates are applied simultaneously to all Pods of a Component. + // The replicas are updated in parallel, with the operator updating all replicas concurrently. + // This strategy provides the fastest update time but may lead to a period of reduced availability or + // capacity during the update process. + ParallelConcurrency UpdateConcurrency = "Parallel" + + // BestEffortParallelConcurrency indicates that the replicas are updated in parallel, with the operator making + // a best-effort attempt to update as many replicas as possible concurrently + // while maintaining the component's availability. + // Unlike the `Parallel` strategy, the `BestEffortParallel` strategy aims to ensure that a minimum number + // of replicas remain available during the update process to maintain the component's quorum and functionality. + // + // For example, consider a component with 5 replicas. To maintain the component's availability and quorum, + // the operator may allow a maximum of 2 replicas to be simultaneously updated. This ensures that at least + // 3 replicas (a quorum) remain available and functional during the update process. + // + // The `BestEffortParallel` strategy strikes a balance between update speed and component availability. + BestEffortParallelConcurrency UpdateConcurrency = "BestEffortParallel" +) diff --git a/apis/apps/v1/zz_generated.deepcopy.go b/apis/apps/v1/zz_generated.deepcopy.go index 7d309c6a4c3..a908b87111c 100644 --- a/apis/apps/v1/zz_generated.deepcopy.go +++ b/apis/apps/v1/zz_generated.deepcopy.go @@ -326,7 +326,7 @@ func (in *ClusterComponentSpec) DeepCopyInto(out *ClusterComponentSpec) { } if in.PodUpdatePolicy != nil { in, out := &in.PodUpdatePolicy, &out.PodUpdatePolicy - *out = new(PodUpdatePolicyType) + *out = new(InstanceUpdatePolicyType) **out = **in } if in.Instances != nil { @@ -1198,7 +1198,7 @@ func (in *ComponentDefinitionSpec) DeepCopyInto(out *ComponentDefinitionSpec) { } if in.UpdateStrategy != nil { in, out := &in.UpdateStrategy, &out.UpdateStrategy - *out = new(UpdateStrategy) + *out = new(UpdateConcurrency) **out = **in } if in.PodManagementPolicy != nil { @@ -1469,7 +1469,7 @@ func (in *ComponentSpec) DeepCopyInto(out *ComponentSpec) { } if in.PodUpdatePolicy != nil { in, out := &in.PodUpdatePolicy, &out.PodUpdatePolicy - *out = new(PodUpdatePolicyType) + *out = new(InstanceUpdatePolicyType) **out = **in } if in.SchedulingPolicy != nil { @@ -2948,12 +2948,12 @@ func (in *ShardingDefinitionSpec) DeepCopyInto(out *ShardingDefinitionSpec) { } if in.ProvisionStrategy != nil { in, out := &in.ProvisionStrategy, &out.ProvisionStrategy - *out = new(UpdateStrategy) + *out = new(UpdateConcurrency) **out = **in } if in.UpdateStrategy != nil { in, out := &in.UpdateStrategy, &out.UpdateStrategy - *out = new(UpdateStrategy) + *out = new(UpdateConcurrency) **out = **in } if in.LifecycleActions != nil { diff --git a/apis/apps/v1alpha1/cluster_conversion.go b/apis/apps/v1alpha1/cluster_conversion.go index 44b5e75d0b3..b5ef1e578fa 100644 --- a/apis/apps/v1alpha1/cluster_conversion.go +++ b/apis/apps/v1alpha1/cluster_conversion.go @@ -104,13 +104,13 @@ func (r *Cluster) changesToCluster(cluster *appsv1.Cluster) { // - volumeClaimTemplates // spec: // resources: corev1.ResourceRequirements -> corev1.VolumeResourceRequirements - // podUpdatePolicy: *workloads.PodUpdatePolicyType -> *PodUpdatePolicyType + // podUpdatePolicy: *workloads.InstanceUpdatePolicyType -> *InstanceUpdatePolicyType // sharings // - template // volumeClaimTemplates // spec: // resources: corev1.ResourceRequirements -> corev1.VolumeResourceRequirements - // podUpdatePolicy: *workloads.PodUpdatePolicyType -> *PodUpdatePolicyType + // podUpdatePolicy: *workloads.InstanceUpdatePolicyType -> *InstanceUpdatePolicyType // status // components // - message: ComponentMessageMap -> map[string]string @@ -132,13 +132,13 @@ func (r *Cluster) changesFromCluster(cluster *appsv1.Cluster) { // - volumeClaimTemplates // spec: // resources: corev1.ResourceRequirements -> corev1.VolumeResourceRequirements - // podUpdatePolicy: *workloads.PodUpdatePolicyType -> *PodUpdatePolicyType + // podUpdatePolicy: *workloads.InstanceUpdatePolicyType -> *InstanceUpdatePolicyType // sharings // - template // volumeClaimTemplates // spec: // resources: corev1.ResourceRequirements -> corev1.VolumeResourceRequirements - // podUpdatePolicy: *workloads.PodUpdatePolicyType -> *PodUpdatePolicyType + // podUpdatePolicy: *workloads.InstanceUpdatePolicyType -> *InstanceUpdatePolicyType // status // components // - message: ComponentMessageMap -> map[string]string diff --git a/controllers/apps/componentdefinition_controller_test.go b/controllers/apps/componentdefinition_controller_test.go index 204a50169e0..a847daf5f75 100644 --- a/controllers/apps/componentdefinition_controller_test.go +++ b/controllers/apps/componentdefinition_controller_test.go @@ -522,7 +522,7 @@ var _ = Describe("ComponentDefinition Controller", func() { Expect(testapps.GetAndChangeObj(&testCtx, client.ObjectKeyFromObject(componentDefObj), func(cmpd *kbappsv1.ComponentDefinition) { cmpd.Spec.Description = "v0.0.2" cmpd.Spec.Runtime.Containers[0].Image = "image:v0.0.2" - parallel := kbappsv1.ParallelStrategy + parallel := kbappsv1.ParallelConcurrency cmpd.Spec.UpdateStrategy = ¶llel })()).Should(Succeed()) @@ -539,7 +539,7 @@ var _ = Describe("ComponentDefinition Controller", func() { } g.Expect(cmpd.Spec.Runtime.Containers[0]).Should(BeEquivalentTo(c)) g.Expect(cmpd.Spec.UpdateStrategy).ShouldNot(BeNil()) - g.Expect(*cmpd.Spec.UpdateStrategy).Should(Equal(kbappsv1.ParallelStrategy)) + g.Expect(*cmpd.Spec.UpdateStrategy).Should(Equal(kbappsv1.ParallelConcurrency)) })).Should(Succeed()) }) @@ -550,7 +550,7 @@ var _ = Describe("ComponentDefinition Controller", func() { Expect(testapps.GetAndChangeObj(&testCtx, client.ObjectKeyFromObject(componentDefObj), func(cmpd *kbappsv1.ComponentDefinition) { cmpd.Spec.Description = "v0.0.2" cmpd.Spec.Runtime.Containers[0].Image = "image:v0.0.2" - parallel := kbappsv1.ParallelStrategy + parallel := kbappsv1.ParallelConcurrency cmpd.Spec.UpdateStrategy = ¶llel })()).Should(Succeed()) @@ -567,7 +567,7 @@ var _ = Describe("ComponentDefinition Controller", func() { } g.Expect(cmpd.Spec.Runtime.Containers[0]).Should(BeEquivalentTo(c)) g.Expect(cmpd.Spec.UpdateStrategy).ShouldNot(BeNil()) - g.Expect(*cmpd.Spec.UpdateStrategy).Should(Equal(kbappsv1.ParallelStrategy)) + g.Expect(*cmpd.Spec.UpdateStrategy).Should(Equal(kbappsv1.ParallelConcurrency)) })).Should(Succeed()) By("revert the change to immutable fields back") @@ -589,7 +589,7 @@ var _ = Describe("ComponentDefinition Controller", func() { } g.Expect(cmpd.Spec.Runtime.Containers[0]).Should(BeEquivalentTo(c)) g.Expect(cmpd.Spec.UpdateStrategy).ShouldNot(BeNil()) - g.Expect(*cmpd.Spec.UpdateStrategy).Should(Equal(kbappsv1.SerialStrategy)) + g.Expect(*cmpd.Spec.UpdateStrategy).Should(Equal(kbappsv1.SerialConcurrency)) })).Should(Succeed()) }) }) diff --git a/controllers/apps/shardingdefinition_controller.go b/controllers/apps/shardingdefinition_controller.go index cbf14138dda..54d65a93127 100644 --- a/controllers/apps/shardingdefinition_controller.go +++ b/controllers/apps/shardingdefinition_controller.go @@ -188,11 +188,11 @@ func (r *ShardingDefinitionReconciler) validateShardsLimit(ctx context.Context, func (r *ShardingDefinitionReconciler) validateProvisionNUpdateStrategy(ctx context.Context, cli client.Client, shardingDef *appsv1.ShardingDefinition) error { - supported := func(strategy *appsv1.UpdateStrategy) bool { + supported := func(strategy *appsv1.UpdateConcurrency) bool { if strategy == nil { return true } - return *strategy == appsv1.SerialStrategy || *strategy == appsv1.ParallelStrategy + return *strategy == appsv1.SerialConcurrency || *strategy == appsv1.ParallelConcurrency } if !supported(shardingDef.Spec.ProvisionStrategy) { return fmt.Errorf("unsupported provision strategy: %s", *shardingDef.Spec.ProvisionStrategy) diff --git a/controllers/apps/shardingdefinition_controller_test.go b/controllers/apps/shardingdefinition_controller_test.go index ee8aff37eba..e2fbd84e831 100644 --- a/controllers/apps/shardingdefinition_controller_test.go +++ b/controllers/apps/shardingdefinition_controller_test.go @@ -115,8 +115,8 @@ var _ = Describe("ShardingDefinition Controller", func() { It("ok", func() { By("create a ShardingDefinition obj") shardingDefObj := testapps.NewShardingDefinitionFactory(shardingDefName, compDefObj.GetName()). - SetProvisionStrategy(appsv1.SerialStrategy). - SetUpdateStrategy(appsv1.ParallelStrategy). + SetProvisionStrategy(appsv1.SerialConcurrency). + SetUpdateStrategy(appsv1.ParallelConcurrency). Create(&testCtx).GetObject() checkObjectStatus(shardingDefObj, appsv1.AvailablePhase) @@ -125,8 +125,8 @@ var _ = Describe("ShardingDefinition Controller", func() { It("unsupported strategy", func() { By("create a ShardingDefinition obj") shardingDefObj := testapps.NewShardingDefinitionFactory(shardingDefName, compDefObj.GetName()). - SetProvisionStrategy(appsv1.BestEffortParallelStrategy). - SetUpdateStrategy(appsv1.BestEffortParallelStrategy). + SetProvisionStrategy(appsv1.BestEffortParallelConcurrency). + SetUpdateStrategy(appsv1.BestEffortParallelConcurrency). Create(&testCtx).GetObject() checkObjectStatus(shardingDefObj, appsv1.UnavailablePhase) diff --git a/pkg/controller/builder/builder_component.go b/pkg/controller/builder/builder_component.go index ce41b00d655..5d8ae199a05 100644 --- a/pkg/controller/builder/builder_component.go +++ b/pkg/controller/builder/builder_component.go @@ -86,7 +86,7 @@ func (builder *ComponentBuilder) SetParallelPodManagementConcurrency(parallelPod return builder } -func (builder *ComponentBuilder) SetPodUpdatePolicy(policy *appsv1.PodUpdatePolicyType) *ComponentBuilder { +func (builder *ComponentBuilder) SetPodUpdatePolicy(policy *appsv1.InstanceUpdatePolicyType) *ComponentBuilder { builder.get().Spec.PodUpdatePolicy = policy return builder } diff --git a/pkg/controller/builder/builder_component_definition.go b/pkg/controller/builder/builder_component_definition.go index 92497251145..c8087da54b6 100644 --- a/pkg/controller/builder/builder_component_definition.go +++ b/pkg/controller/builder/builder_component_definition.go @@ -205,7 +205,7 @@ func (builder *ComponentDefinitionBuilder) AddSystemAccount(accountName string, return builder } -func (builder *ComponentDefinitionBuilder) SetUpdateStrategy(strategy *appsv1.UpdateStrategy) *ComponentDefinitionBuilder { +func (builder *ComponentDefinitionBuilder) SetUpdateStrategy(strategy *appsv1.UpdateConcurrency) *ComponentDefinitionBuilder { builder.get().Spec.UpdateStrategy = strategy return builder } diff --git a/pkg/controller/component/its_convertor.go b/pkg/controller/component/its_convertor.go index af8af9b5d16..3d51af61c54 100644 --- a/pkg/controller/component/its_convertor.go +++ b/pkg/controller/component/its_convertor.go @@ -259,11 +259,11 @@ func getMemberUpdateStrategy(synthesizedComp *SynthesizedComponent) *workloads.M bestEffortParallelUpdate = workloads.BestEffortParallelUpdateStrategy ) switch *synthesizedComp.UpdateStrategy { - case kbappsv1.SerialStrategy: + case kbappsv1.SerialConcurrency: return &serial - case kbappsv1.ParallelStrategy: + case kbappsv1.ParallelConcurrency: return ¶llelUpdate - case kbappsv1.BestEffortParallelStrategy: + case kbappsv1.BestEffortParallelConcurrency: return &bestEffortParallelUpdate default: return nil diff --git a/pkg/controller/component/type.go b/pkg/controller/component/type.go index 7b6d06d9f20..2fbc633c63b 100644 --- a/pkg/controller/component/type.go +++ b/pkg/controller/component/type.go @@ -42,7 +42,7 @@ type SynthesizedComponent struct { Replicas int32 `json:"replicas"` Resources corev1.ResourceRequirements `json:"resources,omitempty"` PodSpec *corev1.PodSpec `json:"podSpec,omitempty"` - SidecarVars []kbappsv1.EnvVar // vars defined by sidecars + SidecarVars []kbappsv1.EnvVar // vars defined by sidecars VolumeClaimTemplates []corev1.PersistentVolumeClaimTemplate `json:"volumeClaimTemplates,omitempty"` LogConfigs []kbappsv1.LogConfig `json:"logConfigs,omitempty"` ConfigTemplates []kbappsv1.ComponentConfigSpec `json:"configTemplates,omitempty"` @@ -51,22 +51,22 @@ type SynthesizedComponent struct { ServiceAccountName string `json:"serviceAccountName,omitempty"` ServiceReferences map[string]*kbappsv1.ServiceDescriptor `json:"serviceReferences,omitempty"` Labels map[string]string `json:"labels,omitempty"` - StaticLabels map[string]string // labels defined by the component definition - DynamicLabels map[string]string // labels defined by the cluster and component API + StaticLabels map[string]string // labels defined by the component definition + DynamicLabels map[string]string // labels defined by the cluster and component API Annotations map[string]string `json:"annotations,omitempty"` - StaticAnnotations map[string]string // annotations defined by the component definition - DynamicAnnotations map[string]string // annotations defined by the cluster and component API + StaticAnnotations map[string]string // annotations defined by the component definition + DynamicAnnotations map[string]string // annotations defined by the cluster and component API TemplateVars map[string]any `json:"templateVars,omitempty"` EnvVars []corev1.EnvVar `json:"envVars,omitempty"` EnvFromSources []corev1.EnvFromSource `json:"envFromSources,omitempty"` Instances []kbappsv1.InstanceTemplate `json:"instances,omitempty"` OfflineInstances []string `json:"offlineInstances,omitempty"` - Roles []kbappsv1.ReplicaRole `json:"roles,omitempty"` - UpdateStrategy *kbappsv1.UpdateStrategy `json:"updateStrategy,omitempty"` - PodManagementPolicy *appsv1.PodManagementPolicyType `json:"podManagementPolicy,omitempty"` - ParallelPodManagementConcurrency *intstr.IntOrString `json:"parallelPodManagementConcurrency,omitempty"` - PodUpdatePolicy *kbappsv1.PodUpdatePolicyType `json:"podUpdatePolicy,omitempty"` - PolicyRules []rbacv1.PolicyRule `json:"policyRules,omitempty"` + Roles []kbappsv1.ReplicaRole `json:"roles,omitempty"` + UpdateStrategy *kbappsv1.UpdateConcurrency `json:"updateStrategy,omitempty"` + PodManagementPolicy *appsv1.PodManagementPolicyType `json:"podManagementPolicy,omitempty"` + ParallelPodManagementConcurrency *intstr.IntOrString `json:"parallelPodManagementConcurrency,omitempty"` + PodUpdatePolicy *kbappsv1.InstanceUpdatePolicyType `json:"podUpdatePolicy,omitempty"` + PolicyRules []rbacv1.PolicyRule `json:"policyRules,omitempty"` LifecycleActions *kbappsv1.ComponentLifecycleActions `json:"lifecycleActions,omitempty"` SystemAccounts []kbappsv1.SystemAccount `json:"systemAccounts,omitempty"` Volumes []kbappsv1.ComponentVolume `json:"volumes,omitempty"` diff --git a/pkg/testutil/apps/componentdefinition_factory.go b/pkg/testutil/apps/componentdefinition_factory.go index 8871c693202..3f177cf257d 100644 --- a/pkg/testutil/apps/componentdefinition_factory.go +++ b/pkg/testutil/apps/componentdefinition_factory.go @@ -276,7 +276,7 @@ func (f *MockComponentDefinitionFactory) AddSystemAccount(accountName string, in return f } -func (f *MockComponentDefinitionFactory) SetUpdateStrategy(strategy *kbappsv1.UpdateStrategy) *MockComponentDefinitionFactory { +func (f *MockComponentDefinitionFactory) SetUpdateStrategy(strategy *kbappsv1.UpdateConcurrency) *MockComponentDefinitionFactory { f.Get().Spec.UpdateStrategy = strategy return f } diff --git a/pkg/testutil/apps/constant.go b/pkg/testutil/apps/constant.go index c18fa35603d..f833612f73f 100644 --- a/pkg/testutil/apps/constant.go +++ b/pkg/testutil/apps/constant.go @@ -206,7 +206,7 @@ var ( }, }, }, - UpdateStrategy: &[]appsv1.UpdateStrategy{appsv1.BestEffortParallelStrategy}[0], + UpdateStrategy: &[]appsv1.UpdateConcurrency{appsv1.BestEffortParallelConcurrency}[0], Roles: []appsv1.ReplicaRole{ { Name: "leader", diff --git a/pkg/testutil/apps/shardingdefinition_factory.go b/pkg/testutil/apps/shardingdefinition_factory.go index d1cbc3e61a4..3f41b75ef82 100644 --- a/pkg/testutil/apps/shardingdefinition_factory.go +++ b/pkg/testutil/apps/shardingdefinition_factory.go @@ -40,12 +40,12 @@ func NewShardingDefinitionFactory(name, compDef string) *MockShardingDefinitionF return f } -func (f *MockShardingDefinitionFactory) SetProvisionStrategy(strategy appsv1.UpdateStrategy) *MockShardingDefinitionFactory { +func (f *MockShardingDefinitionFactory) SetProvisionStrategy(strategy appsv1.UpdateConcurrency) *MockShardingDefinitionFactory { f.Get().Spec.ProvisionStrategy = &strategy return f } -func (f *MockShardingDefinitionFactory) SetUpdateStrategy(strategy appsv1.UpdateStrategy) *MockShardingDefinitionFactory { +func (f *MockShardingDefinitionFactory) SetUpdateStrategy(strategy appsv1.UpdateConcurrency) *MockShardingDefinitionFactory { f.Get().Spec.UpdateStrategy = &strategy return f } From 374702c20ccbd3483f8524841c627980dbd044ab Mon Sep 17 00:00:00 2001 From: free6om Date: Thu, 5 Dec 2024 17:32:31 +0800 Subject: [PATCH 04/11] update stratey constraint --- apis/apps/v1/componentdefinition_types.go | 69 ++++++++++++++++++- apis/apps/v1/zz_generated.deepcopy.go | 4 +- .../componentdefinition_controller_test.go | 18 ++--- .../builder/builder_component_definition.go | 2 +- .../component/synthesize_component.go | 2 +- .../apps/componentdefinition_factory.go | 2 +- pkg/testutil/apps/constant.go | 2 +- 7 files changed, 82 insertions(+), 17 deletions(-) diff --git a/apis/apps/v1/componentdefinition_types.go b/apis/apps/v1/componentdefinition_types.go index b0a714c5542..6defdb14251 100644 --- a/apis/apps/v1/componentdefinition_types.go +++ b/apis/apps/v1/componentdefinition_types.go @@ -444,12 +444,12 @@ type ComponentDefinitionSpec struct { // +optional MinReadySeconds int32 `json:"minReadySeconds,omitempty"` - // Specifies fine-grained control over the spec update process of all instances. + // Specifies constraint on Component's UpdateStrategy. // // This field is immutable. // // +optional - UpdateStrategy *UpdateStrategy `json:"updateStrategy,omitempty"` + UpdateStrategyConstraint *UpdateStrategyConstraint `json:"updateStrategyConstraint,omitempty"` // InstanceSet controls the creation of pods during initial scale up, replacement of pods on nodes, and scaling down. // @@ -2087,3 +2087,68 @@ const ( HTTPProtocol PrometheusScheme = "http" HTTPSProtocol PrometheusScheme = "https" ) + +// UpdateStrategyConstraint defines constraint on Component's UpdateStrategy. +type UpdateStrategyConstraint struct { + // Specifies how an instance should be updated. + // + // - `StrictInPlace` indicates that only allows in-place update. + // Any attempt to modify other fields that not support in-place update will be rejected. + // - `PreferInPlace` indicates that we will first attempt an in-place update of the instance. + // If that fails, it will fall back to the ReCreate, where instance will be recreated. + // Default value is "PreferInPlace". + // + // +kubebuilder:validation:Enum={StrictInPlace,PreferInPlace} + // +optional + InstanceUpdatePolicy *InstanceUpdatePolicyType `json:"instanceUpdatePolicy,omitempty"` + + // Defines what InstanceUpdatePolicyTypes are allowed to be specified in Component. + // + // +optional + InstanceUpdatePoliciesAllowed []InstanceUpdatePolicyType `json:"instanceUpdatePoliciesAllowed,omitempty"` + + // Specifies constraint on Component's RollingUpdate. + // + // +optional + RollingUpdateConstraint *RollingUpdateConstraint `json:"rollingUpdateConstraint,omitempty"` +} + +type RollingUpdateConstraint struct { + // Specifies the concurrency level for updating instances during a rolling update. + // Available levels: + // + // - `Serial`: Updates instances one at a time, ensuring minimal downtime by waiting for each instance to become ready + // before updating the next. + // - `Parallel`: Updates all instances simultaneously, optimizing for speed but potentially reducing availability + // during the update. + // - `BestEffortParallel`: Updates instances concurrently with a limit on simultaneous updates to ensure a minimum + // number of operational replicas for maintaining quorum. + // For example, in a 5-instances setup, updating a maximum of 2 instances simultaneously keeps + // at least 3 operational for quorum. + // + // Defaults to 'Serial'. + // + // +kubebuilder:validation:Enum={Serial,Parallel,BestEffortParallel} + // +kubebuilder:default=Serial + // +optional + UpdateConcurrency *UpdateConcurrency `json:"updateConcurrency,omitempty"` + + // Defines what UpdateConcurrences are allowed to be specified in Component. + // + // +optional + UpdateConcurrencesAllowed []UpdateConcurrency `json:"updateConcurrencesAllowed,omitempty"` + + // The maximum number of instances that can be unavailable during the update. + // Value can be an absolute number (ex: 5) or a percentage of desired instances (ex: 10%). + // Absolute number is calculated from percentage by rounding up. This can not be 0. + // Defaults to 1. The field applies to all instances. That means if there is any unavailable pod, + // it will be counted towards MaxUnavailable. + // + // +optional + MaxUnavailable *string `json:"maxUnavailable,omitempty"` + + // Defines the upper bound of the MaxUnavailable in Component. + // + // +optional + MaxUnavailableLimit *string `json:"maxUnavailableLimit,omitempty"` +} diff --git a/apis/apps/v1/zz_generated.deepcopy.go b/apis/apps/v1/zz_generated.deepcopy.go index a908b87111c..c4a81a7b534 100644 --- a/apis/apps/v1/zz_generated.deepcopy.go +++ b/apis/apps/v1/zz_generated.deepcopy.go @@ -1196,8 +1196,8 @@ func (in *ComponentDefinitionSpec) DeepCopyInto(out *ComponentDefinitionSpec) { *out = make([]ReplicaRole, len(*in)) copy(*out, *in) } - if in.UpdateStrategy != nil { - in, out := &in.UpdateStrategy, &out.UpdateStrategy + if in.UpdateStrategyConstraint != nil { + in, out := &in.UpdateStrategyConstraint, &out.UpdateStrategyConstraint *out = new(UpdateConcurrency) **out = **in } diff --git a/controllers/apps/componentdefinition_controller_test.go b/controllers/apps/componentdefinition_controller_test.go index a847daf5f75..66ae9938fa7 100644 --- a/controllers/apps/componentdefinition_controller_test.go +++ b/controllers/apps/componentdefinition_controller_test.go @@ -523,7 +523,7 @@ var _ = Describe("ComponentDefinition Controller", func() { cmpd.Spec.Description = "v0.0.2" cmpd.Spec.Runtime.Containers[0].Image = "image:v0.0.2" parallel := kbappsv1.ParallelConcurrency - cmpd.Spec.UpdateStrategy = ¶llel + cmpd.Spec.UpdateStrategyConstraint = ¶llel })()).Should(Succeed()) By(fmt.Sprintf("checking the updated object as %s", strings.ToLower(string(kbappsv1.AvailablePhase)))) @@ -538,8 +538,8 @@ var _ = Describe("ComponentDefinition Controller", func() { Command: []string{"command"}, } g.Expect(cmpd.Spec.Runtime.Containers[0]).Should(BeEquivalentTo(c)) - g.Expect(cmpd.Spec.UpdateStrategy).ShouldNot(BeNil()) - g.Expect(*cmpd.Spec.UpdateStrategy).Should(Equal(kbappsv1.ParallelConcurrency)) + g.Expect(cmpd.Spec.UpdateStrategyConstraint).ShouldNot(BeNil()) + g.Expect(*cmpd.Spec.UpdateStrategyConstraint).Should(Equal(kbappsv1.ParallelConcurrency)) })).Should(Succeed()) }) @@ -551,7 +551,7 @@ var _ = Describe("ComponentDefinition Controller", func() { cmpd.Spec.Description = "v0.0.2" cmpd.Spec.Runtime.Containers[0].Image = "image:v0.0.2" parallel := kbappsv1.ParallelConcurrency - cmpd.Spec.UpdateStrategy = ¶llel + cmpd.Spec.UpdateStrategyConstraint = ¶llel })()).Should(Succeed()) By(fmt.Sprintf("checking the updated object as %s", strings.ToLower(string(kbappsv1.UnavailablePhase)))) @@ -566,14 +566,14 @@ var _ = Describe("ComponentDefinition Controller", func() { Command: []string{"command"}, } g.Expect(cmpd.Spec.Runtime.Containers[0]).Should(BeEquivalentTo(c)) - g.Expect(cmpd.Spec.UpdateStrategy).ShouldNot(BeNil()) - g.Expect(*cmpd.Spec.UpdateStrategy).Should(Equal(kbappsv1.ParallelConcurrency)) + g.Expect(cmpd.Spec.UpdateStrategyConstraint).ShouldNot(BeNil()) + g.Expect(*cmpd.Spec.UpdateStrategyConstraint).Should(Equal(kbappsv1.ParallelConcurrency)) })).Should(Succeed()) By("revert the change to immutable fields back") Expect(testapps.GetAndChangeObj(&testCtx, client.ObjectKeyFromObject(componentDefObj), func(cmpd *kbappsv1.ComponentDefinition) { cmpd.Spec.Runtime.Containers[0].Image = "image:v0.0.1" - cmpd.Spec.UpdateStrategy = nil + cmpd.Spec.UpdateStrategyConstraint = nil })()).Should(Succeed()) By(fmt.Sprintf("checking the updated object as %s", strings.ToLower(string(kbappsv1.AvailablePhase)))) @@ -588,8 +588,8 @@ var _ = Describe("ComponentDefinition Controller", func() { Command: []string{"command"}, } g.Expect(cmpd.Spec.Runtime.Containers[0]).Should(BeEquivalentTo(c)) - g.Expect(cmpd.Spec.UpdateStrategy).ShouldNot(BeNil()) - g.Expect(*cmpd.Spec.UpdateStrategy).Should(Equal(kbappsv1.SerialConcurrency)) + g.Expect(cmpd.Spec.UpdateStrategyConstraint).ShouldNot(BeNil()) + g.Expect(*cmpd.Spec.UpdateStrategyConstraint).Should(Equal(kbappsv1.SerialConcurrency)) })).Should(Succeed()) }) }) diff --git a/pkg/controller/builder/builder_component_definition.go b/pkg/controller/builder/builder_component_definition.go index c8087da54b6..d1aaa6eff5a 100644 --- a/pkg/controller/builder/builder_component_definition.go +++ b/pkg/controller/builder/builder_component_definition.go @@ -206,7 +206,7 @@ func (builder *ComponentDefinitionBuilder) AddSystemAccount(accountName string, } func (builder *ComponentDefinitionBuilder) SetUpdateStrategy(strategy *appsv1.UpdateConcurrency) *ComponentDefinitionBuilder { - builder.get().Spec.UpdateStrategy = strategy + builder.get().Spec.UpdateStrategyConstraint = strategy return builder } diff --git a/pkg/controller/component/synthesize_component.go b/pkg/controller/component/synthesize_component.go index f3f102ccd8f..18f248dac13 100644 --- a/pkg/controller/component/synthesize_component.go +++ b/pkg/controller/component/synthesize_component.go @@ -91,7 +91,7 @@ func BuildSynthesizedComponent(ctx context.Context, cli client.Reader, ConfigTemplates: compDefObj.Spec.Configs, ScriptTemplates: compDefObj.Spec.Scripts, Roles: compDefObj.Spec.Roles, - UpdateStrategy: compDefObj.Spec.UpdateStrategy, + UpdateStrategy: compDefObj.Spec.UpdateStrategyConstraint, MinReadySeconds: compDefObj.Spec.MinReadySeconds, PolicyRules: compDefObj.Spec.PolicyRules, LifecycleActions: compDefObj.Spec.LifecycleActions, diff --git a/pkg/testutil/apps/componentdefinition_factory.go b/pkg/testutil/apps/componentdefinition_factory.go index 3f177cf257d..1aa6b160a66 100644 --- a/pkg/testutil/apps/componentdefinition_factory.go +++ b/pkg/testutil/apps/componentdefinition_factory.go @@ -277,7 +277,7 @@ func (f *MockComponentDefinitionFactory) AddSystemAccount(accountName string, in } func (f *MockComponentDefinitionFactory) SetUpdateStrategy(strategy *kbappsv1.UpdateConcurrency) *MockComponentDefinitionFactory { - f.Get().Spec.UpdateStrategy = strategy + f.Get().Spec.UpdateStrategyConstraint = strategy return f } diff --git a/pkg/testutil/apps/constant.go b/pkg/testutil/apps/constant.go index f833612f73f..8e6ad00814f 100644 --- a/pkg/testutil/apps/constant.go +++ b/pkg/testutil/apps/constant.go @@ -206,7 +206,7 @@ var ( }, }, }, - UpdateStrategy: &[]appsv1.UpdateConcurrency{appsv1.BestEffortParallelConcurrency}[0], + UpdateStrategyConstraint: &[]appsv1.UpdateConcurrency{appsv1.BestEffortParallelConcurrency}[0], Roles: []appsv1.ReplicaRole{ { Name: "leader", From 4f3546d7d352556a9d56fc9b1c2f8a6e576e5050 Mon Sep 17 00:00:00 2001 From: free6om Date: Fri, 6 Dec 2024 10:20:33 +0800 Subject: [PATCH 05/11] flattern update strategy in cmpd --- apis/apps/v1/componentdefinition_types.go | 95 +++++++---------------- 1 file changed, 27 insertions(+), 68 deletions(-) diff --git a/apis/apps/v1/componentdefinition_types.go b/apis/apps/v1/componentdefinition_types.go index 6defdb14251..6c129bf2d38 100644 --- a/apis/apps/v1/componentdefinition_types.go +++ b/apis/apps/v1/componentdefinition_types.go @@ -444,12 +444,36 @@ type ComponentDefinitionSpec struct { // +optional MinReadySeconds int32 `json:"minReadySeconds,omitempty"` - // Specifies constraint on Component's UpdateStrategy. + // Specifies how an instance should be updated. // - // This field is immutable. + // - `StrictInPlace` indicates that only allows in-place update. + // Any attempt to modify other fields that not support in-place update will be rejected. + // - `PreferInPlace` indicates that we will first attempt an in-place update of the instance. + // If that fails, it will fall back to the ReCreate, where instance will be recreated. + // Default value is "PreferInPlace". + // + // +kubebuilder:validation:Enum={StrictInPlace,PreferInPlace} + // +optional + InstanceUpdatePolicy *InstanceUpdatePolicyType `json:"instanceUpdatePolicy,omitempty"` + + // Specifies the concurrency level for updating instances during a rolling update. + // Available levels: + // + // - `Serial`: Updates instances one at a time, ensuring minimal downtime by waiting for each instance to become ready + // before updating the next. + // - `Parallel`: Updates all instances simultaneously, optimizing for speed but potentially reducing availability + // during the update. + // - `BestEffortParallel`: Updates instances concurrently with a limit on simultaneous updates to ensure a minimum + // number of operational replicas for maintaining quorum. + // For example, in a 5-instances setup, updating a maximum of 2 instances simultaneously keeps + // at least 3 operational for quorum. + // + // Defaults to 'Serial'. // + // +kubebuilder:validation:Enum={Serial,Parallel,BestEffortParallel} + // +kubebuilder:default=Serial // +optional - UpdateStrategyConstraint *UpdateStrategyConstraint `json:"updateStrategyConstraint,omitempty"` + UpdateConcurrency *UpdateConcurrency `json:"updateConcurrency,omitempty"` // InstanceSet controls the creation of pods during initial scale up, replacement of pods on nodes, and scaling down. // @@ -2087,68 +2111,3 @@ const ( HTTPProtocol PrometheusScheme = "http" HTTPSProtocol PrometheusScheme = "https" ) - -// UpdateStrategyConstraint defines constraint on Component's UpdateStrategy. -type UpdateStrategyConstraint struct { - // Specifies how an instance should be updated. - // - // - `StrictInPlace` indicates that only allows in-place update. - // Any attempt to modify other fields that not support in-place update will be rejected. - // - `PreferInPlace` indicates that we will first attempt an in-place update of the instance. - // If that fails, it will fall back to the ReCreate, where instance will be recreated. - // Default value is "PreferInPlace". - // - // +kubebuilder:validation:Enum={StrictInPlace,PreferInPlace} - // +optional - InstanceUpdatePolicy *InstanceUpdatePolicyType `json:"instanceUpdatePolicy,omitempty"` - - // Defines what InstanceUpdatePolicyTypes are allowed to be specified in Component. - // - // +optional - InstanceUpdatePoliciesAllowed []InstanceUpdatePolicyType `json:"instanceUpdatePoliciesAllowed,omitempty"` - - // Specifies constraint on Component's RollingUpdate. - // - // +optional - RollingUpdateConstraint *RollingUpdateConstraint `json:"rollingUpdateConstraint,omitempty"` -} - -type RollingUpdateConstraint struct { - // Specifies the concurrency level for updating instances during a rolling update. - // Available levels: - // - // - `Serial`: Updates instances one at a time, ensuring minimal downtime by waiting for each instance to become ready - // before updating the next. - // - `Parallel`: Updates all instances simultaneously, optimizing for speed but potentially reducing availability - // during the update. - // - `BestEffortParallel`: Updates instances concurrently with a limit on simultaneous updates to ensure a minimum - // number of operational replicas for maintaining quorum. - // For example, in a 5-instances setup, updating a maximum of 2 instances simultaneously keeps - // at least 3 operational for quorum. - // - // Defaults to 'Serial'. - // - // +kubebuilder:validation:Enum={Serial,Parallel,BestEffortParallel} - // +kubebuilder:default=Serial - // +optional - UpdateConcurrency *UpdateConcurrency `json:"updateConcurrency,omitempty"` - - // Defines what UpdateConcurrences are allowed to be specified in Component. - // - // +optional - UpdateConcurrencesAllowed []UpdateConcurrency `json:"updateConcurrencesAllowed,omitempty"` - - // The maximum number of instances that can be unavailable during the update. - // Value can be an absolute number (ex: 5) or a percentage of desired instances (ex: 10%). - // Absolute number is calculated from percentage by rounding up. This can not be 0. - // Defaults to 1. The field applies to all instances. That means if there is any unavailable pod, - // it will be counted towards MaxUnavailable. - // - // +optional - MaxUnavailable *string `json:"maxUnavailable,omitempty"` - - // Defines the upper bound of the MaxUnavailable in Component. - // - // +optional - MaxUnavailableLimit *string `json:"maxUnavailableLimit,omitempty"` -} From 0a07c96915512af0828369a567b0b36a1ed555b1 Mon Sep 17 00:00:00 2001 From: free6om Date: Wed, 11 Dec 2024 15:41:28 +0800 Subject: [PATCH 06/11] adjust fields name in controllers according to apis --- apis/apps/v1/zz_generated.deepcopy.go | 82 +- apis/workloads/v1/instanceset_types.go | 147 +++- apis/workloads/v1/zz_generated.deepcopy.go | 66 +- .../bases/apps.kubeblocks.io_clusters.yaml | 175 +++- ...ps.kubeblocks.io_componentdefinitions.yaml | 52 +- .../bases/apps.kubeblocks.io_components.yaml | 82 +- .../workloads.kubeblocks.io_instancesets.yaml | 107 +-- .../componentdefinition_controller_test.go | 20 +- .../apps/transformer_cluster_component.go | 2 +- .../apps/transformer_component_workload.go | 5 +- .../crds/apps.kubeblocks.io_clusters.yaml | 175 +++- ...ps.kubeblocks.io_componentdefinitions.yaml | 52 +- .../crds/apps.kubeblocks.io_components.yaml | 82 +- .../workloads.kubeblocks.io_instancesets.yaml | 107 +-- docs/developer_docs/api-reference/cluster.md | 773 +++++++++++------- pkg/controller/builder/builder_component.go | 4 +- .../builder/builder_component_definition.go | 4 +- .../builder/builder_instance_set.go | 20 +- .../builder/builder_instance_set_test.go | 39 +- pkg/controller/component/component.go | 2 +- pkg/controller/component/its_convertor.go | 71 +- .../component/synthesize_component.go | 41 +- pkg/controller/component/type.go | 21 +- pkg/controller/factory/builder_test.go | 10 +- .../instanceset/reconciler_update.go | 61 +- .../instanceset/reconciler_update_test.go | 35 +- pkg/controller/instanceset/update_plan.go | 15 +- .../instanceset/update_plan_test.go | 40 +- pkg/controller/instanceset/utils.go | 10 + .../apps/componentdefinition_factory.go | 4 +- pkg/testutil/apps/constant.go | 3 +- pkg/testutil/apps/instance_set_factoy.go | 4 - 32 files changed, 1550 insertions(+), 761 deletions(-) diff --git a/apis/apps/v1/zz_generated.deepcopy.go b/apis/apps/v1/zz_generated.deepcopy.go index c4a81a7b534..e3b9e59f620 100644 --- a/apis/apps/v1/zz_generated.deepcopy.go +++ b/apis/apps/v1/zz_generated.deepcopy.go @@ -324,10 +324,10 @@ func (in *ClusterComponentSpec) DeepCopyInto(out *ClusterComponentSpec) { *out = new(intstr.IntOrString) **out = **in } - if in.PodUpdatePolicy != nil { - in, out := &in.PodUpdatePolicy, &out.PodUpdatePolicy - *out = new(InstanceUpdatePolicyType) - **out = **in + if in.UpdateStrategy != nil { + in, out := &in.UpdateStrategy, &out.UpdateStrategy + *out = new(UpdateStrategy) + (*in).DeepCopyInto(*out) } if in.Instances != nil { in, out := &in.Instances, &out.Instances @@ -1196,8 +1196,13 @@ func (in *ComponentDefinitionSpec) DeepCopyInto(out *ComponentDefinitionSpec) { *out = make([]ReplicaRole, len(*in)) copy(*out, *in) } - if in.UpdateStrategyConstraint != nil { - in, out := &in.UpdateStrategyConstraint, &out.UpdateStrategyConstraint + if in.InstanceUpdatePolicy != nil { + in, out := &in.InstanceUpdatePolicy, &out.InstanceUpdatePolicy + *out = new(InstanceUpdatePolicyType) + **out = **in + } + if in.UpdateConcurrency != nil { + in, out := &in.UpdateConcurrency, &out.UpdateConcurrency *out = new(UpdateConcurrency) **out = **in } @@ -1462,16 +1467,16 @@ func (in *ComponentSpec) DeepCopyInto(out *ComponentSpec) { (*in)[i].DeepCopyInto(&(*out)[i]) } } + if in.UpdateStrategy != nil { + in, out := &in.UpdateStrategy, &out.UpdateStrategy + *out = new(UpdateStrategy) + (*in).DeepCopyInto(*out) + } if in.ParallelPodManagementConcurrency != nil { in, out := &in.ParallelPodManagementConcurrency, &out.ParallelPodManagementConcurrency *out = new(intstr.IntOrString) **out = **in } - if in.PodUpdatePolicy != nil { - in, out := &in.PodUpdatePolicy, &out.PodUpdatePolicy - *out = new(InstanceUpdatePolicyType) - **out = **in - } if in.SchedulingPolicy != nil { in, out := &in.SchedulingPolicy, &out.SchedulingPolicy *out = new(SchedulingPolicy) @@ -2455,6 +2460,36 @@ func (in *RoledVar) DeepCopy() *RoledVar { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RollingUpdate) DeepCopyInto(out *RollingUpdate) { + *out = *in + if in.Replicas != nil { + in, out := &in.Replicas, &out.Replicas + *out = new(intstr.IntOrString) + **out = **in + } + if in.UpdateConcurrency != nil { + in, out := &in.UpdateConcurrency, &out.UpdateConcurrency + *out = new(UpdateConcurrency) + **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 RollingUpdate. +func (in *RollingUpdate) DeepCopy() *RollingUpdate { + if in == nil { + return nil + } + out := new(RollingUpdate) + 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 @@ -3370,6 +3405,31 @@ func (in *TLSVars) DeepCopy() *TLSVars { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *UpdateStrategy) DeepCopyInto(out *UpdateStrategy) { + *out = *in + if in.InstanceUpdatePolicy != nil { + in, out := &in.InstanceUpdatePolicy, &out.InstanceUpdatePolicy + *out = new(InstanceUpdatePolicyType) + **out = **in + } + if in.RollingUpdate != nil { + in, out := &in.RollingUpdate, &out.RollingUpdate + *out = new(RollingUpdate) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UpdateStrategy. +func (in *UpdateStrategy) DeepCopy() *UpdateStrategy { + if in == nil { + return nil + } + out := new(UpdateStrategy) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *VarSource) DeepCopyInto(out *VarSource) { *out = *in diff --git a/apis/workloads/v1/instanceset_types.go b/apis/workloads/v1/instanceset_types.go index b67f4a2fcd0..80353cc84e3 100644 --- a/apis/workloads/v1/instanceset_types.go +++ b/apis/workloads/v1/instanceset_types.go @@ -171,24 +171,10 @@ type InstanceSetSpec struct { // +optional ParallelPodManagementConcurrency *intstr.IntOrString `json:"parallelPodManagementConcurrency,omitempty"` - // PodUpdatePolicy indicates how pods should be updated - // - // - `StrictInPlace` indicates that only allows in-place upgrades. - // Any attempt to modify other fields will be rejected. - // - `PreferInPlace` indicates that we will first attempt an in-place upgrade of the Pod. - // If that fails, it will fall back to the ReCreate, where pod will be recreated. - // Default value is "PreferInPlace" + // Provides fine-grained control over the spec update process of all instances. // // +optional - PodUpdatePolicy PodUpdatePolicyType `json:"podUpdatePolicy,omitempty"` - - // 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 - // - // Note: This field will be removed in future version. - UpdateStrategy appsv1.StatefulSetUpdateStrategy `json:"updateStrategy,omitempty"` + UpdateStrategy *UpdateStrategy `json:"updateStrategy,omitempty"` // A list of roles defined in the system. // @@ -205,16 +191,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"` @@ -456,16 +432,111 @@ type SchedulingPolicy struct { TopologySpreadConstraints []corev1.TopologySpreadConstraint `json:"topologySpreadConstraints,omitempty"` } -type PodUpdatePolicyType string +// UpdateStrategy defines fine-grained control over the spec update process of all instances. +type UpdateStrategy struct { + // Indicates how instances should be updated. + // + // - `StrictInPlace` indicates that only allows in-place update. + // Any attempt to modify other fields that not support in-place update will be rejected. + // - `PreferInPlace` indicates that we will first attempt an in-place update of the instance. + // If that fails, it will fall back to the ReCreate, where instance will be recreated. + // Default value is "PreferInPlace". + // + // +kubebuilder:validation:Enum={StrictInPlace,PreferInPlace} + // +optional + InstanceUpdatePolicy *InstanceUpdatePolicyType `json:"instanceUpdatePolicy,omitempty"` + + // Specifies how the rolling update should be applied. + // + // +optional + RollingUpdate *RollingUpdate `json:"rollingUpdate,omitempty"` +} + +// RollingUpdate specifies how the rolling update should be applied. +type RollingUpdate struct { + // Indicates the number of instances that should be updated during a rolling update. + // The remaining instances will remain untouched. This is helpful in defining how many instances + // should participate in the update process. + // Value can be an absolute number (ex: 5) or a percentage of desired instances (ex: 10%). + // Absolute number is calculated from percentage by rounding up. + // The default value is ComponentSpec.Replicas (i.e., update all instances). + // + // +optional + Replicas *intstr.IntOrString `json:"replicas,omitempty"` + + // Specifies the concurrency level for updating instances during a rolling update. + // Available levels: + // + // - `Serial`: Updates instances one at a time, ensuring minimal downtime by waiting for each instance to become ready + // before updating the next. + // - `Parallel`: Updates all instances simultaneously, optimizing for speed but potentially reducing availability + // during the update. + // - `BestEffortParallel`: Updates instances concurrently with a limit on simultaneous updates to ensure a minimum + // number of operational replicas for maintaining quorum. + // For example, in a 5-instances setup, updating a maximum of 2 instances simultaneously keeps + // at least 3 operational for quorum. + // + // Defaults to 'Serial'. + // + // +kubebuilder:validation:Enum={Serial,Parallel,BestEffortParallel} + // +kubebuilder:default=Serial + // +optional + UpdateConcurrency *UpdateConcurrency `json:"updateConcurrency,omitempty"` + + // The maximum number of instances that can be unavailable during the update. + // Value can be an absolute number (ex: 5) or a percentage of desired instances (ex: 10%). + // Absolute number is calculated from percentage by rounding up. This can not be 0. + // Defaults to 1. The field applies to all instances. That means if there is any unavailable pod, + // it will be counted towards MaxUnavailable. + // + // +optional + MaxUnavailable *intstr.IntOrString `json:"maxUnavailable,omitempty"` +} + +type InstanceUpdatePolicyType string const ( - // StrictInPlacePodUpdatePolicyType indicates that only allows in-place upgrades. - // Any attempt to modify other fields will be rejected. - StrictInPlacePodUpdatePolicyType PodUpdatePolicyType = "StrictInPlace" + // StrictInPlaceInstanceUpdatePolicyType indicates that only allows in-place update. + // Any attempt to modify other fields that not support in-place update will be rejected. + StrictInPlaceInstanceUpdatePolicyType InstanceUpdatePolicyType = "StrictInPlace" - // PreferInPlacePodUpdatePolicyType indicates that we will first attempt an in-place upgrade of the Pod. - // If that fails, it will fall back to the ReCreate, where pod will be recreated. - PreferInPlacePodUpdatePolicyType PodUpdatePolicyType = "PreferInPlace" + // PreferInPlaceInstanceUpdatePolicyType indicates that we will first attempt an in-place update of the instance. + // If that fails, it will fall back to the ReCreate, where instance will be recreated. + PreferInPlaceInstanceUpdatePolicyType InstanceUpdatePolicyType = "PreferInPlace" +) + +// UpdateConcurrency defines the update concurrency level for cluster components. This concurrency level determines how updates are applied +// across the cluster. +// The available concurrency levels are `Serial`, `BestEffortParallel`, and `Parallel`. +// +// +enum +// +kubebuilder:validation:Enum={Serial,BestEffortParallel,Parallel} +type UpdateConcurrency string + +const ( + // SerialConcurrency indicates that updates are applied one at a time in a sequential manner. + // The operator waits for each replica to be updated and ready before proceeding to the next one. + // This ensures that only one replica is unavailable at a time during the update process. + SerialConcurrency UpdateConcurrency = "Serial" + + // ParallelConcurrency indicates that updates are applied simultaneously to all Pods of a Component. + // The replicas are updated in parallel, with the operator updating all replicas concurrently. + // This strategy provides the fastest update time but may lead to a period of reduced availability or + // capacity during the update process. + ParallelConcurrency UpdateConcurrency = "Parallel" + + // BestEffortParallelConcurrency indicates that the replicas are updated in parallel, with the operator making + // a best-effort attempt to update as many replicas as possible concurrently + // while maintaining the component's availability. + // Unlike the `Parallel` strategy, the `BestEffortParallel` strategy aims to ensure that a minimum number + // of replicas remain available during the update process to maintain the component's quorum and functionality. + // + // For example, consider a component with 5 replicas. To maintain the component's availability and quorum, + // the operator may allow a maximum of 2 replicas to be simultaneously updated. This ensures that at least + // 3 replicas (a quorum) remain available and functional during the update process. + // + // The `BestEffortParallel` strategy strikes a balance between update speed and component availability. + BestEffortParallelConcurrency UpdateConcurrency = "BestEffortParallel" ) type ReplicaRole struct { @@ -628,16 +699,6 @@ type MembershipReconfiguration struct { PromoteAction *Action `json:"promoteAction,omitempty"` } -// MemberUpdateStrategy defines Cluster Component update strategy. -// +enum -type MemberUpdateStrategy string - -const ( - SerialUpdateStrategy MemberUpdateStrategy = "Serial" - BestEffortParallelUpdateStrategy MemberUpdateStrategy = "BestEffortParallel" - ParallelUpdateStrategy MemberUpdateStrategy = "Parallel" -) - type Credential struct { // Defines the user's name for the credential. // The corresponding environment variable will be KB_ITS_USERNAME. diff --git a/apis/workloads/v1/zz_generated.deepcopy.go b/apis/workloads/v1/zz_generated.deepcopy.go index f36aeda179e..8d306d6131f 100644 --- a/apis/workloads/v1/zz_generated.deepcopy.go +++ b/apis/workloads/v1/zz_generated.deepcopy.go @@ -190,7 +190,11 @@ func (in *InstanceSetSpec) DeepCopyInto(out *InstanceSetSpec) { *out = new(intstr.IntOrString) **out = **in } - in.UpdateStrategy.DeepCopyInto(&out.UpdateStrategy) + if in.UpdateStrategy != nil { + in, out := &in.UpdateStrategy, &out.UpdateStrategy + *out = new(UpdateStrategy) + (*in).DeepCopyInto(*out) + } if in.Roles != nil { in, out := &in.Roles, &out.Roles *out = make([]ReplicaRole, len(*in)) @@ -206,11 +210,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) @@ -506,6 +505,36 @@ 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 *RollingUpdate) DeepCopyInto(out *RollingUpdate) { + *out = *in + if in.Replicas != nil { + in, out := &in.Replicas, &out.Replicas + *out = new(intstr.IntOrString) + **out = **in + } + if in.UpdateConcurrency != nil { + in, out := &in.UpdateConcurrency, &out.UpdateConcurrency + *out = new(UpdateConcurrency) + **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 RollingUpdate. +func (in *RollingUpdate) DeepCopy() *RollingUpdate { + if in == nil { + return nil + } + out := new(RollingUpdate) + 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 @@ -546,3 +575,28 @@ func (in *SchedulingPolicy) DeepCopy() *SchedulingPolicy { in.DeepCopyInto(out) return out } + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *UpdateStrategy) DeepCopyInto(out *UpdateStrategy) { + *out = *in + if in.InstanceUpdatePolicy != nil { + in, out := &in.InstanceUpdatePolicy, &out.InstanceUpdatePolicy + *out = new(InstanceUpdatePolicyType) + **out = **in + } + if in.RollingUpdate != nil { + in, out := &in.RollingUpdate, &out.RollingUpdate + *out = new(RollingUpdate) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UpdateStrategy. +func (in *UpdateStrategy) DeepCopy() *UpdateStrategy { + if in == nil { + return nil + } + out := new(UpdateStrategy) + in.DeepCopyInto(out) + return out +} diff --git a/config/crd/bases/apps.kubeblocks.io_clusters.yaml b/config/crd/bases/apps.kubeblocks.io_clusters.yaml index 2d4cc34c940..a8f51511f11 100644 --- a/config/crd/bases/apps.kubeblocks.io_clusters.yaml +++ b/config/crd/bases/apps.kubeblocks.io_clusters.yaml @@ -3783,20 +3783,6 @@ spec: or when scaling down. It only used when `PodManagementPolicy` is set to `Parallel`. The default Concurrency is 100%. x-kubernetes-int-or-string: true - podUpdatePolicy: - description: |- - PodUpdatePolicy indicates how pods should be updated - - - - `StrictInPlace` indicates that only allows in-place upgrades. - Any attempt to modify other fields will be rejected. - - `PreferInPlace` indicates that we will first attempt an in-place upgrade of the Pod. - If that fails, it will fall back to the ReCreate, where pod will be recreated. - Default value is "PreferInPlace" - enum: - - StrictInPlace - - PreferInPlace - type: string replicas: default: 1 description: Specifies the desired number of replicas in the @@ -5352,6 +5338,79 @@ spec: If TLS is enabled, the Component may require additional configuration, such as specifying TLS certificates and keys, to properly set up the secure communication channel. type: boolean + updateStrategy: + description: Provides fine-grained control over the spec update + process of all instances. + properties: + instanceUpdatePolicy: + description: |- + Indicates how instances should be updated. + + + - `StrictInPlace` indicates that only allows in-place update. + Any attempt to modify other fields that not support in-place update will be rejected. + - `PreferInPlace` indicates that we will first attempt an in-place update of the instance. + If that fails, it will fall back to the ReCreate, where instance will be recreated. + Default value is "PreferInPlace". + enum: + - StrictInPlace + - PreferInPlace + type: string + rollingUpdate: + description: Specifies how the rolling update should be + applied. + properties: + maxUnavailable: + anyOf: + - type: integer + - type: string + description: |- + The maximum number of instances that can be unavailable during the update. + Value can be an absolute number (ex: 5) or a percentage of desired instances (ex: 10%). + Absolute number is calculated from percentage by rounding up. This can not be 0. + Defaults to 1. The field applies to all instances. That means if there is any unavailable pod, + it will be counted towards MaxUnavailable. + x-kubernetes-int-or-string: true + replicas: + anyOf: + - type: integer + - type: string + description: |- + Indicates the number of instances that should be updated during a rolling update. + The remaining instances will remain untouched. This is helpful in defining how many instances + should participate in the update process. + Value can be an absolute number (ex: 5) or a percentage of desired instances (ex: 10%). + Absolute number is calculated from percentage by rounding up. + The default value is ComponentSpec.Replicas (i.e., update all instances). + x-kubernetes-int-or-string: true + updateConcurrency: + allOf: + - enum: + - Serial + - BestEffortParallel + - Parallel + - enum: + - Serial + - Parallel + - BestEffortParallel + default: Serial + description: "Specifies the concurrency level for updating + instances during a rolling update.\nAvailable levels:\n\n\n- + `Serial`: Updates instances one at a time, ensuring + minimal downtime by waiting for each instance to become + ready\n before updating the next.\n- `Parallel`: + Updates all instances simultaneously, optimizing for + speed but potentially reducing availability\n during + the update.\n- `BestEffortParallel`: Updates instances + concurrently with a limit on simultaneous updates + to ensure a minimum\n number of operational replicas + for maintaining quorum.\n\t For example, in a 5-instances + setup, updating a maximum of 2 instances simultaneously + keeps\n\t at least 3 operational for quorum.\n\n\nDefaults + to 'Serial'." + type: string + type: object + type: object volumeClaimTemplates: description: |- Specifies a list of PersistentVolumeClaim templates that represent the storage requirements for the Component. @@ -12473,20 +12532,6 @@ spec: or when scaling down. It only used when `PodManagementPolicy` is set to `Parallel`. The default Concurrency is 100%. x-kubernetes-int-or-string: true - podUpdatePolicy: - description: |- - PodUpdatePolicy indicates how pods should be updated - - - - `StrictInPlace` indicates that only allows in-place upgrades. - Any attempt to modify other fields will be rejected. - - `PreferInPlace` indicates that we will first attempt an in-place upgrade of the Pod. - If that fails, it will fall back to the ReCreate, where pod will be recreated. - Default value is "PreferInPlace" - enum: - - StrictInPlace - - PreferInPlace - type: string replicas: default: 1 description: Specifies the desired number of replicas in @@ -14054,6 +14099,80 @@ spec: If TLS is enabled, the Component may require additional configuration, such as specifying TLS certificates and keys, to properly set up the secure communication channel. type: boolean + updateStrategy: + description: Provides fine-grained control over the spec + update process of all instances. + properties: + instanceUpdatePolicy: + description: |- + Indicates how instances should be updated. + + + - `StrictInPlace` indicates that only allows in-place update. + Any attempt to modify other fields that not support in-place update will be rejected. + - `PreferInPlace` indicates that we will first attempt an in-place update of the instance. + If that fails, it will fall back to the ReCreate, where instance will be recreated. + Default value is "PreferInPlace". + enum: + - StrictInPlace + - PreferInPlace + type: string + rollingUpdate: + description: Specifies how the rolling update should + be applied. + properties: + maxUnavailable: + anyOf: + - type: integer + - type: string + description: |- + The maximum number of instances that can be unavailable during the update. + Value can be an absolute number (ex: 5) or a percentage of desired instances (ex: 10%). + Absolute number is calculated from percentage by rounding up. This can not be 0. + Defaults to 1. The field applies to all instances. That means if there is any unavailable pod, + it will be counted towards MaxUnavailable. + x-kubernetes-int-or-string: true + replicas: + anyOf: + - type: integer + - type: string + description: |- + Indicates the number of instances that should be updated during a rolling update. + The remaining instances will remain untouched. This is helpful in defining how many instances + should participate in the update process. + Value can be an absolute number (ex: 5) or a percentage of desired instances (ex: 10%). + Absolute number is calculated from percentage by rounding up. + The default value is ComponentSpec.Replicas (i.e., update all instances). + x-kubernetes-int-or-string: true + updateConcurrency: + allOf: + - enum: + - Serial + - BestEffortParallel + - Parallel + - enum: + - Serial + - Parallel + - BestEffortParallel + default: Serial + description: "Specifies the concurrency level for + updating instances during a rolling update.\nAvailable + levels:\n\n\n- `Serial`: Updates instances one + at a time, ensuring minimal downtime by waiting + for each instance to become ready\n before updating + the next.\n- `Parallel`: Updates all instances + simultaneously, optimizing for speed but potentially + reducing availability\n during the update.\n- + `BestEffortParallel`: Updates instances concurrently + with a limit on simultaneous updates to ensure + a minimum\n number of operational replicas for + maintaining quorum.\n\t For example, in a 5-instances + setup, updating a maximum of 2 instances simultaneously + keeps\n\t at least 3 operational for quorum.\n\n\nDefaults + to 'Serial'." + type: string + type: object + type: object volumeClaimTemplates: description: |- Specifies a list of PersistentVolumeClaim templates that represent the storage requirements for the Component. diff --git a/config/crd/bases/apps.kubeblocks.io_componentdefinitions.yaml b/config/crd/bases/apps.kubeblocks.io_componentdefinitions.yaml index 636b9b48c8e..a592d737879 100644 --- a/config/crd/bases/apps.kubeblocks.io_componentdefinitions.yaml +++ b/config/crd/bases/apps.kubeblocks.io_componentdefinitions.yaml @@ -4489,6 +4489,20 @@ spec: type: object type: array type: object + instanceUpdatePolicy: + description: |- + Specifies how an instance should be updated. + + + - `StrictInPlace` indicates that only allows in-place update. + Any attempt to modify other fields that not support in-place update will be rejected. + - `PreferInPlace` indicates that we will first attempt an in-place update of the instance. + If that fails, it will fall back to the ReCreate, where instance will be recreated. + Default value is "PreferInPlace". + enum: + - StrictInPlace + - PreferInPlace + type: string labels: additionalProperties: type: string @@ -16847,24 +16861,28 @@ spec: - mountPath - volumeName type: object - updateStrategy: + updateConcurrency: + allOf: + - enum: + - Serial + - BestEffortParallel + - Parallel + - enum: + - Serial + - Parallel + - BestEffortParallel default: Serial - description: "Specifies the concurrency strategy for updating multiple - instances of the Component.\nAvailable strategies:\n\n\n- `Serial`: - Updates replicas one at a time, ensuring minimal downtime by waiting - for each replica to become ready\n before updating the next.\n- - `Parallel`: Updates all replicas simultaneously, optimizing for - speed but potentially reducing availability\n during the update.\n- - `BestEffortParallel`: Updates replicas concurrently with a limit - on simultaneous updates to ensure a minimum\n number of operational - replicas for maintaining quorum.\n\t For example, in a 5-replica - component, updating a maximum of 2 replicas simultaneously keeps\n\t - at least 3 operational for quorum.\n\n\nThis field is immutable - and defaults to 'Serial'." - enum: - - Serial - - BestEffortParallel - - Parallel + description: "Specifies the concurrency level for updating instances + during a rolling update.\nAvailable levels:\n\n\n- `Serial`: Updates + instances one at a time, ensuring minimal downtime by waiting for + each instance to become ready\n before updating the next.\n- `Parallel`: + Updates all instances simultaneously, optimizing for speed but potentially + reducing availability\n during the update.\n- `BestEffortParallel`: + Updates instances concurrently with a limit on simultaneous updates + to ensure a minimum\n number of operational replicas for maintaining + quorum.\n\t For example, in a 5-instances setup, updating a maximum + of 2 instances simultaneously keeps\n\t at least 3 operational for + quorum.\n\n\nDefaults to 'Serial'." type: string vars: description: |- diff --git a/config/crd/bases/apps.kubeblocks.io_components.yaml b/config/crd/bases/apps.kubeblocks.io_components.yaml index 9da4fd1e563..ca5f89dbec8 100644 --- a/config/crd/bases/apps.kubeblocks.io_components.yaml +++ b/config/crd/bases/apps.kubeblocks.io_components.yaml @@ -3562,17 +3562,6 @@ spec: or when scaling down. It only used when `PodManagementPolicy` is set to `Parallel`. The default Concurrency is 100%. x-kubernetes-int-or-string: true - podUpdatePolicy: - description: |- - PodUpdatePolicy indicates how pods should be updated - - - - `StrictInPlace` indicates that only allows in-place upgrades. - Any attempt to modify other fields will be rejected. - - `PreferInPlace` indicates that we will first attempt an in-place upgrade of the Pod. - If that fails, it will fall back to the ReCreate, where pod will be recreated. - Default value is "PreferInPlace" - type: string replicas: default: 1 description: Specifies the desired number of replicas in the Component @@ -5612,6 +5601,77 @@ spec: - name type: object type: object + updateStrategy: + description: Provides fine-grained control over the spec update process + of all instances. + properties: + instanceUpdatePolicy: + description: |- + Indicates how instances should be updated. + + + - `StrictInPlace` indicates that only allows in-place update. + Any attempt to modify other fields that not support in-place update will be rejected. + - `PreferInPlace` indicates that we will first attempt an in-place update of the instance. + If that fails, it will fall back to the ReCreate, where instance will be recreated. + Default value is "PreferInPlace". + enum: + - StrictInPlace + - PreferInPlace + type: string + rollingUpdate: + description: Specifies how the rolling update should be applied. + properties: + maxUnavailable: + anyOf: + - type: integer + - type: string + description: |- + The maximum number of instances that can be unavailable during the update. + Value can be an absolute number (ex: 5) or a percentage of desired instances (ex: 10%). + Absolute number is calculated from percentage by rounding up. This can not be 0. + Defaults to 1. The field applies to all instances. That means if there is any unavailable pod, + it will be counted towards MaxUnavailable. + x-kubernetes-int-or-string: true + replicas: + anyOf: + - type: integer + - type: string + description: |- + Indicates the number of instances that should be updated during a rolling update. + The remaining instances will remain untouched. This is helpful in defining how many instances + should participate in the update process. + Value can be an absolute number (ex: 5) or a percentage of desired instances (ex: 10%). + Absolute number is calculated from percentage by rounding up. + The default value is ComponentSpec.Replicas (i.e., update all instances). + x-kubernetes-int-or-string: true + updateConcurrency: + allOf: + - enum: + - Serial + - BestEffortParallel + - Parallel + - enum: + - Serial + - Parallel + - BestEffortParallel + default: Serial + description: "Specifies the concurrency level for updating + instances during a rolling update.\nAvailable levels:\n\n\n- + `Serial`: Updates instances one at a time, ensuring minimal + downtime by waiting for each instance to become ready\n + \ before updating the next.\n- `Parallel`: Updates all instances + simultaneously, optimizing for speed but potentially reducing + availability\n during the update.\n- `BestEffortParallel`: + Updates instances concurrently with a limit on simultaneous + updates to ensure a minimum\n number of operational replicas + for maintaining quorum.\n\t For example, in a 5-instances + setup, updating a maximum of 2 instances simultaneously + keeps\n\t at least 3 operational for quorum.\n\n\nDefaults + to 'Serial'." + type: string + type: object + type: object volumeClaimTemplates: description: |- Specifies a list of PersistentVolumeClaim templates that define the storage requirements for the Component. diff --git a/config/crd/bases/workloads.kubeblocks.io_instancesets.yaml b/config/crd/bases/workloads.kubeblocks.io_instancesets.yaml index 3da51ffb9f8..be1d243810e 100644 --- a/config/crd/bases/workloads.kubeblocks.io_instancesets.yaml +++ b/config/crd/bases/workloads.kubeblocks.io_instancesets.yaml @@ -3941,19 +3941,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: @@ -4154,17 +4141,6 @@ spec: Note: This field will be removed in future version. type: string - podUpdatePolicy: - description: |- - PodUpdatePolicy indicates how pods should be updated - - - - `StrictInPlace` indicates that only allows in-place upgrades. - Any attempt to modify other fields will be rejected. - - `PreferInPlace` indicates that we will first attempt an in-place upgrade of the Pod. - If that fails, it will fall back to the ReCreate, where pod will be recreated. - Default value is "PreferInPlace" - type: string replicas: default: 1 description: |- @@ -11843,46 +11819,75 @@ spec: type: object type: object updateStrategy: - description: |- - 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 + description: Provides fine-grained control over the spec update process + of all instances. + properties: + instanceUpdatePolicy: + description: |- + Indicates how instances should be updated. - Note: This field will be removed in future version. - properties: + - `StrictInPlace` indicates that only allows in-place update. + Any attempt to modify other fields that not support in-place update will be rejected. + - `PreferInPlace` indicates that we will first attempt an in-place update of the instance. + If that fails, it will fall back to the ReCreate, where instance will be recreated. + Default value is "PreferInPlace". + enum: + - StrictInPlace + - PreferInPlace + type: string rollingUpdate: - description: RollingUpdate is used to communicate parameters when - Type is RollingUpdateStatefulSetStrategyType. + description: Specifies how the rolling update should be applied. 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%). + The maximum number of instances that can be unavailable during the update. + Value can be an absolute number (ex: 5) or a percentage of desired instances (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 instances. That means if there is any unavailable pod, + it will be counted towards MaxUnavailable. x-kubernetes-int-or-string: true - partition: + replicas: + anyOf: + - type: integer + - type: string 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 + Indicates the number of instances that should be updated during a rolling update. + The remaining instances will remain untouched. This is helpful in defining how many instances + should participate in the update process. + Value can be an absolute number (ex: 5) or a percentage of desired instances (ex: 10%). + Absolute number is calculated from percentage by rounding up. + The default value is ComponentSpec.Replicas (i.e., update all instances). + x-kubernetes-int-or-string: true + updateConcurrency: + allOf: + - enum: + - Serial + - BestEffortParallel + - Parallel + - enum: + - Serial + - Parallel + - BestEffortParallel + default: Serial + description: "Specifies the concurrency level for updating + instances during a rolling update.\nAvailable levels:\n\n\n- + `Serial`: Updates instances one at a time, ensuring minimal + downtime by waiting for each instance to become ready\n + \ before updating the next.\n- `Parallel`: Updates all instances + simultaneously, optimizing for speed but potentially reducing + availability\n during the update.\n- `BestEffortParallel`: + Updates instances concurrently with a limit on simultaneous + updates to ensure a minimum\n number of operational replicas + for maintaining quorum.\n\t For example, in a 5-instances + setup, updating a maximum of 2 instances simultaneously + keeps\n\t at least 3 operational for quorum.\n\n\nDefaults + to 'Serial'." + type: string type: object - type: - description: |- - Type indicates the type of the StatefulSetUpdateStrategy. - Default is RollingUpdate. - type: string type: object volumeClaimTemplates: description: |- diff --git a/controllers/apps/componentdefinition_controller_test.go b/controllers/apps/componentdefinition_controller_test.go index 66ae9938fa7..d4daa3085a6 100644 --- a/controllers/apps/componentdefinition_controller_test.go +++ b/controllers/apps/componentdefinition_controller_test.go @@ -473,7 +473,7 @@ var _ = Describe("ComponentDefinition Controller", func() { Image: "image:v0.0.1", Command: []string{"command"}, }). - SetUpdateStrategy(nil). + SetUpdateConcurrency(nil). SetPodManagementPolicy(nil) if processor != nil { processor(builder) @@ -523,7 +523,7 @@ var _ = Describe("ComponentDefinition Controller", func() { cmpd.Spec.Description = "v0.0.2" cmpd.Spec.Runtime.Containers[0].Image = "image:v0.0.2" parallel := kbappsv1.ParallelConcurrency - cmpd.Spec.UpdateStrategyConstraint = ¶llel + cmpd.Spec.UpdateConcurrency = ¶llel })()).Should(Succeed()) By(fmt.Sprintf("checking the updated object as %s", strings.ToLower(string(kbappsv1.AvailablePhase)))) @@ -538,8 +538,8 @@ var _ = Describe("ComponentDefinition Controller", func() { Command: []string{"command"}, } g.Expect(cmpd.Spec.Runtime.Containers[0]).Should(BeEquivalentTo(c)) - g.Expect(cmpd.Spec.UpdateStrategyConstraint).ShouldNot(BeNil()) - g.Expect(*cmpd.Spec.UpdateStrategyConstraint).Should(Equal(kbappsv1.ParallelConcurrency)) + g.Expect(cmpd.Spec.UpdateConcurrency).ShouldNot(BeNil()) + g.Expect(*cmpd.Spec.UpdateConcurrency).Should(Equal(kbappsv1.ParallelConcurrency)) })).Should(Succeed()) }) @@ -551,7 +551,7 @@ var _ = Describe("ComponentDefinition Controller", func() { cmpd.Spec.Description = "v0.0.2" cmpd.Spec.Runtime.Containers[0].Image = "image:v0.0.2" parallel := kbappsv1.ParallelConcurrency - cmpd.Spec.UpdateStrategyConstraint = ¶llel + cmpd.Spec.UpdateConcurrency = ¶llel })()).Should(Succeed()) By(fmt.Sprintf("checking the updated object as %s", strings.ToLower(string(kbappsv1.UnavailablePhase)))) @@ -566,14 +566,14 @@ var _ = Describe("ComponentDefinition Controller", func() { Command: []string{"command"}, } g.Expect(cmpd.Spec.Runtime.Containers[0]).Should(BeEquivalentTo(c)) - g.Expect(cmpd.Spec.UpdateStrategyConstraint).ShouldNot(BeNil()) - g.Expect(*cmpd.Spec.UpdateStrategyConstraint).Should(Equal(kbappsv1.ParallelConcurrency)) + g.Expect(cmpd.Spec.UpdateConcurrency).ShouldNot(BeNil()) + g.Expect(*cmpd.Spec.UpdateConcurrency).Should(Equal(kbappsv1.ParallelConcurrency)) })).Should(Succeed()) By("revert the change to immutable fields back") Expect(testapps.GetAndChangeObj(&testCtx, client.ObjectKeyFromObject(componentDefObj), func(cmpd *kbappsv1.ComponentDefinition) { cmpd.Spec.Runtime.Containers[0].Image = "image:v0.0.1" - cmpd.Spec.UpdateStrategyConstraint = nil + cmpd.Spec.UpdateConcurrency = nil })()).Should(Succeed()) By(fmt.Sprintf("checking the updated object as %s", strings.ToLower(string(kbappsv1.AvailablePhase)))) @@ -588,8 +588,8 @@ var _ = Describe("ComponentDefinition Controller", func() { Command: []string{"command"}, } g.Expect(cmpd.Spec.Runtime.Containers[0]).Should(BeEquivalentTo(c)) - g.Expect(cmpd.Spec.UpdateStrategyConstraint).ShouldNot(BeNil()) - g.Expect(*cmpd.Spec.UpdateStrategyConstraint).Should(Equal(kbappsv1.SerialConcurrency)) + g.Expect(cmpd.Spec.UpdateConcurrency).ShouldNot(BeNil()) + g.Expect(*cmpd.Spec.UpdateConcurrency).Should(Equal(kbappsv1.SerialConcurrency)) })).Should(Succeed()) }) }) diff --git a/controllers/apps/transformer_cluster_component.go b/controllers/apps/transformer_cluster_component.go index e359a43dde2..1a0cc8a57b5 100644 --- a/controllers/apps/transformer_cluster_component.go +++ b/controllers/apps/transformer_cluster_component.go @@ -200,7 +200,7 @@ func copyAndMergeComponent(oldCompObj, newCompObj *appsv1.Component) *appsv1.Com compObjCopy.Spec.Configs = compProto.Spec.Configs compObjCopy.Spec.ServiceAccountName = compProto.Spec.ServiceAccountName compObjCopy.Spec.ParallelPodManagementConcurrency = compProto.Spec.ParallelPodManagementConcurrency - compObjCopy.Spec.PodUpdatePolicy = compProto.Spec.PodUpdatePolicy + compObjCopy.Spec.UpdateStrategy = compProto.Spec.UpdateStrategy compObjCopy.Spec.SchedulingPolicy = compProto.Spec.SchedulingPolicy compObjCopy.Spec.TLSConfig = compProto.Spec.TLSConfig compObjCopy.Spec.Instances = compProto.Spec.Instances diff --git a/controllers/apps/transformer_component_workload.go b/controllers/apps/transformer_component_workload.go index ea995677519..aac83613769 100644 --- a/controllers/apps/transformer_component_workload.go +++ b/controllers/apps/transformer_component_workload.go @@ -461,16 +461,15 @@ func copyAndMergeITS(oldITS, newITS *workloads.InstanceSet) *workloads.InstanceS 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 itsObjCopy.Spec.MinReadySeconds = itsProto.Spec.MinReadySeconds itsObjCopy.Spec.VolumeClaimTemplates = itsProto.Spec.VolumeClaimTemplates itsObjCopy.Spec.ParallelPodManagementConcurrency = itsProto.Spec.ParallelPodManagementConcurrency - itsObjCopy.Spec.PodUpdatePolicy = itsProto.Spec.PodUpdatePolicy + itsObjCopy.Spec.UpdateStrategy = itsProto.Spec.UpdateStrategy - if itsProto.Spec.UpdateStrategy.Type != "" || itsProto.Spec.UpdateStrategy.RollingUpdate != nil { + if itsProto.Spec.UpdateStrategy != nil || itsProto.Spec.UpdateStrategy.RollingUpdate != nil { updateUpdateStrategy(itsObjCopy, itsProto) } diff --git a/deploy/helm/crds/apps.kubeblocks.io_clusters.yaml b/deploy/helm/crds/apps.kubeblocks.io_clusters.yaml index 2d4cc34c940..a8f51511f11 100644 --- a/deploy/helm/crds/apps.kubeblocks.io_clusters.yaml +++ b/deploy/helm/crds/apps.kubeblocks.io_clusters.yaml @@ -3783,20 +3783,6 @@ spec: or when scaling down. It only used when `PodManagementPolicy` is set to `Parallel`. The default Concurrency is 100%. x-kubernetes-int-or-string: true - podUpdatePolicy: - description: |- - PodUpdatePolicy indicates how pods should be updated - - - - `StrictInPlace` indicates that only allows in-place upgrades. - Any attempt to modify other fields will be rejected. - - `PreferInPlace` indicates that we will first attempt an in-place upgrade of the Pod. - If that fails, it will fall back to the ReCreate, where pod will be recreated. - Default value is "PreferInPlace" - enum: - - StrictInPlace - - PreferInPlace - type: string replicas: default: 1 description: Specifies the desired number of replicas in the @@ -5352,6 +5338,79 @@ spec: If TLS is enabled, the Component may require additional configuration, such as specifying TLS certificates and keys, to properly set up the secure communication channel. type: boolean + updateStrategy: + description: Provides fine-grained control over the spec update + process of all instances. + properties: + instanceUpdatePolicy: + description: |- + Indicates how instances should be updated. + + + - `StrictInPlace` indicates that only allows in-place update. + Any attempt to modify other fields that not support in-place update will be rejected. + - `PreferInPlace` indicates that we will first attempt an in-place update of the instance. + If that fails, it will fall back to the ReCreate, where instance will be recreated. + Default value is "PreferInPlace". + enum: + - StrictInPlace + - PreferInPlace + type: string + rollingUpdate: + description: Specifies how the rolling update should be + applied. + properties: + maxUnavailable: + anyOf: + - type: integer + - type: string + description: |- + The maximum number of instances that can be unavailable during the update. + Value can be an absolute number (ex: 5) or a percentage of desired instances (ex: 10%). + Absolute number is calculated from percentage by rounding up. This can not be 0. + Defaults to 1. The field applies to all instances. That means if there is any unavailable pod, + it will be counted towards MaxUnavailable. + x-kubernetes-int-or-string: true + replicas: + anyOf: + - type: integer + - type: string + description: |- + Indicates the number of instances that should be updated during a rolling update. + The remaining instances will remain untouched. This is helpful in defining how many instances + should participate in the update process. + Value can be an absolute number (ex: 5) or a percentage of desired instances (ex: 10%). + Absolute number is calculated from percentage by rounding up. + The default value is ComponentSpec.Replicas (i.e., update all instances). + x-kubernetes-int-or-string: true + updateConcurrency: + allOf: + - enum: + - Serial + - BestEffortParallel + - Parallel + - enum: + - Serial + - Parallel + - BestEffortParallel + default: Serial + description: "Specifies the concurrency level for updating + instances during a rolling update.\nAvailable levels:\n\n\n- + `Serial`: Updates instances one at a time, ensuring + minimal downtime by waiting for each instance to become + ready\n before updating the next.\n- `Parallel`: + Updates all instances simultaneously, optimizing for + speed but potentially reducing availability\n during + the update.\n- `BestEffortParallel`: Updates instances + concurrently with a limit on simultaneous updates + to ensure a minimum\n number of operational replicas + for maintaining quorum.\n\t For example, in a 5-instances + setup, updating a maximum of 2 instances simultaneously + keeps\n\t at least 3 operational for quorum.\n\n\nDefaults + to 'Serial'." + type: string + type: object + type: object volumeClaimTemplates: description: |- Specifies a list of PersistentVolumeClaim templates that represent the storage requirements for the Component. @@ -12473,20 +12532,6 @@ spec: or when scaling down. It only used when `PodManagementPolicy` is set to `Parallel`. The default Concurrency is 100%. x-kubernetes-int-or-string: true - podUpdatePolicy: - description: |- - PodUpdatePolicy indicates how pods should be updated - - - - `StrictInPlace` indicates that only allows in-place upgrades. - Any attempt to modify other fields will be rejected. - - `PreferInPlace` indicates that we will first attempt an in-place upgrade of the Pod. - If that fails, it will fall back to the ReCreate, where pod will be recreated. - Default value is "PreferInPlace" - enum: - - StrictInPlace - - PreferInPlace - type: string replicas: default: 1 description: Specifies the desired number of replicas in @@ -14054,6 +14099,80 @@ spec: If TLS is enabled, the Component may require additional configuration, such as specifying TLS certificates and keys, to properly set up the secure communication channel. type: boolean + updateStrategy: + description: Provides fine-grained control over the spec + update process of all instances. + properties: + instanceUpdatePolicy: + description: |- + Indicates how instances should be updated. + + + - `StrictInPlace` indicates that only allows in-place update. + Any attempt to modify other fields that not support in-place update will be rejected. + - `PreferInPlace` indicates that we will first attempt an in-place update of the instance. + If that fails, it will fall back to the ReCreate, where instance will be recreated. + Default value is "PreferInPlace". + enum: + - StrictInPlace + - PreferInPlace + type: string + rollingUpdate: + description: Specifies how the rolling update should + be applied. + properties: + maxUnavailable: + anyOf: + - type: integer + - type: string + description: |- + The maximum number of instances that can be unavailable during the update. + Value can be an absolute number (ex: 5) or a percentage of desired instances (ex: 10%). + Absolute number is calculated from percentage by rounding up. This can not be 0. + Defaults to 1. The field applies to all instances. That means if there is any unavailable pod, + it will be counted towards MaxUnavailable. + x-kubernetes-int-or-string: true + replicas: + anyOf: + - type: integer + - type: string + description: |- + Indicates the number of instances that should be updated during a rolling update. + The remaining instances will remain untouched. This is helpful in defining how many instances + should participate in the update process. + Value can be an absolute number (ex: 5) or a percentage of desired instances (ex: 10%). + Absolute number is calculated from percentage by rounding up. + The default value is ComponentSpec.Replicas (i.e., update all instances). + x-kubernetes-int-or-string: true + updateConcurrency: + allOf: + - enum: + - Serial + - BestEffortParallel + - Parallel + - enum: + - Serial + - Parallel + - BestEffortParallel + default: Serial + description: "Specifies the concurrency level for + updating instances during a rolling update.\nAvailable + levels:\n\n\n- `Serial`: Updates instances one + at a time, ensuring minimal downtime by waiting + for each instance to become ready\n before updating + the next.\n- `Parallel`: Updates all instances + simultaneously, optimizing for speed but potentially + reducing availability\n during the update.\n- + `BestEffortParallel`: Updates instances concurrently + with a limit on simultaneous updates to ensure + a minimum\n number of operational replicas for + maintaining quorum.\n\t For example, in a 5-instances + setup, updating a maximum of 2 instances simultaneously + keeps\n\t at least 3 operational for quorum.\n\n\nDefaults + to 'Serial'." + type: string + type: object + type: object volumeClaimTemplates: description: |- Specifies a list of PersistentVolumeClaim templates that represent the storage requirements for the Component. diff --git a/deploy/helm/crds/apps.kubeblocks.io_componentdefinitions.yaml b/deploy/helm/crds/apps.kubeblocks.io_componentdefinitions.yaml index 636b9b48c8e..a592d737879 100644 --- a/deploy/helm/crds/apps.kubeblocks.io_componentdefinitions.yaml +++ b/deploy/helm/crds/apps.kubeblocks.io_componentdefinitions.yaml @@ -4489,6 +4489,20 @@ spec: type: object type: array type: object + instanceUpdatePolicy: + description: |- + Specifies how an instance should be updated. + + + - `StrictInPlace` indicates that only allows in-place update. + Any attempt to modify other fields that not support in-place update will be rejected. + - `PreferInPlace` indicates that we will first attempt an in-place update of the instance. + If that fails, it will fall back to the ReCreate, where instance will be recreated. + Default value is "PreferInPlace". + enum: + - StrictInPlace + - PreferInPlace + type: string labels: additionalProperties: type: string @@ -16847,24 +16861,28 @@ spec: - mountPath - volumeName type: object - updateStrategy: + updateConcurrency: + allOf: + - enum: + - Serial + - BestEffortParallel + - Parallel + - enum: + - Serial + - Parallel + - BestEffortParallel default: Serial - description: "Specifies the concurrency strategy for updating multiple - instances of the Component.\nAvailable strategies:\n\n\n- `Serial`: - Updates replicas one at a time, ensuring minimal downtime by waiting - for each replica to become ready\n before updating the next.\n- - `Parallel`: Updates all replicas simultaneously, optimizing for - speed but potentially reducing availability\n during the update.\n- - `BestEffortParallel`: Updates replicas concurrently with a limit - on simultaneous updates to ensure a minimum\n number of operational - replicas for maintaining quorum.\n\t For example, in a 5-replica - component, updating a maximum of 2 replicas simultaneously keeps\n\t - at least 3 operational for quorum.\n\n\nThis field is immutable - and defaults to 'Serial'." - enum: - - Serial - - BestEffortParallel - - Parallel + description: "Specifies the concurrency level for updating instances + during a rolling update.\nAvailable levels:\n\n\n- `Serial`: Updates + instances one at a time, ensuring minimal downtime by waiting for + each instance to become ready\n before updating the next.\n- `Parallel`: + Updates all instances simultaneously, optimizing for speed but potentially + reducing availability\n during the update.\n- `BestEffortParallel`: + Updates instances concurrently with a limit on simultaneous updates + to ensure a minimum\n number of operational replicas for maintaining + quorum.\n\t For example, in a 5-instances setup, updating a maximum + of 2 instances simultaneously keeps\n\t at least 3 operational for + quorum.\n\n\nDefaults to 'Serial'." type: string vars: description: |- diff --git a/deploy/helm/crds/apps.kubeblocks.io_components.yaml b/deploy/helm/crds/apps.kubeblocks.io_components.yaml index 9da4fd1e563..ca5f89dbec8 100644 --- a/deploy/helm/crds/apps.kubeblocks.io_components.yaml +++ b/deploy/helm/crds/apps.kubeblocks.io_components.yaml @@ -3562,17 +3562,6 @@ spec: or when scaling down. It only used when `PodManagementPolicy` is set to `Parallel`. The default Concurrency is 100%. x-kubernetes-int-or-string: true - podUpdatePolicy: - description: |- - PodUpdatePolicy indicates how pods should be updated - - - - `StrictInPlace` indicates that only allows in-place upgrades. - Any attempt to modify other fields will be rejected. - - `PreferInPlace` indicates that we will first attempt an in-place upgrade of the Pod. - If that fails, it will fall back to the ReCreate, where pod will be recreated. - Default value is "PreferInPlace" - type: string replicas: default: 1 description: Specifies the desired number of replicas in the Component @@ -5612,6 +5601,77 @@ spec: - name type: object type: object + updateStrategy: + description: Provides fine-grained control over the spec update process + of all instances. + properties: + instanceUpdatePolicy: + description: |- + Indicates how instances should be updated. + + + - `StrictInPlace` indicates that only allows in-place update. + Any attempt to modify other fields that not support in-place update will be rejected. + - `PreferInPlace` indicates that we will first attempt an in-place update of the instance. + If that fails, it will fall back to the ReCreate, where instance will be recreated. + Default value is "PreferInPlace". + enum: + - StrictInPlace + - PreferInPlace + type: string + rollingUpdate: + description: Specifies how the rolling update should be applied. + properties: + maxUnavailable: + anyOf: + - type: integer + - type: string + description: |- + The maximum number of instances that can be unavailable during the update. + Value can be an absolute number (ex: 5) or a percentage of desired instances (ex: 10%). + Absolute number is calculated from percentage by rounding up. This can not be 0. + Defaults to 1. The field applies to all instances. That means if there is any unavailable pod, + it will be counted towards MaxUnavailable. + x-kubernetes-int-or-string: true + replicas: + anyOf: + - type: integer + - type: string + description: |- + Indicates the number of instances that should be updated during a rolling update. + The remaining instances will remain untouched. This is helpful in defining how many instances + should participate in the update process. + Value can be an absolute number (ex: 5) or a percentage of desired instances (ex: 10%). + Absolute number is calculated from percentage by rounding up. + The default value is ComponentSpec.Replicas (i.e., update all instances). + x-kubernetes-int-or-string: true + updateConcurrency: + allOf: + - enum: + - Serial + - BestEffortParallel + - Parallel + - enum: + - Serial + - Parallel + - BestEffortParallel + default: Serial + description: "Specifies the concurrency level for updating + instances during a rolling update.\nAvailable levels:\n\n\n- + `Serial`: Updates instances one at a time, ensuring minimal + downtime by waiting for each instance to become ready\n + \ before updating the next.\n- `Parallel`: Updates all instances + simultaneously, optimizing for speed but potentially reducing + availability\n during the update.\n- `BestEffortParallel`: + Updates instances concurrently with a limit on simultaneous + updates to ensure a minimum\n number of operational replicas + for maintaining quorum.\n\t For example, in a 5-instances + setup, updating a maximum of 2 instances simultaneously + keeps\n\t at least 3 operational for quorum.\n\n\nDefaults + to 'Serial'." + type: string + type: object + type: object volumeClaimTemplates: description: |- Specifies a list of PersistentVolumeClaim templates that define the storage requirements for the Component. diff --git a/deploy/helm/crds/workloads.kubeblocks.io_instancesets.yaml b/deploy/helm/crds/workloads.kubeblocks.io_instancesets.yaml index 3da51ffb9f8..be1d243810e 100644 --- a/deploy/helm/crds/workloads.kubeblocks.io_instancesets.yaml +++ b/deploy/helm/crds/workloads.kubeblocks.io_instancesets.yaml @@ -3941,19 +3941,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: @@ -4154,17 +4141,6 @@ spec: Note: This field will be removed in future version. type: string - podUpdatePolicy: - description: |- - PodUpdatePolicy indicates how pods should be updated - - - - `StrictInPlace` indicates that only allows in-place upgrades. - Any attempt to modify other fields will be rejected. - - `PreferInPlace` indicates that we will first attempt an in-place upgrade of the Pod. - If that fails, it will fall back to the ReCreate, where pod will be recreated. - Default value is "PreferInPlace" - type: string replicas: default: 1 description: |- @@ -11843,46 +11819,75 @@ spec: type: object type: object updateStrategy: - description: |- - 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 + description: Provides fine-grained control over the spec update process + of all instances. + properties: + instanceUpdatePolicy: + description: |- + Indicates how instances should be updated. - Note: This field will be removed in future version. - properties: + - `StrictInPlace` indicates that only allows in-place update. + Any attempt to modify other fields that not support in-place update will be rejected. + - `PreferInPlace` indicates that we will first attempt an in-place update of the instance. + If that fails, it will fall back to the ReCreate, where instance will be recreated. + Default value is "PreferInPlace". + enum: + - StrictInPlace + - PreferInPlace + type: string rollingUpdate: - description: RollingUpdate is used to communicate parameters when - Type is RollingUpdateStatefulSetStrategyType. + description: Specifies how the rolling update should be applied. 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%). + The maximum number of instances that can be unavailable during the update. + Value can be an absolute number (ex: 5) or a percentage of desired instances (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 instances. That means if there is any unavailable pod, + it will be counted towards MaxUnavailable. x-kubernetes-int-or-string: true - partition: + replicas: + anyOf: + - type: integer + - type: string 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 + Indicates the number of instances that should be updated during a rolling update. + The remaining instances will remain untouched. This is helpful in defining how many instances + should participate in the update process. + Value can be an absolute number (ex: 5) or a percentage of desired instances (ex: 10%). + Absolute number is calculated from percentage by rounding up. + The default value is ComponentSpec.Replicas (i.e., update all instances). + x-kubernetes-int-or-string: true + updateConcurrency: + allOf: + - enum: + - Serial + - BestEffortParallel + - Parallel + - enum: + - Serial + - Parallel + - BestEffortParallel + default: Serial + description: "Specifies the concurrency level for updating + instances during a rolling update.\nAvailable levels:\n\n\n- + `Serial`: Updates instances one at a time, ensuring minimal + downtime by waiting for each instance to become ready\n + \ before updating the next.\n- `Parallel`: Updates all instances + simultaneously, optimizing for speed but potentially reducing + availability\n during the update.\n- `BestEffortParallel`: + Updates instances concurrently with a limit on simultaneous + updates to ensure a minimum\n number of operational replicas + for maintaining quorum.\n\t For example, in a 5-instances + setup, updating a maximum of 2 instances simultaneously + keeps\n\t at least 3 operational for quorum.\n\n\nDefaults + to 'Serial'." + type: string type: object - type: - description: |- - Type indicates the type of the StatefulSetUpdateStrategy. - Default is RollingUpdate. - type: string type: object volumeClaimTemplates: description: |- diff --git a/docs/developer_docs/api-reference/cluster.md b/docs/developer_docs/api-reference/cluster.md index 97782c92516..d2406b92647 100644 --- a/docs/developer_docs/api-reference/cluster.md +++ b/docs/developer_docs/api-reference/cluster.md @@ -675,39 +675,32 @@ an existed ServiceAccount in this field.

-parallelPodManagementConcurrency
+updateStrategy
- -Kubernetes api utils intstr.IntOrString + +UpdateStrategy (Optional) -

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%.

+

Provides fine-grained control over the spec update process of all instances.

-podUpdatePolicy
+parallelPodManagementConcurrency
- -PodUpdatePolicyType + +Kubernetes api utils intstr.IntOrString (Optional) -

PodUpdatePolicy indicates how pods should be updated

-
    -
  • StrictInPlace indicates that only allows in-place upgrades. -Any attempt to modify other fields will be rejected.
  • -
  • PreferInPlace indicates that we will first attempt an in-place upgrade of the Pod. -If that fails, it will fall back to the ReCreate, where pod will be recreated. -Default value is “PreferInPlace”
  • -
+

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%.

@@ -1415,28 +1408,49 @@ This ensures the Pod’s stability and readiness to serve requests.

-updateStrategy
+instanceUpdatePolicy
- -UpdateStrategy + +InstanceUpdatePolicyType (Optional) -

Specifies the concurrency strategy for updating multiple instances of the Component. -Available strategies:

+

Specifies how an instance should be updated.

    -
  • Serial: Updates replicas one at a time, ensuring minimal downtime by waiting for each replica to become ready +
  • StrictInPlace indicates that only allows in-place update. +Any attempt to modify other fields that not support in-place update will be rejected.
  • +
  • PreferInPlace indicates that we will first attempt an in-place update of the instance. +If that fails, it will fall back to the ReCreate, where instance will be recreated. +Default value is “PreferInPlace”.
  • +
+ + + + +updateConcurrency
+ + +UpdateConcurrency + + + + +(Optional) +

Specifies the concurrency level for updating instances during a rolling update. +Available levels:

+
    +
  • Serial: Updates instances one at a time, ensuring minimal downtime by waiting for each instance to become ready before updating the next.
  • -
  • Parallel: Updates all replicas simultaneously, optimizing for speed but potentially reducing availability +
  • Parallel: Updates all instances simultaneously, optimizing for speed but potentially reducing availability during the update.
  • -
  • BestEffortParallel: Updates replicas concurrently with a limit on simultaneous updates to ensure a minimum +
  • BestEffortParallel: Updates instances concurrently with a limit on simultaneous updates to ensure a minimum number of operational replicas for maintaining quorum. - For example, in a 5-replica component, updating a maximum of 2 replicas simultaneously keeps + For example, in a 5-instances setup, updating a maximum of 2 instances simultaneously keeps at least 3 operational for quorum.
-

This field is immutable and defaults to ‘Serial’.

+

Defaults to ‘Serial’.

@@ -1927,8 +1941,8 @@ ShardsLimit provisionStrategy
- -UpdateStrategy + +UpdateConcurrency @@ -1942,8 +1956,8 @@ UpdateStrategy updateStrategy
- -UpdateStrategy + +UpdateConcurrency @@ -3020,23 +3034,16 @@ The default Concurrency is 100%.

-podUpdatePolicy
+updateStrategy
- -PodUpdatePolicyType + +UpdateStrategy (Optional) -

PodUpdatePolicy indicates how pods should be updated

-
    -
  • StrictInPlace indicates that only allows in-place upgrades. -Any attempt to modify other fields will be rejected.
  • -
  • PreferInPlace indicates that we will first attempt an in-place upgrade of the Pod. -If that fails, it will fall back to the ReCreate, where pod will be recreated. -Default value is “PreferInPlace”
  • -
+

Provides fine-grained control over the spec update process of all instances.

@@ -5272,28 +5279,49 @@ This ensures the Pod’s stability and readiness to serve requests.

-updateStrategy
+instanceUpdatePolicy
- -UpdateStrategy + +InstanceUpdatePolicyType (Optional) -

Specifies the concurrency strategy for updating multiple instances of the Component. -Available strategies:

+

Specifies how an instance should be updated.

    -
  • Serial: Updates replicas one at a time, ensuring minimal downtime by waiting for each replica to become ready +
  • StrictInPlace indicates that only allows in-place update. +Any attempt to modify other fields that not support in-place update will be rejected.
  • +
  • PreferInPlace indicates that we will first attempt an in-place update of the instance. +If that fails, it will fall back to the ReCreate, where instance will be recreated. +Default value is “PreferInPlace”.
  • +
+ + + + +updateConcurrency
+ + +UpdateConcurrency + + + + +(Optional) +

Specifies the concurrency level for updating instances during a rolling update. +Available levels:

+
    +
  • Serial: Updates instances one at a time, ensuring minimal downtime by waiting for each instance to become ready before updating the next.
  • -
  • Parallel: Updates all replicas simultaneously, optimizing for speed but potentially reducing availability +
  • Parallel: Updates all instances simultaneously, optimizing for speed but potentially reducing availability during the update.
  • -
  • BestEffortParallel: Updates replicas concurrently with a limit on simultaneous updates to ensure a minimum +
  • BestEffortParallel: Updates instances concurrently with a limit on simultaneous updates to ensure a minimum number of operational replicas for maintaining quorum. - For example, in a 5-replica component, updating a maximum of 2 replicas simultaneously keeps + For example, in a 5-instances setup, updating a maximum of 2 instances simultaneously keeps at least 3 operational for quorum.
-

This field is immutable and defaults to ‘Serial’.

+

Defaults to ‘Serial’.

@@ -6158,39 +6186,32 @@ an existed ServiceAccount in this field.

-parallelPodManagementConcurrency
+updateStrategy
- -Kubernetes api utils intstr.IntOrString + +UpdateStrategy (Optional) -

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%.

+

Provides fine-grained control over the spec update process of all instances.

-podUpdatePolicy
+parallelPodManagementConcurrency
- -PodUpdatePolicyType + +Kubernetes api utils intstr.IntOrString (Optional) -

PodUpdatePolicy indicates how pods should be updated

-
    -
  • StrictInPlace indicates that only allows in-place upgrades. -Any attempt to modify other fields will be rejected.
  • -
  • PreferInPlace indicates that we will first attempt an in-place upgrade of the Pod. -If that fails, it will fall back to the ReCreate, where pod will be recreated. -Default value is “PreferInPlace”
  • -
+

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%.

@@ -7938,6 +7959,30 @@ Add new or override existing volume claim templates.

+

InstanceUpdatePolicyType +(string alias)

+

+(Appears on:ComponentDefinitionSpec, UpdateStrategy) +

+
+
+ + + + + + + + + + + + +
ValueDescription

"PreferInPlace"

PreferInPlaceInstanceUpdatePolicyType indicates that we will first attempt an in-place update of the instance. +If that fails, it will fall back to the ReCreate, where instance will be recreated.

+

"StrictInPlace"

StrictInPlaceInstanceUpdatePolicyType indicates that only allows in-place update. +Any attempt to modify other fields that not support in-place update will be rejected.

+

Issuer

@@ -8559,30 +8604,6 @@ Kubernetes core/v1.PersistentVolumeMode -

PodUpdatePolicyType -(string alias)

-

-(Appears on:ClusterComponentSpec, ComponentSpec) -

-
-
- - - - - - - - - - - - -
ValueDescription

"PreferInPlace"

PreferInPlacePodUpdatePolicyType indicates that we will first attempt an in-place upgrade of the Pod. -If that fails, it will fall back to the ReCreate, where pod will be recreated.

-

"StrictInPlace"

StrictInPlacePodUpdatePolicyType indicates that only allows in-place upgrades. -Any attempt to modify other fields will be rejected.

-

PreConditionType (string alias)

@@ -8971,6 +8992,87 @@ VarOption +

RollingUpdate +

+

+(Appears on:UpdateStrategy) +

+
+

RollingUpdate specifies how the rolling update should be applied.

+
+ + + + + + + + + + + + + + + + + + + + + +
FieldDescription
+replicas
+ + +Kubernetes api utils intstr.IntOrString + + +
+(Optional) +

Indicates the number of instances that should be updated during a rolling update. +The remaining instances will remain untouched. This is helpful in defining how many instances +should participate in the update process. +Value can be an absolute number (ex: 5) or a percentage of desired instances (ex: 10%). +Absolute number is calculated from percentage by rounding up. +The default value is ComponentSpec.Replicas (i.e., update all instances).

+
+updateConcurrency
+ + +UpdateConcurrency + + +
+(Optional) +

Specifies the concurrency level for updating instances during a rolling update. +Available levels:

+
    +
  • Serial: Updates instances one at a time, ensuring minimal downtime by waiting for each instance to become ready +before updating the next.
  • +
  • Parallel: Updates all instances simultaneously, optimizing for speed but potentially reducing availability +during the update.
  • +
  • BestEffortParallel: Updates instances concurrently with a limit on simultaneous updates to ensure a minimum +number of operational replicas for maintaining quorum. + For example, in a 5-instances setup, updating a maximum of 2 instances simultaneously keeps +at least 3 operational for quorum.
  • +
+

Defaults to ‘Serial’.

+
+maxUnavailable
+ + +Kubernetes api utils intstr.IntOrString + + +
+(Optional) +

The maximum number of instances that can be unavailable during the update. +Value can be an absolute number (ex: 5) or a percentage of desired instances (ex: 10%). +Absolute number is calculated from percentage by rounding up. This can not be 0. +Defaults to 1. The field applies to all instances. That means if there is any unavailable pod, +it will be counted towards MaxUnavailable.

+

SchedulingPolicy

@@ -10488,8 +10590,8 @@ ShardsLimit provisionStrategy
- -UpdateStrategy + +UpdateConcurrency @@ -10503,8 +10605,8 @@ UpdateStrategy updateStrategy
- -UpdateStrategy + +UpdateConcurrency @@ -11522,15 +11624,15 @@ VarOption -

UpdateStrategy +

UpdateConcurrency (string alias)

-(Appears on:ComponentDefinitionSpec, ShardingDefinitionSpec) +(Appears on:ComponentDefinitionSpec, RollingUpdate, ShardingDefinitionSpec)

-

UpdateStrategy defines the update strategy for cluster components. This strategy determines how updates are applied +

UpdateConcurrency defines the update concurrency level for cluster components. This concurrency level determines how updates are applied across the cluster. -The available strategies are Serial, BestEffortParallel, and Parallel.

+The available concurrency levels are Serial, BestEffortParallel, and Parallel.

@@ -11540,7 +11642,7 @@ The available strategies are Serial, BestEffortParallel - - -

"BestEffortParallel"

BestEffortParallelStrategy indicates that the replicas are updated in parallel, with the operator making +

BestEffortParallelConcurrency indicates that the replicas are updated in parallel, with the operator making a best-effort attempt to update as many replicas as possible concurrently while maintaining the component’s availability. Unlike the Parallel strategy, the BestEffortParallel strategy aims to ensure that a minimum number @@ -11551,33 +11653,25 @@ the operator may allow a maximum of 2 replicas to be simultaneously updated. Thi

The BestEffortParallel strategy strikes a balance between update speed and component availability.

"Parallel"

ParallelStrategy indicates that updates are applied simultaneously to all Pods of a Component. +

ParallelConcurrency indicates that updates are applied simultaneously to all Pods of a Component. The replicas are updated in parallel, with the operator updating all replicas concurrently. This strategy provides the fastest update time but may lead to a period of reduced availability or capacity during the update process.

"Serial"

SerialStrategy indicates that updates are applied one at a time in a sequential manner. +

SerialConcurrency indicates that updates are applied one at a time in a sequential manner. The operator waits for each replica to be updated and ready before proceeding to the next one. This ensures that only one replica is unavailable at a time during the update process.

-

VarOption -(string alias)

-

-(Appears on:ClusterVars, ComponentVars, CredentialVars, NamedVar, RoledVar, ServiceRefVars, ServiceVars, TLSVars) -

-
-

VarOption defines whether a variable is required or optional.

-
-

VarSource +

UpdateStrategy

-(Appears on:EnvVar) +(Appears on:ClusterComponentSpec, ComponentSpec)

-

VarSource represents a source for the value of an EnvVar.

+

UpdateStrategy defines fine-grained control over the spec update process of all instances.

@@ -11589,44 +11683,105 @@ This ensures that only one replica is unavailable at a time during the update pr - - - +
-configMapKeyRef
+instanceUpdatePolicy
- -Kubernetes core/v1.ConfigMapKeySelector + +InstanceUpdatePolicyType
(Optional) -

Selects a key of a ConfigMap.

+

Indicates how instances should be updated.

+
    +
  • StrictInPlace indicates that only allows in-place update. +Any attempt to modify other fields that not support in-place update will be rejected.
  • +
  • PreferInPlace indicates that we will first attempt an in-place update of the instance. +If that fails, it will fall back to the ReCreate, where instance will be recreated. +Default value is “PreferInPlace”.
  • +
-secretKeyRef
+rollingUpdate
- -Kubernetes core/v1.SecretKeySelector + +RollingUpdate
(Optional) -

Selects a key of a Secret.

+

Specifies how the rolling update should be applied.

-hostNetworkVarRef
- - -HostNetworkVarSelector - - -
-(Optional) -

Selects a defined var of host-network resources.

+
+

VarOption +(string alias)

+

+(Appears on:ClusterVars, ComponentVars, CredentialVars, NamedVar, RoledVar, ServiceRefVars, ServiceVars, TLSVars) +

+
+

VarOption defines whether a variable is required or optional.

+
+

VarSource +

+

+(Appears on:EnvVar) +

+
+

VarSource represents a source for the value of an EnvVar.

+
+ + + + + + + + + + + + + + + + + + + @@ -28953,40 +29108,16 @@ The default Concurrency is 100%.

- - - - @@ -29033,25 +29164,6 @@ MembershipReconfiguration - - - - - - - - @@ -29564,25 +29652,6 @@ MembershipReconfiguration - - - -
FieldDescription
+configMapKeyRef
+ + +Kubernetes core/v1.ConfigMapKeySelector + + +
+(Optional) +

Selects a key of a ConfigMap.

+
+secretKeyRef
+ + +Kubernetes core/v1.SecretKeySelector + + +
+(Optional) +

Selects a key of a Secret.

+
+hostNetworkVarRef
+ + +HostNetworkVarSelector + + +
+(Optional) +

Selects a defined var of host-network resources.

-podUpdatePolicy
- - -PodUpdatePolicyType - - -
-(Optional) -

PodUpdatePolicy indicates how pods should be updated

-
    -
  • StrictInPlace indicates that only allows in-place upgrades. -Any attempt to modify other fields will be rejected.
  • -
  • PreferInPlace indicates that we will first attempt an in-place upgrade of the Pod. -If that fails, it will fall back to the ReCreate, where pod will be recreated. -Default value is “PreferInPlace”
  • -
-
updateStrategy
- -Kubernetes apps/v1.StatefulSetUpdateStrategy + +UpdateStrategy
-

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

-

Note: This field will be removed in future version.

+(Optional) +

Provides fine-grained control over the spec update process of all instances.

-memberUpdateStrategy
- - -MemberUpdateStrategy - - -
-(Optional) -

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
  • -
-
paused
bool @@ -29484,40 +29596,16 @@ The default Concurrency is 100%.

-podUpdatePolicy
- - -PodUpdatePolicyType - - -
-(Optional) -

PodUpdatePolicy indicates how pods should be updated

-
    -
  • StrictInPlace indicates that only allows in-place upgrades. -Any attempt to modify other fields will be rejected.
  • -
  • PreferInPlace indicates that we will first attempt an in-place upgrade of the Pod. -If that fails, it will fall back to the ReCreate, where pod will be recreated. -Default value is “PreferInPlace”
  • -
-
updateStrategy
- -Kubernetes apps/v1.StatefulSetUpdateStrategy + +UpdateStrategy
-

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

-

Note: This field will be removed in future version.

+(Optional) +

Provides fine-grained control over the spec update process of all instances.

-memberUpdateStrategy
- - -MemberUpdateStrategy - - -
-(Optional) -

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
  • -
-
paused
bool @@ -30116,6 +30185,30 @@ indicated by UpdateRevisions.

+

InstanceUpdatePolicyType +(string alias)

+

+(Appears on:UpdateStrategy) +

+
+
+ + + + + + + + + + + + +
ValueDescription

"PreferInPlace"

PreferInPlaceInstanceUpdatePolicyType indicates that we will first attempt an in-place update of the instance. +If that fails, it will fall back to the ReCreate, where instance will be recreated.

+

"StrictInPlace"

StrictInPlaceInstanceUpdatePolicyType indicates that only allows in-place update. +Any attempt to modify other fields that not support in-place update will be rejected.

+

MemberStatus

@@ -30158,29 +30251,6 @@ ReplicaRole -

MemberUpdateStrategy -(string alias)

-

-(Appears on:InstanceSetSpec) -

-
-

MemberUpdateStrategy defines Cluster Component update strategy.

-
- - - - - - - - - - - - - - -
ValueDescription

"BestEffortParallel"

"Parallel"

"Serial"

MembershipReconfiguration

@@ -30319,30 +30389,6 @@ If the Image is not configured, the Image from the previous non-nil action will -

PodUpdatePolicyType -(string alias)

-

-(Appears on:InstanceSetSpec) -

-
-
- - - - - - - - - - - - -
ValueDescription

"PreferInPlace"

PreferInPlacePodUpdatePolicyType indicates that we will first attempt an in-place upgrade of the Pod. -If that fails, it will fall back to the ReCreate, where pod will be recreated.

-

"StrictInPlace"

StrictInPlacePodUpdatePolicyType indicates that only allows in-place upgrades. -Any attempt to modify other fields will be rejected.

-

Range

@@ -30581,6 +30627,87 @@ RoleUpdateMechanism +

RollingUpdate +

+

+(Appears on:UpdateStrategy) +

+
+

RollingUpdate specifies how the rolling update should be applied.

+
+ + + + + + + + + + + + + + + + + + + + + +
FieldDescription
+replicas
+ + +Kubernetes api utils intstr.IntOrString + + +
+(Optional) +

Indicates the number of instances that should be updated during a rolling update. +The remaining instances will remain untouched. This is helpful in defining how many instances +should participate in the update process. +Value can be an absolute number (ex: 5) or a percentage of desired instances (ex: 10%). +Absolute number is calculated from percentage by rounding up. +The default value is ComponentSpec.Replicas (i.e., update all instances).

+
+updateConcurrency
+ + +UpdateConcurrency + + +
+(Optional) +

Specifies the concurrency level for updating instances during a rolling update. +Available levels:

+
    +
  • Serial: Updates instances one at a time, ensuring minimal downtime by waiting for each instance to become ready +before updating the next.
  • +
  • Parallel: Updates all instances simultaneously, optimizing for speed but potentially reducing availability +during the update.
  • +
  • BestEffortParallel: Updates instances concurrently with a limit on simultaneous updates to ensure a minimum +number of operational replicas for maintaining quorum. + For example, in a 5-instances setup, updating a maximum of 2 instances simultaneously keeps +at least 3 operational for quorum.
  • +
+

Defaults to ‘Serial’.

+
+maxUnavailable
+ + +Kubernetes api utils intstr.IntOrString + + +
+(Optional) +

The maximum number of instances that can be unavailable during the update. +Value can be an absolute number (ex: 5) or a percentage of desired instances (ex: 10%). +Absolute number is calculated from percentage by rounding up. This can not be 0. +Defaults to 1. The field applies to all instances. That means if there is any unavailable pod, +it will be counted towards MaxUnavailable.

+

SchedulingPolicy

@@ -30692,6 +30819,100 @@ All topologySpreadConstraints are ANDed.

+

UpdateConcurrency +(string alias)

+

+(Appears on:RollingUpdate) +

+
+

UpdateConcurrency defines the update concurrency level for cluster components. This concurrency level determines how updates are applied +across the cluster. +The available concurrency levels are Serial, BestEffortParallel, and Parallel.

+
+ + + + + + + + + + + + + + +
ValueDescription

"BestEffortParallel"

BestEffortParallelConcurrency indicates that the replicas are updated in parallel, with the operator making +a best-effort attempt to update as many replicas as possible concurrently +while maintaining the component’s availability. +Unlike the Parallel strategy, the BestEffortParallel strategy aims to ensure that a minimum number +of replicas remain available during the update process to maintain the component’s quorum and functionality.

+

For example, consider a component with 5 replicas. To maintain the component’s availability and quorum, +the operator may allow a maximum of 2 replicas to be simultaneously updated. This ensures that at least +3 replicas (a quorum) remain available and functional during the update process.

+

The BestEffortParallel strategy strikes a balance between update speed and component availability.

+

"Parallel"

ParallelConcurrency indicates that updates are applied simultaneously to all Pods of a Component. +The replicas are updated in parallel, with the operator updating all replicas concurrently. +This strategy provides the fastest update time but may lead to a period of reduced availability or +capacity during the update process.

+

"Serial"

SerialConcurrency indicates that updates are applied one at a time in a sequential manner. +The operator waits for each replica to be updated and ready before proceeding to the next one. +This ensures that only one replica is unavailable at a time during the update process.

+
+

UpdateStrategy +

+

+(Appears on:InstanceSetSpec) +

+
+

UpdateStrategy defines fine-grained control over the spec update process of all instances.

+
+ + + + + + + + + + + + + + + + + +
FieldDescription
+instanceUpdatePolicy
+ + +InstanceUpdatePolicyType + + +
+(Optional) +

Indicates how instances should be updated.

+
    +
  • StrictInPlace indicates that only allows in-place update. +Any attempt to modify other fields that not support in-place update will be rejected.
  • +
  • PreferInPlace indicates that we will first attempt an in-place update of the instance. +If that fails, it will fall back to the ReCreate, where instance will be recreated. +Default value is “PreferInPlace”.
  • +
+
+rollingUpdate
+ + +RollingUpdate + + +
+(Optional) +

Specifies how the rolling update should be applied.

+

workloads.kubeblocks.io/v1alpha1

diff --git a/pkg/controller/builder/builder_component.go b/pkg/controller/builder/builder_component.go index 5d8ae199a05..e58b3e09760 100644 --- a/pkg/controller/builder/builder_component.go +++ b/pkg/controller/builder/builder_component.go @@ -86,8 +86,8 @@ func (builder *ComponentBuilder) SetParallelPodManagementConcurrency(parallelPod return builder } -func (builder *ComponentBuilder) SetPodUpdatePolicy(policy *appsv1.InstanceUpdatePolicyType) *ComponentBuilder { - builder.get().Spec.PodUpdatePolicy = policy +func (builder *ComponentBuilder) SetUpdateStrategy(strategy *appsv1.UpdateStrategy) *ComponentBuilder { + builder.get().Spec.UpdateStrategy = strategy return builder } diff --git a/pkg/controller/builder/builder_component_definition.go b/pkg/controller/builder/builder_component_definition.go index d1aaa6eff5a..e873b6fe58b 100644 --- a/pkg/controller/builder/builder_component_definition.go +++ b/pkg/controller/builder/builder_component_definition.go @@ -205,8 +205,8 @@ func (builder *ComponentDefinitionBuilder) AddSystemAccount(accountName string, return builder } -func (builder *ComponentDefinitionBuilder) SetUpdateStrategy(strategy *appsv1.UpdateConcurrency) *ComponentDefinitionBuilder { - builder.get().Spec.UpdateStrategyConstraint = strategy +func (builder *ComponentDefinitionBuilder) SetUpdateConcurrency(concurrency *appsv1.UpdateConcurrency) *ComponentDefinitionBuilder { + builder.get().Spec.UpdateConcurrency = concurrency return builder } diff --git a/pkg/controller/builder/builder_instance_set.go b/pkg/controller/builder/builder_instance_set.go index 39f9d71ffaa..3480ddde455 100644 --- a/pkg/controller/builder/builder_instance_set.go +++ b/pkg/controller/builder/builder_instance_set.go @@ -113,21 +113,11 @@ func (builder *InstanceSetBuilder) SetParallelPodManagementConcurrency(parallelP return builder } -func (builder *InstanceSetBuilder) SetPodUpdatePolicy(policy workloads.PodUpdatePolicyType) *InstanceSetBuilder { - builder.get().Spec.PodUpdatePolicy = policy - return builder -} - -func (builder *InstanceSetBuilder) SetUpdateStrategy(strategy apps.StatefulSetUpdateStrategy) *InstanceSetBuilder { +func (builder *InstanceSetBuilder) SetUpdateStrategy(strategy *workloads.UpdateStrategy) *InstanceSetBuilder { builder.get().Spec.UpdateStrategy = strategy return builder } -func (builder *InstanceSetBuilder) SetUpdateStrategyType(strategyType apps.StatefulSetUpdateStrategyType) *InstanceSetBuilder { - builder.get().Spec.UpdateStrategy.Type = strategyType - return builder -} - func (builder *InstanceSetBuilder) SetCustomHandler(handler []workloads.Action) *InstanceSetBuilder { roleProbe := builder.get().Spec.RoleProbe if roleProbe == nil { @@ -160,14 +150,6 @@ func (builder *InstanceSetBuilder) SetMembershipReconfiguration(reconfiguration return builder } -func (builder *InstanceSetBuilder) SetMemberUpdateStrategy(strategy *workloads.MemberUpdateStrategy) *InstanceSetBuilder { - builder.get().Spec.MemberUpdateStrategy = strategy - if strategy != nil { - builder.SetUpdateStrategyType(apps.OnDeleteStatefulSetStrategyType) - } - return builder -} - func (builder *InstanceSetBuilder) SetPaused(paused bool) *InstanceSetBuilder { builder.get().Spec.Paused = paused return builder diff --git a/pkg/controller/builder/builder_instance_set_test.go b/pkg/controller/builder/builder_instance_set_test.go index fd4a3dcabcc..b8066a348d0 100644 --- a/pkg/controller/builder/builder_instance_set_test.go +++ b/pkg/controller/builder/builder_instance_set_test.go @@ -45,7 +45,7 @@ var _ = Describe("instance_set builder", func() { minReadySeconds = int32(11) port = int32(12345) policy = apps.OrderedReadyPodManagement - podUpdatePolicy = workloads.PreferInPlacePodUpdatePolicyType + instanceUpdatePolicy = workloads.PreferInPlaceInstanceUpdatePolicyType ) parallelPodManagementConcurrency := &intstr.IntOrString{Type: intstr.String, StrVal: "100%"} selectors := map[string]string{selectorKey4: selectorValue4} @@ -107,15 +107,17 @@ var _ = Describe("instance_set builder", func() { }, }, } - partition, maxUnavailable := int32(3), intstr.FromInt(2) - strategy := apps.StatefulSetUpdateStrategy{ - Type: apps.RollingUpdateStatefulSetStrategyType, - RollingUpdate: &apps.RollingUpdateStatefulSetStrategy{ - Partition: &partition, - MaxUnavailable: &maxUnavailable, + itUpdatePolicy := workloads.PreferInPlaceInstanceUpdatePolicyType + updateReplicas, maxUnavailable := intstr.FromInt32(3), intstr.FromInt32(2) + updateConcurrency := workloads.BestEffortParallelConcurrency + strategy := workloads.UpdateStrategy{ + InstanceUpdatePolicy: &itUpdatePolicy, + RollingUpdate: &workloads.RollingUpdate{ + Replicas: &updateReplicas, + MaxUnavailable: &maxUnavailable, + UpdateConcurrency: &updateConcurrency, }, } - strategyType := apps.OnDeleteStatefulSetStrategyType delay := int32(10) roleProbe := workloads.RoleProbe{InitialDelaySeconds: delay} actions := []workloads.Action{ @@ -128,7 +130,6 @@ var _ = Describe("instance_set builder", func() { Image: "foo-2", Command: []string{"bar-2"}, } - memberUpdateStrategy := workloads.BestEffortParallelUpdateStrategy paused := true credential := workloads.Credential{ Username: workloads.CredentialVar{Value: "foo"}, @@ -157,13 +158,10 @@ var _ = Describe("instance_set builder", func() { AddVolumeClaimTemplates(vc). SetPodManagementPolicy(policy). SetParallelPodManagementConcurrency(parallelPodManagementConcurrency). - SetPodUpdatePolicy(podUpdatePolicy). - SetUpdateStrategy(strategy). - SetUpdateStrategyType(strategyType). + SetUpdateStrategy(&strategy). SetRoleProbe(&roleProbe). SetCustomHandler(actions). AddCustomHandler(action). - SetMemberUpdateStrategy(&memberUpdateStrategy). SetPaused(paused). SetCredential(credential). SetInstances(instances). @@ -189,20 +187,21 @@ var _ = Describe("instance_set builder", func() { Expect(its.Spec.VolumeClaimTemplates[1]).Should(Equal(vc)) Expect(its.Spec.PodManagementPolicy).Should(Equal(policy)) Expect(its.Spec.ParallelPodManagementConcurrency).Should(Equal(parallelPodManagementConcurrency)) - Expect(its.Spec.PodUpdatePolicy).Should(Equal(podUpdatePolicy)) - Expect(its.Spec.UpdateStrategy.Type).Should(Equal(strategyType)) + Expect(its.Spec.UpdateStrategy).ShouldNot(BeNil()) + Expect(its.Spec.UpdateStrategy.InstanceUpdatePolicy).ShouldNot(BeNil()) + Expect(*its.Spec.UpdateStrategy.InstanceUpdatePolicy).Should(Equal(instanceUpdatePolicy)) Expect(its.Spec.UpdateStrategy.RollingUpdate).ShouldNot(BeNil()) - Expect(its.Spec.UpdateStrategy.RollingUpdate.Partition).ShouldNot(BeNil()) - Expect(*its.Spec.UpdateStrategy.RollingUpdate.Partition).Should(Equal(partition)) + Expect(its.Spec.UpdateStrategy.RollingUpdate.Replicas).ShouldNot(BeNil()) + Expect(*its.Spec.UpdateStrategy.RollingUpdate.Replicas).Should(Equal(updateReplicas)) Expect(its.Spec.UpdateStrategy.RollingUpdate.MaxUnavailable).ShouldNot(BeNil()) - Expect(its.Spec.UpdateStrategy.RollingUpdate.MaxUnavailable).ShouldNot(Equal(maxUnavailable)) + Expect(*its.Spec.UpdateStrategy.RollingUpdate.MaxUnavailable).Should(Equal(maxUnavailable)) + Expect(its.Spec.UpdateStrategy.RollingUpdate.UpdateConcurrency).ShouldNot(BeNil()) + Expect(*its.Spec.UpdateStrategy.RollingUpdate.UpdateConcurrency).Should(Equal(updateConcurrency)) Expect(its.Spec.RoleProbe).ShouldNot(BeNil()) Expect(its.Spec.RoleProbe.InitialDelaySeconds).Should(Equal(delay)) 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.Paused).Should(Equal(paused)) Expect(its.Spec.Credential).ShouldNot(BeNil()) Expect(*its.Spec.Credential).Should(Equal(credential)) diff --git a/pkg/controller/component/component.go b/pkg/controller/component/component.go index 9678d516481..ea2a59fe451 100644 --- a/pkg/controller/component/component.go +++ b/pkg/controller/component/component.go @@ -77,7 +77,7 @@ func BuildComponent(cluster *appsv1.Cluster, compSpec *appsv1.ClusterComponentSp SetResources(compSpec.Resources). SetServiceAccountName(compSpec.ServiceAccountName). SetParallelPodManagementConcurrency(compSpec.ParallelPodManagementConcurrency). - SetPodUpdatePolicy(compSpec.PodUpdatePolicy). + SetUpdateStrategy(compSpec.UpdateStrategy). SetVolumeClaimTemplates(compSpec.VolumeClaimTemplates). SetVolumes(compSpec.Volumes). SetServices(compSpec.Services). diff --git a/pkg/controller/component/its_convertor.go b/pkg/controller/component/its_convertor.go index 3d51af61c54..3b9bf069064 100644 --- a/pkg/controller/component/its_convertor.go +++ b/pkg/controller/component/its_convertor.go @@ -48,10 +48,8 @@ func BuildWorkloadFrom(synthesizeComp *SynthesizedComponent, protoITS *workloads "roleprobe": &itsRoleProbeConvertor{}, "credential": &itsCredentialConvertor{}, "membershipreconfiguration": &itsMembershipReconfigurationConvertor{}, - "memberupdatestrategy": &itsMemberUpdateStrategyConvertor{}, "podmanagementpolicy": &itsPodManagementPolicyConvertor{}, "parallelpodmanagementconcurrency": &itsParallelPodManagementConcurrencyConvertor{}, - "podupdatepolicy": &itsPodUpdatePolicyConvertor{}, "updatestrategy": &itsUpdateStrategyConvertor{}, "instances": &itsInstancesConvertor{}, "offlineinstances": &itsOfflineInstancesConvertor{}, @@ -80,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{} @@ -102,11 +89,7 @@ func (c *itsPodManagementPolicyConvertor) convert(args ...any) (any, error) { if synthesizedComp.PodManagementPolicy != nil { return *synthesizedComp.PodManagementPolicy, nil } - memberUpdateStrategy := getMemberUpdateStrategy(synthesizedComp) - if memberUpdateStrategy == nil || *memberUpdateStrategy == workloads.SerialUpdateStrategy { - return appsv1.OrderedReadyPodManagement, nil - } - return appsv1.ParallelPodManagement, nil + return appsv1.OrderedReadyPodManagement, nil } // itsParallelPodManagementConcurrencyConvertor is an implementation of the convertor interface, used to convert the given object into InstanceSet.Spec.ParallelPodManagementConcurrency. @@ -123,20 +106,6 @@ func (c *itsParallelPodManagementConcurrencyConvertor) convert(args ...any) (any return &intstr.IntOrString{Type: intstr.String, StrVal: "100%"}, nil } -// itsPodUpdatePolicyConvertor is an implementation of the convertor interface, used to convert the given object into InstanceSet.Spec.PodUpdatePolicy. -type itsPodUpdatePolicyConvertor struct{} - -func (c *itsPodUpdatePolicyConvertor) convert(args ...any) (any, error) { - synthesizedComp, err := parseITSConvertorArgs(args...) - if err != nil { - return nil, err - } - if synthesizedComp.PodUpdatePolicy != nil { - return *synthesizedComp.PodUpdatePolicy, nil - } - return workloads.PreferInPlacePodUpdatePolicyType, nil -} - // itsUpdateStrategyConvertor is an implementation of the convertor interface, used to convert the given object into InstanceSet.Spec.Instances. type itsUpdateStrategyConvertor struct{} @@ -145,11 +114,20 @@ 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 + var updateStrategy *workloads.UpdateStrategy + if synthesizedComp.UpdateStrategy != nil { + updateStrategy = &workloads.UpdateStrategy{ + InstanceUpdatePolicy: (*workloads.InstanceUpdatePolicyType)(synthesizedComp.UpdateStrategy.InstanceUpdatePolicy), + } + if synthesizedComp.UpdateStrategy.RollingUpdate != nil { + updateStrategy.RollingUpdate = &workloads.RollingUpdate{ + Replicas: synthesizedComp.UpdateStrategy.RollingUpdate.Replicas, + MaxUnavailable: synthesizedComp.UpdateStrategy.RollingUpdate.MaxUnavailable, + UpdateConcurrency: (*workloads.UpdateConcurrency)(synthesizedComp.UpdateStrategy.RollingUpdate.UpdateConcurrency), + } + } } - return nil, nil + return updateStrategy, nil } // itsInstancesConvertor converts component instanceTemplate to ITS instanceTemplate @@ -249,27 +227,6 @@ func parseITSConvertorArgs(args ...any) (*SynthesizedComponent, error) { return synthesizeComp, nil } -func getMemberUpdateStrategy(synthesizedComp *SynthesizedComponent) *workloads.MemberUpdateStrategy { - if synthesizedComp.UpdateStrategy == nil { - return nil - } - var ( - serial = workloads.SerialUpdateStrategy - parallelUpdate = workloads.ParallelUpdateStrategy - bestEffortParallelUpdate = workloads.BestEffortParallelUpdateStrategy - ) - switch *synthesizedComp.UpdateStrategy { - case kbappsv1.SerialConcurrency: - return &serial - case kbappsv1.ParallelConcurrency: - return ¶llelUpdate - case kbappsv1.BestEffortParallelConcurrency: - return &bestEffortParallelUpdate - default: - return nil - } -} - // itsServiceConvertor converts the given object into InstanceSet.Spec.Service. func (c *itsServiceConvertor) convert(args ...any) (any, error) { return nil, nil diff --git a/pkg/controller/component/synthesize_component.go b/pkg/controller/component/synthesize_component.go index 18f248dac13..3d9c0533e23 100644 --- a/pkg/controller/component/synthesize_component.go +++ b/pkg/controller/component/synthesize_component.go @@ -91,7 +91,6 @@ func BuildSynthesizedComponent(ctx context.Context, cli client.Reader, ConfigTemplates: compDefObj.Spec.Configs, ScriptTemplates: compDefObj.Spec.Scripts, Roles: compDefObj.Spec.Roles, - UpdateStrategy: compDefObj.Spec.UpdateStrategyConstraint, MinReadySeconds: compDefObj.Spec.MinReadySeconds, PolicyRules: compDefObj.Spec.PolicyRules, LifecycleActions: compDefObj.Spec.LifecycleActions, @@ -106,13 +105,15 @@ func BuildSynthesizedComponent(ctx context.Context, cli client.Reader, Stop: comp.Spec.Stop, PodManagementPolicy: compDef.Spec.PodManagementPolicy, ParallelPodManagementConcurrency: comp.Spec.ParallelPodManagementConcurrency, - PodUpdatePolicy: comp.Spec.PodUpdatePolicy, } if err = mergeUserDefinedEnv(synthesizeComp, comp); err != nil { return nil, err } + // build update strategy for workload + buildUpdateStrategy(synthesizeComp, comp, compDefObj) + // build scheduling policy for workload buildSchedulingPolicy(synthesizeComp, comp) @@ -155,6 +156,42 @@ func BuildSynthesizedComponent(ctx context.Context, cli client.Reader, return synthesizeComp, nil } +func buildUpdateStrategy(synthesizeComp *SynthesizedComponent, comp *appsv1.Component, compDef *appsv1.ComponentDefinition) { + var updateStrategy *appsv1.UpdateStrategy + if comp.Spec.UpdateStrategy != nil { + updateStrategy = &appsv1.UpdateStrategy{ + InstanceUpdatePolicy: comp.Spec.UpdateStrategy.InstanceUpdatePolicy, + } + if comp.Spec.UpdateStrategy.RollingUpdate != nil { + updateStrategy.RollingUpdate = &appsv1.RollingUpdate{ + Replicas: comp.Spec.UpdateStrategy.RollingUpdate.Replicas, + MaxUnavailable: comp.Spec.UpdateStrategy.RollingUpdate.MaxUnavailable, + UpdateConcurrency: comp.Spec.UpdateStrategy.RollingUpdate.UpdateConcurrency, + } + } + } + if compDef.Spec.InstanceUpdatePolicy != nil { + if updateStrategy == nil { + updateStrategy = &appsv1.UpdateStrategy{} + } + if updateStrategy.InstanceUpdatePolicy == nil { + updateStrategy.InstanceUpdatePolicy = compDef.Spec.InstanceUpdatePolicy + } + } + if compDef.Spec.UpdateConcurrency != nil { + if updateStrategy == nil { + updateStrategy = &appsv1.UpdateStrategy{} + } + if updateStrategy.RollingUpdate == nil { + updateStrategy.RollingUpdate = &appsv1.RollingUpdate{} + } + if updateStrategy.RollingUpdate.UpdateConcurrency == nil { + updateStrategy.RollingUpdate.UpdateConcurrency = compDef.Spec.UpdateConcurrency + } + } + synthesizeComp.UpdateStrategy = updateStrategy +} + func buildComp2CompDefs(ctx context.Context, cli client.Reader, cluster *appsv1.Cluster) (map[string]string, error) { if cluster == nil { return nil, nil diff --git a/pkg/controller/component/type.go b/pkg/controller/component/type.go index 2fbc633c63b..cb8f8435d46 100644 --- a/pkg/controller/component/type.go +++ b/pkg/controller/component/type.go @@ -42,7 +42,7 @@ type SynthesizedComponent struct { Replicas int32 `json:"replicas"` Resources corev1.ResourceRequirements `json:"resources,omitempty"` PodSpec *corev1.PodSpec `json:"podSpec,omitempty"` - SidecarVars []kbappsv1.EnvVar // vars defined by sidecars + SidecarVars []kbappsv1.EnvVar // vars defined by sidecars VolumeClaimTemplates []corev1.PersistentVolumeClaimTemplate `json:"volumeClaimTemplates,omitempty"` LogConfigs []kbappsv1.LogConfig `json:"logConfigs,omitempty"` ConfigTemplates []kbappsv1.ComponentConfigSpec `json:"configTemplates,omitempty"` @@ -51,22 +51,21 @@ type SynthesizedComponent struct { ServiceAccountName string `json:"serviceAccountName,omitempty"` ServiceReferences map[string]*kbappsv1.ServiceDescriptor `json:"serviceReferences,omitempty"` Labels map[string]string `json:"labels,omitempty"` - StaticLabels map[string]string // labels defined by the component definition - DynamicLabels map[string]string // labels defined by the cluster and component API + StaticLabels map[string]string // labels defined by the component definition + DynamicLabels map[string]string // labels defined by the cluster and component API Annotations map[string]string `json:"annotations,omitempty"` - StaticAnnotations map[string]string // annotations defined by the component definition - DynamicAnnotations map[string]string // annotations defined by the cluster and component API + StaticAnnotations map[string]string // annotations defined by the component definition + DynamicAnnotations map[string]string // annotations defined by the cluster and component API TemplateVars map[string]any `json:"templateVars,omitempty"` EnvVars []corev1.EnvVar `json:"envVars,omitempty"` EnvFromSources []corev1.EnvFromSource `json:"envFromSources,omitempty"` Instances []kbappsv1.InstanceTemplate `json:"instances,omitempty"` OfflineInstances []string `json:"offlineInstances,omitempty"` - Roles []kbappsv1.ReplicaRole `json:"roles,omitempty"` - UpdateStrategy *kbappsv1.UpdateConcurrency `json:"updateStrategy,omitempty"` - PodManagementPolicy *appsv1.PodManagementPolicyType `json:"podManagementPolicy,omitempty"` - ParallelPodManagementConcurrency *intstr.IntOrString `json:"parallelPodManagementConcurrency,omitempty"` - PodUpdatePolicy *kbappsv1.InstanceUpdatePolicyType `json:"podUpdatePolicy,omitempty"` - PolicyRules []rbacv1.PolicyRule `json:"policyRules,omitempty"` + Roles []kbappsv1.ReplicaRole `json:"roles,omitempty"` + UpdateStrategy *kbappsv1.UpdateStrategy `json:"updateStrategy,omitempty"` + PodManagementPolicy *appsv1.PodManagementPolicyType `json:"podManagementPolicy,omitempty"` + ParallelPodManagementConcurrency *intstr.IntOrString `json:"parallelPodManagementConcurrency,omitempty"` + PolicyRules []rbacv1.PolicyRule `json:"policyRules,omitempty"` LifecycleActions *kbappsv1.ComponentLifecycleActions `json:"lifecycleActions,omitempty"` SystemAccounts []kbappsv1.SystemAccount `json:"systemAccounts,omitempty"` Volumes []kbappsv1.ComponentVolume `json:"volumes,omitempty"` diff --git a/pkg/controller/factory/builder_test.go b/pkg/controller/factory/builder_test.go index 30de73608c7..a9977a2e832 100644 --- a/pkg/controller/factory/builder_test.go +++ b/pkg/controller/factory/builder_test.go @@ -126,9 +126,13 @@ var _ = Describe("builder", func() { // test role probe Expect(its.Spec.RoleProbe).Should(BeNil()) - // test member update strategy - Expect(its.Spec.MemberUpdateStrategy).ShouldNot(BeNil()) - Expect(*its.Spec.MemberUpdateStrategy).Should(BeEquivalentTo(workloads.BestEffortParallelUpdateStrategy)) + // test update strategy + Expect(its.Spec.UpdateStrategy).ShouldNot(BeNil()) + Expect(its.Spec.UpdateStrategy.InstanceUpdatePolicy).ShouldNot(BeNil()) + Expect(*its.Spec.UpdateStrategy.InstanceUpdatePolicy).Should(BeEquivalentTo(workloads.PreferInPlaceInstanceUpdatePolicyType)) + Expect(its.Spec.UpdateStrategy.RollingUpdate).ShouldNot(BeNil()) + Expect(its.Spec.UpdateStrategy.RollingUpdate.UpdateConcurrency).ShouldNot(BeNil()) + Expect(*its.Spec.UpdateStrategy.RollingUpdate.UpdateConcurrency).Should(BeEquivalentTo(workloads.BestEffortParallelConcurrency)) }) It("builds ConfigMap with template correctly", func() { diff --git a/pkg/controller/instanceset/reconciler_update.go b/pkg/controller/instanceset/reconciler_update.go index 7e8c453063e..a06eaf8b9fc 100644 --- a/pkg/controller/instanceset/reconciler_update.go +++ b/pkg/controller/instanceset/reconciler_update.go @@ -23,7 +23,6 @@ import ( "fmt" "time" - apps "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -93,13 +92,8 @@ func (r *updateReconciler) Reconcile(tree *kubebuilderx.ObjectTree) (kubebuilder } // 3. do update - // do nothing if UpdateStrategyType is 'OnDelete' - if its.Spec.UpdateStrategy.Type == apps.OnDeleteStatefulSetStrategyType { - return kubebuilderx.Continue, nil - } - // handle 'RollingUpdate' - partition, maxUnavailable, err := parsePartitionNMaxUnavailable(its.Spec.UpdateStrategy.RollingUpdate, len(oldPodList)) + partition, maxUnavailable, err := parseReplicasNMaxUnavailable(its.Spec.UpdateStrategy, len(oldPodList)) if err != nil { return kubebuilderx.Continue, err } @@ -114,8 +108,7 @@ func (r *updateReconciler) Reconcile(tree *kubebuilderx.ObjectTree) (kubebuilder // if it's a roleful InstanceSet, we use updateCount to represent Pods can be updated according to the spec.memberUpdateStrategy. updateCount := len(oldPodList) if len(its.Spec.Roles) > 0 { - itsForPlan := getInstanceSetForUpdatePlan(its) - plan := NewUpdatePlan(*itsForPlan, oldPodList, IsPodUpdated) + plan := NewUpdatePlan(*its, oldPodList, IsPodUpdated) podsToBeUpdated, err := plan.Execute() if err != nil { return kubebuilderx.Continue, err @@ -160,9 +153,13 @@ func (r *updateReconciler) Reconcile(tree *kubebuilderx.ObjectTree) (kubebuilder if err != nil { return kubebuilderx.Continue, err } - if its.Spec.PodUpdatePolicy == workloads.StrictInPlacePodUpdatePolicyType && updatePolicy == RecreatePolicy { - message := fmt.Sprintf("InstanceSet %s/%s blocks on update as the PodUpdatePolicy is %s and the pod %s can not inplace update", - its.Namespace, its.Name, workloads.StrictInPlacePodUpdatePolicyType, pod.Name) + instanceUpdatePolicy := workloads.PreferInPlaceInstanceUpdatePolicyType + if its.Spec.UpdateStrategy != nil && its.Spec.UpdateStrategy.InstanceUpdatePolicy != nil { + instanceUpdatePolicy = *its.Spec.UpdateStrategy.InstanceUpdatePolicy + } + if instanceUpdatePolicy == workloads.StrictInPlaceInstanceUpdatePolicyType && updatePolicy == RecreatePolicy { + message := fmt.Sprintf("InstanceSet %s/%s blocks on update as the InstanceUpdatePolicy is %s and the pod %s can not inplace update", + its.Namespace, its.Name, workloads.StrictInPlaceInstanceUpdatePolicyType, pod.Name) if tree != nil && tree.EventRecorder != nil { tree.EventRecorder.Eventf(its, corev1.EventTypeWarning, EventReasonStrictInPlace, message) } @@ -209,39 +206,33 @@ func buildBlockedCondition(its *workloads.InstanceSet, message string) *metav1.C } } -func getInstanceSetForUpdatePlan(its *workloads.InstanceSet) *workloads.InstanceSet { - if its.Spec.MemberUpdateStrategy != nil { - return its - } - itsForPlan := its.DeepCopy() - updateStrategy := workloads.SerialUpdateStrategy - if its.Spec.PodManagementPolicy == apps.ParallelPodManagement { - updateStrategy = workloads.ParallelUpdateStrategy - } - itsForPlan.Spec.MemberUpdateStrategy = &updateStrategy - return itsForPlan -} - -func parsePartitionNMaxUnavailable(rollingUpdate *apps.RollingUpdateStatefulSetStrategy, replicas int) (int, int, error) { - partition := replicas +func parseReplicasNMaxUnavailable(updateStrategy *workloads.UpdateStrategy, totalReplicas int) (int, int, error) { + replicas := totalReplicas maxUnavailable := 1 + if updateStrategy == nil { + return replicas, maxUnavailable, nil + } + rollingUpdate := updateStrategy.RollingUpdate if rollingUpdate == nil { - return partition, maxUnavailable, nil + return replicas, maxUnavailable, nil } - if rollingUpdate.Partition != nil { - partition = int(*rollingUpdate.Partition) + var err error + if rollingUpdate.Replicas != nil { + replicas, err = intstr.GetScaledValueFromIntOrPercent(rollingUpdate.Replicas, totalReplicas, false) + if err != nil { + return replicas, maxUnavailable, err + } } if rollingUpdate.MaxUnavailable != nil { - maxUnavailableNum, err := intstr.GetScaledValueFromIntOrPercent(intstr.ValueOrDefault(rollingUpdate.MaxUnavailable, intstr.FromInt32(1)), replicas, false) + maxUnavailable, err = intstr.GetScaledValueFromIntOrPercent(intstr.ValueOrDefault(rollingUpdate.MaxUnavailable, intstr.FromInt32(1)), totalReplicas, false) if err != nil { return 0, 0, err } // maxUnavailable might be zero for small percentage with round down. // So we have to enforce it not to be less than 1. - if maxUnavailableNum < 1 { - maxUnavailableNum = 1 + if maxUnavailable < 1 { + maxUnavailable = 1 } - maxUnavailable = maxUnavailableNum } - return partition, maxUnavailable, nil + return replicas, maxUnavailable, nil } diff --git a/pkg/controller/instanceset/reconciler_update_test.go b/pkg/controller/instanceset/reconciler_update_test.go index feb067449e7..8b888cc59c5 100644 --- a/pkg/controller/instanceset/reconciler_update_test.go +++ b/pkg/controller/instanceset/reconciler_update_test.go @@ -159,11 +159,11 @@ var _ = Describe("update reconciler test", func() { Expect(err).Should(BeNil()) root, ok := partitionTree.GetRoot().(*workloads.InstanceSet) Expect(ok).Should(BeTrue()) - partition := int32(3) + updateReplicas := intstr.FromInt32(3) maxUnavailable := intstr.FromInt32(2) - root.Spec.UpdateStrategy = appsv1.StatefulSetUpdateStrategy{ - RollingUpdate: &appsv1.RollingUpdateStatefulSetStrategy{ - Partition: &partition, + root.Spec.UpdateStrategy = &workloads.UpdateStrategy{ + RollingUpdate: &workloads.RollingUpdate{ + Replicas: &updateReplicas, MaxUnavailable: &maxUnavailable, }, } @@ -179,9 +179,9 @@ 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{ - Partition: &partition, + root.Spec.UpdateStrategy = &workloads.UpdateStrategy{ + RollingUpdate: &workloads.RollingUpdate{ + Replicas: &updateReplicas, MaxUnavailable: &maxUnavailable, }, } @@ -198,17 +198,6 @@ var _ = Describe("update reconciler test", func() { Expect(res).Should(Equal(kubebuilderx.Continue)) expectUpdatedPods(partitionTree, []string{"bar-foo-0"}) - By("reconcile with UpdateStrategy='OnDelete'") - onDeleteTree, err := tree.DeepCopy() - Expect(err).Should(BeNil()) - root, ok = onDeleteTree.GetRoot().(*workloads.InstanceSet) - Expect(ok).Should(BeTrue()) - root.Spec.UpdateStrategy.Type = appsv1.OnDeleteStatefulSetStrategyType - res, err = reconciler.Reconcile(onDeleteTree) - Expect(err).Should(BeNil()) - Expect(res).Should(Equal(kubebuilderx.Continue)) - expectUpdatedPods(onDeleteTree, []string{}) - // order: bar-hello-0, bar-foo-1, bar-foo-0, bar-3, bar-2, bar-1, bar-0 // expected: bar-hello-0 being deleted By("reconcile with PodUpdatePolicy='PreferInPlace'") @@ -216,7 +205,10 @@ var _ = Describe("update reconciler test", func() { Expect(err).Should(BeNil()) root, ok = preferInPlaceTree.GetRoot().(*workloads.InstanceSet) Expect(ok).Should(BeTrue()) - root.Spec.PodUpdatePolicy = workloads.PreferInPlacePodUpdatePolicyType + instanceUpdatePolicy := workloads.PreferInPlaceInstanceUpdatePolicyType + root.Spec.UpdateStrategy = &workloads.UpdateStrategy{ + InstanceUpdatePolicy: &instanceUpdatePolicy, + } // try to add env to instanceHello to trigger the recreation root.Spec.Instances[0].Env = []corev1.EnvVar{ { @@ -234,7 +226,10 @@ var _ = Describe("update reconciler test", func() { Expect(err).Should(BeNil()) root, ok = strictInPlaceTree.GetRoot().(*workloads.InstanceSet) Expect(ok).Should(BeTrue()) - root.Spec.PodUpdatePolicy = workloads.StrictInPlacePodUpdatePolicyType + instanceUpdatePolicy = workloads.StrictInPlaceInstanceUpdatePolicyType + root.Spec.UpdateStrategy = &workloads.UpdateStrategy{ + InstanceUpdatePolicy: &instanceUpdatePolicy, + } // try to add env to instanceHello to trigger the recreation root.Spec.Instances[0].Env = []corev1.EnvVar{ { diff --git a/pkg/controller/instanceset/update_plan.go b/pkg/controller/instanceset/update_plan.go index 7c05e6aa60a..343356f6644 100644 --- a/pkg/controller/instanceset/update_plan.go +++ b/pkg/controller/instanceset/update_plan.go @@ -97,7 +97,8 @@ 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 + updateConcurrency := getUpdateConcurrency(&p.its) + serialUpdate := updateConcurrency == workloads.SerialConcurrency hasRoleProbed := len(p.its.Status.MembersStatus) > 0 if !serialUpdate || hasRoleProbed { return ErrWait @@ -121,20 +122,18 @@ func (p *realUpdatePlan) build() { root := &model.ObjectVertex{} p.dag.AddVertex(root) - if p.its.Spec.MemberUpdateStrategy == nil { - return - } + updateConcurrency := getUpdateConcurrency(&p.its) rolePriorityMap := ComposeRolePriorityMap(p.its.Spec.Roles) SortPods(p.pods, rolePriorityMap, false) // generate plan by MemberUpdateStrategy - switch *p.its.Spec.MemberUpdateStrategy { - case workloads.SerialUpdateStrategy: + switch updateConcurrency { + case workloads.SerialConcurrency: p.buildSerialUpdatePlan() - case workloads.ParallelUpdateStrategy: + case workloads.ParallelConcurrency: p.buildParallelUpdatePlan() - case workloads.BestEffortParallelUpdateStrategy: + case workloads.BestEffortParallelConcurrency: p.buildBestEffortParallelUpdatePlan(rolePriorityMap) } } diff --git a/pkg/controller/instanceset/update_plan_test.go b/pkg/controller/instanceset/update_plan_test.go index a789130d9d5..8a33a0fdbe0 100644 --- a/pkg/controller/instanceset/update_plan_test.go +++ b/pkg/controller/instanceset/update_plan_test.go @@ -121,8 +121,12 @@ 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 + updateConcurrency := workloads.SerialConcurrency + its.Spec.UpdateStrategy = &workloads.UpdateStrategy{ + RollingUpdate: &workloads.RollingUpdate{ + UpdateConcurrency: &updateConcurrency, + }, + } expectedPlan := [][]*corev1.Pod{ {pod4}, {pod2}, @@ -137,8 +141,12 @@ 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 + updateConcurrency := workloads.SerialConcurrency + its.Spec.UpdateStrategy = &workloads.UpdateStrategy{ + RollingUpdate: &workloads.RollingUpdate{ + UpdateConcurrency: &updateConcurrency, + }, + } expectedPlan := [][]*corev1.Pod{ {pod4}, {pod2}, @@ -153,8 +161,12 @@ 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 + updateConcurrency := workloads.ParallelConcurrency + its.Spec.UpdateStrategy = &workloads.UpdateStrategy{ + RollingUpdate: &workloads.RollingUpdate{ + UpdateConcurrency: &updateConcurrency, + }, + } expectedPlan := [][]*corev1.Pod{ {pod0, pod1, pod2, pod3, pod4, pod5, pod6}, } @@ -163,8 +175,12 @@ 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 + updateConcurrency := workloads.BestEffortParallelConcurrency + its.Spec.UpdateStrategy = &workloads.UpdateStrategy{ + RollingUpdate: &workloads.RollingUpdate{ + UpdateConcurrency: &updateConcurrency, + }, + } expectedPlan := [][]*corev1.Pod{ {pod2, pod3, pod4, pod6}, {pod1}, @@ -176,8 +192,12 @@ 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 + updateConcurrency := workloads.SerialConcurrency + its.Spec.UpdateStrategy = &workloads.UpdateStrategy{ + RollingUpdate: &workloads.RollingUpdate{ + UpdateConcurrency: &updateConcurrency, + }, + } its.Spec.Roles = nil for _, pod := range []*corev1.Pod{pod0, pod1, pod2, pod3, pod4, pod5, pod6} { labels := pod.Labels diff --git a/pkg/controller/instanceset/utils.go b/pkg/controller/instanceset/utils.go index d3cd1817586..9cd881ab75e 100644 --- a/pkg/controller/instanceset/utils.go +++ b/pkg/controller/instanceset/utils.go @@ -266,3 +266,13 @@ func CalculateConcurrencyReplicas(concurrency *intstr.IntOrString, replicas int) pValue = integer.IntMax(integer.IntMin(pValue, replicas), 1) return pValue, nil } + +func getUpdateConcurrency(its *workloads.InstanceSet) workloads.UpdateConcurrency { + updateConcurrency := workloads.SerialConcurrency + if its.Spec.UpdateStrategy != nil && + its.Spec.UpdateStrategy.RollingUpdate != nil && + its.Spec.UpdateStrategy.RollingUpdate.UpdateConcurrency != nil { + updateConcurrency = *its.Spec.UpdateStrategy.RollingUpdate.UpdateConcurrency + } + return updateConcurrency +} diff --git a/pkg/testutil/apps/componentdefinition_factory.go b/pkg/testutil/apps/componentdefinition_factory.go index 1aa6b160a66..c9e24b95cd3 100644 --- a/pkg/testutil/apps/componentdefinition_factory.go +++ b/pkg/testutil/apps/componentdefinition_factory.go @@ -276,8 +276,8 @@ func (f *MockComponentDefinitionFactory) AddSystemAccount(accountName string, in return f } -func (f *MockComponentDefinitionFactory) SetUpdateStrategy(strategy *kbappsv1.UpdateConcurrency) *MockComponentDefinitionFactory { - f.Get().Spec.UpdateStrategyConstraint = strategy +func (f *MockComponentDefinitionFactory) SetUpdateConcurrency(concurrency *kbappsv1.UpdateConcurrency) *MockComponentDefinitionFactory { + f.Get().Spec.UpdateConcurrency = concurrency return f } diff --git a/pkg/testutil/apps/constant.go b/pkg/testutil/apps/constant.go index 8e6ad00814f..ec4ad432bd8 100644 --- a/pkg/testutil/apps/constant.go +++ b/pkg/testutil/apps/constant.go @@ -206,7 +206,8 @@ var ( }, }, }, - UpdateStrategyConstraint: &[]appsv1.UpdateConcurrency{appsv1.BestEffortParallelConcurrency}[0], + UpdateConcurrency: &[]appsv1.UpdateConcurrency{appsv1.BestEffortParallelConcurrency}[0], + InstanceUpdatePolicy: &[]appsv1.InstanceUpdatePolicyType{appsv1.PreferInPlaceInstanceUpdatePolicyType}[0], Roles: []appsv1.ReplicaRole{ { Name: "leader", diff --git a/pkg/testutil/apps/instance_set_factoy.go b/pkg/testutil/apps/instance_set_factoy.go index e8a12b99077..8eecf14c81b 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,9 +59,6 @@ func NewInstanceSetFactory(namespace, name string, clusterName string, component }, }, }, - UpdateStrategy: appsv1.StatefulSetUpdateStrategy{ - Type: appsv1.OnDeleteStatefulSetStrategyType, - }, }, }, f) return f From 4692b7b317e30bf2f15530a425446129cde169b5 Mon Sep 17 00:00:00 2001 From: free6om Date: Thu, 12 Dec 2024 15:02:59 +0800 Subject: [PATCH 07/11] v1alpha1 <-> v1 conversion --- apis/apps/v1alpha1/cluster_conversion.go | 66 +++++++++++++++++-- apis/apps/v1alpha1/component_conversion.go | 30 +++++++-- .../componentdefinition_conversion.go | 8 +++ .../v1alpha1/instanceset_conversion.go | 58 ++++++++++++++++ 4 files changed, 154 insertions(+), 8 deletions(-) diff --git a/apis/apps/v1alpha1/cluster_conversion.go b/apis/apps/v1alpha1/cluster_conversion.go index b5ef1e578fa..181f87e661d 100644 --- a/apis/apps/v1alpha1/cluster_conversion.go +++ b/apis/apps/v1alpha1/cluster_conversion.go @@ -20,6 +20,8 @@ along with this program. If not, see . package v1alpha1 import ( + "slices" + "github.com/jinzhu/copier" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -104,13 +106,13 @@ func (r *Cluster) changesToCluster(cluster *appsv1.Cluster) { // - volumeClaimTemplates // spec: // resources: corev1.ResourceRequirements -> corev1.VolumeResourceRequirements - // podUpdatePolicy: *workloads.InstanceUpdatePolicyType -> *InstanceUpdatePolicyType + // podUpdatePolicy: *workloads.InstanceUpdatePolicyType -> *UpdateStrategy.InstanceUpdatePolicyType // sharings // - template // volumeClaimTemplates // spec: // resources: corev1.ResourceRequirements -> corev1.VolumeResourceRequirements - // podUpdatePolicy: *workloads.InstanceUpdatePolicyType -> *InstanceUpdatePolicyType + // podUpdatePolicy: *workloads.InstanceUpdatePolicyType -> *UpdateStrategy.InstanceUpdatePolicyType // status // components // - message: ComponentMessageMap -> map[string]string @@ -122,6 +124,38 @@ func (r *Cluster) changesToCluster(cluster *appsv1.Cluster) { } else { cluster.Spec.TerminationPolicy = appsv1.TerminationPolicyType(r.Spec.TerminationPolicy) } + for i := range r.Spec.ComponentSpecs { + spec := &r.Spec.ComponentSpecs[i] + if spec.PodUpdatePolicy == nil { + continue + } + index := slices.IndexFunc(cluster.Spec.ComponentSpecs, func(componentSpec appsv1.ClusterComponentSpec) bool { + return spec.Name == componentSpec.Name + }) + if index < 0 { + continue + } + if cluster.Spec.ComponentSpecs[index].UpdateStrategy == nil { + cluster.Spec.ComponentSpecs[index].UpdateStrategy = &appsv1.UpdateStrategy{} + } + cluster.Spec.ComponentSpecs[index].UpdateStrategy.InstanceUpdatePolicy = (*appsv1.InstanceUpdatePolicyType)(spec.PodUpdatePolicy) + } + for i := range r.Spec.ShardingSpecs { + spec := &r.Spec.ShardingSpecs[i] + if spec.Template.PodUpdatePolicy == nil { + continue + } + index := slices.IndexFunc(cluster.Spec.Shardings, func(sharding appsv1.ClusterSharding) bool { + return spec.Name == sharding.Name + }) + if index < 0 { + continue + } + if cluster.Spec.Shardings[index].Template.UpdateStrategy == nil { + cluster.Spec.Shardings[index].Template.UpdateStrategy = &appsv1.UpdateStrategy{} + } + cluster.Spec.Shardings[index].Template.UpdateStrategy.InstanceUpdatePolicy = (*appsv1.InstanceUpdatePolicyType)(spec.Template.PodUpdatePolicy) + } } func (r *Cluster) changesFromCluster(cluster *appsv1.Cluster) { @@ -132,13 +166,13 @@ func (r *Cluster) changesFromCluster(cluster *appsv1.Cluster) { // - volumeClaimTemplates // spec: // resources: corev1.ResourceRequirements -> corev1.VolumeResourceRequirements - // podUpdatePolicy: *workloads.InstanceUpdatePolicyType -> *InstanceUpdatePolicyType + // podUpdatePolicy: *workloads.InstanceUpdatePolicyType -> *UpdateStrategy.InstanceUpdatePolicyType // sharings // - template // volumeClaimTemplates // spec: // resources: corev1.ResourceRequirements -> corev1.VolumeResourceRequirements - // podUpdatePolicy: *workloads.InstanceUpdatePolicyType -> *InstanceUpdatePolicyType + // podUpdatePolicy: *workloads.InstanceUpdatePolicyType -> *UpdateStrategy.InstanceUpdatePolicyType // status // components // - message: ComponentMessageMap -> map[string]string @@ -146,6 +180,30 @@ func (r *Cluster) changesFromCluster(cluster *appsv1.Cluster) { r.Spec.ClusterDefRef = cluster.Spec.ClusterDef } // appsv1.TerminationPolicyType is a subset of appsv1alpha1.TerminationPolicyType, it can be converted directly. + for _, spec := range cluster.Spec.ComponentSpecs { + if spec.UpdateStrategy == nil || spec.UpdateStrategy.InstanceUpdatePolicy == nil { + continue + } + index := slices.IndexFunc(r.Spec.ComponentSpecs, func(componentSpec ClusterComponentSpec) bool { + return spec.Name == componentSpec.Name + }) + if index < 0 { + continue + } + r.Spec.ComponentSpecs[index].PodUpdatePolicy = (*workloads.PodUpdatePolicyType)(spec.UpdateStrategy.InstanceUpdatePolicy) + } + for _, sharding := range cluster.Spec.Shardings { + if sharding.Template.UpdateStrategy == nil || sharding.Template.UpdateStrategy.InstanceUpdatePolicy == nil { + continue + } + index := slices.IndexFunc(r.Spec.ShardingSpecs, func(spec ShardingSpec) bool { + return spec.Name == sharding.Name + }) + if index < 0 { + continue + } + r.Spec.ShardingSpecs[index].Template.PodUpdatePolicy = (*workloads.PodUpdatePolicyType)(sharding.Template.UpdateStrategy.InstanceUpdatePolicy) + } } type clusterConverter struct { diff --git a/apis/apps/v1alpha1/component_conversion.go b/apis/apps/v1alpha1/component_conversion.go index 11d5da599c5..6f1104ff51e 100644 --- a/apis/apps/v1alpha1/component_conversion.go +++ b/apis/apps/v1alpha1/component_conversion.go @@ -26,6 +26,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/conversion" appsv1 "github.com/apecloud/kubeblocks/apis/apps/v1" + "github.com/apecloud/kubeblocks/apis/workloads/v1alpha1" ) // ConvertTo converts this Component to the Hub version (v1). @@ -76,8 +77,7 @@ func (r *Component) ConvertFrom(srcRaw conversion.Hub) error { func (r *Component) incrementConvertTo(dstRaw metav1.Object) (incrementChange, error) { // changed - comp := dstRaw.(*appsv1.Component) - comp.Status.Message = r.Status.Message + r.changesToComponent(dstRaw.(*appsv1.Component)) // deleted return &componentConverter{ @@ -97,12 +97,34 @@ func (r *Component) incrementConvertFrom(srcRaw metav1.Object, ic incrementChang r.Spec.InstanceUpdateStrategy = c.InstanceUpdateStrategy // changed - comp := srcRaw.(*appsv1.Component) - r.Status.Message = comp.Status.Message + r.changesFromComponent(srcRaw.(*appsv1.Component)) return nil } +func (r *Component) changesToComponent(comp *appsv1.Component) { + // changed: + // spec + // podUpdatePolicy: *workloads.InstanceUpdatePolicyType -> *UpdateStrategy.InstanceUpdatePolicyType + comp.Status.Message = r.Status.Message + if r.Spec.PodUpdatePolicy != nil { + if comp.Spec.UpdateStrategy == nil { + comp.Spec.UpdateStrategy = &appsv1.UpdateStrategy{} + } + comp.Spec.UpdateStrategy.InstanceUpdatePolicy = (*appsv1.InstanceUpdatePolicyType)(r.Spec.PodUpdatePolicy) + } +} + +func (r *Component) changesFromComponent(comp *appsv1.Component) { + // changed: + // spec + // podUpdatePolicy: *workloads.InstanceUpdatePolicyType -> *UpdateStrategy.InstanceUpdatePolicyType + if comp.Spec.UpdateStrategy != nil && comp.Spec.UpdateStrategy.InstanceUpdatePolicy != nil { + r.Spec.PodUpdatePolicy = (*v1alpha1.PodUpdatePolicyType)(comp.Spec.UpdateStrategy.InstanceUpdatePolicy) + } + r.Status.Message = comp.Status.Message +} + type componentConverter struct { EnabledLogs []string `json:"enabledLogs,omitempty"` Affinity *Affinity `json:"affinity,omitempty"` diff --git a/apis/apps/v1alpha1/componentdefinition_conversion.go b/apis/apps/v1alpha1/componentdefinition_conversion.go index bc16131e525..2bfe97e124f 100644 --- a/apis/apps/v1alpha1/componentdefinition_conversion.go +++ b/apis/apps/v1alpha1/componentdefinition_conversion.go @@ -115,6 +115,7 @@ func (r *ComponentDefinition) changesToComponentDefinition(cmpd *appsv1.Componen // - ValueFrom // componentVarRef: // instanceNames -> podNames + // updateStrategy -> updateConcurrency // lifecycleActions for _, v := range r.Spec.Vars { @@ -125,6 +126,9 @@ func (r *ComponentDefinition) changesToComponentDefinition(cmpd *appsv1.Componen return err } } + if r.Spec.UpdateStrategy != nil { + cmpd.Spec.UpdateConcurrency = (*appsv1.UpdateConcurrency)(r.Spec.UpdateStrategy) + } r.toV1LifecycleActions(cmpd) return nil } @@ -136,6 +140,7 @@ func (r *ComponentDefinition) changesFromComponentDefinition(cmpd *appsv1.Compon // - ValueFrom // componentVarRef: // instanceNames -> podNames + // updateStrategy -> updateConcurrency // lifecycleActions for _, v := range cmpd.Spec.Vars { @@ -146,6 +151,9 @@ func (r *ComponentDefinition) changesFromComponentDefinition(cmpd *appsv1.Compon return err } } + if cmpd.Spec.UpdateConcurrency != nil { + r.Spec.UpdateStrategy = (*UpdateStrategy)(cmpd.Spec.UpdateConcurrency) + } r.fromV1LifecycleActions(cmpd) return nil } diff --git a/apis/workloads/v1alpha1/instanceset_conversion.go b/apis/workloads/v1alpha1/instanceset_conversion.go index 7da2721cf7e..75b86909124 100644 --- a/apis/workloads/v1alpha1/instanceset_conversion.go +++ b/apis/workloads/v1alpha1/instanceset_conversion.go @@ -21,6 +21,9 @@ package v1alpha1 import ( "github.com/jinzhu/copier" + appsv1 "k8s.io/api/apps/v1" + "k8s.io/apimachinery/pkg/util/intstr" + "k8s.io/utils/pointer" "sigs.k8s.io/controller-runtime/pkg/conversion" workloadsv1 "github.com/apecloud/kubeblocks/apis/workloads/v1" @@ -37,6 +40,7 @@ func (r *InstanceSet) ConvertTo(dstRaw conversion.Hub) error { if err := copier.Copy(&dst.Spec, &r.Spec); err != nil { return err } + r.changesToInstanceSet(dst) // status if err := copier.Copy(&dst.Status, &r.Status); err != nil { @@ -65,3 +69,57 @@ func (r *InstanceSet) ConvertFrom(srcRaw conversion.Hub) error { return nil } + +func (r *InstanceSet) changesToInstanceSet(its *workloadsv1.InstanceSet) { + // changed: + // spec + // podUpdatePolicy -> updateStrategy.instanceUpdatePolicy + // memberUpdateStrategy -> updateStrategy.rollingUpdate.updateConcurrency + // updateStrategy.rollingUpdate.partition -> updateStrategy.rollingUpdate.replicas + if its.Spec.UpdateStrategy == nil { + its.Spec.UpdateStrategy = &workloadsv1.UpdateStrategy{} + } + its.Spec.UpdateStrategy.InstanceUpdatePolicy = (*workloadsv1.InstanceUpdatePolicyType)(&r.Spec.PodUpdatePolicy) + if r.Spec.MemberUpdateStrategy != nil { + if its.Spec.UpdateStrategy.RollingUpdate == nil { + its.Spec.UpdateStrategy.RollingUpdate = &workloadsv1.RollingUpdate{} + } + its.Spec.UpdateStrategy.RollingUpdate.UpdateConcurrency = (*workloadsv1.UpdateConcurrency)(r.Spec.MemberUpdateStrategy) + } + if r.Spec.UpdateStrategy.RollingUpdate != nil { + if r.Spec.UpdateStrategy.RollingUpdate.Partition != nil { + if its.Spec.UpdateStrategy.RollingUpdate == nil { + its.Spec.UpdateStrategy.RollingUpdate = &workloadsv1.RollingUpdate{} + } + replicas := intstr.FromInt32(*r.Spec.UpdateStrategy.RollingUpdate.Partition) + its.Spec.UpdateStrategy.RollingUpdate.Replicas = &replicas + } + } +} + +func (r *InstanceSet) changesFromInstanceSet(its *workloadsv1.InstanceSet) { + // changed: + // spec + // podUpdatePolicy -> updateStrategy.instanceUpdatePolicy + // memberUpdateStrategy -> updateStrategy.rollingUpdate.updateConcurrency + // updateStrategy.rollingUpdate.partition -> updateStrategy.rollingUpdate.replicas + if its.Spec.UpdateStrategy == nil { + return + } + if its.Spec.UpdateStrategy.InstanceUpdatePolicy != nil { + r.Spec.PodUpdatePolicy = PodUpdatePolicyType(*its.Spec.UpdateStrategy.InstanceUpdatePolicy) + } + if its.Spec.UpdateStrategy.RollingUpdate == nil { + return + } + if its.Spec.UpdateStrategy.RollingUpdate.UpdateConcurrency != nil { + r.Spec.MemberUpdateStrategy = (*MemberUpdateStrategy)(its.Spec.UpdateStrategy.RollingUpdate.UpdateConcurrency) + } + if its.Spec.UpdateStrategy.RollingUpdate.Replicas != nil { + if r.Spec.UpdateStrategy.RollingUpdate == nil { + r.Spec.UpdateStrategy.RollingUpdate = &appsv1.RollingUpdateStatefulSetStrategy{} + } + partition, _ := intstr.GetScaledValueFromIntOrPercent(its.Spec.UpdateStrategy.RollingUpdate.Replicas, int(*its.Spec.Replicas), false) + r.Spec.UpdateStrategy.RollingUpdate.Partition = pointer.Int32(int32(partition)) + } +} From c47b10287681488b406e6fb2838c64026f96be47 Mon Sep 17 00:00:00 2001 From: free6om Date: Thu, 12 Dec 2024 15:43:14 +0800 Subject: [PATCH 08/11] fix staticcheck error --- apis/workloads/v1alpha1/instanceset_conversion.go | 1 + 1 file changed, 1 insertion(+) diff --git a/apis/workloads/v1alpha1/instanceset_conversion.go b/apis/workloads/v1alpha1/instanceset_conversion.go index 75b86909124..e1187ddf5d4 100644 --- a/apis/workloads/v1alpha1/instanceset_conversion.go +++ b/apis/workloads/v1alpha1/instanceset_conversion.go @@ -61,6 +61,7 @@ func (r *InstanceSet) ConvertFrom(srcRaw conversion.Hub) error { if err := copier.Copy(&r.Spec, &src.Spec); err != nil { return err } + r.changesFromInstanceSet(src) // status if err := copier.Copy(&r.Status, &src.Status); err != nil { From 7ffb8078fd8160e8b8dac7b85897b75fdec5b6e3 Mon Sep 17 00:00:00 2001 From: free6om Date: Mon, 23 Dec 2024 10:32:16 +0800 Subject: [PATCH 09/11] remove InstanceUpdatePolicy from cmpd --- apis/apps/v1/componentdefinition_types.go | 12 ------------ apis/apps/v1/zz_generated.deepcopy.go | 5 ----- .../apps.kubeblocks.io_componentdefinitions.yaml | 14 -------------- .../apps.kubeblocks.io_componentdefinitions.yaml | 14 -------------- pkg/controller/component/synthesize_component.go | 8 -------- pkg/testutil/apps/constant.go | 1 - 6 files changed, 54 deletions(-) diff --git a/apis/apps/v1/componentdefinition_types.go b/apis/apps/v1/componentdefinition_types.go index e7f59c0e4b5..5cd57edcab2 100644 --- a/apis/apps/v1/componentdefinition_types.go +++ b/apis/apps/v1/componentdefinition_types.go @@ -444,18 +444,6 @@ type ComponentDefinitionSpec struct { // +optional MinReadySeconds int32 `json:"minReadySeconds,omitempty"` - // Specifies how an instance should be updated. - // - // - `StrictInPlace` indicates that only allows in-place update. - // Any attempt to modify other fields that not support in-place update will be rejected. - // - `PreferInPlace` indicates that we will first attempt an in-place update of the instance. - // If that fails, it will fall back to the ReCreate, where instance will be recreated. - // Default value is "PreferInPlace". - // - // +kubebuilder:validation:Enum={StrictInPlace,PreferInPlace} - // +optional - InstanceUpdatePolicy *InstanceUpdatePolicyType `json:"instanceUpdatePolicy,omitempty"` - // Specifies the concurrency level for updating instances during a rolling update. // Available levels: // diff --git a/apis/apps/v1/zz_generated.deepcopy.go b/apis/apps/v1/zz_generated.deepcopy.go index bf14b491b57..2e35adeb186 100644 --- a/apis/apps/v1/zz_generated.deepcopy.go +++ b/apis/apps/v1/zz_generated.deepcopy.go @@ -1191,11 +1191,6 @@ func (in *ComponentDefinitionSpec) DeepCopyInto(out *ComponentDefinitionSpec) { *out = make([]ReplicaRole, len(*in)) copy(*out, *in) } - if in.InstanceUpdatePolicy != nil { - in, out := &in.InstanceUpdatePolicy, &out.InstanceUpdatePolicy - *out = new(InstanceUpdatePolicyType) - **out = **in - } if in.UpdateConcurrency != nil { in, out := &in.UpdateConcurrency, &out.UpdateConcurrency *out = new(UpdateConcurrency) diff --git a/config/crd/bases/apps.kubeblocks.io_componentdefinitions.yaml b/config/crd/bases/apps.kubeblocks.io_componentdefinitions.yaml index 5a9c213212e..27599fc79f9 100644 --- a/config/crd/bases/apps.kubeblocks.io_componentdefinitions.yaml +++ b/config/crd/bases/apps.kubeblocks.io_componentdefinitions.yaml @@ -4449,20 +4449,6 @@ spec: type: object type: array type: object - instanceUpdatePolicy: - description: |- - Specifies how an instance should be updated. - - - - `StrictInPlace` indicates that only allows in-place update. - Any attempt to modify other fields that not support in-place update will be rejected. - - `PreferInPlace` indicates that we will first attempt an in-place update of the instance. - If that fails, it will fall back to the ReCreate, where instance will be recreated. - Default value is "PreferInPlace". - enum: - - StrictInPlace - - PreferInPlace - type: string labels: additionalProperties: type: string diff --git a/deploy/helm/crds/apps.kubeblocks.io_componentdefinitions.yaml b/deploy/helm/crds/apps.kubeblocks.io_componentdefinitions.yaml index 5a9c213212e..27599fc79f9 100644 --- a/deploy/helm/crds/apps.kubeblocks.io_componentdefinitions.yaml +++ b/deploy/helm/crds/apps.kubeblocks.io_componentdefinitions.yaml @@ -4449,20 +4449,6 @@ spec: type: object type: array type: object - instanceUpdatePolicy: - description: |- - Specifies how an instance should be updated. - - - - `StrictInPlace` indicates that only allows in-place update. - Any attempt to modify other fields that not support in-place update will be rejected. - - `PreferInPlace` indicates that we will first attempt an in-place update of the instance. - If that fails, it will fall back to the ReCreate, where instance will be recreated. - Default value is "PreferInPlace". - enum: - - StrictInPlace - - PreferInPlace - type: string labels: additionalProperties: type: string diff --git a/pkg/controller/component/synthesize_component.go b/pkg/controller/component/synthesize_component.go index 3d9c0533e23..95b562b1e0e 100644 --- a/pkg/controller/component/synthesize_component.go +++ b/pkg/controller/component/synthesize_component.go @@ -170,14 +170,6 @@ func buildUpdateStrategy(synthesizeComp *SynthesizedComponent, comp *appsv1.Comp } } } - if compDef.Spec.InstanceUpdatePolicy != nil { - if updateStrategy == nil { - updateStrategy = &appsv1.UpdateStrategy{} - } - if updateStrategy.InstanceUpdatePolicy == nil { - updateStrategy.InstanceUpdatePolicy = compDef.Spec.InstanceUpdatePolicy - } - } if compDef.Spec.UpdateConcurrency != nil { if updateStrategy == nil { updateStrategy = &appsv1.UpdateStrategy{} diff --git a/pkg/testutil/apps/constant.go b/pkg/testutil/apps/constant.go index ec4ad432bd8..2db15cd5093 100644 --- a/pkg/testutil/apps/constant.go +++ b/pkg/testutil/apps/constant.go @@ -207,7 +207,6 @@ var ( }, }, UpdateConcurrency: &[]appsv1.UpdateConcurrency{appsv1.BestEffortParallelConcurrency}[0], - InstanceUpdatePolicy: &[]appsv1.InstanceUpdatePolicyType{appsv1.PreferInPlaceInstanceUpdatePolicyType}[0], Roles: []appsv1.ReplicaRole{ { Name: "leader", From 48d9c8512b6aaafd884f37853eb8e739836b8ea1 Mon Sep 17 00:00:00 2001 From: free6om Date: Mon, 23 Dec 2024 11:07:14 +0800 Subject: [PATCH 10/11] support OnDelete update strategy --- apis/apps/v1/types.go | 23 ++++ apis/workloads/v1/instanceset_types.go | 23 ++++ .../bases/apps.kubeblocks.io_clusters.yaml | 16 +++ .../bases/apps.kubeblocks.io_components.yaml | 8 ++ .../workloads.kubeblocks.io_instancesets.yaml | 8 ++ .../crds/apps.kubeblocks.io_clusters.yaml | 16 +++ .../crds/apps.kubeblocks.io_components.yaml | 8 ++ .../workloads.kubeblocks.io_instancesets.yaml | 8 ++ docs/developer_docs/api-reference/cluster.md | 128 ++++++++++++------ pkg/controller/component/its_convertor.go | 1 + .../component/synthesize_component.go | 1 + .../instanceset/reconciler_update.go | 5 + .../instanceset/reconciler_update_test.go | 13 ++ pkg/testutil/apps/constant.go | 2 +- pkg/testutil/apps/instance_set_factoy.go | 3 + 15 files changed, 219 insertions(+), 44 deletions(-) diff --git a/apis/apps/v1/types.go b/apis/apps/v1/types.go index 6369e87b4bb..b70c1e13d02 100644 --- a/apis/apps/v1/types.go +++ b/apis/apps/v1/types.go @@ -665,6 +665,12 @@ type InstanceTemplate struct { // UpdateStrategy defines fine-grained control over the spec update process of all instances. type UpdateStrategy struct { + // Indicates the type of the UpdateStrategy. + // Default is RollingUpdate. + // + // +optional + Type UpdateStrategyType `json:"type,omitempty"` + // Indicates how instances should be updated. // // - `StrictInPlace` indicates that only allows in-place update. @@ -683,6 +689,23 @@ type UpdateStrategy struct { RollingUpdate *RollingUpdate `json:"rollingUpdate,omitempty"` } +// UpdateStrategyType is a string enumeration type that enumerates +// all possible update strategies for the KubeBlocks controllers. +// +// +enum +// +kubebuilder:validation:Enum={RollingUpdate,OnDelete} +type UpdateStrategyType string + +const ( + // RollingUpdateStrategyType indicates that update will be + // applied to all Instances with respect to the InstanceSet + // ordering constraints. + RollingUpdateStrategyType UpdateStrategyType = "RollingUpdate" + // OnDeleteStrategyType indicates that ordered rolling restarts are disabled. Instances are recreated + // when they are manually deleted. + OnDeleteStrategyType UpdateStrategyType = "OnDelete" +) + // RollingUpdate specifies how the rolling update should be applied. type RollingUpdate struct { // Indicates the number of instances that should be updated during a rolling update. diff --git a/apis/workloads/v1/instanceset_types.go b/apis/workloads/v1/instanceset_types.go index e88baa08c69..5837bfbd76a 100644 --- a/apis/workloads/v1/instanceset_types.go +++ b/apis/workloads/v1/instanceset_types.go @@ -429,6 +429,12 @@ type SchedulingPolicy struct { // UpdateStrategy defines fine-grained control over the spec update process of all instances. type UpdateStrategy struct { + // Indicates the type of the UpdateStrategy. + // Default is RollingUpdate. + // + // +optional + Type UpdateStrategyType `json:"type,omitempty"` + // Indicates how instances should be updated. // // - `StrictInPlace` indicates that only allows in-place update. @@ -447,6 +453,23 @@ type UpdateStrategy struct { RollingUpdate *RollingUpdate `json:"rollingUpdate,omitempty"` } +// UpdateStrategyType is a string enumeration type that enumerates +// all possible update strategies for the KubeBlocks controllers. +// +// +enum +// +kubebuilder:validation:Enum={RollingUpdate,OnDelete} +type UpdateStrategyType string + +const ( + // RollingUpdateStrategyType indicates that update will be + // applied to all Instances with respect to the InstanceSet + // ordering constraints. + RollingUpdateStrategyType UpdateStrategyType = "RollingUpdate" + // OnDeleteStrategyType indicates that ordered rolling restarts are disabled. Instances are recreated + // when they are manually deleted. + OnDeleteStrategyType UpdateStrategyType = "OnDelete" +) + // RollingUpdate specifies how the rolling update should be applied. type RollingUpdate struct { // Indicates the number of instances that should be updated during a rolling update. diff --git a/config/crd/bases/apps.kubeblocks.io_clusters.yaml b/config/crd/bases/apps.kubeblocks.io_clusters.yaml index a8f51511f11..3d6ba2c3009 100644 --- a/config/crd/bases/apps.kubeblocks.io_clusters.yaml +++ b/config/crd/bases/apps.kubeblocks.io_clusters.yaml @@ -5410,6 +5410,14 @@ spec: to 'Serial'." type: string type: object + type: + description: |- + Indicates the type of the UpdateStrategy. + Default is RollingUpdate. + enum: + - RollingUpdate + - OnDelete + type: string type: object volumeClaimTemplates: description: |- @@ -14172,6 +14180,14 @@ spec: to 'Serial'." type: string type: object + type: + description: |- + Indicates the type of the UpdateStrategy. + Default is RollingUpdate. + enum: + - RollingUpdate + - OnDelete + type: string type: object volumeClaimTemplates: description: |- diff --git a/config/crd/bases/apps.kubeblocks.io_components.yaml b/config/crd/bases/apps.kubeblocks.io_components.yaml index ca5f89dbec8..ed49feac30a 100644 --- a/config/crd/bases/apps.kubeblocks.io_components.yaml +++ b/config/crd/bases/apps.kubeblocks.io_components.yaml @@ -5671,6 +5671,14 @@ spec: to 'Serial'." type: string type: object + type: + description: |- + Indicates the type of the UpdateStrategy. + Default is RollingUpdate. + enum: + - RollingUpdate + - OnDelete + type: string type: object volumeClaimTemplates: description: |- diff --git a/config/crd/bases/workloads.kubeblocks.io_instancesets.yaml b/config/crd/bases/workloads.kubeblocks.io_instancesets.yaml index b76985e1411..b14b0785072 100644 --- a/config/crd/bases/workloads.kubeblocks.io_instancesets.yaml +++ b/config/crd/bases/workloads.kubeblocks.io_instancesets.yaml @@ -11809,6 +11809,14 @@ spec: to 'Serial'." type: string type: object + type: + description: |- + Indicates the type of the UpdateStrategy. + Default is RollingUpdate. + enum: + - RollingUpdate + - OnDelete + type: string type: object volumeClaimTemplates: description: |- diff --git a/deploy/helm/crds/apps.kubeblocks.io_clusters.yaml b/deploy/helm/crds/apps.kubeblocks.io_clusters.yaml index a8f51511f11..3d6ba2c3009 100644 --- a/deploy/helm/crds/apps.kubeblocks.io_clusters.yaml +++ b/deploy/helm/crds/apps.kubeblocks.io_clusters.yaml @@ -5410,6 +5410,14 @@ spec: to 'Serial'." type: string type: object + type: + description: |- + Indicates the type of the UpdateStrategy. + Default is RollingUpdate. + enum: + - RollingUpdate + - OnDelete + type: string type: object volumeClaimTemplates: description: |- @@ -14172,6 +14180,14 @@ spec: to 'Serial'." type: string type: object + type: + description: |- + Indicates the type of the UpdateStrategy. + Default is RollingUpdate. + enum: + - RollingUpdate + - OnDelete + type: string type: object volumeClaimTemplates: description: |- diff --git a/deploy/helm/crds/apps.kubeblocks.io_components.yaml b/deploy/helm/crds/apps.kubeblocks.io_components.yaml index ca5f89dbec8..ed49feac30a 100644 --- a/deploy/helm/crds/apps.kubeblocks.io_components.yaml +++ b/deploy/helm/crds/apps.kubeblocks.io_components.yaml @@ -5671,6 +5671,14 @@ spec: to 'Serial'." type: string type: object + type: + description: |- + Indicates the type of the UpdateStrategy. + Default is RollingUpdate. + enum: + - RollingUpdate + - OnDelete + type: string type: object volumeClaimTemplates: description: |- diff --git a/deploy/helm/crds/workloads.kubeblocks.io_instancesets.yaml b/deploy/helm/crds/workloads.kubeblocks.io_instancesets.yaml index b76985e1411..b14b0785072 100644 --- a/deploy/helm/crds/workloads.kubeblocks.io_instancesets.yaml +++ b/deploy/helm/crds/workloads.kubeblocks.io_instancesets.yaml @@ -11809,6 +11809,14 @@ spec: to 'Serial'." type: string type: object + type: + description: |- + Indicates the type of the UpdateStrategy. + Default is RollingUpdate. + enum: + - RollingUpdate + - OnDelete + type: string type: object volumeClaimTemplates: description: |- diff --git a/docs/developer_docs/api-reference/cluster.md b/docs/developer_docs/api-reference/cluster.md index 619185fe75a..b52a1ae58be 100644 --- a/docs/developer_docs/api-reference/cluster.md +++ b/docs/developer_docs/api-reference/cluster.md @@ -1408,27 +1408,6 @@ This ensures the Pod’s stability and readiness to serve requests.

-instanceUpdatePolicy
- - -InstanceUpdatePolicyType - - - - -(Optional) -

Specifies how an instance should be updated.

-
    -
  • StrictInPlace indicates that only allows in-place update. -Any attempt to modify other fields that not support in-place update will be rejected.
  • -
  • PreferInPlace indicates that we will first attempt an in-place update of the instance. -If that fails, it will fall back to the ReCreate, where instance will be recreated. -Default value is “PreferInPlace”.
  • -
- - - - updateConcurrency
@@ -5259,27 +5238,6 @@ This ensures the Pod’s stability and readiness to serve requests.

-instanceUpdatePolicy
- -
-InstanceUpdatePolicyType - - - - -(Optional) -

Specifies how an instance should be updated.

-
    -
  • StrictInPlace indicates that only allows in-place update. -Any attempt to modify other fields that not support in-place update will be rejected.
  • -
  • PreferInPlace indicates that we will first attempt an in-place update of the instance. -If that fails, it will fall back to the ReCreate, where instance will be recreated. -Default value is “PreferInPlace”.
  • -
- - - - updateConcurrency
@@ -7939,7 +7897,7 @@ Add new or override existing volume claim templates.

InstanceUpdatePolicyType (string alias)

-(Appears on:ComponentDefinitionSpec, UpdateStrategy) +(Appears on:UpdateStrategy)

@@ -11625,6 +11583,21 @@ This ensures that only one replica is unavailable at a time during the update pr +type
+ + +UpdateStrategyType + + + + +(Optional) +

Indicates the type of the UpdateStrategy. +Default is RollingUpdate.

+ + + + instanceUpdatePolicy
@@ -11660,6 +11633,33 @@ RollingUpdate +

UpdateStrategyType +(string alias)

+

+(Appears on:UpdateStrategy) +

+
+

UpdateStrategyType is a string enumeration type that enumerates +all possible update strategies for the KubeBlocks controllers.

+
+ + + + + + + + + + + + +
ValueDescription

"OnDelete"

OnDeleteStrategyType indicates that ordered rolling restarts are disabled. Instances are recreated +when they are manually deleted.

+

"RollingUpdate"

RollingUpdateStrategyType indicates that update will be +applied to all Instances with respect to the InstanceSet +ordering constraints.

+

VarOption (string alias)

@@ -30676,6 +30676,21 @@ This ensures that only one replica is unavailable at a time during the update pr +type
+ + +UpdateStrategyType + + + + +(Optional) +

Indicates the type of the UpdateStrategy. +Default is RollingUpdate.

+ + + + instanceUpdatePolicy
@@ -30711,6 +30726,33 @@ RollingUpdate +

UpdateStrategyType +(string alias)

+

+(Appears on:UpdateStrategy) +

+
+

UpdateStrategyType is a string enumeration type that enumerates +all possible update strategies for the KubeBlocks controllers.

+
+ + + + + + + + + + + + +
ValueDescription

"OnDelete"

OnDeleteStrategyType indicates that ordered rolling restarts are disabled. Instances are recreated +when they are manually deleted.

+

"RollingUpdate"

RollingUpdateStrategyType indicates that update will be +applied to all Instances with respect to the InstanceSet +ordering constraints.

+

workloads.kubeblocks.io/v1alpha1

diff --git a/pkg/controller/component/its_convertor.go b/pkg/controller/component/its_convertor.go index 3b9bf069064..bca212171b2 100644 --- a/pkg/controller/component/its_convertor.go +++ b/pkg/controller/component/its_convertor.go @@ -117,6 +117,7 @@ func (c *itsUpdateStrategyConvertor) convert(args ...any) (any, error) { var updateStrategy *workloads.UpdateStrategy if synthesizedComp.UpdateStrategy != nil { updateStrategy = &workloads.UpdateStrategy{ + Type: workloads.UpdateStrategyType(synthesizedComp.UpdateStrategy.Type), InstanceUpdatePolicy: (*workloads.InstanceUpdatePolicyType)(synthesizedComp.UpdateStrategy.InstanceUpdatePolicy), } if synthesizedComp.UpdateStrategy.RollingUpdate != nil { diff --git a/pkg/controller/component/synthesize_component.go b/pkg/controller/component/synthesize_component.go index 95b562b1e0e..638f3935db7 100644 --- a/pkg/controller/component/synthesize_component.go +++ b/pkg/controller/component/synthesize_component.go @@ -160,6 +160,7 @@ func buildUpdateStrategy(synthesizeComp *SynthesizedComponent, comp *appsv1.Comp var updateStrategy *appsv1.UpdateStrategy if comp.Spec.UpdateStrategy != nil { updateStrategy = &appsv1.UpdateStrategy{ + Type: comp.Spec.UpdateStrategy.Type, InstanceUpdatePolicy: comp.Spec.UpdateStrategy.InstanceUpdatePolicy, } if comp.Spec.UpdateStrategy.RollingUpdate != nil { diff --git a/pkg/controller/instanceset/reconciler_update.go b/pkg/controller/instanceset/reconciler_update.go index a06eaf8b9fc..bee0ca50cbe 100644 --- a/pkg/controller/instanceset/reconciler_update.go +++ b/pkg/controller/instanceset/reconciler_update.go @@ -92,6 +92,11 @@ func (r *updateReconciler) Reconcile(tree *kubebuilderx.ObjectTree) (kubebuilder } // 3. do update + // do nothing if UpdateStrategyType is 'OnDelete' + if its.Spec.UpdateStrategy != nil && its.Spec.UpdateStrategy.Type == workloads.OnDeleteStrategyType { + return kubebuilderx.Continue, nil + } + // handle 'RollingUpdate' partition, maxUnavailable, err := parseReplicasNMaxUnavailable(its.Spec.UpdateStrategy, len(oldPodList)) if err != nil { diff --git a/pkg/controller/instanceset/reconciler_update_test.go b/pkg/controller/instanceset/reconciler_update_test.go index 8b888cc59c5..42d7fff5098 100644 --- a/pkg/controller/instanceset/reconciler_update_test.go +++ b/pkg/controller/instanceset/reconciler_update_test.go @@ -198,6 +198,19 @@ var _ = Describe("update reconciler test", func() { Expect(res).Should(Equal(kubebuilderx.Continue)) expectUpdatedPods(partitionTree, []string{"bar-foo-0"}) + By("reconcile with UpdateStrategy='OnDelete'") + onDeleteTree, err := tree.DeepCopy() + Expect(err).Should(BeNil()) + root, ok = onDeleteTree.GetRoot().(*workloads.InstanceSet) + Expect(ok).Should(BeTrue()) + root.Spec.UpdateStrategy = &workloads.UpdateStrategy{ + Type: workloads.OnDeleteStrategyType, + } + res, err = reconciler.Reconcile(onDeleteTree) + Expect(err).Should(BeNil()) + Expect(res).Should(Equal(kubebuilderx.Continue)) + expectUpdatedPods(onDeleteTree, []string{}) + // order: bar-hello-0, bar-foo-1, bar-foo-0, bar-3, bar-2, bar-1, bar-0 // expected: bar-hello-0 being deleted By("reconcile with PodUpdatePolicy='PreferInPlace'") diff --git a/pkg/testutil/apps/constant.go b/pkg/testutil/apps/constant.go index 2db15cd5093..7d1b5714d08 100644 --- a/pkg/testutil/apps/constant.go +++ b/pkg/testutil/apps/constant.go @@ -206,7 +206,7 @@ var ( }, }, }, - UpdateConcurrency: &[]appsv1.UpdateConcurrency{appsv1.BestEffortParallelConcurrency}[0], + UpdateConcurrency: &[]appsv1.UpdateConcurrency{appsv1.BestEffortParallelConcurrency}[0], Roles: []appsv1.ReplicaRole{ { Name: "leader", diff --git a/pkg/testutil/apps/instance_set_factoy.go b/pkg/testutil/apps/instance_set_factoy.go index 8eecf14c81b..b8fb66baef2 100644 --- a/pkg/testutil/apps/instance_set_factoy.go +++ b/pkg/testutil/apps/instance_set_factoy.go @@ -59,6 +59,9 @@ func NewInstanceSetFactory(namespace, name string, clusterName string, component }, }, }, + UpdateStrategy: &workloads.UpdateStrategy{ + Type: workloads.OnDeleteStrategyType, + }, }, }, f) return f From bcb16358678c0a272f959446dbd7fb3702b35cea Mon Sep 17 00:00:00 2001 From: free6om Date: Mon, 23 Dec 2024 11:20:13 +0800 Subject: [PATCH 11/11] fix broken ut --- pkg/controller/factory/builder_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/controller/factory/builder_test.go b/pkg/controller/factory/builder_test.go index 1d5b21000d9..e122431e82d 100644 --- a/pkg/controller/factory/builder_test.go +++ b/pkg/controller/factory/builder_test.go @@ -125,8 +125,8 @@ var _ = Describe("builder", func() { // test update strategy Expect(its.Spec.UpdateStrategy).ShouldNot(BeNil()) - Expect(its.Spec.UpdateStrategy.InstanceUpdatePolicy).ShouldNot(BeNil()) - Expect(*its.Spec.UpdateStrategy.InstanceUpdatePolicy).Should(BeEquivalentTo(workloads.PreferInPlaceInstanceUpdatePolicyType)) + Expect(its.Spec.UpdateStrategy.Type).Should(BeEmpty()) + Expect(its.Spec.UpdateStrategy.InstanceUpdatePolicy).Should(BeNil()) Expect(its.Spec.UpdateStrategy.RollingUpdate).ShouldNot(BeNil()) Expect(its.Spec.UpdateStrategy.RollingUpdate.UpdateConcurrency).ShouldNot(BeNil()) Expect(*its.Spec.UpdateStrategy.RollingUpdate.UpdateConcurrency).Should(BeEquivalentTo(workloads.BestEffortParallelConcurrency))