Skip to content

Commit

Permalink
Added S3 user management
Browse files Browse the repository at this point in the history
Signed-off-by: utkarshbhatthere <[email protected]>
  • Loading branch information
UtkarshBhatthere committed May 10, 2023
1 parent 4dcad2e commit 0feaf24
Show file tree
Hide file tree
Showing 8 changed files with 499 additions and 2 deletions.
3 changes: 1 addition & 2 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -122,8 +122,7 @@ jobs:
- name: Exercise RGW
run: |
set -eux
sudo microceph.radosgw-admin user create --uid=test --display-name=test
sudo microceph.radosgw-admin key create --uid=test --key-type=s3 --access-key fooAccessKey --secret-key fooSecretKey
sudo microceph s3 create test --access-key=fooAccessKey --secret=fooSecretKey
sudo apt-get -qq install s3cmd
echo hello-radosgw > ~/test.txt
s3cmd --host localhost --host-bucket="localhost/%(bucket)" --access_key=fooAccessKey --secret_key=fooSecretKey --no-ssl mb s3://testbucket
Expand Down
1 change: 1 addition & 0 deletions microceph/api/endpoints.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,5 @@ var Endpoints = []rest.Endpoint{
rgwServiceCmd,
configsCmd,
restartServiceCmd,
s3Cmd,
}
79 changes: 79 additions & 0 deletions microceph/api/s3.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package api

import (
"encoding/json"
"net/http"

"github.com/canonical/microceph/microceph/api/types"
"github.com/canonical/microceph/microceph/ceph"
"github.com/canonical/microcluster/rest"
"github.com/canonical/microcluster/state"
"github.com/lxc/lxd/lxd/response"
)

// /1.0/resources endpoint.
var s3Cmd = rest.Endpoint{
Path: "s3",
Get: rest.EndpointAction{Handler: cmdS3Get, ProxyTarget: true},
Put: rest.EndpointAction{Handler: cmdS3Put, ProxyTarget: true},
Delete: rest.EndpointAction{Handler: cmdS3Delete, ProxyTarget: true},
}

func cmdS3Get(s *state.State, r *http.Request) response.Response {
var err error
var req types.S3User

err = json.NewDecoder(r.Body).Decode(&req)
if err != nil {
return response.InternalError(err)
}

// If a user name is passed.
if len(req.Name) > 0 {
getOutput, err := ceph.GetS3User(req.Name)
if err != nil {
return response.SmartError(err)
}
return response.SyncResponse(true, getOutput)
} else {
listOutput, err := ceph.ListS3Users()
if err != nil {
return response.SmartError(err)
}
return response.SyncResponse(true, listOutput)
}
}

func cmdS3Put(s *state.State, r *http.Request) response.Response {
var err error
var req types.S3User

err = json.NewDecoder(r.Body).Decode(&req)
if err != nil {
return response.InternalError(err)
}

output, err := ceph.CreateS3User(req.Name, req.Key, req.Secret)
if err != nil {
return response.SmartError(err)
}

return response.SyncResponse(true, output)
}

func cmdS3Delete(s *state.State, r *http.Request) response.Response {
var err error
var req types.S3User

err = json.NewDecoder(r.Body).Decode(&req)
if err != nil {
return response.InternalError(err)
}

err = ceph.DeleteS3User(req.Name)
if err != nil {
return response.SmartError(err)
}

return response.EmptySyncResponse
}
9 changes: 9 additions & 0 deletions microceph/api/types/s3.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// Package types provides shared types and structs.
package types

// holds the name, access and secretkey required for exposing an S3 user.
type S3User struct {
Name string `json:"name" yaml:"name"`
Key string `json:"key" yaml:"key"`
Secret string `json:"secret" yaml:"secret"`
}
89 changes: 89 additions & 0 deletions microceph/ceph/rgw_s3.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package ceph

import (
"encoding/json"
"fmt"

"github.com/canonical/microceph/microceph/api/types"
"github.com/tidwall/gjson"
)

