Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

kargs: A few more minor cleanups and test improvements #648

Merged
merged 4 commits into from
Jun 28, 2024
Merged
Changes from 2 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
166 changes: 131 additions & 35 deletions lib/src/kargs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,46 @@ fn get_kargs_in_root(d: &Dir, sys_arch: &str) -> Result<Vec<String>> {
Ok(ret)
}

/// Load kargs.d files from the target ostree commit root
fn get_kargs_from_ostree(
repo: &ostree::Repo,
fetched_tree: &ostree::RepoFile,
sys_arch: &str,
) -> Result<Vec<String>> {
let cancellable = gio::Cancellable::NONE;
let queryattrs = "standard::name,standard::type";
let queryflags = gio::FileQueryInfoFlags::NOFOLLOW_SYMLINKS;
let fetched_iter = fetched_tree.enumerate_children(queryattrs, queryflags, cancellable)?;
let mut ret = Vec::new();
while let Some(fetched_info) = fetched_iter.next_file(cancellable)? {
// only read and parse the file if it is a toml file
let name = fetched_info.name();
let name = if let Some(name) = name.to_str() {
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm totally fine with this change, but I guess I just personally don't like having else statements only containing continue even if it helps with code readability. Once again, okay with this change.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

It's somewhat idiomatic I think in Rust at least.

Although...actually for a while now there's been let-else which I think we can do a tree-wide conversion to.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

➡️ #650

name
} else {
continue;
};
if !name.ends_with(".toml") {
continue;
}
let fetched_child = fetched_iter.child(&fetched_info);
let fetched_child = fetched_child
.downcast::<ostree::RepoFile>()
.expect("downcast");
fetched_child.ensure_resolved()?;
let fetched_contents_checksum = fetched_child.checksum();
let f = ostree::Repo::load_file(repo, fetched_contents_checksum.as_str(), cancellable)?;
let file_content = f.0;
let mut reader =
ostree_ext::prelude::InputStreamExtManual::into_read(file_content.unwrap());
let s = std::io::read_to_string(&mut reader)?;
let parsed_kargs =
parse_kargs_toml(&s, sys_arch).with_context(|| format!("Parsing {name}"))?;
ret.extend(parsed_kargs);
}
Ok(ret)
}

/// Compute the kernel arguments for the new deployment. This starts from the booted
/// karg, but applies the diff between the bootc karg files in /usr/lib/bootc/kargs.d
/// between the booted deployment and the new one.
Expand Down Expand Up @@ -86,33 +126,7 @@ pub(crate) fn get_kargs(
return Ok(kargs);
}

let mut remote_kargs: Vec<String> = vec![];
let queryattrs = "standard::name,standard::type";
let queryflags = gio::FileQueryInfoFlags::NOFOLLOW_SYMLINKS;
let fetched_iter = fetched_tree.enumerate_children(queryattrs, queryflags, cancellable)?;
while let Some(fetched_info) = fetched_iter.next_file(cancellable)? {
// only read and parse the file if it is a toml file
let name = fetched_info.name();
if let Some(name) = name.to_str() {
if name.ends_with(".toml") {
let fetched_child = fetched_iter.child(&fetched_info);
let fetched_child = fetched_child
.downcast::<ostree::RepoFile>()
.expect("downcast");
fetched_child.ensure_resolved()?;
let fetched_contents_checksum = fetched_child.checksum();
let f =
ostree::Repo::load_file(repo, fetched_contents_checksum.as_str(), cancellable)?;
let file_content = f.0;
let mut reader =
ostree_ext::prelude::InputStreamExtManual::into_read(file_content.unwrap());
let s = std::io::read_to_string(&mut reader)?;
let mut parsed_kargs =
parse_kargs_toml(&s, sys_arch).with_context(|| format!("Parsing {name}"))?;
remote_kargs.append(&mut parsed_kargs);
}
}
}
let remote_kargs = get_kargs_from_ostree(repo, &fetched_tree, sys_arch)?;

