From a7556dd6385138315048c8aeff5591fe5ebb6cb6 Mon Sep 17 00:00:00 2001 From: Aditya R Date: Wed, 2 Aug 2023 07:11:45 +0530 Subject: [PATCH] copy: add support for ForceCompressionFormat ForceCompressionFormat allows end users to force selected compression format (set in DestinationCtx.CompressionFormat) which ensures that while copying blobs of other compression algorithms are not reused. Following flag is a frontend wrapper for: https://github.com/containers/image/pull/2023 Will help in: * https://github.com/containers/buildah/issues/4613 * https://github.com/containers/podman/issues/18660 Signed-off-by: Aditya R --- copy/copy.go | 14 ++++++++++++-- copy/multiple.go | 15 ++++++++++----- copy/multiple_test.go | 5 ++++- 3 files changed, 26 insertions(+), 8 deletions(-) diff --git a/copy/copy.go b/copy/copy.go index ac0e6f2fa2..66d06fc51b 100644 --- a/copy/copy.go +++ b/copy/copy.go @@ -133,6 +133,10 @@ type Options struct { // Invalid when copying a non-multi-architecture image. That will probably // change in the future. EnsureCompressionVariantsExist []OptionCompressionVariant + // ForceCompressionFormat ensures that the compression algorithm set in + // DestinationCtx.CompressionFormat is used exclusively, and blobs of other + // compression algorithms are not reused. + ForceCompressionFormat bool } // OptionCompressionVariant allows to supply information about @@ -269,8 +273,11 @@ func Image(ctx context.Context, policyContext *signature.PolicyContext, destRef, if len(options.EnsureCompressionVariantsExist) > 0 { return nil, fmt.Errorf("EnsureCompressionVariantsExist is not implemented when not creating a multi-architecture image") } + if (options.DestinationCtx == nil || options.DestinationCtx.CompressionFormat == nil) && options.ForceCompressionFormat { + return nil, fmt.Errorf("cannot use ForceCompressionFormat with undefined default compression format") + } // The simple case: just copy a single image. - single, err := c.copySingleImage(ctx, c.unparsedToplevel, nil, copySingleImageOptions{requireCompressionFormatMatch: false}) + single, err := c.copySingleImage(ctx, c.unparsedToplevel, nil, copySingleImageOptions{requireCompressionFormatMatch: c.options.ForceCompressionFormat}) if err != nil { return nil, err } @@ -279,6 +286,9 @@ func Image(ctx context.Context, policyContext *signature.PolicyContext, destRef, if len(options.EnsureCompressionVariantsExist) > 0 { return nil, fmt.Errorf("EnsureCompressionVariantsExist is not implemented when not creating a multi-architecture image") } + if (options.DestinationCtx == nil || options.DestinationCtx.CompressionFormat == nil) && options.ForceCompressionFormat { + return nil, fmt.Errorf("cannot use ForceCompressionFormat with undefined default compression format") + } // This is a manifest list, and we weren't asked to copy multiple images. Choose a single image that // matches the current system to copy, and copy it. mfest, manifestType, err := c.unparsedToplevel.Manifest(ctx) @@ -295,7 +305,7 @@ func Image(ctx context.Context, policyContext *signature.PolicyContext, destRef, } logrus.Debugf("Source is a manifest list; copying (only) instance %s for current system", instanceDigest) unparsedInstance := image.UnparsedInstance(rawSource, &instanceDigest) - single, err := c.copySingleImage(ctx, unparsedInstance, nil, copySingleImageOptions{requireCompressionFormatMatch: false}) + single, err := c.copySingleImage(ctx, unparsedInstance, nil, copySingleImageOptions{requireCompressionFormatMatch: c.options.ForceCompressionFormat}) if err != nil { return nil, fmt.Errorf("copying system image from manifest list: %w", err) } diff --git a/copy/multiple.go b/copy/multiple.go index 34f2129d69..096b18b2a6 100644 --- a/copy/multiple.go +++ b/copy/multiple.go @@ -29,8 +29,9 @@ const ( ) type instanceCopy struct { - op instanceCopyKind - sourceDigest digest.Digest + op instanceCopyKind + sourceDigest digest.Digest + forceCompressionFormat bool // Fields which can be used by callers when operation // is `instanceCopyClone` @@ -122,9 +123,13 @@ func prepareInstanceCopies(list internalManifest.List, instanceDigests []digest. if err != nil { return res, fmt.Errorf("getting details for instance %s: %w", instanceDigest, err) } + if (options.DestinationCtx == nil || options.DestinationCtx.CompressionFormat == nil) && options.ForceCompressionFormat { + return res, fmt.Errorf("cannot use ForceCompressionFormat with undefined default compression") + } res = append(res, instanceCopy{ - op: instanceCopyCopy, - sourceDigest: instanceDigest, + op: instanceCopyCopy, + sourceDigest: instanceDigest, + forceCompressionFormat: options.ForceCompressionFormat, }) platform := platformV1ToPlatformComparable(instanceDetails.ReadOnly.Platform) compressionList := compressionsByPlatform[platform] @@ -230,7 +235,7 @@ func (c *copier) copyMultipleImages(ctx context.Context) (copiedManifest []byte, logrus.Debugf("Copying instance %s (%d/%d)", instance.sourceDigest, i+1, len(instanceCopyList)) c.Printf("Copying image %s (%d/%d)\n", instance.sourceDigest, i+1, len(instanceCopyList)) unparsedInstance := image.UnparsedInstance(c.rawSource, &instanceCopyList[i].sourceDigest) - updated, err := c.copySingleImage(ctx, unparsedInstance, &instanceCopyList[i].sourceDigest, copySingleImageOptions{requireCompressionFormatMatch: false}) + updated, err := c.copySingleImage(ctx, unparsedInstance, &instanceCopyList[i].sourceDigest, copySingleImageOptions{requireCompressionFormatMatch: instance.forceCompressionFormat}) if err != nil { return nil, fmt.Errorf("copying image %d/%d from manifest list: %w", i+1, len(instanceCopyList), err) } diff --git a/copy/multiple_test.go b/copy/multiple_test.go index 223dd274dc..64026ac7df 100644 --- a/copy/multiple_test.go +++ b/copy/multiple_test.go @@ -32,7 +32,7 @@ func TestPrepareCopyInstancesforInstanceCopyCopy(t *testing.T) { for _, instance := range sourceInstances { compare = append(compare, instanceCopy{op: instanceCopyCopy, - sourceDigest: instance}) + sourceDigest: instance, forceCompressionFormat: false}) } assert.Equal(t, instancesToCopy, compare) @@ -42,6 +42,9 @@ func TestPrepareCopyInstancesforInstanceCopyCopy(t *testing.T) { compare = []instanceCopy{{op: instanceCopyCopy, sourceDigest: sourceInstances[1]}} assert.Equal(t, instancesToCopy, compare) + + _, err = prepareInstanceCopies(list, sourceInstances, &Options{Instances: []digest.Digest{sourceInstances[1]}, ImageListSelection: CopySpecificImages, ForceCompressionFormat: true}) + require.EqualError(t, err, "cannot use ForceCompressionFormat with undefined default compression") } // Test `instanceCopyClone` cases.