Salvages #24490 by @liuhao1024 against current main. The Docker daemon will silently auto-create a directory at the host path of any `-v <host>:<container>` bind mount when the host path doesn't exist. In Docker-in-Docker setups (where the outer host's real credential file isn't visible inside the agent's parent container), this leaves a directory at the credential mount source — and the inner `docker run` then refuses to mount a directory over a file destination with exit 125. Add defensive shape guards to all three mount loops in DockerEnvironment.__init__: * credentials (expected: file) — skip + warn on directory or missing * skills (expected: dir) — skip + warn when not a directory * cache (expected: dir) — skip + warn when not a directory Failed mounts surface as WARN logs rather than crashing the container start. Existing well-formed sources mount unchanged. The original PR's branch was on a pre-container-reuse-rework base (May 12) and conflicted with the post-May-28 driver work (label tagging, container reuse, orphan reaper). Reconstructed the same intent on current main; the three guard blocks slot cleanly into `tools/environments/docker.py` around the existing mount loops. Three new tests pinned in `tests/tools/test_docker_environment.py`: directory-source skip, missing-source skip, valid-file mounts. Test- first regression verification: reverted just the production code to `origin/main` and confirmed the new tests fail with `'deleted_token.json' is contained here: /root/.hermes/...` — the fixed code makes them pass. Full file passes (54/54). Closes #24490 Co-authored-by: liuhao1024 <11816344+liuhao1024@users.noreply.github.com>
This commit is contained in:
co-authored by
liuhao1024
parent
69b74c15a3
commit
40fa0c1d19
@@ -12,6 +12,7 @@ import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import uuid
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
from tools.environments.base import BaseEnvironment, _popen_bash
|
||||
@@ -577,6 +578,22 @@ class DockerEnvironment(BaseEnvironment):
|
||||
)
|
||||
|
||||
for mount_entry in get_credential_file_mounts():
|
||||
src = Path(mount_entry["host_path"])
|
||||
if src.is_dir():
|
||||
# Docker-in-Docker: Docker auto-created the source path as
|
||||
# a directory when it didn't exist on the host. Mounting a
|
||||
# directory over a file destination causes exit 125.
|
||||
logger.warning(
|
||||
"Docker: skipping credential mount — source is a directory "
|
||||
"(likely Docker-in-Docker auto-creation): %s",
|
||||
src,
|
||||
)
|
||||
continue
|
||||
if not src.is_file():
|
||||
logger.warning(
|
||||
"Docker: skipping credential mount — source not found: %s", src,
|
||||
)
|
||||
continue
|
||||
volume_args.extend([
|
||||
"-v",
|
||||
f"{mount_entry['host_path']}:{mount_entry['container_path']}:ro",
|
||||
@@ -590,6 +607,13 @@ class DockerEnvironment(BaseEnvironment):
|
||||
# Mount skill directories (local + external) so skill
|
||||
# scripts/templates are available inside the container.
|
||||
for skills_mount in get_skills_directory_mount():
|
||||
src = Path(skills_mount["host_path"])
|
||||
if not src.is_dir():
|
||||
logger.warning(
|
||||
"Docker: skipping skills mount — source is not a directory: %s",
|
||||
src,
|
||||
)
|
||||
continue
|
||||
volume_args.extend([
|
||||
"-v",
|
||||
f"{skills_mount['host_path']}:{skills_mount['container_path']}:ro",
|
||||
@@ -605,6 +629,13 @@ class DockerEnvironment(BaseEnvironment):
|
||||
# cached media from inside the container. Read-only — the
|
||||
# container reads these but the host gateway manages writes.
|
||||
for cache_mount in get_cache_directory_mounts():
|
||||
src = Path(cache_mount["host_path"])
|
||||
if not src.is_dir():
|
||||
logger.warning(
|
||||
"Docker: skipping cache mount — source is not a directory: %s",
|
||||
src,
|
||||
)
|
||||
continue
|
||||
volume_args.extend([
|
||||
"-v",
|
||||
f"{cache_mount['host_path']}:{cache_mount['container_path']}:ro",
|
||||
|
||||
Reference in New Issue
Block a user