Преглед изворни кода

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 година
родитељ
комит
bddd892e91
1 измењених фајлова са 53 додато и 7 уклоњено
  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 {