diff --git a/moto/ec2/models.py b/moto/ec2/models.py index dd5abdaf8..72232b9eb 100644 --- a/moto/ec2/models.py +++ b/moto/ec2/models.py @@ -33,7 +33,7 @@ from moto.core.utils import ( ) from moto.core import ACCOUNT_ID from moto.kms import kms_backends -from moto.utilities.utils import load_resource, merge_multiple_dicts +from moto.utilities.utils import load_resource, merge_multiple_dicts, filter_resources from os import listdir from .exceptions import ( @@ -6016,29 +6016,18 @@ class TransitGatewayBackend(object): return transit_gateway def get_all_transit_gateways(self, filters): - transit_gateways = self.transit_gateways.values() + transit_gateways = list(self.transit_gateways.values()) - if filters is not None: - if filters.get("transit-gateway-id") is not None: - transit_gateways = [ - transit_gateway - for transit_gateway in transit_gateways - if transit_gateway.id in filters["transit-gateway-id"] - ] - if filters.get("state") is not None: - transit_gateways = [ - transit_gateway - for transit_gateway in transit_gateways - if transit_gateway.state in filters["state"] - ] - if filters.get("owner-id") is not None: - transit_gateways = [ - transit_gateway - for transit_gateway in transit_gateways - if transit_gateway.owner_id in filters["owner-id"] - ] + attr_pairs = ( + ("transit-gateway-id", "id"), + ("state", "state"), + ("owner-id", "owner_id"), + ) - return transit_gateways + result = transit_gateways + if filters: + result = filter_resources(transit_gateways, filters, attr_pairs) + return result def delete_transit_gateway(self, transit_gateway_id): return self.transit_gateways.pop(transit_gateway_id) @@ -6074,6 +6063,8 @@ class TransitGatewayRouteTable(TaggedEC2Resource): self.state = "available" self.routes = {} self.add_tags(tags or {}) + self.route_table_association = {} + self.route_table_propagation = {} @property def physical_resource_id(self): @@ -6109,9 +6100,9 @@ class TransitGatewayRouteTableBackend(object): return transit_gateways_route_table def get_all_transit_gateway_route_tables( - self, transit_gateway_ids=None, filters=None + self, transit_gateway_route_table_ids=None, filters=None ): - transit_gateway_route_tables = self.transit_gateways_route_tables.values() + transit_gateway_route_tables = list(self.transit_gateways_route_tables.values()) attr_pairs = ( ("default-association-route-table", "default_association_route_table"), @@ -6121,28 +6112,24 @@ class TransitGatewayRouteTableBackend(object): ("transit-gateway-route-table-id", "id"), ) - if transit_gateway_ids: + if transit_gateway_route_table_ids: transit_gateway_route_tables = [ transit_gateway_route_table for transit_gateway_route_table in transit_gateway_route_tables - if transit_gateway_route_table.id in transit_gateway_ids + if transit_gateway_route_table.id in transit_gateway_route_table_ids ] + result = transit_gateway_route_tables if filters: - for attrs in attr_pairs: - values = filters.get(attrs[0]) or None - if values is not None: - transit_gateway_route_tables = [ - transit_gateway_route_table - for transit_gateway_route_table in transit_gateway_route_tables - if not values - or getattr(transit_gateway_route_table, attrs[1]) in values - ] - - return transit_gateway_route_tables + result = filter_resources(transit_gateway_route_tables, filters, attr_pairs) + return result def delete_transit_gateway_route_table(self, transit_gateway_route_table_id): - return self.transit_gateways_route_tables.pop(transit_gateway_route_table_id) + transit_gateways_route_table = self.transit_gateways_route_tables[ + transit_gateway_route_table_id + ] + transit_gateways_route_table.state = "deleted" + return transit_gateways_route_table def create_transit_gateway_route( self, @@ -6151,22 +6138,31 @@ class TransitGatewayRouteTableBackend(object): transit_gateway_attachment_id=None, blackhole=False, ): - transit_gateways_route_table = self.transit_gateways_route_tables[ + transit_gateways_route_table = self.transit_gateways_route_tables.get( transit_gateway_route_table_id - ] + ) + transit_gateway_attachment = self.transit_gateway_attachments.get( + transit_gateway_attachment_id + ) transit_gateways_route_table.routes[destination_cidr_block] = { "destinationCidrBlock": destination_cidr_block, "prefixListId": "", "state": "blackhole" if blackhole else "active", - # TODO: needs to be fixed once we have support for transit gateway attachments - "transitGatewayAttachments": { - "resourceId": "TODO", - "resourceType": "TODO", - "transitGatewayAttachmentId": transit_gateway_attachment_id, - }, - "type": "TODO", + "type": "static", } - return transit_gateways_route_table + + if transit_gateway_attachment: + transit_gateway_attachment_dict = { + "transitGatewayAttachments": { + "resourceId": transit_gateway_attachment.resource_id, + "resourceType": transit_gateway_attachment.resource_type, + "transitGatewayAttachmentId": transit_gateway_attachment_id, + } + } + transit_gateways_route_table.routes[destination_cidr_block].update( + transit_gateway_attachment_dict + ) + return transit_gateways_route_table.routes[destination_cidr_block] def delete_transit_gateway_route( self, transit_gateway_route_table_id, destination_cidr_block, @@ -6180,27 +6176,120 @@ class TransitGatewayRouteTableBackend(object): def search_transit_gateway_routes( self, transit_gateway_route_table_id, filters, max_results=None ): - transit_gateway_route_table = self.transit_gateways_route_tables[ + transit_gateway_route_table = self.transit_gateways_route_tables.get( transit_gateway_route_table_id - ] + ) + if not transit_gateway_route_table: + return [] attr_pairs = ( ("type", "type"), ("state", "state"), ) - for attrs in attr_pairs: - values = filters.get(attrs[0]) or None - if values: - routes = [ - transit_gateway_route_table.routes[key] - for key in transit_gateway_route_table.routes - if transit_gateway_route_table.routes[key][attrs[1]] in values - ] + routes = transit_gateway_route_table.routes.copy() + for key in transit_gateway_route_table.routes: + for attrs in attr_pairs: + values = filters.get(attrs[0]) or None + if values: + if routes.get(key).get(attrs[1]) not in values: + routes.pop(key) + break if max_results: routes = routes[: int(max_results)] return routes + def set_route_table_association( + self, transit_gateway_attachment_id, transit_gateway_route_table_id + ): + self.transit_gateways_route_tables[ + transit_gateway_route_table_id + ].route_table_association = { + "resourceId": self.transit_gateway_attachments[ + transit_gateway_attachment_id + ].resource_id, + "resourceType": self.transit_gateway_attachments[ + transit_gateway_attachment_id + ].resource_type, + "state": "associated", + "transitGatewayAttachmentId": transit_gateway_attachment_id, + } + + def set_route_table_propagation( + self, transit_gateway_attachment_id, transit_gateway_route_table_id + ): + self.transit_gateways_route_tables[ + transit_gateway_route_table_id + ].route_table_propagation = { + "resourceId": self.transit_gateway_attachments[ + transit_gateway_attachment_id + ].resource_id, + "resourceType": self.transit_gateway_attachments[ + transit_gateway_attachment_id + ].resource_type, + "state": "enabled", + "transitGatewayAttachmentId": transit_gateway_attachment_id, + } + + def disable_route_table_propagation(self, transit_gateway_route_table_id): + self.transit_gateways_route_tables[ + transit_gateway_route_table_id + ].route_table_propagation = {} + + def get_all_transit_gateway_route_table_associations( + self, transit_gateway_route_table_id=None, filters=None + ): + transit_gateway_route_tables = list(self.transit_gateways_route_tables.values()) + + if transit_gateway_route_tables: + transit_gateway_route_tables = [ + transit_gateway_route_table + for transit_gateway_route_table in transit_gateway_route_tables + if transit_gateway_route_table.id in transit_gateway_route_table_id + ] + + attr_pairs = ( + ("resource-id", "route_table_association", "resourceId"), + ("resource-type", "route_table_association", "resourceType"), + ( + "transit-gateway-attachment-id", + "route_table_association", + "transitGatewayAttachmentId", + ), + ) + + result = transit_gateway_route_tables + if filters: + result = filter_resources(transit_gateway_route_tables, filters, attr_pairs) + return result + + def get_all_transit_gateway_route_table_propagations( + self, transit_gateway_route_table_id=None, filters=None + ): + transit_gateway_route_tables = list(self.transit_gateways_route_tables.values()) + + if transit_gateway_route_tables: + transit_gateway_route_tables = [ + transit_gateway_route_table + for transit_gateway_route_table in transit_gateway_route_tables + if transit_gateway_route_table.id in transit_gateway_route_table_id + ] + + attr_pairs = ( + ("resource-id", "route_table_propagation", "resourceId"), + ("resource-type", "route_table_propagation", "resourceType"), + ( + "transit-gateway-attachment-id", + "route_table_propagation", + "transitGatewayAttachmentId", + ), + ) + + result = transit_gateway_route_tables + if filters: + result = filter_resources(transit_gateway_route_tables, filters, attr_pairs) + return result + class TransitGatewayAttachment(TaggedEC2Resource): def __init__( @@ -6209,6 +6298,7 @@ class TransitGatewayAttachment(TaggedEC2Resource): self.ec2_backend = backend self.association = {} + self.propagation = {} self.resource_id = resource_id self.resource_type = resource_type @@ -6261,7 +6351,7 @@ class TransitGatewayVpcAttachment(TransitGatewayAttachment): class TransitGatewayAttachmentBackend(object): def __init__(self): - self.transit_gateways_attachments = {} + self.transit_gateway_attachments = {} super(TransitGatewayAttachmentBackend, self).__init__() def create_transit_gateway_vpn_attachment( @@ -6274,7 +6364,7 @@ class TransitGatewayAttachmentBackend(object): transit_gateway_id=transit_gateway_id, tags=tags, ) - self.transit_gateways_attachments[ + self.transit_gateway_attachments[ transit_gateway_vpn_attachment.id ] = transit_gateway_vpn_attachment return transit_gateway_vpn_attachment @@ -6290,7 +6380,7 @@ class TransitGatewayAttachmentBackend(object): subnet_ids=subnet_ids, options=options, ) - self.transit_gateways_attachments[ + self.transit_gateway_attachments[ transit_gateway_vpc_attachment.id ] = transit_gateway_vpc_attachment return transit_gateway_vpc_attachment @@ -6298,7 +6388,7 @@ class TransitGatewayAttachmentBackend(object): def describe_transit_gateway_attachments( self, transit_gateways_attachment_ids=None, filters=None, max_results=0 ): - transit_gateways_attachments = self.transit_gateways_attachments.values() + transit_gateway_attachments = list(self.transit_gateway_attachments.values()) attr_pairs = ( ("resource-id", "resource_id"), @@ -6306,28 +6396,25 @@ class TransitGatewayAttachmentBackend(object): ("transit-gateway-id", "transit_gateway_id"), ) - if transit_gateways_attachment_ids: - transit_gateways_attachments = [ + if ( + not transit_gateways_attachment_ids == [] + and transit_gateways_attachment_ids is not None + ): + transit_gateway_attachments = [ transit_gateways_attachment - for transit_gateways_attachment in transit_gateways_attachments + for transit_gateways_attachment in transit_gateway_attachments if transit_gateways_attachment.id in transit_gateways_attachment_ids ] + result = transit_gateway_attachments if filters: - for attrs in attr_pairs: - values = filters.get(attrs[0]) or None - if values is not None: - transit_gateways_attachments = [ - transit_gateways_attachment - for transit_gateways_attachment in transit_gateways_attachments - if getattr(transit_gateways_attachment, attrs[1]) in values - ] - return transit_gateways_attachments + result = filter_resources(transit_gateway_attachments, filters, attr_pairs) + return result def describe_transit_gateway_vpc_attachments( self, transit_gateways_attachment_ids=None, filters=None, max_results=0 ): - transit_gateways_attachments = self.transit_gateways_attachments.values() + transit_gateway_attachments = list(self.transit_gateway_attachments.values()) attr_pairs = ( ("state", "state"), @@ -6340,23 +6427,155 @@ class TransitGatewayAttachmentBackend(object): not transit_gateways_attachment_ids == [] and transit_gateways_attachment_ids is not None ): - transit_gateways_attachments = [ + transit_gateway_attachments = [ transit_gateways_attachment - for transit_gateways_attachment in transit_gateways_attachments + for transit_gateways_attachment in transit_gateway_attachments if transit_gateways_attachment.id in transit_gateways_attachment_ids ] + result = transit_gateway_attachments if filters: - for attrs in attr_pairs: - values = filters.get(attrs[0]) or None - if values is not None: - transit_gateways_attachments = [ - transit_gateways_attachment - for transit_gateways_attachment in transit_gateways_attachments - if getattr(transit_gateways_attachment, attrs[1]) in values - ] + result = filter_resources(transit_gateway_attachments, filters, attr_pairs) + return result - return transit_gateways_attachments + def delete_transit_gateway_vpc_attachment(self, transit_gateway_attachment_id=None): + transit_gateway_attachment = self.transit_gateway_attachments.pop( + transit_gateway_attachment_id + ) + transit_gateway_attachment.state = "deleted" + return transit_gateway_attachment + + def modify_transit_gateway_vpc_attachment( + self, + add_subnet_ids=None, + options=None, + remove_subnet_ids=None, + transit_gateway_attachment_id=None, + ): + + tgw_attachment = self.transit_gateway_attachments[transit_gateway_attachment_id] + if remove_subnet_ids: + tgw_attachment.subnet_ids = [ + id for id in tgw_attachment.subnet_ids if id not in remove_subnet_ids + ] + + if options: + tgw_attachment.options.update(options) + + if add_subnet_ids: + for id in add_subnet_ids: + tgw_attachment.subnet_ids.append(id) + + return tgw_attachment + + def set_attachment_association( + self, transit_gateway_attachment_id=None, transit_gateway_route_table_id=None + ): + self.transit_gateway_attachments[transit_gateway_attachment_id].association = { + "state": "associated", + "transitGatewayRouteTableId": transit_gateway_route_table_id, + } + + def set_attachment_propagation( + self, transit_gateway_attachment_id=None, transit_gateway_route_table_id=None + ): + self.transit_gateway_attachments[transit_gateway_attachment_id].propagation = { + "state": "enabled", + "transitGatewayRouteTableId": transit_gateway_route_table_id, + } + + def disable_attachment_propagation(self, transit_gateway_attachment_id=None): + self.transit_gateway_attachments[transit_gateway_attachment_id].propagation[ + "state" + ] = "disabled" + + +class TransitGatewayRelations(object): + # this class is for TransitGatewayAssociation and TransitGatewayPropagation + def __init__( + self, + backend, + transit_gateway_attachment_id=None, + transit_gateway_route_table_id=None, + state=None, + ): + self.ec2_backend = backend + self.transit_gateway_attachment_id = transit_gateway_attachment_id + self.transit_gateway_route_table_id = transit_gateway_route_table_id + self.resource_id = backend.transit_gateway_attachments[ + transit_gateway_attachment_id + ].resource_id + self.resource_type = backend.transit_gateway_attachments[ + transit_gateway_attachment_id + ].resource_type + self.state = state + + +class TransitGatewayRelationsBackend(object): + def __init__(self): + self.transit_gateway_associations = {} + self.transit_gateway_propagations = {} + super(TransitGatewayRelationsBackend, self).__init__() + + def associate_transit_gateway_route_table( + self, transit_gateway_attachment_id=None, transit_gateway_route_table_id=None + ): + transit_gateway_association = TransitGatewayRelations( + self, + transit_gateway_attachment_id, + transit_gateway_route_table_id, + state="associated", + ) + self.set_route_table_association( + transit_gateway_attachment_id, transit_gateway_route_table_id + ) + self.set_attachment_association( + transit_gateway_attachment_id, transit_gateway_route_table_id + ) + self.transit_gateway_associations[ + transit_gateway_attachment_id + ] = transit_gateway_association + + return transit_gateway_association + + def enable_transit_gateway_route_table_propagation( + self, transit_gateway_attachment_id=None, transit_gateway_route_table_id=None + ): + transit_gateway_propagation = TransitGatewayRelations( + self, + transit_gateway_attachment_id, + transit_gateway_route_table_id, + state="enabled", + ) + self.set_route_table_propagation( + transit_gateway_attachment_id, transit_gateway_route_table_id + ) + self.set_attachment_propagation( + transit_gateway_attachment_id, transit_gateway_route_table_id + ) + self.transit_gateway_propagations[ + transit_gateway_attachment_id + ] = transit_gateway_propagation + + return transit_gateway_propagation + + def disable_transit_gateway_route_table_propagation( + self, transit_gateway_attachment_id=None, transit_gateway_route_table_id=None + ): + self.disable_route_table_propagation( + transit_gateway_route_table_id=transit_gateway_route_table_id + ) + self.disable_attachment_propagation( + transit_gateway_attachment_id=transit_gateway_attachment_id + ) + self.transit_gateway_propagations[ + transit_gateway_attachment_id + ].state = "disabled" + transit_gateway_propagation = self.transit_gateway_propagations.pop( + transit_gateway_attachment_id + ) + + return transit_gateway_propagation class NatGateway(CloudFormationModel): @@ -6721,6 +6940,7 @@ class EC2Backend( TransitGatewayBackend, TransitGatewayRouteTableBackend, TransitGatewayAttachmentBackend, + TransitGatewayRelationsBackend, LaunchTemplateBackend, IamInstanceProfileAssociationBackend, ): diff --git a/moto/ec2/responses/transit_gateway_attachments.py b/moto/ec2/responses/transit_gateway_attachments.py index 1ac07efb9..c323499f0 100644 --- a/moto/ec2/responses/transit_gateway_attachments.py +++ b/moto/ec2/responses/transit_gateway_attachments.py @@ -41,6 +41,21 @@ class TransitGatewayAttachment(BaseResponse): transit_gateway_vpc_attachments=transit_gateway_vpc_attachments ) + def modify_transit_gateway_vpc_attachment(self): + add_subnet_ids = self._get_multi_param("AddSubnetIds") + options = self._get_multi_param_dict("Options") + remove_subnet_ids = self._get_multi_param("RemoveSubnetIds") + transit_gateway_attachment_id = self._get_param("TransitGatewayAttachmentId") + + transit_gateway_attachment = self.ec2_backend.modify_transit_gateway_vpc_attachment( + add_subnet_ids=add_subnet_ids, + options=options, + remove_subnet_ids=remove_subnet_ids, + transit_gateway_attachment_id=transit_gateway_attachment_id, + ) + template = self.response_template(MODIFY_TRANSIT_GATEWAY_VPC_ATTACHMENTS) + return template.render(transit_gateway_attachment=transit_gateway_attachment) + def describe_transit_gateway_attachments(self): transit_gateways_attachment_ids = self._get_multi_param( "TransitGatewayAttachmentIds" @@ -55,6 +70,44 @@ class TransitGatewayAttachment(BaseResponse): template = self.response_template(DESCRIBE_TRANSIT_GATEWAY_ATTACHMENTS) return template.render(transit_gateway_attachments=transit_gateway_attachments) + def delete_transit_gateway_vpc_attachment(self): + transit_gateway_attachment_id = self._get_param("TransitGatewayAttachmentId") + transit_gateway_attachment = self.ec2_backend.delete_transit_gateway_vpc_attachment( + transit_gateway_attachment_id=transit_gateway_attachment_id + ) + template = self.response_template(DELETE_TRANSIT_GATEWAY_VPC_ATTACHMENTS) + return template.render(transit_gateway_attachment=transit_gateway_attachment) + + def associate_transit_gateway_route_table(self): + transit_gateway_attachment_id = self._get_param("TransitGatewayAttachmentId") + transit_gateway_route_table_id = self._get_param("TransitGatewayRouteTableId") + transit_gateway_association = self.ec2_backend.associate_transit_gateway_route_table( + transit_gateway_attachment_id=transit_gateway_attachment_id, + transit_gateway_route_table_id=transit_gateway_route_table_id, + ) + template = self.response_template(TRANSIT_GATEWAY_ASSOCIATION) + return template.render(transit_gateway_association=transit_gateway_association) + + def enable_transit_gateway_route_table_propagation(self): + transit_gateway_attachment_id = self._get_param("TransitGatewayAttachmentId") + transit_gateway_route_table_id = self._get_param("TransitGatewayRouteTableId") + transit_gateway_propagation = self.ec2_backend.enable_transit_gateway_route_table_propagation( + transit_gateway_attachment_id=transit_gateway_attachment_id, + transit_gateway_route_table_id=transit_gateway_route_table_id, + ) + template = self.response_template(TRANSIT_GATEWAY_PROPAGATION) + return template.render(transit_gateway_propagation=transit_gateway_propagation) + + def disable_transit_gateway_route_table_propagation(self): + transit_gateway_attachment_id = self._get_param("TransitGatewayAttachmentId") + transit_gateway_route_table_id = self._get_param("TransitGatewayRouteTableId") + transit_gateway_propagation = self.ec2_backend.disable_transit_gateway_route_table_propagation( + transit_gateway_attachment_id=transit_gateway_attachment_id, + transit_gateway_route_table_id=transit_gateway_route_table_id, + ) + template = self.response_template(TRANSIT_GATEWAY_PROPAGATION) + return template.render(transit_gateway_propagation=transit_gateway_propagation) + CREATE_TRANSIT_GATEWAY_VPC_ATTACHMENT = """ 9b5766ac-2af6-4b92-9a8a-4d74ae46ae79 @@ -153,3 +206,91 @@ DESCRIBE_TRANSIT_GATEWAY_VPC_ATTACHMENTS = """ """ + + +MODIFY_TRANSIT_GATEWAY_VPC_ATTACHMENTS = """ + 9b5766ac-2af6-4b92-9a8a-4d74ae46ae79 + + {{ transit_gateway_attachment.create_time }} + + {{ transit_gateway_attachment.options.ApplianceModeSupport }} + {{ transit_gateway_attachment.options.DnsSupport }} + {{ transit_gateway_attachment.options.Ipv6Support }} + + {{ transit_gateway_attachment.state }} + + {% for subnet_id in transit_gateway_attachment.subnet_ids %} + {{ subnet_id }} + {% endfor %} + + + {% for tag in transit_gateway_attachment.get_tags() %} + + {{ tag.key }} + {{ tag.value }} + + {% endfor %} + + {{ transit_gateway_attachment.id }} + {{ transit_gateway_attachment.transit_gateway_id }} + {{ transit_gateway_attachment.vpc_id }} + {{ transit_gateway_attachment.resource_owner_id }} + +""" + + +DELETE_TRANSIT_GATEWAY_VPC_ATTACHMENTS = """ + 9b5766ac-2af6-4b92-9a8a-4d74ae46ae79 + + {{ transit_gateway_attachment.create_time }} + + {{ transit_gateway_attachment.options.ApplianceModeSupport }} + {{ transit_gateway_attachment.options.DnsSupport }} + {{ transit_gateway_attachment.options.Ipv6Support }} + + {{ transit_gateway_attachment.state }} + + {% for subnet_id in transit_gateway_attachment.subnet_ids %} + {{ subnet_id }} + {% endfor %} + + + {% for tag in transit_gateway_attachment.get_tags() %} + + {{ tag.key }} + {{ tag.value }} + + {% endfor %} + + {{ transit_gateway_attachment.id }} + {{ transit_gateway_attachment.transit_gateway_id }} + {{ transit_gateway_attachment.vpc_id }} + {{ transit_gateway_attachment.resource_owner_id }} + +""" + + +TRANSIT_GATEWAY_ASSOCIATION = """ + 86a597cf-93ec-44a3-9559-4641863642a5 + + {{ transit_gateway_association.resource_id }} + {{ transit_gateway_association.resource_type }} + {{ transit_gateway_association.state }} + {{ transit_gateway_association.transit_gateway_attachment_id }} + {{ transit_gateway_association.transit_gateway_route_table_id }} + + +""" + + +TRANSIT_GATEWAY_PROPAGATION = """ + c78427d4-e498-46ae-bc14-32841b16bff4 + + {{ transit_gateway_propagation.resource_id }} + {{ transit_gateway_propagation.resource_type }} + {{ transit_gateway_propagation.state }} + {{ transit_gateway_propagation.transit_gateway_attachment_id }} + {{ transit_gateway_propagation.transit_gateway_route_table_id }} + + +""" diff --git a/moto/ec2/responses/transit_gateway_route_tables.py b/moto/ec2/responses/transit_gateway_route_tables.py index f4974e056..da760f335 100644 --- a/moto/ec2/responses/transit_gateway_route_tables.py +++ b/moto/ec2/responses/transit_gateway_route_tables.py @@ -20,11 +20,11 @@ class TransitGatewayRouteTable(BaseResponse): def describe_transit_gateway_route_tables(self): filters = filters_from_querystring(self.querystring) - transit_gateway_ids = ( + transit_gateway_route_table_ids = ( self._get_multi_param("TransitGatewayRouteTableIds") or None ) transit_gateway_route_tables = self.ec2_backend.get_all_transit_gateway_route_tables( - transit_gateway_ids, filters + transit_gateway_route_table_ids, filters ) template = self.response_template(DESCRIBE_TRANSIT_GATEWAY_ROUTE_TABLE_RESPONSE) return template.render( @@ -83,6 +83,32 @@ class TransitGatewayRouteTable(BaseResponse): template = self.response_template(SEARCH_TRANSIT_GATEWAY_ROUTES_RESPONSE) return template.render(transit_gateway_routes=transit_gateway_routes) + def get_transit_gateway_route_table_associations(self): + transit_gateway_route_table_id = self._get_param("TransitGatewayRouteTableId") + filters = filters_from_querystring(self.querystring) + transit_gateway_route_table_associations = self.ec2_backend.get_all_transit_gateway_route_table_associations( + transit_gateway_route_table_id, filters + ) + template = self.response_template( + GET_TRANSIT_GATEWAY_ROUTE_TABLE_ASSOCIATIONS_RESPONSE + ) + return template.render( + transit_gateway_route_table_associations=transit_gateway_route_table_associations + ) + + def get_transit_gateway_route_table_propagations(self): + transit_gateway_route_table_id = self._get_param("TransitGatewayRouteTableId") + filters = filters_from_querystring(self.querystring) + transit_gateway_route_table_propagations = self.ec2_backend.get_all_transit_gateway_route_table_propagations( + transit_gateway_route_table_id, filters + ) + template = self.response_template( + GET_TRANSIT_GATEWAY_ROUTE_TABLE_PROPAGATIONS_RESPONSE + ) + return template.render( + transit_gateway_route_table_propagations=transit_gateway_route_table_propagations + ) + CREATE_TRANSIT_GATEWAY_ROUTE_TABLE_RESPONSE = """ 3a495d25-08d4-466d-822e-477c9b1fc606 @@ -133,16 +159,12 @@ DESCRIBE_TRANSIT_GATEWAY_ROUTE_TABLE_RESPONSE = """ a9a07226-c7b1-4305-9934-0bcfc3ef1c5e - {% for transit_gateway_route_table in transit_gateway_route_tables %} - - {{ transit_gateway_route_table.create_time }} - {{ transit_gateway_route_table.default_association_route_table }} - {{ transit_gateway_route_table.default_propagation_route_table }} - {{ transit_gateway_route_table.state }} - {{ transit_gateway_route_table.transit_gateway_id }} - {{ transit_gateway_route_table.id }} - - {% endfor %} + {{ transit_gateway_route_table.create_time }} + {{ transit_gateway_route_table.default_association_route_table }} + {{ transit_gateway_route_table.default_propagation_route_table }} + {{ transit_gateway_route_table.state }} + {{ transit_gateway_route_table.transit_gateway_id }} + {{ transit_gateway_route_table.id }} """ @@ -152,9 +174,18 @@ CREATE_TRANSIT_GATEWAY_ROUTE_RESPONSE = """ 072b02ce-df3a-4de6-a20b-6653ae4b91a4 - {{ transit_gateway_route_table.routes[destination_cidr_block]['destinationCidrBlock'] }} - {{ transit_gateway_route_table.routes[destination_cidr_block]['state'] }} - {{ transit_gateway_route_table.routes[destination_cidr_block]['type'] }} + {{ transit_gateway_route_table.destinationCidrBlock }} + {{ transit_gateway_route_table.state }} + {{ transit_gateway_route_table.type }} + + {% if transit_gateway_route_table.state != 'blackhole' and transit_gateway_route_table.transitGatewayAttachments %} + + {{ transit_gateway_route_table.transitGatewayAttachments.resourceId }} + {{ transit_gateway_route_table.transitGatewayAttachments.resourceType }} + {{ transit_gateway_route_table.transitGatewayAttachments.transitGatewayAttachmentId }} + + {% endif %} + """ @@ -163,9 +194,9 @@ DELETE_TRANSIT_GATEWAY_ROUTE_RESPONSE = """ 2109d5bb-f874-4f35-b419-4723792a638f - {{ transit_gateway_route_table.routes[destination_cidr_block]['destinationCidrBlock'] }} - {{ transit_gateway_route_table.routes[destination_cidr_block]['state'] }} - {{ transit_gateway_route_table.routes[destination_cidr_block]['type'] }} + {{ transit_gateway_route_table.routes[destination_cidr_block].destinationCidrBlock }} + {{ transit_gateway_route_table.routes[destination_cidr_block].state }} + {{ transit_gateway_route_table.routes[destination_cidr_block].type }} """ @@ -176,12 +207,49 @@ SEARCH_TRANSIT_GATEWAY_ROUTES_RESPONSE = """ {% for route in transit_gateway_routes %} - {{ route['destinationCidrBlock'] }} - {{ route['state'] }} - {{ route['type'] }} + {{ transit_gateway_routes[route].destinationCidrBlock }} + {{ transit_gateway_routes[route].state }} + {{ transit_gateway_routes[route].type }} + {% if transit_gateway_routes[route].get('transitGatewayAttachments') %} + + + {{ transit_gateway_routes[route].transitGatewayAttachments.resourceId }} + {{ transit_gateway_routes[route].transitGatewayAttachments.resourceType }} + {{ transit_gateway_routes[route].transitGatewayAttachments.transitGatewayAttachmentId }} + + + {% endif %} {% endfor %} false """ + +GET_TRANSIT_GATEWAY_ROUTE_TABLE_ASSOCIATIONS_RESPONSE = """ + 92fdc91d-c374-4217-b2b4-33f2fb0a2be7 + + {% for route_table in transit_gateway_route_table_associations %} + + {{ route_table.route_table_association.resourceId }} + {{ route_table.route_table_association.resourceType }} + {{ route_table.route_table_association.state }} + {{ route_table.route_table_association.transitGatewayAttachmentId }} + + {% endfor %} + +""" + +GET_TRANSIT_GATEWAY_ROUTE_TABLE_PROPAGATIONS_RESPONSE = """ + 541bc42d-9ed9-4aef-a5f7-2ea32fbdec16 + + {% for route_table in transit_gateway_route_table_propagations %} + + {{ route_table.route_table_propagation.resourceId }} + {{ route_table.route_table_propagation.resourceType }} + {{ route_table.route_table_propagation.state }} + {{ route_table.route_table_propagation.transitGatewayAttachmentId }} + + {% endfor %} + +""" diff --git a/moto/ec2/responses/transit_gateways.py b/moto/ec2/responses/transit_gateways.py index 82911967d..0de9ef586 100644 --- a/moto/ec2/responses/transit_gateways.py +++ b/moto/ec2/responses/transit_gateways.py @@ -14,6 +14,21 @@ class TransitGateways(BaseResponse): transit_gateway = self.ec2_backend.create_transit_gateway( description=description, options=options, tags=tags ) + + # creating default route table + transit_gateway_route_table = self.ec2_backend.create_transit_gateway_route_table( + transit_gateway_id=transit_gateway.id, + tags={}, + default_association_route_table=True, + default_propagation_route_table=True, + ) + transit_gateway.options[ + "AssociationDefaultRouteTableId" + ] = transit_gateway_route_table.id + transit_gateway.options[ + "PropagationDefaultRouteTableId" + ] = transit_gateway_route_table.id + template = self.response_template(CREATE_TRANSIT_GATEWAY_RESPONSE) return template.render(transit_gateway=transit_gateway) diff --git a/moto/utilities/utils.py b/moto/utilities/utils.py index fc5fe2dfd..fd5c271c4 100644 --- a/moto/utilities/utils.py +++ b/moto/utilities/utils.py @@ -34,3 +34,21 @@ def merge_multiple_dicts(*args): for d in args: result.update(d) return result + + +def filter_resources(resources, filters, attr_pairs): + """ + Used to filter resources. Usually in get and describe apis. + """ + result = resources.copy() + for resource in resources: + for attrs in attr_pairs: + values = filters.get(attrs[0]) or None + if values: + instance = getattr(resource, attrs[1]) + if (len(attrs) <= 2 and instance not in values) or ( + len(attrs) == 3 and instance.get(attrs[2]) not in values + ): + result.remove(resource) + break + return result diff --git a/tests/terraform-tests.failures.txt b/tests/terraform-tests.failures.txt index 2fc89652e..73a4783f7 100644 --- a/tests/terraform-tests.failures.txt +++ b/tests/terraform-tests.failures.txt @@ -2,10 +2,7 @@ TestAccAWSEc2TransitGatewayDxGatewayAttachmentDataSource TestAccAWSEc2TransitGatewayPeeringAttachment TestAccAWSEc2TransitGatewayPeeringAttachmentAccepter TestAccAWSEc2TransitGatewayPeeringAttachmentDataSource -TestAccAWSEc2TransitGatewayRoute TestAccAWSEc2TransitGatewayRouteTableAssociation -TestAccAWSEc2TransitGatewayRouteTablePropagation TestAccAWSEc2TransitGatewayVpcAttachment -TestAccAWSEc2TransitGatewayVpcAttachmentDataSource TestAccAWSFms TestAccAWSIAMRolePolicy \ No newline at end of file diff --git a/tests/terraform-tests.success.txt b/tests/terraform-tests.success.txt index a62f88cf2..2b1fad552 100644 --- a/tests/terraform-tests.success.txt +++ b/tests/terraform-tests.success.txt @@ -26,10 +26,18 @@ TestAccAWSEc2InstanceTypeOfferingDataSource TestAccAWSEc2InstanceTypeOfferingsDataSource TestAccAWSEc2Tag TestAccAWSEc2TransitGateway +TestAccAWSEc2TransitGatewayRoute TestAccAWSEc2TransitGatewayDataSource TestAccAWSEc2TransitGatewayRouteTable TestAccAWSEc2TransitGatewayRouteTableDataSource +TestAccAWSEc2TransitGatewayRouteTablePropagation +TestAccAWSEc2TransitGatewayVpcAttachment_basic +TestAccAWSEc2TransitGatewayVpcAttachment_disappears +TestAccAWSEc2TransitGatewayVpcAttachment_ApplianceModeSupport +TestAccAWSEc2TransitGatewayVpcAttachment_DnsSupport +TestAccAWSEc2TransitGatewayVpcAttachment_SharedTransitGateway TestAccAWSEc2TransitGatewayVpcAttachmentAccepter +TestAccAWSEc2TransitGatewayVpcAttachmentDataSource TestAccAWSEc2TransitGatewayVpnAttachmentDataSource TestAccAWSElasticBeanstalkSolutionStackDataSource TestAccAWSElbHostedZoneId diff --git a/tests/test_ec2/test_transit_gateway.py b/tests/test_ec2/test_transit_gateway.py index f0c6f411c..9e8bbed1b 100644 --- a/tests/test_ec2/test_transit_gateway.py +++ b/tests/test_ec2/test_transit_gateway.py @@ -194,8 +194,7 @@ def test_create_transit_gateway_route_table(): table.should.have.key("Tags").equals([]) tables = ec2.describe_transit_gateway_route_tables()["TransitGatewayRouteTables"] - tables.should.have.length_of(1) - tables[0].should.equal(table) + tables.should.have.length_of(2) @mock_ec2 @@ -235,14 +234,16 @@ def test_delete_transit_gateway_route_table(): ] tables = ec2.describe_transit_gateway_route_tables()["TransitGatewayRouteTables"] - tables.should.have.length_of(1) + tables.should.have.length_of(2) - ec2.delete_transit_gateway_route_table( + table = ec2.delete_transit_gateway_route_table( TransitGatewayRouteTableId=table["TransitGatewayRouteTableId"] ) + table["TransitGatewayRouteTable"].should.have.key("State").equals("deleted") + tables = ec2.describe_transit_gateway_route_tables()["TransitGatewayRouteTables"] - tables.should.have.length_of(0) + tables.should.have.length_of(2) @mock_ec2 @@ -278,7 +279,7 @@ def test_create_transit_gateway_route(): )["Route"] route.should.have.key("DestinationCidrBlock").equal("0.0.0.0") - route.should.have.key("Type").equal("TODO") + route.should.have.key("Type").equal("static") route.should.have.key("State").equal("active") @@ -299,7 +300,7 @@ def test_create_transit_gateway_route_as_blackhole(): )["Route"] route.should.have.key("DestinationCidrBlock").equal("192.168.0.1") - route.should.have.key("Type").equal("TODO") + route.should.have.key("Type").equal("static") route.should.have.key("State").equal("blackhole") @@ -329,7 +330,7 @@ def test_search_transit_gateway_routes_by_state(): )["Routes"] routes.should.equal( - [{"DestinationCidrBlock": "192.168.0.0", "Type": "TODO", "State": "active"}] + [{"DestinationCidrBlock": "192.168.0.0", "Type": "static", "State": "active"}] ) routes = ec2.search_transit_gateway_routes( @@ -338,7 +339,13 @@ def test_search_transit_gateway_routes_by_state(): )["Routes"] routes.should.equal( - [{"DestinationCidrBlock": "192.168.0.1", "Type": "TODO", "State": "blackhole"}] + [ + { + "DestinationCidrBlock": "192.168.0.1", + "Type": "static", + "State": "blackhole", + } + ] ) routes = ec2.search_transit_gateway_routes( @@ -371,7 +378,7 @@ def test_delete_transit_gateway_route(): ) response["Route"].should.equal( - {"DestinationCidrBlock": "192.168.0.0", "Type": "TODO", "State": "deleted"} + {"DestinationCidrBlock": "192.168.0.0", "Type": "static", "State": "deleted"} ) routes = ec2.search_transit_gateway_routes( @@ -380,7 +387,7 @@ def test_delete_transit_gateway_route(): )["Routes"] routes.should.equal( - [{"DestinationCidrBlock": "192.168.0.1", "Type": "TODO", "State": "active"}] + [{"DestinationCidrBlock": "192.168.0.1", "Type": "static", "State": "active"}] )