diff --git a/backend/posix/posix.go b/backend/posix/posix.go index 1584f59..cba7095 100644 --- a/backend/posix/posix.go +++ b/backend/posix/posix.go @@ -1777,7 +1777,7 @@ func (p *Posix) GetBucketTagging(_ context.Context, bucket string) (map[string]s return nil, err } - acl, err := xattr.Get(bucket, "user."+tagHdr) + acl, err := xattr.Get(bucket, proxyBucketAclKey) if isNoAttr(err) { return tags, nil } @@ -1785,7 +1785,7 @@ func (p *Posix) GetBucketTagging(_ context.Context, bucket string) (map[string]s return nil, fmt.Errorf("get tags: %w", err) } - tags[aclkey] = string(acl) + tags[proxyAclKey] = string(acl) return tags, nil } diff --git a/cmd/versitygw/test.go b/cmd/versitygw/test.go index c1c5217..822612c 100644 --- a/cmd/versitygw/test.go +++ b/cmd/versitygw/test.go @@ -268,9 +268,11 @@ func getAction(tf testFunc) func(*cli.Context) error { func extractIntTests() (commands []*cli.Command) { tests := integration.GetIntTests() for key, val := range tests { + testKey := key + testFunc := val commands = append(commands, &cli.Command{ - Name: key, - Usage: fmt.Sprintf("Runs %v integration test", key), + Name: testKey, + Usage: fmt.Sprintf("Runs %v integration test", testKey), Action: func(ctx *cli.Context) error { opts := []integration.Option{ integration.WithAccess(awsID), @@ -283,7 +285,7 @@ func extractIntTests() (commands []*cli.Command) { } s := integration.NewS3Conf(opts...) - err := val(s) + err := testFunc(s) return err }, }) diff --git a/integration/group-tests.go b/integration/group-tests.go index 24d8488..b63a904 100644 --- a/integration/group-tests.go +++ b/integration/group-tests.go @@ -48,6 +48,23 @@ func TestDeleteBucket(s *S3Conf) { DeleteBucket_success_status_code(s) } +func TestPutBucketTagging(s *S3Conf) { + PutBucketTagging_non_existing_bucket(s) + PutBucketTagging_long_tags(s) + PutBucketTagging_success(s) +} + +func TestGetBucketTagging(s *S3Conf) { + GetBucketTagging_non_existing_bucket(s) + GetBucketTagging_success(s) +} + +func TestDeleteBucketTagging(s *S3Conf) { + DeleteBucketTagging_non_existing_object(s) + DeleteBucketTagging_success_status(s) + DeleteBucketTagging_success(s) +} + func TestPutObject(s *S3Conf) { PutObject_non_existing_bucket(s) PutObject_special_chars(s) @@ -199,6 +216,9 @@ func TestFullFlow(s *S3Conf) { TestHeadBucket(s) TestListBuckets(s) TestDeleteBucket(s) + TestPutBucketTagging(s) + TestGetBucketTagging(s) + TestDeleteBucketTagging(s) TestPutObject(s) TestHeadObject(s) TestGetObject(s) @@ -263,6 +283,14 @@ func GetIntTests() IntTests { "DeleteBucket_non_existing_bucket": DeleteBucket_non_existing_bucket, "DeleteBucket_non_empty_bucket": DeleteBucket_non_empty_bucket, "DeleteBucket_success_status_code": DeleteBucket_success_status_code, + "PutBucketTagging_non_existing_bucket": PutBucketTagging_non_existing_bucket, + "PutBucketTagging_long_tags": PutBucketTagging_long_tags, + "PutBucketTagging_success": PutBucketTagging_success, + "GetBucketTagging_non_existing_bucket": GetBucketTagging_non_existing_bucket, + "GetBucketTagging_success": GetBucketTagging_success, + "DeleteBucketTagging_non_existing_object": DeleteBucketTagging_non_existing_object, + "DeleteBucketTagging_success_status": DeleteBucketTagging_success_status, + "DeleteBucketTagging_success": DeleteBucketTagging_success, "PutObject_non_existing_bucket": PutObject_non_existing_bucket, "PutObject_special_chars": PutObject_special_chars, "PutObject_invalid_long_tags": PutObject_invalid_long_tags, diff --git a/integration/tests.go b/integration/tests.go index c014a45..1775e9c 100644 --- a/integration/tests.go +++ b/integration/tests.go @@ -1128,6 +1128,215 @@ func DeleteBucket_success_status_code(s *S3Conf) error { return nil } +func PutBucketTagging_non_existing_bucket(s *S3Conf) error { + testName := "PutBucketTagging_non_existing_bucket" + return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error { + ctx, cancel := context.WithTimeout(context.Background(), shortTimeout) + _, err := s3client.PutBucketTagging(ctx, &s3.PutBucketTaggingInput{ + Bucket: getPtr(getBucketName()), + Tagging: &types.Tagging{TagSet: []types.Tag{}}, + }) + cancel() + if err := checkApiErr(err, s3err.GetAPIError(s3err.ErrNoSuchBucket)); err != nil { + return err + } + + return nil + }) +} + +func PutBucketTagging_long_tags(s *S3Conf) error { + testName := "PutBucketTagging_long_tags" + return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error { + tagging := types.Tagging{TagSet: []types.Tag{{Key: getPtr(genRandString(200)), Value: getPtr("val")}}} + + ctx, cancel := context.WithTimeout(context.Background(), shortTimeout) + _, err := s3client.PutBucketTagging(ctx, &s3.PutBucketTaggingInput{ + Bucket: &bucket, + Tagging: &tagging}) + cancel() + if err := checkApiErr(err, s3err.GetAPIError(s3err.ErrInvalidTag)); err != nil { + return err + } + + tagging = types.Tagging{TagSet: []types.Tag{{Key: getPtr("key"), Value: getPtr(genRandString(300))}}} + + ctx, cancel = context.WithTimeout(context.Background(), shortTimeout) + _, err = s3client.PutBucketTagging(ctx, &s3.PutBucketTaggingInput{ + Bucket: &bucket, + Tagging: &tagging}) + cancel() + if err := checkApiErr(err, s3err.GetAPIError(s3err.ErrInvalidTag)); err != nil { + return err + } + + return nil + }) +} + +func PutBucketTagging_success(s *S3Conf) error { + testName := "PutBucketTagging_success" + return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error { + tagging := types.Tagging{TagSet: []types.Tag{{Key: getPtr("key1"), Value: getPtr("val2")}, {Key: getPtr("key2"), Value: getPtr("val2")}}} + + ctx, cancel := context.WithTimeout(context.Background(), shortTimeout) + _, err := s3client.PutBucketTagging(ctx, &s3.PutBucketTaggingInput{ + Bucket: &bucket, + Tagging: &tagging}) + cancel() + if err != nil { + return err + } + + return nil + }) +} + +func GetBucketTagging_non_existing_bucket(s *S3Conf) error { + testName := "GetBucketTagging_non_existing_object" + return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error { + ctx, cancel := context.WithTimeout(context.Background(), shortTimeout) + _, err := s3client.GetBucketTagging(ctx, &s3.GetBucketTaggingInput{ + Bucket: getPtr(getBucketName()), + }) + cancel() + if err := checkApiErr(err, s3err.GetAPIError(s3err.ErrNoSuchBucket)); err != nil { + return err + } + return nil + }) +} + +func GetBucketTagging_success(s *S3Conf) error { + testName := "GetBucketTagging_success" + return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error { + tagging := types.Tagging{TagSet: []types.Tag{{Key: getPtr("key1"), Value: getPtr("val2")}, {Key: getPtr("key2"), Value: getPtr("val2")}}} + + ctx, cancel := context.WithTimeout(context.Background(), shortTimeout) + _, err := s3client.PutBucketTagging(ctx, &s3.PutBucketTaggingInput{ + Bucket: &bucket, + Tagging: &tagging}) + cancel() + if err != nil { + return err + } + + ctx, cancel = context.WithTimeout(context.Background(), shortTimeout) + out, err := s3client.GetBucketTagging(ctx, &s3.GetBucketTaggingInput{ + Bucket: &bucket, + }) + cancel() + if err != nil { + return nil + } + + if !areTagsSame(out.TagSet, tagging.TagSet) { + return fmt.Errorf("expected %v instead got %v", tagging.TagSet, out.TagSet) + } + + return nil + }) +} + +func DeleteBucketTagging_non_existing_object(s *S3Conf) error { + testName := "DeleteBucketTagging_non_existing_object" + return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error { + ctx, cancel := context.WithTimeout(context.Background(), shortTimeout) + _, err := s3client.DeleteBucketTagging(ctx, &s3.DeleteBucketTaggingInput{ + Bucket: getPtr(getBucketName()), + }) + cancel() + if err := checkApiErr(err, s3err.GetAPIError(s3err.ErrNoSuchBucket)); err != nil { + return err + } + return nil + }) +} + +func DeleteBucketTagging_success_status(s *S3Conf) error { + testName := "DeleteBucketTagging_success_status" + return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error { + tagging := types.Tagging{ + TagSet: []types.Tag{ + { + Key: getPtr("Hello"), + Value: getPtr("World"), + }, + }, + } + + ctx, cancel := context.WithTimeout(context.Background(), shortTimeout) + _, err := s3client.PutBucketTagging(ctx, &s3.PutBucketTaggingInput{ + Bucket: &bucket, + Tagging: &tagging, + }) + cancel() + if err != nil { + return err + } + + req, err := createSignedReq(http.MethodDelete, s.endpoint, fmt.Sprintf("%v?tagging", bucket), s.awsID, s.awsSecret, "s3", s.awsRegion, nil, time.Now()) + if err != nil { + return err + } + + client := http.Client{ + Timeout: shortTimeout, + } + + resp, err := client.Do(req) + if err != nil { + return err + } + + if resp.StatusCode != http.StatusNoContent { + return fmt.Errorf("expected response status to be %v, instead got %v", http.StatusNoContent, resp.StatusCode) + } + + return nil + }) +} + +func DeleteBucketTagging_success(s *S3Conf) error { + testName := "DeleteBucketTagging_success" + return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error { + tagging := types.Tagging{TagSet: []types.Tag{{Key: getPtr("key1"), Value: getPtr("val2")}, {Key: getPtr("key2"), Value: getPtr("val2")}}} + + ctx, cancel := context.WithTimeout(context.Background(), shortTimeout) + _, err := s3client.PutBucketTagging(ctx, &s3.PutBucketTaggingInput{ + Bucket: &bucket, + Tagging: &tagging}) + cancel() + if err != nil { + return err + } + + ctx, cancel = context.WithTimeout(context.Background(), shortTimeout) + _, err = s3client.DeleteBucketTagging(ctx, &s3.DeleteBucketTaggingInput{ + Bucket: &bucket, + }) + cancel() + if err != nil { + return nil + } + + ctx, cancel = context.WithTimeout(context.Background(), shortTimeout) + out, err := s3client.GetBucketTagging(ctx, &s3.GetBucketTaggingInput{ + Bucket: &bucket, + }) + cancel() + if err != nil { + return nil + } + + if len(out.TagSet) > 0 { + return fmt.Errorf("expected empty tag set, instead got %v", out.TagSet) + } + + return nil + }) +} + func PutObject_non_existing_bucket(s *S3Conf) error { testName := "PutObject_non_existing_bucket" return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error { diff --git a/s3api/controllers/base.go b/s3api/controllers/base.go index d9ab202..e988118 100644 --- a/s3api/controllers/base.go +++ b/s3api/controllers/base.go @@ -764,7 +764,7 @@ func (c S3ApiController) DeleteBucket(ctx *fiber.Ctx) error { } err := c.be.DeleteBucketTagging(ctx.Context(), bucket) - return SendResponse(ctx, err, &MetaOpts{Logger: c.logger, Action: "DeleteBucketTagging", BucketOwner: parsedAcl.Owner}) + return SendResponse(ctx, err, &MetaOpts{Logger: c.logger, Action: "DeleteBucketTagging", BucketOwner: parsedAcl.Owner, Status: http.StatusNoContent}) } if err := auth.VerifyACL(parsedAcl, acct.Access, "WRITE", isRoot); err != nil { @@ -774,7 +774,7 @@ func (c S3ApiController) DeleteBucket(ctx *fiber.Ctx) error { err := c.be.DeleteBucket(ctx.Context(), &s3.DeleteBucketInput{ Bucket: &bucket, }) - return SendResponse(ctx, err, &MetaOpts{Logger: c.logger, Action: "DeleteBucket", BucketOwner: parsedAcl.Owner, Status: 204}) + return SendResponse(ctx, err, &MetaOpts{Logger: c.logger, Action: "DeleteBucket", BucketOwner: parsedAcl.Owner, Status: http.StatusNoContent}) } func (c S3ApiController) DeleteObjects(ctx *fiber.Ctx) error { diff --git a/s3api/controllers/base_test.go b/s3api/controllers/base_test.go index 8facf76..e694afd 100644 --- a/s3api/controllers/base_test.go +++ b/s3api/controllers/base_test.go @@ -343,6 +343,9 @@ func TestS3ApiController_ListActions(t *testing.T) { ListObjectsFunc: func(context.Context, *s3.ListObjectsInput) (*s3.ListObjectsOutput, error) { return &s3.ListObjectsOutput{}, nil }, + GetBucketTaggingFunc: func(contextMoqParam context.Context, bucket string) (map[string]string, error) { + return map[string]string{}, nil + }, }, } @@ -365,6 +368,9 @@ func TestS3ApiController_ListActions(t *testing.T) { ListObjectsFunc: func(context.Context, *s3.ListObjectsInput) (*s3.ListObjectsOutput, error) { return nil, s3err.GetAPIError(s3err.ErrNotImplemented) }, + GetBucketTaggingFunc: func(contextMoqParam context.Context, bucket string) (map[string]string, error) { + return nil, s3err.GetAPIError(s3err.ErrNoSuchBucket) + }, }, } appError := fiber.New() @@ -384,6 +390,24 @@ func TestS3ApiController_ListActions(t *testing.T) { wantErr bool statusCode int }{ + { + name: "Get-bucket-tagging-non-existing-bucket", + app: appError, + args: args{ + req: httptest.NewRequest(http.MethodGet, "/my-bucket?tagging", nil), + }, + wantErr: false, + statusCode: 404, + }, + { + name: "Get-bucket-tagging-success", + app: app, + args: args{ + req: httptest.NewRequest(http.MethodGet, "/my-bucket?tagging", nil), + }, + wantErr: false, + statusCode: 200, + }, { name: "Get-bucket-acl-success", app: app, @@ -492,6 +516,17 @@ func TestS3ApiController_PutBucketActions(t *testing.T) { ` + tagBody := ` + + + + organization + marketing + + + + ` + s3ApiController := S3ApiController{ be: &BackendMock{ GetBucketAclFunc: func(context.Context, *s3.GetBucketAclInput) ([]byte, error) { @@ -503,6 +538,9 @@ func TestS3ApiController_PutBucketActions(t *testing.T) { CreateBucketFunc: func(context.Context, *s3.CreateBucketInput, []byte) error { return nil }, + PutBucketTaggingFunc: func(contextMoqParam context.Context, bucket string, tags map[string]string) error { + return nil + }, }, } // Mock ctx.Locals @@ -543,6 +581,24 @@ func TestS3ApiController_PutBucketActions(t *testing.T) { wantErr bool statusCode int }{ + { + name: "Put-bucket-tagging-invalid-body", + app: app, + args: args{ + req: httptest.NewRequest(http.MethodPut, "/my-bucket?tagging", nil), + }, + wantErr: false, + statusCode: 400, + }, + { + name: "Put-bucket-tagging-success", + app: app, + args: args{ + req: httptest.NewRequest(http.MethodPut, "/my-bucket?tagging", strings.NewReader(tagBody)), + }, + wantErr: false, + statusCode: 200, + }, { name: "Put-bucket-acl-invalid-acl", app: app, @@ -869,12 +925,12 @@ func TestS3ApiController_DeleteBucket(t *testing.T) { app := fiber.New() s3ApiController := S3ApiController{ be: &BackendMock{ - GetBucketAclFunc: func(context.Context, *s3.GetBucketAclInput) ([]byte, error) { - return acldata, nil - }, DeleteBucketFunc: func(context.Context, *s3.DeleteBucketInput) error { return nil }, + DeleteBucketTaggingFunc: func(contextMoqParam context.Context, bucket string) error { + return nil + }, }, } @@ -904,6 +960,15 @@ func TestS3ApiController_DeleteBucket(t *testing.T) { wantErr: false, statusCode: 204, }, + { + name: "Delete-bucket-tagging-success", + app: app, + args: args{ + req: httptest.NewRequest(http.MethodDelete, "/my-bucket?tagging", nil), + }, + wantErr: false, + statusCode: 204, + }, } for _, tt := range tests { resp, err := tt.app.Test(tt.args.req)