Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions python/ray/experimental/sandbox/_internal/image_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@ def extract_tar_layer(
else:
tar_fileobj = tar_input

dir_mtimes = []
Comment thread
xyuzh marked this conversation as resolved.
with tarfile.open(fileobj=tar_fileobj, mode="r:*") as tar:
for member in tar.getmembers():
name = member.name.lstrip("/")
Expand Down Expand Up @@ -261,8 +262,22 @@ def extract_tar_layer(
shutil.copyfileobj(f_in, f_out)
if member.mode:
os.chmod(target_path, member.mode)
# Preserve the archived mtime: tools inside the sandbox rely
# on it (apt revalidates its package lists with
# If-Modified-Since from the file mtime, and a reset-to-now
# mtime makes mirrors answer 304 for stale baked lists).
# Best-effort, like the directory pass below.
try:
os.utime(target_path, (member.mtime, member.mtime))
except OSError:
pass
elif member.isdir():
os.makedirs(target_path, exist_ok=True)
# Applied after the loop: extracting children would bump it.
# Skip preserved symlinks (UsrMerge /bin -> usr/bin): utime
# would follow them and stamp the target with the wrong time.
if not os.path.islink(target_path):
dir_mtimes.append((target_path, member.mtime))
elif member.issym():
os.makedirs(parent_dir, exist_ok=True)
try:
Expand All @@ -280,6 +295,12 @@ def extract_tar_layer(
except OSError:
pass

for dir_path, mtime in dir_mtimes:
try:
os.utime(dir_path, (mtime, mtime))
except OSError:
pass


def pull_and_extract_container_image(
image: str,
Expand Down
32 changes: 32 additions & 0 deletions python/ray/experimental/sandbox/tests/test_image_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -510,5 +510,37 @@ def test_prepare_oci_bundle_no_resolv_without_host_side_networking(tmp_path):
assert _prepare(mgr, tmp_path, network=network) is None


def test_extract_tar_layer_preserves_mtimes(tmp_path):
"""Archived mtimes survive extraction: apt inside the sandbox validates
its package lists with If-Modified-Since from the file mtime, so a
reset-to-extraction-time mtime makes mirrors answer 304 for stale
image-baked lists."""
import io
import os
import tarfile

from ray.experimental.sandbox._internal.image_utils import extract_tar_layer

archived_mtime = 1_600_000_000 # 2020-09-13, clearly not "now"
buf = io.BytesIO()
with tarfile.open(fileobj=buf, mode="w") as tar:
dir_info = tarfile.TarInfo("etc")
dir_info.type = tarfile.DIRTYPE
dir_info.mtime = archived_mtime
tar.addfile(dir_info)
file_info = tarfile.TarInfo("etc/os-release")
data = b"ID=debian\n"
file_info.size = len(data)
file_info.mtime = archived_mtime
tar.addfile(file_info, io.BytesIO(data))

dest = tmp_path / "rootfs"
dest.mkdir()
extract_tar_layer(buf.getvalue(), str(dest))

assert int(os.path.getmtime(dest / "etc" / "os-release")) == archived_mtime
assert int(os.path.getmtime(dest / "etc")) == archived_mtime


if __name__ == "__main__":
sys.exit(pytest.main(["-v", __file__]))
Loading