Skip to content

Commit 7972422

Browse files
committed
rgw/admin : add account support in go-ceph
Signed-off-by: Jiffin Tony Thottan <[email protected]>
1 parent 18f6f20 commit 7972422

File tree

7 files changed

+221
-6
lines changed

7 files changed

+221
-6
lines changed

docs/api-status.json

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2123,7 +2123,32 @@
21232123
]
21242124
},
21252125
"rgw/admin": {
2126-
"preview_api": [],
2126+
"preview_api": [
2127+
{
2128+
"name": "API.CreateAccount",
2129+
"comment": "CreateAccount will create a new RGW account\n",
2130+
"added_in_version": "$NEXT_RELEASE",
2131+
"expected_stable_version": "$NEXT_RELEASE_STABLE"
2132+
},
2133+
{
2134+
"name": "API.GetAccount",
2135+
"comment": "GetAccount will return the RGW account details\n",
2136+
"added_in_version": "$NEXT_RELEASE",
2137+
"expected_stable_version": "$NEXT_RELEASE_STABLE"
2138+
},
2139+
{
2140+
"name": "API.DeleteAccount",
2141+
"comment": "DeleteAccount will delete the RGW account\n",
2142+
"added_in_version": "$NEXT_RELEASE",
2143+
"expected_stable_version": "$NEXT_RELEASE_STABLE"
2144+
},
2145+
{
2146+
"name": "API.ModifyAccount",
2147+
"comment": "ModifyAccount will modify the RGW account\n",
2148+
"added_in_version": "$NEXT_RELEASE",
2149+
"expected_stable_version": "$NEXT_RELEASE_STABLE"
2150+
}
2151+
],
21272152
"stable_api": [
21282153
{
21292154
"name": "API.ListBuckets",
@@ -3011,4 +3036,4 @@
30113036
}
30123037
]
30133038
}
3014-
}
3039+
}

docs/api-status.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,14 @@ No Preview/Deprecated APIs found. All APIs are considered stable.
4343

4444
## Package: rgw/admin
4545

46-
No Preview/Deprecated APIs found. All APIs are considered stable.
46+
### Preview APIs
47+
48+
Name | Added in Version | Expected Stable Version |
49+
---- | ---------------- | ----------------------- |
50+
API.CreateAccount | $NEXT_RELEASE | $NEXT_RELEASE_STABLE |
51+
API.GetAccount | $NEXT_RELEASE | $NEXT_RELEASE_STABLE |
52+
API.DeleteAccount | $NEXT_RELEASE | $NEXT_RELEASE_STABLE |
53+
API.ModifyAccount | $NEXT_RELEASE | $NEXT_RELEASE_STABLE |
4754

4855
## Package: common/admin/manager
4956

micro-osd.sh

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,10 @@ launch_radosgw2() {
173173
radosgw-admin caps add --uid=admin --caps="info=read"
174174
}
175175

176+
launch_radosgw3() {
177+
radosgw-admin caps add --uid=admin --caps="accounts=*"
178+
}
179+
176180
selftest() {
177181
ceph --version
178182
ceph status
@@ -195,9 +199,12 @@ if [ -z "$FEATURESET" ] ; then
195199
pacific)
196200
FEATURESET="mon osd mgr mds mds2 rbd-mirror cephfs-mirror rgw selftest"
197201
;;
198-
*)
202+
quincy|reef)
199203
FEATURESET="mon osd mgr mds mds2 rbd-mirror cephfs-mirror rgw rgw2 selftest"
200204
;;
205+
*)
206+
FEATURESET="mon osd mgr mds mds2 rbd-mirror cephfs-mirror rgw rgw2 rgw3 selftest"
207+
;;
201208
esac
202209
fi
203210

@@ -213,6 +220,7 @@ for fname in ${FEATURESET} ; do
213220
cephfs-mirror) launch_cephfs_mirror ;;
214221
rgw|radosgw) launch_radosgw ;;
215222
rgw2|radosgw2) launch_radosgw2 ;;
223+
rgw3|radosgw3) launch_radosgw3 ;;
216224
selftest) selftest ;;
217225
*)
218226
echo "Invalid feature: ${fname}"

