client/image_(inspect,history,load,save): Wrap return values by vvoland · Pull Request #51236 · moby/moby · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions client/client_interfaces.go
15 changes: 7 additions & 8 deletions client/image_history.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"fmt"
"net/url"

"github.com/moby/moby/api/types/image"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
)

Expand All @@ -22,35 +21,35 @@ func ImageHistoryWithPlatform(platform ocispec.Platform) ImageHistoryOption {
}

// ImageHistory returns the changes in an image in history format.
func (cli *Client) ImageHistory(ctx context.Context, imageID string, historyOpts ...ImageHistoryOption) ([]image.HistoryResponseItem, error) {
func (cli *Client) ImageHistory(ctx context.Context, imageID string, historyOpts ...ImageHistoryOption) (ImageHistoryResult, error) {
query := url.Values{}

var opts imageHistoryOpts
for _, o := range historyOpts {
if err := o.Apply(&opts); err != nil {
return nil, err
return ImageHistoryResult{}, err
}
}

if opts.apiOptions.Platform != nil {
if err := cli.NewVersionError(ctx, "1.48", "platform"); err != nil {
return nil, err
return ImageHistoryResult{}, err
}

p, err := encodePlatform(opts.apiOptions.Platform)
if err != nil {
return nil, err
return ImageHistoryResult{}, err
}
query.Set("platform", p)
}

resp, err := cli.get(ctx, "/images/"+imageID+"/history", query, nil)
defer ensureReaderClosed(resp)
if err != nil {
return nil, err
return ImageHistoryResult{}, err
}

var history []image.HistoryResponseItem
err = json.NewDecoder(resp.Body).Decode(&history)
var history ImageHistoryResult
err = json.NewDecoder(resp.Body).Decode(&history.Items)
return history, err
}
9 changes: 8 additions & 1 deletion client/image_history_opts.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
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"
)

// ImageHistoryOption is a type representing functional options for the image history operation.
type ImageHistoryOption interface {
Expand All @@ -20,3 +23,7 @@ type imageHistoryOptions struct {
// Platform from the manifest list to use for history.
Platform *ocispec.Platform
}

type ImageHistoryResult struct {
Items []image.HistoryResponseItem
}
21 changes: 11 additions & 10 deletions client/image_history_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,16 +36,17 @@ func TestImageHistory(t *testing.T) {
}, nil
}))
assert.NilError(t, err)
expected := []image.HistoryResponseItem{
{
ID: "image_id1",
Tags: []string{"tag1", "tag2"},
},
{
ID: "image_id2",
Tags: []string{"tag1", "tag2"},
},
}
expected := ImageHistoryResult{
Items: []image.HistoryResponseItem{
{
ID: "image_id1",
Tags: []string{"tag1", "tag2"},
},
{
ID: "image_id2",
Tags: []string{"tag1", "tag2"},
},
}}

