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>
| ... | ... |
@@ -13,8 +13,10 @@ type ImageImportResult interface {
|
| 13 | 13 |
io.ReadCloser |
| 14 | 14 |
} |
| 15 | 15 |
|
| 16 |
-// ImageImport creates a new image based on the source options. |
|
| 17 |
-// It returns the JSON content in the response body. |
|
| 16 |
+// ImageImport creates a new image based on the source options. It returns the |
|
| 17 |
+// JSON content in the [ImageImportResult]. |
|
| 18 |
+// |
|
| 19 |
+// The underlying [io.ReadCloser] is automatically closed if the context is canceled, |
|
| 18 | 20 |
func (cli *Client) ImageImport(ctx context.Context, source ImageImportSource, ref string, options ImageImportOptions) (ImageImportResult, error) {
|
| 19 | 21 |
if ref != "" {
|
| 20 | 22 |
// Check if the given image name can be resolved |
| ... | ... |
@@ -48,30 +50,17 @@ func (cli *Client) ImageImport(ctx context.Context, source ImageImportSource, re |
| 48 | 48 |
if err != nil {
|
| 49 | 49 |
return nil, err |
| 50 | 50 |
} |
| 51 |
- return &imageImportResult{body: resp.Body}, nil
|
|
| 51 |
+ return &imageImportResult{
|
|
| 52 |
+ ReadCloser: newCancelReadCloser(ctx, resp.Body), |
|
| 53 |
+ }, nil |
|
| 52 | 54 |
} |
| 53 | 55 |
|
| 54 | 56 |
// ImageImportResult holds the response body returned by the daemon for image import. |
| 55 | 57 |
type imageImportResult struct {
|
| 56 |
- // body must be closed to avoid a resource leak |
|
| 57 |
- body io.ReadCloser |
|
| 58 |
+ io.ReadCloser |
|
| 58 | 59 |
} |
| 59 | 60 |
|
| 60 | 61 |
var ( |
| 61 | 62 |
_ io.ReadCloser = (*imageImportResult)(nil) |
| 62 | 63 |
_ ImageImportResult = (*imageImportResult)(nil) |
| 63 | 64 |
) |
| 64 |
- |
|
| 65 |
-func (r *imageImportResult) Read(p []byte) (int, error) {
|
|
| 66 |
- if r == nil || r.body == nil {
|
|
| 67 |
- return 0, io.EOF |
|
| 68 |
- } |
|
| 69 |
- return r.body.Read(p) |
|
| 70 |
-} |
|
| 71 |
- |
|
| 72 |
-func (r *imageImportResult) Close() error {
|
|
| 73 |
- if r == nil || r.body == nil {
|
|
| 74 |
- return nil |
|
| 75 |
- } |
|
| 76 |
- return r.body.Close() |
|
| 77 |
-} |
| ... | ... |
@@ -34,6 +34,7 @@ func TestExportContainerAndImportImage(t *testing.T) {
|
| 34 | 34 |
SourceName: "-", |
| 35 | 35 |
}, reference, client.ImageImportOptions{})
|
| 36 | 36 |
assert.NilError(t, err) |
| 37 |
+ defer func() { _ = importRes.Close() }()
|
|
| 37 | 38 |
|
| 38 | 39 |
// If the import is successfully, then the message output should contain |
| 39 | 40 |
// the image ID and match with the output from `docker images`. |
| ... | ... |
@@ -460,17 +460,17 @@ func imageImport(ctx context.Context, apiClient client.APIClient, path string) e |
| 460 | 460 |
return err |
| 461 | 461 |
} |
| 462 | 462 |
defer file.Close() |
| 463 |
- options := client.ImageImportOptions{}
|
|
| 464 | 463 |
ref := "" |
| 465 | 464 |
source := client.ImageImportSource{
|
| 466 | 465 |
Source: file, |
| 467 | 466 |
SourceName: "-", |
| 468 | 467 |
} |
| 469 |
- responseReader, err := apiClient.ImageImport(ctx, source, ref, options) |
|
| 468 |
+ resp, err := apiClient.ImageImport(ctx, source, ref, client.ImageImportOptions{})
|
|
| 470 | 469 |
if err != nil {
|
| 471 | 470 |
return err |
| 472 | 471 |
} |
| 473 |
- defer responseReader.Close() |
|
| 472 |
+ _, _ = io.Copy(io.Discard, resp) |
|
| 473 |
+ _ = resp.Close() |
|
| 474 | 474 |
return nil |
| 475 | 475 |
} |
| 476 | 476 |
|
| ... | ... |
@@ -13,8 +13,10 @@ type ImageImportResult interface {
|
| 13 | 13 |
io.ReadCloser |
| 14 | 14 |
} |
| 15 | 15 |
|
| 16 |
-// ImageImport creates a new image based on the source options. |
|
| 17 |
-// It returns the JSON content in the response body. |
|
| 16 |
+// ImageImport creates a new image based on the source options. It returns the |
|
| 17 |
+// JSON content in the [ImageImportResult]. |
|
| 18 |
+// |
|
| 19 |
+// The underlying [io.ReadCloser] is automatically closed if the context is canceled, |
|
| 18 | 20 |
func (cli *Client) ImageImport(ctx context.Context, source ImageImportSource, ref string, options ImageImportOptions) (ImageImportResult, error) {
|
| 19 | 21 |
if ref != "" {
|
| 20 | 22 |
// Check if the given image name can be resolved |
| ... | ... |
@@ -48,30 +50,17 @@ func (cli *Client) ImageImport(ctx context.Context, source ImageImportSource, re |
| 48 | 48 |
if err != nil {
|
| 49 | 49 |
return nil, err |
| 50 | 50 |
} |
| 51 |
- return &imageImportResult{body: resp.Body}, nil
|
|
| 51 |
+ return &imageImportResult{
|
|
| 52 |
+ ReadCloser: newCancelReadCloser(ctx, resp.Body), |
|
| 53 |
+ }, nil |
|
| 52 | 54 |
} |
| 53 | 55 |
|
| 54 | 56 |
// ImageImportResult holds the response body returned by the daemon for image import. |
| 55 | 57 |
type imageImportResult struct {
|
| 56 |
- // body must be closed to avoid a resource leak |
|
| 57 |
- body io.ReadCloser |
|
| 58 |
+ io.ReadCloser |
|
| 58 | 59 |
} |
| 59 | 60 |
|
| 60 | 61 |
var ( |
| 61 | 62 |
_ io.ReadCloser = (*imageImportResult)(nil) |
| 62 | 63 |
_ ImageImportResult = (*imageImportResult)(nil) |
| 63 | 64 |
) |
| 64 |
- |
|
| 65 |
-func (r *imageImportResult) Read(p []byte) (int, error) {
|
|
| 66 |
- if r == nil || r.body == nil {
|
|
| 67 |
- return 0, io.EOF |
|
| 68 |
- } |
|
| 69 |
- return r.body.Read(p) |
|
| 70 |
-} |
|
| 71 |
- |
|
| 72 |
-func (r *imageImportResult) Close() error {
|
|
| 73 |
- if r == nil || r.body == nil {
|
|
| 74 |
- return nil |
|
| 75 |
- } |
|
| 76 |
- return r.body.Close() |
|
| 77 |
-} |