func CreateS3User(name string, accessKey string, secret string) (types.S3User, error) {
args := []string{
"user",
"create",
fmt.Sprintf("--uid=%s", name),
fmt.Sprintf("--display-name=%s", name),
}

if len(accessKey) > 0 {
args = append(args, fmt.Sprintf("--access-key=%s", accessKey))
}

if len(secret) > 0 {
args = append(args, fmt.Sprintf("--secret=%s", secret))
}

output, err := processExec.RunCommand("radosgw-admin", args...)
if err != nil {
return types.S3User{}, err
}

ret := types.S3User{
Name: gjson.Get(output, "keys.0.user").Str,
Key: gjson.Get(output, "keys.0.access_key").Str,
Secret: gjson.Get(output, "keys.0.secret_key").Str,
}
return ret, nil
}

func GetS3User(name string) (types.S3User, error) {
args := []string{
"user",
"info",
fmt.Sprintf("--uid=%s", name),
}

output, err := processExec.RunCommand("radosgw-admin", args...)
if err != nil {
return types.S3User{}, err
}

ret := types.S3User{
Name: gjson.Get(output, "keys.0.user").Str,
Key: gjson.Get(output, "keys.0.access_key").Str,
Secret: gjson.Get(output, "keys.0.secret_key").Str,
}
return ret, nil
}

func ListS3Users() ([]string, error) {
args := []string{
"user",
"list",
}

output, err := processExec.RunCommand("radosgw-admin", args...)
if err != nil {
return []string{}, err
}

ret := []string{}
json.Unmarshal([]byte(output), &ret)
return ret, nil
}

func DeleteS3User(name string) error {
args := []string{
"user",
"rm",
fmt.Sprintf("--uid=%s", name),
}

_, err := processExec.RunCommand("radosgw-admin", args...)
if err != nil {
return err
}

return nil
}
69 changes: 69 additions & 0 deletions microceph/client/s3.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// Package client provides a full Go API client.
package client

import (
"context"
"time"

"github.com/canonical/microceph/microceph/api/types"
"github.com/canonical/microcluster/client"
"github.com/lxc/lxd/shared/api"
"github.com/lxc/lxd/shared/logger"
)

func GetS3User(ctx context.Context, c *client.Client, user *types.S3User) (types.S3User, error) {
queryCtx, cancel := context.WithTimeout(ctx, time.Second*60)
defer cancel()

ret := types.S3User{}
err := c.Query(queryCtx, "GET", api.NewURL().Path("s3"), user, &ret)
if err != nil {
logger.Error(err.Error())
return ret, err
}

return ret, nil
}

func ListS3Users(ctx context.Context, c *client.Client) ([]string, error) {
queryCtx, cancel := context.WithTimeout(ctx, time.Second*60)
defer cancel()

ret := []string{} // List of usernames
// GET request with no user name fetches all users.
err := c.Query(queryCtx, "GET", api.NewURL().Path("s3"), &types.S3User{Name: ""}, &ret)
if err != nil {
logger.Error(err.Error())
return ret, err
}

return ret, nil
}

func CreateS3User(ctx context.Context, c *client.Client, user *types.S3User) (types.S3User, error) {
queryCtx, cancel := context.WithTimeout(ctx, time.Second*60)
defer cancel()

ret := types.S3User{}
err := c.Query(queryCtx, "PUT", api.NewURL().Path("s3"), user, &ret)
if err != nil {
logger.Error(err.Error())
return ret, err
}

return ret, nil
}

func DeleteS3User(ctx context.Context, c *client.Client, user *types.S3User) error {
queryCtx, cancel := context.WithTimeout(ctx, time.Second*60)
defer cancel()

ret := types.S3User{}
err := c.Query(queryCtx, "DELETE", api.NewURL().Path("s3"), user, &ret)
if err != nil {
logger.Error(err.Error())
return err
}

return nil
}
3 changes: 3 additions & 0 deletions microceph/cmd/microceph/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ func main() {
var cmdDisk = cmdDisk{common: &commonCmd}
app.AddCommand(cmdDisk.Command())

var cmdS3 = cmdS3{common: &commonCmd}
app.AddCommand(cmdS3.Command())

app.InitDefaultHelpCmd()

err := app.Execute()
Expand Down
Loading

0 comments on commit 0feaf24

Please sign in to comment.