Implement NAT Gateway resource
This commit is contained in:
parent
9b0be24b28
commit
21fb961c6a
@ -15,6 +15,7 @@ from boto.ec2.launchspecification import LaunchSpecification
|
||||
|
||||
from moto.core import BaseBackend
|
||||
from moto.core.models import Model
|
||||
from moto.core.utils import iso_8601_datetime_with_milliseconds
|
||||
from .exceptions import (
|
||||
EC2ClientError,
|
||||
DependencyViolationError,
|
||||
@ -70,6 +71,7 @@ from .utils import (
|
||||
random_instance_id,
|
||||
random_internet_gateway_id,
|
||||
random_ip,
|
||||
random_nat_gateway_id,
|
||||
random_key_pair,
|
||||
random_private_ip,
|
||||
random_public_ip,
|
||||
@ -2955,6 +2957,61 @@ class CustomerGatewayBackend(object):
|
||||
return deleted
|
||||
|
||||
|
||||
class NatGateway(object):
|
||||
|
||||
def __init__(self, backend, subnet_id, allocation_id):
|
||||
# public properties
|
||||
self.id = random_nat_gateway_id()
|
||||
self.subnet_id = subnet_id
|
||||
self.allocation_id = allocation_id
|
||||
self.state = 'available'
|
||||
self.private_ip = random_private_ip()
|
||||
|
||||
# protected properties
|
||||
self._created_at = datetime.utcnow()
|
||||
self._backend = backend
|
||||
# NOTE: this is the core of NAT Gateways creation
|
||||
self._eni = self._backend.create_network_interface(backend.get_subnet(self.subnet_id), self.private_ip)
|
||||
|
||||
# associate allocation with ENI
|
||||
self._backend.associate_address(eni=self._eni, allocation_id=self.allocation_id)
|
||||
|
||||
@property
|
||||
def vpc_id(self):
|
||||
subnet = self._backend.get_subnet(self.subnet_id)
|
||||
return subnet.vpc_id
|
||||
|
||||
@property
|
||||
def create_time(self):
|
||||
return iso_8601_datetime_with_milliseconds(self._created_at)
|
||||
|
||||
@property
|
||||
def network_interface_id(self):
|
||||
return self._eni.id
|
||||
|
||||
@property
|
||||
def public_ip(self):
|
||||
eips = self._backend.address_by_allocation([self.allocation_id])
|
||||
return eips[0].public_ip
|
||||
|
||||
|
||||
class NatGatewayBackend(object):
|
||||
|
||||
def __init__(self):
|
||||
self.nat_gateways = {}
|
||||
|
||||
def get_all_nat_gateways(self, filters):
|
||||
return self.nat_gateways.values()
|
||||
|
||||
def create_nat_gateway(self, subnet_id, allocation_id):
|
||||
nat_gateway = NatGateway(self, subnet_id, allocation_id)
|
||||
self.nat_gateways[nat_gateway.id] = nat_gateway
|
||||
return nat_gateway
|
||||
|
||||
def delete_nat_gateway(self, nat_gateway_id):
|
||||
return self.nat_gateways.pop(nat_gateway_id)
|
||||
|
||||
|
||||
class EC2Backend(BaseBackend, InstanceBackend, TagBackend, AmiBackend,
|
||||
RegionsAndZonesBackend, SecurityGroupBackend, EBSBackend,
|
||||
VPCBackend, SubnetBackend, SubnetRouteTableAssociationBackend,
|
||||
@ -2963,7 +3020,8 @@ class EC2Backend(BaseBackend, InstanceBackend, TagBackend, AmiBackend,
|
||||
RouteTableBackend, RouteBackend, InternetGatewayBackend,
|
||||
VPCGatewayAttachmentBackend, SpotRequestBackend,
|
||||
ElasticAddressBackend, KeyPairBackend, DHCPOptionsSetBackend,
|
||||
NetworkAclBackend, VpnGatewayBackend, CustomerGatewayBackend):
|
||||
NetworkAclBackend, VpnGatewayBackend, CustomerGatewayBackend,
|
||||
NatGatewayBackend):
|
||||
|
||||
def __init__(self, region_name):
|
||||
super(EC2Backend, self).__init__()
|
||||
|
@ -29,6 +29,7 @@ from .vpcs import VPCs
|
||||
from .vpc_peering_connections import VPCPeeringConnections
|
||||
from .vpn_connections import VPNConnections
|
||||
from .windows import Windows
|
||||
from .nat_gateways import NatGateways
|
||||
|
||||
|
||||
class EC2Response(
|
||||
@ -61,6 +62,7 @@ class EC2Response(
|
||||
VPCPeeringConnections,
|
||||
VPNConnections,
|
||||
Windows,
|
||||
NatGateways,
|
||||
):
|
||||
@property
|
||||
def ec2_backend(self):
|
||||
|
73
moto/ec2/responses/nat_gateways.py
Normal file
73
moto/ec2/responses/nat_gateways.py
Normal file
@ -0,0 +1,73 @@
|
||||
from __future__ import unicode_literals
|
||||
from moto.core.responses import BaseResponse
|
||||
from moto.ec2.utils import filters_from_querystring
|
||||
|
||||
|
||||
class NatGateways(BaseResponse):
|
||||
|
||||
def create_nat_gateway(self):
|
||||
subnet_id = self.querystring.get('SubnetId')[0]
|
||||
allocation_id = self.querystring.get('AllocationId')[0]
|
||||
nat_gateway = self.ec2_backend.create_nat_gateway(subnet_id=subnet_id, allocation_id=allocation_id)
|
||||
template = self.response_template(CREATE_NAT_GATEWAY)
|
||||
return template.render(nat_gateway=nat_gateway)
|
||||
|
||||
def delete_nat_gateway(self):
|
||||
nat_gateway_id = self.querystring.get('NatGatewayId')[0]
|
||||
nat_gateway = self.ec2_backend.delete_nat_gateway(nat_gateway_id)
|
||||
template = self.response_template(DELETE_NAT_GATEWAY_RESPONSE)
|
||||
return template.render(nat_gateway=nat_gateway)
|
||||
|
||||
def describe_nat_gateways(self):
|
||||
filters = filters_from_querystring(self.querystring)
|
||||
nat_gateways = self.ec2_backend.get_all_nat_gateways(filters)
|
||||
template = self.response_template(DESCRIBE_NAT_GATEWAYS_RESPONSE)
|
||||
return template.render(nat_gateways=nat_gateways)
|
||||
|
||||
|
||||
DESCRIBE_NAT_GATEWAYS_RESPONSE = """<DescribeNatGatewaysResponse xmlns="http://ec2.amazonaws.com/doc/2015-10-01/">
|
||||
<requestId>bfed02c6-dae9-47c0-86a2-example</requestId>
|
||||
<natGatewaySet>
|
||||
{% for nat_gateway in nat_gateways %}
|
||||
<item>
|
||||
<subnetId>{{ nat_gateway.subnet_id }}</subnetId>
|
||||
<natGatewayAddressSet>
|
||||
<item>
|
||||
<networkInterfaceId>{{ nat_gateway.network_interface_id }}</networkInterfaceId>
|
||||
<publicIp>{{ nat_gateway.public_ip }}</publicIp>
|
||||
<allocationId>{{ nat_gateway.allocation_id }}</allocationId>
|
||||
<privateIp>{{ nat_gateway.private_ip }}</privateIp>
|
||||
</item>
|
||||
</natGatewayAddressSet>
|
||||
<createTime>{{ nat_gateway.create_time }}</createTime>
|
||||
<vpcId>{{ nat_gateway.vpc_id }}</vpcId>
|
||||
<natGatewayId>{{ nat_gateway.id }}</natGatewayId>
|
||||
<state>{{ nat_gateway.state }}</state>
|
||||
</item>
|
||||
{% endfor %}
|
||||
</natGatewaySet>
|
||||
</DescribeNatGatewaysResponse>
|
||||
"""
|
||||
|
||||
CREATE_NAT_GATEWAY = """<CreateNatGatewayResponse xmlns="http://ec2.amazonaws.com/doc/2015-10-01/">
|
||||
<requestId>1b74dc5c-bcda-403f-867d-example</requestId>
|
||||
<natGateway>
|
||||
<subnetId>{{ nat_gateway.subnet_id }}</subnetId>
|
||||
<natGatewayAddressSet>
|
||||
<item>
|
||||
<allocationId>{{ nat_gateway.allocation_id }}</allocationId>
|
||||
</item>
|
||||
</natGatewayAddressSet>
|
||||
<createTime>{{ nat_gateway.create_time }}</createTime>
|
||||
<vpcId>{{ nat_gateway.vpc_id }}</vpcId>
|
||||
<natGatewayId>{{ nat_gateway.id }}</natGatewayId>
|
||||
<state>{{ nat_gateway.state }}</state>
|
||||
</natGateway>
|
||||
</CreateNatGatewayResponse>
|
||||
"""
|
||||
|
||||
|
||||
DELETE_NAT_GATEWAY_RESPONSE = """<DeleteNatGatewayResponse xmlns="http://ec2.amazonaws.com/doc/2015-10-01/">
|
||||
<requestId>741fc8ab-6ebe-452b-b92b-example</requestId>
|
||||
<natGatewayId>{{ nat_gateway.id }}</natGatewayId>
|
||||
</DeleteNatGatewayResponse>"""
|
@ -9,6 +9,7 @@ EC2_RESOURCE_TO_PREFIX = {
|
||||
'image': 'ami',
|
||||
'instance': 'i',
|
||||
'internet-gateway': 'igw',
|
||||
'nat-gateway': 'nat',
|
||||
'network-acl': 'acl',
|
||||
'network-acl-subnet-assoc': 'aclassoc',
|
||||
'network-interface': 'eni',
|
||||
@ -33,8 +34,7 @@ EC2_RESOURCE_TO_PREFIX = {
|
||||
EC2_PREFIX_TO_RESOURCE = dict((v, k) for (k, v) in EC2_RESOURCE_TO_PREFIX.items())
|
||||
|
||||
|
||||
def random_id(prefix=''):
|
||||
size = 8
|
||||
def random_id(prefix='', size=8):
|
||||
chars = list(range(10)) + ['a', 'b', 'c', 'd', 'e', 'f']
|
||||
|
||||
resource_id = ''.join(six.text_type(random.choice(chars)) for x in range(size))
|
||||
@ -133,6 +133,10 @@ def random_eni_attach_id():
|
||||
return random_id(prefix=EC2_RESOURCE_TO_PREFIX['network-interface-attachment'])
|
||||
|
||||
|
||||
def random_nat_gateway_id():
|
||||
return random_id(prefix=EC2_RESOURCE_TO_PREFIX['nat-gateway'], size=17)
|
||||
|
||||
|
||||
def random_public_ip():
|
||||
return '54.214.{0}.{1}'.format(random.choice(range(255)),
|
||||
random.choice(range(255)))
|
||||
|
100
tests/test_ec2/test_nat_gateway.py
Normal file
100
tests/test_ec2/test_nat_gateway.py
Normal file
@ -0,0 +1,100 @@
|
||||
from __future__ import unicode_literals
|
||||
import boto3
|
||||
import sure # noqa
|
||||
from moto import mock_ec2
|
||||
|
||||
|
||||
@mock_ec2
|
||||
def test_describe_nat_gateways():
|
||||
conn = boto3.client('ec2', 'us-east-1')
|
||||
|
||||
response = conn.describe_nat_gateways()
|
||||
|
||||
response['NatGateways'].should.have.length_of(0)
|
||||
|
||||
|
||||
@mock_ec2
|
||||
def test_create_nat_gateway():
|
||||
conn = boto3.client('ec2', 'us-east-1')
|
||||
vpc = conn.create_vpc(CidrBlock='10.0.0.0/16')
|
||||
vpc_id = vpc['Vpc']['VpcId']
|
||||
subnet = conn.create_subnet(
|
||||
VpcId=vpc_id,
|
||||
CidrBlock='10.0.1.0/27',
|
||||
AvailabilityZone='us-east-1a',
|
||||
)
|
||||
allocation_id = conn.allocate_address(Domain='vpc')['AllocationId']
|
||||
subnet_id = subnet['Subnet']['SubnetId']
|
||||
|
||||
response = conn.create_nat_gateway(
|
||||
SubnetId=subnet_id,
|
||||
AllocationId=allocation_id,
|
||||
)
|
||||
|
||||
response['NatGateway']['VpcId'].should.equal(vpc_id)
|
||||
response['NatGateway']['SubnetId'].should.equal(subnet_id)
|
||||
response['NatGateway']['State'].should.equal('available')
|
||||
|
||||
|
||||
@mock_ec2
|
||||
def test_delete_nat_gateway():
|
||||
conn = boto3.client('ec2', 'us-east-1')
|
||||
vpc = conn.create_vpc(CidrBlock='10.0.0.0/16')
|
||||
vpc_id = vpc['Vpc']['VpcId']
|
||||
subnet = conn.create_subnet(
|
||||
VpcId=vpc_id,
|
||||
CidrBlock='10.0.1.0/27',
|
||||
AvailabilityZone='us-east-1a',
|
||||
)
|
||||
allocation_id = conn.allocate_address(Domain='vpc')['AllocationId']
|
||||
subnet_id = subnet['Subnet']['SubnetId']
|
||||
|
||||
nat_gateway = conn.create_nat_gateway(
|
||||
SubnetId=subnet_id,
|
||||
AllocationId=allocation_id,
|
||||
)
|
||||
nat_gateway_id = nat_gateway['NatGateway']['NatGatewayId']
|
||||
response = conn.delete_nat_gateway(NatGatewayId=nat_gateway_id)
|
||||
|
||||
response.should.equal({
|
||||
'NatGatewayId': nat_gateway_id,
|
||||
'ResponseMetadata': {
|
||||
'HTTPStatusCode': 200,
|
||||
'RequestId': '741fc8ab-6ebe-452b-b92b-example'
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@mock_ec2
|
||||
def test_create_and_describe_nat_gateway():
|
||||
conn = boto3.client('ec2', 'us-east-1')
|
||||
vpc = conn.create_vpc(CidrBlock='10.0.0.0/16')
|
||||
vpc_id = vpc['Vpc']['VpcId']
|
||||
subnet = conn.create_subnet(
|
||||
VpcId=vpc_id,
|
||||
CidrBlock='10.0.1.0/27',
|
||||
AvailabilityZone='us-east-1a',
|
||||
)
|
||||
allocation_id = conn.allocate_address(Domain='vpc')['AllocationId']
|
||||
subnet_id = subnet['Subnet']['SubnetId']
|
||||
|
||||
create_response = conn.create_nat_gateway(
|
||||
SubnetId=subnet_id,
|
||||
AllocationId=allocation_id,
|
||||
)
|
||||
nat_gateway_id = create_response['NatGateway']['NatGatewayId']
|
||||
describe_response = conn.describe_nat_gateways()
|
||||
|
||||
enis = conn.describe_network_interfaces()['NetworkInterfaces']
|
||||
eni_id = enis[0]['NetworkInterfaceId']
|
||||
public_ip = conn.describe_addresses(AllocationIds=[allocation_id])['Addresses'][0]['PublicIp']
|
||||
|
||||
describe_response['NatGateways'].should.have.length_of(1)
|
||||
describe_response['NatGateways'][0]['NatGatewayId'].should.equal(nat_gateway_id)
|
||||
describe_response['NatGateways'][0]['State'].should.equal('available')
|
||||
describe_response['NatGateways'][0]['SubnetId'].should.equal(subnet_id)
|
||||
describe_response['NatGateways'][0]['VpcId'].should.equal(vpc_id)
|
||||
describe_response['NatGateways'][0]['NatGatewayAddresses'][0]['AllocationId'].should.equal(allocation_id)
|
||||
describe_response['NatGateways'][0]['NatGatewayAddresses'][0]['NetworkInterfaceId'].should.equal(eni_id)
|
||||
assert describe_response['NatGateways'][0]['NatGatewayAddresses'][0]['PrivateIp'].startswith('10.')
|
||||
describe_response['NatGateways'][0]['NatGatewayAddresses'][0]['PublicIp'].should.equal(public_ip)
|
Loading…
Reference in New Issue
Block a user