from typing import Any, Optional, Dict from phi.resource.base import ResourceBase from phi.docker.api_client import DockerApiClient from phi.cli.console import print_info from phi.utils.log import logger class DockerResource(ResourceBase): """Base class for Docker Resources.""" # Fields received from the DockerApiClient id: Optional[str] = None short_id: Optional[str] = None attrs: Optional[Dict[str, Any]] = None # Pull latest image before create/update pull: Optional[bool] = None docker_client: Optional[DockerApiClient] = None @staticmethod def get_from_cluster(docker_client: DockerApiClient) -> Any: """Gets all resources of this type from the Docker cluster""" logger.warning("@get_from_cluster method not defined") return None def get_docker_client(self) -> DockerApiClient: if self.docker_client is not None: return self.docker_client self.docker_client = DockerApiClient() return self.docker_client def _read(self, docker_client: DockerApiClient) -> Any: logger.warning(f"@_read method not defined for {self.get_resource_name()}") return True def read(self, docker_client: DockerApiClient) -> Any: """Reads the resource from the docker cluster""" # 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: DockerApiClient = docker_client or self.get_docker_client() return self._read(client) def is_active(self, docker_client: DockerApiClient) -> bool: """Returns True if the active is active on the docker cluster""" self.active_resource = self._read(docker_client=docker_client) return True if self.active_resource is not None else False def _create(self, docker_client: DockerApiClient) -> bool: logger.warning(f"@_create method not defined for {self.get_resource_name()}") return True def create(self, docker_client: DockerApiClient) -> bool: """Creates the resource on the docker cluster""" # 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: DockerApiClient = docker_client or self.get_docker_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, docker_client: DockerApiClient) -> bool: return True def _update(self, docker_client: DockerApiClient) -> bool: logger.warning(f"@_update method not defined for {self.get_resource_name()}") return True def update(self, docker_client: DockerApiClient) -> bool: """Updates the resource on the docker cluster""" # 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: DockerApiClient = docker_client or self.get_docker_client() if self.is_active(client): self.resource_updated = self._update(client) else: print_info(f"{self.get_resource_type()}: {self.get_resource_name()} not active, creating...") return self.create(client) # 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, docker_client: DockerApiClient) -> bool: return True def _delete(self, docker_client: DockerApiClient) -> bool: logger.warning(f"@_delete method not defined for {self.get_resource_name()}") return False def delete(self, docker_client: DockerApiClient) -> bool: """Deletes the resource from the docker cluster""" # 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: DockerApiClient = docker_client or self.get_docker_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, docker_client: DockerApiClient) -> bool: return True