Add basics for S3 bucket lifecycles.
This commit is contained in:
parent
72dbd349ac
commit
91fffbb83b
@ -161,6 +161,20 @@ class FakeMultipart(object):
|
||||
yield self.parts[part_id]
|
||||
|
||||
|
||||
class LifecycleRule(object):
|
||||
def __init__(self, id=None, prefix=None, status=None, expiration_days=None,
|
||||
expiration_date=None, transition_days=None,
|
||||
transition_date=None, storage_class=None):
|
||||
self.id = id
|
||||
self.prefix = prefix
|
||||
self.status = status
|
||||
self.expiration_days = expiration_days
|
||||
self.expiration_date = expiration_date
|
||||
self.transition_days = transition_days
|
||||
self.transition_date = transition_date
|
||||
self.storage_class = storage_class
|
||||
|
||||
|
||||
class FakeBucket(object):
|
||||
|
||||
def __init__(self, name, region_name):
|
||||
@ -169,6 +183,7 @@ class FakeBucket(object):
|
||||
self.keys = _VersionedKeyStore()
|
||||
self.multiparts = {}
|
||||
self.versioning_status = None
|
||||
self.rules = []
|
||||
|
||||
@property
|
||||
def location(self):
|
||||
@ -178,6 +193,25 @@ class FakeBucket(object):
|
||||
def is_versioned(self):
|
||||
return self.versioning_status == 'Enabled'
|
||||
|
||||
def set_lifecycle(self, rules):
|
||||
self.rules = []
|
||||
for rule in rules:
|
||||
expiration = rule.get('Expiration')
|
||||
transition = rule.get('Transition')
|
||||
self.rules.append(LifecycleRule(
|
||||
id=rule.get('ID'),
|
||||
prefix=rule['Prefix'],
|
||||
status=rule['Status'],
|
||||
expiration_days=expiration.get('Days') if expiration else None,
|
||||
expiration_date=expiration.get('Date') if expiration else None,
|
||||
transition_days=transition.get('Days') if transition else None,
|
||||
transition_date=transition.get('Date') if transition else None,
|
||||
storage_class=transition['StorageClass'] if transition else None,
|
||||
))
|
||||
|
||||
def delete_lifecycle(self):
|
||||
self.rules = []
|
||||
|
||||
def get_cfn_attribute(self, attribute_name):
|
||||
from moto.cloudformation.exceptions import UnformattedGetAttTemplateException
|
||||
if attribute_name == 'DomainName':
|
||||
@ -235,6 +269,10 @@ class S3Backend(BaseBackend):
|
||||
|
||||
return itertools.chain(*(l for _, l in bucket.keys.iterlists()))
|
||||
|
||||
def set_bucket_lifecycle(self, bucket_name, rules):
|
||||
bucket = self.get_bucket(bucket_name)
|
||||
bucket.set_lifecycle(rules)
|
||||
|
||||
def set_key(self, bucket_name, key_name, value, storage=None, etag=None):
|
||||
key_name = clean_key_name(key_name)
|
||||
|
||||
|
@ -4,6 +4,7 @@ import re
|
||||
|
||||
import six
|
||||
from six.moves.urllib.parse import parse_qs, urlparse
|
||||
import xmltodict
|
||||
|
||||
from moto.core.responses import _TemplateEnvironmentMixin
|
||||
|
||||
@ -65,7 +66,7 @@ class ResponseObject(_TemplateEnvironmentMixin):
|
||||
elif method == 'PUT':
|
||||
return self._bucket_response_put(request, region_name, bucket_name, querystring, headers)
|
||||
elif method == 'DELETE':
|
||||
return self._bucket_response_delete(bucket_name, headers)
|
||||
return self._bucket_response_delete(bucket_name, querystring, headers)
|
||||
elif method == 'POST':
|
||||
return self._bucket_response_post(request, bucket_name, headers)
|
||||
else:
|
||||
@ -92,6 +93,12 @@ class ResponseObject(_TemplateEnvironmentMixin):
|
||||
bucket = self.backend.get_bucket(bucket_name)
|
||||
template = self.response_template(S3_BUCKET_LOCATION)
|
||||
return 200, headers, template.render(location=bucket.location)
|
||||
elif 'lifecycle' in querystring:
|
||||
bucket = self.backend.get_bucket(bucket_name)
|
||||
if not bucket.rules:
|
||||
return 404, headers, "NoSuchLifecycleConfiguration"
|
||||
template = self.response_template(S3_BUCKET_LIFECYCLE_CONFIGURATION)
|
||||
return 200, headers, template.render(rules=bucket.rules)
|
||||
elif 'versioning' in querystring:
|
||||
versioning = self.backend.get_bucket_versioning(bucket_name)
|
||||
template = self.response_template(S3_BUCKET_GET_VERSIONING)
|
||||
@ -143,14 +150,23 @@ class ResponseObject(_TemplateEnvironmentMixin):
|
||||
else:
|
||||
# Flask server
|
||||
body = request.data
|
||||
body = body.decode('utf-8')
|
||||
|
||||
if 'versioning' in querystring:
|
||||
ver = re.search('<Status>([A-Za-z]+)</Status>', body.decode('utf-8'))
|
||||
ver = re.search('<Status>([A-Za-z]+)</Status>', body)
|
||||
if ver:
|
||||
self.backend.set_bucket_versioning(bucket_name, ver.group(1))
|
||||
template = self.response_template(S3_BUCKET_VERSIONING)
|
||||
return template.render(bucket_versioning_status=ver.group(1))
|
||||
else:
|
||||
return 404, headers, ""
|
||||
elif 'lifecycle' in querystring:
|
||||
rules = xmltodict.parse(body)['LifecycleConfiguration']['Rule']
|
||||
if not isinstance(rules, list):
|
||||
# If there is only one rule, xmldict returns just the item
|
||||
rules = [rules]
|
||||
self.backend.set_bucket_lifecycle(bucket_name, rules)
|
||||
return ""
|
||||
else:
|
||||
try:
|
||||
new_bucket = self.backend.create_bucket(bucket_name, region_name)
|
||||
@ -163,7 +179,12 @@ class ResponseObject(_TemplateEnvironmentMixin):
|
||||
template = self.response_template(S3_BUCKET_CREATE_RESPONSE)
|
||||
return 200, headers, template.render(bucket=new_bucket)
|
||||
|
||||
def _bucket_response_delete(self, bucket_name, headers):
|
||||
def _bucket_response_delete(self, bucket_name, querystring, headers):
|
||||
if 'lifecycle' in querystring:
|
||||
bucket = self.backend.get_bucket(bucket_name)
|
||||
bucket.delete_lifecycle()
|
||||
return 204, headers, ""
|
||||
|
||||
removed_bucket = self.backend.delete_bucket(bucket_name)
|
||||
|
||||
if removed_bucket:
|
||||
@ -497,6 +518,39 @@ S3_DELETE_BUCKET_WITH_ITEMS_ERROR = """<?xml version="1.0" encoding="UTF-8"?>
|
||||
S3_BUCKET_LOCATION = """<?xml version="1.0" encoding="UTF-8"?>
|
||||
<LocationConstraint xmlns="http://s3.amazonaws.com/doc/2006-03-01/">{{ location }}</LocationConstraint>"""
|
||||
|
||||
S3_BUCKET_LIFECYCLE_CONFIGURATION = """<?xml version="1.0" encoding="UTF-8"?>
|
||||
<LifecycleConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
|
||||
{% for rule in rules %}
|
||||
<Rule>
|
||||
<ID>{{ rule.id }}</ID>
|
||||
<Prefix>{{ rule.prefix if rule.prefix != None }}</Prefix>
|
||||
<Status>{{ rule.status }}</Status>
|
||||
{% if rule.storage_class %}
|
||||
<Transition>
|
||||
{% if rule.transition_days %}
|
||||
<Days>{{ rule.transition_days }}</Days>
|
||||
{% endif %}
|
||||
{% if rule.transition_date %}
|
||||
<Date>{{ rule.transition_date }}</Date>
|
||||
{% endif %}
|
||||
<StorageClass>{{ rule.storage_class }}</StorageClass>
|
||||
</Transition>
|
||||
{% endif %}
|
||||
{% if rule.expiration_days or rule.expiration_date %}
|
||||
<Expiration>
|
||||
{% if rule.expiration_days %}
|
||||
<Days>{{ rule.expiration_days }}</Days>
|
||||
{% endif %}
|
||||
{% if rule.expiration_date %}
|
||||
<Date>{{ rule.expiration_date }}</Date>
|
||||
{% endif %}
|
||||
</Expiration>
|
||||
{% endif %}
|
||||
</Rule>
|
||||
{% endfor %}
|
||||
</LifecycleConfiguration>
|
||||
"""
|
||||
|
||||
S3_BUCKET_VERSIONING = """
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<VersioningConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
|
||||
|
Loading…
Reference in New Issue
Block a user