aboutsummaryrefslogtreecommitdiff
path: root/vendor/github.com/containers/image/v5/copy/manifest.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/containers/image/v5/copy/manifest.go')
-rw-r--r--vendor/github.com/containers/image/v5/copy/manifest.go154
1 files changed, 154 insertions, 0 deletions
diff --git a/vendor/github.com/containers/image/v5/copy/manifest.go b/vendor/github.com/containers/image/v5/copy/manifest.go
new file mode 100644
index 000000000..f5f6c9c5f
--- /dev/null
+++ b/vendor/github.com/containers/image/v5/copy/manifest.go
@@ -0,0 +1,154 @@
+package copy
+
+import (
+ "context"
+ "strings"
+
+ "github.com/containers/image/v5/manifest"
+ "github.com/containers/image/v5/types"
+ "github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
+)
+
+// preferredManifestMIMETypes lists manifest MIME types in order of our preference, if we can't use the original manifest and need to convert.
+// Prefer v2s2 to v2s1 because v2s2 does not need to be changed when uploading to a different location.
+// Include v2s1 signed but not v2s1 unsigned, because docker/distribution requires a signature even if the unsigned MIME type is used.
+var preferredManifestMIMETypes = []string{manifest.DockerV2Schema2MediaType, manifest.DockerV2Schema1SignedMediaType}
+
+// orderedSet is a list of strings (MIME types in our case), with each string appearing at most once.
+type orderedSet struct {
+ list []string
+ included map[string]struct{}
+}
+
+// newOrderedSet creates a correctly initialized orderedSet.
+// [Sometimes it would be really nice if Golang had constructors…]
+func newOrderedSet() *orderedSet {
+ return &orderedSet{
+ list: []string{},
+ included: map[string]struct{}{},
+ }
+}
+
+// append adds s to the end of os, only if it is not included already.
+func (os *orderedSet) append(s string) {
+ if _, ok := os.included[s]; !ok {
+ os.list = append(os.list, s)
+ os.included[s] = struct{}{}
+ }
+}
+
+// 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.
+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
+}
+
+// isMultiImage returns true if img is a list of images
+func isMultiImage(ctx context.Context, img types.UnparsedImage) (bool, error) {
+ _, mt, err := img.Manifest(ctx)
+ if err != nil {
+ return false, err
+ }
+ return manifest.MIMETypeIsMultiImage(mt), nil
+}
+
+// determineListConversion takes the current MIME type of a list of manifests,
+// the list of MIME types supported for a given destination, and a possible
+// forced value, and returns the MIME type to which we should convert the list
+// of manifests, whether we are converting to it or using it unmodified.
+func (c *copier) determineListConversion(currentListMIMEType string, destSupportedMIMETypes []string, forcedListMIMEType string) (string, error) {
+ // If we're forcing it, we prefer the forced value over everything else.
+ if forcedListMIMEType != "" {
+ return forcedListMIMEType, nil
+ }
+ // If there's no list of supported types, then anything we support is expected to be supported.
+ if len(destSupportedMIMETypes) == 0 {
+ destSupportedMIMETypes = manifest.SupportedListMIMETypes
+ }
+ var selectedType string
+ for i := range destSupportedMIMETypes {
+ // The second priority is the first member of the list of acceptable types that is a list,
+ // but keep going in case current type occurs later in the list.
+ if selectedType == "" && manifest.MIMETypeIsMultiImage(destSupportedMIMETypes[i]) {
+ selectedType = destSupportedMIMETypes[i]
+ }
+ // The first priority is the current type, if it's in the list, since that lets us avoid a
+ // conversion that isn't strictly necessary.
+ if destSupportedMIMETypes[i] == currentListMIMEType {
+ selectedType = destSupportedMIMETypes[i]
+ }
+ }
+ if selectedType == "" {
+ return "", errors.Errorf("destination does not support any supported manifest list types (%v)", manifest.SupportedListMIMETypes)
+ }
+ // Done.
+ return selectedType, nil
+}