mirror of
https://github.com/moby/moby.git
synced 2026-01-11 10:41:43 +00:00
client/image_(inspect,history,load,save): Wrap return values
Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com> Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
committed by
Sebastiaan van Stijn
parent
7066eb3736
commit
2d69edd28a
@@ -119,10 +119,10 @@ type ImageAPIClient interface {
|
||||
ImageTag(ctx context.Context, image, ref string) error
|
||||
ImagesPrune(ctx context.Context, opts ImagePruneOptions) (ImagePruneResult, error)
|
||||
|
||||
ImageInspect(ctx context.Context, image string, _ ...ImageInspectOption) (image.InspectResponse, error)
|
||||
ImageHistory(ctx context.Context, image string, _ ...ImageHistoryOption) ([]image.HistoryResponseItem, error)
|
||||
ImageLoad(ctx context.Context, input io.Reader, _ ...ImageLoadOption) (LoadResponse, error)
|
||||
ImageSave(ctx context.Context, images []string, _ ...ImageSaveOption) (io.ReadCloser, error)
|
||||
ImageInspect(ctx context.Context, image string, _ ...ImageInspectOption) (ImageInspectResult, error)
|
||||
ImageHistory(ctx context.Context, image string, _ ...ImageHistoryOption) (ImageHistoryResult, error)
|
||||
ImageLoad(ctx context.Context, input io.Reader, _ ...ImageLoadOption) (ImageLoadResult, error)
|
||||
ImageSave(ctx context.Context, images []string, _ ...ImageSaveOption) (ImageSaveResult, error)
|
||||
}
|
||||
|
||||
// NetworkAPIClient defines API client methods for the networks
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
|
||||
"github.com/moby/moby/api/types/image"
|
||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
)
|
||||
|
||||
@@ -22,24 +21,24 @@ 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)
|
||||
}
|
||||
@@ -47,10 +46,10 @@ func (cli *Client) ImageHistory(ctx context.Context, imageID string, historyOpts
|
||||
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
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -7,38 +7,36 @@ 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)
|
||||
}
|
||||
@@ -46,7 +44,7 @@ func (cli *Client) ImageInspect(ctx context.Context, imageID string, inspectOpts
|
||||
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
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package client
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
"github.com/moby/moby/api/types/image"
|
||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
})
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@ package client
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"sync"
|
||||
|
||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
)
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -148,8 +148,8 @@ func TestMigrateSaveLoad(t *testing.T) {
|
||||
// Import
|
||||
lr, err := apiClient.ImageLoad(ctx, bytes.NewReader(buf.Bytes()), client.ImageLoadWithQuiet(true))
|
||||
assert.NilError(t, err)
|
||||
io.Copy(io.Discard, lr.Body)
|
||||
lr.Body.Close()
|
||||
io.Copy(io.Discard, lr)
|
||||
lr.Close()
|
||||
|
||||
result := container.RunAttach(ctx, t, apiClient, func(c *container.TestContainerConfig) {
|
||||
c.Name = "Migration-save-load-" + snapshotter
|
||||
|
||||
@@ -25,13 +25,13 @@ func TestAPIImagesHistory(t *testing.T) {
|
||||
|
||||
imgID := build.Do(ctx, t, client, fakecontext.New(t, t.TempDir(), fakecontext.WithDockerfile(dockerfile)))
|
||||
|
||||
historydata, err := client.ImageHistory(ctx, imgID)
|
||||
res, err := client.ImageHistory(ctx, imgID)
|
||||
assert.NilError(t, err)
|
||||
|
||||
assert.Assert(t, len(historydata) != 0)
|
||||
assert.Assert(t, len(res.Items) != 0)
|
||||
|
||||
var found bool
|
||||
for _, imageLayer := range historydata {
|
||||
for _, imageLayer := range res.Items {
|
||||
if imageLayer.ID == imgID {
|
||||
found = true
|
||||
break
|
||||
@@ -107,20 +107,20 @@ func TestAPIImageHistoryCrossPlatform(t *testing.T) {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
ctx := testutil.StartSpan(ctx, t)
|
||||
|
||||
hist, err := apiClient.ImageHistory(ctx, tc.imageRef, tc.options...)
|
||||
res, err := apiClient.ImageHistory(ctx, tc.imageRef, tc.options...)
|
||||
|
||||
assert.NilError(t, err)
|
||||
found := false
|
||||
for _, layer := range hist {
|
||||
for _, layer := range res.Items {
|
||||
if layer.ID == imgID {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
assert.Assert(t, found, "History should contain the built image ID")
|
||||
assert.Assert(t, is.Len(hist, 3))
|
||||
assert.Assert(t, is.Len(res.Items, 3))
|
||||
|
||||
for i, layer := range hist {
|
||||
for i, layer := range res.Items {
|
||||
assert.Assert(t, layer.Size >= 0, "Layer %d should not have negative size", i)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -145,29 +145,29 @@ func TestPruneDontDeleteUsedImage(t *testing.T) {
|
||||
} {
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
imageID func(t *testing.T, inspect image.InspectResponse) string
|
||||
imageID func(t *testing.T, inspect client.ImageInspectResult) string
|
||||
}{
|
||||
{
|
||||
name: "full id",
|
||||
imageID: func(t *testing.T, inspect image.InspectResponse) string {
|
||||
imageID: func(t *testing.T, inspect client.ImageInspectResult) string {
|
||||
return inspect.ID
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "full id without sha256 prefix",
|
||||
imageID: func(t *testing.T, inspect image.InspectResponse) string {
|
||||
imageID: func(t *testing.T, inspect client.ImageInspectResult) string {
|
||||
return strings.TrimPrefix(inspect.ID, "sha256:")
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "truncated id (without sha256 prefix)",
|
||||
imageID: func(t *testing.T, inspect image.InspectResponse) string {
|
||||
imageID: func(t *testing.T, inspect client.ImageInspectResult) string {
|
||||
return strings.TrimPrefix(inspect.ID, "sha256:")[:8]
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "repo and digest without tag",
|
||||
imageID: func(t *testing.T, inspect image.InspectResponse) string {
|
||||
imageID: func(t *testing.T, inspect client.ImageInspectResult) string {
|
||||
skip.If(t, !testEnv.UsingSnapshotter())
|
||||
|
||||
return "busybox@" + inspect.ID
|
||||
@@ -175,7 +175,7 @@ func TestPruneDontDeleteUsedImage(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "tagged and digested",
|
||||
imageID: func(t *testing.T, inspect image.InspectResponse) string {
|
||||
imageID: func(t *testing.T, inspect client.ImageInspectResult) string {
|
||||
skip.If(t, !testEnv.UsingSnapshotter())
|
||||
|
||||
return "busybox:latest@" + inspect.ID
|
||||
@@ -183,7 +183,7 @@ func TestPruneDontDeleteUsedImage(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "repo digest",
|
||||
imageID: func(t *testing.T, inspect image.InspectResponse) string {
|
||||
imageID: func(t *testing.T, inspect client.ImageInspectResult) string {
|
||||
// graphdriver won't have a repo digest
|
||||
skip.If(t, len(inspect.RepoDigests) == 0, "no repo digest")
|
||||
|
||||
|
||||
@@ -94,7 +94,7 @@ func TestRemoveByDigest(t *testing.T) {
|
||||
|
||||
inspect, err = apiClient.ImageInspect(ctx, "test-remove-by-digest")
|
||||
assert.Check(t, is.ErrorType(err, cerrdefs.IsNotFound))
|
||||
assert.Check(t, is.DeepEqual(inspect, image.InspectResponse{}))
|
||||
assert.Check(t, is.DeepEqual(inspect, client.ImageInspectResult{}))
|
||||
}
|
||||
|
||||
func TestRemoveWithPlatform(t *testing.T) {
|
||||
|
||||
@@ -328,8 +328,8 @@ func TestSaveAndLoadPlatform(t *testing.T) {
|
||||
// load the full exported image (all platforms in it)
|
||||
resp, err := apiClient.ImageLoad(ctx, rdr)
|
||||
assert.NilError(t, err)
|
||||
_, err = io.ReadAll(resp.Body)
|
||||
resp.Body.Close()
|
||||
_, err = io.ReadAll(resp)
|
||||
resp.Close()
|
||||
assert.NilError(t, err)
|
||||
|
||||
rdr.Close()
|
||||
@@ -366,8 +366,8 @@ func TestSaveAndLoadPlatform(t *testing.T) {
|
||||
// load the exported image on the specified platforms only
|
||||
resp, err = apiClient.ImageLoad(ctx, rdr, client.ImageLoadWithPlatforms(tc.loadPlatforms...))
|
||||
assert.NilError(t, err)
|
||||
_, err = io.ReadAll(resp.Body)
|
||||
resp.Body.Close()
|
||||
_, err = io.ReadAll(resp)
|
||||
resp.Close()
|
||||
assert.NilError(t, err)
|
||||
|
||||
rdr.Close()
|
||||
|
||||
@@ -30,10 +30,10 @@ func Load(ctx context.Context, t *testing.T, apiClient client.APIClient, imageFu
|
||||
resp, err := apiClient.ImageLoad(ctx, rc, client.ImageLoadWithQuiet(true))
|
||||
assert.NilError(t, err, "Failed to load dangling image")
|
||||
|
||||
defer resp.Body.Close()
|
||||
defer resp.Close()
|
||||
|
||||
if !assert.Check(t, err) {
|
||||
respBody, err := io.ReadAll(resp.Body)
|
||||
respBody, err := io.ReadAll(resp)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to read response body: %v", err)
|
||||
return ""
|
||||
@@ -41,7 +41,7 @@ func Load(ctx context.Context, t *testing.T, apiClient client.APIClient, imageFu
|
||||
t.Fatalf("Failed load: %s", string(respBody))
|
||||
}
|
||||
|
||||
all, err := io.ReadAll(resp.Body)
|
||||
all, err := io.ReadAll(resp)
|
||||
assert.NilError(t, err)
|
||||
|
||||
decoder := json.NewDecoder(bytes.NewReader(all))
|
||||
|
||||
@@ -448,7 +448,7 @@ func imageLoad(ctx context.Context, apiClient client.APIClient, path string) err
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer response.Body.Close()
|
||||
defer response.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -886,7 +886,7 @@ func (d *Daemon) LoadImage(ctx context.Context, t testing.TB, img string) {
|
||||
|
||||
resp, err := c.ImageLoad(ctx, reader, client.ImageLoadWithQuiet(true))
|
||||
assert.NilError(t, err, "[%s] failed to load %s", d.id, img)
|
||||
defer resp.Body.Close()
|
||||
defer resp.Close()
|
||||
}
|
||||
|
||||
func (d *Daemon) getClientConfig() (*clientConfig, error) {
|
||||
|
||||
@@ -114,9 +114,9 @@ func loadFrozenImages(ctx context.Context, apiClient client.APIClient) error {
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to load frozen images")
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
defer resp.Close()
|
||||
fd, isTerminal := term.GetFdInfo(os.Stdout)
|
||||
return jsonmessage.DisplayJSONMessagesStream(resp.Body, os.Stdout, fd, isTerminal, nil)
|
||||
return jsonmessage.DisplayJSONMessagesStream(resp, os.Stdout, fd, isTerminal, nil)
|
||||
}
|
||||
|
||||
func pullImages(ctx context.Context, client client.APIClient, images []string) error {
|
||||
|
||||
8
vendor/github.com/moby/moby/client/client_interfaces.go
generated
vendored
8
vendor/github.com/moby/moby/client/client_interfaces.go
generated
vendored
@@ -119,10 +119,10 @@ type ImageAPIClient interface {
|
||||
ImageTag(ctx context.Context, image, ref string) error
|
||||
ImagesPrune(ctx context.Context, opts ImagePruneOptions) (ImagePruneResult, error)
|
||||
|
||||
ImageInspect(ctx context.Context, image string, _ ...ImageInspectOption) (image.InspectResponse, error)
|
||||
ImageHistory(ctx context.Context, image string, _ ...ImageHistoryOption) ([]image.HistoryResponseItem, error)
|
||||
ImageLoad(ctx context.Context, input io.Reader, _ ...ImageLoadOption) (LoadResponse, error)
|
||||
ImageSave(ctx context.Context, images []string, _ ...ImageSaveOption) (io.ReadCloser, error)
|
||||
ImageInspect(ctx context.Context, image string, _ ...ImageInspectOption) (ImageInspectResult, error)
|
||||
ImageHistory(ctx context.Context, image string, _ ...ImageHistoryOption) (ImageHistoryResult, error)
|
||||
ImageLoad(ctx context.Context, input io.Reader, _ ...ImageLoadOption) (ImageLoadResult, error)
|
||||
ImageSave(ctx context.Context, images []string, _ ...ImageSaveOption) (ImageSaveResult, error)
|
||||
}
|
||||
|
||||
// NetworkAPIClient defines API client methods for the networks
|
||||
|
||||
15
vendor/github.com/moby/moby/client/image_history.go
generated
vendored
15
vendor/github.com/moby/moby/client/image_history.go
generated
vendored
@@ -6,7 +6,6 @@ import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
|
||||
"github.com/moby/moby/api/types/image"
|
||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
)
|
||||
|
||||
@@ -22,24 +21,24 @@ 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)
|
||||
}
|
||||
@@ -47,10 +46,10 @@ func (cli *Client) ImageHistory(ctx context.Context, imageID string, historyOpts
|
||||
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
vendor/github.com/moby/moby/client/image_history_opts.go
generated
vendored
9
vendor/github.com/moby/moby/client/image_history_opts.go
generated
vendored
@@ -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 {
|
||||
@@ -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
|
||||
}
|
||||
|
||||
20
vendor/github.com/moby/moby/client/image_inspect.go
generated
vendored
20
vendor/github.com/moby/moby/client/image_inspect.go
generated
vendored
@@ -7,38 +7,36 @@ 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)
|
||||
}
|
||||
@@ -46,7 +44,7 @@ func (cli *Client) ImageInspect(ctx context.Context, imageID string, inspectOpts
|
||||
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
|
||||
@@ -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
vendor/github.com/moby/moby/client/image_inspect_opts.go
generated
vendored
5
vendor/github.com/moby/moby/client/image_inspect_opts.go
generated
vendored
@@ -3,6 +3,7 @@ package client
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
"github.com/moby/moby/api/types/image"
|
||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
)
|
||||
|
||||
@@ -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
vendor/github.com/moby/moby/client/image_load.go
generated
vendored
31
vendor/github.com/moby/moby/client/image_load.go
generated
vendored
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
15
vendor/github.com/moby/moby/client/image_save.go
generated
vendored
15
vendor/github.com/moby/moby/client/image_save.go
generated
vendored
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
vendor/github.com/moby/moby/client/image_save_opts.go
generated
vendored
33
vendor/github.com/moby/moby/client/image_save_opts.go
generated
vendored
@@ -2,6 +2,8 @@ package client
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"sync"
|
||||
|
||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
)
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user