Skip to content
Draft
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
4 changes: 2 additions & 2 deletions crates/lib/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,7 @@ pub(crate) enum InstallOpts {
///
/// At the current time, the only output key is `root-fs-type` which is a string-valued
/// filesystem name suitable for passing to `mkfs.$type`.
PrintConfiguration,
PrintConfiguration(crate::install::InstallPrintConfigurationOpts),
}

/// Subcommands which can be executed as part of a container build.
Expand Down Expand Up @@ -1483,7 +1483,7 @@ async fn run_from_opt(opt: Opt) -> Result<()> {
crate::install::install_to_existing_root(opts).await
}
InstallOpts::Reset(opts) => crate::install::install_reset(opts).await,
InstallOpts::PrintConfiguration => crate::install::print_configuration(),
InstallOpts::PrintConfiguration(opts) => crate::install::print_configuration(opts),
InstallOpts::EnsureCompletion {} => {
let rootfs = &Dir::open_ambient_dir("/", cap_std::ambient_authority())?;
crate::install::completion::run_from_anaconda(rootfs).await
Expand Down
75 changes: 71 additions & 4 deletions crates/lib/src/install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -437,6 +437,19 @@ pub(crate) struct InstallResetOpts {
karg: Option<Vec<CmdlineOwned>>,
}

#[derive(Debug, clap::Parser, PartialEq, Eq)]
pub(crate) struct InstallPrintConfigurationOpts {
/// Print all configuration.
///
/// Print configuration that is usually handled internally, like kargs.
#[clap(long)]
pub(crate) all: bool,

/// Set an alternative rootdir
#[clap(long, default_value = "/")]
pub(crate) root_dir: Option<String>,
Copy link
Collaborator

Choose a reason for hiding this comment

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

To have default_value work just drop the Option

}

/// Global state captured from the container.
#[derive(Debug, Clone)]
pub(crate) struct SourceInfo {
Expand Down Expand Up @@ -722,11 +735,21 @@ impl SourceInfo {
}
}

pub(crate) fn print_configuration() -> Result<()> {
let mut install_config = config::load_config()?.unwrap_or_default();
install_config.filter_to_external();
pub(crate) fn print_configuration(opts: InstallPrintConfigurationOpts) -> Result<()> {
let stdout = std::io::stdout().lock();
anyhow::Ok(install_config.to_canon_json_writer(stdout)?)
print_configuration_to_writer(opts, stdout)
}

fn print_configuration_to_writer<W: Write>(
opts: InstallPrintConfigurationOpts,
writer: W,
) -> Result<()> {
let root_dir = opts.root_dir.unwrap_or("/".to_string());
let mut install_config = config::load_config_at(&root_dir)?.unwrap_or_default();
if !opts.all {
install_config.filter_to_external();
}
anyhow::Ok(install_config.to_canon_json_writer(writer)?)
}

#[context("Creating ostree deployment")]
Expand Down Expand Up @@ -2444,6 +2467,8 @@ pub(crate) async fn install_finalize(target: &Utf8Path) -> Result<()> {
#[cfg(test)]
mod tests {
use super::*;
use std::fs;
use tempfile::tempdir;

#[test]
fn install_opts_serializable() {
Expand Down Expand Up @@ -2573,4 +2598,46 @@ UUID=boot-uuid /boot ext4 defaults 0 0

Ok(())
}

#[test]
fn test_print_configuration_with_root_dir() -> Result<()> {
use crate::install::config::{
Filesystem, InstallConfiguration, InstallConfigurationToplevel,
};

let temp_dir = tempdir()?;
let root_path = temp_dir.path();

let config_dir = root_path.join("etc/bootc/install");
fs::create_dir_all(&config_dir)?;
let config_path = config_dir.join("10-install.toml");

let test_config = InstallConfigurationToplevel {
install: Some(InstallConfiguration {
root_fs_type: Some(Filesystem::Xfs),
kargs: Some(vec!["quiet".to_string(), "karg2=2".to_string()]),
..Default::default()
}),
};
let toml_content = toml::to_string(&test_config)?;
fs::write(config_path, toml_content)?;

let opts = InstallPrintConfigurationOpts {
root_dir: Some(root_path.to_str().unwrap().to_string()),
all: true,
};

let mut buffer = Vec::new();
print_configuration_to_writer(opts, &mut buffer)?;

let output_json = String::from_utf8(buffer)?;
let output_config: crate::install::config::InstallConfiguration =
serde_json::from_str(&output_json)?;

let install_config = test_config.install.unwrap();
assert_eq!(install_config.kargs, output_config.kargs);
assert_eq!(install_config.root_fs_type, output_config.root_fs_type);

Ok(())
}
}
14 changes: 12 additions & 2 deletions crates/lib/src/install/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use anyhow::{Context, Result};
use clap::ValueEnum;
use fn_error_context::context;
use serde::{Deserialize, Serialize};
use std::path::Path;

#[cfg(feature = "install-to-disk")]
use super::baseline::BlockSetup;
Expand Down Expand Up @@ -191,11 +192,20 @@ impl InstallConfiguration {
#[context("Loading configuration")]
/// Load the install configuration, merging all found configuration files.
pub(crate) fn load_config() -> Result<Option<InstallConfiguration>> {
load_config_at("/")
}

pub(crate) fn load_config_at(root_dir: impl AsRef<Path>) -> Result<Option<InstallConfiguration>> {
let env = EnvProperties {
sys_arch: std::env::consts::ARCH.to_string(),
};
const SYSTEMD_CONVENTIONAL_BASES: &[&str] = &["/usr/lib", "/usr/local/lib", "/etc", "/run"];
let fragments = liboverdrop::scan(SYSTEMD_CONVENTIONAL_BASES, "bootc/install", &["toml"], true);
let root_dir = root_dir.as_ref();
const SYSTEMD_CONVENTIONAL_BASES: &[&str] = &["usr/lib", "usr/local/lib", "etc", "run"];
let systemd_conventional_bases = SYSTEMD_CONVENTIONAL_BASES
.iter()
.map(|v| root_dir.join(v))
.collect::<Vec<_>>();
let fragments = liboverdrop::scan(systemd_conventional_bases, "bootc/install", &["toml"], true);
let mut config: Option<InstallConfiguration> = None;
for (_name, path) in fragments {
let buf = std::fs::read_to_string(&path)?;
Expand Down
Loading