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