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 if opts.apply && host.status.staged.is_some() {
35 crate::reboot::reboot()?;
36 }
37 return Ok(());
38 }
39
40 let Some(target_imgref) = new_spec.image else {
41 anyhow::bail!("Target image is undefined")
42 };
43
44 const COMPOSEFS_SWITCH_JOURNAL_ID: &str = "7a6b5c4d3e2f1a0b9c8d7e6f5a4b3c2d1";
45
46 tracing::info!(
47 message_id = COMPOSEFS_SWITCH_JOURNAL_ID,
48 bootc.operation = "switch",
49 bootc.target_image = target_imgref.to_string(),
50 bootc.apply_mode = opts.apply,
51 "Starting composefs switch operation",
52 );
53
54 let repo = &*booted_cfs.repo;
55 let (image, img_config) = is_image_pulled(repo, &target_imgref).await?;
56
57 let use_unified = if opts.unified_storage_exp {
62 true
63 } else {
64 let booted_imgref = host.spec.image.as_ref();
65 let booted_unified = if let Some(booted) = booted_imgref {
66 crate::deploy::image_exists_in_unified_storage(storage, booted).await?
67 } else {
68 false
69 };
70 let target_unified =
71 crate::deploy::image_exists_in_unified_storage(storage, &target_imgref).await?;
72 booted_unified || target_unified
73 };
74
75 let do_upgrade_opts = DoUpgradeOpts {
76 soft_reboot: opts.soft_reboot,
77 apply: opts.apply,
78 download_only: false,
79 use_unified,
80 };
81
82 if let Some(cfg_verity) = image {
83 let action = validate_update(
84 storage,
85 booted_cfs,
86 &host,
87 img_config.manifest.config().digest().as_ref(),
88 &cfg_verity,
89 true,
90 )?;
91
92 match action {
93 UpdateAction::Skip => {
94 println!("No changes in image: {target_imgref:#}");
95 return Ok(());
96 }
97
98 UpdateAction::Proceed => {
99 return do_upgrade(
100 storage,
101 booted_cfs,
102 &host,
103 &target_imgref,
104 &do_upgrade_opts,
105 &img_config.manifest,
106 )
107 .await;
108 }
109 }
110 }
111
112 do_upgrade(
113 storage,
114 booted_cfs,
115 &host,
116 &target_imgref,
117 &do_upgrade_opts,
118 &img_config.manifest,
119 )
120 .await?;
121
122 Ok(())
123}