Repository: karmada-io/karmada

Issue:
When propagation is suspended, resources will still be propagated even if resources in member clusters are deleted
<!-- 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**:

First, I propagated a deployment `nginx` to the member cluster `m1` with PropagationPolicy.

Second, I update the PropagationPolicy resource's suspension dispatching to true:

```yaml
apiVersion: policy.karmada.io/v1alpha1
kind: PropagationPolicy
metadata:
  name: nginx-propagation
spec:
  suspension:
    dispatching: true
```

Third, I update the replicas of deployment `nginx`, and it didn't sync to cluster `m1`.

Forth, I delete the propagated deployment `nginx` in the cluster `m1`, then I found that the latest deployment `nginx` in the karmada-control-plane has been propagated to the cluster `m1`.

**What you expected to happen**:

When I set the PropagationPolicy resource's suspension dispatching to true, in any case, propagation should be suspended.

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

Follow the above steps.

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

How did i discover the problem?

I have been working on #6437 recently, and I found that when we replaced work `propagation.karmada.io/instruction` label with the work Suspension field, the following situation occurs: 

when using the mcs feature, the `service-export-controller` collects `EndpointSlices` from the source cluster and stores them in the control plane in the form of works. When the `EndpointSlices` in the source cluster are deleted, they will be recreated by Karmada, which is obviously incorrect. We expect that the `EndpointSlice` should only be collected.

**Environment**:
- Karmada version: v1.14
- kubectl-karmada or karmadactl version (the result of `kubectl-karmada version` or `karmadactl version`):
- Others:


Hints from issue comments:
@XiShanYongYe-Chang: Hi @a7i, I would like to know if this issue has any impact on your usage?

/cc @a7i @a7i: Hi @XiShanYongYe-Chang I will take a look tomorrow. I need to set up my environment to test@XiShanYongYe-Chang: /assign

Solution patch:
diff --git a/pkg/controllers/status/work_status_controller.go b/pkg/controllers/status/work_status_controller.go
index 7e40eb255..78a698584 100644
--- a/pkg/controllers/status/work_status_controller.go
+++ b/pkg/controllers/status/work_status_controller.go
@@ -291,6 +291,11 @@ func (c *WorkStatusController) handleDeleteEvent(ctx context.Context, key keys.F
 		return nil
 	}
 
+	// skip processing as the work object is suspended for dispatching.
+	if util.IsWorkSuspendDispatching(work) {
+		return nil
+	}
+
 	//nolint:staticcheck // SA1019 ignore deprecated util.PropagationInstruction
 	if util.GetLabelValue(work.Labels, util.PropagationInstruction) == util.PropagationInstructionSuppressed {
 		return nil



Test patch:
diff --git a/pkg/controllers/status/work_status_controller_test.go b/pkg/controllers/status/work_status_controller_test.go
index 28670559e..4961e4ee7 100644
--- a/pkg/controllers/status/work_status_controller_test.go
+++ b/pkg/controllers/status/work_status_controller_test.go
@@ -26,6 +26,7 @@ import (
 	"github.com/stretchr/testify/assert"
 	corev1 "k8s.io/api/core/v1"
 	rbacv1 "k8s.io/api/rbac/v1"
+	apierrors "k8s.io/apimachinery/pkg/api/errors"
 	"k8s.io/apimachinery/pkg/api/meta"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
@@ -579,6 +580,7 @@ func TestWorkStatusController_syncWorkStatus(t *testing.T) {
 		expectedError             bool
 		wrongWorkNS               bool
 		workApplyFunc             func(work *workv1alpha1.Work)
+		assertFunc                func(t *testing.T, dynamicClientSets *dynamicfake.FakeDynamicClient)
 	}{
 		{
 			name:                      "failed to exec NeedUpdate",
@@ -669,6 +671,23 @@ func TestWorkStatusController_syncWorkStatus(t *testing.T) {
 				work.SetDeletionTimestamp(ptr.To(metav1.Now()))
 			},
 		},
+		{
+			name:                      "resource not found, work suspendDispatching true, should not recreate resource",
+			obj:                       newPodObj("karmada-es-cluster"),
+			pod:                       nil, // Simulate the resource does not exist in the member cluster
+			raw:                       []byte(`{"apiVersion":"v1","kind":"Pod","metadata":{"name":"pod","namespace":"default"}}`),
+			controllerWithoutInformer: true,
+			expectedError:             false,
+			workApplyFunc: func(work *workv1alpha1.Work) {
+				work.Spec.SuspendDispatching = ptr.To(true)
+			},
+			assertFunc: func(t *testing.T, dynamicClientSets *dynamicfake.FakeDynamicClient) {
+				gvr := corev1.SchemeGroupVersion.WithResource("pods")
+				obj, err := dynamicClientSets.Resource(gvr).Namespace("default").Get(context.Background(), "pod", metav1.GetOptions{})
+				assert.True(t, apierrors.IsNotFound(err), "expected a NotFound error but got: %s", err)
+				assert.Nil(t, obj)
+			},
+		},
 	}
 
 	for _, tt := range tests {
@@ -709,6 +728,10 @@ func TestWorkStatusController_syncWorkStatus(t *testing.T) {
 			} else {
 				assert.NoError(t, err)
 			}
+
+			if tt.assertFunc != nil {
+				tt.assertFunc(t, dynamicClientSet)
+			}
 		})
 	}
 }