forked from TrueCloudLab/frostfs-s3-gw
194 lines
4.6 KiB
Go
194 lines
4.6 KiB
Go
|
package handler
|
||
|
|
||
|
import (
|
||
|
"context"
|
||
|
"encoding/xml"
|
||
|
"net/http"
|
||
|
"time"
|
||
|
|
||
|
"github.com/minio/minio/neofs/api"
|
||
|
"github.com/nspcc-dev/neofs-api-go/container"
|
||
|
"github.com/nspcc-dev/neofs-api-go/refs"
|
||
|
"github.com/nspcc-dev/neofs-api-go/service"
|
||
|
"github.com/pkg/errors"
|
||
|
"go.uber.org/zap"
|
||
|
"google.golang.org/grpc"
|
||
|
)
|
||
|
|
||
|
type (
|
||
|
// Owner - bucket owner/principal
|
||
|
Owner struct {
|
||
|
ID string
|
||
|
DisplayName string
|
||
|
}
|
||
|
|
||
|
// Bucket container for bucket metadata
|
||
|
Bucket struct {
|
||
|
Name string
|
||
|
CreationDate string // time string of format "2006-01-02T15:04:05.000Z"
|
||
|
}
|
||
|
|
||
|
// ListBucketsResponse - format for list buckets response
|
||
|
ListBucketsResponse struct {
|
||
|
XMLName xml.Name `xml:"http://s3.amazonaws.com/doc/2006-03-01/ ListAllMyBucketsResult" json:"-"`
|
||
|
|
||
|
Owner Owner
|
||
|
|
||
|
// Container for one or more buckets.
|
||
|
Buckets struct {
|
||
|
Buckets []*Bucket `xml:"Bucket"`
|
||
|
} // Buckets are nested
|
||
|
}
|
||
|
|
||
|
cnrInfoParams struct {
|
||
|
cid refs.CID
|
||
|
con *grpc.ClientConn
|
||
|
tkn *service.BearerTokenMsg
|
||
|
}
|
||
|
)
|
||
|
|
||
|
// TODO should be replaced with auth.GetBearerToken
|
||
|
func getBearerToken(ctx context.Context) (*service.BearerTokenMsg, error) {
|
||
|
if val := ctx.Value("ctxBearerToken"); val == nil {
|
||
|
return nil, errors.New("empty bearer token")
|
||
|
} else if tkn, ok := val.(*service.BearerTokenMsg); ok {
|
||
|
return tkn, nil
|
||
|
}
|
||
|
|
||
|
return nil, errors.New("bad value for bearer token")
|
||
|
}
|
||
|
|
||
|
func (h *handler) getContainerInfo(ctx context.Context, p cnrInfoParams) (*Bucket, error) {
|
||
|
var (
|
||
|
err error
|
||
|
res *container.GetResponse
|
||
|
)
|
||
|
|
||
|
req := new(container.GetRequest)
|
||
|
req.SetCID(p.cid)
|
||
|
req.SetTTL(service.SingleForwardingTTL)
|
||
|
req.SetBearer(p.tkn)
|
||
|
|
||
|
if err = service.SignRequestData(h.key, req); err != nil {
|
||
|
return nil, errors.Wrap(err, "could not sign container info request")
|
||
|
} else if res, err = container.NewServiceClient(p.con).Get(ctx, req); err != nil {
|
||
|
return nil, errors.Wrap(err, "could not fetch container info")
|
||
|
}
|
||
|
|
||
|
// TODO should extract nice name
|
||
|
// and datetime from container info:
|
||
|
_ = res
|
||
|
|
||
|
return &Bucket{
|
||
|
Name: p.cid.String(),
|
||
|
CreationDate: new(time.Time).Format(time.RFC3339),
|
||
|
}, nil
|
||
|
}
|
||
|
|
||
|
func (h *handler) ListBucketsHandler(w http.ResponseWriter, r *http.Request) {
|
||
|
var (
|
||
|
err error
|
||
|
uid = h.uid
|
||
|
inf *Bucket
|
||
|
con *grpc.ClientConn
|
||
|
res *container.ListResponse
|
||
|
)
|
||
|
|
||
|
// TODO think about timeout
|
||
|
ctx, cancel := context.WithTimeout(r.Context(), 30*time.Second)
|
||
|
defer cancel()
|
||
|
|
||
|
// TODO should be replaced with auth.GetBearerToken,
|
||
|
// than if we not received token, should call
|
||
|
// api.WriteErrorResponse
|
||
|
bearer, _ := getBearerToken(ctx)
|
||
|
|
||
|
// should be taken from BearerToken, to display only users containers
|
||
|
// in future
|
||
|
if bearer != nil {
|
||
|
uid = bearer.OwnerID
|
||
|
}
|
||
|
|
||
|
req := new(container.ListRequest)
|
||
|
req.OwnerID = uid
|
||
|
req.SetTTL(service.SingleForwardingTTL)
|
||
|
req.SetBearer(bearer)
|
||
|
// req.SetVersion(APIVersion) ??
|
||
|
|
||
|
if con, err = h.cli.GetConnection(ctx); err != nil {
|
||
|
h.log.Error("could not get connection",
|
||
|
zap.Error(err))
|
||
|
|
||
|
e := api.GetAPIError(api.ErrInternalError)
|
||
|
|
||
|
api.WriteErrorResponse(ctx, w, api.Error{
|
||
|
Code: e.Code,
|
||
|
Description: err.Error(),
|
||
|
HTTPStatusCode: e.HTTPStatusCode,
|
||
|
}, r.URL)
|
||
|
|
||
|
return
|
||
|
} else if err = service.SignRequestData(h.key, req); err != nil {
|
||
|
h.log.Error("could not prepare request",
|
||
|
zap.Error(err))
|
||
|
|
||
|
e := api.GetAPIError(api.ErrInternalError)
|
||
|
|
||
|
api.WriteErrorResponse(ctx, w, api.Error{
|
||
|
Code: e.Code,
|
||
|
Description: err.Error(),
|
||
|
HTTPStatusCode: e.HTTPStatusCode,
|
||
|
}, r.URL)
|
||
|
|
||
|
return
|
||
|
} else if res, err = container.NewServiceClient(con).List(ctx, req); err != nil {
|
||
|
h.log.Error("could not list buckets",
|
||
|
zap.Error(err))
|
||
|
|
||
|
e := api.GetAPIError(api.ErrInternalError)
|
||
|
|
||
|
api.WriteErrorResponse(ctx, w, api.Error{
|
||
|
Code: e.Code,
|
||
|
Description: err.Error(),
|
||
|
HTTPStatusCode: e.HTTPStatusCode,
|
||
|
}, r.URL)
|
||
|
|
||
|
return
|
||
|
}
|
||
|
|
||
|
result := &ListBucketsResponse{Owner: Owner{
|
||
|
ID: uid.String(),
|
||
|
DisplayName: uid.String(),
|
||
|
}}
|
||
|
|
||
|
params := cnrInfoParams{con: con, tkn: bearer}
|
||
|
|
||
|
for _, cid := range res.CID {
|
||
|
// should receive each container info (??):
|
||
|
params.cid = cid
|
||
|
|
||
|
if inf, err = h.getContainerInfo(ctx, params); err != nil {
|
||
|
h.log.Error("could not fetch bucket info",
|
||
|
zap.Error(err))
|
||
|
|
||
|
e := api.GetAPIError(api.ErrInternalError)
|
||
|
|
||
|
api.WriteErrorResponse(ctx, w, api.Error{
|
||
|
Code: e.Code,
|
||
|
Description: err.Error(),
|
||
|
HTTPStatusCode: e.HTTPStatusCode,
|
||
|
}, r.URL)
|
||
|
|
||
|
return
|
||
|
}
|
||
|
|
||
|
result.Buckets.Buckets = append(result.Buckets.Buckets, inf)
|
||
|
}
|
||
|
|
||
|
// Generate response.
|
||
|
encodedSuccessResponse := api.EncodeResponse(result)
|
||
|
|
||
|
// Write response.
|
||
|
api.WriteSuccessResponseXML(w, encodedSuccessResponse)
|
||
|
}
|