Skip to content

Commit 062db46

Browse files
committed
copy: try list conversions on list push failures
Similarly to how we fall back to trying to push single-image manifests in alternate formats when the original/preferred type is rejected by a registry, attempt to do the same for manifest lists. Signed-off-by: Nalin Dahyabhai <[email protected]>
1 parent d8b77ec commit 062db46

File tree

2 files changed

+64
-39
lines changed

2 files changed

+64
-39
lines changed

copy/copy.go

Lines changed: 49 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -356,11 +356,11 @@ func (c *copier) copyMultipleImages(ctx context.Context, policyContext *signatur
356356
if err != nil {
357357
return nil, "", errors.Wrapf(err, "Error reading manifest list")
358358
}
359-
list, err := manifest.ListFromBlob(manifestList, manifestType)
359+
originalList, err := manifest.ListFromBlob(manifestList, manifestType)
360360
if err != nil {
361361
return nil, "", errors.Wrapf(err, "Error parsing manifest list %q", string(manifestList))
362362
}
363-
originalList := list.Clone()
363+
updatedList := originalList.Clone()
364364

365365
// Read and/or clear the set of signatures for this list.
366366
var sigs [][]byte
@@ -390,18 +390,18 @@ func (c *copier) copyMultipleImages(ctx context.Context, policyContext *signatur
390390
case imgspecv1.MediaTypeImageManifest:
391391
forceListMIMEType = imgspecv1.MediaTypeImageIndex
392392
}
393-
selectedListType, err := c.determineListConversion(manifestType, c.dest.SupportedManifestMIMETypes(), forceListMIMEType)
393+
selectedListType, otherManifestMIMETypeCandidates, err := c.determineListConversion(manifestType, c.dest.SupportedManifestMIMETypes(), forceListMIMEType)
394394
if err != nil {
395395
return nil, "", errors.Wrapf(err, "Error determining manifest list type to write to destination")
396396
}
397-
if selectedListType != list.MIMEType() {
397+
if selectedListType != originalList.MIMEType() {
398398
if !canModifyManifestList {
399399
return nil, "", errors.Errorf("Error: manifest list must be converted to type %q to be written to destination, but that would invalidate signatures", selectedListType)
400400
}
401401
}
402402

403403
// Copy each image, or just the ones we want to copy, in turn.
404-
instanceDigests := list.Instances()
404+
instanceDigests := updatedList.Instances()
405405
imagesToCopy := len(instanceDigests)
406406
if options.ImageListSelection == CopySpecificImages {
407407
imagesToCopy = len(options.Instances)
@@ -419,7 +419,7 @@ func (c *copier) copyMultipleImages(ctx context.Context, policyContext *signatur
419419
}
420420
}
421421
if skip {
422-
update, err := list.Instance(instanceDigest)
422+
update, err := updatedList.Instance(instanceDigest)
423423
if err != nil {
424424
return nil, "", err
425425
}
@@ -447,42 +447,58 @@ func (c *copier) copyMultipleImages(ctx context.Context, policyContext *signatur
447447
}
448448

449449
// Now reset the digest/size/types of the manifests in the list to account for any conversions that we made.
450-
if err = list.UpdateInstances(updates); err != nil {
450+
if err = updatedList.UpdateInstances(updates); err != nil {
451451
return nil, "", errors.Wrapf(err, "Error updating manifest list")
452452
}
453453

454-
// Perform the list conversion.
455-
if selectedListType != list.MIMEType() {
456-
list, err = list.ConvertToMIMEType(selectedListType)
454+
// Iterate through supported list types, preferred format first.
455+
c.Printf("Writing manifest list to image destination\n")
456+
var errs []string
457+
for _, thisListType := range append([]string{selectedListType}, otherManifestMIMETypeCandidates...) {
458+
attemptedList := updatedList
459+
460+
logrus.Debugf("Trying to use manifest list type %s…", thisListType)
461+
462+
// Perform the list conversion, if we need one.
463+
if thisListType != updatedList.MIMEType() {
464+
attemptedList, err = updatedList.ConvertToMIMEType(thisListType)
465+
if err != nil {
466+
return nil, "", errors.Wrapf(err, "Error converting manifest list to list with MIME type %q", thisListType)
467+
}
468+
}
469+
470+
// Check if the updates or a type conversion meaningfully changed the list of images
471+
// by serializing them both so that we can compare them.
472+
attemptedManifestList, err := attemptedList.Serialize()
457473
if err != nil {
458-
return nil, "", errors.Wrapf(err, "Error converting manifest list to list with MIME type %q", selectedListType)
474+
return nil, "", errors.Wrapf(err, "Error encoding updated manifest list (%q: %#v)", updatedList.MIMEType(), updatedList.Instances())
475+
}
476+
originalManifestList, err := originalList.Serialize()
477+
if err != nil {
478+
return nil, "", errors.Wrapf(err, "Error encoding original manifest list for comparison (%q: %#v)", originalList.MIMEType(), originalList.Instances())
459479
}
460-
}
461480

462-
// Check if the updates or a type conversion meaningfully changed the list of images
463-
// by serializing them both so that we can compare them.
464-
updatedManifestList, err := list.Serialize()
465-
if err != nil {
466-
return nil, "", errors.Wrapf(err, "Error encoding updated manifest list (%q: %#v)", list.MIMEType(), list.Instances())
467-
}
468-
originalManifestList, err := originalList.Serialize()
469-
if err != nil {
470-
return nil, "", errors.Wrapf(err, "Error encoding original manifest list for comparison (%q: %#v)", originalList.MIMEType(), originalList.Instances())
471-
}
481+
// If we can't just use the original value, but we have to change it, flag an error.
482+
if !bytes.Equal(attemptedManifestList, originalManifestList) {
483+
if !canModifyManifestList {
484+
return nil, "", errors.Errorf("Error: manifest list must be converted to type %q to be written to destination, but that would invalidate signatures", thisListType)
485+
}
486+
logrus.Debugf("Manifest list has been updated")
487+
}
472488

473-
// If we can't just use the original value, but we have to change it, flag an error.
474-
if !bytes.Equal(updatedManifestList, originalManifestList) {
475-
if !canModifyManifestList {
476-
return nil, "", errors.Errorf("Error: manifest list must be converted to type %q to be written to destination, but that would invalidate signatures", selectedListType)
489+
// Save the manifest list.
490+
err = c.dest.PutManifest(ctx, attemptedManifestList, nil)
491+
if err != nil {
492+
logrus.Debugf("Upload of manifest list type %s failed: %v", thisListType, err)
493+
errs = append(errs, fmt.Sprintf("%s(%v)", thisListType, err))
494+
continue
477495
}
478-
manifestList = updatedManifestList
479-
logrus.Debugf("Manifest list has been updated")
496+
errs = nil
497+
manifestList = attemptedManifestList
498+
break
480499
}
481-
482-
// Save the manifest list.
483-
c.Printf("Writing manifest list to image destination\n")
484-
if err = c.dest.PutManifest(ctx, manifestList, nil); err != nil {
485-
return nil, "", errors.Wrapf(err, "Error writing manifest list %q", string(manifestList))
500+
if errs != nil {
501+
return nil, "", fmt.Errorf("Uploading manifest list failed, attempted the following formats: %s", strings.Join(errs, ", "))
486502
}
487503

488504
// Sign the manifest list.

copy/manifest.go

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -125,8 +125,10 @@ func isMultiImage(ctx context.Context, img types.UnparsedImage) (bool, error) {
125125
// determineListConversion takes the current MIME type of a list of manifests,
126126
// the list of MIME types supported for a given destination, and a possible
127127
// forced value, and returns the MIME type to which we should convert the list
128-
// of manifests, whether we are converting to it or using it unmodified.
129-
func (c *copier) determineListConversion(currentListMIMEType string, destSupportedMIMETypes []string, forcedListMIMEType string) (string, error) {
128+
// of manifests (regardless of whether we are converting to it or using it
129+
// unmodified) and a slice of other list types which might be supported by the
130+
// destination.
131+
func (c *copier) determineListConversion(currentListMIMEType string, destSupportedMIMETypes []string, forcedListMIMEType string) (string, []string, error) {
130132
// If there's no list of supported types, then anything we support is expected to be supported.
131133
if len(destSupportedMIMETypes) == 0 {
132134
destSupportedMIMETypes = manifest.SupportedListMIMETypes
@@ -136,6 +138,7 @@ func (c *copier) determineListConversion(currentListMIMEType string, destSupport
136138
destSupportedMIMETypes = []string{forcedListMIMEType}
137139
}
138140
var selectedType string
141+
var otherSupportedTypes []string
139142
for i := range destSupportedMIMETypes {
140143
// The second priority is the first member of the list of acceptable types that is a list,
141144
// but keep going in case current type occurs later in the list.
@@ -148,15 +151,21 @@ func (c *copier) determineListConversion(currentListMIMEType string, destSupport
148151
selectedType = destSupportedMIMETypes[i]
149152
}
150153
}
154+
// Pick out the other list types that we support.
155+
for i := range destSupportedMIMETypes {
156+
if selectedType != destSupportedMIMETypes[i] && manifest.MIMETypeIsMultiImage(destSupportedMIMETypes[i]) {
157+
otherSupportedTypes = append(otherSupportedTypes, destSupportedMIMETypes[i])
158+
}
159+
}
151160
logrus.Debugf("Manifest list has MIME type %s, ordered candidate list [%s]", currentListMIMEType, strings.Join(destSupportedMIMETypes, ", "))
152161
if selectedType == "" {
153-
return "", errors.Errorf("destination does not support any supported manifest list types (%v)", manifest.SupportedListMIMETypes)
162+
return "", nil, errors.Errorf("destination does not support any supported manifest list types (%v)", manifest.SupportedListMIMETypes)
154163
}
155164
if selectedType != currentListMIMEType {
156-
logrus.Debugf("... will convert to %s", selectedType)
165+
logrus.Debugf("... will convert to %s first, and then try %v", selectedType, otherSupportedTypes)
157166
} else {
158-
logrus.Debugf("... will use the original manifest list type")
167+
logrus.Debugf("... will use the original manifest list type, and then try %v", otherSupportedTypes)
159168
}
160169
// Done.
161-
return selectedType, nil
170+
return selectedType, otherSupportedTypes, nil
162171
}

0 commit comments

Comments
 (0)