Skip to content

Commit 02fcaa3

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

File tree

5 files changed

+162
-3
lines changed

5 files changed

+162
-3
lines changed

micro-osd.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ launch_radosgw() {
166166
# not going to try to make shellcheck happy with this line at this time
167167
# shellcheck disable=SC2016
168168
timeout 60 sh -c 'until [ $(ceph -s | grep -c "rgw:") -eq 1 ]; do echo "waiting for rgw to show up" && sleep 1; done'
169-
radosgw-admin user create --uid admin --display-name "Admin User" --caps "buckets=*;users=*;usage=read;metadata=read" --access-key="$S3_ACCESS_KEY" --secret-key="$S3_SECRET_KEY"
169+
radosgw-admin user create --uid admin --display-name "Admin User" --caps "buckets=*;users=*;usage=read;metadata=read;accounts=*" --access-key="$S3_ACCESS_KEY" --secret-key="$S3_SECRET_KEY"
170170
}
171171

172172
launch_radosgw2() {

rgw/admin/account.go

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
//go:build ceph_preview && !squid
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+
func (api *API) CreateAccount(ctx context.Context, account Account) (Account, error) {
29+
30+
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"}))
31+
if err != nil {
32+
return Account{}, err
33+
}
34+
35+
a := Account{}
36+
err = json.Unmarshal(body, &a)
37+
if err != nil {
38+
return Account{}, fmt.Errorf("%s. %s. %w", unmarshalError, string(body), err)
39+
}
40+
41+
return a, nil
42+
}
43+
44+
// GetAccount will return the RGW account details
45+
func (api *API) GetAccount(ctx context.Context, accountID string) (Account, error) {
46+
if accountID == "" {
47+
return Account{}, ErrInvalidArgument
48+
}
49+
50+
body, err := api.call(ctx, http.MethodGet, "/account", valueToURLParams(Account{ID: accountID}, []string{"id"}))
51+
if err != nil {
52+
return Account{}, err
53+
}
54+
55+
a := Account{}
56+
err = json.Unmarshal(body, &a)
57+
if err != nil {
58+
return Account{}, fmt.Errorf("%s. %s. %w", unmarshalError, string(body), err)
59+
}
60+
61+
return a, nil
62+
}
63+
64+
// DeleteAccount will delete the RGW account
65+
func (api *API) DeleteAccount(ctx context.Context, accountID string) error {
66+
if accountID == "" {
67+
return ErrInvalidArgument
68+
}
69+
70+
_, err := api.call(ctx, http.MethodDelete, "/account", valueToURLParams(Account{ID: accountID}, []string{"id"}))
71+
if err != nil {
72+
return err
73+
}
74+
75+
return nil
76+
}
77+
78+
// ModifyAccount will modify the RGW account
79+
func (api *API) ModifyAccount(ctx context.Context, account Account) (Account, error) {
80+
if account.ID == "" {
81+
return Account{}, ErrInvalidArgument
82+
}
83+
84+
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"}))
85+
if err != nil {
86+
return Account{}, err
87+
}
88+
89+
a := Account{}
90+
err = json.Unmarshal(body, &a)
91+
if err != nil {
92+
return Account{}, fmt.Errorf("%s. %s. %w", unmarshalError, string(body), err)
93+
}
94+
95+
return a, nil
96+
}

rgw/admin/account_test.go

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
//go:build ceph_preview && !squid
2+
3+
package admin
4+
5+
import (
6+
"context"
7+
"net/http"
8+
"testing"
9+
10+
"github.com/stretchr/testify/assert"
11+
)
12+
13+
func (suite *RadosGWTestSuite) TestAccount() {
14+
suite.SetupConnection()
15+
co, err := New(suite.endpoint, suite.accessKey, suite.secretKey, newDebugHTTPClient(http.DefaultClient))
16+
assert.NoError(suite.T(), err)
17+
18+
suite.T().Run("successfully create account", func(_ *testing.T) {
19+
account, err := co.CreateAccount(context.Background(), Account{ID: "RGW12345678901234567", Name: "test-account"})
20+
assert.NoError(suite.T(), err)
21+
assert.Equal(suite.T(), "RGW12345678901234567", account.ID)
22+
assert.Equal(suite.T(), "test-account", account.Name)
23+
})
24+
25+
suite.T().Run("fail to get account since no ID provided", func(_ *testing.T) {
26+
_, err := co.GetAccount(context.Background(), "")
27+
assert.ErrorIs(suite.T(), err, ErrInvalidArgument)
28+
})
29+
30+
suite.T().Run("successfully get account", func(_ *testing.T) {
31+
account, err := co.GetAccount(context.Background(), "RGW12345678901234567")
32+
assert.NoError(suite.T(), err)
33+
assert.Equal(suite.T(), "RGW12345678901234567", account.ID)
34+
assert.Equal(suite.T(), "test-account", account.Name)
35+
})
36+
37+
suite.T().Run("fail to modify account since no ID provided", func(_ *testing.T) {
38+
_, err := co.ModifyAccount(context.Background(), Account{Name: "modified-account"})
39+
assert.ErrorIs(suite.T(), err, ErrInvalidArgument)
40+
})
41+
42+
suite.T().Run("successfully modify account", func(_ *testing.T) {
43+
account, err := co.ModifyAccount(context.Background(), Account{ID: "RGW12345678901234567", Name: "modified-account"})
44+
assert.NoError(suite.T(), err)
45+
assert.Equal(suite.T(), "RGW12345678901234567", account.ID)
46+
assert.Equal(suite.T(), "modified-account", account.Name)
47+
})
48+
49+
suite.T().Run("fail to delete account since no ID provided", func(_ *testing.T) {
50+
err := co.DeleteAccount(context.Background(), "")
51+
assert.ErrorIs(suite.T(), err, ErrInvalidArgument)
52+
})
53+
54+
suite.T().Run("successfully delete account", func(_ *testing.T) {
55+
err := co.DeleteAccount(context.Background(), "RGW12345678901234567")
56+
assert.NoError(suite.T(), err)
57+
})
58+
}

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)