mirror of https://github.com/docker/cli.git
Compare commits
177 Commits
master
...
v28.4.0-rc
Author | SHA1 | Date |
---|---|---|
|
aced30d906 | |
|
b9e15efde0 | |
|
4108febfae | |
|
8cfe1f712e | |
|
7c34fd56d0 | |
|
0a2eaa4d05 | |
|
d26f1fd6d3 | |
|
4f6182a50c | |
|
4caa99468d | |
|
c2117f956f | |
|
19b86ef9e2 | |
|
88d6197d4c | |
|
bda15c766a | |
|
e636ed2ae5 | |
|
19b7dfa1cf | |
|
91de2a6dae | |
|
7c6f58affe | |
|
8c6a7fcbc9 | |
|
3e87f59859 | |
|
a621313658 | |
|
69dd67fc06 | |
|
b90c5a8fcb | |
|
99676e6c9c | |
|
524fd156f5 | |
|
07bcb9cca2 | |
|
48ea3fa14f | |
|
a27a86289e | |
|
e112eeea4d | |
|
3b68cde77a | |
|
c712d97172 | |
|
f385cb994f | |
|
44c1d5775d | |
|
1468bb1962 | |
|
44fb305591 | |
|
b74574cd53 | |
|
e60b20f08e | |
|
6f7865f89a | |
|
23518a47dd | |
|
eb120d2c28 | |
|
146f6492c7 | |
|
8def226e53 | |
|
82c5138c8e | |
|
bd3e420cc7 | |
|
bb2ed910bb | |
|
1415080cfa | |
|
0bbdda88e6 | |
|
4133271530 | |
|
0e17907266 | |
|
b0b7dcfe86 | |
|
531ea22e7d | |
|
e8cf8e65f7 | |
|
22c68c5366 | |
|
e424c50373 | |
|
399861e7d2 | |
|
694e92aa10 | |
|
09a36f2ef1 | |
|
73fbe6a020 | |
|
4480024a80 | |
|
b4f2c0ca3d | |
|
85a5e3da36 | |
|
34a75fb6f4 | |
|
b52ab17a14 | |
|
ec89d0ba67 | |
|
aa44c436d7 | |
|
39e1213615 | |
|
5184f2ed65 | |
|
cb42a72704 | |
|
5ef48d620d | |
|
9e08776681 | |
|
85512e35cd | |
|
ce242b0ee8 | |
|
ae3ee9b47b | |
|
733762af0a | |
|
46862878f7 | |
|
4a2b024c57 | |
|
82de0590b6 | |
|
3e36fa624a | |
|
01ea3a7da7 | |
|
0f2721ee4f | |
|
6fc62cf5b6 | |
|
c580c9741e | |
|
ba8b22e783 | |
|
def5bfc11c | |
|
9d258edf27 | |
|
f68a9a06fe | |
|
60caaa39e0 | |
|
c94245da66 | |
|
79c03dcaaf | |
|
acb019a344 | |
|
5a80c3e98d | |
|
e250694a35 | |
|
942eade165 | |
|
a746a99d97 | |
|
66c3dbdfed | |
|
cf44e3164b | |
|
ce3196c726 | |
|
80884714de | |
|
4e00c31c71 | |
|
7126bf7d22 | |
|
53ed958805 | |
|
be307b2925 | |
|
8f8d39c35e | |
|
c61c0cdb3c | |
|
830b5f1d2f | |
|
abfc1f4c76 | |
|
31fa40cb25 | |
|
4881921784 | |
|
4313a2bbb7 | |
|
20c34ef13b | |
|
34a90bdd81 | |
|
98b82d5156 | |
|
0e474e38f1 | |
|
6adde56036 | |
|
7ccd6d29c6 | |
|
14f297204f | |
|
d1617cb0c0 | |
|
7091e8bea4 | |
|
ff42ff9f06 | |
|
bcc479b4c3 | |
|
6a596c007c | |
|
79e0b1d7d9 | |
|
099820d8ca | |
|
ac88b74462 | |
|
7f699066bd | |
|
fe1d790d8e | |
|
5e29918a44 | |
|
09efe3f408 | |
|
79dd3a3c79 | |
|
a8fe4aaa7f | |
|
034dc932d7 | |
|
0718529a7e | |
|
c754ac3c5b | |
|
5d327e8032 | |
|
bce66f6126 | |
|
511821052a | |
|
9fc26d43ed | |
|
2b4e0a0f45 | |
|
5d6e64c4a6 | |
|
ed52ada4a3 | |
|
60d16e20ac | |
|
713ed839fe | |
|
6bfee62d6d | |
|
efdf008933 | |
|
28ffe2416d | |
|
b0bb4acc11 | |
|
2e5a36728b | |
|
a5a17eb2c7 | |
|
e1f79927f3 | |
|
cab6012db9 | |
|
75a4cbbf8e | |
|
9071d3868c | |
|
e7f4f04156 | |
|
7e01a3a8a9 | |
|
72f85ccd16 | |
|
ecd54bc6dd | |
|
8b9baffdf7 | |
|
93a51c39f4 | |
|
2dc9daae32 | |
|
a4f8f22a33 | |
|
7d3bde083c | |
|
32207833d0 | |
|
7399781944 | |
|
efee5879a1 | |
|
b82e19efe0 | |
|
eceff3dbc5 | |
|
3598fc3745 | |
|
2df466710b | |
|
29f2ce760a | |
|
363f4c0031 | |
|
6d4ffec3fb | |
|
5c1cee4630 | |
|
88274f4805 | |
|
5566c3a9b8 | |
|
5edc6748f4 | |
|
6007e83a75 | |
|
ce2a0a4ecb | |
|
3cd108fcd0 |
|
@ -35,7 +35,7 @@ jobs:
|
|||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
-
|
||||
name: Create matrix
|
||||
id: platforms
|
||||
|
@ -143,7 +143,7 @@ jobs:
|
|||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
-
|
||||
name: Create matrix
|
||||
id: platforms
|
||||
|
|
|
@ -46,7 +46,7 @@ jobs:
|
|||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 2
|
||||
# CodeQL 2.16.4's auto-build added support for multi-module repositories,
|
||||
|
@ -63,7 +63,7 @@ jobs:
|
|||
name: Update Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: "1.24.5"
|
||||
go-version: "1.24.6"
|
||||
-
|
||||
name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v3
|
||||
|
|
|
@ -44,7 +44,7 @@ jobs:
|
|||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
-
|
||||
name: Update daemon.json
|
||||
run: |
|
||||
|
|
|
@ -59,14 +59,14 @@ jobs:
|
|||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
path: ${{ env.GOPATH }}/src/github.com/docker/cli
|
||||
-
|
||||
name: Set up Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: "1.24.5"
|
||||
go-version: "1.24.6"
|
||||
-
|
||||
name: Test
|
||||
run: |
|
||||
|
|
|
@ -48,7 +48,7 @@ jobs:
|
|||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
-
|
||||
name: Generate
|
||||
shell: 'script --return --quiet --command "bash {0}"'
|
||||
|
@ -74,7 +74,7 @@ jobs:
|
|||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
-
|
||||
name: Run
|
||||
shell: 'script --return --quiet --command "bash {0}"'
|
||||
|
|
|
@ -5,7 +5,7 @@ run:
|
|||
# which causes it to fallback to go1.17 semantics.
|
||||
#
|
||||
# TODO(thaJeztah): update "usetesting" settings to enable go1.24 features once our minimum version is go1.24
|
||||
go: "1.24.5"
|
||||
go: "1.24.6"
|
||||
|
||||
timeout: 5m
|
||||
|
||||
|
@ -86,6 +86,8 @@ linters:
|
|||
desc: Use github.com/moby/sys/userns instead.
|
||||
- pkg: "github.com/containerd/containerd/platforms"
|
||||
desc: The containerd platforms package was migrated to a separate module. Use github.com/containerd/platforms instead.
|
||||
- pkg: "github.com/docker/docker/errdefs"
|
||||
desc: Use github.com/containerd/errdefs instead.
|
||||
- pkg: "github.com/docker/docker/pkg/system"
|
||||
desc: This package should not be used unless strictly necessary.
|
||||
- pkg: "github.com/docker/distribution/uuid"
|
||||
|
@ -124,10 +126,9 @@ linters:
|
|||
no-unaliased: true
|
||||
|
||||
alias:
|
||||
# Enforce alias to prevent it accidentally being used instead of our
|
||||
# own errdefs package (or vice-versa).
|
||||
- pkg: github.com/containerd/errdefs
|
||||
alias: cerrdefs
|
||||
# Should no longer be aliased, because we no longer allow moby/docker errdefs.
|
||||
- pkg: "github.com/docker/docker/errdefs"
|
||||
alias: ""
|
||||
- pkg: github.com/opencontainers/image-spec/specs-go/v1
|
||||
alias: ocispec
|
||||
# Enforce that gotest.tools/v3/assert/cmp is always aliased as "is"
|
||||
|
@ -221,6 +222,14 @@ linters:
|
|||
linters:
|
||||
- staticcheck
|
||||
|
||||
# Ignore deprecation linting for cli/command/stack/*.
|
||||
#
|
||||
# FIXME(thaJeztah): remove exception once these functions are un-exported or internal; see https://github.com/docker/cli/pull/6389
|
||||
- text: '^(SA1019): '
|
||||
path: "cli/command/stack"
|
||||
linters:
|
||||
- staticcheck
|
||||
|
||||
# Log a warning if an exclusion rule is unused.
|
||||
# Default: false
|
||||
warn-unused: true
|
||||
|
|
|
@ -8,7 +8,7 @@ ARG BASE_VARIANT=alpine
|
|||
ARG ALPINE_VERSION=3.21
|
||||
ARG BASE_DEBIAN_DISTRO=bookworm
|
||||
|
||||
ARG GO_VERSION=1.24.5
|
||||
ARG GO_VERSION=1.24.6
|
||||
ARG XX_VERSION=1.6.1
|
||||
ARG GOVERSIONINFO_VERSION=v1.4.1
|
||||
|
||||
|
@ -31,7 +31,7 @@ FROM --platform=$BUILDPLATFORM tonistiigi/xx:${XX_VERSION} AS xx
|
|||
FROM --platform=$BUILDPLATFORM golang:${GO_VERSION}-alpine${ALPINE_VERSION} AS build-base-alpine
|
||||
ENV GOTOOLCHAIN=local
|
||||
COPY --link --from=xx / /
|
||||
RUN apk add --no-cache bash clang lld llvm file git
|
||||
RUN apk add --no-cache bash clang lld llvm file git git-daemon
|
||||
WORKDIR /go/src/github.com/docker/cli
|
||||
|
||||
FROM build-base-alpine AS build-alpine
|
||||
|
|
|
@ -6,25 +6,35 @@ const (
|
|||
// CommandAnnotationPlugin is added to every stub command added by
|
||||
// AddPluginCommandStubs with the value "true" and so can be
|
||||
// used to distinguish plugin stubs from regular commands.
|
||||
//
|
||||
// Deprecated: use [metadata.CommandAnnotationPlugin]. This alias will be removed in the next release.
|
||||
CommandAnnotationPlugin = metadata.CommandAnnotationPlugin
|
||||
|
||||
// CommandAnnotationPluginVendor is added to every stub command
|
||||
// added by AddPluginCommandStubs and contains the vendor of
|
||||
// that plugin.
|
||||
//
|
||||
// Deprecated: use [metadata.CommandAnnotationPluginVendor]. This alias will be removed in the next release.
|
||||
CommandAnnotationPluginVendor = metadata.CommandAnnotationPluginVendor
|
||||
|
||||
// CommandAnnotationPluginVersion is added to every stub command
|
||||
// added by AddPluginCommandStubs and contains the version of
|
||||
// that plugin.
|
||||
//
|
||||
// Deprecated: use [metadata.CommandAnnotationPluginVersion]. This alias will be removed in the next release.
|
||||
CommandAnnotationPluginVersion = metadata.CommandAnnotationPluginVersion
|
||||
|
||||
// CommandAnnotationPluginInvalid is added to any stub command
|
||||
// added by AddPluginCommandStubs for an invalid command (that
|
||||
// is, one which failed it's candidate test) and contains the
|
||||
// reason for the failure.
|
||||
//
|
||||
// Deprecated: use [metadata.CommandAnnotationPluginInvalid]. This alias will be removed in the next release.
|
||||
CommandAnnotationPluginInvalid = metadata.CommandAnnotationPluginInvalid
|
||||
|
||||
// CommandAnnotationPluginCommandPath is added to overwrite the
|
||||
// command path for a plugin invocation.
|
||||
//
|
||||
// Deprecated: use [metadata.CommandAnnotationPluginCommandPath]. This alias will be removed in the next release.
|
||||
CommandAnnotationPluginCommandPath = metadata.CommandAnnotationPluginCommandPath
|
||||
)
|
||||
|
|
|
@ -6,12 +6,6 @@ import (
|
|||
"github.com/docker/cli/cli-plugins/metadata"
|
||||
)
|
||||
|
||||
// Candidate represents a possible plugin candidate, for mocking purposes
|
||||
type Candidate interface {
|
||||
Path() string
|
||||
Metadata() ([]byte, error)
|
||||
}
|
||||
|
||||
type candidate struct {
|
||||
path string
|
||||
}
|
||||
|
|
|
@ -23,11 +23,6 @@ func (e *pluginError) Error() string {
|
|||
return e.cause.Error()
|
||||
}
|
||||
|
||||
// Cause satisfies the errors.causer interface for pluginError.
|
||||
func (e *pluginError) Cause() error {
|
||||
return e.cause
|
||||
}
|
||||
|
||||
// Unwrap provides compatibility for Go 1.13 error chains.
|
||||
func (e *pluginError) Unwrap() error {
|
||||
return e.cause
|
||||
|
@ -41,14 +36,11 @@ func (e *pluginError) MarshalText() (text []byte, err error) {
|
|||
// wrapAsPluginError wraps an error in a pluginError with an
|
||||
// additional message.
|
||||
func wrapAsPluginError(err error, msg string) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
return &pluginError{cause: fmt.Errorf("%s: %w", msg, err)}
|
||||
}
|
||||
|
||||
// NewPluginError creates a new pluginError, analogous to
|
||||
// newPluginError creates a new pluginError, analogous to
|
||||
// errors.Errorf.
|
||||
func NewPluginError(msg string, args ...any) error {
|
||||
func newPluginError(msg string, args ...any) error {
|
||||
return &pluginError{cause: fmt.Errorf(msg, args...)}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ import (
|
|||
)
|
||||
|
||||
func TestPluginError(t *testing.T) {
|
||||
err := NewPluginError("new error")
|
||||
err := newPluginError("new error")
|
||||
assert.Check(t, is.Error(err, "new error"))
|
||||
|
||||
inner := errors.New("testing")
|
||||
|
@ -21,4 +21,7 @@ func TestPluginError(t *testing.T) {
|
|||
actual, err := json.Marshal(err)
|
||||
assert.Check(t, err)
|
||||
assert.Check(t, is.Equal(`"wrapping: testing"`, string(actual)))
|
||||
|
||||
err = wrapAsPluginError(nil, "wrapping")
|
||||
assert.Check(t, is.Error(err, "wrapping: %!w(<nil>)"))
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/containerd/errdefs"
|
||||
"github.com/docker/cli/cli-plugins/metadata"
|
||||
"github.com/docker/cli/cli/config"
|
||||
"github.com/docker/cli/cli/config/configfile"
|
||||
|
@ -23,12 +24,6 @@ const (
|
|||
// plugin. Assuming $PATH and $CWD remain unchanged this should allow
|
||||
// the plugin to re-execute the original CLI.
|
||||
ReexecEnvvar = metadata.ReexecEnvvar
|
||||
|
||||
// ResourceAttributesEnvvar is the name of the envvar that includes additional
|
||||
// resource attributes for OTEL.
|
||||
//
|
||||
// Deprecated: The "OTEL_RESOURCE_ATTRIBUTES" env-var is part of the OpenTelemetry specification; users should define their own const for this. This const will be removed in the next release.
|
||||
ResourceAttributesEnvvar = "OTEL_RESOURCE_ATTRIBUTES"
|
||||
)
|
||||
|
||||
// errPluginNotFound is the error returned when a plugin could not be found.
|
||||
|
@ -40,15 +35,11 @@ func (e errPluginNotFound) Error() string {
|
|||
return "Error: No such CLI plugin: " + string(e)
|
||||
}
|
||||
|
||||
type notFound interface{ NotFound() }
|
||||
|
||||
// IsNotFound is true if the given error is due to a plugin not being found.
|
||||
//
|
||||
// Deprecated: use [errdefs.IsNotFound].
|
||||
func IsNotFound(err error) bool {
|
||||
if e, ok := err.(*pluginError); ok {
|
||||
err = e.Cause()
|
||||
}
|
||||
_, ok := err.(notFound)
|
||||
return ok
|
||||
return errdefs.IsNotFound(err)
|
||||
}
|
||||
|
||||
// getPluginDirs returns the platform-specific locations to search for plugins
|
||||
|
@ -127,7 +118,7 @@ func getPlugin(name string, pluginDirs []string, rootcmd *cobra.Command) (*Plugi
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !IsNotFound(p.Err) {
|
||||
if !errdefs.IsNotFound(p.Err) {
|
||||
p.ShadowedPaths = paths[1:]
|
||||
}
|
||||
return &p, nil
|
||||
|
@ -164,7 +155,7 @@ func ListPlugins(dockerCli config.Provider, rootcmd *cobra.Command) ([]Plugin, e
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !IsNotFound(p.Err) {
|
||||
if !errdefs.IsNotFound(p.Err) {
|
||||
p.ShadowedPaths = paths[1:]
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
|
@ -185,9 +176,9 @@ func ListPlugins(dockerCli config.Provider, rootcmd *cobra.Command) ([]Plugin, e
|
|||
return plugins, nil
|
||||
}
|
||||
|
||||
// PluginRunCommand returns an "os/exec".Cmd which when .Run() will execute the named plugin.
|
||||
// PluginRunCommand returns an [os/exec.Cmd] which when [os/exec.Cmd.Run] will execute the named plugin.
|
||||
// The rootcmd argument is referenced to determine the set of builtin commands in order to detect conficts.
|
||||
// The error returned satisfies the IsNotFound() predicate if no plugin was found or if the first candidate plugin was invalid somehow.
|
||||
// The error returned satisfies the [errdefs.IsNotFound] predicate if no plugin was found or if the first candidate plugin was invalid somehow.
|
||||
func PluginRunCommand(dockerCli config.Provider, name string, rootcmd *cobra.Command) (*exec.Cmd, error) {
|
||||
// This uses the full original args, not the args which may
|
||||
// have been provided by cobra to our caller. This is because
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/containerd/errdefs"
|
||||
"github.com/docker/cli/cli/config"
|
||||
"github.com/docker/cli/cli/config/configfile"
|
||||
"github.com/docker/cli/internal/test"
|
||||
|
@ -131,7 +132,7 @@ echo '{"SchemaVersion":"0.1.0"}'`, fs.WithMode(0o777)),
|
|||
|
||||
_, err = GetPlugin("ccc", cli, &cobra.Command{})
|
||||
assert.Error(t, err, "Error: No such CLI plugin: ccc")
|
||||
assert.Assert(t, IsNotFound(err))
|
||||
assert.Assert(t, errdefs.IsNotFound(err))
|
||||
}
|
||||
|
||||
func TestListPluginsIsSorted(t *testing.T) {
|
||||
|
@ -166,8 +167,8 @@ func TestErrPluginNotFound(t *testing.T) {
|
|||
var err error = errPluginNotFound("test")
|
||||
err.(errPluginNotFound).NotFound()
|
||||
assert.Error(t, err, "Error: No such CLI plugin: test")
|
||||
assert.Assert(t, IsNotFound(err))
|
||||
assert.Assert(t, !IsNotFound(nil))
|
||||
assert.Assert(t, errdefs.IsNotFound(err))
|
||||
assert.Assert(t, !errdefs.IsNotFound(nil))
|
||||
}
|
||||
|
||||
func TestGetPluginDirs(t *testing.T) {
|
||||
|
|
|
@ -6,18 +6,26 @@ import (
|
|||
|
||||
const (
|
||||
// NamePrefix is the prefix required on all plugin binary names
|
||||
//
|
||||
// Deprecated: use [metadata.NamePrefix]. This alias will be removed in a future release.
|
||||
NamePrefix = metadata.NamePrefix
|
||||
|
||||
// MetadataSubcommandName is the name of the plugin subcommand
|
||||
// which must be supported by every plugin and returns the
|
||||
// plugin metadata.
|
||||
//
|
||||
// Deprecated: use [metadata.MetadataSubcommandName]. This alias will be removed in a future release.
|
||||
MetadataSubcommandName = metadata.MetadataSubcommandName
|
||||
|
||||
// HookSubcommandName is the name of the plugin subcommand
|
||||
// which must be implemented by plugins declaring support
|
||||
// for hooks in their metadata.
|
||||
//
|
||||
// Deprecated: use [metadata.HookSubcommandName]. This alias will be removed in a future release.
|
||||
HookSubcommandName = metadata.HookSubcommandName
|
||||
)
|
||||
|
||||
// Metadata provided by the plugin.
|
||||
//
|
||||
// Deprecated: use [metadata.Metadata]. This alias will be removed in a future release.
|
||||
type Metadata = metadata.Metadata
|
||||
|
|
|
@ -2,6 +2,7 @@ package manager
|
|||
|
||||
import (
|
||||
"context"
|
||||
"encoding"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
@ -31,12 +32,34 @@ type Plugin struct {
|
|||
ShadowedPaths []string `json:",omitempty"`
|
||||
}
|
||||
|
||||
// MarshalJSON implements [json.Marshaler] to handle marshaling the
|
||||
// [Plugin.Err] field (Go doesn't marshal errors by default).
|
||||
func (p *Plugin) MarshalJSON() ([]byte, error) {
|
||||
type Alias Plugin // avoid recursion
|
||||
|
||||
cp := *p // shallow copy to avoid mutating original
|
||||
|
||||
if cp.Err != nil {
|
||||
if _, ok := cp.Err.(encoding.TextMarshaler); !ok {
|
||||
cp.Err = &pluginError{cp.Err}
|
||||
}
|
||||
}
|
||||
|
||||
return json.Marshal((*Alias)(&cp))
|
||||
}
|
||||
|
||||
// pluginCandidate represents a possible plugin candidate, for mocking purposes.
|
||||
type pluginCandidate interface {
|
||||
Path() string
|
||||
Metadata() ([]byte, error)
|
||||
}
|
||||
|
||||
// newPlugin determines if the given candidate is valid and returns a
|
||||
// Plugin. If the candidate fails one of the tests then `Plugin.Err`
|
||||
// is set, and is always a `pluginError`, but the `Plugin` is still
|
||||
// returned with no error. An error is only returned due to a
|
||||
// non-recoverable error.
|
||||
func newPlugin(c Candidate, cmds []*cobra.Command) (Plugin, error) {
|
||||
func newPlugin(c pluginCandidate, cmds []*cobra.Command) (Plugin, error) {
|
||||
path := c.Path()
|
||||
if path == "" {
|
||||
return Plugin{}, errors.New("plugin candidate path cannot be empty")
|
||||
|
@ -63,7 +86,7 @@ func newPlugin(c Candidate, cmds []*cobra.Command) (Plugin, error) {
|
|||
|
||||
// Now apply the candidate tests, so these update p.Err.
|
||||
if !pluginNameRe.MatchString(p.Name) {
|
||||
p.Err = NewPluginError("plugin candidate %q did not match %q", p.Name, pluginNameRe.String())
|
||||
p.Err = newPluginError("plugin candidate %q did not match %q", p.Name, pluginNameRe.String())
|
||||
return p, nil
|
||||
}
|
||||
|
||||
|
@ -75,11 +98,11 @@ func newPlugin(c Candidate, cmds []*cobra.Command) (Plugin, error) {
|
|||
continue
|
||||
}
|
||||
if cmd.Name() == p.Name {
|
||||
p.Err = NewPluginError("plugin %q duplicates builtin command", p.Name)
|
||||
p.Err = newPluginError("plugin %q duplicates builtin command", p.Name)
|
||||
return p, nil
|
||||
}
|
||||
if cmd.HasAlias(p.Name) {
|
||||
p.Err = NewPluginError("plugin %q duplicates an alias of builtin command %q", p.Name, cmd.Name())
|
||||
p.Err = newPluginError("plugin %q duplicates an alias of builtin command %q", p.Name, cmd.Name())
|
||||
return p, nil
|
||||
}
|
||||
}
|
||||
|
@ -96,11 +119,11 @@ func newPlugin(c Candidate, cmds []*cobra.Command) (Plugin, error) {
|
|||
return p, nil
|
||||
}
|
||||
if p.Metadata.SchemaVersion != "0.1.0" {
|
||||
p.Err = NewPluginError("plugin SchemaVersion %q is not valid, must be 0.1.0", p.Metadata.SchemaVersion)
|
||||
p.Err = newPluginError("plugin SchemaVersion %q is not valid, must be 0.1.0", p.Metadata.SchemaVersion)
|
||||
return p, nil
|
||||
}
|
||||
if p.Metadata.Vendor == "" {
|
||||
p.Err = NewPluginError("plugin metadata does not define a vendor")
|
||||
p.Err = newPluginError("plugin metadata does not define a vendor")
|
||||
return p, nil
|
||||
}
|
||||
return p, nil
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
package manager
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"gotest.tools/v3/assert"
|
||||
is "gotest.tools/v3/assert/cmp"
|
||||
)
|
||||
|
||||
func TestPluginMarshal(t *testing.T) {
|
||||
const jsonWithError = `{"Name":"some-plugin","Err":"something went wrong"}`
|
||||
const jsonNoError = `{"Name":"some-plugin"}`
|
||||
|
||||
tests := []struct {
|
||||
doc string
|
||||
error error
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
doc: "no error",
|
||||
expected: jsonNoError,
|
||||
},
|
||||
{
|
||||
doc: "regular error",
|
||||
error: errors.New("something went wrong"),
|
||||
expected: jsonWithError,
|
||||
},
|
||||
{
|
||||
doc: "custom error",
|
||||
error: newPluginError("something went wrong"),
|
||||
expected: jsonWithError,
|
||||
},
|
||||
}
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.doc, func(t *testing.T) {
|
||||
actual, err := json.Marshal(&Plugin{Name: "some-plugin", Err: tc.error})
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.Equal(string(actual), tc.expected))
|
||||
})
|
||||
}
|
||||
}
|
|
@ -175,11 +175,24 @@ func newPluginCommand(dockerCli *command.DockerCli, plugin *cobra.Command, meta
|
|||
newMetadataSubcommand(plugin, meta),
|
||||
)
|
||||
|
||||
cli.DisableFlagsInUseLine(cmd)
|
||||
visitAll(cmd,
|
||||
// prevent adding "[flags]" to the end of the usage line.
|
||||
func(c *cobra.Command) { c.DisableFlagsInUseLine = true },
|
||||
)
|
||||
|
||||
return cli.NewTopLevelCommand(cmd, dockerCli, opts, cmd.Flags())
|
||||
}
|
||||
|
||||
// visitAll traverses all commands from the root.
|
||||
func visitAll(root *cobra.Command, fns ...func(*cobra.Command)) {
|
||||
for _, cmd := range root.Commands() {
|
||||
visitAll(cmd, fns...)
|
||||
}
|
||||
for _, fn := range fns {
|
||||
fn(root)
|
||||
}
|
||||
}
|
||||
|
||||
func newMetadataSubcommand(plugin *cobra.Command, meta metadata.Metadata) *cobra.Command {
|
||||
if meta.ShortDescription == "" {
|
||||
meta.ShortDescription = plugin.Short
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
package plugin
|
||||
|
||||
import (
|
||||
"slices"
|
||||
"testing"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func TestVisitAll(t *testing.T) {
|
||||
root := &cobra.Command{Use: "root"}
|
||||
sub1 := &cobra.Command{Use: "sub1"}
|
||||
sub1sub1 := &cobra.Command{Use: "sub1sub1"}
|
||||
sub1sub2 := &cobra.Command{Use: "sub1sub2"}
|
||||
sub2 := &cobra.Command{Use: "sub2"}
|
||||
|
||||
root.AddCommand(sub1, sub2)
|
||||
sub1.AddCommand(sub1sub1, sub1sub2)
|
||||
|
||||
var visited []string
|
||||
visitAll(root, func(ccmd *cobra.Command) {
|
||||
visited = append(visited, ccmd.Name())
|
||||
})
|
||||
expected := []string{"sub1sub1", "sub1sub2", "sub1", "sub2", "root"}
|
||||
if !slices.Equal(expected, visited) {
|
||||
t.Errorf("expected %#v, got %#v", expected, visited)
|
||||
}
|
||||
}
|
24
cli/cobra.go
24
cli/cobra.go
|
@ -168,34 +168,30 @@ func (tcmd *TopLevelCommand) Initialize(ops ...command.CLIOption) error {
|
|||
}
|
||||
|
||||
// VisitAll will traverse all commands from the root.
|
||||
// This is different from the VisitAll of cobra.Command where only parents
|
||||
// are checked.
|
||||
//
|
||||
// Deprecated: this utility was only used internally and will be removed in the next release.
|
||||
func VisitAll(root *cobra.Command, fn func(*cobra.Command)) {
|
||||
visitAll(root, fn)
|
||||
}
|
||||
|
||||
func visitAll(root *cobra.Command, fn func(*cobra.Command)) {
|
||||
for _, cmd := range root.Commands() {
|
||||
VisitAll(cmd, fn)
|
||||
visitAll(cmd, fn)
|
||||
}
|
||||
fn(root)
|
||||
}
|
||||
|
||||
// DisableFlagsInUseLine sets the DisableFlagsInUseLine flag on all
|
||||
// commands within the tree rooted at cmd.
|
||||
//
|
||||
// Deprecated: this utility was only used internally and will be removed in the next release.
|
||||
func DisableFlagsInUseLine(cmd *cobra.Command) {
|
||||
VisitAll(cmd, func(ccmd *cobra.Command) {
|
||||
visitAll(cmd, func(ccmd *cobra.Command) {
|
||||
// do not add a `[flags]` to the end of the usage line.
|
||||
ccmd.DisableFlagsInUseLine = true
|
||||
})
|
||||
}
|
||||
|
||||
// HasCompletionArg returns true if a cobra completion arg request is found.
|
||||
func HasCompletionArg(args []string) bool {
|
||||
for _, arg := range args {
|
||||
if arg == cobra.ShellCompRequestCmd || arg == cobra.ShellCompNoDescRequestCmd {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
var helpCommand = &cobra.Command{
|
||||
Use: "help [command]",
|
||||
Short: "Help about the command",
|
||||
|
|
|
@ -10,28 +10,6 @@ import (
|
|||
is "gotest.tools/v3/assert/cmp"
|
||||
)
|
||||
|
||||
func TestVisitAll(t *testing.T) {
|
||||
root := &cobra.Command{Use: "root"}
|
||||
sub1 := &cobra.Command{Use: "sub1"}
|
||||
sub1sub1 := &cobra.Command{Use: "sub1sub1"}
|
||||
sub1sub2 := &cobra.Command{Use: "sub1sub2"}
|
||||
sub2 := &cobra.Command{Use: "sub2"}
|
||||
|
||||
root.AddCommand(sub1, sub2)
|
||||
sub1.AddCommand(sub1sub1, sub1sub2)
|
||||
|
||||
// Take the opportunity to test DisableFlagsInUseLine too
|
||||
DisableFlagsInUseLine(root)
|
||||
|
||||
var visited []string
|
||||
VisitAll(root, func(ccmd *cobra.Command) {
|
||||
visited = append(visited, ccmd.Name())
|
||||
assert.Assert(t, ccmd.DisableFlagsInUseLine, "DisableFlagsInUseLine not set on %q", ccmd.Name())
|
||||
})
|
||||
expected := []string{"sub1sub1", "sub1sub2", "sub1", "sub2", "root"}
|
||||
assert.DeepEqual(t, expected, visited)
|
||||
}
|
||||
|
||||
func TestVendorAndVersion(t *testing.T) {
|
||||
// Non plugin.
|
||||
assert.Equal(t, vendorAndVersion(&cobra.Command{Use: "test"}), "")
|
||||
|
|
|
@ -9,17 +9,25 @@ import (
|
|||
)
|
||||
|
||||
// NewBuilderCommand returns a cobra command for `builder` subcommands
|
||||
func NewBuilderCommand(dockerCli command.Cli) *cobra.Command {
|
||||
//
|
||||
// Deprecated: Do not import commands directly. They will be removed in a future release.
|
||||
func NewBuilderCommand(dockerCLI command.Cli) *cobra.Command {
|
||||
return newBuilderCommand(dockerCLI)
|
||||
}
|
||||
|
||||
func newBuilderCommand(dockerCLI command.Cli) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "builder",
|
||||
Short: "Manage builds",
|
||||
Args: cli.NoArgs,
|
||||
RunE: command.ShowHelp(dockerCli.Err()),
|
||||
RunE: command.ShowHelp(dockerCLI.Err()),
|
||||
Annotations: map[string]string{"version": "1.31"},
|
||||
}
|
||||
cmd.AddCommand(
|
||||
NewPruneCommand(dockerCli),
|
||||
image.NewBuildCommand(dockerCli),
|
||||
newPruneCommand(dockerCLI),
|
||||
// we should have a mechanism for registering sub-commands in the cli/internal/commands.Register function.
|
||||
//nolint:staticcheck // TODO: Remove when migration to cli/internal/commands.Register is complete. (see #6283)
|
||||
image.NewBuildCommand(dockerCLI),
|
||||
)
|
||||
return cmd
|
||||
}
|
||||
|
@ -28,7 +36,13 @@ func NewBuilderCommand(dockerCli command.Cli) *cobra.Command {
|
|||
// This command is a placeholder / stub that is dynamically replaced by an
|
||||
// alias for "docker buildx bake" if BuildKit is enabled (and the buildx plugin
|
||||
// installed).
|
||||
//
|
||||
// Deprecated: Do not import commands directly. They will be removed in a future release.
|
||||
func NewBakeStubCommand(dockerCLI command.Streams) *cobra.Command {
|
||||
return newBakeStubCommand(dockerCLI)
|
||||
}
|
||||
|
||||
func newBakeStubCommand(dockerCLI command.Streams) *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "bake [OPTIONS] [TARGET...]",
|
||||
Short: "Build from a file",
|
||||
|
|
|
@ -24,7 +24,14 @@ type pruneOptions struct {
|
|||
}
|
||||
|
||||
// NewPruneCommand returns a new cobra prune command for images
|
||||
//
|
||||
// Deprecated: Do not import commands directly. They will be removed in a future release.
|
||||
func NewPruneCommand(dockerCli command.Cli) *cobra.Command {
|
||||
return newPruneCommand(dockerCli)
|
||||
}
|
||||
|
||||
// newPruneCommand returns a new cobra prune command for images
|
||||
func newPruneCommand(dockerCLI command.Cli) *cobra.Command {
|
||||
options := pruneOptions{filter: opts.NewFilterOpt()}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
|
@ -32,14 +39,14 @@ func NewPruneCommand(dockerCli command.Cli) *cobra.Command {
|
|||
Short: "Remove build cache",
|
||||
Args: cli.NoArgs,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
spaceReclaimed, output, err := runPrune(cmd.Context(), dockerCli, options)
|
||||
spaceReclaimed, output, err := runPrune(cmd.Context(), dockerCLI, options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if output != "" {
|
||||
fmt.Fprintln(dockerCli.Out(), output)
|
||||
_, _ = fmt.Fprintln(dockerCLI.Out(), output)
|
||||
}
|
||||
fmt.Fprintln(dockerCli.Out(), "Total reclaimed space:", units.HumanSize(float64(spaceReclaimed)))
|
||||
_, _ = fmt.Fprintln(dockerCLI.Out(), "Total reclaimed space:", units.HumanSize(float64(spaceReclaimed)))
|
||||
return nil
|
||||
},
|
||||
Annotations: map[string]string{"version": "1.39"},
|
||||
|
|
|
@ -19,7 +19,7 @@ func TestBuilderPromptTermination(t *testing.T) {
|
|||
return nil, errors.New("fakeClient builderPruneFunc should not be called")
|
||||
},
|
||||
})
|
||||
cmd := NewPruneCommand(cli)
|
||||
cmd := newPruneCommand(cli)
|
||||
cmd.SetOut(io.Discard)
|
||||
cmd.SetErr(io.Discard)
|
||||
test.TerminatePrompt(ctx, t, cmd, cli)
|
||||
|
|
|
@ -7,12 +7,18 @@ import (
|
|||
)
|
||||
|
||||
// NewCheckpointCommand returns the `checkpoint` subcommand (only in experimental)
|
||||
func NewCheckpointCommand(dockerCli command.Cli) *cobra.Command {
|
||||
//
|
||||
// Deprecated: Do not import commands directly. They will be removed in a future release.
|
||||
func NewCheckpointCommand(dockerCLI command.Cli) *cobra.Command {
|
||||
return newCheckpointCommand(dockerCLI)
|
||||
}
|
||||
|
||||
func newCheckpointCommand(dockerCLI command.Cli) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "checkpoint",
|
||||
Short: "Manage checkpoints",
|
||||
Args: cli.NoArgs,
|
||||
RunE: command.ShowHelp(dockerCli.Err()),
|
||||
RunE: command.ShowHelp(dockerCLI.Err()),
|
||||
Annotations: map[string]string{
|
||||
"experimental": "",
|
||||
"ostype": "linux",
|
||||
|
@ -20,9 +26,9 @@ func NewCheckpointCommand(dockerCli command.Cli) *cobra.Command {
|
|||
},
|
||||
}
|
||||
cmd.AddCommand(
|
||||
newCreateCommand(dockerCli),
|
||||
newListCommand(dockerCli),
|
||||
newRemoveCommand(dockerCli),
|
||||
newCreateCommand(dockerCLI),
|
||||
newListCommand(dockerCLI),
|
||||
newRemoveCommand(dockerCLI),
|
||||
)
|
||||
return cmd
|
||||
}
|
||||
|
|
|
@ -11,7 +11,14 @@ const (
|
|||
)
|
||||
|
||||
// NewFormat returns a format for use with a checkpoint Context
|
||||
//
|
||||
// Deprecated: this function was only used internally and will be removed in the next release.
|
||||
func NewFormat(source string) formatter.Format {
|
||||
return newFormat(source)
|
||||
}
|
||||
|
||||
// newFormat returns a format for use with a checkpointContext.
|
||||
func newFormat(source string) formatter.Format {
|
||||
if source == formatter.TableFormatKey {
|
||||
return defaultCheckpointFormat
|
||||
}
|
||||
|
@ -19,7 +26,14 @@ func NewFormat(source string) formatter.Format {
|
|||
}
|
||||
|
||||
// FormatWrite writes formatted checkpoints using the Context
|
||||
func FormatWrite(ctx formatter.Context, checkpoints []checkpoint.Summary) error {
|
||||
//
|
||||
// Deprecated: this function was only used internally and will be removed in the next release.
|
||||
func FormatWrite(fmtCtx formatter.Context, checkpoints []checkpoint.Summary) error {
|
||||
return formatWrite(fmtCtx, checkpoints)
|
||||
}
|
||||
|
||||
// formatWrite writes formatted checkpoints using the Context
|
||||
func formatWrite(fmtCtx formatter.Context, checkpoints []checkpoint.Summary) error {
|
||||
render := func(format func(subContext formatter.SubContext) error) error {
|
||||
for _, cp := range checkpoints {
|
||||
if err := format(&checkpointContext{c: cp}); err != nil {
|
||||
|
@ -28,7 +42,7 @@ func FormatWrite(ctx formatter.Context, checkpoints []checkpoint.Summary) error
|
|||
}
|
||||
return nil
|
||||
}
|
||||
return ctx.Write(newCheckpointContext(), render)
|
||||
return fmtCtx.Write(newCheckpointContext(), render)
|
||||
}
|
||||
|
||||
type checkpointContext struct {
|
||||
|
|
|
@ -15,7 +15,7 @@ func TestCheckpointContextFormatWrite(t *testing.T) {
|
|||
expected string
|
||||
}{
|
||||
{
|
||||
formatter.Context{Format: NewFormat(defaultCheckpointFormat)},
|
||||
formatter.Context{Format: newFormat(defaultCheckpointFormat)},
|
||||
`CHECKPOINT NAME
|
||||
checkpoint-1
|
||||
checkpoint-2
|
||||
|
@ -23,14 +23,14 @@ checkpoint-3
|
|||
`,
|
||||
},
|
||||
{
|
||||
formatter.Context{Format: NewFormat("{{.Name}}")},
|
||||
formatter.Context{Format: newFormat("{{.Name}}")},
|
||||
`checkpoint-1
|
||||
checkpoint-2
|
||||
checkpoint-3
|
||||
`,
|
||||
},
|
||||
{
|
||||
formatter.Context{Format: NewFormat("{{.Name}}:")},
|
||||
formatter.Context{Format: newFormat("{{.Name}}:")},
|
||||
`checkpoint-1:
|
||||
checkpoint-2:
|
||||
checkpoint-3:
|
||||
|
@ -41,7 +41,7 @@ checkpoint-3:
|
|||
for _, testcase := range cases {
|
||||
out := bytes.NewBufferString("")
|
||||
testcase.context.Output = out
|
||||
err := FormatWrite(testcase.context, []checkpoint.Summary{
|
||||
err := formatWrite(testcase.context, []checkpoint.Summary{
|
||||
{Name: "checkpoint-1"},
|
||||
{Name: "checkpoint-2"},
|
||||
{Name: "checkpoint-3"},
|
||||
|
|
|
@ -45,7 +45,7 @@ func runList(ctx context.Context, dockerCli command.Cli, container string, opts
|
|||
|
||||
cpCtx := formatter.Context{
|
||||
Output: dockerCli.Out(),
|
||||
Format: NewFormat(formatter.TableFormatKey),
|
||||
Format: newFormat(formatter.TableFormatKey),
|
||||
}
|
||||
return FormatWrite(cpCtx, checkpoints)
|
||||
return formatWrite(cpCtx, checkpoints)
|
||||
}
|
||||
|
|
|
@ -282,6 +282,17 @@ func (cli *DockerCli) Initialize(opts *cliflags.ClientOptions, ops ...CLIOption)
|
|||
}
|
||||
filterResourceAttributesEnvvar()
|
||||
|
||||
// early return if GODEBUG is already set or the docker context is
|
||||
// the default context, i.e. is a virtual context where we won't override
|
||||
// any GODEBUG values.
|
||||
if v := os.Getenv("GODEBUG"); cli.currentContext == DefaultContextName || v != "" {
|
||||
return nil
|
||||
}
|
||||
meta, err := cli.contextStore.GetMetadata(cli.currentContext)
|
||||
if err == nil {
|
||||
setGoDebug(meta)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -475,6 +486,57 @@ func (cli *DockerCli) getDockerEndPoint() (ep docker.Endpoint, err error) {
|
|||
return resolveDockerEndpoint(cli.contextStore, cn)
|
||||
}
|
||||
|
||||
// setGoDebug is an escape hatch that sets the GODEBUG environment
|
||||
// variable value using docker context metadata.
|
||||
//
|
||||
// {
|
||||
// "Name": "my-context",
|
||||
// "Metadata": { "GODEBUG": "x509negativeserial=1" }
|
||||
// }
|
||||
//
|
||||
// WARNING: Setting x509negativeserial=1 allows Go's x509 library to accept
|
||||
// X.509 certificates with negative serial numbers.
|
||||
// This behavior is deprecated and non-compliant with current security
|
||||
// standards (RFC 5280). Accepting negative serial numbers can introduce
|
||||
// serious security vulnerabilities, including the risk of certificate
|
||||
// collision or bypass attacks.
|
||||
// This option should only be used for legacy compatibility and never in
|
||||
// production environments.
|
||||
// Use at your own risk.
|
||||
func setGoDebug(meta store.Metadata) {
|
||||
fieldName := "GODEBUG"
|
||||
godebugEnv := os.Getenv(fieldName)
|
||||
// early return if GODEBUG is already set. We don't want to override what
|
||||
// the user already sets.
|
||||
if godebugEnv != "" {
|
||||
return
|
||||
}
|
||||
|
||||
var cfg any
|
||||
var ok bool
|
||||
switch m := meta.Metadata.(type) {
|
||||
case DockerContext:
|
||||
cfg, ok = m.AdditionalFields[fieldName]
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
case map[string]any:
|
||||
cfg, ok = m[fieldName]
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
default:
|
||||
return
|
||||
}
|
||||
|
||||
v, ok := cfg.(string)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
// set the GODEBUG environment variable with whatever was in the context
|
||||
_ = os.Setenv(fieldName, v)
|
||||
}
|
||||
|
||||
func (cli *DockerCli) initialize() error {
|
||||
cli.init.Do(func() {
|
||||
cli.dockerEndpoint, cli.initErr = cli.getDockerEndPoint()
|
||||
|
|
|
@ -18,6 +18,7 @@ import (
|
|||
|
||||
"github.com/docker/cli/cli/config"
|
||||
"github.com/docker/cli/cli/config/configfile"
|
||||
"github.com/docker/cli/cli/context/store"
|
||||
"github.com/docker/cli/cli/flags"
|
||||
"github.com/docker/docker/api"
|
||||
"github.com/docker/docker/api/types"
|
||||
|
@ -188,16 +189,16 @@ func TestInitializeFromClient(t *testing.T) {
|
|||
|
||||
for _, tc := range testcases {
|
||||
t.Run(tc.doc, func(t *testing.T) {
|
||||
apiclient := &fakeClient{
|
||||
apiClient := &fakeClient{
|
||||
pingFunc: tc.pingFunc,
|
||||
version: defaultVersion,
|
||||
}
|
||||
|
||||
cli := &DockerCli{client: apiclient}
|
||||
cli := &DockerCli{client: apiClient}
|
||||
err := cli.Initialize(flags.NewClientOptions())
|
||||
assert.NilError(t, err)
|
||||
assert.DeepEqual(t, cli.ServerInfo(), tc.expectedServer)
|
||||
assert.Equal(t, apiclient.negotiated, tc.negotiated)
|
||||
assert.Equal(t, apiClient.negotiated, tc.negotiated)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -353,3 +354,23 @@ func TestHooksEnabled(t *testing.T) {
|
|||
assert.Check(t, !cli.HooksEnabled())
|
||||
})
|
||||
}
|
||||
|
||||
func TestSetGoDebug(t *testing.T) {
|
||||
t.Run("GODEBUG already set", func(t *testing.T) {
|
||||
t.Setenv("GODEBUG", "val1,val2")
|
||||
meta := store.Metadata{}
|
||||
setGoDebug(meta)
|
||||
assert.Equal(t, "val1,val2", os.Getenv("GODEBUG"))
|
||||
})
|
||||
t.Run("GODEBUG in context metadata can set env", func(t *testing.T) {
|
||||
meta := store.Metadata{
|
||||
Metadata: DockerContext{
|
||||
AdditionalFields: map[string]any{
|
||||
"GODEBUG": "val1,val2=1",
|
||||
},
|
||||
},
|
||||
}
|
||||
setGoDebug(meta)
|
||||
assert.Equal(t, "val1,val2=1", os.Getenv("GODEBUG"))
|
||||
})
|
||||
}
|
||||
|
|
|
@ -29,69 +29,127 @@ import (
|
|||
func AddCommands(cmd *cobra.Command, dockerCli command.Cli) {
|
||||
cmd.AddCommand(
|
||||
// commonly used shorthands
|
||||
//nolint:staticcheck // TODO: Remove when migration to cli/internal/commands.Register is complete. (see #6283)
|
||||
container.NewRunCommand(dockerCli),
|
||||
//nolint:staticcheck // TODO: Remove when migration to cli/internal/commands.Register is complete. (see #6283)
|
||||
container.NewExecCommand(dockerCli),
|
||||
//nolint:staticcheck // TODO: Remove when migration to cli/internal/commands.Register is complete. (see #6283)
|
||||
container.NewPsCommand(dockerCli),
|
||||
//nolint:staticcheck // TODO: Remove when migration to cli/internal/commands.Register is complete. (see #6283)
|
||||
image.NewBuildCommand(dockerCli),
|
||||
//nolint:staticcheck // TODO: Remove when migration to cli/internal/commands.Register is complete. (see #6283)
|
||||
image.NewPullCommand(dockerCli),
|
||||
//nolint:staticcheck // TODO: Remove when migration to cli/internal/commands.Register is complete. (see #6283)
|
||||
image.NewPushCommand(dockerCli),
|
||||
//nolint:staticcheck // TODO: Remove when migration to cli/internal/commands.Register is complete. (see #6283)
|
||||
image.NewImagesCommand(dockerCli),
|
||||
//nolint:staticcheck // TODO: Remove when migration to cli/internal/commands.Register is complete. (see #6283)
|
||||
registry.NewLoginCommand(dockerCli),
|
||||
//nolint:staticcheck // TODO: Remove when migration to cli/internal/commands.Register is complete. (see #6283)
|
||||
registry.NewLogoutCommand(dockerCli),
|
||||
//nolint:staticcheck // TODO: Remove when migration to cli/internal/commands.Register is complete. (see #6283)
|
||||
registry.NewSearchCommand(dockerCli),
|
||||
//nolint:staticcheck // TODO: Remove when migration to cli/internal/commands.Register is complete. (see #6283)
|
||||
system.NewVersionCommand(dockerCli),
|
||||
//nolint:staticcheck // TODO: Remove when migration to cli/internal/commands.Register is complete. (see #6283)
|
||||
system.NewInfoCommand(dockerCli),
|
||||
|
||||
// management commands
|
||||
//nolint:staticcheck // TODO: Remove when migration to cli/internal/commands.Register is complete. (see #6283)
|
||||
builder.NewBakeStubCommand(dockerCli),
|
||||
//nolint:staticcheck // TODO: Remove when migration to cli/internal/commands.Register is complete. (see #6283)
|
||||
builder.NewBuilderCommand(dockerCli),
|
||||
//nolint:staticcheck // TODO: Remove when migration to cli/internal/commands.Register is complete. (see #6283)
|
||||
checkpoint.NewCheckpointCommand(dockerCli),
|
||||
//nolint:staticcheck // TODO: Remove when migration to cli/internal/commands.Register is complete. (see #6283)
|
||||
container.NewContainerCommand(dockerCli),
|
||||
//nolint:staticcheck // TODO: Remove when migration to cli/internal/commands.Register is complete. (see #6283)
|
||||
context.NewContextCommand(dockerCli),
|
||||
//nolint:staticcheck // TODO: Remove when migration to cli/internal/commands.Register is complete. (see #6283)
|
||||
image.NewImageCommand(dockerCli),
|
||||
//nolint:staticcheck // TODO: Remove when migration to cli/internal/commands.Register is complete. (see #6283)
|
||||
manifest.NewManifestCommand(dockerCli),
|
||||
//nolint:staticcheck // TODO: Remove when migration to cli/internal/commands.Register is complete. (see #6283)
|
||||
network.NewNetworkCommand(dockerCli),
|
||||
//nolint:staticcheck // TODO: Remove when migration to cli/internal/commands.Register is complete. (see #6283)
|
||||
plugin.NewPluginCommand(dockerCli),
|
||||
//nolint:staticcheck // TODO: Remove when migration to cli/internal/commands.Register is complete. (see #6283)
|
||||
system.NewSystemCommand(dockerCli),
|
||||
//nolint:staticcheck // TODO: Remove when migration to cli/internal/commands.Register is complete. (see #6283)
|
||||
trust.NewTrustCommand(dockerCli),
|
||||
//nolint:staticcheck // TODO: Remove when migration to cli/internal/commands.Register is complete. (see #6283)
|
||||
volume.NewVolumeCommand(dockerCli),
|
||||
|
||||
// orchestration (swarm) commands
|
||||
//nolint:staticcheck // TODO: Remove when migration to cli/internal/commands.Register is complete. (see #6283)
|
||||
config.NewConfigCommand(dockerCli),
|
||||
//nolint:staticcheck // TODO: Remove when migration to cli/internal/commands.Register is complete. (see #6283)
|
||||
node.NewNodeCommand(dockerCli),
|
||||
//nolint:staticcheck // TODO: Remove when migration to cli/internal/commands.Register is complete. (see #6283)
|
||||
secret.NewSecretCommand(dockerCli),
|
||||
//nolint:staticcheck // TODO: Remove when migration to cli/internal/commands.Register is complete. (see #6283)
|
||||
service.NewServiceCommand(dockerCli),
|
||||
//nolint:staticcheck // TODO: Remove when migration to cli/internal/commands.Register is complete. (see #6283)
|
||||
stack.NewStackCommand(dockerCli),
|
||||
//nolint:staticcheck // TODO: Remove when migration to cli/internal/commands.Register is complete. (see #6283)
|
||||
swarm.NewSwarmCommand(dockerCli),
|
||||
|
||||
// legacy commands may be hidden
|
||||
//nolint:staticcheck // TODO: Remove when migration to cli/internal/commands.Register is complete. (see #6283)
|
||||
hide(container.NewAttachCommand(dockerCli)),
|
||||
//nolint:staticcheck // TODO: Remove when migration to cli/internal/commands.Register is complete. (see #6283)
|
||||
hide(container.NewCommitCommand(dockerCli)),
|
||||
//nolint:staticcheck // TODO: Remove when migration to cli/internal/commands.Register is complete. (see #6283)
|
||||
hide(container.NewCopyCommand(dockerCli)),
|
||||
//nolint:staticcheck // TODO: Remove when migration to cli/internal/commands.Register is complete. (see #6283)
|
||||
hide(container.NewCreateCommand(dockerCli)),
|
||||
//nolint:staticcheck // TODO: Remove when migration to cli/internal/commands.Register is complete. (see #6283)
|
||||
hide(container.NewDiffCommand(dockerCli)),
|
||||
//nolint:staticcheck // TODO: Remove when migration to cli/internal/commands.Register is complete. (see #6283)
|
||||
hide(container.NewExportCommand(dockerCli)),
|
||||
//nolint:staticcheck // TODO: Remove when migration to cli/internal/commands.Register is complete. (see #6283)
|
||||
hide(container.NewKillCommand(dockerCli)),
|
||||
//nolint:staticcheck // TODO: Remove when migration to cli/internal/commands.Register is complete. (see #6283)
|
||||
hide(container.NewLogsCommand(dockerCli)),
|
||||
//nolint:staticcheck // TODO: Remove when migration to cli/internal/commands.Register is complete. (see #6283)
|
||||
hide(container.NewPauseCommand(dockerCli)),
|
||||
//nolint:staticcheck // TODO: Remove when migration to cli/internal/commands.Register is complete. (see #6283)
|
||||
hide(container.NewPortCommand(dockerCli)),
|
||||
//nolint:staticcheck // TODO: Remove when migration to cli/internal/commands.Register is complete. (see #6283)
|
||||
hide(container.NewRenameCommand(dockerCli)),
|
||||
//nolint:staticcheck // TODO: Remove when migration to cli/internal/commands.Register is complete. (see #6283)
|
||||
hide(container.NewRestartCommand(dockerCli)),
|
||||
//nolint:staticcheck // TODO: Remove when migration to cli/internal/commands.Register is complete. (see #6283)
|
||||
hide(container.NewRmCommand(dockerCli)),
|
||||
//nolint:staticcheck // TODO: Remove when migration to cli/internal/commands.Register is complete. (see #6283)
|
||||
hide(container.NewStartCommand(dockerCli)),
|
||||
//nolint:staticcheck // TODO: Remove when migration to cli/internal/commands.Register is complete. (see #6283)
|
||||
hide(container.NewStatsCommand(dockerCli)),
|
||||
//nolint:staticcheck // TODO: Remove when migration to cli/internal/commands.Register is complete. (see #6283)
|
||||
hide(container.NewStopCommand(dockerCli)),
|
||||
//nolint:staticcheck // TODO: Remove when migration to cli/internal/commands.Register is complete. (see #6283)
|
||||
hide(container.NewTopCommand(dockerCli)),
|
||||
//nolint:staticcheck // TODO: Remove when migration to cli/internal/commands.Register is complete. (see #6283)
|
||||
hide(container.NewUnpauseCommand(dockerCli)),
|
||||
//nolint:staticcheck // TODO: Remove when migration to cli/internal/commands.Register is complete. (see #6283)
|
||||
hide(container.NewUpdateCommand(dockerCli)),
|
||||
//nolint:staticcheck // TODO: Remove when migration to cli/internal/commands.Register is complete. (see #6283)
|
||||
hide(container.NewWaitCommand(dockerCli)),
|
||||
//nolint:staticcheck // TODO: Remove when migration to cli/internal/commands.Register is complete. (see #6283)
|
||||
hide(image.NewHistoryCommand(dockerCli)),
|
||||
//nolint:staticcheck // TODO: Remove when migration to cli/internal/commands.Register is complete. (see #6283)
|
||||
hide(image.NewImportCommand(dockerCli)),
|
||||
//nolint:staticcheck // TODO: Remove when migration to cli/internal/commands.Register is complete. (see #6283)
|
||||
hide(image.NewLoadCommand(dockerCli)),
|
||||
//nolint:staticcheck // TODO: Remove when migration to cli/internal/commands.Register is complete. (see #6283)
|
||||
hide(image.NewRemoveCommand(dockerCli)),
|
||||
//nolint:staticcheck // TODO: Remove when migration to cli/internal/commands.Register is complete. (see #6283)
|
||||
hide(image.NewSaveCommand(dockerCli)),
|
||||
//nolint:staticcheck // TODO: Remove when migration to cli/internal/commands.Register is complete. (see #6283)
|
||||
hide(image.NewTagCommand(dockerCli)),
|
||||
//nolint:staticcheck // TODO: Remove when migration to cli/internal/commands.Register is complete. (see #6283)
|
||||
hide(system.NewEventsCommand(dockerCli)),
|
||||
//nolint:staticcheck // TODO: Remove when migration to cli/internal/commands.Register is complete. (see #6283)
|
||||
hide(system.NewInspectCommand(dockerCli)),
|
||||
)
|
||||
}
|
||||
|
|
|
@ -13,11 +13,6 @@ import (
|
|||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// ValidArgsFn a function to be used by cobra command as `ValidArgsFunction` to offer command line completion.
|
||||
//
|
||||
// Deprecated: use [cobra.CompletionFunc].
|
||||
type ValidArgsFn = cobra.CompletionFunc
|
||||
|
||||
// APIClientProvider provides a method to get an [client.APIClient], initializing
|
||||
// it if needed.
|
||||
//
|
||||
|
|
|
@ -9,22 +9,28 @@ import (
|
|||
)
|
||||
|
||||
// NewConfigCommand returns a cobra command for `config` subcommands
|
||||
func NewConfigCommand(dockerCli command.Cli) *cobra.Command {
|
||||
//
|
||||
// Deprecated: Do not import commands directly. They will be removed in a future release.
|
||||
func NewConfigCommand(dockerCLI command.Cli) *cobra.Command {
|
||||
return newConfigCommand(dockerCLI)
|
||||
}
|
||||
|
||||
func newConfigCommand(dockerCLI command.Cli) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "config",
|
||||
Short: "Manage Swarm configs",
|
||||
Args: cli.NoArgs,
|
||||
RunE: command.ShowHelp(dockerCli.Err()),
|
||||
RunE: command.ShowHelp(dockerCLI.Err()),
|
||||
Annotations: map[string]string{
|
||||
"version": "1.30",
|
||||
"swarm": "manager",
|
||||
},
|
||||
}
|
||||
cmd.AddCommand(
|
||||
newConfigListCommand(dockerCli),
|
||||
newConfigCreateCommand(dockerCli),
|
||||
newConfigInspectCommand(dockerCli),
|
||||
newConfigRemoveCommand(dockerCli),
|
||||
newConfigListCommand(dockerCLI),
|
||||
newConfigCreateCommand(dockerCLI),
|
||||
newConfigInspectCommand(dockerCLI),
|
||||
newConfigRemoveCommand(dockerCLI),
|
||||
)
|
||||
return cmd
|
||||
}
|
||||
|
|
|
@ -16,6 +16,8 @@ import (
|
|||
)
|
||||
|
||||
// CreateOptions specifies some options that are used when creating a config.
|
||||
//
|
||||
// Deprecated: this type was for internal use and will be removed in the next release.
|
||||
type CreateOptions struct {
|
||||
Name string
|
||||
TemplateDriver string
|
||||
|
@ -23,9 +25,17 @@ type CreateOptions struct {
|
|||
Labels opts.ListOpts
|
||||
}
|
||||
|
||||
func newConfigCreateCommand(dockerCli command.Cli) *cobra.Command {
|
||||
createOpts := CreateOptions{
|
||||
Labels: opts.NewListOpts(opts.ValidateLabel),
|
||||
// createOptions specifies some options that are used when creating a config.
|
||||
type createOptions struct {
|
||||
name string
|
||||
templateDriver string
|
||||
file string
|
||||
labels opts.ListOpts
|
||||
}
|
||||
|
||||
func newConfigCreateCommand(dockerCLI command.Cli) *cobra.Command {
|
||||
createOpts := createOptions{
|
||||
labels: opts.NewListOpts(opts.ValidateLabel),
|
||||
}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
|
@ -33,39 +43,51 @@ func newConfigCreateCommand(dockerCli command.Cli) *cobra.Command {
|
|||
Short: "Create a config from a file or STDIN",
|
||||
Args: cli.ExactArgs(2),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
createOpts.Name = args[0]
|
||||
createOpts.File = args[1]
|
||||
return RunConfigCreate(cmd.Context(), dockerCli, createOpts)
|
||||
createOpts.name = args[0]
|
||||
createOpts.file = args[1]
|
||||
return runCreate(cmd.Context(), dockerCLI, createOpts)
|
||||
},
|
||||
ValidArgsFunction: completion.NoComplete,
|
||||
}
|
||||
flags := cmd.Flags()
|
||||
flags.VarP(&createOpts.Labels, "label", "l", "Config labels")
|
||||
flags.StringVar(&createOpts.TemplateDriver, "template-driver", "", "Template driver")
|
||||
flags.SetAnnotation("template-driver", "version", []string{"1.37"})
|
||||
flags.VarP(&createOpts.labels, "label", "l", "Config labels")
|
||||
flags.StringVar(&createOpts.templateDriver, "template-driver", "", "Template driver")
|
||||
_ = flags.SetAnnotation("template-driver", "version", []string{"1.37"})
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
// RunConfigCreate creates a config with the given options.
|
||||
//
|
||||
// Deprecated: this function was for internal use and will be removed in the next release.
|
||||
func RunConfigCreate(ctx context.Context, dockerCLI command.Cli, options CreateOptions) error {
|
||||
return runCreate(ctx, dockerCLI, createOptions{
|
||||
name: options.Name,
|
||||
templateDriver: options.TemplateDriver,
|
||||
file: options.File,
|
||||
labels: options.Labels,
|
||||
})
|
||||
}
|
||||
|
||||
// runCreate creates a config with the given options.
|
||||
func runCreate(ctx context.Context, dockerCLI command.Cli, options createOptions) error {
|
||||
apiClient := dockerCLI.Client()
|
||||
|
||||
configData, err := readConfigData(dockerCLI.In(), options.File)
|
||||
configData, err := readConfigData(dockerCLI.In(), options.file)
|
||||
if err != nil {
|
||||
return errors.Errorf("Error reading content from %q: %v", options.File, err)
|
||||
return errors.Errorf("Error reading content from %q: %v", options.file, err)
|
||||
}
|
||||
|
||||
spec := swarm.ConfigSpec{
|
||||
Annotations: swarm.Annotations{
|
||||
Name: options.Name,
|
||||
Labels: opts.ConvertKVStringsToMap(options.Labels.GetSlice()),
|
||||
Name: options.name,
|
||||
Labels: opts.ConvertKVStringsToMap(options.labels.GetSlice()),
|
||||
},
|
||||
Data: configData,
|
||||
}
|
||||
if options.TemplateDriver != "" {
|
||||
if options.templateDriver != "" {
|
||||
spec.Templating = &swarm.Driver{
|
||||
Name: options.TemplateDriver,
|
||||
Name: options.templateDriver,
|
||||
}
|
||||
}
|
||||
r, err := apiClient.ConfigCreate(ctx, spec)
|
||||
|
|
|
@ -30,7 +30,14 @@ Data:
|
|||
)
|
||||
|
||||
// NewFormat returns a Format for rendering using a config Context
|
||||
//
|
||||
// Deprecated: this function was only used internally and will be removed in the next release.
|
||||
func NewFormat(source string, quiet bool) formatter.Format {
|
||||
return newFormat(source, quiet)
|
||||
}
|
||||
|
||||
// newFormat returns a Format for rendering using a configContext.
|
||||
func newFormat(source string, quiet bool) formatter.Format {
|
||||
switch source {
|
||||
case formatter.PrettyFormatKey:
|
||||
return configInspectPrettyTemplate
|
||||
|
@ -44,7 +51,14 @@ func NewFormat(source string, quiet bool) formatter.Format {
|
|||
}
|
||||
|
||||
// FormatWrite writes the context
|
||||
func FormatWrite(ctx formatter.Context, configs []swarm.Config) error {
|
||||
//
|
||||
// Deprecated: this function was only used internally and will be removed in the next release.
|
||||
func FormatWrite(fmtCtx formatter.Context, configs []swarm.Config) error {
|
||||
return formatWrite(fmtCtx, configs)
|
||||
}
|
||||
|
||||
// formatWrite writes the context
|
||||
func formatWrite(fmtCtx formatter.Context, configs []swarm.Config) error {
|
||||
render := func(format func(subContext formatter.SubContext) error) error {
|
||||
for _, config := range configs {
|
||||
configCtx := &configContext{c: config}
|
||||
|
@ -54,7 +68,7 @@ func FormatWrite(ctx formatter.Context, configs []swarm.Config) error {
|
|||
}
|
||||
return nil
|
||||
}
|
||||
return ctx.Write(newConfigContext(), render)
|
||||
return fmtCtx.Write(newConfigContext(), render)
|
||||
}
|
||||
|
||||
func newConfigContext() *configContext {
|
||||
|
@ -115,9 +129,16 @@ func (c *configContext) Label(name string) string {
|
|||
}
|
||||
|
||||
// InspectFormatWrite renders the context for a list of configs
|
||||
func InspectFormatWrite(ctx formatter.Context, refs []string, getRef inspect.GetRefFunc) error {
|
||||
if ctx.Format != configInspectPrettyTemplate {
|
||||
return inspect.Inspect(ctx.Output, refs, string(ctx.Format), getRef)
|
||||
//
|
||||
// Deprecated: this function was only used internally and will be removed in the next release.
|
||||
func InspectFormatWrite(fmtCtx formatter.Context, refs []string, getRef inspect.GetRefFunc) error {
|
||||
return inspectFormatWrite(fmtCtx, refs, getRef)
|
||||
}
|
||||
|
||||
// inspectFormatWrite renders the context for a list of configs
|
||||
func inspectFormatWrite(fmtCtx formatter.Context, refs []string, getRef inspect.GetRefFunc) error {
|
||||
if fmtCtx.Format != configInspectPrettyTemplate {
|
||||
return inspect.Inspect(fmtCtx.Output, refs, string(fmtCtx.Format), getRef)
|
||||
}
|
||||
render := func(format func(subContext formatter.SubContext) error) error {
|
||||
for _, ref := range refs {
|
||||
|
@ -135,7 +156,7 @@ func InspectFormatWrite(ctx formatter.Context, refs []string, getRef inspect.Get
|
|||
}
|
||||
return nil
|
||||
}
|
||||
return ctx.Write(&configInspectContext{}, render)
|
||||
return fmtCtx.Write(&configInspectContext{}, render)
|
||||
}
|
||||
|
||||
type configInspectContext struct {
|
||||
|
|
|
@ -27,21 +27,21 @@ func TestConfigContextFormatWrite(t *testing.T) {
|
|||
},
|
||||
// Table format
|
||||
{
|
||||
formatter.Context{Format: NewFormat("table", false)},
|
||||
formatter.Context{Format: newFormat("table", false)},
|
||||
`ID NAME CREATED UPDATED
|
||||
1 passwords Less than a second ago Less than a second ago
|
||||
2 id_rsa Less than a second ago Less than a second ago
|
||||
`,
|
||||
},
|
||||
{
|
||||
formatter.Context{Format: NewFormat("table {{.Name}}", true)},
|
||||
formatter.Context{Format: newFormat("table {{.Name}}", true)},
|
||||
`NAME
|
||||
passwords
|
||||
id_rsa
|
||||
`,
|
||||
},
|
||||
{
|
||||
formatter.Context{Format: NewFormat("{{.ID}}-{{.Name}}", false)},
|
||||
formatter.Context{Format: newFormat("{{.ID}}-{{.Name}}", false)},
|
||||
`1-passwords
|
||||
2-id_rsa
|
||||
`,
|
||||
|
@ -64,7 +64,7 @@ id_rsa
|
|||
t.Run(string(tc.context.Format), func(t *testing.T) {
|
||||
var out bytes.Buffer
|
||||
tc.context.Output = &out
|
||||
if err := FormatWrite(tc.context, configs); err != nil {
|
||||
if err := formatWrite(tc.context, configs); err != nil {
|
||||
assert.ErrorContains(t, err, tc.expected)
|
||||
} else {
|
||||
assert.Equal(t, out.String(), tc.expected)
|
||||
|
|
|
@ -16,57 +16,76 @@ import (
|
|||
)
|
||||
|
||||
// InspectOptions contains options for the docker config inspect command.
|
||||
//
|
||||
// Deprecated: this type was for internal use and will be removed in the next release.
|
||||
type InspectOptions struct {
|
||||
Names []string
|
||||
Format string
|
||||
Pretty bool
|
||||
}
|
||||
|
||||
func newConfigInspectCommand(dockerCli command.Cli) *cobra.Command {
|
||||
opts := InspectOptions{}
|
||||
// inspectOptions contains options for the docker config inspect command.
|
||||
type inspectOptions struct {
|
||||
names []string
|
||||
format string
|
||||
pretty bool
|
||||
}
|
||||
|
||||
func newConfigInspectCommand(dockerCLI command.Cli) *cobra.Command {
|
||||
opts := inspectOptions{}
|
||||
cmd := &cobra.Command{
|
||||
Use: "inspect [OPTIONS] CONFIG [CONFIG...]",
|
||||
Short: "Display detailed information on one or more configs",
|
||||
Args: cli.RequiresMinArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
opts.Names = args
|
||||
return RunConfigInspect(cmd.Context(), dockerCli, opts)
|
||||
opts.names = args
|
||||
return runInspect(cmd.Context(), dockerCLI, opts)
|
||||
},
|
||||
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
return completeNames(dockerCli)(cmd, args, toComplete)
|
||||
return completeNames(dockerCLI)(cmd, args, toComplete)
|
||||
},
|
||||
}
|
||||
|
||||
cmd.Flags().StringVarP(&opts.Format, "format", "f", "", flagsHelper.InspectFormatHelp)
|
||||
cmd.Flags().BoolVar(&opts.Pretty, "pretty", false, "Print the information in a human friendly format")
|
||||
cmd.Flags().StringVarP(&opts.format, "format", "f", "", flagsHelper.InspectFormatHelp)
|
||||
cmd.Flags().BoolVar(&opts.pretty, "pretty", false, "Print the information in a human friendly format")
|
||||
return cmd
|
||||
}
|
||||
|
||||
// RunConfigInspect inspects the given Swarm config.
|
||||
//
|
||||
// Deprecated: this function was for internal use and will be removed in the next release.
|
||||
func RunConfigInspect(ctx context.Context, dockerCLI command.Cli, opts InspectOptions) error {
|
||||
return runInspect(ctx, dockerCLI, inspectOptions{
|
||||
names: opts.Names,
|
||||
format: opts.Format,
|
||||
pretty: opts.Pretty,
|
||||
})
|
||||
}
|
||||
|
||||
// runInspect inspects the given Swarm config.
|
||||
func runInspect(ctx context.Context, dockerCLI command.Cli, opts inspectOptions) error {
|
||||
apiClient := dockerCLI.Client()
|
||||
|
||||
if opts.Pretty {
|
||||
opts.Format = "pretty"
|
||||
if opts.pretty {
|
||||
opts.format = "pretty"
|
||||
}
|
||||
|
||||
getRef := func(id string) (any, []byte, error) {
|
||||
return apiClient.ConfigInspectWithRaw(ctx, id)
|
||||
}
|
||||
f := opts.Format
|
||||
|
||||
// check if the user is trying to apply a template to the pretty format, which
|
||||
// is not supported
|
||||
if strings.HasPrefix(f, "pretty") && f != "pretty" {
|
||||
if strings.HasPrefix(opts.format, "pretty") && opts.format != "pretty" {
|
||||
return errors.New("cannot supply extra formatting options to the pretty template")
|
||||
}
|
||||
|
||||
configCtx := formatter.Context{
|
||||
Output: dockerCLI.Out(),
|
||||
Format: NewFormat(f, false),
|
||||
Format: newFormat(opts.format, false),
|
||||
}
|
||||
|
||||
if err := InspectFormatWrite(configCtx, opts.Names, getRef); err != nil {
|
||||
if err := inspectFormatWrite(configCtx, opts.names, getRef); err != nil {
|
||||
return cli.StatusError{StatusCode: 1, Status: err.Error()}
|
||||
}
|
||||
return nil
|
||||
|
|
|
@ -16,14 +16,23 @@ import (
|
|||
)
|
||||
|
||||
// ListOptions contains options for the docker config ls command.
|
||||
//
|
||||
// Deprecated: this type was for internal use and will be removed in the next release.
|
||||
type ListOptions struct {
|
||||
Quiet bool
|
||||
Format string
|
||||
Filter opts.FilterOpt
|
||||
}
|
||||
|
||||
func newConfigListCommand(dockerCli command.Cli) *cobra.Command {
|
||||
listOpts := ListOptions{Filter: opts.NewFilterOpt()}
|
||||
// listOptions contains options for the docker config ls command.
|
||||
type listOptions struct {
|
||||
quiet bool
|
||||
format string
|
||||
filter opts.FilterOpt
|
||||
}
|
||||
|
||||
func newConfigListCommand(dockerCLI command.Cli) *cobra.Command {
|
||||
listOpts := listOptions{filter: opts.NewFilterOpt()}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "ls [OPTIONS]",
|
||||
|
@ -31,31 +40,42 @@ func newConfigListCommand(dockerCli command.Cli) *cobra.Command {
|
|||
Short: "List configs",
|
||||
Args: cli.NoArgs,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return RunConfigList(cmd.Context(), dockerCli, listOpts)
|
||||
return runList(cmd.Context(), dockerCLI, listOpts)
|
||||
},
|
||||
ValidArgsFunction: completion.NoComplete,
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
flags.BoolVarP(&listOpts.Quiet, "quiet", "q", false, "Only display IDs")
|
||||
flags.StringVar(&listOpts.Format, "format", "", flagsHelper.FormatHelp)
|
||||
flags.VarP(&listOpts.Filter, "filter", "f", "Filter output based on conditions provided")
|
||||
flags.BoolVarP(&listOpts.quiet, "quiet", "q", false, "Only display IDs")
|
||||
flags.StringVar(&listOpts.format, "format", "", flagsHelper.FormatHelp)
|
||||
flags.VarP(&listOpts.filter, "filter", "f", "Filter output based on conditions provided")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
// RunConfigList lists Swarm configs.
|
||||
//
|
||||
// Deprecated: this function was for internal use and will be removed in the next release.
|
||||
func RunConfigList(ctx context.Context, dockerCLI command.Cli, options ListOptions) error {
|
||||
return runList(ctx, dockerCLI, listOptions{
|
||||
quiet: options.Quiet,
|
||||
format: options.Format,
|
||||
filter: options.Filter,
|
||||
})
|
||||
}
|
||||
|
||||
// runList lists Swarm configs.
|
||||
func runList(ctx context.Context, dockerCLI command.Cli, options listOptions) error {
|
||||
apiClient := dockerCLI.Client()
|
||||
|
||||
configs, err := apiClient.ConfigList(ctx, swarm.ConfigListOptions{Filters: options.Filter.Value()})
|
||||
configs, err := apiClient.ConfigList(ctx, swarm.ConfigListOptions{Filters: options.filter.Value()})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
format := options.Format
|
||||
format := options.format
|
||||
if len(format) == 0 {
|
||||
if len(dockerCLI.ConfigFile().ConfigFormat) > 0 && !options.Quiet {
|
||||
if len(dockerCLI.ConfigFile().ConfigFormat) > 0 && !options.quiet {
|
||||
format = dockerCLI.ConfigFile().ConfigFormat
|
||||
} else {
|
||||
format = formatter.TableFormatKey
|
||||
|
@ -68,7 +88,7 @@ func RunConfigList(ctx context.Context, dockerCLI command.Cli, options ListOptio
|
|||
|
||||
configCtx := formatter.Context{
|
||||
Output: dockerCLI.Out(),
|
||||
Format: NewFormat(format, options.Quiet),
|
||||
Format: newFormat(format, options.quiet),
|
||||
}
|
||||
return FormatWrite(configCtx, configs)
|
||||
return formatWrite(configCtx, configs)
|
||||
}
|
||||
|
|
|
@ -11,34 +11,40 @@ import (
|
|||
)
|
||||
|
||||
// RemoveOptions contains options for the docker config rm command.
|
||||
//
|
||||
// Deprecated: this type was for internal use and will be removed in the next release.
|
||||
type RemoveOptions struct {
|
||||
Names []string
|
||||
}
|
||||
|
||||
func newConfigRemoveCommand(dockerCli command.Cli) *cobra.Command {
|
||||
func newConfigRemoveCommand(dockerCLI command.Cli) *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "rm CONFIG [CONFIG...]",
|
||||
Aliases: []string{"remove"},
|
||||
Short: "Remove one or more configs",
|
||||
Args: cli.RequiresMinArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
opts := RemoveOptions{
|
||||
Names: args,
|
||||
}
|
||||
return RunConfigRemove(cmd.Context(), dockerCli, opts)
|
||||
return runRemove(cmd.Context(), dockerCLI, args)
|
||||
},
|
||||
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
return completeNames(dockerCli)(cmd, args, toComplete)
|
||||
return completeNames(dockerCLI)(cmd, args, toComplete)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// RunConfigRemove removes the given Swarm configs.
|
||||
//
|
||||
// Deprecated: this function was for internal use and will be removed in the next release.
|
||||
func RunConfigRemove(ctx context.Context, dockerCLI command.Cli, opts RemoveOptions) error {
|
||||
return runRemove(ctx, dockerCLI, opts.Names)
|
||||
}
|
||||
|
||||
// runRemove removes the given Swarm configs.
|
||||
func runRemove(ctx context.Context, dockerCLI command.Cli, names []string) error {
|
||||
apiClient := dockerCLI.Client()
|
||||
|
||||
var errs []error
|
||||
for _, name := range opts.Names {
|
||||
for _, name := range names {
|
||||
if err := apiClient.ConfigRemove(ctx, name); err != nil {
|
||||
errs = append(errs, err)
|
||||
continue
|
||||
|
|
|
@ -41,7 +41,13 @@ func inspectContainerAndCheckState(ctx context.Context, apiClient client.APIClie
|
|||
}
|
||||
|
||||
// NewAttachCommand creates a new cobra.Command for `docker attach`
|
||||
//
|
||||
// Deprecated: Do not import commands directly. They will be removed in a future release.
|
||||
func NewAttachCommand(dockerCLI command.Cli) *cobra.Command {
|
||||
return newAttachCommand(dockerCLI)
|
||||
}
|
||||
|
||||
func newAttachCommand(dockerCLI command.Cli) *cobra.Command {
|
||||
var opts AttachOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
|
|
|
@ -74,7 +74,7 @@ func TestNewAttachCommandErrors(t *testing.T) {
|
|||
}
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
cmd := NewAttachCommand(test.NewFakeCli(&fakeClient{inspectFunc: tc.containerInspectFunc}))
|
||||
cmd := newAttachCommand(test.NewFakeCli(&fakeClient{inspectFunc: tc.containerInspectFunc}))
|
||||
cmd.SetOut(io.Discard)
|
||||
cmd.SetErr(io.Discard)
|
||||
cmd.SetArgs(tc.args)
|
||||
|
|
|
@ -7,39 +7,45 @@ import (
|
|||
)
|
||||
|
||||
// NewContainerCommand returns a cobra command for `container` subcommands
|
||||
func NewContainerCommand(dockerCli command.Cli) *cobra.Command {
|
||||
//
|
||||
// Deprecated: Do not import commands directly. They will be removed in a future release.
|
||||
func NewContainerCommand(dockerCLI command.Cli) *cobra.Command {
|
||||
return newContainerCommand(dockerCLI)
|
||||
}
|
||||
|
||||
func newContainerCommand(dockerCLI command.Cli) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "container",
|
||||
Short: "Manage containers",
|
||||
Args: cli.NoArgs,
|
||||
RunE: command.ShowHelp(dockerCli.Err()),
|
||||
RunE: command.ShowHelp(dockerCLI.Err()),
|
||||
}
|
||||
cmd.AddCommand(
|
||||
NewAttachCommand(dockerCli),
|
||||
NewCommitCommand(dockerCli),
|
||||
NewCopyCommand(dockerCli),
|
||||
NewCreateCommand(dockerCli),
|
||||
NewDiffCommand(dockerCli),
|
||||
NewExecCommand(dockerCli),
|
||||
NewExportCommand(dockerCli),
|
||||
NewKillCommand(dockerCli),
|
||||
NewLogsCommand(dockerCli),
|
||||
NewPauseCommand(dockerCli),
|
||||
NewPortCommand(dockerCli),
|
||||
NewRenameCommand(dockerCli),
|
||||
NewRestartCommand(dockerCli),
|
||||
newRemoveCommand(dockerCli),
|
||||
NewRunCommand(dockerCli),
|
||||
NewStartCommand(dockerCli),
|
||||
NewStatsCommand(dockerCli),
|
||||
NewStopCommand(dockerCli),
|
||||
NewTopCommand(dockerCli),
|
||||
NewUnpauseCommand(dockerCli),
|
||||
NewUpdateCommand(dockerCli),
|
||||
NewWaitCommand(dockerCli),
|
||||
newListCommand(dockerCli),
|
||||
newInspectCommand(dockerCli),
|
||||
NewPruneCommand(dockerCli),
|
||||
newAttachCommand(dockerCLI),
|
||||
newCommitCommand(dockerCLI),
|
||||
newCopyCommand(dockerCLI),
|
||||
newCreateCommand(dockerCLI),
|
||||
newDiffCommand(dockerCLI),
|
||||
newExecCommand(dockerCLI),
|
||||
newExportCommand(dockerCLI),
|
||||
newKillCommand(dockerCLI),
|
||||
newLogsCommand(dockerCLI),
|
||||
newPauseCommand(dockerCLI),
|
||||
newPortCommand(dockerCLI),
|
||||
newRenameCommand(dockerCLI),
|
||||
newRestartCommand(dockerCLI),
|
||||
newRemoveCommand(dockerCLI),
|
||||
newRunCommand(dockerCLI),
|
||||
newStartCommand(dockerCLI),
|
||||
newStatsCommand(dockerCLI),
|
||||
newStopCommand(dockerCLI),
|
||||
newTopCommand(dockerCLI),
|
||||
newUnpauseCommand(dockerCLI),
|
||||
newUpdateCommand(dockerCLI),
|
||||
newWaitCommand(dockerCLI),
|
||||
newListCommand(dockerCLI),
|
||||
newInspectCommand(dockerCLI),
|
||||
newPruneCommand(dockerCLI),
|
||||
)
|
||||
return cmd
|
||||
}
|
||||
|
|
|
@ -23,7 +23,13 @@ type commitOptions struct {
|
|||
}
|
||||
|
||||
// NewCommitCommand creates a new cobra.Command for `docker commit`
|
||||
func NewCommitCommand(dockerCli command.Cli) *cobra.Command {
|
||||
//
|
||||
// Deprecated: Do not import commands directly. They will be removed in a future release.
|
||||
func NewCommitCommand(dockerCLI command.Cli) *cobra.Command {
|
||||
return newCommitCommand(dockerCLI)
|
||||
}
|
||||
|
||||
func newCommitCommand(dockerCLI command.Cli) *cobra.Command {
|
||||
var options commitOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
|
@ -35,12 +41,12 @@ func NewCommitCommand(dockerCli command.Cli) *cobra.Command {
|
|||
if len(args) > 1 {
|
||||
options.reference = args[1]
|
||||
}
|
||||
return runCommit(cmd.Context(), dockerCli, &options)
|
||||
return runCommit(cmd.Context(), dockerCLI, &options)
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
"aliases": "docker container commit, docker commit",
|
||||
},
|
||||
ValidArgsFunction: completion.ContainerNames(dockerCli, false),
|
||||
ValidArgsFunction: completion.ContainerNames(dockerCLI, false),
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
|
|
|
@ -29,7 +29,7 @@ func TestRunCommit(t *testing.T) {
|
|||
},
|
||||
})
|
||||
|
||||
cmd := NewCommitCommand(cli)
|
||||
cmd := newCommitCommand(cli)
|
||||
cmd.SetOut(io.Discard)
|
||||
cmd.SetArgs(
|
||||
[]string{
|
||||
|
@ -60,7 +60,7 @@ func TestRunCommitClientError(t *testing.T) {
|
|||
},
|
||||
})
|
||||
|
||||
cmd := NewCommitCommand(cli)
|
||||
cmd := newCommitCommand(cli)
|
||||
cmd.SetOut(io.Discard)
|
||||
cmd.SetErr(io.Discard)
|
||||
cmd.SetArgs([]string{"container-id"})
|
||||
|
|
|
@ -59,7 +59,7 @@ func TestCompletePid(t *testing.T) {
|
|||
cli := test.NewFakeCli(&fakeClient{
|
||||
containerListFunc: tc.containerListFunc,
|
||||
})
|
||||
completions, directive := completePid(cli)(NewRunCommand(cli), nil, tc.toComplete)
|
||||
completions, directive := completePid(cli)(newRunCommand(cli), nil, tc.toComplete)
|
||||
assert.Check(t, is.DeepEqual(completions, tc.expectedCompletions))
|
||||
assert.Check(t, is.Equal(directive, tc.expectedDirective))
|
||||
})
|
||||
|
|
|
@ -122,7 +122,13 @@ func copyProgress(ctx context.Context, dst io.Writer, header string, total *int6
|
|||
}
|
||||
|
||||
// NewCopyCommand creates a new `docker cp` command
|
||||
func NewCopyCommand(dockerCli command.Cli) *cobra.Command {
|
||||
//
|
||||
// Deprecated: Do not import commands directly. They will be removed in a future release.
|
||||
func NewCopyCommand(dockerCLI command.Cli) *cobra.Command {
|
||||
return newCopyCommand(dockerCLI)
|
||||
}
|
||||
|
||||
func newCopyCommand(dockerCLI command.Cli) *cobra.Command {
|
||||
var opts copyOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
|
@ -147,9 +153,9 @@ container source to stdout.`,
|
|||
opts.destination = args[1]
|
||||
if !cmd.Flag("quiet").Changed {
|
||||
// User did not specify "quiet" flag; suppress output if no terminal is attached
|
||||
opts.quiet = !dockerCli.Out().IsTerminal()
|
||||
opts.quiet = !dockerCLI.Out().IsTerminal()
|
||||
}
|
||||
return runCopy(cmd.Context(), dockerCli, opts)
|
||||
return runCopy(cmd.Context(), dockerCLI, opts)
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
"aliases": "docker container cp, docker cp",
|
||||
|
|
|
@ -11,7 +11,7 @@ import (
|
|||
"path"
|
||||
"strings"
|
||||
|
||||
cerrdefs "github.com/containerd/errdefs"
|
||||
"github.com/containerd/errdefs"
|
||||
"github.com/containerd/platforms"
|
||||
"github.com/distribution/reference"
|
||||
"github.com/docker/cli/cli"
|
||||
|
@ -52,7 +52,13 @@ type createOptions struct {
|
|||
}
|
||||
|
||||
// NewCreateCommand creates a new cobra.Command for `docker create`
|
||||
func NewCreateCommand(dockerCli command.Cli) *cobra.Command {
|
||||
//
|
||||
// Deprecated: Do not import commands directly. They will be removed in a future release.
|
||||
func NewCreateCommand(dockerCLI command.Cli) *cobra.Command {
|
||||
return newCreateCommand(dockerCLI)
|
||||
}
|
||||
|
||||
func newCreateCommand(dockerCLI command.Cli) *cobra.Command {
|
||||
var options createOptions
|
||||
var copts *containerOptions
|
||||
|
||||
|
@ -65,12 +71,12 @@ func NewCreateCommand(dockerCli command.Cli) *cobra.Command {
|
|||
if len(args) > 1 {
|
||||
copts.Args = args[1:]
|
||||
}
|
||||
return runCreate(cmd.Context(), dockerCli, cmd.Flags(), &options, copts)
|
||||
return runCreate(cmd.Context(), dockerCLI, cmd.Flags(), &options, copts)
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
"aliases": "docker container create, docker create",
|
||||
},
|
||||
ValidArgsFunction: completion.ImageNames(dockerCli, -1),
|
||||
ValidArgsFunction: completion.ImageNames(dockerCLI, -1),
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
|
@ -86,11 +92,14 @@ func NewCreateCommand(dockerCli command.Cli) *cobra.Command {
|
|||
// with hostname
|
||||
flags.Bool("help", false, "Print usage")
|
||||
|
||||
command.AddPlatformFlag(flags, &options.platform)
|
||||
command.AddTrustVerificationFlags(flags, &options.untrusted, dockerCli.ContentTrustEnabled())
|
||||
// TODO(thaJeztah): consider adding platform as "image create option" on containerOptions
|
||||
addPlatformFlag(flags, &options.platform)
|
||||
_ = cmd.RegisterFlagCompletionFunc("platform", completion.Platforms)
|
||||
|
||||
flags.BoolVar(&options.untrusted, "disable-content-trust", !dockerCLI.ContentTrustEnabled(), "Skip image verification")
|
||||
copts = addFlags(flags)
|
||||
|
||||
addCompletions(cmd, dockerCli)
|
||||
addCompletions(cmd, dockerCLI)
|
||||
|
||||
flags.VisitAll(func(flag *pflag.Flag) {
|
||||
// Set a default completion function if none was set. We don't look
|
||||
|
@ -341,7 +350,7 @@ func createContainer(ctx context.Context, dockerCli command.Cli, containerCfg *c
|
|||
response, err := dockerCli.Client().ContainerCreate(ctx, config, hostConfig, networkingConfig, platform, options.name)
|
||||
if err != nil {
|
||||
// Pull image if it does not exist locally and we have the PullImageMissing option. Default behavior.
|
||||
if cerrdefs.IsNotFound(err) && namedRef != nil && options.pull == PullImageMissing {
|
||||
if errdefs.IsNotFound(err) && namedRef != nil && options.pull == PullImageMissing {
|
||||
if !options.quiet {
|
||||
// we don't want to write to stdout anything apart from container.ID
|
||||
_, _ = fmt.Fprintf(dockerCli.Err(), "Unable to find image '%s' locally\n", reference.FamiliarString(namedRef))
|
||||
|
|
|
@ -116,7 +116,7 @@ func TestCreateContainerImagePullPolicy(t *testing.T) {
|
|||
t.Run(tc.PullPolicy, func(t *testing.T) {
|
||||
pullCounter := 0
|
||||
|
||||
client := &fakeClient{
|
||||
apiClient := &fakeClient{
|
||||
createContainerFunc: func(
|
||||
config *container.Config,
|
||||
hostConfig *container.HostConfig,
|
||||
|
@ -140,7 +140,7 @@ func TestCreateContainerImagePullPolicy(t *testing.T) {
|
|||
return system.Info{IndexServerAddress: "https://indexserver.example.com"}, nil
|
||||
},
|
||||
}
|
||||
fakeCLI := test.NewFakeCli(client)
|
||||
fakeCLI := test.NewFakeCli(apiClient)
|
||||
id, err := createContainer(context.Background(), fakeCLI, config, &createOptions{
|
||||
name: "name",
|
||||
platform: runtime.GOOS,
|
||||
|
@ -206,7 +206,7 @@ func TestCreateContainerValidateFlags(t *testing.T) {
|
|||
},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
cmd := NewCreateCommand(test.NewFakeCli(&fakeClient{}))
|
||||
cmd := newCreateCommand(test.NewFakeCli(&fakeClient{}))
|
||||
cmd.SetOut(io.Discard)
|
||||
cmd.SetErr(io.Discard)
|
||||
cmd.SetArgs(tc.args)
|
||||
|
@ -260,7 +260,7 @@ func TestNewCreateCommandWithContentTrustErrors(t *testing.T) {
|
|||
},
|
||||
}, test.EnableContentTrust)
|
||||
fakeCLI.SetNotaryClient(tc.notaryFunc)
|
||||
cmd := NewCreateCommand(fakeCLI)
|
||||
cmd := newCreateCommand(fakeCLI)
|
||||
cmd.SetOut(io.Discard)
|
||||
cmd.SetErr(io.Discard)
|
||||
cmd.SetArgs(tc.args)
|
||||
|
@ -314,7 +314,7 @@ func TestNewCreateCommandWithWarnings(t *testing.T) {
|
|||
return container.CreateResponse{Warnings: tc.warnings}, nil
|
||||
},
|
||||
})
|
||||
cmd := NewCreateCommand(fakeCLI)
|
||||
cmd := newCreateCommand(fakeCLI)
|
||||
cmd.SetOut(io.Discard)
|
||||
cmd.SetArgs(tc.args)
|
||||
err := cmd.Execute()
|
||||
|
@ -366,7 +366,7 @@ func TestCreateContainerWithProxyConfig(t *testing.T) {
|
|||
},
|
||||
},
|
||||
})
|
||||
cmd := NewCreateCommand(fakeCLI)
|
||||
cmd := newCreateCommand(fakeCLI)
|
||||
cmd.SetOut(io.Discard)
|
||||
cmd.SetArgs([]string{"image:tag"})
|
||||
err := cmd.Execute()
|
||||
|
|
|
@ -11,18 +11,24 @@ import (
|
|||
)
|
||||
|
||||
// NewDiffCommand creates a new cobra.Command for `docker diff`
|
||||
func NewDiffCommand(dockerCli command.Cli) *cobra.Command {
|
||||
//
|
||||
// Deprecated: Do not import commands directly. They will be removed in a future release.
|
||||
func NewDiffCommand(dockerCLI command.Cli) *cobra.Command {
|
||||
return newDiffCommand(dockerCLI)
|
||||
}
|
||||
|
||||
func newDiffCommand(dockerCLI command.Cli) *cobra.Command {
|
||||
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 {
|
||||
return runDiff(cmd.Context(), dockerCli, args[0])
|
||||
return runDiff(cmd.Context(), dockerCLI, args[0])
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
"aliases": "docker container diff, docker diff",
|
||||
},
|
||||
ValidArgsFunction: completion.ContainerNames(dockerCli, false),
|
||||
ValidArgsFunction: completion.ContainerNames(dockerCLI, false),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -33,7 +39,7 @@ func runDiff(ctx context.Context, dockerCLI command.Cli, containerID string) err
|
|||
}
|
||||
diffCtx := formatter.Context{
|
||||
Output: dockerCLI.Out(),
|
||||
Format: NewDiffFormat("{{.Type}} {{.Path}}"),
|
||||
Format: newDiffFormat("{{.Type}} {{.Path}}"),
|
||||
}
|
||||
return DiffFormatWrite(diffCtx, changes)
|
||||
return diffFormatWrite(diffCtx, changes)
|
||||
}
|
||||
|
|
|
@ -36,7 +36,7 @@ func TestRunDiff(t *testing.T) {
|
|||
},
|
||||
})
|
||||
|
||||
cmd := NewDiffCommand(cli)
|
||||
cmd := newDiffCommand(cli)
|
||||
cmd.SetOut(io.Discard)
|
||||
|
||||
cmd.SetArgs([]string{"container-id"})
|
||||
|
@ -68,7 +68,7 @@ func TestRunDiffClientError(t *testing.T) {
|
|||
},
|
||||
})
|
||||
|
||||
cmd := NewDiffCommand(cli)
|
||||
cmd := newDiffCommand(cli)
|
||||
cmd.SetOut(io.Discard)
|
||||
cmd.SetErr(io.Discard)
|
||||
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
package container
|
||||
|
||||
import cerrdefs "github.com/containerd/errdefs"
|
||||
import "github.com/containerd/errdefs"
|
||||
|
||||
func invalidParameter(err error) error {
|
||||
if err == nil || cerrdefs.IsInvalidArgument(err) {
|
||||
if err == nil || errdefs.IsInvalidArgument(err) {
|
||||
return err
|
||||
}
|
||||
return invalidParameterErr{err}
|
||||
|
@ -17,7 +17,7 @@ func (e invalidParameterErr) Unwrap() error {
|
|||
}
|
||||
|
||||
func notFound(err error) error {
|
||||
if err == nil || cerrdefs.IsNotFound(err) {
|
||||
if err == nil || errdefs.IsNotFound(err) {
|
||||
return err
|
||||
}
|
||||
return notFoundErr{err}
|
||||
|
|
|
@ -40,7 +40,13 @@ func NewExecOptions() ExecOptions {
|
|||
}
|
||||
|
||||
// NewExecCommand creates a new cobra.Command for `docker exec`
|
||||
func NewExecCommand(dockerCli command.Cli) *cobra.Command {
|
||||
//
|
||||
// Deprecated: Do not import commands directly. They will be removed in a future release.
|
||||
func NewExecCommand(dockerCLI command.Cli) *cobra.Command {
|
||||
return newExecCommand(dockerCLI)
|
||||
}
|
||||
|
||||
func newExecCommand(dockerCLI command.Cli) *cobra.Command {
|
||||
options := NewExecOptions()
|
||||
|
||||
cmd := &cobra.Command{
|
||||
|
@ -50,9 +56,9 @@ func NewExecCommand(dockerCli command.Cli) *cobra.Command {
|
|||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
containerIDorName := args[0]
|
||||
options.Command = args[1:]
|
||||
return RunExec(cmd.Context(), dockerCli, containerIDorName, options)
|
||||
return RunExec(cmd.Context(), dockerCLI, containerIDorName, options)
|
||||
},
|
||||
ValidArgsFunction: completion.ContainerNames(dockerCli, false, func(ctr container.Summary) bool {
|
||||
ValidArgsFunction: completion.ContainerNames(dockerCLI, false, func(ctr container.Summary) bool {
|
||||
return ctr.State != container.StatePaused
|
||||
}),
|
||||
Annotations: map[string]string{
|
||||
|
|
|
@ -234,13 +234,13 @@ func TestGetExecExitStatus(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, testcase := range testcases {
|
||||
client := &fakeClient{
|
||||
apiClient := &fakeClient{
|
||||
execInspectFunc: func(id string) (container.ExecInspect, error) {
|
||||
assert.Check(t, is.Equal(execID, id))
|
||||
return container.ExecInspect{ExitCode: testcase.exitCode}, testcase.inspectError
|
||||
},
|
||||
}
|
||||
err := getExecExitStatus(context.Background(), client, execID)
|
||||
err := getExecExitStatus(context.Background(), apiClient, execID)
|
||||
assert.Check(t, is.Equal(testcase.expectedError, err))
|
||||
}
|
||||
}
|
||||
|
@ -263,7 +263,7 @@ func TestNewExecCommandErrors(t *testing.T) {
|
|||
}
|
||||
for _, tc := range testCases {
|
||||
fakeCLI := test.NewFakeCli(&fakeClient{inspectFunc: tc.containerInspectFunc})
|
||||
cmd := NewExecCommand(fakeCLI)
|
||||
cmd := newExecCommand(fakeCLI)
|
||||
cmd.SetOut(io.Discard)
|
||||
cmd.SetArgs(tc.args)
|
||||
assert.ErrorContains(t, cmd.Execute(), tc.expectedError)
|
||||
|
|
|
@ -18,7 +18,13 @@ type exportOptions struct {
|
|||
}
|
||||
|
||||
// NewExportCommand creates a new `docker export` command
|
||||
func NewExportCommand(dockerCli command.Cli) *cobra.Command {
|
||||
//
|
||||
// Deprecated: Do not import commands directly. They will be removed in a future release.
|
||||
func NewExportCommand(dockerCLI command.Cli) *cobra.Command {
|
||||
return newExportCommand(dockerCLI)
|
||||
}
|
||||
|
||||
func newExportCommand(dockerCLI command.Cli) *cobra.Command {
|
||||
var opts exportOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
|
@ -27,12 +33,12 @@ func NewExportCommand(dockerCli command.Cli) *cobra.Command {
|
|||
Args: cli.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
opts.container = args[0]
|
||||
return runExport(cmd.Context(), dockerCli, opts)
|
||||
return runExport(cmd.Context(), dockerCLI, opts)
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
"aliases": "docker container export, docker export",
|
||||
},
|
||||
ValidArgsFunction: completion.ContainerNames(dockerCli, true),
|
||||
ValidArgsFunction: completion.ContainerNames(dockerCLI, true),
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
|
|
|
@ -19,7 +19,7 @@ func TestContainerExportOutputToFile(t *testing.T) {
|
|||
return io.NopCloser(strings.NewReader("bar")), nil
|
||||
},
|
||||
})
|
||||
cmd := NewExportCommand(cli)
|
||||
cmd := newExportCommand(cli)
|
||||
cmd.SetOut(io.Discard)
|
||||
cmd.SetArgs([]string{"-o", dir.Join("foo"), "container"})
|
||||
assert.NilError(t, cmd.Execute())
|
||||
|
@ -37,7 +37,7 @@ func TestContainerExportOutputToIrregularFile(t *testing.T) {
|
|||
return io.NopCloser(strings.NewReader("foo")), nil
|
||||
},
|
||||
})
|
||||
cmd := NewExportCommand(cli)
|
||||
cmd := newExportCommand(cli)
|
||||
cmd.SetOut(io.Discard)
|
||||
cmd.SetErr(io.Discard)
|
||||
cmd.SetArgs([]string{"-o", "/dev/random", "container"})
|
||||
|
|
|
@ -13,7 +13,14 @@ const (
|
|||
)
|
||||
|
||||
// NewDiffFormat returns a format for use with a diff Context
|
||||
//
|
||||
// Deprecated: this function was only used internally and will be removed in the next release.
|
||||
func NewDiffFormat(source string) formatter.Format {
|
||||
return newDiffFormat(source)
|
||||
}
|
||||
|
||||
// newDiffFormat returns a format for use with a diff [formatter.Context].
|
||||
func newDiffFormat(source string) formatter.Format {
|
||||
if source == formatter.TableFormatKey {
|
||||
return defaultDiffTableFormat
|
||||
}
|
||||
|
@ -21,16 +28,22 @@ func NewDiffFormat(source string) formatter.Format {
|
|||
}
|
||||
|
||||
// DiffFormatWrite writes formatted diff using the Context
|
||||
func DiffFormatWrite(ctx formatter.Context, changes []container.FilesystemChange) error {
|
||||
render := func(format func(subContext formatter.SubContext) error) error {
|
||||
//
|
||||
// Deprecated: this function was only used internally and will be removed in the next release.
|
||||
func DiffFormatWrite(fmtCtx formatter.Context, changes []container.FilesystemChange) error {
|
||||
return diffFormatWrite(fmtCtx, changes)
|
||||
}
|
||||
|
||||
// diffFormatWrite writes formatted diff using the [formatter.Context].
|
||||
func diffFormatWrite(fmtCtx formatter.Context, changes []container.FilesystemChange) error {
|
||||
return fmtCtx.Write(newDiffContext(), func(format func(subContext formatter.SubContext) error) error {
|
||||
for _, change := range changes {
|
||||
if err := format(&diffContext{c: change}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return ctx.Write(newDiffContext(), render)
|
||||
})
|
||||
}
|
||||
|
||||
type diffContext struct {
|
||||
|
@ -39,12 +52,14 @@ type diffContext struct {
|
|||
}
|
||||
|
||||
func newDiffContext() *diffContext {
|
||||
diffCtx := diffContext{}
|
||||
diffCtx.Header = formatter.SubHeaderContext{
|
||||
"Type": changeTypeHeader,
|
||||
"Path": pathHeader,
|
||||
return &diffContext{
|
||||
HeaderContext: formatter.HeaderContext{
|
||||
Header: formatter.SubHeaderContext{
|
||||
"Type": changeTypeHeader,
|
||||
"Path": pathHeader,
|
||||
},
|
||||
},
|
||||
}
|
||||
return &diffCtx
|
||||
}
|
||||
|
||||
func (d *diffContext) MarshalJSON() ([]byte, error) {
|
||||
|
|
|
@ -16,7 +16,7 @@ func TestDiffContextFormatWrite(t *testing.T) {
|
|||
expected string
|
||||
}{
|
||||
{
|
||||
formatter.Context{Format: NewDiffFormat("table")},
|
||||
formatter.Context{Format: newDiffFormat("table")},
|
||||
`CHANGE TYPE PATH
|
||||
C /var/log/app.log
|
||||
A /usr/app/app.js
|
||||
|
@ -24,7 +24,7 @@ D /usr/app/old_app.js
|
|||
`,
|
||||
},
|
||||
{
|
||||
formatter.Context{Format: NewDiffFormat("table {{.Path}}")},
|
||||
formatter.Context{Format: newDiffFormat("table {{.Path}}")},
|
||||
`PATH
|
||||
/var/log/app.log
|
||||
/usr/app/app.js
|
||||
|
@ -32,7 +32,7 @@ D /usr/app/old_app.js
|
|||
`,
|
||||
},
|
||||
{
|
||||
formatter.Context{Format: NewDiffFormat("{{.Type}}: {{.Path}}")},
|
||||
formatter.Context{Format: newDiffFormat("{{.Type}}: {{.Path}}")},
|
||||
`C: /var/log/app.log
|
||||
A: /usr/app/app.js
|
||||
D: /usr/app/old_app.js
|
||||
|
@ -50,7 +50,7 @@ D: /usr/app/old_app.js
|
|||
t.Run(string(tc.context.Format), func(t *testing.T) {
|
||||
out := bytes.NewBufferString("")
|
||||
tc.context.Output = out
|
||||
err := DiffFormatWrite(tc.context, diffs)
|
||||
err := diffFormatWrite(tc.context, diffs)
|
||||
if err != nil {
|
||||
assert.Error(t, err, tc.expected)
|
||||
} else {
|
||||
|
|
|
@ -18,7 +18,13 @@ type killOptions struct {
|
|||
}
|
||||
|
||||
// NewKillCommand creates a new cobra.Command for `docker kill`
|
||||
func NewKillCommand(dockerCli command.Cli) *cobra.Command {
|
||||
//
|
||||
// Deprecated: Do not import commands directly. They will be removed in a future release.
|
||||
func NewKillCommand(dockerCLI command.Cli) *cobra.Command {
|
||||
return newKillCommand(dockerCLI)
|
||||
}
|
||||
|
||||
func newKillCommand(dockerCLI command.Cli) *cobra.Command {
|
||||
var opts killOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
|
@ -27,12 +33,12 @@ func NewKillCommand(dockerCli command.Cli) *cobra.Command {
|
|||
Args: cli.RequiresMinArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
opts.containers = args
|
||||
return runKill(cmd.Context(), dockerCli, &opts)
|
||||
return runKill(cmd.Context(), dockerCLI, &opts)
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
"aliases": "docker container kill, docker kill",
|
||||
},
|
||||
ValidArgsFunction: completion.ContainerNames(dockerCli, false),
|
||||
ValidArgsFunction: completion.ContainerNames(dockerCLI, false),
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
|
|
|
@ -24,7 +24,7 @@ func TestRunKill(t *testing.T) {
|
|||
},
|
||||
})
|
||||
|
||||
cmd := NewKillCommand(cli)
|
||||
cmd := newKillCommand(cli)
|
||||
cmd.SetOut(io.Discard)
|
||||
|
||||
cmd.SetArgs([]string{
|
||||
|
@ -56,7 +56,7 @@ func TestRunKillClientError(t *testing.T) {
|
|||
},
|
||||
})
|
||||
|
||||
cmd := NewKillCommand(cli)
|
||||
cmd := newKillCommand(cli)
|
||||
cmd.SetOut(io.Discard)
|
||||
cmd.SetErr(io.Discard)
|
||||
|
||||
|
|
|
@ -29,7 +29,13 @@ type psOptions struct {
|
|||
}
|
||||
|
||||
// NewPsCommand creates a new cobra.Command for `docker ps`
|
||||
//
|
||||
// Deprecated: Do not import commands directly. They will be removed in a future release.
|
||||
func NewPsCommand(dockerCLI command.Cli) *cobra.Command {
|
||||
return newPsCommand(dockerCLI)
|
||||
}
|
||||
|
||||
func newPsCommand(dockerCLI command.Cli) *cobra.Command {
|
||||
options := psOptions{filter: opts.NewFilterOpt()}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
|
@ -62,7 +68,7 @@ func NewPsCommand(dockerCLI command.Cli) *cobra.Command {
|
|||
}
|
||||
|
||||
func newListCommand(dockerCLI command.Cli) *cobra.Command {
|
||||
cmd := *NewPsCommand(dockerCLI)
|
||||
cmd := *newPsCommand(dockerCLI)
|
||||
cmd.Aliases = []string{"ps", "list"}
|
||||
cmd.Use = "ls [OPTIONS]"
|
||||
return &cmd
|
||||
|
|
|
@ -24,7 +24,13 @@ type logsOptions struct {
|
|||
}
|
||||
|
||||
// NewLogsCommand creates a new cobra.Command for `docker logs`
|
||||
func NewLogsCommand(dockerCli command.Cli) *cobra.Command {
|
||||
//
|
||||
// Deprecated: Do not import commands directly. They will be removed in a future release.
|
||||
func NewLogsCommand(dockerCLI command.Cli) *cobra.Command {
|
||||
return newLogsCommand(dockerCLI)
|
||||
}
|
||||
|
||||
func newLogsCommand(dockerCLI command.Cli) *cobra.Command {
|
||||
var opts logsOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
|
@ -33,12 +39,12 @@ func NewLogsCommand(dockerCli command.Cli) *cobra.Command {
|
|||
Args: cli.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
opts.container = args[0]
|
||||
return runLogs(cmd.Context(), dockerCli, &opts)
|
||||
return runLogs(cmd.Context(), dockerCLI, &opts)
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
"aliases": "docker container logs, docker logs",
|
||||
},
|
||||
ValidArgsFunction: completion.ContainerNames(dockerCli, true),
|
||||
ValidArgsFunction: completion.ContainerNames(dockerCLI, true),
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
|
|
|
@ -12,8 +12,8 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/docker/cli/cli/compose/loader"
|
||||
"github.com/docker/cli/internal/lazyregexp"
|
||||
"github.com/docker/cli/internal/volumespec"
|
||||
"github.com/docker/cli/opts"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
mounttypes "github.com/docker/docker/api/types/mount"
|
||||
|
@ -141,6 +141,16 @@ type containerOptions struct {
|
|||
Args []string
|
||||
}
|
||||
|
||||
// addPlatformFlag adds "--platform" to a set of flags for API version 1.32 and
|
||||
// later, using the value of "DOCKER_DEFAULT_PLATFORM" (if set) as a default.
|
||||
//
|
||||
// It should not be used for new uses, which may have a different API version
|
||||
// requirement.
|
||||
func addPlatformFlag(flags *pflag.FlagSet, target *string) {
|
||||
flags.StringVar(target, "platform", os.Getenv("DOCKER_DEFAULT_PLATFORM"), "Set platform if server is multi-platform capable")
|
||||
_ = flags.SetAnnotation("platform", "version", []string{"1.32"})
|
||||
}
|
||||
|
||||
// addFlags adds all command line flags that will be used by parse to the FlagSet
|
||||
func addFlags(flags *pflag.FlagSet) *containerOptions {
|
||||
copts := &containerOptions{
|
||||
|
@ -364,7 +374,7 @@ func parse(flags *pflag.FlagSet, copts *containerOptions, serverOS string) (*con
|
|||
volumes := copts.volumes.GetMap()
|
||||
// add any bind targets to the list of container volumes
|
||||
for bind := range copts.volumes.GetMap() {
|
||||
parsed, err := loader.ParseVolume(bind)
|
||||
parsed, err := volumespec.Parse(bind)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -17,7 +17,13 @@ type pauseOptions struct {
|
|||
}
|
||||
|
||||
// NewPauseCommand creates a new cobra.Command for `docker pause`
|
||||
func NewPauseCommand(dockerCli command.Cli) *cobra.Command {
|
||||
//
|
||||
// Deprecated: Do not import commands directly. They will be removed in a future release.
|
||||
func NewPauseCommand(dockerCLI command.Cli) *cobra.Command {
|
||||
return newPauseCommand(dockerCLI)
|
||||
}
|
||||
|
||||
func newPauseCommand(dockerCLI command.Cli) *cobra.Command {
|
||||
var opts pauseOptions
|
||||
|
||||
return &cobra.Command{
|
||||
|
@ -26,12 +32,12 @@ func NewPauseCommand(dockerCli command.Cli) *cobra.Command {
|
|||
Args: cli.RequiresMinArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
opts.containers = args
|
||||
return runPause(cmd.Context(), dockerCli, &opts)
|
||||
return runPause(cmd.Context(), dockerCLI, &opts)
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
"aliases": "docker container pause, docker pause",
|
||||
},
|
||||
ValidArgsFunction: completion.ContainerNames(dockerCli, false, func(ctr container.Summary) bool {
|
||||
ValidArgsFunction: completion.ContainerNames(dockerCLI, false, func(ctr container.Summary) bool {
|
||||
return ctr.State != container.StatePaused
|
||||
}),
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ func TestRunPause(t *testing.T) {
|
|||
},
|
||||
)
|
||||
|
||||
cmd := NewPauseCommand(cli)
|
||||
cmd := newPauseCommand(cli)
|
||||
cmd.SetOut(io.Discard)
|
||||
cmd.SetArgs([]string{"container-id-1", "container-id-2"})
|
||||
|
||||
|
@ -47,7 +47,7 @@ func TestRunPauseClientError(t *testing.T) {
|
|||
},
|
||||
)
|
||||
|
||||
cmd := NewPauseCommand(cli)
|
||||
cmd := newPauseCommand(cli)
|
||||
cmd.SetOut(io.Discard)
|
||||
cmd.SetErr(io.Discard)
|
||||
cmd.SetArgs([]string{"container-id-1", "container-id-2"})
|
||||
|
|
|
@ -24,7 +24,13 @@ type portOptions struct {
|
|||
}
|
||||
|
||||
// NewPortCommand creates a new cobra.Command for `docker port`
|
||||
//
|
||||
// Deprecated: Do not import commands directly. They will be removed in a future release.
|
||||
func NewPortCommand(dockerCli command.Cli) *cobra.Command {
|
||||
return newPortCommand(dockerCli)
|
||||
}
|
||||
|
||||
func newPortCommand(dockerCli command.Cli) *cobra.Command {
|
||||
var opts portOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
|
|
|
@ -66,7 +66,7 @@ func TestNewPortCommandOutput(t *testing.T) {
|
|||
return ci, nil
|
||||
},
|
||||
})
|
||||
cmd := NewPortCommand(cli)
|
||||
cmd := newPortCommand(cli)
|
||||
cmd.SetErr(io.Discard)
|
||||
cmd.SetArgs([]string{"some_container", tc.port})
|
||||
err := cmd.Execute()
|
||||
|
|
|
@ -20,7 +20,13 @@ type pruneOptions struct {
|
|||
}
|
||||
|
||||
// NewPruneCommand returns a new cobra prune command for containers
|
||||
func NewPruneCommand(dockerCli command.Cli) *cobra.Command {
|
||||
//
|
||||
// Deprecated: Do not import commands directly. They will be removed in a future release.
|
||||
func NewPruneCommand(dockerCLI command.Cli) *cobra.Command {
|
||||
return newPruneCommand(dockerCLI)
|
||||
}
|
||||
|
||||
func newPruneCommand(dockerCLI command.Cli) *cobra.Command {
|
||||
options := pruneOptions{filter: opts.NewFilterOpt()}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
|
@ -28,14 +34,14 @@ func NewPruneCommand(dockerCli command.Cli) *cobra.Command {
|
|||
Short: "Remove all stopped containers",
|
||||
Args: cli.NoArgs,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
spaceReclaimed, output, err := runPrune(cmd.Context(), dockerCli, options)
|
||||
spaceReclaimed, output, err := runPrune(cmd.Context(), dockerCLI, options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if output != "" {
|
||||
fmt.Fprintln(dockerCli.Out(), output)
|
||||
fmt.Fprintln(dockerCLI.Out(), output)
|
||||
}
|
||||
fmt.Fprintln(dockerCli.Out(), "Total reclaimed space:", units.HumanSize(float64(spaceReclaimed)))
|
||||
fmt.Fprintln(dockerCLI.Out(), "Total reclaimed space:", units.HumanSize(float64(spaceReclaimed)))
|
||||
return nil
|
||||
},
|
||||
Annotations: map[string]string{"version": "1.25"},
|
||||
|
|
|
@ -20,7 +20,7 @@ func TestContainerPrunePromptTermination(t *testing.T) {
|
|||
return container.PruneReport{}, errors.New("fakeClient containerPruneFunc should not be called")
|
||||
},
|
||||
})
|
||||
cmd := NewPruneCommand(cli)
|
||||
cmd := newPruneCommand(cli)
|
||||
cmd.SetArgs([]string{})
|
||||
cmd.SetOut(io.Discard)
|
||||
cmd.SetErr(io.Discard)
|
||||
|
|
|
@ -18,7 +18,13 @@ type renameOptions struct {
|
|||
}
|
||||
|
||||
// NewRenameCommand creates a new cobra.Command for `docker rename`
|
||||
func NewRenameCommand(dockerCli command.Cli) *cobra.Command {
|
||||
//
|
||||
// Deprecated: Do not import commands directly. They will be removed in a future release.
|
||||
func NewRenameCommand(dockerCLI command.Cli) *cobra.Command {
|
||||
return newRenameCommand(dockerCLI)
|
||||
}
|
||||
|
||||
func newRenameCommand(dockerCLI command.Cli) *cobra.Command {
|
||||
var opts renameOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
|
@ -28,12 +34,12 @@ func NewRenameCommand(dockerCli command.Cli) *cobra.Command {
|
|||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
opts.oldName = args[0]
|
||||
opts.newName = args[1]
|
||||
return runRename(cmd.Context(), dockerCli, &opts)
|
||||
return runRename(cmd.Context(), dockerCLI, &opts)
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
"aliases": "docker container rename, docker rename",
|
||||
},
|
||||
ValidArgsFunction: completion.ContainerNames(dockerCli, true),
|
||||
ValidArgsFunction: completion.ContainerNames(dockerCLI, true),
|
||||
}
|
||||
return cmd
|
||||
}
|
||||
|
|
|
@ -43,7 +43,7 @@ func TestRunRename(t *testing.T) {
|
|||
},
|
||||
})
|
||||
|
||||
cmd := NewRenameCommand(cli)
|
||||
cmd := newRenameCommand(cli)
|
||||
cmd.SetOut(io.Discard)
|
||||
cmd.SetErr(io.Discard)
|
||||
cmd.SetArgs([]string{tc.oldName, tc.newName})
|
||||
|
@ -66,7 +66,7 @@ func TestRunRenameClientError(t *testing.T) {
|
|||
},
|
||||
})
|
||||
|
||||
cmd := NewRenameCommand(cli)
|
||||
cmd := newRenameCommand(cli)
|
||||
cmd.SetOut(io.Discard)
|
||||
cmd.SetErr(io.Discard)
|
||||
cmd.SetArgs([]string{"oldName", "newName"})
|
||||
|
|
|
@ -21,7 +21,13 @@ type restartOptions struct {
|
|||
}
|
||||
|
||||
// NewRestartCommand creates a new cobra.Command for `docker restart`
|
||||
func NewRestartCommand(dockerCli command.Cli) *cobra.Command {
|
||||
//
|
||||
// Deprecated: Do not import commands directly. They will be removed in a future release.
|
||||
func NewRestartCommand(dockerCLI command.Cli) *cobra.Command {
|
||||
return newRestartCommand(dockerCLI)
|
||||
}
|
||||
|
||||
func newRestartCommand(dockerCLI command.Cli) *cobra.Command {
|
||||
var opts restartOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
|
@ -34,12 +40,12 @@ func NewRestartCommand(dockerCli command.Cli) *cobra.Command {
|
|||
}
|
||||
opts.containers = args
|
||||
opts.timeoutChanged = cmd.Flags().Changed("timeout") || cmd.Flags().Changed("time")
|
||||
return runRestart(cmd.Context(), dockerCli, &opts)
|
||||
return runRestart(cmd.Context(), dockerCLI, &opts)
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
"aliases": "docker container restart, docker restart",
|
||||
},
|
||||
ValidArgsFunction: completion.ContainerNames(dockerCli, true),
|
||||
ValidArgsFunction: completion.ContainerNames(dockerCLI, true),
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
|
|
|
@ -76,7 +76,7 @@ func TestRestart(t *testing.T) {
|
|||
},
|
||||
Version: "1.36",
|
||||
})
|
||||
cmd := NewRestartCommand(cli)
|
||||
cmd := newRestartCommand(cli)
|
||||
cmd.SetOut(io.Discard)
|
||||
cmd.SetErr(io.Discard)
|
||||
cmd.SetArgs(tc.args)
|
||||
|
|
|
@ -6,7 +6,7 @@ import (
|
|||
"fmt"
|
||||
"strings"
|
||||
|
||||
cerrdefs "github.com/containerd/errdefs"
|
||||
"github.com/containerd/errdefs"
|
||||
"github.com/docker/cli/cli"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/cli/cli/command/completion"
|
||||
|
@ -23,7 +23,13 @@ type rmOptions struct {
|
|||
}
|
||||
|
||||
// NewRmCommand creates a new cobra.Command for `docker rm`
|
||||
func NewRmCommand(dockerCli command.Cli) *cobra.Command {
|
||||
//
|
||||
// Deprecated: Do not import commands directly. They will be removed in a future release.
|
||||
func NewRmCommand(dockerCLI command.Cli) *cobra.Command {
|
||||
return newRmCommand(dockerCLI)
|
||||
}
|
||||
|
||||
func newRmCommand(dockerCLI command.Cli) *cobra.Command {
|
||||
var opts rmOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
|
@ -32,12 +38,12 @@ func NewRmCommand(dockerCli command.Cli) *cobra.Command {
|
|||
Args: cli.RequiresMinArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
opts.containers = args
|
||||
return runRm(cmd.Context(), dockerCli, &opts)
|
||||
return runRm(cmd.Context(), dockerCLI, &opts)
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
"aliases": "docker container rm, docker container remove, docker rm",
|
||||
},
|
||||
ValidArgsFunction: completion.ContainerNames(dockerCli, true, func(ctr container.Summary) bool {
|
||||
ValidArgsFunction: completion.ContainerNames(dockerCLI, true, func(ctr container.Summary) bool {
|
||||
return opts.force || ctr.State == container.StateExited || ctr.State == container.StateCreated
|
||||
}),
|
||||
}
|
||||
|
@ -53,7 +59,7 @@ func NewRmCommand(dockerCli command.Cli) *cobra.Command {
|
|||
// top-level "docker rm", it also adds a "remove" alias to support
|
||||
// "docker container remove" in addition to "docker container rm".
|
||||
func newRemoveCommand(dockerCli command.Cli) *cobra.Command {
|
||||
cmd := *NewRmCommand(dockerCli)
|
||||
cmd := *newRmCommand(dockerCli)
|
||||
cmd.Aliases = []string{"rm", "remove"}
|
||||
return &cmd
|
||||
}
|
||||
|
@ -75,7 +81,7 @@ func runRm(ctx context.Context, dockerCLI command.Cli, opts *rmOptions) error {
|
|||
var errs []error
|
||||
for _, name := range opts.containers {
|
||||
if err := <-errChan; err != nil {
|
||||
if opts.force && cerrdefs.IsNotFound(err) {
|
||||
if opts.force && errdefs.IsNotFound(err) {
|
||||
_, _ = fmt.Fprintln(dockerCLI.Err(), err)
|
||||
continue
|
||||
}
|
||||
|
|
|
@ -41,7 +41,7 @@ func TestRemoveForce(t *testing.T) {
|
|||
},
|
||||
Version: "1.36",
|
||||
})
|
||||
cmd := NewRmCommand(cli)
|
||||
cmd := newRmCommand(cli)
|
||||
cmd.SetOut(io.Discard)
|
||||
cmd.SetErr(io.Discard)
|
||||
cmd.SetArgs(tc.args)
|
||||
|
|
|
@ -28,7 +28,13 @@ type runOptions struct {
|
|||
}
|
||||
|
||||
// NewRunCommand create a new `docker run` command
|
||||
func NewRunCommand(dockerCli command.Cli) *cobra.Command {
|
||||
//
|
||||
// Deprecated: Do not import commands directly. They will be removed in a future release.
|
||||
func NewRunCommand(dockerCLI command.Cli) *cobra.Command {
|
||||
return newRunCommand(dockerCLI)
|
||||
}
|
||||
|
||||
func newRunCommand(dockerCLI command.Cli) *cobra.Command {
|
||||
var options runOptions
|
||||
var copts *containerOptions
|
||||
|
||||
|
@ -41,9 +47,9 @@ func NewRunCommand(dockerCli command.Cli) *cobra.Command {
|
|||
if len(args) > 1 {
|
||||
copts.Args = args[1:]
|
||||
}
|
||||
return runRun(cmd.Context(), dockerCli, cmd.Flags(), &options, copts)
|
||||
return runRun(cmd.Context(), dockerCLI, cmd.Flags(), &options, copts)
|
||||
},
|
||||
ValidArgsFunction: completion.ImageNames(dockerCli, 1),
|
||||
ValidArgsFunction: completion.ImageNames(dockerCLI, 1),
|
||||
Annotations: map[string]string{
|
||||
"category-top": "1",
|
||||
"aliases": "docker container run, docker run",
|
||||
|
@ -66,12 +72,13 @@ func NewRunCommand(dockerCli command.Cli) *cobra.Command {
|
|||
// with hostname
|
||||
flags.Bool("help", false, "Print usage")
|
||||
|
||||
command.AddPlatformFlag(flags, &options.platform)
|
||||
command.AddTrustVerificationFlags(flags, &options.untrusted, dockerCli.ContentTrustEnabled())
|
||||
// TODO(thaJeztah): consider adding platform as "image create option" on containerOptions
|
||||
addPlatformFlag(flags, &options.platform)
|
||||
flags.BoolVar(&options.untrusted, "disable-content-trust", !dockerCLI.ContentTrustEnabled(), "Skip image verification")
|
||||
copts = addFlags(flags)
|
||||
|
||||
_ = cmd.RegisterFlagCompletionFunc("detach-keys", completeDetachKeys)
|
||||
addCompletions(cmd, dockerCli)
|
||||
addCompletions(cmd, dockerCLI)
|
||||
|
||||
flags.VisitAll(func(flag *pflag.Flag) {
|
||||
// Set a default completion function if none was set. We don't look
|
||||
|
|
|
@ -2,9 +2,7 @@ package container
|
|||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"syscall"
|
||||
|
@ -20,7 +18,8 @@ import (
|
|||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/image"
|
||||
"github.com/docker/docker/api/types/network"
|
||||
"github.com/docker/docker/pkg/jsonmessage"
|
||||
"github.com/docker/docker/pkg/progress"
|
||||
"github.com/docker/docker/pkg/streamformatter"
|
||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/spf13/pflag"
|
||||
"gotest.tools/v3/assert"
|
||||
|
@ -40,7 +39,7 @@ func TestRunValidateFlags(t *testing.T) {
|
|||
},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
cmd := NewRunCommand(test.NewFakeCli(&fakeClient{}))
|
||||
cmd := newRunCommand(test.NewFakeCli(&fakeClient{}))
|
||||
cmd.SetOut(io.Discard)
|
||||
cmd.SetErr(io.Discard)
|
||||
cmd.SetArgs(tc.args)
|
||||
|
@ -64,7 +63,7 @@ func TestRunLabel(t *testing.T) {
|
|||
},
|
||||
Version: "1.36",
|
||||
})
|
||||
cmd := NewRunCommand(fakeCLI)
|
||||
cmd := newRunCommand(fakeCLI)
|
||||
cmd.SetArgs([]string{"--detach=true", "--label", "foo", "busybox"})
|
||||
assert.NilError(t, cmd.Execute())
|
||||
}
|
||||
|
@ -111,7 +110,7 @@ func TestRunAttach(t *testing.T) {
|
|||
fc.SetIn(streams.NewIn(tty))
|
||||
})
|
||||
|
||||
cmd := NewRunCommand(fakeCLI)
|
||||
cmd := newRunCommand(fakeCLI)
|
||||
cmd.SetArgs([]string{"-it", "busybox"})
|
||||
cmd.SilenceUsage = true
|
||||
cmdErrC := make(chan error, 1)
|
||||
|
@ -188,7 +187,7 @@ func TestRunAttachTermination(t *testing.T) {
|
|||
fc.SetIn(streams.NewIn(tty))
|
||||
})
|
||||
|
||||
cmd := NewRunCommand(fakeCLI)
|
||||
cmd := newRunCommand(fakeCLI)
|
||||
cmd.SetArgs([]string{"-it", "busybox"})
|
||||
cmd.SilenceUsage = true
|
||||
cmdErrC := make(chan error, 1)
|
||||
|
@ -242,23 +241,19 @@ func TestRunPullTermination(t *testing.T) {
|
|||
_ = server.Close()
|
||||
})
|
||||
go func() {
|
||||
enc := json.NewEncoder(server)
|
||||
id := test.RandomID()[:12] // short-ID
|
||||
progressOutput := streamformatter.NewJSONProgressOutput(server, true)
|
||||
for i := 0; i < 100; i++ {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
assert.NilError(t, server.Close(), "failed to close imageCreateFunc server")
|
||||
return
|
||||
default:
|
||||
assert.NilError(t, enc.Encode(jsonmessage.JSONMessage{
|
||||
Status: "Downloading",
|
||||
ID: fmt.Sprintf("id-%d", i),
|
||||
TimeNano: time.Now().UnixNano(),
|
||||
Time: time.Now().Unix(),
|
||||
Progress: &jsonmessage.JSONProgress{
|
||||
Current: int64(i),
|
||||
Total: 100,
|
||||
Start: 0,
|
||||
},
|
||||
assert.NilError(t, progressOutput.WriteProgress(progress.Progress{
|
||||
ID: id,
|
||||
Message: "Downloading",
|
||||
Current: int64(i),
|
||||
Total: 100,
|
||||
}))
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
}
|
||||
|
@ -270,7 +265,7 @@ func TestRunPullTermination(t *testing.T) {
|
|||
Version: "1.30",
|
||||
})
|
||||
|
||||
cmd := NewRunCommand(fakeCLI)
|
||||
cmd := newRunCommand(fakeCLI)
|
||||
cmd.SetOut(io.Discard)
|
||||
cmd.SetErr(io.Discard)
|
||||
cmd.SetArgs([]string{"--pull", "always", "foobar:latest"})
|
||||
|
@ -339,7 +334,7 @@ func TestRunCommandWithContentTrustErrors(t *testing.T) {
|
|||
},
|
||||
}, test.EnableContentTrust)
|
||||
fakeCLI.SetNotaryClient(tc.notaryFunc)
|
||||
cmd := NewRunCommand(fakeCLI)
|
||||
cmd := newRunCommand(fakeCLI)
|
||||
cmd.SetArgs(tc.args)
|
||||
cmd.SetOut(io.Discard)
|
||||
cmd.SetErr(io.Discard)
|
||||
|
|
|
@ -28,7 +28,13 @@ type StartOptions struct {
|
|||
}
|
||||
|
||||
// NewStartCommand creates a new cobra.Command for `docker start`
|
||||
//
|
||||
// Deprecated: Do not import commands directly. They will be removed in a future release.
|
||||
func NewStartCommand(dockerCli command.Cli) *cobra.Command {
|
||||
return newStartCommand(dockerCli)
|
||||
}
|
||||
|
||||
func newStartCommand(dockerCli command.Cli) *cobra.Command {
|
||||
var opts StartOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
|
|
|
@ -64,7 +64,13 @@ type StatsOptions struct {
|
|||
}
|
||||
|
||||
// NewStatsCommand creates a new [cobra.Command] for "docker stats".
|
||||
//
|
||||
// Deprecated: Do not import commands directly. They will be removed in a future release.
|
||||
func NewStatsCommand(dockerCLI command.Cli) *cobra.Command {
|
||||
return newStatsCommand(dockerCLI)
|
||||
}
|
||||
|
||||
func newStatsCommand(dockerCLI command.Cli) *cobra.Command {
|
||||
options := StatsOptions{}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
|
|
|
@ -21,7 +21,13 @@ type stopOptions struct {
|
|||
}
|
||||
|
||||
// NewStopCommand creates a new cobra.Command for `docker stop`
|
||||
func NewStopCommand(dockerCli command.Cli) *cobra.Command {
|
||||
//
|
||||
// Deprecated: Do not import commands directly. They will be removed in a future release.
|
||||
func NewStopCommand(dockerCLI command.Cli) *cobra.Command {
|
||||
return newStopCommand(dockerCLI)
|
||||
}
|
||||
|
||||
func newStopCommand(dockerCLI command.Cli) *cobra.Command {
|
||||
var opts stopOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
|
@ -34,12 +40,12 @@ func NewStopCommand(dockerCli command.Cli) *cobra.Command {
|
|||
}
|
||||
opts.containers = args
|
||||
opts.timeoutChanged = cmd.Flags().Changed("timeout") || cmd.Flags().Changed("time")
|
||||
return runStop(cmd.Context(), dockerCli, &opts)
|
||||
return runStop(cmd.Context(), dockerCLI, &opts)
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
"aliases": "docker container stop, docker stop",
|
||||
},
|
||||
ValidArgsFunction: completion.ContainerNames(dockerCli, false),
|
||||
ValidArgsFunction: completion.ContainerNames(dockerCLI, false),
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
|
|
|
@ -77,7 +77,7 @@ func TestStop(t *testing.T) {
|
|||
},
|
||||
Version: "1.36",
|
||||
})
|
||||
cmd := NewStopCommand(cli)
|
||||
cmd := newStopCommand(cli)
|
||||
cmd.SetOut(io.Discard)
|
||||
cmd.SetErr(io.Discard)
|
||||
cmd.SetArgs(tc.args)
|
||||
|
|
|
@ -19,7 +19,13 @@ type topOptions struct {
|
|||
}
|
||||
|
||||
// NewTopCommand creates a new cobra.Command for `docker top`
|
||||
func NewTopCommand(dockerCli command.Cli) *cobra.Command {
|
||||
//
|
||||
// Deprecated: Do not import commands directly. They will be removed in a future release.
|
||||
func NewTopCommand(dockerCLI command.Cli) *cobra.Command {
|
||||
return newTopCommand(dockerCLI)
|
||||
}
|
||||
|
||||
func newTopCommand(dockerCLI command.Cli) *cobra.Command {
|
||||
var opts topOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
|
@ -29,12 +35,12 @@ func NewTopCommand(dockerCli command.Cli) *cobra.Command {
|
|||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
opts.container = args[0]
|
||||
opts.args = args[1:]
|
||||
return runTop(cmd.Context(), dockerCli, &opts)
|
||||
return runTop(cmd.Context(), dockerCLI, &opts)
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
"aliases": "docker container top, docker top",
|
||||
},
|
||||
ValidArgsFunction: completion.ContainerNames(dockerCli, false),
|
||||
ValidArgsFunction: completion.ContainerNames(dockerCLI, false),
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
|
|
|
@ -17,7 +17,13 @@ type unpauseOptions struct {
|
|||
}
|
||||
|
||||
// NewUnpauseCommand creates a new cobra.Command for `docker unpause`
|
||||
//
|
||||
// Deprecated: Do not import commands directly. They will be removed in a future release.
|
||||
func NewUnpauseCommand(dockerCli command.Cli) *cobra.Command {
|
||||
return newUnpauseCommand(dockerCli)
|
||||
}
|
||||
|
||||
func newUnpauseCommand(dockerCli command.Cli) *cobra.Command {
|
||||
var opts unpauseOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
|
|
|
@ -37,7 +37,13 @@ type updateOptions struct {
|
|||
}
|
||||
|
||||
// NewUpdateCommand creates a new cobra.Command for `docker update`
|
||||
func NewUpdateCommand(dockerCli command.Cli) *cobra.Command {
|
||||
//
|
||||
// Deprecated: Do not import commands directly. They will be removed in a future release.
|
||||
func NewUpdateCommand(dockerCLI command.Cli) *cobra.Command {
|
||||
return newUpdateCommand(dockerCLI)
|
||||
}
|
||||
|
||||
func newUpdateCommand(dockerCLI command.Cli) *cobra.Command {
|
||||
var options updateOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
|
@ -47,12 +53,12 @@ func NewUpdateCommand(dockerCli command.Cli) *cobra.Command {
|
|||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
options.containers = args
|
||||
options.nFlag = cmd.Flags().NFlag()
|
||||
return runUpdate(cmd.Context(), dockerCli, &options)
|
||||
return runUpdate(cmd.Context(), dockerCLI, &options)
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
"aliases": "docker container update, docker update",
|
||||
},
|
||||
ValidArgsFunction: completion.ContainerNames(dockerCli, true),
|
||||
ValidArgsFunction: completion.ContainerNames(dockerCLI, true),
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
|
|
|
@ -16,7 +16,13 @@ type waitOptions struct {
|
|||
}
|
||||
|
||||
// NewWaitCommand creates a new cobra.Command for `docker wait`
|
||||
func NewWaitCommand(dockerCli command.Cli) *cobra.Command {
|
||||
//
|
||||
// Deprecated: Do not import commands directly. They will be removed in a future release.
|
||||
func NewWaitCommand(dockerCLI command.Cli) *cobra.Command {
|
||||
return newWaitCommand(dockerCLI)
|
||||
}
|
||||
|
||||
func newWaitCommand(dockerCLI command.Cli) *cobra.Command {
|
||||
var opts waitOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
|
@ -25,12 +31,12 @@ func NewWaitCommand(dockerCli command.Cli) *cobra.Command {
|
|||
Args: cli.RequiresMinArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
opts.containers = args
|
||||
return runWait(cmd.Context(), dockerCli, &opts)
|
||||
return runWait(cmd.Context(), dockerCLI, &opts)
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
"aliases": "docker container wait, docker wait",
|
||||
},
|
||||
ValidArgsFunction: completion.ContainerNames(dockerCli, false),
|
||||
ValidArgsFunction: completion.ContainerNames(dockerCLI, false),
|
||||
}
|
||||
|
||||
return cmd
|
||||
|
|
|
@ -7,23 +7,30 @@ import (
|
|||
)
|
||||
|
||||
// NewContextCommand returns the context cli subcommand
|
||||
func NewContextCommand(dockerCli command.Cli) *cobra.Command {
|
||||
//
|
||||
// Deprecated: Do not import commands directly. They will be removed in a future release.
|
||||
func NewContextCommand(dockerCLI command.Cli) *cobra.Command {
|
||||
return newContextCommand(dockerCLI)
|
||||
}
|
||||
|
||||
// newContextCommand returns the context cli subcommand
|
||||
func newContextCommand(dockerCLI command.Cli) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "context",
|
||||
Short: "Manage contexts",
|
||||
Args: cli.NoArgs,
|
||||
RunE: command.ShowHelp(dockerCli.Err()),
|
||||
RunE: command.ShowHelp(dockerCLI.Err()),
|
||||
}
|
||||
cmd.AddCommand(
|
||||
newCreateCommand(dockerCli),
|
||||
newListCommand(dockerCli),
|
||||
newUseCommand(dockerCli),
|
||||
newExportCommand(dockerCli),
|
||||
newImportCommand(dockerCli),
|
||||
newRemoveCommand(dockerCli),
|
||||
newUpdateCommand(dockerCli),
|
||||
newInspectCommand(dockerCli),
|
||||
newShowCommand(dockerCli),
|
||||
newCreateCommand(dockerCLI),
|
||||
newListCommand(dockerCLI),
|
||||
newUseCommand(dockerCLI),
|
||||
newExportCommand(dockerCLI),
|
||||
newImportCommand(dockerCLI),
|
||||
newRemoveCommand(dockerCLI),
|
||||
newUpdateCommand(dockerCLI),
|
||||
newInspectCommand(dockerCLI),
|
||||
newShowCommand(dockerCLI),
|
||||
)
|
||||
return cmd
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
|
||||
cerrdefs "github.com/containerd/errdefs"
|
||||
"github.com/containerd/errdefs"
|
||||
"github.com/docker/cli/cli"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/cli/cli/command/completion"
|
||||
|
@ -122,7 +122,7 @@ func checkContextNameForCreation(s store.Reader, name string) error {
|
|||
if err := store.ValidateContextName(name); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := s.GetMetadata(name); !cerrdefs.IsNotFound(err) {
|
||||
if _, err := s.GetMetadata(name); !errdefs.IsNotFound(err) {
|
||||
if err != nil {
|
||||
return fmt.Errorf("error while getting existing contexts: %w", err)
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ import (
|
|||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
cerrdefs "github.com/containerd/errdefs"
|
||||
"github.com/containerd/errdefs"
|
||||
"github.com/docker/cli/cli/config"
|
||||
"github.com/docker/cli/cli/config/configfile"
|
||||
"gotest.tools/v3/assert"
|
||||
|
@ -18,7 +18,7 @@ func TestRemove(t *testing.T) {
|
|||
_, err := cli.ContextStore().GetMetadata("current")
|
||||
assert.NilError(t, err)
|
||||
_, err = cli.ContextStore().GetMetadata("other")
|
||||
assert.Check(t, is.ErrorType(err, cerrdefs.IsNotFound))
|
||||
assert.Check(t, is.ErrorType(err, errdefs.IsNotFound))
|
||||
}
|
||||
|
||||
func TestRemoveNotAContext(t *testing.T) {
|
||||
|
|
|
@ -9,7 +9,7 @@ import (
|
|||
"runtime"
|
||||
"testing"
|
||||
|
||||
cerrdefs "github.com/containerd/errdefs"
|
||||
"github.com/containerd/errdefs"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/cli/cli/config"
|
||||
"github.com/docker/cli/cli/config/configfile"
|
||||
|
@ -47,7 +47,7 @@ func TestUse(t *testing.T) {
|
|||
func TestUseNoExist(t *testing.T) {
|
||||
cli := makeFakeCli(t)
|
||||
err := newUseCommand(cli).RunE(nil, []string{"test"})
|
||||
assert.Check(t, is.ErrorType(err, cerrdefs.IsNotFound))
|
||||
assert.Check(t, is.ErrorType(err, errdefs.IsNotFound))
|
||||
}
|
||||
|
||||
// TestUseDefaultWithoutConfigFile verifies that the CLI does not create
|
||||
|
|
|
@ -7,7 +7,7 @@ import (
|
|||
"crypto/rand"
|
||||
"testing"
|
||||
|
||||
cerrdefs "github.com/containerd/errdefs"
|
||||
"github.com/containerd/errdefs"
|
||||
"github.com/docker/cli/cli/config/configfile"
|
||||
"github.com/docker/cli/cli/context/docker"
|
||||
"github.com/docker/cli/cli/context/store"
|
||||
|
@ -158,7 +158,7 @@ func TestErrCreateDefault(t *testing.T) {
|
|||
Metadata: testContext{Bar: "baz"},
|
||||
Name: "default",
|
||||
})
|
||||
assert.Check(t, is.ErrorType(err, cerrdefs.IsInvalidArgument))
|
||||
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidArgument))
|
||||
assert.Error(t, err, "default context cannot be created nor updated")
|
||||
}
|
||||
|
||||
|
@ -166,7 +166,7 @@ func TestErrRemoveDefault(t *testing.T) {
|
|||
meta := testDefaultMetadata()
|
||||
s := testStore(t, meta, store.ContextTLSData{})
|
||||
err := s.Remove("default")
|
||||
assert.Check(t, is.ErrorType(err, cerrdefs.IsInvalidArgument))
|
||||
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidArgument))
|
||||
assert.Error(t, err, "default context cannot be removed")
|
||||
}
|
||||
|
||||
|
@ -174,5 +174,5 @@ func TestErrTLSDataError(t *testing.T) {
|
|||
meta := testDefaultMetadata()
|
||||
s := testStore(t, meta, store.ContextTLSData{})
|
||||
_, err := s.GetTLSData("default", "noop", "noop")
|
||||
assert.Check(t, is.ErrorType(err, cerrdefs.IsNotFound))
|
||||
assert.Check(t, is.ErrorType(err, errdefs.IsNotFound))
|
||||
}
|
||||
|
|
|
@ -12,13 +12,13 @@ import (
|
|||
)
|
||||
|
||||
func TestResolveError(t *testing.T) {
|
||||
cli := &fakeClient{
|
||||
apiClient := &fakeClient{
|
||||
nodeInspectFunc: func(nodeID string) (swarm.Node, []byte, error) {
|
||||
return swarm.Node{}, []byte{}, errors.New("error inspecting node")
|
||||
},
|
||||
}
|
||||
|
||||
idResolver := New(cli, false)
|
||||
idResolver := New(apiClient, false)
|
||||
_, err := idResolver.Resolve(context.Background(), struct{}{}, "nodeID")
|
||||
|
||||
assert.Error(t, err, "unsupported type")
|
||||
|
@ -26,7 +26,7 @@ func TestResolveError(t *testing.T) {
|
|||
|
||||
func TestResolveWithNoResolveOption(t *testing.T) {
|
||||
resolved := false
|
||||
cli := &fakeClient{
|
||||
apiClient := &fakeClient{
|
||||
nodeInspectFunc: func(nodeID string) (swarm.Node, []byte, error) {
|
||||
resolved = true
|
||||
return swarm.Node{}, []byte{}, nil
|
||||
|
@ -37,7 +37,7 @@ func TestResolveWithNoResolveOption(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
idResolver := New(cli, true)
|
||||
idResolver := New(apiClient, true)
|
||||
id, err := idResolver.Resolve(context.Background(), swarm.Node{}, "nodeID")
|
||||
|
||||
assert.NilError(t, err)
|
||||
|
@ -47,14 +47,14 @@ func TestResolveWithNoResolveOption(t *testing.T) {
|
|||
|
||||
func TestResolveWithCache(t *testing.T) {
|
||||
inspectCounter := 0
|
||||
cli := &fakeClient{
|
||||
apiClient := &fakeClient{
|
||||
nodeInspectFunc: func(nodeID string) (swarm.Node, []byte, error) {
|
||||
inspectCounter++
|
||||
return *builders.Node(builders.NodeName("node-foo")), []byte{}, nil
|
||||
},
|
||||
}
|
||||
|
||||
idResolver := New(cli, false)
|
||||
idResolver := New(apiClient, false)
|
||||
|
||||
ctx := context.Background()
|
||||
for i := 0; i < 2; i++ {
|
||||
|
@ -97,10 +97,10 @@ func TestResolveNode(t *testing.T) {
|
|||
|
||||
ctx := context.Background()
|
||||
for _, tc := range testCases {
|
||||
cli := &fakeClient{
|
||||
apiClient := &fakeClient{
|
||||
nodeInspectFunc: tc.nodeInspectFunc,
|
||||
}
|
||||
idResolver := New(cli, false)
|
||||
idResolver := New(apiClient, false)
|
||||
id, err := idResolver.Resolve(ctx, swarm.Node{}, tc.nodeID)
|
||||
|
||||
assert.NilError(t, err)
|
||||
|
@ -132,10 +132,10 @@ func TestResolveService(t *testing.T) {
|
|||
|
||||
ctx := context.Background()
|
||||
for _, tc := range testCases {
|
||||
cli := &fakeClient{
|
||||
apiClient := &fakeClient{
|
||||
serviceInspectFunc: tc.serviceInspectFunc,
|
||||
}
|
||||
idResolver := New(cli, false)
|
||||
idResolver := New(apiClient, false)
|
||||
id, err := idResolver.Resolve(ctx, swarm.Service{}, tc.serviceID)
|
||||
|
||||
assert.NilError(t, err)
|
||||
|
|
|
@ -28,7 +28,6 @@ import (
|
|||
buildtypes "github.com/docker/docker/api/types/build"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
registrytypes "github.com/docker/docker/api/types/registry"
|
||||
"github.com/docker/docker/builder/remotecontext/urlutil"
|
||||
"github.com/docker/docker/pkg/progress"
|
||||
"github.com/docker/docker/pkg/streamformatter"
|
||||
"github.com/moby/go-archive"
|
||||
|
@ -76,12 +75,6 @@ func (o buildOptions) dockerfileFromStdin() bool {
|
|||
return o.dockerfileName == "-"
|
||||
}
|
||||
|
||||
// contextFromStdin returns true when the user specified that the build context
|
||||
// should be read from stdin
|
||||
func (o buildOptions) contextFromStdin() bool {
|
||||
return o.context == "-"
|
||||
}
|
||||
|
||||
func newBuildOptions() buildOptions {
|
||||
ulimits := make(map[string]*container.Ulimit)
|
||||
return buildOptions{
|
||||
|
@ -94,7 +87,14 @@ func newBuildOptions() buildOptions {
|
|||
}
|
||||
|
||||
// NewBuildCommand creates a new `docker build` command
|
||||
func NewBuildCommand(dockerCli command.Cli) *cobra.Command {
|
||||
//
|
||||
// Deprecated: Do not import commands directly. They will be removed in a future release.
|
||||
func NewBuildCommand(dockerCLI command.Cli) *cobra.Command {
|
||||
return newBuildCommand(dockerCLI)
|
||||
}
|
||||
|
||||
// newBuildCommand creates a new `docker build` command
|
||||
func newBuildCommand(dockerCli command.Cli) *cobra.Command {
|
||||
options := newBuildOptions()
|
||||
|
||||
cmd := &cobra.Command{
|
||||
|
@ -152,7 +152,7 @@ func NewBuildCommand(dockerCli command.Cli) *cobra.Command {
|
|||
flags.SetAnnotation("target", annotation.ExternalURL, []string{"https://docs.docker.com/reference/cli/docker/buildx/build/#target"})
|
||||
flags.StringVar(&options.imageIDFile, "iidfile", "", "Write the image ID to the file")
|
||||
|
||||
command.AddTrustVerificationFlags(flags, &options.untrusted, dockerCli.ContentTrustEnabled())
|
||||
flags.BoolVar(&options.untrusted, "disable-content-trust", !dockerCli.ContentTrustEnabled(), "Skip image verification")
|
||||
|
||||
flags.StringVar(&options.platform, "platform", os.Getenv("DOCKER_DEFAULT_PLATFORM"), "Set platform if server is multi-platform capable")
|
||||
flags.SetAnnotation("platform", "version", []string{"1.38"})
|
||||
|
@ -189,21 +189,24 @@ func runBuild(ctx context.Context, dockerCli command.Cli, options buildOptions)
|
|||
buildCtx io.ReadCloser
|
||||
dockerfileCtx io.ReadCloser
|
||||
contextDir string
|
||||
tempDir string
|
||||
relDockerfile string
|
||||
progBuff io.Writer
|
||||
buildBuff io.Writer
|
||||
remote string
|
||||
)
|
||||
|
||||
contextType, err := build.DetectContextType(options.context)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if options.dockerfileFromStdin() {
|
||||
if options.contextFromStdin() {
|
||||
if contextType == build.ContextTypeStdin {
|
||||
return errors.New("invalid argument: can't use stdin for both build context and dockerfile")
|
||||
}
|
||||
dockerfileCtx = dockerCli.In()
|
||||
}
|
||||
|
||||
specifiedContext := options.context
|
||||
progBuff = dockerCli.Out()
|
||||
buildBuff = dockerCli.Out()
|
||||
if options.quiet {
|
||||
|
@ -217,13 +220,19 @@ func runBuild(ctx context.Context, dockerCli command.Cli, options buildOptions)
|
|||
}
|
||||
}
|
||||
|
||||
switch {
|
||||
case options.contextFromStdin():
|
||||
switch contextType {
|
||||
case build.ContextTypeStdin:
|
||||
// buildCtx is tar archive. if stdin was dockerfile then it is wrapped
|
||||
buildCtx, relDockerfile, err = build.GetContextFromReader(dockerCli.In(), options.dockerfileName)
|
||||
case isLocalDir(specifiedContext):
|
||||
contextDir, relDockerfile, err = build.GetContextFromLocalDir(specifiedContext, options.dockerfileName)
|
||||
if err == nil && strings.HasPrefix(relDockerfile, ".."+string(filepath.Separator)) {
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to prepare context from STDIN: %w", err)
|
||||
}
|
||||
case build.ContextTypeLocal:
|
||||
contextDir, relDockerfile, err = build.GetContextFromLocalDir(options.context, options.dockerfileName)
|
||||
if err != nil {
|
||||
return errors.Errorf("unable to prepare context: %s", err)
|
||||
}
|
||||
if strings.HasPrefix(relDockerfile, ".."+string(filepath.Separator)) {
|
||||
// Dockerfile is outside of build-context; read the Dockerfile and pass it as dockerfileCtx
|
||||
dockerfileCtx, err = os.Open(options.dockerfileName)
|
||||
if err != nil {
|
||||
|
@ -231,24 +240,23 @@ func runBuild(ctx context.Context, dockerCli command.Cli, options buildOptions)
|
|||
}
|
||||
defer dockerfileCtx.Close()
|
||||
}
|
||||
case urlutil.IsGitURL(specifiedContext):
|
||||
tempDir, relDockerfile, err = build.GetContextFromGitURL(specifiedContext, options.dockerfileName)
|
||||
case urlutil.IsURL(specifiedContext):
|
||||
buildCtx, relDockerfile, err = build.GetContextFromURL(progBuff, specifiedContext, options.dockerfileName)
|
||||
default:
|
||||
return errors.Errorf("unable to prepare context: path %q not found", specifiedContext)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
if options.quiet && urlutil.IsURL(specifiedContext) {
|
||||
case build.ContextTypeGit:
|
||||
var tempDir string
|
||||
tempDir, relDockerfile, err = build.GetContextFromGitURL(options.context, options.dockerfileName)
|
||||
if err != nil {
|
||||
return errors.Errorf("unable to prepare context: %s", err)
|
||||
}
|
||||
defer func() {
|
||||
_ = os.RemoveAll(tempDir)
|
||||
}()
|
||||
contextDir = tempDir
|
||||
case build.ContextTypeRemote:
|
||||
buildCtx, relDockerfile, err = build.GetContextFromURL(progBuff, options.context, options.dockerfileName)
|
||||
if err != nil && options.quiet {
|
||||
_, _ = fmt.Fprintln(dockerCli.Err(), progBuff)
|
||||
}
|
||||
return errors.Errorf("unable to prepare context: %s", err)
|
||||
}
|
||||
|
||||
if tempDir != "" {
|
||||
defer os.RemoveAll(tempDir)
|
||||
contextDir = tempDir
|
||||
default:
|
||||
return errors.Errorf("unable to prepare context: path %q not found", options.context)
|
||||
}
|
||||
|
||||
// read from a directory into tar archive
|
||||
|
@ -415,11 +423,6 @@ func runBuild(ctx context.Context, dockerCli command.Cli, options buildOptions)
|
|||
return nil
|
||||
}
|
||||
|
||||
func isLocalDir(c string) bool {
|
||||
_, err := os.Stat(c)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
type translatorFunc func(context.Context, reference.NamedTagged) (reference.Canonical, error)
|
||||
|
||||
// validateTag checks if the given image name can be resolved.
|
||||
|
|
|
@ -16,7 +16,7 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/builder/remotecontext/git"
|
||||
"github.com/docker/cli/cli/command/image/build/internal/git"
|
||||
"github.com/docker/docker/pkg/progress"
|
||||
"github.com/docker/docker/pkg/streamformatter"
|
||||
"github.com/moby/go-archive"
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
package build
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/docker/cli/cli/command/image/build/internal/urlutil"
|
||||
)
|
||||
|
||||
// ContextType describes the type (source) of build-context specified.
|
||||
type ContextType string
|
||||
|
||||
const (
|
||||
ContextTypeStdin ContextType = "stdin" // ContextTypeStdin indicates that the build-context is a TAR archive passed through STDIN.
|
||||
ContextTypeLocal ContextType = "local" // ContextTypeLocal indicates that the build-context is a local directory.
|
||||
ContextTypeRemote ContextType = "remote" // ContextTypeRemote indicates that the build-context is a remote URL.
|
||||
ContextTypeGit ContextType = "git" // ContextTypeGit indicates that the build-context is a GIT URL.
|
||||
)
|
||||
|
||||
// DetectContextType detects the type (source) of the build-context.
|
||||
func DetectContextType(specifiedContext string) (ContextType, error) {
|
||||
switch {
|
||||
case specifiedContext == "-":
|
||||
return ContextTypeStdin, nil
|
||||
case isLocalDir(specifiedContext):
|
||||
return ContextTypeLocal, nil
|
||||
case urlutil.IsGitURL(specifiedContext):
|
||||
return ContextTypeGit, nil
|
||||
case urlutil.IsURL(specifiedContext):
|
||||
return ContextTypeRemote, nil
|
||||
default:
|
||||
return "", fmt.Errorf("unable to prepare context: path %q not found", specifiedContext)
|
||||
}
|
||||
}
|
||||
|
||||
func isLocalDir(c string) bool {
|
||||
_, err := os.Stat(c)
|
||||
return err == nil
|
||||
}
|
|
@ -148,6 +148,9 @@ func supportsShallowClone(remoteURL string) bool {
|
|||
|
||||
// Try a HEAD request and fallback to a Get request on error
|
||||
res, err := http.Head(serviceURL) // #nosec G107
|
||||
if err == nil {
|
||||
_ = res.Body.Close()
|
||||
}
|
||||
if err != nil || res.StatusCode != http.StatusOK {
|
||||
res, err = http.Get(serviceURL) // #nosec G107
|
||||
if err == nil {
|
|
@ -0,0 +1,382 @@
|
|||
package git
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/cgi"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"gotest.tools/v3/assert"
|
||||
is "gotest.tools/v3/assert/cmp"
|
||||
)
|
||||
|
||||
func TestParseRemoteURL(t *testing.T) {
|
||||
tests := []struct {
|
||||
doc string
|
||||
url string
|
||||
expected gitRepo
|
||||
}{
|
||||
{
|
||||
doc: "git scheme uppercase, no url-fragment",
|
||||
url: "GIT://github.com/user/repo.git",
|
||||
expected: gitRepo{
|
||||
remote: "git://github.com/user/repo.git",
|
||||
ref: "master",
|
||||
},
|
||||
},
|
||||
{
|
||||
doc: "git scheme, no url-fragment",
|
||||
url: "git://github.com/user/repo.git",
|
||||
expected: gitRepo{
|
||||
remote: "git://github.com/user/repo.git",
|
||||
ref: "master",
|
||||
},
|
||||
},
|
||||
{
|
||||
doc: "git scheme, with url-fragment",
|
||||
url: "git://github.com/user/repo.git#mybranch:mydir/mysubdir/",
|
||||
expected: gitRepo{
|
||||
remote: "git://github.com/user/repo.git",
|
||||
ref: "mybranch",
|
||||
subdir: "mydir/mysubdir/",
|
||||
},
|
||||
},
|
||||
{
|
||||
doc: "https scheme, no url-fragment",
|
||||
url: "https://github.com/user/repo.git",
|
||||
expected: gitRepo{
|
||||
remote: "https://github.com/user/repo.git",
|
||||
ref: "master",
|
||||
},
|
||||
},
|
||||
{
|
||||
doc: "https scheme, with url-fragment",
|
||||
url: "https://github.com/user/repo.git#mybranch:mydir/mysubdir/",
|
||||
expected: gitRepo{
|
||||
remote: "https://github.com/user/repo.git",
|
||||
ref: "mybranch",
|
||||
subdir: "mydir/mysubdir/",
|
||||
},
|
||||
},
|
||||
{
|
||||
doc: "git@, no url-fragment",
|
||||
url: "git@github.com:user/repo.git",
|
||||
expected: gitRepo{
|
||||
remote: "git@github.com:user/repo.git",
|
||||
ref: "master",
|
||||
},
|
||||
},
|
||||
{
|
||||
doc: "git@, with url-fragment",
|
||||
url: "git@github.com:user/repo.git#mybranch:mydir/mysubdir/",
|
||||
expected: gitRepo{
|
||||
remote: "git@github.com:user/repo.git",
|
||||
ref: "mybranch",
|
||||
subdir: "mydir/mysubdir/",
|
||||
},
|
||||
},
|
||||
{
|
||||
doc: "ssh, no url-fragment",
|
||||
url: "ssh://github.com/user/repo.git",
|
||||
expected: gitRepo{
|
||||
remote: "ssh://github.com/user/repo.git",
|
||||
ref: "master",
|
||||
},
|
||||
},
|
||||
{
|
||||
doc: "ssh, with url-fragment",
|
||||
url: "ssh://github.com/user/repo.git#mybranch:mydir/mysubdir/",
|
||||
expected: gitRepo{
|
||||
remote: "ssh://github.com/user/repo.git",
|
||||
ref: "mybranch",
|
||||
subdir: "mydir/mysubdir/",
|
||||
},
|
||||
},
|
||||
{
|
||||
doc: "ssh, with url-fragment and user",
|
||||
url: "ssh://foo%40barcorp.com@github.com/user/repo.git#mybranch:mydir/mysubdir/",
|
||||
expected: gitRepo{
|
||||
remote: "ssh://foo%40barcorp.com@github.com/user/repo.git",
|
||||
ref: "mybranch",
|
||||
subdir: "mydir/mysubdir/",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.doc, func(t *testing.T) {
|
||||
repo, err := parseRemoteURL(tc.url)
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.DeepEqual(tc.expected, repo, cmp.AllowUnexported(gitRepo{})))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCloneArgsSmartHttp(t *testing.T) {
|
||||
mux := http.NewServeMux()
|
||||
server := httptest.NewServer(mux)
|
||||
serverURL, err := url.Parse(server.URL)
|
||||
assert.NilError(t, err)
|
||||
|
||||
serverURL.Path = "/repo.git"
|
||||
|
||||
mux.HandleFunc("/repo.git/info/refs", func(w http.ResponseWriter, r *http.Request) {
|
||||
q := r.URL.Query().Get("service")
|
||||
w.Header().Set("Content-Type", fmt.Sprintf("application/x-%s-advertisement", q))
|
||||
})
|
||||
|
||||
args := fetchArgs(serverURL.String(), "master")
|
||||
exp := []string{"fetch", "--depth", "1", "origin", "--", "master"}
|
||||
assert.Check(t, is.DeepEqual(exp, args))
|
||||
}
|
||||
|
||||
func TestCloneArgsDumbHttp(t *testing.T) {
|
||||
mux := http.NewServeMux()
|
||||
server := httptest.NewServer(mux)
|
||||
serverURL, _ := url.Parse(server.URL)
|
||||
|
||||
serverURL.Path = "/repo.git"
|
||||
|
||||
mux.HandleFunc("/repo.git/info/refs", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "text/plain")
|
||||
})
|
||||
|
||||
args := fetchArgs(serverURL.String(), "master")
|
||||
exp := []string{"fetch", "origin", "--", "master"}
|
||||
assert.Check(t, is.DeepEqual(exp, args))
|
||||
}
|
||||
|
||||
func TestCloneArgsGit(t *testing.T) {
|
||||
args := fetchArgs("git://github.com/docker/docker", "master")
|
||||
exp := []string{"fetch", "--depth", "1", "origin", "--", "master"}
|
||||
assert.Check(t, is.DeepEqual(exp, args))
|
||||
}
|
||||
|
||||
func gitGetConfig(name string) string {
|
||||
b, err := gitRepo{}.gitWithinDir("", "config", "--get", name)
|
||||
if err != nil {
|
||||
// since we are interested in empty or non empty string,
|
||||
// we can safely ignore the err here.
|
||||
return ""
|
||||
}
|
||||
return strings.TrimSpace(string(b))
|
||||
}
|
||||
|
||||
func TestCheckoutGit(t *testing.T) {
|
||||
root := t.TempDir()
|
||||
|
||||
gitpath, err := exec.LookPath("git")
|
||||
assert.NilError(t, err)
|
||||
gitversion, _ := exec.Command(gitpath, "version").CombinedOutput()
|
||||
t.Logf("%s", gitversion) // E.g. "git version 2.30.2"
|
||||
|
||||
// Serve all repositories under root using the Smart HTTP protocol so
|
||||
// they can be cloned. The Dumb HTTP protocol is incompatible with
|
||||
// shallow cloning but we unconditionally shallow-clone submodules, and
|
||||
// we explicitly disable the file protocol.
|
||||
// (Another option would be to use `git daemon` and the Git protocol,
|
||||
// but that listens on a fixed port number which is a recipe for
|
||||
// disaster in CI. Funnily enough, `git daemon --port=0` works but there
|
||||
// is no easy way to discover which port got picked!)
|
||||
|
||||
// Associate git-http-backend logs with the current (sub)test.
|
||||
// Incompatible with parallel subtests.
|
||||
currentSubtest := t
|
||||
githttp := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
var logs bytes.Buffer
|
||||
(&cgi.Handler{
|
||||
Path: gitpath,
|
||||
Args: []string{"http-backend"},
|
||||
Dir: root,
|
||||
Env: []string{
|
||||
"GIT_PROJECT_ROOT=" + root,
|
||||
"GIT_HTTP_EXPORT_ALL=1",
|
||||
},
|
||||
Stderr: &logs,
|
||||
}).ServeHTTP(w, r)
|
||||
if logs.Len() == 0 {
|
||||
return
|
||||
}
|
||||
for {
|
||||
line, err := logs.ReadString('\n')
|
||||
currentSubtest.Log("git-http-backend: " + line)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
})
|
||||
server := httptest.NewServer(&githttp)
|
||||
defer server.Close()
|
||||
|
||||
eol := "\n"
|
||||
autocrlf := gitGetConfig("core.autocrlf")
|
||||
switch autocrlf {
|
||||
case "true":
|
||||
eol = "\r\n"
|
||||
case "false", "input", "":
|
||||
// accepted values
|
||||
default:
|
||||
t.Logf(`unknown core.autocrlf value: "%s"`, autocrlf)
|
||||
}
|
||||
|
||||
must := func(out []byte, err error) {
|
||||
t.Helper()
|
||||
if len(out) > 0 {
|
||||
t.Logf("%s", out)
|
||||
}
|
||||
assert.NilError(t, err)
|
||||
}
|
||||
|
||||
gitDir := filepath.Join(root, "repo")
|
||||
must(gitRepo{}.gitWithinDir(root, "-c", "init.defaultBranch=master", "init", gitDir))
|
||||
must(gitRepo{}.gitWithinDir(gitDir, "config", "user.email", "test@docker.com"))
|
||||
must(gitRepo{}.gitWithinDir(gitDir, "config", "user.name", "Docker test"))
|
||||
assert.NilError(t, os.WriteFile(filepath.Join(gitDir, "Dockerfile"), []byte("FROM scratch"), 0o644))
|
||||
|
||||
subDir := filepath.Join(gitDir, "subdir")
|
||||
assert.NilError(t, os.Mkdir(subDir, 0o755))
|
||||
assert.NilError(t, os.WriteFile(filepath.Join(subDir, "Dockerfile"), []byte("FROM scratch\nEXPOSE 5000"), 0o644))
|
||||
|
||||
if runtime.GOOS != "windows" {
|
||||
assert.NilError(t, os.Symlink("../subdir", filepath.Join(gitDir, "parentlink")))
|
||||
assert.NilError(t, os.Symlink("/subdir", filepath.Join(gitDir, "absolutelink")))
|
||||
}
|
||||
|
||||
must(gitRepo{}.gitWithinDir(gitDir, "add", "-A"))
|
||||
must(gitRepo{}.gitWithinDir(gitDir, "commit", "-am", "First commit"))
|
||||
must(gitRepo{}.gitWithinDir(gitDir, "checkout", "-b", "test"))
|
||||
|
||||
assert.NilError(t, os.WriteFile(filepath.Join(gitDir, "Dockerfile"), []byte("FROM scratch\nEXPOSE 3000"), 0o644))
|
||||
assert.NilError(t, os.WriteFile(filepath.Join(subDir, "Dockerfile"), []byte("FROM busybox\nEXPOSE 5000"), 0o644))
|
||||
|
||||
must(gitRepo{}.gitWithinDir(gitDir, "add", "-A"))
|
||||
must(gitRepo{}.gitWithinDir(gitDir, "commit", "-am", "Branch commit"))
|
||||
must(gitRepo{}.gitWithinDir(gitDir, "checkout", "master"))
|
||||
|
||||
// set up submodule
|
||||
subrepoDir := filepath.Join(root, "subrepo")
|
||||
must(gitRepo{}.gitWithinDir(root, "-c", "init.defaultBranch=master", "init", subrepoDir))
|
||||
must(gitRepo{}.gitWithinDir(subrepoDir, "config", "user.email", "test@docker.com"))
|
||||
must(gitRepo{}.gitWithinDir(subrepoDir, "config", "user.name", "Docker test"))
|
||||
|
||||
assert.NilError(t, os.WriteFile(filepath.Join(subrepoDir, "subfile"), []byte("subcontents"), 0o644))
|
||||
|
||||
must(gitRepo{}.gitWithinDir(subrepoDir, "add", "-A"))
|
||||
must(gitRepo{}.gitWithinDir(subrepoDir, "commit", "-am", "Subrepo initial"))
|
||||
|
||||
must(gitRepo{}.gitWithinDir(gitDir, "submodule", "add", server.URL+"/subrepo", "sub"))
|
||||
must(gitRepo{}.gitWithinDir(gitDir, "add", "-A"))
|
||||
must(gitRepo{}.gitWithinDir(gitDir, "commit", "-am", "With submodule"))
|
||||
|
||||
type singleCase struct {
|
||||
frag string
|
||||
exp string
|
||||
fail bool
|
||||
submodule bool
|
||||
}
|
||||
|
||||
cases := []singleCase{
|
||||
{"", "FROM scratch", false, true},
|
||||
{"master", "FROM scratch", false, true},
|
||||
{":subdir", "FROM scratch" + eol + "EXPOSE 5000", false, false},
|
||||
{":nosubdir", "", true, false}, // missing directory error
|
||||
{":Dockerfile", "", true, false}, // not a directory error
|
||||
{"master:nosubdir", "", true, false},
|
||||
{"master:subdir", "FROM scratch" + eol + "EXPOSE 5000", false, false},
|
||||
{"master:../subdir", "", true, false},
|
||||
{"test", "FROM scratch" + eol + "EXPOSE 3000", false, false},
|
||||
{"test:", "FROM scratch" + eol + "EXPOSE 3000", false, false},
|
||||
{"test:subdir", "FROM busybox" + eol + "EXPOSE 5000", false, false},
|
||||
}
|
||||
|
||||
if runtime.GOOS != "windows" {
|
||||
// Windows GIT (2.7.1 x64) does not support parentlink/absolutelink. Sample output below
|
||||
// git --work-tree .\repo --git-dir .\repo\.git add -A
|
||||
// error: readlink("absolutelink"): Function not implemented
|
||||
// error: unable to index file absolutelink
|
||||
// fatal: adding files failed
|
||||
cases = append(cases, singleCase{frag: "master:absolutelink", exp: "FROM scratch" + eol + "EXPOSE 5000", fail: false})
|
||||
cases = append(cases, singleCase{frag: "master:parentlink", exp: "FROM scratch" + eol + "EXPOSE 5000", fail: false})
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.frag, func(t *testing.T) {
|
||||
currentSubtest = t
|
||||
ref, subdir := getRefAndSubdir(c.frag)
|
||||
r, err := gitRepo{remote: server.URL + "/repo", ref: ref, subdir: subdir}.clone()
|
||||
|
||||
if c.fail {
|
||||
assert.Check(t, is.ErrorContains(err, ""))
|
||||
return
|
||||
}
|
||||
assert.NilError(t, err)
|
||||
defer os.RemoveAll(r)
|
||||
if c.submodule {
|
||||
b, err := os.ReadFile(filepath.Join(r, "sub/subfile"))
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.Equal("subcontents", string(b)))
|
||||
} else {
|
||||
_, err := os.Stat(filepath.Join(r, "sub/subfile"))
|
||||
assert.ErrorContains(t, err, "")
|
||||
assert.Assert(t, os.IsNotExist(err))
|
||||
}
|
||||
|
||||
b, err := os.ReadFile(filepath.Join(r, "Dockerfile"))
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.Equal(c.exp, string(b)))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidGitTransport(t *testing.T) {
|
||||
gitUrls := []string{
|
||||
"git://github.com/docker/docker",
|
||||
"git@github.com:docker/docker.git",
|
||||
"git@bitbucket.org:atlassianlabs/atlassian-docker.git",
|
||||
"https://github.com/docker/docker.git",
|
||||
"http://github.com/docker/docker.git",
|
||||
"http://github.com/docker/docker.git#branch",
|
||||
"http://github.com/docker/docker.git#:dir",
|
||||
}
|
||||
incompleteGitUrls := []string{
|
||||
"github.com/docker/docker",
|
||||
}
|
||||
|
||||
for _, u := range gitUrls {
|
||||
if !isGitTransport(u) {
|
||||
t.Fatalf("%q should be detected as valid Git prefix", u)
|
||||
}
|
||||
}
|
||||
|
||||
for _, u := range incompleteGitUrls {
|
||||
if isGitTransport(u) {
|
||||
t.Fatalf("%q should not be detected as valid Git prefix", u)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGitInvalidRef(t *testing.T) {
|
||||
gitUrls := []string{
|
||||
"git://github.com/moby/moby#--foo bar",
|
||||
"git@github.com/moby/moby#--upload-pack=sleep;:",
|
||||
"git@g.com:a/b.git#-B",
|
||||
"git@g.com:a/b.git#with space",
|
||||
}
|
||||
|
||||
for _, u := range gitUrls {
|
||||
_, err := Clone(u)
|
||||
assert.Assert(t, err != nil)
|
||||
// On Windows, git has different case for the "invalid refspec" error,
|
||||
// so we can't use ErrorContains.
|
||||
assert.Check(t, is.Contains(strings.ToLower(err.Error()), "invalid refspec"))
|
||||
}
|
||||
}
|
|
@ -8,7 +8,7 @@ package urlutil
|
|||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/docker/docker/internal/lazyregexp"
|
||||
"github.com/docker/cli/internal/lazyregexp"
|
||||
)
|
||||
|
||||
// urlPathWithFragmentSuffix matches fragments to use as Git reference and build
|
|
@ -0,0 +1,42 @@
|
|||
package urlutil
|
||||
|
||||
import "testing"
|
||||
|
||||
var (
|
||||
gitUrls = []string{
|
||||
"git://github.com/docker/docker",
|
||||
"git@github.com:docker/docker.git",
|
||||
"git@bitbucket.org:atlassianlabs/atlassian-docker.git",
|
||||
"https://github.com/docker/docker.git",
|
||||
"http://github.com/docker/docker.git",
|
||||
"http://github.com/docker/docker.git#branch",
|
||||
"http://github.com/docker/docker.git#:dir",
|
||||
}
|
||||
incompleteGitUrls = []string{
|
||||
"github.com/docker/docker",
|
||||
}
|
||||
invalidGitUrls = []string{
|
||||
"http://github.com/docker/docker.git:#branch",
|
||||
"https://github.com/docker/dgit",
|
||||
}
|
||||
)
|
||||
|
||||
func TestIsGIT(t *testing.T) {
|
||||
for _, url := range gitUrls {
|
||||
if !IsGitURL(url) {
|
||||
t.Fatalf("%q should be detected as valid Git url", url)
|
||||
}
|
||||
}
|
||||
|
||||
for _, url := range incompleteGitUrls {
|
||||
if !IsGitURL(url) {
|
||||
t.Fatalf("%q should be detected as valid Git url", url)
|
||||
}
|
||||
}
|
||||
|
||||
for _, url := range invalidGitUrls {
|
||||
if IsGitURL(url) {
|
||||
t.Fatalf("%q should not be detected as valid Git prefix", url)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -123,7 +123,7 @@ COPY data /data
|
|||
// to support testing (ex: docker/cli#294)
|
||||
func TestRunBuildFromGitHubSpecialCase(t *testing.T) {
|
||||
t.Setenv("DOCKER_BUILDKIT", "0")
|
||||
cmd := NewBuildCommand(test.NewFakeCli(&fakeClient{}))
|
||||
cmd := newBuildCommand(test.NewFakeCli(&fakeClient{}))
|
||||
// Clone a small repo that exists so git doesn't prompt for credentials
|
||||
cmd.SetArgs([]string{"github.com/docker/for-win"})
|
||||
cmd.SetOut(io.Discard)
|
||||
|
@ -146,7 +146,7 @@ func TestRunBuildFromLocalGitHubDir(t *testing.T) {
|
|||
assert.NilError(t, err)
|
||||
|
||||
client := test.NewFakeCli(&fakeClient{})
|
||||
cmd := NewBuildCommand(client)
|
||||
cmd := newBuildCommand(client)
|
||||
cmd.SetArgs([]string{buildDir})
|
||||
cmd.SetOut(io.Discard)
|
||||
err = cmd.Execute()
|
||||
|
|
|
@ -7,7 +7,14 @@ import (
|
|||
)
|
||||
|
||||
// NewImageCommand returns a cobra command for `image` subcommands
|
||||
func NewImageCommand(dockerCli command.Cli) *cobra.Command {
|
||||
//
|
||||
// Deprecated: Do not import commands directly. They will be removed in a future release.
|
||||
func NewImageCommand(dockerCLI command.Cli) *cobra.Command {
|
||||
return newImageCommand(dockerCLI)
|
||||
}
|
||||
|
||||
// newImageCommand returns a cobra command for `image` subcommands
|
||||
func newImageCommand(dockerCli command.Cli) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "image",
|
||||
Short: "Manage images",
|
||||
|
@ -15,18 +22,18 @@ func NewImageCommand(dockerCli command.Cli) *cobra.Command {
|
|||
RunE: command.ShowHelp(dockerCli.Err()),
|
||||
}
|
||||
cmd.AddCommand(
|
||||
NewBuildCommand(dockerCli),
|
||||
NewHistoryCommand(dockerCli),
|
||||
NewImportCommand(dockerCli),
|
||||
NewLoadCommand(dockerCli),
|
||||
NewPullCommand(dockerCli),
|
||||
NewPushCommand(dockerCli),
|
||||
NewSaveCommand(dockerCli),
|
||||
NewTagCommand(dockerCli),
|
||||
newBuildCommand(dockerCli),
|
||||
newHistoryCommand(dockerCli),
|
||||
newImportCommand(dockerCli),
|
||||
newLoadCommand(dockerCli),
|
||||
newPullCommand(dockerCli),
|
||||
newPushCommand(dockerCli),
|
||||
newSaveCommand(dockerCli),
|
||||
newTagCommand(dockerCli),
|
||||
newListCommand(dockerCli),
|
||||
newRemoveCommand(dockerCli),
|
||||
newImageRemoveCommand(dockerCli),
|
||||
newInspectCommand(dockerCli),
|
||||
NewPruneCommand(dockerCli),
|
||||
newPruneCommand(dockerCli),
|
||||
)
|
||||
return cmd
|
||||
}
|
||||
|
|
|
@ -20,7 +20,14 @@ const (
|
|||
)
|
||||
|
||||
// NewHistoryFormat returns a format for rendering an HistoryContext
|
||||
//
|
||||
// Deprecated: this function was only used internally and will be removed in the next release.
|
||||
func NewHistoryFormat(source string, quiet bool, human bool) formatter.Format {
|
||||
return newHistoryFormat(source, quiet, human)
|
||||
}
|
||||
|
||||
// newHistoryFormat returns a format for rendering a historyContext.
|
||||
func newHistoryFormat(source string, quiet bool, human bool) formatter.Format {
|
||||
if source == formatter.TableFormatKey {
|
||||
switch {
|
||||
case quiet:
|
||||
|
@ -36,10 +43,17 @@ func NewHistoryFormat(source string, quiet bool, human bool) formatter.Format {
|
|||
}
|
||||
|
||||
// HistoryWrite writes the context
|
||||
func HistoryWrite(ctx formatter.Context, human bool, histories []image.HistoryResponseItem) error {
|
||||
//
|
||||
// Deprecated: this function was only used internally and will be removed in the next release.
|
||||
func HistoryWrite(fmtCtx formatter.Context, human bool, histories []image.HistoryResponseItem) error {
|
||||
return historyWrite(fmtCtx, human, histories)
|
||||
}
|
||||
|
||||
// historyWrite writes the context
|
||||
func historyWrite(fmtCtx formatter.Context, human bool, histories []image.HistoryResponseItem) error {
|
||||
render := func(format func(subContext formatter.SubContext) error) error {
|
||||
for _, history := range histories {
|
||||
historyCtx := &historyContext{trunc: ctx.Trunc, h: history, human: human}
|
||||
historyCtx := &historyContext{trunc: fmtCtx.Trunc, h: history, human: human}
|
||||
if err := format(historyCtx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -55,7 +69,7 @@ func HistoryWrite(ctx formatter.Context, human bool, histories []image.HistoryRe
|
|||
"Size": formatter.SizeHeader,
|
||||
"Comment": commentHeader,
|
||||
}
|
||||
return ctx.Write(historyCtx, render)
|
||||
return fmtCtx.Write(historyCtx, render)
|
||||
}
|
||||
|
||||
type historyContext struct {
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue