diff --git a/docs/src/bootc-install.md b/docs/src/bootc-install.md index b82c0eda..a7a6e57c 100644 --- a/docs/src/bootc-install.md +++ b/docs/src/bootc-install.md @@ -113,14 +113,7 @@ taking precedence. If for example you are building a derived container image fr you could create a `50-myos.toml` that sets `type = "btrfs"` which will override the prior setting. -Other available options, also under the `[install]` section: - -`kargs`: This allows setting kernel arguments which apply only at the time of `bootc install`. -This option is particularly useful when creating derived/layered images; for example, a cloud -image may want to have its default `console=` set, in contrast with a default base image. -The values in this field are space separated. - -`root-fs-type`: This value is the same as `install.filesystem.root.type`. +For other available options, see [bootc-install-config](man/bootc-install-config.md). ## Installing an "unconfigured" image diff --git a/docs/src/man-md/bootc-install-config.md b/docs/src/man-md/bootc-install-config.md index 351493a8..9fd4e515 100644 --- a/docs/src/man-md/bootc-install-config.md +++ b/docs/src/man-md/bootc-install-config.md @@ -20,6 +20,9 @@ This is the only defined toplevel table. The `install`` section supports two subfields: +- `block`: An array of supported `to-disk` backends enabled by this base container image; + if not specified, this will just be `direct`. The only other supported value is `tpm2-luks`. + The first value specified will be the default. To enable both, use `block = ["direct", "tpm2-luks"]`. - `filesystem`: See below. - `kargs`: An array of strings; this will be appended to the set of kernel arguments. diff --git a/lib/src/install/baseline.rs b/lib/src/install/baseline.rs index 2e0bd072..21e69a84 100644 --- a/lib/src/install/baseline.rs +++ b/lib/src/install/baseline.rs @@ -79,9 +79,8 @@ pub(crate) struct InstallBlockDeviceOpts { /// /// direct: Filesystem written directly to block device /// tpm2-luks: Bind unlock of filesystem to presence of the default tpm2 device. - #[clap(long, value_enum, default_value_t)] - #[serde(default)] - pub(crate) block_setup: BlockSetup, + #[clap(long, value_enum)] + pub(crate) block_setup: Option, /// Target root filesystem type. #[clap(long, value_enum)] @@ -297,7 +296,10 @@ pub(crate) fn install_create_rootfs( }; let base_rootdev = findpart(ROOTPN)?; - let (rootdev, root_blockdev_kargs) = match opts.block_setup { + let block_setup = state + .install_config + .get_block_setup(opts.block_setup.as_ref().copied())?; + let (rootdev, root_blockdev_kargs) = match block_setup { BlockSetup::Direct => (base_rootdev, None), BlockSetup::Tpm2Luks => { let uuid = uuid::Uuid::new_v4().to_string(); @@ -394,7 +396,7 @@ pub(crate) fn install_create_rootfs( mount::mount(espdev, &efifs_path)?; } - let luks_device = match opts.block_setup { + let luks_device = match block_setup { BlockSetup::Direct => None, BlockSetup::Tpm2Luks => Some(luks_name.to_string()), }; diff --git a/lib/src/install/config.rs b/lib/src/install/config.rs index 1e8826fd..649a4ecd 100644 --- a/lib/src/install/config.rs +++ b/lib/src/install/config.rs @@ -6,6 +6,8 @@ use anyhow::{Context, Result}; use fn_error_context::context; use serde::{Deserialize, Serialize}; +use super::baseline::BlockSetup; + /// The toplevel config entry for installation configs stored /// in bootc/install (e.g. /etc/bootc/install/05-custom.toml) #[derive(Debug, Clone, Serialize, Deserialize, Default)] @@ -39,6 +41,8 @@ pub(crate) struct BasicFilesystems { pub(crate) struct InstallConfiguration { /// Root filesystem type pub(crate) root_fs_type: Option, + /// Enabled block storage configurations + pub(crate) block: Option>, pub(crate) filesystem: Option, /// Kernel arguments, applied at installation time #[serde(skip_serializing_if = "Option::is_none")] @@ -93,6 +97,7 @@ impl Mergeable for InstallConfiguration { /// Apply any values in other, overriding any existing values in `self`. fn merge(&mut self, other: Self) { merge_basic(&mut self.root_fs_type, other.root_fs_type); + merge_basic(&mut self.block, other.block); self.filesystem.merge(other.filesystem); if let Some(other_kargs) = other.kargs { self.kargs @@ -103,8 +108,8 @@ impl Mergeable for InstallConfiguration { } impl InstallConfiguration { - /// Some fields can be specified multiple ways. This synchronizes the values of the fields - /// to ensure they're the same. + /// Set defaults (e.g. `block`), and also handle fields that can be specified multiple ways + /// by synchronizing the values of the fields to ensure they're the same. /// /// - install.root-fs-type is synchronized with install.filesystems.root.type; if /// both are set, then the latter takes precedence @@ -117,6 +122,10 @@ impl InstallConfiguration { let root = fs.root.get_or_insert_with(Default::default); root.fstype = Some(*rootfs); } + + if self.block.is_none() { + self.block = Some(vec![BlockSetup::Direct]); + } } /// Convenience helper to access the root filesystem @@ -128,6 +137,22 @@ impl InstallConfiguration { pub(crate) fn filter_to_external(&mut self) { self.kargs.take(); } + + pub(crate) fn get_block_setup(&self, default: Option) -> Result { + let valid_block_setups = self + .block + .as_ref() + .map(|v| v.as_slice()) + .unwrap_or_default(); + let default_block = valid_block_setups.iter().next().ok_or_else(|| { + anyhow::anyhow!("Empty block storage configuration in install configuration") + })?; + let block_setup = default.as_ref().unwrap_or(default_block); + if !valid_block_setups.contains(block_setup) { + anyhow::bail!("Block setup {block_setup:?} is not enabled in installation config"); + } + Ok(*block_setup) + } } #[context("Loading configuration")] @@ -257,3 +282,40 @@ type = "xfs" Filesystem::Ext4 ); } + +#[test] +fn test_parse_block() { + let c: InstallConfigurationToplevel = toml::from_str( + r##"[install.filesystem.root] +type = "xfs" +"##, + ) + .unwrap(); + let mut install = c.install.unwrap(); + // Verify the default (but note canonicalization mutates) + { + let mut install = install.clone(); + install.canonicalize(); + assert_eq!(install.get_block_setup(None).unwrap(), BlockSetup::Direct); + } + let other = InstallConfigurationToplevel { + install: Some(InstallConfiguration { + block: Some(vec![]), + ..Default::default() + }), + }; + install.merge(other.install.unwrap()); + // Should be set, but zero length + assert_eq!(install.block.as_ref().unwrap().len(), 0); + assert!(install.get_block_setup(None).is_err()); + + let c: InstallConfigurationToplevel = toml::from_str( + r##"[install] +block = ["tpm2-luks"]"##, + ) + .unwrap(); + let mut install = c.install.unwrap(); + install.canonicalize(); + assert_eq!(install.block.as_ref().unwrap().len(), 1); + assert_eq!(install.get_block_setup(None).unwrap(), BlockSetup::Tpm2Luks); +}