imageHistories, err := client.ImageHistory(context.Background(), "image_id", ImageHistoryWithPlatform(ocispec.Platform{
Architecture: "arm64",
Expand Down
20 changes: 9 additions & 11 deletions client/image_inspect.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,46 +7,44 @@ import (
"fmt"
"io"
"net/url"

"github.com/moby/moby/api/types/image"
)

// ImageInspect returns the image information.
func (cli *Client) ImageInspect(ctx context.Context, imageID string, inspectOpts ...ImageInspectOption) (image.InspectResponse, error) {
func (cli *Client) ImageInspect(ctx context.Context, imageID string, inspectOpts ...ImageInspectOption) (ImageInspectResult, error) {
if imageID == "" {
return image.InspectResponse{}, objectNotFoundError{object: "image", id: imageID}
return ImageInspectResult{}, objectNotFoundError{object: "image", id: imageID}
}

var opts imageInspectOpts
for _, opt := range inspectOpts {
if err := opt.Apply(&opts); err != nil {
return image.InspectResponse{}, fmt.Errorf("error applying image inspect option: %w", err)
return ImageInspectResult{}, fmt.Errorf("error applying image inspect option: %w", err)
}
}

query := url.Values{}
if opts.apiOptions.Manifests {
if err := cli.NewVersionError(ctx, "1.48", "manifests"); err != nil {
return image.InspectResponse{}, err
return ImageInspectResult{}, err
}
query.Set("manifests", "1")
}

if opts.apiOptions.Platform != nil {
if err := cli.NewVersionError(ctx, "1.49", "platform"); err != nil {
return image.InspectResponse{}, err
return ImageInspectResult{}, err
}
platform, err := encodePlatform(opts.apiOptions.Platform)
if err != nil {
return image.InspectResponse{}, err
return ImageInspectResult{}, err
}
query.Set("platform", platform)
}

resp, err := cli.get(ctx, "/images/"+imageID+"/json", query, nil)
defer ensureReaderClosed(resp)
if err != nil {
return image.InspectResponse{}, err
return ImageInspectResult{}, err
}

buf := opts.raw
Expand All @@ -55,10 +53,10 @@ func (cli *Client) ImageInspect(ctx context.Context, imageID string, inspectOpts
}

if _, err := io.Copy(buf, resp.Body); err != nil {
return image.InspectResponse{}, err
return ImageInspectResult{}, err
}

var response image.InspectResponse
var response ImageInspectResult
err = json.Unmarshal(buf.Bytes(), &response)
return response, err
}
5 changes: 5 additions & 0 deletions client/image_inspect_opts.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package client
import (
"bytes"

"github.com/moby/moby/api/types/image"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
)

Expand Down Expand Up @@ -62,3 +63,7 @@ type imageInspectOptions struct {
// This option is only available for API version 1.49 and up.
Platform *ocispec.Platform
}

type ImageInspectResult struct {
image.InspectResponse
}
31 changes: 21 additions & 10 deletions client/image_load.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,16 @@ import (

// ImageLoad loads an image in the docker host from the client host.
// It's up to the caller to close the [io.ReadCloser] in the
// [image.LoadResponse] returned by this function.
// [ImageLoadResult] returned by this function.
//
// Platform is an optional parameter that specifies the platform to load from
// the provided multi-platform image. Passing a platform only has an effect
// if the input image is a multi-platform image.
func (cli *Client) ImageLoad(ctx context.Context, input io.Reader, loadOpts ...ImageLoadOption) (LoadResponse, error) {
func (cli *Client) ImageLoad(ctx context.Context, input io.Reader, loadOpts ...ImageLoadOption) (ImageLoadResult, error) {
var opts imageLoadOpts
for _, opt := range loadOpts {
if err := opt.Apply(&opts); err != nil {
return LoadResponse{}, err
return ImageLoadResult{}, err
}
}

Expand All @@ -29,12 +29,12 @@ func (cli *Client) ImageLoad(ctx context.Context, input io.Reader, loadOpts ...I
}
if len(opts.apiOptions.Platforms) > 0 {
if err := cli.NewVersionError(ctx, "1.48", "platform"); err != nil {
return LoadResponse{}, err
return ImageLoadResult{}, err
}

p, err := encodePlatforms(opts.apiOptions.Platforms...)
if err != nil {
return LoadResponse{}, err
return ImageLoadResult{}, err
}
query["platform"] = p
}
Expand All @@ -43,10 +43,10 @@ func (cli *Client) ImageLoad(ctx context.Context, input io.Reader, loadOpts ...I
"Content-Type": {"application/x-tar"},
})
if err != nil {
return LoadResponse{}, err
return ImageLoadResult{}, err
}
return LoadResponse{
Body: resp.Body,
return ImageLoadResult{
body: resp.Body,
JSON: resp.Header.Get("Content-Type") == "application/json",
}, nil
}
Expand All @@ -73,8 +73,19 @@ func (cli *Client) ImageLoad(ctx context.Context, input io.Reader, loadOpts ...I
//
// We should deprecated the "quiet" option, as it's really a client
// responsibility.
type LoadResponse struct {
type ImageLoadResult struct {
// Body must be closed to avoid a resource leak
Body io.ReadCloser
body io.ReadCloser
JSON bool
}

func (r ImageLoadResult) Read(p []byte) (n int, err error) {
return r.body.Read(p)
}

func (r ImageLoadResult) Close() error {
if r.body == nil {
return nil
}
return r.body.Close()
}
2 changes: 1 addition & 1 deletion client/image_load_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ func TestImageLoad(t *testing.T) {
assert.NilError(t, err)
assert.Check(t, is.Equal(imageLoadResponse.JSON, tc.expectedResponseJSON))

body, err := io.ReadAll(imageLoadResponse.Body)
body, err := io.ReadAll(imageLoadResponse)
assert.NilError(t, err)
assert.Check(t, is.Equal(string(body), expectedOutput))
})
Expand Down
15 changes: 7 additions & 8 deletions client/image_save.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,20 @@ package client

import (
"context"
"io"
"net/url"
)

// ImageSave retrieves one or more images from the docker host as an
// [io.ReadCloser].
// [ImageSaveResult].
//
// Platforms is an optional parameter that specifies the platforms to save
// from the image. Passing a platform only has an effect if the input image
// is a multi-platform image.
func (cli *Client) ImageSave(ctx context.Context, imageIDs []string, saveOpts ...ImageSaveOption) (io.ReadCloser, error) {
func (cli *Client) ImageSave(ctx context.Context, imageIDs []string, saveOpts ...ImageSaveOption) (ImageSaveResult, error) {
var opts imageSaveOpts
for _, opt := range saveOpts {
if err := opt.Apply(&opts); err != nil {
return nil, err
return ImageSaveResult{}, err
}
}

Expand All @@ -26,18 +25,18 @@ func (cli *Client) ImageSave(ctx context.Context, imageIDs []string, saveOpts ..

if len(opts.apiOptions.Platforms) > 0 {
if err := cli.NewVersionError(ctx, "1.48", "platform"); err != nil {
return nil, err
return ImageSaveResult{}, err
}
p, err := encodePlatforms(opts.apiOptions.Platforms...)
if err != nil {
return nil, err
return ImageSaveResult{}, err
}
query["platform"] = p
}

resp, err := cli.get(ctx, "/images/get", query, nil)
if err != nil {
return nil, err
return ImageSaveResult{}, err
}
return resp.Body, nil
return newImageSaveResult(resp.Body), nil
}
33 changes: 33 additions & 0 deletions client/image_save_opts.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package client

import (
"fmt"
"io"
"sync"

ocispec "github.com/opencontainers/image-spec/specs-go/v1"
)
Expand Down Expand Up @@ -36,3 +38,34 @@ type imageSaveOptions struct {
// multi-platform image and has multiple variants.
Platforms []ocispec.Platform
}

func newImageSaveResult(rc io.ReadCloser) ImageSaveResult {
if rc == nil {
panic("nil io.ReadCloser")
}
return ImageSaveResult{
rc: rc,
close: sync.OnceValue(rc.Close),
}
}

type ImageSaveResult struct {
rc io.ReadCloser
close func() error
}

// Read implements io.ReadCloser
func (r ImageSaveResult) Read(p []byte) (n int, err error) {
if r.rc == nil {
return 0, io.EOF
}
return r.rc.Read(p)
}

// Close implements io.ReadCloser
func (r ImageSaveResult) Close() error {
if r.close == nil {
return nil
}
return r.close()
}
2 changes: 1 addition & 1 deletion integration/build/build_squash_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,6 @@ func TestBuildSquashParent(t *testing.T) {

inspect, err = apiClient.ImageInspect(ctx, name)
assert.NilError(t, err)
assert.Check(t, is.Len(testHistory, len(origHistory)+1))
assert.Check(t, is.Len(testHistory.Items, len(origHistory.Items)+1))
assert.Check(t, is.Len(inspect.RootFS.Layers, 2))
}
4 changes: 2 additions & 2 deletions integration/build/build_userns_linux_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,9 +105,9 @@ func TestBuildUserNamespaceValidateCapabilitiesAreV2(t *testing.T) {
tarReader := bufio.NewReader(tarFile)
loadResp, err := clientNoUserRemap.ImageLoad(ctx, tarReader)
assert.NilError(t, err, "failed to load image tar file")
defer loadResp.Body.Close()
defer loadResp.Close()
buf = bytes.NewBuffer(nil)
err = jsonmessage.DisplayJSONMessagesStream(loadResp.Body, buf, 0, false, nil)
err = jsonmessage.DisplayJSONMessagesStream(loadResp, buf, 0, false, nil)
assert.NilError(t, err)

cid := container.Run(ctx, t, clientNoUserRemap,
Expand Down
4 changes: 2 additions & 2 deletions integration/daemon/migration_test.go
Loading
Loading