Skip to content

Commit c952f23

Browse files
authored
Merge pull request #89 from kvaps/fix-deletion
Fix reconciliation logic for existing and deleted objects
2 parents c2f6e65 + 1f832fc commit c952f23

File tree

6 files changed

+248
-52
lines changed

6 files changed

+248
-52
lines changed

controller/pkg/bucketclaim/bucketclaim.go

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ func NewBucketClaimListener() *BucketClaimListener {
3232

3333
// Add creates a bucket in response to a bucketClaim
3434
func (b *BucketClaimListener) Add(ctx context.Context, bucketClaim *v1alpha1.BucketClaim) error {
35+
if !bucketClaim.GetDeletionTimestamp().IsZero() {
36+
return b.handleDeletion(ctx, bucketClaim)
37+
}
38+
3539
klog.V(3).InfoS("Add BucketClaim",
3640
"name", bucketClaim.ObjectMeta.Name,
3741
"ns", bucketClaim.ObjectMeta.Namespace,
@@ -76,18 +80,11 @@ func (b *BucketClaimListener) Update(ctx context.Context, old, new *v1alpha1.Buc
7680
bucketClaim := new.DeepCopy()
7781

7882
if !new.GetDeletionTimestamp().IsZero() {
79-
if controllerutil.ContainsFinalizer(bucketClaim, util.BucketClaimFinalizer) {
80-
bucketName := bucketClaim.Status.BucketName
81-
err := b.buckets().Delete(ctx, bucketName, metav1.DeleteOptions{})
82-
if err != nil {
83-
klog.V(3).ErrorS(err, "Error deleting bucket",
84-
"bucket", bucketName,
85-
"bucketClaim", bucketClaim.ObjectMeta.Name)
86-
return b.recordError(bucketClaim, v1.EventTypeWarning, v1alpha1.FailedDeleteBucket, err)
87-
}
88-
89-
klog.V(5).Infof("Successfully deleted bucket: %s from bucketClaim: %s", bucketName, bucketClaim.ObjectMeta.Name)
90-
}
83+
return b.handleDeletion(ctx, bucketClaim)
84+
}
85+
86+
if err := b.Add(ctx, bucketClaim); err != nil {
87+
return err
9188
}
9289

9390
klog.V(3).InfoS("Update BucketClaim success",
@@ -96,6 +93,27 @@ func (b *BucketClaimListener) Update(ctx context.Context, old, new *v1alpha1.Buc
9693
return nil
9794
}
9895

96+
// handleDeletion processes the deletion of a bucketClaim.
97+
func (b *BucketClaimListener) handleDeletion(ctx context.Context, bucketClaim *v1alpha1.BucketClaim) error {
98+
if !controllerutil.ContainsFinalizer(bucketClaim, util.BucketClaimFinalizer) {
99+
return nil
100+
}
101+
102+
bucketName := bucketClaim.Status.BucketName
103+
if bucketName != "" {
104+
if err := b.buckets().Delete(ctx, bucketName, metav1.DeleteOptions{}); err != nil && !kubeerrors.IsNotFound(err) {
105+
klog.V(3).ErrorS(err, "Error deleting bucket",
106+
"bucket", bucketName,
107+
"bucketClaim", bucketClaim.ObjectMeta.Name)
108+
return b.recordError(bucketClaim, v1.EventTypeWarning, v1alpha1.FailedDeleteBucket, err)
109+
}
110+
klog.V(5).Infof("Successfully requested deletion of bucket: %s for bucketClaim: %s",
111+
bucketName, bucketClaim.ObjectMeta.Name)
112+
}
113+
114+
return nil
115+
}
116+
99117
// Delete processes a bucket for which bucket request is deleted
100118
func (b *BucketClaimListener) Delete(ctx context.Context, bucketClaim *v1alpha1.BucketClaim) error {
101119
klog.V(3).InfoS("Delete BucketClaim",

controller/pkg/bucketclaim/bucketclaim_test.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -323,3 +323,32 @@ func TestRecordEvents(t *testing.T) {
323323
func newEvent(eventType, reason, message string) string {
324324
return fmt.Sprintf("%s %s %s", eventType, reason, message)
325325
}
326+
327+
// Claim already marked for deletion must not create a bucket
328+
func TestAddDeletedBucketClaim(t *testing.T) {
329+
ctx, cancel := context.WithCancel(context.Background())
330+
defer cancel()
331+
332+
client := fakebucketclientset.NewSimpleClientset()
333+
kubeClient := fakekubeclientset.NewSimpleClientset()
334+
eventRecorder := record.NewFakeRecorder(3)
335+
336+
listener := NewBucketClaimListener()
337+
listener.InitializeKubeClient(kubeClient)
338+
listener.InitializeBucketClient(client)
339+
listener.InitializeEventRecorder(eventRecorder)
340+
341+
_, _ = util.CreateBucketClass(ctx, client, &goldClass)
342+
343+
claimToDelete := bucketClaim1.DeepCopy()
344+
now := metav1.Now()
345+
claimToDelete.ObjectMeta.DeletionTimestamp = &now
346+
347+
if err := listener.Add(ctx, claimToDelete); err != nil {
348+
t.Fatalf("Add returned error for deleted claim: %v", err)
349+
}
350+
351+
if bl := util.GetBuckets(ctx, client, 0); len(bl.Items) != 0 {
352+
t.Fatalf("expected 0 buckets, got %d", len(bl.Items))
353+
}
354+
}

sidecar/pkg/bucket/bucket_controller.go

Lines changed: 47 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,10 @@ func (b *BucketListener) Add(ctx context.Context, inputBucket *v1alpha1.Bucket)
6868

6969
var err error
7070

71+
if !bucket.GetDeletionTimestamp().IsZero() {
72+
return b.handleDeletion(ctx, bucket)
73+
}
74+
7175
klog.V(3).InfoS("Add Bucket",
7276
"name", bucket.ObjectMeta.Name)
7377

@@ -212,55 +216,60 @@ func (b *BucketListener) Update(ctx context.Context, old, new *v1alpha1.Bucket)
212216
var err error
213217

214218
if !bucket.GetDeletionTimestamp().IsZero() {
215-
if controllerutil.ContainsFinalizer(bucket, consts.BABucketFinalizer) {
216-
bucketClaimNs := bucket.Spec.BucketClaim.Namespace
217-
bucketClaimName := bucket.Spec.BucketClaim.Name
218-
bucketAccessList, err := b.bucketAccesses(bucketClaimNs).List(ctx, metav1.ListOptions{})
219-
if err != nil {
220-
klog.V(3).ErrorS(err, "Error fetching BucketAccessList",
221-
"bucket", bucket.ObjectMeta.Name)
222-
return err
223-
}
224-
225-
for _, bucketAccess := range bucketAccessList.Items {
226-
if strings.EqualFold(bucketAccess.Spec.BucketClaimName, bucketClaimName) {
227-
err = b.bucketAccesses(bucketClaimNs).Delete(ctx, bucketAccess.Name, metav1.DeleteOptions{})
228-
if err != nil {
229-
klog.V(3).ErrorS(err, "Error deleting BucketAccess",
230-
"name", bucketAccess.Name,
231-
"bucket", bucket.ObjectMeta.Name)
232-
return err
233-
}
234-
}
235-
}
219+
return b.handleDeletion(ctx, bucket)
220+
}
236221

237-
klog.V(5).Infof("Successfully deleted dependent bucketAccess of bucket:%s", bucket.ObjectMeta.Name)
222+
if err = b.Add(ctx, bucket); err != nil {
223+
return err
224+
}
238225

239-
controllerutil.RemoveFinalizer(bucket, consts.BABucketFinalizer)
240-
klog.V(5).Infof("Successfully removed finalizer: %s of bucket: %s", consts.BABucketFinalizer, bucket.ObjectMeta.Name)
241-
}
226+
klog.V(3).InfoS("Update Bucket success",
227+
"name", bucket.ObjectMeta.Name,
228+
"ns", bucket.ObjectMeta.Namespace)
229+
return nil
230+
}
242231

243-
if controllerutil.ContainsFinalizer(bucket, consts.BucketFinalizer) {
244-
err = b.deleteBucketOp(ctx, bucket)
245-
if err != nil {
246-
return b.recordError(bucket, v1.EventTypeWarning, v1alpha1.FailedDeleteBucket, err)
247-
}
232+
func (b *BucketListener) handleDeletion(ctx context.Context, bucket *v1alpha1.Bucket) error {
233+
var err error
248234

249-
controllerutil.RemoveFinalizer(bucket, consts.BucketFinalizer)
250-
klog.V(5).Infof("Successfully removed finalizer: %s of bucket: %s", consts.BucketFinalizer, bucket.ObjectMeta.Name)
251-
}
235+
if controllerutil.ContainsFinalizer(bucket, consts.BABucketFinalizer) {
236+
bucketClaimNs := bucket.Spec.BucketClaim.Namespace
237+
bucketClaimName := bucket.Spec.BucketClaim.Name
252238

253-
_, err = b.buckets().Update(ctx, bucket, metav1.UpdateOptions{})
239+
bucketAccessList, err := b.bucketAccesses(bucketClaimNs).List(ctx, metav1.ListOptions{})
254240
if err != nil {
255-
klog.V(3).ErrorS(err, "Error updating bucket after removing finalizers",
241+
klog.V(3).ErrorS(err, "Error fetching BucketAccessList",
256242
"bucket", bucket.ObjectMeta.Name)
257243
return err
258244
}
245+
246+
for _, ba := range bucketAccessList.Items {
247+
if strings.EqualFold(ba.Spec.BucketClaimName, bucketClaimName) {
248+
if err = b.bucketAccesses(bucketClaimNs).Delete(ctx, ba.Name, metav1.DeleteOptions{}); err != nil {
249+
klog.V(3).ErrorS(err, "Error deleting BucketAccess",
250+
"name", ba.Name,
251+
"bucket", bucket.ObjectMeta.Name)
252+
return err
253+
}
254+
}
255+
}
256+
257+
controllerutil.RemoveFinalizer(bucket, consts.BABucketFinalizer)
258+
}
259+
260+
if controllerutil.ContainsFinalizer(bucket, consts.BucketFinalizer) {
261+
if err = b.deleteBucketOp(ctx, bucket); err != nil {
262+
return b.recordError(bucket, v1.EventTypeWarning, v1alpha1.FailedDeleteBucket, err)
263+
}
264+
controllerutil.RemoveFinalizer(bucket, consts.BucketFinalizer)
265+
}
266+
267+
if _, err = b.buckets().Update(ctx, bucket, metav1.UpdateOptions{}); err != nil {
268+
klog.V(3).ErrorS(err, "Error updating bucket after removing finalizers",
269+
"bucket", bucket.ObjectMeta.Name)
270+
return err
259271
}
260272

261-
klog.V(3).InfoS("Update Bucket success",
262-
"name", bucket.ObjectMeta.Name,
263-
"ns", bucket.ObjectMeta.Namespace)
264273
return nil
265274
}
266275

sidecar/pkg/bucket/bucket_controller_test.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,3 +310,43 @@ func TestRecordEvents(t *testing.T) {
310310
func newEvent(eventType, reason, message string) string {
311311
return fmt.Sprintf("%s %s %s", eventType, reason, message)
312312
}
313+
314+
// TestAddDeletedBucket tests that the Add method does not call the driver
315+
func TestAddDeletedBucket(t *testing.T) {
316+
driver := "driver1"
317+
318+
mpc := struct{ fakespec.FakeProvisionerClient }{}
319+
mpc.FakeDriverDeleteBucket = func(
320+
_ context.Context,
321+
_ *cosi.DriverDeleteBucketRequest,
322+
_ ...grpc.CallOption,
323+
) (*cosi.DriverDeleteBucketResponse, error) {
324+
t.Fatalf("driver should NOT be called from Add when object has DeletionTimestamp")
325+
return nil, nil
326+
}
327+
328+
now := metav1.Now()
329+
b := v1alpha1.Bucket{
330+
ObjectMeta: metav1.ObjectMeta{
331+
Name: "testbucket",
332+
DeletionTimestamp: &now,
333+
ResourceVersion: "1",
334+
},
335+
Spec: v1alpha1.BucketSpec{
336+
DriverName: driver,
337+
BucketClassName: "ignored",
338+
},
339+
}
340+
341+
client := fakebucketclientset.NewSimpleClientset(&b)
342+
343+
bl := BucketListener{
344+
driverName: driver,
345+
provisionerClient: &mpc,
346+
}
347+
bl.InitializeBucketClient(client)
348+
349+
if err := bl.Add(context.TODO(), &b); err != nil {
350+
t.Fatalf("Add returned error for deleted bucket: %v", err)
351+
}
352+
}

sidecar/pkg/bucketaccess/bucketaccess_controller.go

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,12 @@ func NewBucketAccessListener(driverName string, client cosi.ProvisionerClient) *
6868
func (bal *BucketAccessListener) Add(ctx context.Context, inputBucketAccess *v1alpha1.BucketAccess) error {
6969
bucketAccess := inputBucketAccess.DeepCopy()
7070

71+
if !bucketAccess.GetDeletionTimestamp().IsZero() {
72+
klog.V(3).InfoS("BucketAccess has deletion timestamp, handling deletion",
73+
"name", bucketAccess.ObjectMeta.Name)
74+
return bal.deleteBucketAccessOp(ctx, bucketAccess)
75+
}
76+
7177
if bucketAccess.Status.AccessGranted && bucketAccess.Status.AccountID != "" {
7278
klog.V(3).InfoS("BucketAccess already exists", bucketAccess.ObjectMeta.Name)
7379
return nil
@@ -310,10 +316,13 @@ func (bal *BucketAccessListener) Update(ctx context.Context, old, new *v1alpha1.
310316

311317
bucketAccess := new.DeepCopy()
312318
if !bucketAccess.GetDeletionTimestamp().IsZero() {
313-
err := bal.deleteBucketAccessOp(ctx, bucketAccess)
314-
if err != nil {
319+
if err := bal.deleteBucketAccessOp(ctx, bucketAccess); err != nil {
315320
return bal.recordError(bucketAccess, v1.EventTypeWarning, v1alpha1.FailedRevokeAccess, err)
316321
}
322+
} else {
323+
if err := bal.Add(ctx, bucketAccess); err != nil {
324+
return bal.recordError(bucketAccess, v1.EventTypeWarning, v1alpha1.FailedGrantAccess, err)
325+
}
317326
}
318327

319328
klog.V(3).InfoS("Update BucketAccess success",

sidecar/pkg/bucketaccess/bucketaccess_controller_test.go

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -502,3 +502,94 @@ func TestRecordEvents(t *testing.T) {
502502
func newEvent(eventType, reason, message string) string {
503503
return fmt.Sprintf("%s %s %s", eventType, reason, message)
504504
}
505+
506+
// TestAddDeletedBucketAccess tests that a deleted BucketAccess does not
507+
// trigger a call to the driver to grant access, and that no secrets are created.
508+
func TestAddDeletedBucketAccess(t *testing.T) {
509+
driver := "driver"
510+
baName := "bucketaccess-deleted"
511+
ns := "testns"
512+
513+
mpc := struct{ fakespec.FakeProvisionerClient }{}
514+
mpc.FakeDriverGrantBucketAccess = func(
515+
_ context.Context,
516+
_ *cosi.DriverGrantBucketAccessRequest,
517+
_ ...grpc.CallOption,
518+
) (*cosi.DriverGrantBucketAccessResponse, error) {
519+
t.Fatalf("driver Grant should NOT be called on deleted BA")
520+
return nil, nil
521+
}
522+
mpc.FakeDriverRevokeBucketAccess = func(
523+
_ context.Context,
524+
_ *cosi.DriverRevokeBucketAccessRequest,
525+
_ ...grpc.CallOption,
526+
) (*cosi.DriverRevokeBucketAccessResponse, error) {
527+
return &cosi.DriverRevokeBucketAccessResponse{}, nil
528+
}
529+
530+
// minimal stub objects just to satisfy look-ups inside delete-path
531+
bac := &v1alpha1.BucketAccessClass{
532+
ObjectMeta: metav1.ObjectMeta{Name: "bac"},
533+
DriverName: driver,
534+
}
535+
claim := &v1alpha1.BucketClaim{
536+
ObjectMeta: metav1.ObjectMeta{Name: "claim", Namespace: ns},
537+
Status: v1alpha1.BucketClaimStatus{
538+
BucketReady: true,
539+
BucketName: "bucket",
540+
},
541+
}
542+
bucket := &v1alpha1.Bucket{
543+
ObjectMeta: metav1.ObjectMeta{Name: "bucket"},
544+
Status: v1alpha1.BucketStatus{
545+
BucketReady: true,
546+
BucketID: "id",
547+
},
548+
}
549+
550+
now := metav1.Now()
551+
ba := &v1alpha1.BucketAccess{
552+
ObjectMeta: metav1.ObjectMeta{
553+
Name: baName,
554+
Namespace: ns,
555+
DeletionTimestamp: &now,
556+
Finalizers: []string{consts.BAFinalizer},
557+
},
558+
Spec: v1alpha1.BucketAccessSpec{
559+
BucketClaimName: claim.Name,
560+
BucketAccessClassName: bac.Name,
561+
CredentialsSecretName: "creds",
562+
},
563+
Status: v1alpha1.BucketAccessStatus{
564+
AccountID: "acc",
565+
AccessGranted: true,
566+
},
567+
}
568+
569+
secret := &v1.Secret{
570+
ObjectMeta: metav1.ObjectMeta{
571+
Name: "creds",
572+
Namespace: ns,
573+
Finalizers: []string{consts.SecretFinalizer},
574+
},
575+
StringData: map[string]string{"dummy": "val"},
576+
}
577+
578+
client := fakebucketclientset.NewSimpleClientset(bac, claim, bucket, ba)
579+
kubeClient := fakekubeclientset.NewSimpleClientset(secret)
580+
581+
bal := BucketAccessListener{
582+
driverName: driver,
583+
provisionerClient: &mpc,
584+
bucketClient: client,
585+
kubeClient: kubeClient,
586+
}
587+
588+
if err := bal.Add(context.TODO(), ba); err != nil {
589+
t.Fatalf("Add returned error for deleted BucketAccess: %v", err)
590+
}
591+
592+
if _, err := bal.secrets(ns).Get(context.TODO(), "creds", metav1.GetOptions{}); !kubeerrors.IsNotFound(err) {
593+
t.Fatalf("secret was not cleaned up, err=%v", err)
594+
}
595+
}

0 commit comments

Comments
 (0)