Skip to content

Commit f063416

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 79cc6b4 commit f063416

File tree

4 files changed

+98
-9
lines changed

4 files changed

+98
-9
lines changed

lib/src/cli.rs

Lines changed: 85 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,11 @@ use schemars::schema_for;
2626
use serde::{Deserialize, Serialize};
2727

2828
use crate::deploy::RequiredHostSpec;
29-
use crate::lints;
3029
use crate::progress_jsonl::{ProgressWriter, RawProgressFd};
3130
use crate::spec::Host;
3231
use crate::spec::ImageReference;
3332
use crate::utils::sigpolicy_from_opt;
33+
use crate::{lints, reboot};
3434

3535
/// Shared progress options
3636
#[derive(Debug, Parser, PartialEq, Eq)]
@@ -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
@@ -835,6 +835,31 @@ async fn upgrade(opts: UpgradeOpts) -> Result<()> {
835835
println!("Staged update present, not changed.");
836836

837837
if opts.apply {
838+
// Check if we can do a soft-reboot instead of a full reboot
839+
let can_soft_reboot = host
840+
.status
841+
.staged
842+
.as_ref()
843+
.map(|s| s.soft_reboot_capable)
844+
.unwrap_or(false);
845+
846+
if can_soft_reboot {
847+
println!("Staged deployment is soft-reboot capable, performing soft-reboot...");
848+
849+
// Find the staged deployment
850+
let deployments_list = sysroot.deployments();
851+
let staged_deployment = deployments_list
852+
.iter()
853+
.find(|d| d.is_staged())
854+
.ok_or_else(|| anyhow::anyhow!("Failed to find staged deployment"))?;
855+
856+
// Prepare the soft-reboot using native ostree bindings
857+
let cancellable = ostree::gio::Cancellable::NONE;
858+
sysroot
859+
.sysroot
860+
.deployment_prepare_next_root(staged_deployment, false, cancellable)
861+
.context("Failed to prepare soft-reboot")?;
862+
}
838863
crate::reboot::reboot()?;
839864
}
840865
} else if booted_unchanged {
@@ -931,6 +956,33 @@ async fn switch(opts: SwitchOpts) -> Result<()> {
931956
sysroot.update_mtime()?;
932957

933958
if opts.apply {
959+
// Get updated status to check for soft-reboot capability
960+
let (_updated_deployments, updated_host) =
961+
crate::status::get_status(sysroot, Some(&booted_deployment))?;
962+
let can_soft_reboot = updated_host
963+
.status
964+
.staged
965+
.as_ref()
966+
.map(|s| s.soft_reboot_capable)
967+
.unwrap_or(false);
968+
969+
if can_soft_reboot {
970+
println!("Staged deployment is soft-reboot capable, performing soft-reboot...");
971+
972+
// Find the staged deployment
973+
let deployments_list = sysroot.deployments();
974+
let staged_deployment = deployments_list
975+
.iter()
976+
.find(|d| d.is_staged())
977+
.ok_or_else(|| anyhow::anyhow!("Failed to find staged deployment"))?;
978+
979+
// Prepare the soft-reboot using native ostree bindings
980+
let cancellable = ostree::gio::Cancellable::NONE;
981+
sysroot
982+
.sysroot
983+
.deployment_prepare_next_root(staged_deployment, false, cancellable)
984+
.context("Failed to prepare soft-reboot")?;
985+
}
934986
crate::reboot::reboot()?;
935987
}
936988

@@ -941,10 +993,40 @@ async fn switch(opts: SwitchOpts) -> Result<()> {
941993
#[context("Rollback")]
942994
async fn rollback(opts: RollbackOpts) -> Result<()> {
943995
let sysroot = &get_storage().await?;
944-
crate::deploy::rollback(sysroot).await?;
945996

946997
if opts.apply {
998+
// Get status before rollback to check soft-reboot capability
999+
let (_booted_deployment, _deployments, host) =
1000+
crate::status::get_status_require_booted(sysroot)?;
1001+
let can_soft_reboot = host
1002+
.status
1003+
.rollback
1004+
.as_ref()
1005+
.map(|r| r.soft_reboot_capable)
1006+
.unwrap_or(false);
1007+
1008+
// Perform the rollback
1009+
crate::deploy::rollback(sysroot).await?;
1010+
1011+
if can_soft_reboot {
1012+
println!("Rollback deployment is soft-reboot capable, performing soft-reboot...");
1013+
1014+
// For rollback, get the current first deployment after rollback
1015+
let deployments_list = sysroot.deployments();
1016+
let target_deployment = deployments_list
1017+
.first()
1018+
.ok_or_else(|| anyhow::anyhow!("No deployments found after rollback"))?;
1019+
1020+
// Prepare the soft-reboot using native ostree bindings
1021+
let cancellable = ostree::gio::Cancellable::NONE;
1022+
sysroot
1023+
.sysroot
1024+
.deployment_prepare_next_root(target_deployment, false, cancellable)
1025+
.context("Failed to prepare soft-reboot")?;
1026+
}
9471027
crate::reboot::reboot()?;
1028+
} else {
1029+
crate::deploy::rollback(sysroot).await?;
9481030
}
9491031

9501032
Ok(())

lib/src/reboot.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ pub(crate) fn reboot() -> anyhow::Result<()> {
1313
// Flush output streams
1414
let _ = std::io::stdout().flush();
1515
let _ = std::io::stderr().flush();
16-
Task::new("Rebooting system", "reboot").run()?;
16+
Task::new("Rebooting system", "systemd-run").args(["reboot"]).run()?;
1717
tracing::debug!("Initiated reboot, sleeping forever...");
1818
loop {
1919
std::thread::park();

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)