Skip to content

Commit 281ecfb

Browse files
committed
Rework GHA testing: Use bcvk, cover composefs with tmt
Part 1: Use bcvk For local tests, right now testcloud+tmt doesn't support UEFI, see teemtee/tmt#4203 This is a blocker for us doing more testing with UKIs. In this patch we switch to provisioning VMs with bcvk, which fixes this - but beyond that a really compelling thing about this is that bcvk is *also* designed to be ergonomic and efficient beyond just being a test runner, with things like virtiofs mounting of host container storage, etc. In other words, bcvk is the preferred way to run local virt with bootc, and this makes our TMT tests use it. Now a major downside of this though is we're effectively implementing a new "provisioner" for tmt (bypassing the existing `virtual`). In the more medium term I think we want to add `bcvk` as a provisioner option to tmt. Anyways for now, this works by discovers test plans via `tmt plan ls`, spawning a separate VM per test, and then using uses tmt's connect provisioner to run tests targeting these externally provisioned systems. Part 2: Rework the Justfile and Dockerfile This adds `base` and `variant` arguments which are propagated through the system, and we have a new `variant` for sealed composefs. The readonly tests now pass with composefs. Drop the continuous repo tests...as while we could keep that it's actually a whole *other* entry in this matrix. Assisted-by: Claude Code (Sonnet 4.5) Signed-off-by: Colin Walters <[email protected]>
1 parent f028c93 commit 281ecfb

File tree

18 files changed

+838
-207
lines changed

18 files changed

+838
-207
lines changed

.github/workflows/ci.yml

Lines changed: 37 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -38,15 +38,6 @@ jobs:
3838
uses: ./.github/actions/bootc-ubuntu-setup
3939
- name: Validate (default)
4040
run: just validate
41-
# Build container with continuous repository enabled
42-
container-continuous:
43-
runs-on: ubuntu-24.04
44-
steps:
45-
- uses: actions/checkout@v5
46-
- name: Bootc Ubuntu Setup
47-
uses: ./.github/actions/bootc-ubuntu-setup
48-
- name: Build with continuous repo enabled
49-
run: sudo just build --build-arg=continuous_repo=1
5041
# Check for security vulnerabilities and license compliance
5142
cargo-deny:
5243
runs-on: ubuntu-24.04
@@ -141,47 +132,23 @@ jobs:
141132
- name: Install tmt
142133
run: pip install --user "tmt[provision-virtual]"
143134

144-
- name: Build container and disk image
145-
run: |
146-
set -xeuo pipefail
147-
build_args=()
148-
# Map from an ID-VERSIONID pair to a container ref
149-
target=${{ matrix.test_os }}
150-
OS_ID=$(echo "$target" | cut -d '-' -f 1)
151-
OS_VERSION_ID=$(echo "$target" | cut -d '-' -f 2)
152-
# Base image
153-
case "$OS_ID" in
154-
"centos")
155-
BASE="quay.io/centos-bootc/centos-bootc:stream${OS_VERSION_ID}"
156-
;;
157-
"fedora")
158-
BASE="quay.io/fedora/fedora-bootc:${OS_VERSION_ID}"
159-
;;
160-
*) echo "Unknown OS: ${OS_ID}" 1>&2; exit 1
161-
;;
162-
esac
163-
build_args+=("--build-arg=base=$BASE")
164-
just build ${build_args[@]}
165-
just build-integration-test-image
166-
# Cross check we're using the right base
167-
used_vid=$(podman run --rm localhost/bootc-integration bash -c '. /usr/lib/os-release && echo $VERSION_ID')
168-
test "$OS_VERSION_ID" = "${used_vid}"
169-
170-
- name: Run container tests
135+
- name: Setup env
171136
run: |
172-
just test-container
137+
BASE=$(just pullspec-for-os ${{ matrix.test_os }})
138+
echo "BOOTC_base=${BASE}" >> $GITHUB_ENV
173139
174-
- name: Generate disk image
140+
- name: Build container
175141
run: |
176-
mkdir -p target
177-
just build-disk-image localhost/bootc-integration target/bootc-integration-test.qcow2
142+
just build-integration-test-image
143+
# Extra cross-check (duplicating the integration test) that we're using the right base
144+
used_vid=$(podman run --rm localhost/bootc-integration bash -c '. /usr/lib/os-release && echo ${ID}-${VERSION_ID}')
145+
test ${{ matrix.test_os }} = "${used_vid}"
178146
179-
- name: Workaround https://github.com/teemtee/testcloud/issues/18
180-
run: sudo rm -f /usr/bin/chcon && sudo ln -sr /usr/bin/true /usr/bin/chcon
147+
- name: Unit and container integration tests
148+
run: just test-container
181149

