1
0
mirror of https://github.com/containers/bootc.git synced 2026-02-05 06:45:13 +01:00

lib: Add --from-downloaded flag for bootc upgrade

Add a new --from-downloaded flag to bootc upgrade that allows users to
unlock a staged deployment created with --download-only without fetching
updates from the container image source.

This provides a way to apply already-downloaded updates without triggering
a fetch operation, which is useful for scheduled maintenance workflows where
the update was downloaded earlier and should now be applied at a scheduled
time.

Usage:
  # Download update without applying
  bootc upgrade --download-only

  # Later: Apply the staged update (without fetching from image source)
  bootc upgrade --from-downloaded

  # Or: Apply staged update and reboot immediately
  bootc upgrade --from-downloaded --apply

The flag conflicts with --check and --download-only as those operations
have different purposes. It can be combined with --apply to immediately
reboot after unlocking the staged deployment.

This commit also updates the documentation (upgrades.md) to describe all
three ways to apply a download-only update, and updates the download-only
test case (test-25) to use --from-downloaded instead of plain
'bootc upgrade' when clearing the download-only flag.

Assisted-by: Claude Code (Sonnet 4.5)
Signed-off-by: Wei Shi <wshi@redhat.com>
This commit is contained in:
Wei Shi
2025-12-11 18:05:56 +08:00
committed by Colin Walters
parent bf08c17584
commit a5784832d0
7 changed files with 79 additions and 22 deletions

View File

