diff --git a/bastion/src/bastion/tests/fixtures/ipxe-x86_64-golden.json b/bastion/src/bastion/tests/fixtures/ipxe-x86_64-golden.json new file mode 100644 index 0000000..bc77b81 --- /dev/null +++ b/bastion/src/bastion/tests/fixtures/ipxe-x86_64-golden.json @@ -0,0 +1,8 @@ +{ + "boot": "#!ipxe\n\necho\necho ============================================\necho Lab PXE Bastion\necho Contacting server for instructions...\necho ============================================\necho\n\nchain http://10.0.0.1:8080/dispatch?mac=${net0/mac}\n", + "discover": "#!ipxe\n\necho\necho =============================================\necho Lab PXE Bastion - DISCOVERY MODE\necho MAC: aa:bb:cc:dd:ee:ff\necho Collecting hardware info...\necho =============================================\necho\n\nkernel http://10.0.0.1:8080/vmlinuz inst.ks=http://10.0.0.1:8080/discover.ks inst.stage2=https://download.fedoraproject.org/pub/fedora/linux/releases/43/Everything/x86_64/os inst.text nomodeset\ninitrd http://10.0.0.1:8080/initrd.img\nboot\n", + "install": "#!ipxe\n\necho\necho =============================================\necho Lab PXE Bastion - INSTALLING Fedora 43\necho Target: worker-1\necho MAC: aa:bb:cc:dd:ee:ff\necho =============================================\necho\n\nkernel http://10.0.0.1:8080/vmlinuz inst.ks=http://10.0.0.1:8080/ks?mac=aa:bb:cc:dd:ee:ff inst.repo=https://download.fedoraproject.org/pub/fedora/linux/releases/43/Everything/x86_64/os inst.text nomodeset\ninitrd http://10.0.0.1:8080/initrd.img\nboot\n", + "debug": "#!ipxe\n\necho\necho =============================================\necho Lab PXE Bastion - DEBUG/RESCUE MODE\necho Target: worker-1\necho MAC: aa:bb:cc:dd:ee:ff\necho =============================================\necho\n\nkernel http://10.0.0.1:8080/vmlinuz inst.rescue inst.text inst.sshd inst.ks=http://10.0.0.1:8080/debug.ks?mac=aa:bb:cc:dd:ee:ff inst.stage2=https://download.fedoraproject.org/pub/fedora/linux/releases/43/Everything/x86_64/os\ninitrd http://10.0.0.1:8080/initrd.img\nboot\n", + "pxeBoot": "#!ipxe\n\necho\necho =============================================\necho Lab PXE Bastion - PXE BOOT (debug)\necho Target: worker-1\necho MAC: aa:bb:cc:dd:ee:ff\necho Kernel+initrd from PXE, root from NVMe\necho =============================================\necho\n\nkernel http://10.0.0.1:8080/vmlinuz root=/dev/mapper/labvg-root ro rd.lvm.lv=labvg/root rd.lvm.lv=labvg/swap console=tty0\ninitrd http://10.0.0.1:8080/initrd.img\nboot\n", + "localBoot": "#!ipxe\n\necho\necho =============================================\necho Lab PXE Bastion - worker-1\necho Already installed, booting from local disk\necho =============================================\necho\nsleep 3\nexit 1\n" +} diff --git a/bastion/src/bastion/tests/ipxe-x86-regression.test.ts b/bastion/src/bastion/tests/ipxe-x86-regression.test.ts new file mode 100644 index 0000000..2329dce --- /dev/null +++ b/bastion/src/bastion/tests/ipxe-x86-regression.test.ts @@ -0,0 +1,89 @@ +// x86_64 iPXE output regression gate. +// +// The aarch64 PXE work must not change what an x86_64 machine is served. The golden +// fixture was dumped from the templates as they stood before that work started, so +// any diff here is a regression, not an improvement. +// +// The one deliberate exception is renderBootIpxe: its chain URL gained +// `&arch=${buildarch}` so the dispatch endpoint can observe the client's +// architecture at boot time. That single change is asserted explicitly below +// rather than being allowed to slip through the byte-for-byte comparison. + +import { describe, it, expect } from "vitest"; +import { readFileSync } from "node:fs"; +import { join } from "node:path"; +import { fileURLToPath } from "node:url"; +import { dirname } from "node:path"; +import { + renderBootIpxe, + renderDiscoverIpxe, + renderInstallIpxe, + renderDebugIpxe, + renderPxeBootDebugIpxe, + renderLocalBootIpxe, +} from "../src/templates/boot.ipxe.js"; + +const here = dirname(fileURLToPath(import.meta.url)); +const golden = JSON.parse( + readFileSync(join(here, "fixtures", "ipxe-x86_64-golden.json"), "utf-8"), +) as Record; + +// Exactly the parameters used to dump the fixture. +const serverIp = "10.0.0.1"; +const httpPort = 8080; +const mac = "aa:bb:cc:dd:ee:ff"; +const hostname = "worker-1"; +const fedoraVersion = "43"; +const fedoraMirror = + "https://download.fedoraproject.org/pub/fedora/linux/releases/43/Everything/x86_64/os"; + +// The x86_64 LVM layout the fixture was captured with. Before this work the values +// were hardcoded in the template; they are now supplied by the caller from machine +// state, so the fixture pins the rendering, not the defaults. +const x86Root = { + rootDevice: "/dev/mapper/labvg-root", + rootArgs: "rd.lvm.lv=labvg/root rd.lvm.lv=labvg/swap", +}; + +describe("x86_64 iPXE output is unchanged", () => { + it("discover script is byte-identical", () => { + const rendered = renderDiscoverIpxe({ + mac, serverIp, httpPort, fedoraMirror, arch: "x86_64", + }); + expect(rendered).toBe(golden["discover"]); + }); + + it("install script is byte-identical", () => { + const rendered = renderInstallIpxe({ + mac, hostname, serverIp, httpPort, fedoraVersion, fedoraMirror, arch: "x86_64", + }); + expect(rendered).toBe(golden["install"]); + }); + + it("debug/rescue script is byte-identical", () => { + const rendered = renderDebugIpxe({ + mac, hostname, serverIp, httpPort, fedoraMirror, arch: "x86_64", + }); + expect(rendered).toBe(golden["debug"]); + }); + + it("--pxe-boot script is byte-identical when state carries the Fedora LVM layout", () => { + const rendered = renderPxeBootDebugIpxe({ + mac, hostname, serverIp, httpPort, arch: "x86_64", ...x86Root, + }); + expect(rendered).toBe(golden["pxeBoot"]); + }); + + it("local boot script is byte-identical", () => { + expect(renderLocalBootIpxe(hostname)).toBe(golden["localBoot"]); + }); + + it("boot.ipxe differs only by the &arch= chain parameter", () => { + const rendered = renderBootIpxe({ serverIp, httpPort }); + // The sole intended difference. + expect(rendered).toBe(golden["boot"].replace( + "/dispatch?mac=${net0/mac}", + "/dispatch?mac=${net0/mac}&arch=${buildarch}", + )); + }); +});