Skip to content
Merged
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
3 changes: 3 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,9 @@ fi
# target-base), and so without --bootloader too, which bcvk only takes with it.
if [[ "${variant}" == composefs* ]]; then
printf '[install]\nbootloader = "%s"\n' "${bootloader}" > /usr/lib/bootc/install/80-composefs-bootloader.toml
# The composefs backend doesn't install logically bound images yet:
# https://github.com/bootc-dev/bootc/issues/2540
rm -vf /usr/lib/bootc/bound-images.d/*
fi

if [[ "${boot_type}" == "uki" ]]; then
Expand Down
2 changes: 1 addition & 1 deletion crates/lib/src/boundimage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use crate::store::Storage;

/// The path in a root for bound images; this directory should only contain
/// symbolic links to `.container` or `.image` files.
const BOUND_IMAGE_DIR: &str = "usr/lib/bootc/bound-images.d";
pub(crate) const BOUND_IMAGE_DIR: &str = "usr/lib/bootc/bound-images.d";

/// A subset of data parsed from a `.image` or `.container` file with
/// the minimal information necessary to fetch the image.
Expand Down
78 changes: 78 additions & 0 deletions crates/lib/src/install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -438,6 +438,31 @@ impl InstallComposefsOpts {
}
Ok(())
}

/// The composefs backend doesn't install logically bound images yet
/// (<https://github.com/bootc-dev/bootc/issues/2540>), so fail rather
/// than install a system that is missing them.
pub(crate) fn validate_bound_images(
&self,
bound_images: BoundImagesOpt,
root: &Dir,
) -> Result<()> {
if !self.composefs_backend || bound_images == BoundImagesOpt::Skip {
return Ok(());
}
let images = crate::boundimage::query_bound_images(root)?;
if !images.is_empty() {
let images = images
.iter()
.map(|i| i.image.as_str())
.collect::<Vec<_>>()
.join(", ");
anyhow::bail!(
"Logically bound images are not supported with the composefs backend (found {images}); use --bound-images skip to install without them"
);
}
Ok(())
}
}

#[cfg(feature = "install-to-disk")]
Expand Down Expand Up @@ -1778,6 +1803,8 @@ async fn prepare_install(
composefs_options.composefs_backend |= composefs_required || composefs_default;
composefs_options.validate(config_opts.bootloader.as_ref())?;

composefs_options.validate_bound_images(config_opts.bound_images, &rootfs)?;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should be called from inside of composefs_options.validate, but okay for now


// Read the file eagerly so we error out early, and before the mount changes
// below hide a file bind mounted under e.g. /tmp. We may re-exec further down
// and run this again with those mounts in place, so pass the content to the
Expand Down Expand Up @@ -3168,6 +3195,57 @@ mod tests {
assert_eq!(c.block_opts.device, "/dev/vda");
}

#[test]
fn test_composefs_opts_validate_bound_images() -> Result<()> {
use crate::boundimage::BOUND_IMAGE_DIR;
let unbound = cap_std_ext::cap_tempfile::TempDir::new(cap_std::ambient_authority())?;
unbound.create_dir_all(BOUND_IMAGE_DIR)?;
let bound = cap_std_ext::cap_tempfile::TempDir::new(cap_std::ambient_authority())?;
bound.create_dir_all("usr/share/containers/systemd")?;
bound.write(
"usr/share/containers/systemd/app.image",
"[Image]\nImage=quay.io/example/app:latest\n",
)?;
bound.create_dir_all(BOUND_IMAGE_DIR)?;
bound.symlink_contents(
"/usr/share/containers/systemd/app.image",
format!("{BOUND_IMAGE_DIR}/app.image"),
)?;

// (composefs_backend, bound_images, root binds an image, valid)
let cases = [
(false, BoundImagesOpt::Stored, true, true),
(true, BoundImagesOpt::Stored, false, true),
(true, BoundImagesOpt::Stored, true, false),
(true, BoundImagesOpt::Pull, true, false),
(true, BoundImagesOpt::Skip, true, true),
];
for (composefs_backend, bound_images, binds, valid) in cases {
let opts = InstallComposefsOpts {
composefs_backend,
..Default::default()
};
let root = if binds { &bound } else { &unbound };
assert_eq!(
opts.validate_bound_images(bound_images, root).is_ok(),
valid,
"{opts:?} {bound_images:?} {binds}"
);
}

let opts = InstallComposefsOpts {
composefs_backend: true,
..Default::default()
};
assert_eq!(
opts.validate_bound_images(BoundImagesOpt::Stored, &bound)
.unwrap_err()
.to_string(),
"Logically bound images are not supported with the composefs backend (found quay.io/example/app:latest); use --bound-images skip to install without them"
);
Ok(())
}

#[test]
fn source_fetch_reference_uses_config_id_for_host_images() {
let cases = [
Expand Down
Loading