client: Client.ImageSave: 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:
Sebastiaan van Stijn
2025-10-30 11:56:24 +01:00
parent 175e4e5048
commit f76f1fc013
10 changed files with 32 additions and 54 deletions

View File

@@ -75,7 +75,7 @@ func TestBuildUserNamespaceValidateCapabilitiesAreV2(t *testing.T) {
reader, err := clientUserRemap.ImageSave(ctx, []string{imageTag})
assert.NilError(t, err, "failed to download capabilities image")
defer reader.Close()
defer func() { _ = reader.Close() }()
tar, err := os.Create(filepath.Join(tmpDir, "image.tar"))
assert.NilError(t, err, "failed to create image tar file")

View File

@@ -130,8 +130,8 @@ func TestMigrateSaveLoad(t *testing.T) {
rdr, err := apiClient.ImageSave(ctx, []string{"busybox:latest"})
assert.NilError(t, err)
buf := bytes.NewBuffer(nil)
io.Copy(buf, rdr)
rdr.Close()
_, _ = io.Copy(buf, rdr)
defer func() { _ = rdr.Close() }()
// Delete all images
list, err := apiClient.ImageList(ctx, client.ImageListOptions{})

View File

@@ -44,7 +44,7 @@ func tarIndexFS(t *testing.T, rdr io.Reader) fs.FS {
assert.NilError(t, err)
// Do not close at the end of this function otherwise the indexer won't work
t.Cleanup(func() { f.Close() })
t.Cleanup(func() { _ = f.Close() })
_, err = io.Copy(f, rdr)
assert.NilError(t, err)
@@ -64,6 +64,7 @@ func TestSaveCheckTimes(t *testing.T) {
rdr, err := apiClient.ImageSave(ctx, []string{repoName})
assert.NilError(t, err)
defer func() { _ = rdr.Close() }()
created, err := time.Parse(time.RFC3339, img.Created)
assert.NilError(t, err)
@@ -135,7 +136,7 @@ func TestSaveOCI(t *testing.T) {
rdr, err := apiClient.ImageSave(ctx, []string{tc.image})
assert.NilError(t, err)
defer rdr.Close()
defer func() { _ = rdr.Close() }()
tarfs := tarIndexFS(t, rdr)
@@ -171,7 +172,7 @@ func TestSaveOCI(t *testing.T) {
assert.NilError(t, err)
layerDigest, err := testutil.UncompressedTarDigest(f)
f.Close()
_ = f.Close()
assert.NilError(t, err)
@@ -426,7 +427,7 @@ func TestSaveRepoWithMultipleImages(t *testing.T) {
rdr, err := apiClient.ImageSave(ctx, []string{repoName, "busybox:latest"})
assert.NilError(t, err)
defer rdr.Close()
defer func() { _ = rdr.Close() }()
tarfs := tarIndexFS(t, rdr)
@@ -483,7 +484,7 @@ RUN touch /opt/a/b/c && chown user:user /opt/a/b/c`
rdr, err := apiClient.ImageSave(ctx, []string{imgID})
assert.NilError(t, err)
defer rdr.Close()
defer func() { _ = rdr.Close() }()
tarfs := tarIndexFS(t, rdr)

View File

@@ -425,17 +425,17 @@ func TestAuthzPluginEnsureContainerCopyToFrom(t *testing.T) {
}
func imageSave(ctx context.Context, apiClient client.APIClient, path, imgRef string) error {
responseReader, err := apiClient.ImageSave(ctx, []string{imgRef})
resp, err := apiClient.ImageSave(ctx, []string{imgRef})
if err != nil {
return err
}
defer responseReader.Close()
defer func() { _ = resp.Close() }()
file, err := os.Create(path)
if err != nil {
return err
}
defer file.Close()
_, err = io.Copy(file, responseReader)
_, err = io.Copy(file, resp)
_ = file.Close()
return err
}