Skip to content

Commit ca9b4ef

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

File tree

4 files changed

+188
-1
lines changed

4 files changed

+188
-1
lines changed

rgw/admin/account.go

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

rgw/admin/account_test.go

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
package admin
2+
3+
import (
4+
"bytes"
5+
"context"
6+
"encoding/json"
7+
"errors"
8+
"fmt"
9+
"io"
10+
"net/http"
11+
"os"
12+
"slices"
13+
"testing"
14+
15+
"github.com/stretchr/testify/assert"
16+
)
17+
18+
func (suite *RadosGWTestSuite) TestAccount() {
19+
suite.SetupConnection()
20+
co, err := New(suite.endpoint, suite.accessKey, suite.secretKey, newDebugHTTPClient(http.DefaultClient))
21+
assert.NoError(suite.T(), err)
22+
23+
suite.T().Run("fail to create account since no ID provided", func(_ *testing.T) {
24+
_, err := co.CreateAccount(context.Background(), Account{Name: "test-account"})
25+
assert.ErrorIs(suite.T(), err, ErrInvalidArgument)
26+
})
27+
28+
suite.T().Run("fail to create account since no Name provided", func(_ *testing.T) {
29+
_, err := co.CreateAccount(context.Background(), Account{ID: "RGW12345678901234567"})
30+
assert.ErrorIs(suite.T(), err, ErrInvalidArgument)
31+
})
32+
33+
suite.T().Run("fail to create account since invalid ID provided", func(_ *testing.T) {
34+
_, err := co.CreateAccount(context.Background(), Account{ID: "INVALID_ID_12345", Name: "test-account"})
35+
assert.ErrorIs(suite.T(), err, ErrInvalidArgument)
36+
})
37+
38+
suite.T().Run("successfully create account", func(_ *testing.T) {
39+
account, err := co.CreateAccount(context.Background(), Account{ID: "RGW12345678901234567", Name: "test-account"})
40+
assert.NoError(suite.T(), err)
41+
assert.Equal(suite.T(), "RGW12345678901234567", account.ID)
42+
assert.Equal(suite.T(), "test-account", account.Name)
43+
})
44+
45+
suite.T().Run("fail to get account since no ID provided", func(_ *testing.T) {
46+
_, err := co.GetAccount(context.Background(), "")
47+
assert.ErrorIs(suite.T(), err, ErrInvalidArgument)
48+
})
49+
50+
suite.T().Run("successfully get account", func(_ *testing.T) {
51+
account, err := co.GetAccount(context.Background(), "RGW12345678901234567")
52+
assert.NoError(suite.T(), err)
53+
assert.Equal(suite.T(), "RGW12345678901234567", account.ID)
54+
assert.Equal(suite.T(), "test-account", account.Name)
55+
})
56+
57+
suite.T().Run("fail to modify account since no ID provided", func(_ *testing.T) {
58+
_, err := co.ModifyAccount(context.Background(), Account{Name: "modified-account"})
59+
assert.ErrorIs(suite.T(), err, ErrInvalidArgument)
60+
})
61+
62+
suite.T().Run("successfully modify account", func(_ *testing.T) {
63+
account, err := co.ModifyAccount(context.Background(), Account{ID: "RGW12345678901234567", Name: "modified-account"})
64+
assert.NoError(suite.T(), err)
65+
assert.Equal(suite.T(), "RGW12345678901234567", account.ID)
66+
assert.Equal(suite.T(), "modified-account", account.Name)
67+
})
68+
69+
suite.T().Run("fail to delete account since no ID provided", func(_ *testing.T) {
70+
err := co.DeleteAccount(context.Background(), "")
71+
assert.ErrorIs(suite.T(), err, ErrInvalidArgument)
72+
})
73+
74+
suite.T().Run("successfully delete account", func(_ *testing.T) {
75+
err := co.DeleteAccount(context.Background(), "RGW12345678901234567")
76+
assert.NoError(suite.T(), err)
77+
})
78+
}

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+
// ErrAccountExists 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: 3 additions & 1 deletion
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+
Account string `json:"account" url:"account-id"`
38+
AccountRoot *bool `json:"account_root" 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
}

0 commit comments

Comments
 (0)