Skip to content

Run the ppc64le and s390x compose tests in containers - #83

Merged
yuravk merged 8 commits into
almalinuxfrom
ci/native-containers-ppc64le-s390x
Nov 3, 2026
Merged

yuravk merged 8 commits into
almalinuxfrom
ci/native-containers-ppc64le-s390x

Conversation

@yuravk

@yuravk yuravk commented Nov 2, 2026

Copy link
Copy Markdown
Collaborator

Summary

Runs the compose tests for ppc64le and s390x in 4 AlmaLinux containers
(podman, systemd as PID 1), split between them the same way as on
x86_64 and aarch64.

  • AlmaLinux organization: natively on POWER runners
    (ubuntu-26.04-ppc64le, -p10 for AlmaLinux 10 and Kitten) and on
    ubuntu-26.04-s390x.
  • Forks: under QEMU user-mode emulation on an x86_64 runner. The
    tests' time limits are scaled ×8 there instead of ×2, and the tests
    get emulated_guest=true.

The containers share the runner's kernel, so the tests run with the tmt
context container=yes and CONTAINERTEST=1.

Changes

CI

  • ci/container-guests.sh starts the containers and waits for systemd
    to boot in each one.
    • Under emulation, units with credentials fail with
      243/CREDENTIALS. The script runs sysusers and tmpfiles itself,
      gives journald a copy of its unit without credentials, and clears
      the credentials of libvirtd and virtsecretd (libvirt 12.8, Kitten).
    • It checks that the containers resolve names. When they don't, it
      prints what DNS goes through, reloads podman's network once, and
      fails the step if that doesn't help.
  • Container network: if podman's default network (10.88.0.0/16)
    overlaps the runner's routes or nameservers, the containers get a
    network of their own on a free subnet. One native s390x runner's DNS
    server was 10.88.92.1, so the containers' queries went into the
    bridge and no name resolved.
  • Native s390x runner: it's an Incus container with AppArmor. A
    program started from a path one of its profiles attaches to (e.g.
    /usr/bin/hostname, /usr/bin/gs) failed to load libc, so the
    confining profiles are unloaded. The unconfined ones are kept: podman
    needs them.
  • inotify instances: the rootful containers share root's inotify
    instances. The limit is raised to 8192 where the runner allows it.
    The native runners don't allow it and keep 128 or 1024.
    ci/inotify-peak.sh logs the peak count while the tests run, and
    which process and container hold them. With 4 containers the peak is
    46–48, against a limit of 128. In 2 earlier runs something held about
    75 more for over 15 minutes, and D-Bus and libvirtd failed; this log
    is there to catch it if it happens again.
  • Container setup: curl-minimal is replaced with curl, hostname is
    installed, and so are rsync (AlmaLinux 10) and initscripts
    (AlmaLinux 8), so the containers are closer to an installed system.
  • Skipped tests: the run-page summary and the Mattermost message
    list the skipped tests with the reason.

Tests

  • Disabled for container=yes:
    • attr: needs loop-device mounts.
    • libbpf-tools and perf: need BPF and perf events of the runner's
      kernel.
    • libvirt/net: needs libvirt networks.
    • podman: needs nested containers.
    • selinux: needs SELinux.
    • sysstat/iostat: drops the page cache.
  • Skipped with CONTAINERTEST=1:
    • p_kvdo: needs a kernel module.
    • p_tcpdump: only the NIC check.
    • p_osbuild: an image build needs loop devices and mounts.
  • s390x fixes (not product bugs):
    • p_bash: the vendor in the build triplet is ibm up to AlmaLinux 9
      and redhat from 10.
    • libbpf-tools: bpf-numamove isn't shipped for s390x.
    • libvirt/socket: an emulated s390x container has no
      /proc/sysinfo.
    • p_gzip: z15 and newer compress in hardware (DFLTCC), so only the
      uncompressed size and the name are checked.
  • Emulated guests:
    • p_exim, p_openssh: the expect helpers wait 30 seconds for an
      answer.
    • p_osbuild: skipped, because building an image takes over an hour.
  • t_GetArch: takes the architecture from uname when there is no
    kernel package.

Testing

  • Native: AlmaLinux 8, 9, 10 and Kitten pass on both ppc64le and
    s390x. Jobs take 11–25 minutes.
  • Emulated (fork): AlmaLinux 8, 9, 10 and Kitten pass on both
    architectures.
  • Not yet run: the separate network. The runners used since the fix
    don't overlap 10.88.0.0/16, so they kept podman's default network as
    before.

ci/container-guests.sh starts a container per name in $guests from an
almalinux/<version>-init image, systemd as PID 1, of the given
architecture: natively on a runner of that architecture, run by QEMU
user-mode emulation on another one. It waits until systemd has booted
each container and fixes what does not boot under emulation: units with
credentials (ImportCredential=) fail there with 243/CREDENTIALS, so it
runs systemd-sysusers and systemd-tmpfiles itself, gives journald
(which imports credentials since AlmaLinux 10) a copy of its unit
without them, and restarts what failed at boot, D-Bus with it. The
services installed later fail the same way: libvirtd and virtsecretd
load an encrypted credential since libvirt 12.8 (AlmaLinux Kitten), so
under emulation a drop-in clears their credentials. Not a top-level
drop-in for every service: under s390x emulation systemd reports a unit
with a drop-in as changed on disk, and postgresql-setup then refuses to
initialize the database. It lists the units that failed at boot and
those still failed.
Run the compose tests in 4 AlmaLinux ppc64le containers (podman, systemd
as PID 1, ci/container-guests.sh), split between them like on x86_64
and aarch64. The containers share the runner's kernel, so the tests run
with the tmt context container=yes and CONTAINERTEST=1.

