mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-10-30 17:00:48 +01:00
s3api: return NoSuchUpload when aborting an upload that was already completed (#12227)
s3: AbortMultipartUpload answers NoSuchUpload for an upload that no longer exists
Complete and abort both remove .uploads/<id>. abortMultipartUpload answered
204 whenever that record was missing, so aborting an upload that had already
completed reported success while the object stayed in place, and a client
could read it as the object being discarded. AWS returns 404 NoSuchUpload
("The specified multipart upload does not exist") for that case, and
ListParts here already does.
Answer ErrNoSuchUpload when the record is not found. checkUploadStillOpen
reuses abort for cleanup after the upload is gone; NoSuchUpload is not a
failed cleanup there.
The leftover-directory path from #11385 is unchanged: a .uploads/<id> that
outlived its completed object is still removed metadata-only and answered.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
a2f595b207
commit
2a1afadcd0
3 files changed
+32
-8
No files matched your search
@@ -1156,15 +1156,17 @@ func (s3a *S3ApiServer) abortMultipartUpload(input *s3.AbortMultipartUploadInput
|
||||
|
||||
uploadsFolder := s3a.genUploadsFolder(*input.Bucket)
|
||||
uploadEntry, err := s3a.getEntry(uploadsFolder, *input.UploadId)
|
||||
// complete and abort both remove .uploads/<id>; a missing record is an upload
|
||||
// that does not exist (NoSuchUpload on AWS), not one this call discarded
|
||||
if err != nil {
|
||||
if isFilerNotFound(err) {
|
||||
return &s3.AbortMultipartUploadOutput{}, s3err.ErrNone
|
||||
return nil, s3err.ErrNoSuchUpload
|
||||
}
|
||||
glog.Errorf("bucket %s abort upload %s: %v", *input.Bucket, *input.UploadId, err)
|
||||
return nil, s3err.ErrInternalError
|
||||
}
|
||||
if uploadEntry == nil {
|
||||
return &s3.AbortMultipartUploadOutput{}, s3err.ErrNone
|
||||
return nil, s3err.ErrNoSuchUpload
|
||||
}
|
||||
|
||||
object := string(uploadEntry.Extended[s3_constants.ExtMultipartObjectKey])
|
||||
|
||||
@@ -189,14 +189,36 @@ func TestAbortUndecidableCheckRefuses(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// No upload directory at all answers success, the same as before.
|
||||
func TestAbortGoneUploadAnswersSuccess(t *testing.T) {
|
||||
s3a := newAbortTestServer(t, &fakeAbortFiler{})
|
||||
// No upload directory at all is an upload that does not exist: NoSuchUpload
|
||||
// (404), as on AWS, not a successful abort.
|
||||
func TestAbortGoneUploadAnswersNoSuchUpload(t *testing.T) {
|
||||
f := &fakeAbortFiler{}
|
||||
s3a := newAbortTestServer(t, f)
|
||||
|
||||
_, code := s3a.abortMultipartUpload(abortInput("gone"))
|
||||
|
||||
if code != s3err.ErrNone {
|
||||
t.Fatalf("code = %v, want ErrNone", code)
|
||||
if code != s3err.ErrNoSuchUpload {
|
||||
t.Fatalf("code = %v, want ErrNoSuchUpload", code)
|
||||
}
|
||||
if f.deleteReq != nil {
|
||||
t.Fatalf("delete issued for a missing upload: %+v", f.deleteReq)
|
||||
}
|
||||
}
|
||||
|
||||
// Completion removes .uploads/<id>; aborting the completed upload afterwards
|
||||
// must say NoSuchUpload, not 204, so the client cannot read it as the object
|
||||
// being discarded, and must not touch the object.
|
||||
func TestAbortAfterCompleteAnswersNoSuchUpload(t *testing.T) {
|
||||
f := &fakeAbortFiler{objectEntry: versionEntry("a.bin", "up1")}
|
||||
s3a := newAbortTestServer(t, f)
|
||||
|
||||
_, code := s3a.abortMultipartUpload(abortInput("up1"))
|
||||
|
||||
if code != s3err.ErrNoSuchUpload {
|
||||
t.Fatalf("code = %v, want ErrNoSuchUpload", code)
|
||||
}
|
||||
if f.deleteReq != nil {
|
||||
t.Fatalf("delete issued after completion: %+v", f.deleteReq)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -546,7 +546,7 @@ func (s3a *S3ApiServer) checkUploadStillOpen(w http.ResponseWriter, r *http.Requ
|
||||
Bucket: aws.String(bucket),
|
||||
Key: objectKey(aws.String(object)),
|
||||
UploadId: aws.String(uploadID),
|
||||
}); code != s3err.ErrNone {
|
||||
}); code != s3err.ErrNone && code != s3err.ErrNoSuchUpload {
|
||||
glog.Warningf("checkUploadStillOpen %s/%s: part left behind, cleanup failed", bucket, uploadID)
|
||||
}
|
||||
s3err.WriteErrorResponse(w, r, s3err.ErrNoSuchUpload)
|
||||
|
||||
Reference in new issue
Block a user