From 6afefe1ad34cc5eb6625308296b67e952e07329f Mon Sep 17 00:00:00 2001 From: manuvakery1 Date: Thu, 11 Jun 2020 20:25:08 +0530 Subject: [PATCH] s3api: Add basic support for ?tagging requests https://docs.aws.amazon.com/cli/latest/userguide/cliv2-migration.html#cliv2-migration-s3-copy-metadata AWS CLI version 2 improves Amazon S3 handling of file properties and tags when performing multipart copies. We still don't supprt object tagging hence the aws s3 cp command fails for mulitpart copies with default options. This way get tagging request will receive an empty tagset in response and mulitpart copies will work fine Change-Id: I1f031b05025cafac00e86966c240aa5f7258d0bf --- .../middleware/s3api/controllers/__init__.py | 3 + .../middleware/s3api/controllers/tagging.py | 57 +++++++++++++++++++ swift/common/middleware/s3api/s3request.py | 7 ++- .../common/middleware/s3api/test_s3api.py | 19 ++++++- 4 files changed, 83 insertions(+), 3 deletions(-) create mode 100644 swift/common/middleware/s3api/controllers/tagging.py diff --git a/swift/common/middleware/s3api/controllers/__init__.py b/swift/common/middleware/s3api/controllers/__init__.py index 831e2c7d37..ba335b4c67 100644 --- a/swift/common/middleware/s3api/controllers/__init__.py +++ b/swift/common/middleware/s3api/controllers/__init__.py @@ -31,6 +31,8 @@ from swift.common.middleware.s3api.controllers.logging import \ LoggingStatusController from swift.common.middleware.s3api.controllers.versioning import \ VersioningController +from swift.common.middleware.s3api.controllers.tagging import \ + TaggingController __all__ = [ 'Controller', @@ -47,6 +49,7 @@ __all__ = [ 'LocationController', 'LoggingStatusController', 'VersioningController', + 'TaggingController', 'UnsupportedController', ] diff --git a/swift/common/middleware/s3api/controllers/tagging.py b/swift/common/middleware/s3api/controllers/tagging.py new file mode 100644 index 0000000000..ca7bc853d2 --- /dev/null +++ b/swift/common/middleware/s3api/controllers/tagging.py @@ -0,0 +1,57 @@ +# Copyright (c) 2014 OpenStack Foundation. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from swift.common.utils import public + +from swift.common.middleware.s3api.controllers.base import Controller, \ + S3NotImplemented +from swift.common.middleware.s3api.s3response import HTTPOk +from swift.common.middleware.s3api.etree import Element, tostring, \ + SubElement + + +class TaggingController(Controller): + """ + Handles the following APIs: + + * GET Bucket and Object tagging + * PUT Bucket and Object tagging + * DELETE Bucket and Object tagging + + """ + @public + def GET(self, req): + """ + Handles GET Bucket and Object tagging. + """ + elem = Element('Tagging') + SubElement(elem, 'TagSet') + body = tostring(elem) + + return HTTPOk(body=body, content_type=None) + + @public + def PUT(self, req): + """ + Handles PUT Bucket and Object tagging. + """ + raise S3NotImplemented('The requested resource is not implemented') + + @public + def DELETE(self, req): + """ + Handles DELETE Bucket and Object tagging. + """ + raise S3NotImplemented('The requested resource is not implemented') diff --git a/swift/common/middleware/s3api/s3request.py b/swift/common/middleware/s3api/s3request.py index 28e734f0b4..7e80547dc7 100644 --- a/swift/common/middleware/s3api/s3request.py +++ b/swift/common/middleware/s3api/s3request.py @@ -45,7 +45,8 @@ from swift.common.middleware.s3api.controllers import ServiceController, \ ObjectController, AclController, MultiObjectDeleteController, \ LocationController, LoggingStatusController, PartController, \ UploadController, UploadsController, VersioningController, \ - UnsupportedController, S3AclController, BucketController + UnsupportedController, S3AclController, BucketController, \ + TaggingController from swift.common.middleware.s3api.s3response import AccessDenied, \ InvalidArgument, InvalidDigest, BucketAlreadyOwnedByYou, \ RequestTimeTooSkewed, S3Response, SignatureDoesNotMatch, \ @@ -1026,9 +1027,11 @@ class S3Request(swob.Request): return UploadsController if 'versioning' in self.params: return VersioningController + if 'tagging' in self.params: + return TaggingController unsupported = ('notification', 'policy', 'requestPayment', 'torrent', - 'website', 'cors', 'tagging', 'restore') + 'website', 'cors', 'restore') if set(unsupported) & set(self.params): return UnsupportedController diff --git a/test/unit/common/middleware/s3api/test_s3api.py b/test/unit/common/middleware/s3api/test_s3api.py index e7b447613f..2df22a8ac1 100644 --- a/test/unit/common/middleware/s3api/test_s3api.py +++ b/test/unit/common/middleware/s3api/test_s3api.py @@ -705,7 +705,24 @@ class TestS3ApiMiddleware(S3ApiTestCase): self._test_unsupported_resource('cors') def test_tagging(self): - self._test_unsupported_resource('tagging') + req = Request.blank('/bucket?tagging', + environ={'REQUEST_METHOD': 'GET'}, + headers={'Authorization': 'AWS test:tester:hmac', + 'Date': self.get_date_header()}) + status, headers, body = self.call_s3api(req) + self.assertEqual(status.split()[0], '200') + req = Request.blank('/bucket?tagging', + environ={'REQUEST_METHOD': 'PUT'}, + headers={'Authorization': 'AWS test:tester:hmac', + 'Date': self.get_date_header()}) + status, headers, body = self.call_s3api(req) + self.assertEqual(self._get_error_code(body), 'NotImplemented') + req = Request.blank('/bucket?tagging', + environ={'REQUEST_METHOD': 'DELETE'}, + headers={'Authorization': 'AWS test:tester:hmac', + 'Date': self.get_date_header()}) + status, headers, body = self.call_s3api(req) + self.assertEqual(self._get_error_code(body), 'NotImplemented') def test_restore(self): self._test_unsupported_resource('restore')