Skip to content

Commit b5ec31d

Browse files
committed
cli: add support for soft-reboots
Co-authored-by: Colin Walters <[email protected]> Signed-off-by: Joseph Marrero Corchado <[email protected]>
1 parent e7d15d4 commit b5ec31d

File tree

3 files changed

+84
-7
lines changed

3 files changed

+84
-7
lines changed

lib/src/cli.rs

Lines changed: 72 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -536,7 +536,7 @@ pub(crate) enum Opt {
536536
Note on Rollbacks and the `/etc` Directory:
537537
538538
When you perform a rollback (e.g., with `bootc rollback`), any
539-
changes made to files in the `/etc` directory wont carry over
539+
changes made to files in the `/etc` directory won't carry over
540540
to the rolled-back deployment. The `/etc` files will revert
541541
to their state from that previous deployment instead.
542542
@@ -715,6 +715,54 @@ pub(crate) fn require_root(is_container: bool) -> Result<()> {
715715
Ok(())
716716
}
717717

718+
/// Check if a deployment can perform a soft reboot
719+
fn can_perform_soft_reboot(deployment: Option<&crate::spec::BootEntry>) -> bool {
720+
deployment.map(|d| d.soft_reboot_capable).unwrap_or(false)
721+
}
722+
723+
/// Prepare and execute a soft reboot for the given deployment
724+
#[context("Preparing soft reboot")]
725+
fn prepare_soft_reboot(
726+
sysroot: &crate::store::Storage,
727+
deployment: &ostree::Deployment,
728+
) -> Result<()> {
729+
let cancellable = ostree::gio::Cancellable::NONE;
730+
sysroot
731+
.sysroot
732+
.deployment_prepare_next_root(deployment, false, cancellable)
733+
.context("Failed to prepare soft-reboot")?;
734+
Ok(())
735+
}
736+
737+
/// Perform a soft reboot for a staged deployment
738+
#[context("Soft reboot staged deployment")]
739+
fn soft_reboot_staged(sysroot: &crate::store::Storage) -> Result<()> {
740+
println!("Staged deployment is soft-reboot capable, performing soft-reboot...");
741+
742+
let deployments_list = sysroot.deployments();
743+
let staged_deployment = deployments_list
744+
.iter()
745+
.find(|d| d.is_staged())
746+
.ok_or_else(|| anyhow::anyhow!("Failed to find staged deployment"))?;
747+
748+
prepare_soft_reboot(sysroot, staged_deployment)?;
749+
Ok(())
750+
}
751+
752+
/// Perform a soft reboot for a rollback deployment
753+
#[context("Soft reboot rollback deployment")]
754+
fn soft_reboot_rollback(sysroot: &crate::store::Storage) -> Result<()> {
755+
println!("Rollback deployment is soft-reboot capable, performing soft-reboot...");
756+
757+
let deployments_list = sysroot.deployments();
758+
let target_deployment = deployments_list
759+
.first()
760+
.ok_or_else(|| anyhow::anyhow!("No deployments found after rollback"))?;
761+
762+
prepare_soft_reboot(sysroot, target_deployment)?;
763+
Ok(())
764+
}
765+
718766
/// A few process changes that need to be made for writing.
719767
/// IMPORTANT: This may end up re-executing the current process,
720768
/// so anything that happens before this should be idempotent.
@@ -835,6 +883,9 @@ async fn upgrade(opts: UpgradeOpts) -> Result<()> {
835883
println!("Staged update present, not changed.");
836884

837885
if opts.apply {
886+
if can_perform_soft_reboot(host.status.staged.as_ref()) {
887+
soft_reboot_staged(sysroot)?;
888+
}
838889
crate::reboot::reboot()?;
839890
}
840891
} else if booted_unchanged {
@@ -931,6 +982,13 @@ async fn switch(opts: SwitchOpts) -> Result<()> {
931982
sysroot.update_mtime()?;
932983

933984
if opts.apply {
985+
// Get updated status to check for soft-reboot capability
986+
let (_updated_deployments, updated_host) =
987+
crate::status::get_status(sysroot, Some(&booted_deployment))?;
988+
989+
if can_perform_soft_reboot(updated_host.status.staged.as_ref()) {
990+
soft_reboot_staged(sysroot)?;
991+
}
934992
crate::reboot::reboot()?;
935993
}
936994

@@ -941,10 +999,22 @@ async fn switch(opts: SwitchOpts) -> Result<()> {
941999
#[context("Rollback")]
9421000
async fn rollback(opts: RollbackOpts) -> Result<()> {
9431001
let sysroot = &get_storage().await?;
944-
crate::deploy::rollback(sysroot).await?;
9451002

9461003
if opts.apply {
1004+
// Get status before rollback to check soft-reboot capability
1005+
let (_booted_deployment, _deployments, host) =
1006+
crate::status::get_status_require_booted(sysroot)?;
1007+
let can_soft_reboot = can_perform_soft_reboot(host.status.rollback.as_ref());
1008+
1009+
// Perform the rollback
1010+
crate::deploy::rollback(sysroot).await?;
1011+
1012+
if can_soft_reboot {
1013+
soft_reboot_rollback(sysroot)?;
1014+
}
9471015
crate::reboot::reboot()?;
1016+
} else {
1017+
crate::deploy::rollback(sysroot).await?;
9481018
}
9491019

9501020
Ok(())

lib/src/spec.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,9 @@ pub struct BootEntry {
176176
pub incompatible: bool,
177177
/// Whether this entry will be subject to garbage collection
178178
pub pinned: bool,
179+
/// This is true if (relative to the booted system) this is a possible target for a soft reboot
180+
#[serde(default)]
181+
pub soft_reboot_capable: bool,
179182
/// The container storage backend
180183
#[serde(default)]
181184
pub store: Option<Store>,
@@ -517,6 +520,7 @@ mod tests {
517520
image: None,
518521
cached_update: None,
519522
incompatible: false,
523+
soft_reboot_capable: false,
520524
pinned: false,
521525
store: None,
522526
ostree: None,

lib/src/status.rs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -144,10 +144,13 @@ fn boot_entry_from_deployment(
144144
(None, CachedImageStatus::default(), false)
145145
};
146146

147+
let soft_reboot_capable = sysroot.sysroot.deployment_can_soft_reboot(deployment);
148+
147149
let r = BootEntry {
148150
image,
149151
cached_update,
150152
incompatible,
153+
soft_reboot_capable,
151154
store,
152155
pinned: deployment.is_pinned(),
153156
ostree: Some(crate::spec::BootEntryOstree {
@@ -228,17 +231,17 @@ pub(crate) fn get_status(
228231
other,
229232
};
230233

231-
let staged = deployments
232-
.staged
234+
let booted = booted_deployment
233235
.as_ref()
234236
.map(|d| boot_entry_from_deployment(sysroot, d))
235237
.transpose()
236-
.context("Staged deployment")?;
237-
let booted = booted_deployment
238+
.context("Booted deployment")?;
239+
let staged = deployments
240+
.staged
238241
.as_ref()
239242
.map(|d| boot_entry_from_deployment(sysroot, d))
240243
.transpose()
241-
.context("Booted deployment")?;
244+
.context("Staged deployment")?;
242245
let rollback = deployments
243246
.rollback
244247
.as_ref()

0 commit comments

Comments
 (0)