Browse code

client/image_list: Wrap options and result

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
Signed-off-by: Austin Vazquez <austin.vazquez@docker.com>

Paweł Gronowski authored on 2025/10/21 00:28:21
Showing 15 changed files
... ...
@@ -111,7 +111,7 @@ type ImageAPIClient interface {
111 111
 	ImageCreate(ctx context.Context, parentReference string, options ImageCreateOptions) (ImageCreateResult, error)
112 112
 	ImageImport(ctx context.Context, source ImageImportSource, ref string, options ImageImportOptions) (ImageImportResult, error)
113 113
 
114
-	ImageList(ctx context.Context, options ImageListOptions) ([]image.Summary, error)
114
+	ImageList(ctx context.Context, options ImageListOptions) (ImageListResult, error)
115 115
 	ImagePull(ctx context.Context, ref string, options ImagePullOptions) (ImagePullResponse, error)
116 116
 	ImagePush(ctx context.Context, ref string, options ImagePushOptions) (ImagePushResponse, error)
117 117
 	ImageRemove(ctx context.Context, image string, options ImageRemoveOptions) ([]image.DeleteResponse, error)
... ...
@@ -15,7 +15,7 @@ import (
15 15
 // to include [image.Summary.Manifests] with information about image manifests.
16 16
 // This is experimental and might change in the future without any backward
17 17
 // compatibility.
18
-func (cli *Client) ImageList(ctx context.Context, options ImageListOptions) ([]image.Summary, error) {
18
+func (cli *Client) ImageList(ctx context.Context, options ImageListOptions) (ImageListResult, error) {
19 19
 	var images []image.Summary
20 20
 
21 21
 	query := url.Values{}
... ...
@@ -34,7 +34,7 @@ func (cli *Client) ImageList(ctx context.Context, options ImageListOptions) ([]i
34 34
 		// Normally, version-negotiation (if enabled) would not happen until
35 35
 		// the API request is made.
36 36
 		if err := cli.checkVersion(ctx); err != nil {
37
-			return images, err
37
+			return ImageListResult{}, err
38 38
 		}
39 39
 
40 40
 		if versions.GreaterThanOrEqualTo(cli.version, "1.47") {
... ...
@@ -45,9 +45,9 @@ func (cli *Client) ImageList(ctx context.Context, options ImageListOptions) ([]i
45 45
 	resp, err := cli.get(ctx, "/images/json", query, nil)
46 46
 	defer ensureReaderClosed(resp)
47 47
 	if err != nil {
48
-		return images, err
48
+		return ImageListResult{}, err
49 49
 	}
50 50
 
51 51
 	err = json.NewDecoder(resp.Body).Decode(&images)
52
-	return images, err
52
+	return ImageListResult{Items: images}, err
53 53
 }
... ...
@@ -1,5 +1,7 @@
1 1
 package client
2 2
 
3
+import "github.com/moby/moby/api/types/image"
4
+
3 5
 // ImageListOptions holds parameters to list images with.
4 6
 type ImageListOptions struct {
5 7
 	// All controls whether all images in the graph are filtered, or just
... ...
@@ -15,3 +17,8 @@ type ImageListOptions struct {
15 15
 	// Manifests indicates whether the image manifests should be returned.
16 16
 	Manifests bool
17 17
 }
18
+
19
+// ImageListResult holds the result from ImageList.
20
+type ImageListResult struct {
21
+	Items []image.Summary
22
+}
... ...
@@ -108,7 +108,7 @@ func TestImageList(t *testing.T) {
108 108
 
109 109
 		images, err := client.ImageList(context.Background(), listCase.options)
110 110
 		assert.NilError(t, err)
111
-		assert.Check(t, is.Len(images, 2))
111
+		assert.Check(t, is.Len(images.Items, 2))
112 112
 	}
113 113
 }
114 114
 
... ...
@@ -102,10 +102,10 @@ func (s *DockerAPISuite) TestAPIImagesSizeCompatibility(c *testing.T) {
102 102
 	apiclient := testEnv.APIClient()
103 103
 	defer apiclient.Close()
104 104
 
105
-	images, err := apiclient.ImageList(testutil.GetContext(c), client.ImageListOptions{})
105
+	imageList, err := apiclient.ImageList(testutil.GetContext(c), client.ImageListOptions{})
106 106
 	assert.NilError(c, err)
107
-	assert.Assert(c, len(images) != 0)
108
-	for _, img := range images {
107
+	assert.Assert(c, len(imageList.Items) != 0)
108
+	for _, img := range imageList.Items {
109 109
 		assert.Assert(c, img.Size != int64(-1))
110 110
 	}
111 111
 
... ...
@@ -115,8 +115,8 @@ func (s *DockerAPISuite) TestAPIImagesSizeCompatibility(c *testing.T) {
115 115
 
116 116
 	v124Images, err := apiclient.ImageList(testutil.GetContext(c), client.ImageListOptions{})
117 117
 	assert.NilError(c, err)
118
-	assert.Assert(c, len(v124Images) != 0)
119
-	for _, img := range v124Images {
118
+	assert.Assert(c, len(v124Images.Items) != 0)
119
+	for _, img := range v124Images.Items {
120 120
 		assert.Assert(c, img.Size != int64(-1))
121 121
 	}
122 122
 }
... ...
@@ -47,7 +47,7 @@ func TestExportContainerAndImportImage(t *testing.T) {
47 47
 		Filters: make(client.Filters).Add("reference", reference),
48 48
 	})
49 49
 	assert.NilError(t, err)
50
-	assert.Check(t, is.Equal(jm.Status, images.Images[0].ID))
50
+	assert.Check(t, is.Equal(jm.Status, images.Items[0].ID))
51 51
 }
52 52
 
53 53
 // TestExportContainerAfterDaemonRestart checks that a container
... ...
@@ -136,7 +136,7 @@ func TestMigrateSaveLoad(t *testing.T) {
136 136
 	// Delete all images
137 137
 	list, err := apiClient.ImageList(ctx, client.ImageListOptions{})
138 138
 	assert.NilError(t, err)
139
-	for _, i := range list {
139
+	for _, i := range list.Items {
140 140
 		_, err = apiClient.ImageRemove(ctx, i.ID, client.ImageRemoveOptions{Force: true})
141 141
 		assert.NilError(t, err)
142 142
 	}
... ...
@@ -43,12 +43,12 @@ func TestImagesFilterMultiReference(t *testing.T) {
43 43
 	options := client.ImageListOptions{
44 44
 		Filters: make(client.Filters).Add("reference", repoTags[:3]...),
45 45
 	}
46
-	images, err := apiClient.ImageList(ctx, options)
46
+	imageList, err := apiClient.ImageList(ctx, options)
47 47
 	assert.NilError(t, err)
48 48
 
49
-	assert.Assert(t, is.Len(images, 1))
50
-	assert.Check(t, is.Len(images[0].RepoTags, 3))
51
-	for _, repoTag := range images[0].RepoTags {
49
+	assert.Assert(t, is.Len(imageList.Items, 1))
50
+	assert.Check(t, is.Len(imageList.Items[0].RepoTags, 3))
51
+	for _, repoTag := range imageList.Items[0].RepoTags {
52 52
 		if repoTag != repoTags[0] && repoTag != repoTags[1] && repoTag != repoTags[2] {
53 53
 			t.Errorf("list images doesn't match any repoTag we expected, repoTag: %s", repoTag)
54 54
 		}
... ...
@@ -91,7 +91,7 @@ func TestImagesFilterUntil(t *testing.T) {
91 91
 	assert.NilError(t, err)
92 92
 
93 93
 	var listedIDs []string
94
-	for _, i := range list {
94
+	for _, i := range list.Items {
95 95
 		t.Logf("ImageList: ID=%v RepoTags=%v", i.ID, i.RepoTags)
96 96
 		listedIDs = append(listedIDs, i.ID)
97 97
 	}
... ...
@@ -124,7 +124,7 @@ func TestImagesFilterBeforeSince(t *testing.T) {
124 124
 	assert.NilError(t, err)
125 125
 
126 126
 	var listedIDs []string
127
-	for _, i := range list {
127
+	for _, i := range list.Items {
128 128
 		t.Logf("ImageList: ID=%v RepoTags=%v", i.ID, i.RepoTags)
129 129
 		listedIDs = append(listedIDs, i.ID)
130 130
 	}
... ...
@@ -181,12 +181,12 @@ func TestAPIImagesFilters(t *testing.T) {
181 181
 			t.Parallel()
182 182
 
183 183
 			ctx := testutil.StartSpan(ctx, t)
184
-			images, err := apiClient.ImageList(ctx, client.ImageListOptions{
184
+			imageList, err := apiClient.ImageList(ctx, client.ImageListOptions{
185 185
 				Filters: tc.filters,
186 186
 			})
187 187
 			assert.Check(t, err)
188
-			assert.Assert(t, is.Len(images, tc.expectedImages))
189
-			assert.Check(t, is.Len(images[0].RepoTags, tc.expectedRepoTags))
188
+			assert.Assert(t, is.Len(imageList.Items, tc.expectedImages))
189
+			assert.Check(t, is.Len(imageList.Items[0].RepoTags, tc.expectedRepoTags))
190 190
 		})
191 191
 	}
192 192
 }
... ...
@@ -255,31 +255,31 @@ func TestAPIImagesListManifests(t *testing.T) {
255 255
 		// TODO: Remove when MinAPIVersion >= 1.47
256 256
 		c := d.NewClientT(t, client.WithVersion("1.46"))
257 257
 
258
-		images, err := c.ImageList(ctx, client.ImageListOptions{Manifests: true})
258
+		imageList, err := c.ImageList(ctx, client.ImageListOptions{Manifests: true})
259 259
 		assert.NilError(t, err)
260 260
 
261
-		assert.Assert(t, is.Len(images, 1))
262
-		assert.Check(t, is.Nil(images[0].Manifests))
261
+		assert.Assert(t, is.Len(imageList.Items, 1))
262
+		assert.Check(t, is.Nil(imageList.Items[0].Manifests))
263 263
 	})
264 264
 
265 265
 	api147 := d.NewClientT(t, client.WithVersion("1.47"))
266 266
 
267 267
 	t.Run("no manifests if not requested", func(t *testing.T) {
268
-		images, err := api147.ImageList(ctx, client.ImageListOptions{})
268
+		imageList, err := api147.ImageList(ctx, client.ImageListOptions{})
269 269
 		assert.NilError(t, err)
270 270
 
271
-		assert.Assert(t, is.Len(images, 1))
272
-		assert.Check(t, is.Nil(images[0].Manifests))
271
+		assert.Assert(t, is.Len(imageList.Items, 1))
272
+		assert.Check(t, is.Nil(imageList.Items[0].Manifests))
273 273
 	})
274 274
 
275
-	images, err := api147.ImageList(ctx, client.ImageListOptions{Manifests: true})
275
+	imageList, err := api147.ImageList(ctx, client.ImageListOptions{Manifests: true})
276 276
 	assert.NilError(t, err)
277 277
 
278
-	assert.Check(t, is.Len(images, 1))
279
-	assert.Check(t, images[0].Manifests != nil)
280
-	assert.Check(t, is.Len(images[0].Manifests, 3))
278
+	assert.Check(t, is.Len(imageList.Items, 1))
279
+	assert.Check(t, imageList.Items[0].Manifests != nil)
280
+	assert.Check(t, is.Len(imageList.Items[0].Manifests, 3))
281 281
 
282
-	for _, mfst := range images[0].Manifests {
282
+	for _, mfst := range imageList.Items[0].Manifests {
283 283
 		// All manifests should be image manifests
284 284
 		assert.Check(t, is.Equal(mfst.Kind, image.ManifestKindImage))
285 285
 
... ...
@@ -29,7 +29,7 @@ func TestLoadDanglingImages(t *testing.T) {
29 29
 	})
30 30
 
31 31
 	// Should be one image.
32
-	images, err := apiClient.ImageList(ctx, client.ImageListOptions{})
32
+	imageList, err := apiClient.ImageList(ctx, client.ImageListOptions{})
33 33
 	assert.NilError(t, err)
34 34
 
35 35
 	findImageByName := func(images []image.Summary, imageName string) (image.Summary, error) {
... ...
@@ -42,7 +42,7 @@ func TestLoadDanglingImages(t *testing.T) {
42 42
 		return images[index], nil
43 43
 	}
44 44
 
45
-	oldImage, err := findImageByName(images, "namedimage:latest")
45
+	oldImage, err := findImageByName(imageList.Items, "namedimage:latest")
46 46
 	assert.NilError(t, err)
47 47
 
48 48
 	// Retain a copy of the old image and then replace it with a new one.
... ...
@@ -52,10 +52,10 @@ func TestLoadDanglingImages(t *testing.T) {
52 52
 		})
53 53
 	})
54 54
 
55
-	images, err = apiClient.ImageList(ctx, client.ImageListOptions{})
55
+	imageList, err = apiClient.ImageList(ctx, client.ImageListOptions{})
56 56
 	assert.NilError(t, err)
57 57
 
58
-	newImage, err := findImageByName(images, "namedimage:latest")
58
+	newImage, err := findImageByName(imageList.Items, "namedimage:latest")
59 59
 	assert.NilError(t, err)
60 60
 
61 61
 	// IDs should be different.
... ...
@@ -72,7 +72,7 @@ func TestLoadDanglingImages(t *testing.T) {
72 72
 		return images[index], nil
73 73
 	}
74 74
 
75
-	danglingImage, err := findImageById(images, oldImage.ID)
75
+	danglingImage, err := findImageById(imageList.Items, oldImage.ID)
76 76
 	assert.NilError(t, err)
77 77
 	assert.Check(t, is.Len(danglingImage.RepoTags, 0))
78 78
 }
... ...
@@ -94,10 +94,10 @@ func getAllContainers(ctx context.Context, t testing.TB, apiClient client.Contai
94 94
 
95 95
 func deleteAllImages(ctx context.Context, t testing.TB, apiclient client.ImageAPIClient, protectedImages map[string]struct{}) {
96 96
 	t.Helper()
97
-	images, err := apiclient.ImageList(ctx, client.ImageListOptions{})
97
+	imageList, err := apiclient.ImageList(ctx, client.ImageListOptions{})
98 98
 	assert.Check(t, err, "failed to list images")
99 99
 
100
-	for _, img := range images {
100
+	for _, img := range imageList.Items {
101 101
 		tags := tagsFromImageSummary(img)
102 102
 		if _, ok := protectedImages[img.ID]; ok {
103 103
 			continue
... ...
@@ -205,7 +205,7 @@ func (e *Execution) HasExistingImage(t testing.TB, reference string) bool {
205 205
 	})
206 206
 	assert.NilError(t, err, "failed to list images")
207 207
 
208
-	return len(imageList) > 0
208
+	return len(imageList.Items) > 0
209 209
 }
210 210
 
211 211
 // EnsureFrozenImagesLinux loads frozen test images into the daemon
... ...
@@ -121,7 +121,7 @@ func getExistingImages(ctx context.Context, t testing.TB, testEnv *Execution) []
121 121
 	assert.NilError(t, err, "failed to list images")
122 122
 
123 123
 	var images []string
124
-	for _, img := range imageList {
124
+	for _, img := range imageList.Items {
125 125
 		images = append(images, tagsFromImageSummary(img)...)
126 126
 	}
127 127
 	return images
... ...
@@ -111,7 +111,7 @@ type ImageAPIClient interface {
111 111
 	ImageCreate(ctx context.Context, parentReference string, options ImageCreateOptions) (ImageCreateResult, error)
112 112
 	ImageImport(ctx context.Context, source ImageImportSource, ref string, options ImageImportOptions) (ImageImportResult, error)
113 113
 
114
-	ImageList(ctx context.Context, options ImageListOptions) ([]image.Summary, error)
114
+	ImageList(ctx context.Context, options ImageListOptions) (ImageListResult, error)
115 115
 	ImagePull(ctx context.Context, ref string, options ImagePullOptions) (ImagePullResponse, error)
116 116
 	ImagePush(ctx context.Context, ref string, options ImagePushOptions) (ImagePushResponse, error)
117 117
 	ImageRemove(ctx context.Context, image string, options ImageRemoveOptions) ([]image.DeleteResponse, error)
... ...
@@ -15,7 +15,7 @@ import (
15 15
 // to include [image.Summary.Manifests] with information about image manifests.
16 16
 // This is experimental and might change in the future without any backward
17 17
 // compatibility.
18
-func (cli *Client) ImageList(ctx context.Context, options ImageListOptions) ([]image.Summary, error) {
18
+func (cli *Client) ImageList(ctx context.Context, options ImageListOptions) (ImageListResult, error) {
19 19
 	var images []image.Summary
20 20
 
21 21
 	query := url.Values{}
... ...
@@ -34,7 +34,7 @@ func (cli *Client) ImageList(ctx context.Context, options ImageListOptions) ([]i
34 34
 		// Normally, version-negotiation (if enabled) would not happen until
35 35
 		// the API request is made.
36 36
 		if err := cli.checkVersion(ctx); err != nil {
37
-			return images, err
37
+			return ImageListResult{}, err
38 38
 		}
39 39
 
40 40
 		if versions.GreaterThanOrEqualTo(cli.version, "1.47") {
... ...
@@ -45,9 +45,9 @@ func (cli *Client) ImageList(ctx context.Context, options ImageListOptions) ([]i
45 45
 	resp, err := cli.get(ctx, "/images/json", query, nil)
46 46
 	defer ensureReaderClosed(resp)
47 47
 	if err != nil {
48
-		return images, err
48
+		return ImageListResult{}, err
49 49
 	}
50 50
 
51 51
 	err = json.NewDecoder(resp.Body).Decode(&images)
52
-	return images, err
52
+	return ImageListResult{Items: images}, err
53 53
 }
... ...
@@ -1,5 +1,7 @@
1 1
 package client
2 2
 
3
+import "github.com/moby/moby/api/types/image"
4
+
3 5
 // ImageListOptions holds parameters to list images with.
4 6
 type ImageListOptions struct {
5 7
 	// All controls whether all images in the graph are filtered, or just
... ...
@@ -15,3 +17,8 @@ type ImageListOptions struct {
15 15
 	// Manifests indicates whether the image manifests should be returned.
16 16
 	Manifests bool
17 17
 }
18
+
19
+// ImageListResult holds the result from ImageList.
20
+type ImageListResult struct {
21
+	Items []image.Summary
22
+}