Skip to content

Commit 6098379

Browse files
cli/composefs: Implement bootc switch
This does not yet "apply" the switch, it only stages a deployment Also refactor pulling of a composefs repository to a separate function Signed-off-by: Pragyan Poudyal <[email protected]>
1 parent ab8cdc9 commit 6098379

File tree

2 files changed

+107
-47
lines changed

2 files changed

+107
-47
lines changed

lib/src/cli.rs

Lines changed: 72 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ use std::ffi::{CString, OsStr, OsString};
66
use std::io::Seek;
77
use std::os::unix::process::CommandExt;
88
use std::process::Command;
9-
use std::sync::Arc;
109

1110
use anyhow::{ensure, Context, Result};
1211
use camino::Utf8PathBuf;
@@ -28,8 +27,8 @@ use serde::{Deserialize, Serialize};
2827

2928
use crate::deploy::RequiredHostSpec;
3029
use crate::install::{
31-
open_composefs_repo, setup_composefs_bls_boot, setup_composefs_uki_boot, write_composefs_state,
32-
BootType, BootSetupType,
30+
pull_composefs_repo, setup_composefs_bls_boot, setup_composefs_uki_boot, write_composefs_state,
31+
BootSetupType, BootType,
3332
};
3433
use crate::lints;
3534
use crate::progress_jsonl::{ProgressWriter, RawProgressFd};
@@ -38,11 +37,6 @@ use crate::spec::ImageReference;
3837
use crate::status::composefs_deployment_status;
3938
use crate::utils::sigpolicy_from_opt;
4039

