@@ -3,6 +3,7 @@ package bucketclaim
33import (
44 "context"
55 "fmt"
6+ "sync"
67 "testing"
78
89 v1 "k8s.io/api/core/v1"
@@ -13,6 +14,7 @@ import (
1314 "sigs.k8s.io/container-object-storage-interface/client/apis/objectstorage/v1alpha1"
1415 fakebucketclientset "sigs.k8s.io/container-object-storage-interface/client/clientset/versioned/fake"
1516 "sigs.k8s.io/container-object-storage-interface/controller/pkg/util"
17+ "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
1618)
1719
1820var classGoldParameters = map [string ]string {
@@ -352,3 +354,82 @@ func TestAddDeletedBucketClaim(t *testing.T) {
352354 t .Fatalf ("expected 0 buckets, got %d" , len (bl .Items ))
353355 }
354356}
357+
358+ // Test retry logic for conflict errors during status update
359+ func TestRetryOnConflictStatusUpdate (t * testing.T ) {
360+ ctx , cancel := context .WithCancel (context .Background ())
361+ defer cancel ()
362+
363+ client := fakebucketclientset .NewSimpleClientset ()
364+ kubeClient := fakekubeclientset .NewSimpleClientset ()
365+ eventRecorder := record .NewFakeRecorder (3 )
366+
367+ listener := NewBucketClaimListener ()
368+ listener .InitializeKubeClient (kubeClient )
369+ listener .InitializeBucketClient (client )
370+ listener .InitializeEventRecorder (eventRecorder )
371+
372+ bucketclass , err := util .CreateBucketClass (ctx , client , & goldClass )
373+ if err != nil {
374+ t .Fatalf ("Error occurred when creating BucketClass: %v" , err )
375+ }
376+
377+ bucketClaim , err := util .CreateBucketClaim (ctx , client , & bucketClaim1 )
378+ if err != nil {
379+ t .Fatalf ("Error occurred when creating BucketClaim: %v" , err )
380+ }
381+
382+ // Cleanup
383+ defer util .DeleteObjects (ctx , client , * bucketClaim , * bucketclass )
384+
385+ // Simulate concurrent modification by updating the BucketClaim in a goroutine
386+ // This will cause resourceVersion to change, simulating a conflict scenario
387+ var wg sync.WaitGroup
388+ wg .Add (1 )
389+ go func () {
390+ defer wg .Done ()
391+ for i := range 10 {
392+ // Fetch and update the BucketClaim to change its resourceVersion
393+ bc , getErr := client .ObjectstorageV1alpha1 ().BucketClaims (bucketClaim .Namespace ).Get (ctx , bucketClaim .Name , metav1.GetOptions {})
394+ if getErr != nil {
395+ return
396+ }
397+ // Add an annotation to change the resourceVersion
398+ if bc .Annotations == nil {
399+ bc .Annotations = make (map [string ]string )
400+ }
401+ bc .Annotations [fmt .Sprintf ("test-%d" , i )] = "value"
402+ _ , _ = client .ObjectstorageV1alpha1 ().BucketClaims (bc .Namespace ).Update (ctx , bc , metav1.UpdateOptions {})
403+ }
404+ }()
405+
406+ // Call Add which should handle conflicts with retry logic
407+ err = listener .Add (ctx , bucketClaim )
408+ if err != nil {
409+ t .Fatalf ("Add should succeed even with concurrent modifications: %v" , err )
410+ }
411+
412+ // Wait for the goroutine to complete to ensure all concurrent updates are done
413+ wg .Wait ()
414+
415+ // Verify the final state - status should be updated correctly
416+ updatedClaim , err := client .ObjectstorageV1alpha1 ().BucketClaims (bucketClaim .Namespace ).Get (ctx , bucketClaim .Name , metav1.GetOptions {})
417+ if err != nil {
418+ t .Fatalf ("Error occurred when reading BucketClaim: %v" , err )
419+ }
420+
421+ // Verify status was updated
422+ expectedBucketName := fmt .Sprintf ("bucket-%s" , bucketClaim .UID )
423+ if updatedClaim .Status .BucketName != expectedBucketName {
424+ t .Errorf ("Expected BucketName %s, got %s" , expectedBucketName , updatedClaim .Status .BucketName )
425+ }
426+
427+ if updatedClaim .Status .BucketReady != false {
428+ t .Errorf ("Expected BucketReady to be false, got %v" , updatedClaim .Status .BucketReady )
429+ }
430+
431+ // Verify finalizer was added
432+ if ! controllerutil .ContainsFinalizer (updatedClaim , util .BucketClaimFinalizer ) {
433+ t .Errorf ("Expected finalizer to be added, but it was not found" )
434+ }
435+ }
0 commit comments