copy/manifest.go
func (ic *imageCopier) determineManifestConversion(ctx context.Context, destSupportedManifestMIMETypes []string, forceManifestMIMEType string) (string, []string, error) {
	_, srcType, err := ic.src.Manifest(ctx)
	if err != nil { // This should have been cached?!
		return "", nil, errors.Wrap(err, "Error reading manifest")
	}
	normalizedSrcType := manifest.NormalizedMIMEType(srcType)
	if srcType != normalizedSrcType {
		logrus.Debugf("Source manifest MIME type %s, treating it as %s", srcType, normalizedSrcType)
		srcType = normalizedSrcType
	}

	if forceManifestMIMEType != "" {
		destSupportedManifestMIMETypes = []string{forceManifestMIMEType}
	}

	if len(destSupportedManifestMIMETypes) == 0 {
		return srcType, []string{}, nil // Anything goes; just use the original as is, do not try any conversions.
	}
	supportedByDest := map[string]struct{}{}
	for _, t := range destSupportedManifestMIMETypes {
		supportedByDest[t] = struct{}{}
	}

	// destSupportedManifestMIMETypes is a static guess; a particular registry may still only support a subset of the types.
	// So, build a list of types to try in order of decreasing preference.
	// FIXME? This treats manifest.DockerV2Schema1SignedMediaType and manifest.DockerV2Schema1MediaType as distinct,
	// although we are not really making any conversion, and it is very unlikely that a destination would support one but not the other.
	// In practice, schema1 is probably the lowest common denominator, so we would expect to try the first one of the MIME types
	// and never attempt the other one.
	prioritizedTypes := newOrderedSet()

	// First of all, prefer to keep the original manifest unmodified.
	if _, ok := supportedByDest[srcType]; ok {
		prioritizedTypes.append(srcType)
	}
	if !ic.canModifyManifest {
		// We could also drop the !ic.canModifyManifest check and have the caller
		// make the choice; it is already doing that to an extent, to improve error
		// messages.  But it is nice to hide the “if !ic.canModifyManifest, do no conversion”
		// special case in here; the caller can then worry (or not) only about a good UI.
		logrus.Debugf("We can't modify the manifest, hoping for the best...")
		return srcType, []string{}, nil // Take our chances - FIXME? Or should we fail without trying?
	}

	// Then use our list of preferred types.
	for _, t := range preferredManifestMIMETypes {
		if _, ok := supportedByDest[t]; ok {
			prioritizedTypes.append(t)
		}
	}

	// Finally, try anything else the destination supports.
	for _, t := range destSupportedManifestMIMETypes {
		prioritizedTypes.append(t)
	}

	logrus.Debugf("Manifest has MIME type %s, ordered candidate list [%s]", srcType, strings.Join(prioritizedTypes.list, ", "))
	if len(prioritizedTypes.list) == 0 { // Coverage: destSupportedManifestMIMETypes is not empty (or we would have exited in the “Anything goes” case above), so this should never happen.
		return "", nil, errors.New("Internal error: no candidate MIME types")
	}
	preferredType := prioritizedTypes.list[0]
	if preferredType != srcType {
		ic.manifestUpdates.ManifestMIMEType = preferredType
	} else {
		logrus.Debugf("... will first try using the original manifest unmodified")
	}
	return preferredType, prioritizedTypes.list[1:], nil
}
func (ic *imageCopier) determineManifestConversion(ctx context.Context, destSupportedManifestMIMETypes []string, forceManifestMIMEType string) (string, []string, error) {
	_, srcType, err := ic.src.Manifest(ctx)
	if err != nil { // This should have been cached?!
		return "", nil, errors.Wrap(err, "Error reading manifest")
	}
	normalizedSrcType := manifest.NormalizedMIMEType(srcType)
	if srcType != normalizedSrcType {
		logrus.Debugf("Source manifest MIME type %s, treating it as %s", srcType, normalizedSrcType)
		srcType = normalizedSrcType
	}

	if forceManifestMIMEType != "" {
		destSupportedManifestMIMETypes = []string{forceManifestMIMEType}
	}

	if len(destSupportedManifestMIMETypes) == 0 {
		return srcType, []string{}, nil // Anything goes; just use the original as is, do not try any conversions.
	}
	supportedByDest := map[string]struct{}{}
	for _, t := range destSupportedManifestMIMETypes {
		supportedByDest[t] = struct{}{}
	}

	// destSupportedManifestMIMETypes is a static guess; a particular registry may still only support a subset of the types.
	// So, build a list of types to try in order of decreasing preference.
	// FIXME? This treats manifest.DockerV2Schema1SignedMediaType and manifest.DockerV2Schema1MediaType as distinct,
	// although we are not really making any conversion, and it is very unlikely that a destination would support one but not the other.
	// In practice, schema1 is probably the lowest common denominator, so we would expect to try the first one of the MIME types
	// and never attempt the other one.
	prioritizedTypes := newOrderedSet()

	// First of all, prefer to keep the original manifest unmodified.
	if _, ok := supportedByDest[srcType]; ok {
		prioritizedTypes.append(srcType)
	}
	if !ic.canModifyManifest {
		// We could also drop the !ic.canModifyManifest check and have the caller
		// make the choice; it is already doing that to an extent, to improve error
		// messages.  But it is nice to hide the “if !ic.canModifyManifest, do no conversion”
		// special case in here; the caller can then worry (or not) only about a good UI.
		logrus.Debugf("We can't modify the manifest, hoping for the best...")
		return srcType, []string{}, nil // Take our chances - FIXME? Or should we fail without trying?
	}

	// Then use our list of preferred types.
	for _, t := range preferredManifestMIMETypes {
		if _, ok := supportedByDest[t]; ok {
			prioritizedTypes.append(t)
		}
	}

	// Finally, try anything else the destination supports.
	for _, t := range destSupportedManifestMIMETypes {
		prioritizedTypes.append(t)
	}

	logrus.Debugf("Manifest has MIME type %s, ordered candidate list [%s]", srcType, strings.Join(prioritizedTypes.list, ", "))
	if len(prioritizedTypes.list) == 0 { // Coverage: destSupportedManifestMIMETypes is not empty (or we would have exited in the “Anything goes” case above), so this should never happen.
		return "", nil, errors.New("Internal error: no candidate MIME types")
	}
	preferredType := prioritizedTypes.list[0]
	if preferredType != srcType {
		ic.manifestUpdates.ManifestMIMEType = preferredType
	} else {
		logrus.Debugf("... will first try using the original manifest unmodified")
	}
	return preferredType, prioritizedTypes.list[1:], nil
}
// determineManifestConversion updates ic.manifestUpdates to convert manifest to a supported MIME type, if necessary and ic.canModifyManifest.
// Note that the conversion will only happen later, through ic.src.UpdatedImage
// Returns the preferred manifest MIME type (whether we are converting to it or using it unmodified),
// and a list of other possible alternatives, in order.