Skip to content

Commit 17bcec3

Browse files
authored
Merge pull request #97 from desultory/dev
Add cryptsetup test, improve code formatting
2 parents 3b30b07 + b0e8deb commit 17bcec3

File tree

7 files changed

+135
-33
lines changed

7 files changed

+135
-33
lines changed

src/ugrd/base/test.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
__version__ = "1.0.0"
1+
__version__ = "1.1.0"
22

33
from zenlib.util import unset
44

5-
COPY_CONFIG = ["mounts", "tmpdir", "clean", "test_image_size", "test_flag"]
5+
COPY_CONFIG = ["mounts", "tmpdir", "clean", "test_image_size", "test_flag", "cryptsetup"]
66

77

88
@unset("test_kernel")
@@ -75,7 +75,7 @@ def make_test_image(self):
7575
"modules": "ugrd.fs.test_image",
7676
"out_file": self["test_rootfs_name"],
7777
"build_dir": self["test_rootfs_build_dir"],
78-
**{key: self[key] for key in COPY_CONFIG},
78+
**{key: self.get(key) for key in COPY_CONFIG if self.get(key) is not None},
7979
}
8080

8181
target_fs = InitramfsGenerator(**kwargs)

src/ugrd/fs/test_image.py

Lines changed: 52 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
__version__ = "0.8.0"
1+
__version__ = "1.0.0"
22

33
from zenlib.util import contains
44

@@ -9,7 +9,7 @@ def init_banner(self):
99
self["banner"] = f"echo {self['test_flag']}"
1010

1111

12-
def _allocate_image(self, image_path):
12+
def _allocate_image(self, image_path, padding=0):
1313
"""Allocate the test image size"""
1414
if image_path.exists():
1515
if self.clean:
@@ -20,33 +20,70 @@ def _allocate_image(self, image_path):
2020

2121
with open(image_path, "wb") as f:
2222
self.logger.info("Allocating test image file: %s" % f.name)
23-
f.write(b"\0" * self.test_image_size * 2**20)
23+
f.write(b"\0" * (self.test_image_size + padding) * 2**20)
2424

2525

26+
def _get_luks_config(self):
27+
""" Gets the LUKS configuration from the passed cryptsetup config using _cryptsetup_root as the key,
28+
if not found, uses the first defined luks device if there is only one, otherwise raises an exception """
29+
if dev := self["cryptsetup"].get(self["_cryptsetup_root"]):
30+
return dev
31+
if len(self["cryptsetup"]) == 1:
32+
return next(iter(self["cryptsetup"].values()))
33+
raise ValueError("Could not find a LUKS configuration")
34+
35+
def _get_luks_uuid(self):
36+
""" Gets the uuid from the cryptsetup root config """
37+
return _get_luks_config(self).get("uuid")
38+
39+
40+
def _get_luks_keyfile(self):
41+
""" Gets the luks keyfile the cryptsetup root config,
42+
if not defined, generates a keyfile using the banner """
43+
config = _get_luks_config(self)
44+
if keyfile := config.get("key_file"):
45+
return keyfile
46+
raise ValueError("No LUKS key_file is set.")
47+
48+
def make_test_luks_image(self, image_path):
49+
""" Creates a LUKS image to hold the test image """
50+
try:
51+
self._run(["cryptsetup", "status", "test_image"]) # Check if the LUKS device is already open
52+
self.logger.warning("LUKS device 'test_image' is already open, closing it")
53+
self._run(["cryptsetup", "luksClose", "test_image"])
54+
except RuntimeError:
55+
pass
56+
_allocate_image(self, image_path, padding=32) # First allocate the image file, adding padding for the LUKS header
57+
keyfile_path = _get_luks_keyfile(self)
58+
self.logger.info("Using LUKS keyfile: %s" % keyfile_path)
59+
self.logger.info("Creating LUKS image: %s" % image_path)
60+
self._run(["cryptsetup", "luksFormat", image_path, "--uuid", _get_luks_uuid(self), "--batch-mode", "--key-file", keyfile_path])
61+
self.logger.info("Opening LUKS image: %s" % image_path)
62+
self._run(["cryptsetup", "luksOpen", image_path, "test_image", "--key-file", keyfile_path])
63+
2664
def make_test_image(self):
2765
"""Creates a test image from the build dir"""
2866
build_dir = self._get_build_path("/").resolve()
2967
self.logger.info("Creating test image from: %s" % build_dir)
3068

