add support for NoncurrentVersionTransition, NoncurrentVersionExpiration, and AbortIncompleteMultipartUpload actions to s3 lifecycle rules
This commit is contained in:
parent
fe9b312fd2
commit
ea4fcaa82a
@ -341,8 +341,9 @@ class LifecycleAndFilter(BaseModel):
|
|||||||
class LifecycleRule(BaseModel):
|
class LifecycleRule(BaseModel):
|
||||||
|
|
||||||
def __init__(self, id=None, prefix=None, lc_filter=None, status=None, expiration_days=None,
|
def __init__(self, id=None, prefix=None, lc_filter=None, status=None, expiration_days=None,
|
||||||
expiration_date=None, transition_days=None, expired_object_delete_marker=None,
|
expiration_date=None, transition_days=None, transition_date=None, storage_class=None,
|
||||||
transition_date=None, storage_class=None):
|
expired_object_delete_marker=None, nve_noncurrent_days=None, nvt_noncurrent_days=None,
|
||||||
|
nvt_storage_class=None, aimu_days=None):
|
||||||
self.id = id
|
self.id = id
|
||||||
self.prefix = prefix
|
self.prefix = prefix
|
||||||
self.filter = lc_filter
|
self.filter = lc_filter
|
||||||
@ -351,8 +352,12 @@ class LifecycleRule(BaseModel):
|
|||||||
self.expiration_date = expiration_date
|
self.expiration_date = expiration_date
|
||||||
self.transition_days = transition_days
|
self.transition_days = transition_days
|
||||||
self.transition_date = transition_date
|
self.transition_date = transition_date
|
||||||
self.expired_object_delete_marker = expired_object_delete_marker
|
|
||||||
self.storage_class = storage_class
|
self.storage_class = storage_class
|
||||||
|
self.expired_object_delete_marker = expired_object_delete_marker
|
||||||
|
self.nve_noncurrent_days = nve_noncurrent_days
|
||||||
|
self.nvt_noncurrent_days = nvt_noncurrent_days
|
||||||
|
self.nvt_storage_class = nvt_storage_class
|
||||||
|
self.aimu_days = aimu_days
|
||||||
|
|
||||||
|
|
||||||
class CorsRule(BaseModel):
|
class CorsRule(BaseModel):
|
||||||
@ -414,8 +419,12 @@ class FakeBucket(BaseModel):
|
|||||||
def set_lifecycle(self, rules):
|
def set_lifecycle(self, rules):
|
||||||
self.rules = []
|
self.rules = []
|
||||||
for rule in rules:
|
for rule in rules:
|
||||||
|
# Extract actions from Lifecycle rule
|
||||||
expiration = rule.get('Expiration')
|
expiration = rule.get('Expiration')
|
||||||
transition = rule.get('Transition')
|
transition = rule.get('Transition')
|
||||||
|
nve = rule.get('NoncurrentVersionExpiration')
|
||||||
|
nvt = rule.get('NoncurrentVersionTransition')
|
||||||
|
aimu = rule.get('AbortIncompleteMultipartUpload')
|
||||||
|
|
||||||
eodm = None
|
eodm = None
|
||||||
if expiration and expiration.get("ExpiredObjectDeleteMarker") is not None:
|
if expiration and expiration.get("ExpiredObjectDeleteMarker") is not None:
|
||||||
@ -459,11 +468,14 @@ class FakeBucket(BaseModel):
|
|||||||
status=rule['Status'],
|
status=rule['Status'],
|
||||||
expiration_days=expiration.get('Days') if expiration else None,
|
expiration_days=expiration.get('Days') if expiration else None,
|
||||||
expiration_date=expiration.get('Date') if expiration else None,
|
expiration_date=expiration.get('Date') if expiration else None,
|
||||||
expired_object_delete_marker=eodm,
|
|
||||||
transition_days=transition.get('Days') if transition else None,
|
transition_days=transition.get('Days') if transition else None,
|
||||||
transition_date=transition.get('Date') if transition else None,
|
transition_date=transition.get('Date') if transition else None,
|
||||||
storage_class=transition[
|
storage_class=transition.get('StorageClass') if transition else None,
|
||||||
'StorageClass'] if transition else None,
|
expired_object_delete_marker=eodm,
|
||||||
|
nve_noncurrent_days = nve.get('NoncurrentDays') if nve else None,
|
||||||
|
nvt_noncurrent_days = nvt.get('NoncurrentDays') if nvt else None,
|
||||||
|
nvt_storage_class = nvt.get('StorageClass') if nvt else None,
|
||||||
|
aimu_days=aimu.get('DaysAfterInitiation') if aimu else None,
|
||||||
))
|
))
|
||||||
|
|
||||||
def delete_lifecycle(self):
|
def delete_lifecycle(self):
|
||||||
|
@ -1228,6 +1228,22 @@ S3_BUCKET_LIFECYCLE_CONFIGURATION = """<?xml version="1.0" encoding="UTF-8"?>
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</Expiration>
|
</Expiration>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% if rule.nvt_noncurrent_days and rule.nvt_storage_class %}
|
||||||
|
<NoncurrentVersionTransition>
|
||||||
|
<NoncurrentDays>{{ rule.nvt_noncurrent_days }}</NoncurrentDays>
|
||||||
|
<StorageClass>{{ rule.nvt_storage_class }}</StorageClass>
|
||||||
|
</NoncurrentVersionTransition>
|
||||||
|
{% endif %}
|
||||||
|
{% if rule.nve_noncurrent_days %}
|
||||||
|
<NoncurrentVersionExpiration>
|
||||||
|
<NoncurrentDays>{{ rule.nve_noncurrent_days }}</NoncurrentDays>
|
||||||
|
</NoncurrentVersionExpiration>
|
||||||
|
{% endif %}
|
||||||
|
{% if rule.aimu_days %}
|
||||||
|
<AbortIncompleteMultipartUpload>
|
||||||
|
<DaysAfterInitiation>{{ rule.aimu_days }}</DaysAfterInitiation>
|
||||||
|
</AbortIncompleteMultipartUpload>
|
||||||
|
{% endif %}
|
||||||
</Rule>
|
</Rule>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</LifecycleConfiguration>
|
</LifecycleConfiguration>
|
||||||
|
@ -191,6 +191,135 @@ def test_lifecycle_with_eodm():
|
|||||||
assert err.exception.response["Error"]["Code"] == "MalformedXML"
|
assert err.exception.response["Error"]["Code"] == "MalformedXML"
|
||||||
|
|
||||||
|
|
||||||
|
@mock_s3
|
||||||
|
def test_lifecycle_with_nve():
|
||||||
|
client = boto3.client("s3")
|
||||||
|
client.create_bucket(Bucket="bucket")
|
||||||
|
|
||||||
|
lfc = {
|
||||||
|
"Rules": [
|
||||||
|
{
|
||||||
|
"NoncurrentVersionExpiration": {
|
||||||
|
"NoncurrentDays": 30
|
||||||
|
},
|
||||||
|
"ID": "wholebucket",
|
||||||
|
"Filter": {
|
||||||
|
"Prefix": ""
|
||||||
|
},
|
||||||
|
"Status": "Enabled"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
client.put_bucket_lifecycle_configuration(Bucket="bucket", LifecycleConfiguration=lfc)
|
||||||
|
result = client.get_bucket_lifecycle_configuration(Bucket="bucket")
|
||||||
|
assert len(result["Rules"]) == 1
|
||||||
|
assert result["Rules"][0]["NoncurrentVersionExpiration"]["NoncurrentDays"] == 30
|
||||||
|
|
||||||
|
# Change NoncurrentDays:
|
||||||
|
lfc["Rules"][0]["NoncurrentVersionExpiration"]["NoncurrentDays"] = 10
|
||||||
|
client.put_bucket_lifecycle_configuration(Bucket="bucket", LifecycleConfiguration=lfc)
|
||||||
|
result = client.get_bucket_lifecycle_configuration(Bucket="bucket")
|
||||||
|
assert len(result["Rules"]) == 1
|
||||||
|
assert result["Rules"][0]["NoncurrentVersionExpiration"]["NoncurrentDays"] == 10
|
||||||
|
|
||||||
|
# With failures for missing children:
|
||||||
|
del lfc["Rules"][0]["NoncurrentVersionExpiration"]["NoncurrentDays"]
|
||||||
|
with assert_raises(ClientError) as err:
|
||||||
|
client.put_bucket_lifecycle_configuration(Bucket="bucket", LifecycleConfiguration=lfc)
|
||||||
|
assert err.exception.response["Error"]["Code"] == "MalformedXML"
|
||||||
|
|
||||||
|
|
||||||
|
@mock_s3
|
||||||
|
def test_lifecycle_with_nvt():
|
||||||
|
client = boto3.client("s3")
|
||||||
|
client.create_bucket(Bucket="bucket")
|
||||||
|
|
||||||
|
lfc = {
|
||||||
|
"Rules": [
|
||||||
|
{
|
||||||
|
"NoncurrentVersionTransition": {
|
||||||
|
"NoncurrentDays": 30,
|
||||||
|
"StorageClass": "ONEZONE_IA"
|
||||||
|
},
|
||||||
|
"ID": "wholebucket",
|
||||||
|
"Filter": {
|
||||||
|
"Prefix": ""
|
||||||
|
},
|
||||||
|
"Status": "Enabled"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
client.put_bucket_lifecycle_configuration(Bucket="bucket", LifecycleConfiguration=lfc)
|
||||||
|
result = client.get_bucket_lifecycle_configuration(Bucket="bucket")
|
||||||
|
assert len(result["Rules"]) == 1
|
||||||
|
assert result["Rules"][0]["NoncurrentVersionTransition"]["NoncurrentDays"] == 30
|
||||||
|
assert result["Rules"][0]["NoncurrentVersionTransition"]["StorageClass"] == "ONEZONE_IA"
|
||||||
|
|
||||||
|
# Change NoncurrentDays:
|
||||||
|
lfc["Rules"][0]["NoncurrentVersionTransition"]["NoncurrentDays"] = 10
|
||||||
|
client.put_bucket_lifecycle_configuration(Bucket="bucket", LifecycleConfiguration=lfc)
|
||||||
|
result = client.get_bucket_lifecycle_configuration(Bucket="bucket")
|
||||||
|
assert len(result["Rules"]) == 1
|
||||||
|
assert result["Rules"][0]["NoncurrentVersionTransition"]["NoncurrentDays"] == 10
|
||||||
|
|
||||||
|
# Change StorageClass:
|
||||||
|
lfc["Rules"][0]["NoncurrentVersionTransition"]["StorageClass"] = "GLACIER"
|
||||||
|
client.put_bucket_lifecycle_configuration(Bucket="bucket", LifecycleConfiguration=lfc)
|
||||||
|
result = client.get_bucket_lifecycle_configuration(Bucket="bucket")
|
||||||
|
assert len(result["Rules"]) == 1
|
||||||
|
assert result["Rules"][0]["NoncurrentVersionTransition"]["StorageClass"] == "GLACIER"
|
||||||
|
|
||||||
|
# With failures for missing children:
|
||||||
|
del lfc["Rules"][0]["NoncurrentVersionTransition"]["NoncurrentDays"]
|
||||||
|
with assert_raises(ClientError) as err:
|
||||||
|
client.put_bucket_lifecycle_configuration(Bucket="bucket", LifecycleConfiguration=lfc)
|
||||||
|
assert err.exception.response["Error"]["Code"] == "MalformedXML"
|
||||||
|
lfc["Rules"][0]["NoncurrentVersionTransition"]["NoncurrentDays"] = 30
|
||||||
|
|
||||||
|
del lfc["Rules"][0]["NoncurrentVersionTransition"]["StorageClass"]
|
||||||
|
with assert_raises(ClientError) as err:
|
||||||
|
client.put_bucket_lifecycle_configuration(Bucket="bucket", LifecycleConfiguration=lfc)
|
||||||
|
assert err.exception.response["Error"]["Code"] == "MalformedXML"
|
||||||
|
|
||||||
|
|
||||||
|
@mock_s3
|
||||||
|
def test_lifecycle_with_aimu():
|
||||||
|
client = boto3.client("s3")
|
||||||
|
client.create_bucket(Bucket="bucket")
|
||||||
|
|
||||||
|
lfc = {
|
||||||
|
"Rules": [
|
||||||
|
{
|
||||||
|
"AbortIncompleteMultipartUpload": {
|
||||||
|
"DaysAfterInitiation": 7
|
||||||
|
},
|
||||||
|
"ID": "wholebucket",
|
||||||
|
"Filter": {
|
||||||
|
"Prefix": ""
|
||||||
|
},
|
||||||
|
"Status": "Enabled"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
client.put_bucket_lifecycle_configuration(Bucket="bucket", LifecycleConfiguration=lfc)
|
||||||
|
result = client.get_bucket_lifecycle_configuration(Bucket="bucket")
|
||||||
|
assert len(result["Rules"]) == 1
|
||||||
|
assert result["Rules"][0]["AbortIncompleteMultipartUpload"]["DaysAfterInitiation"] == 7
|
||||||
|
|
||||||
|
# Change DaysAfterInitiation:
|
||||||
|
lfc["Rules"][0]["AbortIncompleteMultipartUpload"]["DaysAfterInitiation"] = 30
|
||||||
|
client.put_bucket_lifecycle_configuration(Bucket="bucket", LifecycleConfiguration=lfc)
|
||||||
|
result = client.get_bucket_lifecycle_configuration(Bucket="bucket")
|
||||||
|
assert len(result["Rules"]) == 1
|
||||||
|
assert result["Rules"][0]["AbortIncompleteMultipartUpload"]["DaysAfterInitiation"] == 30
|
||||||
|
|
||||||
|
# With failures for missing children:
|
||||||
|
del lfc["Rules"][0]["AbortIncompleteMultipartUpload"]["DaysAfterInitiation"]
|
||||||
|
with assert_raises(ClientError) as err:
|
||||||
|
client.put_bucket_lifecycle_configuration(Bucket="bucket", LifecycleConfiguration=lfc)
|
||||||
|
assert err.exception.response["Error"]["Code"] == "MalformedXML"
|
||||||
|
|
||||||
|
|
||||||
@mock_s3_deprecated
|
@mock_s3_deprecated
|
||||||
def test_lifecycle_with_glacier_transition():
|
def test_lifecycle_with_glacier_transition():
|
||||||
conn = boto.s3.connect_to_region("us-west-1")
|
conn = boto.s3.connect_to_region("us-west-1")
|
||||||
|
Loading…
Reference in New Issue
Block a user