Implement CloudFormation Stack deletion for VPCs, Subnets (#6118)
This commit is contained in:
parent
ea96013957
commit
d33fe9e086
@ -108,6 +108,18 @@ class Subnet(TaggedEC2Resource, CloudFormationModel):
|
||||
|
||||
return subnet
|
||||
|
||||
@classmethod
|
||||
def delete_from_cloudformation_json( # type: ignore[misc]
|
||||
cls,
|
||||
resource_name: str,
|
||||
cloudformation_json: Dict[str, Any],
|
||||
account_id: str,
|
||||
region_name: str,
|
||||
) -> None:
|
||||
from ..models import ec2_backends
|
||||
|
||||
ec2_backends[account_id][region_name].delete_subnet(resource_name)
|
||||
|
||||
@property
|
||||
def available_ip_addresses(self) -> str:
|
||||
enis = [
|
||||
|
@ -239,6 +239,18 @@ class VPC(TaggedEC2Resource, CloudFormationModel):
|
||||
|
||||
return vpc
|
||||
|
||||
@classmethod
|
||||
def delete_from_cloudformation_json( # type: ignore[misc]
|
||||
cls,
|
||||
resource_name: str,
|
||||
cloudformation_json: Dict[str, Any],
|
||||
account_id: str,
|
||||
region_name: str,
|
||||
) -> None:
|
||||
from ..models import ec2_backends
|
||||
|
||||
ec2_backends[account_id][region_name].delete_vpc(resource_name)
|
||||
|
||||
@property
|
||||
def physical_resource_id(self) -> str:
|
||||
return self.id
|
||||
|
@ -1785,13 +1785,48 @@ def test_delete_stack_by_name():
|
||||
|
||||
|
||||
@mock_cloudformation
|
||||
@mock_ec2
|
||||
def test_delete_stack():
|
||||
cf = boto3.client("cloudformation", region_name="us-east-1")
|
||||
ec2 = boto3.client("ec2", region_name="us-east-1")
|
||||
|
||||
cf.create_stack(StackName="test_stack", TemplateBody=dummy_template_json)
|
||||
state = ec2.describe_instances()["Reservations"][0]["Instances"][0]["State"]
|
||||
|
||||
cf.delete_stack(StackName="test_stack")
|
||||
stacks = cf.list_stacks()
|
||||
assert stacks["StackSummaries"][0]["StackStatus"] == "DELETE_COMPLETE"
|
||||
ec2.describe_instances()["Reservations"][0]["Instances"][0]["State"].shouldnt.equal(
|
||||
state
|
||||
)
|
||||
|
||||
|
||||
@mock_cloudformation
|
||||
@mock_ec2
|
||||
@pytest.mark.skipif(
|
||||
settings.TEST_SERVER_MODE,
|
||||
reason="Can't patch model delete attributes in server mode.",
|
||||
)
|
||||
def test_delete_stack_delete_not_implemented(monkeypatch):
|
||||
monkeypatch.delattr(
|
||||
"moto.ec2.models.instances.Instance.delete_from_cloudformation_json"
|
||||
)
|
||||
monkeypatch.delattr("moto.ec2.models.instances.Instance.delete")
|
||||
|
||||
cf = boto3.client("cloudformation", region_name="us-east-1")
|
||||
ec2 = boto3.client("ec2", region_name="us-east-1")
|
||||
|
||||
cf.create_stack(StackName="test_stack", TemplateBody=dummy_template_json)
|
||||
state = ec2.describe_instances()["Reservations"][0]["Instances"][0]["State"]
|
||||
|
||||
# Mock stack deletion succeeds
|
||||
cf.delete_stack(StackName="test_stack")
|
||||
stacks = cf.list_stacks()
|
||||
assert stacks["StackSummaries"][0]["StackStatus"] == "DELETE_COMPLETE"
|
||||
# But the underlying resource is untouched
|
||||
ec2.describe_instances()["Reservations"][0]["Instances"][0]["State"].should.equal(
|
||||
state
|
||||
)
|
||||
|
||||
|
||||
@mock_cloudformation
|
||||
|
@ -21,6 +21,18 @@ template_vpc = {
|
||||
},
|
||||
}
|
||||
|
||||
template_subnet = {
|
||||
"AWSTemplateFormatVersion": "2010-09-09",
|
||||
"Description": "Create VPC",
|
||||
"Resources": {
|
||||
"VPC": {"Properties": {"CidrBlock": "192.168.0.0/16"}, "Type": "AWS::EC2::VPC"},
|
||||
"Subnet": {
|
||||
"Properties": {"VpcId": {"Ref": "VPC"}, "CidrBlock": "192.168.0.0/18"},
|
||||
"Type": "AWS::EC2::Subnet",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@mock_ec2
|
||||
@mock_cloudformation
|
||||
@ -84,29 +96,44 @@ def test_vpc_single_instance_in_subnet():
|
||||
|
||||
@mock_cloudformation
|
||||
@mock_ec2
|
||||
def test_delete_stack_with_resource_missing_delete_attr():
|
||||
def test_delete_stack_with_vpc():
|
||||
cf = boto3.client("cloudformation", region_name="us-east-1")
|
||||
ec2 = boto3.client("ec2", region_name="us-east-1")
|
||||
name = str(uuid4())[0:6]
|
||||
|
||||
cf.create_stack(StackName=name, TemplateBody=json.dumps(template_vpc))
|
||||
cf.describe_stacks(StackName=name)["Stacks"].should.have.length_of(1)
|
||||
|
||||
resources = cf.list_stack_resources(StackName=name)["StackResourceSummaries"]
|
||||
vpc_id = resources[0]["PhysicalResourceId"]
|
||||
|
||||
cf.delete_stack(
|
||||
StackName=name
|
||||
) # should succeed, despite the fact that the resource itself cannot be deleted
|
||||
with pytest.raises(ClientError) as exc:
|
||||
cf.describe_stacks(StackName=name)
|
||||
err = exc.value.response["Error"]
|
||||
err.should.have.key("Code").equals("ValidationError")
|
||||
err.should.have.key("Message").equals(f"Stack with id {name} does not exist")
|
||||
|
||||
# We still have our VPC, as the VPC-object does not have a delete-method yet
|
||||
ec2.describe_vpcs(VpcIds=[vpc_id])["Vpcs"].should.have.length_of(1)
|
||||
|
||||
cf.delete_stack(StackName=name)
|
||||
with pytest.raises(ec2.exceptions.ClientError):
|
||||
ec2.describe_vpcs(VpcIds=[vpc_id])
|
||||
|
||||
|
||||
@mock_cloudformation
|
||||
@mock_ec2
|
||||
def test_delete_stack_with_subnet():
|
||||
cf = boto3.client("cloudformation", region_name="us-east-1")
|
||||
ec2 = boto3.client("ec2", region_name="us-east-1")
|
||||
name = str(uuid4())[0:6]
|
||||
|
||||
cf.create_stack(StackName=name, TemplateBody=json.dumps(template_subnet))
|
||||
subnet_ids = [
|
||||
resource["PhysicalResourceId"]
|
||||
for resource in cf.list_stack_resources(StackName=name)[
|
||||
"StackResourceSummaries"
|
||||
]
|
||||
if resource["ResourceType"] == "AWS::EC2::Subnet"
|
||||
]
|
||||
|
||||
ec2.describe_subnets(SubnetIds=subnet_ids)["Subnets"].should.have.length_of(1)
|
||||
|
||||
cf.delete_stack(StackName=name)
|
||||
with pytest.raises(ec2.exceptions.ClientError):
|
||||
ec2.describe_subnets(SubnetIds=subnet_ids)
|
||||
|
||||
|
||||
@mock_ec2
|
||||
@mock_cloudformation
|
||||
|
Loading…
Reference in New Issue
Block a user