The network prune joins the image and volume prunes on the shared
PruneJob: it runs in the background, the Networks page shows a progress
bar and a toast with the outcome, and the button now asks before deleting
instead of pruning on click. Docker has no -a for networks and they hold
no data, so there is no option and no space figure.
Candidates are stricter than docker network prune, which goes by active
endpoints and so removes a stopped container's network, after which that
container fails to start ("network not found"). A network any container,
running or stopped, is attached to (by name or id) is kept, as are
bridge/host/none, ingress/docker_gwbridge and swarm-scoped networks.
Removal is per network, so one the daemon refuses does not stop the rest.
- POST /api/networks/prune takes background=; without it it still waits
and returns {NetworksDeleted}. GET /api/networks/prune-status is
admin-only and registered before /{network_id}, which would otherwise
take "prune-status" for an id.
- The long-timeout test for network prune in test_docker_timeouts.py is
superseded by test_network_prune.py.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
56 lines
1.8 KiB
Python
56 lines
1.8 KiB
Python
"""Slow Docker calls must fail with a message, not a bare 500.
|
|
|
|
A volume or image prune that deletes a lot of data easily runs past docker-py's
|
|
60s read timeout. requests' ``ReadTimeout`` is not a ``DockerException``, so it
|
|
slipped past ``safe_call`` and the UI only showed "Request failed with status
|
|
code 500". Prunes now use a client with a long timeout, and any transport
|
|
timeout that still happens is turned into a ``DockerError`` (-> 502 + detail).
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import pytest
|
|
import requests
|
|
|
|
|
|
def test_safe_call_wraps_read_timeout():
|
|
from docker_client import DockerError, safe_call
|
|
|
|
def slow():
|
|
raise requests.exceptions.ReadTimeout("Read timed out. (read timeout=60)")
|
|
|
|
with pytest.raises(DockerError) as info:
|
|
safe_call(slow)
|
|
assert info.value.error == "docker_timeout"
|
|
|
|
|
|
def test_safe_call_wraps_connection_error():
|
|
from docker_client import DockerError, safe_call
|
|
|
|
def gone():
|
|
raise requests.exceptions.ConnectionError("socket went away")
|
|
|
|
with pytest.raises(DockerError) as info:
|
|
safe_call(gone)
|
|
assert info.value.error == "docker_unavailable"
|
|
|
|
|
|
def test_volume_prune_timeout_is_a_502_with_detail(as_admin, monkeypatch):
|
|
from services import volume_service
|
|
|
|
class Api:
|
|
def containers(self, all=False):
|
|
raise requests.exceptions.ReadTimeout("Read timed out.")
|
|
|
|
class Client:
|
|
api = Api()
|
|
|
|
monkeypatch.setattr(volume_service, "get_long_client", lambda: Client())
|
|
r = as_admin.post("/api/volumes/prune")
|
|
assert r.status_code == 502
|
|
assert r.json()["error"] == "docker_timeout"
|
|
|
|
|
|
# The image, volume and network prunes go item by item and have their own
|
|
# tests (test_image_prune.py, test_volume_prune.py, test_network_prune.py),
|
|
# including that they use the long-timeout client.
|