diff --git a/cmd/controller-manager/controller-manager.go b/cmd/controller-manager/controller-manager.go index 533d898..750e833 100644 --- a/cmd/controller-manager/controller-manager.go +++ b/cmd/controller-manager/controller-manager.go @@ -11,6 +11,7 @@ import ( "github.com/spf13/viper" bucketcontroller "github.com/kubernetes-sigs/container-object-storage-interface-api/controller" + "github.com/kubernetes-sigs/container-object-storage-interface-controller/pkg/bucketaccessrequest" "github.com/kubernetes-sigs/container-object-storage-interface-controller/pkg/bucketrequest" "github.com/golang/glog" @@ -59,13 +60,9 @@ func init() { } func main() { - if err := cmd.Execute(); err != nil { - glog.Fatal(err.Error()) - } - - var cancel context.CancelFunc + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() // Just in case - _, cancel = context.WithCancel(cmd.Context()) sigs := make(chan os.Signal, 1) signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) @@ -73,6 +70,10 @@ func main() { <-sigs cancel() }() + + if err := cmd.ExecuteContext(ctx); err != nil { + glog.Fatal(err.Error()) + } } func run(ctx context.Context, args []string) error { @@ -81,5 +82,6 @@ func run(ctx context.Context, args []string) error { return err } ctrl.AddBucketRequestListener(bucketrequest.NewListener()) + ctrl.AddBucketAccessRequestListener(bucketaccessrequest.NewListener()) return ctrl.Run(ctx) } diff --git a/go.mod b/go.mod index 19ac658..f195933 100644 --- a/go.mod +++ b/go.mod @@ -4,10 +4,11 @@ go 1.15 require ( github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b - github.com/kubernetes-sigs/container-object-storage-interface-api v0.0.0-20201204201926-43539346a903 + github.com/kubernetes-sigs/container-object-storage-interface-api v0.0.0-20201210173615-0c3244fa34b9 github.com/spf13/cobra v1.1.1 github.com/spf13/viper v1.7.1 golang.org/x/time v0.0.0-20191024005414-555d28b269f0 + k8s.io/api v0.19.4 k8s.io/apiextensions-apiserver v0.19.4 k8s.io/apimachinery v0.19.4 k8s.io/client-go v0.19.4 diff --git a/go.sum b/go.sum index e2f7664..6d6c49c 100644 --- a/go.sum +++ b/go.sum @@ -257,6 +257,7 @@ github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= +github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.5 h1:JboBksRwiiAJWvIYJVo46AfV+IAIKZpfrSzVKj42R4Q= @@ -289,8 +290,8 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kubernetes-sigs/container-object-storage-interface-api v0.0.0-20201204201926-43539346a903 h1:kBd9bCHv429J7Y8Mp2w1Xg3QtDiRAhittzYC/45/G2E= -github.com/kubernetes-sigs/container-object-storage-interface-api v0.0.0-20201204201926-43539346a903/go.mod h1:C7tjzC+nLe7H7+3UM/Z6a7F24yxOO8FSK3ZaVZrKDPQ= +github.com/kubernetes-sigs/container-object-storage-interface-api v0.0.0-20201210173615-0c3244fa34b9 h1:1Zmlu1GKd1iIv9fdc1mkaTuUtoNqet4NLYLE/8p9ylw= +github.com/kubernetes-sigs/container-object-storage-interface-api v0.0.0-20201210173615-0c3244fa34b9/go.mod h1:C7tjzC+nLe7H7+3UM/Z6a7F24yxOO8FSK3ZaVZrKDPQ= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= @@ -336,6 +337,7 @@ github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.11.0 h1:JAKSXpt1YjtLA7YpPiqO9ss6sNXEsPfSGdwN0UHqzrw= github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.14.2 h1:8mVmC9kjFFmA8H4pKMUhcblgifdkOIXPvbhN1T36q1M= @@ -343,6 +345,7 @@ github.com/onsi/ginkgo v1.14.2/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9k github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.8.1 h1:C5Dqfs/LeauYDX0jJXIe2SWmwCbGzx9yF8C8xy3Lh34= github.com/onsi/gomega v1.8.1/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= @@ -486,7 +489,6 @@ golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCc golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.2.0 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= @@ -661,6 +663,7 @@ gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogR gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= @@ -693,7 +696,6 @@ k8s.io/api v0.18.6/go.mod h1:eeyxr+cwCjMdLAmr2W3RyDI0VvTawSg/3RFFBEnmZGI= k8s.io/api v0.19.4 h1:I+1I4cgJYuCDgiLNjKx7SLmIbwgj9w7N7Zr5vSIdwpo= k8s.io/api v0.19.4/go.mod h1:SbtJ2aHCItirzdJ36YslycFNzWADYH3tgOhvBEFtZAk= k8s.io/apiextensions-apiserver v0.18.2/go.mod h1:q3faSnRGmYimiocj6cHQ1I3WpLqmDgJFlKL37fC4ZvY= -k8s.io/apiextensions-apiserver v0.18.6 h1:vDlk7cyFsDyfwn2rNAO2DbmUbvXy5yT5GE3rrqOzaMo= k8s.io/apiextensions-apiserver v0.18.6/go.mod h1:lv89S7fUysXjLZO7ke783xOwVTm6lKizADfvUM/SS/M= k8s.io/apiextensions-apiserver v0.19.4 h1:D9ak9T012tb3vcGFWYmbQuj9SCC8YM4zhA4XZqsAQC4= k8s.io/apiextensions-apiserver v0.19.4/go.mod h1:B9rpH/nu4JBCtuUp3zTTk8DEjZUupZTBEec7/2zNRYw= @@ -727,6 +729,7 @@ k8s.io/klog/v2 v2.2.0 h1:XRvcwJozkgZ1UQJmfMGpvRthQHOvihEhYtDfAaxMz/A= k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= k8s.io/kube-openapi v0.0.0-20200121204235-bf4fb3bd569c/go.mod h1:GRQhZsXIAJ1xR0C9bd8UpWHZ5plfAS9fzPjJuQ6JL3E= k8s.io/kube-openapi v0.0.0-20200410145947-61e04a5be9a6/go.mod h1:GRQhZsXIAJ1xR0C9bd8UpWHZ5plfAS9fzPjJuQ6JL3E= +k8s.io/kube-openapi v0.0.0-20200805222855-6aeccd4b50c6 h1:+WnxoVtG8TMiudHBSEtrVL1egv36TkkJm+bA8AxicmQ= k8s.io/kube-openapi v0.0.0-20200805222855-6aeccd4b50c6/go.mod h1:UuqjUnNftUyPE5H64/qeyjQoUZhGpeFDVdxjTeEVN2o= k8s.io/kube-openapi v0.0.0-20200923155610-8b5066479488 h1:mNpvQf4lkIHNOXCoM+Veu/UXwA56Yx1J7hY1Tvcs/oM= k8s.io/kube-openapi v0.0.0-20200923155610-8b5066479488/go.mod h1:UuqjUnNftUyPE5H64/qeyjQoUZhGpeFDVdxjTeEVN2o= diff --git a/pkg/bucketaccessrequest/bucketaccessrequest.go b/pkg/bucketaccessrequest/bucketaccessrequest.go new file mode 100644 index 0000000..48cebc9 --- /dev/null +++ b/pkg/bucketaccessrequest/bucketaccessrequest.go @@ -0,0 +1,162 @@ +package bucketaccessrequest + +import ( + "context" + + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "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" + bucketcontroller "github.com/kubernetes-sigs/container-object-storage-interface-api/controller" + "github.com/kubernetes-sigs/container-object-storage-interface-controller/pkg/util" + kubeclientset "k8s.io/client-go/kubernetes" + "k8s.io/client-go/util/retry" + + "github.com/golang/glog" +) + +type bucketAccessRequestListener struct { + kubeClient kubeclientset.Interface + bucketClient bucketclientset.Interface +} + +func NewListener() bucketcontroller.BucketAccessRequestListener { + return &bucketAccessRequestListener{} +} + +func (b *bucketAccessRequestListener) InitializeKubeClient(k kubeclientset.Interface) { + b.kubeClient = k +} + +func (b *bucketAccessRequestListener) InitializeBucketClient(bc bucketclientset.Interface) { + b.bucketClient = bc +} + +func (b *bucketAccessRequestListener) Add(ctx context.Context, obj *v1alpha1.BucketAccessRequest) error { + glog.V(1).Infof("Add called for BucketAccessRequest %s", obj.Name) + bucketAccessRequest := obj + + err := b.provisionBucketAccess(ctx, bucketAccessRequest) + if err != nil { + // Provisioning is 100% finished / not in progress. + switch err { + case util.ErrInvalidBucketAccessClass: + glog.V(1).Infof("BucketAccessClass specified does not exist while processing BucketAccessRequest %v.", bucketAccessRequest.Name) + err = nil + case util.ErrBucketAccessAlreadyExists: + glog.V(1).Infof("BucketAccess already exist for this BucketAccessRequest %v.", bucketAccessRequest.Name) + err = nil + default: + glog.V(1).Infof("Error occurred processing BucketAccessRequest %v: %v", bucketAccessRequest.Name, err) + } + return err + } + + glog.V(1).Infof("BucketAccessRequest %v is successfully processed.", bucketAccessRequest.Name) + return nil +} + +func (b *bucketAccessRequestListener) Update(ctx context.Context, old, new *v1alpha1.BucketAccessRequest) error { + glog.V(1).Infof("Update called for BucketAccessRequest %v", old.Name) + return nil +} + +func (b *bucketAccessRequestListener) Delete(ctx context.Context, obj *v1alpha1.BucketAccessRequest) error { + glog.V(1).Infof("Delete called for BucketAccessRequest %v", obj.Name) + return nil +} + +// provisionBucketAccess attempts to provision a BucketAccess for the given bucketAccessRequest. +// Returns nil error only when the bucketaccess was provisioned. An error is return if we cannot create bucket access. +// A normal error is returned when bucket acess was not provisioned and provisioning should be retried (requeue the bucketAccessRequest), +// or a special error errBucketAccessAlreadyExists, errInvalidBucketAccessClass is returned when provisioning was impossible and +// no further attempts to provision should be tried. +func (b *bucketAccessRequestListener) provisionBucketAccess(ctx context.Context, bucketAccessRequest *v1alpha1.BucketAccessRequest) error { + bucketAccessClassName := bucketAccessRequest.Spec.BucketAccessClassName + + bucketaccess := b.FindBucketAccess(ctx, bucketAccessRequest) + if bucketaccess != nil { + // bucketaccess has provisioned, nothing to do. + return util.ErrBucketAccessAlreadyExists + } + + bucketAccessClass, err := b.bucketClient.ObjectstorageV1alpha1().BucketAccessClasses().Get(ctx, bucketAccessClassName, metav1.GetOptions{}) + if bucketAccessClass == nil { + // bucket access class is invalid or not specified, cannot continue with provisioning. + return util.ErrInvalidBucketAccessClass + } + + bucketRequest, err := b.bucketClient.ObjectstorageV1alpha1().BucketRequests(bucketAccessRequest.Namespace).Get(ctx, bucketAccessRequest.Spec.BucketRequestName, metav1.GetOptions{}) + if bucketRequest == nil { + // bucket request does not exist, we have to reject this provision. + return util.ErrInvalidBucketRequest + } + if err != nil { + return err + } + + if bucketRequest.Spec.BucketInstanceName == "" { + return util.ErrWaitForBucketProvisioning + } + + sa, err := b.kubeClient.CoreV1().ServiceAccounts(bucketAccessRequest.Namespace).Get(ctx, bucketAccessRequest.Spec.ServiceAccountName, metav1.GetOptions{}) + if err != nil { + return err + } + + bucketaccess = &v1alpha1.BucketAccess{} + bucketaccess.Name = util.GetUUID() + + bucketaccess.Spec.BucketInstanceName = bucketRequest.Spec.BucketInstanceName + bucketaccess.Spec.BucketAccessRequest = &v1.ObjectReference{ + Name: bucketAccessRequest.Name, + Namespace: bucketAccessRequest.Namespace, + UID: bucketAccessRequest.ObjectMeta.UID} + bucketaccess.Spec.ServiceAccount = &v1.ObjectReference{ + Name: sa.Name, + Namespace: sa.Namespace, + UID: sa.ObjectMeta.UID} + // bucketaccess.Spec.MintedSecretName - set by the driver + bucketaccess.Spec.PolicyActionsConfigMapData, err = util.ReadConfigData(b.kubeClient, bucketAccessClass.PolicyActionsConfigMap) + if err != nil { + return err + } + // bucketaccess.Spec.Principal - set by the driver + bucketaccess.Spec.Provisioner = bucketAccessClass.Provisioner + bucketaccess.Spec.Parameters = util.CopySS(bucketAccessClass.Parameters) + + bucketaccess, err = b.bucketClient.ObjectstorageV1alpha1().BucketAccesses().Create(context.Background(), bucketaccess, metav1.CreateOptions{}) + if err != nil { + return err + } + + err = retry.RetryOnConflict(retry.DefaultRetry, func() error { + bucketAccessRequest.Spec.BucketAccessName = bucketaccess.Name + _, err := b.bucketClient.ObjectstorageV1alpha1().BucketAccessRequests(bucketAccessRequest.Namespace).Update(ctx, bucketAccessRequest, metav1.UpdateOptions{}) + if err != nil { + return err + } + return nil + }) + if err != nil { + return err + } + glog.Infof("Finished creating BucketAccess %v", bucketaccess.Name) + return nil +} + +func (b *bucketAccessRequestListener) FindBucketAccess(ctx context.Context, bar *v1alpha1.BucketAccessRequest) *v1alpha1.BucketAccess { + bucketAccessList, err := b.bucketClient.ObjectstorageV1alpha1().BucketAccesses().List(ctx, metav1.ListOptions{}) + if err != nil || len(bucketAccessList.Items) <= 0 { + return nil + } + for _, bucketaccess := range bucketAccessList.Items { + if bucketaccess.Spec.BucketAccessRequest.Name == bar.Name && + bucketaccess.Spec.BucketAccessRequest.Namespace == bar.Namespace && + bucketaccess.Spec.BucketAccessRequest.UID == bar.UID { + return &bucketaccess + } + } + return nil +} diff --git a/pkg/bucketaccessrequest/bucketaccessrequest_test.go b/pkg/bucketaccessrequest/bucketaccessrequest_test.go new file mode 100644 index 0000000..7383494 --- /dev/null +++ b/pkg/bucketaccessrequest/bucketaccessrequest_test.go @@ -0,0 +1,332 @@ +package bucketaccessrequest + +import ( + "context" + "testing" + + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + bucketclientset "github.com/kubernetes-sigs/container-object-storage-interface-api/clientset/fake" + "k8s.io/client-go/kubernetes/fake" + + types "github.com/kubernetes-sigs/container-object-storage-interface-api/apis/objectstorage.k8s.io/v1alpha1" + "github.com/kubernetes-sigs/container-object-storage-interface-controller/pkg/util" +) + +var sa1 = v1.ServiceAccount{ + ObjectMeta: metav1.ObjectMeta{ + Name: "sa1", + Namespace: "default", + }, +} + +var sa2 = v1.ServiceAccount{ + ObjectMeta: metav1.ObjectMeta{ + Name: "sa2", + Namespace: "default", + }, +} + +var cosiConfigMap = v1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "testconfigmap", + Namespace: "default", + Labels: map[string]string{ + "cosi-configmap": "test-cred1", + }, + }, + Data: map[string]string{ + "profile": "profile1", + "certfile": "cert1", + }, +} + +var classGoldAccessParameters = map[string]string{ + "param1": "value1", + "param2": "value2", +} + +var goldAccessClass = types.BucketAccessClass{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "objectstorage.k8s.io/v1alpha1", + Kind: "BucketAccessClass", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "classaccessgold", + }, + Provisioner: "provisioner1", + PolicyActionsConfigMap: &v1.ObjectReference{Name: "testconfigmap", Namespace: "default"}, + Parameters: classGoldAccessParameters, +} + +var bucketRequest1 = types.BucketRequest{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "objectstorage.k8s.io/v1alpha1", + Kind: "BucketRequest", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "bucketrequest1", + Namespace: "default", + }, + Spec: types.BucketRequestSpec{ + BucketPrefix: "cosi", + Protocol: types.RequestedProtocol{ + Name: "s3", + Version: "", + }, + BucketClassName: "classgold", + BucketInstanceName: "cosi1234567890", + }, +} + +var bucketAccessRequest1 = types.BucketAccessRequest{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "objectstorage.k8s.io/v1alpha1", + Kind: "BucketAccessRequest", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "bucketaccessrequest1", + Namespace: "default", + }, + Spec: types.BucketAccessRequestSpec{ + ServiceAccountName: "sa1", + BucketRequestName: "bucketrequest1", + BucketAccessClassName: "classaccessgold", + }, +} + +var bucketAccessRequest2 = types.BucketAccessRequest{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "objectstorage.k8s.io/v1alpha1", + Kind: "BucketAccessRequest", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "bucketaccessrequest2", + Namespace: "default", + }, + Spec: types.BucketAccessRequestSpec{ + ServiceAccountName: "sa2", + BucketRequestName: "bucketrequest1", + BucketAccessClassName: "classaccessgold", + }, +} + +// Test basic add functionality +func TestAddBAR(t *testing.T) { + runCreateBucketAccess(t, "add") +} + +// Test add with multipleBRs +func TestAddWithMultipleBAR(t *testing.T) { + runCreateBucketWithMultipleBA(t, "addWithMultipleBR") +} + +// Test add idempotency +func TestAddBARIdempotency(t *testing.T) { + runCreateBucketIdempotency(t, "addWithMultipleBR") +} + +func runCreateBucketAccess(t *testing.T, name string) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + client := bucketclientset.NewSimpleClientset() + kubeClient := fake.NewSimpleClientset() + + listener := NewListener() + listener.InitializeKubeClient(kubeClient) + listener.InitializeBucketClient(client) + + _, err := kubeClient.CoreV1().ServiceAccounts(sa1.Namespace).Create(ctx, &sa1, metav1.CreateOptions{}) + if err != nil { + t.Fatalf("Error occurred when creating ServiceAccount: %v", err) + } + defer kubeClient.CoreV1().ServiceAccounts(sa1.Namespace).Delete(ctx, sa1.Name, metav1.DeleteOptions{}) + + _, err = kubeClient.CoreV1().ConfigMaps(cosiConfigMap.Namespace).Create(ctx, &cosiConfigMap, metav1.CreateOptions{}) + if err != nil { + t.Fatalf("Error occurred when creating ConfigMap: %v", err) + } + defer kubeClient.CoreV1().ConfigMaps(cosiConfigMap.Namespace).Delete(ctx, cosiConfigMap.Name, metav1.DeleteOptions{}) + + bucketaccessclass, err := util.CreateBucketAccessClass(ctx, client, &goldAccessClass) + if err != nil { + t.Fatalf("Error occurred when creating BucketAccessClass: %v", err) + } + + bucketrequest, err := util.CreateBucketRequest(ctx, client, &bucketRequest1) + if err != nil { + t.Fatalf("Error occurred when creating BucketRequest: %v", err) + } + + bucketaccessrequest, err := util.CreateBucketAccessRequest(ctx, client, &bucketAccessRequest1) + if err != nil { + t.Fatalf("Error occurred when creating BucketAccessRequest: %v", err) + } + + listener.Add(ctx, bucketaccessrequest) + + bucketAccessList := util.GetBucketAccesses(ctx, client, 1) + defer util.DeleteObjects(ctx, client, *bucketrequest, *bucketaccessrequest, *bucketaccessclass, bucketAccessList.Items) + + if len(bucketAccessList.Items) != 1 { + t.Fatalf("Expecting a single BucketAccess created but found %v", len(bucketAccessList.Items)) + } + bucketaccess := bucketAccessList.Items[0] + + bucketaccessrequest, err = client.ObjectstorageV1alpha1().BucketAccessRequests(bucketaccessrequest.Namespace).Get(ctx, bucketaccessrequest.Name, metav1.GetOptions{}) + if err != nil { + t.Fatalf("Error occurred when updating BucketAccessRequest: %v", err) + } + + if util.ValidateBucketAccess(bucketaccess, *bucketaccessrequest, *bucketaccessclass) { + return + } else { + t.Fatalf("Failed to compare the resulting BucketAccess with the BucketAccessRequest %v and BucketAccessClass %v", bucketaccessrequest, bucketaccessclass) + } +} + +func runCreateBucketWithMultipleBA(t *testing.T, name string) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + client := bucketclientset.NewSimpleClientset() + kubeClient := fake.NewSimpleClientset() + + listener := NewListener() + listener.InitializeKubeClient(kubeClient) + listener.InitializeBucketClient(client) + + _, err := kubeClient.CoreV1().ServiceAccounts(sa1.Namespace).Create(ctx, &sa1, metav1.CreateOptions{}) + if err != nil { + t.Fatalf("Error occurred when creating ServiceAccount: %v", err) + } + defer kubeClient.CoreV1().ServiceAccounts(sa1.Namespace).Delete(ctx, sa1.Name, metav1.DeleteOptions{}) + + _, err = kubeClient.CoreV1().ServiceAccounts(sa2.Namespace).Create(ctx, &sa2, metav1.CreateOptions{}) + if err != nil { + t.Fatalf("Error occurred when creating ServiceAccount: %v", err) + } + defer kubeClient.CoreV1().ServiceAccounts(sa2.Namespace).Delete(ctx, sa2.Name, metav1.DeleteOptions{}) + + _, err = kubeClient.CoreV1().ConfigMaps(cosiConfigMap.Namespace).Create(ctx, &cosiConfigMap, metav1.CreateOptions{}) + if err != nil { + t.Fatalf("Error occurred when creating ConfigMap: %v", err) + } + defer kubeClient.CoreV1().ConfigMaps(cosiConfigMap.Namespace).Delete(ctx, cosiConfigMap.Name, metav1.DeleteOptions{}) + + bucketaccessclass, err := util.CreateBucketAccessClass(ctx, client, &goldAccessClass) + if err != nil { + t.Fatalf("Error occurred when creating BucketAccessClass: %v", err) + } + + bucketrequest, err := util.CreateBucketRequest(ctx, client, &bucketRequest1) + if err != nil { + t.Fatalf("Error occurred when creating BucketRequest: %v", err) + } + + bucketaccessrequest, err := util.CreateBucketAccessRequest(ctx, client, &bucketAccessRequest1) + if err != nil { + t.Fatalf("Error occurred when creating BucketAccessRequest: %v", err) + } + + bucketaccessrequest2, err := util.CreateBucketAccessRequest(ctx, client, &bucketAccessRequest2) + if err != nil { + t.Fatalf("Error occurred when creating BucketAccessRequest: %v", err) + } + + listener.Add(ctx, bucketaccessrequest) + listener.Add(ctx, bucketaccessrequest2) + + bucketAccessList := util.GetBucketAccesses(ctx, client, 2) + defer util.DeleteObjects(ctx, client, *bucketrequest, *bucketaccessrequest, *bucketaccessrequest2, *bucketaccessclass, bucketAccessList.Items) + + if len(bucketAccessList.Items) != 2 { + t.Fatalf("Expecting a single BucketAccess created but found %v", len(bucketAccessList.Items)) + } + bucketaccess := bucketAccessList.Items[0] + bucketaccess2 := bucketAccessList.Items[1] + + bucketaccessrequest, err = client.ObjectstorageV1alpha1().BucketAccessRequests(bucketaccessrequest.Namespace).Get(ctx, bucketaccessrequest.Name, metav1.GetOptions{}) + if err != nil { + t.Fatalf("Error occurred when updating BucketAccessRequest: %v", err) + } + bucketaccessrequest2, err = client.ObjectstorageV1alpha1().BucketAccessRequests(bucketaccessrequest2.Namespace).Get(ctx, bucketaccessrequest2.Name, metav1.GetOptions{}) + if err != nil { + t.Fatalf("Error occurred when updating BucketAccessRequest: %v", err) + } + + if (util.ValidateBucketAccess(bucketaccess, *bucketaccessrequest, *bucketaccessclass) && util.ValidateBucketAccess(bucketaccess2, *bucketaccessrequest2, *bucketaccessclass)) || + (util.ValidateBucketAccess(bucketaccess2, *bucketaccessrequest, *bucketaccessclass) && util.ValidateBucketAccess(bucketaccess, *bucketaccessrequest2, *bucketaccessclass)) { + return + } else { + t.Fatalf("Failed to compare the resulting BucketAccess with the BucketAccessRequest %v and BucketAccessClass %v", bucketaccessrequest, bucketaccessclass) + } + +} + +func runCreateBucketIdempotency(t *testing.T, name string) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + client := bucketclientset.NewSimpleClientset() + kubeClient := fake.NewSimpleClientset() + + listener := NewListener() + listener.InitializeKubeClient(kubeClient) + listener.InitializeBucketClient(client) + + _, err := kubeClient.CoreV1().ServiceAccounts(sa1.Namespace).Create(ctx, &sa1, metav1.CreateOptions{}) + if err != nil { + t.Fatalf("Error occurred when creating ServiceAccount: %v", err) + } + defer kubeClient.CoreV1().ServiceAccounts(sa1.Namespace).Delete(ctx, sa1.Name, metav1.DeleteOptions{}) + + _, err = kubeClient.CoreV1().ConfigMaps(cosiConfigMap.Namespace).Create(ctx, &cosiConfigMap, metav1.CreateOptions{}) + if err != nil { + t.Fatalf("Error occurred when creating ConfigMap: %v", err) + } + defer kubeClient.CoreV1().ConfigMaps(cosiConfigMap.Namespace).Delete(ctx, cosiConfigMap.Name, metav1.DeleteOptions{}) + + bucketaccessclass, err := util.CreateBucketAccessClass(ctx, client, &goldAccessClass) + if err != nil { + t.Fatalf("Error occurred when creating BucketAccessClass: %v", err) + } + + bucketrequest, err := util.CreateBucketRequest(ctx, client, &bucketRequest1) + if err != nil { + t.Fatalf("Error occurred when creating BucketRequest: %v", err) + } + + bucketaccessrequest, err := util.CreateBucketAccessRequest(ctx, client, &bucketAccessRequest1) + if err != nil { + t.Fatalf("Error occurred when creating BucketAccessRequest: %v", err) + } + + listener.Add(ctx, bucketaccessrequest) + + bucketAccessList := util.GetBucketAccesses(ctx, client, 1) + defer util.DeleteObjects(ctx, client, *bucketrequest, *bucketaccessrequest, *bucketaccessclass, bucketAccessList.Items) + + if len(bucketAccessList.Items) != 1 { + t.Fatalf("Expecting a single BucketAccess created but found %v", len(bucketAccessList.Items)) + } + bucketaccess := bucketAccessList.Items[0] + + bucketaccessrequest, err = client.ObjectstorageV1alpha1().BucketAccessRequests(bucketaccessrequest.Namespace).Get(ctx, bucketaccessrequest.Name, metav1.GetOptions{}) + if err != nil { + t.Fatalf("Error occurred when updating BucketAccessRequest: %v", err) + } + + if !util.ValidateBucketAccess(bucketaccess, *bucketaccessrequest, *bucketaccessclass) { + t.Fatalf("Failed to compare the resulting BucketAccess with the BucketAccessRequest %v and BucketAccessClass %v", bucketaccessrequest, bucketaccessclass) + } + + // call the add directly the second time + listener.Add(ctx, bucketaccessrequest) + bucketAccessList = util.GetBucketAccesses(ctx, client, 1) + if len(bucketAccessList.Items) != 1 { + t.Fatalf("Expecting a single BucketAccess created but found %v", len(bucketAccessList.Items)) + } +} diff --git a/pkg/bucketrequest/bucketrequest.go b/pkg/bucketrequest/bucketrequest.go index 861f233..ab12630 100644 --- a/pkg/bucketrequest/bucketrequest.go +++ b/pkg/bucketrequest/bucketrequest.go @@ -5,6 +5,7 @@ import ( "fmt" "strings" + v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "github.com/kubernetes-sigs/container-object-storage-interface-api/apis/objectstorage.k8s.io/v1alpha1" @@ -12,6 +13,7 @@ import ( bucketcontroller "github.com/kubernetes-sigs/container-object-storage-interface-api/controller" "github.com/kubernetes-sigs/container-object-storage-interface-controller/pkg/util" kubeclientset "k8s.io/client-go/kubernetes" + "k8s.io/client-go/util/retry" "github.com/golang/glog" ) @@ -35,36 +37,37 @@ func (b *bucketRequestListener) InitializeBucketClient(bc bucketclientset.Interf // Add creates a bucket in response to a bucketrequest func (b *bucketRequestListener) Add(ctx context.Context, obj *v1alpha1.BucketRequest) error { - glog.V(1).Infof("add called for bucket %s", obj.Name) + glog.V(1).Infof("Add called for BucketRequest %s", obj.Name) bucketRequest := obj err := b.provisionBucketRequestOperation(ctx, bucketRequest) if err != nil { // Provisioning is 100% finished / not in progress. switch err { case util.ErrInvalidBucketClass: - glog.V(5).Infof("Bucket Class specified does not exist. Stop provisioning, removing bucketRequest %s from bucketRequests in progress", bucketRequest.UID) + glog.V(1).Infof("BucketClass specified does not exist while processing BucketRequest %v.", bucketRequest.Name) err = nil case util.ErrBucketAlreadyExists: - glog.V(5).Infof("Bucket already exist for this bucket request. Stop provisioning, removing bucketRequest %s from bucketRequests in progress", bucketRequest.UID) + glog.V(1).Infof("Bucket already exist for this bucket request %v.", bucketRequest.Name) err = nil default: - glog.V(2).Infof("Final error received, removing buckerRequest %s from bucketRequests in progress", bucketRequest.UID) + glog.V(1).Infof("Error occurred processing BucketRequest %v: %v", bucketRequest.Name, err) } return err } - glog.V(5).Infof("BucketRequest processing succeeded, removing bucketRequest %s from bucketRequests in progress", bucketRequest.UID) + glog.V(1).Infof("BucketRequest %v is successfully processed.", bucketRequest.Name) return nil } // update processes any updates made to the bucket request func (b *bucketRequestListener) Update(ctx context.Context, old, new *v1alpha1.BucketRequest) error { - glog.V(1).Infof("update called for bucket %v", old) + glog.V(1).Infof("Update called for BucketRequest %v", old.Name) return nil } // Delete processes a bucket for which bucket request is deleted func (b *bucketRequestListener) Delete(ctx context.Context, obj *v1alpha1.BucketRequest) error { + glog.V(1).Infof("Delete called for BucketRequest %v", obj.Name) return nil } @@ -93,7 +96,7 @@ func (b *bucketRequestListener) provisionBucketRequestOperation(ctx context.Cont return util.ErrInvalidBucketClass } - glog.Infof("creating bucket for bucketrequest %v", bucketRequest.Name) + glog.Infof("Creating Bucket for BucketRequest %v", bucketRequest.Name) // create bucket bucket = &v1alpha1.Bucket{} @@ -102,7 +105,7 @@ func (b *bucketRequestListener) provisionBucketRequestOperation(ctx context.Cont bucket.Spec.RetentionPolicy = bucketClass.RetentionPolicy bucket.Spec.AnonymousAccessMode = bucketClass.AnonymousAccessMode bucket.Spec.BucketClassName = bucketClass.Name - bucket.Spec.BucketRequest = &v1alpha1.BucketRequestReference{ + bucket.Spec.BucketRequest = &v1.ObjectReference{ Name: bucketRequest.Name, Namespace: bucketRequest.Namespace, UID: bucketRequest.ObjectMeta.UID} @@ -114,11 +117,22 @@ func (b *bucketRequestListener) provisionBucketRequestOperation(ctx context.Cont bucket, err = b.bucketClient.ObjectstorageV1alpha1().Buckets().Create(context.Background(), bucket, metav1.CreateOptions{}) if err != nil { - glog.V(5).Infof("Error occurred when creating bucket %v", err) + glog.V(5).Infof("Error occurred when creating Bucket %v", err) return err } - glog.Infof("Finished creating bucket %v", bucket.Name) + err = retry.RetryOnConflict(retry.DefaultRetry, func() error { + bucketRequest.Spec.BucketInstanceName = bucket.Name + _, err := b.bucketClient.ObjectstorageV1alpha1().BucketRequests(bucketRequest.Namespace).Update(ctx, bucketRequest, metav1.UpdateOptions{}) + if err != nil { + return err + } + return nil + }) + if err != nil { + return err + } + glog.Infof("Finished creating Bucket %v", bucket.Name) return nil } @@ -154,11 +168,6 @@ func (b *bucketRequestListener) FindBucket(ctx context.Context, br *v1alpha1.Buc // cloneTheBucket clones a bucket to a different namespace when a BR is for brownfield. func (b *bucketRequestListener) cloneTheBucket(bucketRequest *v1alpha1.BucketRequest) error { - glog.V(1).Infof("clone called for bucket %s", bucketRequest.Spec.BucketInstanceName) + glog.V(1).Infof("Clone called for Bucket %s", bucketRequest.Spec.BucketInstanceName) return util.ErrNotImplemented } - -// logOperation format and prints logs -func logOperation(operation, format string, a ...interface{}) string { - return fmt.Sprintf(fmt.Sprintf("%s: %s", operation, format), a...) -} diff --git a/pkg/bucketrequest/bucketrequest_test.go b/pkg/bucketrequest/bucketrequest_test.go index cef7e37..87057e5 100644 --- a/pkg/bucketrequest/bucketrequest_test.go +++ b/pkg/bucketrequest/bucketrequest_test.go @@ -71,7 +71,7 @@ var bucketRequest2 = types.BucketRequest{ } // Test basic add functionality -func TestAdd(t *testing.T) { +func TestAddBR(t *testing.T) { runCreateBucket(t, "add") } @@ -81,7 +81,7 @@ func TestAddWithMultipleBR(t *testing.T) { } // Test add idempotency -func TestAddIdempotency(t *testing.T) { +func TestAddBRIdempotency(t *testing.T) { runCreateBucketIdempotency(t, "addWithMultipleBR") } @@ -98,12 +98,12 @@ func runCreateBucket(t *testing.T, name string) { bucketclass, err := util.CreateBucketClass(ctx, client, &goldClass) if err != nil { - t.Fatalf("Error occurred when creating bucketclass: %v", err) + t.Fatalf("Error occurred when creating BucketClass: %v", err) } bucketrequest, err := util.CreateBucketRequest(ctx, client, &bucketRequest1) if err != nil { - t.Fatalf("Error occurred when creating bucketrequest: %v", err) + t.Fatalf("Error occurred when creating BucketRequest: %v", err) } listener.Add(ctx, bucketrequest) @@ -112,14 +112,19 @@ func runCreateBucket(t *testing.T, name string) { defer util.DeleteObjects(ctx, client, *bucketrequest, *bucketclass, bucketList.Items) if len(bucketList.Items) != 1 { - t.Fatalf("Expecting a single bucket created but found %v", len(bucketList.Items)) + t.Fatalf("Expecting a single Bucket created but found %v", len(bucketList.Items)) } bucket := bucketList.Items[0] + bucketrequest, err = client.ObjectstorageV1alpha1().BucketRequests(bucketrequest.Namespace).Get(ctx, bucketrequest.Name, metav1.GetOptions{}) + if err != nil { + t.Fatalf("Error occurred when reading BucketRequest: %v", err) + } + if util.ValidateBucket(bucket, *bucketrequest, *bucketclass) { return } else { - t.Fatalf("Failed to compare the resulting bucket with the BucketRequest %v and BucketClass %v", bucketrequest, bucketclass) + t.Fatalf("Failed to compare the resulting Bucket with the BucketRequest %v and BucketClass %v", bucketrequest, bucketclass) } } @@ -136,17 +141,17 @@ func runCreateBucketWithMultipleBR(t *testing.T, name string) { bucketclass, err := util.CreateBucketClass(ctx, client, &goldClass) if err != nil { - t.Fatalf("Error occurred when creating bucketclass: %v", err) + t.Fatalf("Error occurred when creating BucketClass: %v", err) } bucketrequest, err := util.CreateBucketRequest(ctx, client, &bucketRequest1) if err != nil { - t.Fatalf("Error occurred when creating bucketrequest: %v", err) + t.Fatalf("Error occurred when creating BucketRequest: %v", err) } bucketrequest2, err := util.CreateBucketRequest(ctx, client, &bucketRequest2) if err != nil { - t.Fatalf("Error occurred when creating bucketrequest: %v", err) + t.Fatalf("Error occurred when creating BucketRequest: %v", err) } listener.Add(ctx, bucketrequest) @@ -155,16 +160,25 @@ func runCreateBucketWithMultipleBR(t *testing.T, name string) { bucketList := util.GetBuckets(ctx, client, 2) defer util.DeleteObjects(ctx, client, *bucketrequest, *bucketrequest2, *bucketclass, bucketList.Items) if len(bucketList.Items) != 2 { - t.Fatalf("Expecting two buckets created but found %v", len(bucketList.Items)) + t.Fatalf("Expecting two Buckets created but found %v", len(bucketList.Items)) } bucket := bucketList.Items[0] bucket2 := bucketList.Items[1] + bucketrequest, err = client.ObjectstorageV1alpha1().BucketRequests(bucketrequest.Namespace).Get(ctx, bucketrequest.Name, metav1.GetOptions{}) + if err != nil { + t.Fatalf("Error occurred when reading BucketRequest: %v", err) + } + bucketrequest2, err = client.ObjectstorageV1alpha1().BucketRequests(bucketrequest2.Namespace).Get(ctx, bucketrequest2.Name, metav1.GetOptions{}) + if err != nil { + t.Fatalf("Error occurred when reading BucketRequest: %v", err) + } + if (util.ValidateBucket(bucket, *bucketrequest, *bucketclass) && util.ValidateBucket(bucket2, *bucketrequest2, *bucketclass)) || (util.ValidateBucket(bucket2, *bucketrequest, *bucketclass) && util.ValidateBucket(bucket, *bucketrequest2, *bucketclass)) { return } else { - t.Fatalf("Failed to compare the resulting bucket with the BucketRequest %v and BucketClass %v", bucketrequest, bucketclass) + t.Fatalf("Failed to compare the resulting Bucket with the BucketRequest %v and BucketClass %v", bucketrequest, bucketclass) } } @@ -181,12 +195,12 @@ func runCreateBucketIdempotency(t *testing.T, name string) { bucketclass, err := util.CreateBucketClass(ctx, client, &goldClass) if err != nil { - t.Fatalf("Error occurred when creating bucketclass: %v", err) + t.Fatalf("Error occurred when creating BucketClass: %v", err) } bucketrequest, err := util.CreateBucketRequest(ctx, client, &bucketRequest1) if err != nil { - t.Fatalf("Error occurred when creating bucketrequest: %v", err) + t.Fatalf("Error occurred when creating BucketRequest: %v", err) } listener.Add(ctx, bucketrequest) @@ -195,14 +209,19 @@ func runCreateBucketIdempotency(t *testing.T, name string) { defer util.DeleteObjects(ctx, client, *bucketrequest, *bucketclass, bucketList.Items) if len(bucketList.Items) != 1 { - t.Errorf("Expecting a single bucket created but found %v", len(bucketList.Items)) + t.Errorf("Expecting a single Bucket created but found %v", len(bucketList.Items)) } bucket := bucketList.Items[0] + bucketrequest, err = client.ObjectstorageV1alpha1().BucketRequests(bucketrequest.Namespace).Get(ctx, bucketrequest.Name, metav1.GetOptions{}) + if err != nil { + t.Fatalf("Error occurred when reading BucketRequest: %v", err) + } + if util.ValidateBucket(bucket, *bucketrequest, *bucketclass) { return } else { - t.Fatalf("Failed to compare the resulting bucket with the BucketRequest %v and BucketClass %v", bucketrequest, bucketclass) + t.Fatalf("Failed to compare the resulting Bucket with the BucketRequest %v and BucketClass %v", bucketrequest, bucketclass) // call the add directly the second time } @@ -210,6 +229,6 @@ func runCreateBucketIdempotency(t *testing.T, name string) { bucketList = util.GetBuckets(ctx, client, 1) if len(bucketList.Items) != 1 { - t.Fatalf("Expecting a single bucket created but found %v", len(bucketList.Items)) + t.Fatalf("Expecting a single Bucket created but found %v", len(bucketList.Items)) } } diff --git a/pkg/util/util.go b/pkg/util/util.go index ba7e2c3..14a8ced 100644 --- a/pkg/util/util.go +++ b/pkg/util/util.go @@ -2,6 +2,7 @@ package util import ( "context" + "encoding/json" "errors" "fmt" "golang.org/x/time/rate" @@ -10,6 +11,7 @@ import ( "strings" "time" + v1 "k8s.io/api/core/v1" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" apiextensions "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" @@ -35,10 +37,15 @@ import ( var ( // Error codes that the central controller will return - ErrBucketAlreadyExists = errors.New("A bucket already existing that matches the bucket request") - ErrInvalidBucketClass = errors.New("Cannot find Bucket Class with the name specified in the bucket request") - ErrBCUnavailable = errors.New("BucketClass is not available") - ErrNotImplemented = errors.New("Operation Not Implemented") + ErrBucketAlreadyExists = errors.New("A bucket already existing that matches the bucket request") + ErrInvalidBucketClass = errors.New("Cannot find bucket class with the name specified in the bucket request") + ErrBucketAccessAlreadyExists = errors.New("A bucket access already existing that matches the bucket access request") + ErrInvalidBucketAccessClass = errors.New("Cannot find bucket access class with the name specified in the bucket access request") + ErrInvalidBucketRequest = errors.New("Invalid bucket request specified in the bucket access request") + ErrWaitForBucketProvisioning = errors.New("Bucket instance specified in the bucket request is not available to provision bucket access") + ErrBCUnavailable = errors.New("BucketClass is not available") + ErrNotImplemented = errors.New("Operation Not Implemented") + ErrNilConfigMap = errors.New("ConfigMap cannot be nil") ) func CopySS(m map[string]string) map[string]string { @@ -65,6 +72,25 @@ func GetUUID() string { return string(uuid.NewUUID()) } +func ReadConfigData(kubeClient kubeclientset.Interface, configMapRef *v1.ObjectReference) (string, error) { + if configMapRef == nil { + return "", ErrNilConfigMap + } + configMap, err := kubeClient.CoreV1().ConfigMaps(configMapRef.Namespace).Get(context.TODO(), configMapRef.Name, metav1.GetOptions{}) + if err != nil { + return "", err + } + payload := make(map[string]string) + for name, data := range configMap.Data { + payload[name] = data + } + cData, err := json.Marshal(payload) + if err != nil { + return "", err + } + return string(cData), nil +} + // SetupTest is utility function to create clients and controller // This is used by bucket request and bucket access request unit tests func SetupTest(ctx context.Context) (bucketclientset.Interface, kubeclientset.Interface, *controller.ObjectStorageController) { @@ -138,6 +164,7 @@ func GetBuckets(ctx context.Context, client bucketclientset.Interface, numExpect // This is used by bucket request unit tests func ValidateBucket(bucket types.Bucket, bucketrequest types.BucketRequest, bucketclass types.BucketClass) bool { if strings.HasPrefix(bucket.Name, bucketrequest.Spec.BucketPrefix) && + bucketrequest.Spec.BucketInstanceName == bucket.Name && bucket.Spec.BucketClassName == bucketrequest.Spec.BucketClassName && bucket.Spec.BucketRequest.Name == bucketrequest.Name && bucket.Spec.BucketRequest.Namespace == bucketrequest.Namespace && @@ -175,8 +202,9 @@ func GetBucketAccesses(ctx context.Context, client bucketclientset.Interface, nu // This is used by bucket access request unit tests func ValidateBucketAccess(bucketaccess types.BucketAccess, bucketaccessrequest types.BucketAccessRequest, bucketaccessclass types.BucketAccessClass) bool { if bucketaccess.Spec.BucketInstanceName != "" && - bucketaccess.Spec.BucketAccessRequest == bucketaccessrequest.Name && - bucketaccess.Spec.ServiceAccount == bucketaccessrequest.Spec.ServiceAccountName && + bucketaccessrequest.Spec.BucketAccessName == bucketaccess.Name && + bucketaccess.Spec.BucketAccessRequest.UID == bucketaccessrequest.UID && + bucketaccess.Spec.ServiceAccount.Name == bucketaccessrequest.Spec.ServiceAccountName && bucketaccess.Spec.PolicyActionsConfigMapData != "" && bucketaccess.Spec.Provisioner == bucketaccessclass.Provisioner { return true