182150
- name: Run all TMT tests
183-
run: |
184-
just test-tmt-nobuild
151+
run: just test-tmt
185152

186153
- name: Archive TMT logs
187154
if: always()
@@ -194,7 +161,10 @@ jobs:
194161
strategy:
195162
fail-fast: false
196163
matrix:
164+
# TODO expand this matrix, we need to make it better to override the target
165+
# OS via Justfile variables too
197166
test_os: [centos-10]
167+
variant: [composefs-sealeduki-sdboot]
198168

199169
runs-on: ubuntu-24.04
200170

@@ -204,9 +174,29 @@ jobs:
204174
uses: ./.github/actions/bootc-ubuntu-setup
205175
with:
206176
libvirt: true
177+
- name: Install tmt
178+
run: pip install --user "tmt[provision-virtual]"
179+
180+
- name: Setup env
181+
run: |
182+
BASE=$(just pullspec-for-os ${{ matrix.test_os }})
183+
echo "BOOTC_base=${BASE}" >> $GITHUB_ENV
184+
echo "BOOTC_variant="${{ matrix.variant }} >> $GITHUB_ENV
207185
208186
- name: Build container
209-
run: just build-sealed
187+
run: |
188+
just build-integration-test-image
189+
190+
- name: Unit and container integration tests
191+
run: just test-container
210192

211-
- name: Test
212-
run: just test-composefs
193+
- name: Run readonly TMT tests
194+
# TODO: expand to more tests
195+
run: just test-tmt readonly
196+
197+
- name: Archive TMT logs
198+
if: always()
199+
uses: actions/upload-artifact@v5
200+
with:
201+
name: tmt-log-PR-${{ github.event.number }}-${{ matrix.test_os }}-${{ env.ARCH }}-${{ matrix.tmt_plan }}
202+
path: /var/tmp/tmt

Cargo.lock

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Dockerfile

Lines changed: 51 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
# Build this project from source and write the updated content
22
# (i.e. /usr/bin/bootc and systemd units) to a new derived container
33
# image. See the `Justfile` for an example
4-
#
5-
# Use e.g. --build-arg=base=quay.io/fedora/fedora-bootc:42 to target
6-
# Fedora instead.
74

5+
# Note this is usually overridden via Justfile
86
ARG base=quay.io/centos-bootc/centos-bootc:stream10
97

108
# This first image captures a snapshot of the source code,
@@ -13,31 +11,7 @@ FROM scratch as src
1311
COPY . /src
1412

1513
FROM $base as base
16-
# Set this to anything non-0 to enable https://copr.fedorainfracloud.org/coprs/g/CoreOS/continuous/
17-
ARG continuous_repo=0
18-
RUN <<EORUN
19-
set -xeuo pipefail
20-
if [ "${continuous_repo}" == 0 ]; then
21-
exit 0
22-
fi
23-
# Sadly dnf copr enable looks for epel, not centos-stream....
24-
. /usr/lib/os-release
25-
case $ID in
26-
centos)
27-
curl -L -o /etc/yum.repos.d/continuous.repo https://copr.fedorainfracloud.org/coprs/g/CoreOS/continuous/repo/centos-stream-$VERSION_ID/group_CoreOS-continuous-centos-stream-$VERSION_ID.repo
28-
;;
29-
fedora)
30-
if rpm -q dnf5 &>/dev/null; then
31-
dnf -y install dnf5-plugins
32-
fi
33-
dnf copr enable -y @CoreOS/continuous
34-
;;
35-
*) echo "error: Unsupported OS '$ID'" >&2; exit 1
36-
;;
37-
esac
38-
dnf -y upgrade ostree bootupd
39-
rm -rf /var/cache/* /var/lib/dnf /var/lib/rhsm /var/log/*
40-
EORUN
14+
# We could inject other content here
4115

4216
# This image installs build deps, pulls in our source code, and installs updated
4317
# bootc binaries in /out. The intention is that the target rootfs is extracted from /out
@@ -94,20 +68,60 @@ RUN --mount=type=cache,target=/src/target --mount=type=cache,target=/var/roothom
9468

9569
# The final image that derives from the original base and adds the release binaries
9670
FROM base
97-
# Set this to 1 to default to systemd-boot
98-
ARG sdboot=0
71+
# See the Justfile for possible variants
72+
ARG variant
9973
RUN <<EORUN
10074
set -xeuo pipefail
10175
# Ensure we've flushed out prior state (i.e. files no longer shipped from the old version);
10276
# and yes, we may need to go to building an RPM in this Dockerfile by default.
10377
rm -vf /usr/lib/systemd/system/multi-user.target.wants/bootc-*
104-
if test "$sdboot" = 1; then
105-
dnf -y install systemd-boot-unsigned
106-
# And uninstall bootupd
107-
rpm -e bootupd
108-
rm /usr/lib/bootupd/updates -rf
109-
dnf clean all
110-
rm -rf /var/cache /var/lib/{dnf,rhsm} /var/log/*
78+
case "${variant}" in
79+
*-sdboot)
80+
dnf -y install systemd-boot-unsigned
81+
# And uninstall bootupd
82+
rpm -e bootupd
83+
rm /usr/lib/bootupd/updates -rf
84+
dnf clean all
85+
rm -rf /var/cache /var/lib/{dnf,rhsm} /var/log/*
86+
;;
87+
esac
88+
EORUN
89+
# Support overriding the rootfs at build time conveniently
90+
ARG rootfs=
91+
RUN <<EORUN
92+
set -xeuo pipefail
93+
# Do we have an explicit build-time override? Then write it.
94+
if test -n "$rootfs"; then
95+
cat > /usr/lib/bootc/install/80-rootfs-override.toml <<EOF
96+
[install.filesystem.root]
97+
type = "$rootfs"
98+
EOF
99+
else
100+
# Query the default rootfs
101+
base_rootfs=$(bootc install print-configuration | jq -r '.filesystem.root.type // ""')
102+
# No filesystem override set. If we're doing composefs, we need a FS that
103+
# supports fsverity. If btrfs is available we'll pick that, otherwise ext4.
104+
fs=
105+
case "${variant}" in
106+
composefs*)
107+
btrfs=$(grep -qEe '^CONFIG_BTRFS_FS' /usr/lib/modules/*/config && echo btrfs || true)
108+
fs=${btrfs:-ext4}
109+
;;
110+
*)
111+
# No explicit filesystem set and we're not using composefs. Default to xfs
112+
# with the rationale that we're trying to get filesystem coverage across
113+
# all the cases in general.
114+
if test -z "${base_rootfs}"; then
115+
fs=xfs
116+
fi
117+
;;
118+
esac
119+
if test -n "$fs"; then
120+
cat > /usr/lib/bootc/install/80-ext4-composefs.toml <<EOF
121+
[install.filesystem.root]
122+
type = "${fs}"
123+
EOF
124+
fi
111125
fi
112126
EORUN
113127
# Create a layer that is our new binaries

