[#31] Add force bucket delete flag
All checks were successful
/ DCO (pull_request) Successful in 1m9s
/ Vulncheck (pull_request) Successful in 1m20s
/ Builds (1.21) (pull_request) Successful in 1m23s
/ Builds (1.22) (pull_request) Successful in 1m33s
/ Lint (pull_request) Successful in 2m11s
/ Tests (1.21) (pull_request) Successful in 1m32s
/ Tests (1.22) (pull_request) Successful in 1m24s
All checks were successful
/ DCO (pull_request) Successful in 1m9s
/ Vulncheck (pull_request) Successful in 1m20s
/ Builds (1.21) (pull_request) Successful in 1m23s
/ Builds (1.22) (pull_request) Successful in 1m33s
/ Lint (pull_request) Successful in 2m11s
/ Tests (1.21) (pull_request) Successful in 1m32s
/ Tests (1.22) (pull_request) Successful in 1m24s
Signed-off-by: Pavel Pogodaev <p.pogodaev@yadro.com>
This commit is contained in:
parent
971006a28c
commit
9c0b781193
5 changed files with 99 additions and 9 deletions
|
@ -237,9 +237,18 @@ func (h *handler) DeleteBucketHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
sessionToken = boxData.Gate.SessionTokenForDelete()
|
sessionToken = boxData.Gate.SessionTokenForDelete()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
skipObjCheck := false
|
||||||
|
if value, ok := r.Header[api.AmzForceBucketDelete]; ok {
|
||||||
|
s := value[0]
|
||||||
|
if s == "true" {
|
||||||
|
skipObjCheck = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if err = h.obj.DeleteBucket(r.Context(), &layer.DeleteBucketParams{
|
if err = h.obj.DeleteBucket(r.Context(), &layer.DeleteBucketParams{
|
||||||
BktInfo: bktInfo,
|
BktInfo: bktInfo,
|
||||||
SessionToken: sessionToken,
|
SessionToken: sessionToken,
|
||||||
|
SkipCheck: skipObjCheck,
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
h.logAndSendError(w, "couldn't delete bucket", reqInfo, err)
|
h.logAndSendError(w, "couldn't delete bucket", reqInfo, err)
|
||||||
return
|
return
|
||||||
|
|
|
@ -85,6 +85,24 @@ func TestDeleteBucketOnNotFoundError(t *testing.T) {
|
||||||
deleteBucket(t, hc, bktName, http.StatusNoContent)
|
deleteBucket(t, hc, bktName, http.StatusNoContent)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
deleteBucketForce(t, hc, bktName, http.StatusConflict, "false")
|
||||||
|
deleteBucketForce(t, hc, bktName, http.StatusNoContent, "true")
|
||||||
|
}
|
||||||
|
|
||||||
func TestDeleteMultipleObjectCheckUniqueness(t *testing.T) {
|
func TestDeleteMultipleObjectCheckUniqueness(t *testing.T) {
|
||||||
hc := prepareHandlerContext(t)
|
hc := prepareHandlerContext(t)
|
||||||
|
|
||||||
|
@ -517,6 +535,13 @@ func deleteObjectsBase(hc *handlerContext, bktName string, objVersions [][2]stri
|
||||||
return w
|
return w
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
func deleteBucket(t *testing.T, tc *handlerContext, bktName string, code int) {
|
func deleteBucket(t *testing.T, tc *handlerContext, bktName string, code int) {
|
||||||
w, r := prepareTestRequest(tc, bktName, "", nil)
|
w, r := prepareTestRequest(tc, bktName, "", nil)
|
||||||
tc.Handler().DeleteBucketHandler(w, r)
|
tc.Handler().DeleteBucketHandler(w, r)
|
||||||
|
|
|
@ -62,6 +62,7 @@ const (
|
||||||
AmzMaxParts = "X-Amz-Max-Parts"
|
AmzMaxParts = "X-Amz-Max-Parts"
|
||||||
AmzPartNumberMarker = "X-Amz-Part-Number-Marker"
|
AmzPartNumberMarker = "X-Amz-Part-Number-Marker"
|
||||||
AmzStorageClass = "X-Amz-Storage-Class"
|
AmzStorageClass = "X-Amz-Storage-Class"
|
||||||
|
AmzForceBucketDelete = "X-Amz-Force-Delete-Bucket"
|
||||||
|
|
||||||
AmzServerSideEncryptionCustomerAlgorithm = "x-amz-server-side-encryption-customer-algorithm"
|
AmzServerSideEncryptionCustomerAlgorithm = "x-amz-server-side-encryption-customer-algorithm"
|
||||||
AmzServerSideEncryptionCustomerKey = "x-amz-server-side-encryption-customer-key"
|
AmzServerSideEncryptionCustomerKey = "x-amz-server-side-encryption-customer-key"
|
||||||
|
|
|
@ -170,6 +170,7 @@ type (
|
||||||
DeleteBucketParams struct {
|
DeleteBucketParams struct {
|
||||||
BktInfo *data.BucketInfo
|
BktInfo *data.BucketInfo
|
||||||
SessionToken *session.Container
|
SessionToken *session.Container
|
||||||
|
SkipCheck bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListObjectVersionsParams stores list objects versions parameters.
|
// ListObjectVersionsParams stores list objects versions parameters.
|
||||||
|
@ -804,6 +805,7 @@ func (n *Layer) ResolveBucket(ctx context.Context, name string) (cid.ID, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *Layer) DeleteBucket(ctx context.Context, p *DeleteBucketParams) error {
|
func (n *Layer) DeleteBucket(ctx context.Context, p *DeleteBucketParams) error {
|
||||||
|
if !p.SkipCheck {
|
||||||
res, _, err := n.getAllObjectsVersions(ctx, commonVersionsListingParams{
|
res, _, err := n.getAllObjectsVersions(ctx, commonVersionsListingParams{
|
||||||
BktInfo: p.BktInfo,
|
BktInfo: p.BktInfo,
|
||||||
MaxKeys: 1,
|
MaxKeys: 1,
|
||||||
|
@ -815,6 +817,7 @@ func (n *Layer) DeleteBucket(ctx context.Context, p *DeleteBucketParams) error {
|
||||||
if len(res) != 0 {
|
if len(res) != 0 {
|
||||||
return errors.GetAPIError(errors.ErrBucketNotEmpty)
|
return errors.GetAPIError(errors.ErrBucketNotEmpty)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
n.cache.DeleteBucket(p.BktInfo)
|
n.cache.DeleteBucket(p.BktInfo)
|
||||||
|
|
||||||
|
|
52
docs/extensions.md
Normal file
52
docs/extensions.md
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
# S3 API Extension
|
||||||
|
|
||||||
|
## Bucket operations management
|
||||||
|
|
||||||
|
### Action to delete bucket (DeleteBucket)
|
||||||
|
|
||||||
|
Deletes bucket with all objects in it.
|
||||||
|
|
||||||
|
#### Request Parameters
|
||||||
|
|
||||||
|
- **Bucket**
|
||||||
|
|
||||||
|
Specifies the bucket being deleted.
|
||||||
|
|
||||||
|
|
||||||
|
#### Errors
|
||||||
|
|
||||||
|
- **NoSuchEntity**
|
||||||
|
|
||||||
|
The request was rejected because it referenced a resource entity that does not exist.
|
||||||
|
|
||||||
|
HTTP Status Code: 404
|
||||||
|
|
||||||
|
- **ServiceFailure**
|
||||||
|
|
||||||
|
The request processing has failed because of an unknown error, exception or failure.
|
||||||
|
|
||||||
|
HTTP Status Code: 500
|
||||||
|
|
||||||
|
|
||||||
|
#### Example
|
||||||
|
|
||||||
|
Sample Request
|
||||||
|
|
||||||
|
```text
|
||||||
|
DELETE / HTTP/1.1
|
||||||
|
X-Amz-Force-Delete-Bucket: true
|
||||||
|
Host: data.s3.<Region>.frostfs-s3-gw.com
|
||||||
|
Date: Wed, 01 Mar 2024 12:00:00 GMT
|
||||||
|
Authorization: authorization string
|
||||||
|
```
|
||||||
|
|
||||||
|
Sample Response
|
||||||
|
|
||||||
|
```text
|
||||||
|
HTTP/1.1 204 No Content
|
||||||
|
x-amz-id-2: JuKZqmXuiwFeDQxhD7M8KtsKobSzWA1QEjLbTMTagkKdBX2z7Il/jGhDeJ3j6s80
|
||||||
|
x-amz-request-id: 32FE2CEB32F5EE25
|
||||||
|
Date: Wed, 01 Mar 2006 12:00:00 GMT
|
||||||
|
Connection: close
|
||||||
|
Server: AmazonS3
|
||||||
|
```
|
Loading…
Reference in a new issue