bootc_lib/bootc_composefs/
switch.rs1use anyhow::{Context, Result};
2use fn_error_context::context;
3
4use crate::{
5 bootc_composefs::{
6 state::update_target_imgref_in_origin,
7 status::get_composefs_status,
8 update::{DoUpgradeOpts, UpdateAction, do_upgrade, is_image_pulled, validate_update},
9 },
10 cli::{SwitchOpts, imgref_for_switch},
11 store::{BootedComposefs, Storage},
12};
13
14#[context("Composefs Switching")]
15pub(crate) async fn switch_composefs(
16 opts: SwitchOpts,
17 storage: &Storage,
18 booted_cfs: &BootedComposefs,
19) -> Result<()> {
20 let target = imgref_for_switch(&opts)?;
21
22 let host = get_composefs_status(storage, booted_cfs)
24 .await
25 .context("Getting composefs deployment status")?;
26
27 let new_spec = {
28 let mut new_spec = host.spec.clone();
29 new_spec.image = Some(target.clone());
30 new_spec
31 };
32
33 if new_spec == host.spec {
34 println!("Image specification is unchanged.");
35 return Ok(());
36 }
37
38 let Some(target_imgref) = new_spec.image else {
39 anyhow::bail!("Target image is undefined")
40 };
41
42 const COMPOSEFS_SWITCH_JOURNAL_ID: &str = "7a6b5c4d3e2f1a0b9c8d7e6f5a4b3c2d1";
43
44 tracing::info!(
45 message_id = COMPOSEFS_SWITCH_JOURNAL_ID,
46 bootc.operation = "switch",
47 bootc.target_image = target_imgref.to_string(),
48 bootc.apply_mode = opts.apply,
49 "Starting composefs switch operation",
50 );
51
52 let repo = &*booted_cfs.repo;
53 let (image, img_config) = is_image_pulled(repo, &target_imgref).await?;
54
55 let do_upgrade_opts = DoUpgradeOpts {
56 soft_reboot: opts.soft_reboot,
57 apply: opts.apply,
58 download_only: false,
59 };
60
61 if let Some(cfg_verity) = image {
62 let action = validate_update(
63 storage,
64 booted_cfs,
65 &host,
66 img_config.manifest.config().digest().digest(),
67 &cfg_verity,
68 true,
69 )?;
70
71 match action {
72 UpdateAction::Skip => {
73 println!("No changes in image: {target_imgref:#}");
74 return Ok(());
75 }
76
77 UpdateAction::Proceed => {
78 return do_upgrade(
79 storage,
80 booted_cfs,
81 &host,
82 &target_imgref,
83 &img_config,
84 &do_upgrade_opts,
85 )
86 .await;
87 }
88
89 UpdateAction::UpdateOrigin => {
90 println!("Image already in composefs repository");
92 println!("Updating target image reference");
93 return update_target_imgref_in_origin(storage, booted_cfs, &target_imgref);
94 }
95 }
96 }
97
98 do_upgrade(
99 storage,
100 booted_cfs,
101 &host,
102 &target_imgref,
103 &img_config,
104 &do_upgrade_opts,
105 )
106 .await?;
107
108 Ok(())
109}