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>
This commit is contained in:
Paweł Gronowski
2025-10-20 17:28:21 +02:00
committed by Austin Vazquez
parent a7f409014f
commit b3974f07f5
15 changed files with 61 additions and 47 deletions

View File

@@ -111,7 +111,7 @@ type ImageAPIClient interface {
ImageCreate(ctx context.Context, parentReference string, options ImageCreateOptions) (ImageCreateResult, error) ImageCreate(ctx context.Context, parentReference string, options ImageCreateOptions) (ImageCreateResult, error)
ImageImport(ctx context.Context, source ImageImportSource, ref string, options ImageImportOptions) (ImageImportResult, error) ImageImport(ctx context.Context, source ImageImportSource, ref string, options ImageImportOptions) (ImageImportResult, error)
ImageList(ctx context.Context, options ImageListOptions) ([]image.Summary, error) ImageList(ctx context.Context, options ImageListOptions) (ImageListResult, error)
ImagePull(ctx context.Context, ref string, options ImagePullOptions) (ImagePullResponse, error) ImagePull(ctx context.Context, ref string, options ImagePullOptions) (ImagePullResponse, error)
ImagePush(ctx context.Context, ref string, options ImagePushOptions) (ImagePushResponse, error) ImagePush(ctx context.Context, ref string, options ImagePushOptions) (ImagePushResponse, error)
ImageRemove(ctx context.Context, image string, options ImageRemoveOptions) ([]image.DeleteResponse, error) ImageRemove(ctx context.Context, image string, options ImageRemoveOptions) ([]image.DeleteResponse, error)

View File

@@ -15,7 +15,7 @@ import (
// to include [image.Summary.Manifests] with information about image manifests. // to include [image.Summary.Manifests] with information about image manifests.
// This is experimental and might change in the future without any backward // This is experimental and might change in the future without any backward
// compatibility. // compatibility.
func (cli *Client) ImageList(ctx context.Context, options ImageListOptions) ([]image.Summary, error) { func (cli *Client) ImageList(ctx context.Context, options ImageListOptions) (ImageListResult, error) {
var images []image.Summary var images []image.Summary
query := url.Values{} query := url.Values{}
@@ -34,7 +34,7 @@ func (cli *Client) ImageList(ctx context.Context, options ImageListOptions) ([]i
// Normally, version-negotiation (if enabled) would not happen until // Normally, version-negotiation (if enabled) would not happen until
// the API request is made. // the API request is made.
if err := cli.checkVersion(ctx); err != nil { if err := cli.checkVersion(ctx); err != nil {
return images, err return ImageListResult{}, err
} }
if versions.GreaterThanOrEqualTo(cli.version, "1.47") { if versions.GreaterThanOrEqualTo(cli.version, "1.47") {
@@ -45,9 +45,9 @@ func (cli *Client) ImageList(ctx context.Context, options ImageListOptions) ([]i
resp, err := cli.get(ctx, "/images/json", query, nil) resp, err := cli.get(ctx, "/images/json", query, nil)
defer ensureReaderClosed(resp) defer ensureReaderClosed(resp)
if err != nil { if err != nil {
return images, err return ImageListResult{}, err
} }
err = json.NewDecoder(resp.Body).Decode(&images) err = json.NewDecoder(resp.Body).Decode(&images)
return images, err return ImageListResult{Items: images}, err
} }

View File

@@ -1,5 +1,7 @@
package client package client
import "github.com/moby/moby/api/types/image"
// ImageListOptions holds parameters to list images with. // ImageListOptions holds parameters to list images with.
type ImageListOptions struct { type ImageListOptions struct {
// All controls whether all images in the graph are filtered, or just // All controls whether all images in the graph are filtered, or just
@@ -15,3 +17,8 @@ type ImageListOptions struct {
// Manifests indicates whether the image manifests should be returned. // Manifests indicates whether the image manifests should be returned.
Manifests bool Manifests bool
} }
// ImageListResult holds the result from ImageList.
type ImageListResult struct {
Items []image.Summary
}

View File

@@ -108,7 +108,7 @@ func TestImageList(t *testing.T) {
images, err := client.ImageList(context.Background(), listCase.options) images, err := client.ImageList(context.Background(), listCase.options)
assert.NilError(t, err) assert.NilError(t, err)
assert.Check(t, is.Len(images, 2)) assert.Check(t, is.Len(images.Items, 2))
} }
} }

View File

@@ -102,10 +102,10 @@ func (s *DockerAPISuite) TestAPIImagesSizeCompatibility(c *testing.T) {
apiclient := testEnv.APIClient() apiclient := testEnv.APIClient()
defer apiclient.Close() defer apiclient.Close()
images, err := apiclient.ImageList(testutil.GetContext(c), client.ImageListOptions{}) imageList, err := apiclient.ImageList(testutil.GetContext(c), client.ImageListOptions{})
assert.NilError(c, err) assert.NilError(c, err)
assert.Assert(c, len(images) != 0) assert.Assert(c, len(imageList.Items) != 0)
for _, img := range images { for _, img := range imageList.Items {
assert.Assert(c, img.Size != int64(-1)) assert.Assert(c, img.Size != int64(-1))
} }
@@ -115,8 +115,8 @@ func (s *DockerAPISuite) TestAPIImagesSizeCompatibility(c *testing.T) {
v124Images, err := apiclient.ImageList(testutil.GetContext(c), client.ImageListOptions{}) v124Images, err := apiclient.ImageList(testutil.GetContext(c), client.ImageListOptions{})
assert.NilError(c, err) assert.NilError(c, err)
assert.Assert(c, len(v124Images) != 0) assert.Assert(c, len(v124Images.Items) != 0)
for _, img := range v124Images { for _, img := range v124Images.Items {
assert.Assert(c, img.Size != int64(-1)) assert.Assert(c, img.Size != int64(-1))
} }
} }

View File

@@ -47,7 +47,7 @@ func TestExportContainerAndImportImage(t *testing.T) {
Filters: make(client.Filters).Add("reference", reference), Filters: make(client.Filters).Add("reference", reference),
}) })
assert.NilError(t, err) assert.NilError(t, err)
assert.Check(t, is.Equal(jm.Status, images.Images[0].ID)) assert.Check(t, is.Equal(jm.Status, images.Items[0].ID))
} }
// TestExportContainerAfterDaemonRestart checks that a container // TestExportContainerAfterDaemonRestart checks that a container

