diff --git a/Earthfile b/Earthfile index 6b0f6fd8..65ee2a88 100644 --- a/Earthfile +++ b/Earthfile @@ -31,9 +31,12 @@ ARG RKE2_FLAVOR_TAG=rke2r1 ARG BASE_IMAGE_URL=quay.io/kairos ARG OSBUILDER_VERSION=v0.400.3 ARG OSBUILDER_IMAGE=quay.io/kairos/osbuilder-tools:$OSBUILDER_VERSION -# v0.18.0 is the minimum usable version. v0.16.0 and v0.17.0 do not work for the Hadron - -ARG AURORABOOT_VERSION=v0.21.2 +# v0.18.0 is the minimum usable version. v0.16.0 and v0.17.0 do not work for the Hadron. +# v0.26.2 also fixes the UEFI-only ISO boot path: xorriso appended_part_as=gpt for a +# hybrid MBR+GPT layout (needed by VMware ESXi, OVMF/QEMU, some SuperMicro BMCs), +# and the CD-variant signed GRUB binary (gcdx64.efi.signed) via kairos-sdk v0.25.2. +# Kairos upstream: kairos-io/AuroraBoot#713, kairos-io/kairos-sdk#0.25.2. +ARG AURORABOOT_VERSION=v0.26.2 ARG AURORABOOT_IMAGE=quay.io/kairos/auroraboot:$AURORABOOT_VERSION ARG K3S_PROVIDER_VERSION=v4.10.0 ARG KUBEADM_PROVIDER_VERSION=v4.10.0 @@ -385,7 +388,11 @@ install-k8s: SAVE ARTIFACT --keep-ts /output/ . build-uki-iso: - FROM --platform=linux/${ARCH} $OSBUILDER_IMAGE + # Switched from quay.io/kairos/osbuilder-tools (archived kairos-io/osbuilder + # + kairos-io/enki) to AuroraBoot, which is the maintained successor. The + # build-iso and build-uki subcommands accept a "dir:" source, so the rootfs + # preparation path above is unchanged; only the final CLI invocation differs. + FROM --platform=linux/${ARCH} $AURORABOOT_IMAGE ENV ISO_NAME=${ISO_NAME} COPY overlay/files-iso/ /overlay/ COPY --if-exists +validate-user-data/user-data /overlay/config.yaml @@ -416,22 +423,48 @@ build-uki-iso: WORKDIR /build COPY --platform=linux/${ARCH} --keep-own +iso-image-rootfs/rootfs /build/image + # AuroraBoot v0.26.1 silently ignores --output/-d for "dir:" sources on + # both build-iso and build-uki, dropping the ISO at /tmp/auroraboot/*.iso + # regardless. We hoist it into /iso/ ourselves after the run. IF [ "$ARCH" = "arm64" ] - RUN CMD="/entrypoint.sh --name $ISO_NAME build-iso --date=false --overlay-iso /overlay dir:/build/image --output /iso/ --arch $ARCH" && \ - if [ "$DEBUG" = "true" ]; then CMD="$CMD --debug"; else CMD="$CMD"; fi && \ - $CMD + # arm64 UKI ISO is not supported by upstream today; fall through to a + # plain live/installer ISO, matching the previous osbuilder behavior. + RUN CMD="auroraboot" && \ + if [ "$DEBUG" = "true" ]; then CMD="$CMD --debug"; fi && \ + $CMD build-iso dir:/build/image --overlay-iso /overlay --arch arm64 ELSE IF [ "$ARCH" = "amd64" ] COPY secure-boot/enrollment/ secure-boot/private-keys/ secure-boot/public-keys/ /keys RUN ls -liah /keys - RUN mkdir /iso + # AuroraBoot's build-uki takes explicit key paths instead of osbuilder's + # bundled -k /keys. All key files live at /keys/* because the three + # secure-boot/* dirs above are flattened into the same target. IF [ "$AUTO_ENROLL_SECUREBOOT_KEYS" = "true" ] - RUN enki --config-dir /config build-uki dir:/build/image --extend-cmdline "$CMDLINE" --overlay-iso /overlay --secure-boot-enroll force -t iso -d /iso -k /keys --boot-branding "$BRANDING" + RUN CMD="auroraboot" && \ + if [ "$DEBUG" = "true" ]; then CMD="$CMD --debug"; fi && \ + $CMD build-uki dir:/build/image -t iso \ + --extend-cmdline "$CMDLINE" \ + --overlay-iso /overlay \ + --boot-branding "$BRANDING" \ + --public-keys /keys \ + --sb-key /keys/db.key \ + --sb-cert /keys/db.pem \ + --tpm-pcr-private-key /keys/tpm2-pcr-private.pem \ + --secure-boot-enroll force ELSE - RUN enki --config-dir /config build-uki dir:/build/image --extend-cmdline "$CMDLINE" --overlay-iso /overlay -t iso -d /iso -k /keys --boot-branding "$BRANDING" + RUN CMD="auroraboot" && \ + if [ "$DEBUG" = "true" ]; then CMD="$CMD --debug"; fi && \ + $CMD build-uki dir:/build/image -t iso \ + --extend-cmdline "$CMDLINE" \ + --overlay-iso /overlay \ + --boot-branding "$BRANDING" \ + --public-keys /keys \ + --sb-key /keys/db.key \ + --sb-cert /keys/db.pem \ + --tpm-pcr-private-key /keys/tpm2-pcr-private.pem END END - WORKDIR /iso - RUN mv /iso/*.iso $ISO_NAME.iso + RUN mkdir -p /iso && \ + mv /tmp/auroraboot/*.iso "/iso/$ISO_NAME.iso" SAVE ARTIFACT /iso/* iso: @@ -457,7 +490,12 @@ validate-user-data: build-iso: - FROM --platform=linux/${ARCH} $OSBUILDER_IMAGE + # Switched from quay.io/kairos/osbuilder-tools (archived kairos-io/osbuilder + # + kairos-io/enki) to AuroraBoot, which is the maintained successor. The + # build-iso subcommand accepts a "dir:" source with the same semantics as + # osbuilder's /entrypoint.sh build-iso, so the rootfs preparation path + # above is unchanged; only the final CLI invocation differs. + FROM --platform=linux/${ARCH} $AURORABOOT_IMAGE ENV ISO_NAME=${ISO_NAME} COPY overlay/files-iso/ /overlay/ COPY --if-exists +validate-user-data/user-data /overlay/files-iso/config.yaml @@ -501,39 +539,48 @@ build-iso: rm -f /build/image/opt/spectrocloud/local-ui.tar; \ fi - # Hadron uses AuroraBoot instead of osbuilder's enki: enki writes the grub - # stage as EFI/BOOT/grub.efi, but Hadron's shim chainloads grubx64.efi, so an - # enki-built Hadron ISO does not boot on any UEFI firmware. AuroraBoot names - # the file after its source, giving grubx64.efi. The UKI ISO - # boots systemd-boot directly and has no shim->grub chain. - IF [ "$OS_DISTRIBUTION" = "hadron" ] - WITH DOCKER --pull $AURORABOOT_IMAGE - RUN mkdir -p /iso && \ - LOGLEVEL=info && \ - if [ "$DEBUG" = "true" ]; then LOGLEVEL=debug; fi && \ - docker run --rm --privileged \ - -v /build/image:/rootfs \ - -v /overlay:/overlay \ - -v /iso:/aurora \ - $AURORABOOT_IMAGE \ - build-iso \ - --loglevel "$LOGLEVEL" \ - --override-name "$ISO_NAME" \ - --overlay-iso /overlay \ - --output /aurora \ - dir:/rootfs - END - ELSE IF [ "$ARCH" = "arm64" ] - RUN CMD="/entrypoint.sh --name $ISO_NAME build-iso --date=false --overlay-iso /overlay dir:/build/image --output /iso/ --arch $ARCH" && \ - if [ "$DEBUG" = "true" ]; then CMD="$CMD --debug"; else CMD="$CMD"; fi && \ - $CMD + # AuroraBoot uses Go arch names for both amd64 and arm64 (osbuilder used + # "x86_64" for amd64). --output/--override-name are inert for "dir:" + # sources: the ISO always lands at /tmp/auroraboot/kairos--- + # core--generic-v.iso, so we leave --output default and + # hoist the produced ISO into /iso/ ourselves. + # + # The Hadron-specific WITH DOCKER path is unnecessary now that all builds + # are FROM $AURORABOOT_IMAGE -- AuroraBoot names the grub stage + # grubx64.efi (was: EFI/BOOT/grub.efi under enki), and AuroraBoot v0.26.2 + # fixes the UEFI-only boot path via GPT-hybrid + gcdx64.efi.signed for all + # distros, not just Hadron. + IF [ "$ARCH" = "arm64" ] + RUN CMD="auroraboot" && \ + if [ "$DEBUG" = "true" ]; then CMD="$CMD --debug"; fi && \ + $CMD build-iso dir:/build/image --overlay-iso /overlay --arch arm64 ELSE IF [ "$ARCH" = "amd64" ] - RUN CMD="/entrypoint.sh --name $ISO_NAME build-iso --date=false --overlay-iso /overlay dir:/build/image --output /iso/ --arch x86_64" && \ - if [ "$DEBUG" = "true" ]; then CMD="$CMD --debug"; else CMD="$CMD"; fi && \ - $CMD + RUN CMD="auroraboot" && \ + if [ "$DEBUG" = "true" ]; then CMD="$CMD --debug"; fi && \ + $CMD build-iso dir:/build/image --overlay-iso /overlay --arch amd64 END + RUN mkdir -p /iso && \ + mv /tmp/auroraboot/*.iso "/iso/$ISO_NAME.iso" + + # AuroraBoot v0.26.2's `build-iso` subcommand runs only: + # PrepDirs -> StepCopyCloudConfig -> StepDumpSource -> StepGenISO + # and NEVER calls StepInjectCC. That step is the one that actually copies + # --overlay-iso content onto the finalised ISO tree; its absence means our + # /overlay/... files (Palette-branded /boot/grub2/grub.cfg, user-data, + # cluster config, content bundles, edge_custom_config) silently disappear. + # Empirically verified: the built ISO's /boot/grub2/grub.cfg is + # AuroraBoot's default "Kairos"-branded template, not our overlay's + # "Palette eXtended Kubernetes Edge Installer" version. + # + # Pipeline mode (docker run auroraboot --set ...) invokes StepInjectCC, + # but that adds DinD, container_image loading, and ~100 lines of Earthfile. + # StepInjectCC's actual work is one xorriso command; do it here directly. + # See kairos-io/AuroraBoot pkg/ops/iso.go InjectISO() for the upstream + # equivalent -- same xorriso invocation. + RUN xorriso -indev "/iso/$ISO_NAME.iso" -outdev "/iso/$ISO_NAME.iso" \ + -map /overlay / -boot_image any replay WORKDIR /iso - RUN sha256sum $ISO_NAME.iso > $ISO_NAME.iso.sha256 + RUN sha256sum "$ISO_NAME.iso" > "$ISO_NAME.iso.sha256" SAVE ARTIFACT --keep-ts /iso/* ### UKI targets diff --git a/overlay/files-iso/boot/grub2/grub.cfg b/overlay/files-iso/boot/grub2/grub.cfg index 1aaa44fc..62b5af0f 100644 --- a/overlay/files-iso/boot/grub2/grub.cfg +++ b/overlay/files-iso/boot/grub2/grub.cfg @@ -1,33 +1,56 @@ -search --file --set=root /boot/kernel.xz +search --no-floppy --file --set=root /boot/kernel set default={{DEFAULT_ENTRY}} set timeout=5 set timeout_style=menu set linux=linux set initrd=initrd + +# Video/console parameters are firmware-specific. +# +# BIOS/CSM: vga=795 requests VESA mode 0x31B via INT 10h from the legacy video +# BIOS, giving the kernel a framebuffer console. Unchanged. +# +# UEFI: there is no VBE, so vga= is inert. Combined with nomodeset (which +# blocks the native KMS driver) the kernel ends up with no usable +# framebuffer and the screen stays black. The last console= also wins +# for /dev/console, so console=tty0 must come last or all output goes +# to serial. The baud is pinned because an unqualified console=ttyS0 +# defaults to 9600 while BMC SOL typically runs at 115200. if [ "${grub_platform}" = "efi" ]; then - echo "Please press 't' to show the boot menu on this console" + set consoleparams="console=ttyS0,115200n8 console=tty0" + set videoparams="" +else + set consoleparams="console=tty1 console=ttyS0" + set videoparams="vga=795 nomodeset" fi -set font=($root)/boot/${grub_cpu}/loader/grub2/fonts/unicode.pf2 -if [ -f ${font} ];then - loadfont ${font} + +# loadfont switches GRUB to gfxterm. Some UEFI implementations (observed on +# Supermicro with ATEN BMC) render nothing in that mode, leaving a blank menu, +# so keep the graphical terminal on the BIOS path only. +if [ "${grub_platform}" != "efi" ]; then + set font=($root)/boot/${grub_cpu}/loader/grub2/fonts/unicode.pf2 + if [ -f ${font} ];then + loadfont ${font} + fi fi + menuentry "Palette eXtended Kubernetes Edge Installer" --class os --unrestricted { echo Loading kernel... - $linux ($root)/boot/kernel cdroot root=live:CDLABEL=COS_LIVE rd.live.dir=/ rd.live.squashimg=rootfs.squashfs net.ifnames=1 console=tty1 console=ttyS0 rd.cos.disable vga=795 nomodeset nodepair.enable selinux=0 rd.live.overlay.overlayfs rd.immucore.sysrootwait=600 systemd.unified_cgroup_hierarchy=1 rd.driver.blacklist=nouveau,qat_4xxx modprobe.blacklist=nouveau,qat_4xxx nouveau.modeset=0 + $linux ($root)/boot/kernel cdroot root=live:CDLABEL=COS_LIVE rd.live.dir=/ rd.live.squashimg=rootfs.squashfs net.ifnames=1 $consoleparams rd.cos.disable $videoparams nodepair.enable selinux=0 rd.live.overlay.overlayfs rd.immucore.sysrootwait=600 systemd.unified_cgroup_hierarchy=1 rd.driver.blacklist=nouveau,qat_4xxx modprobe.blacklist=nouveau,qat_4xxx nouveau.modeset=0 echo Loading initrd... $initrd ($root)/boot/initrd } menuentry "Palette eXtended Kubernetes Edge Installer (manual)" --class os --unrestricted { echo Loading kernel... - $linux ($root)/boot/kernel cdroot root=live:CDLABEL=COS_LIVE rd.live.dir=/ rd.live.squashimg=rootfs.squashfs net.ifnames=1 console=tty1 console=ttyS0 rd.cos.disable selinux=0 rd.live.overlay.overlayfs rd.immucore.sysrootwait=600 systemd.unified_cgroup_hierarchy=1 rd.driver.blacklist=nouveau,qat_4xxx modprobe.blacklist=nouveau,qat_4xxx nouveau.modeset=0 + $linux ($root)/boot/kernel cdroot root=live:CDLABEL=COS_LIVE rd.live.dir=/ rd.live.squashimg=rootfs.squashfs net.ifnames=1 $consoleparams rd.cos.disable selinux=0 rd.live.overlay.overlayfs rd.immucore.sysrootwait=600 systemd.unified_cgroup_hierarchy=1 rd.driver.blacklist=nouveau,qat_4xxx modprobe.blacklist=nouveau,qat_4xxx nouveau.modeset=0 echo Loading initrd... $initrd ($root)/boot/initrd } menuentry "Palette Edge Interactive Installer" --class os --unrestricted { echo Loading kernel... - $linux ($root)/boot/kernel cdroot root=live:CDLABEL=COS_LIVE rd.live.dir=/ rd.live.squashimg=rootfs.squashfs net.ifnames=1 console=tty1 console=ttyS0 rd.cos.disable vga=795 nomodeset nodepair.enable selinux=0 rd.live.overlay.overlayfs rd.immucore.sysrootwait=600 systemd.unified_cgroup_hierarchy=1 rd.driver.blacklist=nouveau,qat_4xxx modprobe.blacklist=nouveau,qat_4xxx nouveau.modeset=0 interactive-install + $linux ($root)/boot/kernel cdroot root=live:CDLABEL=COS_LIVE rd.live.dir=/ rd.live.squashimg=rootfs.squashfs net.ifnames=1 $consoleparams rd.cos.disable $videoparams nodepair.enable selinux=0 rd.live.overlay.overlayfs rd.immucore.sysrootwait=600 systemd.unified_cgroup_hierarchy=1 rd.driver.blacklist=nouveau,qat_4xxx modprobe.blacklist=nouveau,qat_4xxx nouveau.modeset=0 interactive-install echo Loading initrd... $initrd ($root)/boot/initrd }