diff --git a/cli/command/container/client_test.go b/cli/command/container/client_test.go index 6f99b171d..fe46be393 100644 --- a/cli/command/container/client_test.go +++ b/cli/command/container/client_test.go @@ -3,33 +3,18 @@ package container import ( "context" "io" - "reflect" + "net/http" "strings" - "unsafe" "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 + return io.NopCloser(strings.NewReader(content)) } 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 + return io.NopCloser(strings.NewReader(content)) } type fakeStreamResult struct { @@ -147,7 +132,7 @@ func (f *fakeClient) ContainerLogs(_ context.Context, containerID string, option if f.logFunc != nil { return f.logFunc(containerID, options) } - return client.ContainerLogsResult{}, nil + return http.NoBody, nil } func (f *fakeClient) ClientVersion() string { @@ -172,7 +157,7 @@ func (f *fakeClient) ContainerExport(_ context.Context, containerID string, _ cl if f.containerExportFunc != nil { return f.containerExportFunc(containerID) } - return client.ContainerExportResult{}, nil + return http.NoBody, nil } func (f *fakeClient) ExecResize(_ context.Context, id string, options client.ExecResizeOptions) (client.ExecResizeResult, error) { @@ -189,7 +174,7 @@ func (f *fakeClient) ContainerKill(ctx context.Context, containerID string, opti return client.ContainerKillResult{}, nil } -func (f *fakeClient) ContainersPrune(ctx context.Context, options client.ContainerPruneOptions) (client.ContainerPruneResult, error) { +func (f *fakeClient) ContainerPrune(ctx context.Context, options client.ContainerPruneOptions) (client.ContainerPruneResult, error) { if f.containerPruneFunc != nil { return f.containerPruneFunc(ctx, options) } diff --git a/cli/command/container/exec.go b/cli/command/container/exec.go index 9b981e3e7..dcb6fdc57 100644 --- a/cli/command/container/exec.go +++ b/cli/command/container/exec.go @@ -101,7 +101,7 @@ func RunExec(ctx context.Context, dockerCLI command.Cli, containerIDorName strin return err } if !options.Detach { - if err := dockerCLI.In().CheckTty(execOptions.AttachStdin, execOptions.Tty); err != nil { + if err := dockerCLI.In().CheckTty(execOptions.AttachStdin, execOptions.TTY); err != nil { return err } } @@ -119,17 +119,10 @@ func RunExec(ctx context.Context, dockerCLI command.Cli, containerIDorName strin } if options.Detach { - var cs client.ConsoleSize - if execOptions.ConsoleSize != nil { - cs = client.ConsoleSize{ - Height: execOptions.ConsoleSize[0], - Width: execOptions.ConsoleSize[1], - } - } _, err := apiClient.ExecStart(ctx, execID, client.ExecStartOptions{ Detach: options.Detach, - TTY: execOptions.Tty, - ConsoleSize: cs, + TTY: execOptions.TTY, + ConsoleSize: client.ConsoleSize{Height: execOptions.ConsoleSize.Height, Width: execOptions.ConsoleSize.Width}, }) return err } @@ -137,9 +130,9 @@ func RunExec(ctx context.Context, dockerCLI command.Cli, containerIDorName strin } func fillConsoleSize(execOptions *client.ExecCreateOptions, dockerCli command.Cli) { - if execOptions.Tty { + if execOptions.TTY { height, width := dockerCli.Out().GetTtySize() - execOptions.ConsoleSize = &[2]uint{height, width} + execOptions.ConsoleSize = client.ConsoleSize{Height: height, Width: width} } } @@ -157,7 +150,7 @@ func interactiveExec(ctx context.Context, dockerCli command.Cli, execOptions *cl out = dockerCli.Out() } if execOptions.AttachStderr { - if execOptions.Tty { + if execOptions.TTY { stderr = dockerCli.Out() } else { stderr = dockerCli.Err() @@ -166,16 +159,9 @@ func interactiveExec(ctx context.Context, dockerCli command.Cli, execOptions *cl fillConsoleSize(execOptions, dockerCli) apiClient := dockerCli.Client() - var cs client.ConsoleSize - if execOptions.ConsoleSize != nil { - cs = client.ConsoleSize{ - Height: execOptions.ConsoleSize[0], - Width: execOptions.ConsoleSize[1], - } - } resp, err := apiClient.ExecAttach(ctx, execID, client.ExecAttachOptions{ - TTY: execOptions.Tty, - ConsoleSize: cs, + TTY: execOptions.TTY, + ConsoleSize: client.ConsoleSize{Height: execOptions.ConsoleSize.Height, Width: execOptions.ConsoleSize.Width}, }) if err != nil { return err @@ -193,7 +179,7 @@ func interactiveExec(ctx context.Context, dockerCli command.Cli, execOptions *cl outputStream: out, errorStream: stderr, resp: resp.HijackedResponse, - tty: execOptions.Tty, + tty: execOptions.TTY, detachKeys: execOptions.DetachKeys, } @@ -201,7 +187,7 @@ func interactiveExec(ctx context.Context, dockerCli command.Cli, execOptions *cl }() }() - if execOptions.Tty && dockerCli.In().IsTerminal() { + if execOptions.TTY && dockerCli.In().IsTerminal() { if err := MonitorTtySize(ctx, dockerCli, execID, true); err != nil { _, _ = fmt.Fprintln(dockerCli.Err(), "Error monitoring TTY size:", err) } @@ -237,7 +223,7 @@ func parseExec(execOpts ExecOptions, configFile *configfile.ConfigFile) (*client execOptions := &client.ExecCreateOptions{ User: execOpts.User, Privileged: execOpts.Privileged, - Tty: execOpts.TTY, + TTY: execOpts.TTY, Cmd: execOpts.Command, WorkingDir: execOpts.Workdir, } diff --git a/cli/command/container/exec_test.go b/cli/command/container/exec_test.go index 246500460..0b2d0d043 100644 --- a/cli/command/container/exec_test.go +++ b/cli/command/container/exec_test.go @@ -69,7 +69,7 @@ TWO=2 AttachStdin: true, AttachStdout: true, AttachStderr: true, - Tty: true, + TTY: true, Cmd: []string{"command"}, }, }, @@ -86,7 +86,7 @@ TWO=2 Detach: true, }), expected: client.ExecCreateOptions{ - Tty: true, + TTY: true, Cmd: []string{"command"}, }, }, diff --git a/cli/command/container/prune.go b/cli/command/container/prune.go index 27214e71c..90aa540e2 100644 --- a/cli/command/container/prune.go +++ b/cli/command/container/prune.go @@ -75,7 +75,7 @@ func runPrune(ctx context.Context, dockerCli command.Cli, options pruneOptions) } } - res, err := dockerCli.Client().ContainersPrune(ctx, client.ContainerPruneOptions{ + res, err := dockerCli.Client().ContainerPrune(ctx, client.ContainerPruneOptions{ Filters: pruneFilters, }) if err != nil { diff --git a/cli/command/formatter/buildcache.go b/cli/command/formatter/buildcache.go index 5fa4541ef..3a3c34998 100644 --- a/cli/command/formatter/buildcache.go +++ b/cli/command/formatter/buildcache.go @@ -51,7 +51,7 @@ shared: {{.Shared}} return Format(source) } -func buildCacheSort(buildCache []*build.CacheRecord) { +func buildCacheSort(buildCache []build.CacheRecord) { sort.Slice(buildCache, func(i, j int) bool { lui, luj := buildCache[i].LastUsedAt, buildCache[j].LastUsedAt switch { @@ -70,7 +70,7 @@ func buildCacheSort(buildCache []*build.CacheRecord) { } // BuildCacheWrite renders the context for a list of containers -func BuildCacheWrite(ctx Context, buildCaches []*build.CacheRecord) error { +func BuildCacheWrite(ctx Context, buildCaches []build.CacheRecord) error { render := func(format func(subContext SubContext) error) error { buildCacheSort(buildCaches) for _, bc := range buildCaches { @@ -87,7 +87,7 @@ func BuildCacheWrite(ctx Context, buildCaches []*build.CacheRecord) error { type buildCacheContext struct { HeaderContext trunc bool - v *build.CacheRecord + v build.CacheRecord } func newBuildCacheContext() *buildCacheContext { diff --git a/cli/command/formatter/disk_usage.go b/cli/command/formatter/disk_usage.go index 5ffc64ec0..eb99dcf23 100644 --- a/cli/command/formatter/disk_usage.go +++ b/cli/command/formatter/disk_usage.go @@ -12,6 +12,7 @@ import ( "github.com/moby/moby/api/types/container" "github.com/moby/moby/api/types/image" "github.com/moby/moby/api/types/volume" + "github.com/moby/moby/client" ) const ( @@ -33,13 +34,12 @@ const ( // DiskUsageContext contains disk usage specific information required by the formatter, encapsulate a Context struct. type DiskUsageContext struct { Context - Verbose bool - LayersSize int64 - Images []*image.Summary - Containers []*container.Summary - Volumes []*volume.Volume - BuildCache []*build.CacheRecord - BuilderSize int64 + Verbose bool + + ImageDiskUsage client.ImagesDiskUsage + BuildCacheDiskUsage client.BuildCacheDiskUsage + ContainerDiskUsage client.ContainersDiskUsage + VolumeDiskUsage client.VolumesDiskUsage } func (ctx *DiskUsageContext) startSubsection(format string) (*template.Template, error) { @@ -96,35 +96,49 @@ func (ctx *DiskUsageContext) Write() (err error) { } err = ctx.contextFormat(tmpl, &diskUsageImagesContext{ - totalSize: ctx.LayersSize, - images: ctx.Images, + totalCount: ctx.ImageDiskUsage.TotalImages, + activeCount: ctx.ImageDiskUsage.ActiveImages, + totalSize: ctx.ImageDiskUsage.TotalSize, + reclaimable: ctx.ImageDiskUsage.Reclaimable, + images: ctx.ImageDiskUsage.Items, }) if err != nil { return err } err = ctx.contextFormat(tmpl, &diskUsageContainersContext{ - containers: ctx.Containers, + totalCount: ctx.ContainerDiskUsage.TotalContainers, + activeCount: ctx.ContainerDiskUsage.ActiveContainers, + totalSize: ctx.ContainerDiskUsage.TotalSize, + reclaimable: ctx.ContainerDiskUsage.Reclaimable, + containers: ctx.ContainerDiskUsage.Items, }) if err != nil { return err } err = ctx.contextFormat(tmpl, &diskUsageVolumesContext{ - volumes: ctx.Volumes, + totalCount: ctx.VolumeDiskUsage.TotalVolumes, + activeCount: ctx.VolumeDiskUsage.ActiveVolumes, + totalSize: ctx.VolumeDiskUsage.TotalSize, + reclaimable: ctx.VolumeDiskUsage.Reclaimable, + volumes: ctx.VolumeDiskUsage.Items, }) if err != nil { return err } err = ctx.contextFormat(tmpl, &diskUsageBuilderContext{ - builderSize: ctx.BuilderSize, - buildCache: ctx.BuildCache, + totalCount: ctx.BuildCacheDiskUsage.TotalBuildCacheRecords, + activeCount: ctx.BuildCacheDiskUsage.ActiveBuildCacheRecords, + builderSize: ctx.BuildCacheDiskUsage.TotalSize, + reclaimable: ctx.BuildCacheDiskUsage.Reclaimable, + buildCache: ctx.BuildCacheDiskUsage.Items, }) if err != nil { return err } - diskUsageContainersCtx := diskUsageContainersContext{containers: []*container.Summary{}} + diskUsageContainersCtx := diskUsageContainersContext{containers: []container.Summary{}} diskUsageContainersCtx.Header = SubHeaderContext{ "Type": typeHeader, "TotalCount": totalHeader, @@ -146,18 +160,18 @@ type diskUsageContext struct { func (ctx *DiskUsageContext) verboseWrite() error { duc := &diskUsageContext{ - Images: make([]*imageContext, 0, len(ctx.Images)), - Containers: make([]*ContainerContext, 0, len(ctx.Containers)), - Volumes: make([]*volumeContext, 0, len(ctx.Volumes)), - BuildCache: make([]*buildCacheContext, 0, len(ctx.BuildCache)), + Images: make([]*imageContext, 0, len(ctx.ImageDiskUsage.Items)), + Containers: make([]*ContainerContext, 0, len(ctx.ContainerDiskUsage.Items)), + Volumes: make([]*volumeContext, 0, len(ctx.VolumeDiskUsage.Items)), + BuildCache: make([]*buildCacheContext, 0, len(ctx.BuildCacheDiskUsage.Items)), } trunc := ctx.Format.IsTable() // First images - for _, i := range ctx.Images { + for _, i := range ctx.ImageDiskUsage.Items { repo := "" tag := "" - if len(i.RepoTags) > 0 && !isDangling(*i) { + if len(i.RepoTags) > 0 && !isDangling(i) { // Only show the first tag ref, err := reference.ParseNormalizedNamed(i.RepoTags[0]) if err != nil { @@ -173,25 +187,25 @@ func (ctx *DiskUsageContext) verboseWrite() error { repo: repo, tag: tag, trunc: trunc, - i: *i, + i: i, }) } // Now containers - for _, c := range ctx.Containers { + for _, c := range ctx.ContainerDiskUsage.Items { // Don't display the virtual size c.SizeRootFs = 0 - duc.Containers = append(duc.Containers, &ContainerContext{trunc: trunc, c: *c}) + duc.Containers = append(duc.Containers, &ContainerContext{trunc: trunc, c: c}) } // And volumes - for _, v := range ctx.Volumes { - duc.Volumes = append(duc.Volumes, &volumeContext{v: *v}) + for _, v := range ctx.VolumeDiskUsage.Items { + duc.Volumes = append(duc.Volumes, &volumeContext{v: v}) } // And build cache - buildCacheSort(ctx.BuildCache) - for _, v := range ctx.BuildCache { + buildCacheSort(ctx.BuildCacheDiskUsage.Items) + for _, v := range ctx.BuildCacheDiskUsage.Items { duc.BuildCache = append(duc.BuildCache, &buildCacheContext{v: v, trunc: trunc}) } @@ -248,7 +262,7 @@ func (ctx *DiskUsageContext) verboseWriteTable(duc *diskUsageContext) error { if err != nil { return err } - _, _ = fmt.Fprintf(ctx.Output, "\nBuild cache usage: %s\n\n", units.HumanSize(float64(ctx.BuilderSize))) + _, _ = fmt.Fprintf(ctx.Output, "\nBuild cache usage: %s\n\n", units.HumanSize(float64(ctx.BuildCacheDiskUsage.TotalSize))) for _, v := range duc.BuildCache { if err := ctx.contextFormat(tmpl, v); err != nil { return err @@ -261,8 +275,11 @@ func (ctx *DiskUsageContext) verboseWriteTable(duc *diskUsageContext) error { type diskUsageImagesContext struct { HeaderContext - totalSize int64 - images []*image.Summary + totalSize int64 + reclaimable int64 + totalCount int64 + activeCount int64 + images []image.Summary } func (c *diskUsageImagesContext) MarshalJSON() ([]byte, error) { @@ -274,18 +291,11 @@ func (*diskUsageImagesContext) Type() string { } func (c *diskUsageImagesContext) TotalCount() string { - return strconv.Itoa(len(c.images)) + return strconv.FormatInt(c.totalCount, 10) } func (c *diskUsageImagesContext) Active() string { - used := 0 - for _, i := range c.images { - if i.Containers > 0 { - used++ - } - } - - return strconv.Itoa(used) + return strconv.FormatInt(c.activeCount, 10) } func (c *diskUsageImagesContext) Size() string { @@ -293,27 +303,19 @@ func (c *diskUsageImagesContext) Size() string { } func (c *diskUsageImagesContext) Reclaimable() string { - var used int64 - - for _, i := range c.images { - if i.Containers != 0 { - if i.Size == -1 || i.SharedSize == -1 { - continue - } - used += i.Size - i.SharedSize - } - } - - reclaimable := c.totalSize - used if c.totalSize > 0 { - return fmt.Sprintf("%s (%v%%)", units.HumanSize(float64(reclaimable)), (reclaimable*100)/c.totalSize) + return fmt.Sprintf("%s (%v%%)", units.HumanSize(float64(c.reclaimable)), (c.reclaimable*100)/c.totalSize) } - return units.HumanSize(float64(reclaimable)) + return units.HumanSize(float64(c.reclaimable)) } type diskUsageContainersContext struct { HeaderContext - containers []*container.Summary + totalCount int64 + activeCount int64 + totalSize int64 + reclaimable int64 + containers []container.Summary } func (c *diskUsageContainersContext) MarshalJSON() ([]byte, error) { @@ -325,62 +327,32 @@ func (*diskUsageContainersContext) Type() string { } func (c *diskUsageContainersContext) TotalCount() string { - return strconv.Itoa(len(c.containers)) -} - -func (*diskUsageContainersContext) isActive(ctr container.Summary) bool { - switch ctr.State { - case container.StateRunning, container.StatePaused, container.StateRestarting: - return true - case container.StateCreated, container.StateRemoving, container.StateExited, container.StateDead: - return false - default: - // Unknown state (should never happen). - return false - } + return strconv.FormatInt(c.totalCount, 10) } func (c *diskUsageContainersContext) Active() string { - used := 0 - for _, ctr := range c.containers { - if c.isActive(*ctr) { - used++ - } - } - - return strconv.Itoa(used) + return strconv.FormatInt(c.activeCount, 10) } func (c *diskUsageContainersContext) Size() string { - var size int64 - - for _, ctr := range c.containers { - size += ctr.SizeRw - } - - return units.HumanSize(float64(size)) + return units.HumanSize(float64(c.totalSize)) } func (c *diskUsageContainersContext) Reclaimable() string { - var reclaimable, totalSize int64 - - for _, ctr := range c.containers { - if !c.isActive(*ctr) { - reclaimable += ctr.SizeRw - } - totalSize += ctr.SizeRw + if c.totalSize > 0 { + return fmt.Sprintf("%s (%v%%)", units.HumanSize(float64(c.reclaimable)), (c.reclaimable*100)/c.totalSize) } - if totalSize > 0 { - return fmt.Sprintf("%s (%v%%)", units.HumanSize(float64(reclaimable)), (reclaimable*100)/totalSize) - } - - return units.HumanSize(float64(reclaimable)) + return units.HumanSize(float64(c.reclaimable)) } type diskUsageVolumesContext struct { HeaderContext - volumes []*volume.Volume + totalCount int64 + activeCount int64 + totalSize int64 + reclaimable int64 + volumes []volume.Volume } func (c *diskUsageVolumesContext) MarshalJSON() ([]byte, error) { @@ -392,56 +364,32 @@ func (*diskUsageVolumesContext) Type() string { } func (c *diskUsageVolumesContext) TotalCount() string { - return strconv.Itoa(len(c.volumes)) + return strconv.FormatInt(c.totalCount, 10) } func (c *diskUsageVolumesContext) Active() string { - used := 0 - for _, v := range c.volumes { - if v.UsageData.RefCount > 0 { - used++ - } - } - - return strconv.Itoa(used) + return strconv.FormatInt(c.activeCount, 10) } func (c *diskUsageVolumesContext) Size() string { - var size int64 - - for _, v := range c.volumes { - if v.UsageData.Size != -1 { - size += v.UsageData.Size - } - } - - return units.HumanSize(float64(size)) + return units.HumanSize(float64(c.totalSize)) } func (c *diskUsageVolumesContext) Reclaimable() string { - var reclaimable int64 - var totalSize int64 - - for _, v := range c.volumes { - if v.UsageData.Size != -1 { - if v.UsageData.RefCount == 0 { - reclaimable += v.UsageData.Size - } - totalSize += v.UsageData.Size - } + if c.totalSize > 0 { + return fmt.Sprintf("%s (%v%%)", units.HumanSize(float64(c.reclaimable)), (c.reclaimable*100)/c.totalSize) } - if totalSize > 0 { - return fmt.Sprintf("%s (%v%%)", units.HumanSize(float64(reclaimable)), (reclaimable*100)/totalSize) - } - - return units.HumanSize(float64(reclaimable)) + return units.HumanSize(float64(c.reclaimable)) } type diskUsageBuilderContext struct { HeaderContext + totalCount int64 + activeCount int64 builderSize int64 - buildCache []*build.CacheRecord + reclaimable int64 + buildCache []build.CacheRecord } func (c *diskUsageBuilderContext) MarshalJSON() ([]byte, error) { @@ -453,17 +401,11 @@ func (*diskUsageBuilderContext) Type() string { } func (c *diskUsageBuilderContext) TotalCount() string { - return strconv.Itoa(len(c.buildCache)) + return strconv.FormatInt(c.totalCount, 10) } func (c *diskUsageBuilderContext) Active() string { - numActive := 0 - for _, bc := range c.buildCache { - if bc.InUse { - numActive++ - } - } - return strconv.Itoa(numActive) + return strconv.FormatInt(c.activeCount, 10) } func (c *diskUsageBuilderContext) Size() string { @@ -471,12 +413,5 @@ func (c *diskUsageBuilderContext) Size() string { } func (c *diskUsageBuilderContext) Reclaimable() string { - var inUseBytes int64 - for _, bc := range c.buildCache { - if bc.InUse && !bc.Shared { - inUseBytes += bc.Size - } - } - - return units.HumanSize(float64(c.builderSize - inUseBytes)) + return units.HumanSize(float64(c.reclaimable)) } diff --git a/cli/command/image/client_test.go b/cli/command/image/client_test.go index 2f0e52ef9..fd2ab4a1c 100644 --- a/cli/command/image/client_test.go +++ b/cli/command/image/client_test.go @@ -19,7 +19,7 @@ type fakeClient struct { imagePushFunc func(ref string, options client.ImagePushOptions) (client.ImagePushResponse, error) infoFunc func() (client.SystemInfoResult, error) imagePullFunc func(ref string, options client.ImagePullOptions) (client.ImagePullResponse, error) - imagesPruneFunc func(options client.ImagePruneOptions) (client.ImagePruneResult, error) + imagePruneFunc func(options client.ImagePruneOptions) (client.ImagePruneResult, error) imageLoadFunc func(input io.Reader, options ...client.ImageLoadOption) (client.ImageLoadResult, error) imageListFunc func(options client.ImageListOptions) (client.ImageListResult, error) imageInspectFunc func(img string) (client.ImageInspectResult, error) @@ -47,7 +47,7 @@ func (cli *fakeClient) ImageSave(_ context.Context, images []string, options ... if cli.imageSaveFunc != nil { return cli.imageSaveFunc(images, options...) } - return client.ImageSaveResult{}, nil + return http.NoBody, nil } func (cli *fakeClient) ImageRemove(_ context.Context, img string, options client.ImageRemoveOptions) (client.ImageRemoveResult, error) { @@ -80,9 +80,9 @@ func (cli *fakeClient) ImagePull(_ context.Context, ref string, options client.I return fakeStreamResult{ReadCloser: http.NoBody}, nil } -func (cli *fakeClient) ImagesPrune(_ context.Context, opts client.ImagePruneOptions) (client.ImagePruneResult, error) { - if cli.imagesPruneFunc != nil { - return cli.imagesPruneFunc(opts) +func (cli *fakeClient) ImagePrune(_ context.Context, opts client.ImagePruneOptions) (client.ImagePruneResult, error) { + if cli.imagePruneFunc != nil { + return cli.imagePruneFunc(opts) } return client.ImagePruneResult{}, nil } @@ -91,7 +91,7 @@ func (cli *fakeClient) ImageLoad(_ context.Context, input io.Reader, options ... if cli.imageLoadFunc != nil { return cli.imageLoadFunc(input, options...) } - return client.ImageLoadResult{}, nil + return http.NoBody, nil } func (cli *fakeClient) ImageList(_ context.Context, options client.ImageListOptions) (client.ImageListResult, error) { @@ -112,7 +112,7 @@ func (cli *fakeClient) ImageImport(_ context.Context, source client.ImageImportS if cli.imageImportFunc != nil { return cli.imageImportFunc(source, ref, options) } - return client.ImageImportResult{}, nil + return http.NoBody, nil } func (cli *fakeClient) ImageHistory(_ context.Context, img string, options ...client.ImageHistoryOption) (client.ImageHistoryResult, error) { diff --git a/cli/command/image/import_test.go b/cli/command/image/import_test.go index 131233a23..2b364d3ef 100644 --- a/cli/command/image/import_test.go +++ b/cli/command/image/import_test.go @@ -3,6 +3,7 @@ package image import ( "errors" "io" + "strings" "testing" "github.com/docker/cli/internal/test" @@ -28,7 +29,7 @@ func TestNewImportCommandErrors(t *testing.T) { args: []string{"testdata/import-command-success.input.txt"}, expectedError: "something went wrong", imageImportFunc: func(source client.ImageImportSource, ref string, options client.ImageImportOptions) (client.ImageImportResult, error) { - return client.ImageImportResult{}, errors.New("something went wrong") + return nil, errors.New("something went wrong") }, }, } @@ -68,7 +69,7 @@ func TestNewImportCommandSuccess(t *testing.T) { args: []string{"-", "image:local"}, imageImportFunc: func(source client.ImageImportSource, ref string, options client.ImageImportOptions) (client.ImageImportResult, error) { assert.Check(t, is.Equal("image:local", ref)) - return client.ImageImportResult{}, nil + return io.NopCloser(strings.NewReader("")), nil }, }, { @@ -76,7 +77,7 @@ func TestNewImportCommandSuccess(t *testing.T) { args: []string{"--message", "test message", "-"}, imageImportFunc: func(source client.ImageImportSource, ref string, options client.ImageImportOptions) (client.ImageImportResult, error) { assert.Check(t, is.Equal("test message", options.Message)) - return client.ImageImportResult{}, nil + return io.NopCloser(strings.NewReader("")), nil }, }, { @@ -84,7 +85,7 @@ func TestNewImportCommandSuccess(t *testing.T) { args: []string{"--change", "ENV DEBUG=true", "-"}, imageImportFunc: func(source client.ImageImportSource, ref string, options client.ImageImportOptions) (client.ImageImportResult, error) { assert.Check(t, is.Equal("ENV DEBUG=true", options.Changes[0])) - return client.ImageImportResult{}, nil + return io.NopCloser(strings.NewReader("")), nil }, }, { @@ -92,7 +93,7 @@ func TestNewImportCommandSuccess(t *testing.T) { args: []string{"--change", "ENV DEBUG true", "-"}, imageImportFunc: func(source client.ImageImportSource, ref string, options client.ImageImportOptions) (client.ImageImportResult, error) { assert.Check(t, is.Equal("ENV DEBUG true", options.Changes[0])) - return client.ImageImportResult{}, nil + return io.NopCloser(strings.NewReader("")), nil }, }, } diff --git a/cli/command/image/load_test.go b/cli/command/image/load_test.go index 78c1ca045..9ca0f321d 100644 --- a/cli/command/image/load_test.go +++ b/cli/command/image/load_test.go @@ -4,10 +4,8 @@ import ( "errors" "fmt" "io" - "reflect" "strings" "testing" - "unsafe" "github.com/docker/cli/internal/test" "github.com/moby/moby/client" @@ -39,7 +37,7 @@ func TestNewLoadCommandErrors(t *testing.T) { args: []string{}, expectedError: "something went wrong", imageLoadFunc: func(input io.Reader, options ...client.ImageLoadOption) (client.ImageLoadResult, error) { - return client.ImageLoadResult{}, errors.New("something went wrong") + return nil, errors.New("something went wrong") }, }, { @@ -47,7 +45,7 @@ func TestNewLoadCommandErrors(t *testing.T) { args: []string{"--platform", ""}, expectedError: `invalid platform`, imageLoadFunc: func(input io.Reader, options ...client.ImageLoadOption) (client.ImageLoadResult, error) { - return client.ImageLoadResult{}, nil + return io.NopCloser(strings.NewReader("")), nil }, }, } @@ -75,14 +73,7 @@ func TestNewLoadCommandInvalidInput(t *testing.T) { } func mockImageLoadResult(content string) client.ImageLoadResult { - out := client.ImageLoadResult{} - - // Set unexported field "body" - v := reflect.ValueOf(&out).Elem() - f := v.FieldByName("body") - r := io.NopCloser(strings.NewReader(content)) - reflect.NewAt(f.Type(), unsafe.Pointer(f.UnsafeAddr())).Elem().Set(reflect.ValueOf(r)) - return out + return io.NopCloser(strings.NewReader(content)) } func TestNewLoadCommandSuccess(t *testing.T) { diff --git a/cli/command/image/prune.go b/cli/command/image/prune.go index 453c35f79..ca12bcf59 100644 --- a/cli/command/image/prune.go +++ b/cli/command/image/prune.go @@ -87,7 +87,7 @@ func runPrune(ctx context.Context, dockerCli command.Cli, options pruneOptions) } } - res, err := dockerCli.Client().ImagesPrune(ctx, client.ImagePruneOptions{ + res, err := dockerCli.Client().ImagePrune(ctx, client.ImagePruneOptions{ Filters: pruneFilters, }) if err != nil { diff --git a/cli/command/image/prune_test.go b/cli/command/image/prune_test.go index eeb68a3ab..a3c0ceac3 100644 --- a/cli/command/image/prune_test.go +++ b/cli/command/image/prune_test.go @@ -18,10 +18,10 @@ import ( func TestNewPruneCommandErrors(t *testing.T) { testCases := []struct { - name string - args []string - expectedError string - imagesPruneFunc func(client.ImagePruneOptions) (client.ImagePruneResult, error) + name string + args []string + expectedError string + imagePruneFunc func(client.ImagePruneOptions) (client.ImagePruneResult, error) }{ { name: "wrong-args", @@ -32,7 +32,7 @@ func TestNewPruneCommandErrors(t *testing.T) { name: "prune-error", args: []string{"--force"}, expectedError: "something went wrong", - imagesPruneFunc: func(client.ImagePruneOptions) (client.ImagePruneResult, error) { + imagePruneFunc: func(client.ImagePruneOptions) (client.ImagePruneResult, error) { return client.ImagePruneResult{}, errors.New("something went wrong") }, }, @@ -40,7 +40,7 @@ func TestNewPruneCommandErrors(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { cmd := newPruneCommand(test.NewFakeCli(&fakeClient{ - imagesPruneFunc: tc.imagesPruneFunc, + imagePruneFunc: tc.imagePruneFunc, })) cmd.SetOut(io.Discard) cmd.SetErr(io.Discard) @@ -52,14 +52,14 @@ func TestNewPruneCommandErrors(t *testing.T) { func TestNewPruneCommandSuccess(t *testing.T) { testCases := []struct { - name string - args []string - imagesPruneFunc func(client.ImagePruneOptions) (client.ImagePruneResult, error) + name string + args []string + imagePruneFunc func(client.ImagePruneOptions) (client.ImagePruneResult, error) }{ { name: "all", args: []string{"--all"}, - imagesPruneFunc: func(opts client.ImagePruneOptions) (client.ImagePruneResult, error) { + imagePruneFunc: func(opts client.ImagePruneOptions) (client.ImagePruneResult, error) { assert.Check(t, opts.Filters["dangling"]["false"]) return client.ImagePruneResult{}, nil }, @@ -67,7 +67,7 @@ func TestNewPruneCommandSuccess(t *testing.T) { { name: "force-deleted", args: []string{"--force"}, - imagesPruneFunc: func(opts client.ImagePruneOptions) (client.ImagePruneResult, error) { + imagePruneFunc: func(opts client.ImagePruneOptions) (client.ImagePruneResult, error) { assert.Check(t, opts.Filters["dangling"]["true"]) return client.ImagePruneResult{ Report: image.PruneReport{ @@ -80,7 +80,7 @@ func TestNewPruneCommandSuccess(t *testing.T) { { name: "label-filter", args: []string{"--force", "--filter", "label=foobar"}, - imagesPruneFunc: func(opts client.ImagePruneOptions) (client.ImagePruneResult, error) { + imagePruneFunc: func(opts client.ImagePruneOptions) (client.ImagePruneResult, error) { assert.Check(t, opts.Filters["label"]["foobar"]) return client.ImagePruneResult{}, nil }, @@ -88,7 +88,7 @@ func TestNewPruneCommandSuccess(t *testing.T) { { name: "force-untagged", args: []string{"--force"}, - imagesPruneFunc: func(opts client.ImagePruneOptions) (client.ImagePruneResult, error) { + imagePruneFunc: func(opts client.ImagePruneOptions) (client.ImagePruneResult, error) { assert.Check(t, opts.Filters["dangling"]["true"]) return client.ImagePruneResult{ Report: image.PruneReport{ @@ -101,7 +101,7 @@ func TestNewPruneCommandSuccess(t *testing.T) { } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - cli := test.NewFakeCli(&fakeClient{imagesPruneFunc: tc.imagesPruneFunc}) + cli := test.NewFakeCli(&fakeClient{imagePruneFunc: tc.imagePruneFunc}) // when prompted, answer "Y" to confirm the prune. // will not be prompted if --force is used. cli.SetIn(streams.NewIn(io.NopCloser(strings.NewReader("Y\n")))) @@ -120,8 +120,8 @@ func TestPrunePromptTermination(t *testing.T) { t.Cleanup(cancel) cli := test.NewFakeCli(&fakeClient{ - imagesPruneFunc: func(client.ImagePruneOptions) (client.ImagePruneResult, error) { - return client.ImagePruneResult{}, errors.New("fakeClient imagesPruneFunc should not be called") + imagePruneFunc: func(client.ImagePruneOptions) (client.ImagePruneResult, error) { + return client.ImagePruneResult{}, errors.New("fakeClient imagePruneFunc should not be called") }, }) cmd := newPruneCommand(cli) diff --git a/cli/command/image/save_test.go b/cli/command/image/save_test.go index 2165a19d4..dbdad6c32 100644 --- a/cli/command/image/save_test.go +++ b/cli/command/image/save_test.go @@ -38,7 +38,7 @@ func TestNewSaveCommandErrors(t *testing.T) { isTerminal: false, expectedError: "error saving image", imageSaveFunc: func(images []string, options ...client.ImageSaveOption) (client.ImageSaveResult, error) { - return client.ImageSaveResult{}, errors.New("error saving image") + return nil, errors.New("error saving image") }, }, { @@ -83,7 +83,7 @@ func TestNewSaveCommandSuccess(t *testing.T) { imageSaveFunc: func(images []string, options ...client.ImageSaveOption) (client.ImageSaveResult, error) { assert.Assert(t, is.Len(images, 1)) assert.Check(t, is.Equal("arg1", images[0])) - return client.ImageSaveResult{}, nil + return io.NopCloser(strings.NewReader("")), nil }, deferredFunc: func() { _ = os.Remove("save_tmp_file") @@ -96,7 +96,7 @@ func TestNewSaveCommandSuccess(t *testing.T) { assert.Assert(t, is.Len(images, 2)) assert.Check(t, is.Equal("arg1", images[0])) assert.Check(t, is.Equal("arg2", images[1])) - return client.ImageSaveResult{}, nil + return io.NopCloser(strings.NewReader("")), nil }, }, { @@ -108,7 +108,7 @@ func TestNewSaveCommandSuccess(t *testing.T) { // FIXME(thaJeztah): need to find appropriate way to test the result of "ImageHistoryWithPlatform" being applied assert.Check(t, len(options) > 0) // can be 1 or two depending on whether a terminal is attached :/ // assert.Check(t, is.Contains(options, client.ImageHistoryWithPlatform(ocispec.Platform{OS: "linux", Architecture: "amd64"}))) - return client.ImageSaveResult{}, nil + return io.NopCloser(strings.NewReader("")), nil }, }, { @@ -118,7 +118,7 @@ func TestNewSaveCommandSuccess(t *testing.T) { assert.Assert(t, is.Len(images, 1)) assert.Check(t, is.Equal("arg1", images[0])) assert.Check(t, len(options) > 0) // can be 1 or 2 depending on whether a terminal is attached :/ - return client.ImageSaveResult{}, nil + return io.NopCloser(strings.NewReader("")), nil }, }, { @@ -128,7 +128,7 @@ func TestNewSaveCommandSuccess(t *testing.T) { assert.Assert(t, is.Len(images, 1)) assert.Check(t, is.Equal("arg1", images[0])) assert.Check(t, len(options) > 0) // can be 1 or 2 depending on whether a terminal is attached :/ - return client.ImageSaveResult{}, nil + return io.NopCloser(strings.NewReader("")), nil }, }, } diff --git a/cli/command/network/prune.go b/cli/command/network/prune.go index 1aa761b25..2d7dc8571 100644 --- a/cli/command/network/prune.go +++ b/cli/command/network/prune.go @@ -72,7 +72,7 @@ func runPrune(ctx context.Context, dockerCli command.Cli, options pruneOptions) } } - res, err := dockerCli.Client().NetworksPrune(ctx, client.NetworkPruneOptions{ + res, err := dockerCli.Client().NetworkPrune(ctx, client.NetworkPruneOptions{ Filters: pruneFilters, }) if err != nil { diff --git a/cli/command/service/formatter.go b/cli/command/service/formatter.go index 893628e55..1ec5263de 100644 --- a/cli/command/service/formatter.go +++ b/cli/command/service/formatter.go @@ -382,11 +382,11 @@ func (ctx *serviceInspectContext) UpdateDelay() time.Duration { } func (ctx *serviceInspectContext) UpdateOnFailure() string { - return ctx.Service.Spec.UpdateConfig.FailureAction + return string(ctx.Service.Spec.UpdateConfig.FailureAction) } func (ctx *serviceInspectContext) UpdateOrder() string { - return ctx.Service.Spec.UpdateConfig.Order + return string(ctx.Service.Spec.UpdateConfig.Order) } func (ctx *serviceInspectContext) HasUpdateMonitor() bool { @@ -418,7 +418,7 @@ func (ctx *serviceInspectContext) RollbackDelay() time.Duration { } func (ctx *serviceInspectContext) RollbackOnFailure() string { - return ctx.Service.Spec.RollbackConfig.FailureAction + return string(ctx.Service.Spec.RollbackConfig.FailureAction) } func (ctx *serviceInspectContext) HasRollbackMonitor() bool { @@ -434,7 +434,7 @@ func (ctx *serviceInspectContext) RollbackMaxFailureRatio() float32 { } func (ctx *serviceInspectContext) RollbackOrder() string { - return ctx.Service.Spec.RollbackConfig.Order + return string(ctx.Service.Spec.RollbackConfig.Order) } func (ctx *serviceInspectContext) ContainerImage() string { diff --git a/cli/command/service/opts.go b/cli/command/service/opts.go index e514492e3..36c8dbb64 100644 --- a/cli/command/service/opts.go +++ b/cli/command/service/opts.go @@ -164,9 +164,9 @@ func updateConfigFromDefaults(defaultUpdateConfig *api.UpdateConfig) *swarm.Upda Parallelism: defaultUpdateConfig.Parallelism, Delay: defaultUpdateConfig.Delay, Monitor: defaultMonitor, - FailureAction: defaultFailureAction, + FailureAction: swarm.FailureAction(defaultFailureAction), MaxFailureRatio: defaultUpdateConfig.MaxFailureRatio, - Order: defaultOrder(defaultUpdateConfig.Order), + Order: swarm.UpdateOrder(defaultOrder(defaultUpdateConfig.Order)), } } @@ -187,13 +187,13 @@ func (o updateOptions) updateConfig(flags *pflag.FlagSet) *swarm.UpdateConfig { updateConfig.Monitor = o.monitor } if flags.Changed(flagUpdateFailureAction) { - updateConfig.FailureAction = o.onFailure + updateConfig.FailureAction = swarm.FailureAction(o.onFailure) } if flags.Changed(flagUpdateMaxFailureRatio) { updateConfig.MaxFailureRatio = o.maxFailureRatio.Value() } if flags.Changed(flagUpdateOrder) { - updateConfig.Order = o.order + updateConfig.Order = swarm.UpdateOrder(o.order) } return updateConfig @@ -216,13 +216,13 @@ func (o updateOptions) rollbackConfig(flags *pflag.FlagSet) *swarm.UpdateConfig updateConfig.Monitor = o.monitor } if flags.Changed(flagRollbackFailureAction) { - updateConfig.FailureAction = o.onFailure + updateConfig.FailureAction = swarm.FailureAction(o.onFailure) } if flags.Changed(flagRollbackMaxFailureRatio) { updateConfig.MaxFailureRatio = o.maxFailureRatio.Value() } if flags.Changed(flagRollbackOrder) { - updateConfig.Order = o.order + updateConfig.Order = swarm.UpdateOrder(o.order) } return updateConfig @@ -299,9 +299,9 @@ func defaultRestartCondition() swarm.RestartPolicyCondition { func defaultOrder(order api.UpdateConfig_UpdateOrder) string { switch order { case api.UpdateConfig_STOP_FIRST: - return swarm.UpdateOrderStopFirst + return string(swarm.UpdateOrderStopFirst) case api.UpdateConfig_START_FIRST: - return swarm.UpdateOrderStartFirst + return string(swarm.UpdateOrderStartFirst) default: return "" } diff --git a/cli/command/service/opts_test.go b/cli/command/service/opts_test.go index 8ebe9d50c..563c42e9a 100644 --- a/cli/command/service/opts_test.go +++ b/cli/command/service/opts_test.go @@ -269,16 +269,16 @@ func TestToServiceUpdateRollback(t *testing.T) { flags.Set("update-parallelism", "23") flags.Set("update-delay", "34s") flags.Set("update-monitor", "54321ns") - flags.Set("update-failure-action", swarm.UpdateFailureActionPause) + flags.Set("update-failure-action", string(swarm.UpdateFailureActionPause)) flags.Set("update-max-failure-ratio", "0.6") - flags.Set("update-order", swarm.UpdateOrderStopFirst) + flags.Set("update-order", string(swarm.UpdateOrderStopFirst)) flags.Set("rollback-parallelism", "12") flags.Set("rollback-delay", "23s") flags.Set("rollback-monitor", "12345ns") - flags.Set("rollback-failure-action", swarm.UpdateFailureActionContinue) + flags.Set("rollback-failure-action", string(swarm.UpdateFailureActionContinue)) flags.Set("rollback-max-failure-ratio", "0.5") - flags.Set("rollback-order", swarm.UpdateOrderStartFirst) + flags.Set("rollback-order", string(swarm.UpdateOrderStartFirst)) o := newServiceOptions() o.mode = "replicated" @@ -286,17 +286,17 @@ func TestToServiceUpdateRollback(t *testing.T) { parallelism: 23, delay: 34 * time.Second, monitor: 54321 * time.Nanosecond, - onFailure: swarm.UpdateFailureActionPause, + onFailure: string(swarm.UpdateFailureActionPause), maxFailureRatio: 0.6, - order: swarm.UpdateOrderStopFirst, + order: string(swarm.UpdateOrderStopFirst), } o.rollback = updateOptions{ parallelism: 12, delay: 23 * time.Second, monitor: 12345 * time.Nanosecond, - onFailure: swarm.UpdateFailureActionContinue, + onFailure: string(swarm.UpdateFailureActionContinue), maxFailureRatio: 0.5, - order: swarm.UpdateOrderStartFirst, + order: string(swarm.UpdateOrderStartFirst), } service, err := o.ToService(context.Background(), &fakeClient{}, flags) @@ -307,18 +307,18 @@ func TestToServiceUpdateRollback(t *testing.T) { func TestToServiceUpdateRollbackOrder(t *testing.T) { flags := newCreateCommand(nil).Flags() - flags.Set("update-order", swarm.UpdateOrderStartFirst) - flags.Set("rollback-order", swarm.UpdateOrderStartFirst) + flags.Set("update-order", string(swarm.UpdateOrderStartFirst)) + flags.Set("rollback-order", string(swarm.UpdateOrderStartFirst)) o := newServiceOptions() o.mode = "replicated" - o.update = updateOptions{order: swarm.UpdateOrderStartFirst} - o.rollback = updateOptions{order: swarm.UpdateOrderStartFirst} + o.update = updateOptions{order: string(swarm.UpdateOrderStartFirst)} + o.rollback = updateOptions{order: string(swarm.UpdateOrderStartFirst)} service, err := o.ToService(context.Background(), &fakeClient{}, flags) assert.NilError(t, err) - assert.Check(t, is.Equal(service.UpdateConfig.Order, o.update.order)) - assert.Check(t, is.Equal(service.RollbackConfig.Order, o.rollback.order)) + assert.Check(t, is.Equal(string(service.UpdateConfig.Order), o.update.order)) + assert.Check(t, is.Equal(string(service.RollbackConfig.Order), o.rollback.order)) } func TestToServiceMaxReplicasGlobalModeConflict(t *testing.T) { diff --git a/cli/command/service/update.go b/cli/command/service/update.go index abfc69e0e..54e83d442 100644 --- a/cli/command/service/update.go +++ b/cli/command/service/update.go @@ -228,7 +228,7 @@ func runUpdate(ctx context.Context, dockerCLI command.Cli, flags *pflag.FlagSet, } updateOpts.EncodedRegistryAuth = encodedAuth } else { - registryAuthFrom = swarm.RegistryAuthFromSpec + registryAuthFrom = string(swarm.RegistryAuthFromSpec) } response, err := apiClient.ServiceUpdate(ctx, res.Service.ID, client.ServiceUpdateOptions{ @@ -236,7 +236,7 @@ func runUpdate(ctx context.Context, dockerCLI command.Cli, flags *pflag.FlagSet, Spec: *spec, EncodedRegistryAuth: encodedAuth, - RegistryAuthFrom: registryAuthFrom, + RegistryAuthFrom: swarm.RegistryAuthSource(registryAuthFrom), Rollback: rollbackAction, }) if err != nil { @@ -433,9 +433,15 @@ func updateService(ctx context.Context, apiClient client.NetworkAPIClient, flags updateUint64(flagUpdateParallelism, &spec.UpdateConfig.Parallelism) updateDuration(flagUpdateDelay, &spec.UpdateConfig.Delay) updateDuration(flagUpdateMonitor, &spec.UpdateConfig.Monitor) - updateString(flagUpdateFailureAction, &spec.UpdateConfig.FailureAction) + if flags.Changed(flagUpdateFailureAction) { + value, _ := flags.GetString(flagUpdateFailureAction) + spec.UpdateConfig.FailureAction = swarm.FailureAction(value) + } updateFloatValue(flagUpdateMaxFailureRatio, &spec.UpdateConfig.MaxFailureRatio) - updateString(flagUpdateOrder, &spec.UpdateConfig.Order) + if flags.Changed(flagUpdateOrder) { + value, _ := flags.GetString(flagUpdateOrder) + spec.UpdateConfig.Order = swarm.UpdateOrder(value) + } } if anyChanged(flags, flagRollbackParallelism, flagRollbackDelay, flagRollbackMonitor, flagRollbackFailureAction, flagRollbackMaxFailureRatio, flagRollbackOrder) { @@ -445,9 +451,15 @@ func updateService(ctx context.Context, apiClient client.NetworkAPIClient, flags updateUint64(flagRollbackParallelism, &spec.RollbackConfig.Parallelism) updateDuration(flagRollbackDelay, &spec.RollbackConfig.Delay) updateDuration(flagRollbackMonitor, &spec.RollbackConfig.Monitor) - updateString(flagRollbackFailureAction, &spec.RollbackConfig.FailureAction) + if flags.Changed(flagRollbackFailureAction) { + value, _ := flags.GetString(flagRollbackFailureAction) + spec.RollbackConfig.FailureAction = swarm.FailureAction(value) + } updateFloatValue(flagRollbackMaxFailureRatio, &spec.RollbackConfig.MaxFailureRatio) - updateString(flagRollbackOrder, &spec.RollbackConfig.Order) + if flags.Changed(flagRollbackOrder) { + value, _ := flags.GetString(flagRollbackOrder) + spec.RollbackConfig.Order = swarm.UpdateOrder(value) + } } if flags.Changed(flagEndpointMode) { diff --git a/cli/command/system/client_test.go b/cli/command/system/client_test.go index de58b9191..898edf916 100644 --- a/cli/command/system/client_test.go +++ b/cli/command/system/client_test.go @@ -38,7 +38,7 @@ func (cli *fakeClient) ContainerList(ctx context.Context, options client.Contain return client.ContainerListResult{}, nil } -func (cli *fakeClient) ContainersPrune(ctx context.Context, opts client.ContainerPruneOptions) (client.ContainerPruneResult, error) { +func (cli *fakeClient) ContainerPrune(ctx context.Context, opts client.ContainerPruneOptions) (client.ContainerPruneResult, error) { if cli.containerPruneFunc != nil { return cli.containerPruneFunc(ctx, opts) } diff --git a/cli/command/system/df.go b/cli/command/system/df.go index 7e9f1d17c..2c689da02 100644 --- a/cli/command/system/df.go +++ b/cli/command/system/df.go @@ -42,7 +42,9 @@ func newDiskUsageCommand(dockerCLI command.Cli) *cobra.Command { func runDiskUsage(ctx context.Context, dockerCli command.Cli, opts diskUsageOptions) error { // TODO expose types.DiskUsageOptions.Types as flag on the command-line and/or as separate commands (docker container df / docker container usage) - du, err := dockerCli.Client().DiskUsage(ctx, client.DiskUsageOptions{}) + du, err := dockerCli.Client().DiskUsage(ctx, client.DiskUsageOptions{ + Verbose: opts.verbose, + }) if err != nil { return err } @@ -52,25 +54,16 @@ func runDiskUsage(ctx context.Context, dockerCli command.Cli, opts diskUsageOpti format = formatter.TableFormatKey } - var bsz int64 - for _, bc := range du.BuildCache { - if !bc.Shared { - bsz += bc.Size - } - } - duCtx := formatter.DiskUsageContext{ Context: formatter.Context{ Output: dockerCli.Out(), Format: formatter.NewDiskUsageFormat(format, opts.verbose), }, - LayersSize: du.LayersSize, - BuilderSize: bsz, - BuildCache: du.BuildCache, - Images: du.Images, - Containers: du.Containers, - Volumes: du.Volumes, - Verbose: opts.verbose, + Verbose: opts.verbose, + ImageDiskUsage: du.Images, + BuildCacheDiskUsage: du.BuildCache, + ContainerDiskUsage: du.Containers, + VolumeDiskUsage: du.Volumes, } return duCtx.Write() diff --git a/cli/command/system/info.go b/cli/command/system/info.go index 84ea71212..2dba53511 100644 --- a/cli/command/system/info.go +++ b/cli/command/system/info.go @@ -202,9 +202,7 @@ func prettyPrintInfo(streams command.Streams, info dockerInfo) error { fprintln(streams.Out()) fprintln(streams.Out(), "Server:") if info.Info != nil { - for _, err := range prettyPrintServerInfo(streams, &info) { - info.ServerErrors = append(info.ServerErrors, err.Error()) - } + prettyPrintServerInfo(streams, &info) } for _, err := range info.ServerErrors { fprintln(streams.Err(), "ERROR:", err) @@ -240,8 +238,7 @@ func prettyPrintClientInfo(streams command.Streams, info clientInfo) { } //nolint:gocyclo -func prettyPrintServerInfo(streams command.Streams, info *dockerInfo) []error { - var errs []error +func prettyPrintServerInfo(streams command.Streams, info *dockerInfo) { output := streams.Out() fprintln(output, " Containers:", info.Containers) @@ -306,17 +303,14 @@ func prettyPrintServerInfo(streams command.Streams, info *dockerInfo) []error { fprintln(output, " containerd version:", info.ContainerdCommit.ID) fprintln(output, " runc version:", info.RuncCommit.ID) fprintln(output, " init version:", info.InitCommit.ID) - if len(info.SecurityOptions) != 0 { - if kvs, err := security.DecodeOptions(info.SecurityOptions); err != nil { - errs = append(errs, err) - } else { - fprintln(output, " Security Options:") - for _, so := range kvs { - fprintln(output, " "+so.Name) - for _, o := range so.Options { - if o.Key == "profile" { - fprintln(output, " Profile:", o.Value) - } + secopts := security.DecodeOptions(info.SecurityOptions) + if len(secopts) != 0 { + fprintln(output, " Security Options:") + for _, so := range secopts { + fprintln(output, " "+so.Name) + for _, o := range so.Options { + if o.Key == "profile" { + fprintln(output, " Profile:", o.Value) } } } @@ -407,8 +401,6 @@ func prettyPrintServerInfo(streams command.Streams, info *dockerInfo) []error { for _, w := range info.Warnings { fprintln(streams.Err(), w) } - - return errs } //nolint:gocyclo diff --git a/cli/command/system/testdata/docker-info-badsec-stderr.golden b/cli/command/system/testdata/docker-info-badsec-stderr.golden index 72a09117c..7cd534e3c 100644 --- a/cli/command/system/testdata/docker-info-badsec-stderr.golden +++ b/cli/command/system/testdata/docker-info-badsec-stderr.golden @@ -1,2 +1 @@ ERROR: a server error occurred -ERROR: invalid empty security option diff --git a/cli/command/volume/client_test.go b/cli/command/volume/client_test.go index cd11a3d7d..f9b44d3e6 100644 --- a/cli/command/volume/client_test.go +++ b/cli/command/volume/client_test.go @@ -36,7 +36,7 @@ func (c *fakeClient) VolumeList(_ context.Context, options client.VolumeListOpti return client.VolumeListResult{}, nil } -func (c *fakeClient) VolumesPrune(_ context.Context, opts client.VolumePruneOptions) (client.VolumePruneResult, error) { +func (c *fakeClient) VolumePrune(_ context.Context, opts client.VolumePruneOptions) (client.VolumePruneResult, error) { if c.volumePruneFunc != nil { return c.volumePruneFunc(opts) } diff --git a/cli/command/volume/prune.go b/cli/command/volume/prune.go index 6fbef2f85..53c218203 100644 --- a/cli/command/volume/prune.go +++ b/cli/command/volume/prune.go @@ -90,7 +90,7 @@ func runPrune(ctx context.Context, dockerCli command.Cli, options pruneOptions) } } - res, err := dockerCli.Client().VolumesPrune(ctx, client.VolumePruneOptions{ + res, err := dockerCli.Client().VolumePrune(ctx, client.VolumePruneOptions{ Filters: pruneFilters, }) if err != nil { diff --git a/cli/compose/convert/service.go b/cli/compose/convert/service.go index 746a9656f..5fbc289d8 100644 --- a/cli/compose/convert/service.go +++ b/cli/compose/convert/service.go @@ -507,10 +507,10 @@ func convertUpdateConfig(source *composetypes.UpdateConfig) *swarm.UpdateConfig return &swarm.UpdateConfig{ Parallelism: parallel, Delay: time.Duration(source.Delay), - FailureAction: source.FailureAction, + FailureAction: swarm.FailureAction(source.FailureAction), Monitor: time.Duration(source.Monitor), MaxFailureRatio: source.MaxFailureRatio, - Order: source.Order, + Order: swarm.UpdateOrder(source.Order), } } diff --git a/cli/compose/convert/service_test.go b/cli/compose/convert/service_test.go index 1b8452a2d..b5999fe6e 100644 --- a/cli/compose/convert/service_test.go +++ b/cli/compose/convert/service_test.go @@ -427,19 +427,19 @@ func TestConvertCredentialSpec(t *testing.T) { func TestConvertUpdateConfigOrder(t *testing.T) { // test default behavior updateConfig := convertUpdateConfig(&composetypes.UpdateConfig{}) - assert.Check(t, is.Equal("", updateConfig.Order)) + assert.Check(t, is.Equal("", string(updateConfig.Order))) // test start-first updateConfig = convertUpdateConfig(&composetypes.UpdateConfig{ Order: "start-first", }) - assert.Check(t, is.Equal(updateConfig.Order, "start-first")) + assert.Check(t, is.Equal(string(updateConfig.Order), "start-first")) // test stop-first updateConfig = convertUpdateConfig(&composetypes.UpdateConfig{ Order: "stop-first", }) - assert.Check(t, is.Equal(updateConfig.Order, "stop-first")) + assert.Check(t, is.Equal(string(updateConfig.Order), "stop-first")) } func TestConvertFileObject(t *testing.T) { diff --git a/vendor.mod b/vendor.mod index f7676e761..18c571bc6 100644 --- a/vendor.mod +++ b/vendor.mod @@ -28,8 +28,8 @@ require ( github.com/google/uuid v1.6.0 github.com/mattn/go-runewidth v0.0.19 github.com/moby/go-archive v0.1.0 - github.com/moby/moby/api v1.52.0-beta.4 - github.com/moby/moby/client v0.1.0-beta.3 + github.com/moby/moby/api v1.52.0-beta.4.0.20251106210608-f7fd9c315acf + github.com/moby/moby/client v0.1.0-beta.3.0.20251106221347-217fd7890581 github.com/moby/patternmatcher v0.6.0 github.com/moby/swarmkit/v2 v2.1.1 github.com/moby/sys/atomicwriter v0.1.0 @@ -105,3 +105,5 @@ require ( google.golang.org/grpc v1.72.2 // indirect google.golang.org/protobuf v1.36.9 // indirect ) + +replace github.com/moby/moby/api => github.com/moby/moby/api v1.52.0-beta.4.0.20251106221347-217fd7890581 diff --git a/vendor.sum b/vendor.sum index 393a061ec..a70a065fb 100644 --- a/vendor.sum +++ b/vendor.sum @@ -113,10 +113,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.4 h1:p05ztW787RcwcQx8YFcda1sb0ExqxDLHTjBQA3Jq6BY= -github.com/moby/moby/api v1.52.0-beta.4/go.mod h1:v0K/motq8oWmx+rtApG1rBTIpQ8KUONUjpf+U73gags= -github.com/moby/moby/client v0.1.0-beta.3 h1:y/ed2VpRmW8As56TFOaXF2zVSli38IS032zAAGt6kcI= -github.com/moby/moby/client v0.1.0-beta.3/go.mod h1:dvvUX065yfMkFXvPk14AXVz8wmew2MdeXJ/rcYEPr7g= +github.com/moby/moby/api v1.52.0-beta.4.0.20251106221347-217fd7890581 h1:Qa8MTSJUIV0K8sbG3RgJeEp5Q0GzWIVCKtalKpWewPo= +github.com/moby/moby/api v1.52.0-beta.4.0.20251106221347-217fd7890581/go.mod h1:v0K/motq8oWmx+rtApG1rBTIpQ8KUONUjpf+U73gags= +github.com/moby/moby/client v0.1.0-beta.3.0.20251106221347-217fd7890581 h1:pW39Fs9C96BH5ZRzwYLrIXNtFLuD0V72BkJHpwtcC/E= +github.com/moby/moby/client v0.1.0-beta.3.0.20251106221347-217fd7890581/go.mod h1:py3jWFsk61C4EZ1Cv8zgbv7nsJ6NjGzZFVdPoSSJ3NE= 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.1 h1:yvTJ8MMCc3f0qTA44J6R59EZ5yZawdYopkpuLk4+ICU= diff --git a/vendor/github.com/moby/moby/api/types/build/disk_usage.go b/vendor/github.com/moby/moby/api/types/build/disk_usage.go new file mode 100644 index 000000000..a55e8545b --- /dev/null +++ b/vendor/github.com/moby/moby/api/types/build/disk_usage.go @@ -0,0 +1,36 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package build + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +// DiskUsage represents system data usage for build cache resources. +// +// swagger:model DiskUsage +type DiskUsage struct { + + // Count of active build cache records. + // + // Example: 1 + ActiveBuildCacheRecords int64 `json:"ActiveBuildCacheRecords,omitempty"` + + // List of build cache records. + // + Items []CacheRecord `json:"Items,omitempty"` + + // Disk space that can be reclaimed by removing inactive build cache records. + // + // Example: 12345678 + Reclaimable int64 `json:"Reclaimable,omitempty"` + + // Count of all build cache records. + // + // Example: 4 + TotalBuildCacheRecords int64 `json:"TotalBuildCacheRecords,omitempty"` + + // Disk space in use by build cache records. + // + // Example: 98765432 + TotalSize int64 `json:"TotalSize,omitempty"` +} diff --git a/vendor/github.com/moby/moby/api/types/container/disk_usage.go b/vendor/github.com/moby/moby/api/types/container/disk_usage.go new file mode 100644 index 000000000..dc0274b78 --- /dev/null +++ b/vendor/github.com/moby/moby/api/types/container/disk_usage.go @@ -0,0 +1,36 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package container + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +// DiskUsage represents system data usage information for container resources. +// +// swagger:model DiskUsage +type DiskUsage struct { + + // Count of active containers. + // + // Example: 1 + ActiveContainers int64 `json:"ActiveContainers,omitempty"` + + // List of container summaries. + // + Items []Summary `json:"Items,omitempty"` + + // Disk space that can be reclaimed by removing inactive containers. + // + // Example: 12345678 + Reclaimable int64 `json:"Reclaimable,omitempty"` + + // Count of all containers. + // + // Example: 4 + TotalContainers int64 `json:"TotalContainers,omitempty"` + + // Disk space in use by containers. + // + // Example: 98765432 + TotalSize int64 `json:"TotalSize,omitempty"` +} diff --git a/vendor/github.com/moby/moby/api/types/image/disk_usage.go b/vendor/github.com/moby/moby/api/types/image/disk_usage.go new file mode 100644 index 000000000..568ea71cf --- /dev/null +++ b/vendor/github.com/moby/moby/api/types/image/disk_usage.go @@ -0,0 +1,36 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package image + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +// DiskUsage represents system data usage for image resources. +// +// swagger:model DiskUsage +type DiskUsage struct { + + // Count of active images. + // + // Example: 1 + ActiveImages int64 `json:"ActiveImages,omitempty"` + + // List of image summaries. + // + Items []Summary `json:"Items,omitempty"` + + // Disk space that can be reclaimed by removing unused images. + // + // Example: 12345678 + Reclaimable int64 `json:"Reclaimable,omitempty"` + + // Count of all images. + // + // Example: 4 + TotalImages int64 `json:"TotalImages,omitempty"` + + // Disk space in use by images. + // + // Example: 98765432 + TotalSize int64 `json:"TotalSize,omitempty"` +} diff --git a/vendor/github.com/moby/moby/api/types/swarm/service.go b/vendor/github.com/moby/moby/api/types/swarm/service.go index b5a6b4f89..0b678dea3 100644 --- a/vendor/github.com/moby/moby/api/types/swarm/service.go +++ b/vendor/github.com/moby/moby/api/types/swarm/service.go @@ -106,18 +106,27 @@ type ReplicatedJob struct { // This type is deliberately empty. type GlobalJob struct{} +// FailureAction is the action to perform when updating a service fails. +type FailureAction string + const ( // UpdateFailureActionPause PAUSE - UpdateFailureActionPause = "pause" + UpdateFailureActionPause FailureAction = "pause" // UpdateFailureActionContinue CONTINUE - UpdateFailureActionContinue = "continue" + UpdateFailureActionContinue FailureAction = "continue" // UpdateFailureActionRollback ROLLBACK - UpdateFailureActionRollback = "rollback" + UpdateFailureActionRollback FailureAction = "rollback" +) +// UpdateOrder is the order of operations when rolling out or rolling back +// an updated tasks for a service. +type UpdateOrder string + +const ( // UpdateOrderStopFirst STOP_FIRST - UpdateOrderStopFirst = "stop-first" + UpdateOrderStopFirst UpdateOrder = "stop-first" // UpdateOrderStartFirst START_FIRST - UpdateOrderStartFirst = "start-first" + UpdateOrderStartFirst UpdateOrder = "start-first" ) // UpdateConfig represents the update configuration. @@ -130,7 +139,7 @@ type UpdateConfig struct { Delay time.Duration `json:",omitempty"` // FailureAction is the action to take when an update failures. - FailureAction string `json:",omitempty"` + FailureAction FailureAction `json:",omitempty"` // Monitor indicates how long to monitor a task for failure after it is // created. If the task fails by ending up in one of the states @@ -156,7 +165,7 @@ type UpdateConfig struct { // Order indicates the order of operations when rolling out an updated // task. Either the old task is shut down before the new task is // started, or the new task is started before the old task is shut down. - Order string + Order UpdateOrder } // ServiceStatus represents the number of running tasks in a service and the @@ -198,8 +207,12 @@ type JobStatus struct { LastExecution time.Time `json:",omitempty"` } +// RegistryAuthSource defines options for the "registryAuthFrom" query parameter +// on service update. +type RegistryAuthSource string + // Values for RegistryAuthFrom in ServiceUpdateOptions const ( - RegistryAuthFromSpec = "spec" - RegistryAuthFromPreviousSpec = "previous-spec" + RegistryAuthFromSpec RegistryAuthSource = "spec" + RegistryAuthFromPreviousSpec RegistryAuthSource = "previous-spec" ) diff --git a/vendor/github.com/moby/moby/api/types/system/disk_usage.go b/vendor/github.com/moby/moby/api/types/system/disk_usage.go index 4d3315f02..53d7a499d 100644 --- a/vendor/github.com/moby/moby/api/types/system/disk_usage.go +++ b/vendor/github.com/moby/moby/api/types/system/disk_usage.go @@ -24,9 +24,27 @@ const ( // DiskUsage contains response of Engine API: // GET "/system/df" type DiskUsage struct { - LayersSize int64 - Images []*image.Summary - Containers []*container.Summary - Volumes []*volume.Volume - BuildCache []*build.CacheRecord + LegacyDiskUsage + + ImageUsage *image.DiskUsage `json:"ImageUsage,omitempty"` + ContainerUsage *container.DiskUsage `json:"ContainerUsage,omitempty"` + VolumeUsage *volume.DiskUsage `json:"VolumeUsage,omitempty"` + BuildCacheUsage *build.DiskUsage `json:"BuildCacheUsage,omitempty"` +} + +type LegacyDiskUsage struct { + // Deprecated: kept to maintain backwards compatibility with API < v1.52, use [ImagesDiskUsage.TotalSize] instead. + LayersSize int64 `json:"LayersSize,omitempty"` + + // Deprecated: kept to maintain backwards compatibility with API < v1.52, use [ImagesDiskUsage.Items] instead. + Images []image.Summary `json:"Images,omitzero"` + + // Deprecated: kept to maintain backwards compatibility with API < v1.52, use [ContainersDiskUsage.Items] instead. + Containers []container.Summary `json:"Containers,omitzero"` + + // Deprecated: kept to maintain backwards compatibility with API < v1.52, use [VolumesDiskUsage.Items] instead. + Volumes []volume.Volume `json:"Volumes,omitzero"` + + // Deprecated: kept to maintain backwards compatibility with API < v1.52, use [BuildCacheDiskUsage.Items] instead. + BuildCache []build.CacheRecord `json:"BuildCache,omitzero"` } diff --git a/vendor/github.com/moby/moby/api/types/volume/disk_usage.go b/vendor/github.com/moby/moby/api/types/volume/disk_usage.go new file mode 100644 index 000000000..a0df0dc0f --- /dev/null +++ b/vendor/github.com/moby/moby/api/types/volume/disk_usage.go @@ -0,0 +1,36 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package volume + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +// DiskUsage represents system data usage for volume resources. +// +// swagger:model DiskUsage +type DiskUsage struct { + + // Count of active volumes. + // + // Example: 1 + ActiveVolumes int64 `json:"ActiveVolumes,omitempty"` + + // List of volumes. + // + Items []Volume `json:"Items,omitempty"` + + // Disk space that can be reclaimed by removing inactive volumes. + // + // Example: 12345678 + Reclaimable int64 `json:"Reclaimable,omitempty"` + + // Disk space in use by volumes. + // + // Example: 98765432 + TotalSize int64 `json:"TotalSize,omitempty"` + + // Count of all volumes. + // + // Example: 4 + TotalVolumes int64 `json:"TotalVolumes,omitempty"` +} diff --git a/vendor/github.com/moby/moby/client/client_interfaces.go b/vendor/github.com/moby/moby/client/client_interfaces.go index 9750076f0..693c91fd0 100644 --- a/vendor/github.com/moby/moby/client/client_interfaces.go +++ b/vendor/github.com/moby/moby/client/client_interfaces.go @@ -4,8 +4,6 @@ import ( "context" "io" "net" - - "github.com/moby/moby/api/types/system" ) // APIClient is an interface that clients that talk with a docker server must implement. @@ -74,7 +72,7 @@ type ContainerAPIClient interface { ContainerWait(ctx context.Context, container string, options ContainerWaitOptions) ContainerWaitResult CopyFromContainer(ctx context.Context, container string, options CopyFromContainerOptions) (CopyFromContainerResult, error) CopyToContainer(ctx context.Context, container string, options CopyToContainerOptions) (CopyToContainerResult, error) - ContainersPrune(ctx context.Context, opts ContainerPruneOptions) (ContainerPruneResult, error) + ContainerPrune(ctx context.Context, opts ContainerPruneOptions) (ContainerPruneResult, error) } type ExecAPIClient interface { @@ -103,7 +101,7 @@ type ImageAPIClient interface { ImageRemove(ctx context.Context, image string, options ImageRemoveOptions) (ImageRemoveResult, error) ImageSearch(ctx context.Context, term string, options ImageSearchOptions) (ImageSearchResult, error) ImageTag(ctx context.Context, options ImageTagOptions) (ImageTagResult, error) - ImagesPrune(ctx context.Context, opts ImagePruneOptions) (ImagePruneResult, error) + ImagePrune(ctx context.Context, opts ImagePruneOptions) (ImagePruneResult, error) ImageInspect(ctx context.Context, image string, _ ...ImageInspectOption) (ImageInspectResult, error) ImageHistory(ctx context.Context, image string, _ ...ImageHistoryOption) (ImageHistoryResult, error) @@ -119,7 +117,7 @@ type NetworkAPIClient interface { NetworkInspect(ctx context.Context, network string, options NetworkInspectOptions) (NetworkInspectResult, error) NetworkList(ctx context.Context, options NetworkListOptions) (NetworkListResult, error) NetworkRemove(ctx context.Context, network string, options NetworkRemoveOptions) (NetworkRemoveResult, error) - NetworksPrune(ctx context.Context, opts NetworkPruneOptions) (NetworkPruneResult, error) + NetworkPrune(ctx context.Context, opts NetworkPruneOptions) (NetworkPruneResult, error) } // NodeAPIClient defines API client methods for the nodes @@ -173,7 +171,7 @@ type SystemAPIClient interface { Events(ctx context.Context, options EventsListOptions) EventsResult Info(ctx context.Context, options InfoOptions) (SystemInfoResult, error) RegistryLogin(ctx context.Context, auth RegistryLoginOptions) (RegistryLoginResult, error) - DiskUsage(ctx context.Context, options DiskUsageOptions) (system.DiskUsage, error) + DiskUsage(ctx context.Context, options DiskUsageOptions) (DiskUsageResult, error) Ping(ctx context.Context, options PingOptions) (PingResult, error) } @@ -183,7 +181,7 @@ type VolumeAPIClient interface { VolumeInspect(ctx context.Context, volumeID string, options VolumeInspectOptions) (VolumeInspectResult, error) VolumeList(ctx context.Context, options VolumeListOptions) (VolumeListResult, error) VolumeRemove(ctx context.Context, volumeID string, options VolumeRemoveOptions) (VolumeRemoveResult, error) - VolumesPrune(ctx context.Context, options VolumePruneOptions) (VolumePruneResult, error) + VolumePrune(ctx context.Context, options VolumePruneOptions) (VolumePruneResult, error) VolumeUpdate(ctx context.Context, volumeID string, options VolumeUpdateOptions) (VolumeUpdateResult, error) } diff --git a/vendor/github.com/moby/moby/client/container_exec.go b/vendor/github.com/moby/moby/client/container_exec.go index c3d1552e6..1af6a4e20 100644 --- a/vendor/github.com/moby/moby/client/container_exec.go +++ b/vendor/github.com/moby/moby/client/container_exec.go @@ -12,17 +12,17 @@ import ( // ExecCreateOptions is a small subset of the Config struct that holds the configuration // for the exec feature of docker. type ExecCreateOptions struct { - User string // User that will run the command - Privileged bool // Is the container in privileged mode - Tty bool // Attach standard streams to a tty. - ConsoleSize *[2]uint `json:",omitempty"` // Initial console size [height, width] - AttachStdin bool // Attach the standard input, makes possible user interaction - AttachStderr bool // Attach the standard error - AttachStdout bool // Attach the standard output - DetachKeys string // Escape keys for detach - Env []string // Environment variables - WorkingDir string // Working directory - Cmd []string // Execution commands and args + User string // User that will run the command + Privileged bool // Is the container in privileged mode + TTY bool // Attach standard streams to a tty. + ConsoleSize ConsoleSize // Initial terminal size [height, width], unused if TTY == false + AttachStdin bool // Attach the standard input, makes possible user interaction + AttachStderr bool // Attach the standard error + AttachStdout bool // Attach the standard output + DetachKeys string // Escape keys for detach + Env []string // Environment variables + WorkingDir string // Working directory + Cmd []string // Execution commands and args } // ExecCreateResult holds the result of creating a container exec. @@ -37,11 +37,16 @@ func (cli *Client) ExecCreate(ctx context.Context, containerID string, options E return ExecCreateResult{}, err } + consoleSize, err := getConsoleSize(options.TTY, options.ConsoleSize) + if err != nil { + return ExecCreateResult{}, err + } + req := container.ExecCreateRequest{ User: options.User, Privileged: options.Privileged, - Tty: options.Tty, - ConsoleSize: options.ConsoleSize, + Tty: options.TTY, + ConsoleSize: consoleSize, AttachStdin: options.AttachStdin, AttachStderr: options.AttachStderr, AttachStdout: options.AttachStdout, @@ -73,7 +78,7 @@ type ExecStartOptions struct { // Check if there's a tty TTY bool // Terminal size [height, width], unused if TTY == false - ConsoleSize ConsoleSize `json:",omitzero"` + ConsoleSize ConsoleSize } // ExecStartResult holds the result of starting a container exec. @@ -82,13 +87,16 @@ type ExecStartResult struct { // ExecStart starts an exec process already created in the docker host. func (cli *Client) ExecStart(ctx context.Context, execID string, options ExecStartOptions) (ExecStartResult, error) { - req := container.ExecStartRequest{ - Detach: options.Detach, - Tty: options.TTY, - } - if err := applyConsoleSize(&req, &options.ConsoleSize); err != nil { + consoleSize, err := getConsoleSize(options.TTY, options.ConsoleSize) + if err != nil { return ExecStartResult{}, err } + + req := container.ExecStartRequest{ + Detach: options.Detach, + Tty: options.TTY, + ConsoleSize: consoleSize, + } resp, err := cli.post(ctx, "/exec/"+execID+"/start", nil, req, nil) defer ensureReaderClosed(resp) return ExecStartResult{}, err @@ -126,27 +134,29 @@ type ExecAttachResult struct { // // [stdcopy.StdCopy]: https://pkg.go.dev/github.com/moby/moby/api/pkg/stdcopy#StdCopy func (cli *Client) ExecAttach(ctx context.Context, execID string, options ExecAttachOptions) (ExecAttachResult, error) { - req := container.ExecStartRequest{ - Detach: false, - Tty: options.TTY, - } - if err := applyConsoleSize(&req, &options.ConsoleSize); err != nil { + consoleSize, err := getConsoleSize(options.TTY, options.ConsoleSize) + if err != nil { return ExecAttachResult{}, err } + req := container.ExecStartRequest{ + Detach: false, + Tty: options.TTY, + ConsoleSize: consoleSize, + } response, err := cli.postHijacked(ctx, "/exec/"+execID+"/start", nil, req, http.Header{ "Content-Type": {"application/json"}, }) return ExecAttachResult{HijackedResponse: response}, err } -func applyConsoleSize(req *container.ExecStartRequest, consoleSize *ConsoleSize) error { +func getConsoleSize(hasTTY bool, consoleSize ConsoleSize) (*[2]uint, error) { if consoleSize.Height != 0 || consoleSize.Width != 0 { - if !req.Tty { - return errdefs.ErrInvalidArgument.WithMessage("console size is only supported when TTY is enabled") + if !hasTTY { + return nil, errdefs.ErrInvalidArgument.WithMessage("console size is only supported when TTY is enabled") } - req.ConsoleSize = &[2]uint{consoleSize.Height, consoleSize.Width} + return &[2]uint{consoleSize.Height, consoleSize.Width}, nil } - return nil + return nil, nil } // ExecInspectOptions holds options for inspecting a container exec. diff --git a/vendor/github.com/moby/moby/client/container_export.go b/vendor/github.com/moby/moby/client/container_export.go index ca40fe78a..2d33efb7d 100644 --- a/vendor/github.com/moby/moby/client/container_export.go +++ b/vendor/github.com/moby/moby/client/container_export.go @@ -4,7 +4,6 @@ import ( "context" "io" "net/url" - "sync" ) // ContainerExportOptions specifies options for container export operations. @@ -13,50 +12,36 @@ type ContainerExportOptions struct { } // ContainerExportResult represents the result of a container export operation. -type ContainerExportResult struct { - rc io.ReadCloser - close func() error +type ContainerExportResult interface { + io.ReadCloser } // ContainerExport retrieves the raw contents of a container // and returns them as an [io.ReadCloser]. It's up to the caller // to close the stream. +// +// The underlying [io.ReadCloser] is automatically closed if the context is canceled, func (cli *Client) ContainerExport(ctx context.Context, containerID string, options ContainerExportOptions) (ContainerExportResult, error) { containerID, err := trimID("container", containerID) if err != nil { - return ContainerExportResult{}, err + return nil, err } resp, err := cli.get(ctx, "/containers/"+containerID+"/export", url.Values{}, nil) if err != nil { - return ContainerExportResult{}, err + return nil, err } - return newContainerExportResult(resp.Body), nil + return &containerExportResult{ + ReadCloser: newCancelReadCloser(ctx, resp.Body), + }, nil } -func newContainerExportResult(rc io.ReadCloser) ContainerExportResult { - if rc == nil { - panic("nil io.ReadCloser") - } - return ContainerExportResult{ - rc: rc, - close: sync.OnceValue(rc.Close), - } +type containerExportResult struct { + io.ReadCloser } -// Read implements io.ReadCloser -func (r ContainerExportResult) Read(p []byte) (n int, err error) { - if r.rc == nil { - return 0, io.EOF - } - return r.rc.Read(p) -} - -// Close implements io.ReadCloser -func (r ContainerExportResult) Close() error { - if r.close == nil { - return nil - } - return r.close() -} +var ( + _ io.ReadCloser = (*containerExportResult)(nil) + _ ContainerExportResult = (*containerExportResult)(nil) +) diff --git a/vendor/github.com/moby/moby/client/container_logs.go b/vendor/github.com/moby/moby/client/container_logs.go index 71c698e9e..636ab2212 100644 --- a/vendor/github.com/moby/moby/client/container_logs.go +++ b/vendor/github.com/moby/moby/client/container_logs.go @@ -5,7 +5,6 @@ import ( "fmt" "io" "net/url" - "sync" "time" "github.com/moby/moby/client/internal/timestamp" @@ -24,14 +23,15 @@ type ContainerLogsOptions struct { } // ContainerLogsResult is the result of a container logs operation. -type ContainerLogsResult struct { - rc io.ReadCloser - close func() error +type ContainerLogsResult interface { + io.ReadCloser } // ContainerLogs returns the logs generated by a container in an [io.ReadCloser]. // It's up to the caller to close the stream. // +// The underlying [io.ReadCloser] is automatically closed if the context is canceled, +// // The stream format on the response uses one of two formats: // // - If the container is using a TTY, there is only a single stream (stdout) @@ -58,7 +58,7 @@ type ContainerLogsResult struct { func (cli *Client) ContainerLogs(ctx context.Context, containerID string, options ContainerLogsOptions) (ContainerLogsResult, error) { containerID, err := trimID("container", containerID) if err != nil { - return ContainerLogsResult{}, err + return nil, err } query := url.Values{} @@ -73,7 +73,7 @@ func (cli *Client) ContainerLogs(ctx context.Context, containerID string, option if options.Since != "" { ts, err := timestamp.GetTimestamp(options.Since, time.Now()) if err != nil { - return ContainerLogsResult{}, fmt.Errorf(`invalid value for "since": %w`, err) + return nil, fmt.Errorf(`invalid value for "since": %w`, err) } query.Set("since", ts) } @@ -81,7 +81,7 @@ func (cli *Client) ContainerLogs(ctx context.Context, containerID string, option if options.Until != "" { ts, err := timestamp.GetTimestamp(options.Until, time.Now()) if err != nil { - return ContainerLogsResult{}, fmt.Errorf(`invalid value for "until": %w`, err) + return nil, fmt.Errorf(`invalid value for "until": %w`, err) } query.Set("until", ts) } @@ -101,33 +101,18 @@ func (cli *Client) ContainerLogs(ctx context.Context, containerID string, option resp, err := cli.get(ctx, "/containers/"+containerID+"/logs", query, nil) if err != nil { - return ContainerLogsResult{}, err + return nil, err } - return newContainerLogsResult(resp.Body), nil + return &containerLogsResult{ + ReadCloser: newCancelReadCloser(ctx, resp.Body), + }, nil } -func newContainerLogsResult(rc io.ReadCloser) ContainerLogsResult { - if rc == nil { - panic("rc cannot be nil") - } - return ContainerLogsResult{ - rc: rc, - close: sync.OnceValue(rc.Close), - } +type containerLogsResult struct { + io.ReadCloser } -// Read implements the io.Reader interface. -func (r ContainerLogsResult) Read(p []byte) (n int, err error) { - if r.rc == nil { - return 0, io.EOF - } - return r.rc.Read(p) -} - -// Close closes the underlying reader. -func (r ContainerLogsResult) Close() error { - if r.close == nil { - return nil - } - return r.close() -} +var ( + _ io.ReadCloser = (*containerLogsResult)(nil) + _ ContainerLogsResult = (*containerLogsResult)(nil) +) diff --git a/vendor/github.com/moby/moby/client/container_prune.go b/vendor/github.com/moby/moby/client/container_prune.go index ee6be2581..f826f8b6f 100644 --- a/vendor/github.com/moby/moby/client/container_prune.go +++ b/vendor/github.com/moby/moby/client/container_prune.go @@ -14,13 +14,13 @@ type ContainerPruneOptions struct { Filters Filters } -// ContainerPruneResult holds the result from the [Client.ContainersPrune] method. +// ContainerPruneResult holds the result from the [Client.ContainerPrune] method. type ContainerPruneResult struct { Report container.PruneReport } -// ContainersPrune requests the daemon to delete unused data -func (cli *Client) ContainersPrune(ctx context.Context, opts ContainerPruneOptions) (ContainerPruneResult, error) { +// ContainerPrune requests the daemon to delete unused data +func (cli *Client) ContainerPrune(ctx context.Context, opts ContainerPruneOptions) (ContainerPruneResult, error) { query := url.Values{} opts.Filters.updateURLValues(query) diff --git a/vendor/github.com/moby/moby/client/container_stats.go b/vendor/github.com/moby/moby/client/container_stats.go index 6a4b1c3b6..277769dbf 100644 --- a/vendor/github.com/moby/moby/client/container_stats.go +++ b/vendor/github.com/moby/moby/client/container_stats.go @@ -43,6 +43,8 @@ type ContainerStatsResult struct { // 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. +// +// The underlying [io.ReadCloser] is automatically closed if the context is canceled, func (cli *Client) ContainerStats(ctx context.Context, containerID string, options ContainerStatsOptions) (ContainerStatsResult, error) { containerID, err := trimID("container", containerID) if err != nil { @@ -68,6 +70,6 @@ func (cli *Client) ContainerStats(ctx context.Context, containerID string, optio } return ContainerStatsResult{ - Body: resp.Body, + Body: newCancelReadCloser(ctx, resp.Body), }, nil } diff --git a/vendor/github.com/moby/moby/client/image_import.go b/vendor/github.com/moby/moby/client/image_import.go index 3718ab50c..f383f76d4 100644 --- a/vendor/github.com/moby/moby/client/image_import.go +++ b/vendor/github.com/moby/moby/client/image_import.go @@ -2,18 +2,26 @@ package client import ( "context" + "io" "net/url" "github.com/distribution/reference" ) -// ImageImport creates a new image based on the source options. -// It returns the JSON content in the response body. +// ImageImportResult holds the response body returned by the daemon for image import. +type ImageImportResult interface { + io.ReadCloser +} + +// ImageImport creates a new image based on the source options. It returns the +// JSON content in the [ImageImportResult]. +// +// The underlying [io.ReadCloser] is automatically closed if the context is canceled, func (cli *Client) ImageImport(ctx context.Context, source ImageImportSource, ref string, options ImageImportOptions) (ImageImportResult, error) { if ref != "" { // Check if the given image name can be resolved if _, err := reference.ParseNormalizedNamed(ref); err != nil { - return ImageImportResult{}, err + return nil, err } } @@ -40,7 +48,19 @@ func (cli *Client) ImageImport(ctx context.Context, source ImageImportSource, re resp, err := cli.postRaw(ctx, "/images/create", query, source.Source, nil) if err != nil { - return ImageImportResult{}, err + return nil, err } - return ImageImportResult{rc: resp.Body}, nil + return &imageImportResult{ + ReadCloser: newCancelReadCloser(ctx, resp.Body), + }, nil } + +// ImageImportResult holds the response body returned by the daemon for image import. +type imageImportResult struct { + io.ReadCloser +} + +var ( + _ io.ReadCloser = (*imageImportResult)(nil) + _ ImageImportResult = (*imageImportResult)(nil) +) diff --git a/vendor/github.com/moby/moby/client/image_import_opts.go b/vendor/github.com/moby/moby/client/image_import_opts.go index 513548cc3..c70473bdd 100644 --- a/vendor/github.com/moby/moby/client/image_import_opts.go +++ b/vendor/github.com/moby/moby/client/image_import_opts.go @@ -19,22 +19,3 @@ type ImageImportOptions struct { Changes []string // Changes are the raw changes to apply to this image Platform ocispec.Platform // Platform is the target platform of the image } - -// ImageImportResult holds the response body returned by the daemon for image import. -type ImageImportResult struct { - rc io.ReadCloser -} - -func (r ImageImportResult) Read(p []byte) (n int, err error) { - if r.rc == nil { - return 0, io.EOF - } - return r.rc.Read(p) -} - -func (r ImageImportResult) Close() error { - if r.rc == nil { - return nil - } - return r.rc.Close() -} diff --git a/vendor/github.com/moby/moby/client/image_load.go b/vendor/github.com/moby/moby/client/image_load.go index 2be186929..ec5fcae6e 100644 --- a/vendor/github.com/moby/moby/client/image_load.go +++ b/vendor/github.com/moby/moby/client/image_load.go @@ -7,18 +7,21 @@ import ( "net/url" ) -// ImageLoad loads an image in the docker host from the client host. -// It's up to the caller to close the [io.ReadCloser] in the -// [ImageLoadResult] returned by this function. +// ImageLoadResult returns information to the client about a load process. +// It implements [io.ReadCloser] and must be closed to avoid a resource leak. +type ImageLoadResult interface { + io.ReadCloser +} + +// ImageLoad loads an image in the docker host from the client host. It's up +// to the caller to close the [ImageLoadResult] returned by this function. // -// Platform is an optional parameter that specifies the platform to load from -// the provided multi-platform image. Passing a platform only has an effect -// if the input image is a multi-platform image. +// The underlying [io.ReadCloser] is automatically closed if the context is canceled, func (cli *Client) ImageLoad(ctx context.Context, input io.Reader, loadOpts ...ImageLoadOption) (ImageLoadResult, error) { var opts imageLoadOpts for _, opt := range loadOpts { if err := opt.Apply(&opts); err != nil { - return ImageLoadResult{}, err + return nil, err } } @@ -29,12 +32,12 @@ func (cli *Client) ImageLoad(ctx context.Context, input io.Reader, loadOpts ...I } if len(opts.apiOptions.Platforms) > 0 { if err := cli.requiresVersion(ctx, "1.48", "platform"); err != nil { - return ImageLoadResult{}, err + return nil, err } p, err := encodePlatforms(opts.apiOptions.Platforms...) if err != nil { - return ImageLoadResult{}, err + return nil, err } query["platform"] = p } @@ -43,26 +46,19 @@ func (cli *Client) ImageLoad(ctx context.Context, input io.Reader, loadOpts ...I "Content-Type": {"application/x-tar"}, }) if err != nil { - return ImageLoadResult{}, err + return nil, err } - return ImageLoadResult{ - body: resp.Body, + return &imageLoadResult{ + ReadCloser: newCancelReadCloser(ctx, resp.Body), }, nil } -// ImageLoadResult returns information to the client about a load process. -type ImageLoadResult struct { - // Body must be closed to avoid a resource leak - body io.ReadCloser +// imageLoadResult returns information to the client about a load process. +type imageLoadResult struct { + io.ReadCloser } -func (r ImageLoadResult) Read(p []byte) (n int, err error) { - return r.body.Read(p) -} - -func (r ImageLoadResult) Close() error { - if r.body == nil { - return nil - } - return r.body.Close() -} +var ( + _ io.ReadCloser = (*imageLoadResult)(nil) + _ ImageLoadResult = (*imageLoadResult)(nil) +) diff --git a/vendor/github.com/moby/moby/client/image_load_opts.go b/vendor/github.com/moby/moby/client/image_load_opts.go index 8792f64a0..aeb4fcf83 100644 --- a/vendor/github.com/moby/moby/client/image_load_opts.go +++ b/vendor/github.com/moby/moby/client/image_load_opts.go @@ -38,6 +38,10 @@ func ImageLoadWithQuiet(quiet bool) ImageLoadOption { } // ImageLoadWithPlatforms sets the platforms to be loaded from the image. +// +// Platform is an optional parameter that specifies the platform to load from +// the provided multi-platform image. Passing a platform only has an effect +// if the input image is a multi-platform image. func ImageLoadWithPlatforms(platforms ...ocispec.Platform) ImageLoadOption { return imageLoadOptionFunc(func(opt *imageLoadOpts) error { if opt.apiOptions.Platforms != nil { diff --git a/vendor/github.com/moby/moby/client/image_prune.go b/vendor/github.com/moby/moby/client/image_prune.go index 0f8d9753e..7f3a25b89 100644 --- a/vendor/github.com/moby/moby/client/image_prune.go +++ b/vendor/github.com/moby/moby/client/image_prune.go @@ -14,13 +14,13 @@ type ImagePruneOptions struct { Filters Filters } -// ImagePruneResult holds the result from the [Client.ImagesPrune] method. +// ImagePruneResult holds the result from the [Client.ImagePrune] method. type ImagePruneResult struct { Report image.PruneReport } -// ImagesPrune requests the daemon to delete unused data -func (cli *Client) ImagesPrune(ctx context.Context, opts ImagePruneOptions) (ImagePruneResult, error) { +// ImagePrune requests the daemon to delete unused data +func (cli *Client) ImagePrune(ctx context.Context, opts ImagePruneOptions) (ImagePruneResult, error) { query := url.Values{} opts.Filters.updateURLValues(query) diff --git a/vendor/github.com/moby/moby/client/image_save.go b/vendor/github.com/moby/moby/client/image_save.go index 5be986418..508f88b7d 100644 --- a/vendor/github.com/moby/moby/client/image_save.go +++ b/vendor/github.com/moby/moby/client/image_save.go @@ -2,11 +2,17 @@ package client import ( "context" + "io" "net/url" ) +type ImageSaveResult interface { + io.ReadCloser +} + // ImageSave retrieves one or more images from the docker host as an -// [ImageSaveResult]. +// [ImageSaveResult]. Callers should close the reader, but the underlying +// [io.ReadCloser] is automatically closed if the context is canceled, // // Platforms is an optional parameter that specifies the platforms to save // from the image. Passing a platform only has an effect if the input image @@ -15,7 +21,7 @@ func (cli *Client) ImageSave(ctx context.Context, imageIDs []string, saveOpts .. var opts imageSaveOpts for _, opt := range saveOpts { if err := opt.Apply(&opts); err != nil { - return ImageSaveResult{}, err + return nil, err } } @@ -25,18 +31,29 @@ func (cli *Client) ImageSave(ctx context.Context, imageIDs []string, saveOpts .. if len(opts.apiOptions.Platforms) > 0 { if err := cli.requiresVersion(ctx, "1.48", "platform"); err != nil { - return ImageSaveResult{}, err + return nil, err } p, err := encodePlatforms(opts.apiOptions.Platforms...) if err != nil { - return ImageSaveResult{}, err + return nil, err } query["platform"] = p } resp, err := cli.get(ctx, "/images/get", query, nil) if err != nil { - return ImageSaveResult{}, err + return nil, err } - return newImageSaveResult(resp.Body), nil + return &imageSaveResult{ + ReadCloser: newCancelReadCloser(ctx, resp.Body), + }, nil } + +type imageSaveResult struct { + io.ReadCloser +} + +var ( + _ io.ReadCloser = (*imageSaveResult)(nil) + _ ImageSaveResult = (*imageSaveResult)(nil) +) diff --git a/vendor/github.com/moby/moby/client/image_save_opts.go b/vendor/github.com/moby/moby/client/image_save_opts.go index 480126544..9c0b3b74a 100644 --- a/vendor/github.com/moby/moby/client/image_save_opts.go +++ b/vendor/github.com/moby/moby/client/image_save_opts.go @@ -2,8 +2,6 @@ package client import ( "fmt" - "io" - "sync" ocispec "github.com/opencontainers/image-spec/specs-go/v1" ) @@ -18,8 +16,11 @@ func (f imageSaveOptionFunc) Apply(o *imageSaveOpts) error { return f(o) } -// ImageSaveWithPlatforms sets the platforms to be saved from the image. +// ImageSaveWithPlatforms sets the platforms to be saved from the image. It +// produces an error if platforms are already set. This option only has an +// effect if the input image is a multi-platform image. func ImageSaveWithPlatforms(platforms ...ocispec.Platform) ImageSaveOption { + // TODO(thaJeztah): verify the GoDoc; do we produce an error for a single-platform image without the given platform? return imageSaveOptionFunc(func(opt *imageSaveOpts) error { if opt.apiOptions.Platforms != nil { return fmt.Errorf("platforms already set to %v", opt.apiOptions.Platforms) @@ -38,34 +39,3 @@ type imageSaveOptions struct { // multi-platform image and has multiple variants. Platforms []ocispec.Platform } - -func newImageSaveResult(rc io.ReadCloser) ImageSaveResult { - if rc == nil { - panic("nil io.ReadCloser") - } - return ImageSaveResult{ - rc: rc, - close: sync.OnceValue(rc.Close), - } -} - -type ImageSaveResult struct { - rc io.ReadCloser - close func() error -} - -// Read implements io.ReadCloser -func (r ImageSaveResult) Read(p []byte) (n int, err error) { - if r.rc == nil { - return 0, io.EOF - } - return r.rc.Read(p) -} - -// Close implements io.ReadCloser -func (r ImageSaveResult) Close() error { - if r.close == nil { - return nil - } - return r.close() -} diff --git a/vendor/github.com/moby/moby/client/network_prune.go b/vendor/github.com/moby/moby/client/network_prune.go index cb266e76c..55f7cac02 100644 --- a/vendor/github.com/moby/moby/client/network_prune.go +++ b/vendor/github.com/moby/moby/client/network_prune.go @@ -14,13 +14,13 @@ type NetworkPruneOptions struct { Filters Filters } -// NetworkPruneResult holds the result from the [Client.NetworksPrune] method. +// NetworkPruneResult holds the result from the [Client.NetworkPrune] method. type NetworkPruneResult struct { Report network.PruneReport } -// NetworksPrune requests the daemon to delete unused networks -func (cli *Client) NetworksPrune(ctx context.Context, opts NetworkPruneOptions) (NetworkPruneResult, error) { +// NetworkPrune requests the daemon to delete unused networks +func (cli *Client) NetworkPrune(ctx context.Context, opts NetworkPruneOptions) (NetworkPruneResult, error) { query := url.Values{} opts.Filters.updateURLValues(query) diff --git a/vendor/github.com/moby/moby/client/pkg/security/security_opts.go b/vendor/github.com/moby/moby/client/pkg/security/security_opts.go index 1ed526d92..b123919c1 100644 --- a/vendor/github.com/moby/moby/client/pkg/security/security_opts.go +++ b/vendor/github.com/moby/moby/client/pkg/security/security_opts.go @@ -1,8 +1,6 @@ package security import ( - "errors" - "fmt" "strings" ) @@ -19,22 +17,14 @@ type KeyValue struct { // DecodeOptions decodes a security options string slice to a // type-safe [Option]. -func DecodeOptions(opts []string) ([]Option, error) { - so := []Option{} +func DecodeOptions(opts []string) []Option { + so := make([]Option, 0, len(opts)) for _, opt := range opts { - // support output from a < 1.13 docker daemon - if !strings.Contains(opt, "=") { - so = append(so, Option{Name: opt}) - continue - } secopt := Option{} for _, s := range strings.Split(opt, ",") { - k, v, ok := strings.Cut(s, "=") - if !ok { - return nil, fmt.Errorf("invalid security option %q", s) - } - if k == "" || v == "" { - return nil, errors.New("invalid empty security option") + k, v, _ := strings.Cut(s, "=") + if k == "" { + continue } if k == "name" { secopt.Name = v @@ -42,7 +32,9 @@ func DecodeOptions(opts []string) ([]Option, error) { } secopt.Options = append(secopt.Options, KeyValue{Key: k, Value: v}) } - so = append(so, secopt) + if secopt.Name != "" { + so = append(so, secopt) + } } - return so, nil + return so } diff --git a/vendor/github.com/moby/moby/client/service_logs.go b/vendor/github.com/moby/moby/client/service_logs.go index cbbb95839..fd565db5a 100644 --- a/vendor/github.com/moby/moby/client/service_logs.go +++ b/vendor/github.com/moby/moby/client/service_logs.go @@ -5,7 +5,6 @@ import ( "fmt" "io" "net/url" - "sync" "time" "github.com/moby/moby/client/internal/timestamp" @@ -26,16 +25,21 @@ type ServiceLogsOptions struct { // ServiceLogsResult holds the result of a service logs operation. // It implements [io.ReadCloser]. // It's up to the caller to close the stream. -type ServiceLogsResult struct { - rc io.ReadCloser - close func() error +type ServiceLogsResult interface { + io.ReadCloser } -// ServiceLogs returns the logs generated by a service in an [ServiceLogsResult]. +// ServiceLogs returns the logs generated by a service in a [ServiceLogsResult]. +// as an [io.ReadCloser]. Callers should close the stream. +// +// The underlying [io.ReadCloser] is automatically closed if the context is canceled, func (cli *Client) ServiceLogs(ctx context.Context, serviceID string, options ServiceLogsOptions) (ServiceLogsResult, error) { + // TODO(thaJeztah): this function needs documentation about the format of ths stream (similar to for container logs) + // TODO(thaJeztah): migrate CLI utilities to the client where suitable; https://github.com/docker/cli/blob/v29.0.0-rc.1/cli/command/service/logs.go#L73-L348 + serviceID, err := trimID("service", serviceID) if err != nil { - return ServiceLogsResult{}, err + return nil, err } query := url.Values{} @@ -50,7 +54,7 @@ func (cli *Client) ServiceLogs(ctx context.Context, serviceID string, options Se if options.Since != "" { ts, err := timestamp.GetTimestamp(options.Since, time.Now()) if err != nil { - return ServiceLogsResult{}, fmt.Errorf(`invalid value for "since": %w`, err) + return nil, fmt.Errorf(`invalid value for "since": %w`, err) } query.Set("since", ts) } @@ -70,33 +74,18 @@ func (cli *Client) ServiceLogs(ctx context.Context, serviceID string, options Se resp, err := cli.get(ctx, "/services/"+serviceID+"/logs", query, nil) if err != nil { - return ServiceLogsResult{}, err + return nil, err } - return newServiceLogsResult(resp.Body), nil + return &serviceLogsResult{ + ReadCloser: newCancelReadCloser(ctx, resp.Body), + }, nil } -func newServiceLogsResult(rc io.ReadCloser) ServiceLogsResult { - if rc == nil { - panic("nil io.ReadCloser") - } - return ServiceLogsResult{ - rc: rc, - close: sync.OnceValue(rc.Close), - } +type serviceLogsResult struct { + io.ReadCloser } -// Read implements [io.ReadCloser] for LogsResult. -func (r ServiceLogsResult) Read(p []byte) (n int, err error) { - if r.rc == nil { - return 0, io.EOF - } - return r.rc.Read(p) -} - -// Close implements [io.ReadCloser] for LogsResult. -func (r ServiceLogsResult) Close() error { - if r.close == nil { - return nil - } - return r.close() -} +var ( + _ io.ReadCloser = (*serviceLogsResult)(nil) + _ ServiceLogsResult = (*serviceLogsResult)(nil) +) diff --git a/vendor/github.com/moby/moby/client/service_update.go b/vendor/github.com/moby/moby/client/service_update.go index 36061c9d1..9e6b52781 100644 --- a/vendor/github.com/moby/moby/client/service_update.go +++ b/vendor/github.com/moby/moby/client/service_update.go @@ -28,7 +28,7 @@ type ServiceUpdateOptions struct { // RegistryAuthFrom specifies where to find the registry authorization // credentials if they are not given in EncodedRegistryAuth. Valid // values are "spec" and "previous-spec". - RegistryAuthFrom string + RegistryAuthFrom swarm.RegistryAuthSource // Rollback indicates whether a server-side rollback should be // performed. When this is set, the provided spec will be ignored. @@ -65,7 +65,7 @@ func (cli *Client) ServiceUpdate(ctx context.Context, serviceID string, options query := url.Values{} if options.RegistryAuthFrom != "" { - query.Set("registryAuthFrom", options.RegistryAuthFrom) + query.Set("registryAuthFrom", string(options.RegistryAuthFrom)) } if options.Rollback != "" { diff --git a/vendor/github.com/moby/moby/client/system_disk_usage.go b/vendor/github.com/moby/moby/client/system_disk_usage.go index 6f78952cb..bbe38b7f7 100644 --- a/vendor/github.com/moby/moby/client/system_disk_usage.go +++ b/vendor/github.com/moby/moby/client/system_disk_usage.go @@ -5,29 +5,315 @@ import ( "encoding/json" "fmt" "net/url" + "slices" + "strings" + "github.com/moby/moby/api/types/build" + "github.com/moby/moby/api/types/container" + "github.com/moby/moby/api/types/image" "github.com/moby/moby/api/types/system" + "github.com/moby/moby/api/types/volume" + "github.com/moby/moby/client/pkg/versions" ) -// DiskUsage requests the current data usage from the daemon -func (cli *Client) DiskUsage(ctx context.Context, options DiskUsageOptions) (system.DiskUsage, error) { - var query url.Values - if len(options.Types) > 0 { - query = url.Values{} - for _, t := range options.Types { - query.Add("type", string(t)) +// DiskUsageOptions holds parameters for [Client.DiskUsage] operations. +type DiskUsageOptions struct { + // Containers controls whether container disk usage should be computed. + Containers bool + + // Images controls whether image disk usage should be computed. + Images bool + + // BuildCache controls whether build cache disk usage should be computed. + BuildCache bool + + // Volumes controls whether volume disk usage should be computed. + Volumes bool + + // Verbose enables more detailed disk usage information. + Verbose bool +} + +// DiskUsageResult is the result of [Client.DiskUsage] operations. +type DiskUsageResult struct { + // Containers holds container disk usage information. + Containers ContainersDiskUsage + + // Images holds image disk usage information. + Images ImagesDiskUsage + + // BuildCache holds build cache disk usage information. + BuildCache BuildCacheDiskUsage + + // Volumes holds volume disk usage information. + Volumes VolumesDiskUsage +} + +// ContainersDiskUsage contains disk usage information for containers. +type ContainersDiskUsage struct { + // ActiveContainers is the number of active containers. + ActiveContainers int64 + + // TotalContainers is the total number of containers. + TotalContainers int64 + + // Reclaimable is the amount of disk space that can be reclaimed. + Reclaimable int64 + + // TotalSize is the total disk space used by all containers. + TotalSize int64 + + // Items holds detailed information about each container. + Items []container.Summary +} + +// ImagesDiskUsage contains disk usage information for images. +type ImagesDiskUsage struct { + // ActiveImages is the number of active images. + ActiveImages int64 + + // TotalImages is the total number of images. + TotalImages int64 + + // Reclaimable is the amount of disk space that can be reclaimed. + Reclaimable int64 + + // TotalSize is the total disk space used by all images. + TotalSize int64 + + // Items holds detailed information about each image. + Items []image.Summary +} + +// VolumesDiskUsage contains disk usage information for volumes. +type VolumesDiskUsage struct { + // ActiveVolumes is the number of active volumes. + ActiveVolumes int64 + + // TotalVolumes is the total number of volumes. + TotalVolumes int64 + + // Reclaimable is the amount of disk space that can be reclaimed. + Reclaimable int64 + + // TotalSize is the total disk space used by all volumes. + TotalSize int64 + + // Items holds detailed information about each volume. + Items []volume.Volume +} + +// BuildCacheDiskUsage contains disk usage information for build cache. +type BuildCacheDiskUsage struct { + // ActiveBuildCacheRecords is the number of active build cache records. + ActiveBuildCacheRecords int64 + + // TotalBuildCacheRecords is the total number of build cache records. + TotalBuildCacheRecords int64 + + // Reclaimable is the amount of disk space that can be reclaimed. + Reclaimable int64 + + // TotalSize is the total disk space used by all build cache records. + TotalSize int64 + + // Items holds detailed information about each build cache record. + Items []build.CacheRecord +} + +// DiskUsage requests the current data usage from the daemon. +func (cli *Client) DiskUsage(ctx context.Context, options DiskUsageOptions) (DiskUsageResult, error) { + query := url.Values{} + + for _, t := range []struct { + flag bool + sysObj system.DiskUsageObject + }{ + {options.Containers, system.ContainerObject}, + {options.Images, system.ImageObject}, + {options.Volumes, system.VolumeObject}, + {options.BuildCache, system.BuildCacheObject}, + } { + if t.flag { + query.Add("type", string(t.sysObj)) } } + if options.Verbose { + query.Set("verbose", "1") + } + resp, err := cli.get(ctx, "/system/df", query, nil) defer ensureReaderClosed(resp) if err != nil { - return system.DiskUsage{}, err + return DiskUsageResult{}, err } var du system.DiskUsage if err := json.NewDecoder(resp.Body).Decode(&du); err != nil { - return system.DiskUsage{}, fmt.Errorf("Error retrieving disk usage: %v", err) + return DiskUsageResult{}, fmt.Errorf("Error retrieving disk usage: %v", err) } - return du, nil + + // Generate result from a legacy response. + if versions.LessThan(cli.version, "1.52") { + return diskUsageResultFromLegacyAPI(&du), nil + } + + var r DiskUsageResult + if idu := du.ImageUsage; idu != nil { + r.Images = ImagesDiskUsage{ + ActiveImages: idu.ActiveImages, + Reclaimable: idu.Reclaimable, + TotalImages: idu.TotalImages, + TotalSize: idu.TotalSize, + } + + if options.Verbose { + r.Images.Items = slices.Clone(idu.Items) + } + } + + if cdu := du.ContainerUsage; cdu != nil { + r.Containers = ContainersDiskUsage{ + ActiveContainers: cdu.ActiveContainers, + Reclaimable: cdu.Reclaimable, + TotalContainers: cdu.TotalContainers, + TotalSize: cdu.TotalSize, + } + + if options.Verbose { + r.Containers.Items = slices.Clone(cdu.Items) + } + } + + if bdu := du.BuildCacheUsage; bdu != nil { + r.BuildCache = BuildCacheDiskUsage{ + ActiveBuildCacheRecords: bdu.ActiveBuildCacheRecords, + Reclaimable: bdu.Reclaimable, + TotalBuildCacheRecords: bdu.TotalBuildCacheRecords, + TotalSize: bdu.TotalSize, + } + + if options.Verbose { + r.BuildCache.Items = slices.Clone(bdu.Items) + } + } + + if vdu := du.VolumeUsage; vdu != nil { + r.Volumes = VolumesDiskUsage{ + ActiveVolumes: vdu.ActiveVolumes, + Reclaimable: vdu.Reclaimable, + TotalVolumes: vdu.TotalVolumes, + TotalSize: vdu.TotalSize, + } + + if options.Verbose { + r.Volumes.Items = slices.Clone(vdu.Items) + } + } + + return r, nil +} + +func diskUsageResultFromLegacyAPI(du *system.DiskUsage) DiskUsageResult { + return DiskUsageResult{ + Images: imageDiskUsageFromLegacyAPI(du), + Containers: containerDiskUsageFromLegacyAPI(du), + BuildCache: buildCacheDiskUsageFromLegacyAPI(du), + Volumes: volumeDiskUsageFromLegacyAPI(du), + } +} + +func imageDiskUsageFromLegacyAPI(du *system.DiskUsage) ImagesDiskUsage { + idu := ImagesDiskUsage{ + TotalSize: du.LayersSize, + TotalImages: int64(len(du.Images)), + Items: du.Images, + } + + var used int64 + for _, i := range idu.Items { + if i.Containers > 0 { + idu.ActiveImages++ + + if i.Size == -1 || i.SharedSize == -1 { + continue + } + used += (i.Size - i.SharedSize) + } + } + + if idu.TotalImages > 0 { + idu.Reclaimable = idu.TotalSize - used + } + + return idu +} + +func containerDiskUsageFromLegacyAPI(du *system.DiskUsage) ContainersDiskUsage { + cdu := ContainersDiskUsage{ + TotalContainers: int64(len(du.Containers)), + Items: du.Containers, + } + + var used int64 + for _, c := range cdu.Items { + cdu.TotalSize += c.SizeRw + switch strings.ToLower(c.State) { + case "running", "paused", "restarting": + cdu.ActiveContainers++ + used += c.SizeRw + } + } + + cdu.Reclaimable = cdu.TotalSize - used + return cdu +} + +func buildCacheDiskUsageFromLegacyAPI(du *system.DiskUsage) BuildCacheDiskUsage { + bdu := BuildCacheDiskUsage{ + TotalBuildCacheRecords: int64(len(du.BuildCache)), + Items: du.BuildCache, + } + + var used int64 + for _, b := range du.BuildCache { + if !b.Shared { + bdu.TotalSize += b.Size + } + + if b.InUse { + bdu.ActiveBuildCacheRecords++ + if !b.Shared { + used += b.Size + } + } + } + + bdu.Reclaimable = bdu.TotalSize - used + return bdu +} + +func volumeDiskUsageFromLegacyAPI(du *system.DiskUsage) VolumesDiskUsage { + vdu := VolumesDiskUsage{ + TotalVolumes: int64(len(du.Volumes)), + Items: du.Volumes, + } + + var used int64 + for _, v := range vdu.Items { + // Ignore volumes with no usage data + if v.UsageData != nil { + if v.UsageData.RefCount > 0 { + vdu.ActiveVolumes++ + used += v.UsageData.Size + } + if v.UsageData.Size > 0 { + vdu.TotalSize += v.UsageData.Size + } + } + } + + vdu.Reclaimable = vdu.TotalSize - used + return vdu } diff --git a/vendor/github.com/moby/moby/client/system_disk_usage_opts.go b/vendor/github.com/moby/moby/client/system_disk_usage_opts.go deleted file mode 100644 index e671b0cb7..000000000 --- a/vendor/github.com/moby/moby/client/system_disk_usage_opts.go +++ /dev/null @@ -1,10 +0,0 @@ -package client - -import "github.com/moby/moby/api/types/system" - -// DiskUsageOptions holds parameters for system disk usage query. -type DiskUsageOptions struct { - // Types specifies what object types to include in the response. If empty, - // all object types are returned. - Types []system.DiskUsageObject -} diff --git a/vendor/github.com/moby/moby/client/task_logs.go b/vendor/github.com/moby/moby/client/task_logs.go index c42c1028a..e4de019f3 100644 --- a/vendor/github.com/moby/moby/client/task_logs.go +++ b/vendor/github.com/moby/moby/client/task_logs.go @@ -4,7 +4,6 @@ import ( "context" "io" "net/url" - "sync" "time" "github.com/moby/moby/client/internal/timestamp" @@ -24,14 +23,18 @@ type TaskLogsOptions struct { // TaskLogsResult holds the result of a task logs operation. // It implements [io.ReadCloser]. -type TaskLogsResult struct { - rc io.ReadCloser - close func() error +type TaskLogsResult interface { + io.ReadCloser } -// TaskLogs returns the logs generated by a task. -// It's up to the caller to close the stream. +// TaskLogs returns the logs generated by a service in a [TaskLogsResult]. +// as an [io.ReadCloser]. Callers should close the stream. +// +// The underlying [io.ReadCloser] is automatically closed if the context is canceled, func (cli *Client) TaskLogs(ctx context.Context, taskID string, options TaskLogsOptions) (TaskLogsResult, error) { + // TODO(thaJeztah): this function needs documentation about the format of ths stream (similar to for container logs) + // TODO(thaJeztah): migrate CLI utilities to the client where suitable; https://github.com/docker/cli/blob/v29.0.0-rc.1/cli/command/service/logs.go#L73-L348 + query := url.Values{} if options.ShowStdout { query.Set("stdout", "1") @@ -44,7 +47,7 @@ func (cli *Client) TaskLogs(ctx context.Context, taskID string, options TaskLogs if options.Since != "" { ts, err := timestamp.GetTimestamp(options.Since, time.Now()) if err != nil { - return TaskLogsResult{}, err + return nil, err } query.Set("since", ts) } @@ -64,33 +67,18 @@ func (cli *Client) TaskLogs(ctx context.Context, taskID string, options TaskLogs resp, err := cli.get(ctx, "/tasks/"+taskID+"/logs", query, nil) if err != nil { - return TaskLogsResult{}, err + return nil, err } - return newTaskLogsResult(resp.Body), nil + return &taskLogsResult{ + ReadCloser: newCancelReadCloser(ctx, resp.Body), + }, nil } -func newTaskLogsResult(rc io.ReadCloser) TaskLogsResult { - if rc == nil { - panic("nil io.ReadCloser") - } - return TaskLogsResult{ - rc: rc, - close: sync.OnceValue(rc.Close), - } +type taskLogsResult struct { + io.ReadCloser } -// Read implements [io.ReadCloser] for LogsResult. -func (r TaskLogsResult) Read(p []byte) (n int, err error) { - if r.rc == nil { - return 0, io.EOF - } - return r.rc.Read(p) -} - -// Close implements [io.ReadCloser] for LogsResult. -func (r TaskLogsResult) Close() error { - if r.close == nil { - return nil - } - return r.close() -} +var ( + _ io.ReadCloser = (*taskLogsResult)(nil) + _ ContainerLogsResult = (*taskLogsResult)(nil) +) diff --git a/vendor/github.com/moby/moby/client/utils.go b/vendor/github.com/moby/moby/client/utils.go index 54d0deef1..6a5fc46ea 100644 --- a/vendor/github.com/moby/moby/client/utils.go +++ b/vendor/github.com/moby/moby/client/utils.go @@ -2,12 +2,14 @@ package client import ( "bytes" + "context" "encoding/json" "errors" "fmt" "io" "net/http" "strings" + "sync" cerrdefs "github.com/containerd/errdefs" ocispec "github.com/opencontainers/image-spec/specs-go/v1" @@ -84,3 +86,22 @@ func decodeWithRaw[T any](resp *http.Response, out *T) (raw json.RawMessage, _ e } return buf.Bytes(), nil } + +// newCancelReadCloser wraps rc so it's automatically closed when ctx is canceled. +// Close is idempotent and returns the first error from rc.Close. +func newCancelReadCloser(ctx context.Context, rc io.ReadCloser) io.ReadCloser { + crc := &cancelReadCloser{ + rc: rc, + close: sync.OnceValue(rc.Close), + } + context.AfterFunc(ctx, func() { _ = crc.Close() }) + return crc +} + +type cancelReadCloser struct { + rc io.ReadCloser + close func() error +} + +func (c *cancelReadCloser) Read(p []byte) (int, error) { return c.rc.Read(p) } +func (c *cancelReadCloser) Close() error { return c.close() } diff --git a/vendor/github.com/moby/moby/client/volume_prune.go b/vendor/github.com/moby/moby/client/volume_prune.go index 548776279..561e328d7 100644 --- a/vendor/github.com/moby/moby/client/volume_prune.go +++ b/vendor/github.com/moby/moby/client/volume_prune.go @@ -20,13 +20,13 @@ type VolumePruneOptions struct { Filters Filters } -// VolumePruneResult holds the result from the [Client.VolumesPrune] method. +// VolumePruneResult holds the result from the [Client.VolumePrune] method. type VolumePruneResult struct { Report volume.PruneReport } -// VolumesPrune requests the daemon to delete unused data -func (cli *Client) VolumesPrune(ctx context.Context, options VolumePruneOptions) (VolumePruneResult, error) { +// VolumePrune requests the daemon to delete unused data +func (cli *Client) VolumePrune(ctx context.Context, options VolumePruneOptions) (VolumePruneResult, error) { if options.All { if _, ok := options.Filters["all"]; ok { return VolumePruneResult{}, errdefs.ErrInvalidArgument.WithMessage(`conflicting options: cannot specify both "all" and "all" filter`) diff --git a/vendor/modules.txt b/vendor/modules.txt index dcedbbb1f..5f91cf4d0 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -167,7 +167,7 @@ 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.4 +# github.com/moby/moby/api v1.52.0-beta.4.0.20251106210608-f7fd9c315acf => github.com/moby/moby/api v1.52.0-beta.4.0.20251106221347-217fd7890581 ## explicit; go 1.23.0 github.com/moby/moby/api/pkg/authconfig github.com/moby/moby/api/pkg/stdcopy @@ -189,7 +189,7 @@ 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.3 +# github.com/moby/moby/client v0.1.0-beta.3.0.20251106221347-217fd7890581 ## explicit; go 1.23.0 github.com/moby/moby/client github.com/moby/moby/client/internal @@ -543,3 +543,4 @@ gotest.tools/v3/skip # tags.cncf.io/container-device-interface v1.0.1 ## explicit; go 1.20 tags.cncf.io/container-device-interface/pkg/parser +# github.com/moby/moby/api => github.com/moby/moby/api v1.52.0-beta.4.0.20251106221347-217fd7890581