1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
|
"""Podman API Errors."""
from typing import Optional, Union, TYPE_CHECKING
from collections.abc import Iterable
from requests import Response
from requests.exceptions import HTTPError
# Break circular import
if TYPE_CHECKING:
from podman.domain.containers import Container
from podman.api.client import APIResponse
class APIError(HTTPError):
"""Wraps HTTP errors for processing by the API and clients."""
def __init__(
self,
message: str,
response: Union[Response, "APIResponse", None] = None,
explanation: Optional[str] = None,
):
"""Initialize APIError.
Args:
message: Message from service. Default: response.text, may be enhanced or wrapped by
bindings
response: HTTP Response from service.
explanation: An enhanced or wrapped version of message with additional context.
"""
super().__init__(message, response=response)
self.explanation = explanation
def __str__(self):
msg = super().__str__()
if self.response is not None:
msg = self.response.reason
if self.is_client_error():
msg = f"{self.status_code} Client Error: {msg}"
elif self.is_server_error():
msg = f"{self.status_code} Server Error: {msg}"
if self.explanation:
msg = f"{msg} ({self.explanation})"
return msg
@property
def status_code(self):
"""Optional[int]: HTTP status code from response."""
if self.response is not None:
return self.response.status_code
return None
def is_error(self) -> bool:
"""Returns True when HTTP operation resulted in an error."""
return self.is_client_error() or self.is_server_error()
def is_client_error(self) -> bool:
"""Returns True when request is incorrect."""
return 400 <= (self.status_code or 0) < 500
def is_server_error(self) -> bool:
"""Returns True when error occurred in service."""
return 500 <= (self.status_code or 0) < 600
class NotFound(APIError):
"""Resource not found on Podman service.
Named for compatibility.
"""
class ImageNotFound(APIError):
"""Image not found on Podman service."""
class DockerException(Exception):
"""Base class for exception hierarchy.
Provided for compatibility.
"""
class PodmanError(DockerException):
"""Base class for PodmanPy exceptions."""
class BuildError(PodmanError):
"""Error occurred during build operation."""
def __init__(self, reason: str, build_log: Iterable[str]) -> None:
"""Initialize BuildError.
Args:
reason: describes the error
build_log: build log output
"""
super().__init__(reason)
self.msg = reason
self.build_log = build_log
class ContainerError(PodmanError):
"""Represents a container that has exited with a non-zero exit code."""
def __init__(
self,
container: "Container",
exit_status: int,
command: Union[str, list[str]],
image: str,
stderr: Optional[Iterable[str]] = None,
): # pylint: disable=too-many-positional-arguments
"""Initialize ContainerError.
Args:
container: Container that reported error.
exit_status: Non-zero status code from Container exit.
command: Command passed to container when created.
image: Name of image that was used to create container.
stderr: Errors reported by Container.
"""
err = f": {stderr}" if stderr is not None else ""
msg = (
f"Command '{command}' in image '{image}' returned non-zero exit "
f"status {exit_status}{err}"
)
super().__init__(msg)
self.container = container
self.exit_status: int = exit_status
self.command = command
self.image = image
self.stderr = stderr
class InvalidArgument(PodmanError):
"""Parameter to method/function was not valid."""
class StreamParseError(RuntimeError):
def __init__(self, reason):
self.msg = reason
|