Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
cgwalters committed Oct 21, 2023
1 parent a8e4dcf commit 6354592
Show file tree
Hide file tree
Showing 4 changed files with 133 additions and 137 deletions.
10 changes: 10 additions & 0 deletions lib/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
253 changes: 121 additions & 132 deletions lib/src/deploy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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<String>,
}

impl<'a> RequiredHostSpec<'a> {
Expand All @@ -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 })
}
}

Expand Down Expand Up @@ -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(
Expand All @@ -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::<ostree::RepoFile>().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::<ostree::RepoFile>().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(())
}
2 changes: 2 additions & 0 deletions lib/src/status.rs
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,8 @@ fn boot_entry_from_deployment(
} else {
(None, false)
}
} else {
(None, false)
};

let r = BootEntry {
Expand Down
5 changes: 0 additions & 5 deletions lib/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down

0 comments on commit 6354592

Please sign in to comment.