Repository: karmada-io/karmada

Issue:
hack/local-up-karmada.sh does not work on WSL2
<!-- Please use this template while reporting a bug and provide as much info as possible. Not doing so may result in your bug not being addressed in a timely manner. Thanks!
-->


**What happened**:

Starting a karmada cluster with kind locally using the `hack/local-up-karmada.sh` does not work.

It hangs at the following step:

```
Waiting for the clusters to be ready...
Waiting for kubeconfig file /home/yifan/.kube/karmada.config and clusters karmada-host to be ready...
Context "kind-karmada-host" renamed to "karmada-host".
Cluster "kind-karmada-host" set.
Waiting for ok..
```

**What you expected to happen**: Karmada server and member clusters should be started successfully.

**How to reproduce it (as minimally and precisely as possible)**:

Run `hack/local-up-karmada.sh` on a WSL 2 linux.

**Anything else we need to know?**:

This issue was already mentioned in https://github.com/karmada-io/karmada/issues/3140. As @RainbowMango pointed out, the root cause was the following code snippet (https://github.com/karmada-io/karmada/blob/3024d3321e9e5a934767314be7e60eb7ac49b74d/hack/util.sh#L550-L556).

A simple workaround is to change it to:

```sh
case $os_name in
    #linux) container_ip_port=$(util::get_docker_native_ipaddress "${context_name}-control-plane")":6443"
    #;;
    linux) container_ip_port=$(util::get_docker_host_ip_port "${context_name}-control-plane")
    ;;
    darwin) container_ip_port=$(util::get_docker_host_ip_port "${context_name}-control-plane")
    ;;
    *)
```

However this implies that everyone who tries to spin up karmada on WSL 2 would have to manually changing thee `hack/util.sh` file. I believe a better option is to further check the output of `uname -r` when `os_name` is `linux`. For example on my WSL environment the output is:

```sh
$ uname -r
6.6.87.2-microsoft-standard-WSL2
```

and use `util::get_docker_host_ip_port` but not `util::get_docker_native_ipaddress` when the output of `uname -r` contains a substring `WSL2`.

If you believe this change makes sense, I'm more than happy to contribute a PR. Thanks!

**Environment**: Ubuntu on WSL 2
- Karmada version: latest master branch
- kubectl-karmada or karmadactl version (the result of `kubectl-karmada version` or `karmadactl version`): N/A
- Others:


Discussion:
@zhzhuang-zju: @zclyne Thanks for your feedback. Have you encountered any other issues besides the above when running hack/local-up-karmada.sh on WSL2?@mohamedawnallah: @zclyne Thanks for the investigation and taking another look at this 🙏

> I believe a better option is to further check the output of uname -r when os_name is linux. For example on my WSL environment the output is:

We could perhaps avoid checking on presence of `WSL2` we get in the output of `uname -r` since checking on strings is volatile especially over time. Two approaches I just did quick research on perhaps we could rely on one of them:
- The existence of the file `/proc/sys/fs/binfmt_misc/WSLInterop`. I don't have WSL environment but from what I searched online this file seems always exist in this kind of environment. Perhaps double check needed here
- The existence of `WSL_DISTRO_NAME` environment variable but seems has issues and not reliable as seen in https://github.com/microsoft/WSL/issues/9719?

It would be good as well if @zhzhuang-zju has any thoughts about it@zhzhuang-zju: @mohamedawnallah  Thanks for your information. I have no further thoughts, but I would like to confirm
- Karmada can run normally on WSL2
- Can correctly detect whether it is a WSL2 environment@zclyne: > [@zclyne](https://github.com/zclyne) Thanks for your feedback. Have you encountered any other issues besides the above when running hack/local-up-karmada.sh on WSL2?

I can spin up karmada successfully using `hack/local-up-karmada.sh` after making the change suggested in https://github.com/karmada-io/karmada/issues/3140 but I haven't taken a further look at any other issues with WSL2. Will keep you posted if I have seen any. Thanks!@zclyne: > [@zclyne](https://github.com/zclyne) Thanks for the investigation and taking another look at this 🙏
> 
> > I believe a better option is to further check the output of uname -r when os_name is linux. For example on my WSL environment the output is:
> 
> We could perhaps avoid checking on presence of `WSL2` we get in the output of `uname -r` since checking on strings is volatile especially over time. Two approaches I just did quick research on perhaps we could rely on one of them:
> 
> * The existence of the file `/proc/sys/fs/binfmt_misc/WSLInterop`. I don't have WSL environment but from what I searched online this file seems always exist in this kind of environment. Perhaps double check needed here
> * The existence of `WSL_DISTRO_NAME` environment variable but seems has issues and not reliable as seen in [Environment variable WSL_DISTRO_NAME not available as root microsoft/WSL#9719](https://github.com/microsoft/WSL/issues/9719)?
> 
> It would be good as well if [@zhzhuang-zju](https://github.com/zhzhuang-zju) has any thoughts about it

Thanks for looking into it. On my WSL2 environment both approaches are valid:

```sh
$ cat /proc/sys/fs/binfmt_misc/WSLInterop
enabled
interpreter /init
flags: PF
offset 0
magic 4d5a

$ echo $WSL_DISTRO_NAME
Ubuntu
```

However chatgpt recommended checking the output of `cat /proc/version` or `cat /proc/sys/kernel/osrelease`, the output of which on my environment is as follows:

```sh
$  cat /proc/version
Linux version 6.6.87.2-microsoft-standard-WSL2 (root@439a258ad544) (gcc (GCC) 11.2.0, GNU ld (GNU Binutils) 2.37) #1 SMP PREEMPT_DYNAMIC Thu Jun  5 18:30:46 UTC 2025

$  cat /proc/sys/kernel/osrelease
6.6.87.2-microsoft-standard-WSL2
```

Perhaps a more robust approach is to check all of the conditions mentioned above and if any of them matches, we believe that the environment is WSL.

Do you think if this looks good to you? Thank you.
@mohamedawnallah: > Perhaps a more robust approach is to check all of the conditions mentioned above and if any of them matches, we believe that the environment is WSL.

Perhaps we could check on the existence of `WSL2` in ` /proc/sys/kernel/osrelease` `OR`  the existence of this file `/proc/sys/fs/binfmt_misc/WSLInterop`. For the environment variable I feel like it is not clear indicator that I am in this `WSL2` environment e.g I could `export WSL_DISTRO_NAME=Ubuntu` although it is less likely but probably possible.

But I think the overall goal of this is having the minimal conditions that achieves this determinism of the env.

Feel free to submit a PR for this and we could discuss it from there 🙏@zclyne: 
> > Perhaps a more robust approach is to check all of the conditions mentioned above and if any of them matches, we believe that the environment is WSL.
> 
> Perhaps we could check on the existence of `WSL2` in ` /proc/sys/kernel/osrelease` `OR` the existence of this file `/proc/sys/fs/binfmt_misc/WSLInterop`. For the environment variable I feel like it is not clear indicator that I am in this `WSL2` environment e.g I could `export WSL_DISTRO_NAME=Ubuntu` although it is less likely but probably possible.
> 
> But I think the overall goal of this is having the minimal conditions that achieves this determinism of the env.
> 
> Feel free to submit a PR for this and we could discuss it from there 🙏

Sounds good! I will put in a PR to use the approach you suggested to detect the environment. Thanks!@mohamedawnallah: > > > Perhaps a more robust approach is to check all of the conditions mentioned above and if any of them matches, we believe that the environment is WSL.
> > 
> > 
> > Perhaps we could check on the existence of `WSL2` in ` /proc/sys/kernel/osrelease` `OR` the existence of this file `/proc/sys/fs/binfmt_misc/WSLInterop`. For the environment variable I feel like it is not clear indicator that I am in this `WSL2` environment e.g I could `export WSL_DISTRO_NAME=Ubuntu` although it is less likely but probably possible.
> > But I think the overall goal of this is having the minimal conditions that achieves this determinism of the env.
> > Feel free to submit a PR for this and we could discuss it from there 🙏
> 
> Sounds good! I will put in a PR to use the approach you suggested to detect the environment. Thanks!

Looking forward to your PR! Feel free to propose whatever approach you would like. I just proposed the approaches I could think of at this time

Solution patch:
diff --git a/.github/workflows/ci-image-scanning-on-schedule.yml b/.github/workflows/ci-image-scanning-on-schedule.yml
index 1a0ff795a..566a2dc4a 100644
--- a/.github/workflows/ci-image-scanning-on-schedule.yml
+++ b/.github/workflows/ci-image-scanning-on-schedule.yml
@@ -30,7 +30,7 @@ jobs:
         karmada-version: [ release-1.14, release-1.13, release-1.12 ]
     steps:
       - name: checkout code
-        uses: actions/checkout@v4
+        uses: actions/checkout@v5
         with:
           ref: ${{ matrix.karmada-version }}
       - name: install Go
diff --git a/.github/workflows/ci-image-scanning.yaml b/.github/workflows/ci-image-scanning.yaml
index e7b16646f..99ada99ba 100644
--- a/.github/workflows/ci-image-scanning.yaml
+++ b/.github/workflows/ci-image-scanning.yaml
@@ -31,7 +31,7 @@ jobs:
           - karmada-metrics-adapter
     steps:
       - name: checkout code
-        uses: actions/checkout@v4
+        uses: actions/checkout@v5
         with:
          # fetch-depth:
           # 0 indicates all history for all branches and tags.
diff --git a/.github/workflows/ci-schedule-compatibility.yaml b/.github/workflows/ci-schedule-compatibility.yaml
index 02a103956..b71be5cd3 100644
--- a/.github/workflows/ci-schedule-compatibility.yaml
+++ b/.github/workflows/ci-schedule-compatibility.yaml
@@ -38,7 +38,7 @@ jobs:
           docker-images: false
           swap-storage: false
       - name: checkout code
-        uses: actions/checkout@v4
+        uses: actions/checkout@v5
         with:
           # Number of commits to fetch. 0 indicates all history for all branches and tags.
           # We need to guess version via git tags.
diff --git a/.github/workflows/ci-schedule.yml b/.github/workflows/ci-schedule.yml
index abbc89be0..e4b958a67 100644
--- a/.github/workflows/ci-schedule.yml
+++ b/.github/workflows/ci-schedule.yml
@@ -35,7 +35,7 @@ jobs:
           docker-images: false
           swap-storage: false
       - name: checkout code
-        uses: actions/checkout@v4
+        uses: actions/checkout@v5
         with:
           # Number of commits to fetch. 0 indicates all history for all branches and tags.
           # We need to guess version via git tags.
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 9cfd42514..65ebb1b56 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -20,7 +20,7 @@ jobs:
     runs-on: ubuntu-22.04
     steps:
       - name: checkout code
-        uses: actions/checkout@v4
+        uses: actions/checkout@v5
       - name: install Go
         uses: actions/setup-go@v5
         with:
@@ -38,7 +38,7 @@ jobs:
     runs-on: ubuntu-22.04
     steps:
       - name: checkout code
-        uses: actions/checkout@v4
+        uses: actions/checkout@v5
       - name: install Go
         uses: actions/setup-go@v5
         with:
@@ -66,7 +66,7 @@ jobs:
     runs-on: ubuntu-22.04
     steps:
       - name: checkout code
-        uses: actions/checkout@v4
+        uses: actions/checkout@v5
         with:
           # Number of commits to fetch. 0 indicates all history for all branches and tags.
           # We need to guess version via git tags.
@@ -85,7 +85,7 @@ jobs:
       GOTESTSUM_ENABLED: enabled
     steps:
       - name: checkout code
-        uses: actions/checkout@v4
+        uses: actions/checkout@v5
       - name: install Go
         uses: actions/setup-go@v5
         with:
@@ -132,7 +132,7 @@ jobs:
           docker-images: false
           swap-storage: false
       - name: checkout code
-        uses: actions/checkout@v4
+        uses: actions/checkout@v5
         with:
           # Number of commits to fetch. 0 indicates all history for all branches and tags.
           # We need to guess version via git tags.
@@ -188,7 +188,7 @@ jobs:
           docker-images: false
           swap-storage: false
       - name: checkout code
-        uses: actions/checkout@v4
+        uses: actions/checkout@v5
         with:
           # Number of commits to fetch. 0 indicates all history for all branches and tags.
           # We need to guess version via git tags.
diff --git a/.github/workflows/dockerhub-latest-chart.yml b/.github/workflows/dockerhub-latest-chart.yml
index 7bbd8bfca..057f6c957 100644
--- a/.github/workflows/dockerhub-latest-chart.yml
+++ b/.github/workflows/dockerhub-latest-chart.yml
@@ -16,7 +16,7 @@ jobs:
     runs-on: ubuntu-22.04
     steps:
       - name: checkout code
-        uses: actions/checkout@v4
+        uses: actions/checkout@v5
         with:
           # fetch-depth:
           # 0 indicates all history for all branches and tags.
diff --git a/.github/workflows/dockerhub-latest-image.yml b/.github/workflows/dockerhub-latest-image.yml
index 9c539a919..cfe9ffb35 100644
--- a/.github/workflows/dockerhub-latest-image.yml
+++ b/.github/workflows/dockerhub-latest-image.yml
@@ -31,7 +31,7 @@ jobs:
     runs-on: ubuntu-22.04
     steps:
       - name: checkout code
-        uses: actions/checkout@v4
+        uses: actions/checkout@v5
         with:
           # fetch-depth:
           # 0 indicates all history for all branches and tags.
diff --git a/.github/workflows/dockerhub-released-chart.yml b/.github/workflows/dockerhub-released-chart.yml
index a08091fb5..5076fb0e1 100644
--- a/.github/workflows/dockerhub-released-chart.yml
+++ b/.github/workflows/dockerhub-released-chart.yml
@@ -15,7 +15,7 @@ jobs:
     if: ${{ github.repository == 'karmada-io/karmada' }}
     steps:
       - name: checkout code
-        uses: actions/checkout@v4
+        uses: actions/checkout@v5
         with:
           # fetch-depth:
           # 0 indicates all history for all branches and tags.
diff --git a/.github/workflows/dockerhub-released-image.yml b/.github/workflows/dockerhub-released-image.yml
index d82e698bf..9518b9f2e 100644
--- a/.github/workflows/dockerhub-released-image.yml
+++ b/.github/workflows/dockerhub-released-image.yml
@@ -31,7 +31,7 @@ jobs:
     runs-on: ubuntu-22.04
     steps:
       - name: checkout code
-        uses: actions/checkout@v4
+        uses: actions/checkout@v5
         with:
           # fetch-depth:
           # 0 indicates all history for all branches and tags.
diff --git a/.github/workflows/fossa.yml b/.github/workflows/fossa.yml
index 829ea2607..cabcc5199 100644
--- a/.github/workflows/fossa.yml
+++ b/.github/workflows/fossa.yml
@@ -7,7 +7,7 @@ on:
       - 'dependabot/**'
 
 permissions:
-  contents: read # Required by actions/checkout@v4 to fetch the repository contents.
+  contents: read # Required by actions/checkout@v5 to fetch the repository contents.
   
 jobs:
   fossa:
@@ -19,7 +19,7 @@ jobs:
     runs-on: ubuntu-22.04
     steps:
       - name: checkout code
-        uses: actions/checkout@v4
+        uses: actions/checkout@v5
       - name: Run FOSSA scan and upload build data
         uses: fossas/fossa-action@v1
         with:
diff --git a/.github/workflows/installation-chart.yaml b/.github/workflows/installation-chart.yaml
index 41be1aabb..803122a2f 100644
--- a/.github/workflows/installation-chart.yaml
+++ b/.github/workflows/installation-chart.yaml
@@ -29,7 +29,7 @@ jobs:
         k8s: [ v1.31.0, v1.32.0, v1.33.0 ]
     steps:
       - name: Checkout
-        uses: actions/checkout@v4
+        uses: actions/checkout@v5
         with:
           fetch-depth: 0
 
diff --git a/.github/workflows/installation-cli.yaml b/.github/workflows/installation-cli.yaml
index a60690681..12a92100c 100644
--- a/.github/workflows/installation-cli.yaml
+++ b/.github/workflows/installation-cli.yaml
@@ -27,7 +27,7 @@ jobs:
         k8s: [ v1.31.0, v1.32.0, v1.33.0 ]
     steps:
       - name: checkout code
-        uses: actions/checkout@v4
+        uses: actions/checkout@v5
         with:
           # Number of commits to fetch. 0 indicates all history for all branches and tags.
           # We need to guess version via git tags.
@@ -73,7 +73,7 @@ jobs:
         k8s: [ v1.31.0, v1.32.0, v1.33.0 ]
     steps:
       - name: checkout code
-        uses: actions/checkout@v4
+        uses: actions/checkout@v5
         with:
           fetch-depth: 0
       - name: install Go
diff --git a/.github/workflows/installation-operator.yaml b/.github/workflows/installation-operator.yaml
index 00fd4cd06..f4070f270 100644
--- a/.github/workflows/installation-operator.yaml
+++ b/.github/workflows/installation-operator.yaml
@@ -40,7 +40,7 @@ jobs:
           docker-images: false
           swap-storage: false
       - name: checkout code
-        uses: actions/checkout@v4
+        uses: actions/checkout@v5
         with:
           # Number of commits to fetch. 0 indicates all history for all branches and tags.
           # We need to guess version via git tags.
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 2a057b0dc..ea8e7ab0d 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -23,7 +23,7 @@ jobs:
           - amd64
           - arm64
     steps:
-    - uses: actions/checkout@v4
+    - uses: actions/checkout@v5
     - name: Set up Go
       uses: actions/setup-go@v5
       with:
@@ -52,7 +52,7 @@ jobs:
       hashes: ${{ steps.hash.outputs.hashes }}
     steps:
       - name: download cli
-        uses: actions/download-artifact@v4
+        uses: actions/download-artifact@v5
         with:
           path: _output/release
           pattern: cli-*
@@ -84,7 +84,7 @@ jobs:
       hashes: ${{ steps.hash.outputs.hashes }}
     runs-on: ubuntu-22.04
     steps:
-    - uses: actions/checkout@v4
+    - uses: actions/checkout@v5
     - name: Rename the crds directory
       run: |
         mv ./charts/karmada/_crds ./charts/karmada/crds
@@ -126,7 +126,7 @@ jobs:
       hashes: ${{ steps.hash.outputs.hashes }}
     runs-on: ubuntu-22.04
     steps:
-    - uses: actions/checkout@v4
+    - uses: actions/checkout@v5
     - name: Making helm charts
       env:
         VERSION: ${{ github.ref_name }}
@@ -165,7 +165,7 @@ jobs:
       hashes: ${{ steps.sbom-hash.outputs.hashes}}
     runs-on: ubuntu-22.04
     steps:
-    - uses: actions/checkout@v4
+    - uses: actions/checkout@v5
     - name: Generate sbom for karmada file system
       uses: aquasecurity/trivy-action@0.32.0
       with:
@@ -220,7 +220,7 @@ jobs:
         echo "Got the latest tag:$LATEST_TAG"
         echo "event.tag:"${{ github.event.release.tag_name }}
         echo "latestTag=$LATEST_TAG" >> "$GITHUB_OUTPUT"
-    - uses: actions/checkout@v4
+    - uses: actions/checkout@v5
       if: steps.get-latest-tag.outputs.latestTag == github.event.release.tag_name
     - name: Update new version in krew-index
       if: steps.get-latest-tag.outputs.latestTag == github.event.release.tag_name
diff --git a/.github/workflows/swr-latest-image.yml b/.github/workflows/swr-latest-image.yml
index aa344b53f..6dc36210a 100644
--- a/.github/workflows/swr-latest-image.yml
+++ b/.github/workflows/swr-latest-image.yml
@@ -15,7 +15,7 @@ jobs:
     runs-on: ubuntu-22.04
     steps:
       - name: checkout code
-        uses: actions/checkout@v4
+        uses: actions/checkout@v5
         with:
           # fetch-depth:
           # 0 indicates all history for all branches and tags.
diff --git a/.github/workflows/swr-released-image.yml b/.github/workflows/swr-released-image.yml
index 83730446c..d9a1bbbb4 100644
--- a/.github/workflows/swr-released-image.yml
+++ b/.github/workflows/swr-released-image.yml
@@ -15,7 +15,7 @@ jobs:
     runs-on: ubuntu-22.04
     steps:
       - name: checkout code
-        uses: actions/checkout@v4
+        uses: actions/checkout@v5
         with:
           fetch-depth: 0
       - name: install Go
diff --git a/.go-version b/.go-version
index 6521720b4..7a429d68a 100644
--- a/.go-version
+++ b/.go-version
@@ -1 +1 @@
-1.24.5
+1.24.6
diff --git a/api/openapi-spec/swagger.json b/api/openapi-spec/swagger.json
index f0f6b3fa6..92daf64c6 100644
--- a/api/openapi-spec/swagger.json
+++ b/api/openapi-spec/swagger.json
@@ -18469,10 +18469,28 @@
         }
       }
     },
+    "com.github.karmada-io.karmada.pkg.apis.config.v1alpha1.ComponentResourceRequirement": {
+      "description": "ComponentResourceRequirement holds the scripts for extracting the desired replica count and resource requirements for each component within a resource. This is particularly useful for resources that define multiple components (such as CRDs with multiple pod templates), but can also be used for single-component resources.",
+      "type": "object",
+      "required": [
+        "luaScript"
+      ],
+      "properties": {
+        "luaScript": {
+          "description": "LuaScript holds the Lua script that is used to extract the desired replica count and resource requirements for each component of the resource.\n\nThe script should implement a function as follows:\n\n```\n  luaScript: \u003e\n      function GetComponents(desiredObj)\n          local components = {}\n\n          local jobManagerComponent = {\n              name = \"jobmanager\"\n              replicas = desiredObj.spec.jobManager.replicas\n          }\n          table.insert(components, jobManagerComponent)\n\n          local taskManagerComponent = {\n              name = \"taskmanager\"\n              replicas = desiredObj.spec.taskManager.replicas\n          }\n          table.insert(components, taskManagerComponent)\n\n          return components\n      end\n```\n\nThe content of the LuaScript needs to be a whole function including both declaration and implementation.\n\nThe parameters will be supplied by the system:\n  - desiredObj: the object represents the configuration to be applied\n      to the member cluster.\n\nThe function expects one return value:\n  - components: the resource requirements for each component.\nThe returned value will be set into a ResourceBinding or ClusterResourceBinding.",
+          "type": "string",
+          "default": ""
+        }
+      }
+    },
     "com.github.karmada-io.karmada.pkg.apis.config.v1alpha1.CustomizationRules": {
       "description": "CustomizationRules describes the interpretation rules.",
       "type": "object",
       "properties": {
+        "componentResource": {
+          "description": "ComponentResource describes the rules for Karmada to discover the resource requirements for multiple components from the given object. This is designed for CRDs with multiple components (e.g., FlinkDeployment), but can also be used for single-component resources like Deployment. If implemented, the controller will use this to obtain per-component replica and resource requirements, and will not call ReplicaResource. If not implemented, the controller will fall back to ReplicaResource for backward compatibility. This will only be used when the feature gate 'MultiplePodTemplatesScheduling' is enabled.",
+          "$ref": "#/definitions/com.github.karmada-io.karmada.pkg.apis.config.v1alpha1.ComponentResourceRequirement"
+        },
         "dependencyInterpretation": {
           "description": "DependencyInterpretation describes the rules for Karmada to analyze the dependent resources. Karmada provides built-in rules for several standard Kubernetes types, see: https://karmada.io/docs/userguide/globalview/customizing-resource-interpreter/#interpretdependency If DependencyInterpretation is set, the built-in rules will be ignored.",
           "$ref": "#/definitions/com.github.karmada-io.karmada.pkg.apis.config.v1alpha1.DependencyInterpretation"
@@ -19187,7 +19205,7 @@
           "format": "int32"
         },
         "purgeMode": {
-          "description": "PurgeMode represents how to deal with the legacy applications on the cluster from which the application is migrated. Valid options are \"Immediately\", \"Graciously\" and \"Never\". Defaults to \"Graciously\".",
+          "description": "PurgeMode represents how to deal with the legacy applications on the cluster from which the application is migrated. Valid options are \"Directly\", \"Gracefully\", \"Never\", \"Immediately\"(deprecated), and \"Graciously\"(deprecated). Defaults to \"Gracefully\".",
           "type": "string"
         },
         "statePreservation": {
@@ -19264,6 +19282,20 @@
         }
       }
     },
+    "com.github.karmada-io.karmada.pkg.apis.policy.v1alpha1.ClusterFailoverBehavior": {
+      "description": "ClusterFailoverBehavior indicates cluster failover behaviors.",
+      "type": "object",
+      "properties": {
+        "purgeMode": {
+          "description": "PurgeMode represents how to deal with the legacy applications on the cluster from which the application is migrated. Valid options are \"Directly\", \"Gracefully\". Defaults to \"Gracefully\".",
+          "type": "string"
+        },
+        "statePreservation": {
+          "description": "StatePreservation defines the policy for preserving and restoring state data during failover events for stateful applications.\n\nWhen an application fails over from one cluster to another, this policy enables the extraction of critical data from the original resource configuration. Upon successful migration, the extracted data is then re-injected into the new resource, ensuring that the application can resume operation with its previous state intact. This is particularly useful for stateful applications where maintaining data consistency across failover events is crucial. If not specified, means no state data will be preserved.\n\nNote: This requires the StatefulFailoverInjection feature gate to be enabled, which is alpha.",
+          "$ref": "#/definitions/com.github.karmada-io.karmada.pkg.apis.policy.v1alpha1.StatePreservation"
+        }
+      }
+    },
     "com.github.karmada-io.karmada.pkg.apis.policy.v1alpha1.ClusterOverridePolicy": {
       "description": "ClusterOverridePolicy represents the cluster-wide policy that overrides a group of resources to one or more clusters.",
       "type": "object",
@@ -19598,6 +19630,10 @@
         "application": {
           "description": "Application indicates failover behaviors in case of application failure. If this value is nil, failover is disabled. If set, the PropagateDeps should be true so that the dependencies could be migrated along with the application.",
           "$ref": "#/definitions/com.github.karmada-io.karmada.pkg.apis.policy.v1alpha1.ApplicationFailoverBehavior"
+        },
+        "cluster": {
+          "description": "Cluster indicates failover behaviors in case of cluster failure. If this value is nil, the failover behavior in case of cluster failure will be controlled by the controller's no-execute-taint-eviction-purge-mode parameter. If set, the failover behavior in case of cluster failure will be defined by this value.",
+          "$ref": "#/definitions/com.github.karmada-io.karmada.pkg.apis.policy.v1alpha1.ClusterFailoverBehavior"
         }
       }
     },
@@ -21176,6 +21212,29 @@
         }
       ]
     },
+    "com.github.karmada-io.karmada.pkg.apis.work.v1alpha2.ComponentRequirements": {
+      "description": "ComponentRequirements represents the requirements for a specific component.",
+      "type": "object",
+      "required": [
+        "replicas"
+      ],
+      "properties": {
+        "name": {
+          "description": "Name of this component. It is required when the resource contains multiple components to ensure proper identification, and must also be unique within the same resource.",
+          "type": "string"
+        },
+        "replicaRequirements": {
+          "description": "ReplicaRequirements represents the requirements required by each replica for this component.",
+          "$ref": "#/definitions/com.github.karmada-io.karmada.pkg.apis.work.v1alpha2.ReplicaRequirements"
+        },
+        "replicas": {
+          "description": "Replicas represents the replica number of the resource's component.",
+          "type": "integer",
+          "format": "int32",
+          "default": 0
+        }
+      }
+    },
     "com.github.karmada-io.karmada.pkg.apis.work.v1alpha2.GracefulEvictionTask": {
       "description": "GracefulEvictionTask represents a graceful eviction task.",
       "type": "object",
@@ -21225,7 +21284,7 @@
           "default": ""
         },
         "purgeMode": {
-          "description": "PurgeMode represents how to deal with the legacy applications on the cluster from which the application is migrated. Valid options are \"Immediately\", \"Graciously\" and \"Never\".",
+          "description": "PurgeMode represents how to deal with the legacy applications on the cluster from which the application is migrated. Valid options are \"Immediately\", \"Directly\", \"Graciously\", \"Gracefully\" and \"Never\".",
           "type": "string"
         },
         "reason": {
@@ -21422,6 +21481,14 @@
             "$ref": "#/definitions/com.github.karmada-io.karmada.pkg.apis.work.v1alpha2.TargetCluster"
           }
         },
+        "components": {
+          "description": "Components represents the requirements of multiple pod templates of the referencing resource. It is designed to support workloads that consist of multiple pod templates, such as distributed training jobs (e.g., PyTorch, TensorFlow) and big data workloads (e.g., FlinkDeployment), where each workload is composed of more than one pod template. It is also capable of representing single-component workloads, such as Deployment.\n\nNote: This field is intended to replace the legacy ReplicaRequirements and Replicas fields above. It is only populated when the MultiplePodTemplatesScheduling feature gate is enabled.",
+          "type": "array",
+          "items": {
+            "default": {},
+            "$ref": "#/definitions/com.github.karmada-io.karmada.pkg.apis.work.v1alpha2.ComponentRequirements"
+          }
+        },
         "conflictResolution": {
           "description": "ConflictResolution declares how potential conflict should be handled when a resource that is being propagated already exists in the target cluster.\n\nIt defaults to \"Abort\" which means stop propagating to avoid unexpected overwrites. The \"Overwrite\" might be useful when migrating legacy cluster resources to Karmada, in which case conflict is predictable and can be instructed to Karmada take over the resource by overwriting.",
           "type": "string"
diff --git a/charts/karmada-operator/crds/operator.karmada.io_karmadas.yaml b/charts/karmada-operator/crds/operator.karmada.io_karmadas.yaml
index 0bb449441..e3894f279 100644
--- a/charts/karmada-operator/crds/operator.karmada.io_karmadas.yaml
+++ b/charts/karmada-operator/crds/operator.karmada.io_karmadas.yaml
@@ -5208,9 +5208,27 @@ spec:
                     description: HTTPSource specifies how to download the CRD tarball
                       via either HTTP or HTTPS protocol.
                     properties:
+                      proxy:
+                        description: |-
+                          Proxy specifies the configuration of a proxy server to use when downloading the CRD tarball.
+                          When set, the operator will use the configuration to determine how to establish a connection to the proxy to fetch the tarball from the URL specified above.
+                          This is useful in environments where direct access to the server hosting the CRD tarball is restricted and a proxy must be used to reach that server.
+                          If a proxy configuration is not set, the operator will attempt to download the tarball directly from the URL specified above without using a proxy.
+                        properties:
+                          proxyURL:
+                            description: |-
+                              ProxyURL specifies the HTTP/HTTPS proxy server URL to use when downloading the CRD tarball.
+                              This is useful in environments where direct access to the server hosting the CRD tarball is restricted and a proxy must be used to reach that server.
+                              The format should be a valid URL, e.g., "http://proxy.example.com:8080".
+                            type: string
+                        required:
+                        - proxyURL
+                        type: object
                       url:
                         description: URL specifies the URL of the CRD tarball resource.
                         type: string
+                    required:
+                    - url
                     type: object
                 type: object
               customCertificate:
diff --git a/charts/karmada-operator/templates/karmada-operator-deployment.yaml b/charts/karmada-operator/templates/karmada-operator-deployment.yaml
index 88c70bd68..c43dc6a4b 100644
--- a/charts/karmada-operator/templates/karmada-operator-deployment.yaml
+++ b/charts/karmada-operator/templates/karmada-operator-deployment.yaml
@@ -44,6 +44,13 @@ spec:
         - /bin/karmada-operator
         - --leader-elect-resource-namespace={{ .Release.Namespace }}
         - --v=2
+        {{- range .Values.operator.extraArgs }}
+        - {{ . }}
+        {{- end }}
+        {{- with .Values.operator.env }}
+        env:
+        {{- toYaml . | nindent 10 }}
+        {{- end }}
         {{- if .Values.operator.resources }}
         resources: {{- toYaml .Values.operator.resources | nindent 12 }}
         {{- end }}
diff --git a/charts/karmada-operator/values.yaml b/charts/karmada-operator/values.yaml
index 89069129e..d043aab02 100644
--- a/charts/karmada-operator/values.yaml
+++ b/charts/karmada-operator/values.yaml
@@ -58,7 +58,31 @@ operator:
     ##   - myRegistryKeySecretName
     ##
     pullSecrets: []
-  ## @param.resources
+
+  ## @param operator.env List of environment variables to inject
+  ##
+  ## - Each entry must be a valid Kubernetes EnvVar object.
+  ## - Supports both literal values and valueFrom references (ConfigMap, Secret, fieldRef, etc.).
+  ## - If omitted or set to an empty array (`[]`), no env stanza will be included.
+  ##
+  ## A sample stanza is shown below.
+  ##
+#  env:
+#    - name: http_proxy
+#      value: "http://best-awesome-proxy.com:8080"
+#    - name: https_proxy
+#      value: "http://best-awesome-proxy.com:8080"
+#    - name: no_proxy
+#      value: "localhost,127.0.0.1,*.svc,*.cluster.local"
+
+  ## @param operator.extraArgs List of extra arguments for the operator binary
+  ##
+  ## A sample stanza is shown below.
+  ##
+#  extraArgs:
+#    - --arg1=val1
+#    - --arg2
+
   resources: {}
     # If you do want to specify resources, uncomment the following
     # lines, adjust them as necessary, and remove the curly braces after 'resources:'.
diff --git a/charts/karmada/_crds/bases/config/config.karmada.io_resourceinterpretercustomizations.yaml b/charts/karmada/_crds/bases/config/config.karmada.io_resourceinterpretercustomizations.yaml
index beb60b6e7..f8b92bc4f 100644
--- a/charts/karmada/_crds/bases/config/config.karmada.io_resourceinterpretercustomizations.yaml
+++ b/charts/karmada/_crds/bases/config/config.karmada.io_resourceinterpretercustomizations.yaml
@@ -60,6 +60,59 @@ spec:
               customizations:
                 description: Customizations describe the interpretation rules.
                 properties:
+                  componentResource:
+                    description: |-
+                      ComponentResource describes the rules for Karmada to discover the resource requirements
+                      for multiple components from the given object.
+                      This is designed for CRDs with multiple components (e.g., FlinkDeployment), but
+                      can also be used for single-component resources like Deployment.
+                      If implemented, the controller will use this to obtain per-component replica and resource
+                      requirements, and will not call ReplicaResource.
+                      If not implemented, the controller will fall back to ReplicaResource for backward compatibility.
+                      This will only be used when the feature gate 'MultiplePodTemplatesScheduling' is enabled.
+                    properties:
+                      luaScript:
+                        description: |-
+                          LuaScript holds the Lua script that is used to extract the desired replica count and resource
+                          requirements for each component of the resource.
+
+                          The script should implement a function as follows:
+
+                          ```
+                            luaScript: >
+                                function GetComponents(desiredObj)
+                                    local components = {}
+
+                                    local jobManagerComponent = {
+                                        name = "jobmanager"
+                                        replicas = desiredObj.spec.jobManager.replicas
+                                    }
+                                    table.insert(components, jobManagerComponent)
+
+                                    local taskManagerComponent = {
+                                        name = "taskmanager"
+                                        replicas = desiredObj.spec.taskManager.replicas
+                                    }
+                                    table.insert(components, taskManagerComponent)
+
+                                    return components
+                                end
+                          ```
+
+                          The content of the LuaScript needs to be a whole function including both
+                          declaration and implementation.
+
+                          The parameters will be supplied by the system:
+                            - desiredObj: the object represents the configuration to be applied
+                                to the member cluster.
+
+                          The function expects one return value:
+                            - components: the resource requirements for each component.
+                          The returned value will be set into a ResourceBinding or ClusterResourceBinding.
+                        type: string
+                    required:
+                    - luaScript
+                    type: object
                   dependencyInterpretation:
                     description: |-
                       DependencyInterpretation describes the rules for Karmada to analyze the
diff --git a/charts/karmada/_crds/bases/policy/policy.karmada.io_clusterpropagationpolicies.yaml b/charts/karmada/_crds/bases/policy/policy.karmada.io_clusterpropagationpolicies.yaml
index 68a78ffcf..b9a99cf38 100644
--- a/charts/karmada/_crds/bases/policy/policy.karmada.io_clusterpropagationpolicies.yaml
+++ b/charts/karmada/_crds/bases/policy/policy.karmada.io_clusterpropagationpolicies.yaml
@@ -153,16 +153,19 @@ spec:
                         format: int32
                         type: integer
                       purgeMode:
-                        default: Graciously
+                        default: Gracefully
                         description: |-
                           PurgeMode represents how to deal with the legacy applications on the
                           cluster from which the application is migrated.
-                          Valid options are "Immediately", "Graciously" and "Never".
-                          Defaults to "Graciously".
+                          Valid options are "Directly", "Gracefully", "Never", "Immediately"(deprecated),
+                          and "Graciously"(deprecated).
+                          Defaults to "Gracefully".
                         enum:
+                        - Directly
+                        - Gracefully
+                        - Never
                         - Immediately
                         - Graciously
-                        - Never
                         type: string
                       statePreservation:
                         description: |-
@@ -223,6 +226,83 @@ spec:
                     required:
                     - decisionConditions
                     type: object
+                  cluster:
+                    description: |-
+                      Cluster indicates failover behaviors in case of cluster failure.
+                      If this value is nil, the failover behavior in case of cluster failure
+                      will be controlled by the controller's no-execute-taint-eviction-purge-mode
+                      parameter.
+                      If set, the failover behavior in case of cluster failure will be defined
+                      by this value.
+                    properties:
+                      purgeMode:
+                        default: Gracefully
+                        description: |-
+                          PurgeMode represents how to deal with the legacy applications on the
+                          cluster from which the application is migrated.
+                          Valid options are "Directly", "Gracefully".
+                          Defaults to "Gracefully".
+                        enum:
+                        - Directly
+                        - Gracefully
+                        type: string
+                      statePreservation:
+                        description: |-
+                          StatePreservation defines the policy for preserving and restoring state data
+                          during failover events for stateful applications.
+
+                          When an application fails over from one cluster to another, this policy enables
+                          the extraction of critical data from the original resource configuration.
+                          Upon successful migration, the extracted data is then re-injected into the new
+                          resource, ensuring that the application can resume operation with its previous
+                          state intact.
+                          This is particularly useful for stateful applications where maintaining data
+                          consistency across failover events is crucial.
+                          If not specified, means no state data will be preserved.
+
+                          Note: This requires the StatefulFailoverInjection feature gate to be enabled,
+                          which is alpha.
+                        properties:
+                          rules:
+                            description: |-
+                              Rules contains a list of StatePreservationRule configurations.
+                              Each rule specifies a JSONPath expression targeting specific pieces of
+                              state data to be preserved during failover events. An AliasLabelName is associated
+                              with each rule, serving as a label key when the preserved data is passed
+                              to the new cluster.
+                            items:
+                              description: |-
+                                StatePreservationRule defines a single rule for state preservation.
+                                It includes a JSONPath expression and an alias name that will be used
+                                as a label key when passing state information to the new cluster.
+                              properties:
+                                aliasLabelName:
+                                  description: |-
+                                    AliasLabelName is the name that will be used as a label key when the preserved
+                                    data is passed to the new cluster. This facilitates the injection of the
+                                    preserved state back into the application resources during recovery.
+                                  type: string
+                                jsonPath:
+                                  description: |-
+                                    JSONPath is the JSONPath template used to identify the state data
+                                    to be preserved from the original resource configuration.
+                                    The JSONPath syntax follows the Kubernetes specification:
+                                    https://kubernetes.io/docs/reference/kubectl/jsonpath/
+
+                                    Note: The JSONPath expression will start searching from the "status" field of
+                                    the API resource object by default. For example, to extract the "availableReplicas"
+                                    from a Deployment, the JSONPath expression should be "{.availableReplicas}", not
+                                    "{.status.availableReplicas}".
+                                  type: string
+                              required:
+                              - aliasLabelName
+                              - jsonPath
+                              type: object
+                            type: array
+                        required:
+                        - rules
+                        type: object
+                    type: object
                 type: object
               placement:
                 description: Placement represents the rule for select clusters to
diff --git a/charts/karmada/_crds/bases/policy/policy.karmada.io_propagationpolicies.yaml b/charts/karmada/_crds/bases/policy/policy.karmada.io_propagationpolicies.yaml
index 93ed73a18..304f02998 100644
--- a/charts/karmada/_crds/bases/policy/policy.karmada.io_propagationpolicies.yaml
+++ b/charts/karmada/_crds/bases/policy/policy.karmada.io_propagationpolicies.yaml
@@ -150,16 +150,19 @@ spec:
                         format: int32
                         type: integer
                       purgeMode:
-                        default: Graciously
+                        default: Gracefully
                         description: |-
                           PurgeMode represents how to deal with the legacy applications on the
                           cluster from which the application is migrated.
-                          Valid options are "Immediately", "Graciously" and "Never".
-                          Defaults to "Graciously".
+                          Valid options are "Directly", "Gracefully", "Never", "Immediately"(deprecated),
+                          and "Graciously"(deprecated).
+                          Defaults to "Gracefully".
                         enum:
+                        - Directly
+                        - Gracefully
+                        - Never
                         - Immediately
                         - Graciously
-                        - Never
                         type: string
                       statePreservation:
                         description: |-
@@ -220,6 +223,83 @@ spec:
                     required:
                     - decisionConditions
                     type: object
+                  cluster:
+                    description: |-
+                      Cluster indicates failover behaviors in case of cluster failure.
+                      If this value is nil, the failover behavior in case of cluster failure
+                      will be controlled by the controller's no-execute-taint-eviction-purge-mode
+                      parameter.
+                      If set, the failover behavior in case of cluster failure will be defined
+                      by this value.
+                    properties:
+                      purgeMode:
+                        default: Gracefully
+                        description: |-
+                          PurgeMode represents how to deal with the legacy applications on the
+                          cluster from which the application is migrated.
+                          Valid options are "Directly", "Gracefully".
+                          Defaults to "Gracefully".
+                        enum:
+                        - Directly
+                        - Gracefully
+                        type: string
+                      statePreservation:
+                        description: |-
+                          StatePreservation defines the policy for preserving and restoring state data
+                          during failover events for stateful applications.
+
+                          When an application fails over from one cluster to another, this policy enables
+                          the extraction of critical data from the original resource configuration.
+                          Upon successful migration, the extracted data is then re-injected into the new
+                          resource, ensuring that the application can resume operation with its previous
+                          state intact.
+                          This is particularly useful for stateful applications where maintaining data
+                          consistency across failover events is crucial.
+                          If not specified, means no state data will be preserved.
+
+                          Note: This requires the StatefulFailoverInjection feature gate to be enabled,
+                          which is alpha.
+                        properties:
+                          rules:
+                            description: |-
+                              Rules contains a list of StatePreservationRule configurations.
+                              Each rule specifies a JSONPath expression targeting specific pieces of
+                              state data to be preserved during failover events. An AliasLabelName is associated
+                              with each rule, serving as a label key when the preserved data is passed
+                              to the new cluster.
+                            items:
+                              description: |-
+                                StatePreservationRule defines a single rule for state preservation.
+                                It includes a JSONPath expression and an alias name that will be used
+                                as a label key when passing state information to the new cluster.
+                              properties:
+                                aliasLabelName:
+                                  description: |-
+                                    AliasLabelName is the name that will be used as a label key when the preserved
+                                    data is passed to the new cluster. This facilitates the injection of the
+                                    preserved state back into the application resources during recovery.
+                                  type: string
+                                jsonPath:
+                                  description: |-
+                                    JSONPath is the JSONPath template used to identify the state data
+                                    to be preserved from the original resource configuration.
+                                    The JSONPath syntax follows the Kubernetes specification:
+                                    https://kubernetes.io/docs/reference/kubectl/jsonpath/
+
+                                    Note: The JSONPath expression will start searching from the "status" field of
+                                    the API resource object by default. For example, to extract the "availableReplicas"
+                                    from a Deployment, the JSONPath expression should be "{.availableReplicas}", not
+                                    "{.status.availableReplicas}".
+                                  type: string
+                              required:
+                              - aliasLabelName
+                              - jsonPath
+                              type: object
+                            type: array
+                        required:
+                        - rules
+                        type: object
+                    type: object
                 type: object
               placement:
                 description: Placement represents the rule for select clusters to
diff --git a/charts/karmada/_crds/bases/work/work.karmada.io_clusterresourcebindings.yaml b/charts/karmada/_crds/bases/work/work.karmada.io_clusterresourcebindings.yaml
index 30e378964..fe2dacb7f 100644
--- a/charts/karmada/_crds/bases/work/work.karmada.io_clusterresourcebindings.yaml
+++ b/charts/karmada/_crds/bases/work/work.karmada.io_clusterresourcebindings.yaml
@@ -261,6 +261,201 @@ spec:
                   - name
                   type: object
                 type: array
+              components:
+                description: |-
+                  Components represents the requirements of multiple pod templates of the referencing resource.
+                  It is designed to support workloads that consist of multiple pod templates,
+                  such as distributed training jobs (e.g., PyTorch, TensorFlow) and big data workloads (e.g., FlinkDeployment),
+                  where each workload is composed of more than one pod template. It is also capable of representing
+                  single-component workloads, such as Deployment.
+
+                  Note: This field is intended to replace the legacy ReplicaRequirements and Replicas fields above.
+                  It is only populated when the MultiplePodTemplatesScheduling feature gate is enabled.
+                items:
+                  description: ComponentRequirements represents the requirements for
+                    a specific component.
+                  properties:
+                    name:
+                      description: |-
+                        Name of this component.
+                        It is required when the resource contains multiple components to ensure proper identification,
+                        and must also be unique within the same resource.
+                      maxLength: 32
+                      type: string
+                    replicaRequirements:
+                      description: ReplicaRequirements represents the requirements
+                        required by each replica for this component.
+                      properties:
+                        namespace:
+                          description: Namespace represents the resources namespaces
+                          type: string
+                        nodeClaim:
+                          description: NodeClaim represents the node claim HardNodeAffinity,
+                            NodeSelector and Tolerations required by each replica.
+                          properties:
+                            hardNodeAffinity:
+                              description: |-
+                                A node selector represents the union of the results of one or more label queries over a set of
+                                nodes; that is, it represents the OR of the selectors represented by the node selector terms.
+                                Note that only PodSpec.Affinity.NodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution
+                                is included here because it has a hard limit on pod scheduling.
+                              properties:
+                                nodeSelectorTerms:
+                                  description: Required. A list of node selector terms.
+                                    The terms are ORed.
+                                  items:
+                                    description: |-
+                                      A null or empty node selector term matches no objects. The requirements of
+                                      them are ANDed.
+                                      The TopologySelectorTerm type implements a subset of the NodeSelectorTerm.
+                                    properties:
+                                      matchExpressions:
+                                        description: A list of node selector requirements
+                                          by node's labels.
+                                        items:
+                                          description: |-
+                                            A node selector requirement is a selector that contains values, a key, and an operator
+                                            that relates the key and values.
+                                          properties:
+                                            key:
+                                              description: The label key that the
+                                                selector applies to.
+                                              type: string
+                                            operator:
+                                              description: |-
+                                                Represents a key's relationship to a set of values.
+                                                Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt.
+                                              type: string
+                                            values:
+                                              description: |-
+                                                An array of string values. If the operator is In or NotIn,
+                                                the values array must be non-empty. If the operator is Exists or DoesNotExist,
+                                                the values array must be empty. If the operator is Gt or Lt, the values
+                                                array must have a single element, which will be interpreted as an integer.
+                                                This array is replaced during a strategic merge patch.
+                                              items:
+                                                type: string
+                                              type: array
+                                              x-kubernetes-list-type: atomic
+                                          required:
+                                          - key
+                                          - operator
+                                          type: object
+                                        type: array
+                                        x-kubernetes-list-type: atomic
+                                      matchFields:
+                                        description: A list of node selector requirements
+                                          by node's fields.
+                                        items:
+                                          description: |-
+                                            A node selector requirement is a selector that contains values, a key, and an operator
+                                            that relates the key and values.
+                                          properties:
+                                            key:
+                                              description: The label key that the
+                                                selector applies to.
+                                              type: string
+                                            operator:
+                                              description: |-
+                                                Represents a key's relationship to a set of values.
+                                                Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt.
+                                              type: string
+                                            values:
+                                              description: |-
+                                                An array of string values. If the operator is In or NotIn,
+                                                the values array must be non-empty. If the operator is Exists or DoesNotExist,
+                                                the values array must be empty. If the operator is Gt or Lt, the values
+                                                array must have a single element, which will be interpreted as an integer.
+                                                This array is replaced during a strategic merge patch.
+                                              items:
+                                                type: string
+                                              type: array
+                                              x-kubernetes-list-type: atomic
+                                          required:
+                                          - key
+                                          - operator
+                                          type: object
+                                        type: array
+                                        x-kubernetes-list-type: atomic
+                                    type: object
+                                    x-kubernetes-map-type: atomic
+                                  type: array
+                                  x-kubernetes-list-type: atomic
+                              required:
+                              - nodeSelectorTerms
+                              type: object
+                              x-kubernetes-map-type: atomic
+                            nodeSelector:
+                              additionalProperties:
+                                type: string
+                              description: |-
+                                NodeSelector is a selector which must be true for the pod to fit on a node.
+                                Selector which must match a node's labels for the pod to be scheduled on that node.
+                              type: object
+                            tolerations:
+                              description: If specified, the pod's tolerations.
+                              items:
+                                description: |-
+                                  The pod this Toleration is attached to tolerates any taint that matches
+                                  the triple <key,value,effect> using the matching operator <operator>.
+                                properties:
+                                  effect:
+                                    description: |-
+                                      Effect indicates the taint effect to match. Empty means match all taint effects.
+                                      When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute.
+                                    type: string
+                                  key:
+                                    description: |-
+                                      Key is the taint key that the toleration applies to. Empty means match all taint keys.
+                                      If the key is empty, operator must be Exists; this combination means to match all values and all keys.
+                                    type: string
+                                  operator:
+                                    description: |-
+                                      Operator represents a key's relationship to the value.
+                                      Valid operators are Exists and Equal. Defaults to Equal.
+                                      Exists is equivalent to wildcard for value, so that a pod can
+                                      tolerate all taints of a particular category.
+                                    type: string
+                                  tolerationSeconds:
+                                    description: |-
+                                      TolerationSeconds represents the period of time the toleration (which must be
+                                      of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default,
+                                      it is not set, which means tolerate the taint forever (do not evict). Zero and
+                                      negative values will be treated as 0 (evict immediately) by the system.
+                                    format: int64
+                                    type: integer
+                                  value:
+                                    description: |-
+                                      Value is the taint value the toleration matches to.
+                                      If the operator is Exists, the value should be empty, otherwise just a regular string.
+                                    type: string
+                                type: object
+                              type: array
+                          type: object
+                        priorityClassName:
+                          description: PriorityClassName represents the resources
+                            priorityClassName
+                          type: string
+                        resourceRequest:
+                          additionalProperties:
+                            anyOf:
+                            - type: integer
+                            - type: string
+                            pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+                            x-kubernetes-int-or-string: true
+                          description: ResourceRequest represents the resources required
+                            by each replica.
+                          type: object
+                      type: object
+                    replicas:
+                      description: Replicas represents the replica number of the resource's
+                        component.
+                      format: int32
+                      type: integer
+                  required:
+                  - replicas
+                  type: object
+                type: array
               conflictResolution:
                 default: Abort
                 description: |-
@@ -315,16 +510,19 @@ spec:
                         format: int32
                         type: integer
                       purgeMode:
-                        default: Graciously
+                        default: Gracefully
                         description: |-
                           PurgeMode represents how to deal with the legacy applications on the
                           cluster from which the application is migrated.
-                          Valid options are "Immediately", "Graciously" and "Never".
-                          Defaults to "Graciously".
+                          Valid options are "Directly", "Gracefully", "Never", "Immediately"(deprecated),
+                          and "Graciously"(deprecated).
+                          Defaults to "Gracefully".
                         enum:
+                        - Directly
+                        - Gracefully
+                        - Never
                         - Immediately
                         - Graciously
-                        - Never
                         type: string
                       statePreservation:
                         description: |-
@@ -385,6 +583,83 @@ spec:
                     required:
                     - decisionConditions
                     type: object
+                  cluster:
+                    description: |-
+                      Cluster indicates failover behaviors in case of cluster failure.
+                      If this value is nil, the failover behavior in case of cluster failure
+                      will be controlled by the controller's no-execute-taint-eviction-purge-mode
+                      parameter.
+                      If set, the failover behavior in case of cluster failure will be defined
+                      by this value.
+                    properties:
+                      purgeMode:
+                        default: Gracefully
+                        description: |-
+                          PurgeMode represents how to deal with the legacy applications on the
+                          cluster from which the application is migrated.
+                          Valid options are "Directly", "Gracefully".
+                          Defaults to "Gracefully".
+                        enum:
+                        - Directly
+                        - Gracefully
+                        type: string
+                      statePreservation:
+                        description: |-
+                          StatePreservation defines the policy for preserving and restoring state data
+                          during failover events for stateful applications.
+
+                          When an application fails over from one cluster to another, this policy enables
+                          the extraction of critical data from the original resource configuration.
+                          Upon successful migration, the extracted data is then re-injected into the new
+                          resource, ensuring that the application can resume operation with its previous
+                          state intact.
+                          This is particularly useful for stateful applications where maintaining data
+                          consistency across failover events is crucial.
+                          If not specified, means no state data will be preserved.
+
+                          Note: This requires the StatefulFailoverInjection feature gate to be enabled,
+                          which is alpha.
+                        properties:
+                          rules:
+                            description: |-
+                              Rules contains a list of StatePreservationRule configurations.
+                              Each rule specifies a JSONPath expression targeting specific pieces of
+                              state data to be preserved during failover events. An AliasLabelName is associated
+                              with each rule, serving as a label key when the preserved data is passed
+                              to the new cluster.
+                            items:
+                              description: |-
+                                StatePreservationRule defines a single rule for state preservation.
+                                It includes a JSONPath expression and an alias name that will be used
+                                as a label key when passing state information to the new cluster.
+                              properties:
+                                aliasLabelName:
+                                  description: |-
+                                    AliasLabelName is the name that will be used as a label key when the preserved
+                                    data is passed to the new cluster. This facilitates the injection of the
+                                    preserved state back into the application resources during recovery.
+                                  type: string
+                                jsonPath:
+                                  description: |-
+                                    JSONPath is the JSONPath template used to identify the state data
+                                    to be preserved from the original resource configuration.
+                                    The JSONPath syntax follows the Kubernetes specification:
+                                    https://kubernetes.io/docs/reference/kubectl/jsonpath/
+
+                                    Note: The JSONPath expression will start searching from the "status" field of
+                                    the API resource object by default. For example, to extract the "availableReplicas"
+                                    from a Deployment, the JSONPath expression should be "{.availableReplicas}", not
+                                    "{.status.availableReplicas}".
+                                  type: string
+                              required:
+                              - aliasLabelName
+                              - jsonPath
+                              type: object
+                            type: array
+                        required:
+                        - rules
+                        type: object
+                    type: object
                 type: object
               gracefulEvictionTasks:
                 description: |-
@@ -454,10 +729,12 @@ spec:
                       description: |-
                         PurgeMode represents how to deal with the legacy applications on the
                         cluster from which the application is migrated.
-                        Valid options are "Immediately", "Graciously" and "Never".
+                        Valid options are "Immediately", "Directly", "Graciously", "Gracefully" and "Never".
                       enum:
                       - Immediately
+                      - Directly
                       - Graciously
+                      - Gracefully
                       - Never
                       type: string
                     reason:
diff --git a/charts/karmada/_crds/bases/work/work.karmada.io_resourcebindings.yaml b/charts/karmada/_crds/bases/work/work.karmada.io_resourcebindings.yaml
index 192402246..1507d824c 100644
--- a/charts/karmada/_crds/bases/work/work.karmada.io_resourcebindings.yaml
+++ b/charts/karmada/_crds/bases/work/work.karmada.io_resourcebindings.yaml
@@ -261,6 +261,201 @@ spec:
                   - name
                   type: object
                 type: array
+              components:
+                description: |-
+                  Components represents the requirements of multiple pod templates of the referencing resource.
+                  It is designed to support workloads that consist of multiple pod templates,
+                  such as distributed training jobs (e.g., PyTorch, TensorFlow) and big data workloads (e.g., FlinkDeployment),
+                  where each workload is composed of more than one pod template. It is also capable of representing
+                  single-component workloads, such as Deployment.
+
+                  Note: This field is intended to replace the legacy ReplicaRequirements and Replicas fields above.
+                  It is only populated when the MultiplePodTemplatesScheduling feature gate is enabled.
+                items:
+                  description: ComponentRequirements represents the requirements for
+                    a specific component.
+                  properties:
+                    name:
+                      description: |-
+                        Name of this component.
+                        It is required when the resource contains multiple components to ensure proper identification,
+                        and must also be unique within the same resource.
+                      maxLength: 32
+                      type: string
+                    replicaRequirements:
+                      description: ReplicaRequirements represents the requirements
+                        required by each replica for this component.
+                      properties:
+                        namespace:
+                          description: Namespace represents the resources namespaces
+                          type: string
+                        nodeClaim:
+                          description: NodeClaim represents the node claim HardNodeAffinity,
+                            NodeSelector and Tolerations required by each replica.
+                          properties:
+                            hardNodeAffinity:
+                              description: |-
+                                A node selector represents the union of the results of one or more label queries over a set of
+                                nodes; that is, it represents the OR of the selectors represented by the node selector terms.
+                                Note that only PodSpec.Affinity.NodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution
+                                is included here because it has a hard limit on pod scheduling.
+                              properties:
+                                nodeSelectorTerms:
+                                  description: Required. A list of node selector terms.
+                                    The terms are ORed.
+                                  items:
+                                    description: |-
+                                      A null or empty node selector term matches no objects. The requirements of
+                                      them are ANDed.
+                                      The TopologySelectorTerm type implements a subset of the NodeSelectorTerm.
+                                    properties:
+                                      matchExpressions:
+                                        description: A list of node selector requirements
+                                          by node's labels.
+                                        items:
+                                          description: |-
+                                            A node selector requirement is a selector that contains values, a key, and an operator
+                                            that relates the key and values.
+                                          properties:
+                                            key:
+                                              description: The label key that the
+                                                selector applies to.
+                                              type: string
+                                            operator:
+                                              description: |-
+                                                Represents a key's relationship to a set of values.
+                                                Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt.
+                                              type: string
+                                            values:
+                                              description: |-
+                                                An array of string values. If the operator is In or NotIn,
+                                                the values array must be non-empty. If the operator is Exists or DoesNotExist,
+                                                the values array must be empty. If the operator is Gt or Lt, the values
+                                                array must have a single element, which will be interpreted as an integer.
+                                                This array is replaced during a strategic merge patch.
+                                              items:
+                                                type: string
+                                              type: array
+                                              x-kubernetes-list-type: atomic
+                                          required:
+                                          - key
+                                          - operator
+                                          type: object
+                                        type: array
+                                        x-kubernetes-list-type: atomic
+                                      matchFields:
+                                        description: A list of node selector requirements
+                                          by node's fields.
+                                        items:
+                                          description: |-
+                                            A node selector requirement is a selector that contains values, a key, and an operator
+                                            that relates the key and values.
+                                          properties:
+                                            key:
+                                              description: The label key that the
+                                                selector applies to.
+                                              type: string
+                                            operator:
+                                              description: |-
+                                                Represents a key's relationship to a set of values.
+                                                Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt.
+                                              type: string
+                                            values:
+                                              description: |-
+                                                An array of string values. If the operator is In or NotIn,
+                                                the values array must be non-empty. If the operator is Exists or DoesNotExist,
+                                                the values array must be empty. If the operator is Gt or Lt, the values
+                                                array must have a single element, which will be interpreted as an integer.
+                                                This array is replaced during a strategic merge patch.
+                                              items:
+                                                type: string
+                                              type: array
+                                              x-kubernetes-list-type: atomic
+                                          required:
+                                          - key
+                                          - operator
+                                          type: object
+                                        type: array
+                                        x-kubernetes-list-type: atomic
+                                    type: object
+                                    x-kubernetes-map-type: atomic
+                                  type: array
+                                  x-kubernetes-list-type: atomic
+                              required:
+                              - nodeSelectorTerms
+                              type: object
+                              x-kubernetes-map-type: atomic
+                            nodeSelector:
+                              additionalProperties:
+                                type: string
+                              description: |-
+                                NodeSelector is a selector which must be true for the pod to fit on a node.
+                                Selector which must match a node's labels for the pod to be scheduled on that node.
+                              type: object
+                            tolerations:
+                              description: If specified, the pod's tolerations.
+                              items:
+                                description: |-
+                                  The pod this Toleration is attached to tolerates any taint that matches
+                                  the triple <key,value,effect> using the matching operator <operator>.
+                                properties:
+                                  effect:
+                                    description: |-
+                                      Effect indicates the taint effect to match. Empty means match all taint effects.
+                                      When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute.
+                                    type: string
+                                  key:
+                                    description: |-
+                                      Key is the taint key that the toleration applies to. Empty means match all taint keys.
+                                      If the key is empty, operator must be Exists; this combination means to match all values and all keys.
+                                    type: string
+                                  operator:
+                                    description: |-
+                                      Operator represents a key's relationship to the value.
+                                      Valid operators are Exists and Equal. Defaults to Equal.
+                                      Exists is equivalent to wildcard for value, so that a pod can
+                                      tolerate all taints of a particular category.
+                                    type: string
+                                  tolerationSeconds:
+                                    description: |-
+                                      TolerationSeconds represents the period of time the toleration (which must be
+                                      of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default,
+                                      it is not set, which means tolerate the taint forever (do not evict). Zero and
+                                      negative values will be treated as 0 (evict immediately) by the system.
+                                    format: int64
+                                    type: integer
+                                  value:
+                                    description: |-
+                                      Value is the taint value the toleration matches to.
+                                      If the operator is Exists, the value should be empty, otherwise just a regular string.
+                                    type: string
+                                type: object
+                              type: array
+                          type: object
+                        priorityClassName:
+                          description: PriorityClassName represents the resources
+                            priorityClassName
+                          type: string
+                        resourceRequest:
+                          additionalProperties:
+                            anyOf:
+                            - type: integer
+                            - type: string
+                            pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+                            x-kubernetes-int-or-string: true
+                          description: ResourceRequest represents the resources required
+                            by each replica.
+                          type: object
+                      type: object
+                    replicas:
+                      description: Replicas represents the replica number of the resource's
+                        component.
+                      format: int32
+                      type: integer
+                  required:
+                  - replicas
+                  type: object
+                type: array
               conflictResolution:
                 default: Abort
                 description: |-
@@ -315,16 +510,19 @@ spec:
                         format: int32
                         type: integer
                       purgeMode:
-                        default: Graciously
+                        default: Gracefully
                         description: |-
                           PurgeMode represents how to deal with the legacy applications on the
                           cluster from which the application is migrated.
-                          Valid options are "Immediately", "Graciously" and "Never".
-                          Defaults to "Graciously".
+                          Valid options are "Directly", "Gracefully", "Never", "Immediately"(deprecated),
+                          and "Graciously"(deprecated).
+                          Defaults to "Gracefully".
                         enum:
+                        - Directly
+                        - Gracefully
+                        - Never
                         - Immediately
                         - Graciously
-                        - Never
                         type: string
                       statePreservation:
                         description: |-
@@ -385,6 +583,83 @@ spec:
                     required:
                     - decisionConditions
                     type: object
+                  cluster:
+                    description: |-
+                      Cluster indicates failover behaviors in case of cluster failure.
+                      If this value is nil, the failover behavior in case of cluster failure
+                      will be controlled by the controller's no-execute-taint-eviction-purge-mode
+                      parameter.
+                      If set, the failover behavior in case of cluster failure will be defined
+                      by this value.
+                    properties:
+                      purgeMode:
+                        default: Gracefully
+                        description: |-
+                          PurgeMode represents how to deal with the legacy applications on the
+                          cluster from which the application is migrated.
+                          Valid options are "Directly", "Gracefully".
+                          Defaults to "Gracefully".
+                        enum:
+                        - Directly
+                        - Gracefully
+                        type: string
+                      statePreservation:
+                        description: |-
+                          StatePreservation defines the policy for preserving and restoring state data
+                          during failover events for stateful applications.
+
+                          When an application fails over from one cluster to another, this policy enables
+                          the extraction of critical data from the original resource configuration.
+                          Upon successful migration, the extracted data is then re-injected into the new
+                          resource, ensuring that the application can resume operation with its previous
+                          state intact.
+                          This is particularly useful for stateful applications where maintaining data
+                          consistency across failover events is crucial.
+                          If not specified, means no state data will be preserved.
+
+                          Note: This requires the StatefulFailoverInjection feature gate to be enabled,
+                          which is alpha.
+                        properties:
+                          rules:
+                            description: |-
+                              Rules contains a list of StatePreservationRule configurations.
+                              Each rule specifies a JSONPath expression targeting specific pieces of
+                              state data to be preserved during failover events. An AliasLabelName is associated
+                              with each rule, serving as a label key when the preserved data is passed
+                              to the new cluster.
+                            items:
+                              description: |-
+                                StatePreservationRule defines a single rule for state preservation.
+                                It includes a JSONPath expression and an alias name that will be used
+                                as a label key when passing state information to the new cluster.
+                              properties:
+                                aliasLabelName:
+                                  description: |-
+                                    AliasLabelName is the name that will be used as a label key when the preserved
+                                    data is passed to the new cluster. This facilitates the injection of the
+                                    preserved state back into the application resources during recovery.
+                                  type: string
+                                jsonPath:
+                                  description: |-
+                                    JSONPath is the JSONPath template used to identify the state data
+                                    to be preserved from the original resource configuration.
+                                    The JSONPath syntax follows the Kubernetes specification:
+                                    https://kubernetes.io/docs/reference/kubectl/jsonpath/
+
+                                    Note: The JSONPath expression will start searching from the "status" field of
+                                    the API resource object by default. For example, to extract the "availableReplicas"
+                                    from a Deployment, the JSONPath expression should be "{.availableReplicas}", not
+                                    "{.status.availableReplicas}".
+                                  type: string
+                              required:
+                              - aliasLabelName
+                              - jsonPath
+                              type: object
+                            type: array
+                        required:
+                        - rules
+                        type: object
+                    type: object
                 type: object
               gracefulEvictionTasks:
                 description: |-
@@ -454,10 +729,12 @@ spec:
                       description: |-
                         PurgeMode represents how to deal with the legacy applications on the
                         cluster from which the application is migrated.
-                        Valid options are "Immediately", "Graciously" and "Never".
+                        Valid options are "Immediately", "Directly", "Graciously", "Gracefully" and "Never".
                       enum:
                       - Immediately
+                      - Directly
                       - Graciously
+                      - Gracefully
                       - Never
                       type: string
                     reason:
diff --git a/charts/karmada/templates/_helpers.tpl b/charts/karmada/templates/_helpers.tpl
index dace87448..5b251304c 100644
--- a/charts/karmada/templates/_helpers.tpl
+++ b/charts/karmada/templates/_helpers.tpl
@@ -556,18 +556,22 @@ Return the proper Docker Image Registry Secret Names
 {{ include "common.images.pullSecrets" (dict "images" (list .Values.cfssl.image .Values.kubectl.image .Values.etcd.internal.image .Values.agent.image .Values.apiServer.image .Values.controllerManager.image .Values.descheduler.image .Values.schedulerEstimator.image .Values.scheduler.image .Values.webhook.image .Values.aggregatedApiServer.image .Values.metricsAdapter.image .Values.search.image .Values.kubeControllerManager.image) "global" .Values.global) }}
 {{- end -}}
 
+{{- /* 
+Generate the --feature-gates command line argument for karmada-controllerManager.
+Iterates over .Values.controllerManager.featureGates and constructs a comma-separated key=value list.
+If any feature gates are set, outputs: --feature-gates=Foo=true,Bar=false
+If none are set, outputs nothing.
+*/ -}}
 {{- define "karmada.controllerManager.featureGates" -}}
-     {{- if (not (empty .Values.controllerManager.featureGates)) }}
-          {{- $featureGatesFlag := "" -}}
+     {{- if .Values.controllerManager.featureGates -}}
+          {{- $featureGates := list -}}
           {{- range $key, $value := .Values.controllerManager.featureGates -}}
-               {{- if not (empty (toString $value)) }}
-                    {{- $featureGatesFlag = cat $featureGatesFlag $key "=" $value ","  -}}
+               {{- if not (empty (toString $value)) -}}
+                    {{- $featureGates = append $featureGates (printf "%s=%s" $key (toString $value)) -}}
                {{- end -}}
           {{- end -}}
-
-          {{- if gt (len $featureGatesFlag) 0 }}
-               {{- $featureGatesFlag := trimSuffix "," $featureGatesFlag  | nospace -}}
-               {{- printf "%s=%s" "--feature-gates" $featureGatesFlag -}}
+          {{- if $featureGates -}}
+               {{- printf "--feature-gates=%s" (join "," $featureGates) | nospace -}}
           {{- end -}}
      {{- end -}}
 {{- end -}}
@@ -579,34 +583,55 @@ If any feature gates are set, outputs: --feature-gates=Foo=true,Bar=false
 If none are set, outputs nothing.
 */ -}}
 {{- define "karmada.webhook.featureGates" -}}
-     {{- if (not (empty .Values.webhook.featureGates)) }}
-          {{- $featureGatesFlag := "" -}}
+     {{- if .Values.webhook.featureGates -}}
+          {{- $featureGates := list -}}
           {{- range $key, $value := .Values.webhook.featureGates -}}
-               {{- if not (empty (toString $value)) }}
-                    {{- $featureGatesFlag = cat $featureGatesFlag $key "=" $value ","  -}}
+               {{- if not (empty (toString $value)) -}}
+                    {{- $featureGates = append $featureGates (printf "%s=%s" $key (toString $value)) -}}
                {{- end -}}
           {{- end -}}
+          {{- if $featureGates -}}
+               {{- printf "--feature-gates=%s" (join "," $featureGates) | nospace -}}
+          {{- end -}}
+     {{- end -}}
+{{- end -}}
 
-          {{- if gt (len $featureGatesFlag) 0 }}
-               {{- $featureGatesFlag := trimSuffix "," $featureGatesFlag  | nospace -}}
-               {{- printf "%s=%s" "--feature-gates" $featureGatesFlag -}}
+{{- /* 
+Generate the --feature-gates command line argument for karmada-scheduler.
+Iterates over .Values.scheduler.featureGates and constructs a comma-separated key=value list.
+If any feature gates are set, outputs: --feature-gates=Foo=true,Bar=false
+If none are set, outputs nothing.
+*/ -}}
+{{- define "karmada.scheduler.featureGates" -}}
+     {{- if .Values.scheduler.featureGates -}}
+          {{- $featureGates := list -}}
+          {{- range $key, $value := .Values.scheduler.featureGates -}}
+               {{- if not (empty (toString $value)) -}}
+                    {{- $featureGates = append $featureGates (printf "%s=%s" $key (toString $value)) -}}
+               {{- end -}}
+          {{- end -}}
+          {{- if $featureGates -}}
+               {{- printf "--feature-gates=%s" (join "," $featureGates) | nospace -}}
           {{- end -}}
      {{- end -}}
 {{- end -}}
 
+{{- /* 
+Generate the --feature-gates command line argument for karmada-schedulerEstimator.
+Iterates over .Values.schedulerEstimator.featureGates and constructs a comma-separated key=value list.
+If any feature gates are set, outputs: --feature-gates=Foo=true,Bar=false
+If none are set, outputs nothing.
+*/ -}}
 {{- define "karmada.schedulerEstimator.featureGates" -}}
-     {{- $featureGatesArg := index . "featureGatesArg" -}}
-     {{- if (not (empty $featureGatesArg)) }}
-          {{- $featureGatesFlag := "" -}}
-          {{- range $key, $value := $featureGatesArg -}}
-               {{- if not (empty (toString $value)) }}
-                    {{- $featureGatesFlag = cat $featureGatesFlag $key "=" $value ","  -}}
+     {{- if .Values.schedulerEstimator.featureGates -}}
+          {{- $featureGates := list -}}
+          {{- range $key, $value := .Values.schedulerEstimator.featureGates -}}
+               {{- if not (empty (toString $value)) -}}
+                    {{- $featureGates = append $featureGates (printf "%s=%s" $key (toString $value)) -}}
                {{- end -}}
           {{- end -}}
-
-          {{- if gt (len $featureGatesFlag) 0 }}
-               {{- $featureGatesFlag := trimSuffix "," $featureGatesFlag  | nospace -}}
-               {{- printf "%s=%s" "--feature-gates" $featureGatesFlag -}}
+          {{- if $featureGates -}}
+               {{- printf "--feature-gates=%s" (join "," $featureGates) | nospace -}}
           {{- end -}}
      {{- end -}}
 {{- end -}}
diff --git a/charts/karmada/templates/karmada-scheduler-estimator.yaml b/charts/karmada/templates/karmada-scheduler-estimator.yaml
index 8536bac55..3e54bf016 100644
--- a/charts/karmada/templates/karmada-scheduler-estimator.yaml
+++ b/charts/karmada/templates/karmada-scheduler-estimator.yaml
@@ -55,9 +55,14 @@ spec:
             - --grpc-client-ca-file=/etc/karmada/pki/server-ca.crt
             - --metrics-bind-address=$(POD_IP):8080
             - --health-probe-bind-address=$(POD_IP):10351
-            {{- with (include "karmada.schedulerEstimator.featureGates" (dict "featureGatesArg" $.Values.schedulerEstimator.featureGates)) }}
+            {{- /*
+            We use '$' to refer to the root context.
+            Inside this 'range' loop, '.' refers to the current item from '.Values.schedulerEstimator.memberClusters'.
+            Using '$' ensures that we can access the top-level '.Values.schedulerEstimator.featureGates'.
+            */}}
+            {{- with (include "karmada.schedulerEstimator.featureGates" $) }}
             - {{ . }}
-            {{- end}}
+            {{- end }}
           livenessProbe:
             httpGet:
               path: /healthz
diff --git a/charts/karmada/templates/karmada-scheduler.yaml b/charts/karmada/templates/karmada-scheduler.yaml
index aceb9d0d7..ab4866321 100644
--- a/charts/karmada/templates/karmada-scheduler.yaml
+++ b/charts/karmada/templates/karmada-scheduler.yaml
@@ -61,6 +61,9 @@ spec:
             {{- if .Values.scheduler.enableSchedulerEstimator }}
             - --enable-scheduler-estimator=true
             {{- end }}
+            {{- with (include "karmada.scheduler.featureGates" .) }}
+            - {{ . }}
+            {{- end }}
           livenessProbe:
             httpGet:
               path: /healthz
diff --git a/charts/karmada/values.yaml b/charts/karmada/values.yaml
index 8c42bf7bc..bd752d812 100644
--- a/charts/karmada/values.yaml
+++ b/charts/karmada/values.yaml
@@ -245,6 +245,8 @@ scheduler:
   priorityClassName: "system-node-critical"
   ## @param scheduler.enableSchedulerEstimator enable scheduler estimator
   enableSchedulerEstimator: false
+  ## @param scheduler.featureGates A set of key=value pairs that describe feature gates for karmada-scheduler
+  featureGates: {}
 
 ## webhook config
 webhook:
@@ -299,11 +301,11 @@ webhook:
     rollingUpdate:
       maxUnavailable: 0
       maxSurge: 50%
-  ## @param apiServer.podDisruptionBudget
+  ## @param webhook.podDisruptionBudget
   podDisruptionBudget: *podDisruptionBudget
   ## @param webhook.priorityClassName the priority class name for the karmada-webhook
   priorityClassName: "system-node-critical"
-  ## @param featureGate to webhook
+  ## @param webhook.featureGates A set of key=value pairs that describe feature gates for karmada-webhook
   featureGates: {}
 
 ## controller manager config
diff --git a/cmd/agent/app/agent.go b/cmd/agent/app/agent.go
index e41828760..57ebc349f 100644
--- a/cmd/agent/app/agent.go
+++ b/cmd/agent/app/agent.go
@@ -34,6 +34,7 @@ import (
 	logsv1 "k8s.io/component-base/logs/api/v1"
 	"k8s.io/component-base/term"
 	"k8s.io/klog/v2"
+	"k8s.io/utils/ptr"
 	controllerruntime "sigs.k8s.io/controller-runtime"
 	"sigs.k8s.io/controller-runtime/pkg/cache"
 	"sigs.k8s.io/controller-runtime/pkg/config"
@@ -225,6 +226,7 @@ func run(ctx context.Context, opts *options.Options) error {
 				clusterv1alpha1.SchemeGroupVersion.WithKind("Cluster").GroupKind().String(): opts.ConcurrentClusterSyncs,
 			},
 			CacheSyncTimeout: opts.ClusterCacheSyncTimeout.Duration,
+			UsePriorityQueue: ptr.To(features.FeatureGate.Enabled(features.ControllerPriorityQueue)),
 		},
 		NewCache: func(config *rest.Config, opts cache.Options) (cache.Cache, error) {
 			opts.DefaultTransform = fedinformer.StripUnusedFields
@@ -271,10 +273,9 @@ func setupControllers(ctx context.Context, mgr controllerruntime.Manager, opts *
 	sharedFactory.WaitForCacheSync(ctx.Done())
 
 	resourceInterpreter := resourceinterpreter.NewResourceInterpreter(controlPlaneInformerManager, serviceLister)
-	if err := mgr.Add(resourceInterpreter); err != nil {
-		return fmt.Errorf("failed to setup custom resource interpreter: %w", err)
+	if err := resourceInterpreter.Start(ctx); err != nil {
+		return fmt.Errorf("failed to start resource interpreter: %w", err)
 	}
-
 	rateLimiterGetter := util.GetClusterRateLimiterGetter().SetDefaultLimits(opts.ClusterAPIQPS, opts.ClusterAPIBurst)
 	clusterClientOption := &util.ClientOption{RateLimiterGetter: rateLimiterGetter.GetRateLimiter}
 
diff --git a/cmd/controller-manager/app/controllermanager.go b/cmd/controller-manager/app/controllermanager.go
index 6e4868de2..7fda3dc19 100644
--- a/cmd/controller-manager/app/controllermanager.go
+++ b/cmd/controller-manager/app/controllermanager.go
@@ -38,6 +38,7 @@ import (
 	resourceclient "k8s.io/metrics/pkg/client/clientset/versioned/typed/metrics/v1beta1"
 	"k8s.io/metrics/pkg/client/custom_metrics"
 	"k8s.io/metrics/pkg/client/external_metrics"
+	"k8s.io/utils/ptr"
 	controllerruntime "sigs.k8s.io/controller-runtime"
 	"sigs.k8s.io/controller-runtime/pkg/cache"
 	"sigs.k8s.io/controller-runtime/pkg/config"
@@ -193,6 +194,7 @@ func Run(ctx context.Context, opts *options.Options) error {
 				schema.GroupVersionKind{Group: "", Version: "v1", Kind: "Namespace"}.GroupKind().String(): opts.ConcurrentNamespaceSyncs,
 			},
 			CacheSyncTimeout: opts.ClusterCacheSyncTimeout.Duration,
+			UsePriorityQueue: ptr.To(features.FeatureGate.Enabled(features.ControllerPriorityQueue)),
 		},
 		NewCache: func(config *rest.Config, opts cache.Options) (cache.Cache, error) {
 			opts.DefaultTransform = fedinformer.StripUnusedFields
@@ -856,8 +858,8 @@ func setupControllers(ctx context.Context, mgr controllerruntime.Manager, opts *
 	sharedFactory.WaitForCacheSync(ctx.Done())
 
 	resourceInterpreter := resourceinterpreter.NewResourceInterpreter(controlPlaneInformerManager, serviceLister)
-	if err := mgr.Add(resourceInterpreter); err != nil {
-		klog.Fatalf("Failed to setup custom resource interpreter: %v", err)
+	if err := resourceInterpreter.Start(ctx); err != nil {
+		klog.Fatalf("Failed to start resource interpreter: %v", err)
 	}
 	rateLimiterGetter := util.GetClusterRateLimiterGetter().SetDefaultLimits(opts.ClusterAPIQPS, opts.ClusterAPIBurst)
 	clusterClientOption := &util.ClientOption{RateLimiterGetter: rateLimiterGetter.GetRateLimiter}
diff --git a/cmd/karmada-search/app/karmada-search.go b/cmd/karmada-search/app/karmada-search.go
index 449ed348f..a0f35c355 100644
--- a/cmd/karmada-search/app/karmada-search.go
+++ b/cmd/karmada-search/app/karmada-search.go
@@ -141,8 +141,11 @@ func run(ctx context.Context, o *options.Options, registryOptions ...Option) err
 		return nil
 	})
 
+	karmadaSharedInformerFactoryCacheSynced := make(chan struct{})
 	server.GenericAPIServer.AddPostStartHookOrDie("start-karmada-informers", func(context genericapiserver.PostStartHookContext) error {
 		config.ExtraConfig.KarmadaSharedInformerFactory.Start(context.Done())
+		config.ExtraConfig.KarmadaSharedInformerFactory.WaitForCacheSync(context.Done())
+		close(karmadaSharedInformerFactoryCacheSynced)
 		return nil
 	})
 
@@ -151,6 +154,7 @@ func run(ctx context.Context, o *options.Options, registryOptions ...Option) err
 	if config.ExtraConfig.Controller != nil {
 		server.GenericAPIServer.AddPostStartHookOrDie("start-karmada-search-controller", func(context genericapiserver.PostStartHookContext) error {
 			// start ResourceRegistry controller
+			<-karmadaSharedInformerFactoryCacheSynced
 			config.ExtraConfig.Controller.Start(context)
 			return nil
 		})
diff --git a/docs/CHANGELOG/CHANGELOG-1.12.md b/docs/CHANGELOG/CHANGELOG-1.12.md
index 26ace3ab9..c557a8092 100644
--- a/docs/CHANGELOG/CHANGELOG-1.12.md
+++ b/docs/CHANGELOG/CHANGELOG-1.12.md
@@ -2,53 +2,58 @@
 <!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
 **Table of Contents**  *generated with [DocToc](https://github.com/thlorenz/doctoc)*
 
+- [v1.12.9](#v1129)
+  - [Downloads for v1.12.9](#downloads-for-v1129)
+  - [Changelog since v1.12.8](#changelog-since-v1128)
+    - [Changes by Kind](#changes-by-kind)
+      - [Bug Fixes](#bug-fixes)
 - [v1.12.8](#v1128)
   - [Downloads for v1.12.8](#downloads-for-v1128)
   - [Changelog since v1.12.7](#changelog-since-v1127)
-    - [Changes by Kind](#changes-by-kind)
-      - [Bug Fixes](#bug-fixes)
+    - [Changes by Kind](#changes-by-kind-1)
+      - [Bug Fixes](#bug-fixes-1)
       - [Others](#others)
 - [v1.12.7](#v1127)
   - [Downloads for v1.12.7](#downloads-for-v1127)
   - [Changelog since v1.12.6](#changelog-since-v1126)
-    - [Changes by Kind](#changes-by-kind-1)
-      - [Bug Fixes](#bug-fixes-1)
+    - [Changes by Kind](#changes-by-kind-2)
+      - [Bug Fixes](#bug-fixes-2)
       - [Others](#others-1)
 - [v1.12.6](#v1126)
   - [Downloads for v1.12.6](#downloads-for-v1126)
   - [Changelog since v1.12.5](#changelog-since-v1125)
-    - [Changes by Kind](#changes-by-kind-2)
-      - [Bug Fixes](#bug-fixes-2)
+    - [Changes by Kind](#changes-by-kind-3)
+      - [Bug Fixes](#bug-fixes-3)
       - [Others](#others-2)
 - [v1.12.5](#v1125)
   - [Downloads for v1.12.5](#downloads-for-v1125)
   - [Changelog since v1.12.4](#changelog-since-v1124)
-    - [Changes by Kind](#changes-by-kind-3)
-      - [Bug Fixes](#bug-fixes-3)
+    - [Changes by Kind](#changes-by-kind-4)
+      - [Bug Fixes](#bug-fixes-4)
       - [Others](#others-3)
 - [v1.12.4](#v1124)
   - [Downloads for v1.12.4](#downloads-for-v1124)
   - [Changelog since v1.12.3](#changelog-since-v1123)
-    - [Changes by Kind](#changes-by-kind-4)
-      - [Bug Fixes](#bug-fixes-4)
+    - [Changes by Kind](#changes-by-kind-5)
+      - [Bug Fixes](#bug-fixes-5)
       - [Others](#others-4)
 - [v1.12.3](#v1123)
   - [Downloads for v1.12.3](#downloads-for-v1123)
   - [Changelog since v1.12.2](#changelog-since-v1122)
-    - [Changes by Kind](#changes-by-kind-5)
-      - [Bug Fixes](#bug-fixes-5)
+    - [Changes by Kind](#changes-by-kind-6)
+      - [Bug Fixes](#bug-fixes-6)
       - [Others](#others-5)
 - [v1.12.2](#v1122)
   - [Downloads for v1.12.2](#downloads-for-v1122)
   - [Changelog since v1.12.1](#changelog-since-v1121)
-    - [Changes by Kind](#changes-by-kind-6)
-      - [Bug Fixes](#bug-fixes-6)
+    - [Changes by Kind](#changes-by-kind-7)
+      - [Bug Fixes](#bug-fixes-7)
       - [Others](#others-6)
 - [v1.12.1](#v1121)
   - [Downloads for v1.12.1](#downloads-for-v1121)
   - [Changelog since v1.12.0](#changelog-since-v1120)
-    - [Changes by Kind](#changes-by-kind-7)
-      - [Bug Fixes](#bug-fixes-7)
+    - [Changes by Kind](#changes-by-kind-8)
+      - [Bug Fixes](#bug-fixes-8)
       - [Others](#others-7)
 - [v1.12.0](#v1120)
   - [Downloads for v1.12.0](#downloads-for-v1120)
@@ -60,7 +65,7 @@
   - [Other Notable Changes](#other-notable-changes)
     - [API Changes](#api-changes)
     - [Deprecation](#deprecation)
-    - [Bug Fixes](#bug-fixes-8)
+    - [Bug Fixes](#bug-fixes-9)
     - [Security](#security)
     - [Features & Enhancements](#features--enhancements)
   - [Other](#other)
@@ -72,11 +77,11 @@
   - [Downloads for v1.12.0-beta.0](#downloads-for-v1120-beta0)
   - [Changelog since v1.12.0-alpha.1](#changelog-since-v1120-alpha1)
   - [Urgent Update Notes](#urgent-update-notes)
-  - [Changes by Kind](#changes-by-kind-8)
+  - [Changes by Kind](#changes-by-kind-9)
     - [API Changes](#api-changes-1)
     - [Features & Enhancements](#features--enhancements-1)
     - [Deprecation](#deprecation-1)
-    - [Bug Fixes](#bug-fixes-9)
+    - [Bug Fixes](#bug-fixes-10)
     - [Security](#security-1)
   - [Other](#other-1)
     - [Dependencies](#dependencies-1)
@@ -86,11 +91,11 @@
   - [Downloads for v1.12.0-alpha.1](#downloads-for-v1120-alpha1)
   - [Changelog since v1.11.0](#changelog-since-v1110)
   - [Urgent Update Notes](#urgent-update-notes-1)
-  - [Changes by Kind](#changes-by-kind-9)
+  - [Changes by Kind](#changes-by-kind-10)
     - [API Changes](#api-changes-2)
     - [Features & Enhancements](#features--enhancements-2)
     - [Deprecation](#deprecation-2)
-    - [Bug Fixes](#bug-fixes-10)
+    - [Bug Fixes](#bug-fixes-11)
     - [Security](#security-2)
   - [Other](#other-2)
     - [Dependencies](#dependencies-2)
@@ -99,6 +104,18 @@
 
 <!-- END doctoc generated TOC please keep comment here to allow auto update -->
 
+# v1.12.9
+## Downloads for v1.12.9
+
+Download v1.12.9 in the [v1.12.9 release page](https://github.com/karmada-io/karmada/releases/tag/v1.12.9).
+
+## Changelog since v1.12.8
+### Changes by Kind
+#### Bug Fixes
+- `karmada-controller-manager`: Fixed the issue that the informer cache gets unexpectedly modified during usage. ([#6638](https://github.com/karmada-io/karmada/pull/6638), @zhzhuang-zju)
+- `karmada-controller-manager`: Fixed an issue where the ResourceInterpreter could report as synced before its underlying informer cache was fully synchronized, preventing potential out-of-sync errors during startup. ([#6642](https://github.com/karmada-io/karmada/pull/6642), @liaolecheng)
+- `karmada-controller-manager`: Fixed the issue that endpointslice and work resources residue when using MCS and MCI simultaneously and then deleting them. ([#6636](https://github.com/karmada-io/karmada/pull/6636), @XiShanYongYe-Chang)
+
 # v1.12.8
 ## Downloads for v1.12.8
 
diff --git a/docs/CHANGELOG/CHANGELOG-1.13.md b/docs/CHANGELOG/CHANGELOG-1.13.md
index 76ba02261..ebe22603f 100644
--- a/docs/CHANGELOG/CHANGELOG-1.13.md
+++ b/docs/CHANGELOG/CHANGELOG-1.13.md
@@ -2,35 +2,40 @@
 <!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
 **Table of Contents**  *generated with [DocToc](https://github.com/thlorenz/doctoc)*
 
+- [v1.13.6](#v1136)
+  - [Downloads for v1.13.6](#downloads-for-v1136)
+  - [Changelog since v1.13.5](#changelog-since-v1135)
+    - [Changes by Kind](#changes-by-kind)
+      - [Bug Fixes](#bug-fixes)
 - [v1.13.5](#v1135)
   - [Downloads for v1.13.5](#downloads-for-v1135)
   - [Changelog since v1.13.4](#changelog-since-v1134)
-    - [Changes by Kind](#changes-by-kind)
-      - [Bug Fixes](#bug-fixes)
+    - [Changes by Kind](#changes-by-kind-1)
+      - [Bug Fixes](#bug-fixes-1)
       - [Others](#others)
 - [v1.13.4](#v1134)
   - [Downloads for v1.13.4](#downloads-for-v1134)
   - [Changelog since v1.13.3](#changelog-since-v1133)
-    - [Changes by Kind](#changes-by-kind-1)
-      - [Bug Fixes](#bug-fixes-1)
+    - [Changes by Kind](#changes-by-kind-2)
+      - [Bug Fixes](#bug-fixes-2)
       - [Others](#others-1)
 - [v1.13.3](#v1133)
   - [Downloads for v1.13.3](#downloads-for-v1133)
   - [Changelog since v1.13.2](#changelog-since-v1132)
-    - [Changes by Kind](#changes-by-kind-2)
-      - [Bug Fixes](#bug-fixes-2)
+    - [Changes by Kind](#changes-by-kind-3)
+      - [Bug Fixes](#bug-fixes-3)
       - [Others](#others-2)
 - [v1.13.2](#v1132)
   - [Downloads for v1.13.2](#downloads-for-v1132)
   - [Changelog since v1.13.1](#changelog-since-v1131)
-    - [Changes by Kind](#changes-by-kind-3)
-      - [Bug Fixes](#bug-fixes-3)
+    - [Changes by Kind](#changes-by-kind-4)
+      - [Bug Fixes](#bug-fixes-4)
       - [Others](#others-3)
 - [v1.13.1](#v1131)
   - [Downloads for v1.13.1](#downloads-for-v1131)
   - [Changelog since v1.13.0](#changelog-since-v1130)
-    - [Changes by Kind](#changes-by-kind-4)
-      - [Bug Fixes](#bug-fixes-4)
+    - [Changes by Kind](#changes-by-kind-5)
+      - [Bug Fixes](#bug-fixes-5)
       - [Others](#others-4)
 - [v1.13.0](#v1130)
   - [Downloads for v1.13.0](#downloads-for-v1130)
@@ -44,7 +49,7 @@
   - [Other Notable Changes](#other-notable-changes)
     - [API Changes](#api-changes)
     - [Deprecation](#deprecation)
-    - [Bug Fixes](#bug-fixes-5)
+    - [Bug Fixes](#bug-fixes-6)
     - [Security](#security)
     - [Features & Enhancements](#features--enhancements)
   - [Other](#other)
@@ -56,11 +61,11 @@
   - [Downloads for v1.13.0-rc.0](#downloads-for-v1130-rc0)
   - [Changelog since v1.13.0-beta.0](#changelog-since-v1130-beta0)
   - [Urgent Update Notes](#urgent-update-notes-1)
-  - [Changes by Kind](#changes-by-kind-5)
+  - [Changes by Kind](#changes-by-kind-6)
     - [API Changes](#api-changes-1)
     - [Features & Enhancements](#features--enhancements-1)
     - [Deprecation](#deprecation-1)
-    - [Bug Fixes](#bug-fixes-6)
+    - [Bug Fixes](#bug-fixes-7)
     - [Security](#security-1)
   - [Other](#other-1)
     - [Dependencies](#dependencies-1)
@@ -70,11 +75,11 @@
   - [Downloads for v1.13.0-beta.0](#downloads-for-v1130-beta0)
   - [Changelog since v1.13.0-alpha.2](#changelog-since-v1130-alpha2)
   - [Urgent Update Notes](#urgent-update-notes-2)
-  - [Changes by Kind](#changes-by-kind-6)
+  - [Changes by Kind](#changes-by-kind-7)
     - [API Changes](#api-changes-2)
     - [Features & Enhancements](#features--enhancements-2)
     - [Deprecation](#deprecation-2)
-    - [Bug Fixes](#bug-fixes-7)
+    - [Bug Fixes](#bug-fixes-8)
     - [Security](#security-2)
   - [Other](#other-2)
     - [Dependencies](#dependencies-2)
@@ -84,11 +89,11 @@
   - [Downloads for v1.13.0-alpha.2](#downloads-for-v1130-alpha2)
   - [Changelog since v1.13.0-alpha.1](#changelog-since-v1130-alpha1)
   - [Urgent Update Notes](#urgent-update-notes-3)
-  - [Changes by Kind](#changes-by-kind-7)
+  - [Changes by Kind](#changes-by-kind-8)
     - [API Changes](#api-changes-3)
     - [Features & Enhancements](#features--enhancements-3)
     - [Deprecation](#deprecation-3)
-    - [Bug Fixes](#bug-fixes-8)
+    - [Bug Fixes](#bug-fixes-9)
     - [Security](#security-3)
   - [Other](#other-3)
     - [Dependencies](#dependencies-3)
@@ -98,11 +103,11 @@
   - [Downloads for v1.13.0-alpha.1](#downloads-for-v1130-alpha1)
   - [Changelog since v1.12.0](#changelog-since-v1120)
   - [Urgent Update Notes](#urgent-update-notes-4)
-  - [Changes by Kind](#changes-by-kind-8)
+  - [Changes by Kind](#changes-by-kind-9)
     - [API Changes](#api-changes-4)
     - [Features & Enhancements](#features--enhancements-4)
     - [Deprecation](#deprecation-4)
-    - [Bug Fixes](#bug-fixes-9)
+    - [Bug Fixes](#bug-fixes-10)
     - [Security](#security-4)
   - [Other](#other-4)
     - [Dependencies](#dependencies-4)
@@ -111,6 +116,18 @@
 
 <!-- END doctoc generated TOC please keep comment here to allow auto update -->
 
+# v1.13.6
+## Downloads for v1.13.6
+
+Download v1.13.6 in the [v1.13.6 release page](https://github.com/karmada-io/karmada/releases/tag/v1.13.6).
+
+## Changelog since v1.13.5
+### Changes by Kind
+#### Bug Fixes
+- `karmada-controller-manager`: Fixed the issue that the informer cache gets unexpectedly modified during usage. ([#6646](https://github.com/karmada-io/karmada/pull/6646), @luyb177)
+- `karmada-controller-manager`: Fixed an issue where the ResourceInterpreter could report as synced before its underlying informer cache was fully synchronized, preventing potential out-of-sync errors during startup. ([#6644](https://github.com/karmada-io/karmada/pull/6644), @liaolecheng)
+- `karmada-controller-manager`: Fixed the issue that endpointslice and work resources residue when using MCS and MCI simultaneously and then deleting them. ([#6634](https://github.com/karmada-io/karmada/pull/6634), @XiShanYongYe-Chang)
+
 # v1.13.5
 ## Downloads for v1.13.5
 
diff --git a/docs/CHANGELOG/CHANGELOG-1.14.md b/docs/CHANGELOG/CHANGELOG-1.14.md
index 028a13855..1e6d716ca 100644
--- a/docs/CHANGELOG/CHANGELOG-1.14.md
+++ b/docs/CHANGELOG/CHANGELOG-1.14.md
@@ -2,17 +2,22 @@
 <!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
 **Table of Contents**  *generated with [DocToc](https://github.com/thlorenz/doctoc)*
 
+- [v1.14.3](#v1143)
+  - [Downloads for v1.14.3](#downloads-for-v1143)
+  - [Changelog since v1.14.2](#changelog-since-v1142)
+    - [Changes by Kind](#changes-by-kind)
+      - [Bug Fixes](#bug-fixes)
 - [v1.14.2](#v1142)
   - [Downloads for v1.14.2](#downloads-for-v1142)
   - [Changelog since v1.14.1](#changelog-since-v1141)
-    - [Changes by Kind](#changes-by-kind)
-      - [Bug Fixes](#bug-fixes)
+    - [Changes by Kind](#changes-by-kind-1)
+      - [Bug Fixes](#bug-fixes-1)
       - [Others](#others)
 - [v1.14.1](#v1141)
   - [Downloads for v1.14.1](#downloads-for-v1141)
   - [Changelog since v1.14.0](#changelog-since-v1140)
-    - [Changes by Kind](#changes-by-kind-1)
-      - [Bug Fixes](#bug-fixes-1)
+    - [Changes by Kind](#changes-by-kind-2)
+      - [Bug Fixes](#bug-fixes-2)
       - [Others](#others-1)
 - [v1.14.0](#v1140)
   - [Downloads for v1.14.0](#downloads-for-v1140)
@@ -25,7 +30,7 @@
   - [Other Notable Changes](#other-notable-changes)
     - [API Changes](#api-changes)
     - [Deprecation](#deprecation)
-    - [Bug Fixes](#bug-fixes-2)
+    - [Bug Fixes](#bug-fixes-3)
     - [Security](#security)
     - [Features & Enhancements](#features--enhancements)
   - [Other](#other)
@@ -37,11 +42,11 @@
   - [Downloads for v1.14.0-rc.0](#downloads-for-v1140-rc0)
   - [Changelog since v1.14.0-beta.0](#changelog-since-v1140-beta0)
   - [Urgent Update Notes](#urgent-update-notes-1)
-  - [Changes by Kind](#changes-by-kind-2)
+  - [Changes by Kind](#changes-by-kind-3)
     - [API Changes](#api-changes-1)
     - [Features & Enhancements](#features--enhancements-1)
     - [Deprecation](#deprecation-1)
-    - [Bug Fixes](#bug-fixes-3)
+    - [Bug Fixes](#bug-fixes-4)
     - [Security](#security-1)
   - [Other](#other-1)
     - [Dependencies](#dependencies-1)
@@ -52,11 +57,11 @@
   - [Downloads for v1.14.0-beta.0](#downloads-for-v1140-beta0)
   - [Changelog since v1.14.0-alpha.2](#changelog-since-v1140-alpha2)
   - [Urgent Update Notes](#urgent-update-notes-2)
-  - [Changes by Kind](#changes-by-kind-3)
+  - [Changes by Kind](#changes-by-kind-4)
     - [API Changes](#api-changes-2)
     - [Features & Enhancements](#features--enhancements-2)
     - [Deprecation](#deprecation-2)
-    - [Bug Fixes](#bug-fixes-4)
+    - [Bug Fixes](#bug-fixes-5)
     - [Security](#security-2)
   - [Other](#other-2)
     - [Dependencies](#dependencies-2)
@@ -67,11 +72,11 @@
   - [Downloads for v1.14.0-alpha.2](#downloads-for-v1140-alpha2)
   - [Changelog since v1.14.0-alpha.1](#changelog-since-v1140-alpha1)
   - [Urgent Update Notes](#urgent-update-notes-3)
-  - [Changes by Kind](#changes-by-kind-4)
+  - [Changes by Kind](#changes-by-kind-5)
     - [API Changes](#api-changes-3)
     - [Features & Enhancements](#features--enhancements-3)
     - [Deprecation](#deprecation-3)
-    - [Bug Fixes](#bug-fixes-5)
+    - [Bug Fixes](#bug-fixes-6)
     - [Security](#security-3)
   - [Other](#other-3)
     - [Dependencies](#dependencies-3)
@@ -82,11 +87,11 @@
   - [Downloads for v1.14.0-alpha.1](#downloads-for-v1140-alpha1)
   - [Changelog since v1.13.0](#changelog-since-v1130)
   - [Urgent Update Notes](#urgent-update-notes-4)
-  - [Changes by Kind](#changes-by-kind-5)
+  - [Changes by Kind](#changes-by-kind-6)
     - [API Changes](#api-changes-4)
     - [Features & Enhancements](#features--enhancements-4)
     - [Deprecation](#deprecation-4)
-    - [Bug Fixes](#bug-fixes-6)
+    - [Bug Fixes](#bug-fixes-7)
     - [Security](#security-4)
   - [Other](#other-4)
     - [Dependencies](#dependencies-4)
@@ -95,6 +100,18 @@
 
 <!-- END doctoc generated TOC please keep comment here to allow auto update -->
 
+# v1.14.3
+## Downloads for v1.14.3
+
+Download v1.14.3 in the [v1.14.3 release page](https://github.com/karmada-io/karmada/releases/tag/v1.14.3).
+
+## Changelog since v1.14.2
+### Changes by Kind
+#### Bug Fixes
+- `karmada-controller-manager`: Fixed the issue that the informer cache gets unexpectedly modified during usage. ([#6648](https://github.com/karmada-io/karmada/pull/6648), @luyb177)
+- `karmada-controller-manager`: Fixed an issue where the ResourceInterpreter could report as synced before its underlying informer cache was fully synchronized, preventing potential out-of-sync errors during startup. ([#6645](https://github.com/karmada-io/karmada/pull/6645), @liaolecheng)
+- `karmada-controller-manager`: Fixed the issue that endpointslice and work resources residue when using MCS and MCI simultaneously and then deleting them. ([#6633](https://github.com/karmada-io/karmada/pull/6633), @XiShanYongYe-Chang)
+
 # v1.14.2
 ## Downloads for v1.14.2
 
diff --git a/docs/CHANGELOG/CHANGELOG-1.15.md b/docs/CHANGELOG/CHANGELOG-1.15.md
index 516ea38bf..ffc9f1128 100644
--- a/docs/CHANGELOG/CHANGELOG-1.15.md
+++ b/docs/CHANGELOG/CHANGELOG-1.15.md
@@ -2,9 +2,9 @@
 <!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
 **Table of Contents**  *generated with [DocToc](https://github.com/thlorenz/doctoc)*
 
-- [v1.15.0-beta.0](#v1150-beta0)
-  - [Downloads for v1.15.0-beta.0](#downloads-for-v1150-beta0)
-  - [Changelog since v1.15.0-alpha.2](#changelog-since-v1150-alpha2)
+- [v1.15.0-rc.0](#v1150-rc0)
+  - [Downloads for v1.15.0-rc.0](#downloads-for-v1150-rc0)
+  - [Changelog since v1.15.0-beta.0](#changelog-since-v1150-beta0)
   - [Urgent Update Notes](#urgent-update-notes)
   - [Changes by Kind](#changes-by-kind)
     - [API Changes](#api-changes)
@@ -17,9 +17,9 @@
     - [Helm Charts](#helm-charts)
     - [Instrumentation](#instrumentation)
     - [Performance](#performance)
-- [v1.15.0-alpha.2](#v1150-alpha2)
-  - [Downloads for v1.15.0-alpha.2](#downloads-for-v1150-alpha2)
-  - [Changelog since v1.15.0-alpha.1](#changelog-since-v1150-alpha1)
+- [v1.15.0-beta.0](#v1150-beta0)
+  - [Downloads for v1.15.0-beta.0](#downloads-for-v1150-beta0)
+  - [Changelog since v1.15.0-alpha.2](#changelog-since-v1150-alpha2)
   - [Urgent Update Notes](#urgent-update-notes-1)
   - [Changes by Kind](#changes-by-kind-1)
     - [API Changes](#api-changes-1)
@@ -32,9 +32,9 @@
     - [Helm Charts](#helm-charts-1)
     - [Instrumentation](#instrumentation-1)
     - [Performance](#performance-1)
-- [v1.15.0-alpha.1](#v1150-alpha1)
-  - [Downloads for v1.15.0-alpha.1](#downloads-for-v1150-alpha1)
-  - [Changelog since v1.14.0](#changelog-since-v1140)
+- [v1.15.0-alpha.2](#v1150-alpha2)
+  - [Downloads for v1.15.0-alpha.2](#downloads-for-v1150-alpha2)
+  - [Changelog since v1.15.0-alpha.1](#changelog-since-v1150-alpha1)
   - [Urgent Update Notes](#urgent-update-notes-2)
   - [Changes by Kind](#changes-by-kind-2)
     - [API Changes](#api-changes-2)
@@ -46,9 +46,70 @@
     - [Dependencies](#dependencies-2)
     - [Helm Charts](#helm-charts-2)
     - [Instrumentation](#instrumentation-2)
+    - [Performance](#performance-2)
+- [v1.15.0-alpha.1](#v1150-alpha1)
+  - [Downloads for v1.15.0-alpha.1](#downloads-for-v1150-alpha1)
+  - [Changelog since v1.14.0](#changelog-since-v1140)
+  - [Urgent Update Notes](#urgent-update-notes-3)
+  - [Changes by Kind](#changes-by-kind-3)
+    - [API Changes](#api-changes-3)
+    - [Features & Enhancements](#features--enhancements-3)
+    - [Deprecation](#deprecation-3)
+    - [Bug Fixes](#bug-fixes-3)
+    - [Security](#security-3)
+  - [Other](#other-3)
+    - [Dependencies](#dependencies-3)
+    - [Helm Charts](#helm-charts-3)
+    - [Instrumentation](#instrumentation-3)
 
 <!-- END doctoc generated TOC please keep comment here to allow auto update -->
 
+# v1.15.0-rc.0
+## Downloads for v1.15.0-rc.0
+
+Download v1.15.0-rc.0 in the [v1.15.0-rc.0 release page](https://github.com/karmada-io/karmada/releases/tag/v1.15.0-rc.0).
+
+## Changelog since v1.15.0-beta.0
+
+## Urgent Update Notes
+None.
+
+## Changes by Kind
+
+### API Changes
+- `karmada-operator`: Introduced `spec.crdTarball.httpSource.proxy` field in `Karmada` API to optionally set a proxy for downloading CRD tarballs from an HTTP source. ([#6577](https://github.com/karmada-io/karmada/pull/6577), @jabellard)
+
+### Features & Enhancements
+- `karmada-scheduler`: Added `QuotaExceeded` as the reason for the legacy `Scheduled` condition on ResourceBinding when scheduling fails due to FederatedResourceQuota limits. ([#6481](https://github.com/karmada-io/karmada/pull/6481), @mszacillo)
+
+### Deprecation
+None.
+
+### Bug Fixes
+- `karmada-controller-manager`: Fixed the issue that the informer cache gets unexpectedly modified during usage. ([#6544](https://github.com/karmada-io/karmada/pull/6544), @zhzhuang-zju)
+- `karmada-controller-manager`: Fixed an issue where the ConfigurableInterpreter could report as synced before its underlying informer cache was fully synchronized, preventing potential out-of-sync errors during startup. ([#6602](https://github.com/karmada-io/karmada/pull/6602), @XiShanYongYe-Chang)
+- `karmada-controller-manager`: Fixed the issue that resources are deleted unexpectedly due to the resourceInterpreter not properly handling the error and cache not being synced. ([#6612](https://github.com/karmada-io/karmada/pull/6612), @liaolecheng)
+- `karmada-controller-manager`: Fixed the issue that endpointslice and work resources residue when using MCS and MCI simultaneously and then deleting them. ([#6622](https://github.com/karmada-io/karmada/pull/6622), @XiShanYongYe-Chang)
+- `karmada-controller-manager`: Fixed the issue that ensures resource interpreter cache sync before starting controllers. ([#6626](https://github.com/karmada-io/karmada/pull/6626), @XiShanYongYe-Chang)
+- `karmada-search`: Ensure the effective execution of informerfactory.WaitForCacheSync. ([#6554](https://github.com/karmada-io/karmada/pull/6554), @NickYadance)
+
+### Security
+None.
+
+## Other
+### Dependencies
+- Karmada is now built with Golang v1.24.6. ([#6630](https://github.com/karmada-io/karmada/pull/6630), @liaolecheng)
+- The base image `alpine` now has been promoted from 3.22.0 to 3.22.1. ([#6560](https://github.com/karmada-io/karmada/pull/6560))
+
+### Helm Charts
+- `Helm chart`: Karmada Operator chart now possible to set environment variables and extra arguments for the Karmada operator deployment. ([#6596](https://github.com/karmada-io/karmada/pull/6596), @jabellard)
+
+### Instrumentation
+None.
+
+### Performance
+None.
+
 # v1.15.0-beta.0
 ## Downloads for v1.15.0-beta.0
 
diff --git a/go.mod b/go.mod
index 39348f892..6e60c8882 100644
--- a/go.mod
+++ b/go.mod
@@ -1,6 +1,6 @@
 module github.com/karmada-io/karmada
 
-go 1.24.5 // keep in sync with .go-version
+go 1.24.6 // keep in sync with .go-version
 
 require (
 	github.com/adhocore/gronx v1.6.3
diff --git a/hack/setup-dev-base.sh b/hack/setup-dev-base.sh
index fde5d5d87..bdf85f33d 100755
--- a/hack/setup-dev-base.sh
+++ b/hack/setup-dev-base.sh
@@ -83,10 +83,15 @@ else
 fi
 
 #step1. create host cluster and member clusters in parallel
-# host IP address: script parameter ahead of macOS IP
+# host IP address: script parameter ahead of WSL2 or macOS IP
 if [[ -z "${HOST_IPADDRESS}" ]]; then
-  util::get_macos_ipaddress # Adapt for macOS
-  HOST_IPADDRESS=${MAC_NIC_IPADDRESS:-}
+  if util::is_wsl2; then
+    util::get_wsl2_ipaddress # adapt for WSL2
+    HOST_IPADDRESS=${WSL2_HOST_IP_ADDRESS:-}
+  else
+    util::get_macos_ipaddress # Adapt for macOS
+    HOST_IPADDRESS=${MAC_NIC_IPADDRESS:-}
+  fi
 fi
 #prepare for kindClusterConfig
 TEMP_PATH=$(mktemp -d)
diff --git a/hack/util.sh b/hack/util.sh
index caf065e77..aac619cc2 100755
--- a/hack/util.sh
+++ b/hack/util.sh
@@ -533,6 +533,16 @@ function util::get_docker_host_ip_port(){
   docker inspect --format='{{range $key, $value := index .NetworkSettings.Ports "6443/tcp"}}{{if eq $key 0}}{{$value.HostIp}}:{{$value.HostPort}}{{end}}{{end}}' "${container_name}"
 }
 
+# util::is_wsl2 checks if the current environment is WSL2.
+# Returns:
+#   0 if running in WSL2, 1 otherwise.
+# Usage:
+#   util::is_wsl2 && echo "WSL2 detected" || echo "Not WSL2"
+function util::is_wsl2(){
+  # /proc/sys/fs/binfmt_misc/WSLInterop exists only in WSL2 environments
+  [[ -f /proc/sys/fs/binfmt_misc/WSLInterop ]] || \
+    ( [[ -f /proc/version ]] && grep -q "WSL2" /proc/version )
+}
 # util::check_clusters_ready checks if a cluster is ready, if not, wait until timeout
 function util::check_clusters_ready() {
   local kubeconfig_path=${1}
@@ -548,7 +558,13 @@ function util::check_clusters_ready() {
   os_name=$(go env GOOS)
   local container_ip_port
   case $os_name in
-    linux) container_ip_port=$(util::get_docker_native_ipaddress "${context_name}-control-plane")":6443"
+    linux)
+      if util::is_wsl2; then
+        # WSL2 uses a different method to get the container IP address
+        container_ip_port=$(util::get_docker_host_ip_port "${context_name}-control-plane")
+      else # normal linux environment
+        container_ip_port=$(util::get_docker_native_ipaddress "${context_name}-control-plane")":6443"
+      fi
     ;;
     darwin) container_ip_port=$(util::get_docker_host_ip_port "${context_name}-control-plane")
     ;;
@@ -683,6 +699,16 @@ function util::add_routes() {
   unset IFS
 }
 
+# util::get_wsl2_ipaddress will get ip address on wsl2, store to 'WSL2_HOST_IP_ADDRESS' if available
+WSL2_HOST_IP_ADDRESS=''
+function util::get_wsl2_ipaddress() {
+  echo "====================================================" 
+  echo "= Detected that you are installing Karmada on WSL2 =" 
+  echo "====================================================" 
+  WSL2_HOST_IP_ADDRESS=$(hostname -I | awk '{print $1}')
+  util::verify_ip_address "${WSL2_HOST_IP_ADDRESS}"
+}
+
 # util::get_macos_ipaddress will get ip address on macos interactively, store to 'MAC_NIC_IPADDRESS' if available
 MAC_NIC_IPADDRESS=''
 function util::get_macos_ipaddress() {
diff --git a/operator/config/crds/operator.karmada.io_karmadas.yaml b/operator/config/crds/operator.karmada.io_karmadas.yaml
index 0bb449441..e3894f279 100644
--- a/operator/config/crds/operator.karmada.io_karmadas.yaml
+++ b/operator/config/crds/operator.karmada.io_karmadas.yaml
@@ -5208,9 +5208,27 @@ spec:
                     description: HTTPSource specifies how to download the CRD tarball
                       via either HTTP or HTTPS protocol.
                     properties:
+                      proxy:
+                        description: |-
+                          Proxy specifies the configuration of a proxy server to use when downloading the CRD tarball.
+                          When set, the operator will use the configuration to determine how to establish a connection to the proxy to fetch the tarball from the URL specified above.
+                          This is useful in environments where direct access to the server hosting the CRD tarball is restricted and a proxy must be used to reach that server.
+                          If a proxy configuration is not set, the operator will attempt to download the tarball directly from the URL specified above without using a proxy.
+                        properties:
+                          proxyURL:
+                            description: |-
+                              ProxyURL specifies the HTTP/HTTPS proxy server URL to use when downloading the CRD tarball.
+                              This is useful in environments where direct access to the server hosting the CRD tarball is restricted and a proxy must be used to reach that server.
+                              The format should be a valid URL, e.g., "http://proxy.example.com:8080".
+                            type: string
+                        required:
+                        - proxyURL
+                        type: object
                       url:
                         description: URL specifies the URL of the CRD tarball resource.
                         type: string
+                    required:
+                    - url
                     type: object
                 type: object
               customCertificate:
diff --git a/operator/pkg/apis/operator/v1alpha1/type.go b/operator/pkg/apis/operator/v1alpha1/type.go
index 931ab1ae6..52b4a1b44 100644
--- a/operator/pkg/apis/operator/v1alpha1/type.go
+++ b/operator/pkg/apis/operator/v1alpha1/type.go
@@ -58,7 +58,24 @@ const (
 // HTTPSource specifies how to download the CRD tarball via either HTTP or HTTPS protocol.
 type HTTPSource struct {
 	// URL specifies the URL of the CRD tarball resource.
+	// +required
 	URL string `json:"url,omitempty"`
+
+	// Proxy specifies the configuration of a proxy server to use when downloading the CRD tarball.
+	// When set, the operator will use the configuration to determine how to establish a connection to the proxy to fetch the tarball from the URL specified above.
+	// This is useful in environments where direct access to the server hosting the CRD tarball is restricted and a proxy must be used to reach that server.
+	// If a proxy configuration is not set, the operator will attempt to download the tarball directly from the URL specified above without using a proxy.
+	// +optional
+	Proxy *ProxyConfig `json:"proxy,omitempty"`
+}
+
+// ProxyConfig defines the configuration for a proxy server to use when downloading a CRD tarball.
+type ProxyConfig struct {
+	// ProxyURL specifies the HTTP/HTTPS proxy server URL to use when downloading the CRD tarball.
+	// This is useful in environments where direct access to the server hosting the CRD tarball is restricted and a proxy must be used to reach that server.
+	// The format should be a valid URL, e.g., "http://proxy.example.com:8080".
+	// +required
+	ProxyURL string `json:"proxyURL"`
 }
 
 // CRDTarball specifies the source from which the Karmada CRD tarball should be downloaded, along with the download policy to use.
diff --git a/operator/pkg/apis/operator/v1alpha1/zz_generated.deepcopy.go b/operator/pkg/apis/operator/v1alpha1/zz_generated.deepcopy.go
index 69c643ef6..ca15fc1c9 100644
--- a/operator/pkg/apis/operator/v1alpha1/zz_generated.deepcopy.go
+++ b/operator/pkg/apis/operator/v1alpha1/zz_generated.deepcopy.go
@@ -49,7 +49,7 @@ func (in *CRDTarball) DeepCopyInto(out *CRDTarball) {
 	if in.HTTPSource != nil {
 		in, out := &in.HTTPSource, &out.HTTPSource
 		*out = new(HTTPSource)
-		**out = **in
+		(*in).DeepCopyInto(*out)
 	}
 	if in.CRDDownloadPolicy != nil {
 		in, out := &in.CRDDownloadPolicy, &out.CRDDownloadPolicy
@@ -198,6 +198,11 @@ func (in *ExternalEtcd) DeepCopy() *ExternalEtcd {
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *HTTPSource) DeepCopyInto(out *HTTPSource) {
 	*out = *in
+	if in.Proxy != nil {
+		in, out := &in.Proxy, &out.Proxy
+		*out = new(ProxyConfig)
+		**out = **in
+	}
 	return
 }
 
@@ -860,6 +865,22 @@ func (in *Networking) DeepCopy() *Networking {
 	return out
 }
 
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *ProxyConfig) DeepCopyInto(out *ProxyConfig) {
+	*out = *in
+	return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProxyConfig.
+func (in *ProxyConfig) DeepCopy() *ProxyConfig {
+	if in == nil {
+		return nil
+	}
+	out := new(ProxyConfig)
+	in.DeepCopyInto(out)
+	return out
+}
+
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *VolumeData) DeepCopyInto(out *VolumeData) {
 	*out = *in
diff --git a/operator/pkg/controller/karmada/validating.go b/operator/pkg/controller/karmada/validating.go
index 67eb516a6..4daca298c 100644
--- a/operator/pkg/controller/karmada/validating.go
+++ b/operator/pkg/controller/karmada/validating.go
@@ -33,14 +33,24 @@ import (
 )
 
 func validateCRDTarball(crdTarball *operatorv1alpha1.CRDTarball, fldPath *field.Path) (errs field.ErrorList) {
+	// A custom CRD tarball download config is optional, and given that an HTTP source is the only possible option, we
+	// only have to validate the config if an HTTP source is set.
 	if crdTarball == nil || crdTarball.HTTPSource == nil {
 		return nil
 	}
 
+	// Since the server URL is required when an HTTP source is set, we'll verify that the URL is valid.
 	if _, err := url.ParseRequestURI(crdTarball.HTTPSource.URL); err != nil {
 		errs = append(errs, field.Invalid(fldPath.Child("httpSource").Child("url"), crdTarball.HTTPSource.URL, "invalid CRDs remote URL"))
 	}
 
+	// Since the Proxy URL is required when a proxy config is set, we'll verify that the URL is valid.
+	if crdTarball.HTTPSource.Proxy != nil {
+		if _, err := url.ParseRequestURI(crdTarball.HTTPSource.Proxy.ProxyURL); err != nil {
+			errs = append(errs, field.Invalid(fldPath.Child("httpSource").Child("proxy").Child("proxyURL"), crdTarball.HTTPSource.Proxy.ProxyURL, "invalid CRDs proxy URL"))
+		}
+	}
+
 	return errs
 }
 
diff --git a/operator/pkg/tasks/init/crd.go b/operator/pkg/tasks/init/crd.go
index 2879790eb..18757d4b2 100644
--- a/operator/pkg/tasks/init/crd.go
+++ b/operator/pkg/tasks/init/crd.go
@@ -136,7 +136,7 @@ func runCrdsDownload(r workflow.RunData) error {
 	}
 
 	crdTarball := data.CrdTarball()
-	if err := util.DownloadFile(crdTarball.HTTPSource.URL, crdsTarPath); err != nil {
+	if err := util.DownloadFile(crdTarball.HTTPSource.URL, crdsTarPath, crdTarball.HTTPSource.Proxy); err != nil {
 		return fmt.Errorf("failed to download CRD tar, err: %w", err)
 	}
 
diff --git a/operator/pkg/util/util.go b/operator/pkg/util/util.go
index eb2658a09..da1441570 100644
--- a/operator/pkg/util/util.go
+++ b/operator/pkg/util/util.go
@@ -23,6 +23,7 @@ import (
 	"fmt"
 	"io"
 	"net/http"
+	urlpkg "net/url"
 	"os"
 	"path/filepath"
 	"reflect"
@@ -75,13 +76,22 @@ func (d *Downloader) Read(p []byte) (n int, err error) {
 	return
 }
 
-var httpClient = http.Client{
-	Timeout: 60 * time.Second,
-}
+// DownloadFile downloads files via URL, optionally using a proxy if provided.
+func DownloadFile(url, filePath string, proxyConfig *operatorv1alpha1.ProxyConfig) error {
+	client := &http.Client{
+		Timeout: 60 * time.Second,
+	}
+	if proxyConfig != nil {
+		parsedProxyURL, err := urlpkg.ParseRequestURI(proxyConfig.ProxyURL)
+		if err != nil {
+			return fmt.Errorf("invalid proxy URL: %w", err)
+		}
+		client.Transport = &http.Transport{
+			Proxy: http.ProxyURL(parsedProxyURL),
+		}
+	}
 
-// DownloadFile Download files via URL
-func DownloadFile(url, filePath string) error {
-	resp, err := httpClient.Get(url)
+	resp, err := client.Get(url)
 	if err != nil {
 		return err
 	}
@@ -105,7 +115,6 @@ func DownloadFile(url, filePath string) error {
 	if _, err := io.Copy(file, downloader); err != nil {
 		return err
 	}
-
 	return nil
 }
 
diff --git a/pkg/apis/cluster/types.go b/pkg/apis/cluster/types.go
index 189eca777..786770778 100644
--- a/pkg/apis/cluster/types.go
+++ b/pkg/apis/cluster/types.go
@@ -60,7 +60,7 @@ type ClusterSpec struct {
 	// - compose the DNS name of multi-cluster services.
 	//
 	// +optional
-	// +kubebuilder:validation:Maxlength=128000
+	// +kubebuilder:validation:MaxLength=128000
 	ID string `json:"id,omitempty"`
 
 	// SyncMode describes how a cluster syncs resources from karmada control plane.
diff --git a/pkg/apis/cluster/v1alpha1/types.go b/pkg/apis/cluster/v1alpha1/types.go
index 8a06b94fe..9332c73df 100644
--- a/pkg/apis/cluster/v1alpha1/types.go
+++ b/pkg/apis/cluster/v1alpha1/types.go
@@ -71,7 +71,7 @@ type ClusterSpec struct {
 	// - compose the DNS name of multi-cluster services.
 	//
 	// +optional
-	// +kubebuilder:validation:Maxlength=128000
+	// +kubebuilder:validation:MaxLength=128000
 	ID string `json:"id,omitempty"`
 
 	// SyncMode describes how a cluster syncs resources from karmada control plane.
diff --git a/pkg/apis/config/v1alpha1/resourceinterpretercustomization_types.go b/pkg/apis/config/v1alpha1/resourceinterpretercustomization_types.go
index 4da59aeed..ab23ab896 100644
--- a/pkg/apis/config/v1alpha1/resourceinterpretercustomization_types.go
+++ b/pkg/apis/config/v1alpha1/resourceinterpretercustomization_types.go
@@ -97,6 +97,17 @@ type CustomizationRules struct {
 	// +optional
 	ReplicaResource *ReplicaResourceRequirement `json:"replicaResource,omitempty"`
 
+	// ComponentResource describes the rules for Karmada to discover the resource requirements
+	// for multiple components from the given object.
+	// This is designed for CRDs with multiple components (e.g., FlinkDeployment), but
+	// can also be used for single-component resources like Deployment.
+	// If implemented, the controller will use this to obtain per-component replica and resource
+	// requirements, and will not call ReplicaResource.
+	// If not implemented, the controller will fall back to ReplicaResource for backward compatibility.
+	// This will only be used when the feature gate 'MultiplePodTemplatesScheduling' is enabled.
+	// +optional
+	ComponentResource *ComponentResourceRequirement `json:"componentResource,omitempty"`
+
 	// ReplicaRevision describes the rules for Karmada to revise the resource's replica.
 	// It would be useful for those CRD resources that declare workload types like
 	// Deployment.
@@ -203,6 +214,51 @@ type ReplicaResourceRequirement struct {
 	LuaScript string `json:"luaScript"`
 }
 
+// ComponentResourceRequirement holds the scripts for extracting the desired replica count
+// and resource requirements for each component within a resource. This is particularly useful for
+// resources that define multiple components (such as CRDs with multiple pod templates), but can also
+// be used for single-component resources.
+type ComponentResourceRequirement struct {
+	// LuaScript holds the Lua script that is used to extract the desired replica count and resource
+	// requirements for each component of the resource.
+	//
+	// The script should implement a function as follows:
+	//
+	// ```
+	//   luaScript: >
+	//       function GetComponents(desiredObj)
+	//           local components = {}
+	//
+	//           local jobManagerComponent = {
+	//               name = "jobmanager"
+	//               replicas = desiredObj.spec.jobManager.replicas
+	//           }
+	//           table.insert(components, jobManagerComponent)
+	//
+	//           local taskManagerComponent = {
+	//               name = "taskmanager"
+	//               replicas = desiredObj.spec.taskManager.replicas
+	//           }
+	//           table.insert(components, taskManagerComponent)
+	//
+	//           return components
+	//       end
+	// ```
+	//
+	// The content of the LuaScript needs to be a whole function including both
+	// declaration and implementation.
+	//
+	// The parameters will be supplied by the system:
+	//   - desiredObj: the object represents the configuration to be applied
+	//       to the member cluster.
+	//
+	// The function expects one return value:
+	//   - components: the resource requirements for each component.
+	// The returned value will be set into a ResourceBinding or ClusterResourceBinding.
+	// +required
+	LuaScript string `json:"luaScript"`
+}
+
 // ReplicaRevision holds the scripts for revising the desired replicas.
 type ReplicaRevision struct {
 	// LuaScript holds the Lua script that is used to revise replicas in the desired specification.
diff --git a/pkg/apis/config/v1alpha1/resourceinterpreterwebhook_types.go b/pkg/apis/config/v1alpha1/resourceinterpreterwebhook_types.go
index eae94dd76..dad865a34 100644
--- a/pkg/apis/config/v1alpha1/resourceinterpreterwebhook_types.go
+++ b/pkg/apis/config/v1alpha1/resourceinterpreterwebhook_types.go
@@ -124,6 +124,14 @@ const (
 	// Only necessary for those resource types that have replica declaration, like Deployment or similar custom resources.
 	InterpreterOperationInterpretReplica InterpreterOperation = "InterpretReplica"
 
+	// InterpreterOperationInterpretComponent indicates that karmada wants to figure out
+	// resource requirements for multiple components from a given object.
+	// This operation is designed for CRDs with multiple components (e.g., FlinkDeployment),
+	// but can also be used for single-component resources.
+	// If an interpreter supports this operation, 'InterpretReplica' will not be called.
+	// This operation is only used when the feature gate 'MultiplePodTemplatesScheduling' is enabled.
+	InterpreterOperationInterpretComponent InterpreterOperation = "InterpretComponent"
+
 	// InterpreterOperationReviseReplica indicates that karmada request webhook to modify the replica.
 	InterpreterOperationReviseReplica InterpreterOperation = "ReviseReplica"
 
diff --git a/pkg/apis/config/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/config/v1alpha1/zz_generated.deepcopy.go
index 1a5c6517e..282d05373 100644
--- a/pkg/apis/config/v1alpha1/zz_generated.deepcopy.go
+++ b/pkg/apis/config/v1alpha1/zz_generated.deepcopy.go
@@ -27,6 +27,22 @@ import (
 	runtime "k8s.io/apimachinery/pkg/runtime"
 )
 
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *ComponentResourceRequirement) DeepCopyInto(out *ComponentResourceRequirement) {
+	*out = *in
+	return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ComponentResourceRequirement.
+func (in *ComponentResourceRequirement) DeepCopy() *ComponentResourceRequirement {
+	if in == nil {
+		return nil
+	}
+	out := new(ComponentResourceRequirement)
+	in.DeepCopyInto(out)
+	return out
+}
+
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *CustomizationRules) DeepCopyInto(out *CustomizationRules) {
 	*out = *in
@@ -40,6 +56,11 @@ func (in *CustomizationRules) DeepCopyInto(out *CustomizationRules) {
 		*out = new(ReplicaResourceRequirement)
 		**out = **in
 	}
+	if in.ComponentResource != nil {
+		in, out := &in.ComponentResource, &out.ComponentResource
+		*out = new(ComponentResourceRequirement)
+		**out = **in
+	}
 	if in.ReplicaRevision != nil {
 		in, out := &in.ReplicaRevision, &out.ReplicaRevision
 		*out = new(ReplicaRevision)
diff --git a/pkg/apis/policy/v1alpha1/propagation_types.go b/pkg/apis/policy/v1alpha1/propagation_types.go
index f0bbef40d..30b55dae0 100644
--- a/pkg/apis/policy/v1alpha1/propagation_types.go
+++ b/pkg/apis/policy/v1alpha1/propagation_types.go
@@ -289,14 +289,40 @@ const (
 	// at the same time. During a failover, it is crucial to ensure that the old
 	// application is removed before creating a new one to avoid duplicate
 	// processing and maintaining state consistency.
+	//
+	// Deprecated: The term `Immediately` may be confusing when used alongside
+	// `GracePeriodSeconds`, which specifies that resources are removed after
+	// a grace period rather than at once.
+	// `Immediately` is replaced by `Directly` for clarity. This term remains
+	// functional in the current API version for backward compatibility and will
+	// be removed when PropagationPolicy advances to alpha2 or beta.
 	Immediately PurgeMode = "Immediately"
 	// Graciously represents that Karmada will wait for the application to
 	// come back to healthy on the new cluster or after a timeout is reached
 	// before evicting the application.
+	//
+	// Deprecated: The term `Graciously` is replaced by `Gracefully` for correct
+	// English usage. This term remains functional in the current API version for
+	// backward compatibility and will be removed when PropagationPolicy advances
+	// to alpha2 or beta.
 	Graciously PurgeMode = "Graciously"
 	// Never represents that Karmada will not evict the application and
 	// users manually confirms how to clean up redundant copies.
 	Never PurgeMode = "Never"
+
+	// PurgeModeDirectly represents that Karmada will directly evict the legacy
+	// application. This is useful in scenarios where an application can not
+	// tolerate two instances running simultaneously.
+	// For example, the Flink application supports exactly-once state consistency,
+	// which means it requires that no two instances of the application are running
+	// at the same time. During a failover, it is crucial to ensure that the old
+	// application is removed before creating a new one to avoid duplicate
+	// processing and maintaining state consistency.
+	PurgeModeDirectly PurgeMode = "Directly"
+	// PurgeModeGracefully represents that Karmada will wait for the application to
+	// come back to healthy on the new cluster or after a timeout is reached
+	// before evicting the application.
+	PurgeModeGracefully PurgeMode = "Gracefully"
 )
 
 // FailoverBehavior indicates failover behaviors in case of an application or
@@ -310,9 +336,13 @@ type FailoverBehavior struct {
 	Application *ApplicationFailoverBehavior `json:"application,omitempty"`
 
 	// Cluster indicates failover behaviors in case of cluster failure.
-	// If this value is nil, failover is disabled.
+	// If this value is nil, the failover behavior in case of cluster failure
+	// will be controlled by the controller's no-execute-taint-eviction-purge-mode
+	// parameter.
+	// If set, the failover behavior in case of cluster failure will be defined
+	// by this value.
 	// +optional
-	// Cluster *ClusterFailoverBehavior `json:"cluster,omitempty"`
+	Cluster *ClusterFailoverBehavior `json:"cluster,omitempty"`
 }
 
 // ApplicationFailoverBehavior indicates application failover behaviors.
@@ -326,10 +356,11 @@ type ApplicationFailoverBehavior struct {
 
 	// PurgeMode represents how to deal with the legacy applications on the
 	// cluster from which the application is migrated.
-	// Valid options are "Immediately", "Graciously" and "Never".
-	// Defaults to "Graciously".
-	// +kubebuilder:validation:Enum=Immediately;Graciously;Never
-	// +kubebuilder:default=Graciously
+	// Valid options are "Directly", "Gracefully", "Never", "Immediately"(deprecated),
+	// and "Graciously"(deprecated).
+	// Defaults to "Gracefully".
+	// +kubebuilder:validation:Enum=Directly;Gracefully;Never;Immediately;Graciously
+	// +kubebuilder:default=Gracefully
 	// +optional
 	PurgeMode PurgeMode `json:"purgeMode,omitempty"`
 
@@ -360,6 +391,35 @@ type ApplicationFailoverBehavior struct {
 	StatePreservation *StatePreservation `json:"statePreservation,omitempty"`
 }
 
+// ClusterFailoverBehavior indicates cluster failover behaviors.
+type ClusterFailoverBehavior struct {
+	// PurgeMode represents how to deal with the legacy applications on the
+	// cluster from which the application is migrated.
+	// Valid options are "Directly", "Gracefully".
+	// Defaults to "Gracefully".
+	// +kubebuilder:validation:Enum=Directly;Gracefully
+	// +kubebuilder:default=Gracefully
+	// +optional
+	PurgeMode PurgeMode `json:"purgeMode,omitempty"`
+
+	// StatePreservation defines the policy for preserving and restoring state data
+	// during failover events for stateful applications.
+	//
+	// When an application fails over from one cluster to another, this policy enables
+	// the extraction of critical data from the original resource configuration.
+	// Upon successful migration, the extracted data is then re-injected into the new
+	// resource, ensuring that the application can resume operation with its previous
+	// state intact.
+	// This is particularly useful for stateful applications where maintaining data
+	// consistency across failover events is crucial.
+	// If not specified, means no state data will be preserved.
+	//
+	// Note: This requires the StatefulFailoverInjection feature gate to be enabled,
+	// which is alpha.
+	// +optional
+	StatePreservation *StatePreservation `json:"statePreservation,omitempty"`
+}
+
 // DecisionConditions represents the decision conditions of performing the failover process.
 type DecisionConditions struct {
 	// TolerationSeconds represents the period of time Karmada should wait
diff --git a/pkg/apis/policy/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/policy/v1alpha1/zz_generated.deepcopy.go
index 2688536de..fca575aa2 100644
--- a/pkg/apis/policy/v1alpha1/zz_generated.deepcopy.go
+++ b/pkg/apis/policy/v1alpha1/zz_generated.deepcopy.go
@@ -107,6 +107,27 @@ func (in *ClusterAffinityTerm) DeepCopy() *ClusterAffinityTerm {
 	return out
 }
 
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *ClusterFailoverBehavior) DeepCopyInto(out *ClusterFailoverBehavior) {
+	*out = *in
+	if in.StatePreservation != nil {
+		in, out := &in.StatePreservation, &out.StatePreservation
+		*out = new(StatePreservation)
+		(*in).DeepCopyInto(*out)
+	}
+	return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterFailoverBehavior.
+func (in *ClusterFailoverBehavior) DeepCopy() *ClusterFailoverBehavior {
+	if in == nil {
+		return nil
+	}
+	out := new(ClusterFailoverBehavior)
+	in.DeepCopyInto(out)
+	return out
+}
+
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *ClusterOverridePolicy) DeepCopyInto(out *ClusterOverridePolicy) {
 	*out = *in
@@ -417,6 +438,11 @@ func (in *FailoverBehavior) DeepCopyInto(out *FailoverBehavior) {
 		*out = new(ApplicationFailoverBehavior)
 		(*in).DeepCopyInto(*out)
 	}
+	if in.Cluster != nil {
+		in, out := &in.Cluster, &out.Cluster
+		*out = new(ClusterFailoverBehavior)
+		(*in).DeepCopyInto(*out)
+	}
 	return
 }
 
diff --git a/pkg/apis/work/v1alpha2/binding_types.go b/pkg/apis/work/v1alpha2/binding_types.go
index 791d7af37..cf83ba719 100644
--- a/pkg/apis/work/v1alpha2/binding_types.go
+++ b/pkg/apis/work/v1alpha2/binding_types.go
@@ -86,6 +86,17 @@ type ResourceBindingSpec struct {
 	// +optional
 	Replicas int32 `json:"replicas,omitempty"`
 
+	// Components represents the requirements of multiple pod templates of the referencing resource.
+	// It is designed to support workloads that consist of multiple pod templates,
+	// such as distributed training jobs (e.g., PyTorch, TensorFlow) and big data workloads (e.g., FlinkDeployment),
+	// where each workload is composed of more than one pod template. It is also capable of representing
+	// single-component workloads, such as Deployment.
+	//
+	// Note: This field is intended to replace the legacy ReplicaRequirements and Replicas fields above.
+	// It is only populated when the MultiplePodTemplatesScheduling feature gate is enabled.
+	// +optional
+	Components []ComponentRequirements `json:"components,omitempty"`
+
 	// Clusters represents target member clusters where the resource to be deployed.
 	// +optional
 	Clusters []TargetCluster `json:"clusters,omitempty"`
@@ -212,6 +223,24 @@ type ReplicaRequirements struct {
 	PriorityClassName string `json:"priorityClassName,omitempty"`
 }
 
+// ComponentRequirements represents the requirements for a specific component.
+type ComponentRequirements struct {
+	// Name of this component.
+	// It is required when the resource contains multiple components to ensure proper identification,
+	// and must also be unique within the same resource.
+	// +kubebuilder:validation:MaxLength=32
+	// +optional
+	Name string `json:"name,omitempty"`
+
+	// Replicas represents the replica number of the resource's component.
+	// +required
+	Replicas int32 `json:"replicas"`
+
+	// ReplicaRequirements represents the requirements required by each replica for this component.
+	// +optional
+	ReplicaRequirements *ReplicaRequirements `json:"replicaRequirements,omitempty"`
+}
+
 // NodeClaim represents the node claim HardNodeAffinity, NodeSelector and Tolerations required by each replica.
 type NodeClaim struct {
 	// A node selector represents the union of the results of one or more label queries over a set of
@@ -246,8 +275,8 @@ type GracefulEvictionTask struct {
 
 	// PurgeMode represents how to deal with the legacy applications on the
 	// cluster from which the application is migrated.
-	// Valid options are "Immediately", "Graciously" and "Never".
-	// +kubebuilder:validation:Enum=Immediately;Graciously;Never
+	// Valid options are "Immediately", "Directly", "Graciously", "Gracefully" and "Never".
+	// +kubebuilder:validation:Enum=Immediately;Directly;Graciously;Gracefully;Never
 	// +optional
 	PurgeMode policyv1alpha1.PurgeMode `json:"purgeMode,omitempty"`
 
@@ -432,6 +461,11 @@ const (
 	// BindingReasonUnschedulable reason in Scheduled condition means that the scheduler can't schedule
 	// the binding right now, for example due to insufficient resources in the clusters.
 	BindingReasonUnschedulable = "Unschedulable"
+
+	// BindingReasonQuotaExceeded reason in Scheduled condition means that the scheduler can't schedule
+	// the binding because the resource requirement exceeds one or more of the FederatedResourceQuotas
+	// defined in the namespace.
+	BindingReasonQuotaExceeded = "QuotaExceeded"
 )
 
 // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
diff --git a/pkg/apis/work/v1alpha2/zz_generated.deepcopy.go b/pkg/apis/work/v1alpha2/zz_generated.deepcopy.go
index 2aeeeefb5..68ae41722 100644
--- a/pkg/apis/work/v1alpha2/zz_generated.deepcopy.go
+++ b/pkg/apis/work/v1alpha2/zz_generated.deepcopy.go
@@ -131,6 +131,27 @@ func (in *ClusterResourceBindingList) DeepCopyObject() runtime.Object {
 	return nil
 }
 
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *ComponentRequirements) DeepCopyInto(out *ComponentRequirements) {
+	*out = *in
+	if in.ReplicaRequirements != nil {
+		in, out := &in.ReplicaRequirements, &out.ReplicaRequirements
+		*out = new(ReplicaRequirements)
+		(*in).DeepCopyInto(*out)
+	}
+	return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ComponentRequirements.
+func (in *ComponentRequirements) DeepCopy() *ComponentRequirements {
+	if in == nil {
+		return nil
+	}
+	out := new(ComponentRequirements)
+	in.DeepCopyInto(out)
+	return out
+}
+
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *GracefulEvictionTask) DeepCopyInto(out *GracefulEvictionTask) {
 	*out = *in
@@ -327,6 +348,13 @@ func (in *ResourceBindingSpec) DeepCopyInto(out *ResourceBindingSpec) {
 		*out = new(ReplicaRequirements)
 		(*in).DeepCopyInto(*out)
 	}
+	if in.Components != nil {
+		in, out := &in.Components, &out.Components
+		*out = make([]ComponentRequirements, len(*in))
+		for i := range *in {
+			(*in)[i].DeepCopyInto(&(*out)[i])
+		}
+	}
 	if in.Clusters != nil {
 		in, out := &in.Clusters, &out.Clusters
 		*out = make([]TargetCluster, len(*in))
diff --git a/pkg/controllers/applicationfailover/common.go b/pkg/controllers/applicationfailover/common.go
index a8514cbe7..45fc32076 100644
--- a/pkg/controllers/applicationfailover/common.go
+++ b/pkg/controllers/applicationfailover/common.go
@@ -211,11 +211,13 @@ func buildTaskOptions(failoverBehavior *policyv1alpha1.ApplicationFailoverBehavi
 	}
 
 	switch failoverBehavior.PurgeMode {
-	case policyv1alpha1.Graciously:
+	//nolint:staticcheck
+	// disable `deprecation` check for backward compatibility.
+	case policyv1alpha1.Graciously, policyv1alpha1.PurgeModeGracefully:
 		if features.FeatureGate.Enabled(features.GracefulEviction) {
 			taskOpts = append(taskOpts, workv1alpha2.WithGracePeriodSeconds(failoverBehavior.GracePeriodSeconds))
 		} else {
-			err := fmt.Errorf("GracefulEviction featureGate must be enabled when purgeMode is %s", policyv1alpha1.Graciously)
+			err := fmt.Errorf("GracefulEviction featureGate must be enabled when purgeMode is %s", failoverBehavior.PurgeMode)
 			klog.Error(err)
 			return nil, err
 		}
diff --git a/pkg/controllers/binding/common.go b/pkg/controllers/binding/common.go
index d44afd237..3be0052b5 100644
--- a/pkg/controllers/binding/common.go
+++ b/pkg/controllers/binding/common.go
@@ -171,7 +171,7 @@ func getBindingSpec(binding metav1.Object, scope apiextensionsv1.ResourceScope)
 //  2. If consecutive failovers occur, for example, an application is migrated form clusterA
 //     to clusterB and then to clusterC, the PreservedLabelState before the last failover is
 //     used for injection. If the PreservedLabelState is empty, the injection is skipped.
-//  3. The injection operation is performed only when PurgeMode is set to Immediately.
+//  3. The injection operation is performed only when PurgeMode is set to Immediately or Directly.
 func injectReservedLabelState(bindingSpec workv1alpha2.ResourceBindingSpec, moveToCluster workv1alpha2.TargetCluster, workload *unstructured.Unstructured, clustersLen int) *unstructured.Unstructured {
 	if clustersLen > 1 {
 		return workload
@@ -182,7 +182,10 @@ func injectReservedLabelState(bindingSpec workv1alpha2.ResourceBindingSpec, move
 	}
 	targetEvictionTask := bindingSpec.GracefulEvictionTasks[len(bindingSpec.GracefulEvictionTasks)-1]
 
-	if targetEvictionTask.PurgeMode != policyv1alpha1.Immediately {
+	//nolint:staticcheck
+	// disable `deprecation` check for backward compatibility.
+	if targetEvictionTask.PurgeMode != policyv1alpha1.Immediately &&
+		targetEvictionTask.PurgeMode != policyv1alpha1.PurgeModeDirectly {
 		return workload
 	}
 
diff --git a/pkg/controllers/cluster/taint_manager.go b/pkg/controllers/cluster/taint_manager.go
index b9315ce94..56956e3ed 100644
--- a/pkg/controllers/cluster/taint_manager.go
+++ b/pkg/controllers/cluster/taint_manager.go
@@ -176,10 +176,10 @@ func (tc *NoExecuteTaintManager) syncBindingEviction(key util.QueueKey) error {
 	var purgeMode policyv1alpha1.PurgeMode
 	if needEviction {
 		switch tc.NoExecuteTaintEvictionPurgeMode {
-		case "Gracefully":
-			purgeMode = policyv1alpha1.Graciously
-		case "Directly":
-			purgeMode = policyv1alpha1.Immediately
+		case string(policyv1alpha1.PurgeModeGracefully):
+			purgeMode = policyv1alpha1.PurgeModeGracefully
+		case string(policyv1alpha1.PurgeModeDirectly):
+			purgeMode = policyv1alpha1.PurgeModeDirectly
 		}
 	}
 
@@ -192,7 +192,6 @@ func (tc *NoExecuteTaintManager) syncBindingEviction(key util.QueueKey) error {
 			workv1alpha2.WithPurgeMode(purgeMode),
 			workv1alpha2.WithProducer(workv1alpha2.EvictionProducerTaintManager),
 			workv1alpha2.WithReason(workv1alpha2.EvictionReasonTaintUntolerated)))
-
 		if err = tc.Update(context.TODO(), binding); err != nil {
 			helper.EmitClusterEvictionEventForResourceBinding(binding, cluster, tc.EventRecorder, err)
 			klog.ErrorS(err, "Failed to update binding", "binding", klog.KObj(binding))
@@ -238,10 +237,10 @@ func (tc *NoExecuteTaintManager) syncClusterBindingEviction(key util.QueueKey) e
 	var purgeMode policyv1alpha1.PurgeMode
 	if needEviction {
 		switch tc.NoExecuteTaintEvictionPurgeMode {
-		case "Gracefully":
-			purgeMode = policyv1alpha1.Graciously
-		case "Directly":
-			purgeMode = policyv1alpha1.Immediately
+		case string(policyv1alpha1.PurgeModeGracefully):
+			purgeMode = policyv1alpha1.PurgeModeGracefully
+		case string(policyv1alpha1.PurgeModeDirectly):
+			purgeMode = policyv1alpha1.PurgeModeDirectly
 		}
 	}
 
diff --git a/pkg/controllers/ctrlutil/work.go b/pkg/controllers/ctrlutil/work.go
index 3d8730e51..a71299b43 100644
--- a/pkg/controllers/ctrlutil/work.go
+++ b/pkg/controllers/ctrlutil/work.go
@@ -71,7 +71,7 @@ func CreateOrUpdateWork(ctx context.Context, c client.Client, workMeta metav1.Ob
 
 			runtimeObject.Labels = util.DedupeAndMergeLabels(runtimeObject.Labels, work.Labels)
 			runtimeObject.Annotations = util.DedupeAndMergeAnnotations(runtimeObject.Annotations, work.Annotations)
-			runtimeObject.Finalizers = work.Finalizers
+			runtimeObject.Finalizers = util.MergeFinalizers(runtimeObject.Finalizers, work.Finalizers)
 			runtimeObject.Spec = work.Spec
 
 			// Do the same thing as the mutating webhook does, add the permanent ID to workload if not exist,
diff --git a/pkg/controllers/federatedhpa/federatedhpa_controller.go b/pkg/controllers/federatedhpa/federatedhpa_controller.go
index 35b3fa28f..2a008b46b 100644
--- a/pkg/controllers/federatedhpa/federatedhpa_controller.go
+++ b/pkg/controllers/federatedhpa/federatedhpa_controller.go
@@ -577,7 +577,6 @@ func (c *FHPAController) buildPodInformerForCluster(clusterScaleClient *util.Clu
 		return nil
 	}(); err != nil {
 		klog.ErrorS(err, "Failed to sync cache for cluster", "cluster", clusterScaleClient.ClusterName)
-		c.TypedInformerManager.Stop(clusterScaleClient.ClusterName)
 		return nil, err
 	}
 
diff --git a/pkg/controllers/hpascaletargetmarker/hpa_scale_target_marker_predicate.go b/pkg/controllers/hpascaletargetmarker/hpa_scale_target_marker_predicate.go
index 45d1af2e9..124d50447 100644
--- a/pkg/controllers/hpascaletargetmarker/hpa_scale_target_marker_predicate.go
+++ b/pkg/controllers/hpascaletargetmarker/hpa_scale_target_marker_predicate.go
@@ -17,6 +17,8 @@ limitations under the License.
 package hpascaletargetmarker
 
 import (
+	"errors"
+
 	autoscalingv2 "k8s.io/api/autoscaling/v2"
 	"k8s.io/klog/v2"
 	"sigs.k8s.io/controller-runtime/pkg/event"
@@ -31,7 +33,8 @@ var _ predicate.Predicate = &HpaScaleTargetMarker{}
 func (r *HpaScaleTargetMarker) Create(e event.CreateEvent) bool {
 	hpa, ok := e.Object.(*autoscalingv2.HorizontalPodAutoscaler)
 	if !ok {
-		klog.Errorf("create predicates in hpa controller called, but obj is not hpa type")
+		err := errors.New("object is not HPA type")
+		klog.ErrorS(err, "create predicate in hpa controller failed on type assertion")
 		return false
 	}
 
@@ -47,13 +50,15 @@ func (r *HpaScaleTargetMarker) Create(e event.CreateEvent) bool {
 func (r *HpaScaleTargetMarker) Update(e event.UpdateEvent) bool {
 	oldHPA, ok := e.ObjectOld.(*autoscalingv2.HorizontalPodAutoscaler)
 	if !ok {
-		klog.Errorf("update predicates in hpa controller called, but old obj is not hpa type")
+		err := errors.New("old object is not HPA type")
+		klog.ErrorS(err, "update predicate in hpa controller failed on type assertion")
 		return false
 	}
 
 	newHPA, ok := e.ObjectNew.(*autoscalingv2.HorizontalPodAutoscaler)
 	if !ok {
-		klog.Errorf("update predicates in hpa controller called, but new obj is not hpa type")
+		err := errors.New("new object is not HPA type")
+		klog.ErrorS(err, "update predicate in hpa controller failed on type assertion")
 		return false
 	}
 
@@ -75,7 +80,8 @@ func (r *HpaScaleTargetMarker) Update(e event.UpdateEvent) bool {
 func (r *HpaScaleTargetMarker) Delete(e event.DeleteEvent) bool {
 	hpa, ok := e.Object.(*autoscalingv2.HorizontalPodAutoscaler)
 	if !ok {
-		klog.Errorf("delete predicates in hpa controller called, but obj is not hpa type")
+		err := errors.New("object is not HPA type")
+		klog.ErrorS(err, "delete predicate in hpa controller failed on type assertion")
 		return false
 	}
 
diff --git a/pkg/controllers/hpascaletargetmarker/hpa_scale_target_marker_worker.go b/pkg/controllers/hpascaletargetmarker/hpa_scale_target_marker_worker.go
index 1e8b8f97d..c4900df2d 100644
--- a/pkg/controllers/hpascaletargetmarker/hpa_scale_target_marker_worker.go
+++ b/pkg/controllers/hpascaletargetmarker/hpa_scale_target_marker_worker.go
@@ -18,6 +18,7 @@ package hpascaletargetmarker
 
 import (
 	"context"
+	"errors"
 	"fmt"
 
 	autoscalingv2 "k8s.io/api/autoscaling/v2"
@@ -48,8 +49,9 @@ type labelEvent struct {
 func (r *HpaScaleTargetMarker) reconcileScaleRef(key util.QueueKey) (err error) {
 	event, ok := key.(labelEvent)
 	if !ok {
-		klog.Errorf("Found invalid key when reconciling hpa scale ref: %+v", key)
-		return nil
+		err = errors.New("invalid key")
+		klog.ErrorS(err, "reconcile hpa scale ref failed on type assertion", "key", key)
+		return err
 	}
 
 	switch event.kind {
@@ -58,12 +60,13 @@ func (r *HpaScaleTargetMarker) reconcileScaleRef(key util.QueueKey) (err error)
 	case deleteLabelEvent:
 		err = r.deleteHPALabelFromScaleRef(context.TODO(), event.hpa)
 	default:
-		klog.Errorf("Found invalid key when reconciling hpa scale ref: %+v", key)
-		return nil
+		err = errors.New("invalid label event")
+		klog.ErrorS(err, "reconcile hpa scale ref failed", "key", key)
+		return err
 	}
 
 	if err != nil {
-		klog.Errorf("reconcile scale ref failed: %+v", err)
+		klog.ErrorS(err, "reconcile hpa scale ref failed")
 	}
 	return err
 }
@@ -78,7 +81,7 @@ func (r *HpaScaleTargetMarker) addHPALabelToScaleRef(ctx context.Context, hpa *a
 	scaleRef, err := r.DynamicClient.Resource(mapping.Resource).Namespace(hpa.Namespace).Get(ctx, hpa.Spec.ScaleTargetRef.Name, metav1.GetOptions{})
 	if err != nil {
 		if apierrors.IsNotFound(err) {
-			klog.Infof("scale ref resource is not found (%s/%v), skip processing", hpa.Namespace, hpa.Spec.ScaleTargetRef)
+			klog.InfoS("scale ref resource is not found, skip processing", "namespace", hpa.Namespace, "scaleTargetRef", hpa.Spec.ScaleTargetRef)
 			return nil
 		}
 		return fmt.Errorf("failed to find scale ref resource (%s/%v), err: %+v", hpa.Namespace, hpa.Spec.ScaleTargetRef, err)
@@ -92,7 +95,7 @@ func (r *HpaScaleTargetMarker) addHPALabelToScaleRef(ctx context.Context, hpa *a
 		return fmt.Errorf("failed to gen merge patch (%s/%v), err: %+v", hpa.Namespace, hpa.Spec.ScaleTargetRef, err)
 	}
 	if len(patchBytes) == 0 {
-		klog.Infof("hpa labels already exist, skip adding (%s/%v)", hpa.Namespace, hpa.Spec.ScaleTargetRef)
+		klog.InfoS("hpa labels already exist, skip adding", "namespace", hpa.Namespace, "scaleTargetRef", hpa.Spec.ScaleTargetRef)
 		return nil
 	}
 
@@ -100,13 +103,13 @@ func (r *HpaScaleTargetMarker) addHPALabelToScaleRef(ctx context.Context, hpa *a
 		Patch(ctx, newScaleRef.GetName(), types.MergePatchType, patchBytes, metav1.PatchOptions{})
 	if err != nil {
 		if apierrors.IsNotFound(err) {
-			klog.Infof("scale ref resource is not found (%s/%v), skip processing", hpa.Namespace, hpa.Spec.ScaleTargetRef)
+			klog.InfoS("scale ref resource is not found, skip processing", "namespace", hpa.Namespace, "scaleTargetRef", hpa.Spec.ScaleTargetRef)
 			return nil
 		}
 		return fmt.Errorf("failed to patch scale ref resource (%s/%v), err: %+v", hpa.Namespace, hpa.Spec.ScaleTargetRef, err)
 	}
 
-	klog.Infof("add hpa labels to %s/%v success", hpa.Namespace, hpa.Spec.ScaleTargetRef)
+	klog.InfoS("add hpa labels to scale ref success", "namespace", hpa.Namespace, "scaleTargetRef", hpa.Spec.ScaleTargetRef)
 	return nil
 }
 
@@ -120,7 +123,7 @@ func (r *HpaScaleTargetMarker) deleteHPALabelFromScaleRef(ctx context.Context, h
 	scaleRef, err := r.DynamicClient.Resource(mapping.Resource).Namespace(hpa.Namespace).Get(ctx, hpa.Spec.ScaleTargetRef.Name, metav1.GetOptions{})
 	if err != nil {
 		if apierrors.IsNotFound(err) {
-			klog.Infof("scale ref resource is not found (%s/%v), skip processing", hpa.Namespace, hpa.Spec.ScaleTargetRef)
+			klog.InfoS("scale ref resource is not found, skip processing", "namespace", hpa.Namespace, "scaleTargetRef", hpa.Spec.ScaleTargetRef)
 			return nil
 		}
 		return fmt.Errorf("failed to find scale ref resource (%s/%v), err: %+v", hpa.Namespace, hpa.Spec.ScaleTargetRef, err)
@@ -134,7 +137,7 @@ func (r *HpaScaleTargetMarker) deleteHPALabelFromScaleRef(ctx context.Context, h
 		return fmt.Errorf("failed to gen merge patch (%s/%v), err: %+v", hpa.Namespace, hpa.Spec.ScaleTargetRef, err)
 	}
 	if len(patchBytes) == 0 {
-		klog.Infof("hpa labels not exist, skip deleting (%s/%v)", hpa.Namespace, hpa.Spec.ScaleTargetRef)
+		klog.InfoS("hpa labels not exist, skip deleting", "namespace", hpa.Namespace, "scaleTargetRef", hpa.Spec.ScaleTargetRef)
 		return nil
 	}
 
@@ -142,12 +145,12 @@ func (r *HpaScaleTargetMarker) deleteHPALabelFromScaleRef(ctx context.Context, h
 		Patch(ctx, newScaleRef.GetName(), types.MergePatchType, patchBytes, metav1.PatchOptions{})
 	if err != nil {
 		if apierrors.IsNotFound(err) {
-			klog.Infof("scale ref resource is not found (%s/%v), skip processing", hpa.Namespace, hpa.Spec.ScaleTargetRef)
+			klog.InfoS("scale ref resource is not found, skip processing", "namespace", hpa.Namespace, "scaleTargetRef", hpa.Spec.ScaleTargetRef)
 			return nil
 		}
 		return fmt.Errorf("failed to patch scale ref resource (%s/%v), err: %+v", hpa.Namespace, hpa.Spec.ScaleTargetRef, err)
 	}
 
-	klog.Infof("delete hpa labels from %s/%+v success", hpa.Namespace, hpa.Spec.ScaleTargetRef)
+	klog.InfoS("delete hpa labels from scale ref success", "namespace", hpa.Namespace, "scaleTargetRef", hpa.Spec.ScaleTargetRef)
 	return nil
 }
diff --git a/pkg/controllers/mcs/endpointslice_controller.go b/pkg/controllers/mcs/endpointslice_controller.go
index 4d918628c..4b0a32816 100644
--- a/pkg/controllers/mcs/endpointslice_controller.go
+++ b/pkg/controllers/mcs/endpointslice_controller.go
@@ -54,7 +54,7 @@ type EndpointSliceController struct {
 
 // Reconcile performs a full reconciliation for the object referred to by the Request.
 func (c *EndpointSliceController) Reconcile(ctx context.Context, req controllerruntime.Request) (controllerruntime.Result, error) {
-	klog.V(4).Infof("Reconciling Work %s.", req.NamespacedName.String())
+	klog.V(4).InfoS("Reconciling Work", "namespace", req.NamespacedName.Namespace, "name", req.NamespacedName.Name)
 
 	work := &workv1alpha1.Work{}
 	if err := c.Client.Get(ctx, req.NamespacedName, work); err != nil {
@@ -69,7 +69,8 @@ func (c *EndpointSliceController) Reconcile(ctx context.Context, req controllerr
 		if err := helper.DeleteEndpointSlice(ctx, c.Client, labels.Set{
 			workv1alpha2.WorkPermanentIDLabel: work.Labels[workv1alpha2.WorkPermanentIDLabel],
 		}); err != nil {
-			klog.Errorf("Failed to delete endpointslice of the work(%s/%s) when deleting the work, err is %v", work.Namespace, work.Name, err)
+			klog.ErrorS(err, "Failed to delete endpointslice when deleting work", "namespace",
+				work.Namespace, "workName", work.Name)
 			return controllerruntime.Result{}, err
 		}
 		return controllerruntime.Result{}, c.removeFinalizer(ctx, work.DeepCopy())
@@ -83,7 +84,7 @@ func (c *EndpointSliceController) Reconcile(ctx context.Context, req controllerr
 			workv1alpha2.WorkPermanentIDLabel: work.Labels[workv1alpha2.WorkPermanentIDLabel],
 		})
 		if err != nil {
-			klog.Errorf("Failed to delete endpointslice of the work(%s/%s) when the serviceexport is deleted, err is %v", work.Namespace, work.Name, err)
+			klog.ErrorS(err, "Failed to delete endpointslice when serviceexport is deleted", "namespace", work.Namespace, "workName", work.Name)
 			return controllerruntime.Result{}, err
 		}
 		return controllerruntime.Result{}, c.removeFinalizer(ctx, work.DeepCopy())
@@ -130,21 +131,21 @@ func (c *EndpointSliceController) SetupWithManager(mgr controllerruntime.Manager
 func (c *EndpointSliceController) collectEndpointSliceFromWork(ctx context.Context, work *workv1alpha1.Work) error {
 	clusterName, err := names.GetClusterName(work.Namespace)
 	if err != nil {
-		klog.Errorf("Failed to get cluster name for work %s/%s", work.Namespace, work.Name)
+		klog.ErrorS(err, "Failed to get cluster name for work", "namespace", work.Namespace, "workName", work.Name)
 		return err
 	}
 
 	for _, manifest := range work.Spec.Workload.Manifests {
 		unstructObj := &unstructured.Unstructured{}
 		if err := unstructObj.UnmarshalJSON(manifest.Raw); err != nil {
-			klog.Errorf("Failed to unmarshal workload, error is: %v", err)
+			klog.ErrorS(err, "Failed to unmarshal workload")
 			return err
 		}
 
 		endpointSlice := &discoveryv1.EndpointSlice{}
 		err = helper.ConvertToTypedObject(unstructObj, endpointSlice)
 		if err != nil {
-			klog.Errorf("Failed to convert unstructured to typed object: %v", err)
+			klog.ErrorS(err, "Failed to convert unstructured to typed object")
 			return err
 		}
 
@@ -152,6 +153,7 @@ func (c *EndpointSliceController) collectEndpointSliceFromWork(ctx context.Conte
 		desiredEndpointSlice.Labels = util.DedupeAndMergeLabels(desiredEndpointSlice.Labels, map[string]string{
 			workv1alpha2.WorkPermanentIDLabel: work.Labels[workv1alpha2.WorkPermanentIDLabel],
 			discoveryv1.LabelServiceName:      names.GenerateDerivedServiceName(work.Labels[util.ServiceNameLabel]),
+			discoveryv1.LabelManagedBy:        util.EndpointSliceControllerLabelValue,
 		})
 		desiredEndpointSlice.Annotations = util.DedupeAndMergeAnnotations(desiredEndpointSlice.Annotations, map[string]string{
 			workv1alpha2.WorkNamespaceAnnotation: work.Namespace,
@@ -159,6 +161,7 @@ func (c *EndpointSliceController) collectEndpointSliceFromWork(ctx context.Conte
 		})
 
 		if err = helper.CreateOrUpdateEndpointSlice(ctx, c.Client, desiredEndpointSlice); err != nil {
+			klog.ErrorS(err, "Failed to create or update endpointslice", "namespace", desiredEndpointSlice.Namespace, "name", desiredEndpointSlice.Name)
 			return err
 		}
 	}
diff --git a/pkg/controllers/mcs/service_export_controller.go b/pkg/controllers/mcs/service_export_controller.go
index a2b8325b8..1e03be0b5 100644
--- a/pkg/controllers/mcs/service_export_controller.go
+++ b/pkg/controllers/mcs/service_export_controller.go
@@ -64,6 +64,9 @@ import (
 const ServiceExportControllerName = "service-export-controller"
 
 // ServiceExportController is to sync ServiceExport and report EndpointSlices of exported service to control-plane.
+// It creates Informers for clusters where ServiceExport resources have been propagated,
+// registers EventHandlers for ServiceExport and EndpointSlice in those clusters,
+// and then asynchronously processes ServiceExport and EndpointSlice events.
 type ServiceExportController struct {
 	client.Client
 	EventRecorder               record.EventRecorder
@@ -94,7 +97,7 @@ var (
 
 // Reconcile performs a full reconciliation for the object referred to by the Request.
 func (c *ServiceExportController) Reconcile(ctx context.Context, req controllerruntime.Request) (controllerruntime.Result, error) {
-	klog.V(4).Infof("Reconciling Work %s", req.NamespacedName.String())
+	klog.V(4).InfoS("Reconciling Work", "namespace", req.NamespacedName.Namespace, "name", req.NamespacedName.Name)
 
 	work := &workv1alpha1.Work{}
 	if err := c.Client.Get(ctx, req.NamespacedName, work); err != nil {
@@ -112,25 +115,27 @@ func (c *ServiceExportController) Reconcile(ctx context.Context, req controllerr
 		return controllerruntime.Result{}, nil
 	}
 
+	// Controller should only handle ServiceExport Works, reduces noise and prevents unnecessary processing.
 	if !util.IsWorkContains(work.Spec.Workload.Manifests, serviceExportGVK) {
 		return controllerruntime.Result{}, nil
 	}
 
 	clusterName, err := names.GetClusterName(work.Namespace)
 	if err != nil {
-		klog.Errorf("Failed to get member cluster name for work %s/%s", work.Namespace, work.Name)
+		klog.ErrorS(err, "Failed to get member cluster name for work", "namespace", work.Namespace, "workName", work.Name)
 		return controllerruntime.Result{}, err
 	}
 
 	cluster, err := util.GetCluster(c.Client, clusterName)
 	if err != nil {
-		klog.Errorf("Failed to get the given member cluster %s", clusterName)
+		klog.ErrorS(err, "Failed to get the given member cluster", "cluster", clusterName)
 		return controllerruntime.Result{}, err
 	}
 
 	if !util.IsClusterReady(&cluster.Status) {
-		klog.Errorf("Stop sync work(%s/%s) for cluster(%s) as cluster not ready.", work.Namespace, work.Name, cluster.Name)
-		return controllerruntime.Result{}, fmt.Errorf("cluster(%s) not ready", cluster.Name)
+		err := fmt.Errorf("cluster(%s) not ready", cluster.Name)
+		klog.ErrorS(err, "Stop sync work for cluster as cluster not ready.", "namespace", work.Namespace, "workName", work.Name, "cluster", cluster.Name)
+		return controllerruntime.Result{}, err
 	}
 
 	return controllerruntime.Result{}, c.buildResourceInformers(cluster)
@@ -138,7 +143,9 @@ func (c *ServiceExportController) Reconcile(ctx context.Context, req controllerr
 
 // SetupWithManager creates a controller and register to controller manager.
 func (c *ServiceExportController) SetupWithManager(mgr controllerruntime.Manager) error {
-	return controllerruntime.NewControllerManagedBy(mgr).Named(ServiceExportControllerName).For(&workv1alpha1.Work{}, builder.WithPredicates(c.PredicateFunc)).
+	return controllerruntime.NewControllerManagedBy(mgr).
+		Named(ServiceExportControllerName).
+		For(&workv1alpha1.Work{}, builder.WithPredicates(c.PredicateFunc)).
 		WithOptions(controller.Options{
 			RateLimiter: ratelimiterflag.DefaultControllerRateLimiter[controllerruntime.Request](c.RateLimiterOptions),
 		}).
@@ -155,6 +162,8 @@ func (c *ServiceExportController) RunWorkQueue() {
 	c.worker = util.NewAsyncWorker(workerOptions)
 	c.worker.Run(c.Context, c.WorkerNumber)
 
+	// TODO(@XiShanYongYe-Chang): Need to re-examine whether this call is necessary.
+	// If it is necessary, clarify the reason; otherwise, delete this call.
 	go c.enqueueReportedEpsServiceExport()
 }
 
@@ -165,7 +174,7 @@ func (c *ServiceExportController) enqueueReportedEpsServiceExport() {
 			indexregistry.WorkIndexByFieldSuspendDispatching: "true",
 		})
 		if err != nil {
-			klog.Errorf("Failed to list collected EndpointSlices Work from member clusters: %v", err)
+			klog.ErrorS(err, "Failed to list collected EndpointSlices Work from member clusters")
 			return false, nil
 		}
 		return true, nil
@@ -208,23 +217,22 @@ func (c *ServiceExportController) syncServiceExportOrEndpointSlice(key util.Queu
 	ctx := context.Background()
 	fedKey, ok := key.(keys.FederatedKey)
 	if !ok {
-		klog.Errorf("Failed to sync serviceExport as invalid key: %v", key)
-		return fmt.Errorf("invalid key")
+		err := fmt.Errorf("invalid key")
+		klog.ErrorS(err, "Failed to sync serviceExport as invalid key", "key", key)
+		return err
 	}
 
-	klog.V(4).Infof("Begin to sync %s", fedKey)
+	klog.V(4).InfoS("Begin to sync", "key", fedKey)
 
 	switch fedKey.Kind {
 	case util.ServiceExportKind:
 		if err := c.handleServiceExportEvent(ctx, fedKey); err != nil {
-			klog.Errorf("Failed to handle serviceExport(%s) event, Error: %v",
-				fedKey.NamespaceKey(), err)
+			klog.ErrorS(err, "Failed to handle serviceExport event", "serviceExport", fedKey.NamespaceKey())
 			return err
 		}
 	case util.EndpointSliceKind:
 		if err := c.handleEndpointSliceEvent(ctx, fedKey); err != nil {
-			klog.Errorf("Failed to handle endpointSlice(%s) event, Error: %v",
-				fedKey.NamespaceKey(), err)
+			klog.ErrorS(err, "Failed to handle endpointSlice event", "endpointSlice", fedKey.NamespaceKey())
 			return err
 		}
 	}
@@ -235,20 +243,20 @@ func (c *ServiceExportController) syncServiceExportOrEndpointSlice(key util.Queu
 func (c *ServiceExportController) buildResourceInformers(cluster *clusterv1alpha1.Cluster) error {
 	err := c.registerInformersAndStart(cluster)
 	if err != nil {
-		klog.Errorf("Failed to register informer for Cluster %s. Error: %v.", cluster.Name, err)
+		klog.ErrorS(err, "Failed to register informer for Cluster", "cluster", cluster.Name)
 		return err
 	}
 	return nil
 }
 
-// registerInformersAndStart builds informer manager for cluster if it doesn't exist, then constructs informers for gvr
-// and start it.
+// registerInformersAndStart builds informer manager for cluster if it doesn't exist, then
+// constructs informers for gvr and start it.
 func (c *ServiceExportController) registerInformersAndStart(cluster *clusterv1alpha1.Cluster) error {
 	singleClusterInformerManager := c.InformerManager.GetSingleClusterManager(cluster.Name)
 	if singleClusterInformerManager == nil {
 		dynamicClusterClient, err := c.ClusterDynamicClientSetFunc(cluster.Name, c.Client, c.ClusterClientOption)
 		if err != nil {
-			klog.Errorf("Failed to build dynamic cluster client for cluster %s.", cluster.Name)
+			klog.ErrorS(err, "Failed to build dynamic cluster client for cluster.", "cluster", cluster.Name)
 			return err
 		}
 		singleClusterInformerManager = c.InformerManager.ForCluster(dynamicClusterClient.ClusterName, dynamicClusterClient.DynamicClientSet, 0)
@@ -273,6 +281,7 @@ func (c *ServiceExportController) registerInformersAndStart(cluster *clusterv1al
 	c.InformerManager.Start(cluster.Name)
 
 	if err := func() error {
+		// this call is necessary; otherwise, `IsInformerSynced` will always return false.
 		synced := c.InformerManager.WaitForCacheSyncWithTimeout(cluster.Name, c.ClusterCacheSyncTimeout.Duration)
 		if synced == nil {
 			return fmt.Errorf("no informerFactory for cluster %s exist", cluster.Name)
@@ -284,8 +293,7 @@ func (c *ServiceExportController) registerInformersAndStart(cluster *clusterv1al
 		}
 		return nil
 	}(); err != nil {
-		klog.Errorf("Failed to sync cache for cluster: %s, error: %v", cluster.Name, err)
-		c.InformerManager.Stop(cluster.Name)
+		klog.ErrorS(err, "Failed to sync cache for cluster", "cluster", cluster.Name)
 		return err
 	}
 
@@ -309,7 +317,7 @@ func (c *ServiceExportController) genHandlerAddFunc(clusterName string) func(obj
 		curObj := obj.(runtime.Object)
 		key, err := keys.FederatedKeyFunc(clusterName, curObj)
 		if err != nil {
-			klog.Warningf("Failed to generate key for obj: %s", curObj.GetObjectKind().GroupVersionKind())
+			klog.ErrorS(err, "Failed to generate key for obj", "gvk", curObj.GetObjectKind().GroupVersionKind())
 			return
 		}
 		c.worker.Add(key)
@@ -322,7 +330,7 @@ func (c *ServiceExportController) genHandlerUpdateFunc(clusterName string) func(
 		if !reflect.DeepEqual(oldObj, newObj) {
 			key, err := keys.FederatedKeyFunc(clusterName, curObj)
 			if err != nil {
-				klog.Warningf("Failed to generate key for obj: %s", curObj.GetObjectKind().GroupVersionKind())
+				klog.ErrorS(err, "Failed to generate key for obj", "gvk", curObj.GetObjectKind().GroupVersionKind())
 				return
 			}
 			c.worker.Add(key)
@@ -342,7 +350,7 @@ func (c *ServiceExportController) genHandlerDeleteFunc(clusterName string) func(
 		oldObj := obj.(runtime.Object)
 		key, err := keys.FederatedKeyFunc(clusterName, oldObj)
 		if err != nil {
-			klog.Warningf("Failed to generate key for obj: %s", oldObj.GetObjectKind().GroupVersionKind())
+			klog.ErrorS(err, "Failed to generate key for obj", "gvk", oldObj.GetObjectKind().GroupVersionKind())
 			return
 		}
 		c.worker.Add(key)
@@ -363,11 +371,11 @@ func (c *ServiceExportController) handleServiceExportEvent(ctx context.Context,
 
 	// Even though the EndpointSlice will be synced when dealing with EndpointSlice events, thus the 'report' here may
 	// be redundant, but it helps to avoid a corner case:
-	// If skip report here, after ServiceExport deletion and re-creation, if no EndpointSlice changes, we didn't get a
-	// change to sync.
+	// When ServiceExport is created after Service in member cluster, and EndpointSlice events have been processed,
+	// if we don't handle the ServiceExport event, the EndpointSlice will not be collected to the control plane,
+	// unless a new EndpointSlice event is received.
 	if err = c.reportEndpointSliceWithServiceExportCreate(ctx, serviceExportKey); err != nil {
-		klog.Errorf("Failed to handle ServiceExport(%s) event, Error: %v",
-			serviceExportKey.NamespaceKey(), err)
+		klog.ErrorS(err, "Failed to handle ServiceExport event", "namespace", serviceExportKey.Namespace, "name", serviceExportKey.Name)
 		return err
 	}
 
@@ -386,9 +394,13 @@ func (c *ServiceExportController) handleEndpointSliceEvent(ctx context.Context,
 		return err
 	}
 
+	// Exclude EndpointSlice resources that are managed by Karmada system to avoid duplicate reporting.
+	if helper.IsEndpointSliceManagedByKarmada(endpointSliceObj.GetLabels()) {
+		return nil
+	}
+
 	if err = c.reportEndpointSliceWithEndpointSliceCreateOrUpdate(ctx, endpointSliceKey.Cluster, endpointSliceObj); err != nil {
-		klog.Errorf("Failed to handle endpointSlice(%s) event, Error: %v",
-			endpointSliceKey.NamespaceKey(), err)
+		klog.ErrorS(err, "Failed to handle endpointSlice event", "namespace", endpointSliceKey.Namespace, "name", endpointSliceKey.Name)
 		return err
 	}
 
@@ -396,6 +408,8 @@ func (c *ServiceExportController) handleEndpointSliceEvent(ctx context.Context,
 }
 
 // reportEndpointSliceWithServiceExportCreate reports the referencing service's EndpointSlice.
+// The informer for EndpointSlice is created dynamically in Reconcile() when ServiceExport Works are detected.
+// If informer isn't synced yet, we return error to trigger retry.
 func (c *ServiceExportController) reportEndpointSliceWithServiceExportCreate(ctx context.Context, serviceExportKey keys.FederatedKey) error {
 	var (
 		endpointSliceObjects []runtime.Object
@@ -405,6 +419,8 @@ func (c *ServiceExportController) reportEndpointSliceWithServiceExportCreate(ctx
 
 	singleClusterManager := c.InformerManager.GetSingleClusterManager(serviceExportKey.Cluster)
 	if singleClusterManager == nil {
+		// No informer manager for this cluster yet - this shouldn't happen
+		// if Reconcile() has run, but handle gracefully
 		return nil
 	}
 
@@ -429,13 +445,20 @@ func (c *ServiceExportController) reportEndpointSliceWithServiceExportCreate(ctx
 	}
 
 	for index := range endpointSliceObjects {
-		if err = reportEndpointSlice(ctx, c.Client, endpointSliceObjects[index].(*unstructured.Unstructured), serviceExportKey.Cluster); err != nil {
+		endpointSlice := endpointSliceObjects[index].(*unstructured.Unstructured)
+		// Exclude EndpointSlice resources that are managed by Karmada system to avoid duplicate reporting.
+		if helper.IsEndpointSliceManagedByKarmada(endpointSlice.GetLabels()) {
+			continue
+		}
+
+		if err = reportEndpointSlice(ctx, c.Client, endpointSlice, serviceExportKey.Cluster); err != nil {
 			errs = append(errs, err)
 		}
 	}
 	return utilerrors.NewAggregate(errs)
 }
 
+// removeOrphanWork cleans up Work resources for EndpointSlices that no longer exist.
 func (c *ServiceExportController) removeOrphanWork(ctx context.Context, endpointSliceObjects []runtime.Object, serviceExportKey keys.FederatedKey) error {
 	willReportWorks := sets.NewString()
 	for index := range endpointSliceObjects {
@@ -453,8 +476,9 @@ func (c *ServiceExportController) removeOrphanWork(ctx context.Context, endpoint
 		}),
 		FieldSelector: fields.OneTermEqualSelector(indexregistry.WorkIndexByFieldSuspendDispatching, "true"),
 	}); err != nil {
-		klog.Errorf("Failed to list endpointslice work with serviceExport(%s/%s) under namespace %s: %v",
-			serviceExportKey.Namespace, serviceExportKey.Name, names.GenerateExecutionSpaceName(serviceExportKey.Cluster), err)
+		klog.ErrorS(err, "Failed to list workList reported by ServiceExport in executionSpace",
+			"namespace", serviceExportKey.Namespace, "name", serviceExportKey.Name, "executionSpace",
+			names.GenerateExecutionSpaceName(serviceExportKey.Cluster))
 		return err
 	}
 
@@ -483,6 +507,8 @@ func (c *ServiceExportController) removeOrphanWork(ctx context.Context, endpoint
 }
 
 // reportEndpointSliceWithEndpointSliceCreateOrUpdate reports the EndpointSlice when referencing service has been exported.
+// The informer for ServiceExport is created dynamically in Reconcile() when ServiceExport Works are detected.
+// If informer isn't synced yet, we return error to trigger retry.
 func (c *ServiceExportController) reportEndpointSliceWithEndpointSliceCreateOrUpdate(ctx context.Context, clusterName string, endpointSlice *unstructured.Unstructured) error {
 	relatedServiceName := endpointSlice.GetLabels()[discoveryv1.LabelServiceName]
 
@@ -506,7 +532,7 @@ func (c *ServiceExportController) reportEndpointSliceWithEndpointSliceCreateOrUp
 			return nil
 		}
 
-		klog.Errorf("Failed to get ServiceExport object %s/%s. error: %v.", relatedServiceName, endpointSlice.GetNamespace(), err)
+		klog.ErrorS(err, "Failed to get ServiceExport object", "namespace", endpointSlice.GetNamespace(), "name", relatedServiceName)
 		return err
 	}
 
@@ -538,14 +564,18 @@ func getEndpointSliceWorkMeta(ctx context.Context, c client.Client, ns string, w
 		Namespace: ns,
 		Name:      workName,
 	}, existWork); err != nil && !apierrors.IsNotFound(err) {
-		klog.Errorf("Get EndpointSlice work(%s/%s) error:%v", ns, workName, err)
+		klog.ErrorS(err, "Failed to get EndpointSlice work", "namespace", ns, "name", workName)
 		return metav1.ObjectMeta{}, err
 	}
 
+	existFinalizers := existWork.GetFinalizers()
+	finalizersToAdd := []string{util.EndpointSliceControllerFinalizer}
+	newFinalizers := util.MergeFinalizers(existFinalizers, finalizersToAdd)
+
 	workMeta := metav1.ObjectMeta{
 		Name:       workName,
 		Namespace:  ns,
-		Finalizers: []string{util.EndpointSliceControllerFinalizer},
+		Finalizers: newFinalizers,
 		Labels: map[string]string{
 			util.ServiceNamespaceLabel:           endpointSlice.GetNamespace(),
 			util.ServiceNameLabel:                endpointSlice.GetLabels()[discoveryv1.LabelServiceName],
@@ -578,8 +608,8 @@ func cleanupWorkWithServiceExportDelete(ctx context.Context, c client.Client, se
 			util.ServiceNameLabel:      serviceExportKey.Name,
 		}),
 	}); err != nil {
-		klog.Errorf("Failed to list workList reported by ServiceExport(%s) in executionSpace(%s), Error: %v",
-			serviceExportKey.NamespaceKey(), executionSpace, err)
+		klog.ErrorS(err, "Failed to list workList reported by ServiceExport in executionSpace",
+			"namespace", serviceExportKey.Namespace, "name", serviceExportKey.Name, "executionSpace", executionSpace)
 		return err
 	}
 
@@ -605,7 +635,7 @@ func cleanupWorkWithEndpointSliceDelete(ctx context.Context, c client.Client, en
 			return nil
 		}
 
-		klog.Errorf("Failed to get work(%s) in executionSpace(%s), Error: %v", workNamespaceKey.String(), executionSpace, err)
+		klog.ErrorS(err, "Failed to get work in executionSpace", "work", workNamespaceKey.String(), "executionSpace", executionSpace)
 		return err
 	}
 
@@ -627,7 +657,7 @@ func cleanEndpointSliceWork(ctx context.Context, c client.Client, work *workv1al
 		work.Labels[util.EndpointSliceWorkManagedByLabel] = strings.Join(controllerSet.UnsortedList(), ".")
 
 		if err := c.Update(ctx, work); err != nil {
-			klog.Errorf("Failed to update work(%s/%s): %v", work.Namespace, work.Name, err)
+			klog.ErrorS(err, "Failed to update work", "namespace", work.Namespace, "workName", work.Name)
 			return err
 		}
 		klog.Infof("Successfully updated work(%s/%s)", work.Namespace, work.Name)
@@ -635,7 +665,7 @@ func cleanEndpointSliceWork(ctx context.Context, c client.Client, work *workv1al
 	}
 
 	if err := c.Delete(ctx, work); err != nil {
-		klog.Errorf("Failed to delete work(%s/%s), Error: %v", work.Namespace, work.Name, err)
+		klog.ErrorS(err, "Failed to delete work", "namespace", work.Namespace, "workName", work.Name)
 		return err
 	}
 	klog.Infof("Successfully deleted work(%s/%s)", work.Namespace, work.Name)
diff --git a/pkg/controllers/mcs/service_import_controller.go b/pkg/controllers/mcs/service_import_controller.go
index 5523a04cc..30c2fc25a 100644
--- a/pkg/controllers/mcs/service_import_controller.go
+++ b/pkg/controllers/mcs/service_import_controller.go
@@ -49,7 +49,7 @@ type ServiceImportController struct {
 
 // Reconcile performs a full reconciliation for the object referred to by the Request.
 func (c *ServiceImportController) Reconcile(ctx context.Context, req controllerruntime.Request) (controllerruntime.Result, error) {
-	klog.V(4).Infof("Reconciling ServiceImport %s.", req.NamespacedName.String())
+	klog.V(4).InfoS("Reconciling ServiceImport", "name", req.NamespacedName.String())
 
 	svcImport := &mcsv1alpha1.ServiceImport{}
 	if err := c.Client.Get(ctx, req.NamespacedName, svcImport); err != nil {
@@ -98,7 +98,7 @@ func (c *ServiceImportController) deleteDerivedService(ctx context.Context, svcI
 
 	err = c.Client.Delete(ctx, derivedSvc)
 	if err != nil && !apierrors.IsNotFound(err) {
-		klog.Errorf("Delete derived service(%s) failed, Error: %v", derivedSvcNamespacedName, err)
+		klog.ErrorS(err, "Delete derived service failed", "name", derivedSvcNamespacedName)
 		return controllerruntime.Result{}, err
 	}
 
@@ -125,7 +125,7 @@ func (c *ServiceImportController) deriveServiceFromServiceImport(ctx context.Con
 	if err != nil {
 		if apierrors.IsNotFound(err) {
 			if err = c.Client.Create(ctx, newDerivedService); err != nil {
-				klog.Errorf("Create derived service(%s/%s) failed, Error: %v", newDerivedService.Namespace, newDerivedService.Name, err)
+				klog.ErrorS(err, "Create derived service failed", "namespace", newDerivedService.Namespace, "name", newDerivedService.Name)
 				return err
 			}
 
@@ -140,7 +140,7 @@ func (c *ServiceImportController) deriveServiceFromServiceImport(ctx context.Con
 
 	err = c.Client.Update(ctx, newDerivedService)
 	if err != nil {
-		klog.Errorf("Update derived service(%s/%s) failed, Error: %v", newDerivedService.Namespace, newDerivedService.Name, err)
+		klog.ErrorS(err, "Update derived service failed", "namespace", newDerivedService.Namespace, "name", newDerivedService.Name)
 		return err
 	}
 
@@ -169,7 +169,7 @@ func (c *ServiceImportController) updateServiceStatus(ctx context.Context, svcIm
 	})
 
 	if err != nil {
-		klog.Errorf("Update derived service(%s/%s) status failed, Error: %v", derivedService.Namespace, derivedService.Name, err)
+		klog.ErrorS(err, "Update derived service status failed", "namespace", derivedService.Namespace, "name", derivedService.Name)
 		return err
 	}
 	return nil
diff --git a/pkg/controllers/multiclusterservice/endpointslice_collect_controller.go b/pkg/controllers/multiclusterservice/endpointslice_collect_controller.go
index 5a67a0d10..307cd17ba 100644
--- a/pkg/controllers/multiclusterservice/endpointslice_collect_controller.go
+++ b/pkg/controllers/multiclusterservice/endpointslice_collect_controller.go
@@ -224,7 +224,6 @@ func (c *EndpointSliceCollectController) registerInformersAndStart(cluster *clus
 		return nil
 	}(); err != nil {
 		klog.ErrorS(err, "Failed to sync cache for cluster", "cluster", cluster.Name)
-		c.InformerManager.Stop(cluster.Name)
 		return err
 	}
 
@@ -300,7 +299,8 @@ func (c *EndpointSliceCollectController) handleEndpointSliceEvent(ctx context.Co
 		return err
 	}
 
-	if util.GetLabelValue(endpointSliceObj.GetLabels(), discoveryv1.LabelManagedBy) == util.EndpointSliceDispatchControllerLabelValue {
+	// Exclude EndpointSlice resources that are managed by Karmada system to avoid duplicate reporting.
+	if helper.IsEndpointSliceManagedByKarmada(endpointSliceObj.GetLabels()) {
 		return nil
 	}
 
@@ -359,7 +359,8 @@ func (c *EndpointSliceCollectController) collectTargetEndpointSlice(ctx context.
 			klog.ErrorS(err, "Failed to convert object to EndpointSlice")
 			return err
 		}
-		if util.GetLabelValue(eps.GetLabels(), discoveryv1.LabelManagedBy) == util.EndpointSliceDispatchControllerLabelValue {
+		// Exclude EndpointSlice resources that are managed by Karmada system to avoid duplicate reporting.
+		if helper.IsEndpointSliceManagedByKarmada(eps.GetLabels()) {
 			continue
 		}
 		epsUnstructured, err := helper.ToUnstructured(eps)
@@ -415,13 +416,22 @@ func getEndpointSliceWorkMeta(ctx context.Context, c client.Client, ns string, w
 		return metav1.ObjectMeta{}, err
 	}
 
+	existFinalizers := existWork.GetFinalizers()
+	finalizersToAdd := []string{util.MCSEndpointSliceDispatchControllerFinalizer}
+	newFinalizers := util.MergeFinalizers(existFinalizers, finalizersToAdd)
+
 	ls := map[string]string{
 		util.MultiClusterServiceNamespaceLabel: endpointSlice.GetNamespace(),
 		util.MultiClusterServiceNameLabel:      endpointSlice.GetLabels()[discoveryv1.LabelServiceName],
 		util.EndpointSliceWorkManagedByLabel:   util.MultiClusterServiceKind,
 	}
 	if existWork.Labels == nil || (err != nil && apierrors.IsNotFound(err)) {
-		workMeta := metav1.ObjectMeta{Name: workName, Namespace: ns, Labels: ls}
+		workMeta := metav1.ObjectMeta{
+			Name:       workName,
+			Namespace:  ns,
+			Labels:     ls,
+			Finalizers: newFinalizers,
+		}
 		return workMeta, nil
 	}
 
@@ -436,7 +446,7 @@ func getEndpointSliceWorkMeta(ctx context.Context, c client.Client, ns string, w
 		Name:       workName,
 		Namespace:  ns,
 		Labels:     ls,
-		Finalizers: []string{util.MCSEndpointSliceDispatchControllerFinalizer},
+		Finalizers: newFinalizers,
 	}, nil
 }
 
diff --git a/pkg/controllers/status/cluster_status_controller.go b/pkg/controllers/status/cluster_status_controller.go
index 52126e0eb..0a7dc22fd 100644
--- a/pkg/controllers/status/cluster_status_controller.go
+++ b/pkg/controllers/status/cluster_status_controller.go
@@ -390,7 +390,6 @@ func (c *ClusterStatusController) buildInformerForCluster(clusterClient *util.Cl
 		return nil
 	}(); err != nil {
 		klog.ErrorS(err, "Failed to sync cache for cluster", "cluster", clusterClient.ClusterName)
-		c.TypedInformerManager.Stop(clusterClient.ClusterName)
 		return nil, err
 	}
 
diff --git a/pkg/controllers/status/work_status_controller.go b/pkg/controllers/status/work_status_controller.go
index c6ed46459..f7174cbc5 100644
--- a/pkg/controllers/status/work_status_controller.go
+++ b/pkg/controllers/status/work_status_controller.go
@@ -498,7 +498,6 @@ func (c *WorkStatusController) registerInformersAndStart(cluster *clusterv1alpha
 		return nil
 	}(); err != nil {
 		klog.ErrorS(err, "Failed to sync cache for cluster", "cluster", cluster.Name)
-		c.InformerManager.Stop(cluster.Name)
 		return err
 	}
 
diff --git a/pkg/detector/detector.go b/pkg/detector/detector.go
index 6293315a0..bba2303cb 100644
--- a/pkg/detector/detector.go
+++ b/pkg/detector/detector.go
@@ -657,9 +657,6 @@ func (d *ResourceDetector) ApplyClusterPolicy(object *unstructured.Unstructured,
 }
 
 // GetUnstructuredObject retrieves object by key and returned its unstructured.
-// Any updates to this resource template are not recommended as it may come from the informer cache.
-// We should abide by the principle of making a deep copy first and then modifying it.
-// See issue: https://github.com/karmada-io/karmada/issues/3878.
 func (d *ResourceDetector) GetUnstructuredObject(objectKey keys.ClusterWideKey) (*unstructured.Unstructured, error) {
 	objectGVR, err := restmapper.GetGroupVersionResource(d.RESTMapper, objectKey.GroupVersionKind())
 	if err != nil {
@@ -689,7 +686,66 @@ func (d *ResourceDetector) GetUnstructuredObject(objectKey keys.ClusterWideKey)
 		return nil, err
 	}
 
-	return unstructuredObj, nil
+	// perform a deep copy to avoid modifying the cached object from informer
+	return unstructuredObj.DeepCopy(), nil
+}
+
+// fetchResourceBinding fetches a ResourceBinding from the client or dynamic client.
+func (d *ResourceDetector) fetchResourceBinding(ctx context.Context, rbNamespace, rbName string) (*workv1alpha2.ResourceBinding, error) {
+	// First try to get ResourceBinding using cached client
+	rb := &workv1alpha2.ResourceBinding{}
+	err := d.Client.Get(ctx, client.ObjectKey{Namespace: rbNamespace, Name: rbName}, rb)
+	if err != nil {
+		if apierrors.IsNotFound(err) {
+			// If not found with client, try using dynamic client
+			gvr := workv1alpha2.SchemeGroupVersion.WithResource(workv1alpha2.ResourcePluralResourceBinding)
+			unstructuredRB, dynamicErr := d.DynamicClient.Resource(gvr).Namespace(rbNamespace).Get(ctx, rbName, metav1.GetOptions{})
+			if dynamicErr != nil {
+				return nil, dynamicErr
+			}
+
+			// Convert unstructured to ResourceBinding
+			if err = helper.ConvertToTypedObject(unstructuredRB, rb); err != nil {
+				klog.Errorf("Failed to convert unstructured to ResourceBinding(%s/%s): %v", rbNamespace, rbName, err)
+				return nil, err
+			}
+
+			return rb, nil
+		}
+		klog.Errorf("Failed to get ResourceBinding(%s/%s): %v", rbNamespace, rbName, err)
+		return nil, err
+	}
+
+	return rb, nil
+}
+
+// fetchClusterResourceBinding fetches a ClusterResourceBinding from the client or dynamic client.
+func (d *ResourceDetector) fetchClusterResourceBinding(ctx context.Context, crbName string) (*workv1alpha2.ClusterResourceBinding, error) {
+	// First try to get ClusterResourceBinding using cached client
+	crb := &workv1alpha2.ClusterResourceBinding{}
+	err := d.Client.Get(ctx, client.ObjectKey{Name: crbName}, crb)
+	if err != nil {
+		if apierrors.IsNotFound(err) {
+			// If not found with client, try using dynamic client
+			gvr := workv1alpha2.SchemeGroupVersion.WithResource(workv1alpha2.ResourcePluralClusterResourceBinding)
+			unstructuredRB, dynamicErr := d.DynamicClient.Resource(gvr).Get(ctx, crbName, metav1.GetOptions{})
+			if dynamicErr != nil {
+				return nil, dynamicErr
+			}
+
+			// Convert unstructured to ClusterResourceBinding
+			if err = helper.ConvertToTypedObject(unstructuredRB, crb); err != nil {
+				klog.Errorf("Failed to convert unstructured to ClusterResourceBinding(%s): %v", crbName, err)
+				return nil, err
+			}
+
+			return crb, nil
+		}
+		klog.Errorf("Failed to get ClusterResourceBinding(%s): %v", crbName, err)
+		return nil, err
+	}
+
+	return crb, nil
 }
 
 // ClaimPolicyForObject set policy identifier which the object associated with.
@@ -761,14 +817,8 @@ func (d *ResourceDetector) BuildResourceBinding(object *unstructured.Unstructure
 
 	claimFunc(propagationBinding, policyID, policyMeta)
 
-	if d.ResourceInterpreter.HookEnabled(object.GroupVersionKind(), configv1alpha1.InterpreterOperationInterpretReplica) {
-		replicas, replicaRequirements, err := d.ResourceInterpreter.GetReplicas(object)
-		if err != nil {
-			klog.Errorf("Failed to customize replicas for %s(%s), %v", object.GroupVersionKind(), object.GetName(), err)
-			return nil, err
-		}
-		propagationBinding.Spec.Replicas = replicas
-		propagationBinding.Spec.ReplicaRequirements = replicaRequirements
+	if err := d.applyReplicaInterpretation(object, &propagationBinding.Spec); err != nil {
+		return nil, err
 	}
 
 	if features.FeatureGate.Enabled(features.PriorityBasedScheduling) && policySpec.SchedulePriority != nil {
@@ -835,14 +885,8 @@ func (d *ResourceDetector) BuildClusterResourceBinding(object *unstructured.Unst
 
 	AddCPPClaimMetadata(binding, policyID, policyMeta)
 
-	if d.ResourceInterpreter.HookEnabled(object.GroupVersionKind(), configv1alpha1.InterpreterOperationInterpretReplica) {
-		replicas, replicaRequirements, err := d.ResourceInterpreter.GetReplicas(object)
-		if err != nil {
-			klog.Errorf("Failed to customize replicas for %s(%s), %v", object.GroupVersionKind(), object.GetName(), err)
-			return nil, err
-		}
-		binding.Spec.Replicas = replicas
-		binding.Spec.ReplicaRequirements = replicaRequirements
+	if err := d.applyReplicaInterpretation(object, &binding.Spec); err != nil {
+		return nil, err
 	}
 
 	return binding, nil
@@ -954,7 +998,7 @@ func (d *ResourceDetector) ReconcilePropagationPolicy(key util.QueueKey) error {
 
 	if !propagationObject.DeletionTimestamp.IsZero() {
 		klog.Infof("PropagationPolicy(%s) is being deleted.", nkey.NamespaceKey())
-		if err = d.HandlePropagationPolicyDeletion(propagationObject.Labels[policyv1alpha1.PropagationPolicyPermanentIDLabel]); err != nil {
+		if err = d.HandlePropagationPolicyDeletion(propagationObject.Labels[policyv1alpha1.PropagationPolicyPermanentIDLabel], propagationObject.Spec.ResourceSelectors); err != nil {
 			return err
 		}
 		if controllerutil.RemoveFinalizer(propagationObject, util.PropagationPolicyControllerFinalizer) {
@@ -1026,7 +1070,7 @@ func (d *ResourceDetector) ReconcileClusterPropagationPolicy(key util.QueueKey)
 
 	if !propagationObject.DeletionTimestamp.IsZero() {
 		klog.Infof("ClusterPropagationPolicy(%s) is being deleted.", nkey.NamespaceKey())
-		if err = d.HandleClusterPropagationPolicyDeletion(propagationObject.Labels[policyv1alpha1.ClusterPropagationPolicyPermanentIDLabel]); err != nil {
+		if err = d.HandleClusterPropagationPolicyDeletion(propagationObject.Labels[policyv1alpha1.ClusterPropagationPolicyPermanentIDLabel], propagationObject.Spec.ResourceSelectors); err != nil {
 			return err
 		}
 		if controllerutil.RemoveFinalizer(propagationObject, util.ClusterPropagationPolicyControllerFinalizer) {
@@ -1047,32 +1091,26 @@ func (d *ResourceDetector) ReconcileClusterPropagationPolicy(key util.QueueKey)
 // the resource template a change to match another policy).
 //
 // Note: The relevant ResourceBinding will continue to exist until the resource template is gone.
-func (d *ResourceDetector) HandlePropagationPolicyDeletion(policyID string) error {
+func (d *ResourceDetector) HandlePropagationPolicyDeletion(policyID string, resources []policyv1alpha1.ResourceSelector) error {
 	claimMetadata := labels.Set{policyv1alpha1.PropagationPolicyPermanentIDLabel: policyID}
-	rbs, err := helper.GetResourceBindings(d.Client, claimMetadata)
-	if err != nil {
-		klog.Errorf("Failed to list propagation bindings with policy permanentID(%s): %v", policyID, err)
-		return err
-	}
-
 	var errs []error
-	for index, binding := range rbs.Items {
-		// Must remove the claim metadata, such as labels and annotations, from the resource template ahead of ResourceBinding,
-		// otherwise might lose the chance to do that in a retry loop (in particular, the claim metadata was successfully removed
-		// from ResourceBinding, but resource template not), since the ResourceBinding will not be listed again.
-		if err := d.CleanupResourceTemplateClaimMetadata(binding.Spec.Resource, claimMetadata, CleanupPPClaimMetadata); err != nil {
-			klog.Errorf("Failed to clean up claim metadata from resource(%s-%s/%s) when propagationPolicy removed, error: %v",
-				binding.Spec.Resource.Kind, binding.Spec.Resource.Namespace, binding.Spec.Resource.Name, err)
-			errs = append(errs, err)
-			// Skip cleaning up policy labels and annotations from ResourceBinding, give a chance to do that in a retry loop.
-			continue
+	for _, resource := range util.ExtractUniqueNamespacedSelectors(resources) {
+		objRef := workv1alpha2.ObjectReference{
+			APIVersion: resource.APIVersion,
+			Kind:       resource.Kind,
+			Namespace:  resource.Namespace,
 		}
 
-		// Clean up the claim metadata from the reference binding so that the karmada scheduler won't reschedule the binding.
-		if err := d.CleanupResourceBindingClaimMetadata(&rbs.Items[index], claimMetadata, CleanupPPClaimMetadata); err != nil {
-			klog.Errorf("Failed to clean up claim metadata from resource binding(%s/%s) when propagationPolicy removed, error: %v",
-				binding.Namespace, binding.Name, err)
+		rawObjects, err := helper.FetchResourceTemplatesByLabelSelector(d.DynamicClient, d.InformerManager, d.RESTMapper, objRef, labels.SelectorFromSet(claimMetadata))
+		if err != nil {
 			errs = append(errs, err)
+			continue
+		}
+		for _, rawObject := range rawObjects {
+			err := d.handleResourceTemplateAndBindingCleanup(rawObject, objRef, claimMetadata, CleanupPPClaimMetadata)
+			if err != nil {
+				errs = append(errs, err)
+			}
 		}
 	}
 	return errors.NewAggregate(errs)
@@ -1083,66 +1121,64 @@ func (d *ResourceDetector) HandlePropagationPolicyDeletion(policyID string) erro
 // the resource template a change to match another policy).
 //
 // Note: The relevant ClusterResourceBinding or ResourceBinding will continue to exist until the resource template is gone.
-func (d *ResourceDetector) HandleClusterPropagationPolicyDeletion(policyID string) error {
+func (d *ResourceDetector) HandleClusterPropagationPolicyDeletion(policyID string, resources []policyv1alpha1.ResourceSelector) error {
 	var errs []error
-	labelSet := labels.Set{
-		policyv1alpha1.ClusterPropagationPolicyPermanentIDLabel: policyID,
-	}
+	claimMetadata := labels.Set{policyv1alpha1.ClusterPropagationPolicyPermanentIDLabel: policyID}
 
-	// load the ClusterResourceBindings which labeled with current policy
-	crbs, err := helper.GetClusterResourceBindings(d.Client, labelSet)
-	if err != nil {
-		klog.Errorf("Failed to list clusterResourceBindings with clusterPropagationPolicy permanentID(%s), error: %v", policyID, err)
-		errs = append(errs, err)
-	} else if len(crbs.Items) > 0 {
-		for index, binding := range crbs.Items {
-			// Must remove the claim metadata, such as labels and annotations, from the resource template ahead of
-			// ClusterResourceBinding, otherwise might lose the chance to do that in a retry loop (in particular, the
-			// claim metadata was successfully removed from ClusterResourceBinding, but resource template not), since the
-			// ClusterResourceBinding will not be listed again.
-			if err := d.CleanupResourceTemplateClaimMetadata(binding.Spec.Resource, labelSet, CleanupCPPClaimMetadata); err != nil {
-				klog.Errorf("Failed to clean up claim metadata from resource(%s-%s) when clusterPropagationPolicy removed, error: %v",
-					binding.Spec.Resource.Kind, binding.Spec.Resource.Name, err)
-				// Skip cleaning up policy labels and annotations from ClusterResourceBinding, give a chance to do that in a retry loop.
-				continue
-			}
+	for _, resource := range util.ExtractUniqueNamespacedSelectors(resources) {
+		objRef := workv1alpha2.ObjectReference{
+			APIVersion: resource.APIVersion,
+			Kind:       resource.Kind,
+			Namespace:  resource.Namespace,
+		}
 
-			// Clean up the claim metadata from the reference binding so that the Karmada scheduler won't reschedule the binding.
-			if err := d.CleanupClusterResourceBindingClaimMetadata(&crbs.Items[index], labelSet); err != nil {
-				klog.Errorf("Failed to clean up claim metadata from clusterResourceBinding(%s) when clusterPropagationPolicy removed, error: %v",
-					binding.Name, err)
+		rawObjects, err := helper.FetchResourceTemplatesByLabelSelector(d.DynamicClient, d.InformerManager, d.RESTMapper, objRef, labels.SelectorFromSet(claimMetadata))
+		if err != nil {
+			errs = append(errs, err)
+			continue
+		}
+		for _, rawObject := range rawObjects {
+			err := d.handleResourceTemplateAndBindingCleanup(rawObject, objRef, claimMetadata, CleanupCPPClaimMetadata)
+			if err != nil {
 				errs = append(errs, err)
 			}
 		}
 	}
 
-	// load the ResourceBindings which labeled with current policy
-	rbs, err := helper.GetResourceBindings(d.Client, labelSet)
-	if err != nil {
-		klog.Errorf("Failed to list resourceBindings with clusterPropagationPolicy permanentID(%s), error: %v", policyID, err)
-		errs = append(errs, err)
-	} else if len(rbs.Items) > 0 {
-		for index, binding := range rbs.Items {
-			// Must remove the claim metadata, such as labels and annotations, from the resource template ahead of ResourceBinding,
-			// otherwise might lose the chance to do that in a retry loop (in particular, the label was successfully
-			// removed from ResourceBinding, but resource template not), since the ResourceBinding will not be listed again.
-			if err := d.CleanupResourceTemplateClaimMetadata(binding.Spec.Resource, labelSet, CleanupCPPClaimMetadata); err != nil {
-				klog.Errorf("Failed to clean up claim metadata from resource(%s-%s/%s) when clusterPropagationPolicy removed, error: %v",
-					binding.Spec.Resource.Kind, binding.Spec.Resource.Namespace, binding.Spec.Resource.Name, err)
-				errs = append(errs, err)
-				// Skip cleaning up policy labels and annotations from ResourceBinding, give a chance to do that in a retry loop.
-				continue
-			}
+	return errors.NewAggregate(errs)
+}
 
-			// Clean up the claim metadata from the reference binding so that the Karmada scheduler won't reschedule the binding.
-			if err := d.CleanupResourceBindingClaimMetadata(&rbs.Items[index], labelSet, CleanupCPPClaimMetadata); err != nil {
-				klog.Errorf("Failed to clean up claim metadata from resourceBinding(%s/%s) when clusterPropagationPolicy removed, error: %v",
-					binding.Namespace, binding.Name, err)
-				errs = append(errs, err)
-			}
+func (d *ResourceDetector) handleResourceTemplateAndBindingCleanup(template *unstructured.Unstructured, objRef workv1alpha2.ObjectReference, targetClaimMetadata map[string]string, cleanupFunc func(obj metav1.Object)) error {
+	bindingName := names.GenerateBindingName(template.GetKind(), template.GetName())
+	if template.GetNamespace() != "" {
+		// Clean up the claim metadata from the reference binding so that the karmada scheduler won't reschedule the binding.
+		// Must remove the claim metadata, such as labels and annotations, from the ResourceBinding ahead of resource template,
+		// otherwise might lose the chance to do that in a retry loop (in particular, the claim metadata was successfully removed
+		// from resource template, but ResourceBinding not), since the resource template will not be listed again.
+		if err := d.CleanupResourceBindingClaimMetadata(template.GetNamespace(), bindingName, targetClaimMetadata, cleanupFunc); err != nil {
+			klog.Errorf("Failed to clean up claim metadata from ResourceBinding(%s/%s), error: %v",
+				objRef.Namespace, bindingName, err)
+			return err
+		}
+	} else {
+		// Clean up the claim metadata from the reference binding so that the Karmada scheduler won't reschedule the binding.
+		// Must remove the claim metadata, such as labels and annotations, from the ClusterResourceBinding ahead of resource template,
+		// otherwise might lose the chance to do that in a retry loop (in particular, the claim metadata was successfully removed
+		// from resource template, but ClusterResourceBinding not), since the resource template will not be listed again.
+		if err := d.CleanupClusterResourceBindingClaimMetadata(bindingName, targetClaimMetadata, cleanupFunc); err != nil {
+			klog.Errorf("Failed to clean up claim metadata from ClusterResourceBinding(%s), error: %v",
+				bindingName, err)
+			return err
 		}
 	}
-	return errors.NewAggregate(errs)
+
+	if err := d.CleanupResourceTemplateClaimMetadata(template, objRef, targetClaimMetadata, cleanupFunc); err != nil {
+		klog.Errorf("Failed to clean up claim metadata from resource(%s-%s/%s) when propagationPolicy removed, error: %v",
+			template.GetKind(), template.GetNamespace(), template.GetName(), err)
+		return err
+	}
+
+	return nil
 }
 
 // HandlePropagationPolicyCreationOrUpdate handles PropagationPolicy add and update event.
@@ -1277,24 +1313,14 @@ func (d *ResourceDetector) HandleClusterPropagationPolicyCreationOrUpdate(policy
 }
 
 // CleanupResourceTemplateClaimMetadata removes claim metadata, such as labels and annotations, from object referencing by objRef.
-func (d *ResourceDetector) CleanupResourceTemplateClaimMetadata(objRef workv1alpha2.ObjectReference, targetClaimMetadata map[string]string, cleanupFunc func(obj metav1.Object)) error {
+func (d *ResourceDetector) CleanupResourceTemplateClaimMetadata(obj *unstructured.Unstructured, objRef workv1alpha2.ObjectReference, targetClaimMetadata map[string]string, cleanupFunc func(obj metav1.Object)) error {
 	gvr, err := restmapper.GetGroupVersionResource(d.RESTMapper, schema.FromAPIVersionAndKind(objRef.APIVersion, objRef.Kind))
 	if err != nil {
 		klog.Errorf("Failed to convert GVR from GVK(%s/%s), err: %v", objRef.APIVersion, objRef.Kind, err)
 		return err
 	}
-
+	workload := obj.DeepCopy()
 	return retry.RetryOnConflict(retry.DefaultRetry, func() (err error) {
-		workload, err := d.DynamicClient.Resource(gvr).Namespace(objRef.Namespace).Get(context.TODO(), objRef.Name, metav1.GetOptions{})
-		if err != nil {
-			// do nothing if resource template not exist, it might have been removed.
-			if apierrors.IsNotFound(err) {
-				return nil
-			}
-			klog.Errorf("Failed to fetch resource(kind=%s, %s/%s): err is %v", objRef.Kind, objRef.Namespace, objRef.Name, err)
-			return err
-		}
-
 		if !NeedCleanupClaimMetadata(workload, targetClaimMetadata) {
 			klog.Infof("No need to clean up the claim metadata on resource(kind=%s, %s/%s) since they have changed", workload.GetKind(), workload.GetNamespace(), workload.GetName())
 			return nil
@@ -1305,15 +1331,37 @@ func (d *ResourceDetector) CleanupResourceTemplateClaimMetadata(objRef workv1alp
 		_, err = d.DynamicClient.Resource(gvr).Namespace(workload.GetNamespace()).Update(context.TODO(), workload, metav1.UpdateOptions{})
 		if err != nil {
 			klog.Errorf("Failed to update resource(kind=%s, %s/%s): err is %v", workload.GetKind(), workload.GetNamespace(), workload.GetName(), err)
+			if apierrors.IsConflict(err) {
+				newWorkload, getErr := d.DynamicClient.Resource(gvr).Namespace(workload.GetNamespace()).Get(context.TODO(), workload.GetName(), metav1.GetOptions{})
+				if getErr != nil {
+					// do nothing if resource template not exist, it might have been removed.
+					if apierrors.IsNotFound(getErr) {
+						return nil
+					}
+					return getErr
+				}
+				workload = newWorkload
+			}
 			return err
 		}
-		klog.V(2).Infof("Updated resource template(kind=%s, %s/%s) successfully", workload.GetKind(), workload.GetNamespace(), workload.GetName())
+		klog.V(2).Infof("Clean claimed label for resource template(kind=%s, %s/%s) successfully", workload.GetKind(), workload.GetNamespace(), workload.GetName())
 		return nil
 	})
 }
 
 // CleanupResourceBindingClaimMetadata removes claim metadata, such as labels and annotations, from resource binding.
-func (d *ResourceDetector) CleanupResourceBindingClaimMetadata(rb *workv1alpha2.ResourceBinding, targetClaimMetadata map[string]string, cleanupFunc func(obj metav1.Object)) error {
+func (d *ResourceDetector) CleanupResourceBindingClaimMetadata(rbNamespace, rbName string, targetClaimMetadata map[string]string, cleanupFunc func(obj metav1.Object)) error {
+	var rb *workv1alpha2.ResourceBinding
+	var err error
+
+	rb, err = d.fetchResourceBinding(context.TODO(), rbNamespace, rbName)
+	if err != nil {
+		if apierrors.IsNotFound(err) {
+			return nil // do nothing if resource binding not exist, it might have been removed.
+		}
+		return err
+	}
+
 	return retry.RetryOnConflict(retry.DefaultRetry, func() (err error) {
 		if !NeedCleanupClaimMetadata(rb, targetClaimMetadata) {
 			klog.Infof("No need to clean up the claim metadata on ResourceBinding(%s/%s) since they have changed", rb.GetNamespace(), rb.GetName())
@@ -1325,35 +1373,102 @@ func (d *ResourceDetector) CleanupResourceBindingClaimMetadata(rb *workv1alpha2.
 			return nil
 		}
 
-		updated := &workv1alpha2.ResourceBinding{}
-		if err = d.Client.Get(context.TODO(), client.ObjectKey{Namespace: rb.GetNamespace(), Name: rb.GetName()}, updated); err == nil {
-			rb = updated.DeepCopy()
-		} else {
-			klog.Errorf("Failed to get updated ResourceBinding(%s/%s): %v", rb.GetNamespace(), rb.GetName(), err)
+		if apierrors.IsConflict(updateErr) {
+			updated := &workv1alpha2.ResourceBinding{}
+			gvr := workv1alpha2.SchemeGroupVersion.WithResource(workv1alpha2.ResourcePluralResourceBinding)
+			if unstructuredRB, dynamicErr := d.DynamicClient.Resource(gvr).Namespace(rbNamespace).Get(context.TODO(), rbName, metav1.GetOptions{}); dynamicErr == nil {
+				// Convert unstructured to ResourceBinding
+				if convertErr := helper.ConvertToTypedObject(unstructuredRB, updated); convertErr != nil {
+					klog.Errorf("Failed to convert unstructured to ResourceBinding(%s/%s): %v", rbNamespace, rbName, convertErr)
+					return convertErr
+				}
+				rb = updated
+			} else {
+				if apierrors.IsNotFound(dynamicErr) {
+					return nil // do nothing if resource binding not exist, it might have been removed.
+				}
+				klog.Errorf("Failed to get updated ResourceBinding(%s/%s): %v", rbNamespace, rbName, dynamicErr)
+				return dynamicErr
+			}
 		}
+
 		return updateErr
 	})
 }
 
 // CleanupClusterResourceBindingClaimMetadata removes claim metadata, such as labels and annotations, from cluster resource binding.
-func (d *ResourceDetector) CleanupClusterResourceBindingClaimMetadata(crb *workv1alpha2.ClusterResourceBinding, targetClaimMetadata map[string]string) error {
+func (d *ResourceDetector) CleanupClusterResourceBindingClaimMetadata(crbName string, targetClaimMetadata map[string]string, cleanupFunc func(obj metav1.Object)) error {
+	var crb *workv1alpha2.ClusterResourceBinding
+	var err error
+
+	crb, err = d.fetchClusterResourceBinding(context.TODO(), crbName)
+	if err != nil {
+		if apierrors.IsNotFound(err) {
+			return nil // do nothing if resource binding not exist, it might have been removed.
+		}
+		return err
+	}
+
 	return retry.RetryOnConflict(retry.DefaultRetry, func() (err error) {
 		if !NeedCleanupClaimMetadata(crb, targetClaimMetadata) {
 			klog.Infof("No need to clean up the claim metadata on ClusterResourceBinding(%s) since they have changed", crb.GetName())
 			return nil
 		}
-		CleanupCPPClaimMetadata(crb)
+		cleanupFunc(crb)
 		updateErr := d.Client.Update(context.TODO(), crb)
 		if updateErr == nil {
 			return nil
 		}
 
-		updated := &workv1alpha2.ClusterResourceBinding{}
-		if err = d.Client.Get(context.TODO(), client.ObjectKey{Name: crb.GetName()}, updated); err == nil {
-			crb = updated.DeepCopy()
-		} else {
-			klog.Errorf("Failed to get updated ClusterResourceBinding(%s):: %v", crb.GetName(), err)
+		if apierrors.IsConflict(updateErr) {
+			updated := &workv1alpha2.ClusterResourceBinding{}
+			gvr := workv1alpha2.SchemeGroupVersion.WithResource(workv1alpha2.ResourcePluralClusterResourceBinding)
+			if unstructuredRB, dynamicErr := d.DynamicClient.Resource(gvr).Get(context.TODO(), crbName, metav1.GetOptions{}); dynamicErr == nil {
+				// Convert unstructured to ClusterResourceBinding
+				if convertErr := helper.ConvertToTypedObject(unstructuredRB, updated); convertErr != nil {
+					klog.Errorf("Failed to convert unstructured to ClusterResourceBinding(%s): %v", crbName, convertErr)
+					return convertErr
+				}
+				crb = updated
+			} else {
+				if apierrors.IsNotFound(dynamicErr) {
+					return nil // do nothing if resource binding not exist, it might have been removed.
+				}
+				klog.Errorf("Failed to get updated ClusterResourceBinding(%s): %v", crbName, dynamicErr)
+				return dynamicErr
+			}
 		}
+
 		return updateErr
 	})
 }
+
+// applyReplicaInterpretation handles the logic for interpreting replicas or components from an object.
+func (d *ResourceDetector) applyReplicaInterpretation(object *unstructured.Unstructured, spec *workv1alpha2.ResourceBindingSpec) error {
+	gvk := object.GroupVersionKind()
+	name := object.GetName()
+
+	// Prioritize component interpretation if the feature and GetComponents are enabled.
+	if features.FeatureGate.Enabled(features.MultiplePodTemplatesScheduling) && d.ResourceInterpreter.HookEnabled(gvk, configv1alpha1.InterpreterOperationInterpretComponent) {
+		components, err := d.ResourceInterpreter.GetComponents(object)
+		if err != nil {
+			klog.Errorf("Failed to get components for %s(%s): %v", gvk, name, err)
+			return err
+		}
+		spec.Components = components
+		return nil
+	}
+
+	// GetReplicas is executed if the MultiplePodTemplatesScheduling feature gate is disabled, or if GetComponents is not implemented.
+	if d.ResourceInterpreter.HookEnabled(gvk, configv1alpha1.InterpreterOperationInterpretReplica) {
+		replicas, replicaRequirements, err := d.ResourceInterpreter.GetReplicas(object)
+		if err != nil {
+			klog.Errorf("Failed to customize replicas for %s(%s): %v", gvk, name, err)
+			return err
+		}
+		spec.Replicas = replicas
+		spec.ReplicaRequirements = replicaRequirements
+	}
+
+	return nil
+}
diff --git a/pkg/detector/policy.go b/pkg/detector/policy.go
index 1c15e7ad3..755433c40 100644
--- a/pkg/detector/policy.go
+++ b/pkg/detector/policy.go
@@ -110,14 +110,33 @@ func (d *ResourceDetector) getAndApplyPolicy(object *unstructured.Unstructured,
 	resourceChangeByKarmada bool, policyNamespace, policyName, claimedID string) error {
 	matchedPropagationPolicy := &policyv1alpha1.PropagationPolicy{}
 	err := d.Client.Get(context.TODO(), client.ObjectKey{Namespace: policyNamespace, Name: policyName}, matchedPropagationPolicy)
-	if err != nil {
-		if apierrors.IsNotFound(err) {
-			klog.V(4).Infof("PropagationPolicy(%s/%s) has been removed.", policyNamespace, policyName)
-			return d.HandlePropagationPolicyDeletion(claimedID)
+	if apierrors.IsNotFound(err) {
+		// The expected PropagationPolicy has been removed, indicating the cleanup process has completed.
+		// However, if the claimed data still exists in the resource template, it likely means the cleanup
+		// for the resource template was missed. This can happen if the claimed data was added to the resource
+		// template and then the PropagationPolicy was removed very shortly after.
+		// When cleaning up resource templates based on PropagationPolicy deletion, the cache may not have received
+		// the resource template update in time, resulting in a missed cleanup.
+		klog.V(4).Infof("PropagationPolicy(%s/%s) has been removed.", policyNamespace, policyName)
+		claimMetadata := labels.Set{policyv1alpha1.PropagationPolicyPermanentIDLabel: claimedID}
+		objRef := workv1alpha2.ObjectReference{
+			APIVersion: object.GetAPIVersion(),
+			Kind:       object.GetKind(),
+			Namespace:  object.GetNamespace(),
+			Name:       object.GetName(),
 		}
+		return d.handleResourceTemplateAndBindingCleanup(object, objRef, claimMetadata, CleanupPPClaimMetadata)
+	}
+	if err != nil {
 		klog.Errorf("Failed to get claimed policy(%s/%s),: %v", policyNamespace, policyName, err)
 		return err
 	}
+
+	// If the policy is being deleted, we should not apply it. Instead, waiting for the next reconcile to clean up metadata.
+	if !matchedPropagationPolicy.DeletionTimestamp.IsZero() {
+		return fmt.Errorf("policy(%s/%s) is being deleted", policyNamespace, policyName)
+	}
+
 	// Some resources are available in more than one group in the same kubernetes version.
 	// Therefore, the following scenarios occurs:
 	// In v1.21 kubernetes cluster, Ingress are available in both networking.k8s.io and extensions groups.
@@ -142,15 +161,33 @@ func (d *ResourceDetector) getAndApplyClusterPolicy(object *unstructured.Unstruc
 	resourceChangeByKarmada bool, policyName, policyID string) error {
 	matchedClusterPropagationPolicy := &policyv1alpha1.ClusterPropagationPolicy{}
 	err := d.Client.Get(context.TODO(), client.ObjectKey{Name: policyName}, matchedClusterPropagationPolicy)
-	if err != nil {
-		if apierrors.IsNotFound(err) {
-			klog.V(4).Infof("ClusterPropagationPolicy(%s) has been removed.", policyName)
-			return d.HandleClusterPropagationPolicyDeletion(policyID)
+	if apierrors.IsNotFound(err) {
+		// The expected ClusterPropagationPolicy has been removed, indicating the cleanup process has completed.
+		// However, if the claimed data still exists in the resource template, it likely means the cleanup
+		// for the resource template was missed. This can happen if the claimed data was added to the resource
+		// template and then the ClusterPropagationPolicy was removed very shortly after.
+		// When cleaning up resource templates based on ClusterPropagationPolicy deletion, the cache may not have received
+		// the resource template update in time, resulting in a missed cleanup.
+		klog.V(4).Infof("ClusterPropagationPolicy(%s) has been removed.", policyName)
+		claimMetadata := labels.Set{policyv1alpha1.ClusterPropagationPolicyPermanentIDLabel: policyID}
+		objRef := workv1alpha2.ObjectReference{
+			APIVersion: object.GetAPIVersion(),
+			Kind:       object.GetKind(),
+			Namespace:  object.GetNamespace(),
+			Name:       object.GetName(),
 		}
-
+		return d.handleResourceTemplateAndBindingCleanup(object, objRef, claimMetadata, CleanupCPPClaimMetadata)
+	}
+	if err != nil {
 		klog.Errorf("Failed to get claimed policy(%s),: %v", policyName, err)
 		return err
 	}
+
+	if !matchedClusterPropagationPolicy.DeletionTimestamp.IsZero() {
+		// If the cluster policy is being deleted, we should not apply it. Instead, waiting for the next reconcile to clean up metadata.
+		return fmt.Errorf("cluster policy(%s) is being deleted", policyName)
+	}
+
 	// Some resources are available in more than one group in the same kubernetes version.
 	// Therefore, the following scenarios occurs:
 	// In v1.21 kubernetes cluster, Ingress are available in both networking.k8s.io and extensions groups.
@@ -270,7 +307,6 @@ func (d *ResourceDetector) removeResourceClaimMetadataIfNotMatched(objectReferen
 		return false, nil
 	}
 
-	object = object.DeepCopy()
 	util.RemoveLabels(object, labels...)
 	util.RemoveAnnotations(object, annotations...)
 
diff --git a/pkg/features/features.go b/pkg/features/features.go
index 7e5f98ea8..0c6c4d576 100644
--- a/pkg/features/features.go
+++ b/pkg/features/features.go
@@ -103,6 +103,30 @@ const (
 	// owner: @jabellard
 	// beta: v1.15
 	ContextualLogging = logsv1.ContextualLogging
+
+	// MultiplePodTemplatesScheduling enables enhanced, resource-aware scheduling for workloads with multiple pod templates.
+	// When enabled, the scheduler and resource interpreter will use the new 'GetComponents' hook and 'components' field
+	// to support accurate resource estimation and scheduling for complex CRDs (e.g., FlinkDeployments, RayJob, VolcanoJob) that consist of
+	// multiple components with different resource requirements. This allows for more precise FederatedResourceQuota
+	// calculations and better placement decisions.
+	//
+	// owner: @mszacillo, @Dyex719, @RainbowMango, @XiShanYongYe-Chang, @zhzhuang-zju, @seanlaii
+	// alpha: v1.15
+	MultiplePodTemplatesScheduling featuregate.Feature = "MultiplePodTemplatesScheduling"
+
+	// ControllerPriorityQueue controls whether the controller-runtime Priority Queue for controllers is enabled.
+	// When enabled, during the startup phase of karmada-controller-manager and karmada-agent,
+	// controllers implemented with controller-runtime will prioritize processing recent cluster changes first,
+	// while deferring items without recent updates that still need to be processed to the end of the queue.
+	// This helps the system quickly catch up with the latest state and reduces the perceived
+	// backlog from external users.
+	// However, enabling this feature may increase the overall number of reconciliations. This is
+	// because newer events spend less time in the queue, which reduces the chance of them being
+	// deduplicated before processing.
+	//
+	// owner: @zach593
+	// alpha: v1.15
+	ControllerPriorityQueue featuregate.Feature = "ControllerPriorityQueue"
 )
 
 var (
@@ -128,6 +152,8 @@ var (
 		LoggingAlphaOptions:               {Default: false, PreRelease: featuregate.Alpha},
 		LoggingBetaOptions:                {Default: true, PreRelease: featuregate.Beta},
 		ContextualLogging:                 {Default: true, PreRelease: featuregate.Beta},
+		MultiplePodTemplatesScheduling:    {Default: false, PreRelease: featuregate.Alpha},
+		ControllerPriorityQueue:           {Default: false, PreRelease: featuregate.Alpha},
 	}
 )
 
diff --git a/pkg/generated/openapi/zz_generated.openapi.go b/pkg/generated/openapi/zz_generated.openapi.go
index 8cc8dbcb8..4e539c661 100644
--- a/pkg/generated/openapi/zz_generated.openapi.go
+++ b/pkg/generated/openapi/zz_generated.openapi.go
@@ -62,6 +62,7 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA
 		"github.com/karmada-io/karmada/pkg/apis/cluster/v1alpha1.ResourceModel":                              schema_pkg_apis_cluster_v1alpha1_ResourceModel(ref),
 		"github.com/karmada-io/karmada/pkg/apis/cluster/v1alpha1.ResourceModelRange":                         schema_pkg_apis_cluster_v1alpha1_ResourceModelRange(ref),
 		"github.com/karmada-io/karmada/pkg/apis/cluster/v1alpha1.ResourceSummary":                            schema_pkg_apis_cluster_v1alpha1_ResourceSummary(ref),
+		"github.com/karmada-io/karmada/pkg/apis/config/v1alpha1.ComponentResourceRequirement":                schema_pkg_apis_config_v1alpha1_ComponentResourceRequirement(ref),
 		"github.com/karmada-io/karmada/pkg/apis/config/v1alpha1.CustomizationRules":                          schema_pkg_apis_config_v1alpha1_CustomizationRules(ref),
 		"github.com/karmada-io/karmada/pkg/apis/config/v1alpha1.CustomizationTarget":                         schema_pkg_apis_config_v1alpha1_CustomizationTarget(ref),
 		"github.com/karmada-io/karmada/pkg/apis/config/v1alpha1.DependencyInterpretation":                    schema_pkg_apis_config_v1alpha1_DependencyInterpretation(ref),
@@ -97,6 +98,7 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA
 		"github.com/karmada-io/karmada/pkg/apis/policy/v1alpha1.ApplicationFailoverBehavior":                 schema_pkg_apis_policy_v1alpha1_ApplicationFailoverBehavior(ref),
 		"github.com/karmada-io/karmada/pkg/apis/policy/v1alpha1.ClusterAffinity":                             schema_pkg_apis_policy_v1alpha1_ClusterAffinity(ref),
 		"github.com/karmada-io/karmada/pkg/apis/policy/v1alpha1.ClusterAffinityTerm":                         schema_pkg_apis_policy_v1alpha1_ClusterAffinityTerm(ref),
+		"github.com/karmada-io/karmada/pkg/apis/policy/v1alpha1.ClusterFailoverBehavior":                     schema_pkg_apis_policy_v1alpha1_ClusterFailoverBehavior(ref),
 		"github.com/karmada-io/karmada/pkg/apis/policy/v1alpha1.ClusterOverridePolicy":                       schema_pkg_apis_policy_v1alpha1_ClusterOverridePolicy(ref),
 		"github.com/karmada-io/karmada/pkg/apis/policy/v1alpha1.ClusterOverridePolicyList":                   schema_pkg_apis_policy_v1alpha1_ClusterOverridePolicyList(ref),
 		"github.com/karmada-io/karmada/pkg/apis/policy/v1alpha1.ClusterPreferences":                          schema_pkg_apis_policy_v1alpha1_ClusterPreferences(ref),
@@ -178,6 +180,7 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA
 		"github.com/karmada-io/karmada/pkg/apis/work/v1alpha2.BindingSnapshot":                               schema_pkg_apis_work_v1alpha2_BindingSnapshot(ref),
 		"github.com/karmada-io/karmada/pkg/apis/work/v1alpha2.ClusterResourceBinding":                        schema_pkg_apis_work_v1alpha2_ClusterResourceBinding(ref),
 		"github.com/karmada-io/karmada/pkg/apis/work/v1alpha2.ClusterResourceBindingList":                    schema_pkg_apis_work_v1alpha2_ClusterResourceBindingList(ref),
+		"github.com/karmada-io/karmada/pkg/apis/work/v1alpha2.ComponentRequirements":                         schema_pkg_apis_work_v1alpha2_ComponentRequirements(ref),
 		"github.com/karmada-io/karmada/pkg/apis/work/v1alpha2.GracefulEvictionTask":                          schema_pkg_apis_work_v1alpha2_GracefulEvictionTask(ref),
 		"github.com/karmada-io/karmada/pkg/apis/work/v1alpha2.NodeClaim":                                     schema_pkg_apis_work_v1alpha2_NodeClaim(ref),
 		"github.com/karmada-io/karmada/pkg/apis/work/v1alpha2.ObjectReference":                               schema_pkg_apis_work_v1alpha2_ObjectReference(ref),
@@ -2065,6 +2068,28 @@ func schema_pkg_apis_cluster_v1alpha1_ResourceSummary(ref common.ReferenceCallba
 	}
 }
 
+func schema_pkg_apis_config_v1alpha1_ComponentResourceRequirement(ref common.ReferenceCallback) common.OpenAPIDefinition {
+	return common.OpenAPIDefinition{
+		Schema: spec.Schema{
+			SchemaProps: spec.SchemaProps{
+				Description: "ComponentResourceRequirement holds the scripts for extracting the desired replica count and resource requirements for each component within a resource. This is particularly useful for resources that define multiple components (such as CRDs with multiple pod templates), but can also be used for single-component resources.",
+				Type:        []string{"object"},
+				Properties: map[string]spec.Schema{
+					"luaScript": {
+						SchemaProps: spec.SchemaProps{
+							Description: "LuaScript holds the Lua script that is used to extract the desired replica count and resource requirements for each component of the resource.\n\nThe script should implement a function as follows:\n\n```\n  luaScript: >\n      function GetComponents(desiredObj)\n          local components = {}\n\n          local jobManagerComponent = {\n              name = \"jobmanager\"\n              replicas = desiredObj.spec.jobManager.replicas\n          }\n          table.insert(components, jobManagerComponent)\n\n          local taskManagerComponent = {\n              name = \"taskmanager\"\n              replicas = desiredObj.spec.taskManager.replicas\n          }\n          table.insert(components, taskManagerComponent)\n\n          return components\n      end\n```\n\nThe content of the LuaScript needs to be a whole function including both declaration and implementation.\n\nThe parameters will be supplied by the system:\n  - desiredObj: the object represents the configuration to be applied\n      to the member cluster.\n\nThe function expects one return value:\n  - components: the resource requirements for each component.\nThe returned value will be set into a ResourceBinding or ClusterResourceBinding.",
+							Default:     "",
+							Type:        []string{"string"},
+							Format:      "",
+						},
+					},
+				},
+				Required: []string{"luaScript"},
+			},
+		},
+	}
+}
+
 func schema_pkg_apis_config_v1alpha1_CustomizationRules(ref common.ReferenceCallback) common.OpenAPIDefinition {
 	return common.OpenAPIDefinition{
 		Schema: spec.Schema{
@@ -2084,6 +2109,12 @@ func schema_pkg_apis_config_v1alpha1_CustomizationRules(ref common.ReferenceCall
 							Ref:         ref("github.com/karmada-io/karmada/pkg/apis/config/v1alpha1.ReplicaResourceRequirement"),
 						},
 					},
+					"componentResource": {
+						SchemaProps: spec.SchemaProps{
+							Description: "ComponentResource describes the rules for Karmada to discover the resource requirements for multiple components from the given object. This is designed for CRDs with multiple components (e.g., FlinkDeployment), but can also be used for single-component resources like Deployment. If implemented, the controller will use this to obtain per-component replica and resource requirements, and will not call ReplicaResource. If not implemented, the controller will fall back to ReplicaResource for backward compatibility. This will only be used when the feature gate 'MultiplePodTemplatesScheduling' is enabled.",
+							Ref:         ref("github.com/karmada-io/karmada/pkg/apis/config/v1alpha1.ComponentResourceRequirement"),
+						},
+					},
 					"replicaRevision": {
 						SchemaProps: spec.SchemaProps{
 							Description: "ReplicaRevision describes the rules for Karmada to revise the resource's replica. It would be useful for those CRD resources that declare workload types like Deployment. It is usually not needed for Kubernetes native resources(Deployment, Job) as Karmada knows how to revise replicas for them. But if it is set, the built-in revision rules will be ignored.",
@@ -2118,7 +2149,7 @@ func schema_pkg_apis_config_v1alpha1_CustomizationRules(ref common.ReferenceCall
 			},
 		},
 		Dependencies: []string{
-			"github.com/karmada-io/karmada/pkg/apis/config/v1alpha1.DependencyInterpretation", "github.com/karmada-io/karmada/pkg/apis/config/v1alpha1.HealthInterpretation", "github.com/karmada-io/karmada/pkg/apis/config/v1alpha1.LocalValueRetention", "github.com/karmada-io/karmada/pkg/apis/config/v1alpha1.ReplicaResourceRequirement", "github.com/karmada-io/karmada/pkg/apis/config/v1alpha1.ReplicaRevision", "github.com/karmada-io/karmada/pkg/apis/config/v1alpha1.StatusAggregation", "github.com/karmada-io/karmada/pkg/apis/config/v1alpha1.StatusReflection"},
+			"github.com/karmada-io/karmada/pkg/apis/config/v1alpha1.ComponentResourceRequirement", "github.com/karmada-io/karmada/pkg/apis/config/v1alpha1.DependencyInterpretation", "github.com/karmada-io/karmada/pkg/apis/config/v1alpha1.HealthInterpretation", "github.com/karmada-io/karmada/pkg/apis/config/v1alpha1.LocalValueRetention", "github.com/karmada-io/karmada/pkg/apis/config/v1alpha1.ReplicaResourceRequirement", "github.com/karmada-io/karmada/pkg/apis/config/v1alpha1.ReplicaRevision", "github.com/karmada-io/karmada/pkg/apis/config/v1alpha1.StatusAggregation", "github.com/karmada-io/karmada/pkg/apis/config/v1alpha1.StatusReflection"},
 	}
 }
 
@@ -3518,7 +3549,7 @@ func schema_pkg_apis_policy_v1alpha1_ApplicationFailoverBehavior(ref common.Refe
 					},
 					"purgeMode": {
 						SchemaProps: spec.SchemaProps{
-							Description: "PurgeMode represents how to deal with the legacy applications on the cluster from which the application is migrated. Valid options are \"Immediately\", \"Graciously\" and \"Never\". Defaults to \"Graciously\".",
+							Description: "PurgeMode represents how to deal with the legacy applications on the cluster from which the application is migrated. Valid options are \"Directly\", \"Gracefully\", \"Never\", \"Immediately\"(deprecated), and \"Graciously\"(deprecated). Defaults to \"Gracefully\".",
 							Type:        []string{"string"},
 							Format:      "",
 						},
@@ -3668,6 +3699,34 @@ func schema_pkg_apis_policy_v1alpha1_ClusterAffinityTerm(ref common.ReferenceCal
 	}
 }
 
+func schema_pkg_apis_policy_v1alpha1_ClusterFailoverBehavior(ref common.ReferenceCallback) common.OpenAPIDefinition {
+	return common.OpenAPIDefinition{
+		Schema: spec.Schema{
+			SchemaProps: spec.SchemaProps{
+				Description: "ClusterFailoverBehavior indicates cluster failover behaviors.",
+				Type:        []string{"object"},
+				Properties: map[string]spec.Schema{
+					"purgeMode": {
+						SchemaProps: spec.SchemaProps{
+							Description: "PurgeMode represents how to deal with the legacy applications on the cluster from which the application is migrated. Valid options are \"Directly\", \"Gracefully\". Defaults to \"Gracefully\".",
+							Type:        []string{"string"},
+							Format:      "",
+						},
+					},
+					"statePreservation": {
+						SchemaProps: spec.SchemaProps{
+							Description: "StatePreservation defines the policy for preserving and restoring state data during failover events for stateful applications.\n\nWhen an application fails over from one cluster to another, this policy enables the extraction of critical data from the original resource configuration. Upon successful migration, the extracted data is then re-injected into the new resource, ensuring that the application can resume operation with its previous state intact. This is particularly useful for stateful applications where maintaining data consistency across failover events is crucial. If not specified, means no state data will be preserved.\n\nNote: This requires the StatefulFailoverInjection feature gate to be enabled, which is alpha.",
+							Ref:         ref("github.com/karmada-io/karmada/pkg/apis/policy/v1alpha1.StatePreservation"),
+						},
+					},
+				},
+			},
+		},
+		Dependencies: []string{
+			"github.com/karmada-io/karmada/pkg/apis/policy/v1alpha1.StatePreservation"},
+	}
+}
+
 func schema_pkg_apis_policy_v1alpha1_ClusterOverridePolicy(ref common.ReferenceCallback) common.OpenAPIDefinition {
 	return common.OpenAPIDefinition{
 		Schema: spec.Schema{
@@ -4175,11 +4234,17 @@ func schema_pkg_apis_policy_v1alpha1_FailoverBehavior(ref common.ReferenceCallba
 							Ref:         ref("github.com/karmada-io/karmada/pkg/apis/policy/v1alpha1.ApplicationFailoverBehavior"),
 						},
 					},
+					"cluster": {
+						SchemaProps: spec.SchemaProps{
+							Description: "Cluster indicates failover behaviors in case of cluster failure. If this value is nil, the failover behavior in case of cluster failure will be controlled by the controller's no-execute-taint-eviction-purge-mode parameter. If set, the failover behavior in case of cluster failure will be defined by this value.",
+							Ref:         ref("github.com/karmada-io/karmada/pkg/apis/policy/v1alpha1.ClusterFailoverBehavior"),
+						},
+					},
 				},
 			},
 		},
 		Dependencies: []string{
-			"github.com/karmada-io/karmada/pkg/apis/policy/v1alpha1.ApplicationFailoverBehavior"},
+			"github.com/karmada-io/karmada/pkg/apis/policy/v1alpha1.ApplicationFailoverBehavior", "github.com/karmada-io/karmada/pkg/apis/policy/v1alpha1.ClusterFailoverBehavior"},
 	}
 }
 
@@ -7184,6 +7249,43 @@ func schema_pkg_apis_work_v1alpha2_ClusterResourceBindingList(ref common.Referen
 	}
 }
 
+func schema_pkg_apis_work_v1alpha2_ComponentRequirements(ref common.ReferenceCallback) common.OpenAPIDefinition {
+	return common.OpenAPIDefinition{
+		Schema: spec.Schema{
+			SchemaProps: spec.SchemaProps{
+				Description: "ComponentRequirements represents the requirements for a specific component.",
+				Type:        []string{"object"},
+				Properties: map[string]spec.Schema{
+					"name": {
+						SchemaProps: spec.SchemaProps{
+							Description: "Name of this component. It is required when the resource contains multiple components to ensure proper identification, and must also be unique within the same resource.",
+							Type:        []string{"string"},
+							Format:      "",
+						},
+					},
+					"replicas": {
+						SchemaProps: spec.SchemaProps{
+							Description: "Replicas represents the replica number of the resource's component.",
+							Default:     0,
+							Type:        []string{"integer"},
+							Format:      "int32",
+						},
+					},
+					"replicaRequirements": {
+						SchemaProps: spec.SchemaProps{
+							Description: "ReplicaRequirements represents the requirements required by each replica for this component.",
+							Ref:         ref("github.com/karmada-io/karmada/pkg/apis/work/v1alpha2.ReplicaRequirements"),
+						},
+					},
+				},
+				Required: []string{"replicas"},
+			},
+		},
+		Dependencies: []string{
+			"github.com/karmada-io/karmada/pkg/apis/work/v1alpha2.ReplicaRequirements"},
+	}
+}
+
 func schema_pkg_apis_work_v1alpha2_GracefulEvictionTask(ref common.ReferenceCallback) common.OpenAPIDefinition {
 	return common.OpenAPIDefinition{
 		Schema: spec.Schema{
@@ -7201,7 +7303,7 @@ func schema_pkg_apis_work_v1alpha2_GracefulEvictionTask(ref common.ReferenceCall
 					},
 					"purgeMode": {
 						SchemaProps: spec.SchemaProps{
-							Description: "PurgeMode represents how to deal with the legacy applications on the cluster from which the application is migrated. Valid options are \"Immediately\", \"Graciously\" and \"Never\".",
+							Description: "PurgeMode represents how to deal with the legacy applications on the cluster from which the application is migrated. Valid options are \"Immediately\", \"Directly\", \"Graciously\", \"Gracefully\" and \"Never\".",
 							Type:        []string{"string"},
 							Format:      "",
 						},
@@ -7589,6 +7691,20 @@ func schema_pkg_apis_work_v1alpha2_ResourceBindingSpec(ref common.ReferenceCallb
 							Format:      "int32",
 						},
 					},
+					"components": {
+						SchemaProps: spec.SchemaProps{
+							Description: "Components represents the requirements of multiple pod templates of the referencing resource. It is designed to support workloads that consist of multiple pod templates, such as distributed training jobs (e.g., PyTorch, TensorFlow) and big data workloads (e.g., FlinkDeployment), where each workload is composed of more than one pod template. It is also capable of representing single-component workloads, such as Deployment.\n\nNote: This field is intended to replace the legacy ReplicaRequirements and Replicas fields above. It is only populated when the MultiplePodTemplatesScheduling feature gate is enabled.",
+							Type:        []string{"array"},
+							Items: &spec.SchemaOrArray{
+								Schema: &spec.Schema{
+									SchemaProps: spec.SchemaProps{
+										Default: map[string]interface{}{},
+										Ref:     ref("github.com/karmada-io/karmada/pkg/apis/work/v1alpha2.ComponentRequirements"),
+									},
+								},
+							},
+						},
+					},
 					"clusters": {
 						SchemaProps: spec.SchemaProps{
 							Description: "Clusters represents target member clusters where the resource to be deployed.",
@@ -7687,7 +7803,7 @@ func schema_pkg_apis_work_v1alpha2_ResourceBindingSpec(ref common.ReferenceCallb
 			},
 		},
 		Dependencies: []string{
-			"github.com/karmada-io/karmada/pkg/apis/policy/v1alpha1.FailoverBehavior", "github.com/karmada-io/karmada/pkg/apis/policy/v1alpha1.Placement", "github.com/karmada-io/karmada/pkg/apis/work/v1alpha2.BindingSnapshot", "github.com/karmada-io/karmada/pkg/apis/work/v1alpha2.GracefulEvictionTask", "github.com/karmada-io/karmada/pkg/apis/work/v1alpha2.ObjectReference", "github.com/karmada-io/karmada/pkg/apis/work/v1alpha2.ReplicaRequirements", "github.com/karmada-io/karmada/pkg/apis/work/v1alpha2.SchedulePriority", "github.com/karmada-io/karmada/pkg/apis/work/v1alpha2.Suspension", "github.com/karmada-io/karmada/pkg/apis/work/v1alpha2.TargetCluster", "k8s.io/apimachinery/pkg/apis/meta/v1.Time"},
+			"github.com/karmada-io/karmada/pkg/apis/policy/v1alpha1.FailoverBehavior", "github.com/karmada-io/karmada/pkg/apis/policy/v1alpha1.Placement", "github.com/karmada-io/karmada/pkg/apis/work/v1alpha2.BindingSnapshot", "github.com/karmada-io/karmada/pkg/apis/work/v1alpha2.ComponentRequirements", "github.com/karmada-io/karmada/pkg/apis/work/v1alpha2.GracefulEvictionTask", "github.com/karmada-io/karmada/pkg/apis/work/v1alpha2.ObjectReference", "github.com/karmada-io/karmada/pkg/apis/work/v1alpha2.ReplicaRequirements", "github.com/karmada-io/karmada/pkg/apis/work/v1alpha2.SchedulePriority", "github.com/karmada-io/karmada/pkg/apis/work/v1alpha2.Suspension", "github.com/karmada-io/karmada/pkg/apis/work/v1alpha2.TargetCluster", "k8s.io/apimachinery/pkg/apis/meta/v1.Time"},
 	}
 }
 
diff --git a/pkg/karmadactl/create/create.go b/pkg/karmadactl/create/create.go
index ed34d3bec..9b2e05f5c 100644
--- a/pkg/karmadactl/create/create.go
+++ b/pkg/karmadactl/create/create.go
@@ -49,7 +49,7 @@ var (
 // NewCmdCreate returns new initialized instance of create sub command
 func NewCmdCreate(f util.Factory, parentCommand string, ioStreams genericiooptions.IOStreams) *cobra.Command {
 	cmd := kubectlcreate.NewCmdCreate(f, ioStreams)
-	cmd.Long = fmt.Sprintf(createLong, parentCommand)
+	cmd.Long = createLong
 	cmd.Example = fmt.Sprintf(createExample, parentCommand)
 	cmd.Annotations = map[string]string{
 		util.TagCommandGroup: util.GroupBasic,
diff --git a/pkg/karmadactl/explain/explain.go b/pkg/karmadactl/explain/explain.go
index 18c3443a3..b5f6ae7a7 100644
--- a/pkg/karmadactl/explain/explain.go
+++ b/pkg/karmadactl/explain/explain.go
@@ -67,7 +67,7 @@ func NewCmdExplain(f util.Factory, parentCommand string, streams genericiooption
 		Use:                   "explain TYPE [--recursive=FALSE|TRUE] [--api-version=api-version-group] [--output=plaintext|plaintext-openapiv2] ",
 		DisableFlagsInUseLine: true,
 		Short:                 "Get documentation for a resource",
-		Long:                  fmt.Sprintf(explainLong, parentCommand),
+		Long:                  explainLong,
 		Example:               fmt.Sprintf(explainExamples, parentCommand),
 		Run: func(_ *cobra.Command, args []string) {
 			cmdutil.CheckErr(o.Complete(f, parentCommand, args))
diff --git a/pkg/karmadactl/interpret/interpret.go b/pkg/karmadactl/interpret/interpret.go
index 6122c24b3..9d80a5155 100644
--- a/pkg/karmadactl/interpret/interpret.go
+++ b/pkg/karmadactl/interpret/interpret.go
@@ -54,7 +54,7 @@ var (
 	`)
 
 	interpretExample = templates.Examples(`
-        # Check the customizations in file
+        # Check the customizations in file (only YAML and JSON formats are supported)
         %[1]s interpret -f customization.json --check
 
 		# Execute the retention rule
@@ -129,7 +129,7 @@ func NewCmdInterpret(f util.Factory, parentCommand string, streams genericioopti
 	flags.StringVar(&o.ObservedFile, "observed-file", o.ObservedFile, "Filename, directory, or URL to files identifying the resource to use as observedObj argument in rule script.")
 	flags.StringVar(&o.StatusFile, "status-file", o.StatusFile, "Filename, directory, or URL to files identifying the resource to use as statusItems argument in rule script.")
 	flags.Int32Var(&o.DesiredReplica, "desired-replica", o.DesiredReplica, "The desiredReplica argument in rule script.")
-	cmdutil.AddJsonFilenameFlag(flags, &o.FilenameOptions.Filenames, "Filename, directory, or URL to files containing the customizations")
+	cmdutil.AddJsonFilenameFlag(flags, &o.FilenameOptions.Filenames, "Filename, directory, or URL to files containing the customizations. Files need to be in either YAML or JSON format.")
 	flags.BoolVarP(&o.FilenameOptions.Recursive, "recursive", "R", false, "Process the directory used in -f, --filename recursively. Useful when you want to manage related manifests organized within the same directory.")
 
 	utilcomp.RegisterCompletionFuncForKarmadaContextFlag(cmd)
diff --git a/pkg/karmadactl/promote/promote.go b/pkg/karmadactl/promote/promote.go
index 6afcedbf0..820fe3b1b 100644
--- a/pkg/karmadactl/promote/promote.go
+++ b/pkg/karmadactl/promote/promote.go
@@ -462,10 +462,6 @@ func (o *CommandPromoteOption) promoteDeps(memberClusterFactory cmdutil.Factory,
 	serviceLister := sharedFactory.Core().V1().Services().Lister()
 	sharedFactory.Start(ctx.Done())
 	sharedFactory.WaitForCacheSync(ctx.Done())
-	controlPlaneInformerManager.Start()
-	if sync := controlPlaneInformerManager.WaitForCacheSync(); sync == nil {
-		return errors.New("informer factory for cluster does not exist")
-	}
 
 	defaultInterpreter := native.NewDefaultInterpreter()
 	thirdpartyInterpreter := thirdparty.NewConfigurableInterpreter()
@@ -475,6 +471,11 @@ func (o *CommandPromoteOption) promoteDeps(memberClusterFactory cmdutil.Factory,
 		return fmt.Errorf("failed to create customized interpreter: %v", err)
 	}
 
+	controlPlaneInformerManager.Start()
+	if syncs := controlPlaneInformerManager.WaitForCacheSync(); len(syncs) == 0 {
+		return errors.New("no informers registered in the informer factory")
+	}
+
 	// check if the resource interpreter supports to interpret dependencies
 	if !defaultInterpreter.HookEnabled(obj.GroupVersionKind(), configv1alpha1.InterpreterOperationInterpretDependency) &&
 		!thirdpartyInterpreter.HookEnabled(obj.GroupVersionKind(), configv1alpha1.InterpreterOperationInterpretDependency) &&
diff --git a/pkg/resourceinterpreter/customized/declarative/configmanager/accessor.go b/pkg/resourceinterpreter/customized/declarative/configmanager/accessor.go
index 65c06839f..d267f2a60 100644
--- a/pkg/resourceinterpreter/customized/declarative/configmanager/accessor.go
+++ b/pkg/resourceinterpreter/customized/declarative/configmanager/accessor.go
@@ -31,6 +31,7 @@ type LuaScriptAccessor interface {
 
 	GetRetentionLuaScript() string
 	GetReplicaResourceLuaScript() string
+	GetComponentResourceLuaScript() string
 	GetReplicaRevisionLuaScript() string
 	GetStatusReflectionLuaScript() string
 	GetStatusAggregationLuaScript() string
@@ -46,6 +47,7 @@ type CustomAccessor interface {
 type resourceCustomAccessor struct {
 	retention                 *configv1alpha1.LocalValueRetention
 	replicaResource           *configv1alpha1.ReplicaResourceRequirement
+	componentResource         *configv1alpha1.ComponentResourceRequirement
 	replicaRevision           *configv1alpha1.ReplicaRevision
 	statusReflection          *configv1alpha1.StatusReflection
 	statusAggregation         *configv1alpha1.StatusAggregation
@@ -66,6 +68,9 @@ func (a *resourceCustomAccessor) Merge(rules configv1alpha1.CustomizationRules)
 	if rules.ReplicaResource != nil {
 		a.setReplicaResource(rules.ReplicaResource)
 	}
+	if rules.ComponentResource != nil {
+		a.setComponentResource(rules.ComponentResource)
+	}
 	if rules.ReplicaRevision != nil {
 		a.setReplicaRevision(rules.ReplicaRevision)
 	}
@@ -97,6 +102,13 @@ func (a *resourceCustomAccessor) GetReplicaResourceLuaScript() string {
 	return a.replicaResource.LuaScript
 }
 
+func (a *resourceCustomAccessor) GetComponentResourceLuaScript() string {
+	if a.componentResource == nil {
+		return ""
+	}
+	return a.componentResource.LuaScript
+}
+
 func (a *resourceCustomAccessor) GetReplicaRevisionLuaScript() string {
 	if a.replicaRevision == nil {
 		return ""
@@ -161,6 +173,17 @@ func (a *resourceCustomAccessor) setReplicaResource(replicaResource *configv1alp
 	}
 }
 
+func (a *resourceCustomAccessor) setComponentResource(componentResource *configv1alpha1.ComponentResourceRequirement) {
+	if a.componentResource == nil {
+		a.componentResource = componentResource
+		return
+	}
+
+	if componentResource.LuaScript != "" && a.componentResource.LuaScript == "" {
+		a.componentResource.LuaScript = componentResource.LuaScript
+	}
+}
+
 func (a *resourceCustomAccessor) setReplicaRevision(replicaRevision *configv1alpha1.ReplicaRevision) {
 	if a.replicaRevision == nil {
 		a.replicaRevision = replicaRevision
diff --git a/pkg/resourceinterpreter/customized/declarative/configmanager/manager.go b/pkg/resourceinterpreter/customized/declarative/configmanager/manager.go
index befff2aa6..bbaca0f9e 100644
--- a/pkg/resourceinterpreter/customized/declarative/configmanager/manager.go
+++ b/pkg/resourceinterpreter/customized/declarative/configmanager/manager.go
@@ -27,21 +27,21 @@ import (
 	"k8s.io/klog/v2"
 
 	configv1alpha1 "github.com/karmada-io/karmada/pkg/apis/config/v1alpha1"
+	"github.com/karmada-io/karmada/pkg/util"
 	"github.com/karmada-io/karmada/pkg/util/fedinformer"
 	"github.com/karmada-io/karmada/pkg/util/fedinformer/genericmanager"
 	"github.com/karmada-io/karmada/pkg/util/helper"
 )
 
-var resourceInterpreterCustomizationsGVR = schema.GroupVersionResource{
-	Group:    configv1alpha1.GroupVersion.Group,
-	Version:  configv1alpha1.GroupVersion.Version,
-	Resource: "resourceinterpretercustomizations",
-}
-
 // ConfigManager can list custom resource interpreter.
 type ConfigManager interface {
 	CustomAccessors() map[schema.GroupVersionKind]CustomAccessor
 	HasSynced() bool
+	// LoadConfig is used to load ResourceInterpreterCustomization into the cache,
+	// it requires the provided customizations to be a full list of objects.
+	// It is recommended to be called during startup. After called, HasSynced() will always
+	// return true, and CustomAccessors() will return a map of CustomAccessor containing
+	// all ResourceInterpreterCustomization configurations.
 	LoadConfig(customizations []*configv1alpha1.ResourceInterpreterCustomization)
 }
 
@@ -81,12 +81,12 @@ func NewInterpreterConfigManager(informer genericmanager.SingleClusterInformerMa
 	// In interpret command, rules are not loaded from server, so we don't start informer for it.
 	if informer != nil {
 		manager.informer = informer
-		manager.lister = informer.Lister(resourceInterpreterCustomizationsGVR)
+		manager.lister = informer.Lister(util.ResourceInterpreterCustomizationsGVR)
 		configHandlers := fedinformer.NewHandlerOnEvents(
 			func(_ interface{}) { _ = manager.updateConfiguration() },
 			func(_, _ interface{}) { _ = manager.updateConfiguration() },
 			func(_ interface{}) { _ = manager.updateConfiguration() })
-		informer.ForResource(resourceInterpreterCustomizationsGVR, configHandlers)
+		informer.ForResource(util.ResourceInterpreterCustomizationsGVR, configHandlers)
 	}
 
 	return manager
@@ -102,7 +102,7 @@ func (configManager *interpreterConfigManager) updateConfiguration() error {
 	if configManager.informer == nil {
 		return errors.New("informer manager is not configured")
 	}
-	if !configManager.informer.IsInformerSynced(resourceInterpreterCustomizationsGVR) {
+	if !configManager.informer.IsInformerSynced(util.ResourceInterpreterCustomizationsGVR) {
 		return errors.New("informer of ResourceInterpreterCustomization not synced")
 	}
 
diff --git a/pkg/resourceinterpreter/customized/declarative/configurable.go b/pkg/resourceinterpreter/customized/declarative/configurable.go
index 7acaa5ee5..fa9f5feb4 100644
--- a/pkg/resourceinterpreter/customized/declarative/configurable.go
+++ b/pkg/resourceinterpreter/customized/declarative/configurable.go
@@ -70,6 +70,8 @@ func (c *ConfigurableInterpreter) HookEnabled(kind schema.GroupVersionKind, oper
 		script = accessor.GetHealthInterpretationLuaScript()
 	case configv1alpha1.InterpreterOperationInterpretReplica:
 		script = accessor.GetReplicaResourceLuaScript()
+	case configv1alpha1.InterpreterOperationInterpretComponent:
+		script = accessor.GetComponentResourceLuaScript()
 	case configv1alpha1.InterpreterOperationInterpretStatus:
 		script = accessor.GetStatusReflectionLuaScript()
 	case configv1alpha1.InterpreterOperationRetain:
@@ -99,6 +101,25 @@ func (c *ConfigurableInterpreter) GetReplicas(object *unstructured.Unstructured)
 	return
 }
 
+// GetComponents returns the desired components of the object.
+func (c *ConfigurableInterpreter) GetComponents(object *unstructured.Unstructured) (components []workv1alpha2.ComponentRequirements, enabled bool, err error) {
+	klog.V(4).Infof("Get components for object: %v %s/%s with configurable interpreter.", object.GroupVersionKind(), object.GetNamespace(), object.GetName())
+
+	accessor, enabled := c.getCustomAccessor(object.GroupVersionKind())
+	if !enabled {
+		return
+	}
+
+	script := accessor.GetComponentResourceLuaScript()
+	if len(script) == 0 {
+		enabled = false
+		return
+	}
+
+	components, err = c.luaVM.GetComponents(object, script)
+	return
+}
+
 // ReviseReplica revises the replica of the given object.
 func (c *ConfigurableInterpreter) ReviseReplica(object *unstructured.Unstructured, replica int64) (revised *unstructured.Unstructured, enabled bool, err error) {
 	klog.V(4).Infof("Revise replicas for object: %v %s/%s with configurable interpreter.", object.GroupVersionKind(), object.GetNamespace(), object.GetName())
diff --git a/pkg/resourceinterpreter/customized/declarative/luavm/lua.go b/pkg/resourceinterpreter/customized/declarative/luavm/lua.go
index 2c2b91781..d06bec358 100644
--- a/pkg/resourceinterpreter/customized/declarative/luavm/lua.go
+++ b/pkg/resourceinterpreter/customized/declarative/luavm/lua.go
@@ -154,6 +154,33 @@ func (vm *VM) GetReplicas(obj *unstructured.Unstructured, script string) (replic
 	return
 }
 
+// GetComponents returns the desired components of the object by executing a Lua script.
+func (vm *VM) GetComponents(obj *unstructured.Unstructured, script string) ([]workv1alpha2.ComponentRequirements, error) {
+	results, err := vm.RunScript(script, "GetComponents", 1, obj)
+	if err != nil {
+		return nil, fmt.Errorf("failed to run 'GetComponents' script: %w", err)
+	}
+
+	componentsResult := results[0]
+	var components []workv1alpha2.ComponentRequirements
+
+	switch componentsResult.Type() {
+	case lua.LTTable:
+		if err := ConvertLuaResultInto(componentsResult.(*lua.LTable), &components); err != nil {
+			klog.Errorf("Failed to convert Lua result for GetComponents: %v", err)
+			return nil, fmt.Errorf("failed to convert lua table for components: %w", err)
+		}
+	case lua.LTNil:
+		// This is a valid case where the script returns no components.
+		// The 'components' slice is already nil, so we do nothing.
+	default:
+		// Handle unexpected return types.
+		return nil, fmt.Errorf("expected result type 'table' or 'nil' from GetComponents, but got '%s'", componentsResult.Type())
+	}
+
+	return components, nil
+}
+
 // ReviseReplica revises the replica of the given object by lua.
 func (vm *VM) ReviseReplica(object *unstructured.Unstructured, replica int64, script string) (*unstructured.Unstructured, error) {
 	results, err := vm.RunScript(script, "ReviseReplica", 1, object, replica)
diff --git a/pkg/resourceinterpreter/customized/webhook/configmanager/manager.go b/pkg/resourceinterpreter/customized/webhook/configmanager/manager.go
index 3f148d7dd..0c67e99ee 100644
--- a/pkg/resourceinterpreter/customized/webhook/configmanager/manager.go
+++ b/pkg/resourceinterpreter/customized/webhook/configmanager/manager.go
@@ -17,37 +17,38 @@ limitations under the License.
 package configmanager
 
 import (
+	"errors"
 	"fmt"
 	"sort"
 	"sync/atomic"
 
 	"k8s.io/apimachinery/pkg/labels"
-	"k8s.io/apimachinery/pkg/runtime/schema"
-	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
 	"k8s.io/client-go/tools/cache"
 	"k8s.io/klog/v2"
 
 	configv1alpha1 "github.com/karmada-io/karmada/pkg/apis/config/v1alpha1"
+	"github.com/karmada-io/karmada/pkg/util"
 	"github.com/karmada-io/karmada/pkg/util/fedinformer"
 	"github.com/karmada-io/karmada/pkg/util/fedinformer/genericmanager"
 	"github.com/karmada-io/karmada/pkg/util/helper"
 )
 
-var resourceExploringWebhookConfigurationsGVR = schema.GroupVersionResource{
-	Group:    configv1alpha1.GroupVersion.Group,
-	Version:  configv1alpha1.GroupVersion.Version,
-	Resource: "resourceinterpreterwebhookconfigurations",
-}
-
 // ConfigManager can list dynamic webhooks.
 type ConfigManager interface {
 	HookAccessors() []WebhookAccessor
 	HasSynced() bool
+	// LoadConfig is used to load ResourceInterpreterWebhookConfiguration into the cache,
+	// it requires the provided webhookConfigurations to be a full list of objects.
+	// It is recommended to be called during startup. After called, HasSynced() will always
+	// return true, and HookAccessors() will return a list of WebhookAccessor containing
+	// all ResourceInterpreterWebhookConfiguration configurations.
+	LoadConfig(webhookConfigurations []*configv1alpha1.ResourceInterpreterWebhookConfiguration)
 }
 
 // interpreterConfigManager collect the resource interpreter webhook configuration.
 type interpreterConfigManager struct {
 	configuration atomic.Value
+	informer      genericmanager.SingleClusterInformerManager
 	lister        cache.GenericLister
 	initialSynced atomic.Bool
 }
@@ -63,61 +64,68 @@ func (m *interpreterConfigManager) HasSynced() bool {
 		return true
 	}
 
-	if configuration, err := m.lister.List(labels.Everything()); err == nil && len(configuration) == 0 {
-		// the empty list we initially stored is valid to use.
-		// Setting initialSynced to true, so subsequent checks
-		// would be able to take the fast path on the atomic boolean in a
-		// cluster without any webhooks configured.
-		m.initialSynced.Store(true)
-		// the informer has synced, and we don't have any items
-		return true
+	err := m.updateConfiguration()
+	if err != nil {
+		klog.ErrorS(err, "error updating configuration")
+		return false
 	}
-	return false
+	return true
 }
 
 // NewExploreConfigManager return a new interpreterConfigManager with resourceinterpreterwebhookconfigurations handlers.
 func NewExploreConfigManager(inform genericmanager.SingleClusterInformerManager) ConfigManager {
 	manager := &interpreterConfigManager{
-		lister: inform.Lister(resourceExploringWebhookConfigurationsGVR),
+		lister: inform.Lister(util.ResourceInterpreterWebhookConfigurationsGVR),
 	}
 
 	manager.configuration.Store([]WebhookAccessor{})
 
+	manager.informer = inform
 	configHandlers := fedinformer.NewHandlerOnEvents(
-		func(_ interface{}) { manager.updateConfiguration() },
-		func(_, _ interface{}) { manager.updateConfiguration() },
-		func(_ interface{}) { manager.updateConfiguration() })
-	inform.ForResource(resourceExploringWebhookConfigurationsGVR, configHandlers)
+		func(_ interface{}) { _ = manager.updateConfiguration() },
+		func(_, _ interface{}) { _ = manager.updateConfiguration() },
+		func(_ interface{}) { _ = manager.updateConfiguration() })
+	inform.ForResource(util.ResourceInterpreterWebhookConfigurationsGVR, configHandlers)
 
 	return manager
 }
 
-func (m *interpreterConfigManager) updateConfiguration() {
+// updateConfiguration is used as the event handler for the ResourceInterpreterWebhookConfiguration resource.
+// Any changes (add, update, delete) to these resources will trigger this method, which loads all
+// ResourceInterpreterWebhookConfiguration resources and refreshes the internal cache accordingly.
+// Note: During startup, some events may be missed if the informer has not yet synced. If all events
+// are missed during startup, updateConfiguration will be called when HasSynced() is invoked for the
+// first time, ensuring the cache is updated on first use.
+func (m *interpreterConfigManager) updateConfiguration() error {
+	if m.informer == nil {
+		return errors.New("informer manager is not configured")
+	}
+	if !m.informer.IsInformerSynced(util.ResourceInterpreterWebhookConfigurationsGVR) {
+		return errors.New("informer of ResourceInterpreterWebhookConfiguration not synced")
+	}
+
 	configurations, err := m.lister.List(labels.Everything())
 	if err != nil {
-		utilruntime.HandleError(fmt.Errorf("error updating configuration: %v", err))
-		return
+		return err
 	}
 
-	configs := make([]*configv1alpha1.ResourceInterpreterWebhookConfiguration, 0)
-	for _, c := range configurations {
-		unstructuredConfig, err := helper.ToUnstructured(c)
-		if err != nil {
-			klog.Errorf("Failed to transform ResourceInterpreterWebhookConfiguration: %v", err)
-			return
-		}
-
+	configs := make([]*configv1alpha1.ResourceInterpreterWebhookConfiguration, len(configurations))
+	for index, c := range configurations {
 		config := &configv1alpha1.ResourceInterpreterWebhookConfiguration{}
-		err = helper.ConvertToTypedObject(unstructuredConfig, config)
+		err = helper.ConvertToTypedObject(c, config)
 		if err != nil {
-			gvk := unstructuredConfig.GroupVersionKind().String()
-			klog.Errorf("Failed to convert object(%s), err: %v", gvk, err)
-			return
+			return err
 		}
-		configs = append(configs, config)
+		configs[index] = config
 	}
 
-	m.configuration.Store(mergeResourceExploreWebhookConfigurations(configs))
+	m.LoadConfig(configs)
+	return nil
+}
+
+// LoadConfig loads the webhook configurations and updates the initialSynced flag to true.
+func (m *interpreterConfigManager) LoadConfig(webhookConfigurations []*configv1alpha1.ResourceInterpreterWebhookConfiguration) {
+	m.configuration.Store(mergeResourceExploreWebhookConfigurations(webhookConfigurations))
 	m.initialSynced.Store(true)
 }
 
diff --git a/pkg/resourceinterpreter/customized/webhook/customized.go b/pkg/resourceinterpreter/customized/webhook/customized.go
index a0ca61604..5a7874b43 100644
--- a/pkg/resourceinterpreter/customized/webhook/customized.go
+++ b/pkg/resourceinterpreter/customized/webhook/customized.go
@@ -355,3 +355,8 @@ func (e *CustomizedInterpreter) InterpretHealth(ctx context.Context, attributes
 
 	return response.Healthy, matched, nil
 }
+
+// LoadConfig loads the webhook configurations.
+func (e *CustomizedInterpreter) LoadConfig(webhookConfigurations []*configv1alpha1.ResourceInterpreterWebhookConfiguration) {
+	e.hookManager.LoadConfig(webhookConfigurations)
+}
diff --git a/pkg/resourceinterpreter/default/native/default.go b/pkg/resourceinterpreter/default/native/default.go
index 36c670016..99f290986 100644
--- a/pkg/resourceinterpreter/default/native/default.go
+++ b/pkg/resourceinterpreter/default/native/default.go
@@ -17,6 +17,7 @@ limitations under the License.
 package native
 
 import (
+	"errors"
 	"fmt"
 
 	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
@@ -110,6 +111,11 @@ func (e *DefaultInterpreter) ReviseReplica(object *unstructured.Unstructured, re
 	return handler(object, replica)
 }
 
+// GetComponents returns the resource requirements for multiple components from the given object.
+func (e *DefaultInterpreter) GetComponents(_ *unstructured.Unstructured) ([]workv1alpha2.ComponentRequirements, error) {
+	return nil, errors.New("no plan to implement this method yet")
+}
+
 // Retain returns the objects that based on the "desired" object but with values retained from the "observed" object.
 func (e *DefaultInterpreter) Retain(desired *unstructured.Unstructured, observed *unstructured.Unstructured) (retained *unstructured.Unstructured, err error) {
 	klog.V(4).Infof("Retain object: %v %s/%s with build-in interpreter.", desired.GroupVersionKind(), desired.GetNamespace(), desired.GetName())
diff --git a/pkg/resourceinterpreter/default/thirdparty/thirdparty.go b/pkg/resourceinterpreter/default/thirdparty/thirdparty.go
index 11dbff462..bcc9b935d 100644
--- a/pkg/resourceinterpreter/default/thirdparty/thirdparty.go
+++ b/pkg/resourceinterpreter/default/thirdparty/thirdparty.go
@@ -56,6 +56,8 @@ func (p *ConfigurableInterpreter) HookEnabled(kind schema.GroupVersionKind, oper
 		script = customAccessor.GetHealthInterpretationLuaScript()
 	case configv1alpha1.InterpreterOperationInterpretReplica:
 		script = customAccessor.GetReplicaResourceLuaScript()
+	case configv1alpha1.InterpreterOperationInterpretComponent:
+		script = customAccessor.GetComponentResourceLuaScript()
 	case configv1alpha1.InterpreterOperationInterpretStatus:
 		script = customAccessor.GetStatusReflectionLuaScript()
 	case configv1alpha1.InterpreterOperationRetain:
@@ -85,6 +87,25 @@ func (p *ConfigurableInterpreter) GetReplicas(object *unstructured.Unstructured)
 	return
 }
 
+// GetComponents returns the desired components of the object.
+func (p *ConfigurableInterpreter) GetComponents(object *unstructured.Unstructured) (components []workv1alpha2.ComponentRequirements, enabled bool, err error) {
+	klog.V(4).Infof("Get components for object: %v %s/%s with thirdparty configurable interpreter.", object.GroupVersionKind(), object.GetNamespace(), object.GetName())
+
+	customAccessor, enabled := p.getCustomAccessor(object.GroupVersionKind())
+	if !enabled {
+		return
+	}
+
+	script := customAccessor.GetComponentResourceLuaScript()
+	if len(script) == 0 {
+		enabled = false
+		return
+	}
+
+	components, err = p.luaVM.GetComponents(object, script)
+	return
+}
+
 // ReviseReplica revises the replica of the given object.
 func (p *ConfigurableInterpreter) ReviseReplica(object *unstructured.Unstructured, replica int64) (revised *unstructured.Unstructured, enabled bool, err error) {
 	klog.V(4).Infof("Revise replicas for object: %v %s/%s with thirdparty configurable interpreter.", object.GroupVersionKind(), object.GetNamespace(), object.GetName())
diff --git a/pkg/resourceinterpreter/interpreter.go b/pkg/resourceinterpreter/interpreter.go
index e82d66c9a..3130a5240 100644
--- a/pkg/resourceinterpreter/interpreter.go
+++ b/pkg/resourceinterpreter/interpreter.go
@@ -18,8 +18,10 @@ package resourceinterpreter
 
 import (
 	"context"
+	"errors"
 
 	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
+	"k8s.io/apimachinery/pkg/labels"
 	"k8s.io/apimachinery/pkg/runtime"
 	"k8s.io/apimachinery/pkg/runtime/schema"
 	corev1 "k8s.io/client-go/listers/core/v1"
@@ -32,12 +34,14 @@ import (
 	"github.com/karmada-io/karmada/pkg/resourceinterpreter/customized/webhook/request"
 	"github.com/karmada-io/karmada/pkg/resourceinterpreter/default/native"
 	"github.com/karmada-io/karmada/pkg/resourceinterpreter/default/thirdparty"
+	"github.com/karmada-io/karmada/pkg/util"
 	"github.com/karmada-io/karmada/pkg/util/fedinformer/genericmanager"
+	"github.com/karmada-io/karmada/pkg/util/helper"
 )
 
 // ResourceInterpreter manages both default and customized webhooks to interpret custom resource structure.
 type ResourceInterpreter interface {
-	// Start starts running the component and will never stop running until the context is closed or an error occurs.
+	// Start initializes the resource interpreter and performs cache synchronization.
 	Start(ctx context.Context) (err error)
 
 	// HookEnabled tells if any hook exist for specific resource type and operation.
@@ -49,6 +53,15 @@ type ResourceInterpreter interface {
 	// ReviseReplica revises the replica of the given object.
 	ReviseReplica(object *unstructured.Unstructured, replica int64) (*unstructured.Unstructured, error)
 
+	// GetComponents extracts the resource requirements for multiple components from the given object.
+	// This hook is designed for CRDs with multiple components (e.g., FlinkDeployment), but can
+	// also be used for single-component resources like Deployment.
+	// If implemented, the controller will use this hook to obtain per-component replica and resource
+	// requirements, and will not call GetReplicas.
+	// If not implemented, the controller will fall back to GetReplicas for backward compatibility.
+	// This hook will only be called when the feature gate 'MultiplePodTemplatesScheduling' is enabled.
+	GetComponents(object *unstructured.Unstructured) (components []workv1alpha2.ComponentRequirements, err error)
+
 	// Retain returns the objects that based on the "desired" object but with values retained from the "observed" object.
 	Retain(desired *unstructured.Unstructured, observed *unstructured.Unstructured) (retained *unstructured.Unstructured, err error)
 
@@ -85,13 +98,16 @@ type customResourceInterpreterImpl struct {
 	defaultInterpreter      *native.DefaultInterpreter
 }
 
-// Start starts running the component and will never stop running until the context is closed or an error occurs.
-func (i *customResourceInterpreterImpl) Start(ctx context.Context) (err error) {
-	klog.Infof("Starting custom resource interpreter.")
+// Start initializes all interpreters and load all ResourceInterpreterCustomization and
+// ResourceInterpreterWebhookConfiguration configurations into the cache.
+// It is recommended to be called before all controllers. After called, the resource interpreter
+// will be ready to interpret custom resources.
+func (i *customResourceInterpreterImpl) Start(_ context.Context) (err error) {
+	klog.Infoln("Starting resource interpreter.")
 
 	i.customizedInterpreter, err = webhook.NewCustomizedInterpreter(i.informer, i.serviceLister)
 	if err != nil {
-		return
+		return err
 	}
 	i.configurableInterpreter = declarative.NewConfigurableInterpreter(i.informer)
 
@@ -100,8 +116,12 @@ func (i *customResourceInterpreterImpl) Start(ctx context.Context) (err error) {
 
 	i.informer.Start()
 	i.informer.WaitForCacheSync()
-	<-ctx.Done()
-	klog.Infof("Stopped as context canceled.")
+
+	if err = i.loadConfig(); err != nil {
+		return err
+	}
+
+	klog.Infoln("Resource interpreter started.")
 	return nil
 }
 
@@ -180,6 +200,35 @@ func (i *customResourceInterpreterImpl) ReviseReplica(object *unstructured.Unstr
 	return i.defaultInterpreter.ReviseReplica(object, replica)
 }
 
+// GetComponents returns the requirements for each component of the given object.
+// This method is intended to extract per-component replica and resource requirements,
+// especially for resources that define multiple components (e.g., CRDs with multiple pod templates).
+func (i *customResourceInterpreterImpl) GetComponents(object *unstructured.Unstructured) ([]workv1alpha2.ComponentRequirements, error) {
+	if object == nil {
+		return nil, errors.New("nil object")
+	}
+
+	components, hookEnabled, err := i.configurableInterpreter.GetComponents(object)
+	if err != nil {
+		return nil, err
+	}
+	if hookEnabled {
+		return components, nil
+	}
+
+	components, hookEnabled, err = i.thirdpartyInterpreter.GetComponents(object)
+	if err != nil {
+		return nil, err
+	}
+	if hookEnabled {
+		return components, nil
+	}
+
+	// TODO(@RainbowMango): Implement GetComponents for extracting per-component replica and resource requirements.
+	// Follow up tracked by: https://github.com/karmada-io/karmada/issues/6641
+	return nil, errors.New("interface GetComponents not implemented yet")
+}
+
 // Retain returns the objects that based on the "desired" object but with values retained from the "observed" object.
 func (i *customResourceInterpreterImpl) Retain(desired *unstructured.Unstructured, observed *unstructured.Unstructured) (*unstructured.Unstructured, error) {
 	obj, hookEnabled, err := i.configurableInterpreter.Retain(desired, observed)
@@ -339,3 +388,46 @@ func (i *customResourceInterpreterImpl) InterpretHealth(object *unstructured.Uns
 	healthy, err = i.defaultInterpreter.InterpretHealth(object)
 	return
 }
+
+// loadConfig loads the full set of ResourceInterpreterCustomization and
+// ResourceInterpreterWebhookConfiguration configurations into the cache. It avoids resource interpreter
+// parsing errors when the resource interpreter starts and the cache is not synchronized.
+func (i *customResourceInterpreterImpl) loadConfig() error {
+	customizations, err := i.informer.Lister(util.ResourceInterpreterCustomizationsGVR).List(labels.Everything())
+	if err != nil {
+		klog.Errorf("Failed to list resourceinterpretercustomizations: %v", err)
+		return err
+	}
+	klog.V(5).Infof("Found %d resourceinterpretercustomizations", len(customizations))
+
+	declareConfigs := make([]*configv1alpha1.ResourceInterpreterCustomization, len(customizations))
+	for index, c := range customizations {
+		config := &configv1alpha1.ResourceInterpreterCustomization{}
+		if err = helper.ConvertToTypedObject(c, config); err != nil {
+			klog.Errorf("Failed to convert resourceinterpretercustomization: %v", err)
+			return err
+		}
+		declareConfigs[index] = config
+	}
+	i.configurableInterpreter.LoadConfig(declareConfigs)
+
+	webhooks, err := i.informer.Lister(util.ResourceInterpreterWebhookConfigurationsGVR).List(labels.Everything())
+	if err != nil {
+		klog.Errorf("Failed to list resourceinterpreterwebhookconfigurations: %v", err)
+		return err
+	}
+	klog.V(5).Infof("Found %d resourceinterpreterwebhookconfigurations", len(webhooks))
+
+	webhookConfigs := make([]*configv1alpha1.ResourceInterpreterWebhookConfiguration, len(webhooks))
+	for index, c := range webhooks {
+		config := &configv1alpha1.ResourceInterpreterWebhookConfiguration{}
+		if err = helper.ConvertToTypedObject(c, config); err != nil {
+			klog.Errorf("Failed to convert resourceinterpreterwebhookconfiguration: %v", err)
+			return err
+		}
+		webhookConfigs[index] = config
+	}
+	i.customizedInterpreter.LoadConfig(webhookConfigs)
+
+	return nil
+}
diff --git a/pkg/scheduler/helper.go b/pkg/scheduler/helper.go
index 14fc632a0..55adcaf69 100644
--- a/pkg/scheduler/helper.go
+++ b/pkg/scheduler/helper.go
@@ -19,8 +19,10 @@ package scheduler
 import (
 	"encoding/json"
 	"errors"
+	"net/http"
 	"reflect"
 
+	apierrors "k8s.io/apimachinery/pkg/api/errors"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	utilerrors "k8s.io/apimachinery/pkg/util/errors"
 	"k8s.io/klog/v2"
@@ -125,6 +127,7 @@ func getConditionByError(err error) (metav1.Condition, bool) {
 	if fitErrMatcher(err) {
 		return util.NewCondition(workv1alpha2.Scheduled, workv1alpha2.BindingReasonNoClusterFit, err.Error(), metav1.ConditionFalse), true
 	}
+
 	var aggregatedErr utilerrors.Aggregate
 	if errors.As(err, &aggregatedErr) {
 		for _, ae := range aggregatedErr.Errors() {
@@ -132,6 +135,11 @@ func getConditionByError(err error) (metav1.Condition, bool) {
 				// if aggregated NoClusterFit error got, we do not ignore error but retry scheduling.
 				return util.NewCondition(workv1alpha2.Scheduled, workv1alpha2.BindingReasonNoClusterFit, err.Error(), metav1.ConditionFalse), false
 			}
+			// ResourceBinding validation webhook will return error with "FederatedResourceQuota" if quota exceeded
+			var statusErr *apierrors.StatusError
+			if errors.As(ae, &statusErr) && statusErr.Status().Code == http.StatusForbidden && statusErr.Status().Reason == util.QuotaExceededReason {
+				return util.NewCondition(workv1alpha2.Scheduled, workv1alpha2.BindingReasonQuotaExceeded, ae.Error(), metav1.ConditionFalse), false
+			}
 		}
 	}
 	return util.NewCondition(workv1alpha2.Scheduled, workv1alpha2.BindingReasonSchedulerError, err.Error(), metav1.ConditionFalse), false
diff --git a/pkg/search/controller.go b/pkg/search/controller.go
index 55071f4f9..8f92f344b 100644
--- a/pkg/search/controller.go
+++ b/pkg/search/controller.go
@@ -134,8 +134,6 @@ func (c *Controller) Start(ctx context.Context) {
 
 	defer runtime.HandleCrash()
 
-	c.informerFactory.WaitForCacheSync(ctx.Done())
-
 	go wait.Until(c.worker, time.Second, ctx.Done())
 
 	go func() {
diff --git a/pkg/util/constants.go b/pkg/util/constants.go
index 762e357ab..c27599cbb 100644
--- a/pkg/util/constants.go
+++ b/pkg/util/constants.go
@@ -20,7 +20,9 @@ import (
 	"time"
 
 	discoveryv1 "k8s.io/api/discovery/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 
+	configv1alpha1 "github.com/karmada-io/karmada/pkg/apis/config/v1alpha1"
 	workv1alpha2 "github.com/karmada-io/karmada/pkg/apis/work/v1alpha2"
 )
 
@@ -54,9 +56,6 @@ const (
 	// managed by karmada controllers.
 	KarmadaSystemLabel = "karmada.io/system"
 
-	// EndpointSliceDispatchControllerLabelValue indicates the endpointSlice are controlled by Karmada
-	EndpointSliceDispatchControllerLabelValue = "endpointslice-dispatch-controller.karmada.io"
-
 	// RetainReplicasLabel is a reserved label to indicate whether the replicas should be retained. e.g:
 	// resourcetemplate.karmada.io/retain-replicas: true   // with value `true` indicates retain
 	// resourcetemplate.karmada.io/retain-replicas: false  // with value `false` and others, indicates not retain
@@ -69,6 +68,7 @@ const (
 	EndpointSliceWorkManagedByLabel = "endpointslice.karmada.io/managed-by"
 )
 
+// Define label values used by Karmada system.
 const (
 	// ManagedByKarmadaLabelValue indicates that these are workloads in member cluster synchronized by karmada controllers.
 	ManagedByKarmadaLabelValue = "true"
@@ -78,6 +78,12 @@ const (
 
 	// RetainReplicasValue is an optional value of RetainReplicasLabel, indicating retain
 	RetainReplicasValue = "true"
+
+	// EndpointSliceDispatchControllerLabelValue indicates the endpointSlice is controlled by Karmada endpointslice-dispatch-controller
+	EndpointSliceDispatchControllerLabelValue = "endpointslice-dispatch-controller.karmada.io"
+
+	// EndpointSliceControllerLabelValue indicates the endpointSlice is controlled by Karmada endpointslice-controller
+	EndpointSliceControllerLabelValue = "endpointslice-controller.karmada.io"
 )
 
 // Define annotations used by karmada system.
@@ -216,6 +222,11 @@ const (
 	CompletionsField = "completions"
 )
 
+const (
+	// QuotaExceededReason is a unique reason to describe QuotaExceeded events
+	QuotaExceededReason metav1.StatusReason = "QuotaExceeded"
+)
+
 // ContextKey is the key of context.
 type ContextKey string
 
@@ -234,6 +245,15 @@ var (
 	EndpointSliceGVK = discoveryv1.SchemeGroupVersion.WithKind("EndpointSlice")
 )
 
+// Define resource group version resource.
+var (
+	// ResourceInterpreterCustomizationsGVR is the GroupVersionResource of ResourceInterpreterCustomizations.
+	ResourceInterpreterCustomizationsGVR = configv1alpha1.SchemeGroupVersion.WithResource("resourceinterpretercustomizations")
+
+	// ResourceInterpreterWebhookConfigurationsGVR is the GroupVersionResource of ResourceInterpreterWebhookConfigurations.
+	ResourceInterpreterWebhookConfigurationsGVR = configv1alpha1.SchemeGroupVersion.WithResource("resourceinterpreterwebhookconfigurations")
+)
+
 const (
 	// DefaultFilePerm default file perm
 	DefaultFilePerm = 0640
diff --git a/pkg/util/finalizer.go b/pkg/util/finalizer.go
new file mode 100644
index 000000000..0c041893d
--- /dev/null
+++ b/pkg/util/finalizer.go
@@ -0,0 +1,30 @@
+/*
+Copyright 2025 The Karmada Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package util
+
+import "k8s.io/apimachinery/pkg/util/sets"
+
+// MergeFinalizers merges the new finalizers into exist finalizers, and deduplicates the finalizers.
+// The result is sorted.
+func MergeFinalizers(existFinalizers, newFinalizers []string) []string {
+	if existFinalizers == nil && newFinalizers == nil {
+		return nil
+	}
+
+	finalizers := sets.New[string](existFinalizers...).Insert(newFinalizers...)
+	return sets.List[string](finalizers)
+}
diff --git a/pkg/util/helper/binding.go b/pkg/util/helper/binding.go
index 133d63815..8712e5c98 100644
--- a/pkg/util/helper/binding.go
+++ b/pkg/util/helper/binding.go
@@ -186,9 +186,13 @@ func ObtainBindingSpecExistingClusters(bindingSpec workv1alpha2.ResourceBindingS
 	}
 
 	for _, task := range bindingSpec.GracefulEvictionTasks {
-		// EvictionTasks with Immediately PurgeMode should not be treated as existing clusters
-		// Work on those clusters should be removed immediately and treated as orphans
-		if task.PurgeMode != policyv1alpha1.Immediately {
+		// EvictionTasks with Directly PurgeMode should not be treated as existing clusters
+		// Work on those clusters should be removed directly and treated as orphans.
+		//
+		//nolint:staticcheck
+		// disable `deprecation` check for backward compatibility.
+		if task.PurgeMode != policyv1alpha1.Immediately &&
+			task.PurgeMode != policyv1alpha1.PurgeModeDirectly {
 			clusterNames.Insert(task.FromCluster)
 		}
 	}
@@ -238,9 +242,6 @@ func RemoveOrphanWorks(ctx context.Context, c client.Client, works []workv1alpha
 }
 
 // FetchResourceTemplate fetches the resource template to be propagated.
-// Any updates to this resource template are not recommended as it may come from the informer cache.
-// We should abide by the principle of making a deep copy first and then modifying it.
-// See issue: https://github.com/karmada-io/karmada/issues/3878.
 func FetchResourceTemplate(
 	ctx context.Context,
 	dynamicClient dynamic.Interface,
@@ -266,12 +267,13 @@ func FetchResourceTemplate(
 		// fall back to call api server in case the cache has not been synchronized yet
 		klog.Warningf("Failed to get resource template (%s/%s/%s) from cache, Error: %v. Fall back to call api server.",
 			resource.Kind, resource.Namespace, resource.Name, err)
-		object, err = dynamicClient.Resource(gvr).Namespace(resource.Namespace).Get(ctx, resource.Name, metav1.GetOptions{})
+		objectFromAPIServer, err := dynamicClient.Resource(gvr).Namespace(resource.Namespace).Get(ctx, resource.Name, metav1.GetOptions{})
 		if err != nil {
 			klog.Errorf("Failed to get resource template (%s/%s/%s) from api server, Error: %v",
 				resource.Kind, resource.Namespace, resource.Name, err)
 			return nil, err
 		}
+		return objectFromAPIServer, nil
 	}
 
 	unstructuredObj, err := ToUnstructured(object)
@@ -280,7 +282,7 @@ func FetchResourceTemplate(
 		return nil, err
 	}
 
-	return unstructuredObj, nil
+	return unstructuredObj.DeepCopy(), nil
 }
 
 // FetchResourceTemplatesByLabelSelector fetches the resource templates by label selector to be propagated.
diff --git a/pkg/util/helper/mcs.go b/pkg/util/helper/mcs.go
index 672151a77..70e6eff64 100644
--- a/pkg/util/helper/mcs.go
+++ b/pkg/util/helper/mcs.go
@@ -141,3 +141,12 @@ func GetConsumerClusters(client client.Client, mcs *networkingv1alpha1.MultiClus
 	}
 	return allClusters, nil
 }
+
+// IsEndpointSliceManagedByKarmada checks if the EndpointSlice is managed by Karmada.
+func IsEndpointSliceManagedByKarmada(epsLabels map[string]string) bool {
+	switch util.GetLabelValue(epsLabels, discoveryv1.LabelManagedBy) {
+	case util.EndpointSliceDispatchControllerLabelValue, util.EndpointSliceControllerLabelValue:
+		return true
+	}
+	return false
+}
diff --git a/pkg/util/helper/policy.go b/pkg/util/helper/policy.go
index ad1a53485..afdb67a07 100644
--- a/pkg/util/helper/policy.go
+++ b/pkg/util/helper/policy.go
@@ -128,7 +128,10 @@ func SetReplicaDivisionPreferenceWeighted(placement *policyv1alpha1.Placement) {
 
 // SetDefaultGracePeriodSeconds sets the default value of GracePeriodSeconds in ApplicationFailoverBehavior
 func SetDefaultGracePeriodSeconds(behavior *policyv1alpha1.ApplicationFailoverBehavior) {
-	if behavior.PurgeMode == policyv1alpha1.Graciously && behavior.GracePeriodSeconds == nil {
+	//nolint:staticcheck
+	// disable `deprecation` check for backward compatibility.
+	if (behavior.PurgeMode == policyv1alpha1.Graciously || behavior.PurgeMode == policyv1alpha1.PurgeModeGracefully) &&
+		behavior.GracePeriodSeconds == nil {
 		behavior.GracePeriodSeconds = ptr.To[int32](600)
 	}
 }
diff --git a/pkg/util/selector.go b/pkg/util/selector.go
index 83a152747..adb5a77c7 100644
--- a/pkg/util/selector.go
+++ b/pkg/util/selector.go
@@ -239,3 +239,24 @@ func matchZones(zoneMatchExpression *corev1.NodeSelectorRequirement, zones []str
 		return false
 	}
 }
+
+// ExtractUniqueNamespacedSelectors returns a new slice of ResourceSelector deduplicated by
+// APIVersion, Kind and Namespace. The returned selectors only contain APIVersion, Kind and Namespace;
+// other fields (e.g. Name, LabelSelector) are intentionally discarded.
+func ExtractUniqueNamespacedSelectors(selectors []policyv1alpha1.ResourceSelector) []policyv1alpha1.ResourceSelector {
+	var results []policyv1alpha1.ResourceSelector
+	handled := make(map[string]bool)
+	for _, selector := range selectors {
+		key := selector.APIVersion + "|" + selector.Kind + "|" + selector.Namespace
+		if handled[key] {
+			continue
+		}
+		results = append(results, policyv1alpha1.ResourceSelector{
+			APIVersion: selector.APIVersion,
+			Kind:       selector.Kind,
+			Namespace:  selector.Namespace,
+		})
+		handled[key] = true
+	}
+	return results
+}
diff --git a/pkg/util/validation/validation.go b/pkg/util/validation/validation.go
index 191c8754d..a0a460a95 100644
--- a/pkg/util/validation/validation.go
+++ b/pkg/util/validation/validation.go
@@ -211,6 +211,7 @@ func ValidateFailover(failoverBehavior *policyv1alpha1.FailoverBehavior, fldPath
 	}
 
 	allErrs = append(allErrs, ValidateApplicationFailover(failoverBehavior.Application, fldPath.Child("application"))...)
+	allErrs = append(allErrs, validateClusterFailover(failoverBehavior.Cluster, fldPath.Child("cluster"))...)
 	return allErrs
 }
 
@@ -226,21 +227,65 @@ func ValidateApplicationFailover(applicationFailoverBehavior *policyv1alpha1.App
 		allErrs = append(allErrs, field.Invalid(fldPath.Child("decisionConditions").Child("tolerationSeconds"), *applicationFailoverBehavior.DecisionConditions.TolerationSeconds, "must be greater than or equal to 0"))
 	}
 
-	if applicationFailoverBehavior.PurgeMode != policyv1alpha1.Graciously && applicationFailoverBehavior.GracePeriodSeconds != nil {
-		allErrs = append(allErrs, field.Invalid(fldPath.Child("gracePeriodSeconds"), *applicationFailoverBehavior.GracePeriodSeconds, "only takes effect when purgeMode is graciously"))
+	//nolint:staticcheck
+	// disable `deprecation` check for backward compatibility.
+	if applicationFailoverBehavior.PurgeMode != policyv1alpha1.Graciously &&
+		applicationFailoverBehavior.PurgeMode != policyv1alpha1.PurgeModeGracefully &&
+		applicationFailoverBehavior.GracePeriodSeconds != nil {
+		allErrs = append(allErrs, field.Invalid(fldPath.Child("gracePeriodSeconds"), *applicationFailoverBehavior.GracePeriodSeconds, "only takes effect when purgeMode is gracefully"))
 	}
 
-	if applicationFailoverBehavior.PurgeMode == policyv1alpha1.Graciously && applicationFailoverBehavior.GracePeriodSeconds == nil {
-		allErrs = append(allErrs, field.Invalid(fldPath.Child("gracePeriodSeconds"), applicationFailoverBehavior.GracePeriodSeconds, "should not be empty when purgeMode is graciously"))
+	//nolint:staticcheck
+	// disable `deprecation` check for backward compatibility.
+	if (applicationFailoverBehavior.PurgeMode == policyv1alpha1.Graciously ||
+		applicationFailoverBehavior.PurgeMode == policyv1alpha1.PurgeModeGracefully) &&
+		applicationFailoverBehavior.GracePeriodSeconds == nil {
+		allErrs = append(allErrs, field.Invalid(fldPath.Child("gracePeriodSeconds"), applicationFailoverBehavior.GracePeriodSeconds, "should not be empty when purgeMode is gracefully"))
 	}
 
 	if applicationFailoverBehavior.GracePeriodSeconds != nil && *applicationFailoverBehavior.GracePeriodSeconds <= 0 {
 		allErrs = append(allErrs, field.Invalid(fldPath.Child("gracePeriodSeconds"), *applicationFailoverBehavior.GracePeriodSeconds, "must be greater than 0"))
 	}
 
+	if applicationFailoverBehavior.StatePreservation != nil {
+		allErrs = append(allErrs, validateStatePreservation(applicationFailoverBehavior.StatePreservation, fldPath.Child("statePreservation"))...)
+	}
+
+	return allErrs
+}
+
+func validateClusterFailover(clusterFailoverBehavior *policyv1alpha1.ClusterFailoverBehavior, fldPath *field.Path) field.ErrorList {
+	var allErrs field.ErrorList
+
+	if clusterFailoverBehavior == nil {
+		return nil
+	}
+
+	if clusterFailoverBehavior.StatePreservation != nil {
+		allErrs = append(allErrs, validateStatePreservation(clusterFailoverBehavior.StatePreservation, fldPath.Child("statePreservation"))...)
+	}
+
+	return allErrs
+}
+
+func validateStatePreservation(statePreservation *policyv1alpha1.StatePreservation, fldPath *field.Path) field.ErrorList {
+	var allErrs field.ErrorList
+
+	if statePreservation == nil {
+		return nil
+	}
+
+	for index, rule := range statePreservation.Rules {
+		allErrs = append(allErrs, validateStatePreservationRule(rule, fldPath.Child("rules").Index(index))...)
+	}
+
 	return allErrs
 }
 
+func validateStatePreservationRule(rule policyv1alpha1.StatePreservationRule, fldPath *field.Path) field.ErrorList {
+	return metav1validation.ValidateLabelName(rule.AliasLabelName, fldPath.Child("aliasLabelName"))
+}
+
 // ValidateOverrideSpec validates that the overrider specification is correctly defined.
 func ValidateOverrideSpec(overrideSpec *policyv1alpha1.OverrideSpec) field.ErrorList {
 	var allErrs field.ErrorList
diff --git a/pkg/webhook/interpreter/http.go b/pkg/webhook/interpreter/http.go
index 809f73838..9f49509e3 100644
--- a/pkg/webhook/interpreter/http.go
+++ b/pkg/webhook/interpreter/http.go
@@ -97,8 +97,17 @@ func (wh *Webhook) writeResponse(w io.Writer, response Response) {
 // writeResourceInterpreterResponse writes ar to w.
 func (wh *Webhook) writeResourceInterpreterResponse(w io.Writer, interpreterContext configv1alpha1.ResourceInterpreterContext) {
 	if err := json.NewEncoder(w).Encode(interpreterContext); err != nil {
-		klog.Errorf("unable to encode the response: %v", err)
-		wh.writeResponse(w, Errored(http.StatusInternalServerError, err))
+		klog.Errorf("unable to encode and write the response: %v", err)
+		// Since the `ar configv1alpha1.ResourceInterpreterContext` is a clear and legal object,
+		// it should not have problem to be marshalled into bytes.
+		// The error here is probably caused by the abnormal HTTP connection,
+		// e.g., broken pipe, so we can only write the error response once,
+		// to avoid endless circular calling.
+		// More info: https://github.com/kubernetes-sigs/controller-runtime/pull/1930
+		serverError := Errored(http.StatusInternalServerError, err)
+		if err = json.NewEncoder(w).Encode(configv1alpha1.ResourceInterpreterContext{Response: &serverError.ResourceInterpreterResponse}); err != nil {
+			klog.Errorf("still unable to encode and write the InternalServerError response: %v", err)
+		}
 	} else {
 		response := interpreterContext.Response
 		if response.Successful {
diff --git a/pkg/webhook/resourcebinding/validating.go b/pkg/webhook/resourcebinding/validating.go
index e63bb28bd..5284c5aea 100644
--- a/pkg/webhook/resourcebinding/validating.go
+++ b/pkg/webhook/resourcebinding/validating.go
@@ -49,28 +49,58 @@ var _ admission.Handler = &ValidatingAdmission{}
 type frqProcessOutcome struct {
 	validationMessages []string
 	updatedFRQCount    int
-	noFRQsFound        bool                // True if this attempt found no FRQs
-	earlyExitResponse  *admission.Response // Response if this attempt decided to exit early (denial, permanent error)
-	ProcessError       error               // Error for RetryOnConflict (e.g., conflict error or nil for success/final decision)
+	noFRQsFound        bool  // True if this attempt found no FRQs
+	earlyExitError     error // Response if this attempt decided to exit early (denial, permanent error)
+	ProcessError       error // Error for RetryOnConflict (e.g., conflict error or nil for success/final decision)
 }
 
 // Handle implements admission.Handler interface.
 func (v *ValidatingAdmission) Handle(ctx context.Context, req admission.Request) admission.Response {
+	rb, oldRB, err := v.decodeRBs(req)
+	if err != nil {
+		return admission.Errored(http.StatusBadRequest, err)
+	}
+	klog.V(2).Infof("Processing ResourceBinding(%s/%s) for request: %s (%s)", rb.Namespace, rb.Name, req.Operation, req.UID)
+
+	if err := v.validateFederatedResourceQuota(ctx, req, rb, oldRB); err != nil {
+		if apierrors.IsInternalError(err) {
+			klog.Errorf("Internal error while processing ResourceBinding %s/%s: %v", rb.Namespace, rb.Name, err)
+			return admission.Errored(http.StatusInternalServerError, err)
+		}
+		klog.Errorf("Admission denied for ResourceBinding %s/%s: %v", rb.Namespace, rb.Name, err)
+		return admission.Denied(err.Error())
+	}
+
+	// further validation can be added here if needed
+
+	return admission.Allowed("")
+}
+
+// validateFederatedResourceQuota checks the FederatedResourceQuota for the ResourceBinding.
+// It returns a list of errors if validation fails, or nil if it passes.
+func (v *ValidatingAdmission) validateFederatedResourceQuota(ctx context.Context, req admission.Request, rb, oldRB *workv1alpha2.ResourceBinding) error {
 	if !features.FeatureGate.Enabled(features.FederatedQuotaEnforcement) {
 		klog.V(5).Infof("FederatedQuotaEnforcement feature gate is disabled, skipping validation for ResourceBinding %s/%s", req.Namespace, req.Name)
-		return admission.Allowed("")
+		return nil
 	}
 
-	isDryRun := req.DryRun != nil && *req.DryRun
+	if req.Operation == admissionv1.Create && len(rb.Spec.Clusters) == 0 {
+		klog.V(4).Infof("ResourceBinding %s/%s is being created but not yet scheduled, skipping quota validation.", rb.Namespace, rb.Name)
+		return nil
+	}
 
-	rb, oldRB, extractResp := v.decodeAndValidateRBs(req)
-	if extractResp != nil {
-		return *extractResp
+	if req.Operation == admissionv1.Update {
+		if !isQuotaRelevantFieldChanged(oldRB, rb) {
+			klog.V(4).Infof("ResourceBinding %s/%s updated, but no quota relevant fields changed, skipping quota validation.", rb.Namespace, rb.Name)
+			return nil
+		}
 	}
 
-	newRbTotalUsage, oldRbTotalUsage, respCalc := v.calculateRBUsages(rb, oldRB)
-	if respCalc != nil {
-		return *respCalc
+	isDryRun := req.DryRun != nil && *req.DryRun
+
+	newRbTotalUsage, oldRbTotalUsage, err := v.calculateRBUsages(rb, oldRB)
+	if err != nil {
+		return err
 	}
 
 	totalRbDelta := calculateDelta(newRbTotalUsage, oldRbTotalUsage)
@@ -78,12 +108,11 @@ func (v *ValidatingAdmission) Handle(ctx context.Context, req admission.Request)
 
 	if len(totalRbDelta) == 0 {
 		klog.V(2).Infof("No effective resource quantity delta for ResourceBinding %s/%s. Skipping quota validation.", rb.Namespace, rb.Name)
-		return admission.Allowed("No effective resource quantity delta for ResourceBinding, skipping quota validation.")
+		return nil
 	}
 
-	frqOutcome := v.processFRQsWithRetries(ctx, rb, totalRbDelta, isDryRun)
-
-	return v.finalizeResponse(rb, frqOutcome)
+	outcome := v.processFRQsWithRetries(ctx, rb, totalRbDelta, isDryRun)
+	return v.handleFRQOutcome(rb, outcome)
 }
 
 func (v *ValidatingAdmission) processFRQsWithRetries(ctx context.Context, rb *workv1alpha2.ResourceBinding, totalRbDelta corev1.ResourceList, isDryRun bool) frqProcessOutcome {
@@ -107,9 +136,9 @@ func (v *ValidatingAdmission) executeFRQProcessingAttempt(ctx context.Context, r
 	var currentFrqsToUpdateStatus []*policyv1alpha1.FederatedResourceQuota
 	var currentValidationMessages []string
 
-	frqList, listResp := v.listFRQs(ctx, rb.Namespace)
-	if listResp != nil {
-		outcome.earlyExitResponse = listResp
+	frqList, listError := v.listFRQs(ctx, rb.Namespace)
+	if listError != nil {
+		outcome.earlyExitError = listError
 		return outcome // attemptError is nil, signaling non-retryable handled error
 	}
 
@@ -120,9 +149,9 @@ func (v *ValidatingAdmission) executeFRQProcessingAttempt(ctx context.Context, r
 	}
 
 	for _, frqItem := range frqList.Items {
-		newStatus, msg, denialResp := v.processSingleFRQ(frqItem, rb.Namespace, rb.Name, totalRbDelta)
-		if denialResp != nil {
-			outcome.earlyExitResponse = denialResp
+		newStatus, msg, denialError := v.processSingleFRQ(&frqItem, rb.Namespace, rb.Name, totalRbDelta)
+		if denialError != nil {
+			outcome.earlyExitError = denialError
 			return outcome // attemptError is nil, request denied
 		}
 		if newStatus != nil {
@@ -144,8 +173,7 @@ func (v *ValidatingAdmission) executeFRQProcessingAttempt(ctx context.Context, r
 		}
 		errMsg := fmt.Sprintf("permanent error updating FRQ statuses for RB %s/%s: %v", rb.Namespace, rb.Name, updateErr)
 		klog.Error(errMsg)
-		resp := admission.Errored(http.StatusInternalServerError, errors.New(errMsg))
-		outcome.earlyExitResponse = &resp
+		outcome.earlyExitError = apierrors.NewInternalError(errors.New(errMsg))
 		return outcome // attemptError is nil, non-retryable handled error
 	}
 
@@ -154,20 +182,20 @@ func (v *ValidatingAdmission) executeFRQProcessingAttempt(ctx context.Context, r
 	return outcome // attemptError is nil, successful attempt
 }
 
-func (v *ValidatingAdmission) finalizeResponse(rb *workv1alpha2.ResourceBinding, outcome frqProcessOutcome) admission.Response {
+func (v *ValidatingAdmission) handleFRQOutcome(rb *workv1alpha2.ResourceBinding, outcome frqProcessOutcome) error {
 	if outcome.ProcessError != nil {
 		errMsg := fmt.Sprintf("failed to apply FederatedResourceQuota updates after multiple retries for RB %s/%s: %v", rb.Namespace, rb.Name, outcome.ProcessError)
 		klog.Error(errMsg)
-		return admission.Errored(http.StatusInternalServerError, errors.New(errMsg))
+		return apierrors.NewInternalError(errors.New(errMsg))
 	}
 
-	if outcome.earlyExitResponse != nil {
-		return *outcome.earlyExitResponse
+	if outcome.earlyExitError != nil {
+		return outcome.earlyExitError
 	}
 
 	if outcome.noFRQsFound {
 		klog.V(2).Infof("No FederatedResourceQuotas found in namespace %s for ResourceBinding %s. Allowing operation.", rb.Namespace, rb.Name)
-		return admission.Allowed("No FederatedResourceQuotas found in the namespace, skipping quota check.")
+		return nil
 	}
 
 	finalMessage := fmt.Sprintf("All relevant FederatedResourceQuota checks passed for ResourceBinding %s/%s.", rb.Namespace, rb.Name)
@@ -180,7 +208,7 @@ func (v *ValidatingAdmission) finalizeResponse(rb *workv1alpha2.ResourceBinding,
 		finalMessage = fmt.Sprintf("%s No FRQs required update.", finalMessage)
 	}
 	klog.V(2).Infof("Admission allowed for ResourceBinding %s/%s: %s", rb.Namespace, rb.Name, finalMessage)
-	return admission.Allowed(finalMessage)
+	return nil
 }
 
 // updateFRQStatusesWithRetrySignal attempts to update FRQ statuses.
@@ -217,53 +245,37 @@ func (v *ValidatingAdmission) updateFRQStatusesWithRetrySignal(ctx context.Conte
 	return nil
 }
 
-// decodeAndValidateRBs decodes current and old (for updates) ResourceBindings,
-// performs essential preliminary checks, and checks for relevant changes in update operations.
-// Returns the RBs or an admission response for early exit.
-func (v *ValidatingAdmission) decodeAndValidateRBs(req admission.Request) (
+// decodeRBs decodes current and old (for updates) ResourceBindings.
+// Returns the RBs or an admission response for early exit on decoding failure.
+func (v *ValidatingAdmission) decodeRBs(req admission.Request) (
 	currentRB *workv1alpha2.ResourceBinding,
-	oldRB *workv1alpha2.ResourceBinding, // Will be nil if not an update or if not applicable
-	earlyExitResp *admission.Response,
+	oldRB *workv1alpha2.ResourceBinding, // Will be nil if not an update
+	earlyExitResp error,
 ) {
 	decodedRB := &workv1alpha2.ResourceBinding{}
 	if err := v.Decoder.Decode(req, decodedRB); err != nil {
 		klog.Errorf("Failed to decode ResourceBinding %s/%s: %v", req.Namespace, req.Name, err)
-		resp := admission.Errored(http.StatusBadRequest, err)
-		return nil, nil, &resp
+		return nil, nil, err
 	}
-	klog.V(2).Infof("Processing ResourceBinding(%s/%s) for request: %s (%s)", decodedRB.Namespace, decodedRB.Name, req.Operation, req.UID)
 
-	if req.Operation == admissionv1.Create && len(decodedRB.Spec.Clusters) == 0 {
-		klog.V(4).Infof("ResourceBinding %s/%s is being created but not yet scheduled, skipping quota validation.", decodedRB.Namespace, decodedRB.Name)
-		resp := admission.Allowed("ResourceBinding not yet scheduled for Create operation.")
-		return decodedRB, nil, &resp
+	if req.Operation != admissionv1.Update {
+		return decodedRB, nil, nil
 	}
 
-	var decodedOldRB *workv1alpha2.ResourceBinding
-	if req.Operation == admissionv1.Update {
-		tempOldRB := &workv1alpha2.ResourceBinding{}
-		if err := v.Decoder.DecodeRaw(req.OldObject, tempOldRB); err != nil {
-			klog.Errorf("Failed to decode old ResourceBinding %s/%s: %v", req.Namespace, req.Name, err)
-			resp := admission.Errored(http.StatusBadRequest, err)
-			return decodedRB, nil, &resp
-		}
-		decodedOldRB = tempOldRB
-
-		if !isQuotaRelevantFieldChanged(decodedOldRB, decodedRB) {
-			klog.V(4).Infof("ResourceBinding %s/%s updated, but no quota relevant fields changed, skipping quota validation.", decodedRB.Namespace, decodedRB.Name)
-			resp := admission.Allowed("No quota relevant fields changed.")
-			return decodedRB, decodedOldRB, &resp
-		}
+	decodedOldRB := &workv1alpha2.ResourceBinding{}
+	if err := v.Decoder.DecodeRaw(req.OldObject, decodedOldRB); err != nil {
+		klog.Errorf("Failed to decode old ResourceBinding %s/%s: %v", req.Namespace, req.Name, err)
+		return nil, nil, err
 	}
+
 	return decodedRB, decodedOldRB, nil
 }
 
-func (v *ValidatingAdmission) calculateRBUsages(rb, oldRB *workv1alpha2.ResourceBinding) (corev1.ResourceList, corev1.ResourceList, *admission.Response) {
+func (v *ValidatingAdmission) calculateRBUsages(rb, oldRB *workv1alpha2.ResourceBinding) (corev1.ResourceList, corev1.ResourceList, error) {
 	newRbTotalUsage, err := calculateResourceUsage(rb)
 	if err != nil {
 		klog.Errorf("Error calculating resource usage for new ResourceBinding %s/%s: %v", rb.Namespace, rb.Name, err)
-		resp := admission.Errored(http.StatusInternalServerError, err)
-		return nil, nil, &resp
+		return nil, nil, apierrors.NewInternalError(err)
 	}
 	klog.V(4).Infof("Calculated total usage for incoming RB %s/%s: %v", rb.Namespace, rb.Name, newRbTotalUsage)
 
@@ -272,26 +284,24 @@ func (v *ValidatingAdmission) calculateRBUsages(rb, oldRB *workv1alpha2.Resource
 		oldRbTotalUsage, err = calculateResourceUsage(oldRB)
 		if err != nil {
 			klog.Errorf("Error calculating resource usage for old ResourceBinding %s/%s: %v", oldRB.Namespace, oldRB.Name, err)
-			resp := admission.Errored(http.StatusInternalServerError, err)
-			return nil, nil, &resp
+			return nil, nil, apierrors.NewInternalError(err)
 		}
 		klog.V(4).Infof("Calculated total usage for old RB %s/%s: %v", oldRB.Namespace, oldRB.Name, oldRbTotalUsage)
 	}
 	return newRbTotalUsage, oldRbTotalUsage, nil
 }
 
-func (v *ValidatingAdmission) listFRQs(ctx context.Context, namespace string) (*policyv1alpha1.FederatedResourceQuotaList, *admission.Response) {
+func (v *ValidatingAdmission) listFRQs(ctx context.Context, namespace string) (*policyv1alpha1.FederatedResourceQuotaList, error) {
 	frqList := &policyv1alpha1.FederatedResourceQuotaList{}
 	if err := v.Client.List(ctx, frqList, client.InNamespace(namespace)); err != nil {
 		klog.Errorf("Failed to list FederatedResourceQuotas in namespace %s: %v", namespace, err)
-		resp := admission.Errored(http.StatusInternalServerError, fmt.Errorf("failed to list FederatedResourceQuotas: %w", err))
-		return nil, &resp
+		return nil, apierrors.NewInternalError(fmt.Errorf("failed to list FederatedResourceQuotas: %w", err))
 	}
 	klog.V(2).Infof("Found %d FederatedResourceQuotas in namespace %s to evaluate.", len(frqList.Items), namespace)
 	return frqList, nil
 }
 
-func (v *ValidatingAdmission) processSingleFRQ(frqItem policyv1alpha1.FederatedResourceQuota, rbNamespace, rbName string, totalRbDelta corev1.ResourceList) (newStatusToSet corev1.ResourceList, validationMsg string, denialResp *admission.Response) {
+func (v *ValidatingAdmission) processSingleFRQ(frqItem *policyv1alpha1.FederatedResourceQuota, rbNamespace, rbName string, totalRbDelta corev1.ResourceList) (newStatusToSet corev1.ResourceList, validationMsg string, err error) {
 	klog.V(4).Infof("Evaluating FRQ %s/%s for RB %s/%s", frqItem.Namespace, frqItem.Name, rbNamespace, rbName)
 	if frqItem.Spec.Overall == nil {
 		klog.V(4).Infof("FRQ %s/%s has no spec.overall defined, skipping its evaluation.", frqItem.Namespace, frqItem.Name)
@@ -309,12 +319,11 @@ func (v *ValidatingAdmission) processSingleFRQ(frqItem policyv1alpha1.FederatedR
 
 	potentialNewOverallUsedForThisFRQ := addResourceLists(frqItem.Status.OverallUsed, deltaForThisFRQ)
 
-	if !isAllowed(potentialNewOverallUsedForThisFRQ, frqItem.Spec.Overall) {
-		errMsg := fmt.Sprintf("Quota exceeded for FederatedResourceQuota %s/%s. ResourceBinding %s/%s will be denied.",
-			frqItem.Namespace, frqItem.Name, rbNamespace, rbName)
-		klog.Warning(errMsg)
-		resp := admission.Denied(errMsg)
-		return nil, "", &resp
+	isAllowed, errMsg := isAllowed(potentialNewOverallUsedForThisFRQ, frqItem)
+	if !isAllowed {
+		klog.Warningf("Quota exceeded for FederatedResourceQuota %s/%s. ResourceBinding %s/%s will be denied. %s",
+			frqItem.Namespace, frqItem.Name, rbNamespace, rbName, errMsg)
+		return nil, "", errors.New(errMsg)
 	}
 
 	msg := fmt.Sprintf("Quota check passed for FRQ %s/%s.", frqItem.Namespace, frqItem.Name)
@@ -437,9 +446,10 @@ func addResourceLists(list1, list2 corev1.ResourceList) corev1.ResourceList {
 	return result
 }
 
-func isAllowed(requested, allowedLimits corev1.ResourceList) bool {
+func isAllowed(requested corev1.ResourceList, frqItem *policyv1alpha1.FederatedResourceQuota) (bool, string) {
+	allowedLimits := frqItem.Spec.Overall
 	if allowedLimits == nil {
-		return true
+		return true, ""
 	}
 	for name, reqQty := range requested {
 		if reqQty.IsZero() {
@@ -451,11 +461,12 @@ func isAllowed(requested, allowedLimits corev1.ResourceList) bool {
 			continue
 		}
 		if reqQty.Cmp(limitQty) > 0 {
-			klog.Warningf("Quota exceeded for resource %s: requested sum %s, limit %s", name, reqQty.String(), limitQty.String())
-			return false
+			msg := fmt.Sprintf("FederatedResourceQuota(%s/%s) exceeded for resource %s: requested sum %s, limit %s.", frqItem.Namespace, frqItem.Name, name, reqQty.String(), limitQty.String())
+			klog.Warning(msg)
+			return false, msg
 		}
 	}
-	return true
+	return true, ""
 }
 
 func filterResourceListByKeys(original corev1.ResourceList, filterKeySource corev1.ResourceList) corev1.ResourceList {



Test patch:
diff --git a/operator/pkg/util/util_test.go b/operator/pkg/util/util_test.go
index b1512c1ad..ff0f7068c 100644
--- a/operator/pkg/util/util_test.go
+++ b/operator/pkg/util/util_test.go
@@ -18,22 +18,21 @@ package util
 
 import (
 	"archive/tar"
-	"bytes"
 	"compress/gzip"
 	"errors"
 	"fmt"
 	"io"
-	"net/http"
 	"os"
 	"path/filepath"
 	"regexp"
 	"strings"
 	"testing"
-	"time"
 
 	"github.com/stretchr/testify/assert"
 	"k8s.io/utils/ptr"
 	"sigs.k8s.io/yaml"
+
+	"github.com/karmada-io/karmada/operator/pkg/apis/operator/v1alpha1"
 )
 
 // mockReader is a simple io.Reader that returns an error after being called.
@@ -51,18 +50,6 @@ func (m *mockReader) Read(p []byte) (n int, err error) {
 	return n, m.err
 }
 
-type mockRoundTripper struct {
-	response *http.Response
-	err      error
-}
-
-func (m *mockRoundTripper) RoundTrip(*http.Request) (*http.Response, error) {
-	if m.err != nil {
-		return nil, m.err
-	}
-	return m.response, nil
-}
-
 func TestRead(t *testing.T) {
 	tests := []struct {
 		name       string
@@ -137,74 +124,37 @@ func TestRead(t *testing.T) {
 
 func TestDownloadFile(t *testing.T) {
 	tests := []struct {
-		name     string
-		url      string
-		filePath string
-		prep     func(url, filePath string) error
-		verify   func(filePath string) error
-		wantErr  bool
-		errMsg   string
+		name        string
+		url         string
+		proxyConfig *v1alpha1.ProxyConfig
+		filePath    string
+		verify      func(filePath string) error
+		wantErr     bool
+		errMsg      string
 	}{
 		{
-			name: "DownloadFile_UrlIsNotFound_FailedToGetResponse",
-			url:  "not-found-url",
-			prep: func(url, _ string) error {
-				httpClient = http.Client{
-					Transport: &mockRoundTripper{
-						err: fmt.Errorf("failed to get url %s, url is not found", url),
-					},
-					Timeout: time.Second,
-				}
-				return nil
-			},
-			verify:  func(string) error { return nil },
-			wantErr: true,
-			errMsg:  "failed to get url not-found-url, url is not found",
+			name:        "DownloadFile_UrlIsNotFound_FailedToGetResponse",
+			url:         "not-found-url",
+			proxyConfig: nil,
+			verify:      func(string) error { return nil },
+			wantErr:     true,
+			errMsg:      "failed to get url not-found-url, url is not found",
 		},
 		{
 			name: "DownloadFile_ServiceIsUnavailable_FailedToReachTheService",
 			url:  "https://www.example.com/test-file",
-			prep: func(_, _ string) error {
-				httpClient = http.Client{
-					Transport: &mockRoundTripper{
-						response: &http.Response{
-							StatusCode: http.StatusServiceUnavailable,
-						},
-					},
-					Timeout: time.Second,
-				}
-				return nil
+			proxyConfig: &v1alpha1.ProxyConfig{
+				ProxyURL: "http://www.dummy-proxy.com",
 			},
 			verify:  func(string) error { return nil },
 			wantErr: true,
 			errMsg:  "failed to download file",
 		},
 		{
-			name:     "DownloadFile_FileDownloaded_",
-			url:      "https://www.example.com/test-file",
-			filePath: filepath.Join(os.TempDir(), "temp-download-file.txt"),
-			prep: func(_, filePath string) error {
-				// Create temp download filepath.
-				tempFile, err := os.Create(filePath)
-				if err != nil {
-					return fmt.Errorf("failed to create temp download file: %w", err)
-				}
-				defer tempFile.Close()
-
-				// Create HTTP client.
-				httpClient = http.Client{
-					Transport: &mockRoundTripper{
-						response: &http.Response{
-							StatusCode:    http.StatusOK,
-							Body:          io.NopCloser(bytes.NewReader([]byte("Hello, World!"))),
-							ContentLength: int64(len("Hello, World!")),
-						},
-					},
-					Timeout: time.Second,
-				}
-
-				return nil
-			},
+			name:        "DownloadFile_FileDownloaded_",
+			url:         "https://www.example.com",
+			proxyConfig: nil,
+			filePath:    filepath.Join(os.TempDir(), "temp-download-file.txt"),
 			verify: func(filePath string) error {
 				// Read the content of the downloaded file.
 				content, err := os.ReadFile(filePath)
@@ -212,10 +162,9 @@ func TestDownloadFile(t *testing.T) {
 					return fmt.Errorf("failed   to read file: %w", err)
 				}
 
-				// Verify the content of the file.
-				expected := "Hello, World!"
-				if string(content) != expected {
-					return fmt.Errorf("unexpected file content: got %q, want %q", string(content), expected)
+				// Verify that content has been downloaded and saved to the file
+				if len(content) == 0 {
+					return fmt.Errorf("file is empty: %s", filePath)
 				}
 
 				if err := os.Remove(filePath); err != nil {
@@ -229,19 +178,13 @@ func TestDownloadFile(t *testing.T) {
 	}
 	for _, test := range tests {
 		t.Run(test.name, func(t *testing.T) {
-			if err := test.prep(test.url, test.filePath); err != nil {
-				t.Fatalf("failed to prep before downloading the file, got: %v", err)
-			}
-			err := DownloadFile(test.url, test.filePath)
+			err := DownloadFile(test.url, test.filePath, test.proxyConfig)
 			if err == nil && test.wantErr {
 				t.Fatal("expected an error, but got none")
 			}
 			if err != nil && !test.wantErr {
 				t.Errorf("unexpected error, got: %v", err)
 			}
-			if err != nil && test.wantErr && !strings.Contains(err.Error(), test.errMsg) {
-				t.Errorf("expected error message %s to be in %s", test.errMsg, err.Error())
-			}
 			if err := test.verify(test.filePath); err != nil {
 				t.Errorf("failed to verify the actual of download of file: %v", err)
 			}
diff --git a/pkg/apis/work/v1alpha2/binding_types_helper_test.go b/pkg/apis/work/v1alpha2/binding_types_helper_test.go
index e088fb95c..595c31c85 100644
--- a/pkg/apis/work/v1alpha2/binding_types_helper_test.go
+++ b/pkg/apis/work/v1alpha2/binding_types_helper_test.go
@@ -174,7 +174,7 @@ func TestResourceBindingSpec_GracefulEvictCluster(t *testing.T) {
 			},
 			EvictEvent: GracefulEvictionTask{
 				FromCluster: "m1",
-				PurgeMode:   policyv1alpha1.Immediately,
+				PurgeMode:   policyv1alpha1.PurgeModeDirectly,
 				Reason:      EvictionReasonTaintUntolerated,
 				Message:     "graceful eviction",
 				Producer:    EvictionProducerTaintManager,
@@ -184,7 +184,7 @@ func TestResourceBindingSpec_GracefulEvictCluster(t *testing.T) {
 				GracefulEvictionTasks: []GracefulEvictionTask{
 					{
 						FromCluster: "m1",
-						PurgeMode:   policyv1alpha1.Immediately,
+						PurgeMode:   policyv1alpha1.PurgeModeDirectly,
 						Replicas:    ptr.To[int32](1),
 						Reason:      EvictionReasonTaintUntolerated,
 						Message:     "graceful eviction",
@@ -226,7 +226,7 @@ func TestResourceBindingSpec_GracefulEvictCluster(t *testing.T) {
 			},
 			EvictEvent: GracefulEvictionTask{
 				FromCluster: "m3",
-				PurgeMode:   policyv1alpha1.Graciously,
+				PurgeMode:   policyv1alpha1.PurgeModeGracefully,
 				Reason:      EvictionReasonTaintUntolerated,
 				Message:     "graceful eviction",
 				Producer:    EvictionProducerTaintManager,
@@ -236,7 +236,7 @@ func TestResourceBindingSpec_GracefulEvictCluster(t *testing.T) {
 				GracefulEvictionTasks: []GracefulEvictionTask{
 					{
 						FromCluster: "m3",
-						PurgeMode:   policyv1alpha1.Graciously,
+						PurgeMode:   policyv1alpha1.PurgeModeGracefully,
 						Replicas:    ptr.To[int32](3),
 						Reason:      EvictionReasonTaintUntolerated,
 						Message:     "graceful eviction",
@@ -253,7 +253,7 @@ func TestResourceBindingSpec_GracefulEvictCluster(t *testing.T) {
 			},
 			EvictEvent: GracefulEvictionTask{
 				FromCluster: "m3",
-				PurgeMode:   policyv1alpha1.Graciously,
+				PurgeMode:   policyv1alpha1.PurgeModeGracefully,
 				Reason:      EvictionReasonTaintUntolerated,
 				Message:     "graceful eviction",
 				Producer:    EvictionProducerTaintManager,
@@ -266,7 +266,7 @@ func TestResourceBindingSpec_GracefulEvictCluster(t *testing.T) {
 					},
 					{
 						FromCluster: "m3",
-						PurgeMode:   policyv1alpha1.Graciously,
+						PurgeMode:   policyv1alpha1.PurgeModeGracefully,
 						Replicas:    ptr.To[int32](3),
 						Reason:      EvictionReasonTaintUntolerated,
 						Message:     "graceful eviction",
@@ -296,7 +296,7 @@ func TestResourceBindingSpec_GracefulEvictCluster(t *testing.T) {
 			},
 			EvictEvent: GracefulEvictionTask{
 				FromCluster: "m1",
-				PurgeMode:   policyv1alpha1.Graciously,
+				PurgeMode:   policyv1alpha1.PurgeModeGracefully,
 				Replicas:    ptr.To[int32](1),
 				Reason:      EvictionReasonTaintUntolerated,
 				Message:     "graceful eviction v2",
diff --git a/pkg/controllers/applicationfailover/common_test.go b/pkg/controllers/applicationfailover/common_test.go
index 037fa497f..c49a16ee7 100644
--- a/pkg/controllers/applicationfailover/common_test.go
+++ b/pkg/controllers/applicationfailover/common_test.go
@@ -472,13 +472,13 @@ func Test_buildTaskOptions(t *testing.T) {
 		wantErr assert.ErrorAssertionFunc
 	}{
 		{
-			name: "Graciously purgeMode with ResourceBinding",
+			name: "Gracefully purgeMode with ResourceBinding",
 			args: args{
 				failoverBehavior: &policyv1alpha1.ApplicationFailoverBehavior{
 					DecisionConditions: policyv1alpha1.DecisionConditions{
 						TolerationSeconds: ptr.To[int32](100),
 					},
-					PurgeMode:          policyv1alpha1.Graciously,
+					PurgeMode:          policyv1alpha1.PurgeModeGracefully,
 					GracePeriodSeconds: ptr.To[int32](120),
 					StatePreservation: &policyv1alpha1.StatePreservation{
 						Rules: []policyv1alpha1.StatePreservationRule{
@@ -496,7 +496,7 @@ func Test_buildTaskOptions(t *testing.T) {
 				clustersBeforeFailover: []string{"c0"},
 			},
 			want: *workv1alpha2.NewTaskOptions(
-				workv1alpha2.WithPurgeMode(policyv1alpha1.Graciously),
+				workv1alpha2.WithPurgeMode(policyv1alpha1.PurgeModeGracefully),
 				workv1alpha2.WithProducer(RBApplicationFailoverControllerName),
 				workv1alpha2.WithReason(workv1alpha2.EvictionReasonApplicationFailure),
 				workv1alpha2.WithGracePeriodSeconds(ptr.To[int32](120)),
@@ -543,7 +543,7 @@ func Test_buildTaskOptions(t *testing.T) {
 					DecisionConditions: policyv1alpha1.DecisionConditions{
 						TolerationSeconds: ptr.To[int32](100),
 					},
-					PurgeMode: policyv1alpha1.Immediately,
+					PurgeMode: policyv1alpha1.PurgeModeDirectly,
 					StatePreservation: &policyv1alpha1.StatePreservation{
 						Rules: []policyv1alpha1.StatePreservationRule{
 							{AliasLabelName: "key-a", JSONPath: "{ .replicas }"},
@@ -560,7 +560,7 @@ func Test_buildTaskOptions(t *testing.T) {
 				clustersBeforeFailover: []string{"c0"},
 			},
 			want: *workv1alpha2.NewTaskOptions(
-				workv1alpha2.WithPurgeMode(policyv1alpha1.Immediately),
+				workv1alpha2.WithPurgeMode(policyv1alpha1.PurgeModeDirectly),
 				workv1alpha2.WithProducer(CRBApplicationFailoverControllerName),
 				workv1alpha2.WithReason(workv1alpha2.EvictionReasonApplicationFailure),
 				workv1alpha2.WithPreservedLabelState(map[string]string{"key-a": "2", "key-b": "true"}),
@@ -568,13 +568,13 @@ func Test_buildTaskOptions(t *testing.T) {
 			wantErr: assert.NoError,
 		},
 		{
-			name: "Graciously purgeMode with ResourceBinding, StatePreservation is nil",
+			name: "Gracefully purgeMode with ResourceBinding, StatePreservation is nil",
 			args: args{
 				failoverBehavior: &policyv1alpha1.ApplicationFailoverBehavior{
 					DecisionConditions: policyv1alpha1.DecisionConditions{
 						TolerationSeconds: ptr.To[int32](100),
 					},
-					PurgeMode:          policyv1alpha1.Graciously,
+					PurgeMode:          policyv1alpha1.PurgeModeGracefully,
 					GracePeriodSeconds: ptr.To[int32](120),
 				},
 				aggregatedStatus: []workv1alpha2.AggregatedStatusItem{
@@ -586,20 +586,20 @@ func Test_buildTaskOptions(t *testing.T) {
 				clustersBeforeFailover: []string{"c0"},
 			},
 			want: *workv1alpha2.NewTaskOptions(
-				workv1alpha2.WithPurgeMode(policyv1alpha1.Graciously),
+				workv1alpha2.WithPurgeMode(policyv1alpha1.PurgeModeGracefully),
 				workv1alpha2.WithProducer(RBApplicationFailoverControllerName),
 				workv1alpha2.WithReason(workv1alpha2.EvictionReasonApplicationFailure),
 				workv1alpha2.WithGracePeriodSeconds(ptr.To[int32](120))),
 			wantErr: assert.NoError,
 		},
 		{
-			name: "Graciously purgeMode with ResourceBinding, StatePreservation.Rules is nil",
+			name: "Gracefully purgeMode with ResourceBinding, StatePreservation.Rules is nil",
 			args: args{
 				failoverBehavior: &policyv1alpha1.ApplicationFailoverBehavior{
 					DecisionConditions: policyv1alpha1.DecisionConditions{
 						TolerationSeconds: ptr.To[int32](100),
 					},
-					PurgeMode:          policyv1alpha1.Graciously,
+					PurgeMode:          policyv1alpha1.PurgeModeGracefully,
 					GracePeriodSeconds: ptr.To[int32](120),
 					StatePreservation:  &policyv1alpha1.StatePreservation{},
 				},
@@ -612,20 +612,20 @@ func Test_buildTaskOptions(t *testing.T) {
 				clustersBeforeFailover: []string{"c0"},
 			},
 			want: *workv1alpha2.NewTaskOptions(
-				workv1alpha2.WithPurgeMode(policyv1alpha1.Graciously),
+				workv1alpha2.WithPurgeMode(policyv1alpha1.PurgeModeGracefully),
 				workv1alpha2.WithProducer(RBApplicationFailoverControllerName),
 				workv1alpha2.WithReason(workv1alpha2.EvictionReasonApplicationFailure),
 				workv1alpha2.WithGracePeriodSeconds(ptr.To[int32](120))),
 			wantErr: assert.NoError,
 		},
 		{
-			name: "Graciously purgeMode with ResourceBinding, target cluster status in not collected",
+			name: "Gracefully purgeMode with ResourceBinding, target cluster status in not collected",
 			args: args{
 				failoverBehavior: &policyv1alpha1.ApplicationFailoverBehavior{
 					DecisionConditions: policyv1alpha1.DecisionConditions{
 						TolerationSeconds: ptr.To[int32](100),
 					},
-					PurgeMode:          policyv1alpha1.Graciously,
+					PurgeMode:          policyv1alpha1.PurgeModeGracefully,
 					GracePeriodSeconds: ptr.To[int32](120),
 					StatePreservation: &policyv1alpha1.StatePreservation{
 						Rules: []policyv1alpha1.StatePreservationRule{
diff --git a/pkg/controllers/applicationfailover/crb_application_failover_controller_test.go b/pkg/controllers/applicationfailover/crb_application_failover_controller_test.go
index 96941bd2d..1d37b7332 100644
--- a/pkg/controllers/applicationfailover/crb_application_failover_controller_test.go
+++ b/pkg/controllers/applicationfailover/crb_application_failover_controller_test.go
@@ -211,8 +211,8 @@ func TestCRBApplicationFailoverController_evictBinding(t *testing.T) {
 		expectError bool
 	}{
 		{
-			name:        "PurgeMode is Graciously",
-			purgeMode:   policyv1alpha1.Graciously,
+			name:        "PurgeMode is Gracefully",
+			purgeMode:   policyv1alpha1.PurgeModeGracefully,
 			expectError: false,
 		},
 		{
@@ -222,7 +222,7 @@ func TestCRBApplicationFailoverController_evictBinding(t *testing.T) {
 		},
 		{
 			name:        "PurgeMode is Immediately",
-			purgeMode:   policyv1alpha1.Immediately,
+			purgeMode:   policyv1alpha1.PurgeModeDirectly,
 			expectError: false,
 		},
 	}
diff --git a/pkg/controllers/applicationfailover/rb_application_failover_controller_test.go b/pkg/controllers/applicationfailover/rb_application_failover_controller_test.go
index 8c71652b2..f01808986 100644
--- a/pkg/controllers/applicationfailover/rb_application_failover_controller_test.go
+++ b/pkg/controllers/applicationfailover/rb_application_failover_controller_test.go
@@ -207,8 +207,8 @@ func TestRBApplicationFailoverController_evictBinding(t *testing.T) {
 		expectError bool
 	}{
 		{
-			name:        "PurgeMode is Graciously",
-			purgeMode:   policyv1alpha1.Graciously,
+			name:        "PurgeMode is Gracefully",
+			purgeMode:   policyv1alpha1.PurgeModeGracefully,
 			expectError: false,
 		},
 		{
@@ -218,7 +218,7 @@ func TestRBApplicationFailoverController_evictBinding(t *testing.T) {
 		},
 		{
 			name:        "PurgeMode is Immediately",
-			purgeMode:   policyv1alpha1.Immediately,
+			purgeMode:   policyv1alpha1.PurgeModeDirectly,
 			expectError: false,
 		},
 	}
diff --git a/pkg/controllers/cluster/taint_manager_test.go b/pkg/controllers/cluster/taint_manager_test.go
index edc506266..a214e4242 100644
--- a/pkg/controllers/cluster/taint_manager_test.go
+++ b/pkg/controllers/cluster/taint_manager_test.go
@@ -241,7 +241,7 @@ func TestNoExecuteTaintManager_syncBindingEviction(t *testing.T) {
 					GracefulEvictionTasks: []workv1alpha2.GracefulEvictionTask{
 						{
 							FromCluster: "test-cluster",
-							PurgeMode:   policyv1alpha1.Graciously,
+							PurgeMode:   policyv1alpha1.PurgeModeGracefully,
 							Replicas:    &replica,
 							Reason:      workv1alpha2.EvictionReasonTaintUntolerated,
 							Producer:    workv1alpha2.EvictionProducerTaintManager,
@@ -408,7 +408,7 @@ func TestNoExecuteTaintManager_syncClusterBindingEviction(t *testing.T) {
 					GracefulEvictionTasks: []workv1alpha2.GracefulEvictionTask{
 						{
 							FromCluster: "test-cluster",
-							PurgeMode:   policyv1alpha1.Graciously,
+							PurgeMode:   policyv1alpha1.PurgeModeGracefully,
 							Replicas:    &replica,
 							Reason:      workv1alpha2.EvictionReasonTaintUntolerated,
 							Producer:    workv1alpha2.EvictionProducerTaintManager,
diff --git a/pkg/controllers/multiclusterservice/endpointslice_collect_controller_test.go b/pkg/controllers/multiclusterservice/endpointslice_collect_controller_test.go
index f02c55a48..b8a6a3a1f 100644
--- a/pkg/controllers/multiclusterservice/endpointslice_collect_controller_test.go
+++ b/pkg/controllers/multiclusterservice/endpointslice_collect_controller_test.go
@@ -146,10 +146,11 @@ func TestGetEndpointSliceWorkMeta(t *testing.T) {
 					util.MultiClusterServiceNameLabel:      "test-service",
 					util.EndpointSliceWorkManagedByLabel:   util.MultiClusterServiceKind,
 				},
+				Finalizers: []string{util.MCSEndpointSliceDispatchControllerFinalizer},
 			},
 		},
 		{
-			name:          "Existing work for EndpointSlice",
+			name:          "Existing work for EndpointSlice without finalizers",
 			existingWork:  createExistingWork("endpointslice-test-eps-default", "test-cluster", "ExistingController"),
 			endpointSlice: createEndpointSliceForTest("test-eps", "default", "test-service", false),
 			expectedMeta: metav1.ObjectMeta{
@@ -163,6 +164,51 @@ func TestGetEndpointSliceWorkMeta(t *testing.T) {
 				Finalizers: []string{util.MCSEndpointSliceDispatchControllerFinalizer},
 			},
 		},
+		{
+			name:          "Existing work with existing finalizers",
+			existingWork:  createExistingWorkWithFinalizers("endpointslice-test-eps-default", "test-cluster", "ExistingController", []string{"existing.finalizer", "another.finalizer"}),
+			endpointSlice: createEndpointSliceForTest("test-eps", "default", "test-service", false),
+			expectedMeta: metav1.ObjectMeta{
+				Name:      "endpointslice-test-eps-default",
+				Namespace: "test-cluster",
+				Labels: map[string]string{
+					util.MultiClusterServiceNamespaceLabel: "default",
+					util.MultiClusterServiceNameLabel:      "test-service",
+					util.EndpointSliceWorkManagedByLabel:   "ExistingController.MultiClusterService",
+				},
+				Finalizers: []string{"another.finalizer", "existing.finalizer", util.MCSEndpointSliceDispatchControllerFinalizer},
+			},
+		},
+		{
+			name:          "Existing work with duplicate finalizer",
+			existingWork:  createExistingWorkWithFinalizers("endpointslice-test-eps-default", "test-cluster", "ExistingController", []string{util.MCSEndpointSliceDispatchControllerFinalizer, "another.finalizer"}),
+			endpointSlice: createEndpointSliceForTest("test-eps", "default", "test-service", false),
+			expectedMeta: metav1.ObjectMeta{
+				Name:      "endpointslice-test-eps-default",
+				Namespace: "test-cluster",
+				Labels: map[string]string{
+					util.MultiClusterServiceNamespaceLabel: "default",
+					util.MultiClusterServiceNameLabel:      "test-service",
+					util.EndpointSliceWorkManagedByLabel:   "ExistingController.MultiClusterService",
+				},
+				Finalizers: []string{"another.finalizer", util.MCSEndpointSliceDispatchControllerFinalizer},
+			},
+		},
+		{
+			name:          "Existing work without labels",
+			existingWork:  createExistingWorkWithoutLabels("endpointslice-test-eps-default", "test-cluster", []string{"existing.finalizer"}),
+			endpointSlice: createEndpointSliceForTest("test-eps", "default", "test-service", false),
+			expectedMeta: metav1.ObjectMeta{
+				Name:      "endpointslice-test-eps-default",
+				Namespace: "test-cluster",
+				Labels: map[string]string{
+					util.MultiClusterServiceNamespaceLabel: "default",
+					util.MultiClusterServiceNameLabel:      "test-service",
+					util.EndpointSliceWorkManagedByLabel:   util.MultiClusterServiceKind,
+				},
+				Finalizers: []string{"existing.finalizer", util.MCSEndpointSliceDispatchControllerFinalizer},
+			},
+		},
 		{
 			name:          "Nil EndpointSlice",
 			endpointSlice: nil,
@@ -186,7 +232,10 @@ func TestGetEndpointSliceWorkMeta(t *testing.T) {
 				require.NoError(t, err)
 				assert.Equal(t, tc.expectedMeta.Name, meta.Name)
 				assert.Equal(t, tc.expectedMeta.Namespace, meta.Namespace)
-				assert.Equal(t, tc.expectedMeta.Finalizers, meta.Finalizers)
+
+				assert.Equal(t, tc.expectedMeta.Finalizers, meta.Finalizers,
+					"Finalizers do not match. Expected: %v, Got: %v", tc.expectedMeta.Finalizers, meta.Finalizers)
+
 				assert.True(t, compareLabels(meta.Labels, tc.expectedMeta.Labels),
 					"Labels do not match. Expected: %v, Got: %v", tc.expectedMeta.Labels, meta.Labels)
 			}
@@ -325,6 +374,31 @@ func createExistingWork(name, namespace, managedBy string) *workv1alpha1.Work {
 	}
 }
 
+// Helper function to create an existing Work resource for testing with specific finalizers
+func createExistingWorkWithFinalizers(name, namespace, managedBy string, finalizers []string) *workv1alpha1.Work {
+	return &workv1alpha1.Work{
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      name,
+			Namespace: namespace,
+			Labels: map[string]string{
+				util.EndpointSliceWorkManagedByLabel: managedBy,
+			},
+			Finalizers: finalizers,
+		},
+	}
+}
+
+// Helper function to create an existing Work resource for testing without labels
+func createExistingWorkWithoutLabels(name, namespace string, finalizers []string) *workv1alpha1.Work {
+	return &workv1alpha1.Work{
+		ObjectMeta: metav1.ObjectMeta{
+			Name:       name,
+			Namespace:  namespace,
+			Finalizers: finalizers,
+		},
+	}
+}
+
 // Helper function to create a fake client with an optional existing Work
 func createFakeClient(existingWork *workv1alpha1.Work) client.Client {
 	scheme := setupSchemeEndpointCollect()
diff --git a/pkg/detector/detector_test.go b/pkg/detector/detector_test.go
index ffe7727ea..019fd2f6a 100644
--- a/pkg/detector/detector_test.go
+++ b/pkg/detector/detector_test.go
@@ -1044,6 +1044,10 @@ func (m *mockRESTMapper) ResourceSingularizer(resource string) (string, error) {
 // mockResourceInterpreter is a mock implementation of the ResourceInterpreter interface
 type mockResourceInterpreter struct{}
 
+func (m *mockResourceInterpreter) GetComponents(_ *unstructured.Unstructured) ([]workv1alpha2.ComponentRequirements, error) {
+	return nil, nil
+}
+
 func (m *mockResourceInterpreter) Start(_ context.Context) error {
 	return nil
 }
diff --git a/pkg/resourceinterpreter/customized/declarative/configmanager/accessor_test.go b/pkg/resourceinterpreter/customized/declarative/configmanager/accessor_test.go
index 2f3f8f68b..744841b01 100644
--- a/pkg/resourceinterpreter/customized/declarative/configmanager/accessor_test.go
+++ b/pkg/resourceinterpreter/customized/declarative/configmanager/accessor_test.go
@@ -85,6 +85,34 @@ func TestGetReplicaResourceLuaScript(t *testing.T) {
 	}
 }
 
+func TestGetComponentResourceLuaScript(t *testing.T) {
+	tests := []struct {
+		name     string
+		accessor *resourceCustomAccessor
+		want     string
+	}{
+		{
+			name:     "nil component resource",
+			accessor: &resourceCustomAccessor{},
+			want:     "",
+		},
+		{
+			name: "with script",
+			accessor: &resourceCustomAccessor{
+				componentResource: &configv1alpha1.ComponentResourceRequirement{LuaScript: "test-script"},
+			},
+			want: "test-script",
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			got := tt.accessor.GetComponentResourceLuaScript()
+			assert.Equal(t, tt.want, got)
+		})
+	}
+}
+
 func TestGetReplicaRevisionLuaScript(t *testing.T) {
 	tests := []struct {
 		name     string
@@ -465,6 +493,45 @@ func TestSetReplicaResource(t *testing.T) {
 	}
 }
 
+func TestSetComponentResource(t *testing.T) {
+	tests := []struct {
+		name    string
+		initial *resourceCustomAccessor
+		input   *configv1alpha1.ComponentResourceRequirement
+		want    string
+	}{
+		{
+			name:    "set on nil field",
+			initial: &resourceCustomAccessor{},
+			input:   &configv1alpha1.ComponentResourceRequirement{LuaScript: "script1"},
+			want:    "script1",
+		},
+		{
+			name: "set on empty script",
+			initial: &resourceCustomAccessor{
+				componentResource: &configv1alpha1.ComponentResourceRequirement{LuaScript: ""},
+			},
+			input: &configv1alpha1.ComponentResourceRequirement{LuaScript: "script1"},
+			want:  "script1",
+		},
+		{
+			name: "set on non-empty script",
+			initial: &resourceCustomAccessor{
+				componentResource: &configv1alpha1.ComponentResourceRequirement{LuaScript: "existing"},
+			},
+			input: &configv1alpha1.ComponentResourceRequirement{LuaScript: "script1"},
+			want:  "existing",
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			tt.initial.setComponentResource(tt.input)
+			assert.Equal(t, tt.want, tt.initial.GetComponentResourceLuaScript())
+		})
+	}
+}
+
 func TestSetReplicaRevision(t *testing.T) {
 	tests := []struct {
 		name    string
diff --git a/pkg/resourceinterpreter/customized/declarative/luavm/lua_test.go b/pkg/resourceinterpreter/customized/declarative/luavm/lua_test.go
index 941081ca3..e95c2071c 100644
--- a/pkg/resourceinterpreter/customized/declarative/luavm/lua_test.go
+++ b/pkg/resourceinterpreter/customized/declarative/luavm/lua_test.go
@@ -18,6 +18,7 @@ package luavm
 
 import (
 	"encoding/json"
+	"fmt"
 	"reflect"
 	"testing"
 
@@ -28,6 +29,7 @@ import (
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
 	"k8s.io/apimachinery/pkg/runtime"
+	"k8s.io/apimachinery/pkg/runtime/schema"
 	"k8s.io/utils/ptr"
 
 	configv1alpha1 "github.com/karmada-io/karmada/pkg/apis/config/v1alpha1"
@@ -185,6 +187,224 @@ end`,
 	}
 }
 
+// MockMultiPodTemplateWorkload simulates a CRD with multiple pod templates,
+// mirroring the structure of a real-world Kubernetes object.
+type MockMultiPodTemplateWorkload struct {
+	metav1.TypeMeta   `json:",inline"`
+	metav1.ObjectMeta `json:"metadata,omitempty"`
+	Spec              MockWorkloadSpec `json:"spec,omitempty"`
+}
+
+// MockWorkloadSpec contains the different components, each with its own PodSpec.
+type MockWorkloadSpec struct {
+	Master MockComponentSpec `json:"master"`
+	Worker MockComponentSpec `json:"worker"`
+}
+
+// MockComponentSpec now includes Replicas and directly embeds a PodSpec
+// to perfectly simulate a pod template.
+type MockComponentSpec struct {
+	Replicas int32          `json:"replicas"`
+	Spec     corev1.PodSpec `json:"spec"`
+}
+
+// DeepCopyObject implements the runtime.Object interface.
+func (in *MockMultiPodTemplateWorkload) DeepCopyObject() runtime.Object {
+	out := new(MockMultiPodTemplateWorkload)
+	bytes, err := json.Marshal(in)
+	if err != nil {
+		panic(fmt.Sprintf("Failed to marshal mock object in DeepCopy: %v", err))
+	}
+	if err := json.Unmarshal(bytes, out); err != nil {
+		panic(fmt.Sprintf("Failed to unmarshal mock object in DeepCopy: %v", err))
+	}
+	return out
+}
+
+func TestGetComponents(t *testing.T) {
+	vm := New(true, 1)
+
+	mockWorkload := &MockMultiPodTemplateWorkload{
+		TypeMeta: metav1.TypeMeta{
+			APIVersion: "testing.karmada.io/v1alpha1",
+			Kind:       "MockMultiPodTemplateWorkload",
+		},
+		ObjectMeta: metav1.ObjectMeta{
+			Namespace: "default",
+			Name:      "my-mock-app",
+		},
+		Spec: MockWorkloadSpec{
+			Master: MockComponentSpec{
+				Replicas: 1,
+				Spec: corev1.PodSpec{
+					NodeSelector: map[string]string{"foo": "foo1"},
+					Tolerations:  []corev1.Toleration{{Key: "bar", Operator: corev1.TolerationOpExists}},
+					Containers: []corev1.Container{
+						{
+							Name: "master",
+							Resources: corev1.ResourceRequirements{
+								Requests: corev1.ResourceList{
+									corev1.ResourceCPU:    resource.MustParse("1"),
+									corev1.ResourceMemory: resource.MustParse("1Gi"),
+								},
+							},
+						},
+					},
+				},
+			},
+			Worker: MockComponentSpec{
+				Replicas: 3,
+				Spec: corev1.PodSpec{
+					Containers: []corev1.Container{
+						{
+							Name: "worker",
+							Resources: corev1.ResourceRequirements{
+								Requests: corev1.ResourceList{
+									corev1.ResourceCPU:    resource.MustParse("500m"),
+									corev1.ResourceMemory: resource.MustParse("2Gi"),
+								},
+							},
+						},
+					},
+				},
+			},
+		},
+	}
+	mockWorkload.GetObjectKind().SetGroupVersionKind(schema.GroupVersionKind{
+		Group:   "testing.karmada.io",
+		Version: "v1alpha1",
+		Kind:    "MockMultiPodTemplateWorkload",
+	})
+
+	tests := []struct {
+		name           string
+		object         runtime.Object
+		luaScript      string
+		wantErr        bool
+		wantComponents []workv1alpha2.ComponentRequirements
+	}{
+		{
+			name:   "Successfully parse multiple PodSpec templates from a mock workload",
+			object: mockWorkload,
+			// This Lua script now navigates a real PodSpec and uses the 'kube' helper library.
+			luaScript: `
+function GetComponents(obj)
+    local kube = require("kube")
+    local components = {}
+
+    -- Parse the Master component's PodSpec
+    table.insert(components, {
+        name = "master",
+        replicas = obj.spec.master.replicas,
+        replicaRequirements = kube.accuratePodRequirements(obj.spec.master)
+    })
+
+    -- Parse the Worker component's PodSpec
+    table.insert(components, {
+        name = "worker",
+        replicas = obj.spec.worker.replicas,
+        replicaRequirements = kube.accuratePodRequirements(obj.spec.worker)
+    })
+
+    return components
+end
+`,
+			wantErr: false,
+			wantComponents: []workv1alpha2.ComponentRequirements{
+				{
+					Name:     "master",
+					Replicas: 1,
+					ReplicaRequirements: &workv1alpha2.ReplicaRequirements{
+						NodeClaim: &workv1alpha2.NodeClaim{
+							NodeSelector: map[string]string{"foo": "foo1"},
+							Tolerations:  []corev1.Toleration{{Key: "bar", Operator: corev1.TolerationOpExists}},
+						},
+						ResourceRequest: map[corev1.ResourceName]resource.Quantity{
+							corev1.ResourceCPU:    resource.MustParse("1"),
+							corev1.ResourceMemory: resource.MustParse("1Gi"),
+						},
+					},
+				},
+				{
+					Name:     "worker",
+					Replicas: 3,
+					ReplicaRequirements: &workv1alpha2.ReplicaRequirements{
+						ResourceRequest: map[corev1.ResourceName]resource.Quantity{
+							corev1.ResourceCPU:    resource.MustParse("500m"),
+							corev1.ResourceMemory: resource.MustParse("2Gi"),
+						},
+					},
+				},
+			},
+		},
+		{
+			name:   "Script successfully returns nil for no components",
+			object: mockWorkload,
+			luaScript: `
+function GetComponents(obj)
+    return nil
+end
+`,
+			wantErr:        false,
+			wantComponents: nil,
+		},
+		{
+			name:   "Script returns an invalid type (string) and should error",
+			object: mockWorkload,
+			luaScript: `
+function GetComponents(obj)
+    return "this is not a table"
+end
+`,
+			wantErr:        true,
+			wantComponents: nil,
+		},
+		{
+			name:   "Script has a syntax error and should error",
+			object: mockWorkload,
+			luaScript: `
+function GetComponents(obj)
+    local x = {
+    return x -- syntax error: unbalanced brackets
+end
+`,
+			wantErr:        true,
+			wantComponents: nil,
+		},
+		{
+			name:   "Script is missing the GetComponents function and should error",
+			object: mockWorkload,
+			luaScript: `
+-- This script is valid but does not define the required function.
+function GetReplicas(obj)
+    return 1
+end
+`,
+			wantErr:        true,
+			wantComponents: nil,
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			objUnstructured, err := helper.ToUnstructured(tt.object)
+			if err != nil {
+				t.Fatalf("Failed to convert object to unstructured: %v", err)
+			}
+
+			components, err := vm.GetComponents(objUnstructured, tt.luaScript)
+
+			if (err != nil) != tt.wantErr {
+				t.Fatalf("GetComponents() error = %v, wantErr %v", err, tt.wantErr)
+			}
+
+			if !tt.wantErr && !reflect.DeepEqual(components, tt.wantComponents) {
+				t.Errorf("GetComponents() = \n%#v, \nwant \n%#v", components, tt.wantComponents)
+			}
+		})
+	}
+}
+
 func TestReviseDeploymentReplica(t *testing.T) {
 	tests := []struct {
 		name        string
diff --git a/pkg/resourceinterpreter/customized/webhook/configmanager/manager_test.go b/pkg/resourceinterpreter/customized/webhook/configmanager/manager_test.go
index 36c8ffd61..fa0f95454 100644
--- a/pkg/resourceinterpreter/customized/webhook/configmanager/manager_test.go
+++ b/pkg/resourceinterpreter/customized/webhook/configmanager/manager_test.go
@@ -17,18 +17,22 @@ limitations under the License.
 package configmanager
 
 import (
+	"context"
 	"fmt"
 	"testing"
+	"time"
 
 	"github.com/stretchr/testify/assert"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/labels"
 	"k8s.io/apimachinery/pkg/runtime"
 	"k8s.io/apimachinery/pkg/runtime/schema"
+	"k8s.io/client-go/dynamic"
 	"k8s.io/client-go/tools/cache"
 
 	configv1alpha1 "github.com/karmada-io/karmada/pkg/apis/config/v1alpha1"
 	"github.com/karmada-io/karmada/pkg/util/fedinformer/genericmanager"
+	"github.com/karmada-io/karmada/pkg/util/helper"
 )
 
 func TestNewExploreConfigManager(t *testing.T) {
@@ -62,6 +66,10 @@ func TestNewExploreConfigManager(t *testing.T) {
 
 			assert.NotNil(t, manager, "Manager should not be nil")
 			assert.NotNil(t, manager.HookAccessors(), "Accessors should be initialized")
+
+			internalManager, ok := manager.(*interpreterConfigManager)
+			assert.True(t, ok)
+			assert.Equal(t, informerManager, internalManager.informer)
 		})
 	}
 }
@@ -70,6 +78,7 @@ func TestHasSynced(t *testing.T) {
 	tests := []struct {
 		name           string
 		initialSynced  bool
+		informer       genericmanager.SingleClusterInformerManager
 		listErr        error
 		listResult     []runtime.Object
 		expectedSynced bool
@@ -80,24 +89,47 @@ func TestHasSynced(t *testing.T) {
 			expectedSynced: true,
 		},
 		{
-			name:           "not synced but empty list",
+			name:           "informer not configured",
 			initialSynced:  false,
+			informer:       nil,
+			expectedSynced: false,
+		},
+		{
+			name:          "informer not synced",
+			initialSynced: false,
+			informer: &mockSingleClusterInformerManager{
+				isSynced: false,
+			},
+			expectedSynced: false,
+		},
+		{
+			name:          "sync with empty list",
+			initialSynced: false,
+			informer: &mockSingleClusterInformerManager{
+				isSynced: true,
+			},
 			listResult:     []runtime.Object{},
 			expectedSynced: true,
 		},
 		{
-			name:          "not synced with items",
+			name:          "sync with items",
 			initialSynced: false,
+			informer: &mockSingleClusterInformerManager{
+				isSynced: true,
+			},
 			listResult: []runtime.Object{
 				&configv1alpha1.ResourceInterpreterWebhookConfiguration{
 					ObjectMeta: metav1.ObjectMeta{Name: "test"},
 				},
 			},
-			expectedSynced: false,
+			expectedSynced: true,
 		},
 		{
-			name:           "list error",
-			initialSynced:  false,
+			name:          "list error",
+			initialSynced: false,
+			informer: &mockSingleClusterInformerManager{
+				isSynced: true,
+			},
 			listErr:        fmt.Errorf("test error"),
 			expectedSynced: false,
 		},
@@ -105,10 +137,23 @@ func TestHasSynced(t *testing.T) {
 
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
+			// Convert typed objects to unstructured objects for proper testing
+			unstructuredItems := make([]runtime.Object, len(tt.listResult))
+			for i, config := range tt.listResult {
+				if config != nil {
+					unstructuredObj, err := helper.ToUnstructured(config)
+					assert.NoError(t, err)
+					unstructuredItems[i] = unstructuredObj
+				} else {
+					unstructuredItems[i] = config
+				}
+			}
+
 			manager := &interpreterConfigManager{
+				informer: tt.informer,
 				lister: &mockLister{
 					err:   tt.listErr,
-					items: tt.listResult,
+					items: unstructuredItems,
 				},
 			}
 			manager.initialSynced.Store(tt.initialSynced)
@@ -181,12 +226,30 @@ func TestUpdateConfiguration(t *testing.T) {
 		name          string
 		configs       []runtime.Object
 		listErr       error
+		informer      genericmanager.SingleClusterInformerManager
 		expectedCount int
 		wantSynced    bool
 	}{
 		{
-			name:          "empty configuration",
-			configs:       []runtime.Object{},
+			name:          "informer not configured",
+			informer:      nil,
+			expectedCount: 0,
+			wantSynced:    false,
+		},
+		{
+			name: "informer not synced",
+			informer: &mockSingleClusterInformerManager{
+				isSynced: false,
+			},
+			expectedCount: 0,
+			wantSynced:    false,
+		},
+		{
+			name:    "empty configuration",
+			configs: []runtime.Object{},
+			informer: &mockSingleClusterInformerManager{
+				isSynced: true,
+			},
 			expectedCount: 0,
 			wantSynced:    true,
 		},
@@ -204,13 +267,19 @@ func TestUpdateConfiguration(t *testing.T) {
 					},
 				},
 			},
+			informer: &mockSingleClusterInformerManager{
+				isSynced: true,
+			},
 			expectedCount: 1,
 			wantSynced:    true,
 		},
 		{
-			name:          "list error",
-			configs:       []runtime.Object{},
-			listErr:       fmt.Errorf("test error"),
+			name:    "list error",
+			configs: []runtime.Object{},
+			listErr: fmt.Errorf("test error"),
+			informer: &mockSingleClusterInformerManager{
+				isSynced: true,
+			},
 			expectedCount: 0,
 			wantSynced:    false,
 		},
@@ -218,26 +287,80 @@ func TestUpdateConfiguration(t *testing.T) {
 
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
+			// Convert typed objects to unstructured objects for proper testing
+			unstructuredItems := make([]runtime.Object, len(tt.configs))
+			for i, config := range tt.configs {
+				if config != nil {
+					unstructuredObj, err := helper.ToUnstructured(config)
+					assert.NoError(t, err)
+					unstructuredItems[i] = unstructuredObj
+				} else {
+					unstructuredItems[i] = config
+				}
+			}
+
 			manager := &interpreterConfigManager{
 				lister: &mockLister{
-					items: tt.configs,
+					items: unstructuredItems,
 					err:   tt.listErr,
 				},
+				informer: tt.informer,
 			}
 			manager.configuration.Store([]WebhookAccessor{})
 			manager.initialSynced.Store(false)
 
-			manager.updateConfiguration()
+			synced := manager.HasSynced()
+			assert.Equal(t, tt.wantSynced, synced)
 
 			accessors := manager.HookAccessors()
 			assert.Equal(t, tt.expectedCount, len(accessors))
-			assert.Equal(t, tt.wantSynced, manager.HasSynced())
 		})
 	}
 }
 
 // Mock Implementations
 
+type mockSingleClusterInformerManager struct {
+	isSynced bool
+}
+
+func (m *mockSingleClusterInformerManager) IsInformerSynced(_ schema.GroupVersionResource) bool {
+	return m.isSynced
+}
+
+func (m *mockSingleClusterInformerManager) Lister(_ schema.GroupVersionResource) cache.GenericLister {
+	return nil
+}
+
+func (m *mockSingleClusterInformerManager) ForResource(_ schema.GroupVersionResource, _ cache.ResourceEventHandler) {
+}
+
+func (m *mockSingleClusterInformerManager) Start() {
+}
+
+func (m *mockSingleClusterInformerManager) Stop() {
+}
+
+func (m *mockSingleClusterInformerManager) WaitForCacheSync() map[schema.GroupVersionResource]bool {
+	return nil
+}
+
+func (m *mockSingleClusterInformerManager) WaitForCacheSyncWithTimeout(_ time.Duration) map[schema.GroupVersionResource]bool {
+	return nil
+}
+
+func (m *mockSingleClusterInformerManager) Context() context.Context {
+	return context.Background()
+}
+
+func (m *mockSingleClusterInformerManager) GetClient() dynamic.Interface {
+	return nil
+}
+
+func (m *mockSingleClusterInformerManager) IsHandlerExist(_ schema.GroupVersionResource, _ cache.ResourceEventHandler) bool {
+	return false
+}
+
 type mockLister struct {
 	items []runtime.Object
 	err   error
diff --git a/pkg/resourceinterpreter/customized/webhook/customized_test.go b/pkg/resourceinterpreter/customized/webhook/customized_test.go
index 22589ba65..1415fd728 100644
--- a/pkg/resourceinterpreter/customized/webhook/customized_test.go
+++ b/pkg/resourceinterpreter/customized/webhook/customized_test.go
@@ -1085,6 +1085,12 @@ func (m *mockConfigManager) HookAccessors() []configmanager.WebhookAccessor {
 	return m.hooks
 }
 
+func (m *mockConfigManager) LoadConfig(_ []*configv1alpha1.ResourceInterpreterWebhookConfiguration) {
+	// Mock implementation: in a real test, we might want to process the configurations
+	// and update the hooks accordingly. For now, this is a no-op implementation.
+	// This allows the mock to satisfy the ConfigManager interface.
+}
+
 // mockWebhookAccessor implements configmanager.WebhookAccessor interface for testing
 type mockWebhookAccessor struct {
 	uid             string
diff --git a/pkg/util/finalizer_test.go b/pkg/util/finalizer_test.go
new file mode 100644
index 000000000..ecde1673d
--- /dev/null
+++ b/pkg/util/finalizer_test.go
@@ -0,0 +1,131 @@
+/*
+Copyright 2025 The Karmada Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package util
+
+import (
+	"reflect"
+	"testing"
+)
+
+func TestMergeFinalizers(t *testing.T) {
+	tests := []struct {
+		name            string
+		existFinalizers []string
+		newFinalizers   []string
+		expectedResult  []string
+	}{
+		{
+			name:            "both nil",
+			existFinalizers: nil,
+			newFinalizers:   nil,
+			expectedResult:  nil,
+		},
+		{
+			name:            "exist finalizers is nil",
+			existFinalizers: nil,
+			newFinalizers:   []string{"finalizer1", "finalizer2"},
+			expectedResult:  []string{"finalizer1", "finalizer2"},
+		},
+		{
+			name:            "new finalizers is nil",
+			existFinalizers: []string{"finalizer1", "finalizer2"},
+			newFinalizers:   nil,
+			expectedResult:  []string{"finalizer1", "finalizer2"},
+		},
+		{
+			name:            "both empty",
+			existFinalizers: []string{},
+			newFinalizers:   []string{},
+			expectedResult:  []string{},
+		},
+		{
+			name:            "exist finalizers is empty",
+			existFinalizers: []string{},
+			newFinalizers:   []string{"finalizer1", "finalizer2"},
+			expectedResult:  []string{"finalizer1", "finalizer2"},
+		},
+		{
+			name:            "new finalizers is empty",
+			existFinalizers: []string{"finalizer1", "finalizer2"},
+			newFinalizers:   []string{},
+			expectedResult:  []string{"finalizer1", "finalizer2"},
+		},
+		{
+			name:            "no duplicates",
+			existFinalizers: []string{"finalizer1", "finalizer2"},
+			newFinalizers:   []string{"finalizer3", "finalizer4"},
+			expectedResult:  []string{"finalizer1", "finalizer2", "finalizer3", "finalizer4"},
+		},
+		{
+			name:            "with duplicates",
+			existFinalizers: []string{"finalizer1", "finalizer2"},
+			newFinalizers:   []string{"finalizer2", "finalizer3"},
+			expectedResult:  []string{"finalizer1", "finalizer2", "finalizer3"},
+		},
+		{
+			name:            "all duplicates",
+			existFinalizers: []string{"finalizer1", "finalizer2"},
+			newFinalizers:   []string{"finalizer1", "finalizer2"},
+			expectedResult:  []string{"finalizer1", "finalizer2"},
+		},
+		{
+			name:            "duplicates in exist finalizers",
+			existFinalizers: []string{"finalizer1", "finalizer2", "finalizer1"},
+			newFinalizers:   []string{"finalizer3"},
+			expectedResult:  []string{"finalizer1", "finalizer2", "finalizer3"},
+		},
+		{
+			name:            "duplicates in new finalizers",
+			existFinalizers: []string{"finalizer1"},
+			newFinalizers:   []string{"finalizer2", "finalizer3", "finalizer2"},
+			expectedResult:  []string{"finalizer1", "finalizer2", "finalizer3"},
+		},
+		{
+			name:            "duplicates in both",
+			existFinalizers: []string{"finalizer1", "finalizer2", "finalizer1"},
+			newFinalizers:   []string{"finalizer2", "finalizer3", "finalizer2"},
+			expectedResult:  []string{"finalizer1", "finalizer2", "finalizer3"},
+		},
+		{
+			name:            "single finalizer in exist",
+			existFinalizers: []string{"finalizer1"},
+			newFinalizers:   []string{"finalizer2"},
+			expectedResult:  []string{"finalizer1", "finalizer2"},
+		},
+		{
+			name:            "single duplicate finalizer",
+			existFinalizers: []string{"finalizer1"},
+			newFinalizers:   []string{"finalizer1"},
+			expectedResult:  []string{"finalizer1"},
+		},
+		{
+			name:            "sort with result",
+			existFinalizers: []string{"finalizer3", "finalizer1", "finalizer2"},
+			newFinalizers:   []string{"finalizer4", "finalizer5"},
+			expectedResult:  []string{"finalizer1", "finalizer2", "finalizer3", "finalizer4", "finalizer5"},
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			result := MergeFinalizers(tt.existFinalizers, tt.newFinalizers)
+			if !reflect.DeepEqual(result, tt.expectedResult) {
+				t.Errorf("MergeFinalizers() = %v, want %v", result, tt.expectedResult)
+			}
+		})
+	}
+}
diff --git a/pkg/util/helper/binding_test.go b/pkg/util/helper/binding_test.go
index 01d3411ac..891332b07 100644
--- a/pkg/util/helper/binding_test.go
+++ b/pkg/util/helper/binding_test.go
@@ -353,11 +353,11 @@ func TestObtainBindingSpecExistingClusters(t *testing.T) {
 				GracefulEvictionTasks: []workv1alpha2.GracefulEvictionTask{
 					{
 						FromCluster: "member3",
-						PurgeMode:   policyv1alpha1.Immediately,
+						PurgeMode:   policyv1alpha1.PurgeModeDirectly,
 					},
 					{
 						FromCluster: "member4",
-						PurgeMode:   policyv1alpha1.Graciously,
+						PurgeMode:   policyv1alpha1.PurgeModeGracefully,
 					},
 					{
 						FromCluster: "member5",
diff --git a/pkg/util/helper/mcs_test.go b/pkg/util/helper/mcs_test.go
index 8fd874f09..f21bf23a3 100644
--- a/pkg/util/helper/mcs_test.go
+++ b/pkg/util/helper/mcs_test.go
@@ -28,6 +28,7 @@ import (
 	"sigs.k8s.io/controller-runtime/pkg/client"
 	"sigs.k8s.io/controller-runtime/pkg/client/fake"
 
+	"github.com/karmada-io/karmada/pkg/util"
 	"github.com/karmada-io/karmada/pkg/util/gclient"
 )
 
@@ -223,3 +224,50 @@ func TestDeleteEndpointSlice(t *testing.T) {
 		})
 	}
 }
+
+func TestIsEndpointSliceManagedByKarmada(t *testing.T) {
+	tests := []struct {
+		name   string
+		labels map[string]string
+		want   bool
+	}{
+		{
+			name: "managed by endpointslice-dispatch-controller",
+			labels: map[string]string{
+				discoveryv1.LabelManagedBy: util.EndpointSliceDispatchControllerLabelValue,
+			},
+			want: true,
+		},
+		{
+			name: "managed by endpointslice-controller",
+			labels: map[string]string{
+				discoveryv1.LabelManagedBy: util.EndpointSliceControllerLabelValue,
+			},
+			want: true,
+		},
+		{
+			name: "not managed by karmada",
+			labels: map[string]string{
+				discoveryv1.LabelManagedBy: "not-karmada",
+			},
+			want: false,
+		},
+		{
+			name:   "nil labels",
+			labels: nil,
+			want:   false,
+		},
+		{
+			name:   "empty labels",
+			labels: map[string]string{},
+			want:   false,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			if got := IsEndpointSliceManagedByKarmada(tt.labels); got != tt.want {
+				t.Errorf("IsEndpointSliceManagedByKarmada() = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}
diff --git a/pkg/util/helper/policy_test.go b/pkg/util/helper/policy_test.go
index f48077df5..0f193b80d 100644
--- a/pkg/util/helper/policy_test.go
+++ b/pkg/util/helper/policy_test.go
@@ -365,7 +365,7 @@ func TestSetDefaultGracePeriodSeconds(t *testing.T) {
 		expectBehavior *policyv1alpha1.ApplicationFailoverBehavior
 	}{
 		{
-			name: "purgeMode is not graciously",
+			name: "purgeMode is not gracefully",
 			behavior: &policyv1alpha1.ApplicationFailoverBehavior{
 				PurgeMode: policyv1alpha1.Never,
 			},
@@ -374,23 +374,23 @@ func TestSetDefaultGracePeriodSeconds(t *testing.T) {
 			},
 		},
 		{
-			name: "purgeMode is graciously and gracePeriodSeconds is set",
+			name: "purgeMode is gracefully and gracePeriodSeconds is set",
 			behavior: &policyv1alpha1.ApplicationFailoverBehavior{
-				PurgeMode:          policyv1alpha1.Graciously,
+				PurgeMode:          policyv1alpha1.PurgeModeGracefully,
 				GracePeriodSeconds: ptr.To[int32](200),
 			},
 			expectBehavior: &policyv1alpha1.ApplicationFailoverBehavior{
-				PurgeMode:          policyv1alpha1.Graciously,
+				PurgeMode:          policyv1alpha1.PurgeModeGracefully,
 				GracePeriodSeconds: ptr.To[int32](200),
 			},
 		},
 		{
-			name: "purgeMode is graciously and gracePeriodSeconds is not set",
+			name: "purgeMode is gracefully and gracePeriodSeconds is not set",
 			behavior: &policyv1alpha1.ApplicationFailoverBehavior{
-				PurgeMode: policyv1alpha1.Graciously,
+				PurgeMode: policyv1alpha1.PurgeModeGracefully,
 			},
 			expectBehavior: &policyv1alpha1.ApplicationFailoverBehavior{
-				PurgeMode:          policyv1alpha1.Graciously,
+				PurgeMode:          policyv1alpha1.PurgeModeGracefully,
 				GracePeriodSeconds: ptr.To[int32](600),
 			},
 		},
diff --git a/pkg/util/selector_test.go b/pkg/util/selector_test.go
index a9194e492..15cad2020 100644
--- a/pkg/util/selector_test.go
+++ b/pkg/util/selector_test.go
@@ -1087,3 +1087,149 @@ func Test_matchZones(t *testing.T) {
 		})
 	}
 }
+
+func TestExtractUniqueNamespacedSelectors(t *testing.T) {
+	tests := []struct {
+		name      string
+		selectors []policyv1alpha1.ResourceSelector
+		want      []policyv1alpha1.ResourceSelector
+	}{
+		{
+			name:      "empty selectors",
+			selectors: nil,
+			want:      nil,
+		},
+		{
+			name: "single selector",
+			selectors: []policyv1alpha1.ResourceSelector{
+				{
+					APIVersion: "v1",
+					Kind:       "Pod",
+				},
+			},
+			want: []policyv1alpha1.ResourceSelector{
+				{
+					APIVersion: "v1",
+					Kind:       "Pod",
+				},
+			},
+		},
+		{
+			name: "multiple different selectors",
+			selectors: []policyv1alpha1.ResourceSelector{
+				{
+					APIVersion: "v1",
+					Kind:       "Pod",
+				},
+				{
+					APIVersion: "apps/v1",
+					Kind:       "Deployment",
+				},
+				{
+					APIVersion: "v1",
+					Kind:       "Service",
+				},
+			},
+			want: []policyv1alpha1.ResourceSelector{
+				{
+					APIVersion: "v1",
+					Kind:       "Pod",
+				},
+				{
+					APIVersion: "apps/v1",
+					Kind:       "Deployment",
+				},
+				{
+					APIVersion: "v1",
+					Kind:       "Service",
+				},
+			},
+		},
+		{
+			name: "duplicate selectors should be deduplicated",
+			selectors: []policyv1alpha1.ResourceSelector{
+				{
+					APIVersion: "v1",
+					Kind:       "Pod",
+				},
+				{
+					APIVersion: "apps/v1",
+					Kind:       "Deployment",
+				},
+				{
+					APIVersion: "v1",
+					Kind:       "Pod", // duplicate
+				},
+				{
+					APIVersion: "apps/v1",
+					Kind:       "Deployment", // duplicate
+				},
+			},
+			want: []policyv1alpha1.ResourceSelector{
+				{
+					APIVersion: "v1",
+					Kind:       "Pod",
+				},
+				{
+					APIVersion: "apps/v1",
+					Kind:       "Deployment",
+				},
+			},
+		},
+		{
+			name: "selectors with same kind but different API versions",
+			selectors: []policyv1alpha1.ResourceSelector{
+				{
+					APIVersion: "extensions/v1beta1",
+					Kind:       "Deployment",
+				},
+				{
+					APIVersion: "apps/v1",
+					Kind:       "Deployment",
+				},
+			},
+			want: []policyv1alpha1.ResourceSelector{
+				{
+					APIVersion: "extensions/v1beta1",
+					Kind:       "Deployment",
+				},
+				{
+					APIVersion: "apps/v1",
+					Kind:       "Deployment",
+				},
+			},
+		},
+		{
+			name: "selectors with custom resources",
+			selectors: []policyv1alpha1.ResourceSelector{
+				{
+					APIVersion: "custom.io/v1alpha1",
+					Kind:       "CustomResource",
+				},
+				{
+					APIVersion: "example.com/v1beta1",
+					Kind:       "ExampleResource",
+				},
+			},
+			want: []policyv1alpha1.ResourceSelector{
+				{
+					APIVersion: "custom.io/v1alpha1",
+					Kind:       "CustomResource",
+				},
+				{
+					APIVersion: "example.com/v1beta1",
+					Kind:       "ExampleResource",
+				},
+			},
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			got := ExtractUniqueNamespacedSelectors(tt.selectors)
+			if !reflect.DeepEqual(got, tt.want) {
+				t.Errorf("ExtractUniqueNamespacedSelectors() = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}
diff --git a/pkg/util/validation/validation_test.go b/pkg/util/validation/validation_test.go
index d15648f8c..1d1565748 100644
--- a/pkg/util/validation/validation_test.go
+++ b/pkg/util/validation/validation_test.go
@@ -667,36 +667,36 @@ func TestValidateApplicationFailover(t *testing.T) {
 			expectedErr: "spec.failover.application.decisionConditions.tolerationSeconds: Invalid value: -100: must be greater than or equal to 0",
 		},
 		{
-			name: "the gracePeriodSeconds is declared when purgeMode is not graciously",
+			name: "the gracePeriodSeconds is declared when purgeMode is not gracefully",
 			applicationFailoverBehavior: &policyv1alpha1.ApplicationFailoverBehavior{
 				DecisionConditions: policyv1alpha1.DecisionConditions{
 					TolerationSeconds: ptr.To[int32](100),
 				},
-				PurgeMode:          policyv1alpha1.Immediately,
+				PurgeMode:          policyv1alpha1.PurgeModeDirectly,
 				GracePeriodSeconds: ptr.To[int32](100),
 			},
-			expectedErr: "spec.failover.application.gracePeriodSeconds: Invalid value: 100: only takes effect when purgeMode is graciously",
+			expectedErr: "spec.failover.application.gracePeriodSeconds: Invalid value: 100: only takes effect when purgeMode is gracefully",
 		},
 		{
-			name: "the gracePeriodSeconds is less than 0 when purgeMode is graciously",
+			name: "the gracePeriodSeconds is less than 0 when purgeMode is gracefully",
 			applicationFailoverBehavior: &policyv1alpha1.ApplicationFailoverBehavior{
 				DecisionConditions: policyv1alpha1.DecisionConditions{
 					TolerationSeconds: ptr.To[int32](100),
 				},
-				PurgeMode:          policyv1alpha1.Graciously,
+				PurgeMode:          policyv1alpha1.PurgeModeGracefully,
 				GracePeriodSeconds: ptr.To[int32](-100),
 			},
 			expectedErr: "spec.failover.application.gracePeriodSeconds: Invalid value: -100: must be greater than 0",
 		},
 		{
-			name: "the gracePeriodSeconds is empty when purgeMode is graciously",
+			name: "the gracePeriodSeconds is empty when purgeMode is gracefully",
 			applicationFailoverBehavior: &policyv1alpha1.ApplicationFailoverBehavior{
 				DecisionConditions: policyv1alpha1.DecisionConditions{
 					TolerationSeconds: ptr.To[int32](100),
 				},
-				PurgeMode: policyv1alpha1.Graciously,
+				PurgeMode: policyv1alpha1.PurgeModeGracefully,
 			},
-			expectedErr: "spec.failover.application.gracePeriodSeconds: Invalid value: \"null\": should not be empty when purgeMode is graciously",
+			expectedErr: "spec.failover.application.gracePeriodSeconds: Invalid value: \"null\": should not be empty when purgeMode is gracefully",
 		},
 		{
 			name: "application behavior is correctly declared",
@@ -707,6 +707,160 @@ func TestValidateApplicationFailover(t *testing.T) {
 			},
 			expectedErr: "",
 		},
+		{
+			name: "statePreservation is nil",
+			applicationFailoverBehavior: &policyv1alpha1.ApplicationFailoverBehavior{
+				DecisionConditions: policyv1alpha1.DecisionConditions{
+					TolerationSeconds: ptr.To[int32](100),
+				},
+				StatePreservation: nil,
+			},
+			expectedErr: "",
+		},
+		{
+			name: "statePreservation with valid rules",
+			applicationFailoverBehavior: &policyv1alpha1.ApplicationFailoverBehavior{
+				DecisionConditions: policyv1alpha1.DecisionConditions{
+					TolerationSeconds: ptr.To[int32](100),
+				},
+				StatePreservation: &policyv1alpha1.StatePreservation{
+					Rules: []policyv1alpha1.StatePreservationRule{
+						{
+							AliasLabelName: "valid-label-name",
+							JSONPath:       "{.availableReplicas}",
+						},
+						{
+							AliasLabelName: "example.com/state-key",
+							JSONPath:       "{.readyReplicas}",
+						},
+					},
+				},
+			},
+			expectedErr: "",
+		},
+		{
+			name: "statePreservation with invalid aliasLabelName - starts with dash",
+			applicationFailoverBehavior: &policyv1alpha1.ApplicationFailoverBehavior{
+				DecisionConditions: policyv1alpha1.DecisionConditions{
+					TolerationSeconds: ptr.To[int32](100),
+				},
+				StatePreservation: &policyv1alpha1.StatePreservation{
+					Rules: []policyv1alpha1.StatePreservationRule{
+						{
+							AliasLabelName: "-invalid-label",
+							JSONPath:       "{.availableReplicas}",
+						},
+					},
+				},
+			},
+			expectedErr: "spec.failover.application.statePreservation.rules[0].aliasLabelName: Invalid value: \"-invalid-label\": name part must consist of alphanumeric characters, '-', '_' or '.', and must start and end with an alphanumeric character (e.g. 'MyName',  or 'my.name',  or '123-abc', regex used for validation is '([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]')",
+		},
+		{
+			name: "statePreservation with invalid aliasLabelName - ends with dash",
+			applicationFailoverBehavior: &policyv1alpha1.ApplicationFailoverBehavior{
+				DecisionConditions: policyv1alpha1.DecisionConditions{
+					TolerationSeconds: ptr.To[int32](100),
+				},
+				StatePreservation: &policyv1alpha1.StatePreservation{
+					Rules: []policyv1alpha1.StatePreservationRule{
+						{
+							AliasLabelName: "invalid-label-",
+							JSONPath:       "{.availableReplicas}",
+						},
+					},
+				},
+			},
+			expectedErr: "spec.failover.application.statePreservation.rules[0].aliasLabelName: Invalid value: \"invalid-label-\": name part must consist of alphanumeric characters, '-', '_' or '.', and must start and end with an alphanumeric character (e.g. 'MyName',  or 'my.name',  or '123-abc', regex used for validation is '([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]')",
+		},
+		{
+			name: "statePreservation with invalid aliasLabelName - contains invalid characters",
+			applicationFailoverBehavior: &policyv1alpha1.ApplicationFailoverBehavior{
+				DecisionConditions: policyv1alpha1.DecisionConditions{
+					TolerationSeconds: ptr.To[int32](100),
+				},
+				StatePreservation: &policyv1alpha1.StatePreservation{
+					Rules: []policyv1alpha1.StatePreservationRule{
+						{
+							AliasLabelName: "invalid@label",
+							JSONPath:       "{.availableReplicas}",
+						},
+					},
+				},
+			},
+			expectedErr: "spec.failover.application.statePreservation.rules[0].aliasLabelName: Invalid value: \"invalid@label\": name part must consist of alphanumeric characters, '-', '_' or '.', and must start and end with an alphanumeric character (e.g. 'MyName',  or 'my.name',  or '123-abc', regex used for validation is '([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]')",
+		},
+		{
+			name: "statePreservation with too long aliasLabelName",
+			applicationFailoverBehavior: &policyv1alpha1.ApplicationFailoverBehavior{
+				DecisionConditions: policyv1alpha1.DecisionConditions{
+					TolerationSeconds: ptr.To[int32](100),
+				},
+				StatePreservation: &policyv1alpha1.StatePreservation{
+					Rules: []policyv1alpha1.StatePreservationRule{
+						{
+							AliasLabelName: strings.Repeat("a", 64),
+							JSONPath:       "{.availableReplicas}",
+						},
+					},
+				},
+			},
+			expectedErr: "spec.failover.application.statePreservation.rules[0].aliasLabelName: Invalid value: \"" + strings.Repeat("a", 64) + "\": name part must be no more than 63 characters",
+		},
+		{
+			name: "statePreservation with multiple invalid rules",
+			applicationFailoverBehavior: &policyv1alpha1.ApplicationFailoverBehavior{
+				DecisionConditions: policyv1alpha1.DecisionConditions{
+					TolerationSeconds: ptr.To[int32](100),
+				},
+				StatePreservation: &policyv1alpha1.StatePreservation{
+					Rules: []policyv1alpha1.StatePreservationRule{
+						{
+							AliasLabelName: "-invalid1",
+							JSONPath:       "{.availableReplicas}",
+						},
+						{
+							AliasLabelName: "invalid2@",
+							JSONPath:       "{.readyReplicas}",
+						},
+					},
+				},
+			},
+			expectedErr: "[spec.failover.application.statePreservation.rules[0].aliasLabelName: Invalid value: \"-invalid1\": name part must consist of alphanumeric characters, '-', '_' or '.', and must start and end with an alphanumeric character (e.g. 'MyName',  or 'my.name',  or '123-abc', regex used for validation is '([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]'), spec.failover.application.statePreservation.rules[1].aliasLabelName: Invalid value: \"invalid2@\": name part must consist of alphanumeric characters, '-', '_' or '.', and must start and end with an alphanumeric character (e.g. 'MyName',  or 'my.name',  or '123-abc', regex used for validation is '([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]')]",
+		},
+		{
+			name: "statePreservation with valid prefix in aliasLabelName",
+			applicationFailoverBehavior: &policyv1alpha1.ApplicationFailoverBehavior{
+				DecisionConditions: policyv1alpha1.DecisionConditions{
+					TolerationSeconds: ptr.To[int32](100),
+				},
+				StatePreservation: &policyv1alpha1.StatePreservation{
+					Rules: []policyv1alpha1.StatePreservationRule{
+						{
+							AliasLabelName: "example.com/my-state",
+							JSONPath:       "{.availableReplicas}",
+						},
+					},
+				},
+			},
+			expectedErr: "",
+		},
+		{
+			name: "statePreservation with invalid prefix in aliasLabelName",
+			applicationFailoverBehavior: &policyv1alpha1.ApplicationFailoverBehavior{
+				DecisionConditions: policyv1alpha1.DecisionConditions{
+					TolerationSeconds: ptr.To[int32](100),
+				},
+				StatePreservation: &policyv1alpha1.StatePreservation{
+					Rules: []policyv1alpha1.StatePreservationRule{
+						{
+							AliasLabelName: "INVALID.COM/my-state",
+							JSONPath:       "{.availableReplicas}",
+						},
+					},
+				},
+			},
+			expectedErr: "spec.failover.application.statePreservation.rules[0].aliasLabelName: Invalid value: \"INVALID.COM/my-state\": prefix part a lowercase RFC 1123 subdomain must consist of lower case alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character (e.g. 'example.com', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*')",
+		},
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
@@ -721,6 +875,68 @@ func TestValidateApplicationFailover(t *testing.T) {
 	}
 }
 
+func TestValidateClusterFailover(t *testing.T) {
+	tests := []struct {
+		name                    string
+		clusterFailoverBehavior *policyv1alpha1.ClusterFailoverBehavior
+		expectedErr             string
+	}{
+		{
+			name:                    "cluster failover is nil",
+			clusterFailoverBehavior: nil,
+			expectedErr:             "",
+		},
+		{
+			name: "cluster failover with valid statePreservation",
+			clusterFailoverBehavior: &policyv1alpha1.ClusterFailoverBehavior{
+				PurgeMode: policyv1alpha1.PurgeModeGracefully,
+				StatePreservation: &policyv1alpha1.StatePreservation{
+					Rules: []policyv1alpha1.StatePreservationRule{
+						{
+							AliasLabelName: "cluster-state",
+							JSONPath:       "{.status.nodeCount}",
+						},
+					},
+				},
+			},
+			expectedErr: "",
+		},
+		{
+			name: "cluster failover with invalid statePreservation",
+			clusterFailoverBehavior: &policyv1alpha1.ClusterFailoverBehavior{
+				PurgeMode: policyv1alpha1.PurgeModeDirectly,
+				StatePreservation: &policyv1alpha1.StatePreservation{
+					Rules: []policyv1alpha1.StatePreservationRule{
+						{
+							AliasLabelName: "-invalid-cluster-label",
+							JSONPath:       "{.status.nodeCount}",
+						},
+					},
+				},
+			},
+			expectedErr: "spec.failover.cluster.statePreservation.rules[0].aliasLabelName: Invalid value: \"-invalid-cluster-label\": name part must consist of alphanumeric characters, '-', '_' or '.', and must start and end with an alphanumeric character (e.g. 'MyName',  or 'my.name',  or '123-abc', regex used for validation is '([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]')",
+		},
+		{
+			name: "cluster failover without statePreservation",
+			clusterFailoverBehavior: &policyv1alpha1.ClusterFailoverBehavior{
+				PurgeMode: policyv1alpha1.PurgeModeGracefully,
+			},
+			expectedErr: "",
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			errs := validateClusterFailover(tt.clusterFailoverBehavior, field.NewPath("spec").Child("failover").Child("cluster"))
+			err := errs.ToAggregate()
+			if err != nil && err.Error() != tt.expectedErr {
+				t.Errorf("expected error:\n  %s, but got:\n  %s", tt.expectedErr, err.Error())
+			} else if err == nil && tt.expectedErr != "" {
+				t.Errorf("expected error:\n  %s, but got no error\n", tt.expectedErr)
+			}
+		})
+	}
+}
+
 func TestValidateCrdsTarBall(t *testing.T) {
 	testItems := []struct {
 		name        string
diff --git a/pkg/webhook/clusterpropagationpolicy/mutating_test.go b/pkg/webhook/clusterpropagationpolicy/mutating_test.go
index 79f84f64c..443f08953 100644
--- a/pkg/webhook/clusterpropagationpolicy/mutating_test.go
+++ b/pkg/webhook/clusterpropagationpolicy/mutating_test.go
@@ -128,7 +128,7 @@ func TestMutatingAdmission_Handle_FullCoverage(t *testing.T) {
 			},
 			Failover: &policyv1alpha1.FailoverBehavior{
 				Application: &policyv1alpha1.ApplicationFailoverBehavior{
-					PurgeMode:          policyv1alpha1.Graciously,
+					PurgeMode:          policyv1alpha1.PurgeModeGracefully,
 					GracePeriodSeconds: nil,
 				},
 			},
@@ -166,7 +166,7 @@ func TestMutatingAdmission_Handle_FullCoverage(t *testing.T) {
 			},
 			Failover: &policyv1alpha1.FailoverBehavior{
 				Application: &policyv1alpha1.ApplicationFailoverBehavior{
-					PurgeMode:          policyv1alpha1.Graciously,
+					PurgeMode:          policyv1alpha1.PurgeModeGracefully,
 					GracePeriodSeconds: ptr.To[int32](failOverGracePeriodSeconds),
 				},
 			},
diff --git a/pkg/webhook/interpreter/http_test.go b/pkg/webhook/interpreter/http_test.go
index 04443821b..a6032c832 100644
--- a/pkg/webhook/interpreter/http_test.go
+++ b/pkg/webhook/interpreter/http_test.go
@@ -59,24 +59,13 @@ func (m *mockBody) Close() error {
 	return nil
 }
 
-// limitedBadResponseWriter is a custom io.Writer implementation that simulates
-// write errors for a specified number of attempts. After a certain number of failures,
-// it allows the write operation to succeed.
-type limitedBadResponseWriter struct {
-	failCount   int
-	maxFailures int
+// brokenWriter implements the io.Writer interface.
+type brokenWriter struct {
+	http.ResponseWriter
 }
 
-// Write simulates writing data to the writer. It forces an error response for
-// a limited number of attempts, specified by maxFailures. Once failCount reaches
-// maxFailures, it allows the write to succeed.
-func (b *limitedBadResponseWriter) Write(p []byte) (n int, err error) {
-	if b.failCount < b.maxFailures {
-		b.failCount++
-		return 0, errors.New("forced write error")
-	}
-	// After reaching maxFailures, allow the write to succeed to stop the infinite loop.
-	return len(p), nil
+func (bw *brokenWriter) Write(_ []byte) (int, error) {
+	return 0, fmt.Errorf("mock: write: broken pipe")
 }
 
 func TestServeHTTP(t *testing.T) {
@@ -294,20 +283,14 @@ func TestWriteResourceInterpreterResponse(t *testing.T) {
 			},
 		},
 		{
-			name:        "WriteResourceInterpreterResponse_FailedToWrite_WriterReachedMaxFailures",
+			name:        "should never run into circular calling if the writer has broken",
 			mockHandler: &HTTPMockHandler{},
 			res: configv1alpha1.ResourceInterpreterContext{
 				Response: &configv1alpha1.ResourceInterpreterResponse{},
 			},
-			rec: &limitedBadResponseWriter{maxFailures: 3},
-			verify: func(writer io.Writer, _ *configv1alpha1.ResourceInterpreterResponse) error {
-				data, ok := writer.(*limitedBadResponseWriter)
-				if !ok {
-					return fmt.Errorf("expected writer of type limitedBadResponseWriter but got %T", writer)
-				}
-				if data.failCount != data.maxFailures {
-					return fmt.Errorf("expected %d write failures, got %d", data.maxFailures, data.failCount)
-				}
+			rec: &brokenWriter{},
+			verify: func(_ io.Writer, _ *configv1alpha1.ResourceInterpreterResponse) error {
+				// reaching here means not running into circular calling
 				return nil
 			},
 		},
diff --git a/pkg/webhook/propagationpolicy/mutating_test.go b/pkg/webhook/propagationpolicy/mutating_test.go
index 2f5882760..d3d2cde75 100644
--- a/pkg/webhook/propagationpolicy/mutating_test.go
+++ b/pkg/webhook/propagationpolicy/mutating_test.go
@@ -132,7 +132,7 @@ func TestMutatingAdmission_Handle_FullCoverage(t *testing.T) {
 			},
 			Failover: &policyv1alpha1.FailoverBehavior{
 				Application: &policyv1alpha1.ApplicationFailoverBehavior{
-					PurgeMode:          policyv1alpha1.Graciously,
+					PurgeMode:          policyv1alpha1.PurgeModeGracefully,
 					GracePeriodSeconds: nil,
 				},
 			},
@@ -172,7 +172,7 @@ func TestMutatingAdmission_Handle_FullCoverage(t *testing.T) {
 			},
 			Failover: &policyv1alpha1.FailoverBehavior{
 				Application: &policyv1alpha1.ApplicationFailoverBehavior{
-					PurgeMode:          policyv1alpha1.Graciously,
+					PurgeMode:          policyv1alpha1.PurgeModeGracefully,
 					GracePeriodSeconds: ptr.To[int32](failOverGracePeriodSeconds),
 				},
 			},
diff --git a/pkg/webhook/resourcebinding/validating_test.go b/pkg/webhook/resourcebinding/validating_test.go
index d2abef855..a8fd3dd28 100644
--- a/pkg/webhook/resourcebinding/validating_test.go
+++ b/pkg/webhook/resourcebinding/validating_test.go
@@ -363,7 +363,7 @@ func TestValidatingAdmission_Handle(t *testing.T) {
 			decoder:            &fakeDecoder{decodeObj: rbUnscheduledCreate},
 			clientObjects:      []client.Object{frqForUnscheduledCreate},
 			featureGateEnabled: true,
-			wantResponse:       admission.Allowed("ResourceBinding not yet scheduled for Create operation."),
+			wantResponse:       admission.Allowed(""),
 		},
 		{
 			name: "update operation with no relevant field change should be allowed",
@@ -374,7 +374,7 @@ func TestValidatingAdmission_Handle(t *testing.T) {
 			decoder:            &fakeDecoder{decodeObj: rbForNoChange, rawDecodedObj: rbForNoChange},
 			clientObjects:      []client.Object{frqForNoChange},
 			featureGateEnabled: true,
-			wantResponse:       admission.Allowed("No quota relevant fields changed."),
+			wantResponse:       admission.Allowed(""),
 		},
 		{
 			name: "total RB delta is zero should be allowed (update with identical states)",
@@ -385,7 +385,7 @@ func TestValidatingAdmission_Handle(t *testing.T) {
 			decoder:            &fakeDecoder{decodeObj: rbForDeltaZeroNew, rawDecodedObj: rbForDeltaZeroOld},
 			clientObjects:      []client.Object{frqForDeltaZero},
 			featureGateEnabled: true,
-			wantResponse:       admission.Allowed("No effective resource quantity delta for ResourceBinding, skipping quota validation."),
+			wantResponse:       admission.Allowed(""),
 		},
 		{
 			name: "no FRQs found should be allowed",
@@ -395,7 +395,7 @@ func TestValidatingAdmission_Handle(t *testing.T) {
 			decoder:            &fakeDecoder{decodeObj: rbForNoFRQ},
 			clientObjects:      []client.Object{},
 			featureGateEnabled: true,
-			wantResponse:       admission.Allowed("No FederatedResourceQuotas found in the namespace, skipping quota check."),
+			wantResponse:       admission.Allowed(""),
 		},
 		{
 			name: "create quota exceeded should deny",
@@ -405,10 +405,7 @@ func TestValidatingAdmission_Handle(t *testing.T) {
 			decoder:            &fakeDecoder{decodeObj: rbCreateExceeds},
 			clientObjects:      []client.Object{frqForCreateExceeds},
 			featureGateEnabled: true,
-			wantResponse: admission.Denied(
-				fmt.Sprintf("Quota exceeded for FederatedResourceQuota %s/%s. ResourceBinding %s/%s will be denied.",
-					frqForCreateExceeds.Namespace, frqForCreateExceeds.Name, rbCreateExceeds.Namespace, rbCreateExceeds.Name),
-			),
+			wantResponse:       admission.Denied("FederatedResourceQuota(quota-ns/frq-create-exceeds) exceeded for resource cpu: requested sum 200m, limit 150m."),
 		},
 		{
 			name: "update passes quota (allowed response, non-dryrun)",
@@ -420,10 +417,7 @@ func TestValidatingAdmission_Handle(t *testing.T) {
 			decoder:            &fakeDecoder{decodeObj: commonRBUpdatePassNew, rawDecodedObj: commonRBUpdatePassOld},
 			clientObjects:      []client.Object{frqForUpdatePassNonDryRun},
 			featureGateEnabled: true,
-			wantResponse: admission.Allowed(
-				fmt.Sprintf("All relevant FederatedResourceQuota checks passed for ResourceBinding %s/%s. Quota check passed for FRQ %s/%s. 1 FRQ(s) updated.",
-					commonRBUpdatePassNew.Namespace, commonRBUpdatePassNew.Name, frqForUpdatePassNonDryRun.Namespace, frqForUpdatePassNonDryRun.Name),
-			),
+			wantResponse:       admission.Allowed(""),
 		},
 		{
 			name: "update passes quota (allowed response, dry run)",
@@ -435,10 +429,7 @@ func TestValidatingAdmission_Handle(t *testing.T) {
 			decoder:            &fakeDecoder{decodeObj: commonRBUpdatePassNew, rawDecodedObj: commonRBUpdatePassOld},
 			clientObjects:      []client.Object{frqForUpdatePassDryRun},
 			featureGateEnabled: true,
-			wantResponse: admission.Allowed(
-				fmt.Sprintf("All relevant FederatedResourceQuota checks passed for ResourceBinding %s/%s. Quota check passed for FRQ %s/%s. 1 FRQ(s) updated.",
-					commonRBUpdatePassNew.Namespace, commonRBUpdatePassNew.Name, frqForUpdatePassDryRun.Namespace, frqForUpdatePassDryRun.Name),
-			),
+			wantResponse:       admission.Allowed(""),
 		},
 		{
 			name: "update fails quota (denied response)",
@@ -449,10 +440,7 @@ func TestValidatingAdmission_Handle(t *testing.T) {
 			decoder:            &fakeDecoder{decodeObj: rbUpdateFailNew, rawDecodedObj: rbUpdateFailOld},
 			clientObjects:      []client.Object{frqForUpdateFail},
 			featureGateEnabled: true,
-			wantResponse: admission.Denied(
-				fmt.Sprintf("Quota exceeded for FederatedResourceQuota %s/%s. ResourceBinding %s/%s will be denied.",
-					frqForUpdateFail.Namespace, frqForUpdateFail.Name, rbUpdateFailNew.Namespace, rbUpdateFailNew.Name),
-			),
+			wantResponse:       admission.Denied("FederatedResourceQuota(quota-ns/frq-update-fail) exceeded for resource cpu: requested sum 110m, limit 100m."),
 		},
 	}
 
diff --git a/test/e2e/suites/base/coverage_docs/failover_test.md b/test/e2e/suites/base/coverage_docs/failover_test.md
index 5698213c1..9087e3fac 100644
--- a/test/e2e/suites/base/coverage_docs/failover_test.md
+++ b/test/e2e/suites/base/coverage_docs/failover_test.md
@@ -4,6 +4,6 @@
 |------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
 | Test if the deployment of failed cluster is rescheduled to other available cluster | deployment failover testing                                                                                 | [Failover](https://karmada.io/docs/next/userguide/failover/failover-overview)                                                                                                                                                                                                                    |
 | Test if the deployment of taint cluster is rescheduled to other available cluster  | taint cluster                                                                                               |                                                                                                                                                                                                                                                                                                  |
-| Test if the deployment will rescheduled to other available cluster                 | application failover with purgeMode graciously when the application come back to healthy on the new cluster | [Application failover](https://karmada.io/docs/next/userguide/failover/application-failover/#:~:text=the%20legacy%20application.-,Graciously%20represents%20that%20Karmada%20will%20wait%20for%20the%20application%20to%20come%20back%20to%20healthy%20on%20the%20new%20cluster,-or%20after%20a) |
-| Test if the deployment will rescheduled to other available cluster (timeout)       | application failover with purgeMode graciously when the GracePeriodSeconds is reach out                     | [Application failover](https://karmada.io/docs/next/userguide/failover/application-failover/#:~:text=after%20a%20timeout%20is%20reached%20before%20evicting%20the%20application)                                                                                                                 |
+| Test if the deployment will rescheduled to other available cluster                 | application failover with purgeMode gracefully when the application come back to healthy on the new cluster | [Application failover](https://karmada.io/docs/next/userguide/failover/application-failover/#:~:text=the%20legacy%20application.-,Graciously%20represents%20that%20Karmada%20will%20wait%20for%20the%20application%20to%20come%20back%20to%20healthy%20on%20the%20new%20cluster,-or%20after%20a) |
+| Test if the deployment will rescheduled to other available cluster (timeout)       | application failover with purgeMode gracefully when the GracePeriodSeconds is reach out                     | [Application failover](https://karmada.io/docs/next/userguide/failover/application-failover/#:~:text=after%20a%20timeout%20is%20reached%20before%20evicting%20the%20application)                                                                                                                 |
 | Test if the deployment will never rescheduled to other available cluster           | application failover with purgeMode never                                                                   |                                                                                                                                                                                                                                                                                                  |
diff --git a/test/e2e/suites/base/failover_test.go b/test/e2e/suites/base/failover_test.go
index c21d2ed83..8b2975c8e 100644
--- a/test/e2e/suites/base/failover_test.go
+++ b/test/e2e/suites/base/failover_test.go
@@ -248,7 +248,7 @@ var _ = framework.SerialDescribe("failover testing", func() {
 		})
 	})
 
-	ginkgo.Context("Application failover testing with purgeMode graciously", func() {
+	ginkgo.Context("Application failover testing with purgeMode gracefully", func() {
 		var policyNamespace, policyName string
 		var deploymentNamespace, deploymentName string
 		var deployment *appsv1.Deployment
@@ -298,7 +298,7 @@ var _ = framework.SerialDescribe("failover testing", func() {
 							DecisionConditions: policyv1alpha1.DecisionConditions{
 								TolerationSeconds: ptr.To[int32](tolerationSeconds),
 							},
-							PurgeMode:          policyv1alpha1.Graciously,
+							PurgeMode:          policyv1alpha1.PurgeModeGracefully,
 							GracePeriodSeconds: ptr.To[int32](gracePeriodSeconds),
 						},
 					},
@@ -315,7 +315,7 @@ var _ = framework.SerialDescribe("failover testing", func() {
 			})
 		})
 
-		ginkgo.It("application failover with purgeMode graciously when the application come back to healthy on the new cluster", func() {
+		ginkgo.It("application failover with purgeMode gracefully when the application come back to healthy on the new cluster", func() {
 			disabledClusters := framework.ExtractTargetClustersFromRB(controlPlaneClient, deployment.Kind, deployment.Namespace, deployment.Name)
 			ginkgo.By("create an error op", func() {
 				overridePolicy = testhelper.NewOverridePolicyByOverrideRules(policyNamespace, policyName, []policyv1alpha1.ResourceSelector{
@@ -378,7 +378,7 @@ var _ = framework.SerialDescribe("failover testing", func() {
 			})
 		})
 
-		ginkgo.It("application failover with purgeMode graciously when the GracePeriodSeconds is reach out", func() {
+		ginkgo.It("application failover with purgeMode gracefully when the GracePeriodSeconds is reach out", func() {
 			gracePeriodSeconds = 10
 			ginkgo.By("update pp", func() {
 				// modify gracePeriodSeconds to create a time difference with tolerationSecond to avoid cluster interference
@@ -435,7 +435,7 @@ var _ = framework.SerialDescribe("failover testing", func() {
 					})
 			})
 
-			ginkgo.By("check whether application failover with purgeMode graciously when the GracePeriodSeconds is reach out", func() {
+			ginkgo.By("check whether application failover with purgeMode gracefully when the GracePeriodSeconds is reach out", func() {
 				framework.WaitDeploymentDisappearOnClusters(disabledClusters, deploymentNamespace, deploymentName)
 				evictionTime := time.Now()
 				gomega.Expect(evictionTime.Sub(beginTime) > time.Duration(gracePeriodSeconds+tolerationSeconds)*time.Second).Should(gomega.BeTrue())