From 06adb5025dc95c942fa9124273d7cc34bc73b6bc Mon Sep 17 00:00:00 2001 From: Justin Miron Date: Tue, 17 Mar 2026 14:21:12 -0500 Subject: [PATCH] Add ReportResolvedSourceReference to copy.Options When registry mirrors are configured via registries.conf, callers of copy.Image() have no way to discover which endpoint was actually contacted. The dockerImageSource already tracks this internally in its physicalRef field, but that information is never surfaced. Introduce a types.ResolvedImageSource optional interface that transports can implement to report the actual endpoint used. The docker transport implements it by returning physicalRef. copy.Image() detects the interface and writes the resolved reference into the new ReportResolvedSourceReference field on Options, following the same pointer-based pattern as the existing ReportResolvedReference for destinations. This enables consumers like CRI-O to expose the resolved registry domain in image pull metrics, giving operators visibility into which mirror or cache endpoint is serving their pulls. --- copy/copy.go | 14 ++++++++++++++ docker/docker_image_src.go | 5 +++++ docker/docker_image_src_test.go | 6 ++++++ types/types.go | 10 ++++++++++ 4 files changed, 35 insertions(+) diff --git a/copy/copy.go b/copy/copy.go index 44675f37cf..d0c7e7a3a8 100644 --- a/copy/copy.go +++ b/copy/copy.go @@ -149,6 +149,13 @@ type Options struct { // WARNING: It is unspecified whether the reference also contains a reference.Named element. ReportResolvedReference *types.ImageReference + // ReportResolvedSourceReference, if non-nil, will be populated by + // copy.Image with the source's "resolved" reference — the actual + // endpoint contacted, which may differ from the requested reference + // when registry mirrors are configured. It is left nil when the + // source transport does not report a resolved reference. + ReportResolvedSourceReference *types.ImageReference + // DestinationTimestamp, if set, will force timestamps of content created in the destination to this value. // Most transports don't support this. // @@ -239,6 +246,13 @@ func Image(ctx context.Context, policyContext *signature.PolicyContext, destRef, rawSource := imagesource.FromPublic(publicRawSource) defer safeClose("src", rawSource) + if options.ReportResolvedSourceReference != nil { + *options.ReportResolvedSourceReference = nil + if resolver, ok := publicRawSource.(types.ResolvedImageSource); ok { + *options.ReportResolvedSourceReference = resolver.ResolvedReference() + } + } + // If reportWriter is not a TTY (e.g., when piping to a file), do not // print the progress bars to avoid long and hard to parse output. // Instead use printCopyInfo() to print single line "Copying ..." messages. diff --git a/docker/docker_image_src.go b/docker/docker_image_src.go index 4eb9cdfba5..4244d6fe55 100644 --- a/docker/docker_image_src.go +++ b/docker/docker_image_src.go @@ -202,6 +202,11 @@ func (s *dockerImageSource) Reference() types.ImageReference { return s.logicalRef } +// ResolvedReference implements types.ResolvedImageSource. +func (s *dockerImageSource) ResolvedReference() types.ImageReference { + return s.physicalRef +} + // Close removes resources associated with an initialized ImageSource, if any. func (s *dockerImageSource) Close() error { return s.c.Close() diff --git a/docker/docker_image_src_test.go b/docker/docker_image_src_test.go index 585d3ddf88..0c2a2c47fe 100644 --- a/docker/docker_image_src_test.go +++ b/docker/docker_image_src_test.go @@ -81,6 +81,12 @@ location = "@REGISTRY@/with-mirror" require.True(t, ok, c.input) assert.Equal(t, "//"+c.input, src2.logicalRef.StringWithinTransport(), c.input) assert.Equal(t, "//"+c.physical, src2.physicalRef.StringWithinTransport(), c.input) + + // Verify ResolvedReference() returns the physical ref through the public interface + resolver, ok := src.(types.ResolvedImageSource) + require.True(t, ok, c.input) + resolved := resolver.ResolvedReference() + assert.Equal(t, "//"+c.physical, resolved.StringWithinTransport(), c.input) } } diff --git a/types/types.go b/types/types.go index a93951780b..ea0b7bf234 100644 --- a/types/types.go +++ b/types/types.go @@ -283,6 +283,16 @@ type ImageSource interface { LayerInfosForCopy(ctx context.Context, instanceDigest *digest.Digest) ([]BlobInfo, error) } +// ResolvedImageSource is an optional interface that ImageSource implementations +// can satisfy to report the actual endpoint used when the source was resolved +// through mirrors or redirects. +type ResolvedImageSource interface { + // ResolvedReference returns the reference to the actual endpoint that + // was contacted, which may differ from Reference() when registry + // mirrors are configured. + ResolvedReference() ImageReference +} + // ImageDestination is a service, possibly remote (= slow), to store components of a single image. // // There is a specific required order for some of the calls: