CloudFormation: Allow deletion of deeply nested structures (#7413)
This commit is contained in:
parent
c404f0877e
commit
cd40abe0a9
@ -835,8 +835,15 @@ class ResourceMap(collections_abc.Mapping): # type: ignore[type-arg]
|
||||
for key, value in self._resource_json_map.items()
|
||||
if not value.get("DeletionPolicy") == "Retain"
|
||||
)
|
||||
tries = 1
|
||||
while remaining_resources and tries < 5:
|
||||
# Keep track of how many resources we deleted before
|
||||
# (set to current + 1 to pretend the number of resources is already going down)
|
||||
previous_remaining_resources = len(remaining_resources) + 1
|
||||
# Delete while we have resources, and while the number of remaining resources is going down
|
||||
while (
|
||||
remaining_resources
|
||||
and len(remaining_resources) < previous_remaining_resources
|
||||
):
|
||||
previous_remaining_resources = len(remaining_resources)
|
||||
for resource in remaining_resources.copy():
|
||||
parsed_resource = self._parsed_resources.get(resource)
|
||||
try:
|
||||
@ -858,8 +865,8 @@ class ResourceMap(collections_abc.Mapping): # type: ignore[type-arg]
|
||||
last_exception = e
|
||||
else:
|
||||
remaining_resources.remove(resource)
|
||||
tries += 1
|
||||
if tries == 5:
|
||||
|
||||
if remaining_resources:
|
||||
raise last_exception
|
||||
|
||||
def _delete_resource(
|
||||
|
@ -743,7 +743,7 @@ class Role(CloudFormationModel):
|
||||
|
||||
for role in backend.roles.values():
|
||||
if role.name == resource_name:
|
||||
for arn in role.policies.keys():
|
||||
for arn in list(role.policies.keys()):
|
||||
role.delete_policy(arn)
|
||||
backend.delete_role(resource_name)
|
||||
|
||||
|
@ -1723,6 +1723,49 @@ def test_delete_stack():
|
||||
assert ec2.describe_instances()["Reservations"][0]["Instances"][0]["State"] != state
|
||||
|
||||
|
||||
@mock_aws
|
||||
@pytest.mark.parametrize("nr_of_resources", [1, 3, 5])
|
||||
def test_delete_stack_with_nested_resources(nr_of_resources):
|
||||
"""
|
||||
Resources can be dependent on eachother
|
||||
Verify that we'll keep deleting resources from a stack until there are none left
|
||||
"""
|
||||
nested_template = {"Resources": {}}
|
||||
for idx in range(nr_of_resources):
|
||||
role_refs = []
|
||||
for idy in range(nr_of_resources):
|
||||
role = {
|
||||
"Type": "AWS::IAM::Role",
|
||||
"Properties": {
|
||||
"AssumeRolePolicyDocument": {
|
||||
"Statement": [
|
||||
{
|
||||
"Action": ["sts:AssumeRole"],
|
||||
"Effect": "Allow",
|
||||
"Principal": {"Service": ["ec2.amazonaws.com"]},
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
}
|
||||
nested_template["Resources"].update({f"role{idx}_{idy}": role})
|
||||
role_refs.append({"Ref": f"role{idx}_{idy}"})
|
||||
|
||||
instance_profile = {
|
||||
"Type": "AWS::IAM::InstanceProfile",
|
||||
"Properties": {"Path": "/", "Roles": role_refs},
|
||||
}
|
||||
nested_template["Resources"].update({f"ip{idx}": instance_profile})
|
||||
cf = boto3.client("cloudformation", region_name=REGION_NAME)
|
||||
|
||||
cf.create_stack(StackName=TEST_STACK_NAME, TemplateBody=json.dumps(nested_template))
|
||||
cf.delete_stack(StackName=TEST_STACK_NAME)
|
||||
|
||||
iam = boto3.client("iam", REGION_NAME)
|
||||
assert not iam.list_roles()["Roles"]
|
||||
assert not iam.list_instance_profiles()["InstanceProfiles"]
|
||||
|
||||
|
||||
@mock_aws
|
||||
@pytest.mark.skipif(
|
||||
settings.TEST_SERVER_MODE,
|
||||
|
Loading…
Reference in New Issue
Block a user