1
0
mirror of https://github.com/containers/bootc.git synced 2026-02-05 15:45:53 +01:00

lib: Remove composefs-backend feature gate

While composefs is still experimental, after looking at this
I think the feature gating we're doing has a pretty high "pain:gain"
ratio - in other words, the risk we're mitigating by having it
off is very low.

Since composefs is a focus of development, let's just remove
the feature gate. We have good CI coverage for the non-composefs
case.

Assisted-by: Claude Code (Sonnet 4.5)
Signed-off-by: Colin Walters <walters@verbum.org>
This commit is contained in:
Colin Walters
2025-10-28 08:33:48 -04:00
parent edbfb11a96
commit 706ff868b6
12 changed files with 10 additions and 115 deletions

View File

@@ -74,13 +74,11 @@ similar-asserts = { workspace = true }
static_assertions = { workspace = true }
[features]
default = ["install-to-disk", "composefs-backend"]
default = ["install-to-disk"]
# This feature enables `bootc install to-disk`, which is considered just a "demo"
# or reference installer; we expect most nontrivial use cases to be using
# `bootc install to-filesystem`.
install-to-disk = []
# Enable support for the composefs native backend
composefs-backend = []
# This featuares enables `bootc internals publish-rhsm-facts` to integrate with
# Red Hat Subscription Manager
rhsm = []

View File

