From ae4c688fd8570c9880feafe81eb67e86403a22b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Gronowski?= Date: Fri, 14 Feb 2025 13:54:11 +0100 Subject: [PATCH] client: Change ImageLoad to use functional options MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Paweł Gronowski --- client/client_interfaces.go | 2 +- client/image_load.go | 50 +++++++++++++++++-- client/image_load_test.go | 11 ++-- integration/build/build_userns_linux_test.go | 2 +- integration/plugin/authz/authz_plugin_test.go | 4 +- internal/testutils/specialimage/load.go | 3 +- testutil/daemon/daemon.go | 2 +- testutil/fixtures/load/frozen.go | 4 +- 8 files changed, 59 insertions(+), 19 deletions(-) diff --git a/client/client_interfaces.go b/client/client_interfaces.go index 27eb6df3ea..39237995a0 100644 --- a/client/client_interfaces.go +++ b/client/client_interfaces.go @@ -125,7 +125,7 @@ type ImageAPIClient interface { ImageInspect(ctx context.Context, image string, _ ...ImageInspectOption) (image.InspectResponse, error) ImageHistory(ctx context.Context, image string, _ ...ImageHistoryOption) ([]image.HistoryResponseItem, error) - ImageLoad(ctx context.Context, input io.Reader, opts image.LoadOptions) (image.LoadResponse, error) + ImageLoad(ctx context.Context, input io.Reader, _ ...ImageLoadOption) (image.LoadResponse, error) ImageSave(ctx context.Context, images []string, opts image.SaveOptions) (io.ReadCloser, error) ImageAPIClientDeprecated diff --git a/client/image_load.go b/client/image_load.go index 50cce4fd01..1a394ea277 100644 --- a/client/image_load.go +++ b/client/image_load.go @@ -2,13 +2,48 @@ package client // import "github.com/docker/docker/client" import ( "context" + "fmt" "io" "net/http" "net/url" "github.com/docker/docker/api/types/image" + ocispec "github.com/opencontainers/image-spec/specs-go/v1" ) +// ImageLoadOption is a type representing functional options for the image load operation. +type ImageLoadOption interface { + Apply(*imageLoadOpts) error +} +type imageLoadOptionFunc func(opt *imageLoadOpts) error + +func (f imageLoadOptionFunc) Apply(o *imageLoadOpts) error { + return f(o) +} + +type imageLoadOpts struct { + apiOptions image.LoadOptions +} + +// ImageLoadWithQuiet sets the quiet option for the image load operation. +func ImageLoadWithQuiet(quiet bool) ImageLoadOption { + return imageLoadOptionFunc(func(opt *imageLoadOpts) error { + opt.apiOptions.Quiet = quiet + return nil + }) +} + +// ImageLoadWithPlatforms sets the platforms to be loaded from the image. +func ImageLoadWithPlatforms(platforms ...ocispec.Platform) ImageLoadOption { + return imageLoadOptionFunc(func(opt *imageLoadOpts) error { + if opt.apiOptions.Platforms != nil { + return fmt.Errorf("platforms already set to %v", opt.apiOptions.Platforms) + } + opt.apiOptions.Platforms = platforms + return nil + }) +} + // ImageLoad loads an image in the docker host from the client host. // It's up to the caller to close the io.ReadCloser in the // ImageLoadResponse returned by this function. @@ -16,18 +51,25 @@ import ( // Platform is an optional parameter that specifies the platform to load from // the provided multi-platform image. This is only has effect if the input image // is a multi-platform image. -func (cli *Client) ImageLoad(ctx context.Context, input io.Reader, opts image.LoadOptions) (image.LoadResponse, error) { +func (cli *Client) ImageLoad(ctx context.Context, input io.Reader, loadOpts ...ImageLoadOption) (image.LoadResponse, error) { + var opts imageLoadOpts + for _, opt := range loadOpts { + if err := opt.Apply(&opts); err != nil { + return image.LoadResponse{}, err + } + } + query := url.Values{} query.Set("quiet", "0") - if opts.Quiet { + if opts.apiOptions.Quiet { query.Set("quiet", "1") } - if len(opts.Platforms) > 0 { + if len(opts.apiOptions.Platforms) > 0 { if err := cli.NewVersionError(ctx, "1.48", "platform"); err != nil { return image.LoadResponse{}, err } - p, err := encodePlatforms(opts.Platforms...) + p, err := encodePlatforms(opts.apiOptions.Platforms...) if err != nil { return image.LoadResponse{}, err } diff --git a/client/image_load_test.go b/client/image_load_test.go index ff512e0318..71a43085de 100644 --- a/client/image_load_test.go +++ b/client/image_load_test.go @@ -8,7 +8,6 @@ import ( "net/url" "testing" - "github.com/docker/docker/api/types/image" "github.com/docker/docker/errdefs" ocispec "github.com/opencontainers/image-spec/specs-go/v1" "gotest.tools/v3/assert" @@ -20,7 +19,7 @@ func TestImageLoadError(t *testing.T) { client: newMockClient(errorMock(http.StatusInternalServerError, "Server error")), } - _, err := client.ImageLoad(context.Background(), nil, image.LoadOptions{Quiet: true}) + _, err := client.ImageLoad(context.Background(), nil, ImageLoadWithQuiet(true)) assert.Check(t, is.ErrorType(err, errdefs.IsSystem)) } @@ -97,10 +96,10 @@ func TestImageLoad(t *testing.T) { } input := bytes.NewReader([]byte(expectedInput)) - imageLoadResponse, err := client.ImageLoad(context.Background(), input, image.LoadOptions{ - Quiet: tc.quiet, - Platforms: tc.platforms, - }) + imageLoadResponse, err := client.ImageLoad(context.Background(), input, + ImageLoadWithQuiet(tc.quiet), + ImageLoadWithPlatforms(tc.platforms...), + ) assert.NilError(t, err) assert.Check(t, is.Equal(imageLoadResponse.JSON, tc.expectedResponseJSON)) diff --git a/integration/build/build_userns_linux_test.go b/integration/build/build_userns_linux_test.go index d4bf7b1cb0..3827fdc2c3 100644 --- a/integration/build/build_userns_linux_test.go +++ b/integration/build/build_userns_linux_test.go @@ -108,7 +108,7 @@ func TestBuildUserNamespaceValidateCapabilitiesAreV2(t *testing.T) { defer tarFile.Close() tarReader := bufio.NewReader(tarFile) - loadResp, err := clientNoUserRemap.ImageLoad(ctx, tarReader, image.LoadOptions{}) + loadResp, err := clientNoUserRemap.ImageLoad(ctx, tarReader) assert.NilError(t, err, "failed to load image tar file") defer loadResp.Body.Close() buf = bytes.NewBuffer(nil) diff --git a/integration/plugin/authz/authz_plugin_test.go b/integration/plugin/authz/authz_plugin_test.go index 1b266773c4..96e9ca0c61 100644 --- a/integration/plugin/authz/authz_plugin_test.go +++ b/integration/plugin/authz/authz_plugin_test.go @@ -439,13 +439,13 @@ func imageSave(ctx context.Context, client client.APIClient, path, imgRef string return err } -func imageLoad(ctx context.Context, client client.APIClient, path string) error { +func imageLoad(ctx context.Context, apiClient client.APIClient, path string) error { file, err := os.Open(path) if err != nil { return err } defer file.Close() - response, err := client.ImageLoad(ctx, file, image.LoadOptions{Quiet: true}) + response, err := apiClient.ImageLoad(ctx, file, client.ImageLoadWithQuiet(true)) if err != nil { return err } diff --git a/internal/testutils/specialimage/load.go b/internal/testutils/specialimage/load.go index db175e525b..1d5b6e049e 100644 --- a/internal/testutils/specialimage/load.go +++ b/internal/testutils/specialimage/load.go @@ -9,7 +9,6 @@ import ( "strings" "testing" - "github.com/docker/docker/api/types/image" "github.com/docker/docker/client" "github.com/docker/docker/pkg/archive" "github.com/docker/docker/pkg/jsonmessage" @@ -30,7 +29,7 @@ func Load(ctx context.Context, t *testing.T, apiClient client.APIClient, imageFu defer rc.Close() - resp, err := apiClient.ImageLoad(ctx, rc, image.LoadOptions{Quiet: true}) + resp, err := apiClient.ImageLoad(ctx, rc, client.ImageLoadWithQuiet(true)) assert.NilError(t, err, "Failed to load dangling image") defer resp.Body.Close() diff --git a/testutil/daemon/daemon.go b/testutil/daemon/daemon.go index 0bd5b208ae..70611bc88b 100644 --- a/testutil/daemon/daemon.go +++ b/testutil/daemon/daemon.go @@ -875,7 +875,7 @@ func (d *Daemon) LoadImage(ctx context.Context, t testing.TB, img string) { c := d.NewClientT(t) defer c.Close() - resp, err := c.ImageLoad(ctx, reader, image.LoadOptions{Quiet: true}) + resp, err := c.ImageLoad(ctx, reader, client.ImageLoadWithQuiet(true)) assert.NilError(t, err, "[%s] failed to load %s", d.id, img) defer resp.Body.Close() } diff --git a/testutil/fixtures/load/frozen.go b/testutil/fixtures/load/frozen.go index e184e6a755..f24172d7a7 100644 --- a/testutil/fixtures/load/frozen.go +++ b/testutil/fixtures/load/frozen.go @@ -92,7 +92,7 @@ func imageExists(ctx context.Context, client client.APIClient, name string) bool return err == nil } -func loadFrozenImages(ctx context.Context, client client.APIClient) error { +func loadFrozenImages(ctx context.Context, apiClient client.APIClient) error { ctx, span := otel.Tracer("").Start(ctx, "load frozen images") defer span.End() @@ -111,7 +111,7 @@ func loadFrozenImages(ctx context.Context, client client.APIClient) error { tarCmd.Start() defer tarCmd.Wait() - resp, err := client.ImageLoad(ctx, out, image.LoadOptions{Quiet: true}) + resp, err := apiClient.ImageLoad(ctx, out, client.ImageLoadWithQuiet(true)) if err != nil { return errors.Wrap(err, "failed to load frozen images") }