fix: file ownership of files copied into the container (#2007)
Revert to using `cat`/`tar` to copy files/folders into the container.
This commit is contained in:
@@ -5,6 +5,7 @@ import json
|
|||||||
import os
|
import os
|
||||||
import platform
|
import platform
|
||||||
import shlex
|
import shlex
|
||||||
|
import shutil
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
import typing
|
import typing
|
||||||
@@ -169,6 +170,9 @@ class OCIContainer:
|
|||||||
self.cwd = cwd
|
self.cwd = cwd
|
||||||
self.name: str | None = None
|
self.name: str | None = None
|
||||||
self.engine = engine
|
self.engine = engine
|
||||||
|
self.host_tar_format = ""
|
||||||
|
if sys.platform.startswith("darwin"):
|
||||||
|
self.host_tar_format = "--format gnutar"
|
||||||
|
|
||||||
def _get_platform_args(self, *, oci_platform: OCIPlatform | None = None) -> tuple[str, str]:
|
def _get_platform_args(self, *, oci_platform: OCIPlatform | None = None) -> tuple[str, str]:
|
||||||
if oci_platform is None:
|
if oci_platform is None:
|
||||||
@@ -311,10 +315,38 @@ class OCIContainer:
|
|||||||
def copy_into(self, from_path: Path, to_path: PurePath) -> None:
|
def copy_into(self, from_path: Path, to_path: PurePath) -> None:
|
||||||
if from_path.is_dir():
|
if from_path.is_dir():
|
||||||
self.call(["mkdir", "-p", to_path])
|
self.call(["mkdir", "-p", to_path])
|
||||||
call(self.engine.name, "cp", f"{from_path}/.", f"{self.name}:{to_path}")
|
subprocess.run(
|
||||||
|
f"tar -c {self.host_tar_format} -f - . | {self.engine.name} exec -i {self.name} tar --no-same-owner -xC {shell_quote(to_path)} -f -",
|
||||||
|
shell=True,
|
||||||
|
check=True,
|
||||||
|
cwd=from_path,
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
self.call(["mkdir", "-p", to_path.parent])
|
exec_process: subprocess.Popen[bytes]
|
||||||
call(self.engine.name, "cp", from_path, f"{self.name}:{to_path}")
|
with subprocess.Popen(
|
||||||
|
[
|
||||||
|
self.engine.name,
|
||||||
|
"exec",
|
||||||
|
"-i",
|
||||||
|
str(self.name),
|
||||||
|
"sh",
|
||||||
|
"-c",
|
||||||
|
f"cat > {shell_quote(to_path)}",
|
||||||
|
],
|
||||||
|
stdin=subprocess.PIPE,
|
||||||
|
) as exec_process:
|
||||||
|
assert exec_process.stdin
|
||||||
|
with open(from_path, "rb") as from_file:
|
||||||
|
# Bug in mypy, https://github.com/python/mypy/issues/15031
|
||||||
|
shutil.copyfileobj(from_file, exec_process.stdin) # type: ignore[misc]
|
||||||
|
|
||||||
|
exec_process.stdin.close()
|
||||||
|
exec_process.wait()
|
||||||
|
|
||||||
|
if exec_process.returncode:
|
||||||
|
raise subprocess.CalledProcessError(
|
||||||
|
exec_process.returncode, exec_process.args, None, None
|
||||||
|
)
|
||||||
|
|
||||||
def copy_out(self, from_path: PurePath, to_path: Path) -> None:
|
def copy_out(self, from_path: PurePath, to_path: Path) -> None:
|
||||||
# note: we assume from_path is a dir
|
# note: we assume from_path is a dir
|
||||||
|
|||||||
@@ -244,6 +244,9 @@ def test_file_operation(tmp_path: Path, container_engine):
|
|||||||
|
|
||||||
container.copy_into(original_test_file, dst_file)
|
container.copy_into(original_test_file, dst_file)
|
||||||
|
|
||||||
|
owner = container.call(["stat", "-c", "%u:%g", dst_file], capture_output=True).strip()
|
||||||
|
assert owner == "0:0"
|
||||||
|
|
||||||
output = container.call(["cat", dst_file], capture_output=True)
|
output = container.call(["cat", dst_file], capture_output=True)
|
||||||
assert test_binary_data == bytes(output, encoding="utf8", errors="surrogateescape")
|
assert test_binary_data == bytes(output, encoding="utf8", errors="surrogateescape")
|
||||||
|
|
||||||
@@ -266,6 +269,12 @@ def test_dir_operations(tmp_path: Path, container_engine):
|
|||||||
dst_file = dst_dir / "test.dat"
|
dst_file = dst_dir / "test.dat"
|
||||||
container.copy_into(test_dir, dst_dir)
|
container.copy_into(test_dir, dst_dir)
|
||||||
|
|
||||||
|
owner = container.call(["stat", "-c", "%u:%g", dst_dir], capture_output=True).strip()
|
||||||
|
assert owner == "0:0"
|
||||||
|
|
||||||
|
owner = container.call(["stat", "-c", "%u:%g", dst_file], capture_output=True).strip()
|
||||||
|
assert owner == "0:0"
|
||||||
|
|
||||||
output = container.call(["cat", dst_file], capture_output=True)
|
output = container.call(["cat", dst_file], capture_output=True)
|
||||||
assert test_binary_data == bytes(output, encoding="utf8", errors="surrogateescape")
|
assert test_binary_data == bytes(output, encoding="utf8", errors="surrogateescape")
|
||||||
|
|
||||||
@@ -276,6 +285,11 @@ def test_dir_operations(tmp_path: Path, container_engine):
|
|||||||
new_test_dir = tmp_path / "test_dir_new"
|
new_test_dir = tmp_path / "test_dir_new"
|
||||||
container.copy_out(dst_dir, new_test_dir)
|
container.copy_out(dst_dir, new_test_dir)
|
||||||
|
|
||||||
|
assert os.getuid() == new_test_dir.stat().st_uid
|
||||||
|
assert os.getgid() == new_test_dir.stat().st_gid
|
||||||
|
assert os.getuid() == (new_test_dir / "test.dat").stat().st_uid
|
||||||
|
assert os.getgid() == (new_test_dir / "test.dat").stat().st_gid
|
||||||
|
|
||||||
assert test_binary_data == (new_test_dir / "test.dat").read_bytes()
|
assert test_binary_data == (new_test_dir / "test.dat").read_bytes()
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user