diff --git a/copy/copy.go b/copy/copy.go index 44675f37c..d0c7e7a3a 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 4eb9cdfba..4244d6fe5 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 585d3ddf8..0c2a2c47f 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 a93951780..ea0b7bf23 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: