Compare commits
197 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d8eb465f86 | |||
| a83e3df40b | |||
| ff5ea757b4 | |||
| c1d14aef3c | |||
| 18adfd54a9 | |||
| 6e20b9d93c | |||
| 3cadbd82c5 | |||
| b3df92053d | |||
| b89b069082 | |||
| 9deb5e9cd3 | |||
| e8be20bc9c | |||
| f2cd1979c0 | |||
| 991d942cc3 | |||
| 8e49313c0c | |||
| 6cd42da462 | |||
| 2b9489d827 | |||
| fbeae8516b | |||
| d593e61275 | |||
| 139968dff2 | |||
| 925db59377 | |||
| 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 |
4
.github/workflows/build.yml
vendored
4
.github/workflows/build.yml
vendored
@ -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
|
||||
|
||||
4
.github/workflows/codeql.yml
vendored
4
.github/workflows/codeql.yml
vendored
@ -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.7"
|
||||
-
|
||||
name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v3
|
||||
|
||||
2
.github/workflows/e2e.yml
vendored
2
.github/workflows/e2e.yml
vendored
@ -44,7 +44,7 @@ jobs:
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
-
|
||||
name: Update daemon.json
|
||||
run: |
|
||||
|
||||
4
.github/workflows/test.yml
vendored
4
.github/workflows/test.yml
vendored
@ -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.7"
|
||||
-
|
||||
name: Test
|
||||
run: |
|
||||
|
||||
4
.github/workflows/validate.yml
vendored
4
.github/workflows/validate.yml
vendored
@ -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.7"
|
||||
|
||||
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.7
|
||||
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"
|
||||
@ -22,13 +23,9 @@ const (
|
||||
// used to originally invoke the docker CLI when executing a
|
||||
// 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"
|
||||
// Deprecated: use [metadata.ReexecEnvvar]. This alias will be removed in the next release.
|
||||
ReexecEnvvar = metadata.ReexecEnvvar
|
||||
)
|
||||
|
||||
// errPluginNotFound is the error returned when a plugin could not be found.
|
||||
@ -40,15 +37,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 +120,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 +157,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 +178,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
|
||||
|
||||
43
cli-plugins/manager/plugin_test.go
Normal file
43
cli-plugins/manager/plugin_test.go
Normal file
@ -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
|
||||
|
||||
28
cli-plugins/plugin/plugin_test.go
Normal file
28
cli-plugins/plugin/plugin_test.go
Normal file
@ -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",
|
||||
|
||||
@ -8,7 +8,6 @@ import (
|
||||
|
||||
"github.com/docker/cli/cli"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/cli/cli/command/completion"
|
||||
"github.com/docker/cli/internal/prompt"
|
||||
"github.com/docker/cli/opts"
|
||||
"github.com/docker/docker/api/types/build"
|
||||
@ -24,7 +23,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,18 +38,18 @@ 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"},
|
||||
ValidArgsFunction: completion.NoComplete,
|
||||
ValidArgsFunction: cobra.NoFileCompletions,
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -6,7 +6,6 @@ import (
|
||||
|
||||
"github.com/docker/cli/cli"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/cli/cli/command/completion"
|
||||
"github.com/docker/docker/api/types/checkpoint"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
@ -30,7 +29,7 @@ func newCreateCommand(dockerCli command.Cli) *cobra.Command {
|
||||
opts.checkpoint = args[1]
|
||||
return runCreate(cmd.Context(), dockerCli, opts)
|
||||
},
|
||||
ValidArgsFunction: completion.NoComplete,
|
||||
ValidArgsFunction: cobra.NoFileCompletions,
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
|
||||
@ -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.
|
||||
//
|
||||
@ -146,7 +141,9 @@ func FileNames(_ *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCom
|
||||
return nil, cobra.ShellCompDirectiveDefault
|
||||
}
|
||||
|
||||
// NoComplete is used for commands where there's no relevant completion
|
||||
// NoComplete is used for commands where there's no relevant completion.
|
||||
//
|
||||
// Deprecated: use [cobra.NoFileCompletions]. This function will be removed in the next release.
|
||||
func NoComplete(_ *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) {
|
||||
return nil, cobra.ShellCompDirectiveNoFileComp
|
||||
}
|
||||
|
||||
@ -288,12 +288,6 @@ func TestCompleteNetworkNames(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestCompleteNoComplete(t *testing.T) {
|
||||
values, directives := NoComplete(nil, nil, "")
|
||||
assert.Check(t, is.Equal(directives, cobra.ShellCompDirectiveNoFileComp))
|
||||
assert.Check(t, is.Len(values, 0))
|
||||
}
|
||||
|
||||
func TestCompletePlatforms(t *testing.T) {
|
||||
values, directives := Platforms(nil, nil, "")
|
||||
assert.Check(t, is.Equal(directives&cobra.ShellCompDirectiveNoFileComp, cobra.ShellCompDirectiveNoFileComp), "Should not perform file completion")
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -7,7 +7,6 @@ import (
|
||||
|
||||
"github.com/docker/cli/cli"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/cli/cli/command/completion"
|
||||
"github.com/docker/cli/opts"
|
||||
"github.com/docker/docker/api/types/swarm"
|
||||
"github.com/moby/sys/sequential"
|
||||
@ -16,6 +15,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 +24,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 +42,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,
|
||||
ValidArgsFunction: cobra.NoFileCompletions,
|
||||
}
|
||||
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
|
||||
|
||||
@ -6,7 +6,6 @@ import (
|
||||
|
||||
"github.com/docker/cli/cli"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/cli/cli/command/completion"
|
||||
"github.com/docker/cli/cli/command/formatter"
|
||||
flagsHelper "github.com/docker/cli/cli/flags"
|
||||
"github.com/docker/cli/opts"
|
||||
@ -16,14 +15,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 +39,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,
|
||||
ValidArgsFunction: cobra.NoFileCompletions,
|
||||
}
|
||||
|
||||
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 +87,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,17 +92,20 @@ 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
|
||||
// up if it does already have one set, because Cobra does this for
|
||||
// us, and returns an error (which we ignore for this reason).
|
||||
_ = cmd.RegisterFlagCompletionFunc(flag.Name, completion.NoComplete)
|
||||
_ = cmd.RegisterFlagCompletionFunc(flag.Name, cobra.NoFileCompletions)
|
||||
})
|
||||
|
||||
return cmd
|
||||
@ -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)
|
||||
|
||||
|
||||
@ -6,7 +6,6 @@ import (
|
||||
|
||||
"github.com/docker/cli/cli"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/cli/cli/command/completion"
|
||||
"github.com/docker/cli/cli/command/formatter"
|
||||
flagsHelper "github.com/docker/cli/cli/flags"
|
||||
"github.com/docker/cli/opts"
|
||||
@ -29,7 +28,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{
|
||||
@ -44,7 +49,7 @@ func NewPsCommand(dockerCLI command.Cli) *cobra.Command {
|
||||
"category-top": "3",
|
||||
"aliases": "docker container ls, docker container list, docker container ps, docker ps",
|
||||
},
|
||||
ValidArgsFunction: completion.NoComplete,
|
||||
ValidArgsFunction: cobra.NoFileCompletions,
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
@ -62,7 +67,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()
|
||||
|
||||
@ -6,7 +6,6 @@ import (
|
||||
|
||||
"github.com/docker/cli/cli"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/cli/cli/command/completion"
|
||||
"github.com/docker/cli/internal/prompt"
|
||||
"github.com/docker/cli/opts"
|
||||
"github.com/docker/go-units"
|
||||
@ -20,7 +19,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,18 +33,18 @@ 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"},
|
||||
ValidArgsFunction: completion.NoComplete,
|
||||
ValidArgsFunction: cobra.NoFileCompletions,
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
|
||||
@ -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,18 +72,19 @@ 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
|
||||
// up if it does already have one set, because Cobra does this for
|
||||
// us, and returns an error (which we ignore for this reason).
|
||||
_ = cmd.RegisterFlagCompletionFunc(flag.Name, completion.NoComplete)
|
||||
_ = cmd.RegisterFlagCompletionFunc(flag.Name, cobra.NoFileCompletions)
|
||||
})
|
||||
|
||||
return cmd
|
||||
|
||||
@ -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,10 +8,9 @@ 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"
|
||||
"github.com/docker/cli/cli/command/formatter/tabwriter"
|
||||
"github.com/docker/cli/cli/context/docker"
|
||||
"github.com/docker/cli/cli/context/store"
|
||||
@ -19,6 +18,8 @@ import (
|
||||
)
|
||||
|
||||
// CreateOptions are the options used for creating a context
|
||||
//
|
||||
// Deprecated: this type was for internal use and will be removed in the next release.
|
||||
type CreateOptions struct {
|
||||
Name string
|
||||
Description string
|
||||
@ -30,6 +31,18 @@ type CreateOptions struct {
|
||||
metaData map[string]any
|
||||
}
|
||||
|
||||
// createOptions are the options used for creating a context
|
||||
type createOptions struct {
|
||||
name string
|
||||
description string
|
||||
endpoint map[string]string
|
||||
from string
|
||||
|
||||
// Additional Metadata to store in the context. This option is not
|
||||
// currently exposed to the user.
|
||||
metaData map[string]any
|
||||
}
|
||||
|
||||
func longCreateDescription() string {
|
||||
buf := bytes.NewBuffer(nil)
|
||||
buf.WriteString("Create a context\n\nDocker endpoint config:\n\n")
|
||||
@ -44,52 +57,68 @@ func longCreateDescription() string {
|
||||
}
|
||||
|
||||
func newCreateCommand(dockerCLI command.Cli) *cobra.Command {
|
||||
opts := &CreateOptions{}
|
||||
opts := createOptions{}
|
||||
cmd := &cobra.Command{
|
||||
Use: "create [OPTIONS] CONTEXT",
|
||||
Short: "Create a context",
|
||||
Args: cli.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
opts.Name = args[0]
|
||||
return RunCreate(dockerCLI, opts)
|
||||
opts.name = args[0]
|
||||
return runCreate(dockerCLI, &opts)
|
||||
},
|
||||
Long: longCreateDescription(),
|
||||
ValidArgsFunction: completion.NoComplete,
|
||||
ValidArgsFunction: cobra.NoFileCompletions,
|
||||
}
|
||||
flags := cmd.Flags()
|
||||
flags.StringVar(&opts.Description, "description", "", "Description of the context")
|
||||
flags.StringToStringVar(&opts.Docker, "docker", nil, "set the docker endpoint")
|
||||
flags.StringVar(&opts.From, "from", "", "create context from a named context")
|
||||
flags.StringVar(&opts.description, "description", "", "Description of the context")
|
||||
flags.StringToStringVar(&opts.endpoint, "docker", nil, "set the docker endpoint")
|
||||
flags.StringVar(&opts.from, "from", "", "create context from a named context")
|
||||
return cmd
|
||||
}
|
||||
|
||||
// RunCreate creates a Docker context
|
||||
|
||||
// Deprecated: this function was for internal use and will be removed in the next release.
|
||||
func RunCreate(dockerCLI command.Cli, o *CreateOptions) error {
|
||||
if o == nil {
|
||||
o = &CreateOptions{}
|
||||
}
|
||||
|
||||
return runCreate(dockerCLI, &createOptions{
|
||||
name: o.Name,
|
||||
description: o.Description,
|
||||
endpoint: o.Docker,
|
||||
metaData: o.metaData,
|
||||
})
|
||||
}
|
||||
|
||||
// runCreate creates a Docker context
|
||||
func runCreate(dockerCLI command.Cli, opts *createOptions) error {
|
||||
s := dockerCLI.ContextStore()
|
||||
err := checkContextNameForCreation(s, o.Name)
|
||||
err := checkContextNameForCreation(s, opts.name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
switch {
|
||||
case o.From == "" && o.Docker == nil:
|
||||
err = createFromExistingContext(s, dockerCLI.CurrentContext(), o)
|
||||
case o.From != "":
|
||||
err = createFromExistingContext(s, o.From, o)
|
||||
case opts.from == "" && opts.endpoint == nil:
|
||||
err = createFromExistingContext(s, dockerCLI.CurrentContext(), opts)
|
||||
case opts.from != "":
|
||||
err = createFromExistingContext(s, opts.from, opts)
|
||||
default:
|
||||
err = createNewContext(s, o)
|
||||
err = createNewContext(s, opts)
|
||||
}
|
||||
if err == nil {
|
||||
_, _ = fmt.Fprintln(dockerCLI.Out(), o.Name)
|
||||
_, _ = fmt.Fprintf(dockerCLI.Err(), "Successfully created context %q\n", o.Name)
|
||||
_, _ = fmt.Fprintln(dockerCLI.Out(), opts.name)
|
||||
_, _ = fmt.Fprintf(dockerCLI.Err(), "Successfully created context %q\n", opts.name)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func createNewContext(contextStore store.ReaderWriter, o *CreateOptions) error {
|
||||
if o.Docker == nil {
|
||||
func createNewContext(contextStore store.ReaderWriter, opts *createOptions) error {
|
||||
if opts.endpoint == nil {
|
||||
return errors.New("docker endpoint configuration is required")
|
||||
}
|
||||
dockerEP, dockerTLS, err := getDockerEndpointMetadataAndTLS(contextStore, o.Docker)
|
||||
dockerEP, dockerTLS, err := getDockerEndpointMetadataAndTLS(contextStore, opts.endpoint)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to create docker endpoint config: %w", err)
|
||||
}
|
||||
@ -98,10 +127,10 @@ func createNewContext(contextStore store.ReaderWriter, o *CreateOptions) error {
|
||||
docker.DockerEndpoint: dockerEP,
|
||||
},
|
||||
Metadata: command.DockerContext{
|
||||
Description: o.Description,
|
||||
AdditionalFields: o.metaData,
|
||||
Description: opts.description,
|
||||
AdditionalFields: opts.metaData,
|
||||
},
|
||||
Name: o.Name,
|
||||
Name: opts.name,
|
||||
}
|
||||
contextTLSData := store.ContextTLSData{}
|
||||
if dockerTLS != nil {
|
||||
@ -115,14 +144,14 @@ func createNewContext(contextStore store.ReaderWriter, o *CreateOptions) error {
|
||||
if err := contextStore.CreateOrUpdate(contextMetadata); err != nil {
|
||||
return err
|
||||
}
|
||||
return contextStore.ResetTLSMaterial(o.Name, &contextTLSData)
|
||||
return contextStore.ResetTLSMaterial(opts.name, &contextTLSData)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
@ -131,16 +160,16 @@ func checkContextNameForCreation(s store.Reader, name string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func createFromExistingContext(s store.ReaderWriter, fromContextName string, o *CreateOptions) error {
|
||||
if len(o.Docker) != 0 {
|
||||
func createFromExistingContext(s store.ReaderWriter, fromContextName string, opts *createOptions) error {
|
||||
if len(opts.endpoint) != 0 {
|
||||
return errors.New("cannot use --docker flag when --from is set")
|
||||
}
|
||||
reader := store.Export(fromContextName, &descriptionDecorator{
|
||||
Reader: s,
|
||||
description: o.Description,
|
||||
description: opts.description,
|
||||
})
|
||||
defer reader.Close()
|
||||
return store.Import(o.Name, s, reader)
|
||||
return store.Import(opts.name, s, reader)
|
||||
}
|
||||
|
||||
type descriptionDecorator struct {
|
||||
|
||||
@ -60,7 +60,7 @@ func TestCreate(t *testing.T) {
|
||||
assert.NilError(t, cli.ContextStore().CreateOrUpdate(store.Metadata{Name: "existing-context"}))
|
||||
tests := []struct {
|
||||
doc string
|
||||
options CreateOptions
|
||||
options createOptions
|
||||
expecterErr string
|
||||
}{
|
||||
{
|
||||
@ -69,30 +69,30 @@ func TestCreate(t *testing.T) {
|
||||
},
|
||||
{
|
||||
doc: "reserved name",
|
||||
options: CreateOptions{
|
||||
Name: "default",
|
||||
options: createOptions{
|
||||
name: "default",
|
||||
},
|
||||
expecterErr: `"default" is a reserved context name`,
|
||||
},
|
||||
{
|
||||
doc: "whitespace-only name",
|
||||
options: CreateOptions{
|
||||
Name: " ",
|
||||
options: createOptions{
|
||||
name: " ",
|
||||
},
|
||||
expecterErr: `context name " " is invalid`,
|
||||
},
|
||||
{
|
||||
doc: "existing context",
|
||||
options: CreateOptions{
|
||||
Name: "existing-context",
|
||||
options: createOptions{
|
||||
name: "existing-context",
|
||||
},
|
||||
expecterErr: `context "existing-context" already exists`,
|
||||
},
|
||||
{
|
||||
doc: "invalid docker host",
|
||||
options: CreateOptions{
|
||||
Name: "invalid-docker-host",
|
||||
Docker: map[string]string{
|
||||
options: createOptions{
|
||||
name: "invalid-docker-host",
|
||||
endpoint: map[string]string{
|
||||
"host": "some///invalid/host",
|
||||
},
|
||||
},
|
||||
@ -100,27 +100,27 @@ func TestCreate(t *testing.T) {
|
||||
},
|
||||
{
|
||||
doc: "ssh host with skip-tls-verify=false",
|
||||
options: CreateOptions{
|
||||
Name: "skip-tls-verify-false",
|
||||
Docker: map[string]string{
|
||||
options: createOptions{
|
||||
name: "skip-tls-verify-false",
|
||||
endpoint: map[string]string{
|
||||
"host": "ssh://example.com,skip-tls-verify=false",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
doc: "ssh host with skip-tls-verify=true",
|
||||
options: CreateOptions{
|
||||
Name: "skip-tls-verify-true",
|
||||
Docker: map[string]string{
|
||||
options: createOptions{
|
||||
name: "skip-tls-verify-true",
|
||||
endpoint: map[string]string{
|
||||
"host": "ssh://example.com,skip-tls-verify=true",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
doc: "ssh host with skip-tls-verify=INVALID",
|
||||
options: CreateOptions{
|
||||
Name: "skip-tls-verify-invalid",
|
||||
Docker: map[string]string{
|
||||
options: createOptions{
|
||||
name: "skip-tls-verify-invalid",
|
||||
endpoint: map[string]string{
|
||||
"host": "ssh://example.com",
|
||||
"skip-tls-verify": "INVALID",
|
||||
},
|
||||
@ -129,9 +129,9 @@ func TestCreate(t *testing.T) {
|
||||
},
|
||||
{
|
||||
doc: "unknown option",
|
||||
options: CreateOptions{
|
||||
Name: "unknown-option",
|
||||
Docker: map[string]string{
|
||||
options: createOptions{
|
||||
name: "unknown-option",
|
||||
endpoint: map[string]string{
|
||||
"UNKNOWN": "value",
|
||||
},
|
||||
},
|
||||
@ -140,7 +140,7 @@ func TestCreate(t *testing.T) {
|
||||
}
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.doc, func(t *testing.T) {
|
||||
err := RunCreate(cli, &tc.options)
|
||||
err := runCreate(cli, &tc.options)
|
||||
if tc.expecterErr == "" {
|
||||
assert.NilError(t, err)
|
||||
} else {
|
||||
@ -159,9 +159,9 @@ func assertContextCreateLogging(t *testing.T, cli *test.FakeCli, n string) {
|
||||
func TestCreateOrchestratorEmpty(t *testing.T) {
|
||||
cli := makeFakeCli(t)
|
||||
|
||||
err := RunCreate(cli, &CreateOptions{
|
||||
Name: "test",
|
||||
Docker: map[string]string{},
|
||||
err := runCreate(cli, &createOptions{
|
||||
name: "test",
|
||||
endpoint: map[string]string{},
|
||||
})
|
||||
assert.NilError(t, err)
|
||||
assertContextCreateLogging(t, cli, "test")
|
||||
@ -187,20 +187,20 @@ func TestCreateFromContext(t *testing.T) {
|
||||
|
||||
cli := makeFakeCli(t)
|
||||
cli.ResetOutputBuffers()
|
||||
assert.NilError(t, RunCreate(cli, &CreateOptions{
|
||||
Name: "original",
|
||||
Description: "original description",
|
||||
Docker: map[string]string{
|
||||
assert.NilError(t, runCreate(cli, &createOptions{
|
||||
name: "original",
|
||||
description: "original description",
|
||||
endpoint: map[string]string{
|
||||
keyHost: "tcp://42.42.42.42:2375",
|
||||
},
|
||||
}))
|
||||
assertContextCreateLogging(t, cli, "original")
|
||||
|
||||
cli.ResetOutputBuffers()
|
||||
assert.NilError(t, RunCreate(cli, &CreateOptions{
|
||||
Name: "dummy",
|
||||
Description: "dummy description",
|
||||
Docker: map[string]string{
|
||||
assert.NilError(t, runCreate(cli, &createOptions{
|
||||
name: "dummy",
|
||||
description: "dummy description",
|
||||
endpoint: map[string]string{
|
||||
keyHost: "tcp://24.24.24.24:2375",
|
||||
},
|
||||
}))
|
||||
@ -211,11 +211,11 @@ func TestCreateFromContext(t *testing.T) {
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
cli.ResetOutputBuffers()
|
||||
err := RunCreate(cli, &CreateOptions{
|
||||
From: "original",
|
||||
Name: tc.name,
|
||||
Description: tc.description,
|
||||
Docker: tc.docker,
|
||||
err := runCreate(cli, &createOptions{
|
||||
from: "original",
|
||||
name: tc.name,
|
||||
description: tc.description,
|
||||
endpoint: tc.docker,
|
||||
})
|
||||
assert.NilError(t, err)
|
||||
assertContextCreateLogging(t, cli, tc.name)
|
||||
@ -251,10 +251,10 @@ func TestCreateFromCurrent(t *testing.T) {
|
||||
|
||||
cli := makeFakeCli(t)
|
||||
cli.ResetOutputBuffers()
|
||||
assert.NilError(t, RunCreate(cli, &CreateOptions{
|
||||
Name: "original",
|
||||
Description: "original description",
|
||||
Docker: map[string]string{
|
||||
assert.NilError(t, runCreate(cli, &createOptions{
|
||||
name: "original",
|
||||
description: "original description",
|
||||
endpoint: map[string]string{
|
||||
keyHost: "tcp://42.42.42.42:2375",
|
||||
},
|
||||
}))
|
||||
@ -265,9 +265,9 @@ func TestCreateFromCurrent(t *testing.T) {
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
cli.ResetOutputBuffers()
|
||||
err := RunCreate(cli, &CreateOptions{
|
||||
Name: tc.name,
|
||||
Description: tc.description,
|
||||
err := runCreate(cli, &createOptions{
|
||||
name: tc.name,
|
||||
description: tc.description,
|
||||
})
|
||||
assert.NilError(t, err)
|
||||
assertContextCreateLogging(t, cli, tc.name)
|
||||
|
||||
@ -21,14 +21,11 @@ func TestExportImportWithFile(t *testing.T) {
|
||||
"MyCustomMetadata": t.Name(),
|
||||
})
|
||||
cli.ErrBuffer().Reset()
|
||||
assert.NilError(t, RunExport(cli, &ExportOptions{
|
||||
ContextName: "test",
|
||||
Dest: contextFile,
|
||||
}))
|
||||
assert.NilError(t, runExport(cli, "test", contextFile))
|
||||
assert.Equal(t, cli.ErrBuffer().String(), fmt.Sprintf("Written file %q\n", contextFile))
|
||||
cli.OutBuffer().Reset()
|
||||
cli.ErrBuffer().Reset()
|
||||
assert.NilError(t, RunImport(cli, "test2", contextFile))
|
||||
assert.NilError(t, runImport(cli, "test2", contextFile))
|
||||
context1, err := cli.ContextStore().GetMetadata("test")
|
||||
assert.NilError(t, err)
|
||||
context2, err := cli.ContextStore().GetMetadata("test2")
|
||||
@ -55,15 +52,12 @@ func TestExportImportPipe(t *testing.T) {
|
||||
})
|
||||
cli.ErrBuffer().Reset()
|
||||
cli.OutBuffer().Reset()
|
||||
assert.NilError(t, RunExport(cli, &ExportOptions{
|
||||
ContextName: "test",
|
||||
Dest: "-",
|
||||
}))
|
||||
assert.NilError(t, runExport(cli, "test", "-"))
|
||||
assert.Equal(t, cli.ErrBuffer().String(), "")
|
||||
cli.SetIn(streams.NewIn(io.NopCloser(bytes.NewBuffer(cli.OutBuffer().Bytes()))))
|
||||
cli.OutBuffer().Reset()
|
||||
cli.ErrBuffer().Reset()
|
||||
assert.NilError(t, RunImport(cli, "test2", "-"))
|
||||
assert.NilError(t, runImport(cli, "test2", "-"))
|
||||
context1, err := cli.ContextStore().GetMetadata("test")
|
||||
assert.NilError(t, err)
|
||||
context2, err := cli.ContextStore().GetMetadata("test2")
|
||||
@ -88,6 +82,6 @@ func TestExportExistingFile(t *testing.T) {
|
||||
cli := makeFakeCli(t)
|
||||
cli.ErrBuffer().Reset()
|
||||
assert.NilError(t, os.WriteFile(contextFile, []byte{}, 0o644))
|
||||
err := RunExport(cli, &ExportOptions{ContextName: "test", Dest: contextFile})
|
||||
err := runExport(cli, "test", contextFile)
|
||||
assert.Assert(t, os.IsExist(err))
|
||||
}
|
||||
|
||||
@ -13,6 +13,8 @@ import (
|
||||
)
|
||||
|
||||
// ExportOptions are the options used for exporting a context
|
||||
//
|
||||
// Deprecated: this type was for internal use and will be removed in the next release.
|
||||
type ExportOptions struct {
|
||||
ContextName string
|
||||
Dest string
|
||||
@ -24,15 +26,14 @@ func newExportCommand(dockerCLI command.Cli) *cobra.Command {
|
||||
Short: "Export a context to a tar archive FILE or a tar stream on STDOUT.",
|
||||
Args: cli.RequiresRangeArgs(1, 2),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
opts := &ExportOptions{
|
||||
ContextName: args[0],
|
||||
}
|
||||
contextName := args[0]
|
||||
var dest string
|
||||
if len(args) == 2 {
|
||||
opts.Dest = args[1]
|
||||
dest = args[1]
|
||||
} else {
|
||||
opts.Dest = opts.ContextName + ".dockercontext"
|
||||
dest = contextName + ".dockercontext"
|
||||
}
|
||||
return RunExport(dockerCLI, opts)
|
||||
return runExport(dockerCLI, contextName, dest)
|
||||
},
|
||||
ValidArgsFunction: completeContextNames(dockerCLI, 1, true),
|
||||
}
|
||||
@ -65,11 +66,21 @@ func writeTo(dockerCli command.Cli, reader io.Reader, dest string) error {
|
||||
}
|
||||
|
||||
// RunExport exports a Docker context
|
||||
//
|
||||
// Deprecated: this function was for internal use and will be removed in the next release.
|
||||
func RunExport(dockerCli command.Cli, opts *ExportOptions) error {
|
||||
if err := store.ValidateContextName(opts.ContextName); err != nil && opts.ContextName != command.DefaultContextName {
|
||||
if opts == nil {
|
||||
opts = &ExportOptions{}
|
||||
}
|
||||
return runExport(dockerCli, opts.ContextName, opts.Dest)
|
||||
}
|
||||
|
||||
// runExport exports a Docker context.
|
||||
func runExport(dockerCLI command.Cli, contextName string, dest string) error {
|
||||
if err := store.ValidateContextName(contextName); err != nil && contextName != command.DefaultContextName {
|
||||
return err
|
||||
}
|
||||
reader := store.Export(opts.ContextName, dockerCli.ContextStore())
|
||||
reader := store.Export(contextName, dockerCLI.ContextStore())
|
||||
defer reader.Close()
|
||||
return writeTo(dockerCli, reader, opts.Dest)
|
||||
return writeTo(dockerCLI, reader, dest)
|
||||
}
|
||||
|
||||
@ -18,7 +18,7 @@ func newImportCommand(dockerCli command.Cli) *cobra.Command {
|
||||
Short: "Import a context from a tar or zip file",
|
||||
Args: cli.ExactArgs(2),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return RunImport(dockerCli, args[0], args[1])
|
||||
return runImport(dockerCli, args[0], args[1])
|
||||
},
|
||||
// TODO(thaJeztah): this should also include "-"
|
||||
ValidArgsFunction: completion.FileNames,
|
||||
@ -27,14 +27,21 @@ func newImportCommand(dockerCli command.Cli) *cobra.Command {
|
||||
}
|
||||
|
||||
// RunImport imports a Docker context
|
||||
func RunImport(dockerCli command.Cli, name string, source string) error {
|
||||
if err := checkContextNameForCreation(dockerCli.ContextStore(), name); err != nil {
|
||||
//
|
||||
// Deprecated: this function was for internal use and will be removed in the next release.
|
||||
func RunImport(dockerCLI command.Cli, name string, source string) error {
|
||||
return runImport(dockerCLI, name, source)
|
||||
}
|
||||
|
||||
// runImport imports a Docker context.
|
||||
func runImport(dockerCLI command.Cli, name string, source string) error {
|
||||
if err := checkContextNameForCreation(dockerCLI.ContextStore(), name); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var reader io.Reader
|
||||
if source == "-" {
|
||||
reader = dockerCli.In()
|
||||
reader = dockerCLI.In()
|
||||
} else {
|
||||
f, err := os.Open(source)
|
||||
if err != nil {
|
||||
@ -44,11 +51,11 @@ func RunImport(dockerCli command.Cli, name string, source string) error {
|
||||
reader = f
|
||||
}
|
||||
|
||||
if err := store.Import(name, dockerCli.ContextStore(), reader); err != nil {
|
||||
if err := store.Import(name, dockerCLI.ContextStore(), reader); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, _ = fmt.Fprintln(dockerCli.Out(), name)
|
||||
_, _ = fmt.Fprintf(dockerCli.Err(), "Successfully imported context %q\n", name)
|
||||
_, _ = fmt.Fprintln(dockerCLI.Out(), name)
|
||||
_, _ = fmt.Fprintf(dockerCLI.Err(), "Successfully imported context %q\n", name)
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -10,7 +10,6 @@ import (
|
||||
|
||||
"github.com/docker/cli/cli"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/cli/cli/command/completion"
|
||||
"github.com/docker/cli/cli/command/formatter"
|
||||
"github.com/docker/cli/cli/context/docker"
|
||||
flagsHelper "github.com/docker/cli/cli/flags"
|
||||
@ -34,7 +33,7 @@ func newListCommand(dockerCli command.Cli) *cobra.Command {
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return runList(dockerCli, opts)
|
||||
},
|
||||
ValidArgsFunction: completion.NoComplete,
|
||||
ValidArgsFunction: cobra.NoFileCompletions,
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
|
||||
@ -19,10 +19,10 @@ func createTestContexts(t *testing.T, cli command.Cli, name ...string) {
|
||||
func createTestContext(t *testing.T, cli command.Cli, name string, metaData map[string]any) {
|
||||
t.Helper()
|
||||
|
||||
err := RunCreate(cli, &CreateOptions{
|
||||
Name: name,
|
||||
Description: "description of " + name,
|
||||
Docker: map[string]string{keyHost: "https://someswarmserver.example.com"},
|
||||
err := runCreate(cli, &createOptions{
|
||||
name: name,
|
||||
description: "description of " + name,
|
||||
endpoint: map[string]string{keyHost: "https://someswarmserver.example.com"},
|
||||
|
||||
metaData: metaData,
|
||||
})
|
||||
|
||||
@ -11,34 +11,48 @@ import (
|
||||
)
|
||||
|
||||
// RemoveOptions are the options used to remove contexts
|
||||
//
|
||||
// Deprecated: this type was for internal use and will be removed in the next release.
|
||||
type RemoveOptions struct {
|
||||
Force bool
|
||||
}
|
||||
|
||||
// removeOptions are the options used to remove contexts.
|
||||
type removeOptions struct {
|
||||
force bool
|
||||
}
|
||||
|
||||
func newRemoveCommand(dockerCLI command.Cli) *cobra.Command {
|
||||
var opts RemoveOptions
|
||||
var opts removeOptions
|
||||
cmd := &cobra.Command{
|
||||
Use: "rm CONTEXT [CONTEXT...]",
|
||||
Aliases: []string{"remove"},
|
||||
Short: "Remove one or more contexts",
|
||||
Args: cli.RequiresMinArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return RunRemove(dockerCLI, opts, args)
|
||||
return runRemove(dockerCLI, opts, args)
|
||||
},
|
||||
ValidArgsFunction: completeContextNames(dockerCLI, -1, false),
|
||||
}
|
||||
cmd.Flags().BoolVarP(&opts.Force, "force", "f", false, "Force the removal of a context in use")
|
||||
cmd.Flags().BoolVarP(&opts.force, "force", "f", false, "Force the removal of a context in use")
|
||||
return cmd
|
||||
}
|
||||
|
||||
// RunRemove removes one or more contexts
|
||||
func RunRemove(dockerCLI command.Cli, opts RemoveOptions, names []string) error {
|
||||
//
|
||||
// Deprecated: this function was for internal use and will be removed in the next release.
|
||||
func RunRemove(dockerCLI command.Cli, opts removeOptions, names []string) error {
|
||||
return runRemove(dockerCLI, opts, names)
|
||||
}
|
||||
|
||||
// runRemove removes one or more contexts.
|
||||
func runRemove(dockerCLI command.Cli, opts removeOptions, names []string) error {
|
||||
var errs []error
|
||||
currentCtx := dockerCLI.CurrentContext()
|
||||
for _, name := range names {
|
||||
if name == "default" {
|
||||
errs = append(errs, errors.New(`context "default" cannot be removed`))
|
||||
} else if err := doRemove(dockerCLI, name, name == currentCtx, opts.Force); err != nil {
|
||||
} else if err := doRemove(dockerCLI, name, name == currentCtx, opts.force); err != nil {
|
||||
errs = append(errs, err)
|
||||
} else {
|
||||
_, _ = fmt.Fprintln(dockerCLI.Out(), name)
|
||||
|
||||
@ -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"
|
||||
@ -14,20 +14,20 @@ import (
|
||||
func TestRemove(t *testing.T) {
|
||||
cli := makeFakeCli(t)
|
||||
createTestContexts(t, cli, "current", "other")
|
||||
assert.NilError(t, RunRemove(cli, RemoveOptions{}, []string{"other"}))
|
||||
assert.NilError(t, runRemove(cli, removeOptions{}, []string{"other"}))
|
||||
_, 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) {
|
||||
cli := makeFakeCli(t)
|
||||
createTestContexts(t, cli, "current", "other")
|
||||
err := RunRemove(cli, RemoveOptions{}, []string{"not-a-context"})
|
||||
err := runRemove(cli, removeOptions{}, []string{"not-a-context"})
|
||||
assert.ErrorContains(t, err, `context "not-a-context" does not exist`)
|
||||
|
||||
err = RunRemove(cli, RemoveOptions{Force: true}, []string{"not-a-context"})
|
||||
err = runRemove(cli, removeOptions{force: true}, []string{"not-a-context"})
|
||||
assert.NilError(t, err)
|
||||
}
|
||||
|
||||
@ -35,7 +35,7 @@ func TestRemoveCurrent(t *testing.T) {
|
||||
cli := makeFakeCli(t)
|
||||
createTestContexts(t, cli, "current", "other")
|
||||
cli.SetCurrentContext("current")
|
||||
err := RunRemove(cli, RemoveOptions{}, []string{"current"})
|
||||
err := runRemove(cli, removeOptions{}, []string{"current"})
|
||||
assert.ErrorContains(t, err, `context "current" is in use, set -f flag to force remove`)
|
||||
}
|
||||
|
||||
@ -49,7 +49,7 @@ func TestRemoveCurrentForce(t *testing.T) {
|
||||
cli := makeFakeCli(t, withCliConfig(testCfg))
|
||||
createTestContexts(t, cli, "current", "other")
|
||||
cli.SetCurrentContext("current")
|
||||
assert.NilError(t, RunRemove(cli, RemoveOptions{Force: true}, []string{"current"}))
|
||||
assert.NilError(t, runRemove(cli, removeOptions{force: true}, []string{"current"}))
|
||||
reloadedConfig, err := config.Load(configDir)
|
||||
assert.NilError(t, err)
|
||||
assert.Equal(t, "", reloadedConfig.CurrentContext)
|
||||
@ -59,6 +59,6 @@ func TestRemoveDefault(t *testing.T) {
|
||||
cli := makeFakeCli(t)
|
||||
createTestContext(t, cli, "other", nil)
|
||||
cli.SetCurrentContext("current")
|
||||
err := RunRemove(cli, RemoveOptions{}, []string{"default"})
|
||||
err := runRemove(cli, removeOptions{}, []string{"default"})
|
||||
assert.ErrorContains(t, err, `context "default" cannot be removed`)
|
||||
}
|
||||
|
||||
@ -5,7 +5,6 @@ import (
|
||||
|
||||
"github.com/docker/cli/cli"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/cli/cli/command/completion"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@ -19,7 +18,7 @@ func newShowCommand(dockerCli command.Cli) *cobra.Command {
|
||||
runShow(dockerCli)
|
||||
return nil
|
||||
},
|
||||
ValidArgsFunction: completion.NoComplete,
|
||||
ValidArgsFunction: cobra.NoFileCompletions,
|
||||
}
|
||||
return cmd
|
||||
}
|
||||
|
||||
@ -13,12 +13,21 @@ import (
|
||||
)
|
||||
|
||||
// UpdateOptions are the options used to update a context
|
||||
//
|
||||
// Deprecated: this type was for internal use and will be removed in the next release.
|
||||
type UpdateOptions struct {
|
||||
Name string
|
||||
Description string
|
||||
Docker map[string]string
|
||||
}
|
||||
|
||||
// updateOptions are the options used to update a context.
|
||||
type updateOptions struct {
|
||||
name string
|
||||
description string
|
||||
endpoint map[string]string
|
||||
}
|
||||
|
||||
func longUpdateDescription() string {
|
||||
buf := bytes.NewBuffer(nil)
|
||||
buf.WriteString("Update a context\n\nDocker endpoint config:\n\n")
|
||||
@ -33,31 +42,45 @@ func longUpdateDescription() string {
|
||||
}
|
||||
|
||||
func newUpdateCommand(dockerCLI command.Cli) *cobra.Command {
|
||||
opts := &UpdateOptions{}
|
||||
opts := updateOptions{}
|
||||
cmd := &cobra.Command{
|
||||
Use: "update [OPTIONS] CONTEXT",
|
||||
Short: "Update a context",
|
||||
Args: cli.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
opts.Name = args[0]
|
||||
return RunUpdate(dockerCLI, opts)
|
||||
opts.name = args[0]
|
||||
return runUpdate(dockerCLI, &opts)
|
||||
},
|
||||
Long: longUpdateDescription(),
|
||||
ValidArgsFunction: completeContextNames(dockerCLI, 1, false),
|
||||
}
|
||||
flags := cmd.Flags()
|
||||
flags.StringVar(&opts.Description, "description", "", "Description of the context")
|
||||
flags.StringToStringVar(&opts.Docker, "docker", nil, "set the docker endpoint")
|
||||
flags.StringVar(&opts.description, "description", "", "Description of the context")
|
||||
flags.StringToStringVar(&opts.endpoint, "docker", nil, "set the docker endpoint")
|
||||
return cmd
|
||||
}
|
||||
|
||||
// RunUpdate updates a Docker context
|
||||
//
|
||||
// Deprecated: this function was for internal use and will be removed in the next release.
|
||||
func RunUpdate(dockerCLI command.Cli, o *UpdateOptions) error {
|
||||
if err := store.ValidateContextName(o.Name); err != nil {
|
||||
if o == nil {
|
||||
o = &UpdateOptions{}
|
||||
}
|
||||
return runUpdate(dockerCLI, &updateOptions{
|
||||
name: o.Name,
|
||||
description: o.Description,
|
||||
endpoint: o.Docker,
|
||||
})
|
||||
}
|
||||
|
||||
// runUpdate updates a Docker context.
|
||||
func runUpdate(dockerCLI command.Cli, opts *updateOptions) error {
|
||||
if err := store.ValidateContextName(opts.name); err != nil {
|
||||
return err
|
||||
}
|
||||
s := dockerCLI.ContextStore()
|
||||
c, err := s.GetMetadata(o.Name)
|
||||
c, err := s.GetMetadata(opts.name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -65,16 +88,16 @@ func RunUpdate(dockerCLI command.Cli, o *UpdateOptions) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if o.Description != "" {
|
||||
dockerContext.Description = o.Description
|
||||
if opts.description != "" {
|
||||
dockerContext.Description = opts.description
|
||||
}
|
||||
|
||||
c.Metadata = dockerContext
|
||||
|
||||
tlsDataToReset := make(map[string]*store.EndpointTLSData)
|
||||
|
||||
if o.Docker != nil {
|
||||
dockerEP, dockerTLS, err := getDockerEndpointMetadataAndTLS(s, o.Docker)
|
||||
if opts.endpoint != nil {
|
||||
dockerEP, dockerTLS, err := getDockerEndpointMetadataAndTLS(s, opts.endpoint)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to create docker endpoint config: %w", err)
|
||||
}
|
||||
@ -88,13 +111,13 @@ func RunUpdate(dockerCLI command.Cli, o *UpdateOptions) error {
|
||||
return err
|
||||
}
|
||||
for ep, tlsData := range tlsDataToReset {
|
||||
if err := s.ResetEndpointTLSMaterial(o.Name, ep, tlsData); err != nil {
|
||||
if err := s.ResetEndpointTLSMaterial(opts.name, ep, tlsData); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
_, _ = fmt.Fprintln(dockerCLI.Out(), o.Name)
|
||||
_, _ = fmt.Fprintf(dockerCLI.Err(), "Successfully updated context %q\n", o.Name)
|
||||
_, _ = fmt.Fprintln(dockerCLI.Out(), opts.name)
|
||||
_, _ = fmt.Fprintf(dockerCLI.Err(), "Successfully updated context %q\n", opts.name)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@ -11,16 +11,16 @@ import (
|
||||
|
||||
func TestUpdateDescriptionOnly(t *testing.T) {
|
||||
cli := makeFakeCli(t)
|
||||
err := RunCreate(cli, &CreateOptions{
|
||||
Name: "test",
|
||||
Docker: map[string]string{},
|
||||
err := runCreate(cli, &createOptions{
|
||||
name: "test",
|
||||
endpoint: map[string]string{},
|
||||
})
|
||||
assert.NilError(t, err)
|
||||
cli.OutBuffer().Reset()
|
||||
cli.ErrBuffer().Reset()
|
||||
assert.NilError(t, RunUpdate(cli, &UpdateOptions{
|
||||
Name: "test",
|
||||
Description: "description",
|
||||
assert.NilError(t, runUpdate(cli, &updateOptions{
|
||||
name: "test",
|
||||
description: "description",
|
||||
}))
|
||||
c, err := cli.ContextStore().GetMetadata("test")
|
||||
assert.NilError(t, err)
|
||||
@ -35,9 +35,9 @@ func TestUpdateDescriptionOnly(t *testing.T) {
|
||||
func TestUpdateDockerOnly(t *testing.T) {
|
||||
cli := makeFakeCli(t)
|
||||
createTestContext(t, cli, "test", nil)
|
||||
assert.NilError(t, RunUpdate(cli, &UpdateOptions{
|
||||
Name: "test",
|
||||
Docker: map[string]string{
|
||||
assert.NilError(t, runUpdate(cli, &updateOptions{
|
||||
name: "test",
|
||||
endpoint: map[string]string{
|
||||
keyHost: "tcp://some-host",
|
||||
},
|
||||
}))
|
||||
@ -52,14 +52,14 @@ func TestUpdateDockerOnly(t *testing.T) {
|
||||
|
||||
func TestUpdateInvalidDockerHost(t *testing.T) {
|
||||
cli := makeFakeCli(t)
|
||||
err := RunCreate(cli, &CreateOptions{
|
||||
Name: "test",
|
||||
Docker: map[string]string{},
|
||||
err := runCreate(cli, &createOptions{
|
||||
name: "test",
|
||||
endpoint: map[string]string{},
|
||||
})
|
||||
assert.NilError(t, err)
|
||||
err = RunUpdate(cli, &UpdateOptions{
|
||||
Name: "test",
|
||||
Docker: map[string]string{
|
||||
err = runUpdate(cli, &updateOptions{
|
||||
name: "test",
|
||||
endpoint: map[string]string{
|
||||
keyHost: "some///invalid/host",
|
||||
},
|
||||
})
|
||||
|
||||
@ -17,7 +17,7 @@ func newUseCommand(dockerCLI command.Cli) *cobra.Command {
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
name := args[0]
|
||||
return RunUse(dockerCLI, name)
|
||||
return runUse(dockerCLI, name)
|
||||
},
|
||||
ValidArgsFunction: completeContextNames(dockerCLI, 1, false),
|
||||
}
|
||||
@ -25,7 +25,14 @@ func newUseCommand(dockerCLI command.Cli) *cobra.Command {
|
||||
}
|
||||
|
||||
// RunUse set the current Docker context
|
||||
//
|
||||
// Deprecated: this function was for internal use and will be removed in the next release.
|
||||
func RunUse(dockerCLI command.Cli, name string) error {
|
||||
return runUse(dockerCLI, name)
|
||||
}
|
||||
|
||||
// runUse set the current Docker context
|
||||
func runUse(dockerCLI command.Cli, name string) error {
|
||||
// configValue uses an empty string for "default"
|
||||
var configValue string
|
||||
if name != command.DefaultContextName {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user