瀏覽代碼

c8d/list: Add a test case for images sharing a top layer

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
Paweł Gronowski 1 年之前
父節點
當前提交
3312b82515
共有 2 個文件被更改,包括 63 次插入15 次删除
  1. 35 0
      integration/image/list_test.go
  2. 28 15
      internal/testutils/specialimage/multilayer.go

+ 35 - 0
integration/image/list_test.go

@@ -10,10 +10,14 @@ import (
 	"github.com/docker/docker/api/types/filters"
 	"github.com/docker/docker/api/types/image"
 	"github.com/docker/docker/integration/internal/container"
+	"github.com/docker/docker/internal/testutils/specialimage"
 	"github.com/docker/docker/testutil"
+	"github.com/docker/docker/testutil/daemon"
 	"github.com/google/go-cmp/cmp/cmpopts"
+	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
 	"gotest.tools/v3/assert"
 	is "gotest.tools/v3/assert/cmp"
+	"gotest.tools/v3/skip"
 )
 
 // Regression : #38171
@@ -192,3 +196,34 @@ func TestAPIImagesFilters(t *testing.T) {
 		})
 	}
 }
+
+// Verify that the size calculation operates on ChainIDs and not DiffIDs.
+// This test calls an image list with two images that share one, top layer.
+func TestAPIImagesListSizeShared(t *testing.T) {
+	skip.If(t, testEnv.DaemonInfo.OSType != "linux")
+
+	ctx := setupTest(t)
+
+	daemon := daemon.New(t)
+	daemon.Start(t)
+	defer daemon.Stop(t)
+
+	client := daemon.NewClientT(t)
+
+	specialimage.Load(ctx, t, client, func(dir string) (*ocispec.Index, error) {
+		return specialimage.MultiLayerCustom(dir, "multilayer:latest", []specialimage.SingleFileLayer{
+			{Name: "bar", Content: []byte("2")},
+			{Name: "foo", Content: []byte("1")},
+		})
+	})
+
+	specialimage.Load(ctx, t, client, func(dir string) (*ocispec.Index, error) {
+		return specialimage.MultiLayerCustom(dir, "multilayer2:latest", []specialimage.SingleFileLayer{
+			{Name: "asdf", Content: []byte("3")},
+			{Name: "foo", Content: []byte("1")},
+		})
+	})
+
+	_, err := client.ImageList(ctx, image.ListOptions{SharedSize: true})
+	assert.NilError(t, err)
+}

+ 28 - 15
internal/testutils/specialimage/multilayer.go

@@ -16,20 +16,32 @@ import (
 	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
 )
 
+type SingleFileLayer struct {
+	Name    string
+	Content []byte
+}
+
 func MultiLayer(dir string) (*ocispec.Index, error) {
-	const imageRef = "multilayer:latest"
+	return MultiLayerCustom(dir, "multilayer:latest", []SingleFileLayer{
+		{Name: "foo", Content: []byte("1")},
+		{Name: "bar", Content: []byte("2")},
+		{Name: "hello", Content: []byte("world")},
+	})
+}
 
-	layer1Desc, err := writeLayerWithOneFile(dir, "foo", []byte("1"))
-	if err != nil {
-		return nil, err
-	}
-	layer2Desc, err := writeLayerWithOneFile(dir, "bar", []byte("2"))
-	if err != nil {
-		return nil, err
-	}
-	layer3Desc, err := writeLayerWithOneFile(dir, "hello", []byte("world"))
-	if err != nil {
-		return nil, err
+func MultiLayerCustom(dir string, imageRef string, layers []SingleFileLayer) (*ocispec.Index, error) {
+	var layerDescs []ocispec.Descriptor
+	var layerDgsts []digest.Digest
+	var layerBlobs []string
+	for _, layer := range layers {
+		layerDesc, err := writeLayerWithOneFile(dir, layer.Name, layer.Content)
+		if err != nil {
+			return nil, err
+		}
+
+		layerDescs = append(layerDescs, layerDesc)
+		layerDgsts = append(layerDgsts, layerDesc.Digest)
+		layerBlobs = append(layerBlobs, blobPath(layerDesc))
 	}
 
 	configDesc, err := writeJsonBlob(dir, ocispec.MediaTypeImageConfig, ocispec.Image{
@@ -39,7 +51,7 @@ func MultiLayer(dir string) (*ocispec.Index, error) {
 		},
 		RootFS: ocispec.RootFS{
 			Type:    "layers",
-			DiffIDs: []digest.Digest{layer1Desc.Digest, layer2Desc.Digest, layer3Desc.Digest},
+			DiffIDs: layerDgsts,
 		},
 	})
 	if err != nil {
@@ -49,14 +61,14 @@ func MultiLayer(dir string) (*ocispec.Index, error) {
 	manifest := ocispec.Manifest{
 		MediaType: ocispec.MediaTypeImageManifest,
 		Config:    configDesc,
-		Layers:    []ocispec.Descriptor{layer1Desc, layer2Desc, layer3Desc},
+		Layers:    layerDescs,
 	}
 
 	legacyManifests := []manifestItem{
 		{
 			Config:   blobPath(configDesc),
 			RepoTags: []string{imageRef},
-			Layers:   []string{blobPath(layer1Desc), blobPath(layer2Desc), blobPath(layer3Desc)},
+			Layers:   layerBlobs,
 		},
 	}
 
@@ -128,6 +140,7 @@ func writeLayerWithOneFile(dir string, filename string, content []byte) (ocispec
 	if err != nil {
 		return ocispec.Descriptor{}, err
 	}
+	defer rd.Close()
 
 	return writeBlob(dir, ocispec.MediaTypeImageLayer, rd)
 }