Repository: apecloud/kubeblocks

Issue:
[Improvement] Redundant Annotation Setup During Cluster Restoration
### Context
When restoring a cluster from a backup, users are required to manually specify the `kubeblocks.io/restore-from-backup` annotation in the cluster CR. 
However, certain fields within this annotation (e.g., EncryptedSystemAccount) appear to duplicate information already stored in the backup object's `kubeblocks.io/encrypted-system-accounts` annotation.

### Problem
The current implementation forces users to:

- Manually copy system account details from the backup's annotation

- Maintain consistency between two separate annotations

But this creates unnecessary complexity and is error-prone.

### Proposed Improvement
Automatically inherit EncryptedSystemAccount, if not specified,  from the backup's `kubeblocks.io/encrypted-system-accounts` annotation during restoration, eliminating manual setup of redundant fields.

Hints from issue comments:
@github-actions: This issue has been marked as stale because it has been open for 30 days with no activity

Solution patch:
diff --git a/apis/apps/v1/componentdefinition_types.go b/apis/apps/v1/componentdefinition_types.go
index 9cd0a19af..d564098da 100644
--- a/apis/apps/v1/componentdefinition_types.go
+++ b/apis/apps/v1/componentdefinition_types.go
@@ -311,14 +311,6 @@ type ComponentDefinitionSpec struct {
 	// +optional
 	Configs2 []ComponentFileTemplate `json:"configs2,omitempty"`
 
-	// Specifies groups of scripts, each provided via a ConfigMap, to be mounted as volumes in the container.
-	// These scripts can be executed during container startup or via specific actions.
-	//
-	// This field is immutable.
-	//
-	// +optional
-	Scripts2 []ComponentFileTemplate `json:"scripts2,omitempty"`
-
 	// Specifies the configuration file templates and volume mount parameters used by the Component.
 	// It also includes descriptions of the parameters in the ConfigMaps, such as value range limitations.
 	//
@@ -369,19 +361,10 @@ type ComponentDefinitionSpec struct {
 	// Specifies groups of scripts, each provided via a ConfigMap, to be mounted as volumes in the container.
 	// These scripts can be executed during container startup or via specific actions.
 	//
-	// Each script group is encapsulated in a ComponentTemplateSpec that includes:
-	//
-	// - The ConfigMap containing the scripts.
-	// - The mount point where the scripts will be mounted inside the container.
-	//
 	// This field is immutable.
 	//
-	// +patchMergeKey=name
-	// +patchStrategy=merge,retainKeys
-	// +listType=map
-	// +listMapKey=name
 	// +optional
-	Scripts []ComponentTemplateSpec `json:"scripts,omitempty"`
+	Scripts []ComponentFileTemplate `json:"scripts,omitempty"`
 
 	// An array of `SystemAccount` objects that define the system accounts needed
 	// for the management operations of the Component.
diff --git a/apis/apps/v1/sidecardefinition_types.go b/apis/apps/v1/sidecardefinition_types.go
index 4c9bdbd02..8bdd74c5a 100644
--- a/apis/apps/v1/sidecardefinition_types.go
+++ b/apis/apps/v1/sidecardefinition_types.go
@@ -108,23 +108,15 @@ type SidecarDefinitionSpec struct {
 	//
 	// This field is immutable.
 	//
-	// +patchMergeKey=name
-	// +patchStrategy=merge,retainKeys
-	// +listType=map
-	// +listMapKey=name
 	// +optional
-	Configs []ComponentTemplateSpec `json:"configs,omitempty"`
+	Configs []ComponentFileTemplate `json:"configs,omitempty"`
 
 	// Specifies the scripts used by the Sidecar.
 	//
 	// This field is immutable.
 	//
-	// +patchMergeKey=name
-	// +patchStrategy=merge,retainKeys
-	// +listType=map
-	// +listMapKey=name
 	// +optional
-	Scripts []ComponentTemplateSpec `json:"scripts,omitempty"`
+	Scripts []ComponentFileTemplate `json:"scripts,omitempty"`
 
 	// TODO:
 	//   1. services, volumes, service-refs, etc.
diff --git a/apis/apps/v1/zz_generated.deepcopy.go b/apis/apps/v1/zz_generated.deepcopy.go
index cc2e8ef0d..8c4eb5c47 100644
--- a/apis/apps/v1/zz_generated.deepcopy.go
+++ b/apis/apps/v1/zz_generated.deepcopy.go
@@ -1157,13 +1157,6 @@ func (in *ComponentDefinitionSpec) DeepCopyInto(out *ComponentDefinitionSpec) {
 			(*in)[i].DeepCopyInto(&(*out)[i])
 		}
 	}
-	if in.Scripts2 != nil {
-		in, out := &in.Scripts2, &out.Scripts2
-		*out = make([]ComponentFileTemplate, len(*in))
-		for i := range *in {
-			(*in)[i].DeepCopyInto(&(*out)[i])
-		}
-	}
 	if in.Configs != nil {
 		in, out := &in.Configs, &out.Configs
 		*out = make([]ComponentTemplateSpec, len(*in))
@@ -1178,7 +1171,7 @@ func (in *ComponentDefinitionSpec) DeepCopyInto(out *ComponentDefinitionSpec) {
 	}
 	if in.Scripts != nil {
 		in, out := &in.Scripts, &out.Scripts
-		*out = make([]ComponentTemplateSpec, len(*in))
+		*out = make([]ComponentFileTemplate, len(*in))
 		for i := range *in {
 			(*in)[i].DeepCopyInto(&(*out)[i])
 		}
@@ -3306,14 +3299,14 @@ func (in *SidecarDefinitionSpec) DeepCopyInto(out *SidecarDefinitionSpec) {
 	}
 	if in.Configs != nil {
 		in, out := &in.Configs, &out.Configs
-		*out = make([]ComponentTemplateSpec, len(*in))
+		*out = make([]ComponentFileTemplate, len(*in))
 		for i := range *in {
 			(*in)[i].DeepCopyInto(&(*out)[i])
 		}
 	}
 	if in.Scripts != nil {
 		in, out := &in.Scripts, &out.Scripts
-		*out = make([]ComponentTemplateSpec, len(*in))
+		*out = make([]ComponentFileTemplate, len(*in))
 		for i := range *in {
 			(*in)[i].DeepCopyInto(&(*out)[i])
 		}
diff --git a/apis/workloads/v1/instanceset_types.go b/apis/workloads/v1/instanceset_types.go
index bb5e6f6fc..fcea1e5fc 100644
--- a/apis/workloads/v1/instanceset_types.go
+++ b/apis/workloads/v1/instanceset_types.go
@@ -226,8 +226,14 @@ type InstanceSetSpec struct {
 	TemplateVars map[string]string `json:"templateVars,omitempty"`
 
 	// Indicates that the InstanceSet is paused, meaning the reconciliation of this InstanceSet object will be paused.
+	//
 	// +optional
 	Paused bool `json:"paused,omitempty"`
+
+	// Describe the configs to be reconfigured.
+	//
+	// +optional
+	Configs []ConfigTemplate `json:"configs,omitempty"`
 }
 
 // InstanceSetStatus defines the observed state of InstanceSet
@@ -294,6 +300,11 @@ type InstanceSetStatus struct {
 	// +optional
 	MembersStatus []MemberStatus `json:"membersStatus,omitempty"`
 
+	// Provides the status of each instance in the ITS.
+	//
+	// +optional
+	InstanceStatus []InstanceStatus `json:"instanceStatus,omitempty"`
+
 	// currentRevisions, if not empty, indicates the old version of the InstanceSet used to generate the underlying workload.
 	// key is the pod name, value is the revision.
 	//
@@ -359,6 +370,31 @@ type MembershipReconfiguration struct {
 	// TODO: member join/leave
 }
 
+type ConfigTemplate struct {
+	// The name of the config.
+	Name string `json:"name"`
+
+	// The generation of the config.
+	Generation int64 `json:"generation"`
+
+	// The custom reconfigure action.
+	//
+	// +optional
+	Reconfigure *kbappsv1.Action `json:"reconfigure,omitempty"`
+
+	// The name of the custom reconfigure action.
+	//
+	// An empty name indicates that the reconfigure action is the default one defined by lifecycle actions.
+	//
+	// +optional
+	ReconfigureActionName string `json:"reconfigureActionName,omitempty"`
+
+	// The parameters to call the reconfigure action.
+	//
+	// +optional
+	Parameters map[string]string `json:"parameters,omitempty"`
+}
+
 type MemberStatus struct {
 	// Represents the name of the pod.
 	//
@@ -372,6 +408,31 @@ type MemberStatus struct {
 	ReplicaRole *ReplicaRole `json:"role,omitempty"`
 }
 
+type InstanceStatus struct {
+	// Represents the name of the pod.
+	//
+	// +kubebuilder:validation:Required
+	// +kubebuilder:default=Unknown
+	PodName string `json:"podName"`
+
+	// The status of configs.
+	//
+	// +optional
+	Configs []InstanceConfigStatus `json:"configs,omitempty"`
+}
+
+type InstanceConfigStatus struct {
+	// The name of the config.
+	//
+	// +kubebuilder:validation:Required
+	Name string `json:"name"`
+
+	// The generation of the config.
+	//
+	// +kubebuilder:validation:Required
+	Generation int64 `json:"generation"`
+}
+
 // InstanceTemplateStatus aggregates the status of replicas for each InstanceTemplate
 type InstanceTemplateStatus struct {
 	// Name, the name of the InstanceTemplate.
diff --git a/apis/workloads/v1/zz_generated.deepcopy.go b/apis/workloads/v1/zz_generated.deepcopy.go
index 130e31bb4..2589097ff 100644
--- a/apis/workloads/v1/zz_generated.deepcopy.go
+++ b/apis/workloads/v1/zz_generated.deepcopy.go
@@ -31,6 +31,48 @@ import (
 	"k8s.io/apimachinery/pkg/util/intstr"
 )
 
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *ConfigTemplate) DeepCopyInto(out *ConfigTemplate) {
+	*out = *in
+	if in.Reconfigure != nil {
+		in, out := &in.Reconfigure, &out.Reconfigure
+		*out = new(appsv1.Action)
+		(*in).DeepCopyInto(*out)
+	}
+	if in.Parameters != nil {
+		in, out := &in.Parameters, &out.Parameters
+		*out = make(map[string]string, len(*in))
+		for key, val := range *in {
+			(*out)[key] = val
+		}
+	}
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConfigTemplate.
+func (in *ConfigTemplate) DeepCopy() *ConfigTemplate {
+	if in == nil {
+		return nil
+	}
+	out := new(ConfigTemplate)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *InstanceConfigStatus) DeepCopyInto(out *InstanceConfigStatus) {
+	*out = *in
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InstanceConfigStatus.
+func (in *InstanceConfigStatus) DeepCopy() *InstanceConfigStatus {
+	if in == nil {
+		return nil
+	}
+	out := new(InstanceConfigStatus)
+	in.DeepCopyInto(out)
+	return out
+}
+
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *InstanceSet) DeepCopyInto(out *InstanceSet) {
 	*out = *in
@@ -161,6 +203,13 @@ func (in *InstanceSetSpec) DeepCopyInto(out *InstanceSetSpec) {
 			(*out)[key] = val
 		}
 	}
+	if in.Configs != nil {
+		in, out := &in.Configs, &out.Configs
+		*out = make([]ConfigTemplate, len(*in))
+		for i := range *in {
+			(*in)[i].DeepCopyInto(&(*out)[i])
+		}
+	}
 }
 
 // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InstanceSetSpec.
@@ -190,6 +239,13 @@ func (in *InstanceSetStatus) DeepCopyInto(out *InstanceSetStatus) {
 			(*in)[i].DeepCopyInto(&(*out)[i])
 		}
 	}
+	if in.InstanceStatus != nil {
+		in, out := &in.InstanceStatus, &out.InstanceStatus
+		*out = make([]InstanceStatus, len(*in))
+		for i := range *in {
+			(*in)[i].DeepCopyInto(&(*out)[i])
+		}
+	}
 	if in.CurrentRevisions != nil {
 		in, out := &in.CurrentRevisions, &out.CurrentRevisions
 		*out = make(map[string]string, len(*in))
@@ -221,6 +277,26 @@ func (in *InstanceSetStatus) DeepCopy() *InstanceSetStatus {
 	return out
 }
 
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *InstanceStatus) DeepCopyInto(out *InstanceStatus) {
+	*out = *in
+	if in.Configs != nil {
+		in, out := &in.Configs, &out.Configs
+		*out = make([]InstanceConfigStatus, len(*in))
+		copy(*out, *in)
+	}
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InstanceStatus.
+func (in *InstanceStatus) DeepCopy() *InstanceStatus {
+	if in == nil {
+		return nil
+	}
+	out := new(InstanceStatus)
+	in.DeepCopyInto(out)
+	return out
+}
+
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *InstanceTemplateStatus) DeepCopyInto(out *InstanceTemplateStatus) {
 	*out = *in
diff --git a/config/crd/bases/apps.kubeblocks.io_componentdefinitions.yaml b/config/crd/bases/apps.kubeblocks.io_componentdefinitions.yaml
index ef1cbc513..ff2b97d2b 100644
--- a/config/crd/bases/apps.kubeblocks.io_componentdefinitions.yaml
+++ b/config/crd/bases/apps.kubeblocks.io_componentdefinitions.yaml
@@ -15954,76 +15954,6 @@ spec:
                   These scripts can be executed during container startup or via specific actions.
 
 
-                  Each script group is encapsulated in a ComponentTemplateSpec that includes:
-
-
-                  - The ConfigMap containing the scripts.
-                  - The mount point where the scripts will be mounted inside the container.
-
-
-                  This field is immutable.
-                items:
-                  properties:
-                    defaultMode:
-                      description: |-
-                        The operator attempts to set default file permissions for scripts (0555) and configurations (0444).
-                        However, certain database engines may require different file permissions.
-                        You can specify the desired file permissions here.
-
-
-                        Must be specified as an octal value between 0000 and 0777 (inclusive),
-                        or as a decimal value between 0 and 511 (inclusive).
-                        YAML supports both octal and decimal values for file permissions.
-
-
-                        Please note that this setting only affects the permissions of the files themselves.
-                        Directories within the specified path are not impacted by this setting.
-                        It's important to be aware that this setting might conflict with other options
-                        that influence the file mode, such as fsGroup.
-                        In such cases, the resulting file mode may have additional bits set.
-                        Refers to documents of k8s.ConfigMapVolumeSource.defaultMode for more information.
-                      format: int32
-                      type: integer
-                    name:
-                      description: Specifies the name of the configuration template.
-                      maxLength: 63
-                      pattern: ^[a-z0-9]([a-z0-9\.\-]*[a-z0-9])?$
-                      type: string
-                    namespace:
-                      default: default
-                      description: |-
-                        Specifies the namespace of the referenced configuration template ConfigMap object.
-                        An empty namespace is equivalent to the "default" namespace.
-                      maxLength: 63
-                      pattern: ^[a-z0-9]([a-z0-9\-]*[a-z0-9])?$
-                      type: string
-                    templateRef:
-                      description: Specifies the name of the referenced configuration
-                        template ConfigMap object.
-                      maxLength: 63
-                      pattern: ^[a-z0-9]([a-z0-9\.\-]*[a-z0-9])?$
-                      type: string
-                    volumeName:
-                      description: |-
-                        Refers to the volume name of PodTemplate. The configuration file produced through the configuration
-                        template will be mounted to the corresponding volume. Must be a DNS_LABEL name.
-                        The volume name must be defined in podSpec.containers[*].volumeMounts.
-                      maxLength: 63
-                      pattern: ^[a-z]([a-z0-9\-]*[a-z0-9])?$
-                      type: string
-                  required:
-                  - name
-                  type: object
-                type: array
-                x-kubernetes-list-map-keys:
-                - name
-                x-kubernetes-list-type: map
-              scripts2:
-                description: |-
-                  Specifies groups of scripts, each provided via a ConfigMap, to be mounted as volumes in the container.
-                  These scripts can be executed during container startup or via specific actions.
-
-
                   This field is immutable.
                 items:
                   properties:
diff --git a/config/crd/bases/apps.kubeblocks.io_sidecardefinitions.yaml b/config/crd/bases/apps.kubeblocks.io_sidecardefinitions.yaml
index 0fd9c030c..b91eac451 100644
--- a/config/crd/bases/apps.kubeblocks.io_sidecardefinitions.yaml
+++ b/config/crd/bases/apps.kubeblocks.io_sidecardefinitions.yaml
@@ -70,9 +70,7 @@ spec:
                   properties:
                     defaultMode:
                       description: |-
-                        The operator attempts to set default file permissions for scripts (0555) and configurations (0444).
-                        However, certain database engines may require different file permissions.
-                        You can specify the desired file permissions here.
+                        The operator attempts to set default file permissions (0444).
 
 
                         Must be specified as an octal value between 0000 and 0777 (inclusive),
@@ -89,28 +87,27 @@ spec:
                       format: int32
                       type: integer
                     name:
-                      description: Specifies the name of the configuration template.
+                      description: Specifies the name of the template.
                       maxLength: 63
                       pattern: ^[a-z0-9]([a-z0-9\.\-]*[a-z0-9])?$
                       type: string
                     namespace:
                       default: default
-                      description: |-
-                        Specifies the namespace of the referenced configuration template ConfigMap object.
-                        An empty namespace is equivalent to the "default" namespace.
+                      description: Specifies the namespace of the referenced template
+                        ConfigMap object.
                       maxLength: 63
                       pattern: ^[a-z0-9]([a-z0-9\-]*[a-z0-9])?$
                       type: string
-                    templateRef:
-                      description: Specifies the name of the referenced configuration
-                        template ConfigMap object.
+                    template:
+                      description: Specifies the name of the referenced template ConfigMap
+                        object.
                       maxLength: 63
                       pattern: ^[a-z0-9]([a-z0-9\.\-]*[a-z0-9])?$
                       type: string
                     volumeName:
                       description: |-
-                        Refers to the volume name of PodTemplate. The configuration file produced through the configuration
-                        template will be mounted to the corresponding volume. Must be a DNS_LABEL name.
+                        Refers to the volume name of PodTemplate. The file produced through the template will be mounted to
+                        the corresponding volume. Must be a DNS_LABEL name.
                         The volume name must be defined in podSpec.containers[*].volumeMounts.
                       maxLength: 63
                       pattern: ^[a-z]([a-z0-9\-]*[a-z0-9])?$
@@ -119,9 +116,6 @@ spec:
                   - name
                   type: object
                 type: array
-                x-kubernetes-list-map-keys:
-                - name
-                x-kubernetes-list-type: map
               containers:
                 description: |-
                   List of containers for the sidecar.
@@ -1487,9 +1481,7 @@ spec:
                   properties:
                     defaultMode:
                       description: |-
-                        The operator attempts to set default file permissions for scripts (0555) and configurations (0444).
-                        However, certain database engines may require different file permissions.
-                        You can specify the desired file permissions here.
+                        The operator attempts to set default file permissions (0444).
 
 
                         Must be specified as an octal value between 0000 and 0777 (inclusive),
@@ -1506,28 +1498,27 @@ spec:
                       format: int32
                       type: integer
                     name:
-                      description: Specifies the name of the configuration template.
+                      description: Specifies the name of the template.
                       maxLength: 63
                       pattern: ^[a-z0-9]([a-z0-9\.\-]*[a-z0-9])?$
                       type: string
                     namespace:
                       default: default
-                      description: |-
-                        Specifies the namespace of the referenced configuration template ConfigMap object.
-                        An empty namespace is equivalent to the "default" namespace.
+                      description: Specifies the namespace of the referenced template
+                        ConfigMap object.
                       maxLength: 63
                       pattern: ^[a-z0-9]([a-z0-9\-]*[a-z0-9])?$
                       type: string
-                    templateRef:
-                      description: Specifies the name of the referenced configuration
-                        template ConfigMap object.
+                    template:
+                      description: Specifies the name of the referenced template ConfigMap
+                        object.
                       maxLength: 63
                       pattern: ^[a-z0-9]([a-z0-9\.\-]*[a-z0-9])?$
                       type: string
                     volumeName:
                       description: |-
-                        Refers to the volume name of PodTemplate. The configuration file produced through the configuration
-                        template will be mounted to the corresponding volume. Must be a DNS_LABEL name.
+                        Refers to the volume name of PodTemplate. The file produced through the template will be mounted to
+                        the corresponding volume. Must be a DNS_LABEL name.
                         The volume name must be defined in podSpec.containers[*].volumeMounts.
                       maxLength: 63
                       pattern: ^[a-z]([a-z0-9\-]*[a-z0-9])?$
@@ -1536,9 +1527,6 @@ spec:
                   - name
                   type: object
                 type: array
-                x-kubernetes-list-map-keys:
-                - name
-                x-kubernetes-list-type: map
               selectors:
                 description: |-
                   Specifies the component definition of components that the sidecar along with.
diff --git a/config/crd/bases/workloads.kubeblocks.io_instancesets.yaml b/config/crd/bases/workloads.kubeblocks.io_instancesets.yaml
index 89335739a..76a0984e0 100644
--- a/config/crd/bases/workloads.kubeblocks.io_instancesets.yaml
+++ b/config/crd/bases/workloads.kubeblocks.io_instancesets.yaml
@@ -65,6 +65,304 @@ spec:
             description: Defines the desired state of the state machine. It includes
               the configuration details for the state machine.
             properties:
+              configs:
+                description: Describe the configs to be reconfigured.
+                items:
+                  properties:
+                    generation:
+                      description: The generation of the config.
+                      format: int64
+                      type: integer
+                    name:
+                      description: The name of the config.
+                      type: string
+                    parameters:
+                      additionalProperties:
+                        type: string
+                      description: The parameters to call the reconfigure action.
+                      type: object
+                    reconfigure:
+                      description: The custom reconfigure action.
+                      properties:
+                        exec:
+                          description: |-
+                            Defines the command to run.
+
+
+                            This field cannot be updated.
+                          properties:
+                            args:
+                              description: Args represents the arguments that are
+                                passed to the `command` for execution.
+                              items:
+                                type: string
+                              type: array
+                            command:
+                              description: |-
+                                Specifies the command to be executed inside the container.
+                                The working directory for this command is the container's root directory('/').
+                                Commands are executed directly without a shell environment, meaning shell-specific syntax ('|', etc.) is not supported.
+                                If the shell is required, it must be explicitly invoked in the command.
+
+
+                                A successful execution is indicated by an exit status of 0; any non-zero status signifies a failure.
+                              items:
+                                type: string
+                              type: array
+                            container:
+                              description: |-
+                                Specifies the name of the container within the same pod whose resources will be shared with the action.
+                                This allows the action to utilize the specified container's resources without executing within it.
+
+
+                                The name must match one of the containers defined in `componentDefinition.spec.runtime`.
+
+
+                                The resources that can be shared are included:
+
+
+                                - volume mounts
+
+
+                                This field cannot be updated.
+                              type: string
+                            env:
+                              description: |-
+                                Represents a list of environment variables that will be injected into the container.
+                                These variables enable the container to adapt its behavior based on the environment it's running in.
+
+
+                                This field cannot be updated.
+                              items:
+                                description: EnvVar represents an environment variable
+                                  present in a Container.
+                                properties:
+                                  name:
+                                    description: Name of the environment variable.
+                                      Must be a C_IDENTIFIER.
+                                    type: string
+                                  value:
+                                    description: |-
+                                      Variable references $(VAR_NAME) are expanded
+                                      using the previously defined environment variables in the container and
+                                      any service environment variables. If a variable cannot be resolved,
+                                      the reference in the input string will be unchanged. Double $$ are reduced
+                                      to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e.
+                                      "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)".
+                                      Escaped references will never be expanded, regardless of whether the variable
+                                      exists or not.
+                                      Defaults to "".
+                                    type: string
+                                  valueFrom:
+                                    description: Source for the environment variable's
+                                      value. Cannot be used if value is not empty.
+                                    properties:
+                                      configMapKeyRef:
+                                        description: Selects a key of a ConfigMap.
+                                        properties:
+                                          key:
+                                            description: The key to select.
+                                            type: string
+                                          name:
+                                            description: |-
+                                              Name of the referent.
+                                              More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
+                                              TODO: Add other useful fields. apiVersion, kind, uid?
+                                            type: string
+                                          optional:
+                                            description: Specify whether the ConfigMap
+                                              or its key must be defined
+                                            type: boolean
+                                        required:
+                                        - key
+                                        type: object
+                                        x-kubernetes-map-type: atomic
+                                      fieldRef:
+                                        description: |-
+                                          Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['<KEY>']`, `metadata.annotations['<KEY>']`,
+                                          spec.nodeName, spec.serviceAccountName, status.hostIP, status.podIP, status.podIPs.
+                                        properties:
+                                          apiVersion:
+                                            description: Version of the schema the
+                                              FieldPath is written in terms of, defaults
+                                              to "v1".
+                                            type: string
+                                          fieldPath:
+                                            description: Path of the field to select
+                                              in the specified API version.
+                                            type: string
+                                        required:
+                                        - fieldPath
+                                        type: object
+                                        x-kubernetes-map-type: atomic
+                                      resourceFieldRef:
+                                        description: |-
+                                          Selects a resource of the container: only resources limits and requests
+                                          (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests.ephemeral-storage) are currently supported.
+                                        properties:
+                                          containerName:
+                                            description: 'Container name: required
+                                              for volumes, optional for env vars'
+                                            type: string
+                                          divisor:
+                                            anyOf:
+                                            - type: integer
+                                            - type: string
+                                            description: Specifies the output format
+                                              of the exposed resources, defaults to
+                                              "1"
+                                            pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+                                            x-kubernetes-int-or-string: true
+                                          resource:
+                                            description: 'Required: resource to select'
+                                            type: string
+                                        required:
+                                        - resource
+                                        type: object
+                                        x-kubernetes-map-type: atomic
+                                      secretKeyRef:
+                                        description: Selects a key of a secret in
+                                          the pod's namespace
+                                        properties:
+                                          key:
+                                            description: The key of the secret to
+                                              select from.  Must be a valid secret
+                                              key.
+                                            type: string
+                                          name:
+                                            description: |-
+                                              Name of the referent.
+                                              More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
+                                              TODO: Add other useful fields. apiVersion, kind, uid?
+                                            type: string
+                                          optional:
+                                            description: Specify whether the Secret
+                                              or its key must be defined
+                                            type: boolean
+                                        required:
+                                        - key
+                                        type: object
+                                        x-kubernetes-map-type: atomic
+                                    type: object
+                                required:
+                                - name
+                                type: object
+                              type: array
+                            image:
+                              description: |-
+                                Specifies the container image to be used for running the Action.
+
+
+                                When specified, a dedicated container will be created using this image to execute the Action.
+                                All actions with same image will share the same container.
+
+
+                                This field cannot be updated.
+                              type: string
+                            matchingKey:
+                              description: |-
+                                Used in conjunction with the `targetPodSelector` field to refine the selection of target pod(s) for Action execution.
+                                The impact of this field depends on the `targetPodSelector` value:
+
+
+                                - When `targetPodSelector` is set to `Any` or `All`, this field will be ignored.
+                                - When `targetPodSelector` is set to `Role`, only those replicas whose role matches the `matchingKey`
+                                  will be selected for the Action.
+
+
+                                This field cannot be updated.
+                              type: string
+                            targetPodSelector:
+                              description: |-
+                                Defines the criteria used to select the target Pod(s) for executing the Action.
+                                This is useful when there is no default target replica identified.
+                                It allows for precise control over which Pod(s) the Action should run in.
+
+
+                                If not specified, the Action will be executed in the pod where the Action is triggered, such as the pod
+                                to be removed or added; or a random pod if the Action is triggered at the component level, such as
+                                post-provision or pre-terminate of the component.
+
+
+                                This field cannot be updated.
+                              enum:
+                              - Any
+                              - All
+                              - Role
+                              - Ordinal
+                              type: string
+                          type: object
+                        preCondition:
+                          description: |-
+                            Specifies the state that the cluster must reach before the Action is executed.
+                            Currently, this is only applicable to the `postProvision` action.
+
+
+                            The conditions are as follows:
+
+
+                            - `Immediately`: Executed right after the Component object is created.
+                              The readiness of the Component and its resources is not guaranteed at this stage.
+                            - `RuntimeReady`: The Action is triggered after the Component object has been created and all associated
+                              runtime resources (e.g. Pods) are in a ready state.
+                            - `ComponentReady`: The Action is triggered after the Component itself is in a ready state.
+                              This process does not affect the readiness state of the Component or the Cluster.
+                            - `ClusterReady`: The Action is executed after the Cluster is in a ready state.
+                              This execution does not alter the Component or the Cluster's state of readiness.
+
+
+                            This field cannot be updated.
+                          type: string
+                        retryPolicy:
+                          description: |-
+                            Defines the strategy to be taken when retrying the Action after a failure.
+
+
+                            It specifies the conditions under which the Action should be retried and the limits to apply,
+                            such as the maximum number of retries and backoff strategy.
+
+
+                            This field cannot be updated.
+                          properties:
+                            maxRetries:
+                              default: 0
+                              description: |-
+                                Defines the maximum number of retry attempts that should be made for a given Action.
+                                This value is set to 0 by default, indicating that no retries will be made.
+                              type: integer
+                            retryInterval:
+                              default: 0
+                              description: |-
+                                Indicates the duration of time to wait between each retry attempt.
+                                This value is set to 0 by default, indicating that there will be no delay between retry attempts.
+                              format: int64
+                              type: integer
+                          type: object
+                        timeoutSeconds:
+                          default: 0
+                          description: |-
+                            Specifies the maximum duration in seconds that the Action is allowed to run.
+
+
+                            If the Action does not complete within this time frame, it will be terminated.
+
+
+                            This field cannot be updated.
+                          format: int32
+                          type: integer
+                      type: object
+                    reconfigureActionName:
+                      description: |-
+                        The name of the custom reconfigure action.
+
+
+                        An empty name indicates that the reconfigure action is the default one defined by lifecycle actions.
+                      type: string
+                  required:
+                  - generation
+                  - name
+                  type: object
+                type: array
               defaultTemplateOrdinals:
                 description: |-
                   Specifies the desired Ordinals of the default template.
@@ -10123,6 +10421,34 @@ spec:
                   Used only when spec.roles set.
                 format: int32
                 type: integer
+              instanceStatus:
+                description: Provides the status of each instance in the ITS.
+                items:
+                  properties:
+                    configs:
+                      description: The status of configs.
+                      items:
+                        properties:
+                          generation:
+                            description: The generation of the config.
+                            format: int64
+                            type: integer
+                          name:
+                            description: The name of the config.
+                            type: string
+                        required:
+                        - generation
+                        - name
+                        type: object
+                      type: array
+                    podName:
+                      default: Unknown
+                      description: Represents the name of the pod.
+                      type: string
+                  required:
+                  - podName
+                  type: object
+                type: array
               membersStatus:
                 description: Provides the status of each member in the cluster.
                 items:
diff --git a/controllers/apps/cluster/transformer_cluster_sharding_account.go b/controllers/apps/cluster/transformer_cluster_sharding_account.go
index 07b9571e5..5b7b3002d 100644
--- a/controllers/apps/cluster/transformer_cluster_sharding_account.go
+++ b/controllers/apps/cluster/transformer_cluster_sharding_account.go
@@ -29,10 +29,10 @@ import (
 	"sigs.k8s.io/controller-runtime/pkg/client"
 
 	appsv1 "github.com/apecloud/kubeblocks/apis/apps/v1"
+	appsutil "github.com/apecloud/kubeblocks/controllers/apps/util"
 	"github.com/apecloud/kubeblocks/pkg/common"
 	"github.com/apecloud/kubeblocks/pkg/constant"
 	"github.com/apecloud/kubeblocks/pkg/controller/builder"
-	"github.com/apecloud/kubeblocks/pkg/controller/factory"
 	"github.com/apecloud/kubeblocks/pkg/controller/graph"
 	"github.com/apecloud/kubeblocks/pkg/controller/model"
 )
@@ -118,7 +118,10 @@ func (t *clusterShardingAccountTransformer) newSystemAccountSecret(transCtx *clu
 	if err != nil {
 		return nil, err
 	}
-	password := t.buildPassword(transCtx, account, sharding.Name)
+	password, err := t.buildPassword(transCtx, account, sharding.Name)
+	if err != nil {
+		return nil, err
+	}
 	return t.newAccountSecretWithPassword(transCtx, sharding, accountName, password)
 }
 
@@ -154,12 +157,15 @@ func (t *clusterShardingAccountTransformer) definedSystemAccount(transCtx *clust
 	return appsv1.SystemAccount{}, fmt.Errorf("system account %s not found in component definition %s", accountName, compDef.Name)
 }
 
-func (t *clusterShardingAccountTransformer) buildPassword(transCtx *clusterTransformContext, account appsv1.SystemAccount, shardingName string) []byte {
-	password := []byte(factory.GetRestoreSystemAccountPassword(transCtx.Cluster.Annotations, shardingName, account.Name))
+func (t *clusterShardingAccountTransformer) buildPassword(transCtx *clusterTransformContext, account appsv1.SystemAccount, shardingName string) ([]byte, error) {
+	password, err := appsutil.GetRestoreSystemAccountPassword(transCtx.Context, transCtx.Client, transCtx.Cluster.Annotations, shardingName, account.Name)
+	if err != nil {
+		return nil, fmt.Errorf("failed to restore password for system account %s of shard %s from annotation", account.Name, shardingName)
+	}
 	if len(password) == 0 {
 		password = t.generatePassword(account)
 	}
-	return password
+	return password, nil
 }
 
 func (t *clusterShardingAccountTransformer) generatePassword(account appsv1.SystemAccount) []byte {
diff --git a/controllers/apps/component/component_controller.go b/controllers/apps/component/component_controller.go
index 0dbe65303..b680c5886 100644
--- a/controllers/apps/component/component_controller.go
+++ b/controllers/apps/component/component_controller.go
@@ -169,8 +169,6 @@ func (r *ComponentReconciler) Reconcile(ctx context.Context, req ctrl.Request) (
 			&componentWorkloadTransformer{Client: r.Client},
 			// handle RBAC for component workloads
 			&componentRBACTransformer{},
-			// reconfigure config/script templates
-			&componentReconfigureTransformer{},
 			// handle component postProvision lifecycle action
 			&componentPostProvisionTransformer{},
 			// update component status
diff --git a/controllers/apps/component/transformer_component_account.go b/controllers/apps/component/transformer_component_account.go
index 3e72ee762..3445db8f5 100644
--- a/controllers/apps/component/transformer_component_account.go
+++ b/controllers/apps/component/transformer_component_account.go
@@ -168,14 +168,17 @@ func (t *componentAccountTransformer) buildAccountHash(account synthesizedSystem
 func (t *componentAccountTransformer) buildAccountSecret(ctx *componentTransformContext,
 	synthesizeComp *component.SynthesizedComponent, account synthesizedSystemAccount) (*corev1.Secret, error) {
 	var password []byte
+	var err error
 	switch {
 	case account.SecretRef != nil:
-		var err error
 		if password, err = t.getPasswordFromSecret(ctx, account); err != nil {
 			return nil, err
 		}
 	default:
-		password = t.buildPassword(ctx, account)
+		password, err = t.buildPassword(ctx, account)
+		if err != nil {
+			return nil, err
+		}
 	}
 	if len(password) > maximumPasswordLength {
 		return nil, errPasswordTooLong
@@ -203,18 +206,22 @@ func (t *componentAccountTransformer) getPasswordFromSecret(ctx graph.TransformC
 	return secret.Data[passwordKey], nil
 }
 
-func (t *componentAccountTransformer) buildPassword(ctx *componentTransformContext, account synthesizedSystemAccount) []byte {
+func (t *componentAccountTransformer) buildPassword(ctx *componentTransformContext, account synthesizedSystemAccount) ([]byte, error) {
 	// get restore password if exists during recovery.
-	password := factory.GetRestoreSystemAccountPassword(ctx.SynthesizeComponent.Annotations, ctx.SynthesizeComponent.Name, account.Name)
-	if account.InitAccount && password == "" {
+	password, err := appsutil.GetRestoreSystemAccountPassword(ctx.Context, ctx.Client,
+		ctx.SynthesizeComponent.Annotations, ctx.SynthesizeComponent.Name, account.Name)
+	if err != nil {
+		return nil, fmt.Errorf("failed to restore password for system account %s of component %s from annotation", account.Name, ctx.SynthesizeComponent.Name)
+	}
+	if account.InitAccount && len(password) == 0 {
 		// initAccount can also restore from factory.GetRestoreSystemAccountPassword(ctx.SynthesizeComponent, account).
 		// This is compatibility processing.
-		password = factory.GetRestorePassword(ctx.SynthesizeComponent)
+		password = []byte(factory.GetRestorePassword(ctx.SynthesizeComponent))
 	}
-	if password == "" {
-		return t.generatePassword(account)
+	if len(password) == 0 {
+		return t.generatePassword(account), nil
 	}
-	return []byte(password)
+	return password, nil
 }
 
 func (t *componentAccountTransformer) generatePassword(account synthesizedSystemAccount) []byte {
diff --git a/controllers/apps/component/transformer_component_reconfigure.go b/controllers/apps/component/transformer_component_reconfigure.go
deleted file mode 100644
index dfb80b75f..000000000
--- a/controllers/apps/component/transformer_component_reconfigure.go
+++ /dev/null
@@ -1,341 +0,0 @@
-/*
-Copyright (C) 2022-2025 ApeCloud Co., Ltd
-
-This file is part of KubeBlocks project
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU Affero General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU Affero General Public License for more details.
-
-You should have received a copy of the GNU Affero General Public License
-along with this program.  If not, see <http://www.gnu.org/licenses/>.
-*/
-
-package component
-
-import (
-	"crypto/sha256"
-	"encoding/json"
-	"errors"
-	"fmt"
-	"path/filepath"
-	"reflect"
-	"slices"
-	"strings"
-	"time"
-
-	corev1 "k8s.io/api/core/v1"
-	"k8s.io/apimachinery/pkg/types"
-	"k8s.io/apimachinery/pkg/util/sets"
-	"k8s.io/utils/ptr"
-	"sigs.k8s.io/controller-runtime/pkg/client"
-
-	workloads "github.com/apecloud/kubeblocks/apis/workloads/v1"
-	"github.com/apecloud/kubeblocks/pkg/controller/component"
-	"github.com/apecloud/kubeblocks/pkg/controller/graph"
-	"github.com/apecloud/kubeblocks/pkg/controller/lifecycle"
-	"github.com/apecloud/kubeblocks/pkg/controller/model"
-	intctrlutil "github.com/apecloud/kubeblocks/pkg/controllerutil"
-)
-
-type componentReconfigureTransformer struct{}
-
-var _ graph.Transformer = &componentReconfigureTransformer{}
-
-func (t *componentReconfigureTransformer) Transform(ctx graph.TransformContext, dag *graph.DAG) error {
-	transCtx, _ := ctx.(*componentTransformContext)
-	if model.IsObjectDeleting(transCtx.ComponentOrig) {
-		return nil
-	}
-
-	runningObjs, err := getFileTemplateObjects(transCtx)
-	if err != nil {
-		return err
-	}
-
-	protoObjs, err := buildFileTemplateObjects(transCtx)
-	if err != nil {
-		return err
-	}
-
-	toCreate, toDelete, toUpdate := mapDiff(runningObjs, protoObjs)
-
-	return t.handleReconfigure(transCtx, dag, runningObjs, protoObjs, toCreate, toDelete, toUpdate)
-}
-
-func (t *componentReconfigureTransformer) handleReconfigure(transCtx *componentTransformContext, dag *graph.DAG,
-	runningObjs, protoObjs map[string]*corev1.ConfigMap, toCreate, toDelete, toUpdate sets.Set[string]) error {
-	if len(toCreate) > 0 || len(toDelete) > 0 {
-		// since pod volumes changed, the workload will be restarted, cancel the queued reconfigure.
-		return t.cancelQueuedReconfigure(transCtx, dag)
-	}
-
-	changes := t.templateFileChanges(transCtx, runningObjs, protoObjs, toUpdate)
-	if len(changes) > 0 {
-		msg, err := json.Marshal(changes)
-		if err != nil {
-			return err
-		}
-		if err := t.queueReconfigure(transCtx, dag, string(msg)); err != nil {
-			return err
-		}
-		return intctrlutil.NewDelayedRequeueError(time.Second, fmt.Sprintf("pending reconfigure task: %s", msg))
-	}
-
-	return t.reconfigure(transCtx, dag)
-}
-
-func (t *componentReconfigureTransformer) templateFileChanges(transCtx *componentTransformContext,
-	runningObjs, protoObjs map[string]*corev1.ConfigMap, update sets.Set[string]) map[string]fileTemplateChanges {
-	diff := func(obj *corev1.ConfigMap, rData, pData map[string]string) fileTemplateChanges {
-		var (
-			tplName = fileTemplateNameFromObject(transCtx.SynthesizeComponent, obj)
-			items   = make([][]string, 3)
-		)
-
-		toAdd, toDelete, toUpdate := mapDiff(rData, pData)
-
-		items[0], items[1] = sets.List(toAdd), sets.List(toDelete)
-		for item := range toUpdate {
-			if !reflect.DeepEqual(rData[item], pData[item]) {
-				absPath := t.absoluteFilePath(transCtx, tplName, item)
-				if len(absPath) > 0 {
-					checksum := sha256.Sum256([]byte(pData[item]))
-					items[2] = append(items[2], fmt.Sprintf("%s:%x", absPath, checksum))
-				}
-			}
-		}
-
-		for i := range items {
-			slices.Sort(items[i])
-		}
-
-		return fileTemplateChanges{
-			Created: strings.Join(items[0], ","),
-			Removed: strings.Join(items[1], ","),
-			Updated: strings.Join(items[2], ","),
-		}
-	}
-
-	result := make(map[string]fileTemplateChanges)
-	for name := range update {
-		rData, pData := runningObjs[name].Data, protoObjs[name].Data
-		if !reflect.DeepEqual(rData, pData) {
-			tplName := fileTemplateNameFromObject(transCtx.SynthesizeComponent, runningObjs[name])
-			result[tplName] = diff(runningObjs[name], rData, pData)
-		}
-	}
-	return result
-}
-
-func (t *componentReconfigureTransformer) absoluteFilePath(transCtx *componentTransformContext, tpl, file string) string {
-	var (
-		synthesizedComp = transCtx.SynthesizeComponent
-	)
-
-	var volName, mountPath string
-	for _, fileTpl := range synthesizedComp.FileTemplates {
-		if fileTpl.Name == tpl {
-			volName = fileTpl.VolumeName
-			break
-		}
-	}
-	if volName == "" {
-		return "" // has no volumes specified
-	}
-
-	for _, container := range synthesizedComp.PodSpec.Containers {
-		for _, mount := range container.VolumeMounts {
-			if mount.Name == volName {
-				mountPath = mount.MountPath
-				break
-			}
-		}
-		if mountPath != "" {
-			break
-		}
-	}
-	if mountPath == "" {
-		return "" // the template is not mounted, ignore it
-	}
-
-	return filepath.Join(mountPath, file)
-}
-
-type fileTemplateChanges struct {
-	Created string `json:"created,omitempty"`
-	Removed string `json:"removed,omitempty"`
-	Updated string `json:"updated,omitempty"`
-}
-
-func (t *componentReconfigureTransformer) reconfigure(transCtx *componentTransformContext, dag *graph.DAG) error {
-	replicas, changes, err := t.reconfigureStatus(transCtx, dag)
-	if err != nil {
-		return err
-	}
-	for _, replica := range replicas {
-		if err := t.reconfigureReplica(transCtx, changes, replica); err != nil {
-			return err
-		}
-		if err := t.reconfigured(transCtx, dag, []string{replica}); err != nil {
-			return err
-		}
-	}
-	return nil
-}
-
-func (t *componentReconfigureTransformer) reconfigureReplica(transCtx *componentTransformContext,
-	changes map[string]fileTemplateChanges, replica string) error {
-	var (
-		synthesizedComp = transCtx.SynthesizeComponent
-	)
-	pod := &corev1.Pod{}
-	podKey := types.NamespacedName{
-		Namespace: synthesizedComp.Namespace,
-		Name:      replica,
-	}
-	if err := transCtx.Client.Get(transCtx.Context, podKey, pod); err != nil {
-		return client.IgnoreNotFound(err)
-	}
-	for _, tpl := range synthesizedComp.FileTemplates {
-		if change, ok := changes[tpl.Name]; ok {
-			if err := t.reconfigureReplicaTemplate(transCtx, tpl, change, pod); err != nil {
-				return err
-			}
-		}
-	}
-	return nil
-}
-
-func (t *componentReconfigureTransformer) reconfigureReplicaTemplate(transCtx *componentTransformContext,
-	tpl component.SynthesizedFileTemplate, changes fileTemplateChanges, pod *corev1.Pod) error {
-	var (
-		synthesizedComp  = transCtx.SynthesizeComponent
-		lifecycleActions = synthesizedComp.LifecycleActions
-	)
-	if (lifecycleActions == nil || lifecycleActions.Reconfigure == nil) && tpl.Reconfigure == nil {
-		return nil // has no reconfigure action defined
-	}
-
-	reconfigure := func(lfa lifecycle.Lifecycle) error {
-		if tpl.ExternalManaged != nil && *tpl.ExternalManaged {
-			if tpl.Reconfigure == nil {
-				return nil // disabled by the external
-			}
-		}
-		if tpl.Reconfigure != nil {
-			actionName := component.UDFReconfigureActionName(tpl)
-			args := lifecycle.FileTemplateChanges(changes.Created, changes.Removed, changes.Updated)
-			return lfa.UserDefined(transCtx.Context, transCtx.Client, nil, actionName, tpl.Reconfigure, args)
-		}
-		return lfa.Reconfigure(transCtx.Context, transCtx.Client, nil, changes.Created, changes.Removed, changes.Updated)
-	}
-
-	lfa, err := lifecycle.New(synthesizedComp.Namespace, synthesizedComp.ClusterName, synthesizedComp.Name,
-		lifecycleActions, synthesizedComp.TemplateVars, pod)
-	if err != nil {
-		return err
-	}
-
-	if err := reconfigure(lfa); err != nil {
-		if errors.Is(err, lifecycle.ErrPreconditionFailed) {
-			return intctrlutil.NewDelayedRequeueError(time.Second,
-				fmt.Sprintf("replicas not up-to-date when reconfiguring: %s", err.Error()))
-		}
-		return err
-	}
-	return nil
-}
-
-func (t *componentReconfigureTransformer) queueReconfigure(transCtx *componentTransformContext, dag *graph.DAG, changes string) error {
-	return t.updateReconfigureStatus(transCtx, dag, func(s *component.ReplicaStatus) {
-		s.Reconfigured = ptr.To(changes)
-	})
-}
-
-func (t *componentReconfigureTransformer) cancelQueuedReconfigure(transCtx *componentTransformContext, dag *graph.DAG) error {
-	return t.updateReconfigureStatus(transCtx, dag, func(s *component.ReplicaStatus) {
-		s.Reconfigured = nil
-	})
-}
-
-func (t *componentReconfigureTransformer) reconfigured(transCtx *componentTransformContext, dag *graph.DAG, replicas []string) error {
-	replicasSet := sets.New(replicas...)
-	return t.updateReconfigureStatus(transCtx, dag, func(s *component.ReplicaStatus) {
-		if replicasSet.Has(s.Name) {
-			s.Reconfigured = ptr.To("")
-		}
-	})
-}
-
-func (t *componentReconfigureTransformer) updateReconfigureStatus(
-	transCtx *componentTransformContext, dag *graph.DAG, f func(*component.ReplicaStatus)) error {
-	its, inDag := t.itsObject(transCtx, dag)
-	if its == nil {
-		return nil
-	}
-	err := component.UpdateReplicasStatusFunc(its, func(r *component.ReplicasStatus) error {
-		for i := range r.Status {
-			f(&r.Status[i])
-		}
-		return nil
-	})
-	if err != nil {
-		return err
-	}
-
-	if !inDag {
-		runningIts := transCtx.RunningWorkload.(*workloads.InstanceSet)
-		if !reflect.DeepEqual(runningIts.Annotations, its.Annotations) {
-			graphCli, _ := transCtx.Client.(model.GraphClient)
-			// its is copied from running its, and only the annotation is modified.
-			graphCli.Update(dag, nil, its)
-		}
-	}
-	return nil
-}
-
-func (t *componentReconfigureTransformer) reconfigureStatus(
-	transCtx *componentTransformContext, dag *graph.DAG) ([]string, map[string]fileTemplateChanges, error) {
-	its, _ := t.itsObject(transCtx, dag)
-	if its == nil {
-		return nil, nil, nil
-	}
-
-	var err1 error
-	changes := map[string]fileTemplateChanges{}
-	replicas, err2 := component.GetReplicasStatusFunc(its, func(r component.ReplicaStatus) bool {
-		if r.Reconfigured == nil || len(*r.Reconfigured) == 0 {
-			return false
-		}
-		if len(changes) == 0 {
-			err1 = json.Unmarshal([]byte(*r.Reconfigured), &changes)
-		}
-		return true
-	})
-	if err2 != nil {
-		return nil, nil, err2
-	}
-	if err1 != nil {
-		return nil, nil, err1
-	}
-	return replicas, changes, nil
-}
-
-func (t *componentReconfigureTransformer) itsObject(transCtx *componentTransformContext, dag *graph.DAG) (*workloads.InstanceSet, bool) {
-	graphCli, _ := transCtx.Client.(model.GraphClient)
-	objs := graphCli.FindAll(dag, &workloads.InstanceSet{})
-	if len(objs) > 0 {
-		return objs[0].(*workloads.InstanceSet), true // reuse it
-	}
-	if transCtx.RunningWorkload != nil {
-		return transCtx.RunningWorkload.(*workloads.InstanceSet).DeepCopy(), false
-	}
-	return nil, false
-}
diff --git a/controllers/apps/component/transformer_component_reload_sidecar.go b/controllers/apps/component/transformer_component_reload_sidecar.go
index ba76c7d42..67030aa1d 100644
--- a/controllers/apps/component/transformer_component_reload_sidecar.go
+++ b/controllers/apps/component/transformer_component_reload_sidecar.go
@@ -85,7 +85,7 @@ func (t *componentReloadActionSidecarTransformer) Transform(ctx graph.TransformC
 
 	var configmaps []*corev1.ConfigMap
 	cachedObjs := resolveRerenderDependOnObjects(dag)
-	for _, tpls := range [][]appsv1.ComponentTemplateSpec{builtinComp.ScriptTemplates, builtinComp.ConfigTemplates} {
+	for _, tpls := range [][]appsv1.ComponentTemplateSpec{builtinComp.ConfigTemplates} {
 		objects, err := render.RenderTemplate(reconcileCtx, cluster, builtinComp, comp, cachedObjs, tpls)
 		if err != nil {
 			return err
@@ -138,7 +138,7 @@ func resolveRerenderDependOnObjects(dag *graph.DAG) []client.Object {
 
 func updatePodVolumes(podSpec *corev1.PodSpec, component *component.SynthesizedComponent) error {
 	volumes := make(map[string]appsv1.ComponentTemplateSpec, len(component.ConfigTemplates))
-	for _, tpls := range [][]appsv1.ComponentTemplateSpec{component.ConfigTemplates, component.ScriptTemplates} {
+	for _, tpls := range [][]appsv1.ComponentTemplateSpec{component.ConfigTemplates} {
 		for _, tpl := range tpls {
 			cmName := core.GetComponentCfgName(component.ClusterName, component.Name, tpl.Name)
 			volumes[cmName] = tpl
diff --git a/controllers/apps/component/transformer_component_status.go b/controllers/apps/component/transformer_component_status.go
index ab2e65a29..c20eda066 100644
--- a/controllers/apps/component/transformer_component_status.go
+++ b/controllers/apps/component/transformer_component_status.go
@@ -20,6 +20,7 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 package component
 
 import (
+	"context"
 	"fmt"
 	"slices"
 	"strconv"
@@ -27,6 +28,7 @@ import (
 	"time"
 
 	corev1 "k8s.io/api/core/v1"
+	apierrors "k8s.io/apimachinery/pkg/api/errors"
 	"k8s.io/apimachinery/pkg/api/meta"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/util/sets"
@@ -374,3 +376,22 @@ func (t *componentStatusTransformer) reconcileAvailableCondition(transCtx *compo
 
 	return nil
 }
+
+func getRunningVolumes(ctx context.Context, cli client.Client, synthesizedComp *component.SynthesizedComponent,
+	itsObj *workloads.InstanceSet, vctName string) ([]*corev1.PersistentVolumeClaim, error) {
+	pvcs, err := component.ListOwnedPVCs(ctx, cli, synthesizedComp.Namespace, synthesizedComp.ClusterName, synthesizedComp.Name)
+	if err != nil {
+		if apierrors.IsNotFound(err) {
+			return nil, nil
+		}
+		return nil, err
+	}
+	matchedPVCs := make([]*corev1.PersistentVolumeClaim, 0)
+	prefix := fmt.Sprintf("%s-%s", vctName, itsObj.Name)
+	for _, pvc := range pvcs {
+		if strings.HasPrefix(pvc.Name, prefix) {
+			matchedPVCs = append(matchedPVCs, pvc)
+		}
+	}
+	return matchedPVCs, nil
+}
diff --git a/controllers/apps/component/transformer_component_workload.go b/controllers/apps/component/transformer_component_workload.go
index 24d777ec7..596c98638 100644
--- a/controllers/apps/component/transformer_component_workload.go
+++ b/controllers/apps/component/transformer_component_workload.go
@@ -22,20 +22,13 @@ package component
 import (
 	"context"
 	"encoding/json"
-	"errors"
-	"fmt"
 	"reflect"
 	"slices"
 	"strings"
-	"time"
 
-	"github.com/spf13/viper"
 	"golang.org/x/exp/maps"
 	corev1 "k8s.io/api/core/v1"
-	"k8s.io/apimachinery/pkg/api/equality"
-	apierrors "k8s.io/apimachinery/pkg/api/errors"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	"k8s.io/apimachinery/pkg/types"
 	"k8s.io/apimachinery/pkg/util/sets"
 	"k8s.io/utils/ptr"
 	"sigs.k8s.io/controller-runtime/pkg/client"
@@ -48,7 +41,6 @@ import (
 	"github.com/apecloud/kubeblocks/pkg/controller/configuration"
 	"github.com/apecloud/kubeblocks/pkg/controller/factory"
 	"github.com/apecloud/kubeblocks/pkg/controller/graph"
-	"github.com/apecloud/kubeblocks/pkg/controller/lifecycle"
 	"github.com/apecloud/kubeblocks/pkg/controller/model"
 	intctrlutil "github.com/apecloud/kubeblocks/pkg/controllerutil"
 )
@@ -63,24 +55,6 @@ type componentWorkloadTransformer struct {
 	client.Client
 }
 
-// componentWorkloadOps handles component workload ops
-type componentWorkloadOps struct {
-	cli            client.Client
-	reqCtx         intctrlutil.RequestCtx
-	component      *appsv1.Component
-	synthesizeComp *component.SynthesizedComponent
-	dag            *graph.DAG
-
-	// runningITS is a snapshot of the InstanceSet that is already running
-	runningITS *workloads.InstanceSet
-	// protoITS is the InstanceSet object that is rebuilt from scratch during each reconcile process
-	protoITS              *workloads.InstanceSet
-	desiredCompPodNames   []string
-	runningItsPodNames    []string
-	desiredCompPodNameSet sets.Set[string]
-	runningItsPodNameSet  sets.Set[string]
-}
-
 var _ graph.Transformer = &componentWorkloadTransformer{}
 
 func (t *componentWorkloadTransformer) Transform(ctx graph.TransformContext, dag *graph.DAG) error {
@@ -92,11 +66,6 @@ func (t *componentWorkloadTransformer) Transform(ctx graph.TransformContext, dag
 	compDef := transCtx.CompDef
 	comp := transCtx.Component
 	synthesizeComp := transCtx.SynthesizeComponent
-	reqCtx := intctrlutil.RequestCtx{
-		Ctx:      transCtx.Context,
-		Log:      transCtx.Logger,
-		Recorder: transCtx.EventRecorder,
-	}
 
 	runningITS, err := t.runningInstanceSetObject(ctx, synthesizeComp)
 	if err != nil {
@@ -129,7 +98,7 @@ func (t *componentWorkloadTransformer) Transform(ctx graph.TransformContext, dag
 		if protoITS == nil {
 			graphCli.Delete(dag, runningITS)
 		} else {
-			err = t.handleUpdate(reqCtx, graphCli, dag, synthesizeComp, comp, runningITS, protoITS)
+			err = t.handleUpdate(transCtx, graphCli, dag, synthesizeComp, comp, runningITS, protoITS)
 		}
 	}
 	return err
@@ -160,7 +129,7 @@ func (t *componentWorkloadTransformer) reconcileWorkload(ctx context.Context, cl
 		protoITS.Spec.Template.Spec.InitContainers[i].Image = intctrlutil.ReplaceImageRegistry(container.Image)
 	}
 
-	buildInstanceSetPlacementAnnotation(comp, protoITS)
+	t.buildInstanceSetPlacementAnnotation(comp, protoITS)
 
 	if err := t.reconcileReplicasStatus(ctx, cli, synthesizedComp, runningITS, protoITS); err != nil {
 		return err
@@ -172,6 +141,16 @@ func (t *componentWorkloadTransformer) reconcileWorkload(ctx context.Context, cl
 	return nil
 }
 
+func (t *componentWorkloadTransformer) buildInstanceSetPlacementAnnotation(comp *appsv1.Component, its *workloads.InstanceSet) {
+	p := appsutil.Placement(comp)
+	if len(p) > 0 {
+		if its.Annotations == nil {
+			its.Annotations = make(map[string]string)
+		}
+		its.Annotations[constant.KBAppMultiClusterPlacementKey] = p
+	}
+}
+
 func (t *componentWorkloadTransformer) reconcileReplicasStatus(ctx context.Context, cli client.Reader,
 	synthesizedComp *component.SynthesizedComponent, runningITS, protoITS *workloads.InstanceSet) error {
 	var (
@@ -209,23 +188,7 @@ func (t *componentWorkloadTransformer) reconcileReplicasStatus(ctx context.Conte
 	return component.StatusReplicasStatus(protoITS, replicas, hasMemberJoinDefined, hasDataActionDefined)
 }
 
-func hasMemberJoinNDataActionDefined(lifecycleActions *appsv1.ComponentLifecycleActions) (bool, bool) {
-	if lifecycleActions == nil {
-		return false, false
-	}
-	hasActionDefined := func(actions []*appsv1.Action) bool {
-		for _, action := range actions {
-			if action == nil || action.Exec == nil {
-				return false
-			}
-		}
-		return true
-	}
-	return hasActionDefined([]*appsv1.Action{lifecycleActions.MemberJoin}),
-		hasActionDefined([]*appsv1.Action{lifecycleActions.DataDump, lifecycleActions.DataLoad})
-}
-
-func (t *componentWorkloadTransformer) handleUpdate(reqCtx intctrlutil.RequestCtx, cli model.GraphClient, dag *graph.DAG,
+func (t *componentWorkloadTransformer) handleUpdate(transCtx *componentTransformContext, cli model.GraphClient, dag *graph.DAG,
 	synthesizedComp *component.SynthesizedComponent, comp *appsv1.Component, runningITS, protoITS *workloads.InstanceSet) error {
 	start, stop, err := t.handleWorkloadStartNStop(synthesizedComp, runningITS, &protoITS)
 	if err != nil {
@@ -234,7 +197,7 @@ func (t *componentWorkloadTransformer) handleUpdate(reqCtx intctrlutil.RequestCt
 
 	if !(start || stop) {
 		// postpone the update of the workload until the component is back to running.
-		if err := t.handleWorkloadUpdate(reqCtx, dag, synthesizedComp, comp, runningITS, protoITS); err != nil {
+		if err := t.handleWorkloadUpdate(transCtx, dag, synthesizedComp, comp, runningITS, protoITS); err != nil {
 			return err
 		}
 	}
@@ -356,34 +319,29 @@ func (t *componentWorkloadTransformer) startWorkload(
 	return nil
 }
 
-func (t *componentWorkloadTransformer) handleWorkloadUpdate(reqCtx intctrlutil.RequestCtx, dag *graph.DAG,
+func (t *componentWorkloadTransformer) handleWorkloadUpdate(transCtx *componentTransformContext, dag *graph.DAG,
 	synthesizeComp *component.SynthesizedComponent, comp *appsv1.Component, obj, its *workloads.InstanceSet) error {
-	cwo, err := newComponentWorkloadOps(reqCtx, t.Client, synthesizeComp, comp, obj, its, dag)
+	cwo, err := newComponentWorkloadOps(transCtx, t.Client, synthesizeComp, comp, obj, its, dag)
 	if err != nil {
 		return err
 	}
-
-	// handle expand volume
 	if err := cwo.expandVolume(); err != nil {
 		return err
 	}
-
-	// handle workload horizontal scale
 	if err := cwo.horizontalScale(); err != nil {
 		return err
 	}
-
+	if err := cwo.reconfigure(); err != nil {
+		return err
+	}
 	return nil
 }
 
 // buildPodSpecVolumeMounts builds podSpec volumeMounts
 func buildPodSpecVolumeMounts(synthesizeComp *component.SynthesizedComponent) {
-	kbScriptAndConfigVolumeNames := make([]string, 0)
-	for _, v := range synthesizeComp.ScriptTemplates {
-		kbScriptAndConfigVolumeNames = append(kbScriptAndConfigVolumeNames, v.VolumeName)
-	}
+	configVolumeNames := make([]string, 0)
 	for _, v := range synthesizeComp.ConfigTemplates {
-		kbScriptAndConfigVolumeNames = append(kbScriptAndConfigVolumeNames, v.VolumeName)
+		configVolumeNames = append(configVolumeNames, v.VolumeName)
 	}
 
 	podSpec := synthesizeComp.PodSpec
@@ -391,8 +349,8 @@ func buildPodSpecVolumeMounts(synthesizeComp *component.SynthesizedComponent) {
 		volumes := podSpec.Volumes
 		for _, c := range *cc {
 			for _, v := range c.VolumeMounts {
-				// if volumeMounts belongs to kbScriptAndConfigVolumeNames, skip
-				if slices.Contains(kbScriptAndConfigVolumeNames, v.Name) {
+				// if volumeMounts belongs to configVolumeNames, skip
+				if slices.Contains(configVolumeNames, v.Name) {
 					continue
 				}
 				// if persistence is not found, add an emptyDir to pod.spec.volumes
@@ -450,6 +408,7 @@ func copyAndMergeITS(oldITS, newITS *workloads.InstanceSet) *workloads.InstanceS
 	itsObjCopy.Spec.InstanceUpdateStrategy = itsProto.Spec.InstanceUpdateStrategy
 	itsObjCopy.Spec.MemberUpdateStrategy = itsProto.Spec.MemberUpdateStrategy
 	itsObjCopy.Spec.Paused = itsProto.Spec.Paused
+	itsObjCopy.Spec.Configs = itsProto.Spec.Configs
 
 	if itsObjCopy.Spec.InstanceUpdateStrategy != nil && itsObjCopy.Spec.InstanceUpdateStrategy.RollingUpdate != nil {
 		// use oldITS because itsObjCopy has been overwritten
@@ -530,590 +489,18 @@ func checkNRollbackProtoImages(itsObj, itsProto *workloads.InstanceSet) {
 	}
 }
 
-// expandVolume handles workload expand volume
-func (r *componentWorkloadOps) expandVolume() error {
-	return r.expandVolumeClaimTemplates(r.runningITS.Spec.VolumeClaimTemplates, r.synthesizeComp.VolumeClaimTemplates)
-}
-
-func (r *componentWorkloadOps) expandVolumeClaimTemplates(runningVCTs []corev1.PersistentVolumeClaim, protoVCTs []corev1.PersistentVolumeClaimTemplate) error {
-	for _, vct := range runningVCTs {
-		var proto *corev1.PersistentVolumeClaimTemplate
-		for i, v := range protoVCTs {
-			if v.Name == vct.Name {
-				proto = &protoVCTs[i]
-				break
-			}
-		}
-		// REVIEW: seems we can remove a volume claim from templates at runtime, without any changes and warning messages?
-		if proto == nil {
-			continue
-		}
-
-		if err := r.expandVolumes(vct.Name, proto); err != nil {
-			return err
-		}
-	}
-	return nil
-}
-
-func (r *componentWorkloadOps) horizontalScale() error {
-	var (
-		in  = r.runningItsPodNameSet.Difference(r.desiredCompPodNameSet)
-		out = r.desiredCompPodNameSet.Difference(r.runningItsPodNameSet)
-	)
-	if in.Len() == 0 && out.Len() == 0 {
-		return r.postHorizontalScale() // TODO: how about consecutive horizontal scales?
-	}
-
-	if in.Len() > 0 {
-		if err := r.scaleIn(); err != nil {
-			return err
-		}
-	}
-
-	if out.Len() > 0 {
-		if err := r.scaleOut(); err != nil {
-			return err
-		}
-	}
-
-	r.reqCtx.Recorder.Eventf(r.component,
-		corev1.EventTypeNormal,
-		"HorizontalScale",
-		"start horizontal scale component %s of cluster %s from %d to %d",
-		r.synthesizeComp.Name, r.synthesizeComp.ClusterName, int(*r.runningITS.Spec.Replicas), r.synthesizeComp.Replicas)
-
-	return nil
-}
-
-func (r *componentWorkloadOps) scaleIn() error {
-	if r.synthesizeComp.Replicas == 0 && len(r.synthesizeComp.VolumeClaimTemplates) > 0 {
-		if r.synthesizeComp.PVCRetentionPolicy.WhenScaled != appsv1.RetainPersistentVolumeClaimRetentionPolicyType {
-			return fmt.Errorf("when intending to scale-in to 0, only the \"Retain\" option is supported for the PVC retention policy")
-		}
-	}
-
-	deleteReplicas := r.runningItsPodNameSet.Difference(r.desiredCompPodNameSet).UnsortedList()
-	joinedReplicas := make([]string, 0)
-	err := component.DeleteReplicasStatus(r.protoITS, deleteReplicas, func(s component.ReplicaStatus) {
-		// has no member join defined or has joined successfully
-		if s.Provisioned && (s.MemberJoined == nil || *s.MemberJoined) {
-			joinedReplicas = append(joinedReplicas, s.Name)
-		}
-	})
-	if err != nil {
-		return err
-	}
-
-	// TODO: check the component definition to determine whether we need to call leave member before deleting replicas.
-	if err := r.leaveMember4ScaleIn(deleteReplicas, joinedReplicas); err != nil {
-		r.reqCtx.Log.Error(err, "leave member at scale-in error")
-		return err
-	}
-	return nil
-}
-
-func (r *componentWorkloadOps) leaveMember4ScaleIn(deleteReplicas, joinedReplicas []string) error {
-	pods, err := component.ListOwnedPods(r.reqCtx.Ctx, r.cli,
-		r.synthesizeComp.Namespace, r.synthesizeComp.ClusterName, r.synthesizeComp.Name)
-	if err != nil {
-		return err
-	}
-
-	deleteReplicasSet := sets.New(deleteReplicas...)
-	joinedReplicasSet := sets.New(joinedReplicas...)
-	hasMemberLeaveDefined := r.synthesizeComp.LifecycleActions != nil && r.synthesizeComp.LifecycleActions.MemberLeave != nil
-	r.reqCtx.Log.Info("leave member at scaling-in", "delete replicas", deleteReplicas,
-		"joined replicas", joinedReplicas, "has member-leave action defined", hasMemberLeaveDefined)
-
-	leaveErrors := make([]error, 0)
-	for _, pod := range pods {
-		if deleteReplicasSet.Has(pod.Name) {
-			if joinedReplicasSet.Has(pod.Name) { // else: hasn't joined yet, no need to leave
-				if err = r.leaveMemberForPod(pod, pods); err != nil {
-					leaveErrors = append(leaveErrors, err)
-				}
-				joinedReplicasSet.Delete(pod.Name)
-			}
-			deleteReplicasSet.Delete(pod.Name)
-		}
-	}
-
-	if hasMemberLeaveDefined && len(joinedReplicasSet) > 0 {
-		leaveErrors = append(leaveErrors,
-			fmt.Errorf("some replicas have joined but not leaved since the Pod object is not exist: %v", sets.List(joinedReplicasSet)))
-	}
-	if len(leaveErrors) > 0 {
-		return intctrlutil.NewRequeueError(time.Second, fmt.Sprintf("%v", leaveErrors))
-	}
-	return nil
-}
-
-func (r *componentWorkloadOps) leaveMemberForPod(pod *corev1.Pod, pods []*corev1.Pod) error {
-	var (
-		synthesizedComp  = r.synthesizeComp
-		lifecycleActions = synthesizedComp.LifecycleActions
-	)
-
-	trySwitchover := func(lfa lifecycle.Lifecycle, pod *corev1.Pod) error {
-		if lifecycleActions.Switchover == nil {
-			return nil
-		}
-		err := lfa.Switchover(r.reqCtx.Ctx, r.cli, nil, "")
-		if err != nil {
-			if errors.Is(err, lifecycle.ErrActionNotDefined) {
-				return nil
-			}
-			return err
-		}
-		r.reqCtx.Log.Info("successfully call switchover action for pod", "pod", pod.Name)
-		return nil
-	}
-
-	tryMemberLeave := func(lfa lifecycle.Lifecycle, pod *corev1.Pod) error {
-		if lifecycleActions.MemberLeave == nil {
-			return nil
-		}
-		err := lfa.MemberLeave(r.reqCtx.Ctx, r.cli, nil)
-		if err != nil {
-			if errors.Is(err, lifecycle.ErrActionNotDefined) {
-				return nil
-			}
-			return err
-		}
-		r.reqCtx.Log.Info("successfully call leave member action for pod", "pod", pod.Name)
-		return nil
-	}
-
-	if lifecycleActions == nil || (lifecycleActions.Switchover == nil && lifecycleActions.MemberLeave == nil) {
-		return nil
-	}
-
-	lfa, err := lifecycle.New(synthesizedComp.Namespace, synthesizedComp.ClusterName, synthesizedComp.Name,
-		lifecycleActions, synthesizedComp.TemplateVars, pod, pods...)
-	if err != nil {
-		return err
-	}
-
-	if err := trySwitchover(lfa, pod); err != nil {
-		return err
-	}
-
-	if err := tryMemberLeave(lfa, pod); err != nil {
-		return err
-	}
-
-	return nil
-}
-
-func (r *componentWorkloadOps) scaleOut() error {
-	// replicas in provisioning that the data has not been loaded
-	provisioningReplicas, err := component.GetReplicasStatusFunc(r.protoITS, func(s component.ReplicaStatus) bool {
-		return s.DataLoaded != nil && !*s.DataLoaded
-	})
-	if err != nil {
-		return err
-	}
-
-	// replicas to be created
-	newReplicas := r.desiredCompPodNameSet.Difference(r.runningItsPodNameSet).UnsortedList()
-
-	hasMemberJoinDefined, hasDataActionDefined := hasMemberJoinNDataActionDefined(r.synthesizeComp.LifecycleActions)
-
-	// build and assign data replication tasks
-	if err := func() error {
-		if !hasDataActionDefined {
-			return nil
-		}
-
-		source, err := r.sourceReplica(r.synthesizeComp.LifecycleActions.DataDump)
-		if err != nil {
-			return err
-		}
-
-		replicas := append(slices.Clone(newReplicas), provisioningReplicas...)
-		parameters, err := component.NewReplicaTask(r.synthesizeComp.FullCompName, r.synthesizeComp.Generation, source, replicas)
-		if err != nil {
-			return err
-		}
-		// apply the updated env to the env CM
-		transCtx := &componentTransformContext{
-			Context:             r.reqCtx.Ctx,
-			Client:              model.NewGraphClient(r.cli),
-			SynthesizeComponent: r.synthesizeComp,
-			Component:           r.component,
-		}
-		if err = createOrUpdateEnvConfigMap(transCtx, r.dag, nil, parameters); err != nil {
-			return err
-		}
-		return nil
-	}(); err != nil {
-		return err
-	}
-
-	return component.NewReplicasStatus(r.protoITS, newReplicas, hasMemberJoinDefined, hasDataActionDefined)
-}
-
-func (r *componentWorkloadOps) sourceReplica(dataDump *appsv1.Action) (*corev1.Pod, error) {
-	pods, err := component.ListOwnedPods(r.reqCtx.Ctx, r.cli,
-		r.synthesizeComp.Namespace, r.synthesizeComp.ClusterName, r.synthesizeComp.Name)
-	if err != nil {
-		return nil, err
-	}
-	if len(pods) > 0 {
-		if len(dataDump.Exec.TargetPodSelector) == 0 {
-			dataDump.Exec.TargetPodSelector = appsv1.AnyReplica
-		}
-		pods, err = lifecycle.SelectTargetPods(pods, nil, dataDump)
-		if err != nil {
-			return nil, err
-		}
-		if len(pods) > 0 {
-			return pods[0], nil
-		}
-	}
-	return nil, fmt.Errorf("no available pod to dump data")
-}
-
-func (r *componentWorkloadOps) postHorizontalScale() error {
-	if err := r.joinMember4ScaleOut(); err != nil {
-		return err
-	}
-	return nil
-}
-
-func (r *componentWorkloadOps) joinMember4ScaleOut() error {
-	pods, err := component.ListOwnedPods(r.reqCtx.Ctx, r.cli,
-		r.synthesizeComp.Namespace, r.synthesizeComp.ClusterName, r.synthesizeComp.Name)
-	if err != nil {
-		return err
-	}
-
-	joinErrors := make([]error, 0)
-	if err = component.UpdateReplicasStatusFunc(r.protoITS, func(replicas *component.ReplicasStatus) error {
-		for _, pod := range pods {
-			i := slices.IndexFunc(replicas.Status, func(r component.ReplicaStatus) bool {
-				return r.Name == pod.Name
-			})
-			if i < 0 {
-				continue // the pod is not in the replicas status?
-			}
-
-			status := replicas.Status[i]
-			if status.MemberJoined == nil || *status.MemberJoined {
-				continue // no need to join or already joined
-			}
-
-			// TODO: should wait for the data to be loaded before joining the member?
-
-			if err := r.joinMemberForPod(pod, pods); err != nil {
-				joinErrors = append(joinErrors, fmt.Errorf("pod %s: %w", pod.Name, err))
-			} else {
-				replicas.Status[i].MemberJoined = ptr.To(true)
-			}
-		}
-
-		notJoinedReplicas := make([]string, 0)
-		for _, r := range replicas.Status {
-			if r.MemberJoined != nil && !*r.MemberJoined {
-				notJoinedReplicas = append(notJoinedReplicas, r.Name)
-			}
-		}
-		if len(notJoinedReplicas) > 0 {
-			joinErrors = append(joinErrors, fmt.Errorf("some replicas have not joined: %v", notJoinedReplicas))
-		}
-		return nil
-	}); err != nil {
-		return err
-	}
-
-	if len(joinErrors) > 0 {
-		return intctrlutil.NewRequeueError(time.Second, fmt.Sprintf("%v", joinErrors))
-	}
-	return nil
-}
-
-func (r *componentWorkloadOps) joinMemberForPod(pod *corev1.Pod, pods []*corev1.Pod) error {
-	synthesizedComp := r.synthesizeComp
-	lfa, err := lifecycle.New(synthesizedComp.Namespace, synthesizedComp.ClusterName, synthesizedComp.Name,
-		synthesizedComp.LifecycleActions, synthesizedComp.TemplateVars, pod, pods...)
-	if err != nil {
-		return err
-	}
-	if err = lfa.MemberJoin(r.reqCtx.Ctx, r.cli, nil); err != nil {
-		if !errors.Is(err, lifecycle.ErrActionNotDefined) {
-			return err
-		}
-	}
-	r.reqCtx.Log.Info("succeed to join member for pod", "pod", pod.Name)
-	return nil
-}
-
-func (r *componentWorkloadOps) expandVolumes(vctName string, proto *corev1.PersistentVolumeClaimTemplate) error {
-	for _, pod := range r.runningItsPodNames {
-		pvc := &corev1.PersistentVolumeClaim{}
-		pvcKey := types.NamespacedName{
-			Namespace: r.synthesizeComp.Namespace,
-			Name:      fmt.Sprintf("%s-%s", vctName, pod),
-		}
-		pvcNotFound := false
-		if err := r.cli.Get(r.reqCtx.Ctx, pvcKey, pvc, appsutil.InDataContext4C()); err != nil {
-			if apierrors.IsNotFound(err) {
-				pvcNotFound = true
-			} else {
-				return err
-			}
-		}
-		if !pvcNotFound {
-			quantity := pvc.Spec.Resources.Requests.Storage()
-			newQuantity := proto.Spec.Resources.Requests.Storage()
-			if quantity.Cmp(*pvc.Status.Capacity.Storage()) == 0 && newQuantity.Cmp(*quantity) < 0 {
-				errMsg := fmt.Sprintf("shrinking the volume is not supported, volume: %s, quantity: %s, new quantity: %s",
-					pvc.GetName(), quantity.String(), newQuantity.String())
-				r.reqCtx.Event(r.component, corev1.EventTypeWarning, "VolumeExpansionFailed", errMsg)
-				return fmt.Errorf("%s", errMsg)
-			}
-		}
-
-		if err := r.updatePVCSize(pvcKey, pvc, pvcNotFound, proto); err != nil {
-			return err
-		}
-	}
-	return nil
-}
-
-func (r *componentWorkloadOps) updatePVCSize(pvcKey types.NamespacedName,
-	pvc *corev1.PersistentVolumeClaim, pvcNotFound bool, vctProto *corev1.PersistentVolumeClaimTemplate) error {
-	// reference: https://kubernetes.io/docs/concepts/storage/persistent-volumes/#recovering-from-failure-when-expanding-volumes
-	// 1. Mark the PersistentVolume(PV) that is bound to the PersistentVolumeClaim(PVC) with Retain reclaim policy.
-	// 2. Delete the PVC. Since PV has Retain reclaim policy - we will not lose any data when we recreate the PVC.
-	// 3. Delete the claimRef entry from PV specs, so as new PVC can bind to it. This should make the PV Available.
-	// 4. Re-create the PVC with smaller size than PV and set volumeName field of the PVC to the name of the PV. This should bind new PVC to existing PV.
-	// 5. Don't forget to restore the reclaim policy of the PV.
-	newPVC := pvc.DeepCopy()
-	if pvcNotFound {
-		newPVC.Name = pvcKey.Name
-		newPVC.Namespace = pvcKey.Namespace
-		newPVC.SetLabels(vctProto.Labels)
-		newPVC.Spec = vctProto.Spec
-		ml := client.MatchingLabels{
-			constant.PVCNameLabelKey: pvcKey.Name,
-		}
-		pvList := corev1.PersistentVolumeList{}
-		if err := r.cli.List(r.reqCtx.Ctx, &pvList, ml, appsutil.InDataContext4C()); err != nil {
-			return err
-		}
-		for _, pv := range pvList.Items {
-			// find pv referenced this pvc
-			if pv.Spec.ClaimRef == nil {
-				continue
-			}
-			if pv.Spec.ClaimRef.Name == pvcKey.Name {
-				newPVC.Spec.VolumeName = pv.Name
-				break
-			}
-		}
-	} else {
-		newPVC.Spec.Resources.Requests[corev1.ResourceStorage] = vctProto.Spec.Resources.Requests[corev1.ResourceStorage]
-		// delete annotation to make it re-bind
-		delete(newPVC.Annotations, "pv.kubernetes.io/bind-completed")
-	}
-
-	pvNotFound := false
-
-	// step 1: update pv to retain
-	pv := &corev1.PersistentVolume{}
-	if len(newPVC.Spec.VolumeName) == 0 {
-		// the PV may be under provisioning
-		pvNotFound = true
-	} else {
-		pvKey := types.NamespacedName{
-			Namespace: pvcKey.Namespace,
-			Name:      newPVC.Spec.VolumeName,
-		}
-		if err := r.cli.Get(r.reqCtx.Ctx, pvKey, pv, appsutil.InDataContext4C()); err != nil {
-			if apierrors.IsNotFound(err) {
-				pvNotFound = true
-			} else {
-				return err
-			}
-		}
+func hasMemberJoinNDataActionDefined(lifecycleActions *appsv1.ComponentLifecycleActions) (bool, bool) {
+	if lifecycleActions == nil {
+		return false, false
 	}
-
-	graphCli := model.NewGraphClient(r.cli)
-
-	type pvcRecreateStep int
-	const (
-		pvPolicyRetainStep pvcRecreateStep = iota
-		deletePVCStep
-		removePVClaimRefStep
-		createPVCStep
-		pvRestorePolicyStep
-	)
-
-	addStepMap := map[pvcRecreateStep]func(fromVertex *model.ObjectVertex, step pvcRecreateStep) *model.ObjectVertex{
-		pvPolicyRetainStep: func(fromVertex *model.ObjectVertex, step pvcRecreateStep) *model.ObjectVertex {
-			// step 1: update pv to retain
-			retainPV := pv.DeepCopy()
-			if retainPV.Labels == nil {
-				retainPV.Labels = make(map[string]string)
-			}
-			// add label to pv, in case pvc get deleted, and we can't find pv
-			retainPV.Labels[constant.PVCNameLabelKey] = pvcKey.Name
-			if retainPV.Annotations == nil {
-				retainPV.Annotations = make(map[string]string)
-			}
-			retainPV.Annotations[constant.PVLastClaimPolicyAnnotationKey] = string(pv.Spec.PersistentVolumeReclaimPolicy)
-			retainPV.Spec.PersistentVolumeReclaimPolicy = corev1.PersistentVolumeReclaimRetain
-			return graphCli.Do(r.dag, pv, retainPV, model.ActionPatchPtr(), fromVertex, appsutil.InDataContext4G())
-		},
-		deletePVCStep: func(fromVertex *model.ObjectVertex, step pvcRecreateStep) *model.ObjectVertex {
-			// step 2: delete pvc, this will not delete pv because policy is 'retain'
-			removeFinalizerPVC := pvc.DeepCopy()
-			removeFinalizerPVC.SetFinalizers([]string{})
-			removeFinalizerPVCVertex := graphCli.Do(r.dag, pvc, removeFinalizerPVC, model.ActionPatchPtr(), fromVertex, appsutil.InDataContext4G())
-			return graphCli.Do(r.dag, nil, removeFinalizerPVC, model.ActionDeletePtr(), removeFinalizerPVCVertex, appsutil.InDataContext4G())
-		},
-		removePVClaimRefStep: func(fromVertex *model.ObjectVertex, step pvcRecreateStep) *model.ObjectVertex {
-			// step 3: remove claimRef in pv
-			removeClaimRefPV := pv.DeepCopy()
-			if removeClaimRefPV.Spec.ClaimRef != nil {
-				removeClaimRefPV.Spec.ClaimRef.UID = ""
-				removeClaimRefPV.Spec.ClaimRef.ResourceVersion = ""
-			}
-			return graphCli.Do(r.dag, pv, removeClaimRefPV, model.ActionPatchPtr(), fromVertex, appsutil.InDataContext4G())
-		},
-		createPVCStep: func(fromVertex *model.ObjectVertex, step pvcRecreateStep) *model.ObjectVertex {
-			// step 4: create new pvc
-			newPVC.SetResourceVersion("")
-			return graphCli.Do(r.dag, nil, newPVC, model.ActionCreatePtr(), fromVertex, appsutil.InDataContext4G())
-		},
-		pvRestorePolicyStep: func(fromVertex *model.ObjectVertex, step pvcRecreateStep) *model.ObjectVertex {
-			// step 5: restore to previous pv policy
-			restorePV := pv.DeepCopy()
-			policy := corev1.PersistentVolumeReclaimPolicy(restorePV.Annotations[constant.PVLastClaimPolicyAnnotationKey])
-			if len(policy) == 0 {
-				policy = corev1.PersistentVolumeReclaimDelete
+	hasActionDefined := func(actions []*appsv1.Action) bool {
+		for _, action := range actions {
+			if action == nil || action.Exec == nil {
+				return false
 			}
-			restorePV.Spec.PersistentVolumeReclaimPolicy = policy
-			return graphCli.Do(r.dag, pv, restorePV, model.ActionPatchPtr(), fromVertex, appsutil.InDataContext4G())
-		},
-	}
-
-	updatePVCByRecreateFromStep := func(fromStep pvcRecreateStep) {
-		lastVertex := r.buildProtoITSWorkloadVertex()
-		// The steps here are decremented in reverse order because during the plan execution, dag.WalkReverseTopoOrder
-		// is called to execute all vertices on the graph according to the reverse topological order.
-		// Therefore, the vertices need to maintain the following edge linkages:
-		// root -> its -> step5 -> step4 -> step3 -> step2 -> step1
-		// So that, during execution, the sequence becomes step1 -> step2 -> step3 -> step4 -> step5
-		for step := pvRestorePolicyStep; step >= fromStep && step >= pvPolicyRetainStep; step-- {
-			lastVertex = addStepMap[step](lastVertex, step)
 		}
+		return true
 	}
-
-	targetQuantity := vctProto.Spec.Resources.Requests[corev1.ResourceStorage]
-	if pvcNotFound && !pvNotFound {
-		// this could happen if create pvc step failed when recreating pvc
-		updatePVCByRecreateFromStep(removePVClaimRefStep)
-		return nil
-	}
-	if pvcNotFound && pvNotFound {
-		// if both pvc and pv not found, do nothing
-		return nil
-	}
-	if equality.Semantic.DeepEqual(pvc.Spec.Resources, newPVC.Spec.Resources) &&
-		pv.Spec.PersistentVolumeReclaimPolicy == corev1.PersistentVolumeReclaimRetain &&
-		pv.Annotations != nil &&
-		len(pv.Annotations[constant.PVLastClaimPolicyAnnotationKey]) > 0 &&
-		pv.Annotations[constant.PVLastClaimPolicyAnnotationKey] != string(corev1.PersistentVolumeReclaimRetain) {
-		// this could happen if create pvc succeeded but last step failed
-		updatePVCByRecreateFromStep(pvRestorePolicyStep)
-		return nil
-	}
-	if pvcQuantity := pvc.Spec.Resources.Requests[corev1.ResourceStorage]; !viper.GetBool(constant.CfgRecoverVolumeExpansionFailure) &&
-		pvcQuantity.Cmp(targetQuantity) == 1 && // check if it's compressing volume
-		targetQuantity.Cmp(*pvc.Status.Capacity.Storage()) >= 0 { // check if target size is greater than or equal to actual size
-		// this branch means we can update pvc size by recreate it
-		updatePVCByRecreateFromStep(pvPolicyRetainStep)
-		return nil
-	}
-	if pvcQuantity := pvc.Spec.Resources.Requests[corev1.ResourceStorage]; pvcQuantity.Cmp(vctProto.Spec.Resources.Requests[corev1.ResourceStorage]) != 0 {
-		// use pvc's update without anything extra
-		graphCli.Update(r.dag, nil, newPVC, appsutil.InDataContext4G())
-		return nil
-	}
-	// all the else means no need to update
-
-	return nil
-}
-
-// buildProtoITSWorkloadVertex builds protoITS workload vertex
-func (r *componentWorkloadOps) buildProtoITSWorkloadVertex() *model.ObjectVertex {
-	for _, vertex := range r.dag.Vertices() {
-		v, _ := vertex.(*model.ObjectVertex)
-		if v.Obj == r.protoITS {
-			return v
-		}
-	}
-	return nil
-}
-
-func getRunningVolumes(ctx context.Context, cli client.Client, synthesizedComp *component.SynthesizedComponent,
-	itsObj *workloads.InstanceSet, vctName string) ([]*corev1.PersistentVolumeClaim, error) {
-	pvcs, err := component.ListOwnedPVCs(ctx, cli, synthesizedComp.Namespace, synthesizedComp.ClusterName, synthesizedComp.Name)
-	if err != nil {
-		if apierrors.IsNotFound(err) {
-			return nil, nil
-		}
-		return nil, err
-	}
-	matchedPVCs := make([]*corev1.PersistentVolumeClaim, 0)
-	prefix := fmt.Sprintf("%s-%s", vctName, itsObj.Name)
-	for _, pvc := range pvcs {
-		if strings.HasPrefix(pvc.Name, prefix) {
-			matchedPVCs = append(matchedPVCs, pvc)
-		}
-	}
-	return matchedPVCs, nil
-}
-
-func buildInstanceSetPlacementAnnotation(comp *appsv1.Component, its *workloads.InstanceSet) {
-	p := appsutil.Placement(comp)
-	if len(p) > 0 {
-		if its.Annotations == nil {
-			its.Annotations = make(map[string]string)
-		}
-		its.Annotations[constant.KBAppMultiClusterPlacementKey] = p
-	}
-}
-
-func newComponentWorkloadOps(reqCtx intctrlutil.RequestCtx,
-	cli client.Client,
-	synthesizeComp *component.SynthesizedComponent,
-	comp *appsv1.Component,
-	runningITS *workloads.InstanceSet,
-	protoITS *workloads.InstanceSet,
-	dag *graph.DAG) (*componentWorkloadOps, error) {
-	compPodNames, err := generatePodNames(synthesizeComp)
-	if err != nil {
-		return nil, err
-	}
-	itsPodNames, err := generatePodNamesByITS(runningITS)
-	if err != nil {
-		return nil, err
-	}
-	return &componentWorkloadOps{
-		cli:                   cli,
-		reqCtx:                reqCtx,
-		component:             comp,
-		synthesizeComp:        synthesizeComp,
-		runningITS:            runningITS,
-		protoITS:              protoITS,
-		dag:                   dag,
-		desiredCompPodNames:   compPodNames,
-		runningItsPodNames:    itsPodNames,
-		desiredCompPodNameSet: sets.New(compPodNames...),
-		runningItsPodNameSet:  sets.New(itsPodNames...),
-	}, nil
+	return hasActionDefined([]*appsv1.Action{lifecycleActions.MemberJoin}),
+		hasActionDefined([]*appsv1.Action{lifecycleActions.DataDump, lifecycleActions.DataLoad})
 }
diff --git a/controllers/apps/component/transformer_component_workload_ops.go b/controllers/apps/component/transformer_component_workload_ops.go
new file mode 100644
index 000000000..a9e9aef5e
--- /dev/null
+++ b/controllers/apps/component/transformer_component_workload_ops.go
@@ -0,0 +1,787 @@
+/*
+Copyright (C) 2022-2025 ApeCloud Co., Ltd
+
+This file is part of KubeBlocks project
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+package component
+
+import (
+	"crypto/sha256"
+	"errors"
+	"fmt"
+	"path/filepath"
+	"reflect"
+	"slices"
+	"strings"
+	"time"
+
+	"github.com/spf13/viper"
+	corev1 "k8s.io/api/core/v1"
+	"k8s.io/apimachinery/pkg/api/equality"
+	apierrors "k8s.io/apimachinery/pkg/api/errors"
+	"k8s.io/apimachinery/pkg/types"
+	"k8s.io/apimachinery/pkg/util/sets"
+	"k8s.io/utils/ptr"
+	"sigs.k8s.io/controller-runtime/pkg/client"
+
+	appsv1 "github.com/apecloud/kubeblocks/apis/apps/v1"
+	workloads "github.com/apecloud/kubeblocks/apis/workloads/v1"
+	appsutil "github.com/apecloud/kubeblocks/controllers/apps/util"
+	"github.com/apecloud/kubeblocks/pkg/constant"
+	"github.com/apecloud/kubeblocks/pkg/controller/component"
+	"github.com/apecloud/kubeblocks/pkg/controller/graph"
+	"github.com/apecloud/kubeblocks/pkg/controller/lifecycle"
+	"github.com/apecloud/kubeblocks/pkg/controller/model"
+	intctrlutil "github.com/apecloud/kubeblocks/pkg/controllerutil"
+)
+
+type componentWorkloadOps struct {
+	transCtx       *componentTransformContext
+	cli            client.Client
+	component      *appsv1.Component
+	synthesizeComp *component.SynthesizedComponent
+	dag            *graph.DAG
+
+	// runningITS is a snapshot of the InstanceSet that is already running
+	runningITS *workloads.InstanceSet
+	// protoITS is the InstanceSet object that is rebuilt from scratch during each reconcile process
+	protoITS              *workloads.InstanceSet
+	desiredCompPodNames   []string
+	runningItsPodNames    []string
+	desiredCompPodNameSet sets.Set[string]
+	runningItsPodNameSet  sets.Set[string]
+}
+
+func newComponentWorkloadOps(transCtx *componentTransformContext,
+	cli client.Client,
+	synthesizedComp *component.SynthesizedComponent,
+	comp *appsv1.Component,
+	runningITS *workloads.InstanceSet,
+	protoITS *workloads.InstanceSet,
+	dag *graph.DAG) (*componentWorkloadOps, error) {
+	compPodNames, err := generatePodNames(synthesizedComp)
+	if err != nil {
+		return nil, err
+	}
+	itsPodNames, err := generatePodNamesByITS(runningITS)
+	if err != nil {
+		return nil, err
+	}
+	return &componentWorkloadOps{
+		transCtx:              transCtx,
+		cli:                   cli,
+		component:             comp,
+		synthesizeComp:        synthesizedComp,
+		runningITS:            runningITS,
+		protoITS:              protoITS,
+		dag:                   dag,
+		desiredCompPodNames:   compPodNames,
+		runningItsPodNames:    itsPodNames,
+		desiredCompPodNameSet: sets.New(compPodNames...),
+		runningItsPodNameSet:  sets.New(itsPodNames...),
+	}, nil
+}
+
+func (r *componentWorkloadOps) horizontalScale() error {
+	var (
+		in  = r.runningItsPodNameSet.Difference(r.desiredCompPodNameSet)
+		out = r.desiredCompPodNameSet.Difference(r.runningItsPodNameSet)
+	)
+	if in.Len() == 0 && out.Len() == 0 {
+		return r.postHorizontalScale() // TODO: how about consecutive horizontal scales?
+	}
+
+	if in.Len() > 0 {
+		if err := r.scaleIn(); err != nil {
+			return err
+		}
+	}
+
+	if out.Len() > 0 {
+		if err := r.scaleOut(); err != nil {
+			return err
+		}
+	}
+
+	r.transCtx.EventRecorder.Eventf(r.component,
+		corev1.EventTypeNormal,
+		"HorizontalScale",
+		"start horizontal scale component %s of cluster %s from %d to %d",
+		r.synthesizeComp.Name, r.synthesizeComp.ClusterName, int(*r.runningITS.Spec.Replicas), r.synthesizeComp.Replicas)
+
+	return nil
+}
+
+func (r *componentWorkloadOps) scaleIn() error {
+	if r.synthesizeComp.Replicas == 0 && len(r.synthesizeComp.VolumeClaimTemplates) > 0 {
+		if r.synthesizeComp.PVCRetentionPolicy.WhenScaled != appsv1.RetainPersistentVolumeClaimRetentionPolicyType {
+			return fmt.Errorf("when intending to scale-in to 0, only the \"Retain\" option is supported for the PVC retention policy")
+		}
+	}
+
+	deleteReplicas := r.runningItsPodNameSet.Difference(r.desiredCompPodNameSet).UnsortedList()
+	joinedReplicas := make([]string, 0)
+	err := component.DeleteReplicasStatus(r.protoITS, deleteReplicas, func(s component.ReplicaStatus) {
+		// has no member join defined or has joined successfully
+		if s.Provisioned && (s.MemberJoined == nil || *s.MemberJoined) {
+			joinedReplicas = append(joinedReplicas, s.Name)
+		}
+	})
+	if err != nil {
+		return err
+	}
+
+	// TODO: check the component definition to determine whether we need to call leave member before deleting replicas.
+	if err := r.leaveMember4ScaleIn(deleteReplicas, joinedReplicas); err != nil {
+		r.transCtx.Logger.Error(err, "leave member at scale-in error")
+		return err
+	}
+	return nil
+}
+
+func (r *componentWorkloadOps) leaveMember4ScaleIn(deleteReplicas, joinedReplicas []string) error {
+	pods, err := component.ListOwnedPods(r.transCtx.Context, r.cli,
+		r.synthesizeComp.Namespace, r.synthesizeComp.ClusterName, r.synthesizeComp.Name)
+	if err != nil {
+		return err
+	}
+
+	deleteReplicasSet := sets.New(deleteReplicas...)
+	joinedReplicasSet := sets.New(joinedReplicas...)
+	hasMemberLeaveDefined := r.synthesizeComp.LifecycleActions != nil && r.synthesizeComp.LifecycleActions.MemberLeave != nil
+	r.transCtx.Logger.Info("leave member at scaling-in", "delete replicas", deleteReplicas,
+		"joined replicas", joinedReplicas, "has member-leave action defined", hasMemberLeaveDefined)
+
+	leaveErrors := make([]error, 0)
+	for _, pod := range pods {
+		if deleteReplicasSet.Has(pod.Name) {
+			if joinedReplicasSet.Has(pod.Name) { // else: hasn't joined yet, no need to leave
+				if err = r.leaveMemberForPod(pod, pods); err != nil {
+					leaveErrors = append(leaveErrors, err)
+				}
+				joinedReplicasSet.Delete(pod.Name)
+			}
+			deleteReplicasSet.Delete(pod.Name)
+		}
+	}
+
+	if hasMemberLeaveDefined && len(joinedReplicasSet) > 0 {
+		leaveErrors = append(leaveErrors,
+			fmt.Errorf("some replicas have joined but not leaved since the Pod object is not exist: %v", sets.List(joinedReplicasSet)))
+	}
+	if len(leaveErrors) > 0 {
+		return intctrlutil.NewRequeueError(time.Second, fmt.Sprintf("%v", leaveErrors))
+	}
+	return nil
+}
+
+func (r *componentWorkloadOps) leaveMemberForPod(pod *corev1.Pod, pods []*corev1.Pod) error {
+	var (
+		synthesizedComp  = r.synthesizeComp
+		lifecycleActions = synthesizedComp.LifecycleActions
+	)
+
+	trySwitchover := func(lfa lifecycle.Lifecycle, pod *corev1.Pod) error {
+		if lifecycleActions.Switchover == nil {
+			return nil
+		}
+		err := lfa.Switchover(r.transCtx.Context, r.cli, nil, "")
+		if err != nil {
+			if errors.Is(err, lifecycle.ErrActionNotDefined) {
+				return nil
+			}
+			return err
+		}
+		r.transCtx.Logger.Info("successfully call switchover action for pod", "pod", pod.Name)
+		return nil
+	}
+
+	tryMemberLeave := func(lfa lifecycle.Lifecycle, pod *corev1.Pod) error {
+		if lifecycleActions.MemberLeave == nil {
+			return nil
+		}
+		err := lfa.MemberLeave(r.transCtx.Context, r.cli, nil)
+		if err != nil {
+			if errors.Is(err, lifecycle.ErrActionNotDefined) {
+				return nil
+			}
+			return err
+		}
+		r.transCtx.Logger.Info("successfully call leave member action for pod", "pod", pod.Name)
+		return nil
+	}
+
+	if lifecycleActions == nil || (lifecycleActions.Switchover == nil && lifecycleActions.MemberLeave == nil) {
+		return nil
+	}
+
+	lfa, err := lifecycle.New(synthesizedComp.Namespace, synthesizedComp.ClusterName, synthesizedComp.Name,
+		lifecycleActions, synthesizedComp.TemplateVars, pod, pods...)
+	if err != nil {
+		return err
+	}
+
+	if err := trySwitchover(lfa, pod); err != nil {
+		return err
+	}
+
+	if err := tryMemberLeave(lfa, pod); err != nil {
+		return err
+	}
+
+	return nil
+}
+
+func (r *componentWorkloadOps) scaleOut() error {
+	// replicas in provisioning that the data has not been loaded
+	provisioningReplicas, err := component.GetReplicasStatusFunc(r.protoITS, func(s component.ReplicaStatus) bool {
+		return s.DataLoaded != nil && !*s.DataLoaded
+	})
+	if err != nil {
+		return err
+	}
+
+	// replicas to be created
+	newReplicas := r.desiredCompPodNameSet.Difference(r.runningItsPodNameSet).UnsortedList()
+
+	hasMemberJoinDefined, hasDataActionDefined := hasMemberJoinNDataActionDefined(r.synthesizeComp.LifecycleActions)
+
+	// build and assign data replication tasks
+	if err := func() error {
+		if !hasDataActionDefined {
+			return nil
+		}
+
+		source, err := r.sourceReplica(r.synthesizeComp.LifecycleActions.DataDump)
+		if err != nil {
+			return err
+		}
+
+		replicas := append(slices.Clone(newReplicas), provisioningReplicas...)
+		parameters, err := component.NewReplicaTask(r.synthesizeComp.FullCompName, r.synthesizeComp.Generation, source, replicas)
+		if err != nil {
+			return err
+		}
+		// apply the updated env to the env CM
+		transCtx := &componentTransformContext{
+			Context:             r.transCtx.Context,
+			Client:              model.NewGraphClient(r.cli),
+			SynthesizeComponent: r.synthesizeComp,
+			Component:           r.component,
+		}
+		if err = createOrUpdateEnvConfigMap(transCtx, r.dag, nil, parameters); err != nil {
+			return err
+		}
+		return nil
+	}(); err != nil {
+		return err
+	}
+
+	return component.NewReplicasStatus(r.protoITS, newReplicas, hasMemberJoinDefined, hasDataActionDefined)
+}
+
+func (r *componentWorkloadOps) sourceReplica(dataDump *appsv1.Action) (*corev1.Pod, error) {
+	pods, err := component.ListOwnedPods(r.transCtx.Context, r.cli,
+		r.synthesizeComp.Namespace, r.synthesizeComp.ClusterName, r.synthesizeComp.Name)
+	if err != nil {
+		return nil, err
+	}
+	if len(pods) > 0 {
+		if len(dataDump.Exec.TargetPodSelector) == 0 {
+			dataDump.Exec.TargetPodSelector = appsv1.AnyReplica
+		}
+		pods, err = lifecycle.SelectTargetPods(pods, nil, dataDump)
+		if err != nil {
+			return nil, err
+		}
+		if len(pods) > 0 {
+			return pods[0], nil
+		}
+	}
+	return nil, fmt.Errorf("no available pod to dump data")
+}
+
+func (r *componentWorkloadOps) postHorizontalScale() error {
+	if err := r.joinMember4ScaleOut(); err != nil {
+		return err
+	}
+	return nil
+}
+
+func (r *componentWorkloadOps) joinMember4ScaleOut() error {
+	pods, err := component.ListOwnedPods(r.transCtx.Context, r.cli,
+		r.synthesizeComp.Namespace, r.synthesizeComp.ClusterName, r.synthesizeComp.Name)
+	if err != nil {
+		return err
+	}
+
+	joinErrors := make([]error, 0)
+	if err = component.UpdateReplicasStatusFunc(r.protoITS, func(replicas *component.ReplicasStatus) error {
+		for _, pod := range pods {
+			i := slices.IndexFunc(replicas.Status, func(r component.ReplicaStatus) bool {
+				return r.Name == pod.Name
+			})
+			if i < 0 {
+				continue // the pod is not in the replicas status?
+			}
+
+			status := replicas.Status[i]
+			if status.MemberJoined == nil || *status.MemberJoined {
+				continue // no need to join or already joined
+			}
+
+			// TODO: should wait for the data to be loaded before joining the member?
+
+			if err := r.joinMemberForPod(pod, pods); err != nil {
+				joinErrors = append(joinErrors, fmt.Errorf("pod %s: %w", pod.Name, err))
+			} else {
+				replicas.Status[i].MemberJoined = ptr.To(true)
+			}
+		}
+
+		notJoinedReplicas := make([]string, 0)
+		for _, r := range replicas.Status {
+			if r.MemberJoined != nil && !*r.MemberJoined {
+				notJoinedReplicas = append(notJoinedReplicas, r.Name)
+			}
+		}
+		if len(notJoinedReplicas) > 0 {
+			joinErrors = append(joinErrors, fmt.Errorf("some replicas have not joined: %v", notJoinedReplicas))
+		}
+		return nil
+	}); err != nil {
+		return err
+	}
+
+	if len(joinErrors) > 0 {
+		return intctrlutil.NewRequeueError(time.Second, fmt.Sprintf("%v", joinErrors))
+	}
+	return nil
+}
+
+func (r *componentWorkloadOps) joinMemberForPod(pod *corev1.Pod, pods []*corev1.Pod) error {
+	synthesizedComp := r.synthesizeComp
+	lfa, err := lifecycle.New(synthesizedComp.Namespace, synthesizedComp.ClusterName, synthesizedComp.Name,
+		synthesizedComp.LifecycleActions, synthesizedComp.TemplateVars, pod, pods...)
+	if err != nil {
+		return err
+	}
+	if err = lfa.MemberJoin(r.transCtx.Context, r.cli, nil); err != nil {
+		if !errors.Is(err, lifecycle.ErrActionNotDefined) {
+			return err
+		}
+	}
+	r.transCtx.Logger.Info("succeed to join member for pod", "pod", pod.Name)
+	return nil
+}
+
+func (r *componentWorkloadOps) expandVolume() error {
+	for _, vct := range r.runningITS.Spec.VolumeClaimTemplates {
+		var proto *corev1.PersistentVolumeClaimTemplate
+		for i, v := range r.synthesizeComp.VolumeClaimTemplates {
+			if v.Name == vct.Name {
+				proto = &r.synthesizeComp.VolumeClaimTemplates[i]
+				break
+			}
+		}
+		// REVIEW: seems we can remove a volume claim from templates at runtime, without any changes and warning messages?
+		if proto == nil {
+			continue
+		}
+		if err := r.expandVolumes(vct.Name, proto); err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
+func (r *componentWorkloadOps) expandVolumes(vctName string, proto *corev1.PersistentVolumeClaimTemplate) error {
+	for _, pod := range r.runningItsPodNames {
+		pvc := &corev1.PersistentVolumeClaim{}
+		pvcKey := types.NamespacedName{
+			Namespace: r.synthesizeComp.Namespace,
+			Name:      fmt.Sprintf("%s-%s", vctName, pod),
+		}
+		pvcNotFound := false
+		if err := r.cli.Get(r.transCtx.Context, pvcKey, pvc, appsutil.InDataContext4C()); err != nil {
+			if apierrors.IsNotFound(err) {
+				pvcNotFound = true
+			} else {
+				return err
+			}
+		}
+		if !pvcNotFound {
+			quantity := pvc.Spec.Resources.Requests.Storage()
+			newQuantity := proto.Spec.Resources.Requests.Storage()
+			if quantity.Cmp(*pvc.Status.Capacity.Storage()) == 0 && newQuantity.Cmp(*quantity) < 0 {
+				errMsg := fmt.Sprintf("shrinking the volume is not supported, volume: %s, quantity: %s, new quantity: %s",
+					pvc.GetName(), quantity.String(), newQuantity.String())
+				r.transCtx.Event(r.component, corev1.EventTypeWarning, "VolumeExpansionFailed", errMsg)
+				return fmt.Errorf("%s", errMsg)
+			}
+		}
+
+		if err := r.updatePVCSize(pvcKey, pvc, pvcNotFound, proto); err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
+func (r *componentWorkloadOps) updatePVCSize(pvcKey types.NamespacedName,
+	pvc *corev1.PersistentVolumeClaim, pvcNotFound bool, vctProto *corev1.PersistentVolumeClaimTemplate) error {
+	// reference: https://kubernetes.io/docs/concepts/storage/persistent-volumes/#recovering-from-failure-when-expanding-volumes
+	// 1. Mark the PersistentVolume(PV) that is bound to the PersistentVolumeClaim(PVC) with Retain reclaim policy.
+	// 2. Delete the PVC. Since PV has Retain reclaim policy - we will not lose any data when we recreate the PVC.
+	// 3. Delete the claimRef entry from PV specs, so as new PVC can bind to it. This should make the PV Available.
+	// 4. Re-create the PVC with smaller size than PV and set volumeName field of the PVC to the name of the PV. This should bind new PVC to existing PV.
+	// 5. Don't forget to restore the reclaim policy of the PV.
+	newPVC := pvc.DeepCopy()
+	if pvcNotFound {
+		newPVC.Name = pvcKey.Name
+		newPVC.Namespace = pvcKey.Namespace
+		newPVC.SetLabels(vctProto.Labels)
+		newPVC.Spec = vctProto.Spec
+		ml := client.MatchingLabels{
+			constant.PVCNameLabelKey: pvcKey.Name,
+		}
+		pvList := corev1.PersistentVolumeList{}
+		if err := r.cli.List(r.transCtx.Context, &pvList, ml, appsutil.InDataContext4C()); err != nil {
+			return err
+		}
+		for _, pv := range pvList.Items {
+			// find pv referenced this pvc
+			if pv.Spec.ClaimRef == nil {
+				continue
+			}
+			if pv.Spec.ClaimRef.Name == pvcKey.Name {
+				newPVC.Spec.VolumeName = pv.Name
+				break
+			}
+		}
+	} else {
+		newPVC.Spec.Resources.Requests[corev1.ResourceStorage] = vctProto.Spec.Resources.Requests[corev1.ResourceStorage]
+		// delete annotation to make it re-bind
+		delete(newPVC.Annotations, "pv.kubernetes.io/bind-completed")
+	}
+
+	pvNotFound := false
+
+	// step 1: update pv to retain
+	pv := &corev1.PersistentVolume{}
+	if len(newPVC.Spec.VolumeName) == 0 {
+		// the PV may be under provisioning
+		pvNotFound = true
+	} else {
+		pvKey := types.NamespacedName{
+			Namespace: pvcKey.Namespace,
+			Name:      newPVC.Spec.VolumeName,
+		}
+		if err := r.cli.Get(r.transCtx.Context, pvKey, pv, appsutil.InDataContext4C()); err != nil {
+			if apierrors.IsNotFound(err) {
+				pvNotFound = true
+			} else {
+				return err
+			}
+		}
+	}
+
+	graphCli := model.NewGraphClient(r.cli)
+
+	type pvcRecreateStep int
+	const (
+		pvPolicyRetainStep pvcRecreateStep = iota
+		deletePVCStep
+		removePVClaimRefStep
+		createPVCStep
+		pvRestorePolicyStep
+	)
+
+	addStepMap := map[pvcRecreateStep]func(fromVertex *model.ObjectVertex, step pvcRecreateStep) *model.ObjectVertex{
+		pvPolicyRetainStep: func(fromVertex *model.ObjectVertex, step pvcRecreateStep) *model.ObjectVertex {
+			// step 1: update pv to retain
+			retainPV := pv.DeepCopy()
+			if retainPV.Labels == nil {
+				retainPV.Labels = make(map[string]string)
+			}
+			// add label to pv, in case pvc get deleted, and we can't find pv
+			retainPV.Labels[constant.PVCNameLabelKey] = pvcKey.Name
+			if retainPV.Annotations == nil {
+				retainPV.Annotations = make(map[string]string)
+			}
+			retainPV.Annotations[constant.PVLastClaimPolicyAnnotationKey] = string(pv.Spec.PersistentVolumeReclaimPolicy)
+			retainPV.Spec.PersistentVolumeReclaimPolicy = corev1.PersistentVolumeReclaimRetain
+			return graphCli.Do(r.dag, pv, retainPV, model.ActionPatchPtr(), fromVertex, appsutil.InDataContext4G())
+		},
+		deletePVCStep: func(fromVertex *model.ObjectVertex, step pvcRecreateStep) *model.ObjectVertex {
+			// step 2: delete pvc, this will not delete pv because policy is 'retain'
+			removeFinalizerPVC := pvc.DeepCopy()
+			removeFinalizerPVC.SetFinalizers([]string{})
+			removeFinalizerPVCVertex := graphCli.Do(r.dag, pvc, removeFinalizerPVC, model.ActionPatchPtr(), fromVertex, appsutil.InDataContext4G())
+			return graphCli.Do(r.dag, nil, removeFinalizerPVC, model.ActionDeletePtr(), removeFinalizerPVCVertex, appsutil.InDataContext4G())
+		},
+		removePVClaimRefStep: func(fromVertex *model.ObjectVertex, step pvcRecreateStep) *model.ObjectVertex {
+			// step 3: remove claimRef in pv
+			removeClaimRefPV := pv.DeepCopy()
+			if removeClaimRefPV.Spec.ClaimRef != nil {
+				removeClaimRefPV.Spec.ClaimRef.UID = ""
+				removeClaimRefPV.Spec.ClaimRef.ResourceVersion = ""
+			}
+			return graphCli.Do(r.dag, pv, removeClaimRefPV, model.ActionPatchPtr(), fromVertex, appsutil.InDataContext4G())
+		},
+		createPVCStep: func(fromVertex *model.ObjectVertex, step pvcRecreateStep) *model.ObjectVertex {
+			// step 4: create new pvc
+			newPVC.SetResourceVersion("")
+			return graphCli.Do(r.dag, nil, newPVC, model.ActionCreatePtr(), fromVertex, appsutil.InDataContext4G())
+		},
+		pvRestorePolicyStep: func(fromVertex *model.ObjectVertex, step pvcRecreateStep) *model.ObjectVertex {
+			// step 5: restore to previous pv policy
+			restorePV := pv.DeepCopy()
+			policy := corev1.PersistentVolumeReclaimPolicy(restorePV.Annotations[constant.PVLastClaimPolicyAnnotationKey])
+			if len(policy) == 0 {
+				policy = corev1.PersistentVolumeReclaimDelete
+			}
+			restorePV.Spec.PersistentVolumeReclaimPolicy = policy
+			return graphCli.Do(r.dag, pv, restorePV, model.ActionPatchPtr(), fromVertex, appsutil.InDataContext4G())
+		},
+	}
+
+	updatePVCByRecreateFromStep := func(fromStep pvcRecreateStep) {
+		lastVertex := r.buildProtoITSWorkloadVertex()
+		// The steps here are decremented in reverse order because during the plan execution, dag.WalkReverseTopoOrder
+		// is called to execute all vertices on the graph according to the reverse topological order.
+		// Therefore, the vertices need to maintain the following edge linkages:
+		// root -> its -> step5 -> step4 -> step3 -> step2 -> step1
+		// So that, during execution, the sequence becomes step1 -> step2 -> step3 -> step4 -> step5
+		for step := pvRestorePolicyStep; step >= fromStep && step >= pvPolicyRetainStep; step-- {
+			lastVertex = addStepMap[step](lastVertex, step)
+		}
+	}
+
+	targetQuantity := vctProto.Spec.Resources.Requests[corev1.ResourceStorage]
+	if pvcNotFound && !pvNotFound {
+		// this could happen if create pvc step failed when recreating pvc
+		updatePVCByRecreateFromStep(removePVClaimRefStep)
+		return nil
+	}
+	if pvcNotFound && pvNotFound {
+		// if both pvc and pv not found, do nothing
+		return nil
+	}
+	if equality.Semantic.DeepEqual(pvc.Spec.Resources, newPVC.Spec.Resources) &&
+		pv.Spec.PersistentVolumeReclaimPolicy == corev1.PersistentVolumeReclaimRetain &&
+		pv.Annotations != nil &&
+		len(pv.Annotations[constant.PVLastClaimPolicyAnnotationKey]) > 0 &&
+		pv.Annotations[constant.PVLastClaimPolicyAnnotationKey] != string(corev1.PersistentVolumeReclaimRetain) {
+		// this could happen if create pvc succeeded but last step failed
+		updatePVCByRecreateFromStep(pvRestorePolicyStep)
+		return nil
+	}
+	if pvcQuantity := pvc.Spec.Resources.Requests[corev1.ResourceStorage]; !viper.GetBool(constant.CfgRecoverVolumeExpansionFailure) &&
+		pvcQuantity.Cmp(targetQuantity) == 1 && // check if it's compressing volume
+		targetQuantity.Cmp(*pvc.Status.Capacity.Storage()) >= 0 { // check if target size is greater than or equal to actual size
+		// this branch means we can update pvc size by recreate it
+		updatePVCByRecreateFromStep(pvPolicyRetainStep)
+		return nil
+	}
+	if pvcQuantity := pvc.Spec.Resources.Requests[corev1.ResourceStorage]; pvcQuantity.Cmp(vctProto.Spec.Resources.Requests[corev1.ResourceStorage]) != 0 {
+		// use pvc's update without anything extra
+		graphCli.Update(r.dag, nil, newPVC, appsutil.InDataContext4G())
+		return nil
+	}
+	// all the else means no need to update
+
+	return nil
+}
+
+func (r *componentWorkloadOps) buildProtoITSWorkloadVertex() *model.ObjectVertex {
+	for _, vertex := range r.dag.Vertices() {
+		v, _ := vertex.(*model.ObjectVertex)
+		if v.Obj == r.protoITS {
+			return v
+		}
+	}
+	return nil
+}
+
+func (r *componentWorkloadOps) reconfigure() error {
+	runningObjs, err := getFileTemplateObjects(r.transCtx)
+	if err != nil {
+		return err
+	}
+
+	protoObjs, err := buildFileTemplateObjects(r.transCtx)
+	if err != nil {
+		return err
+	}
+
+	toCreate, toDelete, toUpdate := mapDiff(runningObjs, protoObjs)
+
+	return r.handleReconfigure(r.transCtx, runningObjs, protoObjs, toCreate, toDelete, toUpdate)
+}
+
+func (r *componentWorkloadOps) handleReconfigure(transCtx *componentTransformContext,
+	runningObjs, protoObjs map[string]*corev1.ConfigMap, toCreate, toDelete, toUpdate sets.Set[string]) error {
+	var (
+		synthesizedComp = transCtx.SynthesizeComponent
+	)
+
+	if r.runningITS == nil {
+		r.protoITS.Spec.Configs = nil
+		return nil // the workload hasn't been provisioned
+	}
+
+	if len(toCreate) > 0 || len(toDelete) > 0 {
+		// since pod volumes changed, the workload will be restarted
+		r.protoITS.Spec.Configs = nil
+		return nil
+	}
+
+	reconfigure := func(tpl component.SynthesizedFileTemplate, changes fileTemplateChanges) {
+		var (
+			action     *appsv1.Action
+			actionName string
+		)
+		if tpl.ExternalManaged != nil && *tpl.ExternalManaged {
+			if tpl.Reconfigure == nil {
+				return // disabled by the external system
+			}
+		}
+		action = tpl.Reconfigure
+		actionName = component.UDFReconfigureActionName(tpl)
+		if action == nil && synthesizedComp.LifecycleActions != nil {
+			action = synthesizedComp.LifecycleActions.Reconfigure
+			actionName = "" // default reconfigure action
+		}
+		if action == nil {
+			return // has no reconfigure action defined
+		}
+
+		config := workloads.ConfigTemplate{
+			Name:                  tpl.Name,
+			Generation:            r.component.Generation,
+			Reconfigure:           action,
+			ReconfigureActionName: actionName,
+			Parameters:            lifecycle.FileTemplateChanges(changes.Created, changes.Removed, changes.Updated),
+		}
+		if r.protoITS.Spec.Configs == nil {
+			r.protoITS.Spec.Configs = make([]workloads.ConfigTemplate, 0)
+		}
+		idx := slices.IndexFunc(r.protoITS.Spec.Configs, func(cfg workloads.ConfigTemplate) bool {
+			return cfg.Name == tpl.Name
+		})
+		if idx >= 0 {
+			r.protoITS.Spec.Configs[idx] = config
+		} else {
+			r.protoITS.Spec.Configs = append(r.protoITS.Spec.Configs, config)
+		}
+	}
+
+	// make a copy of configs from the running ITS
+	r.protoITS.Spec.Configs = slices.Clone(r.runningITS.Spec.Configs)
+
+	templateChanges := r.templateFileChanges(transCtx, runningObjs, protoObjs, toUpdate)
+	for _, tpl := range synthesizedComp.FileTemplates {
+		if changes, ok := templateChanges[tpl.Name]; ok {
+			reconfigure(tpl, changes)
+		}
+	}
+	return nil
+}
+
+func (r *componentWorkloadOps) templateFileChanges(transCtx *componentTransformContext,
+	runningObjs, protoObjs map[string]*corev1.ConfigMap, update sets.Set[string]) map[string]fileTemplateChanges {
+	diff := func(obj *corev1.ConfigMap, rData, pData map[string]string) fileTemplateChanges {
+		var (
+			tplName = fileTemplateNameFromObject(transCtx.SynthesizeComponent, obj)
+			items   = make([][]string, 3)
+		)
+
+		toAdd, toDelete, toUpdate := mapDiff(rData, pData)
+
+		items[0], items[1] = sets.List(toAdd), sets.List(toDelete)
+		for item := range toUpdate {
+			if !reflect.DeepEqual(rData[item], pData[item]) {
+				absPath := r.absoluteFilePath(transCtx, tplName, item)
+				if len(absPath) > 0 {
+					checksum := sha256.Sum256([]byte(pData[item]))
+					items[2] = append(items[2], fmt.Sprintf("%s:%x", absPath, checksum))
+				}
+			}
+		}
+
+		for i := range items {
+			slices.Sort(items[i])
+		}
+
+		return fileTemplateChanges{
+			Created: strings.Join(items[0], ","),
+			Removed: strings.Join(items[1], ","),
+			Updated: strings.Join(items[2], ","),
+		}
+	}
+
+	result := make(map[string]fileTemplateChanges)
+	for name := range update {
+		rData, pData := runningObjs[name].Data, protoObjs[name].Data
+		if !reflect.DeepEqual(rData, pData) {
+			tplName := fileTemplateNameFromObject(transCtx.SynthesizeComponent, runningObjs[name])
+			result[tplName] = diff(runningObjs[name], rData, pData)
+		}
+	}
+	return result
+}
+
+func (r *componentWorkloadOps) absoluteFilePath(transCtx *componentTransformContext, tpl, file string) string {
+	var (
+		synthesizedComp = transCtx.SynthesizeComponent
+	)
+
+	var volName, mountPath string
+	for _, fileTpl := range synthesizedComp.FileTemplates {
+		if fileTpl.Name == tpl {
+			volName = fileTpl.VolumeName
+			break
+		}
+	}
+	if volName == "" {
+		return "" // has no volumes specified
+	}
+
+	for _, container := range synthesizedComp.PodSpec.Containers {
+		for _, mount := range container.VolumeMounts {
+			if mount.Name == volName {
+				mountPath = mount.MountPath
+				break
+			}
+		}
+		if mountPath != "" {
+			break
+		}
+	}
+	if mountPath == "" {
+		return "" // the template is not mounted, ignore it
+	}
+
+	return filepath.Join(mountPath, file)
+}
+
+type fileTemplateChanges struct {
+	Created string `json:"created,omitempty"`
+	Removed string `json:"removed,omitempty"`
+	Updated string `json:"updated,omitempty"`
+}
diff --git a/controllers/apps/componentdefinition_controller.go b/controllers/apps/componentdefinition_controller.go
index 1b8995de1..6fd9f6019 100644
--- a/controllers/apps/componentdefinition_controller.go
+++ b/controllers/apps/componentdefinition_controller.go
@@ -191,7 +191,7 @@ func (r *ComponentDefinitionReconciler) validate(cli client.Client, rctx intctrl
 		r.validateVolumes,
 		r.validateHostNetwork,
 		r.validateServices,
-		r.validateConfigs,
+		r.validateConfigNScript,
 		r.validateSystemAccounts,
 		r.validateReplicasLimit,
 		r.validateAvailable,
@@ -331,9 +331,55 @@ func (r *ComponentDefinitionReconciler) validateServices(cli client.Client, rctx
 	return nil
 }
 
-func (r *ComponentDefinitionReconciler) validateConfigs(cli client.Client, rctx intctrlutil.RequestCtx,
+func (r *ComponentDefinitionReconciler) validateConfigNScript(cli client.Client, rctx intctrlutil.RequestCtx,
 	compDef *appsv1.ComponentDefinition) error {
-	return validateComponentTemplate(cli, rctx, compDef)
+	if err := r.validateComponentTemplate(cli, rctx, compDef); err != nil {
+		return err
+	}
+	return validateComponentFileTemplate(cli, rctx, compDef)
+}
+
+func (r *ComponentDefinitionReconciler) validateComponentTemplate(cli client.Client, rctx intctrlutil.RequestCtx,
+	cmpd *appsv1.ComponentDefinition) error {
+	validateObject := func(objectKey client.ObjectKey) error {
+		configObj := &corev1.ConfigMap{}
+		return cli.Get(rctx.Ctx, objectKey, configObj)
+	}
+	validateTemplate := func(tpl appsv1.ComponentTemplateSpec) error {
+		if tpl.TemplateRef != "" {
+			return validateObject(client.ObjectKey{Namespace: tpl.Namespace, Name: tpl.TemplateRef})
+		}
+		return nil
+	}
+	for _, tpls := range [][]appsv1.ComponentTemplateSpec{cmpd.Spec.Configs} {
+		for _, tpl := range tpls {
+			if err := validateTemplate(tpl); err != nil {
+				return err
+			}
+		}
+	}
+	return nil
+}
+
+func validateComponentFileTemplate(cli client.Client, rctx intctrlutil.RequestCtx, cmpd *appsv1.ComponentDefinition) error {
+	validateObject := func(objectKey client.ObjectKey) error {
+		configObj := &corev1.ConfigMap{}
+		return cli.Get(rctx.Ctx, objectKey, configObj)
+	}
+	validateTemplate := func(tpl appsv1.ComponentFileTemplate) error {
+		if tpl.Template != "" {
+			return validateObject(client.ObjectKey{Namespace: tpl.Namespace, Name: tpl.Template})
+		}
+		return nil
+	}
+	for _, tpls := range [][]appsv1.ComponentFileTemplate{cmpd.Spec.Configs2, cmpd.Spec.Scripts} {
+		for _, tpl := range tpls {
+			if err := validateTemplate(tpl); err != nil {
+				return err
+			}
+		}
+	}
+	return nil
 }
 
 func (r *ComponentDefinitionReconciler) validateSystemAccounts(cli client.Client, rctx intctrlutil.RequestCtx,
diff --git a/controllers/apps/sidecardefinition_controller.go b/controllers/apps/sidecardefinition_controller.go
index 55f0aba90..2648b2822 100644
--- a/controllers/apps/sidecardefinition_controller.go
+++ b/controllers/apps/sidecardefinition_controller.go
@@ -233,11 +233,11 @@ func (r *SidecarDefinitionReconciler) validateConfigNScript(cli client.Client, r
 
 	compDef := &appsv1.ComponentDefinition{
 		Spec: appsv1.ComponentDefinitionSpec{
-			Configs: sidecarDef.Spec.Configs,
-			Scripts: sidecarDef.Spec.Scripts,
+			Configs2: sidecarDef.Spec.Configs,
+			Scripts:  sidecarDef.Spec.Scripts,
 		},
 	}
-	return validateComponentTemplate(cli, rctx, compDef)
+	return validateComponentFileTemplate(cli, rctx, compDef)
 }
 
 func (r *SidecarDefinitionReconciler) validateNResolveOwner(_ client.Client, _ intctrlutil.RequestCtx,
@@ -314,7 +314,22 @@ func (r *SidecarDefinitionReconciler) validateMatchedCompDef(sidecarDef *appsv1.
 	}
 
 	templates := func() error {
-		validate := func(key string, sidecar, comp []appsv1.ComponentTemplateSpec) error {
+		validate := func(key string, sidecar []appsv1.ComponentFileTemplate, comp []appsv1.ComponentTemplateSpec) error {
+			if len(sidecar) == 0 || len(comp) == 0 {
+				return nil
+			}
+			names := sets.New[string]()
+			for _, v := range comp {
+				names.Insert(v.Name)
+			}
+			for _, v := range sidecar {
+				if names.Has(v.Name) {
+					return fmt.Errorf("%s template %s is conflicted with the component definition %s", key, v.Name, compDef.Name)
+				}
+			}
+			return nil
+		}
+		validate2 := func(key string, sidecar, comp []appsv1.ComponentFileTemplate) error {
 			if len(sidecar) == 0 || len(comp) == 0 {
 				return nil
 			}
@@ -329,11 +344,10 @@ func (r *SidecarDefinitionReconciler) validateMatchedCompDef(sidecarDef *appsv1.
 			}
 			return nil
 		}
-
 		if err := validate("config", sidecarDef.Spec.Configs, compDef.Spec.Configs); err != nil {
 			return err
 		}
-		return validate("script", sidecarDef.Spec.Scripts, compDef.Spec.Scripts)
+		return validate2("script", sidecarDef.Spec.Scripts, compDef.Spec.Scripts)
 	}
 	for _, f := range []func() error{vars, templates} {
 		if err := f(); err != nil {
diff --git a/controllers/apps/util/utils.go b/controllers/apps/util/utils.go
index 388043b89..71fc23614 100644
--- a/controllers/apps/util/utils.go
+++ b/controllers/apps/util/utils.go
@@ -20,6 +20,9 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 package util
 
 import (
+	"context"
+	"encoding/json"
+	"fmt"
 	"reflect"
 	"time"
 
@@ -27,8 +30,11 @@ import (
 	"k8s.io/client-go/tools/record"
 	"sigs.k8s.io/controller-runtime/pkg/client"
 
+	dpv1alpha1 "github.com/apecloud/kubeblocks/apis/dataprotection/v1alpha1"
 	workloads "github.com/apecloud/kubeblocks/apis/workloads/v1"
+	"github.com/apecloud/kubeblocks/pkg/constant"
 	intctrlutil "github.com/apecloud/kubeblocks/pkg/controllerutil"
+	viper "github.com/apecloud/kubeblocks/pkg/viperx"
 )
 
 var RequeueDuration = time.Millisecond * 1000
@@ -121,3 +127,59 @@ func IsOwnedByInstanceSet(obj client.Object) bool {
 	}
 	return false
 }
+
+func GetRestoreSystemAccountPassword(
+	ctx context.Context,
+	cli client.Reader,
+	annotations map[string]string,
+	componentName,
+	accountName string,
+) ([]byte, error) {
+	valueString := annotations[constant.RestoreFromBackupAnnotationKey]
+	if len(valueString) == 0 {
+		return nil, nil
+	}
+	backupMap := map[string]map[string]string{}
+	err := json.Unmarshal([]byte(valueString), &backupMap)
+	if err != nil {
+		return nil, err
+	}
+	backupSource, ok := backupMap[componentName]
+	if !ok {
+		return nil, nil
+	}
+	name, ok := backupSource[constant.BackupNameKeyForRestore]
+	if !ok || len(name) == 0 {
+		return nil, fmt.Errorf("backup name not found in restore annotation")
+	}
+	namespace, ok := backupSource[constant.BackupNamespaceKeyForRestore]
+	if !ok || len(namespace) == 0 {
+		return nil, fmt.Errorf("backup namespace not found in restore annotation")
+	}
+	backup := &dpv1alpha1.Backup{}
+	if err := cli.Get(ctx, client.ObjectKey{
+		Name:      name,
+		Namespace: namespace,
+	}, backup); err != nil {
+		return nil, err
+	}
+	systemAccountsMap := map[string]string{}
+	encryptedSystemAccountsString := backup.Annotations[constant.EncryptedSystemAccountsAnnotationKey]
+	if encryptedSystemAccountsString != "" {
+		encryptedSystemAccountsMap := map[string]map[string]string{}
+		if err = json.Unmarshal([]byte(encryptedSystemAccountsString), &encryptedSystemAccountsMap); err != nil {
+			return nil, err
+		}
+		if val, ok := encryptedSystemAccountsMap[componentName]; ok {
+			systemAccountsMap = val
+		}
+	}
+
+	e := intctrlutil.NewEncryptor(viper.GetString(constant.CfgKeyDPEncryptionKey))
+	encryptedPwd, ok := systemAccountsMap[accountName]
+	if !ok {
+		return nil, nil
+	}
+	password, err := e.Decrypt([]byte(encryptedPwd))
+	return []byte(password), err
+}
diff --git a/controllers/apps/utils.go b/controllers/apps/utils.go
deleted file mode 100644
index eb3584f41..000000000
--- a/controllers/apps/utils.go
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
-Copyright (C) 2022-2025 ApeCloud Co., Ltd
-
-This file is part of KubeBlocks project
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU Affero General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU Affero General Public License for more details.
-
-You should have received a copy of the GNU Affero General Public License
-along with this program.  If not, see <http://www.gnu.org/licenses/>.
-*/
-
-package apps
-
-import (
-	corev1 "k8s.io/api/core/v1"
-	"sigs.k8s.io/controller-runtime/pkg/client"
-
-	appsv1 "github.com/apecloud/kubeblocks/apis/apps/v1"
-	intctrlutil "github.com/apecloud/kubeblocks/pkg/controllerutil"
-)
-
-func validateComponentTemplate(cli client.Client, rctx intctrlutil.RequestCtx, compd *appsv1.ComponentDefinition) error {
-	validateObject := func(objectKey client.ObjectKey) error {
-		configObj := &corev1.ConfigMap{}
-		return cli.Get(rctx.Ctx, objectKey, configObj)
-	}
-	validateTemplate := func(tpl appsv1.ComponentTemplateSpec) error {
-		if tpl.TemplateRef != "" {
-			return validateObject(client.ObjectKey{Namespace: tpl.Namespace, Name: tpl.TemplateRef})
-		}
-		return nil
-	}
-	for _, tpls := range [][]appsv1.ComponentTemplateSpec{compd.Spec.Configs, compd.Spec.Scripts} {
-		for _, tpl := range tpls {
-			if err := validateTemplate(tpl); err != nil {
-				return err
-			}
-		}
-	}
-	return nil
-}
diff --git a/controllers/parameters/parameter_util.go b/controllers/parameters/parameter_util.go
index 859e8554a..1aa29eac4 100644
--- a/controllers/parameters/parameter_util.go
+++ b/controllers/parameters/parameter_util.go
@@ -71,7 +71,7 @@ func syncReconfiguringPhase(rctx *ReconcileContext, status *parametersv1alpha1.C
 			rctx.Log.Info("component status or spec not found", "component", parameterStatus.Name, "template", parameterStatus.Name)
 			continue
 		}
-		parameterStatus.Phase = intctrlutil.GetConfigSpecReconcilePhase(cm, *compSpec, compStatus)
+		parameterStatus.Phase = intctrlutil.GetUpdatedParametersReconciledPhase(cm, *compSpec, compStatus)
 		if finished {
 			finished = intctrlutil.IsParameterFinished(parameterStatus.Phase)
 		}
diff --git a/controllers/parameters/reconcile_task.go b/controllers/parameters/reconcile_task.go
index 67ae16fa6..a3803b1be 100644
--- a/controllers/parameters/reconcile_task.go
+++ b/controllers/parameters/reconcile_task.go
@@ -117,7 +117,7 @@ func NewTask(item parametersv1alpha1.ConfigTemplateItemDetail, status *parameter
 			}
 			// Do reconcile for config template
 			configMap := resource.ConfigMapObj
-			switch intctrlutil.GetConfigSpecReconcilePhase(configMap, item, status) {
+			switch intctrlutil.GetUpdatedParametersReconciledPhase(configMap, item, status) {
 			default:
 				return syncStatus(configMap, status)
 			case parametersv1alpha1.CInitPhase,
@@ -138,7 +138,7 @@ func syncImpl(taskCtx *TaskContext,
 	status *parametersv1alpha1.ConfigTemplateItemDetailStatus,
 	revision string,
 	configMap *corev1.ConfigMap) (err error) {
-	if intctrlutil.IsApplyConfigChanged(configMap, item) {
+	if intctrlutil.IsApplyUpdatedParameters(configMap, item) {
 		return syncStatus(configMap, status)
 	}
 
diff --git a/deploy/helm/crds/apps.kubeblocks.io_componentdefinitions.yaml b/deploy/helm/crds/apps.kubeblocks.io_componentdefinitions.yaml
index ef1cbc513..ff2b97d2b 100644
--- a/deploy/helm/crds/apps.kubeblocks.io_componentdefinitions.yaml
+++ b/deploy/helm/crds/apps.kubeblocks.io_componentdefinitions.yaml
@@ -15954,76 +15954,6 @@ spec:
                   These scripts can be executed during container startup or via specific actions.
 
 
-                  Each script group is encapsulated in a ComponentTemplateSpec that includes:
-
-
-                  - The ConfigMap containing the scripts.
-                  - The mount point where the scripts will be mounted inside the container.
-
-
-                  This field is immutable.
-                items:
-                  properties:
-                    defaultMode:
-                      description: |-
-                        The operator attempts to set default file permissions for scripts (0555) and configurations (0444).
-                        However, certain database engines may require different file permissions.
-                        You can specify the desired file permissions here.
-
-
-                        Must be specified as an octal value between 0000 and 0777 (inclusive),
-                        or as a decimal value between 0 and 511 (inclusive).
-                        YAML supports both octal and decimal values for file permissions.
-
-
-                        Please note that this setting only affects the permissions of the files themselves.
-                        Directories within the specified path are not impacted by this setting.
-                        It's important to be aware that this setting might conflict with other options
-                        that influence the file mode, such as fsGroup.
-                        In such cases, the resulting file mode may have additional bits set.
-                        Refers to documents of k8s.ConfigMapVolumeSource.defaultMode for more information.
-                      format: int32
-                      type: integer
-                    name:
-                      description: Specifies the name of the configuration template.
-                      maxLength: 63
-                      pattern: ^[a-z0-9]([a-z0-9\.\-]*[a-z0-9])?$
-                      type: string
-                    namespace:
-                      default: default
-                      description: |-
-                        Specifies the namespace of the referenced configuration template ConfigMap object.
-                        An empty namespace is equivalent to the "default" namespace.
-                      maxLength: 63
-                      pattern: ^[a-z0-9]([a-z0-9\-]*[a-z0-9])?$
-                      type: string
-                    templateRef:
-                      description: Specifies the name of the referenced configuration
-                        template ConfigMap object.
-                      maxLength: 63
-                      pattern: ^[a-z0-9]([a-z0-9\.\-]*[a-z0-9])?$
-                      type: string
-                    volumeName:
-                      description: |-
-                        Refers to the volume name of PodTemplate. The configuration file produced through the configuration
-                        template will be mounted to the corresponding volume. Must be a DNS_LABEL name.
-                        The volume name must be defined in podSpec.containers[*].volumeMounts.
-                      maxLength: 63
-                      pattern: ^[a-z]([a-z0-9\-]*[a-z0-9])?$
-                      type: string
-                  required:
-                  - name
-                  type: object
-                type: array
-                x-kubernetes-list-map-keys:
-                - name
-                x-kubernetes-list-type: map
-              scripts2:
-                description: |-
-                  Specifies groups of scripts, each provided via a ConfigMap, to be mounted as volumes in the container.
-                  These scripts can be executed during container startup or via specific actions.
-
-
                   This field is immutable.
                 items:
                   properties:
diff --git a/deploy/helm/crds/apps.kubeblocks.io_sidecardefinitions.yaml b/deploy/helm/crds/apps.kubeblocks.io_sidecardefinitions.yaml
index 0fd9c030c..b91eac451 100644
--- a/deploy/helm/crds/apps.kubeblocks.io_sidecardefinitions.yaml
+++ b/deploy/helm/crds/apps.kubeblocks.io_sidecardefinitions.yaml
@@ -70,9 +70,7 @@ spec:
                   properties:
                     defaultMode:
                       description: |-
-                        The operator attempts to set default file permissions for scripts (0555) and configurations (0444).
-                        However, certain database engines may require different file permissions.
-                        You can specify the desired file permissions here.
+                        The operator attempts to set default file permissions (0444).
 
 
                         Must be specified as an octal value between 0000 and 0777 (inclusive),
@@ -89,28 +87,27 @@ spec:
                       format: int32
                       type: integer
                     name:
-                      description: Specifies the name of the configuration template.
+                      description: Specifies the name of the template.
                       maxLength: 63
                       pattern: ^[a-z0-9]([a-z0-9\.\-]*[a-z0-9])?$
                       type: string
                     namespace:
                       default: default
-                      description: |-
-                        Specifies the namespace of the referenced configuration template ConfigMap object.
-                        An empty namespace is equivalent to the "default" namespace.
+                      description: Specifies the namespace of the referenced template
+                        ConfigMap object.
                       maxLength: 63
                       pattern: ^[a-z0-9]([a-z0-9\-]*[a-z0-9])?$
                       type: string
-                    templateRef:
-                      description: Specifies the name of the referenced configuration
-                        template ConfigMap object.
+                    template:
+                      description: Specifies the name of the referenced template ConfigMap
+                        object.
                       maxLength: 63
                       pattern: ^[a-z0-9]([a-z0-9\.\-]*[a-z0-9])?$
                       type: string
                     volumeName:
                       description: |-
-                        Refers to the volume name of PodTemplate. The configuration file produced through the configuration
-                        template will be mounted to the corresponding volume. Must be a DNS_LABEL name.
+                        Refers to the volume name of PodTemplate. The file produced through the template will be mounted to
+                        the corresponding volume. Must be a DNS_LABEL name.
                         The volume name must be defined in podSpec.containers[*].volumeMounts.
                       maxLength: 63
                       pattern: ^[a-z]([a-z0-9\-]*[a-z0-9])?$
@@ -119,9 +116,6 @@ spec:
                   - name
                   type: object
                 type: array
-                x-kubernetes-list-map-keys:
-                - name
-                x-kubernetes-list-type: map
               containers:
                 description: |-
                   List of containers for the sidecar.
@@ -1487,9 +1481,7 @@ spec:
                   properties:
                     defaultMode:
                       description: |-
-                        The operator attempts to set default file permissions for scripts (0555) and configurations (0444).
-                        However, certain database engines may require different file permissions.
-                        You can specify the desired file permissions here.
+                        The operator attempts to set default file permissions (0444).
 
 
                         Must be specified as an octal value between 0000 and 0777 (inclusive),
@@ -1506,28 +1498,27 @@ spec:
                       format: int32
                       type: integer
                     name:
-                      description: Specifies the name of the configuration template.
+                      description: Specifies the name of the template.
                       maxLength: 63
                       pattern: ^[a-z0-9]([a-z0-9\.\-]*[a-z0-9])?$
                       type: string
                     namespace:
                       default: default
-                      description: |-
-                        Specifies the namespace of the referenced configuration template ConfigMap object.
-                        An empty namespace is equivalent to the "default" namespace.
+                      description: Specifies the namespace of the referenced template
+                        ConfigMap object.
                       maxLength: 63
                       pattern: ^[a-z0-9]([a-z0-9\-]*[a-z0-9])?$
                       type: string
-                    templateRef:
-                      description: Specifies the name of the referenced configuration
-                        template ConfigMap object.
+                    template:
+                      description: Specifies the name of the referenced template ConfigMap
+                        object.
                       maxLength: 63
                       pattern: ^[a-z0-9]([a-z0-9\.\-]*[a-z0-9])?$
                       type: string
                     volumeName:
                       description: |-
-                        Refers to the volume name of PodTemplate. The configuration file produced through the configuration
-                        template will be mounted to the corresponding volume. Must be a DNS_LABEL name.
+                        Refers to the volume name of PodTemplate. The file produced through the template will be mounted to
+                        the corresponding volume. Must be a DNS_LABEL name.
                         The volume name must be defined in podSpec.containers[*].volumeMounts.
                       maxLength: 63
                       pattern: ^[a-z]([a-z0-9\-]*[a-z0-9])?$
@@ -1536,9 +1527,6 @@ spec:
                   - name
                   type: object
                 type: array
-                x-kubernetes-list-map-keys:
-                - name
-                x-kubernetes-list-type: map
               selectors:
                 description: |-
                   Specifies the component definition of components that the sidecar along with.
diff --git a/deploy/helm/crds/workloads.kubeblocks.io_instancesets.yaml b/deploy/helm/crds/workloads.kubeblocks.io_instancesets.yaml
index 89335739a..76a0984e0 100644
--- a/deploy/helm/crds/workloads.kubeblocks.io_instancesets.yaml
+++ b/deploy/helm/crds/workloads.kubeblocks.io_instancesets.yaml
@@ -65,6 +65,304 @@ spec:
             description: Defines the desired state of the state machine. It includes
               the configuration details for the state machine.
             properties:
+              configs:
+                description: Describe the configs to be reconfigured.
+                items:
+                  properties:
+                    generation:
+                      description: The generation of the config.
+                      format: int64
+                      type: integer
+                    name:
+                      description: The name of the config.
+                      type: string
+                    parameters:
+                      additionalProperties:
+                        type: string
+                      description: The parameters to call the reconfigure action.
+                      type: object
+                    reconfigure:
+                      description: The custom reconfigure action.
+                      properties:
+                        exec:
+                          description: |-
+                            Defines the command to run.
+
+
+                            This field cannot be updated.
+                          properties:
+                            args:
+                              description: Args represents the arguments that are
+                                passed to the `command` for execution.
+                              items:
+                                type: string
+                              type: array
+                            command:
+                              description: |-
+                                Specifies the command to be executed inside the container.
+                                The working directory for this command is the container's root directory('/').
+                                Commands are executed directly without a shell environment, meaning shell-specific syntax ('|', etc.) is not supported.
+                                If the shell is required, it must be explicitly invoked in the command.
+
+
+                                A successful execution is indicated by an exit status of 0; any non-zero status signifies a failure.
+                              items:
+                                type: string
+                              type: array
+                            container:
+                              description: |-
+                                Specifies the name of the container within the same pod whose resources will be shared with the action.
+                                This allows the action to utilize the specified container's resources without executing within it.
+
+
+                                The name must match one of the containers defined in `componentDefinition.spec.runtime`.
+
+
+                                The resources that can be shared are included:
+
+
+                                - volume mounts
+
+
+                                This field cannot be updated.
+                              type: string
+                            env:
+                              description: |-
+                                Represents a list of environment variables that will be injected into the container.
+                                These variables enable the container to adapt its behavior based on the environment it's running in.
+
+
+                                This field cannot be updated.
+                              items:
+                                description: EnvVar represents an environment variable
+                                  present in a Container.
+                                properties:
+                                  name:
+                                    description: Name of the environment variable.
+                                      Must be a C_IDENTIFIER.
+                                    type: string
+                                  value:
+                                    description: |-
+                                      Variable references $(VAR_NAME) are expanded
+                                      using the previously defined environment variables in the container and
+                                      any service environment variables. If a variable cannot be resolved,
+                                      the reference in the input string will be unchanged. Double $$ are reduced
+                                      to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e.
+                                      "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)".
+                                      Escaped references will never be expanded, regardless of whether the variable
+                                      exists or not.
+                                      Defaults to "".
+                                    type: string
+                                  valueFrom:
+                                    description: Source for the environment variable's
+                                      value. Cannot be used if value is not empty.
+                                    properties:
+                                      configMapKeyRef:
+                                        description: Selects a key of a ConfigMap.
+                                        properties:
+                                          key:
+                                            description: The key to select.
+                                            type: string
+                                          name:
+                                            description: |-
+                                              Name of the referent.
+                                              More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
+                                              TODO: Add other useful fields. apiVersion, kind, uid?
+                                            type: string
+                                          optional:
+                                            description: Specify whether the ConfigMap
+                                              or its key must be defined
+                                            type: boolean
+                                        required:
+                                        - key
+                                        type: object
+                                        x-kubernetes-map-type: atomic
+                                      fieldRef:
+                                        description: |-
+                                          Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['<KEY>']`, `metadata.annotations['<KEY>']`,
+                                          spec.nodeName, spec.serviceAccountName, status.hostIP, status.podIP, status.podIPs.
+                                        properties:
+                                          apiVersion:
+                                            description: Version of the schema the
+                                              FieldPath is written in terms of, defaults
+                                              to "v1".
+                                            type: string
+                                          fieldPath:
+                                            description: Path of the field to select
+                                              in the specified API version.
+                                            type: string
+                                        required:
+                                        - fieldPath
+                                        type: object
+                                        x-kubernetes-map-type: atomic
+                                      resourceFieldRef:
+                                        description: |-
+                                          Selects a resource of the container: only resources limits and requests
+                                          (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests.ephemeral-storage) are currently supported.
+                                        properties:
+                                          containerName:
+                                            description: 'Container name: required
+                                              for volumes, optional for env vars'
+                                            type: string
+                                          divisor:
+                                            anyOf:
+                                            - type: integer
+                                            - type: string
+                                            description: Specifies the output format
+                                              of the exposed resources, defaults to
+                                              "1"
+                                            pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+                                            x-kubernetes-int-or-string: true
+                                          resource:
+                                            description: 'Required: resource to select'
+                                            type: string
+                                        required:
+                                        - resource
+                                        type: object
+                                        x-kubernetes-map-type: atomic
+                                      secretKeyRef:
+                                        description: Selects a key of a secret in
+                                          the pod's namespace
+                                        properties:
+                                          key:
+                                            description: The key of the secret to
+                                              select from.  Must be a valid secret
+                                              key.
+                                            type: string
+                                          name:
+                                            description: |-
+                                              Name of the referent.
+                                              More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
+                                              TODO: Add other useful fields. apiVersion, kind, uid?
+                                            type: string
+                                          optional:
+                                            description: Specify whether the Secret
+                                              or its key must be defined
+                                            type: boolean
+                                        required:
+                                        - key
+                                        type: object
+                                        x-kubernetes-map-type: atomic
+                                    type: object
+                                required:
+                                - name
+                                type: object
+                              type: array
+                            image:
+                              description: |-
+                                Specifies the container image to be used for running the Action.
+
+
+                                When specified, a dedicated container will be created using this image to execute the Action.
+                                All actions with same image will share the same container.
+
+
+                                This field cannot be updated.
+                              type: string
+                            matchingKey:
+                              description: |-
+                                Used in conjunction with the `targetPodSelector` field to refine the selection of target pod(s) for Action execution.
+                                The impact of this field depends on the `targetPodSelector` value:
+
+
+                                - When `targetPodSelector` is set to `Any` or `All`, this field will be ignored.
+                                - When `targetPodSelector` is set to `Role`, only those replicas whose role matches the `matchingKey`
+                                  will be selected for the Action.
+
+
+                                This field cannot be updated.
+                              type: string
+                            targetPodSelector:
+                              description: |-
+                                Defines the criteria used to select the target Pod(s) for executing the Action.
+                                This is useful when there is no default target replica identified.
+                                It allows for precise control over which Pod(s) the Action should run in.
+
+
+                                If not specified, the Action will be executed in the pod where the Action is triggered, such as the pod
+                                to be removed or added; or a random pod if the Action is triggered at the component level, such as
+                                post-provision or pre-terminate of the component.
+
+
+                                This field cannot be updated.
+                              enum:
+                              - Any
+                              - All
+                              - Role
+                              - Ordinal
+                              type: string
+                          type: object
+                        preCondition:
+                          description: |-
+                            Specifies the state that the cluster must reach before the Action is executed.
+                            Currently, this is only applicable to the `postProvision` action.
+
+
+                            The conditions are as follows:
+
+
+                            - `Immediately`: Executed right after the Component object is created.
+                              The readiness of the Component and its resources is not guaranteed at this stage.
+                            - `RuntimeReady`: The Action is triggered after the Component object has been created and all associated
+                              runtime resources (e.g. Pods) are in a ready state.
+                            - `ComponentReady`: The Action is triggered after the Component itself is in a ready state.
+                              This process does not affect the readiness state of the Component or the Cluster.
+                            - `ClusterReady`: The Action is executed after the Cluster is in a ready state.
+                              This execution does not alter the Component or the Cluster's state of readiness.
+
+
+                            This field cannot be updated.
+                          type: string
+                        retryPolicy:
+                          description: |-
+                            Defines the strategy to be taken when retrying the Action after a failure.
+
+
+                            It specifies the conditions under which the Action should be retried and the limits to apply,
+                            such as the maximum number of retries and backoff strategy.
+
+
+                            This field cannot be updated.
+                          properties:
+                            maxRetries:
+                              default: 0
+                              description: |-
+                                Defines the maximum number of retry attempts that should be made for a given Action.
+                                This value is set to 0 by default, indicating that no retries will be made.
+                              type: integer
+                            retryInterval:
+                              default: 0
+                              description: |-
+                                Indicates the duration of time to wait between each retry attempt.
+                                This value is set to 0 by default, indicating that there will be no delay between retry attempts.
+                              format: int64
+                              type: integer
+                          type: object
+                        timeoutSeconds:
+                          default: 0
+                          description: |-
+                            Specifies the maximum duration in seconds that the Action is allowed to run.
+
+
+                            If the Action does not complete within this time frame, it will be terminated.
+
+
+                            This field cannot be updated.
+                          format: int32
+                          type: integer
+                      type: object
+                    reconfigureActionName:
+                      description: |-
+                        The name of the custom reconfigure action.
+
+
+                        An empty name indicates that the reconfigure action is the default one defined by lifecycle actions.
+                      type: string
+                  required:
+                  - generation
+                  - name
+                  type: object
+                type: array
               defaultTemplateOrdinals:
                 description: |-
                   Specifies the desired Ordinals of the default template.
@@ -10123,6 +10421,34 @@ spec:
                   Used only when spec.roles set.
                 format: int32
                 type: integer
+              instanceStatus:
+                description: Provides the status of each instance in the ITS.
+                items:
+                  properties:
+                    configs:
+                      description: The status of configs.
+                      items:
+                        properties:
+                          generation:
+                            description: The generation of the config.
+                            format: int64
+                            type: integer
+                          name:
+                            description: The name of the config.
+                            type: string
+                        required:
+                        - generation
+                        - name
+                        type: object
+                      type: array
+                    podName:
+                      default: Unknown
+                      description: Represents the name of the pod.
+                      type: string
+                  required:
+                  - podName
+                  type: object
+                type: array
               membersStatus:
                 description: Provides the status of each member in the cluster.
                 items:
diff --git a/deploy/helm/templates/rbac/parameters_componentparameter_role.yaml b/deploy/helm/templates/rbac/parameters_componentparameter_role.yaml
new file mode 100644
index 000000000..5bdc08bef
--- /dev/null
+++ b/deploy/helm/templates/rbac/parameters_componentparameter_role.yaml
@@ -0,0 +1,26 @@
+# permissions for end users to edit componentparameters.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  name: {{ include "kubeblocks.fullname" . }}-componentparameter-role
+  labels:
+    {{- include "kubeblocks.labels" . | nindent 4 }}
+rules:
+- apiGroups:
+  - parameters.kubeblocks.io
+  resources:
+  - componentparameters
+  verbs:
+  - create
+  - delete
+  - get
+  - list
+  - patch
+  - update
+  - watch
+- apiGroups:
+  - parameters.kubeblocks.io
+  resources:
+  - componentparameters/status
+  verbs:
+  - get
diff --git a/deploy/helm/templates/rbac/parameters_paramconfigrenderer_role.yaml b/deploy/helm/templates/rbac/parameters_paramconfigrenderer_role.yaml
new file mode 100644
index 000000000..617305eba
--- /dev/null
+++ b/deploy/helm/templates/rbac/parameters_paramconfigrenderer_role.yaml
@@ -0,0 +1,26 @@
+# permissions for end users to edit paramconfigrenderers.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  name: {{ include "kubeblocks.fullname" . }}-paramconfigrenderer-role
+  labels:
+    {{- include "kubeblocks.labels" . | nindent 4 }}
+rules:
+- apiGroups:
+  - parameters.kubeblocks.io
+  resources:
+  - paramconfigrenderers
+  verbs:
+  - create
+  - delete
+  - get
+  - list
+  - patch
+  - update
+  - watch
+- apiGroups:
+  - parameters.kubeblocks.io
+  resources:
+  - paramconfigrenderers/status
+  verbs:
+  - get
diff --git a/deploy/helm/templates/rbac/parameters_parameter_role.yaml b/deploy/helm/templates/rbac/parameters_parameter_role.yaml
new file mode 100644
index 000000000..4a5f91b1c
--- /dev/null
+++ b/deploy/helm/templates/rbac/parameters_parameter_role.yaml
@@ -0,0 +1,26 @@
+# permissions for end users to edit parameters.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  name: {{ include "kubeblocks.fullname" . }}-parameter-role
+  labels:
+    {{- include "kubeblocks.labels" . | nindent 4 }}
+rules:
+- apiGroups:
+  - parameters.kubeblocks.io
+  resources:
+  - parameters
+  verbs:
+  - create
+  - delete
+  - get
+  - list
+  - patch
+  - update
+  - watch
+- apiGroups:
+  - parameters.kubeblocks.io
+  resources:
+  - parameters/status
+  verbs:
+  - get
diff --git a/deploy/helm/templates/rbac/parameters_parametersdefinition_role.yaml b/deploy/helm/templates/rbac/parameters_parametersdefinition_role.yaml
new file mode 100644
index 000000000..b62cef55b
--- /dev/null
+++ b/deploy/helm/templates/rbac/parameters_parametersdefinition_role.yaml
@@ -0,0 +1,26 @@
+# permissions for end users to edit parametersdefinitions.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  name: {{ include "kubeblocks.fullname" . }}-parametersdefinition-role
+  labels:
+    {{- include "kubeblocks.labels" . | nindent 4 }}
+rules:
+- apiGroups:
+  - parameters.kubeblocks.io
+  resources:
+  - parametersdefinitions
+  verbs:
+  - create
+  - delete
+  - get
+  - list
+  - patch
+  - update
+  - watch
+- apiGroups:
+  - parameters.kubeblocks.io
+  resources:
+  - parametersdefinitions/status
+  verbs:
+  - get
diff --git a/docs/developer_docs/api-reference/cluster.md b/docs/developer_docs/api-reference/cluster.md
index 6a6014061..d8a409636 100644
--- a/docs/developer_docs/api-reference/cluster.md
+++ b/docs/developer_docs/api-reference/cluster.md
@@ -1289,22 +1289,6 @@ Each template is represented as a ConfigMap and may contain multiple config file
 </tr>
 <tr>
 <td>
-<code>scripts2</code><br/>
-<em>
-<a href="#apps.kubeblocks.io/v1.ComponentFileTemplate">
-[]ComponentFileTemplate
-</a>
-</em>
-</td>
-<td>
-<em>(Optional)</em>
-<p>Specifies groups of scripts, each provided via a ConfigMap, to be mounted as volumes in the container.
-These scripts can be executed during container startup or via specific actions.</p>
-<p>This field is immutable.</p>
-</td>
-</tr>
-<tr>
-<td>
 <code>configs</code><br/>
 <em>
 <a href="#apps.kubeblocks.io/v1.ComponentTemplateSpec">
@@ -1356,8 +1340,8 @@ It allows you to specify multiple log types and their respective file paths for
 <td>
 <code>scripts</code><br/>
 <em>
-<a href="#apps.kubeblocks.io/v1.ComponentTemplateSpec">
-[]ComponentTemplateSpec
+<a href="#apps.kubeblocks.io/v1.ComponentFileTemplate">
+[]ComponentFileTemplate
 </a>
 </em>
 </td>
@@ -1365,11 +1349,6 @@ It allows you to specify multiple log types and their respective file paths for
 <em>(Optional)</em>
 <p>Specifies groups of scripts, each provided via a ConfigMap, to be mounted as volumes in the container.
 These scripts can be executed during container startup or via specific actions.</p>
-<p>Each script group is encapsulated in a ComponentTemplateSpec that includes:</p>
-<ul>
-<li>The ConfigMap containing the scripts.</li>
-<li>The mount point where the scripts will be mounted inside the container.</li>
-</ul>
 <p>This field is immutable.</p>
 </td>
 </tr>
@@ -2221,8 +2200,8 @@ the component definition of @selectors automatically.</p>
 <td>
 <code>configs</code><br/>
 <em>
-<a href="#apps.kubeblocks.io/v1.ComponentTemplateSpec">
-[]ComponentTemplateSpec
+<a href="#apps.kubeblocks.io/v1.ComponentFileTemplate">
+[]ComponentFileTemplate
 </a>
 </em>
 </td>
@@ -2236,8 +2215,8 @@ the component definition of @selectors automatically.</p>
 <td>
 <code>scripts</code><br/>
 <em>
-<a href="#apps.kubeblocks.io/v1.ComponentTemplateSpec">
-[]ComponentTemplateSpec
+<a href="#apps.kubeblocks.io/v1.ComponentFileTemplate">
+[]ComponentFileTemplate
 </a>
 </em>
 </td>
@@ -2267,7 +2246,7 @@ SidecarDefinitionStatus
 <h3 id="apps.kubeblocks.io/v1.Action">Action
 </h3>
 <p>
-(<em>Appears on:</em><a href="#apps.kubeblocks.io/v1.ClusterComponentConfig">ClusterComponentConfig</a>, <a href="#apps.kubeblocks.io/v1.ComponentLifecycleActions">ComponentLifecycleActions</a>, <a href="#apps.kubeblocks.io/v1.Probe">Probe</a>, <a href="#apps.kubeblocks.io/v1.ShardingLifecycleActions">ShardingLifecycleActions</a>, <a href="#workloads.kubeblocks.io/v1.MembershipReconfiguration">MembershipReconfiguration</a>)
+(<em>Appears on:</em><a href="#apps.kubeblocks.io/v1.ClusterComponentConfig">ClusterComponentConfig</a>, <a href="#apps.kubeblocks.io/v1.ComponentLifecycleActions">ComponentLifecycleActions</a>, <a href="#apps.kubeblocks.io/v1.Probe">Probe</a>, <a href="#apps.kubeblocks.io/v1.ShardingLifecycleActions">ShardingLifecycleActions</a>, <a href="#workloads.kubeblocks.io/v1.ConfigTemplate">ConfigTemplate</a>, <a href="#workloads.kubeblocks.io/v1.MembershipReconfiguration">MembershipReconfiguration</a>)
 </p>
 <div>
 <p>Action defines a customizable hook or procedure tailored for different database engines,
@@ -5175,22 +5154,6 @@ Each template is represented as a ConfigMap and may contain multiple config file
 </tr>
 <tr>
 <td>
-<code>scripts2</code><br/>
-<em>
-<a href="#apps.kubeblocks.io/v1.ComponentFileTemplate">
-[]ComponentFileTemplate
-</a>
-</em>
-</td>
-<td>
-<em>(Optional)</em>
-<p>Specifies groups of scripts, each provided via a ConfigMap, to be mounted as volumes in the container.
-These scripts can be executed during container startup or via specific actions.</p>
-<p>This field is immutable.</p>
-</td>
-</tr>
-<tr>
-<td>
 <code>configs</code><br/>
 <em>
 <a href="#apps.kubeblocks.io/v1.ComponentTemplateSpec">
@@ -5242,8 +5205,8 @@ It allows you to specify multiple log types and their respective file paths for
 <td>
 <code>scripts</code><br/>
 <em>
-<a href="#apps.kubeblocks.io/v1.ComponentTemplateSpec">
-[]ComponentTemplateSpec
+<a href="#apps.kubeblocks.io/v1.ComponentFileTemplate">
+[]ComponentFileTemplate
 </a>
 </em>
 </td>
@@ -5251,11 +5214,6 @@ It allows you to specify multiple log types and their respective file paths for
 <em>(Optional)</em>
 <p>Specifies groups of scripts, each provided via a ConfigMap, to be mounted as volumes in the container.
 These scripts can be executed during container startup or via specific actions.</p>
-<p>Each script group is encapsulated in a ComponentTemplateSpec that includes:</p>
-<ul>
-<li>The ConfigMap containing the scripts.</li>
-<li>The mount point where the scripts will be mounted inside the container.</li>
-</ul>
 <p>This field is immutable.</p>
 </td>
 </tr>
@@ -5573,7 +5531,7 @@ string
 <h3 id="apps.kubeblocks.io/v1.ComponentFileTemplate">ComponentFileTemplate
 </h3>
 <p>
-(<em>Appears on:</em><a href="#apps.kubeblocks.io/v1.ComponentDefinitionSpec">ComponentDefinitionSpec</a>)
+(<em>Appears on:</em><a href="#apps.kubeblocks.io/v1.ComponentDefinitionSpec">ComponentDefinitionSpec</a>, <a href="#apps.kubeblocks.io/v1.SidecarDefinitionSpec">SidecarDefinitionSpec</a>)
 </p>
 <div>
 </div>
@@ -6747,7 +6705,7 @@ ProvisionSecretRef
 <h3 id="apps.kubeblocks.io/v1.ComponentTemplateSpec">ComponentTemplateSpec
 </h3>
 <p>
-(<em>Appears on:</em><a href="#apps.kubeblocks.io/v1.ComponentDefinitionSpec">ComponentDefinitionSpec</a>, <a href="#apps.kubeblocks.io/v1.SidecarDefinitionSpec">SidecarDefinitionSpec</a>)
+(<em>Appears on:</em><a href="#apps.kubeblocks.io/v1.ComponentDefinitionSpec">ComponentDefinitionSpec</a>)
 </p>
 <div>
 </div>
@@ -11403,8 +11361,8 @@ the component definition of @selectors automatically.</p>
 <td>
 <code>configs</code><br/>
 <em>
-<a href="#apps.kubeblocks.io/v1.ComponentTemplateSpec">
-[]ComponentTemplateSpec
+<a href="#apps.kubeblocks.io/v1.ComponentFileTemplate">
+[]ComponentFileTemplate
 </a>
 </em>
 </td>
@@ -11418,8 +11376,8 @@ the component definition of @selectors automatically.</p>
 <td>
 <code>scripts</code><br/>
 <em>
-<a href="#apps.kubeblocks.io/v1.ComponentTemplateSpec">
-[]ComponentTemplateSpec
+<a href="#apps.kubeblocks.io/v1.ComponentFileTemplate">
+[]ComponentFileTemplate
 </a>
 </em>
 </td>
@@ -29595,6 +29553,20 @@ bool
 <p>Indicates that the InstanceSet is paused, meaning the reconciliation of this InstanceSet object will be paused.</p>
 </td>
 </tr>
+<tr>
+<td>
+<code>configs</code><br/>
+<em>
+<a href="#workloads.kubeblocks.io/v1.ConfigTemplate">
+[]ConfigTemplate
+</a>
+</em>
+</td>
+<td>
+<em>(Optional)</em>
+<p>Describe the configs to be reconfigured.</p>
+</td>
+</tr>
 </table>
 </td>
 </tr>
@@ -29642,6 +29614,123 @@ PodUpdatePolicy is set to StrictInPlace but the pods cannot be updated in-place)
 </td>
 </tr></tbody>
 </table>
+<h3 id="workloads.kubeblocks.io/v1.ConfigTemplate">ConfigTemplate
+</h3>
+<p>
+(<em>Appears on:</em><a href="#workloads.kubeblocks.io/v1.InstanceSetSpec">InstanceSetSpec</a>)
+</p>
+<div>
+</div>
+<table>
+<thead>
+<tr>
+<th>Field</th>
+<th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td>
+<code>name</code><br/>
+<em>
+string
+</em>
+</td>
+<td>
+<p>The name of the config.</p>
+</td>
+</tr>
+<tr>
+<td>
+<code>generation</code><br/>
+<em>
+int64
+</em>
+</td>
+<td>
+<p>The generation of the config.</p>
+</td>
+</tr>
+<tr>
+<td>
+<code>reconfigure</code><br/>
+<em>
+<a href="#apps.kubeblocks.io/v1.Action">
+Action
+</a>
+</em>
+</td>
+<td>
+<em>(Optional)</em>
+<p>The custom reconfigure action.</p>
+</td>
+</tr>
+<tr>
+<td>
+<code>reconfigureActionName</code><br/>
+<em>
+string
+</em>
+</td>
+<td>
+<em>(Optional)</em>
+<p>The name of the custom reconfigure action.</p>
+<p>An empty name indicates that the reconfigure action is the default one defined by lifecycle actions.</p>
+</td>
+</tr>
+<tr>
+<td>
+<code>parameters</code><br/>
+<em>
+map[string]string
+</em>
+</td>
+<td>
+<em>(Optional)</em>
+<p>The parameters to call the reconfigure action.</p>
+</td>
+</tr>
+</tbody>
+</table>
+<h3 id="workloads.kubeblocks.io/v1.InstanceConfigStatus">InstanceConfigStatus
+</h3>
+<p>
+(<em>Appears on:</em><a href="#workloads.kubeblocks.io/v1.InstanceStatus">InstanceStatus</a>)
+</p>
+<div>
+</div>
+<table>
+<thead>
+<tr>
+<th>Field</th>
+<th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td>
+<code>name</code><br/>
+<em>
+string
+</em>
+</td>
+<td>
+<p>The name of the config.</p>
+</td>
+</tr>
+<tr>
+<td>
+<code>generation</code><br/>
+<em>
+int64
+</em>
+</td>
+<td>
+<p>The generation of the config.</p>
+</td>
+</tr>
+</tbody>
+</table>
 <h3 id="workloads.kubeblocks.io/v1.InstanceSetSpec">InstanceSetSpec
 </h3>
 <p>
@@ -29959,6 +30048,20 @@ bool
 <p>Indicates that the InstanceSet is paused, meaning the reconciliation of this InstanceSet object will be paused.</p>
 </td>
 </tr>
+<tr>
+<td>
+<code>configs</code><br/>
+<em>
+<a href="#workloads.kubeblocks.io/v1.ConfigTemplate">
+[]ConfigTemplate
+</a>
+</em>
+</td>
+<td>
+<em>(Optional)</em>
+<p>Describe the configs to be reconfigured.</p>
+</td>
+</tr>
 </tbody>
 </table>
 <h3 id="workloads.kubeblocks.io/v1.InstanceSetStatus">InstanceSetStatus
@@ -30131,6 +30234,20 @@ Used only when spec.roles set.</p>
 </tr>
 <tr>
 <td>
+<code>instanceStatus</code><br/>
+<em>
+<a href="#workloads.kubeblocks.io/v1.InstanceStatus">
+[]InstanceStatus
+</a>
+</em>
+</td>
+<td>
+<em>(Optional)</em>
+<p>Provides the status of each instance in the ITS.</p>
+</td>
+</tr>
+<tr>
+<td>
 <code>currentRevisions</code><br/>
 <em>
 map[string]string
@@ -30171,6 +30288,48 @@ key is the pod name, value is the revision.</p>
 </tr>
 </tbody>
 </table>
+<h3 id="workloads.kubeblocks.io/v1.InstanceStatus">InstanceStatus
+</h3>
+<p>
+(<em>Appears on:</em><a href="#workloads.kubeblocks.io/v1.InstanceSetStatus">InstanceSetStatus</a>)
+</p>
+<div>
+</div>
+<table>
+<thead>
+<tr>
+<th>Field</th>
+<th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td>
+<code>podName</code><br/>
+<em>
+string
+</em>
+</td>
+<td>
+<p>Represents the name of the pod.</p>
+</td>
+</tr>
+<tr>
+<td>
+<code>configs</code><br/>
+<em>
+<a href="#workloads.kubeblocks.io/v1.InstanceConfigStatus">
+[]InstanceConfigStatus
+</a>
+</em>
+</td>
+<td>
+<em>(Optional)</em>
+<p>The status of configs.</p>
+</td>
+</tr>
+</tbody>
+</table>
 <h3 id="workloads.kubeblocks.io/v1.InstanceTemplateStatus">InstanceTemplateStatus
 </h3>
 <p>
diff --git a/go.mod b/go.mod
index 25418b627..64459e0b1 100644
--- a/go.mod
+++ b/go.mod
@@ -61,14 +61,14 @@ require (
 	gopkg.in/ini.v1 v1.67.0
 	gopkg.in/yaml.v2 v2.4.0
 	helm.sh/helm/v3 v3.14.2
-	k8s.io/api v0.29.2
-	k8s.io/apiextensions-apiserver v0.29.0
-	k8s.io/apimachinery v0.29.2
-	k8s.io/cli-runtime v0.29.0
-	k8s.io/client-go v0.29.2
-	k8s.io/code-generator v0.29.0
-	k8s.io/component-helpers v0.29.0
-	k8s.io/cri-api v0.29.0
+	k8s.io/api v0.29.14
+	k8s.io/apiextensions-apiserver v0.29.14
+	k8s.io/apimachinery v0.29.14
+	k8s.io/cli-runtime v0.29.14
+	k8s.io/client-go v0.29.14
+	k8s.io/code-generator v0.29.14
+	k8s.io/component-helpers v0.29.14
+	k8s.io/cri-api v0.29.14
 	k8s.io/gengo v0.0.0-20230829151522-9cce18d56c01
 	k8s.io/klog v1.0.0
 	k8s.io/klog/v2 v2.120.1
@@ -235,8 +235,8 @@ require (
 	gopkg.in/inf.v0 v0.9.1 // indirect
 	gopkg.in/yaml.v3 v3.0.1 // indirect
 	gotest.tools/v3 v3.5.0 // indirect
-	k8s.io/apiserver v0.29.0 // indirect
-	k8s.io/component-base v0.29.0 // indirect
+	k8s.io/apiserver v0.29.14 // indirect
+	k8s.io/component-base v0.29.14 // indirect
 	k8s.io/gengo/v2 v2.0.0-20240228010128-51d4e06bde70 // indirect
 	k8s.io/metrics v0.29.0 // indirect
 	oras.land/oras-go v1.2.5 // indirect
diff --git a/go.sum b/go.sum
index 294b9ec81..d288b48c8 100644
--- a/go.sum
+++ b/go.sum
@@ -1164,29 +1164,29 @@ honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt
 honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
 honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
 k8s.io/api v0.19.0/go.mod h1:I1K45XlvTrDjmj5LoM5LuP/KYrhWbjUKT/SoPG0qTjw=
-k8s.io/api v0.29.2 h1:hBC7B9+MU+ptchxEqTNW2DkUosJpp1P+Wn6YncZ474A=
-k8s.io/api v0.29.2/go.mod h1:sdIaaKuU7P44aoyyLlikSLayT6Vb7bvJNCX105xZXY0=
-k8s.io/apiextensions-apiserver v0.29.0 h1:0VuspFG7Hj+SxyF/Z/2T0uFbI5gb5LRgEyUVE3Q4lV0=
-k8s.io/apiextensions-apiserver v0.29.0/go.mod h1:TKmpy3bTS0mr9pylH0nOt/QzQRrW7/h7yLdRForMZwc=
+k8s.io/api v0.29.14 h1:JWFh5ufowH3Y6tCgEzY3URVJHb27f0tEDEej0nCjWDw=
+k8s.io/api v0.29.14/go.mod h1:IV8YqKxMm8JGLBLlHM13Npn5lCITH10XYipWEW+YEOQ=
+k8s.io/apiextensions-apiserver v0.29.14 h1:gw9WXrZJPu5kpI1UC+Wf8BVe9PWwRUB/UZXU8VzBsq4=
+k8s.io/apiextensions-apiserver v0.29.14/go.mod h1:TJ51W+HKW2XqTtAsEFOz1/OohsMtekbKaTXh8ldioL4=
 k8s.io/apimachinery v0.19.0/go.mod h1:DnPGDnARWFvYa3pMHgSxtbZb7gpzzAZ1pTfaUNDVlmA=
-k8s.io/apimachinery v0.29.2 h1:EWGpfJ856oj11C52NRCHuU7rFDwxev48z+6DSlGNsV8=
-k8s.io/apimachinery v0.29.2/go.mod h1:6HVkd1FwxIagpYrHSwJlQqZI3G9LfYWRPAkUvLnXTKU=
-k8s.io/apiserver v0.29.0 h1:Y1xEMjJkP+BIi0GSEv1BBrf1jLU9UPfAnnGGbbDdp7o=
-k8s.io/apiserver v0.29.0/go.mod h1:31n78PsRKPmfpee7/l9NYEv67u6hOL6AfcE761HapDM=
-k8s.io/cli-runtime v0.29.0 h1:q2kC3cex4rOBLfPOnMSzV2BIrrQlx97gxHJs21KxKS4=
-k8s.io/cli-runtime v0.29.0/go.mod h1:VKudXp3X7wR45L+nER85YUzOQIru28HQpXr0mTdeCrk=
+k8s.io/apimachinery v0.29.14 h1:IDhwnGNCp836SLOwW1SoEfFNV77wxIklhxeAHX9vmSo=
+k8s.io/apimachinery v0.29.14/go.mod h1:i3FJVwhvSp/6n8Fl4K97PJEP8C+MM+aoDq4+ZJBf70Y=
+k8s.io/apiserver v0.29.14 h1:XTo9lDDsG4NkywEuzG0SDLrBv6+AyVIpqVzZjg+pWRs=
+k8s.io/apiserver v0.29.14/go.mod h1:jC0HqUfqFKMp111xs97CXkf8XTQXtnbukRuuwDH74yE=
+k8s.io/cli-runtime v0.29.14 h1:CJis59afFwxzV4RBKR6rpS94C5onIypVsRqq4hyydPk=
+k8s.io/cli-runtime v0.29.14/go.mod h1:PBDI2lTaFj/qdNx5C6HGlCH+KMmtl1JVJcmCLMveTc4=
 k8s.io/client-go v0.19.0/go.mod h1:H9E/VT95blcFQnlyShFgnFT9ZnJOAceiUHM3MlRC+mU=
-k8s.io/client-go v0.29.2 h1:FEg85el1TeZp+/vYJM7hkDlSTFZ+c5nnK44DJ4FyoRg=
-k8s.io/client-go v0.29.2/go.mod h1:knlvFZE58VpqbQpJNbCbctTVXcd35mMyAAwBdpt4jrA=
+k8s.io/client-go v0.29.14 h1:OSnzZ9DClaFRgl3zMAY2kGZhNjdGJkEb+RDz+MW2h6k=
+k8s.io/client-go v0.29.14/go.mod h1:XtZt5n5UxKfPJ+sCoTPcEavWgZbLFFxMnAFFRQGK1RY=
 k8s.io/code-generator v0.19.0/go.mod h1:moqLn7w0t9cMs4+5CQyxnfA/HV8MF6aAVENF+WZZhgk=
-k8s.io/code-generator v0.29.0 h1:2LQfayGDhaIlaamXjIjEQlCMy4JNCH9lrzas4DNW1GQ=
-k8s.io/code-generator v0.29.0/go.mod h1:5bqIZoCxs2zTRKMWNYqyQWW/bajc+ah4rh0tMY8zdGA=
-k8s.io/component-base v0.29.0 h1:T7rjd5wvLnPBV1vC4zWd/iWRbV8Mdxs+nGaoaFzGw3s=
-k8s.io/component-base v0.29.0/go.mod h1:sADonFTQ9Zc9yFLghpDpmNXEdHyQmFIGbiuZbqAXQ1M=
-k8s.io/component-helpers v0.29.0 h1:Y8W70NGeitKxWwhsPo/vEQbQx5VqJV+3xfLpP3V1VxU=
-k8s.io/component-helpers v0.29.0/go.mod h1:j2coxVfmzTOXWSE6sta0MTgNSr572Dcx68F6DD+8fWc=
-k8s.io/cri-api v0.29.0 h1:atenAqOltRsFqcCQlFFpDnl/R4aGfOELoNLTDJfd7t8=
-k8s.io/cri-api v0.29.0/go.mod h1:Rls2JoVwfC7kW3tndm7267kriuRukQ02qfht0PCRuIc=
+k8s.io/code-generator v0.29.14 h1:Fuhe0MsDWD4kb5s2RKJcZWrAH7KN/60I9goj9kPgy8g=
+k8s.io/code-generator v0.29.14/go.mod h1:7TYnI0dYItL2cKuhhgPSuF3WED9uMdELgbVXFfn/joE=
+k8s.io/component-base v0.29.14 h1:SF1DWN7bc2VloJ/ysegGoi/aHnopEo81aw9CslhqXIw=
+k8s.io/component-base v0.29.14/go.mod h1:FoK1PHhFTaEQVvQLw29/Uyfd8Ug0qUKHrUcXIXJ1VxI=
+k8s.io/component-helpers v0.29.14 h1:xVdPm0SAlQlclESC/3Q3DV6mRkyWZiOfG+jEEaEveyM=
+k8s.io/component-helpers v0.29.14/go.mod h1:vpLvJu1de/+ZCPdj34549/b83FG6/djZsFzCWt6Qses=
+k8s.io/cri-api v0.29.14 h1:ajYndA+B4nmfHDQ8nN1G+7zsaWFc9lgZS3T30+LvIs4=
+k8s.io/cri-api v0.29.14/go.mod h1:A6pdbjzML2xi9B0Clqn5qt1HJ3Ik12x2j+jv/TkqjRE=
 k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
 k8s.io/gengo v0.0.0-20200428234225-8167cfdcfc14/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
 k8s.io/gengo v0.0.0-20230829151522-9cce18d56c01 h1:pWEwq4Asjm4vjW7vcsmijwBhOr1/shsbSYiWXmNGlks=
diff --git a/pkg/constant/backup.go b/pkg/constant/backup.go
index 4ab6d5032..b93ef28dd 100755
--- a/pkg/constant/backup.go
+++ b/pkg/constant/backup.go
@@ -28,5 +28,4 @@ const (
 	EnvForRestore                     = "restoreEnv"
 	ParametersForRestore              = "restoreParameters"
 	ConnectionPassword                = "connectionPassword"
-	EncryptedSystemAccounts           = "encryptedSystemAccounts"
 )
diff --git a/pkg/controller/builder/builder_component_definition.go b/pkg/controller/builder/builder_component_definition.go
index beb8f4db3..685193031 100644
--- a/pkg/controller/builder/builder_component_definition.go
+++ b/pkg/controller/builder/builder_component_definition.go
@@ -127,49 +127,6 @@ func (builder *ComponentDefinitionBuilder) AddServiceExt(name, serviceName strin
 	return builder
 }
 
-func (builder *ComponentDefinitionBuilder) AddConfigTemplate(name, configTemplateRef,
-	namespace, volumeName string) *ComponentDefinitionBuilder {
-	config := appsv1.ComponentTemplateSpec{
-		Name:        name,
-		TemplateRef: configTemplateRef,
-		Namespace:   namespace,
-		VolumeName:  volumeName,
-	}
-	if builder.get().Spec.Configs == nil {
-		builder.get().Spec.Configs = make([]appsv1.ComponentTemplateSpec, 0)
-	}
-	builder.get().Spec.Configs = append(builder.get().Spec.Configs, config)
-	return builder
-}
-
-func (builder *ComponentDefinitionBuilder) AddLogConfig(name, filePathPattern string) *ComponentDefinitionBuilder {
-	logConfig := appsv1.LogConfig{
-		FilePathPattern: filePathPattern,
-		Name:            name,
-	}
-	if builder.get().Spec.LogConfigs == nil {
-		builder.get().Spec.LogConfigs = make([]appsv1.LogConfig, 0)
-	}
-	builder.get().Spec.LogConfigs = append(builder.get().Spec.LogConfigs, logConfig)
-	return builder
-}
-
-func (builder *ComponentDefinitionBuilder) AddScriptTemplate(name, configTemplateRef, namespace, volumeName string,
-	mode *int32) *ComponentDefinitionBuilder {
-	script := appsv1.ComponentTemplateSpec{
-		Name:        name,
-		TemplateRef: configTemplateRef,
-		Namespace:   namespace,
-		VolumeName:  volumeName,
-		DefaultMode: mode,
-	}
-	if builder.get().Spec.Scripts == nil {
-		builder.get().Spec.Scripts = make([]appsv1.ComponentTemplateSpec, 0)
-	}
-	builder.get().Spec.Scripts = append(builder.get().Spec.Scripts, script)
-	return builder
-}
-
 func (builder *ComponentDefinitionBuilder) SetPolicyRules(rules []rbacv1.PolicyRule) *ComponentDefinitionBuilder {
 	builder.get().Spec.PolicyRules = rules
 	return builder
diff --git a/pkg/controller/component/sidecar.go b/pkg/controller/component/sidecar.go
index 1d062ba7e..75c3fac02 100644
--- a/pkg/controller/component/sidecar.go
+++ b/pkg/controller/component/sidecar.go
@@ -29,27 +29,28 @@ import (
 	appsv1 "github.com/apecloud/kubeblocks/apis/apps/v1"
 )
 
-func buildSidecars(ctx context.Context, cli client.Reader, comp *appsv1.Component, synthesizedComp *SynthesizedComponent) error {
+func buildSidecars(ctx context.Context, cli client.Reader, synthesizedComp *SynthesizedComponent, comp *appsv1.Component) error {
 	for _, sidecar := range comp.Spec.Sidecars {
-		if err := buildSidecar(ctx, cli, sidecar, synthesizedComp); err != nil {
+		if err := buildSidecar(ctx, cli, synthesizedComp, comp, sidecar); err != nil {
 			return err
 		}
 	}
 	return nil
 }
 
-func buildSidecar(ctx context.Context, cli client.Reader, sidecar appsv1.Sidecar, synthesizedComp *SynthesizedComponent) error {
+func buildSidecar(ctx context.Context, cli client.Reader,
+	synthesizedComp *SynthesizedComponent, comp *appsv1.Component, sidecar appsv1.Sidecar) error {
 	sidecarDef, err := getNCheckSidecarDefinition(ctx, cli, sidecar.SidecarDef)
 	if err != nil {
 		return err
 	}
-	for _, builder := range []func(*appsv1.SidecarDefinition, *SynthesizedComponent) error{
+	for _, builder := range []func(*SynthesizedComponent, *appsv1.SidecarDefinition, *appsv1.Component) error{
 		buildSidecarContainers,
 		buildSidecarVars,
 		buildSidecarConfigs,
 		buildSidecarScripts,
 	} {
-		if err := builder(sidecarDef, synthesizedComp); err != nil {
+		if err := builder(synthesizedComp, sidecarDef, comp); err != nil {
 			return err
 		}
 	}
@@ -73,12 +74,12 @@ func getNCheckSidecarDefinition(ctx context.Context, cli client.Reader, name str
 	return sidecarDef, nil
 }
 
-func buildSidecarContainers(sidecarDef *appsv1.SidecarDefinition, synthesizedComp *SynthesizedComponent) error {
+func buildSidecarContainers(synthesizedComp *SynthesizedComponent, sidecarDef *appsv1.SidecarDefinition, _ *appsv1.Component) error {
 	synthesizedComp.PodSpec.Containers = append(synthesizedComp.PodSpec.Containers, sidecarDef.Spec.Containers...)
 	return nil
 }
 
-func buildSidecarVars(sidecarDef *appsv1.SidecarDefinition, synthesizedComp *SynthesizedComponent) error {
+func buildSidecarVars(synthesizedComp *SynthesizedComponent, sidecarDef *appsv1.SidecarDefinition, _ *appsv1.Component) error {
 	if sidecarDef.Spec.Vars != nil {
 		if synthesizedComp.SidecarVars == nil {
 			synthesizedComp.SidecarVars = make([]appsv1.EnvVar, 0)
@@ -89,16 +90,16 @@ func buildSidecarVars(sidecarDef *appsv1.SidecarDefinition, synthesizedComp *Syn
 	return nil
 }
 
-func buildSidecarConfigs(sidecarDef *appsv1.SidecarDefinition, synthesizedComp *SynthesizedComponent) error {
-	if sidecarDef.Spec.Configs != nil {
-		synthesizedComp.ConfigTemplates = append(synthesizedComp.ConfigTemplates, sidecarDef.Spec.Configs...)
+func buildSidecarConfigs(synthesizedComp *SynthesizedComponent, sidecarDef *appsv1.SidecarDefinition, comp *appsv1.Component) error {
+	for _, tpl := range sidecarDef.Spec.Configs {
+		synthesizedComp.FileTemplates = append(synthesizedComp.FileTemplates, synthesizeFileTemplate(comp, tpl, true))
 	}
 	return nil
 }
 
-func buildSidecarScripts(sidecarDef *appsv1.SidecarDefinition, synthesizedComp *SynthesizedComponent) error {
-	if sidecarDef.Spec.Scripts != nil {
-		synthesizedComp.ScriptTemplates = append(synthesizedComp.ScriptTemplates, sidecarDef.Spec.Scripts...)
+func buildSidecarScripts(synthesizedComp *SynthesizedComponent, sidecarDef *appsv1.SidecarDefinition, comp *appsv1.Component) error {
+	for _, tpl := range sidecarDef.Spec.Scripts {
+		synthesizedComp.FileTemplates = append(synthesizedComp.FileTemplates, synthesizeFileTemplate(comp, tpl, false))
 	}
 	return nil
 }
diff --git a/pkg/controller/component/synthesize_component.go b/pkg/controller/component/synthesize_component.go
index 2ce7b7bae..9f0dcb0fc 100644
--- a/pkg/controller/component/synthesize_component.go
+++ b/pkg/controller/component/synthesize_component.go
@@ -99,7 +99,6 @@ func BuildSynthesizedComponent(ctx context.Context, cli client.Reader,
 		ComponentServices:                compDefObj.Spec.Services,
 		LogConfigs:                       compDefObj.Spec.LogConfigs,
 		ConfigTemplates:                  compDefObj.Spec.Configs,
-		ScriptTemplates:                  compDefObj.Spec.Scripts,
 		Roles:                            compDefObj.Spec.Roles,
 		MinReadySeconds:                  compDefObj.Spec.MinReadySeconds,
 		PolicyRules:                      compDefObj.Spec.PolicyRules,
@@ -132,7 +131,7 @@ func BuildSynthesizedComponent(ctx context.Context, cli client.Reader,
 
 	// build volumes & volumeClaimTemplates
 	buildVolumeClaimTemplates(synthesizeComp, comp)
-	if err = mergeUserDefinedVolumes(synthesizeComp, comp); err != nil {
+	if err = mergeUserDefinedVolumes(synthesizeComp, compDef, comp); err != nil {
 		return nil, err
 	}
 
@@ -142,7 +141,6 @@ func BuildSynthesizedComponent(ctx context.Context, cli client.Reader,
 	overrideComponentServices(synthesizeComp, comp)
 
 	buildFileTemplates(synthesizeComp, compDef, comp)
-
 	if err = overrideNCheckConfigTemplates(synthesizeComp, comp); err != nil {
 		return nil, err
 	}
@@ -153,7 +151,7 @@ func BuildSynthesizedComponent(ctx context.Context, cli client.Reader,
 	// build runtimeClassName
 	buildRuntimeClassName(synthesizeComp, comp)
 
-	if err = buildSidecars(ctx, cli, comp, synthesizeComp); err != nil {
+	if err = buildSidecars(ctx, cli, synthesizeComp, comp); err != nil {
 		return nil, err
 	}
 
@@ -277,7 +275,7 @@ func buildVolumeClaimTemplates(synthesizeComp *SynthesizedComponent, comp *appsv
 	}
 }
 
-func mergeUserDefinedVolumes(synthesizedComp *SynthesizedComponent, comp *appsv1.Component) error {
+func mergeUserDefinedVolumes(synthesizedComp *SynthesizedComponent, compDef *appsv1.ComponentDefinition, comp *appsv1.Component) error {
 	if comp == nil {
 		return nil
 	}
@@ -297,20 +295,20 @@ func mergeUserDefinedVolumes(synthesizedComp *SynthesizedComponent, comp *appsv1
 		volumes[vct.Name] = true
 	}
 
-	checkConfigNScriptTemplate := func(tpl appsv1.ComponentTemplateSpec) error {
-		if volumes[tpl.VolumeName] {
-			return fmt.Errorf("duplicated volume %s for template %s", tpl.VolumeName, tpl.Name)
+	checkConfigNScriptTemplate := func(name, volumeName string) error {
+		if volumes[volumeName] {
+			return fmt.Errorf("duplicated volume %s for template %s", volumeName, name)
 		}
-		volumes[tpl.VolumeName] = true
+		volumes[volumeName] = true
 		return nil
 	}
 	for _, tpl := range synthesizedComp.ConfigTemplates {
-		if err := checkConfigNScriptTemplate(tpl); err != nil {
+		if err := checkConfigNScriptTemplate(tpl.Name, tpl.VolumeName); err != nil {
 			return err
 		}
 	}
-	for _, tpl := range synthesizedComp.ScriptTemplates {
-		if err := checkConfigNScriptTemplate(tpl); err != nil {
+	for _, tpl := range compDef.Spec.Scripts {
+		if err := checkConfigNScriptTemplate(tpl.Name, tpl.VolumeName); err != nil {
 			return err
 		}
 	}
@@ -428,6 +426,17 @@ func checkConfigTemplates(synthesizedComp *SynthesizedComponent) error {
 }
 
 func buildFileTemplates(synthesizedComp *SynthesizedComponent, compDef *appsv1.ComponentDefinition, comp *appsv1.Component) {
+	templates := make([]SynthesizedFileTemplate, 0)
+	for _, tpl := range compDef.Spec.Configs2 {
+		templates = append(templates, synthesizeFileTemplate(comp, tpl, true))
+	}
+	for _, tpl := range compDef.Spec.Scripts {
+		templates = append(templates, synthesizeFileTemplate(comp, tpl, false))
+	}
+	synthesizedComp.FileTemplates = templates
+}
+
+func synthesizeFileTemplate(comp *appsv1.Component, tpl appsv1.ComponentFileTemplate, config bool) SynthesizedFileTemplate {
 	merge := func(tpl SynthesizedFileTemplate, utpl appsv1.ClusterComponentConfig) SynthesizedFileTemplate {
 		tpl.Variables = utpl.Variables
 		if utpl.ConfigMap != nil {
@@ -447,28 +456,17 @@ func buildFileTemplates(synthesizedComp *SynthesizedComponent, compDef *appsv1.C
 		return tpl
 	}
 
-	synthesize := func(tpl appsv1.ComponentFileTemplate, config bool) SynthesizedFileTemplate {
-		stpl := SynthesizedFileTemplate{
-			ComponentFileTemplate: tpl,
-		}
-		if config {
-			for _, utpl := range comp.Spec.Configs {
-				if utpl.Name != nil && *utpl.Name == tpl.Name {
-					return merge(stpl, utpl)
-				}
+	stpl := SynthesizedFileTemplate{
+		ComponentFileTemplate: tpl,
+	}
+	if config {
+		for _, utpl := range comp.Spec.Configs {
+			if utpl.Name != nil && *utpl.Name == tpl.Name {
+				return merge(stpl, utpl)
 			}
 		}
-		return stpl
-	}
-
-	templates := make([]SynthesizedFileTemplate, 0)
-	for _, tpl := range compDef.Spec.Configs2 {
-		templates = append(templates, synthesize(tpl, true))
 	}
-	for _, tpl := range compDef.Spec.Scripts2 {
-		templates = append(templates, synthesize(tpl, false))
-	}
-	synthesizedComp.FileTemplates = templates
+	return stpl
 }
 
 // buildServiceAccountName builds serviceAccountName for component and podSpec.
diff --git a/pkg/controller/component/type.go b/pkg/controller/component/type.go
index 9e718dcc6..58cdd667e 100644
--- a/pkg/controller/component/type.go
+++ b/pkg/controller/component/type.go
@@ -49,7 +49,6 @@ type SynthesizedComponent struct {
 	LogConfigs                       []kbappsv1.LogConfig `json:"logConfigs,omitempty"`
 	FileTemplates                    []SynthesizedFileTemplate
 	ConfigTemplates                  []kbappsv1.ComponentTemplateSpec       `json:"configTemplates,omitempty"`
-	ScriptTemplates                  []kbappsv1.ComponentTemplateSpec       `json:"scriptTemplates,omitempty"`
 	TLSConfig                        *kbappsv1.TLSConfig                    `json:"tlsConfig"`
 	ServiceAccountName               string                                 `json:"serviceAccountName,omitempty"`
 	ServiceReferences                map[string]*kbappsv1.ServiceDescriptor `json:"serviceReferences,omitempty"`
diff --git a/pkg/controller/configuration/annotation_utils.go b/pkg/controller/configuration/annotation_utils.go
index bc47767c0..a5d0c81b7 100644
--- a/pkg/controller/configuration/annotation_utils.go
+++ b/pkg/controller/configuration/annotation_utils.go
@@ -33,9 +33,6 @@ func BuildConfigTemplateAnnotations(object client.Object, synthesizedComp *compo
 	for _, configTplSpec := range synthesizedComp.ConfigTemplates {
 		asMapAnnotations[core.GenerateTPLUniqLabelKeyWithConfig(configTplSpec.Name)] = core.GetComponentCfgName(synthesizedComp.ClusterName, synthesizedComp.Name, configTplSpec.Name)
 	}
-	for _, scriptTplSpec := range synthesizedComp.ScriptTemplates {
-		asMapAnnotations[core.GenerateTPLUniqLabelKeyWithConfig(scriptTplSpec.Name)] = core.GetComponentCfgName(synthesizedComp.ClusterName, synthesizedComp.Name, scriptTplSpec.Name)
-	}
 	updateLabelsOrAnnotations(asMapAnnotations, object.GetAnnotations, object.SetAnnotations, constant.ConfigurationTplLabelPrefixKey)
 }
 
diff --git a/pkg/controller/factory/builder.go b/pkg/controller/factory/builder.go
index 5fedd8203..99e794c64 100644
--- a/pkg/controller/factory/builder.go
+++ b/pkg/controller/factory/builder.go
@@ -249,39 +249,6 @@ func GetRestorePassword(synthesizedComp *component.SynthesizedComponent) string
 	return password
 }
 
-// GetRestoreSystemAccountPassword gets restore password if exists during recovery.
-func GetRestoreSystemAccountPassword(annotations map[string]string, componentName, accountName string) string {
-	valueString := annotations[constant.RestoreFromBackupAnnotationKey]
-	if len(valueString) == 0 {
-		return ""
-	}
-	backupMap := map[string]map[string]string{}
-	err := json.Unmarshal([]byte(valueString), &backupMap)
-	if err != nil {
-		return ""
-	}
-	backupSource, ok := backupMap[componentName]
-	if !ok {
-		return ""
-	}
-	systemAccountsString, ok := backupSource[constant.EncryptedSystemAccounts]
-	if !ok {
-		return ""
-	}
-	systemAccountsMap := map[string]string{}
-	err = json.Unmarshal([]byte(systemAccountsString), &systemAccountsMap)
-	if err != nil {
-		return ""
-	}
-	e := intctrlutil.NewEncryptor(viper.GetString(constant.CfgKeyDPEncryptionKey))
-	encryptedPwd, ok := systemAccountsMap[accountName]
-	if !ok {
-		return ""
-	}
-	password, _ := e.Decrypt([]byte(encryptedPwd))
-	return password
-}
-
 // TODO: add dynamicLabels and dynamicAnnotations by @zhangtao
 
 func BuildConfigMapWithTemplate(cluster *kbappsv1.Cluster,
diff --git a/pkg/controller/instanceset/reconciler_instance_alignment.go b/pkg/controller/instanceset/reconciler_instance_alignment.go
index 81aa668bd..81955186a 100644
--- a/pkg/controller/instanceset/reconciler_instance_alignment.go
+++ b/pkg/controller/instanceset/reconciler_instance_alignment.go
@@ -51,7 +51,7 @@ func (r *instanceAlignmentReconciler) PreCondition(tree *kubebuilderx.ObjectTree
 	}
 	its, _ := tree.GetRoot().(*workloads.InstanceSet)
 	if err := validateSpec(its, tree); err != nil {
-		return kubebuilderx.CheckResultWithError(err)
+		return kubebuilderx.ConditionUnsatisfiedWithError(err)
 	}
 	return kubebuilderx.ConditionSatisfied
 }
diff --git a/pkg/controller/instanceset/reconciler_revision_update.go b/pkg/controller/instanceset/reconciler_revision_update.go
index 43254aed8..74970df75 100644
--- a/pkg/controller/instanceset/reconciler_revision_update.go
+++ b/pkg/controller/instanceset/reconciler_revision_update.go
@@ -46,7 +46,7 @@ func (r *revisionUpdateReconciler) PreCondition(tree *kubebuilderx.ObjectTree) *
 	}
 	its, _ := tree.GetRoot().(*workloads.InstanceSet)
 	if err := validateSpec(its, tree); err != nil {
-		return kubebuilderx.CheckResultWithError(err)
+		return kubebuilderx.ConditionUnsatisfiedWithError(err)
 	}
 	return kubebuilderx.ConditionSatisfied
 }
diff --git a/pkg/controller/instanceset/reconciler_status.go b/pkg/controller/instanceset/reconciler_status.go
index 640d21b40..6c4810efa 100644
--- a/pkg/controller/instanceset/reconciler_status.go
+++ b/pkg/controller/instanceset/reconciler_status.go
@@ -189,6 +189,9 @@ func (r *statusReconciler) Reconcile(tree *kubebuilderx.ObjectTree) (kubebuilder
 	// 4. set members status
 	setMembersStatus(its, podList)
 
+	// 5. set instance status
+	setInstanceStatus(its, podList)
+
 	if its.Spec.MinReadySeconds > 0 && availableReplicas != readyReplicas {
 		return kubebuilderx.RetryAfter(time.Second), nil
 	}
@@ -327,3 +330,55 @@ func sortMembersStatus(membersStatus []workloads.MemberStatus, rolePriorityMap m
 	}
 	baseSort(membersStatus, getNameNOrdinalFunc, getRolePriorityFunc, true)
 }
+
+func setInstanceStatus(its *workloads.InstanceSet, pods []*corev1.Pod) {
+	// compose new instance status
+	newInstanceStatus := make([]workloads.InstanceStatus, 0)
+	for _, pod := range pods {
+		instanceStatus := workloads.InstanceStatus{
+			PodName: pod.Name,
+		}
+		newInstanceStatus = append(newInstanceStatus, instanceStatus)
+	}
+
+	syncInstanceConfigStatus(its, newInstanceStatus)
+
+	its.Status.InstanceStatus = newInstanceStatus
+}
+
+func syncInstanceConfigStatus(its *workloads.InstanceSet, instanceStatus []workloads.InstanceStatus) {
+	if its.Status.InstanceStatus == nil {
+		// initialize
+		configs := make([]workloads.InstanceConfigStatus, 0)
+		for _, config := range its.Spec.Configs {
+			configs = append(configs, workloads.InstanceConfigStatus{
+				Name:       config.Name,
+				Generation: config.Generation,
+			})
+		}
+		for i := range instanceStatus {
+			instanceStatus[i].Configs = configs
+		}
+	} else {
+		// HACK: copy the existing config status from the current its.status.instanceStatus
+		configs := sets.New[string]()
+		for _, config := range its.Spec.Configs {
+			configs.Insert(config.Name)
+		}
+		for i, newStatus := range instanceStatus {
+			for _, status := range its.Status.InstanceStatus {
+				if status.PodName == newStatus.PodName {
+					if instanceStatus[i].Configs == nil {
+						instanceStatus[i].Configs = make([]workloads.InstanceConfigStatus, 0)
+					}
+					for j, config := range status.Configs {
+						if configs.Has(config.Name) {
+							instanceStatus[i].Configs = append(instanceStatus[i].Configs, status.Configs[j])
+						}
+					}
+					break
+				}
+			}
+		}
+	}
+}
diff --git a/pkg/controller/instanceset/reconciler_update.go b/pkg/controller/instanceset/reconciler_update.go
index 243f47a3a..48ed676e6 100644
--- a/pkg/controller/instanceset/reconciler_update.go
+++ b/pkg/controller/instanceset/reconciler_update.go
@@ -22,6 +22,7 @@ package instanceset
 import (
 	"errors"
 	"fmt"
+	"slices"
 	"time"
 
 	corev1 "k8s.io/api/core/v1"
@@ -36,6 +37,7 @@ import (
 	"github.com/apecloud/kubeblocks/pkg/controller/kubebuilderx"
 	"github.com/apecloud/kubeblocks/pkg/controller/lifecycle"
 	"github.com/apecloud/kubeblocks/pkg/controller/model"
+	intctrlutil "github.com/apecloud/kubeblocks/pkg/controllerutil"
 )
 
 // updateReconciler handles the updates of instances based on the UpdateStrategy.
@@ -57,7 +59,7 @@ func (r *updateReconciler) PreCondition(tree *kubebuilderx.ObjectTree) *kubebuil
 	}
 	its, _ := tree.GetRoot().(*workloads.InstanceSet)
 	if err := validateSpec(its, tree); err != nil {
-		return kubebuilderx.CheckResultWithError(err)
+		return kubebuilderx.ConditionUnsatisfiedWithError(err)
 	}
 	return kubebuilderx.ConditionSatisfied
 }
@@ -117,7 +119,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 {
-		plan := NewUpdatePlan(*its, oldPodList, IsPodUpdated)
+		plan := NewUpdatePlan(*its, oldPodList, r.isPodOrConfigUpdated)
 		podsToBeUpdated, err := plan.Execute()
 		if err != nil {
 			return kubebuilderx.Continue, err
@@ -196,6 +198,18 @@ func (r *updateReconciler) Reconcile(tree *kubebuilderx.ObjectTree) (kubebuilder
 			}
 			updatingPods++
 		}
+
+		// actively reload the new configuration when the pod or container has not been updated
+		if updatePolicy == NoOpsPolicy {
+			allUpdated, err := r.reconfigure(tree, its, pod)
+			if err != nil {
+				return kubebuilderx.Continue, err
+			}
+			if !allUpdated {
+				updatingPods++
+			}
+		}
+
 		updatedPods++
 	}
 	if !isBlocked {
@@ -212,17 +226,7 @@ func (r *updateReconciler) switchover(tree *kubebuilderx.ObjectTree, its *worklo
 		return nil
 	}
 
-	clusterName, err := func() (string, error) {
-		var clusterName string
-		if its.Labels != nil {
-			clusterName = its.Labels[constant.AppInstanceLabelKey]
-		}
-		if len(clusterName) == 0 {
-			return "", fmt.Errorf("InstanceSet %s/%s has no label %s", its.Namespace, its.Name, constant.AppInstanceLabelKey)
-		}
-		return clusterName, nil
-
-	}()
+	clusterName, err := r.clusterName(its)
 	if err != nil {
 		return err
 	}
@@ -255,6 +259,138 @@ func (r *updateReconciler) switchover(tree *kubebuilderx.ObjectTree, its *worklo
 	return nil
 }
 
+func (r *updateReconciler) reconfigure(tree *kubebuilderx.ObjectTree, its *workloads.InstanceSet, pod *corev1.Pod) (bool, error) {
+	allUpdated := true
+	for _, config := range its.Spec.Configs {
+		if !r.isConfigUpdated(its, pod, config) {
+			allUpdated = false
+			if err := r.reconfigureConfig(tree, its, pod, config); err != nil {
+				return false, err
+			}
+		}
+		// TODO: compose the status from pods but not the its spec and status
+		r.setInstanceConfigStatus(its, pod, config)
+	}
+	return allUpdated, nil
+}
+
+func (r *updateReconciler) reconfigureConfig(tree *kubebuilderx.ObjectTree, its *workloads.InstanceSet, pod *corev1.Pod, config workloads.ConfigTemplate) error {
+	if config.Reconfigure == nil {
+		return nil // skip
+	}
+
+	clusterName, err := r.clusterName(its)
+	if err != nil {
+		return err
+	}
+
+	lifecycleActions := &kbappsv1.ComponentLifecycleActions{
+		Reconfigure: config.Reconfigure,
+	}
+	templateVars := func() map[string]any {
+		if its.Spec.TemplateVars == nil {
+			return nil
+		}
+		m := make(map[string]any)
+		for k, v := range its.Spec.TemplateVars {
+			m[k] = v
+		}
+		return m
+	}()
+	lfa, err := lifecycle.New(its.Namespace, clusterName, its.Name, lifecycleActions, templateVars, pod)
+	if err != nil {
+		return err
+	}
+
+	if len(config.ReconfigureActionName) == 0 {
+		err = lfa.Reconfigure(tree.Context, nil, nil, config.Parameters)
+	} else {
+		err = lfa.UserDefined(tree.Context, nil, nil, config.ReconfigureActionName, config.Reconfigure, config.Parameters)
+	}
+	if err != nil {
+		if errors.Is(err, lifecycle.ErrActionNotDefined) {
+			return nil
+		}
+		if errors.Is(err, lifecycle.ErrPreconditionFailed) {
+			return intctrlutil.NewDelayedRequeueError(time.Second,
+				fmt.Sprintf("replicas not up-to-date when reconfiguring: %s", err.Error()))
+		}
+		return err
+	}
+	tree.Logger.Info("successfully reconfigure the pod", "pod", pod.Name, "generation", config.Generation)
+	return nil
+}
+
+func (r *updateReconciler) setInstanceConfigStatus(its *workloads.InstanceSet, pod *corev1.Pod, config workloads.ConfigTemplate) {
+	if its.Status.InstanceStatus == nil {
+		its.Status.InstanceStatus = make([]workloads.InstanceStatus, 0)
+	}
+	idx := slices.IndexFunc(its.Status.InstanceStatus, func(instance workloads.InstanceStatus) bool {
+		return instance.PodName == pod.Name
+	})
+	if idx < 0 {
+		its.Status.InstanceStatus = append(its.Status.InstanceStatus, workloads.InstanceStatus{PodName: pod.Name})
+		idx = len(its.Status.InstanceStatus) - 1
+	}
+
+	if its.Status.InstanceStatus[idx].Configs == nil {
+		its.Status.InstanceStatus[idx].Configs = make([]workloads.InstanceConfigStatus, 0)
+	}
+	status := workloads.InstanceConfigStatus{
+		Name:       config.Name,
+		Generation: config.Generation,
+	}
+	for i, configStatus := range its.Status.InstanceStatus[idx].Configs {
+		if configStatus.Name == config.Name {
+			its.Status.InstanceStatus[idx].Configs[i] = status
+			return
+		}
+	}
+	its.Status.InstanceStatus[idx].Configs = append(its.Status.InstanceStatus[idx].Configs, status)
+}
+
+func (r *updateReconciler) isPodOrConfigUpdated(its *workloads.InstanceSet, pod *corev1.Pod) (bool, error) {
+	policy, err := getPodUpdatePolicy(its, pod)
+	if err != nil {
+		return false, err
+	}
+	if policy != NoOpsPolicy {
+		return false, nil
+	}
+	for _, config := range its.Spec.Configs {
+		if !r.isConfigUpdated(its, pod, config) {
+			return false, nil
+		}
+	}
+	return true, nil
+}
+
+func (r *updateReconciler) isConfigUpdated(its *workloads.InstanceSet, pod *corev1.Pod, config workloads.ConfigTemplate) bool {
+	idx := slices.IndexFunc(its.Status.InstanceStatus, func(instance workloads.InstanceStatus) bool {
+		return instance.PodName == pod.Name
+	})
+	if idx < 0 {
+		return true // new pod provisioned
+	}
+	for _, configStatus := range its.Status.InstanceStatus[idx].Configs {
+		if configStatus.Name == config.Name {
+			return config.Generation <= configStatus.Generation
+		}
+	}
+	return config.Generation <= 0
+}
+
+func (r *updateReconciler) clusterName(its *workloads.InstanceSet) (string, error) {
+	var clusterName string
+	if its.Labels != nil {
+		clusterName = its.Labels[constant.AppInstanceLabelKey]
+	}
+	if len(clusterName) == 0 {
+		return "", fmt.Errorf("InstanceSet %s/%s has no label %s", its.Namespace, its.Name, constant.AppInstanceLabelKey)
+	}
+	return clusterName, nil
+}
+
 func buildBlockedCondition(its *workloads.InstanceSet, message string) *metav1.Condition {
 	return &metav1.Condition{
 		Type:               string(workloads.InstanceUpdateRestricted),
diff --git a/pkg/controller/kubebuilderx/controller.go b/pkg/controller/kubebuilderx/controller.go
index d1597044e..383157fef 100644
--- a/pkg/controller/kubebuilderx/controller.go
+++ b/pkg/controller/kubebuilderx/controller.go
@@ -37,9 +37,23 @@ import (
 // 1. expose EventRecorder & Logger
 // 2. parallel workflow-style reconciler chain
 
+// Controller interface should be implemented by a controller using kubebuilderx.
+// Typically these methods are chained like:
+//
+//	kubebuilderx.NewController(ctx, client, req, recorder, logger).
+//		Prepare(treeloader).
+//		Do(reconciler).
+//		Do(moreReconciler).
+//		Commit()
 type Controller interface {
+	// Prepare loads the object tree from Kubernetes.
 	Prepare(TreeLoader) Controller
+
+	// Do is the computation phase. It contains the business logic and modifies the object tree.
 	Do(...Reconciler) Controller
+
+	// Commit is the plan execution phase. It computes the difference between
+	// the old and new object trees, and applies the difference to Kubernetes.
 	Commit() (ctrl.Result, error)
 }
 
diff --git a/pkg/controller/kubebuilderx/doc.go b/pkg/controller/kubebuilderx/doc.go
index 953b8633a..77b551824 100644
--- a/pkg/controller/kubebuilderx/doc.go
+++ b/pkg/controller/kubebuilderx/doc.go
@@ -21,12 +21,15 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 // which abstracts the reconciliation process into two stages.
 // The first stage is the pure computation phase, where the goal is to generate an execution plan.
 // The second stage is the plan execution phase, responsible for applying the changes computed in the first stage to the K8s API server.
+//
 // The design choice of making the first stage purely computational serves two purposes.
-// Firstly, it allows leveraging the experience and patterns from functional programming to make the code more robust.
-// Secondly, it enables breaking down complex business logic into smaller units, facilitating testing.
+//   - it allows leveraging the experience and patterns from functional programming to make the code more robust.
+//   - it enables breaking down complex business logic into smaller units, facilitating testing.
+//
 // The new framework retains this concept while attempting to address the following issues of the original approach:
-// 1. The low-level exposure of the DAG data structure, which should be abstracted away.
-// 2. The execution of business logic code being deferred, making step-by-step tracing and debugging challenging.
+//   - The low-level exposure of the DAG data structure, which should be abstracted away.
+//   - The execution of business logic code being deferred, making step-by-step tracing and debugging challenging.
+//
 // Additionally, the new framework further abstracts the concept of object snapshots into an ObjectTree,
 // making it easier to apply the experience of editing a group of related objects using kubectl.
 //
diff --git a/pkg/controller/kubebuilderx/reconciler.go b/pkg/controller/kubebuilderx/reconciler.go
index 1053159b6..18bbe993f 100644
--- a/pkg/controller/kubebuilderx/reconciler.go
+++ b/pkg/controller/kubebuilderx/reconciler.go
@@ -55,20 +55,25 @@ type CheckResult struct {
 	Err       error
 }
 
-var ConditionUnsatisfied = &CheckResult{}
-var ConditionSatisfied = &CheckResult{Satisfied: true}
+var (
+	// ConditionSatisfied means the corresponding Reconcile() should be invoked
+	ConditionSatisfied = &CheckResult{Satisfied: true}
+
+	// ConditionUnsatisfied means the corresponding Reconcile() should be skipped
+	ConditionUnsatisfied = &CheckResult{}
+
+	// ConditionUnsatisfiedWithError means the corresponding and all the following
+	// Reconcile() should be skipped
+	ConditionUnsatisfiedWithError = func(err error) *CheckResult {
+		return &CheckResult{Satisfied: false, Err: err}
+	}
+)
 
 type controlMethod string
 
 const (
-	// cntn tells the control flow to continue
 	cntn controlMethod = "Continue"
-
-	// cmmt tells the control flow to stop and jump to the commit phase
 	cmmt controlMethod = "Commit"
-
-	// rtry tells the control flow to stop, jump to the commit phase
-	// and retry from the beginning with a delay specified by Result.RetryAfter
 	rtry controlMethod = "Retry"
 )
 
@@ -77,22 +82,29 @@ type Result struct {
 	RetryAfter time.Duration
 }
 
-var Continue = Result{Next: cntn}
-var Commit = Result{Next: cmmt}
+var (
+	// Continue tells the control flow to continue
+	Continue = Result{Next: cntn}
+
+	// Commit tells the control flow to stop and jump to the commit phase
+	Commit = Result{Next: cmmt}
+
+	// RetryAfter tells the control flow to stop, jump to the commit phase
+	// and retry from the beginning with a delay specified by `after`.
+	RetryAfter = func(after time.Duration) Result {
+		return Result{Next: rtry, RetryAfter: after}
+	}
+)
 
 var ErrDeepCopyFailed = errors.New("DeepCopyFailed")
 
 type Reconciler interface {
+	// PreCondition should return ConditionSatisfied if Reconcile() should be invoked,
+	// otherwise return ConditionUnsatisfied.
 	PreCondition(*ObjectTree) *CheckResult
-	Reconcile(tree *ObjectTree) (Result, error)
-}
 
-func CheckResultWithError(err error) *CheckResult {
-	return &CheckResult{Satisfied: false, Err: err}
-}
-
-func RetryAfter(after time.Duration) Result {
-	return Result{Next: rtry, RetryAfter: after}
+	// Reconcile contains the business logic and modifies the object tree.
+	Reconcile(tree *ObjectTree) (Result, error)
 }
 
 func (t *ObjectTree) DeepCopy() (*ObjectTree, error) {
diff --git a/pkg/controller/lifecycle/kbagent.go b/pkg/controller/lifecycle/kbagent.go
index 7b560c074..4804434e0 100644
--- a/pkg/controller/lifecycle/kbagent.go
+++ b/pkg/controller/lifecycle/kbagent.go
@@ -113,11 +113,9 @@ func (a *kbagent) MemberLeave(ctx context.Context, cli client.Reader, opts *Opti
 	return a.ignoreOutput(a.checkedCallAction(ctx, cli, a.lifecycleActions.MemberLeave, lfa, opts))
 }
 
-func (a *kbagent) Reconfigure(ctx context.Context, cli client.Reader, opts *Options, created, removed, updated string) error {
+func (a *kbagent) Reconfigure(ctx context.Context, cli client.Reader, opts *Options, args map[string]string) error {
 	lfa := &reconfigure{
-		created: created,
-		removed: removed,
-		updated: updated,
+		args: args,
 	}
 	return a.ignoreOutput(a.checkedCallAction(ctx, cli, a.lifecycleActions.Reconfigure, lfa, opts))
 }
diff --git a/pkg/controller/lifecycle/lfa_replica.go b/pkg/controller/lifecycle/lfa_replica.go
index 4ea7872f6..f991d8d7a 100644
--- a/pkg/controller/lifecycle/lfa_replica.go
+++ b/pkg/controller/lifecycle/lfa_replica.go
@@ -40,9 +40,7 @@ func FileTemplateChanges(created, removed, updated string) map[string]string {
 }
 
 type reconfigure struct {
-	created string
-	removed string
-	updated string
+	args map[string]string
 }
 
 var _ lifecycleAction = &reconfigure{}
@@ -57,5 +55,5 @@ func (a *reconfigure) parameters(ctx context.Context, cli client.Reader) (map[st
 	// - KB_CONFIG_FILES_CREATED: file1,file2...
 	// - KB_CONFIG_FILES_REMOVED: file1,file2...
 	// - KB_CONFIG_FILES_UPDATED: file1:checksum1,file2:checksum2...
-	return FileTemplateChanges(a.created, a.removed, a.updated), nil
+	return a.args, nil
 }
diff --git a/pkg/controller/lifecycle/lifecycle.go b/pkg/controller/lifecycle/lifecycle.go
index d7edf3b5d..2bf9496f8 100644
--- a/pkg/controller/lifecycle/lifecycle.go
+++ b/pkg/controller/lifecycle/lifecycle.go
@@ -52,7 +52,7 @@ type Lifecycle interface {
 
 	// Readwrite(ctx context.Context, cli client.Reader, opts *Options) error
 
-	Reconfigure(ctx context.Context, cli client.Reader, opts *Options, created, removed, updated string) error
+	Reconfigure(ctx context.Context, cli client.Reader, opts *Options, args map[string]string) error
 
 	AccountProvision(ctx context.Context, cli client.Reader, opts *Options, statement, user, password string) error
 
diff --git a/pkg/controller/plan/restore.go b/pkg/controller/plan/restore.go
index 0a3c70e17..95aa79fb5 100644
--- a/pkg/controller/plan/restore.go
+++ b/pkg/controller/plan/restore.go
@@ -167,6 +167,7 @@ func (r *RestoreManager) BuildPrepareDataRestore(comp *component.SynthesizedComp
 
 	var templates []dpv1alpha1.RestoreVolumeClaim
 	pvcLabels := constant.GetCompLabels(r.Cluster.Name, comp.Name)
+	intctrlutil.MergeMetadataMapInplace(comp.Labels, &pvcLabels)
 	// TODO: create pvc by the volumeClaimTemplates of instance template if it is necessary.
 	for _, v := range comp.VolumeClaimTemplates {
 		if !dputils.ExistTargetVolume(targetVolumes, v.Name) {
diff --git a/pkg/controllerutil/config_util.go b/pkg/controllerutil/config_util.go
index ad62986e6..cf22c6ae7 100644
--- a/pkg/controllerutil/config_util.go
+++ b/pkg/controllerutil/config_util.go
@@ -133,8 +133,8 @@ func fromUpdatedConfig(m map[string]string, sets *set.LinkedHashSetString) map[s
 	return r
 }
 
-// IsApplyConfigChanged checks if the configuration is changed
-func IsApplyConfigChanged(configMap *corev1.ConfigMap, item parametersv1alpha1.ConfigTemplateItemDetail) bool {
+// IsApplyUpdatedParameters checks if the configuration is changed
+func IsApplyUpdatedParameters(configMap *corev1.ConfigMap, item parametersv1alpha1.ConfigTemplateItemDetail) bool {
 	if configMap == nil {
 		return false
 	}
@@ -170,16 +170,23 @@ func IsRerender(configMap *corev1.ConfigMap, item parametersv1alpha1.ConfigTempl
 		!reflect.DeepEqual(updatedVersion.CustomTemplates, item.CustomTemplates)
 }
 
-// GetConfigSpecReconcilePhase gets the configuration phase
-func GetConfigSpecReconcilePhase(configMap *corev1.ConfigMap,
+// GetUpdatedParametersReconciledPhase gets the configuration phase
+func GetUpdatedParametersReconciledPhase(configMap *corev1.ConfigMap,
 	item parametersv1alpha1.ConfigTemplateItemDetail,
 	status *parametersv1alpha1.ConfigTemplateItemDetailStatus) parametersv1alpha1.ParameterPhase {
 	if status == nil || status.Phase == "" {
 		return parametersv1alpha1.CCreatingPhase
 	}
-	if !IsApplyConfigChanged(configMap, item) {
+	if !IsApplyUpdatedParameters(configMap, item) {
 		return parametersv1alpha1.CPendingPhase
 	}
+	if status.Phase == parametersv1alpha1.CFinishedPhase {
+		// Check if the cr subresource (status) is the last version.
+		lastRevision, ok := configMap.Annotations[constant.ConfigurationRevision]
+		if !ok || status.UpdateRevision != lastRevision {
+			return parametersv1alpha1.CRunningPhase
+		}
+	}
 	return status.Phase
 }
 
diff --git a/pkg/dataprotection/restore/utils.go b/pkg/dataprotection/restore/utils.go
index 2d1773b34..3987414dc 100644
--- a/pkg/dataprotection/restore/utils.go
+++ b/pkg/dataprotection/restore/utils.go
@@ -353,16 +353,6 @@ func GetRestoreFromBackupAnnotation(
 	if connectionPassword != "" {
 		restoreInfoMap[constant.ConnectionPassword] = connectionPassword
 	}
-	encryptedSystemAccountsString := backup.Annotations[constant.EncryptedSystemAccountsAnnotationKey]
-	if encryptedSystemAccountsString != "" {
-		encryptedSystemAccountsMap := map[string]map[string]string{}
-		_ = json.Unmarshal([]byte(encryptedSystemAccountsString), &encryptedSystemAccountsMap)
-		// only set systemAccounts owned by this component
-		if encryptedSystemAccountsMap[componentName] != nil {
-			encryptedComponentSystemAccountsBytes, _ := json.Marshal(encryptedSystemAccountsMap[componentName])
-			restoreInfoMap[constant.EncryptedSystemAccounts] = string(encryptedComponentSystemAccountsBytes)
-		}
-	}
 	restoreForClusterMap := map[string]map[string]string{}
 	restoreForClusterMap[componentName] = restoreInfoMap
 	bytes, err := json.Marshal(restoreForClusterMap)
diff --git a/pkg/operations/restore.go b/pkg/operations/restore.go
index 4ac61c6fb..3725ef204 100644
--- a/pkg/operations/restore.go
+++ b/pkg/operations/restore.go
@@ -125,7 +125,7 @@ func (r RestoreOpsHandler) ReconcileAction(reqCtx intctrlutil.RequestCtx, cli cl
 	// check if the cluster is running
 	if cluster.Status.Phase == appsv1.RunningClusterPhase {
 		return opsv1alpha1.OpsSucceedPhase, 0, nil
-	} else if cluster.Status.Phase == appsv1.FailedClusterPhase {
+	} else if cluster.Status.Phase == appsv1.FailedClusterPhase || cluster.IsDeleting() {
 		return opsv1alpha1.OpsFailedPhase, 0, fmt.Errorf("restore failed")
 	}
 	return opsv1alpha1.OpsRunningPhase, 0, nil



Test patch:
diff --git a/controllers/apps/cluster/suite_test.go b/controllers/apps/cluster/suite_test.go
index 52749c8d6..e34dd540e 100644
--- a/controllers/apps/cluster/suite_test.go
+++ b/controllers/apps/cluster/suite_test.go
@@ -159,8 +159,6 @@ var _ = BeforeSuite(func() {
 
 	viper.SetDefault("CERT_DIR", "/tmp/k8s-webhook-server/serving-certs")
 	viper.SetDefault(constant.KBToolsImage, "docker.io/apecloud/kubeblocks-tools:latest")
-	viper.SetDefault("PROBE_SERVICE_PORT", 3501)
-	viper.SetDefault("PROBE_SERVICE_LOG_LEVEL", "info")
 	viper.SetDefault("CM_NAMESPACE", "default")
 	viper.SetDefault("HOST_PORT_CM_NAME", "kubeblocks-host-ports")
 	viper.SetDefault(constant.EnableRBACManager, true)
diff --git a/controllers/apps/component/component_controller_test.go b/controllers/apps/component/component_controller_test.go
index 987976244..52cc019ed 100644
--- a/controllers/apps/component/component_controller_test.go
+++ b/controllers/apps/component/component_controller_test.go
@@ -20,7 +20,6 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 package component
 
 import (
-	"context"
 	"fmt"
 	"strconv"
 	"strings"
@@ -29,7 +28,6 @@ import (
 	. "github.com/onsi/ginkgo/v2"
 	. "github.com/onsi/gomega"
 
-	"github.com/golang/mock/gomock"
 	"github.com/sethvargo/go-password/password"
 	"golang.org/x/exp/maps"
 	appsv1 "k8s.io/api/apps/v1"
@@ -53,7 +51,6 @@ import (
 	intctrlutil "github.com/apecloud/kubeblocks/pkg/controllerutil"
 	"github.com/apecloud/kubeblocks/pkg/generics"
 	kbacli "github.com/apecloud/kubeblocks/pkg/kbagent/client"
-	kbagentproto "github.com/apecloud/kubeblocks/pkg/kbagent/proto"
 	testapps "github.com/apecloud/kubeblocks/pkg/testutil/apps"
 	testk8s "github.com/apecloud/kubeblocks/pkg/testutil/k8s"
 	viper "github.com/apecloud/kubeblocks/pkg/viperx"
@@ -1634,21 +1631,6 @@ var _ = Describe("Component Controller", func() {
 	}
 
 	testReconfigure := func(compName, compDefName, fileTemplate string) {
-		By("mock reconfigure action calls")
-		var (
-			reconfigure string
-			parameters  map[string]string
-		)
-		testapps.MockKBAgentClient(func(recorder *kbacli.MockClientMockRecorder) {
-			recorder.Action(gomock.Any(), gomock.Any()).DoAndReturn(func(ctx context.Context, req kbagentproto.ActionRequest) (kbagentproto.ActionResponse, error) {
-				if req.Action == "reconfigure" || strings.HasPrefix(req.Action, "udf-reconfigure") {
-					reconfigure = req.Action
-					parameters = req.Parameters
-				}
-				return kbagentproto.ActionResponse{}, nil
-			}).AnyTimes()
-		})
-
 		createCompObj(compName, compDefName, nil)
 
 		By("check the file template object")
@@ -1660,12 +1642,6 @@ var _ = Describe("Component Controller", func() {
 			g.Expect(cm.Data).Should(HaveKeyWithValue("level", "info"))
 		})).Should(Succeed())
 
-		By("mock pods")
-		pods := mockPodsForTest(clusterKey.Name, compName, compDefName, int(compObj.Spec.Replicas))
-		for i := range pods {
-			Expect(testCtx.CheckedCreateObj(testCtx.Ctx, pods[i])).Should(Succeed())
-		}
-
 		By("update the config template variables")
 		Expect(testapps.GetAndChangeObj(&testCtx, compKey, func(comp *kbappsv1.Component) {
 			comp.Spec.Configs = []kbappsv1.ClusterComponentConfig{
@@ -1683,31 +1659,20 @@ var _ = Describe("Component Controller", func() {
 			g.Expect(cm.Data).Should(HaveKeyWithValue("level", "debug"))
 		})).Should(Succeed())
 
-		By("check the reconfigure action call")
-		Eventually(func(g Gomega) {
-			g.Expect(reconfigure).Should(Equal("reconfigure"))
-			g.Expect(parameters).ShouldNot(BeNil())
-			g.Expect(parameters).Should(HaveKey("KB_CONFIG_FILES_UPDATED"))
-			g.Expect(parameters["KB_CONFIG_FILES_UPDATED"]).Should(ContainSubstring("level"))
-		}).Should(Succeed())
+		By("check the workload updated")
+		itsKey := compKey
+		Eventually(testapps.CheckObj(&testCtx, itsKey, func(g Gomega, its *workloads.InstanceSet) {
+			g.Expect(its.Spec.Configs).Should(HaveLen(1))
+			g.Expect(its.Spec.Configs[0].Name).Should(Equal(fileTemplate))
+			g.Expect(its.Spec.Configs[0].Generation).Should(Equal(its.Generation))
+			g.Expect(its.Spec.Configs[0].Reconfigure).ShouldNot(BeNil())
+			g.Expect(its.Spec.Configs[0].ReconfigureActionName).Should(BeEmpty())
+			g.Expect(its.Spec.Configs[0].Parameters).Should(HaveKey("KB_CONFIG_FILES_UPDATED"))
+			g.Expect(its.Spec.Configs[0].Parameters["KB_CONFIG_FILES_UPDATED"]).Should(ContainSubstring("level"))
+		})).Should(Succeed())
 	}
 
 	testReconfigureUDF := func(compName, compDefName, fileTemplate string) {
-		By("mock reconfigure action calls")
-		var (
-			reconfigure string
-			parameters  map[string]string
-		)
-		testapps.MockKBAgentClient(func(recorder *kbacli.MockClientMockRecorder) {
-			recorder.Action(gomock.Any(), gomock.Any()).DoAndReturn(func(ctx context.Context, req kbagentproto.ActionRequest) (kbagentproto.ActionResponse, error) {
-				if req.Action == "reconfigure" || strings.HasPrefix(req.Action, "udf-reconfigure") {
-					reconfigure = req.Action
-					parameters = req.Parameters
-				}
-				return kbagentproto.ActionResponse{}, nil
-			}).AnyTimes()
-		})
-
 		createCompObj(compName, compDefName, func(f *testapps.MockComponentFactory) {
 			f.SetConfigs([]kbappsv1.ClusterComponentConfig{
 				{
@@ -1729,12 +1694,6 @@ var _ = Describe("Component Controller", func() {
 			g.Expect(cm.Data).Should(HaveKeyWithValue("level", "debug"))
 		})).Should(Succeed())
 
-		By("mock pods")
-		pods := mockPodsForTest(clusterKey.Name, compName, compDefName, int(compObj.Spec.Replicas))
-		for i := range pods {
-			Expect(testCtx.CheckedCreateObj(testCtx.Ctx, pods[i])).Should(Succeed())
-		}
-
 		By("update the config template variables")
 		Expect(testapps.GetAndChangeObj(&testCtx, compKey, func(comp *kbappsv1.Component) {
 			comp.Spec.Configs[0].Variables = map[string]string{
@@ -1747,88 +1706,21 @@ var _ = Describe("Component Controller", func() {
 			g.Expect(cm.Data).Should(HaveKeyWithValue("level", "warn"))
 		})).Should(Succeed())
 
-		By("check the reconfigure action call")
-		Eventually(func(g Gomega) {
-			g.Expect(reconfigure).Should(Equal(fmt.Sprintf("udf-reconfigure-%s", fileTemplate)))
-			g.Expect(parameters).ShouldNot(BeNil())
-			g.Expect(parameters).Should(HaveKey("KB_CONFIG_FILES_UPDATED"))
-			g.Expect(parameters["KB_CONFIG_FILES_UPDATED"]).Should(ContainSubstring("level"))
-		}).Should(Succeed())
-	}
-
-	testReconfigureStatus := func(compName, compDefName, fileTemplate string) {
-		By("mock reconfigure action calls")
-		var (
-			replicas  = 3
-			callTimes = 0
-		)
-		testapps.MockKBAgentClient(func(recorder *kbacli.MockClientMockRecorder) {
-			recorder.Action(gomock.Any(), gomock.Any()).DoAndReturn(func(ctx context.Context, req kbagentproto.ActionRequest) (kbagentproto.ActionResponse, error) {
-				if req.Action == "reconfigure" || strings.HasPrefix(req.Action, "udf-reconfigure") {
-					callTimes += 1
-					if callTimes >= replicas {
-						return kbagentproto.ActionResponse{}, fmt.Errorf("mock internal error")
-					}
-				}
-				return kbagentproto.ActionResponse{}, nil
-			}).AnyTimes()
-		})
-
-		createCompObj(compName, compDefName, func(f *testapps.MockComponentFactory) {
-			f.SetReplicas(int32(replicas))
-		})
-
-		By("check the file template object")
-		fileTemplateCMKey := types.NamespacedName{
-			Namespace: testCtx.DefaultNamespace,
-			Name:      fileTemplateObjectName(&component.SynthesizedComponent{FullCompName: compKey.Name}, fileTemplate),
-		}
-		Eventually(testapps.CheckObj(&testCtx, fileTemplateCMKey, func(g Gomega, cm *corev1.ConfigMap) {
-			g.Expect(cm.Data).Should(HaveKeyWithValue("level", "info"))
-		})).Should(Succeed())
-
-		By("mock pods")
-		pods := mockPodsForTest(clusterKey.Name, compName, compDefName, int(compObj.Spec.Replicas))
-		for i := range pods {
-			Expect(testCtx.CheckedCreateObj(testCtx.Ctx, pods[i])).Should(Succeed())
-		}
-
-		By("update the config template variables")
-		Expect(testapps.GetAndChangeObj(&testCtx, compKey, func(comp *kbappsv1.Component) {
-			comp.Spec.Configs = []kbappsv1.ClusterComponentConfig{
-				{
-					Name: ptr.To(fileTemplate),
-					Variables: map[string]string{
-						"LOG_LEVEL": "debug",
-					},
-				},
-			}
-		})()).Should(Succeed())
-
-		By("check the file template object again")
-		Eventually(testapps.CheckObj(&testCtx, fileTemplateCMKey, func(g Gomega, cm *corev1.ConfigMap) {
-			g.Expect(cm.Data).Should(HaveKeyWithValue("level", "debug"))
-		})).Should(Succeed())
-
-		By("check the reconfigure action call")
-		Eventually(func(g Gomega) {
-			g.Expect(callTimes >= replicas).Should(BeTrue())
-		}).Should(Succeed())
-
-		By("check the replicas status")
+		By("check the workload updated")
 		itsKey := compKey
 		Eventually(testapps.CheckObj(&testCtx, itsKey, func(g Gomega, its *workloads.InstanceSet) {
-			replicas, err := component.GetReplicasStatusFunc(its, func(r component.ReplicaStatus) bool {
-				g.Expect(r.Reconfigured).ShouldNot(BeNil())
-				return len(*r.Reconfigured) > 0
-			})
-			g.Expect(err).Should(BeNil())
-			g.Expect(len(replicas)).Should(Equal(1))
+			g.Expect(its.Spec.Configs).Should(HaveLen(1))
+			g.Expect(its.Spec.Configs[0].Name).Should(Equal(fileTemplate))
+			g.Expect(its.Spec.Configs[0].Generation).Should(Equal(its.Generation))
+			g.Expect(its.Spec.Configs[0].Reconfigure).ShouldNot(BeNil())
+			g.Expect(its.Spec.Configs[0].ReconfigureActionName).Should(Equal(fmt.Sprintf("reconfigure-%s", fileTemplate)))
+			g.Expect(its.Spec.Configs[0].Parameters).Should(HaveKey("KB_CONFIG_FILES_UPDATED"))
+			g.Expect(its.Spec.Configs[0].Parameters["KB_CONFIG_FILES_UPDATED"]).Should(ContainSubstring("level"))
 		})).Should(Succeed())
 	}
 
-	testReconfigureStatusCanceled := func(compName, compDefName, fileTemplate string) {
-		testReconfigureStatus(compName, compDefName, fileTemplate)
+	testReconfigureVolumeChanged := func(compName, compDefName, fileTemplate string) {
+		testReconfigure(compName, compDefName, fileTemplate)
 
 		By("update the cmpd to add a new config template (volume)")
 		compDefKey := client.ObjectKeyFromObject(compDefObj)
@@ -1855,16 +1747,10 @@ var _ = Describe("Component Controller", func() {
 		}
 		Eventually(testapps.CheckObjExists(&testCtx, newFileTemplateCMKey, &corev1.ConfigMap{}, true)).Should(Succeed())
 
-		By("check the replicas status")
+		By("check the workload updated")
 		itsKey := compKey
 		Eventually(testapps.CheckObj(&testCtx, itsKey, func(g Gomega, its *workloads.InstanceSet) {
-			// all the reconfigure tasks of all replicas have been canceled
-			replicas, err := component.GetReplicasStatusFunc(its, func(r component.ReplicaStatus) bool {
-				g.Expect(r.Reconfigured).Should(BeNil())
-				return true
-			})
-			g.Expect(err).Should(BeNil())
-			g.Expect(len(replicas)).Should(Equal(3))
+			g.Expect(its.Spec.Configs).Should(BeNil())
 		})).Should(Succeed())
 	}
 
@@ -2427,12 +2313,8 @@ var _ = Describe("Component Controller", func() {
 			testReconfigureUDF(defaultCompName, compDefObj.Name, fileTemplate)
 		})
 
-		It("reconfigure - status", func() {
-			testReconfigureStatus(defaultCompName, compDefObj.Name, fileTemplate)
-		})
-
-		It("reconfigure - canceled by volumes change", func() {
-			testReconfigureStatusCanceled(defaultCompName, compDefObj.Name, fileTemplate)
+		It("reconfigure - volume changed", func() {
+			testReconfigureVolumeChanged(defaultCompName, compDefObj.Name, fileTemplate)
 		})
 	})
 })
diff --git a/controllers/apps/component/suite_test.go b/controllers/apps/component/suite_test.go
index c3978784c..0b2a348ac 100644
--- a/controllers/apps/component/suite_test.go
+++ b/controllers/apps/component/suite_test.go
@@ -164,8 +164,6 @@ var _ = BeforeSuite(func() {
 
 	viper.SetDefault("CERT_DIR", "/tmp/k8s-webhook-server/serving-certs")
 	viper.SetDefault(constant.KBToolsImage, "docker.io/apecloud/kubeblocks-tools:latest")
-	viper.SetDefault("PROBE_SERVICE_PORT", 3501)
-	viper.SetDefault("PROBE_SERVICE_LOG_LEVEL", "info")
 	viper.SetDefault("CM_NAMESPACE", "default")
 	viper.SetDefault("HOST_PORT_CM_NAME", "kubeblocks-host-ports")
 	viper.SetDefault(constant.EnableRBACManager, true)
diff --git a/controllers/apps/component/transformer_component_workload_test.go b/controllers/apps/component/transformer_component_workload_test.go
index 3f727c91c..649a20476 100644
--- a/controllers/apps/component/transformer_component_workload_test.go
+++ b/controllers/apps/component/transformer_component_workload_test.go
@@ -31,7 +31,6 @@ import (
 	"github.com/apecloud/kubeblocks/pkg/controller/component"
 	"github.com/apecloud/kubeblocks/pkg/controller/graph"
 	"github.com/apecloud/kubeblocks/pkg/controller/model"
-	intctrlutil "github.com/apecloud/kubeblocks/pkg/controllerutil"
 	kbacli "github.com/apecloud/kubeblocks/pkg/kbagent/client"
 	kbagentproto "github.com/apecloud/kubeblocks/pkg/kbagent/proto"
 	testapps "github.com/apecloud/kubeblocks/pkg/testutil/apps"
@@ -158,8 +157,12 @@ var _ = Describe("Component Workload Operations Test", func() {
 				GetObject()
 
 			ops = &componentWorkloadOps{
+				transCtx: &componentTransformContext{
+					Context:       ctx,
+					Logger:        logger,
+					EventRecorder: clusterRecorder,
+				},
 				cli:            k8sClient,
-				reqCtx:         intctrlutil.RequestCtx{Ctx: ctx, Log: logger, Recorder: clusterRecorder},
 				component:      comp,
 				synthesizeComp: synthesizeComp,
 				runningITS:     mockITS,
diff --git a/controllers/apps/suite_test.go b/controllers/apps/suite_test.go
index 50f9ba9c4..4264e7e2f 100644
--- a/controllers/apps/suite_test.go
+++ b/controllers/apps/suite_test.go
@@ -145,8 +145,6 @@ var _ = BeforeSuite(func() {
 
 	viper.SetDefault("CERT_DIR", "/tmp/k8s-webhook-server/serving-certs")
 	viper.SetDefault(constant.KBToolsImage, "docker.io/apecloud/kubeblocks-tools:latest")
-	viper.SetDefault("PROBE_SERVICE_PORT", 3501)
-	viper.SetDefault("PROBE_SERVICE_LOG_LEVEL", "info")
 	viper.SetDefault("CM_NAMESPACE", "default")
 	viper.SetDefault("HOST_PORT_CM_NAME", "kubeblocks-host-ports")
 	viper.SetDefault(constant.EnableRBACManager, true)
diff --git a/controllers/apps/util/utils_test.go b/controllers/apps/util/utils_test.go
index f34d5fc6f..75a349251 100644
--- a/controllers/apps/util/utils_test.go
+++ b/controllers/apps/util/utils_test.go
@@ -20,6 +20,7 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 package util
 
 import (
+	"context"
 	"fmt"
 	"reflect"
 	"testing"
@@ -27,11 +28,17 @@ import (
 	"github.com/stretchr/testify/assert"
 	appsv1 "k8s.io/api/apps/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/runtime"
 	"k8s.io/utils/pointer"
 	"sigs.k8s.io/controller-runtime/pkg/client"
+	"sigs.k8s.io/controller-runtime/pkg/client/fake"
 
 	kbappsv1 "github.com/apecloud/kubeblocks/apis/apps/v1"
+	dpv1alpha1 "github.com/apecloud/kubeblocks/apis/dataprotection/v1alpha1"
 	workloads "github.com/apecloud/kubeblocks/apis/workloads/v1"
+	"github.com/apecloud/kubeblocks/pkg/constant"
+	intctrlutil "github.com/apecloud/kubeblocks/pkg/controllerutil"
+	viper "github.com/apecloud/kubeblocks/pkg/viperx"
 )
 
 func TestReflect(t *testing.T) {
@@ -79,3 +86,160 @@ func TestIsOwnedByInstanceSet(t *testing.T) {
 	}
 	assert.False(t, IsOwnedByInstanceSet(its))
 }
+
+func TestGetRestoreSystemAccountPassword(t *testing.T) {
+	encryptor := intctrlutil.NewEncryptor(viper.GetString(constant.CfgKeyDPEncryptionKey))
+	encryptedPwd, _ := encryptor.Encrypt([]byte("test-password"))
+
+	tests := []struct {
+		name        string
+		annotations map[string]string
+		backup      *dpv1alpha1.Backup
+		component   string
+		account     string
+		wantPwd     []byte
+		wantErr     bool
+	}{
+		{
+			name: "normal case",
+			annotations: map[string]string{
+				constant.RestoreFromBackupAnnotationKey: `{"comp1":{"name":"backup1","namespace":"default"}}`,
+			},
+			backup: &dpv1alpha1.Backup{
+				ObjectMeta: metav1.ObjectMeta{
+					Name:      "backup1",
+					Namespace: "default",
+					Annotations: map[string]string{
+						constant.EncryptedSystemAccountsAnnotationKey: fmt.Sprintf(`{"comp1":{"account1":"%s"}}`, encryptedPwd),
+					},
+				},
+			},
+			component: "comp1",
+			account:   "account1",
+			wantPwd:   []byte("test-password"),
+			wantErr:   false,
+		},
+		{
+			name: "empty restore annotation",
+			annotations: map[string]string{
+				constant.RestoreFromBackupAnnotationKey: "",
+			},
+			backup:    &dpv1alpha1.Backup{},
+			component: "comp1",
+			account:   "account1",
+			wantPwd:   nil,
+			wantErr:   false,
+		},
+		{
+			name: "invalid restore annotation json",
+			annotations: map[string]string{
+				constant.RestoreFromBackupAnnotationKey: "invalid-json",
+			},
+			backup:    &dpv1alpha1.Backup{},
+			component: "comp1",
+			account:   "account1",
+			wantPwd:   nil,
+			wantErr:   true,
+		},
+		{
+			name: "missing backup name in annotation",
+			annotations: map[string]string{
+				constant.RestoreFromBackupAnnotationKey: `{"comp1":{}}`,
+			},
+			backup:    &dpv1alpha1.Backup{},
+			component: "comp1",
+			account:   "account1",
+			wantPwd:   nil,
+			wantErr:   true,
+		},
+		{
+			name: "missing backup namespace in annotation",
+			annotations: map[string]string{
+				constant.RestoreFromBackupAnnotationKey: `{"comp1":{"name":"backup1"}}`,
+			},
+			backup:    &dpv1alpha1.Backup{},
+			component: "comp1",
+			account:   "account1",
+			wantPwd:   nil,
+			wantErr:   true,
+		},
+		{
+			name: "invalid encrypted accounts json in backup",
+			annotations: map[string]string{
+				constant.RestoreFromBackupAnnotationKey: `{"comp1":{"name":"backup1","namespace":"default"}}`,
+			},
+			backup: &dpv1alpha1.Backup{
+				ObjectMeta: metav1.ObjectMeta{
+					Name:      "backup1",
+					Namespace: "default",
+					Annotations: map[string]string{
+						constant.EncryptedSystemAccountsAnnotationKey: "invalid-json",
+					},
+				},
+			},
+			component: "comp1",
+			account:   "account1",
+			wantPwd:   nil,
+			wantErr:   true,
+		},
+		{
+			name: "account not found in backup",
+			annotations: map[string]string{
+				constant.RestoreFromBackupAnnotationKey: `{"comp1":{"name":"backup1","namespace":"default"}}`,
+			},
+			backup: &dpv1alpha1.Backup{
+				ObjectMeta: metav1.ObjectMeta{
+					Name:      "backup1",
+					Namespace: "default",
+					Annotations: map[string]string{
+						constant.EncryptedSystemAccountsAnnotationKey: `{"comp1":{"other-account":"pwd"}}`,
+					},
+				},
+			},
+			component: "comp1",
+			account:   "account1",
+			wantPwd:   nil,
+			wantErr:   false,
+		},
+		{
+			name: "component not found in encrypted accounts",
+			annotations: map[string]string{
+				constant.RestoreFromBackupAnnotationKey: `{"comp1":{"name":"backup1","namespace":"default"}}`,
+			},
+			backup: &dpv1alpha1.Backup{
+				ObjectMeta: metav1.ObjectMeta{
+					Name:      "backup1",
+					Namespace: "default",
+					Annotations: map[string]string{
+						constant.EncryptedSystemAccountsAnnotationKey: `{"other-comp":{"account1":"pwd"}}`,
+					},
+				},
+			},
+			component: "comp1",
+			account:   "account1",
+			wantPwd:   nil,
+			wantErr:   false,
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			scheme := runtime.NewScheme()
+			_ = dpv1alpha1.AddToScheme(scheme)
+			cli := fake.NewClientBuilder().WithScheme(scheme)
+			if tt.backup != nil {
+				cli = cli.WithObjects(tt.backup)
+			}
+
+			pwd, err := GetRestoreSystemAccountPassword(context.Background(), cli.Build(),
+				tt.annotations, tt.component, tt.account)
+
+			if tt.wantErr {
+				assert.Error(t, err)
+			} else {
+				assert.NoError(t, err)
+				assert.Equal(t, tt.wantPwd, pwd)
+			}
+		})
+	}
+}
diff --git a/controllers/operations/suite_test.go b/controllers/operations/suite_test.go
index 795367734..0d6b7214b 100644
--- a/controllers/operations/suite_test.go
+++ b/controllers/operations/suite_test.go
@@ -174,8 +174,6 @@ var _ = BeforeSuite(func() {
 
 	viper.SetDefault("CERT_DIR", "/tmp/k8s-webhook-server/serving-certs")
 	viper.SetDefault(constant.KBToolsImage, "apecloud/kubeblocks-tools:latest")
-	viper.SetDefault("PROBE_SERVICE_PORT", 3501)
-	viper.SetDefault("PROBE_SERVICE_LOG_LEVEL", "info")
 	viper.SetDefault("CM_NAMESPACE", "default")
 	viper.SetDefault("HOST_PORT_CM_NAME", "kubeblocks-host-ports")
 	viper.SetDefault(constant.EnableRBACManager, true)
diff --git a/controllers/workloads/instanceset_controller_test.go b/controllers/workloads/instanceset_controller_test.go
index 06cb01aca..7af40c331 100644
--- a/controllers/workloads/instanceset_controller_test.go
+++ b/controllers/workloads/instanceset_controller_test.go
@@ -20,11 +20,14 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 package workloads
 
 import (
+	"context"
 	"fmt"
+	"strings"
 
 	. "github.com/onsi/ginkgo/v2"
 	. "github.com/onsi/gomega"
 
+	"github.com/golang/mock/gomock"
 	corev1 "k8s.io/api/core/v1"
 	"k8s.io/apimachinery/pkg/api/resource"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -37,6 +40,8 @@ import (
 	"github.com/apecloud/kubeblocks/pkg/constant"
 	"github.com/apecloud/kubeblocks/pkg/controller/builder"
 	"github.com/apecloud/kubeblocks/pkg/generics"
+	kbacli "github.com/apecloud/kubeblocks/pkg/kbagent/client"
+	kbaproto "github.com/apecloud/kubeblocks/pkg/kbagent/proto"
 	testapps "github.com/apecloud/kubeblocks/pkg/testutil/apps"
 	viper "github.com/apecloud/kubeblocks/pkg/viperx"
 )
@@ -73,7 +78,7 @@ var _ = Describe("InstanceSet Controller", func() {
 		cleanEnv()
 	})
 
-	createITSObj := func(name string, processor func(factory *testapps.MockInstanceSetFactory)) {
+	createITSObj := func(name string, processors ...func(factory *testapps.MockInstanceSetFactory)) {
 		By("create a ITS object")
 		container := corev1.Container{
 			Name:  "foo",
@@ -84,8 +89,10 @@ var _ = Describe("InstanceSet Controller", func() {
 			AddAnnotations(constant.CRDAPIVersionAnnotationKey, workloads.GroupVersion.String()).
 			AddContainer(container).
 			SetReplicas(1)
-		if processor != nil {
-			processor(f)
+		for _, processor := range processors {
+			if processor != nil {
+				processor(f)
+			}
 		}
 		itsObj = f.Create(&testCtx).GetObject()
 		itsKey = client.ObjectKeyFromObject(itsObj)
@@ -290,4 +297,257 @@ var _ = Describe("InstanceSet Controller", func() {
 			Eventually(testapps.CheckObjExists(&testCtx, pvcKey, &corev1.PersistentVolumeClaim{}, true)).Should(Succeed())
 		})
 	})
+
+	Context("reconfigure", func() {
+		It("instance status", func() {
+			createITSObj(itsName, func(f *testapps.MockInstanceSetFactory) {
+				f.AddConfigs(workloads.ConfigTemplate{
+					Name:       "server",
+					Generation: int64(1),
+				})
+			})
+
+			By("check instance status")
+			Eventually(testapps.CheckObj(&testCtx, itsKey, func(g Gomega, its *workloads.InstanceSet) {
+				g.Expect(its.Status.InstanceStatus).Should(HaveLen(1))
+				g.Expect(its.Status.InstanceStatus[0]).Should(Equal(workloads.InstanceStatus{
+					PodName: fmt.Sprintf("%s-0", itsObj.Name),
+					Configs: []workloads.InstanceConfigStatus{
+						{
+							Name:       "server",
+							Generation: int64(1),
+						},
+					},
+				}))
+			})).Should(Succeed())
+		})
+
+		It("reconfigure", func() {
+			By("mock reconfigure action calls")
+			var (
+				reconfigure string
+				parameters  map[string]string
+			)
+			testapps.MockKBAgentClient(func(recorder *kbacli.MockClientMockRecorder) {
+				recorder.Action(gomock.Any(), gomock.Any()).DoAndReturn(func(ctx context.Context, req kbaproto.ActionRequest) (kbaproto.ActionResponse, error) {
+					if req.Action == "reconfigure" || strings.HasPrefix(req.Action, "udf-reconfigure") {
+						reconfigure = req.Action
+						parameters = req.Parameters
+					}
+					return kbaproto.ActionResponse{}, nil
+				}).AnyTimes()
+			})
+
+			createITSObj(itsName, func(f *testapps.MockInstanceSetFactory) {
+				f.SetInstanceUpdateStrategy(&workloads.InstanceUpdateStrategy{
+					Type: kbappsv1.RollingUpdateStrategyType,
+				}).AddConfigs([]workloads.ConfigTemplate{
+					{
+						Name:       "server",
+						Generation: int64(1),
+					},
+					{
+						Name:       "logging",
+						Generation: int64(2),
+					},
+				}...)
+			})
+
+			By("mock pods running and available")
+			podKey := types.NamespacedName{
+				Namespace: itsObj.Namespace,
+				Name:      fmt.Sprintf("%s-0", itsObj.Name),
+			}
+			Expect(testapps.GetAndChangeObjStatus(&testCtx, podKey, func(pod *corev1.Pod) {
+				pod.Status.Phase = corev1.PodRunning
+				pod.Status.Conditions = []corev1.PodCondition{
+					{
+						Type:               corev1.PodReady,
+						Status:             corev1.ConditionTrue,
+						LastTransitionTime: metav1.Now(),
+					},
+				}
+				pod.Status.ContainerStatuses = []corev1.ContainerStatus{
+					{
+						Name: pod.Spec.Containers[0].Name,
+						State: corev1.ContainerState{
+							Running: &corev1.ContainerStateRunning{},
+						},
+						Image: pod.Spec.Containers[0].Image,
+					},
+				}
+			})()).ShouldNot(HaveOccurred())
+
+			By("check the init instance status")
+			Eventually(testapps.CheckObj(&testCtx, itsKey, func(g Gomega, its *workloads.InstanceSet) {
+				g.Expect(its.Status.InstanceStatus).Should(HaveLen(1))
+				g.Expect(its.Status.InstanceStatus[0]).Should(Equal(workloads.InstanceStatus{
+					PodName: fmt.Sprintf("%s-0", itsObj.Name),
+					Configs: []workloads.InstanceConfigStatus{
+						{
+							Name:       "server",
+							Generation: int64(1),
+						},
+						{
+							Name:       "logging",
+							Generation: int64(2),
+						},
+					},
+				}))
+			})).Should(Succeed())
+
+			By("check the reconfigure action not called")
+			Eventually(func(g Gomega) {
+				g.Expect(reconfigure).Should(BeEmpty())
+				g.Expect(parameters).Should(BeNil())
+			}).Should(Succeed())
+
+			By("update configs")
+			Expect(testapps.GetAndChangeObj(&testCtx, itsKey, func(its *workloads.InstanceSet) {
+				its.Spec.Configs[1].Generation = 128
+				its.Spec.Configs[1].Reconfigure = testapps.NewLifecycleAction("reconfigure")
+				its.Spec.Configs[1].ReconfigureActionName = ""
+				its.Spec.Configs[1].Parameters = map[string]string{"foo": "bar"}
+			})()).ShouldNot(HaveOccurred())
+
+			By("check the instance status updated")
+			Eventually(testapps.CheckObj(&testCtx, itsKey, func(g Gomega, its *workloads.InstanceSet) {
+				g.Expect(its.Status.InstanceStatus).Should(HaveLen(1))
+				g.Expect(its.Status.InstanceStatus[0]).Should(Equal(workloads.InstanceStatus{
+					PodName: fmt.Sprintf("%s-0", itsObj.Name),
+					Configs: []workloads.InstanceConfigStatus{
+						{
+							Name:       "server",
+							Generation: int64(1),
+						},
+						{
+							Name:       "logging",
+							Generation: int64(128),
+						},
+					},
+				}))
+			})).Should(Succeed())
+
+			By("check the reconfigure action call")
+			Eventually(func(g Gomega) {
+				g.Expect(reconfigure).Should(Equal("reconfigure"))
+				g.Expect(parameters).ShouldNot(BeNil())
+				g.Expect(parameters).Should(HaveKeyWithValue("foo", "bar"))
+			}).Should(Succeed())
+		})
+
+		It("reconfigure - udf", func() {
+			By("mock reconfigure action calls")
+			var (
+				reconfigure string
+				parameters  map[string]string
+			)
+			testapps.MockKBAgentClient(func(recorder *kbacli.MockClientMockRecorder) {
+				recorder.Action(gomock.Any(), gomock.Any()).DoAndReturn(func(ctx context.Context, req kbaproto.ActionRequest) (kbaproto.ActionResponse, error) {
+					if req.Action == "reconfigure" || strings.HasPrefix(req.Action, "udf-reconfigure") {
+						reconfigure = req.Action
+						parameters = req.Parameters
+					}
+					return kbaproto.ActionResponse{}, nil
+				}).AnyTimes()
+			})
+
+			createITSObj(itsName, func(f *testapps.MockInstanceSetFactory) {
+				f.SetInstanceUpdateStrategy(&workloads.InstanceUpdateStrategy{
+					Type: kbappsv1.RollingUpdateStrategyType,
+				}).AddConfigs([]workloads.ConfigTemplate{
+					{
+						Name:       "server",
+						Generation: int64(1),
+					},
+					{
+						Name:       "logging",
+						Generation: int64(2),
+					},
+				}...)
+			})
+
+			By("mock pods running and available")
+			podKey := types.NamespacedName{
+				Namespace: itsObj.Namespace,
+				Name:      fmt.Sprintf("%s-0", itsObj.Name),
+			}
+			Expect(testapps.GetAndChangeObjStatus(&testCtx, podKey, func(pod *corev1.Pod) {
+				pod.Status.Phase = corev1.PodRunning
+				pod.Status.Conditions = []corev1.PodCondition{
+					{
+						Type:               corev1.PodReady,
+						Status:             corev1.ConditionTrue,
+						LastTransitionTime: metav1.Now(),
+					},
+				}
+				pod.Status.ContainerStatuses = []corev1.ContainerStatus{
+					{
+						Name: pod.Spec.Containers[0].Name,
+						State: corev1.ContainerState{
+							Running: &corev1.ContainerStateRunning{},
+						},
+						Image: pod.Spec.Containers[0].Image,
+					},
+				}
+			})()).ShouldNot(HaveOccurred())
+
+			By("check the init instance status")
+			Eventually(testapps.CheckObj(&testCtx, itsKey, func(g Gomega, its *workloads.InstanceSet) {
+				g.Expect(its.Status.InstanceStatus).Should(HaveLen(1))
+				g.Expect(its.Status.InstanceStatus[0]).Should(Equal(workloads.InstanceStatus{
+					PodName: fmt.Sprintf("%s-0", itsObj.Name),
+					Configs: []workloads.InstanceConfigStatus{
+						{
+							Name:       "server",
+							Generation: int64(1),
+						},
+						{
+							Name:       "logging",
+							Generation: int64(2),
+						},
+					},
+				}))
+			})).Should(Succeed())
+
+			By("check the reconfigure action not called")
+			Eventually(func(g Gomega) {
+				g.Expect(reconfigure).Should(BeEmpty())
+				g.Expect(parameters).Should(BeNil())
+			}).Should(Succeed())
+
+			By("update configs")
+			Expect(testapps.GetAndChangeObj(&testCtx, itsKey, func(its *workloads.InstanceSet) {
+				its.Spec.Configs[1].Generation = 128
+				its.Spec.Configs[1].Reconfigure = testapps.NewLifecycleAction("reconfigure")
+				its.Spec.Configs[1].ReconfigureActionName = "reconfigure-server"
+				its.Spec.Configs[1].Parameters = map[string]string{"foo": "bar"}
+			})()).ShouldNot(HaveOccurred())
+
+			By("check the instance status updated")
+			Eventually(testapps.CheckObj(&testCtx, itsKey, func(g Gomega, its *workloads.InstanceSet) {
+				g.Expect(its.Status.InstanceStatus).Should(HaveLen(1))
+				g.Expect(its.Status.InstanceStatus[0]).Should(Equal(workloads.InstanceStatus{
+					PodName: fmt.Sprintf("%s-0", itsObj.Name),
+					Configs: []workloads.InstanceConfigStatus{
+						{
+							Name:       "server",
+							Generation: int64(1),
+						},
+						{
+							Name:       "logging",
+							Generation: int64(128),
+						},
+					},
+				}))
+			})).Should(Succeed())
+
+			By("check the reconfigure action call")
+			Eventually(func(g Gomega) {
+				g.Expect(reconfigure).Should(ContainSubstring("reconfigure-server"))
+				g.Expect(parameters).ShouldNot(BeNil())
+				g.Expect(parameters).Should(HaveKeyWithValue("foo", "bar"))
+			}).Should(Succeed())
+		})
+	})
 })
diff --git a/pkg/controller/kubebuilderx/controller_test.go b/pkg/controller/kubebuilderx/controller_test.go
index 49d6b4286..9706aafca 100644
--- a/pkg/controller/kubebuilderx/controller_test.go
+++ b/pkg/controller/kubebuilderx/controller_test.go
@@ -135,7 +135,7 @@ type dummyReconciler struct {
 
 func (d *dummyReconciler) PreCondition(tree *ObjectTree) *CheckResult {
 	if d.preErr != nil {
-		return CheckResultWithError(d.preErr)
+		return ConditionUnsatisfiedWithError(d.preErr)
 	}
 	if d.unsatisfied {
 		return ConditionUnsatisfied
diff --git a/pkg/controllerutil/config_util_test.go b/pkg/controllerutil/config_util_test.go
index 2283dd493..ffcc06e98 100644
--- a/pkg/controllerutil/config_util_test.go
+++ b/pkg/controllerutil/config_util_test.go
@@ -236,8 +236,8 @@ func TestGetConfigSpecReconcilePhase(t *testing.T) {
 	}}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
-			if got := GetConfigSpecReconcilePhase(tt.args.cm, tt.args.item, tt.args.status); got != tt.want {
-				t.Errorf("GetConfigSpecReconcilePhase() = %v, want %v", got, tt.want)
+			if got := GetUpdatedParametersReconciledPhase(tt.args.cm, tt.args.item, tt.args.status); got != tt.want {
+				t.Errorf("GetUpdatedParametersReconciledPhase() = %v, want %v", got, tt.want)
 			}
 		})
 	}
diff --git a/pkg/testutil/apps/componentdefinition_factory.go b/pkg/testutil/apps/componentdefinition_factory.go
index f376f7edd..03bc1b7d7 100644
--- a/pkg/testutil/apps/componentdefinition_factory.go
+++ b/pkg/testutil/apps/componentdefinition_factory.go
@@ -186,56 +186,17 @@ func (f *MockComponentDefinitionFactory) AddConfigTemplate(name, configTemplateR
 	return f
 }
 
-func (f *MockComponentDefinitionFactory) AddConfigs(configs []kbappsv1.ComponentTemplateSpec) *MockComponentDefinitionFactory {
-	if f.Get().Spec.Configs == nil {
-		f.Get().Spec.Configs = make([]kbappsv1.ComponentTemplateSpec, 0)
-	}
-	f.Get().Spec.Configs = append(f.Get().Spec.Configs, configs...)
-	return f
-}
-
-func (f *MockComponentDefinitionFactory) AddScripts(scripts []kbappsv1.ComponentTemplateSpec) *MockComponentDefinitionFactory {
-	if f.Get().Spec.Scripts == nil {
-		f.Get().Spec.Scripts = make([]kbappsv1.ComponentTemplateSpec, 0)
-	}
-	f.Get().Spec.Scripts = append(f.Get().Spec.Scripts, scripts...)
-	return f
-}
-
-func (f *MockComponentDefinitionFactory) AddLogConfig(name, filePathPattern string) *MockComponentDefinitionFactory {
-	logConfig := kbappsv1.LogConfig{
-		FilePathPattern: filePathPattern,
-		Name:            name,
-	}
-	if f.Get().Spec.LogConfigs == nil {
-		f.Get().Spec.LogConfigs = make([]kbappsv1.LogConfig, 0)
-	}
-	f.Get().Spec.LogConfigs = append(f.Get().Spec.LogConfigs, logConfig)
-	return f
-}
-
-// func (f *MockComponentDefinitionFactory) SetMonitor(builtIn bool, scrapePort intstr.IntOrString, scrapePath string) *MockComponentDefinitionFactory {
-// 	f.Get().Spec.Monitor = &kbappsv1.MonitorConfig{
-// 		BuiltIn: builtIn,
-// 		Exporter: &kbappsv1.ExporterConfig{
-// 			ScrapePort: scrapePort,
-// 			ScrapePath: scrapePath,
-// 		},
-// 	}
-// 	return f
-// }
-
-func (f *MockComponentDefinitionFactory) AddScriptTemplate(name, configTemplateRef, namespace, volumeName string,
+func (f *MockComponentDefinitionFactory) AddScriptTemplate(name, configTemplate, namespace, volumeName string,
 	mode *int32) *MockComponentDefinitionFactory {
-	script := kbappsv1.ComponentTemplateSpec{
+	script := kbappsv1.ComponentFileTemplate{
 		Name:        name,
-		TemplateRef: configTemplateRef,
+		Template:    configTemplate,
 		Namespace:   namespace,
 		VolumeName:  volumeName,
 		DefaultMode: mode,
 	}
 	if f.Get().Spec.Scripts == nil {
-		f.Get().Spec.Scripts = make([]kbappsv1.ComponentTemplateSpec, 0)
+		f.Get().Spec.Scripts = make([]kbappsv1.ComponentFileTemplate, 0)
 	}
 	f.Get().Spec.Scripts = append(f.Get().Spec.Scripts, script)
 	return f
diff --git a/pkg/testutil/apps/instance_set_factoy.go b/pkg/testutil/apps/instance_set_factoy.go
index 358c3bfd6..dcf1af814 100644
--- a/pkg/testutil/apps/instance_set_factoy.go
+++ b/pkg/testutil/apps/instance_set_factoy.go
@@ -113,3 +113,14 @@ func (factory *MockInstanceSetFactory) AddContainer(container corev1.Container)
 	*containers = append(*containers, container)
 	return factory
 }
+
+func (factory *MockInstanceSetFactory) AddConfigs(config ...workloads.ConfigTemplate) *MockInstanceSetFactory {
+	configs := &factory.Get().Spec.Configs
+	*configs = append(*configs, config...)
+	return factory
+}
+
+func (factory *MockInstanceSetFactory) SetInstanceUpdateStrategy(instanceUpdateStrategy *workloads.InstanceUpdateStrategy) *MockInstanceSetFactory {
+	factory.Get().Spec.InstanceUpdateStrategy = instanceUpdateStrategy
+	return factory
+}