Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 49 additions & 2 deletions crates/lib/src/lints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -573,12 +573,17 @@ fn check_api_dirs(root: &Dir, _config: &LintExecutionConfig) -> LintResult {
static LINT_COMPOSEFS: Lint = Lint::new_warning(
"baseimage-composefs",
indoc! { r#"
Check that composefs is enabled for ostree. More in
Check that composefs is enabled for ostree. Skipped for composefs-native
images, i.e. those that ship /usr/lib/composefs/setup-root-conf.toml. More in
<https://ostreedev.github.io/ostree/composefs/>.
"#},
check_composefs,
);
fn check_composefs(dir: &Dir, _config: &LintExecutionConfig) -> LintResult {
// ostree's prepare-root.conf is irrelevant for composefs-native images.
if is_composefs_native(dir)? {
return lint_ok();
}
if let Err(e) = check_prepareroot_composefs_norecurse(dir)? {
return Ok(Err(e));
}
Expand All @@ -592,6 +597,20 @@ fn check_composefs(dir: &Dir, _config: &LintExecutionConfig) -> LintResult {
lint_ok()
}

/// The setup-root configuration, relative to the root directory.
fn setup_root_conf_path() -> &'static str {
bootc_initramfs_setup::SETUP_ROOT_CONF_PATH.trim_start_matches('/')
}

/// Whether the image is intended to be deployed only with the composefs
/// backend, which is signaled by the presence of a setup-root configuration
/// file (even if empty).
fn is_composefs_native(root: &Dir) -> Result<bool> {
Ok(root
.symlink_metadata_optional(setup_root_conf_path())?
.is_some())
}

/// Check for a few files and directories we expect in the base image.
fn check_baseimage_root_norecurse(dir: &Dir, _config: &LintExecutionConfig) -> LintResult {
// Check /sysroot
Expand All @@ -604,6 +623,10 @@ fn check_baseimage_root_norecurse(dir: &Dir, _config: &LintExecutionConfig) -> L

// Check /ostree -> sysroot/ostree
let Some(meta) = dir.symlink_metadata_optional("ostree")? else {
// Composefs-native images don't use ostree, so they don't need it.
if is_composefs_native(dir)? {
return lint_ok();
}
return lint_err("Missing ostree -> sysroot/ostree link");
};
if !meta.is_symlink() {
Expand All @@ -624,7 +647,9 @@ static LINT_BASEIMAGE_ROOT: Lint = Lint::new_fatal(
"baseimage-root",
indoc! { r#"
Check that expected files are present in the root of the filesystem; such
as /sysroot and a composefs configuration for ostree. More in
as /sysroot and a composefs configuration for ostree. The /ostree symlink
is not required for composefs-native images, i.e. those that ship
/usr/lib/composefs/setup-root-conf.toml. More in
<https://bootc.dev/bootc/bootc-images.html#standard-image-content>.
"#},
check_baseimage_root,
Expand Down Expand Up @@ -1365,6 +1390,21 @@ mod tests {
drop(td);
let td = passing_fixture()?;
check_baseimage_root(&td, config).unwrap().unwrap();

// Composefs-native images don't need /ostree...
td.remove_file("ostree")?;
assert!(check_baseimage_root(&td, config).unwrap().is_err());
let conf = Utf8Path::new(setup_root_conf_path());
td.create_dir_all(conf.parent().unwrap())?;
td.write(conf, "")?;
check_baseimage_root(&td, config).unwrap().unwrap();
// ...but if they have it, it must still be correct
td.create_dir("ostree")?;
assert!(check_baseimage_root(&td, config).unwrap().is_err());
td.remove_dir("ostree")?;
// ...and they still need /sysroot
td.remove_dir("sysroot")?;
assert!(check_baseimage_root(&td, config).unwrap().is_err());
Ok(())
}

Expand All @@ -1388,6 +1428,13 @@ mod tests {
// Now it should fail because composefs is explicitly disabled.
assert!(check_composefs(&td, config).unwrap().is_err());

// ...unless the image is composefs-native, where ostree's
// configuration doesn't matter.
let conf = Utf8Path::new(setup_root_conf_path());
td.create_dir_all(conf.parent().unwrap())?;
td.write(conf, "")?;
check_composefs(&td, config).unwrap().unwrap();

Ok(())
}

Expand Down
21 changes: 10 additions & 11 deletions crates/tests-integration/src/container.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,17 +114,11 @@ pub(crate) fn test_bootc_upgrade() -> Result<()> {
Ok(())
}

/// This is a single test because it adds a drop-in to /run/bootc/install,
/// which would race with any other test loading the install configuration:
/// libtest runs tests in parallel, and a file that vanishes between the
/// directory scan and the read fails `print-configuration`.
pub(crate) fn test_bootc_install_config() -> Result<()> {
let sh = &xshell::Shell::new()?;
let config = cmd!(sh, "bootc install print-configuration").read()?;
let config: serde_json::Value =
serde_json::from_str(&config).context("Parsing install config")?;
// check that it parses okay, but also ensure kargs is not available here (only via --all)
assert!(config.get("kargs").is_none());
Ok(())
}

pub(crate) fn test_bootc_install_config_all() -> Result<()> {
#[derive(Deserialize)]
#[serde(rename_all = "kebab-case")]
struct TestOstreeConfig {
Expand Down Expand Up @@ -152,6 +146,12 @@ pub(crate) fn test_bootc_install_config_all() -> Result<()> {
}

let sh = &xshell::Shell::new()?;
let config = cmd!(sh, "bootc install print-configuration").read()?;
let config: serde_json::Value =
serde_json::from_str(&config).context("Parsing install config")?;
// check that it parses okay, but also ensure kargs is not available here (only via --all)
assert!(config.get("kargs").is_none());

let config = cmd!(sh, "bootc install print-configuration --all").read()?;
let config: TestInstallConfig =
serde_json::from_str(&config).context("Parsing install config")?;
Expand Down Expand Up @@ -531,7 +531,6 @@ pub(crate) fn run(testargs: libtest_mimic::Arguments) -> Result<()> {
new_test("variant-base-crosscheck", test_variant_base_crosscheck),
new_test("bootc upgrade", test_bootc_upgrade),
new_test("install config", test_bootc_install_config),
new_test("printconfig --all", test_bootc_install_config_all),
new_test("status", test_bootc_status),
new_test("container inspect", test_bootc_container_inspect),
new_test("system-reinstall --help", test_system_reinstall_help),
Expand Down
6 changes: 5 additions & 1 deletion docs/src/bootc-images.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,11 @@ For the composefs backend, the UKI must be located at `/boot/EFI/Linux/$kver.efi

### /ostree symlink and `bootc container lint`

This is [a bug](https://github.com/bootc-dev/bootc/issues/2256): currently a `/ostree -> /sysroot/ostree` symlink is required just for `bootc container lint` to pass, even though it's not required for `/sysroot/ostree` to exist.
`bootc container lint` requires a `/ostree -> sysroot/ostree` symlink,
unless the image is composefs-native, which is signaled by the presence of
`/usr/lib/composefs/setup-root-conf.toml` (it may be empty); see
[bootc-setup-root-conf.toml(5)](man/bootc-setup-root-conf.5.md). Such
images also don't need ostree's `prepare-root.conf` to enable composefs.

## composefs backend

Expand Down
5 changes: 5 additions & 0 deletions docs/src/man/bootc-setup-root-conf.5.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ mounted.

If the file does not exist all options take their documented defaults.

The presence of this file (even if empty) also marks the image as
composefs-native; `bootc container lint` then no longer requires the
`/ostree` symlink or the ostree `prepare-root.conf` composefs configuration
used by the ostree backend.

The `51bootc` dracut module installs this file into the initramfs automatically
when it is present on the host image. Image authors can therefore ship the
file at this path in their container image and rebuild the initramfs with a
Expand Down
Loading