Skip to content

Commit

Permalink
bib: extract new partition table helpers and add new DiskCustomization
Browse files Browse the repository at this point in the history
This commit makes use of the excellent work in
osbuild/images#1041 and wires up support
to generate LVM and btrfs volumes via the new disk customizations.

It also add tests.
  • Loading branch information
mvo5 committed Nov 22, 2024
1 parent 2c4fe64 commit 60169e7
Show file tree
Hide file tree
Showing 4 changed files with 132 additions and 5 deletions.
42 changes: 40 additions & 2 deletions bib/cmd/bootc-image-builder/image.go
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,44 @@ func setFSTypes(pt *disk.PartitionTable, rootfs string) error {
}

func genPartitionTable(c *ManifestConfig, customizations *blueprint.Customizations, rng *rand.Rand) (*disk.PartitionTable, error) {
fsCust := customizations.GetFilesystems()
diskCust, err := customizations.GetPartitioning()
if err != nil {
return nil, fmt.Errorf("error reading disk customizations: %w", err)
}
switch {
// XXX: move into images library
case fsCust != nil && diskCust != nil:
return nil, fmt.Errorf("cannot combine disk and filesystem customizations")
case diskCust != nil:
return genPartitionTableDiskCust(c, diskCust, rng)
default:
return genPartitionTableFsCust(c, fsCust, rng)
}
}

func genPartitionTableDiskCust(c *ManifestConfig, diskCust *blueprint.DiskCustomization, rng *rand.Rand) (*disk.PartitionTable, error) {
diskCust.MinSize = max(diskCust.MinSize, c.RootfsMinsize)

basept, ok := partitionTables[c.Architecture.String()]
if !ok {
return nil, fmt.Errorf("pipelines: no partition tables defined for %s", c.Architecture)
}
defaultFSType, err := disk.NewFSType(c.RootFSType)
if err != nil {
return nil, err
}

partOptions := &disk.CustomPartitionTableOptions{
PartitionTableType: basept.Type,
// XXX: not setting/defaults will fail to boot with btrfs/lvm
BootMode: platform.BOOT_HYBRID,
DefaultFSType: defaultFSType,
}
return disk.NewCustomPartitionTable(diskCust, partOptions, rng)
}

func genPartitionTableFsCust(c *ManifestConfig, fsCust []blueprint.FilesystemCustomization, rng *rand.Rand) (*disk.PartitionTable, error) {
basept, ok := partitionTables[c.Architecture.String()]
if !ok {
return nil, fmt.Errorf("pipelines: no partition tables defined for %s", c.Architecture)
Expand All @@ -211,10 +249,10 @@ func genPartitionTable(c *ManifestConfig, customizations *blueprint.Customizatio
if c.RootFSType == "btrfs" {
partitioningMode = disk.BtrfsPartitioningMode
}
if err := checkFilesystemCustomizations(customizations.GetFilesystems(), partitioningMode); err != nil {
if err := checkFilesystemCustomizations(fsCust, partitioningMode); err != nil {
return nil, err
}
fsCustomizations := updateFilesystemSizes(customizations.GetFilesystems(), c.RootfsMinsize)
fsCustomizations := updateFilesystemSizes(fsCust, c.RootfsMinsize)

pt, err := disk.NewPartitionTable(&basept, fsCustomizations, DEFAULT_SIZE, partitioningMode, nil, rng)
if err != nil {
Expand Down
2 changes: 1 addition & 1 deletion package-requires.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# from the Containerfile by default, using leading '#' as comments.

# This project uses osbuild
osbuild osbuild-ostree osbuild-depsolve-dnf
osbuild osbuild-ostree osbuild-depsolve-dnf osbuild-lvm2

# We mount container images internally
podman
Expand Down
81 changes: 81 additions & 0 deletions test/test_manifest.py
Original file line number Diff line number Diff line change
Expand Up @@ -550,3 +550,84 @@ def test_manifest_fips_customization(tmp_path, build_container):
], text=True)
st = find_grub2_iso_stage_from(output)
assert "fips=1" in st["options"]["kernel"]["opts"]



def find_bootc_install_to_fs_stage_from(manifest_str):
manifest = json.loads(manifest_str)
for pipeline in manifest["pipelines"]:
# the fstab stage in cross-arch manifests is in the "ostree-deployment" pipeline
if pipeline["name"] == "image":
for st in pipeline["stages"]:
if st["type"] == "org.osbuild.bootc.install-to-filesystem":
return st
raise ValueError(f"cannot find bootc.install-to-filesystem stage in manifest:\n{manifest_str}")


def test_manifest_disk_customization_lvm(tmp_path, build_container):
container_ref = "quay.io/centos-bootc/centos-bootc:stream9"

config = {
"customizations": {
"disk": {
"partitions": [
{
"type": "lvm",
"logical_volumes": [
{
"fs_type": "ext4",
"mountpoint": "/",
}
]
}
]
}
}
}
config_path = tmp_path / "config.json"
with config_path.open("w") as config_file:
json.dump(config, config_file)

output = subprocess.check_output([
*testutil.podman_run_common,
"-v", f"{config_path}:/config.json:ro",
build_container,
"manifest", f"{container_ref}",
])
st = find_bootc_install_to_fs_stage_from(output)
assert st["devices"]["rootlv"]["type"] == "org.osbuild.lvm2.lv"


def test_manifest_disk_customization_btrfs(tmp_path, build_container):
container_ref = "quay.io/centos-bootc/centos-bootc:stream9"

config = {
"customizations": {
"disk": {
"partitions": [
{
"type": "btrfs",
"subvolumes": [
{
"name": "root",
"mountpoint": "/",
}
]
}
]
}
}
}
config_path = tmp_path / "config.json"
with config_path.open("w") as config_file:
json.dump(config, config_file)

output = subprocess.check_output([
*testutil.podman_run_common,
"-v", f"{config_path}:/config.json:ro",
build_container,
"manifest", f"{container_ref}",
])
st = find_bootc_install_to_fs_stage_from(output)
assert st["mounts"][0]["type"] == "org.osbuild.btrfs"
assert st["mounts"][0]["target"] == "/"
12 changes: 10 additions & 2 deletions test/testcases.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ class TestCase:
rootfs: str = ""
# Sign the container_ref and use the new signed image instead of the original one
sign: bool = False
# use special partition_mode like "lvm"
partition_mode: str = ""

def bib_rootfs_args(self):
if self.rootfs:
Expand Down Expand Up @@ -82,11 +84,17 @@ def gen_testcases(what): # pylint: disable=too-many-return-statements
TestCaseCentos(image="anaconda-iso"),
]
if what == "qemu-boot":
# test partition defaults with qcow2
test_cases = [
klass(image=img)
klass(image="qcow2")
for klass in (TestCaseCentos, TestCaseFedora)
for img in ("raw", "qcow2")
]
# and custom with raw (this is arbitrary, we could do it the
# other way around too
test_cases.append(
TestCaseCentos(image="raw", partition_mode="lvm"))
test_cases.append(
TestCaseFedora(image="raw", partition_mode="btrfs"))
# do a cross arch test too
if platform.machine() == "x86_64":
# TODO: re-enable once
Expand Down

0 comments on commit 60169e7

Please sign in to comment.