from typing import Optional, Any, Dict, List, Union from phi.aws.api_client import AwsApiClient from phi.aws.resource.base import AwsResource from phi.aws.resource.ec2.subnet import Subnet from phi.cli.console import print_info from phi.utils.log import logger class TargetGroup(AwsResource): """ Reference: - https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/elbv2/client/create_target_group.html """ resource_type: Optional[str] = "TargetGroup" service_name: str = "elbv2" # Name of the Target Group name: str protocol: Optional[str] = None protocol_version: Optional[str] = None port: Optional[int] = None vpc_id: Optional[str] = None subnets: Optional[List[Union[str, Subnet]]] = None health_check_protocol: Optional[str] = None health_check_port: Optional[str] = None health_check_enabled: Optional[bool] = None health_check_path: Optional[str] = None health_check_interval_seconds: Optional[int] = None health_check_timeout_seconds: Optional[int] = None healthy_threshold_count: Optional[int] = None unhealthy_threshold_count: Optional[int] = None matcher: Optional[Dict[str, str]] = None target_type: Optional[str] = None tags: Optional[List[Dict[str, str]]] = None ip_address_type: Optional[str] = None def _create(self, aws_client: AwsApiClient) -> bool: """Creates the Target Group Args: aws_client: The AwsApiClient for the current Target Group """ print_info(f"Creating {self.get_resource_type()}: {self.get_resource_name()}") # create a dict of args which are not null, otherwise aws type validation fails not_null_args: Dict[str, Any] = {} # Get vpc_id vpc_id = self.vpc_id if vpc_id is None and self.subnets is not None: from phi.aws.resource.ec2.subnet import get_vpc_id_from_subnet_ids subnet_ids = [] for subnet in self.subnets: if isinstance(subnet, Subnet): subnet_ids.append(subnet.name) elif isinstance(subnet, str): subnet_ids.append(subnet) vpc_id = get_vpc_id_from_subnet_ids(subnet_ids, aws_client) if vpc_id is not None: not_null_args["VpcId"] = vpc_id if self.protocol is not None: not_null_args["Protocol"] = self.protocol if self.protocol_version is not None: not_null_args["ProtocolVersion"] = self.protocol_version if self.port is not None: not_null_args["Port"] = self.port if self.health_check_protocol is not None: not_null_args["HealthCheckProtocol"] = self.health_check_protocol if self.health_check_port is not None: not_null_args["HealthCheckPort"] = self.health_check_port if self.health_check_enabled is not None: not_null_args["HealthCheckEnabled"] = self.health_check_enabled if self.health_check_path is not None: not_null_args["HealthCheckPath"] = self.health_check_path if self.health_check_interval_seconds is not None: not_null_args["HealthCheckIntervalSeconds"] = self.health_check_interval_seconds if self.health_check_timeout_seconds is not None: not_null_args["HealthCheckTimeoutSeconds"] = self.health_check_timeout_seconds if self.healthy_threshold_count is not None: not_null_args["HealthyThresholdCount"] = self.healthy_threshold_count if self.unhealthy_threshold_count is not None: not_null_args["UnhealthyThresholdCount"] = self.unhealthy_threshold_count if self.matcher is not None: not_null_args["Matcher"] = self.matcher if self.target_type is not None: not_null_args["TargetType"] = self.target_type if self.tags is not None: not_null_args["Tags"] = self.tags if self.ip_address_type is not None: not_null_args["IpAddressType"] = self.ip_address_type # Create TargetGroup service_client = self.get_service_client(aws_client) try: create_response = service_client.create_target_group( Name=self.name, **not_null_args, ) logger.debug(f"Response: {create_response}") resource_dict = create_response.get("TargetGroups", {}) # Validate resource creation if resource_dict is not None: self.active_resource = create_response return True except Exception as e: logger.error(f"{self.get_resource_type()} could not be created.") logger.error(e) return False def _read(self, aws_client: AwsApiClient) -> Optional[Any]: """Returns the TargetGroup Args: aws_client: The AwsApiClient for the current TargetGroup """ from botocore.exceptions import ClientError logger.debug(f"Reading {self.get_resource_type()}: {self.get_resource_name()}") service_client = self.get_service_client(aws_client) try: describe_response = service_client.describe_target_groups(Names=[self.name]) logger.debug(f"Describe Response: {describe_response}") resource_list = describe_response.get("TargetGroups", None) if resource_list is not None and isinstance(resource_list, list): for resource in resource_list: if resource.get("TargetGroupName") == self.name: self.active_resource = resource except ClientError as ce: logger.debug(f"ClientError: {ce}") except Exception as e: logger.error(f"Error reading {self.get_resource_type()}.") logger.error(e) return self.active_resource def _delete(self, aws_client: AwsApiClient) -> bool: """Deletes the TargetGroup Args: aws_client: The AwsApiClient for the current TargetGroup """ print_info(f"Deleting {self.get_resource_type()}: {self.get_resource_name()}") service_client = self.get_service_client(aws_client) self.active_resource = None try: tg_arn = self.get_arn(aws_client) if tg_arn is None: logger.error(f"TargetGroup {self.get_resource_name()} not found.") return True delete_response = service_client.delete_target_group(TargetGroupArn=tg_arn) logger.debug(f"Delete Response: {delete_response}") return True except Exception as e: logger.error(f"{self.get_resource_type()} could not be deleted.") logger.error("Please try again or delete resources manually.") logger.error(e) return False def _update(self, aws_client: AwsApiClient) -> bool: """Update EcsService""" print_info(f"Updating {self.get_resource_type()}: {self.get_resource_name()}") tg_arn = self.get_arn(aws_client=aws_client) if tg_arn is None: logger.error(f"TargetGroup {self.get_resource_name()} not found.") return True # create a dict of args which are not null, otherwise aws type validation fails not_null_args: Dict[str, Any] = {} if self.health_check_protocol is not None: not_null_args["HealthCheckProtocol"] = self.health_check_protocol if self.health_check_port is not None: not_null_args["HealthCheckPort"] = self.health_check_port if self.health_check_enabled is not None: not_null_args["HealthCheckEnabled"] = self.health_check_enabled if self.health_check_path is not None: not_null_args["HealthCheckPath"] = self.health_check_path if self.health_check_interval_seconds is not None: not_null_args["HealthCheckIntervalSeconds"] = self.health_check_interval_seconds if self.health_check_timeout_seconds is not None: not_null_args["HealthCheckTimeoutSeconds"] = self.health_check_timeout_seconds if self.healthy_threshold_count is not None: not_null_args["HealthyThresholdCount"] = self.healthy_threshold_count if self.unhealthy_threshold_count is not None: not_null_args["UnhealthyThresholdCount"] = self.unhealthy_threshold_count if self.matcher is not None: not_null_args["Matcher"] = self.matcher service_client = self.get_service_client(aws_client) try: response = service_client.modify_target_group( TargetGroupArn=tg_arn, **not_null_args, ) logger.debug(f"Update Response: {response}") resource_dict = response.get("TargetGroups", {}) # Validate resource creation if resource_dict is not None: print_info(f"TargetGroup updated: {self.get_resource_name()}") self.active_resource = response return True except Exception as e: logger.error(f"{self.get_resource_type()} could not be created.") logger.error(e) return False def get_arn(self, aws_client: AwsApiClient) -> Optional[str]: tg = self._read(aws_client) if tg is None: return None tg_arn = tg.get("TargetGroupArn", None) return tg_arn