3169
rootfs_uuid = self["mounts"]["root"]["uuid"]
3270
rootfs_type = self["mounts"]["root"]["type"]
33-
3471
image_path = self._get_out_path(self["out_file"])
35-
if rootfs_type == "ext4":
36-
# Create the test image file, flll with 0s
72+
73+
if self.get("cryptsetup"): # If there is cryptsetup config, create a LUKS image
74+
make_test_luks_image(self, image_path)
75+
image_path = "/dev/mapper/test_image"
76+
else:
3777
_allocate_image(self, image_path)
78+
79+
if rootfs_type == "ext4":
3880
self._run(["mkfs", "-t", rootfs_type, "-d", build_dir, "-U", rootfs_uuid, "-F", image_path])
3981
elif rootfs_type == "btrfs":
40-
if self["clean"] and image_path.exists():
41-
self.logger.warning("Removing existing test image file: %s" % image_path)
42-
image_path.unlink()
4382
self._run(["mkfs", "-t", rootfs_type, "-f", "--rootdir", build_dir, "-U", rootfs_uuid, image_path])
4483
elif rootfs_type == "xfs":
45-
_allocate_image(self, image_path)
4684
self._run(["mkfs", "-t", rootfs_type, "-m", "uuid=%s" % rootfs_uuid, image_path])
4785
try: # XFS doesn't support importing a directory as a filesystem, it must be mounted
4886
from tempfile import TemporaryDirectory
49-
5087
with TemporaryDirectory() as tmp_dir:
5188
self._run(["mount", image_path, tmp_dir])
5289
self._run(["cp", "-a", f"{build_dir}/.", tmp_dir])
@@ -55,3 +92,7 @@ def make_test_image(self):
5592
raise RuntimeError("Could not mount the XFS test image: %s", e)
5693
else:
5794
raise NotImplementedError("Unsupported test rootfs type: %s" % rootfs_type)
95+
96+
if self.get("cryptsetup"): # Leave it open in the event of failure, close it before executing tests
97+
self.logger.info("Closing LUKS image: %s" % image_path)
98+
self._run(["cryptsetup", "luksClose", "test_image"])

src/ugrd/fs/test_image.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ paths = ['dev', 'sys', 'proc']
33

44
test_image_size = 16
55
shebang = "#!/bin/bash"
6+
_cryptsetup_root = "root"
7+
68

79
[imports.build_pre]
810
"ugrd.fs.test_image" = ["init_banner"]
@@ -13,6 +15,8 @@ shebang = "#!/bin/bash"
1315
[custom_parameters]
1416
shebang = "str" # Add the shebang property, because the test mode disables the base module
1517
mounts = "dict" # We only need the mounts dict, not the whole module
18+
cryptsetup = "dict" # Same as above
19+
_cryptsetup_root = "str" # Define the root device for cryptsetup
1620
test_image_size = "int" # Define the size of the test image in MiB
1721
test_flag = "str" # Define the success flag used to determine if the test was successful
1822

tests/cryptsetup_included_key.toml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Test included keyfile
2+
3+
modules = [
4+
"ugrd.crypto.cryptsetup",
5+
"ugrd.base.test",
6+
]
7+
8+
# The initramfs will be built in /tmp/initramfs if "build_dir" is not specified not specified
9+
out_dir = "initramfs_test"
10+
test_memory = '2G'
11+
12+
#kernel_version = "6.6.35-gentoo-dist"
13+
#test_kernel = "/boot/vmlinuz-6.6.35-gentoo-dist"
14+
cpio_compression = false
15+
hostonly = false
16+
17+
[cryptsetup.root]
18+
uuid = "abcd1234-abcd-1234-abcd-1234abcd1234"
19+
key_file = "/tmp/ugrd_test_key" # Currently hardcoded for this test
20+
include_key = true
21+
22+
[mounts.root]
23+
uuid = "10101010-abcd-1234-abcd-101010101010"
24+
type = "ext4"
25+

