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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user