Justfile

Lines changed: 35 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -11,26 +11,35 @@
1111

1212
# --------------------------------------------------------------------
1313

14+
# ostree: The default
15+
# composefs-sealeduki-sdboot: A system with a sealed composefs using systemd-boot
16+
variant := env("BOOTC_variant", "ostree")
17+
base := env("BOOTC_base", "quay.io/centos-bootc/centos-bootc:stream10")
18+
19+
buildargs := "--build-arg=base=" + base + " --build-arg=variant=" + variant
20+
1421
# Build the container image from current sources.
1522
# Note commonly you might want to override the base image via e.g.
1623
# `just build --build-arg=base=quay.io/fedora/fedora-bootc:42`
17-
build *ARGS:
18-
podman build --jobs=4 -t localhost/bootc {{ARGS}} .
19-
20-
# Build a sealed image from current sources. This will default to
21-
# generating Secure Boot keys in target/test-secureboot.
22-
build-sealed *ARGS:
23-
podman build --build-arg=sdboot=1 --jobs=4 -t localhost/bootc-unsealed {{ARGS}} .
24-
./tests/build-sealed localhost/bootc-unsealed localhost/bootc
24+
build:
25+
podman build --jobs=4 -t localhost/bootc-bin {{buildargs}} .
26+
./tests/build-sealed {{variant}} localhost/bootc-bin localhost/bootc
2527

2628
# This container image has additional testing content and utilities
27-
build-integration-test-image *ARGS:
28-
cd hack && podman build --jobs=4 -t localhost/bootc-integration -f Containerfile {{ARGS}} .
29+
build-integration-test-image: build
30+
cd hack && podman build --jobs=4 -t localhost/bootc-integration-bin {{buildargs}} -f Containerfile .
31+
./tests/build-sealed {{variant}} localhost/bootc-integration-bin localhost/bootc-integration
2932
# Keep these in sync with what's used in hack/lbi
3033
podman pull -q --retry 5 --retry-delay 5s quay.io/curl/curl:latest quay.io/curl/curl-base:latest registry.access.redhat.com/ubi9/podman:latest
3134

32-
test-composefs: build-sealed
35+
# Build+test composefs; compat alias
36+
test-composefs:
37+
# These first two are currently a distinct test suite from tmt that directly
38+
# runs an integration test binary in the base image via bcvk
39+
just variant=composefs-sealeduki-sdboot build
3340
cargo run --release -p tests-integration -- composefs-bcvk localhost/bootc
41+
# We're trying to move more testing to tmt, so
42+
just variant=composefs-sealeduki-sdboot test-tmt readonly
3443

