Commit b60a2bd
feat: add armhf (32-bit ARM) architecture support [ISD-5856] (#233)
* feat: add armhf (32-bit ARM) architecture support
Add "armhf"/"arm" as a buildable image architecture. armhf images are built
on arm64 (aarch64) hosts running a 32-bit userspace and use the GitHub runner
binary published by the canonical/github-actions-runner fork
(actions-runner-linux-arm-<version>.tar.gz).
Changes:
- charm state.py: add ARM to the Arch enum and map the "armhf"/"arm"/"armv7l"
config values to it in Arch.from_charm.
- app config.py: add ARM to the Arch enum (drives the runner download arch
"arm") with to_openstack() -> "armhf"; add ARM_ADDITIONAL_APT_PACKAGES
(libicu74, libatomic1) required at runtime by the self-contained .NET runtime
bundled in the linux-arm runner tarball.
- app cloud_image.py: map Arch.ARM to the "armhf" Ubuntu cloud-image arch.
- app openstack_builder.py: install the armhf runtime apt packages when
building for Arch.ARM.
- charmcraft.yaml: document the "armhf" architecture option.
Unit tests added for every new arch mapping (TDD).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Apply suggestions from code review
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* feat: pre-install rustup and docker-buildx on armhf images
Add rustup and docker-buildx to the armhf-only additional apt packages and
set the default Rust toolchain for the ubuntu user during image build.
- config.py: ARM_ADDITIONAL_APT_PACKAGES gains rustup and docker-buildx
(docker.io is already in the default package set).
- cloud-init.sh.j2: after configuring system users, run
`sudo -u ubuntu rustup default stable`, guarded on the arm arch so it is a
no-op on other architectures (rustup is only installed on armhf images).
Test updated: the armhf cloud-init assertion now expects the extra packages
and the rustup toolchain step.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* feat: restrict armhf builds to noble+ base images
The armhf additional apt packages (libicu74, rustup, docker-buildx) are only
available from the noble (24.04) archive onwards. On focal/jammy these package
names do not exist, so an apt-get install would fail midway through the image
build with a confusing error.
Fail fast in _generate_cloud_init_script: when arch == ARM and the requested
base image is older than noble, raise UnsupportedArchitectureError with a clear
message instead of producing a cloud-init script that is guaranteed to fail.
Tests: the cloud-init render test now exercises armhf on a supported (noble)
base, and a new test asserts UnsupportedArchitectureError is raised for armhf
on focal/jammy.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* docs: document UnsupportedArchitectureError in _generate_cloud_init_script
Add the missing Raises: section for the fail-fast guard that rejects
armhf images on pre-noble base images, addressing review feedback.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* refactor(tests): move TESTDATA_TEST_SCRIPT_URL to commands.py
Breaks the commands.py <-> helpers.py circular import so command
constants can be imported without the heavy OpenStack/fabric deps,
enabling unit tests for arch-conditional command selection.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test: add arch-conditional armhf runner assertions
Add ARM_RUNNER_COMMANDS (rustc/cargo, docker buildx, 32-bit
Runner.Listener) and a pure commands_for_arch() selector, with unit
tests.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test: run arch-specific commands in openstack integration test
Thread arch through run_openstack_tests so armhf images additionally
assert rustup, docker-buildx, and the 32-bit runner binary.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test: map --arch armhf to Arch.ARM in integration conftest
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* ci: add armhf integration-test leg on noble+resolute
Reuses the arm64 OpenStack tenant (creds/network/flavor) with
--arch armhf. armhf is restricted to noble and resolute to match
ARM_SUPPORTED_BASE_IMAGES.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test: allow --arch armhf in integration pytest option
The pytest --arch option restricted choices to amd64/arm64/s390x/ppc64le,
so pytest rejected 'armhf' before arch_fixture could map it. Add armhf to
the allowed choices.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix: use armv7l as OpenStack architecture for armhf
Nova validates the glance image 'architecture' property against its
architecture enum, which uses 'armv7l' for 32-bit ARM; 'armhf' (the
Ubuntu userland ABI name) is rejected with 'Architecture name armhf is
not valid'. The cloud-image download filename still uses 'armhf' via
cloud_image.py, which is unaffected.
Surfaced by the new armhf integration test when booting an OpenStack
server from the built image.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix(armhf): set virtio image properties so 32-bit ARM images boot
The armhf base image is tagged architecture=armv7l and booted on aarch64
compute hosts, which use the QEMU "virt" machine type. That machine type
has no IDE bus, so libvirt defaulted to an IDE controller for the root disk
and the config-drive CD-ROM and rejected the domain with "IDE controllers
are unsupported for this QEMU binary or machine type", leaving the builder
VM in ERROR.
Set hw_machine_type=virt and virtio/scsi disk/cdrom buses on ARM image
uploads so the guest boots. amd64 keeps the default PC machine type.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix(armhf): put config drive on virtio-blk bus
The armhf builder VMs booted on the aarch64 virt machine type but never
became SSH-reachable. The root disk used virtio-blk (so the VM booted), but
the config drive was attached as a virtio-scsi CD-ROM. The 32-bit armhf guest
kernel lacks the virtio-scsi driver, so it could not read the config drive,
cloud-init received no network metadata, and networking never came up.
Attach the config drive on the same virtio-blk bus as the root disk and drop
the now-unneeded virtio-scsi model.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix(armhf): revert config drive to virtio-scsi CD-ROM bus
virtio-blk cannot back ejectable CD-ROM media (libvirt rejects the domain
with "disk type of 'vdb' does not support ejectable media"), so the
config-drive CD-ROM must ride a virtio-scsi controller. This restores the
bus layout that at least boots the guest so its serial console can be
inspected to root-cause the SSH-unreachable behaviour on the aarch64 hosts.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* docs(armhf): add design for arm64 boot image + 32-bit runner payload
Native armhf VMs (32-bit kernel) do not boot on the ps7 arm64 hosts (empty
console). The proven-working model from runner PRs #155/#169/#171 is an arm64
kernel running 32-bit armhf userland via native AArch32. This spec redefines
the image-builder armhf path to build an arm64 boot image carrying the 32-bit
linux-arm runner agent plus armhf multiarch userland.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix(armhf): map Arch.ARM glance architecture to aarch64
The armhf runner image is an arm64 boot image carrying a 32-bit runner
payload; native armhf (32-bit kernel) images do not boot on the aarch64
hosts. Schedule/boot the base image and snapshot as aarch64.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix(armhf): download arm64 base cloud image for Arch.ARM
The builder VM must boot a 64-bit kernel on the aarch64 hosts. Download the
arm64 base cloud image; the 32-bit runner payload is added via multiarch.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix(armhf): drop obsolete virtio glance properties
With the armhf runner image now built from an arm64 base (64-bit kernel), it
boots with the cloud's normal defaults. The virtio-scsi/machine-type
workaround for booting a native armhf (32-bit kernel) image is no longer
needed.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix(armhf): install armhf-qualified runtime deps for the 32-bit runner
The 32-bit linux-arm runner agent needs the armhf loader (libc6:armhf) and
armhf builds of its .NET runtime deps (libicu74:armhf, libatomic1:armhf) to
exec on the arm64 image via native AArch32.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* feat(armhf): enable armhf multiarch in cloud-init
Add dpkg --add-architecture armhf (guarded to the arm runner) before apt
install so the arm64 image can install and run the 32-bit linux-arm runner
and its armhf runtime deps via native AArch32. armhf packages are served by
ports.ubuntu.com, which the arm64 base already uses.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* docs(armhf): add arm64 multiarch image implementation plan
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* chore: stop tracking superpowers planning artifacts
The docs/superpowers plan and spec are internal agent planning artifacts,
not product documentation. Untrack them and gitignore the directory so the
Vale docs linter does not scan them.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix(armhf): fetch armhf packages from ports.ubuntu.com
The arm64 cloud image's apt mirrors (clouds.archive/security.ubuntu.com)
only carry the native architecture; armhf is a ports architecture and its
package indices 404 there. Pin the stock sources to the native arch and add
a ports.ubuntu.com source for armhf so the 32-bit runner's runtime deps
(libc6:armhf, libicu74:armhf, libatomic1:armhf) resolve.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix(armhf): resolve armhf apt package conflicts
Two armhf package-selection bugs surfaced once the builder VM boots and runs
apt:
- rustup conflicts with the distro cargo/rustc packages ('held broken
packages'). rustup provides the armhf/armv7 Rust toolchain, so drop cargo
and rustc from the default apt set on armhf.
- libicu's soname is release-specific (noble ships libicu74, resolute ships
libicu78); the hardcoded libicu74:armhf is 'Unable to locate' on resolute.
Select the armhf libicu matching the base image.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix(armhf): preserve pinned apt sources on runner VM
The runner VM boots from a snapshot whose cloud-init state was cleared
via "cloud-init clean --configs all" before snapshotting. On first boot
cloud-init regenerates the stock apt sources, dropping the native-arch
pin added by enable_armhf_multiarch, while the armhf dpkg architecture
persists. apt-get update then requests binary-armhf indexes from the
primary/security mirrors (which only serve arm64) and 404s, failing the
runner's apt-get update.
Bake an /etc/cloud/cloud.cfg.d drop-in with apt.preserve_sources_list:
true so cloud-init keeps the pinned sources baked into the image. The
drop-in lives outside /var/lib/cloud so it survives cloud-init clean.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* chore: bump copyright year to 2026 in test_integration_commands
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>1 parent 82fe06b commit b60a2bd
19 files changed
Lines changed: 320 additions & 28 deletions
File tree
- .github/workflows
- app
- src/github_runner_image_builder
- templates
- tests
- integration
- unit
- src
- tests/unit
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
15 | 15 | | |
16 | 16 | | |
17 | 17 | | |
18 | | - | |
| 18 | + | |
19 | 19 | | |
20 | 20 | | |
21 | 21 | | |
22 | 22 | | |
23 | 23 | | |
| 24 | + | |
| 25 | + | |
24 | 26 | | |
25 | 27 | | |
26 | 28 | | |
27 | 29 | | |
28 | 30 | | |
29 | 31 | | |
| 32 | + | |
| 33 | + | |
30 | 34 | | |
31 | 35 | | |
32 | 36 | | |
| |||
59 | 63 | | |
60 | 64 | | |
61 | 65 | | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
62 | 73 | | |
63 | 74 | | |
64 | 75 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
30 | 30 | | |
31 | 31 | | |
32 | 32 | | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
41 | 41 | | |
42 | 42 | | |
43 | 43 | | |
44 | | - | |
| 44 | + | |
45 | 45 | | |
46 | 46 | | |
47 | 47 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
20 | 20 | | |
21 | 21 | | |
22 | 22 | | |
23 | | - | |
| 23 | + | |
24 | 24 | | |
25 | 25 | | |
26 | 26 | | |
| |||
95 | 95 | | |
96 | 96 | | |
97 | 97 | | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
98 | 103 | | |
99 | 104 | | |
100 | 105 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
20 | 20 | | |
21 | 21 | | |
22 | 22 | | |
| 23 | + | |
23 | 24 | | |
24 | 25 | | |
25 | 26 | | |
26 | 27 | | |
27 | 28 | | |
28 | 29 | | |
| 30 | + | |
29 | 31 | | |
30 | 32 | | |
31 | 33 | | |
| |||
42 | 44 | | |
43 | 45 | | |
44 | 46 | | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
45 | 54 | | |
46 | 55 | | |
47 | 56 | | |
| |||
127 | 136 | | |
128 | 137 | | |
129 | 138 | | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
130 | 159 | | |
131 | 160 | | |
132 | 161 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
40 | 40 | | |
41 | 41 | | |
42 | 42 | | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
43 | 46 | | |
44 | 47 | | |
45 | 48 | | |
| |||
60 | 63 | | |
61 | 64 | | |
62 | 65 | | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
63 | 70 | | |
64 | 71 | | |
65 | 72 | | |
| |||
540 | 547 | | |
541 | 548 | | |
542 | 549 | | |
| 550 | + | |
| 551 | + | |
| 552 | + | |
| 553 | + | |
543 | 554 | | |
544 | 555 | | |
545 | 556 | | |
| |||
552 | 563 | | |
553 | 564 | | |
554 | 565 | | |
| 566 | + | |
| 567 | + | |
| 568 | + | |
| 569 | + | |
| 570 | + | |
| 571 | + | |
| 572 | + | |
| 573 | + | |
| 574 | + | |
| 575 | + | |
| 576 | + | |
| 577 | + | |
| 578 | + | |
| 579 | + | |
| 580 | + | |
| 581 | + | |
| 582 | + | |
| 583 | + | |
| 584 | + | |
| 585 | + | |
| 586 | + | |
| 587 | + | |
| 588 | + | |
555 | 589 | | |
556 | 590 | | |
557 | 591 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
77 | 77 | | |
78 | 78 | | |
79 | 79 | | |
| 80 | + | |
80 | 81 | | |
81 | 82 | | |
82 | 83 | | |
83 | 84 | | |
84 | 85 | | |
85 | | - | |
| 86 | + | |
86 | 87 | | |
87 | 88 | | |
88 | 89 | | |
| |||
Lines changed: 39 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
53 | 53 | | |
54 | 54 | | |
55 | 55 | | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
56 | 84 | | |
57 | 85 | | |
58 | 86 | | |
| |||
205 | 233 | | |
206 | 234 | | |
207 | 235 | | |
| 236 | + | |
| 237 | + | |
| 238 | + | |
| 239 | + | |
| 240 | + | |
208 | 241 | | |
209 | 242 | | |
210 | 243 | | |
| |||
218 | 251 | | |
219 | 252 | | |
220 | 253 | | |
| 254 | + | |
| 255 | + | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
5 | 5 | | |
6 | 6 | | |
7 | 7 | | |
8 | | - | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
9 | 14 | | |
10 | 15 | | |
11 | 16 | | |
| |||
125 | 130 | | |
126 | 131 | | |
127 | 132 | | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
33 | 33 | | |
34 | 34 | | |
35 | 35 | | |
| 36 | + | |
| 37 | + | |
36 | 38 | | |
37 | 39 | | |
38 | 40 | | |
| |||
0 commit comments