diff --git a/moto/ec2/exceptions.py b/moto/ec2/exceptions.py index 780ed933c041..9163cca1fdab 100644 --- a/moto/ec2/exceptions.py +++ b/moto/ec2/exceptions.py @@ -632,21 +632,23 @@ def __init__(self, cidr_block: str): super().__init__("InvalidVpc.Range", f"The CIDR '{cidr_block}' is invalid.") -# accept exception +# Raised when attempting to accept a VPC peering connection request in own account but in the requester region class OperationNotPermitted2(EC2ClientError): def __init__(self, client_region: str, pcx_id: str, acceptor_region: str): super().__init__( "OperationNotPermitted", - f"Incorrect region ({client_region}) specified for this request.VPC peering connection {pcx_id} must be accepted in region {acceptor_region}", + f"Incorrect region ({client_region}) specified for this request. " + f"VPC peering connection {pcx_id} must be accepted in region {acceptor_region}", ) -# reject exception +# Raised when attempting to reject a VPC peering connection request in own account but in the requester region class OperationNotPermitted3(EC2ClientError): def __init__(self, client_region: str, pcx_id: str, acceptor_region: str): super().__init__( "OperationNotPermitted", - f"Incorrect region ({client_region}) specified for this request.VPC peering connection {pcx_id} must be accepted or rejected in region {acceptor_region}", + f"Incorrect region ({client_region}) specified for this request. " + f"VPC peering connection {pcx_id} must be accepted or rejected in region {acceptor_region}", ) @@ -658,6 +660,15 @@ def __init__(self, instance_id: str): ) +# Raised when attempting to accept or reject a VPC peering connection request for a VPC not belonging to self +class OperationNotPermitted5(EC2ClientError): + def __init__(self, account_id: str, pcx_id: str, operation: str): + super().__init__( + "OperationNotPermitted", + f"User ({account_id}) cannot {operation} peering {pcx_id}", + ) + + class InvalidLaunchTemplateNameAlreadyExistsError(EC2ClientError): def __init__(self) -> None: super().__init__( diff --git a/moto/ec2/models/transit_gateway_attachments.py b/moto/ec2/models/transit_gateway_attachments.py index 2ed80b05d880..0bc535085e47 100644 --- a/moto/ec2/models/transit_gateway_attachments.py +++ b/moto/ec2/models/transit_gateway_attachments.py @@ -95,7 +95,7 @@ def __init__( "region": region_name, "transitGatewayId": transit_gateway_id, } - self.status = PeeringConnectionStatus() + self.status = PeeringConnectionStatus(accepter_id=peer_account_id) class TransitGatewayAttachmentBackend: @@ -342,5 +342,5 @@ def delete_transit_gateway_peering_attachment( transit_gateway_attachment_id ] transit_gateway_attachment.state = "deleted" - transit_gateway_attachment.status.deleted() # type: ignore[attr-defined] + transit_gateway_attachment.status.deleted(deleter_id=self.account_id) # type: ignore[attr-defined] return transit_gateway_attachment diff --git a/moto/ec2/models/vpc_peering_connections.py b/moto/ec2/models/vpc_peering_connections.py index 0498080fdd27..075f526fe9ae 100644 --- a/moto/ec2/models/vpc_peering_connections.py +++ b/moto/ec2/models/vpc_peering_connections.py @@ -7,6 +7,7 @@ InvalidVPCPeeringConnectionStateTransitionError, OperationNotPermitted2, OperationNotPermitted3, + OperationNotPermitted5, ) from .core import TaggedEC2Resource from .vpcs import VPC @@ -14,21 +15,24 @@ class PeeringConnectionStatus: - def __init__(self, code: str = "initiating-request", message: str = ""): + def __init__( + self, accepter_id: str, code: str = "initiating-request", message: str = "" + ): + self.accepter_id = accepter_id self.code = code self.message = message - def deleted(self) -> None: + def deleted(self, deleter_id: str) -> None: self.code = "deleted" - self.message = "Deleted by {deleter ID}" + self.message = f"Deleted by {deleter_id}" def initiating(self) -> None: self.code = "initiating-request" - self.message = "Initiating Request to {accepter ID}" + self.message = f"Initiating Request to {self.accepter_id}" def pending(self) -> None: self.code = "pending-acceptance" - self.message = "Pending Acceptance by {accepter ID}" + self.message = f"Pending Acceptance by {self.accepter_id}" def accept(self) -> None: self.code = "active" @@ -61,7 +65,7 @@ def __init__( self.requester_options = self.DEFAULT_OPTIONS.copy() self.accepter_options = self.DEFAULT_OPTIONS.copy() self.add_tags(tags or {}) - self._status = PeeringConnectionStatus() + self._status = PeeringConnectionStatus(accepter_id=peer_vpc.owner_id) @staticmethod def cloudformation_name_type() -> str: @@ -79,7 +83,7 @@ def create_from_cloudformation_json( # type: ignore[misc] cloudformation_json: Any, account_id: str, region_name: str, - **kwargs: Any + **kwargs: Any, ) -> "VPCPeeringConnection": from ..models import ec2_backends @@ -120,11 +124,15 @@ def create_vpc_peering_connection( vpc_pcx = VPCPeeringConnection(self, vpc_pcx_id, vpc, peer_vpc, tags) vpc_pcx._status.pending() self.vpc_pcxs[vpc_pcx_id] = vpc_pcx - # insert cross region peering info - if vpc.ec2_backend.region_name != peer_vpc.ec2_backend.region_name: - for vpc_pcx_cx in peer_vpc.ec2_backend.get_vpc_pcx_refs(): - if vpc_pcx_cx.region_name == peer_vpc.ec2_backend.region_name: - vpc_pcx_cx.vpc_pcxs[vpc_pcx_id] = vpc_pcx + # insert cross-account/cross-region peering info + if vpc.owner_id != peer_vpc.owner_id or vpc.region != peer_vpc.region: + for backend in peer_vpc.ec2_backend.get_vpc_pcx_refs(): + if ( + backend.account_id == peer_vpc.owner_id + and backend.region_name == peer_vpc.region + ): + backend.vpc_pcxs[vpc_pcx_id] = vpc_pcx + return vpc_pcx def describe_vpc_peering_connections( @@ -142,16 +150,24 @@ def get_vpc_peering_connection(self, vpc_pcx_id: str) -> VPCPeeringConnection: def delete_vpc_peering_connection(self, vpc_pcx_id: str) -> VPCPeeringConnection: deleted = self.get_vpc_peering_connection(vpc_pcx_id) - deleted._status.deleted() + deleted._status.deleted(deleter_id=self.account_id) # type: ignore[attr-defined] return deleted def accept_vpc_peering_connection(self, vpc_pcx_id: str) -> VPCPeeringConnection: vpc_pcx = self.get_vpc_peering_connection(vpc_pcx_id) - # if cross region need accepter from another region - pcx_req_region = vpc_pcx.vpc.ec2_backend.region_name - pcx_acp_region = vpc_pcx.peer_vpc.ec2_backend.region_name + + # validate cross-account acceptance + req_account_id = vpc_pcx.vpc.owner_id + acp_account_id = vpc_pcx.peer_vpc.owner_id + if req_account_id != acp_account_id and self.account_id != acp_account_id: # type: ignore[attr-defined] + raise OperationNotPermitted5(self.account_id, vpc_pcx_id, "accept") # type: ignore[attr-defined] + + # validate cross-region acceptance + pcx_req_region = vpc_pcx.vpc.region + pcx_acp_region = vpc_pcx.peer_vpc.region if pcx_req_region != pcx_acp_region and self.region_name == pcx_req_region: # type: ignore[attr-defined] raise OperationNotPermitted2(self.region_name, vpc_pcx.id, pcx_acp_region) # type: ignore[attr-defined] + if vpc_pcx._status.code != "pending-acceptance": raise InvalidVPCPeeringConnectionStateTransitionError(vpc_pcx.id) vpc_pcx._status.accept() @@ -159,11 +175,19 @@ def accept_vpc_peering_connection(self, vpc_pcx_id: str) -> VPCPeeringConnection def reject_vpc_peering_connection(self, vpc_pcx_id: str) -> VPCPeeringConnection: vpc_pcx = self.get_vpc_peering_connection(vpc_pcx_id) - # if cross region need accepter from another region - pcx_req_region = vpc_pcx.vpc.ec2_backend.region_name - pcx_acp_region = vpc_pcx.peer_vpc.ec2_backend.region_name + + # validate cross-account rejection + req_account_id = vpc_pcx.vpc.owner_id + acp_account_id = vpc_pcx.peer_vpc.owner_id + if req_account_id != acp_account_id and self.account_id != acp_account_id: # type: ignore[attr-defined] + raise OperationNotPermitted5(self.account_id, vpc_pcx_id, "reject") # type: ignore[attr-defined] + + # validate cross-region acceptance + pcx_req_region = vpc_pcx.vpc.region + pcx_acp_region = vpc_pcx.peer_vpc.region if pcx_req_region != pcx_acp_region and self.region_name == pcx_req_region: # type: ignore[attr-defined] raise OperationNotPermitted3(self.region_name, vpc_pcx.id, pcx_acp_region) # type: ignore[attr-defined] + if vpc_pcx._status.code != "pending-acceptance": raise InvalidVPCPeeringConnectionStateTransitionError(vpc_pcx.id) vpc_pcx._status.reject() diff --git a/moto/ec2/models/vpcs.py b/moto/ec2/models/vpcs.py index badb85b196ad..73ae96e8b2f7 100644 --- a/moto/ec2/models/vpcs.py +++ b/moto/ec2/models/vpcs.py @@ -205,6 +205,10 @@ def __init__( def owner_id(self) -> str: return self.ec2_backend.account_id + @property + def region(self) -> str: + return self.ec2_backend.region_name + @staticmethod def cloudformation_name_type() -> str: return "" diff --git a/moto/ec2/responses/vpc_peering_connections.py b/moto/ec2/responses/vpc_peering_connections.py index a85b5fffce42..cae1f7df3569 100644 --- a/moto/ec2/responses/vpc_peering_connections.py +++ b/moto/ec2/responses/vpc_peering_connections.py @@ -3,21 +3,23 @@ class VPCPeeringConnections(EC2BaseResponse): def create_vpc_peering_connection(self) -> str: - peer_region = self._get_param("PeerRegion") tags = self._parse_tag_specification().get("vpc-peering-connection", {}) - if peer_region == self.region or peer_region is None: - peer_vpc = self.ec2_backend.get_vpc(self._get_param("PeerVpcId")) - else: - from moto.ec2.models import ec2_backends + account_id = self._get_param("PeerOwnerId") or self.current_account + region_name = self._get_param("PeerRegion") or self.region - peer_vpc = ec2_backends[self.current_account][peer_region].get_vpc( - self._get_param("PeerVpcId") - ) vpc = self.ec2_backend.get_vpc(self._get_param("VpcId")) + + # Peer VPC could belong to another account or region + from moto.ec2.models import ec2_backends + + peer_vpc = ec2_backends[account_id][region_name].get_vpc( + self._get_param("PeerVpcId") + ) + vpc_pcx = self.ec2_backend.create_vpc_peering_connection(vpc, peer_vpc, tags) template = self.response_template(CREATE_VPC_PEERING_CONNECTION_RESPONSE) - return template.render(account_id=self.current_account, vpc_pcx=vpc_pcx) + return template.render(vpc_pcx=vpc_pcx) def delete_vpc_peering_connection(self) -> str: vpc_pcx_id = self._get_param("VpcPeeringConnectionId") @@ -31,13 +33,13 @@ def describe_vpc_peering_connections(self) -> str: vpc_peering_ids=ids ) template = self.response_template(DESCRIBE_VPC_PEERING_CONNECTIONS_RESPONSE) - return template.render(account_id=self.current_account, vpc_pcxs=vpc_pcxs) + return template.render(vpc_pcxs=vpc_pcxs) def accept_vpc_peering_connection(self) -> str: vpc_pcx_id = self._get_param("VpcPeeringConnectionId") vpc_pcx = self.ec2_backend.accept_vpc_peering_connection(vpc_pcx_id) template = self.response_template(ACCEPT_VPC_PEERING_CONNECTION_RESPONSE) - return template.render(account_id=self.current_account, vpc_pcx=vpc_pcx) + return template.render(vpc_pcx=vpc_pcx) def reject_vpc_peering_connection(self) -> str: vpc_pcx_id = self._get_param("VpcPeeringConnectionId") @@ -68,39 +70,46 @@ def modify_vpc_peering_connection_options(self) -> str: 7a62c49f-347e-4fc4-9331-6e8eEXAMPLE - {{ vpc_pcx.id }} - - {{ account_id }} + {{ vpc_pcx.id }} + + {{ vpc_pcx.vpc.owner_id }} + {{ vpc_pcx.vpc.region }} {{ vpc_pcx.vpc.id }} {{ vpc_pcx.vpc.cidr_block }} + + {{ vpc_pcx.requester_options.AllowEgressFromLocalClassicLinkToRemoteVpc or '' }} {{ vpc_pcx.requester_options.AllowEgressFromLocalVpcToRemoteClassicLink or '' }} {{ vpc_pcx.requester_options.AllowDnsResolutionFromRemoteVpc or '' }} - - - {{ account_id }} - {{ vpc_pcx.peer_vpc.id }} - - {{ vpc_pcx.accepter_options.AllowEgressFromLocalClassicLinkToRemoteVpc or '' }} - {{ vpc_pcx.accepter_options.AllowEgressFromLocalVpcToRemoteClassicLink or '' }} - {{ vpc_pcx.accepter_options.AllowDnsResolutionFromRemoteVpc or '' }} - - - + + + {{ vpc_pcx.peer_vpc.owner_id }} + {{ vpc_pcx.peer_vpc.region }} + {{ vpc_pcx.peer_vpc.id }} + {{ vpc_pcx.peer_vpc.cidr_block }} + + + + {{ vpc_pcx.accepter_options.AllowEgressFromLocalClassicLinkToRemoteVpc or '' }} + {{ vpc_pcx.accepter_options.AllowEgressFromLocalVpcToRemoteClassicLink or '' }} + {{ vpc_pcx.accepter_options.AllowDnsResolutionFromRemoteVpc or '' }} + + + initiating-request - Initiating Request to {accepter ID} - - 2014-02-18T14:37:25.000Z - - {% for tag in vpc_pcx.get_tags() %} - - {{ tag.key }} - {{ tag.value }} - - {% endfor %} - + Initiating Request to {{ vpc_pcx.peer_vpc.owner_id }} + + 2014-02-18T14:37:25.000Z + + {% for tag in vpc_pcx.get_tags() %} + + {{ tag.key }} + {{ tag.value }} + + {% endfor %} + """ @@ -113,10 +122,12 @@ def modify_vpc_peering_connection_options(self) -> str: {{ vpc_pcx.id }} - {{ account_id }} + {{ vpc_pcx.vpc.owner_id }} + {{ vpc_pcx.vpc.region }} {{ vpc_pcx.vpc.id }} {{ vpc_pcx.vpc.cidr_block }} - {{ vpc_pcx.vpc.ec2_backend.region_name }} + + {{ vpc_pcx.requester_options.AllowEgressFromLocalClassicLinkToRemoteVpc or '' }} {{ vpc_pcx.requester_options.AllowEgressFromLocalVpcToRemoteClassicLink or '' }} @@ -124,10 +135,12 @@ def modify_vpc_peering_connection_options(self) -> str: - {{ account_id }} + {{ vpc_pcx.peer_vpc.owner_id }} + {{ vpc_pcx.peer_vpc.region }} {{ vpc_pcx.peer_vpc.id }} {{ vpc_pcx.peer_vpc.cidr_block }} - {{ vpc_pcx.peer_vpc.ec2_backend.region_name }} + + {{ vpc_pcx.accepter_options.AllowEgressFromLocalClassicLinkToRemoteVpc or '' }} {{ vpc_pcx.accepter_options.AllowEgressFromLocalVpcToRemoteClassicLink or '' }} @@ -165,21 +178,30 @@ def modify_vpc_peering_connection_options(self) -> str: {{ vpc_pcx.id }} - {{ account_id }} - {{ vpc_pcx.vpc.id }} - {{ vpc_pcx.vpc.cidr_block }} - {{ vpc_pcx.vpc.ec2_backend.region_name }} + {{ vpc_pcx.vpc.owner_id }} + {{ vpc_pcx.vpc.region }} + {{ vpc_pcx.vpc.id }} + {{ vpc_pcx.vpc.cidr_block }} + + + + {{ vpc_pcx.requester_options.AllowEgressFromLocalClassicLinkToRemoteVpc or '' }} + {{ vpc_pcx.requester_options.AllowEgressFromLocalVpcToRemoteClassicLink or '' }} + {{ vpc_pcx.requester_options.AllowDnsResolutionFromRemoteVpc or '' }} + - {{ account_id }} + {{ vpc_pcx.peer_vpc.owner_id }} + {{ vpc_pcx.peer_vpc.region }} {{ vpc_pcx.peer_vpc.id }} {{ vpc_pcx.peer_vpc.cidr_block }} + + {{ vpc_pcx.accepter_options.AllowEgressFromLocalClassicLinkToRemoteVpc or '' }} {{ vpc_pcx.accepter_options.AllowEgressFromLocalVpcToRemoteClassicLink or '' }} {{ vpc_pcx.accepter_options.AllowDnsResolutionFromRemoteVpc or '' }} - {{ vpc_pcx.peer_vpc.ec2_backend.region_name }} {{ vpc_pcx._status.code }} diff --git a/tests/test_ec2/test_vpc_peering.py b/tests/test_ec2/test_vpc_peering.py index 36da5588917d..4bf4bc0d23c8 100644 --- a/tests/test_ec2/test_vpc_peering.py +++ b/tests/test_ec2/test_vpc_peering.py @@ -1,9 +1,12 @@ +import os +from unittest import mock + import boto3 import pytest from botocore.exceptions import ClientError -from moto import mock_ec2 +from moto import mock_ec2, settings def create_vpx_pcx(ec2, client): @@ -40,6 +43,7 @@ def test_vpc_peering_connections_get_all_boto3(): if vpc_pcx["VpcPeeringConnectionId"] == vpc_pcx_id ][0] assert my_vpc_pcx["Status"]["Code"] == "pending-acceptance" + assert my_vpc_pcx["Status"]["Message"] == "Pending Acceptance by 123456789012" @mock_ec2 @@ -115,24 +119,84 @@ def test_vpc_peering_connections_delete_boto3(): @mock_ec2 -def test_vpc_peering_connections_cross_region(): +@pytest.mark.parametrize( + "account1,account2", + [ + pytest.param("111111111111", "111111111111", id="within account"), + pytest.param("111111111111", "222222222222", id="across accounts"), + ], +) +@pytest.mark.skipif( + settings.TEST_SERVER_MODE, reason="Cannot set account ID in server mode" +) +def test_vpc_peering_connections_cross_region(account1, account2): # 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") + with mock.patch.dict(os.environ, {"MOTO_ACCOUNT_ID": account1}): + ec2_usw1 = boto3.resource("ec2", region_name="us-west-1") + vpc_usw1 = ec2_usw1.create_vpc(CidrBlock="10.90.0.0/16") + + with mock.patch.dict(os.environ, {"MOTO_ACCOUNT_ID": account2}): + 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_usw1 = ec2_usw1.create_vpc_peering_connection( - VpcId=vpc_usw1.id, PeerVpcId=vpc_apn1.id, PeerRegion="ap-northeast-1" + with mock.patch.dict(os.environ, {"MOTO_ACCOUNT_ID": account1}): + ec2_usw1 = boto3.client("ec2", region_name="us-west-1") + vpc_pcx_usw1 = ec2_usw1.create_vpc_peering_connection( + VpcId=vpc_usw1.id, + PeerVpcId=vpc_apn1.id, + PeerRegion="ap-northeast-1", + PeerOwnerId=account2, + ) + + assert ( + vpc_pcx_usw1["VpcPeeringConnection"]["Status"]["Code"] == "initiating-request" + ) + assert ( + vpc_pcx_usw1["VpcPeeringConnection"]["Status"]["Message"] + == f"Initiating Request to {account2}" + ) + assert ( + vpc_pcx_usw1["VpcPeeringConnection"]["RequesterVpcInfo"]["VpcId"] == vpc_usw1.id + ) + assert ( + vpc_pcx_usw1["VpcPeeringConnection"]["RequesterVpcInfo"]["CidrBlock"] + == "10.90.0.0/16" + ) + assert ( + vpc_pcx_usw1["VpcPeeringConnection"]["RequesterVpcInfo"]["OwnerId"] == account1 + ) + assert ( + vpc_pcx_usw1["VpcPeeringConnection"]["RequesterVpcInfo"]["Region"] + == "us-west-1" + ) + assert ( + vpc_pcx_usw1["VpcPeeringConnection"]["AccepterVpcInfo"]["VpcId"] == vpc_apn1.id + ) + assert ( + vpc_pcx_usw1["VpcPeeringConnection"]["AccepterVpcInfo"]["CidrBlock"] + == "10.20.0.0/16" + ) + assert ( + vpc_pcx_usw1["VpcPeeringConnection"]["AccepterVpcInfo"]["OwnerId"] == account2 + ) + assert ( + vpc_pcx_usw1["VpcPeeringConnection"]["AccepterVpcInfo"]["Region"] + == "ap-northeast-1" ) - assert vpc_pcx_usw1.status["Code"] == "initiating-request" - assert vpc_pcx_usw1.requester_vpc.id == vpc_usw1.id - assert vpc_pcx_usw1.accepter_vpc.id == vpc_apn1.id + # test cross region vpc peering connection exist - vpc_pcx_apn1 = ec2_apn1.VpcPeeringConnection(vpc_pcx_usw1.id) - assert vpc_pcx_apn1.id == vpc_pcx_usw1.id - assert vpc_pcx_apn1.requester_vpc.id == vpc_usw1.id - assert vpc_pcx_apn1.accepter_vpc.id == vpc_apn1.id + with mock.patch.dict(os.environ, {"MOTO_ACCOUNT_ID": account2}): + vpc_pcx_apn1 = ec2_apn1.VpcPeeringConnection( + vpc_pcx_usw1["VpcPeeringConnection"]["VpcPeeringConnectionId"] + ) + assert ( + vpc_pcx_apn1.id + == vpc_pcx_usw1["VpcPeeringConnection"]["VpcPeeringConnectionId"] + ) + assert vpc_pcx_apn1.requester_vpc.id == vpc_usw1.id + assert vpc_pcx_apn1.accepter_vpc.id == vpc_apn1.id + # Quick check to verify the options have a default value accepter_options = vpc_pcx_apn1.accepter_vpc_info["PeeringOptions"] assert accepter_options["AllowDnsResolutionFromRemoteVpc"] is False @@ -145,28 +209,50 @@ def test_vpc_peering_connections_cross_region(): @mock_ec2 -def test_modify_vpc_peering_connections_accepter_only(): +@pytest.mark.parametrize( + "account1,account2", + [ + pytest.param("111111111111", "111111111111", id="within account"), + pytest.param("111111111111", "222222222222", id="across accounts"), + ], +) +@pytest.mark.skipif( + settings.TEST_SERVER_MODE, reason="Cannot set account ID in server mode" +) +def test_modify_vpc_peering_connections_accepter_only(account1, account2): # create vpc in us-west-1 and ap-northeast-1 - ec2_usw1 = boto3.resource("ec2", region_name="us-west-1") - client = boto3.client("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") + with mock.patch.dict(os.environ, {"MOTO_ACCOUNT_ID": account1}): + ec2_usw1 = boto3.resource("ec2", region_name="us-west-1") + client = boto3.client("ec2", region_name="us-west-1") + vpc_usw1 = ec2_usw1.create_vpc(CidrBlock="10.90.0.0/16") + + with mock.patch.dict(os.environ, {"MOTO_ACCOUNT_ID": account2}): + 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_usw1 = ec2_usw1.create_vpc_peering_connection( - VpcId=vpc_usw1.id, PeerVpcId=vpc_apn1.id, PeerRegion="ap-northeast-1" - ) - # - client.modify_vpc_peering_connection_options( - VpcPeeringConnectionId=vpc_pcx_usw1.id, - AccepterPeeringConnectionOptions={"AllowDnsResolutionFromRemoteVpc": True}, - ) - # Accepter options are different - vpc_pcx_usw1.reload() + with mock.patch.dict(os.environ, {"MOTO_ACCOUNT_ID": account1}): + vpc_pcx_usw1 = ec2_usw1.create_vpc_peering_connection( + VpcId=vpc_usw1.id, + PeerVpcId=vpc_apn1.id, + PeerRegion="ap-northeast-1", + PeerOwnerId=account2, + ) + + # modify peering connection options + client.modify_vpc_peering_connection_options( + VpcPeeringConnectionId=vpc_pcx_usw1.id, + AccepterPeeringConnectionOptions={"AllowDnsResolutionFromRemoteVpc": True}, + ) + + # Accepter options are different + vpc_pcx_usw1.reload() + accepter_options = vpc_pcx_usw1.accepter_vpc_info["PeeringOptions"] assert accepter_options["AllowDnsResolutionFromRemoteVpc"] is True assert accepter_options["AllowEgressFromLocalClassicLinkToRemoteVpc"] is False assert accepter_options["AllowEgressFromLocalVpcToRemoteClassicLink"] is False + # Requester options are untouched requester_options = vpc_pcx_usw1.requester_vpc_info["PeeringOptions"] assert requester_options["AllowDnsResolutionFromRemoteVpc"] is False @@ -175,30 +261,50 @@ def test_modify_vpc_peering_connections_accepter_only(): @mock_ec2 -def test_modify_vpc_peering_connections_requester_only(): +@pytest.mark.parametrize( + "account1,account2", + [ + pytest.param("111111111111", "111111111111", id="within account"), + pytest.param("111111111111", "222222222222", id="across accounts"), + ], +) +@pytest.mark.skipif( + settings.TEST_SERVER_MODE, reason="Cannot set account ID in server mode" +) +def test_modify_vpc_peering_connections_requester_only(account1, account2): # create vpc in us-west-1 and ap-northeast-1 - ec2_usw1 = boto3.resource("ec2", region_name="us-west-1") - client = boto3.client("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") + with mock.patch.dict(os.environ, {"MOTO_ACCOUNT_ID": account1}): + ec2_usw1 = boto3.resource("ec2", region_name="us-west-1") + client = boto3.client("ec2", region_name="us-west-1") + vpc_usw1 = ec2_usw1.create_vpc(CidrBlock="10.90.0.0/16") + + with mock.patch.dict(os.environ, {"MOTO_ACCOUNT_ID": account2}): + 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_usw1 = ec2_usw1.create_vpc_peering_connection( - VpcId=vpc_usw1.id, PeerVpcId=vpc_apn1.id, PeerRegion="ap-northeast-1" - ) - # - client.modify_vpc_peering_connection_options( - VpcPeeringConnectionId=vpc_pcx_usw1.id, - RequesterPeeringConnectionOptions={ - "AllowEgressFromLocalVpcToRemoteClassicLink": True, - }, - ) - # Requester options are different - vpc_pcx_usw1.reload() + with mock.patch.dict(os.environ, {"MOTO_ACCOUNT_ID": account1}): + vpc_pcx_usw1 = ec2_usw1.create_vpc_peering_connection( + VpcId=vpc_usw1.id, + PeerVpcId=vpc_apn1.id, + PeerRegion="ap-northeast-1", + PeerOwnerId=account2, + ) + # + client.modify_vpc_peering_connection_options( + VpcPeeringConnectionId=vpc_pcx_usw1.id, + RequesterPeeringConnectionOptions={ + "AllowEgressFromLocalVpcToRemoteClassicLink": True, + }, + ) + # Requester options are different + vpc_pcx_usw1.reload() + requester_options = vpc_pcx_usw1.requester_vpc_info["PeeringOptions"] assert requester_options["AllowDnsResolutionFromRemoteVpc"] is False assert requester_options["AllowEgressFromLocalClassicLinkToRemoteVpc"] is False assert requester_options["AllowEgressFromLocalVpcToRemoteClassicLink"] is True + # Accepter options are untouched accepter_options = vpc_pcx_usw1.accepter_vpc_info["PeeringOptions"] assert accepter_options["AllowDnsResolutionFromRemoteVpc"] is False @@ -207,97 +313,180 @@ def test_modify_vpc_peering_connections_requester_only(): @mock_ec2 -def test_modify_vpc_peering_connections_unknown_vpc(): +@pytest.mark.parametrize( + "account1,account2", + [ + pytest.param("111111111111", "111111111111", id="within account"), + pytest.param("111111111111", "222222222222", id="across accounts"), + ], +) +@pytest.mark.skipif( + settings.TEST_SERVER_MODE, reason="Cannot set account ID in server mode" +) +def test_modify_vpc_peering_connections_unknown_vpc(account1, account2): # create vpc in us-west-1 and ap-northeast-1 - ec2_usw1 = boto3.resource("ec2", region_name="us-west-1") - client = boto3.client("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") + with mock.patch.dict(os.environ, {"MOTO_ACCOUNT_ID": account1}): + ec2_usw1 = boto3.resource("ec2", region_name="us-west-1") + client = boto3.client("ec2", region_name="us-west-1") + vpc_usw1 = ec2_usw1.create_vpc(CidrBlock="10.90.0.0/16") + + with mock.patch.dict(os.environ, {"MOTO_ACCOUNT_ID": account2}): + ec2_apn1 = boto3.resource("ec2", region_name="ap-northeast-1") + vpc_apn1 = ec2_apn1.create_vpc(CidrBlock="10.20.0.0/16") + # create peering - ec2_usw1.create_vpc_peering_connection( - VpcId=vpc_usw1.id, PeerVpcId=vpc_apn1.id, PeerRegion="ap-northeast-1" - ) - # - with pytest.raises(ClientError) as ex: - client.modify_vpc_peering_connection_options( - VpcPeeringConnectionId="vpx-unknown", RequesterPeeringConnectionOptions={} + with mock.patch.dict(os.environ, {"MOTO_ACCOUNT_ID": account1}): + ec2_usw1.create_vpc_peering_connection( + VpcId=vpc_usw1.id, + PeerVpcId=vpc_apn1.id, + PeerRegion="ap-northeast-1", + PeerOwnerId=account2, ) + + with pytest.raises(ClientError) as ex: + client.modify_vpc_peering_connection_options( + VpcPeeringConnectionId="vpx-unknown", + RequesterPeeringConnectionOptions={}, + ) err = ex.value.response["Error"] assert err["Code"] == "InvalidVpcPeeringConnectionId.NotFound" assert err["Message"] == "VpcPeeringConnectionID vpx-unknown does not exist." @mock_ec2 -def test_vpc_peering_connections_cross_region_fail(): +@pytest.mark.parametrize( + "account1,account2", + [ + pytest.param("111111111111", "111111111111", id="within account"), + pytest.param("111111111111", "222222222222", id="across accounts"), + ], +) +@pytest.mark.skipif( + settings.TEST_SERVER_MODE, reason="Cannot set account ID in server mode" +) +def test_vpc_peering_connections_cross_region_fail(account1, account2): # 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") + with mock.patch.dict(os.environ, {"MOTO_ACCOUNT_ID": account1}): + ec2_usw1 = boto3.resource("ec2", region_name="us-west-1") + vpc_usw1 = ec2_usw1.create_vpc(CidrBlock="10.90.0.0/16") + + with mock.patch.dict(os.environ, {"MOTO_ACCOUNT_ID": account2}): + 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 pytest.raises(ClientError) as cm: - ec2_usw1.create_vpc_peering_connection( - VpcId=vpc_usw1.id, PeerVpcId=vpc_apn1.id, PeerRegion="ap-northeast-2" - ) + with mock.patch.dict(os.environ, {"MOTO_ACCOUNT_ID": account1}): + ec2_usw1.create_vpc_peering_connection( + VpcId=vpc_usw1.id, + PeerVpcId=vpc_apn1.id, + PeerRegion="ap-northeast-2", + PeerOwnerId=account2, + ) assert cm.value.response["Error"]["Code"] == "InvalidVpcID.NotFound" @mock_ec2 -def test_describe_vpc_peering_connections_only_returns_requested_id(): +@pytest.mark.parametrize( + "account1,account2", + [ + pytest.param("111111111111", "111111111111", id="within account"), + pytest.param("111111111111", "222222222222", id="across accounts"), + ], +) +@pytest.mark.skipif( + settings.TEST_SERVER_MODE, reason="Cannot set account ID in server mode" +) +def test_describe_vpc_peering_connections_only_returns_requested_id(account1, account2): # 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") + with mock.patch.dict(os.environ, {"MOTO_ACCOUNT_ID": account1}): + ec2_usw1 = boto3.resource("ec2", region_name="us-west-1") + vpc_usw1 = ec2_usw1.create_vpc(CidrBlock="10.90.0.0/16") + + with mock.patch.dict(os.environ, {"MOTO_ACCOUNT_ID": account2}): + 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_usw1 = ec2_usw1.create_vpc_peering_connection( - VpcId=vpc_usw1.id, PeerVpcId=vpc_apn1.id, PeerRegion="ap-northeast-1" - ) - vpc_pcx_usw2 = ec2_usw1.create_vpc_peering_connection( - VpcId=vpc_usw1.id, PeerVpcId=vpc_apn1.id, PeerRegion="ap-northeast-1" - ) - # describe peering - ec2_usw1 = boto3.client("ec2", region_name="us-west-1") - our_vpcx = [vpcx["VpcPeeringConnectionId"] for vpcx in retrieve_all(ec2_usw1)] - assert vpc_pcx_usw1.id in our_vpcx - assert vpc_pcx_usw2.id in our_vpcx - assert vpc_apn1.id not in our_vpcx - - both_pcx = ec2_usw1.describe_vpc_peering_connections( - VpcPeeringConnectionIds=[vpc_pcx_usw1.id, vpc_pcx_usw2.id] - )["VpcPeeringConnections"] - assert len(both_pcx) == 2 + with mock.patch.dict(os.environ, {"MOTO_ACCOUNT_ID": account1}): + vpc_pcx_usw1 = ec2_usw1.create_vpc_peering_connection( + VpcId=vpc_usw1.id, + PeerVpcId=vpc_apn1.id, + PeerRegion="ap-northeast-1", + PeerOwnerId=account2, + ) + vpc_pcx_usw2 = ec2_usw1.create_vpc_peering_connection( + VpcId=vpc_usw1.id, + PeerVpcId=vpc_apn1.id, + PeerRegion="ap-northeast-1", + PeerOwnerId=account2, + ) - one_pcx = ec2_usw1.describe_vpc_peering_connections( - VpcPeeringConnectionIds=[vpc_pcx_usw1.id] - )["VpcPeeringConnections"] - assert len(one_pcx) == 1 + # describe peering + ec2_usw1 = boto3.client("ec2", region_name="us-west-1") + our_vpcx = [vpcx["VpcPeeringConnectionId"] for vpcx in retrieve_all(ec2_usw1)] + + assert vpc_pcx_usw1.id in our_vpcx + assert vpc_pcx_usw2.id in our_vpcx + assert vpc_apn1.id not in our_vpcx + + both_pcx = ec2_usw1.describe_vpc_peering_connections( + VpcPeeringConnectionIds=[vpc_pcx_usw1.id, vpc_pcx_usw2.id] + )["VpcPeeringConnections"] + assert len(both_pcx) == 2 + + one_pcx = ec2_usw1.describe_vpc_peering_connections( + VpcPeeringConnectionIds=[vpc_pcx_usw1.id] + )["VpcPeeringConnections"] + assert len(one_pcx) == 1 @mock_ec2 -def test_vpc_peering_connections_cross_region_accept(): +@pytest.mark.parametrize( + "account1,account2", + [ + pytest.param("111111111111", "111111111111", id="within account"), + pytest.param("111111111111", "222222222222", id="across accounts"), + ], +) +@pytest.mark.skipif( + settings.TEST_SERVER_MODE, reason="Cannot set account ID in server mode" +) +def test_vpc_peering_connections_cross_region_accept(account1, account2): # 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") + with mock.patch.dict(os.environ, {"MOTO_ACCOUNT_ID": account1}): + ec2_usw1 = boto3.resource("ec2", region_name="us-west-1") + vpc_usw1 = ec2_usw1.create_vpc(CidrBlock="10.90.0.0/16") + + with mock.patch.dict(os.environ, {"MOTO_ACCOUNT_ID": account2}): + 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_usw1 = ec2_usw1.create_vpc_peering_connection( - VpcId=vpc_usw1.id, PeerVpcId=vpc_apn1.id, PeerRegion="ap-northeast-1" - ) + with mock.patch.dict(os.environ, {"MOTO_ACCOUNT_ID": account1}): + vpc_pcx_usw1 = ec2_usw1.create_vpc_peering_connection( + VpcId=vpc_usw1.id, + PeerVpcId=vpc_apn1.id, + PeerRegion="ap-northeast-1", + PeerOwnerId=account2, + ) + # accept peering from ap-northeast-1 - ec2_apn1 = boto3.client("ec2", region_name="ap-northeast-1") - ec2_usw1 = boto3.client("ec2", region_name="us-west-1") - acp_pcx_apn1 = ec2_apn1.accept_vpc_peering_connection( - VpcPeeringConnectionId=vpc_pcx_usw1.id - ) - des_pcx_apn1 = ec2_usw1.describe_vpc_peering_connections( - VpcPeeringConnectionIds=[vpc_pcx_usw1.id] - ) - des_pcx_usw1 = ec2_usw1.describe_vpc_peering_connections( - VpcPeeringConnectionIds=[vpc_pcx_usw1.id] - ) + with mock.patch.dict(os.environ, {"MOTO_ACCOUNT_ID": account2}): + ec2_apn1 = boto3.client("ec2", region_name="ap-northeast-1") + acp_pcx_apn1 = ec2_apn1.accept_vpc_peering_connection( + VpcPeeringConnectionId=vpc_pcx_usw1.id + ) + + with mock.patch.dict(os.environ, {"MOTO_ACCOUNT_ID": account1}): + ec2_usw1 = boto3.client("ec2", region_name="us-west-1") + des_pcx_apn1 = ec2_usw1.describe_vpc_peering_connections( + VpcPeeringConnectionIds=[vpc_pcx_usw1.id] + ) + des_pcx_usw1 = ec2_usw1.describe_vpc_peering_connections( + VpcPeeringConnectionIds=[vpc_pcx_usw1.id] + ) + assert acp_pcx_apn1["VpcPeeringConnection"]["Status"]["Code"] == "active" assert ( acp_pcx_apn1["VpcPeeringConnection"]["AccepterVpcInfo"]["Region"] @@ -328,102 +517,231 @@ def test_vpc_peering_connections_cross_region_accept(): @mock_ec2 -def test_vpc_peering_connections_cross_region_reject(): +@pytest.mark.parametrize( + "account1,account2", + [ + pytest.param("111111111111", "111111111111", id="within account"), + pytest.param("111111111111", "222222222222", id="across accounts"), + ], +) +@pytest.mark.skipif( + settings.TEST_SERVER_MODE, reason="Cannot set account ID in server mode" +) +def test_vpc_peering_connections_cross_region_reject(account1, account2): # 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") + with mock.patch.dict(os.environ, {"MOTO_ACCOUNT_ID": account1}): + ec2_usw1 = boto3.resource("ec2", region_name="us-west-1") + vpc_usw1 = ec2_usw1.create_vpc(CidrBlock="10.90.0.0/16") + + with mock.patch.dict(os.environ, {"MOTO_ACCOUNT_ID": account2}): + 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_usw1 = ec2_usw1.create_vpc_peering_connection( - VpcId=vpc_usw1.id, PeerVpcId=vpc_apn1.id, PeerRegion="ap-northeast-1" - ) + with mock.patch.dict(os.environ, {"MOTO_ACCOUNT_ID": account1}): + vpc_pcx_usw1 = ec2_usw1.create_vpc_peering_connection( + VpcId=vpc_usw1.id, + PeerVpcId=vpc_apn1.id, + PeerRegion="ap-northeast-1", + PeerOwnerId=account2, + ) + # reject peering from ap-northeast-1 - ec2_apn1 = boto3.client("ec2", region_name="ap-northeast-1") - ec2_usw1 = boto3.client("ec2", region_name="us-west-1") - rej_pcx_apn1 = ec2_apn1.reject_vpc_peering_connection( - VpcPeeringConnectionId=vpc_pcx_usw1.id - ) - des_pcx_apn1 = ec2_usw1.describe_vpc_peering_connections( - VpcPeeringConnectionIds=[vpc_pcx_usw1.id] - ) - des_pcx_usw1 = ec2_usw1.describe_vpc_peering_connections( - VpcPeeringConnectionIds=[vpc_pcx_usw1.id] - ) + with mock.patch.dict(os.environ, {"MOTO_ACCOUNT_ID": account2}): + ec2_apn1 = boto3.client("ec2", region_name="ap-northeast-1") + rej_pcx_apn1 = ec2_apn1.reject_vpc_peering_connection( + VpcPeeringConnectionId=vpc_pcx_usw1.id + ) + + with mock.patch.dict(os.environ, {"MOTO_ACCOUNT_ID": account1}): + ec2_usw1 = boto3.client("ec2", region_name="us-west-1") + des_pcx_apn1 = ec2_usw1.describe_vpc_peering_connections( + VpcPeeringConnectionIds=[vpc_pcx_usw1.id] + ) + des_pcx_usw1 = ec2_usw1.describe_vpc_peering_connections( + VpcPeeringConnectionIds=[vpc_pcx_usw1.id] + ) assert rej_pcx_apn1["Return"] is True assert des_pcx_apn1["VpcPeeringConnections"][0]["Status"]["Code"] == "rejected" assert des_pcx_usw1["VpcPeeringConnections"][0]["Status"]["Code"] == "rejected" @mock_ec2 -def test_vpc_peering_connections_cross_region_delete(): +@pytest.mark.parametrize( + "account1,account2", + [ + pytest.param("111111111111", "111111111111", id="within account"), + pytest.param("111111111111", "222222222222", id="across accounts"), + ], +) +@pytest.mark.skipif( + settings.TEST_SERVER_MODE, reason="Cannot set account ID in server mode" +) +def test_vpc_peering_connections_cross_region_delete(account1, account2): # 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") + with mock.patch.dict(os.environ, {"MOTO_ACCOUNT_ID": account1}): + ec2_usw1 = boto3.resource("ec2", region_name="us-west-1") + vpc_usw1 = ec2_usw1.create_vpc(CidrBlock="10.90.0.0/16") + + with mock.patch.dict(os.environ, {"MOTO_ACCOUNT_ID": account2}): + 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_usw1 = ec2_usw1.create_vpc_peering_connection( - VpcId=vpc_usw1.id, PeerVpcId=vpc_apn1.id, PeerRegion="ap-northeast-1" - ) - # reject peering from ap-northeast-1 - ec2_apn1 = boto3.client("ec2", region_name="ap-northeast-1") - ec2_usw1 = boto3.client("ec2", region_name="us-west-1") - del_pcx_apn1 = ec2_apn1.delete_vpc_peering_connection( - VpcPeeringConnectionId=vpc_pcx_usw1.id - ) - des_pcx_apn1 = ec2_usw1.describe_vpc_peering_connections( - VpcPeeringConnectionIds=[vpc_pcx_usw1.id] - ) - des_pcx_usw1 = ec2_usw1.describe_vpc_peering_connections( - VpcPeeringConnectionIds=[vpc_pcx_usw1.id] - ) + with mock.patch.dict(os.environ, {"MOTO_ACCOUNT_ID": account1}): + vpc_pcx_usw1 = ec2_usw1.create_vpc_peering_connection( + VpcId=vpc_usw1.id, + PeerVpcId=vpc_apn1.id, + PeerRegion="ap-northeast-1", + PeerOwnerId=account2, + ) + + with mock.patch.dict(os.environ, {"MOTO_ACCOUNT_ID": account2}): + # reject peering from ap-northeast-1 + ec2_apn1 = boto3.client("ec2", region_name="ap-northeast-1") + del_pcx_apn1 = ec2_apn1.delete_vpc_peering_connection( + VpcPeeringConnectionId=vpc_pcx_usw1.id + ) + + with mock.patch.dict(os.environ, {"MOTO_ACCOUNT_ID": account1}): + ec2_usw1 = boto3.client("ec2", region_name="us-west-1") + des_pcx_apn1 = ec2_usw1.describe_vpc_peering_connections( + VpcPeeringConnectionIds=[vpc_pcx_usw1.id] + ) + des_pcx_usw1 = ec2_usw1.describe_vpc_peering_connections( + VpcPeeringConnectionIds=[vpc_pcx_usw1.id] + ) + assert del_pcx_apn1["Return"] is True assert des_pcx_apn1["VpcPeeringConnections"][0]["Status"]["Code"] == "deleted" + assert ( + des_pcx_apn1["VpcPeeringConnections"][0]["Status"]["Message"] + == f"Deleted by {account2}" + ) + assert des_pcx_usw1["VpcPeeringConnections"][0]["Status"]["Code"] == "deleted" + assert ( + des_pcx_usw1["VpcPeeringConnections"][0]["Status"]["Message"] + == f"Deleted by {account2}" + ) @mock_ec2 -def test_vpc_peering_connections_cross_region_accept_wrong_region(): +@pytest.mark.parametrize( + "account1,account2", + [ + pytest.param("111111111111", "111111111111", id="within account"), + pytest.param("111111111111", "222222222222", id="across accounts"), + ], +) +@pytest.mark.skipif( + settings.TEST_SERVER_MODE, reason="Cannot set account ID in server mode" +) +def test_vpc_peering_connections_cross_region_accept_wrong_region(account1, account2): # 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") + with mock.patch.dict(os.environ, {"MOTO_ACCOUNT_ID": account1}): + ec2_usw1 = boto3.resource("ec2", region_name="us-west-1") + vpc_usw1 = ec2_usw1.create_vpc(CidrBlock="10.90.0.0/16") + + with mock.patch.dict(os.environ, {"MOTO_ACCOUNT_ID": account2}): + 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_usw1 = ec2_usw1.create_vpc_peering_connection( - VpcId=vpc_usw1.id, PeerVpcId=vpc_apn1.id, PeerRegion="ap-northeast-1" - ) + with mock.patch.dict(os.environ, {"MOTO_ACCOUNT_ID": account1}): + vpc_pcx_usw1 = ec2_usw1.create_vpc_peering_connection( + VpcId=vpc_usw1.id, + PeerVpcId=vpc_apn1.id, + PeerRegion="ap-northeast-1", + PeerOwnerId=account2, + ) # accept wrong peering from us-west-1 which will raise error - ec2_apn1 = boto3.client("ec2", region_name="ap-northeast-1") - ec2_usw1 = boto3.client("ec2", region_name="us-west-1") - with pytest.raises(ClientError) as cm: - ec2_usw1.accept_vpc_peering_connection(VpcPeeringConnectionId=vpc_pcx_usw1.id) - assert cm.value.response["Error"]["Code"] == "OperationNotPermitted" - exp_msg = f"Incorrect region (us-west-1) specified for this request.VPC peering connection {vpc_pcx_usw1.id} must be accepted in region ap-northeast-1" - assert cm.value.response["Error"]["Message"] == exp_msg + # only applicable for cross-region intra-account peering. + if account1 == account2: + with mock.patch.dict(os.environ, {"MOTO_ACCOUNT_ID": account1}): + ec2_usw1 = boto3.client("ec2", region_name="us-west-1") + with pytest.raises(ClientError) as cm: + ec2_usw1.accept_vpc_peering_connection( + VpcPeeringConnectionId=vpc_pcx_usw1.id + ) + + assert cm.value.response["Error"]["Code"] == "OperationNotPermitted" + exp_msg = f"Incorrect region (us-west-1) specified for this request. VPC peering connection {vpc_pcx_usw1.id} must be accepted in region ap-northeast-1" + assert cm.value.response["Error"]["Message"] == exp_msg + + # Ensure accepting peering from requester account raises + # only applicable for cross-region inter-account peering. + if account1 != account2: + with mock.patch.dict(os.environ, {"MOTO_ACCOUNT_ID": account1}): + ec2_usw1 = boto3.client("ec2", region_name="us-west-1") + with pytest.raises(ClientError) as cm: + ec2_usw1.accept_vpc_peering_connection( + VpcPeeringConnectionId=vpc_pcx_usw1.id + ) + + assert cm.value.response["Error"]["Code"] == "OperationNotPermitted" + exp_msg = f"User ({account1}) cannot accept peering {vpc_pcx_usw1.id}" + assert cm.value.response["Error"]["Message"] == exp_msg @mock_ec2 -def test_vpc_peering_connections_cross_region_reject_wrong_region(): +@pytest.mark.parametrize( + "account1,account2", + [ + pytest.param("111111111111", "111111111111", id="within account"), + pytest.param("111111111111", "222222222222", id="across accounts"), + ], +) +@pytest.mark.skipif( + settings.TEST_SERVER_MODE, reason="Cannot set account ID in server mode" +) +def test_vpc_peering_connections_cross_region_reject_wrong_region(account1, account2): # 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") + with mock.patch.dict(os.environ, {"MOTO_ACCOUNT_ID": account1}): + ec2_usw1 = boto3.resource("ec2", region_name="us-west-1") + vpc_usw1 = ec2_usw1.create_vpc(CidrBlock="10.90.0.0/16") + + with mock.patch.dict(os.environ, {"MOTO_ACCOUNT_ID": account2}): + 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_usw1 = ec2_usw1.create_vpc_peering_connection( - VpcId=vpc_usw1.id, PeerVpcId=vpc_apn1.id, PeerRegion="ap-northeast-1" - ) - # reject wrong peering from us-west-1 which will raise error - ec2_apn1 = boto3.client("ec2", region_name="ap-northeast-1") - ec2_usw1 = boto3.client("ec2", region_name="us-west-1") - with pytest.raises(ClientError) as cm: - ec2_usw1.reject_vpc_peering_connection(VpcPeeringConnectionId=vpc_pcx_usw1.id) - assert cm.value.response["Error"]["Code"] == "OperationNotPermitted" - exp_msg = f"Incorrect region (us-west-1) specified for this request.VPC peering connection {vpc_pcx_usw1.id} must be accepted or rejected in region ap-northeast-1" - assert cm.value.response["Error"]["Message"] == exp_msg + with mock.patch.dict(os.environ, {"MOTO_ACCOUNT_ID": account1}): + vpc_pcx_usw1 = ec2_usw1.create_vpc_peering_connection( + VpcId=vpc_usw1.id, + PeerVpcId=vpc_apn1.id, + PeerRegion="ap-northeast-1", + PeerOwnerId=account2, + ) + + # reject wrong peering from us-west-1 which will raise error. + # only applicable for cross-region intra-account peering. + if account1 == account2: + with mock.patch.dict(os.environ, {"MOTO_ACCOUNT_ID": account1}): + ec2_usw1 = boto3.client("ec2", region_name="us-west-1") + with pytest.raises(ClientError) as cm: + ec2_usw1.reject_vpc_peering_connection( + VpcPeeringConnectionId=vpc_pcx_usw1.id + ) + + assert cm.value.response["Error"]["Code"] == "OperationNotPermitted" + exp_msg = f"Incorrect region (us-west-1) specified for this request. VPC peering connection {vpc_pcx_usw1.id} must be accepted or rejected in region ap-northeast-1" + assert cm.value.response["Error"]["Message"] == exp_msg + + # Ensure rejecting peering from requester account raises + # only applicable for cross-region inter-account peering. + if account1 != account2: + with mock.patch.dict(os.environ, {"MOTO_ACCOUNT_ID": account1}): + ec2_usw1 = boto3.client("ec2", region_name="us-west-1") + with pytest.raises(ClientError) as cm: + ec2_usw1.reject_vpc_peering_connection( + VpcPeeringConnectionId=vpc_pcx_usw1.id + ) + + assert cm.value.response["Error"]["Code"] == "OperationNotPermitted" + exp_msg = f"User ({account1}) cannot reject peering {vpc_pcx_usw1.id}" + assert cm.value.response["Error"]["Message"] == exp_msg def retrieve_all(client):