From d2302bae214f78174f2d8352df55aeae1160b767 Mon Sep 17 00:00:00 2001 From: Steve Pulec Date: Fri, 8 Jan 2016 16:48:01 -0500 Subject: [PATCH] Add Customer Gateway Support. Closes #465 --- moto/ec2/exceptions.py | 8 +++ moto/ec2/models.py | 47 ++++++++++++++- moto/ec2/responses/customer_gateways.py | 75 +++++++++++++++++++++++- moto/ec2/utils.py | 6 +- tests/test_ec2/test_customer_gateways.py | 40 ++++++++++++- 5 files changed, 167 insertions(+), 9 deletions(-) diff --git a/moto/ec2/exceptions.py b/moto/ec2/exceptions.py index 5c3d64727..9e8f93004 100644 --- a/moto/ec2/exceptions.py +++ b/moto/ec2/exceptions.py @@ -92,6 +92,14 @@ class InvalidVpnConnectionIdError(EC2ClientError): .format(network_acl_id)) +class InvalidCustomerGatewayIdError(EC2ClientError): + def __init__(self, customer_gateway_id): + super(InvalidCustomerGatewayIdError, self).__init__( + "InvalidCustomerGatewayID.NotFound", + "The customer gateway ID '{0}' does not exist" + .format(customer_gateway_id)) + + class InvalidNetworkInterfaceIdError(EC2ClientError): def __init__(self, eni_id): super(InvalidNetworkInterfaceIdError, self).__init__( diff --git a/moto/ec2/models.py b/moto/ec2/models.py index 4ae2c00c3..501615722 100644 --- a/moto/ec2/models.py +++ b/moto/ec2/models.py @@ -55,7 +55,8 @@ from .exceptions import ( InvalidCIDRSubnetError, InvalidNetworkAclIdError, InvalidVpnGatewayIdError, - InvalidVpnConnectionIdError + InvalidVpnConnectionIdError, + InvalidCustomerGatewayIdError, ) from .utils import ( EC2_RESOURCE_TO_PREFIX, @@ -95,6 +96,7 @@ from .utils import ( random_network_acl_subnet_association_id, random_vpn_gateway_id, random_vpn_connection_id, + random_customer_gateway_id, is_tag_filter, ) @@ -2798,6 +2800,45 @@ class VpnGatewayBackend(object): return detached +class CustomerGateway(TaggedEC2Resource): + def __init__(self, ec2_backend, id, type, ip_address, bgp_asn): + self.ec2_backend = ec2_backend + self.id = id + self.type = type + self.ip_address = ip_address + self.bgp_asn = bgp_asn + self.attachments = {} + super(CustomerGateway, self).__init__() + + +class CustomerGatewayBackend(object): + def __init__(self): + self.customer_gateways = {} + super(CustomerGatewayBackend, self).__init__() + + def create_customer_gateway(self, type='ipsec.1', ip_address=None, bgp_asn=None): + customer_gateway_id = random_customer_gateway_id() + customer_gateway = CustomerGateway(self, customer_gateway_id, type, ip_address, bgp_asn) + self.customer_gateways[customer_gateway_id] = customer_gateway + return customer_gateway + + def get_all_customer_gateways(self, filters=None): + customer_gateways = self.customer_gateways.values() + return generic_filter(filters, customer_gateways) + + def get_customer_gateway(self, customer_gateway_id): + customer_gateway = self.customer_gateways.get(customer_gateway_id, None) + if not customer_gateway: + raise InvalidCustomerGatewayIdError(customer_gateway_id) + return customer_gateway + + def delete_customer_gateway(self, customer_gateway_id): + deleted = self.customer_gateways.pop(customer_gateway_id, None) + if not deleted: + raise InvalidCustomerGatewayIdError(customer_gateway_id) + return deleted + + class EC2Backend(BaseBackend, InstanceBackend, TagBackend, AmiBackend, RegionsAndZonesBackend, SecurityGroupBackend, EBSBackend, VPCBackend, SubnetBackend, SubnetRouteTableAssociationBackend, @@ -2806,7 +2847,7 @@ class EC2Backend(BaseBackend, InstanceBackend, TagBackend, AmiBackend, RouteTableBackend, RouteBackend, InternetGatewayBackend, VPCGatewayAttachmentBackend, SpotRequestBackend, ElasticAddressBackend, KeyPairBackend, DHCPOptionsSetBackend, - NetworkAclBackend, VpnGatewayBackend): + NetworkAclBackend, VpnGatewayBackend, CustomerGatewayBackend): def __init__(self, region_name): super(EC2Backend, self).__init__() @@ -2831,7 +2872,7 @@ class EC2Backend(BaseBackend, InstanceBackend, TagBackend, AmiBackend, for resource_id in resource_ids: resource_prefix = get_prefix(resource_id) if resource_prefix == EC2_RESOURCE_TO_PREFIX['customer-gateway']: - self.raise_not_implemented_error('DescribeCustomerGateways') + self.get_customer_gateway(customer_gateway_id=resource_id) elif resource_prefix == EC2_RESOURCE_TO_PREFIX['dhcp-options']: self.describe_dhcp_options(options_ids=[resource_id]) elif resource_prefix == EC2_RESOURCE_TO_PREFIX['image']: diff --git a/moto/ec2/responses/customer_gateways.py b/moto/ec2/responses/customer_gateways.py index 150ed755b..a6d460c88 100644 --- a/moto/ec2/responses/customer_gateways.py +++ b/moto/ec2/responses/customer_gateways.py @@ -1,13 +1,82 @@ from __future__ import unicode_literals from moto.core.responses import BaseResponse +from moto.ec2.utils import filters_from_querystring class CustomerGateways(BaseResponse): + def create_customer_gateway(self): - raise NotImplementedError('CustomerGateways(AmazonVPC).create_customer_gateway is not yet implemented') + # raise NotImplementedError('CustomerGateways(AmazonVPC).create_customer_gateway is not yet implemented') + type = self.querystring.get('Type', None)[0] + ip_address = self.querystring.get('IpAddress', None)[0] + bgp_asn = self.querystring.get('BgpAsn', None)[0] + customer_gateway = self.ec2_backend.create_customer_gateway(type, ip_address=ip_address, bgp_asn=bgp_asn) + template = self.response_template(CREATE_CUSTOMER_GATEWAY_RESPONSE) + return template.render(customer_gateway=customer_gateway) def delete_customer_gateway(self): - raise NotImplementedError('CustomerGateways(AmazonVPC).delete_customer_gateway is not yet implemented') + customer_gateway_id = self.querystring.get('CustomerGatewayId')[0] + delete_status = self.ec2_backend.delete_customer_gateway(customer_gateway_id) + template = self.response_template(DELETE_CUSTOMER_GATEWAY_RESPONSE) + return template.render(customer_gateway=delete_status) def describe_customer_gateways(self): - raise NotImplementedError('CustomerGateways(AmazonVPC).describe_customer_gateways is not yet implemented') + filters = filters_from_querystring(self.querystring) + customer_gateways = self.ec2_backend.get_all_customer_gateways(filters) + template = self.response_template(DESCRIBE_CUSTOMER_GATEWAYS_RESPONSE) + return template.render(customer_gateways=customer_gateways) + + +CREATE_CUSTOMER_GATEWAY_RESPONSE = """ + + 7a62c49f-347e-4fc4-9331-6e8eEXAMPLE + + {{ customer_gateway.id }} + pending + {{ customer_gateway.type }} + {{ customer_gateway.ip_address }} + {{ customer_gateway.bgp_asn }} + + {% for tag in customer_gateway.get_tags() %} + + {{ tag.resource_id }} + {{ tag.resource_type }} + {{ tag.key }} + {{ tag.value }} + + {% endfor %} + + +""" + +DELETE_CUSTOMER_GATEWAY_RESPONSE = """ + + 7a62c49f-347e-4fc4-9331-6e8eEXAMPLE + {{ delete_status }} +""" + +DESCRIBE_CUSTOMER_GATEWAYS_RESPONSE = """ + + 7a62c49f-347e-4fc4-9331-6e8eEXAMPLE + + {% for customer_gateway in customer_gateways %} + + {{ customer_gateway.id }} + {{ customer_gateway.state }} + available + {{ customer_gateway.ip_address }} + {{ customer_gateway.bgp_asn }} + + {% for tag in customer_gateway.get_tags() %} + + {{ tag.resource_id }} + {{ tag.resource_type }} + {{ tag.key }} + {{ tag.value }} + + {% endfor %} + + + {% endfor %} + +""" diff --git a/moto/ec2/utils.py b/moto/ec2/utils.py index e0edde7d6..5b7743bf4 100644 --- a/moto/ec2/utils.py +++ b/moto/ec2/utils.py @@ -89,6 +89,10 @@ def random_vpn_connection_id(): return random_id(prefix=EC2_RESOURCE_TO_PREFIX['vpn-connection']) +def random_customer_gateway_id(): + return random_id(prefix=EC2_RESOURCE_TO_PREFIX['customer-gateway']) + + def random_volume_id(): return random_id(prefix=EC2_RESOURCE_TO_PREFIX['volume']) @@ -314,7 +318,7 @@ def get_object_value(obj, attr): def is_tag_filter(filter_name): - return (filter_name.startswith('tag:') or + return (filter_name.startswith('tag:') or filter_name.startswith('tag-value') or filter_name.startswith('tag-key')) diff --git a/tests/test_ec2/test_customer_gateways.py b/tests/test_ec2/test_customer_gateways.py index 80fddf287..efd6ce993 100644 --- a/tests/test_ec2/test_customer_gateways.py +++ b/tests/test_ec2/test_customer_gateways.py @@ -1,10 +1,46 @@ from __future__ import unicode_literals import boto import sure # noqa +from nose.tools import assert_raises +from nose.tools import assert_false +from boto.exception import EC2ResponseError from moto import mock_ec2 @mock_ec2 -def test_customer_gateways(): - pass +def test_create_customer_gateways(): + conn = boto.connect_vpc('the_key', 'the_secret') + + customer_gateway = conn.create_customer_gateway('ipsec.1', '205.251.242.54', 65534) + customer_gateway.should_not.be.none + customer_gateway.id.should.match(r'cgw-\w+') + customer_gateway.type.should.equal('ipsec.1') + customer_gateway.bgp_asn.should.equal(65534) + customer_gateway.ip_address.should.equal('205.251.242.54') + +@mock_ec2 +def test_describe_customer_gateways(): + conn = boto.connect_vpc('the_key', 'the_secret') + customer_gateway = conn.create_customer_gateway('ipsec.1', '205.251.242.54', 65534) + cgws = conn.get_all_customer_gateways() + cgws.should.have.length_of(1) + cgws[0].id.should.match(customer_gateway.id) + +@mock_ec2 +def test_delete_customer_gateways(): + conn = boto.connect_vpc('the_key', 'the_secret') + + customer_gateway = conn.create_customer_gateway('ipsec.1', '205.251.242.54', 65534) + customer_gateway.should_not.be.none + cgws = conn.get_all_customer_gateways() + cgws[0].id.should.match(customer_gateway.id) + deleted = conn.delete_customer_gateway(customer_gateway.id) + cgws = conn.get_all_customer_gateways() + cgws.should.have.length_of(0) + +@mock_ec2 +def test_delete_customer_gateways_bad_id(): + conn = boto.connect_vpc('the_key', 'the_secret') + with assert_raises(EC2ResponseError) as cm: + conn.delete_customer_gateway('cgw-0123abcd')