Skip to content

Commit ed83e7e

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 cec16f4 commit ed83e7e

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;
@@ -29,8 +28,8 @@ use serde::{Deserialize, Serialize};
2928

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

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

786-
let booted_image = host
787-
.status
788-
.booted
789-
.ok_or(anyhow::anyhow!("Could not find booted image"))?
790-
.image
791-
.ok_or(anyhow::anyhow!("Could not find booted image"))?;
792-
793-
tracing::debug!("booted_image: {booted_image:#?}");
794-
tracing::debug!("imgref: {imgref:#?}");
795-
796-
let digest = booted_image
797-
.digest()
798-
.context("Getting digest for booted image")?;
780+
// let booted_image = host
781+
// .status
782+
// .booted
783+
// .ok_or(anyhow::anyhow!("Could not find booted image"))?
784+
// .image
785+
// .ok_or(anyhow::anyhow!("Could not find booted image"))?;
799786

800-
let rootfs_dir = cap_std::fs::Dir::open_ambient_dir("/sysroot", cap_std::ambient_authority())?;
787+
// tracing::debug!("booted_image: {booted_image:#?}");
788+
// tracing::debug!("imgref: {imgref:#?}");
801789

802-
let repo = open_composefs_repo(&rootfs_dir).context("Opening compoesfs repo")?;
790+
// let digest = booted_image
791+
// .digest()
792+
// .context("Getting digest for booted image")?;
803793

804-
let (id, verity) = composefs_oci_pull(
805-
&Arc::new(repo),
806-
&format!("{}:{}", imgref.transport, imgref.image),
807-
None,
808-
)
809-
.await
810-
.context("Pulling composefs repo")?;
811-
812-
tracing::debug!(
813-
"id = {id}, verity = {verity}",
814-
id = hex::encode(id),
815-
verity = verity.to_hex()
816-
);
817-
818-
let repo = open_composefs_repo(&rootfs_dir)?;
819-
let mut fs = create_composefs_filesystem(&repo, digest.digest(), None)
820-
.context("Failed to create composefs filesystem")?;
821-
822-
let entries = fs.transform_for_boot(&repo)?;
823-
let id = fs.commit_image(&repo, None)?;
794+
let (repo, entries, id) = pull_composefs_repo(&imgref.transport, &imgref.image).await?;
824795

825796
let Some(entry) = entries.into_iter().next() else {
826797
anyhow::bail!("No boot entries!");
@@ -949,9 +920,7 @@ async fn upgrade(opts: UpgradeOpts) -> Result<()> {
949920
Ok(())
950921
}
951922

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

965984
// If we're doing an in-place mutation, we shortcut most of the rest of the work here
@@ -1182,7 +1201,13 @@ async fn run_from_opt(opt: Opt) -> Result<()> {
11821201
upgrade(opts).await
11831202
}
11841203
}
1185-
Opt::Switch(opts) => switch(opts).await,
1204+
Opt::Switch(opts) => {
1205+
if composefs_booted()? {
1206+
switch_composefs(opts).await
1207+
} else {
1208+
switch(opts).await
1209+
}
1210+
}
11861211
Opt::Rollback(opts) => rollback(opts).await,
11871212
Opt::Edit(opts) => edit(opts).await,
11881213
Opt::UsrOverlay => usroverlay().await,

lib/src/install.rs

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

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

0 commit comments

Comments
 (0)