Fix #1830 Add support for cross-region VPC peering
Add a class level store in models/VPCBackend of ec2 for saving vpcs of all regions info. Any instance can correctly find vpc in another region when connecting vpc of cross-region or vpc of same region. Modify vpc_peering_connections in ec2/responses to handle vpc peering of same region or cross region. Update vpc_peering_connections response template content to latest (2016-11-15) . Add vpc cross region peering successful test case. Add vpc cross region peering fail test case. Related: https://github.com/spulec/moto/issues/1830 Reference CreateVpcPeeringConnection Sample Response https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_CreateVpcPeeringConnection.html
This commit is contained in:
parent
e8c65d3d85
commit
cedb89dc3b
@ -13,6 +13,7 @@ from pkg_resources import resource_filename
|
|||||||
import boto.ec2
|
import boto.ec2
|
||||||
|
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
|
import weakref
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from boto.ec2.instance import Instance as BotoInstance, Reservation
|
from boto.ec2.instance import Instance as BotoInstance, Reservation
|
||||||
from boto.ec2.blockdevicemapping import BlockDeviceMapping, BlockDeviceType
|
from boto.ec2.blockdevicemapping import BlockDeviceMapping, BlockDeviceType
|
||||||
@ -2115,10 +2116,20 @@ class VPC(TaggedEC2Resource):
|
|||||||
|
|
||||||
|
|
||||||
class VPCBackend(object):
|
class VPCBackend(object):
|
||||||
|
__refs__ = defaultdict(list)
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.vpcs = {}
|
self.vpcs = {}
|
||||||
|
self.__refs__[self.__class__].append(weakref.ref(self))
|
||||||
super(VPCBackend, self).__init__()
|
super(VPCBackend, self).__init__()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_instances(cls):
|
||||||
|
for inst_ref in cls.__refs__[cls]:
|
||||||
|
inst = inst_ref()
|
||||||
|
if inst is not None:
|
||||||
|
yield inst
|
||||||
|
|
||||||
def create_vpc(self, cidr_block, instance_tenancy='default', amazon_provided_ipv6_cidr_block=False):
|
def create_vpc(self, cidr_block, instance_tenancy='default', amazon_provided_ipv6_cidr_block=False):
|
||||||
vpc_id = random_vpc_id()
|
vpc_id = random_vpc_id()
|
||||||
vpc = VPC(self, vpc_id, cidr_block, len(self.vpcs) == 0, instance_tenancy, amazon_provided_ipv6_cidr_block)
|
vpc = VPC(self, vpc_id, cidr_block, len(self.vpcs) == 0, instance_tenancy, amazon_provided_ipv6_cidr_block)
|
||||||
@ -2142,6 +2153,13 @@ class VPCBackend(object):
|
|||||||
raise InvalidVPCIdError(vpc_id)
|
raise InvalidVPCIdError(vpc_id)
|
||||||
return self.vpcs.get(vpc_id)
|
return self.vpcs.get(vpc_id)
|
||||||
|
|
||||||
|
# get vpc by vpc id and aws region
|
||||||
|
def get_cross_vpc(self, vpc_id, peer_region):
|
||||||
|
for vpcs in self.get_instances():
|
||||||
|
if vpcs.region_name == peer_region:
|
||||||
|
match_vpc = vpcs.get_vpc(vpc_id)
|
||||||
|
return match_vpc
|
||||||
|
|
||||||
def get_all_vpcs(self, vpc_ids=None, filters=None):
|
def get_all_vpcs(self, vpc_ids=None, filters=None):
|
||||||
matches = self.vpcs.values()
|
matches = self.vpcs.values()
|
||||||
if vpc_ids:
|
if vpc_ids:
|
||||||
|
@ -5,8 +5,12 @@ from moto.core.responses import BaseResponse
|
|||||||
class VPCPeeringConnections(BaseResponse):
|
class VPCPeeringConnections(BaseResponse):
|
||||||
|
|
||||||
def create_vpc_peering_connection(self):
|
def create_vpc_peering_connection(self):
|
||||||
|
peer_region = self._get_param('PeerRegion')
|
||||||
|
if peer_region == self.region or peer_region is None:
|
||||||
|
peer_vpc = self.ec2_backend.get_vpc(self._get_param('PeerVpcId'))
|
||||||
|
else:
|
||||||
|
peer_vpc = self.ec2_backend.get_cross_vpc(self._get_param('PeerVpcId'), peer_region)
|
||||||
vpc = self.ec2_backend.get_vpc(self._get_param('VpcId'))
|
vpc = self.ec2_backend.get_vpc(self._get_param('VpcId'))
|
||||||
peer_vpc = self.ec2_backend.get_vpc(self._get_param('PeerVpcId'))
|
|
||||||
vpc_pcx = self.ec2_backend.create_vpc_peering_connection(vpc, peer_vpc)
|
vpc_pcx = self.ec2_backend.create_vpc_peering_connection(vpc, peer_vpc)
|
||||||
template = self.response_template(
|
template = self.response_template(
|
||||||
CREATE_VPC_PEERING_CONNECTION_RESPONSE)
|
CREATE_VPC_PEERING_CONNECTION_RESPONSE)
|
||||||
@ -41,26 +45,31 @@ class VPCPeeringConnections(BaseResponse):
|
|||||||
|
|
||||||
|
|
||||||
CREATE_VPC_PEERING_CONNECTION_RESPONSE = """
|
CREATE_VPC_PEERING_CONNECTION_RESPONSE = """
|
||||||
<CreateVpcPeeringConnectionResponse xmlns="http://ec2.amazonaws.com/doc/2013-10-15/">
|
<CreateVpcPeeringConnectionResponse xmlns="http://ec2.amazonaws.com/doc/2016-11-15/">
|
||||||
<requestId>7a62c49f-347e-4fc4-9331-6e8eEXAMPLE</requestId>
|
<requestId>7a62c49f-347e-4fc4-9331-6e8eEXAMPLE</requestId>
|
||||||
<vpcPeeringConnection>
|
<vpcPeeringConnection>
|
||||||
<vpcPeeringConnectionId>{{ vpc_pcx.id }}</vpcPeeringConnectionId>
|
<vpcPeeringConnectionId>{{ vpc_pcx.id }}</vpcPeeringConnectionId>
|
||||||
<requesterVpcInfo>
|
<requesterVpcInfo>
|
||||||
<ownerId>777788889999</ownerId>
|
<ownerId>777788889999</ownerId>
|
||||||
<vpcId>{{ vpc_pcx.vpc.id }}</vpcId>
|
<vpcId>{{ vpc_pcx.vpc.id }}</vpcId>
|
||||||
<cidrBlock>{{ vpc_pcx.vpc.cidr_block }}</cidrBlock>
|
<cidrBlock>{{ vpc_pcx.vpc.cidr_block }}</cidrBlock>
|
||||||
|
<peeringOptions>
|
||||||
|
<allowEgressFromLocalClassicLinkToRemoteVpc>false</allowEgressFromLocalClassicLinkToRemoteVpc>
|
||||||
|
<allowEgressFromLocalVpcToRemoteClassicLink>false</allowEgressFromLocalVpcToRemoteClassicLink>
|
||||||
|
<allowDnsResolutionFromRemoteVpc>false</allowDnsResolutionFromRemoteVpc>
|
||||||
|
</peeringOptions>
|
||||||
</requesterVpcInfo>
|
</requesterVpcInfo>
|
||||||
<accepterVpcInfo>
|
<accepterVpcInfo>
|
||||||
<ownerId>123456789012</ownerId>
|
<ownerId>123456789012</ownerId>
|
||||||
<vpcId>{{ vpc_pcx.peer_vpc.id }}</vpcId>
|
<vpcId>{{ vpc_pcx.peer_vpc.id }}</vpcId>
|
||||||
</accepterVpcInfo>
|
</accepterVpcInfo>
|
||||||
<status>
|
<status>
|
||||||
<code>initiating-request</code>
|
<code>initiating-request</code>
|
||||||
<message>Initiating request to {accepter ID}.</message>
|
<message>Initiating Request to {accepter ID}</message>
|
||||||
</status>
|
</status>
|
||||||
<expirationTime>2014-02-18T14:37:25.000Z</expirationTime>
|
<expirationTime>2014-02-18T14:37:25.000Z</expirationTime>
|
||||||
<tagSet/>
|
<tagSet/>
|
||||||
</vpcPeeringConnection>
|
</vpcPeeringConnection>
|
||||||
</CreateVpcPeeringConnectionResponse>
|
</CreateVpcPeeringConnectionResponse>
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@ -2,12 +2,15 @@ from __future__ import unicode_literals
|
|||||||
# Ensure 'assert_raises' context manager support for Python 2.6
|
# Ensure 'assert_raises' context manager support for Python 2.6
|
||||||
import tests.backport_assert_raises
|
import tests.backport_assert_raises
|
||||||
from nose.tools import assert_raises
|
from nose.tools import assert_raises
|
||||||
|
from moto.ec2.exceptions import EC2ClientError
|
||||||
|
from botocore.exceptions import ClientError
|
||||||
|
|
||||||
|
import boto3
|
||||||
import boto
|
import boto
|
||||||
from boto.exception import EC2ResponseError
|
from boto.exception import EC2ResponseError
|
||||||
import sure # noqa
|
import sure # noqa
|
||||||
|
|
||||||
from moto import mock_ec2_deprecated
|
from moto import mock_ec2, mock_ec2_deprecated
|
||||||
from tests.helpers import requires_boto_gte
|
from tests.helpers import requires_boto_gte
|
||||||
|
|
||||||
|
|
||||||
@ -93,3 +96,37 @@ def test_vpc_peering_connections_delete():
|
|||||||
cm.exception.code.should.equal('InvalidVpcPeeringConnectionId.NotFound')
|
cm.exception.code.should.equal('InvalidVpcPeeringConnectionId.NotFound')
|
||||||
cm.exception.status.should.equal(400)
|
cm.exception.status.should.equal(400)
|
||||||
cm.exception.request_id.should_not.be.none
|
cm.exception.request_id.should_not.be.none
|
||||||
|
|
||||||
|
|
||||||
|
@mock_ec2
|
||||||
|
def test_vpc_peering_connections_cross_region():
|
||||||
|
# create vpc in us-west-1 and ap-northeast-1
|
||||||
|
ec2_usw1 = boto3.resource('ec2', region_name='us-west-1')
|
||||||
|
vpc_usw1 = ec2_usw1.create_vpc(CidrBlock='10.90.0.0/16')
|
||||||
|
ec2_apn1 = boto3.resource('ec2', region_name='ap-northeast-1')
|
||||||
|
vpc_apn1 = ec2_apn1.create_vpc(CidrBlock='10.20.0.0/16')
|
||||||
|
# create peering
|
||||||
|
vpc_pcx = ec2_usw1.create_vpc_peering_connection(
|
||||||
|
VpcId=vpc_usw1.id,
|
||||||
|
PeerVpcId=vpc_apn1.id,
|
||||||
|
PeerRegion='ap-northeast-1',
|
||||||
|
)
|
||||||
|
vpc_pcx.status['Code'].should.equal('initiating-request')
|
||||||
|
vpc_pcx.requester_vpc.id.should.equal(vpc_usw1.id)
|
||||||
|
vpc_pcx.accepter_vpc.id.should.equal(vpc_apn1.id)
|
||||||
|
|
||||||
|
|
||||||
|
@mock_ec2
|
||||||
|
def test_vpc_peering_connections_cross_region_fail():
|
||||||
|
# create vpc in us-west-1 and ap-northeast-1
|
||||||
|
ec2_usw1 = boto3.resource('ec2', region_name='us-west-1')
|
||||||
|
vpc_usw1 = ec2_usw1.create_vpc(CidrBlock='10.90.0.0/16')
|
||||||
|
ec2_apn1 = boto3.resource('ec2', region_name='ap-northeast-1')
|
||||||
|
vpc_apn1 = ec2_apn1.create_vpc(CidrBlock='10.20.0.0/16')
|
||||||
|
# create peering wrong region with no vpc
|
||||||
|
with assert_raises(ClientError) as cm:
|
||||||
|
ec2_usw1.create_vpc_peering_connection(
|
||||||
|
VpcId=vpc_usw1.id,
|
||||||
|
PeerVpcId=vpc_apn1.id,
|
||||||
|
PeerRegion='ap-northeast-2')
|
||||||
|
cm.exception.response['Error']['Code'].should.equal('InvalidVpcID.NotFound')
|
||||||
|
Loading…
Reference in New Issue
Block a user