View File

@@ -136,7 +136,7 @@ func TestMigrateSaveLoad(t *testing.T) {
// Delete all images // Delete all images
list, err := apiClient.ImageList(ctx, client.ImageListOptions{}) list, err := apiClient.ImageList(ctx, client.ImageListOptions{})
assert.NilError(t, err) assert.NilError(t, err)
for _, i := range list { for _, i := range list.Items {
_, err = apiClient.ImageRemove(ctx, i.ID, client.ImageRemoveOptions{Force: true}) _, err = apiClient.ImageRemove(ctx, i.ID, client.ImageRemoveOptions{Force: true})
assert.NilError(t, err) assert.NilError(t, err)
} }

View File

@@ -43,12 +43,12 @@ func TestImagesFilterMultiReference(t *testing.T) {
options := client.ImageListOptions{ options := client.ImageListOptions{
Filters: make(client.Filters).Add("reference", repoTags[:3]...), Filters: make(client.Filters).Add("reference", repoTags[:3]...),
} }
images, err := apiClient.ImageList(ctx, options) imageList, err := apiClient.ImageList(ctx, options)
assert.NilError(t, err) assert.NilError(t, err)
assert.Assert(t, is.Len(images, 1)) assert.Assert(t, is.Len(imageList.Items, 1))
assert.Check(t, is.Len(images[0].RepoTags, 3)) assert.Check(t, is.Len(imageList.Items[0].RepoTags, 3))
for _, repoTag := range images[0].RepoTags { for _, repoTag := range imageList.Items[0].RepoTags {
if repoTag != repoTags[0] && repoTag != repoTags[1] && repoTag != repoTags[2] { if repoTag != repoTags[0] && repoTag != repoTags[1] && repoTag != repoTags[2] {
t.Errorf("list images doesn't match any repoTag we expected, repoTag: %s", repoTag) t.Errorf("list images doesn't match any repoTag we expected, repoTag: %s", repoTag)
} }
@@ -91,7 +91,7 @@ func TestImagesFilterUntil(t *testing.T) {
assert.NilError(t, err) assert.NilError(t, err)
var listedIDs []string var listedIDs []string
for _, i := range list { for _, i := range list.Items {
t.Logf("ImageList: ID=%v RepoTags=%v", i.ID, i.RepoTags) t.Logf("ImageList: ID=%v RepoTags=%v", i.ID, i.RepoTags)
listedIDs = append(listedIDs, i.ID) listedIDs = append(listedIDs, i.ID)
} }
@@ -124,7 +124,7 @@ func TestImagesFilterBeforeSince(t *testing.T) {
assert.NilError(t, err) assert.NilError(t, err)
var listedIDs []string var listedIDs []string
for _, i := range list { for _, i := range list.Items {
t.Logf("ImageList: ID=%v RepoTags=%v", i.ID, i.RepoTags) t.Logf("ImageList: ID=%v RepoTags=%v", i.ID, i.RepoTags)
listedIDs = append(listedIDs, i.ID) listedIDs = append(listedIDs, i.ID)
} }
@@ -181,12 +181,12 @@ func TestAPIImagesFilters(t *testing.T) {
t.Parallel() t.Parallel()
ctx := testutil.StartSpan(ctx, t) ctx := testutil.StartSpan(ctx, t)
images, err := apiClient.ImageList(ctx, client.ImageListOptions{ imageList, err := apiClient.ImageList(ctx, client.ImageListOptions{
Filters: tc.filters, Filters: tc.filters,
}) })
assert.Check(t, err) assert.Check(t, err)
assert.Assert(t, is.Len(images, tc.expectedImages)) assert.Assert(t, is.Len(imageList.Items, tc.expectedImages))
assert.Check(t, is.Len(images[0].RepoTags, tc.expectedRepoTags)) assert.Check(t, is.Len(imageList.Items[0].RepoTags, tc.expectedRepoTags))
}) })
} }
} }
@@ -255,31 +255,31 @@ func TestAPIImagesListManifests(t *testing.T) {
// TODO: Remove when MinAPIVersion >= 1.47 // TODO: Remove when MinAPIVersion >= 1.47
c := d.NewClientT(t, client.WithVersion("1.46")) c := d.NewClientT(t, client.WithVersion("1.46"))
images, err := c.ImageList(ctx, client.ImageListOptions{Manifests: true}) imageList, err := c.ImageList(ctx, client.ImageListOptions{Manifests: true})
assert.NilError(t, err) assert.NilError(t, err)
assert.Assert(t, is.Len(images, 1)) assert.Assert(t, is.Len(imageList.Items, 1))
assert.Check(t, is.Nil(images[0].Manifests)) assert.Check(t, is.Nil(imageList.Items[0].Manifests))
}) })
api147 := d.NewClientT(t, client.WithVersion("1.47")) api147 := d.NewClientT(t, client.WithVersion("1.47"))
t.Run("no manifests if not requested", func(t *testing.T) { t.Run("no manifests if not requested", func(t *testing.T) {
images, err := api147.ImageList(ctx, client.ImageListOptions{}) imageList, err := api147.ImageList(ctx, client.ImageListOptions{})
assert.NilError(t, err) assert.NilError(t, err)
assert.Assert(t, is.Len(images, 1)) assert.Assert(t, is.Len(imageList.Items, 1))
assert.Check(t, is.Nil(images[0].Manifests)) assert.Check(t, is.Nil(imageList.Items[0].Manifests))
}) })
images, err := api147.ImageList(ctx, client.ImageListOptions{Manifests: true}) imageList, err := api147.ImageList(ctx, client.ImageListOptions{Manifests: true})
assert.NilError(t, err) assert.NilError(t, err)
assert.Check(t, is.Len(images, 1)) assert.Check(t, is.Len(imageList.Items, 1))
assert.Check(t, images[0].Manifests != nil) assert.Check(t, imageList.Items[0].Manifests != nil)
assert.Check(t, is.Len(images[0].Manifests, 3)) assert.Check(t, is.Len(imageList.Items[0].Manifests, 3))
for _, mfst := range images[0].Manifests { for _, mfst := range imageList.Items[0].Manifests {
// All manifests should be image manifests // All manifests should be image manifests
assert.Check(t, is.Equal(mfst.Kind, image.ManifestKindImage)) assert.Check(t, is.Equal(mfst.Kind, image.ManifestKindImage))

View File

@@ -29,7 +29,7 @@ func TestLoadDanglingImages(t *testing.T) {
}) })
// Should be one image. // Should be one image.
images, err := apiClient.ImageList(ctx, client.ImageListOptions{}) imageList, err := apiClient.ImageList(ctx, client.ImageListOptions{})
assert.NilError(t, err) assert.NilError(t, err)
findImageByName := func(images []image.Summary, imageName string) (image.Summary, error) { findImageByName := func(images []image.Summary, imageName string) (image.Summary, error) {
@@ -42,7 +42,7 @@ func TestLoadDanglingImages(t *testing.T) {
return images[index], nil return images[index], nil
} }
oldImage, err := findImageByName(images, "namedimage:latest") oldImage, err := findImageByName(imageList.Items, "namedimage:latest")
assert.NilError(t, err) assert.NilError(t, err)
// Retain a copy of the old image and then replace it with a new one. // Retain a copy of the old image and then replace it with a new one.
@@ -52,10 +52,10 @@ func TestLoadDanglingImages(t *testing.T) {
}) })
}) })
images, err = apiClient.ImageList(ctx, client.ImageListOptions{}) imageList, err = apiClient.ImageList(ctx, client.ImageListOptions{})
assert.NilError(t, err) assert.NilError(t, err)
newImage, err := findImageByName(images, "namedimage:latest") newImage, err := findImageByName(imageList.Items, "namedimage:latest")
assert.NilError(t, err) assert.NilError(t, err)
// IDs should be different. // IDs should be different.
@@ -72,7 +72,7 @@ func TestLoadDanglingImages(t *testing.T) {
return images[index], nil return images[index], nil
} }
danglingImage, err := findImageById(images, oldImage.ID) danglingImage, err := findImageById(imageList.Items, oldImage.ID)
assert.NilError(t, err) assert.NilError(t, err)
assert.Check(t, is.Len(danglingImage.RepoTags, 0)) assert.Check(t, is.Len(danglingImage.RepoTags, 0))
} }

