from typing import Optional, Any, Dict, List from phi.aws.api_client import AwsApiClient from phi.aws.resource.base import AwsResource from phi.cli.console import print_info from phi.utils.log import logger class EcsCluster(AwsResource): """ Reference: - https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/ecs.html """ resource_type: Optional[str] = "EcsCluster" service_name: str = "ecs" # Name of the cluster. name: str # Name for the cluster. # Use name if not provided. ecs_cluster_name: Optional[str] = None tags: Optional[List[Dict[str, str]]] = None # The setting to use when creating a cluster. settings: Optional[List[Dict[str, Any]]] = None # The execute command configuration for the cluster. configuration: Optional[Dict[str, Any]] = None # The short name of one or more capacity providers to associate with the cluster. # A capacity provider must be associated with a cluster before it can be included as part of the default capacity # provider strategy of the cluster or used in a capacity provider strategy when calling the CreateService/RunTask. capacity_providers: Optional[List[str]] = None # The capacity provider strategy to set as the default for the cluster. After a default capacity provider strategy # is set for a cluster, when you call the RunTask or CreateService APIs with no capacity provider strategy or # launch type specified, the default capacity provider strategy for the cluster is used. default_capacity_provider_strategy: Optional[List[Dict[str, Any]]] = None # Use this parameter to set a default Service Connect namespace. # After you set a default Service Connect namespace, any new services with Service Connect turned on that are # created in the cluster are added as client services in the namespace. service_connect_namespace: Optional[str] = None def get_ecs_cluster_name(self): return self.ecs_cluster_name or self.name def _create(self, aws_client: AwsApiClient) -> bool: """Creates the EcsCluster Args: aws_client: The AwsApiClient for the current cluster """ 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] = {} if self.tags is not None: not_null_args["tags"] = self.tags if self.settings is not None: not_null_args["settings"] = self.settings if self.configuration is not None: not_null_args["configuration"] = self.configuration if self.capacity_providers is not None: not_null_args["capacityProviders"] = self.capacity_providers if self.default_capacity_provider_strategy is not None: not_null_args["defaultCapacityProviderStrategy"] = self.default_capacity_provider_strategy if self.service_connect_namespace is not None: not_null_args["serviceConnectDefaults"] = { "namespace": self.service_connect_namespace, } # Create EcsCluster service_client = self.get_service_client(aws_client) try: create_response = service_client.create_cluster( clusterName=self.get_ecs_cluster_name(), **not_null_args, ) logger.debug(f"EcsCluster: {create_response}") resource_dict = create_response.get("cluster", {}) # 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 EcsCluster Args: aws_client: The AwsApiClient for the current cluster """ logger.debug(f"Reading {self.get_resource_type()}: {self.get_resource_name()}") from botocore.exceptions import ClientError service_client = self.get_service_client(aws_client) try: cluster_name = self.get_ecs_cluster_name() describe_response = service_client.describe_clusters(clusters=[cluster_name]) logger.debug(f"EcsCluster: {describe_response}") resource_list = describe_response.get("clusters", None) if resource_list is not None and isinstance(resource_list, list): for resource in resource_list: _cluster_identifier = resource.get("clusterName", None) if _cluster_identifier == cluster_name: _cluster_status = resource.get("status", None) if _cluster_status == "ACTIVE": self.active_resource = resource break 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 EcsCluster Args: aws_client: The AwsApiClient for the current cluster """ 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: delete_response = service_client.delete_cluster(cluster=self.get_ecs_cluster_name()) logger.debug(f"EcsCluster: {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 get_arn(self, aws_client: AwsApiClient) -> Optional[str]: tg = self._read(aws_client) if tg is None: return None tg_arn = tg.get("ListenerArn", None) return tg_arn