mirror of
https://github.com/moby/moby.git
synced 2026-01-11 18:51:37 +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
|
// 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.
|
// 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) {
|
func (cli *Client) ImageLoad(ctx context.Context, input io.Reader, loadOpts ...ImageLoadOption) (ImageLoadResult, error) {
|
||||||
var opts imageLoadOpts
|
var opts imageLoadOpts
|
||||||
for _, opt := range loadOpts {
|
for _, opt := range loadOpts {
|
||||||
@@ -47,31 +49,16 @@ func (cli *Client) ImageLoad(ctx context.Context, input io.Reader, loadOpts ...I
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &imageLoadResult{
|
return &imageLoadResult{
|
||||||
body: resp.Body,
|
ReadCloser: newCancelReadCloser(ctx, resp.Body),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// imageLoadResult returns information to the client about a load process.
|
// imageLoadResult returns information to the client about a load process.
|
||||||
type imageLoadResult struct {
|
type imageLoadResult struct {
|
||||||
// body must be closed to avoid a resource leak
|
io.ReadCloser
|
||||||
body io.ReadCloser
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
_ io.ReadCloser = (*imageLoadResult)(nil)
|
_ io.ReadCloser = (*imageLoadResult)(nil)
|
||||||
_ ImageLoadResult = (*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)
|
loadResp, err := clientNoUserRemap.ImageLoad(ctx, tarReader)
|
||||||
assert.NilError(t, err, "failed to load image tar file")
|
assert.NilError(t, err, "failed to load image tar file")
|
||||||
defer loadResp.Close()
|
defer loadResp.Close()
|
||||||
buf = bytes.NewBuffer(nil)
|
var buf2 bytes.Buffer
|
||||||
err = jsonmessage.DisplayJSONMessagesStream(loadResp, buf, 0, false, nil)
|
err = jsonmessage.DisplayJSONMessagesStream(loadResp, &buf2, 0, false, nil)
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
|
|
||||||
cid := container.Run(ctx, t, clientNoUserRemap,
|
cid := container.Run(ctx, t, clientNoUserRemap,
|
||||||
|
|||||||
@@ -146,10 +146,10 @@ func TestMigrateSaveLoad(t *testing.T) {
|
|||||||
assert.Equal(t, info.Images, 0)
|
assert.Equal(t, info.Images, 0)
|
||||||
|
|
||||||
// Import
|
// 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)
|
assert.NilError(t, err)
|
||||||
io.Copy(io.Discard, lr)
|
_, _ = io.Copy(io.Discard, resp)
|
||||||
lr.Close()
|
_ = resp.Close()
|
||||||
|
|
||||||
result := container.RunAttach(ctx, t, apiClient, func(c *container.TestContainerConfig) {
|
result := container.RunAttach(ctx, t, apiClient, func(c *container.TestContainerConfig) {
|
||||||
c.Name = "Migration-save-load-" + snapshotter
|
c.Name = "Migration-save-load-" + snapshotter
|
||||||
|
|||||||
@@ -343,11 +343,9 @@ func TestSaveAndLoadPlatform(t *testing.T) {
|
|||||||
// load the full exported image (all platforms in it)
|
// load the full exported image (all platforms in it)
|
||||||
resp, err := apiClient.ImageLoad(ctx, rdr)
|
resp, err := apiClient.ImageLoad(ctx, rdr)
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
_, err = io.ReadAll(resp)
|
_, _ = io.Copy(io.Discard, resp)
|
||||||
resp.Close()
|
_ = resp.Close()
|
||||||
assert.NilError(t, err)
|
_ = rdr.Close()
|
||||||
|
|
||||||
rdr.Close()
|
|
||||||
|
|
||||||
// verify the loaded image has all the expected platforms
|
// verify the loaded image has all the expected platforms
|
||||||
for _, p := range tc.expectedSavedPlatforms {
|
for _, p := range tc.expectedSavedPlatforms {
|
||||||
@@ -381,11 +379,9 @@ func TestSaveAndLoadPlatform(t *testing.T) {
|
|||||||
// load the exported image on the specified platforms only
|
// load the exported image on the specified platforms only
|
||||||
resp, err = apiClient.ImageLoad(ctx, rdr, client.ImageLoadWithPlatforms(tc.loadPlatforms...))
|
resp, err = apiClient.ImageLoad(ctx, rdr, client.ImageLoadWithPlatforms(tc.loadPlatforms...))
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
_, err = io.ReadAll(resp)
|
_, _ = io.Copy(io.Discard, resp)
|
||||||
resp.Close()
|
_ = resp.Close()
|
||||||
assert.NilError(t, err)
|
_ = rdr.Close()
|
||||||
|
|
||||||
rdr.Close()
|
|
||||||
|
|
||||||
// verify the image was loaded for the specified platforms
|
// verify the image was loaded for the specified platforms
|
||||||
for _, p := range tc.expectedLoadedPlatforms {
|
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))
|
resp, err := apiClient.ImageLoad(ctx, rc, client.ImageLoadWithQuiet(true))
|
||||||
assert.NilError(t, err, "Failed to load dangling image")
|
assert.NilError(t, err, "Failed to load dangling image")
|
||||||
|
|
||||||
defer resp.Close()
|
defer func() { _ = resp.Close() }()
|
||||||
|
|
||||||
if !assert.Check(t, err) {
|
if !assert.Check(t, err) {
|
||||||
respBody, err := io.ReadAll(resp)
|
respBody, err := io.ReadAll(resp)
|
||||||
|
|||||||
@@ -445,11 +445,12 @@ func imageLoad(ctx context.Context, apiClient client.APIClient, path string) err
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer file.Close()
|
defer file.Close()
|
||||||
response, err := apiClient.ImageLoad(ctx, file, client.ImageLoadWithQuiet(true))
|
resp, err := apiClient.ImageLoad(ctx, file, client.ImageLoadWithQuiet(true))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer response.Close()
|
_, _ = io.Copy(io.Discard, resp)
|
||||||
|
_ = resp.Close()
|
||||||
return nil
|
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))
|
resp, err := c.ImageLoad(ctx, reader, client.ImageLoadWithQuiet(true))
|
||||||
assert.NilError(t, err, "[%s] failed to load %s", d.id, img)
|
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) {
|
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
|
// 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.
|
// 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) {
|
func (cli *Client) ImageLoad(ctx context.Context, input io.Reader, loadOpts ...ImageLoadOption) (ImageLoadResult, error) {
|
||||||
var opts imageLoadOpts
|
var opts imageLoadOpts
|
||||||
for _, opt := range loadOpts {
|
for _, opt := range loadOpts {
|
||||||
@@ -47,31 +49,16 @@ func (cli *Client) ImageLoad(ctx context.Context, input io.Reader, loadOpts ...I
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &imageLoadResult{
|
return &imageLoadResult{
|
||||||
body: resp.Body,
|
ReadCloser: newCancelReadCloser(ctx, resp.Body),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// imageLoadResult returns information to the client about a load process.
|
// imageLoadResult returns information to the client about a load process.
|
||||||
type imageLoadResult struct {
|
type imageLoadResult struct {
|
||||||
// body must be closed to avoid a resource leak
|
io.ReadCloser
|
||||||
body io.ReadCloser
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
_ io.ReadCloser = (*imageLoadResult)(nil)
|
_ io.ReadCloser = (*imageLoadResult)(nil)
|
||||||
_ ImageLoadResult = (*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