forked from TrueCloudLab/frostfs-s3-gw
181 lines
4.4 KiB
Go
181 lines
4.4 KiB
Go
package handler
|
|
|
|
import (
|
|
"context"
|
|
"encoding/xml"
|
|
"net/http"
|
|
"time"
|
|
|
|
"github.com/minio/minio/auth"
|
|
"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
|
|
tkn *service.BearerTokenMsg
|
|
}
|
|
)
|
|
|
|
func (h *handler) getContainerInfo(ctx context.Context, p cnrInfoParams) (*Bucket, error) {
|
|
var (
|
|
err error
|
|
con *grpc.ClientConn
|
|
res *container.GetResponse
|
|
)
|
|
|
|
req := new(container.GetRequest)
|
|
req.SetCID(p.cid)
|
|
req.SetTTL(service.SingleForwardingTTL)
|
|
req.SetBearer(p.tkn)
|
|
|
|
if con, err = h.cli.GetConnection(ctx); err != nil {
|
|
return nil, errors.Wrap(err, "could not fetch connection")
|
|
} else 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(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) getContainerList(ctx context.Context, tkn *service.BearerTokenMsg) ([]refs.CID, error) {
|
|
var (
|
|
err error
|
|
con *grpc.ClientConn
|
|
res *container.ListResponse
|
|
)
|
|
|
|
req := new(container.ListRequest)
|
|
req.OwnerID = tkn.OwnerID
|
|
req.SetTTL(service.SingleForwardingTTL)
|
|
req.SetBearer(tkn)
|
|
|
|
if con, err = h.cli.GetConnection(ctx); err != nil {
|
|
return nil, errors.Wrap(err, "could not fetch connection")
|
|
} else if err = service.SignRequestData(h.key, req); err != nil {
|
|
return nil, errors.Wrap(err, "could not sign request")
|
|
} else if res, err = container.NewServiceClient(con).List(ctx, req); err != nil {
|
|
return nil, errors.Wrap(err, "could not fetch list containers")
|
|
}
|
|
|
|
return res.CID, nil
|
|
}
|
|
|
|
func (h *handler) ListBucketsHandler(w http.ResponseWriter, r *http.Request) {
|
|
var (
|
|
err error
|
|
inf *Bucket
|
|
lst []refs.CID
|
|
tkn *service.BearerTokenMsg
|
|
)
|
|
|
|
// TODO think about deadlines
|
|
ctx, cancel := context.WithTimeout(r.Context(), 30*time.Second)
|
|
defer cancel()
|
|
|
|
if tkn, err = auth.GetBearerToken(ctx); err != nil {
|
|
h.log.Error("could not fetch bearer token",
|
|
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 lst, err = h.getContainerList(ctx, tkn); err != nil {
|
|
h.log.Error("could not fetch bearer token",
|
|
zap.Error(err))
|
|
|
|
// TODO check that error isn't gRPC error
|
|
|
|
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: tkn.OwnerID.String(),
|
|
DisplayName: tkn.OwnerID.String(),
|
|
}}
|
|
|
|
params := cnrInfoParams{tkn: tkn}
|
|
|
|
for _, cid := range lst {
|
|
// 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))
|
|
|
|
// TODO check that error isn't gRPC error
|
|
|
|
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)
|
|
}
|