rgw/admin/account.go

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
//go:build !(pacific || quincy || reef) && ceph_preview
2+
3+
package admin
4+
5+
import (
6+
"context"
7+
"encoding/json"
8+
"fmt"
9+
"net/http"
10+
)
11+
12+
// Account represents an RGW account
13+
type Account struct {
14+
ID string `json:"id" url:"id"`
15+
Name string `json:"name" url:"name"`
16+
Email string `json:"email" url:"email"`
17+
Tenant string `json:"tenant" url:"tenant"`
18+
MaxUsers *int64 `json:"max_users" url:"max-users"`
19+
MaxRoles *int64 `json:"max_roles" url:"max-roles"`
20+
MaxGroups *int64 `json:"max_groups" url:"max-groups"`
21+
MaxAccessKeys *int64 `json:"max_access_keys" url:"max-access-keys"`
22+
MaxBuckets *int64 `json:"max_buckets" url:"max-buckets"`
23+
Quota QuotaSpec `json:"quota"`
24+
BucketQuota QuotaSpec `json:"bucket_quota"`
25+
}
26+
27+
// CreateAccount will create a new RGW account
28+
// https://docs.ceph.com/en/latest/radosgw/adminops/#create-account
29+
func (api *API) CreateAccount(ctx context.Context, account Account) (Account, error) {
30+
31+
body, err := api.call(ctx, http.MethodPost, "/account", valueToURLParams(account, []string{"id", "name", "email", "tenant", "max-users", "max-roles", "max-groups", "max-access-keys", "max-buckets"}))
32+
if err != nil {
33+
return Account{}, err
34+
}
35+
36+
a := Account{}
37+
err = json.Unmarshal(body, &a)
38+
if err != nil {
39+
return Account{}, fmt.Errorf("%s. %s. %w", unmarshalError, string(body), err)
40+
}
41+
42+
return a, nil
43+
}
44+
45+
// GetAccount will return the RGW account details
46+
// https://docs.ceph.com/en/latest/radosgw/adminops/#get-account-info
47+
func (api *API) GetAccount(ctx context.Context, accountID string) (Account, error) {
48+
if accountID == "" {
49+
return Account{}, ErrInvalidArgument
50+
}
51+
52+
body, err := api.call(ctx, http.MethodGet, "/account", valueToURLParams(Account{ID: accountID}, []string{"id"}))
53+
if err != nil {
54+
return Account{}, err
55+
}
56+
57+
a := Account{}
58+
err = json.Unmarshal(body, &a)
59+
if err != nil {
60+
return Account{}, fmt.Errorf("%s. %s. %w", unmarshalError, string(body), err)
61+
}
62+
63+
return a, nil
64+
}
65+
66+
// DeleteAccount will delete the RGW account
67+
// https://docs.ceph.com/en/latest/radosgw/adminops/#remove-account
68+
func (api *API) DeleteAccount(ctx context.Context, accountID string) error {
69+
if accountID == "" {
70+
return ErrInvalidArgument
71+
}
72+
73+
_, err := api.call(ctx, http.MethodDelete, "/account", valueToURLParams(Account{ID: accountID}, []string{"id"}))
74+
if err != nil {
75+
return err
76+
}
77+
78+
return nil
79+
}
80+
81+
// ModifyAccount will modify the RGW account
82+
// https://docs.ceph.com/en/latest/radosgw/adminops/#modify-account
83+
func (api *API) ModifyAccount(ctx context.Context, account Account) (Account, error) {
84+
if account.ID == "" {
85+
return Account{}, ErrInvalidArgument
86+
}
87+
88+
body, err := api.call(ctx, http.MethodPut, "/account", valueToURLParams(account, []string{"id", "name", "email", "tenant", "max-users", "max-roles", "max-groups", "max-access-keys", "max-buckets"}))
89+
if err != nil {
90+
return Account{}, err
91+
}
92+
93+
a := Account{}
94+
err = json.Unmarshal(body, &a)
95+
if err != nil {
96+
return Account{}, fmt.Errorf("%s. %s. %w", unmarshalError, string(body), err)
97+
}
98+
99+
return a, nil
100+
}