3544
# Only used by ci.yml right now
3645
build-install-test-image: build-integration-test-image
@@ -59,27 +68,27 @@ validate-local:
5968
build-disk *ARGS:
6069
./tests/build.sh {{ARGS}}
6170

62-
# The tests which run a fully booted bootc system (i.e. where in place
63-
# updates are supported) as if it were a production environment use
64-
# https://github.com/teemtee/tmt.
71+
# Run tmt-based test suites using local virtual machines with
72+
# bcvk.
6573
#
66-
# This task runs *all* of the tmt-based tests targeting the disk image generated
67-
# in the previous step.
68-
test-tmt *ARGS: build-disk
69-
./tests/run-tmt.sh {{ARGS}}
74+
# To run an individual test, pass it as an argument like:
75+
# `just test-tmt readonly`
76+
test-tmt *ARGS: build-integration-test-image
77+
cargo xtask run-tmt --env=BOOTC_variant={{variant}} localhost/bootc-integration {{ARGS}}
7078

71-
# Like test-tmt but assumes that a disk image is already built
72-
test-tmt-nobuild *ARGS:
73-
./tests/run-tmt.sh {{ARGS}}
74-
75-
# Run just one tmt test: `just test-tmt-one test-20-local-upgrade`
76-
test-tmt-one PLAN: build-disk
77-
./tests/run-tmt.sh plan --name {{PLAN}}
79+
# Cleanup all test VMs created by tmt tests
80+
tmt-vm-cleanup:
81+
bcvk libvirt rm --stop --force --label bootc.test=1
7882

7983
# Run tests (unit and integration) that are containerized
8084
test-container: build-units build-integration-test-image
8185
podman run --rm --read-only localhost/bootc-units /usr/bin/bootc-units
82-
podman run --rm localhost/bootc-integration bootc-integration-tests container
86+
# Pass these through for cross-checking
87+
podman run --rm --env=BOOTC_variant={{variant}} --env=BOOTC_base={{base}} localhost/bootc-integration bootc-integration-tests container
88+
89+
# Print the container image reference for a given short $ID-VERSION_ID
90+
pullspec-for-os NAME:
91+
@jq -r --arg v "{{NAME}}" '.[$v]' < hack/os-image-map.json
8392

8493
build-mdbook:
8594
cd docs && podman build -t localhost/bootc-mdbook -f Dockerfile.mdbook

crates/tests-integration/src/container.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use std::process::Command;
22

33
use anyhow::{Context, Result};
4+
use camino::Utf8Path;
45
use fn_error_context::context;
56
use libtest_mimic::Trial;
67
use xshell::{cmd, Shell};
@@ -49,10 +50,42 @@ fn test_system_reinstall_help() -> Result<()> {
4950
Ok(())
5051
}
5152

53+
/// Verify that the values of `variant` and `base` from Justfile actually applied
54+
/// to this container image.
55+
fn test_variant_base_crosscheck() -> Result<()> {
56+
if let Some(variant) = std::env::var("BOOTC_variant").ok() {
57+
// TODO add this to `bootc status` or so?
58+
let boot_efi = Utf8Path::new("/boot/EFI");
59+
match variant.as_str() {
60+
"ostree" => {
61+
assert!(!boot_efi.try_exists()?);
62+
}
63+
"composefs-sealeduki-sdboot" => {
64+
assert!(boot_efi.try_exists()?);
65+
}
66+
o => panic!("Unhandled variant: {o}"),
67+
}
68+
}
69+
if let Some(base) = std::env::var("BOOTC_base").ok() {
70+
// Hackily reverse back from container pull spec to ID-VERSION_ID
71+
// TODO: move the OsReleaseInfo into an internal crate we use
72+
let osrelease = std::fs::read_to_string("/usr/lib/os-release")?;
73+
if base.contains("centos-bootc") {
74+
assert!(osrelease.contains(r#"ID="centos""#))
75+
} else if base.contains("fedora-bootc") {
76+
assert!(osrelease.contains(r#"ID=fedora"#));
77+
} else {
78+
eprintln!("notice: Unhandled base {base}")
79+
}
80+
}
81+
Ok(())
82+
}
83+
5284
/// Tests that should be run in a default container image.
5385
#[context("Container tests")]
5486
pub(crate) fn run(testargs: libtest_mimic::Arguments) -> Result<()> {
5587
let tests = [
88+
new_test("variant-base-crosscheck", test_variant_base_crosscheck),
5689
new_test("bootc upgrade", test_bootc_upgrade),
5790
new_test("install config", test_bootc_install_config),
5891
new_test("status", test_bootc_status),

0 commit comments

Comments
 (0)