forked from TrueCloudLab/frostfs-s3-gw
Remove legacy code usage
This commit is contained in:
parent
28fa75fb69
commit
e223876d53
5 changed files with 135 additions and 355 deletions
|
@ -8,17 +8,9 @@ import (
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/Azure/azure-storage-blob-go/azblob"
|
"github.com/minio/minio-go/v7/pkg/tags"
|
||||||
"github.com/minio/minio-go/v6"
|
|
||||||
"github.com/minio/minio-go/v6/pkg/tags"
|
|
||||||
"github.com/minio/minio/auth"
|
"github.com/minio/minio/auth"
|
||||||
"github.com/minio/minio/neofs/api/crypto"
|
"github.com/minio/minio/neofs/api/crypto"
|
||||||
"github.com/minio/minio/pkg/bucket/lifecycle"
|
|
||||||
objectlock "github.com/minio/minio/pkg/bucket/object/lock"
|
|
||||||
"github.com/minio/minio/pkg/bucket/policy"
|
|
||||||
"github.com/minio/minio/pkg/event"
|
|
||||||
"github.com/minio/minio/pkg/hash"
|
|
||||||
"google.golang.org/api/googleapi"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
|
@ -1629,7 +1621,7 @@ func (e errorCodeMap) ToAPIErr(errCode ErrorCode) Error {
|
||||||
// toAPIErrorCode - Converts embedded errors. Convenience
|
// toAPIErrorCode - Converts embedded errors. Convenience
|
||||||
// function written to handle all cases where we have known types of
|
// function written to handle all cases where we have known types of
|
||||||
// errors returned by underlying layers.
|
// errors returned by underlying layers.
|
||||||
func toAPIErrorCode(ctx context.Context, err error) (apiErr ErrorCode) {
|
func toAPIErrorCode(_ context.Context, err error) (apiErr ErrorCode) {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return ErrNone
|
return ErrNone
|
||||||
}
|
}
|
||||||
|
@ -1690,16 +1682,16 @@ func toAPIErrorCode(ctx context.Context, err error) (apiErr ErrorCode) {
|
||||||
apiErr = ErrOperationTimedOut
|
apiErr = ErrOperationTimedOut
|
||||||
case errDiskNotFound:
|
case errDiskNotFound:
|
||||||
apiErr = ErrSlowDown
|
apiErr = ErrSlowDown
|
||||||
case objectlock.ErrInvalidRetentionDate:
|
// case objectlock.ErrInvalidRetentionDate:
|
||||||
apiErr = ErrInvalidRetentionDate
|
// apiErr = ErrInvalidRetentionDate
|
||||||
case objectlock.ErrPastObjectLockRetainDate:
|
// case objectlock.ErrPastObjectLockRetainDate:
|
||||||
apiErr = ErrPastObjectLockRetainDate
|
// apiErr = ErrPastObjectLockRetainDate
|
||||||
case objectlock.ErrUnknownWORMModeDirective:
|
// case objectlock.ErrUnknownWORMModeDirective:
|
||||||
apiErr = ErrUnknownWORMModeDirective
|
// apiErr = ErrUnknownWORMModeDirective
|
||||||
case objectlock.ErrObjectLockInvalidHeaders:
|
// case objectlock.ErrObjectLockInvalidHeaders:
|
||||||
apiErr = ErrObjectLockInvalidHeaders
|
// apiErr = ErrObjectLockInvalidHeaders
|
||||||
case objectlock.ErrMalformedXML:
|
// case objectlock.ErrMalformedXML:
|
||||||
apiErr = ErrMalformedXML
|
// apiErr = ErrMalformedXML
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compression errors
|
// Compression errors
|
||||||
|
@ -1722,8 +1714,8 @@ func toAPIErrorCode(ctx context.Context, err error) (apiErr ErrorCode) {
|
||||||
switch err.(type) {
|
switch err.(type) {
|
||||||
case StorageFull:
|
case StorageFull:
|
||||||
apiErr = ErrStorageFull
|
apiErr = ErrStorageFull
|
||||||
case hash.BadDigest:
|
// case hash.BadDigest:
|
||||||
apiErr = ErrBadDigest
|
// apiErr = ErrBadDigest
|
||||||
case AllAccessDisabled:
|
case AllAccessDisabled:
|
||||||
apiErr = ErrAllAccessDisabled
|
apiErr = ErrAllAccessDisabled
|
||||||
case IncompleteBody:
|
case IncompleteBody:
|
||||||
|
@ -1774,8 +1766,8 @@ func toAPIErrorCode(ctx context.Context, err error) (apiErr ErrorCode) {
|
||||||
apiErr = ErrEntityTooSmall
|
apiErr = ErrEntityTooSmall
|
||||||
case SignatureDoesNotMatch:
|
case SignatureDoesNotMatch:
|
||||||
apiErr = ErrSignatureDoesNotMatch
|
apiErr = ErrSignatureDoesNotMatch
|
||||||
case hash.SHA256Mismatch:
|
// case hash.SHA256Mismatch:
|
||||||
apiErr = ErrContentSHA256Mismatch
|
// apiErr = ErrContentSHA256Mismatch
|
||||||
case ObjectTooLarge:
|
case ObjectTooLarge:
|
||||||
apiErr = ErrEntityTooLarge
|
apiErr = ErrEntityTooLarge
|
||||||
case ObjectTooSmall:
|
case ObjectTooSmall:
|
||||||
|
@ -1800,28 +1792,28 @@ func toAPIErrorCode(ctx context.Context, err error) (apiErr ErrorCode) {
|
||||||
apiErr = ErrAdminNoSuchQuotaConfiguration
|
apiErr = ErrAdminNoSuchQuotaConfiguration
|
||||||
case BucketQuotaExceeded:
|
case BucketQuotaExceeded:
|
||||||
apiErr = ErrAdminBucketQuotaExceeded
|
apiErr = ErrAdminBucketQuotaExceeded
|
||||||
case *event.ErrInvalidEventName:
|
// case *event.ErrInvalidEventName:
|
||||||
apiErr = ErrEventNotification
|
// apiErr = ErrEventNotification
|
||||||
case *event.ErrInvalidARN:
|
// case *event.ErrInvalidARN:
|
||||||
apiErr = ErrARNNotification
|
// apiErr = ErrARNNotification
|
||||||
case *event.ErrARNNotFound:
|
// case *event.ErrARNNotFound:
|
||||||
apiErr = ErrARNNotification
|
// apiErr = ErrARNNotification
|
||||||
case *event.ErrUnknownRegion:
|
// case *event.ErrUnknownRegion:
|
||||||
apiErr = ErrRegionNotification
|
// apiErr = ErrRegionNotification
|
||||||
case *event.ErrInvalidFilterName:
|
// case *event.ErrInvalidFilterName:
|
||||||
apiErr = ErrFilterNameInvalid
|
// apiErr = ErrFilterNameInvalid
|
||||||
case *event.ErrFilterNamePrefix:
|
// case *event.ErrFilterNamePrefix:
|
||||||
apiErr = ErrFilterNamePrefix
|
// apiErr = ErrFilterNamePrefix
|
||||||
case *event.ErrFilterNameSuffix:
|
// case *event.ErrFilterNameSuffix:
|
||||||
apiErr = ErrFilterNameSuffix
|
// apiErr = ErrFilterNameSuffix
|
||||||
case *event.ErrInvalidFilterValue:
|
// case *event.ErrInvalidFilterValue:
|
||||||
apiErr = ErrFilterValueInvalid
|
// apiErr = ErrFilterValueInvalid
|
||||||
case *event.ErrDuplicateEventName:
|
// case *event.ErrDuplicateEventName:
|
||||||
apiErr = ErrOverlappingConfigs
|
// apiErr = ErrOverlappingConfigs
|
||||||
case *event.ErrDuplicateQueueConfiguration:
|
// case *event.ErrDuplicateQueueConfiguration:
|
||||||
apiErr = ErrOverlappingFilterNotification
|
// apiErr = ErrOverlappingFilterNotification
|
||||||
case *event.ErrUnsupportedConfiguration:
|
// case *event.ErrUnsupportedConfiguration:
|
||||||
apiErr = ErrUnsupportedNotification
|
// apiErr = ErrUnsupportedNotification
|
||||||
case OperationTimedOut:
|
case OperationTimedOut:
|
||||||
apiErr = ErrOperationTimedOut
|
apiErr = ErrOperationTimedOut
|
||||||
case BackendDown:
|
case BackendDown:
|
||||||
|
@ -1884,55 +1876,36 @@ func toAPIError(ctx context.Context, err error) Error {
|
||||||
e.Error()),
|
e.Error()),
|
||||||
HTTPStatusCode: http.StatusBadRequest,
|
HTTPStatusCode: http.StatusBadRequest,
|
||||||
}
|
}
|
||||||
case lifecycle.Error:
|
// case lifecycle.Error:
|
||||||
apiErr = Error{
|
// apiErr = Error{
|
||||||
Code: "InvalidRequest",
|
// Code: "InvalidRequest",
|
||||||
Description: e.Error(),
|
// Description: e.Error(),
|
||||||
HTTPStatusCode: http.StatusBadRequest,
|
// HTTPStatusCode: http.StatusBadRequest,
|
||||||
}
|
// }
|
||||||
case tags.Error:
|
case tags.Error:
|
||||||
apiErr = Error{
|
apiErr = Error{
|
||||||
Code: e.Code(),
|
Code: e.Code(),
|
||||||
Description: e.Error(),
|
Description: e.Error(),
|
||||||
HTTPStatusCode: http.StatusBadRequest,
|
HTTPStatusCode: http.StatusBadRequest,
|
||||||
}
|
}
|
||||||
case policy.Error:
|
// case policy.Error:
|
||||||
apiErr = Error{
|
// apiErr = Error{
|
||||||
Code: "MalformedPolicy",
|
// Code: "MalformedPolicy",
|
||||||
Description: e.Error(),
|
// Description: e.Error(),
|
||||||
HTTPStatusCode: http.StatusBadRequest,
|
// HTTPStatusCode: http.StatusBadRequest,
|
||||||
}
|
// }
|
||||||
case crypto.Error:
|
case crypto.Error:
|
||||||
apiErr = Error{
|
apiErr = Error{
|
||||||
Code: "XMinIOEncryptionError",
|
Code: "XMinIOEncryptionError",
|
||||||
Description: e.Error(),
|
Description: e.Error(),
|
||||||
HTTPStatusCode: http.StatusBadRequest,
|
HTTPStatusCode: http.StatusBadRequest,
|
||||||
}
|
}
|
||||||
case minio.ErrorResponse:
|
case ErrorResponse:
|
||||||
apiErr = Error{
|
apiErr = Error{
|
||||||
Code: e.Code,
|
Code: e.Code,
|
||||||
Description: e.Message,
|
Description: e.Message,
|
||||||
HTTPStatusCode: e.StatusCode,
|
HTTPStatusCode: e.StatusCode,
|
||||||
}
|
}
|
||||||
case *googleapi.Error:
|
|
||||||
apiErr = Error{
|
|
||||||
Code: "XGCSInternalError",
|
|
||||||
Description: e.Message,
|
|
||||||
HTTPStatusCode: e.Code,
|
|
||||||
}
|
|
||||||
// GCS may send multiple errors, just pick the first one
|
|
||||||
// since S3 only sends one Error XML response.
|
|
||||||
if len(e.Errors) >= 1 {
|
|
||||||
apiErr.Code = e.Errors[0].Reason
|
|
||||||
|
|
||||||
}
|
|
||||||
case azblob.StorageError:
|
|
||||||
apiErr = Error{
|
|
||||||
Code: string(e.ServiceCode()),
|
|
||||||
Description: e.Error(),
|
|
||||||
HTTPStatusCode: e.Response().StatusCode,
|
|
||||||
}
|
|
||||||
// Add more Gateway SDKs here if any in future.
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,131 +1,37 @@
|
||||||
package handler
|
package handler
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"errors"
|
||||||
"crypto/ecdsa"
|
|
||||||
"math"
|
|
||||||
|
|
||||||
"github.com/minio/minio/neofs/api"
|
"github.com/minio/minio/neofs/api"
|
||||||
"github.com/minio/minio/neofs/pool"
|
"github.com/minio/minio/neofs/layer"
|
||||||
"github.com/nspcc-dev/neofs-api-go/refs"
|
|
||||||
"github.com/nspcc-dev/neofs-api-go/service"
|
|
||||||
"github.com/nspcc-dev/neofs-api-go/session"
|
|
||||||
crypto "github.com/nspcc-dev/neofs-crypto"
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
handler struct {
|
handler struct {
|
||||||
log *zap.Logger
|
log *zap.Logger
|
||||||
cli pool.Client
|
obj layer.Client
|
||||||
uid refs.OwnerID
|
|
||||||
tkn *service.Token
|
|
||||||
key *ecdsa.PrivateKey
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Params struct {
|
Params struct {
|
||||||
Cli pool.Client
|
|
||||||
Log *zap.Logger
|
Log *zap.Logger
|
||||||
Key *ecdsa.PrivateKey
|
Obj layer.Client
|
||||||
}
|
|
||||||
|
|
||||||
queryParams struct {
|
|
||||||
key *ecdsa.PrivateKey
|
|
||||||
addr refs.Address
|
|
||||||
verb service.Token_Info_Verb
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ api.Handler = (*handler)(nil)
|
var _ api.Handler = (*handler)(nil)
|
||||||
|
|
||||||
func New(ctx context.Context, p Params) (api.Handler, error) {
|
func New(log *zap.Logger, obj layer.Client) (api.Handler, error) {
|
||||||
var (
|
|
||||||
err error
|
|
||||||
uid refs.OwnerID
|
|
||||||
tkn *service.Token
|
|
||||||
)
|
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case p.Key == nil:
|
case obj == nil:
|
||||||
return nil, errors.New("empty private key")
|
return nil, errors.New("empty NeoFS Object Layer")
|
||||||
case p.Cli == nil:
|
case log == nil:
|
||||||
return nil, errors.New("empty gRPC client")
|
|
||||||
case p.Log == nil:
|
|
||||||
return nil, errors.New("empty logger")
|
return nil, errors.New("empty logger")
|
||||||
}
|
}
|
||||||
|
|
||||||
if uid, err = refs.NewOwnerID(&p.Key.PublicKey); err != nil {
|
|
||||||
return nil, errors.Wrap(err, "could not fetch OwnerID")
|
|
||||||
} else if tkn, err = generateToken(ctx, p.Cli, p.Key); err != nil {
|
|
||||||
return nil, errors.Wrap(err, "could not prepare session token")
|
|
||||||
}
|
|
||||||
|
|
||||||
return &handler{
|
return &handler{
|
||||||
uid: uid,
|
log: log,
|
||||||
tkn: tkn,
|
obj: obj,
|
||||||
key: p.Key,
|
|
||||||
log: p.Log,
|
|
||||||
cli: p.Cli,
|
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func generateToken(ctx context.Context, cli pool.Client, key *ecdsa.PrivateKey) (*service.Token, error) {
|
|
||||||
owner, err := refs.NewOwnerID(&key.PublicKey)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
token := new(service.Token)
|
|
||||||
token.SetOwnerID(owner)
|
|
||||||
token.SetExpirationEpoch(math.MaxUint64)
|
|
||||||
token.SetOwnerKey(crypto.MarshalPublicKey(&key.PublicKey))
|
|
||||||
|
|
||||||
conn, err := cli.GetConnection(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
creator, err := session.NewGRPCCreator(conn, key)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
res, err := creator.Create(ctx, token)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
token.SetID(res.GetID())
|
|
||||||
token.SetSessionKey(res.GetSessionKey())
|
|
||||||
|
|
||||||
return token, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func prepareToken(t *service.Token, p queryParams) (*service.Token, error) {
|
|
||||||
sig := make([]byte, len(t.Signature))
|
|
||||||
copy(sig, t.Signature)
|
|
||||||
|
|
||||||
token := &service.Token{
|
|
||||||
Token_Info: service.Token_Info{
|
|
||||||
ID: t.ID,
|
|
||||||
OwnerID: t.OwnerID,
|
|
||||||
Verb: t.Verb,
|
|
||||||
Address: t.Address,
|
|
||||||
TokenLifetime: t.TokenLifetime,
|
|
||||||
SessionKey: t.SessionKey,
|
|
||||||
OwnerKey: t.OwnerKey,
|
|
||||||
},
|
|
||||||
Signature: sig,
|
|
||||||
}
|
|
||||||
|
|
||||||
token.SetAddress(p.addr)
|
|
||||||
token.SetVerb(p.verb)
|
|
||||||
|
|
||||||
err := service.AddSignatureWithKey(p.key, service.NewSignedSessionToken(token))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return token, nil
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,171 +0,0 @@
|
||||||
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 {
|
|
||||||
CID refs.CID `xml:"-"` // ignored by response
|
|
||||||
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{
|
|
||||||
CID: p.cid,
|
|
||||||
Name: p.cid.String(),
|
|
||||||
CreationDate: new(time.Time).Format(time.RFC3339),
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *handler) getContainerList(ctx context.Context, tkn *service.BearerTokenMsg) ([]*Bucket, error) {
|
|
||||||
var (
|
|
||||||
err error
|
|
||||||
inf *Bucket
|
|
||||||
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")
|
|
||||||
}
|
|
||||||
|
|
||||||
params := cnrInfoParams{tkn: tkn}
|
|
||||||
result := make([]*Bucket, 0, len(res.CID))
|
|
||||||
|
|
||||||
for _, cid := range res.CID {
|
|
||||||
params.cid = cid
|
|
||||||
if inf, err = h.getContainerInfo(ctx, params); err != nil {
|
|
||||||
return nil, errors.Wrap(err, "could not fetch container info")
|
|
||||||
}
|
|
||||||
|
|
||||||
result = append(result, inf)
|
|
||||||
}
|
|
||||||
|
|
||||||
return result, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *handler) ListBucketsHandler(w http.ResponseWriter, r *http.Request) {
|
|
||||||
var (
|
|
||||||
err error
|
|
||||||
lst []*Bucket
|
|
||||||
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(),
|
|
||||||
}}
|
|
||||||
|
|
||||||
result.Buckets.Buckets = lst
|
|
||||||
|
|
||||||
// Generate response.
|
|
||||||
encodedSuccessResponse := api.EncodeResponse(result)
|
|
||||||
|
|
||||||
// Write response.
|
|
||||||
api.WriteSuccessResponseXML(w, encodedSuccessResponse)
|
|
||||||
}
|
|
|
@ -6,6 +6,14 @@ import (
|
||||||
"github.com/minio/minio/neofs/api"
|
"github.com/minio/minio/neofs/api"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func (h *handler) ListBucketsHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
api.WriteErrorResponse(r.Context(), w, api.Error{
|
||||||
|
Code: "XNeoFSUnimplemented",
|
||||||
|
Description: "implement me ListBucketsHandler",
|
||||||
|
HTTPStatusCode: http.StatusNotImplemented,
|
||||||
|
}, r.URL)
|
||||||
|
}
|
||||||
|
|
||||||
func (h *handler) HeadObjectHandler(w http.ResponseWriter, r *http.Request) {
|
func (h *handler) HeadObjectHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
api.WriteErrorResponse(r.Context(), w, api.Error{
|
||||||
Code: "XNeoFSUnimplemented",
|
Code: "XNeoFSUnimplemented",
|
||||||
|
|
|
@ -14,7 +14,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
// APIErrorResponse - error response format
|
// ErrorResponse - error response format
|
||||||
ErrorResponse struct {
|
ErrorResponse struct {
|
||||||
XMLName xml.Name `xml:"Error" json:"-"`
|
XMLName xml.Name `xml:"Error" json:"-"`
|
||||||
Code string
|
Code string
|
||||||
|
@ -22,9 +22,18 @@ type (
|
||||||
Key string `xml:"Key,omitempty" json:"Key,omitempty"`
|
Key string `xml:"Key,omitempty" json:"Key,omitempty"`
|
||||||
BucketName string `xml:"BucketName,omitempty" json:"BucketName,omitempty"`
|
BucketName string `xml:"BucketName,omitempty" json:"BucketName,omitempty"`
|
||||||
Resource string
|
Resource string
|
||||||
Region string `xml:"Region,omitempty" json:"Region,omitempty"`
|
|
||||||
RequestID string `xml:"RequestId" json:"RequestId"`
|
RequestID string `xml:"RequestId" json:"RequestId"`
|
||||||
HostID string `xml:"HostId" json:"HostId"`
|
HostID string `xml:"HostId" json:"HostId"`
|
||||||
|
|
||||||
|
// Region where the bucket is located. This header is returned
|
||||||
|
// only in HEAD bucket and ListObjects response.
|
||||||
|
Region string `xml:"Region,omitempty" json:"Region,omitempty"`
|
||||||
|
|
||||||
|
// Captures the server string returned in response header.
|
||||||
|
Server string `xml:"-" json:"-"`
|
||||||
|
|
||||||
|
// Underlying HTTP status code for the returned error
|
||||||
|
StatusCode int `xml:"-" json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// APIError structure
|
// APIError structure
|
||||||
|
@ -61,6 +70,49 @@ const (
|
||||||
|
|
||||||
var deploymentID, _ = uuid.NewRandom()
|
var deploymentID, _ = uuid.NewRandom()
|
||||||
|
|
||||||
|
// Non exhaustive list of AWS S3 standard error responses -
|
||||||
|
// http://docs.aws.amazon.com/AmazonS3/latest/API/ErrorResponses.html
|
||||||
|
var s3ErrorResponseMap = map[string]string{
|
||||||
|
"AccessDenied": "Access Denied.",
|
||||||
|
"BadDigest": "The Content-Md5 you specified did not match what we received.",
|
||||||
|
"EntityTooSmall": "Your proposed upload is smaller than the minimum allowed object size.",
|
||||||
|
"EntityTooLarge": "Your proposed upload exceeds the maximum allowed object size.",
|
||||||
|
"IncompleteBody": "You did not provide the number of bytes specified by the Content-Length HTTP header.",
|
||||||
|
"InternalError": "We encountered an internal error, please try again.",
|
||||||
|
"InvalidAccessKeyId": "The access key ID you provided does not exist in our records.",
|
||||||
|
"InvalidBucketName": "The specified bucket is not valid.",
|
||||||
|
"InvalidDigest": "The Content-Md5 you specified is not valid.",
|
||||||
|
"InvalidRange": "The requested range is not satisfiable",
|
||||||
|
"MalformedXML": "The XML you provided was not well-formed or did not validate against our published schema.",
|
||||||
|
"MissingContentLength": "You must provide the Content-Length HTTP header.",
|
||||||
|
"MissingContentMD5": "Missing required header for this request: Content-Md5.",
|
||||||
|
"MissingRequestBodyError": "Request body is empty.",
|
||||||
|
"NoSuchBucket": "The specified bucket does not exist.",
|
||||||
|
"NoSuchBucketPolicy": "The bucket policy does not exist",
|
||||||
|
"NoSuchKey": "The specified key does not exist.",
|
||||||
|
"NoSuchUpload": "The specified multipart upload does not exist. The upload ID may be invalid, or the upload may have been aborted or completed.",
|
||||||
|
"NotImplemented": "A header you provided implies functionality that is not implemented",
|
||||||
|
"PreconditionFailed": "At least one of the pre-conditions you specified did not hold",
|
||||||
|
"RequestTimeTooSkewed": "The difference between the request time and the server's time is too large.",
|
||||||
|
"SignatureDoesNotMatch": "The request signature we calculated does not match the signature you provided. Check your key and signing method.",
|
||||||
|
"MethodNotAllowed": "The specified method is not allowed against this resource.",
|
||||||
|
"InvalidPart": "One or more of the specified parts could not be found.",
|
||||||
|
"InvalidPartOrder": "The list of parts was not in ascending order. The parts list must be specified in order by part number.",
|
||||||
|
"InvalidObjectState": "The operation is not valid for the current state of the object.",
|
||||||
|
"AuthorizationHeaderMalformed": "The authorization header is malformed; the region is wrong.",
|
||||||
|
"MalformedPOSTRequest": "The body of your POST request is not well-formed multipart/form-data.",
|
||||||
|
"BucketNotEmpty": "The bucket you tried to delete is not empty",
|
||||||
|
"AllAccessDisabled": "All access to this bucket has been disabled.",
|
||||||
|
"MalformedPolicy": "Policy has invalid resource.",
|
||||||
|
"MissingFields": "Missing fields in request.",
|
||||||
|
"AuthorizationQueryParametersError": "Error parsing the X-Amz-Credential parameter; the Credential is mal-formed; expecting \"<YOUR-AKID>/YYYYMMDD/REGION/SERVICE/aws4_request\".",
|
||||||
|
"MalformedDate": "Invalid date format header, expected to be in ISO8601, RFC1123 or RFC1123Z time format.",
|
||||||
|
"BucketAlreadyOwnedByYou": "Your previous request to create the named bucket succeeded and you already own it.",
|
||||||
|
"InvalidDuration": "Duration provided in the request is invalid.",
|
||||||
|
"XAmzContentSHA256Mismatch": "The provided 'x-amz-content-sha256' header does not match what was computed.",
|
||||||
|
// Add new API errors here.
|
||||||
|
}
|
||||||
|
|
||||||
// WriteErrorResponse writes error headers
|
// WriteErrorResponse writes error headers
|
||||||
func WriteErrorResponse(ctx context.Context, w http.ResponseWriter, err Error, reqURL *url.URL) {
|
func WriteErrorResponse(ctx context.Context, w http.ResponseWriter, err Error, reqURL *url.URL) {
|
||||||
switch err.Code {
|
switch err.Code {
|
||||||
|
@ -134,3 +186,15 @@ func EncodeResponse(response interface{}) []byte {
|
||||||
func WriteSuccessResponseXML(w http.ResponseWriter, response []byte) {
|
func WriteSuccessResponseXML(w http.ResponseWriter, response []byte) {
|
||||||
writeResponse(w, http.StatusOK, response, mimeXML)
|
writeResponse(w, http.StatusOK, response, mimeXML)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Error - Returns S3 error string.
|
||||||
|
func (e ErrorResponse) Error() string {
|
||||||
|
if e.Message == "" {
|
||||||
|
msg, ok := s3ErrorResponseMap[e.Code]
|
||||||
|
if !ok {
|
||||||
|
msg = fmt.Sprintf("Error response code %s.", e.Code)
|
||||||
|
}
|
||||||
|
return msg
|
||||||
|
}
|
||||||
|
return e.Message
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue