1
0
mirror of https://github.com/docker/cli.git synced 2026-01-13 18:22:35 +03:00

Merge pull request #6584 from thaJeztah/bump_modules

vendor: github.com/moby/moby/api, moby/moby/client master
This commit is contained in:
Sebastiaan van Stijn
2025-10-28 19:26:27 +01:00
committed by GitHub
46 changed files with 406 additions and 249 deletions

View File

@@ -15,7 +15,7 @@ type fakeClient struct {
execInspectFunc func(execID string) (client.ExecInspectResult, error)
execCreateFunc func(containerID string, options client.ExecCreateOptions) (client.ExecCreateResult, error)
createContainerFunc func(options client.ContainerCreateOptions) (client.ContainerCreateResult, error)
containerStartFunc func(containerID string, options client.ContainerStartOptions) 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)
@@ -25,16 +25,16 @@ type fakeClient struct {
containerListFunc func(client.ContainerListOptions) ([]container.Summary, error)
containerExportFunc func(string) (io.ReadCloser, error)
containerExecResizeFunc func(id string, options client.ExecResizeOptions) (client.ExecResizeResult, error)
containerRemoveFunc func(ctx context.Context, containerID string, options client.ContainerRemoveOptions) error
containerRestartFunc func(ctx context.Context, containerID string, options client.ContainerStopOptions) error
containerStopFunc func(ctx context.Context, containerID string, options client.ContainerStopOptions) error
containerKillFunc func(ctx context.Context, containerID, signal string) 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)
containerStopFunc func(ctx context.Context, containerID string, options client.ContainerStopOptions) (client.ContainerStopResult, error)
containerKillFunc func(ctx context.Context, containerID string, options client.ContainerKillOptions) (client.ContainerKillResult, error)
containerPruneFunc func(ctx context.Context, options client.ContainerPruneOptions) (client.ContainerPruneResult, error)
containerAttachFunc func(ctx context.Context, containerID string, options client.ContainerAttachOptions) (client.ContainerAttachResult, error)
containerDiffFunc func(ctx context.Context, containerID string) (client.ContainerDiffResult, error)
containerRenameFunc func(ctx context.Context, oldName, newName string) error
containerCommitFunc func(ctx context.Context, container string, options client.ContainerCommitOptions) (client.ContainerCommitResult, error)
containerPauseFunc func(ctx context.Context, container string) error
containerPauseFunc func(ctx context.Context, container string, options client.ContainerPauseOptions) (client.ContainerPauseResult, error)
Version string
}
@@ -77,11 +77,11 @@ func (f *fakeClient) ContainerCreate(_ context.Context, options client.Container
return client.ContainerCreateResult{}, nil
}
func (f *fakeClient) ContainerRemove(ctx context.Context, containerID string, options client.ContainerRemoveOptions) error {
func (f *fakeClient) ContainerRemove(ctx context.Context, containerID string, options client.ContainerRemoveOptions) (client.ContainerRemoveResult, error) {
if f.containerRemoveFunc != nil {
return f.containerRemoveFunc(ctx, containerID, options)
}
return nil
return client.ContainerRemoveResult{}, nil
}
func (f *fakeClient) ImageCreate(ctx context.Context, parentReference string, options client.ImageCreateOptions) (client.ImageCreateResult, error) {
@@ -130,11 +130,11 @@ func (f *fakeClient) ContainerWait(_ context.Context, containerID string, _ cont
return nil, nil
}
func (f *fakeClient) ContainerStart(_ context.Context, containerID string, options client.ContainerStartOptions) error {
func (f *fakeClient) ContainerStart(_ context.Context, containerID string, options client.ContainerStartOptions) (client.ContainerStartResult, error) {
if f.containerStartFunc != nil {
return f.containerStartFunc(containerID, options)
}
return nil
return client.ContainerStartResult{}, nil
}
func (f *fakeClient) ContainerExport(_ context.Context, containerID string) (io.ReadCloser, error) {
@@ -151,11 +151,11 @@ func (f *fakeClient) ExecResize(_ context.Context, id string, options client.Exe
return client.ExecResizeResult{}, nil
}
func (f *fakeClient) ContainerKill(ctx context.Context, containerID, signal string) error {
func (f *fakeClient) ContainerKill(ctx context.Context, containerID string, options client.ContainerKillOptions) (client.ContainerKillResult, error) {
if f.containerKillFunc != nil {
return f.containerKillFunc(ctx, containerID, signal)
return f.containerKillFunc(ctx, containerID, options)
}
return nil
return client.ContainerKillResult{}, nil
}
func (f *fakeClient) ContainersPrune(ctx context.Context, options client.ContainerPruneOptions) (client.ContainerPruneResult, error) {
@@ -165,18 +165,18 @@ func (f *fakeClient) ContainersPrune(ctx context.Context, options client.Contain
return client.ContainerPruneResult{}, nil
}
func (f *fakeClient) ContainerRestart(ctx context.Context, containerID string, options client.ContainerStopOptions) error {
func (f *fakeClient) ContainerRestart(ctx context.Context, containerID string, options client.ContainerRestartOptions) (client.ContainerRestartResult, error) {
if f.containerRestartFunc != nil {
return f.containerRestartFunc(ctx, containerID, options)
}
return nil
return client.ContainerRestartResult{}, nil
}
func (f *fakeClient) ContainerStop(ctx context.Context, containerID string, options client.ContainerStopOptions) error {
func (f *fakeClient) ContainerStop(ctx context.Context, containerID string, options client.ContainerStopOptions) (client.ContainerStopResult, error) {
if f.containerStopFunc != nil {
return f.containerStopFunc(ctx, containerID, options)
}
return nil
return client.ContainerStopResult{}, nil
}
func (f *fakeClient) ContainerAttach(ctx context.Context, containerID string, options client.ContainerAttachOptions) (client.ContainerAttachResult, error) {
@@ -209,10 +209,10 @@ func (f *fakeClient) ContainerCommit(ctx context.Context, containerID string, op
return client.ContainerCommitResult{}, nil
}
func (f *fakeClient) ContainerPause(ctx context.Context, containerID string) error {
func (f *fakeClient) ContainerPause(ctx context.Context, containerID string, options client.ContainerPauseOptions) (client.ContainerPauseResult, error) {
if f.containerPauseFunc != nil {
return f.containerPauseFunc(ctx, containerID)
return f.containerPauseFunc(ctx, containerID, options)
}
return nil
return client.ContainerPauseResult{}, nil
}

View File

@@ -8,6 +8,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/spf13/cobra"
)
@@ -47,7 +48,10 @@ func newKillCommand(dockerCLI command.Cli) *cobra.Command {
func runKill(ctx context.Context, dockerCLI command.Cli, opts *killOptions) error {
apiClient := dockerCLI.Client()
errChan := parallelOperation(ctx, opts.containers, func(ctx context.Context, container string) error {
return apiClient.ContainerKill(ctx, container, opts.signal)
_, err := apiClient.ContainerKill(ctx, container, client.ContainerKillOptions{
Signal: opts.signal,
})
return err
})
var errs []error

View File

@@ -8,19 +8,16 @@ import (
"testing"
"github.com/docker/cli/internal/test"
"github.com/moby/moby/client"
"gotest.tools/v3/assert"
is "gotest.tools/v3/assert/cmp"
)
func TestRunKill(t *testing.T) {
cli := test.NewFakeCli(&fakeClient{
containerKillFunc: func(
ctx context.Context,
container string,
signal string,
) error {
assert.Assert(t, is.Equal(signal, "STOP"))
return nil
containerKillFunc: func(ctx context.Context, container string, options client.ContainerKillOptions) (client.ContainerKillResult, error) {
assert.Assert(t, is.Equal(options.Signal, "STOP"))
return client.ContainerKillResult{}, nil
},
})
@@ -47,12 +44,8 @@ func TestRunKill(t *testing.T) {
func TestRunKillClientError(t *testing.T) {
cli := test.NewFakeCli(&fakeClient{
containerKillFunc: func(
ctx context.Context,
container string,
signal string,
) error {
return fmt.Errorf("client error for container %s", container)
containerKillFunc: func(ctx context.Context, container string, options client.ContainerKillOptions) (client.ContainerKillResult, error) {
return client.ContainerKillResult{}, fmt.Errorf("client error for container %s", container)
},
})

View File

@@ -9,6 +9,7 @@ import (
"github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/command/completion"
"github.com/moby/moby/api/types/container"
"github.com/moby/moby/client"
"github.com/spf13/cobra"
)
@@ -40,7 +41,10 @@ func newPauseCommand(dockerCLI command.Cli) *cobra.Command {
func runPause(ctx context.Context, dockerCLI command.Cli, opts *pauseOptions) error {
apiClient := dockerCLI.Client()
errChan := parallelOperation(ctx, opts.containers, apiClient.ContainerPause)
errChan := parallelOperation(ctx, opts.containers, func(ctx context.Context, container string) error {
_, err := apiClient.ContainerPause(ctx, container, client.ContainerPauseOptions{})
return err
})
var errs []error
for _, ctr := range opts.containers {

View File

@@ -8,18 +8,13 @@ import (
"testing"
"github.com/docker/cli/internal/test"
"github.com/moby/moby/client"
"gotest.tools/v3/assert"
is "gotest.tools/v3/assert/cmp"
)
func TestRunPause(t *testing.T) {
cli := test.NewFakeCli(
&fakeClient{
containerPauseFunc: func(ctx context.Context, container string) error {
return nil
},
},
)
cli := test.NewFakeCli(&fakeClient{})
cmd := newPauseCommand(cli)
cmd.SetOut(io.Discard)
@@ -41,8 +36,8 @@ func TestRunPause(t *testing.T) {
func TestRunPauseClientError(t *testing.T) {
cli := test.NewFakeCli(
&fakeClient{
containerPauseFunc: func(ctx context.Context, container string) error {
return fmt.Errorf("client error for container %s", container)
containerPauseFunc: func(ctx context.Context, container string, options client.ContainerPauseOptions) (client.ContainerPauseResult, error) {
return client.ContainerPauseResult{}, fmt.Errorf("client error for container %s", container)
},
},
)

View File

@@ -66,7 +66,7 @@ func runRestart(ctx context.Context, dockerCLI command.Cli, opts *restartOptions
var errs []error
// TODO(thaJeztah): consider using parallelOperation for restart, similar to "stop" and "remove"
for _, name := range opts.containers {
err := apiClient.ContainerRestart(ctx, name, client.ContainerStopOptions{
_, err := apiClient.ContainerRestart(ctx, name, client.ContainerRestartOptions{
Signal: opts.signal,
Timeout: timeout,
})

View File

@@ -19,7 +19,7 @@ func TestRestart(t *testing.T) {
name string
args []string
restarted []string
expectedOpts client.ContainerStopOptions
expectedOpts client.ContainerRestartOptions
expectedErr string
}{
{
@@ -36,19 +36,19 @@ func TestRestart(t *testing.T) {
{
name: "with -t",
args: []string{"-t", "2", "container-1"},
expectedOpts: client.ContainerStopOptions{Timeout: func(to int) *int { return &to }(2)},
expectedOpts: client.ContainerRestartOptions{Timeout: func(to int) *int { return &to }(2)},
restarted: []string{"container-1"},
},
{
name: "with --timeout",
args: []string{"--timeout", "2", "container-1"},
expectedOpts: client.ContainerStopOptions{Timeout: func(to int) *int { return &to }(2)},
expectedOpts: client.ContainerRestartOptions{Timeout: func(to int) *int { return &to }(2)},
restarted: []string{"container-1"},
},
{
name: "with --time",
args: []string{"--time", "2", "container-1"},
expectedOpts: client.ContainerStopOptions{Timeout: func(to int) *int { return &to }(2)},
expectedOpts: client.ContainerRestartOptions{Timeout: func(to int) *int { return &to }(2)},
restarted: []string{"container-1"},
},
{
@@ -62,17 +62,17 @@ func TestRestart(t *testing.T) {
mutex := new(sync.Mutex)
cli := test.NewFakeCli(&fakeClient{
containerRestartFunc: func(ctx context.Context, containerID string, options client.ContainerStopOptions) error {
containerRestartFunc: func(ctx context.Context, containerID string, options client.ContainerRestartOptions) (client.ContainerRestartResult, error) {
assert.Check(t, is.DeepEqual(options, tc.expectedOpts))
if containerID == "nosuchcontainer" {
return notFound(errors.New("Error: no such container: " + containerID))
return client.ContainerRestartResult{}, notFound(errors.New("Error: no such container: " + containerID))
}
// TODO(thaJeztah): consider using parallelOperation for restart, similar to "stop" and "remove"
mutex.Lock()
restarted = append(restarted, containerID)
mutex.Unlock()
return nil
return client.ContainerRestartResult{}, nil
},
Version: "1.36",
})

View File

@@ -67,11 +67,12 @@ func runRm(ctx context.Context, dockerCLI command.Cli, opts *rmOptions) error {
if ctrID == "" {
return errors.New("container name cannot be empty")
}
return apiClient.ContainerRemove(ctx, ctrID, client.ContainerRemoveOptions{
_, err := apiClient.ContainerRemove(ctx, ctrID, client.ContainerRemoveOptions{
RemoveVolumes: opts.rmVolumes,
RemoveLinks: opts.rmLink,
Force: opts.force,
})
return err
})
var errs []error

View File

@@ -27,7 +27,7 @@ func TestRemoveForce(t *testing.T) {
mutex := new(sync.Mutex)
cli := test.NewFakeCli(&fakeClient{
containerRemoveFunc: func(ctx context.Context, container string, options client.ContainerRemoveOptions) error {
containerRemoveFunc: func(ctx context.Context, container string, options client.ContainerRemoveOptions) (client.ContainerRemoveResult, error) {
// containerRemoveFunc is called in parallel for each container
// by the remove command so append must be synchronized.
mutex.Lock()
@@ -35,9 +35,9 @@ func TestRemoveForce(t *testing.T) {
mutex.Unlock()
if container == "nosuchcontainer" {
return notFound(errors.New("Error: no such container: " + container))
return client.ContainerRemoveResult{}, notFound(errors.New("Error: no such container: " + container))
}
return nil
return client.ContainerRemoveResult{}, nil
},
Version: "1.36",
})

View File

@@ -193,7 +193,7 @@ func runContainer(ctx context.Context, dockerCli command.Cli, runOpts *runOption
statusChan := waitExitOrRemoved(statusCtx, apiClient, containerID, copts.autoRemove)
// start the container
if err := apiClient.ContainerStart(ctx, containerID, client.ContainerStartOptions{}); err != nil {
if _, err := apiClient.ContainerStart(ctx, containerID, client.ContainerStartOptions{}); err != nil {
// If we have hijackedIOStreamer, we should notify
// hijackedIOStreamer we are going to exit and wait
// to avoid the terminal are not restored.

View File

@@ -14,11 +14,11 @@ import (
"github.com/docker/cli/cli/streams"
"github.com/docker/cli/internal/test"
"github.com/docker/cli/internal/test/notary"
"github.com/moby/moby/api/pkg/progress"
"github.com/moby/moby/api/pkg/streamformatter"
"github.com/moby/moby/api/types"
"github.com/moby/moby/api/types/container"
"github.com/moby/moby/client"
"github.com/moby/moby/client/pkg/progress"
"github.com/moby/moby/client/pkg/streamformatter"
"github.com/spf13/pflag"
"gotest.tools/v3/assert"
is "gotest.tools/v3/assert/cmp"
@@ -149,11 +149,11 @@ func TestRunAttachTermination(t *testing.T) {
createContainerFunc: func(options client.ContainerCreateOptions) (client.ContainerCreateResult, error) {
return client.ContainerCreateResult{ID: "id"}, nil
},
containerKillFunc: func(ctx context.Context, containerID, sig string) error {
if sig == "TERM" {
containerKillFunc: func(ctx context.Context, container string, options client.ContainerKillOptions) (client.ContainerKillResult, error) {
if options.Signal == "TERM" {
close(killCh)
}
return nil
return client.ContainerKillResult{}, nil
},
containerAttachFunc: func(ctx context.Context, containerID string, options client.ContainerAttachOptions) (client.ContainerAttachResult, error) {
server, clientConn := net.Pipe()

View File

@@ -48,7 +48,10 @@ func ForwardAllSignals(ctx context.Context, apiClient client.ContainerAPIClient,
continue
}
if err := apiClient.ContainerKill(ctx, cid, sig); err != nil {
_, err := apiClient.ContainerKill(ctx, cid, client.ContainerKillOptions{
Signal: sig,
})
if err != nil {
logrus.Debugf("Error sending signal: %s", err)
}
}

View File

@@ -6,6 +6,7 @@ import (
"testing"
"time"
"github.com/moby/moby/client"
"github.com/moby/sys/signal"
)
@@ -14,9 +15,9 @@ func TestForwardSignals(t *testing.T) {
defer cancel()
called := make(chan struct{})
apiClient := &fakeClient{containerKillFunc: func(ctx context.Context, container, signal string) error {
apiClient := &fakeClient{containerKillFunc: func(ctx context.Context, container string, options client.ContainerKillOptions) (client.ContainerKillResult, error) {
close(called)
return nil
return client.ContainerKillResult{}, nil
}}
sigc := make(chan os.Signal)

View File

@@ -9,6 +9,7 @@ import (
"testing"
"time"
"github.com/moby/moby/client"
"golang.org/x/sys/unix"
"gotest.tools/v3/assert"
)
@@ -22,9 +23,9 @@ func TestIgnoredSignals(t *testing.T) {
defer cancel()
var called bool
apiClient := &fakeClient{containerKillFunc: func(ctx context.Context, container, signal string) error {
apiClient := &fakeClient{containerKillFunc: func(ctx context.Context, container string, options client.ContainerKillOptions) (client.ContainerKillResult, error) {
called = true
return nil
return client.ContainerKillResult{}, nil
}}
sigc := make(chan os.Signal)

View File

@@ -145,7 +145,7 @@ func RunStart(ctx context.Context, dockerCli command.Cli, opts *StartOptions) er
statusChan := waitExitOrRemoved(ctx, dockerCli.Client(), c.Container.ID, c.Container.HostConfig.AutoRemove)
// 4. Start the container.
err = dockerCli.Client().ContainerStart(ctx, c.Container.ID, client.ContainerStartOptions{
_, err = dockerCli.Client().ContainerStart(ctx, c.Container.ID, client.ContainerStartOptions{
CheckpointID: opts.Checkpoint,
CheckpointDir: opts.CheckpointDir,
})
@@ -183,10 +183,11 @@ func RunStart(ctx context.Context, dockerCli command.Cli, opts *StartOptions) er
return errors.New("you cannot restore multiple containers at once")
}
ctr := opts.Containers[0]
return dockerCli.Client().ContainerStart(ctx, ctr, client.ContainerStartOptions{
_, err := dockerCli.Client().ContainerStart(ctx, ctr, client.ContainerStartOptions{
CheckpointID: opts.Checkpoint,
CheckpointDir: opts.CheckpointDir,
})
return err
default:
// We're not going to attach to anything.
// Start as many containers as we want.
@@ -197,7 +198,7 @@ func RunStart(ctx context.Context, dockerCli command.Cli, opts *StartOptions) er
func startContainersWithoutAttachments(ctx context.Context, dockerCli command.Cli, containers []string) error {
var failedContainers []string
for _, ctr := range containers {
if err := dockerCli.Client().ContainerStart(ctx, ctr, client.ContainerStartOptions{}); err != nil {
if _, err := dockerCli.Client().ContainerStart(ctx, ctr, client.ContainerStartOptions{}); err != nil {
_, _ = fmt.Fprintln(dockerCli.Err(), err)
failedContainers = append(failedContainers, ctr)
continue

View File

@@ -109,6 +109,8 @@ func RunStats(ctx context.Context, dockerCLI command.Cli, options *StatsOptions)
apiClient := dockerCLI.Client()
// Get the daemonOSType to handle platform-specific stats fields.
// This value is used as a fallback for docker < v29, which did not
// include the OSType field per stats.
daemonOSType = dockerCLI.ServerInfo().OSType
// waitFirst is a WaitGroup to wait first stat data's reach for each container

View File

@@ -60,7 +60,10 @@ func collect(ctx context.Context, s *Stats, cli client.ContainerAPIClient, strea
}
}()
response, err := cli.ContainerStats(ctx, s.Container, streamStats)
response, err := cli.ContainerStats(ctx, s.Container, client.ContainerStatsOptions{
Stream: streamStats,
IncludePreviousSample: !streamStats, // collect previous CPU value for the first result when not streaming.
})
if err != nil {
s.SetError(err)
return
@@ -85,6 +88,12 @@ func collect(ctx context.Context, s *Stats, cli client.ContainerAPIClient, strea
continue
}
// Daemon versions before v29 did not return per-stats OSType;
// fall back to using the daemon's OSType.
if v.OSType == "" {
v.OSType = daemonOSType
}
if daemonOSType == "windows" {
netRx, netTx := calculateNetwork(v.Networks)
s.SetStatistics(StatsEntry{

View File

@@ -64,10 +64,11 @@ func runStop(ctx context.Context, dockerCLI command.Cli, opts *stopOptions) erro
apiClient := dockerCLI.Client()
errChan := parallelOperation(ctx, opts.containers, func(ctx context.Context, id string) error {
return apiClient.ContainerStop(ctx, id, client.ContainerStopOptions{
_, err := apiClient.ContainerStop(ctx, id, client.ContainerStopOptions{
Signal: opts.signal,
Timeout: timeout,
})
return err
})
var errs []error
for _, ctr := range opts.containers {

View File

@@ -62,10 +62,10 @@ func TestStop(t *testing.T) {
mutex := new(sync.Mutex)
cli := test.NewFakeCli(&fakeClient{
containerStopFunc: func(ctx context.Context, containerID string, options client.ContainerStopOptions) error {
containerStopFunc: func(ctx context.Context, containerID string, options client.ContainerStopOptions) (client.ContainerStopResult, error) {
assert.Check(t, is.DeepEqual(options, tc.expectedOpts))
if containerID == "nosuchcontainer" {
return notFound(errors.New("Error: no such container: " + containerID))
return client.ContainerStopResult{}, notFound(errors.New("Error: no such container: " + containerID))
}
// containerStopFunc is called in parallel for each container
@@ -73,7 +73,7 @@ func TestStop(t *testing.T) {
mutex.Lock()
stopped = append(stopped, containerID)
mutex.Unlock()
return nil
return client.ContainerStopResult{}, nil
},
Version: "1.36",
})

View File

@@ -27,7 +27,7 @@ func resizeTTYTo(ctx context.Context, apiClient client.ContainerAPIClient, id st
Width: width,
})
} else {
err = apiClient.ContainerResize(ctx, id, client.ContainerResizeOptions{
_, err = apiClient.ContainerResize(ctx, id, client.ContainerResizeOptions{
Height: height,
Width: width,
})

View File

@@ -9,6 +9,7 @@ import (
"github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/command/completion"
"github.com/moby/moby/api/types/container"
"github.com/moby/moby/client"
"github.com/spf13/cobra"
)
@@ -41,7 +42,10 @@ 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, apiClient.ContainerUnpause)
errChan := parallelOperation(ctx, opts.containers, func(ctx context.Context, container string) error {
_, err := apiClient.ContainerUnpause(ctx, container, client.ContainerUnPauseOptions{})
return err
})
var errs []error
for _, ctr := range opts.containers {
if err := <-errChan; err != nil {

View File

@@ -21,12 +21,12 @@ import (
"github.com/docker/cli/internal/jsonstream"
"github.com/docker/cli/opts"
"github.com/moby/go-archive"
"github.com/moby/moby/api/pkg/progress"
"github.com/moby/moby/api/pkg/streamformatter"
buildtypes "github.com/moby/moby/api/types/build"
"github.com/moby/moby/api/types/container"
registrytypes "github.com/moby/moby/api/types/registry"
"github.com/moby/moby/client"
"github.com/moby/moby/client/pkg/progress"
"github.com/moby/moby/client/pkg/streamformatter"
"github.com/spf13/cobra"
)

View File

@@ -20,8 +20,8 @@ import (
"github.com/docker/cli/cli/command/image/build/internal/git"
"github.com/moby/go-archive"
"github.com/moby/go-archive/compression"
"github.com/moby/moby/api/pkg/progress"
"github.com/moby/moby/api/pkg/streamformatter"
"github.com/moby/moby/client/pkg/progress"
"github.com/moby/moby/client/pkg/streamformatter"
"github.com/moby/patternmatcher"
)

View File

@@ -12,10 +12,10 @@ import (
"time"
"github.com/docker/cli/cli/command/formatter"
"github.com/moby/moby/api/pkg/progress"
"github.com/moby/moby/api/pkg/streamformatter"
"github.com/moby/moby/api/types/swarm"
"github.com/moby/moby/client"
"github.com/moby/moby/client/pkg/progress"
"github.com/moby/moby/client/pkg/streamformatter"
)
var (

View File

@@ -5,8 +5,8 @@ import (
"strconv"
"testing"
"github.com/moby/moby/api/pkg/progress"
"github.com/moby/moby/api/types/swarm"
"github.com/moby/moby/client/pkg/progress"
"gotest.tools/v3/assert"
is "gotest.tools/v3/assert/cmp"
)

View File

@@ -8,10 +8,10 @@ import (
"os/signal"
"time"
"github.com/moby/moby/api/pkg/progress"
"github.com/moby/moby/api/pkg/streamformatter"
"github.com/moby/moby/api/types/swarm"
"github.com/moby/moby/client"
"github.com/moby/moby/client/pkg/progress"
"github.com/moby/moby/client/pkg/streamformatter"
"github.com/opencontainers/go-digest"
)

View File

@@ -28,8 +28,8 @@ require (
github.com/google/uuid v1.6.0
github.com/mattn/go-runewidth v0.0.17
github.com/moby/go-archive v0.1.0
github.com/moby/moby/api v1.52.0-beta.2.0.20251026152250-0a134ecc1623 // master
github.com/moby/moby/client v0.1.0-beta.2.0.20251026152250-0a134ecc1623 // master
github.com/moby/moby/api v1.52.0-beta.2.0.20251028144225-90109a373da9 // master
github.com/moby/moby/client v0.1.0-beta.2.0.20251028144225-90109a373da9 // master
github.com/moby/patternmatcher v0.6.0
github.com/moby/swarmkit/v2 v2.1.0
github.com/moby/sys/atomicwriter v0.1.0

View File

@@ -170,10 +170,10 @@ github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3N
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
github.com/moby/go-archive v0.1.0 h1:Kk/5rdW/g+H8NHdJW2gsXyZ7UnzvJNOy6VKJqueWdcQ=
github.com/moby/go-archive v0.1.0/go.mod h1:G9B+YoujNohJmrIYFBpSd54GTUB4lt9S+xVQvsJyFuo=
github.com/moby/moby/api v1.52.0-beta.2.0.20251026152250-0a134ecc1623 h1:9Ulm1meUBVvvSDB65RmF7VymYBGmZ0peXCVWh6fzdoM=
github.com/moby/moby/api v1.52.0-beta.2.0.20251026152250-0a134ecc1623/go.mod h1:/ou52HkRydg4+odrUR3vFsGgjIyHvprrpEQEkweL10s=
github.com/moby/moby/client v0.1.0-beta.2.0.20251026152250-0a134ecc1623 h1:PkaM+LLZasetNAPJX0whFXr1GXjNQWMlyMEeUAeNUnE=
github.com/moby/moby/client v0.1.0-beta.2.0.20251026152250-0a134ecc1623/go.mod h1:sxVfwGqVgh7n+tdxA4gFToQ/lf+bM7zATnvQjVnsKT4=
github.com/moby/moby/api v1.52.0-beta.2.0.20251028144225-90109a373da9 h1:DDumCtiHK751YhrY/tbUdMo5BN8dRarxbW4ScVh/KZg=
github.com/moby/moby/api v1.52.0-beta.2.0.20251028144225-90109a373da9/go.mod h1:v0K/motq8oWmx+rtApG1rBTIpQ8KUONUjpf+U73gags=
github.com/moby/moby/client v0.1.0-beta.2.0.20251028144225-90109a373da9 h1:iAQD/kAfqMAb2p48advqXGOT8eWyEv+Xge7KbcsZduk=
github.com/moby/moby/client v0.1.0-beta.2.0.20251028144225-90109a373da9/go.mod h1:1YrJTvhL771Q4xiwwe72NSS17lgsCF67xu8fEfSd77g=
github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk=
github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc=
github.com/moby/swarmkit/v2 v2.1.0 h1:u+cJ5hSyF3HnzsyI+NtegYxdIPQIuibk7IbpXNxuISM=

View File

@@ -149,24 +149,76 @@ type PidsStats struct {
// StatsResponse aggregates all types of stats of one container.
type StatsResponse struct {
// ID is the ID of the container for which the stats were collected.
ID string `json:"id,omitempty"`
// Name is the name of the container for which the stats were collected.
Name string `json:"name,omitempty"`
ID string `json:"id,omitempty"`
// Common stats
Read time.Time `json:"read"`
PreRead time.Time `json:"preread"`
// OSType is the OS of the container ("linux" or "windows") to allow
// platform-specific handling of stats.
OSType string `json:"os_type,omitempty"`
// Linux specific stats, not populated on Windows.
PidsStats PidsStats `json:"pids_stats,omitempty"`
// Read is the date and time at which this sample was collected.
Read time.Time `json:"read"`
// CPUStats contains CPU related info of the container.
CPUStats CPUStats `json:"cpu_stats,omitempty"`
// MemoryStats aggregates all memory stats since container inception on Linux.
// Windows returns stats for commit and private working set only.
MemoryStats MemoryStats `json:"memory_stats,omitempty"`
// Networks contains Nntwork statistics for the container per interface.
//
// This field is omitted if the container has no networking enabled.
Networks map[string]NetworkStats `json:"networks,omitempty"`
// -------------------------------------------------------------------------
// Linux-specific stats, not populated on Windows.
// -------------------------------------------------------------------------
// PidsStats contains Linux-specific stats of a container's process-IDs (PIDs).
//
// This field is Linux-specific and omitted for Windows containers.
PidsStats PidsStats `json:"pids_stats,omitempty"`
// BlkioStats stores all IO service stats for data read and write.
//
// This type is Linux-specific and holds many fields that are specific
// to cgroups v1.
//
// On a cgroup v2 host, all fields other than "io_service_bytes_recursive"
// are omitted or "null".
//
// This type is only populated on Linux and omitted for Windows containers.
BlkioStats BlkioStats `json:"blkio_stats,omitempty"`
// Windows specific stats, not populated on Linux.
NumProcs uint32 `json:"num_procs"`
// -------------------------------------------------------------------------
// Windows-specific stats, not populated on Linux.
// -------------------------------------------------------------------------
// NumProcs is the number of processors on the system.
//
// This field is Windows-specific and always zero for Linux containers.
NumProcs uint32 `json:"num_procs"`
// StorageStats is the disk I/O stats for read/write on Windows.
//
// This type is Windows-specific and omitted for Linux containers.
StorageStats StorageStats `json:"storage_stats,omitempty"`
// Shared stats
CPUStats CPUStats `json:"cpu_stats,omitempty"`
PreCPUStats CPUStats `json:"precpu_stats,omitempty"` // "Pre"="Previous"
MemoryStats MemoryStats `json:"memory_stats,omitempty"`
Networks map[string]NetworkStats `json:"networks,omitempty"`
// -------------------------------------------------------------------------
// PreRead and PreCPUStats contain the previous sample of stats for
// the container, and can be used to perform delta-calculation.
// -------------------------------------------------------------------------
// PreRead is the date and time at which this first sample was collected.
// This field is not propagated if the "one-shot" option is set. If the
// "one-shot" option is set, this field may be omitted, empty, or set
// to a default date (`0001-01-01T00:00:00Z`).
PreRead time.Time `json:"preread"`
// PreCPUStats contains the CPUStats of the previous sample.
PreCPUStats CPUStats `json:"precpu_stats,omitempty"`
}

View File

@@ -0,0 +1,15 @@
package jsonstream
import "encoding/json"
// JSONMessage defines a message struct. It describes
// the created time, where it from, status, ID of the
// message. It's used for docker events.
type Message struct {
Stream string `json:"stream,omitempty"`
Status string `json:"status,omitempty"`
Progress *Progress `json:"progressDetail,omitempty"`
ID string `json:"id,omitempty"`
Error *Error `json:"errorDetail,omitempty"`
Aux *json.RawMessage `json:"aux,omitempty"` // Aux contains out-of-band data, such as digests for push signing and image id after building.
}

View File

@@ -64,21 +64,20 @@ type ContainerAPIClient interface {
ExecAPIClient
ContainerExport(ctx context.Context, container string) (io.ReadCloser, error)
ContainerInspect(ctx context.Context, container string, options ContainerInspectOptions) (ContainerInspectResult, error)
ContainerKill(ctx context.Context, container, signal string) error
ContainerKill(ctx context.Context, container string, options ContainerKillOptions) (ContainerKillResult, error)
ContainerList(ctx context.Context, options ContainerListOptions) ([]container.Summary, error)
ContainerLogs(ctx context.Context, container string, options ContainerLogsOptions) (io.ReadCloser, error)
ContainerPause(ctx context.Context, container string) error
ContainerRemove(ctx context.Context, container string, options ContainerRemoveOptions) error
ContainerPause(ctx context.Context, container string, options ContainerPauseOptions) (ContainerPauseResult, error)
ContainerRemove(ctx context.Context, container string, options ContainerRemoveOptions) (ContainerRemoveResult, error)
ContainerRename(ctx context.Context, container, newContainerName string) error
ContainerResize(ctx context.Context, container string, options ContainerResizeOptions) error
ContainerRestart(ctx context.Context, container string, options ContainerStopOptions) error
ContainerResize(ctx context.Context, container string, options ContainerResizeOptions) (ContainerResizeResult, error)
ContainerRestart(ctx context.Context, container string, options ContainerRestartOptions) (ContainerRestartResult, error)
ContainerStatPath(ctx context.Context, container, path string) (container.PathStat, error)
ContainerStats(ctx context.Context, container string, stream bool) (StatsResponseReader, error)
ContainerStatsOneShot(ctx context.Context, container string) (StatsResponseReader, error)
ContainerStart(ctx context.Context, container string, options ContainerStartOptions) error
ContainerStop(ctx context.Context, container string, options ContainerStopOptions) error
ContainerStats(ctx context.Context, container string, options ContainerStatsOptions) (ContainerStatsResult, error)
ContainerStart(ctx context.Context, container string, options ContainerStartOptions) (ContainerStartResult, error)
ContainerStop(ctx context.Context, container string, options ContainerStopOptions) (ContainerStopResult, error)
ContainerTop(ctx context.Context, container string, arguments []string) (container.TopResponse, error)
ContainerUnpause(ctx context.Context, container string) error
ContainerUnpause(ctx context.Context, container string, options ContainerUnPauseOptions) (ContainerUnPauseResult, error)
ContainerUpdate(ctx context.Context, container string, updateConfig container.UpdateConfig) (container.UpdateResponse, error)
ContainerWait(ctx context.Context, container string, condition container.WaitCondition) (<-chan container.WaitResponse, <-chan error)
CopyFromContainer(ctx context.Context, container, srcPath string) (io.ReadCloser, container.PathStat, error)

View File

@@ -5,19 +5,35 @@ import (
"net/url"
)
// ContainerKillOptions holds options for [Client.ContainerKill].
type ContainerKillOptions struct {
// Signal (optional) is the signal to send to the container to (gracefully)
// stop it before forcibly terminating the container with SIGKILL after a
// timeout. If no value is set, the default (SIGKILL) is used.
Signal string `json:",omitempty"`
}
// ContainerKillResult holds the result of [Client.ContainerKill],
type ContainerKillResult struct {
// Add future fields here.
}
// ContainerKill terminates the container process but does not remove the container from the docker host.
func (cli *Client) ContainerKill(ctx context.Context, containerID, signal string) error {
func (cli *Client) ContainerKill(ctx context.Context, containerID string, options ContainerKillOptions) (ContainerKillResult, error) {
containerID, err := trimID("container", containerID)
if err != nil {
return err
return ContainerKillResult{}, err
}
query := url.Values{}
if signal != "" {
query.Set("signal", signal)
if options.Signal != "" {
query.Set("signal", options.Signal)
}
resp, err := cli.post(ctx, "/containers/"+containerID+"/kill", query, nil, nil)
defer ensureReaderClosed(resp)
return err
if err != nil {
return ContainerKillResult{}, err
}
return ContainerKillResult{}, nil
}

View File

@@ -2,14 +2,27 @@ package client
import "context"
// ContainerPauseOptions holds options for [Client.ContainerPause].
type ContainerPauseOptions struct {
// Add future optional parameters here.
}
// ContainerPauseResult holds the result of [Client.ContainerPause],
type ContainerPauseResult struct {
// Add future fields here.
}
// ContainerPause pauses the main process of a given container without terminating it.
func (cli *Client) ContainerPause(ctx context.Context, containerID string) error {
func (cli *Client) ContainerPause(ctx context.Context, containerID string, options ContainerPauseOptions) (ContainerPauseResult, error) {
containerID, err := trimID("container", containerID)
if err != nil {
return err
return ContainerPauseResult{}, err
}
resp, err := cli.post(ctx, "/containers/"+containerID+"/pause", nil, nil, nil)
defer ensureReaderClosed(resp)
return err
if err != nil {
return ContainerPauseResult{}, err
}
return ContainerPauseResult{}, nil
}

View File

@@ -12,11 +12,16 @@ type ContainerRemoveOptions struct {
Force bool
}
// ContainerRemoveResult holds the result of [Client.ContainerRemove],
type ContainerRemoveResult struct {
// Add future fields here.
}
// ContainerRemove kills and removes a container from the docker host.
func (cli *Client) ContainerRemove(ctx context.Context, containerID string, options ContainerRemoveOptions) error {
func (cli *Client) ContainerRemove(ctx context.Context, containerID string, options ContainerRemoveOptions) (ContainerRemoveResult, error) {
containerID, err := trimID("container", containerID)
if err != nil {
return err
return ContainerRemoveResult{}, err
}
query := url.Values{}
@@ -33,5 +38,8 @@ func (cli *Client) ContainerRemove(ctx context.Context, containerID string, opti
resp, err := cli.delete(ctx, "/containers/"+containerID, query, nil)
defer ensureReaderClosed(resp)
return err
if err != nil {
return ContainerRemoveResult{}, err
}
return ContainerRemoveResult{}, nil
}

View File

@@ -14,13 +14,28 @@ type ContainerResizeOptions struct {
Width uint
}
// ContainerResizeResult holds the result of [Client.ContainerResize],
type ContainerResizeResult struct {
// Add future fields here.
}
// ContainerResize changes the size of the pseudo-TTY for a container.
func (cli *Client) ContainerResize(ctx context.Context, containerID string, options ContainerResizeOptions) error {
func (cli *Client) ContainerResize(ctx context.Context, containerID string, options ContainerResizeOptions) (ContainerResizeResult, error) {
containerID, err := trimID("container", containerID)
if err != nil {
return err
return ContainerResizeResult{}, err
}
return cli.resize(ctx, "/containers/"+containerID, options.Height, options.Width)
// FIXME(thaJeztah): the API / backend accepts uint32, but container.ResizeOptions uses uint.
query := url.Values{}
query.Set("h", strconv.FormatUint(uint64(options.Height), 10))
query.Set("w", strconv.FormatUint(uint64(options.Width), 10))
resp, err := cli.post(ctx, "/containers/"+containerID+"/resize", query, nil, nil)
defer ensureReaderClosed(resp)
if err != nil {
return ContainerResizeResult{}, err
}
return ContainerResizeResult{}, nil
}
// ExecResizeOptions holds options for resizing a container exec TTY.
@@ -36,17 +51,16 @@ func (cli *Client) ExecResize(ctx context.Context, execID string, options ExecRe
if err != nil {
return ExecResizeResult{}, err
}
err = cli.resize(ctx, "/exec/"+execID, options.Height, options.Width)
return ExecResizeResult{}, err
}
func (cli *Client) resize(ctx context.Context, basePath string, height, width uint) error {
// FIXME(thaJeztah): the API / backend accepts uint32, but container.ResizeOptions uses uint.
query := url.Values{}
query.Set("h", strconv.FormatUint(uint64(height), 10))
query.Set("w", strconv.FormatUint(uint64(width), 10))
query.Set("h", strconv.FormatUint(uint64(options.Height), 10))
query.Set("w", strconv.FormatUint(uint64(options.Width), 10))
resp, err := cli.post(ctx, basePath+"/resize", query, nil, nil)
resp, err := cli.post(ctx, "/exec/"+execID+"/resize", query, nil, nil)
defer ensureReaderClosed(resp)
return err
if err != nil {
return ExecResizeResult{}, err
}
return ExecResizeResult{}, nil
}

View File

@@ -6,13 +6,36 @@ import (
"strconv"
)
// ContainerRestartOptions holds options for [Client.ContainerRestart].
type ContainerRestartOptions struct {
// Signal (optional) is the signal to send to the container to (gracefully)
// stop it before forcibly terminating the container with SIGKILL after the
// timeout expires. If no value is set, the default (SIGTERM) is used.
Signal string `json:",omitempty"`
// Timeout (optional) is the timeout (in seconds) to wait for the container
// to stop gracefully before forcibly terminating it with SIGKILL.
//
// - Use nil to use the default timeout (10 seconds).
// - Use '-1' to wait indefinitely.
// - Use '0' to not wait for the container to exit gracefully, and
// immediately proceeds to forcibly terminating the container.
// - Other positive values are used as timeout (in seconds).
Timeout *int `json:",omitempty"`
}
// ContainerRestartResult holds the result of [Client.ContainerRestart],
type ContainerRestartResult struct {
// Add future fields here.
}
// ContainerRestart stops, and starts a container again.
// It makes the daemon wait for the container to be up again for
// a specific amount of time, given the timeout.
func (cli *Client) ContainerRestart(ctx context.Context, containerID string, options ContainerStopOptions) error {
func (cli *Client) ContainerRestart(ctx context.Context, containerID string, options ContainerRestartOptions) (ContainerRestartResult, error) {
containerID, err := trimID("container", containerID)
if err != nil {
return err
return ContainerRestartResult{}, err
}
query := url.Values{}
@@ -24,5 +47,8 @@ func (cli *Client) ContainerRestart(ctx context.Context, containerID string, opt
}
resp, err := cli.post(ctx, "/containers/"+containerID+"/restart", query, nil, nil)
defer ensureReaderClosed(resp)
return err
if err != nil {
return ContainerRestartResult{}, err
}
return ContainerRestartResult{}, nil
}

View File

@@ -5,17 +5,22 @@ import (
"net/url"
)
// ContainerStartOptions holds parameters to start containers.
// ContainerStartOptions holds options for [Client.ContainerStart].
type ContainerStartOptions struct {
CheckpointID string
CheckpointDir string
}
// ContainerStartResult holds the result of [Client.ContainerStart],
type ContainerStartResult struct {
// Add future fields here.
}
// ContainerStart sends a request to the docker daemon to start a container.
func (cli *Client) ContainerStart(ctx context.Context, containerID string, options ContainerStartOptions) error {
func (cli *Client) ContainerStart(ctx context.Context, containerID string, options ContainerStartOptions) (ContainerStartResult, error) {
containerID, err := trimID("container", containerID)
if err != nil {
return err
return ContainerStartResult{}, err
}
query := url.Values{}
@@ -28,5 +33,8 @@ func (cli *Client) ContainerStart(ctx context.Context, containerID string, optio
resp, err := cli.post(ctx, "/containers/"+containerID+"/start", query, nil, nil)
defer ensureReaderClosed(resp)
return err
if err != nil {
return ContainerStartResult{}, err
}
return ContainerStartResult{}, nil
}

View File

@@ -6,62 +6,68 @@ import (
"net/url"
)
// StatsResponseReader wraps an [io.ReadCloser] to read (a stream of) stats
// for a container, as produced by the GET "/stats" endpoint.
//
// The OSType field is set to the server's platform to allow
// platform-specific handling of the response.
//
// TODO(thaJeztah): remove this wrapper, and make OSType part of [github.com/moby/moby/api/types/container.StatsResponse].
type StatsResponseReader struct {
Body io.ReadCloser `json:"body"`
OSType string `json:"ostype"`
// ContainerStatsOptions holds parameters to retrieve container statistics
// using the [Client.ContainerStats] method.
type ContainerStatsOptions struct {
// Stream enables streaming [container.StatsResponse] results instead
// of collecting a single sample. If enabled, the client remains attached
// until the [ContainerStatsResult.Body] is closed or the context is
// cancelled.
Stream bool
// IncludePreviousSample asks the daemon to collect a prior sample to populate the
// [container.StatsResponse.PreRead] and [container.StatsResponse.PreCPUStats]
// fields.
//
// It set, the daemon collects two samples at a one-second interval before
// returning the result. The first sample populates the PreCPUStats (“previous
// CPU”) field, allowing delta calculations for CPU usage. If false, only
// a single sample is taken and returned immediately, leaving PreRead and
// PreCPUStats empty.
//
// This option has no effect if Stream is enabled. If Stream is enabled,
// [container.StatsResponse.PreCPUStats] is never populated for the first
// record.
IncludePreviousSample bool
}
// ContainerStats returns near realtime stats for a given container.
// It's up to the caller to close the [io.ReadCloser] returned.
func (cli *Client) ContainerStats(ctx context.Context, containerID string, stream bool) (StatsResponseReader, error) {
// ContainerStatsResult holds the result from [Client.ContainerStats].
//
// It wraps an [io.ReadCloser] that provides one or more [container.StatsResponse]
// objects for a container, as produced by the "GET /containers/{id}/stats" endpoint.
// If streaming is disabled, the stream contains a single record.
type ContainerStatsResult struct {
Body io.ReadCloser
}
// ContainerStats retrieves live resource usage statistics for the specified
// container. The caller must close the [io.ReadCloser] in the returned result
// to release associated resources.
func (cli *Client) ContainerStats(ctx context.Context, containerID string, options ContainerStatsOptions) (ContainerStatsResult, error) {
containerID, err := trimID("container", containerID)
if err != nil {
return StatsResponseReader{}, err
return ContainerStatsResult{}, err
}
query := url.Values{}
query.Set("stream", "0")
if stream {
query.Set("stream", "1")
if options.Stream {
query.Set("stream", "true")
} else {
// Note: daemons before v29.0 return an error if both set: "cannot have stream=true and one-shot=true"
//
// TODO(thaJeztah): consider making "stream=false" the default for the API as well, or using Accept Header to switch.
query.Set("stream", "false")
if !options.IncludePreviousSample {
query.Set("one-shot", "true")
}
}
resp, err := cli.get(ctx, "/containers/"+containerID+"/stats", query, nil)
if err != nil {
return StatsResponseReader{}, err
return ContainerStatsResult{}, err
}
return StatsResponseReader{
Body: resp.Body,
OSType: resp.Header.Get("Ostype"),
}, nil
}
// ContainerStatsOneShot gets a single stat entry from a container.
// It differs from `ContainerStats` in that the API should not wait to prime the stats
func (cli *Client) ContainerStatsOneShot(ctx context.Context, containerID string) (StatsResponseReader, error) {
containerID, err := trimID("container", containerID)
if err != nil {
return StatsResponseReader{}, err
}
query := url.Values{}
query.Set("stream", "0")
query.Set("one-shot", "1")
resp, err := cli.get(ctx, "/containers/"+containerID+"/stats", query, nil)
if err != nil {
return StatsResponseReader{}, err
}
return StatsResponseReader{
Body: resp.Body,
OSType: resp.Header.Get("Ostype"),
return ContainerStatsResult{
Body: resp.Body,
}, nil
}

View File

@@ -6,11 +6,11 @@ import (
"strconv"
)
// ContainerStopOptions holds the options to stop or restart a container.
// ContainerStopOptions holds the options for [Client.ContainerStop].
type ContainerStopOptions struct {
// Signal (optional) is the signal to send to the container to (gracefully)
// stop it before forcibly terminating the container with SIGKILL after the
// timeout expires. If not value is set, the default (SIGTERM) is used.
// timeout expires. If no value is set, the default (SIGTERM) is used.
Signal string `json:",omitempty"`
// Timeout (optional) is the timeout (in seconds) to wait for the container
@@ -24,6 +24,11 @@ type ContainerStopOptions struct {
Timeout *int `json:",omitempty"`
}
// ContainerStopResult holds the result of [Client.ContainerStop],
type ContainerStopResult struct {
// Add future fields here.
}
// ContainerStop stops a container. In case the container fails to stop
// gracefully within a time frame specified by the timeout argument,
// it is forcefully terminated (killed).
@@ -31,10 +36,10 @@ type ContainerStopOptions struct {
// If the timeout is nil, the container's StopTimeout value is used, if set,
// otherwise the engine default. A negative timeout value can be specified,
// meaning no timeout, i.e. no forceful termination is performed.
func (cli *Client) ContainerStop(ctx context.Context, containerID string, options ContainerStopOptions) error {
func (cli *Client) ContainerStop(ctx context.Context, containerID string, options ContainerStopOptions) (ContainerStopResult, error) {
containerID, err := trimID("container", containerID)
if err != nil {
return err
return ContainerStopResult{}, err
}
query := url.Values{}
@@ -46,5 +51,8 @@ func (cli *Client) ContainerStop(ctx context.Context, containerID string, option
}
resp, err := cli.post(ctx, "/containers/"+containerID+"/stop", query, nil, nil)
defer ensureReaderClosed(resp)
return err
if err != nil {
return ContainerStopResult{}, err
}
return ContainerStopResult{}, nil
}

View File

@@ -2,14 +2,27 @@ package client
import "context"
// ContainerUnPauseOptions holds options for [Client.ContainerUnpause].
type ContainerUnPauseOptions struct {
// Add future optional parameters here.
}
// ContainerUnPauseResult holds the result of [Client.ContainerUnpause],
type ContainerUnPauseResult struct {
// Add future fields here.
}
// ContainerUnpause resumes the process execution within a container.
func (cli *Client) ContainerUnpause(ctx context.Context, containerID string) error {
func (cli *Client) ContainerUnpause(ctx context.Context, containerID string, options ContainerUnPauseOptions) (ContainerUnPauseResult, error) {
containerID, err := trimID("container", containerID)
if err != nil {
return err
return ContainerUnPauseResult{}, err
}
resp, err := cli.post(ctx, "/containers/"+containerID+"/unpause", nil, nil, nil)
defer ensureReaderClosed(resp)
return err
if err != nil {
return ContainerUnPauseResult{}, err
}
return ContainerUnPauseResult{}, nil
}

View File

@@ -47,36 +47,13 @@ func (cli *Client) ImageLoad(ctx context.Context, input io.Reader, loadOpts ...I
}
return ImageLoadResult{
body: resp.Body,
JSON: resp.Header.Get("Content-Type") == "application/json",
}, nil
}
// ImageLoadResult returns information to the client about a load process.
//
// TODO(thaJeztah): remove this type, and just use an io.ReadCloser
//
// This type was added in https://github.com/moby/moby/pull/18878, related
// to https://github.com/moby/moby/issues/19177;
//
// Make docker load to output json when the response content type is json
// Swarm hijacks the response from docker load and returns JSON rather
// than plain text like the Engine does. This makes the API library to return
// information to figure that out.
//
// However the "load" endpoint unconditionally returns JSON;
// https://github.com/moby/moby/blob/7b9d2ef6e5518a3d3f3cc418459f8df786cfbbd1/api/server/router/image/image_routes.go#L248-L255
//
// PR https://github.com/moby/moby/pull/21959 made the response-type depend
// on whether "quiet" was set, but this logic got changed in a follow-up
// https://github.com/moby/moby/pull/25557, which made the JSON response-type
// unconditionally, but the output produced depend on whether"quiet" was set.
//
// We should deprecated the "quiet" option, as it's really a client
// responsibility.
type ImageLoadResult struct {
// Body must be closed to avoid a resource leak
body io.ReadCloser
JSON bool
}
func (r ImageLoadResult) Read(p []byte) (n int, err error) {

View File

@@ -10,29 +10,10 @@ import (
"time"
"github.com/docker/go-units"
"github.com/moby/moby/api/pkg/progress"
"github.com/moby/moby/api/types/jsonstream"
"github.com/moby/moby/client/pkg/progress"
)
// jsonMessage defines a message struct. It describes
// the created time, where it from, status, ID of the
// message. It's used for docker events.
//
// It is a reduced set of [jsonmessage.JSONMessage].
type jsonMessage struct {
Stream string `json:"stream,omitempty"`
Status string `json:"status,omitempty"`
Progress *jsonstream.Progress `json:"progressDetail,omitempty"`
ID string `json:"id,omitempty"`
Error *jsonstream.Error `json:"errorDetail,omitempty"`
Aux *json.RawMessage `json:"aux,omitempty"` // Aux contains out-of-band data, such as digests for push signing and image id after building.
// ErrorMessage contains errors encountered during the operation.
//
// Deprecated: this field is deprecated since docker v0.6.0 / API v1.4. Use [Error.Message] instead. This field will be omitted in a future release.
ErrorMessage string `json:"error,omitempty"` // deprecated
}
const streamNewline = "\r\n"
type jsonProgressFormatter struct{}
@@ -44,7 +25,7 @@ func appendNewline(source []byte) []byte {
// FormatStatus formats the specified objects according to the specified format (and id).
func FormatStatus(id, format string, a ...any) []byte {
str := fmt.Sprintf(format, a...)
b, err := json.Marshal(&jsonMessage{ID: id, Status: str})
b, err := json.Marshal(&jsonstream.Message{ID: id, Status: str})
if err != nil {
return FormatError(err)
}
@@ -57,7 +38,7 @@ func FormatError(err error) []byte {
if !ok {
jsonError = &jsonstream.Error{Message: err.Error()}
}
if b, err := json.Marshal(&jsonMessage{Error: jsonError, ErrorMessage: err.Error()}); err == nil {
if b, err := json.Marshal(&jsonstream.Message{Error: jsonError}); err == nil {
return appendNewline(b)
}
return []byte(`{"error":"format error"}` + streamNewline)
@@ -81,7 +62,7 @@ func (sf *jsonProgressFormatter) formatProgress(id, action string, progress *jso
auxJSON = new(json.RawMessage)
*auxJSON = auxJSONBytes
}
b, err := json.Marshal(&jsonMessage{
b, err := json.Marshal(&jsonstream.Message{
Status: action,
Progress: progress,
ID: id,
@@ -234,7 +215,7 @@ func (sf *AuxFormatter) Emit(id string, aux any) error {
}
auxJSON := new(json.RawMessage)
*auxJSON = auxJSONBytes
msgJSON, err := json.Marshal(&jsonMessage{ID: id, Aux: auxJSON})
msgJSON, err := json.Marshal(&jsonstream.Message{ID: id, Aux: auxJSON})
if err != nil {
return err
}

View File

@@ -3,6 +3,8 @@ package streamformatter
import (
"encoding/json"
"io"
"github.com/moby/moby/api/types/jsonstream"
)
type streamWriter struct {
@@ -20,7 +22,7 @@ func (sw *streamWriter) Write(buf []byte) (int, error) {
}
func (sw *streamWriter) format(buf []byte) []byte {
msg := &jsonMessage{Stream: sw.lineFormat(buf)}
msg := &jsonstream.Message{Stream: sw.lineFormat(buf)}
b, err := json.Marshal(msg)
if err != nil {
return FormatError(err)

8
vendor/modules.txt vendored
View File

@@ -168,12 +168,10 @@ github.com/moby/docker-image-spec/specs-go/v1
github.com/moby/go-archive
github.com/moby/go-archive/compression
github.com/moby/go-archive/tarheader
# github.com/moby/moby/api v1.52.0-beta.2.0.20251026152250-0a134ecc1623
# github.com/moby/moby/api v1.52.0-beta.2.0.20251028144225-90109a373da9
## explicit; go 1.23.0
github.com/moby/moby/api/pkg/authconfig
github.com/moby/moby/api/pkg/progress
github.com/moby/moby/api/pkg/stdcopy
github.com/moby/moby/api/pkg/streamformatter
github.com/moby/moby/api/types
github.com/moby/moby/api/types/auxprogress
github.com/moby/moby/api/types/blkiodev
@@ -192,13 +190,15 @@ github.com/moby/moby/api/types/storage
github.com/moby/moby/api/types/swarm
github.com/moby/moby/api/types/system
github.com/moby/moby/api/types/volume
# github.com/moby/moby/client v0.1.0-beta.2.0.20251026152250-0a134ecc1623
# github.com/moby/moby/client v0.1.0-beta.2.0.20251028144225-90109a373da9
## explicit; go 1.23.0
github.com/moby/moby/client
github.com/moby/moby/client/internal
github.com/moby/moby/client/internal/timestamp
github.com/moby/moby/client/pkg/jsonmessage
github.com/moby/moby/client/pkg/progress
github.com/moby/moby/client/pkg/security
github.com/moby/moby/client/pkg/streamformatter
github.com/moby/moby/client/pkg/stringid
github.com/moby/moby/client/pkg/versions
# github.com/moby/patternmatcher v0.6.0