From c7c99a47c7edca83fecdd021e61224691ac4f7cd Mon Sep 17 00:00:00 2001 From: "kubestellar-hive[bot]" Date: Fri, 21 Aug 2026 14:08:13 -0400 Subject: [PATCH] [scanner] feat: add PXE installer artifacts Refs #14 Signed-off-by: kubestellar-hive[bot] --- AGENTS.md | 1 + Justfile | 10 +++- docs/skills/ddi-installer.md | 5 ++ docs/skills/index.md | 1 + docs/skills/pxe-netboot.md | 34 +++++++++++++ elements/installer/installer-stack.bst | 3 ++ elements/oci/bluefin-server-installer.bst | 59 ++++++++++++++++++++++- 7 files changed, 111 insertions(+), 2 deletions(-) create mode 100644 docs/skills/pxe-netboot.md diff --git a/AGENTS.md b/AGENTS.md index c5593d0..2dce8e0 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -40,6 +40,7 @@ All `just` targets run BuildStream inside the FSDK `bst2` container via `just bs | Task | Skill | |---|---| | Build or debug the installer / DDI | [`docs/skills/ddi-installer.md`](docs/skills/ddi-installer.md), [`docs/skills/ddi-installer-build.md`](docs/skills/ddi-installer-build.md) | +| PXE/netboot installation | [`docs/skills/pxe-netboot.md`](docs/skills/pxe-netboot.md) | | Factory role, k3s sysext rationale, lab integration | [`docs/skills/factory-integration.md`](docs/skills/factory-integration.md) | | Work with `systemd-sysext` / `systemd-confext` | [`docs/skills/systemd-sysext-extensions.md`](docs/skills/systemd-sysext-extensions.md) | | Build or ship the k3s sysext | [`docs/skills/k3s-sysext.md`](docs/skills/k3s-sysext.md), [`docs/skills/k3s-sysext-ops.md`](docs/skills/k3s-sysext-ops.md) | diff --git a/Justfile b/Justfile index 1e91ee1..9a4ca0c 100644 --- a/Justfile +++ b/Justfile @@ -101,7 +101,7 @@ cluster-build REF="main": -n argo \ --watch -# Export the installer disk image + SHA256SUMS to dist/. +# Export the installer disk image, UKI, PXE artifacts + SHA256SUMS to dist/. # bst artifact checkout requires an empty destination, and dist/ may # already hold dist/ddi/ or dist/sysext/ from earlier export steps, so # check out into a clean staging directory and move the files over. @@ -115,6 +115,14 @@ export-installer: build-installer rm -rf dist/installer-checkout @echo "==> wrote:" && ls -lh dist/ +# Export standalone PXE kernel/initrd artifacts to dist/pxe/. +[group('installer')] +export-pxe: export-installer + rm -rf dist/pxe + mkdir -p dist/pxe + cp dist/bluefin-server-pxe-* dist/SHA256SUMS dist/pxe/ + @echo "==> wrote PXE artifacts:" && ls -lh dist/pxe/ + # -- k3s systemd-sysext ------------------------------------------------------- # Produces a systemd-sysext extension image for k3s. diff --git a/docs/skills/ddi-installer.md b/docs/skills/ddi-installer.md index 6f36b1d..17497b4 100644 --- a/docs/skills/ddi-installer.md +++ b/docs/skills/ddi-installer.md @@ -74,6 +74,11 @@ remains stateless. stack includes `xfsprogs` so the root and `/var` filesystems can grow to fill their partitions on first boot. +For PXE/netboot, use the standalone kernel and initrd artifacts with the +opt-in `inst.ddi_url` parameters documented in +[pxe-netboot.md](pxe-netboot.md). Without those parameters, installation +continues to use the embedded DDI and requires no network. + ## Partition Layout ### Installer media (the USB/raw disk image) diff --git a/docs/skills/index.md b/docs/skills/index.md index 8382e0a..3178169 100644 --- a/docs/skills/index.md +++ b/docs/skills/index.md @@ -31,6 +31,7 @@ This is the lazy-load routing table for agent skills. Keep this file in memory w | [`gap-analysis-distros.md`](gap-analysis-distros.md) | Comparing Bluefin Server to other server OSes | Source-verified comparison to Ubuntu, Talos, Flatcar, FCOS. | | [`k3s-sysext-ops.md`](k3s-sysext-ops.md) | Building the k3s sysext | BuildStream element and publish steps for the k3s sysext. | | [`k3s-sysext.md`](k3s-sysext.md) | Operating k3s on Bluefin Server | Runtime operation and reboot coordination for k3s. | +| [`pxe-netboot.md`](pxe-netboot.md) | Installing over PXE/netboot | Standalone boot artifacts, DDI URL parameters, and troubleshooting. | | [`skill-improvement.md`](skill-improvement.md) | Adding, splitting, or refactoring skills | Meta-skill that owns the documentation loop. | | [`system-containers.md`](system-containers.md) | Running `systemd-nspawn` toolboxes | System container operation with `machinectl`. | | [`systemd-sysext-extensions.md`](systemd-sysext-extensions.md) | Optional layers via `systemd-sysext` / `systemd-confext` | Extension identity, loading, and Flatcar compatibility. | diff --git a/docs/skills/pxe-netboot.md b/docs/skills/pxe-netboot.md new file mode 100644 index 0000000..bc0da73 --- /dev/null +++ b/docs/skills/pxe-netboot.md @@ -0,0 +1,34 @@ +--- +name: pxe-netboot +description: PXE/netboot installation using standalone Bluefin Server boot artifacts +--- +# PXE/netboot installation + +PXE installation is an opt-in variant of the normal offline installer. Releases +publish a kernel (`bluefin-server-pxe-vmlinuz-`) and cpio initrd +(`bluefin-server-pxe-initrd-.cpio.gz`) alongside the installer, DDI, +and signed `SHA256SUMS` manifest. + +The PXE loader supplies `inst.ddi_url=https://.../bluefin-server-ddi-.raw.zst`. +Use `inst.ddi_sha256=` to verify the download before +the target disk is touched. `inst.target_disk=/dev/...` can select a specific +disk; without it, unattended mode selects the first writable disk that is not +installer media. + +Example iPXE stanza: + +```text +#!ipxe +set base https://mirror.example/bluefin +kernel ${base}/bluefin-server-pxe-vmlinuz- systemd.unit=system-install.target console=tty0 console=ttyS0,115200 rw unattended inst.ddi_url=${base}/bluefin-server-ddi-.raw.zst inst.ddi_sha256= +initrd ${base}/bluefin-server-pxe-initrd-.cpio.gz +boot +``` + +The initrd downloads and decompresses the DDI into `/run/installer`, so allow +RAM or writable runtime storage for the expanded DDI. HTTPS requires the +installer CA bundle. DHCP is enabled on Ethernet interfaces; provide a serial +console with `console=ttyS0,115200` when diagnosing DHCP or fetch failures. + +Without `inst.ddi_url`, behavior is unchanged: the installer reads the embedded +DDI from `bluefin-installer-data` and needs no network. diff --git a/elements/installer/installer-stack.bst b/elements/installer/installer-stack.bst index de1ab3a..7322b91 100644 --- a/elements/installer/installer-stack.bst +++ b/elements/installer/installer-stack.bst @@ -27,6 +27,9 @@ depends: - freedesktop-sdk.bst:components/cryptsetup.bst - freedesktop-sdk.bst:components/xfsprogs.bst - freedesktop-sdk.bst:components/dosfstools.bst + # Optional PXE installer path (curl fetches the compressed DDI; zstd expands it). + - freedesktop-sdk.bst:components/curl.bst + - freedesktop-sdk.bst:components/zstd.bst # Kernel (installer boots from this; vmlinuz used as the UKI --linux= arg) - freedesktop-sdk.bst:components/linux.bst diff --git a/elements/oci/bluefin-server-installer.bst b/elements/oci/bluefin-server-installer.bst index d4c85e8..2a9c68c 100644 --- a/elements/oci/bluefin-server-installer.bst +++ b/elements/oci/bluefin-server-installer.bst @@ -130,8 +130,58 @@ config: # Auto-detect target disk for unattended install (any raw disk of type "disk" that is not the installer, is not read-only, and has size > 0) TARGET_DISK="" INSTALLER_PART="$(readlink -f /dev/disk/by-partlabel/bluefin-installer-data 2>/dev/null || true)" + CMDLINE="$(cat /proc/cmdline 2>/dev/null || true)" + + # PXE installs opt in with inst.ddi_url. Keep the compressed download on + # the installer filesystem, then expose the expanded image at the same + # path used by the embedded-media repart definition. + DDI_URL="" + DDI_SHA256="" + REQUESTED_TARGET="" + for arg in ${CMDLINE}; do + case "${arg}" in + inst.ddi_url=*) DDI_URL="${arg#inst.ddi_url=}" ;; + inst.ddi_sha256=*) DDI_SHA256="${arg#inst.ddi_sha256=}" ;; + inst.target_disk=*) REQUESTED_TARGET="${arg#inst.target_disk=}" ;; + esac + done + + if [ -n "${DDI_URL}" ]; then + case "${DDI_URL}" in + http://*|https://*) ;; + *) echo "ERROR: inst.ddi_url must use http:// or https://" >&2; exit 1 ;; + esac + mkdir -p /run/installer + echo "==> Fetching DDI from ${DDI_URL}" + curl --fail --show-error --location --retry 3 --output /run/installer/bluefin-server-ddi.raw.zst "${DDI_URL}" + if [ -n "${DDI_SHA256}" ]; then + printf '%s %s\n' "${DDI_SHA256}" /run/installer/bluefin-server-ddi.raw.zst \ + | sha256sum --check --status - + echo "==> DDI checksum verified" + fi + zstd --decompress --stdout /run/installer/bluefin-server-ddi.raw.zst \ + > /run/installer/bluefin-server-ddi.raw + rm -f /run/installer/bluefin-server-ddi.raw.zst + mkdir -p /dev/disk/by-partlabel + ln -sfn /run/installer/bluefin-server-ddi.raw \ + /dev/disk/by-partlabel/bluefin-installer-data + INSTALLER_PART="" + fi + + if [ -n "${REQUESTED_TARGET}" ]; then + case "${REQUESTED_TARGET}" in + /dev/*) ;; + *) echo "ERROR: inst.target_disk must be an absolute /dev path" >&2; exit 1 ;; + esac + if [ ! -b "${REQUESTED_TARGET}" ]; then + echo "ERROR: inst.target_disk is not a block device: ${REQUESTED_TARGET}" >&2 + exit 1 + fi + TARGET_DISK="${REQUESTED_TARGET}" + fi while read -r name type ro size; do + [ -z "${TARGET_DISK}" ] || break [ "${type}" = "disk" ] || continue [ "${ro}" = "0" ] || continue [ "${size}" -gt 0 ] || continue @@ -146,7 +196,6 @@ config: done < <(lsblk -p -d -n -o NAME,TYPE,RO,SIZE -b) # Check for unattended in kernel command line - CMDLINE="$(cat /proc/cmdline 2>/dev/null || true)" if [[ " ${CMDLINE} " == *" unattended "* ]]; then echo "==> Running in UNATTENDED mode..." if [ -n "${TARGET_DISK}" ]; then @@ -181,6 +230,8 @@ config: mkdir -p /layer/etc/systemd/system/systemd-sysinstall.service.d cat > /layer/etc/systemd/system/systemd-sysinstall.service.d/override.conf << 'EOF' [Unit] + Wants=network-online.target + After=network-online.target SuccessAction=poweroff FailureAction=poweroff @@ -316,7 +367,13 @@ config: # Also export the target UKI (.efi) for GitHub Releases and systemd-sysupdate cp /layer/usr/lib/bluefin-server/bluefin-server.efi "bluefin-server-%{installer-version}.efi" + # Export separate PXE boot artifacts. Their cmdline is supplied by the + # PXE loader, unlike the fixed cmdline embedded in the UKI. + cp /layer/boot/vmlinuz "bluefin-server-pxe-vmlinuz-%{installer-version}" + cp /installer.cpio.gz "bluefin-server-pxe-initrd-%{installer-version}.cpio.gz" sha256sum --binary "${FNAME}.zst" > SHA256SUMS sha256sum --binary "bluefin-server-%{installer-version}.efi" >> SHA256SUMS + sha256sum --binary "bluefin-server-pxe-vmlinuz-%{installer-version}" >> SHA256SUMS + sha256sum --binary "bluefin-server-pxe-initrd-%{installer-version}.cpio.gz" >> SHA256SUMS ls -lh