Skip to content

Commit

Permalink
fix matching rule when ExpiredObjectDeleteMarker is set (#8752)
Browse files Browse the repository at this point in the history
  • Loading branch information
bentsku committed Jul 28, 2023
1 parent b7d8458 commit eca5bfd
Show file tree
Hide file tree
Showing 4 changed files with 110 additions and 3 deletions.
6 changes: 5 additions & 1 deletion localstack/services/s3/provider.py
Expand Up @@ -1915,7 +1915,11 @@ def validate_lifecycle_configuration(lifecycle_conf: BucketLifecycleConfiguratio
if len(rule_filter) > 1:
raise MalformedXML()

if exp_date := (rule.get("Expiration", {}).get("Date")):
if (expiration := rule.get("Expiration", {})) and "ExpiredObjectDeleteMarker" in expiration:
if len(expiration) > 1:
raise MalformedXML()

if exp_date := (expiration.get("Date")):
if exp_date.timetz() != datetime.time(
hour=0, minute=0, second=0, microsecond=0, tzinfo=ZoneInfo("GMT")
):
Expand Down
2 changes: 1 addition & 1 deletion localstack/services/s3/utils.py
Expand Up @@ -460,7 +460,7 @@ def get_lifecycle_rule_from_object(
lifecycle_conf_rules: LifecycleRules, moto_object: FakeKey, object_tags: dict[str, str]
) -> LifecycleRule:
for rule in lifecycle_conf_rules:
if "Expiration" not in rule:
if not (expiration := rule.get("Expiration")) or "ExpiredObjectDeleteMarker" in expiration:
continue

if not (rule_filter := rule.get("Filter")):
Expand Down
53 changes: 53 additions & 0 deletions tests/integration/s3/test_s3.py
Expand Up @@ -8344,6 +8344,24 @@ def test_put_bucket_lifecycle_conf_exc(self, s3_bucket, snapshot, aws_client):

snapshot.match("duplicate-tag-keys", e.value.response)

with pytest.raises(ClientError) as e:
lfc["Rules"] = [
{
"ID": "expired-delete-marker-and-days",
"Filter": {},
"Status": "Enabled",
"Expiration": {
"Days": 1,
"ExpiredObjectDeleteMarker": True,
},
}
]
aws_client.s3.put_bucket_lifecycle_configuration(
Bucket=s3_bucket, LifecycleConfiguration=lfc
)

snapshot.match("expired-delete-marker-and-days", e.value.response)

@markers.parity.aws_validated
def test_bucket_lifecycle_configuration_date(self, s3_bucket, snapshot, aws_client):
snapshot.add_transformer(
Expand Down Expand Up @@ -8755,6 +8773,41 @@ def test_bucket_lifecycle_tag_rules(self, s3_bucket, snapshot, aws_client):
snapshot.match("put-object-no-match", put_object_3)
assert "Expiration" not in put_object_3

@markers.parity.aws_validated
@markers.snapshot.skip_snapshot_verify(paths=["$..ServerSideEncryption"])
def test_lifecycle_expired_object_delete_marker(self, s3_bucket, snapshot, aws_client):
snapshot.add_transformer(
[
snapshot.transform.key_value("BucketName"),
snapshot.transform.key_value(
"Expiration", reference_replacement=False, value_replacement="<expiration>"
),
]
)
rule_id = "rule-marker"
lfc = {
"Rules": [
{
"Expiration": {"ExpiredObjectDeleteMarker": True},
"ID": rule_id,
"Filter": {},
"Status": "Enabled",
}
]
}
aws_client.s3.put_bucket_lifecycle_configuration(
Bucket=s3_bucket, LifecycleConfiguration=lfc
)
result = aws_client.s3.get_bucket_lifecycle_configuration(Bucket=s3_bucket)
snapshot.match("get-bucket-lifecycle-conf", result)

key = "test-expired-object-delete-marker"
put_object = aws_client.s3.put_object(Body=b"test", Bucket=s3_bucket, Key=key)
snapshot.match("put-object", put_object)

response = aws_client.s3.head_object(Bucket=s3_bucket, Key=key)
snapshot.match("head-object", response)


def _anon_client(service: str):
conf = Config(signature_version=UNSIGNED)
Expand Down
52 changes: 51 additions & 1 deletion tests/integration/s3/test_s3.snapshot.json
Expand Up @@ -8062,7 +8062,7 @@
}
},
"tests/integration/s3/test_s3.py::TestS3BucketLifecycle::test_put_bucket_lifecycle_conf_exc": {
"recorded-date": "07-07-2023, 20:42:44",
"recorded-date": "26-07-2023, 15:06:44",
"recorded-content": {
"missing-id": {
"Error": {
Expand Down Expand Up @@ -8135,6 +8135,16 @@
"HTTPHeaders": {},
"HTTPStatusCode": 400
}
},
"expired-delete-marker-and-days": {
"Error": {
"Code": "MalformedXML",
"Message": "The XML you provided was not well-formed or did not validate against our published schema"
},
"ResponseMetadata": {
"HTTPHeaders": {},
"HTTPStatusCode": 400
}
}
}
},
Expand Down Expand Up @@ -8634,5 +8644,45 @@
"Message": "Bucket is missing Object Lock Configuration"
}
}
},
"tests/integration/s3/test_s3.py::TestS3BucketLifecycle::test_lifecycle_expired_object_delete_marker": {
"recorded-date": "26-07-2023, 15:14:49",
"recorded-content": {
"get-bucket-lifecycle-conf": {
"Rules": [
{
"Expiration": "<expiration>",
"Filter": {},
"ID": "rule-marker",
"Status": "Enabled"
}
],
"ResponseMetadata": {
"HTTPHeaders": {},
"HTTPStatusCode": 200
}
},
"put-object": {
"ETag": "\"098f6bcd4621d373cade4e832627b4f6\"",
"ServerSideEncryption": "AES256",
"ResponseMetadata": {
"HTTPHeaders": {},
"HTTPStatusCode": 200
}
},
"head-object": {
"AcceptRanges": "bytes",
"ContentLength": 4,
"ContentType": "binary/octet-stream",
"ETag": "\"098f6bcd4621d373cade4e832627b4f6\"",
"LastModified": "datetime",
"Metadata": {},
"ServerSideEncryption": "AES256",
"ResponseMetadata": {
"HTTPHeaders": {},
"HTTPStatusCode": 200
}
}
}
}
}

0 comments on commit eca5bfd

Please sign in to comment.