From 776df96543d783901ec141d2dbb0ffb8e0f56120 Mon Sep 17 00:00:00 2001 From: mrbobbytables Date: Sun, 13 Sep 2026 23:30:51 +0000 Subject: [PATCH 1/2] feat(flatcar): add flatcar-usr.bst importing Flatcar /usr as the OS payload base - Add elements/flatcar/flatcar-usr.bst to import Flatcar /usr as the OS payload base - Mirror flatcar-zfs.bst with single pinned remote source flatcar-container.tar.gz - Set strip-binaries to empty string to prevent stripping prebuilt binaries - Flatten Flatcar's nested usr/lib/modules/// layout - Explicitly remove Flatcar's update and provisioning stack with replacement comments - Preserve usr/lib/flatcar/bootengine.img for stage-2 kernel boot - Add contract tests in tests/unit/test_flatcar_usr.py Assisted-by: Gemini 3.8 Flash via GitHub Copilot Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Signed-off-by: mrbobbytables --- elements/flatcar/flatcar-usr.bst | 87 +++++++++++ tests/unit/test_flatcar_usr.py | 257 +++++++++++++++++++++++++++++++ 2 files changed, 344 insertions(+) create mode 100644 elements/flatcar/flatcar-usr.bst create mode 100644 tests/unit/test_flatcar_usr.py diff --git a/elements/flatcar/flatcar-usr.bst b/elements/flatcar/flatcar-usr.bst new file mode 100644 index 0000000..0014d24 --- /dev/null +++ b/elements/flatcar/flatcar-usr.bst @@ -0,0 +1,87 @@ +kind: manual +description: | + Import the Flatcar LTS /usr as the Bluefin Server OS payload base. + Extracts ./usr from upstream Flatcar container rootfs, flattens the nested + kernel module directory, and strips Flatcar's update and provisioning stack + in favor of native Bluefin equivalents (systemd-sysupdate, systemd-creds, Kured). + +(@): + - include/arch.yml + - include/flatcar.yml + +build-depends: + - base/base-stack.bst + - freedesktop-sdk.bst:components/tar.bst + - freedesktop-sdk.bst:components/gzip.bst + - freedesktop-sdk.bst:components/findutils.bst + +variables: + strip-binaries: "" + +sources: + - kind: remote + url: flatcar:stable/%{flatcar-board}/%{flatcar-version}/flatcar-container.tar.gz + ref: c533ff576e128d11ce240e01f818c358d75b9ff118b2dd9199fbf4684547a32c + +config: + install-commands: + - | + set -euo pipefail + + mkdir -p "%{install-root}" + + # Extract the complete Flatcar /usr into the element install root. + tar -xzf flatcar-container.tar.gz -C "%{install-root}" ./usr + + # Flatten Flatcar's nested usr/lib/modules/// layout. + # Upstream Flatcar nests modules under usr/lib/modules///. + # Collapse this into the single-level usr/lib/modules// directory + # expected by dracut, depmod, and kmod. + KVER="%{flatcar-kver}" + if [ -d "%{install-root}/usr/lib/modules/${KVER}/${KVER}" ]; then + mv "%{install-root}/usr/lib/modules/${KVER}/${KVER}" "%{install-root}/usr/lib/modules/${KVER}.nested" + find "%{install-root}/usr/lib/modules/${KVER}.nested" -mindepth 1 -maxdepth 1 -exec mv -t "%{install-root}/usr/lib/modules/${KVER}" {} + + rmdir "%{install-root}/usr/lib/modules/${KVER}.nested" + fi + + # Remove Flatcar's update and provisioning stack with explicit rm lines. + # Each removal names its Bluefin replacement. No wildcard sweeps are used + # so that an upstream unit rename fails loudly instead of silently regressing. + # Note: /usr/lib/flatcar/bootengine.img is deliberately preserved here; + # stage 1 initramfs loop-mounts it from /usr as the stage 2 initramfs. + + # update_engine: replaced by Bluefin's systemd-sysupdate (os-sysupdate.bst) for A/B image updates + rm "%{install-root}/usr/bin/update_engine" + + # update_engine_client: replaced by Bluefin's systemd-sysupdate CLI and systemctl + rm "%{install-root}/usr/bin/update_engine_client" + + # update_engine_stub: replaced by Bluefin's native systemd-sysupdate mechanism + rm "%{install-root}/usr/bin/update_engine_stub" + + # locksmithd: replaced by Bluefin's Kured reboot coordination (os-kured-hook.bst) via /run/reboot-required + rm "%{install-root}/usr/lib/locksmith/locksmithd" + + # locksmithd.service: replaced by Bluefin's Kured reboot coordination (os-kured-hook.bst) + rm "%{install-root}/usr/lib/systemd/system/locksmithd.service" + rm "%{install-root}/usr/lib/systemd/system/multi-user.target.wants/locksmithd.service" + + # ignition: replaced by Bluefin's systemd-creds first-boot credential provisioning (os-creds-prov.bst) + rm "%{install-root}/usr/bin/ignition" + + # ignition-delete-config.service: replaced by Bluefin's systemd-creds credential provisioning + rm "%{install-root}/usr/lib/systemd/system/ignition-delete-config.service" + rm "%{install-root}/usr/lib/systemd/system/sysinit.target.wants/ignition-delete-config.service" + + # coreos-cloudinit: replaced by Bluefin's systemd-creds first-boot credential provisioning (os-creds-prov.bst) + rm "%{install-root}/usr/bin/coreos-cloudinit" + + # flatcar-update: replaced by Bluefin's systemd-sysupdate (os-sysupdate.bst) and os-justfile + rm "%{install-root}/usr/bin/flatcar-update" + + # download_sysext: replaced by Bluefin's declarative sysext delivery via systemd-sysext + rm "%{install-root}/usr/bin/download_sysext" + + # ensure-sysext.service: replaced by Bluefin's systemd-sysext.service and sysupdate integration + rm "%{install-root}/usr/lib/systemd/system/ensure-sysext.service" + rm "%{install-root}/usr/lib/systemd/system/sysinit.target.wants/ensure-sysext.service" diff --git a/tests/unit/test_flatcar_usr.py b/tests/unit/test_flatcar_usr.py new file mode 100644 index 0000000..081bcc3 --- /dev/null +++ b/tests/unit/test_flatcar_usr.py @@ -0,0 +1,257 @@ +"""Contract tests for elements/flatcar/flatcar-usr.bst. + +Verifies that the Flatcar /usr base import: +- Uses kind: manual with prebuilt binaries unstripped (strip-binaries: ""). +- Imports flatcar-container.tar.gz pinned by sha256 via the flatcar: alias. +- Flattens Flatcar's nested usr/lib/modules/// layout into a + single-level module directory. +- Explicitly removes Flatcar's update and provisioning stack (update_engine, + locksmithd, ignition, coreos-cloudinit, flatcar-update, download_sysext, + ensure-sysext) with individual rm commands documented with their Bluefin replacements. +- Preserves usr/lib/flatcar/bootengine.img for two-stage kernel boot. +- Preserves verified OS components (systemd 257, glibc, bash, sshd, crictl, etc.). +""" + +from __future__ import annotations + +import re +import subprocess +from pathlib import Path + +import yaml + +REPO_ROOT = Path(__file__).resolve().parents[2] +FLATCAR_USR_ELEMENT = REPO_ROOT / "elements" / "flatcar" / "flatcar-usr.bst" +FLATCAR_YML = REPO_ROOT / "include" / "flatcar.yml" + +REMOVED_BINARIES = [ + "usr/bin/update_engine", + "usr/bin/update_engine_client", + "usr/bin/update_engine_stub", + "usr/lib/locksmith/locksmithd", + "usr/bin/ignition", + "usr/bin/coreos-cloudinit", + "usr/bin/flatcar-update", + "usr/bin/download_sysext", +] + +REMOVED_UNITS = [ + "usr/lib/systemd/system/locksmithd.service", + "usr/lib/systemd/system/ignition-delete-config.service", + "usr/lib/systemd/system/ensure-sysext.service", +] + +REMOVED_COMPONENTS = [ + "update_engine", + "update_engine_client", + "update_engine_stub", + "locksmithd", + "locksmithd.service", + "ignition", + "ignition-delete-config.service", + "coreos-cloudinit", + "flatcar-update", + "download_sysext", + "ensure-sysext.service", +] + + +def _load_element_data() -> dict: + content = FLATCAR_USR_ELEMENT.read_text(encoding="utf-8") + return yaml.safe_load(content) + + +def test_flatcar_usr_element_structure() -> None: + """Verify flatcar-usr.bst declares the expected BuildStream structure.""" + assert FLATCAR_USR_ELEMENT.is_file(), f"{FLATCAR_USR_ELEMENT} must exist" + data = _load_element_data() + + assert data.get("kind") == "manual", "flatcar-usr.bst must be kind: manual" + + # Must include arch.yml and flatcar.yml + includes = data.get("(@)", []) + assert "include/arch.yml" in includes + assert "include/flatcar.yml" in includes + + # Must declare build-depends for sandbox execution + build_deps = data.get("build-depends", []) + assert "base/base-stack.bst" in build_deps, "Must depend on base/base-stack.bst" + assert "freedesktop-sdk.bst:components/tar.bst" in build_deps + assert "freedesktop-sdk.bst:components/gzip.bst" in build_deps + assert "freedesktop-sdk.bst:components/findutils.bst" in build_deps + + # Prebuilt binaries must not be stripped + variables = data.get("variables", {}) + assert variables.get("strip-binaries") == "", ( + "variables.strip-binaries must be empty string to prevent stripping" + ) + + # Exactly one remote source for flatcar-container.tar.gz pinned by sha256 + sources = data.get("sources", []) + assert len(sources) == 1, "Must declare exactly one source" + src = sources[0] + assert src.get("kind") == "remote" + assert src.get("url") == "flatcar:stable/%{flatcar-board}/%{flatcar-version}/flatcar-container.tar.gz" + assert len(src.get("ref", "")) == 64, "Source ref must be a valid sha256 hash" + + +def test_flatcar_usr_explicit_removals_and_replacements() -> None: + """Verify explicit rm lines and Bluefin replacement documentation.""" + content = FLATCAR_USR_ELEMENT.read_text(encoding="utf-8") + + for component in REMOVED_COMPONENTS: + assert component in content, ( + f"flatcar-usr.bst must explicitly name {component} in its removal commands" + ) + + # Assert no wildcard sweeps in removal section + install_commands = _load_element_data().get("config", {}).get("install-commands", []) + script = "\n".join(install_commands) + for line in script.splitlines(): + clean = line.strip() + if clean.startswith("rm "): + assert "*" not in clean, ( + f"Wildcard removal forbidden to ensure rename failures: {line}" + ) + + # Assert each removed component is commented with replacement + for component in [ + "update_engine", + "locksmithd", + "ignition", + "coreos-cloudinit", + "flatcar-update", + "download_sysext", + "ensure-sysext", + ]: + pattern = rf"#\s*{component}.*?:.*?replaced by" + assert re.search(pattern, content, re.IGNORECASE), ( + f"flatcar-usr.bst must document replacement for {component}" + ) + + +def test_flatcar_usr_preserves_bootengine_img() -> None: + """Verify usr/lib/flatcar/bootengine.img is preserved and never deleted.""" + content = FLATCAR_USR_ELEMENT.read_text(encoding="utf-8") + + assert "bootengine.img" in content, ( + "flatcar-usr.bst must explicitly document preservation of bootengine.img" + ) + + install_commands = _load_element_data().get("config", {}).get("install-commands", []) + script = "\n".join(install_commands) + for line in script.splitlines(): + if "bootengine" in line: + assert not line.strip().startswith("rm"), ( + f"bootengine.img must not be removed: {line}" + ) + + +def test_flatcar_usr_module_flattening_logic() -> None: + """Verify script logic flattens nested usr/lib/modules///.""" + content = FLATCAR_USR_ELEMENT.read_text(encoding="utf-8") + assert "usr/lib/modules/${KVER}/${KVER}" in content or "usr/lib/modules/${kver}/${kver}" in content, ( + "Script must check for and flatten nested kernel module directory" + ) + + +def test_flatcar_usr_contract_execution(tmp_path: Path) -> None: + """Simulate execution of flatcar-usr.bst install-commands against a staged /usr. + + Verifies: + 1. Removed binaries are absent. + 2. Removed units are absent. + 3. bootengine.img is preserved. + 4. Module directory is flattened from nested // to single-level /. + 5. Retained files (bash, sshd, systemd, crictl) remain untouched. + """ + flatcar_yml_data = yaml.safe_load(FLATCAR_YML.read_text(encoding="utf-8")) + kver = flatcar_yml_data["variables"]["flatcar-kver"] + + install_root = tmp_path / "install-root" + usr = install_root / "usr" + + # Populate mock files that flatcar-container.tar.gz delivers + bin_dir = usr / "bin" + bin_dir.mkdir(parents=True) + for b in [ + "bash", "crictl", "sshd", "update_engine", "update_engine_client", + "update_engine_stub", "ignition", "coreos-cloudinit", "flatcar-update", + "download_sysext" + ]: + (bin_dir / b).write_text(f"mock-{b}", encoding="utf-8") + + locksmith_dir = usr / "lib" / "locksmith" + locksmith_dir.mkdir(parents=True) + (locksmith_dir / "locksmithd").write_text("mock-locksmithd", encoding="utf-8") + + systemd_dir = usr / "lib" / "systemd" / "system" + systemd_dir.mkdir(parents=True) + (systemd_dir / "locksmithd.service").write_text("mock-unit", encoding="utf-8") + (systemd_dir / "ignition-delete-config.service").write_text("mock-unit", encoding="utf-8") + (systemd_dir / "ensure-sysext.service").write_text("mock-unit", encoding="utf-8") + + wants_dir1 = systemd_dir / "multi-user.target.wants" + wants_dir1.mkdir(parents=True) + (wants_dir1 / "locksmithd.service").symlink_to("../locksmithd.service") + + wants_dir2 = systemd_dir / "sysinit.target.wants" + wants_dir2.mkdir(parents=True) + (wants_dir2 / "ignition-delete-config.service").symlink_to("../ignition-delete-config.service") + (wants_dir2 / "ensure-sysext.service").symlink_to("../ensure-sysext.service") + + flatcar_dir = usr / "lib" / "flatcar" + flatcar_dir.mkdir(parents=True) + bootengine = flatcar_dir / "bootengine.img" + bootengine.write_bytes(b"mock-bootengine-squashfs") + + # Nested module directory: usr/lib/modules/// + nested_mod_dir = usr / "lib" / "modules" / kver / kver + (nested_mod_dir / "kernel" / "drivers").mkdir(parents=True) + (nested_mod_dir / "kernel" / "drivers" / "driver.ko").write_bytes(b"mock-driver") + (nested_mod_dir / "modules.dep").write_text("mock modules.dep", encoding="utf-8") + + # Extract script from flatcar-usr.bst and adapt variables + install_commands = _load_element_data().get("config", {}).get("install-commands", []) + script = "\n".join(install_commands) + + # Replace BuildStream variables + script = script.replace("%{install-root}", str(install_root)) + script = script.replace("%{flatcar-kver}", kver) + + # In test environment, the mock files are already populated, so bypass tar extraction + script = re.sub(r"tar\s+-xzf\s+flatcar-container\.tar\.gz\s+-C\s+.*", "# tar extract bypassed", script) + + # Run the script in bash + result = subprocess.run(["bash", "-c", script], capture_output=True, text=True) + assert result.returncode == 0, f"Script failed:\nSTDOUT:\n{result.stdout}\nSTDERR:\n{result.stderr}" + + # 1. Assert removed binaries are absent + for rel_path in REMOVED_BINARIES: + target = install_root / rel_path + assert not target.exists(), f"Removed binary {rel_path} must be absent from output" + + # 2. Assert removed units are absent + for rel_path in REMOVED_UNITS: + target = install_root / rel_path + assert not target.exists(), f"Removed unit {rel_path} must be absent from output" + + # 3. Assert module tree is single-level + mod_root = usr / "lib" / "modules" / kver + assert mod_root.is_dir(), f"Module directory {mod_root} must exist" + assert not (mod_root / kver).exists(), ( + f"Nested module directory {mod_root / kver} must be flattened into single level" + ) + assert (mod_root / "kernel" / "drivers" / "driver.ko").is_file(), ( + "Modules must be moved to single-level module root" + ) + assert (mod_root / "modules.dep").is_file(), ( + "Module dependency maps must be moved to single-level module root" + ) + + # 4. Assert usr/lib/flatcar/bootengine.img exists in output + assert bootengine.is_file(), "usr/lib/flatcar/bootengine.img must exist in element output" + + # 5. Assert kept binaries remain + for kept in ["bash", "crictl", "sshd"]: + assert (bin_dir / kept).is_file(), f"Kept binary {kept} must remain in output" From a6f1f54f15e97981c941c694e0037bb8240c7314 Mon Sep 17 00:00:00 2001 From: mrbobbytables Date: Tue, 15 Sep 2026 03:03:48 +0000 Subject: [PATCH 2/2] fix(flatcar): strip /usr/lib/modules to avoid collision with flatcar-kernel Removes dead nested module flattening code and strips /usr/lib/modules from flatcar-usr.bst completely so kernel modules are exclusively managed by flatcar-kernel.bst. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- elements/flatcar/flatcar-usr.bst | 21 +++++++----------- tests/unit/test_flatcar_usr.py | 37 ++++++++++++-------------------- 2 files changed, 22 insertions(+), 36 deletions(-) diff --git a/elements/flatcar/flatcar-usr.bst b/elements/flatcar/flatcar-usr.bst index 0014d24..ca23d98 100644 --- a/elements/flatcar/flatcar-usr.bst +++ b/elements/flatcar/flatcar-usr.bst @@ -1,9 +1,10 @@ kind: manual description: | Import the Flatcar LTS /usr as the Bluefin Server OS payload base. - Extracts ./usr from upstream Flatcar container rootfs, flattens the nested - kernel module directory, and strips Flatcar's update and provisioning stack - in favor of native Bluefin equivalents (systemd-sysupdate, systemd-creds, Kured). + Extracts ./usr from upstream Flatcar container rootfs, strips Flatcar's + update and provisioning stack in favor of native Bluefin equivalents + (systemd-sysupdate, systemd-creds, Kured), and removes /usr/lib/modules + to leave kernel module management exclusively to flatcar-kernel.bst. (@): - include/arch.yml @@ -33,16 +34,10 @@ config: # Extract the complete Flatcar /usr into the element install root. tar -xzf flatcar-container.tar.gz -C "%{install-root}" ./usr - # Flatten Flatcar's nested usr/lib/modules/// layout. - # Upstream Flatcar nests modules under usr/lib/modules///. - # Collapse this into the single-level usr/lib/modules// directory - # expected by dracut, depmod, and kmod. - KVER="%{flatcar-kver}" - if [ -d "%{install-root}/usr/lib/modules/${KVER}/${KVER}" ]; then - mv "%{install-root}/usr/lib/modules/${KVER}/${KVER}" "%{install-root}/usr/lib/modules/${KVER}.nested" - find "%{install-root}/usr/lib/modules/${KVER}.nested" -mindepth 1 -maxdepth 1 -exec mv -t "%{install-root}/usr/lib/modules/${KVER}" {} + - rmdir "%{install-root}/usr/lib/modules/${KVER}.nested" - fi + # Remove kernel modules; elements/flatcar/flatcar-kernel.bst extracts and owns + # /usr/lib/modules/%{flatcar-kver} (uncompressing .ko.xz modules and running depmod) + # to prevent filesystem overlap collisions when composed together in os-stack.bst. + rm -rf "%{install-root}/usr/lib/modules" # Remove Flatcar's update and provisioning stack with explicit rm lines. # Each removal names its Bluefin replacement. No wildcard sweeps are used diff --git a/tests/unit/test_flatcar_usr.py b/tests/unit/test_flatcar_usr.py index 081bcc3..ee35b39 100644 --- a/tests/unit/test_flatcar_usr.py +++ b/tests/unit/test_flatcar_usr.py @@ -3,8 +3,7 @@ Verifies that the Flatcar /usr base import: - Uses kind: manual with prebuilt binaries unstripped (strip-binaries: ""). - Imports flatcar-container.tar.gz pinned by sha256 via the flatcar: alias. -- Flattens Flatcar's nested usr/lib/modules/// layout into a - single-level module directory. +- Removes /usr/lib/modules to avoid filesystem collisions with flatcar-kernel.bst. - Explicitly removes Flatcar's update and provisioning stack (update_engine, locksmithd, ignition, coreos-cloudinit, flatcar-update, download_sysext, ensure-sysext) with individual rm commands documented with their Bluefin replacements. @@ -147,11 +146,11 @@ def test_flatcar_usr_preserves_bootengine_img() -> None: ) -def test_flatcar_usr_module_flattening_logic() -> None: - """Verify script logic flattens nested usr/lib/modules///.""" +def test_flatcar_usr_removes_modules() -> None: + """Verify script logic removes /usr/lib/modules to avoid collision with flatcar-kernel.bst.""" content = FLATCAR_USR_ELEMENT.read_text(encoding="utf-8") - assert "usr/lib/modules/${KVER}/${KVER}" in content or "usr/lib/modules/${kver}/${kver}" in content, ( - "Script must check for and flatten nested kernel module directory" + assert 'rm -rf "%{install-root}/usr/lib/modules"' in content, ( + "Script must remove /usr/lib/modules to prevent collision with flatcar-kernel.bst" ) @@ -162,7 +161,7 @@ def test_flatcar_usr_contract_execution(tmp_path: Path) -> None: 1. Removed binaries are absent. 2. Removed units are absent. 3. bootengine.img is preserved. - 4. Module directory is flattened from nested // to single-level /. + 4. Module directory /usr/lib/modules is removed to avoid collision with flatcar-kernel.bst. 5. Retained files (bash, sshd, systemd, crictl) remain untouched. """ flatcar_yml_data = yaml.safe_load(FLATCAR_YML.read_text(encoding="utf-8")) @@ -205,11 +204,11 @@ def test_flatcar_usr_contract_execution(tmp_path: Path) -> None: bootengine = flatcar_dir / "bootengine.img" bootengine.write_bytes(b"mock-bootengine-squashfs") - # Nested module directory: usr/lib/modules/// - nested_mod_dir = usr / "lib" / "modules" / kver / kver - (nested_mod_dir / "kernel" / "drivers").mkdir(parents=True) - (nested_mod_dir / "kernel" / "drivers" / "driver.ko").write_bytes(b"mock-driver") - (nested_mod_dir / "modules.dep").write_text("mock modules.dep", encoding="utf-8") + # Module directory: usr/lib/modules// + mod_dir = usr / "lib" / "modules" / kver + (mod_dir / "kernel" / "drivers").mkdir(parents=True) + (mod_dir / "kernel" / "drivers" / "driver.ko").write_bytes(b"mock-driver") + (mod_dir / "modules.dep").write_text("mock modules.dep", encoding="utf-8") # Extract script from flatcar-usr.bst and adapt variables install_commands = _load_element_data().get("config", {}).get("install-commands", []) @@ -236,17 +235,9 @@ def test_flatcar_usr_contract_execution(tmp_path: Path) -> None: target = install_root / rel_path assert not target.exists(), f"Removed unit {rel_path} must be absent from output" - # 3. Assert module tree is single-level - mod_root = usr / "lib" / "modules" / kver - assert mod_root.is_dir(), f"Module directory {mod_root} must exist" - assert not (mod_root / kver).exists(), ( - f"Nested module directory {mod_root / kver} must be flattened into single level" - ) - assert (mod_root / "kernel" / "drivers" / "driver.ko").is_file(), ( - "Modules must be moved to single-level module root" - ) - assert (mod_root / "modules.dep").is_file(), ( - "Module dependency maps must be moved to single-level module root" + # 3. Assert module tree is completely removed to avoid collision with flatcar-kernel.bst + assert not (usr / "lib" / "modules").exists(), ( + "/usr/lib/modules must be removed to avoid collision with flatcar-kernel.bst" ) # 4. Assert usr/lib/flatcar/bootengine.img exists in output