diff --git a/apis/apps/v1alpha1/cluster_types.go b/apis/apps/v1alpha1/cluster_types.go index cd2d179d140..3902b6450f1 100644 --- a/apis/apps/v1alpha1/cluster_types.go +++ b/apis/apps/v1alpha1/cluster_types.go @@ -394,6 +394,25 @@ type UserResourceRefs struct { ConfigMapRefs []ConfigMapRef `json:"configMapRefs,omitempty"` } +// InstanceUpdateStrategy indicates the strategy that the InstanceSet +// controller will use to perform updates. +type InstanceUpdateStrategy struct { + // Partition indicates the ordinal at which the InstanceSet should be partitioned + // for updates. During a rolling update, all pods from ordinal Replicas-1 to + // Partition are updated. All pods from ordinal Partition-1 to 0 remain untouched. + // This is helpful in being able to do a canary based deployment. The default value is 0. + // +optional + Partition *int32 `json:"partition,omitempty"` + // The maximum number of pods that can be unavailable during the update. + // Value can be an absolute number (ex: 5) or a percentage of desired pods (ex: 10%). + // Absolute number is calculated from percentage by rounding up. This can not be 0. + // Defaults to 1. The field applies to all pods in the range 0 to Replicas-1. + // That means if there is any unavailable pod in the range 0 to Replicas-1, + // it will be counted towards MaxUnavailable. + // +optional + MaxUnavailable *intstr.IntOrString `json:"maxUnavailable,omitempty"` +} + // InstanceTemplate allows customization of individual replica configurations in a Component. type InstanceTemplate struct { // Name specifies the unique name of the instance Pod created using this InstanceTemplate. @@ -800,6 +819,13 @@ type ClusterComponentSpec struct { // +optional UpdateStrategy *UpdateStrategy `json:"updateStrategy,omitempty"` + // Indicates the InstanceUpdateStrategy that will be + // employed to update Pods in the InstanceSet when a revision is made to + // Template. + // + // +optional + InstanceUpdateStrategy InstanceUpdateStrategy `json:"instanceUpdateStrategy,omitempty"` + // Controls the concurrency of pods during initial scale up, when replacing pods on nodes, // or when scaling down. It only used when `PodManagementPolicy` is set to `Parallel`. // The default Concurrency is 100%. diff --git a/apis/apps/v1alpha1/component_types.go b/apis/apps/v1alpha1/component_types.go index f90aa925e01..b2b0effa8b7 100644 --- a/apis/apps/v1alpha1/component_types.go +++ b/apis/apps/v1alpha1/component_types.go @@ -163,6 +163,13 @@ type ComponentSpec struct { // +optional ServiceAccountName string `json:"serviceAccountName,omitempty"` + // Indicates the InstanceUpdateStrategy that will be + // employed to update Pods in the InstanceSet when a revision is made to + // Template. + // + // +optional + InstanceUpdateStrategy InstanceUpdateStrategy `json:"instanceUpdateStrategy,omitempty"` + // Controls the concurrency of pods during initial scale up, when replacing pods on nodes, // or when scaling down. It only used when `PodManagementPolicy` is set to `Parallel`. // The default Concurrency is 100%. diff --git a/apis/apps/v1alpha1/zz_generated.deepcopy.go b/apis/apps/v1alpha1/zz_generated.deepcopy.go index fb60452a065..fc4605dd217 100644 --- a/apis/apps/v1alpha1/zz_generated.deepcopy.go +++ b/apis/apps/v1alpha1/zz_generated.deepcopy.go @@ -531,6 +531,7 @@ func (in *ClusterComponentSpec) DeepCopyInto(out *ClusterComponentSpec) { *out = new(UpdateStrategy) **out = **in } + in.InstanceUpdateStrategy.DeepCopyInto(&out.InstanceUpdateStrategy) if in.ParallelPodManagementConcurrency != nil { in, out := &in.ParallelPodManagementConcurrency, &out.ParallelPodManagementConcurrency *out = new(intstr.IntOrString) @@ -1583,6 +1584,7 @@ func (in *ComponentSpec) DeepCopyInto(out *ComponentSpec) { *out = make([]string, len(*in)) copy(*out, *in) } + in.InstanceUpdateStrategy.DeepCopyInto(&out.InstanceUpdateStrategy) if in.ParallelPodManagementConcurrency != nil { in, out := &in.ParallelPodManagementConcurrency, &out.ParallelPodManagementConcurrency *out = new(intstr.IntOrString) @@ -2915,6 +2917,31 @@ func (in *InstanceTemplate) DeepCopy() *InstanceTemplate { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *InstanceUpdateStrategy) DeepCopyInto(out *InstanceUpdateStrategy) { + *out = *in + if in.Partition != nil { + in, out := &in.Partition, &out.Partition + *out = new(int32) + **out = **in + } + if in.MaxUnavailable != nil { + in, out := &in.MaxUnavailable, &out.MaxUnavailable + *out = new(intstr.IntOrString) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InstanceUpdateStrategy. +func (in *InstanceUpdateStrategy) DeepCopy() *InstanceUpdateStrategy { + if in == nil { + return nil + } + out := new(InstanceUpdateStrategy) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *InstanceVolumeClaimTemplate) DeepCopyInto(out *InstanceVolumeClaimTemplate) { *out = *in diff --git a/apis/workloads/v1alpha1/instanceset_types.go b/apis/workloads/v1alpha1/instanceset_types.go index 22502344e92..3b1c723f471 100644 --- a/apis/workloads/v1alpha1/instanceset_types.go +++ b/apis/workloads/v1alpha1/instanceset_types.go @@ -75,6 +75,35 @@ type SchedulingPolicy struct { TopologySpreadConstraints []corev1.TopologySpreadConstraint `json:"topologySpreadConstraints,omitempty"` } +// InstanceUpdateStrategy indicates the strategy that the InstanceSet +// controller will use to perform updates. It includes any additional parameters +// necessary to perform the update for the indicated strategy. +type InstanceUpdateStrategy struct { + // Partition indicates the ordinal at which the InstanceSet should be partitioned + // for updates. During a rolling update, all pods from ordinal Replicas-1 to + // Partition are updated. All pods from ordinal Partition-1 to 0 remain untouched. + // This is helpful in being able to do a canary based deployment. The default value is 0. + // +optional + Partition *int32 `json:"partition,omitempty"` + // The maximum number of pods that can be unavailable during the update. + // Value can be an absolute number (ex: 5) or a percentage of desired pods (ex: 10%). + // Absolute number is calculated from percentage by rounding up. This can not be 0. + // Defaults to 1. The field applies to all pods in the range 0 to Replicas-1. + // That means if there is any unavailable pod in the range 0 to Replicas-1, + // it will be counted towards MaxUnavailable. + // +optional + MaxUnavailable *intstr.IntOrString `json:"maxUnavailable,omitempty"` + // 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"` +} + // Range represents a range with a start and an end value. // It is used to define a continuous segment. type Range struct { @@ -326,10 +355,9 @@ type InstanceSetSpec struct { // Indicates the StatefulSetUpdateStrategy that will be // employed to update Pods in the InstanceSet when a revision is made to // Template. - // UpdateStrategy.Type will be set to appsv1.OnDeleteStatefulSetStrategyType if MemberUpdateStrategy is not nil // // Note: This field will be removed in future version. - UpdateStrategy appsv1.StatefulSetUpdateStrategy `json:"updateStrategy,omitempty"` + UpdateStrategy InstanceUpdateStrategy `json:"updateStrategy,omitempty"` // A list of roles defined in the system. // @@ -348,11 +376,13 @@ type InstanceSetSpec struct { // Members(Pods) update strategy. // + // Deprecated since v0.9.0 // - 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} + // +kubebuilder:deprecatedversion:warning="This field has been deprecated since 0.9.0" // +optional MemberUpdateStrategy *MemberUpdateStrategy `json:"memberUpdateStrategy,omitempty"` diff --git a/apis/workloads/v1alpha1/zz_generated.deepcopy.go b/apis/workloads/v1alpha1/zz_generated.deepcopy.go index 4a67647f9c5..574b70e9952 100644 --- a/apis/workloads/v1alpha1/zz_generated.deepcopy.go +++ b/apis/workloads/v1alpha1/zz_generated.deepcopy.go @@ -374,6 +374,36 @@ func (in *InstanceTemplateStatus) DeepCopy() *InstanceTemplateStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *InstanceUpdateStrategy) DeepCopyInto(out *InstanceUpdateStrategy) { + *out = *in + if in.Partition != nil { + in, out := &in.Partition, &out.Partition + *out = new(int32) + **out = **in + } + if in.MaxUnavailable != nil { + in, out := &in.MaxUnavailable, &out.MaxUnavailable + *out = new(intstr.IntOrString) + **out = **in + } + if in.MemberUpdateStrategy != nil { + in, out := &in.MemberUpdateStrategy, &out.MemberUpdateStrategy + *out = new(MemberUpdateStrategy) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InstanceUpdateStrategy. +func (in *InstanceUpdateStrategy) DeepCopy() *InstanceUpdateStrategy { + if in == nil { + return nil + } + out := new(InstanceUpdateStrategy) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *MemberStatus) DeepCopyInto(out *MemberStatus) { *out = *in diff --git a/config/crd/bases/apps.kubeblocks.io_clusters.yaml b/config/crd/bases/apps.kubeblocks.io_clusters.yaml index 50e115068a0..2d4274a6576 100644 --- a/config/crd/bases/apps.kubeblocks.io_clusters.yaml +++ b/config/crd/bases/apps.kubeblocks.io_clusters.yaml @@ -655,6 +655,33 @@ spec: - name type: object type: array + instanceUpdateStrategy: + description: |- + Indicates the InstanceUpdateStrategy that will be + employed to update Pods in the InstanceSet when a revision is made to + Template. + properties: + maxUnavailable: + anyOf: + - type: integer + - type: string + description: |- + The maximum number of pods that can be unavailable during the update. + Value can be an absolute number (ex: 5) or a percentage of desired pods (ex: 10%). + Absolute number is calculated from percentage by rounding up. This can not be 0. + Defaults to 1. The field applies to all pods in the range 0 to Replicas-1. + That means if there is any unavailable pod in the range 0 to Replicas-1, + it will be counted towards MaxUnavailable. + x-kubernetes-int-or-string: true + partition: + description: |- + Partition indicates the ordinal at which the InstanceSet should be partitioned + for updates. During a rolling update, all pods from ordinal Replicas-1 to + Partition are updated. All pods from ordinal Partition-1 to 0 remain untouched. + This is helpful in being able to do a canary based deployment. The default value is 0. + format: int32 + type: integer + type: object instances: description: |- Allows for the customization of configuration values for each instance within a Component. @@ -9772,6 +9799,33 @@ spec: - name type: object type: array + instanceUpdateStrategy: + description: |- + Indicates the InstanceUpdateStrategy that will be + employed to update Pods in the InstanceSet when a revision is made to + Template. + properties: + maxUnavailable: + anyOf: + - type: integer + - type: string + description: |- + The maximum number of pods that can be unavailable during the update. + Value can be an absolute number (ex: 5) or a percentage of desired pods (ex: 10%). + Absolute number is calculated from percentage by rounding up. This can not be 0. + Defaults to 1. The field applies to all pods in the range 0 to Replicas-1. + That means if there is any unavailable pod in the range 0 to Replicas-1, + it will be counted towards MaxUnavailable. + x-kubernetes-int-or-string: true + partition: + description: |- + Partition indicates the ordinal at which the InstanceSet should be partitioned + for updates. During a rolling update, all pods from ordinal Replicas-1 to + Partition are updated. All pods from ordinal Partition-1 to 0 remain untouched. + This is helpful in being able to do a canary based deployment. The default value is 0. + format: int32 + type: integer + type: object instances: description: |- Allows for the customization of configuration values for each instance within a Component. diff --git a/config/crd/bases/apps.kubeblocks.io_components.yaml b/config/crd/bases/apps.kubeblocks.io_components.yaml index fbe8afc4f84..bc07da5c7c4 100644 --- a/config/crd/bases/apps.kubeblocks.io_components.yaml +++ b/config/crd/bases/apps.kubeblocks.io_components.yaml @@ -401,6 +401,33 @@ spec: - name type: object type: array + instanceUpdateStrategy: + description: |- + Indicates the InstanceUpdateStrategy that will be + employed to update Pods in the InstanceSet when a revision is made to + Template. + properties: + maxUnavailable: + anyOf: + - type: integer + - type: string + description: |- + The maximum number of pods that can be unavailable during the update. + Value can be an absolute number (ex: 5) or a percentage of desired pods (ex: 10%). + Absolute number is calculated from percentage by rounding up. This can not be 0. + Defaults to 1. The field applies to all pods in the range 0 to Replicas-1. + That means if there is any unavailable pod in the range 0 to Replicas-1, + it will be counted towards MaxUnavailable. + x-kubernetes-int-or-string: true + partition: + description: |- + Partition indicates the ordinal at which the InstanceSet should be partitioned + for updates. During a rolling update, all pods from ordinal Replicas-1 to + Partition are updated. All pods from ordinal Partition-1 to 0 remain untouched. + This is helpful in being able to do a canary based deployment. The default value is 0. + format: int32 + type: integer + type: object instances: description: |- Allows for the customization of configuration values for each instance within a Component. diff --git a/config/crd/bases/workloads.kubeblocks.io_instancesets.yaml b/config/crd/bases/workloads.kubeblocks.io_instancesets.yaml index 8f81c4fca90..024fe1e0d04 100644 --- a/config/crd/bases/workloads.kubeblocks.io_instancesets.yaml +++ b/config/crd/bases/workloads.kubeblocks.io_instancesets.yaml @@ -3947,6 +3947,7 @@ spec: Members(Pods) update strategy. + Deprecated since v0.9.0 - 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 @@ -12391,42 +12392,43 @@ spec: Indicates the StatefulSetUpdateStrategy that will be employed to update Pods in the InstanceSet when a revision is made to Template. - UpdateStrategy.Type will be set to appsv1.OnDeleteStatefulSetStrategyType if MemberUpdateStrategy is not nil Note: This field will be removed in future version. properties: - rollingUpdate: - description: RollingUpdate is used to communicate parameters when - Type is RollingUpdateStatefulSetStrategyType. - properties: - maxUnavailable: - anyOf: - - type: integer - - type: string - description: |- - The maximum number of pods that can be unavailable during the update. - Value can be an absolute number (ex: 5) or a percentage of desired pods (ex: 10%). - Absolute number is calculated from percentage by rounding up. This can not be 0. - Defaults to 1. 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. - x-kubernetes-int-or-string: true - partition: - 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 - type: object - type: + maxUnavailable: + anyOf: + - type: integer + - type: string + description: |- + The maximum number of pods that can be unavailable during the update. + Value can be an absolute number (ex: 5) or a percentage of desired pods (ex: 10%). + Absolute number is calculated from percentage by rounding up. This can not be 0. + Defaults to 1. The field applies to all pods in the range 0 to Replicas-1. + That means if there is any unavailable pod in the range 0 to Replicas-1, + it will be counted towards MaxUnavailable. + x-kubernetes-int-or-string: true + memberUpdateStrategy: description: |- - Type indicates the type of the StatefulSetUpdateStrategy. - Default is RollingUpdate. + 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 + partition: + description: |- + Partition indicates the ordinal at which the InstanceSet should be partitioned + for updates. During a rolling update, all pods from ordinal Replicas-1 to + Partition are updated. All pods from ordinal Partition-1 to 0 remain untouched. + This is helpful in being able to do a canary based deployment. The default value is 0. + format: int32 + type: integer type: object volumeClaimTemplates: description: |- diff --git a/controllers/apps/configuration/reconfigure_policy.go b/controllers/apps/configuration/reconfigure_policy.go index 849db0c55fc..039da93632b 100644 --- a/controllers/apps/configuration/reconfigure_policy.go +++ b/controllers/apps/configuration/reconfigure_policy.go @@ -161,8 +161,8 @@ func (param *reconfigureParams) maxRollingReplicas() int32 { var maxUnavailable *intstr.IntOrString for _, its := range param.InstanceSetUnits { - if its.Spec.UpdateStrategy.RollingUpdate != nil { - maxUnavailable = its.Spec.UpdateStrategy.RollingUpdate.MaxUnavailable + if its.Spec.UpdateStrategy.MaxUnavailable != nil { + maxUnavailable = its.Spec.UpdateStrategy.MaxUnavailable } if maxUnavailable != nil { break diff --git a/controllers/apps/transformer_cluster_component.go b/controllers/apps/transformer_cluster_component.go index bbdd5998075..a80e1386767 100644 --- a/controllers/apps/transformer_cluster_component.go +++ b/controllers/apps/transformer_cluster_component.go @@ -218,6 +218,7 @@ func copyAndMergeComponent(oldCompObj, newCompObj *appsv1alpha1.Component) *apps // compObjCopy.Spec.Monitor = compProto.Spec.Monitor compObjCopy.Spec.EnabledLogs = compProto.Spec.EnabledLogs compObjCopy.Spec.ServiceAccountName = compProto.Spec.ServiceAccountName + compObjCopy.Spec.InstanceUpdateStrategy = compProto.Spec.InstanceUpdateStrategy compObjCopy.Spec.ParallelPodManagementConcurrency = compProto.Spec.ParallelPodManagementConcurrency compObjCopy.Spec.PodUpdatePolicy = compProto.Spec.PodUpdatePolicy compObjCopy.Spec.Affinity = compProto.Spec.Affinity diff --git a/controllers/apps/transformer_component_workload.go b/controllers/apps/transformer_component_workload.go index a039d4d6228..3d151739643 100644 --- a/controllers/apps/transformer_component_workload.go +++ b/controllers/apps/transformer_component_workload.go @@ -32,7 +32,6 @@ import ( corev1 "k8s.io/api/core/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/types" - "k8s.io/apimachinery/pkg/util/intstr" "k8s.io/apimachinery/pkg/util/sets" "sigs.k8s.io/controller-runtime/pkg/client" @@ -264,22 +263,6 @@ func copyAndMergeITS(oldITS, newITS *workloads.InstanceSet, synthesizeComp *comp } } - updateUpdateStrategy := func(itsObj, itsProto *workloads.InstanceSet) { - var objMaxUnavailable *intstr.IntOrString - if itsObj.Spec.UpdateStrategy.RollingUpdate != nil { - objMaxUnavailable = itsObj.Spec.UpdateStrategy.RollingUpdate.MaxUnavailable - } - itsObj.Spec.UpdateStrategy = itsProto.Spec.UpdateStrategy - if objMaxUnavailable == nil && itsObj.Spec.UpdateStrategy.RollingUpdate != nil { - // HACK: This field is alpha-level (since v1.24) and is only honored by servers that enable the - // MaxUnavailableStatefulSet feature. - // When we get a nil MaxUnavailable from k8s, we consider that the field is not supported by the server, - // and set the MaxUnavailable as nil explicitly to avoid the workload been updated unexpectedly. - // Ref: https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#maximum-unavailable-pods - itsObj.Spec.UpdateStrategy.RollingUpdate.MaxUnavailable = nil - } - } - // be compatible with existed cluster updateService := func(itsObj, itsProto *workloads.InstanceSet) *corev1.Service { if itsProto.Spec.Service != nil { @@ -333,10 +316,7 @@ func copyAndMergeITS(oldITS, newITS *workloads.InstanceSet, synthesizeComp *comp itsObjCopy.Spec.VolumeClaimTemplates = itsProto.Spec.VolumeClaimTemplates itsObjCopy.Spec.ParallelPodManagementConcurrency = itsProto.Spec.ParallelPodManagementConcurrency itsObjCopy.Spec.PodUpdatePolicy = itsProto.Spec.PodUpdatePolicy - - if itsProto.Spec.UpdateStrategy.Type != "" || itsProto.Spec.UpdateStrategy.RollingUpdate != nil { - updateUpdateStrategy(itsObjCopy, itsProto) - } + itsObjCopy.Spec.UpdateStrategy = itsProto.Spec.UpdateStrategy intctrlutil.ResolvePodSpecDefaultFields(oldITS.Spec.Template.Spec, &itsObjCopy.Spec.Template.Spec) delayUpdateInstanceSetSystemFields(oldITS.Spec, &itsObjCopy.Spec) diff --git a/deploy/helm/crds/apps.kubeblocks.io_clusters.yaml b/deploy/helm/crds/apps.kubeblocks.io_clusters.yaml index 50e115068a0..2d4274a6576 100644 --- a/deploy/helm/crds/apps.kubeblocks.io_clusters.yaml +++ b/deploy/helm/crds/apps.kubeblocks.io_clusters.yaml @@ -655,6 +655,33 @@ spec: - name type: object type: array + instanceUpdateStrategy: + description: |- + Indicates the InstanceUpdateStrategy that will be + employed to update Pods in the InstanceSet when a revision is made to + Template. + properties: + maxUnavailable: + anyOf: + - type: integer + - type: string + description: |- + The maximum number of pods that can be unavailable during the update. + Value can be an absolute number (ex: 5) or a percentage of desired pods (ex: 10%). + Absolute number is calculated from percentage by rounding up. This can not be 0. + Defaults to 1. The field applies to all pods in the range 0 to Replicas-1. + That means if there is any unavailable pod in the range 0 to Replicas-1, + it will be counted towards MaxUnavailable. + x-kubernetes-int-or-string: true + partition: + description: |- + Partition indicates the ordinal at which the InstanceSet should be partitioned + for updates. During a rolling update, all pods from ordinal Replicas-1 to + Partition are updated. All pods from ordinal Partition-1 to 0 remain untouched. + This is helpful in being able to do a canary based deployment. The default value is 0. + format: int32 + type: integer + type: object instances: description: |- Allows for the customization of configuration values for each instance within a Component. @@ -9772,6 +9799,33 @@ spec: - name type: object type: array + instanceUpdateStrategy: + description: |- + Indicates the InstanceUpdateStrategy that will be + employed to update Pods in the InstanceSet when a revision is made to + Template. + properties: + maxUnavailable: + anyOf: + - type: integer + - type: string + description: |- + The maximum number of pods that can be unavailable during the update. + Value can be an absolute number (ex: 5) or a percentage of desired pods (ex: 10%). + Absolute number is calculated from percentage by rounding up. This can not be 0. + Defaults to 1. The field applies to all pods in the range 0 to Replicas-1. + That means if there is any unavailable pod in the range 0 to Replicas-1, + it will be counted towards MaxUnavailable. + x-kubernetes-int-or-string: true + partition: + description: |- + Partition indicates the ordinal at which the InstanceSet should be partitioned + for updates. During a rolling update, all pods from ordinal Replicas-1 to + Partition are updated. All pods from ordinal Partition-1 to 0 remain untouched. + This is helpful in being able to do a canary based deployment. The default value is 0. + format: int32 + type: integer + type: object instances: description: |- Allows for the customization of configuration values for each instance within a Component. diff --git a/deploy/helm/crds/apps.kubeblocks.io_components.yaml b/deploy/helm/crds/apps.kubeblocks.io_components.yaml index fbe8afc4f84..bc07da5c7c4 100644 --- a/deploy/helm/crds/apps.kubeblocks.io_components.yaml +++ b/deploy/helm/crds/apps.kubeblocks.io_components.yaml @@ -401,6 +401,33 @@ spec: - name type: object type: array + instanceUpdateStrategy: + description: |- + Indicates the InstanceUpdateStrategy that will be + employed to update Pods in the InstanceSet when a revision is made to + Template. + properties: + maxUnavailable: + anyOf: + - type: integer + - type: string + description: |- + The maximum number of pods that can be unavailable during the update. + Value can be an absolute number (ex: 5) or a percentage of desired pods (ex: 10%). + Absolute number is calculated from percentage by rounding up. This can not be 0. + Defaults to 1. The field applies to all pods in the range 0 to Replicas-1. + That means if there is any unavailable pod in the range 0 to Replicas-1, + it will be counted towards MaxUnavailable. + x-kubernetes-int-or-string: true + partition: + description: |- + Partition indicates the ordinal at which the InstanceSet should be partitioned + for updates. During a rolling update, all pods from ordinal Replicas-1 to + Partition are updated. All pods from ordinal Partition-1 to 0 remain untouched. + This is helpful in being able to do a canary based deployment. The default value is 0. + format: int32 + type: integer + type: object instances: description: |- Allows for the customization of configuration values for each instance within a Component. diff --git a/deploy/helm/crds/workloads.kubeblocks.io_instancesets.yaml b/deploy/helm/crds/workloads.kubeblocks.io_instancesets.yaml index 8f81c4fca90..024fe1e0d04 100644 --- a/deploy/helm/crds/workloads.kubeblocks.io_instancesets.yaml +++ b/deploy/helm/crds/workloads.kubeblocks.io_instancesets.yaml @@ -3947,6 +3947,7 @@ spec: Members(Pods) update strategy. + Deprecated since v0.9.0 - 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 @@ -12391,42 +12392,43 @@ spec: Indicates the StatefulSetUpdateStrategy that will be employed to update Pods in the InstanceSet when a revision is made to Template. - UpdateStrategy.Type will be set to appsv1.OnDeleteStatefulSetStrategyType if MemberUpdateStrategy is not nil Note: This field will be removed in future version. properties: - rollingUpdate: - description: RollingUpdate is used to communicate parameters when - Type is RollingUpdateStatefulSetStrategyType. - properties: - maxUnavailable: - anyOf: - - type: integer - - type: string - description: |- - The maximum number of pods that can be unavailable during the update. - Value can be an absolute number (ex: 5) or a percentage of desired pods (ex: 10%). - Absolute number is calculated from percentage by rounding up. This can not be 0. - Defaults to 1. 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. - x-kubernetes-int-or-string: true - partition: - 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 - type: object - type: + maxUnavailable: + anyOf: + - type: integer + - type: string + description: |- + The maximum number of pods that can be unavailable during the update. + Value can be an absolute number (ex: 5) or a percentage of desired pods (ex: 10%). + Absolute number is calculated from percentage by rounding up. This can not be 0. + Defaults to 1. The field applies to all pods in the range 0 to Replicas-1. + That means if there is any unavailable pod in the range 0 to Replicas-1, + it will be counted towards MaxUnavailable. + x-kubernetes-int-or-string: true + memberUpdateStrategy: description: |- - Type indicates the type of the StatefulSetUpdateStrategy. - Default is RollingUpdate. + 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 + partition: + description: |- + Partition indicates the ordinal at which the InstanceSet should be partitioned + for updates. During a rolling update, all pods from ordinal Replicas-1 to + Partition are updated. All pods from ordinal Partition-1 to 0 remain untouched. + This is helpful in being able to do a canary based deployment. The default value is 0. + format: int32 + type: integer type: object volumeClaimTemplates: description: |- diff --git a/pkg/controller/builder/builder_component.go b/pkg/controller/builder/builder_component.go index a4be30cc111..2151ee7ebaa 100644 --- a/pkg/controller/builder/builder_component.go +++ b/pkg/controller/builder/builder_component.go @@ -82,6 +82,11 @@ func (builder *ComponentBuilder) SetServiceAccountName(serviceAccountName string return builder } +func (builder *ComponentBuilder) SetInstanceUpdateStrategy(instanceUpdateStrategy appsv1alpha1.InstanceUpdateStrategy) *ComponentBuilder { + builder.get().Spec.InstanceUpdateStrategy = instanceUpdateStrategy + return builder +} + func (builder *ComponentBuilder) SetParallelPodManagementConcurrency(parallelPodManagementConcurrency *intstr.IntOrString) *ComponentBuilder { builder.get().Spec.ParallelPodManagementConcurrency = parallelPodManagementConcurrency return builder diff --git a/pkg/controller/builder/builder_instance_set.go b/pkg/controller/builder/builder_instance_set.go index d05696011a8..7c4f797f02e 100644 --- a/pkg/controller/builder/builder_instance_set.go +++ b/pkg/controller/builder/builder_instance_set.go @@ -118,16 +118,11 @@ func (builder *InstanceSetBuilder) SetPodUpdatePolicy(policy workloads.PodUpdate return builder } -func (builder *InstanceSetBuilder) SetUpdateStrategy(strategy apps.StatefulSetUpdateStrategy) *InstanceSetBuilder { +func (builder *InstanceSetBuilder) SetUpdateStrategy(strategy workloads.InstanceUpdateStrategy) *InstanceSetBuilder { builder.get().Spec.UpdateStrategy = strategy return builder } -func (builder *InstanceSetBuilder) SetUpdateStrategyType(strategyType apps.StatefulSetUpdateStrategyType) *InstanceSetBuilder { - builder.get().Spec.UpdateStrategy.Type = strategyType - return builder -} - func (builder *InstanceSetBuilder) SetCustomHandler(handler []workloads.Action) *InstanceSetBuilder { roleProbe := builder.get().Spec.RoleProbe if roleProbe == nil { @@ -167,9 +162,7 @@ func (builder *InstanceSetBuilder) SetMembershipReconfiguration(reconfiguration func (builder *InstanceSetBuilder) SetMemberUpdateStrategy(strategy *workloads.MemberUpdateStrategy) *InstanceSetBuilder { builder.get().Spec.MemberUpdateStrategy = strategy - if strategy != nil { - builder.SetUpdateStrategyType(apps.OnDeleteStatefulSetStrategyType) - } + builder.get().Spec.UpdateStrategy.MemberUpdateStrategy = strategy return builder } diff --git a/pkg/controller/builder/builder_instance_set_test.go b/pkg/controller/builder/builder_instance_set_test.go index 522439d183e..a4bfd29024c 100644 --- a/pkg/controller/builder/builder_instance_set_test.go +++ b/pkg/controller/builder/builder_instance_set_test.go @@ -108,14 +108,10 @@ 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, - }, + strategy := workloads.InstanceUpdateStrategy{ + Partition: &partition, + MaxUnavailable: &maxUnavailable, } - strategyType := apps.OnDeleteStatefulSetStrategyType delay := int32(10) roleProbe := workloads.RoleProbe{InitialDelaySeconds: delay} actions := []workloads.Action{ @@ -170,7 +166,6 @@ var _ = Describe("instance_set builder", func() { SetParallelPodManagementConcurrency(parallelPodManagementConcurrency). SetPodUpdatePolicy(podUpdatePolicy). SetUpdateStrategy(strategy). - SetUpdateStrategyType(strategyType). SetRoleProbe(&roleProbe). SetCustomHandler(actions). AddCustomHandler(action). @@ -202,12 +197,10 @@ var _ = Describe("instance_set builder", func() { 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.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.MaxUnavailable).ShouldNot(BeNil()) - Expect(its.Spec.UpdateStrategy.RollingUpdate.MaxUnavailable).ShouldNot(Equal(maxUnavailable)) + Expect(its.Spec.UpdateStrategy.Partition).ShouldNot(BeNil()) + Expect(*its.Spec.UpdateStrategy.Partition).Should(Equal(partition)) + Expect(its.Spec.UpdateStrategy.MaxUnavailable).ShouldNot(BeNil()) + Expect(its.Spec.UpdateStrategy.MaxUnavailable).ShouldNot(Equal(maxUnavailable)) Expect(its.Spec.RoleProbe).ShouldNot(BeNil()) Expect(its.Spec.RoleProbe.InitialDelaySeconds).Should(Equal(delay)) Expect(its.Spec.RoleProbe.CustomHandler).Should(HaveLen(2)) @@ -215,6 +208,8 @@ var _ = Describe("instance_set builder", func() { Expect(its.Spec.RoleProbe.CustomHandler[1]).Should(Equal(action)) Expect(its.Spec.MemberUpdateStrategy).ShouldNot(BeNil()) Expect(*its.Spec.MemberUpdateStrategy).Should(Equal(memberUpdateStrategy)) + Expect(its.Spec.UpdateStrategy.MemberUpdateStrategy).ShouldNot(BeNil()) + Expect(*its.Spec.UpdateStrategy.MemberUpdateStrategy).Should(Equal(memberUpdateStrategy)) Expect(its.Spec.Service).ShouldNot(BeNil()) Expect(its.Spec.Service).Should(BeEquivalentTo(service)) Expect(its.Spec.Paused).Should(Equal(paused)) diff --git a/pkg/controller/component/component.go b/pkg/controller/component/component.go index 49474c03979..9a11e08ad53 100644 --- a/pkg/controller/component/component.go +++ b/pkg/controller/component/component.go @@ -89,6 +89,7 @@ func BuildComponent(cluster *appsv1alpha1.Cluster, compSpec *appsv1alpha1.Cluste SetReplicas(compSpec.Replicas). SetResources(compSpec.Resources). SetServiceAccountName(compSpec.ServiceAccountName). + SetInstanceUpdateStrategy(compSpec.InstanceUpdateStrategy). SetParallelPodManagementConcurrency(compSpec.ParallelPodManagementConcurrency). SetPodUpdatePolicy(compSpec.PodUpdatePolicy). SetVolumeClaimTemplates(compSpec.VolumeClaimTemplates). diff --git a/pkg/controller/component/its_convertor.go b/pkg/controller/component/its_convertor.go index 2363f902f1f..a82e730c07e 100644 --- a/pkg/controller/component/its_convertor.go +++ b/pkg/controller/component/its_convertor.go @@ -47,7 +47,6 @@ func BuildWorkloadFrom(synthesizeComp *SynthesizedComponent, protoITS *workloads "roleprobe": &itsRoleProbeConvertor{}, "credential": &itsCredentialConvertor{}, "membershipreconfiguration": &itsMembershipReconfigurationConvertor{}, - "memberupdatestrategy": &itsMemberUpdateStrategyConvertor{}, "podmanagementpolicy": &itsPodManagementPolicyConvertor{}, "parallelpodmanagementconcurrency": &itsParallelPodManagementConcurrencyConvertor{}, "podupdatepolicy": &itsPodUpdatePolicyConvertor{}, @@ -79,17 +78,6 @@ type itsCredentialConvertor struct{} // itsMembershipReconfigurationConvertor is an implementation of the convertor interface, used to convert the given object into InstanceSet.Spec.MembershipReconfiguration. type itsMembershipReconfigurationConvertor struct{} -// itsMemberUpdateStrategyConvertor is an implementation of the convertor interface, used to convert the given object into InstanceSet.Spec.MemberUpdateStrategy. -type itsMemberUpdateStrategyConvertor struct{} - -func (c *itsMemberUpdateStrategyConvertor) convert(args ...any) (any, error) { - synthesizeComp, err := parseITSConvertorArgs(args...) - if err != nil { - return nil, err - } - return getMemberUpdateStrategy(synthesizeComp), nil -} - // itsPodManagementPolicyConvertor is an implementation of the convertor interface, used to convert the given object into InstanceSet.Spec.PodManagementPolicy. type itsPodManagementPolicyConvertor struct{} @@ -144,11 +132,15 @@ func (c *itsUpdateStrategyConvertor) convert(args ...any) (any, error) { if err != nil { return nil, err } - if getMemberUpdateStrategy(synthesizedComp) != nil { - // appsv1.OnDeleteStatefulSetStrategyType is the default value if member update strategy is set. - return appsv1.StatefulSetUpdateStrategy{}, nil + if memberUpdateStrategy := getMemberUpdateStrategy(synthesizedComp); memberUpdateStrategy != nil { + return workloads.InstanceUpdateStrategy{ + MemberUpdateStrategy: memberUpdateStrategy, + }, nil } - return nil, nil + return workloads.InstanceUpdateStrategy{ + Partition: synthesizedComp.InstanceUpdateStrategy.Partition, + MaxUnavailable: synthesizedComp.InstanceUpdateStrategy.MaxUnavailable, + }, nil } // itsInstancesConvertor converts component instanceTemplate to ITS instanceTemplate diff --git a/pkg/controller/component/synthesize_component.go b/pkg/controller/component/synthesize_component.go index a067feca4be..9c74668bf88 100644 --- a/pkg/controller/component/synthesize_component.go +++ b/pkg/controller/component/synthesize_component.go @@ -165,6 +165,7 @@ func buildSynthesizedComponent(reqCtx intctrlutil.RequestCtx, OfflineInstances: comp.Spec.OfflineInstances, DisableExporter: comp.Spec.DisableExporter, Stop: comp.Spec.Stop, + InstanceUpdateStrategy: comp.Spec.InstanceUpdateStrategy, PodManagementPolicy: compDef.Spec.PodManagementPolicy, ParallelPodManagementConcurrency: comp.Spec.ParallelPodManagementConcurrency, PodUpdatePolicy: comp.Spec.PodUpdatePolicy, diff --git a/pkg/controller/component/type.go b/pkg/controller/component/type.go index 32e8a957230..d53c002e721 100644 --- a/pkg/controller/component/type.go +++ b/pkg/controller/component/type.go @@ -61,6 +61,7 @@ type SynthesizedComponent struct { Labels map[string]string `json:"labels,omitempty"` Annotations map[string]string `json:"annotations,omitempty"` UpdateStrategy *v1alpha1.UpdateStrategy `json:"updateStrategy,omitempty"` + InstanceUpdateStrategy v1alpha1.InstanceUpdateStrategy `json:"instanceUpdateStrategy,omitempty"` PodManagementPolicy *appsv1.PodManagementPolicyType `json:"podManagementPolicy,omitempty"` ParallelPodManagementConcurrency *intstr.IntOrString `json:"parallelPodManagementConcurrency,omitempty"` PodUpdatePolicy *workloads.PodUpdatePolicyType `json:"podUpdatePolicy,omitempty"` diff --git a/pkg/controller/factory/builder_test.go b/pkg/controller/factory/builder_test.go index 3e4fef47caf..1eb25f8031c 100644 --- a/pkg/controller/factory/builder_test.go +++ b/pkg/controller/factory/builder_test.go @@ -186,6 +186,8 @@ var _ = Describe("builder", func() { // test member update strategy Expect(its.Spec.MemberUpdateStrategy).ShouldNot(BeNil()) Expect(*its.Spec.MemberUpdateStrategy).Should(BeEquivalentTo(workloads.BestEffortParallelUpdateStrategy)) + Expect(its.Spec.UpdateStrategy.MemberUpdateStrategy).ShouldNot(BeNil()) + Expect(*its.Spec.UpdateStrategy.MemberUpdateStrategy).Should(BeEquivalentTo(workloads.BestEffortParallelUpdateStrategy)) }) It("builds BackupJob correctly", func() { diff --git a/pkg/controller/instanceset/reconciler_update.go b/pkg/controller/instanceset/reconciler_update.go index 38d5a38d405..993dfefca63 100644 --- a/pkg/controller/instanceset/reconciler_update.go +++ b/pkg/controller/instanceset/reconciler_update.go @@ -92,13 +92,7 @@ func (r *updateReconciler) Reconcile(tree *kubebuilderx.ObjectTree) (kubebuilder } // 3. do update - // do nothing if UpdateStrategyType is 'OnDelete' - if its.Spec.UpdateStrategy.Type == apps.OnDeleteStatefulSetStrategyType { - return kubebuilderx.Continue, nil - } - - // handle 'RollingUpdate' - partition, maxUnavailable, err := parsePartitionNMaxUnavailable(its.Spec.UpdateStrategy.RollingUpdate, len(oldPodList)) + partition, maxUnavailable, err := parsePartitionNMaxUnavailable(its.Spec.UpdateStrategy, len(oldPodList)) if err != nil { return kubebuilderx.Continue, err } @@ -189,6 +183,9 @@ func buildBlockedCondition(its *workloads.InstanceSet, message string) *metav1.C } func getInstanceSetForUpdatePlan(its *workloads.InstanceSet) *workloads.InstanceSet { + if its.Spec.UpdateStrategy.MemberUpdateStrategy != nil { + its.Spec.MemberUpdateStrategy = its.Spec.UpdateStrategy.MemberUpdateStrategy + } if its.Spec.MemberUpdateStrategy != nil { return its } @@ -201,10 +198,10 @@ func getInstanceSetForUpdatePlan(its *workloads.InstanceSet) *workloads.Instance return itsForPlan } -func parsePartitionNMaxUnavailable(rollingUpdate *apps.RollingUpdateStatefulSetStrategy, replicas int) (int, int, error) { +func parsePartitionNMaxUnavailable(rollingUpdate workloads.InstanceUpdateStrategy, replicas int) (int, int, error) { partition := replicas maxUnavailable := 1 - if rollingUpdate == nil { + if rollingUpdate.Partition == nil && rollingUpdate.MaxUnavailable == nil { return partition, maxUnavailable, nil } if rollingUpdate.Partition != nil { diff --git a/pkg/controller/instanceset/reconciler_update_test.go b/pkg/controller/instanceset/reconciler_update_test.go index 8ed437941b1..2cae033e194 100644 --- a/pkg/controller/instanceset/reconciler_update_test.go +++ b/pkg/controller/instanceset/reconciler_update_test.go @@ -157,11 +157,9 @@ var _ = Describe("update reconciler test", func() { Expect(ok).Should(BeTrue()) partition := int32(3) maxUnavailable := intstr.FromInt32(2) - root.Spec.UpdateStrategy = appsv1.StatefulSetUpdateStrategy{ - RollingUpdate: &appsv1.RollingUpdateStatefulSetStrategy{ - Partition: &partition, - MaxUnavailable: &maxUnavailable, - }, + root.Spec.UpdateStrategy = workloads.InstanceUpdateStrategy{ + Partition: &partition, + MaxUnavailable: &maxUnavailable, } // order: bar-hello-0, bar-foo-1, bar-foo-0, bar-3, bar-2, bar-1, bar-0 // expected: bar-hello-0, bar-foo-1 being deleted @@ -175,11 +173,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, - MaxUnavailable: &maxUnavailable, - }, + root.Spec.UpdateStrategy = workloads.InstanceUpdateStrategy{ + Partition: &partition, + MaxUnavailable: &maxUnavailable, } for _, name := range []string{"bar-hello-0", "bar-foo-1"} { pod := builder.NewPodBuilder(namespace, name).GetObject() @@ -194,17 +190,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'") diff --git a/pkg/controller/instanceset/revision_util_test.go b/pkg/controller/instanceset/revision_util_test.go index 93906594371..c48543d1beb 100644 --- a/pkg/controller/instanceset/revision_util_test.go +++ b/pkg/controller/instanceset/revision_util_test.go @@ -88,7 +88,6 @@ var _ = Describe("revision util test", func() { } } }, - "memberUpdateStrategy": "Serial", "podManagementPolicy": "Parallel", "replicas": 1, "roleProbe": { @@ -903,7 +902,7 @@ var _ = Describe("revision util test", func() { } }, "updateStrategy": { - "type": "OnDelete" + "memberUpdateStrategy": "Serial" }, "volumeClaimTemplates": [ { diff --git a/pkg/controller/instanceset/update_plan.go b/pkg/controller/instanceset/update_plan.go index 2d7c0ede9c9..f2410635bec 100644 --- a/pkg/controller/instanceset/update_plan.go +++ b/pkg/controller/instanceset/update_plan.go @@ -97,6 +97,9 @@ 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. + if p.its.Spec.UpdateStrategy.MemberUpdateStrategy != nil { + p.its.Spec.MemberUpdateStrategy = p.its.Spec.UpdateStrategy.MemberUpdateStrategy + } serialUpdate := p.its.Spec.MemberUpdateStrategy != nil && *p.its.Spec.MemberUpdateStrategy == workloads.SerialUpdateStrategy hasRoleProbed := len(p.its.Status.MembersStatus) > 0 if !serialUpdate || hasRoleProbed { @@ -121,6 +124,10 @@ func (p *realUpdatePlan) build() { root := &model.ObjectVertex{} p.dag.AddVertex(root) + if p.its.Spec.UpdateStrategy.MemberUpdateStrategy != nil { + p.its.Spec.MemberUpdateStrategy = p.its.Spec.UpdateStrategy.MemberUpdateStrategy + } + if p.its.Spec.MemberUpdateStrategy == nil { return } diff --git a/pkg/controller/instanceset/update_plan_test.go b/pkg/controller/instanceset/update_plan_test.go index ad1032f5438..905ed188801 100644 --- a/pkg/controller/instanceset/update_plan_test.go +++ b/pkg/controller/instanceset/update_plan_test.go @@ -123,6 +123,7 @@ var _ = Describe("update plan test.", func() { By("build a serial plan") strategy := workloads.SerialUpdateStrategy its.Spec.MemberUpdateStrategy = &strategy + its.Spec.UpdateStrategy.MemberUpdateStrategy = &strategy expectedPlan := [][]*corev1.Pod{ {pod4}, {pod2}, @@ -139,6 +140,7 @@ var _ = Describe("update plan test.", func() { By("build a serial plan") strategy := workloads.SerialUpdateStrategy its.Spec.MemberUpdateStrategy = &strategy + its.Spec.UpdateStrategy.MemberUpdateStrategy = &strategy expectedPlan := [][]*corev1.Pod{ {pod4}, {pod2}, @@ -155,6 +157,7 @@ var _ = Describe("update plan test.", func() { By("build a parallel plan") strategy := workloads.ParallelUpdateStrategy its.Spec.MemberUpdateStrategy = &strategy + its.Spec.UpdateStrategy.MemberUpdateStrategy = &strategy expectedPlan := [][]*corev1.Pod{ {pod0, pod1, pod2, pod3, pod4, pod5, pod6}, } @@ -165,6 +168,7 @@ var _ = Describe("update plan test.", func() { By("build a best effort parallel plan") strategy := workloads.BestEffortParallelUpdateStrategy its.Spec.MemberUpdateStrategy = &strategy + its.Spec.UpdateStrategy.MemberUpdateStrategy = &strategy expectedPlan := [][]*corev1.Pod{ {pod2, pod3, pod4, pod6}, {pod1}, @@ -178,6 +182,7 @@ var _ = Describe("update plan test.", func() { By("build a serial plan with role-less and heterogeneous pods") strategy := workloads.SerialUpdateStrategy its.Spec.MemberUpdateStrategy = &strategy + its.Spec.UpdateStrategy.MemberUpdateStrategy = &strategy its.Spec.Roles = nil for _, pod := range []*corev1.Pod{pod0, pod1, pod2, pod3, pod4, pod5, pod6} { labels := pod.Labels diff --git a/pkg/testutil/apps/instance_set_factoy.go b/pkg/testutil/apps/instance_set_factoy.go index 62ddd938d94..e22097002b6 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