org.osbuild.mkdir: support creating dirs on mounts

This allows creating new directories on mounts:
```
- type: org.osbuild.mkdir
  options:
    paths:
      - path: mount:///boot/efi
  devices:
    disk: ...
  mounts:
    - name: boot
      target: /boot
      ...
```
This commit is contained in:
Nikita Dubrovskii 2024-09-30 15:46:31 +02:00 committed by Achilleas Koutsou
parent ad7c646712
commit 23f01307b2
3 changed files with 119 additions and 13 deletions

View file

@ -3,23 +3,26 @@ import os
import sys
import osbuild.api
from osbuild.util.path import in_tree
from osbuild.util import parsing
def main(tree, options):
def main(args):
options = args["options"]
for item in options["paths"]:
path = item["path"]
mode = item.get("mode", 0o777)
parents = item.get("parents", False)
exist_ok = item.get("exist_ok", False)
if not path.startswith("/"):
print("WARNING: relative path used, this is discouraged!")
target = os.path.join(tree, path.lstrip("/"))
if not in_tree(target, tree):
raise ValueError(f"path {path} not in tree")
if "://" not in path:
if not path.startswith("/"):
print("WARNING: relative path used, this is discouraged!")
path = f"tree:///{path}"
else:
path = f"tree://{path}"
target = parsing.parse_location(path, args)
if parents:
os.makedirs(target, mode=mode, exist_ok=exist_ok)
else:
@ -33,5 +36,4 @@ def main(tree, options):
if __name__ == "__main__":
args = osbuild.api.arguments()
sys.exit(main(args["tree"], args["options"]))
sys.exit(main(osbuild.api.arguments()))

View file

@ -1,5 +1,5 @@
{
"summary": "Create directories within the tree.",
"summary": "Create directories within the tree or mount.",
"description": [
"Can create one or more directories, optionally also the",
"intermediate directories. The stage can gracefully handle",
@ -31,8 +31,23 @@
],
"properties": {
"path": {
"type": "string",
"pattern": "^\\/?(?!\\.\\.)((?!\\/\\.\\.\\/).)+$"
"anyOf": [
{
"type": "string",
"description": "Target path, if a tree",
"pattern": "^\\/?(?!\\.\\.)((?!\\/\\.\\.\\/).)+$"
},
{
"type": "string",
"description": "Target path, if a mount",
"pattern": "^mount://.+"
},
{
"type": "string",
"description": "Target path, if a tree",
"pattern": "^tree://.+"
}
]
},
"mode": {
"type": "number",

89
stages/test/test_mkdir.py Normal file
View file

@ -0,0 +1,89 @@
#!/usr/bin/python3
import contextlib
import os
import subprocess
import pytest # type: ignore
from osbuild.testutil import has_executable
STAGE_NAME = "org.osbuild.mkdir"
def test_mkdir(tmp_path, stage_module):
tree = tmp_path / "tree"
tree.mkdir()
options = {
"paths": [
{"path": "/fake_dir"},
{"path": "fake_relative_dir"}
]
}
args = {
"tree": f"{tree}",
"options": options
}
stage_module.main(args)
assert (tree / "fake_dir").exists()
assert (tree / "fake_relative_dir").exists()
def test_mkdir_on_a_tree(tmp_path, stage_module):
tree = tmp_path / "tree"
tree.mkdir()
options = {
"paths": [
{
"path": "tree:///fake_parent/fake_dir",
"parents": 1
}
]
}
args = {
"tree": f"{tree}",
"options": options
}
stage_module.main(args)
assert (tree / "fake_parent/fake_dir").exists()
@pytest.mark.skipif(os.getuid() != 0, reason="needs root")
@pytest.mark.skipif(not has_executable("mkfs.ext4"), reason="need mkfs.ext4")
def test_mkdir_on_a_mount(tmp_path, stage_module):
tree = tmp_path / "tree"
tree.mkdir()
# Create fake EXT4 disk image
fake_disk_path = tmp_path / "fake.img"
with fake_disk_path.open("w") as fp:
fp.truncate(10 * 1024 * 1024)
subprocess.run(
["mkfs.ext4", os.fspath(fake_disk_path)], check=True)
fake_disk_mnt = tmp_path / "mounts"
fake_disk_mnt.mkdir()
with contextlib.ExitStack() as cm:
subprocess.run(["mount", fake_disk_path, fake_disk_mnt], check=True)
cm.callback(subprocess.run, ["umount", fake_disk_mnt], check=True)
options = {
"paths": [
{
"path": "mount:///fake_parent/fake_dir",
"parents": 1
}
]
}
args = {
"tree": f"{tree}",
"options": options,
"paths": {
"mounts": fake_disk_mnt,
}
}
stage_module.main(args)
assert (fake_disk_mnt / "fake_parent/fake_dir").exists()