Browse code

client/image_remove&search: Wrap options and result

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

Paweł Gronowski authored on 2025/10/21 02:27:20
Showing 14 changed files
... ...
@@ -8,7 +8,6 @@ import (
8 8
 	"github.com/moby/moby/api/types"
9 9
 	"github.com/moby/moby/api/types/container"
10 10
 	"github.com/moby/moby/api/types/events"
11
-	"github.com/moby/moby/api/types/image"
12 11
 	"github.com/moby/moby/api/types/network"
13 12
 	"github.com/moby/moby/api/types/plugin"
14 13
 	"github.com/moby/moby/api/types/registry"
... ...
@@ -114,8 +113,8 @@ type ImageAPIClient interface {
114 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
-	ImageRemove(ctx context.Context, image string, options ImageRemoveOptions) ([]image.DeleteResponse, error)
118
-	ImageSearch(ctx context.Context, term string, options ImageSearchOptions) ([]registry.SearchResult, error)
117
+	ImageRemove(ctx context.Context, image string, options ImageRemoveOptions) (ImageRemoveResult, error)
118
+	ImageSearch(ctx context.Context, term string, options ImageSearchOptions) (ImageSearchResult, error)
119 119
 	ImageTag(ctx context.Context, image, ref string) error
120 120
 	ImagesPrune(ctx context.Context, opts ImagePruneOptions) (ImagePruneResult, error)
121 121
 
... ...
@@ -9,7 +9,7 @@ import (
9 9
 )
10 10
 
11 11
 // ImageRemove removes an image from the docker host.
12
-func (cli *Client) ImageRemove(ctx context.Context, imageID string, options ImageRemoveOptions) ([]image.DeleteResponse, error) {
12
+func (cli *Client) ImageRemove(ctx context.Context, imageID string, options ImageRemoveOptions) (ImageRemoveResult, error) {
13 13
 	query := url.Values{}
14 14
 
15 15
 	if options.Force {
... ...
@@ -22,7 +22,7 @@ func (cli *Client) ImageRemove(ctx context.Context, imageID string, options Imag
22 22
 	if len(options.Platforms) > 0 {
23 23
 		p, err := encodePlatforms(options.Platforms...)
24 24
 		if err != nil {
25
-			return nil, err
25
+			return ImageRemoveResult{}, err
26 26
 		}
27 27
 		query["platforms"] = p
28 28
 	}
... ...
@@ -30,10 +30,10 @@ func (cli *Client) ImageRemove(ctx context.Context, imageID string, options Imag
30 30
 	resp, err := cli.delete(ctx, "/images/"+imageID, query, nil)
31 31
 	defer ensureReaderClosed(resp)
32 32
 	if err != nil {
33
-		return nil, err
33
+		return ImageRemoveResult{}, err
34 34
 	}
35 35
 
36 36
 	var dels []image.DeleteResponse
37 37
 	err = json.NewDecoder(resp.Body).Decode(&dels)
38
-	return dels, err
38
+	return ImageRemoveResult{Deleted: dels}, err
39 39
 }
... ...
@@ -1,6 +1,9 @@
1 1
 package client
2 2
 
3
-import ocispec "github.com/opencontainers/image-spec/specs-go/v1"
3
+import (
4
+	"github.com/moby/moby/api/types/image"
5
+	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
6
+)
4 7
 
5 8
 // ImageRemoveOptions holds parameters to remove images.
6 9
 type ImageRemoveOptions struct {
... ...
@@ -8,3 +11,8 @@ type ImageRemoveOptions struct {
8 8
 	Force         bool
9 9
 	PruneChildren bool
10 10
 }
11
+
12
+// ImageRemoveResult holds the delete responses returned by the daemon.
13
+type ImageRemoveResult struct {
14
+	Deleted []image.DeleteResponse
15
+}
... ...
@@ -108,6 +108,6 @@ func TestImageRemove(t *testing.T) {
108 108
 
109 109
 		imageDeletes, err := client.ImageRemove(context.Background(), "image_id", opts)
110 110
 		assert.NilError(t, err)
111
-		assert.Check(t, is.Len(imageDeletes, 2))
111
+		assert.Check(t, is.Len(imageDeletes.Deleted, 2))
112 112
 	}
113 113
 }
... ...
@@ -13,7 +13,7 @@ import (
13 13
 
14 14
 // ImageSearch makes the docker host search by a term in a remote registry.
15 15
 // The list of results is not sorted in any fashion.
16
-func (cli *Client) ImageSearch(ctx context.Context, term string, options ImageSearchOptions) ([]registry.SearchResult, error) {
16
+func (cli *Client) ImageSearch(ctx context.Context, term string, options ImageSearchOptions) (ImageSearchResult, error) {
17 17
 	var results []registry.SearchResult
18 18
 	query := url.Values{}
19 19
 	query.Set("term", term)
... ...
@@ -28,16 +28,16 @@ func (cli *Client) ImageSearch(ctx context.Context, term string, options ImageSe
28 28
 	if cerrdefs.IsUnauthorized(err) && options.PrivilegeFunc != nil {
29 29
 		newAuthHeader, privilegeErr := options.PrivilegeFunc(ctx)
30 30
 		if privilegeErr != nil {
31
-			return results, privilegeErr
31
+			return ImageSearchResult{}, privilegeErr
32 32
 		}
33 33
 		resp, err = cli.tryImageSearch(ctx, query, newAuthHeader)
34 34
 	}
35 35
 	if err != nil {
36
-		return results, err
36
+		return ImageSearchResult{}, err
37 37
 	}
38 38
 
39 39
 	err = json.NewDecoder(resp.Body).Decode(&results)
40
-	return results, err
40
+	return ImageSearchResult{Items: results}, err
41 41
 }
42 42
 
43 43
 func (cli *Client) tryImageSearch(ctx context.Context, query url.Values, registryAuth string) (*http.Response, error) {
... ...
@@ -2,8 +2,15 @@ package client
2 2
 
3 3
 import (
4 4
 	"context"
5
+
6
+	"github.com/moby/moby/api/types/registry"
5 7
 )
6 8
 
9
+// ImageSearchResult wraps results returned by ImageSearch.
10
+type ImageSearchResult struct {
11
+	Items []registry.SearchResult
12
+}
13
+
7 14
 // ImageSearchOptions holds parameters to search images with.
8 15
 type ImageSearchOptions struct {
9 16
 	RegistryAuth string
... ...
@@ -97,7 +97,7 @@ func TestImageSearchWithPrivilegedFuncNoError(t *testing.T) {
97 97
 		PrivilegeFunc: privilegeFunc,
98 98
 	})
99 99
 	assert.NilError(t, err)
100
-	assert.Check(t, is.Len(results, 1))
100
+	assert.Check(t, is.Len(results.Items, 1))
101 101
 }
102 102
 
103 103
 func TestImageSearchWithoutErrors(t *testing.T) {
... ...
@@ -135,5 +135,5 @@ func TestImageSearchWithoutErrors(t *testing.T) {
135 135
 		Filters: make(Filters).Add("is-automated", "true").Add("stars", "3"),
136 136
 	})
137 137
 	assert.NilError(t, err)
138
-	assert.Check(t, is.Len(results, 1))
138
+	assert.Check(t, is.Len(results.Items, 1))
139 139
 }
... ...
@@ -800,9 +800,9 @@ func TestBuildHistoryDoesNotPreventRemoval(t *testing.T) {
800 800
 	err := buildImage("history-a")
801 801
 	assert.NilError(t, err)
802 802
 
803
-	resp, err := apiClient.ImageRemove(ctx, "history-a", client.ImageRemoveOptions{})
803
+	res, err := apiClient.ImageRemove(ctx, "history-a", client.ImageRemoveOptions{})
804 804
 	assert.NilError(t, err)
805
-	assert.Check(t, slices.ContainsFunc(resp, func(r image.DeleteResponse) bool {
805
+	assert.Check(t, slices.ContainsFunc(res.Deleted, func(r image.DeleteResponse) bool {
806 806
 		return r.Deleted != ""
807 807
 	}))
808 808
 }
... ...
@@ -142,25 +142,25 @@ func TestRemoveWithPlatform(t *testing.T) {
142 142
 		{platform: &platformHost, deleted: descs[0]},
143 143
 		{platform: &someOtherPlatform, deleted: descs[3]},
144 144
 	} {
145
-		resp, err := apiClient.ImageRemove(ctx, imgName, client.ImageRemoveOptions{
145
+		res, err := apiClient.ImageRemove(ctx, imgName, client.ImageRemoveOptions{
146 146
 			Platforms: []ocispec.Platform{*tc.platform},
147 147
 			Force:     true,
148 148
 		})
149 149
 		assert.NilError(t, err)
150
-		assert.Check(t, is.Len(resp, 1))
151
-		for _, r := range resp {
150
+		assert.Check(t, is.Len(res.Deleted, 1))
151
+		for _, r := range res.Deleted {
152 152
 			assert.Check(t, is.Equal(r.Untagged, ""), "No image should be untagged")
153 153
 		}
154
-		checkPlatformDeleted(t, imageIdx, resp, tc.deleted)
154
+		checkPlatformDeleted(t, imageIdx, res.Deleted, tc.deleted)
155 155
 	}
156 156
 
157 157
 	// Delete the rest
158 158
 	resp, err := apiClient.ImageRemove(ctx, imgName, client.ImageRemoveOptions{})
159 159
 	assert.NilError(t, err)
160 160
 
161
-	assert.Check(t, is.Len(resp, 2))
162
-	assert.Check(t, is.Equal(resp[0].Untagged, imgName))
163
-	assert.Check(t, is.Equal(resp[1].Deleted, imageIdx.Manifests[0].Digest.String()))
161
+	assert.Check(t, is.Len(resp.Deleted, 2))
162
+	assert.Check(t, is.Equal(resp.Deleted[0].Untagged, imgName))
163
+	assert.Check(t, is.Equal(resp.Deleted[1].Deleted, imageIdx.Manifests[0].Digest.String()))
164 164
 	// TODO(vvoland): Should it also include platform-specific manifests? https://github.com/moby/moby/pull/49982
165 165
 }
166 166
 
... ...
@@ -8,7 +8,6 @@ import (
8 8
 	"github.com/moby/moby/api/types"
9 9
 	"github.com/moby/moby/api/types/container"
10 10
 	"github.com/moby/moby/api/types/events"
11
-	"github.com/moby/moby/api/types/image"
12 11
 	"github.com/moby/moby/api/types/network"
13 12
 	"github.com/moby/moby/api/types/plugin"
14 13
 	"github.com/moby/moby/api/types/registry"
... ...
@@ -114,8 +113,8 @@ type ImageAPIClient interface {
114 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
-	ImageRemove(ctx context.Context, image string, options ImageRemoveOptions) ([]image.DeleteResponse, error)
118
-	ImageSearch(ctx context.Context, term string, options ImageSearchOptions) ([]registry.SearchResult, error)
117
+	ImageRemove(ctx context.Context, image string, options ImageRemoveOptions) (ImageRemoveResult, error)
118
+	ImageSearch(ctx context.Context, term string, options ImageSearchOptions) (ImageSearchResult, error)
119 119
 	ImageTag(ctx context.Context, image, ref string) error
120 120
 	ImagesPrune(ctx context.Context, opts ImagePruneOptions) (ImagePruneResult, error)
121 121
 
... ...
@@ -9,7 +9,7 @@ import (
9 9
 )
10 10
 
11 11
 // ImageRemove removes an image from the docker host.
12
-func (cli *Client) ImageRemove(ctx context.Context, imageID string, options ImageRemoveOptions) ([]image.DeleteResponse, error) {
12
+func (cli *Client) ImageRemove(ctx context.Context, imageID string, options ImageRemoveOptions) (ImageRemoveResult, error) {
13 13
 	query := url.Values{}
14 14
 
15 15
 	if options.Force {
... ...
@@ -22,7 +22,7 @@ func (cli *Client) ImageRemove(ctx context.Context, imageID string, options Imag
22 22
 	if len(options.Platforms) > 0 {
23 23
 		p, err := encodePlatforms(options.Platforms...)
24 24
 		if err != nil {
25
-			return nil, err
25
+			return ImageRemoveResult{}, err
26 26
 		}
27 27
 		query["platforms"] = p
28 28
 	}
... ...
@@ -30,10 +30,10 @@ func (cli *Client) ImageRemove(ctx context.Context, imageID string, options Imag
30 30
 	resp, err := cli.delete(ctx, "/images/"+imageID, query, nil)
31 31
 	defer ensureReaderClosed(resp)
32 32
 	if err != nil {
33
-		return nil, err
33
+		return ImageRemoveResult{}, err
34 34
 	}
35 35
 
36 36
 	var dels []image.DeleteResponse
37 37
 	err = json.NewDecoder(resp.Body).Decode(&dels)
38
-	return dels, err
38
+	return ImageRemoveResult{Deleted: dels}, err
39 39
 }
... ...
@@ -1,6 +1,9 @@
1 1
 package client
2 2
 
3
-import ocispec "github.com/opencontainers/image-spec/specs-go/v1"
3
+import (
4
+	"github.com/moby/moby/api/types/image"
5
+	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
6
+)
4 7
 
5 8
 // ImageRemoveOptions holds parameters to remove images.
6 9
 type ImageRemoveOptions struct {
... ...
@@ -8,3 +11,8 @@ type ImageRemoveOptions struct {
8 8
 	Force         bool
9 9
 	PruneChildren bool
10 10
 }
11
+
12
+// ImageRemoveResult holds the delete responses returned by the daemon.
13
+type ImageRemoveResult struct {
14
+	Deleted []image.DeleteResponse
15
+}
... ...
@@ -13,7 +13,7 @@ import (
13 13
 
14 14
 // ImageSearch makes the docker host search by a term in a remote registry.
15 15
 // The list of results is not sorted in any fashion.
16
-func (cli *Client) ImageSearch(ctx context.Context, term string, options ImageSearchOptions) ([]registry.SearchResult, error) {
16
+func (cli *Client) ImageSearch(ctx context.Context, term string, options ImageSearchOptions) (ImageSearchResult, error) {
17 17
 	var results []registry.SearchResult
18 18
 	query := url.Values{}
19 19
 	query.Set("term", term)
... ...
@@ -28,16 +28,16 @@ func (cli *Client) ImageSearch(ctx context.Context, term string, options ImageSe
28 28
 	if cerrdefs.IsUnauthorized(err) && options.PrivilegeFunc != nil {
29 29
 		newAuthHeader, privilegeErr := options.PrivilegeFunc(ctx)
30 30
 		if privilegeErr != nil {
31
-			return results, privilegeErr
31
+			return ImageSearchResult{}, privilegeErr
32 32
 		}
33 33
 		resp, err = cli.tryImageSearch(ctx, query, newAuthHeader)
34 34
 	}
35 35
 	if err != nil {
36
-		return results, err
36
+		return ImageSearchResult{}, err
37 37
 	}
38 38
 
39 39
 	err = json.NewDecoder(resp.Body).Decode(&results)
40
-	return results, err
40
+	return ImageSearchResult{Items: results}, err
41 41
 }
42 42
 
43 43
 func (cli *Client) tryImageSearch(ctx context.Context, query url.Values, registryAuth string) (*http.Response, error) {
... ...
@@ -2,8 +2,15 @@ package client
2 2
 
3 3
 import (
4 4
 	"context"
5
+
6
+	"github.com/moby/moby/api/types/registry"
5 7
 )
6 8
 
9
+// ImageSearchResult wraps results returned by ImageSearch.
10
+type ImageSearchResult struct {
11
+	Items []registry.SearchResult
12
+}
13
+
7 14
 // ImageSearchOptions holds parameters to search images with.
8 15
 type ImageSearchOptions struct {
9 16
 	RegistryAuth string