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