Skip to content

Commit 82209b9

Browse files
committed
install: mount esp part before clean_boot_directories()
- On existing ostree OS like FCOS, esp is not mounted after booted, need to find esp and mount before clean, or the /boot/efi will be removed. - Add test to verify bootc install to-existing working on FCOS. Signed-off-by: Huijing Hei <hhei@redhat.com>
1 parent 124370c commit 82209b9

File tree

8 files changed

+191
-4
lines changed

8 files changed

+191
-4
lines changed

.github/workflows/ci.yml

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ jobs:
106106
sudo find /ostree/repo/objects -name '*.file' -type f | while read f; do
107107
sudo fsverity measure $f >/dev/null
108108
done
109+
109110
# Test that we can build documentation
110111
docs:
111112
runs-on: ubuntu-24.04
@@ -197,12 +198,20 @@ jobs:
197198
- name: Unit and container integration tests
198199
run: just test-container
199200

200-
- name: Run TMT tests
201+
- name: Run TMT integration tests
201202
run: |
202203
if [ "${{ matrix.variant }}" = "composefs-sealeduki-sdboot" ]; then
203204
just test-composefs
204205
else
205-
just test-tmt
206+
just test-tmt integration
207+
fi
208+
- name: Run TMT test about bootc install on coreos
209+
run: |
210+
# Only test fedora-43 on fedora-coreos:testing-devel
211+
if [ "${{ matrix.test_os }}" = "fedora-43" ] && [ "${{ matrix.variant }}" = "ostree" ]; then
212+
just test-tmt-on-coreos plan-bootc-install-on-coreos
213+
else
214+
echo "skipped"
206215
fi
207216
208217
- name: Archive TMT logs

Justfile

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ buildargs := base_buildargs + " --secret=id=secureboot_key,src=target/test-secur
4343
# Args for build-sealed (no base arg, it sets that itself)
4444
sealed_buildargs := "--build-arg=variant=" + variant + " --secret=id=secureboot_key,src=target/test-secureboot/db.key --secret=id=secureboot_cert,src=target/test-secureboot/db.crt"
4545

46+
# Needed by bootc install on ostree
47+
fedora-coreos := "quay.io/fedora/fedora-coreos:testing-devel"
48+
4649
# The default target: build the container image from current sources.
4750
# Note commonly you might want to override the base image via e.g.
4851
# `just build --build-arg=base=quay.io/fedora/fedora-bootc:42`
@@ -161,6 +164,13 @@ _build-upgrade-image:
161164
test-tmt-nobuild *ARGS:
162165
cargo xtask run-tmt --env=BOOTC_variant={{variant}} --upgrade-image={{integration_upgrade_img}} {{integration_img}} {{ARGS}}
163166

167+
# Test bootc install on coreos (FCOS)
168+
# Assume the localhost/bootc-integration image is up to date, and just run tests.
169+
# BOOTC_target is the bootc-integration, that will be used for bootc install
170+
test-tmt-on-coreos *ARGS:
171+
@if ! podman image exists {{integration_img}}; then just build-integration-test-image; fi
172+
cargo xtask run-tmt --env=BOOTC_variant={{variant}} --env=BOOTC_target={{integration_img}}:latest {{fedora-coreos}} {{ARGS}}
173+
164174
# Cleanup all test VMs created by tmt tests
165175
tmt-vm-cleanup:
166176
bcvk libvirt rm --stop --force --label bootc.test=1
@@ -175,6 +185,7 @@ test-container: build-units build-integration-test-image
175185
clean-local-images:
176186
podman images --filter "label={{testimage_label}}"
177187
podman images --filter "label={{testimage_label}}" --format "{{{{.ID}}" | xargs -r podman rmi -f
188+
podman rmi {{fedora-coreos}} -f
178189

179190
# Print the container image reference for a given short $ID-VERSION_ID for NAME
180191
# and 'base' or 'buildroot-base' for TYPE (base image type)

crates/lib/src/bootloader.rs

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,13 @@ use anyhow::{anyhow, bail, Context, Result};
44
use bootc_utils::CommandRunExt;
55
use camino::Utf8Path;
66
use cap_std_ext::cap_std::fs::Dir;
7+
use cap_std_ext::dirext::CapStdExtDirExt;
78
use fn_error_context::context;
89

910
use bootc_blockdev::{Partition, PartitionTable};
1011
use bootc_mount as mount;
1112

12-
use crate::bootc_composefs::boot::mount_esp;
13+
use crate::bootc_composefs::boot::{get_sysroot_parent_dev, mount_esp};
1314
use crate::{discoverable_partition_specification, utils};
1415

