Skip to content

Commit f7496e0

Browse files
committed
Add Packer configuration to build a minimal Alpine 3.23 UEFI VM for XCP-ng tests
The image is built with the vatesfr/packer-plugin-xenserver plugin targeting a local XCP-ng host. It produces a zstd-compressed XVA with: - Alpine Linux 3.23.4 (x86_64), UEFI boot (grub + GPT) - 1 vCPU, 256 MiB RAM, 512 MiB disk, no swap - xe-guest-utilities installed from the Alpine community repo - XCP-ng CI public SSH keys authorised for root - Post-processed by xva_bridge.py to set bridge=xenbr0 and zstd compression Signed-off-by: Gaëtan Lehmann <gaetan.lehmann@vates.tech>
1 parent 81137d3 commit f7496e0

5 files changed

Lines changed: 261 additions & 0 deletions

File tree

packer/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export

packer/README.md

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
# Packer: Alpine 3.23 Minimal UEFI for XCP-ng
2+
3+
Builds a minimal Alpine Linux 3.23.4 VM image (XVA, zstd-compressed) for use
4+
in xcp-ng-tests. The image is UEFI-booted, has the XCP-ng guest tools
5+
installed, and allows root SSH access using the xcp-ng CI public keys.
6+
7+
## VM Specs
8+
9+
| Property | Value |
10+
|-------------|------------------------------|
11+
| OS | Alpine Linux 3.23.4 (x86_64) |
12+
| vCPUs | 1 |
13+
| RAM | 256 MiB |
14+
| Disk | 512 MiB |
15+
| Boot | UEFI (grub + GPT) |
16+
| Network | DHCP (eth0) |
17+
| Format | XVA (zstd) |
18+
| Guest tools | xe-guest-utilities (apk) |
19+
20+
## Prerequisites
21+
22+
### On the machine running Packer
23+
24+
- [Packer](https://developer.hashicorp.com/packer) (tested with v1.x)
25+
- The `xenserver-iso` builder plugin from
26+
[xenserver/packer-plugin-xenserver](https://github.com/xenserver/packer-plugin-xenserver)
27+
- Python 3 with `libarchive-c==5.3` (for zstd post-processing):
28+
29+
```sh
30+
pip install 'libarchive-c==5.3'
31+
```
32+
33+
### On the XCP-ng host
34+
35+
- An ISO SR where Packer can upload the Alpine ISO (the plugin uploads it
36+
automatically if you use `iso_url`)
37+
- A network accessible from the build machine; the default variable value is
38+
`Pool-wide network associated with eth0` — override with `network_name` if
39+
needed
40+
41+
## Building
42+
43+
```sh
44+
cd packer/
45+
packer init alpine-3.23-uefi.pkr.hcl
46+
packer build \
47+
-var xen_host=<XCP-ng IP or hostname> \
48+
-var xen_password=<XCP-ng root password> \
49+
alpine-3.23-uefi.pkr.hcl
50+
```
51+
52+
Optional overrides:
53+
54+
```sh
55+
-var xen_username=root # default: root
56+
-var network_name="<name>" # default: Pool-wide network associated with eth0
57+
-var root_password=<password> # temporary build password; default: packer
58+
```
59+
60+
The output XVA is written to:
61+
62+
```
63+
output-alpine-3.23-uefi/alpine-3.23-uefi.xva
64+
```
65+
66+
After the build the post-processor rewrites it in-place with zstd compression
67+
and sets the bridge to `xenbr0`.
68+
69+
## Directory structure
70+
71+
```
72+
packer/
73+
├── alpine-3.23-uefi.pkr.hcl # Packer build definition (HCL)
74+
├── http_files/
75+
│ └── answers.txt # Alpine setup-alpine answerfile
76+
└── scripts/
77+
└── cleanup.sh # Post-install cleanup run inside the VM
78+
```
79+
80+
## Notes
81+
82+
- The Alpine guest tools ISO `install.sh` script does not support Alpine;
83+
`xe-guest-utilities` is installed from the Alpine community repository
84+
instead.
85+
- `ROOTSSHKEY` in `answers.txt` injects the xcp-ng CI public keys during
86+
installation, so no separate provisioner step is needed for SSH keys.
87+
- `USE_EFI=1` is passed on the kernel cmdline via `boot_command`; this makes
88+
`setup-disk` use grub and a GPT layout automatically.
89+
- Swap is disabled (`SWAP_SIZE=0`) to keep the image within 512 MiB.
90+
91+
## Troubleshooting
92+
93+
**Boot hangs / VNC never gets past the boot prompt**
94+
: Increase `boot_wait` in the JSON (default `10s`) and ensure VNC is
95+
reachable from the Packer host.
96+
97+
**SSH timeout**
98+
: The install takes up to ~3 minutes on a busy host. Increase
99+
`ssh_wait_timeout` if needed.
100+
101+
**Disk full during install**
102+
: If `setup-disk` fails due to space, the disk size can be bumped to 1024 MB
103+
by passing `-var disk_size=1024` — but note that the builder does not expose
104+
this directly; you would need to edit the JSON.
105+
106+
**xva_bridge.py fails**
107+
: Ensure `libarchive-c==5.3` is installed. Other versions may not work due to
108+
internal API usage.

packer/alpine-3.23-uefi.pkr.hcl

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
packer {
2+
required_plugins {
3+
xenserver = {
4+
version = ">= v0.11.0"
5+
source = "github.com/vatesfr/xenserver"
6+
}
7+
}
8+
}
9+
10+
variable "xen_host" {
11+
type = string
12+
description = "XCP-ng pool master hostname or IP"
13+
}
14+
15+
variable "xen_username" {
16+
type = string
17+
description = "XCP-ng SSH username"
18+
default = "root"
19+
}
20+
21+
variable "xen_password" {
22+
type = string
23+
description = "XCP-ng SSH password"
24+
sensitive = true
25+
}
26+
27+
variable "network_name" {
28+
type = string
29+
description = "Name of the XCP-ng network to attach the VM to"
30+
default = "Pool-wide network associated with eth0"
31+
}
32+
33+
variable "sr_name" {
34+
type = string
35+
description = "Name of the SR to store the VM disk on (defaults to pool default)"
36+
default = ""
37+
}
38+
39+
variable "sr_iso_name" {
40+
type = string
41+
description = "Name of the ISO SR to upload the Alpine ISO to (defaults to pool default)"
42+
default = ""
43+
}
44+
45+
variable "root_password" {
46+
type = string
47+
description = "Temporary root password set during the build"
48+
default = "vateslab"
49+
sensitive = true
50+
}
51+
52+
source "xenserver-iso" "alpine" {
53+
remote_host = var.xen_host
54+
remote_username = var.xen_username
55+
remote_password = var.xen_password
56+
57+
vm_name = "alpine-3.23-uefi"
58+
vm_description = "Alpine Linux 3.23.4 minimal UEFI - built with Packer"
59+
vcpus_max = 1
60+
vcpus_atstartup = 1
61+
vm_memory = 256
62+
disk_size = 512
63+
64+
firmware = "uefi"
65+
clone_template = "Other install media"
66+
67+
network_names = [var.network_name]
68+
69+
sr_name = var.sr_name
70+
sr_iso_name = var.sr_iso_name
71+
72+
iso_url = "https://dl-cdn.alpinelinux.org/alpine/v3.23/releases/x86_64/alpine-standard-3.23.4-x86_64.iso"
73+
iso_checksum = "sha256:cfef39c7954f7c4447bcb321b9f4a1cef834536a321309d2c31275d9f2475a4e"
74+
75+
http_directory = "http_files"
76+
boot_wait = "30s"
77+
boot_command = [
78+
"root<enter>",
79+
"<wait3>",
80+
"ifconfig eth0 up && udhcpc -i eth0<enter><wait5>",
81+
# "USE<leftShiftOn>-<leftShiftOff>EFI=1 SWAP<leftShiftOn>-<leftShiftOff>SIZE=0 ERASE<leftShiftOn>-<leftShiftOff>DISKS=/dev/xvda setup-alpine -f http://{{.HTTPIP}}:{{.HTTPPort}}/answers.txt<enter>",
82+
"USE<leftShiftOn>-<leftShiftOff>EFI=1 SWAP<leftShiftOn>-<leftShiftOff>SIZE=0 ERASE<leftShiftOn>-<leftShiftOff>DISKS=/dev/xvda setup-alpine -f https://raw.githubusercontent.com/xcp-ng/xcp-ng-tests/refs/heads/gln/packer-alpine-uefi-xcpng-loun/packer/http_files/answers.txt<enter>",
83+
"<wait10>",
84+
"${var.root_password}<enter>",
85+
"<wait>",
86+
"${var.root_password}<enter>",
87+
"<wait60>",
88+
"apk add --no-cache xe-guest-utilities<enter>",
89+
"rc-update add xe-guest-utilities default<enter>",
90+
# "rc-service xe-guest-utilities start<enter>",
91+
# Remove log files
92+
"find /var/log -type f -exec truncate -s 0 {} \\;<enter>",
93+
# Remove temporary files
94+
"rm -rf /tmp/* /var/tmp/*<enter>",
95+
# Clear machine-id so each clone gets a unique one
96+
"echo > /etc/machine-id<enter>",
97+
# Remove history
98+
"rm -f /root/.ash_history<enter>",
99+
"<wait3>",
100+
]
101+
102+
communicator = "none"
103+
ssh_username = "root"
104+
ssh_password = var.root_password
105+
ssh_wait_timeout = "10m"
106+
107+
install_timeout = "30m"
108+
output_directory = "export"
109+
keep_vm = "never"
110+
format = "xva_zstd"
111+
}
112+
113+
build {
114+
sources = ["xenserver-iso.alpine"]
115+
}

packer/http_files/answers.txt

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
KEYMAPOPTS=none
2+
HOSTNAMEOPTS=alpine
3+
DEVDOPTS=mdev
4+
INTERFACESOPTS="auto lo
5+
iface lo inet loopback
6+
auto eth0
7+
iface eth0 inet dhcp
8+
hostname alpine
9+
"
10+
TIMEZONEOPTS=UTC
11+
PROXYOPTS=none
12+
APKREPOSOPTS="-1 -c"
13+
USEROPTS=none
14+
SSHDOPTS=openssh
15+
ROOTSSHKEY="ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMnN/wVdQqHA8KsndfrLS7fktH/IEgxoa533efuXR6rw XCP-ng CI
16+
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDKz9uQOoxq6Q0SQ0XTzQHhDolvuo/7EyrDZsYQbRELhcPJG8MT/o5u3HyJFhIP2+HqBSXXgmqRPJUkwz9wUwb2sUwf44qZm/pyPUWOoxyVtrDXzokU/uiaNKUMhbnfaXMz6Ogovtjua63qld2+ZRXnIgrVtYKtYBeu/qKGVSnf4FTOUKl1w3uKkr59IUwwAO8ay3wVnxXIHI/iJgq6JBgQNHbn3C/SpYU++nqL9G7dMyqGD36QPFuqH/cayL8TjNZ67TgAzsPX8OvmRSqjrv3KFbeSlpS/R4enHkSemhgfc8Z2f49tE7qxWZ6x4Uyp5E6ur37FsRf/tEtKIUJGMRXN XCP-ng CI"
17+
NTPOPTS=none
18+
DISKOPTS="-m sys /dev/xvda"
19+
LBUOPTS=none
20+
APKCACHEOPTS=none

packer/scripts/cleanup.sh

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#!/bin/sh
2+
set -e
3+
4+
# Remove apk cache
5+
rm -rf /var/cache/apk/*
6+
7+
# Remove log files
8+
find /var/log -type f -exec truncate -s 0 {} \;
9+
10+
# Remove temporary files
11+
rm -rf /tmp/* /var/tmp/*
12+
13+
# Clear machine-id so each clone gets a unique one
14+
printf '' > /etc/machine-id 2>/dev/null || true
15+
16+
# Remove history
17+
rm -f /root/.ash_history /root/.bash_history

0 commit comments

Comments
 (0)