diff --git a/image/copy/copy.go b/image/copy/copy.go index 5d70482519..e5ba48f541 100644 --- a/image/copy/copy.go +++ b/image/copy/copy.go @@ -157,6 +157,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. // @@ -262,6 +269,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/image/docker/docker_image_src.go b/image/docker/docker_image_src.go index 4003af5d27..e6de78cd7d 100644 --- a/image/docker/docker_image_src.go +++ b/image/docker/docker_image_src.go @@ -203,6 +203,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/image/docker/docker_image_src_test.go b/image/docker/docker_image_src_test.go index 4d81bde7e0..9ef6375083 100644 --- a/image/docker/docker_image_src_test.go +++ b/image/docker/docker_image_src_test.go @@ -74,6 +74,12 @@ location = "@REGISTRY@/with-mirror" // The observable behavior assert.Equal(t, "//"+c.input, src.Reference().StringWithinTransport(), c.input) assert.Equal(t, ref.StringWithinTransport(), src.Reference().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) + // Also peek into internal state src2, ok := src.(*dockerImageSource) require.True(t, ok, c.input) diff --git a/image/types/types.go b/image/types/types.go index 1c0007e6e4..9108cc0768 100644 --- a/image/types/types.go +++ b/image/types/types.go @@ -702,6 +702,16 @@ type SystemContext struct { CompressionLevel *int } +// 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 +} + // ProgressEvent is the type of events a progress reader can produce // Warning: new event types may be added any time. type ProgressEvent uint