diff --git a/PendingReleaseNotes.md b/PendingReleaseNotes.md index c850f99df..7683a1a58 100644 --- a/PendingReleaseNotes.md +++ b/PendingReleaseNotes.md @@ -12,3 +12,4 @@ - Enable periodic monitoring for CephBlockPoolRadosNamespaces mirroring (see [#14896](https://github.com/rook/rook/pull/14896)). - Allow migration of PVC based OSDs to enable or disable encryption (see [#14776](https://github.com/rook/rook/pull/14776)). - Support `rgw_enable_apis` option for CephObjectStore (see [#15064](https://github.com/rook/rook/pull/15064)). +- ObjectBucketClaim management of s3 bucket policy via the `bucketPolicy` field (see [#15138](https://github.com/rook/rook/pull/15138)). diff --git a/pkg/operator/ceph/object/bucket/provisioner.go b/pkg/operator/ceph/object/bucket/provisioner.go index eba551186..d2d7cb9cc 100644 --- a/pkg/operator/ceph/object/bucket/provisioner.go +++ b/pkg/operator/ceph/object/bucket/provisioner.go @@ -22,6 +22,7 @@ import ( "strings" "github.com/aws/aws-sdk-go/aws/awserr" + "github.com/aws/aws-sdk-go/service/s3" "github.com/ceph/go-ceph/rgw/admin" "github.com/coreos/pkg/capnslog" "github.com/google/go-cmp/cmp" @@ -62,6 +63,7 @@ type additionalConfigSpec struct { maxSize *int64 bucketMaxObjects *int64 bucketMaxSize *int64 + bucketPolicy *string } var _ apibkt.Provisioner = &Provisioner{} @@ -129,7 +131,12 @@ func (p Provisioner) Provision(options *apibkt.BucketOptions) (*bktv1alpha1.Obje } logger.Infof("set user %q bucket max to %d", p.cephUserName, singleBucketQuota) - err = p.setAdditionalSettings(options) + additionalConfig, err := additionalConfigSpecFromMap(options.ObjectBucketClaim.Spec.AdditionalConfig) + if err != nil { + return nil, errors.Wrap(err, "failed to process additionalConfig") + } + + err = p.setAdditionalSettings(additionalConfig) if err != nil { return nil, errors.Wrapf(err, "failed to set additional settings for OBC %q in NS %q associated with CephObjectStore %q in NS %q", options.ObjectBucketClaim.Name, options.ObjectBucketClaim.Namespace, p.objectStoreName, p.clusterInfo.Namespace) } @@ -173,6 +180,24 @@ func (p Provisioner) Grant(options *apibkt.BucketOptions) (*bktv1alpha1.ObjectBu return nil, err } + additionalConfig, err := additionalConfigSpecFromMap(options.ObjectBucketClaim.Spec.AdditionalConfig) + if err != nil { + return nil, errors.Wrap(err, "failed to process additionalConfig") + } + + // setting quota limit if it is enabled + err = p.setAdditionalSettings(additionalConfig) + if err != nil { + return nil, errors.Wrapf(err, "failed to set additional settings for OBC %q in NS %q associated with CephObjectStore %q in NS %q", options.ObjectBucketClaim.Name, options.ObjectBucketClaim.Namespace, p.objectStoreName, p.clusterInfo.Namespace) + } + + if additionalConfig.bucketPolicy != nil { + // if the user is managing the bucket policy, there's nothing else to do + return p.composeObjectBucket(), nil + } + + // generate the bucket policy if it isn't managed by the user + // if the policy does not exist, we'll create a new and append the statement to it policy, err := p.s3Agent.GetBucketPolicy(p.bucketName) if err != nil { @@ -202,12 +227,6 @@ func (p Provisioner) Grant(options *apibkt.BucketOptions) (*bktv1alpha1.ObjectBu return nil, err } - // setting quota limit if it is enabled - err = p.setAdditionalSettings(options) - if err != nil { - return nil, errors.Wrapf(err, "failed to set additional settings for OBC %q in NS %q associated with CephObjectStore %q in NS %q", options.ObjectBucketClaim.Name, options.ObjectBucketClaim.Namespace, p.objectStoreName, p.clusterInfo.Namespace) - } - // returned ob with connection info return p.composeObjectBucket(), nil } @@ -552,13 +571,8 @@ func (p *Provisioner) populateDomainAndPort(sc *storagev1.StorageClass) error { } // Check for additional options mentioned in OBC and set them accordingly -func (p *Provisioner) setAdditionalSettings(options *apibkt.BucketOptions) error { - additionalConfig, err := additionalConfigSpecFromMap(options.ObjectBucketClaim.Spec.AdditionalConfig) - if err != nil { - return errors.Wrap(err, "failed to process additionalConfig") - } - - err = p.setUserQuota(additionalConfig) +func (p *Provisioner) setAdditionalSettings(additionalConfig *additionalConfigSpec) error { + err := p.setUserQuota(additionalConfig) if err != nil { return errors.Wrap(err, "failed to set user quota") } @@ -568,6 +582,11 @@ func (p *Provisioner) setAdditionalSettings(options *apibkt.BucketOptions) error return errors.Wrap(err, "failed to set bucket quota") } + err = p.setBucketPolicy(additionalConfig) + if err != nil { + return errors.Wrap(err, "failed to set bucket policy") + } + return nil } @@ -675,6 +694,53 @@ func (p *Provisioner) setBucketQuota(additionalConfig *additionalConfigSpec) err return nil } +func (p *Provisioner) setBucketPolicy(additionalConfig *additionalConfigSpec) error { + svc := p.s3Agent.Client + var livePolicy *string + + policyResp, err := svc.GetBucketPolicy(&s3.GetBucketPolicyInput{ + Bucket: &p.bucketName, + }) + if err != nil { + // when no policy is set, an err with code "NoSuchBucketPolicy" is returned + if aerr, ok := err.(awserr.Error); ok { + if aerr.Code() != "NoSuchBucketPolicy" { + return errors.Wrapf(err, "failed to fetch policy for bucket %q", p.bucketName) + } + } + } else { + livePolicy = policyResp.Policy + } + + diff := cmp.Diff(&livePolicy, additionalConfig.bucketPolicy) + if diff == "" { + // policy is in sync + return nil + } + + logger.Debugf("Policy for bucket %q has changed. diff:%s", p.bucketName, diff) + if additionalConfig.bucketPolicy == nil { + // if policy is out of sync and the new policy is nil, we should delete the live policy + _, err = svc.DeleteBucketPolicy(&s3.DeleteBucketPolicyInput{ + Bucket: &p.bucketName, + }) + if err != nil { + return errors.Wrapf(err, "failed to delete policy for bucket %q", p.bucketName) + } + } else { + // set the new policy + _, err = svc.PutBucketPolicy(&s3.PutBucketPolicyInput{ + Bucket: &p.bucketName, + Policy: additionalConfig.bucketPolicy, + }) + if err != nil { + return errors.Wrapf(err, "failed to set policy for bucket %q", p.bucketName) + } + } + + return nil +} + func (p *Provisioner) setTlsCaCert() error { objStore, err := p.getObjectStore() if err != nil { diff --git a/pkg/operator/ceph/object/bucket/provisioner_test.go b/pkg/operator/ceph/object/bucket/provisioner_test.go index 467b0a54f..e5a6ea478 100644 --- a/pkg/operator/ceph/object/bucket/provisioner_test.go +++ b/pkg/operator/ceph/object/bucket/provisioner_test.go @@ -25,9 +25,8 @@ import ( "strings" "testing" + "github.com/aws/aws-sdk-go/aws" "github.com/ceph/go-ceph/rgw/admin" - "github.com/kube-object-storage/lib-bucket-provisioner/pkg/apis/objectbucket.io/v1alpha1" - apibkt "github.com/kube-object-storage/lib-bucket-provisioner/pkg/provisioner/api" cephv1 "github.com/rook/rook/pkg/apis/ceph.rook.io/v1" rookclient "github.com/rook/rook/pkg/client/clientset/versioned/fake" "github.com/rook/rook/pkg/clusterd" @@ -146,7 +145,7 @@ func TestQuanityToInt64(t *testing.T) { } } -func TestProvisioner_setAdditionalSettings(t *testing.T) { +func TestProvisioner_setUserQuota(t *testing.T) { newProvisioner := func(t *testing.T, getResult *map[string]string, getSeen, putSeen *map[string][]string) *Provisioner { mockClient := &object.MockClient{ MockDo: func(req *http.Request) (*http.Response, error) { @@ -196,9 +195,194 @@ func TestProvisioner_setAdditionalSettings(t *testing.T) { return p } - t.Run("user and bucket quota should remain disabled", func(t *testing.T) { + t.Run("user quota should remain disabled", func(t *testing.T) { + getResult := map[string]string{ + userPath: `{"enabled":false,"check_on_raw":false,"max_size":-1024,"max_size_kb":0,"max_objects":-1}`, + } + getSeen := map[string][]string{} + putSeen := map[string][]string{} + + p := newProvisioner(t, &getResult, &getSeen, &putSeen) + p.setBucketName("bob") + + err := p.setUserQuota(&additionalConfigSpec{}) + assert.NoError(t, err) + + assert.Len(t, getSeen[userPath], 1) + assert.Equal(t, numberOfCallsWithValue("uid=bob", getSeen[userPath]), 1) + + assert.Len(t, putSeen[userPath], 0) // user quota should not be touched + }) + + t.Run("user quota should be disabled", func(t *testing.T) { + getResult := map[string]string{ + userPath: `{"enabled":true,"check_on_raw":false,"max_size":-1024,"max_size_kb":0,"max_objects":2}`, + } + getSeen := map[string][]string{} + putSeen := map[string][]string{} + + p := newProvisioner(t, &getResult, &getSeen, &putSeen) + p.setBucketName("bob") + + err := p.setUserQuota(&additionalConfigSpec{}) + assert.NoError(t, err) + + assert.Len(t, getSeen[userPath], 1) + assert.Equal(t, numberOfCallsWithValue("uid=bob", getSeen[userPath]), 1) + + assert.Len(t, putSeen[userPath], 1) + assert.Equal(t, 1, numberOfCallsWithValue("enabled=false", putSeen[userPath])) + }) + + t.Run("user maxSize quota should be enabled", func(t *testing.T) { + getResult := map[string]string{ + userPath: `{"enabled":false,"check_on_raw":false,"max_size":-1024,"max_size_kb":0,"max_objects":-1}`, + } + getSeen := map[string][]string{} + putSeen := map[string][]string{} + + p := newProvisioner(t, &getResult, &getSeen, &putSeen) + p.setBucketName("bob") + + err := p.setUserQuota(&additionalConfigSpec{ + maxSize: aws.Int64(2), + }) + assert.NoError(t, err) + + assert.Len(t, getSeen[userPath], 1) + assert.Equal(t, numberOfCallsWithValue("uid=bob", getSeen[userPath]), 1) + + assert.Len(t, putSeen[userPath], 1) + assert.Equal(t, numberOfCallsWithValue("enabled=true", putSeen[userPath]), 1) + assert.Equal(t, numberOfCallsWithValue("max-size=2", putSeen[userPath]), 1) + }) + + t.Run("user maxObjects quota should be enabled", func(t *testing.T) { + getResult := map[string]string{ + userPath: `{"enabled":false,"check_on_raw":false,"max_size":-1024,"max_size_kb":0,"max_objects":-1}`, + } + getSeen := map[string][]string{} + putSeen := map[string][]string{} + + p := newProvisioner(t, &getResult, &getSeen, &putSeen) + p.setBucketName("bob") + + err := p.setUserQuota(&additionalConfigSpec{ + maxObjects: aws.Int64(2), + }) + assert.NoError(t, err) + + assert.Len(t, getSeen[userPath], 1) + assert.Equal(t, numberOfCallsWithValue("uid=bob", getSeen[userPath]), 1) + + assert.Len(t, putSeen[userPath], 1) + assert.Equal(t, numberOfCallsWithValue("enabled=true", putSeen[userPath]), 1) + assert.Equal(t, numberOfCallsWithValue("max-objects=2", putSeen[userPath]), 1) + }) + + t.Run("user maxObjects and maxSize quotas should be enabled", func(t *testing.T) { + getResult := map[string]string{ + userPath: `{"enabled":false,"check_on_raw":false,"max_size":-1024,"max_size_kb":0,"max_objects":-1}`, + } + getSeen := map[string][]string{} + putSeen := map[string][]string{} + + p := newProvisioner(t, &getResult, &getSeen, &putSeen) + p.setBucketName("bob") + + err := p.setUserQuota(&additionalConfigSpec{ + maxObjects: aws.Int64(2), + maxSize: aws.Int64(3), + }) + assert.NoError(t, err) + + assert.Len(t, getSeen[userPath], 1) + assert.Equal(t, numberOfCallsWithValue("uid=bob", getSeen[userPath]), 1) + + assert.Len(t, putSeen[userPath], 1) + assert.Equal(t, numberOfCallsWithValue("enabled=true", putSeen[userPath]), 1) + assert.Equal(t, numberOfCallsWithValue("max-objects=2", putSeen[userPath]), 1) + assert.Equal(t, numberOfCallsWithValue("max-size=3", putSeen[userPath]), 1) + }) + + t.Run("user quotas are enabled and need updated enabled", func(t *testing.T) { + getResult := map[string]string{ + userPath: `{"enabled":true,"check_on_raw":false,"max_size":1,"max_size_kb":0,"max_objects":1}`, + } + getSeen := map[string][]string{} + putSeen := map[string][]string{} + + p := newProvisioner(t, &getResult, &getSeen, &putSeen) + p.setBucketName("bob") + + err := p.setUserQuota(&additionalConfigSpec{ + maxObjects: aws.Int64(12), + maxSize: aws.Int64(13), + }) + assert.NoError(t, err) + + assert.Len(t, getSeen[userPath], 1) + assert.Equal(t, numberOfCallsWithValue("uid=bob", getSeen[userPath]), 1) + + assert.Len(t, putSeen[userPath], 1) + assert.Equal(t, numberOfCallsWithValue("enabled=true", putSeen[userPath]), 1) + assert.Equal(t, numberOfCallsWithValue("max-objects=12", putSeen[userPath]), 1) + assert.Equal(t, numberOfCallsWithValue("max-size=13", putSeen[userPath]), 1) + }) +} + +func TestProvisioner_setBucketQuota(t *testing.T) { + newProvisioner := func(t *testing.T, getResult *map[string]string, getSeen, putSeen *map[string][]string) *Provisioner { + mockClient := &object.MockClient{ + MockDo: func(req *http.Request) (*http.Response, error) { + path := req.URL.Path + + // t.Logf("HTTP req: %#v", req) + t.Logf("HTTP %s: %s %s", req.Method, path, req.URL.RawQuery) + + statusCode := 200 + if _, ok := (*getResult)[path]; !ok { + // path not configured + statusCode = 500 + } + responseBody := []byte(`[]`) + + switch method := req.Method; method { + case http.MethodGet: + (*getSeen)[path] = append((*getSeen)[path], req.URL.RawQuery) + responseBody = []byte((*getResult)[path]) + case http.MethodPut: + if (*putSeen)[path] == nil { + (*putSeen)[path] = []string{} + } + (*putSeen)[path] = append((*putSeen)[path], req.URL.RawQuery) + default: + panic(fmt.Sprintf("unexpected request: %q. method %q. path %q", req.URL.RawQuery, req.Method, path)) + } + + return &http.Response{ + StatusCode: statusCode, + Body: io.NopCloser(bytes.NewReader(responseBody)), + }, nil + }, + } + + adminClient, err := admin.New("rgw.test", "accesskey", "secretkey", mockClient) + assert.NoError(t, err) + + p := &Provisioner{ + clusterInfo: &client.ClusterInfo{ + Context: context.Background(), + }, + cephUserName: "bob", + adminOpsClient: adminClient, + } + + return p + } + + t.Run("bucket quota should remain disabled", func(t *testing.T) { getResult := map[string]string{ - userPath: `{"enabled":false,"check_on_raw":false,"max_size":-1024,"max_size_kb":0,"max_objects":-1}`, bucketPath: `{"bucket_quota":{"enabled":false,"check_on_raw":false,"max_size":-1024,"max_size_kb":0,"max_objects":-1}}`, } getSeen := map[string][]string{} @@ -207,27 +391,17 @@ func TestProvisioner_setAdditionalSettings(t *testing.T) { p := newProvisioner(t, &getResult, &getSeen, &putSeen) p.setBucketName("bob") - err := p.setAdditionalSettings(&apibkt.BucketOptions{ - ObjectBucketClaim: &v1alpha1.ObjectBucketClaim{ - Spec: v1alpha1.ObjectBucketClaimSpec{ - AdditionalConfig: map[string]string{}, - }, - }, - }) + err := p.setBucketQuota(&additionalConfigSpec{}) assert.NoError(t, err) - assert.Len(t, getSeen[userPath], 1) - assert.Equal(t, numberOfCallsWithValue("uid=bob", getSeen[userPath]), 1) assert.Len(t, getSeen[bucketPath], 1) assert.Equal(t, numberOfCallsWithValue("bucket=bob", getSeen[bucketPath]), 1) - assert.Len(t, putSeen[userPath], 0) // user quota should not be touched assert.Len(t, putSeen[bucketPath], 0) // bucket quota should not be touched }) - t.Run("user and bucket quota should be disabled", func(t *testing.T) { + t.Run("bucket quota should be disabled", func(t *testing.T) { getResult := map[string]string{ - userPath: `{"enabled":true,"check_on_raw":false,"max_size":-1024,"max_size_kb":0,"max_objects":2}`, bucketPath: `{"owner": "bob","bucket_quota":{"enabled":true,"check_on_raw":false,"max_size":-1024,"max_size_kb":0,"max_objects":3}}`, } getSeen := map[string][]string{} @@ -236,165 +410,18 @@ func TestProvisioner_setAdditionalSettings(t *testing.T) { p := newProvisioner(t, &getResult, &getSeen, &putSeen) p.setBucketName("bob") - err := p.setAdditionalSettings(&apibkt.BucketOptions{ - ObjectBucketClaim: &v1alpha1.ObjectBucketClaim{ - Spec: v1alpha1.ObjectBucketClaimSpec{ - AdditionalConfig: map[string]string{}, - }, - }, - }) + err := p.setBucketQuota(&additionalConfigSpec{}) assert.NoError(t, err) - assert.Len(t, getSeen[userPath], 1) - assert.Equal(t, numberOfCallsWithValue("uid=bob", getSeen[userPath]), 1) assert.Len(t, getSeen[bucketPath], 1) assert.Equal(t, numberOfCallsWithValue("bucket=bob", getSeen[bucketPath]), 1) - assert.Len(t, putSeen[userPath], 1) - assert.Equal(t, 1, numberOfCallsWithValue("enabled=false", putSeen[userPath])) assert.Len(t, putSeen[bucketPath], 1) assert.Equal(t, 1, numberOfCallsWithValue("enabled=false", putSeen[bucketPath])) }) - t.Run("user maxSize quota should be enabled", func(t *testing.T) { - getResult := map[string]string{ - userPath: `{"enabled":false,"check_on_raw":false,"max_size":-1024,"max_size_kb":0,"max_objects":-1}`, - bucketPath: `{"bucket_quota":{"enabled":false,"check_on_raw":false,"max_size":-1,"max_size_kb":0,"max_objects":-1}}`, - } - getSeen := map[string][]string{} - putSeen := map[string][]string{} - - p := newProvisioner(t, &getResult, &getSeen, &putSeen) - p.setBucketName("bob") - - err := p.setAdditionalSettings(&apibkt.BucketOptions{ - ObjectBucketClaim: &v1alpha1.ObjectBucketClaim{ - Spec: v1alpha1.ObjectBucketClaimSpec{ - AdditionalConfig: map[string]string{ - "maxSize": "2", - }, - }, - }, - }) - assert.NoError(t, err) - - assert.Len(t, getSeen[userPath], 1) - assert.Equal(t, numberOfCallsWithValue("uid=bob", getSeen[userPath]), 1) - assert.Len(t, getSeen[bucketPath], 1) - assert.Equal(t, numberOfCallsWithValue("bucket=bob", getSeen[bucketPath]), 1) - - assert.Len(t, putSeen[userPath], 1) - assert.Equal(t, numberOfCallsWithValue("enabled=true", putSeen[userPath]), 1) - assert.Equal(t, numberOfCallsWithValue("max-size=2", putSeen[userPath]), 1) - assert.Len(t, putSeen[bucketPath], 0) // bucket quota should not be touched - }) - - t.Run("user maxObjects quota should be enabled", func(t *testing.T) { - getResult := map[string]string{ - userPath: `{"enabled":false,"check_on_raw":false,"max_size":-1024,"max_size_kb":0,"max_objects":-1}`, - bucketPath: `{"bucket_quota":{"enabled":false,"check_on_raw":false,"max_size":-1,"max_size_kb":0,"max_objects":-1}}`, - } - getSeen := map[string][]string{} - putSeen := map[string][]string{} - - p := newProvisioner(t, &getResult, &getSeen, &putSeen) - p.setBucketName("bob") - - err := p.setAdditionalSettings(&apibkt.BucketOptions{ - ObjectBucketClaim: &v1alpha1.ObjectBucketClaim{ - Spec: v1alpha1.ObjectBucketClaimSpec{ - AdditionalConfig: map[string]string{ - "maxObjects": "2", - }, - }, - }, - }) - assert.NoError(t, err) - - assert.Len(t, getSeen[userPath], 1) - assert.Equal(t, numberOfCallsWithValue("uid=bob", getSeen[userPath]), 1) - assert.Len(t, getSeen[bucketPath], 1) - assert.Equal(t, numberOfCallsWithValue("bucket=bob", getSeen[bucketPath]), 1) - - assert.Len(t, putSeen[userPath], 1) - assert.Equal(t, numberOfCallsWithValue("enabled=true", putSeen[userPath]), 1) - assert.Equal(t, numberOfCallsWithValue("max-objects=2", putSeen[userPath]), 1) - assert.Len(t, putSeen[bucketPath], 0) // bucket quota should not be touched - }) - - t.Run("user maxObjects and maxSize quotas should be enabled", func(t *testing.T) { - getResult := map[string]string{ - userPath: `{"enabled":false,"check_on_raw":false,"max_size":-1024,"max_size_kb":0,"max_objects":-1}`, - bucketPath: `{"bucket_quota":{"enabled":false,"check_on_raw":false,"max_size":-1,"max_size_kb":0,"max_objects":-1}}`, - } - getSeen := map[string][]string{} - putSeen := map[string][]string{} - - p := newProvisioner(t, &getResult, &getSeen, &putSeen) - p.setBucketName("bob") - - err := p.setAdditionalSettings(&apibkt.BucketOptions{ - ObjectBucketClaim: &v1alpha1.ObjectBucketClaim{ - Spec: v1alpha1.ObjectBucketClaimSpec{ - AdditionalConfig: map[string]string{ - "maxObjects": "2", - "maxSize": "3", - }, - }, - }, - }) - assert.NoError(t, err) - - assert.Len(t, getSeen[userPath], 1) - assert.Equal(t, numberOfCallsWithValue("uid=bob", getSeen[userPath]), 1) - assert.Len(t, getSeen[bucketPath], 1) - assert.Equal(t, numberOfCallsWithValue("bucket=bob", getSeen[bucketPath]), 1) - - assert.Len(t, putSeen[userPath], 1) - assert.Equal(t, numberOfCallsWithValue("enabled=true", putSeen[userPath]), 1) - assert.Equal(t, numberOfCallsWithValue("max-objects=2", putSeen[userPath]), 1) - assert.Equal(t, numberOfCallsWithValue("max-size=3", putSeen[userPath]), 1) - assert.Len(t, putSeen[bucketPath], 0) // bucket quota should not be touched - }) - - t.Run("user quotas are enabled and need updated enabled", func(t *testing.T) { - getResult := map[string]string{ - userPath: `{"enabled":true,"check_on_raw":false,"max_size":1,"max_size_kb":0,"max_objects":1}`, - bucketPath: `{"bucket_quota":{"enabled":false,"check_on_raw":false,"max_size":-1,"max_size_kb":0,"max_objects":-1}}`, - } - getSeen := map[string][]string{} - putSeen := map[string][]string{} - - p := newProvisioner(t, &getResult, &getSeen, &putSeen) - p.setBucketName("bob") - - err := p.setAdditionalSettings(&apibkt.BucketOptions{ - ObjectBucketClaim: &v1alpha1.ObjectBucketClaim{ - Spec: v1alpha1.ObjectBucketClaimSpec{ - AdditionalConfig: map[string]string{ - "maxObjects": "12", - "maxSize": "13", - }, - }, - }, - }) - assert.NoError(t, err) - - assert.Len(t, getSeen[userPath], 1) - assert.Equal(t, numberOfCallsWithValue("uid=bob", getSeen[userPath]), 1) - assert.Len(t, getSeen[bucketPath], 1) - assert.Equal(t, numberOfCallsWithValue("bucket=bob", getSeen[bucketPath]), 1) - - assert.Len(t, putSeen[userPath], 1) - assert.Equal(t, numberOfCallsWithValue("enabled=true", putSeen[userPath]), 1) - assert.Equal(t, numberOfCallsWithValue("max-objects=12", putSeen[userPath]), 1) - assert.Equal(t, numberOfCallsWithValue("max-size=13", putSeen[userPath]), 1) - assert.Len(t, putSeen[bucketPath], 0) // bucket quota should not be touched - }) - t.Run("bucket maxObjects quota should be enabled", func(t *testing.T) { getResult := map[string]string{ - userPath: `{"enabled":false,"check_on_raw":false,"max_size":-1024,"max_size_kb":0,"max_objects":-1}`, bucketPath: `{"bucket_quota":{"enabled":false,"check_on_raw":false,"max_size":-1,"max_size_kb":0,"max_objects":-1}}`, } getSeen := map[string][]string{} @@ -403,23 +430,14 @@ func TestProvisioner_setAdditionalSettings(t *testing.T) { p := newProvisioner(t, &getResult, &getSeen, &putSeen) p.setBucketName("bob") - err := p.setAdditionalSettings(&apibkt.BucketOptions{ - ObjectBucketClaim: &v1alpha1.ObjectBucketClaim{ - Spec: v1alpha1.ObjectBucketClaimSpec{ - AdditionalConfig: map[string]string{ - "bucketMaxObjects": "4", - }, - }, - }, + err := p.setBucketQuota(&additionalConfigSpec{ + bucketMaxObjects: aws.Int64(4), }) assert.NoError(t, err) - assert.Len(t, getSeen[userPath], 1) - assert.Equal(t, numberOfCallsWithValue("uid=bob", getSeen[userPath]), 1) assert.Len(t, getSeen[bucketPath], 1) assert.Equal(t, numberOfCallsWithValue("bucket=bob", getSeen[bucketPath]), 1) - assert.Len(t, putSeen[userPath], 0) // user quota should not be touched assert.Len(t, putSeen[bucketPath], 1) assert.Equal(t, numberOfCallsWithValue("enabled=true", putSeen[bucketPath]), 1) assert.Equal(t, numberOfCallsWithValue("max-objects=4", putSeen[bucketPath]), 1) @@ -427,7 +445,6 @@ func TestProvisioner_setAdditionalSettings(t *testing.T) { t.Run("bucket maxSize quota should be enabled", func(t *testing.T) { getResult := map[string]string{ - userPath: `{"enabled":false,"check_on_raw":false,"max_size":-1024,"max_size_kb":0,"max_objects":-1}`, bucketPath: `{"bucket_quota":{"enabled":false,"check_on_raw":false,"max_size":-1024,"max_size_kb":0,"max_objects":-1}}`, } getSeen := map[string][]string{} @@ -436,23 +453,14 @@ func TestProvisioner_setAdditionalSettings(t *testing.T) { p := newProvisioner(t, &getResult, &getSeen, &putSeen) p.setBucketName("bob") - err := p.setAdditionalSettings(&apibkt.BucketOptions{ - ObjectBucketClaim: &v1alpha1.ObjectBucketClaim{ - Spec: v1alpha1.ObjectBucketClaimSpec{ - AdditionalConfig: map[string]string{ - "bucketMaxSize": "5", - }, - }, - }, + err := p.setBucketQuota(&additionalConfigSpec{ + bucketMaxSize: aws.Int64(5), }) assert.NoError(t, err) - assert.Len(t, getSeen[userPath], 1) - assert.Equal(t, numberOfCallsWithValue("uid=bob", getSeen[userPath]), 1) assert.Len(t, getSeen[bucketPath], 1) assert.Equal(t, numberOfCallsWithValue("bucket=bob", getSeen[bucketPath]), 1) - assert.Len(t, putSeen[userPath], 0) // user quota should not be touched assert.Len(t, putSeen[bucketPath], 1) assert.Equal(t, numberOfCallsWithValue("enabled=true", putSeen[bucketPath]), 1) assert.Equal(t, numberOfCallsWithValue("max-size=5", putSeen[bucketPath]), 1) @@ -460,7 +468,6 @@ func TestProvisioner_setAdditionalSettings(t *testing.T) { t.Run("bucket quotas are enabled and need updated enabled", func(t *testing.T) { getResult := map[string]string{ - userPath: `{"enabled":false,"check_on_raw":false,"max_size":-1024,"max_size_kb":0,"max_objects":-1}`, bucketPath: `{"bucket_quota":{"enabled":true,"check_on_raw":false,"max_size":5,"max_size_kb":0,"max_objects":4}}`, } getSeen := map[string][]string{} @@ -469,24 +476,15 @@ func TestProvisioner_setAdditionalSettings(t *testing.T) { p := newProvisioner(t, &getResult, &getSeen, &putSeen) p.setBucketName("bob") - err := p.setAdditionalSettings(&apibkt.BucketOptions{ - ObjectBucketClaim: &v1alpha1.ObjectBucketClaim{ - Spec: v1alpha1.ObjectBucketClaimSpec{ - AdditionalConfig: map[string]string{ - "bucketMaxObjects": "14", - "bucketMaxSize": "15", - }, - }, - }, + err := p.setBucketQuota(&additionalConfigSpec{ + bucketMaxObjects: aws.Int64(14), + bucketMaxSize: aws.Int64(15), }) assert.NoError(t, err) - assert.Len(t, getSeen[userPath], 1) - assert.Equal(t, numberOfCallsWithValue("uid=bob", getSeen[userPath]), 1) assert.Len(t, getSeen[bucketPath], 1) assert.Equal(t, numberOfCallsWithValue("bucket=bob", getSeen[bucketPath]), 1) - assert.Len(t, putSeen[userPath], 0) // user quota should not be touched assert.Len(t, putSeen[bucketPath], 1) assert.Equal(t, numberOfCallsWithValue("enabled=true", putSeen[bucketPath]), 1) assert.Equal(t, numberOfCallsWithValue("max-objects=14", putSeen[bucketPath]), 1) diff --git a/pkg/operator/ceph/object/bucket/util.go b/pkg/operator/ceph/object/bucket/util.go index de1936d26..22a0e04db 100644 --- a/pkg/operator/ceph/object/bucket/util.go +++ b/pkg/operator/ceph/object/bucket/util.go @@ -120,6 +120,11 @@ func additionalConfigSpecFromMap(config map[string]string) (*additionalConfigSpe } } + if _, ok := config["bucketPolicy"]; ok { + policy := config["bucketPolicy"] + spec.bucketPolicy = &policy + } + return &spec, nil }