diff --git a/backend/backend.go b/backend/backend.go index 0e52dfb..41f9b8b 100644 --- a/backend/backend.go +++ b/backend/backend.go @@ -39,7 +39,7 @@ type Backend interface { PutBucketAcl(_ context.Context, bucket string, data []byte) error DeleteBucket(context.Context, *s3.DeleteBucketInput) error PutBucketVersioning(_ context.Context, bucket string, status types.BucketVersioningStatus) error - GetBucketVersioning(_ context.Context, bucket string) (*s3.GetBucketVersioningOutput, error) + GetBucketVersioning(_ context.Context, bucket string) (s3response.GetBucketVersioningOutput, error) PutBucketPolicy(_ context.Context, bucket string, policy []byte) error GetBucketPolicy(_ context.Context, bucket string) ([]byte, error) DeleteBucketPolicy(_ context.Context, bucket string) error @@ -129,8 +129,8 @@ func (BackendUnsupported) DeleteBucket(context.Context, *s3.DeleteBucketInput) e func (BackendUnsupported) PutBucketVersioning(_ context.Context, bucket string, status types.BucketVersioningStatus) error { return s3err.GetAPIError(s3err.ErrNotImplemented) } -func (BackendUnsupported) GetBucketVersioning(_ context.Context, bucket string) (*s3.GetBucketVersioningOutput, error) { - return nil, s3err.GetAPIError(s3err.ErrNotImplemented) +func (BackendUnsupported) GetBucketVersioning(_ context.Context, bucket string) (s3response.GetBucketVersioningOutput, error) { + return s3response.GetBucketVersioningOutput{}, s3err.GetAPIError(s3err.ErrNotImplemented) } func (BackendUnsupported) PutBucketPolicy(_ context.Context, bucket string, policy []byte) error { return s3err.GetAPIError(s3err.ErrNotImplemented) diff --git a/backend/posix/posix.go b/backend/posix/posix.go index 06b7c06..1b1c75a 100644 --- a/backend/posix/posix.go +++ b/backend/posix/posix.go @@ -466,38 +466,41 @@ func (p *Posix) PutBucketVersioning(_ context.Context, bucket string, status typ return nil } -func (p *Posix) GetBucketVersioning(_ context.Context, bucket string) (*s3.GetBucketVersioningOutput, error) { +func (p *Posix) GetBucketVersioning(_ context.Context, bucket string) (s3response.GetBucketVersioningOutput, error) { if !p.versioningEnabled() { // AWS returns empty response, if versioning is not set //TODO: Maybe we need to return our custom error here? - return &s3.GetBucketVersioningOutput{}, nil + return s3response.GetBucketVersioningOutput{}, nil } _, err := os.Stat(bucket) if errors.Is(err, fs.ErrNotExist) { - return nil, s3err.GetAPIError(s3err.ErrNoSuchBucket) + return s3response.GetBucketVersioningOutput{}, s3err.GetAPIError(s3err.ErrNoSuchBucket) } if err != nil { - return nil, fmt.Errorf("stat bucket: %w", err) + return s3response.GetBucketVersioningOutput{}, fmt.Errorf("stat bucket: %w", err) } vData, err := p.meta.RetrieveAttribute(bucket, "", versioningKey) if errors.Is(err, meta.ErrNoSuchKey) { - return &s3.GetBucketVersioningOutput{}, nil + return s3response.GetBucketVersioningOutput{}, nil + } else if err != nil { + return s3response.GetBucketVersioningOutput{}, fmt.Errorf("get bucket versioning config: %w", err) } + enabled, suspended := types.BucketVersioningStatusEnabled, types.BucketVersioningStatusSuspended switch vData[0] { case 1: - return &s3.GetBucketVersioningOutput{ - Status: types.BucketVersioningStatusEnabled, + return s3response.GetBucketVersioningOutput{ + Status: &enabled, }, nil case 0: - return &s3.GetBucketVersioningOutput{ - Status: types.BucketVersioningStatusSuspended, + return s3response.GetBucketVersioningOutput{ + Status: &suspended, }, nil } - return &s3.GetBucketVersioningOutput{}, nil + return s3response.GetBucketVersioningOutput{}, nil } // Returns the specified bucket versioning status @@ -507,7 +510,11 @@ func (p *Posix) isBucketVersioningEnabled(ctx context.Context, bucket string) (b return false, err } - return res.Status == types.BucketVersioningStatusEnabled, nil + if res.Status != nil { + return *res.Status == types.BucketVersioningStatusEnabled, nil + } + + return false, nil } // Generates the object version path in the versioning directory diff --git a/backend/s3proxy/s3.go b/backend/s3proxy/s3.go index 3086427..14a6878 100644 --- a/backend/s3proxy/s3.go +++ b/backend/s3proxy/s3.go @@ -172,12 +172,15 @@ func (s *S3Proxy) PutBucketVersioning(ctx context.Context, bucket string, status return handleError(err) } -func (s *S3Proxy) GetBucketVersioning(ctx context.Context, bucket string) (*s3.GetBucketVersioningOutput, error) { +func (s *S3Proxy) GetBucketVersioning(ctx context.Context, bucket string) (s3response.GetBucketVersioningOutput, error) { out, err := s.client.GetBucketVersioning(ctx, &s3.GetBucketVersioningInput{ Bucket: &bucket, }) - return out, handleError(err) + return s3response.GetBucketVersioningOutput{ + Status: &out.Status, + MFADelete: &out.MFADelete, + }, handleError(err) } func (s *S3Proxy) ListObjectVersions(ctx context.Context, input *s3.ListObjectVersionsInput) (s3response.ListVersionsResult, error) { diff --git a/s3api/controllers/backend_moq_test.go b/s3api/controllers/backend_moq_test.go index 75c4804..2b1bbac 100644 --- a/s3api/controllers/backend_moq_test.go +++ b/s3api/controllers/backend_moq_test.go @@ -74,7 +74,7 @@ var _ backend.Backend = &BackendMock{} // GetBucketTaggingFunc: func(contextMoqParam context.Context, bucket string) (map[string]string, error) { // panic("mock out the GetBucketTagging method") // }, -// GetBucketVersioningFunc: func(contextMoqParam context.Context, bucket string) (*s3.GetBucketVersioningOutput, error) { +// GetBucketVersioningFunc: func(contextMoqParam context.Context, bucket string) (s3response.GetBucketVersioningOutput, error) { // panic("mock out the GetBucketVersioning method") // }, // GetObjectFunc: func(contextMoqParam context.Context, getObjectInput *s3.GetObjectInput) (*s3.GetObjectOutput, error) { @@ -235,7 +235,7 @@ type BackendMock struct { GetBucketTaggingFunc func(contextMoqParam context.Context, bucket string) (map[string]string, error) // GetBucketVersioningFunc mocks the GetBucketVersioning method. - GetBucketVersioningFunc func(contextMoqParam context.Context, bucket string) (*s3.GetBucketVersioningOutput, error) + GetBucketVersioningFunc func(contextMoqParam context.Context, bucket string) (s3response.GetBucketVersioningOutput, error) // GetObjectFunc mocks the GetObject method. GetObjectFunc func(contextMoqParam context.Context, getObjectInput *s3.GetObjectInput) (*s3.GetObjectOutput, error) @@ -1412,7 +1412,7 @@ func (mock *BackendMock) GetBucketTaggingCalls() []struct { } // GetBucketVersioning calls GetBucketVersioningFunc. -func (mock *BackendMock) GetBucketVersioning(contextMoqParam context.Context, bucket string) (*s3.GetBucketVersioningOutput, error) { +func (mock *BackendMock) GetBucketVersioning(contextMoqParam context.Context, bucket string) (s3response.GetBucketVersioningOutput, error) { if mock.GetBucketVersioningFunc == nil { panic("BackendMock.GetBucketVersioningFunc: method is nil but Backend.GetBucketVersioning was just called") } diff --git a/s3api/controllers/base_test.go b/s3api/controllers/base_test.go index 96ff2dd..9869a24 100644 --- a/s3api/controllers/base_test.go +++ b/s3api/controllers/base_test.go @@ -382,8 +382,8 @@ func TestS3ApiController_ListActions(t *testing.T) { GetBucketTaggingFunc: func(contextMoqParam context.Context, bucket string) (map[string]string, error) { return map[string]string{}, nil }, - GetBucketVersioningFunc: func(contextMoqParam context.Context, bucket string) (*s3.GetBucketVersioningOutput, error) { - return &s3.GetBucketVersioningOutput{}, nil + GetBucketVersioningFunc: func(contextMoqParam context.Context, bucket string) (s3response.GetBucketVersioningOutput, error) { + return s3response.GetBucketVersioningOutput{}, nil }, ListObjectVersionsFunc: func(contextMoqParam context.Context, listObjectVersionsInput *s3.ListObjectVersionsInput) (s3response.ListVersionsResult, error) { return s3response.ListVersionsResult{}, nil diff --git a/s3response/s3response.go b/s3response/s3response.go index 9d8b9b0..fc29af6 100644 --- a/s3response/s3response.go +++ b/s3response/s3response.go @@ -384,3 +384,9 @@ type ListVersionsResult struct { VersionIdMarker *string Versions []types.ObjectVersion `xml:"Version"` } + +type GetBucketVersioningOutput struct { + XMLName xml.Name `xml:"http://s3.amazonaws.com/doc/2006-03-01/ VersioningConfiguration" json:"-"` + MFADelete *types.MFADeleteStatus + Status *types.BucketVersioningStatus +} diff --git a/tests/integration/group-tests.go b/tests/integration/group-tests.go index 275632b..0ceaa1c 100644 --- a/tests/integration/group-tests.go +++ b/tests/integration/group-tests.go @@ -521,6 +521,7 @@ func TestVersioning(s *S3Conf) { PutBucketVersioning_success(s) // GetBucketVersioning action GetBucketVersioning_non_existing_bucket(s) + GetBucketVersioning_empty_response(s) GetBucketVersioning_success(s) Versioning_PutObject_success(s) // CopyObject action @@ -873,6 +874,7 @@ func GetIntTests() IntTests { "PutBucketVersioning_invalid_status": PutBucketVersioning_invalid_status, "PutBucketVersioning_success": PutBucketVersioning_success, "GetBucketVersioning_non_existing_bucket": GetBucketVersioning_non_existing_bucket, + "GetBucketVersioning_empty_response": GetBucketVersioning_empty_response, "GetBucketVersioning_success": GetBucketVersioning_success, "Versioning_PutObject_success": Versioning_PutObject_success, "Versioning_CopyObject_success": Versioning_CopyObject_success, diff --git a/tests/integration/tests.go b/tests/integration/tests.go index 9d47b8e..ff5e559 100644 --- a/tests/integration/tests.go +++ b/tests/integration/tests.go @@ -10447,6 +10447,29 @@ func GetBucketVersioning_non_existing_bucket(s *S3Conf) error { }) } +func GetBucketVersioning_empty_response(s *S3Conf) error { + testName := "GetBucketVersioning_empty_response" + return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error { + ctx, cancel := context.WithTimeout(context.Background(), shortTimeout) + res, err := s3client.GetBucketVersioning(ctx, &s3.GetBucketVersioningInput{ + Bucket: &bucket, + }) + cancel() + if err != nil { + return err + } + + if res.Status != "" { + return fmt.Errorf("expected empty versioning status, instead got %v", res.Status) + } + if res.MFADelete != "" { + return fmt.Errorf("expected empty mfa delete status, instead got %v", res.MFADelete) + } + + return nil + }) +} + func GetBucketVersioning_success(s *S3Conf) error { testName := "GetBucketVersioning_success" return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {