2022-06-15 12:17:29 +00:00
|
|
|
package handler
|
|
|
|
|
|
|
|
import (
|
2022-07-25 13:00:35 +00:00
|
|
|
"bytes"
|
2023-10-17 08:20:37 +00:00
|
|
|
"encoding/xml"
|
2022-06-15 12:17:29 +00:00
|
|
|
"net/http"
|
2023-06-30 09:03:55 +00:00
|
|
|
"net/http/httptest"
|
2022-06-15 12:17:29 +00:00
|
|
|
"net/url"
|
|
|
|
"testing"
|
|
|
|
|
2023-03-07 14:38:08 +00:00
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api"
|
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/data"
|
2024-09-27 09:18:41 +00:00
|
|
|
apierr "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/errors"
|
2023-10-19 14:22:26 +00:00
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/layer/encryption"
|
2023-04-24 16:40:18 +00:00
|
|
|
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
|
|
|
|
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
2023-10-17 08:20:37 +00:00
|
|
|
"github.com/aws/aws-sdk-go/aws"
|
|
|
|
"github.com/aws/aws-sdk-go/private/protocol/xml/xmlutil"
|
|
|
|
"github.com/aws/aws-sdk-go/service/s3"
|
2022-06-15 12:17:29 +00:00
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
)
|
|
|
|
|
2022-06-24 12:39:30 +00:00
|
|
|
const (
|
|
|
|
emptyVersion = ""
|
|
|
|
)
|
|
|
|
|
2023-04-24 16:40:18 +00:00
|
|
|
func TestDeleteBucketOnAlreadyRemovedError(t *testing.T) {
|
|
|
|
hc := prepareHandlerContext(t)
|
|
|
|
|
|
|
|
bktName, objName := "bucket-for-removal", "object-to-delete"
|
|
|
|
bktInfo := createTestBucket(hc, bktName)
|
|
|
|
|
2023-08-21 13:05:16 +00:00
|
|
|
putObject(hc, bktName, objName)
|
2023-04-24 16:40:18 +00:00
|
|
|
|
2023-06-29 12:46:42 +00:00
|
|
|
addr := getAddressOfLastVersion(hc, bktInfo, objName)
|
2023-08-16 14:08:12 +00:00
|
|
|
hc.tp.SetObjectError(addr, &apistatus.ObjectAlreadyRemoved{})
|
2023-04-24 16:40:18 +00:00
|
|
|
|
|
|
|
deleteObjects(t, hc, bktName, [][2]string{{objName, emptyVersion}})
|
|
|
|
|
2024-10-29 11:18:01 +00:00
|
|
|
hc.owner = bktInfo.Owner
|
2023-04-24 16:40:18 +00:00
|
|
|
deleteBucket(t, hc, bktName, http.StatusNoContent)
|
|
|
|
}
|
|
|
|
|
2023-06-29 12:46:42 +00:00
|
|
|
func getAddressOfLastVersion(hc *handlerContext, bktInfo *data.BucketInfo, objName string) oid.Address {
|
|
|
|
nodeVersion, err := hc.tree.GetLatestVersion(hc.context, bktInfo, objName)
|
|
|
|
require.NoError(hc.t, err)
|
|
|
|
var addr oid.Address
|
|
|
|
addr.SetContainer(bktInfo.CID)
|
|
|
|
addr.SetObject(nodeVersion.OID)
|
|
|
|
return addr
|
|
|
|
}
|
|
|
|
|
2022-07-21 09:05:47 +00:00
|
|
|
func TestDeleteBucket(t *testing.T) {
|
|
|
|
tc := prepareHandlerContext(t)
|
|
|
|
|
|
|
|
bktName, objName := "bucket-for-removal", "object-to-delete"
|
2024-10-29 11:18:01 +00:00
|
|
|
bktInfo, objInfo := createVersionedBucketAndObject(t, tc, bktName, objName)
|
2022-07-21 09:05:47 +00:00
|
|
|
|
2022-07-25 13:00:35 +00:00
|
|
|
deleteMarkerVersion, isDeleteMarker := deleteObject(t, tc, bktName, objName, emptyVersion)
|
|
|
|
require.True(t, isDeleteMarker)
|
|
|
|
|
2024-10-29 11:18:01 +00:00
|
|
|
tc.owner = bktInfo.Owner
|
2022-07-21 09:05:47 +00:00
|
|
|
deleteBucket(t, tc, bktName, http.StatusConflict)
|
2022-08-04 17:31:33 +00:00
|
|
|
deleteObject(t, tc, bktName, objName, objInfo.VersionID())
|
2022-07-21 09:05:47 +00:00
|
|
|
deleteBucket(t, tc, bktName, http.StatusConflict)
|
2022-07-25 13:00:35 +00:00
|
|
|
deleteObject(t, tc, bktName, objName, deleteMarkerVersion)
|
2022-07-21 09:05:47 +00:00
|
|
|
deleteBucket(t, tc, bktName, http.StatusNoContent)
|
|
|
|
}
|
|
|
|
|
2024-01-26 13:16:14 +00:00
|
|
|
func TestDeleteBucketOnNotFoundError(t *testing.T) {
|
|
|
|
hc := prepareHandlerContext(t)
|
|
|
|
|
|
|
|
bktName, objName := "bucket-for-removal", "object-to-delete"
|
|
|
|
bktInfo := createTestBucket(hc, bktName)
|
|
|
|
|
|
|
|
putObject(hc, bktName, objName)
|
|
|
|
|
|
|
|
nodeVersion, err := hc.tree.GetUnversioned(hc.context, bktInfo, objName)
|
|
|
|
require.NoError(t, err)
|
|
|
|
var addr oid.Address
|
|
|
|
addr.SetContainer(bktInfo.CID)
|
|
|
|
addr.SetObject(nodeVersion.OID)
|
|
|
|
hc.tp.SetObjectError(addr, &apistatus.ObjectNotFound{})
|
|
|
|
|
|
|
|
deleteObjects(t, hc, bktName, [][2]string{{objName, emptyVersion}})
|
|
|
|
|
2024-10-29 11:18:01 +00:00
|
|
|
hc.owner = bktInfo.Owner
|
2024-01-26 13:16:14 +00:00
|
|
|
deleteBucket(t, hc, bktName, http.StatusNoContent)
|
|
|
|
}
|
2023-06-01 12:31:05 +00:00
|
|
|
|
2024-07-19 19:20:40 +00:00
|
|
|
func TestForceDeleteBucket(t *testing.T) {
|
|
|
|
hc := prepareHandlerContext(t)
|
|
|
|
|
|
|
|
bktName, objName := "bucket-for-removal", "object-to-delete"
|
|
|
|
bktInfo := createTestBucket(hc, bktName)
|
|
|
|
|
|
|
|
putObject(hc, bktName, objName)
|
|
|
|
|
|
|
|
nodeVersion, err := hc.tree.GetUnversioned(hc.context, bktInfo, objName)
|
|
|
|
require.NoError(t, err)
|
|
|
|
var addr oid.Address
|
|
|
|
addr.SetContainer(bktInfo.CID)
|
|
|
|
addr.SetObject(nodeVersion.OID)
|
|
|
|
|
2024-10-29 11:18:01 +00:00
|
|
|
hc.owner = bktInfo.Owner
|
2024-07-19 19:20:40 +00:00
|
|
|
deleteBucketForce(t, hc, bktName, http.StatusConflict, "false")
|
|
|
|
deleteBucketForce(t, hc, bktName, http.StatusNoContent, "true")
|
|
|
|
}
|
|
|
|
|
2024-06-26 13:31:43 +00:00
|
|
|
func TestDeleteMultipleObjectCheckUniqueness(t *testing.T) {
|
|
|
|
hc := prepareHandlerContext(t)
|
|
|
|
|
|
|
|
bktName, objName := "bucket", "object"
|
|
|
|
createTestBucket(hc, bktName)
|
|
|
|
|
|
|
|
putObject(hc, bktName, objName)
|
|
|
|
|
|
|
|
resp := deleteObjects(t, hc, bktName, [][2]string{{objName, emptyVersion}, {objName, emptyVersion}})
|
|
|
|
require.Empty(t, resp.Errors)
|
|
|
|
require.Len(t, resp.DeletedObjects, 1)
|
|
|
|
}
|
|
|
|
|
2023-10-17 08:20:37 +00:00
|
|
|
func TestDeleteObjectsError(t *testing.T) {
|
|
|
|
hc := prepareHandlerContext(t)
|
|
|
|
|
|
|
|
bktName, objName := "bucket-for-removal", "object-to-delete"
|
|
|
|
bktInfo := createTestBucket(hc, bktName)
|
|
|
|
putBucketVersioning(t, hc, bktName, true)
|
|
|
|
|
|
|
|
putObject(hc, bktName, objName)
|
|
|
|
|
|
|
|
nodeVersion, err := hc.tree.GetLatestVersion(hc.context, bktInfo, objName)
|
|
|
|
require.NoError(t, err)
|
|
|
|
var addr oid.Address
|
|
|
|
addr.SetContainer(bktInfo.CID)
|
|
|
|
addr.SetObject(nodeVersion.OID)
|
|
|
|
|
2024-09-27 09:18:41 +00:00
|
|
|
expectedError := apierr.GetAPIError(apierr.ErrAccessDenied)
|
2023-10-17 08:20:37 +00:00
|
|
|
hc.tp.SetObjectError(addr, expectedError)
|
|
|
|
|
|
|
|
w := deleteObjectsBase(hc, bktName, [][2]string{{objName, nodeVersion.OID.EncodeToString()}})
|
|
|
|
|
|
|
|
res := &s3.DeleteObjectsOutput{}
|
|
|
|
err = xmlutil.UnmarshalXML(res, xml.NewDecoder(w.Result().Body), "")
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
require.ElementsMatch(t, []*s3.Error{{
|
|
|
|
Code: aws.String(expectedError.Code),
|
|
|
|
Key: aws.String(objName),
|
|
|
|
Message: aws.String(expectedError.Error()),
|
|
|
|
VersionId: aws.String(nodeVersion.OID.EncodeToString()),
|
|
|
|
}}, res.Errors)
|
|
|
|
}
|
|
|
|
|
2022-06-15 12:17:29 +00:00
|
|
|
func TestDeleteObject(t *testing.T) {
|
|
|
|
tc := prepareHandlerContext(t)
|
|
|
|
|
2022-06-24 12:39:30 +00:00
|
|
|
bktName, objName := "bucket-for-removal", "object-to-delete"
|
2022-10-04 08:31:09 +00:00
|
|
|
bktInfo, objInfo := createBucketAndObject(tc, bktName, objName)
|
2022-06-15 12:17:29 +00:00
|
|
|
|
2022-06-24 12:39:30 +00:00
|
|
|
checkFound(t, tc, bktName, objName, emptyVersion)
|
|
|
|
deleteObject(t, tc, bktName, objName, emptyVersion)
|
|
|
|
checkNotFound(t, tc, bktName, objName, emptyVersion)
|
2022-06-15 12:17:29 +00:00
|
|
|
|
2022-12-20 08:38:58 +00:00
|
|
|
require.False(t, existInMockedFrostFS(tc, bktInfo, objInfo))
|
2022-06-24 12:39:30 +00:00
|
|
|
}
|
2022-06-15 12:17:29 +00:00
|
|
|
|
2022-07-25 13:00:35 +00:00
|
|
|
func TestDeleteObjectFromSuspended(t *testing.T) {
|
|
|
|
tc := prepareHandlerContext(t)
|
|
|
|
bktName, objName := "bucket-versioned-for-removal", "object-to-delete"
|
|
|
|
|
|
|
|
createSuspendedBucket(t, tc, bktName)
|
2023-08-21 13:05:16 +00:00
|
|
|
putObject(tc, bktName, objName)
|
2022-07-25 13:00:35 +00:00
|
|
|
|
|
|
|
versionID, isDeleteMarker := deleteObject(t, tc, bktName, objName, emptyVersion)
|
|
|
|
require.True(t, isDeleteMarker)
|
2022-08-05 01:41:33 +00:00
|
|
|
require.Equal(t, data.UnversionedObjectVersionID, versionID)
|
2022-07-25 13:00:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestDeleteDeletedObject(t *testing.T) {
|
|
|
|
tc := prepareHandlerContext(t)
|
|
|
|
|
|
|
|
t.Run("unversioned bucket", func(t *testing.T) {
|
|
|
|
bktName, objName := "bucket-unversioned-removal", "object-to-delete"
|
2022-10-04 08:31:09 +00:00
|
|
|
createBucketAndObject(tc, bktName, objName)
|
2022-07-25 13:00:35 +00:00
|
|
|
|
|
|
|
versionID, isDeleteMarker := deleteObject(t, tc, bktName, objName, emptyVersion)
|
|
|
|
require.Empty(t, versionID)
|
|
|
|
require.False(t, isDeleteMarker)
|
|
|
|
versionID, isDeleteMarker = deleteObject(t, tc, bktName, objName, emptyVersion)
|
|
|
|
require.Empty(t, versionID)
|
|
|
|
require.False(t, isDeleteMarker)
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("versioned bucket", func(t *testing.T) {
|
|
|
|
bktName, objName := "bucket-versioned-for-removal", "object-to-delete"
|
|
|
|
createVersionedBucketAndObject(t, tc, bktName, objName)
|
|
|
|
|
|
|
|
_, isDeleteMarker := deleteObject(t, tc, bktName, objName, emptyVersion)
|
|
|
|
require.True(t, isDeleteMarker)
|
|
|
|
_, isDeleteMarker = deleteObject(t, tc, bktName, objName, emptyVersion)
|
|
|
|
require.True(t, isDeleteMarker)
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("versioned bucket not found obj", func(t *testing.T) {
|
2024-02-12 12:28:55 +00:00
|
|
|
bktName, objName := "bucket-versioned-for-removal-not-found", "object-to-delete"
|
2022-07-25 13:00:35 +00:00
|
|
|
_, objInfo := createVersionedBucketAndObject(t, tc, bktName, objName)
|
|
|
|
|
2022-08-04 17:31:33 +00:00
|
|
|
versionID, isDeleteMarker := deleteObject(t, tc, bktName, objName, objInfo.VersionID())
|
2022-07-25 13:00:35 +00:00
|
|
|
require.False(t, isDeleteMarker)
|
2022-08-04 17:31:33 +00:00
|
|
|
require.Equal(t, objInfo.VersionID(), versionID)
|
2022-07-25 13:00:35 +00:00
|
|
|
|
|
|
|
versionID2, isDeleteMarker := deleteObject(t, tc, bktName, objName, versionID)
|
|
|
|
require.False(t, isDeleteMarker)
|
2022-08-04 17:31:33 +00:00
|
|
|
require.Equal(t, objInfo.VersionID(), versionID2)
|
2022-07-25 13:00:35 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2022-06-24 12:39:30 +00:00
|
|
|
func TestDeleteObjectVersioned(t *testing.T) {
|
|
|
|
tc := prepareHandlerContext(t)
|
2022-06-15 12:17:29 +00:00
|
|
|
|
2022-06-24 12:39:30 +00:00
|
|
|
bktName, objName := "bucket-for-removal", "object-to-delete"
|
|
|
|
bktInfo, objInfo := createVersionedBucketAndObject(t, tc, bktName, objName)
|
2022-06-15 12:17:29 +00:00
|
|
|
|
2022-06-24 12:39:30 +00:00
|
|
|
checkFound(t, tc, bktName, objName, emptyVersion)
|
|
|
|
deleteObject(t, tc, bktName, objName, emptyVersion)
|
|
|
|
checkNotFound(t, tc, bktName, objName, emptyVersion)
|
|
|
|
|
2022-08-04 17:31:33 +00:00
|
|
|
checkFound(t, tc, bktName, objName, objInfo.VersionID())
|
|
|
|
deleteObject(t, tc, bktName, objName, objInfo.VersionID())
|
|
|
|
checkNotFound(t, tc, bktName, objName, objInfo.VersionID())
|
2022-06-15 12:17:29 +00:00
|
|
|
|
2022-12-20 08:38:58 +00:00
|
|
|
require.False(t, existInMockedFrostFS(tc, bktInfo, objInfo), "object exists but shouldn't")
|
2022-06-15 12:17:29 +00:00
|
|
|
}
|
|
|
|
|
2022-07-01 09:32:31 +00:00
|
|
|
func TestDeleteObjectUnversioned(t *testing.T) {
|
|
|
|
tc := prepareHandlerContext(t)
|
|
|
|
|
|
|
|
bktName, objName := "bucket-for-removal-unversioned", "object-to-delete-unversioned"
|
2022-10-04 08:31:09 +00:00
|
|
|
bktInfo, objInfo := createBucketAndObject(tc, bktName, objName)
|
2022-07-01 09:32:31 +00:00
|
|
|
|
|
|
|
checkFound(t, tc, bktName, objName, emptyVersion)
|
|
|
|
deleteObject(t, tc, bktName, objName, emptyVersion)
|
|
|
|
checkNotFound(t, tc, bktName, objName, emptyVersion)
|
|
|
|
|
|
|
|
versions := listVersions(t, tc, bktName)
|
|
|
|
require.Len(t, versions.DeleteMarker, 0, "delete markers must be empty")
|
|
|
|
require.Len(t, versions.Version, 0, "versions must be empty")
|
|
|
|
|
2022-12-20 08:38:58 +00:00
|
|
|
require.False(t, existInMockedFrostFS(tc, bktInfo, objInfo), "object exists but shouldn't")
|
2022-07-01 09:32:31 +00:00
|
|
|
}
|
|
|
|
|
2022-06-24 12:39:30 +00:00
|
|
|
func TestRemoveDeleteMarker(t *testing.T) {
|
2022-06-15 12:17:29 +00:00
|
|
|
tc := prepareHandlerContext(t)
|
|
|
|
|
2022-06-24 12:39:30 +00:00
|
|
|
bktName, objName := "bucket-for-removal", "object-to-delete"
|
|
|
|
bktInfo, objInfo := createVersionedBucketAndObject(t, tc, bktName, objName)
|
2022-06-15 12:17:29 +00:00
|
|
|
|
2022-06-24 12:39:30 +00:00
|
|
|
checkFound(t, tc, bktName, objName, emptyVersion)
|
2022-07-25 13:00:35 +00:00
|
|
|
deleteMarkerVersion, isDeleteMarker := deleteObject(t, tc, bktName, objName, emptyVersion)
|
|
|
|
require.True(t, isDeleteMarker)
|
2022-06-24 12:39:30 +00:00
|
|
|
checkNotFound(t, tc, bktName, objName, emptyVersion)
|
2022-06-15 12:17:29 +00:00
|
|
|
|
2022-08-04 17:31:33 +00:00
|
|
|
checkFound(t, tc, bktName, objName, objInfo.VersionID())
|
2022-06-24 12:39:30 +00:00
|
|
|
deleteObject(t, tc, bktName, objName, deleteMarkerVersion)
|
2022-07-05 08:04:21 +00:00
|
|
|
checkFound(t, tc, bktName, objName, emptyVersion)
|
2022-06-15 12:17:29 +00:00
|
|
|
|
2022-12-20 08:38:58 +00:00
|
|
|
require.True(t, existInMockedFrostFS(tc, bktInfo, objInfo), "object doesn't exist but should")
|
2022-06-24 12:39:30 +00:00
|
|
|
}
|
2022-06-15 12:17:29 +00:00
|
|
|
|
2023-04-14 12:36:22 +00:00
|
|
|
func TestDeleteMarkerVersioned(t *testing.T) {
|
|
|
|
tc := prepareHandlerContext(t)
|
|
|
|
|
|
|
|
bktName, objName := "bucket-for-removal", "object-to-delete"
|
|
|
|
createVersionedBucketAndObject(t, tc, bktName, objName)
|
|
|
|
|
|
|
|
t.Run("not create new delete marker if last version is delete marker", func(t *testing.T) {
|
|
|
|
deleteMarkerVersion, isDeleteMarker := deleteObject(t, tc, bktName, objName, emptyVersion)
|
|
|
|
require.True(t, isDeleteMarker)
|
|
|
|
versions := listVersions(t, tc, bktName)
|
2024-01-17 14:26:02 +00:00
|
|
|
require.Len(t, versions.DeleteMarker, 1)
|
2023-04-14 12:36:22 +00:00
|
|
|
require.Equal(t, deleteMarkerVersion, versions.DeleteMarker[0].VersionID)
|
|
|
|
|
|
|
|
_, isDeleteMarker = deleteObject(t, tc, bktName, objName, emptyVersion)
|
|
|
|
require.True(t, isDeleteMarker)
|
|
|
|
versions = listVersions(t, tc, bktName)
|
|
|
|
require.Len(t, versions.DeleteMarker, 1)
|
|
|
|
require.Equal(t, deleteMarkerVersion, versions.DeleteMarker[0].VersionID)
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("do not create delete marker if object does not exist", func(t *testing.T) {
|
|
|
|
versionsBefore := listVersions(t, tc, bktName)
|
|
|
|
_, isDeleteMarker := deleteObject(t, tc, bktName, "dummy", emptyVersion)
|
|
|
|
require.False(t, isDeleteMarker)
|
|
|
|
versionsAfter := listVersions(t, tc, bktName)
|
|
|
|
require.Equal(t, versionsBefore, versionsAfter)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestDeleteMarkerSuspended(t *testing.T) {
|
|
|
|
tc := prepareHandlerContext(t)
|
|
|
|
|
|
|
|
bktName, objName := "bucket-for-removal", "object-to-delete"
|
|
|
|
bktInfo, _ := createVersionedBucketAndObject(t, tc, bktName, objName)
|
|
|
|
putBucketVersioning(t, tc, bktName, false)
|
|
|
|
|
|
|
|
t.Run("not create new delete marker if last version is delete marker", func(t *testing.T) {
|
|
|
|
deleteMarkerVersion, isDeleteMarker := deleteObject(t, tc, bktName, objName, emptyVersion)
|
|
|
|
require.True(t, isDeleteMarker)
|
|
|
|
require.Equal(t, data.UnversionedObjectVersionID, deleteMarkerVersion)
|
|
|
|
|
|
|
|
deleteMarkerVersion, isDeleteMarker = deleteObject(t, tc, bktName, objName, emptyVersion)
|
|
|
|
require.True(t, isDeleteMarker)
|
|
|
|
require.Equal(t, data.UnversionedObjectVersionID, deleteMarkerVersion)
|
|
|
|
|
|
|
|
versions := listVersions(t, tc, bktName)
|
|
|
|
require.Len(t, versions.DeleteMarker, 1)
|
|
|
|
require.Equal(t, deleteMarkerVersion, versions.DeleteMarker[0].VersionID)
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("do not create delete marker if object does not exist", func(t *testing.T) {
|
|
|
|
versionsBefore := listVersions(t, tc, bktName)
|
|
|
|
_, isDeleteMarker := deleteObject(t, tc, bktName, "dummy", emptyVersion)
|
|
|
|
require.False(t, isDeleteMarker)
|
|
|
|
versionsAfter := listVersions(t, tc, bktName)
|
|
|
|
require.Equal(t, versionsBefore, versionsAfter)
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("remove last unversioned non delete marker", func(t *testing.T) {
|
|
|
|
objName := "obj3"
|
2023-08-21 13:05:16 +00:00
|
|
|
putObject(tc, bktName, objName)
|
2023-04-14 12:36:22 +00:00
|
|
|
|
|
|
|
nodeVersion, err := tc.tree.GetUnversioned(tc.Context(), bktInfo, objName)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
deleteMarkerVersion, isDeleteMarker := deleteObject(t, tc, bktName, objName, emptyVersion)
|
|
|
|
require.True(t, isDeleteMarker)
|
|
|
|
require.Equal(t, data.UnversionedObjectVersionID, deleteMarkerVersion)
|
|
|
|
|
|
|
|
objVersions := getVersion(listVersions(t, tc, bktName), objName)
|
|
|
|
require.Len(t, objVersions, 0)
|
|
|
|
|
|
|
|
require.False(t, tc.MockedPool().ObjectExists(nodeVersion.OID))
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2022-06-24 12:39:30 +00:00
|
|
|
func TestDeleteObjectCombined(t *testing.T) {
|
|
|
|
tc := prepareHandlerContext(t)
|
2022-06-15 12:17:29 +00:00
|
|
|
|
2022-06-24 12:39:30 +00:00
|
|
|
bktName, objName := "bucket-for-removal", "object-to-delete"
|
2022-10-04 08:31:09 +00:00
|
|
|
bktInfo, objInfo := createBucketAndObject(tc, bktName, objName)
|
2022-06-15 12:17:29 +00:00
|
|
|
|
2022-06-24 12:39:30 +00:00
|
|
|
putBucketVersioning(t, tc, bktName, true)
|
2022-06-15 12:17:29 +00:00
|
|
|
|
2022-06-24 12:39:30 +00:00
|
|
|
checkFound(t, tc, bktName, objName, emptyVersion)
|
|
|
|
deleteObject(t, tc, bktName, objName, emptyVersion)
|
|
|
|
checkNotFound(t, tc, bktName, objName, emptyVersion)
|
2022-06-15 12:17:29 +00:00
|
|
|
|
2022-08-04 17:31:33 +00:00
|
|
|
checkFound(t, tc, bktName, objName, objInfo.VersionID())
|
2022-06-15 12:17:29 +00:00
|
|
|
|
2022-12-20 08:38:58 +00:00
|
|
|
require.True(t, existInMockedFrostFS(tc, bktInfo, objInfo), "object doesn't exist but should")
|
2022-06-15 12:17:29 +00:00
|
|
|
}
|
|
|
|
|
2022-06-24 12:39:30 +00:00
|
|
|
func TestDeleteObjectSuspended(t *testing.T) {
|
2022-06-15 12:17:29 +00:00
|
|
|
tc := prepareHandlerContext(t)
|
|
|
|
|
2022-06-24 12:39:30 +00:00
|
|
|
bktName, objName := "bucket-for-removal", "object-to-delete"
|
2022-10-04 08:31:09 +00:00
|
|
|
bktInfo, objInfo := createBucketAndObject(tc, bktName, objName)
|
2022-06-24 12:39:30 +00:00
|
|
|
|
|
|
|
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)
|
2022-08-04 17:31:33 +00:00
|
|
|
checkNotFound(t, tc, bktName, objName, objInfo.VersionID())
|
2022-06-24 12:39:30 +00:00
|
|
|
|
2022-12-20 08:38:58 +00:00
|
|
|
require.False(t, existInMockedFrostFS(tc, bktInfo, objInfo), "object exists but shouldn't")
|
2022-06-24 12:39:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestDeleteMarkers(t *testing.T) {
|
|
|
|
tc := prepareHandlerContext(t)
|
|
|
|
|
|
|
|
bktName, objName := "bucket-for-removal", "object-to-delete"
|
2022-10-04 08:31:09 +00:00
|
|
|
createTestBucket(tc, bktName)
|
2022-06-24 12:39:30 +00:00
|
|
|
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)
|
2023-04-14 12:36:22 +00:00
|
|
|
require.Len(t, versions.DeleteMarker, 0, "invalid delete markers length")
|
2022-06-24 12:39:30 +00:00
|
|
|
require.Len(t, versions.Version, 0, "versions must be empty")
|
|
|
|
|
2022-12-20 08:38:58 +00:00
|
|
|
require.Len(t, listOIDsFromMockedFrostFS(t, tc, bktName), 0, "shouldn't be any object in frostfs")
|
2022-06-24 12:39:30 +00:00
|
|
|
}
|
|
|
|
|
2023-10-17 11:21:39 +00:00
|
|
|
func TestGetHeadDeleteMarker(t *testing.T) {
|
|
|
|
hc := prepareHandlerContext(t)
|
|
|
|
|
|
|
|
bktName, objName := "bucket-for-removal", "object-to-delete"
|
|
|
|
createTestBucket(hc, bktName)
|
|
|
|
putBucketVersioning(t, hc, bktName, true)
|
|
|
|
|
|
|
|
putObject(hc, bktName, objName)
|
|
|
|
|
|
|
|
deleteMarkerVersionID, _ := deleteObject(t, hc, bktName, objName, emptyVersion)
|
|
|
|
|
|
|
|
w := headObjectBase(hc, bktName, objName, deleteMarkerVersionID)
|
|
|
|
require.Equal(t, w.Code, http.StatusMethodNotAllowed)
|
|
|
|
require.Equal(t, w.Result().Header.Get(api.AmzDeleteMarker), "true")
|
|
|
|
|
|
|
|
w, r := prepareTestRequest(hc, bktName, objName, nil)
|
|
|
|
hc.Handler().GetObjectHandler(w, r)
|
|
|
|
assertStatus(hc.t, w, http.StatusNotFound)
|
|
|
|
require.Equal(t, w.Result().Header.Get(api.AmzDeleteMarker), "true")
|
|
|
|
}
|
|
|
|
|
2022-07-05 06:25:23 +00:00
|
|
|
func TestDeleteObjectFromListCache(t *testing.T) {
|
|
|
|
tc := prepareHandlerContext(t)
|
|
|
|
|
|
|
|
bktName, objName := "bucket-for-removal", "object-to-delete"
|
|
|
|
bktInfo, objInfo := createVersionedBucketAndObject(t, tc, bktName, objName)
|
|
|
|
|
2023-08-16 13:54:12 +00:00
|
|
|
versions := listObjectsV1(tc, bktName, "", "", "", -1)
|
2022-07-05 06:25:23 +00:00
|
|
|
require.Len(t, versions.Contents, 1)
|
|
|
|
|
2022-08-04 17:31:33 +00:00
|
|
|
checkFound(t, tc, bktName, objName, objInfo.VersionID())
|
|
|
|
deleteObject(t, tc, bktName, objName, objInfo.VersionID())
|
|
|
|
checkNotFound(t, tc, bktName, objName, objInfo.VersionID())
|
2022-07-05 06:25:23 +00:00
|
|
|
|
|
|
|
// check cache is clean after object removal
|
2023-08-16 13:54:12 +00:00
|
|
|
versions = listObjectsV1(tc, bktName, "", "", "", -1)
|
2022-07-05 06:25:23 +00:00
|
|
|
require.Len(t, versions.Contents, 0)
|
|
|
|
|
2022-12-20 08:38:58 +00:00
|
|
|
require.False(t, existInMockedFrostFS(tc, bktInfo, objInfo))
|
2022-07-05 06:25:23 +00:00
|
|
|
}
|
|
|
|
|
2022-07-05 08:04:21 +00:00
|
|
|
func TestDeleteObjectCheckMarkerReturn(t *testing.T) {
|
|
|
|
tc := prepareHandlerContext(t)
|
|
|
|
|
|
|
|
bktName, objName := "bucket-for-removal", "object-to-delete"
|
|
|
|
createVersionedBucketAndObject(t, tc, bktName, objName)
|
|
|
|
|
2022-07-25 13:00:35 +00:00
|
|
|
deleteMarkerVersion, isDeleteMarker := deleteObject(t, tc, bktName, objName, emptyVersion)
|
|
|
|
require.True(t, isDeleteMarker)
|
2022-07-05 08:04:21 +00:00
|
|
|
|
|
|
|
versions := listVersions(t, tc, bktName)
|
|
|
|
require.Len(t, versions.DeleteMarker, 1)
|
2022-07-25 13:00:35 +00:00
|
|
|
require.Equal(t, deleteMarkerVersion, versions.DeleteMarker[0].VersionID)
|
2022-07-05 08:04:21 +00:00
|
|
|
|
2022-07-25 13:00:35 +00:00
|
|
|
deleteMarkerVersion2, isDeleteMarker2 := deleteObject(t, tc, bktName, objName, deleteMarkerVersion)
|
|
|
|
require.True(t, isDeleteMarker2)
|
2022-07-05 08:04:21 +00:00
|
|
|
versions = listVersions(t, tc, bktName)
|
|
|
|
require.Len(t, versions.DeleteMarker, 0)
|
2022-07-25 13:00:35 +00:00
|
|
|
require.Equal(t, deleteMarkerVersion, deleteMarkerVersion2)
|
2022-07-05 08:04:21 +00:00
|
|
|
}
|
|
|
|
|
2024-10-29 11:18:01 +00:00
|
|
|
func TestDeleteBucketByNotOwner(t *testing.T) {
|
|
|
|
hc := prepareHandlerContext(t)
|
|
|
|
|
|
|
|
bktName := "bucket-name"
|
|
|
|
bktInfo := createTestBucket(hc, bktName)
|
|
|
|
deleteBucket(t, hc, bktName, http.StatusForbidden)
|
|
|
|
|
|
|
|
hc.owner = bktInfo.Owner
|
|
|
|
deleteBucket(t, hc, bktName, http.StatusNoContent)
|
|
|
|
}
|
|
|
|
|
2022-10-04 08:31:09 +00:00
|
|
|
func createBucketAndObject(tc *handlerContext, bktName, objName string) (*data.BucketInfo, *data.ObjectInfo) {
|
|
|
|
bktInfo := createTestBucket(tc, bktName)
|
2022-06-15 12:17:29 +00:00
|
|
|
|
2023-10-19 14:22:26 +00:00
|
|
|
objInfo := createTestObject(tc, bktInfo, objName, encryption.Params{})
|
2022-06-15 12:17:29 +00:00
|
|
|
|
2022-06-24 12:39:30 +00:00
|
|
|
return bktInfo, objInfo
|
|
|
|
}
|
|
|
|
|
2024-01-22 08:09:11 +00:00
|
|
|
func createVersionedBucketAndObject(_ *testing.T, tc *handlerContext, bktName, objName string) (*data.BucketInfo, *data.ObjectInfo) {
|
2024-01-19 09:53:53 +00:00
|
|
|
bktInfo := createVersionedBucket(tc, bktName)
|
2023-10-19 14:22:26 +00:00
|
|
|
objInfo := createTestObject(tc, bktInfo, objName, encryption.Params{})
|
2022-06-24 12:39:30 +00:00
|
|
|
|
|
|
|
return bktInfo, objInfo
|
|
|
|
}
|
|
|
|
|
2024-01-19 09:53:53 +00:00
|
|
|
func createVersionedBucket(hc *handlerContext, bktName string) *data.BucketInfo {
|
|
|
|
bktInfo := createTestBucket(hc, bktName)
|
|
|
|
putBucketVersioning(hc.t, hc, bktName, true)
|
|
|
|
|
|
|
|
return bktInfo
|
|
|
|
}
|
|
|
|
|
2022-06-24 12:39:30 +00:00
|
|
|
func putBucketVersioning(t *testing.T, tc *handlerContext, bktName string, enabled bool) {
|
|
|
|
cfg := &VersioningConfiguration{Status: "Suspended"}
|
|
|
|
if enabled {
|
|
|
|
cfg.Status = "Enabled"
|
|
|
|
}
|
2022-10-04 08:31:09 +00:00
|
|
|
w, r := prepareTestRequest(tc, bktName, "", cfg)
|
2022-06-15 12:17:29 +00:00
|
|
|
tc.Handler().PutBucketVersioningHandler(w, r)
|
|
|
|
assertStatus(t, w, http.StatusOK)
|
2022-06-24 12:39:30 +00:00
|
|
|
}
|
|
|
|
|
2024-07-16 14:36:52 +00:00
|
|
|
func getBucketVersioning(hc *handlerContext, bktName string) *VersioningConfiguration {
|
|
|
|
w, r := prepareTestRequest(hc, bktName, "", nil)
|
|
|
|
hc.Handler().GetBucketVersioningHandler(w, r)
|
|
|
|
assertStatus(hc.t, w, http.StatusOK)
|
|
|
|
|
|
|
|
res := &VersioningConfiguration{}
|
|
|
|
parseTestResponse(hc.t, w, res)
|
|
|
|
return res
|
|
|
|
}
|
|
|
|
|
2022-07-25 13:00:35 +00:00
|
|
|
func deleteObject(t *testing.T, tc *handlerContext, bktName, objName, version string) (string, bool) {
|
2022-06-24 12:39:30 +00:00
|
|
|
query := make(url.Values)
|
|
|
|
query.Add(api.QueryVersionID, version)
|
2022-06-15 12:17:29 +00:00
|
|
|
|
2022-10-04 08:31:09 +00:00
|
|
|
w, r := prepareTestFullRequest(tc, bktName, objName, query, nil)
|
2022-06-15 12:17:29 +00:00
|
|
|
tc.Handler().DeleteObjectHandler(w, r)
|
|
|
|
assertStatus(t, w, http.StatusNoContent)
|
|
|
|
|
2022-07-25 13:00:35 +00:00
|
|
|
return w.Header().Get(api.AmzVersionID), w.Header().Get(api.AmzDeleteMarker) != ""
|
2022-06-24 12:39:30 +00:00
|
|
|
}
|
|
|
|
|
2023-04-24 16:40:18 +00:00
|
|
|
func deleteObjects(t *testing.T, tc *handlerContext, bktName string, objVersions [][2]string) *DeleteObjectsResponse {
|
2023-10-17 08:20:37 +00:00
|
|
|
w := deleteObjectsBase(tc, bktName, objVersions)
|
|
|
|
|
|
|
|
res := &DeleteObjectsResponse{}
|
|
|
|
parseTestResponse(t, w, res)
|
|
|
|
return res
|
|
|
|
}
|
|
|
|
|
|
|
|
func deleteObjectsBase(hc *handlerContext, bktName string, objVersions [][2]string) *httptest.ResponseRecorder {
|
2023-04-24 16:40:18 +00:00
|
|
|
req := &DeleteObjectsRequest{}
|
|
|
|
for _, version := range objVersions {
|
|
|
|
req.Objects = append(req.Objects, ObjectIdentifier{
|
|
|
|
ObjectName: version[0],
|
|
|
|
VersionID: version[1],
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2023-10-17 08:20:37 +00:00
|
|
|
w, r := prepareTestRequest(hc, bktName, "", req)
|
2023-04-24 16:40:18 +00:00
|
|
|
r.Header.Set(api.ContentMD5, "")
|
2023-10-17 08:20:37 +00:00
|
|
|
hc.Handler().DeleteMultipleObjectsHandler(w, r)
|
|
|
|
assertStatus(hc.t, w, http.StatusOK)
|
2023-04-24 16:40:18 +00:00
|
|
|
|
2023-10-17 08:20:37 +00:00
|
|
|
return w
|
2023-04-24 16:40:18 +00:00
|
|
|
}
|
|
|
|
|
2024-07-19 19:20:40 +00:00
|
|
|
func deleteBucketForce(t *testing.T, tc *handlerContext, bktName string, code int, value string) {
|
|
|
|
w, r := prepareTestRequest(tc, bktName, "", nil)
|
|
|
|
r.Header.Set(api.AmzForceBucketDelete, value)
|
|
|
|
tc.Handler().DeleteBucketHandler(w, r)
|
|
|
|
assertStatus(t, w, code)
|
|
|
|
}
|
|
|
|
|
2022-07-21 09:05:47 +00:00
|
|
|
func deleteBucket(t *testing.T, tc *handlerContext, bktName string, code int) {
|
2022-10-04 08:31:09 +00:00
|
|
|
w, r := prepareTestRequest(tc, bktName, "", nil)
|
2022-07-21 09:05:47 +00:00
|
|
|
tc.Handler().DeleteBucketHandler(w, r)
|
|
|
|
assertStatus(t, w, code)
|
|
|
|
}
|
|
|
|
|
2023-06-30 09:03:55 +00:00
|
|
|
func checkNotFound(t *testing.T, hc *handlerContext, bktName, objName, version string) {
|
|
|
|
w := headObjectBase(hc, bktName, objName, version)
|
2022-06-15 12:17:29 +00:00
|
|
|
assertStatus(t, w, http.StatusNotFound)
|
2022-06-24 12:39:30 +00:00
|
|
|
}
|
2022-06-15 12:17:29 +00:00
|
|
|
|
2024-09-27 09:18:41 +00:00
|
|
|
func headObjectAssertS3Error(hc *handlerContext, bktName, objName, version string, code apierr.ErrorCode) {
|
2023-06-30 09:03:55 +00:00
|
|
|
w := headObjectBase(hc, bktName, objName, version)
|
2024-09-27 09:18:41 +00:00
|
|
|
assertS3Error(hc.t, w, apierr.GetAPIError(code))
|
2023-06-29 12:46:42 +00:00
|
|
|
}
|
|
|
|
|
2023-06-30 09:03:55 +00:00
|
|
|
func checkFound(t *testing.T, hc *handlerContext, bktName, objName, version string) {
|
|
|
|
w := headObjectBase(hc, bktName, objName, version)
|
|
|
|
assertStatus(t, w, http.StatusOK)
|
|
|
|
}
|
|
|
|
|
2024-11-11 13:23:17 +00:00
|
|
|
func headObjectWithHeaders(hc *handlerContext, bktName, objName, version string, headers map[string]string) *httptest.ResponseRecorder {
|
|
|
|
query := make(url.Values)
|
|
|
|
query.Add(api.QueryVersionID, version)
|
|
|
|
|
|
|
|
w, r := prepareTestFullRequest(hc, bktName, objName, query, nil)
|
|
|
|
for k, v := range headers {
|
|
|
|
r.Header.Set(k, v)
|
|
|
|
}
|
|
|
|
hc.Handler().HeadObjectHandler(w, r)
|
|
|
|
return w
|
|
|
|
}
|
|
|
|
|
2023-06-30 09:03:55 +00:00
|
|
|
func headObjectBase(hc *handlerContext, bktName, objName, version string) *httptest.ResponseRecorder {
|
2022-06-15 12:17:29 +00:00
|
|
|
query := make(url.Values)
|
2022-06-24 12:39:30 +00:00
|
|
|
query.Add(api.QueryVersionID, version)
|
2022-06-15 12:17:29 +00:00
|
|
|
|
2023-06-30 09:03:55 +00:00
|
|
|
w, r := prepareTestFullRequest(hc, bktName, objName, query, nil)
|
|
|
|
hc.Handler().HeadObjectHandler(w, r)
|
|
|
|
return w
|
2022-06-24 12:39:30 +00:00
|
|
|
}
|
2022-06-15 12:17:29 +00:00
|
|
|
|
2023-10-12 08:10:43 +00:00
|
|
|
func listVersions(_ *testing.T, tc *handlerContext, bktName string) *ListObjectsVersionsResponse {
|
|
|
|
return listObjectsVersions(tc, bktName, "", "", "", "", -1)
|
2022-06-15 12:17:29 +00:00
|
|
|
}
|
2022-07-05 06:25:23 +00:00
|
|
|
|
2023-04-14 12:36:22 +00:00
|
|
|
func getVersion(resp *ListObjectsVersionsResponse, objName string) []*ObjectVersionResponse {
|
|
|
|
var res []*ObjectVersionResponse
|
|
|
|
for i, version := range resp.Version {
|
|
|
|
if version.Key == objName {
|
|
|
|
res = append(res, &resp.Version[i])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return res
|
|
|
|
}
|
|
|
|
|
2023-08-21 13:05:16 +00:00
|
|
|
func putObject(hc *handlerContext, bktName, objName string) {
|
2022-07-25 13:00:35 +00:00
|
|
|
body := bytes.NewReader([]byte("content"))
|
2023-08-21 13:05:16 +00:00
|
|
|
w, r := prepareTestPayloadRequest(hc, bktName, objName, body)
|
|
|
|
hc.Handler().PutObjectHandler(w, r)
|
|
|
|
assertStatus(hc.t, w, http.StatusOK)
|
2022-07-25 13:00:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func createSuspendedBucket(t *testing.T, tc *handlerContext, bktName string) *data.BucketInfo {
|
2022-10-04 08:31:09 +00:00
|
|
|
createTestBucket(tc, bktName)
|
2022-07-25 13:00:35 +00:00
|
|
|
bktInfo, err := tc.Layer().GetBucketInfo(tc.Context(), bktName)
|
|
|
|
require.NoError(t, err)
|
|
|
|
putBucketVersioning(t, tc, bktName, false)
|
|
|
|
return bktInfo
|
|
|
|
}
|