From 6354592bec79d75f5f74bd435987cc491bd2463a Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Sat, 21 Oct 2023 09:08:37 -0400 Subject: [PATCH] wip --- lib/src/config.rs | 10 ++ lib/src/deploy.rs | 253 ++++++++++++++++++++++------------------------ lib/src/status.rs | 2 + lib/src/utils.rs | 5 - 4 files changed, 133 insertions(+), 137 deletions(-) diff --git a/lib/src/config.rs b/lib/src/config.rs index 278f9837a..c24a2a93f 100644 --- a/lib/src/config.rs +++ b/lib/src/config.rs @@ -309,6 +309,16 @@ fn write_configmap( Ok(()) } +#[context("Applying configmap")] +pub(crate) fn apply_configmap( + sysroot: &SysrootLock, + sepolicy: Option<&ostree::SePolicy>, + name: &str, + cancellable: Option<&gio::Cancellable>, +) -> Result<()> { + todo!() +} + /// Parse a manifest, returning the single configmap descriptor (layer) fn configmap_object_from_manifest( manifest: &oci_spec::image::ImageManifest, diff --git a/lib/src/deploy.rs b/lib/src/deploy.rs index 80d0e3747..4161a415d 100644 --- a/lib/src/deploy.rs +++ b/lib/src/deploy.rs @@ -4,17 +4,17 @@ use anyhow::{Context, Result}; +use cap_std_ext::cap_std::fs::Dir; use cap_std_ext::cap_tempfile; use fn_error_context::context; +use glib::prelude::{Cast, ToVariant}; use ostree::{gio, glib}; use ostree_container::store::LayeredImageState; use ostree_container::OstreeImageReference; use ostree_ext::container as ostree_container; use ostree_ext::ostree; use ostree_ext::ostree::Deployment; -use ostree_ext::prelude::Cast; -use ostree_ext::prelude::ToVariant; use ostree_ext::sysroot::SysrootLock; use std::borrow::Cow; use std::collections::HashMap; @@ -34,6 +34,7 @@ const BOOTC_DERIVED_KEY: &str = "bootc.derived"; /// Variant of HostSpec but required to be filled out pub(crate) struct RequiredHostSpec<'a> { pub(crate) image: &'a ImageReference, + pub(crate) configmaps: &'a Vec, } impl<'a> RequiredHostSpec<'a> { @@ -44,7 +45,8 @@ impl<'a> RequiredHostSpec<'a> { .image .as_ref() .ok_or_else(|| anyhow::anyhow!("Missing image in specification"))?; - Ok(Self { image }) + let configmaps = &spec.configmap_sources; + Ok(Self { image, configmaps }) } } @@ -107,29 +109,6 @@ pub(crate) fn require_base_commit<'a>( Ok(r) } -#[context("Writing deployment")] -pub(crate) async fn deploy( - sysroot: &SysrootLock, - merge_deployment: Option<&Deployment>, - stateroot: &str, - image: &LayeredImageState, - origin: &glib::KeyFile, -) -> Result<()> { - let stateroot = Some(stateroot); - // Copy to move into thread - let base_commit = image.get_commit().to_owned(); - let cancellable = gio::Cancellable::NONE; - let _new_deployment = sysroot.stage_tree_with_options( - stateroot, - &base_commit, - Some(origin), - merge_deployment, - &Default::default(), - cancellable, - )?; - Ok(()) -} - /// Stage (queue deployment of) a fetched container image. #[context("Staging")] pub(crate) async fn stage( @@ -138,134 +117,144 @@ pub(crate) async fn stage( image: &LayeredImageState, spec: &RequiredHostSpec<'_>, ) -> Result<()> { + let repo = sysroot.repo(); let merge_deployment = sysroot.merge_deployment(Some(stateroot)); let origin = glib::KeyFile::new(); let imgref = OstreeImageReference::from(spec.image.clone()); + let base_commit = require_base_commit(&repo, &image.merge_commit)?; origin.set_string( "origin", ostree_container::deploy::ORIGIN_CONTAINER, imgref.to_string().as_str(), ); - crate::deploy::deploy( - sysroot, - merge_deployment.as_ref(), - stateroot, - &image, - &origin, - ) - .await?; - crate::deploy::cleanup(sysroot).await?; - println!("Queued for next boot: {imgref}"); - if let Some(version) = image - .configuration - .as_ref() - .and_then(ostree_container::version_for_config) - { - println!(" Version: {version}"); - } - println!(" Digest: {}", image.manifest_digest); - ostree_container::deploy::remove_undeployed_images(sysroot).context("Pruning images")?; - - tracing::debug!("Configmaps to overlay: {}", configs.len()); - let merge_commit = - ostree_ext::tokio_util::spawn_blocking_cancellable_flatten(move |cancellable| { - use rustix::fd::AsRawFd; - let cancellable = Some(cancellable); - let repo = &repo; - let txn = repo.auto_transaction(cancellable)?; - - let tmp_baseref = format!("{BASE_IMAGE_PREFIX}/{TMP_REF}"); - txn.repo() - .transaction_set_ref(None, &tmp_baseref, Some(image.merge_commit.as_str())); - drop(tmp_baseref); + if spec.configmaps.is_empty() { + let stateroot = Some(stateroot); + let base_commit = image.get_commit().to_owned(); + let cancellable = gio::Cancellable::NONE; + let _new_deployment = sysroot.stage_tree_with_options( + stateroot, + &base_commit, + Some(&origin), + merge_deployment.as_ref(), + &Default::default(), + cancellable, + )?; + } else { + tracing::debug!("Configmaps to overlay: {}", spec.configmaps.len()); + // Create cloned data to pass into thread + let thread_sysroot = sysroot.sysroot.clone(); + let configmaps = spec.configmaps.clone(); + let image_merge_commit = image.merge_commit.clone(); + let base_commit = base_commit.into_owned(); + let merge_commit = + ostree_ext::tokio_util::spawn_blocking_cancellable_flatten(move |cancellable| { + let sysroot = + &ostree_ext::sysroot::SysrootLock::from_assumed_locked(&thread_sysroot); + use rustix::fd::AsRawFd; + let cancellable = Some(cancellable); + let repo = &repo; + let txn = repo.auto_transaction(cancellable)?; - let devino = ostree::RepoDevInoCache::new(); - let repodir = repo.dfd_as_dir()?; - let repo_tmp = repodir.open_dir("tmp")?; - let td = cap_tempfile::TempDir::new_in(&repo_tmp)?; + let tmp_baseref = format!("{BASE_IMAGE_PREFIX}/{TMP_REF}"); + txn.repo().transaction_set_ref( + None, + &tmp_baseref, + Some(image_merge_commit.as_str()), + ); + drop(tmp_baseref); - let rootpath = "root"; - let checkout_mode = if repo.mode() == ostree::RepoMode::Bare { - ostree::RepoCheckoutMode::None - } else { - ostree::RepoCheckoutMode::User - }; - let mut checkout_opts = ostree::RepoCheckoutAtOptions { - mode: checkout_mode, - overwrite_mode: ostree::RepoCheckoutOverwriteMode::UnionFiles, - devino_to_csum_cache: Some(devino.clone()), - no_copy_fallback: true, - force_copy_zerosized: true, - process_whiteouts: false, - ..Default::default() - }; - repo.checkout_at( - Some(&checkout_opts), - (*td).as_raw_fd(), - rootpath, - &base_commit, - cancellable, - ) - .context("Checking out base commit")?; + let devino = ostree::RepoDevInoCache::new(); + let repodir = Dir::reopen_dir(&repo.dfd_borrow())?; + let repo_tmp = repodir.open_dir("tmp")?; + let td = cap_tempfile::TempDir::new_in(&repo_tmp)?; - // Layer all configmaps - checkout_opts.process_whiteouts = true; - for config in configs { - let oref = config.ostree_ref()?; - let commit = repo.require_rev(&oref)?; + let rootpath = "root"; + let checkout_mode = if repo.mode() == ostree::RepoMode::Bare { + ostree::RepoCheckoutMode::None + } else { + ostree::RepoCheckoutMode::User + }; + let mut checkout_opts = ostree::RepoCheckoutAtOptions { + mode: checkout_mode, + overwrite_mode: ostree::RepoCheckoutOverwriteMode::UnionFiles, + devino_to_csum_cache: Some(devino.clone()), + no_copy_fallback: true, + force_copy_zerosized: true, + process_whiteouts: false, + ..Default::default() + }; repo.checkout_at( Some(&checkout_opts), (*td).as_raw_fd(), rootpath, - &commit, + &base_commit, cancellable, ) - .with_context(|| format!("Checking out layer {commit}"))?; - } + .context("Checking out base commit")?; - let modifier = - ostree::RepoCommitModifier::new(ostree::RepoCommitModifierFlags::CONSUME, None); - modifier.set_devino_cache(&devino); + let sepolicy = ostree::SePolicy::new_at((*td).as_raw_fd(), cancellable)?; - let mt = ostree::MutableTree::new(); - repo.write_dfd_to_mtree( - (*td).as_raw_fd(), - rootpath, - &mt, - Some(&modifier), - cancellable, - ) - .context("Writing merged filesystem to mtree")?; + // Layer all configmaps + checkout_opts.process_whiteouts = true; + for name in configmaps { + crate::config::apply_configmap(sysroot, Some(&sepolicy), &name, cancellable)?; + } + + let modifier = + ostree::RepoCommitModifier::new(ostree::RepoCommitModifierFlags::CONSUME, None); + modifier.set_devino_cache(&devino); - let mut metadata = HashMap::new(); - metadata.insert(BOOTC_DERIVED_KEY, base_commit.to_variant()); - let metadata = metadata.to_variant(); + let mt = ostree::MutableTree::new(); + repo.write_dfd_to_mtree( + (*td).as_raw_fd(), + rootpath, + &mt, + Some(&modifier), + cancellable, + ) + .context("Writing merged filesystem to mtree")?; - let merged_root = repo - .write_mtree(&mt, cancellable) - .context("Writing mtree")?; - let merged_root = merged_root.downcast::().unwrap(); - let merged_commit = repo - .write_commit(None, None, None, Some(&metadata), &merged_root, cancellable) - .context("Writing commit")?; - txn.commit(cancellable)?; + let mut metadata = HashMap::new(); + metadata.insert(BOOTC_DERIVED_KEY, base_commit.to_variant()); + let metadata = metadata.to_variant(); - anyhow::Ok(merged_commit.to_string()) - }) - .await?; - // TODO spawn once origin files are Send - // let origin = origin.clone(); - // ostree_ext::tokio_util::spawn_blocking_cancellable_flatten(move |cancellable| { + let merged_root = repo + .write_mtree(&mt, cancellable) + .context("Writing mtree")?; + let merged_root = merged_root.downcast::().unwrap(); + let merged_commit = repo + .write_commit(None, None, None, Some(&metadata), &merged_root, cancellable) + .context("Writing commit")?; + txn.commit(cancellable)?; + + anyhow::Ok(merged_commit.to_string()) + }) + .await?; + // TODO spawn once origin files are Send + // let origin = origin.clone(); + // ostree_ext::tokio_util::spawn_blocking_cancellable_flatten(move |cancellable| { + { + let cancellable = gio::Cancellable::NONE; + let _new_deployment = sysroot.stage_tree_with_options( + Some(stateroot), + merge_commit.as_str(), + Some(&origin), + merge_deployment.as_ref(), + &Default::default(), + cancellable, + )?; + } + } + crate::deploy::cleanup(sysroot).await?; + println!("Queued for next boot: {imgref}"); + if let Some(version) = image + .configuration + .as_ref() + .and_then(ostree_container::version_for_config) { - let cancellable = gio::Cancellable::NONE; - let _new_deployment = sysroot.stage_tree_with_options( - stateroot, - merge_commit.as_str(), - Some(&origin), - merge_deployment.as_ref(), - &Default::default(), - cancellable, - )?; - anyhow::Ok(()) + println!(" Version: {version}"); } + println!(" Digest: {}", image.manifest_digest); + ostree_container::deploy::remove_undeployed_images(sysroot).context("Pruning images")?; + anyhow::Ok(()) } diff --git a/lib/src/status.rs b/lib/src/status.rs index 416b1a9ab..4d31e056a 100644 --- a/lib/src/status.rs +++ b/lib/src/status.rs @@ -143,6 +143,8 @@ fn boot_entry_from_deployment( } else { (None, false) } + } else { + (None, false) }; let r = BootEntry { diff --git a/lib/src/utils.rs b/lib/src/utils.rs index 53705e3d6..51a73c414 100644 --- a/lib/src/utils.rs +++ b/lib/src/utils.rs @@ -5,11 +5,6 @@ use anyhow::{Context, Result}; use ostree::glib; use ostree_ext::ostree; -pub(crate) fn new_http_client() -> reqwest::ClientBuilder { - const USER_AGENT: &str = env!("CARGO_PKG_VERSION"); - reqwest::Client::builder().user_agent(USER_AGENT) -} - /// Try to look for keys injected by e.g. rpm-ostree requesting machine-local /// changes; if any are present, return `true`. pub(crate) fn origin_has_rpmostree_stuff(kf: &glib::KeyFile) -> bool {