From a8e4dcf75e7fae13e25a2c0116cddf33f77e1c19 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Sun, 12 Feb 2023 16:45:33 -0500 Subject: [PATCH] Add support for configmaps This is part of https://github.com/containers/bootc/issues/22 --- lib/src/deploy.rs | 127 +++++++++++++++++++++++++++++++++++++++++++++- lib/src/lsm.rs | 1 + lib/src/status.rs | 3 +- lib/src/utils.rs | 5 ++ 4 files changed, 132 insertions(+), 4 deletions(-) diff --git a/lib/src/deploy.rs b/lib/src/deploy.rs index 45936a071..80d0e3747 100644 --- a/lib/src/deploy.rs +++ b/lib/src/deploy.rs @@ -4,6 +4,8 @@ use anyhow::{Context, Result}; +use cap_std_ext::cap_tempfile; + use fn_error_context::context; use ostree::{gio, glib}; use ostree_container::store::LayeredImageState; @@ -11,13 +13,20 @@ 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; use crate::spec::HostSpec; use crate::spec::ImageReference; // TODO use https://github.com/ostreedev/ostree-rs-ext/pull/493/commits/afc1837ff383681b947de30c0cefc70080a4f87a const BASE_IMAGE_PREFIX: &str = "ostree/container/baseimage/bootc"; +/// This is a temporary pointer used until a deployment is committed to +/// hold a strong reference to the base image. +const TMP_REF: &str = "tmp"; /// Set on an ostree commit if this is a derived commit const BOOTC_DERIVED_KEY: &str = "bootc.derived"; @@ -85,8 +94,21 @@ pub(crate) fn get_base_commit(repo: &ostree::Repo, commit: &str) -> Result( + repo: &ostree::Repo, + commit: &'a str, +) -> Result> { + let r = get_base_commit(repo, commit)? + .map(Cow::Owned) + .unwrap_or_else(|| Cow::Borrowed(commit)); + Ok(r) +} + #[context("Writing deployment")] -async fn deploy( +pub(crate) async fn deploy( sysroot: &SysrootLock, merge_deployment: Option<&Deployment>, stateroot: &str, @@ -144,5 +166,106 @@ pub(crate) async fn stage( println!(" Digest: {}", image.manifest_digest); ostree_container::deploy::remove_undeployed_images(sysroot).context("Pruning images")?; - Ok(()) + 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); + + 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 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")?; + + // Layer all configmaps + checkout_opts.process_whiteouts = true; + for config in configs { + let oref = config.ostree_ref()?; + let commit = repo.require_rev(&oref)?; + repo.checkout_at( + Some(&checkout_opts), + (*td).as_raw_fd(), + rootpath, + &commit, + cancellable, + ) + .with_context(|| format!("Checking out layer {commit}"))?; + } + + let modifier = + ostree::RepoCommitModifier::new(ostree::RepoCommitModifierFlags::CONSUME, None); + modifier.set_devino_cache(&devino); + + 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 mut metadata = HashMap::new(); + metadata.insert(BOOTC_DERIVED_KEY, base_commit.to_variant()); + let metadata = metadata.to_variant(); + + 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( + stateroot, + merge_commit.as_str(), + Some(&origin), + merge_deployment.as_ref(), + &Default::default(), + cancellable, + )?; + anyhow::Ok(()) + } } diff --git a/lib/src/lsm.rs b/lib/src/lsm.rs index a2ea2c4ed..f5d209730 100644 --- a/lib/src/lsm.rs +++ b/lib/src/lsm.rs @@ -11,6 +11,7 @@ use fn_error_context::context; use gvariant::{aligned_bytes::TryAsAligned, Marker, Structure}; #[cfg(feature = "install")] use ostree_ext::ostree; +use ostree_ext::prelude::ToVariant; #[cfg(feature = "install")] use crate::task::Task; diff --git a/lib/src/status.rs b/lib/src/status.rs index 8c573559a..416b1a9ab 100644 --- a/lib/src/status.rs +++ b/lib/src/status.rs @@ -143,9 +143,8 @@ fn boot_entry_from_deployment( } else { (None, false) } - } else { - (None, false) }; + let r = BootEntry { image, configmaps, diff --git a/lib/src/utils.rs b/lib/src/utils.rs index 51a73c414..53705e3d6 100644 --- a/lib/src/utils.rs +++ b/lib/src/utils.rs @@ -5,6 +5,11 @@ 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 {