from collections import OrderedDict from typing import Any, Dict, List, Optional from moto.core.common_models import CloudFormationModel from ..exceptions import ( InvalidLaunchTemplateNameAlreadyExistsError, InvalidLaunchTemplateNameNotFoundError, InvalidLaunchTemplateNameNotFoundWithNameError, MissingParameterError, ) from ..utils import ( convert_tag_spec, generic_filter, random_launch_template_id, random_launch_template_name, utc_date_and_time, ) from .core import TaggedEC2Resource class LaunchTemplateVersion: def __init__( self, template: "LaunchTemplate", number: int, data: Dict[str, Any], description: str, ): self.template = template self.number = number self.data = data self.description = description self.create_time = utc_date_and_time() self.instance_tags = convert_tag_spec(data.get("TagSpecification", [])).get( "instance", {} ) @property def image_id(self) -> str: return self.data.get("ImageId", "") @property def instance_type(self) -> str: return self.data.get("InstanceType", "") @property def security_groups(self) -> List[str]: return self.data.get("SecurityGroups", []) @property def user_data(self) -> str: return self.data.get("UserData", "") class LaunchTemplate(TaggedEC2Resource, CloudFormationModel): def __init__( self, backend: Any, name: str, template_data: Dict[str, Any], version_description: str, tag_spec: Dict[str, Dict[str, str]], ): self.ec2_backend = backend self.name = name self.id = random_launch_template_id() self.create_time = utc_date_and_time() tag_map: Dict[str, str] = tag_spec.get("launch-template", {}) self.add_tags(tag_map) self.tags = self.get_tags() self.versions: List[LaunchTemplateVersion] = [] self.create_version(template_data, version_description) self.default_version_number = 1 def create_version( self, data: Dict[str, Any], description: str ) -> LaunchTemplateVersion: num = len(self.versions) + 1 version = LaunchTemplateVersion(self, num, data, description) self.versions.append(version) return version def is_default(self, version: LaunchTemplateVersion) -> bool: return self.default_version == version.number # type: ignore def get_version(self, num: Any) -> LaunchTemplateVersion: if str(num).lower() == "$latest": return self.versions[-1] if str(num).lower() == "$default": return self.default_version() return self.versions[int(num) - 1] def default_version(self) -> LaunchTemplateVersion: return self.versions[self.default_version_number - 1] def latest_version(self) -> LaunchTemplateVersion: return self.versions[-1] @property def latest_version_number(self) -> int: return self.latest_version().number @property def physical_resource_id(self) -> str: return self.id def get_filter_value( self, filter_name: str, method_name: Optional[str] = None ) -> Any: if filter_name == "launch-template-name": return self.name else: return super().get_filter_value(filter_name, "DescribeLaunchTemplates") @staticmethod def cloudformation_name_type() -> str: return "LaunchTemplateName" @staticmethod def cloudformation_type() -> str: # https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-launchtemplate.html return "AWS::EC2::LaunchTemplate" @classmethod def create_from_cloudformation_json( # type: ignore[misc] cls, resource_name: str, cloudformation_json: Any, account_id: str, region_name: str, **kwargs: Any, ) -> "LaunchTemplate": from ..models import ec2_backends backend = ec2_backends[account_id][region_name] properties = cloudformation_json["Properties"] name = properties.get("LaunchTemplateName") data = properties.get("LaunchTemplateData") description = properties.get("VersionDescription") tag_spec = convert_tag_spec( properties.get("TagSpecifications", {}), tag_key="Tags" ) if name is None: name = random_launch_template_name() launch_template = backend.create_launch_template( name, description, data, tag_spec ) return launch_template @classmethod def update_from_cloudformation_json( # type: ignore[misc] cls, original_resource: Any, new_resource_name: str, cloudformation_json: Any, account_id: str, region_name: str, ) -> "LaunchTemplate": from ..models import ec2_backends backend = ec2_backends[account_id][region_name] properties = cloudformation_json["Properties"] data = properties.get("LaunchTemplateData") description = properties.get("VersionDescription") launch_template = backend.get_launch_template(original_resource.id) launch_template.create_version(data, description) return launch_template def delete( self, account_id: str, region_name: str, ) -> None: from ..models import ec2_backends backend = ec2_backends[account_id][region_name] backend.delete_launch_template(self.name, None) @classmethod def has_cfn_attr(cls, attr: str) -> bool: return attr in [ "DefaultVersionNumber", "LaunchTemplateId", "LatestVersionNumber", ] def get_cfn_attribute(self, attribute_name: str) -> str: from moto.cloudformation.exceptions import UnformattedGetAttTemplateException if attribute_name == "DefaultVersionNumber": return str(self.default_version_number) if attribute_name == "LaunchTemplateId": return self.id if attribute_name == "LatestVersionNumber": return str(self.latest_version_number) raise UnformattedGetAttTemplateException() class LaunchTemplateBackend: def __init__(self) -> None: self.launch_template_name_to_ids: Dict[str, str] = {} self.launch_templates: Dict[str, LaunchTemplate] = OrderedDict() self.launch_template_insert_order: List[str] = [] def create_launch_template( self, name: str, description: str, template_data: Dict[str, Any], tag_spec: Dict[str, Any], ) -> LaunchTemplate: if name in self.launch_template_name_to_ids: raise InvalidLaunchTemplateNameAlreadyExistsError() template = LaunchTemplate(self, name, template_data, description, tag_spec) self.launch_templates[template.id] = template self.launch_template_name_to_ids[template.name] = template.id self.launch_template_insert_order.append(template.id) return template def modify_launch_template( self, default_version: str, template_name: Optional[str] = None, template_id: Optional[str] = None, ) -> LaunchTemplate: if template_name: template_id = self.launch_template_name_to_ids.get(template_name) if template_id is None: raise MissingParameterError("launch template ID or launch template name") if template_id not in self.launch_templates: raise InvalidLaunchTemplateNameNotFoundError() self.launch_templates[template_id].default_version_number = int(default_version) template = self.launch_templates[template_id] return template def get_launch_template(self, template_id: str) -> LaunchTemplate: return self.launch_templates[template_id] def get_launch_template_by_name(self, name: str) -> LaunchTemplate: if name not in self.launch_template_name_to_ids: raise InvalidLaunchTemplateNameNotFoundWithNameError(name) return self.get_launch_template(self.launch_template_name_to_ids[name]) def delete_launch_template(self, name: str, tid: Optional[str]) -> LaunchTemplate: if name: tid = self.launch_template_name_to_ids.get(name) if tid is None: raise MissingParameterError("launch template ID or launch template name") if tid not in self.launch_templates: raise InvalidLaunchTemplateNameNotFoundError() template = self.launch_templates.pop(tid) self.launch_template_name_to_ids.pop(template.name, None) return template def describe_launch_templates( self, template_names: Optional[List[str]] = None, template_ids: Optional[List[str]] = None, filters: Any = None, ) -> List[LaunchTemplate]: if template_names and not template_ids: template_ids = [] for name in template_names: if name not in self.launch_template_name_to_ids: raise InvalidLaunchTemplateNameNotFoundError() template_ids.append(self.launch_template_name_to_ids[name]) if template_ids: templates = [ self.launch_templates[tid] for tid in template_ids if tid in self.launch_templates ] else: templates = list(self.launch_templates.values()) return generic_filter(filters, templates) def get_launch_template_data(self, instance_id: str) -> Any: return self.get_instance(instance_id) # type: ignore[attr-defined]
Memory