@@ -276,6 +276,9 @@ pub(crate) async fn upgrade_composefs(
if opts.download_only {
anyhow::bail!("--download-only is not yet supported for composefs backend");
}
if opts.from_downloaded {
anyhow::bail!("--from-downloaded is not yet supported for composefs backend");
}
let host = get_composefs_status(storage, composefs)
.await

View File

@@ -108,6 +108,14 @@ pub(crate) struct UpgradeOpts {
#[clap(long, conflicts_with_all = ["check", "apply"])]
pub(crate) download_only: bool,
/// Apply a staged deployment that was previously downloaded with --download-only.
///
/// This unlocks the staged deployment without fetching updates from the container image source.
/// The deployment will be applied on the next shutdown or reboot. Use with --apply to
/// reboot immediately.
#[clap(long, conflicts_with_all = ["check", "download_only"])]
pub(crate) from_downloaded: bool,
#[clap(flatten)]
pub(crate) progress: ProgressOptions,
}
@@ -933,6 +941,28 @@ async fn upgrade(
let staged = host.status.staged.as_ref();
let staged_image = staged.as_ref().and_then(|s| s.image.as_ref());
let mut changed = false;
// Handle --from-downloaded: unlock existing staged deployment without fetching from image source
if opts.from_downloaded {
let ostree = storage.get_ostree()?;
let staged_deployment = ostree
.staged_deployment()
.ok_or_else(|| anyhow::anyhow!("No staged deployment found"))?;
if staged_deployment.is_finalization_locked() {
ostree.change_finalization(&staged_deployment)?;
println!("Staged deployment will now be applied on reboot");
} else {
println!("Staged deployment is already set to apply on reboot");
}
handle_staged_soft_reboot(booted_ostree, opts.soft_reboot, &host)?;
if opts.apply {
crate::reboot::reboot()?;
}
return Ok(());
}
if opts.check {
let imgref = imgref.clone().into();
let mut imp = crate::deploy::new_importer(repo, &imgref).await?;

View File

@@ -65,6 +65,10 @@ Soft reboot allows faster system restart by avoiding full hardware reboot when p
Download and stage the update without applying it
**--from-downloaded**
Apply a staged deployment that was previously downloaded with --download-only
<!-- END GENERATED OPTIONS -->
# EXAMPLES

View File

@@ -6,7 +6,7 @@ updates from a registry and booting into them, while supporting rollback.
## The `bootc upgrade` verb
This will query the registry and queue an updated container image for the next boot.
This will query the container image source and queue an updated container image for the next boot.
This is backed today by ostree, implementing an A/B style upgrade system.
Changes to the base image are staged, and the running system is not
@@ -23,7 +23,7 @@ them on the next reboot:
bootc upgrade --download-only
```
This will pull the new container image from the registry and create a staged deployment
This will pull the new container image from the container image source and create a staged deployment
in download-only mode. The deployment will not be applied on shutdown or reboot until
you explicitly apply it.
@@ -40,24 +40,36 @@ In the output, you'll see `Download-only: yes` for deployments in download-only
#### Applying download-only updates
There are two ways to apply a staged update that is in download-only mode:
There are three ways to apply a staged update that is in download-only mode:
**Option 1: Apply immediately with reboot**
**Option 1: Apply the staged update without checking for newer updates**
```shell
bootc upgrade --apply
bootc upgrade --from-downloaded
```
This will clear the download-only flag and immediately reboot into the staged deployment.
This unlocks the staged deployment for automatic application on the next shutdown or reboot,
without fetching updates from the container image source. This is useful when you want to apply the
already-downloaded update at a scheduled time.
**Option 2: Clear download-only for automatic application**
**Option 2: Apply the staged update and reboot immediately**
```shell
bootc upgrade --from-downloaded --apply
```
This unlocks the staged deployment and immediately reboots into it, without checking for
newer updates.
**Option 3: Check for newer updates and apply**
```shell
bootc upgrade
```
Running `bootc upgrade` without flags on a staged deployment in download-only mode will
clear the flag. The deployment will then be applied automatically on the next shutdown or reboot.
Running `bootc upgrade` without flags will pull from the container image source to check for updates.
If the staged deployment matches the latest available update, it will be unlocked. If a newer update is
available, the staged deployment will be replaced with the newer version.
#### Checking for updates without side effects
@@ -84,15 +96,23 @@ bootc status --verbose
# 3. Test or wait for maintenance window...
# 4. Apply the update (choose one):
# Option A: Clear download-only flag and let it apply on next shutdown
bootc upgrade
# Option A: Apply staged update without fetching from image source
bootc upgrade --from-downloaded
# Option B: Apply immediately with reboot
bootc upgrade --apply
# Option B: Apply staged update and reboot immediately (without fetching from image source)
bootc upgrade --from-downloaded --apply
# Option C: Check for newer updates first, then apply
bootc upgrade
```
**Important notes**:
- **Image source check difference**: `bootc upgrade --from-downloaded` does NOT fetch from the
container image source to check for newer updates, while `bootc upgrade` always does.
Use `--from-downloaded` when you want to apply the specific version you already downloaded,
regardless of whether newer updates are available.
- If you reboot before applying a download-only update, the system will boot into the
current deployment and the staged deployment will be discarded. However, the downloaded image
data remains cached, so re-running `bootc upgrade --download-only` will be fast and won't

View File

@@ -95,12 +95,12 @@ execute:
test:
- /tmt/tests/tests/test-25-soft-reboot
/plan-25-download-only-upgrade:
/plan-26-download-only-upgrade:
summary: Execute download-only upgrade tests
discover:
how: fmf
test:
- /tmt/tests/tests/test-25-download-only-upgrade
- /tmt/tests/tests/test-26-download-only-upgrade
/plan-27-custom-selinux-policy:
summary: Execute custom selinux policy test

View File

@@ -1,4 +1,4 @@
# number: 25
# number: 26
# tmt:
# summary: Execute download-only upgrade tests
# duration: 40m
@@ -13,7 +13,7 @@
# reboot (should still boot into v1, staged deployment discarded)
# verify staged deployment is null (discarded on reboot)
# bootc upgrade --download-only (re-stage v2 in download-only mode)
# bootc upgrade (clear download-only mode)
# bootc upgrade --from-downloaded (clear download-only mode without fetching from image source)
# reboot (should boot into v2)
#
use std assert
@@ -113,9 +113,9 @@ def third_boot [] {
assert ($status_json.status.staged != null) "Staged deployment should exist"
assert ($status_json.status.staged.downloadOnly) "Staged deployment should be in download-only mode"
# Now clear download-only mode by running upgrade without flags
print "Clearing download-only mode with bootc upgrade"
bootc upgrade
# Now clear download-only mode by running upgrade --from-downloaded (without fetching from image source)
print "Clearing download-only mode with bootc upgrade --from-downloaded"
bootc upgrade --from-downloaded
# Verify via JSON that deployment is not in download-only mode
let status_after_json = bootc status --json | from json

View File

@@ -41,10 +41,10 @@
duration: 30m
test: nu booted/test-soft-reboot.nu
/test-25-download-only-upgrade:
/test-26-download-only-upgrade:
summary: Execute download-only upgrade tests
duration: 40m
test: nu booted/test-25-download-only-upgrade.nu
test: nu booted/test-download-only-upgrade.nu
/test-27-custom-selinux-policy:
summary: Execute custom selinux policy test