diff --git a/Dockerfile b/Dockerfile index f5213cbf18c5..6ea150890011 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,19 +1,30 @@ # syntax=docker/dockerfile:1 ARG BASE_VARIANT=alpine + +# ALPINE_VERSION sets the version of the alpine base image to use, including for the golang image. +# It must be a supported tag in the docker.io/library/alpine image repository +# that's also available as alpine image variant for the Golang version used. ARG ALPINE_VERSION=3.21 ARG BASE_DEBIAN_DISTRO=bookworm ARG GO_VERSION=1.24.5 ARG XX_VERSION=1.6.1 ARG GOVERSIONINFO_VERSION=v1.4.1 -ARG GOTESTSUM_VERSION=v1.12.0 + +# GOTESTSUM_VERSION sets the version of gotestsum to install in the dev container. +# It must be a valid tag in the https://github.com/gotestyourself/gotestsum repository. +ARG GOTESTSUM_VERSION=v1.12.3 # BUILDX_VERSION sets the version of buildx to use for the e2e tests. # It must be a tag in the docker.io/docker/buildx-bin image repository # on Docker Hub. -ARG BUILDX_VERSION=0.24.0 -ARG COMPOSE_VERSION=v2.36.2 +ARG BUILDX_VERSION=0.25.0 + +# COMPOSE_VERSION is the version of compose to install in the dev container. +# It must be a tag in the docker.io/docker/compose-bin image repository +# on Docker Hub. +ARG COMPOSE_VERSION=v2.38.2 FROM --platform=$BUILDPLATFORM tonistiigi/xx:${XX_VERSION} AS xx diff --git a/cli/command/config/formatter.go b/cli/command/config/formatter.go index 42072d75807f..f2defa721b80 100644 --- a/cli/command/config/formatter.go +++ b/cli/command/config/formatter.go @@ -8,7 +8,7 @@ import ( "github.com/docker/cli/cli/command/formatter" "github.com/docker/cli/cli/command/inspect" "github.com/docker/docker/api/types/swarm" - units "github.com/docker/go-units" + "github.com/docker/go-units" ) const ( diff --git a/cli/command/container/cp.go b/cli/command/container/cp.go index 9fd0eda9b303..40b038458703 100644 --- a/cli/command/container/cp.go +++ b/cli/command/container/cp.go @@ -16,7 +16,7 @@ import ( "github.com/docker/cli/cli/command" "github.com/docker/cli/cli/streams" "github.com/docker/docker/api/types/container" - units "github.com/docker/go-units" + "github.com/docker/go-units" "github.com/moby/go-archive" "github.com/morikuni/aec" "github.com/pkg/errors" @@ -398,8 +398,7 @@ func copyToContainer(ctx context.Context, dockerCLI command.Cli, copyConfig cpCo } options := container.CopyToContainerOptions{ - AllowOverwriteDirWithFile: false, - CopyUIDGID: copyConfig.copyUIDGID, + CopyUIDGID: copyConfig.copyUIDGID, } if copyConfig.quiet { diff --git a/cli/command/container/diff.go b/cli/command/container/diff.go index 15699f5dbb06..93791fbd094e 100644 --- a/cli/command/container/diff.go +++ b/cli/command/container/diff.go @@ -7,25 +7,17 @@ import ( "github.com/docker/cli/cli/command" "github.com/docker/cli/cli/command/completion" "github.com/docker/cli/cli/command/formatter" - "github.com/pkg/errors" "github.com/spf13/cobra" ) -type diffOptions struct { - container string -} - // NewDiffCommand creates a new cobra.Command for `docker diff` func NewDiffCommand(dockerCli command.Cli) *cobra.Command { - var opts diffOptions - return &cobra.Command{ Use: "diff CONTAINER", Short: "Inspect changes to files or directories on a container's filesystem", Args: cli.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { - opts.container = args[0] - return runDiff(cmd.Context(), dockerCli, &opts) + return runDiff(cmd.Context(), dockerCli, args[0]) }, Annotations: map[string]string{ "aliases": "docker container diff, docker diff", @@ -34,16 +26,13 @@ func NewDiffCommand(dockerCli command.Cli) *cobra.Command { } } -func runDiff(ctx context.Context, dockerCli command.Cli, opts *diffOptions) error { - if opts.container == "" { - return errors.New("Container name cannot be empty") - } - changes, err := dockerCli.Client().ContainerDiff(ctx, opts.container) +func runDiff(ctx context.Context, dockerCLI command.Cli, containerID string) error { + changes, err := dockerCLI.Client().ContainerDiff(ctx, containerID) if err != nil { return err } diffCtx := formatter.Context{ - Output: dockerCli.Out(), + Output: dockerCLI.Out(), Format: NewDiffFormat("{{.Type}} {{.Path}}"), } return DiffFormatWrite(diffCtx, changes) diff --git a/cli/command/container/diff_test.go b/cli/command/container/diff_test.go index fd2271e434da..685a95d125ba 100644 --- a/cli/command/container/diff_test.go +++ b/cli/command/container/diff_test.go @@ -77,17 +77,3 @@ func TestRunDiffClientError(t *testing.T) { err := cmd.Execute() assert.ErrorIs(t, err, clientError) } - -func TestRunDiffEmptyContainerError(t *testing.T) { - cli := test.NewFakeCli(&fakeClient{}) - - cmd := NewDiffCommand(cli) - cmd.SetOut(io.Discard) - cmd.SetErr(io.Discard) - - containerID := "" - cmd.SetArgs([]string{containerID}) - - err := cmd.Execute() - assert.Error(t, err, "Container name cannot be empty") -} diff --git a/cli/command/container/formatter_stats.go b/cli/command/container/formatter_stats.go index b7fdf4733353..48371eedc890 100644 --- a/cli/command/container/formatter_stats.go +++ b/cli/command/container/formatter_stats.go @@ -5,8 +5,7 @@ import ( "sync" "github.com/docker/cli/cli/command/formatter" - "github.com/docker/docker/pkg/stringid" - units "github.com/docker/go-units" + "github.com/docker/go-units" ) const ( @@ -176,7 +175,7 @@ func (c *statsContext) Name() string { func (c *statsContext) ID() string { if c.trunc { - return stringid.TruncateID(c.s.ID) + return formatter.TruncateID(c.s.ID) } return c.s.ID } diff --git a/cli/command/container/formatter_stats_test.go b/cli/command/container/formatter_stats_test.go index 8569ef4de462..91c69a5d7c26 100644 --- a/cli/command/container/formatter_stats_test.go +++ b/cli/command/container/formatter_stats_test.go @@ -5,13 +5,13 @@ import ( "testing" "github.com/docker/cli/cli/command/formatter" - "github.com/docker/docker/pkg/stringid" + "github.com/docker/cli/internal/test" "gotest.tools/v3/assert" is "gotest.tools/v3/assert/cmp" ) func TestContainerStatsContext(t *testing.T) { - containerID := stringid.GenerateRandomID() + containerID := test.RandomID() var ctx statsContext tt := []struct { diff --git a/cli/command/container/hijack.go b/cli/command/container/hijack.go index 4d5ad26786d2..8e725cb44432 100644 --- a/cli/command/container/hijack.go +++ b/cli/command/container/hijack.go @@ -9,7 +9,6 @@ import ( "github.com/docker/cli/cli/command" "github.com/docker/docker/api/types" - "github.com/docker/docker/pkg/ioutils" "github.com/docker/docker/pkg/stdcopy" "github.com/moby/term" "github.com/sirupsen/logrus" @@ -19,6 +18,18 @@ import ( // TODO: This could be moved to `pkg/term`. var defaultEscapeKeys = []byte{16, 17} +// readCloserWrapper wraps an io.Reader, and implements an io.ReadCloser +// It calls the given callback function when closed. +type readCloserWrapper struct { + io.Reader + closer func() error +} + +// Close calls back the passed closer function +func (r *readCloserWrapper) Close() error { + return r.closer() +} + // A hijackedIOStreamer handles copying input to and output from streams to the // connection. type hijackedIOStreamer struct { @@ -100,7 +111,10 @@ func (h *hijackedIOStreamer) setupInput() (restore func(), err error) { } } - h.inputStream = ioutils.NewReadCloserWrapper(term.NewEscapeProxy(h.inputStream, escapeKeys), h.inputStream.Close) + h.inputStream = &readCloserWrapper{ + Reader: term.NewEscapeProxy(h.inputStream, escapeKeys), + closer: h.inputStream.Close, + } return restore, nil } diff --git a/cli/command/container/prune.go b/cli/command/container/prune.go index 7a1d575d6e78..d75338718e36 100644 --- a/cli/command/container/prune.go +++ b/cli/command/container/prune.go @@ -9,7 +9,7 @@ import ( "github.com/docker/cli/cli/command/completion" "github.com/docker/cli/internal/prompt" "github.com/docker/cli/opts" - units "github.com/docker/go-units" + "github.com/docker/go-units" "github.com/pkg/errors" "github.com/spf13/cobra" ) diff --git a/cli/command/formatter/buildcache.go b/cli/command/formatter/buildcache.go index a170e0eaf8d1..ade5de73f4fc 100644 --- a/cli/command/formatter/buildcache.go +++ b/cli/command/formatter/buildcache.go @@ -7,7 +7,6 @@ import ( "time" "github.com/docker/docker/api/types/build" - "github.com/docker/docker/pkg/stringid" "github.com/docker/go-units" ) @@ -115,7 +114,7 @@ func (c *buildCacheContext) MarshalJSON() ([]byte, error) { func (c *buildCacheContext) ID() string { id := c.v.ID if c.trunc { - id = stringid.TruncateID(c.v.ID) + id = TruncateID(c.v.ID) } if c.v.InUse { return id + "*" @@ -131,7 +130,7 @@ func (c *buildCacheContext) Parent() string { parent = c.v.Parent //nolint:staticcheck // Ignore SA1019: Field was deprecated in API v1.42, but kept for backward compatibility } if c.trunc { - return stringid.TruncateID(parent) + return TruncateID(parent) } return parent } diff --git a/cli/command/formatter/container.go b/cli/command/formatter/container.go index 4480221fbfee..0a5c587afe23 100644 --- a/cli/command/formatter/container.go +++ b/cli/command/formatter/container.go @@ -14,7 +14,6 @@ import ( "github.com/containerd/platforms" "github.com/distribution/reference" "github.com/docker/docker/api/types/container" - "github.com/docker/docker/pkg/stringid" "github.com/docker/go-units" ocispec "github.com/opencontainers/image-spec/specs-go/v1" ) @@ -135,7 +134,7 @@ func (c *ContainerContext) MarshalJSON() ([]byte, error) { // option being set, the full or truncated ID is returned. func (c *ContainerContext) ID() string { if c.trunc { - return stringid.TruncateID(c.c.ID) + return TruncateID(c.c.ID) } return c.c.ID } @@ -172,7 +171,7 @@ func (c *ContainerContext) Image() string { return "" } if c.trunc { - if trunc := stringid.TruncateID(c.c.ImageID); trunc == stringid.TruncateID(c.c.Image) { + if trunc := TruncateID(c.c.ImageID); trunc == TruncateID(c.c.Image) { return trunc } // truncate digest if no-trunc option was not selected diff --git a/cli/command/formatter/container_test.go b/cli/command/formatter/container_test.go index 87928926a516..6974584003cc 100644 --- a/cli/command/formatter/container_test.go +++ b/cli/command/formatter/container_test.go @@ -13,7 +13,6 @@ import ( "github.com/docker/cli/internal/test" "github.com/docker/docker/api/types/container" - "github.com/docker/docker/pkg/stringid" ocispec "github.com/opencontainers/image-spec/specs-go/v1" "gotest.tools/v3/assert" is "gotest.tools/v3/assert/cmp" @@ -21,7 +20,7 @@ import ( ) func TestContainerPsContext(t *testing.T) { - containerID := stringid.GenerateRandomID() + containerID := test.RandomID() unix := time.Now().Add(-65 * time.Second).Unix() var ctx ContainerContext @@ -34,7 +33,7 @@ func TestContainerPsContext(t *testing.T) { { container: container.Summary{ID: containerID}, trunc: true, - expValue: stringid.TruncateID(containerID), + expValue: TruncateID(containerID), call: ctx.ID, }, { diff --git a/cli/command/formatter/disk_usage.go b/cli/command/formatter/disk_usage.go index 770697f3cff2..b663c59b20b7 100644 --- a/cli/command/formatter/disk_usage.go +++ b/cli/command/formatter/disk_usage.go @@ -11,7 +11,7 @@ import ( "github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/image" "github.com/docker/docker/api/types/volume" - units "github.com/docker/go-units" + "github.com/docker/go-units" ) const ( diff --git a/cli/command/formatter/displayutils.go b/cli/command/formatter/displayutils.go index c6d2845c4e00..b062c3391bcb 100644 --- a/cli/command/formatter/displayutils.go +++ b/cli/command/formatter/displayutils.go @@ -27,6 +27,25 @@ func charWidth(r rune) int { } } +const shortLen = 12 + +// TruncateID returns a shorthand version of a string identifier for presentation, +// after trimming digest algorithm prefix (if any). +// +// This function is a copy of [stringid.TruncateID] for presentation / formatting +// purposes. +// +// [stringid.TruncateID]: https://github.com/moby/moby/blob/v28.3.2/pkg/stringid/stringid.go#L19 +func TruncateID(id string) string { + if i := strings.IndexRune(id, ':'); i >= 0 { + id = id[i+1:] + } + if len(id) > shortLen { + id = id[:shortLen] + } + return id +} + // Ellipsis truncates a string to fit within maxDisplayWidth, and appends ellipsis (…). // For maxDisplayWidth of 1 and lower, no ellipsis is appended. // For maxDisplayWidth of 1, first char of string will return even if its width > 1. diff --git a/cli/command/formatter/displayutils_test.go b/cli/command/formatter/displayutils_test.go index 1dd3e75cb7d7..131f90db1c75 100644 --- a/cli/command/formatter/displayutils_test.go +++ b/cli/command/formatter/displayutils_test.go @@ -7,6 +7,49 @@ import ( is "gotest.tools/v3/assert/cmp" ) +func TestTruncateID(t *testing.T) { + tests := []struct { + doc, id, expected string + }{ + { + doc: "empty ID", + id: "", + expected: "", + }, + { + // IDs are expected to be 12 (short) or 64 characters, and not be numeric only, + // but TruncateID should handle these gracefully. + doc: "invalid ID", + id: "1234", + expected: "1234", + }, + { + doc: "full ID", + id: "90435eec5c4e124e741ef731e118be2fc799a68aba0466ec17717f24ce2ae6a2", + expected: "90435eec5c4e", + }, + { + doc: "digest", + id: "sha256:90435eec5c4e124e741ef731e118be2fc799a68aba0466ec17717f24ce2ae6a2", + expected: "90435eec5c4e", + }, + { + doc: "very long ID", + id: "90435eec5c4e124e741ef731e118be2fc799a68aba0466ec17717f24ce2ae6a290435eec5c4e124e741ef731e118be2fc799a68aba0466ec17717f24ce2ae6a2", + expected: "90435eec5c4e", + }, + } + + for _, tc := range tests { + t.Run(tc.doc, func(t *testing.T) { + actual := TruncateID(tc.id) + if actual != tc.expected { + t.Errorf("expected: %q, got: %q", tc.expected, actual) + } + }) + } +} + func TestEllipsis(t *testing.T) { testcases := []struct { source string diff --git a/cli/command/formatter/image.go b/cli/command/formatter/image.go index d16f42b5ba2c..74c2fe758f84 100644 --- a/cli/command/formatter/image.go +++ b/cli/command/formatter/image.go @@ -6,8 +6,7 @@ import ( "github.com/distribution/reference" "github.com/docker/docker/api/types/image" - "github.com/docker/docker/pkg/stringid" - units "github.com/docker/go-units" + "github.com/docker/go-units" ) const ( @@ -216,7 +215,7 @@ func (c *imageContext) MarshalJSON() ([]byte, error) { func (c *imageContext) ID() string { if c.trunc { - return stringid.TruncateID(c.i.ID) + return TruncateID(c.i.ID) } return c.i.ID } diff --git a/cli/command/formatter/image_test.go b/cli/command/formatter/image_test.go index e180f4dba007..bb792f043988 100644 --- a/cli/command/formatter/image_test.go +++ b/cli/command/formatter/image_test.go @@ -9,13 +9,12 @@ import ( "github.com/docker/cli/internal/test" "github.com/docker/docker/api/types/image" - "github.com/docker/docker/pkg/stringid" "gotest.tools/v3/assert" is "gotest.tools/v3/assert/cmp" ) func TestImageContext(t *testing.T) { - imageID := stringid.GenerateRandomID() + imageID := test.RandomID() unix := time.Now().Unix() zeroTime := int64(-62135596800) @@ -27,7 +26,7 @@ func TestImageContext(t *testing.T) { }{ { imageCtx: imageContext{i: image.Summary{ID: imageID}, trunc: true}, - expValue: stringid.TruncateID(imageID), + expValue: TruncateID(imageID), call: ctx.ID, }, { diff --git a/cli/command/formatter/volume.go b/cli/command/formatter/volume.go index 85f070793148..bf9ea5d44e5d 100644 --- a/cli/command/formatter/volume.go +++ b/cli/command/formatter/volume.go @@ -6,7 +6,7 @@ import ( "strings" "github.com/docker/docker/api/types/volume" - units "github.com/docker/go-units" + "github.com/docker/go-units" ) const ( diff --git a/cli/command/formatter/volume_test.go b/cli/command/formatter/volume_test.go index 212c47d499b3..cae16c7cd103 100644 --- a/cli/command/formatter/volume_test.go +++ b/cli/command/formatter/volume_test.go @@ -12,13 +12,12 @@ import ( "github.com/docker/cli/internal/test" "github.com/docker/docker/api/types/volume" - "github.com/docker/docker/pkg/stringid" "gotest.tools/v3/assert" is "gotest.tools/v3/assert/cmp" ) func TestVolumeContext(t *testing.T) { - volumeName := stringid.GenerateRandomID() + volumeName := test.RandomID() var ctx volumeContext cases := []struct { diff --git a/cli/command/image/build/context.go b/cli/command/image/build/context.go index 4a6abf1515d9..ca70d5484c77 100644 --- a/cli/command/image/build/context.go +++ b/cli/command/image/build/context.go @@ -4,6 +4,8 @@ import ( "archive/tar" "bufio" "bytes" + "crypto/rand" + "encoding/hex" "fmt" "io" "net/http" @@ -15,10 +17,8 @@ import ( "time" "github.com/docker/docker/builder/remotecontext/git" - "github.com/docker/docker/pkg/ioutils" "github.com/docker/docker/pkg/progress" "github.com/docker/docker/pkg/streamformatter" - "github.com/docker/docker/pkg/stringid" "github.com/moby/go-archive" "github.com/moby/go-archive/compression" "github.com/moby/patternmatcher" @@ -108,7 +108,7 @@ func DetectArchiveReader(input io.ReadCloser) (rc io.ReadCloser, isArchive bool, return nil, false, errors.Errorf("failed to peek context header from STDIN: %v", err) } - return ioutils.NewReadCloserWrapper(buf, func() error { return input.Close() }), IsArchive(magic), nil + return newReadCloserWrapper(buf, func() error { return input.Close() }), IsArchive(magic), nil } // WriteTempDockerfile writes a Dockerfile stream to a temporary file with a @@ -169,7 +169,7 @@ func GetContextFromReader(rc io.ReadCloser, dockerfileName string) (out io.ReadC return nil, "", err } - return ioutils.NewReadCloserWrapper(tarArchive, func() error { + return newReadCloserWrapper(tarArchive, func() error { err := tarArchive.Close() os.RemoveAll(dockerfileDir) return err @@ -227,7 +227,7 @@ func GetContextFromURL(out io.Writer, remoteURL, dockerfileName string) (io.Read // Pass the response body through a progress reader. progReader := progress.NewProgressReader(response.Body, progressOutput, response.ContentLength, "", "Downloading build context from remote url: "+remoteURL) - return GetContextFromReader(ioutils.NewReadCloserWrapper(progReader, func() error { return response.Body.Close() }), dockerfileName) + return GetContextFromReader(newReadCloserWrapper(progReader, func() error { return response.Body.Close() }), dockerfileName) } // getWithStatusError does an http.Get() and returns an error if the @@ -379,7 +379,7 @@ func AddDockerfileToBuildContext(dockerfileCtx io.ReadCloser, buildCtx io.ReadCl return nil, "", err } now := time.Now() - randomName := ".dockerfile." + stringid.GenerateRandomID()[:20] + randomName := ".dockerfile." + randomSuffix() buildCtx = archive.ReplaceFileTarWrapper(buildCtx, map[string]archive.TarModifierFunc{ // Add the dockerfile with a random filename @@ -422,6 +422,15 @@ func AddDockerfileToBuildContext(dockerfileCtx io.ReadCloser, buildCtx io.ReadCl return buildCtx, randomName, nil } +// randomSuffix returns a unique, 20-character ID consisting of a-z, 0-9. +func randomSuffix() string { + b := make([]byte, 32) + if _, err := rand.Read(b); err != nil { + panic(err) // This shouldn't happen + } + return hex.EncodeToString(b)[:20] +} + // Compress the build context for sending to the API func Compress(buildCtx io.ReadCloser) (io.ReadCloser, error) { pipeReader, pipeWriter := io.Pipe() @@ -444,3 +453,25 @@ func Compress(buildCtx io.ReadCloser) (io.ReadCloser, error) { return pipeReader, nil } + +// readCloserWrapper wraps an io.Reader, and implements an io.ReadCloser +// It calls the given callback function when closed. It should be constructed +// with [newReadCloserWrapper]. +type readCloserWrapper struct { + io.Reader + closer func() error +} + +// Close calls back the passed closer function +func (r *readCloserWrapper) Close() error { + return r.closer() +} + +// newReadCloserWrapper wraps an io.Reader, and implements an io.ReadCloser. +// It calls the given callback function when closed. +func newReadCloserWrapper(r io.Reader, closer func() error) io.ReadCloser { + return &readCloserWrapper{ + Reader: r, + closer: closer, + } +} diff --git a/cli/command/image/build/context_windows.go b/cli/command/image/build/context_windows.go index f127e5a14019..ffd6ec53dd2d 100644 --- a/cli/command/image/build/context_windows.go +++ b/cli/command/image/build/context_windows.go @@ -2,8 +2,7 @@ package build import ( "path/filepath" - - "github.com/docker/docker/pkg/longpath" + "strings" ) func getContextRoot(srcPath string) (string, error) { @@ -11,5 +10,27 @@ func getContextRoot(srcPath string) (string, error) { if err != nil { return "", err } - return longpath.AddPrefix(cr), nil + return addPrefix(cr), nil +} + +// longPathPrefix is the longpath prefix for Windows file paths. +const longPathPrefix = `\\?\` + +// addPrefix adds the Windows long path prefix to the path provided if +// it does not already have it. +// +// See https://github.com/moby/moby/pull/15898 +// +// This is a copy of [longpath.AddPrefix]. +// +// [longpath.AddPrefix]:https://pkg.go.dev/github.com/docker/docker@v28.3.2+incompatible/pkg/longpath#AddPrefix +func addPrefix(path string) string { + if strings.HasPrefix(path, longPathPrefix) { + return path + } + if strings.HasPrefix(path, `\\`) { + // This is a UNC path, so we need to add 'UNC' to the path as well. + return longPathPrefix + `UNC` + path[1:] + } + return longPathPrefix + path } diff --git a/cli/command/image/build/context_windows_test.go b/cli/command/image/build/context_windows_test.go new file mode 100644 index 000000000000..bf91b8718f60 --- /dev/null +++ b/cli/command/image/build/context_windows_test.go @@ -0,0 +1,22 @@ +package build + +import ( + "strings" + "testing" +) + +func TestStandardLongPath(t *testing.T) { + c := `C:\simple\path` + longC := addPrefix(c) + if !strings.EqualFold(longC, `\\?\C:\simple\path`) { + t.Errorf("Wrong long path returned. Original = %s ; Long = %s", c, longC) + } +} + +func TestUNCLongPath(t *testing.T) { + c := `\\server\share\path` + longC := addPrefix(c) + if !strings.EqualFold(longC, `\\?\UNC\server\share\path`) { + t.Errorf("Wrong UNC long path returned. Original = %s ; Long = %s", c, longC) + } +} diff --git a/cli/command/image/formatter_history.go b/cli/command/image/formatter_history.go index 08b7febe3b1c..e2fcd155ce23 100644 --- a/cli/command/image/formatter_history.go +++ b/cli/command/image/formatter_history.go @@ -7,8 +7,7 @@ import ( "github.com/docker/cli/cli/command/formatter" "github.com/docker/docker/api/types/image" - "github.com/docker/docker/pkg/stringid" - units "github.com/docker/go-units" + "github.com/docker/go-units" ) const ( @@ -72,7 +71,7 @@ func (c *historyContext) MarshalJSON() ([]byte, error) { func (c *historyContext) ID() string { if c.trunc { - return stringid.TruncateID(c.h.ID) + return formatter.TruncateID(c.h.ID) } return c.h.ID } diff --git a/cli/command/image/formatter_history_test.go b/cli/command/image/formatter_history_test.go index e67fa946f006..611f7d50674f 100644 --- a/cli/command/image/formatter_history_test.go +++ b/cli/command/image/formatter_history_test.go @@ -10,7 +10,6 @@ import ( "github.com/docker/cli/cli/command/formatter" "github.com/docker/cli/internal/test" "github.com/docker/docker/api/types/image" - "github.com/docker/docker/pkg/stringid" "gotest.tools/v3/assert" ) @@ -21,7 +20,7 @@ type historyCase struct { } func TestHistoryContext_ID(t *testing.T) { - id := stringid.GenerateRandomID() + id := test.RandomID() var ctx historyContext cases := []historyCase{ @@ -35,7 +34,7 @@ func TestHistoryContext_ID(t *testing.T) { historyContext{ h: image.HistoryResponseItem{ID: id}, trunc: true, - }, stringid.TruncateID(id), ctx.ID, + }, formatter.TruncateID(id), ctx.ID, }, } diff --git a/cli/command/image/tree.go b/cli/command/image/tree.go index f12db416199a..c12c7096b5c5 100644 --- a/cli/command/image/tree.go +++ b/cli/command/image/tree.go @@ -12,10 +12,10 @@ import ( "github.com/containerd/platforms" "github.com/docker/cli/cli/command" + "github.com/docker/cli/cli/command/formatter" "github.com/docker/cli/internal/tui" "github.com/docker/docker/api/types/filters" imagetypes "github.com/docker/docker/api/types/image" - "github.com/docker/docker/pkg/stringid" "github.com/docker/go-units" "github.com/morikuni/aec" "github.com/opencontainers/go-digest" @@ -222,7 +222,7 @@ func printImageTree(dockerCLI command.Cli, view treeView) error { Align: alignLeft, Width: 12, DetailsValue: func(d *imageDetails) string { - return stringid.TruncateID(d.ID) + return formatter.TruncateID(d.ID) }, }, { diff --git a/cli/command/network/formatter.go b/cli/command/network/formatter.go index fb2177e7ab16..6a15a6be1070 100644 --- a/cli/command/network/formatter.go +++ b/cli/command/network/formatter.go @@ -6,7 +6,6 @@ import ( "github.com/docker/cli/cli/command/formatter" "github.com/docker/docker/api/types/network" - "github.com/docker/docker/pkg/stringid" ) const ( @@ -73,7 +72,7 @@ func (c *networkContext) MarshalJSON() ([]byte, error) { func (c *networkContext) ID() string { if c.trunc { - return stringid.TruncateID(c.n.ID) + return formatter.TruncateID(c.n.ID) } return c.n.ID } diff --git a/cli/command/network/formatter_test.go b/cli/command/network/formatter_test.go index ac40c2ffd488..ddeeb895fdb6 100644 --- a/cli/command/network/formatter_test.go +++ b/cli/command/network/formatter_test.go @@ -14,13 +14,12 @@ import ( "github.com/docker/cli/cli/command/formatter" "github.com/docker/cli/internal/test" "github.com/docker/docker/api/types/network" - "github.com/docker/docker/pkg/stringid" "gotest.tools/v3/assert" is "gotest.tools/v3/assert/cmp" ) func TestNetworkContext(t *testing.T) { - networkID := stringid.GenerateRandomID() + networkID := test.RandomID() var ctx networkContext cases := []struct { @@ -35,7 +34,7 @@ func TestNetworkContext(t *testing.T) { {networkContext{ n: network.Summary{ID: networkID}, trunc: true, - }, stringid.TruncateID(networkID), ctx.ID}, + }, formatter.TruncateID(networkID), ctx.ID}, {networkContext{ n: network.Summary{Name: "network_name"}, }, "network_name", ctx.Name}, diff --git a/cli/command/node/formatter.go b/cli/command/node/formatter.go index 19c3530bb047..c49ad30abe5b 100644 --- a/cli/command/node/formatter.go +++ b/cli/command/node/formatter.go @@ -10,7 +10,7 @@ import ( "github.com/docker/cli/cli/command/inspect" "github.com/docker/docker/api/types/swarm" "github.com/docker/docker/api/types/system" - units "github.com/docker/go-units" + "github.com/docker/go-units" ) const ( diff --git a/cli/command/node/formatter_test.go b/cli/command/node/formatter_test.go index c8e4658e7762..8983f337a6cb 100644 --- a/cli/command/node/formatter_test.go +++ b/cli/command/node/formatter_test.go @@ -14,13 +14,12 @@ import ( "github.com/docker/cli/internal/test" "github.com/docker/docker/api/types/swarm" "github.com/docker/docker/api/types/system" - "github.com/docker/docker/pkg/stringid" "gotest.tools/v3/assert" is "gotest.tools/v3/assert/cmp" ) func TestNodeContext(t *testing.T) { - nodeID := stringid.GenerateRandomID() + nodeID := test.RandomID() var ctx nodeContext cases := []struct { diff --git a/cli/command/plugin/formatter.go b/cli/command/plugin/formatter.go index c5c0c9c4e4ca..27c24b52cdeb 100644 --- a/cli/command/plugin/formatter.go +++ b/cli/command/plugin/formatter.go @@ -5,7 +5,6 @@ import ( "github.com/docker/cli/cli/command/formatter" "github.com/docker/docker/api/types" - "github.com/docker/docker/pkg/stringid" ) const ( @@ -66,7 +65,7 @@ func (c *pluginContext) MarshalJSON() ([]byte, error) { func (c *pluginContext) ID() string { if c.trunc { - return stringid.TruncateID(c.p.ID) + return formatter.TruncateID(c.p.ID) } return c.p.ID } diff --git a/cli/command/plugin/formatter_test.go b/cli/command/plugin/formatter_test.go index 6b2f83c4b635..63f1ce29e983 100644 --- a/cli/command/plugin/formatter_test.go +++ b/cli/command/plugin/formatter_test.go @@ -12,13 +12,12 @@ import ( "github.com/docker/cli/cli/command/formatter" "github.com/docker/cli/internal/test" "github.com/docker/docker/api/types" - "github.com/docker/docker/pkg/stringid" "gotest.tools/v3/assert" is "gotest.tools/v3/assert/cmp" ) func TestPluginContext(t *testing.T) { - pluginID := stringid.GenerateRandomID() + pluginID := test.RandomID() var ctx pluginContext cases := []struct { @@ -33,7 +32,7 @@ func TestPluginContext(t *testing.T) { {pluginContext{ p: types.Plugin{ID: pluginID}, trunc: true, - }, stringid.TruncateID(pluginID), ctx.ID}, + }, formatter.TruncateID(pluginID), ctx.ID}, {pluginContext{ p: types.Plugin{Name: "plugin_name"}, }, "plugin_name", ctx.Name}, diff --git a/cli/command/secret/formatter.go b/cli/command/secret/formatter.go index 2883bfcf4c79..e30547628665 100644 --- a/cli/command/secret/formatter.go +++ b/cli/command/secret/formatter.go @@ -8,7 +8,7 @@ import ( "github.com/docker/cli/cli/command/formatter" "github.com/docker/cli/cli/command/inspect" "github.com/docker/docker/api/types/swarm" - units "github.com/docker/go-units" + "github.com/docker/go-units" ) const ( diff --git a/cli/command/service/formatter.go b/cli/command/service/formatter.go index 499217ec114f..aee5453f10f9 100644 --- a/cli/command/service/formatter.go +++ b/cli/command/service/formatter.go @@ -14,8 +14,7 @@ import ( mounttypes "github.com/docker/docker/api/types/mount" "github.com/docker/docker/api/types/network" "github.com/docker/docker/api/types/swarm" - "github.com/docker/docker/pkg/stringid" - units "github.com/docker/go-units" + "github.com/docker/go-units" "github.com/fvbommel/sortorder" "github.com/pkg/errors" ) @@ -645,7 +644,7 @@ func (c *serviceContext) MarshalJSON() ([]byte, error) { } func (c *serviceContext) ID() string { - return stringid.TruncateID(c.service.ID) + return formatter.TruncateID(c.service.ID) } func (c *serviceContext) Name() string { diff --git a/cli/command/service/logs.go b/cli/command/service/logs.go index 7d394267b8c0..7c651d859bb2 100644 --- a/cli/command/service/logs.go +++ b/cli/command/service/logs.go @@ -13,13 +13,13 @@ import ( "github.com/docker/cli/cli" "github.com/docker/cli/cli/command" "github.com/docker/cli/cli/command/completion" + "github.com/docker/cli/cli/command/formatter" "github.com/docker/cli/cli/command/idresolver" "github.com/docker/cli/internal/logdetails" "github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/swarm" "github.com/docker/docker/client" "github.com/docker/docker/pkg/stdcopy" - "github.com/docker/docker/pkg/stringid" "github.com/pkg/errors" "github.com/spf13/cobra" "github.com/spf13/pflag" @@ -220,7 +220,7 @@ func (f *taskFormatter) format(ctx context.Context, logCtx logContext) (string, if f.opts.noTrunc { taskName += "." + task.ID } else { - taskName += "." + stringid.TruncateID(task.ID) + taskName += "." + formatter.TruncateID(task.ID) } } diff --git a/cli/command/service/progress/progress.go b/cli/command/service/progress/progress.go index c5c00e41c672..5f87e29125e7 100644 --- a/cli/command/service/progress/progress.go +++ b/cli/command/service/progress/progress.go @@ -11,12 +11,12 @@ import ( "strings" "time" + "github.com/docker/cli/cli/command/formatter" "github.com/docker/docker/api/types/filters" "github.com/docker/docker/api/types/swarm" "github.com/docker/docker/client" "github.com/docker/docker/pkg/progress" "github.com/docker/docker/pkg/streamformatter" - "github.com/docker/docker/pkg/stringid" ) var ( @@ -505,7 +505,7 @@ func (u *globalProgressUpdater) writeTaskProgress(task swarm.Task, nodeCount int if task.Status.Err != "" { u.progressOut.WriteProgress(progress.Progress{ - ID: stringid.TruncateID(task.NodeID), + ID: formatter.TruncateID(task.NodeID), Action: truncError(task.Status.Err), }) return @@ -513,7 +513,7 @@ func (u *globalProgressUpdater) writeTaskProgress(task swarm.Task, nodeCount int if !terminalState(task.DesiredState) && !terminalState(task.Status.State) { u.progressOut.WriteProgress(progress.Progress{ - ID: stringid.TruncateID(task.NodeID), + ID: formatter.TruncateID(task.NodeID), Action: fmt.Sprintf("%-[1]*s", longestState, task.Status.State), Current: numberedStates[task.Status.State], Total: maxProgress, diff --git a/cli/command/task/formatter.go b/cli/command/task/formatter.go index 0ce03d66918e..b87ab3a4b715 100644 --- a/cli/command/task/formatter.go +++ b/cli/command/task/formatter.go @@ -8,7 +8,6 @@ import ( "github.com/distribution/reference" "github.com/docker/cli/cli/command/formatter" "github.com/docker/docker/api/types/swarm" - "github.com/docker/docker/pkg/stringid" "github.com/docker/go-units" ) @@ -79,7 +78,7 @@ func (c *taskContext) MarshalJSON() ([]byte, error) { func (c *taskContext) ID() string { if c.trunc { - return stringid.TruncateID(c.task.ID) + return formatter.TruncateID(c.task.ID) } return c.task.ID } diff --git a/cli/command/trust/formatter.go b/cli/command/trust/formatter.go index 5cf9e9d3357f..9597cfbd7697 100644 --- a/cli/command/trust/formatter.go +++ b/cli/command/trust/formatter.go @@ -5,7 +5,6 @@ import ( "strings" "github.com/docker/cli/cli/command/formatter" - "github.com/docker/docker/pkg/stringid" ) const ( @@ -119,7 +118,7 @@ func (c *signerInfoContext) Keys() string { truncatedKeys := []string{} if c.trunc { for _, keyID := range c.s.Keys { - truncatedKeys = append(truncatedKeys, stringid.TruncateID(keyID)) + truncatedKeys = append(truncatedKeys, formatter.TruncateID(keyID)) } return strings.Join(truncatedKeys, ", ") } diff --git a/cli/command/trust/formatter_test.go b/cli/command/trust/formatter_test.go index db1534526f0f..4c0c194f4c2a 100644 --- a/cli/command/trust/formatter_test.go +++ b/cli/command/trust/formatter_test.go @@ -5,13 +5,13 @@ import ( "testing" "github.com/docker/cli/cli/command/formatter" - "github.com/docker/docker/pkg/stringid" + "github.com/docker/cli/internal/test" "gotest.tools/v3/assert" is "gotest.tools/v3/assert/cmp" ) func TestTrustTag(t *testing.T) { - digest := stringid.GenerateRandomID() + digest := test.RandomID() trustedTag := "tag" var ctx trustTagContext diff --git a/cli/compose/loader/loader.go b/cli/compose/loader/loader.go index 4085f2a28f74..b2673394b5bb 100644 --- a/cli/compose/loader/loader.go +++ b/cli/compose/loader/loader.go @@ -21,7 +21,7 @@ import ( "github.com/docker/cli/opts/swarmopts" "github.com/docker/docker/api/types/versions" "github.com/docker/go-connections/nat" - units "github.com/docker/go-units" + "github.com/docker/go-units" "github.com/go-viper/mapstructure/v2" "github.com/google/shlex" "github.com/pkg/errors" diff --git a/dockerfiles/Dockerfile.authors b/dockerfiles/Dockerfile.authors index 35d79283142a..e2a25e84165a 100644 --- a/dockerfiles/Dockerfile.authors +++ b/dockerfiles/Dockerfile.authors @@ -1,5 +1,7 @@ # syntax=docker/dockerfile:1 +# ALPINE_VERSION sets the version of the alpine base image to use. +# It must be a supported tag in the docker.io/library/alpine image repository. ARG ALPINE_VERSION=3.21 FROM alpine:${ALPINE_VERSION} AS gen diff --git a/dockerfiles/Dockerfile.dev b/dockerfiles/Dockerfile.dev index 4067f16de816..0b81e090c8b9 100644 --- a/dockerfiles/Dockerfile.dev +++ b/dockerfiles/Dockerfile.dev @@ -1,12 +1,16 @@ # syntax=docker/dockerfile:1 ARG GO_VERSION=1.24.5 + +# ALPINE_VERSION sets the version of the alpine base image to use, including for the golang image. +# It must be a supported tag in the docker.io/library/alpine image repository +# that's also available as alpine image variant for the Golang version used. ARG ALPINE_VERSION=3.21 # BUILDX_VERSION sets the version of buildx to install in the dev container. # It must be a valid tag in the docker.io/docker/buildx-bin image repository # on Docker Hub. -ARG BUILDX_VERSION=0.24.0 +ARG BUILDX_VERSION=0.25.0 FROM docker/buildx-bin:${BUILDX_VERSION} AS buildx FROM golang:${GO_VERSION}-alpine${ALPINE_VERSION} AS golang @@ -22,7 +26,9 @@ RUN --mount=type=cache,target=/root/.cache/go-build \ && gofumpt --version FROM golang AS gotestsum -ARG GOTESTSUM_VERSION=v1.12.0 +# GOTESTSUM_VERSION sets the version of gotestsum to install in the dev container. +# It must be a valid tag in the https://github.com/gotestyourself/gotestsum repository. +ARG GOTESTSUM_VERSION=v1.12.3 RUN --mount=type=cache,target=/root/.cache/go-build \ --mount=type=cache,target=/go/pkg/mod \ --mount=type=tmpfs,target=/go/src/ \ diff --git a/dockerfiles/Dockerfile.lint b/dockerfiles/Dockerfile.lint index 1f10951328bf..345943c2e5c0 100644 --- a/dockerfiles/Dockerfile.lint +++ b/dockerfiles/Dockerfile.lint @@ -1,6 +1,10 @@ # syntax=docker/dockerfile:1 ARG GO_VERSION=1.24.5 + +# ALPINE_VERSION sets the version of the alpine base image to use, including for the golang image. +# It must be a supported tag in the docker.io/library/alpine image repository +# that's also available as alpine image variant for the Golang version used. ARG ALPINE_VERSION=3.21 ARG GOLANGCI_LINT_VERSION=v2.1.5 diff --git a/dockerfiles/Dockerfile.vendor b/dockerfiles/Dockerfile.vendor index 4b9cea56b0c4..e70a5921781d 100644 --- a/dockerfiles/Dockerfile.vendor +++ b/dockerfiles/Dockerfile.vendor @@ -1,6 +1,10 @@ # syntax=docker/dockerfile:1 ARG GO_VERSION=1.24.5 + +# ALPINE_VERSION sets the version of the alpine base image to use, including for the golang image. +# It must be a supported tag in the docker.io/library/alpine image repository +# that's also available as alpine image variant for the Golang version used. ARG ALPINE_VERSION=3.21 ARG MODOUTDATED_VERSION=v0.8.0 diff --git a/internal/test/randomid.go b/internal/test/randomid.go new file mode 100644 index 000000000000..fd5ed2ca73ba --- /dev/null +++ b/internal/test/randomid.go @@ -0,0 +1,15 @@ +package test + +import ( + "crypto/rand" + "encoding/hex" +) + +// RandomID returns a unique, 64-character ID consisting of a-z, 0-9. +func RandomID() string { + b := make([]byte, 32) + if _, err := rand.Read(b); err != nil { + panic(err) // This shouldn't happen + } + return hex.EncodeToString(b) +} diff --git a/vendor/github.com/docker/docker/pkg/ioutils/fswriters_deprecated.go b/vendor/github.com/docker/docker/pkg/ioutils/fswriters_deprecated.go deleted file mode 100644 index f34e06911cc9..000000000000 --- a/vendor/github.com/docker/docker/pkg/ioutils/fswriters_deprecated.go +++ /dev/null @@ -1,44 +0,0 @@ -package ioutils - -import ( - "io" - "os" - - "github.com/moby/sys/atomicwriter" -) - -// NewAtomicFileWriter returns WriteCloser so that writing to it writes to a -// temporary file and closing it atomically changes the temporary file to -// destination path. Writing and closing concurrently is not allowed. -// NOTE: umask is not considered for the file's permissions. -// -// Deprecated: use [atomicwriter.New] instead. -func NewAtomicFileWriter(filename string, perm os.FileMode) (io.WriteCloser, error) { - return atomicwriter.New(filename, perm) -} - -// AtomicWriteFile atomically writes data to a file named by filename and with the specified permission bits. -// NOTE: umask is not considered for the file's permissions. -// -// Deprecated: use [atomicwriter.WriteFile] instead. -func AtomicWriteFile(filename string, data []byte, perm os.FileMode) error { - return atomicwriter.WriteFile(filename, data, perm) -} - -// AtomicWriteSet is used to atomically write a set -// of files and ensure they are visible at the same time. -// Must be committed to a new directory. -// -// Deprecated: use [atomicwriter.WriteSet] instead. -type AtomicWriteSet = atomicwriter.WriteSet - -// NewAtomicWriteSet creates a new atomic write set to -// atomically create a set of files. The given directory -// is used as the base directory for storing files before -// commit. If no temporary directory is given the system -// default is used. -// -// Deprecated: use [atomicwriter.NewWriteSet] instead. -func NewAtomicWriteSet(tmpDir string) (*atomicwriter.WriteSet, error) { - return atomicwriter.NewWriteSet(tmpDir) -} diff --git a/vendor/github.com/docker/docker/pkg/ioutils/readers.go b/vendor/github.com/docker/docker/pkg/ioutils/readers.go deleted file mode 100644 index 21c7a2f68875..000000000000 --- a/vendor/github.com/docker/docker/pkg/ioutils/readers.go +++ /dev/null @@ -1,118 +0,0 @@ -package ioutils - -import ( - "context" - "io" - "runtime/debug" - "sync/atomic" - - "github.com/containerd/log" -) - -// readCloserWrapper wraps an io.Reader, and implements an io.ReadCloser -// It calls the given callback function when closed. It should be constructed -// with NewReadCloserWrapper -type readCloserWrapper struct { - io.Reader - closer func() error - closed atomic.Bool -} - -// Close calls back the passed closer function -func (r *readCloserWrapper) Close() error { - if !r.closed.CompareAndSwap(false, true) { - subsequentCloseWarn("ReadCloserWrapper") - return nil - } - return r.closer() -} - -// NewReadCloserWrapper wraps an io.Reader, and implements an io.ReadCloser. -// It calls the given callback function when closed. -func NewReadCloserWrapper(r io.Reader, closer func() error) io.ReadCloser { - return &readCloserWrapper{ - Reader: r, - closer: closer, - } -} - -// cancelReadCloser wraps an io.ReadCloser with a context for cancelling read -// operations. -type cancelReadCloser struct { - cancel func() - pR *io.PipeReader // Stream to read from - pW *io.PipeWriter - closed atomic.Bool -} - -// NewCancelReadCloser creates a wrapper that closes the ReadCloser when the -// context is cancelled. The returned io.ReadCloser must be closed when it is -// no longer needed. -func NewCancelReadCloser(ctx context.Context, in io.ReadCloser) io.ReadCloser { - pR, pW := io.Pipe() - - // Create a context used to signal when the pipe is closed - doneCtx, cancel := context.WithCancel(context.Background()) - - p := &cancelReadCloser{ - cancel: cancel, - pR: pR, - pW: pW, - } - - go func() { - _, err := io.Copy(pW, in) - select { - case <-ctx.Done(): - // If the context was closed, p.closeWithError - // was already called. Calling it again would - // change the error that Read returns. - default: - p.closeWithError(err) - } - in.Close() - }() - go func() { - for { - select { - case <-ctx.Done(): - p.closeWithError(ctx.Err()) - case <-doneCtx.Done(): - return - } - } - }() - - return p -} - -// Read wraps the Read method of the pipe that provides data from the wrapped -// ReadCloser. -func (p *cancelReadCloser) Read(buf []byte) (int, error) { - return p.pR.Read(buf) -} - -// closeWithError closes the wrapper and its underlying reader. It will -// cause future calls to Read to return err. -func (p *cancelReadCloser) closeWithError(err error) { - _ = p.pW.CloseWithError(err) - p.cancel() -} - -// Close closes the wrapper its underlying reader. It will cause -// future calls to Read to return io.EOF. -func (p *cancelReadCloser) Close() error { - if !p.closed.CompareAndSwap(false, true) { - subsequentCloseWarn("cancelReadCloser") - return nil - } - p.closeWithError(io.EOF) - return nil -} - -func subsequentCloseWarn(name string) { - log.G(context.TODO()).Error("subsequent attempt to close " + name) - if log.GetLevel() >= log.DebugLevel { - log.G(context.TODO()).Errorf("stack trace: %s", string(debug.Stack())) - } -} diff --git a/vendor/github.com/docker/docker/pkg/ioutils/writeflusher.go b/vendor/github.com/docker/docker/pkg/ioutils/writeflusher.go deleted file mode 100644 index 8d60ef2f62e4..000000000000 --- a/vendor/github.com/docker/docker/pkg/ioutils/writeflusher.go +++ /dev/null @@ -1,96 +0,0 @@ -package ioutils - -import ( - "io" - "sync" -) - -// WriteFlusher wraps the Write and Flush operation ensuring that every write -// is a flush. In addition, the Close method can be called to intercept -// Read/Write calls if the targets lifecycle has already ended. -type WriteFlusher struct { - w io.Writer - flusher flusher - flushed chan struct{} - flushedOnce sync.Once - closed chan struct{} - closeLock sync.Mutex -} - -type flusher interface { - Flush() -} - -func (wf *WriteFlusher) Write(b []byte) (int, error) { - select { - case <-wf.closed: - return 0, io.EOF - default: - } - - n, err := wf.w.Write(b) - wf.Flush() // every write is a flush. - return n, err -} - -// Flush the stream immediately. -func (wf *WriteFlusher) Flush() { - select { - case <-wf.closed: - return - default: - } - - wf.flushedOnce.Do(func() { - close(wf.flushed) - }) - wf.flusher.Flush() -} - -// Flushed returns the state of flushed. -// If it's flushed, return true, or else it return false. -func (wf *WriteFlusher) Flushed() bool { - // BUG(stevvooe): Remove this method. Its use is inherently racy. Seems to - // be used to detect whether or a response code has been issued or not. - // Another hook should be used instead. - var flushed bool - select { - case <-wf.flushed: - flushed = true - default: - } - return flushed -} - -// Close closes the write flusher, disallowing any further writes to the -// target. After the flusher is closed, all calls to write or flush will -// result in an error. -func (wf *WriteFlusher) Close() error { - wf.closeLock.Lock() - defer wf.closeLock.Unlock() - - select { - case <-wf.closed: - return io.EOF - default: - close(wf.closed) - } - return nil -} - -// nopFlusher represents a type which flush operation is nop. -type nopFlusher struct{} - -// Flush is a nop operation. -func (f *nopFlusher) Flush() {} - -// NewWriteFlusher returns a new WriteFlusher. -func NewWriteFlusher(w io.Writer) *WriteFlusher { - var fl flusher - if f, ok := w.(flusher); ok { - fl = f - } else { - fl = &nopFlusher{} - } - return &WriteFlusher{w: w, flusher: fl, closed: make(chan struct{}), flushed: make(chan struct{})} -} diff --git a/vendor/github.com/docker/docker/pkg/ioutils/writers.go b/vendor/github.com/docker/docker/pkg/ioutils/writers.go deleted file mode 100644 index 4cf2d4de9530..000000000000 --- a/vendor/github.com/docker/docker/pkg/ioutils/writers.go +++ /dev/null @@ -1,28 +0,0 @@ -package ioutils - -import ( - "io" - "sync/atomic" -) - -type writeCloserWrapper struct { - io.Writer - closer func() error - closed atomic.Bool -} - -func (r *writeCloserWrapper) Close() error { - if !r.closed.CompareAndSwap(false, true) { - subsequentCloseWarn("WriteCloserWrapper") - return nil - } - return r.closer() -} - -// NewWriteCloserWrapper returns a new io.WriteCloser. -func NewWriteCloserWrapper(r io.Writer, closer func() error) io.WriteCloser { - return &writeCloserWrapper{ - Writer: r, - closer: closer, - } -} diff --git a/vendor/github.com/docker/docker/pkg/longpath/longpath.go b/vendor/github.com/docker/docker/pkg/longpath/longpath.go deleted file mode 100644 index e5f454c96247..000000000000 --- a/vendor/github.com/docker/docker/pkg/longpath/longpath.go +++ /dev/null @@ -1,42 +0,0 @@ -// Package longpath introduces some constants and helper functions for handling -// long paths in Windows. -// -// Long paths are expected to be prepended with "\\?\" and followed by either a -// drive letter, a UNC server\share, or a volume identifier. -package longpath - -import ( - "os" - "runtime" - "strings" -) - -// longPathPrefix is the longpath prefix for Windows file paths. -const longPathPrefix = `\\?\` - -// AddPrefix adds the Windows long path prefix to the path provided if -// it does not already have it. -func AddPrefix(path string) string { - if strings.HasPrefix(path, longPathPrefix) { - return path - } - if strings.HasPrefix(path, `\\`) { - // This is a UNC path, so we need to add 'UNC' to the path as well. - return longPathPrefix + `UNC` + path[1:] - } - return longPathPrefix + path -} - -// MkdirTemp is the equivalent of [os.MkdirTemp], except that on Windows -// the result is in Windows longpath format. On Unix systems it is -// equivalent to [os.MkdirTemp]. -func MkdirTemp(dir, prefix string) (string, error) { - tempDir, err := os.MkdirTemp(dir, prefix) - if err != nil { - return "", err - } - if runtime.GOOS != "windows" { - return tempDir, nil - } - return AddPrefix(tempDir), nil -} diff --git a/vendor/github.com/docker/docker/pkg/stringid/stringid.go b/vendor/github.com/docker/docker/pkg/stringid/stringid.go deleted file mode 100644 index 919cd5a500f6..000000000000 --- a/vendor/github.com/docker/docker/pkg/stringid/stringid.go +++ /dev/null @@ -1,63 +0,0 @@ -// Package stringid provides helper functions for dealing with string identifiers -package stringid - -import ( - "crypto/rand" - "encoding/hex" - "strings" -) - -const ( - shortLen = 12 - fullLen = 64 -) - -// TruncateID returns a shorthand version of a string identifier for convenience. -// A collision with other shorthands is very unlikely, but possible. -// In case of a collision a lookup with TruncIndex.Get() will fail, and the caller -// will need to use a longer prefix, or the full-length Id. -func TruncateID(id string) string { - if i := strings.IndexRune(id, ':'); i >= 0 { - id = id[i+1:] - } - if len(id) > shortLen { - id = id[:shortLen] - } - return id -} - -// GenerateRandomID returns a unique, 64-character ID consisting of a-z, 0-9. -// It guarantees that the ID, when truncated ([TruncateID]) does not consist -// of numbers only, so that the truncated ID can be used as hostname for -// containers. -func GenerateRandomID() string { - b := make([]byte, 32) - for { - if _, err := rand.Read(b); err != nil { - panic(err) // This shouldn't happen - } - id := hex.EncodeToString(b) - - // make sure that the truncated ID does not consist of only numeric - // characters, as it's used as default hostname for containers. - // - // See: - // - https://github.com/moby/moby/issues/3869 - // - https://bugzilla.redhat.com/show_bug.cgi?id=1059122 - if allNum(id[:shortLen]) { - // all numbers; try again - continue - } - return id - } -} - -// allNum checks whether id consists of only numbers (0-9). -func allNum(id string) bool { - for _, c := range []byte(id) { - if c > '9' || c < '0' { - return false - } - } - return true -} diff --git a/vendor/modules.txt b/vendor/modules.txt index d6abd3b510e2..037c93d3bec6 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -95,14 +95,11 @@ github.com/docker/docker/client github.com/docker/docker/internal/lazyregexp github.com/docker/docker/internal/multierror github.com/docker/docker/pkg/homedir -github.com/docker/docker/pkg/ioutils github.com/docker/docker/pkg/jsonmessage -github.com/docker/docker/pkg/longpath github.com/docker/docker/pkg/process github.com/docker/docker/pkg/progress github.com/docker/docker/pkg/stdcopy github.com/docker/docker/pkg/streamformatter -github.com/docker/docker/pkg/stringid github.com/docker/docker/registry # github.com/docker/docker-credential-helpers v0.9.3 ## explicit; go 1.21