diff --git a/components/cli/circle.yml b/components/cli/circle.yml index 073d4cf69e..3b4d9259e9 100644 --- a/components/cli/circle.yml +++ b/components/cli/circle.yml @@ -16,9 +16,7 @@ jobs: - run: name: "Lint" command: | - dockerfile=dockerfiles/Dockerfile.lint - echo "COPY . ." >> $dockerfile - docker build -f $dockerfile --tag cli-linter:$CIRCLE_BUILD_NUM . + docker build -f dockerfiles/Dockerfile.lint --tag cli-linter:$CIRCLE_BUILD_NUM . docker run --rm cli-linter:$CIRCLE_BUILD_NUM cross: @@ -34,9 +32,7 @@ jobs: - run: name: "Cross" command: | - dockerfile=dockerfiles/Dockerfile.cross - echo "COPY . ." >> $dockerfile - docker build -f $dockerfile --tag cli-builder:$CIRCLE_BUILD_NUM . + docker build -f dockerfiles/Dockerfile.cross --tag cli-builder:$CIRCLE_BUILD_NUM . name=cross-$CIRCLE_BUILD_NUM-$CIRCLE_NODE_INDEX docker run \ -e CROSS_GROUP=$CIRCLE_NODE_INDEX \ @@ -60,9 +56,7 @@ jobs: - run: name: "Unit Test with Coverage" command: | - dockerfile=dockerfiles/Dockerfile.dev - echo "COPY . ." >> $dockerfile - docker build -f $dockerfile --tag cli-builder:$CIRCLE_BUILD_NUM . + docker build -f dockerfiles/Dockerfile.dev --tag cli-builder:$CIRCLE_BUILD_NUM . docker run --name \ test-$CIRCLE_BUILD_NUM cli-builder:$CIRCLE_BUILD_NUM \ make test-coverage @@ -89,10 +83,8 @@ jobs: - run: name: "Validate Vendor, Docs, and Code Generation" command: | - dockerfile=dockerfiles/Dockerfile.dev - echo "COPY . ." >> $dockerfile rm -f .dockerignore # include .git - docker build -f $dockerfile --tag cli-builder-with-git:$CIRCLE_BUILD_NUM . + docker build -f dockerfiles/Dockerfile.dev --tag cli-builder-with-git:$CIRCLE_BUILD_NUM . docker run --rm cli-builder-with-git:$CIRCLE_BUILD_NUM \ make ci-validate shellcheck: @@ -107,9 +99,7 @@ jobs: - run: name: "Run shellcheck" command: | - dockerfile=dockerfiles/Dockerfile.shellcheck - echo "COPY . ." >> $dockerfile - docker build -f $dockerfile --tag cli-validator:$CIRCLE_BUILD_NUM . + docker build -f dockerfiles/Dockerfile.shellcheck --tag cli-validator:$CIRCLE_BUILD_NUM . docker run --rm cli-validator:$CIRCLE_BUILD_NUM \ make shellcheck workflows: diff --git a/components/cli/cli/command/container/client_test.go b/components/cli/cli/command/container/client_test.go index a2c39bc6ff..b883ad1db1 100644 --- a/components/cli/cli/command/container/client_test.go +++ b/components/cli/cli/command/container/client_test.go @@ -12,19 +12,24 @@ import ( type fakeClient struct { client.Client - inspectFunc func(string) (types.ContainerJSON, error) - execInspectFunc func(execID string) (types.ContainerExecInspect, error) - execCreateFunc func(container string, config types.ExecConfig) (types.IDResponse, error) - createContainerFunc func(config *container.Config, hostConfig *container.HostConfig, networkingConfig *network.NetworkingConfig, containerName string) (container.ContainerCreateCreatedBody, error) - containerStartFunc func(container string, options types.ContainerStartOptions) error - imageCreateFunc func(parentReference string, options types.ImageCreateOptions) (io.ReadCloser, error) - infoFunc func() (types.Info, error) - containerStatPathFunc func(container, path string) (types.ContainerPathStat, error) - containerCopyFromFunc func(container, srcPath string) (io.ReadCloser, types.ContainerPathStat, error) - logFunc func(string, types.ContainerLogsOptions) (io.ReadCloser, error) - waitFunc func(string) (<-chan container.ContainerWaitOKBody, <-chan error) - containerListFunc func(types.ContainerListOptions) ([]types.Container, error) - Version string + inspectFunc func(string) (types.ContainerJSON, error) + execInspectFunc func(execID string) (types.ContainerExecInspect, error) + execCreateFunc func(container string, config types.ExecConfig) (types.IDResponse, error) + createContainerFunc func(config *container.Config, + hostConfig *container.HostConfig, + networkingConfig *network.NetworkingConfig, + containerName string) (container.ContainerCreateCreatedBody, error) + containerStartFunc func(container string, options types.ContainerStartOptions) error + imageCreateFunc func(parentReference string, options types.ImageCreateOptions) (io.ReadCloser, error) + infoFunc func() (types.Info, error) + containerStatPathFunc func(container, path string) (types.ContainerPathStat, error) + containerCopyFromFunc func(container, srcPath string) (io.ReadCloser, types.ContainerPathStat, error) + logFunc func(string, types.ContainerLogsOptions) (io.ReadCloser, error) + waitFunc func(string) (<-chan container.ContainerWaitOKBody, <-chan error) + containerListFunc func(types.ContainerListOptions) ([]types.Container, error) + containerExportFunc func(string) (io.ReadCloser, error) + containerExecResizeFunc func(id string, options types.ResizeOptions) error + Version string } func (f *fakeClient) ContainerList(_ context.Context, options types.ContainerListOptions) ([]types.Container, error) { @@ -124,3 +129,17 @@ func (f *fakeClient) ContainerStart(_ context.Context, container string, options } return nil } + +func (f *fakeClient) ContainerExport(_ context.Context, container string) (io.ReadCloser, error) { + if f.containerExportFunc != nil { + return f.containerExportFunc(container) + } + return nil, nil +} + +func (f *fakeClient) ContainerExecResize(_ context.Context, id string, options types.ResizeOptions) error { + if f.containerExecResizeFunc != nil { + return f.containerExecResizeFunc(id, options) + } + return nil +} diff --git a/components/cli/cli/command/container/export_test.go b/components/cli/cli/command/container/export_test.go new file mode 100644 index 0000000000..b961ec7630 --- /dev/null +++ b/components/cli/cli/command/container/export_test.go @@ -0,0 +1,33 @@ +package container + +import ( + "io" + "io/ioutil" + "strings" + "testing" + + "github.com/docker/cli/internal/test" + "gotest.tools/assert" + "gotest.tools/fs" +) + +func TestContainerExportOutputToFile(t *testing.T) { + dir := fs.NewDir(t, "export-test") + defer dir.Remove() + + cli := test.NewFakeCli(&fakeClient{ + containerExportFunc: func(container string) (io.ReadCloser, error) { + return ioutil.NopCloser(strings.NewReader("bar")), nil + }, + }) + cmd := NewExportCommand(cli) + cmd.SetOutput(ioutil.Discard) + cmd.SetArgs([]string{"-o", dir.Join("foo"), "container"}) + assert.NilError(t, cmd.Execute()) + + expected := fs.Expected(t, + fs.WithFile("foo", "bar", fs.MatchAnyFileMode), + ) + + assert.Assert(t, fs.Equal(dir.Path(), expected)) +} diff --git a/components/cli/cli/command/container/tty.go b/components/cli/cli/command/container/tty.go index cb49ded8ef..b7003f1a04 100644 --- a/components/cli/cli/command/container/tty.go +++ b/components/cli/cli/command/container/tty.go @@ -16,9 +16,9 @@ import ( ) // resizeTtyTo resizes tty to specific height and width -func resizeTtyTo(ctx context.Context, client client.ContainerAPIClient, id string, height, width uint, isExec bool) { +func resizeTtyTo(ctx context.Context, client client.ContainerAPIClient, id string, height, width uint, isExec bool) error { if height == 0 && width == 0 { - return + return nil } options := types.ResizeOptions{ @@ -34,19 +34,42 @@ func resizeTtyTo(ctx context.Context, client client.ContainerAPIClient, id strin } if err != nil { - logrus.Debugf("Error resize: %s", err) + logrus.Debugf("Error resize: %s\r", err) + } + return err +} + +// resizeTty is to resize the tty with cli out's tty size +func resizeTty(ctx context.Context, cli command.Cli, id string, isExec bool) error { + height, width := cli.Out().GetTtySize() + return resizeTtyTo(ctx, cli.Client(), id, height, width, isExec) +} + +// initTtySize is to init the tty's size to the same as the window, if there is an error, it will retry 5 times. +func initTtySize(ctx context.Context, cli command.Cli, id string, isExec bool, resizeTtyFunc func(ctx context.Context, cli command.Cli, id string, isExec bool) error) { + rttyFunc := resizeTtyFunc + if rttyFunc == nil { + rttyFunc = resizeTty + } + if err := rttyFunc(ctx, cli, id, isExec); err != nil { + go func() { + var err error + for retry := 0; retry < 5; retry++ { + time.Sleep(10 * time.Millisecond) + if err = rttyFunc(ctx, cli, id, isExec); err == nil { + break + } + } + if err != nil { + fmt.Fprintln(cli.Err(), "failed to resize tty, using default size") + } + }() } } // MonitorTtySize updates the container tty size when the terminal tty changes size func MonitorTtySize(ctx context.Context, cli command.Cli, id string, isExec bool) error { - resizeTty := func() { - height, width := cli.Out().GetTtySize() - resizeTtyTo(ctx, cli.Client(), id, height, width, isExec) - } - - resizeTty() - + initTtySize(ctx, cli, id, isExec, resizeTty) if runtime.GOOS == "windows" { go func() { prevH, prevW := cli.Out().GetTtySize() @@ -55,7 +78,7 @@ func MonitorTtySize(ctx context.Context, cli command.Cli, id string, isExec bool h, w := cli.Out().GetTtySize() if prevW != w || prevH != h { - resizeTty() + resizeTty(ctx, cli, id, isExec) } prevH = h prevW = w @@ -66,7 +89,7 @@ func MonitorTtySize(ctx context.Context, cli command.Cli, id string, isExec bool gosignal.Notify(sigchan, signal.SIGWINCH) go func() { for range sigchan { - resizeTty() + resizeTty(ctx, cli, id, isExec) } }() } diff --git a/components/cli/cli/command/container/tty_test.go b/components/cli/cli/command/container/tty_test.go new file mode 100644 index 0000000000..eb2e0bfaab --- /dev/null +++ b/components/cli/cli/command/container/tty_test.go @@ -0,0 +1,30 @@ +package container + +import ( + "context" + "testing" + "time" + + "github.com/docker/cli/cli/command" + "github.com/docker/cli/internal/test" + "github.com/docker/docker/api/types" + "github.com/pkg/errors" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" +) + +func TestInitTtySizeErrors(t *testing.T) { + expectedError := "failed to resize tty, using default size\n" + fakeContainerExecResizeFunc := func(id string, options types.ResizeOptions) error { + return errors.Errorf("Error response from daemon: no such exec") + } + fakeResizeTtyFunc := func(ctx context.Context, cli command.Cli, id string, isExec bool) error { + height, width := uint(1024), uint(768) + return resizeTtyTo(ctx, cli.Client(), id, height, width, isExec) + } + ctx := context.Background() + cli := test.NewFakeCli(&fakeClient{containerExecResizeFunc: fakeContainerExecResizeFunc}) + initTtySize(ctx, cli, "8mm8nn8tt8bb", true, fakeResizeTtyFunc) + time.Sleep(100 * time.Millisecond) + assert.Check(t, is.Equal(expectedError, cli.ErrBuffer().String())) +} diff --git a/components/cli/contrib/completion/bash/docker b/components/cli/contrib/completion/bash/docker index 192cdbf526..92d5740861 100644 --- a/components/cli/contrib/completion/bash/docker +++ b/components/cli/contrib/completion/bash/docker @@ -1,12 +1,14 @@ #!/usr/bin/env bash -# shellcheck disable=SC2016,SC2119,SC2155 +# shellcheck disable=SC2016,SC2119,SC2155,SC2206,SC2207 # # Shellcheck ignore list: # - SC2016: Expressions don't expand in single quotes, use double quotes for that. # - SC2119: Use foo "$@" if function's $1 should mean script's $1. # - SC2155: Declare and assign separately to avoid masking return values. -# -# You can find more details for each warning at the following page: +# - SC2206: Quote to prevent word splitting, or split robustly with mapfile or read -a. +# - SC2207: Prefer mapfile or read -a to split command output (or quote to avoid splitting). +# +# You can find more details for each warning at the following page: # https://github.com/koalaman/shellcheck/wiki/ # # bash completion file for core docker commands diff --git a/components/cli/docker.Makefile b/components/cli/docker.Makefile index 2ff2594a7f..628e989fb0 100644 --- a/components/cli/docker.Makefile +++ b/components/cli/docker.Makefile @@ -17,24 +17,29 @@ ENVVARS = -e VERSION=$(VERSION) -e GITCOMMIT -e PLATFORM # build docker image (dockerfiles/Dockerfile.build) .PHONY: build_docker_image build_docker_image: - docker build ${DOCKER_BUILD_ARGS} -t $(DEV_DOCKER_IMAGE_NAME) -f ./dockerfiles/Dockerfile.dev . + # build dockerfile from stdin so that we don't send the build-context; source is bind-mounted in the development environment + cat ./dockerfiles/Dockerfile.dev | docker build ${DOCKER_BUILD_ARGS} -t $(DEV_DOCKER_IMAGE_NAME) - # build docker image having the linting tools (dockerfiles/Dockerfile.lint) .PHONY: build_linter_image build_linter_image: - docker build ${DOCKER_BUILD_ARGS} -t $(LINTER_IMAGE_NAME) -f ./dockerfiles/Dockerfile.lint . + # build dockerfile from stdin so that we don't send the build-context; source is bind-mounted in the development environment + cat ./dockerfiles/Dockerfile.lint | docker build ${DOCKER_BUILD_ARGS} -t $(LINTER_IMAGE_NAME) - .PHONY: build_cross_image build_cross_image: - docker build ${DOCKER_BUILD_ARGS} -t $(CROSS_IMAGE_NAME) -f ./dockerfiles/Dockerfile.cross . + # build dockerfile from stdin so that we don't send the build-context; source is bind-mounted in the development environment + cat ./dockerfiles/Dockerfile.cross | docker build ${DOCKER_BUILD_ARGS} -t $(CROSS_IMAGE_NAME) - .PHONY: build_shell_validate_image build_shell_validate_image: - docker build -t $(VALIDATE_IMAGE_NAME) -f ./dockerfiles/Dockerfile.shellcheck . + # build dockerfile from stdin so that we don't send the build-context; source is bind-mounted in the development environment + cat ./dockerfiles/Dockerfile.shellcheck | docker build -t $(VALIDATE_IMAGE_NAME) - .PHONY: build_binary_native_image build_binary_native_image: - docker build -t $(BINARY_NATIVE_IMAGE_NAME) -f ./dockerfiles/Dockerfile.binary-native . + # build dockerfile from stdin so that we don't send the build-context; source is bind-mounted in the development environment + cat ./dockerfiles/Dockerfile.binary-native | docker build -t $(BINARY_NATIVE_IMAGE_NAME) - .PHONY: build_e2e_image build_e2e_image: diff --git a/components/cli/dockerfiles/Dockerfile.cross b/components/cli/dockerfiles/Dockerfile.cross index 9ee67e4eef..240ff2bfb5 100644 --- a/components/cli/dockerfiles/Dockerfile.cross +++ b/components/cli/dockerfiles/Dockerfile.cross @@ -1,3 +1,4 @@ FROM dockercore/golang-cross:1.10.8@sha256:a93210f55a8137b4aa4b9f033ac7a80b66ab6337e98e7afb62abe93b4ad73cad ENV DISABLE_WARN_OUTSIDE_CONTAINER=1 WORKDIR /go/src/github.com/docker/cli +COPY . . diff --git a/components/cli/dockerfiles/Dockerfile.dev b/components/cli/dockerfiles/Dockerfile.dev index c155587a7b..c8ce67ab06 100644 --- a/components/cli/dockerfiles/Dockerfile.dev +++ b/components/cli/dockerfiles/Dockerfile.dev @@ -22,3 +22,4 @@ ENV CGO_ENABLED=0 \ DISABLE_WARN_OUTSIDE_CONTAINER=1 WORKDIR /go/src/github.com/docker/cli CMD sh +COPY . . diff --git a/components/cli/dockerfiles/Dockerfile.lint b/components/cli/dockerfiles/Dockerfile.lint index 8be2d6114c..2a5354f432 100644 --- a/components/cli/dockerfiles/Dockerfile.lint +++ b/components/cli/dockerfiles/Dockerfile.lint @@ -15,3 +15,4 @@ ENV CGO_ENABLED=0 ENV DISABLE_WARN_OUTSIDE_CONTAINER=1 ENTRYPOINT ["/usr/local/bin/gometalinter"] CMD ["--config=gometalinter.json", "./..."] +COPY . . diff --git a/components/cli/dockerfiles/Dockerfile.shellcheck b/components/cli/dockerfiles/Dockerfile.shellcheck index 43112b314d..dd3cab79b2 100644 --- a/components/cli/dockerfiles/Dockerfile.shellcheck +++ b/components/cli/dockerfiles/Dockerfile.shellcheck @@ -1,9 +1,5 @@ -FROM debian:stretch-slim - -RUN apt-get update && \ - apt-get -y install make shellcheck && \ - apt-get clean - +FROM koalaman/shellcheck-alpine:v0.6.0 +RUN apk add --no-cache bash make WORKDIR /go/src/github.com/docker/cli ENV DISABLE_WARN_OUTSIDE_CONTAINER=1 -CMD bash +COPY . . diff --git a/components/cli/scripts/gen/windows-resources b/components/cli/scripts/gen/windows-resources index 14b45e08e0..5fdc1b4ade 100755 --- a/components/cli/scripts/gen/windows-resources +++ b/components/cli/scripts/gen/windows-resources @@ -7,7 +7,7 @@ set -eu -o pipefail SCRIPTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" # shellcheck source=/go/src/github.com/docker/cli/scripts/build/.variables -source $SCRIPTDIR/../build/.variables +source "$SCRIPTDIR"/../build/.variables RESOURCES=$SCRIPTDIR/../winresources @@ -26,9 +26,9 @@ VERSION_QUAD=$(echo -n "$VERSION" | sed -re 's/^([0-9.]*).*$/\1/' | tr . ,) # Pass version and commit information into the resource compiler defs= -[ ! -z "$VERSION" ] && defs+=( "-D DOCKER_VERSION=\"$VERSION\"") -[ ! -z "$VERSION_QUAD" ] && defs+=( "-D DOCKER_VERSION_QUAD=$VERSION_QUAD") -[ ! -z "$GITCOMMIT" ] && defs+=( "-D DOCKER_COMMIT=\"$GITCOMMIT\"") +[ -n "$VERSION" ] && defs+=( "-D DOCKER_VERSION=\"$VERSION\"") +[ -n "$VERSION_QUAD" ] && defs+=( "-D DOCKER_VERSION_QUAD=$VERSION_QUAD") +[ -n "$GITCOMMIT" ] && defs+=( "-D DOCKER_COMMIT=\"$GITCOMMIT\"") function makeres { "$WINDRES" \ diff --git a/components/cli/scripts/test/e2e/run b/components/cli/scripts/test/e2e/run index 173397a703..b960709701 100755 --- a/components/cli/scripts/test/e2e/run +++ b/components/cli/scripts/test/e2e/run @@ -70,7 +70,7 @@ function runtests { GOPATH="$GOPATH" \ PATH="$PWD/build/:/usr/bin" \ HOME="$HOME" \ - "$(which go)" test -v ./e2e/... ${TESTFLAGS-} + "$(command -v go)" test -v ./e2e/... ${TESTFLAGS-} } export unique_id="${E2E_UNIQUE_ID:-cliendtoendsuite}" diff --git a/components/cli/scripts/warn-outside-container b/components/cli/scripts/warn-outside-container index e937caafeb..ecbecc0ea2 100755 --- a/components/cli/scripts/warn-outside-container +++ b/components/cli/scripts/warn-outside-container @@ -1,9 +1,9 @@ -#!/usr/bin/env bash +#!/usr/bin/env sh set -eu target="${1:-}" -if [[ "$target" != "help" && -z "${DISABLE_WARN_OUTSIDE_CONTAINER:-}" ]]; then +if [ "$target" != "help" ] && [ -z "${DISABLE_WARN_OUTSIDE_CONTAINER:-}" ]; then ( echo echo