Compare commits
196 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| baeda1f82a | |||
| 3e3677e47d | |||
| e814bd038d | |||
| 20e3951aeb | |||
| 643e2e50ae | |||
| 75d7ce92a2 | |||
| 9fdeb9c3de | |||
| a12c535f6e | |||
| d18a3e9004 | |||
| 932ca73874 | |||
| 7d51e65e72 | |||
| d85ef84533 | |||
| d1f9546dc3 | |||
| 1ea8d69d6f | |||
| e82aa85741 | |||
| e9176b36cc | |||
| a39f3fbdfd | |||
| 2fa0c6253a | |||
| bc6ff39e42 | |||
| 3fa7a8654f | |||
| 18e275c52e | |||
| 3e06ce8bfa | |||
| b40c2f6b5d | |||
| 93eead45ee | |||
| bd04f199f7 | |||
| 2484f7e046 | |||
| 45075ea08c | |||
| c2dcaecf19 | |||
| 613b9362d0 | |||
| b30d250320 | |||
| fe0cdaf027 | |||
| 6b25bc3003 | |||
| 0785bd743d | |||
| bdac0b38d9 | |||
| c70b01ec1f | |||
| 0389090aeb | |||
| c904936d69 | |||
| 386d50c2e9 | |||
| 990186f2f6 | |||
| 86bf1966e2 | |||
| b3022b91d1 | |||
| f14ba9f5d7 | |||
| ae45936575 | |||
| c189c4dbea | |||
| 0c46ffc1f9 | |||
| 6be9ce798e | |||
| c8b7ef1e29 | |||
| 779ed309a8 | |||
| 2f7e84be65 | |||
| e628209d9b | |||
| 80a3add604 | |||
| 715cfc4c2f | |||
| 80fb0d575e | |||
| 9d28e08a2d | |||
| d72bef2088 | |||
| 100c70180f | |||
| 7502d7e560 | |||
| 4d718932c1 | |||
| 826eaafa6d | |||
| 308624c3b1 | |||
| de7d866b6a | |||
| aa7e414fdc | |||
| 240e4b5501 | |||
| 5d4776bd90 | |||
| 1841277463 | |||
| 49e9c2ae3d | |||
| 87a3ce2699 | |||
| 1d8abed17d | |||
| fd82621d35 | |||
| e5ca16bda7 | |||
| 31dad66f9a | |||
| 113c5b526a | |||
| c73edb36ca | |||
| 80f673bf9e | |||
| 3d4cc8e699 | |||
| 30277a8f80 | |||
| 994ed4085b | |||
| b10a23a46f | |||
| 26d906094a | |||
| 9c091c4346 | |||
| a13500b745 | |||
| cfef3a7dc1 | |||
| 53426025c3 | |||
| 573a664639 | |||
| cf0ab7ac4c | |||
| d05fd4ffc8 | |||
| 870f138250 | |||
| 7ffc243093 | |||
| 198d6b8724 | |||
| 55a14ec851 | |||
| 1f9a0df05a | |||
| 4ae338b33a | |||
| 6380142dd4 | |||
| 82f422fcf3 | |||
| 80fd77903b | |||
| c3d4d623c8 | |||
| 2e82d11def | |||
| 738a6ee1cc | |||
| 246d96bb6c | |||
| 2fd0f17057 | |||
| 5fa500000a | |||
| 1e6a8ce2b7 | |||
| 81d965569d | |||
| 6f7a931a2d | |||
| 16169434a4 | |||
| 91bab605f7 | |||
| a224086349 | |||
| a282e0c5d2 | |||
| f3764ff5f9 | |||
| 700364e304 | |||
| 62d27c32ff | |||
| c0e952cf04 | |||
| 04104a04d3 | |||
| b721998b7b | |||
| 4065e1246e | |||
| f1002eb9fb | |||
| e97c7b240e | |||
| aa78937634 | |||
| 40fe0573aa | |||
| c9737e1c37 | |||
| 5c6723d080 | |||
| 59a8a0906f | |||
| 3f9857e6a8 | |||
| fd5fc61ecd | |||
| 3624019d83 | |||
| 96f2cf80ab | |||
| f3ff8e6ad6 | |||
| ee1ac1b319 | |||
| e91ed5707e | |||
| 38dd744a11 | |||
| cedc602a78 | |||
| 4de40a825e | |||
| dea9396e18 | |||
| 03fa8f92c8 | |||
| b485636f4b | |||
| 241523cc1a | |||
| 9989fdbc40 | |||
| 0e20c1fd21 | |||
| c4cb29837f | |||
| 0b56256638 | |||
| 1c0927a041 | |||
| 82f9d5921b | |||
| adb01ca79d | |||
| 8260476a06 | |||
| aa5b6b7728 | |||
| 859e303655 | |||
| bce2e1f953 | |||
| 44064f51c8 | |||
| 292779add5 | |||
| f2e79b826c | |||
| fa46b92361 | |||
| 400f81089a | |||
| c72057c8db | |||
| 77db97d595 | |||
| cbf0d2b7b7 | |||
| d0014a86bc | |||
| 6c1c8b55aa | |||
| c2ea9bc90b | |||
| 44fdac11f5 | |||
| 893e52cf4b | |||
| 061051c24d | |||
| 62eae52c2a | |||
| 2012fbf111 | |||
| 42d1c02750 | |||
| 3967b7d28e | |||
| 0b924e51fc | |||
| 6288e8b1ac | |||
| 1e9575e81a | |||
| c98e9c47ca | |||
| a6f6b5fa34 | |||
| 8437cfefae | |||
| 68a5ca859f | |||
| f9d091f4b1 | |||
| e9b8231d6a | |||
| 8a64739631 | |||
| a555c853b0 | |||
| 260ba1a8a2 | |||
| f63cb8b97e | |||
| c1492eabde | |||
| 48e6b44379 | |||
| bfcd17b5b7 | |||
| 8279b718ea | |||
| 644c003606 | |||
| 0d17280a30 | |||
| eedfe50a99 | |||
| f3dd1ee6c1 | |||
| c7cf60f657 | |||
| 1d37fb3027 | |||
| 0793f96394 | |||
| b639ea8b89 | |||
| 063e3dd329 | |||
| 0168626037 | |||
| 00ea8bdc41 | |||
| e3a9a92b14 | |||
| 6da4ee40c7 | |||
| ab733b5564 |
@ -4,13 +4,13 @@ jobs:
|
||||
|
||||
lint:
|
||||
working_directory: /work
|
||||
docker: [{image: 'docker:19.03-git'}]
|
||||
docker: [{image: 'docker:20.10-git'}]
|
||||
environment:
|
||||
DOCKER_BUILDKIT: 1
|
||||
steps:
|
||||
- checkout
|
||||
- setup_remote_docker:
|
||||
version: 19.03.12
|
||||
version: 20.10.6
|
||||
reusable: true
|
||||
exclusive: false
|
||||
- run:
|
||||
@ -39,15 +39,15 @@ jobs:
|
||||
|
||||
cross:
|
||||
working_directory: /work
|
||||
docker: [{image: 'docker:19.03-git'}]
|
||||
docker: [{image: 'docker:20.10-git'}]
|
||||
environment:
|
||||
DOCKER_BUILDKIT: 1
|
||||
BUILDX_VERSION: "v0.5.1"
|
||||
BUILDX_VERSION: "v0.8.2"
|
||||
parallelism: 3
|
||||
steps:
|
||||
- checkout
|
||||
- setup_remote_docker:
|
||||
version: 19.03.12
|
||||
version: 20.10.6
|
||||
reusable: true
|
||||
exclusive: false
|
||||
- run:
|
||||
@ -69,13 +69,13 @@ jobs:
|
||||
|
||||
test:
|
||||
working_directory: /work
|
||||
docker: [{image: 'docker:19.03-git'}]
|
||||
docker: [{image: 'docker:20.10-git'}]
|
||||
environment:
|
||||
DOCKER_BUILDKIT: 1
|
||||
steps:
|
||||
- checkout
|
||||
- setup_remote_docker:
|
||||
version: 19.03.12
|
||||
version: 20.10.6
|
||||
reusable: true
|
||||
exclusive: false
|
||||
- run:
|
||||
@ -116,13 +116,13 @@ jobs:
|
||||
|
||||
validate:
|
||||
working_directory: /work
|
||||
docker: [{image: 'docker:19.03-git'}]
|
||||
docker: [{image: 'docker:20.10-git'}]
|
||||
environment:
|
||||
DOCKER_BUILDKIT: 1
|
||||
steps:
|
||||
- checkout
|
||||
- setup_remote_docker:
|
||||
version: 19.03.12
|
||||
version: 20.10.6
|
||||
reusable: true
|
||||
exclusive: false
|
||||
- run:
|
||||
|
||||
@ -5,12 +5,10 @@ linters:
|
||||
- dogsled
|
||||
- gocyclo
|
||||
- goimports
|
||||
- golint
|
||||
- gosec
|
||||
- gosimple
|
||||
- govet
|
||||
- ineffassign
|
||||
- interfacer
|
||||
- lll
|
||||
- megacheck
|
||||
- misspell
|
||||
@ -21,6 +19,7 @@ linters:
|
||||
- unconvert
|
||||
- unparam
|
||||
- unused
|
||||
- revive
|
||||
- varcheck
|
||||
|
||||
disable:
|
||||
@ -54,30 +53,65 @@ issues:
|
||||
- parameter .* always receives
|
||||
|
||||
exclude-rules:
|
||||
# These are copied from the default exclude rules, except for "ineffective break statement"
|
||||
# and GoDoc checks.
|
||||
# https://github.com/golangci/golangci-lint/blob/0cc87df732aaf1d5ad9ce9ca538d38d916918b36/pkg/config/config.go#L36
|
||||
- text: "Error return value of .((os\\.)?std(out|err)\\..*|.*Close|.*Flush|os\\.Remove(All)?|.*printf?|os\\.(Un)?Setenv). is not checked"
|
||||
# We prefer to use an "exclude-list" so that new "default" exclusions are not
|
||||
# automatically inherited. We can decide whether or not to follow upstream
|
||||
# defaults when updating golang-ci-lint versions.
|
||||
# Unfortunately, this means we have to copy the whole exclusion pattern, as
|
||||
# (unlike the "include" option), the "exclude" option does not take exclusion
|
||||
# ID's.
|
||||
#
|
||||
# These exclusion patterns are copied from the default excluses at:
|
||||
# https://github.com/golangci/golangci-lint/blob/v1.44.0/pkg/config/issues.go#L10-L104
|
||||
|
||||
# EXC0001
|
||||
- text: "Error return value of .((os\\.)?std(out|err)\\..*|.*Close|.*Flush|os\\.Remove(All)?|.*print(f|ln)?|os\\.(Un)?Setenv). is not checked"
|
||||
linters:
|
||||
- errcheck
|
||||
# EXC0003
|
||||
- text: "func name will be used as test\\.Test.* by other packages, and that stutters; consider calling this"
|
||||
linters:
|
||||
- golint
|
||||
- text: "G103: Use of unsafe calls should be audited"
|
||||
- revive
|
||||
# EXC0006
|
||||
- text: "Use of unsafe calls should be audited"
|
||||
linters:
|
||||
- gosec
|
||||
- text: "G104: Errors unhandled"
|
||||
# EXC0007
|
||||
- text: "Subprocess launch(ed with variable|ing should be audited)"
|
||||
linters:
|
||||
- gosec
|
||||
- text: "G204: Subprocess launch(ed with (variable|function call)|ing should be audited)"
|
||||
# EXC0008
|
||||
# TODO: evaluate these and fix where needed: G307: Deferring unsafe method "*os.File" on type "Close" (gosec)
|
||||
- text: "(G104|G307)"
|
||||
linters:
|
||||
- gosec
|
||||
- text: "(G301|G302): (Expect directory permissions to be 0750 or less|Expect file permissions to be 0600 or less)"
|
||||
# EXC0009
|
||||
- text: "(Expect directory permissions to be 0750 or less|Expect file permissions to be 0600 or less)"
|
||||
linters:
|
||||
- gosec
|
||||
- text: "G304: Potential file inclusion via variable"
|
||||
# EXC0010
|
||||
- text: "Potential file inclusion via variable"
|
||||
linters:
|
||||
- gosec
|
||||
- text: "(G201|G202): SQL string (formatting|concatenation)"
|
||||
|
||||
# Looks like the match in "EXC0007" above doesn't catch this one
|
||||
# TODO: consider upstreaming this to golangci-lint's default exclusion rules
|
||||
- text: "G204: Subprocess launched with a potential tainted input or cmd arguments"
|
||||
linters:
|
||||
- gosec
|
||||
# Looks like the match in "EXC0009" above doesn't catch this one
|
||||
# TODO: consider upstreaming this to golangci-lint's default exclusion rules
|
||||
- text: "G306: Expect WriteFile permissions to be 0600 or less"
|
||||
linters:
|
||||
- gosec
|
||||
|
||||
# Exclude some linters from running on tests files.
|
||||
- path: _test\.go
|
||||
linters:
|
||||
- errcheck
|
||||
- gosec
|
||||
|
||||
# Maximum issues count per one linter. Set to 0 to disable. Default is 50.
|
||||
max-issues-per-linter: 0
|
||||
|
||||
# Maximum count of issues with the same text. Set to 0 to disable. Default is 3.
|
||||
max-same-issues: 0
|
||||
|
||||
24
Dockerfile
24
Dockerfile
@ -1,22 +1,12 @@
|
||||
#syntax=docker/dockerfile:1.2
|
||||
# syntax=docker/dockerfile:1
|
||||
|
||||
ARG BASE_VARIANT=alpine
|
||||
ARG GO_VERSION=1.13.15
|
||||
ARG GO_VERSION=1.18.7
|
||||
ARG XX_VERSION=1.1.0
|
||||
|
||||
FROM --platform=$BUILDPLATFORM golang:${GO_VERSION}-${BASE_VARIANT} AS gostable
|
||||
FROM --platform=$BUILDPLATFORM golang:1.16-${BASE_VARIANT} AS golatest
|
||||
FROM --platform=$BUILDPLATFORM tonistiigi/xx:${XX_VERSION} AS xx
|
||||
|
||||
FROM gostable AS go-linux
|
||||
FROM golatest AS go-darwin
|
||||
FROM golatest AS go-windows-amd64
|
||||
FROM golatest AS go-windows-386
|
||||
FROM golatest AS go-windows-arm
|
||||
FROM --platform=$BUILDPLATFORM tonistiigi/golang:497feff1-${BASE_VARIANT} AS go-windows-arm64
|
||||
FROM go-windows-${TARGETARCH} AS go-windows
|
||||
|
||||
FROM --platform=$BUILDPLATFORM tonistiigi/xx@sha256:620d36a9d7f1e3b102a5c7e8eff12081ac363828b3a44390f24fa8da2d49383d AS xx
|
||||
|
||||
FROM go-${TARGETOS} AS build-base-alpine
|
||||
FROM --platform=$BUILDPLATFORM golang:${GO_VERSION}-${BASE_VARIANT} AS build-base-alpine
|
||||
COPY --from=xx / /
|
||||
RUN apk add --no-cache clang lld llvm file git
|
||||
WORKDIR /go/src/github.com/docker/cli
|
||||
@ -26,7 +16,7 @@ ARG TARGETPLATFORM
|
||||
# gcc is installed for libgcc only
|
||||
RUN xx-apk add --no-cache musl-dev gcc
|
||||
|
||||
FROM go-${TARGETOS} AS build-base-buster
|
||||
FROM --platform=$BUILDPLATFORM golang:${GO_VERSION}-buster AS build-base-buster
|
||||
COPY --from=xx / /
|
||||
RUN apt-get update && apt-get install --no-install-recommends -y clang lld file
|
||||
WORKDIR /go/src/github.com/docker/cli
|
||||
@ -54,7 +44,7 @@ RUN --mount=ro --mount=type=cache,target=/root/.cache \
|
||||
TARGET=/out ./scripts/build/binary && \
|
||||
xx-verify $([ "$GO_LINKMODE" = "static" ] && echo "--static") /out/docker
|
||||
|
||||
FROM build-base-${BASE_VARIANT} AS dev
|
||||
FROM build-base-${BASE_VARIANT} AS dev
|
||||
COPY . .
|
||||
|
||||
FROM scratch AS binary
|
||||
|
||||
6
Jenkinsfile
vendored
6
Jenkinsfile
vendored
@ -1,6 +1,6 @@
|
||||
pipeline {
|
||||
agent {
|
||||
label "linux && x86_64"
|
||||
label "amd64 && ubuntu-1804 && overlay2"
|
||||
}
|
||||
|
||||
options {
|
||||
@ -21,9 +21,9 @@ pipeline {
|
||||
make -f docker.Makefile test-e2e-non-experimental"
|
||||
}
|
||||
}
|
||||
stage("e2e (non-experimental) - 18.09 engine") {
|
||||
stage("e2e (non-experimental) - 19.03 engine") {
|
||||
steps {
|
||||
sh "E2E_ENGINE_VERSION=18.09-dind \
|
||||
sh "E2E_ENGINE_VERSION=19.03-dind \
|
||||
E2E_UNIQUE_ID=clie2e${BUILD_NUMBER} \
|
||||
IMAGE_TAG=clie2e${BUILD_NUMBER} \
|
||||
make -f docker.Makefile test-e2e-non-experimental"
|
||||
|
||||
@ -4,7 +4,7 @@ clone_folder: c:\gopath\src\github.com\docker\cli
|
||||
|
||||
environment:
|
||||
GOPATH: c:\gopath
|
||||
GOVERSION: 1.13.15
|
||||
GOVERSION: 1.18.7
|
||||
DEPVERSION: v0.4.1
|
||||
|
||||
install:
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
package manager
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
package manager
|
||||
|
||||
@ -255,7 +255,7 @@ func (cli *DockerCli) Initialize(opts *cliflags.ClientOptions, ops ...Initialize
|
||||
if tlsconfig.IsErrEncryptedKey(err) {
|
||||
passRetriever := passphrase.PromptRetrieverWithInOut(cli.In(), cli.Out(), nil)
|
||||
newClient := func(password string) (client.APIClient, error) {
|
||||
cli.dockerEndpoint.TLSPassword = password
|
||||
cli.dockerEndpoint.TLSPassword = password //nolint: staticcheck // SA1019: cli.dockerEndpoint.TLSPassword is deprecated
|
||||
return newAPIClientFromEndpoint(cli.dockerEndpoint, cli.configFile)
|
||||
}
|
||||
cli.client, err = getClientWithPassword(passRetriever, newClient)
|
||||
|
||||
@ -6,7 +6,6 @@ import (
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"runtime"
|
||||
"testing"
|
||||
@ -80,24 +79,6 @@ func TestNewAPIClientFromFlagsWithAPIVersionFromEnv(t *testing.T) {
|
||||
assert.Check(t, is.Equal(customVersion, apiclient.ClientVersion()))
|
||||
}
|
||||
|
||||
func TestNewAPIClientFromFlagsWithHttpProxyEnv(t *testing.T) {
|
||||
defer env.Patch(t, "HTTP_PROXY", "http://proxy.acme.com:1234")()
|
||||
defer env.Patch(t, "DOCKER_HOST", "tcp://docker.acme.com:2376")()
|
||||
|
||||
opts := &flags.CommonOptions{}
|
||||
configFile := &configfile.ConfigFile{}
|
||||
apiclient, err := NewAPIClientFromFlags(opts, configFile)
|
||||
assert.NilError(t, err)
|
||||
transport, ok := apiclient.HTTPClient().Transport.(*http.Transport)
|
||||
assert.Assert(t, ok)
|
||||
assert.Assert(t, transport.Proxy != nil)
|
||||
request, err := http.NewRequest(http.MethodGet, "tcp://docker.acme.com:2376", nil)
|
||||
assert.NilError(t, err)
|
||||
url, err := transport.Proxy(request)
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.Equal("http://proxy.acme.com:1234", url.String()))
|
||||
}
|
||||
|
||||
type fakeClient struct {
|
||||
client.Client
|
||||
pingFunc func() (types.Ping, error)
|
||||
|
||||
@ -19,13 +19,11 @@ func TestConfigContextFormatWrite(t *testing.T) {
|
||||
// Errors
|
||||
{
|
||||
formatter.Context{Format: "{{InvalidFunction}}"},
|
||||
`Template parsing error: template: :1: function "InvalidFunction" not defined
|
||||
`,
|
||||
`template parsing error: template: :1: function "InvalidFunction" not defined`,
|
||||
},
|
||||
{
|
||||
formatter.Context{Format: "{{nil}}"},
|
||||
`Template parsing error: template: :1:2: executing "" at <nil>: nil is not a command
|
||||
`,
|
||||
`template parsing error: template: :1:2: executing "" at <nil>: nil is not a command`,
|
||||
},
|
||||
// Table format
|
||||
{formatter.Context{Format: NewFormat("table", false)},
|
||||
|
||||
@ -36,7 +36,7 @@ func TestConfigInspectErrors(t *testing.T) {
|
||||
flags: map[string]string{
|
||||
"format": "{{invalid format}}",
|
||||
},
|
||||
expectedError: "Template parsing error",
|
||||
expectedError: "template parsing error",
|
||||
},
|
||||
{
|
||||
args: []string{"foo", "bar"},
|
||||
|
||||
@ -155,7 +155,7 @@ func (cid *cidFile) Close() error {
|
||||
return nil
|
||||
}
|
||||
if err := os.Remove(cid.path); err != nil {
|
||||
return errors.Errorf("failed to remove the CID file '%s': %s \n", cid.path, err)
|
||||
return errors.Wrapf(err, "failed to remove the CID file '%s'", cid.path)
|
||||
}
|
||||
|
||||
return nil
|
||||
@ -166,7 +166,7 @@ func (cid *cidFile) Write(id string) error {
|
||||
return nil
|
||||
}
|
||||
if _, err := cid.file.Write([]byte(id)); err != nil {
|
||||
return errors.Errorf("Failed to write the container ID to the file: %s", err)
|
||||
return errors.Wrap(err, "failed to write the container ID to the file")
|
||||
}
|
||||
cid.written = true
|
||||
return nil
|
||||
@ -177,12 +177,12 @@ func newCIDFile(path string) (*cidFile, error) {
|
||||
return &cidFile{}, nil
|
||||
}
|
||||
if _, err := os.Stat(path); err == nil {
|
||||
return nil, errors.Errorf("Container ID file found, make sure the other container isn't running or delete %s", path)
|
||||
return nil, errors.Errorf("container ID file found, make sure the other container isn't running or delete %s", path)
|
||||
}
|
||||
|
||||
f, err := os.Create(path)
|
||||
if err != nil {
|
||||
return nil, errors.Errorf("Failed to create the container ID file: %s", err)
|
||||
return nil, errors.Wrap(err, "failed to create the container ID file")
|
||||
}
|
||||
|
||||
return &cidFile{path: path, file: f}, nil
|
||||
|
||||
@ -39,7 +39,7 @@ func TestNewCIDFileWhenFileAlreadyExists(t *testing.T) {
|
||||
defer tempfile.Remove()
|
||||
|
||||
_, err := newCIDFile(tempfile.Path())
|
||||
assert.ErrorContains(t, err, "Container ID file found")
|
||||
assert.ErrorContains(t, err, "container ID file found")
|
||||
}
|
||||
|
||||
func TestCIDFileCloseWithNoWrite(t *testing.T) {
|
||||
@ -133,7 +133,7 @@ func TestCreateContainerImagePullPolicy(t *testing.T) {
|
||||
return ioutil.NopCloser(strings.NewReader("")), nil
|
||||
},
|
||||
infoFunc: func() (types.Info, error) {
|
||||
return types.Info{IndexServerAddress: "http://indexserver"}, nil
|
||||
return types.Info{IndexServerAddress: "https://indexserver.example.com"}, nil
|
||||
},
|
||||
}
|
||||
cli := test.NewFakeCli(client)
|
||||
|
||||
@ -181,14 +181,14 @@ func (c *statsContext) ID() string {
|
||||
|
||||
func (c *statsContext) CPUPerc() string {
|
||||
if c.s.IsInvalid {
|
||||
return fmt.Sprintf("--")
|
||||
return "--"
|
||||
}
|
||||
return fmt.Sprintf("%.2f%%", c.s.CPUPercentage)
|
||||
}
|
||||
|
||||
func (c *statsContext) MemUsage() string {
|
||||
if c.s.IsInvalid {
|
||||
return fmt.Sprintf("-- / --")
|
||||
return "-- / --"
|
||||
}
|
||||
if c.os == winOSType {
|
||||
return units.BytesSize(c.s.Memory)
|
||||
@ -198,28 +198,28 @@ func (c *statsContext) MemUsage() string {
|
||||
|
||||
func (c *statsContext) MemPerc() string {
|
||||
if c.s.IsInvalid || c.os == winOSType {
|
||||
return fmt.Sprintf("--")
|
||||
return "--"
|
||||
}
|
||||
return fmt.Sprintf("%.2f%%", c.s.MemoryPercentage)
|
||||
}
|
||||
|
||||
func (c *statsContext) NetIO() string {
|
||||
if c.s.IsInvalid {
|
||||
return fmt.Sprintf("--")
|
||||
return "--"
|
||||
}
|
||||
return fmt.Sprintf("%s / %s", units.HumanSizeWithPrecision(c.s.NetworkRx, 3), units.HumanSizeWithPrecision(c.s.NetworkTx, 3))
|
||||
}
|
||||
|
||||
func (c *statsContext) BlockIO() string {
|
||||
if c.s.IsInvalid {
|
||||
return fmt.Sprintf("--")
|
||||
return "--"
|
||||
}
|
||||
return fmt.Sprintf("%s / %s", units.HumanSizeWithPrecision(c.s.BlockRead, 3), units.HumanSizeWithPrecision(c.s.BlockWrite, 3))
|
||||
}
|
||||
|
||||
func (c *statsContext) PIDs() string {
|
||||
if c.s.IsInvalid || c.os == winOSType {
|
||||
return fmt.Sprintf("--")
|
||||
return "--"
|
||||
}
|
||||
return fmt.Sprintf("%d", c.s.PidsCurrent)
|
||||
}
|
||||
|
||||
@ -54,13 +54,11 @@ func TestContainerStatsContextWrite(t *testing.T) {
|
||||
}{
|
||||
{
|
||||
formatter.Context{Format: "{{InvalidFunction}}"},
|
||||
`Template parsing error: template: :1: function "InvalidFunction" not defined
|
||||
`,
|
||||
`template parsing error: template: :1: function "InvalidFunction" not defined`,
|
||||
},
|
||||
{
|
||||
formatter.Context{Format: "{{nil}}"},
|
||||
`Template parsing error: template: :1:2: executing "" at <nil>: nil is not a command
|
||||
`,
|
||||
`template parsing error: template: :1:2: executing "" at <nil>: nil is not a command`,
|
||||
},
|
||||
{
|
||||
formatter.Context{Format: "table {{.MemUsage}}"},
|
||||
|
||||
@ -14,13 +14,17 @@ import (
|
||||
)
|
||||
|
||||
func TestRemoveForce(t *testing.T) {
|
||||
var removed []string
|
||||
var (
|
||||
removed1 []string
|
||||
removed2 []string
|
||||
)
|
||||
|
||||
cli := test.NewFakeCli(&fakeClient{
|
||||
containerRemoveFunc: func(ctx context.Context, container string, options types.ContainerRemoveOptions) error {
|
||||
removed = append(removed, container)
|
||||
removed1 = append(removed1, container)
|
||||
removed2 = append(removed2, container)
|
||||
if container == "nosuchcontainer" {
|
||||
return errdefs.NotFound(fmt.Errorf("Error: No such container: " + container))
|
||||
return errdefs.NotFound(fmt.Errorf("Error: no such container: " + container))
|
||||
}
|
||||
return nil
|
||||
},
|
||||
@ -31,16 +35,16 @@ func TestRemoveForce(t *testing.T) {
|
||||
|
||||
t.Run("without force", func(t *testing.T) {
|
||||
cmd.SetArgs([]string{"nosuchcontainer", "mycontainer"})
|
||||
removed = []string{}
|
||||
assert.ErrorContains(t, cmd.Execute(), "No such container")
|
||||
sort.Strings(removed)
|
||||
assert.DeepEqual(t, removed, []string{"mycontainer", "nosuchcontainer"})
|
||||
removed1 = []string{}
|
||||
assert.ErrorContains(t, cmd.Execute(), "no such container")
|
||||
sort.Strings(removed1)
|
||||
assert.DeepEqual(t, removed1, []string{"mycontainer", "nosuchcontainer"})
|
||||
})
|
||||
t.Run("with force", func(t *testing.T) {
|
||||
cmd.SetArgs([]string{"--force", "nosuchcontainer", "mycontainer"})
|
||||
removed = []string{}
|
||||
removed2 = []string{}
|
||||
assert.NilError(t, cmd.Execute())
|
||||
sort.Strings(removed)
|
||||
assert.DeepEqual(t, removed, []string{"mycontainer", "nosuchcontainer"})
|
||||
sort.Strings(removed2)
|
||||
assert.DeepEqual(t, removed2, []string{"mycontainer", "nosuchcontainer"})
|
||||
})
|
||||
}
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
package container
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
package container
|
||||
|
||||
@ -62,8 +62,11 @@ func newCreateCommand(dockerCli command.Cli) *cobra.Command {
|
||||
&opts.DefaultStackOrchestrator,
|
||||
"default-stack-orchestrator", "",
|
||||
"Default orchestrator for stack operations to use with this context (swarm|kubernetes|all)")
|
||||
flags.SetAnnotation("default-stack-orchestrator", "deprecated", nil)
|
||||
flags.StringToStringVar(&opts.Docker, "docker", nil, "set the docker endpoint")
|
||||
flags.StringToStringVar(&opts.Kubernetes, "kubernetes", nil, "set the kubernetes endpoint")
|
||||
flags.SetAnnotation("kubernetes", "kubernetes", nil)
|
||||
flags.SetAnnotation("kubernetes", "deprecated", nil)
|
||||
flags.StringVar(&opts.From, "from", "", "create context from a named context")
|
||||
return cmd
|
||||
}
|
||||
|
||||
@ -169,7 +169,7 @@ func validateTestKubeEndpoint(t *testing.T, s store.Reader, name string) {
|
||||
kubeMeta := ctxMetadata.Endpoints[kubernetes.KubernetesEndpoint].(kubernetes.EndpointMeta)
|
||||
kubeEP, err := kubeMeta.WithTLSData(s, name)
|
||||
assert.NilError(t, err)
|
||||
assert.Equal(t, "https://someserver", kubeEP.Host)
|
||||
assert.Equal(t, "https://someserver.example.com", kubeEP.Host)
|
||||
assert.Equal(t, "the-ca", string(kubeEP.TLSData.CA))
|
||||
assert.Equal(t, "the-cert", string(kubeEP.TLSData.Cert))
|
||||
assert.Equal(t, "the-key", string(kubeEP.TLSData.Key))
|
||||
@ -287,7 +287,7 @@ func TestCreateFromContext(t *testing.T) {
|
||||
assert.Equal(t, newContextTyped.Description, c.expectedDescription)
|
||||
assert.Equal(t, newContextTyped.StackOrchestrator, c.expectedOrchestrator)
|
||||
assert.Equal(t, dockerEndpoint.Host, "tcp://42.42.42.42:2375")
|
||||
assert.Equal(t, kubeEndpoint.Host, "https://someserver")
|
||||
assert.Equal(t, kubeEndpoint.Host, "https://someserver.example.com")
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -361,7 +361,7 @@ func TestCreateFromCurrent(t *testing.T) {
|
||||
assert.Equal(t, newContextTyped.Description, c.expectedDescription)
|
||||
assert.Equal(t, newContextTyped.StackOrchestrator, c.expectedOrchestrator)
|
||||
assert.Equal(t, dockerEndpoint.Host, "tcp://42.42.42.42:2375")
|
||||
assert.Equal(t, kubeEndpoint.Host, "https://someserver")
|
||||
assert.Equal(t, kubeEndpoint.Host, "https://someserver.example.com")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -46,6 +46,8 @@ func newExportCommand(dockerCli command.Cli) *cobra.Command {
|
||||
|
||||
flags := cmd.Flags()
|
||||
flags.BoolVar(&opts.Kubeconfig, "kubeconfig", false, "Export as a kubeconfig file")
|
||||
flags.SetAnnotation("kubeconfig", "kubernetes", nil)
|
||||
flags.SetAnnotation("kubeconfig", "deprecated", nil)
|
||||
return cmd
|
||||
}
|
||||
|
||||
|
||||
@ -18,7 +18,7 @@ func createTestContextWithKubeAndSwarm(t *testing.T, cli command.Cli, name strin
|
||||
DefaultStackOrchestrator: orchestrator,
|
||||
Description: "description of " + name,
|
||||
Kubernetes: map[string]string{keyFrom: "default"},
|
||||
Docker: map[string]string{keyHost: "https://someswarmserver"},
|
||||
Docker: map[string]string{keyHost: "https://someswarmserver.example.com"},
|
||||
})
|
||||
assert.NilError(t, err)
|
||||
}
|
||||
|
||||
4
cli/command/context/testdata/inspect.golden
vendored
4
cli/command/context/testdata/inspect.golden
vendored
@ -7,11 +7,11 @@
|
||||
},
|
||||
"Endpoints": {
|
||||
"docker": {
|
||||
"Host": "https://someswarmserver",
|
||||
"Host": "https://someswarmserver.example.com",
|
||||
"SkipTLSVerify": false
|
||||
},
|
||||
"kubernetes": {
|
||||
"Host": "https://someserver",
|
||||
"Host": "https://someserver.example.com",
|
||||
"SkipTLSVerify": false,
|
||||
"DefaultNamespace": "default"
|
||||
}
|
||||
|
||||
10
cli/command/context/testdata/list.golden
vendored
10
cli/command/context/testdata/list.golden
vendored
@ -1,5 +1,5 @@
|
||||
NAME DESCRIPTION DOCKER ENDPOINT KUBERNETES ENDPOINT ORCHESTRATOR
|
||||
current * description of current https://someswarmserver https://someserver (default) all
|
||||
default Current DOCKER_HOST based configuration unix:///var/run/docker.sock swarm
|
||||
other description of other https://someswarmserver https://someserver (default) all
|
||||
unset description of unset https://someswarmserver https://someserver (default)
|
||||
NAME DESCRIPTION DOCKER ENDPOINT KUBERNETES ENDPOINT ORCHESTRATOR
|
||||
current * description of current https://someswarmserver.example.com https://someserver.example.com (default) all
|
||||
default Current DOCKER_HOST based configuration unix:///var/run/docker.sock swarm
|
||||
other description of other https://someswarmserver.example.com https://someserver.example.com (default) all
|
||||
unset description of unset https://someswarmserver.example.com https://someserver.example.com (default)
|
||||
|
||||
2
cli/command/context/testdata/test-kubeconfig
vendored
2
cli/command/context/testdata/test-kubeconfig
vendored
@ -2,7 +2,7 @@ apiVersion: v1
|
||||
clusters:
|
||||
- cluster:
|
||||
certificate-authority-data: dGhlLWNh
|
||||
server: https://someserver
|
||||
server: https://someserver.example.com
|
||||
name: test-cluster
|
||||
contexts:
|
||||
- context:
|
||||
|
||||
@ -61,8 +61,11 @@ func newUpdateCommand(dockerCli command.Cli) *cobra.Command {
|
||||
&opts.DefaultStackOrchestrator,
|
||||
"default-stack-orchestrator", "",
|
||||
"Default orchestrator for stack operations to use with this context (swarm|kubernetes|all)")
|
||||
flags.SetAnnotation("default-stack-orchestrator", "deprecated", nil)
|
||||
flags.StringToStringVar(&opts.Docker, "docker", nil, "set the docker endpoint")
|
||||
flags.StringToStringVar(&opts.Kubernetes, "kubernetes", nil, "set the kubernetes endpoint")
|
||||
flags.SetAnnotation("kubernetes", "kubernetes", nil)
|
||||
flags.SetAnnotation("kubernetes", "deprecated", nil)
|
||||
return cmd
|
||||
}
|
||||
|
||||
|
||||
@ -130,13 +130,11 @@ func TestContainerContextWrite(t *testing.T) {
|
||||
// Errors
|
||||
{
|
||||
Context{Format: "{{InvalidFunction}}"},
|
||||
`Template parsing error: template: :1: function "InvalidFunction" not defined
|
||||
`,
|
||||
`template parsing error: template: :1: function "InvalidFunction" not defined`,
|
||||
},
|
||||
{
|
||||
Context{Format: "{{nil}}"},
|
||||
`Template parsing error: template: :1:2: executing "" at <nil>: nil is not a command
|
||||
`,
|
||||
`template parsing error: template: :1:2: executing "" at <nil>: nil is not a command`,
|
||||
},
|
||||
// Table Format
|
||||
{
|
||||
|
||||
@ -61,8 +61,7 @@ CACHE ID CACHE TYPE SIZE CREATED LAST USED USAGE SHARED
|
||||
Format: "{{InvalidFunction}}",
|
||||
},
|
||||
},
|
||||
`Template parsing error: template: :1: function "InvalidFunction" not defined
|
||||
`,
|
||||
`template parsing error: template: :1: function "InvalidFunction" not defined`,
|
||||
},
|
||||
{
|
||||
DiskUsageContext{
|
||||
@ -70,8 +69,7 @@ CACHE ID CACHE TYPE SIZE CREATED LAST USED USAGE SHARED
|
||||
Format: "{{nil}}",
|
||||
},
|
||||
},
|
||||
`Template parsing error: template: :1:2: executing "" at <nil>: nil is not a command
|
||||
`,
|
||||
`template parsing error: template: :1:2: executing "" at <nil>: nil is not a command`,
|
||||
},
|
||||
// Table Format
|
||||
{
|
||||
|
||||
@ -64,7 +64,7 @@ func (c *Context) preFormat() {
|
||||
func (c *Context) parseFormat() (*template.Template, error) {
|
||||
tmpl, err := templates.Parse(c.finalFormat)
|
||||
if err != nil {
|
||||
return tmpl, errors.Errorf("Template parsing error: %v\n", err)
|
||||
return tmpl, errors.Wrap(err, "template parsing error")
|
||||
}
|
||||
return tmpl, err
|
||||
}
|
||||
@ -85,7 +85,7 @@ func (c *Context) postFormat(tmpl *template.Template, subContext SubContext) {
|
||||
|
||||
func (c *Context) contextFormat(tmpl *template.Template, subContext SubContext) error {
|
||||
if err := tmpl.Execute(c.buffer, subContext); err != nil {
|
||||
return errors.Errorf("Template parsing error: %v\n", err)
|
||||
return errors.Wrap(err, "template parsing error")
|
||||
}
|
||||
if c.Format.IsTable() && c.header != nil {
|
||||
c.header = subContext.FullHeader()
|
||||
|
||||
@ -119,8 +119,7 @@ func TestImageContextWrite(t *testing.T) {
|
||||
Format: "{{InvalidFunction}}",
|
||||
},
|
||||
},
|
||||
`Template parsing error: template: :1: function "InvalidFunction" not defined
|
||||
`,
|
||||
`template parsing error: template: :1: function "InvalidFunction" not defined`,
|
||||
},
|
||||
{
|
||||
ImageContext{
|
||||
@ -128,8 +127,7 @@ func TestImageContextWrite(t *testing.T) {
|
||||
Format: "{{nil}}",
|
||||
},
|
||||
},
|
||||
`Template parsing error: template: :1:2: executing "" at <nil>: nil is not a command
|
||||
`,
|
||||
`template parsing error: template: :1:2: executing "" at <nil>: nil is not a command`,
|
||||
},
|
||||
// Table Format
|
||||
{
|
||||
|
||||
@ -62,13 +62,11 @@ func TestVolumeContextWrite(t *testing.T) {
|
||||
// Errors
|
||||
{
|
||||
Context{Format: "{{InvalidFunction}}"},
|
||||
`Template parsing error: template: :1: function "InvalidFunction" not defined
|
||||
`,
|
||||
`template parsing error: template: :1: function "InvalidFunction" not defined`,
|
||||
},
|
||||
{
|
||||
Context{Format: "{{nil}}"},
|
||||
`Template parsing error: template: :1:2: executing "" at <nil>: nil is not a command
|
||||
`,
|
||||
`template parsing error: template: :1:2: executing "" at <nil>: nil is not a command`,
|
||||
},
|
||||
// Table format
|
||||
{
|
||||
|
||||
@ -297,7 +297,7 @@ func runBuild(dockerCli command.Cli, options buildOptions) error {
|
||||
}
|
||||
|
||||
if err := build.ValidateContextDirectory(contextDir, excludes); err != nil {
|
||||
return errors.Errorf("error checking context: '%s'.", err)
|
||||
return errors.Wrap(err, "error checking context")
|
||||
}
|
||||
|
||||
// And canonicalize dockerfile name to a platform-independent one
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
package build
|
||||
|
||||
@ -1,5 +1,3 @@
|
||||
// +build windows
|
||||
|
||||
package build
|
||||
|
||||
import (
|
||||
|
||||
@ -15,17 +15,17 @@ import (
|
||||
)
|
||||
|
||||
func TestENVTrustServer(t *testing.T) {
|
||||
defer env.PatchAll(t, map[string]string{"DOCKER_CONTENT_TRUST_SERVER": "https://notary-test.com:5000"})()
|
||||
defer env.PatchAll(t, map[string]string{"DOCKER_CONTENT_TRUST_SERVER": "https://notary-test.example.com:5000"})()
|
||||
indexInfo := ®istrytypes.IndexInfo{Name: "testserver"}
|
||||
output, err := trust.Server(indexInfo)
|
||||
expectedStr := "https://notary-test.com:5000"
|
||||
expectedStr := "https://notary-test.example.com:5000"
|
||||
if err != nil || output != expectedStr {
|
||||
t.Fatalf("Expected server to be %s, got %s", expectedStr, output)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHTTPENVTrustServer(t *testing.T) {
|
||||
defer env.PatchAll(t, map[string]string{"DOCKER_CONTENT_TRUST_SERVER": "http://notary-test.com:5000"})()
|
||||
defer env.PatchAll(t, map[string]string{"DOCKER_CONTENT_TRUST_SERVER": "http://notary-test.example.com:5000"})()
|
||||
indexInfo := ®istrytypes.IndexInfo{Name: "testserver"}
|
||||
_, err := trust.Server(indexInfo)
|
||||
if err == nil {
|
||||
|
||||
@ -44,7 +44,7 @@ func NewTemplateInspectorFromString(out io.Writer, tmplStr string) (Inspector, e
|
||||
|
||||
tmpl, err := templates.Parse(tmplStr)
|
||||
if err != nil {
|
||||
return nil, errors.Errorf("Template parsing error: %s", err)
|
||||
return nil, errors.Errorf("template parsing error: %s", err)
|
||||
}
|
||||
return NewTemplateInspector(out, tmpl), nil
|
||||
}
|
||||
@ -94,7 +94,7 @@ func (i *TemplateInspector) Inspect(typedElement interface{}, rawElement []byte)
|
||||
buffer := new(bytes.Buffer)
|
||||
if err := i.tmpl.Execute(buffer, typedElement); err != nil {
|
||||
if rawElement == nil {
|
||||
return errors.Errorf("Template parsing error: %v", err)
|
||||
return errors.Errorf("template parsing error: %v", err)
|
||||
}
|
||||
return i.tryRawInspectFallback(rawElement)
|
||||
}
|
||||
@ -118,7 +118,7 @@ func (i *TemplateInspector) tryRawInspectFallback(rawElement []byte) error {
|
||||
|
||||
tmplMissingKey := i.tmpl.Option("missingkey=error")
|
||||
if rawErr := tmplMissingKey.Execute(buffer, raw); rawErr != nil {
|
||||
return errors.Errorf("Template parsing error: %v", rawErr)
|
||||
return errors.Errorf("template parsing error: %v", rawErr)
|
||||
}
|
||||
|
||||
i.buffer.Write(buffer.Bytes())
|
||||
|
||||
@ -62,7 +62,7 @@ func TestTemplateInspectorTemplateError(t *testing.T) {
|
||||
t.Fatal("Expected error got nil")
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(err.Error(), "Template parsing error") {
|
||||
if !strings.HasPrefix(err.Error(), "template parsing error") {
|
||||
t.Fatalf("Expected template error, got %v", err)
|
||||
}
|
||||
}
|
||||
@ -98,7 +98,7 @@ func TestTemplateInspectorRawFallbackError(t *testing.T) {
|
||||
t.Fatal("Expected error got nil")
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(err.Error(), "Template parsing error") {
|
||||
if !strings.HasPrefix(err.Error(), "template parsing error") {
|
||||
t.Fatalf("Expected template error, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
@ -79,13 +79,11 @@ func TestNetworkContextWrite(t *testing.T) {
|
||||
// Errors
|
||||
{
|
||||
formatter.Context{Format: "{{InvalidFunction}}"},
|
||||
`Template parsing error: template: :1: function "InvalidFunction" not defined
|
||||
`,
|
||||
`template parsing error: template: :1: function "InvalidFunction" not defined`,
|
||||
},
|
||||
{
|
||||
formatter.Context{Format: "{{nil}}"},
|
||||
`Template parsing error: template: :1:2: executing "" at <nil>: nil is not a command
|
||||
`,
|
||||
`template parsing error: template: :1:2: executing "" at <nil>: nil is not a command`,
|
||||
},
|
||||
// Table format
|
||||
{
|
||||
|
||||
@ -62,15 +62,13 @@ func TestNodeContextWrite(t *testing.T) {
|
||||
|
||||
// Errors
|
||||
{
|
||||
context: formatter.Context{Format: "{{InvalidFunction}}"},
|
||||
expected: `Template parsing error: template: :1: function "InvalidFunction" not defined
|
||||
`,
|
||||
context: formatter.Context{Format: "{{InvalidFunction}}"},
|
||||
expected: `template parsing error: template: :1: function "InvalidFunction" not defined`,
|
||||
clusterInfo: swarm.ClusterInfo{TLSInfo: swarm.TLSInfo{TrustRoot: "hi"}},
|
||||
},
|
||||
{
|
||||
context: formatter.Context{Format: "{{nil}}"},
|
||||
expected: `Template parsing error: template: :1:2: executing "" at <nil>: nil is not a command
|
||||
`,
|
||||
context: formatter.Context{Format: "{{nil}}"},
|
||||
expected: `template parsing error: template: :1:2: executing "" at <nil>: nil is not a command`,
|
||||
clusterInfo: swarm.ClusterInfo{TLSInfo: swarm.TLSInfo{TrustRoot: "hi"}},
|
||||
},
|
||||
// Table format
|
||||
|
||||
@ -59,13 +59,11 @@ func TestPluginContextWrite(t *testing.T) {
|
||||
// Errors
|
||||
{
|
||||
formatter.Context{Format: "{{InvalidFunction}}"},
|
||||
`Template parsing error: template: :1: function "InvalidFunction" not defined
|
||||
`,
|
||||
`template parsing error: template: :1: function "InvalidFunction" not defined`,
|
||||
},
|
||||
{
|
||||
formatter.Context{Format: "{{nil}}"},
|
||||
`Template parsing error: template: :1:2: executing "" at <nil>: nil is not a command
|
||||
`,
|
||||
`template parsing error: template: :1:2: executing "" at <nil>: nil is not a command`,
|
||||
},
|
||||
// Table format
|
||||
{
|
||||
|
||||
@ -61,7 +61,7 @@ func TestInspectErrors(t *testing.T) {
|
||||
flags: map[string]string{
|
||||
"format": "{{invalid format}}",
|
||||
},
|
||||
expectedError: "Template parsing error",
|
||||
expectedError: "template parsing error",
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@ -41,7 +41,7 @@ func TestListErrors(t *testing.T) {
|
||||
flags: map[string]string{
|
||||
"format": "{{invalid format}}",
|
||||
},
|
||||
expectedError: "Template parsing error",
|
||||
expectedError: "template parsing error",
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@ -63,17 +63,14 @@ func RegistryAuthenticationPrivilegedFunc(cli Cli, index *registrytypes.IndexInf
|
||||
indexServer := registry.GetAuthConfigKey(index)
|
||||
isDefaultRegistry := indexServer == ElectAuthServer(context.Background(), cli)
|
||||
authConfig, err := GetDefaultAuthConfig(cli, true, indexServer, isDefaultRegistry)
|
||||
if authConfig == nil {
|
||||
authConfig = &types.AuthConfig{}
|
||||
}
|
||||
if err != nil {
|
||||
fmt.Fprintf(cli.Err(), "Unable to retrieve stored credentials for %s, error: %s.\n", indexServer, err)
|
||||
}
|
||||
err = ConfigureAuth(cli, "", "", authConfig, isDefaultRegistry)
|
||||
err = ConfigureAuth(cli, "", "", &authConfig, isDefaultRegistry)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return EncodeAuthToBase64(*authConfig)
|
||||
return EncodeAuthToBase64(authConfig)
|
||||
}
|
||||
}
|
||||
|
||||
@ -92,7 +89,7 @@ func ResolveAuthConfig(ctx context.Context, cli Cli, index *registrytypes.IndexI
|
||||
|
||||
// GetDefaultAuthConfig gets the default auth config given a serverAddress
|
||||
// If credentials for given serverAddress exists in the credential store, the configuration will be populated with values in it
|
||||
func GetDefaultAuthConfig(cli Cli, checkCredStore bool, serverAddress string, isDefaultRegistry bool) (*types.AuthConfig, error) {
|
||||
func GetDefaultAuthConfig(cli Cli, checkCredStore bool, serverAddress string, isDefaultRegistry bool) (types.AuthConfig, error) {
|
||||
if !isDefaultRegistry {
|
||||
serverAddress = registry.ConvertToHostname(serverAddress)
|
||||
}
|
||||
@ -101,13 +98,15 @@ func GetDefaultAuthConfig(cli Cli, checkCredStore bool, serverAddress string, is
|
||||
if checkCredStore {
|
||||
authconfig, err = cli.ConfigFile().GetAuthConfig(serverAddress)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return types.AuthConfig{
|
||||
ServerAddress: serverAddress,
|
||||
}, err
|
||||
}
|
||||
}
|
||||
authconfig.ServerAddress = serverAddress
|
||||
authconfig.IdentityToken = ""
|
||||
res := types.AuthConfig(authconfig)
|
||||
return &res, nil
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// ConfigureAuth handles prompting of user's username and password if needed
|
||||
|
||||
@ -112,13 +112,11 @@ func TestSearchContextWrite(t *testing.T) {
|
||||
// Errors
|
||||
{
|
||||
formatter.Context{Format: "{{InvalidFunction}}"},
|
||||
`Template parsing error: template: :1: function "InvalidFunction" not defined
|
||||
`,
|
||||
`template parsing error: template: :1: function "InvalidFunction" not defined`,
|
||||
},
|
||||
{
|
||||
formatter.Context{Format: "{{nil}}"},
|
||||
`Template parsing error: template: :1:2: executing "" at <nil>: nil is not a command
|
||||
`,
|
||||
`template parsing error: template: :1:2: executing "" at <nil>: nil is not a command`,
|
||||
},
|
||||
// Table format
|
||||
{
|
||||
|
||||
@ -114,22 +114,19 @@ func runLogin(dockerCli command.Cli, opts loginOptions) error { //nolint: gocycl
|
||||
var response registrytypes.AuthenticateOKBody
|
||||
isDefaultRegistry := serverAddress == authServer
|
||||
authConfig, err := command.GetDefaultAuthConfig(dockerCli, opts.user == "" && opts.password == "", serverAddress, isDefaultRegistry)
|
||||
if authConfig == nil {
|
||||
authConfig = &types.AuthConfig{}
|
||||
}
|
||||
if err == nil && authConfig.Username != "" && authConfig.Password != "" {
|
||||
response, err = loginWithCredStoreCreds(ctx, dockerCli, authConfig)
|
||||
response, err = loginWithCredStoreCreds(ctx, dockerCli, &authConfig)
|
||||
}
|
||||
if err != nil || authConfig.Username == "" || authConfig.Password == "" {
|
||||
err = command.ConfigureAuth(dockerCli, opts.user, opts.password, authConfig, isDefaultRegistry)
|
||||
err = command.ConfigureAuth(dockerCli, opts.user, opts.password, &authConfig, isDefaultRegistry)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
response, err = clnt.RegistryLogin(ctx, *authConfig)
|
||||
response, err = clnt.RegistryLogin(ctx, authConfig)
|
||||
if err != nil && client.IsErrConnectionFailed(err) {
|
||||
// If the server isn't responding (yet) attempt to login purely client side
|
||||
response, err = loginClientSide(ctx, *authConfig)
|
||||
response, err = loginClientSide(ctx, authConfig)
|
||||
}
|
||||
// If we (still) have an error, give up
|
||||
if err != nil {
|
||||
@ -152,7 +149,7 @@ func runLogin(dockerCli command.Cli, opts loginOptions) error { //nolint: gocycl
|
||||
}
|
||||
}
|
||||
|
||||
if err := creds.Store(configtypes.AuthConfig(*authConfig)); err != nil {
|
||||
if err := creds.Store(configtypes.AuthConfig(authConfig)); err != nil {
|
||||
return errors.Errorf("Error saving credentials: %v", err)
|
||||
}
|
||||
|
||||
|
||||
@ -66,10 +66,10 @@ func TestElectAuthServer(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
expectedAuthServer: "https://foo.bar",
|
||||
expectedAuthServer: "https://foo.example.com",
|
||||
expectedWarning: "",
|
||||
infoFunc: func() (types.Info, error) {
|
||||
return types.Info{IndexServerAddress: "https://foo.bar"}, nil
|
||||
return types.Info{IndexServerAddress: "https://foo.example.com"}, nil
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -145,7 +145,21 @@ func TestGetDefaultAuthConfig(t *testing.T) {
|
||||
assert.Check(t, is.Equal(tc.expectedErr, err.Error()))
|
||||
} else {
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.DeepEqual(tc.expectedAuthConfig, *authconfig))
|
||||
assert.Check(t, is.DeepEqual(tc.expectedAuthConfig, authconfig))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetDefaultAuthConfig_HelperError(t *testing.T) {
|
||||
cli := test.NewFakeCli(&fakeClient{})
|
||||
errBuf := new(bytes.Buffer)
|
||||
cli.SetErr(errBuf)
|
||||
cli.ConfigFile().CredentialsStore = "fake-does-not-exist"
|
||||
serverAddress := "test-server-address"
|
||||
expectedAuthConfig := types.AuthConfig{
|
||||
ServerAddress: serverAddress,
|
||||
}
|
||||
authconfig, err := GetDefaultAuthConfig(cli, true, serverAddress, serverAddress == "https://index.docker.io/v1/")
|
||||
assert.Check(t, is.DeepEqual(expectedAuthConfig, authconfig))
|
||||
assert.Check(t, is.ErrorContains(err, "docker-credential-fake-does-not-exist"))
|
||||
}
|
||||
|
||||
@ -19,13 +19,11 @@ func TestSecretContextFormatWrite(t *testing.T) {
|
||||
// Errors
|
||||
{
|
||||
formatter.Context{Format: "{{InvalidFunction}}"},
|
||||
`Template parsing error: template: :1: function "InvalidFunction" not defined
|
||||
`,
|
||||
`template parsing error: template: :1: function "InvalidFunction" not defined`,
|
||||
},
|
||||
{
|
||||
formatter.Context{Format: "{{nil}}"},
|
||||
`Template parsing error: template: :1:2: executing "" at <nil>: nil is not a command
|
||||
`,
|
||||
`template parsing error: template: :1:2: executing "" at <nil>: nil is not a command`,
|
||||
},
|
||||
// Table format
|
||||
{formatter.Context{Format: NewFormat("table", false)},
|
||||
|
||||
@ -36,7 +36,7 @@ func TestSecretInspectErrors(t *testing.T) {
|
||||
flags: map[string]string{
|
||||
"format": "{{invalid format}}",
|
||||
},
|
||||
expectedError: "Template parsing error",
|
||||
expectedError: "template parsing error",
|
||||
},
|
||||
{
|
||||
args: []string{"foo", "bar"},
|
||||
|
||||
@ -29,13 +29,11 @@ func TestServiceContextWrite(t *testing.T) {
|
||||
// Errors
|
||||
{
|
||||
formatter.Context{Format: "{{InvalidFunction}}"},
|
||||
`Template parsing error: template: :1: function "InvalidFunction" not defined
|
||||
`,
|
||||
`template parsing error: template: :1: function "InvalidFunction" not defined`,
|
||||
},
|
||||
{
|
||||
formatter.Context{Format: "{{nil}}"},
|
||||
`Template parsing error: template: :1:2: executing "" at <nil>: nil is not a command
|
||||
`,
|
||||
`template parsing error: template: :1:2: executing "" at <nil>: nil is not a command`,
|
||||
},
|
||||
// Table format
|
||||
{
|
||||
|
||||
@ -914,7 +914,7 @@ func addServiceFlags(flags *pflag.FlagSet, opts *serviceOptions, defaultFlagValu
|
||||
}
|
||||
|
||||
const (
|
||||
flagCredentialSpec = "credential-spec"
|
||||
flagCredentialSpec = "credential-spec" //nolint:gosec // ignore G101: Potential hardcoded credentials
|
||||
flagPlacementPref = "placement-pref"
|
||||
flagPlacementPrefAdd = "placement-pref-add"
|
||||
flagPlacementPrefRemove = "placement-pref-rm"
|
||||
|
||||
@ -69,7 +69,9 @@ func NewStackCommand(dockerCli command.Cli) *cobra.Command {
|
||||
flags := cmd.PersistentFlags()
|
||||
flags.String("kubeconfig", "", "Kubernetes config file")
|
||||
flags.SetAnnotation("kubeconfig", "kubernetes", nil)
|
||||
flags.SetAnnotation("kubeconfig", "deprecated", nil)
|
||||
flags.String("orchestrator", "", "Orchestrator to use (swarm|kubernetes|all)")
|
||||
flags.SetAnnotation("orchestrator", "deprecated", nil)
|
||||
return cmd
|
||||
}
|
||||
|
||||
|
||||
@ -16,13 +16,11 @@ func TestStackContextWrite(t *testing.T) {
|
||||
// Errors
|
||||
{
|
||||
formatter.Context{Format: "{{InvalidFunction}}"},
|
||||
`Template parsing error: template: :1: function "InvalidFunction" not defined
|
||||
`,
|
||||
`template parsing error: template: :1: function "InvalidFunction" not defined`,
|
||||
},
|
||||
{
|
||||
formatter.Context{Format: "{{nil}}"},
|
||||
`Template parsing error: template: :1:2: executing "" at <nil>: nil is not a command
|
||||
`,
|
||||
`template parsing error: template: :1:2: executing "" at <nil>: nil is not a command`,
|
||||
},
|
||||
// Table format
|
||||
{
|
||||
|
||||
@ -50,6 +50,7 @@ func NewOptions(flags *flag.FlagSet, orchestrator command.Orchestrator) Options
|
||||
func AddNamespaceFlag(flags *flag.FlagSet) {
|
||||
flags.String("namespace", "", "Kubernetes namespace to use")
|
||||
flags.SetAnnotation("namespace", "kubernetes", nil)
|
||||
flags.SetAnnotation("namespace", "deprecated", nil)
|
||||
}
|
||||
|
||||
// WrapCli wraps command.Cli with kubernetes specifics
|
||||
|
||||
@ -30,8 +30,10 @@ func newListCommand(dockerCli command.Cli, common *commonOptions) *cobra.Command
|
||||
flags.StringVar(&opts.Format, "format", "", "Pretty-print stacks using a Go template")
|
||||
flags.StringSliceVar(&opts.Namespaces, "namespace", []string{}, "Kubernetes namespaces to use")
|
||||
flags.SetAnnotation("namespace", "kubernetes", nil)
|
||||
flags.SetAnnotation("namespace", "deprecated", nil)
|
||||
flags.BoolVarP(&opts.AllNamespaces, "all-namespaces", "", false, "List stacks from all Kubernetes namespaces")
|
||||
flags.SetAnnotation("all-namespaces", "kubernetes", nil)
|
||||
flags.SetAnnotation("all-namespaces", "deprecated", nil)
|
||||
return cmd
|
||||
}
|
||||
|
||||
|
||||
@ -33,7 +33,7 @@ func TestListErrors(t *testing.T) {
|
||||
flags: map[string]string{
|
||||
"format": "{{invalid format}}",
|
||||
},
|
||||
expectedError: "Template parsing error",
|
||||
expectedError: "template parsing error",
|
||||
},
|
||||
{
|
||||
serviceListFunc: func(options types.ServiceListOptions) ([]swarm.Service, error) {
|
||||
|
||||
@ -28,6 +28,7 @@ func LoadComposefile(dockerCli command.Cli, opts options.Deploy) (*composetypes.
|
||||
config, err := loader.Load(configDetails)
|
||||
if err != nil {
|
||||
if fpe, ok := err.(*loader.ForbiddenPropertiesError); ok {
|
||||
//nolint:revive // ignore capitalization error; this error is intentionally formatted multi-line
|
||||
return nil, errors.Errorf("Compose file contains unsupported options:\n\n%s\n",
|
||||
propertyWarnings(fpe.Properties))
|
||||
}
|
||||
|
||||
@ -62,7 +62,7 @@ func TestStackServicesErrors(t *testing.T) {
|
||||
serviceListFunc: func(options types.ServiceListOptions) ([]swarm.Service, error) {
|
||||
return []swarm.Service{*Service()}, nil
|
||||
},
|
||||
expectedError: "Template parsing error",
|
||||
expectedError: "template parsing error",
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@ -104,20 +104,20 @@ func TestDisplayTrustRootInvalidFlags(t *testing.T) {
|
||||
errorMsg: "flag requires the `--rotate` flag to update the CA",
|
||||
},
|
||||
{
|
||||
args: []string{"--external-ca=protocol=cfssl,url=https://some.com/https/url"},
|
||||
args: []string{"--external-ca=protocol=cfssl,url=https://some.example.com/https/url"},
|
||||
errorMsg: "flag requires the `--rotate` flag to update the CA",
|
||||
},
|
||||
{ // to make sure we're not erroring because we didn't provide a CA cert and external CA
|
||||
args: []string{
|
||||
"--ca-cert=" + tmpfile,
|
||||
"--external-ca=protocol=cfssl,url=https://some.com/https/url",
|
||||
"--external-ca=protocol=cfssl,url=https://some.example.com/https/url",
|
||||
},
|
||||
errorMsg: "flag requires the `--rotate` flag to update the CA",
|
||||
},
|
||||
{
|
||||
args: []string{
|
||||
"--rotate",
|
||||
"--external-ca=protocol=cfssl,url=https://some.com/https/url",
|
||||
"--external-ca=protocol=cfssl,url=https://some.example.com/https/url",
|
||||
},
|
||||
errorMsg: "rotating to an external CA requires the `--ca-cert` flag to specify the external CA's cert - " +
|
||||
"to add an external CA with the current root CA certificate, use the `update` command instead",
|
||||
@ -243,7 +243,7 @@ func TestUpdateSwarmSpecCertAndExternalCA(t *testing.T) {
|
||||
"--rotate",
|
||||
"--detach",
|
||||
"--ca-cert=" + certfile,
|
||||
"--external-ca=protocol=cfssl,url=https://some.external.ca"})
|
||||
"--external-ca=protocol=cfssl,url=https://some.external.ca.example.com"})
|
||||
cmd.SetOut(cli.OutBuffer())
|
||||
assert.NilError(t, cmd.Execute())
|
||||
|
||||
@ -253,7 +253,7 @@ func TestUpdateSwarmSpecCertAndExternalCA(t *testing.T) {
|
||||
expected.CAConfig.ExternalCAs = []*swarm.ExternalCA{
|
||||
{
|
||||
Protocol: swarm.ExternalCAProtocolCFSSL,
|
||||
URL: "https://some.external.ca",
|
||||
URL: "https://some.external.ca.example.com",
|
||||
CACert: cert,
|
||||
Options: make(map[string]string),
|
||||
},
|
||||
@ -281,7 +281,7 @@ func TestUpdateSwarmSpecCertAndKeyAndExternalCA(t *testing.T) {
|
||||
"--detach",
|
||||
"--ca-cert=" + certfile,
|
||||
"--ca-key=" + keyfile,
|
||||
"--external-ca=protocol=cfssl,url=https://some.external.ca"})
|
||||
"--external-ca=protocol=cfssl,url=https://some.external.ca.example.com"})
|
||||
cmd.SetOut(cli.OutBuffer())
|
||||
assert.NilError(t, cmd.Execute())
|
||||
|
||||
@ -291,7 +291,7 @@ func TestUpdateSwarmSpecCertAndKeyAndExternalCA(t *testing.T) {
|
||||
expected.CAConfig.ExternalCAs = []*swarm.ExternalCA{
|
||||
{
|
||||
Protocol: swarm.ExternalCAProtocolCFSSL,
|
||||
URL: "https://some.external.ca",
|
||||
URL: "https://some.external.ca.example.com",
|
||||
CACert: cert,
|
||||
Options: make(map[string]string),
|
||||
},
|
||||
|
||||
@ -14,6 +14,7 @@ import (
|
||||
"github.com/docker/cli/templates"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/swarm"
|
||||
"github.com/docker/docker/api/types/versions"
|
||||
"github.com/docker/go-units"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
@ -211,9 +212,6 @@ func prettyPrintServerInfo(dockerCli command.Cli, info types.Info) []error {
|
||||
for _, o := range so.Options {
|
||||
switch o.Key {
|
||||
case "profile":
|
||||
if o.Value != "default" {
|
||||
fmt.Fprintln(dockerCli.Err(), " WARNING: You're not using the default seccomp profile")
|
||||
}
|
||||
fmt.Fprintln(dockerCli.Out(), " Profile:", o.Value)
|
||||
}
|
||||
}
|
||||
@ -378,6 +376,9 @@ func printSwarmInfo(dockerCli command.Cli, info types.Info) {
|
||||
}
|
||||
|
||||
func printServerWarnings(dockerCli command.Cli, info types.Info) {
|
||||
if versions.LessThan(dockerCli.Client().ClientVersion(), "1.42") {
|
||||
printSecurityOptionsWarnings(dockerCli, info)
|
||||
}
|
||||
if len(info.Warnings) > 0 {
|
||||
fmt.Fprintln(dockerCli.Err(), strings.Join(info.Warnings, "\n"))
|
||||
return
|
||||
@ -387,6 +388,29 @@ func printServerWarnings(dockerCli command.Cli, info types.Info) {
|
||||
printServerWarningsLegacy(dockerCli, info)
|
||||
}
|
||||
|
||||
// printSecurityOptionsWarnings prints warnings based on the security options
|
||||
// returned by the daemon.
|
||||
// DEPRECATED: warnings are now generated by the daemon, and returned in
|
||||
// info.Warnings. This function is used to provide backward compatibility with
|
||||
// daemons that do not provide these warnings. No new warnings should be added
|
||||
// here.
|
||||
func printSecurityOptionsWarnings(dockerCli command.Cli, info types.Info) {
|
||||
if info.OSType == "windows" {
|
||||
return
|
||||
}
|
||||
kvs, _ := types.DecodeSecurityOptions(info.SecurityOptions)
|
||||
for _, so := range kvs {
|
||||
if so.Name != "seccomp" {
|
||||
continue
|
||||
}
|
||||
for _, o := range so.Options {
|
||||
if o.Key == "profile" && o.Value != "default" && o.Value != "builtin" {
|
||||
_, _ = fmt.Fprintln(dockerCli.Err(), "WARNING: You're not using the default seccomp profile")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// printServerWarningsLegacy generates warnings based on information returned by the daemon.
|
||||
// DEPRECATED: warnings are now generated by the daemon, and returned in
|
||||
// info.Warnings. This function is used to provide backward compatibility with
|
||||
@ -482,7 +506,7 @@ func formatInfo(dockerCli command.Cli, info info, format string) error {
|
||||
tmpl, err := templates.Parse(format)
|
||||
if err != nil {
|
||||
return cli.StatusError{StatusCode: 64,
|
||||
Status: "Template parsing error: " + err.Error()}
|
||||
Status: "template parsing error: " + err.Error()}
|
||||
}
|
||||
err = tmpl.Execute(dockerCli.Out(), info)
|
||||
dockerCli.Out().Write([]byte{'\n'})
|
||||
|
||||
@ -394,7 +394,7 @@ func TestFormatInfo(t *testing.T) {
|
||||
{
|
||||
doc: "syntax",
|
||||
template: "{{}",
|
||||
expectedError: `Status: Template parsing error: template: :1: unexpected "}" in command, Code: 64`,
|
||||
expectedError: `Status: template parsing error: template: :1: unexpected "}" in command, Code: 64`,
|
||||
},
|
||||
{
|
||||
doc: "syntax",
|
||||
|
||||
@ -114,6 +114,7 @@ func NewVersionCommand(dockerCli command.Cli) *cobra.Command {
|
||||
flags.StringVarP(&opts.format, "format", "f", "", "Format the output using the given Go template")
|
||||
flags.StringVar(&opts.kubeConfig, "kubeconfig", "", "Kubernetes config file")
|
||||
flags.SetAnnotation("kubeconfig", "kubernetes", nil)
|
||||
flags.SetAnnotation("kubeconfig", "deprecated", nil)
|
||||
|
||||
return cmd
|
||||
}
|
||||
@ -234,7 +235,7 @@ func newVersionTemplate(templateFormat string) (*template.Template, error) {
|
||||
tmpl := templates.New("version").Funcs(template.FuncMap{"getDetailsOrder": getDetailsOrder})
|
||||
tmpl, err := tmpl.Parse(templateFormat)
|
||||
|
||||
return tmpl, errors.Wrap(err, "Template parsing error")
|
||||
return tmpl, errors.Wrap(err, "template parsing error")
|
||||
}
|
||||
|
||||
func getDetailsOrder(v types.ComponentVersion) []string {
|
||||
|
||||
@ -20,13 +20,11 @@ func TestTaskContextWrite(t *testing.T) {
|
||||
}{
|
||||
{
|
||||
formatter.Context{Format: "{{InvalidFunction}}"},
|
||||
`Template parsing error: template: :1: function "InvalidFunction" not defined
|
||||
`,
|
||||
`template parsing error: template: :1: function "InvalidFunction" not defined`,
|
||||
},
|
||||
{
|
||||
formatter.Context{Format: "{{nil}}"},
|
||||
`Template parsing error: template: :1:2: executing "" at <nil>: nil is not a command
|
||||
`,
|
||||
`template parsing error: template: :1:2: executing "" at <nil>: nil is not a command`,
|
||||
},
|
||||
{
|
||||
formatter.Context{Format: NewTaskFormat("table", true)},
|
||||
|
||||
@ -95,15 +95,13 @@ func TestTrustTagContextWrite(t *testing.T) {
|
||||
formatter.Context{
|
||||
Format: "{{InvalidFunction}}",
|
||||
},
|
||||
`Template parsing error: template: :1: function "InvalidFunction" not defined
|
||||
`,
|
||||
`template parsing error: template: :1: function "InvalidFunction" not defined`,
|
||||
},
|
||||
{
|
||||
formatter.Context{
|
||||
Format: "{{nil}}",
|
||||
},
|
||||
`Template parsing error: template: :1:2: executing "" at <nil>: nil is not a command
|
||||
`,
|
||||
`template parsing error: template: :1:2: executing "" at <nil>: nil is not a command`,
|
||||
},
|
||||
// Table Format
|
||||
{
|
||||
@ -191,15 +189,13 @@ func TestSignerInfoContextWrite(t *testing.T) {
|
||||
formatter.Context{
|
||||
Format: "{{InvalidFunction}}",
|
||||
},
|
||||
`Template parsing error: template: :1: function "InvalidFunction" not defined
|
||||
`,
|
||||
`template parsing error: template: :1: function "InvalidFunction" not defined`,
|
||||
},
|
||||
{
|
||||
formatter.Context{
|
||||
Format: "{{nil}}",
|
||||
},
|
||||
`Template parsing error: template: :1:2: executing "" at <nil>: nil is not a command
|
||||
`,
|
||||
`template parsing error: template: :1:2: executing "" at <nil>: nil is not a command`,
|
||||
},
|
||||
// Table Format
|
||||
{
|
||||
|
||||
@ -32,7 +32,7 @@ func newCreateCommand(dockerCli command.Cli) *cobra.Command {
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if len(args) == 1 {
|
||||
if options.name != "" {
|
||||
return errors.Errorf("Conflicting options: either specify --name or provide positional arg, not both\n")
|
||||
return errors.Errorf("conflicting options: either specify --name or provide positional arg, not both")
|
||||
}
|
||||
options.name = args[0]
|
||||
}
|
||||
|
||||
@ -26,7 +26,7 @@ func TestVolumeCreateErrors(t *testing.T) {
|
||||
flags: map[string]string{
|
||||
"name": "volumeName",
|
||||
},
|
||||
expectedError: "Conflicting options: either specify --name or provide positional arg, not both",
|
||||
expectedError: "conflicting options: either specify --name or provide positional arg, not both",
|
||||
},
|
||||
{
|
||||
args: []string{"too", "many"},
|
||||
|
||||
@ -35,7 +35,7 @@ func TestVolumeInspectErrors(t *testing.T) {
|
||||
flags: map[string]string{
|
||||
"format": "{{invalid format}}",
|
||||
},
|
||||
expectedError: "Template parsing error",
|
||||
expectedError: "template parsing error",
|
||||
},
|
||||
{
|
||||
args: []string{"foo", "bar"},
|
||||
|
||||
@ -99,7 +99,7 @@ func newPathError(path Path, err error) error {
|
||||
return nil
|
||||
case *template.InvalidTemplateError:
|
||||
return errors.Errorf(
|
||||
"invalid interpolation format for %s: %#v. You may need to escape any $ with another $.",
|
||||
"invalid interpolation format for %s: %#v; you may need to escape any $ with another $",
|
||||
path, err.Template)
|
||||
default:
|
||||
return errors.Wrapf(err, "error while interpolating %s", path)
|
||||
|
||||
@ -58,7 +58,7 @@ func TestInvalidInterpolation(t *testing.T) {
|
||||
},
|
||||
}
|
||||
_, err := Interpolate(services, Options{LookupValue: defaultMapping})
|
||||
assert.Error(t, err, `invalid interpolation format for servicea.image: "${". You may need to escape any $ with another $.`)
|
||||
assert.Error(t, err, `invalid interpolation format for servicea.image: "${"; you may need to escape any $ with another $`)
|
||||
}
|
||||
|
||||
func TestInterpolateWithDefaults(t *testing.T) {
|
||||
|
||||
@ -104,14 +104,18 @@ func LoadFromReader(configData io.Reader) (*configfile.ConfigFile, error) {
|
||||
return &configFile, err
|
||||
}
|
||||
|
||||
// TODO remove this temporary hack, which is used to warn about the deprecated ~/.dockercfg file
|
||||
var printLegacyFileWarning bool
|
||||
|
||||
// Load reads the configuration files in the given directory, and sets up
|
||||
// the auth config information and returns values.
|
||||
// FIXME: use the internal golang config parser
|
||||
func Load(configDir string) (*configfile.ConfigFile, error) {
|
||||
printLegacyFileWarning = false
|
||||
cfg, _, err := load(configDir)
|
||||
return cfg, err
|
||||
}
|
||||
|
||||
// TODO remove this temporary hack, which is used to warn about the deprecated ~/.dockercfg file
|
||||
// so we can remove the bool return value and collapse this back into `Load`
|
||||
func load(configDir string) (*configfile.ConfigFile, bool, error) {
|
||||
printLegacyFileWarning := false
|
||||
|
||||
if configDir == "" {
|
||||
configDir = Dir()
|
||||
@ -127,11 +131,11 @@ func Load(configDir string) (*configfile.ConfigFile, error) {
|
||||
if err != nil {
|
||||
err = errors.Wrap(err, filename)
|
||||
}
|
||||
return configFile, err
|
||||
return configFile, printLegacyFileWarning, err
|
||||
} else if !os.IsNotExist(err) {
|
||||
// if file is there but we can't stat it for any reason other
|
||||
// than it doesn't exist then stop
|
||||
return configFile, errors.Wrap(err, filename)
|
||||
return configFile, printLegacyFileWarning, errors.Wrap(err, filename)
|
||||
}
|
||||
|
||||
// Can't find latest config file so check for the old one
|
||||
@ -140,16 +144,16 @@ func Load(configDir string) (*configfile.ConfigFile, error) {
|
||||
printLegacyFileWarning = true
|
||||
defer file.Close()
|
||||
if err := configFile.LegacyLoadFromReader(file); err != nil {
|
||||
return configFile, errors.Wrap(err, filename)
|
||||
return configFile, printLegacyFileWarning, errors.Wrap(err, filename)
|
||||
}
|
||||
}
|
||||
return configFile, nil
|
||||
return configFile, printLegacyFileWarning, nil
|
||||
}
|
||||
|
||||
// LoadDefaultConfigFile attempts to load the default config file and returns
|
||||
// an initialized ConfigFile struct if none is found.
|
||||
func LoadDefaultConfigFile(stderr io.Writer) *configfile.ConfigFile {
|
||||
configFile, err := Load(Dir())
|
||||
configFile, printLegacyFileWarning, err := load(Dir())
|
||||
if err != nil {
|
||||
fmt.Fprintf(stderr, "WARNING: Error loading config file: %v\n", err)
|
||||
}
|
||||
|
||||
@ -119,7 +119,7 @@ func (configFile *ConfigFile) LegacyLoadFromReader(configData io.Reader) error {
|
||||
// LoadFromReader reads the configuration data given and sets up the auth config
|
||||
// information with given directory and populates the receiver object
|
||||
func (configFile *ConfigFile) LoadFromReader(configData io.Reader) error {
|
||||
if err := json.NewDecoder(configData).Decode(&configFile); err != nil && !errors.Is(err, io.EOF) {
|
||||
if err := json.NewDecoder(configData).Decode(configFile); err != nil && !errors.Is(err, io.EOF) {
|
||||
return err
|
||||
}
|
||||
var err error
|
||||
|
||||
@ -27,16 +27,19 @@ func TestEncodeAuth(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestProxyConfig(t *testing.T) {
|
||||
httpProxy := "http://proxy.mycorp.com:3128"
|
||||
httpsProxy := "https://user:password@proxy.mycorp.com:3129"
|
||||
ftpProxy := "http://ftpproxy.mycorp.com:21"
|
||||
noProxy := "*.intra.mycorp.com"
|
||||
defaultProxyConfig := ProxyConfig{
|
||||
HTTPProxy: httpProxy,
|
||||
HTTPSProxy: httpsProxy,
|
||||
FTPProxy: ftpProxy,
|
||||
NoProxy: noProxy,
|
||||
}
|
||||
var (
|
||||
httpProxy = "http://proxy.mycorp.example.com:3128"
|
||||
httpsProxy = "https://user:password@proxy.mycorp.example.com:3129"
|
||||
ftpProxy = "http://ftpproxy.mycorp.example.com:21"
|
||||
noProxy = "*.intra.mycorp.example.com"
|
||||
|
||||
defaultProxyConfig = ProxyConfig{
|
||||
HTTPProxy: httpProxy,
|
||||
HTTPSProxy: httpsProxy,
|
||||
FTPProxy: ftpProxy,
|
||||
NoProxy: noProxy,
|
||||
}
|
||||
)
|
||||
|
||||
cfg := ConfigFile{
|
||||
Proxies: map[string]ProxyConfig{
|
||||
@ -59,18 +62,21 @@ func TestProxyConfig(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestProxyConfigOverride(t *testing.T) {
|
||||
httpProxy := "http://proxy.mycorp.com:3128"
|
||||
overrideHTTPProxy := "http://proxy.example.com:3128"
|
||||
overrideNoProxy := ""
|
||||
httpsProxy := "https://user:password@proxy.mycorp.com:3129"
|
||||
ftpProxy := "http://ftpproxy.mycorp.com:21"
|
||||
noProxy := "*.intra.mycorp.com"
|
||||
defaultProxyConfig := ProxyConfig{
|
||||
HTTPProxy: httpProxy,
|
||||
HTTPSProxy: httpsProxy,
|
||||
FTPProxy: ftpProxy,
|
||||
NoProxy: noProxy,
|
||||
}
|
||||
var (
|
||||
httpProxy = "http://proxy.mycorp.example.com:3128"
|
||||
httpProxyOverride = "http://proxy.example.com:3128"
|
||||
httpsProxy = "https://user:password@proxy.mycorp.example.com:3129"
|
||||
ftpProxy = "http://ftpproxy.mycorp.example.com:21"
|
||||
noProxy = "*.intra.mycorp.example.com"
|
||||
noProxyOverride = ""
|
||||
|
||||
defaultProxyConfig = ProxyConfig{
|
||||
HTTPProxy: httpProxy,
|
||||
HTTPSProxy: httpsProxy,
|
||||
FTPProxy: ftpProxy,
|
||||
NoProxy: noProxy,
|
||||
}
|
||||
)
|
||||
|
||||
cfg := ConfigFile{
|
||||
Proxies: map[string]ProxyConfig{
|
||||
@ -84,46 +90,49 @@ func TestProxyConfigOverride(t *testing.T) {
|
||||
}
|
||||
|
||||
ropts := map[string]*string{
|
||||
"HTTP_PROXY": clone(overrideHTTPProxy),
|
||||
"NO_PROXY": clone(overrideNoProxy),
|
||||
"HTTP_PROXY": clone(httpProxyOverride),
|
||||
"NO_PROXY": clone(noProxyOverride),
|
||||
}
|
||||
proxyConfig := cfg.ParseProxyConfig("/var/run/docker.sock", ropts)
|
||||
expected := map[string]*string{
|
||||
"HTTP_PROXY": &overrideHTTPProxy,
|
||||
"HTTP_PROXY": &httpProxyOverride,
|
||||
"http_proxy": &httpProxy,
|
||||
"HTTPS_PROXY": &httpsProxy,
|
||||
"https_proxy": &httpsProxy,
|
||||
"FTP_PROXY": &ftpProxy,
|
||||
"ftp_proxy": &ftpProxy,
|
||||
"NO_PROXY": &overrideNoProxy,
|
||||
"NO_PROXY": &noProxyOverride,
|
||||
"no_proxy": &noProxy,
|
||||
}
|
||||
assert.Check(t, is.DeepEqual(expected, proxyConfig))
|
||||
}
|
||||
|
||||
func TestProxyConfigPerHost(t *testing.T) {
|
||||
httpProxy := "http://proxy.mycorp.com:3128"
|
||||
httpsProxy := "https://user:password@proxy.mycorp.com:3129"
|
||||
ftpProxy := "http://ftpproxy.mycorp.com:21"
|
||||
noProxy := "*.intra.mycorp.com"
|
||||
var (
|
||||
httpProxy = "http://proxy.mycorp.example.com:3128"
|
||||
httpsProxy = "https://user:password@proxy.mycorp.example.com:3129"
|
||||
ftpProxy = "http://ftpproxy.mycorp.example.com:21"
|
||||
noProxy = "*.intra.mycorp.example.com"
|
||||
|
||||
extHTTPProxy := "http://proxy.example.com:3128"
|
||||
extHTTPSProxy := "https://user:password@proxy.example.com:3129"
|
||||
extFTPProxy := "http://ftpproxy.example.com:21"
|
||||
extNoProxy := "*.intra.example.com"
|
||||
extHTTPProxy = "http://proxy.example.com:3128"
|
||||
extHTTPSProxy = "https://user:password@proxy.example.com:3129"
|
||||
extFTPProxy = "http://ftpproxy.example.com:21"
|
||||
extNoProxy = "*.intra.example.com"
|
||||
|
||||
defaultProxyConfig := ProxyConfig{
|
||||
HTTPProxy: httpProxy,
|
||||
HTTPSProxy: httpsProxy,
|
||||
FTPProxy: ftpProxy,
|
||||
NoProxy: noProxy,
|
||||
}
|
||||
externalProxyConfig := ProxyConfig{
|
||||
HTTPProxy: extHTTPProxy,
|
||||
HTTPSProxy: extHTTPSProxy,
|
||||
FTPProxy: extFTPProxy,
|
||||
NoProxy: extNoProxy,
|
||||
}
|
||||
defaultProxyConfig = ProxyConfig{
|
||||
HTTPProxy: httpProxy,
|
||||
HTTPSProxy: httpsProxy,
|
||||
FTPProxy: ftpProxy,
|
||||
NoProxy: noProxy,
|
||||
}
|
||||
|
||||
externalProxyConfig = ProxyConfig{
|
||||
HTTPProxy: extHTTPProxy,
|
||||
HTTPSProxy: extHTTPSProxy,
|
||||
FTPProxy: extFTPProxy,
|
||||
NoProxy: extNoProxy,
|
||||
}
|
||||
)
|
||||
|
||||
cfg := ConfigFile{
|
||||
Proxies: map[string]ProxyConfig{
|
||||
@ -226,9 +235,11 @@ func TestGetAllCredentialsCredsStore(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetAllCredentialsCredHelper(t *testing.T) {
|
||||
testCredHelperSuffix := "test_cred_helper"
|
||||
testCredHelperRegistryHostname := "credhelper.com"
|
||||
testExtraCredHelperRegistryHostname := "somethingweird.com"
|
||||
const (
|
||||
testCredHelperSuffix = "test_cred_helper"
|
||||
testCredHelperRegistryHostname = "credhelper.com"
|
||||
testExtraCredHelperRegistryHostname = "somethingweird.com"
|
||||
)
|
||||
|
||||
unexpectedCredHelperAuth := types.AuthConfig{
|
||||
Username: "file_store_user",
|
||||
@ -265,9 +276,11 @@ func TestGetAllCredentialsCredHelper(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetAllCredentialsFileStoreAndCredHelper(t *testing.T) {
|
||||
testFileStoreRegistryHostname := "example.com"
|
||||
testCredHelperSuffix := "test_cred_helper"
|
||||
testCredHelperRegistryHostname := "credhelper.com"
|
||||
const (
|
||||
testFileStoreRegistryHostname = "example.com"
|
||||
testCredHelperSuffix = "test_cred_helper"
|
||||
testCredHelperRegistryHostname = "credhelper.com"
|
||||
)
|
||||
|
||||
expectedFileStoreAuth := types.AuthConfig{
|
||||
Username: "file_store_user",
|
||||
@ -301,10 +314,12 @@ func TestGetAllCredentialsFileStoreAndCredHelper(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetAllCredentialsCredStoreAndCredHelper(t *testing.T) {
|
||||
testCredStoreSuffix := "test_creds_store"
|
||||
testCredStoreRegistryHostname := "credstore.com"
|
||||
testCredHelperSuffix := "test_cred_helper"
|
||||
testCredHelperRegistryHostname := "credhelper.com"
|
||||
const (
|
||||
testCredStoreSuffix = "test_creds_store"
|
||||
testCredStoreRegistryHostname = "credstore.com"
|
||||
testCredHelperSuffix = "test_cred_helper"
|
||||
testCredHelperRegistryHostname = "credhelper.com"
|
||||
)
|
||||
|
||||
configFile := New("filename")
|
||||
configFile.CredentialsStore = testCredStoreSuffix
|
||||
@ -343,9 +358,11 @@ func TestGetAllCredentialsCredStoreAndCredHelper(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetAllCredentialsCredHelperOverridesDefaultStore(t *testing.T) {
|
||||
testCredStoreSuffix := "test_creds_store"
|
||||
testCredHelperSuffix := "test_cred_helper"
|
||||
testRegistryHostname := "example.com"
|
||||
const (
|
||||
testCredStoreSuffix = "test_creds_store"
|
||||
testCredHelperSuffix = "test_cred_helper"
|
||||
testRegistryHostname = "example.com"
|
||||
)
|
||||
|
||||
configFile := New("filename")
|
||||
configFile.CredentialsStore = testCredStoreSuffix
|
||||
@ -424,38 +441,36 @@ func TestCheckKubernetesConfigurationRaiseAnErrorOnInvalidValue(t *testing.T) {
|
||||
expectError bool
|
||||
}{
|
||||
{
|
||||
"no kubernetes config is valid",
|
||||
nil,
|
||||
false,
|
||||
name: "no kubernetes config is valid",
|
||||
},
|
||||
{
|
||||
"enabled is valid",
|
||||
&KubernetesConfig{AllNamespaces: "enabled"},
|
||||
false,
|
||||
name: "enabled is valid",
|
||||
config: &KubernetesConfig{AllNamespaces: "enabled"},
|
||||
},
|
||||
{
|
||||
"disabled is valid",
|
||||
&KubernetesConfig{AllNamespaces: "disabled"},
|
||||
false,
|
||||
name: "disabled is valid",
|
||||
config: &KubernetesConfig{AllNamespaces: "disabled"},
|
||||
},
|
||||
{
|
||||
"empty string is valid",
|
||||
&KubernetesConfig{AllNamespaces: ""},
|
||||
false,
|
||||
name: "empty string is valid",
|
||||
config: &KubernetesConfig{AllNamespaces: ""},
|
||||
},
|
||||
{
|
||||
"other value is invalid",
|
||||
&KubernetesConfig{AllNamespaces: "unknown"},
|
||||
true,
|
||||
name: "other value is invalid",
|
||||
config: &KubernetesConfig{AllNamespaces: "unknown"},
|
||||
expectError: true,
|
||||
},
|
||||
}
|
||||
for _, test := range testCases {
|
||||
err := checkKubernetesConfiguration(test.config)
|
||||
if test.expectError {
|
||||
assert.Assert(t, err != nil, test.name)
|
||||
} else {
|
||||
assert.NilError(t, err, test.name)
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
test := tc
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
err := checkKubernetesConfiguration(test.config)
|
||||
if test.expectError {
|
||||
assert.Assert(t, err != nil, test.name)
|
||||
} else {
|
||||
assert.NilError(t, err, test.name)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
package configfile
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
//go:build !windows && !darwin && !linux
|
||||
// +build !windows,!darwin,!linux
|
||||
|
||||
package credentials
|
||||
|
||||
@ -70,7 +70,7 @@ func TestFileStoreGet(t *testing.T) {
|
||||
|
||||
func TestFileStoreGetAll(t *testing.T) {
|
||||
s1 := "https://example.com"
|
||||
s2 := "https://example2.com"
|
||||
s2 := "https://example2.example.com"
|
||||
f := newStore(map[string]types.AuthConfig{
|
||||
s1: {
|
||||
Auth: "super_secret_token",
|
||||
@ -80,7 +80,7 @@ func TestFileStoreGetAll(t *testing.T) {
|
||||
s2: {
|
||||
Auth: "super_secret_token2",
|
||||
Email: "foo@example2.com",
|
||||
ServerAddress: "https://example2.com",
|
||||
ServerAddress: "https://example2.example.com",
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
@ -7,7 +7,7 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
remoteCredentialsPrefix = "docker-credential-"
|
||||
remoteCredentialsPrefix = "docker-credential-" //nolint:gosec // ignore G101: Potential hardcoded credentials
|
||||
tokenUsername = "<token>"
|
||||
)
|
||||
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
package commandconn
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
//go:build !linux
|
||||
// +build !linux
|
||||
|
||||
package commandconn
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
package commandconn
|
||||
|
||||
@ -49,7 +49,7 @@ func getConnectionHelper(daemonURL string, sshFlags []string) (*ConnectionHelper
|
||||
Dialer: func(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||
return commandconn.New(ctx, "ssh", append(sshFlags, sp.Args("docker", "system", "dial-stdio")...)...)
|
||||
},
|
||||
Host: "http://docker",
|
||||
Host: "http://docker.example.com",
|
||||
}, nil
|
||||
}
|
||||
// Future version may support plugins via ~/.docker/config.json. e.g. "dind"
|
||||
@ -63,6 +63,6 @@ func GetCommandConnectionHelper(cmd string, flags ...string) (*ConnectionHelper,
|
||||
Dialer: func(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||
return commandconn.New(ctx, cmd, flags...)
|
||||
},
|
||||
Host: "http://docker",
|
||||
Host: "http://docker.example.com",
|
||||
}, nil
|
||||
}
|
||||
|
||||
@ -26,7 +26,12 @@ type EndpointMeta = context.EndpointMetaBase
|
||||
// a Docker Engine endpoint, with its tls data
|
||||
type Endpoint struct {
|
||||
EndpointMeta
|
||||
TLSData *context.TLSData
|
||||
TLSData *context.TLSData
|
||||
|
||||
// Deprecated: Use of encrypted TLS private keys has been deprecated, and
|
||||
// will be removed in a future release. Golang has deprecated support for
|
||||
// legacy PEM encryption (as specified in RFC 1423), as it is insecure by
|
||||
// design (see https://go-review.googlesource.com/c/go/+/264159).
|
||||
TLSPassword string
|
||||
}
|
||||
|
||||
@ -66,8 +71,9 @@ func (c *Endpoint) tlsConfig() (*tls.Config, error) {
|
||||
}
|
||||
|
||||
var err error
|
||||
if x509.IsEncryptedPEMBlock(pemBlock) {
|
||||
keyBytes, err = x509.DecryptPEMBlock(pemBlock, []byte(c.TLSPassword))
|
||||
// TODO should we follow Golang, and deprecate RFC 1423 encryption, and produce a warning (or just error)? see https://github.com/docker/cli/issues/3212
|
||||
if x509.IsEncryptedPEMBlock(pemBlock) { //nolint: staticcheck // SA1019: x509.IsEncryptedPEMBlock is deprecated, and insecure by design
|
||||
keyBytes, err = x509.DecryptPEMBlock(pemBlock, []byte(c.TLSPassword)) //nolint: staticcheck // SA1019: x509.IsEncryptedPEMBlock is deprecated, and insecure by design
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "private key is encrypted, but could not decrypt it")
|
||||
}
|
||||
|
||||
@ -1954,6 +1954,7 @@ _docker_container_run_and_create() {
|
||||
--oom-score-adj
|
||||
--pid
|
||||
--pids-limit
|
||||
--platform
|
||||
--publish -p
|
||||
--pull
|
||||
--restart
|
||||
@ -1981,9 +1982,6 @@ _docker_container_run_and_create() {
|
||||
--io-maxiops
|
||||
--isolation
|
||||
"
|
||||
__docker_server_is_experimental && options_with_args+="
|
||||
--platform
|
||||
"
|
||||
|
||||
local boolean_options="
|
||||
--disable-content-trust=false
|
||||
@ -2831,6 +2829,7 @@ _docker_image_build() {
|
||||
--memory -m
|
||||
--memory-swap
|
||||
--network
|
||||
--platform
|
||||
--shm-size
|
||||
--tag -t
|
||||
--target
|
||||
@ -2851,9 +2850,6 @@ _docker_image_build() {
|
||||
"
|
||||
|
||||
if __docker_server_is_experimental ; then
|
||||
options_with_args+="
|
||||
--platform
|
||||
"
|
||||
boolean_options+="
|
||||
--squash
|
||||
"
|
||||
@ -2862,7 +2858,6 @@ _docker_image_build() {
|
||||
if [ "$DOCKER_BUILDKIT" = "1" ] ; then
|
||||
options_with_args+="
|
||||
--output -o
|
||||
--platform
|
||||
--progress
|
||||
--secret
|
||||
--ssh
|
||||
@ -2993,8 +2988,7 @@ _docker_image_import() {
|
||||
|
||||
case "$cur" in
|
||||
-*)
|
||||
local options="--change -c --help --message -m"
|
||||
__docker_server_is_experimental && options+=" --platform"
|
||||
local options="--change -c --help --message -m --platform"
|
||||
COMPREPLY=( $( compgen -W "$options" -- "$cur" ) )
|
||||
;;
|
||||
*)
|
||||
@ -3102,9 +3096,7 @@ _docker_image_pull() {
|
||||
|
||||
case "$cur" in
|
||||
-*)
|
||||
local options="--all-tags -a --disable-content-trust=false --help --quiet -q"
|
||||
__docker_server_is_experimental && options+=" --platform"
|
||||
|
||||
local options="--all-tags -a --disable-content-trust=false --help --platform --quiet -q"
|
||||
COMPREPLY=( $( compgen -W "$options" -- "$cur" ) )
|
||||
;;
|
||||
*)
|
||||
@ -5485,6 +5477,23 @@ _docker_wait() {
|
||||
_docker_container_wait
|
||||
}
|
||||
|
||||
COMPOSE_PLUGIN_PATH=$(docker info --format '{{range .ClientInfo.Plugins}}{{if eq .Name "compose"}}{{.Path}}{{end}}{{end}}')
|
||||
|
||||
_docker_compose() {
|
||||
local completionCommand="__completeNoDesc"
|
||||
local resultArray=($COMPOSE_PLUGIN_PATH $completionCommand compose)
|
||||
for value in "${words[@]:2}"; do
|
||||
if [ -z "$value" ]; then
|
||||
resultArray+=( "''" )
|
||||
else
|
||||
resultArray+=( "$value" )
|
||||
fi
|
||||
done
|
||||
local result=$(eval "${resultArray[*]}" 2> /dev/null | grep -v '^:[0-9]*$')
|
||||
|
||||
COMPREPLY=( $(compgen -W "${result}" -- "$current") )
|
||||
}
|
||||
|
||||
_docker() {
|
||||
local previous_extglob_setting=$(shopt -p extglob)
|
||||
shopt -s extglob
|
||||
@ -5554,11 +5563,17 @@ _docker() {
|
||||
wait
|
||||
)
|
||||
|
||||
local known_plugin_commands=()
|
||||
|
||||
if [ -f "$COMPOSE_PLUGIN_PATH" ] ; then
|
||||
known_plugin_commands+=("compose")
|
||||
fi
|
||||
|
||||
local experimental_server_commands=(
|
||||
checkpoint
|
||||
)
|
||||
|
||||
local commands=(${management_commands[*]} ${top_level_commands[*]})
|
||||
local commands=(${management_commands[*]} ${top_level_commands[*]} ${known_plugin_commands[*]})
|
||||
[ -z "$DOCKER_HIDE_LEGACY_COMMANDS" ] && commands+=(${legacy_commands[*]})
|
||||
|
||||
# These options are valid as global options for all client commands
|
||||
|
||||
@ -2652,7 +2652,7 @@ __docker_commands() {
|
||||
then
|
||||
local -a lines
|
||||
lines=(${(f)"$(_call_program commands docker 2>&1)"})
|
||||
_docker_subcommands=(${${${(M)${lines[$((${lines[(i)*Commands:]} + 1)),-1]}:# *}## #}/ ##/:})
|
||||
_docker_subcommands=(${${${(M)${lines[$((${lines[(i)*Commands:]} + 1)),-1]}:# *}## #}/\*# ##/:})
|
||||
_docker_subcommands=($_docker_subcommands 'daemon:Enable daemon mode' 'help:Show help for a command')
|
||||
(( $#_docker_subcommands > 2 )) && _store_cache docker_subcommands _docker_subcommands
|
||||
fi
|
||||
@ -3094,6 +3094,7 @@ _docker() {
|
||||
_arguments $(__docker_arguments) -C \
|
||||
"(: -)"{-h,--help}"[Print usage]" \
|
||||
"($help)--config[Location of client config files]:path:_directories" \
|
||||
"($help -c --context)"{-c=,--context=}"[Execute the command in a docker context]:context:__docker_complete_contexts" \
|
||||
"($help -D --debug)"{-D,--debug}"[Enable debug mode]" \
|
||||
"($help -H --host)"{-H=,--host=}"[tcp://host:port to bind/connect to]:host: " \
|
||||
"($help -l --log-level)"{-l=,--log-level=}"[Logging level]:level:(debug info warn error fatal)" \
|
||||
@ -3109,7 +3110,8 @@ _docker() {
|
||||
|
||||
local host=${opt_args[-H]}${opt_args[--host]}
|
||||
local config=${opt_args[--config]}
|
||||
local docker_options="${host:+--host $host} ${config:+--config $config}"
|
||||
local context=${opt_args[-c]}${opt_args[--context]}
|
||||
local docker_options="${host:+--host $host} ${config:+--config $config} ${context:+--context $context} "
|
||||
|
||||
case $state in
|
||||
(command)
|
||||
|
||||
@ -1,3 +1,6 @@
|
||||
variable "GO_VERSION" {
|
||||
default = "1.18.7"
|
||||
}
|
||||
variable "VERSION" {
|
||||
default = ""
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
ARG GO_VERSION=1.13.15
|
||||
ARG GO_VERSION=1.18.7
|
||||
|
||||
FROM golang:${GO_VERSION}-alpine
|
||||
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
# syntax=docker/dockerfile:1.1.7-experimental
|
||||
ARG GO_VERSION=1.13.15
|
||||
# syntax=docker/dockerfile:1
|
||||
|
||||
ARG GO_VERSION=1.18.7
|
||||
|
||||
FROM golang:${GO_VERSION}-alpine AS golang
|
||||
ENV CGO_ENABLED=0
|
||||
@ -9,21 +10,21 @@ ARG ESC_VERSION=v0.2.0
|
||||
RUN --mount=type=cache,target=/root/.cache/go-build \
|
||||
--mount=type=cache,target=/go/pkg/mod \
|
||||
--mount=type=tmpfs,target=/go/src/ \
|
||||
GO111MODULE=on go get github.com/mjibson/esc@${ESC_VERSION}
|
||||
GO111MODULE=on go install github.com/mjibson/esc@${ESC_VERSION}
|
||||
|
||||
FROM golang AS gotestsum
|
||||
ARG GOTESTSUM_VERSION=v0.4.0
|
||||
RUN --mount=type=cache,target=/root/.cache/go-build \
|
||||
--mount=type=cache,target=/go/pkg/mod \
|
||||
--mount=type=tmpfs,target=/go/src/ \
|
||||
GO111MODULE=on go get gotest.tools/gotestsum@${GOTESTSUM_VERSION}
|
||||
GO111MODULE=on go install gotest.tools/gotestsum@${GOTESTSUM_VERSION}
|
||||
|
||||
FROM golang AS vndr
|
||||
ARG VNDR_VERSION=v0.1.2
|
||||
RUN --mount=type=cache,target=/root/.cache/go-build \
|
||||
--mount=type=cache,target=/go/pkg/mod \
|
||||
--mount=type=tmpfs,target=/go/src/ \
|
||||
GO111MODULE=on go get github.com/LK4D4/vndr@${VNDR_VERSION}
|
||||
GO111MODULE=on go install github.com/LK4D4/vndr@${VNDR_VERSION}
|
||||
|
||||
FROM golang AS dev
|
||||
RUN apk add --no-cache \
|
||||
@ -43,4 +44,5 @@ COPY --from=vndr /go/bin/* /go/bin/
|
||||
COPY --from=gotestsum /go/bin/* /go/bin/
|
||||
|
||||
WORKDIR /go/src/github.com/docker/cli
|
||||
ENV GO111MODULE=auto
|
||||
COPY . .
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
ARG GO_VERSION=1.13.15
|
||||
ARG GO_VERSION=1.18.7
|
||||
|
||||
# Use Debian based image as docker-compose requires glibc.
|
||||
FROM golang:${GO_VERSION}-buster
|
||||
@ -10,7 +10,7 @@ RUN apt-get update && apt-get install -y \
|
||||
openssh-client \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
ARG COMPOSE_VERSION=1.25.1
|
||||
ARG COMPOSE_VERSION=1.29.2
|
||||
RUN curl -fsSL https://github.com/docker/compose/releases/download/${COMPOSE_VERSION}/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose \
|
||||
&& chmod +x /usr/local/bin/docker-compose
|
||||
|
||||
@ -39,7 +39,6 @@ ARG GITCOMMIT
|
||||
ENV VERSION=${VERSION}
|
||||
ENV GITCOMMIT=${GITCOMMIT}
|
||||
ENV DOCKER_BUILDKIT=1
|
||||
ENV COMPOSE_DOCKER_CLI_BUILD=1
|
||||
RUN ./scripts/build/binary
|
||||
RUN ./scripts/build/plugins e2e/cli-plugins/plugins/*
|
||||
|
||||
|
||||
@ -1,18 +1,19 @@
|
||||
# syntax=docker/dockerfile:1.1.3-experimental
|
||||
# syntax=docker/dockerfile:1
|
||||
|
||||
ARG GO_VERSION=1.13.15
|
||||
ARG GOLANGCI_LINTER_SHA="v1.21.0"
|
||||
ARG GO_VERSION=1.18.7
|
||||
ARG GOLANGCI_LINT_VERSION=v1.45.2
|
||||
|
||||
FROM golang:${GO_VERSION}-alpine AS build
|
||||
ENV CGO_ENABLED=0
|
||||
RUN apk add --no-cache git
|
||||
ARG GOLANGCI_LINTER_SHA
|
||||
ARG GOLANGCI_LINT_VERSION
|
||||
ARG GO111MODULE=on
|
||||
RUN --mount=type=cache,target=/root/.cache/go-build \
|
||||
--mount=type=cache,target=/go/pkg/mod \
|
||||
go get github.com/golangci/golangci-lint/cmd/golangci-lint@${GOLANGCI_LINTER_SHA}
|
||||
go install github.com/golangci/golangci-lint/cmd/golangci-lint@${GOLANGCI_LINT_VERSION}
|
||||
|
||||
FROM golang:${GO_VERSION}-alpine AS lint
|
||||
ENV GO111MODULE=off
|
||||
ENV CGO_ENABLED=0
|
||||
ENV DISABLE_WARN_OUTSIDE_CONTAINER=1
|
||||
COPY --from=build /go/bin/golangci-lint /usr/local/bin
|
||||
|
||||
@ -48,54 +48,74 @@ The table below provides an overview of the current status of deprecated feature
|
||||
alternatives. In such cases, a warning may be printed, and users should not rely
|
||||
on this feature.
|
||||
|
||||
Status | Feature | Deprecated | Remove
|
||||
-----------|------------------------------------------------------------------------------------------------------------------------------------|------------|------------
|
||||
Deprecated | [Pulling images from non-compliant image registries](#pulling-images-from-non-compliant-image-registries) | v20.10 | -
|
||||
Deprecated | [Linux containers on Windows (LCOW)](#linux-containers-on-windows-lcow-experimental) | v20.10 | -
|
||||
Deprecated | [BLKIO weight options with cgroups v1](#blkio-weight-options–with-cgroups-v1) | v20.10 | -
|
||||
Deprecated | [Kernel memory limit](#kernel-memory-limit) | v20.10 | -
|
||||
Deprecated | [Classic Swarm and overlay networks using external key/value stores](#classic-swarm-and-overlay-networks-using-cluster-store) | v20.10 | -
|
||||
Deprecated | [Support for the legacy `~/.dockercfg` configuration file for authentication](#support-for-legacy-dockercfg-configuration-files) | v20.10 | -
|
||||
Deprecated | [CLI plugins support](#cli-plugins-support) | v20.10 | -
|
||||
Deprecated | [Dockerfile legacy `ENV name value` syntax](#dockerfile-legacy-env-name-value-syntax) | v20.10 | -
|
||||
Removed | [`docker build --stream` flag (experimental)](#docker-build---stream-flag-experimental) | v20.10 | v20.10
|
||||
Deprecated | [Configuration options for experimental CLI features](#configuration-options-for-experimental-cli-features) | v19.03 | v20.10
|
||||
Deprecated | [Pushing and pulling with image manifest v2 schema 1](#pushing-and-pulling-with-image-manifest-v2-schema-1) | v19.03 | v20.10
|
||||
Removed | [`docker engine` subcommands](#docker-engine-subcommands) | v19.03 | v20.10
|
||||
Removed | [Top-level `docker deploy` subcommand (experimental)](#top-level-docker-deploy-subcommand-experimental) | v19.03 | v20.10
|
||||
Removed | [`docker stack deploy` using "dab" files (experimental)](#docker-stack-deploy-using-dab-files-experimental) | v19.03 | v20.10
|
||||
Deprecated | [AuFS storage driver](#aufs-storage-driver) | v19.03 | -
|
||||
Deprecated | [Legacy "overlay" storage driver](#legacy-overlay-storage-driver) | v18.09 | -
|
||||
Deprecated | [Device mapper storage driver](#device-mapper-storage-driver) | v18.09 | -
|
||||
Removed | [Use of reserved namespaces in engine labels](#use-of-reserved-namespaces-in-engine-labels) | v18.06 | v20.10
|
||||
Removed | [`--disable-legacy-registry` override daemon option](#--disable-legacy-registry-override-daemon-option) | v17.12 | v19.03
|
||||
Removed | [Interacting with V1 registries](#interacting-with-v1-registries) | v17.06 | v17.12
|
||||
Removed | [Asynchronous `service create` and `service update` as default](#asynchronous-service-create-and-service-update-as-default) | v17.05 | v17.10
|
||||
Removed | [`-g` and `--graph` flags on `dockerd`](#-g-and---graph-flags-on-dockerd) | v17.05 | -
|
||||
Deprecated | [Top-level network properties in NetworkSettings](#top-level-network-properties-in-networksettings) | v1.13 | v17.12
|
||||
Removed | [`filter` param for `/images/json` endpoint](#filter-param-for-imagesjson-endpoint) | v1.13 | v20.10
|
||||
Removed | [`repository:shortid` image references](#repositoryshortid-image-references) | v1.13 | v17.12
|
||||
Removed | [`docker daemon` subcommand](#docker-daemon-subcommand) | v1.13 | v17.12
|
||||
Removed | [Duplicate keys with conflicting values in engine labels](#duplicate-keys-with-conflicting-values-in-engine-labels) | v1.13 | v17.12
|
||||
Deprecated | [`MAINTAINER` in Dockerfile](#maintainer-in-dockerfile) | v1.13 | -
|
||||
Deprecated | [API calls without a version](#api-calls-without-a-version) | v1.13 | v17.12
|
||||
Removed | [Backing filesystem without `d_type` support for overlay/overlay2](#backing-filesystem-without-d_type-support-for-overlayoverlay2) | v1.13 | v17.12
|
||||
Removed | [`--automated` and `--stars` flags on `docker search`](#--automated-and---stars-flags-on-docker-search) | v1.12 | v20.10
|
||||
Deprecated | [`-h` shorthand for `--help`](#-h-shorthand-for---help) | v1.12 | v17.09
|
||||
Removed | [`-e` and `--email` flags on `docker login`](#-e-and---email-flags-on-docker-login) | v1.11 | v17.06
|
||||
Deprecated | [Separator (`:`) of `--security-opt` flag on `docker run`](#separator--of---security-opt-flag-on-docker-run) | v1.11 | v17.06
|
||||
Deprecated | [Ambiguous event fields in API](#ambiguous-event-fields-in-api) | v1.10 | -
|
||||
Removed | [`-f` flag on `docker tag`](#-f-flag-on-docker-tag) | v1.10 | v1.12
|
||||
Removed | [HostConfig at API container start](#hostconfig-at-api-container-start) | v1.10 | v1.12
|
||||
Removed | [`--before` and `--since` flags on `docker ps`](#--before-and---since-flags-on-docker-ps) | v1.10 | v1.12
|
||||
Removed | [Driver-specific log tags](#driver-specific-log-tags) | v1.9 | v1.12
|
||||
Removed | [Docker Content Trust `ENV` passphrase variables name change](#docker-content-trust-env-passphrase-variables-name-change) | v1.9 | v1.12
|
||||
Removed | [`/containers/(id or name)/copy` endpoint](#containersid-or-namecopy-endpoint) | v1.8 | v1.12
|
||||
Removed | [LXC built-in exec driver](#lxc-built-in-exec-driver) | v1.8 | v1.10
|
||||
Removed | [Old Command Line Options](#old-command-line-options) | v1.8 | v1.10
|
||||
Removed | [`--api-enable-cors` flag on `dockerd`](#--api-enable-cors-flag-on-dockerd) | v1.6 | v17.09
|
||||
Removed | [`--run` flag on `docker commit`](#--run-flag-on-docker-commit) | v0.10 | v1.13
|
||||
Removed | [Three arguments form in `docker import`](#three-arguments-form-in-docker-import) | v0.6.7 | v1.12
|
||||
| Status | Feature | Deprecated | Remove |
|
||||
|------------|------------------------------------------------------------------------------------------------------------------------------------|------------|--------|
|
||||
| Deprecated | [Support for encrypted TLS private keys](#support-for-encrypted-tls-private-keys) | v20.10 | - |
|
||||
| Deprecated | [Kubernetes stack and context support](#kubernetes-stack-and-context-support) | v20.10 | - |
|
||||
| Deprecated | [Pulling images from non-compliant image registries](#pulling-images-from-non-compliant-image-registries) | v20.10 | - |
|
||||
| Deprecated | [Linux containers on Windows (LCOW)](#linux-containers-on-windows-lcow-experimental) | v20.10 | - |
|
||||
| Deprecated | [BLKIO weight options with cgroups v1](#blkio-weight-options-with-cgroups-v1) | v20.10 | - |
|
||||
| Deprecated | [Kernel memory limit](#kernel-memory-limit) | v20.10 | - |
|
||||
| Deprecated | [Classic Swarm and overlay networks using external key/value stores](#classic-swarm-and-overlay-networks-using-cluster-store) | v20.10 | - |
|
||||
| Deprecated | [Support for the legacy `~/.dockercfg` configuration file for authentication](#support-for-legacy-dockercfg-configuration-files) | v20.10 | - |
|
||||
| Deprecated | [CLI plugins support](#cli-plugins-support) | v20.10 | - |
|
||||
| Deprecated | [Dockerfile legacy `ENV name value` syntax](#dockerfile-legacy-env-name-value-syntax) | v20.10 | - |
|
||||
| Removed | [`docker build --stream` flag (experimental)](#docker-build---stream-flag-experimental) | v20.10 | v20.10 |
|
||||
| Deprecated | [`fluentd-async-connect` log opt](#fluentd-async-connect-log-opt) | v20.10 | - |
|
||||
| Deprecated | [Configuration options for experimental CLI features](#configuration-options-for-experimental-cli-features) | v19.03 | v20.10 |
|
||||
| Deprecated | [Pushing and pulling with image manifest v2 schema 1](#pushing-and-pulling-with-image-manifest-v2-schema-1) | v19.03 | v20.10 |
|
||||
| Removed | [`docker engine` subcommands](#docker-engine-subcommands) | v19.03 | v20.10 |
|
||||
| Removed | [Top-level `docker deploy` subcommand (experimental)](#top-level-docker-deploy-subcommand-experimental) | v19.03 | v20.10 |
|
||||
| Removed | [`docker stack deploy` using "dab" files (experimental)](#docker-stack-deploy-using-dab-files-experimental) | v19.03 | v20.10 |
|
||||
| Disabled | [Support for the `overlay2.override_kernel_check` storage option](#support-for-the-overlay2override_kernel_check-storage-option) | v19.03 | - |
|
||||
| Deprecated | [AuFS storage driver](#aufs-storage-driver) | v19.03 | - |
|
||||
| Deprecated | [Legacy "overlay" storage driver](#legacy-overlay-storage-driver) | v18.09 | - |
|
||||
| Deprecated | [Device mapper storage driver](#device-mapper-storage-driver) | v18.09 | - |
|
||||
| Removed | [Use of reserved namespaces in engine labels](#use-of-reserved-namespaces-in-engine-labels) | v18.06 | v20.10 |
|
||||
| Removed | [`--disable-legacy-registry` override daemon option](#--disable-legacy-registry-override-daemon-option) | v17.12 | v19.03 |
|
||||
| Removed | [Interacting with V1 registries](#interacting-with-v1-registries) | v17.06 | v17.12 |
|
||||
| Removed | [Asynchronous `service create` and `service update` as default](#asynchronous-service-create-and-service-update-as-default) | v17.05 | v17.10 |
|
||||
| Removed | [`-g` and `--graph` flags on `dockerd`](#-g-and---graph-flags-on-dockerd) | v17.05 | - |
|
||||
| Deprecated | [Top-level network properties in NetworkSettings](#top-level-network-properties-in-networksettings) | v1.13 | v17.12 |
|
||||
| Removed | [`filter` param for `/images/json` endpoint](#filter-param-for-imagesjson-endpoint) | v1.13 | v20.10 |
|
||||
| Removed | [`repository:shortid` image references](#repositoryshortid-image-references) | v1.13 | v17.12 |
|
||||
| Removed | [`docker daemon` subcommand](#docker-daemon-subcommand) | v1.13 | v17.12 |
|
||||
| Removed | [Duplicate keys with conflicting values in engine labels](#duplicate-keys-with-conflicting-values-in-engine-labels) | v1.13 | v17.12 |
|
||||
| Deprecated | [`MAINTAINER` in Dockerfile](#maintainer-in-dockerfile) | v1.13 | - |
|
||||
| Deprecated | [API calls without a version](#api-calls-without-a-version) | v1.13 | v17.12 |
|
||||
| Removed | [Backing filesystem without `d_type` support for overlay/overlay2](#backing-filesystem-without-d_type-support-for-overlayoverlay2) | v1.13 | v17.12 |
|
||||
| Removed | [`--automated` and `--stars` flags on `docker search`](#--automated-and---stars-flags-on-docker-search) | v1.12 | v20.10 |
|
||||
| Deprecated | [`-h` shorthand for `--help`](#-h-shorthand-for---help) | v1.12 | v17.09 |
|
||||
| Removed | [`-e` and `--email` flags on `docker login`](#-e-and---email-flags-on-docker-login) | v1.11 | v17.06 |
|
||||
| Deprecated | [Separator (`:`) of `--security-opt` flag on `docker run`](#separator--of---security-opt-flag-on-docker-run) | v1.11 | v17.06 |
|
||||
| Deprecated | [Ambiguous event fields in API](#ambiguous-event-fields-in-api) | v1.10 | - |
|
||||
| Removed | [`-f` flag on `docker tag`](#-f-flag-on-docker-tag) | v1.10 | v1.12 |
|
||||
| Removed | [HostConfig at API container start](#hostconfig-at-api-container-start) | v1.10 | v1.12 |
|
||||
| Removed | [`--before` and `--since` flags on `docker ps`](#--before-and---since-flags-on-docker-ps) | v1.10 | v1.12 |
|
||||
| Removed | [Driver-specific log tags](#driver-specific-log-tags) | v1.9 | v1.12 |
|
||||
| Removed | [Docker Content Trust `ENV` passphrase variables name change](#docker-content-trust-env-passphrase-variables-name-change) | v1.9 | v1.12 |
|
||||
| Removed | [`/containers/(id or name)/copy` endpoint](#containersid-or-namecopy-endpoint) | v1.8 | v1.12 |
|
||||
| Removed | [LXC built-in exec driver](#lxc-built-in-exec-driver) | v1.8 | v1.10 |
|
||||
| Removed | [Old Command Line Options](#old-command-line-options) | v1.8 | v1.10 |
|
||||
| Removed | [`--api-enable-cors` flag on `dockerd`](#--api-enable-cors-flag-on-dockerd) | v1.6 | v17.09 |
|
||||
| Removed | [`--run` flag on `docker commit`](#--run-flag-on-docker-commit) | v0.10 | v1.13 |
|
||||
| Removed | [Three arguments form in `docker import`](#three-arguments-form-in-docker-import) | v0.6.7 | v1.12 |
|
||||
|
||||
### Support for encrypted TLS private keys
|
||||
|
||||
**Deprecated in Release: v20.10**
|
||||
|
||||
Use of encrypted TLS private keys has been deprecated, and will be removed in a
|
||||
future release. Golang has deprecated support for legacy PEM encryption (as
|
||||
specified in [RFC 1423](https://datatracker.ietf.org/doc/html/rfc1423)), as it
|
||||
is insecure by design (see [https://go-review.googlesource.com/c/go/+/264159](https://go-review.googlesource.com/c/go/+/264159)).
|
||||
|
||||
### Kubernetes stack and context support
|
||||
|
||||
**Deprecated in Release: v20.10**
|
||||
|
||||
Following the deprecation of [Compose on Kubernetes](https://github.com/docker/compose-on-kubernetes), support for
|
||||
Kubernetes in the `stack` and `context` commands in the docker CLI is now marked as deprecated as well.
|
||||
|
||||
### Pulling images from non-compliant image registries
|
||||
|
||||
@ -236,6 +256,21 @@ Users that want to use this feature are encouraged to enable BuildKit by setting
|
||||
the `DOCKER_BUILDKIT=1` environment variable or through the daemon or CLI configuration
|
||||
files.
|
||||
|
||||
### `fluentd-async-connect` log opt
|
||||
|
||||
**Deprecated in Release: v20.10**
|
||||
|
||||
The `--log-opt fluentd-async-connect` option for the fluentd logging driver is
|
||||
[deprecated in favor of `--log-opt fluentd-async`](https://github.com/moby/moby/pull/39086).
|
||||
A deprecation message is logged in the daemon logs if the old option is used:
|
||||
|
||||
```console
|
||||
fluent#New: AsyncConnect is now deprecated, please use Async instead
|
||||
```
|
||||
|
||||
Users are encouraged to use the `fluentd-async` option going forward, as support
|
||||
for the old option will be removed in a future release.
|
||||
|
||||
### Pushing and pulling with image manifest v2 schema 1
|
||||
|
||||
**Deprecated in Release: v19.03**
|
||||
@ -289,6 +324,19 @@ format, support for the DAB file format and the top-level docker deploy command
|
||||
(hidden by default in 19.03), will be removed, in favour of `docker stack deploy`
|
||||
using compose files.
|
||||
|
||||
### Support for the `overlay2.override_kernel_check` storage option
|
||||
|
||||
**Deprecated in Release: v19.03**
|
||||
**Disabled in Release: v19.03**
|
||||
|
||||
This daemon configuration option disabled the Linux kernel version check used
|
||||
to detect if the kernel supported OverlayFS with multiple lower dirs, which is
|
||||
required for the overlay2 storage driver. Starting with Docker v19.03.7, the
|
||||
detection was improved to no longer depend on the kernel _version_, so this
|
||||
option was no longer used.
|
||||
|
||||
Docker v22.06 logs a warning in the daemon logs if this option is set, and
|
||||
users should remove this option from their daemon configuration.
|
||||
|
||||
### AuFS storage driver
|
||||
|
||||
@ -570,9 +618,9 @@ Log tags are now generated in a standard way across different logging drivers.
|
||||
Because of which, the driver specific log tag options `syslog-tag`, `gelf-tag` and
|
||||
`fluentd-tag` have been deprecated in favor of the generic `tag` option.
|
||||
|
||||
```bash
|
||||
```console
|
||||
{% raw %}
|
||||
docker --log-driver=syslog --log-opt tag="{{.ImageName}}/{{.Name}}/{{.ID}}"
|
||||
$ docker --log-driver=syslog --log-opt tag="{{.ImageName}}/{{.Name}}/{{.ID}}"
|
||||
{% endraw %}
|
||||
```
|
||||
|
||||
|
||||
@ -14,9 +14,9 @@ keywords: "API, Usage, plugins, documentation, developer"
|
||||
|
||||
# Docker Engine managed plugin system
|
||||
|
||||
* [Installing and using a plugin](index.md#installing-and-using-a-plugin)
|
||||
* [Developing a plugin](index.md#developing-a-plugin)
|
||||
* [Debugging plugins](index.md#debugging-plugins)
|
||||
- [Installing and using a plugin](index.md#installing-and-using-a-plugin)
|
||||
- [Developing a plugin](index.md#developing-a-plugin)
|
||||
- [Debugging plugins](index.md#debugging-plugins)
|
||||
|
||||
Docker Engine's plugin system allows you to install, start, stop, and remove
|
||||
plugins using Docker Engine.
|
||||
@ -55,7 +55,7 @@ enabled, and use it to create a volume.
|
||||
|
||||
1. Install the `sshfs` plugin.
|
||||
|
||||
```bash
|
||||
```console
|
||||
$ docker plugin install vieux/sshfs
|
||||
|
||||
Plugin "vieux/sshfs" is requesting the following privileges:
|
||||
@ -70,11 +70,11 @@ enabled, and use it to create a volume.
|
||||
|
||||
- It needs access to the `host` network.
|
||||
- It needs the `CAP_SYS_ADMIN` capability, which allows the plugin to run
|
||||
the `mount` command.
|
||||
the `mount` command.
|
||||
|
||||
2. Check that the plugin is enabled in the output of `docker plugin ls`.
|
||||
|
||||
```bash
|
||||
```console
|
||||
$ docker plugin ls
|
||||
|
||||
ID NAME TAG DESCRIPTION ENABLED
|
||||
@ -87,7 +87,7 @@ enabled, and use it to create a volume.
|
||||
|
||||
This volume can now be mounted into containers.
|
||||
|
||||
```bash
|
||||
```console
|
||||
$ docker volume create \
|
||||
-d vieux/sshfs \
|
||||
--name sshvolume \
|
||||
@ -96,9 +96,10 @@ enabled, and use it to create a volume.
|
||||
|
||||
sshvolume
|
||||
```
|
||||
|
||||
4. Verify that the volume was created successfully.
|
||||
|
||||
```bash
|
||||
```console
|
||||
$ docker volume ls
|
||||
|
||||
DRIVER NAME
|
||||
@ -107,34 +108,36 @@ enabled, and use it to create a volume.
|
||||
|
||||
5. Start a container that uses the volume `sshvolume`.
|
||||
|
||||
```bash
|
||||
```console
|
||||
$ docker run --rm -v sshvolume:/data busybox ls /data
|
||||
|
||||
<content of /remote on machine 1.2.3.4>
|
||||
```
|
||||
|
||||
6. Remove the volume `sshvolume`
|
||||
```bash
|
||||
docker volume rm sshvolume
|
||||
|
||||
```console
|
||||
$ docker volume rm sshvolume
|
||||
|
||||
sshvolume
|
||||
```
|
||||
|
||||
To disable a plugin, use the `docker plugin disable` command. To completely
|
||||
remove it, use the `docker plugin remove` command. For other available
|
||||
commands and options, see the
|
||||
[command line reference](https://docs.docker.com/engine/reference/commandline/cli/).
|
||||
|
||||
|
||||
## Developing a plugin
|
||||
|
||||
#### The rootfs directory
|
||||
|
||||
The `rootfs` directory represents the root filesystem of the plugin. In this
|
||||
example, it was created from a Dockerfile:
|
||||
|
||||
>**Note:** The `/run/docker/plugins` directory is mandatory inside of the
|
||||
plugin's filesystem for docker to communicate with the plugin.
|
||||
> **Note:** The `/run/docker/plugins` directory is mandatory inside of the
|
||||
> plugin's filesystem for docker to communicate with the plugin.
|
||||
|
||||
```bash
|
||||
```console
|
||||
$ git clone https://github.com/vieux/docker-volume-sshfs
|
||||
$ cd docker-volume-sshfs
|
||||
$ docker build -t rootfsimage .
|
||||
@ -153,19 +156,19 @@ Consider the following `config.json` file.
|
||||
|
||||
```json
|
||||
{
|
||||
"description": "sshFS plugin for Docker",
|
||||
"documentation": "https://docs.docker.com/engine/extend/plugins/",
|
||||
"entrypoint": ["/docker-volume-sshfs"],
|
||||
"network": {
|
||||
"type": "host"
|
||||
},
|
||||
"interface" : {
|
||||
"types": ["docker.volumedriver/1.0"],
|
||||
"socket": "sshfs.sock"
|
||||
},
|
||||
"linux": {
|
||||
"capabilities": ["CAP_SYS_ADMIN"]
|
||||
}
|
||||
"description": "sshFS plugin for Docker",
|
||||
"documentation": "https://docs.docker.com/engine/extend/plugins/",
|
||||
"entrypoint": ["/docker-volume-sshfs"],
|
||||
"network": {
|
||||
"type": "host"
|
||||
},
|
||||
"interface": {
|
||||
"types": ["docker.volumedriver/1.0"],
|
||||
"socket": "sshfs.sock"
|
||||
},
|
||||
"linux": {
|
||||
"capabilities": ["CAP_SYS_ADMIN"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@ -185,7 +188,6 @@ After that the plugin `<plugin-name>` will show up in `docker plugin ls`.
|
||||
Plugins can be pushed to remote registries with
|
||||
`docker plugin push <plugin-name>`.
|
||||
|
||||
|
||||
## Debugging plugins
|
||||
|
||||
Stdout of a plugin is redirected to dockerd logs. Such entries have a
|
||||
@ -193,13 +195,13 @@ Stdout of a plugin is redirected to dockerd logs. Such entries have a
|
||||
`f52a3df433b9aceee436eaada0752f5797aab1de47e5485f1690a073b860ff62` and their
|
||||
corresponding log entries in the docker daemon logs.
|
||||
|
||||
```bash
|
||||
```console
|
||||
$ docker plugin install tiborvass/sample-volume-plugin
|
||||
|
||||
INFO[0036] Starting... Found 0 volumes on startup plugin=f52a3df433b9aceee436eaada0752f5797aab1de47e5485f1690a073b860ff62
|
||||
```
|
||||
|
||||
```bash
|
||||
```console
|
||||
$ docker volume create -d tiborvass/sample-volume-plugin samplevol
|
||||
|
||||
INFO[0193] Create Called... Ensuring directory /data/samplevol exists on host... plugin=f52a3df433b9aceee436eaada0752f5797aab1de47e5485f1690a073b860ff62
|
||||
@ -208,7 +210,7 @@ INFO[0193] Created volume samplevol with mountpoint /data/samp
|
||||
INFO[0193] Path Called... Returned path /data/samplevol plugin=f52a3df433b9aceee436eaada0752f5797aab1de47e5485f1690a073b860ff62
|
||||
```
|
||||
|
||||
```bash
|
||||
```console
|
||||
$ docker run -v samplevol:/tmp busybox sh
|
||||
|
||||
INFO[0421] Get Called... Found samplevol plugin=f52a3df433b9aceee436eaada0752f5797aab1de47e5485f1690a073b860ff62
|
||||
@ -223,8 +225,8 @@ INFO[0421] Unmount Called... Unmounted samplevol plugin=f52a3df433b9a
|
||||
plugins. This is specifically useful to collect plugin logs if they are
|
||||
redirected to a file.
|
||||
|
||||
```bash
|
||||
$ sudo docker-runc --root /var/run/docker/plugins/runtime-root/moby-plugins list
|
||||
```console
|
||||
$ sudo runc --root /run/docker/runtime-runc/plugins.moby list
|
||||
|
||||
ID PID STATUS BUNDLE CREATED OWNER
|
||||
93f1e7dbfe11c938782c2993628c895cf28e2274072c4a346a6002446c949b25 15806 running /run/docker/containerd/daemon/io.containerd.runtime.v1.linux/moby-plugins/93f1e7dbfe11c938782c2993628c895cf28e2274072c4a346a6002446c949b25 2018-02-08T21:40:08.621358213Z root
|
||||
@ -232,14 +234,15 @@ ID PID S
|
||||
c5bb4b90941efcaccca999439ed06d6a6affdde7081bb34dc84126b57b3e793d 14984 running /run/docker/containerd/daemon/io.containerd.runtime.v1.linux/moby-plugins/c5bb4b90941efcaccca999439ed06d6a6affdde7081bb34dc84126b57b3e793d 2018-02-08T21:35:12.321288966Z root
|
||||
```
|
||||
|
||||
```bash
|
||||
$ sudo docker-runc --root /var/run/docker/plugins/runtime-root/moby-plugins exec 93f1e7dbfe11c938782c2993628c895cf28e2274072c4a346a6002446c949b25 cat /var/log/plugin.log
|
||||
```console
|
||||
$ sudo runc --root /run/docker/runtime-runc/plugins.moby exec 93f1e7dbfe11c938782c2993628c895cf28e2274072c4a346a6002446c949b25 cat /var/log/plugin.log
|
||||
```
|
||||
|
||||
If the plugin has a built-in shell, then exec into the plugin can be done as
|
||||
follows:
|
||||
```bash
|
||||
$ sudo docker-runc --root /var/run/docker/plugins/runtime-root/moby-plugins exec -t 93f1e7dbfe11c938782c2993628c895cf28e2274072c4a346a6002446c949b25 sh
|
||||
|
||||
```console
|
||||
$ sudo runc --root /run/docker/runtime-runc/plugins.moby exec -t 93f1e7dbfe11c938782c2993628c895cf28e2274072c4a346a6002446c949b25 sh
|
||||
```
|
||||
|
||||
#### Using curl to debug plugin socket issues.
|
||||
@ -250,18 +253,18 @@ docker host to volume and network plugins using curl 7.47.0 to ensure that
|
||||
the plugin is listening on the said socket. For a well functioning plugin,
|
||||
these basic requests should work. Note that plugin sockets are available on the host under `/var/run/docker/plugins/<pluginID>`
|
||||
|
||||
|
||||
```bash
|
||||
curl -H "Content-Type: application/json" -XPOST -d '{}' --unix-socket /var/run/docker/plugins/e8a37ba56fc879c991f7d7921901723c64df6b42b87e6a0b055771ecf8477a6d/plugin.sock http:/VolumeDriver.List
|
||||
```console
|
||||
$ curl -H "Content-Type: application/json" -XPOST -d '{}' --unix-socket /var/run/docker/plugins/e8a37ba56fc879c991f7d7921901723c64df6b42b87e6a0b055771ecf8477a6d/plugin.sock http:/VolumeDriver.List
|
||||
|
||||
{"Mountpoint":"","Err":"","Volumes":[{"Name":"myvol1","Mountpoint":"/data/myvol1"},{"Name":"myvol2","Mountpoint":"/data/myvol2"}],"Volume":null}
|
||||
```
|
||||
|
||||
```bash
|
||||
curl -H "Content-Type: application/json" -XPOST -d '{}' --unix-socket /var/run/docker/plugins/45e00a7ce6185d6e365904c8bcf62eb724b1fe307e0d4e7ecc9f6c1eb7bcdb70/plugin.sock http:/NetworkDriver.GetCapabilities
|
||||
```console
|
||||
$ curl -H "Content-Type: application/json" -XPOST -d '{}' --unix-socket /var/run/docker/plugins/45e00a7ce6185d6e365904c8bcf62eb724b1fe307e0d4e7ecc9f6c1eb7bcdb70/plugin.sock http:/NetworkDriver.GetCapabilities
|
||||
|
||||
{"Scope":"local"}
|
||||
```
|
||||
|
||||
When using curl 7.5 and above, the URL should be of the form
|
||||
`http://hostname/APICall`, where `hostname` is the valid hostname where the
|
||||
plugin is installed and `APICall` is the call to the plugin API.
|
||||
|
||||
@ -72,15 +72,15 @@ The sections below provide an inexhaustive overview of available plugins.
|
||||
| [Horcrux Volume Plugin](https://github.com/muthu-r/horcrux) | A volume plugin that allows on-demand, version controlled access to your data. Horcrux is an open-source plugin, written in Go, and supports SCP, [Minio](https://www.minio.io) and Amazon S3. |
|
||||
| [HPE 3Par Volume Plugin](https://github.com/hpe-storage/python-hpedockerplugin/) | A volume plugin that supports HPE 3Par and StoreVirtual iSCSI storage arrays. |
|
||||
| [Infinit volume plugin](https://infinit.sh/documentation/docker/volume-plugin) | A volume plugin that makes it easy to mount and manage Infinit volumes using Docker. |
|
||||
| [IPFS Volume Plugin](http://github.com/vdemeester/docker-volume-ipfs) | An open source volume plugin that allows using an [ipfs](https://ipfs.io/) filesystem as a volume. |
|
||||
| [IPFS Volume Plugin](https://github.com/vdemeester/docker-volume-ipfs) | An open source volume plugin that allows using an [ipfs](https://ipfs.io/) filesystem as a volume. |
|
||||
| [Keywhiz plugin](https://github.com/calavera/docker-volume-keywhiz) | A plugin that provides credentials and secret management using Keywhiz as a central repository. |
|
||||
| [Local Persist Plugin](https://github.com/CWSpear/local-persist) | A volume plugin that extends the default `local` driver's functionality by allowing you specify a mountpoint anywhere on the host, which enables the files to *always persist*, even if the volume is removed via `docker volume rm`. |
|
||||
| [NetApp Plugin](https://github.com/NetApp/netappdvp) (nDVP) | A volume plugin that provides direct integration with the Docker ecosystem for the NetApp storage portfolio. The nDVP package supports the provisioning and management of storage resources from the storage platform to Docker hosts, with a robust framework for adding additional platforms in the future. |
|
||||
| [Netshare plugin](https://github.com/ContainX/docker-volume-netshare) | A volume plugin that provides volume management for NFS 3/4, AWS EFS and CIFS file systems. |
|
||||
| [Nimble Storage Volume Plugin](https://connect.nimblestorage.com/community/app-integration/docker) | A volume plug-in that integrates with Nimble Storage Unified Flash Fabric arrays. The plug-in abstracts array volume capabilities to the Docker administrator to allow self-provisioning of secure multi-tenant volumes and clones. |
|
||||
| [Nimble Storage Volume Plugin](https://scod.hpedev.io/docker_volume_plugins/hpe_nimble_storage/index.html) | A volume plug-in that integrates with Nimble Storage Unified Flash Fabric arrays. The plug-in abstracts array volume capabilities to the Docker administrator to allow self-provisioning of secure multi-tenant volumes and clones. |
|
||||
| [OpenStorage Plugin](https://github.com/libopenstorage/openstorage) | A cluster-aware volume plugin that provides volume management for file and block storage solutions. It implements a vendor neutral specification for implementing extensions such as CoS, encryption, and snapshots. It has example drivers based on FUSE, NFS, NBD and EBS to name a few. |
|
||||
| [Portworx Volume Plugin](https://github.com/portworx/px-dev) | A volume plugin that turns any server into a scale-out converged compute/storage node, providing container granular storage and highly available volumes across any node, using a shared-nothing storage backend that works with any docker scheduler. |
|
||||
| [Quobyte Volume Plugin](https://github.com/quobyte/docker-volume) | A volume plugin that connects Docker to [Quobyte](http://www.quobyte.com/containers)'s data center file system, a general-purpose scalable and fault-tolerant storage platform. |
|
||||
| [Quobyte Volume Plugin](https://github.com/quobyte/docker-volume) | A volume plugin that connects Docker to [Quobyte](https://www.quobyte.com/containers)'s data center file system, a general-purpose scalable and fault-tolerant storage platform. |
|
||||
| [REX-Ray plugin](https://github.com/emccode/rexray) | A volume plugin which is written in Go and provides advanced storage functionality for many platforms including VirtualBox, EC2, Google Compute Engine, OpenStack, and EMC. |
|
||||
| [Virtuozzo Storage and Ploop plugin](https://github.com/virtuozzo/docker-volume-ploop) | A volume plugin with support for Virtuozzo Storage distributed cloud file system as well as ploop devices. |
|
||||
| [VMware vSphere Storage Plugin](https://github.com/vmware/docker-volume-vsphere) | Docker Volume Driver for vSphere enables customers to address persistent storage requirements for Docker containers in vSphere environments. |
|
||||
|
||||
@ -90,7 +90,7 @@ The `TLSConfig` field is optional and TLS will only be verified if this configur
|
||||
Plugins should be started before Docker, and stopped after Docker. For
|
||||
example, when packaging a plugin for a platform which supports `systemd`, you
|
||||
might use [`systemd` dependencies](
|
||||
http://www.freedesktop.org/software/systemd/man/systemd.unit.html#Before=) to
|
||||
https://www.freedesktop.org/software/systemd/man/systemd.unit.html#Before=) to
|
||||
manage startup and shutdown order.
|
||||
|
||||
When upgrading a plugin, you should first stop the Docker daemon, upgrade the
|
||||
@ -114,7 +114,7 @@ a `service` file and a `socket` file.
|
||||
|
||||
The `service` file (for example `/lib/systemd/system/your-plugin.service`):
|
||||
|
||||
```
|
||||
```systemd
|
||||
[Unit]
|
||||
Description=Your plugin
|
||||
Before=docker.service
|
||||
@ -127,9 +127,10 @@ ExecStart=/usr/lib/docker/your-plugin
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
|
||||
The `socket` file (for example `/lib/systemd/system/your-plugin.socket`):
|
||||
|
||||
```
|
||||
```systemd
|
||||
[Unit]
|
||||
Description=Your plugin
|
||||
|
||||
@ -166,7 +167,8 @@ Plugins are activated via the following "handshake" API call.
|
||||
**Request:** empty body
|
||||
|
||||
**Response:**
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"Implements": ["VolumeDriver"]
|
||||
}
|
||||
|
||||
@ -114,9 +114,9 @@ Enable the authorization plugin with a dedicated command line flag in the
|
||||
`--authorization-plugin=PLUGIN_ID` format. The flag supplies a `PLUGIN_ID`
|
||||
value. This value can be the plugin’s socket or a path to a specification file.
|
||||
Authorization plugins can be loaded without restarting the daemon. Refer
|
||||
to the [`dockerd` documentation](../reference/commandline/dockerd.md#configuration-reloading) for more information.
|
||||
to the [`dockerd` documentation](../reference/commandline/dockerd.md#configuration-reload-behavior) for more information.
|
||||
|
||||
```bash
|
||||
```console
|
||||
$ dockerd --authorization-plugin=plugin1 --authorization-plugin=plugin2,...
|
||||
```
|
||||
|
||||
@ -124,26 +124,26 @@ Docker's authorization subsystem supports multiple `--authorization-plugin` para
|
||||
|
||||
### Calling authorized command (allow)
|
||||
|
||||
```bash
|
||||
```console
|
||||
$ docker pull centos
|
||||
...
|
||||
<...>
|
||||
f1b10cd84249: Pull complete
|
||||
...
|
||||
<...>
|
||||
```
|
||||
|
||||
### Calling unauthorized command (deny)
|
||||
|
||||
```bash
|
||||
```console
|
||||
$ docker pull centos
|
||||
...
|
||||
<...>
|
||||
docker: Error response from daemon: authorization denied by plugin PLUGIN_NAME: volumes are not allowed.
|
||||
```
|
||||
|
||||
### Error from plugins
|
||||
|
||||
```bash
|
||||
```console
|
||||
$ docker pull centos
|
||||
...
|
||||
<...>
|
||||
docker: Error response from daemon: plugin PLUGIN_NAME failed with error: AuthZPlugin.AuthZReq: Cannot connect to the Docker daemon. Is the docker daemon running on this host?.
|
||||
```
|
||||
|
||||
@ -180,6 +180,7 @@ should implement the following two methods:
|
||||
"Err": "The error message if things go wrong"
|
||||
}
|
||||
```
|
||||
|
||||
#### /AuthZPlugin.AuthZRes
|
||||
|
||||
**Request**:
|
||||
|
||||
@ -31,7 +31,7 @@ You need to install and enable the plugin and then restart the Docker daemon
|
||||
before using the plugin. See the following example for the correct ordering
|
||||
of steps.
|
||||
|
||||
```
|
||||
```console
|
||||
$ docker plugin install cpuguy83/docker-overlay2-graphdriver-plugin # this command also enables the driver
|
||||
<output suppressed>
|
||||
$ pkill dockerd
|
||||
@ -309,6 +309,7 @@ Get an archive of the changes between the filesystem layers specified by the `ID
|
||||
and `Parent`. `Parent` may be an empty string, in which case there is no parent.
|
||||
|
||||
**Response**:
|
||||
|
||||
```
|
||||
{% raw %}
|
||||
{{ TAR STREAM }}
|
||||
@ -354,6 +355,7 @@ Respond with a non-empty string error if an error occurred.
|
||||
### /GraphDriver.ApplyDiff
|
||||
|
||||
**Request**:
|
||||
|
||||
```
|
||||
{% raw %}
|
||||
{{ TAR STREAM }}
|
||||
|
||||
@ -211,6 +211,7 @@ as they come in once the existing logs have been read.
|
||||
to determine what set of logs to read.
|
||||
|
||||
**Response**:
|
||||
|
||||
```
|
||||
{% raw %}{{ log stream }}{% endraw %}
|
||||
```
|
||||
|
||||
@ -42,7 +42,7 @@ Once running however, network driver plugins are used just like the built-in
|
||||
network drivers: by being mentioned as a driver in network-oriented Docker
|
||||
commands. For example,
|
||||
|
||||
```bash
|
||||
```console
|
||||
$ docker network create --driver weave mynet
|
||||
```
|
||||
|
||||
@ -51,7 +51,7 @@ Some network driver plugins are listed in [plugins](legacy_plugins.md)
|
||||
The `mynet` network is now owned by `weave`, so subsequent commands
|
||||
referring to that network will be sent to the plugin,
|
||||
|
||||
```bash
|
||||
```console
|
||||
$ docker run --network=mynet busybox top
|
||||
```
|
||||
|
||||
|
||||
@ -29,20 +29,20 @@ node1 is the manager and node2 is the worker.
|
||||
|
||||
1. Prepare manager. In node 1:
|
||||
|
||||
```bash
|
||||
```console
|
||||
$ docker swarm init
|
||||
Swarm initialized: current node (dxn1zf6l61qsb1josjja83ngz) is now a manager.
|
||||
```
|
||||
|
||||
2. Join swarm, install plugin and create volume on worker. In node 2:
|
||||
|
||||
```bash
|
||||
```console
|
||||
$ docker swarm join \
|
||||
--token SWMTKN-1-49nj1cmql0jkz5s954yi3oex3nedyz0fb0xx14ie39trti4wxv-8vxv8rssmk743ojnwacrr2e7c \
|
||||
192.168.99.100:2377
|
||||
--token SWMTKN-1-49nj1cmql0jkz5s954yi3oex3nedyz0fb0xx14ie39trti4wxv-8vxv8rssmk743ojnwacrr2e7c \
|
||||
192.168.99.100:2377
|
||||
```
|
||||
|
||||
```bash
|
||||
```console
|
||||
$ docker plugin install tiborvass/sample-volume-plugin
|
||||
latest: Pulling from tiborvass/sample-volume-plugin
|
||||
eb9c16fbdc53: Download complete
|
||||
@ -51,23 +51,24 @@ node1 is the manager and node2 is the worker.
|
||||
Installed plugin tiborvass/sample-volume-plugin
|
||||
```
|
||||
|
||||
```bash
|
||||
```console
|
||||
$ docker volume create -d tiborvass/sample-volume-plugin --name pluginVol
|
||||
```
|
||||
|
||||
3. Create a service using the plugin and volume. In node1:
|
||||
|
||||
```bash
|
||||
```console
|
||||
$ docker service create --name my-service --mount type=volume,volume-driver=tiborvass/sample-volume-plugin,source=pluginVol,destination=/tmp busybox top
|
||||
|
||||
$ docker service ls
|
||||
z1sj8bb8jnfn my-service replicated 1/1 busybox:latest
|
||||
```
|
||||
docker service ls shows service 1 instance of service running.
|
||||
|
||||
`docker service ls` shows service 1 instance of service running.
|
||||
|
||||
4. Observe the task getting scheduled in node 2:
|
||||
|
||||
```bash
|
||||
```console
|
||||
{% raw %}
|
||||
$ docker ps --format '{{.ID}}\t {{.Status}} {{.Names}} {{.Command}}'
|
||||
83fc1e842599 Up 2 days my-service.1.9jn59qzn7nbc3m0zt1hij12xs "top"
|
||||
@ -87,7 +88,7 @@ Note that node1 is the manager and node2 is the worker.
|
||||
1. Install a global scoped network plugin on both manager and worker. On node1
|
||||
and node2:
|
||||
|
||||
```bash
|
||||
```console
|
||||
$ docker plugin install bboreham/weave2
|
||||
Plugin "bboreham/weave2" is requesting the following privileges:
|
||||
- network: [host]
|
||||
@ -102,7 +103,7 @@ Note that node1 is the manager and node2 is the worker.
|
||||
|
||||
2. Create a network using plugin on manager. On node1:
|
||||
|
||||
```bash
|
||||
```console
|
||||
$ docker network create --driver=bboreham/weave2:latest globalnet
|
||||
|
||||
$ docker network ls
|
||||
@ -115,12 +116,12 @@ containers get scheduled on both manager and worker.
|
||||
|
||||
On node 1:
|
||||
|
||||
```bash
|
||||
```console
|
||||
$ docker service create --network globalnet --name myservice --replicas=8 mrjana/simpleweb simpleweb
|
||||
w90drnfzw85nygbie9kb89vpa
|
||||
```
|
||||
|
||||
```bash
|
||||
```console
|
||||
$ docker ps
|
||||
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
|
||||
87520965206a mrjana/simpleweb@sha256:317d7f221d68c86d503119b0ea12c29de42af0a22ca087d522646ad1069a47a4 "simpleweb" 5 seconds ago Up 4 seconds myservice.4.ytdzpktmwor82zjxkh118uf1v
|
||||
@ -131,7 +132,7 @@ w90drnfzw85nygbie9kb89vpa
|
||||
|
||||
On node 2:
|
||||
|
||||
```bash
|
||||
```console
|
||||
$ docker ps
|
||||
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
|
||||
53c0ae7c1dae mrjana/simpleweb@sha256:317d7f221d68c86d503119b0ea12c29de42af0a22ca087d522646ad1069a47a4 "simpleweb" 2 seconds ago Up Less than a second myservice.7.x44tvvdm3iwkt9kif35f7ykz1
|
||||
@ -142,14 +143,14 @@ w90drnfzw85nygbie9kb89vpa
|
||||
|
||||
4. Scale down the number of instances. On node1:
|
||||
|
||||
```bash
|
||||
```console
|
||||
$ docker service scale myservice=0
|
||||
myservice scaled to 0
|
||||
```
|
||||
|
||||
5. Disable and uninstall the plugin on the worker. On node2:
|
||||
|
||||
```bash
|
||||
```console
|
||||
$ docker plugin rm -f bboreham/weave2
|
||||
bboreham/weave2
|
||||
```
|
||||
@ -159,12 +160,12 @@ scheduled on the master and not on the worker, because the plugin is not availab
|
||||
|
||||
On node 1:
|
||||
|
||||
```bash
|
||||
```console
|
||||
$ docker service scale myservice=8
|
||||
myservice scaled to 8
|
||||
```
|
||||
|
||||
```bash
|
||||
```console
|
||||
$ docker ps
|
||||
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
|
||||
cf4b0ec2415e mrjana/simpleweb@sha256:317d7f221d68c86d503119b0ea12c29de42af0a22ca087d522646ad1069a47a4 "simpleweb" 39 seconds ago Up 36 seconds myservice.3.r7p5o208jmlzpcbm2ytl3q6n1
|
||||
@ -179,7 +180,7 @@ scheduled on the master and not on the worker, because the plugin is not availab
|
||||
|
||||
On node 2:
|
||||
|
||||
```bash
|
||||
```console
|
||||
$ docker ps
|
||||
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
|
||||
```
|
||||
|
||||
@ -54,7 +54,7 @@ flags on the `docker container run` command. The `--volume` (or `-v`) flag
|
||||
accepts a volume name and path on the host, and the `--volume-driver` flag
|
||||
accepts a driver type.
|
||||
|
||||
```bash
|
||||
```console
|
||||
$ docker volume create --driver=flocker volumename
|
||||
|
||||
$ docker container run -it --volume volumename:/data busybox sh
|
||||
|
||||
@ -112,7 +112,7 @@ instructions.
|
||||
|
||||
Whenever possible, Docker uses a build-cache to accelerate the `docker build`
|
||||
process significantly. This is indicated by the `CACHED` message in the console
|
||||
output. (For more information, see the [`Dockerfile` best practices guide](https://docs.docker.com/engine/userguide/eng-image/dockerfile_best-practices/):
|
||||
output. (For more information, see the [`Dockerfile` best practices guide](https://docs.docker.com/engine/userguide/eng-image/dockerfile_best-practices/)):
|
||||
|
||||
```console
|
||||
$ docker build -t svendowideit/ambassador .
|
||||
@ -159,8 +159,8 @@ implementation. For example, BuildKit can:
|
||||
To use the BuildKit backend, you need to set an environment variable
|
||||
`DOCKER_BUILDKIT=1` on the CLI before invoking `docker build`.
|
||||
|
||||
To learn about the experimental Dockerfile syntax available to BuildKit-based
|
||||
builds [refer to the documentation in the BuildKit repository](https://github.com/moby/buildkit/blob/master/frontend/dockerfile/docs/experimental.md).
|
||||
To learn about the Dockerfile syntax available to BuildKit-based
|
||||
builds [refer to the documentation in the BuildKit repository](https://github.com/moby/buildkit/blob/master/frontend/dockerfile/docs/syntax.md).
|
||||
|
||||
## Format
|
||||
|
||||
@ -179,7 +179,7 @@ Docker runs instructions in a `Dockerfile` in order. A `Dockerfile` **must
|
||||
begin with a `FROM` instruction**. This may be after [parser
|
||||
directives](#parser-directives), [comments](#format), and globally scoped
|
||||
[ARGs](#arg). The `FROM` instruction specifies the [*Parent
|
||||
Image*](https://docs.docker.com/glossary/#parent_image) from which you are
|
||||
Image*](https://docs.docker.com/glossary/#parent-image) from which you are
|
||||
building. `FROM` may only be preceded by one or more `ARG` instructions, which
|
||||
declare arguments that are used in `FROM` lines in the `Dockerfile`.
|
||||
|
||||
@ -599,10 +599,10 @@ This file causes the following build behavior:
|
||||
|
||||
|
||||
Matching is done using Go's
|
||||
[filepath.Match](http://golang.org/pkg/path/filepath#Match) rules. A
|
||||
[filepath.Match](https://golang.org/pkg/path/filepath#Match) rules. A
|
||||
preprocessing step removes leading and trailing whitespace and
|
||||
eliminates `.` and `..` elements using Go's
|
||||
[filepath.Clean](http://golang.org/pkg/path/filepath/#Clean). Lines
|
||||
[filepath.Clean](https://golang.org/pkg/path/filepath/#Clean). Lines
|
||||
that are blank after preprocessing are ignored.
|
||||
|
||||
Beyond Go's filepath.Match rules, Docker also supports a special
|
||||
@ -677,7 +677,7 @@ FROM [--platform=<platform>] <image>[@<digest>] [AS <name>]
|
||||
```
|
||||
|
||||
The `FROM` instruction initializes a new build stage and sets the
|
||||
[*Base Image*](https://docs.docker.com/glossary/#base_image) for subsequent instructions. As such, a
|
||||
[*Base Image*](https://docs.docker.com/glossary/#base-image) for subsequent instructions. As such, a
|
||||
valid `Dockerfile` must start with a `FROM` instruction. The image can be
|
||||
any valid image – it is especially easy to start by **pulling an image** from
|
||||
the [*Public Repositories*](https://docs.docker.com/docker-hub/repos/).
|
||||
@ -759,6 +759,7 @@ RUN instruction onto the next line. For example, consider these two lines:
|
||||
RUN /bin/bash -c 'source $HOME/.bashrc; \
|
||||
echo $HOME'
|
||||
```
|
||||
|
||||
Together they are equivalent to this single line:
|
||||
|
||||
```dockerfile
|
||||
@ -938,6 +939,7 @@ the `--format` option to show just the labels;
|
||||
```console
|
||||
$ docker image inspect --format='{{json .Config.Labels}}' myimage
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"com.example.vendor": "ACME Incorporated",
|
||||
@ -1115,7 +1117,7 @@ directories, their paths are interpreted as relative to the source of
|
||||
the context of the build.
|
||||
|
||||
Each `<src>` may contain wildcards and matching will be done using Go's
|
||||
[filepath.Match](http://golang.org/pkg/path/filepath#Match) rules. For example:
|
||||
[filepath.Match](https://golang.org/pkg/path/filepath#Match) rules. For example:
|
||||
|
||||
To add all files starting with "hom":
|
||||
|
||||
@ -1291,7 +1293,7 @@ directories will be interpreted as relative to the source of the context
|
||||
of the build.
|
||||
|
||||
Each `<src>` may contain wildcards and matching will be done using Go's
|
||||
[filepath.Match](http://golang.org/pkg/path/filepath#Match) rules. For example:
|
||||
[filepath.Match](https://golang.org/pkg/path/filepath#Match) rules. For example:
|
||||
|
||||
To add all files starting with "hom":
|
||||
|
||||
@ -1630,7 +1632,7 @@ If you forget to add `exec` to the beginning of your `ENTRYPOINT`:
|
||||
```dockerfile
|
||||
FROM ubuntu
|
||||
ENTRYPOINT top -b
|
||||
CMD --ignored-param1
|
||||
CMD -- --ignored-param1
|
||||
```
|
||||
|
||||
You can then run it (giving it a name for the next step):
|
||||
@ -1638,12 +1640,15 @@ You can then run it (giving it a name for the next step):
|
||||
```console
|
||||
$ docker run -it --name test top --ignored-param2
|
||||
|
||||
Mem: 1704184K used, 352484K free, 0K shrd, 0K buff, 140621524238337K cached
|
||||
CPU: 9% usr 2% sys 0% nic 88% idle 0% io 0% irq 0% sirq
|
||||
Load average: 0.01 0.02 0.05 2/101 7
|
||||
PID PPID USER STAT VSZ %VSZ %CPU COMMAND
|
||||
1 0 root S 3168 0% 0% /bin/sh -c top -b cmd cmd2
|
||||
7 1 root R 3164 0% 0% top -b
|
||||
top - 13:58:24 up 17 min, 0 users, load average: 0.00, 0.00, 0.00
|
||||
Tasks: 2 total, 1 running, 1 sleeping, 0 stopped, 0 zombie
|
||||
%Cpu(s): 16.7 us, 33.3 sy, 0.0 ni, 50.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
|
||||
MiB Mem : 1990.8 total, 1354.6 free, 231.4 used, 404.7 buff/cache
|
||||
MiB Swap: 1024.0 total, 1024.0 free, 0.0 used. 1639.8 avail Mem
|
||||
|
||||
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
|
||||
1 root 20 0 2612 604 536 S 0.0 0.0 0:00.02 sh
|
||||
6 root 20 0 5956 3188 2768 R 0.0 0.2 0:00.00 top
|
||||
```
|
||||
|
||||
You can see from the output of `top` that the specified `ENTRYPOINT` is not `PID 1`.
|
||||
@ -1652,12 +1657,12 @@ If you then run `docker stop test`, the container will not exit cleanly - the
|
||||
`stop` command will be forced to send a `SIGKILL` after the timeout:
|
||||
|
||||
```console
|
||||
$ docker exec -it test ps aux
|
||||
$ docker exec -it test ps waux
|
||||
|
||||
PID USER COMMAND
|
||||
1 root /bin/sh -c top -b cmd cmd2
|
||||
7 root top -b
|
||||
8 root ps aux
|
||||
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
|
||||
root 1 0.4 0.0 2612 604 pts/0 Ss+ 13:58 0:00 /bin/sh -c top -b --ignored-param2
|
||||
root 6 0.0 0.1 5956 3188 pts/0 S+ 13:58 0:00 top -b
|
||||
root 7 0.0 0.1 5884 2816 pts/1 Rs+ 13:58 0:00 ps waux
|
||||
|
||||
$ /usr/bin/time docker stop test
|
||||
|
||||
@ -1822,6 +1827,11 @@ RUN pwd
|
||||
The output of the final `pwd` command in this `Dockerfile` would be
|
||||
`/path/$DIRNAME`
|
||||
|
||||
If not specified, the default working directory is `/`. In practice, if you aren't building a Dockerfile from scratch (`FROM scratch`),
|
||||
the `WORKDIR` may likely be set by the base image you're using.
|
||||
|
||||
Therefore, to avoid unintended operations in unknown directories, it is best practice to set your `WORKDIR` explicitly.
|
||||
|
||||
## ARG
|
||||
|
||||
```dockerfile
|
||||
@ -2171,9 +2181,14 @@ ONBUILD RUN /usr/local/bin/python-build --dir /app/src
|
||||
STOPSIGNAL signal
|
||||
```
|
||||
|
||||
The `STOPSIGNAL` instruction sets the system call signal that will be sent to the container to exit.
|
||||
This signal can be a valid unsigned number that matches a position in the kernel's syscall table, for instance 9,
|
||||
or a signal name in the format SIGNAME, for instance SIGKILL.
|
||||
The `STOPSIGNAL` instruction sets the system call signal that will be sent to the
|
||||
container to exit. This signal can be a signal name in the format `SIG<NAME>`,
|
||||
for instance `SIGKILL`, or an unsigned number that matches a position in the
|
||||
kernel's syscall table, for instance `9`. The default is `SIGTERM` if not
|
||||
defined.
|
||||
|
||||
The image's default stopsignal can be overridden per container, using the
|
||||
`--stop-signal` flag on `docker run` and `docker create`.
|
||||
|
||||
## HEALTHCHECK
|
||||
|
||||
|
||||
@ -84,7 +84,7 @@ containers, see [**Configuration file** section](cli.md#configuration-files).
|
||||
|
||||
### Attach to and detach from a running container
|
||||
|
||||
```bash
|
||||
```console
|
||||
$ docker run -d --name topdemo ubuntu /usr/bin/top -b
|
||||
|
||||
$ docker attach topdemo
|
||||
@ -130,22 +130,19 @@ $ docker ps -a | grep topdemo
|
||||
And in this second example, you can see the exit code returned by the `bash`
|
||||
process is returned by the `docker attach` command to its caller too:
|
||||
|
||||
```bash
|
||||
$ docker run --name test -d -it debian
|
||||
```console
|
||||
$ docker run --name test -d -it debian
|
||||
275c44472aebd77c926d4527885bb09f2f6db21d878c75f0a1c212c03d3bcfab
|
||||
|
||||
275c44472aebd77c926d4527885bb09f2f6db21d878c75f0a1c212c03d3bcfab
|
||||
$ docker attach test
|
||||
root@f38c87f2a42d:/# exit 13
|
||||
|
||||
$ docker attach test
|
||||
exit
|
||||
|
||||
root@f38c87f2a42d:/# exit 13
|
||||
$ echo $?
|
||||
13
|
||||
|
||||
exit
|
||||
$ docker ps -a | grep test
|
||||
|
||||
$ echo $?
|
||||
|
||||
13
|
||||
|
||||
$ docker ps -a | grep test
|
||||
|
||||
275c44472aeb debian:7 "/bin/bash" 26 seconds ago Exited (13) 17 seconds ago test
|
||||
275c44472aeb debian:7 "/bin/bash" 26 seconds ago Exited (13) 17 seconds ago test
|
||||
```
|
||||
|
||||
@ -92,23 +92,23 @@ context.
|
||||
For example, run this command to use a directory called `docker` in the branch
|
||||
`container`:
|
||||
|
||||
```bash
|
||||
```console
|
||||
$ docker build https://github.com/docker/rootfs.git#container:docker
|
||||
```
|
||||
|
||||
The following table represents all the valid suffixes with their build
|
||||
contexts:
|
||||
|
||||
Build Syntax Suffix | Commit Used | Build Context Used
|
||||
--------------------------------|-----------------------|-------------------
|
||||
`myrepo.git` | `refs/heads/master` | `/`
|
||||
`myrepo.git#mytag` | `refs/tags/mytag` | `/`
|
||||
`myrepo.git#mybranch` | `refs/heads/mybranch` | `/`
|
||||
`myrepo.git#pull/42/head` | `refs/pull/42/head` | `/`
|
||||
`myrepo.git#:myfolder` | `refs/heads/master` | `/myfolder`
|
||||
`myrepo.git#master:myfolder` | `refs/heads/master` | `/myfolder`
|
||||
`myrepo.git#mytag:myfolder` | `refs/tags/mytag` | `/myfolder`
|
||||
`myrepo.git#mybranch:myfolder` | `refs/heads/mybranch` | `/myfolder`
|
||||
| Build Syntax Suffix | Commit Used | Build Context Used |
|
||||
|--------------------------------|-----------------------|--------------------|
|
||||
| `myrepo.git` | `refs/heads/master` | `/` |
|
||||
| `myrepo.git#mytag` | `refs/tags/mytag` | `/` |
|
||||
| `myrepo.git#mybranch` | `refs/heads/mybranch` | `/` |
|
||||
| `myrepo.git#pull/42/head` | `refs/pull/42/head` | `/` |
|
||||
| `myrepo.git#:myfolder` | `refs/heads/master` | `/myfolder` |
|
||||
| `myrepo.git#master:myfolder` | `refs/heads/master` | `/myfolder` |
|
||||
| `myrepo.git#mytag:myfolder` | `refs/tags/mytag` | `/myfolder` |
|
||||
| `myrepo.git#mybranch:myfolder` | `refs/heads/mybranch` | `/myfolder` |
|
||||
|
||||
> **Note**
|
||||
>
|
||||
@ -120,7 +120,7 @@ Build Syntax Suffix | Commit Used | Build Context Used
|
||||
|
||||
If you pass an URL to a remote tarball, the URL itself is sent to the daemon:
|
||||
|
||||
```bash
|
||||
```console
|
||||
$ docker build http://server/context.tar.gz
|
||||
```
|
||||
|
||||
@ -136,7 +136,7 @@ build context. Tarball contexts must be tar archives conforming to the standard
|
||||
Instead of specifying a context, you can pass a single `Dockerfile` in the
|
||||
`URL` or pipe the file in via `STDIN`. To pipe a `Dockerfile` from `STDIN`:
|
||||
|
||||
```bash
|
||||
```console
|
||||
$ docker build - < Dockerfile
|
||||
```
|
||||
|
||||
@ -176,7 +176,7 @@ build fails, a non-zero failure code will be returned.
|
||||
There should be informational output of the reason for failure output to
|
||||
`STDERR`:
|
||||
|
||||
```bash
|
||||
```console
|
||||
$ docker build -t fail .
|
||||
|
||||
Sending build context to Docker daemon 2.048 kB
|
||||
@ -198,7 +198,7 @@ See also:
|
||||
|
||||
### Build with PATH
|
||||
|
||||
```bash
|
||||
```console
|
||||
$ docker build .
|
||||
|
||||
Uploading context 10240 bytes
|
||||
@ -243,7 +243,7 @@ you must use `--rm=false`. This does not affect the build cache.
|
||||
|
||||
### Build with URL
|
||||
|
||||
```bash
|
||||
```console
|
||||
$ docker build github.com/creack/docker-firefox
|
||||
```
|
||||
|
||||
@ -251,7 +251,7 @@ This will clone the GitHub repository and use the cloned repository as context.
|
||||
The Dockerfile at the root of the repository is used as Dockerfile. You can
|
||||
specify an arbitrary Git repository by using the `git://` or `git@` scheme.
|
||||
|
||||
```bash
|
||||
```console
|
||||
$ docker build -f ctx/Dockerfile http://server/ctx.tar.gz
|
||||
|
||||
Downloading context: http://server/ctx.tar.gz [===================>] 240 B/240 B
|
||||
@ -277,7 +277,7 @@ ctx/container.cfg /` operation works as expected.
|
||||
|
||||
### Build with -
|
||||
|
||||
```bash
|
||||
```console
|
||||
$ docker build - < Dockerfile
|
||||
```
|
||||
|
||||
@ -286,7 +286,7 @@ context, no contents of any local directory will be sent to the Docker daemon.
|
||||
Since there is no context, a Dockerfile `ADD` only works if it refers to a
|
||||
remote URL.
|
||||
|
||||
```bash
|
||||
```console
|
||||
$ docker build - < context.tar.gz
|
||||
```
|
||||
|
||||
@ -295,7 +295,7 @@ formats are: bzip2, gzip and xz.
|
||||
|
||||
### Use a .dockerignore file
|
||||
|
||||
```bash
|
||||
```console
|
||||
$ docker build .
|
||||
|
||||
Uploading context 18.829 MB
|
||||
@ -323,18 +323,18 @@ directory from the context. Its effect can be seen in the changed size of the
|
||||
uploaded context. The builder reference contains detailed information on
|
||||
[creating a .dockerignore file](../builder.md#dockerignore-file).
|
||||
|
||||
When using the [BuildKit backend](../builder.md#buildkit), `docker build` searches
|
||||
for a `.dockerignore` file relative to the Dockerfile name. For example, running
|
||||
`docker build -f myapp.Dockerfile .` will first look for an ignore file named
|
||||
`myapp.Dockerfile.dockerignore`. If such a file is not found, the `.dockerignore`
|
||||
file is used if present. Using a Dockerfile based `.dockerignore` is useful if a
|
||||
project contains multiple Dockerfiles that expect to ignore different sets of
|
||||
files.
|
||||
When using the [BuildKit backend](https://docs.docker.com/build/buildkit/),
|
||||
`docker build` searches for a `.dockerignore` file relative to the Dockerfile
|
||||
name. For example, running `docker build -f myapp.Dockerfile .` will first look
|
||||
for an ignore file named `myapp.Dockerfile.dockerignore`. If such a file is not
|
||||
found, the `.dockerignore` file is used if present. Using a Dockerfile based
|
||||
`.dockerignore` is useful if a project contains multiple Dockerfiles that
|
||||
expect to ignore different sets of files.
|
||||
|
||||
|
||||
### Tag an image (-t)
|
||||
|
||||
```bash
|
||||
```console
|
||||
$ docker build -t vieux/apache:2.0 .
|
||||
```
|
||||
|
||||
@ -348,27 +348,27 @@ version.
|
||||
For example, to tag an image both as `whenry/fedora-jboss:latest` and
|
||||
`whenry/fedora-jboss:v2.1`, use the following:
|
||||
|
||||
```bash
|
||||
```console
|
||||
$ docker build -t whenry/fedora-jboss:latest -t whenry/fedora-jboss:v2.1 .
|
||||
```
|
||||
|
||||
### Specify a Dockerfile (-f)
|
||||
|
||||
```bash
|
||||
```console
|
||||
$ docker build -f Dockerfile.debug .
|
||||
```
|
||||
|
||||
This will use a file called `Dockerfile.debug` for the build instructions
|
||||
instead of `Dockerfile`.
|
||||
|
||||
```bash
|
||||
```console
|
||||
$ curl example.com/remote/Dockerfile | docker build -f - .
|
||||
```
|
||||
|
||||
The above command will use the current directory as the build context and read
|
||||
a Dockerfile from stdin.
|
||||
|
||||
```bash
|
||||
```console
|
||||
$ docker build -f dockerfiles/Dockerfile.debug -t myapp_debug .
|
||||
$ docker build -f dockerfiles/Dockerfile.prod -t myapp_prod .
|
||||
```
|
||||
@ -377,7 +377,7 @@ The above commands will build the current build context (as specified by the
|
||||
`.`) twice, once using a debug version of a `Dockerfile` and once using a
|
||||
production version.
|
||||
|
||||
```bash
|
||||
```console
|
||||
$ cd /home/me/myapp/some/dir/really/deep
|
||||
$ docker build -f /home/me/myapp/dockerfiles/debug /home/me/myapp
|
||||
$ docker build -f ../../../../dockerfiles/debug /home/me/myapp
|
||||
@ -420,7 +420,7 @@ A good example is `http_proxy` or source versions for pulling intermediate
|
||||
files. The `ARG` instruction lets Dockerfile authors define values that users
|
||||
can set at build-time using the `--build-arg` flag:
|
||||
|
||||
```bash
|
||||
```console
|
||||
$ docker build --build-arg HTTP_PROXY=http://10.20.30.2:1234 --build-arg FTP_PROXY=http://40.50.60.5:4567 .
|
||||
```
|
||||
|
||||
@ -439,7 +439,7 @@ You may also use the `--build-arg` flag without a value, in which case the value
|
||||
from the local environment will be propagated into the Docker container being
|
||||
built:
|
||||
|
||||
```bash
|
||||
```console
|
||||
$ export HTTP_PROXY=http://10.20.30.2:1234
|
||||
$ docker build --build-arg HTTP_PROXY .
|
||||
```
|
||||
@ -461,11 +461,11 @@ technology. On Linux, the only supported is the `default` option which uses
|
||||
Linux namespaces. On Microsoft Windows, you can specify these values:
|
||||
|
||||
|
||||
| Value | Description |
|
||||
|-----------|---------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `default` | Use the value specified by the Docker daemon's `--exec-opt` . If the `daemon` does not specify an isolation technology, Microsoft Windows uses `process` as its default value. |
|
||||
| `process` | Namespace isolation only. |
|
||||
| `hyperv` | Hyper-V hypervisor partition-based isolation. |
|
||||
| Value | Description |
|
||||
|-----------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `default` | Use the value specified by the Docker daemon's `--exec-opt` . If the `daemon` does not specify an isolation technology, Microsoft Windows uses `process` as its default value. |
|
||||
| `process` | Namespace isolation only. |
|
||||
| `hyperv` | Hyper-V hypervisor partition-based isolation. |
|
||||
|
||||
Specifying the `--isolation` flag without a value is the same as setting `--isolation="default"`.
|
||||
|
||||
@ -485,13 +485,13 @@ image. Commands after the target stage will be skipped.
|
||||
|
||||
```dockerfile
|
||||
FROM debian AS build-env
|
||||
...
|
||||
# ...
|
||||
|
||||
FROM alpine AS production-env
|
||||
...
|
||||
# ...
|
||||
```
|
||||
|
||||
```bash
|
||||
```console
|
||||
$ docker build -t mybuildimage --target build-env .
|
||||
```
|
||||
|
||||
@ -516,7 +516,7 @@ The following example builds an image using the current directory (`.`) as build
|
||||
context, and exports the files to a directory named `out` in the current directory.
|
||||
If the directory does not exist, Docker creates the directory automatically:
|
||||
|
||||
```bash
|
||||
```console
|
||||
$ docker build -o out .
|
||||
```
|
||||
|
||||
@ -525,13 +525,13 @@ thus uses the default (`local`) exporter. The example below shows the equivalent
|
||||
using the long-hand CSV syntax, specifying both `type` and `dest` (destination
|
||||
path):
|
||||
|
||||
```bash
|
||||
```console
|
||||
$ docker build --output type=local,dest=out .
|
||||
```
|
||||
|
||||
Use the `tar` type to export the files as a `.tar` archive:
|
||||
|
||||
```bash
|
||||
```console
|
||||
$ docker build --output type=tar,dest=out.tar .
|
||||
```
|
||||
|
||||
@ -540,8 +540,8 @@ case, `-` is specified as destination, which automatically selects the `tar` typ
|
||||
and writes the output tarball to standard output, which is then redirected to
|
||||
the `out.tar` file:
|
||||
|
||||
```bash
|
||||
docker build -o - . > out.tar
|
||||
```console
|
||||
$ docker build -o - . > out.tar
|
||||
```
|
||||
|
||||
The `--output` option exports all files from the target stage. A common pattern
|
||||
@ -562,7 +562,7 @@ COPY --from=build-stage /go/bin/vndr /
|
||||
When building the Dockerfile with the `-o` option, only the files from the final
|
||||
stage are exported to the `out` directory, in this case, the `vndr` binary:
|
||||
|
||||
```bash
|
||||
```console
|
||||
$ docker build -o out .
|
||||
|
||||
[+] Building 2.3s (7/7) FINISHED
|
||||
@ -585,8 +585,9 @@ vndr
|
||||
> **Note**
|
||||
>
|
||||
> This feature requires the BuildKit backend. You can either
|
||||
> [enable BuildKit](../builder.md#buildkit) or use the [buildx](https://github.com/docker/buildx)
|
||||
> plugin which provides more output type options.
|
||||
> [enable BuildKit](https://docs.docker.com/build/buildkit/#getting-started) or
|
||||
> use the [buildx](https://github.com/docker/buildx) plugin which provides more
|
||||
> output type options.
|
||||
|
||||
### Specifying external cache sources
|
||||
|
||||
@ -610,7 +611,7 @@ options) allow pulling layer data for intermediate stages in multi-stage builds.
|
||||
The following example builds an image with inline-cache metadata and pushes it
|
||||
to a registry, then uses the image as a cache source on another machine:
|
||||
|
||||
```bash
|
||||
```console
|
||||
$ docker build -t myname/myapp --build-arg BUILDKIT_INLINE_CACHE=1 .
|
||||
$ docker push myname/myapp
|
||||
```
|
||||
@ -618,17 +619,18 @@ $ docker push myname/myapp
|
||||
After pushing the image, the image is used as cache source on another machine.
|
||||
BuildKit automatically pulls the image from the registry if needed.
|
||||
|
||||
```bash
|
||||
# on another machine
|
||||
On another machine:
|
||||
|
||||
```console
|
||||
$ docker build --cache-from myname/myapp .
|
||||
```
|
||||
|
||||
> **Note**
|
||||
>
|
||||
> This feature requires the BuildKit backend. You can either
|
||||
> [enable BuildKit](../builder.md#buildkit) or use the [buildx](https://github.com/docker/buildx)
|
||||
> plugin. The previous builder has limited support for reusing cache from
|
||||
> pre-pulled images.
|
||||
> [enable BuildKit](https://docs.docker.com/build/buildkit/#getting-started) or
|
||||
> use the [buildx](https://github.com/docker/buildx) plugin. The previous
|
||||
> builder has limited support for reusing cache from pre-pulled images.
|
||||
|
||||
### Squash an image's layers (--squash) (experimental)
|
||||
|
||||
@ -666,7 +668,7 @@ The `--squash` option has a number of known limitations:
|
||||
base image is still supported.
|
||||
- When using this option you may see significantly more space used due to
|
||||
storing two copies of the image, one for the build cache with all the cache
|
||||
layers in tact, and one for the squashed version.
|
||||
layers intact, and one for the squashed version.
|
||||
- While squashing layers may produce smaller images, it may have a negative
|
||||
impact on performance, as a single layer takes longer to extract, and
|
||||
downloading a single layer cannot be parallelized.
|
||||
@ -725,7 +727,7 @@ To enable experimental features, you need to start the Docker daemon with
|
||||
|
||||
Then make sure the experimental flag is enabled:
|
||||
|
||||
```bash
|
||||
```console
|
||||
$ docker version -f '{{.Server.Experimental}}'
|
||||
true
|
||||
```
|
||||
@ -745,15 +747,15 @@ RUN rm /remove_me
|
||||
|
||||
An image named `test` is built with `--squash` argument.
|
||||
|
||||
```bash
|
||||
```console
|
||||
$ docker build --squash -t test .
|
||||
|
||||
[...]
|
||||
<...>
|
||||
```
|
||||
|
||||
If everything is right, the history looks like this:
|
||||
|
||||
```bash
|
||||
```console
|
||||
$ docker history test
|
||||
|
||||
IMAGE CREATED CREATED BY SIZE COMMENT
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user