diff --git a/go.mod b/go.mod index 94b76473f..65881bb00 100644 --- a/go.mod +++ b/go.mod @@ -15,7 +15,7 @@ require ( github.com/google/uuid v1.1.2 github.com/hashicorp/vault/api v1.0.5-0.20200902155336-f9d5ce5a171a github.com/k8snetworkplumbingwg/network-attachment-definition-client v1.1.0 - github.com/kube-object-storage/lib-bucket-provisioner v0.0.0-20210311161930-4bea5edaff58 + github.com/kube-object-storage/lib-bucket-provisioner v0.0.0-20210818162813-3eee31c01875 github.com/libopenstorage/secrets v0.0.0-20210709082113-dde442ea20ec github.com/openshift/cluster-api v0.0.0-20191129101638-b09907ac6668 github.com/openshift/machine-api-operator v0.2.1-0.20190903202259-474e14e4965a diff --git a/go.sum b/go.sum index 5acd43305..fef8d50ba 100644 --- a/go.sum +++ b/go.sum @@ -719,8 +719,8 @@ github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/kube-object-storage/lib-bucket-provisioner v0.0.0-20210311161930-4bea5edaff58 h1:O9m6tfyhjr3F3yKGsgGMp1+seFyTRovmGMmbcNp6GSo= -github.com/kube-object-storage/lib-bucket-provisioner v0.0.0-20210311161930-4bea5edaff58/go.mod h1:XpQ9HGG9uF5aJCBP+s6w5kSiyTIVSqCV8+XAE4qms5E= +github.com/kube-object-storage/lib-bucket-provisioner v0.0.0-20210818162813-3eee31c01875 h1:jX3VXgmNOye8XYKjwcTVXcBYcPv3jj657fwX8DN/HiM= +github.com/kube-object-storage/lib-bucket-provisioner v0.0.0-20210818162813-3eee31c01875/go.mod h1:XpQ9HGG9uF5aJCBP+s6w5kSiyTIVSqCV8+XAE4qms5E= github.com/kubernetes-csi/csi-lib-utils v0.9.1 h1:sGq6ifVujfMSkfTsMZip44Ttv8SDXvsBlFk9GdYl/b8= github.com/kubernetes-csi/csi-lib-utils v0.9.1/go.mod h1:8E2jVUX9j3QgspwHXa6LwyN7IHQDjW9jX3kwoWnSC+M= github.com/kubernetes-csi/external-snapshotter/client/v4 v4.0.0/go.mod h1:YBCo4DoEeDndqvAn6eeu0vWM7QdXmHEeI9cFWplmBys= diff --git a/pkg/operator/ceph/object/bucket/provisioner.go b/pkg/operator/ceph/object/bucket/provisioner.go index 29bed4651..81eaeba4a 100644 --- a/pkg/operator/ceph/object/bucket/provisioner.go +++ b/pkg/operator/ceph/object/bucket/provisioner.go @@ -554,8 +554,8 @@ func (p *Provisioner) deleteOBCResourceLogError(bucketname string) { // Check for additional options mentioned in OBC and set them accordingly func (p Provisioner) setAdditionalSettings(options *apibkt.BucketOptions) error { quotaEnabled := true - maxObjects := MaxObjectQuota(options) - maxSize := MaxSizeQuota(options) + maxObjects := MaxObjectQuota(options.ObjectBucketClaim.Spec.AdditionalConfig) + maxSize := MaxSizeQuota(options.ObjectBucketClaim.Spec.AdditionalConfig) if maxObjects == "" && maxSize == "" { return nil } @@ -665,3 +665,69 @@ func (p *Provisioner) setAdminOpsAPIClient() error { return nil } +func (p Provisioner) updateAdditionalSettings(ob *bktv1alpha1.ObjectBucket) error { + var maxObjectsInt64 int64 + var maxSizeInt64 int64 + var err error + var quotaEnabled bool + maxObjects := MaxObjectQuota(ob.Spec.Endpoint.AdditionalConfigData) + maxSize := MaxSizeQuota(ob.Spec.Endpoint.AdditionalConfigData) + if maxObjects != "" { + maxObjectsInt, err := strconv.Atoi(maxObjects) + if err != nil { + return errors.Wrap(err, "failed to convert maxObjects to integer") + } + maxObjectsInt64 = int64(maxObjectsInt) + } + if maxSize != "" { + maxSizeInt64, err = maxSizeToInt64(maxSize) + if err != nil { + return errors.Wrapf(err, "failed to parse maxSize quota for user %q", p.cephUserName) + } + } + objectUser, err := p.adminOpsClient.GetUser(context.TODO(), admin.User{ID: ob.Spec.Connection.AdditionalState[cephUser]}) + if err != nil { + return errors.Wrapf(err, "failed to fetch user %q", p.cephUserName) + } + if *objectUser.UserQuota.Enabled && + (maxObjects == "" || maxObjectsInt64 < 0) && + (maxSize == "" || maxSizeInt64 < 0) { + quotaEnabled = false + err = p.adminOpsClient.SetUserQuota(context.TODO(), admin.QuotaSpec{UID: p.cephUserName, Enabled: "aEnabled}) + if err != nil { + return errors.Wrapf(err, "failed to disable quota to user %q", p.cephUserName) + } + return nil + } + + quotaEnabled = true + quotaSpec := admin.QuotaSpec{UID: p.cephUserName, Enabled: "aEnabled} + + //MaxObject is modified + if maxObjects != "" && (maxObjectsInt64 != *objectUser.UserQuota.MaxObjects) { + quotaSpec.MaxObjects = &maxObjectsInt64 + } + + //MaxSize is modified + if maxSize != "" && (maxSizeInt64 != *objectUser.UserQuota.MaxSize) { + quotaSpec.MaxSize = &maxSizeInt64 + } + err = p.adminOpsClient.SetUserQuota(context.TODO(), quotaSpec) + if err != nil { + return errors.Wrapf(err, "failed to update quota to user %q", p.cephUserName) + } + + return nil +} + +// Update is sent when only there is modification to AdditionalConfig field in OBC +func (p Provisioner) Update(ob *bktv1alpha1.ObjectBucket) error { + logger.Debugf("Update event for OB: %+v", ob) + + err := p.initializeDeleteOrRevoke(ob) + if err != nil { + return err + } + + return p.updateAdditionalSettings(ob) +} diff --git a/pkg/operator/ceph/object/bucket/util.go b/pkg/operator/ceph/object/bucket/util.go index 71faa16cf..9fd4ed6ef 100644 --- a/pkg/operator/ceph/object/bucket/util.go +++ b/pkg/operator/ceph/object/bucket/util.go @@ -23,7 +23,6 @@ import ( "github.com/coreos/pkg/capnslog" bktv1alpha1 "github.com/kube-object-storage/lib-bucket-provisioner/pkg/apis/objectbucket.io/v1alpha1" "github.com/kube-object-storage/lib-bucket-provisioner/pkg/provisioner" - apibkt "github.com/kube-object-storage/lib-bucket-provisioner/pkg/provisioner/api" "github.com/pkg/errors" storagev1 "k8s.io/api/storage/v1" kerrors "k8s.io/apimachinery/pkg/api/errors" @@ -117,10 +116,10 @@ func randomString(n int) string { return string(b) } -func MaxObjectQuota(options *apibkt.BucketOptions) string { - return options.ObjectBucketClaim.Spec.AdditionalConfig["maxObjects"] +func MaxObjectQuota(AdditionalConfig map[string]string) string { + return AdditionalConfig["maxObjects"] } -func MaxSizeQuota(options *apibkt.BucketOptions) string { - return options.ObjectBucketClaim.Spec.AdditionalConfig["maxSize"] +func MaxSizeQuota(AdditionalConfig map[string]string) string { + return AdditionalConfig["maxSize"] } diff --git a/tests/framework/clients/bucket.go b/tests/framework/clients/bucket.go index 70e73f8b5..8f3edeb86 100644 --- a/tests/framework/clients/bucket.go +++ b/tests/framework/clients/bucket.go @@ -53,6 +53,10 @@ func (b *BucketOperation) DeleteObc(obcName string, storageClassName string, buc return b.k8sh.ResourceOperation("delete", b.manifests.GetOBC(obcName, storageClassName, bucketName, maxObject, createBucket)) } +func (b *BucketOperation) UpdateObc(obcName string, storageClassName string, bucketName string, maxObject string, createBucket bool) error { + return b.k8sh.ResourceOperation("apply", b.manifests.GetOBC(obcName, storageClassName, bucketName, maxObject, createBucket)) +} + // CheckOBC, returns true if the obc, secret and configmap are all in the "check" state, // and returns false if any of these resources are not in the "check" state. // Check state values: @@ -123,3 +127,10 @@ func (b *BucketOperation) GetSecretKey(obcName string) (string, error) { return string(decode), nil } + +// Checks whether MaxObject is updated for ob +func (b *BucketOperation) CheckOBMaxObject(obcName, maxobject string) bool { + obName, _ := b.k8sh.GetResource("obc", obcName, "--output", "jsonpath={.spec.objectBucketName}") + fetchMaxObject, _ := b.k8sh.GetResource("ob", obName, "--output", "jsonpath={.spec.endpoint.additionalConfig.maxObjects}") + return maxobject == fetchMaxObject +} diff --git a/tests/integration/ceph_base_object_test.go b/tests/integration/ceph_base_object_test.go index 6cc912f90..cb54a0165 100644 --- a/tests/integration/ceph_base_object_test.go +++ b/tests/integration/ceph_base_object_test.go @@ -48,10 +48,12 @@ var ( ObjectKey1 = "rookObj1" ObjectKey2 = "rookObj2" ObjectKey3 = "rookObj3" + ObjectKey4 = "rookObj4" contentType = "plain/text" obcName = "smoke-delete-bucket" region = "us-east-1" maxObject = "2" + newMaxObject = "3" bucketStorageClassName = "rook-smoke-delete-bucket" ) @@ -280,11 +282,27 @@ func testObjectStoreOperations(s suite.Suite, helper *clients.TestClient, k8sh * assert.Error(s.T(), poErr) }) + t.Run("test update quota on OBC bucket", func(t *testing.T) { + poErr := helper.BucketClient.UpdateObc(obcName, bucketStorageClassName, bucketname, newMaxObject, true) + assert.Nil(s.T(), poErr) + updated := utils.Retry(5, 2*time.Second, "OBC is updated", func() bool { + return helper.BucketClient.CheckOBMaxObject(obcName, newMaxObject) + }) + assert.True(s.T(), updated) + logger.Infof("Testing the updated object limit") + _, poErr = s3client.PutObjectInBucket(bucketname, ObjBody, ObjectKey3, contentType) + assert.NoError(s.T(), poErr) + _, poErr = s3client.PutObjectInBucket(bucketname, ObjBody, ObjectKey4, contentType) + assert.Error(s.T(), poErr) + }) + t.Run("delete objects on OBC bucket", func(t *testing.T) { _, delobjErr := s3client.DeleteObjectInBucket(bucketname, ObjectKey1) assert.Nil(s.T(), delobjErr) _, delobjErr = s3client.DeleteObjectInBucket(bucketname, ObjectKey2) assert.Nil(s.T(), delobjErr) + _, delobjErr = s3client.DeleteObjectInBucket(bucketname, ObjectKey3) + assert.Nil(s.T(), delobjErr) logger.Info("Objects deleted on bucket successfully") }) })