rgw/admin/account_test.go

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
//go:build ceph_preview && !(pacific || quincy || reef)
2+
3+
package admin
4+
5+
import (
6+
"context"
7+
"net/http"
8+
"testing"
9+
10+
"github.com/ceph/go-ceph/internal/util"
11+
"github.com/stretchr/testify/assert"
12+
)
13+
14+
func (suite *RadosGWTestSuite) TestAccount() {
15+
suite.SetupConnection()
16+
co, err := New(suite.endpoint, suite.accessKey, suite.secretKey, newDebugHTTPClient(http.DefaultClient))
17+
assert.NoError(suite.T(), err)
18+
19+
suite.T().Run("successfully create account", func(_ *testing.T) {
20+
account, err := co.CreateAccount(context.Background(), Account{ID: "RGW12345678901234567", Name: "test-account"})
21+
assert.NoError(suite.T(), err)
22+
assert.Equal(suite.T(), "RGW12345678901234567", account.ID)
23+
assert.Equal(suite.T(), "test-account", account.Name)
24+
})
25+
26+
suite.T().Run("try to create account that already exists", func(_ *testing.T) {
27+
_, err := co.CreateAccount(context.Background(), Account{ID: "RGW12345678901234567", Name: "test-account"})
28+
assert.ErrorIs(suite.T(), err, ErrAccountAlreadyExists)
29+
})
30+
31+
suite.T().Run("fail to get account since no ID provided", func(_ *testing.T) {
32+
_, err := co.GetAccount(context.Background(), "")
33+
assert.ErrorIs(suite.T(), err, ErrInvalidArgument)
34+
})
35+
36+
suite.T().Run("successfully get account", func(t *testing.T) {
37+
if util.CurrentCephVersion() <= util.CephTentacle {
38+
t.Skipf("GetAccount is not yet supported on %s", util.CurrentCephVersionString())
39+
}
40+
account, err := co.GetAccount(context.Background(), "RGW12345678901234567")
41+
assert.NoError(t, err)
42+
assert.Equal(t, "RGW12345678901234567", account.ID)
43+
assert.Equal(t, "test-account", account.Name)
44+
})
45+
46+
suite.T().Run("fail to modify account since no ID provided", func(_ *testing.T) {
47+
_, err := co.ModifyAccount(context.Background(), Account{Name: "modified-account"})
48+
assert.ErrorIs(suite.T(), err, ErrInvalidArgument)
49+
})
50+
51+
suite.T().Run("successfully modify account", func(_ *testing.T) {
52+
account, err := co.ModifyAccount(context.Background(), Account{ID: "RGW12345678901234567", Name: "modified-account"})
53+
assert.NoError(suite.T(), err)
54+
assert.Equal(suite.T(), "RGW12345678901234567", account.ID)
55+
assert.Equal(suite.T(), "modified-account", account.Name)
56+
})
57+
58+
suite.T().Run("fail to delete account since no ID provided", func(_ *testing.T) {
59+
err := co.DeleteAccount(context.Background(), "")
60+
assert.ErrorIs(suite.T(), err, ErrInvalidArgument)
61+
})
62+
63+
suite.T().Run("successfully delete account", func(t *testing.T) {
64+
if util.CurrentCephVersion() <= util.CephTentacle {
65+
t.Skipf("DeleteAccount is not yet supported on %s", util.CurrentCephVersionString())
66+
}
67+
err := co.DeleteAccount(context.Background(), "RGW12345678901234567")
68+
assert.NoError(t, err)
69+
})
70+
}

rgw/admin/errors.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,9 @@ const (
8585
// ErrSignatureDoesNotMatch - the query to the API has invalid parameters
8686
ErrSignatureDoesNotMatch errorReason = "SignatureDoesNotMatch"
8787

88+
// ErrAccountAlreadyExists indicates that the account already exists
89+
ErrAccountAlreadyExists errorReason = "AccountAlreadyExists"
90+
8891
unmarshalError = "failed to unmarshal radosgw http response"
8992
)
9093

rgw/admin/user.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ type User struct {
3434
GenerateStat *bool `url:"stats"`
3535
Stat UserStat `json:"stats"`
3636
UserCaps string `url:"user-caps"`
37+
AccountID string `json:"account_id" url:"account-id"`
38+
AccountRoot *bool `url:"account-root"`
3739
}
3840

3941
// SubuserSpec represents a subusers of a ceph-rgw user
@@ -160,7 +162,7 @@ func (api *API) CreateUser(ctx context.Context, user User) (User, error) {
160162
}
161163

162164
// valid parameters not supported by go-ceph: system, exclusive, placement-tags
163-
body, err := api.call(ctx, http.MethodPut, "/user", valueToURLParams(user, []string{"uid", "display-name", "default-placement", "email", "key-type", "access-key", "secret-key", "user-caps", "tenant", "generate-key", "max-buckets", "suspended", "op-mask"}))
165+
body, err := api.call(ctx, http.MethodPut, "/user", valueToURLParams(user, []string{"uid", "display-name", "default-placement", "email", "key-type", "access-key", "secret-key", "user-caps", "tenant", "generate-key", "max-buckets", "suspended", "op-mask", "account-id", "account-root"}))
164166
if err != nil {
165167
return User{}, err
166168
}
@@ -196,7 +198,7 @@ func (api *API) ModifyUser(ctx context.Context, user User) (User, error) {
196198
}
197199

198200
// valid parameters not supported by go-ceph: system, placement-tags
199-
body, err := api.call(ctx, http.MethodPost, "/user", valueToURLParams(user, []string{"uid", "display-name", "default-placement", "email", "generate-key", "access-key", "secret-key", "key-type", "max-buckets", "suspended", "op-mask"}))
201+
body, err := api.call(ctx, http.MethodPost, "/user", valueToURLParams(user, []string{"uid", "display-name", "default-placement", "email", "generate-key", "access-key", "secret-key", "key-type", "max-buckets", "suspended", "op-mask", "account-id", "account-root"}))
200202
if err != nil {
201203
return User{}, err
202204
}

0 commit comments

Comments
 (0)