from typing import Any, Optional from phi.resource.base import ResourceBase from phi.aws.api_client import AwsApiClient from phi.cli.console import print_info from phi.utils.log import logger class AwsResource(ResourceBase): service_name: str service_client: Optional[Any] = None service_resource: Optional[Any] = None aws_region: Optional[str] = None aws_profile: Optional[str] = None aws_client: Optional[AwsApiClient] = None def get_aws_region(self) -> Optional[str]: # Priority 1: Use aws_region from resource if self.aws_region: return self.aws_region # Priority 2: Get aws_region from workspace settings if self.workspace_settings is not None and self.workspace_settings.aws_region is not None: self.aws_region = self.workspace_settings.aws_region return self.aws_region # Priority 3: Get aws_region from env from os import getenv from phi.constants import AWS_REGION_ENV_VAR aws_region_env = getenv(AWS_REGION_ENV_VAR) if aws_region_env is not None: logger.debug(f"{AWS_REGION_ENV_VAR}: {aws_region_env}") self.aws_region = aws_region_env return self.aws_region def get_aws_profile(self) -> Optional[str]: # Priority 1: Use aws_region from resource if self.aws_profile: return self.aws_profile # Priority 2: Get aws_profile from workspace settings if self.workspace_settings is not None and self.workspace_settings.aws_profile is not None: self.aws_profile = self.workspace_settings.aws_profile return self.aws_profile # Priority 3: Get aws_profile from env from os import getenv from phi.constants import AWS_PROFILE_ENV_VAR aws_profile_env = getenv(AWS_PROFILE_ENV_VAR) if aws_profile_env is not None: logger.debug(f"{AWS_PROFILE_ENV_VAR}: {aws_profile_env}") self.aws_profile = aws_profile_env return self.aws_profile def get_service_client(self, aws_client: AwsApiClient): from boto3 import session if self.service_client is None: boto3_session: session = aws_client.boto3_session self.service_client = boto3_session.client(service_name=self.service_name) return self.service_client def get_service_resource(self, aws_client: AwsApiClient): from boto3 import session if self.service_resource is None: boto3_session: session = aws_client.boto3_session self.service_resource = boto3_session.resource(service_name=self.service_name) return self.service_resource def get_aws_client(self) -> AwsApiClient: if self.aws_client is not None: return self.aws_client self.aws_client = AwsApiClient(aws_region=self.get_aws_region(), aws_profile=self.get_aws_profile()) return self.aws_client def _read(self, aws_client: AwsApiClient) -> Any: logger.warning(f"@_read method not defined for {self.get_resource_name()}") return True def read(self, aws_client: Optional[AwsApiClient] = None) -> Any: """Reads the resource from Aws""" # Step 1: Use cached value if available if self.use_cache and self.active_resource is not None: return self.active_resource # Step 2: Skip resource creation if skip_read = True if self.skip_read: print_info(f"Skipping read: {self.get_resource_name()}") return True # Step 3: Read resource client: AwsApiClient = aws_client or self.get_aws_client() return self._read(client) def is_active(self, aws_client: AwsApiClient) -> bool: """Returns True if the resource is active on Aws""" _resource = self.read(aws_client=aws_client) return True if _resource is not None else False def _create(self, aws_client: AwsApiClient) -> bool: logger.warning(f"@_create method not defined for {self.get_resource_name()}") return True def create(self, aws_client: Optional[AwsApiClient] = None) -> bool: """Creates the resource on Aws""" # Step 1: Skip resource creation if skip_create = True if self.skip_create: print_info(f"Skipping create: {self.get_resource_name()}") return True # Step 2: Check if resource is active and use_cache = True client: AwsApiClient = aws_client or self.get_aws_client() if self.use_cache and self.is_active(client): self.resource_created = True print_info(f"{self.get_resource_type()}: {self.get_resource_name()} already exists") # Step 3: Create the resource else: self.resource_created = self._create(client) if self.resource_created: print_info(f"{self.get_resource_type()}: {self.get_resource_name()} created") # Step 4: Run post create steps if self.resource_created: if self.save_output: self.save_output_file() logger.debug(f"Running post-create for {self.get_resource_type()}: {self.get_resource_name()}") return self.post_create(client) logger.error(f"Failed to create {self.get_resource_type()}: {self.get_resource_name()}") return self.resource_created def post_create(self, aws_client: AwsApiClient) -> bool: return True def _update(self, aws_client: AwsApiClient) -> Any: logger.warning(f"@_update method not defined for {self.get_resource_name()}") return True def update(self, aws_client: Optional[AwsApiClient] = None) -> bool: """Updates the resource on Aws""" # Step 1: Skip resource update if skip_update = True if self.skip_update: print_info(f"Skipping update: {self.get_resource_name()}") return True # Step 2: Update the resource client: AwsApiClient = aws_client or self.get_aws_client() if self.is_active(client): self.resource_updated = self._update(client) else: print_info(f"{self.get_resource_type()}: {self.get_resource_name()} does not exist") return True # Step 3: Run post update steps if self.resource_updated: print_info(f"{self.get_resource_type()}: {self.get_resource_name()} updated") if self.save_output: self.save_output_file() logger.debug(f"Running post-update for {self.get_resource_type()}: {self.get_resource_name()}") return self.post_update(client) logger.error(f"Failed to update {self.get_resource_type()}: {self.get_resource_name()}") return self.resource_updated def post_update(self, aws_client: AwsApiClient) -> bool: return True def _delete(self, aws_client: AwsApiClient) -> Any: logger.warning(f"@_delete method not defined for {self.get_resource_name()}") return True def delete(self, aws_client: Optional[AwsApiClient] = None) -> bool: """Deletes the resource from Aws""" # Step 1: Skip resource deletion if skip_delete = True if self.skip_delete: print_info(f"Skipping delete: {self.get_resource_name()}") return True # Step 2: Delete the resource client: AwsApiClient = aws_client or self.get_aws_client() if self.is_active(client): self.resource_deleted = self._delete(client) else: print_info(f"{self.get_resource_type()}: {self.get_resource_name()} does not exist") return True # Step 3: Run post delete steps if self.resource_deleted: print_info(f"{self.get_resource_type()}: {self.get_resource_name()} deleted") if self.save_output: self.delete_output_file() logger.debug(f"Running post-delete for {self.get_resource_type()}: {self.get_resource_name()}.") return self.post_delete(client) logger.error(f"Failed to delete {self.get_resource_type()}: {self.get_resource_name()}") return self.resource_deleted def post_delete(self, aws_client: AwsApiClient) -> bool: return True