question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Specialized DockerExceptions

See original GitHub issue

Hello again!

while I’m working on the integration of python on whales into my applications, I found the error management mostly thought for a human use.

Currently all errors raise a DockerException that contains the exact error output produced by the CLI. That’s great when using the package by a human and provides enough information to debug the problem. But when the package is used to automate processes this approach is not very helpful and to be able to properly react, a parser of the exception is needed.

Just a practical example:

docker.service.inspect("my_service")

This command can fail for a number of reasons, for example:

the swarm is not initialized:

python_on_whales.utils.DockerException: The docker command executed was `/usr/local/bin/docker service inspect myservice`.
It returned with code 1
The content of stdout is '[]
'
The content of stderr is 'Status: Error response from daemon: This node is not a swarm manager. Use "docker swarm init" or "docker swarm join" to connect this node to swarm and try again., Code: 1
'

or the service does not exist:

python_on_whales.utils.DockerException: The docker command executed was `/usr/local/bin/docker service inspect myservice`.
It returned with code 1
The content of stdout is '[]
'
The content of stderr is 'Status: Error: no such service: myservice, Code: 1
'

Probably there are much more possible reasons, but two are enough for the example

To implement specific reactions to the different problems a parser of the exception is needed:

try:
    docker.service.inspect("my_service")
except python_on_whales.utils.DockerException as e:
    if "This node is not a swarm manager" is str(e):
        initialized_my_node()
    elif "no such service" in str(e):
        log.error("This service does not exist")
    else:
        log.critical("Unexpected error")

I think that raise specific exceptions (that would extend the parent DockerException, of course) would be very very useful for the user experience:

try:
    docker.service.inspect("my_service")
except python_on_whales.utils.SwarmNotInitialized:
        initialized_my_node()
except python_on_whales.utis.NoSuchService:
        log.error("This service does not exist")
except Exception:
        log.critical("Unexpected error")

That means, in a practical way, move the parsing directly into the package to centralized that operation and make it transparent to the final user.

I understand that is a very boring issue but I’m pretty sure that would greatly improve the adoption… and of course I’m 100% available to ease this task as much as I can

Issue Analytics

  • State:open
  • Created 2 years ago
  • Comments:12 (12 by maintainers)

github_iconTop GitHub Comments

1reaction
gabrieldemarmiessecommented, Jul 9, 2021

My plan is

  1. raise different exceptions for different errors quickly, so by parsing the error message. Currently, with python-on-whales, it’s impossible to answer the question “does that image/container/network/volume exists?”, by calling the engine api, we can do that. So we need to fix it quicky. I would like a function exists(). So that we can do docker.container.exists("my_container_name") or my_container.exists(). This function would return a bool. We should also have an exception for different objects that don’t exists.

  2. Ask the docker team for different error codes from the CLI. It’s not only useful for python-on-whales, it may be also useful for people doing bash scripting. From a bash script, knowing if an image exists or not is only possible by parsing stderr. So not great. The CLI has some progress to do there.

I’m going to do the first step, but if you want to help and contribute, I’d be happy to review any pull request I can get 😃

0reactions
mdantoniocommented, Aug 30, 2021

Unfortunately still no activity from the issue about standardising exit-codes in docker-cli, but it was somehow expected.

In the meanwhile can I ask you some additional exceptions?

I’m working with local docker registries (with self signed certificates) and remote engines and I found some exceptions in case of untrusted certificates and invalid remote hosts

Invalid remote hosts are quite easy to be reproduced:

>>> from python_on_whales import DockerClient