41-
use ostree_ext::composefs_boot::BootOps;
42-
use ostree_ext::composefs_oci::{
43-
image::create_filesystem as create_composefs_filesystem, pull as composefs_oci_pull,
44-
};
45-
4640
/// Shared progress options
4741
#[derive(Debug, Parser, PartialEq, Eq)]
4842
pub(crate) struct ProgressOptions {
@@ -773,44 +767,21 @@ async fn upgrade_composefs(_opts: UpgradeOpts) -> Result<()> {
773767
.as_ref()
774768
.ok_or_else(|| anyhow::anyhow!("No image source specified"))?;
775769

776-
let booted_image = host
777-
.status
778-
.booted
779-
.ok_or(anyhow::anyhow!("Could not find booted image"))?
780-
.image
781-
.ok_or(anyhow::anyhow!("Could not find booted image"))?;
782-
783-
tracing::debug!("booted_image: {booted_image:#?}");
784-
tracing::debug!("imgref: {imgref:#?}");
785-
786-
let digest = booted_image
787-
.digest()
788-
.context("Getting digest for booted image")?;
770+
// let booted_image = host
771+
// .status
772+
// .booted
773+
// .ok_or(anyhow::anyhow!("Could not find booted image"))?
774+
// .image
775+
// .ok_or(anyhow::anyhow!("Could not find booted image"))?;
789776

790-
let rootfs_dir = cap_std::fs::Dir::open_ambient_dir("/sysroot", cap_std::ambient_authority())?;
777+
// tracing::debug!("booted_image: {booted_image:#?}");
778+
// tracing::debug!("imgref: {imgref:#?}");
791779

792-
let repo = open_composefs_repo(&rootfs_dir).context("Opening compoesfs repo")?;
780+
// let digest = booted_image
781+
// .digest()
782+
// .context("Getting digest for booted image")?;
793783

794-
let (id, verity) = composefs_oci_pull(
795-
&Arc::new(repo),
796-
&format!("{}:{}", imgref.transport, imgref.image),
797-
None,
798-
)
799-
.await
800-
.context("Pulling composefs repo")?;
801-
802-
tracing::debug!(
803-
"id = {id}, verity = {verity}",
804-
id = hex::encode(id),
805-
verity = verity.to_hex()
806-
);
807-
808-
let repo = open_composefs_repo(&rootfs_dir)?;
809-
let mut fs = create_composefs_filesystem(&repo, digest.digest(), None)
810-
.context("Failed to create composefs filesystem")?;
811-
812-
let entries = fs.transform_for_boot(&repo)?;
813-
let id = fs.commit_image(&repo, None)?;
784+
let (repo, entries, id) = pull_composefs_repo(&imgref.transport, &imgref.image).await?;
814785

815786
let Some(entry) = entries.into_iter().next() else {
816787
anyhow::bail!("No boot entries!");
@@ -939,9 +910,7 @@ async fn upgrade(opts: UpgradeOpts) -> Result<()> {
939910
Ok(())
940911
}
941912

942-
/// Implementation of the `bootc switch` CLI command.
943-
#[context("Switching")]
944-
async fn switch(opts: SwitchOpts) -> Result<()> {
913+
fn imgref_for_switch(opts: &SwitchOpts) -> Result<ImageReference> {
945914
let transport = ostree_container::Transport::try_from(opts.transport.as_str())?;
946915
let imgref = ostree_container::ImageReference {
947916
transport,
@@ -950,6 +919,56 @@ async fn switch(opts: SwitchOpts) -> Result<()> {
950919
let sigverify = sigpolicy_from_opt(opts.enforce_container_sigpolicy);
951920
let target = ostree_container::OstreeImageReference { sigverify, imgref };
952921
let target = ImageReference::from(target);
922+
923+
return Ok(target);
924+
}
925+
926+
#[context("Composefs Switching")]
927+
async fn switch_composefs(opts: SwitchOpts) -> Result<()> {
928+
let target = imgref_for_switch(&opts)?;
929+
// TODO: Handle in-place
930+
931+
let host = composefs_deployment_status()
932+
.await
933+
.context("Getting composefs deployment status")?;
934+
935+
let new_spec = {
936+
let mut new_spec = host.spec.clone();
937+
new_spec.image = Some(target.clone());
938+
new_spec
939+
};
940+
941+
if new_spec == host.spec {
942+
println!("Image specification is unchanged.");
943+
return Ok(());
944+
}
945+
946+
let Some(target_imgref) = new_spec.image else {
947+
anyhow::bail!("Target image is undefined")
948+
};
949+
950+
let (repo, entries, id) =
951+
pull_composefs_repo(&target_imgref.transport, &target_imgref.image).await?;
952+
953+
let Some(entry) = entries.into_iter().next() else {
954+
anyhow::bail!("No boot entries!");
955+
};
956+
957+
match BootType::from(&entry) {
958+
BootType::Bls => setup_composefs_bls_boot(BootSetupType::Upgrade, repo, &id, entry),
959+
BootType::Uki => setup_composefs_uki_boot(BootSetupType::Upgrade, repo, &id, entry),
960+
}?;
961+
962+
write_composefs_state(&Utf8PathBuf::from("/sysroot"), id, &target_imgref, true)?;
963+
964+
Ok(())
965+
}
966+
967+
/// Implementation of the `bootc switch` CLI command.
968+
#[context("Switching")]
969+
async fn switch(opts: SwitchOpts) -> Result<()> {
970+
let target = imgref_for_switch(&opts)?;
971+
953972
let prog: ProgressWriter = opts.progress.try_into()?;
954973

955974
// If we're doing an in-place mutation, we shortcut most of the rest of the work here
@@ -1170,7 +1189,13 @@ async fn run_from_opt(opt: Opt) -> Result<()> {
11701189
upgrade(opts).await
11711190
}
11721191
}
1173-
Opt::Switch(opts) => switch(opts).await,
1192+
Opt::Switch(opts) => {
1193+
if composefs_booted()? {
1194+
switch_composefs(opts).await
1195+
} else {
1196+
switch(opts).await
1197+
}
1198+
}
11741199
Opt::Rollback(opts) => rollback(opts).await,
11751200
Opt::Edit(opts) => edit(opts).await,
11761201
Opt::UsrOverlay => usroverlay().await,

lib/src/install.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1742,6 +1742,41 @@ fi
17421742
Ok(())
17431743
}
17441744

1745+
/// Pulls the `image` from `transport` into a composefs repository at /sysroot
1746+
/// Checks for boot entries in the image and returns them
1747+
#[context("Pulling composefs repository")]
1748+
pub(crate) async fn pull_composefs_repo(
1749+
transport: &String,
1750+
image: &String,
1751+
) -> Result<(
1752+
ComposefsRepository<Sha256HashValue>,
1753+
Vec<BootEntry<Sha256HashValue>>,
1754+
Sha256HashValue,
1755+
)> {
1756+
let rootfs_dir = cap_std::fs::Dir::open_ambient_dir("/sysroot", cap_std::ambient_authority())?;
1757+
1758+
let repo = open_composefs_repo(&rootfs_dir).context("Opening compoesfs repo")?;
1759+
1760+
let (id, verity) = composefs_oci_pull(&Arc::new(repo), &format!("{transport}:{image}"), None)
1761+
.await
1762+
.context("Pulling composefs repo")?;
1763+
1764+
tracing::debug!(
1765+
"id = {id}, verity = {verity}",
1766+
id = hex::encode(id),
1767+
verity = verity.to_hex()
1768+
);
1769+
1770+
let repo = open_composefs_repo(&rootfs_dir)?;
1771+
let mut fs = create_composefs_filesystem(&repo, &hex::encode(id), None)
1772+
.context("Failed to create composefs filesystem")?;
1773+
1774+
let entries = fs.transform_for_boot(&repo)?;
1775+
let id = fs.commit_image(&repo, None)?;
1776+
1777+
Ok((repo, entries, id))
1778+
}
1779+
17451780
#[context("Setting up composefs boot")]
17461781
fn setup_composefs_boot(root_setup: &RootSetup, state: &State, image_id: &str) -> Result<()> {
17471782
let boot_uuid = root_setup

0 commit comments

Comments
 (0)