From 9195ed366f72b4199f9de6575438e4fc34330748 Mon Sep 17 00:00:00 2001 From: Rob Rati Date: Tue, 8 Dec 2020 19:39:46 -0500 Subject: [PATCH] Added bucket controller --- pkg/controller/bucket/bucket_controller.go | 200 ++++++++ .../bucket/bucket_controller_test.go | 481 ++++++++++++++++++ 2 files changed, 681 insertions(+) create mode 100644 pkg/controller/bucket/bucket_controller.go create mode 100644 pkg/controller/bucket/bucket_controller_test.go diff --git a/pkg/controller/bucket/bucket_controller.go b/pkg/controller/bucket/bucket_controller.go new file mode 100644 index 0000000..943c4da --- /dev/null +++ b/pkg/controller/bucket/bucket_controller.go @@ -0,0 +1,200 @@ +/* +Copyright 2020 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package bucket + +import ( + "context" + "fmt" + "strings" + "time" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + utilversion "k8s.io/apimachinery/pkg/util/version" + + kubeclientset "k8s.io/client-go/kubernetes" + "k8s.io/client-go/util/retry" + "k8s.io/client-go/util/workqueue" + + "github.com/kubernetes-sigs/container-object-storage-interface-api/apis/objectstorage.k8s.io/v1alpha1" + bucketclientset "github.com/kubernetes-sigs/container-object-storage-interface-api/clientset" + "github.com/kubernetes-sigs/container-object-storage-interface-api/controller" + + osspec "github.com/kubernetes-sigs/container-object-storage-interface-spec" + + "k8s.io/klog" + + "golang.org/x/time/rate" +) + +// bucketListener manages Bucket objects +type bucketListener struct { + kubeClient kubeclientset.Interface + bucketClient bucketclientset.Interface + provisionerClient osspec.ProvisionerClient + + // The name of the provisioner for which this controller dynamically + // provisions buckets. + provisionerName string + kubeVersion *utilversion.Version +} + +// NewBucketController returns a controller that manages Bucket objects +func NewBucketController(provisionerName string, client osspec.ProvisionerClient) (*controller.ObjectStorageController, error) { + rateLimit := workqueue.NewMaxOfRateLimiter( + workqueue.NewItemExponentialFailureRateLimiter(5*time.Second, 60*time.Minute), + &workqueue.BucketRateLimiter{Limiter: rate.NewLimiter(rate.Limit(10), 100)}, + ) + + identity := fmt.Sprintf("object-storage-sidecar-%s", provisionerName) + bc, err := controller.NewObjectStorageController(identity, "bucket-controller", 5, rateLimit) + if err != nil { + return nil, err + } + + bl := bucketListener{ + provisionerName: provisionerName, + provisionerClient: client, + } + bc.AddBucketListener(&bl) + + return bc, nil +} + +// InitializeKubeClient initializes the kubernetes client +func (bl *bucketListener) InitializeKubeClient(k kubeclientset.Interface) { + bl.kubeClient = k + + serverVersion, err := k.Discovery().ServerVersion() + if err != nil { + klog.Errorf("unable to get server version: %v", err) + } else { + bl.kubeVersion = utilversion.MustParseSemantic(serverVersion.GitVersion) + } +} + +// InitializeBucketClient initializes the object storage bucket client +func (bl *bucketListener) InitializeBucketClient(bc bucketclientset.Interface) { + bl.bucketClient = bc +} + +// Add will call the provisioner and add a bucket +func (bl *bucketListener) Add(ctx context.Context, obj *v1alpha1.Bucket) error { + klog.V(1).Infof("bucketListener: add called for bucket %s", obj.Name) + + // Verify this bucket is for this provisioner + if !strings.EqualFold(obj.Spec.Provisioner, bl.provisionerName) { + return nil + } + + req := osspec.ProvisionerCreateBucketRequest{ + BucketName: obj.Name, + BucketContext: obj.Spec.Parameters, + } + + req.BucketContext["ProtocolVersion"] = obj.Spec.Protocol.Version + + if obj.Spec.AnonymousAccessMode.Private { + req.AnonymousBucketAccessMode = osspec.ProvisionerCreateBucketRequest_BUCKET_PRIVATE + } else if obj.Spec.AnonymousAccessMode.PublicReadOnly { + req.AnonymousBucketAccessMode = osspec.ProvisionerCreateBucketRequest_BUCKET_READ_ONLY + } else if obj.Spec.AnonymousAccessMode.PublicReadWrite { + req.AnonymousBucketAccessMode = osspec.ProvisionerCreateBucketRequest_BUCKET_WRITE_ONLY + } else if obj.Spec.AnonymousAccessMode.PublicWriteOnly { + req.AnonymousBucketAccessMode = osspec.ProvisionerCreateBucketRequest_BUCKET_READ_WRITE + } + + // TODO set grpc timeout + rsp, err := bl.provisionerClient.ProvisionerCreateBucket(ctx, &req) + if err != nil { + klog.Errorf("error calling ProvisionerCreateBucket: %v", err) + return err + } + klog.V(1).Infof("provisioner returned create bucket response %v", rsp) + + // TODO update the bucket protocol in the bucket spec + + // update bucket availability to true + return bl.updateStatus(ctx, obj.Name, "Bucket Provisioned", true) +} + +// Update does nothing +func (bl *bucketListener) Update(ctx context.Context, old, new *v1alpha1.Bucket) error { + klog.V(1).Infof("bucketListener: update called for bucket %s", old.Name) + return nil +} + +// Delete will call the provisioner and delete a bucket +func (bl *bucketListener) Delete(ctx context.Context, obj *v1alpha1.Bucket) error { + klog.V(1).Infof("bucketListener: delete called for bucket %s", obj.Name) + + // Verify this bucket is for this provisioner + if !strings.EqualFold(obj.Spec.Provisioner, bl.provisionerName) { + return nil + } + + req := osspec.ProvisionerDeleteBucketRequest{ + BucketContext: obj.Spec.Parameters, + } + + switch obj.Spec.Protocol.Name { + case v1alpha1.ProtocolNameS3: + req.BucketName = obj.Spec.Protocol.S3.BucketName + req.BucketContext["Region"] = obj.Spec.Protocol.S3.Region + req.BucketContext["SignatureVersion"] = string(obj.Spec.Protocol.S3.SignatureVersion) + req.BucketContext["Endpoint"] = obj.Spec.Protocol.S3.Endpoint + case v1alpha1.ProtocolNameAzure: + req.BucketName = obj.Spec.Protocol.AzureBlob.ContainerName + req.BucketContext["StorageAccount"] = obj.Spec.Protocol.AzureBlob.StorageAccount + case v1alpha1.ProtocolNameGCS: + req.BucketName = obj.Spec.Protocol.GCS.BucketName + req.BucketContext["ServiceAccount"] = obj.Spec.Protocol.GCS.ServiceAccount + req.BucketContext["PrivateKeyName"] = obj.Spec.Protocol.GCS.PrivateKeyName + req.BucketContext["ProjectID"] = obj.Spec.Protocol.GCS.ProjectID + default: + return fmt.Errorf("unknown protocol: %s", obj.Spec.Protocol.Name) + } + + req.BucketContext["ProtocolVersion"] = obj.Spec.Protocol.Version + + // TODO set grpc timeout + rsp, err := bl.provisionerClient.ProvisionerDeleteBucket(ctx, &req) + if err != nil { + klog.Errorf("error calling ProvisionerDeleteBucket: %v", err) + obj.Status.Message = "Bucket Deleting" + obj.Status.BucketAvailable = false + _, err = bl.bucketClient.ObjectstorageV1alpha1().Buckets().UpdateStatus(ctx, obj, metav1.UpdateOptions{}) + return err + } + klog.V(1).Infof("provisioner returned delete bucket response %v", rsp) + + // update bucket availability to false + return bl.updateStatus(ctx, obj.Name, "Bucket Deleted", false) +} + +func (bl *bucketListener) updateStatus(ctx context.Context, name, msg string, state bool) error { + err := retry.RetryOnConflict(retry.DefaultRetry, func() error { + bucket, err := bl.bucketClient.ObjectstorageV1alpha1().Buckets().Get(ctx, name, metav1.GetOptions{}) + if err != nil { + return err + } + bucket.Status.Message = msg + bucket.Status.BucketAvailable = state + _, err = bl.bucketClient.ObjectstorageV1alpha1().Buckets().UpdateStatus(ctx, bucket, metav1.UpdateOptions{}) + return err + }) + return err +} diff --git a/pkg/controller/bucket/bucket_controller_test.go b/pkg/controller/bucket/bucket_controller_test.go new file mode 100644 index 0000000..219ed1b --- /dev/null +++ b/pkg/controller/bucket/bucket_controller_test.go @@ -0,0 +1,481 @@ +/* +Copyright 2020 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package bucket + +import ( + "context" + "reflect" + "testing" + + "github.com/kubernetes-sigs/container-object-storage-interface-api/apis/objectstorage.k8s.io/v1alpha1" + + fakebucketclientset "github.com/kubernetes-sigs/container-object-storage-interface-api/clientset/fake" + + osspec "github.com/kubernetes-sigs/container-object-storage-interface-spec" + fakespec "github.com/kubernetes-sigs/container-object-storage-interface-spec/fake" + + corev1 "k8s.io/api/core/v1" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + utilversion "k8s.io/apimachinery/pkg/util/version" + "k8s.io/apimachinery/pkg/version" + + fakediscovery "k8s.io/client-go/discovery/fake" + fakekubeclientset "k8s.io/client-go/kubernetes/fake" + + "google.golang.org/grpc" +) + +func TestInitializeKubeClient(t *testing.T) { + client := fakekubeclientset.NewSimpleClientset() + fakeDiscovery, ok := client.Discovery().(*fakediscovery.FakeDiscovery) + if !ok { + t.Fatalf("couldn't convert Discovery() to *FakeDiscovery") + } + + fakeVersion := &version.Info{ + GitVersion: "v1.0.0", + } + fakeDiscovery.FakedServerVersion = fakeVersion + + bl := bucketListener{} + bl.InitializeKubeClient(client) + + if bl.kubeClient == nil { + t.Errorf("kubeClient was nil") + } + + expected := utilversion.MustParseSemantic(fakeVersion.GitVersion) + if !reflect.DeepEqual(expected, bl.kubeVersion) { + t.Errorf("expected %+v, but got %+v", expected, bl.kubeVersion) + } +} + +func TestInitializeBucketClient(t *testing.T) { + client := fakebucketclientset.NewSimpleClientset() + + bl := bucketListener{} + bl.InitializeBucketClient(client) + + if bl.bucketClient == nil { + t.Errorf("bucketClient was nil") + } +} + +func TestAddWrongProvisioner(t *testing.T) { + provisioner := "provisioner1" + mpc := struct{ fakespec.MockProvisionerClient }{} + mpc.CreateBucket = func(ctx context.Context, in *osspec.ProvisionerCreateBucketRequest, opts ...grpc.CallOption) (*osspec.ProvisionerCreateBucketResponse, error) { + t.Errorf("grpc client called") + return nil, nil + } + + bl := bucketListener{ + provisionerName: provisioner, + provisionerClient: &mpc, + } + + b := v1alpha1.Bucket{ + Spec: v1alpha1.BucketSpec{ + Provisioner: "provisioner2", + }, + } + ctx := context.TODO() + err := bl.Add(ctx, &b) + if err != nil { + t.Errorf("error returned: %+v", err) + } +} + +func TestAddValidProtocols(t *testing.T) { + provisioner := "provisioner1" + region := "region1" + bucketName := "bucket1" + protocolVersion := "proto1" + sigVersion := v1alpha1.S3SignatureVersionV2 + account := "account1" + keyName := "keyName1" + projID := "id1" + anonAccess := "BUCKET_PRIVATE" + mpc := struct{ fakespec.MockProvisionerClient }{} + + testCases := []struct { + name string + protocolName v1alpha1.ProtocolName + createFunc func(ctx context.Context, in *osspec.ProvisionerCreateBucketRequest, opts ...grpc.CallOption) (*osspec.ProvisionerCreateBucketResponse, error) + params map[string]string + }{ + { + name: "S3", + protocolName: v1alpha1.ProtocolNameS3, + createFunc: func(ctx context.Context, in *osspec.ProvisionerCreateBucketRequest, opts ...grpc.CallOption) (*osspec.ProvisionerCreateBucketResponse, error) { + if in.BucketName != bucketName { + t.Errorf("expected %s, got %s", bucketName, in.BucketName) + } + if in.BucketContext["Region"] != region { + t.Errorf("expected %s, got %s", region, in.BucketContext["Region"]) + } + if in.BucketContext["ProtocolVersion"] != protocolVersion { + t.Errorf("expected %s, got %s", protocolVersion, in.BucketContext["ProtocolVersion"]) + } + if in.BucketContext["SignatureVersion"] != string(sigVersion) { + t.Errorf("expected %s, got %s", sigVersion, in.BucketContext["SignatureVersion"]) + } + return &osspec.ProvisionerCreateBucketResponse{}, nil + }, + params: map[string]string{ + "Region": region, + "SignatureVersion": string(sigVersion), + }, + }, + { + name: "GCS", + protocolName: v1alpha1.ProtocolNameGCS, + createFunc: func(ctx context.Context, in *osspec.ProvisionerCreateBucketRequest, opts ...grpc.CallOption) (*osspec.ProvisionerCreateBucketResponse, error) { + if in.BucketName != bucketName { + t.Errorf("expected %s, got %s", bucketName, in.BucketName) + } + if in.BucketContext["ServiceAccount"] != account { + t.Errorf("expected %s, got %s", account, in.BucketContext["ServiceAccount"]) + } + if in.BucketContext["PrivateKeyName"] != keyName { + t.Errorf("expected %s, got %s", keyName, in.BucketContext["PrivateKeyName"]) + } + if in.BucketContext["ProjectID"] != projID { + t.Errorf("expected %s, got %s", projID, in.BucketContext["ProjectID"]) + } + if in.BucketContext["ProtocolVersion"] != protocolVersion { + t.Errorf("expected %s, got %s", protocolVersion, in.BucketContext["ProtocolVersion"]) + } + return &osspec.ProvisionerCreateBucketResponse{}, nil + }, + params: map[string]string{ + "ServiceAccount": account, + "PrivateKeyName": keyName, + "ProjectID": projID, + }, + }, + { + name: "AzureBlob", + protocolName: v1alpha1.ProtocolNameAzure, + createFunc: func(ctx context.Context, in *osspec.ProvisionerCreateBucketRequest, opts ...grpc.CallOption) (*osspec.ProvisionerCreateBucketResponse, error) { + if in.BucketName != bucketName { + t.Errorf("expected %s, got %s", bucketName, in.BucketName) + } + if in.BucketContext["StorageAccount"] != account { + t.Errorf("expected %s, got %s", account, in.BucketContext["StorageAccount"]) + } + if in.BucketContext["ProtocolVersion"] != protocolVersion { + t.Errorf("expected %s, got %s", protocolVersion, in.BucketContext["ProtocolVersion"]) + } + return &osspec.ProvisionerCreateBucketResponse{}, nil + }, + params: map[string]string{ + "StorageAccount": account, + }, + }, + { + name: "AnonymousAccessMode", + protocolName: v1alpha1.ProtocolNameAzure, + createFunc: func(ctx context.Context, in *osspec.ProvisionerCreateBucketRequest, opts ...grpc.CallOption) (*osspec.ProvisionerCreateBucketResponse, error) { + if in.BucketName != bucketName { + t.Errorf("expected %s, got %s", bucketName, in.BucketName) + } + if in.BucketContext["StorageAccount"] != account { + t.Errorf("expected %s, got %s", account, in.BucketContext["StorageAccount"]) + } + aMode := osspec.ProvisionerCreateBucketRequest_AnonymousBucketAccessMode(osspec.ProvisionerCreateBucketRequest_AnonymousBucketAccessMode_value[anonAccess]) + if in.AnonymousBucketAccessMode != aMode { + t.Errorf("expected %s, got %s", aMode, in.AnonymousBucketAccessMode) + } + if in.BucketContext["ProtocolVersion"] != protocolVersion { + t.Errorf("expected %s, got %s", protocolVersion, in.BucketContext["ProtocolVersion"]) + } + return &osspec.ProvisionerCreateBucketResponse{}, nil + }, + params: map[string]string{ + "StorageAccount": account, + "AnonymousAccessMode": anonAccess, + }, + }, + } + + for _, tc := range testCases { + b := v1alpha1.Bucket{ + ObjectMeta: metav1.ObjectMeta{ + Name: bucketName, + }, + Spec: v1alpha1.BucketSpec{ + Provisioner: provisioner, + Protocol: v1alpha1.Protocol{ + RequestedProtocol: v1alpha1.RequestedProtocol{ + Name: tc.protocolName, + Version: protocolVersion, + }, + }, + Parameters: tc.params, + }, + } + + ctx := context.TODO() + client := fakebucketclientset.NewSimpleClientset(&b) + kubeClient := fakekubeclientset.NewSimpleClientset() + mpc.CreateBucket = tc.createFunc + bl := bucketListener{ + provisionerName: provisioner, + provisionerClient: &mpc, + bucketClient: client, + kubeClient: kubeClient, + } + + t.Logf("Testing protocol %s", tc.name) + err := bl.Add(ctx, &b) + if err != nil { + t.Errorf("add returned: %+v", err) + } + + updatedB, _ := client.ObjectstorageV1alpha1().Buckets().Get(ctx, b.Name, metav1.GetOptions{}) + if updatedB.Status.BucketAvailable != true { + t.Errorf("expected %t, got %t", true, b.Status.BucketAvailable) + } + } +} + +func TestDeleteWrongProvisioner(t *testing.T) { + provisioner := "provisioner1" + mpc := struct{ fakespec.MockProvisionerClient }{} + mpc.DeleteBucket = func(ctx context.Context, in *osspec.ProvisionerDeleteBucketRequest, opts ...grpc.CallOption) (*osspec.ProvisionerDeleteBucketResponse, error) { + t.Errorf("grpc client called") + return nil, nil + } + + bl := bucketListener{ + provisionerName: provisioner, + provisionerClient: &mpc, + } + + b := v1alpha1.Bucket{ + Spec: v1alpha1.BucketSpec{ + Provisioner: "provisioner2", + }, + } + ctx := context.TODO() + err := bl.Delete(ctx, &b) + if err != nil { + t.Errorf("error returned: %+v", err) + } +} + +func TestDeleteValidProtocols(t *testing.T) { + provisioner := "provisioner1" + region := "region1" + bucketName := "bucket1" + protocolVersion := "proto1" + sigVersion := v1alpha1.S3SignatureVersion(v1alpha1.S3SignatureVersionV2) + account := "account1" + keyName := "keyName1" + projID := "id1" + endpoint := "endpoint1" + mpc := struct{ fakespec.MockProvisionerClient }{} + extraParamName := "ParamName" + extraParamValue := "ParamValue" + + testCases := []struct { + name string + setProtocol func(b *v1alpha1.Bucket) + protocolName v1alpha1.ProtocolName + deleteFunc func(ctx context.Context, in *osspec.ProvisionerDeleteBucketRequest, opts ...grpc.CallOption) (*osspec.ProvisionerDeleteBucketResponse, error) + params map[string]string + }{ + { + name: "S3", + setProtocol: func(b *v1alpha1.Bucket) { + b.Spec.Protocol.S3 = &v1alpha1.S3Protocol{ + Region: region, + Version: protocolVersion, + SignatureVersion: sigVersion, + BucketName: bucketName, + Endpoint: endpoint, + } + }, + protocolName: v1alpha1.ProtocolNameS3, + deleteFunc: func(ctx context.Context, in *osspec.ProvisionerDeleteBucketRequest, opts ...grpc.CallOption) (*osspec.ProvisionerDeleteBucketResponse, error) { + if in.BucketName != bucketName { + t.Errorf("expected %s, got %s", bucketName, in.BucketName) + } + if in.BucketContext["Region"] != region { + t.Errorf("expected %s, got %s", region, in.BucketContext["Region"]) + } + if in.BucketContext["ProtocolVersion"] != protocolVersion { + t.Errorf("expected %s, got %s", protocolVersion, in.BucketContext["ProtocolVersion"]) + } + if in.BucketContext["SignatureVersion"] != string(sigVersion) { + t.Errorf("expected %s, got %s", sigVersion, in.BucketContext["SignatureVersion"]) + } + if in.BucketContext["Endpoint"] != endpoint { + t.Errorf("expected %s, got %s", endpoint, in.BucketContext["Endpoint"]) + } + if in.BucketContext[extraParamName] != extraParamValue { + t.Errorf("expected %s, got %s", extraParamValue, in.BucketContext[extraParamName]) + } + if in.BucketContext["ProtocolVersion"] != protocolVersion { + t.Errorf("expected %s, got %s", protocolVersion, in.BucketContext["ProtocolVersion"]) + } + return &osspec.ProvisionerDeleteBucketResponse{}, nil + }, + params: map[string]string{ + extraParamName: extraParamValue, + }, + }, + { + name: "GCS", + setProtocol: func(b *v1alpha1.Bucket) { + b.Spec.Protocol.GCS = &v1alpha1.GCSProtocol{ + ServiceAccount: account, + PrivateKeyName: keyName, + ProjectID: projID, + BucketName: bucketName, + } + }, + protocolName: v1alpha1.ProtocolNameGCS, + deleteFunc: func(ctx context.Context, in *osspec.ProvisionerDeleteBucketRequest, opts ...grpc.CallOption) (*osspec.ProvisionerDeleteBucketResponse, error) { + if in.BucketName != bucketName { + t.Errorf("expected %s, got %s", bucketName, in.BucketName) + } + if in.BucketContext["ServiceAccount"] != account { + t.Errorf("expected %s, got %s", region, in.BucketContext["ServiceAccount"]) + } + if in.BucketContext["PrivateKeyName"] != keyName { + t.Errorf("expected %s, got %s", region, in.BucketContext["PrivateKeyName"]) + } + if in.BucketContext["ProjectID"] != projID { + t.Errorf("expected %s, got %s", region, in.BucketContext["ProjectID"]) + } + if in.BucketContext[extraParamName] != extraParamValue { + t.Errorf("expected %s, got %s", extraParamValue, in.BucketContext[extraParamName]) + } + if in.BucketContext["ProtocolVersion"] != protocolVersion { + t.Errorf("expected %s, got %s", protocolVersion, in.BucketContext["ProtocolVersion"]) + } + return &osspec.ProvisionerDeleteBucketResponse{}, nil + }, + params: map[string]string{ + extraParamName: extraParamValue, + }, + }, + { + name: "AzureBlob", + setProtocol: func(b *v1alpha1.Bucket) { + b.Spec.Protocol.AzureBlob = &v1alpha1.AzureProtocol{ + StorageAccount: account, + ContainerName: bucketName, + } + }, + protocolName: v1alpha1.ProtocolNameAzure, + deleteFunc: func(ctx context.Context, in *osspec.ProvisionerDeleteBucketRequest, opts ...grpc.CallOption) (*osspec.ProvisionerDeleteBucketResponse, error) { + if in.BucketName != bucketName { + t.Errorf("expected %s, got %s", bucketName, in.BucketName) + } + if in.BucketContext["StorageAccount"] != account { + t.Errorf("expected %s, got %s", region, in.BucketContext["StorageAccount"]) + } + if in.BucketContext[extraParamName] != extraParamValue { + t.Errorf("expected %s, got %s", extraParamValue, in.BucketContext[extraParamName]) + } + if in.BucketContext["ProtocolVersion"] != protocolVersion { + t.Errorf("expected %s, got %s", protocolVersion, in.BucketContext["ProtocolVersion"]) + } + return &osspec.ProvisionerDeleteBucketResponse{}, nil + }, + params: map[string]string{ + extraParamName: extraParamValue, + }, + }, + } + + for _, tc := range testCases { + b := v1alpha1.Bucket{ + Spec: v1alpha1.BucketSpec{ + Provisioner: provisioner, + Protocol: v1alpha1.Protocol{ + RequestedProtocol: v1alpha1.RequestedProtocol{ + Name: tc.protocolName, + Version: protocolVersion, + }, + }, + Parameters: tc.params, + }, + Status: v1alpha1.BucketStatus{ + BucketAvailable: true, + }, + } + + ctx := context.TODO() + client := fakebucketclientset.NewSimpleClientset(&b) + mpc.DeleteBucket = tc.deleteFunc + bl := bucketListener{ + provisionerName: provisioner, + provisionerClient: &mpc, + bucketClient: client, + } + + tc.setProtocol(&b) + t.Logf("Testing protocol %s", tc.name) + err := bl.Delete(ctx, &b) + if err != nil { + t.Errorf("delete returned: %+v", err) + } + + updatedB, _ := client.ObjectstorageV1alpha1().Buckets().Get(ctx, b.Name, metav1.GetOptions{}) + if updatedB.Status.BucketAvailable != false { + t.Errorf("expected %t, got %t", false, b.Status.BucketAvailable) + } + } +} + +func TestDeleteInvalidProtocol(t *testing.T) { + const ( + protocolName v1alpha1.ProtocolName = "invalid" + ) + + bucketName := "bucket1" + provisioner := "provisioner1" + + bl := bucketListener{ + provisionerName: provisioner, + } + + b := v1alpha1.Bucket{ + Spec: v1alpha1.BucketSpec{ + BucketRequest: &corev1.ObjectReference{ + Name: bucketName, + }, + Provisioner: provisioner, + Protocol: v1alpha1.Protocol{ + RequestedProtocol: v1alpha1.RequestedProtocol{ + Name: protocolName, + }, + }, + }, + } + + ctx := context.TODO() + err := bl.Delete(ctx, &b) + if err == nil { + t.Errorf("invalidProtocol: no error returned") + } +}