forked from TrueCloudLab/frostfs-s3-gw
[#143] Add more context to some s3 errors
Signed-off-by: Denis Kirillov <d.kirillov@yadro.com>
This commit is contained in:
parent
f921bc8af5
commit
d531b13866
13 changed files with 105 additions and 73 deletions
|
@ -3,6 +3,8 @@ package errors
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
frosterrors "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/internal/frostfs/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
|
@ -1700,6 +1702,7 @@ var errorCodes = errorCodeMap{
|
||||||
|
|
||||||
// IsS3Error checks if the provided error is a specific s3 error.
|
// IsS3Error checks if the provided error is a specific s3 error.
|
||||||
func IsS3Error(err error, code ErrorCode) bool {
|
func IsS3Error(err error, code ErrorCode) bool {
|
||||||
|
err = frosterrors.UnwrapErr(err)
|
||||||
e, ok := err.(Error)
|
e, ok := err.(Error)
|
||||||
return ok && e.ErrCode == code
|
return ok && e.ErrCode == code
|
||||||
}
|
}
|
||||||
|
|
|
@ -507,7 +507,7 @@ func checkOwner(info *data.BucketInfo, owner string) error {
|
||||||
|
|
||||||
// may need to convert owner to appropriate format
|
// may need to convert owner to appropriate format
|
||||||
if info.Owner.String() != owner {
|
if info.Owner.String() != owner {
|
||||||
return errors.GetAPIError(errors.ErrAccessDenied)
|
return fmt.Errorf("%w: mismatch owner", errors.GetAPIError(errors.ErrAccessDenied))
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -11,10 +11,12 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api"
|
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/data"
|
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/data"
|
||||||
|
s3errors "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/errors"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/creds/accessbox"
|
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/creds/accessbox"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer"
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/eacl"
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/eacl"
|
||||||
|
@ -1480,9 +1482,14 @@ func createAccessBox(t *testing.T) (*accessbox.Box, *keys.PrivateKey) {
|
||||||
|
|
||||||
tok := new(session.Container)
|
tok := new(session.Container)
|
||||||
tok.ForVerb(session.VerbContainerSetEACL)
|
tok.ForVerb(session.VerbContainerSetEACL)
|
||||||
|
err = tok.Sign(key.PrivateKey)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
tok2 := new(session.Container)
|
tok2 := new(session.Container)
|
||||||
tok2.ForVerb(session.VerbContainerPut)
|
tok2.ForVerb(session.VerbContainerPut)
|
||||||
|
err = tok2.Sign(key.PrivateKey)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
box := &accessbox.Box{
|
box := &accessbox.Box{
|
||||||
Gate: &accessbox.GateData{
|
Gate: &accessbox.GateData{
|
||||||
SessionTokens: []*session.Container{tok, tok2},
|
SessionTokens: []*session.Container{tok, tok2},
|
||||||
|
@ -1493,18 +1500,28 @@ func createAccessBox(t *testing.T) (*accessbox.Box, *keys.PrivateKey) {
|
||||||
return box, key
|
return box, key
|
||||||
}
|
}
|
||||||
|
|
||||||
func createBucket(t *testing.T, tc *handlerContext, bktName string, box *accessbox.Box) *data.BucketInfo {
|
func createBucket(t *testing.T, hc *handlerContext, bktName string, box *accessbox.Box) *data.BucketInfo {
|
||||||
w, r := prepareTestRequest(tc, bktName, "", nil)
|
w := createBucketBase(hc, bktName, box)
|
||||||
ctx := context.WithValue(r.Context(), api.BoxData, box)
|
|
||||||
r = r.WithContext(ctx)
|
|
||||||
tc.Handler().CreateBucketHandler(w, r)
|
|
||||||
assertStatus(t, w, http.StatusOK)
|
assertStatus(t, w, http.StatusOK)
|
||||||
|
|
||||||
bktInfo, err := tc.Layer().GetBucketInfo(tc.Context(), bktName)
|
bktInfo, err := hc.Layer().GetBucketInfo(hc.Context(), bktName)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
return bktInfo
|
return bktInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func createBucketAssertS3Error(hc *handlerContext, bktName string, box *accessbox.Box, code s3errors.ErrorCode) {
|
||||||
|
w := createBucketBase(hc, bktName, box)
|
||||||
|
assertS3Error(hc.t, w, s3errors.GetAPIError(code))
|
||||||
|
}
|
||||||
|
|
||||||
|
func createBucketBase(hc *handlerContext, bktName string, box *accessbox.Box) *httptest.ResponseRecorder {
|
||||||
|
w, r := prepareTestRequest(hc, bktName, "", nil)
|
||||||
|
ctx := context.WithValue(r.Context(), api.BoxData, box)
|
||||||
|
r = r.WithContext(ctx)
|
||||||
|
hc.Handler().CreateBucketHandler(w, r)
|
||||||
|
return w
|
||||||
|
}
|
||||||
|
|
||||||
func putBucketACL(t *testing.T, tc *handlerContext, bktName string, box *accessbox.Box, header map[string]string) {
|
func putBucketACL(t *testing.T, tc *handlerContext, bktName string, box *accessbox.Box, header map[string]string) {
|
||||||
w, r := prepareTestRequest(tc, bktName, "", nil)
|
w, r := prepareTestRequest(tc, bktName, "", nil)
|
||||||
for key, val := range header {
|
for key, val := range header {
|
||||||
|
|
|
@ -3,6 +3,7 @@ package handler
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
"net/url"
|
"net/url"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
@ -431,31 +432,28 @@ func deleteBucket(t *testing.T, tc *handlerContext, bktName string, code int) {
|
||||||
assertStatus(t, w, code)
|
assertStatus(t, w, code)
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkNotFound(t *testing.T, tc *handlerContext, bktName, objName, version string) {
|
func checkNotFound(t *testing.T, hc *handlerContext, bktName, objName, version string) {
|
||||||
query := make(url.Values)
|
w := headObjectBase(hc, bktName, objName, version)
|
||||||
query.Add(api.QueryVersionID, version)
|
|
||||||
|
|
||||||
w, r := prepareTestFullRequest(tc, bktName, objName, query, nil)
|
|
||||||
tc.Handler().HeadObjectHandler(w, r)
|
|
||||||
assertStatus(t, w, http.StatusNotFound)
|
assertStatus(t, w, http.StatusNotFound)
|
||||||
}
|
}
|
||||||
|
|
||||||
func headObjectAssertS3Error(hc *handlerContext, bktName, objName, version string, code apiErrors.ErrorCode) {
|
func headObjectAssertS3Error(hc *handlerContext, bktName, objName, version string, code apiErrors.ErrorCode) {
|
||||||
|
w := headObjectBase(hc, bktName, objName, version)
|
||||||
|
assertS3Error(hc.t, w, apiErrors.GetAPIError(code))
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkFound(t *testing.T, hc *handlerContext, bktName, objName, version string) {
|
||||||
|
w := headObjectBase(hc, bktName, objName, version)
|
||||||
|
assertStatus(t, w, http.StatusOK)
|
||||||
|
}
|
||||||
|
|
||||||
|
func headObjectBase(hc *handlerContext, bktName, objName, version string) *httptest.ResponseRecorder {
|
||||||
query := make(url.Values)
|
query := make(url.Values)
|
||||||
query.Add(api.QueryVersionID, version)
|
query.Add(api.QueryVersionID, version)
|
||||||
|
|
||||||
w, r := prepareTestFullRequest(hc, bktName, objName, query, nil)
|
w, r := prepareTestFullRequest(hc, bktName, objName, query, nil)
|
||||||
hc.Handler().HeadObjectHandler(w, r)
|
hc.Handler().HeadObjectHandler(w, r)
|
||||||
assertS3Error(hc.t, w, apiErrors.GetAPIError(code))
|
return w
|
||||||
}
|
|
||||||
|
|
||||||
func checkFound(t *testing.T, tc *handlerContext, bktName, objName, version string) {
|
|
||||||
query := make(url.Values)
|
|
||||||
query.Add(api.QueryVersionID, version)
|
|
||||||
|
|
||||||
w, r := prepareTestFullRequest(tc, bktName, objName, query, nil)
|
|
||||||
tc.Handler().HeadObjectHandler(w, r)
|
|
||||||
assertStatus(t, w, http.StatusOK)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func listVersions(t *testing.T, tc *handlerContext, bktName string) *ListObjectsVersionsResponse {
|
func listVersions(t *testing.T, tc *handlerContext, bktName string) *ListObjectsVersionsResponse {
|
||||||
|
|
|
@ -16,7 +16,7 @@ import (
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api"
|
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/auth"
|
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/auth"
|
||||||
v4 "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/auth/signer/v4"
|
v4 "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/auth/signer/v4"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/errors"
|
s3errors "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/errors"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/layer"
|
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/layer"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/creds/accessbox"
|
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/creds/accessbox"
|
||||||
"github.com/aws/aws-sdk-go/aws/credentials"
|
"github.com/aws/aws-sdk-go/aws/credentials"
|
||||||
|
@ -166,7 +166,7 @@ func TestPutObjectWithStreamBodyError(t *testing.T) {
|
||||||
r.Header.Set(api.AmzContentSha256, api.StreamingContentSHA256)
|
r.Header.Set(api.AmzContentSha256, api.StreamingContentSHA256)
|
||||||
r.Header.Set(api.ContentEncoding, api.AwsChunked)
|
r.Header.Set(api.ContentEncoding, api.AwsChunked)
|
||||||
tc.Handler().PutObjectHandler(w, r)
|
tc.Handler().PutObjectHandler(w, r)
|
||||||
assertS3Error(t, w, errors.GetAPIError(errors.ErrMissingContentLength))
|
assertS3Error(t, w, s3errors.GetAPIError(s3errors.ErrMissingContentLength))
|
||||||
|
|
||||||
checkNotFound(t, tc, bktName, objName, emptyVersion)
|
checkNotFound(t, tc, bktName, objName, emptyVersion)
|
||||||
}
|
}
|
||||||
|
@ -239,3 +239,15 @@ func TestPutObjectWithStreamBodyAWSExample(t *testing.T) {
|
||||||
require.Equal(t, chunk[i], data[i])
|
require.Equal(t, chunk[i], data[i])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCreateBucket(t *testing.T) {
|
||||||
|
hc := prepareHandlerContext(t)
|
||||||
|
bktName := "bkt-name"
|
||||||
|
|
||||||
|
box, _ := createAccessBox(t)
|
||||||
|
createBucket(t, hc, bktName, box)
|
||||||
|
createBucketAssertS3Error(hc, bktName, box, s3errors.ErrBucketAlreadyOwnedByYou)
|
||||||
|
|
||||||
|
box2, _ := createAccessBox(t)
|
||||||
|
createBucketAssertS3Error(hc, bktName, box2, s3errors.ErrBucketAlreadyExists)
|
||||||
|
}
|
||||||
|
|
|
@ -2,10 +2,11 @@ package layer
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
errorsStd "errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/data"
|
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/data"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/errors"
|
s3errors "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (n *layer) GetObjectTaggingAndLock(ctx context.Context, objVersion *ObjectVersion, nodeVersion *data.NodeVersion) (map[string]string, *data.LockInfo, error) {
|
func (n *layer) GetObjectTaggingAndLock(ctx context.Context, objVersion *ObjectVersion, nodeVersion *data.NodeVersion) (map[string]string, *data.LockInfo, error) {
|
||||||
|
@ -28,8 +29,8 @@ func (n *layer) GetObjectTaggingAndLock(ctx context.Context, objVersion *ObjectV
|
||||||
|
|
||||||
tags, lockInfo, err = n.treeService.GetObjectTaggingAndLock(ctx, objVersion.BktInfo, nodeVersion)
|
tags, lockInfo, err = n.treeService.GetObjectTaggingAndLock(ctx, objVersion.BktInfo, nodeVersion)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errorsStd.Is(err, ErrNodeNotFound) {
|
if errors.Is(err, ErrNodeNotFound) {
|
||||||
return nil, nil, errors.GetAPIError(errors.ErrNoSuchKey)
|
return nil, nil, fmt.Errorf("%w: %s", s3errors.GetAPIError(s3errors.ErrNoSuchKey), err.Error())
|
||||||
}
|
}
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ import (
|
||||||
v2container "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/container"
|
v2container "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/container"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api"
|
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/data"
|
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/data"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/errors"
|
s3errors "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/errors"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client"
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container"
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container"
|
||||||
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||||
|
@ -43,10 +43,8 @@ func (n *layer) containerInfo(ctx context.Context, idCnr cid.ID) (*data.BucketIn
|
||||||
)
|
)
|
||||||
res, err = n.frostFS.Container(ctx, idCnr)
|
res, err = n.frostFS.Container(ctx, idCnr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("could not fetch container", zap.Error(err))
|
|
||||||
|
|
||||||
if client.IsErrContainerNotFound(err) {
|
if client.IsErrContainerNotFound(err) {
|
||||||
return nil, errors.GetAPIError(errors.ErrNoSuchBucket)
|
return nil, fmt.Errorf("%w: %s", s3errors.GetAPIError(s3errors.ErrNoSuchBucket), err.Error())
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf("get frostfs container: %w", err)
|
return nil, fmt.Errorf("get frostfs container: %w", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -73,7 +73,7 @@ func (n *layer) GetBucketCORS(ctx context.Context, bktInfo *data.BucketInfo) (*d
|
||||||
cors, err := n.getCORS(ctx, bktInfo)
|
cors, err := n.getCORS(ctx, bktInfo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errorsStd.Is(err, ErrNodeNotFound) {
|
if errorsStd.Is(err, ErrNodeNotFound) {
|
||||||
return nil, errors.GetAPIError(errors.ErrNoSuchCORSConfiguration)
|
return nil, fmt.Errorf("%w: %s", errors.GetAPIError(errors.ErrNoSuchCORSConfiguration), err.Error())
|
||||||
}
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -361,7 +361,7 @@ func (n *layer) GetBucketInfo(ctx context.Context, name string) (*data.BucketInf
|
||||||
containerID, err := n.ResolveBucket(ctx, name)
|
containerID, err := n.ResolveBucket(ctx, name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if strings.Contains(err.Error(), "not found") {
|
if strings.Contains(err.Error(), "not found") {
|
||||||
return nil, errors.GetAPIError(errors.ErrNoSuchBucket)
|
return nil, fmt.Errorf("%w: %s", errors.GetAPIError(errors.ErrNoSuchBucket), err.Error())
|
||||||
}
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ package layer
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
stderrors "errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"sort"
|
"sort"
|
||||||
|
@ -12,7 +12,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/data"
|
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/data"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/errors"
|
s3errors "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/errors"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/layer/encryption"
|
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/layer/encryption"
|
||||||
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
|
||||||
|
@ -174,14 +174,14 @@ func (n *layer) CreateMultipartUpload(ctx context.Context, p *CreateMultipartPar
|
||||||
func (n *layer) UploadPart(ctx context.Context, p *UploadPartParams) (string, error) {
|
func (n *layer) UploadPart(ctx context.Context, p *UploadPartParams) (string, error) {
|
||||||
multipartInfo, err := n.treeService.GetMultipartUpload(ctx, p.Info.Bkt, p.Info.Key, p.Info.UploadID)
|
multipartInfo, err := n.treeService.GetMultipartUpload(ctx, p.Info.Bkt, p.Info.Key, p.Info.UploadID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if stderrors.Is(err, ErrNodeNotFound) {
|
if errors.Is(err, ErrNodeNotFound) {
|
||||||
return "", errors.GetAPIError(errors.ErrNoSuchUpload)
|
return "", fmt.Errorf("%w: %s", s3errors.GetAPIError(s3errors.ErrNoSuchUpload), err.Error())
|
||||||
}
|
}
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
if p.Size > uploadMaxSize {
|
if p.Size > uploadMaxSize {
|
||||||
return "", errors.GetAPIError(errors.ErrEntityTooLarge)
|
return "", fmt.Errorf("%w: %d/%d", s3errors.GetAPIError(s3errors.ErrEntityTooLarge), p.Size, uploadMaxSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
objInfo, err := n.uploadPart(ctx, multipartInfo, p)
|
objInfo, err := n.uploadPart(ctx, multipartInfo, p)
|
||||||
|
@ -196,7 +196,7 @@ func (n *layer) uploadPart(ctx context.Context, multipartInfo *data.MultipartInf
|
||||||
encInfo := FormEncryptionInfo(multipartInfo.Meta)
|
encInfo := FormEncryptionInfo(multipartInfo.Meta)
|
||||||
if err := p.Info.Encryption.MatchObjectEncryption(encInfo); err != nil {
|
if err := p.Info.Encryption.MatchObjectEncryption(encInfo); err != nil {
|
||||||
n.reqLogger(ctx).Warn("mismatched obj encryptionInfo", zap.Error(err))
|
n.reqLogger(ctx).Warn("mismatched obj encryptionInfo", zap.Error(err))
|
||||||
return nil, errors.GetAPIError(errors.ErrInvalidEncryptionParameters)
|
return nil, s3errors.GetAPIError(s3errors.ErrInvalidEncryptionParameters)
|
||||||
}
|
}
|
||||||
|
|
||||||
bktInfo := p.Info.Bkt
|
bktInfo := p.Info.Bkt
|
||||||
|
@ -246,7 +246,7 @@ func (n *layer) uploadPart(ctx context.Context, multipartInfo *data.MultipartInf
|
||||||
}
|
}
|
||||||
|
|
||||||
oldPartID, err := n.treeService.AddPart(ctx, bktInfo, multipartInfo.ID, partInfo)
|
oldPartID, err := n.treeService.AddPart(ctx, bktInfo, multipartInfo.ID, partInfo)
|
||||||
oldPartIDNotFound := stderrors.Is(err, ErrNoNodeToRemove)
|
oldPartIDNotFound := errors.Is(err, ErrNoNodeToRemove)
|
||||||
if err != nil && !oldPartIDNotFound {
|
if err != nil && !oldPartIDNotFound {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -275,8 +275,8 @@ func (n *layer) uploadPart(ctx context.Context, multipartInfo *data.MultipartInf
|
||||||
func (n *layer) UploadPartCopy(ctx context.Context, p *UploadCopyParams) (*data.ObjectInfo, error) {
|
func (n *layer) UploadPartCopy(ctx context.Context, p *UploadCopyParams) (*data.ObjectInfo, error) {
|
||||||
multipartInfo, err := n.treeService.GetMultipartUpload(ctx, p.Info.Bkt, p.Info.Key, p.Info.UploadID)
|
multipartInfo, err := n.treeService.GetMultipartUpload(ctx, p.Info.Bkt, p.Info.Key, p.Info.UploadID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if stderrors.Is(err, ErrNodeNotFound) {
|
if errors.Is(err, ErrNodeNotFound) {
|
||||||
return nil, errors.GetAPIError(errors.ErrNoSuchUpload)
|
return nil, fmt.Errorf("%w: %s", s3errors.GetAPIError(s3errors.ErrNoSuchUpload), err.Error())
|
||||||
}
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -285,11 +285,11 @@ func (n *layer) UploadPartCopy(ctx context.Context, p *UploadCopyParams) (*data.
|
||||||
if p.Range != nil {
|
if p.Range != nil {
|
||||||
size = p.Range.End - p.Range.Start + 1
|
size = p.Range.End - p.Range.Start + 1
|
||||||
if p.Range.End > p.SrcObjInfo.Size {
|
if p.Range.End > p.SrcObjInfo.Size {
|
||||||
return nil, errors.GetAPIError(errors.ErrInvalidCopyPartRangeSource)
|
return nil, fmt.Errorf("%w: %d-%d/%d", s3errors.GetAPIError(s3errors.ErrInvalidCopyPartRangeSource), p.Range.Start, p.Range.End, p.SrcObjInfo.Size)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if size > uploadMaxSize {
|
if size > uploadMaxSize {
|
||||||
return nil, errors.GetAPIError(errors.ErrEntityTooLarge)
|
return nil, fmt.Errorf("%w: %d/%d", s3errors.GetAPIError(s3errors.ErrEntityTooLarge), size, uploadMaxSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
pr, pw := io.Pipe()
|
pr, pw := io.Pipe()
|
||||||
|
@ -333,7 +333,7 @@ type multiObjectReader struct {
|
||||||
func (x *multiObjectReader) Read(p []byte) (n int, err error) {
|
func (x *multiObjectReader) Read(p []byte) (n int, err error) {
|
||||||
if x.curReader != nil {
|
if x.curReader != nil {
|
||||||
n, err = x.curReader.Read(p)
|
n, err = x.curReader.Read(p)
|
||||||
if !stderrors.Is(err, io.EOF) {
|
if !errors.Is(err, io.EOF) {
|
||||||
return n, err
|
return n, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -359,7 +359,7 @@ func (x *multiObjectReader) Read(p []byte) (n int, err error) {
|
||||||
func (n *layer) CompleteMultipartUpload(ctx context.Context, p *CompleteMultipartParams) (*UploadData, *data.ExtendedObjectInfo, error) {
|
func (n *layer) CompleteMultipartUpload(ctx context.Context, p *CompleteMultipartParams) (*UploadData, *data.ExtendedObjectInfo, error) {
|
||||||
for i := 1; i < len(p.Parts); i++ {
|
for i := 1; i < len(p.Parts); i++ {
|
||||||
if p.Parts[i].PartNumber <= p.Parts[i-1].PartNumber {
|
if p.Parts[i].PartNumber <= p.Parts[i-1].PartNumber {
|
||||||
return nil, nil, errors.GetAPIError(errors.ErrInvalidPartOrder)
|
return nil, nil, s3errors.GetAPIError(s3errors.ErrInvalidPartOrder)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -370,7 +370,7 @@ func (n *layer) CompleteMultipartUpload(ctx context.Context, p *CompleteMultipar
|
||||||
encInfo := FormEncryptionInfo(multipartInfo.Meta)
|
encInfo := FormEncryptionInfo(multipartInfo.Meta)
|
||||||
|
|
||||||
if len(partsInfo) < len(p.Parts) {
|
if len(partsInfo) < len(p.Parts) {
|
||||||
return nil, nil, errors.GetAPIError(errors.ErrInvalidPart)
|
return nil, nil, fmt.Errorf("%w: found %d parts, need %d", s3errors.GetAPIError(s3errors.ErrInvalidPart), len(partsInfo), len(p.Parts))
|
||||||
}
|
}
|
||||||
|
|
||||||
var multipartObjetSize uint64
|
var multipartObjetSize uint64
|
||||||
|
@ -381,11 +381,11 @@ func (n *layer) CompleteMultipartUpload(ctx context.Context, p *CompleteMultipar
|
||||||
for i, part := range p.Parts {
|
for i, part := range p.Parts {
|
||||||
partInfo := partsInfo[part.PartNumber]
|
partInfo := partsInfo[part.PartNumber]
|
||||||
if partInfo == nil || part.ETag != partInfo.ETag {
|
if partInfo == nil || part.ETag != partInfo.ETag {
|
||||||
return nil, nil, errors.GetAPIError(errors.ErrInvalidPart)
|
return nil, nil, fmt.Errorf("%w: unknown part %d or etag mismatched", s3errors.GetAPIError(s3errors.ErrInvalidPart), part.PartNumber)
|
||||||
}
|
}
|
||||||
// for the last part we have no minimum size limit
|
// for the last part we have no minimum size limit
|
||||||
if i != len(p.Parts)-1 && partInfo.Size < uploadMinSize {
|
if i != len(p.Parts)-1 && partInfo.Size < uploadMinSize {
|
||||||
return nil, nil, errors.GetAPIError(errors.ErrEntityTooSmall)
|
return nil, nil, fmt.Errorf("%w: %d/%d", s3errors.GetAPIError(s3errors.ErrEntityTooSmall), partInfo.Size, uploadMinSize)
|
||||||
}
|
}
|
||||||
parts = append(parts, partInfo)
|
parts = append(parts, partInfo)
|
||||||
multipartObjetSize += partInfo.Size // even if encryption is enabled size is actual (decrypted)
|
multipartObjetSize += partInfo.Size // even if encryption is enabled size is actual (decrypted)
|
||||||
|
@ -455,7 +455,7 @@ func (n *layer) CompleteMultipartUpload(ctx context.Context, p *CompleteMultipar
|
||||||
zap.String("uploadKey", p.Info.Key),
|
zap.String("uploadKey", p.Info.Key),
|
||||||
zap.Error(err))
|
zap.Error(err))
|
||||||
|
|
||||||
return nil, nil, errors.GetAPIError(errors.ErrInternalError)
|
return nil, nil, s3errors.GetAPIError(s3errors.ErrInternalError)
|
||||||
}
|
}
|
||||||
|
|
||||||
var addr oid.Address
|
var addr oid.Address
|
||||||
|
@ -559,7 +559,7 @@ func (n *layer) ListParts(ctx context.Context, p *ListPartsParams) (*ListPartsIn
|
||||||
encInfo := FormEncryptionInfo(multipartInfo.Meta)
|
encInfo := FormEncryptionInfo(multipartInfo.Meta)
|
||||||
if err = p.Info.Encryption.MatchObjectEncryption(encInfo); err != nil {
|
if err = p.Info.Encryption.MatchObjectEncryption(encInfo); err != nil {
|
||||||
n.reqLogger(ctx).Warn("mismatched obj encryptionInfo", zap.Error(err))
|
n.reqLogger(ctx).Warn("mismatched obj encryptionInfo", zap.Error(err))
|
||||||
return nil, errors.GetAPIError(errors.ErrInvalidEncryptionParameters)
|
return nil, s3errors.GetAPIError(s3errors.ErrInvalidEncryptionParameters)
|
||||||
}
|
}
|
||||||
|
|
||||||
res.Owner = multipartInfo.Owner
|
res.Owner = multipartInfo.Owner
|
||||||
|
@ -602,8 +602,8 @@ func (n *layer) ListParts(ctx context.Context, p *ListPartsParams) (*ListPartsIn
|
||||||
func (n *layer) getUploadParts(ctx context.Context, p *UploadInfoParams) (*data.MultipartInfo, map[int]*data.PartInfo, error) {
|
func (n *layer) getUploadParts(ctx context.Context, p *UploadInfoParams) (*data.MultipartInfo, map[int]*data.PartInfo, error) {
|
||||||
multipartInfo, err := n.treeService.GetMultipartUpload(ctx, p.Bkt, p.Key, p.UploadID)
|
multipartInfo, err := n.treeService.GetMultipartUpload(ctx, p.Bkt, p.Key, p.UploadID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if stderrors.Is(err, ErrNodeNotFound) {
|
if errors.Is(err, ErrNodeNotFound) {
|
||||||
return nil, nil, errors.GetAPIError(errors.ErrNoSuchUpload)
|
return nil, nil, fmt.Errorf("%w: %s", s3errors.GetAPIError(s3errors.ErrNoSuchUpload), err.Error())
|
||||||
}
|
}
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -308,13 +308,13 @@ func (n *layer) headLastVersionIfNotDeleted(ctx context.Context, bkt *data.Bucke
|
||||||
node, err := n.treeService.GetLatestVersion(ctx, bkt, objectName)
|
node, err := n.treeService.GetLatestVersion(ctx, bkt, objectName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, ErrNodeNotFound) {
|
if errors.Is(err, ErrNodeNotFound) {
|
||||||
return nil, apiErrors.GetAPIError(apiErrors.ErrNoSuchKey)
|
return nil, fmt.Errorf("%w: %s", apiErrors.GetAPIError(apiErrors.ErrNoSuchKey), err.Error())
|
||||||
}
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if node.IsDeleteMarker() {
|
if node.IsDeleteMarker() {
|
||||||
return nil, apiErrors.GetAPIError(apiErrors.ErrNoSuchKey)
|
return nil, fmt.Errorf("%w: found version is delete marker", apiErrors.GetAPIError(apiErrors.ErrNoSuchKey))
|
||||||
}
|
}
|
||||||
|
|
||||||
meta, err := n.objectHead(ctx, bkt, node.OID)
|
meta, err := n.objectHead(ctx, bkt, node.OID)
|
||||||
|
@ -343,7 +343,7 @@ func (n *layer) headVersion(ctx context.Context, bkt *data.BucketInfo, p *HeadOb
|
||||||
foundVersion, err = n.treeService.GetUnversioned(ctx, bkt, p.Object)
|
foundVersion, err = n.treeService.GetUnversioned(ctx, bkt, p.Object)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, ErrNodeNotFound) {
|
if errors.Is(err, ErrNodeNotFound) {
|
||||||
return nil, apiErrors.GetAPIError(apiErrors.ErrNoSuchVersion)
|
return nil, fmt.Errorf("%w: %s", apiErrors.GetAPIError(apiErrors.ErrNoSuchVersion), err.Error())
|
||||||
}
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -360,7 +360,7 @@ func (n *layer) headVersion(ctx context.Context, bkt *data.BucketInfo, p *HeadOb
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if foundVersion == nil {
|
if foundVersion == nil {
|
||||||
return nil, apiErrors.GetAPIError(apiErrors.ErrNoSuchVersion)
|
return nil, fmt.Errorf("%w: there isn't tree node with requested version id", apiErrors.GetAPIError(apiErrors.ErrNoSuchVersion))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -372,7 +372,7 @@ func (n *layer) headVersion(ctx context.Context, bkt *data.BucketInfo, p *HeadOb
|
||||||
meta, err := n.objectHead(ctx, bkt, foundVersion.OID)
|
meta, err := n.objectHead(ctx, bkt, foundVersion.OID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if client.IsErrObjectNotFound(err) {
|
if client.IsErrObjectNotFound(err) {
|
||||||
return nil, apiErrors.GetAPIError(apiErrors.ErrNoSuchVersion)
|
return nil, fmt.Errorf("%w: %s", apiErrors.GetAPIError(apiErrors.ErrNoSuchVersion), err.Error())
|
||||||
}
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -167,7 +167,7 @@ func (n *layer) getCORS(ctx context.Context, bkt *data.BucketInfo) (*data.CORSCo
|
||||||
}
|
}
|
||||||
|
|
||||||
if objIDNotFound {
|
if objIDNotFound {
|
||||||
return nil, errors.GetAPIError(errors.ErrNoSuchCORSConfiguration)
|
return nil, fmt.Errorf("%w: %s", errors.GetAPIError(errors.ErrNoSuchCORSConfiguration), err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
obj, err := n.objectGet(ctx, bkt, objID)
|
obj, err := n.objectGet(ctx, bkt, objID)
|
||||||
|
|
|
@ -2,10 +2,11 @@ package layer
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
errorsStd "errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/data"
|
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/data"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/errors"
|
s3errors "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/errors"
|
||||||
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
||||||
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
|
||||||
|
@ -52,8 +53,8 @@ func (n *layer) GetObjectTagging(ctx context.Context, p *GetObjectTaggingParams)
|
||||||
|
|
||||||
tags, err := n.treeService.GetObjectTagging(ctx, p.ObjectVersion.BktInfo, nodeVersion)
|
tags, err := n.treeService.GetObjectTagging(ctx, p.ObjectVersion.BktInfo, nodeVersion)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errorsStd.Is(err, ErrNodeNotFound) {
|
if errors.Is(err, ErrNodeNotFound) {
|
||||||
return "", nil, errors.GetAPIError(errors.ErrNoSuchKey)
|
return "", nil, fmt.Errorf("%w: %s", s3errors.GetAPIError(s3errors.ErrNoSuchKey), err.Error())
|
||||||
}
|
}
|
||||||
return "", nil, err
|
return "", nil, err
|
||||||
}
|
}
|
||||||
|
@ -75,8 +76,8 @@ func (n *layer) PutObjectTagging(ctx context.Context, p *PutObjectTaggingParams)
|
||||||
|
|
||||||
err = n.treeService.PutObjectTagging(ctx, p.ObjectVersion.BktInfo, nodeVersion, p.TagSet)
|
err = n.treeService.PutObjectTagging(ctx, p.ObjectVersion.BktInfo, nodeVersion, p.TagSet)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errorsStd.Is(err, ErrNodeNotFound) {
|
if errors.Is(err, ErrNodeNotFound) {
|
||||||
return nil, errors.GetAPIError(errors.ErrNoSuchKey)
|
return nil, fmt.Errorf("%w: %s", s3errors.GetAPIError(s3errors.ErrNoSuchKey), err.Error())
|
||||||
}
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -94,8 +95,8 @@ func (n *layer) DeleteObjectTagging(ctx context.Context, p *ObjectVersion) (*dat
|
||||||
|
|
||||||
err = n.treeService.DeleteObjectTagging(ctx, p.BktInfo, version)
|
err = n.treeService.DeleteObjectTagging(ctx, p.BktInfo, version)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errorsStd.Is(err, ErrNodeNotFound) {
|
if errors.Is(err, ErrNodeNotFound) {
|
||||||
return nil, errors.GetAPIError(errors.ErrNoSuchKey)
|
return nil, fmt.Errorf("%w: %s", s3errors.GetAPIError(s3errors.ErrNoSuchKey), err.Error())
|
||||||
}
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -115,7 +116,7 @@ func (n *layer) GetBucketTagging(ctx context.Context, bktInfo *data.BucketInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
tags, err := n.treeService.GetBucketTagging(ctx, bktInfo)
|
tags, err := n.treeService.GetBucketTagging(ctx, bktInfo)
|
||||||
if err != nil && !errorsStd.Is(err, ErrNodeNotFound) {
|
if err != nil && !errors.Is(err, ErrNodeNotFound) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -168,12 +169,14 @@ func (n *layer) getNodeVersion(ctx context.Context, objVersion *ObjectVersion) (
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if version == nil {
|
if version == nil {
|
||||||
err = errors.GetAPIError(errors.ErrNoSuchVersion)
|
err = fmt.Errorf("%w: there isn't tree node with requested version id", s3errors.GetAPIError(s3errors.ErrNoSuchVersion))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err == nil && version.IsDeleteMarker() && !objVersion.NoErrorOnDeleteMarker || errorsStd.Is(err, ErrNodeNotFound) {
|
if err == nil && version.IsDeleteMarker() && !objVersion.NoErrorOnDeleteMarker {
|
||||||
return nil, errors.GetAPIError(errors.ErrNoSuchKey)
|
return nil, fmt.Errorf("%w: found version is delete marker", s3errors.GetAPIError(s3errors.ErrNoSuchKey))
|
||||||
|
} else if errors.Is(err, ErrNodeNotFound) {
|
||||||
|
return nil, fmt.Errorf("%w: %s", s3errors.GetAPIError(s3errors.ErrNoSuchKey), err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
if err == nil && version != nil && !version.IsDeleteMarker() {
|
if err == nil && version != nil && !version.IsDeleteMarker() {
|
||||||
|
|
Loading…
Reference in a new issue