vendor: github.com/moby/moby/api, moby/moby/client master

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
Sebastiaan van Stijn
2025-10-28 21:19:59 +01:00
parent 606f1c65d0
commit 053aa376ea
99 changed files with 1009 additions and 683 deletions

View File

@ -74,7 +74,7 @@ func RunAttach(ctx context.Context, dockerCLI command.Cli, containerID string, o
// request channel to wait for client
waitCtx := context.WithoutCancel(ctx)
resultC, errC := apiClient.ContainerWait(waitCtx, containerID, "")
waitRes := apiClient.ContainerWait(waitCtx, containerID, client.ContainerWaitOptions{})
c, err := inspectContainerAndCheckState(ctx, apiClient, containerID)
if err != nil {
@ -152,19 +152,19 @@ func RunAttach(ctx context.Context, dockerCLI command.Cli, containerID string, o
return err
}
return getExitStatus(errC, resultC)
return getExitStatus(waitRes)
}
func getExitStatus(errC <-chan error, resultC <-chan container.WaitResponse) error {
func getExitStatus(waitRes client.ContainerWaitResult) error {
select {
case result := <-resultC:
case result := <-waitRes.Result:
if result.Error != nil {
return errors.New(result.Error.Message)
}
if result.StatusCode != 0 {
return cli.StatusError{StatusCode: int(result.StatusCode)}
}
case err := <-errC:
case err := <-waitRes.Error:
return err
}

View File

@ -125,7 +125,10 @@ func TestGetExitStatus(t *testing.T) {
resultC <- *testcase.result
}
err := getExitStatus(errC, resultC)
err := getExitStatus(client.ContainerWaitResult{
Result: resultC,
Error: errC,
})
if testcase.expectedError == nil {
assert.NilError(t, err)

View File

@ -3,12 +3,35 @@ package container
import (
"context"
"io"
"reflect"
"strings"
"unsafe"
"github.com/moby/moby/api/types/container"
"github.com/moby/moby/api/types/system"
"github.com/moby/moby/client"
)
func mockContainerExportResult(content string) client.ContainerExportResult {
out := client.ContainerExportResult{}
// Set unexported field "rc"
v := reflect.ValueOf(&out).Elem()
f := v.FieldByName("rc")
r := io.NopCloser(strings.NewReader(content))
reflect.NewAt(f.Type(), unsafe.Pointer(f.UnsafeAddr())).Elem().Set(reflect.ValueOf(r))
return out
}
func mockContainerLogsResult(content string) client.ContainerLogsResult {
out := client.ContainerLogsResult{}
// Set unexported field "rc"
v := reflect.ValueOf(&out).Elem()
f := v.FieldByName("rc")
r := io.NopCloser(strings.NewReader(content))
reflect.NewAt(f.Type(), unsafe.Pointer(f.UnsafeAddr())).Elem().Set(reflect.ValueOf(r))
return out
}
type fakeClient struct {
client.Client
inspectFunc func(string) (client.ContainerInspectResult, error)
@ -17,13 +40,13 @@ type fakeClient struct {
createContainerFunc func(options client.ContainerCreateOptions) (client.ContainerCreateResult, error)
containerStartFunc func(containerID string, options client.ContainerStartOptions) (client.ContainerStartResult, error)
imageCreateFunc func(ctx context.Context, parentReference string, options client.ImageCreateOptions) (client.ImageCreateResult, error)
infoFunc func() (system.Info, error)
containerStatPathFunc func(containerID, path string) (container.PathStat, error)
containerCopyFromFunc func(containerID, srcPath string) (io.ReadCloser, container.PathStat, error)
logFunc func(string, client.ContainerLogsOptions) (io.ReadCloser, error)
waitFunc func(string) (<-chan container.WaitResponse, <-chan error)
containerListFunc func(client.ContainerListOptions) ([]container.Summary, error)
containerExportFunc func(string) (io.ReadCloser, error)
infoFunc func() (client.SystemInfoResult, error)
containerStatPathFunc func(containerID, path string) (client.ContainerStatPathResult, error)
containerCopyFromFunc func(containerID, srcPath string) (client.CopyFromContainerResult, error)
logFunc func(string, client.ContainerLogsOptions) (client.ContainerLogsResult, error)
waitFunc func(string) client.ContainerWaitResult
containerListFunc func(client.ContainerListOptions) (client.ContainerListResult, error)
containerExportFunc func(string) (client.ContainerExportResult, error)
containerExecResizeFunc func(id string, options client.ExecResizeOptions) (client.ExecResizeResult, error)
containerRemoveFunc func(ctx context.Context, containerID string, options client.ContainerRemoveOptions) (client.ContainerRemoveResult, error)
containerRestartFunc func(ctx context.Context, containerID string, options client.ContainerRestartOptions) (client.ContainerRestartResult, error)
@ -38,14 +61,14 @@ type fakeClient struct {
Version string
}
func (f *fakeClient) ContainerList(_ context.Context, options client.ContainerListOptions) ([]container.Summary, error) {
func (f *fakeClient) ContainerList(_ context.Context, options client.ContainerListOptions) (client.ContainerListResult, error) {
if f.containerListFunc != nil {
return f.containerListFunc(options)
}
return []container.Summary{}, nil
return client.ContainerListResult{}, nil
}
func (f *fakeClient) ContainerInspect(_ context.Context, containerID string, options client.ContainerInspectOptions) (client.ContainerInspectResult, error) {
func (f *fakeClient) ContainerInspect(_ context.Context, containerID string, _ client.ContainerInspectOptions) (client.ContainerInspectResult, error) {
if f.inspectFunc != nil {
return f.inspectFunc(containerID)
}
@ -91,43 +114,43 @@ func (f *fakeClient) ImageCreate(ctx context.Context, parentReference string, op
return client.ImageCreateResult{}, nil
}
func (f *fakeClient) Info(_ context.Context) (system.Info, error) {
func (f *fakeClient) Info(context.Context, client.InfoOptions) (client.SystemInfoResult, error) {
if f.infoFunc != nil {
return f.infoFunc()
}
return system.Info{}, nil
return client.SystemInfoResult{}, nil
}
func (f *fakeClient) ContainerStatPath(_ context.Context, containerID, path string) (container.PathStat, error) {
func (f *fakeClient) ContainerStatPath(_ context.Context, containerID string, options client.ContainerStatPathOptions) (client.ContainerStatPathResult, error) {
if f.containerStatPathFunc != nil {
return f.containerStatPathFunc(containerID, path)
return f.containerStatPathFunc(containerID, options.Path)
}
return container.PathStat{}, nil
return client.ContainerStatPathResult{}, nil
}
func (f *fakeClient) CopyFromContainer(_ context.Context, containerID, srcPath string) (io.ReadCloser, container.PathStat, error) {
func (f *fakeClient) CopyFromContainer(_ context.Context, containerID string, options client.CopyFromContainerOptions) (client.CopyFromContainerResult, error) {
if f.containerCopyFromFunc != nil {
return f.containerCopyFromFunc(containerID, srcPath)
return f.containerCopyFromFunc(containerID, options.SourcePath)
}
return nil, container.PathStat{}, nil
return client.CopyFromContainerResult{}, nil
}
func (f *fakeClient) ContainerLogs(_ context.Context, containerID string, options client.ContainerLogsOptions) (io.ReadCloser, error) {
func (f *fakeClient) ContainerLogs(_ context.Context, containerID string, options client.ContainerLogsOptions) (client.ContainerLogsResult, error) {
if f.logFunc != nil {
return f.logFunc(containerID, options)
}
return nil, nil
return client.ContainerLogsResult{}, nil
}
func (f *fakeClient) ClientVersion() string {
return f.Version
}
func (f *fakeClient) ContainerWait(_ context.Context, containerID string, _ container.WaitCondition) (<-chan container.WaitResponse, <-chan error) {
func (f *fakeClient) ContainerWait(_ context.Context, containerID string, _ client.ContainerWaitOptions) client.ContainerWaitResult {
if f.waitFunc != nil {
return f.waitFunc(containerID)
}
return nil, nil
return client.ContainerWaitResult{}
}
func (f *fakeClient) ContainerStart(_ context.Context, containerID string, options client.ContainerStartOptions) (client.ContainerStartResult, error) {
@ -137,11 +160,11 @@ func (f *fakeClient) ContainerStart(_ context.Context, containerID string, optio
return client.ContainerStartResult{}, nil
}
func (f *fakeClient) ContainerExport(_ context.Context, containerID string) (io.ReadCloser, error) {
func (f *fakeClient) ContainerExport(_ context.Context, containerID string, _ client.ContainerExportOptions) (client.ContainerExportResult, error) {
if f.containerExportFunc != nil {
return f.containerExportFunc(containerID)
}
return nil, nil
return client.ContainerExportResult{}, nil
}
func (f *fakeClient) ExecResize(_ context.Context, id string, options client.ExecResizeOptions) (client.ExecResizeResult, error) {
@ -194,12 +217,12 @@ func (f *fakeClient) ContainerDiff(ctx context.Context, containerID string, _ cl
return client.ContainerDiffResult{}, nil
}
func (f *fakeClient) ContainerRename(ctx context.Context, oldName, newName string) error {
func (f *fakeClient) ContainerRename(ctx context.Context, oldName string, options client.ContainerRenameOptions) (client.ContainerRenameResult, error) {
if f.containerRenameFunc != nil {
return f.containerRenameFunc(ctx, oldName, newName)
return client.ContainerRenameResult{}, f.containerRenameFunc(ctx, oldName, options.NewName)
}
return nil
return client.ContainerRenameResult{}, nil
}
func (f *fakeClient) ContainerCommit(ctx context.Context, containerID string, options client.ContainerCommitOptions) (client.ContainerCommitResult, error) {

View File

@ -9,6 +9,7 @@ import (
"github.com/docker/cli/cli/command/completion"
"github.com/moby/moby/api/types/container"
"github.com/moby/moby/client"
"github.com/moby/sys/capability"
"github.com/moby/sys/signal"
"github.com/spf13/cobra"
@ -186,11 +187,11 @@ func completeLink(dockerCLI completion.APIClientProvider) cobra.CompletionFunc {
// of the build-in log drivers.
func completeLogDriver(dockerCLI completion.APIClientProvider) cobra.CompletionFunc {
return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
info, err := dockerCLI.Client().Info(cmd.Context())
res, err := dockerCLI.Client().Info(cmd.Context(), client.InfoOptions{})
if err != nil {
return builtInLogDrivers(), cobra.ShellCompDirectiveNoFileComp
}
drivers := info.Plugins.Log
drivers := res.Info.Plugins.Log
return drivers, cobra.ShellCompDirectiveNoFileComp
}
}
@ -279,12 +280,12 @@ func completeUlimit(_ *cobra.Command, _ []string, _ string) ([]string, cobra.She
// completeVolumeDriver contacts the API to get the built-in and installed volume drivers.
func completeVolumeDriver(dockerCLI completion.APIClientProvider) cobra.CompletionFunc {
return func(cmd *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) {
info, err := dockerCLI.Client().Info(cmd.Context())
res, err := dockerCLI.Client().Info(cmd.Context(), client.InfoOptions{})
if err != nil {
// fallback: the built-in drivers
return []string{"local"}, cobra.ShellCompDirectiveNoFileComp
}
drivers := info.Plugins.Volume
drivers := res.Info.Plugins.Volume
return drivers, cobra.ShellCompDirectiveNoFileComp
}
}

View File

@ -27,7 +27,7 @@ func TestCompleteLinuxCapabilityNames(t *testing.T) {
func TestCompletePid(t *testing.T) {
tests := []struct {
containerListFunc func(client.ContainerListOptions) ([]container.Summary, error)
containerListFunc func(client.ContainerListOptions) (client.ContainerListResult, error)
toComplete string
expectedCompletions []string
expectedDirective cobra.ShellCompDirective
@ -43,10 +43,12 @@ func TestCompletePid(t *testing.T) {
expectedDirective: cobra.ShellCompDirectiveNoSpace,
},
{
containerListFunc: func(client.ContainerListOptions) ([]container.Summary, error) {
return []container.Summary{
*builders.Container("c1"),
*builders.Container("c2"),
containerListFunc: func(client.ContainerListOptions) (client.ContainerListResult, error) {
return client.ContainerListResult{
Items: []container.Summary{
*builders.Container("c1"),
*builders.Container("c2"),
},
}, nil
},
toComplete: "container:",

View File

@ -230,11 +230,13 @@ func copyFromContainer(ctx context.Context, dockerCLI command.Cli, copyConfig cp
// if client requests to follow symbol link, then must decide target file to be copied
var rebaseName string
if copyConfig.followLink {
srcStat, err := apiClient.ContainerStatPath(ctx, copyConfig.container, srcPath)
src, err := apiClient.ContainerStatPath(ctx, copyConfig.container, client.ContainerStatPathOptions{
Path: srcPath,
})
// If the destination is a symbolic link, we should follow it.
if err == nil && srcStat.Mode&os.ModeSymlink != 0 {
linkTarget := srcStat.LinkTarget
if err == nil && src.Stat.Mode&os.ModeSymlink != 0 {
linkTarget := src.Stat.LinkTarget
if !isAbs(linkTarget) {
// Join with the parent directory.
srcParent, _ := archive.SplitPathDirEntry(srcPath)
@ -249,11 +251,14 @@ func copyFromContainer(ctx context.Context, dockerCLI command.Cli, copyConfig cp
ctx, cancel := signal.NotifyContext(ctx, os.Interrupt)
defer cancel()
content, stat, err := apiClient.CopyFromContainer(ctx, copyConfig.container, srcPath)
cpRes, err := apiClient.CopyFromContainer(ctx, copyConfig.container, client.CopyFromContainerOptions{
SourcePath: srcPath,
})
if err != nil {
return err
}
defer content.Close()
content := cpRes.Content
defer func() { _ = content.Close() }()
if dstPath == "-" {
_, err = io.Copy(dockerCLI.Out(), content)
@ -263,7 +268,7 @@ func copyFromContainer(ctx context.Context, dockerCLI command.Cli, copyConfig cp
srcInfo := archive.CopyInfo{
Path: srcPath,
Exists: true,
IsDir: stat.Mode.IsDir(),
IsDir: cpRes.Stat.Mode.IsDir(),
RebaseName: rebaseName,
}
@ -315,10 +320,10 @@ func copyToContainer(ctx context.Context, dockerCLI command.Cli, copyConfig cpCo
apiClient := dockerCLI.Client()
// Prepare destination copy info by stat-ing the container path.
dstInfo := archive.CopyInfo{Path: dstPath}
if dstStat, err := apiClient.ContainerStatPath(ctx, copyConfig.container, dstPath); err == nil {
if dst, err := apiClient.ContainerStatPath(ctx, copyConfig.container, client.ContainerStatPathOptions{Path: dstPath}); err == nil {
// If the destination is a symbolic link, we should evaluate it.
if dstStat.Mode&os.ModeSymlink != 0 {
linkTarget := dstStat.LinkTarget
if dst.Stat.Mode&os.ModeSymlink != 0 {
linkTarget := dst.Stat.LinkTarget
if !isAbs(linkTarget) {
// Join with the parent directory.
dstParent, _ := archive.SplitPathDirEntry(dstPath)
@ -326,14 +331,14 @@ func copyToContainer(ctx context.Context, dockerCLI command.Cli, copyConfig cpCo
}
dstInfo.Path = linkTarget
dstStat, err = apiClient.ContainerStatPath(ctx, copyConfig.container, linkTarget)
dst, err = apiClient.ContainerStatPath(ctx, copyConfig.container, client.ContainerStatPathOptions{Path: linkTarget})
}
// Validate the destination path
if err == nil {
if err := command.ValidateOutputPathFileMode(dstStat.Mode); err != nil {
if err := command.ValidateOutputPathFileMode(dst.Stat.Mode); err != nil {
return fmt.Errorf(`destination "%s:%s" must be a directory or a regular file: %w`, copyConfig.container, dstPath, err)
}
dstInfo.Exists, dstInfo.IsDir = true, dstStat.Mode.IsDir()
dstInfo.Exists, dstInfo.IsDir = true, dst.Stat.Mode.IsDir()
}
// Ignore any error and assume that the parent directory of the destination
@ -399,22 +404,26 @@ func copyToContainer(ctx context.Context, dockerCLI command.Cli, copyConfig cpCo
}
options := client.CopyToContainerOptions{
CopyUIDGID: copyConfig.copyUIDGID,
DestinationPath: resolvedDstPath,
Content: content,
CopyUIDGID: copyConfig.copyUIDGID,
}
if copyConfig.quiet {
return apiClient.CopyToContainer(ctx, copyConfig.container, resolvedDstPath, content, options)
_, err := apiClient.CopyToContainer(ctx, copyConfig.container, options)
return err
}
ctx, cancel := signal.NotifyContext(ctx, os.Interrupt)
restore, done := copyProgress(ctx, dockerCLI.Err(), copyToContainerHeader, &copiedSize)
res := apiClient.CopyToContainer(ctx, copyConfig.container, resolvedDstPath, content, options)
// TODO(thaJeztah): error-handling looks odd here; should it be handled differently?
_, err := apiClient.CopyToContainer(ctx, copyConfig.container, options)
cancel()
<-done
restore()
_, _ = fmt.Fprintln(dockerCLI.Err(), "Successfully copied", progressHumanSize(copiedSize), "to", copyConfig.container+":"+dstInfo.Path)
return res
return err
}
// We use `:` as a delimiter between CONTAINER and PATH, but `:` could also be

View File

@ -11,7 +11,7 @@ import (
"github.com/docker/cli/internal/test"
"github.com/moby/go-archive"
"github.com/moby/go-archive/compression"
"github.com/moby/moby/api/types/container"
"github.com/moby/moby/client"
"gotest.tools/v3/assert"
is "gotest.tools/v3/assert/cmp"
"gotest.tools/v3/fs"
@ -52,9 +52,11 @@ func TestRunCopyFromContainerToStdout(t *testing.T) {
tarContent := "the tar content"
cli := test.NewFakeCli(&fakeClient{
containerCopyFromFunc: func(ctr, srcPath string) (io.ReadCloser, container.PathStat, error) {
containerCopyFromFunc: func(ctr, srcPath string) (client.CopyFromContainerResult, error) {
assert.Check(t, is.Equal("container", ctr))
return io.NopCloser(strings.NewReader(tarContent)), container.PathStat{}, nil
return client.CopyFromContainerResult{
Content: io.NopCloser(strings.NewReader(tarContent)),
}, nil
},
})
err := runCopy(context.TODO(), cli, copyOptions{
@ -73,10 +75,12 @@ func TestRunCopyFromContainerToFilesystem(t *testing.T) {
destDir := fs.NewDir(t, "cp-test")
cli := test.NewFakeCli(&fakeClient{
containerCopyFromFunc: func(ctr, srcPath string) (io.ReadCloser, container.PathStat, error) {
containerCopyFromFunc: func(ctr, srcPath string) (client.CopyFromContainerResult, error) {
assert.Check(t, is.Equal("container", ctr))
readCloser, err := archive.Tar(srcDir.Path(), compression.None)
return readCloser, container.PathStat{}, err
return client.CopyFromContainerResult{
Content: readCloser,
}, err
},
})
err := runCopy(context.TODO(), cli, copyOptions{
@ -99,10 +103,12 @@ func TestRunCopyFromContainerToFilesystemMissingDestinationDirectory(t *testing.
defer destDir.Remove()
cli := test.NewFakeCli(&fakeClient{
containerCopyFromFunc: func(ctr, srcPath string) (io.ReadCloser, container.PathStat, error) {
containerCopyFromFunc: func(ctr, srcPath string) (client.CopyFromContainerResult, error) {
assert.Check(t, is.Equal("container", ctr))
readCloser, err := archive.TarWithOptions(destDir.Path(), &archive.TarOptions{})
return readCloser, container.PathStat{}, err
return client.CopyFromContainerResult{
Content: readCloser,
}, err
},
})
err := runCopy(context.TODO(), cli, copyOptions{

View File

@ -410,7 +410,7 @@ func validatePullOpt(val string) error {
//
// The path should be an absolute path in the container, commonly
// /root/.docker/config.json.
func copyDockerConfigIntoContainer(ctx context.Context, dockerAPI client.APIClient, containerID string, configPath string, config *configfile.ConfigFile) error {
func copyDockerConfigIntoContainer(ctx context.Context, apiClient client.APIClient, containerID string, configPath string, config *configfile.ConfigFile) error {
var configBuf bytes.Buffer
if err := config.SaveToWriter(&configBuf); err != nil {
return fmt.Errorf("saving creds: %w", err)
@ -433,8 +433,11 @@ func copyDockerConfigIntoContainer(ctx context.Context, dockerAPI client.APIClie
return fmt.Errorf("closing tar for config copy failed: %w", err)
}
if err := dockerAPI.CopyToContainer(ctx, containerID, "/",
&tarBuf, client.CopyToContainerOptions{}); err != nil {
_, err := apiClient.CopyToContainer(ctx, containerID, client.CopyToContainerOptions{
DestinationPath: "/",
Content: &tarBuf,
})
if err != nil {
return fmt.Errorf("copying config.json into container failed: %w", err)
}

View File

@ -128,8 +128,10 @@ func TestCreateContainerImagePullPolicy(t *testing.T) {
defer func() { pullCounter++ }()
return client.ImageCreateResult{Body: io.NopCloser(strings.NewReader(""))}, nil
},
infoFunc: func() (system.Info, error) {
return system.Info{IndexServerAddress: "https://indexserver.example.com"}, nil
infoFunc: func() (client.SystemInfoResult, error) {
return client.SystemInfoResult{
Info: system.Info{IndexServerAddress: "https://indexserver.example.com"},
}, nil
},
}
fakeCLI := test.NewFakeCli(apiClient)

View File

@ -9,6 +9,7 @@ import (
"github.com/docker/cli/cli"
"github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/command/completion"
"github.com/moby/moby/client"
"github.com/moby/sys/atomicwriter"
"github.com/spf13/cobra"
)
@ -60,7 +61,7 @@ func runExport(ctx context.Context, dockerCLI command.Cli, opts exportOptions) e
output = writer
}
responseBody, err := dockerCLI.Client().ContainerExport(ctx, opts.container)
responseBody, err := dockerCLI.Client().ContainerExport(ctx, opts.container, client.ContainerExportOptions{})
if err != nil {
return err
}

View File

@ -2,10 +2,10 @@ package container
import (
"io"
"strings"
"testing"
"github.com/docker/cli/internal/test"
"github.com/moby/moby/client"
"gotest.tools/v3/assert"
"gotest.tools/v3/fs"
)
@ -15,8 +15,9 @@ func TestContainerExportOutputToFile(t *testing.T) {
defer dir.Remove()
cli := test.NewFakeCli(&fakeClient{
containerExportFunc: func(container string) (io.ReadCloser, error) {
return io.NopCloser(strings.NewReader("bar")), nil
containerExportFunc: func(container string) (client.ContainerExportResult, error) {
// FIXME(thaJeztah): how to mock this?
return mockContainerExportResult("bar"), nil
},
})
cmd := newExportCommand(cli)
@ -33,8 +34,9 @@ func TestContainerExportOutputToFile(t *testing.T) {
func TestContainerExportOutputToIrregularFile(t *testing.T) {
cli := test.NewFakeCli(&fakeClient{
containerExportFunc: func(container string) (io.ReadCloser, error) {
return io.NopCloser(strings.NewReader("foo")), nil
containerExportFunc: func(container string) (client.ContainerExportResult, error) {
// FIXME(thaJeztah): how to mock this?
return mockContainerExportResult("foo"), nil
},
})
cmd := newExportCommand(cli)

View File

@ -68,8 +68,8 @@ func newListCommand(dockerCLI command.Cli) *cobra.Command {
return &cmd
}
func buildContainerListOptions(options *psOptions) (*client.ContainerListOptions, error) {
listOptions := &client.ContainerListOptions{
func buildContainerListOptions(options *psOptions) (client.ContainerListOptions, error) {
listOptions := client.ContainerListOptions{
All: options.all,
Limit: options.last,
Size: options.size,
@ -84,7 +84,7 @@ func buildContainerListOptions(options *psOptions) (*client.ContainerListOptions
if len(options.format) > 0 {
tmpl, err := templates.Parse(options.format)
if err != nil {
return nil, fmt.Errorf("failed to parse template: %w", err)
return client.ContainerListOptions{}, fmt.Errorf("failed to parse template: %w", err)
}
optionsProcessor := formatter.NewContainerContext()
@ -92,7 +92,7 @@ func buildContainerListOptions(options *psOptions) (*client.ContainerListOptions
// This shouldn't error out but swallowing the error makes it harder
// to track down if preProcessor issues come up.
if err := tmpl.Execute(io.Discard, optionsProcessor); err != nil {
return nil, fmt.Errorf("failed to execute template: %w", err)
return client.ContainerListOptions{}, fmt.Errorf("failed to execute template: %w", err)
}
// if `size` was not explicitly set to false (with `--size=false`)
@ -127,7 +127,7 @@ func runPs(ctx context.Context, dockerCLI command.Cli, options *psOptions) error
return err
}
containers, err := dockerCLI.Client().ContainerList(ctx, *listOptions)
res, err := dockerCLI.Client().ContainerList(ctx, listOptions)
if err != nil {
return err
}
@ -137,5 +137,5 @@ func runPs(ctx context.Context, dockerCLI command.Cli, options *psOptions) error
Format: formatter.NewContainerFormat(options.format, options.quiet, listOptions.Size),
Trunc: !options.noTrunc,
}
return formatter.ContainerWrite(containerCtx, containers)
return formatter.ContainerWrite(containerCtx, res.Items)
}

View File

@ -109,7 +109,7 @@ func TestContainerListBuildContainerListOptions(t *testing.T) {
func TestContainerListErrors(t *testing.T) {
testCases := []struct {
flags map[string]string
containerListFunc func(client.ContainerListOptions) ([]container.Summary, error)
containerListFunc func(client.ContainerListOptions) (client.ContainerListResult, error)
expectedError string
}{
{
@ -125,8 +125,8 @@ func TestContainerListErrors(t *testing.T) {
expectedError: `wrong number of args for join`,
},
{
containerListFunc: func(_ client.ContainerListOptions) ([]container.Summary, error) {
return nil, errors.New("error listing containers")
containerListFunc: func(_ client.ContainerListOptions) (client.ContainerListResult, error) {
return client.ContainerListResult{}, errors.New("error listing containers")
},
expectedError: "error listing containers",
},
@ -149,13 +149,15 @@ func TestContainerListErrors(t *testing.T) {
func TestContainerListWithoutFormat(t *testing.T) {
cli := test.NewFakeCli(&fakeClient{
containerListFunc: func(_ client.ContainerListOptions) ([]container.Summary, error) {
return []container.Summary{
*builders.Container("c1"),
*builders.Container("c2", builders.WithName("foo")),
*builders.Container("c3", builders.WithPort(80, 80, builders.TCP), builders.WithPort(81, 81, builders.TCP), builders.WithPort(82, 82, builders.TCP)),
*builders.Container("c4", builders.WithPort(81, 81, builders.UDP)),
*builders.Container("c5", builders.WithPort(82, 82, builders.IP("8.8.8.8"), builders.TCP)),
containerListFunc: func(_ client.ContainerListOptions) (client.ContainerListResult, error) {
return client.ContainerListResult{
Items: []container.Summary{
*builders.Container("c1"),
*builders.Container("c2", builders.WithName("foo")),
*builders.Container("c3", builders.WithPort(80, 80, builders.TCP), builders.WithPort(81, 81, builders.TCP), builders.WithPort(82, 82, builders.TCP)),
*builders.Container("c4", builders.WithPort(81, 81, builders.UDP)),
*builders.Container("c5", builders.WithPort(82, 82, builders.IP("8.8.8.8"), builders.TCP)),
},
}, nil
},
})
@ -169,10 +171,12 @@ func TestContainerListWithoutFormat(t *testing.T) {
func TestContainerListNoTrunc(t *testing.T) {
cli := test.NewFakeCli(&fakeClient{
containerListFunc: func(_ client.ContainerListOptions) ([]container.Summary, error) {
return []container.Summary{
*builders.Container("c1"),
*builders.Container("c2", builders.WithName("foo/bar")),
containerListFunc: func(_ client.ContainerListOptions) (client.ContainerListResult, error) {
return client.ContainerListResult{
Items: []container.Summary{
*builders.Container("c1"),
*builders.Container("c2", builders.WithName("foo/bar")),
},
}, nil
},
})
@ -188,10 +192,12 @@ func TestContainerListNoTrunc(t *testing.T) {
// Test for GitHub issue docker/docker#21772
func TestContainerListNamesMultipleTime(t *testing.T) {
cli := test.NewFakeCli(&fakeClient{
containerListFunc: func(_ client.ContainerListOptions) ([]container.Summary, error) {
return []container.Summary{
*builders.Container("c1"),
*builders.Container("c2", builders.WithName("foo/bar")),
containerListFunc: func(_ client.ContainerListOptions) (client.ContainerListResult, error) {
return client.ContainerListResult{
Items: []container.Summary{
*builders.Container("c1"),
*builders.Container("c2", builders.WithName("foo/bar")),
},
}, nil
},
})
@ -207,10 +213,12 @@ func TestContainerListNamesMultipleTime(t *testing.T) {
// Test for GitHub issue docker/docker#30291
func TestContainerListFormatTemplateWithArg(t *testing.T) {
cli := test.NewFakeCli(&fakeClient{
containerListFunc: func(_ client.ContainerListOptions) ([]container.Summary, error) {
return []container.Summary{
*builders.Container("c1", builders.WithLabel("some.label", "value")),
*builders.Container("c2", builders.WithName("foo/bar"), builders.WithLabel("foo", "bar")),
containerListFunc: func(_ client.ContainerListOptions) (client.ContainerListResult, error) {
return client.ContainerListResult{
Items: []container.Summary{
*builders.Container("c1", builders.WithLabel("some.label", "value")),
*builders.Container("c2", builders.WithName("foo/bar"), builders.WithLabel("foo", "bar")),
},
}, nil
},
})
@ -260,9 +268,9 @@ func TestContainerListFormatSizeSetsOption(t *testing.T) {
for _, tc := range tests {
t.Run(tc.doc, func(t *testing.T) {
cli := test.NewFakeCli(&fakeClient{
containerListFunc: func(options client.ContainerListOptions) ([]container.Summary, error) {
containerListFunc: func(options client.ContainerListOptions) (client.ContainerListResult, error) {
assert.Check(t, is.Equal(options.Size, tc.sizeExpected))
return []container.Summary{}, nil
return client.ContainerListResult{}, nil
},
})
cmd := newListCommand(cli)
@ -280,10 +288,12 @@ func TestContainerListFormatSizeSetsOption(t *testing.T) {
func TestContainerListWithConfigFormat(t *testing.T) {
cli := test.NewFakeCli(&fakeClient{
containerListFunc: func(_ client.ContainerListOptions) ([]container.Summary, error) {
return []container.Summary{
*builders.Container("c1", builders.WithLabel("some.label", "value"), builders.WithSize(10700000)),
*builders.Container("c2", builders.WithName("foo/bar"), builders.WithLabel("foo", "bar"), builders.WithSize(3200000)),
containerListFunc: func(_ client.ContainerListOptions) (client.ContainerListResult, error) {
return client.ContainerListResult{
Items: []container.Summary{
*builders.Container("c1", builders.WithLabel("some.label", "value"), builders.WithSize(10700000)),
*builders.Container("c2", builders.WithName("foo/bar"), builders.WithLabel("foo", "bar"), builders.WithSize(3200000)),
},
}, nil
},
})
@ -300,10 +310,12 @@ func TestContainerListWithConfigFormat(t *testing.T) {
func TestContainerListWithFormat(t *testing.T) {
cli := test.NewFakeCli(&fakeClient{
containerListFunc: func(_ client.ContainerListOptions) ([]container.Summary, error) {
return []container.Summary{
*builders.Container("c1", builders.WithLabel("some.label", "value")),
*builders.Container("c2", builders.WithName("foo/bar"), builders.WithLabel("foo", "bar")),
containerListFunc: func(_ client.ContainerListOptions) (client.ContainerListResult, error) {
return client.ContainerListResult{
Items: []container.Summary{
*builders.Container("c1", builders.WithLabel("some.label", "value")),
*builders.Container("c2", builders.WithName("foo/bar"), builders.WithLabel("foo", "bar")),
},
}, nil
},
})

View File

@ -59,7 +59,7 @@ func runLogs(ctx context.Context, dockerCli command.Cli, opts *logsOptions) erro
return err
}
responseBody, err := dockerCli.Client().ContainerLogs(ctx, c.Container.ID, client.ContainerLogsOptions{
resp, err := dockerCli.Client().ContainerLogs(ctx, c.Container.ID, client.ContainerLogsOptions{
ShowStdout: true,
ShowStderr: true,
Since: opts.since,
@ -72,12 +72,12 @@ func runLogs(ctx context.Context, dockerCli command.Cli, opts *logsOptions) erro
if err != nil {
return err
}
defer responseBody.Close()
defer func() { _ = resp.Close() }()
if c.Container.Config.Tty {
_, err = io.Copy(dockerCli.Out(), responseBody)
_, err = io.Copy(dockerCli.Out(), resp)
} else {
_, err = stdcopy.StdCopy(dockerCli.Out(), dockerCli.Err(), responseBody)
_, err = stdcopy.StdCopy(dockerCli.Out(), dockerCli.Err(), resp)
}
return err
}

View File

@ -2,8 +2,6 @@ package container
import (
"context"
"io"
"strings"
"testing"
"github.com/docker/cli/internal/test"
@ -13,12 +11,6 @@ import (
is "gotest.tools/v3/assert/cmp"
)
var logFn = func(expectedOut string) func(string, client.ContainerLogsOptions) (io.ReadCloser, error) {
return func(container string, opts client.ContainerLogsOptions) (io.ReadCloser, error) {
return io.NopCloser(strings.NewReader(expectedOut)), nil
}
}
func TestRunLogs(t *testing.T) {
inspectFn := func(containerID string) (client.ContainerInspectResult, error) {
return client.ContainerInspectResult{
@ -41,7 +33,13 @@ func TestRunLogs(t *testing.T) {
doc: "successful logs",
expectedOut: "foo",
options: &logsOptions{},
client: &fakeClient{logFunc: logFn("foo"), inspectFunc: inspectFn},
client: &fakeClient{
logFunc: func(container string, opts client.ContainerLogsOptions) (client.ContainerLogsResult, error) {
// FIXME(thaJeztah): how to mock this?
return mockContainerLogsResult("foo"), nil
},
inspectFunc: inspectFn,
},
},
}

View File

@ -1,14 +1,13 @@
package container
import (
"context"
"errors"
"fmt"
"strings"
"github.com/docker/cli/cli"
"github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/command/completion"
"github.com/moby/moby/client"
"github.com/spf13/cobra"
)
@ -19,7 +18,18 @@ func newRenameCommand(dockerCLI command.Cli) *cobra.Command {
Short: "Rename a container",
Args: cli.ExactArgs(2),
RunE: func(cmd *cobra.Command, args []string) error {
return runRename(cmd.Context(), dockerCLI, args[0], args[1])
oldName, newName := args[0], args[1]
if newName == "" {
// TODO(thaJeztah): remove once https://github.com/moby/moby/pull/51336 is merged and vendored.
return errors.New("new name cannot be blank")
}
_, err := dockerCLI.Client().ContainerRename(cmd.Context(), oldName, client.ContainerRenameOptions{
NewName: newName,
})
if err != nil {
return fmt.Errorf("failed to rename container: %w", err)
}
return nil
},
Annotations: map[string]string{
"aliases": "docker container rename, docker rename",
@ -29,16 +39,3 @@ func newRenameCommand(dockerCLI command.Cli) *cobra.Command {
}
return cmd
}
func runRename(ctx context.Context, dockerCLI command.Cli, oldName, newName string) error {
newName = strings.TrimSpace(newName)
if newName == "" {
// TODO(thaJeztah): improve validation in ContainerRename and daemon; the daemon returns an obscure error when providing whitespace-only new-name:
// Error response from daemon: Error when allocating new name: Invalid container name (/ ), only [a-zA-Z0-9][a-zA-Z0-9_.-] are allowed
return errors.New("new name cannot be blank")
}
if err := dockerCLI.Client().ContainerRename(ctx, oldName, newName); err != nil {
return fmt.Errorf("failed to rename container: %w", err)
}
return nil
}

View File

@ -89,14 +89,17 @@ func TestRunAttach(t *testing.T) {
HijackedResponse: client.NewHijackedResponse(clientConn, types.MediaTypeRawStream),
}, nil
},
waitFunc: func(_ string) (<-chan container.WaitResponse, <-chan error) {
waitFunc: func(_ string) client.ContainerWaitResult {
responseChan := make(chan container.WaitResponse, 1)
errChan := make(chan error)
responseChan <- container.WaitResponse{
StatusCode: 33,
}
return responseChan, errChan
return client.ContainerWaitResult{
Result: responseChan,
Error: errChan,
}
},
// use new (non-legacy) wait API
// see: https://github.com/docker/cli/commit/38591f20d07795aaef45d400df89ca12f29c603b
@ -166,14 +169,17 @@ func TestRunAttachTermination(t *testing.T) {
HijackedResponse: client.NewHijackedResponse(clientConn, types.MediaTypeRawStream),
}, nil
},
waitFunc: func(_ string) (<-chan container.WaitResponse, <-chan error) {
waitFunc: func(_ string) client.ContainerWaitResult {
responseChan := make(chan container.WaitResponse, 1)
errChan := make(chan error)
<-killCh
responseChan <- container.WaitResponse{
StatusCode: 130,
}
return responseChan, errChan
return client.ContainerWaitResult{
Result: responseChan,
Error: errChan,
}
},
// use new (non-legacy) wait API
// see: https://github.com/docker/cli/commit/38591f20d07795aaef45d400df89ca12f29c603b

View File

@ -183,7 +183,7 @@ func RunStats(ctx context.Context, dockerCLI command.Cli, options *StatsOptions)
// to list containers and to filter events, but the "type" filter
// is not valid for filtering containers.
f := options.Filters.Clone().Add("type", string(events.ContainerEventType))
eventChan, errChan := apiClient.Events(ctx, client.EventsListOptions{
res := apiClient.Events(ctx, client.EventsListOptions{
Filters: f,
})
@ -198,9 +198,9 @@ func RunStats(ctx context.Context, dockerCLI command.Cli, options *StatsOptions)
return
case <-ctx.Done():
return
case event := <-eventChan:
case event := <-res.Messages:
c <- event
case err := <-errChan:
case err := <-res.Err:
// Prevent blocking if closeChan is full or unread
select {
case closeChan <- err:
@ -229,7 +229,7 @@ func RunStats(ctx context.Context, dockerCLI command.Cli, options *StatsOptions)
if err != nil {
return err
}
for _, ctr := range cs {
for _, ctr := range cs.Items {
if s := NewStats(ctr.ID); cStats.add(s) {
waitFirst.Add(1)
log.G(ctx).WithFields(map[string]any{

View File

@ -9,6 +9,7 @@ import (
"github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/command/completion"
"github.com/docker/cli/cli/command/formatter/tabwriter"
"github.com/moby/moby/client"
"github.com/spf13/cobra"
)
@ -45,17 +46,19 @@ func newTopCommand(dockerCLI command.Cli) *cobra.Command {
}
func runTop(ctx context.Context, dockerCli command.Cli, opts *topOptions) error {
procList, err := dockerCli.Client().ContainerTop(ctx, opts.container, opts.args)
procList, err := dockerCli.Client().ContainerTop(ctx, opts.container, client.ContainerTopOptions{
Arguments: opts.args,
})
if err != nil {
return err
}
w := tabwriter.NewWriter(dockerCli.Out(), 20, 1, 3, ' ', 0)
fmt.Fprintln(w, strings.Join(procList.Titles, "\t"))
_, _ = fmt.Fprintln(w, strings.Join(procList.Titles, "\t"))
for _, proc := range procList.Processes {
fmt.Fprintln(w, strings.Join(proc, "\t"))
_, _ = fmt.Fprintln(w, strings.Join(proc, "\t"))
}
w.Flush()
_ = w.Flush()
return nil
}

View File

@ -43,7 +43,7 @@ func newUnpauseCommand(dockerCLI command.Cli) *cobra.Command {
func runUnpause(ctx context.Context, dockerCLI command.Cli, opts *unpauseOptions) error {
apiClient := dockerCLI.Client()
errChan := parallelOperation(ctx, opts.containers, func(ctx context.Context, container string) error {
_, err := apiClient.ContainerUnpause(ctx, container, client.ContainerUnPauseOptions{})
_, err := apiClient.ContainerUnpause(ctx, container, client.ContainerUnpauseOptions{})
return err
})
var errs []error

View File

@ -11,6 +11,7 @@ import (
"github.com/docker/cli/cli/command/completion"
"github.com/docker/cli/opts"
containertypes "github.com/moby/moby/api/types/container"
"github.com/moby/moby/client"
"github.com/spf13/cobra"
)
@ -60,9 +61,9 @@ func newUpdateCommand(dockerCLI command.Cli) *cobra.Command {
flags.Int64Var(&options.cpuPeriod, "cpu-period", 0, "Limit CPU CFS (Completely Fair Scheduler) period")
flags.Int64Var(&options.cpuQuota, "cpu-quota", 0, "Limit CPU CFS (Completely Fair Scheduler) quota")
flags.Int64Var(&options.cpuRealtimePeriod, "cpu-rt-period", 0, "Limit the CPU real-time period in microseconds")
flags.SetAnnotation("cpu-rt-period", "version", []string{"1.25"})
_ = flags.SetAnnotation("cpu-rt-period", "version", []string{"1.25"})
flags.Int64Var(&options.cpuRealtimeRuntime, "cpu-rt-runtime", 0, "Limit the CPU real-time runtime in microseconds")
flags.SetAnnotation("cpu-rt-runtime", "version", []string{"1.25"})
_ = flags.SetAnnotation("cpu-rt-runtime", "version", []string{"1.25"})
flags.StringVar(&options.cpusetCpus, "cpuset-cpus", "", "CPUs in which to allow execution (0-3, 0,1)")
flags.StringVar(&options.cpusetMems, "cpuset-mems", "", "MEMs in which to allow execution (0-3, 0,1)")
flags.Int64VarP(&options.cpuShares, "cpu-shares", "c", 0, "CPU shares (relative weight)")
@ -72,10 +73,10 @@ func newUpdateCommand(dockerCLI command.Cli) *cobra.Command {
flags.StringVar(&options.restartPolicy, "restart", "", "Restart policy to apply when a container exits")
flags.Int64Var(&options.pidsLimit, "pids-limit", 0, `Tune container pids limit (set -1 for unlimited)`)
flags.SetAnnotation("pids-limit", "version", []string{"1.40"})
_ = flags.SetAnnotation("pids-limit", "version", []string{"1.40"})
flags.Var(&options.cpus, "cpus", "Number of CPUs")
flags.SetAnnotation("cpus", "version", []string{"1.29"})
_ = flags.SetAnnotation("cpus", "version", []string{"1.29"})
_ = cmd.RegisterFlagCompletionFunc("restart", completeRestartPolicies)
@ -107,8 +108,8 @@ func runUpdate(ctx context.Context, dockerCli command.Cli, options *updateOption
pidsLimit = &options.pidsLimit
}
updateConfig := containertypes.UpdateConfig{
Resources: containertypes.Resources{
updateConfig := client.ContainerUpdateOptions{
Resources: &containertypes.Resources{
BlkioWeight: options.blkioWeight,
CpusetCpus: options.cpusetCpus,
CpusetMems: options.cpusetMems,
@ -123,7 +124,7 @@ func runUpdate(ctx context.Context, dockerCli command.Cli, options *updateOption
NanoCPUs: options.cpus.Value(),
PidsLimit: pidsLimit,
},
RestartPolicy: restartPolicy,
RestartPolicy: &restartPolicy,
}
var (

View File

@ -20,7 +20,9 @@ func waitExitOrRemoved(ctx context.Context, apiClient client.APIClient, containe
condition = container.WaitConditionRemoved
}
resultC, errC := apiClient.ContainerWait(ctx, containerID, condition)
waitRes := apiClient.ContainerWait(ctx, containerID, client.ContainerWaitOptions{
Condition: condition,
})
statusC := make(chan int)
go func() {
@ -28,14 +30,14 @@ func waitExitOrRemoved(ctx context.Context, apiClient client.APIClient, containe
select {
case <-ctx.Done():
return
case result := <-resultC:
case result := <-waitRes.Result:
if result.Error != nil {
logrus.Errorf("Error waiting for container: %v", result.Error.Message)
statusC <- 125
} else {
statusC <- int(result.StatusCode)
}
case err := <-errC:
case err := <-waitRes.Error:
if errors.Is(err, context.Canceled) {
return
}

View File

@ -12,7 +12,7 @@ import (
is "gotest.tools/v3/assert/cmp"
)
func waitFn(cid string) (<-chan container.WaitResponse, <-chan error) {
func waitFn(cid string) client.ContainerWaitResult {
resC := make(chan container.WaitResponse)
errC := make(chan error, 1)
var res container.WaitResponse
@ -33,8 +33,10 @@ func waitFn(cid string) (<-chan container.WaitResponse, <-chan error) {
resC <- res
}
}()
return resC, errC
return client.ContainerWaitResult{
Result: resC,
Error: errC,
}
}
func TestWaitExitOrRemoved(t *testing.T) {

View File

@ -4,10 +4,12 @@ import (
"context"
"errors"
"fmt"
"strconv"
"github.com/docker/cli/cli"
"github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/command/completion"
"github.com/moby/moby/client"
"github.com/spf13/cobra"
)
@ -42,12 +44,12 @@ func runWait(ctx context.Context, dockerCLI command.Cli, opts *waitOptions) erro
var errs []error
for _, ctr := range opts.containers {
resultC, errC := apiClient.ContainerWait(ctx, ctr, "")
res := apiClient.ContainerWait(ctx, ctr, client.ContainerWaitOptions{})
select {
case result := <-resultC:
_, _ = fmt.Fprintf(dockerCLI.Out(), "%d\n", result.StatusCode)
case err := <-errC:
case result := <-res.Result:
_, _ = fmt.Fprintln(dockerCLI.Out(), strconv.FormatInt(result.StatusCode, 10))
case err := <-res.Error:
errs = append(errs, err)
}
}