[#47] handler, layer: Add CreateBucket

Renamed PutBucket to CreateBucket

Signed-off-by: Angira Kekteeva <kira@nspcc.ru>
This commit is contained in:
Angira Kekteeva 2021-06-23 23:21:15 +03:00
parent 95f75ec880
commit c12abf6243
5 changed files with 133 additions and 12 deletions

View file

@ -62,11 +62,3 @@ func (h *handler) PutBucketNotificationHandler(w http.ResponseWriter, r *http.Re
HTTPStatusCode: http.StatusNotImplemented, HTTPStatusCode: http.StatusNotImplemented,
}, r.URL) }, r.URL)
} }
func (h *handler) PutBucketHandler(w http.ResponseWriter, r *http.Request) {
api.WriteErrorResponse(r.Context(), w, api.Error{
Code: api.GetAPIError(api.ErrBadRequest).Code,
Description: notSupported + mux.CurrentRoute(r).GetName(),
HTTPStatusCode: http.StatusNotImplemented,
}, r.URL)
}

View file

@ -1,14 +1,27 @@
package handler package handler
import ( import (
"fmt"
"net/http" "net/http"
"strconv"
"strings"
"github.com/gorilla/mux" "github.com/gorilla/mux"
"github.com/nspcc-dev/neofs-api-go/pkg/acl"
"github.com/nspcc-dev/neofs-node/pkg/policy"
"github.com/nspcc-dev/neofs-s3-gw/api" "github.com/nspcc-dev/neofs-s3-gw/api"
"github.com/nspcc-dev/neofs-s3-gw/api/layer" "github.com/nspcc-dev/neofs-s3-gw/api/layer"
"go.uber.org/zap" "go.uber.org/zap"
) )
// keywords of predefined basic ACL values.
const (
basicACLPrivate = "private"
basicACLReadOnly = "public-read"
basicACLPublic = "public-read-write"
defaultPolicy = "REP 3"
)
func (h *handler) PutObjectHandler(w http.ResponseWriter, r *http.Request) { func (h *handler) PutObjectHandler(w http.ResponseWriter, r *http.Request) {
var ( var (
err error err error
@ -58,3 +71,81 @@ func (h *handler) PutObjectHandler(w http.ResponseWriter, r *http.Request) {
api.WriteSuccessResponseHeadersOnly(w) api.WriteSuccessResponseHeadersOnly(w)
} }
func (h *handler) CreateBucketHandler(w http.ResponseWriter, r *http.Request) {
var (
err error
p = layer.CreateBucketParams{}
rid = api.GetRequestID(r.Context())
req = mux.Vars(r)
)
p.Name = req["bucket"]
if val, ok := r.Header["X-Amz-Acl"]; ok {
p.ACL, err = parseBasicACL(val[0])
} else {
p.ACL = acl.PrivateBasicRule
}
if err != nil {
h.log.Error("could not parse basic ACL",
zap.String("request_id", rid),
zap.Error(err))
api.WriteErrorResponse(r.Context(), w, api.Error{
Code: api.GetAPIError(api.ErrBadRequest).Code,
Description: err.Error(),
HTTPStatusCode: http.StatusBadRequest,
}, r.URL)
}
p.Policy, err = policy.Parse(defaultPolicy)
if err != nil {
h.log.Error("could not parse policy",
zap.String("request_id", rid),
zap.Error(err))
api.WriteErrorResponse(r.Context(), w, api.Error{
Code: api.GetAPIError(api.ErrBadRequest).Code,
Description: err.Error(),
HTTPStatusCode: http.StatusBadRequest,
}, r.URL)
}
cid, err := h.obj.CreateBucket(r.Context(), &p)
if err != nil {
h.log.Error("could not create bucket",
zap.String("request_id", rid),
zap.Error(err))
api.WriteErrorResponse(r.Context(), w, api.Error{
Code: api.GetAPIError(api.ErrInternalError).Code,
Description: err.Error(),
HTTPStatusCode: http.StatusInternalServerError,
}, r.URL)
}
h.log.Info("bucket is created",
zap.String("container_id", cid.String()))
api.WriteSuccessResponseHeadersOnly(w)
}
func parseBasicACL(basicACL string) (uint32, error) {
switch basicACL {
case basicACLPublic:
return acl.PublicBasicRule, nil
case basicACLPrivate:
return acl.PrivateBasicRule, nil
case basicACLReadOnly:
return acl.ReadOnlyBasicRule, nil
default:
basicACL = strings.Trim(strings.ToLower(basicACL), "0x")
value, err := strconv.ParseUint(basicACL, 16, 32)
if err != nil {
return 0, fmt.Errorf("can't parse basic ACL: %s", basicACL)
}
return uint32(value), nil
}
}

View file

@ -2,6 +2,7 @@ package layer
import ( import (
"context" "context"
"fmt"
"strconv" "strconv"
"time" "time"
@ -9,6 +10,8 @@ import (
cid "github.com/nspcc-dev/neofs-api-go/pkg/container/id" cid "github.com/nspcc-dev/neofs-api-go/pkg/container/id"
"github.com/nspcc-dev/neofs-api-go/pkg/owner" "github.com/nspcc-dev/neofs-api-go/pkg/owner"
"github.com/nspcc-dev/neofs-s3-gw/api" "github.com/nspcc-dev/neofs-s3-gw/api"
"github.com/nspcc-dev/neofs-s3-gw/creds/accessbox"
"github.com/nspcc-dev/neofs-sdk-go/pkg/pool"
"go.uber.org/zap" "go.uber.org/zap"
) )
@ -125,3 +128,26 @@ func (n *layer) containerList(ctx context.Context) ([]*BucketInfo, error) {
return list, nil return list, nil
} }
func (n *layer) createContainer(ctx context.Context, p *CreateBucketParams) (*cid.ID, error) {
cnr := container.New(
container.WithPolicy(p.Policy),
container.WithCustomBasicACL(p.ACL),
container.WithAttribute(container.AttributeName, p.Name),
container.WithAttribute(container.AttributeTimestamp, strconv.FormatInt(time.Now().Unix(), 10)))
cnr.SetSessionToken(ctx.Value(api.GateData).(*accessbox.GateData).SessionToken)
cnr.SetOwnerID(n.Owner(ctx))
cid, err := n.pool.PutContainer(ctx, cnr)
if err != nil {
return nil, fmt.Errorf("failed to create a bucket: %w", err)
}
err = n.pool.WaitForContainerPresence(ctx, cid, pool.DefaultPollingParams())
if err != nil {
return nil, err
}
return cid, nil
}

View file

@ -11,6 +11,7 @@ import (
"github.com/nspcc-dev/neofs-api-go/pkg/client" "github.com/nspcc-dev/neofs-api-go/pkg/client"
cid "github.com/nspcc-dev/neofs-api-go/pkg/container/id" cid "github.com/nspcc-dev/neofs-api-go/pkg/container/id"
"github.com/nspcc-dev/neofs-api-go/pkg/netmap"
"github.com/nspcc-dev/neofs-api-go/pkg/object" "github.com/nspcc-dev/neofs-api-go/pkg/object"
"github.com/nspcc-dev/neofs-api-go/pkg/owner" "github.com/nspcc-dev/neofs-api-go/pkg/owner"
"github.com/nspcc-dev/neofs-s3-gw/api" "github.com/nspcc-dev/neofs-s3-gw/api"
@ -61,6 +62,12 @@ type (
DstObject string DstObject string
Header map[string]string Header map[string]string
} }
// CreateBucketParams stores bucket create request parameters.
CreateBucketParams struct {
Name string
ACL uint32
Policy *netmap.PlacementPolicy
}
// NeoFS provides basic NeoFS interface. // NeoFS provides basic NeoFS interface.
NeoFS interface { NeoFS interface {
@ -73,6 +80,7 @@ type (
ListBuckets(ctx context.Context) ([]*BucketInfo, error) ListBuckets(ctx context.Context) ([]*BucketInfo, error)
GetBucketInfo(ctx context.Context, name string) (*BucketInfo, error) GetBucketInfo(ctx context.Context, name string) (*BucketInfo, error)
CreateBucket(ctx context.Context, p *CreateBucketParams) (*cid.ID, error)
GetObject(ctx context.Context, p *GetObjectParams) error GetObject(ctx context.Context, p *GetObjectParams) error
GetObjectInfo(ctx context.Context, bucketName, objectName string) (*ObjectInfo, error) GetObjectInfo(ctx context.Context, bucketName, objectName string) (*ObjectInfo, error)
@ -408,3 +416,7 @@ func (n *layer) DeleteObjects(ctx context.Context, bucket string, objects []stri
return errs return errs
} }
func (n *layer) CreateBucket(ctx context.Context, p *CreateBucketParams) (*cid.ID, error) {
return n.createContainer(ctx, p)
}

View file

@ -68,7 +68,7 @@ type (
PutBucketTaggingHandler(http.ResponseWriter, *http.Request) PutBucketTaggingHandler(http.ResponseWriter, *http.Request)
PutBucketVersioningHandler(http.ResponseWriter, *http.Request) PutBucketVersioningHandler(http.ResponseWriter, *http.Request)
PutBucketNotificationHandler(http.ResponseWriter, *http.Request) PutBucketNotificationHandler(http.ResponseWriter, *http.Request)
PutBucketHandler(http.ResponseWriter, *http.Request) CreateBucketHandler(http.ResponseWriter, *http.Request)
HeadBucketHandler(http.ResponseWriter, *http.Request) HeadBucketHandler(http.ResponseWriter, *http.Request)
PostPolicyBucketHandler(http.ResponseWriter, *http.Request) PostPolicyBucketHandler(http.ResponseWriter, *http.Request)
DeleteMultipleObjectsHandler(http.ResponseWriter, *http.Request) DeleteMultipleObjectsHandler(http.ResponseWriter, *http.Request)
@ -412,10 +412,10 @@ func Attach(r *mux.Router, domains []string, m MaxClients, h Handler, center aut
bucket.Methods(http.MethodPut).HandlerFunc( bucket.Methods(http.MethodPut).HandlerFunc(
m.Handle(metrics.APIStats("putbucketnotification", h.PutBucketNotificationHandler))).Queries("notification", ""). m.Handle(metrics.APIStats("putbucketnotification", h.PutBucketNotificationHandler))).Queries("notification", "").
Name("PutBucketNotification") Name("PutBucketNotification")
// PutBucket // CreateBucket
bucket.Methods(http.MethodPut).HandlerFunc( bucket.Methods(http.MethodPut).HandlerFunc(
m.Handle(metrics.APIStats("putbucket", h.PutBucketHandler))). m.Handle(metrics.APIStats("createbucket", h.CreateBucketHandler))).
Name("PutBucket") Name("CreateBucket")
// HeadBucket // HeadBucket
bucket.Methods(http.MethodHead).HandlerFunc( bucket.Methods(http.MethodHead).HandlerFunc(
m.Handle(metrics.APIStats("headbucket", h.HeadBucketHandler))). m.Handle(metrics.APIStats("headbucket", h.HeadBucketHandler))).