objectstore: add new skip_preserve_owner to Object.export()

This commit allows to exclude preserving ownership from an object
export. This is required to fix the issue that on macOS the an
podman based workflow cannot export objects with preserving
ownerships.

Originally this was a `no_preserve: Optional[List[str]] = None)`
to be super flexible in what we pass to `cp` but then I felt like
YAGNI - if we need more we can trivially change this (internal)
API again :)
This commit is contained in:
Michael Vogt 2023-12-19 17:35:29 +01:00
parent 975057a0a9
commit e35d841509
2 changed files with 42 additions and 11 deletions

View file

@ -244,18 +244,20 @@ class Object:
if self.mode != want:
raise ValueError(f"Wrong object mode: {self.mode}, want {want}")
def export(self, to_directory: PathLike):
def export(self, to_directory: PathLike, skip_preserve_owner=False):
"""Copy object into an external directory"""
subprocess.run(
[
"cp",
"--reflink=auto",
"-a",
os.fspath(self.tree) + "/.",
os.fspath(to_directory),
],
check=True,
)
cp_cmd = [
"cp",
"--reflink=auto",
"-a",
]
if skip_preserve_owner:
cp_cmd += ["--no-preserve=ownership"]
cp_cmd += [
os.fspath(self.tree) + "/.",
os.fspath(to_directory),
]
subprocess.run(cp_cmd, check=True)
def __fspath__(self):
return self.tree

View file

@ -320,3 +320,32 @@ def test_store_server(tmp_path):
with pytest.raises(RuntimeError):
_ = client.read_tree_at("42", tmp_path, "/nonexistent")
@pytest.mark.skipif(os.getuid() != 0, reason="needs root")
def test_object_export_preserves_ownership_by_default(tmp_path, object_store):
tree = object_store.new("a")
p = Path(tree, "A")
p.touch()
os.chown(os.fspath(p), 1000, 1000)
tree.export(tmp_path)
expected_exported_path = Path(tmp_path, "A")
assert expected_exported_path.exists()
assert expected_exported_path.stat().st_uid == 1000
assert expected_exported_path.stat().st_gid == 1000
@pytest.mark.skipif(os.getuid() != 0, reason="needs root")
def test_object_export_preserves_override(tmp_path, object_store):
tree = object_store.new("a")
p = Path(tree, "A")
p.touch()
os.chown(os.fspath(p), 1000, 1000)
tree.export(tmp_path, skip_preserve_owner=True)
expected_exported_path = Path(tmp_path, "A")
assert expected_exported_path.exists()
assert expected_exported_path.stat().st_uid == 0
assert expected_exported_path.stat().st_gid == 0