mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-09-10 10:12:20 +02: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
|
||||
# trees, then repair the app-owned mount roots separately.
|
||||
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"
|
||||
done
|
||||
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
"""Static regressions for Docker/devops hardening contracts."""
|
||||
|
||||
import ast
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
import uuid
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
import yaml
|
||||
from starlette.applications import Starlette
|
||||
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
|
||||
|
||||
|
||||
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():
|
||||
patterns = set((ROOT / ".dockerignore").read_text(encoding="utf-8").splitlines())
|
||||
assert {
|
||||
|
||||
Reference in New Issue
Block a user