1516
/// The name of the mountpoint for efi (as a subdirectory of /boot, or at the toplevel)
@@ -26,6 +27,36 @@ pub(crate) fn esp_in(device: &PartitionTable) -> Result<&Partition> {
2627
.ok_or(anyhow::anyhow!("ESP not found in partition table"))
2728
}
2829

30+
/// Get esp partition node based on the root dir
31+
pub(crate) fn get_esp_partition_node(root: &Dir) -> Result<Option<String>> {
32+
let device = get_sysroot_parent_dev(&root)?;
33+
let base_partitions = bootc_blockdev::partitions_of(Utf8Path::new(&device))?;
34+
let esp = base_partitions.find_partition_of_esp()?;
35+
if let Some(esp) = esp {
36+
return Ok(Some(esp.node.clone()));
37+
};
38+
Ok(None)
39+
}
40+
41+
// Mount esp part at /boot/efi
42+
pub(crate) fn mount_esp_part(root: &Dir, root_path: &Utf8Path, physical_root: &Dir) -> Result<()> {
43+
let efi_path = Utf8Path::new("boot").join(crate::bootloader::EFI_DIR);
44+
if let Some(esp_fd) = root
45+
.open_dir_optional(&efi_path)
46+
.context("Opening /boot/efi")?
47+
{
48+
if let Some(false) = esp_fd.is_mountpoint(".")? {
49+
tracing::debug!("Not a mountpoint: /boot/efi");
50+
// On ostree env, should use /target/sysroot because of composefs
51+
if let Some(esp_part) = get_esp_partition_node(&physical_root)? {
52+
bootc_mount::mount(&esp_part, &root_path.join(&efi_path))?;
53+
tracing::debug!("Mounted {esp_part} at /boot/efi");
54+
}
55+
}
56+
}
57+
Ok(())
58+
}
59+
2960
/// Determine if the invoking environment contains bootupd, and if there are bootupd-based
3061
/// updates in the target root.
3162
#[context("Querying for bootupd")]

crates/lib/src/install.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2168,6 +2168,15 @@ pub(crate) async fn install_to_filesystem(
21682168
.await??;
21692169
}
21702170
Some(ReplaceMode::Alongside) => {
2171+
// On existing ostree OS like FCOS, esp is not mounted after booted,
2172+
// need to find esp and mount before clean
2173+
if ARCH_USES_EFI {
2174+
crate::bootloader::mount_esp_part(
2175+
&target_rootfs_fd,
2176+
&target_root_path,
2177+
&rootfs_fd,
2178+
)?;
2179+
}
21712180
clean_boot_directories(&target_rootfs_fd, is_already_ostree)?
21722181
}
21732182
None => require_empty_rootdir(&rootfs_fd)?,

crates/xtask/src/tmt.rs

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,25 @@ fn build_firmware_args(sh: &Shell, image: &str) -> Result<Vec<String>> {
137137
Ok(r)
138138
}
139139

140+
/// Detect VARIANT_ID from container image by reading os-release
141+
/// Returns string like "coreos" or empty
142+
#[context("Detecting distro from image")]
143+
fn detect_variantid_from_image(sh: &Shell, image: &str) -> Result<Option<String>> {
144+
let variant_id = cmd!(
145+
sh,
146+
"podman run --rm {image} bash -c '. /usr/lib/os-release && echo $VARIANT_ID'"
147+
)
148+
.read()
149+
.context("Failed to run image as container to detect distro")?;
150+
151+
let variant_id = variant_id.trim();
152+
if variant_id.is_empty() {
153+
return Ok(None);
154+
}
155+
156+
Ok(Some(variant_id.to_string()))
157+
}
158+
140159
/// Check if a distro supports --bind-storage-ro
141160
/// CentOS 9 lacks systemd.extra-unit.* support required for bind-storage-ro
142161
fn distro_supports_bind_storage_ro(distro: &str) -> bool {
@@ -269,18 +288,25 @@ pub(crate) fn run_tmt(sh: &Shell, args: &RunTmtArgs) -> Result<()> {
269288

270289
// Detect distro from the image
271290
let distro = detect_distro_from_image(sh, image)?;
291+
// Detect VARIANT_ID from the image
292+
// As this can not be empty value in context, use "unknown" instead
293+
let variant_id = detect_variantid_from_image(sh, image)?.unwrap_or("unknown".to_string());
272294

273295
let context = args
274296
.context
275297
.iter()
276298
.map(|v| format!("--context={}", v))
277299
.chain(std::iter::once(format!("--context=running_env=image_mode")))
278300
.chain(std::iter::once(format!("--context=distro={}", distro)))
301+
.chain(std::iter::once(format!(
302+
"--context=VARIANT_ID={variant_id}"
303+
)))
279304
.collect::<Vec<_>>();
280305
let preserve_vm = args.preserve_vm;
281306

282307
println!("Using bcvk image: {}", image);
283308
println!("Detected distro: {}", distro);
309+
println!("Detected VARIANT_ID: {variant_id}");
284310

285311
let firmware_args = build_firmware_args(sh, image)?;
286312

@@ -387,7 +413,16 @@ pub(crate) fn run_tmt(sh: &Shell, args: &RunTmtArgs) -> Result<()> {
387413
distro
388414
);
389415
}
390-
416+
// Add --filesystem=xfs by default on fedora-coreos
417+
// Add --bind-storage-ro if supported
418+
if variant_id == "coreos" {
419+
if distro.starts_with("fedora") {
420+
opts.push("--filesystem=xfs".to_string());
421+
}
422+
if supports_bind_storage_ro {
423+
opts.push(BCVK_OPT_BIND_STORAGE_RO.to_string());
424+
}
425+
}
391426
opts
392427
};
393428

