@@ -3,6 +3,7 @@ package containerd
33import (
44 "bytes"
55 "context"
6+ "fmt"
67 "math/rand"
78 "os"
89 "path/filepath"
@@ -16,15 +17,18 @@ import (
1617 "github.com/docker/docker/internal/testutils/labelstore"
1718 "github.com/docker/docker/internal/testutils/specialimage"
1819 "github.com/moby/go-archive"
20+ "github.com/moby/moby/api/types/backend"
21+ "github.com/moby/moby/api/types/image"
1922 ocispec "github.com/opencontainers/image-spec/specs-go/v1"
2023 "gotest.tools/v3/assert"
2124 is "gotest.tools/v3/assert/cmp"
2225)
2326
24- func TestImageLoadMissing (t * testing.T ) {
27+ func TestImageLoad (t * testing.T ) {
2528 linuxAmd64 := ocispec.Platform {OS : "linux" , Architecture : "amd64" }
2629 linuxArm64 := ocispec.Platform {OS : "linux" , Architecture : "arm64" }
2730 linuxArmv5 := ocispec.Platform {OS : "linux" , Architecture : "arm" , Variant : "v5" }
31+ linuxRiscv64 := ocispec.Platform {OS : "linux" , Architecture : "riskv64" }
2832
2933 ctx := namespaces .WithNamespace (context .TODO (), "testing-" + t .Name ())
3034
@@ -35,7 +39,7 @@ func TestImageLoadMissing(t *testing.T) {
3539 // Mock the daemon platform.
3640 imgSvc .defaultPlatformOverride = platforms .Only (linuxAmd64 )
3741
38- tryLoad := func (ctx context.Context , t * testing.T , dir string , platform ocispec.Platform ) error {
42+ tryLoad := func (ctx context.Context , t * testing.T , dir string , platformList [] ocispec.Platform ) error {
3943 tarRc , err := archive .Tar (dir , archive .Uncompressed )
4044 assert .NilError (t , err )
4145 defer tarRc .Close ()
@@ -46,10 +50,19 @@ func TestImageLoadMissing(t *testing.T) {
4650 t .Log (buf .String ())
4751 }()
4852
49- return imgSvc .LoadImage (ctx , tarRc , & platform , & buf , true )
53+ return imgSvc .LoadImage (ctx , tarRc , platformList , & buf , true )
5054 }
5155
52- clearStore := func (ctx context.Context , t * testing.T ) {
56+ cleanup := func (ctx context.Context , t * testing.T ) {
57+ // Remove all existing images to start fresh
58+ images , err := imgSvc .Images (ctx , image.ListOptions {})
59+ assert .NilError (t , err )
60+ for _ , img := range images {
61+ _ , err := imgSvc .ImageDelete (ctx , img .ID , image.RemoveOptions {PruneChildren : true })
62+ assert .NilError (t , err )
63+ }
64+
65+ // Remove all content from the store
5366 assert .NilError (t , store .Walk (ctx , func (info content.Info ) error {
5467 return store .Delete (ctx , info .Digest )
5568 }), "failed to delete all content" )
@@ -60,39 +73,64 @@ func TestImageLoadMissing(t *testing.T) {
6073 _ , err := specialimage .EmptyIndex (imgDataDir )
6174 assert .NilError (t , err )
6275
63- err = tryLoad (ctx , t , imgDataDir , linuxAmd64 )
64- assert .Check (t , is .Error (err , "image emptyindex:latest was loaded, but doesn't provide the requested platform (linux/amd64)" ))
76+ err = tryLoad (ctx , t , imgDataDir , []ocispec. Platform { linuxAmd64 } )
77+ assert .Check (t , is .Error (err , "image emptyindex:latest was loaded, but doesn't provide the requested platform ([ linux/amd64] )" ))
6578 assert .Check (t , is .ErrorType (err , cerrdefs .IsNotFound ))
6679 })
67- clearStore (ctx , t )
80+ cleanup (ctx , t )
6881
6982 t .Run ("single platform" , func (t * testing.T ) {
7083 imgDataDir := t .TempDir ()
7184 r := rand .NewSource (0x9127371238 )
72- _ , err : = specialimage .RandomSinglePlatform (imgDataDir , linuxAmd64 , r )
85+ _ , err = specialimage .RandomSinglePlatform (imgDataDir , linuxAmd64 , r )
7386 assert .NilError (t , err )
7487
75- err = tryLoad (ctx , t , imgDataDir , linuxArm64 )
76- assert .Check (t , is .ErrorContains (err , "doesn't provide the requested platform (linux/arm64)" ))
88+ platforms := []ocispec.Platform {linuxAmd64 }
89+ err = tryLoad (ctx , t , imgDataDir , platforms )
90+ assert .NilError (t , err )
91+
92+ err = tryLoad (ctx , t , imgDataDir , []ocispec.Platform {linuxArm64 })
93+ assert .Check (t , is .ErrorContains (err , "doesn't provide the requested platform ([linux/arm64])" ))
7794 assert .Check (t , is .ErrorType (err , cerrdefs .IsNotFound ))
7895 })
96+ cleanup (ctx , t )
7997
80- clearStore (ctx , t )
81-
82- t .Run ("2 platform image" , func (t * testing.T ) {
98+ t .Run ("multi-platform image" , func (t * testing.T ) {
8399 imgDataDir := t .TempDir ()
84- _ , mfstDescs , err := specialimage .MultiPlatform (imgDataDir , "multiplatform:latest" , []ocispec.Platform {linuxAmd64 , linuxArm64 })
100+ imgRef := "multiplatform:latest"
101+ _ , mfstDescs , err := specialimage .MultiPlatform (imgDataDir , imgRef , []ocispec.Platform {linuxAmd64 , linuxArm64 , linuxRiscv64 })
85102 assert .NilError (t , err )
86103
104+ t .Run ("one platform in index" , func (t * testing.T ) {
105+ platforms := []ocispec.Platform {linuxAmd64 }
106+ err = tryLoad (ctx , t , imgDataDir , platforms )
107+ assert .NilError (t , err )
108+
109+ // verify that the loaded image has the correct platform
110+ err = verifyImagePlatforms (ctx , imgSvc , imgRef , platforms )
111+ assert .NilError (t , err )
112+ })
113+ cleanup (ctx , t )
114+
115+ t .Run ("all platforms in index" , func (t * testing.T ) {
116+ platforms := []ocispec.Platform {linuxAmd64 , linuxArm64 , linuxRiscv64 }
117+ err = tryLoad (ctx , t , imgDataDir , platforms )
118+ assert .NilError (t , err )
119+
120+ // verify that the loaded image has the correct platforms
121+ err = verifyImagePlatforms (ctx , imgSvc , imgRef , platforms )
122+ assert .NilError (t , err )
123+ })
124+ cleanup (ctx , t )
125+
87126 t .Run ("platform not included in index" , func (t * testing.T ) {
88- err = tryLoad (ctx , t , imgDataDir , linuxArmv5 )
89- assert .Check (t , is .Error (err , "image multiplatform:latest was loaded, but doesn't provide the requested platform (linux/arm/v5)" ))
127+ err = tryLoad (ctx , t , imgDataDir , []ocispec. Platform { linuxArmv5 } )
128+ assert .Check (t , is .Error (err , "image multiplatform:latest was loaded, but doesn't provide the requested platform ([ linux/arm/v5] )" ))
90129 assert .Check (t , is .ErrorType (err , cerrdefs .IsNotFound ))
91130 })
131+ cleanup (ctx , t )
92132
93- clearStore (ctx , t )
94-
95- t .Run ("platform blobs missing" , func (t * testing.T ) {
133+ t .Run ("platform included but blobs missing" , func (t * testing.T ) {
96134 // Assumption: arm64 image is second in the index (implementation detail of specialimage.MultiPlatform)
97135 mfstDesc := mfstDescs [1 ]
98136 assert .Assert (t , mfstDesc .Platform .Architecture == linuxArm64 .Architecture )
@@ -104,9 +142,37 @@ func TestImageLoadMissing(t *testing.T) {
104142 mfstPath := filepath .Join (imgDataDir , "blobs/sha256" , mfstDesc .Digest .Encoded ())
105143 assert .NilError (t , os .Remove (mfstPath ))
106144
107- err = tryLoad (ctx , t , imgDataDir , linuxArm64 )
108- assert .Check (t , is .ErrorContains (err , "requested platform ( linux/arm64) found, but some content is missing" ))
145+ err = tryLoad (ctx , t , imgDataDir , []ocispec. Platform { linuxArm64 } )
146+ assert .Check (t , is .ErrorContains (err , "requested platform(s) ([ linux/arm64] ) found, but some content is missing" ))
109147 assert .Check (t , is .ErrorType (err , cerrdefs .IsNotFound ))
110148 })
149+ cleanup (ctx , t )
111150 })
112151}
152+
153+ func verifyImagePlatforms (ctx context.Context , imgSvc * ImageService , imgRef string , expectedPlatforms []ocispec.Platform ) error {
154+ // get the manifest(s) for the image
155+ img , err := imgSvc .ImageInspect (ctx , imgRef , backend.ImageInspectOpts {Manifests : true })
156+ if err != nil {
157+ return err
158+ }
159+ // verify that the image manifest has the expected platforms
160+ for _ , ep := range expectedPlatforms {
161+ want := platforms .FormatAll (ep )
162+ found := false
163+ for _ , m := range img .Manifests {
164+ if m .Descriptor .Platform != nil {
165+ got := platforms .FormatAll (* m .Descriptor .Platform )
166+ if got == want {
167+ found = true
168+ break
169+ }
170+ }
171+ }
172+ if ! found {
173+ return fmt .Errorf ("expected platform %q not found in loaded images" , want )
174+ }
175+ }
176+
177+ return nil
178+ }
0 commit comments