Skip to content

copy: add OmitPrimaryManifestUpdateIfUnchanged option - #1033

Open
satwiksps wants to merge 1 commit into
podman-container-tools:mainfrom
satwiksps:issue-918-omit-manifest-update
Open

copy: add OmitPrimaryManifestUpdateIfUnchanged option#1033
satwiksps wants to merge 1 commit into
podman-container-tools:mainfrom
satwiksps:issue-918-omit-manifest-update

Conversation

@satwiksps

@satwiksps satwiksps commented Jul 28, 2026

Copy link
Copy Markdown

Copying an image that is already at the destination still writes the image's primary manifest unconditionally. registries that enforce tag immutability (Harbor, Artifactory, Quay) reject that write, so a copy that changes nothing fails at the very last step, even though every child manifest was correctly skipped

This adds OmitPrimaryManifestUpdateIfUnchanged to copy.Options. when set, the copy reads the manifest currently stored at the destination and skips the write if it is byte-for-byte identical. check lives in a new (*copier).putManifest helper that both single.go and multiple.go call, so the two paths can't drift apart and the manifestList = attemptedManifestList flow isn't duplicated. deliberately only applies to the primary manifest. Instances of a manifest list are written by digest, which those registries don't restrict, so checking each one would cost an extra read per instance for nothing, the helper documents that rationale. The option name says "Primary" for the same reason: "omit any unchanged manifest update" wouldn't be accurate

copy_test.go covers the option on and off, changed/unchanged/absent destination manifests, and that instances are never skipped

@github-actions github-actions Bot added the image Related to "image" package label Jul 28, 2026
@satwiksps

Copy link
Copy Markdown
Author

Hi @mtrmac ,I added optimization check for top-level manifests but skipping overhead for child images (pushed by digest), just as you suggested.
I like to add a test to prevent regression, but it requires a mock destination that can return the existing manifest via NewImageSource(), i wanted to ask, what would be clean way to mock this in current test suite?

@mtrmac mtrmac left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks!

  • I think this would be rather cleaner as a separate copier.putManifest helper, or something like that. E.g. the manifestList = attemptedManifestList flow should not be duplicated.
  • That helper should document the performance rationale of not always doing this (per #918)

Comment thread image/copy/copy.go Outdated
// is slightly pessimistic if the destination image doesn't exist, or is not equivalent.
OptimizeDestinationImageAlreadyExists bool

// OmitManifestUpdateIfUnchanged, if set, skips the manifest update (PutManifest) if the

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Callers don’t know / don’t care what PutManifest is.

Comment thread image/copy/copy.go Outdated
// OmitManifestUpdateIfUnchanged, if set, skips the manifest update (PutManifest) if the
// destination already contains exactly the same manifest. This is useful for registries
// which reject writes to existing tags (tag immutability).
OmitManifestUpdateIfUnchanged bool

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This option name does not match what the implementation does, nor what the user actually wants.

Copying an image that is already at the destination still writes the
image's primary manifest unconditionally. Registries which enforce tag
immutability reject that write, so an otherwise complete no-op copy
fails against them.

Add an option that makes the copy read the manifest currently stored at
the destination and skip the write when it is byte-for-byte identical.
The check lives in a new (*copier).putManifest helper used by both the
single-image and the manifest-list paths, so the two cannot drift apart.

The helper only does this for the primary manifest: instances of a
manifest list are written by digest, which such registries do not
restrict, and checking each of them would cost one extra read per
instance for nothing. The option name says so, since the behaviour is
not "omit any unchanged manifest update".

Fixes: podman-container-tools#918
Signed-off-by: Satwik Sai Prakash Sahoo <sahoospsatwik@gmail.com>
@satwiksps
satwiksps force-pushed the issue-918-omit-manifest-update branch from a097491 to e7f6b11 Compare August 4, 2026 00:56
@satwiksps satwiksps changed the title copy: add OmitManifestUpdateIfUnchanged option copy: add OmitPrimaryManifestUpdateIfUnchanged option Aug 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

image Related to "image" package

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Allow not failing on no-op copies to registries which reject writes to existing tags

2 participants