From 98ec2e31d6d460da06bb646172e6d9d7db5384c5 Mon Sep 17 00:00:00 2001 From: mergify-bot Date: Mon, 14 Mar 2022 19:39:42 +0000 Subject: [PATCH] rgw/admin: add suport for link/unlink bucket (#646) rgw/admin: add suport for link/unlink bucket Signed-off-by: wanggangfeng * rgw/admin: add comment and update-api Signed-off-by: wanggangfeng * rgw/admin: add ceph_preview Signed-off-by: wanggangfeng * Merge branch 'master' into feature-645 * Merge branch 'master' into feature-645 * Merge branch 'master' into feature-645 * Merge branch 'master' into feature-645 * rgw/admin: bucketLink 1 update api doc 2 remove optional check 3 update test Signed-off-by: wanggangfeng * Update rgw/admin/link_test.go Co-authored-by: Jiffin Tony Thottan * rgw/admin: improve parameters name Signed-off-by: wanggangfeng * Merge branch 'master' into feature-645 * Merge branch 'master' into feature-645 * Merge branch 'master' into feature-645 --- docs/api-status.json | 15 +++++++- docs/api-status.md | 2 ++ rgw/admin/errors.go | 2 ++ rgw/admin/link.go | 44 +++++++++++++++++++++++ rgw/admin/link_test.go | 82 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 144 insertions(+), 1 deletion(-) create mode 100644 rgw/admin/link.go create mode 100644 rgw/admin/link_test.go diff --git a/docs/api-status.json b/docs/api-status.json index 4ee6641..6a12eb8 100644 --- a/docs/api-status.json +++ b/docs/api-status.json @@ -1764,7 +1764,20 @@ ] }, "rgw/admin": { - "preview_api": [], + "preview_api": [ + { + "name": "API.UnlinkBucket", + "comment": "UnlinkBucket unlink a bucket from a specified user\nPrimarily useful for changing bucket ownership.\n PREVIEW\n", + "added_in_version": "v0.15.0", + "expected_stable_version": "v0.17.0" + }, + { + "name": "API.LinkBucket", + "comment": "LinkBucket will link a bucket to a specified user\nunlinking the bucket from any previous user\n PREVIEW\n", + "added_in_version": "v0.15.0", + "expected_stable_version": "v0.17.0" + } + ], "stable_api": [ { "name": "API.ListBuckets", diff --git a/docs/api-status.md b/docs/api-status.md index 63b9873..c72ceb6 100644 --- a/docs/api-status.md +++ b/docs/api-status.md @@ -57,6 +57,8 @@ Snapshot.Set | v0.10.0 | | Name | Added in Version | Expected Stable Version | ---- | ---------------- | ----------------------- | +API.UnlinkBucket | v0.15.0 | v0.17.0 | +API.LinkBucket | v0.15.0 | v0.17.0 | ## Package: common/admin/manager diff --git a/rgw/admin/errors.go b/rgw/admin/errors.go index c0b41ed..ce9161a 100644 --- a/rgw/admin/errors.go +++ b/rgw/admin/errors.go @@ -90,6 +90,8 @@ var ( errMissingUserAccessKey = errors.New("missing user access key") errMissingUserDisplayName = errors.New("missing user display name") errMissingUserCap = errors.New("missing user capabilities") + errMissingBucketID = errors.New("missing bucket ID") + errMissingBucket = errors.New("missing bucket") ) // errorReason is the reason of the error diff --git a/rgw/admin/link.go b/rgw/admin/link.go new file mode 100644 index 0000000..c88f14e --- /dev/null +++ b/rgw/admin/link.go @@ -0,0 +1,44 @@ +//go:build ceph_preview +// +build ceph_preview + +package admin + +import ( + "context" + "net/http" +) + +// BucketLinkInput the bucket link/unlink input parameters +type BucketLinkInput struct { + Bucket string `url:"bucket" json:"bucket"` + BucketID string `url:"bucket-id" json:"bucket_id"` + UID string `url:"uid" json:"uid"` +} + +// UnlinkBucket unlink a bucket from a specified user +// Primarily useful for changing bucket ownership. +// PREVIEW +func (api *API) UnlinkBucket(ctx context.Context, link BucketLinkInput) error { + if link.UID == "" { + return errMissingUserID + } + if link.Bucket == "" { + return errMissingBucket + } + _, err := api.call(ctx, http.MethodPost, "/bucket", valueToURLParams(link)) + return err +} + +// LinkBucket will link a bucket to a specified user +// unlinking the bucket from any previous user +// PREVIEW +func (api *API) LinkBucket(ctx context.Context, link BucketLinkInput) error { + if link.UID == "" { + return errMissingUserID + } + if link.Bucket == "" { + return errMissingBucket + } + _, err := api.call(ctx, http.MethodPut, "/bucket", valueToURLParams(link)) + return err +} diff --git a/rgw/admin/link_test.go b/rgw/admin/link_test.go new file mode 100644 index 0000000..fec4967 --- /dev/null +++ b/rgw/admin/link_test.go @@ -0,0 +1,82 @@ +//go:build ceph_preview +// +build ceph_preview + +package admin + +import ( + "context" + "net/http" + "testing" + + "github.com/stretchr/testify/assert" +) + +func (suite *RadosGWTestSuite) TestLink() { + suite.SetupConnection() + co, err := New(suite.endpoint, suite.accessKey, suite.secretKey, newDebugHTTPClient(http.DefaultClient)) + assert.NoError(suite.T(), err) + + suite.T().Run("create test user1", func(t *testing.T) { + user, err := co.CreateUser(context.Background(), User{ID: "test-user1", DisplayName: "test-user1", Email: "test1@example.com"}) + assert.NoError(suite.T(), err) + assert.Equal(suite.T(), "test-user1", user.ID) + assert.Zero(suite.T(), len(user.Caps)) + }) + + suite.T().Run("create test bucket", func(t *testing.T) { + s3, err := newS3Agent(suite.accessKey, suite.secretKey, suite.endpoint, true) + assert.NoError(t, err) + + err = s3.createBucket(suite.bucketTestName) + assert.NoError(t, err) + }) + + suite.T().Run("create test user2", func(t *testing.T) { + user, err := co.CreateUser(context.Background(), User{ID: "test-user2", DisplayName: "test-user2", Email: "test2@example.com"}) + assert.NoError(suite.T(), err) + assert.Equal(suite.T(), "test-user2", user.ID) + assert.Zero(suite.T(), len(user.Caps)) + }) + + suite.T().Run("link test-user2", func(t *testing.T) { + bucket, err := co.GetBucketInfo(context.Background(), Bucket{Bucket: suite.bucketTestName}) + assert.NoError(t, err) + + err = co.LinkBucket(context.Background(), BucketLinkInput{ + Bucket: suite.bucketTestName, + BucketID: bucket.ID, + UID: "test-user2", + }) + assert.NoError(t, err) + + bucket, err = co.GetBucketInfo(context.Background(), Bucket{Bucket: suite.bucketTestName}) + assert.NoError(t, err) + assert.Equal(t, bucket.Owner, "test-user2") + }) + + suite.T().Run("unlink test-user2", func(t *testing.T) { + bucket, err := co.GetBucketInfo(context.Background(), Bucket{Bucket: suite.bucketTestName}) + assert.NoError(t, err) + + err = co.UnlinkBucket(context.Background(), BucketLinkInput{ + Bucket: suite.bucketTestName, + UID: bucket.Owner, + }) + assert.NoError(t, err) + }) + + suite.T().Run("remove bucket", func(t *testing.T) { + err := co.RemoveBucket(context.Background(), Bucket{Bucket: suite.bucketTestName}) + assert.NoError(suite.T(), err) + }) + + suite.T().Run("delete test user1", func(t *testing.T) { + err := co.RemoveUser(context.Background(), User{ID: "test-user1"}) + assert.NoError(suite.T(), err) + }) + + suite.T().Run("delete test user2", func(t *testing.T) { + err := co.RemoveUser(context.Background(), User{ID: "test-user2"}) + assert.NoError(suite.T(), err) + }) +}