diff --git a/internal/vm/qemu.go b/internal/vm/qemu.go index 5fd4584..ba8c96c 100644 --- a/internal/vm/qemu.go +++ b/internal/vm/qemu.go @@ -675,16 +675,32 @@ func normalizeArch(arch string) string { } } +// needsCIDATASeed reports whether a profile's distro needs the cloud-init seed +// delivered as a CIDATA-labelled disk/ISO rather than the NoCloud-over-SMBIOS +// network seed (the Ubuntu/Debian default). EL (RHEL/AlmaLinux/Rocky/CentOS/ +// Oracle), Amazon Linux, and SUSE cloud-init configs do not reliably honour the +// SMBIOS-net seed: on EL8 cloud-init falls back to DataSourceNone and never +// authorises the injected SSH key (observed booting AlmaLinux/Rocky 8). Those +// images do read a CIDATA disk/ConfigDrive, so use that instead. +func needsCIDATASeed(profile Profile) bool { + switch strings.ToLower(strings.TrimSpace(profile.Distro)) { + case "rhel", "almalinux", "rocky", "centos", "centos-stream", + "oracle", "oracle-linux", "amazon-linux", "amazonlinux", + "sles", "suse", "opensuse": + return true + } + // Keep the explicit ID for any profile that omits a recognised distro. + return strings.EqualFold(strings.TrimSpace(profile.ID), "rhel-8-4.18") +} + func seedDeliveryForProfile(profile Profile) seedDeliveryMode { - switch strings.ToLower(strings.TrimSpace(profile.ID)) { - case "rhel-8-4.18": + if needsCIDATASeed(profile) { if commandAvailable("cloud-localds") { return seedDeliveryNoCloudConfigDrive } return seedDeliveryNoCloudConfigFS - default: - return seedDeliveryNoCloudNet } + return seedDeliveryNoCloudNet } func commandAvailable(name string) bool { diff --git a/internal/vm/qemu_test.go b/internal/vm/qemu_test.go index af975e3..8771250 100644 --- a/internal/vm/qemu_test.go +++ b/internal/vm/qemu_test.go @@ -378,9 +378,20 @@ func TestSeedDeliveryForProfile(t *testing.T) { if got := seedDeliveryForProfile(Profile{ID: "rhel-8-4.18"}); got != seedDeliveryNoCloudConfigDrive && got != seedDeliveryNoCloudConfigFS { t.Fatalf("expected rhel-8-4.18 seed delivery %q or %q, got %q", seedDeliveryNoCloudConfigDrive, seedDeliveryNoCloudConfigFS, got) } - if got := seedDeliveryForProfile(Profile{ID: "ubuntu-22.04-5.15"}); got != seedDeliveryNoCloudNet { + if got := seedDeliveryForProfile(Profile{ID: "ubuntu-22.04-5.15", Distro: "ubuntu"}); got != seedDeliveryNoCloudNet { t.Fatalf("expected default seed delivery %q, got %q", seedDeliveryNoCloudNet, got) } + if got := seedDeliveryForProfile(Profile{ID: "debian-12-6.1", Distro: "debian"}); got != seedDeliveryNoCloudNet { + t.Fatalf("expected debian seed delivery %q, got %q", seedDeliveryNoCloudNet, got) + } + // EL-family / Amazon / SUSE must use the CIDATA disk seed (SMBIOS-net is + // ignored by their cloud-init). + for _, distro := range []string{"almalinux", "rocky", "rhel", "centos-stream", "oracle", "amazon-linux", "sles", "opensuse"} { + got := seedDeliveryForProfile(Profile{ID: distro + "-x", Distro: distro}) + if got != seedDeliveryNoCloudConfigDrive && got != seedDeliveryNoCloudConfigFS { + t.Fatalf("expected CIDATA seed for distro %q, got %q", distro, got) + } + } } func TestMapFixupArgs(t *testing.T) { diff --git a/scripts/hetzner-bootstrap-vm.sh b/scripts/hetzner-bootstrap-vm.sh index fcb1ddf..50a3597 100755 --- a/scripts/hetzner-bootstrap-vm.sh +++ b/scripts/hetzner-bootstrap-vm.sh @@ -47,7 +47,12 @@ sudo apt-get update -y sudo apt-get install -y \ build-essential ca-certificates curl git jq make pkg-config \ clang llvm libbpf-dev libelf-dev zlib1g-dev zstd \ - qemu-system-x86 qemu-utils qemu-kvm openssh-client + qemu-system-x86 qemu-utils qemu-kvm openssh-client \ + cloud-image-utils +# cloud-image-utils provides cloud-localds, used to build a CIDATA ConfigDrive +# ISO seed for EL/Amazon/SUSE guests whose cloud-init ignores the SMBIOS-net +# seed (see internal/vm seedDeliveryForProfile). Without it those guests fall +# back to an unreliable vvfat seed and fail to boot. # bpfcompat requires Go ${GO_VERSION}; Ubuntu's apt golang is too old, so install # the official toolchain to /usr/local/go and symlink it onto the default PATH