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: More cleanups #674

Merged
merged 3 commits into from
Jul 12, 2024
Merged
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
18 changes: 3 additions & 15 deletions lib/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -541,7 +541,6 @@ async fn upgrade(opts: UpgradeOpts) -> Result<()> {
}
} else {
let fetched = crate::deploy::pull(repo, imgref, opts.quiet).await?;
let kargs = crate::kargs::get_kargs(repo, &booted_deployment, fetched.as_ref())?;
let staged_digest = staged_image.as_ref().map(|s| s.image_digest.as_str());
let fetched_digest = fetched.manifest_digest.as_str();
tracing::debug!("staged: {staged_digest:?}");
Expand All @@ -563,10 +562,7 @@ async fn upgrade(opts: UpgradeOpts) -> Result<()> {
println!("No update available.")
} else {
let osname = booted_deployment.osname();
let mut opts = ostree::SysrootDeployTreeOpts::default();
let kargs: Vec<&str> = kargs.iter().map(|s| s.as_str()).collect();
opts.override_kernel_argv = Some(kargs.as_slice());
crate::deploy::stage(sysroot, &osname, &fetched, &spec, Some(opts)).await?;
crate::deploy::stage(sysroot, &osname, &fetched, &spec).await?;
changed = true;
if let Some(prev) = booted_image.as_ref() {
if let Some(fetched_manifest) = fetched.get_manifest(repo)? {
Expand Down Expand Up @@ -638,7 +634,6 @@ async fn switch(opts: SwitchOpts) -> Result<()> {
let new_spec = RequiredHostSpec::from_spec(&new_spec)?;

let fetched = crate::deploy::pull(repo, &target, opts.quiet).await?;
let kargs = crate::kargs::get_kargs(repo, &booted_deployment, fetched.as_ref())?;

if !opts.retain {
// By default, we prune the previous ostree ref so it will go away after later upgrades
Expand All @@ -652,10 +647,7 @@ async fn switch(opts: SwitchOpts) -> Result<()> {
}

let stateroot = booted_deployment.osname();
let mut opts = ostree::SysrootDeployTreeOpts::default();
let kargs: Vec<&str> = kargs.iter().map(|s| s.as_str()).collect();
opts.override_kernel_argv = Some(kargs.as_slice());
crate::deploy::stage(sysroot, &stateroot, &fetched, &new_spec, Some(opts)).await?;
crate::deploy::stage(sysroot, &stateroot, &fetched, &new_spec).await?;

Ok(())
}
Expand Down Expand Up @@ -700,15 +692,11 @@ async fn edit(opts: EditOpts) -> Result<()> {
}

let fetched = crate::deploy::pull(repo, new_spec.image, opts.quiet).await?;
let kargs = crate::kargs::get_kargs(repo, &booted_deployment, fetched.as_ref())?;

// TODO gc old layers here

let stateroot = booted_deployment.osname();
let mut opts = ostree::SysrootDeployTreeOpts::default();
let kargs: Vec<&str> = kargs.iter().map(|s| s.as_str()).collect();
opts.override_kernel_argv = Some(kargs.as_slice());
crate::deploy::stage(sysroot, &stateroot, &fetched, &new_spec, Some(opts)).await?;
crate::deploy::stage(sysroot, &stateroot, &fetched, &new_spec).await?;

Ok(())
}
Expand Down
22 changes: 18 additions & 4 deletions lib/src/deploy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -327,10 +327,26 @@ async fn deploy(
stateroot: &str,
image: &ImageState,
origin: &glib::KeyFile,
opts: Option<ostree::SysrootDeployTreeOpts<'_>>,
) -> Result<Deployment> {
let stateroot = Some(stateroot);
let opts = opts.unwrap_or_default();
let mut opts = ostree::SysrootDeployTreeOpts::default();
// Compute the kernel argument overrides. In practice today this API is always expecting
// a merge deployment. The kargs code also always looks at the booted root (which
// is a distinct minor issue, but not super important as right now the install path
// doesn't use this API).
let override_kargs = if let Some(deployment) = merge_deployment {
Some(crate::kargs::get_kargs(sysroot, &deployment, image)?)
} else {
None
};
// Because the C API expects a Vec<&str>, we need to generate a new Vec<>
// that borrows.
let override_kargs = override_kargs
.as_deref()
.map(|v| v.iter().map(|s| s.as_str()).collect::<Vec<_>>());
if let Some(kargs) = override_kargs.as_deref() {
opts.override_kernel_argv = Some(&kargs);
}
// Copy to move into thread
let cancellable = gio::Cancellable::NONE;
return sysroot
Expand Down Expand Up @@ -364,7 +380,6 @@ pub(crate) async fn stage(
stateroot: &str,
image: &ImageState,
spec: &RequiredHostSpec<'_>,
opts: Option<ostree::SysrootDeployTreeOpts<'_>>,
) -> Result<()> {
let merge_deployment = sysroot.merge_deployment(Some(stateroot));
let origin = origin_from_imageref(spec.image)?;
Expand All @@ -374,7 +389,6 @@ pub(crate) async fn stage(
stateroot,
image,
&origin,
opts,
)
.await?;
crate::deploy::cleanup(sysroot).await?;
Expand Down
28 changes: 15 additions & 13 deletions lib/src/kargs.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use anyhow::{Context, Result};
use camino::Utf8Path;
use cap_std_ext::cap_std;
use cap_std_ext::cap_std::fs::Dir;
use cap_std_ext::dirext::CapStdExtDirExt;
use ostree::gio;
Expand All @@ -9,6 +8,7 @@ use ostree_ext::ostree::Deployment;
use ostree_ext::prelude::Cast;
use ostree_ext::prelude::FileEnumeratorExt;
use ostree_ext::prelude::FileExt;
use ostree_ext::sysroot::SysrootLock;
use serde::Deserialize;

use crate::deploy::ImageState;
Expand Down Expand Up @@ -101,25 +101,26 @@ fn get_kargs_from_ostree(
/// 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,
sysroot: &SysrootLock,
merge_deployment: &Deployment,
fetched: &ImageState,
) -> Result<Vec<String>> {
let cancellable = gio::Cancellable::NONE;
let mut kargs: Vec<String> = vec![];
let repo = &sysroot.repo();
let mut kargs = vec![];
let sys_arch = std::env::consts::ARCH;

// Get the running kargs of the booted system
if let Some(bootconfig) = ostree::Deployment::bootconfig(booted_deployment) {
// Get the kargs used for the merge in the bootloader config
if let Some(bootconfig) = ostree::Deployment::bootconfig(merge_deployment) {
if let Some(options) = ostree::BootconfigParser::get(&bootconfig, "options") {
let options = options.split_whitespace().map(|s| s.to_owned());
kargs.extend(options);
}
};

// Get the kargs in kargs.d of the booted system
let root = &cap_std::fs::Dir::open_ambient_dir("/", cap_std::ambient_authority())?;
let existing_kargs: Vec<String> = get_kargs_in_root(root, sys_arch)?;
// Get the kargs in kargs.d of the merge
let merge_root = &crate::utils::deployment_fd(sysroot, merge_deployment)?;
let existing_kargs = get_kargs_in_root(merge_root, sys_arch)?;

// Get the kargs in kargs.d of the pending image
let (fetched_tree, _) = repo.read_commit(fetched.ostree_commit.as_str(), cancellable)?;
Expand All @@ -138,16 +139,16 @@ pub(crate) fn get_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
let mut added_kargs = remote_kargs
.clone()
.into_iter()
.filter(|item| !existing_kargs.contains(item))
.collect();
let removed_kargs: Vec<String> = existing_kargs
.collect::<Vec<_>>();
let removed_kargs = existing_kargs
.clone()
.into_iter()
.filter(|item| !remote_kargs.contains(item))
.collect();
.collect::<Vec<_>>();

tracing::debug!(
"kargs: added={:?} removed={:?}",
Expand Down Expand Up @@ -179,6 +180,7 @@ fn parse_kargs_toml(contents: &str, sys_arch: &str) -> Result<Vec<String>> {

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

Expand Down
20 changes: 20 additions & 0 deletions lib/src/utils.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
use std::future::Future;
use std::io::Write;
use std::os::fd::BorrowedFd;
use std::process::Command;
use std::time::Duration;

use anyhow::{Context, Result};
use cap_std_ext::cap_std::fs::Dir;
use ostree::glib;
use ostree_ext::container::SignatureSource;
use ostree_ext::ostree;
Expand All @@ -21,6 +23,24 @@ pub(crate) fn origin_has_rpmostree_stuff(kf: &glib::KeyFile) -> bool {
false
}

// Access the file descriptor for a sysroot
#[allow(unsafe_code)]
pub(crate) fn sysroot_fd(sysroot: &ostree::Sysroot) -> BorrowedFd {
unsafe { BorrowedFd::borrow_raw(sysroot.fd()) }
}

// Return a cap-std `Dir` type for a deployment.
// TODO: in the future this should perhaps actually mount via composefs
#[allow(unsafe_code)]
pub(crate) fn deployment_fd(
sysroot: &ostree::Sysroot,
deployment: &ostree::Deployment,
) -> Result<Dir> {
let sysroot_dir = &Dir::reopen_dir(&sysroot_fd(sysroot))?;
let dirpath = sysroot.deployment_dirpath(deployment);
sysroot_dir.open_dir(&dirpath).map_err(Into::into)
}

/// Given an mount option string list like foo,bar=baz,something=else,ro parse it and find
/// the first entry like $optname=
/// This will not match a bare `optname` without an equals.
Expand Down