From fa79dabd3667534382cc7e7be4a1736781f5c276 Mon Sep 17 00:00:00 2001 From: Simon Walker Date: Fri, 1 Nov 2024 17:02:59 +0000 Subject: [PATCH 1/3] Make create_vpc function more user friendly We allow a default fallback for tag_specification as this is not always required --- tests/aws/services/ec2/test_ec2.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/aws/services/ec2/test_ec2.py b/tests/aws/services/ec2/test_ec2.py index b2c3f2a03ae7f..d5a6576972710 100644 --- a/tests/aws/services/ec2/test_ec2.py +++ b/tests/aws/services/ec2/test_ec2.py @@ -48,8 +48,9 @@ def create_vpc(aws_client): def _create_vpc( cidr_block: str, - tag_specifications: list[dict], + tag_specifications: list[dict] | None = None, ): + tag_specifications = tag_specifications or [] vpc = aws_client.ec2.create_vpc(CidrBlock=cidr_block, TagSpecifications=tag_specifications) vpcs.append(vpc["Vpc"]["VpcId"]) return vpc From b6905343dac212f1bd424981c1784883aaab9f60 Mon Sep 17 00:00:00 2001 From: Simon Walker Date: Fri, 1 Nov 2024 17:05:51 +0000 Subject: [PATCH 2/3] Support customisation of the VPC id --- .../localstack/services/ec2/patches.py | 46 ++++++++++++++++++- tests/aws/services/ec2/test_ec2.py | 14 ++++++ 2 files changed, 59 insertions(+), 1 deletion(-) diff --git a/localstack-core/localstack/services/ec2/patches.py b/localstack-core/localstack/services/ec2/patches.py index ebd5ab3cc96e2..2df31e54c82dc 100644 --- a/localstack-core/localstack/services/ec2/patches.py +++ b/localstack-core/localstack/services/ec2/patches.py @@ -1,4 +1,6 @@ import logging +import random +import string from typing import Optional from moto.ec2 import models as ec2_models @@ -9,11 +11,43 @@ InvalidSubnetDuplicateCustomIdError, InvalidVpcDuplicateCustomIdError, ) +from localstack.utils.id_generator import ( + ExistingIds, + ResourceIdentifier, + Tags, + localstack_id, + localstack_id_manager, +) from localstack.utils.patch import patch LOG = logging.getLogger(__name__) +@localstack_id +def generate_vpc_id( + resource_identifier: ResourceIdentifier, + existing_ids: ExistingIds = None, + tags: Tags = None, +) -> str: + suffix = "".join(random.choice(string.ascii_letters) for _ in range(17)) + return f"vpc-{suffix}" + + +class VpcIdentifier(ResourceIdentifier): + service = "ec2" + resource = "vpc" + + def __init__(self, account_id: str, region: str, cidr_block: str): + super().__init__(account_id, region, name=cidr_block) + + def generate(self, existing_ids: ExistingIds = None, tags: Tags = None) -> str: + return generate_vpc_id( + resource_identifier=self, + existing_ids=existing_ids, + tags=tags, + ) + + def apply_patches(): @patch(ec2_models.subnets.SubnetBackend.create_subnet) def ec2_create_subnet( @@ -82,6 +116,7 @@ def ec2_create_security_group( def ec2_create_vpc( fn: ec2_models.vpcs.VPCBackend.create_vpc, self: ec2_models.vpcs.VPCBackend, + cidr_block: str, *args, tags: Optional[list[dict[str, str]]] = None, is_default: bool = False, @@ -92,12 +127,21 @@ def ec2_create_vpc( custom_ids = [tag["Value"] for tag in tags if tag["Key"] == TAG_KEY_CUSTOM_ID] custom_id = custom_ids[0] if len(custom_ids) > 0 else None + # try to generate a custom id from the id store + if not custom_id: + resource_identifier = VpcIdentifier(self.account_id, self.region_name, cidr_block) + id = localstack_id_manager.get_custom_id(resource_identifier) + if id: + custom_id = id + # Check if custom id is unique if custom_id and custom_id in self.vpcs: raise InvalidVpcDuplicateCustomIdError(custom_id) # Generate VPC with moto library - result: ec2_models.vpcs.VPC = fn(self, *args, tags=tags, is_default=is_default, **kwargs) + result: ec2_models.vpcs.VPC = fn( + self, cidr_block, *args, tags=tags, is_default=is_default, **kwargs + ) vpc_id = result.id if custom_id: diff --git a/tests/aws/services/ec2/test_ec2.py b/tests/aws/services/ec2/test_ec2.py index d5a6576972710..01952fe0aa7ee 100644 --- a/tests/aws/services/ec2/test_ec2.py +++ b/tests/aws/services/ec2/test_ec2.py @@ -11,6 +11,7 @@ ) from localstack.constants import TAG_KEY_CUSTOM_ID +from localstack.services.ec2.patches import VpcIdentifier from localstack.testing.pytest import markers from localstack.utils.strings import short_uid from localstack.utils.sync import retry @@ -817,3 +818,16 @@ def test_pickle_ec2_backend(pickle_backends, aws_client): _ = aws_client.ec2.describe_account_attributes() pickle_backends(ec2_backends) assert pickle_backends(ec2_backends) + + +@markers.aws.only_localstack +def test_create_specific_vpc_id(account_id, region_name, create_vpc, set_resource_custom_id): + cidr_block = "10.0.0.0/16" + custom_id = "my-custom-id" + set_resource_custom_id( + VpcIdentifier(account_id=account_id, region=region_name, cidr_block=cidr_block), + f"vpc-{custom_id}", + ) + + vpc = create_vpc(cidr_block=cidr_block) + assert vpc["Vpc"]["VpcId"] == f"vpc-{custom_id}" From 732e3932b52da370806b1d5166e186ebf8b54fc3 Mon Sep 17 00:00:00 2001 From: Simon Walker Date: Wed, 13 Nov 2024 17:28:17 +0000 Subject: [PATCH 3/3] Simplify vpc id generation based on review feedback --- .../localstack/services/ec2/patches.py | 22 +++++-------------- 1 file changed, 5 insertions(+), 17 deletions(-) diff --git a/localstack-core/localstack/services/ec2/patches.py b/localstack-core/localstack/services/ec2/patches.py index 2df31e54c82dc..7f1dbdb6f9959 100644 --- a/localstack-core/localstack/services/ec2/patches.py +++ b/localstack-core/localstack/services/ec2/patches.py @@ -1,9 +1,8 @@ import logging -import random -import string from typing import Optional from moto.ec2 import models as ec2_models +from moto.utilities.id_generator import Tags from localstack.constants import TAG_KEY_CUSTOM_ID from localstack.services.ec2.exceptions import ( @@ -14,9 +13,7 @@ from localstack.utils.id_generator import ( ExistingIds, ResourceIdentifier, - Tags, localstack_id, - localstack_id_manager, ) from localstack.utils.patch import patch @@ -29,8 +26,8 @@ def generate_vpc_id( existing_ids: ExistingIds = None, tags: Tags = None, ) -> str: - suffix = "".join(random.choice(string.ascii_letters) for _ in range(17)) - return f"vpc-{suffix}" + # We return an empty string here to differentiate between when a custom ID was used, or when it was randomly generated by `moto`. + return "" class VpcIdentifier(ResourceIdentifier): @@ -122,17 +119,8 @@ def ec2_create_vpc( is_default: bool = False, **kwargs, ): - # Extract custom ID from tags if it exists - tags: list[dict[str, str]] = tags or [] - custom_ids = [tag["Value"] for tag in tags if tag["Key"] == TAG_KEY_CUSTOM_ID] - custom_id = custom_ids[0] if len(custom_ids) > 0 else None - - # try to generate a custom id from the id store - if not custom_id: - resource_identifier = VpcIdentifier(self.account_id, self.region_name, cidr_block) - id = localstack_id_manager.get_custom_id(resource_identifier) - if id: - custom_id = id + resource_identifier = VpcIdentifier(self.account_id, self.region_name, cidr_block) + custom_id = resource_identifier.generate(tags=tags) # Check if custom id is unique if custom_id and custom_id in self.vpcs: