client/image_remove&search: Wrap options and result

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
This commit is contained in:
Paweł Gronowski
2025-10-20 19:27:20 +02:00
committed by Austin Vazquez
parent b3974f07f5
commit 347693a580
14 changed files with 64 additions and 36 deletions

View File

@@ -8,7 +8,6 @@ import (
"github.com/moby/moby/api/types" "github.com/moby/moby/api/types"
"github.com/moby/moby/api/types/container" "github.com/moby/moby/api/types/container"
"github.com/moby/moby/api/types/events" "github.com/moby/moby/api/types/events"
"github.com/moby/moby/api/types/image"
"github.com/moby/moby/api/types/network" "github.com/moby/moby/api/types/network"
"github.com/moby/moby/api/types/plugin" "github.com/moby/moby/api/types/plugin"
"github.com/moby/moby/api/types/registry" "github.com/moby/moby/api/types/registry"
@@ -114,8 +113,8 @@ type ImageAPIClient interface {
ImageList(ctx context.Context, options ImageListOptions) (ImageListResult, 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) (ImageRemoveResult, error)
ImageSearch(ctx context.Context, term string, options ImageSearchOptions) ([]registry.SearchResult, error) ImageSearch(ctx context.Context, term string, options ImageSearchOptions) (ImageSearchResult, error)
ImageTag(ctx context.Context, image, ref string) error ImageTag(ctx context.Context, image, ref string) error
ImagesPrune(ctx context.Context, opts ImagePruneOptions) (ImagePruneResult, error) ImagesPrune(ctx context.Context, opts ImagePruneOptions) (ImagePruneResult, error)

View File

@@ -9,7 +9,7 @@ import (
) )
// ImageRemove removes an image from the docker host. // ImageRemove removes an image from the docker host.
func (cli *Client) ImageRemove(ctx context.Context, imageID string, options ImageRemoveOptions) ([]image.DeleteResponse, error) { func (cli *Client) ImageRemove(ctx context.Context, imageID string, options ImageRemoveOptions) (ImageRemoveResult, error) {
query := url.Values{} query := url.Values{}
if options.Force { if options.Force {
@@ -22,7 +22,7 @@ func (cli *Client) ImageRemove(ctx context.Context, imageID string, options Imag
if len(options.Platforms) > 0 { if len(options.Platforms) > 0 {
p, err := encodePlatforms(options.Platforms...) p, err := encodePlatforms(options.Platforms...)
if err != nil { if err != nil {
return nil, err return ImageRemoveResult{}, err
} }
query["platforms"] = p query["platforms"] = p
} }
@@ -30,10 +30,10 @@ func (cli *Client) ImageRemove(ctx context.Context, imageID string, options Imag
resp, err := cli.delete(ctx, "/images/"+imageID, query, nil) resp, err := cli.delete(ctx, "/images/"+imageID, query, nil)
defer ensureReaderClosed(resp) defer ensureReaderClosed(resp)
if err != nil { if err != nil {
return nil, err return ImageRemoveResult{}, err
} }
var dels []image.DeleteResponse var dels []image.DeleteResponse
err = json.NewDecoder(resp.Body).Decode(&dels) err = json.NewDecoder(resp.Body).Decode(&dels)
return dels, err return ImageRemoveResult{Deleted: dels}, err
} }

View File

@@ -1,6 +1,9 @@
package client package client
import ocispec "github.com/opencontainers/image-spec/specs-go/v1" import (
"github.com/moby/moby/api/types/image"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
)
// ImageRemoveOptions holds parameters to remove images. // ImageRemoveOptions holds parameters to remove images.
type ImageRemoveOptions struct { type ImageRemoveOptions struct {
@@ -8,3 +11,8 @@ type ImageRemoveOptions struct {
Force bool Force bool
PruneChildren bool PruneChildren bool
} }
// ImageRemoveResult holds the delete responses returned by the daemon.
type ImageRemoveResult struct {
Deleted []image.DeleteResponse
}

View File

@@ -108,6 +108,6 @@ func TestImageRemove(t *testing.T) {
imageDeletes, err := client.ImageRemove(context.Background(), "image_id", opts) imageDeletes, err := client.ImageRemove(context.Background(), "image_id", opts)
assert.NilError(t, err) assert.NilError(t, err)
assert.Check(t, is.Len(imageDeletes, 2)) assert.Check(t, is.Len(imageDeletes.Deleted, 2))
} }
} }

View File

@@ -13,7 +13,7 @@ import (
// ImageSearch makes the docker host search by a term in a remote registry. // ImageSearch makes the docker host search by a term in a remote registry.
// The list of results is not sorted in any fashion. // The list of results is not sorted in any fashion.
func (cli *Client) ImageSearch(ctx context.Context, term string, options ImageSearchOptions) ([]registry.SearchResult, error) { func (cli *Client) ImageSearch(ctx context.Context, term string, options ImageSearchOptions) (ImageSearchResult, error) {
var results []registry.SearchResult var results []registry.SearchResult
query := url.Values{} query := url.Values{}
query.Set("term", term) query.Set("term", term)
@@ -28,16 +28,16 @@ func (cli *Client) ImageSearch(ctx context.Context, term string, options ImageSe
if cerrdefs.IsUnauthorized(err) && options.PrivilegeFunc != nil { if cerrdefs.IsUnauthorized(err) && options.PrivilegeFunc != nil {
newAuthHeader, privilegeErr := options.PrivilegeFunc(ctx) newAuthHeader, privilegeErr := options.PrivilegeFunc(ctx)
if privilegeErr != nil { if privilegeErr != nil {
return results, privilegeErr return ImageSearchResult{}, privilegeErr
} }
resp, err = cli.tryImageSearch(ctx, query, newAuthHeader) resp, err = cli.tryImageSearch(ctx, query, newAuthHeader)
} }
if err != nil { if err != nil {
return results, err return ImageSearchResult{}, err
} }
err = json.NewDecoder(resp.Body).Decode(&results) err = json.NewDecoder(resp.Body).Decode(&results)
return results, err return ImageSearchResult{Items: results}, err
} }
func (cli *Client) tryImageSearch(ctx context.Context, query url.Values, registryAuth string) (*http.Response, error) { func (cli *Client) tryImageSearch(ctx context.Context, query url.Values, registryAuth string) (*http.Response, error) {

View File

@@ -2,8 +2,15 @@ package client
import ( import (
"context" "context"
"github.com/moby/moby/api/types/registry"
) )
// ImageSearchResult wraps results returned by ImageSearch.
type ImageSearchResult struct {
Items []registry.SearchResult
}
// ImageSearchOptions holds parameters to search images with. // ImageSearchOptions holds parameters to search images with.
type ImageSearchOptions struct { type ImageSearchOptions struct {
RegistryAuth string RegistryAuth string

View File

@@ -97,7 +97,7 @@ func TestImageSearchWithPrivilegedFuncNoError(t *testing.T) {
PrivilegeFunc: privilegeFunc, PrivilegeFunc: privilegeFunc,
}) })
assert.NilError(t, err) assert.NilError(t, err)
assert.Check(t, is.Len(results, 1)) assert.Check(t, is.Len(results.Items, 1))
} }
func TestImageSearchWithoutErrors(t *testing.T) { func TestImageSearchWithoutErrors(t *testing.T) {
@@ -135,5 +135,5 @@ func TestImageSearchWithoutErrors(t *testing.T) {
Filters: make(Filters).Add("is-automated", "true").Add("stars", "3"), Filters: make(Filters).Add("is-automated", "true").Add("stars", "3"),
}) })
assert.NilError(t, err) assert.NilError(t, err)
assert.Check(t, is.Len(results, 1)) assert.Check(t, is.Len(results.Items, 1))
} }

View File

@@ -800,9 +800,9 @@ func TestBuildHistoryDoesNotPreventRemoval(t *testing.T) {
err := buildImage("history-a") err := buildImage("history-a")
assert.NilError(t, err) assert.NilError(t, err)
resp, err := apiClient.ImageRemove(ctx, "history-a", client.ImageRemoveOptions{}) res, err := apiClient.ImageRemove(ctx, "history-a", client.ImageRemoveOptions{})
assert.NilError(t, err) assert.NilError(t, err)
assert.Check(t, slices.ContainsFunc(resp, func(r image.DeleteResponse) bool { assert.Check(t, slices.ContainsFunc(res.Deleted, func(r image.DeleteResponse) bool {
return r.Deleted != "" return r.Deleted != ""
})) }))
} }

View File

@@ -142,25 +142,25 @@ func TestRemoveWithPlatform(t *testing.T) {
{platform: &platformHost, deleted: descs[0]}, {platform: &platformHost, deleted: descs[0]},
{platform: &someOtherPlatform, deleted: descs[3]}, {platform: &someOtherPlatform, deleted: descs[3]},
} { } {
resp, err := apiClient.ImageRemove(ctx, imgName, client.ImageRemoveOptions{ res, err := apiClient.ImageRemove(ctx, imgName, client.ImageRemoveOptions{
Platforms: []ocispec.Platform{*tc.platform}, Platforms: []ocispec.Platform{*tc.platform},
Force: true, Force: true,
}) })
assert.NilError(t, err) assert.NilError(t, err)
assert.Check(t, is.Len(resp, 1)) assert.Check(t, is.Len(res.Deleted, 1))
for _, r := range resp { for _, r := range res.Deleted {
assert.Check(t, is.Equal(r.Untagged, ""), "No image should be untagged") assert.Check(t, is.Equal(r.Untagged, ""), "No image should be untagged")
} }
checkPlatformDeleted(t, imageIdx, resp, tc.deleted) checkPlatformDeleted(t, imageIdx, res.Deleted, tc.deleted)
} }
// Delete the rest // Delete the rest
resp, err := apiClient.ImageRemove(ctx, imgName, client.ImageRemoveOptions{}) resp, err := apiClient.ImageRemove(ctx, imgName, client.ImageRemoveOptions{})
assert.NilError(t, err) assert.NilError(t, err)
assert.Check(t, is.Len(resp, 2)) assert.Check(t, is.Len(resp.Deleted, 2))
assert.Check(t, is.Equal(resp[0].Untagged, imgName)) assert.Check(t, is.Equal(resp.Deleted[0].Untagged, imgName))
assert.Check(t, is.Equal(resp[1].Deleted, imageIdx.Manifests[0].Digest.String())) assert.Check(t, is.Equal(resp.Deleted[1].Deleted, imageIdx.Manifests[0].Digest.String()))
// TODO(vvoland): Should it also include platform-specific manifests? https://github.com/moby/moby/pull/49982 // TODO(vvoland): Should it also include platform-specific manifests? https://github.com/moby/moby/pull/49982
} }

