diff --git a/copy/copy.go b/copy/copy.go index ab36ddc20..75d6b8234 100644 --- a/copy/copy.go +++ b/copy/copy.go @@ -137,6 +137,16 @@ type Options struct { // DestinationCtx.CompressionFormat is used exclusively, and blobs of other // compression algorithms are not reused. ForceCompressionFormat bool + + // ReportResolvedReference, if set, asks the destination transport to store + // a “resolved” (more detailed) reference to the created image + // into the value this option points to. + // What “resolved” means is transport-specific. + // Most transports don’t support this, and cause the value to be set to nil. + // + // For the containers-storage: transport, the reference contains an image ID, + // so that storage.ResolveReference returns exactly the created image. + ReportResolvedReference *types.ImageReference } // OptionCompressionVariant allows to supply information about @@ -337,8 +347,12 @@ func Image(ctx context.Context, policyContext *signature.PolicyContext, destRef, } } + if options.ReportResolvedReference != nil { + *options.ReportResolvedReference = nil // The default outcome, if not specifically supported by the transport. + } if err := c.dest.CommitWithOptions(ctx, private.CommitOptions{ - UnparsedToplevel: c.unparsedToplevel, + UnparsedToplevel: c.unparsedToplevel, + ReportResolvedReference: options.ReportResolvedReference, }); err != nil { return nil, fmt.Errorf("committing the finished image: %w", err) } diff --git a/internal/private/private.go b/internal/private/private.go index 3ab2cc478..4247a8db7 100644 --- a/internal/private/private.go +++ b/internal/private/private.go @@ -158,6 +158,11 @@ type CommitOptions struct { // if PutManifest was only called for the single-arch image with instanceDigest == nil), primarily to allow lookups by the // original manifest list digest, if desired. UnparsedToplevel types.UnparsedImage + // ReportResolvedReference, if set, asks the transport to store a “resolved” (more detailed) reference to the created image + // into the value this option points to. + // What “resolved” means is transport-specific. + // Transports which don’t support reporting resolved references can ignore the field; the generic copy code writes "nil" into the value. + ReportResolvedReference *types.ImageReference } // ImageSourceChunk is a portion of a blob. diff --git a/storage/storage_dest.go b/storage/storage_dest.go index 8919066f3..ce66bccec 100644 --- a/storage/storage_dest.go +++ b/storage/storage_dest.go @@ -1343,6 +1343,13 @@ func (s *storageImageDestination) CommitWithOptions(ctx context.Context, options } logrus.Debugf("added name %q to image %q", name, img.ID) } + if options.ReportResolvedReference != nil { + resolved, err := newReference(s.imageRef.transport, s.imageRef.named, intendedID) + if err != nil { + return fmt.Errorf("creating a resolved reference for (%s, %s): %w", s.imageRef.StringWithinTransport(), intendedID, err) + } + *options.ReportResolvedReference = resolved + } commitSucceeded = true return nil