Repository navigation
switch: Add --target-imgref to Support a separate update image reference - #2603
HarshwardhanPatil07 wants to merge 8 commits into
Conversation
75ea0d9 to
8055a2f
Compare
There was a problem hiding this comment.
🟡 Changes recommended
Unresolved issues can misidentify image sources, lose or strand pending origin changes, and break addon activation, unified-store updates, or soft-reboot behavior.
9 open issues
Prevent addon mutations from bypassing pending origin activation · New Keep status and recovery usable with stale origin transactions · New Normalize legacy manifest hashes before comparing image identity · New Allow auto soft reboot to leave origin changes staged · New Preserve pending origin changes until replacement succeeds · New Defer soft-reboot restrictions until the activation target is known · New Disambiguate registry host-and-port references from transport syntax · New Preserve origin-only no-op when target image reference is absent · New Preserve unified-store mode when importing the target image · New
Review effort: Balanced
a63c7f4 to
b00d6b9
Compare
| .booted | ||
| .as_ref() | ||
| .context("No booted deployment")?; | ||
| if booted.require_composefs()?.verity != self.deployment |
There was a problem hiding this comment.
when will this not be true? afaics, we're only ever setting OriginChange::deployment to the booted depl verity
There was a problem hiding this comment.
it is always true when we create the pending change. The check is for when we read it after a reboot: a download-only origin change can remain on disk for deployment A, while the user boots rollback deployment B. In that case, we should ignore A’s pending change rather than show it as staged for B.
Should i add a comment there to make i more clear?
| opts, | ||
| UkiAddonCliOpts::Add { .. } | UkiAddonCliOpts::Remove { .. } | ||
| ) { | ||
| anyhow::ensure!( |
There was a problem hiding this comment.
why not? We allow it when we have a staged deployment
There was a problem hiding this comment.
This guard applies only while an origin-only change is pending, addon changes with a real staged deployment still work.
|
Also the first two commits need proper messages and not just |
b00d6b9 to
2c72014
Compare
Allow switch to accept an update origin separate from the image fetched now. Pass the target through OSTree staging and composefs switch so later updates can follow the recorded reference. Signed-off-by: Pragyan Poudyal <pragyanpoudyal41999@gmail.com> Co-authored-by: HarshwardhanPatil07 <harshpat@redhat.com>
Keep the pulled image name alongside a staged deployment when switch chooses a different update origin. This prepares the staged state to distinguish the delivery source from the recorded origin. Signed-off-by: Pragyan Poudyal <pragyanpoudyal41999@gmail.com> Co-authored-by: HarshwardhanPatil07 <harshpat@redhat.com>
2c72014 to
462a54b
Compare
Offline delivery should accept the same registry names users pass to install. Normalize target references before fetching, reject incompatible options, and avoid advertising enforced verification on an unsupported composefs path. Assisted-by: AI Signed-off-by: HarshwardhanPatil07 <harshpat@redhat.com>
A cached import must retain the reference used by the deployment origin so cleanup does not discard its image metadata. Handle this in the importer, respecting set_no_imgref, so direct and unified pulls share the same behavior. Reuse the existing pull preparation helper. Assisted-by: AI Signed-off-by: HarshwardhanPatil07 <harshpat@redhat.com>
462a54b to
7818ea5
Compare
The update origin cannot identify which offline delivery was staged. Record the pull source in each deployment origin and compare both references before fetching, preserving download-only locks on repeats. Route OSTree imports through the source while retaining the target. Cover offline repeats, source replacement, activation, and provenance after reboot on both backends. Assisted-by: AI Signed-off-by: HarshwardhanPatil07 <harshpat@redhat.com>
Recording an origin is only useful if later updates actually use it. Extend the reboot test to check unchanged content and a newer image at the recorded registry after completing an offline delivery. Assisted-by: AI Signed-off-by: HarshwardhanPatil07 <harshpat@redhat.com>
Explain how to obtain an image from a temporary source while retaining the normal update origin. Document repeat behavior, activation, and user-visible restrictions with local-storage and registry examples. Assisted-by: AI Signed-off-by: HarshwardhanPatil07 <harshpat@redhat.com>
A redirected deployment records its update origin while containers-storage keeps the delivery name. Detect the stored host image by its OCI config ID and retain that ID during cleanup, so later updates keep using unified storage without requiring another explicit opt-in. Use the existing deployment metadata for both backends without adding persistent state or changing image aliases. Assisted-by: AI Signed-off-by: HarshwardhanPatil07 <harshpat@redhat.com>
7818ea5 to
f511178
Compare
| location = "localhost:5555" | ||
| insecure = true | ||
| ' | save /etc/containers/registries.conf.d/target-imgref-test.conf | ||
| podman run -d --name $registry -p 127.0.0.1:5555:5000 docker.io/library/registry:2 |
There was a problem hiding this comment.
Is this necessary for testing? We'd want to limit internet access in our tests to as little as possible to prevent flakes. We could just have another containers-storage:<image> as the target imgref
| } | ||
|
|
||
| /// Parse a transport-prefixed reference, defaulting bare image names to the registry. | ||
| pub(crate) fn parse_image_reference(value: &str) -> Result<containers_image_proxy::ImageReference> { |
There was a problem hiding this comment.
Why do we need this function? Is there some case where containers_image_proxy::ImageReference::try_from fails?
| continue; | ||
| } | ||
|
|
||
| if let Some(id) = deployment.container_image_id(storage)? { |
There was a problem hiding this comment.
We should do this below where we read the origin. We can just check if the origin contains our new key, if it does use that as the image name else use the default one
| ); | ||
| let mut config = tini::Ini::new() | ||
| .section("origin") | ||
| // The composefs pull path does not yet enforce the spec signature mode. |
There was a problem hiding this comment.
Let's not remove the TODO from here
| target_imgref: &ImageReference, | ||
| source_imgref: Option<&ImageReference>, |
There was a problem hiding this comment.
probably worth switching the variable names here
| current_origin: Option<&ImageReference>, | ||
| previous_source: Option<&str>, | ||
| ) -> Result<bool> { | ||
| let Some(current_origin) = current_origin else { |
There was a problem hiding this comment.
Shouldn't the current_origin always exist?
| source: &ImageReference, | ||
| target: Option<&ImageReference>, | ||
| current_origin: Option<&ImageReference>, | ||
| previous_source: Option<&str>, |
There was a problem hiding this comment.
These variables are poorly named, not entirely obvious what previous_source is. Renaming or at least a doc comment on the function would help. Also, why we can accept ImageReference for previous_source right to keep it all consistent?
| mod tests { | ||
| use super::*; | ||
|
|
||
| #[test] |
There was a problem hiding this comment.
These should all be tmt integration tests imo
| /// Detect a deployment's stored image even when its pull source and origin differ. | ||
| pub(crate) async fn deployment_uses_unified_storage( | ||
| store: &Storage, | ||
| deployment: Option<&crate::spec::BootEntry>, |
There was a problem hiding this comment.
Having an Option here doesn't make sense
| return Ok(false); | ||
| }; | ||
| let Some(id) = deployment.container_image_id(store)? else { | ||
| return Ok(false); |
There was a problem hiding this comment.
Shouldn't this be an error if we fail to find an image associated with a deployment or am I missing something?
There was a problem hiding this comment.
🟡 Changes recommended
Active issues misparse valid transport-prefixed targets and cause offline switches from host-only containers-storage sources to fail on OSTree hosts using unified storage.
2 open issues
9 resolved since last review
Preserve unified-store mode when importing the target image Preserve origin-only no-op when target image reference is absent Disambiguate registry host-and-port references from transport syntax Defer soft-reboot restrictions until the activation target is known Preserve pending origin changes until replacement succeeds Allow auto soft reboot to leave origin changes staged Normalize legacy manifest hashes before comparing image identity Keep status and recovery usable with stale origin transactions Prevent addon mutations from bypassing pending origin activation
Review effort: Balanced
| || crate::deploy::deployment_uses_unified_storage( | ||
| storage, | ||
| host.status.staged.as_ref().or(host.status.booted.as_ref()), | ||
| ) | ||
| .await? |
| // is named after a transport. Use oci:./5000/os for an OCI relative path. | ||
| let registry_port = value.split_once('/').is_some_and(|(authority, _)| { | ||
| authority | ||
| .rsplit_once(':') |

Adds --target-imgref to bootc switch for OSTree and composefs, allowing an image to be downloaded from a temporary source while keeping a different registry for future updates.
Manually verified composefs UKI switching, repeated switches, and reboot with the previous deployment available for rollback. Verified OSTree download-only staging and both references in its .origin file.
Fixes #2464