tests/test_cryptsetup.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
from unittest import TestCase, main
2+
3+
from ugrd.initramfs_generator import InitramfsGenerator
4+
from zenlib.logging import loggify
5+
6+
7+
@loggify
8+
class TestCryptsetup(TestCase):
9+
def setUp(self):
10+
"""Create the test keyfile"""
11+
from pathlib import Path
12+
from uuid import uuid4
13+
14+
keyfile = Path("/tmp/ugrd_test_key")
15+
if keyfile.exists():
16+
raise FileExistsError("Test Keyfile already exists!: %s" % keyfile)
17+
18+
with open("/tmp/ugrd_test_key", "wb") as f:
19+
f.write(uuid4().bytes)
20+
21+
def tearDown(self):
22+
"""Remove the test keyfile"""
23+
from pathlib import Path
24+
25+
keyfile = Path("/tmp/ugrd_test_key")
26+
if keyfile.exists():
27+
keyfile.unlink()
28+
29+
def test_cryptsetup_included_key(self):
30+
generator = InitramfsGenerator(logger=self.logger, config="tests/cryptsetup_included_key.toml")
31+
generator.build()
32+
33+
34+
if __name__ == "__main__":
35+
main()

tests/test_output_paths.py

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,41 @@
1-
from unittest import TestCase, main
21
from pathlib import Path
2+
from unittest import TestCase, main
33
from uuid import uuid4
44

55
from ugrd.initramfs_generator import InitramfsGenerator
6-
76
from zenlib.logging import loggify
87

98

109
@loggify
1110
class TestOutFile(TestCase):
1211
def test_absolute_out_file(self):
13-
out_file = Path(f'/tmp/{uuid4()}.cpio')
12+
out_file = Path(f"/tmp/{uuid4()}.cpio")
1413
if out_file.exists():
15-
self.fail(f'File {out_file} already exists')
16-
generator = InitramfsGenerator(logger=self.logger, config='tests/fullauto.toml', out_file=out_file)
14+
self.fail(f"File {out_file} already exists")
15+
generator = InitramfsGenerator(logger=self.logger, config="tests/fullauto.toml", out_file=out_file)
1716
generator.build()
1817
self.assertTrue(out_file.exists())
1918
out_file.unlink()
2019

2120
def test_named_out_file(self):
2221
out_file = Path(f"{uuid4()}.cpio")
23-
generator = InitramfsGenerator(logger=self.logger, config='tests/fullauto.toml', out_file=out_file)
22+
generator = InitramfsGenerator(logger=self.logger, config="tests/fullauto.toml", out_file=out_file)
2423
out_path = generator._get_out_path(out_file)
2524
if out_path.exists():
26-
self.fail(f'File {out_path} already exists')
25+
self.fail(f"File {out_path} already exists")
2726
generator.build()
2827
self.assertTrue(out_path.exists())
2928

30-
3129
def test_relative_out_file(self):
32-
out_file = f'./{uuid4()}.cpio'
33-
generator = InitramfsGenerator(logger=self.logger, config='tests/fullauto.toml', out_file=out_file)
30+
out_file = f"./{uuid4()}.cpio"
31+
generator = InitramfsGenerator(logger=self.logger, config="tests/fullauto.toml", out_file=out_file)
3432
out_path = Path(out_file)
3533
if out_path.exists():
36-
self.fail(f'File {out_file} already exists')
34+
self.fail(f"File {out_file} already exists")
3735
generator.build()
3836
self.assertTrue(out_path.exists())
3937
out_path.unlink()
4038

4139

42-
if __name__ == '__main__':
40+
if __name__ == "__main__":
4341
main()

tests/test_ugrd.py

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,24 @@
1-
from unittest import TestCase, main, expectedFailure
1+
from unittest import TestCase, expectedFailure, main
22

33
from ugrd.initramfs_generator import InitramfsGenerator
4-
54
from zenlib.logging import loggify
65

76

87
@loggify
9-
class TestCpio(TestCase):
8+
class TestUGRD(TestCase):
109
def test_fullauto(self):
11-
generator = InitramfsGenerator(logger=self.logger, config='tests/fullauto.toml')
10+
generator = InitramfsGenerator(logger=self.logger, config="tests/fullauto.toml")
1211
generator.build()
1312

1413
def test_xz(self):
15-
generator = InitramfsGenerator(logger=self.logger, config='tests/fullauto.toml', cpio_compression='xz')
14+
generator = InitramfsGenerator(logger=self.logger, config="tests/fullauto.toml", cpio_compression="xz")
1615
generator.build()
1716

1817
@expectedFailure
1918
def test_bad_config(self):
20-
generator = InitramfsGenerator(logger=self.logger, config='tests/bad_config.toml')
19+
generator = InitramfsGenerator(logger=self.logger, config="tests/bad_config.toml")
2120
generator.build()
2221

2322

24-
if __name__ == '__main__':
23+
if __name__ == "__main__":
2524
main()

0 commit comments

Comments
 (0)