Skip to content

feat(switch): add --target-imgref to decouple pull source from upgrade origin - #2467

Open
Jorge-Polanco-Roque wants to merge 3 commits into
bootc-dev:mainfrom
Jorge-Polanco-Roque:feat/switch-target-imgref
Open

Jorge-Polanco-Roque wants to merge 3 commits into
bootc-dev:mainfrom
Jorge-Polanco-Roque:feat/switch-target-imgref

Conversation

@Jorge-Polanco-Roque

Copy link
Copy Markdown

Problem

Following a customer case: after losing connectivity to their registry, they side-loaded an OCI image (scp + podman load) and applied it with bootc switch --transport containers-storage <img>. That worked for the immediate switch, but subsequent upgrades then tried to fetch from containers-storage — because the source used to pull is also persisted as the origin for upgrades. There was no way to say "pull from here now, but track this other imgref for upgrades."

Closes: #2464

Approach

Add a --target-imgref option (plus --target-transport, defaulting to registry) to bootc switch, mirroring what bootc install already exposes via InstallTargetOpts. This decouples two concepts that switch previously conflated:

  • source — where the image is pulled from now (--transport + the positional target).
  • origin — the imgref persisted for future upgrades (--target-imgref if given, otherwise the source).

The underlying mechanism already existed: deploy::pull/pull_unified accept a target_imgref that install uses and switch was passing as None. This change wires switch to build that target ref the same way install does, so no new pull machinery is introduced.

The experimental composefs backend currently dereferences a single ref through do_upgrade; rather than silently ignore the new option there, it returns a clear error that --target-imgref is not yet supported on that backend.

Testing

  • Added test_parse_switch_target_imgref (clap parsing + propagation through both helpers): confirms source stays on containers-storage while the target-imgref resolves to the registry transport.
  • Built and tested bootc-lib on Fedora (matching CI deps): cargo test -p bootc-lib passes, cargo fmt --check clean, and cargo clippy introduces no new warnings in the touched files.

Notes

Open questions for maintainers, happy to adjust: whether to keep --target-transport (install parity) or require the transport inside the imgref; the conflicts_with = from_downloaded guard; and the composefs-backend follow-up.

Disclosure: this change was prepared with AI assistance for code review and drafting; a human authored and reviewed it and signed off (DCO).

…e origin

When an image is side-loaded (e.g. scp + podman load into containers-storage
after losing registry connectivity) and applied with
`bootc switch --transport containers-storage <img>`, bootc would then keep
fetching upgrades from containers-storage.

Add `--target-imgref` (and `--target-transport`, mirroring `bootc install`)
so the image can be pulled from a local source now while a different imgref is
persisted as the origin for future upgrades. Reuses the existing target_imgref
mechanism that deploy::pull already accepted and switch passed as None.

Closes: bootc-dev#2464

Assisted-by: Claude (AI)
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Signed-off-by: Jorge Polanco <[email protected]>
@bootc-bot
bootc-bot Bot requested a review from cgwalters September 16, 2026 14:15
Ran `cargo xtask update-generated` after adding the --target-imgref and
--target-transport options so the generated man page matches the CLI and
the validate check passes.

Assisted-by: Claude (AI)
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Signed-off-by: Jorge Polanco <[email protected]>
@github-actions github-actions Bot added the area/documentation Updates to the documentation label Sep 16, 2026
@cgwalters
cgwalters requested a balanced review from Copilot September 16, 2026 22:21

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The unchanged-spec fast path defeats the primary recovery scenario, and some option combinations are silently ignored.

Get a fresh assessment by requesting another Copilot review.

Pull request overview

Adds separate pull-source and future-upgrade-origin handling to bootc switch.

Changes:

  • Adds --target-imgref and --target-transport.
  • Passes the target origin through OSTree pull paths.
  • Rejects --target-imgref on composefs.
File summaries
File Description
docs/src/man/bootc-switch.8.md Documents the new options.
crates/lib/src/cli.rs Implements parsing and OSTree switching behavior.
crates/lib/src/bootc_composefs/switch.rs Reports unsupported composefs usage.
Review details

Suppressed comments (1)

crates/lib/src/cli.rs:182

  • An explicitly supplied --target-transport is silently ignored when --target-imgref is absent because target_imgref_for_switch returns before parsing it. This accepts meaningless input (including invalid transport names) and can leave users believing a different upgrade origin was recorded. Make this option require --target-imgref (or define and implement standalone semantics).
    #[clap(long, default_value = "registry")]
    pub(crate) target_transport: String,
  • Files reviewed: 3/3 changed files
  • Comments generated: 2
  • Review effort level: Balanced

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread crates/lib/src/cli.rs Outdated
Comment on lines +176 to +177
#[clap(long, conflicts_with = "from_downloaded")]
pub(crate) target_imgref: Option<String>,
Comment thread crates/lib/src/cli.rs
Comment on lines +1563 to +1565
let origin_ref = match target_imgref.as_ref() {
Some(t) => ImageReference::from(t.clone()),
None => source.clone(),

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, this is a very valid issue and a little bit more complicated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For composefs backend we can add another field in the JSON we store in /run/composefs/staged-deployment

…-source changes

Address review: reject --target-imgref with the no-pull --mutate-in-place
mode (conflicts_with), and don't short-circuit switch as a no-op when only
the origin is unchanged but the pull source differs — that is exactly the
containers-storage recovery case from bootc-dev#2464.

Assisted-by: Claude (AI)
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Signed-off-by: Jorge Polanco <[email protected]>

@Johan-Liebert1 Johan-Liebert1 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for starting this

}

if opts.target_imgref.is_some() {
anyhow::bail!("--target-imgref is not yet supported with the composefs backend");

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's keep both backends in sync

Comment thread crates/lib/src/cli.rs
));
}

#[test]

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We'd rather have tmt integration tests. Please take a look at the tmt directory in repository root

Comment thread crates/lib/src/cli.rs
Comment on lines +1563 to +1565
let origin_ref = match target_imgref.as_ref() {
Some(t) => ImageReference::from(t.clone()),
None => source.clone(),

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, this is a very valid issue and a little bit more complicated

Comment thread crates/lib/src/cli.rs
Comment on lines +1563 to +1565
let origin_ref = match target_imgref.as_ref() {
Some(t) => ImageReference::from(t.clone()),
None => source.clone(),

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For composefs backend we can add another field in the JSON we store in /run/composefs/staged-deployment

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/documentation Updates to the documentation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add --target-imgref option to bootc switch

3 participants