first working version of s3 refactor.
This commit is contained in:
		
							parent
							
								
									705ec314a3
								
							
						
					
					
						commit
						1df454a632
					
				| @ -8,6 +8,9 @@ import xmltodict | |||||||
| 
 | 
 | ||||||
| from moto.core.responses import _TemplateEnvironmentMixin | from moto.core.responses import _TemplateEnvironmentMixin | ||||||
| 
 | 
 | ||||||
|  | from moto.s3bucket_path.utils import bucket_name_from_url as bucketpath_bucket_name_from_url, parse_key_name as bucketpath_parse_key_name, is_delete_keys as bucketpath_is_delete_keys | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| from .exceptions import BucketAlreadyExists, S3ClientError, InvalidPartOrder | from .exceptions import BucketAlreadyExists, S3ClientError, InvalidPartOrder | ||||||
| from .models import s3_backend, get_canned_acl, FakeGrantee, FakeGrant, FakeAcl | from .models import s3_backend, get_canned_acl, FakeGrantee, FakeGrant, FakeAcl | ||||||
| from .utils import bucket_name_from_url, metadata_from_headers | from .utils import bucket_name_from_url, metadata_from_headers | ||||||
| @ -21,19 +24,25 @@ def parse_key_name(pth): | |||||||
|     return pth.lstrip("/") |     return pth.lstrip("/") | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | def is_delete_keys(path, bucket_name): | ||||||
|  |     return path == u'/?delete' | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| class ResponseObject(_TemplateEnvironmentMixin): | class ResponseObject(_TemplateEnvironmentMixin): | ||||||
|     def __init__(self, backend, bucket_name_from_url, parse_key_name, |     def __init__(self, backend, parse_key_name, bucket_name_from_url, | ||||||
|                  is_delete_keys=None): |                  is_delete_keys=None): | ||||||
|         super(ResponseObject, self).__init__() |         super(ResponseObject, self).__init__() | ||||||
|         self.backend = backend |         self.backend = backend | ||||||
|         self.bucket_name_from_url = bucket_name_from_url |         # self.bucket_name_from_url = bucket_name_from_url | ||||||
|         self.parse_key_name = parse_key_name |         # self.parse_key_name = parse_key_name | ||||||
|         if is_delete_keys: |         # if is_delete_keys: | ||||||
|             self.is_delete_keys = is_delete_keys |         #     self.is_delete_keys = is_delete_keys | ||||||
| 
 | 
 | ||||||
|     @staticmethod |     def is_delete_keys(self, request, path, bucket_name): | ||||||
|     def is_delete_keys(path, bucket_name): |         if self.is_path_based_buckets(request): | ||||||
|         return path == u'/?delete' |             return bucketpath_is_delete_keys(path, bucket_name) | ||||||
|  |         else: | ||||||
|  |             return is_delete_keys(path, bucket_name) | ||||||
| 
 | 
 | ||||||
|     def all_buckets(self): |     def all_buckets(self): | ||||||
|         # No bucket specified. Listing all buckets |         # No bucket specified. Listing all buckets | ||||||
| @ -41,6 +50,30 @@ class ResponseObject(_TemplateEnvironmentMixin): | |||||||
|         template = self.response_template(S3_ALL_BUCKETS) |         template = self.response_template(S3_ALL_BUCKETS) | ||||||
|         return template.render(buckets=all_buckets) |         return template.render(buckets=all_buckets) | ||||||
| 
 | 
 | ||||||
|  |     def is_path_based_buckets(self, request): | ||||||
|  |         return request.headers['host'] == 's3.amazonaws.com' | ||||||
|  | 
 | ||||||
|  |     def parse_bucket_name_from_url(self, request, url): | ||||||
|  |         if self.is_path_based_buckets(request): | ||||||
|  |             return bucketpath_bucket_name_from_url(url) | ||||||
|  |         else: | ||||||
|  |             return bucket_name_from_url(url) | ||||||
|  | 
 | ||||||
