@@ -20,6 +20,7 @@ import (
2020 "context"
2121 "fmt"
2222 "reflect"
23+ "strings"
2324 "testing"
2425 "time"
2526
@@ -34,6 +35,7 @@ import (
3435 "k8s.io/client-go/dynamic"
3536 dynamicfake "k8s.io/client-go/dynamic/fake"
3637 "k8s.io/client-go/kubernetes/scheme"
38+ "k8s.io/client-go/tools/record"
3739 "k8s.io/utils/ptr"
3840 "sigs.k8s.io/controller-runtime/pkg/client"
3941 "sigs.k8s.io/controller-runtime/pkg/client/fake"
@@ -42,6 +44,7 @@ import (
4244 configv1alpha1 "github.com/karmada-io/karmada/pkg/apis/config/v1alpha1"
4345 policyv1alpha1 "github.com/karmada-io/karmada/pkg/apis/policy/v1alpha1"
4446 workv1alpha2 "github.com/karmada-io/karmada/pkg/apis/work/v1alpha2"
47+ "github.com/karmada-io/karmada/pkg/events"
4548 "github.com/karmada-io/karmada/pkg/util"
4649 "github.com/karmada-io/karmada/pkg/util/fedinformer/genericmanager"
4750 "github.com/karmada-io/karmada/pkg/util/fedinformer/keys"
@@ -2523,7 +2526,15 @@ func Test_createOrUpdateAttachedBinding(t *testing.T) {
25232526 },
25242527 },
25252528 }
2526- return fake .NewClientBuilder ().WithScheme (Scheme ).WithObjects (rb ).Build ()
2529+
2530+ // Add the referenced ResourceBindings that will be looked up during conflict checks.
2531+ ref1 := & workv1alpha2.ResourceBinding {ObjectMeta : metav1.ObjectMeta {Name : "default-binding-1" , Namespace : "default-1" }}
2532+ ref2 := & workv1alpha2.ResourceBinding {ObjectMeta : metav1.ObjectMeta {Name : "default-binding-2" , Namespace : "default-2" }}
2533+ ref3 := & workv1alpha2.ResourceBinding {ObjectMeta : metav1.ObjectMeta {Name : "default-binding-3" , Namespace : "default-3" }}
2534+ ref4 := & workv1alpha2.ResourceBinding {ObjectMeta : metav1.ObjectMeta {Name : "test-binding-1" , Namespace : "test-1" }}
2535+ ref5 := & workv1alpha2.ResourceBinding {ObjectMeta : metav1.ObjectMeta {Name : "test-binding-2" , Namespace : "test-2" }}
2536+
2537+ return fake .NewClientBuilder ().WithScheme (Scheme ).WithObjects (rb , ref1 , ref2 , ref3 , ref4 , ref5 ).Build ()
25272538 },
25282539 },
25292540 {
@@ -2826,7 +2837,15 @@ func Test_createOrUpdateAttachedBinding(t *testing.T) {
28262837 },
28272838 },
28282839 }
2829- return fake .NewClientBuilder ().WithScheme (Scheme ).WithObjects (rb ).Build ()
2840+
2841+ // Referenced ResourceBindings for conflict checks
2842+ ref1 := & workv1alpha2.ResourceBinding {ObjectMeta : metav1.ObjectMeta {Name : "default-binding-1" , Namespace : "default-1" }}
2843+ ref2 := & workv1alpha2.ResourceBinding {ObjectMeta : metav1.ObjectMeta {Name : "default-binding-2" , Namespace : "default-2" }}
2844+ ref3 := & workv1alpha2.ResourceBinding {ObjectMeta : metav1.ObjectMeta {Name : "default-binding-3" , Namespace : "default-3" }}
2845+ ref4 := & workv1alpha2.ResourceBinding {ObjectMeta : metav1.ObjectMeta {Name : "test-binding-1" , Namespace : "test-1" }}
2846+ ref5 := & workv1alpha2.ResourceBinding {ObjectMeta : metav1.ObjectMeta {Name : "test-binding-2" , Namespace : "test-2" }}
2847+
2848+ return fake .NewClientBuilder ().WithScheme (Scheme ).WithObjects (rb , ref1 , ref2 , ref3 , ref4 , ref5 ).Build ()
28302849 },
28312850 },
28322851 {
@@ -2933,7 +2952,12 @@ func Test_createOrUpdateAttachedBinding(t *testing.T) {
29332952 },
29342953 },
29352954 }
2936- return fake .NewClientBuilder ().WithScheme (Scheme ).WithObjects (rb ).Build ()
2955+
2956+ // Referenced ResourceBindings for conflict checks
2957+ ref1 := & workv1alpha2.ResourceBinding {ObjectMeta : metav1.ObjectMeta {Name : "default-binding-1" , Namespace : "default-1" }}
2958+ ref2 := & workv1alpha2.ResourceBinding {ObjectMeta : metav1.ObjectMeta {Name : "test-binding-1" , Namespace : "test-1" }}
2959+
2960+ return fake .NewClientBuilder ().WithScheme (Scheme ).WithObjects (rb , ref1 , ref2 ).Build ()
29372961 },
29382962 },
29392963 }
@@ -2942,7 +2966,7 @@ func Test_createOrUpdateAttachedBinding(t *testing.T) {
29422966 d := & DependenciesDistributor {
29432967 Client : tt .setupClient (),
29442968 }
2945- err := d .createOrUpdateAttachedBinding (tt .attachedBinding )
2969+ err := d .createOrUpdateAttachedBinding (context . TODO (), tt .attachedBinding )
29462970 if (err != nil ) != tt .wantErr {
29472971 t .Errorf ("createOrUpdateAttachedBinding() error = %v, wantErr %v" , err , tt .wantErr )
29482972 }
@@ -2962,6 +2986,87 @@ func Test_createOrUpdateAttachedBinding(t *testing.T) {
29622986 }
29632987}
29642988
2989+ func Test_createOrUpdateAttachedBinding_emitsConflictEvent (t * testing.T ) {
2990+ Scheme := runtime .NewScheme ()
2991+ utilruntime .Must (scheme .AddToScheme (Scheme ))
2992+ utilruntime .Must (workv1alpha2 .Install (Scheme ))
2993+
2994+ // Existing attached binding to be updated (generated by dependency mechanism: Placement == nil)
2995+ exist := & workv1alpha2.ResourceBinding {
2996+ ObjectMeta : metav1.ObjectMeta {
2997+ Name : "test-binding" ,
2998+ Namespace : "test" ,
2999+ ResourceVersion : "1000" ,
3000+ OwnerReferences : []metav1.OwnerReference {{
3001+ UID : "uid-1" ,
3002+ Controller : ptr.To [bool ](true ),
3003+ }},
3004+ },
3005+ Spec : workv1alpha2.ResourceBindingSpec {},
3006+ }
3007+
3008+ // Referenced ResourceBindings with conflicting policies
3009+ rb1 := & workv1alpha2.ResourceBinding {
3010+ ObjectMeta : metav1.ObjectMeta {Name : "rb-1" , Namespace : "ns-a" },
3011+ Spec : workv1alpha2.ResourceBindingSpec {
3012+ PreserveResourcesOnDeletion : ptr .To (true ),
3013+ ConflictResolution : policyv1alpha1 .ConflictOverwrite ,
3014+ },
3015+ }
3016+ rb2 := & workv1alpha2.ResourceBinding {
3017+ ObjectMeta : metav1.ObjectMeta {Name : "rb-2" , Namespace : "ns-b" },
3018+ Spec : workv1alpha2.ResourceBindingSpec {
3019+ PreserveResourcesOnDeletion : ptr .To (false ),
3020+ },
3021+ }
3022+
3023+ attached := & workv1alpha2.ResourceBinding {
3024+ ObjectMeta : metav1.ObjectMeta {
3025+ Name : "test-binding" ,
3026+ Namespace : "test" ,
3027+ OwnerReferences : []metav1.OwnerReference {{
3028+ UID : "uid-1" ,
3029+ Controller : ptr.To [bool ](true ),
3030+ }},
3031+ },
3032+ Spec : workv1alpha2.ResourceBindingSpec {
3033+ Resource : workv1alpha2.ObjectReference {APIVersion : "apps/v1" , Kind : "Deployment" , Namespace : "fake-ns" , Name : "demo-app" , ResourceVersion : "1" },
3034+ RequiredBy : []workv1alpha2.BindingSnapshot {
3035+ {Namespace : "ns-a" , Name : "rb-1" },
3036+ {Namespace : "ns-b" , Name : "rb-2" },
3037+ },
3038+ },
3039+ }
3040+
3041+ fakeRec := record .NewFakeRecorder (10 )
3042+ d := & DependenciesDistributor {
3043+ Client : fake .NewClientBuilder ().WithScheme (Scheme ).WithObjects (exist , rb1 , rb2 ).Build (),
3044+ EventRecorder : fakeRec ,
3045+ }
3046+
3047+ if err := d .createOrUpdateAttachedBinding (context .TODO (), attached ); err != nil {
3048+ t .Fatalf ("createOrUpdateAttachedBinding() error = %v" , err )
3049+ }
3050+
3051+ select {
3052+ case e := <- fakeRec .Events :
3053+ if ! strings .Contains (e , corev1 .EventTypeWarning ) {
3054+ t .Fatalf ("expected Warning event, got %q" , e )
3055+ }
3056+ if ! strings .Contains (e , events .EventReasonDependencyPolicyConflict ) {
3057+ t .Fatalf ("expected reason %q in event, got %q" , events .EventReasonDependencyPolicyConflict , e )
3058+ }
3059+ if ! strings .Contains (e , "ConflictResolution mismatch (Overwrite vs Abort)" ) {
3060+ t .Fatalf ("expected ConflictResolution mismatch hint in event, got %q" , e )
3061+ }
3062+ if ! strings .Contains (e , "PreserveResourcesOnDeletion mismatch (true vs false)" ) {
3063+ t .Fatalf ("expected PreserveResourcesOnDeletion mismatch hint in event, got %q" , e )
3064+ }
3065+ case <- time .After (1 * time .Second ):
3066+ t .Fatalf ("expected dependency policy conflict event, but none received" )
3067+ }
3068+ }
3069+
29653070func Test_buildAttachedBinding (t * testing.T ) {
29663071 blockOwnerDeletion := true
29673072 isController := true
@@ -3432,3 +3537,69 @@ func Test_deleteBindingFromSnapshot(t *testing.T) {
34323537 })
34333538 }
34343539}
3540+
3541+ func Test_ResolveResourceBindingFromSnapshots_returnsBindings (t * testing.T ) {
3542+ Scheme := runtime .NewScheme ()
3543+ utilruntime .Must (scheme .AddToScheme (Scheme ))
3544+ utilruntime .Must (workv1alpha2 .Install (Scheme ))
3545+
3546+ rb1 := & workv1alpha2.ResourceBinding {ObjectMeta : metav1.ObjectMeta {Name : "rb-1" , Namespace : "ns-a" }}
3547+ rb2 := & workv1alpha2.ResourceBinding {ObjectMeta : metav1.ObjectMeta {Name : "rb-2" , Namespace : "ns-b" }}
3548+
3549+ d := & DependenciesDistributor {
3550+ Client : fake .NewClientBuilder ().WithScheme (Scheme ).WithObjects (rb1 , rb2 ).Build (),
3551+ }
3552+
3553+ snaps := []workv1alpha2.BindingSnapshot {
3554+ {Namespace : "ns-a" , Name : "rb-1" },
3555+ {Namespace : "ns-b" , Name : "rb-2" },
3556+ }
3557+
3558+ got , err := d .ResolveResourceBindingFromSnapshots (context .TODO (), snaps )
3559+ if err != nil {
3560+ t .Fatalf ("ResolveResourceBindingFromSnapshots() error = %v" , err )
3561+ }
3562+ if len (got ) != 2 {
3563+ t .Fatalf ("ResolveResourceBindingFromSnapshots() len = %d, want 2" , len (got ))
3564+ }
3565+ if got [0 ].Name != "rb-1" || got [0 ].Namespace != "ns-a" {
3566+ t .Fatalf ("ResolveResourceBindingFromSnapshots()[0] = %s/%s, want ns-a/rb-1" , got [0 ].Namespace , got [0 ].Name )
3567+ }
3568+ if got [1 ].Name != "rb-2" || got [1 ].Namespace != "ns-b" {
3569+ t .Fatalf ("ResolveResourceBindingFromSnapshots()[1] = %s/%s, want ns-b/rb-2" , got [1 ].Namespace , got [1 ].Name )
3570+ }
3571+ }
3572+
3573+ func Test_conflictDetectedForConflictResolution_detectsConflict (t * testing.T ) {
3574+ d := & DependenciesDistributor {}
3575+
3576+ rbs := []* workv1alpha2.ResourceBinding {
3577+ {Spec : workv1alpha2.ResourceBindingSpec {ConflictResolution : policyv1alpha1 .ConflictOverwrite }},
3578+ {Spec : workv1alpha2.ResourceBindingSpec {}},
3579+ }
3580+
3581+ has , err := d .conflictDetectedForConflictResolution (rbs )
3582+ if err != nil {
3583+ t .Fatalf ("conflictDetectedForConflictResolution() error = %v" , err )
3584+ }
3585+ if ! has {
3586+ t .Fatalf ("conflictDetectedForConflictResolution() = false, want true" )
3587+ }
3588+ }
3589+
3590+ func Test_conflictDetectedForPreserveOnDeletion_detectsConflict (t * testing.T ) {
3591+ d := & DependenciesDistributor {}
3592+
3593+ rbs := []* workv1alpha2.ResourceBinding {
3594+ {Spec : workv1alpha2.ResourceBindingSpec {PreserveResourcesOnDeletion : ptr .To (true )}},
3595+ {Spec : workv1alpha2.ResourceBindingSpec {PreserveResourcesOnDeletion : ptr .To (false )}},
3596+ }
3597+
3598+ has , err := d .conflictDetectedForPreserveOnDeletion (rbs )
3599+ if err != nil {
3600+ t .Fatalf ("conflictDetectedForPreserveOnDeletion() error = %v" , err )
3601+ }
3602+ if ! has {
3603+ t .Fatalf ("conflictDetectedForPreserveOnDeletion() = false, want true" )
3604+ }
3605+ }
0 commit comments