[#542] Fix object removal

Signed-off-by: Denis Kirillov <denis@nspcc.ru>
This commit is contained in:
Denis Kirillov 2022-06-24 15:39:30 +03:00 committed by Alex Vanin
parent 88c392d024
commit fdf0974679
5 changed files with 268 additions and 181 deletions

View file

@ -78,16 +78,14 @@ func (h *handler) DeleteObjectHandler(w http.ResponseWriter, r *http.Request) {
} }
p := &layer.DeleteObjectParams{ p := &layer.DeleteObjectParams{
BktInfo: bktInfo, BktInfo: bktInfo,
Objects: versionedObject, Objects: versionedObject,
Settings: bktSettings,
} }
deletedObjects, err := h.obj.DeleteObjects(r.Context(), p) deletedObjects := h.obj.DeleteObjects(r.Context(), p)
deletedObject := deletedObjects[0] deletedObject := deletedObjects[0]
if err == nil { if deletedObject.Error != nil {
err = deletedObject.Error if isErrObjectLocked(deletedObject.Error) {
}
if err != nil {
if isErrObjectLocked(err) {
h.logAndSendError(w, "object is locked", reqInfo, errors.GetAPIError(errors.ErrAccessDenied)) h.logAndSendError(w, "object is locked", reqInfo, errors.GetAPIError(errors.ErrAccessDenied))
return return
} }
@ -95,7 +93,7 @@ func (h *handler) DeleteObjectHandler(w http.ResponseWriter, r *http.Request) {
zap.String("request_id", reqInfo.RequestID), zap.String("request_id", reqInfo.RequestID),
zap.String("bucket_name", reqInfo.BucketName), zap.String("bucket_name", reqInfo.BucketName),
zap.String("object_name", reqInfo.ObjectName), zap.String("object_name", reqInfo.ObjectName),
zap.Error(err)) zap.Error(deletedObject.Error))
} }
var m *SendNotificationParams var m *SendNotificationParams
@ -198,6 +196,12 @@ func (h *handler) DeleteMultipleObjectsHandler(w http.ResponseWriter, r *http.Re
return return
} }
bktSettings, err := h.obj.GetBucketSettings(r.Context(), bktInfo)
if err != nil {
h.logAndSendError(w, "could not get bucket settings", reqInfo, err)
return
}
marshaler := zapcore.ArrayMarshalerFunc(func(encoder zapcore.ArrayEncoder) error { marshaler := zapcore.ArrayMarshalerFunc(func(encoder zapcore.ArrayEncoder) error {
for _, obj := range toRemove { for _, obj := range toRemove {
encoder.AppendString(obj.String()) encoder.AppendString(obj.String())
@ -206,14 +210,11 @@ func (h *handler) DeleteMultipleObjectsHandler(w http.ResponseWriter, r *http.Re
}) })
p := &layer.DeleteObjectParams{ p := &layer.DeleteObjectParams{
BktInfo: bktInfo, BktInfo: bktInfo,
Objects: toRemove, Objects: toRemove,
} Settings: bktSettings,
deletedObjects, err := h.obj.DeleteObjects(r.Context(), p)
if !requested.Quiet && err != nil {
h.logAndSendError(w, "couldn't delete objects", reqInfo, err)
return
} }
deletedObjects := h.obj.DeleteObjects(r.Context(), p)
var errs []error var errs []error
for _, obj := range deletedObjects { for _, obj := range deletedObjects {

View file

@ -1,142 +1,187 @@
package handler package handler
import ( import (
"context"
"io"
"net/http" "net/http"
"net/url" "net/url"
"testing" "testing"
"github.com/nspcc-dev/neofs-s3-gw/api" "github.com/nspcc-dev/neofs-s3-gw/api"
"github.com/nspcc-dev/neofs-s3-gw/api/layer" "github.com/nspcc-dev/neofs-s3-gw/api/data"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
const (
emptyVersion = ""
)
func TestDeleteObject(t *testing.T) { func TestDeleteObject(t *testing.T) {
ctx := context.Background()
tc := prepareHandlerContext(t) tc := prepareHandlerContext(t)
bktName := "bucket-for-removal" bktName, objName := "bucket-for-removal", "object-to-delete"
createTestBucket(ctx, t, tc, bktName) bktInfo, objInfo := createBucketAndObject(t, tc, bktName, objName)
bktInfo, err := tc.Layer().GetBucketInfo(ctx, bktName)
require.NoError(t, err)
objName := "object" checkFound(t, tc, bktName, objName, emptyVersion)
objInfo := createTestObject(ctx, t, tc, bktInfo, objName) deleteObject(t, tc, bktName, objName, emptyVersion)
checkNotFound(t, tc, bktName, objName, emptyVersion)
w, r := prepareTestRequest(t, bktName, objName, nil) require.False(t, existInMockedNeoFS(tc, bktInfo, objInfo))
tc.Handler().HeadObjectHandler(w, r)
assertStatus(t, w, http.StatusOK)
w, r = prepareTestRequest(t, bktName, objName, nil)
tc.Handler().DeleteObjectHandler(w, r)
assertStatus(t, w, http.StatusNoContent)
w, r = prepareTestRequest(t, bktName, objName, nil)
tc.Handler().HeadObjectHandler(w, r)
assertStatus(t, w, http.StatusNotFound)
p := &layer.GetObjectParams{
BucketInfo: bktInfo,
ObjectInfo: objInfo,
Writer: io.Discard,
}
err = tc.Layer().GetObject(ctx, p)
require.Error(t, err)
} }
func TestDeleteObjectVersioned(t *testing.T) { func TestDeleteObjectVersioned(t *testing.T) {
ctx := context.Background()
tc := prepareHandlerContext(t) tc := prepareHandlerContext(t)
bktName := "bucket-for-removal" bktName, objName := "bucket-for-removal", "object-to-delete"
createTestBucket(ctx, t, tc, bktName) bktInfo, objInfo := createVersionedBucketAndObject(t, tc, bktName, objName)
bktInfo, err := tc.Layer().GetBucketInfo(ctx, bktName)
require.NoError(t, err)
cfg := &VersioningConfiguration{Status: "Enabled"} checkFound(t, tc, bktName, objName, emptyVersion)
w, r := prepareTestRequest(t, bktName, "", cfg) deleteObject(t, tc, bktName, objName, emptyVersion)
tc.Handler().PutBucketVersioningHandler(w, r) checkNotFound(t, tc, bktName, objName, emptyVersion)
assertStatus(t, w, http.StatusOK)
objName := "object" checkFound(t, tc, bktName, objName, objInfo.Version())
objInfo := createTestObject(ctx, t, tc, bktInfo, objName) deleteObject(t, tc, bktName, objName, objInfo.Version())
checkNotFound(t, tc, bktName, objName, objInfo.Version())
w, r = prepareTestRequest(t, bktName, objName, nil) require.False(t, existInMockedNeoFS(tc, bktInfo, objInfo), "object exists but shouldn't")
tc.Handler().HeadObjectHandler(w, r) }
assertStatus(t, w, http.StatusOK)
w, r = prepareTestRequest(t, bktName, objName, nil) func TestRemoveDeleteMarker(t *testing.T) {
tc.Handler().DeleteObjectHandler(w, r) tc := prepareHandlerContext(t)
assertStatus(t, w, http.StatusNoContent)
w, r = prepareTestRequest(t, bktName, objName, nil) bktName, objName := "bucket-for-removal", "object-to-delete"
tc.Handler().HeadObjectHandler(w, r) bktInfo, objInfo := createVersionedBucketAndObject(t, tc, bktName, objName)
assertStatus(t, w, http.StatusNotFound)
query := make(url.Values) checkFound(t, tc, bktName, objName, emptyVersion)
query.Add(api.QueryVersionID, objInfo.Version()) deleteMarkerVersion := deleteObject(t, tc, bktName, objName, emptyVersion)
checkNotFound(t, tc, bktName, objName, emptyVersion)
w, r = prepareTestFullRequest(t, bktName, objName, query, nil) checkFound(t, tc, bktName, objName, objInfo.Version())
tc.Handler().HeadObjectHandler(w, r) deleteObject(t, tc, bktName, objName, deleteMarkerVersion)
assertStatus(t, w, http.StatusOK) checkNotFound(t, tc, bktName, objName, emptyVersion)
w, r = prepareTestFullRequest(t, bktName, objName, query, nil) require.True(t, existInMockedNeoFS(tc, bktInfo, objInfo), "object doesn't exist but should")
r.URL.RawQuery = query.Encode()
tc.Handler().DeleteObjectHandler(w, r)
assertStatus(t, w, http.StatusNoContent)
p := &layer.GetObjectParams{
BucketInfo: bktInfo,
ObjectInfo: objInfo,
Writer: io.Discard,
}
err = tc.Layer().GetObject(ctx, p)
require.Error(t, err)
} }
func TestDeleteObjectCombined(t *testing.T) { func TestDeleteObjectCombined(t *testing.T) {
ctx := context.Background()
tc := prepareHandlerContext(t) tc := prepareHandlerContext(t)
bktName := "bucket-for-removal" bktName, objName := "bucket-for-removal", "object-to-delete"
createTestBucket(ctx, t, tc, bktName) bktInfo, objInfo := createBucketAndObject(t, tc, bktName, objName)
bktInfo, err := tc.Layer().GetBucketInfo(ctx, bktName)
putBucketVersioning(t, tc, bktName, true)
checkFound(t, tc, bktName, objName, emptyVersion)
deleteObject(t, tc, bktName, objName, emptyVersion)
checkNotFound(t, tc, bktName, objName, emptyVersion)
checkFound(t, tc, bktName, objName, objInfo.Version())
require.True(t, existInMockedNeoFS(tc, bktInfo, objInfo), "object doesn't exist but should")
}
func TestDeleteObjectSuspended(t *testing.T) {
tc := prepareHandlerContext(t)
bktName, objName := "bucket-for-removal", "object-to-delete"
bktInfo, objInfo := createBucketAndObject(t, tc, bktName, objName)
putBucketVersioning(t, tc, bktName, true)
checkFound(t, tc, bktName, objName, emptyVersion)
deleteObject(t, tc, bktName, objName, emptyVersion)
checkNotFound(t, tc, bktName, objName, emptyVersion)
putBucketVersioning(t, tc, bktName, false)
deleteObject(t, tc, bktName, objName, emptyVersion)
checkNotFound(t, tc, bktName, objName, objInfo.Version())
require.False(t, existInMockedNeoFS(tc, bktInfo, objInfo), "object exists but shouldn't")
}
func TestDeleteMarkers(t *testing.T) {
tc := prepareHandlerContext(t)
bktName, objName := "bucket-for-removal", "object-to-delete"
createTestBucket(tc.Context(), t, tc, bktName)
putBucketVersioning(t, tc, bktName, true)
checkNotFound(t, tc, bktName, objName, emptyVersion)
deleteObject(t, tc, bktName, objName, emptyVersion)
deleteObject(t, tc, bktName, objName, emptyVersion)
deleteObject(t, tc, bktName, objName, emptyVersion)
versions := listVersions(t, tc, bktName)
require.Len(t, versions.DeleteMarker, 3, "invalid delete markers length")
require.Len(t, versions.Version, 0, "versions must be empty")
require.Len(t, listOIDsFromMockedNeoFS(t, tc, bktName, objName), 0, "shouldn't be any object in neofs")
}
func createBucketAndObject(t *testing.T, tc *handlerContext, bktName, objName string) (*data.BucketInfo, *data.ObjectInfo) {
createTestBucket(tc.Context(), t, tc, bktName)
bktInfo, err := tc.Layer().GetBucketInfo(tc.Context(), bktName)
require.NoError(t, err) require.NoError(t, err)
objName := "object" objInfo := createTestObject(tc.Context(), t, tc, bktInfo, objName)
objInfo := createTestObject(ctx, t, tc, bktInfo, objName)
w, r := prepareTestRequest(t, bktName, objName, nil) return bktInfo, objInfo
tc.Handler().HeadObjectHandler(w, r) }
assertStatus(t, w, http.StatusOK)
cfg := &VersioningConfiguration{Status: "Enabled"} func createVersionedBucketAndObject(t *testing.T, tc *handlerContext, bktName, objName string) (*data.BucketInfo, *data.ObjectInfo) {
w, r = prepareTestRequest(t, bktName, objName, cfg) createTestBucket(tc.Context(), t, tc, bktName)
bktInfo, err := tc.Layer().GetBucketInfo(tc.Context(), bktName)
require.NoError(t, err)
putBucketVersioning(t, tc, bktName, true)
objInfo := createTestObject(tc.Context(), t, tc, bktInfo, objName)
return bktInfo, objInfo
}
func putBucketVersioning(t *testing.T, tc *handlerContext, bktName string, enabled bool) {
cfg := &VersioningConfiguration{Status: "Suspended"}
if enabled {
cfg.Status = "Enabled"
}
w, r := prepareTestRequest(t, bktName, "", cfg)
tc.Handler().PutBucketVersioningHandler(w, r) tc.Handler().PutBucketVersioningHandler(w, r)
assertStatus(t, w, http.StatusOK) assertStatus(t, w, http.StatusOK)
}
w, r = prepareTestRequest(t, bktName, objName, nil) func deleteObject(t *testing.T, tc *handlerContext, bktName, objName, version string) string {
query := make(url.Values)
query.Add(api.QueryVersionID, version)
w, r := prepareTestFullRequest(t, bktName, objName, query, nil)
tc.Handler().DeleteObjectHandler(w, r) tc.Handler().DeleteObjectHandler(w, r)
assertStatus(t, w, http.StatusNoContent) assertStatus(t, w, http.StatusNoContent)
w, r = prepareTestRequest(t, bktName, objName, nil) return w.Header().Get(api.AmzVersionID)
}
func checkNotFound(t *testing.T, tc *handlerContext, bktName, objName, version string) {
query := make(url.Values)
query.Add(api.QueryVersionID, version)
w, r := prepareTestFullRequest(t, bktName, objName, query, nil)
tc.Handler().HeadObjectHandler(w, r) tc.Handler().HeadObjectHandler(w, r)
assertStatus(t, w, http.StatusNotFound) assertStatus(t, w, http.StatusNotFound)
}
query := make(url.Values)
query.Add(api.QueryVersionID, objInfo.Version()) func checkFound(t *testing.T, tc *handlerContext, bktName, objName, version string) {
query := make(url.Values)
w, r = prepareTestFullRequest(t, bktName, objName, query, nil) query.Add(api.QueryVersionID, version)
tc.Handler().HeadObjectHandler(w, r)
assertStatus(t, w, http.StatusNotFound) // because we remove null version w, r := prepareTestFullRequest(t, bktName, objName, query, nil)
tc.Handler().HeadObjectHandler(w, r)
p := &layer.GetObjectParams{ assertStatus(t, w, http.StatusOK)
BucketInfo: bktInfo, }
ObjectInfo: objInfo,
Writer: io.Discard, func listVersions(t *testing.T, tc *handlerContext, bktName string) *ListObjectsVersionsResponse {
} w, r := prepareTestRequest(t, bktName, "", nil)
err = tc.Layer().GetObject(ctx, p) tc.Handler().ListBucketObjectVersionsHandler(w, r)
require.Error(t, err) assertStatus(t, w, http.StatusOK)
res := &ListObjectsVersionsResponse{}
parseTestResponse(t, w, res)
return res
} }

View file

@ -20,6 +20,7 @@ import (
"github.com/nspcc-dev/neofs-s3-gw/api/resolver" "github.com/nspcc-dev/neofs-s3-gw/api/resolver"
cid "github.com/nspcc-dev/neofs-sdk-go/container/id" cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
"github.com/nspcc-dev/neofs-sdk-go/object" "github.com/nspcc-dev/neofs-sdk-go/object"
oid "github.com/nspcc-dev/neofs-sdk-go/object/id"
"github.com/nspcc-dev/neofs-sdk-go/user" "github.com/nspcc-dev/neofs-sdk-go/user"
usertest "github.com/nspcc-dev/neofs-sdk-go/user/test" usertest "github.com/nspcc-dev/neofs-sdk-go/user/test"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
@ -27,8 +28,9 @@ import (
) )
type handlerContext struct { type handlerContext struct {
h *handler h *handler
tp *layer.TestNeoFS tp *layer.TestNeoFS
context context.Context
} }
func (hc *handlerContext) Handler() *handler { func (hc *handlerContext) Handler() *handler {
@ -43,6 +45,10 @@ func (hc *handlerContext) Layer() layer.Client {
return hc.h.obj return hc.h.obj
} }
func (hc *handlerContext) Context() context.Context {
return hc.context
}
func prepareHandlerContext(t *testing.T) *handlerContext { func prepareHandlerContext(t *testing.T) *handlerContext {
key, err := keys.NewPrivateKey() key, err := keys.NewPrivateKey()
require.NoError(t, err) require.NoError(t, err)
@ -69,8 +75,9 @@ func prepareHandlerContext(t *testing.T) *handlerContext {
} }
return &handlerContext{ return &handlerContext{
h: h, h: h,
tp: tp, tp: tp,
context: context.Background(),
} }
} }
@ -167,3 +174,29 @@ func parseTestResponse(t *testing.T, response *httptest.ResponseRecorder, body i
err := xml.NewDecoder(response.Result().Body).Decode(body) err := xml.NewDecoder(response.Result().Body).Decode(body)
require.NoError(t, err) require.NoError(t, err)
} }
func existInMockedNeoFS(tc *handlerContext, bktInfo *data.BucketInfo, objInfo *data.ObjectInfo) bool {
p := &layer.GetObjectParams{
BucketInfo: bktInfo,
ObjectInfo: objInfo,
Writer: io.Discard,
}
return tc.Layer().GetObject(tc.Context(), p) == nil
}
func listOIDsFromMockedNeoFS(t *testing.T, tc *handlerContext, bktName, objectName string) []oid.ID {
bktInfo, err := tc.Layer().GetBucketInfo(tc.Context(), bktName)
require.NoError(t, err)
p := layer.PrmObjectSelect{
Container: bktInfo.CID,
ExactAttribute: [2]string{
object.AttributeFileName, objectName,
},
}
ids, err := tc.MockedPool().SelectObjects(tc.Context(), p)
require.NoError(t, err)
return ids
}

View file

@ -115,8 +115,9 @@ type (
} }
DeleteObjectParams struct { DeleteObjectParams struct {
BktInfo *data.BucketInfo BktInfo *data.BucketInfo
Objects []*VersionedObject Objects []*VersionedObject
Settings *data.BucketSettings
} }
// PutSettingsParams stores object copy request parameters. // PutSettingsParams stores object copy request parameters.
@ -236,7 +237,7 @@ type (
ListObjectsV2(ctx context.Context, p *ListObjectsParamsV2) (*ListObjectsInfoV2, error) ListObjectsV2(ctx context.Context, p *ListObjectsParamsV2) (*ListObjectsInfoV2, error)
ListObjectVersions(ctx context.Context, p *ListObjectVersionsParams) (*ListObjectVersionsInfo, error) ListObjectVersions(ctx context.Context, p *ListObjectVersionsParams) (*ListObjectVersionsInfo, error)
DeleteObjects(ctx context.Context, p *DeleteObjectParams) ([]*VersionedObject, error) DeleteObjects(ctx context.Context, p *DeleteObjectParams) []*VersionedObject
CreateMultipartUpload(ctx context.Context, p *CreateMultipartParams) error CreateMultipartUpload(ctx context.Context, p *CreateMultipartParams) error
CompleteMultipartUpload(ctx context.Context, p *CompleteMultipartParams) (*UploadData, *data.ObjectInfo, error) CompleteMultipartUpload(ctx context.Context, p *CompleteMultipartParams) (*UploadData, *data.ObjectInfo, error)
@ -473,55 +474,10 @@ func getRandomOID() (oid.ID, error) {
return objID, nil return objID, nil
} }
// DeleteObject removes all objects with the passed nice name. func (n *layer) deleteObject(ctx context.Context, bkt *data.BucketInfo, settings *data.BucketSettings, obj *VersionedObject) *VersionedObject {
func (n *layer) deleteObject(ctx context.Context, bkt *data.BucketInfo, obj *VersionedObject) *VersionedObject { if len(obj.VersionID) != 0 {
if len(obj.VersionID) == 0 { var nodeVersion *data.NodeVersion
obj.VersionID = UnversionedObjectVersionID if nodeVersion, obj.Error = n.getNodeVersionToDelete(ctx, bkt, obj); obj.Error != nil {
}
objVersion := &ObjectVersion{
BktInfo: bkt,
ObjectName: obj.Name,
VersionID: obj.VersionID,
NoErrorOnDeleteMarker: true,
}
var nodeVersion *data.NodeVersion
nodeVersion, obj.Error = n.getNodeVersion(ctx, objVersion)
if obj.VersionID == UnversionedObjectVersionID {
if obj.Error == nil {
if obj.DeleteMarkVersion, obj.Error = n.removeOldVersion(ctx, bkt, nodeVersion, obj); obj.Error != nil {
return obj
}
} else if !errors.IsS3Error(obj.Error, errors.ErrNoSuchKey) {
return obj
}
randOID, err := getRandomOID()
if err != nil {
obj.Error = fmt.Errorf("couldn't get random oid: %w", err)
return obj
}
newVersion := &data.NodeVersion{
BaseNodeVersion: data.BaseNodeVersion{
OID: randOID,
FilePath: obj.Name,
},
DeleteMarker: &data.DeleteMarkerInfo{
Created: time.Now(),
Owner: n.Owner(ctx),
},
IsUnversioned: true,
}
if obj.Error = n.treeService.AddVersion(ctx, bkt.CID, newVersion); obj.Error != nil {
return obj
}
n.namesCache.Delete(bkt.Name + "/" + obj.Name)
} else {
if obj.Error != nil {
return obj return obj
} }
@ -529,16 +485,65 @@ func (n *layer) deleteObject(ctx context.Context, bkt *data.BucketInfo, obj *Ver
return obj return obj
} }
if obj.Error = n.treeService.RemoveVersion(ctx, bkt.CID, nodeVersion.ID); obj.Error != nil { obj.Error = n.treeService.RemoveVersion(ctx, bkt.CID, nodeVersion.ID)
return obj
}
var newVersion *data.NodeVersion
if !settings.VersioningEnabled {
obj.VersionID = UnversionedObjectVersionID
var nodeVersion *data.NodeVersion
if nodeVersion, obj.Error = n.getNodeVersionToDelete(ctx, bkt, obj); obj.Error != nil {
return obj
}
if obj.Error == nil {
if obj.DeleteMarkVersion, obj.Error = n.removeOldVersion(ctx, bkt, nodeVersion, obj); obj.Error != nil {
return obj
}
} else if !errors.IsS3Error(obj.Error, errors.ErrNoSuchKey) {
return obj return obj
} }
} }
n.listsCache.CleanCacheEntriesContainingObject(obj.Name, bkt.CID) randOID, err := getRandomOID()
if err != nil {
obj.Error = fmt.Errorf("couldn't get random oid: %w", err)
return obj
}
newVersion = &data.NodeVersion{
BaseNodeVersion: data.BaseNodeVersion{
OID: randOID,
FilePath: obj.Name,
},
DeleteMarker: &data.DeleteMarkerInfo{
Created: time.Now(),
Owner: n.Owner(ctx),
},
IsUnversioned: !settings.VersioningEnabled,
}
if obj.Error = n.treeService.AddVersion(ctx, bkt.CID, newVersion); obj.Error != nil {
return obj
}
n.namesCache.Delete(bkt.Name + "/" + obj.Name)
return obj return obj
} }
func (n *layer) getNodeVersionToDelete(ctx context.Context, bkt *data.BucketInfo, obj *VersionedObject) (*data.NodeVersion, error) {
objVersion := &ObjectVersion{
BktInfo: bkt,
ObjectName: obj.Name,
VersionID: obj.VersionID,
NoErrorOnDeleteMarker: true,
}
return n.getNodeVersion(ctx, objVersion)
}
func (n *layer) removeOldVersion(ctx context.Context, bkt *data.BucketInfo, nodeVersion *data.NodeVersion, obj *VersionedObject) (string, error) { func (n *layer) removeOldVersion(ctx context.Context, bkt *data.BucketInfo, nodeVersion *data.NodeVersion, obj *VersionedObject) (string, error) {
if nodeVersion.DeleteMarker != nil { if nodeVersion.DeleteMarker != nil {
return obj.VersionID, nil return obj.VersionID, nil
@ -548,12 +553,12 @@ func (n *layer) removeOldVersion(ctx context.Context, bkt *data.BucketInfo, node
} }
// DeleteObjects from the storage. // DeleteObjects from the storage.
func (n *layer) DeleteObjects(ctx context.Context, p *DeleteObjectParams) ([]*VersionedObject, error) { func (n *layer) DeleteObjects(ctx context.Context, p *DeleteObjectParams) []*VersionedObject {
for i, obj := range p.Objects { for i, obj := range p.Objects {
p.Objects[i] = n.deleteObject(ctx, p.BktInfo, obj) p.Objects[i] = n.deleteObject(ctx, p.BktInfo, p.Settings, obj)
} }
return p.Objects, nil return p.Objects
} }
func (n *layer) CreateBucket(ctx context.Context, p *CreateBucketParams) (*data.BucketInfo, error) { func (n *layer) CreateBucket(ctx context.Context, p *CreateBucketParams) (*data.BucketInfo, error) {

View file

@ -53,15 +53,15 @@ func (tc *testContext) getObject(objectName, versionID string, needError bool) (
return extendedInfo.ObjectInfo, content.Bytes() return extendedInfo.ObjectInfo, content.Bytes()
} }
func (tc *testContext) deleteObject(objectName, versionID string) { func (tc *testContext) deleteObject(objectName, versionID string, settings *data.BucketSettings) {
p := &DeleteObjectParams{ p := &DeleteObjectParams{
BktInfo: tc.bktInfo, BktInfo: tc.bktInfo,
Settings: settings,
Objects: []*VersionedObject{ Objects: []*VersionedObject{
{Name: objectName, VersionID: versionID}, {Name: objectName, VersionID: versionID},
}, },
} }
deletedObjects, err := tc.layer.DeleteObjects(tc.ctx, p) deletedObjects := tc.layer.DeleteObjects(tc.ctx, p)
require.NoError(tc.t, err)
for _, obj := range deletedObjects { for _, obj := range deletedObjects {
require.NoError(tc.t, obj.Error) require.NoError(tc.t, obj.Error)
} }
@ -230,7 +230,7 @@ func TestVersioningDeleteObject(t *testing.T) {
tc.putObject([]byte("content obj1 v1")) tc.putObject([]byte("content obj1 v1"))
tc.putObject([]byte("content obj1 v2")) tc.putObject([]byte("content obj1 v2"))
tc.deleteObject(tc.obj, "") tc.deleteObject(tc.obj, "", settings)
tc.getObject(tc.obj, "", true) tc.getObject(tc.obj, "", true)
tc.checkListObjects() tc.checkListObjects()
@ -268,19 +268,19 @@ func TestVersioningDeleteSpecificObjectVersion(t *testing.T) {
objV3Content := []byte("content obj1 v3") objV3Content := []byte("content obj1 v3")
objV3Info := tc.putObject(objV3Content) objV3Info := tc.putObject(objV3Content)
tc.deleteObject(tc.obj, objV2Info.Version()) tc.deleteObject(tc.obj, objV2Info.Version(), settings)
tc.getObject(tc.obj, objV2Info.Version(), true) tc.getObject(tc.obj, objV2Info.Version(), true)
_, buffer3 := tc.getObject(tc.obj, "", false) _, buffer3 := tc.getObject(tc.obj, "", false)
require.Equal(t, objV3Content, buffer3) require.Equal(t, objV3Content, buffer3)
tc.deleteObject(tc.obj, "") tc.deleteObject(tc.obj, "", settings)
tc.getObject(tc.obj, "", true) tc.getObject(tc.obj, "", true)
versions := tc.listVersions() versions := tc.listVersions()
for _, ver := range versions.DeleteMarker { for _, ver := range versions.DeleteMarker {
if ver.IsLatest { if ver.IsLatest {
tc.deleteObject(tc.obj, ver.Object.Version()) tc.deleteObject(tc.obj, ver.Object.Version(), settings)
} }
} }
@ -295,7 +295,10 @@ func TestNoVersioningDeleteObject(t *testing.T) {
tc.putObject([]byte("content obj1 v1")) tc.putObject([]byte("content obj1 v1"))
tc.putObject([]byte("content obj1 v2")) tc.putObject([]byte("content obj1 v2"))
tc.deleteObject(tc.obj, "") settings, err := tc.layer.GetBucketSettings(tc.ctx, tc.bktInfo)
require.NoError(t, err)
tc.deleteObject(tc.obj, "", settings)
tc.getObject(tc.obj, "", true) tc.getObject(tc.obj, "", true)
tc.checkListObjects() tc.checkListObjects()
} }