mirror of
https://github.com/docker/cli.git
synced 2026-08-25 02:24:25 -05:00
Compare commits
@@ -0,0 +1,83 @@
|
||||
linters:
|
||||
enable:
|
||||
- bodyclose
|
||||
- deadcode
|
||||
- dogsled
|
||||
- gocyclo
|
||||
- goimports
|
||||
- golint
|
||||
- gosec
|
||||
- gosimple
|
||||
- govet
|
||||
- ineffassign
|
||||
- interfacer
|
||||
- lll
|
||||
- megacheck
|
||||
- misspell
|
||||
- nakedret
|
||||
- staticcheck
|
||||
- structcheck
|
||||
- typecheck
|
||||
- unconvert
|
||||
- unparam
|
||||
- unused
|
||||
- varcheck
|
||||
|
||||
disable:
|
||||
- errcheck
|
||||
|
||||
run:
|
||||
timeout: 5m
|
||||
skip-dirs:
|
||||
- cli/command/stack/kubernetes/api/openapi
|
||||
- cli/command/stack/kubernetes/api/client
|
||||
skip-files:
|
||||
- cli/compose/schema/bindata.go
|
||||
- .*generated.*
|
||||
|
||||
linters-settings:
|
||||
gocyclo:
|
||||
min-complexity: 16
|
||||
govet:
|
||||
check-shadowing: false
|
||||
lll:
|
||||
line-length: 200
|
||||
nakedret:
|
||||
command: nakedret
|
||||
pattern: ^(?P<path>.*?\\.go):(?P<line>\\d+)\\s*(?P<message>.*)$
|
||||
|
||||
issues:
|
||||
# The default exclusion rules are a bit too permissive, so copying the relevant ones below
|
||||
exclude-use-default: false
|
||||
|
||||
exclude:
|
||||
- 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"
|
||||
linters:
|
||||
- errcheck
|
||||
- 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"
|
||||
linters:
|
||||
- gosec
|
||||
- text: "G104: Errors unhandled"
|
||||
linters:
|
||||
- gosec
|
||||
- text: "G204: Subprocess launch(ed with (variable|function call)|ing should be audited)"
|
||||
linters:
|
||||
- gosec
|
||||
- text: "(G301|G302): (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"
|
||||
linters:
|
||||
- gosec
|
||||
- text: "(G201|G202): SQL string (formatting|concatenation)"
|
||||
linters:
|
||||
- gosec
|
||||
@@ -1,4 +1,5 @@
|
||||
[](https://circleci.com/gh/docker/cli/tree/master) [](https://jenkins.dockerproject.org/job/docker/job/cli/job/master/)
|
||||
[](https://circleci.com/gh/docker/cli/tree/master)
|
||||
[](https://ci.docker.com/public/job/cli/job/master)
|
||||
|
||||
docker/cli
|
||||
==========
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ clone_folder: c:\gopath\src\github.com\docker\cli
|
||||
|
||||
environment:
|
||||
GOPATH: c:\gopath
|
||||
GOVERSION: 1.12.10
|
||||
GOVERSION: 1.12.16
|
||||
DEPVERSION: v0.4.1
|
||||
|
||||
install:
|
||||
|
||||
+48
-22
@@ -14,11 +14,27 @@ jobs:
|
||||
reusable: true
|
||||
exclusive: false
|
||||
- run:
|
||||
name: "Docker version"
|
||||
command: docker version
|
||||
- run:
|
||||
name: "Docker info"
|
||||
command: docker info
|
||||
- run:
|
||||
name: "Shellcheck - build image"
|
||||
command: |
|
||||
docker build --progress=plain -f dockerfiles/Dockerfile.shellcheck --tag cli-validator:$CIRCLE_BUILD_NUM .
|
||||
- run:
|
||||
name: "Shellcheck"
|
||||
command: |
|
||||
docker run --rm cli-validator:$CIRCLE_BUILD_NUM \
|
||||
make shellcheck
|
||||
- run:
|
||||
name: "Lint - build image"
|
||||
command: |
|
||||
docker build --progress=plain -f dockerfiles/Dockerfile.lint --tag cli-linter:$CIRCLE_BUILD_NUM .
|
||||
- run:
|
||||
name: "Lint"
|
||||
command: |
|
||||
docker build --progress=plain -f dockerfiles/Dockerfile.lint --tag cli-linter:$CIRCLE_BUILD_NUM .
|
||||
docker run --rm cli-linter:$CIRCLE_BUILD_NUM
|
||||
|
||||
cross:
|
||||
@@ -34,9 +50,18 @@ jobs:
|
||||
reusable: true
|
||||
exclusive: false
|
||||
- run:
|
||||
name: "Cross"
|
||||
name: "Docker version"
|
||||
command: docker version
|
||||
- run:
|
||||
name: "Docker info"
|
||||
command: docker info
|
||||
- run:
|
||||
name: "Cross - build image"
|
||||
command: |
|
||||
docker build --progress=plain -f dockerfiles/Dockerfile.cross --tag cli-builder:$CIRCLE_BUILD_NUM .
|
||||
- run:
|
||||
name: "Cross"
|
||||
command: |
|
||||
name=cross-$CIRCLE_BUILD_NUM-$CIRCLE_NODE_INDEX
|
||||
docker run \
|
||||
-e CROSS_GROUP=$CIRCLE_NODE_INDEX \
|
||||
@@ -60,10 +85,19 @@ jobs:
|
||||
reusable: true
|
||||
exclusive: false
|
||||
- run:
|
||||
name: "Unit Test with Coverage"
|
||||
name: "Docker version"
|
||||
command: docker version
|
||||
- run:
|
||||
name: "Docker info"
|
||||
command: docker info
|
||||
- run:
|
||||
name: "Unit Test with Coverage - build image"
|
||||
command: |
|
||||
mkdir -p test-results/unit-tests
|
||||
docker build --progress=plain -f dockerfiles/Dockerfile.dev --tag cli-builder:$CIRCLE_BUILD_NUM .
|
||||
- run:
|
||||
name: "Unit Test with Coverage"
|
||||
command: |
|
||||
docker run \
|
||||
-e GOTESTSUM_JUNITFILE=/tmp/junit.xml \
|
||||
--name \
|
||||
@@ -98,30 +132,23 @@ jobs:
|
||||
reusable: true
|
||||
exclusive: false
|
||||
- run:
|
||||
name: "Validate Vendor, Docs, and Code Generation"
|
||||
name: "Docker version"
|
||||
command: docker version
|
||||
- run:
|
||||
name: "Docker info"
|
||||
command: docker info
|
||||
- run:
|
||||
name: "Validate - build image"
|
||||
command: |
|
||||
rm -f .dockerignore # include .git
|
||||
docker build --progress=plain -f dockerfiles/Dockerfile.dev --tag cli-builder-with-git:$CIRCLE_BUILD_NUM .
|
||||
- run:
|
||||
name: "Validate Vendor, Docs, and Code Generation"
|
||||
command: |
|
||||
docker run --rm cli-builder-with-git:$CIRCLE_BUILD_NUM \
|
||||
make ci-validate
|
||||
no_output_timeout: 15m
|
||||
shellcheck:
|
||||
working_directory: /work
|
||||
docker: [{image: 'docker:18.09-git'}]
|
||||
environment:
|
||||
DOCKER_BUILDKIT: 1
|
||||
steps:
|
||||
- checkout
|
||||
- setup_remote_docker:
|
||||
version: 18.09.3
|
||||
reusable: true
|
||||
exclusive: false
|
||||
- run:
|
||||
name: "Run shellcheck"
|
||||
command: |
|
||||
docker build --progress=plain -f dockerfiles/Dockerfile.shellcheck --tag cli-validator:$CIRCLE_BUILD_NUM .
|
||||
docker run --rm cli-validator:$CIRCLE_BUILD_NUM \
|
||||
make shellcheck
|
||||
|
||||
workflows:
|
||||
version: 2
|
||||
ci:
|
||||
@@ -130,4 +157,3 @@ workflows:
|
||||
- cross
|
||||
- test
|
||||
- validate
|
||||
- shellcheck
|
||||
|
||||
@@ -152,6 +152,7 @@ func TestInitializeFromClient(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, testcase := range testcases {
|
||||
testcase := testcase
|
||||
t.Run(testcase.doc, func(t *testing.T) {
|
||||
apiclient := &fakeClient{
|
||||
pingFunc: testcase.pingFunc,
|
||||
@@ -189,6 +190,7 @@ func TestExperimentalCLI(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, testcase := range testcases {
|
||||
testcase := testcase
|
||||
t.Run(testcase.doc, func(t *testing.T) {
|
||||
dir := fs.NewDir(t, testcase.doc, fs.WithFile("config.json", testcase.configfile))
|
||||
defer dir.Remove()
|
||||
@@ -242,6 +244,7 @@ func TestGetClientWithPassword(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, testcase := range testcases {
|
||||
testcase := testcase
|
||||
t.Run(testcase.doc, func(t *testing.T) {
|
||||
passRetriever := func(_, _ string, _ bool, attempts int) (passphrase string, giveup bool, err error) {
|
||||
// Always return an invalid pass first to test iteration
|
||||
@@ -294,11 +297,12 @@ func TestNewDockerCliAndOperators(t *testing.T) {
|
||||
inbuf := bytes.NewBuffer([]byte("input"))
|
||||
outbuf := bytes.NewBuffer(nil)
|
||||
errbuf := bytes.NewBuffer(nil)
|
||||
cli.Apply(
|
||||
err = cli.Apply(
|
||||
WithInputStream(ioutil.NopCloser(inbuf)),
|
||||
WithOutputStream(outbuf),
|
||||
WithErrorStream(errbuf),
|
||||
)
|
||||
assert.NilError(t, err)
|
||||
// Check input stream
|
||||
inputStream, err := ioutil.ReadAll(cli.In())
|
||||
assert.NilError(t, err)
|
||||
|
||||
@@ -7,10 +7,9 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/docker/cli/internal/test"
|
||||
. "github.com/docker/cli/internal/test/builders" // Import builders to get the builder function as package function
|
||||
"github.com/docker/docker/api/types/swarm"
|
||||
"github.com/pkg/errors"
|
||||
// Import builders to get the builder function as package function
|
||||
. "github.com/docker/cli/internal/test/builders"
|
||||
"gotest.tools/assert"
|
||||
"gotest.tools/golden"
|
||||
)
|
||||
|
||||
@@ -7,11 +7,10 @@ import (
|
||||
|
||||
"github.com/docker/cli/cli/config/configfile"
|
||||
"github.com/docker/cli/internal/test"
|
||||
. "github.com/docker/cli/internal/test/builders" // Import builders to get the builder function as package function
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/swarm"
|
||||
"github.com/pkg/errors"
|
||||
// Import builders to get the builder function as package function
|
||||
. "github.com/docker/cli/internal/test/builders"
|
||||
"gotest.tools/assert"
|
||||
is "gotest.tools/assert/cmp"
|
||||
"gotest.tools/golden"
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http/httputil"
|
||||
|
||||
"github.com/docker/cli/cli"
|
||||
"github.com/docker/cli/cli/command"
|
||||
@@ -103,10 +102,7 @@ func runAttach(dockerCli command.Cli, opts *attachOptions) error {
|
||||
}
|
||||
|
||||
resp, errAttach := client.ContainerAttach(ctx, opts.container, options)
|
||||
if errAttach != nil && errAttach != httputil.ErrPersistEOF {
|
||||
// ContainerAttach returns an ErrPersistEOF (connection closed)
|
||||
// means server met an error and put it in Hijacked connection
|
||||
// keep the error and read detailed error message from hijacked connection later
|
||||
if errAttach != nil {
|
||||
return errAttach
|
||||
}
|
||||
defer resp.Close()
|
||||
@@ -142,10 +138,6 @@ func runAttach(dockerCli command.Cli, opts *attachOptions) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if errAttach != nil {
|
||||
return errAttach
|
||||
}
|
||||
|
||||
return getExitStatus(errC, resultC)
|
||||
}
|
||||
|
||||
|
||||
@@ -151,6 +151,7 @@ func TestNewCreateCommandWithContentTrustErrors(t *testing.T) {
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
tc := tc
|
||||
cli := test.NewFakeCli(&fakeClient{
|
||||
createContainerFunc: func(config *container.Config,
|
||||
hostConfig *container.HostConfig,
|
||||
@@ -209,6 +210,7 @@ func TestNewCreateCommandWithWarnings(t *testing.T) {
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
tc := tc
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
cli := test.NewFakeCli(&fakeClient{
|
||||
createContainerFunc: func(config *container.Config,
|
||||
|
||||
@@ -7,9 +7,8 @@ import (
|
||||
|
||||
"github.com/docker/cli/cli/config/configfile"
|
||||
"github.com/docker/cli/internal/test"
|
||||
. "github.com/docker/cli/internal/test/builders" // Import builders to get the builder function as package function
|
||||
"github.com/docker/docker/api/types"
|
||||
// Import builders to get the builder function as package function
|
||||
. "github.com/docker/cli/internal/test/builders"
|
||||
"gotest.tools/assert"
|
||||
"gotest.tools/golden"
|
||||
)
|
||||
|
||||
@@ -799,7 +799,7 @@ func parsePortOpts(publishOpts []string) ([]string, error) {
|
||||
|
||||
params[opt[0]] = opt[1]
|
||||
}
|
||||
optsList = append(optsList, fmt.Sprintf("%s:%s/%s", params["target"], params["published"], params["protocol"]))
|
||||
optsList = append(optsList, fmt.Sprintf("%s:%s/%s", params["published"], params["target"], params["protocol"]))
|
||||
}
|
||||
return optsList, nil
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ func setupRunFlags() (*pflag.FlagSet, *containerOptions) {
|
||||
}
|
||||
|
||||
func parseMustError(t *testing.T, args string) {
|
||||
_, _, _, err := parseRun(strings.Split(args+" ubuntu bash", " "))
|
||||
_, _, _, err := parseRun(strings.Split(args+" ubuntu bash", " ")) //nolint:dogsled
|
||||
assert.ErrorContains(t, err, "", args)
|
||||
}
|
||||
|
||||
@@ -539,7 +539,7 @@ func TestParseModes(t *testing.T) {
|
||||
}
|
||||
|
||||
// uts ko
|
||||
_, _, _, err = parseRun([]string{"--uts=container:", "img", "cmd"})
|
||||
_, _, _, err = parseRun([]string{"--uts=container:", "img", "cmd"}) //nolint:dogsled
|
||||
assert.ErrorContains(t, err, "--uts: invalid UTS mode")
|
||||
|
||||
// uts ok
|
||||
@@ -600,7 +600,7 @@ func TestParseRestartPolicy(t *testing.T) {
|
||||
|
||||
func TestParseRestartPolicyAutoRemove(t *testing.T) {
|
||||
expected := "Conflicting options: --restart and --rm"
|
||||
_, _, _, err := parseRun([]string{"--rm", "--restart=always", "img", "cmd"})
|
||||
_, _, _, err := parseRun([]string{"--rm", "--restart=always", "img", "cmd"}) //nolint:dogsled
|
||||
if err == nil || err.Error() != expected {
|
||||
t.Fatalf("Expected error %v, but got none", expected)
|
||||
}
|
||||
@@ -854,3 +854,9 @@ func TestParseSystemPaths(t *testing.T) {
|
||||
assert.DeepEqual(t, readonlyPaths, tc.readonly)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParsePortOpts(t *testing.T) {
|
||||
parsed, err := parsePortOpts([]string{"published=1500,target=200", "target=80,published=90"})
|
||||
assert.NilError(t, err)
|
||||
assert.DeepEqual(t, []string{"1500:200/tcp", "90:80/tcp"}, parsed)
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ func NewRmCommand(dockerCli command.Cli) *cobra.Command {
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
flags.BoolVarP(&opts.rmVolumes, "volumes", "v", false, "Remove the volumes associated with the container")
|
||||
flags.BoolVarP(&opts.rmVolumes, "volumes", "v", false, "Remove anonymous volumes associated with the container")
|
||||
flags.BoolVarP(&opts.rmLink, "link", "l", false, "Remove the specified link")
|
||||
flags.BoolVarP(&opts.force, "force", "f", false, "Force the removal of a running container (uses SIGKILL)")
|
||||
return cmd
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http/httputil"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
@@ -115,11 +114,6 @@ func runContainer(dockerCli command.Cli, opts *runOptions, copts *containerOptio
|
||||
config.StdinOnce = false
|
||||
}
|
||||
|
||||
// Disable sigProxy when in TTY mode
|
||||
if config.Tty {
|
||||
opts.sigProxy = false
|
||||
}
|
||||
|
||||
// Telling the Windows daemon the initial size of the tty during start makes
|
||||
// a far better user experience rather than relying on subsequent resizes
|
||||
// to cause things to catch up.
|
||||
@@ -253,10 +247,7 @@ func attachContainer(
|
||||
}
|
||||
|
||||
resp, errAttach := dockerCli.Client().ContainerAttach(ctx, containerID, options)
|
||||
if errAttach != nil && errAttach != httputil.ErrPersistEOF {
|
||||
// ContainerAttach returns an ErrPersistEOF (connection closed)
|
||||
// means server met an error and put it in Hijacked connection
|
||||
// keep the error and read detailed error message from hijacked connection later
|
||||
if errAttach != nil {
|
||||
return nil, errAttach
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http/httputil"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/cli/cli"
|
||||
@@ -98,10 +97,7 @@ func runStart(dockerCli command.Cli, opts *startOptions) error {
|
||||
}
|
||||
|
||||
resp, errAttach := dockerCli.Client().ContainerAttach(ctx, c.ID, options)
|
||||
if errAttach != nil && errAttach != httputil.ErrPersistEOF {
|
||||
// ContainerAttach return an ErrPersistEOF (connection closed)
|
||||
// means server met an error and already put it in Hijacked connection,
|
||||
// we would keep the error and read the detailed error message from hijacked connection
|
||||
if errAttach != nil {
|
||||
return errAttach
|
||||
}
|
||||
defer resp.Close()
|
||||
@@ -154,7 +150,7 @@ func runStart(dockerCli command.Cli, opts *startOptions) error {
|
||||
}
|
||||
}
|
||||
if attachErr := <-cErr; attachErr != nil {
|
||||
if _, ok := err.(term.EscapeError); ok {
|
||||
if _, ok := attachErr.(term.EscapeError); ok {
|
||||
// The user entered the detach escape sequence.
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -208,7 +208,9 @@ func runStats(dockerCli command.Cli, opts *statsOptions) error {
|
||||
}
|
||||
|
||||
var err error
|
||||
for range time.Tick(500 * time.Millisecond) {
|
||||
ticker := time.NewTicker(500 * time.Millisecond)
|
||||
defer ticker.Stop()
|
||||
for range ticker.C {
|
||||
cleanScreen()
|
||||
ccstats := []StatsEntry{}
|
||||
cStats.mu.Lock()
|
||||
|
||||
@@ -263,6 +263,7 @@ func TestCreateFromContext(t *testing.T) {
|
||||
cli.SetCurrentContext("dummy")
|
||||
|
||||
for _, c := range cases {
|
||||
c := c
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
cli.ResetOutputBuffers()
|
||||
err := RunCreate(cli, &CreateOptions{
|
||||
@@ -339,6 +340,7 @@ func TestCreateFromCurrent(t *testing.T) {
|
||||
cli.SetCurrentContext("original")
|
||||
|
||||
for _, c := range cases {
|
||||
c := c
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
cli.ResetOutputBuffers()
|
||||
err := RunCreate(cli, &CreateOptions{
|
||||
|
||||
@@ -304,7 +304,8 @@ func TestContainerContextWriteWithNoContainers(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, context := range contexts {
|
||||
ContainerWrite(context.context, containers)
|
||||
err := ContainerWrite(context.context, containers)
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.Equal(context.expected, out.String()))
|
||||
// Clean buffer
|
||||
out.Reset()
|
||||
|
||||
@@ -348,7 +348,8 @@ func TestImageContextWriteWithNoImage(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, context := range contexts {
|
||||
ImageWrite(context.context, images)
|
||||
err := ImageWrite(context.context, images)
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.Equal(context.expected, out.String()))
|
||||
// Clean buffer
|
||||
out.Reset()
|
||||
|
||||
@@ -1,16 +1,14 @@
|
||||
package idresolver
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
. "github.com/docker/cli/internal/test/builders" // Import builders to get the builder function as package function
|
||||
"github.com/docker/docker/api/types/swarm"
|
||||
"github.com/pkg/errors"
|
||||
"gotest.tools/assert"
|
||||
is "gotest.tools/assert/cmp"
|
||||
// Import builders to get the builder function as package function
|
||||
"context"
|
||||
|
||||
. "github.com/docker/cli/internal/test/builders"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func TestResolveError(t *testing.T) {
|
||||
|
||||
@@ -120,28 +120,40 @@ func NewBuildCommand(dockerCli command.Cli) *cobra.Command {
|
||||
flags.VarP(&options.tags, "tag", "t", "Name and optionally a tag in the 'name:tag' format")
|
||||
flags.Var(&options.buildArgs, "build-arg", "Set build-time variables")
|
||||
flags.Var(options.ulimits, "ulimit", "Ulimit options")
|
||||
flags.SetAnnotation("ulimit", "no-buildkit", nil)
|
||||
flags.StringVarP(&options.dockerfileName, "file", "f", "", "Name of the Dockerfile (Default is 'PATH/Dockerfile')")
|
||||
flags.VarP(&options.memory, "memory", "m", "Memory limit")
|
||||
flags.SetAnnotation("memory", "no-buildkit", nil)
|
||||
flags.Var(&options.memorySwap, "memory-swap", "Swap limit equal to memory plus swap: '-1' to enable unlimited swap")
|
||||
flags.SetAnnotation("memory-swap", "no-buildkit", nil)
|
||||
flags.Var(&options.shmSize, "shm-size", "Size of /dev/shm")
|
||||
flags.SetAnnotation("shm-size", "no-buildkit", nil)
|
||||
flags.Int64VarP(&options.cpuShares, "cpu-shares", "c", 0, "CPU shares (relative weight)")
|
||||
flags.SetAnnotation("cpu-shares", "no-buildkit", nil)
|
||||
flags.Int64Var(&options.cpuPeriod, "cpu-period", 0, "Limit the CPU CFS (Completely Fair Scheduler) period")
|
||||
flags.SetAnnotation("cpu-period", "no-buildkit", nil)
|
||||
flags.Int64Var(&options.cpuQuota, "cpu-quota", 0, "Limit the CPU CFS (Completely Fair Scheduler) quota")
|
||||
flags.SetAnnotation("cpu-quota", "no-buildkit", nil)
|
||||
flags.StringVar(&options.cpuSetCpus, "cpuset-cpus", "", "CPUs in which to allow execution (0-3, 0,1)")
|
||||
flags.SetAnnotation("cpuset-cpus", "no-buildkit", nil)
|
||||
flags.StringVar(&options.cpuSetMems, "cpuset-mems", "", "MEMs in which to allow execution (0-3, 0,1)")
|
||||
flags.SetAnnotation("cpuset-mems", "no-buildkit", nil)
|
||||
flags.StringVar(&options.cgroupParent, "cgroup-parent", "", "Optional parent cgroup for the container")
|
||||
flags.SetAnnotation("cgroup-parent", "no-buildkit", nil)
|
||||
flags.StringVar(&options.isolation, "isolation", "", "Container isolation technology")
|
||||
flags.Var(&options.labels, "label", "Set metadata for an image")
|
||||
flags.BoolVar(&options.noCache, "no-cache", false, "Do not use cache when building the image")
|
||||
flags.BoolVar(&options.rm, "rm", true, "Remove intermediate containers after a successful build")
|
||||
flags.SetAnnotation("rm", "no-buildkit", nil)
|
||||
flags.BoolVar(&options.forceRm, "force-rm", false, "Always remove intermediate containers")
|
||||
flags.SetAnnotation("force-rm", "no-buildkit", nil)
|
||||
flags.BoolVarP(&options.quiet, "quiet", "q", false, "Suppress the build output and print image ID on success")
|
||||
flags.BoolVar(&options.pull, "pull", false, "Always attempt to pull a newer version of the image")
|
||||
flags.StringSliceVar(&options.cacheFrom, "cache-from", []string{}, "Images to consider as cache sources")
|
||||
flags.BoolVar(&options.compress, "compress", false, "Compress the build context using gzip")
|
||||
flags.SetAnnotation("compress", "no-buildkit", nil)
|
||||
|
||||
flags.StringSliceVar(&options.securityOpt, "security-opt", []string{}, "Security options")
|
||||
flags.SetAnnotation("security-opt", "no-buildkit", nil)
|
||||
flags.StringVar(&options.networkMode, "network", "default", "Set the networking mode for the RUN instructions during build")
|
||||
flags.SetAnnotation("network", "version", []string{"1.25"})
|
||||
flags.Var(&options.extraHosts, "add-host", "Add a custom host-to-IP mapping (host:ip)")
|
||||
@@ -474,7 +486,7 @@ func runBuild(dockerCli command.Cli, options buildOptions) error {
|
||||
// should be just the image ID and we'll print that to stdout.
|
||||
if options.quiet {
|
||||
imageID = fmt.Sprintf("%s", buildBuff)
|
||||
fmt.Fprintf(dockerCli.Out(), imageID)
|
||||
_, _ = fmt.Fprint(dockerCli.Out(), imageID)
|
||||
}
|
||||
|
||||
if options.imageIDFile != "" {
|
||||
|
||||
@@ -232,6 +232,7 @@ func GetContextFromURL(out io.Writer, remoteURL, dockerfileName string) (io.Read
|
||||
// getWithStatusError does an http.Get() and returns an error if the
|
||||
// status code is 4xx or 5xx.
|
||||
func getWithStatusError(url string) (resp *http.Response, err error) {
|
||||
// #nosec G107
|
||||
if resp, err = http.Get(url); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -242,9 +243,9 @@ func getWithStatusError(url string) (resp *http.Response, err error) {
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
resp.Body.Close()
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, msg+": error reading body")
|
||||
return nil, errors.Wrapf(err, "%s: error reading body", msg)
|
||||
}
|
||||
return nil, errors.Errorf(msg+": %s", bytes.TrimSpace(body))
|
||||
return nil, errors.Errorf("%s: %s", msg, bytes.TrimSpace(body))
|
||||
}
|
||||
|
||||
// GetContextFromLocalDir uses the given local directory as context for a
|
||||
@@ -344,7 +345,6 @@ func getDockerfileRelPath(absContextDir, givenDockerfile string) (string, error)
|
||||
absDockerfile, err = filepath.EvalSymlinks(absDockerfile)
|
||||
if err != nil {
|
||||
return "", errors.Errorf("unable to evaluate symlinks in Dockerfile path: %v", err)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -24,12 +24,12 @@ var prepareEmpty = func(t *testing.T) (string, func()) {
|
||||
}
|
||||
|
||||
var prepareNoFiles = func(t *testing.T) (string, func()) {
|
||||
return createTestTempDir(t, "", "builder-context-test")
|
||||
return createTestTempDir(t, "builder-context-test")
|
||||
}
|
||||
|
||||
var prepareOneFile = func(t *testing.T) (string, func()) {
|
||||
contextDir, cleanup := createTestTempDir(t, "", "builder-context-test")
|
||||
createTestTempFile(t, contextDir, DefaultDockerfileName, dockerfileContents, 0777)
|
||||
contextDir, cleanup := createTestTempDir(t, "builder-context-test")
|
||||
createTestTempFile(t, contextDir, DefaultDockerfileName, dockerfileContents)
|
||||
return contextDir, cleanup
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ func testValidateContextDirectory(t *testing.T, prepare func(t *testing.T) (stri
|
||||
}
|
||||
|
||||
func TestGetContextFromLocalDirNoDockerfile(t *testing.T) {
|
||||
contextDir, cleanup := createTestTempDir(t, "", "builder-context-test")
|
||||
contextDir, cleanup := createTestTempDir(t, "builder-context-test")
|
||||
defer cleanup()
|
||||
|
||||
_, _, err := GetContextFromLocalDir(contextDir, "")
|
||||
@@ -50,7 +50,7 @@ func TestGetContextFromLocalDirNoDockerfile(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetContextFromLocalDirNotExistingDir(t *testing.T) {
|
||||
contextDir, cleanup := createTestTempDir(t, "", "builder-context-test")
|
||||
contextDir, cleanup := createTestTempDir(t, "builder-context-test")
|
||||
defer cleanup()
|
||||
|
||||
fakePath := filepath.Join(contextDir, "fake")
|
||||
@@ -60,7 +60,7 @@ func TestGetContextFromLocalDirNotExistingDir(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetContextFromLocalDirNotExistingDockerfile(t *testing.T) {
|
||||
contextDir, cleanup := createTestTempDir(t, "", "builder-context-test")
|
||||
contextDir, cleanup := createTestTempDir(t, "builder-context-test")
|
||||
defer cleanup()
|
||||
|
||||
fakePath := filepath.Join(contextDir, "fake")
|
||||
@@ -70,10 +70,10 @@ func TestGetContextFromLocalDirNotExistingDockerfile(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetContextFromLocalDirWithNoDirectory(t *testing.T) {
|
||||
contextDir, dirCleanup := createTestTempDir(t, "", "builder-context-test")
|
||||
contextDir, dirCleanup := createTestTempDir(t, "builder-context-test")
|
||||
defer dirCleanup()
|
||||
|
||||
createTestTempFile(t, contextDir, DefaultDockerfileName, dockerfileContents, 0777)
|
||||
createTestTempFile(t, contextDir, DefaultDockerfileName, dockerfileContents)
|
||||
|
||||
chdirCleanup := chdir(t, contextDir)
|
||||
defer chdirCleanup()
|
||||
@@ -86,10 +86,10 @@ func TestGetContextFromLocalDirWithNoDirectory(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetContextFromLocalDirWithDockerfile(t *testing.T) {
|
||||
contextDir, cleanup := createTestTempDir(t, "", "builder-context-test")
|
||||
contextDir, cleanup := createTestTempDir(t, "builder-context-test")
|
||||
defer cleanup()
|
||||
|
||||
createTestTempFile(t, contextDir, DefaultDockerfileName, dockerfileContents, 0777)
|
||||
createTestTempFile(t, contextDir, DefaultDockerfileName, dockerfileContents)
|
||||
|
||||
absContextDir, relDockerfile, err := GetContextFromLocalDir(contextDir, "")
|
||||
assert.NilError(t, err)
|
||||
@@ -99,11 +99,11 @@ func TestGetContextFromLocalDirWithDockerfile(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetContextFromLocalDirLocalFile(t *testing.T) {
|
||||
contextDir, cleanup := createTestTempDir(t, "", "builder-context-test")
|
||||
contextDir, cleanup := createTestTempDir(t, "builder-context-test")
|
||||
defer cleanup()
|
||||
|
||||
createTestTempFile(t, contextDir, DefaultDockerfileName, dockerfileContents, 0777)
|
||||
testFilename := createTestTempFile(t, contextDir, "tmpTest", "test", 0777)
|
||||
createTestTempFile(t, contextDir, DefaultDockerfileName, dockerfileContents)
|
||||
testFilename := createTestTempFile(t, contextDir, "tmpTest", "test")
|
||||
|
||||
absContextDir, relDockerfile, err := GetContextFromLocalDir(testFilename, "")
|
||||
|
||||
@@ -121,13 +121,13 @@ func TestGetContextFromLocalDirLocalFile(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetContextFromLocalDirWithCustomDockerfile(t *testing.T) {
|
||||
contextDir, cleanup := createTestTempDir(t, "", "builder-context-test")
|
||||
contextDir, cleanup := createTestTempDir(t, "builder-context-test")
|
||||
defer cleanup()
|
||||
|
||||
chdirCleanup := chdir(t, contextDir)
|
||||
defer chdirCleanup()
|
||||
|
||||
createTestTempFile(t, contextDir, DefaultDockerfileName, dockerfileContents, 0777)
|
||||
createTestTempFile(t, contextDir, DefaultDockerfileName, dockerfileContents)
|
||||
|
||||
absContextDir, relDockerfile, err := GetContextFromLocalDir(contextDir, DefaultDockerfileName)
|
||||
assert.NilError(t, err)
|
||||
@@ -173,10 +173,10 @@ func TestGetContextFromReaderString(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetContextFromReaderTar(t *testing.T) {
|
||||
contextDir, cleanup := createTestTempDir(t, "", "builder-context-test")
|
||||
contextDir, cleanup := createTestTempDir(t, "builder-context-test")
|
||||
defer cleanup()
|
||||
|
||||
createTestTempFile(t, contextDir, DefaultDockerfileName, dockerfileContents, 0777)
|
||||
createTestTempFile(t, contextDir, DefaultDockerfileName, dockerfileContents)
|
||||
|
||||
tarStream, err := archive.Tar(contextDir, archive.Uncompressed)
|
||||
assert.NilError(t, err)
|
||||
@@ -241,17 +241,18 @@ func TestValidateContextDirectoryWithOneFileExcludes(t *testing.T) {
|
||||
// createTestTempDir creates a temporary directory for testing.
|
||||
// It returns the created path and a cleanup function which is meant to be used as deferred call.
|
||||
// When an error occurs, it terminates the test.
|
||||
func createTestTempDir(t *testing.T, dir, prefix string) (string, func()) {
|
||||
path, err := ioutil.TempDir(dir, prefix)
|
||||
//nolint: unparam
|
||||
func createTestTempDir(t *testing.T, prefix string) (string, func()) {
|
||||
path, err := ioutil.TempDir("", prefix)
|
||||
assert.NilError(t, err)
|
||||
return path, func() { assert.NilError(t, os.RemoveAll(path)) }
|
||||
}
|
||||
|
||||
// createTestTempFile creates a temporary file within dir with specific contents and permissions.
|
||||
// When an error occurs, it terminates the test
|
||||
func createTestTempFile(t *testing.T, dir, filename, contents string, perm os.FileMode) string {
|
||||
func createTestTempFile(t *testing.T, dir, filename, contents string) string {
|
||||
filePath := filepath.Join(dir, filename)
|
||||
err := ioutil.WriteFile(filePath, []byte(contents), perm)
|
||||
err := ioutil.WriteFile(filePath, []byte(contents), 0777)
|
||||
assert.NilError(t, err)
|
||||
return filePath
|
||||
}
|
||||
|
||||
@@ -476,16 +476,13 @@ func parseSecret(value string) (*secretsprovider.FileSource, error) {
|
||||
func parseSSHSpecs(sl []string) (session.Attachable, error) {
|
||||
configs := make([]sshprovider.AgentConfig, 0, len(sl))
|
||||
for _, v := range sl {
|
||||
c, err := parseSSH(v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c := parseSSH(v)
|
||||
configs = append(configs, *c)
|
||||
}
|
||||
return sshprovider.NewSSHAgentProvider(configs)
|
||||
}
|
||||
|
||||
func parseSSH(value string) (*sshprovider.AgentConfig, error) {
|
||||
func parseSSH(value string) *sshprovider.AgentConfig {
|
||||
parts := strings.SplitN(value, "=", 2)
|
||||
cfg := sshprovider.AgentConfig{
|
||||
ID: parts[0],
|
||||
@@ -493,5 +490,5 @@ func parseSSH(value string) (*sshprovider.AgentConfig, error) {
|
||||
if len(parts) > 1 {
|
||||
cfg.Paths = strings.Split(parts[1], ",")
|
||||
}
|
||||
return &cfg, nil
|
||||
return &cfg
|
||||
}
|
||||
|
||||
@@ -35,16 +35,13 @@ func isSessionSupported(dockerCli command.Cli, forStream bool) bool {
|
||||
}
|
||||
|
||||
func trySession(dockerCli command.Cli, contextDir string, forStream bool) (*session.Session, error) {
|
||||
var s *session.Session
|
||||
if isSessionSupported(dockerCli, forStream) {
|
||||
sharedKey, err := getBuildSharedKey(contextDir)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get build shared key")
|
||||
}
|
||||
s, err = session.NewSession(context.Background(), filepath.Base(contextDir), sharedKey)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to create session")
|
||||
}
|
||||
if !isSessionSupported(dockerCli, forStream) {
|
||||
return nil, nil
|
||||
}
|
||||
sharedKey := getBuildSharedKey(contextDir)
|
||||
s, err := session.NewSession(context.Background(), filepath.Base(contextDir), sharedKey)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to create session")
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
@@ -130,10 +127,10 @@ func (bw *bufferedWriter) String() string {
|
||||
return fmt.Sprintf("%s", bw.Writer)
|
||||
}
|
||||
|
||||
func getBuildSharedKey(dir string) (string, error) {
|
||||
func getBuildSharedKey(dir string) string {
|
||||
// build session is hash of build dir with node based randomness
|
||||
s := sha256.Sum256([]byte(fmt.Sprintf("%s:%s", tryNodeIdentifier(), dir)))
|
||||
return hex.EncodeToString(s[:]), nil
|
||||
return hex.EncodeToString(s[:])
|
||||
}
|
||||
|
||||
func tryNodeIdentifier() string {
|
||||
|
||||
@@ -50,7 +50,6 @@ func TestNewPullCommandSuccess(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
args []string
|
||||
flags map[string]string
|
||||
expectedTag string
|
||||
}{
|
||||
{
|
||||
@@ -64,11 +63,8 @@ func TestNewPullCommandSuccess(t *testing.T) {
|
||||
expectedTag: "image:latest",
|
||||
},
|
||||
{
|
||||
name: "simple-quiet",
|
||||
args: []string{"image"},
|
||||
flags: map[string]string{
|
||||
"quiet": "true",
|
||||
},
|
||||
name: "simple-quiet",
|
||||
args: []string{"--quiet", "image"},
|
||||
expectedTag: "image:latest",
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,2 +1 @@
|
||||
Using default tag: latest
|
||||
docker.io/library/image:latest
|
||||
|
||||
@@ -5,11 +5,10 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/docker/cli/internal/test"
|
||||
. "github.com/docker/cli/internal/test/builders" // Import builders to get the builder function as package functions
|
||||
"github.com/docker/docker/api/types/swarm"
|
||||
"github.com/pkg/errors"
|
||||
"gotest.tools/assert"
|
||||
// Import builders to get the builder function as package function
|
||||
. "github.com/docker/cli/internal/test/builders"
|
||||
)
|
||||
|
||||
func TestNodeDemoteErrors(t *testing.T) {
|
||||
|
||||
@@ -6,11 +6,10 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/docker/cli/internal/test"
|
||||
. "github.com/docker/cli/internal/test/builders" // Import builders to get the builder function as package functions
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/swarm"
|
||||
"github.com/pkg/errors"
|
||||
// Import builders to get the builder function as package function
|
||||
. "github.com/docker/cli/internal/test/builders"
|
||||
"gotest.tools/assert"
|
||||
"gotest.tools/golden"
|
||||
)
|
||||
|
||||
@@ -6,14 +6,13 @@ import (
|
||||
|
||||
"github.com/docker/cli/cli/config/configfile"
|
||||
"github.com/docker/cli/internal/test"
|
||||
. "github.com/docker/cli/internal/test/builders" // Import builders to get the builder function as package function
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/swarm"
|
||||
"github.com/pkg/errors"
|
||||
"gotest.tools/assert"
|
||||
is "gotest.tools/assert/cmp"
|
||||
"gotest.tools/golden"
|
||||
// Import builders to get the builder function as package function
|
||||
. "github.com/docker/cli/internal/test/builders"
|
||||
)
|
||||
|
||||
func TestNodeListErrorOnAPIFailure(t *testing.T) {
|
||||
|
||||
@@ -5,11 +5,10 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/docker/cli/internal/test"
|
||||
. "github.com/docker/cli/internal/test/builders" // Import builders to get the builder function as package function
|
||||
"github.com/docker/docker/api/types/swarm"
|
||||
"github.com/pkg/errors"
|
||||
"gotest.tools/assert"
|
||||
// Import builders to get the builder function as package function
|
||||
. "github.com/docker/cli/internal/test/builders"
|
||||
)
|
||||
|
||||
func TestNodePromoteErrors(t *testing.T) {
|
||||
|
||||
@@ -8,11 +8,10 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/docker/cli/internal/test"
|
||||
. "github.com/docker/cli/internal/test/builders" // Import builders to get the builder function as package function
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/swarm"
|
||||
"github.com/pkg/errors"
|
||||
// Import builders to get the builder function as package function
|
||||
. "github.com/docker/cli/internal/test/builders"
|
||||
"gotest.tools/assert"
|
||||
"gotest.tools/golden"
|
||||
)
|
||||
|
||||
@@ -5,11 +5,10 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/docker/cli/internal/test"
|
||||
. "github.com/docker/cli/internal/test/builders" // Import builders to get the builder function as package function
|
||||
"github.com/docker/docker/api/types/swarm"
|
||||
"github.com/pkg/errors"
|
||||
"gotest.tools/assert"
|
||||
// Import builders to get the builder function as package function
|
||||
. "github.com/docker/cli/internal/test/builders"
|
||||
)
|
||||
|
||||
func TestNodeUpdateErrors(t *testing.T) {
|
||||
|
||||
@@ -58,7 +58,7 @@ func TestListErrors(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestList(t *testing.T) {
|
||||
singlePluginListFunc := func(filter filters.Args) (types.PluginsListResponse, error) {
|
||||
singlePluginListFunc := func(_ filters.Args) (types.PluginsListResponse, error) {
|
||||
return types.PluginsListResponse{
|
||||
{
|
||||
ID: "id-foo",
|
||||
@@ -113,7 +113,7 @@ func TestList(t *testing.T) {
|
||||
"format": "{{ .ID }}",
|
||||
},
|
||||
golden: "plugin-list-with-no-trunc-option.golden",
|
||||
listFunc: func(filter filters.Args) (types.PluginsListResponse, error) {
|
||||
listFunc: func(_ filters.Args) (types.PluginsListResponse, error) {
|
||||
return types.PluginsListResponse{
|
||||
{
|
||||
ID: "xyg4z2hiSLO5yTnBJfg4OYia9gKA6Qjd",
|
||||
@@ -142,7 +142,7 @@ func TestList(t *testing.T) {
|
||||
"format": "{{ .Name }}",
|
||||
},
|
||||
golden: "plugin-list-sort.golden",
|
||||
listFunc: func(filter filters.Args) (types.PluginsListResponse, error) {
|
||||
listFunc: func(_ filters.Args) (types.PluginsListResponse, error) {
|
||||
return types.PluginsListResponse{
|
||||
{
|
||||
ID: "id-1",
|
||||
|
||||
@@ -69,7 +69,7 @@ func TestLoginWithCredStoreCreds(t *testing.T) {
|
||||
}
|
||||
ctx := context.Background()
|
||||
for _, tc := range testCases {
|
||||
cli := (*test.FakeCli)(test.NewFakeCli(&fakeClient{}))
|
||||
cli := test.NewFakeCli(&fakeClient{})
|
||||
errBuf := new(bytes.Buffer)
|
||||
cli.SetErr(errBuf)
|
||||
loginWithCredStoreCreds(ctx, cli, &tc.inputAuthConfig)
|
||||
@@ -166,7 +166,7 @@ func TestRunLogin(t *testing.T) {
|
||||
|
||||
if tc.inputStoredCred != nil {
|
||||
cred := *tc.inputStoredCred
|
||||
configfile.GetCredentialsStore(cred.ServerAddress).Store(cred)
|
||||
assert.NilError(t, configfile.GetCredentialsStore(cred.ServerAddress).Store(cred))
|
||||
}
|
||||
loginErr := runLogin(cli, tc.inputLoginOption)
|
||||
if tc.expectedErr != "" {
|
||||
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
defaultSecretTableFormat = "table {{.ID}}\t{{.Name}}\t{{.Driver}}\t{{.CreatedAt}}\t{{.UpdatedAt}}"
|
||||
defaultSecretTableFormat = "table {{.ID}}\t{{.Name}}\t{{.Driver}}\t{{.CreatedAt}}\t{{.UpdatedAt}}" // #nosec G101
|
||||
secretIDHeader = "ID"
|
||||
secretCreatedHeader = "CREATED"
|
||||
secretUpdatedHeader = "UPDATED"
|
||||
|
||||
@@ -7,10 +7,9 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/docker/cli/internal/test"
|
||||
. "github.com/docker/cli/internal/test/builders" // Import builders to get the builder function as package function
|
||||
"github.com/docker/docker/api/types/swarm"
|
||||
"github.com/pkg/errors"
|
||||
// Import builders to get the builder function as package function
|
||||
. "github.com/docker/cli/internal/test/builders"
|
||||
"gotest.tools/assert"
|
||||
"gotest.tools/golden"
|
||||
)
|
||||
|
||||
@@ -7,11 +7,10 @@ import (
|
||||
|
||||
"github.com/docker/cli/cli/config/configfile"
|
||||
"github.com/docker/cli/internal/test"
|
||||
. "github.com/docker/cli/internal/test/builders" // Import builders to get the builder function as package function
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/swarm"
|
||||
"github.com/pkg/errors"
|
||||
// Import builders to get the builder function as package function
|
||||
. "github.com/docker/cli/internal/test/builders"
|
||||
"gotest.tools/assert"
|
||||
is "gotest.tools/assert/cmp"
|
||||
"gotest.tools/golden"
|
||||
|
||||
@@ -3,11 +3,10 @@ package service
|
||||
import (
|
||||
"context"
|
||||
|
||||
. "github.com/docker/cli/internal/test/builders" // Import builders to get the builder function as package function
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/swarm"
|
||||
"github.com/docker/docker/client"
|
||||
// Import builders to get the builder function as package function
|
||||
. "github.com/docker/cli/internal/test/builders"
|
||||
)
|
||||
|
||||
type fakeClient struct {
|
||||
|
||||
@@ -202,9 +202,9 @@ func TestToServiceNetwork(t *testing.T) {
|
||||
}
|
||||
|
||||
nwo := opts.NetworkOpt{}
|
||||
nwo.Set("zzz-network")
|
||||
nwo.Set("mmm-network")
|
||||
nwo.Set("aaa-network")
|
||||
assert.NilError(t, nwo.Set("zzz-network"))
|
||||
assert.NilError(t, nwo.Set("mmm-network"))
|
||||
assert.NilError(t, nwo.Set("aaa-network"))
|
||||
|
||||
o := newServiceOptions()
|
||||
o.mode = "replicated"
|
||||
|
||||
@@ -28,7 +28,7 @@ func TestUpdateServiceArgs(t *testing.T) {
|
||||
cspec := spec.TaskTemplate.ContainerSpec
|
||||
cspec.Args = []string{"old", "args"}
|
||||
|
||||
updateService(nil, nil, flags, spec)
|
||||
updateService(context.TODO(), nil, flags, spec)
|
||||
assert.Check(t, is.DeepEqual([]string{"the", "new args"}, cspec.Args))
|
||||
}
|
||||
|
||||
@@ -532,18 +532,18 @@ func TestUpdateReadOnly(t *testing.T) {
|
||||
// Update with --read-only=true, changed to true
|
||||
flags := newUpdateCommand(nil).Flags()
|
||||
flags.Set("read-only", "true")
|
||||
updateService(nil, nil, flags, spec)
|
||||
updateService(context.TODO(), nil, flags, spec)
|
||||
assert.Check(t, cspec.ReadOnly)
|
||||
|
||||
// Update without --read-only, no change
|
||||
flags = newUpdateCommand(nil).Flags()
|
||||
updateService(nil, nil, flags, spec)
|
||||
updateService(context.TODO(), nil, flags, spec)
|
||||
assert.Check(t, cspec.ReadOnly)
|
||||
|
||||
// Update with --read-only=false, changed to false
|
||||
flags = newUpdateCommand(nil).Flags()
|
||||
flags.Set("read-only", "false")
|
||||
updateService(nil, nil, flags, spec)
|
||||
updateService(context.TODO(), nil, flags, spec)
|
||||
assert.Check(t, !cspec.ReadOnly)
|
||||
}
|
||||
|
||||
@@ -558,18 +558,18 @@ func TestUpdateInit(t *testing.T) {
|
||||
// Update with --init=true
|
||||
flags := newUpdateCommand(nil).Flags()
|
||||
flags.Set("init", "true")
|
||||
updateService(nil, nil, flags, spec)
|
||||
updateService(context.TODO(), nil, flags, spec)
|
||||
assert.Check(t, is.Equal(true, *cspec.Init))
|
||||
|
||||
// Update without --init, no change
|
||||
flags = newUpdateCommand(nil).Flags()
|
||||
updateService(nil, nil, flags, spec)
|
||||
updateService(context.TODO(), nil, flags, spec)
|
||||
assert.Check(t, is.Equal(true, *cspec.Init))
|
||||
|
||||
// Update with --init=false
|
||||
flags = newUpdateCommand(nil).Flags()
|
||||
flags.Set("init", "false")
|
||||
updateService(nil, nil, flags, spec)
|
||||
updateService(context.TODO(), nil, flags, spec)
|
||||
assert.Check(t, is.Equal(false, *cspec.Init))
|
||||
}
|
||||
|
||||
@@ -584,18 +584,18 @@ func TestUpdateStopSignal(t *testing.T) {
|
||||
// Update with --stop-signal=SIGUSR1
|
||||
flags := newUpdateCommand(nil).Flags()
|
||||
flags.Set("stop-signal", "SIGUSR1")
|
||||
updateService(nil, nil, flags, spec)
|
||||
updateService(context.TODO(), nil, flags, spec)
|
||||
assert.Check(t, is.Equal("SIGUSR1", cspec.StopSignal))
|
||||
|
||||
// Update without --stop-signal, no change
|
||||
flags = newUpdateCommand(nil).Flags()
|
||||
updateService(nil, nil, flags, spec)
|
||||
updateService(context.TODO(), nil, flags, spec)
|
||||
assert.Check(t, is.Equal("SIGUSR1", cspec.StopSignal))
|
||||
|
||||
// Update with --stop-signal=SIGWINCH
|
||||
flags = newUpdateCommand(nil).Flags()
|
||||
flags.Set("stop-signal", "SIGWINCH")
|
||||
updateService(nil, nil, flags, spec)
|
||||
updateService(context.TODO(), nil, flags, spec)
|
||||
assert.Check(t, is.Equal("SIGWINCH", cspec.StopSignal))
|
||||
}
|
||||
|
||||
|
||||
@@ -195,6 +195,7 @@ func TestHandlePullSecret(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
c := c
|
||||
t.Run(c.version, func(t *testing.T) {
|
||||
conv, err := NewStackConverter(c.version)
|
||||
assert.NilError(t, err)
|
||||
@@ -222,6 +223,7 @@ func TestHandlePullPolicy(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
c := c
|
||||
t.Run(c.version, func(t *testing.T) {
|
||||
conv, err := NewStackConverter(c.version)
|
||||
assert.NilError(t, err)
|
||||
@@ -271,6 +273,7 @@ func TestHandleInternalServiceType(t *testing.T) {
|
||||
},
|
||||
}
|
||||
for _, c := range cases {
|
||||
c := c
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
res, err := fromComposeServiceConfig(composetypes.ServiceConfig{
|
||||
Name: "test",
|
||||
|
||||
@@ -56,16 +56,16 @@ func TestCreateChildResourcesV1Beta1(t *testing.T) {
|
||||
secrets))
|
||||
c, err := configs.Get("test", metav1.GetOptions{})
|
||||
assert.NilError(t, err)
|
||||
checkOwnerReferences(t, c.ObjectMeta, "test", v1beta1.SchemeGroupVersion.String())
|
||||
checkOwnerReferences(t, c.ObjectMeta, v1beta1.SchemeGroupVersion.String())
|
||||
s, err := secrets.Get("test", metav1.GetOptions{})
|
||||
assert.NilError(t, err)
|
||||
checkOwnerReferences(t, s.ObjectMeta, "test", v1beta1.SchemeGroupVersion.String())
|
||||
checkOwnerReferences(t, s.ObjectMeta, v1beta1.SchemeGroupVersion.String())
|
||||
}
|
||||
|
||||
func checkOwnerReferences(t *testing.T, objMeta metav1.ObjectMeta, stackName, stackVersion string) {
|
||||
func checkOwnerReferences(t *testing.T, objMeta metav1.ObjectMeta, stackVersion string) {
|
||||
t.Helper()
|
||||
assert.Equal(t, len(objMeta.OwnerReferences), 1)
|
||||
assert.Equal(t, objMeta.OwnerReferences[0].Name, stackName)
|
||||
assert.Equal(t, objMeta.OwnerReferences[0].Name, "test")
|
||||
assert.Equal(t, objMeta.OwnerReferences[0].Kind, "Stack")
|
||||
assert.Equal(t, objMeta.OwnerReferences[0].APIVersion, stackVersion)
|
||||
}
|
||||
@@ -82,10 +82,10 @@ func TestCreateChildResourcesV1Beta2(t *testing.T) {
|
||||
secrets))
|
||||
c, err := configs.Get("test", metav1.GetOptions{})
|
||||
assert.NilError(t, err)
|
||||
checkOwnerReferences(t, c.ObjectMeta, "test", v1beta2.SchemeGroupVersion.String())
|
||||
checkOwnerReferences(t, c.ObjectMeta, v1beta2.SchemeGroupVersion.String())
|
||||
s, err := secrets.Get("test", metav1.GetOptions{})
|
||||
assert.NilError(t, err)
|
||||
checkOwnerReferences(t, s.ObjectMeta, "test", v1beta2.SchemeGroupVersion.String())
|
||||
checkOwnerReferences(t, s.ObjectMeta, v1beta2.SchemeGroupVersion.String())
|
||||
}
|
||||
|
||||
func TestCreateChildResourcesV1Alpha3(t *testing.T) {
|
||||
@@ -100,10 +100,10 @@ func TestCreateChildResourcesV1Alpha3(t *testing.T) {
|
||||
secrets))
|
||||
c, err := configs.Get("test", metav1.GetOptions{})
|
||||
assert.NilError(t, err)
|
||||
checkOwnerReferences(t, c.ObjectMeta, "test", v1alpha3.SchemeGroupVersion.String())
|
||||
checkOwnerReferences(t, c.ObjectMeta, v1alpha3.SchemeGroupVersion.String())
|
||||
s, err := secrets.Get("test", metav1.GetOptions{})
|
||||
assert.NilError(t, err)
|
||||
checkOwnerReferences(t, s.ObjectMeta, "test", v1alpha3.SchemeGroupVersion.String())
|
||||
checkOwnerReferences(t, s.ObjectMeta, v1alpha3.SchemeGroupVersion.String())
|
||||
}
|
||||
|
||||
func TestCreateChildResourcesWithStackCreationErrorV1Beta1(t *testing.T) {
|
||||
|
||||
@@ -29,7 +29,7 @@ func GetStacks(kubeCli *KubeCli, opts options.List) ([]*formatter.Stack, error)
|
||||
}
|
||||
|
||||
func isAllNamespacesDisabled(kubeCliConfig *configfile.KubernetesConfig) bool {
|
||||
return kubeCliConfig == nil || kubeCliConfig != nil && kubeCliConfig.AllNamespaces != "disabled"
|
||||
return kubeCliConfig == nil || kubeCliConfig.AllNamespaces != "disabled"
|
||||
}
|
||||
|
||||
func getStacks(kubeCli *KubeCli, opts options.List) ([]*formatter.Stack, error) {
|
||||
|
||||
@@ -41,11 +41,13 @@ func newTestPodAndStackRepository(initialPods []apiv1.Pod, initialStacks []apiv1
|
||||
|
||||
o := k8stesting.NewObjectTracker(scheme, codecs.UniversalDecoder())
|
||||
for _, obj := range initialPods {
|
||||
obj := obj
|
||||
if err := o.Add(&obj); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
for _, obj := range initialStacks {
|
||||
obj := obj
|
||||
if err := o.Add(&obj); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
@@ -6,8 +6,7 @@ import (
|
||||
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/cli/internal/test"
|
||||
// Import builders to get the builder function as package function
|
||||
. "github.com/docker/cli/internal/test/builders"
|
||||
. "github.com/docker/cli/internal/test/builders" // Import builders to get the builder function as package function
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/swarm"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
@@ -7,8 +7,7 @@ import (
|
||||
|
||||
"github.com/docker/cli/cli/config/configfile"
|
||||
"github.com/docker/cli/internal/test"
|
||||
// Import builders to get the builder function as package function
|
||||
. "github.com/docker/cli/internal/test/builders"
|
||||
. "github.com/docker/cli/internal/test/builders" // Import builders to get the builder function as package function
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/swarm"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
@@ -6,8 +6,7 @@ import (
|
||||
|
||||
"github.com/docker/cli/cli/config/configfile"
|
||||
"github.com/docker/cli/internal/test"
|
||||
// Import builders to get the builder function as package function
|
||||
. "github.com/docker/cli/internal/test/builders"
|
||||
. "github.com/docker/cli/internal/test/builders" // Import builders to get the builder function as package function
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/swarm"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
@@ -6,11 +6,10 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/docker/cli/internal/test"
|
||||
. "github.com/docker/cli/internal/test/builders" // Import builders to get the builder function as package function
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/swarm"
|
||||
"github.com/pkg/errors"
|
||||
// Import builders to get the builder function as package function
|
||||
. "github.com/docker/cli/internal/test/builders"
|
||||
"gotest.tools/assert"
|
||||
"gotest.tools/golden"
|
||||
)
|
||||
|
||||
@@ -6,11 +6,10 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/docker/cli/internal/test"
|
||||
. "github.com/docker/cli/internal/test/builders" // Import builders to get the builder function as package function
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/swarm"
|
||||
"github.com/pkg/errors"
|
||||
// Import builders to get the builder function as package function
|
||||
. "github.com/docker/cli/internal/test/builders"
|
||||
"gotest.tools/assert"
|
||||
"gotest.tools/golden"
|
||||
)
|
||||
|
||||
@@ -7,12 +7,10 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/docker/cli/internal/test"
|
||||
. "github.com/docker/cli/internal/test/builders" // Import builders to get the builder function as package function
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/swarm"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
// Import builders to get the builder function as package function
|
||||
. "github.com/docker/cli/internal/test/builders"
|
||||
"gotest.tools/assert"
|
||||
"gotest.tools/golden"
|
||||
)
|
||||
|
||||
@@ -88,9 +88,7 @@ func runInfo(cmd *cobra.Command, dockerCli command.Cli, opts *infoOptions) error
|
||||
func prettyPrintInfo(dockerCli command.Cli, info info) error {
|
||||
fmt.Fprintln(dockerCli.Out(), "Client:")
|
||||
if info.ClientInfo != nil {
|
||||
if err := prettyPrintClientInfo(dockerCli, *info.ClientInfo); err != nil {
|
||||
info.ClientErrors = append(info.ClientErrors, err.Error())
|
||||
}
|
||||
prettyPrintClientInfo(dockerCli, *info.ClientInfo)
|
||||
}
|
||||
for _, err := range info.ClientErrors {
|
||||
fmt.Fprintln(dockerCli.Out(), "ERROR:", err)
|
||||
@@ -113,7 +111,7 @@ func prettyPrintInfo(dockerCli command.Cli, info info) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func prettyPrintClientInfo(dockerCli command.Cli, info clientInfo) error {
|
||||
func prettyPrintClientInfo(dockerCli command.Cli, info clientInfo) {
|
||||
fmt.Fprintln(dockerCli.Out(), " Debug Mode:", info.Debug)
|
||||
|
||||
if len(info.Plugins) > 0 {
|
||||
@@ -134,8 +132,6 @@ func prettyPrintClientInfo(dockerCli command.Cli, info clientInfo) error {
|
||||
if len(info.Warnings) > 0 {
|
||||
fmt.Fprintln(dockerCli.Err(), strings.Join(info.Warnings, "\n"))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// nolint: gocyclo
|
||||
|
||||
@@ -8,8 +8,7 @@ import (
|
||||
"github.com/docker/cli/cli/command/formatter"
|
||||
"github.com/docker/cli/cli/command/idresolver"
|
||||
"github.com/docker/cli/internal/test"
|
||||
// Import builders to get the builder function as package function
|
||||
. "github.com/docker/cli/internal/test/builders"
|
||||
. "github.com/docker/cli/internal/test/builders" // Import builders to get the builder function as package function
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/swarm"
|
||||
"gotest.tools/assert"
|
||||
|
||||
@@ -393,11 +393,6 @@ func TestGetSignerRolesWithKeyIDs(t *testing.T) {
|
||||
"bob": {"key71", "key72"},
|
||||
}
|
||||
|
||||
var roleWithSigs []client.RoleWithSignatures
|
||||
for _, role := range roles {
|
||||
roleWithSig := client.RoleWithSignatures{Role: role, Signatures: nil}
|
||||
roleWithSigs = append(roleWithSigs, roleWithSig)
|
||||
}
|
||||
signerRoleToKeyIDs := getDelegationRoleToKeyMap(roles)
|
||||
assert.Check(t, is.DeepEqual(expectedSignerRoleToKeyIDs, signerRoleToKeyIDs))
|
||||
}
|
||||
|
||||
@@ -88,7 +88,7 @@ func validateAndGenerateKey(streams command.Streams, keyName string, workingDir
|
||||
|
||||
pubPEM, err := generateKeyAndOutputPubPEM(keyName, privKeyFileStore)
|
||||
if err != nil {
|
||||
fmt.Fprintf(streams.Out(), err.Error())
|
||||
fmt.Fprint(streams.Out(), err.Error())
|
||||
return errors.Wrapf(err, "failed to generate key for %s", keyName)
|
||||
}
|
||||
|
||||
@@ -108,8 +108,7 @@ func generateKeyAndOutputPubPEM(keyName string, privKeyStore trustmanager.KeySto
|
||||
return pem.Block{}, err
|
||||
}
|
||||
|
||||
privKeyStore.AddKey(trustmanager.KeyInfo{Role: data.RoleName(keyName)}, privKey)
|
||||
if err != nil {
|
||||
if err := privKeyStore.AddKey(trustmanager.KeyInfo{Role: data.RoleName(keyName)}, privKey); err != nil {
|
||||
return pem.Block{}, err
|
||||
}
|
||||
|
||||
|
||||
@@ -117,6 +117,7 @@ var testKeys = map[string][]byte{
|
||||
func TestLoadKeyFromPath(t *testing.T) {
|
||||
skip.If(t, runtime.GOOS == "windows")
|
||||
for keyID, keyBytes := range testKeys {
|
||||
keyID, keyBytes := keyID, keyBytes
|
||||
t.Run(fmt.Sprintf("load-key-id-%s-from-path", keyID), func(t *testing.T) {
|
||||
testLoadKeyFromPath(t, keyID, keyBytes)
|
||||
})
|
||||
@@ -172,6 +173,7 @@ func testLoadKeyFromPath(t *testing.T, privKeyID string, privKeyFixture []byte)
|
||||
func TestLoadKeyTooPermissive(t *testing.T) {
|
||||
skip.If(t, runtime.GOOS == "windows")
|
||||
for keyID, keyBytes := range testKeys {
|
||||
keyID, keyBytes := keyID, keyBytes
|
||||
t.Run(fmt.Sprintf("load-key-id-%s-too-permissive", keyID), func(t *testing.T) {
|
||||
testLoadKeyTooPermissive(t, keyBytes)
|
||||
})
|
||||
|
||||
@@ -116,7 +116,7 @@ func TestGetOrGenerateNotaryKey(t *testing.T) {
|
||||
assert.Check(t, is.DeepEqual(rootKeyA.Public(), rootKeyB.Public()))
|
||||
|
||||
// Now also try with a delegation key
|
||||
releasesKey, err := getOrGenerateNotaryKey(notaryRepo, data.RoleName(trust.ReleasesRole))
|
||||
releasesKey, err := getOrGenerateNotaryKey(notaryRepo, trust.ReleasesRole)
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, releasesKey != nil)
|
||||
|
||||
|
||||
@@ -78,7 +78,7 @@ func PromptForConfirmation(ins io.Reader, outs io.Writer, message string) bool {
|
||||
}
|
||||
message += " [y/N] "
|
||||
|
||||
fmt.Fprintf(outs, message)
|
||||
_, _ = fmt.Fprint(outs, message)
|
||||
|
||||
// On Windows, force the use of the regular OS stdin stream.
|
||||
if runtime.GOOS == "windows" {
|
||||
@@ -130,7 +130,7 @@ func AddPlatformFlag(flags *pflag.FlagSet, target *string) {
|
||||
|
||||
// ValidateOutputPath validates the output paths of the `export` and `save` commands.
|
||||
func ValidateOutputPath(path string) error {
|
||||
dir := filepath.Dir(path)
|
||||
dir := filepath.Dir(filepath.Clean(path))
|
||||
if dir != "" && dir != "." {
|
||||
if _, err := os.Stat(dir); os.IsNotExist(err) {
|
||||
return errors.Errorf("invalid output path: directory %q does not exist", dir)
|
||||
@@ -139,6 +139,10 @@ func ValidateOutputPath(path string) error {
|
||||
// check whether `path` points to a regular file
|
||||
// (if the path exists and doesn't point to a directory)
|
||||
if fileInfo, err := os.Stat(path); !os.IsNotExist(err) {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if fileInfo.Mode().IsDir() || fileInfo.Mode().IsRegular() {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"gotest.tools/assert"
|
||||
)
|
||||
|
||||
@@ -31,3 +35,39 @@ func TestStringSliceReplaceAt(t *testing.T) {
|
||||
assert.Assert(t, !ok)
|
||||
assert.DeepEqual(t, []string{"foo"}, out)
|
||||
}
|
||||
|
||||
func TestValidateOutputPath(t *testing.T) {
|
||||
basedir, err := ioutil.TempDir("", "TestValidateOutputPath")
|
||||
assert.NilError(t, err)
|
||||
defer os.RemoveAll(basedir)
|
||||
dir := filepath.Join(basedir, "dir")
|
||||
notexist := filepath.Join(basedir, "notexist")
|
||||
err = os.MkdirAll(dir, 0755)
|
||||
assert.NilError(t, err)
|
||||
file := filepath.Join(dir, "file")
|
||||
err = ioutil.WriteFile(file, []byte("hi"), 0644)
|
||||
assert.NilError(t, err)
|
||||
var testcases = []struct {
|
||||
path string
|
||||
err error
|
||||
}{
|
||||
{basedir, nil},
|
||||
{file, nil},
|
||||
{dir, nil},
|
||||
{dir + string(os.PathSeparator), nil},
|
||||
{notexist, nil},
|
||||
{notexist + string(os.PathSeparator), nil},
|
||||
{filepath.Join(notexist, "file"), errors.New("does not exist")},
|
||||
}
|
||||
|
||||
for _, testcase := range testcases {
|
||||
t.Run(testcase.path, func(t *testing.T) {
|
||||
err := ValidateOutputPath(testcase.path)
|
||||
if testcase.err == nil {
|
||||
assert.NilError(t, err)
|
||||
} else {
|
||||
assert.ErrorContains(t, err, testcase.err.Error())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,10 +6,9 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/docker/cli/internal/test"
|
||||
. "github.com/docker/cli/internal/test/builders" // Import builders to get the builder function as package function
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/pkg/errors"
|
||||
// Import builders to get the builder function as package function
|
||||
. "github.com/docker/cli/internal/test/builders"
|
||||
"gotest.tools/assert"
|
||||
"gotest.tools/golden"
|
||||
)
|
||||
|
||||
@@ -6,12 +6,11 @@ import (
|
||||
|
||||
"github.com/docker/cli/cli/config/configfile"
|
||||
"github.com/docker/cli/internal/test"
|
||||
. "github.com/docker/cli/internal/test/builders" // Import builders to get the builder function as package function
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
volumetypes "github.com/docker/docker/api/types/volume"
|
||||
"github.com/pkg/errors"
|
||||
// Import builders to get the builder function as package function
|
||||
. "github.com/docker/cli/internal/test/builders"
|
||||
"gotest.tools/assert"
|
||||
"gotest.tools/golden"
|
||||
)
|
||||
|
||||
@@ -109,7 +109,7 @@ func Secrets(namespace Namespace, secrets map[string]composetypes.SecretConfig)
|
||||
var obj swarmFileObject
|
||||
var err error
|
||||
if secret.Driver != "" {
|
||||
obj, err = driverObjectConfig(namespace, name, composetypes.FileObjectConfig(secret))
|
||||
obj = driverObjectConfig(namespace, name, composetypes.FileObjectConfig(secret))
|
||||
} else {
|
||||
obj, err = fileObjectConfig(namespace, name, composetypes.FileObjectConfig(secret))
|
||||
}
|
||||
@@ -161,7 +161,7 @@ type swarmFileObject struct {
|
||||
Data []byte
|
||||
}
|
||||
|
||||
func driverObjectConfig(namespace Namespace, name string, obj composetypes.FileObjectConfig) (swarmFileObject, error) {
|
||||
func driverObjectConfig(namespace Namespace, name string, obj composetypes.FileObjectConfig) swarmFileObject {
|
||||
if obj.Name != "" {
|
||||
name = obj.Name
|
||||
} else {
|
||||
@@ -174,7 +174,7 @@ func driverObjectConfig(namespace Namespace, name string, obj composetypes.FileO
|
||||
Labels: AddStackLabel(namespace, obj.Labels),
|
||||
},
|
||||
Data: []byte{},
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func fileObjectConfig(namespace Namespace, name string, obj composetypes.FileObjectConfig) (swarmFileObject, error) {
|
||||
|
||||
@@ -66,11 +66,7 @@ func Service(
|
||||
configs []*swarm.ConfigReference,
|
||||
) (swarm.ServiceSpec, error) {
|
||||
name := namespace.Scope(service.Name)
|
||||
|
||||
endpoint, err := convertEndpointSpec(service.Deploy.EndpointMode, service.Ports)
|
||||
if err != nil {
|
||||
return swarm.ServiceSpec{}, err
|
||||
}
|
||||
endpoint := convertEndpointSpec(service.Deploy.EndpointMode, service.Ports)
|
||||
|
||||
mode, err := convertDeployMode(service.Deploy.Mode, service.Deploy.Replicas)
|
||||
if err != nil {
|
||||
@@ -103,10 +99,7 @@ func Service(
|
||||
return swarm.ServiceSpec{}, err
|
||||
}
|
||||
|
||||
dnsConfig, err := convertDNSConfig(service.DNS, service.DNSSearch)
|
||||
if err != nil {
|
||||
return swarm.ServiceSpec{}, err
|
||||
}
|
||||
dnsConfig := convertDNSConfig(service.DNS, service.DNSSearch)
|
||||
|
||||
var privileges swarm.Privileges
|
||||
privileges.CredentialSpec, err = convertCredentialSpec(
|
||||
@@ -575,7 +568,7 @@ func convertResources(source composetypes.Resources) (*swarm.ResourceRequirement
|
||||
return resources, nil
|
||||
}
|
||||
|
||||
func convertEndpointSpec(endpointMode string, source []composetypes.ServicePortConfig) (*swarm.EndpointSpec, error) {
|
||||
func convertEndpointSpec(endpointMode string, source []composetypes.ServicePortConfig) *swarm.EndpointSpec {
|
||||
portConfigs := []swarm.PortConfig{}
|
||||
for _, port := range source {
|
||||
portConfig := swarm.PortConfig{
|
||||
@@ -594,7 +587,7 @@ func convertEndpointSpec(endpointMode string, source []composetypes.ServicePortC
|
||||
return &swarm.EndpointSpec{
|
||||
Mode: swarm.ResolutionMode(strings.ToLower(endpointMode)),
|
||||
Ports: portConfigs,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func convertEnvironment(source map[string]*string) []string {
|
||||
@@ -629,14 +622,14 @@ func convertDeployMode(mode string, replicas *uint64) (swarm.ServiceMode, error)
|
||||
return serviceMode, nil
|
||||
}
|
||||
|
||||
func convertDNSConfig(DNS []string, DNSSearch []string) (*swarm.DNSConfig, error) {
|
||||
func convertDNSConfig(DNS []string, DNSSearch []string) *swarm.DNSConfig {
|
||||
if DNS != nil || DNSSearch != nil {
|
||||
return &swarm.DNSConfig{
|
||||
Nameservers: DNS,
|
||||
Search: DNSSearch,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func convertCredentialSpec(namespace Namespace, spec composetypes.CredentialSpecConfig, refs []*swarm.ConfigReference) (*swarm.CredentialSpec, error) {
|
||||
|
||||
@@ -177,7 +177,7 @@ func TestConvertEndpointSpec(t *testing.T) {
|
||||
Published: 80,
|
||||
},
|
||||
}
|
||||
endpoint, err := convertEndpointSpec("vip", source)
|
||||
endpoint := convertEndpointSpec("vip", source)
|
||||
|
||||
expected := swarm.EndpointSpec{
|
||||
Mode: swarm.ResolutionMode(strings.ToLower("vip")),
|
||||
@@ -195,7 +195,6 @@ func TestConvertEndpointSpec(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.DeepEqual(expected, *endpoint))
|
||||
}
|
||||
|
||||
@@ -271,13 +270,11 @@ func TestConvertServiceNetworksCustomDefault(t *testing.T) {
|
||||
}
|
||||
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.DeepEqual(expected, []swarm.NetworkAttachmentConfig(configs)))
|
||||
assert.Check(t, is.DeepEqual(expected, configs))
|
||||
}
|
||||
|
||||
func TestConvertDNSConfigEmpty(t *testing.T) {
|
||||
dnsConfig, err := convertDNSConfig(nil, nil)
|
||||
|
||||
assert.NilError(t, err)
|
||||
dnsConfig := convertDNSConfig(nil, nil)
|
||||
assert.Check(t, is.DeepEqual((*swarm.DNSConfig)(nil), dnsConfig))
|
||||
}
|
||||
|
||||
@@ -287,8 +284,7 @@ var (
|
||||
)
|
||||
|
||||
func TestConvertDNSConfigAll(t *testing.T) {
|
||||
dnsConfig, err := convertDNSConfig(nameservers, search)
|
||||
assert.NilError(t, err)
|
||||
dnsConfig := convertDNSConfig(nameservers, search)
|
||||
assert.Check(t, is.DeepEqual(&swarm.DNSConfig{
|
||||
Nameservers: nameservers,
|
||||
Search: search,
|
||||
@@ -296,8 +292,7 @@ func TestConvertDNSConfigAll(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestConvertDNSConfigNameservers(t *testing.T) {
|
||||
dnsConfig, err := convertDNSConfig(nameservers, nil)
|
||||
assert.NilError(t, err)
|
||||
dnsConfig := convertDNSConfig(nameservers, nil)
|
||||
assert.Check(t, is.DeepEqual(&swarm.DNSConfig{
|
||||
Nameservers: nameservers,
|
||||
Search: nil,
|
||||
@@ -305,8 +300,7 @@ func TestConvertDNSConfigNameservers(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestConvertDNSConfigSearch(t *testing.T) {
|
||||
dnsConfig, err := convertDNSConfig(nil, search)
|
||||
assert.NilError(t, err)
|
||||
dnsConfig := convertDNSConfig(nil, search)
|
||||
assert.Check(t, is.DeepEqual(&swarm.DNSConfig{
|
||||
Nameservers: nil,
|
||||
Search: search,
|
||||
|
||||
@@ -54,7 +54,6 @@ func Interpolate(config map[string]interface{}, opts Options) (map[string]interf
|
||||
|
||||
func recursiveInterpolate(value interface{}, path Path, opts Options) (interface{}, error) {
|
||||
switch value := value.(type) {
|
||||
|
||||
case string:
|
||||
newValue, err := opts.Substitute(value, template.Mapping(opts.LookupValue))
|
||||
if err != nil || newValue == value {
|
||||
@@ -91,7 +90,6 @@ func recursiveInterpolate(value interface{}, path Path, opts Options) (interface
|
||||
|
||||
default:
|
||||
return value, nil
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ var interpolateTypeCastMapping = map[interp.Path]interp.Cast{
|
||||
servicePath("deploy", "rollback_config", "parallelism"): toInt,
|
||||
servicePath("deploy", "rollback_config", "max_failure_ratio"): toFloat,
|
||||
servicePath("deploy", "restart_policy", "max_attempts"): toInt,
|
||||
servicePath("deploy", "placement", "max_replicas_per_node"): toInt,
|
||||
servicePath("ports", interp.PathMatchList, "target"): toInt,
|
||||
servicePath("ports", interp.PathMatchList, "published"): toInt,
|
||||
servicePath("ulimits", interp.PathMatchAll): toInt,
|
||||
|
||||
@@ -32,6 +32,14 @@ type Options struct {
|
||||
SkipInterpolation bool
|
||||
// Interpolation options
|
||||
Interpolate *interp.Options
|
||||
// Discard 'env_file' entries after resolving to 'environment' section
|
||||
discardEnvFiles bool
|
||||
}
|
||||
|
||||
// WithDiscardEnvFiles sets the Options to discard the `env_file` section after resolving to
|
||||
// the `environment` section
|
||||
func WithDiscardEnvFiles(opts *Options) {
|
||||
opts.discardEnvFiles = true
|
||||
}
|
||||
|
||||
// ParseYAML reads the bytes from a file, parses the bytes into a mapping
|
||||
@@ -105,6 +113,11 @@ func Load(configDetails types.ConfigDetails, options ...func(*Options)) (*types.
|
||||
return nil, err
|
||||
}
|
||||
cfg.Filename = file.Filename
|
||||
if opts.discardEnvFiles {
|
||||
for i := range cfg.Services {
|
||||
cfg.Services[i].EnvFile = nil
|
||||
}
|
||||
}
|
||||
|
||||
configs = append(configs, cfg)
|
||||
}
|
||||
@@ -284,10 +297,13 @@ func Transform(source interface{}, target interface{}, additionalTransformers ..
|
||||
return decoder.Decode(source)
|
||||
}
|
||||
|
||||
// TransformerFunc defines a function to perform the actual transformation
|
||||
type TransformerFunc func(interface{}) (interface{}, error)
|
||||
|
||||
// Transformer defines a map to type transformer
|
||||
type Transformer struct {
|
||||
TypeOf reflect.Type
|
||||
Func func(interface{}) (interface{}, error)
|
||||
Func TransformerFunc
|
||||
}
|
||||
|
||||
func createTransformHook(additionalTransformers ...Transformer) mapstructure.DecodeHookFuncType {
|
||||
@@ -671,7 +687,7 @@ func absPath(workingDir string, filePath string) string {
|
||||
return filepath.Join(workingDir, filePath)
|
||||
}
|
||||
|
||||
func transformMapStringString(data interface{}) (interface{}, error) {
|
||||
var transformMapStringString TransformerFunc = func(data interface{}) (interface{}, error) {
|
||||
switch value := data.(type) {
|
||||
case map[string]interface{}:
|
||||
return toMapStringString(value, false), nil
|
||||
@@ -682,7 +698,7 @@ func transformMapStringString(data interface{}) (interface{}, error) {
|
||||
}
|
||||
}
|
||||
|
||||
func transformExternal(data interface{}) (interface{}, error) {
|
||||
var transformExternal TransformerFunc = func(data interface{}) (interface{}, error) {
|
||||
switch value := data.(type) {
|
||||
case bool:
|
||||
return map[string]interface{}{"external": value}, nil
|
||||
@@ -693,7 +709,7 @@ func transformExternal(data interface{}) (interface{}, error) {
|
||||
}
|
||||
}
|
||||
|
||||
func transformServicePort(data interface{}) (interface{}, error) {
|
||||
var transformServicePort TransformerFunc = func(data interface{}) (interface{}, error) {
|
||||
switch entries := data.(type) {
|
||||
case []interface{}:
|
||||
// We process the list instead of individual items here.
|
||||
@@ -726,7 +742,7 @@ func transformServicePort(data interface{}) (interface{}, error) {
|
||||
}
|
||||
}
|
||||
|
||||
func transformStringSourceMap(data interface{}) (interface{}, error) {
|
||||
var transformStringSourceMap TransformerFunc = func(data interface{}) (interface{}, error) {
|
||||
switch value := data.(type) {
|
||||
case string:
|
||||
return map[string]interface{}{"source": value}, nil
|
||||
@@ -737,7 +753,7 @@ func transformStringSourceMap(data interface{}) (interface{}, error) {
|
||||
}
|
||||
}
|
||||
|
||||
func transformBuildConfig(data interface{}) (interface{}, error) {
|
||||
var transformBuildConfig TransformerFunc = func(data interface{}) (interface{}, error) {
|
||||
switch value := data.(type) {
|
||||
case string:
|
||||
return map[string]interface{}{"context": value}, nil
|
||||
@@ -748,7 +764,7 @@ func transformBuildConfig(data interface{}) (interface{}, error) {
|
||||
}
|
||||
}
|
||||
|
||||
func transformServiceVolumeConfig(data interface{}) (interface{}, error) {
|
||||
var transformServiceVolumeConfig TransformerFunc = func(data interface{}) (interface{}, error) {
|
||||
switch value := data.(type) {
|
||||
case string:
|
||||
return ParseVolume(value)
|
||||
@@ -759,7 +775,7 @@ func transformServiceVolumeConfig(data interface{}) (interface{}, error) {
|
||||
}
|
||||
}
|
||||
|
||||
func transformServiceNetworkMap(value interface{}) (interface{}, error) {
|
||||
var transformServiceNetworkMap TransformerFunc = func(value interface{}) (interface{}, error) {
|
||||
if list, ok := value.([]interface{}); ok {
|
||||
mapValue := map[interface{}]interface{}{}
|
||||
for _, name := range list {
|
||||
@@ -770,7 +786,7 @@ func transformServiceNetworkMap(value interface{}) (interface{}, error) {
|
||||
return value, nil
|
||||
}
|
||||
|
||||
func transformStringOrNumberList(value interface{}) (interface{}, error) {
|
||||
var transformStringOrNumberList TransformerFunc = func(value interface{}) (interface{}, error) {
|
||||
list := value.([]interface{})
|
||||
result := make([]string, len(list))
|
||||
for i, item := range list {
|
||||
@@ -779,7 +795,7 @@ func transformStringOrNumberList(value interface{}) (interface{}, error) {
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func transformStringList(data interface{}) (interface{}, error) {
|
||||
var transformStringList TransformerFunc = func(data interface{}) (interface{}, error) {
|
||||
switch value := data.(type) {
|
||||
case string:
|
||||
return []string{value}, nil
|
||||
@@ -790,13 +806,13 @@ func transformStringList(data interface{}) (interface{}, error) {
|
||||
}
|
||||
}
|
||||
|
||||
func transformMappingOrListFunc(sep string, allowNil bool) func(interface{}) (interface{}, error) {
|
||||
func transformMappingOrListFunc(sep string, allowNil bool) TransformerFunc {
|
||||
return func(data interface{}) (interface{}, error) {
|
||||
return transformMappingOrList(data, sep, allowNil), nil
|
||||
}
|
||||
}
|
||||
|
||||
func transformListOrMappingFunc(sep string, allowNil bool) func(interface{}) (interface{}, error) {
|
||||
func transformListOrMappingFunc(sep string, allowNil bool) TransformerFunc {
|
||||
return func(data interface{}) (interface{}, error) {
|
||||
return transformListOrMapping(data, sep, allowNil), nil
|
||||
}
|
||||
@@ -835,14 +851,14 @@ func transformMappingOrList(mappingOrList interface{}, sep string, allowNil bool
|
||||
panic(errors.Errorf("expected a map or a list, got %T: %#v", mappingOrList, mappingOrList))
|
||||
}
|
||||
|
||||
func transformShellCommand(value interface{}) (interface{}, error) {
|
||||
var transformShellCommand TransformerFunc = func(value interface{}) (interface{}, error) {
|
||||
if str, ok := value.(string); ok {
|
||||
return shellwords.Parse(str)
|
||||
}
|
||||
return value, nil
|
||||
}
|
||||
|
||||
func transformHealthCheckTest(data interface{}) (interface{}, error) {
|
||||
var transformHealthCheckTest TransformerFunc = func(data interface{}) (interface{}, error) {
|
||||
switch value := data.(type) {
|
||||
case string:
|
||||
return append([]string{"CMD-SHELL"}, value), nil
|
||||
@@ -853,7 +869,7 @@ func transformHealthCheckTest(data interface{}) (interface{}, error) {
|
||||
}
|
||||
}
|
||||
|
||||
func transformSize(value interface{}) (interface{}, error) {
|
||||
var transformSize TransformerFunc = func(value interface{}) (interface{}, error) {
|
||||
switch value := value.(type) {
|
||||
case int:
|
||||
return int64(value), nil
|
||||
@@ -863,7 +879,7 @@ func transformSize(value interface{}) (interface{}, error) {
|
||||
panic(errors.Errorf("invalid type for size %T", value))
|
||||
}
|
||||
|
||||
func transformStringToDuration(value interface{}) (interface{}, error) {
|
||||
var transformStringToDuration TransformerFunc = func(value interface{}) (interface{}, error) {
|
||||
switch value := value.(type) {
|
||||
case string:
|
||||
d, err := time.ParseDuration(value)
|
||||
|
||||
@@ -582,7 +582,7 @@ volumes:
|
||||
|
||||
func TestLoadWithInterpolationCastFull(t *testing.T) {
|
||||
dict, err := ParseYAML([]byte(`
|
||||
version: "3.7"
|
||||
version: "3.8"
|
||||
services:
|
||||
web:
|
||||
configs:
|
||||
@@ -604,6 +604,8 @@ services:
|
||||
max_failure_ratio: $thefloat
|
||||
restart_policy:
|
||||
max_attempts: $theint
|
||||
placement:
|
||||
max_replicas_per_node: $theint
|
||||
ports:
|
||||
- $theint
|
||||
- "34567"
|
||||
@@ -652,7 +654,7 @@ networks:
|
||||
assert.NilError(t, err)
|
||||
expected := &types.Config{
|
||||
Filename: "filename.yml",
|
||||
Version: "3.7",
|
||||
Version: "3.8",
|
||||
Services: []types.ServiceConfig{
|
||||
{
|
||||
Name: "web",
|
||||
@@ -685,6 +687,9 @@ networks:
|
||||
RestartPolicy: &types.RestartPolicy{
|
||||
MaxAttempts: uint64Ptr(555),
|
||||
},
|
||||
Placement: types.Placement{
|
||||
MaxReplicas: 555,
|
||||
},
|
||||
},
|
||||
Ports: []types.ServicePortConfig{
|
||||
{Target: 555, Mode: "ingress", Protocol: "tcp"},
|
||||
@@ -759,6 +764,38 @@ services:
|
||||
assert.Check(t, is.DeepEqual([]string{"build", "links", "pid"}, unsupported))
|
||||
}
|
||||
|
||||
func TestDiscardEnvFileOption(t *testing.T) {
|
||||
dict, err := ParseYAML([]byte(`version: "3"
|
||||
services:
|
||||
web:
|
||||
image: nginx
|
||||
env_file:
|
||||
- example1.env
|
||||
- example2.env
|
||||
`))
|
||||
expectedEnvironmentMap := types.MappingWithEquals{
|
||||
"FOO": strPtr("foo_from_env_file"),
|
||||
"BAZ": strPtr("baz_from_env_file"),
|
||||
"BAR": strPtr("bar_from_env_file_2"), // Original value is overwritten by example2.env
|
||||
"QUX": strPtr("quz_from_env_file_2"),
|
||||
}
|
||||
assert.NilError(t, err)
|
||||
configDetails := buildConfigDetails(dict, nil)
|
||||
|
||||
// Default behavior keeps the `env_file` entries
|
||||
configWithEnvFiles, err := Load(configDetails)
|
||||
assert.NilError(t, err)
|
||||
assert.DeepEqual(t, configWithEnvFiles.Services[0].EnvFile, types.StringList{"example1.env",
|
||||
"example2.env"})
|
||||
assert.DeepEqual(t, configWithEnvFiles.Services[0].Environment, expectedEnvironmentMap)
|
||||
|
||||
// Custom behavior removes the `env_file` entries
|
||||
configWithoutEnvFiles, err := Load(configDetails, WithDiscardEnvFiles)
|
||||
assert.NilError(t, err)
|
||||
assert.DeepEqual(t, configWithoutEnvFiles.Services[0].EnvFile, types.StringList(nil))
|
||||
assert.DeepEqual(t, configWithoutEnvFiles.Services[0].Environment, expectedEnvironmentMap)
|
||||
}
|
||||
|
||||
func TestBuildProperties(t *testing.T) {
|
||||
dict, err := ParseYAML([]byte(`
|
||||
version: "3"
|
||||
@@ -1551,6 +1588,7 @@ services:
|
||||
},
|
||||
}
|
||||
for _, testcase := range testcases {
|
||||
testcase := testcase
|
||||
t.Run(testcase.doc, func(t *testing.T) {
|
||||
config, err := loadYAML(testcase.yaml)
|
||||
assert.NilError(t, err)
|
||||
|
||||
@@ -60,6 +60,7 @@ func mergeServices(base, override []types.ServiceConfig) ([]types.ServiceConfig,
|
||||
},
|
||||
}
|
||||
for name, overrideService := range overrideServices {
|
||||
overrideService := overrideService
|
||||
if baseService, ok := baseServices[name]; ok {
|
||||
if err := mergo.Merge(&baseService, &overrideService, mergo.WithAppendSlice, mergo.WithOverride, mergo.WithTransformers(specials)); err != nil {
|
||||
return base, errors.Wrapf(err, "cannot merge service %s", name)
|
||||
|
||||
@@ -43,7 +43,8 @@ var winisabstests = []IsAbsTest{
|
||||
}
|
||||
|
||||
func TestIsAbs(t *testing.T) {
|
||||
tests := append(isabstests, winisabstests...)
|
||||
tests := winisabstests
|
||||
|
||||
// All non-windows tests should fail, because they have no volume letter.
|
||||
for _, test := range isabstests {
|
||||
tests = append(tests, IsAbsTest{test.path, false})
|
||||
@@ -53,7 +54,7 @@ func TestIsAbs(t *testing.T) {
|
||||
tests = append(tests, IsAbsTest{"c:" + test.path, test.isAbs})
|
||||
}
|
||||
|
||||
for _, test := range winisabstests {
|
||||
for _, test := range tests {
|
||||
if r := isAbs(test.path); r != test.isAbs {
|
||||
t.Errorf("IsAbs(%q) = %v, want %v", test.path, r, test.isAbs)
|
||||
}
|
||||
|
||||
@@ -117,7 +117,6 @@ func TestValidateCredentialSpecs(t *testing.T) {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestValidateSecretConfigNames(t *testing.T) {
|
||||
|
||||
@@ -275,6 +275,7 @@ func TestExtractVariables(t *testing.T) {
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
tc := tc
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
actual := ExtractVariables(tc.dict, defaultPattern)
|
||||
assert.Check(t, is.DeepEqual(actual, tc.expected))
|
||||
|
||||
@@ -103,7 +103,7 @@ type Config struct {
|
||||
Volumes map[string]VolumeConfig `yaml:",omitempty" json:"volumes,omitempty"`
|
||||
Secrets map[string]SecretConfig `yaml:",omitempty" json:"secrets,omitempty"`
|
||||
Configs map[string]ConfigObjConfig `yaml:",omitempty" json:"configs,omitempty"`
|
||||
Extras map[string]interface{} `yaml:",inline", json:"-"`
|
||||
Extras map[string]interface{} `yaml:",inline" json:"-"`
|
||||
}
|
||||
|
||||
// MarshalJSON makes Config implement json.Marshaler
|
||||
|
||||
@@ -454,7 +454,6 @@ func TestJSONWithPsFormatNoFile(t *testing.T) {
|
||||
if config.PsFormat != `table {{.ID}}\t{{.Label "com.docker.label.cpu"}}` {
|
||||
t.Fatalf("Unknown ps format: %s\n", config.PsFormat)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestJSONSaveWithNoFile(t *testing.T) {
|
||||
@@ -578,6 +577,7 @@ func TestConfigPath(t *testing.T) {
|
||||
expectedErr: fmt.Sprintf("is outside of root config directory %q", "dummy"),
|
||||
},
|
||||
} {
|
||||
tc := tc
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
SetDir(tc.dir)
|
||||
f, err := Path(tc.path...)
|
||||
|
||||
@@ -123,9 +123,11 @@ func (configFile *ConfigFile) LoadFromReader(configData io.Reader) error {
|
||||
}
|
||||
var err error
|
||||
for addr, ac := range configFile.AuthConfigs {
|
||||
ac.Username, ac.Password, err = decodeAuth(ac.Auth)
|
||||
if err != nil {
|
||||
return err
|
||||
if ac.Auth != "" {
|
||||
ac.Username, ac.Password, err = decodeAuth(ac.Auth)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
ac.Auth = ""
|
||||
ac.ServerAddress = addr
|
||||
|
||||
@@ -2,6 +2,7 @@ package configfile
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
@@ -380,6 +381,41 @@ func TestGetAllCredentialsCredHelperOverridesDefaultStore(t *testing.T) {
|
||||
assert.Check(t, is.Equal(0, testCredHelper.(*mockNativeStore).GetAllCallCount))
|
||||
}
|
||||
|
||||
func TestLoadFromReaderWithUsernamePassword(t *testing.T) {
|
||||
configFile := New("test-load")
|
||||
defer os.Remove("test-load")
|
||||
|
||||
want := types.AuthConfig{
|
||||
Username: "user",
|
||||
Password: "pass",
|
||||
}
|
||||
|
||||
for _, tc := range []types.AuthConfig{
|
||||
want,
|
||||
{
|
||||
Auth: encodeAuth(&want),
|
||||
},
|
||||
} {
|
||||
cf := ConfigFile{
|
||||
AuthConfigs: map[string]types.AuthConfig{
|
||||
"example.com/foo": tc,
|
||||
},
|
||||
}
|
||||
|
||||
b, err := json.Marshal(cf)
|
||||
assert.NilError(t, err)
|
||||
|
||||
err = configFile.LoadFromReader(bytes.NewReader(b))
|
||||
assert.NilError(t, err)
|
||||
|
||||
got, err := configFile.GetAuthConfig("example.com/foo")
|
||||
assert.NilError(t, err)
|
||||
|
||||
assert.Check(t, is.DeepEqual(want.Username, got.Username))
|
||||
assert.Check(t, is.DeepEqual(want.Password, got.Password))
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckKubernetesConfigurationRaiseAnErrorOnInvalidValue(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
|
||||
@@ -153,8 +153,8 @@ func TestImportTarInvalid(t *testing.T) {
|
||||
tf := path.Join(testDir, "test.context")
|
||||
|
||||
f, err := os.Create(tf)
|
||||
defer f.Close()
|
||||
assert.NilError(t, err)
|
||||
defer f.Close()
|
||||
|
||||
tw := tar.NewWriter(f)
|
||||
hdr := &tar.Header{
|
||||
@@ -186,8 +186,8 @@ func TestImportZip(t *testing.T) {
|
||||
zf := path.Join(testDir, "test.zip")
|
||||
|
||||
f, err := os.Create(zf)
|
||||
defer f.Close()
|
||||
assert.NilError(t, err)
|
||||
defer f.Close()
|
||||
w := zip.NewWriter(f)
|
||||
|
||||
meta, err := json.Marshal(Metadata{
|
||||
@@ -237,8 +237,8 @@ func TestImportZipInvalid(t *testing.T) {
|
||||
zf := path.Join(testDir, "test.zip")
|
||||
|
||||
f, err := os.Create(zf)
|
||||
defer f.Close()
|
||||
assert.NilError(t, err)
|
||||
defer f.Close()
|
||||
w := zip.NewWriter(f)
|
||||
|
||||
df, err := w.Create("dummy-file")
|
||||
|
||||
@@ -75,5 +75,4 @@ func TestTlsListAndBatchRemove(t *testing.T) {
|
||||
resEmpty, err := testee.listContextData("test-ctx")
|
||||
assert.NilError(t, err)
|
||||
assert.DeepEqual(t, resEmpty, map[string]EndpointFiles{})
|
||||
|
||||
}
|
||||
|
||||
@@ -93,6 +93,7 @@ func TestStoreSaveAndGet(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, testcase := range testcases {
|
||||
testcase := testcase
|
||||
t.Run(testcase.manifestRef.String(), func(t *testing.T) {
|
||||
actual, err := store.Get(testcase.listRef, testcase.manifestRef)
|
||||
if testcase.expectedErr != "" {
|
||||
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
"github.com/docker/distribution/manifest/schema2"
|
||||
"github.com/docker/distribution/reference"
|
||||
"github.com/docker/distribution/registry/api/errcode"
|
||||
"github.com/docker/distribution/registry/api/v2"
|
||||
v2 "github.com/docker/distribution/registry/api/v2"
|
||||
distclient "github.com/docker/distribution/registry/client"
|
||||
"github.com/docker/docker/registry"
|
||||
digest "github.com/opencontainers/go-digest"
|
||||
@@ -103,9 +103,6 @@ func pullManifestSchemaV2ImageConfig(ctx context.Context, dgst digest.Digest, re
|
||||
}
|
||||
|
||||
verifier := dgst.Verifier()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err := verifier.Write(configJSON); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -212,7 +209,6 @@ func (c *client) iterateEndpoints(ctx context.Context, namedRef reference.Named,
|
||||
|
||||
confirmedTLSRegistries := make(map[string]bool)
|
||||
for _, endpoint := range endpoints {
|
||||
|
||||
if endpoint.Version == registry.APIVersion1 {
|
||||
logrus.Debugf("skipping v1 endpoint %s", endpoint.URL)
|
||||
continue
|
||||
|
||||
@@ -99,6 +99,7 @@ func ExactArgs(number int) cobra.PositionalArgs {
|
||||
}
|
||||
}
|
||||
|
||||
//nolint: unparam
|
||||
func pluralize(word string, number int) string {
|
||||
if number == 1 {
|
||||
return word
|
||||
|
||||
@@ -550,17 +550,18 @@ __docker_complete_nodes() {
|
||||
# output to the IDs or names of matching items. This setting takes
|
||||
# precedence over the environment setting.
|
||||
__docker_services() {
|
||||
local fields='$2' # default: service name only
|
||||
[ "${DOCKER_COMPLETION_SHOW_SERVICE_IDS}" = yes ] && fields='$1,$2' # ID & name
|
||||
local format='{{.Name}}' # default: service name only
|
||||
[ "${DOCKER_COMPLETION_SHOW_SERVICE_IDS}" = yes ] && format='{{.ID}} {{.Name}}' # ID & name
|
||||
|
||||
if [ "$1" = "--id" ] ; then
|
||||
fields='$1' # IDs only
|
||||
format='{{.ID}}' # IDs only
|
||||
shift
|
||||
elif [ "$1" = "--name" ] ; then
|
||||
fields='$2' # names only
|
||||
format='{{.Name}}' # names only
|
||||
shift
|
||||
fi
|
||||
__docker_q service ls "$@" | awk "NR>1 {print $fields}"
|
||||
|
||||
__docker_q service ls --quiet --format "$format" "$@"
|
||||
}
|
||||
|
||||
# __docker_complete_services applies completion of services based on the current
|
||||
@@ -572,7 +573,7 @@ __docker_complete_services() {
|
||||
current="$2"
|
||||
shift 2
|
||||
fi
|
||||
COMPREPLY=( $(compgen -W "$(__docker_services "$@")" -- "$current") )
|
||||
COMPREPLY=( $(__docker_services "$@" --filter "name=$current") )
|
||||
}
|
||||
|
||||
# __docker_tasks returns a list of all task IDs.
|
||||
@@ -1204,6 +1205,7 @@ _docker_build() {
|
||||
|
||||
_docker_builder() {
|
||||
local subcommands="
|
||||
build
|
||||
prune
|
||||
"
|
||||
__docker_subcommands "$subcommands" && return
|
||||
@@ -1218,6 +1220,10 @@ _docker_builder() {
|
||||
esac
|
||||
}
|
||||
|
||||
_docker_builder_build() {
|
||||
_docker_image_build
|
||||
}
|
||||
|
||||
_docker_builder_prune() {
|
||||
case "$prev" in
|
||||
--filter)
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
function __fish_docker_no_subcommand --description 'Test if docker has yet to be given the subcommand'
|
||||
for i in (commandline -opc)
|
||||
if contains -- $i attach build commit cp create diff events exec export history images import info inspect kill load login logout logs pause port ps pull push rename restart rm rmi run save search start stop tag top trust unpause version wait stats
|
||||
if contains -- $i attach build commit cp create diff events exec export history images import info inspect kill load login logout logs network pause port ps pull push rename restart rm rmi run save search start stop tag top trust unpause version wait stats
|
||||
return 1
|
||||
end
|
||||
end
|
||||
@@ -34,6 +34,11 @@ function __fish_print_docker_containers --description 'Print a list of docker co
|
||||
end
|
||||
end
|
||||
|
||||
function __fish_print_docker_networks --description 'Print a list of docker networks'
|
||||
docker network ls --format "{{.ID}}\n{{.Name}}" | tr ',' '\n'
|
||||
end
|
||||
|
||||
|
||||
function __fish_docker_no_subcommand_trust --description 'Test if docker has yet to be given the trust subcommand'
|
||||
if __fish_seen_subcommand_from trust
|
||||
for i in (commandline -opc)
|
||||
@@ -370,6 +375,21 @@ complete -c docker -A -f -n '__fish_seen_subcommand_from logs' -l since -d 'Show
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from logs' -l tail -d 'Output the specified number of lines at the end of logs (defaults to all logs)'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from logs' -a '(__fish_print_docker_containers running)' -d "Container"
|
||||
|
||||
# network
|
||||
complete -c docker -f -n '__fish_docker_no_subcommand' -a network -d 'Manage networks'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from network' -a connect -d 'Connect a container to a network'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from network' -a create -d 'Create a network'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from network' -a disconnect -d 'Disconnect a container from a network'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from network' -a inspect -d 'Display detailed information on one or more networks'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from network' -a ls -d 'List networks'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from network' -a prune -d 'Remove all unused networks'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from network' -a rm -d 'Remove one or more networks'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from network' -l help -d 'Print usage'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from network rm' -a '(__fish_print_docker_networks)' -d "Network"
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from network connect' -a '(__fish_print_docker_networks)' -d "Network"
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from network disconnect' -a '(__fish_print_docker_networks)' -d "Network"
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from network inspect' -a '(__fish_print_docker_networks)' -d "Network"
|
||||
|
||||
# port
|
||||
complete -c docker -f -n '__fish_docker_no_subcommand' -a port -d 'Lookup the public-facing port that is NAT-ed to PRIVATE_PORT'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from port' -l help -d 'Print usage'
|
||||
@@ -419,7 +439,7 @@ complete -c docker -f -n '__fish_docker_no_subcommand' -a rm -d 'Remove one or m
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from rm' -s f -l force -d 'Force the removal of a running container (uses SIGKILL)'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from rm' -l help -d 'Print usage'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from rm' -s l -l link -d 'Remove the specified link and not the underlying container'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from rm' -s v -l volumes -d 'Remove the volumes associated with the container'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from rm' -s v -l volumes -d 'Remove anonymous volumes associated with the container'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from rm' -a '(__fish_print_docker_containers stopped)' -d "Container"
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from rm' -s f -l force -a '(__fish_print_docker_containers all)' -d "Container"
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
ARG GO_VERSION=1.12.10
|
||||
ARG GO_VERSION=1.12.16
|
||||
|
||||
FROM golang:${GO_VERSION}-alpine
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
ARG GO_VERSION=1.12.10
|
||||
ARG GO_VERSION=1.12.16
|
||||
|
||||
FROM dockercore/golang-cross:${GO_VERSION}
|
||||
ENV DISABLE_WARN_OUTSIDE_CONTAINER=1
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
ARG GO_VERSION=1.12.10
|
||||
ARG GO_VERSION=1.12.16
|
||||
|
||||
FROM golang:${GO_VERSION}-alpine
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
ARG GO_VERSION=1.12.10
|
||||
ARG GO_VERSION=1.12.16
|
||||
|
||||
# Use Debian based image as docker-compose requires glibc.
|
||||
FROM golang:${GO_VERSION}
|
||||
|
||||
+17
-14
@@ -1,20 +1,23 @@
|
||||
ARG GO_VERSION=1.12.10
|
||||
# syntax=docker/dockerfile:1.1.3-experimental
|
||||
|
||||
FROM golang:${GO_VERSION}-alpine
|
||||
ARG GO_VERSION=1.12.16
|
||||
ARG GOLANGCI_LINTER_SHA="v1.21.0"
|
||||
|
||||
RUN apk add -U git
|
||||
FROM golang:${GO_VERSION}-alpine AS build
|
||||
ENV CGO_ENABLED=0
|
||||
RUN apk add --no-cache git
|
||||
ARG GOLANGCI_LINTER_SHA
|
||||
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}
|
||||
|
||||
ARG GOMETALINTER_SHA=v2.0.6
|
||||
RUN go get -d github.com/alecthomas/gometalinter && \
|
||||
cd /go/src/github.com/alecthomas/gometalinter && \
|
||||
git checkout -q "$GOMETALINTER_SHA" && \
|
||||
go build -v -o /usr/local/bin/gometalinter . && \
|
||||
gometalinter --install && \
|
||||
rm -rf /go/src/* /go/pkg/*
|
||||
|
||||
WORKDIR /go/src/github.com/docker/cli
|
||||
FROM golang:${GO_VERSION}-alpine AS lint
|
||||
ENV CGO_ENABLED=0
|
||||
ENV DISABLE_WARN_OUTSIDE_CONTAINER=1
|
||||
ENTRYPOINT ["/usr/local/bin/gometalinter"]
|
||||
CMD ["--config=gometalinter.json", "./..."]
|
||||
COPY --from=build /go/bin/golangci-lint /usr/local/bin
|
||||
WORKDIR /go/src/github.com/docker/cli
|
||||
ENV GOGC=75
|
||||
ENTRYPOINT ["/usr/local/bin/golangci-lint"]
|
||||
CMD ["run", "--config=.golangci.yml"]
|
||||
COPY . .
|
||||
|
||||
+200
-93
@@ -15,15 +15,68 @@ keywords: "docker, documentation, about, technology, deprecate"
|
||||
|
||||
# Deprecated Engine Features
|
||||
|
||||
The following list of features are deprecated in Engine.
|
||||
To learn more about Docker Engine's deprecation policy,
|
||||
see [Feature Deprecation Policy](https://docs.docker.com/engine/#feature-deprecation-policy).
|
||||
The following list of features are deprecated in Engine. To learn more about Docker
|
||||
Engine's deprecation policy, see [Feature Deprecation Policy](https://docs.docker.com/engine/#feature-deprecation-policy).
|
||||
|
||||
The table below provides an overview of the current status of deprecated features:
|
||||
|
||||
- **Deprecated**: the feature is marked "deprecated" and should no longer be used.
|
||||
The feature may be removed, disabled, or change behavior in a future release.
|
||||
The _"Deprecated"_ column contains the release in which the feature was marked
|
||||
deprecated, whereas the _"Remove"_ column contains a tentative release in which
|
||||
the feature is to be removed. If no release is included in the _"Remove"_ column,
|
||||
the release is yet to be decided on.
|
||||
- **Removed**: the feature was removed, disabled, or hidden. Refer to the linked
|
||||
section for details. Some features are "soft" deprecated, which means that they
|
||||
remain functional for backward compatibility, and to allow users to migrate to
|
||||
alternatives. In such cases, a warning may be printed, and users should not rely
|
||||
on this feature.
|
||||
|
||||
Status | Feature | Deprecated | Remove
|
||||
-----------|------------------------------------------------------------------------------------------------------------------------------------|------------|------------
|
||||
Deprecated | [Pushing and pulling with image manifest v2 schema 1](#pushing-and-pulling-with-image-manifest-v2-schema-1) | v19.03.0 | v20.03.0
|
||||
Deprecated | [`docker engine` subcommands](#docker-engine-subcommands) | v19.03.0 | v20.03.0
|
||||
Deprecated | [Top-level `docker deploy` subcommand (experimental)](#top-level-docker-deploy-subcommand-experimental) | v19.03.0 | v20.03.0
|
||||
Deprecated | [`docker stack deploy` using "dab" files (experimental)](#docker-stack-deploy-using-dab-files-experimental) | v19.03.0 | v20.03.0
|
||||
Deprecated | [AuFS storage driver](#aufs-storage-driver) | v19.03.0 | -
|
||||
Deprecated | [Legacy "overlay" storage driver](#legacy-overlay-storage-driver) | v18.09.0 | -
|
||||
Deprecated | [Device mapper storage driver](#device-mapper-storage-driver) | v18.09.0 | -
|
||||
Deprecated | [Reserved namespaces in engine labels](#reserved-namespaces-in-engine-labels) | v18.06.0 | v20.03.0
|
||||
Removed | [`--disable-legacy-registry` override daemon option](#--disable-legacy-registry-override-daemon-option) | v17.12.0 | v19.03.0
|
||||
Removed | [Interacting with V1 registries](#interacting-with-v1-registries) | v17.06.0 | v17.12.0
|
||||
Removed | [Asynchronous `service create` and `service update` as default](#asynchronous-service-create-and-service-update-as-default) | v17.05.0 | v17.10.0
|
||||
Removed | [`-g` and `--graph` flags on `dockerd`](#-g-and---graph-flags-on-dockerd) | v17.05.0 | -
|
||||
Deprecated | [Top-level network properties in NetworkSettings](#top-level-network-properties-in-networksettings) | v1.13.0 | v17.12.0
|
||||
Deprecated | [`filter` param for `/images/json` endpoint](#filter-param-for-imagesjson-endpoint) | v1.13.0 | v17.12.0
|
||||
Removed | [`repository:shortid` image references](#repositoryshortid-image-references) | v1.13.0 | v17.12.0
|
||||
Removed | [`docker daemon` subcommand](#docker-daemon-subcommand) | v1.13.0 | v17.12.0
|
||||
Removed | [Duplicate keys with conflicting values in engine labels](#duplicate-keys-with-conflicting-values-in-engine-labels) | v1.13.0 | v17.12.0
|
||||
Deprecated | [`MAINTAINER` in Dockerfile](#maintainer-in-dockerfile) | v1.13.0 | -
|
||||
Deprecated | [API calls without a version](#api-calls-without-a-version) | v1.13.0 | v17.12.0
|
||||
Removed | [Backing filesystem without `d_type` support for overlay/overlay2](#backing-filesystem-without-d_type-support-for-overlayoverlay2) | v1.13.0 | v17.12.0
|
||||
Deprecated | [`--automated` and `--stars` flags on `docker search`](#--automated-and---stars-flags-on-docker-search) | v1.12.0 | v17.09.0
|
||||
Deprecated | [`-h` shorthand for `--help`](#-h-shorthand-for---help) | v1.12.0 | v17.09.0
|
||||
Removed | [`-e` and `--email` flags on `docker login`](#-e-and---email-flags-on-docker-login) | v1.11.0 | v17.06.0
|
||||
Deprecated | [Separator (`:`) of `--security-opt` flag on `docker run`](#separator--of---security-opt-flag-on-docker-run) | v1.11.0 | v17.06.0
|
||||
Deprecated | [Ambiguous event fields in API](#ambiguous-event-fields-in-api) | v1.10.0 | -
|
||||
Removed | [`-f` flag on `docker tag`](#-f-flag-on-docker-tag) | v1.10.0 | v1.12.0
|
||||
Removed | [HostConfig at API container start](#hostconfig-at-api-container-start) | v1.10.0 | v1.12.0
|
||||
Removed | [`--before` and `--since` flags on `docker ps`](#--before-and---since-flags-on-docker-ps) | v1.10.0 | v1.12.0
|
||||
Removed | [Driver-specific log tags](#driver-specific-log-tags) | v1.9.0 | v1.12.0
|
||||
Removed | [Docker Content Trust `ENV` passphrase variables name change](#docker-content-trust-env-passphrase-variables-name-change) | v1.9.0 | v1.12.0
|
||||
Removed | [`/containers/(id or name)/copy` endpoint](#containersid-or-namecopy-endpoint) | v1.8.0 | v1.12.0
|
||||
Removed | [LXC built-in exec driver](#lxc-built-in-exec-driver) | v1.8.0 | v1.10.0
|
||||
Removed | [Old Command Line Options](#old-command-line-options) | v1.8.0 | v1.10.0
|
||||
Removed | [`--api-enable-cors` flag on `dockerd`](#--api-enable-cors-flag-on-dockerd) | v1.6.0 | v17.09.0
|
||||
Removed | [`--run` flag on `docker commit`](#--run-flag-on-docker-commit) | v0.10.0 | v1.13.0
|
||||
Removed | [Three arguments form in `docker import`](#three-arguments-form-in-docker-import) | v0.6.7 | v1.12.0
|
||||
|
||||
|
||||
### Pushing and pulling with image manifest v2 schema 1
|
||||
|
||||
**Deprecated in Release: v19.03.0**
|
||||
|
||||
**Target For Removal In Release: v19.09.0**
|
||||
**Target For Removal In Release: v20.03.0**
|
||||
|
||||
The image manifest
|
||||
[v2 schema 1](https://github.com/docker/distribution/blob/fda42e5ef908bdba722d435ff1f330d40dfcd56c/docs/spec/manifest-v2-1.md)
|
||||
@@ -32,36 +85,46 @@ format is deprecated in favor of the
|
||||
|
||||
If the registry you are using still supports v2 schema 1, urge their administrators to move to v2 schema 2.
|
||||
|
||||
### Legacy "overlay" storage driver
|
||||
|
||||
**Deprecated in Release: v18.09.0**
|
||||
### `docker engine` subcommands
|
||||
|
||||
The `overlay` storage driver is deprecated in favor of the `overlay2` storage
|
||||
driver, which has all the benefits of `overlay`, without its limitations (excessive
|
||||
inode consumption). The legacy `overlay` storage driver will be removed in a future
|
||||
release. Users of the `overlay` storage driver should migrate to the `overlay2`
|
||||
storage driver.
|
||||
**Deprecated in Release: v19.03.0**
|
||||
|
||||
The legacy `overlay` storage driver allowed using overlayFS-backed filesystems
|
||||
on pre 4.x kernels. Now that all supported distributions are able to run `overlay2`
|
||||
(as they are either on kernel 4.x, or have support for multiple lowerdirs
|
||||
backported), there is no reason to keep maintaining the `overlay` storage driver.
|
||||
**Target For Removal In Release: v20.03.0**
|
||||
|
||||
### device mapper storage driver
|
||||
The `docker engine activate`, `docker engine check`, and `docker engine update`
|
||||
provided an alternative installation method to upgrade Docker Community engines
|
||||
to Docker Enterprise, using an image-based distribution of the Docker Engine.
|
||||
|
||||
**Deprecated in Release: v18.09.0**
|
||||
This feature was only available on Linux, and only when executed on a local node.
|
||||
Given the limitations of this feature, and the feature not getting widely adopted,
|
||||
the `docker engine` subcommands will be removed, in favor of installation through
|
||||
standard package managers.
|
||||
|
||||
The `devicemapper` storage driver is deprecated in favor of `overlay2`, and will
|
||||
be removed in a future release. Users of the `devicemapper` storage driver are
|
||||
recommended to migrate to a different storage driver, such as `overlay2`, which
|
||||
is now the default storage driver.
|
||||
|
||||
The `devicemapper` storage driver facilitates running Docker on older (3.x) kernels
|
||||
that have no support for other storage drivers (such as overlay2, or AUFS).
|
||||
### Top-level `docker deploy` subcommand (experimental)
|
||||
|
||||
**Deprecated in Release: v19.03.0**
|
||||
|
||||
**Target For Removal In Release: v20.03.0**
|
||||
|
||||
The top-level `docker deploy` command (using the "Docker Application Bundle"
|
||||
(.dab) file format was introduced as an experimental feature in Docker 1.13 /
|
||||
17.03, but superseded by support for Docker Compose files using the `docker stack deploy`
|
||||
subcommand.
|
||||
|
||||
|
||||
### `docker stack deploy` using "dab" files (experimental)
|
||||
|
||||
**Deprecated in Release: v19.03.0**
|
||||
|
||||
**Target For Removal In Release: v20.03.0**
|
||||
|
||||
With no development being done on this feature, and no active use of the file
|
||||
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.
|
||||
|
||||
Now that support for `overlay2` is added to all supported distros (as they are
|
||||
either on kernel 4.x, or have support for multiple lowerdirs backported), there
|
||||
is no reason to continue maintenance of the `devicemapper` storage driver.
|
||||
|
||||
### AuFS storage driver
|
||||
|
||||
@@ -81,6 +144,39 @@ is available to all supported distros (as they are either on kernel 4.x, or have
|
||||
support for multiple lowerdirs backported), there is no reason to continue
|
||||
maintenance of the `aufs` storage driver.
|
||||
|
||||
|
||||
### Legacy "overlay" storage driver
|
||||
|
||||
**Deprecated in Release: v18.09.0**
|
||||
|
||||
The `overlay` storage driver is deprecated in favor of the `overlay2` storage
|
||||
driver, which has all the benefits of `overlay`, without its limitations (excessive
|
||||
inode consumption). The legacy `overlay` storage driver will be removed in a future
|
||||
release. Users of the `overlay` storage driver should migrate to the `overlay2`
|
||||
storage driver.
|
||||
|
||||
The legacy `overlay` storage driver allowed using overlayFS-backed filesystems
|
||||
on pre 4.x kernels. Now that all supported distributions are able to run `overlay2`
|
||||
(as they are either on kernel 4.x, or have support for multiple lowerdirs
|
||||
backported), there is no reason to keep maintaining the `overlay` storage driver.
|
||||
|
||||
### Device mapper storage driver
|
||||
|
||||
**Deprecated in Release: v18.09.0**
|
||||
|
||||
The `devicemapper` storage driver is deprecated in favor of `overlay2`, and will
|
||||
be removed in a future release. Users of the `devicemapper` storage driver are
|
||||
recommended to migrate to a different storage driver, such as `overlay2`, which
|
||||
is now the default storage driver.
|
||||
|
||||
The `devicemapper` storage driver facilitates running Docker on older (3.x) kernels
|
||||
that have no support for other storage drivers (such as overlay2, or AUFS).
|
||||
|
||||
Now that support for `overlay2` is added to all supported distros (as they are
|
||||
either on kernel 4.x, or have support for multiple lowerdirs backported), there
|
||||
is no reason to continue maintenance of the `devicemapper` storage driver.
|
||||
|
||||
|
||||
### Reserved namespaces in engine labels
|
||||
|
||||
**Deprecated in Release: v18.06.0**
|
||||
@@ -89,9 +185,41 @@ The namespaces `com.docker.*`, `io.docker.*`, and `org.dockerproject.*` in engin
|
||||
were always documented to be reserved, but there was never any enforcement.
|
||||
|
||||
Usage of these namespaces will now cause a warning in the engine logs to discourage their
|
||||
use, and will error instead in 18.12 and above.
|
||||
use, and will error instead in v20.03.0 and above.
|
||||
|
||||
### Asynchronous `service create` and `service update`
|
||||
|
||||
### `--disable-legacy-registry` override daemon option
|
||||
|
||||
**Disabled In Release: v17.12**
|
||||
|
||||
**Removed In Release: v19.03**
|
||||
|
||||
The `--disable-legacy-registry` flag was disabled in Docker 17.12 and will print
|
||||
an error when used. For this error to be printed, the flag itself is still present,
|
||||
but hidden. The flag has been removed in Docker 19.03.
|
||||
|
||||
|
||||
### Interacting with V1 registries
|
||||
|
||||
**Disabled By Default In Release: v17.06**
|
||||
|
||||
**Removed In Release: v17.12**
|
||||
|
||||
Version 1.8.3 added a flag (`--disable-legacy-registry=false`) which prevents the
|
||||
docker daemon from `pull`, `push`, and `login` operations against v1
|
||||
registries. Though enabled by default, this signals the intent to deprecate
|
||||
the v1 protocol.
|
||||
|
||||
Support for the v1 protocol to the public registry was removed in 1.13. Any
|
||||
mirror configurations using v1 should be updated to use a
|
||||
[v2 registry mirror](https://docs.docker.com/registry/recipes/mirror/).
|
||||
|
||||
Starting with Docker 17.12, support for V1 registries has been removed, and the
|
||||
`--disable-legacy-registry` flag can no longer be used, and `dockerd` will fail to
|
||||
start when set.
|
||||
|
||||
|
||||
### Asynchronous `service create` and `service update` as default
|
||||
|
||||
**Deprecated In Release: v17.05.0**
|
||||
|
||||
@@ -199,12 +327,16 @@ to 17.12, a warning will be printed.
|
||||
Please also refer to [#27358](https://github.com/docker/docker/issues/27358) for
|
||||
further information.
|
||||
|
||||
### Three arguments form in `docker import`
|
||||
**Deprecated In Release: [v0.6.7](https://github.com/docker/docker/releases/tag/v0.6.7)**
|
||||
|
||||
**Removed In Release: [v1.12.0](https://github.com/docker/docker/releases/tag/v1.12.0)**
|
||||
### `--automated` and `--stars` flags on `docker search`
|
||||
|
||||
**Deprecated in Release: [v1.12.0](https://github.com/docker/docker/releases/tag/v1.12.0)**
|
||||
|
||||
**Target For Removal In Release: v17.09**
|
||||
|
||||
The `docker search --automated` and `docker search --stars` options are deprecated.
|
||||
Use `docker search --filter=is-automated=...` and `docker search --filter=stars=...` instead.
|
||||
|
||||
The `docker import` command format `file|URL|- [REPOSITORY [TAG]]` is deprecated since November 2013. It's no more supported.
|
||||
|
||||
### `-h` shorthand for `--help`
|
||||
|
||||
@@ -231,14 +363,6 @@ The docker login command is removing the ability to automatically register for a
|
||||
|
||||
The flag `--security-opt` doesn't use the colon separator (`:`) anymore to divide keys and values, it uses the equal symbol (`=`) for consistency with other similar flags, like `--storage-opt`.
|
||||
|
||||
### `/containers/(id or name)/copy` endpoint
|
||||
|
||||
**Deprecated In Release: [v1.8.0](https://github.com/docker/docker/releases/tag/v1.8.0)**
|
||||
|
||||
**Removed In Release: [v1.12.0](https://github.com/docker/docker/releases/tag/v1.12.0)**
|
||||
|
||||
The endpoint `/containers/(id or name)/copy` is deprecated in favor of `/containers/(id or name)/archive`.
|
||||
|
||||
### Ambiguous event fields in API
|
||||
**Deprecated In Release: [v1.10.0](https://github.com/docker/docker/releases/tag/v1.10.0)**
|
||||
|
||||
@@ -269,16 +393,8 @@ defining it at container creation (`POST /containers/create`).
|
||||
The `docker ps --before` and `docker ps --since` options are deprecated.
|
||||
Use `docker ps --filter=before=...` and `docker ps --filter=since=...` instead.
|
||||
|
||||
### `--automated` and `--stars` flags on `docker search`
|
||||
|
||||
**Deprecated in Release: [v1.12.0](https://github.com/docker/docker/releases/tag/v1.12.0)**
|
||||
|
||||
**Target For Removal In Release: v17.09**
|
||||
|
||||
The `docker search --automated` and `docker search --stars` options are deprecated.
|
||||
Use `docker search --filter=is-automated=...` and `docker search --filter=stars=...` instead.
|
||||
|
||||
### Driver Specific Log Tags
|
||||
### Driver-specific log tags
|
||||
**Deprecated In Release: [v1.9.0](https://github.com/docker/docker/releases/tag/v1.9.0)**
|
||||
|
||||
**Removed In Release: [v1.12.0](https://github.com/docker/docker/releases/tag/v1.12.0)**
|
||||
@@ -293,6 +409,27 @@ docker --log-driver=syslog --log-opt tag="{{.ImageName}}/{{.Name}}/{{.ID}}"
|
||||
{% endraw %}
|
||||
```
|
||||
|
||||
|
||||
### Docker Content Trust ENV passphrase variables name change
|
||||
**Deprecated In Release: [v1.9.0](https://github.com/docker/docker/releases/tag/v1.9.0)**
|
||||
|
||||
**Removed In Release: [v1.12.0](https://github.com/docker/docker/releases/tag/v1.12.0)**
|
||||
|
||||
Since 1.9, Docker Content Trust Offline key has been renamed to Root key and the Tagging key has been renamed to Repository key. Due to this renaming, we're also changing the corresponding environment variables
|
||||
|
||||
- DOCKER_CONTENT_TRUST_OFFLINE_PASSPHRASE is now named DOCKER_CONTENT_TRUST_ROOT_PASSPHRASE
|
||||
- DOCKER_CONTENT_TRUST_TAGGING_PASSPHRASE is now named DOCKER_CONTENT_TRUST_REPOSITORY_PASSPHRASE
|
||||
|
||||
|
||||
### `/containers/(id or name)/copy` endpoint
|
||||
|
||||
**Deprecated In Release: [v1.8.0](https://github.com/docker/docker/releases/tag/v1.8.0)**
|
||||
|
||||
**Removed In Release: [v1.12.0](https://github.com/docker/docker/releases/tag/v1.12.0)**
|
||||
|
||||
The endpoint `/containers/(id or name)/copy` is deprecated in favor of `/containers/(id or name)/archive`.
|
||||
|
||||
|
||||
### LXC built-in exec driver
|
||||
**Deprecated In Release: [v1.8.0](https://github.com/docker/docker/releases/tag/v1.8.0)**
|
||||
|
||||
@@ -300,6 +437,7 @@ docker --log-driver=syslog --log-opt tag="{{.ImageName}}/{{.Name}}/{{.ID}}"
|
||||
|
||||
The built-in LXC execution driver, the lxc-conf flag, and API fields have been removed.
|
||||
|
||||
|
||||
### Old Command Line Options
|
||||
**Deprecated In Release: [v1.8.0](https://github.com/docker/docker/releases/tag/v1.8.0)**
|
||||
|
||||
@@ -358,6 +496,16 @@ The single-dash (`-help`) was removed, in favor of the double-dash `--help`
|
||||
docker -help
|
||||
docker [COMMAND] -help
|
||||
|
||||
|
||||
### `--api-enable-cors` flag on dockerd
|
||||
|
||||
**Deprecated In Release: [v1.6.0](https://github.com/docker/docker/releases/tag/v1.6.0)**
|
||||
|
||||
**Removed In Release: [v17.09](https://github.com/docker/docker-ce/releases/tag/v17.09.0-ce)**
|
||||
|
||||
The flag `--api-enable-cors` is deprecated since v1.6.0. Use the flag
|
||||
`--api-cors-header` instead.
|
||||
|
||||
### `--run` flag on docker commit
|
||||
|
||||
**Deprecated In Release: [v0.10.0](https://github.com/docker/docker/releases/tag/v0.10.0)**
|
||||
@@ -368,51 +516,10 @@ The flag `--run` of the docker commit (and its short version `-run`) were deprec
|
||||
of the `--changes` flag that allows to pass `Dockerfile` commands.
|
||||
|
||||
|
||||
### Interacting with V1 registries
|
||||
|
||||
**Disabled By Default In Release: v17.06**
|
||||
|
||||
**Removed In Release: v17.12**
|
||||
|
||||
Version 1.8.3 added a flag (`--disable-legacy-registry=false`) which prevents the
|
||||
docker daemon from `pull`, `push`, and `login` operations against v1
|
||||
registries. Though enabled by default, this signals the intent to deprecate
|
||||
the v1 protocol.
|
||||
|
||||
Support for the v1 protocol to the public registry was removed in 1.13. Any
|
||||
mirror configurations using v1 should be updated to use a
|
||||
[v2 registry mirror](https://docs.docker.com/registry/recipes/mirror/).
|
||||
|
||||
Starting with Docker 17.12, support for V1 registries has been removed, and the
|
||||
`--disable-legacy-registry` flag can no longer be used, and `dockerd` will fail to
|
||||
start when set.
|
||||
|
||||
### `--disable-legacy-registry` override daemon option
|
||||
|
||||
**Disabled In Release: v17.12**
|
||||
|
||||
**Target For Removal In Release: v18.03**
|
||||
|
||||
The `--disable-legacy-registry` flag was disabled in Docker 17.12 and will print
|
||||
an error when used. For this error to be printed, the flag itself is still present,
|
||||
but hidden. The flag will be removed in Docker 18.03.
|
||||
|
||||
|
||||
### Docker Content Trust ENV passphrase variables name change
|
||||
**Deprecated In Release: [v1.9.0](https://github.com/docker/docker/releases/tag/v1.9.0)**
|
||||
### Three arguments form in `docker import`
|
||||
**Deprecated In Release: [v0.6.7](https://github.com/docker/docker/releases/tag/v0.6.7)**
|
||||
|
||||
**Removed In Release: [v1.12.0](https://github.com/docker/docker/releases/tag/v1.12.0)**
|
||||
|
||||
Since 1.9, Docker Content Trust Offline key has been renamed to Root key and the Tagging key has been renamed to Repository key. Due to this renaming, we're also changing the corresponding environment variables
|
||||
The `docker import` command format `file|URL|- [REPOSITORY [TAG]]` is deprecated since November 2013. It's no more supported.
|
||||
|
||||
- DOCKER_CONTENT_TRUST_OFFLINE_PASSPHRASE is now named DOCKER_CONTENT_TRUST_ROOT_PASSPHRASE
|
||||
- DOCKER_CONTENT_TRUST_TAGGING_PASSPHRASE is now named DOCKER_CONTENT_TRUST_REPOSITORY_PASSPHRASE
|
||||
|
||||
### `--api-enable-cors` flag on dockerd
|
||||
|
||||
**Deprecated In Release: [v1.6.0](https://github.com/docker/docker/releases/tag/v1.6.0)**
|
||||
|
||||
**Removed In Release: [v17.09](https://github.com/docker/docker-ce/releases/tag/v17.09.0-ce)**
|
||||
|
||||
The flag `--api-enable-cors` is deprecated since v1.6.0. Use the flag
|
||||
`--api-cors-header` instead.
|
||||
|
||||
+34
-29
@@ -28,7 +28,7 @@ Practices](https://docs.docker.com/engine/userguide/eng-image/dockerfile_best-pr
|
||||
|
||||
## Usage
|
||||
|
||||
The [`docker build`](commandline/build.md) command builds an image from
|
||||
The [docker build](commandline/build.md) command builds an image from
|
||||
a `Dockerfile` and a *context*. The build's context is the set of files at a
|
||||
specified location `PATH` or `URL`. The `PATH` is a directory on your local
|
||||
filesystem. The `URL` is a Git repository location.
|
||||
@@ -93,8 +93,7 @@ instructions.
|
||||
Whenever possible, Docker will re-use the intermediate images (cache),
|
||||
to accelerate the `docker build` process significantly. This is indicated by
|
||||
the `Using cache` message in the console output.
|
||||
(For more information, see the [Build cache section](https://docs.docker.com/engine/userguide/eng-image/dockerfile_best-practices/#build-cache) in the
|
||||
`Dockerfile` best practices guide):
|
||||
(For more information, see the [`Dockerfile` best practices guide](https://docs.docker.com/engine/userguide/eng-image/dockerfile_best-practices/):
|
||||
|
||||
$ docker build -t svendowideit/ambassador .
|
||||
Sending build context to Docker daemon 15.36 kB
|
||||
@@ -137,7 +136,7 @@ implementation. For example, BuildKit can:
|
||||
* Avoid side-effects with rest of the API (intermediate images and containers)
|
||||
* Prioritize your build cache for automatic pruning
|
||||
|
||||
To use the BuildKit backend, you need to set an environment variable
|
||||
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
|
||||
@@ -157,10 +156,12 @@ be UPPERCASE to distinguish them from arguments more easily.
|
||||
|
||||
|
||||
Docker runs instructions in a `Dockerfile` in order. A `Dockerfile` **must
|
||||
start with a \`FROM\` instruction**. The `FROM` instruction specifies the [*Base
|
||||
Image*](glossary.md#base-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`.
|
||||
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*](glossary.md#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`.
|
||||
|
||||
Docker treats lines that *begin* with `#` as a comment, unless the line is
|
||||
a valid [parser directive](#parser-directives). A `#` marker anywhere
|
||||
@@ -267,7 +268,7 @@ This feature is only enabled if the [BuildKit](#buildkit) backend is used.
|
||||
|
||||
The syntax directive defines the location of the Dockerfile builder that is used for
|
||||
building the current Dockerfile. The BuildKit backend allows to seamlessly use
|
||||
external implementations of builders that are distributed as Docker images and
|
||||
external implementations of builders that are distributed as Docker images and
|
||||
execute inside a container sandbox environment.
|
||||
|
||||
Custom Dockerfile implementation allows you to:
|
||||
@@ -278,9 +279,9 @@ Custom Dockerfile implementation allows you to:
|
||||
|
||||
### Official releases
|
||||
|
||||
Docker distributes official versions of the images that can be used for building
|
||||
Dockerfiles under `docker/dockerfile` repository on Docker Hub. There are two
|
||||
channels where new images are released: stable and experimental.
|
||||
Docker distributes official versions of the images that can be used for building
|
||||
Dockerfiles under `docker/dockerfile` repository on Docker Hub. There are two
|
||||
channels where new images are released: stable and experimental.
|
||||
|
||||
Stable channel follows semantic versioning. For example:
|
||||
|
||||
@@ -296,9 +297,9 @@ component from the stable channel on the time of the release. For example:
|
||||
- docker/dockerfile:1.0-experimental - latest experimental releases after 1.0
|
||||
- docker/dockerfile:experimental - latest release on experimental channel
|
||||
|
||||
You should choose a channel that best fits your needs. If you only want
|
||||
You should choose a channel that best fits your needs. If you only want
|
||||
bugfixes, you should use `docker/dockerfile:1.0`. If you want to benefit from
|
||||
experimental features, you should use the experimental channel. If you are using
|
||||
experimental features, you should use the experimental channel. If you are using
|
||||
the experimental channel, newer releases may not be backwards compatible, so it
|
||||
is recommended to use an immutable full version variant.
|
||||
|
||||
@@ -558,39 +559,44 @@ the first pattern, followed by one or more `!` exception patterns.
|
||||
|
||||
## FROM
|
||||
|
||||
FROM <image> [AS <name>]
|
||||
FROM [--platform=<platform>] <image> [AS <name>]
|
||||
|
||||
Or
|
||||
|
||||
FROM <image>[:<tag>] [AS <name>]
|
||||
FROM [--platform=<platform>] <image>[:<tag>] [AS <name>]
|
||||
|
||||
Or
|
||||
|
||||
FROM <image>[@<digest>] [AS <name>]
|
||||
FROM [--platform=<platform>] <image>[@<digest>] [AS <name>]
|
||||
|
||||
The `FROM` instruction initializes a new build stage and sets the
|
||||
[*Base Image*](glossary.md#base-image) for subsequent instructions. As such, a
|
||||
[*Base Image*](../../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/engine/tutorials/dockerrepos/).
|
||||
|
||||
- `ARG` is the only instruction that may precede `FROM` in the `Dockerfile`.
|
||||
See [Understand how ARG and FROM interact](#understand-how-arg-and-from-interact).
|
||||
|
||||
- `FROM` can appear multiple times within a single `Dockerfile` to
|
||||
create multiple images or use one build stage as a dependency for another.
|
||||
Simply make a note of the last image ID output by the commit before each new
|
||||
`FROM` instruction. Each `FROM` instruction clears any state created by previous
|
||||
instructions.
|
||||
|
||||
- Optionally a name can be given to a new build stage by adding `AS name` to the
|
||||
`FROM` instruction. The name can be used in subsequent `FROM` and
|
||||
`COPY --from=<name|index>` instructions to refer to the image built in this stage.
|
||||
|
||||
- The `tag` or `digest` values are optional. If you omit either of them, the
|
||||
builder assumes a `latest` tag by default. The builder returns an error if it
|
||||
cannot find the `tag` value.
|
||||
|
||||
The optional `--platform` flag can be used to specify the platform of the image
|
||||
in case `FROM` references a multi-platform image. For example, `linux/amd64`,
|
||||
`linux/arm64`, or `windows/amd64`. By default, the target platform of the build
|
||||
request is used. Global build arguments can be used in the value of this flag,
|
||||
for example [automatic platform ARGs](#automatic-platform-args-in-the-global-scope)
|
||||
allow you to force a stage to native build platform (`--platform=$BUILDPLATFORM`),
|
||||
and use it to cross-compile to the target platform inside the stage.
|
||||
|
||||
### Understand how ARG and FROM interact
|
||||
|
||||
`FROM` instructions support variables that are declared by any `ARG`
|
||||
@@ -687,7 +693,7 @@ cache for `RUN` instructions can be invalidated by using the `--no-cache`
|
||||
flag, for example `docker build --no-cache`.
|
||||
|
||||
See the [`Dockerfile` Best Practices
|
||||
guide](https://docs.docker.com/engine/userguide/eng-image/dockerfile_best-practices/#/build-cache) for more information.
|
||||
guide](https://docs.docker.com/engine/userguide/eng-image/dockerfile_best-practices/) for more information.
|
||||
|
||||
The cache for `RUN` instructions can be invalidated by `ADD` instructions. See
|
||||
[below](#add) for details.
|
||||
@@ -1008,7 +1014,7 @@ of whether or not the file has changed and the cache should be updated.
|
||||
> following instructions from the Dockerfile if the contents of `<src>` have
|
||||
> changed. This includes invalidating the cache for `RUN` instructions.
|
||||
> See the [`Dockerfile` Best Practices
|
||||
guide](https://docs.docker.com/engine/userguide/eng-image/dockerfile_best-practices/#/build-cache) for more information.
|
||||
guide](https://docs.docker.com/engine/userguide/eng-image/dockerfile_best-practices/) for more information.
|
||||
|
||||
|
||||
`ADD` obeys the following rules:
|
||||
@@ -1429,7 +1435,7 @@ containers. The value can be a JSON array, `VOLUME ["/var/log/"]`, or a plain
|
||||
string with multiple arguments, such as `VOLUME /var/log` or `VOLUME /var/log
|
||||
/var/db`. For more information/examples and mounting instructions via the
|
||||
Docker client, refer to
|
||||
[*Share Directories via Volumes*](https://docs.docker.com/engine/tutorials/dockervolumes/#/mount-a-host-directory-as-a-data-volume)
|
||||
[*Share Directories via Volumes*](https://docs.docker.com/engine/tutorials/dockervolumes/)
|
||||
documentation.
|
||||
|
||||
The `docker run` command initializes the newly created volume with any data
|
||||
@@ -1720,8 +1726,8 @@ The following `ARG` variables are set automatically:
|
||||
* `TARGETVARIANT` - variant component of TARGETPLATFORM
|
||||
* `BUILDPLATFORM` - platform of the node performing the build.
|
||||
* `BUILDOS` - OS component of BUILDPLATFORM
|
||||
* `BUILDARCH` - OS component of BUILDPLATFORM
|
||||
* `BUILDVARIANT` - OS component of BUILDPLATFORM
|
||||
* `BUILDARCH` - architecture component of BUILDPLATFORM
|
||||
* `BUILDVARIANT` - variant component of BUILDPLATFORM
|
||||
|
||||
These arguments are defined in the global scope so are not automatically
|
||||
available inside build stages or for your `RUN` commands. To expose one of
|
||||
@@ -2048,13 +2054,12 @@ The `SHELL` feature was added in Docker 1.12.
|
||||
This feature is only available when using the [BuildKit](#buildkit) backend.
|
||||
|
||||
Docker build supports experimental features like cache mounts, build secrets and
|
||||
ssh forwarding that are enabled by using an external implementation of the
|
||||
ssh forwarding that are enabled by using an external implementation of the
|
||||
builder with a syntax directive. To learn about these features, [refer to the documentation in BuildKit repository](https://github.com/moby/buildkit/blob/master/frontend/dockerfile/docs/experimental.md).
|
||||
|
||||
## Dockerfile examples
|
||||
|
||||
Below you can see some examples of Dockerfile syntax. If you're interested in
|
||||
something more realistic, take a look at the list of [Dockerization examples](https://docs.docker.com/engine/examples/).
|
||||
Below you can see some examples of Dockerfile syntax.
|
||||
|
||||
```
|
||||
# Nginx
|
||||
|
||||
@@ -47,6 +47,7 @@ Options:
|
||||
'host': use the Docker host network stack
|
||||
'<network-name>|<network-id>': connect to a user-defined network
|
||||
--no-cache Do not use cache when building the image
|
||||
-o, --output Output destination (format: type=local,dest=path)
|
||||
--pull Always attempt to pull a newer version of the image
|
||||
--progress Set type of progress output (only if BuildKit enabled) (auto, plain, tty).
|
||||
Use plain to show container output
|
||||
@@ -323,7 +324,16 @@ Successfully built 99cc1ad10469
|
||||
This example shows the use of the `.dockerignore` file to exclude the `.git`
|
||||
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)
|
||||
[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.
|
||||
|
||||
|
||||
### Tag an image (-t)
|
||||
|
||||
@@ -489,6 +499,137 @@ FROM alpine AS production-env
|
||||
$ docker build -t mybuildimage --target build-env .
|
||||
```
|
||||
|
||||
### Custom build outputs
|
||||
|
||||
By default, a local container image is created from the build result. The
|
||||
`--output` (or `-o`) flag allows you to override this behavior, and a specify a
|
||||
custom exporter. For example, custom exporters allow you to export the build
|
||||
artifacts as files on the local filesystem instead of a Docker image, which can
|
||||
be useful for generating local binaries, code generation etc.
|
||||
|
||||
The value for `--output` is a CSV-formatted string defining the exporter type
|
||||
and options. Currently, `local` and `tar` exporters are supported. The `local`
|
||||
exporter writes the resulting build files to a directory on the client side. The
|
||||
`tar` exporter is similar but writes the files as a single tarball (`.tar`).
|
||||
|
||||
If no type is specified, the value defaults to the output directory of the local
|
||||
exporter. Use a hyphen (`-`) to write the output tarball to standard output
|
||||
(`STDOUT`).
|
||||
|
||||
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
|
||||
$ docker build -o out .
|
||||
```
|
||||
|
||||
The example above uses the short-hand syntax, omitting the `type` options, and
|
||||
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
|
||||
$ docker build --output type=local,dest=out .
|
||||
```
|
||||
|
||||
Use the `tar` type to export the files as a `.tar` archive:
|
||||
|
||||
```bash
|
||||
$ docker build --output type=tar,dest=out.tar .
|
||||
```
|
||||
|
||||
The example below shows the equivalent when using the short-hand syntax. In this
|
||||
case, `-` is specified as destination, which automatically selects the `tar` type,
|
||||
and writes the output tarball to standard output, which is then redirected to
|
||||
the `out.tar` file:
|
||||
|
||||
```bash
|
||||
docker build -o - . > out.tar
|
||||
```
|
||||
|
||||
The `--output` option exports all files from the target stage. A common pattern
|
||||
for exporting only specific files is to do multi-stage builds and to copy the
|
||||
desired files to a new scratch stage with [`COPY --from`](../builder.md#copy).
|
||||
|
||||
The example `Dockerfile` below uses a separate stage to collect the
|
||||
build-artifacts for exporting:
|
||||
|
||||
```Dockerfile
|
||||
FROM golang AS build-stage
|
||||
RUN go get -u github.com/LK4D4/vndr
|
||||
|
||||
FROM scratch AS export-stage
|
||||
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
|
||||
$ docker build -o out .
|
||||
|
||||
[+] Building 2.3s (7/7) FINISHED
|
||||
=> [internal] load build definition from Dockerfile 0.1s
|
||||
=> => transferring dockerfile: 176B 0.0s
|
||||
=> [internal] load .dockerignore 0.0s
|
||||
=> => transferring context: 2B 0.0s
|
||||
=> [internal] load metadata for docker.io/library/golang:latest 1.6s
|
||||
=> [build-stage 1/2] FROM docker.io/library/golang@sha256:2df96417dca0561bf1027742dcc5b446a18957cd28eba6aa79269f23f1846d3f 0.0s
|
||||
=> => resolve docker.io/library/golang@sha256:2df96417dca0561bf1027742dcc5b446a18957cd28eba6aa79269f23f1846d3f 0.0s
|
||||
=> CACHED [build-stage 2/2] RUN go get -u github.com/LK4D4/vndr 0.0s
|
||||
=> [export-stage 1/1] COPY --from=build-stage /go/bin/vndr / 0.2s
|
||||
=> exporting to client 0.4s
|
||||
=> => copying files 10.30MB 0.3s
|
||||
|
||||
$ ls ./out
|
||||
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.
|
||||
|
||||
### Specifying external cache sources
|
||||
|
||||
In addition to local build cache, the builder can reuse the cache generated from
|
||||
previous builds with the `--cache-from` flag pointing to an image in the registry.
|
||||
|
||||
To use an image as a cache source, cache metadata needs to be written into the
|
||||
image on creation. This can be done by setting `--build-arg BUILDKIT_INLINE_CACHE=1`
|
||||
when building the image. After that, the built image can be used as a cache source
|
||||
for subsequent builds.
|
||||
|
||||
Upon importing the cache, the builder will only pull the JSON metadata from the
|
||||
registry and determine possible cache hits based on that information. If there
|
||||
is a cache hit, the matched layers are pulled into the local environment.
|
||||
|
||||
In addition to images, the cache can also be pulled from special cache manifests
|
||||
generated by [`buildx`](https://github.com/docker/buildx) or the BuildKit CLI
|
||||
(`buildctl`). These manifests (when built with the `type=registry` and `mode=max`
|
||||
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
|
||||
$ docker build -t myname/myapp --build-arg BUILDKIT_INLINE_CACHE=1 .
|
||||
$ 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
|
||||
$ 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.
|
||||
|
||||
### Squash an image's layers (--squash) (experimental)
|
||||
|
||||
#### Overview
|
||||
|
||||
@@ -54,7 +54,7 @@ each `docker` command with `sudo`. To avoid having to use `sudo` with the
|
||||
For more information about installing Docker or `sudo` configuration, refer to
|
||||
the [installation](https://docs.docker.com/install/) instructions for your operating system.
|
||||
|
||||
### Environment variables
|
||||
## Environment variables
|
||||
|
||||
For easy reference, the following list of environment variables are supported
|
||||
by the `docker` command line:
|
||||
@@ -99,7 +99,7 @@ By default, the Docker command line stores its configuration files in a
|
||||
directory called `.docker` within your `$HOME` directory.
|
||||
|
||||
Docker manages most of the files in the configuration directory
|
||||
and you should not modify them. However, you *can modify* the
|
||||
and you should not modify them. However, you *can* modify the
|
||||
`config.json` file to control certain aspects of how the `docker`
|
||||
command behaves.
|
||||
|
||||
@@ -111,12 +111,12 @@ variable. Command line options override environment variables and environment
|
||||
variables override properties you specify in a `config.json` file.
|
||||
|
||||
|
||||
#### Change the `.docker` directory
|
||||
### Change the `.docker` directory
|
||||
|
||||
To specify a different directory, use the `DOCKER_CONFIG`
|
||||
environment variable or the `--config` command line option. If both are
|
||||
specified, then the `--config` option overrides the `DOCKER_CONFIG` environment
|
||||
variable. The example below overrides runs the `docker ps` command using a
|
||||
variable. The example below overrides the `docker ps` command using a
|
||||
`config.json` file located in the `~/testconfigs/` directory.
|
||||
|
||||
```bash
|
||||
@@ -132,7 +132,7 @@ directory to be `HOME/newdir/.docker`.
|
||||
echo export DOCKER_CONFIG=$HOME/newdir/.docker > ~/.profile
|
||||
```
|
||||
|
||||
#### `config.json` properties
|
||||
### `config.json` properties
|
||||
|
||||
The `config.json` file stores a JSON encoding of several properties:
|
||||
|
||||
@@ -221,6 +221,23 @@ running `docker stack` management commands. Valid values are `"swarm"`,
|
||||
`"kubernetes"`, and `"all"`. This property can be overridden with the
|
||||
`DOCKER_STACK_ORCHESTRATOR` environment variable, or the `--orchestrator` flag.
|
||||
|
||||
The property `proxies` specifies proxy environment variables to be automatically
|
||||
set on containers, and set as `--build-arg` on containers used during `docker build`.
|
||||
A `"default"` set of proxies can be configured, and will be used for any docker
|
||||
daemon that the client connects to, or a configuration per host (docker daemon),
|
||||
for example, "https://docker-daemon1.example.com". The following properties can
|
||||
be set for each environment:
|
||||
|
||||
* `httpProxy` (sets the value of `HTTP_PROXY` and `http_proxy`)
|
||||
* `httpsProxy` (sets the value of `HTTPS_PROXY` and `https_proxy`)
|
||||
* `ftpProxy` (sets the value of `FTP_PROXY` and `ftp_proxy`)
|
||||
* `noProxy` (sets the value of `NO_PROXY` and `no_proxy`)
|
||||
|
||||
> **Warning**: Proxy settings may contain sensitive information (for example,
|
||||
> if the proxy requires authentication). Environment variables are stored as
|
||||
> plain text in the container's configuration, and as such can be inspected
|
||||
> through the remote API or committed to an image when using `docker commit`.
|
||||
|
||||
Once attached to a container, users detach from it and leave it running using
|
||||
the using `CTRL-p CTRL-q` key sequence. This detach key sequence is customizable
|
||||
using the `detachKeys` property. Specify a `<sequence>` value for the
|
||||
@@ -275,6 +292,18 @@ Following is a sample `config.json` file:
|
||||
"anotheroption": "anothervalue",
|
||||
"athirdoption": "athirdvalue"
|
||||
}
|
||||
},
|
||||
"proxies": {
|
||||
"default": {
|
||||
"httpProxy": "http://user:pass@example.com:3128",
|
||||
"httpsProxy": "http://user:pass@example.com:3128",
|
||||
"noProxy": "http://user:pass@example.com:3128",
|
||||
"ftpProxy": "http://user:pass@example.com:3128"
|
||||
},
|
||||
"https://manager1.mycorp.example.com:2377": {
|
||||
"httpProxy": "http://user:pass@example.com:3128",
|
||||
"httpsProxy": "http://user:pass@example.com:3128"
|
||||
},
|
||||
}
|
||||
}
|
||||
{% endraw %}
|
||||
|
||||
@@ -159,12 +159,12 @@ a single overlay network. Each of the subnetworks has 126 usable addresses.
|
||||
|
||||
```bash
|
||||
$ docker network create -d overlay \
|
||||
--subnet=192.168.1.0/25 \
|
||||
--subnet=192.170.2.0/25 \
|
||||
--gateway=192.168.1.100 \
|
||||
--gateway=192.170.2.100 \
|
||||
--aux-address="my-router=192.168.1.5" --aux-address="my-switch=192.168.1.6" \
|
||||
--aux-address="my-printer=192.170.1.5" --aux-address="my-nas=192.170.1.6" \
|
||||
--subnet=192.168.10.0/25 \
|
||||
--subnet=192.168.20.0/25 \
|
||||
--gateway=192.168.10.100 \
|
||||
--gateway=192.168.20.100 \
|
||||
--aux-address="my-router=192.168.10.5" --aux-address="my-switch=192.168.10.6" \
|
||||
--aux-address="my-printer=192.168.20.5" --aux-address="my-nas=192.168.20.6" \
|
||||
my-multihost-network
|
||||
```
|
||||
|
||||
|
||||
@@ -27,9 +27,11 @@ Options:
|
||||
|
||||
## Description
|
||||
|
||||
Demotes an existing manager so that it is no longer a manager. This command
|
||||
targets a docker engine that is a manager in the swarm.
|
||||
Demotes an existing manager so that it is no longer a manager.
|
||||
|
||||
> **Note**: This is a cluster management command, and must be executed on a swarm
|
||||
> manager node. To learn about managers and workers, refer to the [Swarm mode
|
||||
> section](https://docs.docker.com/engine/swarm/) in the documentation.
|
||||
|
||||
## Examples
|
||||
|
||||
|
||||
@@ -34,6 +34,10 @@ given template for each result. Go's
|
||||
[text/template](http://golang.org/pkg/text/template/) package describes all the
|
||||
details of the format.
|
||||
|
||||
> **Note**: This is a cluster management command, and must be executed on a swarm
|
||||
> manager node. To learn about managers and workers, refer to the [Swarm mode
|
||||
> section](https://docs.docker.com/engine/swarm/) in the documentation.
|
||||
|
||||
## Examples
|
||||
|
||||
### Inspect a node
|
||||
|
||||
@@ -36,6 +36,10 @@ Lists all the nodes that the Docker Swarm manager knows about. You can filter
|
||||
using the `-f` or `--filter` flag. Refer to the [filtering](#filtering) section
|
||||
for more information about available filter options.
|
||||
|
||||
> **Note**: This is a cluster management command, and must be executed on a swarm
|
||||
> manager node. To learn about managers and workers, refer to the [Swarm mode
|
||||
> section](https://docs.docker.com/engine/swarm/) in the documentation.
|
||||
|
||||
## Examples
|
||||
|
||||
```bash
|
||||
|
||||
@@ -28,6 +28,10 @@ Options:
|
||||
|
||||
Promotes a node to manager. This command can only be executed on a manager node.
|
||||
|
||||
> **Note**: This is a cluster management command, and must be executed on a swarm
|
||||
> manager node. To learn about managers and workers, refer to the [Swarm mode
|
||||
> section](https://docs.docker.com/engine/swarm/) in the documentation.
|
||||
|
||||
## Examples
|
||||
|
||||
```bash
|
||||
|
||||
@@ -32,7 +32,13 @@ Options:
|
||||
|
||||
## Description
|
||||
|
||||
Lists all the tasks on a Node that Docker knows about. You can filter using the `-f` or `--filter` flag. Refer to the [filtering](#filtering) section for more information about available filter options.
|
||||
Lists all the tasks on a Node that Docker knows about. You can filter using the
|
||||
`-f` or `--filter` flag. Refer to the [filtering](#filtering) section for more
|
||||
information about available filter options.
|
||||
|
||||
> **Note**: This is a cluster management command, and must be executed on a swarm
|
||||
> manager node. To learn about managers and workers, refer to the [Swarm mode
|
||||
> section](https://docs.docker.com/engine/swarm/) in the documentation.
|
||||
|
||||
## Examples
|
||||
|
||||
|
||||
@@ -30,8 +30,11 @@ Options:
|
||||
|
||||
## Description
|
||||
|
||||
When run from a manager node, removes the specified nodes from a swarm.
|
||||
Removes the specified nodes from a swarm.
|
||||
|
||||
> **Note**: This is a cluster management command, and must be executed on a swarm
|
||||
> manager node. To learn about managers and workers, refer to the [Swarm mode
|
||||
> section](https://docs.docker.com/engine/swarm/) in the documentation.
|
||||
|
||||
## Examples
|
||||
|
||||
|
||||
@@ -32,6 +32,10 @@ Options:
|
||||
|
||||
Update metadata about a node, such as its availability, labels, or roles.
|
||||
|
||||
> **Note**: This is a cluster management command, and must be executed on a swarm
|
||||
> manager node. To learn about managers and workers, refer to the [Swarm mode
|
||||
> section](https://docs.docker.com/engine/swarm/) in the documentation.
|
||||
|
||||
## Examples
|
||||
|
||||
### Add label metadata to a node
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user