client: Change ImageLoad to use functional options

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
This commit is contained in:
Paweł Gronowski
2025-02-14 13:54:11 +01:00
parent 85808a6abf
commit ae4c688fd8
8 changed files with 59 additions and 19 deletions

View File

@@ -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

View File

@@ -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
}

View File

@@ -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))

View File

@@ -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)

View File

@@ -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
}

View File

@@ -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()

View File

@@ -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()
}

View File

@@ -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")
}