diff --git a/crates/lib/src/bootc_composefs/status.rs b/crates/lib/src/bootc_composefs/status.rs index ac1b261dd..49a6349c8 100644 --- a/crates/lib/src/bootc_composefs/status.rs +++ b/crates/lib/src/bootc_composefs/status.rs @@ -36,7 +36,8 @@ use bootc_utils::try_deserialize_timestamp; use cap_std_ext::{cap_std::fs::Dir, dirext::CapStdExtDirExt}; use ostree_container::OstreeImageReference; use ostree_ext::container::{self as ostree_container}; -use ostree_ext::containers_image_proxy; +use ostree_ext::containers_image_proxy::{ImageProxy, ImageReference}; + use ostree_ext::oci_spec; use ostree_ext::{container::deploy::ORIGIN_CONTAINER, oci_spec::image::ImageConfiguration}; @@ -379,14 +380,16 @@ pub(crate) fn list_bootloader_entries(storage: &Storage) -> Result Result { let mut config = crate::deploy::new_proxy_config(); - ostree_ext::container::merge_default_container_proxy_opts(&mut config)?; - let proxy = containers_image_proxy::ImageProxy::new_with_config(config).await?; + + ostree_ext::container::apply_container_proxy_opts_for_transport(&mut config, imgref.transport)?; + + let proxy = ImageProxy::new_with_config(config).await?; let img = proxy - .open_image(&imgref) + .open_image_ref(&imgref) .await .with_context(|| format!("Opening image {imgref}"))?; diff --git a/crates/lib/src/bootc_composefs/update.rs b/crates/lib/src/bootc_composefs/update.rs index a1fb2722f..79c823159 100644 --- a/crates/lib/src/bootc_composefs/update.rs +++ b/crates/lib/src/bootc_composefs/update.rs @@ -58,7 +58,7 @@ pub(crate) async fn is_image_pulled( imgref: &ImageReference, ) -> Result<(Option, ImgConfigManifest)> { let imgref_repr = imgref.to_image_proxy_ref()?; - let img_config_manifest = get_container_manifest_and_config(&imgref_repr.to_string()).await?; + let img_config_manifest = get_container_manifest_and_config(&imgref_repr).await?; let img_digest = img_config_manifest.manifest.config().digest().digest(); diff --git a/crates/lib/src/cli.rs b/crates/lib/src/cli.rs index dd80fd6ad..9a7c307ff 100644 --- a/crates/lib/src/cli.rs +++ b/crates/lib/src/cli.rs @@ -442,6 +442,13 @@ pub(crate) enum ContainerOpts { #[clap(long)] write_dumpfile_to: Option, + /// The directory containing the kernel and initramfs.img + /// Must be of the format /parent/$kernel_version + /// + /// Ex. /boot/6.18.7-100.fc42.x86_64 + #[clap(long)] + kernel_dir: Option, + /// Additional arguments to pass to ukify (after `--`). #[clap(last = true)] args: Vec, @@ -1902,12 +1909,36 @@ async fn run_from_opt(opt: Opt) -> Result<()> { kargs, allow_missing_verity, write_dumpfile_to, + kernel_dir, args, } => { + let kernel = match kernel_dir { + Some(kernel_dir) => { + let kver = kernel_dir + .components() + .last() + .ok_or_else(|| anyhow::anyhow!("Could not determine kernel version"))?; + + Some(crate::kernel::KernelInternal { + kernel: crate::kernel::Kernel { + unified: false, + version: kver.to_string(), + }, + k_type: crate::kernel::KernelType::Vmlinuz { + path: kernel_dir.join("vmlinuz"), + initramfs: kernel_dir.join("initramfs.img"), + }, + }) + } + + None => None, + }; + crate::ukify::build_ukify( &rootfs, &kargs, &args, + kernel, allow_missing_verity, write_dumpfile_to.as_deref(), ) diff --git a/crates/lib/src/install.rs b/crates/lib/src/install.rs index c9fcaf88c..d49c6a350 100644 --- a/crates/lib/src/install.rs +++ b/crates/lib/src/install.rs @@ -2017,8 +2017,7 @@ async fn install_to_filesystem_impl( // Pre-flight disk space check for native composefs install path. { let imgref = &state.source.imageref; - let imgref_repr = imgref.to_string(); - let img_manifest_config = get_container_manifest_and_config(&imgref_repr).await?; + let img_manifest_config = get_container_manifest_and_config(&imgref).await?; crate::store::ensure_composefs_dir(&rootfs.physical_root)?; // Use init_path since the repo may not exist yet during install let (cfs_repo, _created) = crate::store::ComposefsRepository::init_path( diff --git a/crates/lib/src/ukify.rs b/crates/lib/src/ukify.rs index 265b867c4..523836217 100644 --- a/crates/lib/src/ukify.rs +++ b/crates/lib/src/ukify.rs @@ -15,6 +15,7 @@ use fn_error_context::context; use crate::bootc_composefs::digest::compute_composefs_digest; use crate::bootc_composefs::status::ComposefsCmdline; +use crate::kernel::KernelInternal; /// Build a UKI from the given rootfs. /// @@ -30,6 +31,7 @@ pub(crate) async fn build_ukify( rootfs: &Utf8Path, extra_kargs: &[String], args: &[OsString], + kernel: Option, allow_missing_fsverity: bool, write_dumpfile_to: Option<&Utf8Path>, ) -> Result<()> { @@ -52,12 +54,14 @@ pub(crate) async fn build_ukify( let root = Dir::open_ambient_dir(rootfs, cap_std_ext::cap_std::ambient_authority()) .with_context(|| format!("Opening rootfs {rootfs}"))?; - // Find the kernel - let kernel = crate::kernel::find_kernel(&root)? - .ok_or_else(|| anyhow::anyhow!("No kernel found in {rootfs}"))?; + let kernel_final = match kernel { + Some(ref kernel) => kernel, + None => &crate::kernel::find_kernel(&root)? + .ok_or_else(|| anyhow::anyhow!("No kernel found in {rootfs}"))?, + }; // Extract vmlinuz and initramfs paths, or bail if this is already a UKI - let (vmlinuz_path, initramfs_path) = match kernel.k_type { + let (vmlinuz_path, initramfs_path) = match &kernel_final.k_type { crate::kernel::KernelType::Vmlinuz { path, initramfs } => (path, initramfs), crate::kernel::KernelType::Uki { path, .. } => { anyhow::bail!("Cannot build UKI: rootfs already contains a UKI at {path}"); @@ -65,17 +69,31 @@ pub(crate) async fn build_ukify( }; // Verify kernel and initramfs exist - if !root - .try_exists(&vmlinuz_path) - .context("Checking for vmlinuz")? - { - anyhow::bail!("Kernel not found at {vmlinuz_path}"); - } - if !root - .try_exists(&initramfs_path) - .context("Checking for initramfs")? - { - anyhow::bail!("Initramfs not found at {initramfs_path}"); + // + // NOTE: Not using cap_std here as the vmlinuz/initramfs path from + // args can be outside of "rootfs" + if kernel.is_some() { + if !vmlinuz_path.exists() { + anyhow::bail!("Kernel not found at {vmlinuz_path}"); + } + + if !initramfs_path.exists() { + anyhow::bail!("Initramfs not found at {initramfs_path}"); + } + } else { + if !root + .try_exists(&vmlinuz_path) + .context("Checking for vmlinuz")? + { + anyhow::bail!("Kernel not found at {vmlinuz_path}"); + } + + if !root + .try_exists(&initramfs_path) + .context("Checking for initramfs")? + { + anyhow::bail!("Initramfs not found at {initramfs_path}"); + } } // Compute the composefs digest @@ -105,7 +123,7 @@ pub(crate) async fn build_ukify( .arg("--initrd") .arg(&initramfs_path) .arg("--uname") - .arg(&kernel.kernel.version) + .arg(&kernel_final.kernel.version) .arg("--cmdline") .arg(&cmdline_str) .arg("--os-release") @@ -134,7 +152,7 @@ mod tests { let tempdir = tempfile::tempdir().unwrap(); let path = Utf8Path::from_path(tempdir.path()).unwrap(); - let result = build_ukify(path, &[], &[], false, None).await; + let result = build_ukify(path, &[], &[], None, false, None).await; assert!(result.is_err()); let err = format!("{:#}", result.unwrap_err()); assert!( @@ -156,7 +174,7 @@ mod tests { ) .unwrap(); - let result = build_ukify(path, &[], &[], false, None).await; + let result = build_ukify(path, &[], &[], None, false, None).await; assert!(result.is_err()); let err = format!("{:#}", result.unwrap_err()); assert!( diff --git a/crates/ostree-ext/src/container/mod.rs b/crates/ostree-ext/src/container/mod.rs index 3419bdf82..e73361852 100644 --- a/crates/ostree-ext/src/container/mod.rs +++ b/crates/ostree-ext/src/container/mod.rs @@ -496,6 +496,20 @@ pub fn version_for_config(config: &oci_spec::image::ImageConfiguration) -> Optio None } +/// Apply appropriate container proxy options based on transport type +pub fn apply_container_proxy_opts_for_transport( + config: &mut containers_image_proxy::ImageProxyConfig, + transport: Transport, +) -> Result<()> { + if transport == Transport::ContainerStorage { + // Fetching from containers-storage, may require privileges to read files + merge_default_container_proxy_opts_with_isolation(config, None) + } else { + // Apply our defaults to the proxy config + merge_default_container_proxy_opts(config) + } +} + pub mod deploy; mod encapsulate; pub use encapsulate::*; diff --git a/crates/ostree-ext/src/container/store.rs b/crates/ostree-ext/src/container/store.rs index 8acf5831e..f810bb7ca 100644 --- a/crates/ostree-ext/src/container/store.rs +++ b/crates/ostree-ext/src/container/store.rs @@ -635,13 +635,7 @@ impl ImageImporter { imgref: &OstreeImageReference, mut config: ImageProxyConfig, ) -> Result { - if imgref.imgref.transport == Transport::ContainerStorage { - // Fetching from containers-storage, may require privileges to read files - merge_default_container_proxy_opts_with_isolation(&mut config, None)?; - } else { - // Apply our defaults to the proxy config - merge_default_container_proxy_opts(&mut config)?; - } + apply_container_proxy_opts_for_transport(&mut config, imgref.imgref.transport)?; let proxy = ImageProxy::new_with_config(config).await?; system_repo_journal_print( diff --git a/docs/src/man/bootc-container-ukify.8.md b/docs/src/man/bootc-container-ukify.8.md index bad9e10cc..d98e32589 100644 --- a/docs/src/man/bootc-container-ukify.8.md +++ b/docs/src/man/bootc-container-ukify.8.md @@ -35,6 +35,10 @@ Any additional arguments after `--` are passed through to ukify unchanged. Write a dumpfile to this path +**--kernel-dir**=*KERNEL_DIR* + + The directory containing the kernel and initramfs.img Must be of the format /parent/$kernel_version + # EXAMPLES diff --git a/tmt/plans/integration.fmf b/tmt/plans/integration.fmf index b3366648b..9830acd87 100644 --- a/tmt/plans/integration.fmf +++ b/tmt/plans/integration.fmf @@ -217,6 +217,7 @@ execute: how: fmf test: - /tmt/tests/tests/test-37-install-no-boot-dir + extra-fixme_skip_if_composefs: true /plan-37-upgrade-check-status: summary: Verify upgrade --check populates cached update in status @@ -232,6 +233,7 @@ execute: how: fmf test: - /tmt/tests/tests/test-38-install-bootloader-none + extra-fixme_skip_if_composefs: true /plan-39-upgrade-tag: summary: Test bootc upgrade --tag functionality with containers-storage diff --git a/tmt/tests/booted/test-install-bootloader-none.nu b/tmt/tests/booted/test-install-bootloader-none.nu index ef89deae7..ab36260e6 100644 --- a/tmt/tests/booted/test-install-bootloader-none.nu +++ b/tmt/tests/booted/test-install-bootloader-none.nu @@ -2,7 +2,10 @@ # tmt: # summary: Test bootc install with --bootloader=none # duration: 30m -# +# extra: +# # bootloader=none is not supported for composefs +# fixme_skip_if_composefs: true + use std assert use tap.nu diff --git a/tmt/tests/booted/test-install-no-boot-dir.nu b/tmt/tests/booted/test-install-no-boot-dir.nu index bc012fd66..82494abb5 100644 --- a/tmt/tests/booted/test-install-no-boot-dir.nu +++ b/tmt/tests/booted/test-install-no-boot-dir.nu @@ -2,7 +2,12 @@ # tmt: # summary: Test bootc install to-filesystem without /boot directory # duration: 30m -# +# extra: +# # bootloader=none is not supported for composefs and this test fails +# # when trying to install bootloader for composefs. For ostree, the +# # bootloader installation is simply skipped +# fixme_skip_if_composefs: true + use std assert use tap.nu @@ -19,7 +24,7 @@ def main [] { mount -o loop disk.img /var/mnt setenforce 0 - + tap run_install $"bootc install to-filesystem --disable-selinux --bootloader=none --source-imgref ($target_image) /var/mnt" umount /var/mnt