// get the diff between the existing and remote kargs
let mut added_kargs: Vec<String> = remote_kargs
Expand Down Expand Up @@ -156,6 +170,9 @@ fn parse_kargs_toml(contents: &str, sys_arch: &str) -> Result<Vec<String>> {

#[cfg(test)]
mod tests {
use fn_error_context::context;
use rustix::fd::{AsFd, AsRawFd};

use super::*;

#[test]
Expand Down Expand Up @@ -208,6 +225,20 @@ match-architectures = ["x86_64", "aarch64"]
assert!(parse_kargs_toml(test_missing, "x86_64").is_err());
}

#[context("writing test kargs")]
fn write_test_kargs(td: &Dir) -> Result<()> {
td.write(
"usr/lib/bootc/kargs.d/01-foo.toml",
r##"kargs = ["console=tty0", "nosmt"]"##,
)?;
td.write(
"usr/lib/bootc/kargs.d/02-bar.toml",
r##"kargs = ["console=ttyS1"]"##,
)?;

Ok(())
}

#[test]
fn test_get_kargs_in_root() -> Result<()> {
let td = cap_std_ext::cap_tempfile::TempDir::new(cap_std::ambient_authority())?;
Expand All @@ -220,18 +251,83 @@ match-architectures = ["x86_64", "aarch64"]
// Non-toml file
td.write("usr/lib/bootc/kargs.d/somegarbage", "garbage")?;
assert_eq!(get_kargs_in_root(&td, "x86_64").unwrap().len(), 0);
td.write(
"usr/lib/bootc/kargs.d/01-foo.toml",
r##"kargs = ["console=tty0", "nosmt"]"##,
)?;
td.write(
"usr/lib/bootc/kargs.d/02-bar.toml",
r##"kargs = ["console=ttyS1"]"##,
)?;

write_test_kargs(&td)?;

let args = get_kargs_in_root(&td, "x86_64").unwrap();
similar_asserts::assert_eq!(args, ["console=tty0", "nosmt", "console=ttyS1"]);

Ok(())
}

#[context("ostree commit")]
fn ostree_commit(
repo: &ostree::Repo,
d: &Dir,
path: &Utf8Path,
ostree_ref: &str,
) -> Result<()> {
let cancellable = gio::Cancellable::NONE;
let txn = repo.auto_transaction(cancellable)?;

let mt = ostree::MutableTree::new();
repo.write_dfd_to_mtree(d.as_fd().as_raw_fd(), path.as_str(), &mt, None, cancellable)
.context("Writing merged filesystem to mtree")?;

let merged_root = repo
.write_mtree(&mt, cancellable)
.context("Writing mtree")?;
let merged_root = merged_root.downcast::<ostree::RepoFile>().unwrap();
let merged_commit = repo
.write_commit(None, None, None, None, &merged_root, cancellable)
.context("Writing commit")?;
repo.transaction_set_ref(None, &ostree_ref, Some(merged_commit.as_str()));
txn.commit(cancellable)?;
Ok(())
}

#[test]
fn test_get_kargs_in_ostree() -> Result<()> {
let cancellable = gio::Cancellable::NONE;
let td = cap_std_ext::cap_tempfile::TempDir::new(cap_std::ambient_authority())?;

td.create_dir("repo")?;
let repo = &ostree::Repo::create_at(
td.as_fd().as_raw_fd(),
"repo",
ostree::RepoMode::Bare,
None,
gio::Cancellable::NONE,
)?;

td.create_dir("rootfs")?;
let test_rootfs = &td.open_dir("rootfs")?;

ostree_commit(repo, &test_rootfs, ".".into(), "testref")?;
// Helper closure to read the kargs
let get_kargs = |sys_arch: &str| -> Result<Vec<String>> {
let rootfs = repo.read_commit("testref", cancellable)?.0;
let rootfs = rootfs.downcast_ref::<ostree::RepoFile>().unwrap();
let fetched_tree = rootfs.resolve_relative_path("/usr/lib/bootc/kargs.d");
let fetched_tree = fetched_tree
.downcast::<ostree::RepoFile>()
.expect("downcast");
if !fetched_tree.query_exists(cancellable) {
return Ok(Default::default());
}
get_kargs_from_ostree(repo, &fetched_tree, sys_arch)
};

// rootfs is empty
assert_eq!(get_kargs("x86_64").unwrap().len(), 0);

test_rootfs.create_dir_all("usr/lib/bootc/kargs.d")?;
write_test_kargs(&test_rootfs).unwrap();
ostree_commit(repo, &test_rootfs, ".".into(), "testref")?;

let args = get_kargs("x86_64").unwrap();
similar_asserts::assert_eq!(args, ["console=tty0", "nosmt", "console=ttyS1"]);

Ok(())
}
}