diff --git a/backend/azure/azure.go b/backend/azure/azure.go index e2905ce..2ca3abf 100644 --- a/backend/azure/azure.go +++ b/backend/azure/azure.go @@ -769,7 +769,7 @@ func (az *Azure) DeleteObjectTagging(ctx context.Context, bucket, object string) return nil } -func (az *Azure) CreateMultipartUpload(ctx context.Context, input *s3.CreateMultipartUploadInput) (*s3.CreateMultipartUploadOutput, error) { +func (az *Azure) CreateMultipartUpload(ctx context.Context, input *s3.CreateMultipartUploadInput) (s3response.InitiateMultipartUploadResult, error) { // Multipart upload starts with UploadPart action so there is no // correlating function for creating mutlipart uploads. // TODO: since azure only allows for a single multipart upload @@ -779,10 +779,10 @@ func (az *Azure) CreateMultipartUpload(ctx context.Context, input *s3.CreateMult // Alternatively, is there something we can do with upload ids to // keep concurrent uploads unique still? I haven't found an efficient // way to rename final objects. - return &s3.CreateMultipartUploadOutput{ - Bucket: input.Bucket, - Key: input.Key, - UploadId: input.Key, + return s3response.InitiateMultipartUploadResult{ + Bucket: *input.Bucket, + Key: *input.Key, + UploadId: *input.Key, }, nil } diff --git a/backend/backend.go b/backend/backend.go index 7e7238d..00c6b82 100644 --- a/backend/backend.go +++ b/backend/backend.go @@ -48,7 +48,7 @@ type Backend interface { DeleteBucketOwnershipControls(_ context.Context, bucket string) error // multipart operations - CreateMultipartUpload(context.Context, *s3.CreateMultipartUploadInput) (*s3.CreateMultipartUploadOutput, error) + CreateMultipartUpload(context.Context, *s3.CreateMultipartUploadInput) (s3response.InitiateMultipartUploadResult, error) CompleteMultipartUpload(context.Context, *s3.CompleteMultipartUploadInput) (*s3.CompleteMultipartUploadOutput, error) AbortMultipartUpload(context.Context, *s3.AbortMultipartUploadInput) error ListMultipartUploads(context.Context, *s3.ListMultipartUploadsInput) (s3response.ListMultipartUploadsResult, error) @@ -151,8 +151,8 @@ func (BackendUnsupported) DeleteBucketOwnershipControls(_ context.Context, bucke return s3err.GetAPIError(s3err.ErrNotImplemented) } -func (BackendUnsupported) CreateMultipartUpload(context.Context, *s3.CreateMultipartUploadInput) (*s3.CreateMultipartUploadOutput, error) { - return nil, s3err.GetAPIError(s3err.ErrNotImplemented) +func (BackendUnsupported) CreateMultipartUpload(context.Context, *s3.CreateMultipartUploadInput) (s3response.InitiateMultipartUploadResult, error) { + return s3response.InitiateMultipartUploadResult{}, s3err.GetAPIError(s3err.ErrNotImplemented) } func (BackendUnsupported) CompleteMultipartUpload(context.Context, *s3.CompleteMultipartUploadInput) (*s3.CompleteMultipartUploadOutput, error) { return nil, s3err.GetAPIError(s3err.ErrNotImplemented) diff --git a/backend/posix/posix.go b/backend/posix/posix.go index 25c30b7..c931f37 100644 --- a/backend/posix/posix.go +++ b/backend/posix/posix.go @@ -377,12 +377,12 @@ func (p *Posix) DeleteBucketOwnershipControls(_ context.Context, bucket string) return nil } -func (p *Posix) CreateMultipartUpload(ctx context.Context, mpu *s3.CreateMultipartUploadInput) (*s3.CreateMultipartUploadOutput, error) { +func (p *Posix) CreateMultipartUpload(ctx context.Context, mpu *s3.CreateMultipartUploadInput) (s3response.InitiateMultipartUploadResult, error) { if mpu.Bucket == nil { - return nil, s3err.GetAPIError(s3err.ErrInvalidBucketName) + return s3response.InitiateMultipartUploadResult{}, s3err.GetAPIError(s3err.ErrInvalidBucketName) } if mpu.Key == nil { - return nil, s3err.GetAPIError(s3err.ErrNoSuchKey) + return s3response.InitiateMultipartUploadResult{}, s3err.GetAPIError(s3err.ErrNoSuchKey) } bucket := *mpu.Bucket @@ -390,16 +390,16 @@ func (p *Posix) CreateMultipartUpload(ctx context.Context, mpu *s3.CreateMultipa _, err := os.Stat(bucket) if errors.Is(err, fs.ErrNotExist) { - return nil, s3err.GetAPIError(s3err.ErrNoSuchBucket) + return s3response.InitiateMultipartUploadResult{}, s3err.GetAPIError(s3err.ErrNoSuchBucket) } if err != nil { - return nil, fmt.Errorf("stat bucket: %w", err) + return s3response.InitiateMultipartUploadResult{}, fmt.Errorf("stat bucket: %w", err) } if strings.HasSuffix(*mpu.Key, "/") { // directory objects can't be uploaded with mutlipart uploads // because posix directories can't contain data - return nil, s3err.GetAPIError(s3err.ErrDirectoryObjectContainsData) + return s3response.InitiateMultipartUploadResult{}, s3err.GetAPIError(s3err.ErrDirectoryObjectContainsData) } // parse object tags @@ -410,10 +410,10 @@ func (p *Posix) CreateMultipartUpload(ctx context.Context, mpu *s3.CreateMultipa for _, prt := range tagParts { p := strings.Split(prt, "=") if len(p) != 2 { - return nil, s3err.GetAPIError(s3err.ErrInvalidTag) + return s3response.InitiateMultipartUploadResult{}, s3err.GetAPIError(s3err.ErrInvalidTag) } if len(p[0]) > 128 || len(p[1]) > 256 { - return nil, s3err.GetAPIError(s3err.ErrInvalidTag) + return s3response.InitiateMultipartUploadResult{}, s3err.GetAPIError(s3err.ErrInvalidTag) } tags[p[0]] = p[1] } @@ -431,7 +431,7 @@ func (p *Posix) CreateMultipartUpload(ctx context.Context, mpu *s3.CreateMultipa // associated with this specific multipart upload err = os.MkdirAll(filepath.Join(tmppath, uploadID), 0755) if err != nil { - return nil, fmt.Errorf("create upload temp dir: %w", err) + return s3response.InitiateMultipartUploadResult{}, fmt.Errorf("create upload temp dir: %w", err) } // set an attribute with the original object name so that we can @@ -443,7 +443,7 @@ func (p *Posix) CreateMultipartUpload(ctx context.Context, mpu *s3.CreateMultipa // other uploads for the same object name outstanding os.RemoveAll(filepath.Join(tmppath, uploadID)) os.Remove(tmppath) - return nil, fmt.Errorf("set name attr for upload: %w", err) + return s3response.InitiateMultipartUploadResult{}, fmt.Errorf("set name attr for upload: %w", err) } // set user metadata @@ -454,7 +454,7 @@ func (p *Posix) CreateMultipartUpload(ctx context.Context, mpu *s3.CreateMultipa // cleanup object if returning error os.RemoveAll(filepath.Join(tmppath, uploadID)) os.Remove(tmppath) - return nil, fmt.Errorf("set user attr %q: %w", k, err) + return s3response.InitiateMultipartUploadResult{}, fmt.Errorf("set user attr %q: %w", k, err) } } @@ -465,7 +465,7 @@ func (p *Posix) CreateMultipartUpload(ctx context.Context, mpu *s3.CreateMultipa // cleanup object if returning error os.RemoveAll(filepath.Join(tmppath, uploadID)) os.Remove(tmppath) - return nil, err + return s3response.InitiateMultipartUploadResult{}, err } } @@ -477,7 +477,7 @@ func (p *Posix) CreateMultipartUpload(ctx context.Context, mpu *s3.CreateMultipa // cleanup object if returning error os.RemoveAll(filepath.Join(tmppath, uploadID)) os.Remove(tmppath) - return nil, fmt.Errorf("set content-type: %w", err) + return s3response.InitiateMultipartUploadResult{}, fmt.Errorf("set content-type: %w", err) } } @@ -487,7 +487,7 @@ func (p *Posix) CreateMultipartUpload(ctx context.Context, mpu *s3.CreateMultipa // cleanup object if returning error os.RemoveAll(filepath.Join(tmppath, uploadID)) os.Remove(tmppath) - return nil, err + return s3response.InitiateMultipartUploadResult{}, err } } @@ -502,20 +502,20 @@ func (p *Posix) CreateMultipartUpload(ctx context.Context, mpu *s3.CreateMultipa // cleanup object if returning error os.RemoveAll(filepath.Join(tmppath, uploadID)) os.Remove(tmppath) - return nil, fmt.Errorf("parse object lock retention: %w", err) + return s3response.InitiateMultipartUploadResult{}, fmt.Errorf("parse object lock retention: %w", err) } if err := p.PutObjectRetention(ctx, bucket, filepath.Join(objdir, uploadID), "", true, retParsed); err != nil { // cleanup object if returning error os.RemoveAll(filepath.Join(tmppath, uploadID)) os.Remove(tmppath) - return nil, err + return s3response.InitiateMultipartUploadResult{}, err } } - return &s3.CreateMultipartUploadOutput{ - Bucket: &bucket, - Key: &object, - UploadId: &uploadID, + return s3response.InitiateMultipartUploadResult{ + Bucket: bucket, + Key: object, + UploadId: uploadID, }, nil } diff --git a/backend/s3proxy/s3.go b/backend/s3proxy/s3.go index 9a9533c..6960e94 100644 --- a/backend/s3proxy/s3.go +++ b/backend/s3proxy/s3.go @@ -161,9 +161,17 @@ func (s *S3Proxy) DeleteBucketOwnershipControls(ctx context.Context, bucket stri return handleError(err) } -func (s *S3Proxy) CreateMultipartUpload(ctx context.Context, input *s3.CreateMultipartUploadInput) (*s3.CreateMultipartUploadOutput, error) { +func (s *S3Proxy) CreateMultipartUpload(ctx context.Context, input *s3.CreateMultipartUploadInput) (s3response.InitiateMultipartUploadResult, error) { out, err := s.client.CreateMultipartUpload(ctx, input) - return out, handleError(err) + if err != nil { + return s3response.InitiateMultipartUploadResult{}, handleError(err) + } + + return s3response.InitiateMultipartUploadResult{ + Bucket: *out.Bucket, + Key: *out.Key, + UploadId: *out.UploadId, + }, nil } func (s *S3Proxy) CompleteMultipartUpload(ctx context.Context, input *s3.CompleteMultipartUploadInput) (*s3.CompleteMultipartUploadOutput, error) { diff --git a/s3api/controllers/backend_moq_test.go b/s3api/controllers/backend_moq_test.go index b5e9d5c..5e1af21 100644 --- a/s3api/controllers/backend_moq_test.go +++ b/s3api/controllers/backend_moq_test.go @@ -38,7 +38,7 @@ var _ backend.Backend = &BackendMock{} // CreateBucketFunc: func(contextMoqParam context.Context, createBucketInput *s3.CreateBucketInput, defaultACL []byte) error { // panic("mock out the CreateBucket method") // }, -// CreateMultipartUploadFunc: func(contextMoqParam context.Context, createMultipartUploadInput *s3.CreateMultipartUploadInput) (*s3.CreateMultipartUploadOutput, error) { +// CreateMultipartUploadFunc: func(contextMoqParam context.Context, createMultipartUploadInput *s3.CreateMultipartUploadInput) (s3response.InitiateMultipartUploadResult, error) { // panic("mock out the CreateMultipartUpload method") // }, // DeleteBucketFunc: func(contextMoqParam context.Context, deleteBucketInput *s3.DeleteBucketInput) error { @@ -199,7 +199,7 @@ type BackendMock struct { CreateBucketFunc func(contextMoqParam context.Context, createBucketInput *s3.CreateBucketInput, defaultACL []byte) error // CreateMultipartUploadFunc mocks the CreateMultipartUpload method. - CreateMultipartUploadFunc func(contextMoqParam context.Context, createMultipartUploadInput *s3.CreateMultipartUploadInput) (*s3.CreateMultipartUploadOutput, error) + CreateMultipartUploadFunc func(contextMoqParam context.Context, createMultipartUploadInput *s3.CreateMultipartUploadInput) (s3response.InitiateMultipartUploadResult, error) // DeleteBucketFunc mocks the DeleteBucket method. DeleteBucketFunc func(contextMoqParam context.Context, deleteBucketInput *s3.DeleteBucketInput) error @@ -974,7 +974,7 @@ func (mock *BackendMock) CreateBucketCalls() []struct { } // CreateMultipartUpload calls CreateMultipartUploadFunc. -func (mock *BackendMock) CreateMultipartUpload(contextMoqParam context.Context, createMultipartUploadInput *s3.CreateMultipartUploadInput) (*s3.CreateMultipartUploadOutput, error) { +func (mock *BackendMock) CreateMultipartUpload(contextMoqParam context.Context, createMultipartUploadInput *s3.CreateMultipartUploadInput) (s3response.InitiateMultipartUploadResult, error) { if mock.CreateMultipartUploadFunc == nil { panic("BackendMock.CreateMultipartUploadFunc: method is nil but Backend.CreateMultipartUpload was just called") } diff --git a/s3api/controllers/base_test.go b/s3api/controllers/base_test.go index d881df8..5c633ca 100644 --- a/s3api/controllers/base_test.go +++ b/s3api/controllers/base_test.go @@ -1697,8 +1697,8 @@ func TestS3ApiController_CreateActions(t *testing.T) { CompleteMultipartUploadFunc: func(context.Context, *s3.CompleteMultipartUploadInput) (*s3.CompleteMultipartUploadOutput, error) { return &s3.CompleteMultipartUploadOutput{}, nil }, - CreateMultipartUploadFunc: func(context.Context, *s3.CreateMultipartUploadInput) (*s3.CreateMultipartUploadOutput, error) { - return &s3.CreateMultipartUploadOutput{}, nil + CreateMultipartUploadFunc: func(context.Context, *s3.CreateMultipartUploadInput) (s3response.InitiateMultipartUploadResult, error) { + return s3response.InitiateMultipartUploadResult{}, nil }, SelectObjectContentFunc: func(context.Context, *s3.SelectObjectContentInput) func(w *bufio.Writer) { return func(w *bufio.Writer) {} diff --git a/s3response/s3response.go b/s3response/s3response.go index 0a76766..e7ed5e4 100644 --- a/s3response/s3response.go +++ b/s3response/s3response.go @@ -221,3 +221,9 @@ type Grantee struct { type OwnershipControls struct { Rules []types.OwnershipControlsRule `xml:"Rule"` } + +type InitiateMultipartUploadResult struct { + Bucket string + Key string + UploadId string +}