In the AlmaLinux organization the containers run natively on a POWER
runner, a POWER10 one for AlmaLinux 10 (ubuntu-26.04-ppc64le(-p10)).
Elsewhere, e.g. in forks, they run on an x86_64 runner (ubuntu-26.04)
under QEMU user-mode emulation, which needs QEMU 10.1 or newer (QEMU
issue 1769) and the binfmt_misc C flag for setuid programs; the tests'
time limits are scaled x8 there instead of x2, and they get
emulated_guest=true.

The containers are made more like an installed system: curl-minimal is
swapped for curl and hostname installed, rsync (tmt from pip) on
AlmaLinux 10 and initscripts (/sbin/service) on AlmaLinux 8. The
runner's inotify instances limit, which the rootful containers share,
is raised where the runner allows it, and its use shown.
Run the compose tests in 4 AlmaLinux s390x containers like the ppc64le
ones: natively in the AlmaLinux organization (ubuntu-26.04-s390x), under
QEMU user-mode emulation on an x86_64 runner elsewhere, e.g. in forks.

The native s390x runner is an Incus container with AppArmor and Ubuntu's
profiles, and podman runs the containers unconfined, so a program
started from a path a runner profile attaches to gets that profile:
anything started as /usr/bin/hostname or /usr/bin/gs failed to load
libc ("cannot apply additional memory protection after relocation:
Permission denied"), even with the profile in complain mode. Before
starting the containers unload these profiles, but not those flagged
unconfined: they confine nothing, they grant (user namespaces to podman,
crun, ...), the containers run under one of them, and reloaded in
complain mode they broke podman's overlay storage.
The ppc64le and s390x tests run in containers (tmt context
container=yes, CONTAINERTEST=1), which share the runner's kernel.

Disable for container=yes the tests a container cannot run: attr (no
loop device mounts), libbpf-tools and perf (BPF programs and perf
events of the runner's kernel), libvirt/net (no libvirt networks),
podman (no nested containers), selinux (no SELinux), sysstat/iostat (no
dropping the page cache). Skip with CONTAINERTEST=1 p_kvdo (a kernel
module), the NIC check of p_tcpdump and p_osbuild (an image build needs
loop devices and mounts). t_GetArch takes the architecture from uname
when there is no kernel package.

List the skipped tests, with the reason, in the run-page summary and
the Mattermost message of the ppc64le and s390x workflows: they only
count the tests that ran.
- p_bash: bash on s390x is built for the IBM vendor up to AlmaLinux 9
  (s390x-ibm-linux-gnu), for redhat since 10 (s390x-redhat-linux-gnu).
- libbpf-tools: bpf-numamove is not shipped for s390x.
- libvirt/socket: libvirt reads the host's system information on s390x
  from /proc/sysinfo, which only an s390x kernel has: an emulated s390x
  container on another one has none, so skip the sysinfo check there.
- p_gzip: s390x machines with DFLTCC (z15 and newer) compress in
  hardware, which gives the test file another compressed size: check
  the uncompressed size and name of 'gzip -l' only, not the md5sum of
  its whole output.
The ppc64le and s390x tests run emulated on forks (emulated_guest=true),
several times slower than on the hardware the tests are meant for.

- p_exim, p_openssh: the expect helpers wait 1 or 2 seconds for an
  answer, too short there: wait 30 seconds in an emulated guest.
- p_osbuild: building an image takes more than an hour there: skip it
  in an emulated guest.
On the native s390x runner the containers could not resolve any name for
a whole job, in 3 of 21 runs, while the runner itself could: "Update the
system" waited 2.5 minutes for its first name in every container, then
failed with "Could not resolve host: mirrors.almalinux.org". The
containers reach the runner's network through podman's default one,
10.88.0.0/16, and such a runner's DNS server was 10.88.92.1: the
containers sent their queries to the bridge, so no name resolved (HTTPS
to an address still worked).

Before starting the containers, take the first of 10.88.0.0/16,
10.89.0.0/16, ... that is none of the runner's routes nor nameservers.
When that is not 10.88.0.0/16, start the containers on a network of
their own with it, without DNS like podman's default network, so that
they still use the runner's nameservers.

After starting them check that each one resolves a name, for about a
minute. If they still cannot, print what DNS goes through (the runner's
and the containers' nameservers, podman's network, IP forwarding, whether
a container reaches the internet by address), reload podman's network,
which re-applies its firewall rules, and check again. Fail the step at
once if that does not help.
The count of root's inotify instances in use logged after the
containers start does not show how near the runner's limit a run gets:
in the native ppc64le runs that failed, with the limit at 128, D-Bus
and libvirtd ran out of them minutes into the tests and for the rest of
them. Count root's inotify instances every 5 seconds while the tests
run and log the largest count, with how many each process held and in
which container, to tell what holds them when it happens again.
@yuravk
yuravk merged commit a96ab95 into almalinux Nov 3, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants