Skip to content

Commit d1e50d9

Browse files
mvo5cgwalters
andcommitted
cli: add install print-configuration --sysroot
We would like to only inspect a container image with {bootc,}image-builder and not actually run the container. One reason is (too much?) paranoia, i.e. we want to eventually support bootc containers coming from the users that get passed into the service and not having to run anything on the container initially to generate the osbuild manifest minimizes the risk. So to still get the require parameters like preferred rootfs or kargs we would like to run our own bootc and then pass ``` bootc install print-configuration --sysroot /path/to/mounted/cnt ``` to generate the manifest. The actual build will still run the `boots install to-filesystem` from the container. But that step happens in an isolated instance that is destroyed after each build (we already do this for package based image builds as users can also inject custom content/rpms here). It also tweaks print_configuration to make it easier to test. With that we could drop parts of the tests for PR#1820 from the container.rs and move them in here. Signed-off-by: Michael Vogt <michael.vogt@gmail.com> Co-authored-by: Colin Walters <walters@verbum.org>
1 parent 3efcbdd commit d1e50d9

File tree

3 files changed

+80
-6
lines changed

3 files changed

+80
-6
lines changed

crates/lib/src/install.rs

Lines changed: 62 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use std::collections::HashMap;
1818
use std::io::Write;
1919
use std::os::fd::{AsFd, AsRawFd};
2020
use std::os::unix::process::CommandExt;
21-
use std::path::Path;
21+
use std::path::{Path, PathBuf};
2222
use std::process;
2323
use std::process::Command;
2424
use std::str::FromStr;
@@ -453,6 +453,13 @@ pub(crate) struct InstallPrintConfigurationOpts {
453453
/// Print configuration that is usually handled internally, like kargs.
454454
#[clap(long)]
455455
pub(crate) all: bool,
456+
457+
/// Set an alternative root filesystem.
458+
///
459+
/// This can be used as part of a multi-stage build operation to detect
460+
/// the desired configuration before executing code from a target container image.
461+
#[clap(long, default_value = "/")]
462+
pub(crate) sysroot: PathBuf,
456463
}
457464

458465
/// Global state captured from the container.
@@ -742,12 +749,19 @@ impl SourceInfo {
742749
}
743750

744751
pub(crate) fn print_configuration(opts: InstallPrintConfigurationOpts) -> Result<()> {
745-
let mut install_config = config::load_config()?.unwrap_or_default();
752+
let stdout = std::io::stdout().lock();
753+
print_configuration_to_writer(opts, stdout)
754+
}
755+
756+
fn print_configuration_to_writer<W: Write>(
757+
opts: InstallPrintConfigurationOpts,
758+
writer: W,
759+
) -> Result<()> {
760+
let mut install_config = config::load_config_at(&opts.sysroot)?.unwrap_or_default();
746761
if !opts.all {
747762
install_config.filter_to_external();
748763
}
749-
let stdout = std::io::stdout().lock();
750-
anyhow::Ok(install_config.to_canon_json_writer(stdout)?)
764+
anyhow::Ok(install_config.to_canon_json_writer(writer)?)
751765
}
752766

753767
#[context("Creating ostree deployment")]
@@ -2517,6 +2531,8 @@ pub(crate) async fn install_finalize(target: &Utf8Path) -> Result<()> {
25172531
#[cfg(test)]
25182532
mod tests {
25192533
use super::*;
2534+
use std::fs;
2535+
use tempfile::tempdir;
25202536

25212537
#[test]
25222538
fn install_opts_serializable() {
@@ -2721,4 +2737,46 @@ UUID=boot-uuid /boot ext4 defaults 0 0
27212737

27222738
Ok(())
27232739
}
2740+
2741+
#[test]
2742+
fn test_print_configuration_with_root_dir() -> Result<()> {
2743+
use crate::install::config::{
2744+
Filesystem, InstallConfiguration, InstallConfigurationToplevel,
2745+
};
2746+
2747+
let temp_dir = tempdir()?;
2748+
let root_path = temp_dir.path();
2749+
2750+
let config_dir = root_path.join("etc/bootc/install");
2751+
fs::create_dir_all(&config_dir)?;
2752+
let config_path = config_dir.join("10-install.toml");
2753+
2754+
let test_config = InstallConfigurationToplevel {
2755+
install: Some(InstallConfiguration {
2756+
root_fs_type: Some(Filesystem::Xfs),
2757+
kargs: Some(vec!["quiet".to_string(), "karg2=2".to_string()]),
2758+
..Default::default()
2759+
}),
2760+
};
2761+
let toml_content = toml::to_string(&test_config)?;
2762+
fs::write(config_path, toml_content)?;
2763+
2764+
let opts = InstallPrintConfigurationOpts {
2765+
sysroot: root_path.to_owned(),
2766+
all: true,
2767+
};
2768+
2769+
let mut buffer = Vec::new();
2770+
print_configuration_to_writer(opts, &mut buffer)?;
2771+
2772+
let output_json = String::from_utf8(buffer)?;
2773+
let output_config: crate::install::config::InstallConfiguration =
2774+
serde_json::from_str(&output_json)?;
2775+
2776+
let install_config = test_config.install.unwrap();
2777+
assert_eq!(install_config.kargs, output_config.kargs);
2778+
assert_eq!(install_config.root_fs_type, output_config.root_fs_type);
2779+
2780+
Ok(())
2781+
}
27242782
}

crates/lib/src/install/config.rs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use anyhow::{Context, Result};
66
use clap::ValueEnum;
77
use fn_error_context::context;
88
use serde::{Deserialize, Serialize};
9+
use std::path::Path;
910

1011
#[cfg(feature = "install-to-disk")]
1112
use super::baseline::BlockSetup;
@@ -191,11 +192,20 @@ impl InstallConfiguration {
191192
#[context("Loading configuration")]
192193
/// Load the install configuration, merging all found configuration files.
193194
pub(crate) fn load_config() -> Result<Option<InstallConfiguration>> {
195+
load_config_at("/")
196+
}
197+
198+
pub(crate) fn load_config_at(root_dir: impl AsRef<Path>) -> Result<Option<InstallConfiguration>> {
194199
let env = EnvProperties {
195200
sys_arch: std::env::consts::ARCH.to_string(),
196201
};
197-
const SYSTEMD_CONVENTIONAL_BASES: &[&str] = &["/usr/lib", "/usr/local/lib", "/etc", "/run"];
198-
let fragments = liboverdrop::scan(SYSTEMD_CONVENTIONAL_BASES, "bootc/install", &["toml"], true);
202+
let root_dir = root_dir.as_ref();
203+
const SYSTEMD_CONVENTIONAL_BASES: &[&str] = &["usr/lib", "usr/local/lib", "etc", "run"];
204+
let systemd_conventional_bases = SYSTEMD_CONVENTIONAL_BASES
205+
.iter()
206+
.map(|v| root_dir.join(v))
207+
.collect::<Vec<_>>();
208+
let fragments = liboverdrop::scan(systemd_conventional_bases, "bootc/install", &["toml"], true);
199209
let mut config: Option<InstallConfiguration> = None;
200210
for (_name, path) in fragments {
201211
let buf = std::fs::read_to_string(&path)?;

docs/src/man/bootc-install-print-configuration.8.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,12 @@ string-valued filesystem name suitable for passing to `mkfs.\$type`.
2626

2727
Print all configuration
2828

29+
**--sysroot**=*SYSROOT*
30+
31+
Set an alternative root filesystem
32+
33+
Default: /
34+
2935
<!-- END GENERATED OPTIONS -->
3036

3137
# VERSION

0 commit comments

Comments
 (0)