Skip to content

Commit 9953c46

Browse files
committed
fix: fail UKI build when bootloader config is missing
Fail the UKI builds when none of the packages provided a systemd-boot configuration. Add integration test to validate the error. Signed-off-by: Arnaldo Garcia Rincon <agarrcia@amazon.com>
1 parent 0a2cb13 commit 9953c46

3 files changed

Lines changed: 117 additions & 6 deletions

File tree

twoliter/embedded/imghelper

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -742,19 +742,18 @@ provide_certs() {
742742
# `*.efi` glob used to copy the loader binaries does not pick it up, so it needs
743743
# its own directory and copy here.
744744
#
745-
# A missing configuration file is not fatal: systemd-boot falls back to its
746-
# compiled-in defaults, which still boot the image. Warn instead of failing the
747-
# whole image build, since the rest of the ESP is intact.
745+
# A missing configuration file is treated as a build error: the file is
746+
# expected to be present whenever this function is called, so a missing file
747+
# indicates a packaging problem rather than an intentionally absent config.
748748
provide_loader_config() {
749749
local efi_image efi_mount loader_config
750750
efi_image="${1:?}"
751751
efi_mount="${2:?}"
752752
loader_config="${efi_mount}/loader/loader.conf"
753753

754754
if [[ ! -f "${loader_config}" ]]; then
755-
echo "no systemd-boot configuration found at ${loader_config}," \
756-
"so systemd-boot will use its compiled-in defaults" >&2
757-
return 0
755+
echo "no systemd-boot configuration found at ${loader_config}" >&2
756+
exit 1
758757
fi
759758

760759
mmd -i "${efi_image}" ::/loader
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
#!/usr/bin/env bash
2+
#
3+
# Test `provide_loader_config` in `imghelper`:
4+
#
5+
# When `loader/loader.conf` is missing from the ESP staging mount, the
6+
# function must fail hard (non-zero exit) with an actionable error naming
7+
# the searched path, instead of silently continuing with systemd-boot's
8+
# compiled-in defaults.
9+
#
10+
# Run from the repo root:
11+
# bash twoliter/embedded/tests/test_provide_loader_config.sh
12+
13+
set -eu -o pipefail
14+
15+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
16+
EMBEDDED_DIR="${SCRIPT_DIR}/.."
17+
IMGHELPER="${EMBEDDED_DIR}/imghelper"
18+
19+
if [[ ! -f "${IMGHELPER}" ]]; then
20+
echo "test_provide_loader_config: required file not found: ${IMGHELPER}" >&2
21+
exit 1
22+
fi
23+
24+
pass_count=0
25+
fail_count=0
26+
27+
pass() { pass_count=$((pass_count + 1)); echo " ok: $1"; }
28+
fail() { fail_count=$((fail_count + 1)); echo " FAIL: $1" >&2; }
29+
30+
tmp=$(mktemp -d)
31+
trap 'rm -rf "${tmp}"' EXIT
32+
33+
# ---------------------------------------------------------------------------
34+
# Test: missing loader.conf fails hard with an actionable message that
35+
# names the searched path.
36+
# ---------------------------------------------------------------------------
37+
efi_mount="${tmp}/efi_mount"
38+
mkdir -p "${efi_mount}"
39+
efi_image="${tmp}/efi.img"
40+
loader_config="${efi_mount}/loader/loader.conf"
41+
42+
out=$(
43+
VERSION_ID=x BUILD_ID=x IMAGE_NAME=x VARIANT=x ARCH=x86_64 \
44+
bash -c "
45+
set -eu -o pipefail
46+
. '${IMGHELPER}'
47+
provide_loader_config '${efi_image}' '${efi_mount}'
48+
" 2>&1
49+
) && rc=0 || rc=$?
50+
51+
if [[ "${rc}" -eq 0 ]]; then
52+
fail "provide_loader_config should fail when loader.conf is missing, got exit 0"
53+
elif [[ "${out}" != *"${loader_config}"* ]]; then
54+
fail "error message should name the searched path '${loader_config}'; got: ${out}"
55+
else
56+
pass "provide_loader_config fails hard and names the searched path when loader.conf is missing"
57+
fi
58+
59+
echo
60+
echo "test_provide_loader_config: ${pass_count} passed, ${fail_count} failed"
61+
[[ "${fail_count}" -eq 0 ]]
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
//! Integration test that runs the bash-side `provide_loader_config` test
2+
//! suite.
3+
//!
4+
//! The actual assertions live in
5+
//! `embedded/tests/test_provide_loader_config.sh`, which exercises:
6+
//!
7+
//! * `provide_loader_config` (defined in `imghelper`) fails hard with an
8+
//! actionable error naming the searched `loader/loader.conf` path when
9+
//! the systemd-boot loader configuration is missing from the ESP
10+
//! staging mount, instead of silently continuing with systemd-boot's
11+
//! compiled-in defaults.
12+
//!
13+
//! This Rust wrapper exists so the bash tests run under `cargo test`
14+
//! alongside the other embedded-bundle and buildsys tests.
15+
16+
use std::path::PathBuf;
17+
use std::process::Command;
18+
19+
#[test]
20+
fn provide_loader_config_bash_tests() {
21+
// The `embedded` directory is a symlink to `twoliter/embedded` at the
22+
// crate root, so `CARGO_MANIFEST_DIR` reaches the script either way.
23+
let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
24+
let script = manifest_dir.join("embedded/tests/test_provide_loader_config.sh");
25+
26+
assert!(
27+
script.is_file(),
28+
"test_provide_loader_config.sh not found at {}",
29+
script.display(),
30+
);
31+
32+
let output = Command::new("bash")
33+
.arg(&script)
34+
.output()
35+
.expect("failed to invoke bash");
36+
37+
let stdout = String::from_utf8_lossy(&output.stdout);
38+
let stderr = String::from_utf8_lossy(&output.stderr);
39+
40+
// Always print so failures show what the bash runner saw.
41+
println!("--- test_provide_loader_config.sh stdout ---\n{stdout}");
42+
if !stderr.is_empty() {
43+
println!("--- test_provide_loader_config.sh stderr ---\n{stderr}");
44+
}
45+
46+
assert!(
47+
output.status.success(),
48+
"test_provide_loader_config.sh failed with status {:?}",
49+
output.status,
50+
);
51+
}

0 commit comments

Comments
 (0)