@@ -18,6 +18,8 @@ import (
1818 "github.com/docker/docker/api/types/versions"
1919 "github.com/docker/docker/integration/internal/build"
2020 "github.com/docker/docker/integration/internal/container"
21+ "github.com/docker/docker/internal/testutils"
22+ "github.com/docker/docker/internal/testutils/specialimage"
2123 "github.com/docker/docker/pkg/archive"
2224 "github.com/docker/docker/testutil/fakecontext"
2325 "github.com/opencontainers/go-digest"
@@ -88,45 +90,126 @@ func TestSaveCheckTimes(t *testing.T) {
8890}
8991
9092// Regression test for https://github.com/moby/moby/issues/47065
91- func TestSaveCheckManifestLayers (t * testing.T ) {
93+ func TestSaveOCI (t * testing.T ) {
9294 skip .If (t , versions .LessThan (testEnv .DaemonAPIVersion (), "1.44" ), "OCI layout support was introduced in v25" )
9395
9496 ctx := setupTest (t )
9597 client := testEnv .APIClient ()
9698
97- t .Parallel ()
98-
99- const repoName = "busybox:latest"
100- img , _ , err := client .ImageInspectWithRaw (ctx , repoName )
99+ const busybox = "busybox:latest"
100+ inspectBusybox , _ , err := client .ImageInspectWithRaw (ctx , busybox )
101101 assert .NilError (t , err )
102102
103- rdr , err := client .ImageSave (ctx , []string {repoName })
104- assert .NilError (t , err )
103+ type testCase struct {
104+ image string
105+ expectedOCIRef string
106+ expectedContainerdRef string
107+ }
105108
106- tarfs := tarIndexFS (t , rdr )
109+ testCases := []testCase {
110+ // Busybox by tagged name
111+ testCase {image : busybox , expectedContainerdRef : "docker.io/library/busybox:latest" , expectedOCIRef : "latest" },
107112
108- indexData , err := fs .ReadFile (tarfs , "index.json" )
109- assert .NilError (t , err )
113+ // Busybox by ID
114+ testCase {image : inspectBusybox .ID },
115+ }
110116
111- var index ocispec.Index
112- assert .NilError (t , json .Unmarshal (indexData , & index ))
117+ if testEnv .DaemonInfo .OSType != "windows" {
118+ multiLayerImage := specialimage .Load (ctx , t , client , specialimage .MultiLayer )
119+ // Multi-layer image
120+ testCases = append (testCases , testCase {image : multiLayerImage , expectedContainerdRef : "docker.io/library/multilayer:latest" , expectedOCIRef : "latest" })
113121
114- assert . Assert ( t , is . Len ( index . Manifests , 1 ))
122+ }
115123
116- manifestData , err := fs .ReadFile (tarfs , "blobs/sha256/" + index .Manifests [0 ].Digest .Encoded ())
117- assert .NilError (t , err )
124+ // Busybox frozen image will have empty RepoDigests when loaded into the
125+ // graphdriver image store so we can't use it.
126+ // This will work with the containerd image store though.
127+ if len (inspectBusybox .RepoDigests ) > 0 {
128+ // Digested reference
129+ testCases = append (testCases , testCase {
130+ image : inspectBusybox .RepoDigests [0 ],
131+ })
132+ }
118133
119- var manifest ocispec.Manifest
120- assert .NilError (t , json .Unmarshal (manifestData , & manifest ))
134+ for _ , tc := range testCases {
135+ tc := tc
136+ t .Run (tc .image , func (t * testing.T ) {
137+ // Get information about the original image.
138+ inspect , _ , err := client .ImageInspectWithRaw (ctx , tc .image )
139+ assert .NilError (t , err )
121140
122- assert .Check (t , is .Len (manifest .Layers , len (img .RootFS .Layers )))
123- for _ , l := range manifest .Layers {
124- stat , err := fs .Stat (tarfs , "blobs/sha256/" + l .Digest .Encoded ())
125- if ! assert .Check (t , err ) {
126- continue
127- }
141+ rdr , err := client .ImageSave (ctx , []string {tc .image })
142+ assert .NilError (t , err )
143+ defer rdr .Close ()
144+
145+ tarfs := tarIndexFS (t , rdr )
146+
147+ indexData , err := fs .ReadFile (tarfs , "index.json" )
148+ assert .NilError (t , err , "failed to read index.json" )
149+
150+ var index ocispec.Index
151+ assert .NilError (t , json .Unmarshal (indexData , & index ), "failed to unmarshal index.json" )
128152
129- assert .Check (t , is .Equal (l .Size , stat .Size ()))
153+ // All test images are single-platform, so they should have only one manifest.
154+ assert .Assert (t , is .Len (index .Manifests , 1 ))
155+
156+ manifestData , err := fs .ReadFile (tarfs , "blobs/sha256/" + index .Manifests [0 ].Digest .Encoded ())
157+ assert .NilError (t , err )
158+
159+ var manifest ocispec.Manifest
160+ assert .NilError (t , json .Unmarshal (manifestData , & manifest ))
161+
162+ t .Run ("Manifest" , func (t * testing.T ) {
163+ assert .Check (t , is .Len (manifest .Layers , len (inspect .RootFS .Layers )))
164+
165+ var digests []string
166+ // Check if layers referenced by the manifest exist in the archive
167+ // and match the layers from the original image.
168+ for _ , l := range manifest .Layers {
169+ layerPath := "blobs/sha256/" + l .Digest .Encoded ()
170+ stat , err := fs .Stat (tarfs , layerPath )
171+ assert .NilError (t , err )
172+
173+ assert .Check (t , is .Equal (l .Size , stat .Size ()))
174+
175+ f , err := tarfs .Open (layerPath )
176+ assert .NilError (t , err )
177+
178+ layerDigest , err := testutils .UncompressedTarDigest (f )
179+ f .Close ()
180+
181+ assert .NilError (t , err )
182+
183+ digests = append (digests , layerDigest .String ())
184+ }
185+
186+ assert .Check (t , is .DeepEqual (digests , inspect .RootFS .Layers ))
187+ })
188+
189+ t .Run ("Config" , func (t * testing.T ) {
190+ configData , err := fs .ReadFile (tarfs , "blobs/sha256/" + manifest .Config .Digest .Encoded ())
191+ assert .NilError (t , err )
192+
193+ var config ocispec.Image
194+ assert .NilError (t , json .Unmarshal (configData , & config ))
195+
196+ var diffIDs []string
197+ for _ , l := range config .RootFS .DiffIDs {
198+ diffIDs = append (diffIDs , l .String ())
199+ }
200+
201+ assert .Check (t , is .DeepEqual (diffIDs , inspect .RootFS .Layers ))
202+ })
203+
204+ t .Run ("Containerd image name" , func (t * testing.T ) {
205+ assert .Check (t , is .Equal (index .Manifests [0 ].Annotations ["io.containerd.image.name" ], tc .expectedContainerdRef ))
206+ })
207+
208+ t .Run ("OCI reference tag" , func (t * testing.T ) {
209+ assert .Check (t , is .Equal (index .Manifests [0 ].Annotations ["org.opencontainers.image.ref.name" ], tc .expectedOCIRef ))
210+ })
211+
212+ })
130213 }
131214}
132215
0 commit comments