|  |     def parse_key_name(self, request, url): | ||||||
|  |         if self.is_path_based_buckets(request): | ||||||
|  |             return bucketpath_parse_key_name(url) | ||||||
|  |         else: | ||||||
|  |             return parse_key_name(url) | ||||||
|  | 
 | ||||||
|  |     def response(self, request, full_url, headers): | ||||||
|  |         # Depending on which calling format the client is using, we don't know | ||||||
|  |         # if this is a bucket or key request so we have to check | ||||||
|  |         if self.is_path_based_buckets(request): | ||||||
|  |             # Using path-based buckets | ||||||
|  |             return self.bucket_response(request, full_url, headers) | ||||||
|  |         else: | ||||||
|  |             return self.key_response(request, full_url, headers) | ||||||
|  | 
 | ||||||
|     def bucket_response(self, request, full_url, headers): |     def bucket_response(self, request, full_url, headers): | ||||||
|         try: |         try: | ||||||
|             response = self._bucket_response(request, full_url, headers) |             response = self._bucket_response(request, full_url, headers) | ||||||
| @ -62,7 +95,7 @@ class ResponseObject(_TemplateEnvironmentMixin): | |||||||
|         if region_match: |         if region_match: | ||||||
|             region_name = region_match.groups()[0] |             region_name = region_match.groups()[0] | ||||||
| 
 | 
 | ||||||
|         bucket_name = self.bucket_name_from_url(full_url) |         bucket_name = self.parse_bucket_name_from_url(request, full_url) | ||||||
|         if not bucket_name: |         if not bucket_name: | ||||||
|             # If no bucket specified, list all buckets |             # If no bucket specified, list all buckets | ||||||
|             return self.all_buckets() |             return self.all_buckets() | ||||||
| @ -232,7 +265,7 @@ class ResponseObject(_TemplateEnvironmentMixin): | |||||||
|             return 409, headers, template.render(bucket=removed_bucket) |             return 409, headers, template.render(bucket=removed_bucket) | ||||||
| 
 | 
 | ||||||
|     def _bucket_response_post(self, request, bucket_name, headers): |     def _bucket_response_post(self, request, bucket_name, headers): | ||||||
|         if self.is_delete_keys(request.path, bucket_name): |         if self.is_delete_keys(request, request.path, bucket_name): | ||||||
|             return self._bucket_response_delete_keys(request, bucket_name, headers) |             return self._bucket_response_delete_keys(request, bucket_name, headers) | ||||||
| 
 | 
 | ||||||
|         # POST to bucket-url should create file from form |         # POST to bucket-url should create file from form | ||||||
| @ -320,8 +353,8 @@ class ResponseObject(_TemplateEnvironmentMixin): | |||||||
|         query = parse_qs(parsed_url.query, keep_blank_values=True) |         query = parse_qs(parsed_url.query, keep_blank_values=True) | ||||||
|         method = request.method |         method = request.method | ||||||
| 
 | 
 | ||||||
|         key_name = self.parse_key_name(parsed_url.path) |         key_name = self.parse_key_name(request, parsed_url.path) | ||||||
|         bucket_name = self.bucket_name_from_url(full_url) |         bucket_name = self.parse_bucket_name_from_url(request, full_url) | ||||||
| 
 | 
 | ||||||
|         if hasattr(request, 'body'): |         if hasattr(request, 'body'): | ||||||
|             # Boto |             # Boto | ||||||
|  | |||||||
| @ -2,10 +2,23 @@ from __future__ import unicode_literals | |||||||
| from .responses import S3ResponseInstance | from .responses import S3ResponseInstance | ||||||
| 
 | 
 | ||||||
