Krikri::PluginHelpers::DockerSdkError
DockerSdkError - the Docker Python SDK's APIError rendering that real community.docker modules quote in their failure messages:
500 Server Error for http+docker://localhost/v1.41/auth:
Internal Server Error ("daemon's own message")
docr words the same failure "Code: 500 Message: ..." and throws
away both the request URL and the status line's reason phrase, so
the plugins catch the error and re-render it here instead.
Previously only docker_container reproduced this wording inline;
every Docker module now shares it.
Class methods
The Docker Python SDK's APIError.str (its errors.py): the
request line's own reason phrase, then the daemon's own message -
the message field of its JSON error body, or the raw body when
it isn't JSON - in quotes.
Same rendering for a DockerAPIError raised by a traced client: the URL is the one the failed request used (with the version prefix the SDK always includes and docr never sends), and the daemon's message is recovered from docr's "Code: NNN Message: ..." exception text. Without a recorded URL (an error raised before any request went out) there is nothing to re-render, and docr's own text is the best available fallback. api_version can be passed in by tests (which have no daemon to negotiate with); nil means ask the daemon, like every real caller.
Real's AnsibleDockerClientBase (module_utils/common_api.py) wraps every exception escaping the SDK Client's init in "Error connecting: %s" - and that init's api_version property is the daemon's first round trip (GET /version), which is why the text always carries the SDK's own "Error while fetching server API version: ..." middle. The transport error after it is requests' own rendering: a one-element tuple of the wrapped exception's repr for a UNIX-socket connect ("('Connection aborted.', FileNotFoundError(2, 'No such file or directory'))"), or urllib3's pool-retry prose for a TCP one. docker_host is the resolved docker_host (param or environment) - only needed to pick the TCP branch's pool spelling.
How Python's requests form-encodes a query value (its urlencode):
everything outside the unreserved set percent-encoded, so a / in
a repository name becomes %2F exactly like Ansible's own pull URL.
Ansible's SDK derives the API version it puts in every endpoint URL
from the daemon's own /version reply (its _retrieve_server_version),
which docr never asks for since it calls every endpoint
unversioned.
Real (both docker_container and docker_image pull through
AnsibleDockerClientBase.pull_image) POSTs the pull itself and wraps
any failure in its own prefix, so the wrapped text is the SDK's
APIError rendering. That URL is the versioned pull URL the SDK
builds - query in the SDK's own "tag=" then "fromImage=" order -
while docr calls every endpoint unversioned and orders its query
the other way round, so the pull goes out here directly to keep the
exact text Ansible reports.
Real docker_login validates registry credentials with the daemon's POST /auth (docker_login.py's _login) and quotes the SDK's APIError text in its "Logging into ... failed - ..." failure. Returns nil when the daemon accepted the credentials, otherwise that SDK text.
Ansible's SDK base_url, the literal host part of every URL it quotes in an error message: the http+docker:// placeholder it mounts its UNIX-socket adapter under, or scheme://host:port for a TCP(+TLS) daemon.
The status line's reason phrase, which Crystal's HTTP::Client doesn't keep - its own HTTP::Status enum name is the closest stand-in, and for the codes a Docker daemon actually answers with ("Internal Server Error" on a 500) it is the very same text Ansible's message quotes.