View File

@@ -8,7 +8,6 @@ import (
"github.com/moby/moby/api/types" "github.com/moby/moby/api/types"
"github.com/moby/moby/api/types/container" "github.com/moby/moby/api/types/container"
"github.com/moby/moby/api/types/events" "github.com/moby/moby/api/types/events"
"github.com/moby/moby/api/types/image"
"github.com/moby/moby/api/types/network" "github.com/moby/moby/api/types/network"
"github.com/moby/moby/api/types/plugin" "github.com/moby/moby/api/types/plugin"
"github.com/moby/moby/api/types/registry" "github.com/moby/moby/api/types/registry"
@@ -114,8 +113,8 @@ type ImageAPIClient interface {
ImageList(ctx context.Context, options ImageListOptions) (ImageListResult, 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) (ImageRemoveResult, error)
ImageSearch(ctx context.Context, term string, options ImageSearchOptions) ([]registry.SearchResult, error) ImageSearch(ctx context.Context, term string, options ImageSearchOptions) (ImageSearchResult, error)
ImageTag(ctx context.Context, image, ref string) error ImageTag(ctx context.Context, image, ref string) error
ImagesPrune(ctx context.Context, opts ImagePruneOptions) (ImagePruneResult, error) ImagesPrune(ctx context.Context, opts ImagePruneOptions) (ImagePruneResult, error)

View File

@@ -9,7 +9,7 @@ import (
) )
// ImageRemove removes an image from the docker host. // ImageRemove removes an image from the docker host.
func (cli *Client) ImageRemove(ctx context.Context, imageID string, options ImageRemoveOptions) ([]image.DeleteResponse, error) { func (cli *Client) ImageRemove(ctx context.Context, imageID string, options ImageRemoveOptions) (ImageRemoveResult, error) {
query := url.Values{} query := url.Values{}
if options.Force { if options.Force {
@@ -22,7 +22,7 @@ func (cli *Client) ImageRemove(ctx context.Context, imageID string, options Imag
if len(options.Platforms) > 0 { if len(options.Platforms) > 0 {
p, err := encodePlatforms(options.Platforms...) p, err := encodePlatforms(options.Platforms...)
if err != nil { if err != nil {
return nil, err return ImageRemoveResult{}, err
} }
query["platforms"] = p query["platforms"] = p
} }
@@ -30,10 +30,10 @@ func (cli *Client) ImageRemove(ctx context.Context, imageID string, options Imag
resp, err := cli.delete(ctx, "/images/"+imageID, query, nil) resp, err := cli.delete(ctx, "/images/"+imageID, query, nil)
defer ensureReaderClosed(resp) defer ensureReaderClosed(resp)
if err != nil { if err != nil {
return nil, err return ImageRemoveResult{}, err
} }
var dels []image.DeleteResponse var dels []image.DeleteResponse
err = json.NewDecoder(resp.Body).Decode(&dels) err = json.NewDecoder(resp.Body).Decode(&dels)
return dels, err return ImageRemoveResult{Deleted: dels}, err
} }

View File

@@ -1,6 +1,9 @@
package client package client
import ocispec "github.com/opencontainers/image-spec/specs-go/v1" import (
"github.com/moby/moby/api/types/image"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
)
// ImageRemoveOptions holds parameters to remove images. // ImageRemoveOptions holds parameters to remove images.
type ImageRemoveOptions struct { type ImageRemoveOptions struct {
@@ -8,3 +11,8 @@ type ImageRemoveOptions struct {
Force bool Force bool
PruneChildren bool PruneChildren bool
} }
// ImageRemoveResult holds the delete responses returned by the daemon.
type ImageRemoveResult struct {
Deleted []image.DeleteResponse
}

View File

@@ -13,7 +13,7 @@ import (
// ImageSearch makes the docker host search by a term in a remote registry. // ImageSearch makes the docker host search by a term in a remote registry.
// The list of results is not sorted in any fashion. // The list of results is not sorted in any fashion.
func (cli *Client) ImageSearch(ctx context.Context, term string, options ImageSearchOptions) ([]registry.SearchResult, error) { func (cli *Client) ImageSearch(ctx context.Context, term string, options ImageSearchOptions) (ImageSearchResult, error) {
var results []registry.SearchResult var results []registry.SearchResult
query := url.Values{} query := url.Values{}
query.Set("term", term) query.Set("term", term)
@@ -28,16 +28,16 @@ func (cli *Client) ImageSearch(ctx context.Context, term string, options ImageSe
if cerrdefs.IsUnauthorized(err) && options.PrivilegeFunc != nil { if cerrdefs.IsUnauthorized(err) && options.PrivilegeFunc != nil {
newAuthHeader, privilegeErr := options.PrivilegeFunc(ctx) newAuthHeader, privilegeErr := options.PrivilegeFunc(ctx)
if privilegeErr != nil { if privilegeErr != nil {
return results, privilegeErr return ImageSearchResult{}, privilegeErr
} }
resp, err = cli.tryImageSearch(ctx, query, newAuthHeader) resp, err = cli.tryImageSearch(ctx, query, newAuthHeader)
} }
if err != nil { if err != nil {
return results, err return ImageSearchResult{}, err
} }
err = json.NewDecoder(resp.Body).Decode(&results) err = json.NewDecoder(resp.Body).Decode(&results)
return results, err return ImageSearchResult{Items: results}, err
} }
func (cli *Client) tryImageSearch(ctx context.Context, query url.Values, registryAuth string) (*http.Response, error) { func (cli *Client) tryImageSearch(ctx context.Context, query url.Values, registryAuth string) (*http.Response, error) {

View File

@@ -2,8 +2,15 @@ package client
import ( import (
"context" "context"
"github.com/moby/moby/api/types/registry"
) )
// ImageSearchResult wraps results returned by ImageSearch.
type ImageSearchResult struct {
Items []registry.SearchResult
}
// ImageSearchOptions holds parameters to search images with. // ImageSearchOptions holds parameters to search images with.
type ImageSearchOptions struct { type ImageSearchOptions struct {
RegistryAuth string RegistryAuth string