diff --git a/pkg/clients/s3/s3_client_test.go b/pkg/clients/s3/s3_client_test.go index 66e3fa8b..68e2301c 100644 --- a/pkg/clients/s3/s3_client_test.go +++ b/pkg/clients/s3/s3_client_test.go @@ -128,4 +128,31 @@ var _ = Describe("S3Client", func() { Expect(err).NotTo(BeNil()) }) }) + + Describe("DeleteBucket", func() { + var mockS3 *mock.MockS3Client + var client *s3client.S3Client + + BeforeEach(func() { + mockS3 = &mock.MockS3Client{} + client = &s3client.S3Client{ + S3Service: mockS3, + } + }) + + It("should successfully delete a bucket", func(ctx SpecContext) { + err := client.DeleteBucket(ctx, "test-bucket") + Expect(err).To(BeNil()) + }) + + It("should handle errors when deleting a bucket", func(ctx SpecContext) { + mockS3.DeleteBucketFunc = func(ctx context.Context, input *s3.DeleteBucketInput, opts ...func(*s3.Options)) (*s3.DeleteBucketOutput, error) { + return nil, fmt.Errorf("mock delete bucket error") + } + + err := client.DeleteBucket(ctx, "test-bucket") + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("mock delete bucket error")) + }) + }) }) diff --git a/pkg/driver/provisioner_server_impl_test.go b/pkg/driver/provisioner_server_impl_test.go index dd21e93e..a50646e6 100644 --- a/pkg/driver/provisioner_server_impl_test.go +++ b/pkg/driver/provisioner_server_impl_test.go @@ -248,32 +248,6 @@ var _ = Describe("ProvisionerServer DriverCreateBucket", Ordered, func() { }) }) -var _ = Describe("ProvisionerServer Unimplemented Methods", Ordered, func() { - var ( - provisioner *driver.ProvisionerServer - clientset *fake.Clientset - bucketName string - ) - - BeforeEach(func() { - clientset = fake.NewSimpleClientset() - provisioner = &driver.ProvisionerServer{ - Provisioner: "test-provisioner", - Clientset: clientset, - } - bucketName = "test-bucket" - }) - - It("DriverDeleteBucket should return Unimplemented error", func(ctx SpecContext) { - request := &cosiapi.DriverDeleteBucketRequest{BucketId: bucketName} - resp, err := provisioner.DriverDeleteBucket(ctx, request) - Expect(resp).To(BeNil()) - Expect(err).To(HaveOccurred()) - Expect(status.Code(err)).To(Equal(codes.Unimplemented)) - Expect(err.Error()).To(ContainSubstring("DriverCreateBucket: not implemented")) - }) -}) - var _ = Describe("FetchSecretInformation", Ordered, func() { var ( parameters map[string]string @@ -808,3 +782,129 @@ var _ = Describe("ProvisionerServer DriverRevokeBucketAccess", Ordered, func() { Expect(err.Error()).To(ContainSubstring("unsupported client type for IAM operations")) }) }) + +var _ = Describe("ProvisionerServer DriverDeleteBucket", Ordered, func() { + var ( + mockS3Client *mock.MockS3Client + provisioner *driver.ProvisionerServer + clientset *fake.Clientset + bucketName string + request *cosiapi.DriverDeleteBucketRequest + originalInitializeClient func(ctx context.Context, clientset kubernetes.Interface, parameters map[string]string, service string) (interface{}, *util.StorageClientParameters, error) + bucketClientset *bucketclientfake.Clientset + secretName, namespace string + s3Params util.StorageClientParameters + ) + + BeforeEach(func() { + mockS3Client = &mock.MockS3Client{} + clientset = fake.NewSimpleClientset() + bucketClientset = bucketclientfake.NewSimpleClientset() + + provisioner = &driver.ProvisionerServer{ + Provisioner: "test-provisioner", + Clientset: clientset, + BucketClientset: bucketClientset, + } + + s3Params = util.StorageClientParameters{ + AccessKeyID: "test-access-key", + SecretAccessKey: "test-secret-key", + Endpoint: "https://test-endpoint", + Region: "us-west-2", + } + secretName = "my-storage-secret" + namespace = "test-namespace" + + // Create a fake Bucket object with appropriate parameters + _, err := bucketClientset.ObjectstorageV1alpha1().Buckets().Create(context.TODO(), &bucketv1alpha1.Bucket{ + ObjectMeta: metav1.ObjectMeta{ + Name: bucketName, + }, + Spec: bucketv1alpha1.BucketSpec{ + Parameters: map[string]string{ + "objectStorageSecretName": secretName, + "objectStorageSecretNamespace": namespace, + }, + }, + }, metav1.CreateOptions{}) + Expect(err).To(BeNil()) + + request = &cosiapi.DriverDeleteBucketRequest{BucketId: bucketName} + originalInitializeClient = driver.InitializeClient + driver.InitializeClient = func(ctx context.Context, clientset kubernetes.Interface, parameters map[string]string, service string) (interface{}, *util.StorageClientParameters, error) { + // Validate parameters + Expect(parameters["objectStorageSecretName"]).To(Equal(secretName)) + Expect(parameters["objectStorageSecretNamespace"]).To(Equal(namespace)) + + if service == "S3" { + return &s3client.S3Client{S3Service: mockS3Client}, &s3Params, nil + } + return nil, nil, errors.New("unsupported service") + } + }) + + AfterEach(func() { + mockS3Client = nil + bucketClientset = nil + clientset = nil + provisioner = nil + driver.InitializeClient = originalInitializeClient + }) + + It("should successfully delete bucket when bucket exists and parameters are valid", func(ctx SpecContext) { + resp, err := provisioner.DriverDeleteBucket(ctx, request) + + Expect(err).To(BeNil()) + Expect(resp).NotTo(BeNil()) + Expect(resp).To(BeAssignableToTypeOf(&cosiapi.DriverDeleteBucketResponse{})) + }) + + It("should return error if the bucket does not exist", func(ctx SpecContext) { + err := bucketClientset.ObjectstorageV1alpha1().Buckets().Delete(context.TODO(), bucketName, metav1.DeleteOptions{}) + Expect(err).To(BeNil()) + + resp, err := provisioner.DriverDeleteBucket(ctx, request) + + Expect(resp).To(BeNil()) + Expect(err).To(HaveOccurred()) + Expect(status.Code(err)).To(Equal(codes.Internal)) + Expect(err.Error()).To(ContainSubstring("failed to get bucket object from kubernetes")) + }) + + It("should return error if S3 client initialization fails", func(ctx SpecContext) { + driver.InitializeClient = func(ctx context.Context, clientset kubernetes.Interface, parameters map[string]string, service string) (interface{}, *util.StorageClientParameters, error) { + return nil, nil, fmt.Errorf("mock S3 client initialization error") + } + + resp, err := provisioner.DriverDeleteBucket(ctx, request) + Expect(resp).To(BeNil()) + Expect(err).To(HaveOccurred()) + Expect(status.Code(err)).To(Equal(codes.Internal)) + Expect(err.Error()).To(ContainSubstring("failed to initialize object storage provider S3 client")) + }) + + It("should return InvalidArgument error for unsupported client type", func(ctx SpecContext) { + driver.InitializeClient = func(ctx context.Context, clientset kubernetes.Interface, parameters map[string]string, service string) (interface{}, *util.StorageClientParameters, error) { + return &struct{}{}, &s3Params, nil + } + + resp, err := provisioner.DriverDeleteBucket(ctx, request) + Expect(resp).To(BeNil()) + Expect(err).To(HaveOccurred()) + Expect(status.Code(err)).To(Equal(codes.InvalidArgument)) + Expect(err.Error()).To(ContainSubstring("unsupported client type for bucket deletion")) + }) + + It("should return error if unable to delete bucket", func(ctx SpecContext) { + mockS3Client.DeleteBucketFunc = func(ctx context.Context, input *s3.DeleteBucketInput, opts ...func(*s3.Options)) (*s3.DeleteBucketOutput, error) { + return nil, fmt.Errorf("mock failure: unable to delete bucket") + } + + resp, err := provisioner.DriverDeleteBucket(ctx, request) + Expect(resp).To(BeNil()) + Expect(err).To(HaveOccurred()) + Expect(status.Code(err)).To(Equal(codes.Internal)) + Expect(err.Error()).To(ContainSubstring("failed to delete bucket")) + }) +}) diff --git a/pkg/mock/mock_s3.go b/pkg/mock/mock_s3.go index d14940a3..13247372 100644 --- a/pkg/mock/mock_s3.go +++ b/pkg/mock/mock_s3.go @@ -9,6 +9,7 @@ import ( // MockS3Client simulates the behavior of an S3 client for testing. type MockS3Client struct { CreateBucketFunc func(ctx context.Context, input *s3.CreateBucketInput, opts ...func(*s3.Options)) (*s3.CreateBucketOutput, error) + DeleteBucketFunc func(ctx context.Context, input *s3.DeleteBucketInput, opts ...func(*s3.Options)) (*s3.DeleteBucketOutput, error) } // CreateBucket executes the mock CreateBucketFunc if defined, otherwise returns a default response. @@ -18,3 +19,11 @@ func (m *MockS3Client) CreateBucket(ctx context.Context, input *s3.CreateBucketI } return &s3.CreateBucketOutput{}, nil } + +// DeleteBucket executes the mock DeleteBucketFunc if defined, otherwise returns a default response. +func (m *MockS3Client) DeleteBucket(ctx context.Context, input *s3.DeleteBucketInput, opts ...func(*s3.Options)) (*s3.DeleteBucketOutput, error) { + if m.DeleteBucketFunc != nil { + return m.DeleteBucketFunc(ctx, input, opts...) + } + return &s3.DeleteBucketOutput{}, nil +}