From 99c661781e18f53c48d315c51b8b0ca482b441d2 Mon Sep 17 00:00:00 2001 From: Mohit Alonja Date: Sat, 11 Sep 2021 12:51:01 +0530 Subject: [PATCH] Fix VPN gateway (#4279) --- moto/ec2/exceptions.py | 14 +++++++-- moto/ec2/models.py | 29 +++++++++++++++---- .../ec2/responses/virtual_private_gateways.py | 9 +++++- moto/ec2/utils.py | 4 +++ tests/terraform-tests.success.txt | 5 ++-- .../test_ec2/test_virtual_private_gateways.py | 3 +- 6 files changed, 51 insertions(+), 13 deletions(-) diff --git a/moto/ec2/exceptions.py b/moto/ec2/exceptions.py index cc8535e84..4811a9f8b 100644 --- a/moto/ec2/exceptions.py +++ b/moto/ec2/exceptions.py @@ -124,11 +124,19 @@ class InvalidNetworkAclIdError(EC2ClientError): class InvalidVpnGatewayIdError(EC2ClientError): - def __init__(self, network_acl_id): + def __init__(self, vpn_gw): super(InvalidVpnGatewayIdError, self).__init__( "InvalidVpnGatewayID.NotFound", - "The virtual private gateway ID '{0}' does not exist".format( - network_acl_id + "The virtual private gateway ID '{0}' does not exist".format(vpn_gw), + ) + + +class InvalidVpnGatewayAttachmentError(EC2ClientError): + def __init__(self, vpn_gw, vpc_id): + super(InvalidVpnGatewayAttachmentError, self).__init__( + "InvalidVpnGatewayAttachment.NotFound", + "The attachment with vpn gateway ID '{}' and vpc ID '{}' does not exist".format( + vpn_gw, vpc_id ), ) diff --git a/moto/ec2/models.py b/moto/ec2/models.py index 99c52b8f0..56514ed6f 100644 --- a/moto/ec2/models.py +++ b/moto/ec2/models.py @@ -95,6 +95,7 @@ from .exceptions import ( InvalidVPCIdError, InvalidVPCRangeError, InvalidVpnGatewayIdError, + InvalidVpnGatewayAttachmentError, InvalidVpnConnectionIdError, InvalidSubnetCidrBlockAssociationID, MalformedAMIIdError, @@ -3576,6 +3577,18 @@ class VPCBackend(object): return matches def delete_vpc(self, vpc_id): + # Do not delete if any VPN Gateway is attached + vpn_gateways = self.describe_vpn_gateways(filters={"attachment.vpc-id": vpc_id}) + vpn_gateways = [ + item + for item in vpn_gateways + if item.attachments.get(vpc_id).state == "attached" + ] + if vpn_gateways: + raise DependencyViolationError( + "The vpc {0} has dependencies and cannot be deleted.".format(vpc_id) + ) + # Delete route table if only main route table remains. route_tables = self.describe_route_tables(filters={"vpc-id": vpc_id}) if len(route_tables) > 1: @@ -6676,8 +6689,10 @@ class VpnGatewayBackend(object): self.vpn_gateways[vpn_gateway_id] = vpn_gateway return vpn_gateway - def describe_vpn_gateways(self, filters=None): - vpn_gateways = self.vpn_gateways.values() + def describe_vpn_gateways(self, filters=None, vpn_gw_ids=None): + vpn_gateways = list(self.vpn_gateways.values() or []) + if vpn_gw_ids: + vpn_gateways = [item for item in vpn_gateways if item.id in vpn_gw_ids] return generic_filter(filters, vpn_gateways) def get_vpn_gateway(self, vpn_gateway_id): @@ -6690,21 +6705,25 @@ class VpnGatewayBackend(object): vpn_gateway = self.get_vpn_gateway(vpn_gateway_id) self.get_vpc(vpc_id) attachment = VpnGatewayAttachment(vpc_id, state="attached") + for key in vpn_gateway.attachments.copy(): + if key.startswith("vpc-"): + vpn_gateway.attachments.pop(key) vpn_gateway.attachments[vpc_id] = attachment return attachment def delete_vpn_gateway(self, vpn_gateway_id): - deleted = self.vpn_gateways.pop(vpn_gateway_id, None) + deleted = self.vpn_gateways.get(vpn_gateway_id, None) if not deleted: raise InvalidVpnGatewayIdError(vpn_gateway_id) + deleted.state = "deleted" return deleted def detach_vpn_gateway(self, vpn_gateway_id, vpc_id): vpn_gateway = self.get_vpn_gateway(vpn_gateway_id) - self.get_vpc(vpc_id) detached = vpn_gateway.attachments.get(vpc_id, None) if not detached: - raise InvalidVPCIdError(vpc_id) + + raise InvalidVpnGatewayAttachmentError(vpn_gateway.id, vpc_id) detached.state = "detached" return detached diff --git a/moto/ec2/responses/virtual_private_gateways.py b/moto/ec2/responses/virtual_private_gateways.py index 1d2d325eb..1ee56b27a 100644 --- a/moto/ec2/responses/virtual_private_gateways.py +++ b/moto/ec2/responses/virtual_private_gateways.py @@ -36,7 +36,8 @@ class VirtualPrivateGateways(BaseResponse): def describe_vpn_gateways(self): filters = filters_from_querystring(self.querystring) - vpn_gateways = self.ec2_backend.describe_vpn_gateways(filters) + vpn_gw_ids = self._get_multi_param("VpnGatewayId") + vpn_gateways = self.ec2_backend.describe_vpn_gateways(filters, vpn_gw_ids) template = self.response_template(DESCRIBE_VPN_GATEWAYS_RESPONSE) return template.render(vpn_gateways=vpn_gateways) @@ -53,6 +54,9 @@ CREATE_VPN_GATEWAY_RESPONSE = """ 7a62c49f-347e-4fc4-9331-6e8eEXAMPLE {{ vpn_gateway.id }} + {% if vpn_gateway.amazon_side_asn %} + {{ vpn_gateway.amazon_side_asn }} + {% endif %} {{ vpn_gateway.state }} {{ vpn_gateway.type }} {{ vpn_gateway.availability_zone }} @@ -75,6 +79,9 @@ DESCRIBE_VPN_GATEWAYS_RESPONSE = """ {% for vpn_gateway in vpn_gateways %} {{ vpn_gateway.id }} + {% if vpn_gateway.amazon_side_asn %} + {{ vpn_gateway.amazon_side_asn }} + {% endif %} {{ vpn_gateway.state }} {{ vpn_gateway.id }} {{ vpn_gateway.availability_zone }} diff --git a/moto/ec2/utils.py b/moto/ec2/utils.py index 7043acd1a..89c28ce29 100644 --- a/moto/ec2/utils.py +++ b/moto/ec2/utils.py @@ -522,6 +522,10 @@ def is_filter_matching(obj, filter, filter_value): return True return False + if isinstance(value, type({}.keys())): + if isinstance(filter_value, str) and filter_value in value: + return True + try: value = set(value) return (value and value.issubset(filter_value)) or value.issuperset( diff --git a/tests/terraform-tests.success.txt b/tests/terraform-tests.success.txt index 100544d70..4a8aa6bdd 100644 --- a/tests/terraform-tests.success.txt +++ b/tests/terraform-tests.success.txt @@ -120,7 +120,6 @@ TestAccAwsEc2ManagedPrefixList TestAccAWSEgressOnlyInternetGateway TestAccAWSSecurityGroup_ TestAccAWSInternetGateway -TestAccAWSVpnGateway_basic -TestAccAWSVpnGateway_delete -TestAccAWSVpnGateway_tags TestAccAWSSecurityGroupRule_ +TestAccAWSVpnGateway +TestAccAWSVpnGatewayAttachment diff --git a/tests/test_ec2/test_virtual_private_gateways.py b/tests/test_ec2/test_virtual_private_gateways.py index 151f26782..38136d363 100644 --- a/tests/test_ec2/test_virtual_private_gateways.py +++ b/tests/test_ec2/test_virtual_private_gateways.py @@ -233,7 +233,8 @@ def test_delete_vpn_gateway(): conn.delete_vpn_gateway(vpn_gateway.id) vgws = conn.get_all_vpn_gateways() - vgws.should.have.length_of(0) + vgws.should.have.length_of(1) + vgws[0].state.should.equal("deleted") @mock_ec2_deprecated