From 1b864972ae82aa94457df19205776dc18f0e1b0b Mon Sep 17 00:00:00 2001 From: Viren Nadkarni Date: Wed, 11 Oct 2023 15:58:13 +0530 Subject: [PATCH 1/2] Introduce a default VPC DHCP option set --- moto/ec2/models/dhcp_options.py | 5 +++++ moto/ec2/responses/dhcp_options.py | 7 +++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/moto/ec2/models/dhcp_options.py b/moto/ec2/models/dhcp_options.py index 8f8e2ebe77e7..397407147991 100644 --- a/moto/ec2/models/dhcp_options.py +++ b/moto/ec2/models/dhcp_options.py @@ -69,6 +69,11 @@ def associate_dhcp_options(self, dhcp_options: DHCPOptionsSet, vpc: Any) -> None dhcp_options.vpc = vpc vpc.dhcp_options = dhcp_options + def disassociate_dhcp_options(self, vpc: Any) -> None: + if vpc.dhcp_options: + vpc.dhcp_options.vpc = None + vpc.dhcp_options = None + def create_dhcp_options( self, domain_name_servers: Optional[List[str]] = None, diff --git a/moto/ec2/responses/dhcp_options.py b/moto/ec2/responses/dhcp_options.py index 6c3160004631..153943eed184 100644 --- a/moto/ec2/responses/dhcp_options.py +++ b/moto/ec2/responses/dhcp_options.py @@ -6,10 +6,13 @@ def associate_dhcp_options(self) -> str: dhcp_opt_id = self._get_param("DhcpOptionsId") vpc_id = self._get_param("VpcId") - dhcp_opt = self.ec2_backend.describe_dhcp_options([dhcp_opt_id])[0] vpc = self.ec2_backend.get_vpc(vpc_id) - self.ec2_backend.associate_dhcp_options(dhcp_opt, vpc) + if dhcp_opt_id == "default": + self.ec2_backend.disassociate_dhcp_options(vpc) + else: + dhcp_opt = self.ec2_backend.describe_dhcp_options([dhcp_opt_id])[0] + self.ec2_backend.associate_dhcp_options(dhcp_opt, vpc) template = self.response_template(ASSOCIATE_DHCP_OPTIONS_RESPONSE) return template.render() From 571b2052ab19d996cbcbf9094a6e22649dda2b86 Mon Sep 17 00:00:00 2001 From: Viren Nadkarni Date: Wed, 11 Oct 2023 16:18:21 +0530 Subject: [PATCH 2/2] Add tests --- moto/ec2/responses/vpcs.py | 4 ++-- tests/test_ec2/test_dhcp_options.py | 28 ++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/moto/ec2/responses/vpcs.py b/moto/ec2/responses/vpcs.py index 2f0f2e91ff3f..93a80f90c388 100644 --- a/moto/ec2/responses/vpcs.py +++ b/moto/ec2/responses/vpcs.py @@ -374,7 +374,7 @@ def modify_managed_prefix_list(self) -> str: {% endfor %} {% endif %} - {% if vpc.dhcp_options %}{{ vpc.dhcp_options.id }}{% else %}dopt-1a2b3c4d2{% endif %} + {% if vpc.dhcp_options %}{{ vpc.dhcp_options.id }}{% else %}default{% endif %} {{ vpc.instance_tenancy }} {{ vpc.owner_id }} @@ -475,7 +475,7 @@ def modify_managed_prefix_list(self) -> str: {% endfor %} {% endif %} - {% if vpc.dhcp_options %}{{ vpc.dhcp_options.id }}{% else %}dopt-7a8b9c2d{% endif %} + {% if vpc.dhcp_options %}{{ vpc.dhcp_options.id }}{% else %}default{% endif %} {{ vpc.instance_tenancy }} {{ vpc.is_default }} {{ vpc.owner_id }} diff --git a/tests/test_ec2/test_dhcp_options.py b/tests/test_ec2/test_dhcp_options.py index d63a48838258..7910c0c81204 100644 --- a/tests/test_ec2/test_dhcp_options.py +++ b/tests/test_ec2/test_dhcp_options.py @@ -66,6 +66,34 @@ def test_dhcp_options_associate_invalid_vpc_id(): assert ex.value.response["Error"]["Code"] == "InvalidVpcID.NotFound" +@mock_ec2 +def test_dhcp_options_disassociation(): + """Ensure that VPCs can be set to the 'default' DHCP options set for disassociation.""" + ec2 = boto3.resource("ec2", region_name="us-west-1") + client = boto3.client("ec2", region_name="us-west-1") + dhcp_options = ec2.create_dhcp_options( + DhcpConfigurations=[ + {"Key": "domain-name", "Values": [SAMPLE_DOMAIN_NAME]}, + {"Key": "domain-name-servers", "Values": SAMPLE_NAME_SERVERS}, + ] + ) + vpc = ec2.create_vpc(CidrBlock="10.0.0.0/16") + + # Ensure newly created VPCs without any DHCP options can be disassociated + client.associate_dhcp_options(DhcpOptionsId="default", VpcId=vpc.id) + vpc.reload() + assert vpc.dhcp_options_id == "default" + + client.associate_dhcp_options(DhcpOptionsId=dhcp_options.id, VpcId=vpc.id) + vpc.reload() + assert vpc.dhcp_options_id == dhcp_options.id + + # Ensure VPCs with already associated DHCP options can be disassociated + client.associate_dhcp_options(DhcpOptionsId="default", VpcId=vpc.id) + vpc.reload() + assert vpc.dhcp_options_id == "default" + + @mock_ec2 def test_dhcp_options_delete_with_vpc(): """Test deletion of dhcp options with vpc"""