mirror of
https://github.com/moby/moby.git
synced 2026-01-11 02:31:44 +00:00
client: Client.ImageLoad: close reader on context cancellation
Use a cancelReadCloser to automatically close the reader when the context is cancelled. Consumers are still recommended to manually close the reader, but the cancelReadCloser makes the Close idempotent. Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
@@ -15,6 +15,8 @@ type ImageLoadResult interface {
|
||||
|
||||
// ImageLoad loads an image in the docker host from the client host. It's up
|
||||
// to the caller to close the [ImageLoadResult] returned by this function.
|
||||
//
|
||||
// The underlying [io.ReadCloser] is automatically closed if the context is canceled,
|
||||
func (cli *Client) ImageLoad(ctx context.Context, input io.Reader, loadOpts ...ImageLoadOption) (ImageLoadResult, error) {
|
||||
var opts imageLoadOpts
|
||||
for _, opt := range loadOpts {
|
||||
@@ -47,31 +49,16 @@ func (cli *Client) ImageLoad(ctx context.Context, input io.Reader, loadOpts ...I
|
||||
return nil, err
|
||||
}
|
||||
return &imageLoadResult{
|
||||
body: resp.Body,
|
||||
ReadCloser: newCancelReadCloser(ctx, resp.Body),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// imageLoadResult returns information to the client about a load process.
|
||||
type imageLoadResult struct {
|
||||
// body must be closed to avoid a resource leak
|
||||
body io.ReadCloser
|
||||
io.ReadCloser
|
||||
}
|
||||
|
||||
var (
|
||||
_ io.ReadCloser = (*imageLoadResult)(nil)
|
||||
_ ImageLoadResult = (*imageLoadResult)(nil)
|
||||
)
|
||||
|
||||
func (r *imageLoadResult) Read(p []byte) (int, error) {
|
||||
if r == nil || r.body == nil {
|
||||
return 0, io.EOF
|
||||
}
|
||||
return r.body.Read(p)
|
||||
}
|
||||
|
||||
func (r *imageLoadResult) Close() error {
|
||||
if r == nil || r.body == nil {
|
||||
return nil
|
||||
}
|
||||
return r.body.Close()
|
||||
}
|
||||
|
||||
@@ -106,8 +106,8 @@ func TestBuildUserNamespaceValidateCapabilitiesAreV2(t *testing.T) {
|
||||
loadResp, err := clientNoUserRemap.ImageLoad(ctx, tarReader)
|
||||
assert.NilError(t, err, "failed to load image tar file")
|
||||
defer loadResp.Close()
|
||||
buf = bytes.NewBuffer(nil)
|
||||
err = jsonmessage.DisplayJSONMessagesStream(loadResp, buf, 0, false, nil)
|
||||
var buf2 bytes.Buffer
|
||||
err = jsonmessage.DisplayJSONMessagesStream(loadResp, &buf2, 0, false, nil)
|
||||
assert.NilError(t, err)
|
||||
|
||||
cid := container.Run(ctx, t, clientNoUserRemap,
|
||||
|
||||
@@ -146,10 +146,10 @@ func TestMigrateSaveLoad(t *testing.T) {
|
||||
assert.Equal(t, info.Images, 0)
|
||||
|
||||
// Import
|
||||
lr, err := apiClient.ImageLoad(ctx, bytes.NewReader(buf.Bytes()), client.ImageLoadWithQuiet(true))
|
||||
resp, err := apiClient.ImageLoad(ctx, bytes.NewReader(buf.Bytes()), client.ImageLoadWithQuiet(true))
|
||||
assert.NilError(t, err)
|
||||
io.Copy(io.Discard, lr)
|
||||
lr.Close()
|
||||
_, _ = io.Copy(io.Discard, resp)
|
||||
_ = resp.Close()
|
||||
|
||||
result := container.RunAttach(ctx, t, apiClient, func(c *container.TestContainerConfig) {
|
||||
c.Name = "Migration-save-load-" + snapshotter
|
||||
|
||||
@@ -343,11 +343,9 @@ 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)
|
||||
resp.Close()
|
||||
assert.NilError(t, err)
|
||||
|
||||
rdr.Close()
|
||||
_, _ = io.Copy(io.Discard, resp)
|
||||
_ = resp.Close()
|
||||
_ = rdr.Close()
|
||||
|
||||
// verify the loaded image has all the expected platforms
|
||||
for _, p := range tc.expectedSavedPlatforms {
|
||||
@@ -381,11 +379,9 @@ 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)
|
||||
resp.Close()
|
||||
assert.NilError(t, err)
|
||||
|
||||
rdr.Close()
|
||||
_, _ = io.Copy(io.Discard, resp)
|
||||
_ = resp.Close()
|
||||
_ = rdr.Close()
|
||||
|
||||
// verify the image was loaded for the specified platforms
|
||||
for _, p := range tc.expectedLoadedPlatforms {
|
||||
|
||||
@@ -30,7 +30,7 @@ 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.Close()
|
||||
defer func() { _ = resp.Close() }()
|
||||
|
||||
if !assert.Check(t, err) {
|
||||
respBody, err := io.ReadAll(resp)
|
||||
|
||||
@@ -445,11 +445,12 @@ func imageLoad(ctx context.Context, apiClient client.APIClient, path string) err
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
response, err := apiClient.ImageLoad(ctx, file, client.ImageLoadWithQuiet(true))
|
||||
resp, err := apiClient.ImageLoad(ctx, file, client.ImageLoadWithQuiet(true))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer response.Close()
|
||||
_, _ = io.Copy(io.Discard, resp)
|
||||
_ = resp.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -886,7 +886,8 @@ 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.Close()
|
||||
_, _ = io.Copy(io.Discard, resp)
|
||||
_ = resp.Close()
|
||||
}
|
||||
|
||||
func (d *Daemon) getClientConfig() (*clientConfig, error) {
|
||||
|
||||
21
vendor/github.com/moby/moby/client/image_load.go
generated
vendored
21
vendor/github.com/moby/moby/client/image_load.go
generated
vendored
@@ -15,6 +15,8 @@ type ImageLoadResult interface {
|
||||
|
||||
// ImageLoad loads an image in the docker host from the client host. It's up
|
||||
// to the caller to close the [ImageLoadResult] returned by this function.
|
||||
//
|
||||
// The underlying [io.ReadCloser] is automatically closed if the context is canceled,
|
||||
func (cli *Client) ImageLoad(ctx context.Context, input io.Reader, loadOpts ...ImageLoadOption) (ImageLoadResult, error) {
|
||||
var opts imageLoadOpts
|
||||
for _, opt := range loadOpts {
|
||||
@@ -47,31 +49,16 @@ func (cli *Client) ImageLoad(ctx context.Context, input io.Reader, loadOpts ...I
|
||||
return nil, err
|
||||
}
|
||||
return &imageLoadResult{
|
||||
body: resp.Body,
|
||||
ReadCloser: newCancelReadCloser(ctx, resp.Body),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// imageLoadResult returns information to the client about a load process.
|
||||
type imageLoadResult struct {
|
||||
// body must be closed to avoid a resource leak
|
||||
body io.ReadCloser
|
||||
io.ReadCloser
|
||||
}
|
||||
|
||||
var (
|
||||
_ io.ReadCloser = (*imageLoadResult)(nil)
|
||||
_ ImageLoadResult = (*imageLoadResult)(nil)
|
||||
)
|
||||
|
||||
func (r *imageLoadResult) Read(p []byte) (int, error) {
|
||||
if r == nil || r.body == nil {
|
||||
return 0, io.EOF
|
||||
}
|
||||
return r.body.Read(p)
|
||||
}
|
||||
|
||||
func (r *imageLoadResult) Close() error {
|
||||
if r == nil || r.body == nil {
|
||||
return nil
|
||||
}
|
||||
return r.body.Close()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user