diff --git a/podman/client.py b/podman/client.py index 5785f6b9..05b2c163 100644 --- a/podman/client.py +++ b/podman/client.py @@ -19,6 +19,7 @@ from podman.domain.secrets import SecretsManager from podman.domain.system import SystemManager from podman.domain.volumes import VolumesManager +from podman.errors.exceptions import PodmanConnectionError logger = logging.getLogger("podman") @@ -114,27 +115,38 @@ def from_env( Client used to communicate with a Podman service. Raises: - ValueError when required environment variable is not set + PodmanConnectionError: When connection to service fails or environment is invalid """ - environment = environment or os.environ - credstore_env = credstore_env or {} - - if version == "auto": - version = None - - kwargs = { - 'version': version, - 'timeout': timeout, - 'tls': False, - 'credstore_env': credstore_env, - 'max_pool_size': max_pool_size, - } - - host = environment.get("CONTAINER_HOST") or environment.get("DOCKER_HOST") or None - if host is not None: - kwargs['base_url'] = host - - return PodmanClient(**kwargs) + try: + environment = environment or os.environ + credstore_env = credstore_env or {} + + if version == "auto": + version = None + + kwargs = { + 'version': version, + 'timeout': timeout, + 'tls': False, + 'credstore_env': credstore_env, + 'max_pool_size': max_pool_size, + } + + host = environment.get("CONTAINER_HOST") or environment.get("DOCKER_HOST") or None + if host is not None: + kwargs['base_url'] = host + + return PodmanClient(**kwargs) + except Exception as exc: + error_msg = "Failed to initialize Podman client from environment" + if isinstance(exc, ValueError): + error_msg = "Invalid environment configuration for Podman client" + elif isinstance(exc, (ConnectionError, TimeoutError)): + error_msg = "Failed to connect to Podman service" + + raise PodmanConnectionError( + message=error_msg, environment=environment, host=host, original_error=exc + ) from exc @cached_property def containers(self) -> ContainersManager: diff --git a/podman/errors/exceptions.py b/podman/errors/exceptions.py index 7cf5970a..61527218 100644 --- a/podman/errors/exceptions.py +++ b/podman/errors/exceptions.py @@ -1,6 +1,6 @@ """Podman API Errors.""" -from typing import Iterable, List, Optional, Union, TYPE_CHECKING +from typing import Iterable, List, Optional, Union, Dict, TYPE_CHECKING from requests import Response from requests.exceptions import HTTPError @@ -142,3 +142,58 @@ def __init__( class InvalidArgument(PodmanError): """Parameter to method/function was not valid.""" + + +class PodmanConnectionError(PodmanError): + """Exception raised when connection to Podman service fails using environment configuration.""" + + def __init__( + self, + message: str, + environment: Optional[Dict[str, str]] = None, + host: Optional[str] = None, + original_error: Optional[Exception] = None, + ): + """Initialize PodmanConnectionError. + + Args: + message: Description of the error + environment: Environment variables used in connection attempt + host: URL to Podman service that failed + original_error: Original exception that caused this error + """ + super().__init__(message) + self.environment = environment + self.host = host + self.original_error = original_error + + def __str__(self) -> str: + """Format error message with details about connection attempt.""" + msg = [super().__str__()] + + if self.host: + msg.append(f"Host: {self.host}") + + if self.environment: + relevant_vars = { + k: v + for k, v in self.environment.items() + if k + in ( + 'DOCKER_HOST', + 'CONTAINER_HOST', + 'DOCKER_TLS_VERIFY', + 'CONTAINER_TLS_VERIFY', + 'DOCKER_CERT_PATH', + 'CONTAINER_CERT_PATH', + ) + } + if relevant_vars: + msg.append("Environment:") + for key, value in relevant_vars.items(): + msg.append(f" {key}={value}") + + if self.original_error: + msg.append(f"Caused by: {str(self.original_error)}") + + return " | ".join(msg)