-
Notifications
You must be signed in to change notification settings - Fork 70
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fixes #255. Allows users to create files within /usr/lib/bootc/kargs.d with kernel arguments. These arguments can now be applied on a switch, upgrade, or edit. General process: - use ostree commit of fetched container image to return the file tree - navigate to /usr/lib/bootc/kargs.d - read each file within the directory - calculate the diff between the booted and fetched kargs in kargs.d - apply the diff to the kargs currently on the running system - pass the kargs to the stage() function Signed-off-by: Luke Yang <luyang@redhat.com>
- Loading branch information
1 parent
dda4aaf
commit 3768c05
Showing
4 changed files
with
188 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,166 @@ | ||
use anyhow::Ok; | ||
use anyhow::Result; | ||
|
||
use crate::deploy::ImageState; | ||
use ostree::gio; | ||
use ostree_ext::ostree; | ||
use ostree_ext::ostree::Deployment; | ||
use ostree_ext::prelude::Cast; | ||
use ostree_ext::prelude::FileEnumeratorExt; | ||
use ostree_ext::prelude::FileExt; | ||
|
||
use serde::Deserialize; | ||
|
||
#[derive(Deserialize)] | ||
#[serde(rename_all = "kebab-case", deny_unknown_fields)] | ||
struct Config { | ||
kargs: Vec<String>, | ||
match_architectures: Option<Vec<String>>, | ||
} | ||
|
||
/// 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. | ||
pub(crate) fn get_kargs( | ||
repo: &ostree::Repo, | ||
booted_deployment: &Deployment, | ||
fetched: &ImageState, | ||
) -> Result<Vec<String>> { | ||
let cancellable = gio::Cancellable::NONE; | ||
let mut kargs: Vec<String> = vec![]; | ||
let sys_arch = std::env::consts::ARCH.to_string(); | ||
|
||
// Get the running kargs of the booted system | ||
if let Some(bootconfig) = ostree::Deployment::bootconfig(booted_deployment) { | ||
if let Some(options) = ostree::BootconfigParser::get(&bootconfig, "options") { | ||
let options: Vec<&str> = options.split_whitespace().collect(); | ||
let mut options: Vec<String> = options.into_iter().map(|s| s.to_string()).collect(); | ||
kargs.append(&mut options); | ||
} | ||
}; | ||
|
||
// Get the kargs in kargs.d of the booted system | ||
let mut existing_kargs: Vec<String> = vec![]; | ||
let fragments = liboverdrop::scan(&["/usr/lib"], "bootc/kargs.d", &["toml"], true); | ||
for (_name, path) in fragments { | ||
let s = std::fs::read_to_string(&path)?; | ||
let mut parsed_kargs = parse_file(s.clone(), sys_arch.clone())?; | ||
existing_kargs.append(&mut parsed_kargs); | ||
} | ||
|
||
// Get the kargs in kargs.d of the pending image | ||
let mut remote_kargs: Vec<String> = vec![]; | ||
let (fetched_tree, _) = repo.read_commit(fetched.ostree_commit.as_str(), cancellable)?; | ||
let fetched_tree = fetched_tree.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()); | ||
} | ||
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_file(s.clone(), sys_arch.clone())?; | ||
remote_kargs.append(&mut parsed_kargs); | ||
} | ||
} | ||
} | ||
|
||
// get the diff between the existing and remote kargs | ||
let mut added_kargs: Vec<String> = remote_kargs | ||
.clone() | ||
.into_iter() | ||
.filter(|item| !existing_kargs.contains(item)) | ||
.collect(); | ||
let removed_kargs: Vec<String> = existing_kargs | ||
.clone() | ||
.into_iter() | ||
.filter(|item| !remote_kargs.contains(item)) | ||
.collect(); | ||
|
||
tracing::debug!( | ||
"kargs: added={:?} removed={:?}", | ||
&added_kargs, | ||
removed_kargs | ||
); | ||
|
||
// apply the diff to the system kargs | ||
kargs.retain(|x| !removed_kargs.contains(x)); | ||
kargs.append(&mut added_kargs); | ||
|
||
Ok(kargs) | ||
} | ||
|
||
pub fn parse_file(file_content: String, sys_arch: String) -> Result<Vec<String>> { | ||
let mut de: Config = toml::from_str(&file_content)?; | ||
let mut parsed_kargs: Vec<String> = vec![]; | ||
// if arch specified, apply kargs only if the arch matches | ||
// if arch not specified, apply kargs unconditionally | ||
match de.match_architectures { | ||
None => parsed_kargs = de.kargs, | ||
Some(match_architectures) => { | ||
if match_architectures.contains(&sys_arch) { | ||
parsed_kargs.append(&mut de.kargs); | ||
} | ||
} | ||
} | ||
Ok(parsed_kargs) | ||
} | ||
|
||
#[test] | ||
/// Verify that kargs are only applied to supported architectures | ||
fn test_arch() { | ||
// no arch specified, kargs ensure that kargs are applied unconditionally | ||
let sys_arch = "x86_64".to_string(); | ||
let file_content = r##"kargs = ["console=tty0", "nosmt"]"##.to_string(); | ||
let parsed_kargs = parse_file(file_content.clone(), sys_arch.clone()).unwrap(); | ||
assert_eq!(parsed_kargs, ["console=tty0", "nosmt"]); | ||
let sys_arch = "aarch64".to_string(); | ||
let parsed_kargs = parse_file(file_content.clone(), sys_arch.clone()).unwrap(); | ||
assert_eq!(parsed_kargs, ["console=tty0", "nosmt"]); | ||
|
||
// one arch matches and one doesn't, ensure that kargs are only applied for the matching arch | ||
let sys_arch = "aarch64".to_string(); | ||
let file_content = r##"kargs = ["console=tty0", "nosmt"] | ||
match-architectures = ["x86_64"] | ||
"## | ||
.to_string(); | ||
let parsed_kargs = parse_file(file_content.clone(), sys_arch.clone()).unwrap(); | ||
assert_eq!(parsed_kargs, [] as [String; 0]); | ||
let file_content = r##"kargs = ["console=tty0", "nosmt"] | ||
match-architectures = ["aarch64"] | ||
"## | ||
.to_string(); | ||
let parsed_kargs = parse_file(file_content.clone(), sys_arch.clone()).unwrap(); | ||
assert_eq!(parsed_kargs, ["console=tty0", "nosmt"]); | ||
|
||
// multiple arch specified, ensure that kargs are applied to both archs | ||
let sys_arch = "x86_64".to_string(); | ||
let file_content = r##"kargs = ["console=tty0", "nosmt"] | ||
match-architectures = ["x86_64", "aarch64"] | ||
"## | ||
.to_string(); | ||
let parsed_kargs = parse_file(file_content.clone(), sys_arch.clone()).unwrap(); | ||
assert_eq!(parsed_kargs, ["console=tty0", "nosmt"]); | ||
std::env::set_var("ARCH", "aarch64"); | ||
let parsed_kargs = parse_file(file_content.clone(), sys_arch.clone()).unwrap(); | ||
assert_eq!(parsed_kargs, ["console=tty0", "nosmt"]); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters