from typing import Optional, Any, Dict from typing_extensions import Literal 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 EksAddon(AwsResource): """ Reference: - https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/eks.html """ resource_type: Optional[str] = "EksAddon" service_name: str = "eks" # Addon name name: str # EKS cluster name cluster_name: str # Addon version version: Optional[str] = None service_account_role_arn: Optional[str] = None resolve_conflicts: Optional[Literal["OVERWRITE", "NONE", "PRESERVE"]] = None client_request_token: Optional[str] = None tags: Optional[Dict[str, str]] = None preserve: Optional[bool] = False # provided by api on create created_at: Optional[str] = None status: Optional[str] = None wait_for_create: bool = False wait_for_delete: bool = False wait_for_update: bool = False def _create(self, aws_client: AwsApiClient) -> bool: """Creates the EksAddon 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.version: not_null_args["addonVersion"] = self.version if self.service_account_role_arn: not_null_args["serviceAccountRoleArn"] = self.service_account_role_arn if self.resolve_conflicts: not_null_args["resolveConflicts"] = self.resolve_conflicts if self.client_request_token: not_null_args["clientRequestToken"] = self.client_request_token if self.tags: not_null_args["tags"] = self.tags # Step 1: Create EksAddon service_client = self.get_service_client(aws_client) try: create_response = service_client.create_addon( clusterName=self.cluster_name, addonName=self.name, **not_null_args, ) logger.debug(f"EksAddon: {create_response}") # logger.debug(f"EksAddon type: {type(create_response)}") # Validate Cluster creation self.created_at = create_response.get("addon", {}).get("createdAt", None) self.status = create_response.get("addon", {}).get("status", None) logger.debug(f"created_at: {self.created_at}") logger.debug(f"status: {self.status}") if self.created_at is not None: print_info(f"EksAddon created: {self.name}") self.active_resource = create_response return True except service_client.exceptions.ResourceInUseException: print_info(f"Addon already exists: {self.name}") return True except Exception as e: logger.error(f"{self.get_resource_type()} could not be created.") logger.error(e) return False def post_create(self, aws_client: AwsApiClient) -> bool: # Wait for Addon to be created if self.wait_for_create: try: print_info(f"Waiting for {self.get_resource_type()} to be active.") waiter = self.get_service_client(aws_client).get_waiter("addon_active") waiter.wait( clusterName=self.cluster_name, addonName=self.name, WaiterConfig={ "Delay": self.waiter_delay, "MaxAttempts": self.waiter_max_attempts, }, ) except Exception: # logger.error(f"Waiter failed: {awe}") pass return True def _read(self, aws_client: AwsApiClient) -> Optional[Any]: """Returns the EksAddon Args: aws_client: The AwsApiClient for the current cluster """ 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_addon(clusterName=self.cluster_name, addonName=self.name) # logger.debug(f"EksAddon: {describe_response}") # logger.debug(f"EksAddon type: {type(describe_response)}") addon_dict = describe_response.get("addon", {}) self.created_at = addon_dict.get("createdAt", None) self.status = addon_dict.get("status", None) logger.debug(f"EksAddon created_at: {self.created_at}") logger.debug(f"EksAddon status: {self.status}") if self.created_at is not None: logger.debug(f"EksAddon found: {self.name}") self.active_resource = describe_response 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 EksAddon Args: aws_client: The AwsApiClient for the current cluster """ print_info(f"Deleting {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.preserve: not_null_args["preserve"] = self.preserve # Step 1: Delete EksAddon service_client = self.get_service_client(aws_client) self.active_resource = None try: delete_response = service_client.delete_addon( clusterName=self.cluster_name, addonName=self.name, **not_null_args ) logger.debug(f"EksAddon: {delete_response}") # logger.debug(f"EksAddon type: {type(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 post_delete(self, aws_client: AwsApiClient) -> bool: # Wait for Addon to be deleted if self.wait_for_delete: try: print_info(f"Waiting for {self.get_resource_type()} to be deleted.") waiter = self.get_service_client(aws_client).get_waiter("addon_deleted") waiter.wait( clusterName=self.cluster_name, addonName=self.name, WaiterConfig={ "Delay": self.waiter_delay, "MaxAttempts": self.waiter_max_attempts, }, ) except Exception as awe: logger.error(f"Waiter failed: {awe}") return True