| url_bases = [ | url_bases = [ | ||||||
|  |     "https?://s3(.*).amazonaws.com", | ||||||
|     "https?://(?P<bucket_name>[a-zA-Z0-9\-_.]*)\.?s3(.*).amazonaws.com" |     "https?://(?P<bucket_name>[a-zA-Z0-9\-_.]*)\.?s3(.*).amazonaws.com" | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| url_paths = { | url_paths = { | ||||||
|  |     # subdomain bucket | ||||||
|     '{0}/$': S3ResponseInstance.bucket_response, |     '{0}/$': S3ResponseInstance.bucket_response, | ||||||
|     '{0}/(?P<key_name>.+)': S3ResponseInstance.key_response, | 
 | ||||||
|  |     # subdomain key of path-based bucket | ||||||
|  |     '{0}/(?P<key_name>.+)': S3ResponseInstance.response, | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     # path-based bucket + key | ||||||
|  |     '{0}/(?P<bucket_name_path>[a-zA-Z0-9\-_./]+)/(?P<key_name>.+)': S3ResponseInstance.key_response, | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     # '{0}/(?P<bucket_name>[a-zA-Z0-9\-_.]+)$': ro.bucket_response, | ||||||
|  |     # '{0}/(?P<bucket_name>[a-zA-Z0-9\-_.]+)/$': bucket_response2, | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,3 +1,6 @@ | |||||||
| from __future__ import unicode_literals | from __future__ import unicode_literals | ||||||
| from .models import s3bucket_path_backend | # from .models import s3bucket_path_backend | ||||||
| mock_s3bucket_path = s3bucket_path_backend.decorator | from moto import mock_s3 | ||||||
|  | 
 | ||||||
|  | # mock_s3bucket_path = s3bucket_path_backend.decorator | ||||||
|  | mock_s3bucket_path = mock_s3 | ||||||
|  | |||||||
| @ -1,19 +1,11 @@ | |||||||
| from __future__ import unicode_literals | from __future__ import unicode_literals | ||||||
| from .models import s3bucket_path_backend | from .models import s3bucket_path_backend | ||||||
| 
 | 
 | ||||||
| from .utils import bucket_name_from_url | from .utils import bucket_name_from_url, parse_key_name, is_delete_keys | ||||||
| 
 | 
 | ||||||
| from moto.s3.responses import ResponseObject | from moto.s3.responses import ResponseObject | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def parse_key_name(pth): |  | ||||||
|     return "/".join(pth.rstrip("/").split("/")[2:]) |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| def is_delete_keys(path, bucket_name): |  | ||||||
|     return path == u'/' + bucket_name + u'/?delete' |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| S3BucketPathResponseInstance = ResponseObject( | S3BucketPathResponseInstance = ResponseObject( | ||||||
|     s3bucket_path_backend, |     s3bucket_path_backend, | ||||||
|     bucket_name_from_url, |     bucket_name_from_url, | ||||||
|  | |||||||
| @ -9,3 +9,11 @@ def bucket_name_from_url(url): | |||||||
|     if len(l) == 0 or l[0] == "": |     if len(l) == 0 or l[0] == "": | ||||||
|         return None |         return None | ||||||
|     return l[0] |     return l[0] | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def parse_key_name(path): | ||||||
|  |     return "/".join(path.rstrip("/").split("/")[2:]) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def is_delete_keys(path, bucket_name): | ||||||
|  |     return path == u'/' + bucket_name + u'/?delete' | ||||||
|  | |||||||
| @ -7,12 +7,14 @@ import moto.server as server | |||||||
| Test the different server responses | Test the different server responses | ||||||
| ''' | ''' | ||||||
| 
 | 
 | ||||||
|  | HEADERS = {'host': 's3.amazonaws.com'} | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| def test_s3_server_get(): | def test_s3_server_get(): | ||||||
|     backend = server.create_backend_app("s3bucket_path") |     backend = server.create_backend_app("s3bucket_path") | ||||||
|     test_client = backend.test_client() |     test_client = backend.test_client() | ||||||
| 
 | 
 | ||||||
|     res = test_client.get('/') |     res = test_client.get('/', headers=HEADERS) | ||||||
| 
 | 
 | ||||||
|     res.data.should.contain(b'ListAllMyBucketsResult') |     res.data.should.contain(b'ListAllMyBucketsResult') | ||||||
| 
 | 
 | ||||||
| @ -21,23 +23,23 @@ def test_s3_server_bucket_create(): | |||||||
|     backend = server.create_backend_app("s3bucket_path") |     backend = server.create_backend_app("s3bucket_path") | ||||||
|     test_client = backend.test_client() |     test_client = backend.test_client() | ||||||
| 
 | 
 | ||||||
|     res = test_client.put('/foobar/', 'http://localhost:5000') |     res = test_client.put('/foobar/', 'http://localhost:5000', headers=HEADERS) | ||||||
|     res.status_code.should.equal(200) |     res.status_code.should.equal(200) | ||||||
| 
 | 
 | ||||||
|     res = test_client.get('/') |     res = test_client.get('/', headers=HEADERS) | ||||||
|     res.data.should.contain(b'<Name>foobar</Name>') |     res.data.should.contain(b'<Name>foobar</Name>') | ||||||
| 
 | 
 | ||||||
|     res = test_client.get('/foobar/', 'http://localhost:5000') |     res = test_client.get('/foobar/', 'http://localhost:5000', headers=HEADERS) | ||||||
|     res.status_code.should.equal(200) |     res.status_code.should.equal(200) | ||||||
|     res.data.should.contain(b"ListBucketResult") |     res.data.should.contain(b"ListBucketResult") | ||||||
| 
 | 
 | ||||||
|     res = test_client.get('/missing-bucket/', 'http://localhost:5000') |     res = test_client.get('/missing-bucket/', 'http://localhost:5000', headers=HEADERS) | ||||||
|     res.status_code.should.equal(404) |     res.status_code.should.equal(404) | ||||||
| 
 | 
 | ||||||
|     res = test_client.put('/foobar/bar/', 'http://localhost:5000', data='test value') |     res = test_client.put('/foobar/bar/', 'http://localhost:5000', data='test value', headers=HEADERS) | ||||||
|     res.status_code.should.equal(200) |     res.status_code.should.equal(200) | ||||||
| 
 | 
 | ||||||
|     res = test_client.get('/foobar/bar/', 'http://localhost:5000') |     res = test_client.get('/foobar/bar/', 'http://localhost:5000', headers=HEADERS) | ||||||
|     res.status_code.should.equal(200) |     res.status_code.should.equal(200) | ||||||
|     res.data.should.equal(b"test value") |     res.data.should.equal(b"test value") | ||||||
| 
 | 
 | ||||||
| @ -46,14 +48,14 @@ def test_s3_server_post_to_bucket(): | |||||||
|     backend = server.create_backend_app("s3bucket_path") |     backend = server.create_backend_app("s3bucket_path") | ||||||
|     test_client = backend.test_client() |     test_client = backend.test_client() | ||||||
| 
 | 
 | ||||||
|     res = test_client.put('/foobar2/', 'http://localhost:5000/') |     res = test_client.put('/foobar2/', 'http://localhost:5000/', headers=HEADERS) | ||||||
|     res.status_code.should.equal(200) |     res.status_code.should.equal(200) | ||||||
| 
 | 
 | ||||||
|     test_client.post('/foobar2/', "https://localhost:5000/", data={ |     test_client.post('/foobar2/', "https://localhost:5000/", data={ | ||||||
|         'key': 'the-key', |         'key': 'the-key', | ||||||
|         'file': 'nothing' |         'file': 'nothing' | ||||||
|     }) |     }, headers=HEADERS) | ||||||
| 
 | 
 | ||||||
|     res = test_client.get('/foobar2/the-key/', 'http://localhost:5000/') |     res = test_client.get('/foobar2/the-key/', 'http://localhost:5000/', headers=HEADERS) | ||||||
|     res.status_code.should.equal(200) |     res.status_code.should.equal(200) | ||||||
|     res.data.should.equal(b"nothing") |     res.data.should.equal(b"nothing") | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user