>>> DockerClient(host="invalid").container.list()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/lib/python3.9/dist-packages/python_on_whales/components/container/cli_wrapper.py", line 955, in list
    for x in run(full_cmd).splitlines()
  File "/usr/local/lib/python3.9/dist-packages/python_on_whales/utils.py", line 145, in run
    raise DockerException(
python_on_whales.exceptions.DockerException: The docker command executed was `/usr/local/bin/docker --host invalid container list -q --no-trunc`.
It returned with code 1
The content of stdout is ''
The content of stderr is 'error during connect: Get "http://invalid:2375/v1.24/containers/json": dial tcp: lookup invalid: Temporary failure in name resolution
'

>>> DockerClient(host="192.168.1.19").container.list()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/lib/python3.9/dist-packages/python_on_whales/components/container/cli_wrapper.py", line 955, in list
    for x in run(full_cmd).splitlines()
  File "/usr/local/lib/python3.9/dist-packages/python_on_whales/utils.py", line 145, in run
    raise DockerException(
python_on_whales.exceptions.DockerException: The docker command executed was `/usr/local/bin/docker --host 192.168.1.19 container list -q --no-trunc`.
It returned with code 1
The content of stdout is ''
The content of stderr is 'error during connect: Get "http://192.168.1.19:2375/v1.24/containers/json": dial tcp 192.168.1.19:2375: connect: no route to host
'

>>> DockerClient(host="ssh://invalid@invalid").container.list()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/lib/python3.9/dist-packages/python_on_whales/components/container/cli_wrapper.py", line 955, in list
    for x in run(full_cmd).splitlines()
  File "/usr/local/lib/python3.9/dist-packages/python_on_whales/utils.py", line 145, in run
    raise DockerException(
python_on_whales.exceptions.DockerException: The docker command executed was `/usr/local/bin/docker --host ssh://invalid@invalid container list -q --no-trunc`.
It returned with code 1
The content of stdout is ''
The content of stderr is 'error during connect: Get "http://docker.example.com/v1.24/containers/json": command [ssh -l invalid -- invalid docker system dial-stdio] has exited with exit status 255, please make sure the URL is valid, and Docker 18.09 or later is installed on the remote host: stderr=ssh: Could not resolve hostname invalid: Temporary failure in name resolution

'

On the other side to raise errors for untrusted certificates it is need to spawn a docker registry with https enabled and a self signed certificate. This is a bit tricky so feel free to decline my request 😃

First, create a self signed certificate (note: the docker registry requires a specific format for the registry or the TLS certificate will not be accepted… after many tries I found the following workflow to produce a valid self signed certificate (in particular to create a certificate containing an IP SAN)

  1. create a config.ini file (in /tmp/certs/config.ini in this snippet):
[req]
default_bits = 4096
default_md = sha256
distinguished_name = req_distinguished_name
x509_extensions = v3_req
prompt = no
[req_distinguished_name]
C = XX
ST = XX
L = XXX
O = NoCompany
OU = Orgainizational_Unit
CN = 192.168.1.7
[v3_req]
keyUsage = keyEncipherment, dataEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt_names
[alt_names]
IP.1 = 192.168.1.7

Please note that 192.168.1.7 is the local IP address of my network adapter, adjust with your own IP

If you want to extract the IP from python, I use the following code:

import socket

s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.connect(("8.8.8.8", 80))
print(s.getsockname()[0])

After creating the config.ini you can create the self signed certificate with:

openssl req -newkey rsa:4096 -nodes -sha256 -x509 -days 365 -config /tmp/certs/config.ini -keyout /tmp/certs/mycert.key -out /tmp/certs/mycert.pem -subj '/CN=*/'

Now you can spawn the registry:

docker.container.run("registry:2.7.1", publish=[(5000,5000)], remove=True, detach=True, volumes=[("/tmp/certs", "/certs")], envs={"REGISTRY_HTTP_TLS_CERTIFICATE": "/certs/mycert.pem", "REGISTRY_HTTP_TLS_KEY": "/certs/mycert.key"})

And finally the exception:

>>> docker.image.pull("192.168.1.7:5000/random/image")

Using default tag: latest
Error response from daemon: Get "https://192.168.1.7:5000/v2/": x509: certificate signed by unknown authority
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/lib/python3.9/dist-packages/python_on_whales/components/image/cli_wrapper.py", line 387, in pull
    return self._pull_single_tag(x, quiet=quiet)
  File "/usr/local/lib/python3.9/dist-packages/python_on_whales/components/image/cli_wrapper.py", line 405, in _pull_single_tag
    run(full_cmd, capture_stdout=quiet, capture_stderr=quiet)
  File "/usr/local/lib/python3.9/dist-packages/python_on_whales/utils.py", line 145, in run
    raise DockerException(
python_on_whales.exceptions.DockerException: The docker command executed was `/usr/local/bin/docker image pull 192.168.1.7:5000/random/image`.
It returned with code 1
The content of stdout can be found above the stacktrace (it wasn't captured).
The content of stderr can be found above the stacktrace (it wasn't captured).

as usual, thank you very much for you support!

Read more comments on GitHub >

github_iconTop Results From Across the Web

docker.errors.DockerException: Error while fetching server API ...
In my case I got docker.errors.DockerException: Error while fetching server API version: (2, 'CreateFile', 'The system cannot find the file ...
Read more >
com.github.dockerjava.api.exception.BadRequestException java ...
... default: throw new DockerException(getBodyAsMessage(responseContext), status); } } ... A specialized Writer that writes to a file in the file system.
Read more >
Adobe Connect Learn & Support
Get started with Adobe Connect. Find tutorials, the user guide, answers to common questions, and help from the community forum.
Read more >
Docker Failed to Start: File not found 'com.docker.backend.exe'
DockerException : File not found 'C:\Program ... OS: Windows 10 Pro Edition: Professional Id: 2009 Build: 19043 BuildLabName: ...
Read more >
Specialized Bicycles (@iamspecialized) / Twitter
Local bike shops are the cornerstone of the cycling community. They're the Third Place for many, the meetup spot for every great group...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found