Browse code

client/image_create&import: Wrap options and result

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>

Paweł Gronowski authored on 2025/10/21 03:15:04
Showing 13 changed files
... ...
@@ -108,8 +108,8 @@ type ImageAPIClient interface {
108 108
 	ImageBuild(ctx context.Context, context io.Reader, options ImageBuildOptions) (ImageBuildResult, error)
109 109
 	BuildCachePrune(ctx context.Context, opts BuildCachePruneOptions) (BuildCachePruneResult, error)
110 110
 	BuildCancel(ctx context.Context, id string, opts BuildCancelOptions) (BuildCancelResult, error)
111
-	ImageCreate(ctx context.Context, parentReference string, options ImageCreateOptions) (io.ReadCloser, error)
112
-	ImageImport(ctx context.Context, source ImageImportSource, ref string, options ImageImportOptions) (io.ReadCloser, error)
111
+	ImageCreate(ctx context.Context, parentReference string, options ImageCreateOptions) (ImageCreateResult, error)
112
+	ImageImport(ctx context.Context, source ImageImportSource, ref string, options ImageImportOptions) (ImageImportResult, error)
113 113
 
114 114
 	ImageList(ctx context.Context, options ImageListOptions) ([]image.Summary, error)
115 115
 	ImagePull(ctx context.Context, ref string, options ImagePullOptions) (ImagePullResponse, error)
... ...
@@ -2,7 +2,6 @@ package client
2 2
 
3 3
 import (
4 4
 	"context"
5
-	"io"
6 5
 	"net/http"
7 6
 	"net/url"
8 7
 	"strings"
... ...
@@ -13,10 +12,10 @@ import (
13 13
 
14 14
 // ImageCreate creates a new image based on the parent options.
15 15
 // It returns the JSON content in the response body.
16
-func (cli *Client) ImageCreate(ctx context.Context, parentReference string, options ImageCreateOptions) (io.ReadCloser, error) {
16
+func (cli *Client) ImageCreate(ctx context.Context, parentReference string, options ImageCreateOptions) (ImageCreateResult, error) {
17 17
 	ref, err := reference.ParseNormalizedNamed(parentReference)
18 18
 	if err != nil {
19
-		return nil, err
19
+		return ImageCreateResult{}, err
20 20
 	}
21 21
 
22 22
 	query := url.Values{}
... ...
@@ -27,9 +26,9 @@ func (cli *Client) ImageCreate(ctx context.Context, parentReference string, opti
27 27
 	}
28 28
 	resp, err := cli.tryImageCreate(ctx, query, staticAuth(options.RegistryAuth))
29 29
 	if err != nil {
30
-		return nil, err
30
+		return ImageCreateResult{}, err
31 31
 	}
32
-	return resp.Body, nil
32
+	return ImageCreateResult{Body: resp.Body}, nil
33 33
 }
34 34
 
35 35
 func (cli *Client) tryImageCreate(ctx context.Context, query url.Values, resolveAuth registry.RequestAuthConfig) (*http.Response, error) {
... ...
@@ -1,7 +1,14 @@
1 1
 package client
2 2
 
3
+import "io"
4
+
3 5
 // ImageCreateOptions holds information to create images.
4 6
 type ImageCreateOptions struct {
5 7
 	RegistryAuth string // RegistryAuth is the base64 encoded credentials for the registry.
6 8
 	Platform     string // Platform is the target platform of the image if it needs to be pulled from the registry.
7 9
 }
10
+
11
+// ImageCreateResult holds the response body returned by the daemon for image create.
12
+type ImageCreateResult struct {
13
+	Body io.ReadCloser
14
+}
... ...
@@ -57,13 +57,13 @@ func TestImageCreate(t *testing.T) {
57 57
 	}))
58 58
 	assert.NilError(t, err)
59 59
 
60
-	createResponse, err := client.ImageCreate(context.Background(), specifiedReference, ImageCreateOptions{
60
+	createResult, err := client.ImageCreate(context.Background(), specifiedReference, ImageCreateOptions{
61 61
 		RegistryAuth: expectedRegistryAuth,
62 62
 	})
63 63
 	assert.NilError(t, err)
64
-	response, err := io.ReadAll(createResponse)
64
+	response, err := io.ReadAll(createResult.Body)
65 65
 	assert.NilError(t, err)
66
-	err = createResponse.Close()
66
+	err = createResult.Body.Close()
67 67
 	assert.NilError(t, err)
68 68
 	assert.Check(t, is.Equal(string(response), "body"))
69 69
 }
... ...
@@ -2,7 +2,6 @@ package client
2 2
 
3 3
 import (
4 4
 	"context"
5
-	"io"
6 5
 	"net/url"
7 6
 	"strings"
8 7
 
... ...
@@ -11,11 +10,11 @@ import (
11 11
 
12 12
 // ImageImport creates a new image based on the source options.
13 13
 // It returns the JSON content in the response body.
14
-func (cli *Client) ImageImport(ctx context.Context, source ImageImportSource, ref string, options ImageImportOptions) (io.ReadCloser, error) {
14
+func (cli *Client) ImageImport(ctx context.Context, source ImageImportSource, ref string, options ImageImportOptions) (ImageImportResult, error) {
15 15
 	if ref != "" {
16 16
 		// Check if the given image name can be resolved
17 17
 		if _, err := reference.ParseNormalizedNamed(ref); err != nil {
18
-			return nil, err
18
+			return ImageImportResult{}, err
19 19
 		}
20 20
 	}
21 21
 
... ...
@@ -41,7 +40,7 @@ func (cli *Client) ImageImport(ctx context.Context, source ImageImportSource, re
41 41
 
42 42
 	resp, err := cli.postRaw(ctx, "/images/create", query, source.Source, nil)
43 43
 	if err != nil {
44
-		return nil, err
44
+		return ImageImportResult{}, err
45 45
 	}
46
-	return resp.Body, nil
46
+	return ImageImportResult{body: resp.Body}, nil
47 47
 }
... ...
@@ -17,3 +17,19 @@ type ImageImportOptions struct {
17 17
 	Changes  []string // Changes are the raw changes to apply to this image
18 18
 	Platform string   // Platform is the target platform of the image
19 19
 }
20
+
21
+// ImageImportResult holds the response body returned by the daemon for image import.
22
+type ImageImportResult struct {
23
+	body io.ReadCloser
24
+}
25
+
26
+func (r ImageImportResult) Read(p []byte) (n int, err error) {
27
+	return r.body.Read(p)
28
+}
29
+
30
+func (r ImageImportResult) Close() error {
31
+	if r.body == nil {
32
+		return nil
33
+	}
34
+	return r.body.Close()
35
+}
... ...
@@ -77,14 +77,14 @@ func TestImageImport(t *testing.T) {
77 77
 				}, nil
78 78
 			}))
79 79
 			assert.NilError(t, err)
80
-			resp, err := client.ImageImport(context.Background(), ImageImportSource{
80
+			result, err := client.ImageImport(context.Background(), ImageImportSource{
81 81
 				Source:     strings.NewReader("source"),
82 82
 				SourceName: "image_source",
83 83
 			}, "repository_name:imported", tc.options)
84 84
 			assert.NilError(t, err)
85
-			defer assert.NilError(t, resp.Close())
85
+			defer assert.NilError(t, result.Close())
86 86
 
87
-			body, err := io.ReadAll(resp)
87
+			body, err := io.ReadAll(result)
88 88
 			assert.NilError(t, err)
89 89
 			assert.Check(t, is.Equal(string(body), expectedOutput))
90 90
 		})
... ...
@@ -27,10 +27,10 @@ func TestExportContainerAndImportImage(t *testing.T) {
27 27
 	poll.WaitOn(t, container.IsStopped(ctx, apiClient, cID))
28 28
 
29 29
 	reference := "repo/" + strings.ToLower(t.Name()) + ":v1"
30
-	exportResp, err := apiClient.ContainerExport(ctx, cID)
30
+	exportRes, err := apiClient.ContainerExport(ctx, cID)
31 31
 	assert.NilError(t, err)
32
-	importResp, err := apiClient.ImageImport(ctx, client.ImageImportSource{
33
-		Source:     exportResp,
32
+	importRes, err := apiClient.ImageImport(ctx, client.ImageImportSource{
33
+		Source:     exportRes,
34 34
 		SourceName: "-",
35 35
 	}, reference, client.ImageImportOptions{})
36 36
 	assert.NilError(t, err)
... ...
@@ -38,7 +38,7 @@ func TestExportContainerAndImportImage(t *testing.T) {
38 38
 	// If the import is successfully, then the message output should contain
39 39
 	// the image ID and match with the output from `docker images`.
40 40
 
41
-	dec := json.NewDecoder(importResp)
41
+	dec := json.NewDecoder(importRes)
42 42
 	var jm jsonmessage.JSONMessage
43 43
 	err = dec.Decode(&jm)
44 44
 	assert.NilError(t, err)
... ...
@@ -47,7 +47,7 @@ func TestExportContainerAndImportImage(t *testing.T) {
47 47
 		Filters: make(client.Filters).Add("reference", reference),
48 48
 	})
49 49
 	assert.NilError(t, err)
50
-	assert.Check(t, is.Equal(jm.Status, images[0].ID))
50
+	assert.Check(t, is.Equal(jm.Status, images.Images[0].ID))
51 51
 }
52 52
 
53 53
 // TestExportContainerAfterDaemonRestart checks that a container
... ...
@@ -108,8 +108,8 @@ type ImageAPIClient interface {
108 108
 	ImageBuild(ctx context.Context, context io.Reader, options ImageBuildOptions) (ImageBuildResult, error)
109 109
 	BuildCachePrune(ctx context.Context, opts BuildCachePruneOptions) (BuildCachePruneResult, error)
110 110
 	BuildCancel(ctx context.Context, id string, opts BuildCancelOptions) (BuildCancelResult, error)
111
-	ImageCreate(ctx context.Context, parentReference string, options ImageCreateOptions) (io.ReadCloser, error)
112
-	ImageImport(ctx context.Context, source ImageImportSource, ref string, options ImageImportOptions) (io.ReadCloser, error)
111
+	ImageCreate(ctx context.Context, parentReference string, options ImageCreateOptions) (ImageCreateResult, error)
112
+	ImageImport(ctx context.Context, source ImageImportSource, ref string, options ImageImportOptions) (ImageImportResult, error)
113 113
 
114 114
 	ImageList(ctx context.Context, options ImageListOptions) ([]image.Summary, error)
115 115
 	ImagePull(ctx context.Context, ref string, options ImagePullOptions) (ImagePullResponse, error)
... ...
@@ -2,7 +2,6 @@ package client
2 2
 
3 3
 import (
4 4
 	"context"
5
-	"io"
6 5
 	"net/http"
7 6
 	"net/url"
8 7
 	"strings"
... ...
@@ -13,10 +12,10 @@ import (
13 13
 
14 14
 // ImageCreate creates a new image based on the parent options.
15 15
 // It returns the JSON content in the response body.
16
-func (cli *Client) ImageCreate(ctx context.Context, parentReference string, options ImageCreateOptions) (io.ReadCloser, error) {
16
+func (cli *Client) ImageCreate(ctx context.Context, parentReference string, options ImageCreateOptions) (ImageCreateResult, error) {
17 17
 	ref, err := reference.ParseNormalizedNamed(parentReference)
18 18
 	if err != nil {
19
-		return nil, err
19
+		return ImageCreateResult{}, err
20 20
 	}
21 21
 
22 22
 	query := url.Values{}
... ...
@@ -27,9 +26,9 @@ func (cli *Client) ImageCreate(ctx context.Context, parentReference string, opti
27 27
 	}
28 28
 	resp, err := cli.tryImageCreate(ctx, query, staticAuth(options.RegistryAuth))
29 29
 	if err != nil {
30
-		return nil, err
30
+		return ImageCreateResult{}, err
31 31
 	}
32
-	return resp.Body, nil
32
+	return ImageCreateResult{Body: resp.Body}, nil
33 33
 }
34 34
 
35 35
 func (cli *Client) tryImageCreate(ctx context.Context, query url.Values, resolveAuth registry.RequestAuthConfig) (*http.Response, error) {
... ...
@@ -1,7 +1,14 @@
1 1
 package client
2 2
 
3
+import "io"
4
+
3 5
 // ImageCreateOptions holds information to create images.
4 6
 type ImageCreateOptions struct {
5 7
 	RegistryAuth string // RegistryAuth is the base64 encoded credentials for the registry.
6 8
 	Platform     string // Platform is the target platform of the image if it needs to be pulled from the registry.
7 9
 }
10
+
11
+// ImageCreateResult holds the response body returned by the daemon for image create.
12
+type ImageCreateResult struct {
13
+	Body io.ReadCloser
14
+}
... ...
@@ -2,7 +2,6 @@ package client
2 2
 
3 3
 import (
4 4
 	"context"
5
-	"io"
6 5
 	"net/url"
7 6
 	"strings"
8 7
 
... ...
@@ -11,11 +10,11 @@ import (
11 11
 
12 12
 // ImageImport creates a new image based on the source options.
13 13
 // It returns the JSON content in the response body.
14
-func (cli *Client) ImageImport(ctx context.Context, source ImageImportSource, ref string, options ImageImportOptions) (io.ReadCloser, error) {
14
+func (cli *Client) ImageImport(ctx context.Context, source ImageImportSource, ref string, options ImageImportOptions) (ImageImportResult, error) {
15 15
 	if ref != "" {
16 16
 		// Check if the given image name can be resolved
17 17
 		if _, err := reference.ParseNormalizedNamed(ref); err != nil {
18
-			return nil, err
18
+			return ImageImportResult{}, err
19 19
 		}
20 20
 	}
21 21
 
... ...
@@ -41,7 +40,7 @@ func (cli *Client) ImageImport(ctx context.Context, source ImageImportSource, re
41 41
 
42 42
 	resp, err := cli.postRaw(ctx, "/images/create", query, source.Source, nil)
43 43
 	if err != nil {
44
-		return nil, err
44
+		return ImageImportResult{}, err
45 45
 	}
46
-	return resp.Body, nil
46
+	return ImageImportResult{body: resp.Body}, nil
47 47
 }
... ...
@@ -17,3 +17,19 @@ type ImageImportOptions struct {
17 17
 	Changes  []string // Changes are the raw changes to apply to this image
18 18
 	Platform string   // Platform is the target platform of the image
19 19
 }
20
+
21
+// ImageImportResult holds the response body returned by the daemon for image import.
22
+type ImageImportResult struct {
23
+	body io.ReadCloser
24
+}
25
+
26
+func (r ImageImportResult) Read(p []byte) (n int, err error) {
27
+	return r.body.Read(p)
28
+}
29
+
30
+func (r ImageImportResult) Close() error {
31
+	if r.body == nil {
32
+		return nil
33
+	}
34
+	return r.body.Close()
35
+}