Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
| ... | ... |
@@ -125,7 +125,7 @@ type ImageAPIClient interface {
|
| 125 | 125 |
|
| 126 | 126 |
ImageInspect(ctx context.Context, image string, _ ...ImageInspectOption) (image.InspectResponse, error) |
| 127 | 127 |
ImageHistory(ctx context.Context, image string, _ ...ImageHistoryOption) ([]image.HistoryResponseItem, error) |
| 128 |
- ImageLoad(ctx context.Context, input io.Reader, opts image.LoadOptions) (image.LoadResponse, error) |
|
| 128 |
+ ImageLoad(ctx context.Context, input io.Reader, _ ...ImageLoadOption) (image.LoadResponse, error) |
|
| 129 | 129 |
ImageSave(ctx context.Context, images []string, opts image.SaveOptions) (io.ReadCloser, error) |
| 130 | 130 |
|
| 131 | 131 |
ImageAPIClientDeprecated |
| ... | ... |
@@ -2,13 +2,48 @@ package client // import "github.com/docker/docker/client" |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 | 4 |
"context" |
| 5 |
+ "fmt" |
|
| 5 | 6 |
"io" |
| 6 | 7 |
"net/http" |
| 7 | 8 |
"net/url" |
| 8 | 9 |
|
| 9 | 10 |
"github.com/docker/docker/api/types/image" |
| 11 |
+ ocispec "github.com/opencontainers/image-spec/specs-go/v1" |
|
| 10 | 12 |
) |
| 11 | 13 |
|
| 14 |
+// ImageLoadOption is a type representing functional options for the image load operation. |
|
| 15 |
+type ImageLoadOption interface {
|
|
| 16 |
+ Apply(*imageLoadOpts) error |
|
| 17 |
+} |
|
| 18 |
+type imageLoadOptionFunc func(opt *imageLoadOpts) error |
|
| 19 |
+ |
|
| 20 |
+func (f imageLoadOptionFunc) Apply(o *imageLoadOpts) error {
|
|
| 21 |
+ return f(o) |
|
| 22 |
+} |
|
| 23 |
+ |
|
| 24 |
+type imageLoadOpts struct {
|
|
| 25 |
+ apiOptions image.LoadOptions |
|
| 26 |
+} |
|
| 27 |
+ |
|
| 28 |
+// ImageLoadWithQuiet sets the quiet option for the image load operation. |
|
| 29 |
+func ImageLoadWithQuiet(quiet bool) ImageLoadOption {
|
|
| 30 |
+ return imageLoadOptionFunc(func(opt *imageLoadOpts) error {
|
|
| 31 |
+ opt.apiOptions.Quiet = quiet |
|
| 32 |
+ return nil |
|
| 33 |
+ }) |
|
| 34 |
+} |
|
| 35 |
+ |
|
| 36 |
+// ImageLoadWithPlatforms sets the platforms to be loaded from the image. |
|
| 37 |
+func ImageLoadWithPlatforms(platforms ...ocispec.Platform) ImageLoadOption {
|
|
| 38 |
+ return imageLoadOptionFunc(func(opt *imageLoadOpts) error {
|
|
| 39 |
+ if opt.apiOptions.Platforms != nil {
|
|
| 40 |
+ return fmt.Errorf("platforms already set to %v", opt.apiOptions.Platforms)
|
|
| 41 |
+ } |
|
| 42 |
+ opt.apiOptions.Platforms = platforms |
|
| 43 |
+ return nil |
|
| 44 |
+ }) |
|
| 45 |
+} |
|
| 46 |
+ |
|
| 12 | 47 |
// ImageLoad loads an image in the docker host from the client host. |
| 13 | 48 |
// It's up to the caller to close the io.ReadCloser in the |
| 14 | 49 |
// ImageLoadResponse returned by this function. |
| ... | ... |
@@ -16,18 +51,25 @@ import ( |
| 16 | 16 |
// Platform is an optional parameter that specifies the platform to load from |
| 17 | 17 |
// the provided multi-platform image. This is only has effect if the input image |
| 18 | 18 |
// is a multi-platform image. |
| 19 |
-func (cli *Client) ImageLoad(ctx context.Context, input io.Reader, opts image.LoadOptions) (image.LoadResponse, error) {
|
|
| 19 |
+func (cli *Client) ImageLoad(ctx context.Context, input io.Reader, loadOpts ...ImageLoadOption) (image.LoadResponse, error) {
|
|
| 20 |
+ var opts imageLoadOpts |
|
| 21 |
+ for _, opt := range loadOpts {
|
|
| 22 |
+ if err := opt.Apply(&opts); err != nil {
|
|
| 23 |
+ return image.LoadResponse{}, err
|
|
| 24 |
+ } |
|
| 25 |
+ } |
|
| 26 |
+ |
|
| 20 | 27 |
query := url.Values{}
|
| 21 | 28 |
query.Set("quiet", "0")
|
| 22 |
- if opts.Quiet {
|
|
| 29 |
+ if opts.apiOptions.Quiet {
|
|
| 23 | 30 |
query.Set("quiet", "1")
|
| 24 | 31 |
} |
| 25 |
- if len(opts.Platforms) > 0 {
|
|
| 32 |
+ if len(opts.apiOptions.Platforms) > 0 {
|
|
| 26 | 33 |
if err := cli.NewVersionError(ctx, "1.48", "platform"); err != nil {
|
| 27 | 34 |
return image.LoadResponse{}, err
|
| 28 | 35 |
} |
| 29 | 36 |
|
| 30 |
- p, err := encodePlatforms(opts.Platforms...) |
|
| 37 |
+ p, err := encodePlatforms(opts.apiOptions.Platforms...) |
|
| 31 | 38 |
if err != nil {
|
| 32 | 39 |
return image.LoadResponse{}, err
|
| 33 | 40 |
} |
| ... | ... |
@@ -8,7 +8,6 @@ import ( |
| 8 | 8 |
"net/url" |
| 9 | 9 |
"testing" |
| 10 | 10 |
|
| 11 |
- "github.com/docker/docker/api/types/image" |
|
| 12 | 11 |
"github.com/docker/docker/errdefs" |
| 13 | 12 |
ocispec "github.com/opencontainers/image-spec/specs-go/v1" |
| 14 | 13 |
"gotest.tools/v3/assert" |
| ... | ... |
@@ -20,7 +19,7 @@ func TestImageLoadError(t *testing.T) {
|
| 20 | 20 |
client: newMockClient(errorMock(http.StatusInternalServerError, "Server error")), |
| 21 | 21 |
} |
| 22 | 22 |
|
| 23 |
- _, err := client.ImageLoad(context.Background(), nil, image.LoadOptions{Quiet: true})
|
|
| 23 |
+ _, err := client.ImageLoad(context.Background(), nil, ImageLoadWithQuiet(true)) |
|
| 24 | 24 |
assert.Check(t, is.ErrorType(err, errdefs.IsSystem)) |
| 25 | 25 |
} |
| 26 | 26 |
|
| ... | ... |
@@ -97,10 +96,10 @@ func TestImageLoad(t *testing.T) {
|
| 97 | 97 |
} |
| 98 | 98 |
|
| 99 | 99 |
input := bytes.NewReader([]byte(expectedInput)) |
| 100 |
- imageLoadResponse, err := client.ImageLoad(context.Background(), input, image.LoadOptions{
|
|
| 101 |
- Quiet: tc.quiet, |
|
| 102 |
- Platforms: tc.platforms, |
|
| 103 |
- }) |
|
| 100 |
+ imageLoadResponse, err := client.ImageLoad(context.Background(), input, |
|
| 101 |
+ ImageLoadWithQuiet(tc.quiet), |
|
| 102 |
+ ImageLoadWithPlatforms(tc.platforms...), |
|
| 103 |
+ ) |
|
| 104 | 104 |
assert.NilError(t, err) |
| 105 | 105 |
assert.Check(t, is.Equal(imageLoadResponse.JSON, tc.expectedResponseJSON)) |
| 106 | 106 |
|
| ... | ... |
@@ -108,7 +108,7 @@ func TestBuildUserNamespaceValidateCapabilitiesAreV2(t *testing.T) {
|
| 108 | 108 |
defer tarFile.Close() |
| 109 | 109 |
|
| 110 | 110 |
tarReader := bufio.NewReader(tarFile) |
| 111 |
- loadResp, err := clientNoUserRemap.ImageLoad(ctx, tarReader, image.LoadOptions{})
|
|
| 111 |
+ loadResp, err := clientNoUserRemap.ImageLoad(ctx, tarReader) |
|
| 112 | 112 |
assert.NilError(t, err, "failed to load image tar file") |
| 113 | 113 |
defer loadResp.Body.Close() |
| 114 | 114 |
buf = bytes.NewBuffer(nil) |
| ... | ... |
@@ -439,13 +439,13 @@ func imageSave(ctx context.Context, client client.APIClient, path, imgRef string |
| 439 | 439 |
return err |
| 440 | 440 |
} |
| 441 | 441 |
|
| 442 |
-func imageLoad(ctx context.Context, client client.APIClient, path string) error {
|
|
| 442 |
+func imageLoad(ctx context.Context, apiClient client.APIClient, path string) error {
|
|
| 443 | 443 |
file, err := os.Open(path) |
| 444 | 444 |
if err != nil {
|
| 445 | 445 |
return err |
| 446 | 446 |
} |
| 447 | 447 |
defer file.Close() |
| 448 |
- response, err := client.ImageLoad(ctx, file, image.LoadOptions{Quiet: true})
|
|
| 448 |
+ response, err := apiClient.ImageLoad(ctx, file, client.ImageLoadWithQuiet(true)) |
|
| 449 | 449 |
if err != nil {
|
| 450 | 450 |
return err |
| 451 | 451 |
} |
| ... | ... |
@@ -9,7 +9,6 @@ import ( |
| 9 | 9 |
"strings" |
| 10 | 10 |
"testing" |
| 11 | 11 |
|
| 12 |
- "github.com/docker/docker/api/types/image" |
|
| 13 | 12 |
"github.com/docker/docker/client" |
| 14 | 13 |
"github.com/docker/docker/pkg/archive" |
| 15 | 14 |
"github.com/docker/docker/pkg/jsonmessage" |
| ... | ... |
@@ -30,7 +29,7 @@ func Load(ctx context.Context, t *testing.T, apiClient client.APIClient, imageFu |
| 30 | 30 |
|
| 31 | 31 |
defer rc.Close() |
| 32 | 32 |
|
| 33 |
- resp, err := apiClient.ImageLoad(ctx, rc, image.LoadOptions{Quiet: true})
|
|
| 33 |
+ resp, err := apiClient.ImageLoad(ctx, rc, client.ImageLoadWithQuiet(true)) |
|
| 34 | 34 |
assert.NilError(t, err, "Failed to load dangling image") |
| 35 | 35 |
|
| 36 | 36 |
defer resp.Body.Close() |
| ... | ... |
@@ -875,7 +875,7 @@ func (d *Daemon) LoadImage(ctx context.Context, t testing.TB, img string) {
|
| 875 | 875 |
c := d.NewClientT(t) |
| 876 | 876 |
defer c.Close() |
| 877 | 877 |
|
| 878 |
- resp, err := c.ImageLoad(ctx, reader, image.LoadOptions{Quiet: true})
|
|
| 878 |
+ resp, err := c.ImageLoad(ctx, reader, client.ImageLoadWithQuiet(true)) |
|
| 879 | 879 |
assert.NilError(t, err, "[%s] failed to load %s", d.id, img) |
| 880 | 880 |
defer resp.Body.Close() |
| 881 | 881 |
} |
| ... | ... |
@@ -92,7 +92,7 @@ func imageExists(ctx context.Context, client client.APIClient, name string) bool |
| 92 | 92 |
return err == nil |
| 93 | 93 |
} |
| 94 | 94 |
|
| 95 |
-func loadFrozenImages(ctx context.Context, client client.APIClient) error {
|
|
| 95 |
+func loadFrozenImages(ctx context.Context, apiClient client.APIClient) error {
|
|
| 96 | 96 |
ctx, span := otel.Tracer("").Start(ctx, "load frozen images")
|
| 97 | 97 |
defer span.End() |
| 98 | 98 |
|
| ... | ... |
@@ -111,7 +111,7 @@ func loadFrozenImages(ctx context.Context, client client.APIClient) error {
|
| 111 | 111 |
tarCmd.Start() |
| 112 | 112 |
defer tarCmd.Wait() |
| 113 | 113 |
|
| 114 |
- resp, err := client.ImageLoad(ctx, out, image.LoadOptions{Quiet: true})
|
|
| 114 |
+ resp, err := apiClient.ImageLoad(ctx, out, client.ImageLoadWithQuiet(true)) |
|
| 115 | 115 |
if err != nil {
|
| 116 | 116 |
return errors.Wrap(err, "failed to load frozen images") |
| 117 | 117 |
} |