feat: make the docker binary path a hook
Where docker lives is device-specific; what to do with it is not. QNAP's Container Station installs docker under /share/<pool>/.qpkg/ and never puts it on PATH, so a QTS driver inheriting this class found no docker at all. Rather than reimplementing the Docker surface in the vendor driver, the path becomes a single overridable method and every call site goes through it. Per docs/ARCHITECTURE.md 4.4, generic logic belongs to the shared driver and only the device-specific mechanics belong to the vendor one. A test asserts that *every* docker call site uses the hook — a half converted set would let detection find the binary while the actual queries still missed it, which only shows up against real hardware.
This commit is contained in:
+23
-10
@@ -1480,6 +1480,16 @@ class LinuxDriver(OSDriver):
|
||||
# Docker
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
def _docker_bin(self) -> str:
|
||||
"""Path to the docker binary.
|
||||
|
||||
A hook rather than a literal because the Docker *logic* is the same
|
||||
everywhere while the *location* is not: QTS ships Container Station's
|
||||
docker under /share/<pool>/.qpkg/ and never puts it on PATH. Subclasses
|
||||
override this one method instead of reimplementing the surface.
|
||||
"""
|
||||
return "docker"
|
||||
|
||||
def get_docker_info(self) -> DockerInfoDict:
|
||||
"""Return information about the local Docker environment.
|
||||
|
||||
@@ -1498,26 +1508,28 @@ class LinuxDriver(OSDriver):
|
||||
"""
|
||||
import json as _json
|
||||
|
||||
docker = self._docker_bin()
|
||||
|
||||
# Check docker binary first (docker --version doesn't need socket access)
|
||||
if not self._send("command -v docker 2>/dev/null").strip():
|
||||
if not self._send(f"command -v {docker} 2>/dev/null").strip():
|
||||
return {"available": False}
|
||||
|
||||
# Verify socket access — docker ps is cheaper and fails immediately on permission errors
|
||||
ps_check = self._send("docker ps 2>&1")
|
||||
ps_check = self._send(f"{docker} ps 2>&1")
|
||||
if "permission denied" in ps_check.lower() or "cannot connect" in ps_check.lower():
|
||||
return {"available": False, "permission_denied": True}
|
||||
|
||||
version = self._send("docker --version 2>/dev/null").strip()
|
||||
version = self._send(f"{docker} --version 2>/dev/null").strip()
|
||||
|
||||
combined = self._send(
|
||||
"echo '---CONTAINERS---'; "
|
||||
"docker ps -a --format '{{json .}}' 2>/dev/null; "
|
||||
f"{docker} ps -a --format '{{{{json .}}}}' 2>/dev/null; "
|
||||
"echo '---IMAGES---'; "
|
||||
"docker images --format '{{json .}}' 2>/dev/null; "
|
||||
f"{docker} images --format '{{{{json .}}}}' 2>/dev/null; "
|
||||
"echo '---VOLUMES---'; "
|
||||
"docker volume ls --format '{{json .}}' 2>/dev/null; "
|
||||
f"{docker} volume ls --format '{{{{json .}}}}' 2>/dev/null; "
|
||||
"echo '---NETWORKS---'; "
|
||||
"docker network ls --format '{{json .}}' 2>/dev/null",
|
||||
f"{docker} network ls --format '{{{{json .}}}}' 2>/dev/null",
|
||||
read_timeout=60,
|
||||
)
|
||||
|
||||
@@ -1662,7 +1674,8 @@ class LinuxDriver(OSDriver):
|
||||
for img_name in candidate_images:
|
||||
try:
|
||||
local_raw = self._send(
|
||||
f"docker inspect {img_name!r} --format '{{{{index .RepoDigests 0}}}}' 2>/dev/null",
|
||||
f"{self._docker_bin()} inspect {img_name!r} "
|
||||
f"--format '{{{{index .RepoDigests 0}}}}' 2>/dev/null",
|
||||
read_timeout=5,
|
||||
).strip()
|
||||
if not local_raw or "@" not in local_raw:
|
||||
@@ -1670,7 +1683,7 @@ class LinuxDriver(OSDriver):
|
||||
local_digest = local_raw.split("@", 1)[1]
|
||||
|
||||
remote_full = self._send(
|
||||
f"docker buildx imagetools inspect {img_name!r} 2>&1",
|
||||
f"{self._docker_bin()} buildx imagetools inspect {img_name!r} 2>&1",
|
||||
read_timeout=30,
|
||||
).strip()
|
||||
if ("429" in remote_full
|
||||
@@ -1712,7 +1725,7 @@ class LinuxDriver(OSDriver):
|
||||
import shlex as _shlex
|
||||
|
||||
raw = self._send(
|
||||
f"docker inspect {_shlex.quote(container_id)} 2>/dev/null",
|
||||
f"{self._docker_bin()} inspect {_shlex.quote(container_id)} 2>/dev/null",
|
||||
read_timeout=10,
|
||||
).strip()
|
||||
if not raw:
|
||||
|
||||
Reference in New Issue
Block a user