View File

@@ -94,10 +94,10 @@ func getAllContainers(ctx context.Context, t testing.TB, apiClient client.Contai
func deleteAllImages(ctx context.Context, t testing.TB, apiclient client.ImageAPIClient, protectedImages map[string]struct{}) { func deleteAllImages(ctx context.Context, t testing.TB, apiclient client.ImageAPIClient, protectedImages map[string]struct{}) {
t.Helper() t.Helper()
images, err := apiclient.ImageList(ctx, client.ImageListOptions{}) imageList, err := apiclient.ImageList(ctx, client.ImageListOptions{})
assert.Check(t, err, "failed to list images") assert.Check(t, err, "failed to list images")
for _, img := range images { for _, img := range imageList.Items {
tags := tagsFromImageSummary(img) tags := tagsFromImageSummary(img)
if _, ok := protectedImages[img.ID]; ok { if _, ok := protectedImages[img.ID]; ok {
continue continue

View File

@@ -205,7 +205,7 @@ func (e *Execution) HasExistingImage(t testing.TB, reference string) bool {
}) })
assert.NilError(t, err, "failed to list images") assert.NilError(t, err, "failed to list images")
return len(imageList) > 0 return len(imageList.Items) > 0
} }
// EnsureFrozenImagesLinux loads frozen test images into the daemon // EnsureFrozenImagesLinux loads frozen test images into the daemon

View File

@@ -121,7 +121,7 @@ func getExistingImages(ctx context.Context, t testing.TB, testEnv *Execution) []
assert.NilError(t, err, "failed to list images") assert.NilError(t, err, "failed to list images")
var images []string var images []string
for _, img := range imageList { for _, img := range imageList.Items {
images = append(images, tagsFromImageSummary(img)...) images = append(images, tagsFromImageSummary(img)...)
} }
return images return images

View File

@@ -111,7 +111,7 @@ type ImageAPIClient interface {
ImageCreate(ctx context.Context, parentReference string, options ImageCreateOptions) (ImageCreateResult, error) ImageCreate(ctx context.Context, parentReference string, options ImageCreateOptions) (ImageCreateResult, error)
ImageImport(ctx context.Context, source ImageImportSource, ref string, options ImageImportOptions) (ImageImportResult, error) ImageImport(ctx context.Context, source ImageImportSource, ref string, options ImageImportOptions) (ImageImportResult, error)
ImageList(ctx context.Context, options ImageListOptions) ([]image.Summary, error) ImageList(ctx context.Context, options ImageListOptions) (ImageListResult, error)
ImagePull(ctx context.Context, ref string, options ImagePullOptions) (ImagePullResponse, error) ImagePull(ctx context.Context, ref string, options ImagePullOptions) (ImagePullResponse, error)
ImagePush(ctx context.Context, ref string, options ImagePushOptions) (ImagePushResponse, error) ImagePush(ctx context.Context, ref string, options ImagePushOptions) (ImagePushResponse, error)
ImageRemove(ctx context.Context, image string, options ImageRemoveOptions) ([]image.DeleteResponse, error) ImageRemove(ctx context.Context, image string, options ImageRemoveOptions) ([]image.DeleteResponse, error)

View File

@@ -15,7 +15,7 @@ import (
// to include [image.Summary.Manifests] with information about image manifests. // to include [image.Summary.Manifests] with information about image manifests.
// This is experimental and might change in the future without any backward // This is experimental and might change in the future without any backward
// compatibility. // compatibility.
func (cli *Client) ImageList(ctx context.Context, options ImageListOptions) ([]image.Summary, error) { func (cli *Client) ImageList(ctx context.Context, options ImageListOptions) (ImageListResult, error) {
var images []image.Summary var images []image.Summary
query := url.Values{} query := url.Values{}
@@ -34,7 +34,7 @@ func (cli *Client) ImageList(ctx context.Context, options ImageListOptions) ([]i
// Normally, version-negotiation (if enabled) would not happen until // Normally, version-negotiation (if enabled) would not happen until
// the API request is made. // the API request is made.
if err := cli.checkVersion(ctx); err != nil { if err := cli.checkVersion(ctx); err != nil {
return images, err return ImageListResult{}, err
} }
if versions.GreaterThanOrEqualTo(cli.version, "1.47") { if versions.GreaterThanOrEqualTo(cli.version, "1.47") {
@@ -45,9 +45,9 @@ func (cli *Client) ImageList(ctx context.Context, options ImageListOptions) ([]i
resp, err := cli.get(ctx, "/images/json", query, nil) resp, err := cli.get(ctx, "/images/json", query, nil)
defer ensureReaderClosed(resp) defer ensureReaderClosed(resp)
if err != nil { if err != nil {
return images, err return ImageListResult{}, err
} }
err = json.NewDecoder(resp.Body).Decode(&images) err = json.NewDecoder(resp.Body).Decode(&images)
return images, err return ImageListResult{Items: images}, err
} }

View File

@@ -1,5 +1,7 @@
package client package client
import "github.com/moby/moby/api/types/image"
// ImageListOptions holds parameters to list images with. // ImageListOptions holds parameters to list images with.
type ImageListOptions struct { type ImageListOptions struct {
// All controls whether all images in the graph are filtered, or just // All controls whether all images in the graph are filtered, or just
@@ -15,3 +17,8 @@ type ImageListOptions struct {
// Manifests indicates whether the image manifests should be returned. // Manifests indicates whether the image manifests should be returned.
Manifests bool Manifests bool
} }
// ImageListResult holds the result from ImageList.
type ImageListResult struct {
Items []image.Summary
}