Compare commits
22 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 980b856816 | |||
| 9c256146ac | |||
| bc01f8489d | |||
| ea2a0c3b8a | |||
| 3d985799d4 | |||
| f5f3b027e8 | |||
| 143f36133a | |||
| d7181e47e2 | |||
| 8b6436ecee | |||
| 7d574b816d | |||
| 0f2b709c7c | |||
| 7668b683d2 | |||
| 53d02ece89 | |||
| 3600ebca76 | |||
| 9b047a501f | |||
| e0f4bc699c | |||
| 1264a59779 | |||
| e6b8cc1c7d | |||
| 50fa436c21 | |||
| 0be687acc0 | |||
| c69d8bde4a | |||
| 8eac03d5fa |
17
Dockerfile
17
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
|
||||
|
||||
|
||||
@ -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 (
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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")
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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"
|
||||
)
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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 "<no image>"
|
||||
}
|
||||
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
|
||||
|
||||
@ -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,
|
||||
},
|
||||
{
|
||||
|
||||
@ -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 (
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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,
|
||||
},
|
||||
{
|
||||
|
||||
@ -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 (
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
22
cli/command/image/build/context_windows_test.go
Normal file
22
cli/command/image/build/context_windows_test.go
Normal file
@ -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)
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@ -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)
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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},
|
||||
|
||||
@ -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 (
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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},
|
||||
|
||||
@ -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 (
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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, ", ")
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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/ \
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -965,7 +965,7 @@ This starts an `ubuntu` container with access to the specified CDI device,
|
||||
available on the system running the daemon, in one of the configured CDI
|
||||
specification directories.
|
||||
- The CDI feature has been enabled in the daemon; see [Enable CDI
|
||||
devices](https://docs.docker.com/reference/cli/dockerd/#enable-cdi-devices).
|
||||
devices](https://docs.docker.com/reference/cli/dockerd/#configure-cdi-devices).
|
||||
|
||||
### <a name="attach"></a> Attach to STDIN/STDOUT/STDERR (-a, --attach)
|
||||
|
||||
|
||||
15
internal/test/randomid.go
Normal file
15
internal/test/randomid.go
Normal file
@ -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)
|
||||
}
|
||||
44
vendor/github.com/docker/docker/pkg/ioutils/fswriters_deprecated.go
generated
vendored
44
vendor/github.com/docker/docker/pkg/ioutils/fswriters_deprecated.go
generated
vendored
@ -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)
|
||||
}
|
||||
118
vendor/github.com/docker/docker/pkg/ioutils/readers.go
generated
vendored
118
vendor/github.com/docker/docker/pkg/ioutils/readers.go
generated
vendored
@ -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()))
|
||||
}
|
||||
}
|
||||
96
vendor/github.com/docker/docker/pkg/ioutils/writeflusher.go
generated
vendored
96
vendor/github.com/docker/docker/pkg/ioutils/writeflusher.go
generated
vendored
@ -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{})}
|
||||
}
|
||||
28
vendor/github.com/docker/docker/pkg/ioutils/writers.go
generated
vendored
28
vendor/github.com/docker/docker/pkg/ioutils/writers.go
generated
vendored
@ -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,
|
||||
}
|
||||
}
|
||||
42
vendor/github.com/docker/docker/pkg/longpath/longpath.go
generated
vendored
42
vendor/github.com/docker/docker/pkg/longpath/longpath.go
generated
vendored
@ -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
|
||||
}
|
||||
63
vendor/github.com/docker/docker/pkg/stringid/stringid.go
generated
vendored
63
vendor/github.com/docker/docker/pkg/stringid/stringid.go
generated
vendored
@ -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
|
||||
}
|
||||
3
vendor/modules.txt
vendored
3
vendor/modules.txt
vendored
@ -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
|
||||
|
||||
Reference in New Issue
Block a user