mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-11-04 20:22:21 +01:00
fix(docker): repair app cache parent ownership (#6158)
* fix(docker): repair app cache parent ownership * fix(docker): avoid walking mounted model cache * test(docker): exercise nested cache ownership --------- Co-authored-by: Raul <9117159+raultcj@users.noreply.github.com>
This commit is contained in:
+10
-1
@@ -96,7 +96,16 @@ repair_bind_mount_ownership() {
|
|||||||
# Repair image-owned writable paths without walking into bind-mounted host
|
# Repair image-owned writable paths without walking into bind-mounted host
|
||||||
# trees, then repair the app-owned mount roots separately.
|
# trees, then repair the app-owned mount roots separately.
|
||||||
repair_app_tree_ownership
|
repair_app_tree_ownership
|
||||||
for dir in /app/data /app/logs /app/.ssh /app/.cache/huggingface /app/.local; do
|
# Docker creates the parent of the HuggingFace bind mount as root before this
|
||||||
|
# entrypoint runs. Repair only the parent directory itself so app-user caches
|
||||||
|
# such as /app/.cache/vllm and /app/.cache/flashinfer can be created without
|
||||||
|
# recursively walking the mounted model cache.
|
||||||
|
chown "$PUID:$PGID" /app/.cache 2>/dev/null || true
|
||||||
|
# The Hugging Face cache can contain hundreds of gigabytes and is a nested
|
||||||
|
# mount with its own ownership contract. Repair its mount root so new cache
|
||||||
|
# entries are writable, but never traverse or rewrite existing model files.
|
||||||
|
chown "$PUID:$PGID" /app/.cache/huggingface 2>/dev/null || true
|
||||||
|
for dir in /app/data /app/logs /app/.ssh /app/.local; do
|
||||||
repair_bind_mount_ownership "$dir"
|
repair_bind_mount_ownership "$dir"
|
||||||
done
|
done
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,14 @@
|
|||||||
"""Static regressions for Docker/devops hardening contracts."""
|
"""Static regressions for Docker/devops hardening contracts."""
|
||||||
|
|
||||||
import ast
|
import ast
|
||||||
|
import os
|
||||||
import re
|
import re
|
||||||
|
import shutil
|
||||||
|
import subprocess
|
||||||
|
import uuid
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
import pytest
|
||||||
import yaml
|
import yaml
|
||||||
from starlette.applications import Starlette
|
from starlette.applications import Starlette
|
||||||
from starlette.middleware.cors import CORSMiddleware
|
from starlette.middleware.cors import CORSMiddleware
|
||||||
@@ -115,6 +120,85 @@ def test_docker_entrypoint_ownership_repair_stays_inside_expected_mounts():
|
|||||||
assert "Skipping recursive ownership repair" in script
|
assert "Skipping recursive ownership repair" in script
|
||||||
|
|
||||||
|
|
||||||
|
def test_docker_entrypoint_repairs_cache_parent_without_recursive_walk():
|
||||||
|
"""Pin the hard-coded container-path contract without running entrypoint as root."""
|
||||||
|
script = (ROOT / "docker" / "entrypoint.sh").read_text(encoding="utf-8")
|
||||||
|
app_repair = script.index("repair_app_tree_ownership\n")
|
||||||
|
cache_parent_repair = script.index(
|
||||||
|
'chown "$PUID:$PGID" /app/.cache 2>/dev/null || true'
|
||||||
|
)
|
||||||
|
mounted_cache_root_repair = script.index(
|
||||||
|
'chown "$PUID:$PGID" /app/.cache/huggingface 2>/dev/null || true'
|
||||||
|
)
|
||||||
|
|
||||||
|
assert app_repair < cache_parent_repair < mounted_cache_root_repair
|
||||||
|
assert 'repair_tree_ownership "/app/.cache"' not in script
|
||||||
|
assert 'repair_bind_mount_ownership "/app/.cache/huggingface"' not in script
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.skipif(shutil.which("docker") is None, reason="Docker CLI is unavailable")
|
||||||
|
def test_docker_entrypoint_cache_parent_with_nested_volume():
|
||||||
|
"""Run the real entrypoint against a disposable nested-volume layout."""
|
||||||
|
image = os.environ.get("ODYSSEUS_DOCKER_TEST_IMAGE", "odysseus-odysseus:latest")
|
||||||
|
if subprocess.run(
|
||||||
|
["docker", "image", "inspect", image],
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
check=False,
|
||||||
|
).returncode != 0:
|
||||||
|
pytest.skip(f"Docker test image is unavailable: {image}")
|
||||||
|
|
||||||
|
volume = f"odysseus-cache-parent-test-{uuid.uuid4().hex}"
|
||||||
|
subprocess.run(
|
||||||
|
["docker", "volume", "create", volume],
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
check=True,
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
subprocess.run(
|
||||||
|
[
|
||||||
|
"docker", "run", "--rm", "--pull=never",
|
||||||
|
"--entrypoint", "sh",
|
||||||
|
"-v", f"{volume}:/fixture",
|
||||||
|
image,
|
||||||
|
"-c", "mkdir -p /fixture/nested && touch /fixture/nested/sentinel",
|
||||||
|
],
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
check=True,
|
||||||
|
)
|
||||||
|
result = subprocess.run(
|
||||||
|
[
|
||||||
|
"docker", "run", "--rm", "--pull=never",
|
||||||
|
"-e", "PUID=23456",
|
||||||
|
"-e", "PGID=23456",
|
||||||
|
"-v", f"{volume}:/app/.cache/huggingface",
|
||||||
|
image,
|
||||||
|
"sh", "-c",
|
||||||
|
"mkdir -p /app/.cache/vllm && "
|
||||||
|
"touch /app/.cache/vllm/probe && "
|
||||||
|
"printf 'CACHE_TEST %s %s %s %s\\n' "
|
||||||
|
"\"$(stat -c %u /app/.cache)\" "
|
||||||
|
"\"$(stat -c %u /app/.cache/vllm/probe)\" "
|
||||||
|
"\"$(stat -c %u /app/.cache/huggingface)\" "
|
||||||
|
"\"$(stat -c %u /app/.cache/huggingface/nested/sentinel)\"",
|
||||||
|
],
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
check=True,
|
||||||
|
)
|
||||||
|
finally:
|
||||||
|
subprocess.run(
|
||||||
|
["docker", "volume", "rm", "-f", volume],
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
check=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert "CACHE_TEST 23456 23456 23456 0" in result.stdout
|
||||||
|
|
||||||
|
|
||||||
def test_dockerignore_excludes_secrets_editor_backups():
|
def test_dockerignore_excludes_secrets_editor_backups():
|
||||||
patterns = set((ROOT / ".dockerignore").read_text(encoding="utf-8").splitlines())
|
patterns = set((ROOT / ".dockerignore").read_text(encoding="utf-8").splitlines())
|
||||||
assert {
|
assert {
|
||||||
|
|||||||
Reference in New Issue
Block a user