@@ -43,8 +43,8 @@ fn delete_type1_entry(depl: &DeploymentEntry, boot_dir: &Dir, deleting_staged: b
// We reuse kernel + initrd if they're the same for two deployments
// We don't want to delete the (being deleted) deployment's kernel + initrd
// if it's in use by any other deployment
let should_del_kernel = match &depl.deployment.boot_digest {
Some(digest) => find_vmlinuz_initrd_duplicates(&digest)?
let should_del_kernel = match depl.deployment.boot_digest.as_ref() {
Some(digest) => find_vmlinuz_initrd_duplicates(digest)?
.is_some_and(|vec| vec.iter().any(|digest| *digest != depl.deployment.verity)),
None => false,
};

View File

@@ -8,7 +8,6 @@ use fn_error_context::context;
use bootc_blockdev::{Partition, PartitionTable};
use bootc_mount as mount;
#[cfg(any(feature = "composefs-backend", feature = "install-to-disk"))]
use crate::bootc_composefs::boot::mount_esp;
use crate::{discoverable_partition_specification, utils};
@@ -72,7 +71,6 @@ pub(crate) fn install_via_bootupd(
}
#[context("Installing bootloader")]
#[cfg(any(feature = "composefs-backend", feature = "install-to-disk"))]
pub(crate) fn install_systemd_boot(
device: &PartitionTable,
_rootfs: &Utf8Path,

View File

@@ -33,9 +33,7 @@ use schemars::schema_for;
use serde::{Deserialize, Serialize};
use tempfile::tempdir_in;
#[cfg(feature = "composefs-backend")]
use crate::bootc_composefs::delete::delete_composefs_deployment;
#[cfg(feature = "composefs-backend")]
use crate::bootc_composefs::{
finalize::{composefs_backend_finalize, get_etc_diff},
rollback::composefs_rollback,
@@ -669,13 +667,12 @@ pub(crate) enum Opt {
#[clap(subcommand)]
#[clap(hide = true)]
Internals(InternalsOpts),
#[cfg(feature = "composefs-backend")]
ComposefsFinalizeStaged,
#[cfg(feature = "composefs-backend")]
/// Diff current /etc configuration versus default
ConfigDiff,
#[cfg(feature = "composefs-backend")]
DeleteDeployment { depl_id: String },
DeleteDeployment {
depl_id: String,
},
}
/// Ensure we've entered a mount namespace, so that we can remount
@@ -1267,38 +1264,26 @@ async fn run_from_opt(opt: Opt) -> Result<()> {
let root = &Dir::open_ambient_dir("/", cap_std::ambient_authority())?;
match opt {
Opt::Upgrade(opts) => {
#[cfg(feature = "composefs-backend")]
if composefs_booted()?.is_some() {
upgrade_composefs(opts).await
} else {
upgrade(opts).await
}
#[cfg(not(feature = "composefs-backend"))]
upgrade(opts).await
}
Opt::Switch(opts) => {
#[cfg(feature = "composefs-backend")]
if composefs_booted()?.is_some() {
switch_composefs(opts).await
} else {
switch(opts).await
}
#[cfg(not(feature = "composefs-backend"))]
switch(opts).await
}
Opt::Rollback(opts) => {
#[cfg(feature = "composefs-backend")]
if composefs_booted()?.is_some() {
composefs_rollback().await?
} else {
rollback(&opts).await?
}
#[cfg(not(feature = "composefs-backend"))]
rollback(&opts).await?;
if opts.apply {
crate::reboot::reboot()?;
}
@@ -1307,15 +1292,11 @@ async fn run_from_opt(opt: Opt) -> Result<()> {
}
Opt::Edit(opts) => edit(opts).await,
Opt::UsrOverlay => {
#[cfg(feature = "composefs-backend")]
if composefs_booted()?.is_some() {
composefs_usr_overlay()
} else {
usroverlay().await
}
#[cfg(not(feature = "composefs-backend"))]
usroverlay().await
}
Opt::Container(opts) => match opts {
ContainerOpts::Lint {
@@ -1608,13 +1589,10 @@ async fn run_from_opt(opt: Opt) -> Result<()> {
}
},
#[cfg(feature = "composefs-backend")]
Opt::ComposefsFinalizeStaged => composefs_backend_finalize().await,
#[cfg(feature = "composefs-backend")]
Opt::ConfigDiff => get_etc_diff().await,
#[cfg(feature = "composefs-backend")]
Opt::DeleteDeployment { depl_id } => delete_composefs_deployment(&depl_id).await,
}
}

View File

@@ -53,7 +53,6 @@ use serde::{Deserialize, Serialize};
#[cfg(feature = "install-to-disk")]
use self::baseline::InstallBlockDeviceOpts;
#[cfg(feature = "composefs-backend")]
use crate::bootc_composefs::{boot::setup_composefs_boot, repo::initialize_composefs_repository};
use crate::boundimage::{BoundImage, ResolvedBoundImage};
use crate::containerenv::ContainerExecutionInfo;
@@ -66,7 +65,6 @@ use crate::task::Task;
use crate::utils::sigpolicy_from_opt;
use bootc_kernel_cmdline::{bytes, utf8, INITRD_ARG_PREFIX, ROOTFLAGS};
use bootc_mount::Filesystem;
#[cfg(feature = "composefs-backend")]
use composefs::fsverity::FsVerityHashValue;
/// The toplevel boot directory
@@ -88,7 +86,6 @@ const SELINUXFS: &str = "/sys/fs/selinux";
pub(crate) const EFIVARFS: &str = "/sys/firmware/efi/efivars";
pub(crate) const ARCH_USES_EFI: bool = cfg!(any(target_arch = "x86_64", target_arch = "aarch64"));
#[cfg(feature = "composefs-backend")]
pub(crate) const EFI_LOADER_INFO: &str = "LoaderInfo-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f";
const DEFAULT_REPO_CONFIG: &[(&str, &str)] = &[
@@ -278,7 +275,6 @@ pub(crate) struct InstallToDiskOpts {
#[clap(flatten)]
#[serde(flatten)]
#[cfg(feature = "composefs-backend")]
pub(crate) composefs_opts: InstallComposefsOpts,
}
@@ -356,7 +352,6 @@ pub(crate) struct InstallToFilesystemOpts {
#[clap(flatten)]
pub(crate) config_opts: InstallConfigOpts,
#[cfg(feature = "composefs-backend")]
#[clap(flatten)]
pub(crate) composefs_opts: InstallComposefsOpts,
}
@@ -391,7 +386,6 @@ pub(crate) struct InstallToExistingRootOpts {
#[clap(default_value = ALONGSIDE_ROOT_MOUNT)]
pub(crate) root_path: Utf8PathBuf,
#[cfg(feature = "composefs-backend")]
#[clap(flatten)]
pub(crate) composefs_opts: InstallComposefsOpts,
}
@@ -434,7 +428,6 @@ pub(crate) struct State {
pub(crate) composefs_required: bool,
// If Some, then --composefs_native is passed
#[cfg(feature = "composefs-backend")]
pub(crate) composefs_options: InstallComposefsOpts,
/// Detected bootloader type for the target system
@@ -565,7 +558,7 @@ impl FromStr for MountSpec {
}
}
#[cfg(all(feature = "install-to-disk", feature = "composefs-backend"))]
#[cfg(feature = "install-to-disk")]
impl InstallToDiskOpts {
pub(crate) fn validate(&self) -> Result<()> {
if !self.composefs_opts.composefs_backend {
@@ -1221,11 +1214,7 @@ async fn verify_target_fetch(
}
fn root_has_uki(root: &Dir) -> Result<bool> {
#[cfg(feature = "composefs-backend")]
return crate::bootc_composefs::boot::container_root_has_uki(root);
#[cfg(not(feature = "composefs-backend"))]
Ok(false)
crate::bootc_composefs::boot::container_root_has_uki(root)
}
/// Preparation for an install; validates and prepares some (thereafter immutable) global state.
@@ -1233,7 +1222,7 @@ async fn prepare_install(
config_opts: InstallConfigOpts,
source_opts: InstallSourceOpts,
target_opts: InstallTargetOpts,
#[cfg(feature = "composefs-backend")] mut composefs_options: InstallComposefsOpts,
mut composefs_options: InstallComposefsOpts,
) -> Result<Arc<State>> {
tracing::trace!("Preparing install");
let rootfs = cap_std::fs::Dir::open_ambient_dir("/", cap_std::ambient_authority())
@@ -1308,7 +1297,6 @@ async fn prepare_install(
tracing::debug!("Composefs required: {composefs_required}");
#[cfg(feature = "composefs-backend")]
if composefs_required {
composefs_options.composefs_backend = true;
}
@@ -1381,7 +1369,6 @@ async fn prepare_install(
// Determine bootloader type for the target system
// Priority: user-specified > bootupd availability > systemd-boot fallback
#[cfg(feature = "composefs-backend")]
let detected_bootloader = {
if let Some(bootloader) = composefs_options.bootloader.clone() {
bootloader
@@ -1393,8 +1380,6 @@ async fn prepare_install(
}
}
};
#[cfg(not(feature = "composefs-backend"))]
let detected_bootloader = crate::spec::Bootloader::Grub;
println!("Bootloader: {detected_bootloader}");
// Create our global (read-only) state which gets wrapped in an Arc
@@ -1413,7 +1398,6 @@ async fn prepare_install(
host_is_container,
composefs_required,
detected_bootloader,
#[cfg(feature = "composefs-backend")]
composefs_options,
});
@@ -1585,7 +1569,6 @@ async fn install_to_filesystem_impl(
}
}
#[cfg(feature = "composefs-backend")]
if state.composefs_options.composefs_backend {
// Load a fd for the mounted target physical root
@@ -1596,9 +1579,6 @@ async fn install_to_filesystem_impl(
ostree_install(state, rootfs, cleanup).await?;
}
#[cfg(not(feature = "composefs-backend"))]
ostree_install(state, rootfs, cleanup).await?;
// Finalize mounted filesystems
if !rootfs.skip_finalize {
let bootfs = rootfs.boot.as_ref().map(|_| ("boot", "boot"));
@@ -1618,7 +1598,6 @@ fn installation_complete() {
#[context("Installing to disk")]
#[cfg(feature = "install-to-disk")]
pub(crate) async fn install_to_disk(mut opts: InstallToDiskOpts) -> Result<()> {
#[cfg(feature = "composefs-backend")]
opts.validate()?;
// Log the disk installation operation to systemd journal
@@ -1667,7 +1646,6 @@ pub(crate) async fn install_to_disk(mut opts: InstallToDiskOpts) -> Result<()> {
opts.config_opts,
opts.source_opts,
opts.target_opts,
#[cfg(feature = "composefs-backend")]
opts.composefs_opts,
)
.await?;
@@ -1905,7 +1883,6 @@ pub(crate) async fn install_to_filesystem(
opts.config_opts,
opts.source_opts,
opts.target_opts,
#[cfg(feature = "composefs-backend")]
opts.composefs_opts,
)
.await?;
@@ -2177,7 +2154,6 @@ pub(crate) async fn install_to_existing_root(opts: InstallToExistingRootOpts) ->
source_opts: opts.source_opts,
target_opts: opts.target_opts,
config_opts: opts.config_opts,
#[cfg(feature = "composefs-backend")]
composefs_opts: opts.composefs_opts,
};

View File

@@ -4,7 +4,6 @@
//! to provide a fully "container native" tool for using
//! bootable container images.
#[cfg(feature = "composefs-backend")]
mod bootc_composefs;
pub(crate) mod bootc_kargs;
mod bootloader;

View File

@@ -27,7 +27,6 @@ use linkme::distributed_slice;
use ostree_ext::ostree_prepareroot;
use serde::Serialize;
#[cfg(feature = "composefs-backend")]
use crate::bootc_composefs::boot::EFI_LINUX;
/// Reference to embedded default baseimage content that should exist.
@@ -770,7 +769,6 @@ fn check_boot(root: &Dir, config: &LintExecutionConfig) -> LintResult {
})
.collect();
let mut entries = entries?;
#[cfg(feature = "composefs-backend")]
{
// Work around https://github.com/containers/composefs-rs/issues/131
let efidir = Utf8Path::new(EFI_LINUX)

View File

@@ -11,7 +11,6 @@ use ostree_ext::{container::OstreeImageReference, oci_spec};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
#[cfg(feature = "composefs-backend")]
use crate::bootc_composefs::boot::BootType;
use crate::{k8sapitypes, status::Slot};
@@ -201,7 +200,6 @@ impl FromStr for Bootloader {
/// A bootable entry
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
#[serde(rename_all = "camelCase")]
#[cfg(feature = "composefs-backend")]
pub struct BootEntryComposefs {
/// The erofs verity
pub verity: String,
@@ -235,7 +233,6 @@ pub struct BootEntry {
/// If this boot entry is ostree based, the corresponding state
pub ostree: Option<BootEntryOstree>,
/// If this boot entry is composefs based, the corresponding state
#[cfg(feature = "composefs-backend")]
pub composefs: Option<BootEntryComposefs>,
}
@@ -272,7 +269,6 @@ pub struct HostStatus {
pub ty: Option<HostType>,
}
#[cfg(feature = "composefs-backend")]
pub(crate) struct DeploymentEntry<'a> {
pub(crate) ty: Option<Slot>,
pub(crate) deployment: &'a BootEntryComposefs,
@@ -315,7 +311,6 @@ impl Host {
}
}
#[cfg(feature = "composefs-backend")]
pub(crate) fn require_composefs_booted(&self) -> anyhow::Result<&BootEntryComposefs> {
let cfs = self
.status
@@ -328,7 +323,6 @@ impl Host {
}
/// Returns all composefs deployments in a list
#[cfg(feature = "composefs-backend")]
#[fn_error_context::context("Getting all composefs deployments")]
pub(crate) fn all_composefs_deployments<'a>(&'a self) -> Result<Vec<DeploymentEntry<'a>>> {
let mut all_deps = vec![];
@@ -636,7 +630,6 @@ mod tests {
pinned: false,
store: None,
ostree: None,
#[cfg(feature = "composefs-backend")]
composefs: None,
}
}

View File

@@ -19,10 +19,8 @@ use ostree_ext::sysroot::SysrootLock;
use ostree_ext::ostree;
#[cfg(feature = "composefs-backend")]
use crate::bootc_composefs::status::{composefs_booted, composefs_deployment_status};
use crate::cli::OutputFormat;
#[cfg(feature = "composefs-backend")]
use crate::spec::BootEntryComposefs;
use crate::spec::ImageStatus;
use crate::spec::{BootEntry, BootOrder, Host, HostSpec, HostStatus, HostType};
@@ -212,7 +210,6 @@ fn boot_entry_from_deployment(
deploy_serial: deployment.deployserial().try_into().unwrap(),
stateroot: deployment.stateroot().into(),
}),
#[cfg(feature = "composefs-backend")]
composefs: None,
};
Ok(r)
@@ -234,7 +231,6 @@ impl BootEntry {
}
}
#[cfg(feature = "composefs-backend")]
pub(crate) fn require_composefs(&self) -> Result<&BootEntryComposefs> {
self.composefs.as_ref().ok_or(anyhow::anyhow!(
"BootEntry is not a composefs native boot entry"
@@ -349,7 +345,6 @@ pub(crate) fn get_status(
Ok((deployments, host))
}
#[cfg(feature = "composefs-backend")]
async fn get_host() -> Result<Host> {
let host = if ostree_booted()? {
let sysroot = super::cli::get_storage().await?;
@@ -366,21 +361,6 @@ async fn get_host() -> Result<Host> {
Ok(host)
}
#[cfg(not(feature = "composefs-backend"))]
async fn get_host() -> Result<Host> {
let host = if ostree_booted()? {
let sysroot = super::cli::get_storage().await?;
let ostree = sysroot.get_ostree()?;
let booted_deployment = ostree.booted_deployment();
let (_deployments, host) = get_status(&ostree, booted_deployment.as_ref())?;
host
} else {
Default::default()
};
Ok(host)
}
/// Implementation of the `bootc status` CLI command.
#[context("Status")]
pub(crate) async fn status(opts: super::cli::StatusOpts) -> Result<()> {
@@ -524,7 +504,6 @@ fn human_render_slot(
writeln!(out, "{digest} ({arch})")?;
// Write the EROFS verity if present
#[cfg(feature = "composefs-backend")]
if let Some(composefs) = &entry.composefs {
write_row_name(&mut out, "Verity", prefix_len)?;
writeln!(out, "{}", composefs.verity)?;
@@ -631,7 +610,6 @@ fn human_render_slot_ostree(
}
/// Output a rendering of a non-container composefs boot entry.
#[cfg(feature = "composefs-backend")]
fn human_render_slot_composefs(
mut out: impl Write,
slot: Slot,
@@ -666,7 +644,6 @@ fn human_readable_output_booted(mut out: impl Write, host: &Host, verbose: bool)
writeln!(out)?;
}
#[cfg(feature = "composefs-backend")]
if let Some(image) = &host_status.image {
human_render_slot(&mut out, Some(slot_name), host_status, image, verbose)?;
} else if let Some(ostree) = host_status.ostree.as_ref() {
@@ -682,21 +659,6 @@ fn human_readable_output_booted(mut out: impl Write, host: &Host, verbose: bool)
} else {
writeln!(out, "Current {slot_name} state is unknown")?;
}
#[cfg(not(feature = "composefs-backend"))]
if let Some(image) = &host_status.image {
human_render_slot(&mut out, Some(slot_name), host_status, image, verbose)?;
} else if let Some(ostree) = host_status.ostree.as_ref() {
human_render_slot_ostree(
&mut out,
Some(slot_name),
host_status,
&ostree.checksum,
verbose,
)?;
} else {
writeln!(out, "Current {slot_name} state is unknown")?;
}
}
}

View File

@@ -37,7 +37,6 @@ use crate::utils::deployment_fd;
/// See https://github.com/containers/composefs-rs/issues/159
pub type ComposefsRepository =
composefs::repository::Repository<composefs::fsverity::Sha512HashValue>;
#[cfg(feature = "composefs-backend")]
pub type ComposefsFilesystem = composefs::tree::FileSystem<composefs::fsverity::Sha512HashValue>;
/// Path to the physical root

View File

@@ -1,7 +1,6 @@
use std::future::Future;
use std::io::Write;
use std::os::fd::BorrowedFd;
#[cfg(feature = "composefs-backend")]
use std::path::{Component, Path, PathBuf};
use std::process::Command;
use std::time::Duration;
@@ -202,7 +201,6 @@ pub(crate) fn digested_pullspec(image: &str, digest: &str) -> String {
format!("{image}@{digest}")
}
#[cfg(feature = "composefs-backend")]
#[derive(Debug)]
pub enum EfiError {
SystemNotUEFI,
@@ -213,14 +211,12 @@ pub enum EfiError {
Io(std::io::Error),
}
#[cfg(feature = "composefs-backend")]
impl From<std::io::Error> for EfiError {
fn from(e: std::io::Error) -> Self {
EfiError::Io(e)
}
}
#[cfg(feature = "composefs-backend")]
pub fn read_uefi_var(var_name: &str) -> Result<String, EfiError> {
use crate::install::EFIVARFS;
use cap_std_ext::cap_std::ambient_authority;
@@ -262,7 +258,6 @@ pub fn read_uefi_var(var_name: &str) -> Result<String, EfiError> {
/// Computes a relative path from `from` to `to`.
///
/// Both `from` and `to` must be absolute paths.
#[cfg(feature = "composefs-backend")]
pub(crate) fn path_relative_to(from: &Path, to: &Path) -> Result<PathBuf> {
if !from.is_absolute() || !to.is_absolute() {
anyhow::bail!("Paths must be absolute");
@@ -321,7 +316,6 @@ mod tests {
}
#[test]
#[cfg(feature = "composefs-backend")]
fn test_relative_path() {
let from = Path::new("/sysroot/state/deploy/image_id");
let to = Path::new("/sysroot/state/os/default/var");

View File

@@ -9,7 +9,7 @@ Tracking issue: <https://github.com/bootc-dev/bootc/issues/1190>
The composefs backend is an experimental alternative storage backend that uses [composefs-rs](https://github.com/containers/composefs-rs) instead of ostree for storing and managing bootc system deployments.
**Status**: Experimental. The composefs backend is under active development and not yet suitable for production use. The feature is currently gated behind the `composefs-backend` compile-time feature flag, which in current git main is enabled by default.
**Status**: Experimental. The composefs backend is under active development and not yet suitable for production use. The feature is always compiled in as of bootc v1.10.1.
## Key Benefits