tmt/plans/tests-install.fmf

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
discover:
2+
how: fmf
3+
execute:
4+
how: tmt
5+
# Because of the two issues, run tmt on github runner directly failed
6+
# - selinux disabled on ubuntu (https://github.com/teemtee/tmt/issues/3364)
7+
# - uefi firmware is not supported (https://github.com/teemtee/tmt/issues/4203)
8+
provision:
9+
how: virtual
10+
hardware:
11+
boot:
12+
method: uefi
13+
image: fedora-coreos-next
14+
user: root
15+
memory: 4096
16+
disk: 20
17+
18+
/plan-bootc-install-on-coreos:
19+
summary: Execute bootc install on ostree OS
20+
adjust:
21+
- when: VARIANT_ID != coreos
22+
enabled: false
23+
because: this needs to start an ostree OS firstly
24+
discover:
25+
how: fmf
26+
test:
27+
- /tmt/tests/install/test-bootc-install-on-coreos
28+
extra-try_bind_storage: true
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# number: 50
2+
# extra:
3+
# try_bind_storage: true
4+
# tmt:
5+
# summary: Test bootc install on ostree OS
6+
# duration: 30m
7+
# adjust:
8+
# - when: VARIANT_ID != coreos
9+
# enabled: false
10+
# because: this needs to start an ostree OS firstly
11+
#
12+
#!/bin/bash
13+
set -eux
14+
15+
echo "Testing bootc install on ostree"
16+
17+
# BOOTC_target is integration image
18+
[ -n "$BOOTC_target" ]
19+
20+
if [ "$TMT_REBOOT_COUNT" -eq 0 ]; then
21+
echo "Running before first reboot"
22+
pwd
23+
ls -l
24+
# Verify testing on ostree OS
25+
if [ ! -f "/run/ostree-booted" ]; then
26+
echo "Should be ostree OS"
27+
exit 1
28+
fi
29+
podman image exists ${BOOTC_target}
30+
# Run bootc install using the same stateroot for shared /var
31+
stateroot=$(bootc status --json | jq -r .status.booted.ostree.stateroot)
32+
33+
# Need bind mount for /run/host-container-storage
34+
podman run --rm --privileged \
35+
-v /dev:/dev \
36+
-v /run/host-container-storage:/run/host-container-storage:ro \
37+
-v /:/target \
38+
--pid=host \
39+
--security-opt label=type:unconfined_t \
40+
${BOOTC_target} \
41+
env BOOTC_BOOTLOADER_DEBUG=1 STORAGE_OPTS=additionalimagestore=/run/host-container-storage \
42+
bootc install to-existing-root \
43+
--stateroot=${stateroot} \
44+
--skip-fetch-check \
45+
--acknowledge-destructive \
46+
--karg=console=ttyS0,115200n8
47+
48+
bootc status
49+
tmt-reboot
50+
elif [ "$TMT_REBOOT_COUNT" -eq 1 ]; then
51+
echo 'After the reboot'
52+
booted=$(bootc status --json | jq -r .status.booted.image.image.image)
53+
[ ${booted} == ${BOOTC_target} ]
54+
fi
55+
56+
echo "Run bootc install on ostree OS successfully"

tmt/tests/install.fmf

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
/test-bootc-install-on-coreos:
2+
summary: Test bootc install on coreos
3+
duration: 30m
4+
adjust:
5+
- when: VARIANT_ID != coreos
6+
enabled: false
7+
because: this needs to start an ostree OS firstly
8+
test: bash examples/test-on-ostree/test-install-on-ostree.sh

0 commit comments

Comments
 (0)