From 84100c44831a2060ceb33a36ecdab339bbf68d0b Mon Sep 17 00:00:00 2001 From: usmankb Date: Wed, 29 Apr 2020 00:28:19 +0530 Subject: [PATCH 1/3] enhancement Create-VPC-endpoint --- moto/ec2/models.py | 98 +++++++++++++++++++++++++++++ moto/ec2/responses/vpcs.py | 65 +++++++++++++++++++ moto/ec2/utils.py | 16 +++++ tests/test_ec2/test_route_tables.py | 36 +++++++++++ 4 files changed, 215 insertions(+) diff --git a/moto/ec2/models.py b/moto/ec2/models.py index ce9c3ef5b..118fc6804 100644 --- a/moto/ec2/models.py +++ b/moto/ec2/models.py @@ -104,6 +104,7 @@ from .utils import ( random_internet_gateway_id, random_ip, random_ipv6_cidr, + randor_ipv4_cidr, random_launch_template_id, random_nat_gateway_id, random_key_pair, @@ -112,6 +113,8 @@ from .utils import ( random_reservation_id, random_route_table_id, generate_route_id, + generate_vpc_end_point_id, + create_dns_entries, split_route_id, random_security_group_id, random_snapshot_id, @@ -2735,6 +2738,7 @@ class VPCBackend(object): def __init__(self): self.vpcs = {} + self.vpc_end_points = {} self.vpc_refs[self.__class__].add(weakref.ref(self)) super(VPCBackend, self).__init__() @@ -2877,6 +2881,66 @@ class VPCBackend(object): vpc = self.get_vpc(vpc_id) return vpc.associate_vpc_cidr_block(cidr_block, amazon_provided_ipv6_cidr_block) + def create_vpc_endpoint(self, + vpc_id, + service_name, + type=None, + policy_document=False, + route_table_ids=None, + subnet_ids=[], + network_interface_ids=[], + dns_entries=None, + client_token=None, + security_group=None, + tag_specifications=None, + private_dns_enabled=None + ): + + vpc_endpoint_id = generate_vpc_end_point_id(vpc_id) + + #validates if vpc is present or not. + self.get_vpc(vpc_id) + + if type == "interface" or "Interface ": + + network_interface_ids = [] + for subnet_id in subnet_ids: + self.get_subnet(subnet_id) + eni = self.create_network_interface(subnet_id, random_private_ip()) + network_interface_ids.append(eni.id) + + dns_entries = create_dns_entries(service_name, vpc_endpoint_id) + + else : + # considering gateway if type is not mentioned. + service_destination_cidr = randor_ipv4_cidr() + + for route_table_id in route_table_ids: + self.create_route( + route_table_id, + service_destination_cidr + ) + + vpc_end_point = VPCEndPoint( + vpc_endpoint_id, + vpc_id, + service_name, + type, + policy_document, + route_table_ids, + subnet_ids, + network_interface_ids, + [dns_entries], + client_token, + security_group, + tag_specifications, + private_dns_enabled + ) + + self.vpc_end_points[vpc_endpoint_id] = vpc_end_point + + return vpc_end_point + class VPCPeeringConnectionStatus(object): def __init__(self, code="initiating-request", message=""): @@ -3485,6 +3549,40 @@ class Route(object): return route_table +class VPCEndPoint(TaggedEC2Resource): + def __init__( + self, + id, + vpc_id, + service_name, + type=None, + policy_document=False, + route_table_ids=None, + subnet_ids =None, + network_interface_ids=None, + dns_entries=None, + client_token=None, + security_group=None, + tag_specifications=None, + private_dns_enabled=None, + ): + + self.id = id + self.vpc_id = vpc_id + self.service_name = service_name + self.type = type + self.policy_document = policy_document + self.route_table_ids = route_table_ids + self.network_interface_ids = network_interface_ids + self.subnet_ids = subnet_ids + self.client_token = client_token + self.security_group = security_group + self.tag_specifications = tag_specifications + self.private_dns_enabled = private_dns_enabled + self.created_at = datetime.utcnow() + self.dns_entries = dns_entries + + class RouteBackend(object): def __init__(self): super(RouteBackend, self).__init__() diff --git a/moto/ec2/responses/vpcs.py b/moto/ec2/responses/vpcs.py index 0fd198378..2af4c0b29 100644 --- a/moto/ec2/responses/vpcs.py +++ b/moto/ec2/responses/vpcs.py @@ -163,6 +163,36 @@ class VPCs(BaseResponse): cidr_block_state="disassociating", ) + def create_vpc_endpoint(self): + vpc_id = self._get_param("VpcId") + service_name = self._get_param("ServiceName") + route_table_ids = self._get_multi_param("RouteTableId") + subnet_ids = self._get_multi_param("SubnetId") + type = self._get_param("VpcEndpointType") + policy_document = self._get_param("PolicyDocument") + client_token = self._get_param("ClientToken") + tag_specifications = self._get_param("TagSpecifications") + private_dns_enabled = self._get_param("PrivateDNSEnabled") + security_group = self._get_param("SecurityGroup") + + vpc_end_point = self.ec2_backend.create_vpc_endpoint( + vpc_id=vpc_id, + service_name=service_name, + type=type, + policy_document=policy_document, + route_table_ids=route_table_ids, + subnet_ids=subnet_ids, + client_token=client_token, + security_group=security_group, + tag_specifications=tag_specifications, + private_dns_enabled=private_dns_enabled + ) + + template = self.response_template(CREATE_VPC_END_POINT) + return template.render( + vpc_end_point=vpc_end_point + ) + CREATE_VPC_RESPONSE = """ @@ -384,3 +414,38 @@ IPV6_DISASSOCIATE_VPC_CIDR_BLOCK_RESPONSE = """ """ + +CREATE_VPC_END_POINT = """ + + {{ vpc_end_point.policy_document }} + available + false + {{ vpc_end_point.service_name }} + {{ vpc_end_point.vpc_id }} + {{ vpc_end_point.id }} + + {% for routeid in vpc_end_point.route_table_ids %} + {{ routeid }} + {% endfor %} + + + {% for network_interface_id in vpc_end_point.network_interface_ids %} + {{ network_interface_id }} + {% endfor %} + + + {% for subnetId in vpc_end_point.subnet_ids %} + {{ subnetId }} + {% endfor %} + + + {% for entry in vpc_end_point.dns_entries %} + + {{ entry["hosted_zone_id"] }} + {{ entry["dns_name"] }} + + {% endfor %} + + {{ vpc_end_point.created_at }} + +""" \ No newline at end of file diff --git a/moto/ec2/utils.py b/moto/ec2/utils.py index 3b363e45d..408ee9be5 100644 --- a/moto/ec2/utils.py +++ b/moto/ec2/utils.py @@ -181,6 +181,10 @@ def random_ip(): ) +def randor_ipv4_cidr(): + return "10.0.{}.{}/16".format(random.randint(0, 255), random.randint(0, 255)) + + def random_ipv6_cidr(): return "2400:6500:{}:{}::/56".format(random_resource_id(4), random_resource_id(4)) @@ -189,6 +193,18 @@ def generate_route_id(route_table_id, cidr_block): return "%s~%s" % (route_table_id, cidr_block) +def generate_vpc_end_point_id(vpc_id): + return "%s-%s" % ('vpce', vpc_id[4:]) + + +def create_dns_entries(service_name, vpc_endpoint_id): + dns_entries = {} + dns_entries["dns_name"] = "{}-{}.{}".format(vpc_endpoint_id, + random_resource_id(8), service_name) + dns_entries["hosted_zone_id"] = random_resource_id(13).upper() + return dns_entries + + def split_route_id(route_id): values = route_id.split("~") return values[0], values[1] diff --git a/tests/test_ec2/test_route_tables.py b/tests/test_ec2/test_route_tables.py index 347464691..182776f95 100644 --- a/tests/test_ec2/test_route_tables.py +++ b/tests/test_ec2/test_route_tables.py @@ -618,3 +618,39 @@ def test_describe_route_tables_with_nat_gateway(): nat_gw_routes[0]["DestinationCidrBlock"].should.equal("0.0.0.0/0") nat_gw_routes[0]["NatGatewayId"].should.equal(nat_gw_id) nat_gw_routes[0]["State"].should.equal("active") + + +@mock_ec2 +def test_create_vpc_end_point(): + + ec2 = boto3.client("ec2", region_name="us-west-1") + vpc = ec2.create_vpc(CidrBlock="10.0.0.0/16") + subnet = ec2.create_subnet(VpcId=vpc["Vpc"]["VpcId"], + CidrBlock="10.0.0.0/24") + + route_table = ec2.create_route_table(VpcId=vpc["Vpc"]["VpcId"]) + + vpc_end_point = ec2.create_vpc_endpoint( + VpcId=vpc["Vpc"]["VpcId"], + ServiceName="com.amazonaws.us-east-1.s3", + RouteTableIds=[route_table["RouteTable"]["RouteTableId"]] + ) + + vpc_end_point["VpcEndpoint"]["ServiceName"].\ + should.equal("com.amazonaws.us-east-1.s3") + vpc_end_point["VpcEndpoint"]["RouteTableIds"][0].\ + should.equal(route_table["RouteTable"]["RouteTableId"]) + vpc_end_point["VpcEndpoint"]["VpcId"].should.equal(vpc["Vpc"]["VpcId"]) + + vpc_end_point = ec2.create_vpc_endpoint( + VpcId=vpc["Vpc"]["VpcId"], + ServiceName="com.amazonaws.us-east-1.s3", + SubnetIds=[subnet["Subnet"]["SubnetId"]], + VpcEndpointType="interface" + ) + + vpc_end_point["VpcEndpoint"]["ServiceName"].\ + should.equal("com.amazonaws.us-east-1.s3") + vpc_end_point["VpcEndpoint"]["SubnetIds"][0].\ + should.equal(subnet["Subnet"]["SubnetId"]) + vpc_end_point["VpcEndpoint"]["VpcId"].should.equal(vpc["Vpc"]["VpcId"]) From f4888da33498592d6a1e25a073ddf4673947afa6 Mon Sep 17 00:00:00 2001 From: usmankb Date: Wed, 29 Apr 2020 18:02:02 +0530 Subject: [PATCH 2/3] added test asserts and review comments --- moto/ec2/models.py | 6 ++++-- moto/ec2/responses/vpcs.py | 2 ++ tests/test_ec2/test_route_tables.py | 19 +++++++++++++++++++ 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/moto/ec2/models.py b/moto/ec2/models.py index 118fc6804..c35fa339d 100644 --- a/moto/ec2/models.py +++ b/moto/ec2/models.py @@ -2901,7 +2901,7 @@ class VPCBackend(object): #validates if vpc is present or not. self.get_vpc(vpc_id) - if type == "interface" or "Interface ": + if type and type.lower() == "interface": network_interface_ids = [] for subnet_id in subnet_ids: @@ -2920,6 +2920,8 @@ class VPCBackend(object): route_table_id, service_destination_cidr ) + if dns_entries: + dns_entries = [dns_entries] vpc_end_point = VPCEndPoint( vpc_endpoint_id, @@ -2930,7 +2932,7 @@ class VPCBackend(object): route_table_ids, subnet_ids, network_interface_ids, - [dns_entries], + dns_entries, client_token, security_group, tag_specifications, diff --git a/moto/ec2/responses/vpcs.py b/moto/ec2/responses/vpcs.py index 2af4c0b29..4b0aa76d8 100644 --- a/moto/ec2/responses/vpcs.py +++ b/moto/ec2/responses/vpcs.py @@ -439,12 +439,14 @@ CREATE_VPC_END_POINT = """ {{ entry["dns_name"] }} {% endfor %} + {% endif %} {{ vpc_end_point.created_at }} diff --git a/tests/test_ec2/test_route_tables.py b/tests/test_ec2/test_route_tables.py index 182776f95..f8be8dc80 100644 --- a/tests/test_ec2/test_route_tables.py +++ b/tests/test_ec2/test_route_tables.py @@ -630,6 +630,7 @@ def test_create_vpc_end_point(): route_table = ec2.create_route_table(VpcId=vpc["Vpc"]["VpcId"]) + # test without any end point type specified vpc_end_point = ec2.create_vpc_endpoint( VpcId=vpc["Vpc"]["VpcId"], ServiceName="com.amazonaws.us-east-1.s3", @@ -641,7 +642,24 @@ def test_create_vpc_end_point(): vpc_end_point["VpcEndpoint"]["RouteTableIds"][0].\ should.equal(route_table["RouteTable"]["RouteTableId"]) vpc_end_point["VpcEndpoint"]["VpcId"].should.equal(vpc["Vpc"]["VpcId"]) + vpc_end_point["VpcEndpoint"]["DnsEntries"].should.have.length_of(0) + # test with any end point type as gateway + vpc_end_point = ec2.create_vpc_endpoint( + VpcId=vpc["Vpc"]["VpcId"], + ServiceName="com.amazonaws.us-east-1.s3", + RouteTableIds=[route_table["RouteTable"]["RouteTableId"]], + VpcEndpointType="gateway" + ) + + vpc_end_point["VpcEndpoint"]["ServiceName"]. \ + should.equal("com.amazonaws.us-east-1.s3") + vpc_end_point["VpcEndpoint"]["RouteTableIds"][0]. \ + should.equal(route_table["RouteTable"]["RouteTableId"]) + vpc_end_point["VpcEndpoint"]["VpcId"].should.equal(vpc["Vpc"]["VpcId"]) + vpc_end_point["VpcEndpoint"]["DnsEntries"].should.have.length_of(0) + + # test with end point type as interface vpc_end_point = ec2.create_vpc_endpoint( VpcId=vpc["Vpc"]["VpcId"], ServiceName="com.amazonaws.us-east-1.s3", @@ -654,3 +672,4 @@ def test_create_vpc_end_point(): vpc_end_point["VpcEndpoint"]["SubnetIds"][0].\ should.equal(subnet["Subnet"]["SubnetId"]) vpc_end_point["VpcEndpoint"]["VpcId"].should.equal(vpc["Vpc"]["VpcId"]) + len(vpc_end_point["VpcEndpoint"]["DnsEntries"]).should.be.greater_than(0) \ No newline at end of file From 2d0087d500ad392fd017b603f94b9030a63f5a52 Mon Sep 17 00:00:00 2001 From: Bert Blommers Date: Wed, 29 Apr 2020 16:29:25 +0100 Subject: [PATCH 3/3] Linting --- moto/ec2/models.py | 42 ++++++++++++++-------------- moto/ec2/responses/vpcs.py | 26 ++++++++--------- moto/ec2/utils.py | 7 +++-- tests/test_ec2/test_route_tables.py | 43 ++++++++++++++++------------- 4 files changed, 60 insertions(+), 58 deletions(-) diff --git a/moto/ec2/models.py b/moto/ec2/models.py index c35fa339d..edc216eb3 100644 --- a/moto/ec2/models.py +++ b/moto/ec2/models.py @@ -2881,24 +2881,25 @@ class VPCBackend(object): vpc = self.get_vpc(vpc_id) return vpc.associate_vpc_cidr_block(cidr_block, amazon_provided_ipv6_cidr_block) - def create_vpc_endpoint(self, - vpc_id, - service_name, - type=None, - policy_document=False, - route_table_ids=None, - subnet_ids=[], - network_interface_ids=[], - dns_entries=None, - client_token=None, - security_group=None, - tag_specifications=None, - private_dns_enabled=None - ): + def create_vpc_endpoint( + self, + vpc_id, + service_name, + type=None, + policy_document=False, + route_table_ids=None, + subnet_ids=[], + network_interface_ids=[], + dns_entries=None, + client_token=None, + security_group=None, + tag_specifications=None, + private_dns_enabled=None, + ): vpc_endpoint_id = generate_vpc_end_point_id(vpc_id) - #validates if vpc is present or not. + # validates if vpc is present or not. self.get_vpc(vpc_id) if type and type.lower() == "interface": @@ -2911,15 +2912,12 @@ class VPCBackend(object): dns_entries = create_dns_entries(service_name, vpc_endpoint_id) - else : + else: # considering gateway if type is not mentioned. service_destination_cidr = randor_ipv4_cidr() for route_table_id in route_table_ids: - self.create_route( - route_table_id, - service_destination_cidr - ) + self.create_route(route_table_id, service_destination_cidr) if dns_entries: dns_entries = [dns_entries] @@ -2936,7 +2934,7 @@ class VPCBackend(object): client_token, security_group, tag_specifications, - private_dns_enabled + private_dns_enabled, ) self.vpc_end_points[vpc_endpoint_id] = vpc_end_point @@ -3560,7 +3558,7 @@ class VPCEndPoint(TaggedEC2Resource): type=None, policy_document=False, route_table_ids=None, - subnet_ids =None, + subnet_ids=None, network_interface_ids=None, dns_entries=None, client_token=None, diff --git a/moto/ec2/responses/vpcs.py b/moto/ec2/responses/vpcs.py index 4b0aa76d8..59222207d 100644 --- a/moto/ec2/responses/vpcs.py +++ b/moto/ec2/responses/vpcs.py @@ -176,22 +176,20 @@ class VPCs(BaseResponse): security_group = self._get_param("SecurityGroup") vpc_end_point = self.ec2_backend.create_vpc_endpoint( - vpc_id=vpc_id, - service_name=service_name, - type=type, - policy_document=policy_document, - route_table_ids=route_table_ids, - subnet_ids=subnet_ids, - client_token=client_token, - security_group=security_group, - tag_specifications=tag_specifications, - private_dns_enabled=private_dns_enabled + vpc_id=vpc_id, + service_name=service_name, + type=type, + policy_document=policy_document, + route_table_ids=route_table_ids, + subnet_ids=subnet_ids, + client_token=client_token, + security_group=security_group, + tag_specifications=tag_specifications, + private_dns_enabled=private_dns_enabled, ) template = self.response_template(CREATE_VPC_END_POINT) - return template.render( - vpc_end_point=vpc_end_point - ) + return template.render(vpc_end_point=vpc_end_point) CREATE_VPC_RESPONSE = """ @@ -450,4 +448,4 @@ CREATE_VPC_END_POINT = """ """ diff --git a/moto/ec2/utils.py b/moto/ec2/utils.py index 408ee9be5..c07c470a9 100644 --- a/moto/ec2/utils.py +++ b/moto/ec2/utils.py @@ -194,13 +194,14 @@ def generate_route_id(route_table_id, cidr_block): def generate_vpc_end_point_id(vpc_id): - return "%s-%s" % ('vpce', vpc_id[4:]) + return "%s-%s" % ("vpce", vpc_id[4:]) def create_dns_entries(service_name, vpc_endpoint_id): dns_entries = {} - dns_entries["dns_name"] = "{}-{}.{}".format(vpc_endpoint_id, - random_resource_id(8), service_name) + dns_entries["dns_name"] = "{}-{}.{}".format( + vpc_endpoint_id, random_resource_id(8), service_name + ) dns_entries["hosted_zone_id"] = random_resource_id(13).upper() return dns_entries diff --git a/tests/test_ec2/test_route_tables.py b/tests/test_ec2/test_route_tables.py index f8be8dc80..a64fbae1a 100644 --- a/tests/test_ec2/test_route_tables.py +++ b/tests/test_ec2/test_route_tables.py @@ -625,8 +625,7 @@ def test_create_vpc_end_point(): ec2 = boto3.client("ec2", region_name="us-west-1") vpc = ec2.create_vpc(CidrBlock="10.0.0.0/16") - subnet = ec2.create_subnet(VpcId=vpc["Vpc"]["VpcId"], - CidrBlock="10.0.0.0/24") + subnet = ec2.create_subnet(VpcId=vpc["Vpc"]["VpcId"], CidrBlock="10.0.0.0/24") route_table = ec2.create_route_table(VpcId=vpc["Vpc"]["VpcId"]) @@ -634,13 +633,15 @@ def test_create_vpc_end_point(): vpc_end_point = ec2.create_vpc_endpoint( VpcId=vpc["Vpc"]["VpcId"], ServiceName="com.amazonaws.us-east-1.s3", - RouteTableIds=[route_table["RouteTable"]["RouteTableId"]] - ) + RouteTableIds=[route_table["RouteTable"]["RouteTableId"]], + ) - vpc_end_point["VpcEndpoint"]["ServiceName"].\ - should.equal("com.amazonaws.us-east-1.s3") - vpc_end_point["VpcEndpoint"]["RouteTableIds"][0].\ - should.equal(route_table["RouteTable"]["RouteTableId"]) + vpc_end_point["VpcEndpoint"]["ServiceName"].should.equal( + "com.amazonaws.us-east-1.s3" + ) + vpc_end_point["VpcEndpoint"]["RouteTableIds"][0].should.equal( + route_table["RouteTable"]["RouteTableId"] + ) vpc_end_point["VpcEndpoint"]["VpcId"].should.equal(vpc["Vpc"]["VpcId"]) vpc_end_point["VpcEndpoint"]["DnsEntries"].should.have.length_of(0) @@ -649,13 +650,15 @@ def test_create_vpc_end_point(): VpcId=vpc["Vpc"]["VpcId"], ServiceName="com.amazonaws.us-east-1.s3", RouteTableIds=[route_table["RouteTable"]["RouteTableId"]], - VpcEndpointType="gateway" + VpcEndpointType="gateway", ) - vpc_end_point["VpcEndpoint"]["ServiceName"]. \ - should.equal("com.amazonaws.us-east-1.s3") - vpc_end_point["VpcEndpoint"]["RouteTableIds"][0]. \ - should.equal(route_table["RouteTable"]["RouteTableId"]) + vpc_end_point["VpcEndpoint"]["ServiceName"].should.equal( + "com.amazonaws.us-east-1.s3" + ) + vpc_end_point["VpcEndpoint"]["RouteTableIds"][0].should.equal( + route_table["RouteTable"]["RouteTableId"] + ) vpc_end_point["VpcEndpoint"]["VpcId"].should.equal(vpc["Vpc"]["VpcId"]) vpc_end_point["VpcEndpoint"]["DnsEntries"].should.have.length_of(0) @@ -664,12 +667,14 @@ def test_create_vpc_end_point(): VpcId=vpc["Vpc"]["VpcId"], ServiceName="com.amazonaws.us-east-1.s3", SubnetIds=[subnet["Subnet"]["SubnetId"]], - VpcEndpointType="interface" + VpcEndpointType="interface", ) - vpc_end_point["VpcEndpoint"]["ServiceName"].\ - should.equal("com.amazonaws.us-east-1.s3") - vpc_end_point["VpcEndpoint"]["SubnetIds"][0].\ - should.equal(subnet["Subnet"]["SubnetId"]) + vpc_end_point["VpcEndpoint"]["ServiceName"].should.equal( + "com.amazonaws.us-east-1.s3" + ) + vpc_end_point["VpcEndpoint"]["SubnetIds"][0].should.equal( + subnet["Subnet"]["SubnetId"] + ) vpc_end_point["VpcEndpoint"]["VpcId"].should.equal(vpc["Vpc"]["VpcId"]) - len(vpc_end_point["VpcEndpoint"]["DnsEntries"]).should.be.greater_than(0) \ No newline at end of file + len(vpc_end_point["VpcEndpoint"]["DnsEntries"]).should.be.greater_than(0)