@@ -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,94 @@ 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 (
394+ ctx ,
395+ bucketClaim .Name ,
396+ metav1.GetOptions {},
397+ )
398+ if getErr != nil {
399+ return
400+ }
401+ // Add an annotation to change the resourceVersion
402+ if bc .Annotations == nil {
403+ bc .Annotations = make (map [string ]string )
404+ }
405+ bc .Annotations [fmt .Sprintf ("test-%d" , i )] = "value"
406+ _ , _ = client .ObjectstorageV1alpha1 ().BucketClaims (bc .Namespace ).Update (
407+ ctx ,
408+ bc ,
409+ metav1.UpdateOptions {},
410+ )
411+ }
412+ }()
413+
414+ // Call Add which should handle conflicts with retry logic
415+ err = listener .Add (ctx , bucketClaim )
416+ if err != nil {
417+ t .Fatalf ("Add should succeed even with concurrent modifications: %v" , err )
418+ }
419+
420+ // Wait for the goroutine to complete to ensure all concurrent updates are done
421+ wg .Wait ()
422+
423+ // Verify the final state - status should be updated correctly
424+ updatedClaim , err := client .ObjectstorageV1alpha1 ().BucketClaims (bucketClaim .Namespace ).Get (
425+ ctx ,
426+ bucketClaim .Name ,
427+ metav1.GetOptions {},
428+ )
429+ if err != nil {
430+ t .Fatalf ("Error occurred when reading BucketClaim: %v" , err )
431+ }
432+
433+ // Verify status was updated
434+ expectedBucketName := fmt .Sprintf ("bucket-%s" , bucketClaim .UID )
435+ if updatedClaim .Status .BucketName != expectedBucketName {
436+ t .Errorf ("Expected BucketName %s, got %s" , expectedBucketName , updatedClaim .Status .BucketName )
437+ }
438+
439+ if updatedClaim .Status .BucketReady != false {
440+ t .Errorf ("Expected BucketReady to be false, got %v" , updatedClaim .Status .BucketReady )
441+ }
442+
443+ // Verify finalizer was added
444+ if ! controllerutil .ContainsFinalizer (updatedClaim , util .BucketClaimFinalizer ) {
445+ t .Errorf ("Expected finalizer to be added, but it was not found" )
446+ }
447+ }
0 commit comments