s3api: make conditional mutations atomic and AWS-compatible (#8802)
* s3api: serialize conditional write finalization * s3api: add conditional delete mutation checks * s3api: enforce destination conditions for copy * s3api: revalidate multipart completion under lock * s3api: rollback failed put finalization hooks * s3api: report delete-marker version deletions * s3api: fix copy destination versioning edge cases * s3api: make versioned multipart completion idempotent * test/s3: cover conditional mutation regressions * s3api: rollback failed copy version finalization * s3api: resolve suspended delete conditions via latest entry * s3api: remove copy test null-version injection * s3api: reject out-of-order multipart completions * s3api: preserve multipart replay version metadata * s3api: surface copy destination existence errors * s3api: simplify delete condition target resolution * test/s3: make conditional delete assertions order independent * test/s3: add distributed lock gateway integration * s3api: fail closed multipart versioned completion * s3api: harden copy metadata and overwrite paths * s3api: create delete markers for suspended deletes * s3api: allow duplicate multipart completion parts
This commit is contained in:
@@ -2,6 +2,7 @@ package s3api
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"net/http"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -54,6 +55,42 @@ func TestListPartsResult(t *testing.T) {
|
||||
|
||||
}
|
||||
|
||||
func TestCompleteMultipartResultIncludesVersionId(t *testing.T) {
|
||||
r := &http.Request{Host: "localhost", Header: make(http.Header)}
|
||||
input := &s3.CompleteMultipartUploadInput{
|
||||
Bucket: aws.String("example-bucket"),
|
||||
Key: aws.String("example-object"),
|
||||
}
|
||||
|
||||
entry := &filer_pb.Entry{
|
||||
Extended: map[string][]byte{
|
||||
s3_constants.ExtVersionIdKey: []byte("version-123"),
|
||||
},
|
||||
}
|
||||
|
||||
result := completeMultipartResult(r, input, "\"etag-value\"", entry)
|
||||
if assert.NotNil(t, result.VersionId) {
|
||||
assert.Equal(t, "version-123", *result.VersionId)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCompleteMultipartResultOmitsNullVersionId(t *testing.T) {
|
||||
r := &http.Request{Host: "localhost", Header: make(http.Header)}
|
||||
input := &s3.CompleteMultipartUploadInput{
|
||||
Bucket: aws.String("example-bucket"),
|
||||
Key: aws.String("example-object"),
|
||||
}
|
||||
|
||||
entry := &filer_pb.Entry{
|
||||
Extended: map[string][]byte{
|
||||
s3_constants.ExtVersionIdKey: []byte("null"),
|
||||
},
|
||||
}
|
||||
|
||||
result := completeMultipartResult(r, input, "\"etag-value\"", entry)
|
||||
assert.Nil(t, result.VersionId)
|
||||
}
|
||||
|
||||
func Test_parsePartNumber(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
@@ -190,3 +227,41 @@ func TestValidateCompletePartETag(t *testing.T) {
|
||||
assert.True(t, invalid)
|
||||
})
|
||||
}
|
||||
|
||||
func TestCompleteMultipartUploadRejectsOutOfOrderParts(t *testing.T) {
|
||||
s3a := NewS3ApiServerForTest()
|
||||
input := &s3.CompleteMultipartUploadInput{
|
||||
Bucket: aws.String("bucket"),
|
||||
Key: aws.String("object"),
|
||||
UploadId: aws.String("upload"),
|
||||
}
|
||||
parts := &CompleteMultipartUpload{
|
||||
Parts: []CompletedPart{
|
||||
{PartNumber: 2, ETag: "\"etag-2\""},
|
||||
{PartNumber: 1, ETag: "\"etag-1\""},
|
||||
},
|
||||
}
|
||||
|
||||
result, errCode := s3a.completeMultipartUpload(&http.Request{Header: make(http.Header)}, input, parts)
|
||||
assert.Nil(t, result)
|
||||
assert.Equal(t, s3err.ErrInvalidPartOrder, errCode)
|
||||
}
|
||||
|
||||
func TestCompleteMultipartUploadAllowsDuplicatePartNumbers(t *testing.T) {
|
||||
s3a := NewS3ApiServerForTest()
|
||||
input := &s3.CompleteMultipartUploadInput{
|
||||
Bucket: aws.String("bucket"),
|
||||
Key: aws.String("object"),
|
||||
UploadId: aws.String("upload"),
|
||||
}
|
||||
parts := &CompleteMultipartUpload{
|
||||
Parts: []CompletedPart{
|
||||
{PartNumber: 1, ETag: "\"etag-older\""},
|
||||
{PartNumber: 1, ETag: "\"etag-newer\""},
|
||||
},
|
||||
}
|
||||
|
||||
result, errCode := s3a.completeMultipartUpload(&http.Request{Header: make(http.Header)}, input, parts)
|
||||
assert.Nil(t, result)
|
||||
assert.Equal(t, s3err.ErrNoSuchUpload, errCode)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user