Pārlūkot izejas kodu

c8d: Adjust "image list" to return only a single item for each image store entry

This will return a single entry for each name/value pair, and for now
all the "image specific" metadata (labels, config, size) should be
either "default platform" or "first platform we have locally" (which
then matches the logic for commands like `docker image inspect`, etc)
with everything else (just ID, maybe?) coming from the manifest
list/index.

That leaves room for the longer-term implementation to add new fields to
describe the _other_ images that are part of the manifest list/index.

Co-authored-by: Tianon Gravi <admwiggin@gmail.com>

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
Paweł Gronowski 1 gadu atpakaļ
vecāks
revīzija
bddd892e91
1 mainītis faili ar 53 papildinājumiem un 7 dzēšanām
  1. 53 7
      daemon/containerd/image_list.go

+ 53 - 7
daemon/containerd/image_list.go

@@ -11,6 +11,7 @@ import (
 	cerrdefs "github.com/containerd/containerd/errdefs"
 	"github.com/containerd/containerd/images"
 	"github.com/containerd/containerd/labels"
+	cplatforms "github.com/containerd/containerd/platforms"
 	"github.com/containerd/containerd/snapshots"
 	"github.com/containerd/log"
 	"github.com/distribution/reference"
@@ -21,6 +22,7 @@ import (
 	"github.com/docker/docker/container"
 	"github.com/docker/docker/errdefs"
 	"github.com/docker/docker/image"
+	dockerspec "github.com/moby/docker-image-spec/specs-go/v1"
 	"github.com/opencontainers/go-digest"
 	"github.com/opencontainers/image-spec/identity"
 	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
@@ -123,6 +125,9 @@ func (i *ImageService) Images(ctx context.Context, opts imagetypes.ListOptions)
 		}
 	}
 
+	// TODO: Allow platform override?
+	platformMatcher := matchAllWithPreference(cplatforms.Default())
+
 	for _, img := range imgs {
 		isDangling := isDanglingImage(img)
 
@@ -154,7 +159,14 @@ func (i *ImageService) Images(ctx context.Context, opts imagetypes.ListOptions)
 		allContainers = i.containers.List()
 	}
 
+	type tempImage struct {
+		img           *ImageManifest
+		indexPlatform *ocispec.Platform
+		dockerImage   *dockerspec.DockerOCIImage
+	}
+
 	for _, img := range uniqueImages {
+		var presentImages []tempImage
 		err := i.walkImageManifests(ctx, img, func(img *ImageManifest) error {
 			if isPseudo, err := img.IsPseudoImage(ctx); isPseudo || err != nil {
 				return err
@@ -174,26 +186,60 @@ func (i *ImageService) Images(ctx context.Context, opts imagetypes.ListOptions)
 				return nil
 			}
 
-			image, chainIDs, err := i.singlePlatformImage(ctx, contentStore, tagsByDigest[img.RealTarget.Digest], img, opts, allContainers)
+			conf, err := img.Config(ctx)
 			if err != nil {
 				return err
 			}
 
-			summaries = append(summaries, image)
+			var dockerImage dockerspec.DockerOCIImage
+			if err := readConfig(ctx, contentStore, conf, &dockerImage); err != nil {
+				return err
+			}
+
+			presentImages = append(presentImages, tempImage{
+				img:           img,
+				indexPlatform: img.Target().Platform,
+				dockerImage:   &dockerImage,
+			})
+			return nil
+		})
+		if err != nil {
+			return nil, err
+		}
+
+		if len(presentImages) == 0 {
+			// TODO we should probably show *something* for images we've pulled
+			// but are 100% shallow or an empty manifest list/index
+			// ("tianon/scratch:index" is an empty example image index and
+			// "tianon/scratch:list" is an empty example manifest list)
+			continue
+		}
 
-			if opts.SharedSize {
-				root = append(root, &chainIDs)
-				for _, id := range chainIDs {
-					layers[id] = layers[id] + 1
+		sort.SliceStable(presentImages, func(i, j int) bool {
+			platformFromIndexOrConfig := func(idx int) ocispec.Platform {
+				if presentImages[i].indexPlatform != nil {
+					return *presentImages[i].indexPlatform
 				}
+				return presentImages[i].dockerImage.Platform
 			}
 
-			return nil
+			return platformMatcher.Less(platformFromIndexOrConfig(i), platformFromIndexOrConfig(j))
 		})
+
+		best := presentImages[0].img
+		image, chainIDs, err := i.singlePlatformImage(ctx, contentStore, tagsByDigest[best.RealTarget.Digest], best, opts, allContainers)
 		if err != nil {
 			return nil, err
 		}
 
+		summaries = append(summaries, image)
+
+		if opts.SharedSize {
+			root = append(root, &chainIDs)
+			for _, id := range chainIDs {
+				layers[id] = layers[id] + 1
+			}
+		}
 	}
 
 	if opts.SharedSize {