Skip to content

Commit

Permalink
org.osbuild.mkdir: support creating dirs on mounts
Browse files Browse the repository at this point in the history
This allows creating new directories on mounts:
```
- type: org.osbuild.mkdir
  options:
    paths:
      - path: mount:///boot/efi
  devices:
    disk: ...
  mounts:
    - name: boot
      target: /boot
      ...
```
  • Loading branch information
nikita-dubrovskii committed Oct 23, 2024
1 parent e5e3aad commit 3f8e100
Show file tree
Hide file tree
Showing 3 changed files with 119 additions and 13 deletions.
22 changes: 12 additions & 10 deletions stages/org.osbuild.mkdir
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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()))
21 changes: 18 additions & 3 deletions stages/org.osbuild.mkdir.meta.json
Original file line number Diff line number Diff line change
@@ -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",
Expand Down Expand Up @@ -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",
Expand Down
89 changes: 89 additions & 0 deletions stages/test/test_mkdir.py
Original file line number Diff line number Diff line change
@@ -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()

0 comments on commit 3f8e100

Please sign in to comment.