satellite/metabase: better error message while move

Before this change we were returning full DB error message.
That can be very confusing for end user. This change is translating
error message into more user frindly version and fixes also DRPC
error status code.

Fixes https://github.com/storj/team-metainfo/issues/76

Change-Id: I29b06ab4ba50a0d14db7a822a2906d95d65ab524
This commit is contained in:
Michał Niewrzał 2022-01-27 11:30:45 +01:00
parent bc161794fc
commit 2e31ef3f29
6 changed files with 36 additions and 6 deletions

View File

@ -192,7 +192,7 @@ func (db *DB) BeginObjectExactVersion(ctx context.Context, opts BeginObjectExact
)
if err != nil {
if code := pgerrcode.FromError(err); code == pgxerrcode.UniqueViolation {
return Object{}, ErrConflict.New("object already exists")
return Object{}, Error.Wrap(ErrObjectAlreadyExists.New(""))
}
return Object{}, Error.New("unable to insert object: %w", err)
}

View File

@ -494,8 +494,7 @@ func TestBeginObjectExactVersion(t *testing.T) {
Encryption: metabasetest.DefaultEncryption,
},
Version: -1,
ErrClass: &metabase.ErrConflict,
ErrText: "object already exists",
ErrClass: &metabase.ErrObjectAlreadyExists,
}.Check(ctx, t, db)
metabasetest.Verify{
@ -545,8 +544,7 @@ func TestBeginObjectExactVersion(t *testing.T) {
Encryption: metabasetest.DefaultEncryption,
},
Version: -1,
ErrClass: &metabase.ErrConflict,
ErrText: "object already exists",
ErrClass: &metabase.ErrObjectAlreadyExists,
}.Check(ctx, t, db)
metabasetest.Verify{

View File

@ -19,6 +19,9 @@ import (
// Error is the default error for metabase.
var Error = errs.Class("metabase")
// ErrObjectAlreadyExists is used to indicate that object already exists.
var ErrObjectAlreadyExists = errs.Class("object already exists")
// Common constants for segment keys.
const (
Delimiter = '/'

View File

@ -8,9 +8,12 @@ import (
"database/sql"
"errors"
pgxerrcode "github.com/jackc/pgerrcode"
"storj.io/common/storj"
"storj.io/common/uuid"
"storj.io/private/dbutil/pgutil"
"storj.io/private/dbutil/pgutil/pgerrcode"
"storj.io/private/dbutil/txutil"
"storj.io/private/tagsql"
)
@ -167,7 +170,9 @@ func (db *DB) FinishMoveObject(ctx context.Context, opts FinishMoveObject) (err
var segmentsCount int
row := db.db.QueryRowContext(ctx, updateObjectsQuery, []byte(opts.NewBucket), opts.NewEncryptedObjectKey, opts.NewEncryptedMetadataKey, opts.NewEncryptedMetadataKeyNonce, opts.ProjectID, []byte(opts.BucketName), opts.ObjectKey, opts.Version, opts.StreamID)
if err = row.Scan(&segmentsCount); err != nil {
if errors.Is(err, sql.ErrNoRows) {
if code := pgerrcode.FromError(err); code == pgxerrcode.UniqueViolation {
return Error.Wrap(ErrObjectAlreadyExists.New(""))
} else if errors.Is(err, sql.ErrNoRows) {
return storj.ErrObjectNotFound.New("object not found")
}
return Error.New("unable to update object: %w", err)

View File

@ -190,6 +190,28 @@ func TestFinishMoveObject(t *testing.T) {
metabasetest.Verify{}.Check(ctx, t, db)
})
t.Run("object already exists", func(t *testing.T) {
defer metabasetest.DeleteAll{}.Check(ctx, t, db)
moveObjStream := metabasetest.RandObjectStream()
metabasetest.CreateObject(ctx, t, db, moveObjStream, 0)
conflictObjStream := metabasetest.RandObjectStream()
conflictObjStream.ProjectID = moveObjStream.ProjectID
metabasetest.CreateObject(ctx, t, db, conflictObjStream, 0)
metabasetest.FinishMoveObject{
Opts: metabase.FinishMoveObject{
NewBucket: conflictObjStream.BucketName,
ObjectStream: moveObjStream,
NewEncryptedObjectKey: []byte(conflictObjStream.ObjectKey),
NewEncryptedMetadataKeyNonce: testrand.Nonce().Bytes(),
NewEncryptedMetadataKey: testrand.Bytes(265),
},
ErrClass: &metabase.ErrObjectAlreadyExists,
}.Check(ctx, t, db)
})
t.Run("object does not exist", func(t *testing.T) {
defer metabasetest.DeleteAll{}.Check(ctx, t, db)

View File

@ -278,6 +278,8 @@ func (endpoint *Endpoint) convertMetabaseErr(err error) error {
return rpcstatus.Error(rpcstatus.NotFound, err.Error())
case metabase.ErrInvalidRequest.Has(err):
return rpcstatus.Error(rpcstatus.InvalidArgument, err.Error())
case metabase.ErrObjectAlreadyExists.Has(err):
return rpcstatus.Error(rpcstatus.AlreadyExists, err.Error())
default:
endpoint.log.Error("internal", zap.Error(err))
return rpcstatus.Error(rpcstatus.Internal, err.Error())