feat(switch): add --target-imgref to decouple pull source from upgrade origin - #2467
Jorge-Polanco-Roque wants to merge 3 commits into
Conversation
…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]>
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]>
There was a problem hiding this comment.
🟡 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-imgrefand--target-transport. - Passes the target origin through OSTree pull paths.
- Rejects
--target-imgrefon 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-transportis silently ignored when--target-imgrefis absent becausetarget_imgref_for_switchreturns 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.
| #[clap(long, conflicts_with = "from_downloaded")] | ||
| pub(crate) target_imgref: Option<String>, |
| let origin_ref = match target_imgref.as_ref() { | ||
| Some(t) => ImageReference::from(t.clone()), | ||
| None => source.clone(), |
There was a problem hiding this comment.
Yeah, this is a very valid issue and a little bit more complicated
There was a problem hiding this comment.
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
left a comment
There was a problem hiding this comment.
Thanks for starting this
| } | ||
|
|
||
| if opts.target_imgref.is_some() { | ||
| anyhow::bail!("--target-imgref is not yet supported with the composefs backend"); |
There was a problem hiding this comment.
Let's keep both backends in sync
| )); | ||
| } | ||
|
|
||
| #[test] |
There was a problem hiding this comment.
We'd rather have tmt integration tests. Please take a look at the tmt directory in repository root
| let origin_ref = match target_imgref.as_ref() { | ||
| Some(t) => ImageReference::from(t.clone()), | ||
| None => source.clone(), |
There was a problem hiding this comment.
Yeah, this is a very valid issue and a little bit more complicated
| let origin_ref = match target_imgref.as_ref() { | ||
| Some(t) => ImageReference::from(t.clone()), | ||
| None => source.clone(), |
There was a problem hiding this comment.
For composefs backend we can add another field in the JSON we store in /run/composefs/staged-deployment
Problem
Following a customer case: after losing connectivity to their registry, they side-loaded an OCI image (scp +
podman load) and applied it withbootc switch --transport containers-storage <img>. That worked for the immediate switch, but subsequent upgrades then tried to fetch fromcontainers-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-imgrefoption (plus--target-transport, defaulting toregistry) tobootc switch, mirroring whatbootc installalready exposes viaInstallTargetOpts. This decouples two concepts thatswitchpreviously conflated:--transport+ the positional target).--target-imgrefif given, otherwise the source).The underlying mechanism already existed:
deploy::pull/pull_unifiedaccept atarget_imgrefthatinstalluses andswitchwas passing asNone. This change wiresswitchto build that target ref the same wayinstalldoes, 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-imgrefis not yet supported on that backend.Testing
test_parse_switch_target_imgref(clap parsing + propagation through both helpers): confirms source stays oncontainers-storagewhile the target-imgref resolves to theregistrytransport.bootc-libon Fedora (matching CI deps):cargo test -p bootc-libpasses,cargo fmt --checkclean, andcargo clippyintroduces 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; theconflicts_with = from_downloadedguard; 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).