Merge pull request #156 from DreadPirateShawn/VPCPeeringConnections
Add support for VPCPeeringConnections.
This commit is contained in:
commit
9d06ccf0cd
@ -4,6 +4,7 @@ python:
|
||||
- 2.7
|
||||
env:
|
||||
matrix:
|
||||
- BOTO_VERSION=2.32.1
|
||||
- BOTO_VERSION=2.31.1
|
||||
- BOTO_VERSION=2.25.0
|
||||
- BOTO_VERSION=2.19.0
|
||||
|
@ -37,6 +37,22 @@ class InvalidVPCIdError(EC2ClientError):
|
||||
.format(vpc_id))
|
||||
|
||||
|
||||
class InvalidVPCPeeringConnectionIdError(EC2ClientError):
|
||||
def __init__(self, vpc_peering_connection_id):
|
||||
super(InvalidVPCPeeringConnectionIdError, self).__init__(
|
||||
"InvalidVpcPeeringConnectionId.NotFound",
|
||||
"VpcPeeringConnectionID {0} does not exist."
|
||||
.format(vpc_peering_connection_id))
|
||||
|
||||
|
||||
class InvalidVPCPeeringConnectionStateTransitionError(EC2ClientError):
|
||||
def __init__(self, vpc_peering_connection_id):
|
||||
super(InvalidVPCPeeringConnectionStateTransitionError, self).__init__(
|
||||
"InvalidStateTransition",
|
||||
"VpcPeeringConnectionID {0} is not in the correct state for the request."
|
||||
.format(vpc_peering_connection_id))
|
||||
|
||||
|
||||
class InvalidParameterValueError(EC2ClientError):
|
||||
def __init__(self, parameter_value):
|
||||
super(InvalidParameterValueError, self).__init__(
|
||||
|
@ -15,7 +15,9 @@ from .exceptions import (
|
||||
InvalidInternetGatewayIDError,
|
||||
GatewayNotAttachedError,
|
||||
ResourceAlreadyAssociatedError,
|
||||
InvalidVPCIdError
|
||||
InvalidVPCIdError,
|
||||
InvalidVPCPeeringConnectionIdError,
|
||||
InvalidVPCPeeringConnectionStateTransitionError
|
||||
)
|
||||
from .utils import (
|
||||
random_ami_id,
|
||||
@ -35,6 +37,7 @@ from .utils import (
|
||||
random_subnet_id,
|
||||
random_volume_id,
|
||||
random_vpc_id,
|
||||
random_vpc_peering_connection_id,
|
||||
)
|
||||
|
||||
|
||||
@ -715,6 +718,89 @@ class VPCBackend(object):
|
||||
return vpc
|
||||
|
||||
|
||||
class VPCPeeringConnectionStatus(object):
|
||||
def __init__(self, code='initiating-request', message=''):
|
||||
self.code = code
|
||||
self.message = message
|
||||
|
||||
def initiating(self):
|
||||
self.code = 'initiating-request'
|
||||
self.message = 'Initiating Request to {accepter ID}'
|
||||
|
||||
def pending(self):
|
||||
self.code = 'pending-acceptance'
|
||||
self.message = 'Pending Acceptance by {accepter ID}'
|
||||
|
||||
def accept(self):
|
||||
self.code = 'active'
|
||||
self.message = 'Active'
|
||||
|
||||
def reject(self):
|
||||
self.code = 'rejected'
|
||||
self.message = 'Inactive'
|
||||
|
||||
|
||||
class VPCPeeringConnection(TaggedEC2Instance):
|
||||
def __init__(self, vpc_pcx_id, vpc, peer_vpc):
|
||||
self.id = vpc_pcx_id
|
||||
self.vpc = vpc
|
||||
self.peer_vpc = peer_vpc
|
||||
self._status = VPCPeeringConnectionStatus()
|
||||
|
||||
@classmethod
|
||||
def create_from_cloudformation_json(cls, resource_name, cloudformation_json):
|
||||
properties = cloudformation_json['Properties']
|
||||
|
||||
vpc = self.get_vpc(properties['VpcId'])
|
||||
peer_vpc = self.get_vpc(properties['PeerVpcId'])
|
||||
|
||||
vpc_pcx = ec2_backend.create_vpc_peering_connection(vpc, peer_vpc)
|
||||
|
||||
return vpc_pcx
|
||||
|
||||
@property
|
||||
def physical_resource_id(self):
|
||||
return self.id
|
||||
|
||||
|
||||
class VPCPeeringConnectionBackend(object):
|
||||
def __init__(self):
|
||||
self.vpc_pcxs = {}
|
||||
super(VPCPeeringConnectionBackend, self).__init__()
|
||||
|
||||
def create_vpc_peering_connection(self, vpc, peer_vpc):
|
||||
vpc_pcx_id = random_vpc_peering_connection_id()
|
||||
vpc_pcx = VPCPeeringConnection(vpc_pcx_id, vpc, peer_vpc)
|
||||
vpc_pcx._status.pending()
|
||||
self.vpc_pcxs[vpc_pcx_id] = vpc_pcx
|
||||
return vpc_pcx
|
||||
|
||||
def get_all_vpc_peering_connections(self):
|
||||
return self.vpc_pcxs.values()
|
||||
|
||||
def get_vpc_peering_connection(self, vpc_pcx_id):
|
||||
if vpc_pcx_id not in self.vpc_pcxs:
|
||||
raise InvalidVPCPeeringConnectionIdError(vpc_pcx_id)
|
||||
return self.vpc_pcxs.get(vpc_pcx_id)
|
||||
|
||||
def delete_vpc_peering_connection(self, vpc_pcx_id):
|
||||
return self.vpc_pcxs.pop(vpc_pcx_id, None)
|
||||
|
||||
def accept_vpc_peering_connection(self, vpc_pcx_id):
|
||||
vpc_pcx = self.get_vpc_peering_connection(vpc_pcx_id)
|
||||
if vpc_pcx._status.code != 'pending-acceptance':
|
||||
raise InvalidVPCPeeringConnectionStateTransitionError(vpc_pcx.id)
|
||||
vpc_pcx._status.accept()
|
||||
return vpc_pcx
|
||||
|
||||
def reject_vpc_peering_connection(self, vpc_pcx_id):
|
||||
vpc_pcx = self.get_vpc_peering_connection(vpc_pcx_id)
|
||||
if vpc_pcx._status.code != 'pending-acceptance':
|
||||
raise InvalidVPCPeeringConnectionStateTransitionError(vpc_pcx.id)
|
||||
vpc_pcx._status.reject()
|
||||
return vpc_pcx
|
||||
|
||||
|
||||
class Subnet(TaggedEC2Instance):
|
||||
def __init__(self, subnet_id, vpc_id, cidr_block):
|
||||
self.id = subnet_id
|
||||
@ -1176,6 +1262,7 @@ class DHCPOptionsSetBackend(object):
|
||||
class EC2Backend(BaseBackend, InstanceBackend, TagBackend, AmiBackend,
|
||||
RegionsAndZonesBackend, SecurityGroupBackend, EBSBackend,
|
||||
VPCBackend, SubnetBackend, SubnetRouteTableAssociationBackend,
|
||||
VPCPeeringConnectionBackend,
|
||||
RouteTableBackend, RouteBackend, InternetGatewayBackend,
|
||||
VPCGatewayAttachmentBackend, SpotRequestBackend,
|
||||
ElasticAddressBackend, KeyPairBackend, DHCPOptionsSetBackend):
|
||||
|
@ -24,6 +24,7 @@ from .virtual_private_gateways import VirtualPrivateGateways
|
||||
from .vm_export import VMExport
|
||||
from .vm_import import VMImport
|
||||
from .vpcs import VPCs
|
||||
from .vpc_peering_connections import VPCPeeringConnections
|
||||
from .vpn_connections import VPNConnections
|
||||
from .windows import Windows
|
||||
|
||||
@ -55,6 +56,7 @@ class EC2Response(
|
||||
VMExport,
|
||||
VMImport,
|
||||
VPCs,
|
||||
VPCPeeringConnections,
|
||||
VPNConnections,
|
||||
Windows,
|
||||
):
|
||||
|
137
moto/ec2/responses/vpc_peering_connections.py
Normal file
137
moto/ec2/responses/vpc_peering_connections.py
Normal file
@ -0,0 +1,137 @@
|
||||
from jinja2 import Template
|
||||
|
||||
from moto.core.responses import BaseResponse
|
||||
from moto.ec2.models import ec2_backend
|
||||
|
||||
|
||||
class VPCPeeringConnections(BaseResponse):
|
||||
def create_vpc_peering_connection(self):
|
||||
vpc = ec2_backend.get_vpc(self.querystring.get('VpcId')[0])
|
||||
peer_vpc = ec2_backend.get_vpc(self.querystring.get('PeerVpcId')[0])
|
||||
vpc_pcx = ec2_backend.create_vpc_peering_connection(vpc, peer_vpc)
|
||||
template = Template(CREATE_VPC_PEERING_CONNECTION_RESPONSE)
|
||||
return template.render(vpc_pcx=vpc_pcx)
|
||||
|
||||
def delete_vpc_peering_connection(self):
|
||||
vpc_pcx_id = self.querystring.get('VpcPeeringConnectionId')[0]
|
||||
vpc_pcx = ec2_backend.delete_vpc_peering_connection(vpc_pcx_id)
|
||||
if vpc_pcx:
|
||||
template = Template(DELETE_VPC_PEERING_CONNECTION_RESPONSE)
|
||||
return template.render(vpc_pcx=vpc_pcx)
|
||||
else:
|
||||
return "", dict(status=404)
|
||||
|
||||
def describe_vpc_peering_connections(self):
|
||||
vpc_pcxs = ec2_backend.get_all_vpc_peering_connections()
|
||||
template = Template(DESCRIBE_VPC_PEERING_CONNECTIONS_RESPONSE)
|
||||
return template.render(vpc_pcxs=vpc_pcxs)
|
||||
|
||||
def accept_vpc_peering_connection(self):
|
||||
vpc_pcx_id = self.querystring.get('VpcPeeringConnectionId')[0]
|
||||
vpc_pcx = ec2_backend.accept_vpc_peering_connection(vpc_pcx_id)
|
||||
if vpc_pcx:
|
||||
template = Template(ACCEPT_VPC_PEERING_CONNECTION_RESPONSE)
|
||||
return template.render(vpc_pcx=vpc_pcx)
|
||||
else:
|
||||
return "", dict(status=404)
|
||||
|
||||
def reject_vpc_peering_connection(self):
|
||||
vpc_pcx_id = self.querystring.get('VpcPeeringConnectionId')[0]
|
||||
vpc_pcx = ec2_backend.reject_vpc_peering_connection(vpc_pcx_id)
|
||||
if vpc_pcx:
|
||||
template = Template(REJECT_VPC_PEERING_CONNECTION_RESPONSE)
|
||||
return template.render()
|
||||
else:
|
||||
return "", dict(status=404)
|
||||
|
||||
|
||||
CREATE_VPC_PEERING_CONNECTION_RESPONSE = """
|
||||
<CreateVpcPeeringConnectionResponse xmlns="http://ec2.amazonaws.com/doc/2014-06-15/">
|
||||
<requestId>7a62c49f-347e-4fc4-9331-6e8eEXAMPLE</requestId>
|
||||
<vpcPeeringConnection>
|
||||
<vpcPeeringConnectionId>{{ vpc_pcx.id }}</vpcPeeringConnectionId>
|
||||
<requesterVpcInfo>
|
||||
<ownerId>777788889999</ownerId>
|
||||
<vpcId>{{ vpc_pcx.vpc.id }}</vpcId>
|
||||
<cidrBlock>{{ vpc_pcx.vpc.cidr_block }}</cidrBlock>
|
||||
</requesterVpcInfo>
|
||||
<accepterVpcInfo>
|
||||
<ownerId>123456789012</ownerId>
|
||||
<vpcId>{{ vpc_pcx.peer_vpc.id }}</vpcId>
|
||||
</accepterVpcInfo>
|
||||
<status>
|
||||
<code>initiating-request</code>
|
||||
<message>Initiating request to {accepter ID}.</message>
|
||||
</status>
|
||||
<expirationTime>2014-02-18T14:37:25.000Z</expirationTime>
|
||||
<tagSet/>
|
||||
</vpcPeeringConnection>
|
||||
</CreateVpcPeeringConnectionResponse>
|
||||
"""
|
||||
|
||||
DESCRIBE_VPC_PEERING_CONNECTIONS_RESPONSE = """
|
||||
<DescribeVpcPeeringConnectionsResponse xmlns="http://ec2.amazonaws.com/doc/2014-06-15/">
|
||||
<requestId>7a62c49f-347e-4fc4-9331-6e8eEXAMPLE</requestId>
|
||||
<vpcPeeringConnectionSet>
|
||||
{% for vpc_pcx in vpc_pcxs %}
|
||||
<item>
|
||||
<vpcPeeringConnectionId>{{ vpc_pcx.id }}</vpcPeeringConnectionId>
|
||||
<requesterVpcInfo>
|
||||
<ownerId>777788889999</ownerId>
|
||||
<vpcId>{{ vpc_pcx.vpc.id }}</vpcId>
|
||||
<cidrBlock>{{ vpc_pcx.vpc.cidr_block }}</cidrBlock>
|
||||
</requesterVpcInfo>
|
||||
<accepterVpcInfo>
|
||||
<ownerId>111122223333</ownerId>
|
||||
<vpcId>{{ vpc_pcx.peer_vpc.id }}</vpcId>
|
||||
</accepterVpcInfo>
|
||||
<status>
|
||||
<code>{{ vpc_pcx._status.code }}</code>
|
||||
<message>{{ vpc_pcx._status.message }}</message>
|
||||
</status>
|
||||
<expirationTime>2014-02-17T16:00:50.000Z</expirationTime>
|
||||
<tagSet/>
|
||||
</item>
|
||||
{% endfor %}
|
||||
</vpcPeeringConnectionSet>
|
||||
</DescribeVpcPeeringConnectionsResponse>
|
||||
"""
|
||||
|
||||
DELETE_VPC_PEERING_CONNECTION_RESPONSE = """
|
||||
<DeleteVpcPeeringConnectionResponse xmlns="http://ec2.amazonaws.com/doc/2014-06-15/">
|
||||
<requestId>7a62c49f-347e-4fc4-9331-6e8eEXAMPLE</requestId>
|
||||
<return>true</return>
|
||||
</DeleteVpcPeeringConnectionResponse>
|
||||
"""
|
||||
|
||||
ACCEPT_VPC_PEERING_CONNECTION_RESPONSE = """
|
||||
<AcceptVpcPeeringConnectionResponse xmlns="http://ec2.amazonaws.com/doc/2014-06-15/">
|
||||
<requestId>7a62c49f-347e-4fc4-9331-6e8eEXAMPLE</requestId>
|
||||
<vpcPeeringConnection>
|
||||
<vpcPeeringConnectionId>{{ vpc_pcx.id }}</vpcPeeringConnectionId>
|
||||
<requesterVpcInfo>
|
||||
<ownerId>123456789012</ownerId>
|
||||
<vpcId>{{ vpc_pcx.vpc.id }}</vpcId>
|
||||
<cidrBlock>{{ vpc_pcx.vpc.cidr_block }}</cidrBlock>
|
||||
</requesterVpcInfo>
|
||||
<accepterVpcInfo>
|
||||
<ownerId>777788889999</ownerId>
|
||||
<vpcId>{{ vpc_pcx.peer_vpc.id }}</vpcId>
|
||||
<cidrBlock>{{ vpc_pcx.peer_vpc.cidr_block }}</cidrBlock>
|
||||
</accepterVpcInfo>
|
||||
<status>
|
||||
<code>{{ vpc_pcx._status.code }}</code>
|
||||
<message>{{ vpc_pcx._status.message }}</message>
|
||||
</status>
|
||||
<tagSet/>
|
||||
</vpcPeeringConnection>
|
||||
</AcceptVpcPeeringConnectionResponse>
|
||||
"""
|
||||
|
||||
REJECT_VPC_PEERING_CONNECTION_RESPONSE = """
|
||||
<RejectVpcPeeringConnectionResponse xmlns="http://ec2.amazonaws.com/doc/2014-06-15/">
|
||||
<requestId>7a62c49f-347e-4fc4-9331-6e8eEXAMPLE</requestId>
|
||||
<return>true</return>
|
||||
</RejectVpcPeeringConnectionResponse>
|
||||
"""
|
||||
|
@ -46,6 +46,10 @@ def random_vpc_id():
|
||||
return random_id(prefix='vpc')
|
||||
|
||||
|
||||
def random_vpc_peering_connection_id():
|
||||
return random_id(prefix='pcx')
|
||||
|
||||
|
||||
def random_eip_association_id():
|
||||
return random_id(prefix='eipassoc')
|
||||
|
||||
|
82
tests/test_ec2/test_vpc_peering.py
Normal file
82
tests/test_ec2/test_vpc_peering.py
Normal file
@ -0,0 +1,82 @@
|
||||
import boto
|
||||
from boto.exception import EC2ResponseError
|
||||
import sure # noqa
|
||||
|
||||
from moto import mock_ec2
|
||||
from tests.helpers import requires_boto_gte
|
||||
|
||||
|
||||
@requires_boto_gte("2.32.0")
|
||||
@mock_ec2
|
||||
def test_vpc_peering_connections():
|
||||
conn = boto.connect_vpc('the_key', 'the_secret')
|
||||
vpc = conn.create_vpc("10.0.0.0/16")
|
||||
peer_vpc = conn.create_vpc("11.0.0.0/16")
|
||||
|
||||
vpc_pcx = conn.create_vpc_peering_connection(vpc.id, peer_vpc.id)
|
||||
vpc_pcx._status.code.should.equal('initiating-request')
|
||||
|
||||
return vpc_pcx
|
||||
|
||||
|
||||
@requires_boto_gte("2.32.0")
|
||||
@mock_ec2
|
||||
def test_vpc_peering_connections_get_all():
|
||||
conn = boto.connect_vpc('the_key', 'the_secret')
|
||||
vpc_pcx = test_vpc_peering_connections()
|
||||
vpc_pcx._status.code.should.equal('initiating-request')
|
||||
|
||||
all_vpc_pcxs = conn.get_all_vpc_peering_connections()
|
||||
all_vpc_pcxs.should.have.length_of(1)
|
||||
all_vpc_pcxs[0]._status.code.should.equal('pending-acceptance')
|
||||
|
||||
|
||||
@requires_boto_gte("2.32.0")
|
||||
@mock_ec2
|
||||
def test_vpc_peering_connections_accept():
|
||||
conn = boto.connect_vpc('the_key', 'the_secret')
|
||||
vpc_pcx = test_vpc_peering_connections()
|
||||
|
||||
vpc_pcx = conn.accept_vpc_peering_connection(vpc_pcx.id)
|
||||
vpc_pcx._status.code.should.equal('active')
|
||||
|
||||
conn.reject_vpc_peering_connection.when.called_with(
|
||||
vpc_pcx.id).should.throw(EC2ResponseError)
|
||||
|
||||
all_vpc_pcxs = conn.get_all_vpc_peering_connections()
|
||||
all_vpc_pcxs.should.have.length_of(1)
|
||||
all_vpc_pcxs[0]._status.code.should.equal('active')
|
||||
|
||||
|
||||
@requires_boto_gte("2.32.0")
|
||||
@mock_ec2
|
||||
def test_vpc_peering_connections_reject():
|
||||
conn = boto.connect_vpc('the_key', 'the_secret')
|
||||
vpc_pcx = test_vpc_peering_connections()
|
||||
|
||||
verdict = conn.reject_vpc_peering_connection(vpc_pcx.id)
|
||||
verdict.should.equal(True)
|
||||
|
||||
conn.accept_vpc_peering_connection.when.called_with(
|
||||
vpc_pcx.id).should.throw(EC2ResponseError)
|
||||
|
||||
all_vpc_pcxs = conn.get_all_vpc_peering_connections()
|
||||
all_vpc_pcxs.should.have.length_of(1)
|
||||
all_vpc_pcxs[0]._status.code.should.equal('rejected')
|
||||
|
||||
|
||||
@requires_boto_gte("2.32.1")
|
||||
@mock_ec2
|
||||
def test_vpc_peering_connections_delete():
|
||||
conn = boto.connect_vpc('the_key', 'the_secret')
|
||||
vpc_pcx = test_vpc_peering_connections()
|
||||
|
||||
verdict = vpc_pcx.delete()
|
||||
verdict.should.equal(True)
|
||||
|
||||
all_vpc_pcxs = conn.get_all_vpc_peering_connections()
|
||||
all_vpc_pcxs.should.have.length_of(0)
|
||||
|
||||
conn.delete_vpc_peering_connection.when.called_with(
|
||||
"pcx-1234abcd").should.throw(EC2ResponseError)
|
||||
|
Loading…
Reference in New Issue
Block a user