From 07f6f804a970ad8357c2d1bd46225e09edd1eff3 Mon Sep 17 00:00:00 2001 From: Nicolas De Loof Date: Tue, 31 Aug 2021 19:08:07 +0200 Subject: [PATCH] use docker/compose/v2 Signed-off-by: Nicolas De Loof --- .github/workflows/ci.yml | 9 +- .github/workflows/plugin-release.yaml | 42 - Dockerfile | 20 +- Makefile | 12 +- aci/aci.go | 4 +- aci/backend.go | 9 +- aci/compose.go | 4 +- aci/containers.go | 2 +- aci/context.go | 4 +- aci/convert/convert.go | 2 +- aci/convert/convert_test.go | 2 +- aci/convert/volume.go | 6 +- aci/e2e/e2e-aci_test.go | 2 +- aci/login/client.go | 3 +- aci/login/login.go | 2 +- aci/volumes.go | 7 +- api/backend/backend.go | 2 +- api/client/client.go | 3 +- api/client/compose.go | 2 +- api/client/containers.go | 3 +- api/client/resources.go | 3 +- api/client/secrets.go | 3 +- api/client/volume.go | 3 +- api/cloud/api.go | 2 +- api/context/store/store.go | 3 +- api/context/store/store_test.go | 2 +- builder.Makefile | 15 - cli/cmd/context/create_aci.go | 2 +- cli/cmd/context/create_ecs.go | 2 +- cli/cmd/context/create_kube.go | 2 +- cli/cmd/context/ls.go | 15 +- cli/cmd/context/rm.go | 3 +- cli/cmd/context/update.go | 2 +- cli/cmd/inspect.go | 3 +- cli/cmd/kill.go | 6 +- cli/cmd/login/login.go | 3 +- cli/cmd/logout/azure.go | 3 +- cli/cmd/ps.go | 2 +- cli/cmd/rm.go | 6 +- cli/cmd/run/run.go | 3 +- cli/cmd/secrets.go | 3 +- cli/cmd/start.go | 5 +- cli/cmd/stop.go | 6 +- cli/cmd/version.go | 6 +- cli/cmd/volume/command.go | 5 +- cli/cmd/volume/list.go | 2 +- cli/main.go | 6 +- cli/metrics/generatecommands/main.go | 2 +- cli/metrics/metrics.go | 2 +- cli/mobycli/exec.go | 8 +- cli/server/metrics.go | 2 +- cli/server/metrics_test.go | 5 +- cmd/compose/build.go | 99 - cmd/compose/completion.go | 48 - cmd/compose/compose.go | 289 --- cmd/compose/compose_test.go | 53 - cmd/compose/convert.go | 214 -- cmd/compose/convert_kube.go | 31 - cmd/compose/cp.go | 89 - cmd/compose/create.go | 121 -- cmd/compose/down.go | 94 - cmd/compose/events.go | 81 - cmd/compose/exec.go | 125 -- cmd/compose/images.go | 108 - cmd/compose/kill.go | 43 - cmd/compose/list.go | 114 -- cmd/compose/logs.go | 79 - cmd/compose/pause.go | 85 - cmd/compose/port.go | 77 - cmd/compose/ps.go | 188 -- cmd/compose/pull.go | 93 - cmd/compose/push.go | 60 - cmd/compose/remove.go | 81 - cmd/compose/restart.go | 62 - cmd/compose/run.go | 232 --- cmd/compose/start.go | 52 - cmd/compose/stop.go | 70 - cmd/compose/top.go | 92 - cmd/compose/up.go | 209 -- cmd/compose/up_test.go | 43 - cmd/compose/version.go | 64 - cmd/formatter/colors.go | 124 -- cmd/formatter/consts.go | 26 - cmd/formatter/formatter.go | 73 - cmd/formatter/formatter_test.go | 73 - cmd/formatter/json.go | 39 - cmd/formatter/logs.go | 115 -- cmd/formatter/multierrformat.go | 38 - cmd/formatter/pretty.go | 32 - cmd/main.go | 65 - docs/yaml/main/generate.go | 3 +- ecs/aws.go | 2 +- ecs/awsResources.go | 2 +- ecs/aws_mock.go | 8 +- ecs/backend.go | 3 +- ecs/cloudformation.go | 5 +- ecs/cloudformation_test.go | 3 +- ecs/context.go | 8 +- ecs/context_test.go | 6 +- ecs/down.go | 5 +- ecs/exec.go | 3 +- ecs/images.go | 2 +- ecs/list.go | 2 +- ecs/local/backend.go | 5 +- ecs/local/compose.go | 2 +- ecs/local/context.go | 3 +- ecs/logs.go | 3 +- ecs/ps.go | 2 +- ecs/run.go | 2 +- ecs/sdk.go | 8 +- ecs/tags.go | 3 +- ecs/top.go | 2 +- ecs/up.go | 5 +- ecs/volumes.go | 6 +- ecs/wait.go | 3 +- go.mod | 11 +- go.sum | 2 + kube/backend.go | 3 +- kube/client/client.go | 5 +- kube/client/client_test.go | 2 +- kube/client/utils.go | 5 +- kube/compose.go | 6 +- kube/context.go | 4 +- kube/helm/helm.go | 2 +- kube/resources/kube.go | 2 +- local/backend.go | 4 +- local/containers.go | 2 +- pkg/api/api.go | 405 ---- pkg/api/api_test.go | 38 - pkg/api/errors.go | 87 - pkg/api/errors_test.go | 52 - pkg/api/labels.go | 67 - pkg/api/proxy.go | 331 --- pkg/compose/attach.go | 163 -- pkg/compose/build.go | 309 --- pkg/compose/build_win.go | 28 - pkg/compose/compose.go | 76 - pkg/compose/container.go | 75 - pkg/compose/containers.go | 119 -- pkg/compose/convergence.go | 587 ------ pkg/compose/convergence_test.go | 55 - pkg/compose/convert.go | 83 - pkg/compose/cp.go | 280 --- pkg/compose/create.go | 1046 ---------- pkg/compose/create_test.go | 83 - pkg/compose/dependencies.go | 360 ---- pkg/compose/dependencies_test.go | 69 - pkg/compose/down.go | 277 --- pkg/compose/down_test.go | 105 - pkg/compose/errors.go | 72 - pkg/compose/events.go | 81 - pkg/compose/exec.go | 185 -- pkg/compose/filters.go | 48 - pkg/compose/hash.go | 38 - pkg/compose/images.go | 114 -- pkg/compose/kill.go | 62 - pkg/compose/kill_test.go | 107 - pkg/compose/logs.go | 87 - pkg/compose/ls.go | 107 - pkg/compose/ls_test.go | 66 - pkg/compose/metrics.go | 79 - pkg/compose/pause.go | 83 - pkg/compose/port.go | 50 - pkg/compose/printer.go | 111 - pkg/compose/ps.go | 96 - pkg/compose/ps_test.go | 74 - pkg/compose/pull.go | 244 --- pkg/compose/push.go | 154 -- pkg/compose/remove.go | 93 - pkg/compose/resize.go | 74 - pkg/compose/restart.go | 70 - pkg/compose/run.go | 206 -- pkg/compose/start.go | 143 -- pkg/compose/stop.go | 50 - pkg/compose/stop_test.go | 63 - pkg/compose/testdata/compose.yaml | 5 - pkg/compose/top.go | 54 - pkg/compose/up.go | 98 - pkg/e2e/cancel_test.go | 74 - pkg/e2e/cascade_stop_test.go | 50 - pkg/e2e/compose_build_test.go | 147 -- pkg/e2e/compose_exec_test.go | 57 - pkg/e2e/compose_run_test.go | 106 - pkg/e2e/compose_test.go | 200 -- pkg/e2e/cp_test.go | 118 -- pkg/e2e/fixtures/attach-restart/compose.yaml | 8 - pkg/e2e/fixtures/build-infinite/compose.yaml | 3 - .../build-infinite/service1/Dockerfile | 17 - pkg/e2e/fixtures/build-test/compose.yaml | 9 - .../build-test/nginx-build/Dockerfile | 19 - .../build-test/nginx-build/static/index.html | 10 - .../build-test/nginx-build2/Dockerfile | 17 - .../nginx-build2/static2/index.html | 10 - .../fixtures/cascade-stop-test/compose.yaml | 7 - pkg/e2e/fixtures/cp-test/compose.yaml | 3 - pkg/e2e/fixtures/cp-test/cp-folder/cp-me.txt | 1 - pkg/e2e/fixtures/cp-test/cp-me.txt | 1 - pkg/e2e/fixtures/init-container/compose.yaml | 11 - pkg/e2e/fixtures/ipam/compose.yaml | 12 - pkg/e2e/fixtures/ipc-test/compose.yaml | 13 - pkg/e2e/fixtures/logs-test/compose.yaml | 7 - pkg/e2e/fixtures/network-alias/compose.yaml | 13 - pkg/e2e/fixtures/network-test/compose.yaml | 30 - pkg/e2e/fixtures/restart-test/compose.yaml | 4 - pkg/e2e/fixtures/run-test/compose.yaml | 24 - pkg/e2e/fixtures/sentences/compose.yaml | 16 - .../fixtures/simple-build-test/compose.yaml | 3 - .../simple-build-test/nginx-build/Dockerfile | 19 - .../nginx-build/static/index.html | 10 - .../fixtures/simple-composefile/compose.yaml | 7 - pkg/e2e/fixtures/start-stop/compose.yaml | 5 - pkg/e2e/fixtures/volume-test/compose.yaml | 33 - .../volume-test/nginx-build/Dockerfile | 15 - .../fixtures/volume-test/static/index.html | 10 - .../wrong-composefile/build-error.yml | 3 - .../fixtures/wrong-composefile/compose.yaml | 4 - .../wrong-composefile/service1/Dockerfile | 17 - .../wrong-composefile/unknown-image.yml | 3 - pkg/e2e/framework.go | 267 --- pkg/e2e/ipc_test.go | 63 - pkg/e2e/logs_test.go | 58 - pkg/e2e/metrics_test.go | 55 - pkg/e2e/networks_test.go | 117 -- pkg/e2e/restart_test.go | 64 - pkg/e2e/scan_message_test.go | 78 - pkg/e2e/start_stop_test.go | 97 - pkg/e2e/volumes_test.go | 90 - pkg/mocks/mock_docker_api.go | 1812 ----------------- pkg/progress/event.go | 133 -- pkg/progress/noop.go | 37 - pkg/progress/plain.go | 49 - pkg/progress/spinner.go | 66 - pkg/progress/tty.go | 235 --- pkg/progress/tty_test.go | 105 - pkg/progress/writer.go | 116 -- pkg/progress/writer_test.go | 31 - pkg/prompt/prompt.go | 78 - pkg/prompt/prompt_mock.go | 94 - pkg/utils/scan_suggest.go | 87 - pkg/utils/stringutils.go | 27 - pkg/utils/writer.go | 62 - pkg/utils/writer_test.go | 39 - utils/logs.go | 2 +- 243 files changed, 164 insertions(+), 16223 deletions(-) delete mode 100644 .github/workflows/plugin-release.yaml delete mode 100644 cmd/compose/build.go delete mode 100644 cmd/compose/completion.go delete mode 100644 cmd/compose/compose.go delete mode 100644 cmd/compose/compose_test.go delete mode 100644 cmd/compose/convert.go delete mode 100644 cmd/compose/convert_kube.go delete mode 100644 cmd/compose/cp.go delete mode 100644 cmd/compose/create.go delete mode 100644 cmd/compose/down.go delete mode 100644 cmd/compose/events.go delete mode 100644 cmd/compose/exec.go delete mode 100644 cmd/compose/images.go delete mode 100644 cmd/compose/kill.go delete mode 100644 cmd/compose/list.go delete mode 100644 cmd/compose/logs.go delete mode 100644 cmd/compose/pause.go delete mode 100644 cmd/compose/port.go delete mode 100644 cmd/compose/ps.go delete mode 100644 cmd/compose/pull.go delete mode 100644 cmd/compose/push.go delete mode 100644 cmd/compose/remove.go delete mode 100644 cmd/compose/restart.go delete mode 100644 cmd/compose/run.go delete mode 100644 cmd/compose/start.go delete mode 100644 cmd/compose/stop.go delete mode 100644 cmd/compose/top.go delete mode 100644 cmd/compose/up.go delete mode 100644 cmd/compose/up_test.go delete mode 100644 cmd/compose/version.go delete mode 100644 cmd/formatter/colors.go delete mode 100644 cmd/formatter/consts.go delete mode 100644 cmd/formatter/formatter.go delete mode 100644 cmd/formatter/formatter_test.go delete mode 100644 cmd/formatter/json.go delete mode 100644 cmd/formatter/logs.go delete mode 100644 cmd/formatter/multierrformat.go delete mode 100644 cmd/formatter/pretty.go delete mode 100644 cmd/main.go delete mode 100644 pkg/api/api.go delete mode 100644 pkg/api/api_test.go delete mode 100644 pkg/api/errors.go delete mode 100644 pkg/api/errors_test.go delete mode 100644 pkg/api/labels.go delete mode 100644 pkg/api/proxy.go delete mode 100644 pkg/compose/attach.go delete mode 100644 pkg/compose/build.go delete mode 100644 pkg/compose/build_win.go delete mode 100644 pkg/compose/compose.go delete mode 100644 pkg/compose/container.go delete mode 100644 pkg/compose/containers.go delete mode 100644 pkg/compose/convergence.go delete mode 100644 pkg/compose/convergence_test.go delete mode 100644 pkg/compose/convert.go delete mode 100644 pkg/compose/cp.go delete mode 100644 pkg/compose/create.go delete mode 100644 pkg/compose/create_test.go delete mode 100644 pkg/compose/dependencies.go delete mode 100644 pkg/compose/dependencies_test.go delete mode 100644 pkg/compose/down.go delete mode 100644 pkg/compose/down_test.go delete mode 100644 pkg/compose/errors.go delete mode 100644 pkg/compose/events.go delete mode 100644 pkg/compose/exec.go delete mode 100644 pkg/compose/filters.go delete mode 100644 pkg/compose/hash.go delete mode 100644 pkg/compose/images.go delete mode 100644 pkg/compose/kill.go delete mode 100644 pkg/compose/kill_test.go delete mode 100644 pkg/compose/logs.go delete mode 100644 pkg/compose/ls.go delete mode 100644 pkg/compose/ls_test.go delete mode 100644 pkg/compose/metrics.go delete mode 100644 pkg/compose/pause.go delete mode 100644 pkg/compose/port.go delete mode 100644 pkg/compose/printer.go delete mode 100644 pkg/compose/ps.go delete mode 100644 pkg/compose/ps_test.go delete mode 100644 pkg/compose/pull.go delete mode 100644 pkg/compose/push.go delete mode 100644 pkg/compose/remove.go delete mode 100644 pkg/compose/resize.go delete mode 100644 pkg/compose/restart.go delete mode 100644 pkg/compose/run.go delete mode 100644 pkg/compose/start.go delete mode 100644 pkg/compose/stop.go delete mode 100644 pkg/compose/stop_test.go delete mode 100644 pkg/compose/testdata/compose.yaml delete mode 100644 pkg/compose/top.go delete mode 100644 pkg/compose/up.go delete mode 100644 pkg/e2e/cancel_test.go delete mode 100644 pkg/e2e/cascade_stop_test.go delete mode 100644 pkg/e2e/compose_build_test.go delete mode 100644 pkg/e2e/compose_exec_test.go delete mode 100644 pkg/e2e/compose_run_test.go delete mode 100644 pkg/e2e/compose_test.go delete mode 100644 pkg/e2e/cp_test.go delete mode 100644 pkg/e2e/fixtures/attach-restart/compose.yaml delete mode 100644 pkg/e2e/fixtures/build-infinite/compose.yaml delete mode 100644 pkg/e2e/fixtures/build-infinite/service1/Dockerfile delete mode 100644 pkg/e2e/fixtures/build-test/compose.yaml delete mode 100644 pkg/e2e/fixtures/build-test/nginx-build/Dockerfile delete mode 100644 pkg/e2e/fixtures/build-test/nginx-build/static/index.html delete mode 100644 pkg/e2e/fixtures/build-test/nginx-build2/Dockerfile delete mode 100644 pkg/e2e/fixtures/build-test/nginx-build2/static2/index.html delete mode 100644 pkg/e2e/fixtures/cascade-stop-test/compose.yaml delete mode 100644 pkg/e2e/fixtures/cp-test/compose.yaml delete mode 100644 pkg/e2e/fixtures/cp-test/cp-folder/cp-me.txt delete mode 100644 pkg/e2e/fixtures/cp-test/cp-me.txt delete mode 100644 pkg/e2e/fixtures/init-container/compose.yaml delete mode 100644 pkg/e2e/fixtures/ipam/compose.yaml delete mode 100644 pkg/e2e/fixtures/ipc-test/compose.yaml delete mode 100644 pkg/e2e/fixtures/logs-test/compose.yaml delete mode 100644 pkg/e2e/fixtures/network-alias/compose.yaml delete mode 100644 pkg/e2e/fixtures/network-test/compose.yaml delete mode 100644 pkg/e2e/fixtures/restart-test/compose.yaml delete mode 100644 pkg/e2e/fixtures/run-test/compose.yaml delete mode 100644 pkg/e2e/fixtures/sentences/compose.yaml delete mode 100644 pkg/e2e/fixtures/simple-build-test/compose.yaml delete mode 100644 pkg/e2e/fixtures/simple-build-test/nginx-build/Dockerfile delete mode 100644 pkg/e2e/fixtures/simple-build-test/nginx-build/static/index.html delete mode 100644 pkg/e2e/fixtures/simple-composefile/compose.yaml delete mode 100644 pkg/e2e/fixtures/start-stop/compose.yaml delete mode 100644 pkg/e2e/fixtures/volume-test/compose.yaml delete mode 100644 pkg/e2e/fixtures/volume-test/nginx-build/Dockerfile delete mode 100644 pkg/e2e/fixtures/volume-test/static/index.html delete mode 100644 pkg/e2e/fixtures/wrong-composefile/build-error.yml delete mode 100644 pkg/e2e/fixtures/wrong-composefile/compose.yaml delete mode 100644 pkg/e2e/fixtures/wrong-composefile/service1/Dockerfile delete mode 100644 pkg/e2e/fixtures/wrong-composefile/unknown-image.yml delete mode 100644 pkg/e2e/framework.go delete mode 100644 pkg/e2e/ipc_test.go delete mode 100644 pkg/e2e/logs_test.go delete mode 100644 pkg/e2e/metrics_test.go delete mode 100644 pkg/e2e/networks_test.go delete mode 100644 pkg/e2e/restart_test.go delete mode 100644 pkg/e2e/scan_message_test.go delete mode 100644 pkg/e2e/start_stop_test.go delete mode 100644 pkg/e2e/volumes_test.go delete mode 100644 pkg/mocks/mock_docker_api.go delete mode 100644 pkg/progress/event.go delete mode 100644 pkg/progress/noop.go delete mode 100644 pkg/progress/plain.go delete mode 100644 pkg/progress/spinner.go delete mode 100644 pkg/progress/tty.go delete mode 100644 pkg/progress/tty_test.go delete mode 100644 pkg/progress/writer.go delete mode 100644 pkg/progress/writer_test.go delete mode 100644 pkg/prompt/prompt.go delete mode 100644 pkg/prompt/prompt_mock.go delete mode 100644 pkg/utils/scan_suggest.go delete mode 100644 pkg/utils/stringutils.go delete mode 100644 pkg/utils/writer.go delete mode 100644 pkg/utils/writer_test.go diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 65710f96..d9457413 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,9 +25,6 @@ jobs: - name: Validate go-mod is up-to-date and license headers run: make validate - - name: Validate imports - run: make import-restrictions - - name: Run golangci-lint env: BUILD_TAGS: kube,e2e @@ -60,7 +57,7 @@ jobs: # Ensure we don't discover cross platform build issues at release time. # Time used to build linux here is gained back in the build for local E2E step - name: Build packages - run: make -f builder.Makefile cross cross-compose-plugin + run: make -f builder.Makefile cross build: name: Build @@ -99,7 +96,7 @@ jobs: - name: Build for local E2E env: BUILD_TAGS: e2e - run: make -f builder.Makefile cli compose-plugin + run: make -f builder.Makefile cli - name: E2E Test - run: make e2e-compose + run: make e2e-local diff --git a/.github/workflows/plugin-release.yaml b/.github/workflows/plugin-release.yaml deleted file mode 100644 index e393a305..00000000 --- a/.github/workflows/plugin-release.yaml +++ /dev/null @@ -1,42 +0,0 @@ -name: Releaser - -on: - push: - tags: - - "v2*" -jobs: - upload-release: - runs-on: ubuntu-latest - steps: - - name: Set up Go 1.16 - uses: actions/setup-go@v2 - with: - go-version: 1.16 - id: go - - - name: Setup docker CLI - run: | - curl https://download.docker.com/linux/static/stable/x86_64/docker-20.10.3.tgz | tar xz - sudo cp ./docker/docker /usr/bin/ && rm -rf docker && docker version - - - name: Checkout code into the Go module directory - uses: actions/checkout@v2 - - - uses: actions/cache@v2 - with: - path: ~/go/pkg/mod - key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} - restore-keys: | - ${{ runner.os }}-go- - - - name: Build - run: make -f builder.Makefile cross-compose-plugin - - - name: License - run: cp packaging/* bin/ - - - uses: ncipollo/release-action@v1 - with: - artifacts: "bin/*" - prerelease: true - token: ${{ secrets.GITHUB_TOKEN }} diff --git a/Dockerfile b/Dockerfile index c3720bc0..cce5f646 100644 --- a/Dockerfile +++ b/Dockerfile @@ -77,21 +77,6 @@ RUN --mount=target=. \ GIT_TAG=${GIT_TAG} \ make BINARY=/out/docker -f builder.Makefile cli -FROM base AS make-compose-plugin -ENV CGO_ENABLED=0 -ARG TARGETOS -ARG TARGETARCH -ARG BUILD_TAGS -ARG GIT_TAG -RUN --mount=target=. \ - --mount=type=cache,target=/go/pkg/mod \ - --mount=type=cache,target=/root/.cache/go-build \ - GOOS=${TARGETOS} \ - GOARCH=${TARGETARCH} \ - BUILD_TAGS=${BUILD_TAGS} \ - GIT_TAG=${GIT_TAG} \ - make COMPOSE_BINARY=/out/docker-compose -f builder.Makefile compose-plugin - FROM base AS make-cross ARG BUILD_TAGS ARG GIT_TAG @@ -100,7 +85,7 @@ RUN --mount=target=. \ --mount=type=cache,target=/root/.cache/go-build \ BUILD_TAGS=${BUILD_TAGS} \ GIT_TAG=${GIT_TAG} \ - make BINARY=/out/docker COMPOSE_BINARY=/out/docker-compose -f builder.Makefile cross + make BINARY=/out/docker -f builder.Makefile cross FROM scratch AS protos COPY --from=make-protos /compose-cli/cli/server/protos . @@ -108,9 +93,6 @@ COPY --from=make-protos /compose-cli/cli/server/protos . FROM scratch AS cli COPY --from=make-cli /out/* . -FROM scratch AS compose-plugin -COPY --from=make-compose-plugin /out/* . - FROM scratch AS cross COPY --from=make-cross /out/* . diff --git a/Makefile b/Makefile index 68e364c1..7a3fa30d 100644 --- a/Makefile +++ b/Makefile @@ -31,7 +31,7 @@ else TEST_FLAGS=-run $(E2E_TEST) endif -all: cli compose-plugin +all: cli protos: ## Generate go code from .proto files @docker build . --target protos \ @@ -44,16 +44,6 @@ cli: ## Compile the cli --build-arg GIT_TAG=$(GIT_TAG) \ --output ./bin -compose-plugin: ## Compile the compose cli-plugin - @docker build . --target compose-plugin \ - --platform local \ - --build-arg BUILD_TAGS=e2e,kube \ - --build-arg GIT_TAG=$(GIT_TAG) \ - --output ./bin - -e2e-compose: ## Run End to end local tests. Set E2E_TEST=TestName to run a single test - gotestsum $(TEST_FLAGS) ./pkg/e2e -- -count=1 - e2e-local: ## Run End to end local tests. Set E2E_TEST=TestName to run a single test gotestsum $(TEST_FLAGS) ./local/e2e/container ./local/e2e/cli-only -- -count=1 diff --git a/aci/aci.go b/aci/aci.go index 9bb98eaf..3d2de93c 100644 --- a/aci/aci.go +++ b/aci/aci.go @@ -29,6 +29,8 @@ import ( "github.com/Azure/go-autorest/autorest/to" tm "github.com/buger/goterm" "github.com/compose-spec/compose-go/types" + "github.com/docker/compose/v2/pkg/api" + "github.com/docker/compose/v2/pkg/progress" "github.com/gobwas/ws" "github.com/gobwas/ws/wsutil" "github.com/morikuni/aec" @@ -39,8 +41,6 @@ import ( "github.com/docker/compose-cli/api/client" "github.com/docker/compose-cli/api/containers" "github.com/docker/compose-cli/api/context/store" - "github.com/docker/compose-cli/pkg/api" - "github.com/docker/compose-cli/pkg/progress" ) func createACIContainers(ctx context.Context, aciContext store.AciContext, groupDefinition containerinstance.ContainerGroup) error { diff --git a/aci/backend.go b/aci/backend.go index ac782356..6bf3aaf0 100644 --- a/aci/backend.go +++ b/aci/backend.go @@ -21,20 +21,19 @@ import ( "github.com/Azure/azure-sdk-for-go/services/containerinstance/mgmt/2019-12-01/containerinstance" "github.com/Azure/go-autorest/autorest/to" + "github.com/docker/compose/v2/pkg/api" "github.com/pkg/errors" "github.com/docker/compose-cli/aci/convert" "github.com/docker/compose-cli/aci/login" "github.com/docker/compose-cli/api/backend" + "github.com/docker/compose-cli/api/cloud" "github.com/docker/compose-cli/api/containers" + apicontext "github.com/docker/compose-cli/api/context" + "github.com/docker/compose-cli/api/context/store" "github.com/docker/compose-cli/api/resources" "github.com/docker/compose-cli/api/secrets" "github.com/docker/compose-cli/api/volumes" - "github.com/docker/compose-cli/pkg/api" - - "github.com/docker/compose-cli/api/cloud" - apicontext "github.com/docker/compose-cli/api/context" - "github.com/docker/compose-cli/api/context/store" ) const ( diff --git a/aci/compose.go b/aci/compose.go index e3a83135..c63c2b28 100644 --- a/aci/compose.go +++ b/aci/compose.go @@ -22,14 +22,14 @@ import ( "net/http" "github.com/compose-spec/compose-go/types" + "github.com/docker/compose/v2/pkg/api" + "github.com/docker/compose/v2/pkg/progress" "github.com/pkg/errors" "github.com/sirupsen/logrus" "github.com/docker/compose-cli/aci/convert" "github.com/docker/compose-cli/aci/login" "github.com/docker/compose-cli/api/context/store" - "github.com/docker/compose-cli/pkg/api" - "github.com/docker/compose-cli/pkg/progress" "github.com/docker/compose-cli/utils/formatter" ) diff --git a/aci/containers.go b/aci/containers.go index 60407e2b..06863cb1 100644 --- a/aci/containers.go +++ b/aci/containers.go @@ -26,6 +26,7 @@ import ( "github.com/Azure/azure-sdk-for-go/services/containerinstance/mgmt/2019-12-01/containerinstance" "github.com/Azure/go-autorest/autorest" "github.com/Azure/go-autorest/autorest/to" + "github.com/docker/compose/v2/pkg/api" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -33,7 +34,6 @@ import ( "github.com/docker/compose-cli/aci/login" "github.com/docker/compose-cli/api/containers" "github.com/docker/compose-cli/api/context/store" - "github.com/docker/compose-cli/pkg/api" ) type aciContainerService struct { diff --git a/aci/context.go b/aci/context.go index 42f7b627..822331b7 100644 --- a/aci/context.go +++ b/aci/context.go @@ -24,12 +24,12 @@ import ( "github.com/AlecAivazis/survey/v2/terminal" "github.com/Azure/azure-sdk-for-go/profiles/preview/preview/subscription/mgmt/subscription" "github.com/Azure/azure-sdk-for-go/services/resources/mgmt/2018-05-01/resources" + "github.com/docker/compose/v2/pkg/api" + "github.com/docker/compose/v2/pkg/prompt" "github.com/hashicorp/go-uuid" "github.com/pkg/errors" "github.com/docker/compose-cli/api/context/store" - "github.com/docker/compose-cli/pkg/api" - "github.com/docker/compose-cli/pkg/prompt" ) // ContextParams options for creating ACI context diff --git a/aci/convert/convert.go b/aci/convert/convert.go index d92f6ebf..a890dc0e 100644 --- a/aci/convert/convert.go +++ b/aci/convert/convert.go @@ -28,12 +28,12 @@ import ( "github.com/Azure/azure-sdk-for-go/services/containerinstance/mgmt/2019-12-01/containerinstance" "github.com/Azure/go-autorest/autorest/to" "github.com/compose-spec/compose-go/types" + "github.com/docker/compose/v2/pkg/api" "github.com/pkg/errors" "github.com/docker/compose-cli/aci/login" "github.com/docker/compose-cli/api/containers" "github.com/docker/compose-cli/api/context/store" - "github.com/docker/compose-cli/pkg/api" "github.com/docker/compose-cli/utils/formatter" ) diff --git a/aci/convert/convert_test.go b/aci/convert/convert_test.go index fd6cf028..49b9324c 100644 --- a/aci/convert/convert_test.go +++ b/aci/convert/convert_test.go @@ -25,12 +25,12 @@ import ( "github.com/Azure/azure-sdk-for-go/services/containerinstance/mgmt/2019-12-01/containerinstance" "github.com/Azure/go-autorest/autorest/to" "github.com/compose-spec/compose-go/types" + "github.com/docker/compose/v2/pkg/api" "gotest.tools/v3/assert" is "gotest.tools/v3/assert/cmp" "github.com/docker/compose-cli/api/containers" "github.com/docker/compose-cli/api/context/store" - "github.com/docker/compose-cli/pkg/api" ) var ( diff --git a/aci/convert/volume.go b/aci/convert/volume.go index 79b6fcb3..78f7d1e8 100644 --- a/aci/convert/volume.go +++ b/aci/convert/volume.go @@ -22,13 +22,13 @@ import ( "strconv" "strings" - "github.com/docker/compose-cli/aci/login" - "github.com/docker/compose-cli/pkg/api" - "github.com/Azure/azure-sdk-for-go/services/containerinstance/mgmt/2019-12-01/containerinstance" "github.com/Azure/go-autorest/autorest/to" "github.com/compose-spec/compose-go/types" + "github.com/docker/compose/v2/pkg/api" "github.com/pkg/errors" + + "github.com/docker/compose-cli/aci/login" ) const ( diff --git a/aci/e2e/e2e-aci_test.go b/aci/e2e/e2e-aci_test.go index 5a15fa5c..12eb127d 100644 --- a/aci/e2e/e2e-aci_test.go +++ b/aci/e2e/e2e-aci_test.go @@ -38,6 +38,7 @@ import ( "github.com/Azure/azure-sdk-for-go/profiles/2019-03-01/resources/mgmt/resources" "github.com/Azure/azure-storage-file-go/azfile" "github.com/Azure/go-autorest/autorest/to" + "github.com/docker/compose/v2/pkg/api" "github.com/docker/docker/pkg/fileutils" "github.com/prometheus/tsdb/fileutil" "gotest.tools/v3/assert" @@ -51,7 +52,6 @@ import ( "github.com/docker/compose-cli/api/containers" "github.com/docker/compose-cli/api/context/store" "github.com/docker/compose-cli/cli/cmd" - "github.com/docker/compose-cli/pkg/api" . "github.com/docker/compose-cli/utils/e2e" ) diff --git a/aci/login/client.go b/aci/login/client.go index 79c3baf7..d481a318 100644 --- a/aci/login/client.go +++ b/aci/login/client.go @@ -30,8 +30,9 @@ import ( "github.com/Azure/go-autorest/autorest/date" "github.com/pkg/errors" + "github.com/docker/compose/v2/pkg/api" + "github.com/docker/compose-cli/internal" - "github.com/docker/compose-cli/pkg/api" ) // UserAgentName is the default user agent used by the cli diff --git a/aci/login/login.go b/aci/login/login.go index a4dffce1..96456441 100644 --- a/aci/login/login.go +++ b/aci/login/login.go @@ -25,7 +25,7 @@ import ( "os" "time" - "github.com/docker/compose-cli/pkg/api" + "github.com/docker/compose/v2/pkg/api" "github.com/Azure/go-autorest/autorest/adal" "github.com/Azure/go-autorest/autorest/azure/auth" diff --git a/aci/volumes.go b/aci/volumes.go index 530c836a..ff4bc7c8 100644 --- a/aci/volumes.go +++ b/aci/volumes.go @@ -22,17 +22,16 @@ import ( "net/http" "strings" - "github.com/pkg/errors" - "github.com/Azure/azure-sdk-for-go/services/containerinstance/mgmt/2019-12-01/containerinstance" "github.com/Azure/azure-sdk-for-go/services/storage/mgmt/2019-06-01/storage" "github.com/Azure/go-autorest/autorest/to" + "github.com/docker/compose/v2/pkg/api" + "github.com/docker/compose/v2/pkg/progress" + "github.com/pkg/errors" "github.com/docker/compose-cli/aci/login" "github.com/docker/compose-cli/api/context/store" "github.com/docker/compose-cli/api/volumes" - "github.com/docker/compose-cli/pkg/api" - "github.com/docker/compose-cli/pkg/progress" ) type aciVolumeService struct { diff --git a/api/backend/backend.go b/api/backend/backend.go index c278a3c0..228366fb 100644 --- a/api/backend/backend.go +++ b/api/backend/backend.go @@ -20,6 +20,7 @@ import ( "errors" "fmt" + "github.com/docker/compose/v2/pkg/api" "github.com/sirupsen/logrus" "github.com/docker/compose-cli/api/cloud" @@ -27,7 +28,6 @@ import ( "github.com/docker/compose-cli/api/resources" "github.com/docker/compose-cli/api/secrets" "github.com/docker/compose-cli/api/volumes" - "github.com/docker/compose-cli/pkg/api" ) var ( diff --git a/api/client/client.go b/api/client/client.go index 67144f34..c33f130b 100644 --- a/api/client/client.go +++ b/api/client/client.go @@ -19,6 +19,8 @@ package client import ( "context" + "github.com/docker/compose/v2/pkg/api" + "github.com/docker/compose-cli/api/backend" "github.com/docker/compose-cli/api/cloud" "github.com/docker/compose-cli/api/containers" @@ -27,7 +29,6 @@ import ( "github.com/docker/compose-cli/api/resources" "github.com/docker/compose-cli/api/secrets" "github.com/docker/compose-cli/api/volumes" - "github.com/docker/compose-cli/pkg/api" ) // New returns a backend client associated with current context diff --git a/api/client/compose.go b/api/client/compose.go index 1b6f5821..f55bb9a3 100644 --- a/api/client/compose.go +++ b/api/client/compose.go @@ -20,7 +20,7 @@ import ( "context" "github.com/compose-spec/compose-go/types" - "github.com/docker/compose-cli/pkg/api" + "github.com/docker/compose/v2/pkg/api" ) type composeService struct { diff --git a/api/client/containers.go b/api/client/containers.go index 5269c390..bb297367 100644 --- a/api/client/containers.go +++ b/api/client/containers.go @@ -19,8 +19,9 @@ package client import ( "context" + "github.com/docker/compose/v2/pkg/api" + "github.com/docker/compose-cli/api/containers" - "github.com/docker/compose-cli/pkg/api" ) type containerService struct { diff --git a/api/client/resources.go b/api/client/resources.go index 7f23b41b..61b60c06 100644 --- a/api/client/resources.go +++ b/api/client/resources.go @@ -19,8 +19,9 @@ package client import ( "context" + "github.com/docker/compose/v2/pkg/api" + "github.com/docker/compose-cli/api/resources" - "github.com/docker/compose-cli/pkg/api" ) type resourceService struct { diff --git a/api/client/secrets.go b/api/client/secrets.go index d2545b83..bf369b8c 100644 --- a/api/client/secrets.go +++ b/api/client/secrets.go @@ -19,8 +19,9 @@ package client import ( "context" + "github.com/docker/compose/v2/pkg/api" + "github.com/docker/compose-cli/api/secrets" - "github.com/docker/compose-cli/pkg/api" ) type secretsService struct { diff --git a/api/client/volume.go b/api/client/volume.go index 62cdc9d7..1df898cd 100644 --- a/api/client/volume.go +++ b/api/client/volume.go @@ -19,8 +19,9 @@ package client import ( "context" + "github.com/docker/compose/v2/pkg/api" + "github.com/docker/compose-cli/api/volumes" - "github.com/docker/compose-cli/pkg/api" ) type volumeService struct { diff --git a/api/cloud/api.go b/api/cloud/api.go index e6ae7ad0..f8b6e876 100644 --- a/api/cloud/api.go +++ b/api/cloud/api.go @@ -19,7 +19,7 @@ package cloud import ( "context" - "github.com/docker/compose-cli/pkg/api" + "github.com/docker/compose/v2/pkg/api" ) // Service cloud specific services diff --git a/api/context/store/store.go b/api/context/store/store.go index 6f3cc80d..10fb8a71 100644 --- a/api/context/store/store.go +++ b/api/context/store/store.go @@ -24,8 +24,7 @@ import ( "path/filepath" "reflect" - "github.com/docker/compose-cli/pkg/api" - + "github.com/docker/compose/v2/pkg/api" "github.com/opencontainers/go-digest" "github.com/pkg/errors" ) diff --git a/api/context/store/store_test.go b/api/context/store/store_test.go index 54fc6a25..7ea385f6 100644 --- a/api/context/store/store_test.go +++ b/api/context/store/store_test.go @@ -22,7 +22,7 @@ import ( "os" "testing" - "github.com/docker/compose-cli/pkg/api" + "github.com/docker/compose/v2/pkg/api" "gotest.tools/v3/assert" "gotest.tools/v3/assert/cmp" diff --git a/builder.Makefile b/builder.Makefile index c15f175b..8892db21 100644 --- a/builder.Makefile +++ b/builder.Makefile @@ -53,10 +53,6 @@ protos: cli: GOOS=${GOOS} GOARCH=${GOARCH} $(GO_BUILD) $(TAGS) -o $(BINARY_WITH_EXTENSION) ./cli -.PHONY: compose-plugin -compose-plugin: - GOOS=${GOOS} GOARCH=${GOARCH} $(GO_BUILD) $(TAGS) -o $(COMPOSE_BINARY_WITH_EXTENSION) ./cmd - .PHONY: cross cross: GOOS=linux GOARCH=amd64 $(GO_BUILD) $(TAGS) -o $(BINARY)-linux-amd64 ./cli @@ -68,17 +64,6 @@ cross: GOOS=darwin GOARCH=arm64 $(GO_BUILD) $(TAGS) -o $(BINARY)-darwin-arm64 ./cli GOOS=windows GOARCH=amd64 $(GO_BUILD) $(TAGS) -o $(BINARY)-windows-amd64.exe ./cli -.PHONY: cross-compose-plugin -cross-compose-plugin: - GOOS=linux GOARCH=amd64 $(GO_BUILD) $(TAGS) -o $(COMPOSE_BINARY)-linux-amd64 ./cmd - GOOS=linux GOARCH=arm64 $(GO_BUILD) $(TAGS) -o $(COMPOSE_BINARY)-linux-arm64 ./cmd - GOOS=linux GOARM=6 GOARCH=arm $(GO_BUILD) $(TAGS) -o $(COMPOSE_BINARY)-linux-armv6 ./cmd - GOOS=linux GOARM=7 GOARCH=arm $(GO_BUILD) $(TAGS) -o $(COMPOSE_BINARY)-linux-armv7 ./cmd - GOOS=linux GOARCH=s390x $(GO_BUILD) $(TAGS) -o $(COMPOSE_BINARY)-linux-s390x ./cmd - GOOS=darwin GOARCH=amd64 $(GO_BUILD) $(TAGS) -o $(COMPOSE_BINARY)-darwin-amd64 ./cmd - GOOS=darwin GOARCH=arm64 $(GO_BUILD) $(TAGS) -o $(COMPOSE_BINARY)-darwin-arm64 ./cmd - GOOS=windows GOARCH=amd64 $(GO_BUILD) $(TAGS) -o $(COMPOSE_BINARY)-windows-amd64.exe ./cmd - .PHONY: test test: go test $(TAGS) -cover $(shell go list $(TAGS) ./... | grep -vE 'e2e') diff --git a/cli/cmd/context/create_aci.go b/cli/cmd/context/create_aci.go index 0a06a24f..5c7bc650 100644 --- a/cli/cmd/context/create_aci.go +++ b/cli/cmd/context/create_aci.go @@ -19,13 +19,13 @@ package context import ( "context" + "github.com/docker/compose/v2/pkg/api" "github.com/pkg/errors" "github.com/spf13/cobra" "github.com/docker/compose-cli/aci" "github.com/docker/compose-cli/api/client" "github.com/docker/compose-cli/api/context/store" - "github.com/docker/compose-cli/pkg/api" ) func init() { diff --git a/cli/cmd/context/create_ecs.go b/cli/cmd/context/create_ecs.go index eabf24e4..bb2539e8 100644 --- a/cli/cmd/context/create_ecs.go +++ b/cli/cmd/context/create_ecs.go @@ -23,13 +23,13 @@ import ( "os" "strings" + "github.com/docker/compose/v2/pkg/api" "github.com/pkg/errors" "github.com/spf13/cobra" "github.com/docker/compose-cli/api/client" "github.com/docker/compose-cli/api/context/store" "github.com/docker/compose-cli/ecs" - "github.com/docker/compose-cli/pkg/api" ) func init() { diff --git a/cli/cmd/context/create_kube.go b/cli/cmd/context/create_kube.go index 3f24abac..59d95808 100644 --- a/cli/cmd/context/create_kube.go +++ b/cli/cmd/context/create_kube.go @@ -19,7 +19,7 @@ package context import ( - "github.com/docker/compose-cli/pkg/api" + "github.com/docker/compose/v2/pkg/api" "github.com/pkg/errors" "github.com/spf13/cobra" diff --git a/cli/cmd/context/ls.go b/cli/cmd/context/ls.go index a83d8519..4d8b13f4 100644 --- a/cli/cmd/context/ls.go +++ b/cli/cmd/context/ls.go @@ -23,8 +23,7 @@ import ( "sort" "strings" - formatter2 "github.com/docker/compose-cli/cmd/formatter" - + "github.com/docker/compose/v2/cmd/formatter" "github.com/pkg/errors" "github.com/spf13/cobra" @@ -69,7 +68,7 @@ func runList(cmd *cobra.Command, opts lsOpts) error { return err } format := strings.ToLower(strings.ReplaceAll(opts.format, " ", "")) - if format != "" && format != formatter2.JSON && format != formatter2.PRETTY && format != formatter2.TemplateLegacyJSON { + if format != "" && format != formatter.JSON && format != formatter.PRETTY && format != formatter.TemplateLegacyJSON { mobycli.Exec(cmd.Root()) return nil } @@ -92,15 +91,15 @@ func runList(cmd *cobra.Command, opts lsOpts) error { return nil } - if opts.json || format == formatter2.JSON { - opts.format = formatter2.JSON + if opts.json || format == formatter.JSON { + opts.format = formatter.JSON } - if format == formatter2.TemplateLegacyJSON { - opts.format = formatter2.TemplateLegacyJSON + if format == formatter.TemplateLegacyJSON { + opts.format = formatter.TemplateLegacyJSON } view := viewFromContextList(contexts, currentContext) - return formatter2.Print(view, opts.format, os.Stdout, + return formatter.Print(view, opts.format, os.Stdout, func(w io.Writer) { for _, c := range view { contextName := c.Name diff --git a/cli/cmd/context/rm.go b/cli/cmd/context/rm.go index 1602c9d4..d04ba87e 100644 --- a/cli/cmd/context/rm.go +++ b/cli/cmd/context/rm.go @@ -20,8 +20,7 @@ import ( "errors" "fmt" - "github.com/docker/compose-cli/cmd/formatter" - + "github.com/docker/compose/v2/cmd/formatter" "github.com/hashicorp/go-multierror" "github.com/spf13/cobra" diff --git a/cli/cmd/context/update.go b/cli/cmd/context/update.go index e71bc105..f36b6822 100644 --- a/cli/cmd/context/update.go +++ b/cli/cmd/context/update.go @@ -17,7 +17,7 @@ package context import ( - "github.com/docker/compose-cli/pkg/api" + "github.com/docker/compose/v2/pkg/api" "github.com/pkg/errors" "github.com/spf13/cobra" diff --git a/cli/cmd/inspect.go b/cli/cmd/inspect.go index 8f84f714..909694eb 100644 --- a/cli/cmd/inspect.go +++ b/cli/cmd/inspect.go @@ -20,10 +20,9 @@ import ( "context" "fmt" - "github.com/docker/compose-cli/cmd/formatter" - "github.com/Azure/go-autorest/autorest/to" "github.com/compose-spec/compose-go/types" + "github.com/docker/compose/v2/cmd/formatter" "github.com/pkg/errors" "github.com/spf13/cobra" diff --git a/cli/cmd/kill.go b/cli/cmd/kill.go index be467152..e91f133f 100644 --- a/cli/cmd/kill.go +++ b/cli/cmd/kill.go @@ -20,14 +20,14 @@ import ( "context" "fmt" - "github.com/docker/compose-cli/cmd/formatter" - + "github.com/docker/compose/v2/cmd/formatter" "github.com/hashicorp/go-multierror" "github.com/pkg/errors" "github.com/spf13/cobra" + "github.com/docker/compose/v2/pkg/api" + "github.com/docker/compose-cli/api/client" - "github.com/docker/compose-cli/pkg/api" ) type killOpts struct { diff --git a/cli/cmd/login/login.go b/cli/cmd/login/login.go index d7450482..1ea3173b 100644 --- a/cli/cmd/login/login.go +++ b/cli/cmd/login/login.go @@ -23,9 +23,10 @@ import ( "github.com/pkg/errors" "github.com/spf13/cobra" + "github.com/docker/compose/v2/pkg/api" + "github.com/docker/compose-cli/api/client" "github.com/docker/compose-cli/cli/mobycli" - "github.com/docker/compose-cli/pkg/api" ) // Command returns the login command diff --git a/cli/cmd/logout/azure.go b/cli/cmd/logout/azure.go index 0b998357..1d4155e0 100644 --- a/cli/cmd/logout/azure.go +++ b/cli/cmd/logout/azure.go @@ -23,8 +23,9 @@ import ( "github.com/pkg/errors" "github.com/spf13/cobra" + "github.com/docker/compose/v2/pkg/api" + "github.com/docker/compose-cli/api/client" - "github.com/docker/compose-cli/pkg/api" ) // AzureLogoutCommand returns the azure logout command diff --git a/cli/cmd/ps.go b/cli/cmd/ps.go index 940a6b14..6c023277 100644 --- a/cli/cmd/ps.go +++ b/cli/cmd/ps.go @@ -23,12 +23,12 @@ import ( "os" "strings" + format "github.com/docker/compose/v2/cmd/formatter" "github.com/pkg/errors" "github.com/spf13/cobra" "github.com/docker/compose-cli/api/client" "github.com/docker/compose-cli/api/containers" - format "github.com/docker/compose-cli/cmd/formatter" "github.com/docker/compose-cli/utils/formatter" ) diff --git a/cli/cmd/rm.go b/cli/cmd/rm.go index 5f012e1f..b2cfc3aa 100644 --- a/cli/cmd/rm.go +++ b/cli/cmd/rm.go @@ -20,15 +20,15 @@ import ( "context" "fmt" - "github.com/docker/compose-cli/cmd/formatter" - + "github.com/docker/compose/v2/cmd/formatter" "github.com/hashicorp/go-multierror" "github.com/pkg/errors" "github.com/spf13/cobra" + "github.com/docker/compose/v2/pkg/api" + "github.com/docker/compose-cli/api/client" "github.com/docker/compose-cli/api/containers" - "github.com/docker/compose-cli/pkg/api" ) type rmOpts struct { diff --git a/cli/cmd/run/run.go b/cli/cmd/run/run.go index 3e83376a..ba939faf 100644 --- a/cli/cmd/run/run.go +++ b/cli/cmd/run/run.go @@ -26,11 +26,12 @@ import ( "github.com/containerd/console" "github.com/spf13/cobra" + "github.com/docker/compose/v2/pkg/progress" + "github.com/docker/compose-cli/api/client" "github.com/docker/compose-cli/api/containers" "github.com/docker/compose-cli/api/context/store" "github.com/docker/compose-cli/cli/options/run" - "github.com/docker/compose-cli/pkg/progress" ) // Command runs a container diff --git a/cli/cmd/secrets.go b/cli/cmd/secrets.go index 45360b7d..96d877fc 100644 --- a/cli/cmd/secrets.go +++ b/cli/cmd/secrets.go @@ -22,8 +22,7 @@ import ( "io/ioutil" "os" - "github.com/docker/compose-cli/cmd/formatter" - + "github.com/docker/compose/v2/cmd/formatter" "github.com/spf13/cobra" "github.com/docker/compose-cli/api/client" diff --git a/cli/cmd/start.go b/cli/cmd/start.go index a12e4b10..9850279a 100644 --- a/cli/cmd/start.go +++ b/cli/cmd/start.go @@ -20,14 +20,13 @@ import ( "context" "fmt" - "github.com/docker/compose-cli/cmd/formatter" - + "github.com/docker/compose/v2/cmd/formatter" + "github.com/docker/compose/v2/pkg/api" "github.com/hashicorp/go-multierror" "github.com/pkg/errors" "github.com/spf13/cobra" "github.com/docker/compose-cli/api/client" - "github.com/docker/compose-cli/pkg/api" ) // StartCommand starts containers diff --git a/cli/cmd/stop.go b/cli/cmd/stop.go index e66587ca..b5678436 100644 --- a/cli/cmd/stop.go +++ b/cli/cmd/stop.go @@ -20,14 +20,14 @@ import ( "context" "fmt" - "github.com/docker/compose-cli/cmd/formatter" - + "github.com/docker/compose/v2/cmd/formatter" "github.com/hashicorp/go-multierror" "github.com/pkg/errors" "github.com/spf13/cobra" + "github.com/docker/compose/v2/pkg/api" + "github.com/docker/compose-cli/api/client" - "github.com/docker/compose-cli/pkg/api" ) type stopOpts struct { diff --git a/cli/cmd/version.go b/cli/cmd/version.go index 4465cb80..99fb9a76 100644 --- a/cli/cmd/version.go +++ b/cli/cmd/version.go @@ -21,11 +21,9 @@ import ( "os" "strings" - "github.com/docker/compose-cli/cmd/formatter" - - "github.com/spf13/cobra" - "github.com/docker/cli/cli" + "github.com/docker/compose/v2/cmd/formatter" + "github.com/spf13/cobra" "github.com/docker/compose-cli/cli/mobycli" "github.com/docker/compose-cli/internal" diff --git a/cli/cmd/volume/command.go b/cli/cmd/volume/command.go index 0aef21ce..569689d1 100644 --- a/cli/cmd/volume/command.go +++ b/cli/cmd/volume/command.go @@ -20,13 +20,14 @@ import ( "context" "fmt" - format "github.com/docker/compose-cli/cmd/formatter" + format "github.com/docker/compose/v2/cmd/formatter" + + "github.com/docker/compose/v2/pkg/progress" "github.com/docker/compose-cli/aci" "github.com/docker/compose-cli/api/client" "github.com/docker/compose-cli/api/context/store" "github.com/docker/compose-cli/ecs" - "github.com/docker/compose-cli/pkg/progress" "github.com/hashicorp/go-multierror" "github.com/spf13/cobra" diff --git a/cli/cmd/volume/list.go b/cli/cmd/volume/list.go index 38b54fdb..8a92d7ca 100644 --- a/cli/cmd/volume/list.go +++ b/cli/cmd/volume/list.go @@ -21,7 +21,7 @@ import ( "io" "os" - formatter2 "github.com/docker/compose-cli/cmd/formatter" + formatter2 "github.com/docker/compose/v2/cmd/formatter" "github.com/spf13/cobra" diff --git a/cli/main.go b/cli/main.go index 2c002594..2b82a8cc 100644 --- a/cli/main.go +++ b/cli/main.go @@ -30,6 +30,9 @@ import ( "github.com/compose-spec/compose-go/types" "github.com/docker/cli/cli" + compose2 "github.com/docker/compose/v2/cmd/compose" + "github.com/docker/compose/v2/pkg/api" + "github.com/docker/compose/v2/pkg/compose" "github.com/pkg/errors" "github.com/sirupsen/logrus" "github.com/spf13/cobra" @@ -48,10 +51,7 @@ import ( "github.com/docker/compose-cli/cli/metrics" "github.com/docker/compose-cli/cli/mobycli" cliopts "github.com/docker/compose-cli/cli/options" - compose2 "github.com/docker/compose-cli/cmd/compose" "github.com/docker/compose-cli/local" - "github.com/docker/compose-cli/pkg/api" - "github.com/docker/compose-cli/pkg/compose" // Backend registrations _ "github.com/docker/compose-cli/aci" diff --git a/cli/metrics/generatecommands/main.go b/cli/metrics/generatecommands/main.go index caa29197..681728cf 100644 --- a/cli/metrics/generatecommands/main.go +++ b/cli/metrics/generatecommands/main.go @@ -21,7 +21,7 @@ import ( "os/exec" "strings" - "github.com/docker/compose-cli/pkg/utils" + "github.com/docker/compose/v2/pkg/utils" ) var managementCommands = []string{"ecs", "scan"} diff --git a/cli/metrics/metrics.go b/cli/metrics/metrics.go index fcd3967a..7840892c 100644 --- a/cli/metrics/metrics.go +++ b/cli/metrics/metrics.go @@ -20,7 +20,7 @@ import ( "os" "strings" - "github.com/docker/compose-cli/pkg/utils" + "github.com/docker/compose/v2/pkg/utils" ) // Track sends the tracking analytics to Docker Desktop diff --git a/cli/mobycli/exec.go b/cli/mobycli/exec.go index 552987f8..eecf9b88 100644 --- a/cli/mobycli/exec.go +++ b/cli/mobycli/exec.go @@ -25,14 +25,14 @@ import ( "path/filepath" "regexp" + "github.com/docker/compose/v2/pkg/compose" + "github.com/docker/compose/v2/pkg/utils" + "github.com/spf13/cobra" + apicontext "github.com/docker/compose-cli/api/context" "github.com/docker/compose-cli/api/context/store" "github.com/docker/compose-cli/cli/metrics" "github.com/docker/compose-cli/cli/mobycli/resolvepath" - "github.com/spf13/cobra" - - "github.com/docker/compose-cli/pkg/compose" - "github.com/docker/compose-cli/pkg/utils" ) var delegatedContextTypes = []string{store.DefaultContextType} diff --git a/cli/server/metrics.go b/cli/server/metrics.go index 439eb69a..17881a84 100644 --- a/cli/server/metrics.go +++ b/cli/server/metrics.go @@ -19,11 +19,11 @@ package server import ( "context" + "github.com/docker/compose/v2/pkg/compose" "google.golang.org/grpc" "github.com/docker/compose-cli/cli/metrics" "github.com/docker/compose-cli/cli/server/proxy" - "github.com/docker/compose-cli/pkg/compose" ) var ( diff --git a/cli/server/metrics_test.go b/cli/server/metrics_test.go index 8c4501e2..82eb980f 100644 --- a/cli/server/metrics_test.go +++ b/cli/server/metrics_test.go @@ -21,8 +21,7 @@ import ( "strings" "testing" - "github.com/docker/compose-cli/api/resources" - + "github.com/docker/compose/v2/pkg/api" "github.com/stretchr/testify/mock" "google.golang.org/grpc" "google.golang.org/grpc/metadata" @@ -30,6 +29,7 @@ import ( "github.com/docker/compose-cli/api/client" "github.com/docker/compose-cli/api/containers" + "github.com/docker/compose-cli/api/resources" "github.com/docker/compose-cli/api/secrets" "github.com/docker/compose-cli/api/volumes" "github.com/docker/compose-cli/cli/metrics" @@ -38,7 +38,6 @@ import ( streamsv1 "github.com/docker/compose-cli/cli/server/protos/streams/v1" volumesv1 "github.com/docker/compose-cli/cli/server/protos/volumes/v1" "github.com/docker/compose-cli/cli/server/proxy" - "github.com/docker/compose-cli/pkg/api" ) func TestAllMethodsHaveCorrespondingCliCommand(t *testing.T) { diff --git a/cmd/compose/build.go b/cmd/compose/build.go deleted file mode 100644 index dfe15f5b..00000000 --- a/cmd/compose/build.go +++ /dev/null @@ -1,99 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package compose - -import ( - "context" - "fmt" - "os" - - "github.com/compose-spec/compose-go/types" - "github.com/spf13/cobra" - - "github.com/docker/compose-cli/pkg/api" -) - -type buildOptions struct { - *projectOptions - composeOptions - quiet bool - pull bool - progress string - args []string - noCache bool - memory string -} - -func buildCommand(p *projectOptions, backend api.Service) *cobra.Command { - opts := buildOptions{ - projectOptions: p, - } - cmd := &cobra.Command{ - Use: "build [SERVICE...]", - Short: "Build or rebuild services", - PreRunE: Adapt(func(ctx context.Context, args []string) error { - if opts.memory != "" { - fmt.Println("WARNING --memory is ignored as not supported in buildkit.") - } - if opts.quiet { - devnull, err := os.Open(os.DevNull) - if err != nil { - return err - } - os.Stdout = devnull - } - return nil - }), - RunE: Adapt(func(ctx context.Context, args []string) error { - return runBuild(ctx, backend, opts, args) - }), - ValidArgsFunction: serviceCompletion(p), - } - cmd.Flags().BoolVarP(&opts.quiet, "quiet", "q", false, "Don't print anything to STDOUT") - cmd.Flags().BoolVar(&opts.pull, "pull", false, "Always attempt to pull a newer version of the image.") - cmd.Flags().StringVar(&opts.progress, "progress", "auto", `Set type of progress output ("auto", "plain", "noTty")`) - cmd.Flags().StringArrayVar(&opts.args, "build-arg", []string{}, "Set build-time variables for services.") - cmd.Flags().Bool("parallel", true, "Build images in parallel. DEPRECATED") - cmd.Flags().MarkHidden("parallel") //nolint:errcheck - cmd.Flags().Bool("compress", true, "Compress the build context using gzip. DEPRECATED") - cmd.Flags().MarkHidden("compress") //nolint:errcheck - cmd.Flags().Bool("force-rm", true, "Always remove intermediate containers. DEPRECATED") - cmd.Flags().MarkHidden("force-rm") //nolint:errcheck - cmd.Flags().BoolVar(&opts.noCache, "no-cache", false, "Do not use cache when building the image") - cmd.Flags().Bool("no-rm", false, "Do not remove intermediate containers after a successful build. DEPRECATED") - cmd.Flags().MarkHidden("no-rm") //nolint:errcheck - cmd.Flags().StringVarP(&opts.memory, "memory", "m", "", "Set memory limit for the build container. Not supported on buildkit yet.") - cmd.Flags().MarkHidden("memory") //nolint:errcheck - - return cmd -} - -func runBuild(ctx context.Context, backend api.Service, opts buildOptions, services []string) error { - project, err := opts.toProject(services) - if err != nil { - return err - } - - return backend.Build(ctx, project, api.BuildOptions{ - Pull: opts.pull, - Progress: opts.progress, - Args: types.NewMappingWithEquals(opts.args), - NoCache: opts.noCache, - Quiet: opts.quiet, - Services: services, - }) -} diff --git a/cmd/compose/completion.go b/cmd/compose/completion.go deleted file mode 100644 index 891539b6..00000000 --- a/cmd/compose/completion.go +++ /dev/null @@ -1,48 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package compose - -import ( - "strings" - - "github.com/spf13/cobra" -) - -// validArgsFn defines a completion func to be returned to fetch completion options -type validArgsFn func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) - -func noCompletion() validArgsFn { - return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - return nil, cobra.ShellCompDirectiveNoFileComp - } -} - -func serviceCompletion(p *projectOptions) validArgsFn { - return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - project, err := p.toProject(nil) - if err != nil { - return nil, cobra.ShellCompDirectiveNoFileComp - } - var serviceNames []string - for _, s := range project.ServiceNames() { - if toComplete == "" || strings.HasPrefix(s, toComplete) { - serviceNames = append(serviceNames, s) - } - } - return serviceNames, cobra.ShellCompDirectiveNoFileComp - } -} diff --git a/cmd/compose/compose.go b/cmd/compose/compose.go deleted file mode 100644 index 260d5f81..00000000 --- a/cmd/compose/compose.go +++ /dev/null @@ -1,289 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package compose - -import ( - "context" - "fmt" - "os" - "os/signal" - "path/filepath" - "strings" - "syscall" - - "github.com/docker/compose-cli/cmd/formatter" - - "github.com/sirupsen/logrus" - - "github.com/compose-spec/compose-go/cli" - "github.com/compose-spec/compose-go/types" - dockercli "github.com/docker/cli/cli" - "github.com/morikuni/aec" - "github.com/pkg/errors" - "github.com/spf13/cobra" - "github.com/spf13/pflag" - - "github.com/docker/compose-cli/pkg/api" - "github.com/docker/compose-cli/pkg/compose" -) - -// Command defines a compose CLI command as a func with args -type Command func(context.Context, []string) error - -// Adapt a Command func to cobra library -func Adapt(fn Command) func(cmd *cobra.Command, args []string) error { - return func(cmd *cobra.Command, args []string) error { - ctx := cmd.Context() - contextString := fmt.Sprintf("%s", ctx) - if !strings.HasSuffix(contextString, ".WithCancel") { // need to handle cancel - cancellableCtx, cancel := context.WithCancel(cmd.Context()) - ctx = cancellableCtx - s := make(chan os.Signal, 1) - signal.Notify(s, syscall.SIGTERM, syscall.SIGINT) - go func() { - <-s - cancel() - }() - } - err := fn(ctx, args) - var composeErr compose.Error - if api.IsErrCanceled(err) || errors.Is(ctx.Err(), context.Canceled) { - err = dockercli.StatusError{ - StatusCode: 130, - Status: compose.CanceledStatus, - } - } - if errors.As(err, &composeErr) { - err = dockercli.StatusError{ - StatusCode: composeErr.GetMetricsFailureCategory().ExitCode, - Status: err.Error(), - } - } - return err - } -} - -// Warning is a global warning to be displayed to user on command failure -var Warning string - -type projectOptions struct { - ProjectName string - Profiles []string - ConfigPaths []string - WorkDir string - ProjectDir string - EnvFile string -} - -// ProjectFunc does stuff within a types.Project -type ProjectFunc func(ctx context.Context, project *types.Project) error - -// ProjectServicesFunc does stuff within a types.Project and a selection of services -type ProjectServicesFunc func(ctx context.Context, project *types.Project, services []string) error - -// WithServices creates a cobra run command from a ProjectFunc based on configured project options and selected services -func (o *projectOptions) WithProject(fn ProjectFunc) func(cmd *cobra.Command, args []string) error { - return o.WithServices(func(ctx context.Context, project *types.Project, services []string) error { - return fn(ctx, project) - }) -} - -// WithServices creates a cobra run command from a ProjectFunc based on configured project options and selected services -func (o *projectOptions) WithServices(fn ProjectServicesFunc) func(cmd *cobra.Command, args []string) error { - return Adapt(func(ctx context.Context, args []string) error { - project, err := o.toProject(args) - if err != nil { - return err - } - - if o.EnvFile != "" { - var services types.Services - for _, s := range project.Services { - ef := o.EnvFile - if ef != "" { - if !filepath.IsAbs(ef) { - ef = filepath.Join(project.WorkingDir, o.EnvFile) - } - if s.Labels == nil { - s.Labels = make(map[string]string) - } - s.Labels[api.EnvironmentFileLabel] = ef - services = append(services, s) - } - } - project.Services = services - } - - return fn(ctx, project, args) - }) -} - -func (o *projectOptions) addProjectFlags(f *pflag.FlagSet) { - f.StringArrayVar(&o.Profiles, "profile", []string{}, "Specify a profile to enable") - f.StringVarP(&o.ProjectName, "project-name", "p", "", "Project name") - f.StringArrayVarP(&o.ConfigPaths, "file", "f", []string{}, "Compose configuration files") - f.StringVar(&o.EnvFile, "env-file", "", "Specify an alternate environment file.") - f.StringVar(&o.ProjectDir, "project-directory", "", "Specify an alternate working directory\n(default: the path of the Compose file)") - f.StringVar(&o.WorkDir, "workdir", "", "DEPRECATED! USE --project-directory INSTEAD.\nSpecify an alternate working directory\n(default: the path of the Compose file)") - _ = f.MarkHidden("workdir") -} - -func (o *projectOptions) toProjectName() (string, error) { - if o.ProjectName != "" { - return o.ProjectName, nil - } - - project, err := o.toProject(nil) - if err != nil { - return "", err - } - return project.Name, nil -} - -func (o *projectOptions) toProject(services []string, po ...cli.ProjectOptionsFn) (*types.Project, error) { - options, err := o.toProjectOptions(po...) - if err != nil { - return nil, compose.WrapComposeError(err) - } - - project, err := cli.ProjectFromOptions(options) - if err != nil { - return nil, compose.WrapComposeError(err) - } - - if len(services) > 0 { - s, err := project.GetServices(services...) - if err != nil { - return nil, err - } - o.Profiles = append(o.Profiles, s.GetProfiles()...) - } - - if profiles, ok := options.Environment["COMPOSE_PROFILES"]; ok { - o.Profiles = append(o.Profiles, strings.Split(profiles, ",")...) - } - - project.ApplyProfiles(o.Profiles) - - project.WithoutUnnecessaryResources() - - err = project.ForServices(services) - return project, err -} - -func (o *projectOptions) toProjectOptions(po ...cli.ProjectOptionsFn) (*cli.ProjectOptions, error) { - return cli.NewProjectOptions(o.ConfigPaths, - append(po, - cli.WithEnvFile(o.EnvFile), - cli.WithDotEnv, - cli.WithOsEnv, - cli.WithWorkingDirectory(o.ProjectDir), - cli.WithConfigFileEnv, - cli.WithDefaultConfigPath, - cli.WithName(o.ProjectName))...) -} - -// RootCommand returns the compose command with its child commands -func RootCommand(backend api.Service) *cobra.Command { - opts := projectOptions{} - var ( - ansi string - noAnsi bool - verbose bool - ) - command := &cobra.Command{ - Short: "Docker Compose", - Use: "compose", - TraverseChildren: true, - // By default (no Run/RunE in parent command) for typos in subcommands, cobra displays the help of parent command but exit(0) ! - RunE: func(cmd *cobra.Command, args []string) error { - if len(args) == 0 { - return cmd.Help() - } - _ = cmd.Help() - return dockercli.StatusError{ - StatusCode: compose.CommandSyntaxFailure.ExitCode, - Status: fmt.Sprintf("unknown docker command: %q", "compose "+args[0]), - } - }, - PersistentPreRunE: func(cmd *cobra.Command, args []string) error { - parent := cmd.Root() - parentPrerun := parent.PersistentPreRunE - if parentPrerun != nil { - err := parentPrerun(cmd, args) - if err != nil { - return err - } - } - if noAnsi { - if ansi != "auto" { - return errors.New(`cannot specify DEPRECATED "--no-ansi" and "--ansi". Please use only "--ansi"`) - } - ansi = "never" - fmt.Fprint(os.Stderr, aec.Apply("option '--no-ansi' is DEPRECATED ! Please use '--ansi' instead.\n", aec.RedF)) - } - if verbose { - logrus.SetLevel(logrus.TraceLevel) - } - formatter.SetANSIMode(ansi) - if opts.WorkDir != "" { - if opts.ProjectDir != "" { - return errors.New(`cannot specify DEPRECATED "--workdir" and "--project-directory". Please use only "--project-directory" instead`) - } - opts.ProjectDir = opts.WorkDir - fmt.Fprint(os.Stderr, aec.Apply("option '--workdir' is DEPRECATED at root level! Please use '--project-directory' instead.\n", aec.RedF)) - } - return nil - }, - } - - command.AddCommand( - upCommand(&opts, backend), - downCommand(&opts, backend), - startCommand(&opts, backend), - restartCommand(&opts, backend), - stopCommand(&opts, backend), - psCommand(&opts, backend), - listCommand(backend), - logsCommand(&opts, backend), - convertCommand(&opts, backend), - killCommand(&opts, backend), - runCommand(&opts, backend), - removeCommand(&opts, backend), - execCommand(&opts, backend), - pauseCommand(&opts, backend), - unpauseCommand(&opts, backend), - topCommand(&opts, backend), - eventsCommand(&opts, backend), - portCommand(&opts, backend), - imagesCommand(&opts, backend), - versionCommand(), - buildCommand(&opts, backend), - pushCommand(&opts, backend), - pullCommand(&opts, backend), - createCommand(&opts, backend), - copyCommand(&opts, backend), - ) - command.Flags().SetInterspersed(false) - opts.addProjectFlags(command.Flags()) - command.Flags().StringVar(&ansi, "ansi", "auto", `Control when to print ANSI control characters ("never"|"always"|"auto")`) - command.Flags().BoolVar(&noAnsi, "no-ansi", false, `Do not print ANSI control characters (DEPRECATED)`) - command.Flags().MarkHidden("no-ansi") //nolint:errcheck - command.Flags().BoolVar(&verbose, "verbose", false, "Show more output") - command.Flags().MarkHidden("verbose") //nolint:errcheck - return command -} diff --git a/cmd/compose/compose_test.go b/cmd/compose/compose_test.go deleted file mode 100644 index b1940442..00000000 --- a/cmd/compose/compose_test.go +++ /dev/null @@ -1,53 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package compose - -import ( - "testing" - - "github.com/compose-spec/compose-go/types" - "gotest.tools/v3/assert" -) - -func TestFilterServices(t *testing.T) { - p := &types.Project{ - Services: []types.ServiceConfig{ - { - Name: "foo", - Links: []string{"bar"}, - }, - { - Name: "bar", - NetworkMode: types.NetworkModeServicePrefix + "zot", - }, - { - Name: "zot", - }, - { - Name: "qix", - }, - }, - } - err := p.ForServices([]string{"bar"}) - assert.NilError(t, err) - - assert.Equal(t, len(p.Services), 2) - _, err = p.GetService("bar") - assert.NilError(t, err) - _, err = p.GetService("zot") - assert.NilError(t, err) -} diff --git a/cmd/compose/convert.go b/cmd/compose/convert.go deleted file mode 100644 index 6f921263..00000000 --- a/cmd/compose/convert.go +++ /dev/null @@ -1,214 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package compose - -import ( - "bufio" - "context" - "fmt" - "io" - "os" - "sort" - "strings" - - "github.com/cnabio/cnab-to-oci/remotes" - "github.com/compose-spec/compose-go/cli" - "github.com/compose-spec/compose-go/types" - "github.com/distribution/distribution/v3/reference" - cliconfig "github.com/docker/cli/cli/config" - "github.com/opencontainers/go-digest" - "github.com/spf13/cobra" - - "github.com/docker/compose-cli/pkg/api" - "github.com/docker/compose-cli/pkg/compose" -) - -type convertOptions struct { - *projectOptions - Format string - Output string - quiet bool - resolve bool - noInterpolate bool - services bool - volumes bool - profiles bool - hash string -} - -var addFlagsFuncs []func(cmd *cobra.Command, opts *convertOptions) - -func convertCommand(p *projectOptions, backend api.Service) *cobra.Command { - opts := convertOptions{ - projectOptions: p, - } - cmd := &cobra.Command{ - Aliases: []string{"config"}, - Use: "convert SERVICES", - Short: "Converts the compose file to platform's canonical format", - PreRunE: Adapt(func(ctx context.Context, args []string) error { - if opts.quiet { - devnull, err := os.Open(os.DevNull) - if err != nil { - return err - } - os.Stdout = devnull - } - return nil - }), - RunE: Adapt(func(ctx context.Context, args []string) error { - if opts.services { - return runServices(opts) - } - if opts.volumes { - return runVolumes(opts) - } - if opts.hash != "" { - return runHash(opts) - } - if opts.profiles { - return runProfiles(opts, args) - } - - return runConvert(ctx, backend, opts, args) - }), - ValidArgsFunction: serviceCompletion(p), - } - flags := cmd.Flags() - flags.StringVar(&opts.Format, "format", "yaml", "Format the output. Values: [yaml | json]") - flags.BoolVar(&opts.resolve, "resolve-image-digests", false, "Pin image tags to digests.") - flags.BoolVarP(&opts.quiet, "quiet", "q", false, "Only validate the configuration, don't print anything.") - flags.BoolVar(&opts.noInterpolate, "no-interpolate", false, "Don't interpolate environment variables.") - - flags.BoolVar(&opts.services, "services", false, "Print the service names, one per line.") - flags.BoolVar(&opts.volumes, "volumes", false, "Print the volume names, one per line.") - flags.BoolVar(&opts.profiles, "profiles", false, "Print the profile names, one per line.") - flags.StringVar(&opts.hash, "hash", "", "Print the service config hash, one per line.") - - // add flags for hidden backends - for _, f := range addFlagsFuncs { - f(cmd, &opts) - } - return cmd -} - -func runConvert(ctx context.Context, backend api.Service, opts convertOptions, services []string) error { - var json []byte - project, err := opts.toProject(services, cli.WithInterpolation(!opts.noInterpolate)) - if err != nil { - return err - } - - if opts.resolve { - configFile := cliconfig.LoadDefaultConfigFile(os.Stderr) - - resolver := remotes.CreateResolver(configFile) - err = project.ResolveImages(func(named reference.Named) (digest.Digest, error) { - _, desc, err := resolver.Resolve(ctx, named.String()) - return desc.Digest, err - }) - if err != nil { - return err - } - } - - json, err = backend.Convert(ctx, project, api.ConvertOptions{ - Format: opts.Format, - Output: opts.Output, - }) - if err != nil { - return err - } - - if opts.quiet { - return nil - } - - var out io.Writer = os.Stdout - if opts.Output != "" && len(json) > 0 { - file, err := os.Create(opts.Output) - if err != nil { - return err - } - out = bufio.NewWriter(file) - } - _, err = fmt.Fprint(out, string(json)) - return err -} - -func runServices(opts convertOptions) error { - project, err := opts.toProject(nil) - if err != nil { - return err - } - return project.WithServices(project.ServiceNames(), func(s types.ServiceConfig) error { - fmt.Println(s.Name) - return nil - }) -} - -func runVolumes(opts convertOptions) error { - project, err := opts.toProject(nil) - if err != nil { - return err - } - for n := range project.Volumes { - fmt.Println(n) - } - return nil -} - -func runHash(opts convertOptions) error { - var services []string - if opts.hash != "*" { - services = append(services, strings.Split(opts.hash, ",")...) - } - project, err := opts.toProject(services) - if err != nil { - return err - } - for _, s := range project.Services { - hash, err := compose.ServiceHash(s) - if err != nil { - return err - } - fmt.Printf("%s %s\n", s.Name, hash) - } - return nil -} - -func runProfiles(opts convertOptions, services []string) error { - set := map[string]struct{}{} - project, err := opts.toProject(services) - if err != nil { - return err - } - for _, s := range project.AllServices() { - for _, p := range s.Profiles { - set[p] = struct{}{} - } - } - profiles := make([]string, 0, len(set)) - for p := range set { - profiles = append(profiles, p) - } - sort.Strings(profiles) - for _, p := range profiles { - fmt.Println(p) - } - return nil -} diff --git a/cmd/compose/convert_kube.go b/cmd/compose/convert_kube.go deleted file mode 100644 index 77680032..00000000 --- a/cmd/compose/convert_kube.go +++ /dev/null @@ -1,31 +0,0 @@ -// +build kube - -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package compose - -import ( - "github.com/spf13/cobra" -) - -func init() { - addFlagsFuncs = append(addFlagsFuncs, func(cmd *cobra.Command, opts *convertOptions) { - flags := cmd.Flags() - flags.StringVar(&opts.Output, "output", "", "Save to directory") - }) - -} diff --git a/cmd/compose/cp.go b/cmd/compose/cp.go deleted file mode 100644 index 99f0952f..00000000 --- a/cmd/compose/cp.go +++ /dev/null @@ -1,89 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package compose - -import ( - "context" - "errors" - - "github.com/docker/cli/cli" - "github.com/spf13/cobra" - - "github.com/docker/compose-cli/pkg/api" -) - -type copyOptions struct { - *projectOptions - - source string - destination string - index int - all bool - followLink bool - copyUIDGID bool -} - -func copyCommand(p *projectOptions, backend api.Service) *cobra.Command { - opts := copyOptions{ - projectOptions: p, - } - copyCmd := &cobra.Command{ - Use: `cp [OPTIONS] SERVICE:SRC_PATH DEST_PATH|- - docker compose cp [OPTIONS] SRC_PATH|- SERVICE:DEST_PATH`, - Short: "Copy files/folders between a service container and the local filesystem", - Args: cli.ExactArgs(2), - PreRunE: Adapt(func(ctx context.Context, args []string) error { - if args[0] == "" { - return errors.New("source can not be empty") - } - if args[1] == "" { - return errors.New("destination can not be empty") - } - return nil - }), - RunE: Adapt(func(ctx context.Context, args []string) error { - opts.source = args[0] - opts.destination = args[1] - return runCopy(ctx, backend, opts) - }), - ValidArgsFunction: serviceCompletion(p), - } - - flags := copyCmd.Flags() - flags.IntVar(&opts.index, "index", 1, "Index of the container if there are multiple instances of a service [default: 1].") - flags.BoolVar(&opts.all, "all", false, "Copy to all the containers of the service.") - flags.BoolVarP(&opts.followLink, "follow-link", "L", false, "Always follow symbol link in SRC_PATH") - flags.BoolVarP(&opts.copyUIDGID, "archive", "a", false, "Archive mode (copy all uid/gid information)") - - return copyCmd -} - -func runCopy(ctx context.Context, backend api.Service, opts copyOptions) error { - projects, err := opts.toProject(nil) - if err != nil { - return err - } - - return backend.Copy(ctx, projects, api.CopyOptions{ - Source: opts.source, - Destination: opts.destination, - All: opts.all, - Index: opts.index, - FollowLink: opts.followLink, - CopyUIDGID: opts.copyUIDGID, - }) -} diff --git a/cmd/compose/create.go b/cmd/compose/create.go deleted file mode 100644 index 77c3087c..00000000 --- a/cmd/compose/create.go +++ /dev/null @@ -1,121 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package compose - -import ( - "context" - "fmt" - "time" - - "github.com/compose-spec/compose-go/types" - "github.com/spf13/cobra" - - "github.com/docker/compose-cli/pkg/api" -) - -type createOptions struct { - Build bool - noBuild bool - removeOrphans bool - forceRecreate bool - noRecreate bool - recreateDeps bool - noInherit bool - timeChanged bool - timeout int - quietPull bool -} - -func createCommand(p *projectOptions, backend api.Service) *cobra.Command { - opts := createOptions{} - cmd := &cobra.Command{ - Use: "create [SERVICE...]", - Short: "Creates containers for a service.", - PreRunE: Adapt(func(ctx context.Context, args []string) error { - if opts.Build && opts.noBuild { - return fmt.Errorf("--build and --no-build are incompatible") - } - if opts.forceRecreate && opts.noRecreate { - return fmt.Errorf("--force-recreate and --no-recreate are incompatible") - } - return nil - }), - RunE: p.WithProject(func(ctx context.Context, project *types.Project) error { - return backend.Create(ctx, project, api.CreateOptions{ - RemoveOrphans: opts.removeOrphans, - Recreate: opts.recreateStrategy(), - RecreateDependencies: opts.dependenciesRecreateStrategy(), - Inherit: !opts.noInherit, - Timeout: opts.GetTimeout(), - QuietPull: false, - }) - }), - ValidArgsFunction: serviceCompletion(p), - } - flags := cmd.Flags() - flags.BoolVar(&opts.Build, "build", false, "Build images before starting containers.") - flags.BoolVar(&opts.noBuild, "no-build", false, "Don't build an image, even if it's missing.") - flags.BoolVar(&opts.forceRecreate, "force-recreate", false, "Recreate containers even if their configuration and image haven't changed.") - flags.BoolVar(&opts.noRecreate, "no-recreate", false, "If containers already exist, don't recreate them. Incompatible with --force-recreate.") - return cmd -} - -func (opts createOptions) recreateStrategy() string { - if opts.noRecreate { - return api.RecreateNever - } - if opts.forceRecreate { - return api.RecreateForce - } - return api.RecreateDiverged -} - -func (opts createOptions) dependenciesRecreateStrategy() string { - if opts.noRecreate { - return api.RecreateNever - } - if opts.recreateDeps { - return api.RecreateForce - } - return api.RecreateDiverged -} - -func (opts createOptions) GetTimeout() *time.Duration { - if opts.timeChanged { - t := time.Duration(opts.timeout) * time.Second - return &t - } - return nil -} - -func (opts createOptions) Apply(project *types.Project) { - if opts.Build { - for i, service := range project.Services { - if service.Build == nil { - continue - } - service.PullPolicy = types.PullPolicyBuild - project.Services[i] = service - } - } - if opts.noBuild { - for i, service := range project.Services { - service.Build = nil - project.Services[i] = service - } - } -} diff --git a/cmd/compose/down.go b/cmd/compose/down.go deleted file mode 100644 index e851fb8c..00000000 --- a/cmd/compose/down.go +++ /dev/null @@ -1,94 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package compose - -import ( - "context" - "fmt" - "time" - - "github.com/compose-spec/compose-go/types" - "github.com/spf13/cobra" - - "github.com/docker/compose-cli/pkg/api" -) - -type downOptions struct { - *projectOptions - removeOrphans bool - timeChanged bool - timeout int - volumes bool - images string -} - -func downCommand(p *projectOptions, backend api.Service) *cobra.Command { - opts := downOptions{ - projectOptions: p, - } - downCmd := &cobra.Command{ - Use: "down", - Short: "Stop and remove containers, networks", - PreRun: func(cmd *cobra.Command, args []string) { - opts.timeChanged = cmd.Flags().Changed("timeout") - }, - PreRunE: Adapt(func(ctx context.Context, args []string) error { - if opts.images != "" { - if opts.images != "all" && opts.images != "local" { - return fmt.Errorf("invalid value for --rmi: %q", opts.images) - } - } - return nil - }), - RunE: Adapt(func(ctx context.Context, args []string) error { - return runDown(ctx, backend, opts) - }), - ValidArgsFunction: noCompletion(), - } - flags := downCmd.Flags() - flags.BoolVar(&opts.removeOrphans, "remove-orphans", false, "Remove containers for services not defined in the Compose file.") - flags.IntVarP(&opts.timeout, "timeout", "t", 10, "Specify a shutdown timeout in seconds") - flags.BoolVarP(&opts.volumes, "volumes", "v", false, " Remove named volumes declared in the `volumes` section of the Compose file and anonymous volumes attached to containers.") - flags.StringVar(&opts.images, "rmi", "", `Remove images used by services. "local" remove only images that don't have a custom tag ("local"|"all")`) - return downCmd -} - -func runDown(ctx context.Context, backend api.Service, opts downOptions) error { - name := opts.ProjectName - var project *types.Project - if opts.ProjectName == "" { - p, err := opts.toProject(nil) - if err != nil { - return err - } - project = p - name = p.Name - } - - var timeout *time.Duration - if opts.timeChanged { - timeoutValue := time.Duration(opts.timeout) * time.Second - timeout = &timeoutValue - } - return backend.Down(ctx, name, api.DownOptions{ - RemoveOrphans: opts.removeOrphans, - Project: project, - Timeout: timeout, - Images: opts.images, - Volumes: opts.volumes, - }) -} diff --git a/cmd/compose/events.go b/cmd/compose/events.go deleted file mode 100644 index 6f7368e9..00000000 --- a/cmd/compose/events.go +++ /dev/null @@ -1,81 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package compose - -import ( - "context" - "encoding/json" - "fmt" - - "github.com/docker/compose-cli/pkg/api" - - "github.com/spf13/cobra" -) - -type eventsOpts struct { - *composeOptions - json bool -} - -func eventsCommand(p *projectOptions, backend api.Service) *cobra.Command { - opts := eventsOpts{ - composeOptions: &composeOptions{ - projectOptions: p, - }, - } - cmd := &cobra.Command{ - Use: "events [options] [--] [SERVICE...]", - Short: "Receive real time events from containers.", - RunE: Adapt(func(ctx context.Context, args []string) error { - return runEvents(ctx, backend, opts, args) - }), - ValidArgsFunction: serviceCompletion(p), - } - - cmd.Flags().BoolVar(&opts.json, "json", false, "Output events as a stream of json objects") - return cmd -} - -func runEvents(ctx context.Context, backend api.Service, opts eventsOpts, services []string) error { - project, err := opts.toProjectName() - if err != nil { - return err - } - - return backend.Events(ctx, project, api.EventsOptions{ - Services: services, - Consumer: func(event api.Event) error { - if opts.json { - marshal, err := json.Marshal(map[string]interface{}{ - "time": event.Timestamp, - "type": "container", - "service": event.Service, - "id": event.Container, - "action": event.Status, - "attributes": event.Attributes, - }) - if err != nil { - return err - } - fmt.Println(string(marshal)) - } else { - fmt.Println(event) - } - return nil - }, - }) -} diff --git a/cmd/compose/exec.go b/cmd/compose/exec.go deleted file mode 100644 index 653a4441..00000000 --- a/cmd/compose/exec.go +++ /dev/null @@ -1,125 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package compose - -import ( - "context" - "fmt" - "os" - - "github.com/containerd/console" - "github.com/docker/cli/cli" - "github.com/spf13/cobra" - - "github.com/docker/compose-cli/pkg/api" -) - -type execOpts struct { - *composeOptions - - service string - command []string - environment []string - workingDir string - - noTty bool - user string - detach bool - index int - privileged bool -} - -func execCommand(p *projectOptions, backend api.Service) *cobra.Command { - opts := execOpts{ - composeOptions: &composeOptions{ - projectOptions: p, - }, - } - runCmd := &cobra.Command{ - Use: "exec [options] [-e KEY=VAL...] [--] SERVICE COMMAND [ARGS...]", - Short: "Execute a command in a running container.", - Args: cobra.MinimumNArgs(2), - PreRunE: Adapt(func(ctx context.Context, args []string) error { - opts.service = args[0] - opts.command = args[1:] - return nil - }), - RunE: Adapt(func(ctx context.Context, args []string) error { - return runExec(ctx, backend, opts) - }), - ValidArgsFunction: serviceCompletion(p), - } - - runCmd.Flags().BoolVarP(&opts.detach, "detach", "d", false, "Detached mode: Run command in the background.") - runCmd.Flags().StringArrayVarP(&opts.environment, "env", "e", []string{}, "Set environment variables") - runCmd.Flags().IntVar(&opts.index, "index", 1, "index of the container if there are multiple instances of a service [default: 1].") - runCmd.Flags().BoolVarP(&opts.privileged, "privileged", "", false, "Give extended privileges to the process.") - runCmd.Flags().StringVarP(&opts.user, "user", "u", "", "Run the command as this user.") - runCmd.Flags().BoolVarP(&opts.noTty, "no-TTY", "T", notAtTTY(), "Disable pseudo-TTY allocation. By default `docker compose exec` allocates a TTY.") - runCmd.Flags().StringVarP(&opts.workingDir, "workdir", "w", "", "Path to workdir directory for this command.") - - runCmd.Flags().SetInterspersed(false) - return runCmd -} - -func runExec(ctx context.Context, backend api.Service, opts execOpts) error { - project, err := opts.toProject(nil) - if err != nil { - return err - } - - execOpts := api.RunOptions{ - Service: opts.service, - Command: opts.command, - Environment: opts.environment, - Tty: !opts.noTty, - User: opts.user, - Privileged: opts.privileged, - Index: opts.index, - Detach: opts.detach, - WorkingDir: opts.workingDir, - - Stdin: os.Stdin, - Stdout: os.Stdout, - Stderr: os.Stderr, - } - - if execOpts.Tty { - con := console.Current() - if err := con.SetRaw(); err != nil { - return err - } - defer func() { - if err := con.Reset(); err != nil { - fmt.Println("Unable to close the console") - } - }() - - execOpts.Stdin = con - execOpts.Stdout = con - execOpts.Stderr = con - } - exitCode, err := backend.Exec(ctx, project, execOpts) - if exitCode != 0 { - errMsg := "" - if err != nil { - errMsg = err.Error() - } - return cli.StatusError{StatusCode: exitCode, Status: errMsg} - } - return err -} diff --git a/cmd/compose/images.go b/cmd/compose/images.go deleted file mode 100644 index bed8a615..00000000 --- a/cmd/compose/images.go +++ /dev/null @@ -1,108 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package compose - -import ( - "context" - "fmt" - "io" - "os" - "sort" - "strings" - - "github.com/docker/docker/pkg/stringid" - "github.com/docker/go-units" - "github.com/spf13/cobra" - - "github.com/docker/compose-cli/cmd/formatter" - "github.com/docker/compose-cli/pkg/api" - "github.com/docker/compose-cli/pkg/utils" -) - -type imageOptions struct { - *projectOptions - Quiet bool -} - -func imagesCommand(p *projectOptions, backend api.Service) *cobra.Command { - opts := imageOptions{ - projectOptions: p, - } - imgCmd := &cobra.Command{ - Use: "images [SERVICE...]", - Short: "List images used by the created containers", - RunE: Adapt(func(ctx context.Context, args []string) error { - return runImages(ctx, backend, opts, args) - }), - ValidArgsFunction: serviceCompletion(p), - } - imgCmd.Flags().BoolVarP(&opts.Quiet, "quiet", "q", false, "Only display IDs") - return imgCmd -} - -func runImages(ctx context.Context, backend api.Service, opts imageOptions, services []string) error { - projectName, err := opts.toProjectName() - if err != nil { - return err - } - - images, err := backend.Images(ctx, projectName, api.ImagesOptions{ - Services: services, - }) - if err != nil { - return err - } - - if opts.Quiet { - ids := []string{} - for _, img := range images { - id := img.ID - if i := strings.IndexRune(img.ID, ':'); i >= 0 { - id = id[i+1:] - } - if !utils.StringContains(ids, id) { - ids = append(ids, id) - } - } - for _, img := range ids { - fmt.Println(img) - } - return nil - } - - sort.Slice(images, func(i, j int) bool { - return images[i].ContainerName < images[j].ContainerName - }) - - return formatter.Print(images, formatter.PRETTY, os.Stdout, - func(w io.Writer) { - for _, img := range images { - id := stringid.TruncateID(img.ID) - size := units.HumanSizeWithPrecision(float64(img.Size), 3) - repo := img.Repository - if repo == "" { - repo = "" - } - tag := img.Tag - if tag == "" { - tag = "" - } - _, _ = fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\n", img.ContainerName, repo, tag, id, size) - } - }, - "Container", "Repository", "Tag", "Image Id", "Size") -} diff --git a/cmd/compose/kill.go b/cmd/compose/kill.go deleted file mode 100644 index d4b33a11..00000000 --- a/cmd/compose/kill.go +++ /dev/null @@ -1,43 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package compose - -import ( - "context" - - "github.com/compose-spec/compose-go/types" - "github.com/spf13/cobra" - - "github.com/docker/compose-cli/pkg/api" -) - -func killCommand(p *projectOptions, backend api.Service) *cobra.Command { - var opts api.KillOptions - cmd := &cobra.Command{ - Use: "kill [options] [SERVICE...]", - Short: "Force stop service containers.", - RunE: p.WithProject(func(ctx context.Context, project *types.Project) error { - return backend.Kill(ctx, project, opts) - }), - ValidArgsFunction: serviceCompletion(p), - } - - flags := cmd.Flags() - flags.StringVarP(&opts.Signal, "signal", "s", "SIGKILL", "SIGNAL to send to the container.") - - return cmd -} diff --git a/cmd/compose/list.go b/cmd/compose/list.go deleted file mode 100644 index 0b1045b6..00000000 --- a/cmd/compose/list.go +++ /dev/null @@ -1,114 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package compose - -import ( - "context" - "fmt" - "io" - "os" - "strings" - - "github.com/docker/compose-cli/cmd/formatter" - - "github.com/docker/cli/opts" - "github.com/spf13/cobra" - - "github.com/docker/compose-cli/pkg/api" -) - -type lsOptions struct { - Format string - Quiet bool - All bool - Filter opts.FilterOpt -} - -func listCommand(backend api.Service) *cobra.Command { - opts := lsOptions{Filter: opts.NewFilterOpt()} - lsCmd := &cobra.Command{ - Use: "ls", - Short: "List running compose projects", - RunE: Adapt(func(ctx context.Context, args []string) error { - return runList(ctx, backend, opts) - }), - ValidArgsFunction: noCompletion(), - } - lsCmd.Flags().StringVar(&opts.Format, "format", "pretty", "Format the output. Values: [pretty | json].") - lsCmd.Flags().BoolVarP(&opts.Quiet, "quiet", "q", false, "Only display IDs.") - lsCmd.Flags().Var(&opts.Filter, "filter", "Filter output based on conditions provided.") - lsCmd.Flags().BoolVarP(&opts.All, "all", "a", false, "Show all stopped Compose projects") - - return lsCmd -} - -var acceptedListFilters = map[string]bool{ - "name": true, -} - -func runList(ctx context.Context, backend api.Service, opts lsOptions) error { - filters := opts.Filter.Value() - err := filters.Validate(acceptedListFilters) - if err != nil { - return err - } - - stackList, err := backend.List(ctx, api.ListOptions{All: opts.All}) - if err != nil { - return err - } - if opts.Quiet { - for _, s := range stackList { - fmt.Println(s.Name) - } - return nil - } - - if filters.Len() > 0 { - var filtered []api.Stack - for _, s := range stackList { - if filters.Contains("name") && !filters.Match("name", s.Name) { - continue - } - filtered = append(filtered, s) - } - stackList = filtered - } - - view := viewFromStackList(stackList) - return formatter.Print(view, opts.Format, os.Stdout, func(w io.Writer) { - for _, stack := range view { - _, _ = fmt.Fprintf(w, "%s\t%s\n", stack.Name, stack.Status) - } - }, "NAME", "STATUS") -} - -type stackView struct { - Name string - Status string -} - -func viewFromStackList(stackList []api.Stack) []stackView { - retList := make([]stackView, len(stackList)) - for i, s := range stackList { - retList[i] = stackView{ - Name: s.Name, - Status: strings.TrimSpace(fmt.Sprintf("%s %s", s.Status, s.Reason)), - } - } - return retList -} diff --git a/cmd/compose/logs.go b/cmd/compose/logs.go deleted file mode 100644 index 0d672bd7..00000000 --- a/cmd/compose/logs.go +++ /dev/null @@ -1,79 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package compose - -import ( - "context" - "os" - - "github.com/docker/compose-cli/cmd/formatter" - - "github.com/spf13/cobra" - - "github.com/docker/compose-cli/pkg/api" -) - -type logsOptions struct { - *projectOptions - composeOptions - follow bool - tail string - since string - until string - noColor bool - noPrefix bool - timestamps bool -} - -func logsCommand(p *projectOptions, backend api.Service) *cobra.Command { - opts := logsOptions{ - projectOptions: p, - } - logsCmd := &cobra.Command{ - Use: "logs [SERVICE...]", - Short: "View output from containers", - RunE: Adapt(func(ctx context.Context, args []string) error { - return runLogs(ctx, backend, opts, args) - }), - ValidArgsFunction: serviceCompletion(p), - } - flags := logsCmd.Flags() - flags.BoolVarP(&opts.follow, "follow", "f", false, "Follow log output.") - flags.StringVar(&opts.since, "since", "", "Show logs since timestamp (e.g. 2013-01-02T13:23:37Z) or relative (e.g. 42m for 42 minutes)") - flags.StringVar(&opts.until, "until", "", "Show logs before a timestamp (e.g. 2013-01-02T13:23:37Z) or relative (e.g. 42m for 42 minutes)") - flags.BoolVar(&opts.noColor, "no-color", false, "Produce monochrome output.") - flags.BoolVar(&opts.noPrefix, "no-log-prefix", false, "Don't print prefix in logs.") - flags.BoolVarP(&opts.timestamps, "timestamps", "t", false, "Show timestamps.") - flags.StringVar(&opts.tail, "tail", "all", "Number of lines to show from the end of the logs for each container.") - return logsCmd -} - -func runLogs(ctx context.Context, backend api.Service, opts logsOptions, services []string) error { - projectName, err := opts.toProjectName() - if err != nil { - return err - } - consumer := formatter.NewLogConsumer(ctx, os.Stdout, !opts.noColor, !opts.noPrefix) - return backend.Logs(ctx, projectName, consumer, api.LogOptions{ - Services: services, - Follow: opts.follow, - Tail: opts.tail, - Since: opts.since, - Until: opts.until, - Timestamps: opts.timestamps, - }) -} diff --git a/cmd/compose/pause.go b/cmd/compose/pause.go deleted file mode 100644 index 52c7fbb6..00000000 --- a/cmd/compose/pause.go +++ /dev/null @@ -1,85 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package compose - -import ( - "context" - - "github.com/spf13/cobra" - - "github.com/docker/compose-cli/pkg/api" -) - -type pauseOptions struct { - *projectOptions -} - -func pauseCommand(p *projectOptions, backend api.Service) *cobra.Command { - opts := pauseOptions{ - projectOptions: p, - } - cmd := &cobra.Command{ - Use: "pause [SERVICE...]", - Short: "pause services", - RunE: Adapt(func(ctx context.Context, args []string) error { - return runPause(ctx, backend, opts, args) - }), - ValidArgsFunction: serviceCompletion(p), - } - return cmd -} - -func runPause(ctx context.Context, backend api.Service, opts pauseOptions, services []string) error { - project, err := opts.toProjectName() - if err != nil { - return err - } - - return backend.Pause(ctx, project, api.PauseOptions{ - Services: services, - }) -} - -type unpauseOptions struct { - *projectOptions -} - -func unpauseCommand(p *projectOptions, backend api.Service) *cobra.Command { - opts := unpauseOptions{ - projectOptions: p, - } - cmd := &cobra.Command{ - Use: "unpause [SERVICE...]", - Short: "unpause services", - RunE: Adapt(func(ctx context.Context, args []string) error { - return runUnPause(ctx, backend, opts, args) - }), - ValidArgsFunction: serviceCompletion(p), - } - return cmd -} - -func runUnPause(ctx context.Context, backend api.Service, opts unpauseOptions, services []string) error { - project, err := opts.toProjectName() - if err != nil { - return err - } - - return backend.UnPause(ctx, project, api.PauseOptions{ - Services: services, - }) -} diff --git a/cmd/compose/port.go b/cmd/compose/port.go deleted file mode 100644 index 37ce0019..00000000 --- a/cmd/compose/port.go +++ /dev/null @@ -1,77 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package compose - -import ( - "context" - "fmt" - "strconv" - - "github.com/spf13/cobra" - - "github.com/docker/compose-cli/pkg/api" -) - -type portOptions struct { - *projectOptions - port int - protocol string - index int -} - -func portCommand(p *projectOptions, backend api.Service) *cobra.Command { - opts := portOptions{ - projectOptions: p, - } - cmd := &cobra.Command{ - Use: "port [options] [--] SERVICE PRIVATE_PORT", - Short: "Print the public port for a port binding.", - Args: cobra.MinimumNArgs(2), - PreRunE: Adapt(func(ctx context.Context, args []string) error { - port, err := strconv.Atoi(args[1]) - if err != nil { - return err - } - opts.port = port - return nil - }), - RunE: Adapt(func(ctx context.Context, args []string) error { - return runPort(ctx, backend, opts, args[0]) - }), - ValidArgsFunction: serviceCompletion(p), - } - cmd.Flags().StringVar(&opts.protocol, "protocol", "tcp", "tcp or udp") - cmd.Flags().IntVar(&opts.index, "index", 1, "index of the container if service has multiple replicas") - return cmd -} - -func runPort(ctx context.Context, backend api.Service, opts portOptions, service string) error { - projectName, err := opts.toProjectName() - if err != nil { - return err - } - ip, port, err := backend.Port(ctx, projectName, service, opts.port, api.PortOptions{ - Protocol: opts.protocol, - Index: opts.index, - }) - if err != nil { - return err - } - - fmt.Printf("%s:%d\n", ip, port) - return nil -} diff --git a/cmd/compose/ps.go b/cmd/compose/ps.go deleted file mode 100644 index f86ec0cb..00000000 --- a/cmd/compose/ps.go +++ /dev/null @@ -1,188 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package compose - -import ( - "context" - "fmt" - "io" - "os" - "sort" - "strconv" - "strings" - - "github.com/docker/compose-cli/cmd/formatter" - - formatter2 "github.com/docker/cli/cli/command/formatter" - "github.com/pkg/errors" - "github.com/spf13/cobra" - - "github.com/docker/compose-cli/pkg/api" - "github.com/docker/compose-cli/pkg/utils" -) - -type psOptions struct { - *projectOptions - Format string - All bool - Quiet bool - Services bool - Filter string - Status string -} - -func (p *psOptions) parseFilter() error { - if p.Filter == "" { - return nil - } - parts := strings.SplitN(p.Filter, "=", 2) - if len(parts) != 2 { - return errors.New("arguments to --filter should be in form KEY=VAL") - } - switch parts[0] { - case "status": - p.Status = parts[1] - case "source": - return api.ErrNotImplemented - default: - return fmt.Errorf("unknow filter %s", parts[0]) - } - return nil -} - -func psCommand(p *projectOptions, backend api.Service) *cobra.Command { - opts := psOptions{ - projectOptions: p, - } - psCmd := &cobra.Command{ - Use: "ps [SERVICE...]", - Short: "List containers", - PreRunE: func(cmd *cobra.Command, args []string) error { - return opts.parseFilter() - }, - RunE: Adapt(func(ctx context.Context, args []string) error { - return runPs(ctx, backend, args, opts) - }), - ValidArgsFunction: serviceCompletion(p), - } - flags := psCmd.Flags() - flags.StringVar(&opts.Format, "format", "pretty", "Format the output. Values: [pretty | json]") - flags.StringVar(&opts.Filter, "filter", "", "Filter services by a property") - flags.StringVar(&opts.Status, "status", "", "Filter services by status") - flags.BoolVarP(&opts.Quiet, "quiet", "q", false, "Only display IDs") - flags.BoolVar(&opts.Services, "services", false, "Display services") - flags.BoolVarP(&opts.All, "all", "a", false, "Show all stopped containers (including those created by the run command)") - flags.Lookup("filter").Hidden = true - return psCmd -} - -func runPs(ctx context.Context, backend api.Service, services []string, opts psOptions) error { - projectName, err := opts.toProjectName() - if err != nil { - return err - } - containers, err := backend.Ps(ctx, projectName, api.PsOptions{ - All: opts.All, - Services: services, - }) - if err != nil { - return err - } - - if opts.Services { - services := []string{} - for _, s := range containers { - if !utils.StringContains(services, s.Service) { - services = append(services, s.Service) - } - } - fmt.Println(strings.Join(services, "\n")) - return nil - } - -SERVICES: - for _, s := range services { - for _, c := range containers { - if c.Service == s { - continue SERVICES - } - } - return fmt.Errorf("no such service: %s", s) - } - - if len(containers) == 0 { - return api.ErrNotFound - } - - if opts.Status != "" { - containers = filterByStatus(containers, opts.Status) - } - - sort.Slice(containers, func(i, j int) bool { - return containers[i].Name < containers[j].Name - }) - - if opts.Quiet { - for _, c := range containers { - fmt.Println(c.ID) - } - return nil - } - - return formatter.Print(containers, opts.Format, os.Stdout, - writter(containers), - "NAME", "COMMAND", "SERVICE", "STATUS", "PORTS") -} - -func writter(containers []api.ContainerSummary) func(w io.Writer) { - return func(w io.Writer) { - for _, container := range containers { - var ports []string - for _, p := range container.Publishers { - if p.URL == "" { - ports = append(ports, fmt.Sprintf("%d/%s", p.TargetPort, p.Protocol)) - } else { - ports = append(ports, fmt.Sprintf("%s->%d/%s", p.URL, p.TargetPort, p.Protocol)) - } - } - status := container.State - if status == "running" && container.Health != "" { - status = fmt.Sprintf("%s (%s)", container.State, container.Health) - } else if status == "exited" || status == "dead" { - status = fmt.Sprintf("%s (%d)", container.State, container.ExitCode) - } - command := formatter2.Ellipsis(container.Command, 20) - _, _ = fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\n", container.Name, strconv.Quote(command), container.Service, status, strings.Join(ports, ", ")) - } - } -} - -func filterByStatus(containers []api.ContainerSummary, status string) []api.ContainerSummary { - hasContainerWithState := map[string]struct{}{} - for _, c := range containers { - if c.State == status { - hasContainerWithState[c.Service] = struct{}{} - } - } - var filtered []api.ContainerSummary - for _, c := range containers { - if _, ok := hasContainerWithState[c.Service]; ok { - filtered = append(filtered, c) - } - } - return filtered -} diff --git a/cmd/compose/pull.go b/cmd/compose/pull.go deleted file mode 100644 index c7213cc6..00000000 --- a/cmd/compose/pull.go +++ /dev/null @@ -1,93 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package compose - -import ( - "context" - "fmt" - "os" - - "github.com/morikuni/aec" - "github.com/spf13/cobra" - - "github.com/docker/compose-cli/pkg/api" - "github.com/docker/compose-cli/pkg/utils" -) - -type pullOptions struct { - *projectOptions - composeOptions - quiet bool - parallel bool - noParallel bool - includeDeps bool - ignorePullFailures bool -} - -func pullCommand(p *projectOptions, backend api.Service) *cobra.Command { - opts := pullOptions{ - projectOptions: p, - } - cmd := &cobra.Command{ - Use: "pull [SERVICE...]", - Short: "Pull service images", - PreRunE: Adapt(func(ctx context.Context, args []string) error { - if opts.noParallel { - fmt.Fprint(os.Stderr, aec.Apply("option '--no-parallel' is DEPRECATED and will be ignored.\n", aec.RedF)) - } - return nil - }), - RunE: Adapt(func(ctx context.Context, args []string) error { - return runPull(ctx, backend, opts, args) - }), - ValidArgsFunction: serviceCompletion(p), - } - flags := cmd.Flags() - flags.BoolVarP(&opts.quiet, "quiet", "q", false, "Pull without printing progress information") - cmd.Flags().BoolVar(&opts.includeDeps, "include-deps", false, "Also pull services declared as dependencies") - cmd.Flags().BoolVar(&opts.parallel, "parallel", true, "DEPRECATED pull multiple images in parallel.") - flags.MarkHidden("parallel") //nolint:errcheck - cmd.Flags().BoolVar(&opts.parallel, "no-parallel", true, "DEPRECATED disable parallel pulling.") - flags.MarkHidden("no-parallel") //nolint:errcheck - cmd.Flags().BoolVar(&opts.ignorePullFailures, "ignore-pull-failures", false, "Pull what it can and ignores images with pull failures") - return cmd -} - -func runPull(ctx context.Context, backend api.Service, opts pullOptions, services []string) error { - project, err := opts.toProject(services) - if err != nil { - return err - } - - if !opts.includeDeps { - enabled, err := project.GetServices(services...) - if err != nil { - return err - } - for _, s := range project.Services { - if !utils.StringContains(services, s.Name) { - project.DisabledServices = append(project.DisabledServices, s) - } - } - project.Services = enabled - } - - return backend.Pull(ctx, project, api.PullOptions{ - Quiet: opts.quiet, - IgnoreFailures: opts.ignorePullFailures, - }) -} diff --git a/cmd/compose/push.go b/cmd/compose/push.go deleted file mode 100644 index 906578dd..00000000 --- a/cmd/compose/push.go +++ /dev/null @@ -1,60 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package compose - -import ( - "context" - - "github.com/spf13/cobra" - - "github.com/docker/compose-cli/pkg/api" -) - -type pushOptions struct { - *projectOptions - composeOptions - - Ignorefailures bool -} - -func pushCommand(p *projectOptions, backend api.Service) *cobra.Command { - opts := pushOptions{ - projectOptions: p, - } - pushCmd := &cobra.Command{ - Use: "push [SERVICE...]", - Short: "Push service images", - RunE: Adapt(func(ctx context.Context, args []string) error { - return runPush(ctx, backend, opts, args) - }), - ValidArgsFunction: serviceCompletion(p), - } - pushCmd.Flags().BoolVar(&opts.Ignorefailures, "ignore-push-failures", false, "Push what it can and ignores images with push failures") - - return pushCmd -} - -func runPush(ctx context.Context, backend api.Service, opts pushOptions, services []string) error { - project, err := opts.toProject(services) - if err != nil { - return err - } - - return backend.Push(ctx, project, api.PushOptions{ - IgnoreFailures: opts.Ignorefailures, - }) -} diff --git a/cmd/compose/remove.go b/cmd/compose/remove.go deleted file mode 100644 index 5d41f728..00000000 --- a/cmd/compose/remove.go +++ /dev/null @@ -1,81 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package compose - -import ( - "context" - - "github.com/docker/compose-cli/pkg/api" - "github.com/spf13/cobra" -) - -type removeOptions struct { - *projectOptions - force bool - stop bool - volumes bool -} - -func removeCommand(p *projectOptions, backend api.Service) *cobra.Command { - opts := removeOptions{ - projectOptions: p, - } - cmd := &cobra.Command{ - Use: "rm [SERVICE...]", - Short: "Removes stopped service containers", - Long: `Removes stopped service containers - -By default, anonymous volumes attached to containers will not be removed. You -can override this with -v. To list all volumes, use "docker volume ls". - -Any data which is not in a volume will be lost.`, - RunE: Adapt(func(ctx context.Context, args []string) error { - return runRemove(ctx, backend, opts, args) - }), - ValidArgsFunction: serviceCompletion(p), - } - f := cmd.Flags() - f.BoolVarP(&opts.force, "force", "f", false, "Don't ask to confirm removal") - f.BoolVarP(&opts.stop, "stop", "s", false, "Stop the containers, if required, before removing") - f.BoolVarP(&opts.volumes, "volumes", "v", false, "Remove any anonymous volumes attached to containers") - f.BoolP("all", "a", false, "Deprecated - no effect") - f.MarkHidden("all") //nolint:errcheck - - return cmd -} - -func runRemove(ctx context.Context, backend api.Service, opts removeOptions, services []string) error { - project, err := opts.toProject(services) - if err != nil { - return err - } - - if opts.stop { - err := backend.Stop(ctx, project, api.StopOptions{ - Services: services, - }) - if err != nil { - return err - } - } - - return backend.Remove(ctx, project, api.RemoveOptions{ - Services: services, - Force: opts.force, - Volumes: opts.volumes, - }) -} diff --git a/cmd/compose/restart.go b/cmd/compose/restart.go deleted file mode 100644 index 8a022ed9..00000000 --- a/cmd/compose/restart.go +++ /dev/null @@ -1,62 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package compose - -import ( - "context" - "time" - - "github.com/spf13/cobra" - - "github.com/docker/compose-cli/pkg/api" -) - -type restartOptions struct { - *projectOptions - timeout int -} - -func restartCommand(p *projectOptions, backend api.Service) *cobra.Command { - opts := restartOptions{ - projectOptions: p, - } - restartCmd := &cobra.Command{ - Use: "restart", - Short: "Restart containers", - RunE: Adapt(func(ctx context.Context, args []string) error { - return runRestart(ctx, backend, opts, args) - }), - ValidArgsFunction: serviceCompletion(p), - } - flags := restartCmd.Flags() - flags.IntVarP(&opts.timeout, "timeout", "t", 10, "Specify a shutdown timeout in seconds") - - return restartCmd -} - -func runRestart(ctx context.Context, backend api.Service, opts restartOptions, services []string) error { - project, err := opts.toProject(services) - if err != nil { - return err - } - - timeout := time.Duration(opts.timeout) * time.Second - return backend.Restart(ctx, project, api.RestartOptions{ - Timeout: &timeout, - Services: services, - }) -} diff --git a/cmd/compose/run.go b/cmd/compose/run.go deleted file mode 100644 index cdaeeab0..00000000 --- a/cmd/compose/run.go +++ /dev/null @@ -1,232 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package compose - -import ( - "context" - "fmt" - "os" - "strings" - - "github.com/compose-spec/compose-go/loader" - "github.com/compose-spec/compose-go/types" - "github.com/mattn/go-isatty" - "github.com/mattn/go-shellwords" - "github.com/spf13/cobra" - - "github.com/docker/cli/cli" - "github.com/docker/compose-cli/pkg/api" - "github.com/docker/compose-cli/pkg/progress" -) - -type runOptions struct { - *composeOptions - Service string - Command []string - environment []string - Detach bool - Remove bool - noTty bool - user string - workdir string - entrypoint string - labels []string - volumes []string - publish []string - useAliases bool - servicePorts bool - name string - noDeps bool -} - -func (opts runOptions) apply(project *types.Project) error { - target, err := project.GetService(opts.Service) - if err != nil { - return err - } - if !opts.servicePorts { - target.Ports = []types.ServicePortConfig{} - } - if len(opts.publish) > 0 { - target.Ports = []types.ServicePortConfig{} - for _, p := range opts.publish { - config, err := types.ParsePortConfig(p) - if err != nil { - return err - } - target.Ports = append(target.Ports, config...) - } - } - if len(opts.volumes) > 0 { - for _, v := range opts.volumes { - volume, err := loader.ParseVolume(v) - if err != nil { - return err - } - target.Volumes = append(target.Volumes, volume) - } - } - - if opts.noDeps { - for _, s := range project.Services { - if s.Name != opts.Service { - project.DisabledServices = append(project.DisabledServices, s) - } - } - project.Services = types.Services{target} - } - - for i, s := range project.Services { - if s.Name == opts.Service { - project.Services[i] = target - break - } - } - return nil -} - -func runCommand(p *projectOptions, backend api.Service) *cobra.Command { - opts := runOptions{ - composeOptions: &composeOptions{ - projectOptions: p, - }, - } - cmd := &cobra.Command{ - Use: "run [options] [-v VOLUME...] [-p PORT...] [-e KEY=VAL...] [-l KEY=VALUE...] SERVICE [COMMAND] [ARGS...]", - Short: "Run a one-off command on a service.", - Args: cobra.MinimumNArgs(1), - PreRunE: Adapt(func(ctx context.Context, args []string) error { - opts.Service = args[0] - if len(args) > 1 { - opts.Command = args[1:] - } - if len(opts.publish) > 0 && opts.servicePorts { - return fmt.Errorf("--service-ports and --publish are incompatible") - } - return nil - }), - RunE: Adapt(func(ctx context.Context, args []string) error { - project, err := p.toProject([]string{opts.Service}) - if err != nil { - return err - } - return runRun(ctx, backend, project, opts) - }), - ValidArgsFunction: serviceCompletion(p), - } - flags := cmd.Flags() - flags.BoolVarP(&opts.Detach, "detach", "d", false, "Run container in background and print container ID") - flags.StringArrayVarP(&opts.environment, "env", "e", []string{}, "Set environment variables") - flags.StringArrayVarP(&opts.labels, "labels", "l", []string{}, "Add or override a label") - flags.BoolVar(&opts.Remove, "rm", false, "Automatically remove the container when it exits") - flags.BoolVarP(&opts.noTty, "no-TTY", "T", notAtTTY(), "Disable pseudo-noTty allocation. By default docker compose run allocates a TTY") - flags.StringVar(&opts.name, "name", "", " Assign a name to the container") - flags.StringVarP(&opts.user, "user", "u", "", "Run as specified username or uid") - flags.StringVarP(&opts.workdir, "workdir", "w", "", "Working directory inside the container") - flags.StringVar(&opts.entrypoint, "entrypoint", "", "Override the entrypoint of the image") - flags.BoolVar(&opts.noDeps, "no-deps", false, "Don't start linked services.") - flags.StringArrayVarP(&opts.volumes, "volumes", "v", []string{}, "Bind mount a volume.") - flags.StringArrayVarP(&opts.publish, "publish", "p", []string{}, "Publish a container's port(s) to the host.") - flags.BoolVar(&opts.useAliases, "use-aliases", false, "Use the service's network useAliases in the network(s) the container connects to.") - flags.BoolVar(&opts.servicePorts, "service-ports", false, "Run command with the service's ports enabled and mapped to the host.") - - flags.SetInterspersed(false) - return cmd -} - -func notAtTTY() bool { - return !isatty.IsTerminal(os.Stdout.Fd()) -} - -func runRun(ctx context.Context, backend api.Service, project *types.Project, opts runOptions) error { - err := opts.apply(project) - if err != nil { - return err - } - - err = progress.Run(ctx, func(ctx context.Context) error { - return startDependencies(ctx, backend, *project, opts.Service) - }) - if err != nil { - return err - } - - var entrypoint []string - if opts.entrypoint != "" { - entrypoint, err = shellwords.Parse(opts.entrypoint) - if err != nil { - return err - } - } - - labels := types.Labels{} - for _, s := range opts.labels { - parts := strings.SplitN(s, "=", 2) - if len(parts) != 2 { - return fmt.Errorf("label must be set as KEY=VALUE") - } - labels[parts[0]] = parts[1] - } - - // start container and attach to container streams - runOpts := api.RunOptions{ - Name: opts.name, - Service: opts.Service, - Command: opts.Command, - Detach: opts.Detach, - AutoRemove: opts.Remove, - Stdin: os.Stdin, - Stdout: os.Stdout, - Stderr: os.Stderr, - Tty: !opts.noTty, - WorkingDir: opts.workdir, - User: opts.user, - Environment: opts.environment, - Entrypoint: entrypoint, - Labels: labels, - UseNetworkAliases: opts.useAliases, - Index: 0, - } - exitCode, err := backend.RunOneOffContainer(ctx, project, runOpts) - if exitCode != 0 { - errMsg := "" - if err != nil { - errMsg = err.Error() - } - return cli.StatusError{StatusCode: exitCode, Status: errMsg} - } - return err -} - -func startDependencies(ctx context.Context, backend api.Service, project types.Project, requestedServiceName string) error { - dependencies := types.Services{} - var requestedService types.ServiceConfig - for _, service := range project.Services { - if service.Name != requestedServiceName { - dependencies = append(dependencies, service) - } else { - requestedService = service - } - } - - project.Services = dependencies - project.DisabledServices = append(project.DisabledServices, requestedService) - if err := backend.Create(ctx, &project, api.CreateOptions{}); err != nil { - return err - } - return backend.Start(ctx, &project, api.StartOptions{}) -} diff --git a/cmd/compose/start.go b/cmd/compose/start.go deleted file mode 100644 index d3d46245..00000000 --- a/cmd/compose/start.go +++ /dev/null @@ -1,52 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package compose - -import ( - "context" - - "github.com/docker/compose-cli/pkg/api" - "github.com/spf13/cobra" -) - -type startOptions struct { - *projectOptions -} - -func startCommand(p *projectOptions, backend api.Service) *cobra.Command { - opts := startOptions{ - projectOptions: p, - } - startCmd := &cobra.Command{ - Use: "start [SERVICE...]", - Short: "Start services", - RunE: Adapt(func(ctx context.Context, args []string) error { - return runStart(ctx, backend, opts, args) - }), - ValidArgsFunction: serviceCompletion(p), - } - return startCmd -} - -func runStart(ctx context.Context, backend api.Service, opts startOptions, services []string) error { - project, err := opts.toProject(services) - if err != nil { - return err - } - - return backend.Start(ctx, project, api.StartOptions{}) -} diff --git a/cmd/compose/stop.go b/cmd/compose/stop.go deleted file mode 100644 index 81b90ad9..00000000 --- a/cmd/compose/stop.go +++ /dev/null @@ -1,70 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package compose - -import ( - "context" - "time" - - "github.com/spf13/cobra" - - "github.com/docker/compose-cli/pkg/api" -) - -type stopOptions struct { - *projectOptions - timeChanged bool - timeout int -} - -func stopCommand(p *projectOptions, backend api.Service) *cobra.Command { - opts := stopOptions{ - projectOptions: p, - } - cmd := &cobra.Command{ - Use: "stop [SERVICE...]", - Short: "Stop services", - PreRun: func(cmd *cobra.Command, args []string) { - opts.timeChanged = cmd.Flags().Changed("timeout") - }, - RunE: Adapt(func(ctx context.Context, args []string) error { - return runStop(ctx, backend, opts, args) - }), - ValidArgsFunction: serviceCompletion(p), - } - flags := cmd.Flags() - flags.IntVarP(&opts.timeout, "timeout", "t", 10, "Specify a shutdown timeout in seconds") - - return cmd -} - -func runStop(ctx context.Context, backend api.Service, opts stopOptions, services []string) error { - project, err := opts.toProject(services) - if err != nil { - return err - } - - var timeout *time.Duration - if opts.timeChanged { - timeoutValue := time.Duration(opts.timeout) * time.Second - timeout = &timeoutValue - } - return backend.Stop(ctx, project, api.StopOptions{ - Timeout: timeout, - Services: services, - }) -} diff --git a/cmd/compose/top.go b/cmd/compose/top.go deleted file mode 100644 index 441deeaa..00000000 --- a/cmd/compose/top.go +++ /dev/null @@ -1,92 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package compose - -import ( - "context" - "fmt" - "io" - "os" - "sort" - "strings" - "text/tabwriter" - - "github.com/spf13/cobra" - - "github.com/docker/compose-cli/pkg/api" -) - -type topOptions struct { - *projectOptions -} - -func topCommand(p *projectOptions, backend api.Service) *cobra.Command { - opts := topOptions{ - projectOptions: p, - } - topCmd := &cobra.Command{ - Use: "top [SERVICES...]", - Short: "Display the running processes", - RunE: Adapt(func(ctx context.Context, args []string) error { - return runTop(ctx, backend, opts, args) - }), - ValidArgsFunction: serviceCompletion(p), - } - return topCmd -} - -func runTop(ctx context.Context, backend api.Service, opts topOptions, services []string) error { - projectName, err := opts.toProjectName() - if err != nil { - return err - } - containers, err := backend.Top(ctx, projectName, services) - if err != nil { - return err - } - - sort.Slice(containers, func(i, j int) bool { - return containers[i].Name < containers[j].Name - }) - - for _, container := range containers { - fmt.Printf("%s\n", container.Name) - err := psPrinter(os.Stdout, func(w io.Writer) { - for _, proc := range container.Processes { - info := []interface{}{} - for _, p := range proc { - info = append(info, p) - } - _, _ = fmt.Fprintf(w, strings.Repeat("%s\t", len(info))+"\n", info...) - - } - fmt.Fprintln(w) - }, - container.Titles...) - if err != nil { - return err - } - } - return nil -} - -func psPrinter(out io.Writer, printer func(writer io.Writer), headers ...string) error { - w := tabwriter.NewWriter(out, 5, 1, 3, ' ', 0) - _, _ = fmt.Fprintln(w, strings.Join(headers, "\t")) - printer(w) - return w.Flush() -} diff --git a/cmd/compose/up.go b/cmd/compose/up.go deleted file mode 100644 index a056c83b..00000000 --- a/cmd/compose/up.go +++ /dev/null @@ -1,209 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package compose - -import ( - "context" - "fmt" - "os" - "strconv" - "strings" - - "github.com/docker/compose-cli/cmd/formatter" - - "github.com/compose-spec/compose-go/types" - "github.com/spf13/cobra" - - "github.com/docker/compose-cli/pkg/api" - "github.com/docker/compose-cli/pkg/utils" -) - -// composeOptions hold options common to `up` and `run` to run compose project -type composeOptions struct { - *projectOptions -} - -type upOptions struct { - *composeOptions - Detach bool - Environment []string - noStart bool - noDeps bool - cascadeStop bool - exitCodeFrom string - scale []string - noColor bool - noPrefix bool - attachDependencies bool -} - -func (opts upOptions) apply(project *types.Project, services []string) error { - if opts.noDeps { - enabled, err := project.GetServices(services...) - if err != nil { - return err - } - for _, s := range project.Services { - if !utils.StringContains(services, s.Name) { - project.DisabledServices = append(project.DisabledServices, s) - } - } - project.Services = enabled - } - - if opts.exitCodeFrom != "" { - _, err := project.GetService(opts.exitCodeFrom) - if err != nil { - return err - } - } - - for _, scale := range opts.scale { - split := strings.Split(scale, "=") - if len(split) != 2 { - return fmt.Errorf("invalid --scale option %q. Should be SERVICE=NUM", scale) - } - name := split[0] - replicas, err := strconv.Atoi(split[1]) - if err != nil { - return err - } - err = setServiceScale(project, name, replicas) - if err != nil { - return err - } - } - - return nil -} - -func upCommand(p *projectOptions, backend api.Service) *cobra.Command { - up := upOptions{} - create := createOptions{} - upCmd := &cobra.Command{ - Use: "up [SERVICE...]", - Short: "Create and start containers", - PreRun: func(cmd *cobra.Command, args []string) { - create.timeChanged = cmd.Flags().Changed("timeout") - }, - PreRunE: Adapt(func(ctx context.Context, args []string) error { - if up.exitCodeFrom != "" { - up.cascadeStop = true - } - if create.Build && create.noBuild { - return fmt.Errorf("--build and --no-build are incompatible") - } - if up.Detach && (up.attachDependencies || up.cascadeStop) { - return fmt.Errorf("--detach cannot be combined with --abort-on-container-exit or --attach-dependencies") - } - if create.forceRecreate && create.noRecreate { - return fmt.Errorf("--force-recreate and --no-recreate are incompatible") - } - if create.recreateDeps && create.noRecreate { - return fmt.Errorf("--always-recreate-deps and --no-recreate are incompatible") - } - return nil - }), - RunE: p.WithServices(func(ctx context.Context, project *types.Project, services []string) error { - return runUp(ctx, backend, create, up, project, services) - }), - ValidArgsFunction: serviceCompletion(p), - } - flags := upCmd.Flags() - flags.StringArrayVarP(&up.Environment, "environment", "e", []string{}, "Environment variables") - flags.BoolVarP(&up.Detach, "detach", "d", false, "Detached mode: Run containers in the background") - flags.BoolVar(&create.Build, "build", false, "Build images before starting containers.") - flags.BoolVar(&create.noBuild, "no-build", false, "Don't build an image, even if it's missing.") - flags.BoolVar(&create.removeOrphans, "remove-orphans", false, "Remove containers for services not defined in the Compose file.") - flags.StringArrayVar(&up.scale, "scale", []string{}, "Scale SERVICE to NUM instances. Overrides the `scale` setting in the Compose file if present.") - flags.BoolVar(&up.noColor, "no-color", false, "Produce monochrome output.") - flags.BoolVar(&up.noPrefix, "no-log-prefix", false, "Don't print prefix in logs.") - flags.BoolVar(&create.forceRecreate, "force-recreate", false, "Recreate containers even if their configuration and image haven't changed.") - flags.BoolVar(&create.noRecreate, "no-recreate", false, "If containers already exist, don't recreate them. Incompatible with --force-recreate.") - flags.BoolVar(&up.noStart, "no-start", false, "Don't start the services after creating them.") - flags.BoolVar(&up.cascadeStop, "abort-on-container-exit", false, "Stops all containers if any container was stopped. Incompatible with -d") - flags.StringVar(&up.exitCodeFrom, "exit-code-from", "", "Return the exit code of the selected service container. Implies --abort-on-container-exit") - flags.IntVarP(&create.timeout, "timeout", "t", 10, "Use this timeout in seconds for container shutdown when attached or when containers are already running.") - flags.BoolVar(&up.noDeps, "no-deps", false, "Don't start linked services.") - flags.BoolVar(&create.recreateDeps, "always-recreate-deps", false, "Recreate dependent containers. Incompatible with --no-recreate.") - flags.BoolVarP(&create.noInherit, "renew-anon-volumes", "V", false, "Recreate anonymous volumes instead of retrieving data from the previous containers.") - flags.BoolVar(&up.attachDependencies, "attach-dependencies", false, "Attach to dependent containers.") - flags.BoolVar(&create.quietPull, "quiet-pull", false, "Pull without printing progress information.") - - return upCmd -} - -func runUp(ctx context.Context, backend api.Service, createOptions createOptions, upOptions upOptions, project *types.Project, services []string) error { - if len(project.Services) == 0 { - return fmt.Errorf("no service selected") - } - - createOptions.Apply(project) - - err := upOptions.apply(project, services) - if err != nil { - return err - } - - var consumer api.LogConsumer - if !upOptions.Detach { - consumer = formatter.NewLogConsumer(ctx, os.Stdout, !upOptions.noColor, !upOptions.noPrefix) - } - - attachTo := services - if upOptions.attachDependencies { - attachTo = project.ServiceNames() - } - - create := api.CreateOptions{ - RemoveOrphans: createOptions.removeOrphans, - Recreate: createOptions.recreateStrategy(), - RecreateDependencies: createOptions.dependenciesRecreateStrategy(), - Inherit: !createOptions.noInherit, - Timeout: createOptions.GetTimeout(), - QuietPull: createOptions.quietPull, - } - - if upOptions.noStart { - return backend.Create(ctx, project, create) - } - - return backend.Up(ctx, project, api.UpOptions{ - Create: create, - Start: api.StartOptions{ - Attach: consumer, - AttachTo: attachTo, - ExitCodeFrom: upOptions.exitCodeFrom, - CascadeStop: upOptions.cascadeStop, - }, - }) -} - -func setServiceScale(project *types.Project, name string, replicas int) error { - for i, s := range project.Services { - if s.Name == name { - service, err := project.GetService(name) - if err != nil { - return err - } - service.Scale = replicas - project.Services[i] = service - return nil - } - } - return fmt.Errorf("unknown service %q", name) -} diff --git a/cmd/compose/up_test.go b/cmd/compose/up_test.go deleted file mode 100644 index 4042d9ca..00000000 --- a/cmd/compose/up_test.go +++ /dev/null @@ -1,43 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package compose - -import ( - "testing" - - "github.com/compose-spec/compose-go/types" - "gotest.tools/v3/assert" -) - -func TestApplyScaleOpt(t *testing.T) { - p := types.Project{ - Services: []types.ServiceConfig{ - { - Name: "foo", - }, - { - Name: "bar", - }, - }, - } - opt := upOptions{scale: []string{"foo=2"}} - err := opt.apply(&p, nil) - assert.NilError(t, err) - foo, err := p.GetService("foo") - assert.NilError(t, err) - assert.Equal(t, foo.Scale, 2) -} diff --git a/cmd/compose/version.go b/cmd/compose/version.go deleted file mode 100644 index b366a40c..00000000 --- a/cmd/compose/version.go +++ /dev/null @@ -1,64 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package compose - -import ( - "fmt" - - "github.com/docker/compose-cli/cmd/formatter" - - "github.com/spf13/cobra" - - "github.com/docker/compose-cli/internal" -) - -type versionOptions struct { - format string - short bool -} - -func versionCommand() *cobra.Command { - opts := versionOptions{} - cmd := &cobra.Command{ - Use: "version", - Short: "Show the Docker Compose version information", - Args: cobra.MaximumNArgs(0), - Hidden: true, - RunE: func(cmd *cobra.Command, _ []string) error { - runVersion(opts) - return nil - }, - } - // define flags for backward compatibility with com.docker.cli - flags := cmd.Flags() - flags.StringVarP(&opts.format, "format", "f", "", "Format the output. Values: [pretty | json]. (Default: pretty)") - flags.BoolVar(&opts.short, "short", false, "Shows only Compose's version number.") - - return cmd -} - -func runVersion(opts versionOptions) { - if opts.short { - fmt.Println(internal.Version) - return - } - if opts.format == formatter.JSON { - fmt.Printf(`{"version":%q}\n`, internal.Version) - return - } - fmt.Println("Docker Compose version", internal.Version) -} diff --git a/cmd/formatter/colors.go b/cmd/formatter/colors.go deleted file mode 100644 index 8c248088..00000000 --- a/cmd/formatter/colors.go +++ /dev/null @@ -1,124 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package formatter - -import ( - "fmt" - "os" - "strconv" - - "github.com/mattn/go-isatty" -) - -var names = []string{ - "grey", - "red", - "green", - "yellow", - "blue", - "magenta", - "cyan", - "white", -} - -const ( - // Never use ANSI codes - Never = "never" - - // Always use ANSI codes - Always = "always" - - // Auto detect terminal is a tty and can use ANSI codes - Auto = "auto" -) - -// SetANSIMode configure formatter for colored output on ANSI-compliant console -func SetANSIMode(ansi string) { - if !useAnsi(ansi) { - nextColor = func() colorFunc { - return monochrome - } - } -} - -func useAnsi(ansi string) bool { - switch ansi { - case Always: - return true - case Auto: - return isatty.IsTerminal(os.Stdout.Fd()) - } - return false -} - -// colorFunc use ANSI codes to render colored text on console -type colorFunc func(s string) string - -var monochrome = func(s string) string { - return s -} - -func ansiColor(code, s string) string { - return fmt.Sprintf("%s%s%s", ansi(code), s, ansi("0")) -} - -func ansi(code string) string { - return fmt.Sprintf("\033[%sm", code) -} - -func makeColorFunc(code string) colorFunc { - return func(s string) string { - return ansiColor(code, s) - } -} - -var nextColor = rainbowColor - -func rainbowColor() colorFunc { - return <-loop -} - -var loop = make(chan colorFunc) - -func init() { - colors := map[string]colorFunc{} - for i, name := range names { - colors[name] = makeColorFunc(strconv.Itoa(30 + i)) - colors["intense_"+name] = makeColorFunc(strconv.Itoa(30+i) + ";1") - } - - go func() { - i := 0 - rainbow := []colorFunc{ - colors["cyan"], - colors["yellow"], - colors["green"], - colors["magenta"], - colors["blue"], - colors["intense_cyan"], - colors["intense_yellow"], - colors["intense_green"], - colors["intense_magenta"], - colors["intense_blue"], - } - - for { - loop <- rainbow[i] - i = (i + 1) % len(rainbow) - } - }() -} diff --git a/cmd/formatter/consts.go b/cmd/formatter/consts.go deleted file mode 100644 index 0bb06bf7..00000000 --- a/cmd/formatter/consts.go +++ /dev/null @@ -1,26 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package formatter - -const ( - // JSON is the constant for Json formats on list commands - JSON = "json" - // TemplateLegacyJSON the legacy json formatting value using go template - TemplateLegacyJSON = "{{json.}}" - // PRETTY is the constant for default formats on list commands - PRETTY = "pretty" -) diff --git a/cmd/formatter/formatter.go b/cmd/formatter/formatter.go deleted file mode 100644 index c0452781..00000000 --- a/cmd/formatter/formatter.go +++ /dev/null @@ -1,73 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package formatter - -import ( - "fmt" - "io" - "reflect" - "strings" - - "github.com/docker/compose-cli/pkg/api" - - "github.com/pkg/errors" -) - -// Print prints formatted lists in different formats -func Print(toJSON interface{}, format string, outWriter io.Writer, writerFn func(w io.Writer), headers ...string) error { - switch strings.ToLower(format) { - case PRETTY, "": - return PrintPrettySection(outWriter, writerFn, headers...) - case TemplateLegacyJSON: - switch reflect.TypeOf(toJSON).Kind() { - case reflect.Slice: - s := reflect.ValueOf(toJSON) - for i := 0; i < s.Len(); i++ { - obj := s.Index(i).Interface() - outJSON, err := ToJSON(obj, "", "") - if err != nil { - return err - } - _, _ = fmt.Fprint(outWriter, outJSON) - } - default: - outJSON, err := ToStandardJSON(toJSON) - if err != nil { - return err - } - _, _ = fmt.Fprintln(outWriter, outJSON) - } - case JSON: - switch reflect.TypeOf(toJSON).Kind() { - case reflect.Slice: - outJSON, err := ToJSON(toJSON, "", "") - if err != nil { - return err - } - _, _ = fmt.Fprint(outWriter, outJSON) - default: - outJSON, err := ToStandardJSON(toJSON) - if err != nil { - return err - } - _, _ = fmt.Fprintln(outWriter, outJSON) - } - default: - return errors.Wrapf(api.ErrParsingFailed, "format value %q could not be parsed", format) - } - return nil -} diff --git a/cmd/formatter/formatter_test.go b/cmd/formatter/formatter_test.go deleted file mode 100644 index be7f0bd2..00000000 --- a/cmd/formatter/formatter_test.go +++ /dev/null @@ -1,73 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package formatter - -import ( - "bytes" - "fmt" - "io" - "testing" - - "gotest.tools/assert" -) - -type testStruct struct { - Name string - Status string -} - -// Print prints formatted lists in different formats -func TestPrint(t *testing.T) { - testList := []testStruct{ - { - Name: "myName1", - Status: "myStatus1", - }, - { - Name: "myName2", - Status: "myStatus2", - }, - } - - b := &bytes.Buffer{} - assert.NilError(t, Print(testList, PRETTY, b, func(w io.Writer) { - for _, t := range testList { - _, _ = fmt.Fprintf(w, "%s\t%s\n", t.Name, t.Status) - } - }, "NAME", "STATUS")) - assert.Equal(t, b.String(), "NAME STATUS\nmyName1 myStatus1\nmyName2 myStatus2\n") - - b.Reset() - assert.NilError(t, Print(testList, JSON, b, func(w io.Writer) { - for _, t := range testList { - _, _ = fmt.Fprintf(w, "%s\t%s\n", t.Name, t.Status) - } - }, "NAME", "STATUS")) - assert.Equal(t, b.String(), `[{"Name":"myName1","Status":"myStatus1"},{"Name":"myName2","Status":"myStatus2"}] -`) - - b.Reset() - assert.NilError(t, Print(testList, TemplateLegacyJSON, b, func(w io.Writer) { - for _, t := range testList { - _, _ = fmt.Fprintf(w, "%s\t%s\n", t.Name, t.Status) - } - }, "NAME", "STATUS")) - json := b.String() - assert.Equal(t, json, `{"Name":"myName1","Status":"myStatus1"} -{"Name":"myName2","Status":"myStatus2"} -`) -} diff --git a/cmd/formatter/json.go b/cmd/formatter/json.go deleted file mode 100644 index afadb0c8..00000000 --- a/cmd/formatter/json.go +++ /dev/null @@ -1,39 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package formatter - -import ( - "bytes" - "encoding/json" -) - -const standardIndentation = " " - -// ToStandardJSON return a string with the JSON representation of the interface{} -func ToStandardJSON(i interface{}) (string, error) { - return ToJSON(i, "", standardIndentation) -} - -// ToJSON return a string with the JSON representation of the interface{} -func ToJSON(i interface{}, prefix string, indentation string) (string, error) { - buffer := &bytes.Buffer{} - encoder := json.NewEncoder(buffer) - encoder.SetEscapeHTML(false) - encoder.SetIndent(prefix, indentation) - err := encoder.Encode(i) - return buffer.String(), err -} diff --git a/cmd/formatter/logs.go b/cmd/formatter/logs.go deleted file mode 100644 index 18f45cc9..00000000 --- a/cmd/formatter/logs.go +++ /dev/null @@ -1,115 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package formatter - -import ( - "context" - "fmt" - "io" - "strconv" - "strings" - - "github.com/docker/compose-cli/pkg/api" -) - -// NewLogConsumer creates a new LogConsumer -func NewLogConsumer(ctx context.Context, w io.Writer, color bool, prefix bool) api.LogConsumer { - return &logConsumer{ - ctx: ctx, - presenters: map[string]*presenter{}, - width: 0, - writer: w, - color: color, - prefix: prefix, - } -} - -func (l *logConsumer) Register(name string) { - l.register(name) -} - -func (l *logConsumer) register(name string) *presenter { - cf := monochrome - if l.color { - cf = nextColor() - } - p := &presenter{ - colors: cf, - name: name, - } - l.presenters[name] = p - if l.prefix { - l.computeWidth() - for _, p := range l.presenters { - p.setPrefix(l.width) - } - } - return p -} - -// Log formats a log message as received from name/container -func (l *logConsumer) Log(container, service, message string) { - if l.ctx.Err() != nil { - return - } - p, ok := l.presenters[container] - if !ok { // should have been registered, but ¯\_(ツ)_/¯ - p = l.register(container) - } - for _, line := range strings.Split(message, "\n") { - fmt.Fprintf(l.writer, "%s %s\n", p.prefix, line) // nolint:errcheck - } -} - -func (l *logConsumer) Status(container, msg string) { - p, ok := l.presenters[container] - if !ok { - p = l.register(container) - } - s := p.colors(fmt.Sprintf("%s %s\n", container, msg)) - l.writer.Write([]byte(s)) // nolint:errcheck -} - -func (l *logConsumer) computeWidth() { - width := 0 - for _, p := range l.presenters { - if len(p.name) > width { - width = len(p.name) - } - } - l.width = width + 1 -} - -// LogConsumer consume logs from services and format them -type logConsumer struct { - ctx context.Context - presenters map[string]*presenter - width int - writer io.Writer - color bool - prefix bool -} - -type presenter struct { - colors colorFunc - name string - prefix string -} - -func (p *presenter) setPrefix(width int) { - p.prefix = p.colors(fmt.Sprintf("%-"+strconv.Itoa(width)+"s |", p.name)) -} diff --git a/cmd/formatter/multierrformat.go b/cmd/formatter/multierrformat.go deleted file mode 100644 index 28d538f5..00000000 --- a/cmd/formatter/multierrformat.go +++ /dev/null @@ -1,38 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package formatter - -import ( - "strings" - - "github.com/hashicorp/go-multierror" -) - -// SetMultiErrorFormat set cli default format for multi-errors -func SetMultiErrorFormat(errs *multierror.Error) { - if errs != nil { - errs.ErrorFormat = formatErrors - } -} - -func formatErrors(errs []error) string { - messages := make([]string, len(errs)) - for i, err := range errs { - messages[i] = "Error: " + err.Error() - } - return strings.Join(messages, "\n") -} diff --git a/cmd/formatter/pretty.go b/cmd/formatter/pretty.go deleted file mode 100644 index bb85dede..00000000 --- a/cmd/formatter/pretty.go +++ /dev/null @@ -1,32 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package formatter - -import ( - "fmt" - "io" - "strings" - "text/tabwriter" -) - -// PrintPrettySection prints a tabbed section on the writer parameter -func PrintPrettySection(out io.Writer, printer func(writer io.Writer), headers ...string) error { - w := tabwriter.NewWriter(out, 20, 1, 3, ' ', 0) - _, _ = fmt.Fprintln(w, strings.Join(headers, "\t")) - printer(w) - return w.Flush() -} diff --git a/cmd/main.go b/cmd/main.go deleted file mode 100644 index af2e64b6..00000000 --- a/cmd/main.go +++ /dev/null @@ -1,65 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package main - -import ( - dockercli "github.com/docker/cli/cli" - "github.com/docker/cli/cli-plugins/manager" - "github.com/docker/cli/cli-plugins/plugin" - "github.com/docker/cli/cli/command" - "github.com/spf13/cobra" - - commands "github.com/docker/compose-cli/cmd/compose" - "github.com/docker/compose-cli/internal" - "github.com/docker/compose-cli/pkg/api" - "github.com/docker/compose-cli/pkg/compose" -) - -func init() { - commands.Warning = "The new 'docker compose' command is currently experimental. " + - "To provide feedback or request new features please open issues at https://github.com/docker/compose-cli" -} - -func main() { - plugin.Run(func(dockerCli command.Cli) *cobra.Command { - lazyInit := api.NewServiceProxy() - cmd := commands.RootCommand(lazyInit) - originalPreRun := cmd.PersistentPreRunE - cmd.PersistentPreRunE = func(cmd *cobra.Command, args []string) error { - if err := plugin.PersistentPreRunE(cmd, args); err != nil { - return err - } - lazyInit.WithService(compose.NewComposeService(dockerCli.Client(), dockerCli.ConfigFile())) - if originalPreRun != nil { - return originalPreRun(cmd, args) - } - return nil - } - cmd.SetFlagErrorFunc(func(c *cobra.Command, err error) error { - return dockercli.StatusError{ - StatusCode: compose.CommandSyntaxFailure.ExitCode, - Status: err.Error(), - } - }) - return cmd - }, - manager.Metadata{ - SchemaVersion: "0.1.0", - Vendor: "Docker Inc.", - Version: internal.Version, - }) -} diff --git a/docs/yaml/main/generate.go b/docs/yaml/main/generate.go index 00108de4..40d29417 100644 --- a/docs/yaml/main/generate.go +++ b/docs/yaml/main/generate.go @@ -27,7 +27,8 @@ import ( "github.com/spf13/cobra" "github.com/spf13/pflag" - "github.com/docker/compose-cli/cmd/compose" + "github.com/docker/compose/v2/cmd/compose" + . "github.com/docker/compose-cli/docs/yaml" ) diff --git a/ecs/aws.go b/ecs/aws.go index 45b79ba0..51936bf4 100644 --- a/ecs/aws.go +++ b/ecs/aws.go @@ -21,9 +21,9 @@ import ( "github.com/aws/aws-sdk-go/service/cloudformation" "github.com/aws/aws-sdk-go/service/ecs" + "github.com/docker/compose/v2/pkg/api" "github.com/docker/compose-cli/api/secrets" - "github.com/docker/compose-cli/pkg/api" ) const ( diff --git a/ecs/awsResources.go b/ecs/awsResources.go index dfb0e4c1..7abb7cf9 100644 --- a/ecs/awsResources.go +++ b/ecs/awsResources.go @@ -30,7 +30,7 @@ import ( "github.com/awslabs/goformation/v4/cloudformation/efs" "github.com/awslabs/goformation/v4/cloudformation/elasticloadbalancingv2" "github.com/compose-spec/compose-go/types" - "github.com/docker/compose-cli/pkg/api" + "github.com/docker/compose/v2/pkg/api" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) diff --git a/ecs/aws_mock.go b/ecs/aws_mock.go index 3e16ae51..879e744f 100644 --- a/ecs/aws_mock.go +++ b/ecs/aws_mock.go @@ -6,12 +6,14 @@ package ecs import ( context "context" + reflect "reflect" + cloudformation "github.com/aws/aws-sdk-go/service/cloudformation" ecs "github.com/aws/aws-sdk-go/service/ecs" - secrets "github.com/docker/compose-cli/api/secrets" - compose "github.com/docker/compose-cli/pkg/api" + compose "github.com/docker/compose/v2/pkg/api" gomock "github.com/golang/mock/gomock" - reflect "reflect" + + secrets "github.com/docker/compose-cli/api/secrets" ) // MockAPI is a mock of API interface diff --git a/ecs/backend.go b/ecs/backend.go index 7fd06d0a..b176cfb7 100644 --- a/ecs/backend.go +++ b/ecs/backend.go @@ -21,7 +21,6 @@ import ( "fmt" "github.com/docker/compose-cli/api/backend" - "github.com/docker/compose-cli/api/cloud" "github.com/docker/compose-cli/api/containers" apicontext "github.com/docker/compose-cli/api/context" @@ -29,10 +28,10 @@ import ( "github.com/docker/compose-cli/api/resources" "github.com/docker/compose-cli/api/secrets" "github.com/docker/compose-cli/api/volumes" - "github.com/docker/compose-cli/pkg/api" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/session" + "github.com/docker/compose/v2/pkg/api" ) const backendType = store.EcsContextType diff --git a/ecs/cloudformation.go b/ecs/cloudformation.go index 8577fbd0..5d3a6176 100644 --- a/ecs/cloudformation.go +++ b/ecs/cloudformation.go @@ -38,11 +38,12 @@ import ( "github.com/compose-spec/compose-go/types" "github.com/distribution/distribution/v3/reference" cliconfig "github.com/docker/cli/cli/config" - "github.com/docker/compose-cli/api/config" - "github.com/docker/compose-cli/pkg/api" + "github.com/docker/compose/v2/pkg/api" "github.com/opencontainers/go-digest" "sigs.k8s.io/kustomize/kyaml/yaml" "sigs.k8s.io/kustomize/kyaml/yaml/merge2" + + "github.com/docker/compose-cli/api/config" ) func (b *ecsAPIService) Kill(ctx context.Context, project *types.Project, options api.KillOptions) error { diff --git a/ecs/cloudformation_test.go b/ecs/cloudformation_test.go index eb70773b..a3fc7bd7 100644 --- a/ecs/cloudformation_test.go +++ b/ecs/cloudformation_test.go @@ -23,8 +23,6 @@ import ( "reflect" "testing" - "github.com/docker/compose-cli/pkg/api" - "github.com/aws/aws-sdk-go/service/elbv2" "github.com/awslabs/goformation/v4/cloudformation" "github.com/awslabs/goformation/v4/cloudformation/ec2" @@ -35,6 +33,7 @@ import ( "github.com/awslabs/goformation/v4/cloudformation/logs" "github.com/compose-spec/compose-go/loader" "github.com/compose-spec/compose-go/types" + "github.com/docker/compose/v2/pkg/api" "github.com/golang/mock/gomock" "gotest.tools/v3/assert" "gotest.tools/v3/golden" diff --git a/ecs/context.go b/ecs/context.go index 4566b591..87b2a44e 100644 --- a/ecs/context.go +++ b/ecs/context.go @@ -24,18 +24,18 @@ import ( "sort" "strings" - "github.com/docker/compose-cli/pkg/prompt" - "github.com/AlecAivazis/survey/v2/terminal" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/credentials" "github.com/aws/aws-sdk-go/aws/defaults" "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/ec2" - "github.com/docker/compose-cli/api/context/store" - "github.com/docker/compose-cli/pkg/api" + "github.com/docker/compose/v2/pkg/api" + "github.com/docker/compose/v2/pkg/prompt" "github.com/pkg/errors" "gopkg.in/ini.v1" + + "github.com/docker/compose-cli/api/context/store" ) func getEnvVars() ContextParams { diff --git a/ecs/context_test.go b/ecs/context_test.go index 1889e70f..54e2728c 100644 --- a/ecs/context_test.go +++ b/ecs/context_test.go @@ -21,13 +21,13 @@ import ( "os" "testing" - "github.com/docker/compose-cli/api/context/store" - "github.com/docker/compose-cli/pkg/prompt" - + "github.com/docker/compose/v2/pkg/prompt" "github.com/golang/mock/gomock" "gotest.tools/v3/assert" "gotest.tools/v3/fs" "gotest.tools/v3/golden" + + "github.com/docker/compose-cli/api/context/store" ) func TestCreateContextDataFromEnv(t *testing.T) { diff --git a/ecs/down.go b/ecs/down.go index 3c12ec0e..45ae72dd 100644 --- a/ecs/down.go +++ b/ecs/down.go @@ -19,10 +19,9 @@ package ecs import ( "context" + "github.com/docker/compose/v2/pkg/api" + "github.com/docker/compose/v2/pkg/progress" "github.com/pkg/errors" - - "github.com/docker/compose-cli/pkg/api" - "github.com/docker/compose-cli/pkg/progress" ) func (b *ecsAPIService) Down(ctx context.Context, projectName string, options api.DownOptions) error { diff --git a/ecs/exec.go b/ecs/exec.go index 9fc7c479..3d6d05e4 100644 --- a/ecs/exec.go +++ b/ecs/exec.go @@ -20,8 +20,7 @@ import ( "context" "github.com/compose-spec/compose-go/types" - - "github.com/docker/compose-cli/pkg/api" + "github.com/docker/compose/v2/pkg/api" ) func (b *ecsAPIService) Exec(ctx context.Context, project *types.Project, opts api.RunOptions) (int, error) { diff --git a/ecs/images.go b/ecs/images.go index d713047e..525025e6 100644 --- a/ecs/images.go +++ b/ecs/images.go @@ -19,7 +19,7 @@ package ecs import ( "context" - "github.com/docker/compose-cli/pkg/api" + "github.com/docker/compose/v2/pkg/api" ) func (b *ecsAPIService) Images(ctx context.Context, projectName string, options api.ImagesOptions) ([]api.ImageSummary, error) { diff --git a/ecs/list.go b/ecs/list.go index 378ef972..d54b2c45 100644 --- a/ecs/list.go +++ b/ecs/list.go @@ -20,7 +20,7 @@ import ( "context" "fmt" - "github.com/docker/compose-cli/pkg/api" + "github.com/docker/compose/v2/pkg/api" ) func (b *ecsAPIService) List(ctx context.Context, opts api.ListOptions) ([]api.Stack, error) { diff --git a/ecs/local/backend.go b/ecs/local/backend.go index 04e20ad7..71440c06 100644 --- a/ecs/local/backend.go +++ b/ecs/local/backend.go @@ -22,6 +22,9 @@ import ( cliconfig "github.com/docker/cli/cli/config" "github.com/docker/docker/client" + "github.com/docker/compose/v2/pkg/api" + "github.com/docker/compose/v2/pkg/compose" + "github.com/docker/compose-cli/api/backend" "github.com/docker/compose-cli/api/cloud" "github.com/docker/compose-cli/api/containers" @@ -29,8 +32,6 @@ import ( "github.com/docker/compose-cli/api/resources" "github.com/docker/compose-cli/api/secrets" "github.com/docker/compose-cli/api/volumes" - "github.com/docker/compose-cli/pkg/api" - "github.com/docker/compose-cli/pkg/compose" ) const backendType = store.EcsLocalSimulationContextType diff --git a/ecs/local/compose.go b/ecs/local/compose.go index 8bc21ba0..278abe6c 100644 --- a/ecs/local/compose.go +++ b/ecs/local/compose.go @@ -28,7 +28,7 @@ import ( "github.com/pkg/errors" "github.com/sanathkr/go-yaml" - "github.com/docker/compose-cli/pkg/api" + "github.com/docker/compose/v2/pkg/api" ) func (e ecsLocalSimulation) Build(ctx context.Context, project *types.Project, options api.BuildOptions) error { diff --git a/ecs/local/context.go b/ecs/local/context.go index d6857370..1a531d13 100644 --- a/ecs/local/context.go +++ b/ecs/local/context.go @@ -19,9 +19,10 @@ package local import ( "context" + "github.com/docker/compose/v2/pkg/api" + "github.com/docker/compose-cli/api/cloud" "github.com/docker/compose-cli/ecs" - "github.com/docker/compose-cli/pkg/api" ) var _ cloud.Service = ecsLocalSimulation{} diff --git a/ecs/logs.go b/ecs/logs.go index 4018a7fb..830bafd8 100644 --- a/ecs/logs.go +++ b/ecs/logs.go @@ -19,7 +19,8 @@ package ecs import ( "context" - "github.com/docker/compose-cli/pkg/api" + "github.com/docker/compose/v2/pkg/api" + "github.com/docker/compose-cli/utils" ) diff --git a/ecs/ps.go b/ecs/ps.go index 8627bb6c..0e8bad32 100644 --- a/ecs/ps.go +++ b/ecs/ps.go @@ -19,7 +19,7 @@ package ecs import ( "context" - "github.com/docker/compose-cli/pkg/api" + "github.com/docker/compose/v2/pkg/api" ) func (b *ecsAPIService) Ps(ctx context.Context, projectName string, options api.PsOptions) ([]api.ContainerSummary, error) { diff --git a/ecs/run.go b/ecs/run.go index 5758fd85..9d993c08 100644 --- a/ecs/run.go +++ b/ecs/run.go @@ -21,7 +21,7 @@ import ( "github.com/compose-spec/compose-go/types" - "github.com/docker/compose-cli/pkg/api" + "github.com/docker/compose/v2/pkg/api" ) func (b *ecsAPIService) RunOneOffContainer(ctx context.Context, project *types.Project, opts api.RunOptions) (int, error) { diff --git a/ecs/sdk.go b/ecs/sdk.go index c0fbb737..3fa18ef9 100644 --- a/ecs/sdk.go +++ b/ecs/sdk.go @@ -24,10 +24,6 @@ import ( "strings" "time" - "github.com/docker/compose-cli/api/secrets" - "github.com/docker/compose-cli/internal" - "github.com/docker/compose-cli/pkg/api" - "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/arn" "github.com/aws/aws-sdk-go/aws/request" @@ -55,10 +51,14 @@ import ( "github.com/aws/aws-sdk-go/service/secretsmanager/secretsmanageriface" "github.com/aws/aws-sdk-go/service/ssm" "github.com/aws/aws-sdk-go/service/ssm/ssmiface" + "github.com/docker/compose/v2/pkg/api" "github.com/hashicorp/go-multierror" "github.com/hashicorp/go-uuid" "github.com/pkg/errors" "github.com/sirupsen/logrus" + + "github.com/docker/compose-cli/api/secrets" + "github.com/docker/compose-cli/internal" ) type sdk struct { diff --git a/ecs/tags.go b/ecs/tags.go index 15c52960..6449e796 100644 --- a/ecs/tags.go +++ b/ecs/tags.go @@ -19,8 +19,7 @@ package ecs import ( "github.com/awslabs/goformation/v4/cloudformation/tags" "github.com/compose-spec/compose-go/types" - - "github.com/docker/compose-cli/pkg/api" + "github.com/docker/compose/v2/pkg/api" ) func projectTags(project *types.Project) []tags.Tag { diff --git a/ecs/top.go b/ecs/top.go index e1a87965..8ba06b3a 100644 --- a/ecs/top.go +++ b/ecs/top.go @@ -19,7 +19,7 @@ package ecs import ( "context" - "github.com/docker/compose-cli/pkg/api" + "github.com/docker/compose/v2/pkg/api" ) func (b *ecsAPIService) Top(ctx context.Context, projectName string, services []string) ([]api.ContainerProcSummary, error) { diff --git a/ecs/up.go b/ecs/up.go index c3a6f285..57fe6f0c 100644 --- a/ecs/up.go +++ b/ecs/up.go @@ -24,10 +24,9 @@ import ( "syscall" "github.com/compose-spec/compose-go/types" + "github.com/docker/compose/v2/pkg/api" + "github.com/docker/compose/v2/pkg/progress" "github.com/sirupsen/logrus" - - "github.com/docker/compose-cli/pkg/api" - "github.com/docker/compose-cli/pkg/progress" ) func (b *ecsAPIService) Build(ctx context.Context, project *types.Project, options api.BuildOptions) error { diff --git a/ecs/volumes.go b/ecs/volumes.go index 6d8c5309..876b825f 100644 --- a/ecs/volumes.go +++ b/ecs/volumes.go @@ -20,13 +20,13 @@ import ( "context" "fmt" - "github.com/docker/compose-cli/api/volumes" - "github.com/docker/compose-cli/pkg/api" - "github.com/awslabs/goformation/v4/cloudformation" "github.com/awslabs/goformation/v4/cloudformation/efs" "github.com/compose-spec/compose-go/types" + "github.com/docker/compose/v2/pkg/api" "github.com/pkg/errors" + + "github.com/docker/compose-cli/api/volumes" ) func (b *ecsAPIService) createNFSMountTarget(project *types.Project, resources awsResources, template *cloudformation.Template) { diff --git a/ecs/wait.go b/ecs/wait.go index 3974035e..ebc36d39 100644 --- a/ecs/wait.go +++ b/ecs/wait.go @@ -23,9 +23,8 @@ import ( "strings" "time" - "github.com/docker/compose-cli/pkg/progress" - "github.com/aws/aws-sdk-go/aws" + "github.com/docker/compose/v2/pkg/progress" "github.com/iancoleman/strcase" ) diff --git a/go.mod b/go.mod index 8c8fb105..98fed9c9 100644 --- a/go.mod +++ b/go.mod @@ -23,8 +23,8 @@ require ( github.com/containerd/console v1.0.2 github.com/containerd/containerd v1.5.4 github.com/distribution/distribution/v3 v3.0.0-20210316161203-a01c71e2477e - github.com/docker/buildx v0.5.2-0.20210422185057-908a856079fc github.com/docker/cli v20.10.7+incompatible + github.com/docker/compose/v2 v2.0.0-00010101000000-000000000000 github.com/docker/docker v20.10.7+incompatible github.com/docker/go-connections v0.4.0 github.com/docker/go-units v0.4.0 @@ -37,18 +37,11 @@ require ( github.com/google/go-cmp v0.5.5 github.com/hashicorp/go-multierror v1.1.0 github.com/hashicorp/go-uuid v1.0.2 - github.com/hashicorp/go-version v1.3.0 github.com/iancoleman/strcase v0.1.2 github.com/joho/godotenv v1.3.0 - github.com/kr/pty v1.1.8 // indirect github.com/labstack/echo v3.3.10+incompatible github.com/labstack/gommon v0.3.0 // indirect - github.com/mattn/go-colorable v0.1.6 // indirect github.com/mattn/go-ieproxy v0.0.1 // indirect - github.com/mattn/go-isatty v0.0.12 - github.com/mattn/go-shellwords v1.0.12 - github.com/moby/buildkit v0.8.2-0.20210401015549-df49b648c8bf - github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 github.com/morikuni/aec v1.0.0 github.com/opencontainers/go-digest v1.0.0 github.com/opencontainers/image-spec v1.0.1 @@ -82,3 +75,5 @@ require ( // (for buildx) replace github.com/jaguilar/vt100 => github.com/tonistiigi/vt100 v0.0.0-20190402012908-ad4c4a574305 + +replace github.com/docker/compose/v2 => github.com/docker/compose-cli/v2 v2.0.0-rc.2.0.20210831165642-09745fa0c3fe diff --git a/go.sum b/go.sum index b34077c1..9534e2c2 100644 --- a/go.sum +++ b/go.sum @@ -412,6 +412,8 @@ github.com/docker/cli v20.10.5+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHv github.com/docker/cli v20.10.6+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/cli v20.10.7+incompatible h1:pv/3NqibQKphWZiAskMzdz8w0PRbtTaEB+f6NwdU7Is= github.com/docker/cli v20.10.7+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/compose-cli/v2 v2.0.0-rc.2.0.20210831165642-09745fa0c3fe h1:udoy+K5rRZwGsHMPyMvOB4tyxHmIu6+tEObiFRZH9w8= +github.com/docker/compose-cli/v2 v2.0.0-rc.2.0.20210831165642-09745fa0c3fe/go.mod h1:uiQC3eBapoiY1u6a+8IFMyHdDRKeJRQVx5mD0/ozrGg= github.com/docker/compose-on-kubernetes v0.4.19-0.20190128150448-356b2919c496/go.mod h1:iT2pYfi580XlpaV4KmK0T6+4/9+XoKmk/fhoDod1emE= github.com/docker/distribution v0.0.0-20190905152932-14b96e55d84c/go.mod h1:0+TTO4EOBfRPhZXAeF1Vu+W3hHZ8eLp8PgKVZlcvtFY= github.com/docker/distribution v2.6.0-rc.1.0.20180327202408-83389a148052+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= diff --git a/kube/backend.go b/kube/backend.go index a439c2a3..9d21c750 100644 --- a/kube/backend.go +++ b/kube/backend.go @@ -19,6 +19,8 @@ package kube import ( + "github.com/docker/compose/v2/pkg/api" + "github.com/docker/compose-cli/api/backend" "github.com/docker/compose-cli/api/cloud" "github.com/docker/compose-cli/api/containers" @@ -26,7 +28,6 @@ import ( "github.com/docker/compose-cli/api/resources" "github.com/docker/compose-cli/api/secrets" "github.com/docker/compose-cli/api/volumes" - "github.com/docker/compose-cli/pkg/api" ) const backendType = store.KubeContextType diff --git a/kube/client/client.go b/kube/client/client.go index e363163f..0d575cec 100644 --- a/kube/client/client.go +++ b/kube/client/client.go @@ -27,9 +27,8 @@ import ( "strings" "time" - "github.com/docker/compose-cli/pkg/api" - "github.com/docker/compose-cli/pkg/utils" - + "github.com/docker/compose/v2/pkg/api" + "github.com/docker/compose/v2/pkg/utils" "golang.org/x/sync/errgroup" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" diff --git a/kube/client/client_test.go b/kube/client/client_test.go index 0c91295c..6c7e4c24 100644 --- a/kube/client/client_test.go +++ b/kube/client/client_test.go @@ -26,7 +26,7 @@ import ( "gotest.tools/v3/assert" - "github.com/docker/compose-cli/pkg/api" + "github.com/docker/compose/v2/pkg/api" ) func TestPodToContainerSummary(t *testing.T) { diff --git a/kube/client/utils.go b/kube/client/utils.go index ae4c0866..e9cf02ea 100644 --- a/kube/client/utils.go +++ b/kube/client/utils.go @@ -22,9 +22,8 @@ import ( "fmt" "time" - "github.com/docker/compose-cli/pkg/api" - "github.com/docker/compose-cli/pkg/utils" - + "github.com/docker/compose/v2/pkg/api" + "github.com/docker/compose/v2/pkg/utils" corev1 "k8s.io/api/core/v1" ) diff --git a/kube/compose.go b/kube/compose.go index f33663a1..76149d82 100644 --- a/kube/compose.go +++ b/kube/compose.go @@ -24,6 +24,9 @@ import ( "strings" "github.com/compose-spec/compose-go/types" + "github.com/docker/compose/v2/pkg/api" + "github.com/docker/compose/v2/pkg/progress" + utils2 "github.com/docker/compose/v2/pkg/utils" "github.com/pkg/errors" apicontext "github.com/docker/compose-cli/api/context" @@ -31,9 +34,6 @@ import ( "github.com/docker/compose-cli/kube/client" "github.com/docker/compose-cli/kube/helm" "github.com/docker/compose-cli/kube/resources" - "github.com/docker/compose-cli/pkg/api" - "github.com/docker/compose-cli/pkg/progress" - utils2 "github.com/docker/compose-cli/pkg/utils" "github.com/docker/compose-cli/utils" ) diff --git a/kube/context.go b/kube/context.go index 0159036a..e0c3065a 100644 --- a/kube/context.go +++ b/kube/context.go @@ -22,11 +22,11 @@ import ( "fmt" "github.com/AlecAivazis/survey/v2/terminal" + "github.com/docker/compose/v2/pkg/api" + "github.com/docker/compose/v2/pkg/prompt" "github.com/docker/compose-cli/api/context/store" "github.com/docker/compose-cli/kube/resources" - "github.com/docker/compose-cli/pkg/api" - "github.com/docker/compose-cli/pkg/prompt" ) // ContextParams options for creating a Kubernetes context diff --git a/kube/helm/helm.go b/kube/helm/helm.go index 84610260..c726f2ad 100644 --- a/kube/helm/helm.go +++ b/kube/helm/helm.go @@ -21,7 +21,7 @@ package helm import ( "errors" - "github.com/docker/compose-cli/pkg/api" + "github.com/docker/compose/v2/pkg/api" "helm.sh/helm/v3/pkg/action" "helm.sh/helm/v3/pkg/chart" env "helm.sh/helm/v3/pkg/cli" diff --git a/kube/resources/kube.go b/kube/resources/kube.go index 0b2913bd..da4aae51 100644 --- a/kube/resources/kube.go +++ b/kube/resources/kube.go @@ -25,7 +25,7 @@ import ( "time" "github.com/compose-spec/compose-go/types" - "github.com/docker/compose-cli/pkg/api" + "github.com/docker/compose/v2/pkg/api" apps "k8s.io/api/apps/v1" core "k8s.io/api/core/v1" resource "k8s.io/apimachinery/pkg/api/resource" diff --git a/local/backend.go b/local/backend.go index b92ee349..dfc11fe4 100644 --- a/local/backend.go +++ b/local/backend.go @@ -22,6 +22,8 @@ import ( "github.com/docker/cli/cli/command" cliconfig "github.com/docker/cli/cli/config" cliflags "github.com/docker/cli/cli/flags" + "github.com/docker/compose/v2/pkg/api" + "github.com/docker/compose/v2/pkg/compose" "github.com/docker/docker/client" "github.com/docker/compose-cli/api/backend" @@ -30,8 +32,6 @@ import ( "github.com/docker/compose-cli/api/secrets" "github.com/docker/compose-cli/api/volumes" cliopts "github.com/docker/compose-cli/cli/options" - "github.com/docker/compose-cli/pkg/api" - "github.com/docker/compose-cli/pkg/compose" ) type local struct { diff --git a/local/containers.go b/local/containers.go index 358b2f56..cddca640 100644 --- a/local/containers.go +++ b/local/containers.go @@ -23,6 +23,7 @@ import ( "strings" "time" + "github.com/docker/compose/v2/pkg/api" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/mount" @@ -35,7 +36,6 @@ import ( "github.com/docker/compose-cli/api/containers" "github.com/docker/compose-cli/local/moby" - "github.com/docker/compose-cli/pkg/api" ) type containerService struct { diff --git a/pkg/api/api.go b/pkg/api/api.go deleted file mode 100644 index 08193438..00000000 --- a/pkg/api/api.go +++ /dev/null @@ -1,405 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package api - -import ( - "context" - "fmt" - "io" - "strings" - "time" - - "github.com/compose-spec/compose-go/types" -) - -// Service manages a compose project -type Service interface { - // Build executes the equivalent to a `compose build` - Build(ctx context.Context, project *types.Project, options BuildOptions) error - // Push executes the equivalent ot a `compose push` - Push(ctx context.Context, project *types.Project, options PushOptions) error - // Pull executes the equivalent of a `compose pull` - Pull(ctx context.Context, project *types.Project, opts PullOptions) error - // Create executes the equivalent to a `compose create` - Create(ctx context.Context, project *types.Project, opts CreateOptions) error - // Start executes the equivalent to a `compose start` - Start(ctx context.Context, project *types.Project, options StartOptions) error - // Restart restarts containers - Restart(ctx context.Context, project *types.Project, options RestartOptions) error - // Stop executes the equivalent to a `compose stop` - Stop(ctx context.Context, project *types.Project, options StopOptions) error - // Up executes the equivalent to a `compose up` - Up(ctx context.Context, project *types.Project, options UpOptions) error - // Down executes the equivalent to a `compose down` - Down(ctx context.Context, projectName string, options DownOptions) error - // Logs executes the equivalent to a `compose logs` - Logs(ctx context.Context, projectName string, consumer LogConsumer, options LogOptions) error - // Ps executes the equivalent to a `compose ps` - Ps(ctx context.Context, projectName string, options PsOptions) ([]ContainerSummary, error) - // List executes the equivalent to a `docker stack ls` - List(ctx context.Context, options ListOptions) ([]Stack, error) - // Convert translate compose model into backend's native format - Convert(ctx context.Context, project *types.Project, options ConvertOptions) ([]byte, error) - // Kill executes the equivalent to a `compose kill` - Kill(ctx context.Context, project *types.Project, options KillOptions) error - // RunOneOffContainer creates a service oneoff container and starts its dependencies - RunOneOffContainer(ctx context.Context, project *types.Project, opts RunOptions) (int, error) - // Remove executes the equivalent to a `compose rm` - Remove(ctx context.Context, project *types.Project, options RemoveOptions) error - // Exec executes a command in a running service container - Exec(ctx context.Context, project *types.Project, opts RunOptions) (int, error) - // Copy copies a file/folder between a service container and the local filesystem - Copy(ctx context.Context, project *types.Project, opts CopyOptions) error - // Pause executes the equivalent to a `compose pause` - Pause(ctx context.Context, project string, options PauseOptions) error - // UnPause executes the equivalent to a `compose unpause` - UnPause(ctx context.Context, project string, options PauseOptions) error - // Top executes the equivalent to a `compose top` - Top(ctx context.Context, projectName string, services []string) ([]ContainerProcSummary, error) - // Events executes the equivalent to a `compose events` - Events(ctx context.Context, project string, options EventsOptions) error - // Port executes the equivalent to a `compose port` - Port(ctx context.Context, project string, service string, port int, options PortOptions) (string, int, error) - // Images executes the equivalent of a `compose images` - Images(ctx context.Context, projectName string, options ImagesOptions) ([]ImageSummary, error) -} - -// BuildOptions group options of the Build API -type BuildOptions struct { - // Pull always attempt to pull a newer version of the image - Pull bool - // Progress set type of progress output ("auto", "plain", "tty") - Progress string - // Args set build-time args - Args types.MappingWithEquals - // NoCache disables cache use - NoCache bool - // Quiet make the build process not output to the console - Quiet bool - // Services passed in the command line to be built - Services []string -} - -// CreateOptions group options of the Create API -type CreateOptions struct { - // Services defines the services user interacts with - Services []string - // Remove legacy containers for services that are not defined in the project - RemoveOrphans bool - // Recreate define the strategy to apply on existing containers - Recreate string - // RecreateDependencies define the strategy to apply on dependencies services - RecreateDependencies string - // Inherit reuse anonymous volumes from previous container - Inherit bool - // Timeout set delay to wait for container to gracelfuly stop before sending SIGKILL - Timeout *time.Duration - // QuietPull makes the pulling process quiet - QuietPull bool -} - -// StartOptions group options of the Start API -type StartOptions struct { - // Attach to container and forward logs if not nil - Attach LogConsumer - // AttachTo set the services to attach to - AttachTo []string - // CascadeStop stops the application when a container stops - CascadeStop bool - // ExitCodeFrom return exit code from specified service - ExitCodeFrom string -} - -// RestartOptions group options of the Restart API -type RestartOptions struct { - // Timeout override container restart timeout - Timeout *time.Duration - // Services passed in the command line to be restarted - Services []string -} - -// StopOptions group options of the Stop API -type StopOptions struct { - // Timeout override container stop timeout - Timeout *time.Duration - // Services passed in the command line to be stopped - Services []string -} - -// UpOptions group options of the Up API -type UpOptions struct { - Create CreateOptions - Start StartOptions -} - -// DownOptions group options of the Down API -type DownOptions struct { - // RemoveOrphans will cleanup containers that are not declared on the compose model but own the same labels - RemoveOrphans bool - // Project is the compose project used to define this app. Might be nil if user ran `down` just with project name - Project *types.Project - // Timeout override container stop timeout - Timeout *time.Duration - // Images remove image used by services. 'all': Remove all images. 'local': Remove only images that don't have a tag - Images string - // Volumes remove volumes, both declared in the `volumes` section and anonymous ones - Volumes bool -} - -// ConvertOptions group options of the Convert API -type ConvertOptions struct { - // Format define the output format used to dump converted application model (json|yaml) - Format string - // Output defines the path to save the application model - Output string -} - -// PushOptions group options of the Push API -type PushOptions struct { - IgnoreFailures bool -} - -// PullOptions group options of the Pull API -type PullOptions struct { - Quiet bool - IgnoreFailures bool -} - -// ImagesOptions group options of the Images API -type ImagesOptions struct { - Services []string -} - -// KillOptions group options of the Kill API -type KillOptions struct { - // Signal to send to containers - Signal string -} - -// RemoveOptions group options of the Remove API -type RemoveOptions struct { - // DryRun just list removable resources - DryRun bool - // Volumes remove anonymous volumes - Volumes bool - // Force don't ask to confirm removal - Force bool - // Services passed in the command line to be removed - Services []string -} - -// RunOptions group options of the Run API -type RunOptions struct { - Name string - Service string - Command []string - Entrypoint []string - Detach bool - AutoRemove bool - Stdin io.ReadCloser - Stdout io.WriteCloser - Stderr io.WriteCloser - Tty bool - WorkingDir string - User string - Environment []string - Labels types.Labels - Privileged bool - UseNetworkAliases bool - // used by exec - Index int -} - -// EventsOptions group options of the Events API -type EventsOptions struct { - Services []string - Consumer func(event Event) error -} - -// Event is a container runtime event served by Events API -type Event struct { - Timestamp time.Time - Service string - Container string - Status string - Attributes map[string]string -} - -// PortOptions group options of the Port API -type PortOptions struct { - Protocol string - Index int -} - -func (e Event) String() string { - t := e.Timestamp.Format("2006-01-02 15:04:05.000000") - var attr []string - for k, v := range e.Attributes { - attr = append(attr, fmt.Sprintf("%s=%s", k, v)) - } - return fmt.Sprintf("%s container %s %s (%s)\n", t, e.Status, e.Container, strings.Join(attr, ", ")) - -} - -// ListOptions group options of the ls API -type ListOptions struct { - All bool -} - -// PsOptions group options of the Ps API -type PsOptions struct { - All bool - Services []string -} - -// CopyOptions group options of the cp API -type CopyOptions struct { - Source string - Destination string - All bool - Index int - FollowLink bool - CopyUIDGID bool -} - -// PortPublisher hold status about published port -type PortPublisher struct { - URL string - TargetPort int - PublishedPort int - Protocol string -} - -// ContainerSummary hold high-level description of a container -type ContainerSummary struct { - ID string - Name string - Command string - Project string - Service string - State string - Health string - ExitCode int - Publishers []PortPublisher -} - -// ContainerProcSummary holds container processes top data -type ContainerProcSummary struct { - ID string - Name string - Processes [][]string - Titles []string -} - -// ImageSummary holds container image description -type ImageSummary struct { - ID string - ContainerName string - Repository string - Tag string - Size int64 -} - -// ServiceStatus hold status about a service -type ServiceStatus struct { - ID string - Name string - Replicas int - Desired int - Ports []string - Publishers []PortPublisher -} - -// LogOptions defines optional parameters for the `Log` API -type LogOptions struct { - Services []string - Tail string - Since string - Until string - Follow bool - Timestamps bool -} - -// PauseOptions group options of the Pause API -type PauseOptions struct { - // Services passed in the command line to be started - Services []string -} - -const ( - // STARTING indicates that stack is being deployed - STARTING string = "Starting" - // RUNNING indicates that stack is deployed and services are running - RUNNING string = "Running" - // UPDATING indicates that some stack resources are being recreated - UPDATING string = "Updating" - // REMOVING indicates that stack is being deleted - REMOVING string = "Removing" - // UNKNOWN indicates unknown stack state - UNKNOWN string = "Unknown" - // FAILED indicates that stack deployment failed - FAILED string = "Failed" -) - -const ( - // RecreateDiverged to recreate services which configuration diverges from compose model - RecreateDiverged = "diverged" - // RecreateForce to force service container being recreated - RecreateForce = "force" - // RecreateNever to never recreate existing service containers - RecreateNever = "never" -) - -// Stack holds the name and state of a compose application/stack -type Stack struct { - ID string - Name string - Status string - Reason string -} - -// LogConsumer is a callback to process log messages from services -type LogConsumer interface { - Log(service, container, message string) - Status(container, msg string) - Register(container string) -} - -// ContainerEventListener is a callback to process ContainerEvent from services -type ContainerEventListener func(event ContainerEvent) - -// ContainerEvent notify an event has been collected on source container implementing Service -type ContainerEvent struct { - Type int - Container string - Service string - Line string - // ContainerEventExit only - ExitCode int - Restarting bool -} - -const ( - // ContainerEventLog is a ContainerEvent of type log. Line is set - ContainerEventLog = iota - // ContainerEventAttach is a ContainerEvent of type attach. First event sent about a container - ContainerEventAttach - // ContainerEventExit is a ContainerEvent of type exit. ExitCode is set - ContainerEventExit - // UserCancel user cancelled compose up, we are stopping containers - UserCancel -) diff --git a/pkg/api/api_test.go b/pkg/api/api_test.go deleted file mode 100644 index 0d963315..00000000 --- a/pkg/api/api_test.go +++ /dev/null @@ -1,38 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package api - -import ( - "testing" - - "github.com/compose-spec/compose-go/types" - "gotest.tools/v3/assert" -) - -func TestRunOptionsEnvironmentMap(t *testing.T) { - opts := RunOptions{ - Environment: []string{ - "FOO=BAR", - "ZOT=", - "QIX", - }, - } - env := types.NewMappingWithEquals(opts.Environment) - assert.Equal(t, *env["FOO"], "BAR") - assert.Equal(t, *env["ZOT"], "") - assert.Check(t, env["QIX"] == nil) -} diff --git a/pkg/api/errors.go b/pkg/api/errors.go deleted file mode 100644 index 10f628be..00000000 --- a/pkg/api/errors.go +++ /dev/null @@ -1,87 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package api - -import ( - "github.com/pkg/errors" -) - -const ( - //ExitCodeLoginRequired exit code when command cannot execute because it requires cloud login - // This will be used by VSCode to detect when creating context if the user needs to login first - ExitCodeLoginRequired = 5 -) - -var ( - // ErrNotFound is returned when an object is not found - ErrNotFound = errors.New("not found") - // ErrAlreadyExists is returned when an object already exists - ErrAlreadyExists = errors.New("already exists") - // ErrForbidden is returned when an operation is not permitted - ErrForbidden = errors.New("forbidden") - // ErrUnknown is returned when the error type is unmapped - ErrUnknown = errors.New("unknown") - // ErrLoginFailed is returned when login failed - ErrLoginFailed = errors.New("login failed") - // ErrLoginRequired is returned when login is required for a specific action - ErrLoginRequired = errors.New("login required") - // ErrNotImplemented is returned when a backend doesn't implement - // an action - ErrNotImplemented = errors.New("not implemented") - // ErrCanceled is returned when the command was canceled by user - ErrCanceled = errors.New("canceled") - // ErrParsingFailed is returned when a string cannot be parsed - ErrParsingFailed = errors.New("parsing failed") - // ErrWrongContextType is returned when the caller tries to get a context - // with the wrong type - ErrWrongContextType = errors.New("wrong context type") -) - -// IsNotFoundError returns true if the unwrapped error is ErrNotFound -func IsNotFoundError(err error) bool { - return errors.Is(err, ErrNotFound) -} - -// IsAlreadyExistsError returns true if the unwrapped error is ErrAlreadyExists -func IsAlreadyExistsError(err error) bool { - return errors.Is(err, ErrAlreadyExists) -} - -// IsForbiddenError returns true if the unwrapped error is ErrForbidden -func IsForbiddenError(err error) bool { - return errors.Is(err, ErrForbidden) -} - -// IsUnknownError returns true if the unwrapped error is ErrUnknown -func IsUnknownError(err error) bool { - return errors.Is(err, ErrUnknown) -} - -// IsErrNotImplemented returns true if the unwrapped error is ErrNotImplemented -func IsErrNotImplemented(err error) bool { - return errors.Is(err, ErrNotImplemented) -} - -// IsErrParsingFailed returns true if the unwrapped error is ErrParsingFailed -func IsErrParsingFailed(err error) bool { - return errors.Is(err, ErrParsingFailed) -} - -// IsErrCanceled returns true if the unwrapped error is ErrCanceled -func IsErrCanceled(err error) bool { - return errors.Is(err, ErrCanceled) -} diff --git a/pkg/api/errors_test.go b/pkg/api/errors_test.go deleted file mode 100644 index ea47edb6..00000000 --- a/pkg/api/errors_test.go +++ /dev/null @@ -1,52 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package api - -import ( - "testing" - - "github.com/pkg/errors" - "gotest.tools/v3/assert" -) - -func TestIsNotFound(t *testing.T) { - err := errors.Wrap(ErrNotFound, `object "name"`) - assert.Assert(t, IsNotFoundError(err)) - - assert.Assert(t, !IsNotFoundError(errors.New("another error"))) -} - -func TestIsAlreadyExists(t *testing.T) { - err := errors.Wrap(ErrAlreadyExists, `object "name"`) - assert.Assert(t, IsAlreadyExistsError(err)) - - assert.Assert(t, !IsAlreadyExistsError(errors.New("another error"))) -} - -func TestIsForbidden(t *testing.T) { - err := errors.Wrap(ErrForbidden, `object "name"`) - assert.Assert(t, IsForbiddenError(err)) - - assert.Assert(t, !IsForbiddenError(errors.New("another error"))) -} - -func TestIsUnknown(t *testing.T) { - err := errors.Wrap(ErrUnknown, `object "name"`) - assert.Assert(t, IsUnknownError(err)) - - assert.Assert(t, !IsUnknownError(errors.New("another error"))) -} diff --git a/pkg/api/labels.go b/pkg/api/labels.go deleted file mode 100644 index 32b0ca11..00000000 --- a/pkg/api/labels.go +++ /dev/null @@ -1,67 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package api - -import ( - "fmt" - - "github.com/hashicorp/go-version" - - "github.com/docker/compose-cli/internal" -) - -const ( - // ProjectLabel allow to track resource related to a compose project - ProjectLabel = "com.docker.compose.project" - // ServiceLabel allow to track resource related to a compose service - ServiceLabel = "com.docker.compose.service" - // ConfigHashLabel stores configuration hash for a compose service - ConfigHashLabel = "com.docker.compose.config-hash" - // ContainerNumberLabel stores the container index of a replicated service - ContainerNumberLabel = "com.docker.compose.container-number" - // VolumeLabel allow to track resource related to a compose volume - VolumeLabel = "com.docker.compose.volume" - // NetworkLabel allow to track resource related to a compose network - NetworkLabel = "com.docker.compose.network" - // WorkingDirLabel stores absolute path to compose project working directory - WorkingDirLabel = "com.docker.compose.project.working_dir" - // ConfigFilesLabel stores absolute path to compose project configuration files - ConfigFilesLabel = "com.docker.compose.project.config_files" - // EnvironmentFileLabel stores absolute path to compose project env file set by `--env-file` - EnvironmentFileLabel = "com.docker.compose.project.environment_file" - // OneoffLabel stores value 'True' for one-off containers created by `compose run` - OneoffLabel = "com.docker.compose.oneoff" - // SlugLabel stores unique slug used for one-off container identity - SlugLabel = "com.docker.compose.slug" - // ImageDigestLabel stores digest of the container image used to run service - ImageDigestLabel = "com.docker.compose.image" - // VersionLabel stores the compose tool version used to run application - VersionLabel = "com.docker.compose.version" -) - -// ComposeVersion is the compose tool version as declared by label VersionLabel -var ComposeVersion string - -func init() { - v, err := version.NewVersion(internal.Version) - if err == nil { - segments := v.Segments() - if len(segments) > 2 { - ComposeVersion = fmt.Sprintf("%d.%d.%d", segments[0], segments[1], segments[2]) - } - } -} diff --git a/pkg/api/proxy.go b/pkg/api/proxy.go deleted file mode 100644 index 03163a73..00000000 --- a/pkg/api/proxy.go +++ /dev/null @@ -1,331 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package api - -import ( - "context" - - "github.com/compose-spec/compose-go/types" -) - -// ServiceProxy implements Service by delegating to implementation functions. This allows lazy init and per-method overrides -type ServiceProxy struct { - BuildFn func(ctx context.Context, project *types.Project, options BuildOptions) error - PushFn func(ctx context.Context, project *types.Project, options PushOptions) error - PullFn func(ctx context.Context, project *types.Project, opts PullOptions) error - CreateFn func(ctx context.Context, project *types.Project, opts CreateOptions) error - StartFn func(ctx context.Context, project *types.Project, options StartOptions) error - RestartFn func(ctx context.Context, project *types.Project, options RestartOptions) error - StopFn func(ctx context.Context, project *types.Project, options StopOptions) error - UpFn func(ctx context.Context, project *types.Project, options UpOptions) error - DownFn func(ctx context.Context, projectName string, options DownOptions) error - LogsFn func(ctx context.Context, projectName string, consumer LogConsumer, options LogOptions) error - PsFn func(ctx context.Context, projectName string, options PsOptions) ([]ContainerSummary, error) - ListFn func(ctx context.Context, options ListOptions) ([]Stack, error) - ConvertFn func(ctx context.Context, project *types.Project, options ConvertOptions) ([]byte, error) - KillFn func(ctx context.Context, project *types.Project, options KillOptions) error - RunOneOffContainerFn func(ctx context.Context, project *types.Project, opts RunOptions) (int, error) - RemoveFn func(ctx context.Context, project *types.Project, options RemoveOptions) error - ExecFn func(ctx context.Context, project *types.Project, opts RunOptions) (int, error) - CopyFn func(ctx context.Context, project *types.Project, opts CopyOptions) error - PauseFn func(ctx context.Context, project string, options PauseOptions) error - UnPauseFn func(ctx context.Context, project string, options PauseOptions) error - TopFn func(ctx context.Context, projectName string, services []string) ([]ContainerProcSummary, error) - EventsFn func(ctx context.Context, project string, options EventsOptions) error - PortFn func(ctx context.Context, project string, service string, port int, options PortOptions) (string, int, error) - ImagesFn func(ctx context.Context, projectName string, options ImagesOptions) ([]ImageSummary, error) - interceptors []Interceptor -} - -// NewServiceProxy produces a ServiceProxy -func NewServiceProxy() *ServiceProxy { - return &ServiceProxy{} -} - -// Interceptor allow to customize the compose types.Project before the actual Service method is executed -type Interceptor func(ctx context.Context, project *types.Project) - -var _ Service = &ServiceProxy{} - -// WithService configure proxy to use specified Service as delegate -func (s *ServiceProxy) WithService(service Service) *ServiceProxy { - s.BuildFn = service.Build - s.PushFn = service.Push - s.PullFn = service.Pull - s.CreateFn = service.Create - s.StartFn = service.Start - s.RestartFn = service.Restart - s.StopFn = service.Stop - s.UpFn = service.Up - s.DownFn = service.Down - s.LogsFn = service.Logs - s.PsFn = service.Ps - s.ListFn = service.List - s.ConvertFn = service.Convert - s.KillFn = service.Kill - s.RunOneOffContainerFn = service.RunOneOffContainer - s.RemoveFn = service.Remove - s.ExecFn = service.Exec - s.CopyFn = service.Copy - s.PauseFn = service.Pause - s.UnPauseFn = service.UnPause - s.TopFn = service.Top - s.EventsFn = service.Events - s.PortFn = service.Port - s.ImagesFn = service.Images - return s -} - -// WithInterceptor configures Interceptor to be applied to Service method execution -func (s *ServiceProxy) WithInterceptor(interceptors ...Interceptor) *ServiceProxy { - s.interceptors = append(s.interceptors, interceptors...) - return s -} - -// Build implements Service interface -func (s *ServiceProxy) Build(ctx context.Context, project *types.Project, options BuildOptions) error { - if s.BuildFn == nil { - return ErrNotImplemented - } - for _, i := range s.interceptors { - i(ctx, project) - } - return s.BuildFn(ctx, project, options) -} - -// Push implements Service interface -func (s *ServiceProxy) Push(ctx context.Context, project *types.Project, options PushOptions) error { - if s.PushFn == nil { - return ErrNotImplemented - } - for _, i := range s.interceptors { - i(ctx, project) - } - return s.PushFn(ctx, project, options) -} - -// Pull implements Service interface -func (s *ServiceProxy) Pull(ctx context.Context, project *types.Project, options PullOptions) error { - if s.PullFn == nil { - return ErrNotImplemented - } - for _, i := range s.interceptors { - i(ctx, project) - } - return s.PullFn(ctx, project, options) -} - -// Create implements Service interface -func (s *ServiceProxy) Create(ctx context.Context, project *types.Project, options CreateOptions) error { - if s.CreateFn == nil { - return ErrNotImplemented - } - for _, i := range s.interceptors { - i(ctx, project) - } - return s.CreateFn(ctx, project, options) -} - -// Start implements Service interface -func (s *ServiceProxy) Start(ctx context.Context, project *types.Project, options StartOptions) error { - if s.StartFn == nil { - return ErrNotImplemented - } - for _, i := range s.interceptors { - i(ctx, project) - } - return s.StartFn(ctx, project, options) -} - -// Restart implements Service interface -func (s *ServiceProxy) Restart(ctx context.Context, project *types.Project, options RestartOptions) error { - if s.RestartFn == nil { - return ErrNotImplemented - } - for _, i := range s.interceptors { - i(ctx, project) - } - return s.RestartFn(ctx, project, options) -} - -// Stop implements Service interface -func (s *ServiceProxy) Stop(ctx context.Context, project *types.Project, options StopOptions) error { - if s.StopFn == nil { - return ErrNotImplemented - } - for _, i := range s.interceptors { - i(ctx, project) - } - return s.StopFn(ctx, project, options) -} - -// Up implements Service interface -func (s *ServiceProxy) Up(ctx context.Context, project *types.Project, options UpOptions) error { - if s.UpFn == nil { - return ErrNotImplemented - } - for _, i := range s.interceptors { - i(ctx, project) - } - return s.UpFn(ctx, project, options) -} - -// Down implements Service interface -func (s *ServiceProxy) Down(ctx context.Context, project string, options DownOptions) error { - if s.DownFn == nil { - return ErrNotImplemented - } - return s.DownFn(ctx, project, options) -} - -// Logs implements Service interface -func (s *ServiceProxy) Logs(ctx context.Context, projectName string, consumer LogConsumer, options LogOptions) error { - if s.LogsFn == nil { - return ErrNotImplemented - } - return s.LogsFn(ctx, projectName, consumer, options) -} - -// Ps implements Service interface -func (s *ServiceProxy) Ps(ctx context.Context, project string, options PsOptions) ([]ContainerSummary, error) { - if s.PsFn == nil { - return nil, ErrNotImplemented - } - return s.PsFn(ctx, project, options) -} - -// List implements Service interface -func (s *ServiceProxy) List(ctx context.Context, options ListOptions) ([]Stack, error) { - if s.ListFn == nil { - return nil, ErrNotImplemented - } - return s.ListFn(ctx, options) -} - -// Convert implements Service interface -func (s *ServiceProxy) Convert(ctx context.Context, project *types.Project, options ConvertOptions) ([]byte, error) { - if s.ConvertFn == nil { - return nil, ErrNotImplemented - } - for _, i := range s.interceptors { - i(ctx, project) - } - return s.ConvertFn(ctx, project, options) -} - -// Kill implements Service interface -func (s *ServiceProxy) Kill(ctx context.Context, project *types.Project, options KillOptions) error { - if s.KillFn == nil { - return ErrNotImplemented - } - for _, i := range s.interceptors { - i(ctx, project) - } - return s.KillFn(ctx, project, options) -} - -// RunOneOffContainer implements Service interface -func (s *ServiceProxy) RunOneOffContainer(ctx context.Context, project *types.Project, options RunOptions) (int, error) { - if s.RunOneOffContainerFn == nil { - return 0, ErrNotImplemented - } - for _, i := range s.interceptors { - i(ctx, project) - } - return s.RunOneOffContainerFn(ctx, project, options) -} - -// Remove implements Service interface -func (s *ServiceProxy) Remove(ctx context.Context, project *types.Project, options RemoveOptions) error { - if s.RemoveFn == nil { - return ErrNotImplemented - } - for _, i := range s.interceptors { - i(ctx, project) - } - return s.RemoveFn(ctx, project, options) -} - -// Exec implements Service interface -func (s *ServiceProxy) Exec(ctx context.Context, project *types.Project, options RunOptions) (int, error) { - if s.ExecFn == nil { - return 0, ErrNotImplemented - } - for _, i := range s.interceptors { - i(ctx, project) - } - return s.ExecFn(ctx, project, options) -} - -// Copy implements Service interface -func (s *ServiceProxy) Copy(ctx context.Context, project *types.Project, options CopyOptions) error { - if s.CopyFn == nil { - return ErrNotImplemented - } - for _, i := range s.interceptors { - i(ctx, project) - } - return s.CopyFn(ctx, project, options) -} - -// Pause implements Service interface -func (s *ServiceProxy) Pause(ctx context.Context, project string, options PauseOptions) error { - if s.PauseFn == nil { - return ErrNotImplemented - } - return s.PauseFn(ctx, project, options) -} - -// UnPause implements Service interface -func (s *ServiceProxy) UnPause(ctx context.Context, project string, options PauseOptions) error { - if s.UnPauseFn == nil { - return ErrNotImplemented - } - return s.UnPauseFn(ctx, project, options) -} - -// Top implements Service interface -func (s *ServiceProxy) Top(ctx context.Context, project string, services []string) ([]ContainerProcSummary, error) { - if s.TopFn == nil { - return nil, ErrNotImplemented - } - return s.TopFn(ctx, project, services) -} - -// Events implements Service interface -func (s *ServiceProxy) Events(ctx context.Context, project string, options EventsOptions) error { - if s.EventsFn == nil { - return ErrNotImplemented - } - return s.EventsFn(ctx, project, options) -} - -// Port implements Service interface -func (s *ServiceProxy) Port(ctx context.Context, project string, service string, port int, options PortOptions) (string, int, error) { - if s.PortFn == nil { - return "", 0, ErrNotImplemented - } - return s.PortFn(ctx, project, service, port, options) -} - -// Images implements Service interface -func (s *ServiceProxy) Images(ctx context.Context, project string, options ImagesOptions) ([]ImageSummary, error) { - if s.ImagesFn == nil { - return nil, ErrNotImplemented - } - return s.ImagesFn(ctx, project, options) -} diff --git a/pkg/compose/attach.go b/pkg/compose/attach.go deleted file mode 100644 index 01b8d9e9..00000000 --- a/pkg/compose/attach.go +++ /dev/null @@ -1,163 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package compose - -import ( - "context" - "fmt" - "io" - "strings" - - "github.com/compose-spec/compose-go/types" - "github.com/docker/cli/cli/streams" - moby "github.com/docker/docker/api/types" - "github.com/docker/docker/pkg/stdcopy" - "github.com/moby/term" - - "github.com/docker/compose-cli/pkg/api" - "github.com/docker/compose-cli/pkg/utils" -) - -func (s *composeService) attach(ctx context.Context, project *types.Project, listener api.ContainerEventListener, selectedServices []string) (Containers, error) { - containers, err := s.getContainers(ctx, project.Name, oneOffExclude, true, selectedServices...) - if err != nil { - return nil, err - } - - containers.sorted() // This enforce predictable colors assignment - - var names []string - for _, c := range containers { - names = append(names, getContainerNameWithoutProject(c)) - } - - fmt.Printf("Attaching to %s\n", strings.Join(names, ", ")) - - for _, container := range containers { - err := s.attachContainer(ctx, container, listener, project) - if err != nil { - return nil, err - } - } - return containers, err -} - -func (s *composeService) attachContainer(ctx context.Context, container moby.Container, listener api.ContainerEventListener, project *types.Project) error { - serviceName := container.Labels[api.ServiceLabel] - containerName := getContainerNameWithoutProject(container) - service, err := project.GetService(serviceName) - if err != nil { - return err - } - - listener(api.ContainerEvent{ - Type: api.ContainerEventAttach, - Container: containerName, - Service: serviceName, - }) - - w := utils.GetWriter(func(line string) { - listener(api.ContainerEvent{ - Type: api.ContainerEventLog, - Container: containerName, - Service: serviceName, - Line: line, - }) - }) - _, _, err = s.attachContainerStreams(ctx, container.ID, service.Tty, nil, w, w) - return err -} - -func (s *composeService) attachContainerStreams(ctx context.Context, container string, tty bool, stdin io.ReadCloser, stdout, stderr io.Writer) (func(), chan bool, error) { - detached := make(chan bool) - var ( - restore = func() { /* noop */ } - ) - if stdin != nil { - in := streams.NewIn(stdin) - if in.IsTerminal() { - state, err := term.SetRawTerminal(in.FD()) - if err != nil { - return restore, detached, err - } - restore = func() { - term.RestoreTerminal(in.FD(), state) //nolint:errcheck - } - } - } - - streamIn, streamOut, err := s.getContainerStreams(ctx, container) - if err != nil { - return restore, detached, err - } - - go func() { - <-ctx.Done() - if stdin != nil { - stdin.Close() //nolint:errcheck - } - streamOut.Close() //nolint:errcheck - }() - - if streamIn != nil && stdin != nil { - go func() { - _, err := io.Copy(streamIn, stdin) - if _, ok := err.(term.EscapeError); ok { - close(detached) - } - }() - } - - if stdout != nil { - go func() { - if tty { - io.Copy(stdout, streamOut) // nolint:errcheck - } else { - stdcopy.StdCopy(stdout, stderr, streamOut) // nolint:errcheck - } - }() - } - return restore, detached, nil -} - -func (s *composeService) getContainerStreams(ctx context.Context, container string) (io.WriteCloser, io.ReadCloser, error) { - var stdout io.ReadCloser - var stdin io.WriteCloser - cnx, err := s.apiClient.ContainerAttach(ctx, container, moby.ContainerAttachOptions{ - Stream: true, - Stdin: true, - Stdout: true, - Stderr: true, - Logs: false, - }) - if err == nil { - stdout = ContainerStdout{HijackedResponse: cnx} - stdin = ContainerStdin{HijackedResponse: cnx} - return stdin, stdout, nil - } - - // Fallback to logs API - logs, err := s.apiClient.ContainerLogs(ctx, container, moby.ContainerLogsOptions{ - ShowStdout: true, - ShowStderr: true, - Follow: true, - }) - if err != nil { - return nil, nil, err - } - return stdin, logs, nil -} diff --git a/pkg/compose/build.go b/pkg/compose/build.go deleted file mode 100644 index 2fcb4d06..00000000 --- a/pkg/compose/build.go +++ /dev/null @@ -1,309 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package compose - -import ( - "context" - "fmt" - "os" - - "github.com/compose-spec/compose-go/types" - "github.com/containerd/containerd/platforms" - "github.com/docker/buildx/build" - "github.com/docker/buildx/driver" - _ "github.com/docker/buildx/driver/docker" // required to get default driver registered - "github.com/docker/buildx/util/buildflags" - xprogress "github.com/docker/buildx/util/progress" - bclient "github.com/moby/buildkit/client" - "github.com/moby/buildkit/session" - "github.com/moby/buildkit/session/auth/authprovider" - specs "github.com/opencontainers/image-spec/specs-go/v1" - - "github.com/docker/compose-cli/pkg/api" - "github.com/docker/compose-cli/pkg/progress" - "github.com/docker/compose-cli/pkg/utils" -) - -func (s *composeService) Build(ctx context.Context, project *types.Project, options api.BuildOptions) error { - return progress.Run(ctx, func(ctx context.Context) error { - return s.build(ctx, project, options) - }) -} - -func (s *composeService) build(ctx context.Context, project *types.Project, options api.BuildOptions) error { - opts := map[string]build.Options{} - imagesToBuild := []string{} - - args := flatten(options.Args.Resolve(func(s string) (string, bool) { - s, ok := project.Environment[s] - return s, ok - })) - - services, err := project.GetServices(options.Services...) - if err != nil { - return err - } - - for _, service := range services { - if service.Build != nil { - imageName := getImageName(service, project.Name) - imagesToBuild = append(imagesToBuild, imageName) - buildOptions, err := s.toBuildOptions(project, service, imageName) - if err != nil { - return err - } - buildOptions.Pull = options.Pull - buildOptions.BuildArgs = mergeArgs(buildOptions.BuildArgs, args) - buildOptions.NoCache = options.NoCache - opts[imageName] = buildOptions - buildOptions.CacheFrom, err = buildflags.ParseCacheEntry(service.Build.CacheFrom) - if err != nil { - return err - } - - for _, image := range service.Build.CacheFrom { - buildOptions.CacheFrom = append(buildOptions.CacheFrom, bclient.CacheOptionsEntry{ - Type: "registry", - Attrs: map[string]string{"ref": image}, - }) - } - } - } - - _, err = s.doBuild(ctx, project, opts, options.Progress) - if err == nil { - if len(imagesToBuild) > 0 && !options.Quiet { - utils.DisplayScanSuggestMsg() - } - } - - return err -} - -func (s *composeService) ensureImagesExists(ctx context.Context, project *types.Project, quietPull bool) error { - for _, service := range project.Services { - if service.Image == "" && service.Build == nil { - return fmt.Errorf("invalid service %q. Must specify either image or build", service.Name) - } - } - - images, err := s.getLocalImagesDigests(ctx, project) - if err != nil { - return err - } - - err = s.pullRequiredImages(ctx, project, images, quietPull) - if err != nil { - return err - } - - mode := xprogress.PrinterModeAuto - if quietPull { - mode = xprogress.PrinterModeQuiet - } - opts, err := s.getBuildOptions(project, images) - if err != nil { - return err - } - builtImages, err := s.doBuild(ctx, project, opts, mode) - if err != nil { - return err - } - - if len(builtImages) > 0 { - utils.DisplayScanSuggestMsg() - } - for name, digest := range builtImages { - images[name] = digest - } - // set digest as com.docker.compose.image label so we can detect outdated containers - for i, service := range project.Services { - image := getImageName(service, project.Name) - digest, ok := images[image] - if ok { - if project.Services[i].Labels == nil { - project.Services[i].Labels = types.Labels{} - } - project.Services[i].Labels[api.ImageDigestLabel] = digest - project.Services[i].Image = image - } - } - return nil -} - -func (s *composeService) getBuildOptions(project *types.Project, images map[string]string) (map[string]build.Options, error) { - opts := map[string]build.Options{} - for _, service := range project.Services { - if service.Image == "" && service.Build == nil { - return nil, fmt.Errorf("invalid service %q. Must specify either image or build", service.Name) - } - imageName := getImageName(service, project.Name) - _, localImagePresent := images[imageName] - - if service.Build != nil { - if localImagePresent && service.PullPolicy != types.PullPolicyBuild { - continue - } - opt, err := s.toBuildOptions(project, service, imageName) - if err != nil { - return nil, err - } - opts[imageName] = opt - continue - } - } - return opts, nil - -} - -func (s *composeService) getLocalImagesDigests(ctx context.Context, project *types.Project) (map[string]string, error) { - imageNames := []string{} - for _, s := range project.Services { - imgName := getImageName(s, project.Name) - if !utils.StringContains(imageNames, imgName) { - imageNames = append(imageNames, imgName) - } - } - imgs, err := s.getImages(ctx, imageNames) - if err != nil { - return nil, err - } - images := map[string]string{} - for name, info := range imgs { - images[name] = info.ID - } - return images, nil -} - -func (s *composeService) doBuild(ctx context.Context, project *types.Project, opts map[string]build.Options, mode string) (map[string]string, error) { - info, err := s.apiClient.Info(ctx) - if err != nil { - return nil, err - } - - if info.OSType == "windows" { - // no support yet for Windows container builds in Buildkit - // https://docs.docker.com/develop/develop-images/build_enhancements/#limitations - err := s.windowsBuild(opts, mode) - return nil, WrapCategorisedComposeError(err, BuildFailure) - } - if len(opts) == 0 { - return nil, nil - } - const drivername = "default" - - d, err := driver.GetDriver(ctx, drivername, nil, s.apiClient, s.configFile, nil, nil, "", nil, nil, project.WorkingDir) - if err != nil { - return nil, err - } - driverInfo := []build.DriverInfo{ - { - Name: "default", - Driver: d, - }, - } - - // Progress needs its own context that lives longer than the - // build one otherwise it won't read all the messages from - // build and will lock - progressCtx, cancel := context.WithCancel(context.Background()) - defer cancel() - w := xprogress.NewPrinter(progressCtx, os.Stdout, mode) - - // We rely on buildx "docker" builder integrated in docker engine, so don't need a DockerAPI here - response, err := build.Build(ctx, driverInfo, opts, nil, nil, w) - errW := w.Wait() - if err == nil { - err = errW - } - if err != nil { - return nil, WrapCategorisedComposeError(err, BuildFailure) - } - - imagesBuilt := map[string]string{} - for name, img := range response { - if img == nil || len(img.ExporterResponse) == 0 { - continue - } - digest, ok := img.ExporterResponse["containerimage.digest"] - if !ok { - continue - } - imagesBuilt[name] = digest - } - - return imagesBuilt, err -} - -func (s *composeService) toBuildOptions(project *types.Project, service types.ServiceConfig, imageTag string) (build.Options, error) { - var tags []string - tags = append(tags, imageTag) - - buildArgs := flatten(service.Build.Args.Resolve(func(s string) (string, bool) { - s, ok := project.Environment[s] - return s, ok - })) - - var plats []specs.Platform - if service.Platform != "" { - p, err := platforms.Parse(service.Platform) - if err != nil { - return build.Options{}, err - } - plats = append(plats, p) - } - - return build.Options{ - Inputs: build.Inputs{ - ContextPath: service.Build.Context, - DockerfilePath: service.Build.Dockerfile, - }, - BuildArgs: buildArgs, - Tags: tags, - Target: service.Build.Target, - Exports: []bclient.ExportEntry{{Type: "image", Attrs: map[string]string{}}}, - Platforms: plats, - Labels: service.Build.Labels, - Session: []session.Attachable{ - authprovider.NewDockerAuthProvider(os.Stderr), - }, - }, nil -} - -func flatten(in types.MappingWithEquals) types.Mapping { - if len(in) == 0 { - return nil - } - out := types.Mapping{} - for k, v := range in { - if v == nil { - continue - } - out[k] = *v - } - return out -} - -func mergeArgs(m ...types.Mapping) types.Mapping { - merged := types.Mapping{} - for _, mapping := range m { - for key, val := range mapping { - merged[key] = val - } - } - return merged -} diff --git a/pkg/compose/build_win.go b/pkg/compose/build_win.go deleted file mode 100644 index 3bae1646..00000000 --- a/pkg/compose/build_win.go +++ /dev/null @@ -1,28 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package compose - -import ( - "github.com/docker/buildx/build" - - "github.com/docker/compose-cli/pkg/api" -) - -func (s *composeService) windowsBuild(opts map[string]build.Options, mode string) error { - // FIXME copy/paste or reuse code from https://github.com/docker/cli/blob/master/cli/command/image/build.go - return api.ErrNotImplemented -} diff --git a/pkg/compose/compose.go b/pkg/compose/compose.go deleted file mode 100644 index f26af89c..00000000 --- a/pkg/compose/compose.go +++ /dev/null @@ -1,76 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package compose - -import ( - "context" - "encoding/json" - "fmt" - "strings" - - "github.com/docker/compose-cli/pkg/api" - - "github.com/compose-spec/compose-go/types" - "github.com/docker/cli/cli/config/configfile" - moby "github.com/docker/docker/api/types" - "github.com/docker/docker/client" - "github.com/sanathkr/go-yaml" -) - -// NewComposeService create a local implementation of the compose.Service API -func NewComposeService(apiClient client.APIClient, configFile *configfile.ConfigFile) api.Service { - return &composeService{ - apiClient: apiClient, - configFile: configFile, - } -} - -type composeService struct { - apiClient client.APIClient - configFile *configfile.ConfigFile -} - -func getCanonicalContainerName(c moby.Container) string { - // Names return container canonical name /foo + link aliases /linked_by/foo - for _, name := range c.Names { - if strings.LastIndex(name, "/") == 0 { - return name[1:] - } - } - return c.Names[0][1:] -} - -func getContainerNameWithoutProject(c moby.Container) string { - name := getCanonicalContainerName(c) - project := c.Labels[api.ProjectLabel] - prefix := fmt.Sprintf("%s_%s_", project, c.Labels[api.ServiceLabel]) - if strings.HasPrefix(name, prefix) { - return name[len(project)+1:] - } - return name -} - -func (s *composeService) Convert(ctx context.Context, project *types.Project, options api.ConvertOptions) ([]byte, error) { - switch options.Format { - case "json": - return json.MarshalIndent(project, "", " ") - case "yaml": - return yaml.Marshal(project) - default: - return nil, fmt.Errorf("unsupported format %q", options) - } -} diff --git a/pkg/compose/container.go b/pkg/compose/container.go deleted file mode 100644 index 954aed72..00000000 --- a/pkg/compose/container.go +++ /dev/null @@ -1,75 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package compose - -import ( - "io" - - moby "github.com/docker/docker/api/types" -) - -const ( - // ContainerCreated created status - ContainerCreated = "created" - // ContainerRestarting restarting status - ContainerRestarting = "restarting" - // ContainerRunning running status - ContainerRunning = "running" - // ContainerRemoving removing status - ContainerRemoving = "removing" //nolint - // ContainerPaused paused status - ContainerPaused = "paused" //nolint - // ContainerExited exited status - ContainerExited = "exited" //nolint - // ContainerDead dead status - ContainerDead = "dead" //nolint -) - -var _ io.ReadCloser = ContainerStdout{} - -// ContainerStdout implement ReadCloser for moby.HijackedResponse -type ContainerStdout struct { - moby.HijackedResponse -} - -// Read implement io.ReadCloser -func (l ContainerStdout) Read(p []byte) (n int, err error) { - return l.Reader.Read(p) -} - -// Close implement io.ReadCloser -func (l ContainerStdout) Close() error { - l.HijackedResponse.Close() - return nil -} - -var _ io.WriteCloser = ContainerStdin{} - -// ContainerStdin implement WriteCloser for moby.HijackedResponse -type ContainerStdin struct { - moby.HijackedResponse -} - -// Write implement io.WriteCloser -func (c ContainerStdin) Write(p []byte) (n int, err error) { - return c.Conn.Write(p) -} - -// Close implement io.WriteCloser -func (c ContainerStdin) Close() error { - return c.CloseWrite() -} diff --git a/pkg/compose/containers.go b/pkg/compose/containers.go deleted file mode 100644 index 9ab2f61c..00000000 --- a/pkg/compose/containers.go +++ /dev/null @@ -1,119 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package compose - -import ( - "context" - "sort" - - moby "github.com/docker/docker/api/types" - "github.com/docker/docker/api/types/filters" - - "github.com/docker/compose-cli/pkg/api" - "github.com/docker/compose-cli/pkg/utils" -) - -// Containers is a set of moby Container -type Containers []moby.Container - -type oneOff int - -const ( - oneOffInclude = oneOff(iota) - oneOffExclude - oneOffOnly -) - -func (s *composeService) getContainers(ctx context.Context, project string, oneOff oneOff, stopped bool, selectedServices ...string) (Containers, error) { - var containers Containers - f := []filters.KeyValuePair{projectFilter(project)} - if len(selectedServices) == 1 { - f = append(f, serviceFilter(selectedServices[0])) - } - switch oneOff { - case oneOffOnly: - f = append(f, oneOffFilter(true)) - case oneOffExclude: - f = append(f, oneOffFilter(false)) - case oneOffInclude: - } - containers, err := s.apiClient.ContainerList(ctx, moby.ContainerListOptions{ - Filters: filters.NewArgs(f...), - All: stopped, - }) - if err != nil { - return nil, err - } - if len(selectedServices) > 1 { - containers = containers.filter(isService(selectedServices...)) - } - return containers, nil -} - -// containerPredicate define a predicate we want container to satisfy for filtering operations -type containerPredicate func(c moby.Container) bool - -func isService(services ...string) containerPredicate { - return func(c moby.Container) bool { - service := c.Labels[api.ServiceLabel] - return utils.StringContains(services, service) - } -} - -func isNotService(services ...string) containerPredicate { - return func(c moby.Container) bool { - service := c.Labels[api.ServiceLabel] - return !utils.StringContains(services, service) - } -} - -func isNotOneOff(c moby.Container) bool { - v, ok := c.Labels[api.OneoffLabel] - return !ok || v == "False" -} - -// filter return Containers with elements to match predicate -func (containers Containers) filter(predicate containerPredicate) Containers { - var filtered Containers - for _, c := range containers { - if predicate(c) { - filtered = append(filtered, c) - } - } - return filtered -} - -func (containers Containers) names() []string { - var names []string - for _, c := range containers { - names = append(names, getCanonicalContainerName(c)) - } - return names -} - -func (containers Containers) forEach(fn func(moby.Container)) { - for _, c := range containers { - fn(c) - } -} - -func (containers Containers) sorted() Containers { - sort.Slice(containers, func(i, j int) bool { - return getCanonicalContainerName(containers[i]) < getCanonicalContainerName(containers[j]) - }) - return containers -} diff --git a/pkg/compose/convergence.go b/pkg/compose/convergence.go deleted file mode 100644 index 89c9ba3b..00000000 --- a/pkg/compose/convergence.go +++ /dev/null @@ -1,587 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package compose - -import ( - "context" - "fmt" - "strconv" - "strings" - "sync" - "time" - - "github.com/compose-spec/compose-go/types" - "github.com/containerd/containerd/platforms" - moby "github.com/docker/docker/api/types" - "github.com/docker/docker/api/types/filters" - "github.com/docker/docker/api/types/network" - specs "github.com/opencontainers/image-spec/specs-go/v1" - "github.com/sirupsen/logrus" - "golang.org/x/sync/errgroup" - - "github.com/docker/compose-cli/pkg/api" - "github.com/docker/compose-cli/pkg/progress" - "github.com/docker/compose-cli/pkg/utils" -) - -const ( - extLifecycle = "x-lifecycle" - forceRecreate = "force_recreate" - - doubledContainerNameWarning = "WARNING: The %q service is using the custom container name %q. " + - "Docker requires each container to have a unique name. " + - "Remove the custom name to scale the service.\n" -) - -// convergence manages service's container lifecycle. -// Based on initially observed state, it reconciles the existing container with desired state, which might include -// re-creating container, adding or removing replicas, or starting stopped containers. -// Cross services dependencies are managed by creating services in expected order and updating `service:xx` reference -// when a service has converged, so dependent ones can be managed with resolved containers references. -type convergence struct { - service *composeService - observedState map[string]Containers - stateMutex sync.Mutex -} - -func (c *convergence) getObservedState(serviceName string) Containers { - c.stateMutex.Lock() - defer c.stateMutex.Unlock() - return c.observedState[serviceName] -} - -func (c *convergence) setObservedState(serviceName string, containers Containers) { - c.stateMutex.Lock() - defer c.stateMutex.Unlock() - c.observedState[serviceName] = containers -} - -func newConvergence(services []string, state Containers, s *composeService) *convergence { - observedState := map[string]Containers{} - for _, s := range services { - observedState[s] = Containers{} - } - for _, c := range state.filter(isNotOneOff) { - service := c.Labels[api.ServiceLabel] - observedState[service] = append(observedState[service], c) - } - return &convergence{ - service: s, - observedState: observedState, - } -} - -func (c *convergence) apply(ctx context.Context, project *types.Project, options api.CreateOptions) error { - return InDependencyOrder(ctx, project, func(ctx context.Context, name string) error { - service, err := project.GetService(name) - if err != nil { - return err - } - - strategy := options.RecreateDependencies - if utils.StringContains(options.Services, name) { - strategy = options.Recreate - } - err = c.ensureService(ctx, project, service, strategy, options.Inherit, options.Timeout) - if err != nil { - return err - } - - c.updateProject(project, name) - return nil - }) -} - -var mu sync.Mutex - -// updateProject updates project after service converged, so dependent services relying on `service:xx` can refer to actual containers. -func (c *convergence) updateProject(project *types.Project, service string) { - containers := c.getObservedState(service) - if len(containers) == 0 { - return - } - container := containers[0] - - // operation is protected by a Mutex so that we can safely update project.Services while running concurrent convergence on services - mu.Lock() - defer mu.Unlock() - - for i, s := range project.Services { - if d := getDependentServiceFromMode(s.NetworkMode); d == service { - s.NetworkMode = types.NetworkModeContainerPrefix + container.ID - } - if d := getDependentServiceFromMode(s.Ipc); d == service { - s.Ipc = types.NetworkModeContainerPrefix + container.ID - } - if d := getDependentServiceFromMode(s.Pid); d == service { - s.Pid = types.NetworkModeContainerPrefix + container.ID - } - var links []string - for _, serviceLink := range s.Links { - parts := strings.Split(serviceLink, ":") - serviceName := serviceLink - serviceAlias := "" - if len(parts) == 2 { - serviceName = parts[0] - serviceAlias = parts[1] - } - if serviceName != service { - links = append(links, serviceLink) - continue - } - for _, container := range containers { - name := getCanonicalContainerName(container) - if serviceAlias != "" { - links = append(links, - fmt.Sprintf("%s:%s", name, serviceAlias)) - } - links = append(links, - fmt.Sprintf("%s:%s", name, name), - fmt.Sprintf("%s:%s", name, getContainerNameWithoutProject(container))) - } - s.Links = links - } - project.Services[i] = s - } -} - -func (c *convergence) ensureService(ctx context.Context, project *types.Project, service types.ServiceConfig, recreate string, inherit bool, timeout *time.Duration) error { - expected, err := getScale(service) - if err != nil { - return err - } - containers := c.getObservedState(service.Name) - actual := len(containers) - updated := make(Containers, expected) - - eg, _ := errgroup.WithContext(ctx) - - for i, container := range containers { - if i > expected { - // Scale Down - container := container - eg.Go(func() error { - err := c.service.apiClient.ContainerStop(ctx, container.ID, timeout) - if err != nil { - return err - } - return c.service.apiClient.ContainerRemove(ctx, container.ID, moby.ContainerRemoveOptions{}) - }) - continue - } - - if recreate == api.RecreateNever { - continue - } - // Re-create diverged containers - configHash, err := ServiceHash(service) - if err != nil { - return err - } - name := getContainerProgressName(container) - diverged := container.Labels[api.ConfigHashLabel] != configHash - if diverged || recreate == api.RecreateForce || service.Extensions[extLifecycle] == forceRecreate { - i, container := i, container - eg.Go(func() error { - recreated, err := c.service.recreateContainer(ctx, project, service, container, inherit, timeout) - updated[i] = recreated - return err - }) - continue - } - - // Enforce non-diverged containers are running - w := progress.ContextWriter(ctx) - switch container.State { - case ContainerRunning: - w.Event(progress.RunningEvent(name)) - case ContainerCreated: - case ContainerRestarting: - case ContainerExited: - w.Event(progress.CreatedEvent(name)) - default: - container := container - eg.Go(func() error { - return c.service.startContainer(ctx, container) - }) - } - updated[i] = container - } - - next, err := nextContainerNumber(containers) - if err != nil { - return err - } - for i := 0; i < expected-actual; i++ { - // Scale UP - number := next + i - name := getContainerName(project.Name, service, number) - i := i - eg.Go(func() error { - container, err := c.service.createContainer(ctx, project, service, name, number, false, true) - updated[actual+i] = container - return err - }) - continue - } - - err = eg.Wait() - c.setObservedState(service.Name, updated) - return err -} - -func getContainerName(projectName string, service types.ServiceConfig, number int) string { - name := fmt.Sprintf("%s_%s_%d", projectName, service.Name, number) - if service.ContainerName != "" { - name = service.ContainerName - } - return name -} - -func getContainerProgressName(container moby.Container) string { - return "Container " + getCanonicalContainerName(container) -} - -func (s *composeService) waitDependencies(ctx context.Context, project *types.Project, service types.ServiceConfig) error { - eg, _ := errgroup.WithContext(ctx) - for dep, config := range service.DependsOn { - dep, config := dep, config - eg.Go(func() error { - ticker := time.NewTicker(500 * time.Millisecond) - defer ticker.Stop() - for { - <-ticker.C - switch config.Condition { - case types.ServiceConditionHealthy: - healthy, err := s.isServiceHealthy(ctx, project, dep) - if err != nil { - return err - } - if healthy { - return nil - } - case types.ServiceConditionCompletedSuccessfully: - exited, code, err := s.isServiceCompleted(ctx, project, dep) - if err != nil { - return err - } - if exited { - if code != 0 { - return fmt.Errorf("service %q didn't completed successfully: exit %d", dep, code) - } - return nil - } - case types.ServiceConditionStarted: - // already managed by InDependencyOrder - return nil - default: - logrus.Warnf("unsupported depends_on condition: %s", config.Condition) - return nil - } - } - }) - } - return eg.Wait() -} - -func nextContainerNumber(containers []moby.Container) (int, error) { - max := 0 - for _, c := range containers { - n, err := strconv.Atoi(c.Labels[api.ContainerNumberLabel]) - if err != nil { - return 0, err - } - if n > max { - max = n - } - } - return max + 1, nil - -} - -func getScale(config types.ServiceConfig) (int, error) { - scale := 1 - var err error - if config.Deploy != nil && config.Deploy.Replicas != nil { - scale = int(*config.Deploy.Replicas) - } - if config.Scale != 0 { - scale = config.Scale - } - if scale > 1 && config.ContainerName != "" { - scale = -1 - err = fmt.Errorf(doubledContainerNameWarning, - config.Name, - config.ContainerName) - } - return scale, err -} - -func (s *composeService) createContainer(ctx context.Context, project *types.Project, service types.ServiceConfig, - name string, number int, autoRemove bool, useNetworkAliases bool) (container moby.Container, err error) { - w := progress.ContextWriter(ctx) - eventName := "Container " + name - w.Event(progress.CreatingEvent(eventName)) - container, err = s.createMobyContainer(ctx, project, service, name, number, nil, autoRemove, useNetworkAliases) - if err != nil { - return - } - w.Event(progress.CreatedEvent(eventName)) - return -} - -func (s *composeService) recreateContainer(ctx context.Context, project *types.Project, service types.ServiceConfig, - replaced moby.Container, inherit bool, timeout *time.Duration) (moby.Container, error) { - var created moby.Container - w := progress.ContextWriter(ctx) - w.Event(progress.NewEvent(getContainerProgressName(replaced), progress.Working, "Recreate")) - err := s.apiClient.ContainerStop(ctx, replaced.ID, timeout) - if err != nil { - return created, err - } - name := getCanonicalContainerName(replaced) - tmpName := fmt.Sprintf("%s_%s", replaced.ID[:12], name) - err = s.apiClient.ContainerRename(ctx, replaced.ID, tmpName) - if err != nil { - return created, err - } - number, err := strconv.Atoi(replaced.Labels[api.ContainerNumberLabel]) - if err != nil { - return created, err - } - - var inherited *moby.Container - if inherit { - inherited = &replaced - } - created, err = s.createMobyContainer(ctx, project, service, name, number, inherited, false, true) - if err != nil { - return created, err - } - err = s.apiClient.ContainerRemove(ctx, replaced.ID, moby.ContainerRemoveOptions{}) - if err != nil { - return created, err - } - w.Event(progress.NewEvent(getContainerProgressName(replaced), progress.Done, "Recreated")) - setDependentLifecycle(project, service.Name, forceRecreate) - return created, err -} - -// setDependentLifecycle define the Lifecycle strategy for all services to depend on specified service -func setDependentLifecycle(project *types.Project, service string, strategy string) { - for i, s := range project.Services { - if utils.StringContains(s.GetDependencies(), service) { - if s.Extensions == nil { - s.Extensions = map[string]interface{}{} - } - s.Extensions[extLifecycle] = strategy - project.Services[i] = s - } - } -} - -func (s *composeService) startContainer(ctx context.Context, container moby.Container) error { - w := progress.ContextWriter(ctx) - w.Event(progress.NewEvent(getContainerProgressName(container), progress.Working, "Restart")) - err := s.apiClient.ContainerStart(ctx, container.ID, moby.ContainerStartOptions{}) - if err != nil { - return err - } - w.Event(progress.NewEvent(getContainerProgressName(container), progress.Done, "Restarted")) - return nil -} - -func (s *composeService) createMobyContainer(ctx context.Context, project *types.Project, service types.ServiceConfig, - name string, number int, inherit *moby.Container, autoRemove bool, useNetworkAliases bool) (moby.Container, error) { - var created moby.Container - containerConfig, hostConfig, networkingConfig, err := s.getCreateOptions(ctx, project, service, number, inherit, autoRemove) - if err != nil { - return created, err - } - var plat *specs.Platform - if service.Platform != "" { - var p specs.Platform - p, err = platforms.Parse(service.Platform) - if err != nil { - return created, err - } - plat = &p - } - response, err := s.apiClient.ContainerCreate(ctx, containerConfig, hostConfig, networkingConfig, plat, name) - if err != nil { - return created, err - } - inspectedContainer, err := s.apiClient.ContainerInspect(ctx, response.ID) - if err != nil { - return created, err - } - created = moby.Container{ - ID: inspectedContainer.ID, - Labels: inspectedContainer.Config.Labels, - Names: []string{inspectedContainer.Name}, - NetworkSettings: &moby.SummaryNetworkSettings{ - Networks: inspectedContainer.NetworkSettings.Networks, - }, - } - links := append(service.Links, service.ExternalLinks...) - for _, netName := range service.NetworksByPriority() { - netwrk := project.Networks[netName] - cfg := service.Networks[netName] - aliases := []string{getContainerName(project.Name, service, number)} - if useNetworkAliases { - aliases = append(aliases, service.Name) - if cfg != nil { - aliases = append(aliases, cfg.Aliases...) - } - } - if val, ok := created.NetworkSettings.Networks[netwrk.Name]; ok { - if shortIDAliasExists(created.ID, val.Aliases...) { - continue - } - err = s.apiClient.NetworkDisconnect(ctx, netwrk.Name, created.ID, false) - if err != nil { - return created, err - } - } - err = s.connectContainerToNetwork(ctx, created.ID, netwrk.Name, cfg, links, aliases...) - if err != nil { - return created, err - } - } - return created, err -} - -func shortIDAliasExists(containerID string, aliases ...string) bool { - for _, alias := range aliases { - if alias == containerID[:12] { - return true - } - } - return false -} - -func (s *composeService) connectContainerToNetwork(ctx context.Context, id string, netwrk string, cfg *types.ServiceNetworkConfig, links []string, aliases ...string) error { - var ( - ipv4Address string - ipv6Address string - ipam *network.EndpointIPAMConfig - ) - if cfg != nil { - ipv4Address = cfg.Ipv4Address - ipv6Address = cfg.Ipv6Address - ipam = &network.EndpointIPAMConfig{ - IPv4Address: ipv4Address, - IPv6Address: ipv6Address, - } - } - err := s.apiClient.NetworkConnect(ctx, netwrk, id, &network.EndpointSettings{ - Aliases: aliases, - IPAddress: ipv4Address, - GlobalIPv6Address: ipv6Address, - Links: links, - IPAMConfig: ipam, - }) - if err != nil { - return err - } - return nil -} - -func (s *composeService) isServiceHealthy(ctx context.Context, project *types.Project, service string) (bool, error) { - containers, err := s.getContainers(ctx, project.Name, oneOffExclude, false, service) - if err != nil { - return false, err - } - - if len(containers) == 0 { - return false, nil - } - for _, c := range containers { - container, err := s.apiClient.ContainerInspect(ctx, c.ID) - if err != nil { - return false, err - } - if container.State == nil || container.State.Health == nil { - return false, fmt.Errorf("container for service %q has no healthcheck configured", service) - } - if container.State.Health.Status != moby.Healthy { - return false, nil - } - } - return true, nil -} - -func (s *composeService) isServiceCompleted(ctx context.Context, project *types.Project, dep string) (bool, int, error) { - containers, err := s.getContainers(ctx, project.Name, oneOffExclude, true, dep) - if err != nil { - return false, 0, err - } - for _, c := range containers { - container, err := s.apiClient.ContainerInspect(ctx, c.ID) - if err != nil { - return false, 0, err - } - if container.State != nil && container.State.Status == "exited" { - return true, container.State.ExitCode, nil - } - } - return false, 0, nil -} - -func (s *composeService) startService(ctx context.Context, project *types.Project, service types.ServiceConfig) error { - err := s.waitDependencies(ctx, project, service) - if err != nil { - return err - } - containers, err := s.apiClient.ContainerList(ctx, moby.ContainerListOptions{ - Filters: filters.NewArgs( - projectFilter(project.Name), - serviceFilter(service.Name), - oneOffFilter(false), - ), - All: true, - }) - if err != nil { - return err - } - - if len(containers) == 0 { - if scale, err := getScale(service); err != nil && scale == 0 { - return nil - } - return fmt.Errorf("no containers to start") - } - - w := progress.ContextWriter(ctx) - eg, ctx := errgroup.WithContext(ctx) - for _, container := range containers { - if container.State == ContainerRunning { - continue - } - container := container - eg.Go(func() error { - eventName := getContainerProgressName(container) - w.Event(progress.StartingEvent(eventName)) - err := s.apiClient.ContainerStart(ctx, container.ID, moby.ContainerStartOptions{}) - if err == nil { - w.Event(progress.StartedEvent(eventName)) - } - return err - }) - } - return eg.Wait() -} diff --git a/pkg/compose/convergence_test.go b/pkg/compose/convergence_test.go deleted file mode 100644 index 3a5009a0..00000000 --- a/pkg/compose/convergence_test.go +++ /dev/null @@ -1,55 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package compose - -import ( - "fmt" - "testing" - - "github.com/compose-spec/compose-go/types" - "gotest.tools/assert" -) - -func TestContainerName(t *testing.T) { - var replicas uint64 = 1 - s := types.ServiceConfig{ - Name: "testservicename", - ContainerName: "testcontainername", - Scale: 1, - Deploy: &types.DeployConfig{}, - } - ret, err := getScale(s) - assert.NilError(t, err) - assert.Equal(t, ret, s.Scale) - - s.Scale = 0 - s.Deploy.Replicas = &replicas - ret, err = getScale(s) - assert.NilError(t, err) - assert.Equal(t, ret, int(*s.Deploy.Replicas)) - - s.Deploy.Replicas = nil - s.Scale = 2 - _, err = getScale(s) - assert.Error(t, err, fmt.Sprintf(doubledContainerNameWarning, s.Name, s.ContainerName)) - - replicas = 2 - s.Deploy.Replicas = &replicas - s.Scale = 0 - _, err = getScale(s) - assert.Error(t, err, fmt.Sprintf(doubledContainerNameWarning, s.Name, s.ContainerName)) -} diff --git a/pkg/compose/convert.go b/pkg/compose/convert.go deleted file mode 100644 index 6f092ee2..00000000 --- a/pkg/compose/convert.go +++ /dev/null @@ -1,83 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package compose - -import ( - "fmt" - "time" - - compose "github.com/compose-spec/compose-go/types" - "github.com/docker/docker/api/types/container" -) - -// ToMobyEnv convert into []string -func ToMobyEnv(environment compose.MappingWithEquals) []string { - var env []string - for k, v := range environment { - if v == nil { - env = append(env, k) - } else { - env = append(env, fmt.Sprintf("%s=%s", k, *v)) - } - } - return env -} - -// ToMobyHealthCheck convert into container.HealthConfig -func ToMobyHealthCheck(check *compose.HealthCheckConfig) *container.HealthConfig { - if check == nil { - return nil - } - var ( - interval time.Duration - timeout time.Duration - period time.Duration - retries int - ) - if check.Interval != nil { - interval = time.Duration(*check.Interval) - } - if check.Timeout != nil { - timeout = time.Duration(*check.Timeout) - } - if check.StartPeriod != nil { - period = time.Duration(*check.StartPeriod) - } - if check.Retries != nil { - retries = int(*check.Retries) - } - test := check.Test - if check.Disable { - test = []string{"NONE"} - } - return &container.HealthConfig{ - Test: test, - Interval: interval, - Timeout: timeout, - StartPeriod: period, - Retries: retries, - } -} - -// ToSeconds convert into seconds -func ToSeconds(d *compose.Duration) *int { - if d == nil { - return nil - } - s := int(time.Duration(*d).Seconds()) - return &s -} diff --git a/pkg/compose/cp.go b/pkg/compose/cp.go deleted file mode 100644 index fc1cc305..00000000 --- a/pkg/compose/cp.go +++ /dev/null @@ -1,280 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package compose - -import ( - "context" - "fmt" - "io" - "os" - "path/filepath" - "strings" - - "golang.org/x/sync/errgroup" - - "github.com/compose-spec/compose-go/types" - "github.com/docker/cli/cli/command" - "github.com/docker/compose-cli/pkg/api" - moby "github.com/docker/docker/api/types" - "github.com/docker/docker/api/types/filters" - "github.com/docker/docker/pkg/archive" - "github.com/docker/docker/pkg/system" - "github.com/pkg/errors" -) - -type copyDirection int - -const ( - fromService copyDirection = 1 << iota - toService - acrossServices = fromService | toService -) - -func (s *composeService) Copy(ctx context.Context, project *types.Project, opts api.CopyOptions) error { - srcService, srcPath := splitCpArg(opts.Source) - destService, dstPath := splitCpArg(opts.Destination) - - var direction copyDirection - var serviceName string - if srcService != "" { - direction |= fromService - serviceName = srcService - - // copying from multiple containers of a services doesn't make sense. - if opts.All { - return errors.New("cannot use the --all flag when copying from a service") - } - } - if destService != "" { - direction |= toService - serviceName = destService - } - - f := filters.NewArgs( - projectFilter(project.Name), - serviceFilter(serviceName), - ) - if !opts.All { - f.Add("label", fmt.Sprintf("%s=%d", api.ContainerNumberLabel, opts.Index)) - } - containers, err := s.apiClient.ContainerList(ctx, moby.ContainerListOptions{Filters: f}) - if err != nil { - return err - } - - if len(containers) < 1 { - return fmt.Errorf("service %s not running", serviceName) - } - - g := errgroup.Group{} - for _, container := range containers { - containerID := container.ID - g.Go(func() error { - switch direction { - case fromService: - return s.copyFromContainer(ctx, containerID, srcPath, dstPath, opts) - case toService: - return s.copyToContainer(ctx, containerID, srcPath, dstPath, opts) - case acrossServices: - return errors.New("copying between services is not supported") - default: - return errors.New("unknown copy direction") - } - }) - } - - return g.Wait() -} - -func (s *composeService) copyToContainer(ctx context.Context, containerID string, srcPath string, dstPath string, opts api.CopyOptions) error { - var err error - if srcPath != "-" { - // Get an absolute source path. - srcPath, err = resolveLocalPath(srcPath) - if err != nil { - return err - } - } - - // Prepare destination copy info by stat-ing the container path. - dstInfo := archive.CopyInfo{Path: dstPath} - dstStat, err := s.apiClient.ContainerStatPath(ctx, containerID, dstPath) - - // If the destination is a symbolic link, we should evaluate it. - if err == nil && dstStat.Mode&os.ModeSymlink != 0 { - linkTarget := dstStat.LinkTarget - if !system.IsAbs(linkTarget) { - // Join with the parent directory. - dstParent, _ := archive.SplitPathDirEntry(dstPath) - linkTarget = filepath.Join(dstParent, linkTarget) - } - - dstInfo.Path = linkTarget - dstStat, err = s.apiClient.ContainerStatPath(ctx, containerID, linkTarget) - } - - // Validate the destination path - if err := command.ValidateOutputPathFileMode(dstStat.Mode); err != nil { - return errors.Wrapf(err, `destination "%s:%s" must be a directory or a regular file`, containerID, dstPath) - } - - // Ignore any error and assume that the parent directory of the destination - // path exists, in which case the copy may still succeed. If there is any - // type of conflict (e.g., non-directory overwriting an existing directory - // or vice versa) the extraction will fail. If the destination simply did - // not exist, but the parent directory does, the extraction will still - // succeed. - if err == nil { - dstInfo.Exists, dstInfo.IsDir = true, dstStat.Mode.IsDir() - } - - var ( - content io.Reader - resolvedDstPath string - ) - - if srcPath == "-" { - content = os.Stdin - resolvedDstPath = dstInfo.Path - if !dstInfo.IsDir { - return errors.Errorf("destination \"%s:%s\" must be a directory", containerID, dstPath) - } - } else { - // Prepare source copy info. - srcInfo, err := archive.CopyInfoSourcePath(srcPath, opts.FollowLink) - if err != nil { - return err - } - - srcArchive, err := archive.TarResource(srcInfo) - if err != nil { - return err - } - defer srcArchive.Close() //nolint:errcheck - - // With the stat info about the local source as well as the - // destination, we have enough information to know whether we need to - // alter the archive that we upload so that when the server extracts - // it to the specified directory in the container we get the desired - // copy behavior. - - // See comments in the implementation of `archive.PrepareArchiveCopy` - // for exactly what goes into deciding how and whether the source - // archive needs to be altered for the correct copy behavior when it is - // extracted. This function also infers from the source and destination - // info which directory to extract to, which may be the parent of the - // destination that the user specified. - dstDir, preparedArchive, err := archive.PrepareArchiveCopy(srcArchive, srcInfo, dstInfo) - if err != nil { - return err - } - defer preparedArchive.Close() //nolint:errcheck - - resolvedDstPath = dstDir - content = preparedArchive - } - - options := moby.CopyToContainerOptions{ - AllowOverwriteDirWithFile: false, - CopyUIDGID: opts.CopyUIDGID, - } - return s.apiClient.CopyToContainer(ctx, containerID, resolvedDstPath, content, options) -} - -func (s *composeService) copyFromContainer(ctx context.Context, containerID, srcPath, dstPath string, opts api.CopyOptions) error { - var err error - if dstPath != "-" { - // Get an absolute destination path. - dstPath, err = resolveLocalPath(dstPath) - if err != nil { - return err - } - } - - if err := command.ValidateOutputPath(dstPath); err != nil { - return err - } - - // if client requests to follow symbol link, then must decide target file to be copied - var rebaseName string - if opts.FollowLink { - srcStat, err := s.apiClient.ContainerStatPath(ctx, containerID, srcPath) - - // If the destination is a symbolic link, we should follow it. - if err == nil && srcStat.Mode&os.ModeSymlink != 0 { - linkTarget := srcStat.LinkTarget - if !system.IsAbs(linkTarget) { - // Join with the parent directory. - srcParent, _ := archive.SplitPathDirEntry(srcPath) - linkTarget = filepath.Join(srcParent, linkTarget) - } - - linkTarget, rebaseName = archive.GetRebaseName(srcPath, linkTarget) - srcPath = linkTarget - } - } - - content, stat, err := s.apiClient.CopyFromContainer(ctx, containerID, srcPath) - if err != nil { - return err - } - defer content.Close() //nolint:errcheck - - if dstPath == "-" { - _, err = io.Copy(os.Stdout, content) - return err - } - - srcInfo := archive.CopyInfo{ - Path: srcPath, - Exists: true, - IsDir: stat.Mode.IsDir(), - RebaseName: rebaseName, - } - - preArchive := content - if len(srcInfo.RebaseName) != 0 { - _, srcBase := archive.SplitPathDirEntry(srcInfo.Path) - preArchive = archive.RebaseArchiveEntries(content, srcBase, srcInfo.RebaseName) - } - - return archive.CopyTo(preArchive, srcInfo, dstPath) -} - -func splitCpArg(arg string) (container, path string) { - if system.IsAbs(arg) { - // Explicit local absolute path, e.g., `C:\foo` or `/foo`. - return "", arg - } - - parts := strings.SplitN(arg, ":", 2) - - if len(parts) == 1 || strings.HasPrefix(parts[0], ".") { - // Either there's no `:` in the arg - // OR it's an explicit local relative path like `./file:name.txt`. - return "", arg - } - - return parts[0], parts[1] -} - -func resolveLocalPath(localPath string) (absPath string, err error) { - if absPath, err = filepath.Abs(localPath); err != nil { - return - } - return archive.PreserveTrailingDotOrSeparator(absPath, localPath, filepath.Separator), nil -} diff --git a/pkg/compose/create.go b/pkg/compose/create.go deleted file mode 100644 index 3f477ee4..00000000 --- a/pkg/compose/create.go +++ /dev/null @@ -1,1046 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package compose - -import ( - "context" - "fmt" - "path" - "path/filepath" - "strconv" - "strings" - - "github.com/compose-spec/compose-go/types" - moby "github.com/docker/docker/api/types" - "github.com/docker/docker/api/types/blkiodev" - "github.com/docker/docker/api/types/container" - "github.com/docker/docker/api/types/mount" - "github.com/docker/docker/api/types/network" - "github.com/docker/docker/api/types/strslice" - volume_api "github.com/docker/docker/api/types/volume" - "github.com/docker/docker/errdefs" - "github.com/docker/go-connections/nat" - "github.com/docker/go-units" - "github.com/pkg/errors" - "github.com/sirupsen/logrus" - - "github.com/docker/compose-cli/pkg/api" - "github.com/docker/compose-cli/pkg/progress" - "github.com/docker/compose-cli/pkg/utils" -) - -func (s *composeService) Create(ctx context.Context, project *types.Project, options api.CreateOptions) error { - return progress.Run(ctx, func(ctx context.Context) error { - return s.create(ctx, project, options) - }) -} - -func (s *composeService) create(ctx context.Context, project *types.Project, options api.CreateOptions) error { - if len(options.Services) == 0 { - options.Services = project.ServiceNames() - } - - var observedState Containers - observedState, err := s.getContainers(ctx, project.Name, oneOffInclude, true) - if err != nil { - return err - } - - err = s.ensureImagesExists(ctx, project, options.QuietPull) - if err != nil { - return err - } - - prepareNetworks(project) - - err = prepareVolumes(project) - if err != nil { - return err - } - - if err := s.ensureNetworks(ctx, project.Networks); err != nil { - return err - } - - if err := s.ensureProjectVolumes(ctx, project); err != nil { - return err - } - - allServices := project.AllServices() - allServiceNames := []string{} - for _, service := range allServices { - allServiceNames = append(allServiceNames, service.Name) - } - orphans := observedState.filter(isNotService(allServiceNames...)) - if len(orphans) > 0 { - if options.RemoveOrphans { - w := progress.ContextWriter(ctx) - err := s.removeContainers(ctx, w, orphans, nil, false) - if err != nil { - return err - } - } else { - logrus.Warnf("Found orphan containers (%s) for this project. If "+ - "you removed or renamed this service in your compose "+ - "file, you can run this command with the "+ - "--remove-orphans flag to clean it up.", orphans.names()) - } - } - - err = prepareServicesDependsOn(project) - if err != nil { - return err - } - - return newConvergence(options.Services, observedState, s).apply(ctx, project, options) -} - -func prepareVolumes(p *types.Project) error { - for i := range p.Services { - volumesFrom, dependServices, err := getVolumesFrom(p, p.Services[i].VolumesFrom) - if err != nil { - return err - } - p.Services[i].VolumesFrom = volumesFrom - if len(dependServices) > 0 { - if p.Services[i].DependsOn == nil { - p.Services[i].DependsOn = make(types.DependsOnConfig, len(dependServices)) - } - for _, service := range p.Services { - if utils.StringContains(dependServices, service.Name) { - p.Services[i].DependsOn[service.Name] = types.ServiceDependency{ - Condition: types.ServiceConditionStarted, - } - } - } - } - } - return nil -} - -func prepareNetworks(project *types.Project) { - for k, network := range project.Networks { - network.Labels = network.Labels.Add(api.NetworkLabel, k) - network.Labels = network.Labels.Add(api.ProjectLabel, project.Name) - network.Labels = network.Labels.Add(api.VersionLabel, api.ComposeVersion) - project.Networks[k] = network - } -} - -func prepareServicesDependsOn(p *types.Project) error { - for i, service := range p.Services { - var dependencies []string - networkDependency := getDependentServiceFromMode(service.NetworkMode) - if networkDependency != "" { - dependencies = append(dependencies, networkDependency) - } - - ipcDependency := getDependentServiceFromMode(service.Ipc) - if ipcDependency != "" { - dependencies = append(dependencies, ipcDependency) - } - - pidDependency := getDependentServiceFromMode(service.Pid) - if pidDependency != "" { - dependencies = append(dependencies, pidDependency) - } - - for _, vol := range service.VolumesFrom { - spec := strings.Split(vol, ":") - if len(spec) == 0 { - continue - } - if spec[0] == "container" { - continue - } - dependencies = append(dependencies, spec[0]) - } - - if len(dependencies) == 0 { - continue - } - if service.DependsOn == nil { - service.DependsOn = make(types.DependsOnConfig) - } - deps, err := p.GetServices(dependencies...) - if err != nil { - return err - } - for _, d := range deps { - if _, ok := service.DependsOn[d.Name]; !ok { - service.DependsOn[d.Name] = types.ServiceDependency{ - Condition: types.ServiceConditionStarted, - } - } - } - p.Services[i] = service - } - return nil -} - -func (s *composeService) ensureNetworks(ctx context.Context, networks types.Networks) error { - for _, network := range networks { - err := s.ensureNetwork(ctx, network) - if err != nil { - return err - } - } - return nil -} - -func (s *composeService) ensureProjectVolumes(ctx context.Context, project *types.Project) error { - for k, volume := range project.Volumes { - volume.Labels = volume.Labels.Add(api.VolumeLabel, k) - volume.Labels = volume.Labels.Add(api.ProjectLabel, project.Name) - volume.Labels = volume.Labels.Add(api.VersionLabel, api.ComposeVersion) - err := s.ensureVolume(ctx, volume) - if err != nil { - return err - } - } - return nil -} - -func getImageName(service types.ServiceConfig, projectName string) string { - imageName := service.Image - if imageName == "" { - imageName = projectName + "_" + service.Name - } - return imageName -} - -func (s *composeService) getCreateOptions(ctx context.Context, p *types.Project, service types.ServiceConfig, number int, inherit *moby.Container, - autoRemove bool) (*container.Config, *container.HostConfig, *network.NetworkingConfig, error) { - - hash, err := ServiceHash(service) - if err != nil { - return nil, nil, nil, err - } - - labels := map[string]string{} - for k, v := range service.Labels { - labels[k] = v - } - - labels[api.ProjectLabel] = p.Name - labels[api.ServiceLabel] = service.Name - labels[api.VersionLabel] = api.ComposeVersion - if _, ok := service.Labels[api.OneoffLabel]; !ok { - labels[api.OneoffLabel] = "False" - } - labels[api.ConfigHashLabel] = hash - labels[api.WorkingDirLabel] = p.WorkingDir - labels[api.ConfigFilesLabel] = strings.Join(p.ComposeFiles, ",") - labels[api.ContainerNumberLabel] = strconv.Itoa(number) - - var ( - runCmd strslice.StrSlice - entrypoint strslice.StrSlice - ) - if len(service.Command) > 0 { - runCmd = strslice.StrSlice(service.Command) - } - if len(service.Entrypoint) > 0 { - entrypoint = strslice.StrSlice(service.Entrypoint) - } - - var ( - tty = service.Tty - stdinOpen = service.StdinOpen - attachStdin = false - ) - - volumeMounts, binds, mounts, err := s.buildContainerVolumes(ctx, *p, service, inherit) - if err != nil { - return nil, nil, nil, err - } - - proxyConfig := types.MappingWithEquals(s.configFile.ParseProxyConfig(s.apiClient.DaemonHost(), nil)) - env := proxyConfig.OverrideBy(service.Environment) - - containerConfig := container.Config{ - Hostname: service.Hostname, - Domainname: service.DomainName, - User: service.User, - ExposedPorts: buildContainerPorts(service), - Tty: tty, - OpenStdin: stdinOpen, - StdinOnce: attachStdin && stdinOpen, - AttachStdin: attachStdin, - AttachStderr: true, - AttachStdout: true, - Cmd: runCmd, - Image: getImageName(service, p.Name), - WorkingDir: service.WorkingDir, - Entrypoint: entrypoint, - NetworkDisabled: service.NetworkMode == "disabled", - MacAddress: service.MacAddress, - Labels: labels, - StopSignal: service.StopSignal, - Env: ToMobyEnv(env), - Healthcheck: ToMobyHealthCheck(service.HealthCheck), - Volumes: volumeMounts, - StopTimeout: ToSeconds(service.StopGracePeriod), - } - - portBindings := buildContainerPortBindingOptions(service) - - resources := getDeployResources(service) - - if service.NetworkMode == "" { - service.NetworkMode = getDefaultNetworkMode(p, service) - } - - var networkConfig *network.NetworkingConfig - - for _, id := range service.NetworksByPriority() { - net := p.Networks[id] - config := service.Networks[id] - var ipam *network.EndpointIPAMConfig - var ( - ipv4Address string - ipv6Address string - ) - if config != nil { - ipv4Address = config.Ipv4Address - ipv6Address = config.Ipv6Address - ipam = &network.EndpointIPAMConfig{ - IPv4Address: ipv4Address, - IPv6Address: ipv6Address, - } - } - networkConfig = &network.NetworkingConfig{ - EndpointsConfig: map[string]*network.EndpointSettings{ - net.Name: { - Aliases: getAliases(service, config), - IPAddress: ipv4Address, - IPv6Gateway: ipv6Address, - IPAMConfig: ipam, - }, - }, - } - break //nolint:staticcheck - } - - tmpfs := map[string]string{} - for _, t := range service.Tmpfs { - if arr := strings.SplitN(t, ":", 2); len(arr) > 1 { - tmpfs[arr[0]] = arr[1] - } else { - tmpfs[arr[0]] = "" - } - } - - var logConfig container.LogConfig - if service.Logging != nil { - logConfig = container.LogConfig{ - Type: service.Logging.Driver, - Config: service.Logging.Options, - } - } - - hostConfig := container.HostConfig{ - AutoRemove: autoRemove, - Binds: binds, - Mounts: mounts, - CapAdd: strslice.StrSlice(service.CapAdd), - CapDrop: strslice.StrSlice(service.CapDrop), - NetworkMode: container.NetworkMode(service.NetworkMode), - Init: service.Init, - IpcMode: container.IpcMode(service.Ipc), - ReadonlyRootfs: service.ReadOnly, - RestartPolicy: getRestartPolicy(service), - ShmSize: int64(service.ShmSize), - Sysctls: service.Sysctls, - PortBindings: portBindings, - Resources: resources, - VolumeDriver: service.VolumeDriver, - VolumesFrom: service.VolumesFrom, - DNS: service.DNS, - DNSSearch: service.DNSSearch, - DNSOptions: service.DNSOpts, - ExtraHosts: service.ExtraHosts, - SecurityOpt: service.SecurityOpt, - UsernsMode: container.UsernsMode(service.UserNSMode), - Privileged: service.Privileged, - PidMode: container.PidMode(service.Pid), - Tmpfs: tmpfs, - Isolation: container.Isolation(service.Isolation), - LogConfig: logConfig, - } - - return &containerConfig, &hostConfig, networkConfig, nil -} - -func getDefaultNetworkMode(project *types.Project, service types.ServiceConfig) string { - mode := "none" - if len(project.Networks) > 0 { - for name := range getNetworksForService(service) { - mode = project.Networks[name].Name - break - } - } - return mode -} - -func getRestartPolicy(service types.ServiceConfig) container.RestartPolicy { - var restart container.RestartPolicy - if service.Restart != "" { - split := strings.Split(service.Restart, ":") - var attempts int - if len(split) > 1 { - attempts, _ = strconv.Atoi(split[1]) - } - restart = container.RestartPolicy{ - Name: split[0], - MaximumRetryCount: attempts, - } - } - if service.Deploy != nil && service.Deploy.RestartPolicy != nil { - policy := *service.Deploy.RestartPolicy - var attempts int - if policy.MaxAttempts != nil { - attempts = int(*policy.MaxAttempts) - } - restart = container.RestartPolicy{ - Name: policy.Condition, - MaximumRetryCount: attempts, - } - } - return restart -} - -func getDeployResources(s types.ServiceConfig) container.Resources { - var swappiness *int64 - if s.MemSwappiness != 0 { - val := int64(s.MemSwappiness) - swappiness = &val - } - resources := container.Resources{ - CgroupParent: s.CgroupParent, - Memory: int64(s.MemLimit), - MemorySwap: int64(s.MemSwapLimit), - MemorySwappiness: swappiness, - MemoryReservation: int64(s.MemReservation), - CPUCount: s.CPUCount, - CPUPeriod: s.CPUPeriod, - CPUQuota: s.CPUQuota, - CPURealtimePeriod: s.CPURTPeriod, - CPURealtimeRuntime: s.CPURTRuntime, - CPUShares: s.CPUShares, - CPUPercent: int64(s.CPUS * 100), - CpusetCpus: s.CPUSet, - } - - setBlkio(s.BlkioConfig, &resources) - - if s.Deploy != nil { - setLimits(s.Deploy.Resources.Limits, &resources) - setReservations(s.Deploy.Resources.Reservations, &resources) - } - - for _, device := range s.Devices { - // FIXME should use docker/cli parseDevice, unfortunately private - src := "" - dst := "" - permissions := "rwm" - arr := strings.Split(device, ":") - switch len(arr) { - case 3: - permissions = arr[2] - fallthrough - case 2: - dst = arr[1] - fallthrough - case 1: - src = arr[0] - } - resources.Devices = append(resources.Devices, container.DeviceMapping{ - PathOnHost: src, - PathInContainer: dst, - CgroupPermissions: permissions, - }) - } - - for name, u := range s.Ulimits { - soft := u.Single - if u.Soft != 0 { - soft = u.Soft - } - hard := u.Single - if u.Hard != 0 { - hard = u.Hard - } - resources.Ulimits = append(resources.Ulimits, &units.Ulimit{ - Name: name, - Hard: int64(hard), - Soft: int64(soft), - }) - } - return resources -} - -func setReservations(reservations *types.Resource, resources *container.Resources) { - if reservations == nil { - return - } - for _, device := range reservations.Devices { - resources.DeviceRequests = append(resources.DeviceRequests, container.DeviceRequest{ - Capabilities: [][]string{device.Capabilities}, - Count: int(device.Count), - DeviceIDs: device.IDs, - Driver: device.Driver, - }) - } -} - -func setLimits(limits *types.Resource, resources *container.Resources) { - if limits == nil { - return - } - if limits.MemoryBytes != 0 { - resources.Memory = int64(limits.MemoryBytes) - } - if limits.NanoCPUs != "" { - i, _ := strconv.ParseInt(limits.NanoCPUs, 10, 64) - resources.NanoCPUs = i - } -} - -func setBlkio(blkio *types.BlkioConfig, resources *container.Resources) { - if blkio == nil { - return - } - resources.BlkioWeight = blkio.Weight - for _, b := range blkio.WeightDevice { - resources.BlkioWeightDevice = append(resources.BlkioWeightDevice, &blkiodev.WeightDevice{ - Path: b.Path, - Weight: b.Weight, - }) - } - for _, b := range blkio.DeviceReadBps { - resources.BlkioDeviceReadBps = append(resources.BlkioDeviceReadBps, &blkiodev.ThrottleDevice{ - Path: b.Path, - Rate: b.Rate, - }) - } - for _, b := range blkio.DeviceReadIOps { - resources.BlkioDeviceReadIOps = append(resources.BlkioDeviceReadIOps, &blkiodev.ThrottleDevice{ - Path: b.Path, - Rate: b.Rate, - }) - } - for _, b := range blkio.DeviceWriteBps { - resources.BlkioDeviceWriteBps = append(resources.BlkioDeviceWriteBps, &blkiodev.ThrottleDevice{ - Path: b.Path, - Rate: b.Rate, - }) - } - for _, b := range blkio.DeviceWriteIOps { - resources.BlkioDeviceWriteIOps = append(resources.BlkioDeviceWriteIOps, &blkiodev.ThrottleDevice{ - Path: b.Path, - Rate: b.Rate, - }) - } -} - -func buildContainerPorts(s types.ServiceConfig) nat.PortSet { - ports := nat.PortSet{} - for _, s := range s.Expose { - p := nat.Port(s) - ports[p] = struct{}{} - } - for _, p := range s.Ports { - p := nat.Port(fmt.Sprintf("%d/%s", p.Target, p.Protocol)) - ports[p] = struct{}{} - } - return ports -} - -func buildContainerPortBindingOptions(s types.ServiceConfig) nat.PortMap { - bindings := nat.PortMap{} - for _, port := range s.Ports { - p := nat.Port(fmt.Sprintf("%d/%s", port.Target, port.Protocol)) - bind := bindings[p] - binding := nat.PortBinding{ - HostIP: port.HostIP, - } - if port.Published > 0 { - binding.HostPort = fmt.Sprint(port.Published) - } - bind = append(bind, binding) - bindings[p] = bind - } - return bindings -} - -func getVolumesFrom(project *types.Project, volumesFrom []string) ([]string, []string, error) { - var volumes = []string{} - var services = []string{} - // parse volumes_from - if len(volumesFrom) == 0 { - return volumes, services, nil - } - for _, vol := range volumesFrom { - spec := strings.Split(vol, ":") - if len(spec) == 0 { - continue - } - if spec[0] == "container" { - volumes = append(volumes, strings.Join(spec[1:], ":")) - continue - } - serviceName := spec[0] - services = append(services, serviceName) - service, err := project.GetService(serviceName) - if err != nil { - return nil, nil, err - } - - firstContainer := getContainerName(project.Name, service, 1) - v := fmt.Sprintf("%s:%s", firstContainer, strings.Join(spec[1:], ":")) - volumes = append(volumes, v) - } - return volumes, services, nil - -} - -func getDependentServiceFromMode(mode string) string { - if strings.HasPrefix(mode, types.NetworkModeServicePrefix) { - return mode[len(types.NetworkModeServicePrefix):] - } - return "" -} - -func (s *composeService) buildContainerVolumes(ctx context.Context, p types.Project, service types.ServiceConfig, - inherit *moby.Container) (map[string]struct{}, []string, []mount.Mount, error) { - var mounts = []mount.Mount{} - - image := getImageName(service, p.Name) - imgInspect, _, err := s.apiClient.ImageInspectWithRaw(ctx, image) - if err != nil { - return nil, nil, nil, err - } - - mountOptions, err := buildContainerMountOptions(p, service, imgInspect, inherit) - if err != nil { - return nil, nil, nil, err - } - - volumeMounts := map[string]struct{}{} - binds := []string{} -MOUNTS: - for _, m := range mountOptions { - volumeMounts[m.Target] = struct{}{} - // `Bind` API is used when host path need to be created if missing, `Mount` is preferred otherwise - if m.Type == mount.TypeBind || m.Type == mount.TypeNamedPipe { - for _, v := range service.Volumes { - if v.Target == m.Target && v.Bind != nil && v.Bind.CreateHostPath { - mode := "rw" - if m.ReadOnly { - mode = "ro" - } - binds = append(binds, fmt.Sprintf("%s:%s:%s", m.Source, m.Target, mode)) - continue MOUNTS - } - } - } - mounts = append(mounts, m) - } - return volumeMounts, binds, mounts, nil -} - -func buildContainerMountOptions(p types.Project, s types.ServiceConfig, img moby.ImageInspect, inherit *moby.Container) ([]mount.Mount, error) { - var mounts = map[string]mount.Mount{} - if inherit != nil { - for _, m := range inherit.Mounts { - if m.Type == "tmpfs" { - continue - } - src := m.Source - if m.Type == "volume" { - src = m.Name - } - m.Destination = path.Clean(m.Destination) - - if img.Config != nil { - if _, ok := img.Config.Volumes[m.Destination]; ok { - // inherit previous container's anonymous volume - mounts[m.Destination] = mount.Mount{ - Type: m.Type, - Source: src, - Target: m.Destination, - ReadOnly: !m.RW, - } - } - } - for i, v := range s.Volumes { - if v.Target != m.Destination { - continue - } - if v.Source == "" { - // inherit previous container's anonymous volume - mounts[m.Destination] = mount.Mount{ - Type: m.Type, - Source: src, - Target: m.Destination, - ReadOnly: !m.RW, - } - // Avoid mount to be later re-defined - l := len(s.Volumes) - 1 - s.Volumes[i] = s.Volumes[l] - s.Volumes = s.Volumes[:l] - } - } - } - } - - mounts, err := fillBindMounts(p, s, mounts) - if err != nil { - return nil, err - } - - values := make([]mount.Mount, 0, len(mounts)) - for _, v := range mounts { - values = append(values, v) - } - return values, nil -} - -func fillBindMounts(p types.Project, s types.ServiceConfig, m map[string]mount.Mount) (map[string]mount.Mount, error) { - for _, v := range s.Volumes { - bindMount, err := buildMount(p, v) - if err != nil { - return nil, err - } - m[bindMount.Target] = bindMount - } - - secrets, err := buildContainerSecretMounts(p, s) - if err != nil { - return nil, err - } - for _, s := range secrets { - if _, found := m[s.Target]; found { - continue - } - m[s.Target] = s - } - - configs, err := buildContainerConfigMounts(p, s) - if err != nil { - return nil, err - } - for _, c := range configs { - if _, found := m[c.Target]; found { - continue - } - m[c.Target] = c - } - return m, nil -} - -func buildContainerConfigMounts(p types.Project, s types.ServiceConfig) ([]mount.Mount, error) { - var mounts = map[string]mount.Mount{} - - configsBaseDir := "/" - for _, config := range s.Configs { - target := config.Target - if config.Target == "" { - target = configsBaseDir + config.Source - } else if !isUnixAbs(config.Target) { - target = configsBaseDir + config.Target - } - - definedConfig := p.Configs[config.Source] - if definedConfig.External.External { - return nil, fmt.Errorf("unsupported external config %s", definedConfig.Name) - } - - bindMount, err := buildMount(p, types.ServiceVolumeConfig{ - Type: types.VolumeTypeBind, - Source: definedConfig.File, - Target: target, - ReadOnly: true, - }) - if err != nil { - return nil, err - } - mounts[target] = bindMount - } - values := make([]mount.Mount, 0, len(mounts)) - for _, v := range mounts { - values = append(values, v) - } - return values, nil -} - -func buildContainerSecretMounts(p types.Project, s types.ServiceConfig) ([]mount.Mount, error) { - var mounts = map[string]mount.Mount{} - - secretsDir := "/run/secrets/" - for _, secret := range s.Secrets { - target := secret.Target - if secret.Target == "" { - target = secretsDir + secret.Source - } else if !isUnixAbs(secret.Target) { - target = secretsDir + secret.Target - } - - definedSecret := p.Secrets[secret.Source] - if definedSecret.External.External { - return nil, fmt.Errorf("unsupported external secret %s", definedSecret.Name) - } - - mount, err := buildMount(p, types.ServiceVolumeConfig{ - Type: types.VolumeTypeBind, - Source: definedSecret.File, - Target: target, - ReadOnly: true, - }) - if err != nil { - return nil, err - } - mounts[target] = mount - } - values := make([]mount.Mount, 0, len(mounts)) - for _, v := range mounts { - values = append(values, v) - } - return values, nil -} - -func isUnixAbs(path string) bool { - return strings.HasPrefix(path, "/") -} - -func buildMount(project types.Project, volume types.ServiceVolumeConfig) (mount.Mount, error) { - source := volume.Source - // on windows, filepath.IsAbs(source) is false for unix style abs path like /var/run/docker.sock. - // do not replace these with filepath.Abs(source) that will include a default drive. - if volume.Type == types.VolumeTypeBind && !filepath.IsAbs(source) && !strings.HasPrefix(source, "/") { - // volume source has already been prefixed with workdir if required, by compose-go project loader - var err error - source, err = filepath.Abs(source) - if err != nil { - return mount.Mount{}, err - } - } - if volume.Type == types.VolumeTypeVolume { - if volume.Source != "" { - pVolume, ok := project.Volumes[volume.Source] - if ok { - source = pVolume.Name - } - } - } - - bind, vol, tmpfs := buildMountOptions(volume) - - volume.Target = path.Clean(volume.Target) - - return mount.Mount{ - Type: mount.Type(volume.Type), - Source: source, - Target: volume.Target, - ReadOnly: volume.ReadOnly, - Consistency: mount.Consistency(volume.Consistency), - BindOptions: bind, - VolumeOptions: vol, - TmpfsOptions: tmpfs, - }, nil -} - -func buildMountOptions(volume types.ServiceVolumeConfig) (*mount.BindOptions, *mount.VolumeOptions, *mount.TmpfsOptions) { - switch volume.Type { - case "bind": - if volume.Volume != nil { - logrus.Warnf("mount of type `bind` should not define `volume` option") - } - if volume.Tmpfs != nil { - logrus.Warnf("mount of type `tmpfs` should not define `tmpfs` option") - } - return buildBindOption(volume.Bind), nil, nil - case "volume": - if volume.Bind != nil { - logrus.Warnf("mount of type `volume` should not define `bind` option") - } - if volume.Tmpfs != nil { - logrus.Warnf("mount of type `volume` should not define `tmpfs` option") - } - return nil, buildVolumeOptions(volume.Volume), nil - case "tmpfs": - if volume.Bind != nil { - logrus.Warnf("mount of type `tmpfs` should not define `bind` option") - } - if volume.Tmpfs != nil { - logrus.Warnf("mount of type `tmpfs` should not define `volumeZ` option") - } - return nil, nil, buildTmpfsOptions(volume.Tmpfs) - } - return nil, nil, nil -} - -func buildBindOption(bind *types.ServiceVolumeBind) *mount.BindOptions { - if bind == nil { - return nil - } - return &mount.BindOptions{ - Propagation: mount.Propagation(bind.Propagation), - // NonRecursive: false, FIXME missing from model ? - } -} - -func buildVolumeOptions(vol *types.ServiceVolumeVolume) *mount.VolumeOptions { - if vol == nil { - return nil - } - return &mount.VolumeOptions{ - NoCopy: vol.NoCopy, - // Labels: , // FIXME missing from model ? - // DriverConfig: , // FIXME missing from model ? - } -} - -func buildTmpfsOptions(tmpfs *types.ServiceVolumeTmpfs) *mount.TmpfsOptions { - if tmpfs == nil { - return nil - } - return &mount.TmpfsOptions{ - SizeBytes: tmpfs.Size, - // Mode: , // FIXME missing from model ? - } -} - -func getAliases(s types.ServiceConfig, c *types.ServiceNetworkConfig) []string { - aliases := []string{s.Name} - if c != nil { - aliases = append(aliases, c.Aliases...) - } - return aliases -} - -func getNetworksForService(s types.ServiceConfig) map[string]*types.ServiceNetworkConfig { - if len(s.Networks) > 0 { - return s.Networks - } - if s.NetworkMode != "" { - return nil - } - return map[string]*types.ServiceNetworkConfig{"default": nil} -} - -func (s *composeService) ensureNetwork(ctx context.Context, n types.NetworkConfig) error { - _, err := s.apiClient.NetworkInspect(ctx, n.Name, moby.NetworkInspectOptions{}) - if err != nil { - if errdefs.IsNotFound(err) { - if n.External.External { - return fmt.Errorf("network %s declared as external, but could not be found", n.Name) - } - var ipam *network.IPAM - if n.Ipam.Config != nil { - var config []network.IPAMConfig - for _, pool := range n.Ipam.Config { - config = append(config, network.IPAMConfig{ - Subnet: pool.Subnet, - IPRange: pool.IPRange, - Gateway: pool.Gateway, - AuxAddress: pool.AuxiliaryAddresses, - }) - } - ipam = &network.IPAM{ - Driver: n.Ipam.Driver, - Config: config, - } - } - createOpts := moby.NetworkCreate{ - // TODO NameSpace Labels - Labels: n.Labels, - Driver: n.Driver, - Options: n.DriverOpts, - Internal: n.Internal, - Attachable: n.Attachable, - IPAM: ipam, - } - - if n.Ipam.Driver != "" || len(n.Ipam.Config) > 0 { - createOpts.IPAM = &network.IPAM{} - } - - if n.Ipam.Driver != "" { - createOpts.IPAM.Driver = n.Ipam.Driver - } - - for _, ipamConfig := range n.Ipam.Config { - config := network.IPAMConfig{ - Subnet: ipamConfig.Subnet, - } - createOpts.IPAM.Config = append(createOpts.IPAM.Config, config) - } - networkEventName := fmt.Sprintf("Network %s", n.Name) - w := progress.ContextWriter(ctx) - w.Event(progress.CreatingEvent(networkEventName)) - if _, err := s.apiClient.NetworkCreate(ctx, n.Name, createOpts); err != nil { - w.Event(progress.ErrorEvent(networkEventName)) - return errors.Wrapf(err, "failed to create network %s", n.Name) - } - w.Event(progress.CreatedEvent(networkEventName)) - return nil - } - return err - } - return nil -} - -func (s *composeService) removeNetwork(ctx context.Context, networkID string, networkName string) error { - w := progress.ContextWriter(ctx) - eventName := fmt.Sprintf("Network %s", networkName) - w.Event(progress.RemovingEvent(eventName)) - - if err := s.apiClient.NetworkRemove(ctx, networkID); err != nil { - w.Event(progress.ErrorEvent(eventName)) - return errors.Wrapf(err, fmt.Sprintf("failed to remove network %s", networkID)) - } - - w.Event(progress.RemovedEvent(eventName)) - return nil -} - -func (s *composeService) ensureVolume(ctx context.Context, volume types.VolumeConfig) error { - // TODO could identify volume by label vs name - _, err := s.apiClient.VolumeInspect(ctx, volume.Name) - if err != nil { - if !errdefs.IsNotFound(err) { - return err - } - eventName := fmt.Sprintf("Volume %q", volume.Name) - w := progress.ContextWriter(ctx) - w.Event(progress.CreatingEvent(eventName)) - _, err := s.apiClient.VolumeCreate(ctx, volume_api.VolumeCreateBody{ - Labels: volume.Labels, - Name: volume.Name, - Driver: volume.Driver, - DriverOpts: volume.DriverOpts, - }) - if err != nil { - w.Event(progress.ErrorEvent(eventName)) - return err - } - w.Event(progress.CreatedEvent(eventName)) - } - return nil -} diff --git a/pkg/compose/create_test.go b/pkg/compose/create_test.go deleted file mode 100644 index 0303deaa..00000000 --- a/pkg/compose/create_test.go +++ /dev/null @@ -1,83 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package compose - -import ( - "os" - "path/filepath" - "testing" - - "github.com/docker/compose-cli/pkg/api" - - "github.com/compose-spec/compose-go/types" - composetypes "github.com/compose-spec/compose-go/types" - mountTypes "github.com/docker/docker/api/types/mount" - "gotest.tools/v3/assert" -) - -func TestBuildBindMount(t *testing.T) { - project := composetypes.Project{} - volume := composetypes.ServiceVolumeConfig{ - Type: composetypes.VolumeTypeBind, - Source: "", - Target: "/data", - } - mount, err := buildMount(project, volume) - assert.NilError(t, err) - assert.Assert(t, filepath.IsAbs(mount.Source)) - _, err = os.Stat(mount.Source) - assert.NilError(t, err) - assert.Equal(t, mount.Type, mountTypes.TypeBind) -} - -func TestBuildVolumeMount(t *testing.T) { - project := composetypes.Project{ - Name: "myProject", - Volumes: composetypes.Volumes(map[string]composetypes.VolumeConfig{ - "myVolume": { - Name: "myProject_myVolume", - }, - }), - } - volume := composetypes.ServiceVolumeConfig{ - Type: composetypes.VolumeTypeVolume, - Source: "myVolume", - Target: "/data", - } - mount, err := buildMount(project, volume) - assert.NilError(t, err) - assert.Equal(t, mount.Source, "myProject_myVolume") - assert.Equal(t, mount.Type, mountTypes.TypeVolume) -} - -func TestServiceImageName(t *testing.T) { - assert.Equal(t, getImageName(types.ServiceConfig{Image: "myImage"}, "myProject"), "myImage") - assert.Equal(t, getImageName(types.ServiceConfig{Name: "aService"}, "myProject"), "myProject_aService") -} - -func TestPrepareNetworkLabels(t *testing.T) { - project := types.Project{ - Name: "myProject", - Networks: types.Networks(map[string]types.NetworkConfig{"skynet": {}}), - } - prepareNetworks(&project) - assert.DeepEqual(t, project.Networks["skynet"].Labels, types.Labels(map[string]string{ - "com.docker.compose.network": "skynet", - "com.docker.compose.project": "myProject", - "com.docker.compose.version": api.ComposeVersion, - })) -} diff --git a/pkg/compose/dependencies.go b/pkg/compose/dependencies.go deleted file mode 100644 index 6d65a033..00000000 --- a/pkg/compose/dependencies.go +++ /dev/null @@ -1,360 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package compose - -import ( - "context" - "fmt" - "strings" - "sync" - - "github.com/compose-spec/compose-go/types" - "golang.org/x/sync/errgroup" - - "github.com/docker/compose-cli/pkg/utils" -) - -// ServiceStatus indicates the status of a service -type ServiceStatus int - -// Services status flags -const ( - ServiceStopped ServiceStatus = iota - ServiceStarted -) - -type graphTraversalConfig struct { - extremityNodesFn func(*Graph) []*Vertex // leaves or roots - adjacentNodesFn func(*Vertex) []*Vertex // getParents or getChildren - filterAdjacentByStatusFn func(*Graph, string, ServiceStatus) []*Vertex // filterChildren or filterParents - targetServiceStatus ServiceStatus - adjacentServiceStatusToSkip ServiceStatus -} - -var ( - upDirectionTraversalConfig = graphTraversalConfig{ - extremityNodesFn: leaves, - adjacentNodesFn: getParents, - filterAdjacentByStatusFn: filterChildren, - adjacentServiceStatusToSkip: ServiceStopped, - targetServiceStatus: ServiceStarted, - } - downDirectionTraversalConfig = graphTraversalConfig{ - extremityNodesFn: roots, - adjacentNodesFn: getChildren, - filterAdjacentByStatusFn: filterParents, - adjacentServiceStatusToSkip: ServiceStarted, - targetServiceStatus: ServiceStopped, - } -) - -// InDependencyOrder applies the function to the services of the project taking in account the dependency order -func InDependencyOrder(ctx context.Context, project *types.Project, fn func(context.Context, string) error) error { - return visit(ctx, project, upDirectionTraversalConfig, fn, ServiceStopped) -} - -// InReverseDependencyOrder applies the function to the services of the project in reverse order of dependencies -func InReverseDependencyOrder(ctx context.Context, project *types.Project, fn func(context.Context, string) error) error { - return visit(ctx, project, downDirectionTraversalConfig, fn, ServiceStarted) -} - -func visit(ctx context.Context, project *types.Project, traversalConfig graphTraversalConfig, fn func(context.Context, string) error, initialStatus ServiceStatus) error { - g := NewGraph(project.Services, initialStatus) - if b, err := g.HasCycles(); b { - return err - } - - nodes := traversalConfig.extremityNodesFn(g) - - eg, _ := errgroup.WithContext(ctx) - eg.Go(func() error { - return run(ctx, g, eg, nodes, traversalConfig, fn) - }) - - return eg.Wait() -} - -// Note: this could be `graph.walk` or whatever -func run(ctx context.Context, graph *Graph, eg *errgroup.Group, nodes []*Vertex, traversalConfig graphTraversalConfig, fn func(context.Context, string) error) error { - for _, node := range nodes { - // Don't start this service yet if all of its children have - // not been started yet. - if len(traversalConfig.filterAdjacentByStatusFn(graph, node.Service, traversalConfig.adjacentServiceStatusToSkip)) != 0 { - continue - } - - node := node - eg.Go(func() error { - err := fn(ctx, node.Service) - if err != nil { - return err - } - - graph.UpdateStatus(node.Service, traversalConfig.targetServiceStatus) - - return run(ctx, graph, eg, traversalConfig.adjacentNodesFn(node), traversalConfig, fn) - }) - } - - return nil -} - -// Graph represents project as service dependencies -type Graph struct { - Vertices map[string]*Vertex - lock sync.RWMutex -} - -// Vertex represents a service in the dependencies structure -type Vertex struct { - Key string - Service string - Status ServiceStatus - Children map[string]*Vertex - Parents map[string]*Vertex -} - -func getParents(v *Vertex) []*Vertex { - return v.GetParents() -} - -// GetParents returns a slice with the parent vertexes of the a Vertex -func (v *Vertex) GetParents() []*Vertex { - var res []*Vertex - for _, p := range v.Parents { - res = append(res, p) - } - return res -} - -func getChildren(v *Vertex) []*Vertex { - return v.GetChildren() -} - -// GetChildren returns a slice with the child vertexes of the a Vertex -func (v *Vertex) GetChildren() []*Vertex { - var res []*Vertex - for _, p := range v.Children { - res = append(res, p) - } - return res -} - -// NewGraph returns the dependency graph of the services -func NewGraph(services types.Services, initialStatus ServiceStatus) *Graph { - graph := &Graph{ - lock: sync.RWMutex{}, - Vertices: map[string]*Vertex{}, - } - - for _, s := range services { - graph.AddVertex(s.Name, s.Name, initialStatus) - } - - for _, s := range services { - for _, name := range s.GetDependencies() { - _ = graph.AddEdge(s.Name, name) - } - } - - return graph -} - -// NewVertex is the constructor function for the Vertex -func NewVertex(key string, service string, initialStatus ServiceStatus) *Vertex { - return &Vertex{ - Key: key, - Service: service, - Status: initialStatus, - Parents: map[string]*Vertex{}, - Children: map[string]*Vertex{}, - } -} - -// AddVertex adds a vertex to the Graph -func (g *Graph) AddVertex(key string, service string, initialStatus ServiceStatus) { - g.lock.Lock() - defer g.lock.Unlock() - - v := NewVertex(key, service, initialStatus) - g.Vertices[key] = v -} - -// AddEdge adds a relationship of dependency between vertexes `source` and `destination` -func (g *Graph) AddEdge(source string, destination string) error { - g.lock.Lock() - defer g.lock.Unlock() - - sourceVertex := g.Vertices[source] - destinationVertex := g.Vertices[destination] - - if sourceVertex == nil { - return fmt.Errorf("could not find %s", source) - } - if destinationVertex == nil { - return fmt.Errorf("could not find %s", destination) - } - - // If they are already connected - if _, ok := sourceVertex.Children[destination]; ok { - return nil - } - - sourceVertex.Children[destination] = destinationVertex - destinationVertex.Parents[source] = sourceVertex - - return nil -} - -func leaves(g *Graph) []*Vertex { - return g.Leaves() -} - -// Leaves returns the slice of leaves of the graph -func (g *Graph) Leaves() []*Vertex { - g.lock.Lock() - defer g.lock.Unlock() - - var res []*Vertex - for _, v := range g.Vertices { - if len(v.Children) == 0 { - res = append(res, v) - } - } - - return res -} - -func roots(g *Graph) []*Vertex { - return g.Roots() -} - -// Roots returns the slice of "Roots" of the graph -func (g *Graph) Roots() []*Vertex { - g.lock.Lock() - defer g.lock.Unlock() - - var res []*Vertex - for _, v := range g.Vertices { - if len(v.Parents) == 0 { - res = append(res, v) - } - } - return res -} - -// UpdateStatus updates the status of a certain vertex -func (g *Graph) UpdateStatus(key string, status ServiceStatus) { - g.lock.Lock() - defer g.lock.Unlock() - g.Vertices[key].Status = status -} - -func filterChildren(g *Graph, k string, s ServiceStatus) []*Vertex { - return g.FilterChildren(k, s) -} - -// FilterChildren returns children of a certain vertex that are in a certain status -func (g *Graph) FilterChildren(key string, status ServiceStatus) []*Vertex { - g.lock.Lock() - defer g.lock.Unlock() - - var res []*Vertex - vertex := g.Vertices[key] - - for _, child := range vertex.Children { - if child.Status == status { - res = append(res, child) - } - } - - return res -} - -func filterParents(g *Graph, k string, s ServiceStatus) []*Vertex { - return g.FilterParents(k, s) -} - -// FilterParents returns the parents of a certain vertex that are in a certain status -func (g *Graph) FilterParents(key string, status ServiceStatus) []*Vertex { - g.lock.Lock() - defer g.lock.Unlock() - - var res []*Vertex - vertex := g.Vertices[key] - - for _, parent := range vertex.Parents { - if parent.Status == status { - res = append(res, parent) - } - } - - return res -} - -// HasCycles detects cycles in the graph -func (g *Graph) HasCycles() (bool, error) { - discovered := []string{} - finished := []string{} - - for _, vertex := range g.Vertices { - path := []string{ - vertex.Key, - } - if !utils.StringContains(discovered, vertex.Key) && !utils.StringContains(finished, vertex.Key) { - var err error - discovered, finished, err = g.visit(vertex.Key, path, discovered, finished) - - if err != nil { - return true, err - } - } - } - - return false, nil -} - -func (g *Graph) visit(key string, path []string, discovered []string, finished []string) ([]string, []string, error) { - discovered = append(discovered, key) - - for _, v := range g.Vertices[key].Children { - path := append(path, v.Key) - if utils.StringContains(discovered, v.Key) { - return nil, nil, fmt.Errorf("cycle found: %s", strings.Join(path, " -> ")) - } - - if !utils.StringContains(finished, v.Key) { - if _, _, err := g.visit(v.Key, path, discovered, finished); err != nil { - return nil, nil, err - } - } - } - - discovered = remove(discovered, key) - finished = append(finished, key) - return discovered, finished, nil -} - -func remove(slice []string, item string) []string { - var s []string - for _, i := range slice { - if i != item { - s = append(s, i) - } - } - return s -} diff --git a/pkg/compose/dependencies_test.go b/pkg/compose/dependencies_test.go deleted file mode 100644 index 5d5871a5..00000000 --- a/pkg/compose/dependencies_test.go +++ /dev/null @@ -1,69 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package compose - -import ( - "context" - "testing" - - "github.com/compose-spec/compose-go/types" - "gotest.tools/v3/assert" -) - -var project = types.Project{ - Services: []types.ServiceConfig{ - { - Name: "test1", - DependsOn: map[string]types.ServiceDependency{ - "test2": {}, - }, - }, - { - Name: "test2", - DependsOn: map[string]types.ServiceDependency{ - "test3": {}, - }, - }, - { - Name: "test3", - }, - }, -} - -func TestInDependencyUpCommandOrder(t *testing.T) { - order := make(chan string) - //nolint:errcheck, unparam - go InDependencyOrder(context.TODO(), &project, func(ctx context.Context, config string) error { - order <- config - return nil - }) - assert.Equal(t, <-order, "test3") - assert.Equal(t, <-order, "test2") - assert.Equal(t, <-order, "test1") -} - -func TestInDependencyReverseDownCommandOrder(t *testing.T) { - order := make(chan string) - //nolint:errcheck, unparam - go InReverseDependencyOrder(context.TODO(), &project, func(ctx context.Context, config string) error { - order <- config - return nil - }) - assert.Equal(t, <-order, "test1") - assert.Equal(t, <-order, "test2") - assert.Equal(t, <-order, "test3") -} diff --git a/pkg/compose/down.go b/pkg/compose/down.go deleted file mode 100644 index 273b9a66..00000000 --- a/pkg/compose/down.go +++ /dev/null @@ -1,277 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package compose - -import ( - "context" - "fmt" - "strings" - "time" - - "github.com/compose-spec/compose-go/cli" - "github.com/compose-spec/compose-go/types" - moby "github.com/docker/docker/api/types" - "github.com/docker/docker/api/types/filters" - "github.com/docker/docker/errdefs" - "golang.org/x/sync/errgroup" - - "github.com/docker/compose-cli/pkg/api" - "github.com/docker/compose-cli/pkg/progress" -) - -type downOp func() error - -func (s *composeService) Down(ctx context.Context, projectName string, options api.DownOptions) error { - return progress.Run(ctx, func(ctx context.Context) error { - return s.down(ctx, strings.ToLower(projectName), options) - }) -} - -func (s *composeService) down(ctx context.Context, projectName string, options api.DownOptions) error { - w := progress.ContextWriter(ctx) - resourceToRemove := false - - var containers Containers - containers, err := s.getContainers(ctx, projectName, oneOffInclude, true) - if err != nil { - return err - } - - if options.Project == nil { - project, err := s.projectFromContainerLabels(containers, projectName) - if err != nil { - return err - } - options.Project = project - } - - if len(containers) > 0 { - resourceToRemove = true - } - - err = InReverseDependencyOrder(ctx, options.Project, func(c context.Context, service string) error { - serviceContainers := containers.filter(isService(service)) - err := s.removeContainers(ctx, w, serviceContainers, options.Timeout, options.Volumes) - return err - }) - if err != nil { - return err - } - - orphans := containers.filter(isNotService(options.Project.ServiceNames()...)) - if options.RemoveOrphans && len(orphans) > 0 { - err := s.removeContainers(ctx, w, orphans, options.Timeout, false) - if err != nil { - return err - } - } - - ops, err := s.ensureNetwoksDown(ctx, projectName) - if err != nil { - return err - } - - if options.Images != "" { - ops = append(ops, s.ensureImagesDown(ctx, projectName, options, w)...) - } - - if options.Volumes { - rm, err := s.ensureVolumesDown(ctx, projectName, w) - if err != nil { - return err - } - ops = append(ops, rm...) - } - - if !resourceToRemove && len(ops) == 0 { - w.Event(progress.NewEvent(projectName, progress.Done, "Warning: No resource found to remove")) - } - - eg, _ := errgroup.WithContext(ctx) - for _, op := range ops { - eg.Go(op) - } - return eg.Wait() -} - -func (s *composeService) ensureVolumesDown(ctx context.Context, projectName string, w progress.Writer) ([]downOp, error) { - var ops []downOp - volumes, err := s.apiClient.VolumeList(ctx, filters.NewArgs(projectFilter(projectName))) - if err != nil { - return ops, err - } - for _, vol := range volumes.Volumes { - id := vol.Name - ops = append(ops, func() error { - return s.removeVolume(ctx, id, w) - }) - } - return ops, nil -} - -func (s *composeService) ensureImagesDown(ctx context.Context, projectName string, options api.DownOptions, w progress.Writer) []downOp { - var ops []downOp - for image := range s.getServiceImages(options, projectName) { - image := image - ops = append(ops, func() error { - return s.removeImage(ctx, image, w) - }) - } - return ops -} - -func (s *composeService) ensureNetwoksDown(ctx context.Context, projectName string) ([]downOp, error) { - var ops []downOp - networks, err := s.apiClient.NetworkList(ctx, moby.NetworkListOptions{Filters: filters.NewArgs(projectFilter(projectName))}) - if err != nil { - return ops, err - } - for _, n := range networks { - networkID := n.ID - networkName := n.Name - ops = append(ops, func() error { - return s.removeNetwork(ctx, networkID, networkName) - }) - } - return ops, nil -} - -func (s *composeService) getServiceImages(options api.DownOptions, projectName string) map[string]struct{} { - images := map[string]struct{}{} - for _, service := range options.Project.Services { - image := service.Image - if options.Images == "local" && image != "" { - continue - } - if image == "" { - image = getImageName(service, projectName) - } - images[image] = struct{}{} - } - return images -} - -func (s *composeService) removeImage(ctx context.Context, image string, w progress.Writer) error { - id := fmt.Sprintf("Image %s", image) - w.Event(progress.NewEvent(id, progress.Working, "Removing")) - _, err := s.apiClient.ImageRemove(ctx, image, moby.ImageRemoveOptions{}) - if err == nil { - w.Event(progress.NewEvent(id, progress.Done, "Removed")) - return nil - } - if errdefs.IsNotFound(err) { - w.Event(progress.NewEvent(id, progress.Done, "Warning: No resource found to remove")) - return nil - } - return err -} - -func (s *composeService) removeVolume(ctx context.Context, id string, w progress.Writer) error { - resource := fmt.Sprintf("Volume %s", id) - w.Event(progress.NewEvent(resource, progress.Working, "Removing")) - err := s.apiClient.VolumeRemove(ctx, id, true) - if err == nil { - w.Event(progress.NewEvent(resource, progress.Done, "Removed")) - return nil - } - if errdefs.IsNotFound(err) { - w.Event(progress.NewEvent(resource, progress.Done, "Warning: No resource found to remove")) - return nil - } - return err -} - -func (s *composeService) stopContainers(ctx context.Context, w progress.Writer, containers []moby.Container, timeout *time.Duration) error { - eg, ctx := errgroup.WithContext(ctx) - for _, container := range containers { - container := container - eg.Go(func() error { - eventName := getContainerProgressName(container) - w.Event(progress.StoppingEvent(eventName)) - err := s.apiClient.ContainerStop(ctx, container.ID, timeout) - if err != nil { - w.Event(progress.ErrorMessageEvent(eventName, "Error while Stopping")) - return err - } - w.Event(progress.StoppedEvent(eventName)) - return nil - }) - } - return eg.Wait() -} - -func (s *composeService) removeContainers(ctx context.Context, w progress.Writer, containers []moby.Container, timeout *time.Duration, volumes bool) error { - eg, _ := errgroup.WithContext(ctx) - for _, container := range containers { - container := container - eg.Go(func() error { - eventName := getContainerProgressName(container) - w.Event(progress.StoppingEvent(eventName)) - err := s.stopContainers(ctx, w, []moby.Container{container}, timeout) - if err != nil { - w.Event(progress.ErrorMessageEvent(eventName, "Error while Stopping")) - return err - } - w.Event(progress.RemovingEvent(eventName)) - err = s.apiClient.ContainerRemove(ctx, container.ID, moby.ContainerRemoveOptions{ - Force: true, - RemoveVolumes: volumes, - }) - if err != nil { - w.Event(progress.ErrorMessageEvent(eventName, "Error while Removing")) - return err - } - w.Event(progress.RemovedEvent(eventName)) - return nil - }) - } - return eg.Wait() -} - -func (s *composeService) projectFromContainerLabels(containers Containers, projectName string) (*types.Project, error) { - fakeProject := &types.Project{ - Name: projectName, - } - if len(containers) == 0 { - return fakeProject, nil - } - options, err := loadProjectOptionsFromLabels(containers[0]) - if err != nil { - return nil, err - } - if options.ConfigPaths[0] == "-" { - for _, container := range containers { - fakeProject.Services = append(fakeProject.Services, types.ServiceConfig{ - Name: container.Labels[api.ServiceLabel], - }) - } - return fakeProject, nil - } - project, err := cli.ProjectFromOptions(options) - if err != nil { - return nil, err - } - - return project, nil -} - -func loadProjectOptionsFromLabels(c moby.Container) (*cli.ProjectOptions, error) { - return cli.NewProjectOptions(strings.Split(c.Labels[api.ConfigFilesLabel], ","), - cli.WithOsEnv, - cli.WithWorkingDirectory(c.Labels[api.WorkingDirLabel]), - cli.WithName(c.Labels[api.ProjectLabel])) -} diff --git a/pkg/compose/down_test.go b/pkg/compose/down_test.go deleted file mode 100644 index 5dcd3843..00000000 --- a/pkg/compose/down_test.go +++ /dev/null @@ -1,105 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package compose - -import ( - "context" - "strings" - "testing" - - compose "github.com/docker/compose-cli/pkg/api" - "github.com/docker/compose-cli/pkg/mocks" - - moby "github.com/docker/docker/api/types" - "github.com/docker/docker/api/types/filters" - "github.com/docker/docker/api/types/volume" - "github.com/golang/mock/gomock" - "gotest.tools/v3/assert" -) - -func TestDown(t *testing.T) { - mockCtrl := gomock.NewController(t) - defer mockCtrl.Finish() - api := mocks.NewMockAPIClient(mockCtrl) - tested.apiClient = api - - api.EXPECT().ContainerList(gomock.Any(), projectFilterListOpt()).Return( - []moby.Container{testContainer("service1", "123"), testContainer("service2", "456"), testContainer("service2", "789"), testContainer("service_orphan", "321")}, nil) - - api.EXPECT().ContainerStop(gomock.Any(), "123", nil).Return(nil) - api.EXPECT().ContainerStop(gomock.Any(), "456", nil).Return(nil) - api.EXPECT().ContainerStop(gomock.Any(), "789", nil).Return(nil) - - api.EXPECT().ContainerRemove(gomock.Any(), "123", moby.ContainerRemoveOptions{Force: true}).Return(nil) - api.EXPECT().ContainerRemove(gomock.Any(), "456", moby.ContainerRemoveOptions{Force: true}).Return(nil) - api.EXPECT().ContainerRemove(gomock.Any(), "789", moby.ContainerRemoveOptions{Force: true}).Return(nil) - - api.EXPECT().NetworkList(gomock.Any(), moby.NetworkListOptions{Filters: filters.NewArgs(projectFilter(strings.ToLower(testProject)))}).Return([]moby.NetworkResource{{ID: "myProject_default"}}, - nil) - - api.EXPECT().NetworkRemove(gomock.Any(), "myProject_default").Return(nil) - - err := tested.Down(context.Background(), strings.ToLower(testProject), compose.DownOptions{}) - assert.NilError(t, err) -} - -func TestDownRemoveOrphans(t *testing.T) { - mockCtrl := gomock.NewController(t) - defer mockCtrl.Finish() - api := mocks.NewMockAPIClient(mockCtrl) - tested.apiClient = api - - api.EXPECT().ContainerList(gomock.Any(), projectFilterListOpt()).Return( - []moby.Container{testContainer("service1", "123"), testContainer("service2", "789"), testContainer("service_orphan", "321")}, nil) - - api.EXPECT().ContainerStop(gomock.Any(), "123", nil).Return(nil) - api.EXPECT().ContainerStop(gomock.Any(), "789", nil).Return(nil) - api.EXPECT().ContainerStop(gomock.Any(), "321", nil).Return(nil) - - api.EXPECT().ContainerRemove(gomock.Any(), "123", moby.ContainerRemoveOptions{Force: true}).Return(nil) - api.EXPECT().ContainerRemove(gomock.Any(), "789", moby.ContainerRemoveOptions{Force: true}).Return(nil) - api.EXPECT().ContainerRemove(gomock.Any(), "321", moby.ContainerRemoveOptions{Force: true}).Return(nil) - - api.EXPECT().NetworkList(gomock.Any(), moby.NetworkListOptions{Filters: filters.NewArgs(projectFilter(strings.ToLower(testProject)))}).Return([]moby.NetworkResource{{ID: "myProject_default"}}, - nil) - - api.EXPECT().NetworkRemove(gomock.Any(), "myProject_default").Return(nil) - - err := tested.Down(context.Background(), strings.ToLower(testProject), compose.DownOptions{RemoveOrphans: true}) - assert.NilError(t, err) -} - -func TestDownRemoveVolumes(t *testing.T) { - mockCtrl := gomock.NewController(t) - defer mockCtrl.Finish() - api := mocks.NewMockAPIClient(mockCtrl) - tested.apiClient = api - - api.EXPECT().ContainerList(gomock.Any(), projectFilterListOpt()).Return( - []moby.Container{testContainer("service1", "123")}, nil) - - api.EXPECT().ContainerStop(gomock.Any(), "123", nil).Return(nil) - api.EXPECT().ContainerRemove(gomock.Any(), "123", moby.ContainerRemoveOptions{Force: true, RemoveVolumes: true}).Return(nil) - - api.EXPECT().NetworkList(gomock.Any(), moby.NetworkListOptions{Filters: filters.NewArgs(projectFilter(strings.ToLower(testProject)))}).Return(nil, nil) - - api.EXPECT().VolumeList(gomock.Any(), filters.NewArgs(projectFilter(strings.ToLower(testProject)))).Return(volume.VolumeListOKBody{Volumes: []*moby.Volume{{Name: "myProject_volume"}}}, nil) - api.EXPECT().VolumeRemove(gomock.Any(), "myProject_volume", true).Return(nil) - - err := tested.Down(context.Background(), strings.ToLower(testProject), compose.DownOptions{Volumes: true}) - assert.NilError(t, err) -} diff --git a/pkg/compose/errors.go b/pkg/compose/errors.go deleted file mode 100644 index d97fe713..00000000 --- a/pkg/compose/errors.go +++ /dev/null @@ -1,72 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package compose - -import ( - "io/fs" - - "github.com/pkg/errors" - - "github.com/compose-spec/compose-go/errdefs" -) - -// Error error to categorize failures and extract metrics info -type Error struct { - Err error - Category *FailureCategory -} - -// WrapComposeError wraps the error if not nil, otherwise returns nil -func WrapComposeError(err error) error { - if err == nil { - return nil - } - return Error{ - Err: err, - } -} - -// WrapCategorisedComposeError wraps the error if not nil, otherwise returns nil -func WrapCategorisedComposeError(err error, failure FailureCategory) error { - if err == nil { - return nil - } - return Error{ - Err: err, - Category: &failure, - } -} - -// Unwrap get underlying error -func (e Error) Unwrap() error { return e.Err } - -func (e Error) Error() string { return e.Err.Error() } - -// GetMetricsFailureCategory get metrics status and error code corresponding to this error -func (e Error) GetMetricsFailureCategory() FailureCategory { - if e.Category != nil { - return *e.Category - } - var pathError *fs.PathError - if errors.As(e.Err, &pathError) { - return FileNotFoundFailure - } - if errdefs.IsNotFoundError(e.Err) { - return FileNotFoundFailure - } - return ComposeParseFailure -} diff --git a/pkg/compose/events.go b/pkg/compose/events.go deleted file mode 100644 index bc28838d..00000000 --- a/pkg/compose/events.go +++ /dev/null @@ -1,81 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package compose - -import ( - "context" - "strings" - "time" - - moby "github.com/docker/docker/api/types" - "github.com/docker/docker/api/types/filters" - - "github.com/docker/compose-cli/pkg/api" - - "github.com/docker/compose-cli/pkg/utils" -) - -func (s *composeService) Events(ctx context.Context, project string, options api.EventsOptions) error { - events, errors := s.apiClient.Events(ctx, moby.EventsOptions{ - Filters: filters.NewArgs(projectFilter(project)), - }) - for { - select { - case event := <-events: - // TODO: support other event types - if event.Type != "container" { - continue - } - - oneOff := event.Actor.Attributes[api.OneoffLabel] - if oneOff == "True" { - // ignore - continue - } - service := event.Actor.Attributes[api.ServiceLabel] - if len(options.Services) > 0 && !utils.StringContains(options.Services, service) { - continue - } - - attributes := map[string]string{} - for k, v := range event.Actor.Attributes { - if strings.HasPrefix(k, "com.docker.compose.") { - continue - } - attributes[k] = v - } - - timestamp := time.Unix(event.Time, 0) - if event.TimeNano != 0 { - timestamp = time.Unix(0, event.TimeNano) - } - err := options.Consumer(api.Event{ - Timestamp: timestamp, - Service: service, - Container: event.ID, - Status: event.Status, - Attributes: attributes, - }) - if err != nil { - return err - } - - case err := <-errors: - return err - } - } -} diff --git a/pkg/compose/exec.go b/pkg/compose/exec.go deleted file mode 100644 index eadb8d09..00000000 --- a/pkg/compose/exec.go +++ /dev/null @@ -1,185 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package compose - -import ( - "context" - "fmt" - "io" - - "github.com/compose-spec/compose-go/types" - "github.com/docker/cli/cli/streams" - moby "github.com/docker/docker/api/types" - "github.com/docker/docker/api/types/filters" - "github.com/docker/docker/pkg/stdcopy" - "github.com/moby/term" - - "github.com/docker/compose-cli/pkg/api" -) - -func (s *composeService) Exec(ctx context.Context, project *types.Project, opts api.RunOptions) (int, error) { - service, err := project.GetService(opts.Service) - if err != nil { - return 0, err - } - - container, err := s.getExecTarget(ctx, project, service, opts) - if err != nil { - return 0, err - } - - exec, err := s.apiClient.ContainerExecCreate(ctx, container.ID, moby.ExecConfig{ - Cmd: opts.Command, - Env: s.getExecEnvironment(project, service, opts), - User: opts.User, - Privileged: opts.Privileged, - Tty: opts.Tty, - Detach: opts.Detach, - WorkingDir: opts.WorkingDir, - - AttachStdin: true, - AttachStdout: true, - AttachStderr: true, - }) - if err != nil { - return 0, err - } - - if opts.Detach { - return 0, s.apiClient.ContainerExecStart(ctx, exec.ID, moby.ExecStartCheck{ - Detach: true, - Tty: opts.Tty, - }) - } - - resp, err := s.apiClient.ContainerExecAttach(ctx, exec.ID, moby.ExecStartCheck{ - Tty: opts.Tty, - }) - if err != nil { - return 0, err - } - defer resp.Close() //nolint:errcheck - - if opts.Tty { - s.monitorTTySize(ctx, exec.ID, s.apiClient.ContainerExecResize) - if err != nil { - return 0, err - } - } - - err = s.interactiveExec(ctx, opts, resp) - if err != nil { - return 0, err - } - - return s.getExecExitStatus(ctx, exec.ID) -} - -// inspired by https://github.com/docker/cli/blob/master/cli/command/container/exec.go#L116 -func (s *composeService) interactiveExec(ctx context.Context, opts api.RunOptions, resp moby.HijackedResponse) error { - outputDone := make(chan error) - inputDone := make(chan error) - - stdout := ContainerStdout{HijackedResponse: resp} - stdin := ContainerStdin{HijackedResponse: resp} - r, err := s.getEscapeKeyProxy(opts.Stdin) - if err != nil { - return err - } - - in := streams.NewIn(opts.Stdin) - if in.IsTerminal() { - state, err := term.SetRawTerminal(in.FD()) - if err != nil { - return err - } - defer term.RestoreTerminal(in.FD(), state) //nolint:errcheck - } - - go func() { - if opts.Tty { - _, err := io.Copy(opts.Stdout, stdout) - outputDone <- err - } else { - _, err := stdcopy.StdCopy(opts.Stdout, opts.Stderr, stdout) - outputDone <- err - } - stdout.Close() //nolint:errcheck - }() - - go func() { - _, err := io.Copy(stdin, r) - inputDone <- err - stdin.Close() //nolint:errcheck - }() - - for { - select { - case err := <-outputDone: - return err - case err := <-inputDone: - if _, ok := err.(term.EscapeError); ok { - return nil - } - if err != nil { - return err - } - // Wait for output to complete streaming - case <-ctx.Done(): - return ctx.Err() - } - } -} - -func (s *composeService) getExecTarget(ctx context.Context, project *types.Project, service types.ServiceConfig, opts api.RunOptions) (moby.Container, error) { - containers, err := s.apiClient.ContainerList(ctx, moby.ContainerListOptions{ - Filters: filters.NewArgs( - projectFilter(project.Name), - serviceFilter(service.Name), - containerNumberFilter(opts.Index), - ), - }) - if err != nil { - return moby.Container{}, err - } - if len(containers) < 1 { - return moby.Container{}, fmt.Errorf("container %s not running", getContainerName(project.Name, service, opts.Index)) - } - container := containers[0] - return container, nil -} - -func (s *composeService) getExecEnvironment(project *types.Project, service types.ServiceConfig, opts api.RunOptions) []string { - var env []string - for k, v := range service.Environment.OverrideBy(types.NewMappingWithEquals(opts.Environment)). - Resolve(func(s string) (string, bool) { - v, ok := project.Environment[s] - return v, ok - }). - RemoveEmpty() { - env = append(env, fmt.Sprintf("%s=%s", k, *v)) - } - return env -} - -func (s *composeService) getExecExitStatus(ctx context.Context, execID string) (int, error) { - resp, err := s.apiClient.ContainerExecInspect(ctx, execID) - if err != nil { - return 0, err - } - return resp.ExitCode, nil -} diff --git a/pkg/compose/filters.go b/pkg/compose/filters.go deleted file mode 100644 index 317353cf..00000000 --- a/pkg/compose/filters.go +++ /dev/null @@ -1,48 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package compose - -import ( - "fmt" - - "github.com/docker/compose-cli/pkg/api" - "github.com/docker/docker/api/types/filters" -) - -func projectFilter(projectName string) filters.KeyValuePair { - return filters.Arg("label", fmt.Sprintf("%s=%s", api.ProjectLabel, projectName)) -} - -func serviceFilter(serviceName string) filters.KeyValuePair { - return filters.Arg("label", fmt.Sprintf("%s=%s", api.ServiceLabel, serviceName)) -} - -func oneOffFilter(b bool) filters.KeyValuePair { - v := "False" - if b { - v = "True" - } - return filters.Arg("label", fmt.Sprintf("%s=%s", api.OneoffLabel, v)) -} - -func containerNumberFilter(index int) filters.KeyValuePair { - return filters.Arg("label", fmt.Sprintf("%s=%d", api.ContainerNumberLabel, index)) -} - -func hasProjectLabelFilter() filters.KeyValuePair { - return filters.Arg("label", api.ProjectLabel) -} diff --git a/pkg/compose/hash.go b/pkg/compose/hash.go deleted file mode 100644 index 56f7e3e9..00000000 --- a/pkg/compose/hash.go +++ /dev/null @@ -1,38 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package compose - -import ( - "encoding/json" - - "github.com/compose-spec/compose-go/types" - "github.com/opencontainers/go-digest" -) - -// ServiceHash compute configuration has for a service -// TODO move this to compose-go -func ServiceHash(o types.ServiceConfig) (string, error) { - // remove the Build config when generating the service hash - o.Build = nil - o.PullPolicy = "" - o.Scale = 1 - bytes, err := json.Marshal(o) - if err != nil { - return "", err - } - return digest.SHA256.FromBytes(bytes).Encoded(), nil -} diff --git a/pkg/compose/images.go b/pkg/compose/images.go deleted file mode 100644 index 92351570..00000000 --- a/pkg/compose/images.go +++ /dev/null @@ -1,114 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package compose - -import ( - "context" - "fmt" - "strings" - "sync" - - moby "github.com/docker/docker/api/types" - "github.com/docker/docker/api/types/filters" - "github.com/docker/docker/errdefs" - "golang.org/x/sync/errgroup" - - "github.com/docker/compose-cli/pkg/api" - "github.com/docker/compose-cli/pkg/utils" -) - -func (s *composeService) Images(ctx context.Context, projectName string, options api.ImagesOptions) ([]api.ImageSummary, error) { - allContainers, err := s.apiClient.ContainerList(ctx, moby.ContainerListOptions{ - Filters: filters.NewArgs(projectFilter(projectName)), - }) - if err != nil { - return nil, err - } - containers := []moby.Container{} - if len(options.Services) > 0 { - // filter service containers - for _, c := range allContainers { - if utils.StringContains(options.Services, c.Labels[api.ServiceLabel]) { - containers = append(containers, c) - - } - } - } else { - containers = allContainers - } - - imageIDs := []string{} - // aggregate image IDs - for _, c := range containers { - if !utils.StringContains(imageIDs, c.ImageID) { - imageIDs = append(imageIDs, c.ImageID) - } - } - images, err := s.getImages(ctx, imageIDs) - if err != nil { - return nil, err - } - summary := make([]api.ImageSummary, len(containers)) - for i, container := range containers { - img, ok := images[container.ImageID] - if !ok { - return nil, fmt.Errorf("failed to retrieve image for container %s", getCanonicalContainerName(container)) - } - - summary[i] = img - summary[i].ContainerName = getCanonicalContainerName(container) - } - return summary, nil -} - -func (s *composeService) getImages(ctx context.Context, images []string) (map[string]api.ImageSummary, error) { - summary := map[string]api.ImageSummary{} - l := sync.Mutex{} - eg, ctx := errgroup.WithContext(ctx) - for _, img := range images { - img := img - eg.Go(func() error { - inspect, _, err := s.apiClient.ImageInspectWithRaw(ctx, img) - if err != nil { - if errdefs.IsNotFound(err) { - return nil - } - return err - } - tag := "" - repository := "" - if len(inspect.RepoTags) > 0 { - - repotag := strings.Split(inspect.RepoTags[0], ":") - repository = repotag[0] - if len(repotag) > 1 { - tag = repotag[1] - } - } - l.Lock() - summary[img] = api.ImageSummary{ - ID: inspect.ID, - Repository: repository, - Tag: tag, - Size: inspect.Size, - } - l.Unlock() - return nil - }) - } - return summary, eg.Wait() -} diff --git a/pkg/compose/kill.go b/pkg/compose/kill.go deleted file mode 100644 index e542dcd5..00000000 --- a/pkg/compose/kill.go +++ /dev/null @@ -1,62 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package compose - -import ( - "context" - - "github.com/compose-spec/compose-go/types" - moby "github.com/docker/docker/api/types" - "golang.org/x/sync/errgroup" - - "github.com/docker/compose-cli/pkg/api" - "github.com/docker/compose-cli/pkg/progress" -) - -func (s *composeService) Kill(ctx context.Context, project *types.Project, options api.KillOptions) error { - return progress.Run(ctx, func(ctx context.Context) error { - return s.kill(ctx, project, options) - }) -} - -func (s *composeService) kill(ctx context.Context, project *types.Project, options api.KillOptions) error { - w := progress.ContextWriter(ctx) - - var containers Containers - containers, err := s.getContainers(ctx, project.Name, oneOffInclude, true) - if err != nil { - return err - } - - eg, ctx := errgroup.WithContext(ctx) - containers. - filter(isService(project.ServiceNames()...)). - forEach(func(container moby.Container) { - eg.Go(func() error { - eventName := getContainerProgressName(container) - w.Event(progress.KillingEvent(eventName)) - err := s.apiClient.ContainerKill(ctx, container.ID, options.Signal) - if err != nil { - w.Event(progress.ErrorMessageEvent(eventName, "Error while Killing")) - return err - } - w.Event(progress.KilledEvent(eventName)) - return nil - }) - }) - return eg.Wait() -} diff --git a/pkg/compose/kill_test.go b/pkg/compose/kill_test.go deleted file mode 100644 index 5a9bb993..00000000 --- a/pkg/compose/kill_test.go +++ /dev/null @@ -1,107 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package compose - -import ( - "context" - "path/filepath" - "strings" - "testing" - - "github.com/compose-spec/compose-go/types" - moby "github.com/docker/docker/api/types" - "github.com/docker/docker/api/types/filters" - "github.com/golang/mock/gomock" - "gotest.tools/v3/assert" - - compose "github.com/docker/compose-cli/pkg/api" - "github.com/docker/compose-cli/pkg/mocks" -) - -const testProject = "testProject" - -var tested = composeService{} - -func TestKillAll(t *testing.T) { - mockCtrl := gomock.NewController(t) - defer mockCtrl.Finish() - api := mocks.NewMockAPIClient(mockCtrl) - tested.apiClient = api - - project := types.Project{Name: strings.ToLower(testProject), Services: []types.ServiceConfig{testService("service1"), testService("service2")}} - - ctx := context.Background() - api.EXPECT().ContainerList(ctx, projectFilterListOpt()).Return( - []moby.Container{testContainer("service1", "123"), testContainer("service1", "456"), testContainer("service2", "789")}, nil) - api.EXPECT().ContainerKill(anyCancellableContext(), "123", "").Return(nil) - api.EXPECT().ContainerKill(anyCancellableContext(), "456", "").Return(nil) - api.EXPECT().ContainerKill(anyCancellableContext(), "789", "").Return(nil) - - err := tested.kill(ctx, &project, compose.KillOptions{}) - assert.NilError(t, err) -} - -func TestKillSignal(t *testing.T) { - mockCtrl := gomock.NewController(t) - defer mockCtrl.Finish() - api := mocks.NewMockAPIClient(mockCtrl) - tested.apiClient = api - - project := types.Project{Name: strings.ToLower(testProject), Services: []types.ServiceConfig{testService("service1")}} - - ctx := context.Background() - api.EXPECT().ContainerList(ctx, projectFilterListOpt()).Return([]moby.Container{testContainer("service1", "123")}, nil) - api.EXPECT().ContainerKill(anyCancellableContext(), "123", "SIGTERM").Return(nil) - - err := tested.kill(ctx, &project, compose.KillOptions{Signal: "SIGTERM"}) - assert.NilError(t, err) -} - -func testService(name string) types.ServiceConfig { - return types.ServiceConfig{Name: name} -} - -func testContainer(service string, id string) moby.Container { - return moby.Container{ - ID: id, - Names: []string{id}, - Labels: containerLabels(service), - } -} - -func containerLabels(service string) map[string]string { - workingdir, _ := filepath.Abs("testdata") - composefile := filepath.Join(workingdir, "compose.yaml") - return map[string]string{ - compose.ServiceLabel: service, - compose.ConfigFilesLabel: composefile, - compose.WorkingDirLabel: workingdir, - compose.ProjectLabel: strings.ToLower(testProject)} -} - -func anyCancellableContext() gomock.Matcher { - ctxWithCancel, cancel := context.WithCancel(context.Background()) - cancel() - return gomock.AssignableToTypeOf(ctxWithCancel) -} - -func projectFilterListOpt() moby.ContainerListOptions { - return moby.ContainerListOptions{ - Filters: filters.NewArgs(projectFilter(strings.ToLower(testProject))), - All: true, - } -} diff --git a/pkg/compose/logs.go b/pkg/compose/logs.go deleted file mode 100644 index 30786d51..00000000 --- a/pkg/compose/logs.go +++ /dev/null @@ -1,87 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package compose - -import ( - "context" - "io" - - "github.com/docker/docker/pkg/stdcopy" - "golang.org/x/sync/errgroup" - - "github.com/docker/compose-cli/pkg/api" - "github.com/docker/compose-cli/pkg/utils" - "github.com/docker/docker/api/types" -) - -func (s *composeService) Logs(ctx context.Context, projectName string, consumer api.LogConsumer, options api.LogOptions) error { - containers, err := s.getContainers(ctx, projectName, oneOffExclude, true, options.Services...) - if err != nil { - return err - } - - eg, ctx := errgroup.WithContext(ctx) - if options.Follow { - eg.Go(func() error { - printer := newLogPrinter(consumer) - return s.watchContainers(projectName, options.Services, printer.HandleEvent, containers, func(c types.Container) error { - return s.logContainers(ctx, consumer, c, options) - }) - }) - } - - for _, c := range containers { - c := c - eg.Go(func() error { - return s.logContainers(ctx, consumer, c, options) - }) - } - return eg.Wait() -} - -func (s *composeService) logContainers(ctx context.Context, consumer api.LogConsumer, c types.Container, options api.LogOptions) error { - cnt, err := s.apiClient.ContainerInspect(ctx, c.ID) - if err != nil { - return err - } - - service := c.Labels[api.ServiceLabel] - r, err := s.apiClient.ContainerLogs(ctx, cnt.ID, types.ContainerLogsOptions{ - ShowStdout: true, - ShowStderr: true, - Follow: options.Follow, - Since: options.Since, - Until: options.Until, - Tail: options.Tail, - Timestamps: options.Timestamps, - }) - if err != nil { - return err - } - defer r.Close() // nolint errcheck - - name := getContainerNameWithoutProject(c) - w := utils.GetWriter(func(line string) { - consumer.Log(name, service, line) - }) - if cnt.Config.Tty { - _, err = io.Copy(w, r) - } else { - _, err = stdcopy.StdCopy(w, w, r) - } - return err -} diff --git a/pkg/compose/ls.go b/pkg/compose/ls.go deleted file mode 100644 index a57a1091..00000000 --- a/pkg/compose/ls.go +++ /dev/null @@ -1,107 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package compose - -import ( - "context" - "fmt" - "sort" - - "github.com/docker/compose-cli/pkg/api" - - moby "github.com/docker/docker/api/types" - "github.com/docker/docker/api/types/filters" -) - -func (s *composeService) List(ctx context.Context, opts api.ListOptions) ([]api.Stack, error) { - list, err := s.apiClient.ContainerList(ctx, moby.ContainerListOptions{ - Filters: filters.NewArgs(hasProjectLabelFilter()), - All: opts.All, - }) - if err != nil { - return nil, err - } - - return containersToStacks(list) -} - -func containersToStacks(containers []moby.Container) ([]api.Stack, error) { - containersByLabel, keys, err := groupContainerByLabel(containers, api.ProjectLabel) - if err != nil { - return nil, err - } - var projects []api.Stack - for _, project := range keys { - projects = append(projects, api.Stack{ - ID: project, - Name: project, - Status: combinedStatus(containerToState(containersByLabel[project])), - }) - } - return projects, nil -} - -func containerToState(containers []moby.Container) []string { - statuses := []string{} - for _, c := range containers { - statuses = append(statuses, c.State) - } - return statuses -} - -func combinedStatus(statuses []string) string { - nbByStatus := map[string]int{} - keys := []string{} - for _, status := range statuses { - nb, ok := nbByStatus[status] - if !ok { - nb = 0 - keys = append(keys, status) - } - nbByStatus[status] = nb + 1 - } - sort.Strings(keys) - result := "" - for _, status := range keys { - nb := nbByStatus[status] - if result != "" { - result = result + ", " - } - result = result + fmt.Sprintf("%s(%d)", status, nb) - } - return result -} - -func groupContainerByLabel(containers []moby.Container, labelName string) (map[string][]moby.Container, []string, error) { - containersByLabel := map[string][]moby.Container{} - keys := []string{} - for _, c := range containers { - label, ok := c.Labels[labelName] - if !ok { - return nil, nil, fmt.Errorf("No label %q set on container %q of compose project", labelName, c.ID) - } - labelContainers, ok := containersByLabel[label] - if !ok { - labelContainers = []moby.Container{} - keys = append(keys, label) - } - labelContainers = append(labelContainers, c) - containersByLabel[label] = labelContainers - } - sort.Strings(keys) - return containersByLabel, keys, nil -} diff --git a/pkg/compose/ls_test.go b/pkg/compose/ls_test.go deleted file mode 100644 index c964fc74..00000000 --- a/pkg/compose/ls_test.go +++ /dev/null @@ -1,66 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package compose - -import ( - "testing" - - "github.com/docker/compose-cli/pkg/api" - - moby "github.com/docker/docker/api/types" - "gotest.tools/v3/assert" -) - -func TestContainersToStacks(t *testing.T) { - containers := []moby.Container{ - { - ID: "service1", - State: "running", - Labels: map[string]string{api.ProjectLabel: "project1"}, - }, - { - ID: "service2", - State: "running", - Labels: map[string]string{api.ProjectLabel: "project1"}, - }, - { - ID: "service3", - State: "running", - Labels: map[string]string{api.ProjectLabel: "project2"}, - }, - } - stacks, err := containersToStacks(containers) - assert.NilError(t, err) - assert.DeepEqual(t, stacks, []api.Stack{ - { - ID: "project1", - Name: "project1", - Status: "running(2)", - }, - { - ID: "project2", - Name: "project2", - Status: "running(1)", - }, - }) -} - -func TestStacksMixedStatus(t *testing.T) { - assert.Equal(t, combinedStatus([]string{"running"}), "running(1)") - assert.Equal(t, combinedStatus([]string{"running", "running", "running"}), "running(3)") - assert.Equal(t, combinedStatus([]string{"running", "exited", "running"}), "exited(1), running(2)") -} diff --git a/pkg/compose/metrics.go b/pkg/compose/metrics.go deleted file mode 100644 index 2cdc927e..00000000 --- a/pkg/compose/metrics.go +++ /dev/null @@ -1,79 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package compose - -// FailureCategory sruct regrouping metrics failure status and specific exit code -type FailureCategory struct { - MetricsStatus string - ExitCode int -} - -const ( - // APISource is sent for API metrics - APISource = "api" - // SuccessStatus command success - SuccessStatus = "success" - // FailureStatus command failure - FailureStatus = "failure" - // ComposeParseFailureStatus failure while parsing compose file - ComposeParseFailureStatus = "failure-compose-parse" - // FileNotFoundFailureStatus failure getting compose file - FileNotFoundFailureStatus = "failure-file-not-found" - // CommandSyntaxFailureStatus failure reading command - CommandSyntaxFailureStatus = "failure-cmd-syntax" - // BuildFailureStatus failure building imge - BuildFailureStatus = "failure-build" - // PullFailureStatus failure pulling imge - PullFailureStatus = "failure-pull" - // CanceledStatus command canceled - CanceledStatus = "canceled" -) - -var ( - // FileNotFoundFailure failure for compose file not found - FileNotFoundFailure = FailureCategory{MetricsStatus: FileNotFoundFailureStatus, ExitCode: 14} - // ComposeParseFailure failure for composefile parse error - ComposeParseFailure = FailureCategory{MetricsStatus: ComposeParseFailureStatus, ExitCode: 15} - // CommandSyntaxFailure failure for command line syntax - CommandSyntaxFailure = FailureCategory{MetricsStatus: CommandSyntaxFailureStatus, ExitCode: 16} - //BuildFailure failure while building images. - BuildFailure = FailureCategory{MetricsStatus: BuildFailureStatus, ExitCode: 17} - // PullFailure failure while pulling image - PullFailure = FailureCategory{MetricsStatus: PullFailureStatus, ExitCode: 18} -) - -//ByExitCode retrieve FailureCategory based on command exit code -func ByExitCode(exitCode int) FailureCategory { - switch exitCode { - case 0: - return FailureCategory{MetricsStatus: SuccessStatus, ExitCode: 0} - case 14: - return FileNotFoundFailure - case 15: - return ComposeParseFailure - case 16: - return CommandSyntaxFailure - case 17: - return BuildFailure - case 18: - return PullFailure - case 130: - return FailureCategory{MetricsStatus: CanceledStatus, ExitCode: exitCode} - default: - return FailureCategory{MetricsStatus: FailureStatus, ExitCode: exitCode} - } -} diff --git a/pkg/compose/pause.go b/pkg/compose/pause.go deleted file mode 100644 index cf306113..00000000 --- a/pkg/compose/pause.go +++ /dev/null @@ -1,83 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package compose - -import ( - "context" - - moby "github.com/docker/docker/api/types" - "golang.org/x/sync/errgroup" - - "github.com/docker/compose-cli/pkg/api" - "github.com/docker/compose-cli/pkg/progress" -) - -func (s *composeService) Pause(ctx context.Context, project string, options api.PauseOptions) error { - return progress.Run(ctx, func(ctx context.Context) error { - return s.pause(ctx, project, options) - }) -} - -func (s *composeService) pause(ctx context.Context, project string, options api.PauseOptions) error { - containers, err := s.getContainers(ctx, project, oneOffExclude, true, options.Services...) - if err != nil { - return err - } - - w := progress.ContextWriter(ctx) - eg, ctx := errgroup.WithContext(ctx) - containers.forEach(func(container moby.Container) { - eg.Go(func() error { - err := s.apiClient.ContainerPause(ctx, container.ID) - if err == nil { - eventName := getContainerProgressName(container) - w.Event(progress.NewEvent(eventName, progress.Done, "Paused")) - } - return err - }) - - }) - return eg.Wait() -} - -func (s *composeService) UnPause(ctx context.Context, project string, options api.PauseOptions) error { - return progress.Run(ctx, func(ctx context.Context) error { - return s.unPause(ctx, project, options) - }) -} - -func (s *composeService) unPause(ctx context.Context, project string, options api.PauseOptions) error { - containers, err := s.getContainers(ctx, project, oneOffExclude, true, options.Services...) - if err != nil { - return err - } - - w := progress.ContextWriter(ctx) - eg, ctx := errgroup.WithContext(ctx) - containers.forEach(func(container moby.Container) { - eg.Go(func() error { - err = s.apiClient.ContainerUnpause(ctx, container.ID) - if err == nil { - eventName := getContainerProgressName(container) - w.Event(progress.NewEvent(eventName, progress.Done, "Unpaused")) - } - return err - }) - - }) - return eg.Wait() -} diff --git a/pkg/compose/port.go b/pkg/compose/port.go deleted file mode 100644 index c9f4c069..00000000 --- a/pkg/compose/port.go +++ /dev/null @@ -1,50 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package compose - -import ( - "context" - "fmt" - - "github.com/docker/compose-cli/pkg/api" - - moby "github.com/docker/docker/api/types" - "github.com/docker/docker/api/types/filters" -) - -func (s *composeService) Port(ctx context.Context, project string, service string, port int, options api.PortOptions) (string, int, error) { - list, err := s.apiClient.ContainerList(ctx, moby.ContainerListOptions{ - Filters: filters.NewArgs( - projectFilter(project), - serviceFilter(service), - containerNumberFilter(options.Index), - ), - }) - if err != nil { - return "", 0, err - } - if len(list) == 0 { - return "", 0, fmt.Errorf("no container found for %s_%d", service, options.Index) - } - container := list[0] - for _, p := range container.Ports { - if p.PrivatePort == uint16(port) && p.Type == options.Protocol { - return p.IP, int(p.PublicPort), nil - } - } - return "", 0, err -} diff --git a/pkg/compose/printer.go b/pkg/compose/printer.go deleted file mode 100644 index 0ba41496..00000000 --- a/pkg/compose/printer.go +++ /dev/null @@ -1,111 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package compose - -import ( - "fmt" - - "github.com/docker/compose-cli/pkg/api" - - "github.com/sirupsen/logrus" -) - -// logPrinter watch application containers an collect their logs -type logPrinter interface { - HandleEvent(event api.ContainerEvent) - Run(cascadeStop bool, exitCodeFrom string, stopFn func() error) (int, error) - Cancel() -} - -// newLogPrinter builds a LogPrinter passing containers logs to LogConsumer -func newLogPrinter(consumer api.LogConsumer) logPrinter { - queue := make(chan api.ContainerEvent) - printer := printer{ - consumer: consumer, - queue: queue, - } - return &printer -} - -func (p *printer) Cancel() { - p.queue <- api.ContainerEvent{ - Type: api.UserCancel, - } -} - -type printer struct { - queue chan api.ContainerEvent - consumer api.LogConsumer -} - -func (p *printer) HandleEvent(event api.ContainerEvent) { - p.queue <- event -} - -func (p *printer) Run(cascadeStop bool, exitCodeFrom string, stopFn func() error) (int, error) { - var ( - aborting bool - exitCode int - ) - containers := map[string]struct{}{} - for { - event := <-p.queue - container := event.Container - switch event.Type { - case api.UserCancel: - aborting = true - case api.ContainerEventAttach: - if _, ok := containers[container]; ok { - continue - } - containers[container] = struct{}{} - p.consumer.Register(container) - case api.ContainerEventExit: - if !event.Restarting { - delete(containers, container) - } - if !aborting { - p.consumer.Status(container, fmt.Sprintf("exited with code %d", event.ExitCode)) - } - if cascadeStop { - if !aborting { - aborting = true - fmt.Println("Aborting on container exit...") - err := stopFn() - if err != nil { - return 0, err - } - } - if exitCodeFrom == "" { - exitCodeFrom = event.Service - } - if exitCodeFrom == event.Service { - logrus.Error(event.ExitCode) - exitCode = event.ExitCode - } - } - if len(containers) == 0 { - // Last container terminated, done - return exitCode, nil - } - case api.ContainerEventLog: - if !aborting { - p.consumer.Log(container, event.Service, event.Line) - } - } - } -} diff --git a/pkg/compose/ps.go b/pkg/compose/ps.go deleted file mode 100644 index d2fa718c..00000000 --- a/pkg/compose/ps.go +++ /dev/null @@ -1,96 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package compose - -import ( - "context" - "fmt" - "sort" - - "golang.org/x/sync/errgroup" - - "github.com/docker/compose-cli/pkg/api" -) - -func (s *composeService) Ps(ctx context.Context, projectName string, options api.PsOptions) ([]api.ContainerSummary, error) { - oneOff := oneOffExclude - if options.All { - oneOff = oneOffInclude - } - containers, err := s.getContainers(ctx, projectName, oneOff, true, options.Services...) - if err != nil { - return nil, err - } - - summary := make([]api.ContainerSummary, len(containers)) - eg, ctx := errgroup.WithContext(ctx) - for i, container := range containers { - i, container := i, container - eg.Go(func() error { - var publishers []api.PortPublisher - sort.Slice(container.Ports, func(i, j int) bool { - return container.Ports[i].PrivatePort < container.Ports[j].PrivatePort - }) - for _, p := range container.Ports { - var url string - if p.PublicPort != 0 { - url = fmt.Sprintf("%s:%d", p.IP, p.PublicPort) - } - publishers = append(publishers, api.PortPublisher{ - URL: url, - TargetPort: int(p.PrivatePort), - PublishedPort: int(p.PublicPort), - Protocol: p.Type, - }) - } - - inspect, err := s.apiClient.ContainerInspect(ctx, container.ID) - if err != nil { - return err - } - - var ( - health string - exitCode int - ) - if inspect.State != nil { - switch inspect.State.Status { - case "running": - if inspect.State.Health != nil { - health = inspect.State.Health.Status - } - case "exited", "dead": - exitCode = inspect.State.ExitCode - } - } - - summary[i] = api.ContainerSummary{ - ID: container.ID, - Name: getCanonicalContainerName(container), - Project: container.Labels[api.ProjectLabel], - Service: container.Labels[api.ServiceLabel], - Command: container.Command, - State: container.State, - Health: health, - ExitCode: exitCode, - Publishers: publishers, - } - return nil - }) - } - return summary, eg.Wait() -} diff --git a/pkg/compose/ps_test.go b/pkg/compose/ps_test.go deleted file mode 100644 index b66c5d03..00000000 --- a/pkg/compose/ps_test.go +++ /dev/null @@ -1,74 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package compose - -import ( - "context" - "strings" - "testing" - - "github.com/golang/mock/gomock" - "gotest.tools/v3/assert" - - moby "github.com/docker/docker/api/types" - "github.com/docker/docker/api/types/filters" - - compose "github.com/docker/compose-cli/pkg/api" - "github.com/docker/compose-cli/pkg/mocks" -) - -func TestPs(t *testing.T) { - mockCtrl := gomock.NewController(t) - defer mockCtrl.Finish() - api := mocks.NewMockAPIClient(mockCtrl) - tested.apiClient = api - - ctx := context.Background() - args := filters.NewArgs(projectFilter(strings.ToLower(testProject))) - args.Add("label", "com.docker.compose.oneoff=False") - listOpts := moby.ContainerListOptions{Filters: args, All: true} - c1, inspect1 := containerDetails("service1", "123", "running", "healthy", 0) - c2, inspect2 := containerDetails("service1", "456", "running", "", 0) - c2.Ports = []moby.Port{{PublicPort: 80, PrivatePort: 90, IP: "localhost"}} - c3, inspect3 := containerDetails("service2", "789", "exited", "", 130) - api.EXPECT().ContainerList(ctx, listOpts).Return([]moby.Container{c1, c2, c3}, nil) - api.EXPECT().ContainerInspect(anyCancellableContext(), "123").Return(inspect1, nil) - api.EXPECT().ContainerInspect(anyCancellableContext(), "456").Return(inspect2, nil) - api.EXPECT().ContainerInspect(anyCancellableContext(), "789").Return(inspect3, nil) - - containers, err := tested.Ps(ctx, strings.ToLower(testProject), compose.PsOptions{}) - - expected := []compose.ContainerSummary{ - {ID: "123", Name: "123", Project: strings.ToLower(testProject), Service: "service1", State: "running", Health: "healthy", Publishers: nil}, - {ID: "456", Name: "456", Project: strings.ToLower(testProject), Service: "service1", State: "running", Health: "", Publishers: []compose.PortPublisher{{URL: "localhost:80", TargetPort: 90, - PublishedPort: 80}}}, - {ID: "789", Name: "789", Project: strings.ToLower(testProject), Service: "service2", State: "exited", Health: "", ExitCode: 130, Publishers: nil}, - } - assert.NilError(t, err) - assert.DeepEqual(t, containers, expected) -} - -func containerDetails(service string, id string, status string, health string, exitCode int) (moby.Container, moby.ContainerJSON) { - container := moby.Container{ - ID: id, - Names: []string{"/" + id}, - Labels: containerLabels(service), - State: status, - } - inspect := moby.ContainerJSON{ContainerJSONBase: &moby.ContainerJSONBase{State: &moby.ContainerState{Status: status, Health: &moby.Health{Status: health}, ExitCode: exitCode}}} - return container, inspect -} diff --git a/pkg/compose/pull.go b/pkg/compose/pull.go deleted file mode 100644 index 0202eb5f..00000000 --- a/pkg/compose/pull.go +++ /dev/null @@ -1,244 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package compose - -import ( - "context" - "encoding/base64" - "encoding/json" - "errors" - "io" - "strings" - - "github.com/compose-spec/compose-go/types" - "github.com/distribution/distribution/v3/reference" - "github.com/docker/buildx/driver" - moby "github.com/docker/docker/api/types" - "github.com/docker/docker/pkg/jsonmessage" - "github.com/docker/docker/registry" - "golang.org/x/sync/errgroup" - - "github.com/docker/compose-cli/pkg/api" - "github.com/docker/compose-cli/pkg/progress" -) - -func (s *composeService) Pull(ctx context.Context, project *types.Project, opts api.PullOptions) error { - if opts.Quiet { - return s.pull(ctx, project, opts) - } - return progress.Run(ctx, func(ctx context.Context) error { - return s.pull(ctx, project, opts) - }) -} - -func (s *composeService) pull(ctx context.Context, project *types.Project, opts api.PullOptions) error { - info, err := s.apiClient.Info(ctx) - if err != nil { - return err - } - - if info.IndexServerAddress == "" { - info.IndexServerAddress = registry.IndexServer - } - - w := progress.ContextWriter(ctx) - eg, ctx := errgroup.WithContext(ctx) - - var mustBuild []string - for _, service := range project.Services { - service := service - if service.Image == "" { - w.Event(progress.Event{ - ID: service.Name, - Status: progress.Done, - Text: "Skipped", - }) - continue - } - eg.Go(func() error { - err := s.pullServiceImage(ctx, service, info, s.configFile, w, false) - if err != nil { - if !opts.IgnoreFailures { - if service.Build != nil { - mustBuild = append(mustBuild, service.Name) - } - return err - } - w.TailMsgf("Pulling %s: %s", service.Name, err.Error()) - } - return nil - }) - } - - err = eg.Wait() - - if !opts.IgnoreFailures && len(mustBuild) > 0 { - w.TailMsgf("WARNING: Some service image(s) must be built from source by running:\n docker compose build %s", strings.Join(mustBuild, " ")) - } - - return err -} - -func (s *composeService) pullServiceImage(ctx context.Context, service types.ServiceConfig, info moby.Info, configFile driver.Auth, w progress.Writer, quietPull bool) error { - w.Event(progress.Event{ - ID: service.Name, - Status: progress.Working, - Text: "Pulling", - }) - ref, err := reference.ParseNormalizedNamed(service.Image) - if err != nil { - return err - } - - repoInfo, err := registry.ParseRepositoryInfo(ref) - if err != nil { - return err - } - - key := repoInfo.Index.Name - if repoInfo.Index.Official { - key = info.IndexServerAddress - } - - authConfig, err := configFile.GetAuthConfig(key) - if err != nil { - return err - } - - buf, err := json.Marshal(authConfig) - if err != nil { - return err - } - - stream, err := s.apiClient.ImagePull(ctx, service.Image, moby.ImagePullOptions{ - RegistryAuth: base64.URLEncoding.EncodeToString(buf), - Platform: service.Platform, - }) - if err != nil { - w.Event(progress.Event{ - ID: service.Name, - Status: progress.Error, - Text: "Error", - }) - return WrapCategorisedComposeError(err, PullFailure) - } - - dec := json.NewDecoder(stream) - for { - var jm jsonmessage.JSONMessage - if err := dec.Decode(&jm); err != nil { - if err == io.EOF { - break - } - return WrapCategorisedComposeError(err, PullFailure) - } - if jm.Error != nil { - return WrapCategorisedComposeError(errors.New(jm.Error.Message), PullFailure) - } - if !quietPull { - toPullProgressEvent(service.Name, jm, w) - } - } - w.Event(progress.Event{ - ID: service.Name, - Status: progress.Done, - Text: "Pulled", - }) - return nil -} - -func (s *composeService) pullRequiredImages(ctx context.Context, project *types.Project, images map[string]string, quietPull bool) error { - info, err := s.apiClient.Info(ctx) - if err != nil { - return err - } - - if info.IndexServerAddress == "" { - info.IndexServerAddress = registry.IndexServer - } - - var needPull []types.ServiceConfig - for _, service := range project.Services { - if service.Image == "" { - continue - } - switch service.PullPolicy { - case "", types.PullPolicyMissing, types.PullPolicyIfNotPresent: - if _, ok := images[service.Image]; ok { - continue - } - case types.PullPolicyNever, types.PullPolicyBuild: - continue - case types.PullPolicyAlways: - // force pull - } - needPull = append(needPull, service) - } - if len(needPull) == 0 { - return nil - } - - return progress.Run(ctx, func(ctx context.Context) error { - w := progress.ContextWriter(ctx) - eg, ctx := errgroup.WithContext(ctx) - for _, service := range needPull { - service := service - eg.Go(func() error { - err := s.pullServiceImage(ctx, service, info, s.configFile, w, quietPull) - if err != nil && service.Build != nil { - // image can be built, so we can ignore pull failure - return nil - } - return err - }) - } - return eg.Wait() - }) -} - -func toPullProgressEvent(parent string, jm jsonmessage.JSONMessage, w progress.Writer) { - if jm.ID == "" || jm.Progress == nil { - return - } - - var ( - text string - status = progress.Working - ) - - text = jm.Progress.String() - - if jm.Status == "Pull complete" || - jm.Status == "Already exists" || - strings.Contains(jm.Status, "Image is up to date") || - strings.Contains(jm.Status, "Downloaded newer image") { - status = progress.Done - } - - if jm.Error != nil { - status = progress.Error - text = jm.Error.Message - } - - w.Event(progress.Event{ - ID: jm.ID, - ParentID: parent, - Text: jm.Status, - Status: status, - StatusText: text, - }) -} diff --git a/pkg/compose/push.go b/pkg/compose/push.go deleted file mode 100644 index 3ed58686..00000000 --- a/pkg/compose/push.go +++ /dev/null @@ -1,154 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package compose - -import ( - "context" - "encoding/base64" - "encoding/json" - "fmt" - "io" - - "github.com/compose-spec/compose-go/types" - "github.com/distribution/distribution/v3/reference" - "github.com/docker/buildx/driver" - moby "github.com/docker/docker/api/types" - "github.com/docker/docker/pkg/jsonmessage" - "github.com/docker/docker/registry" - "github.com/pkg/errors" - "golang.org/x/sync/errgroup" - - "github.com/docker/compose-cli/pkg/api" - "github.com/docker/compose-cli/pkg/progress" -) - -func (s *composeService) Push(ctx context.Context, project *types.Project, options api.PushOptions) error { - return progress.Run(ctx, func(ctx context.Context) error { - return s.push(ctx, project, options) - }) -} - -func (s *composeService) push(ctx context.Context, project *types.Project, options api.PushOptions) error { - eg, ctx := errgroup.WithContext(ctx) - - info, err := s.apiClient.Info(ctx) - if err != nil { - return err - } - if info.IndexServerAddress == "" { - info.IndexServerAddress = registry.IndexServer - } - - w := progress.ContextWriter(ctx) - for _, service := range project.Services { - if service.Build == nil || service.Image == "" { - w.Event(progress.Event{ - ID: service.Name, - Status: progress.Done, - Text: "Skipped", - }) - continue - } - service := service - eg.Go(func() error { - err := s.pushServiceImage(ctx, service, info, s.configFile, w) - if err != nil { - if !options.IgnoreFailures { - return err - } - w.TailMsgf("Pushing %s: %s", service.Name, err.Error()) - } - return nil - }) - } - return eg.Wait() -} - -func (s *composeService) pushServiceImage(ctx context.Context, service types.ServiceConfig, info moby.Info, configFile driver.Auth, w progress.Writer) error { - ref, err := reference.ParseNormalizedNamed(service.Image) - if err != nil { - return err - } - - repoInfo, err := registry.ParseRepositoryInfo(ref) - if err != nil { - return err - } - - key := repoInfo.Index.Name - if repoInfo.Index.Official { - key = info.IndexServerAddress - } - authConfig, err := configFile.GetAuthConfig(key) - if err != nil { - return err - } - - buf, err := json.Marshal(authConfig) - if err != nil { - return err - } - - stream, err := s.apiClient.ImagePush(ctx, service.Image, moby.ImagePushOptions{ - RegistryAuth: base64.URLEncoding.EncodeToString(buf), - }) - if err != nil { - return err - } - dec := json.NewDecoder(stream) - for { - var jm jsonmessage.JSONMessage - if err := dec.Decode(&jm); err != nil { - if err == io.EOF { - break - } - return err - } - if jm.Error != nil { - return errors.New(jm.Error.Message) - } - toPushProgressEvent(service.Name, jm, w) - } - return nil -} - -func toPushProgressEvent(prefix string, jm jsonmessage.JSONMessage, w progress.Writer) { - if jm.ID == "" { - // skipped - return - } - var ( - text string - status = progress.Working - ) - if jm.Status == "Pull complete" || jm.Status == "Already exists" { - status = progress.Done - } - if jm.Error != nil { - status = progress.Error - text = jm.Error.Message - } - if jm.Progress != nil { - text = jm.Progress.String() - } - w.Event(progress.Event{ - ID: fmt.Sprintf("Pushing %s: %s", prefix, jm.ID), - Text: jm.Status, - Status: status, - StatusText: text, - }) -} diff --git a/pkg/compose/remove.go b/pkg/compose/remove.go deleted file mode 100644 index 81d7351c..00000000 --- a/pkg/compose/remove.go +++ /dev/null @@ -1,93 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package compose - -import ( - "context" - "fmt" - "strings" - - "github.com/compose-spec/compose-go/types" - "github.com/docker/compose-cli/pkg/api" - moby "github.com/docker/docker/api/types" - "golang.org/x/sync/errgroup" - - "github.com/docker/compose-cli/pkg/progress" - "github.com/docker/compose-cli/pkg/prompt" -) - -func (s *composeService) Remove(ctx context.Context, project *types.Project, options api.RemoveOptions) error { - services := options.Services - if len(services) == 0 { - services = project.ServiceNames() - } - - containers, err := s.getContainers(ctx, project.Name, oneOffInclude, true, services...) - if err != nil { - return err - } - - stoppedContainers := containers.filter(func(c moby.Container) bool { - return c.State != ContainerRunning - }) - - var names []string - stoppedContainers.forEach(func(c moby.Container) { - names = append(names, getCanonicalContainerName(c)) - }) - - if len(names) == 0 { - fmt.Println("No stopped containers") - return nil - } - msg := fmt.Sprintf("Going to remove %s", strings.Join(names, ", ")) - if options.Force { - fmt.Println(msg) - } else { - confirm, err := prompt.User{}.Confirm(msg, false) - if err != nil { - return err - } - if !confirm { - return nil - } - } - return progress.Run(ctx, func(ctx context.Context) error { - return s.remove(ctx, stoppedContainers, options) - }) -} - -func (s *composeService) remove(ctx context.Context, containers Containers, options api.RemoveOptions) error { - w := progress.ContextWriter(ctx) - eg, ctx := errgroup.WithContext(ctx) - for _, container := range containers { - container := container - eg.Go(func() error { - eventName := getContainerProgressName(container) - w.Event(progress.RemovingEvent(eventName)) - err := s.apiClient.ContainerRemove(ctx, container.ID, moby.ContainerRemoveOptions{ - RemoveVolumes: options.Volumes, - Force: options.Force, - }) - if err == nil { - w.Event(progress.RemovedEvent(eventName)) - } - return err - }) - } - return eg.Wait() -} diff --git a/pkg/compose/resize.go b/pkg/compose/resize.go deleted file mode 100644 index 0c972381..00000000 --- a/pkg/compose/resize.go +++ /dev/null @@ -1,74 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package compose - -import ( - "context" - "os" - gosignal "os/signal" - "runtime" - "time" - - "github.com/buger/goterm" - moby "github.com/docker/docker/api/types" - "github.com/docker/docker/pkg/signal" -) - -func (s *composeService) monitorTTySize(ctx context.Context, container string, resize func(context.Context, string, moby.ResizeOptions) error) { - err := resize(ctx, container, moby.ResizeOptions{ // nolint:errcheck - Height: uint(goterm.Height()), - Width: uint(goterm.Width()), - }) - if err != nil { - return - } - - sigchan := make(chan os.Signal, 1) - gosignal.Notify(sigchan, signal.SIGWINCH) - - if runtime.GOOS == "windows" { - // Windows has no SIGWINCH support, so we have to poll tty size ¯\_(ツ)_/¯ - go func() { - prevH := goterm.Height() - prevW := goterm.Width() - for { - time.Sleep(time.Millisecond * 250) - h := goterm.Height() - w := goterm.Width() - if prevW != w || prevH != h { - sigchan <- signal.SIGWINCH - } - prevH = h - prevW = w - } - }() - } - - go func() { - for { - select { - case <-sigchan: - resize(ctx, container, moby.ResizeOptions{ // nolint:errcheck - Height: uint(goterm.Height()), - Width: uint(goterm.Width()), - }) - case <-ctx.Done(): - return - } - } - }() -} diff --git a/pkg/compose/restart.go b/pkg/compose/restart.go deleted file mode 100644 index e4f67a06..00000000 --- a/pkg/compose/restart.go +++ /dev/null @@ -1,70 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package compose - -import ( - "context" - - "github.com/compose-spec/compose-go/types" - "github.com/docker/compose-cli/pkg/api" - "golang.org/x/sync/errgroup" - - "github.com/docker/compose-cli/pkg/progress" - "github.com/docker/compose-cli/pkg/utils" -) - -func (s *composeService) Restart(ctx context.Context, project *types.Project, options api.RestartOptions) error { - return progress.Run(ctx, func(ctx context.Context) error { - return s.restart(ctx, project, options) - }) -} - -func (s *composeService) restart(ctx context.Context, project *types.Project, options api.RestartOptions) error { - observedState, err := s.getContainers(ctx, project.Name, oneOffInclude, true) - if err != nil { - return err - } - - if len(options.Services) == 0 { - options.Services = project.ServiceNames() - } - - w := progress.ContextWriter(ctx) - err = InDependencyOrder(ctx, project, func(c context.Context, service string) error { - if !utils.StringContains(options.Services, service) { - return nil - } - eg, ctx := errgroup.WithContext(ctx) - for _, container := range observedState.filter(isService(service)) { - container := container - eg.Go(func() error { - eventName := getContainerProgressName(container) - w.Event(progress.RestartingEvent(eventName)) - err := s.apiClient.ContainerRestart(ctx, container.ID, options.Timeout) - if err == nil { - w.Event(progress.StartedEvent(eventName)) - } - return err - }) - } - return eg.Wait() - }) - if err != nil { - return err - } - return nil -} diff --git a/pkg/compose/run.go b/pkg/compose/run.go deleted file mode 100644 index 929c820a..00000000 --- a/pkg/compose/run.go +++ /dev/null @@ -1,206 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package compose - -import ( - "context" - "fmt" - "io" - - "github.com/docker/compose-cli/pkg/api" - - "github.com/compose-spec/compose-go/types" - "github.com/docker/cli/cli/streams" - moby "github.com/docker/docker/api/types" - "github.com/docker/docker/pkg/ioutils" - "github.com/docker/docker/pkg/stdcopy" - "github.com/docker/docker/pkg/stringid" - "github.com/moby/term" -) - -func (s *composeService) RunOneOffContainer(ctx context.Context, project *types.Project, opts api.RunOptions) (int, error) { - containerID, err := s.prepareRun(ctx, project, opts) - if err != nil { - return 0, err - } - - if opts.Detach { - err := s.apiClient.ContainerStart(ctx, containerID, moby.ContainerStartOptions{}) - if err != nil { - return 0, err - } - fmt.Fprintln(opts.Stdout, containerID) - return 0, nil - } - - return s.runInteractive(ctx, containerID, opts) -} - -func (s *composeService) runInteractive(ctx context.Context, containerID string, opts api.RunOptions) (int, error) { - r, err := s.getEscapeKeyProxy(opts.Stdin) - if err != nil { - return 0, err - } - - stdin, stdout, err := s.getContainerStreams(ctx, containerID) - if err != nil { - return 0, err - } - - in := streams.NewIn(opts.Stdin) - if in.IsTerminal() { - state, err := term.SetRawTerminal(in.FD()) - if err != nil { - return 0, err - } - defer term.RestoreTerminal(in.FD(), state) //nolint:errcheck - } - - outputDone := make(chan error) - inputDone := make(chan error) - - go func() { - if opts.Tty { - _, err := io.Copy(opts.Stdout, stdout) //nolint:errcheck - outputDone <- err - } else { - _, err := stdcopy.StdCopy(opts.Stdout, opts.Stderr, stdout) //nolint:errcheck - outputDone <- err - } - stdout.Close() //nolint:errcheck - }() - - go func() { - _, err := io.Copy(stdin, r) - inputDone <- err - stdin.Close() //nolint:errcheck - }() - - err = s.apiClient.ContainerStart(ctx, containerID, moby.ContainerStartOptions{}) - if err != nil { - return 0, err - } - - s.monitorTTySize(ctx, containerID, s.apiClient.ContainerResize) - - for { - select { - case err := <-outputDone: - if err != nil { - return 0, err - } - inspect, err := s.apiClient.ContainerInspect(ctx, containerID) - if err != nil { - return 0, err - } - exitCode := 0 - if inspect.State != nil { - exitCode = inspect.State.ExitCode - } - return exitCode, nil - case err := <-inputDone: - if _, ok := err.(term.EscapeError); ok { - return 0, nil - } - if err != nil { - return 0, err - } - // Wait for output to complete streaming - case <-ctx.Done(): - return 0, ctx.Err() - } - } -} - -func (s *composeService) prepareRun(ctx context.Context, project *types.Project, opts api.RunOptions) (string, error) { - service, err := project.GetService(opts.Service) - if err != nil { - return "", err - } - - applyRunOptions(project, &service, opts) - - slug := stringid.GenerateRandomID() - if service.ContainerName == "" { - service.ContainerName = fmt.Sprintf("%s_%s_run_%s", project.Name, service.Name, stringid.TruncateID(slug)) - } - service.Scale = 1 - service.StdinOpen = true - service.Restart = "" - if service.Deploy != nil { - service.Deploy.RestartPolicy = nil - } - service.Labels = service.Labels.Add(api.SlugLabel, slug) - service.Labels = service.Labels.Add(api.OneoffLabel, "True") - if err := prepareVolumes(project); err != nil { // all dependencies already checked, but might miss service img - return "", err - } - - if err := s.ensureImagesExists(ctx, project, false); err != nil { // all dependencies already checked, but might miss service img - return "", err - } - if err := s.waitDependencies(ctx, project, service); err != nil { - return "", err - } - created, err := s.createContainer(ctx, project, service, service.ContainerName, 1, opts.AutoRemove, opts.UseNetworkAliases) - if err != nil { - return "", err - } - containerID := created.ID - return containerID, nil -} - -func (s *composeService) getEscapeKeyProxy(r io.ReadCloser) (io.ReadCloser, error) { - var escapeKeys = []byte{16, 17} - if s.configFile.DetachKeys != "" { - customEscapeKeys, err := term.ToBytes(s.configFile.DetachKeys) - if err != nil { - return nil, err - } - escapeKeys = customEscapeKeys - } - return ioutils.NewReadCloserWrapper(term.NewEscapeProxy(r, escapeKeys), r.Close), nil -} - -func applyRunOptions(project *types.Project, service *types.ServiceConfig, opts api.RunOptions) { - service.Tty = opts.Tty - service.ContainerName = opts.Name - - if len(opts.Command) > 0 { - service.Command = opts.Command - } - if len(opts.User) > 0 { - service.User = opts.User - } - if len(opts.WorkingDir) > 0 { - service.WorkingDir = opts.WorkingDir - } - if len(opts.Entrypoint) > 0 { - service.Entrypoint = opts.Entrypoint - } - if len(opts.Environment) > 0 { - env := types.NewMappingWithEquals(opts.Environment) - projectEnv := env.Resolve(func(s string) (string, bool) { - v, ok := project.Environment[s] - return v, ok - }).RemoveEmpty() - service.Environment.OverrideBy(projectEnv) - } - for k, v := range opts.Labels { - service.Labels.Add(k, v) - } -} diff --git a/pkg/compose/start.go b/pkg/compose/start.go deleted file mode 100644 index 9359ef64..00000000 --- a/pkg/compose/start.go +++ /dev/null @@ -1,143 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package compose - -import ( - "context" - - "github.com/compose-spec/compose-go/types" - moby "github.com/docker/docker/api/types" - "github.com/pkg/errors" - "golang.org/x/sync/errgroup" - - "github.com/docker/compose-cli/pkg/api" - "github.com/docker/compose-cli/pkg/progress" -) - -func (s *composeService) Start(ctx context.Context, project *types.Project, options api.StartOptions) error { - return progress.Run(ctx, func(ctx context.Context) error { - return s.start(ctx, project, options, nil) - }) -} - -func (s *composeService) start(ctx context.Context, project *types.Project, options api.StartOptions, listener api.ContainerEventListener) error { - if len(options.AttachTo) == 0 { - options.AttachTo = project.ServiceNames() - } - - eg, ctx := errgroup.WithContext(ctx) - if listener != nil { - attached, err := s.attach(ctx, project, listener, options.AttachTo) - if err != nil { - return err - } - - eg.Go(func() error { - return s.watchContainers(project.Name, options.AttachTo, listener, attached, func(container moby.Container) error { - return s.attachContainer(ctx, container, listener, project) - }) - }) - } - - err := InDependencyOrder(ctx, project, func(c context.Context, name string) error { - service, err := project.GetService(name) - if err != nil { - return err - } - return s.startService(ctx, project, service) - }) - if err != nil { - return err - } - return eg.Wait() -} - -type containerWatchFn func(container moby.Container) error - -// watchContainers uses engine events to capture container start/die and notify ContainerEventListener -func (s *composeService) watchContainers(projectName string, services []string, listener api.ContainerEventListener, containers Containers, onStart containerWatchFn) error { - watched := map[string]int{} - for _, c := range containers { - watched[c.ID] = 0 - } - - ctx, stop := context.WithCancel(context.Background()) - err := s.Events(ctx, projectName, api.EventsOptions{ - Services: services, - Consumer: func(event api.Event) error { - inspected, err := s.apiClient.ContainerInspect(ctx, event.Container) - if err != nil { - return err - } - container := moby.Container{ - ID: inspected.ID, - Names: []string{inspected.Name}, - Labels: inspected.Config.Labels, - } - name := getContainerNameWithoutProject(container) - - if event.Status == "die" { - restarted := watched[container.ID] - watched[container.ID] = restarted + 1 - // Container terminated. - willRestart := inspected.HostConfig.RestartPolicy.MaximumRetryCount > restarted - - listener(api.ContainerEvent{ - Type: api.ContainerEventExit, - Container: name, - Service: container.Labels[api.ServiceLabel], - ExitCode: inspected.State.ExitCode, - Restarting: willRestart, - }) - - if !willRestart { - // we're done with this one - delete(watched, container.ID) - } - - if len(watched) == 0 { - // all project containers stopped, we're done - stop() - } - return nil - } - - if event.Status == "start" { - count, ok := watched[container.ID] - mustAttach := ok && count > 0 // Container restarted, need to re-attach - if !ok { - // A new container has just been added to service by scale - watched[container.ID] = 0 - mustAttach = true - } - if mustAttach { - // Container restarted, need to re-attach - err := onStart(container) - if err != nil { - return err - } - } - } - - return nil - }, - }) - if errors.Is(ctx.Err(), context.Canceled) { - return nil - } - return err -} diff --git a/pkg/compose/stop.go b/pkg/compose/stop.go deleted file mode 100644 index f292305a..00000000 --- a/pkg/compose/stop.go +++ /dev/null @@ -1,50 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package compose - -import ( - "context" - - "github.com/docker/compose-cli/pkg/api" - "github.com/docker/compose-cli/pkg/progress" - - "github.com/compose-spec/compose-go/types" -) - -func (s *composeService) Stop(ctx context.Context, project *types.Project, options api.StopOptions) error { - return progress.Run(ctx, func(ctx context.Context) error { - return s.stop(ctx, project, options) - }) -} - -func (s *composeService) stop(ctx context.Context, project *types.Project, options api.StopOptions) error { - w := progress.ContextWriter(ctx) - - services := options.Services - if len(services) == 0 { - services = project.ServiceNames() - } - var containers Containers - containers, err := s.getContainers(ctx, project.Name, oneOffInclude, true, services...) - if err != nil { - return err - } - - return InReverseDependencyOrder(ctx, project, func(c context.Context, service string) error { - return s.stopContainers(ctx, w, containers.filter(isService(service)), options.Timeout) - }) -} diff --git a/pkg/compose/stop_test.go b/pkg/compose/stop_test.go deleted file mode 100644 index d61b1bdb..00000000 --- a/pkg/compose/stop_test.go +++ /dev/null @@ -1,63 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package compose - -import ( - "context" - "strings" - "testing" - "time" - - compose "github.com/docker/compose-cli/pkg/api" - "github.com/docker/compose-cli/pkg/mocks" - - "github.com/compose-spec/compose-go/types" - moby "github.com/docker/docker/api/types" - "github.com/golang/mock/gomock" - "gotest.tools/v3/assert" -) - -func TestStopTimeout(t *testing.T) { - mockCtrl := gomock.NewController(t) - defer mockCtrl.Finish() - api := mocks.NewMockAPIClient(mockCtrl) - tested.apiClient = api - - ctx := context.Background() - api.EXPECT().ContainerList(gomock.Any(), projectFilterListOpt()).Return( - []moby.Container{ - testContainer("service1", "123"), - testContainer("service1", "456"), - testContainer("service2", "789"), - }, nil) - - timeout := time.Duration(2) * time.Second - api.EXPECT().ContainerStop(gomock.Any(), "123", &timeout).Return(nil) - api.EXPECT().ContainerStop(gomock.Any(), "456", &timeout).Return(nil) - api.EXPECT().ContainerStop(gomock.Any(), "789", &timeout).Return(nil) - - err := tested.Stop(ctx, &types.Project{ - Name: strings.ToLower(testProject), - Services: []types.ServiceConfig{ - {Name: "service1"}, - {Name: "service2"}, - }, - }, compose.StopOptions{ - Timeout: &timeout, - }) - assert.NilError(t, err) -} diff --git a/pkg/compose/testdata/compose.yaml b/pkg/compose/testdata/compose.yaml deleted file mode 100644 index 4e3e6cb9..00000000 --- a/pkg/compose/testdata/compose.yaml +++ /dev/null @@ -1,5 +0,0 @@ -services: - service1: - image: nginx - service2: - image: mysql diff --git a/pkg/compose/top.go b/pkg/compose/top.go deleted file mode 100644 index 4a15b98a..00000000 --- a/pkg/compose/top.go +++ /dev/null @@ -1,54 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package compose - -import ( - "context" - - "github.com/docker/compose-cli/pkg/api" - "golang.org/x/sync/errgroup" -) - -func (s *composeService) Top(ctx context.Context, projectName string, services []string) ([]api.ContainerProcSummary, error) { - var containers Containers - containers, err := s.getContainers(ctx, projectName, oneOffInclude, false) - if err != nil { - return nil, err - } - if len(services) > 0 { - containers = containers.filter(isService(services...)) - } - summary := make([]api.ContainerProcSummary, len(containers)) - eg, ctx := errgroup.WithContext(ctx) - for i, container := range containers { - i, container := i, container - eg.Go(func() error { - topContent, err := s.apiClient.ContainerTop(ctx, container.ID, []string{}) - if err != nil { - return err - } - summary[i] = api.ContainerProcSummary{ - ID: container.ID, - Name: getCanonicalContainerName(container), - Processes: topContent.Processes, - Titles: topContent.Titles, - } - return nil - }) - } - return summary, eg.Wait() -} diff --git a/pkg/compose/up.go b/pkg/compose/up.go deleted file mode 100644 index acf4fdf2..00000000 --- a/pkg/compose/up.go +++ /dev/null @@ -1,98 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package compose - -import ( - "context" - "fmt" - "os" - "os/signal" - "syscall" - - "github.com/docker/compose-cli/pkg/api" - "github.com/docker/compose-cli/pkg/progress" - - "github.com/compose-spec/compose-go/types" - "github.com/docker/cli/cli" - "golang.org/x/sync/errgroup" -) - -func (s *composeService) Up(ctx context.Context, project *types.Project, options api.UpOptions) error { - err := progress.Run(ctx, func(ctx context.Context) error { - err := s.create(ctx, project, options.Create) - if err != nil { - return err - } - if options.Start.Attach == nil { - return s.start(ctx, project, options.Start, nil) - } - return nil - }) - if err != nil { - return err - } - - if options.Start.Attach == nil { - return err - } - - printer := newLogPrinter(options.Start.Attach) - - signalChan := make(chan os.Signal, 1) - signal.Notify(signalChan, syscall.SIGINT, syscall.SIGTERM) - - stopFunc := func() error { - ctx := context.Background() - return progress.Run(ctx, func(ctx context.Context) error { - go func() { - <-signalChan - s.Kill(ctx, project, api.KillOptions{}) // nolint:errcheck - }() - - return s.Stop(ctx, project, api.StopOptions{}) - }) - } - go func() { - <-signalChan - printer.Cancel() - fmt.Println("Gracefully stopping... (press Ctrl+C again to force)") - stopFunc() // nolint:errcheck - }() - - var exitCode int - eg, ctx := errgroup.WithContext(ctx) - eg.Go(func() error { - code, err := printer.Run(options.Start.CascadeStop, options.Start.ExitCodeFrom, stopFunc) - exitCode = code - return err - }) - - err = s.start(ctx, project, options.Start, printer.HandleEvent) - if err != nil { - return err - } - - err = eg.Wait() - if exitCode != 0 { - errMsg := "" - if err != nil { - errMsg = err.Error() - } - return cli.StatusError{StatusCode: exitCode, Status: errMsg} - } - return err -} diff --git a/pkg/e2e/cancel_test.go b/pkg/e2e/cancel_test.go deleted file mode 100644 index dc698dae..00000000 --- a/pkg/e2e/cancel_test.go +++ /dev/null @@ -1,74 +0,0 @@ -// +build !windows - -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package e2e - -import ( - "bytes" - "fmt" - "os/exec" - "strings" - "syscall" - "testing" - "time" - - "gotest.tools/v3/assert" - "gotest.tools/v3/icmd" -) - -func TestComposeCancel(t *testing.T) { - c := NewParallelE2eCLI(t, binDir) - - t.Run("metrics on cancel Compose build", func(t *testing.T) { - c.RunDockerCmd("compose", "ls") - buildProjectPath := "fixtures/build-infinite/compose.yaml" - - // require a separate groupID from the process running tests, in order to simulate ctrl+C from a terminal. - // sending kill signal - cmd, stdout, stderr, err := StartWithNewGroupID(c.NewDockerCmd("compose", "-f", buildProjectPath, "build", "--progress", "plain")) - assert.NilError(t, err) - - c.WaitForCondition(func() (bool, string) { - out := stdout.String() - errors := stderr.String() - return strings.Contains(out, "RUN sleep infinity"), fmt.Sprintf("'RUN sleep infinity' not found in : \n%s\nStderr: \n%s\n", out, errors) - }, 30*time.Second, 1*time.Second) - - err = syscall.Kill(-cmd.Process.Pid, syscall.SIGINT) // simulate Ctrl-C : send signal to processGroup, children will have same groupId by default - - assert.NilError(t, err) - c.WaitForCondition(func() (bool, string) { - out := stdout.String() - errors := stderr.String() - return strings.Contains(out, "CANCELED"), fmt.Sprintf("'CANCELED' not found in : \n%s\nStderr: \n%s\n", out, errors) - }, 10*time.Second, 1*time.Second) - }) -} - -func StartWithNewGroupID(command icmd.Cmd) (*exec.Cmd, *bytes.Buffer, *bytes.Buffer, error) { - cmd := exec.Command(command.Command[0], command.Command[1:]...) - cmd.Env = command.Env - cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true} - - var stdout bytes.Buffer - var stderr bytes.Buffer - cmd.Stdout = &stdout - cmd.Stderr = &stderr - err := cmd.Start() - return cmd, &stdout, &stderr, err -} diff --git a/pkg/e2e/cascade_stop_test.go b/pkg/e2e/cascade_stop_test.go deleted file mode 100644 index 7b0cb80a..00000000 --- a/pkg/e2e/cascade_stop_test.go +++ /dev/null @@ -1,50 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package e2e - -import ( - "testing" - - "gotest.tools/v3/icmd" -) - -func TestCascadeStop(t *testing.T) { - c := NewParallelE2eCLI(t, binDir) - - const projectName = "e2e-cascade-stop" - - t.Run("abort-on-container-exit", func(t *testing.T) { - res := c.RunDockerOrExitError("compose", "-f", "./fixtures/cascade-stop-test/compose.yaml", "--project-name", projectName, "up", "--abort-on-container-exit") - res.Assert(t, icmd.Expected{ExitCode: 1, Out: `should_fail_1 exited with code 1`}) - res.Assert(t, icmd.Expected{ExitCode: 1, Out: `Aborting on container exit...`}) - }) - - t.Run("exit-code-from", func(t *testing.T) { - res := c.RunDockerOrExitError("compose", "-f", "./fixtures/cascade-stop-test/compose.yaml", "--project-name", projectName, "up", "--exit-code-from=sleep") - res.Assert(t, icmd.Expected{ExitCode: 137, Out: `should_fail_1 exited with code 1`}) - res.Assert(t, icmd.Expected{ExitCode: 137, Out: `Aborting on container exit...`}) - }) - - t.Run("exit-code-from unknown", func(t *testing.T) { - res := c.RunDockerOrExitError("compose", "-f", "./fixtures/cascade-stop-test/compose.yaml", "--project-name", projectName, "up", "--exit-code-from=unknown") - res.Assert(t, icmd.Expected{ExitCode: 1, Err: `no such service: unknown`}) - }) - - t.Run("down", func(t *testing.T) { - _ = c.RunDockerCmd("compose", "--project-name", projectName, "down") - }) -} diff --git a/pkg/e2e/compose_build_test.go b/pkg/e2e/compose_build_test.go deleted file mode 100644 index 94d76c02..00000000 --- a/pkg/e2e/compose_build_test.go +++ /dev/null @@ -1,147 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package e2e - -import ( - "io/ioutil" - "net/http" - "os" - "path/filepath" - "strings" - "testing" - "time" - - "gotest.tools/v3/assert" - "gotest.tools/v3/icmd" -) - -func TestLocalComposeBuild(t *testing.T) { - c := NewParallelE2eCLI(t, binDir) - - t.Run("build named and unnamed images", func(t *testing.T) { - // ensure local test run does not reuse previously build image - c.RunDockerOrExitError("rmi", "build-test_nginx") - c.RunDockerOrExitError("rmi", "custom-nginx") - - res := c.RunDockerCmd("compose", "--project-directory", "fixtures/build-test", "build") - - res.Assert(t, icmd.Expected{Out: "COPY static /usr/share/nginx/html"}) - c.RunDockerCmd("image", "inspect", "build-test_nginx") - c.RunDockerCmd("image", "inspect", "custom-nginx") - }) - - t.Run("build with build-arg", func(t *testing.T) { - // ensure local test run does not reuse previously build image - c.RunDockerOrExitError("rmi", "build-test_nginx") - c.RunDockerOrExitError("rmi", "custom-nginx") - - c.RunDockerCmd("compose", "--project-directory", "fixtures/build-test", "build", "--build-arg", "FOO=BAR") - - res := c.RunDockerCmd("image", "inspect", "build-test_nginx") - res.Assert(t, icmd.Expected{Out: `"FOO": "BAR"`}) - }) - - t.Run("build with build-arg set by env", func(t *testing.T) { - // ensure local test run does not reuse previously build image - c.RunDockerOrExitError("rmi", "build-test_nginx") - c.RunDockerOrExitError("rmi", "custom-nginx") - - icmd.RunCmd(c.NewDockerCmd("compose", "--project-directory", "fixtures/build-test", "build", "--build-arg", "FOO"), - func(cmd *icmd.Cmd) { - cmd.Env = append(cmd.Env, "FOO=BAR") - }) - - res := c.RunDockerCmd("image", "inspect", "build-test_nginx") - res.Assert(t, icmd.Expected{Out: `"FOO": "BAR"`}) - }) - - t.Run("build as part of up", func(t *testing.T) { - c.RunDockerOrExitError("rmi", "build-test_nginx") - c.RunDockerOrExitError("rmi", "custom-nginx") - - res := c.RunDockerCmd("compose", "--project-directory", "fixtures/build-test", "up", "-d") - t.Cleanup(func() { - c.RunDockerCmd("compose", "--project-directory", "fixtures/build-test", "down") - }) - - res.Assert(t, icmd.Expected{Out: "COPY static /usr/share/nginx/html"}) - res.Assert(t, icmd.Expected{Out: "COPY static2 /usr/share/nginx/html"}) - - output := HTTPGetWithRetry(t, "http://localhost:8070", http.StatusOK, 2*time.Second, 20*time.Second) - assert.Assert(t, strings.Contains(output, "Hello from Nginx container")) - - c.RunDockerCmd("image", "inspect", "build-test_nginx") - c.RunDockerCmd("image", "inspect", "custom-nginx") - }) - - t.Run("no rebuild when up again", func(t *testing.T) { - res := c.RunDockerCmd("compose", "--project-directory", "fixtures/build-test", "up", "-d") - - assert.Assert(t, !strings.Contains(res.Stdout(), "COPY static"), res.Stdout()) - }) - - t.Run("rebuild when up --build", func(t *testing.T) { - res := c.RunDockerCmd("compose", "--workdir", "fixtures/build-test", "up", "-d", "--build") - - res.Assert(t, icmd.Expected{Out: "COPY static /usr/share/nginx/html"}) - res.Assert(t, icmd.Expected{Out: "COPY static2 /usr/share/nginx/html"}) - }) - - t.Run("cleanup build project", func(t *testing.T) { - c.RunDockerCmd("compose", "--project-directory", "fixtures/build-test", "down") - c.RunDockerCmd("rmi", "build-test_nginx") - c.RunDockerCmd("rmi", "custom-nginx") - }) -} - -func TestLocalComposeBuildStaticDockerfilePath(t *testing.T) { - c := NewParallelE2eCLI(t, binDir) - - t.Run("build ddev-style compose files", func(t *testing.T) { - dir, err := ioutil.TempDir("", "ddev") - assert.NilError(t, err) - defer os.RemoveAll(dir) //nolint:errcheck - - assert.NilError(t, ioutil.WriteFile(filepath.Join(dir, "compose.yaml"), []byte(`services: - service1: - build: - context: `+dir+`/service1 - dockerfile: Dockerfile - service2: - build: - context: `+dir+`/service2 - dockerfile: `+dir+`/service2/Dockerfile - `), 0644)) - - assert.NilError(t, os.Mkdir(filepath.Join(dir, "service1"), 0700)) - assert.NilError(t, ioutil.WriteFile(filepath.Join(dir, "service1", "Dockerfile"), []byte(`FROM alpine - RUN echo "hello" - `), 0644)) - - assert.NilError(t, os.Mkdir(filepath.Join(dir, "service2"), 0700)) - assert.NilError(t, ioutil.WriteFile(filepath.Join(dir, "service2", "Dockerfile"), []byte(`FROM alpine - RUN echo "world" - `), 0644)) - - res := c.RunDockerCmd("compose", "-f", filepath.Join(dir, "compose.yaml"), "build") - - res.Assert(t, icmd.Expected{Out: `RUN echo "hello"`}) - res.Assert(t, icmd.Expected{Out: `RUN echo "world"`}) - - c.RunDockerCmd("compose", "-f", filepath.Join(dir, "compose.yaml"), "down", "--rmi", "all") - }) -} diff --git a/pkg/e2e/compose_exec_test.go b/pkg/e2e/compose_exec_test.go deleted file mode 100644 index 36e6b970..00000000 --- a/pkg/e2e/compose_exec_test.go +++ /dev/null @@ -1,57 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package e2e - -import ( - "strings" - "testing" - - "gotest.tools/v3/assert" - "gotest.tools/v3/icmd" -) - -func TestLocalComposeExec(t *testing.T) { - c := NewParallelE2eCLI(t, binDir) - - const projectName = "compose-e2e-exec" - - c.RunDockerCmd("compose", "--project-directory", "fixtures/simple-composefile", "--project-name", projectName, "up", "-d") - - t.Run("exec true", func(t *testing.T) { - res := c.RunDockerOrExitError("exec", "compose-e2e-exec_simple_1", "/bin/true") - res.Assert(t, icmd.Expected{ExitCode: 0}) - }) - - t.Run("exec false", func(t *testing.T) { - res := c.RunDockerOrExitError("exec", "compose-e2e-exec_simple_1", "/bin/false") - res.Assert(t, icmd.Expected{ExitCode: 1}) - }) - - t.Run("exec with env set", func(t *testing.T) { - res := icmd.RunCmd(c.NewDockerCmd("exec", "-e", "FOO", "compose-e2e-exec_simple_1", "/usr/bin/env"), - func(cmd *icmd.Cmd) { - cmd.Env = append(cmd.Env, "FOO=BAR") - }) - res.Assert(t, icmd.Expected{Out: "FOO=BAR"}) - }) - - t.Run("exec without env set", func(t *testing.T) { - res := c.RunDockerOrExitError("exec", "-e", "FOO", "compose-e2e-exec_simple_1", "/usr/bin/env") - res.Assert(t, icmd.Expected{ExitCode: 0}) - assert.Check(t, !strings.Contains(res.Stdout(), "FOO=")) - }) -} diff --git a/pkg/e2e/compose_run_test.go b/pkg/e2e/compose_run_test.go deleted file mode 100644 index 44707985..00000000 --- a/pkg/e2e/compose_run_test.go +++ /dev/null @@ -1,106 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package e2e - -import ( - "os" - "strings" - "testing" - - "gotest.tools/v3/assert" - "gotest.tools/v3/icmd" -) - -func TestLocalComposeRun(t *testing.T) { - c := NewParallelE2eCLI(t, binDir) - - t.Run("compose run", func(t *testing.T) { - res := c.RunDockerCmd("compose", "-f", "./fixtures/run-test/compose.yaml", "run", "back") - lines := Lines(res.Stdout()) - assert.Equal(t, lines[len(lines)-1], "Hello there!!", res.Stdout()) - assert.Assert(t, !strings.Contains(res.Combined(), "orphan")) - res = c.RunDockerCmd("compose", "-f", "./fixtures/run-test/compose.yaml", "run", "back", "echo", "Hello one more time") - lines = Lines(res.Stdout()) - assert.Equal(t, lines[len(lines)-1], "Hello one more time", res.Stdout()) - assert.Assert(t, !strings.Contains(res.Combined(), "orphan")) - }) - - t.Run("check run container exited", func(t *testing.T) { - res := c.RunDockerCmd("ps", "--all") - lines := Lines(res.Stdout()) - var runContainerID string - var truncatedSlug string - for _, line := range lines { - fields := strings.Fields(line) - containerID := fields[len(fields)-1] - assert.Assert(t, !strings.HasPrefix(containerID, "run-test_front")) - if strings.HasPrefix(containerID, "run-test_back") { - // only the one-off container for back service - assert.Assert(t, strings.HasPrefix(containerID, "run-test_back_run_"), containerID) - truncatedSlug = strings.Replace(containerID, "run-test_back_run_", "", 1) - runContainerID = containerID - } - if strings.HasPrefix(containerID, "run-test_db_1") { - assert.Assert(t, strings.Contains(line, "Up"), line) - } - } - assert.Assert(t, runContainerID != "") - res = c.RunDockerCmd("inspect", runContainerID) - res.Assert(t, icmd.Expected{Out: ` "Status": "exited"`}) - res.Assert(t, icmd.Expected{Out: `"com.docker.compose.container-number": "1"`}) - res.Assert(t, icmd.Expected{Out: `"com.docker.compose.project": "run-test"`}) - res.Assert(t, icmd.Expected{Out: `"com.docker.compose.oneoff": "True",`}) - res.Assert(t, icmd.Expected{Out: `"com.docker.compose.slug": "` + truncatedSlug}) - }) - - t.Run("compose run --rm", func(t *testing.T) { - res := c.RunDockerCmd("compose", "-f", "./fixtures/run-test/compose.yaml", "run", "--rm", "back", "/bin/sh", "-c", "echo Hello again") - lines := Lines(res.Stdout()) - assert.Equal(t, lines[len(lines)-1], "Hello again", res.Stdout()) - - res = c.RunDockerCmd("ps", "--all") - assert.Assert(t, strings.Contains(res.Stdout(), "run-test_back"), res.Stdout()) - }) - - t.Run("down", func(t *testing.T) { - c.RunDockerCmd("compose", "-f", "./fixtures/run-test/compose.yaml", "down") - res := c.RunDockerCmd("ps", "--all") - assert.Assert(t, !strings.Contains(res.Stdout(), "run-test"), res.Stdout()) - }) - - t.Run("compose run --volumes", func(t *testing.T) { - wd, err := os.Getwd() - assert.NilError(t, err) - res := c.RunDockerCmd("compose", "-f", "./fixtures/run-test/compose.yaml", "run", "--volumes", wd+":/foo", "back", "/bin/sh", "-c", "ls /foo") - res.Assert(t, icmd.Expected{Out: "compose_run_test.go"}) - - res = c.RunDockerCmd("ps", "--all") - assert.Assert(t, strings.Contains(res.Stdout(), "run-test_back"), res.Stdout()) - }) - - t.Run("compose run --publish", func(t *testing.T) { - c.RunDockerCmd("compose", "-f", "./fixtures/run-test/compose.yaml", "run", "--publish", "8081:80", "-d", "back", "/bin/sh", "-c", "sleep 1") - res := c.RunDockerCmd("ps") - assert.Assert(t, strings.Contains(res.Stdout(), "8081->80/tcp"), res.Stdout()) - }) - - t.Run("down", func(t *testing.T) { - c.RunDockerCmd("compose", "-f", "./fixtures/run-test/compose.yaml", "down") - res := c.RunDockerCmd("ps", "--all") - assert.Assert(t, !strings.Contains(res.Stdout(), "run-test"), res.Stdout()) - }) -} diff --git a/pkg/e2e/compose_test.go b/pkg/e2e/compose_test.go deleted file mode 100644 index 11a3b947..00000000 --- a/pkg/e2e/compose_test.go +++ /dev/null @@ -1,200 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package e2e - -import ( - "fmt" - "io/ioutil" - "net/http" - "os" - "path/filepath" - "strings" - "testing" - "time" - - testify "github.com/stretchr/testify/assert" - "gotest.tools/v3/assert" - "gotest.tools/v3/icmd" -) - -var binDir string - -func TestMain(m *testing.M) { - exitCode := m.Run() - os.Exit(exitCode) -} - -func TestLocalComposeUp(t *testing.T) { - c := NewParallelE2eCLI(t, binDir) - - const projectName = "compose-e2e-demo" - - t.Run("up", func(t *testing.T) { - c.RunDockerCmd("compose", "-f", "./fixtures/sentences/compose.yaml", "--project-name", projectName, "up", "-d") - }) - - t.Run("check accessing running app", func(t *testing.T) { - res := c.RunDockerCmd("compose", "-p", projectName, "ps") - res.Assert(t, icmd.Expected{Out: `web`}) - - endpoint := "http://localhost:90" - output := HTTPGetWithRetry(t, endpoint+"/words/noun", http.StatusOK, 2*time.Second, 20*time.Second) - assert.Assert(t, strings.Contains(output, `"word":`)) - - res = c.RunDockerCmd("network", "ls") - res.Assert(t, icmd.Expected{Out: projectName + "_default"}) - }) - - t.Run("top", func(t *testing.T) { - res := c.RunDockerCmd("compose", "-p", projectName, "top") - output := res.Stdout() - head := []string{"UID", "PID", "PPID", "C", "STIME", "TTY", "TIME", "CMD"} - for _, h := range head { - assert.Assert(t, strings.Contains(output, h), output) - } - assert.Assert(t, strings.Contains(output, `java -Xmx8m -Xms8m -jar /app/words.jar`), output) - assert.Assert(t, strings.Contains(output, `/dispatcher`), output) - }) - - t.Run("check compose labels", func(t *testing.T) { - res := c.RunDockerCmd("inspect", projectName+"_web_1") - res.Assert(t, icmd.Expected{Out: `"com.docker.compose.container-number": "1"`}) - res.Assert(t, icmd.Expected{Out: `"com.docker.compose.project": "compose-e2e-demo"`}) - res.Assert(t, icmd.Expected{Out: `"com.docker.compose.oneoff": "False",`}) - res.Assert(t, icmd.Expected{Out: `"com.docker.compose.config-hash":`}) - res.Assert(t, icmd.Expected{Out: `"com.docker.compose.project.config_files":`}) - res.Assert(t, icmd.Expected{Out: `"com.docker.compose.project.working_dir":`}) - res.Assert(t, icmd.Expected{Out: `"com.docker.compose.service": "web"`}) - res.Assert(t, icmd.Expected{Out: `"com.docker.compose.version":`}) - - res = c.RunDockerCmd("network", "inspect", projectName+"_default") - res.Assert(t, icmd.Expected{Out: `"com.docker.compose.network": "default"`}) - res.Assert(t, icmd.Expected{Out: `"com.docker.compose.project": `}) - res.Assert(t, icmd.Expected{Out: `"com.docker.compose.version": `}) - }) - - t.Run("check user labels", func(t *testing.T) { - res := c.RunDockerCmd("inspect", projectName+"_web_1") - res.Assert(t, icmd.Expected{Out: `"my-label": "test"`}) - - }) - - t.Run("check healthcheck output", func(t *testing.T) { - c.WaitForCmdResult(c.NewDockerCmd("compose", "-p", projectName, "ps", "--format", "json"), - StdoutContains(`"Name":"compose-e2e-demo_web_1","Command":"/dispatcher","Project":"compose-e2e-demo","Service":"web","State":"running","Health":"healthy"`), - 5*time.Second, 1*time.Second) - - res := c.RunDockerCmd("compose", "-p", projectName, "ps") - res.Assert(t, icmd.Expected{Out: `NAME COMMAND SERVICE STATUS PORTS`}) - res.Assert(t, icmd.Expected{Out: `compose-e2e-demo_web_1 "/dispatcher" web running (healthy) 0.0.0.0:90->80/tcp, :::90->80/tcp`}) - res.Assert(t, icmd.Expected{Out: `compose-e2e-demo_db_1 "docker-entrypoint.s…" db running 5432/tcp`}) - }) - - t.Run("images", func(t *testing.T) { - res := c.RunDockerCmd("compose", "-p", projectName, "images") - res.Assert(t, icmd.Expected{Out: `compose-e2e-demo_db_1 gtardif/sentences-db latest`}) - res.Assert(t, icmd.Expected{Out: `compose-e2e-demo_web_1 gtardif/sentences-web latest`}) - res.Assert(t, icmd.Expected{Out: `compose-e2e-demo_words_1 gtardif/sentences-api latest`}) - }) - - t.Run("down", func(t *testing.T) { - _ = c.RunDockerCmd("compose", "--project-name", projectName, "down") - }) - - t.Run("check containers after down", func(t *testing.T) { - res := c.RunDockerCmd("ps", "--all") - assert.Assert(t, !strings.Contains(res.Combined(), projectName), res.Combined()) - }) - - t.Run("check networks after down", func(t *testing.T) { - res := c.RunDockerCmd("network", "ls") - assert.Assert(t, !strings.Contains(res.Combined(), projectName), res.Combined()) - }) -} - -func TestComposePull(t *testing.T) { - c := NewParallelE2eCLI(t, binDir) - - res := c.RunDockerOrExitError("compose", "--project-directory", "fixtures/simple-composefile", "pull") - output := res.Combined() - - assert.Assert(t, strings.Contains(output, "simple Pulled")) - assert.Assert(t, strings.Contains(output, "another Pulled")) -} - -func TestDownComposefileInParentFolder(t *testing.T) { - - c := NewParallelE2eCLI(t, binDir) - - tmpFolder, err := ioutil.TempDir("fixtures/simple-composefile", "test-tmp") - assert.NilError(t, err) - defer os.Remove(tmpFolder) // nolint: errcheck - projectName := filepath.Base(tmpFolder) - - res := c.RunDockerCmd("compose", "--project-directory", tmpFolder, "up", "-d") - res.Assert(t, icmd.Expected{Err: "Started", ExitCode: 0}) - - res = c.RunDockerCmd("compose", "-p", projectName, "down") - res.Assert(t, icmd.Expected{Err: "Removed", ExitCode: 0}) -} - -func TestAttachRestart(t *testing.T) { - c := NewParallelE2eCLI(t, binDir) - - cmd := c.NewDockerCmd("compose", "--ansi=never", "--project-directory", "./fixtures/attach-restart", "up") - res := icmd.StartCmd(cmd) - defer c.RunDockerOrExitError("compose", "-p", "attach-restart", "down") - - c.WaitForCondition(func() (bool, string) { - debug := res.Combined() - return strings.Count(res.Stdout(), "failing_1 exited with code 1") == 3, fmt.Sprintf("'failing_1 exited with code 1' not found 3 times in : \n%s\n", debug) - }, 2*time.Minute, 2*time.Second) - - assert.Equal(t, strings.Count(res.Stdout(), "failing_1 | world"), 3, res.Combined()) -} - -func TestInitContainer(t *testing.T) { - c := NewParallelE2eCLI(t, binDir) - - res := c.RunDockerOrExitError("compose", "--ansi=never", "--project-directory", "./fixtures/init-container", "up") - defer c.RunDockerOrExitError("compose", "-p", "init-container", "down") - testify.Regexp(t, "foo_1 | hello(?m:.*)bar_1 | world", res.Stdout()) -} - -func TestRm(t *testing.T) { - c := NewParallelE2eCLI(t, binDir) - - const projectName = "compose-e2e-rm" - - t.Run("up", func(t *testing.T) { - c.RunDockerCmd("compose", "-f", "./fixtures/simple-composefile/compose.yaml", "-p", projectName, "up", "-d") - }) - - t.Run("rm -sf", func(t *testing.T) { - res := c.RunDockerCmd("compose", "-f", "./fixtures/simple-composefile/compose.yaml", "-p", projectName, "rm", "-sf", "simple") - res.Assert(t, icmd.Expected{Err: "Removed", ExitCode: 0}) - }) - - t.Run("check containers after rm -sf", func(t *testing.T) { - res := c.RunDockerCmd("ps", "--all") - assert.Assert(t, !strings.Contains(res.Combined(), projectName+"_simple"), res.Combined()) - }) - - t.Run("down", func(t *testing.T) { - c.RunDockerCmd("compose", "-p", projectName, "down") - }) -} diff --git a/pkg/e2e/cp_test.go b/pkg/e2e/cp_test.go deleted file mode 100644 index 67f5c63e..00000000 --- a/pkg/e2e/cp_test.go +++ /dev/null @@ -1,118 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package e2e - -import ( - "os" - "strings" - "testing" - - "gotest.tools/v3/assert" - "gotest.tools/v3/icmd" -) - -func TestCopy(t *testing.T) { - c := NewParallelE2eCLI(t, binDir) - - const projectName = "copy_e2e" - - t.Cleanup(func() { - c.RunDockerCmd("compose", "-f", "./fixtures/cp-test/compose.yaml", "--project-name", projectName, "down") - - os.Remove("./fixtures/cp-test/from-default.txt") //nolint:errcheck - os.Remove("./fixtures/cp-test/from-indexed.txt") //nolint:errcheck - os.RemoveAll("./fixtures/cp-test/cp-folder2") //nolint:errcheck - }) - - t.Run("start service", func(t *testing.T) { - c.RunDockerCmd("compose", "-f", "./fixtures/cp-test/compose.yaml", "--project-name", projectName, "up", "--scale", "nginx=5", "-d") - }) - - t.Run("make sure service is running", func(t *testing.T) { - res := c.RunDockerCmd("compose", "-p", projectName, "ps") - res.Assert(t, icmd.Expected{Out: `nginx running`}) - }) - - t.Run("copy to container copies the file to the first container by default", func(t *testing.T) { - res := c.RunDockerCmd("compose", "-f", "./fixtures/cp-test/compose.yaml", "-p", projectName, "cp", "./fixtures/cp-test/cp-me.txt", "nginx:/tmp/default.txt") - res.Assert(t, icmd.Expected{ExitCode: 0}) - - output := c.RunDockerCmd("exec", projectName+"_nginx_1", "cat", "/tmp/default.txt").Stdout() - assert.Assert(t, strings.Contains(output, `hello world`), output) - - res = c.RunDockerOrExitError("exec", projectName+"_nginx_2", "cat", "/tmp/default.txt") - res.Assert(t, icmd.Expected{ExitCode: 1}) - }) - - t.Run("copy to container with a given index copies the file to the given container", func(t *testing.T) { - res := c.RunDockerCmd("compose", "-f", "./fixtures/cp-test/compose.yaml", "-p", projectName, "cp", "--index=3", "./fixtures/cp-test/cp-me.txt", "nginx:/tmp/indexed.txt") - res.Assert(t, icmd.Expected{ExitCode: 0}) - - output := c.RunDockerCmd("exec", projectName+"_nginx_3", "cat", "/tmp/indexed.txt").Stdout() - assert.Assert(t, strings.Contains(output, `hello world`), output) - - res = c.RunDockerOrExitError("exec", projectName+"_nginx_2", "cat", "/tmp/indexed.txt") - res.Assert(t, icmd.Expected{ExitCode: 1}) - }) - - t.Run("copy to container with the all flag copies the file to all containers", func(t *testing.T) { - res := c.RunDockerCmd("compose", "-f", "./fixtures/cp-test/compose.yaml", "-p", projectName, "cp", "--all", "./fixtures/cp-test/cp-me.txt", "nginx:/tmp/all.txt") - res.Assert(t, icmd.Expected{ExitCode: 0}) - - output := c.RunDockerCmd("exec", projectName+"_nginx_1", "cat", "/tmp/all.txt").Stdout() - assert.Assert(t, strings.Contains(output, `hello world`), output) - - output = c.RunDockerCmd("exec", projectName+"_nginx_2", "cat", "/tmp/all.txt").Stdout() - assert.Assert(t, strings.Contains(output, `hello world`), output) - - output = c.RunDockerCmd("exec", projectName+"_nginx_3", "cat", "/tmp/all.txt").Stdout() - assert.Assert(t, strings.Contains(output, `hello world`), output) - }) - - t.Run("copy from a container copies the file to the host from the first container by default", func(t *testing.T) { - res := c.RunDockerCmd("compose", "-f", "./fixtures/cp-test/compose.yaml", "-p", projectName, "cp", "nginx:/tmp/default.txt", "./fixtures/cp-test/from-default.txt") - res.Assert(t, icmd.Expected{ExitCode: 0}) - - data, err := os.ReadFile("./fixtures/cp-test/from-default.txt") - assert.NilError(t, err) - assert.Equal(t, `hello world`, string(data)) - }) - - t.Run("copy from a container with a given index copies the file to host", func(t *testing.T) { - res := c.RunDockerCmd("compose", "-f", "./fixtures/cp-test/compose.yaml", "-p", projectName, "cp", "--index=3", "nginx:/tmp/indexed.txt", "./fixtures/cp-test/from-indexed.txt") - res.Assert(t, icmd.Expected{ExitCode: 0}) - - data, err := os.ReadFile("./fixtures/cp-test/from-indexed.txt") - assert.NilError(t, err) - assert.Equal(t, `hello world`, string(data)) - }) - - t.Run("copy to and from a container also work with folder", func(t *testing.T) { - res := c.RunDockerCmd("compose", "-f", "./fixtures/cp-test/compose.yaml", "-p", projectName, "cp", "./fixtures/cp-test/cp-folder", "nginx:/tmp") - res.Assert(t, icmd.Expected{ExitCode: 0}) - - output := c.RunDockerCmd("exec", projectName+"_nginx_1", "cat", "/tmp/cp-folder/cp-me.txt").Stdout() - assert.Assert(t, strings.Contains(output, `hello world from folder`), output) - - res = c.RunDockerCmd("compose", "-f", "./fixtures/cp-test/compose.yaml", "-p", projectName, "cp", "nginx:/tmp/cp-folder", "./fixtures/cp-test/cp-folder2") - res.Assert(t, icmd.Expected{ExitCode: 0}) - - data, err := os.ReadFile("./fixtures/cp-test/cp-folder2/cp-me.txt") - assert.NilError(t, err) - assert.Equal(t, `hello world from folder`, string(data)) - }) -} diff --git a/pkg/e2e/fixtures/attach-restart/compose.yaml b/pkg/e2e/fixtures/attach-restart/compose.yaml deleted file mode 100644 index d9214367..00000000 --- a/pkg/e2e/fixtures/attach-restart/compose.yaml +++ /dev/null @@ -1,8 +0,0 @@ -services: - failing: - image: alpine - command: sh -c "sleep 0.1 && echo world && /bin/false" - deploy: - restart_policy: - condition: "on-failure" - max_attempts: 2 diff --git a/pkg/e2e/fixtures/build-infinite/compose.yaml b/pkg/e2e/fixtures/build-infinite/compose.yaml deleted file mode 100644 index cdc1e169..00000000 --- a/pkg/e2e/fixtures/build-infinite/compose.yaml +++ /dev/null @@ -1,3 +0,0 @@ -services: - service1: - build: service1 \ No newline at end of file diff --git a/pkg/e2e/fixtures/build-infinite/service1/Dockerfile b/pkg/e2e/fixtures/build-infinite/service1/Dockerfile deleted file mode 100644 index 3fd64e7b..00000000 --- a/pkg/e2e/fixtures/build-infinite/service1/Dockerfile +++ /dev/null @@ -1,17 +0,0 @@ -# Copyright 2020 Docker Compose CLI authors - -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at - -# http://www.apache.org/licenses/LICENSE-2.0 - -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -FROM busybox - -RUN sleep infinity \ No newline at end of file diff --git a/pkg/e2e/fixtures/build-test/compose.yaml b/pkg/e2e/fixtures/build-test/compose.yaml deleted file mode 100644 index 2db602a2..00000000 --- a/pkg/e2e/fixtures/build-test/compose.yaml +++ /dev/null @@ -1,9 +0,0 @@ -services: - nginx: - build: nginx-build - ports: - - 8070:80 - - nginx2: - build: nginx-build2 - image: custom-nginx diff --git a/pkg/e2e/fixtures/build-test/nginx-build/Dockerfile b/pkg/e2e/fixtures/build-test/nginx-build/Dockerfile deleted file mode 100644 index dd79c0e4..00000000 --- a/pkg/e2e/fixtures/build-test/nginx-build/Dockerfile +++ /dev/null @@ -1,19 +0,0 @@ -# Copyright 2020 Docker Compose CLI authors - -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at - -# http://www.apache.org/licenses/LICENSE-2.0 - -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -FROM nginx:alpine - -ARG FOO -LABEL FOO=$FOO -COPY static /usr/share/nginx/html diff --git a/pkg/e2e/fixtures/build-test/nginx-build/static/index.html b/pkg/e2e/fixtures/build-test/nginx-build/static/index.html deleted file mode 100644 index 63159b9e..00000000 --- a/pkg/e2e/fixtures/build-test/nginx-build/static/index.html +++ /dev/null @@ -1,10 +0,0 @@ - - - - - Docker Nginx - - -

Hello from Nginx container

- - diff --git a/pkg/e2e/fixtures/build-test/nginx-build2/Dockerfile b/pkg/e2e/fixtures/build-test/nginx-build2/Dockerfile deleted file mode 100644 index f8a39fac..00000000 --- a/pkg/e2e/fixtures/build-test/nginx-build2/Dockerfile +++ /dev/null @@ -1,17 +0,0 @@ -# Copyright 2020 Docker Compose CLI authors - -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at - -# http://www.apache.org/licenses/LICENSE-2.0 - -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -FROM nginx:alpine - -COPY static2 /usr/share/nginx/html diff --git a/pkg/e2e/fixtures/build-test/nginx-build2/static2/index.html b/pkg/e2e/fixtures/build-test/nginx-build2/static2/index.html deleted file mode 100644 index 63159b9e..00000000 --- a/pkg/e2e/fixtures/build-test/nginx-build2/static2/index.html +++ /dev/null @@ -1,10 +0,0 @@ - - - - - Docker Nginx - - -

Hello from Nginx container

- - diff --git a/pkg/e2e/fixtures/cascade-stop-test/compose.yaml b/pkg/e2e/fixtures/cascade-stop-test/compose.yaml deleted file mode 100644 index 1274fc53..00000000 --- a/pkg/e2e/fixtures/cascade-stop-test/compose.yaml +++ /dev/null @@ -1,7 +0,0 @@ -services: - should_fail: - image: alpine - command: ls /does_not_exist - sleep: # will be killed - image: alpine - command: ping localhost diff --git a/pkg/e2e/fixtures/cp-test/compose.yaml b/pkg/e2e/fixtures/cp-test/compose.yaml deleted file mode 100644 index f835ebee..00000000 --- a/pkg/e2e/fixtures/cp-test/compose.yaml +++ /dev/null @@ -1,3 +0,0 @@ -services: - nginx: - image: nginx:alpine \ No newline at end of file diff --git a/pkg/e2e/fixtures/cp-test/cp-folder/cp-me.txt b/pkg/e2e/fixtures/cp-test/cp-folder/cp-me.txt deleted file mode 100644 index a97acd87..00000000 --- a/pkg/e2e/fixtures/cp-test/cp-folder/cp-me.txt +++ /dev/null @@ -1 +0,0 @@ -hello world from folder \ No newline at end of file diff --git a/pkg/e2e/fixtures/cp-test/cp-me.txt b/pkg/e2e/fixtures/cp-test/cp-me.txt deleted file mode 100644 index 95d09f2b..00000000 --- a/pkg/e2e/fixtures/cp-test/cp-me.txt +++ /dev/null @@ -1 +0,0 @@ -hello world \ No newline at end of file diff --git a/pkg/e2e/fixtures/init-container/compose.yaml b/pkg/e2e/fixtures/init-container/compose.yaml deleted file mode 100644 index 275727ed..00000000 --- a/pkg/e2e/fixtures/init-container/compose.yaml +++ /dev/null @@ -1,11 +0,0 @@ -services: - foo: - image: alpine - command: "echo hello" - - bar: - image: alpine - command: "echo world" - depends_on: - foo: - condition: "service_completed_successfully" diff --git a/pkg/e2e/fixtures/ipam/compose.yaml b/pkg/e2e/fixtures/ipam/compose.yaml deleted file mode 100644 index 4cc479ed..00000000 --- a/pkg/e2e/fixtures/ipam/compose.yaml +++ /dev/null @@ -1,12 +0,0 @@ -services: - foo: - image: alpine - entrypoint: ["sleep", "600"] - networks: - default: - ipv4_address: 10.1.0.100 # <-- Fixed IP address -networks: - default: - ipam: - config: - - subnet: 10.1.0.0/16 \ No newline at end of file diff --git a/pkg/e2e/fixtures/ipc-test/compose.yaml b/pkg/e2e/fixtures/ipc-test/compose.yaml deleted file mode 100644 index 0659dd96..00000000 --- a/pkg/e2e/fixtures/ipc-test/compose.yaml +++ /dev/null @@ -1,13 +0,0 @@ -services: - service: - image: alpine - command: top - ipc: "service:shareable" - container: - image: alpine - command: top - ipc: "container:ipc_mode_container" - shareable: - image: alpine - command: top - ipc: shareable diff --git a/pkg/e2e/fixtures/logs-test/compose.yaml b/pkg/e2e/fixtures/logs-test/compose.yaml deleted file mode 100644 index 2b2c4c3b..00000000 --- a/pkg/e2e/fixtures/logs-test/compose.yaml +++ /dev/null @@ -1,7 +0,0 @@ -services: - ping: - image: alpine - command: ping localhost -c 1 - hello: - image: alpine - command: echo hello diff --git a/pkg/e2e/fixtures/network-alias/compose.yaml b/pkg/e2e/fixtures/network-alias/compose.yaml deleted file mode 100644 index f74726fe..00000000 --- a/pkg/e2e/fixtures/network-alias/compose.yaml +++ /dev/null @@ -1,13 +0,0 @@ -services: - - container1: - image: nginx - links: - - container2:container - - container2: - image: nginx - networks: - default: - aliases: - - alias-of-container2 diff --git a/pkg/e2e/fixtures/network-test/compose.yaml b/pkg/e2e/fixtures/network-test/compose.yaml deleted file mode 100644 index 0045ec7b..00000000 --- a/pkg/e2e/fixtures/network-test/compose.yaml +++ /dev/null @@ -1,30 +0,0 @@ -services: - mydb: - image: mariadb - network_mode: "service:db" - environment: - - MYSQL_ALLOW_EMPTY_PASSWORD=yes - db: - image: gtardif/sentences-db - networks: - - dbnet - words: - image: gtardif/sentences-api - ports: - - "8080:8080" - networks: - - dbnet - - servicenet - web: - image: gtardif/sentences-web - ports: - - "80:80" - labels: - - "my-label=test" - networks: - - servicenet - -networks: - dbnet: - servicenet: - name: microservices diff --git a/pkg/e2e/fixtures/restart-test/compose.yaml b/pkg/e2e/fixtures/restart-test/compose.yaml deleted file mode 100644 index 34038d5c..00000000 --- a/pkg/e2e/fixtures/restart-test/compose.yaml +++ /dev/null @@ -1,4 +0,0 @@ -services: - restart: - image: alpine - command: ash -c "if [[ -f /tmp/restart.lock ]] ; then sleep infinity; else touch /tmp/restart.lock; fi" diff --git a/pkg/e2e/fixtures/run-test/compose.yaml b/pkg/e2e/fixtures/run-test/compose.yaml deleted file mode 100644 index 0168dc24..00000000 --- a/pkg/e2e/fixtures/run-test/compose.yaml +++ /dev/null @@ -1,24 +0,0 @@ -version: '3.8' -services: - back: - image: alpine - command: echo "Hello there!!" - depends_on: - - db - networks: - - backnet - db: - image: nginx:alpine - networks: - - backnet - volumes: - - data:/test - front: - image: nginx:alpine - networks: - - frontnet -networks: - frontnet: - backnet: -volumes: - data: diff --git a/pkg/e2e/fixtures/sentences/compose.yaml b/pkg/e2e/fixtures/sentences/compose.yaml deleted file mode 100644 index 7916d1f4..00000000 --- a/pkg/e2e/fixtures/sentences/compose.yaml +++ /dev/null @@ -1,16 +0,0 @@ -services: - db: - image: gtardif/sentences-db - words: - image: gtardif/sentences-api - ports: - - "95:8080" - web: - image: gtardif/sentences-web - ports: - - "90:80" - labels: - - "my-label=test" - healthcheck: - test: ["CMD", "curl", "-f", "http://localhost:80/"] - interval: 2s diff --git a/pkg/e2e/fixtures/simple-build-test/compose.yaml b/pkg/e2e/fixtures/simple-build-test/compose.yaml deleted file mode 100644 index 2d072dbf..00000000 --- a/pkg/e2e/fixtures/simple-build-test/compose.yaml +++ /dev/null @@ -1,3 +0,0 @@ -services: - nginx: - build: nginx-build diff --git a/pkg/e2e/fixtures/simple-build-test/nginx-build/Dockerfile b/pkg/e2e/fixtures/simple-build-test/nginx-build/Dockerfile deleted file mode 100644 index dd79c0e4..00000000 --- a/pkg/e2e/fixtures/simple-build-test/nginx-build/Dockerfile +++ /dev/null @@ -1,19 +0,0 @@ -# Copyright 2020 Docker Compose CLI authors - -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at - -# http://www.apache.org/licenses/LICENSE-2.0 - -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -FROM nginx:alpine - -ARG FOO -LABEL FOO=$FOO -COPY static /usr/share/nginx/html diff --git a/pkg/e2e/fixtures/simple-build-test/nginx-build/static/index.html b/pkg/e2e/fixtures/simple-build-test/nginx-build/static/index.html deleted file mode 100644 index 63159b9e..00000000 --- a/pkg/e2e/fixtures/simple-build-test/nginx-build/static/index.html +++ /dev/null @@ -1,10 +0,0 @@ - - - - - Docker Nginx - - -

Hello from Nginx container

- - diff --git a/pkg/e2e/fixtures/simple-composefile/compose.yaml b/pkg/e2e/fixtures/simple-composefile/compose.yaml deleted file mode 100644 index 8b0b49ea..00000000 --- a/pkg/e2e/fixtures/simple-composefile/compose.yaml +++ /dev/null @@ -1,7 +0,0 @@ -services: - simple: - image: alpine - command: top - another: - image: alpine - command: top diff --git a/pkg/e2e/fixtures/start-stop/compose.yaml b/pkg/e2e/fixtures/start-stop/compose.yaml deleted file mode 100644 index 15f69b2e..00000000 --- a/pkg/e2e/fixtures/start-stop/compose.yaml +++ /dev/null @@ -1,5 +0,0 @@ -services: - simple: - image: nginx:alpine - another: - image: nginx:alpine diff --git a/pkg/e2e/fixtures/volume-test/compose.yaml b/pkg/e2e/fixtures/volume-test/compose.yaml deleted file mode 100644 index 7567da42..00000000 --- a/pkg/e2e/fixtures/volume-test/compose.yaml +++ /dev/null @@ -1,33 +0,0 @@ -services: - nginx: - build: nginx-build - volumes: - - ./static:/usr/share/nginx/html - ports: - - 8090:80 - - nginx2: - build: nginx-build - volumes: - - staticVol:/usr/share/nginx/html:ro - - /usr/src/app/node_modules - - otherVol:/usr/share/nginx/test - ports: - - 9090:80 - configs: - - myconfig - secrets: - - mysecret - -volumes: - staticVol: - otherVol: - name: myVolume - -configs: - myconfig: - file: ./static/index.html - -secrets: - mysecret: - file: ./static/index.html \ No newline at end of file diff --git a/pkg/e2e/fixtures/volume-test/nginx-build/Dockerfile b/pkg/e2e/fixtures/volume-test/nginx-build/Dockerfile deleted file mode 100644 index a05029ea..00000000 --- a/pkg/e2e/fixtures/volume-test/nginx-build/Dockerfile +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright 2020 Docker Compose CLI authors - -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at - -# http://www.apache.org/licenses/LICENSE-2.0 - -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -FROM nginx:alpine diff --git a/pkg/e2e/fixtures/volume-test/static/index.html b/pkg/e2e/fixtures/volume-test/static/index.html deleted file mode 100644 index 63159b9e..00000000 --- a/pkg/e2e/fixtures/volume-test/static/index.html +++ /dev/null @@ -1,10 +0,0 @@ - - - - - Docker Nginx - - -

Hello from Nginx container

- - diff --git a/pkg/e2e/fixtures/wrong-composefile/build-error.yml b/pkg/e2e/fixtures/wrong-composefile/build-error.yml deleted file mode 100644 index ea13a847..00000000 --- a/pkg/e2e/fixtures/wrong-composefile/build-error.yml +++ /dev/null @@ -1,3 +0,0 @@ -services: - simple: - build: service1 diff --git a/pkg/e2e/fixtures/wrong-composefile/compose.yaml b/pkg/e2e/fixtures/wrong-composefile/compose.yaml deleted file mode 100644 index b1c12a69..00000000 --- a/pkg/e2e/fixtures/wrong-composefile/compose.yaml +++ /dev/null @@ -1,4 +0,0 @@ -services: - simple: - image: nginx:alpine - wrongField: test diff --git a/pkg/e2e/fixtures/wrong-composefile/service1/Dockerfile b/pkg/e2e/fixtures/wrong-composefile/service1/Dockerfile deleted file mode 100644 index b57af619..00000000 --- a/pkg/e2e/fixtures/wrong-composefile/service1/Dockerfile +++ /dev/null @@ -1,17 +0,0 @@ -# Copyright 2020 Docker Compose CLI authors - -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at - -# http://www.apache.org/licenses/LICENSE-2.0 - -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -FROM nginx - -WRONG DOCKERFILE diff --git a/pkg/e2e/fixtures/wrong-composefile/unknown-image.yml b/pkg/e2e/fixtures/wrong-composefile/unknown-image.yml deleted file mode 100644 index 3000c468..00000000 --- a/pkg/e2e/fixtures/wrong-composefile/unknown-image.yml +++ /dev/null @@ -1,3 +0,0 @@ -services: - simple: - image: unknownimage diff --git a/pkg/e2e/framework.go b/pkg/e2e/framework.go deleted file mode 100644 index 1dd5a024..00000000 --- a/pkg/e2e/framework.go +++ /dev/null @@ -1,267 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package e2e - -import ( - "fmt" - "io" - "io/ioutil" - "net/http" - "os" - "path" - "path/filepath" - "runtime" - "strings" - "testing" - "time" - - "github.com/pkg/errors" - "gotest.tools/v3/assert" - is "gotest.tools/v3/assert/cmp" - "gotest.tools/v3/icmd" - "gotest.tools/v3/poll" -) - -var ( - // DockerExecutableName is the OS dependent Docker CLI binary name - DockerExecutableName = "docker" -) - -func init() { - if runtime.GOOS == "windows" { - DockerExecutableName = DockerExecutableName + ".exe" - } -} - -// E2eCLI is used to wrap the CLI for end to end testing -// nolint stutter -type E2eCLI struct { - BinDir string - ConfigDir string - test *testing.T -} - -// NewParallelE2eCLI returns a configured TestE2eCLI with t.Parallel() set -func NewParallelE2eCLI(t *testing.T, binDir string) *E2eCLI { - t.Parallel() - return newE2eCLI(t, binDir) -} - -func newE2eCLI(t *testing.T, binDir string) *E2eCLI { - d, err := ioutil.TempDir("", "") - assert.Check(t, is.Nil(err)) - - t.Cleanup(func() { - if t.Failed() { - conf, _ := ioutil.ReadFile(filepath.Join(d, "config.json")) - t.Errorf("Config: %s\n", string(conf)) - t.Error("Contents of config dir:") - for _, p := range dirContents(d) { - t.Errorf(p) - } - } - _ = os.RemoveAll(d) - }) - - _ = os.MkdirAll(filepath.Join(d, "cli-plugins"), 0755) - composePluginFile := "docker-compose" - scanPluginFile := "docker-scan" - if runtime.GOOS == "windows" { - composePluginFile += ".exe" - scanPluginFile += ".exe" - } - composePlugin, err := findExecutable(composePluginFile, []string{"../../bin", "../../../bin"}) - if os.IsNotExist(err) { - fmt.Println("WARNING: docker-compose cli-plugin not found") - } - if err == nil { - err = CopyFile(composePlugin, filepath.Join(d, "cli-plugins", composePluginFile)) - if err != nil { - panic(err) - } - // We don't need a functional scan plugin, but a valid plugin binary - err = CopyFile(composePlugin, filepath.Join(d, "cli-plugins", scanPluginFile)) - if err != nil { - panic(err) - } - } - - return &E2eCLI{binDir, d, t} -} - -func dirContents(dir string) []string { - res := []string{} - _ = filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { - res = append(res, filepath.Join(dir, path)) - return nil - }) - return res -} - -func findExecutable(executableName string, paths []string) (string, error) { - for _, p := range paths { - bin, err := filepath.Abs(path.Join(p, executableName)) - if err != nil { - return "", err - } - - if _, err := os.Stat(bin); os.IsNotExist(err) { - continue - } - - return bin, nil - } - - return "", errors.Wrap(os.ErrNotExist, "executable not found") -} - -// CopyFile copies a file from a sourceFile to a destinationFile setting permissions to 0755 -func CopyFile(sourceFile string, destinationFile string) error { - src, err := os.Open(sourceFile) - if err != nil { - return err - } - // nolint: errcheck - defer src.Close() - - dst, err := os.OpenFile(destinationFile, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0755) - if err != nil { - return err - } - // nolint: errcheck - defer dst.Close() - - if _, err = io.Copy(dst, src); err != nil { - return err - } - - return err -} - -// NewCmd creates a cmd object configured with the test environment set -func (c *E2eCLI) NewCmd(command string, args ...string) icmd.Cmd { - env := append(os.Environ(), - "DOCKER_CONFIG="+c.ConfigDir, - "KUBECONFIG=invalid", - ) - return icmd.Cmd{ - Command: append([]string{command}, args...), - Env: env, - } -} - -// MetricsSocket get the path where test metrics will be sent -func (c *E2eCLI) MetricsSocket() string { - return filepath.Join(c.ConfigDir, "./docker-cli.sock") -} - -// NewDockerCmd creates a docker cmd without running it -func (c *E2eCLI) NewDockerCmd(args ...string) icmd.Cmd { - return c.NewCmd(DockerExecutableName, args...) -} - -// RunDockerOrExitError runs a docker command and returns a result -func (c *E2eCLI) RunDockerOrExitError(args ...string) *icmd.Result { - fmt.Printf("\t[%s] docker %s\n", c.test.Name(), strings.Join(args, " ")) - return icmd.RunCmd(c.NewDockerCmd(args...)) -} - -// RunCmd runs a command, expects no error and returns a result -func (c *E2eCLI) RunCmd(args ...string) *icmd.Result { - fmt.Printf("\t[%s] %s\n", c.test.Name(), strings.Join(args, " ")) - assert.Assert(c.test, len(args) >= 1, "require at least one command in parameters") - res := icmd.RunCmd(c.NewCmd(args[0], args[1:]...)) - res.Assert(c.test, icmd.Success) - return res -} - -// RunDockerCmd runs a docker command, expects no error and returns a result -func (c *E2eCLI) RunDockerCmd(args ...string) *icmd.Result { - res := c.RunDockerOrExitError(args...) - res.Assert(c.test, icmd.Success) - return res -} - -// StdoutContains returns a predicate on command result expecting a string in stdout -func StdoutContains(expected string) func(*icmd.Result) bool { - return func(res *icmd.Result) bool { - return strings.Contains(res.Stdout(), expected) - } -} - -// WaitForCmdResult try to execute a cmd until resulting output matches given predicate -func (c *E2eCLI) WaitForCmdResult(command icmd.Cmd, predicate func(*icmd.Result) bool, timeout time.Duration, delay time.Duration) { - assert.Assert(c.test, timeout.Nanoseconds() > delay.Nanoseconds(), "timeout must be greater than delay") - var res *icmd.Result - checkStopped := func(logt poll.LogT) poll.Result { - fmt.Printf("\t[%s] %s\n", c.test.Name(), strings.Join(command.Command, " ")) - res = icmd.RunCmd(command) - if !predicate(res) { - return poll.Continue("Cmd output did not match requirement: %q", res.Combined()) - } - return poll.Success() - } - poll.WaitOn(c.test, checkStopped, poll.WithDelay(delay), poll.WithTimeout(timeout)) -} - -// WaitForCondition wait for predicate to execute to true -func (c *E2eCLI) WaitForCondition(predicate func() (bool, string), timeout time.Duration, delay time.Duration) { - checkStopped := func(logt poll.LogT) poll.Result { - pass, description := predicate() - if !pass { - return poll.Continue("Condition not met: %q", description) - } - return poll.Success() - } - poll.WaitOn(c.test, checkStopped, poll.WithDelay(delay), poll.WithTimeout(timeout)) -} - -//Lines split output into lines -func Lines(output string) []string { - return strings.Split(strings.TrimSpace(output), "\n") -} - -// HTTPGetWithRetry performs an HTTP GET on an `endpoint`, using retryDelay also as a request timeout. -// In the case of an error or the response status is not the expeted one, it retries the same request, -// returning the response body as a string (empty if we could not reach it) -func HTTPGetWithRetry(t *testing.T, endpoint string, expectedStatus int, retryDelay time.Duration, timeout time.Duration) string { - var ( - r *http.Response - err error - ) - client := &http.Client{ - Timeout: retryDelay, - } - fmt.Printf("\t[%s] GET %s\n", t.Name(), endpoint) - checkUp := func(t poll.LogT) poll.Result { - r, err = client.Get(endpoint) - if err != nil { - return poll.Continue("reaching %q: Error %s", endpoint, err.Error()) - } - if r.StatusCode == expectedStatus { - return poll.Success() - } - return poll.Continue("reaching %q: %d != %d", endpoint, r.StatusCode, expectedStatus) - } - poll.WaitOn(t, checkUp, poll.WithDelay(retryDelay), poll.WithTimeout(timeout)) - if r != nil { - b, err := ioutil.ReadAll(r.Body) - assert.NilError(t, err) - return string(b) - } - return "" -} diff --git a/pkg/e2e/ipc_test.go b/pkg/e2e/ipc_test.go deleted file mode 100644 index 4bf2d709..00000000 --- a/pkg/e2e/ipc_test.go +++ /dev/null @@ -1,63 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package e2e - -import ( - "fmt" - "strings" - "testing" - - "gotest.tools/v3/icmd" -) - -func TestIPC(t *testing.T) { - c := NewParallelE2eCLI(t, binDir) - - const projectName = "ipc_e2e" - var cid string - t.Run("create ipc mode container", func(t *testing.T) { - res := c.RunDockerCmd("run", "-d", "--rm", "--ipc=shareable", "--name", "ipc_mode_container", "alpine", "top") - cid = strings.Trim(res.Stdout(), "\n") - }) - - t.Run("up", func(t *testing.T) { - c.RunDockerCmd("compose", "-f", "./fixtures/ipc-test/compose.yaml", "--project-name", projectName, "up", "-d") - }) - - t.Run("check running project", func(t *testing.T) { - res := c.RunDockerCmd("compose", "-p", projectName, "ps") - res.Assert(t, icmd.Expected{Out: `shareable`}) - }) - - t.Run("check ipcmode in container inspect", func(t *testing.T) { - res := c.RunDockerCmd("inspect", projectName+"_shareable_1") - res.Assert(t, icmd.Expected{Out: `"IpcMode": "shareable",`}) - - res = c.RunDockerCmd("inspect", projectName+"_service_1") - res.Assert(t, icmd.Expected{Out: `"IpcMode": "container:`}) - - res = c.RunDockerCmd("inspect", projectName+"_container_1") - res.Assert(t, icmd.Expected{Out: fmt.Sprintf(`"IpcMode": "container:%s",`, cid)}) - }) - - t.Run("down", func(t *testing.T) { - _ = c.RunDockerCmd("compose", "--project-name", projectName, "down") - }) - t.Run("remove ipc mode container", func(t *testing.T) { - _ = c.RunDockerCmd("rm", "-f", "ipc_mode_container") - }) -} diff --git a/pkg/e2e/logs_test.go b/pkg/e2e/logs_test.go deleted file mode 100644 index 896c8eb4..00000000 --- a/pkg/e2e/logs_test.go +++ /dev/null @@ -1,58 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package e2e - -import ( - "strings" - "testing" - - "gotest.tools/v3/assert" - - "gotest.tools/v3/icmd" -) - -func TestLocalComposeLogs(t *testing.T) { - c := NewParallelE2eCLI(t, binDir) - - const projectName = "compose-e2e-logs" - - t.Run("up", func(t *testing.T) { - c.RunDockerCmd("compose", "-f", "./fixtures/logs-test/compose.yaml", "--project-name", projectName, "up", "-d") - }) - - t.Run("logs", func(t *testing.T) { - res := c.RunDockerCmd("compose", "--project-name", projectName, "logs") - res.Assert(t, icmd.Expected{Out: `PING localhost (127.0.0.1)`}) - res.Assert(t, icmd.Expected{Out: `hello`}) - }) - - t.Run("logs ping", func(t *testing.T) { - res := c.RunDockerCmd("compose", "--project-name", projectName, "logs", "ping") - res.Assert(t, icmd.Expected{Out: `PING localhost (127.0.0.1)`}) - assert.Assert(t, !strings.Contains(res.Stdout(), "hello")) - }) - - t.Run("logs hello", func(t *testing.T) { - res := c.RunDockerCmd("compose", "--project-name", projectName, "logs", "hello", "ping") - res.Assert(t, icmd.Expected{Out: `PING localhost (127.0.0.1)`}) - res.Assert(t, icmd.Expected{Out: `hello`}) - }) - - t.Run("down", func(t *testing.T) { - _ = c.RunDockerCmd("compose", "--project-name", projectName, "down") - }) -} diff --git a/pkg/e2e/metrics_test.go b/pkg/e2e/metrics_test.go deleted file mode 100644 index 6af51954..00000000 --- a/pkg/e2e/metrics_test.go +++ /dev/null @@ -1,55 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package e2e - -import ( - "runtime" - "testing" - - "gotest.tools/v3/icmd" -) - -func TestComposeMetrics(t *testing.T) { - c := NewParallelE2eCLI(t, binDir) - - t.Run("catch specific failure metrics", func(t *testing.T) { - res := c.RunDockerOrExitError("compose", "-f", "fixtures/does-not-exist/compose.yaml", "build") - expectedErr := "fixtures/does-not-exist/compose.yaml: no such file or directory" - if runtime.GOOS == "windows" { - expectedErr = "does-not-exist\\compose.yaml: The system cannot find the path specified" - } - res.Assert(t, icmd.Expected{ExitCode: 14, Err: expectedErr}) - res = c.RunDockerOrExitError("compose", "-f", "fixtures/wrong-composefile/compose.yaml", "up", "-d") - res.Assert(t, icmd.Expected{ExitCode: 15, Err: "services.simple Additional property wrongField is not allowed"}) - res = c.RunDockerOrExitError("compose", "up") - res.Assert(t, icmd.Expected{ExitCode: 14, Err: "can't find a suitable configuration file in this directory or any parent: not found"}) - res = c.RunDockerOrExitError("compose", "up", "-f", "fixtures/wrong-composefile/compose.yaml") - res.Assert(t, icmd.Expected{ExitCode: 16, Err: "unknown shorthand flag: 'f' in -f"}) - res = c.RunDockerOrExitError("compose", "up", "--file", "fixtures/wrong-composefile/compose.yaml") - res.Assert(t, icmd.Expected{ExitCode: 16, Err: "unknown flag: --file"}) - res = c.RunDockerOrExitError("compose", "donw", "--file", "fixtures/wrong-composefile/compose.yaml") - res.Assert(t, icmd.Expected{ExitCode: 16, Err: `unknown docker command: "compose donw"`}) - res = c.RunDockerOrExitError("compose", "--file", "fixtures/wrong-composefile/build-error.yml", "build") - res.Assert(t, icmd.Expected{ExitCode: 17, Err: `line 17: unknown instruction: WRONG`}) - res = c.RunDockerOrExitError("compose", "--file", "fixtures/wrong-composefile/build-error.yml", "up") - res.Assert(t, icmd.Expected{ExitCode: 17, Err: `line 17: unknown instruction: WRONG`}) - res = c.RunDockerOrExitError("compose", "--file", "fixtures/wrong-composefile/unknown-image.yml", "pull") - res.Assert(t, icmd.Expected{ExitCode: 18, Err: `pull access denied for unknownimage, repository does not exist or may require 'docker login'`}) - res = c.RunDockerOrExitError("compose", "--file", "fixtures/wrong-composefile/unknown-image.yml", "up") - res.Assert(t, icmd.Expected{ExitCode: 18, Err: `pull access denied for unknownimage, repository does not exist or may require 'docker login'`}) - }) -} diff --git a/pkg/e2e/networks_test.go b/pkg/e2e/networks_test.go deleted file mode 100644 index 20d1f15e..00000000 --- a/pkg/e2e/networks_test.go +++ /dev/null @@ -1,117 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package e2e - -import ( - "net/http" - "strings" - "testing" - "time" - - "gotest.tools/v3/assert" - "gotest.tools/v3/icmd" -) - -func TestNetworks(t *testing.T) { - c := NewParallelE2eCLI(t, binDir) - - const projectName = "network_e2e" - - t.Run("ensure we do not reuse previous networks", func(t *testing.T) { - c.RunDockerOrExitError("network", "rm", projectName+"_dbnet") - c.RunDockerOrExitError("network", "rm", "microservices") - }) - - t.Run("up", func(t *testing.T) { - c.RunDockerCmd("compose", "-f", "./fixtures/network-test/compose.yaml", "--project-name", projectName, "up", "-d") - }) - - t.Run("check running project", func(t *testing.T) { - res := c.RunDockerCmd("compose", "-p", projectName, "ps") - res.Assert(t, icmd.Expected{Out: `web`}) - - endpoint := "http://localhost:80" - output := HTTPGetWithRetry(t, endpoint+"/words/noun", http.StatusOK, 2*time.Second, 20*time.Second) - assert.Assert(t, strings.Contains(output, `"word":`)) - - res = c.RunDockerCmd("network", "ls") - res.Assert(t, icmd.Expected{Out: projectName + "_dbnet"}) - res.Assert(t, icmd.Expected{Out: "microservices"}) - }) - - t.Run("port", func(t *testing.T) { - res := c.RunDockerCmd("compose", "--project-name", projectName, "port", "words", "8080") - res.Assert(t, icmd.Expected{Out: `0.0.0.0:8080`}) - }) - - t.Run("down", func(t *testing.T) { - _ = c.RunDockerCmd("compose", "--project-name", projectName, "down") - }) - - t.Run("check networks after down", func(t *testing.T) { - res := c.RunDockerCmd("network", "ls") - assert.Assert(t, !strings.Contains(res.Combined(), projectName), res.Combined()) - assert.Assert(t, !strings.Contains(res.Combined(), "microservices"), res.Combined()) - }) -} - -func TestNetworkAliassesAndLinks(t *testing.T) { - c := NewParallelE2eCLI(t, binDir) - - const projectName = "network_alias_e2e" - - t.Run("up", func(t *testing.T) { - c.RunDockerCmd("compose", "-f", "./fixtures/network-alias/compose.yaml", "--project-name", projectName, "up", "-d") - }) - - t.Run("curl alias", func(t *testing.T) { - res := c.RunDockerCmd("compose", "-f", "./fixtures/network-alias/compose.yaml", "--project-name", projectName, "exec", "-T", "container1", "curl", "http://alias-of-container2/") - assert.Assert(t, strings.Contains(res.Stdout(), "Welcome to nginx!"), res.Stdout()) - }) - - t.Run("curl links", func(t *testing.T) { - res := c.RunDockerCmd("compose", "-f", "./fixtures/network-alias/compose.yaml", "--project-name", projectName, "exec", "-T", "container1", "curl", "http://container/") - assert.Assert(t, strings.Contains(res.Stdout(), "Welcome to nginx!"), res.Stdout()) - }) - - t.Run("down", func(t *testing.T) { - _ = c.RunDockerCmd("compose", "--project-name", projectName, "down") - }) -} - -func TestIPAMConfig(t *testing.T) { - c := NewParallelE2eCLI(t, binDir) - - const projectName = "ipam_e2e" - - t.Run("ensure we do not reuse previous networks", func(t *testing.T) { - c.RunDockerOrExitError("network", "rm", projectName+"_default") - }) - - t.Run("up", func(t *testing.T) { - c.RunDockerCmd("compose", "-f", "./fixtures/ipam/compose.yaml", "--project-name", projectName, "up", "-d") - }) - - t.Run("ensure service get fixed IP assigned", func(t *testing.T) { - res := c.RunDockerCmd("inspect", projectName+"_foo_1", "-f", "{{ .NetworkSettings.Networks."+projectName+"_default.IPAddress }}") - res.Assert(t, icmd.Expected{Out: "10.1.0.100"}) - }) - - t.Run("down", func(t *testing.T) { - _ = c.RunDockerCmd("compose", "--project-name", projectName, "down") - }) -} diff --git a/pkg/e2e/restart_test.go b/pkg/e2e/restart_test.go deleted file mode 100644 index dee349ff..00000000 --- a/pkg/e2e/restart_test.go +++ /dev/null @@ -1,64 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package e2e - -import ( - "fmt" - "strings" - "testing" - "time" - - testify "github.com/stretchr/testify/assert" - "gotest.tools/v3/assert" -) - -func TestRestart(t *testing.T) { - c := NewParallelE2eCLI(t, binDir) - const projectName = "e2e-restart" - - getServiceRegx := func(service string, status string) string { - // match output with random spaces like: - // e2e-start-stop_db_1 "echo hello" db running - return fmt.Sprintf("%s_%s_1.+%s\\s+%s", projectName, service, service, status) - } - - t.Run("Up a project", func(t *testing.T) { - // This is just to ensure the containers do NOT exist - c.RunDockerOrExitError("compose", "--project-name", projectName, "down") - - res := c.RunDockerOrExitError("compose", "-f", "./fixtures/restart-test/compose.yaml", "--project-name", projectName, "up", "-d") - assert.Assert(t, strings.Contains(res.Combined(), "Container e2e-restart_restart_1 Started"), res.Combined()) - - c.WaitForCmdResult(c.NewDockerCmd("compose", "--project-name", projectName, "ps", "-a", "--format", "json"), - StdoutContains(`"State":"exited"`), - 10*time.Second, 1*time.Second) - - res = c.RunDockerOrExitError("compose", "--project-name", projectName, "ps", "-a") - testify.Regexp(t, getServiceRegx("restart", "exited"), res.Stdout()) - - _ = c.RunDockerOrExitError("compose", "-f", "./fixtures/restart-test/compose.yaml", "--project-name", projectName, "restart") - - // Give the same time but it must NOT exit - time.Sleep(time.Second) - - res = c.RunDockerOrExitError("compose", "--project-name", projectName, "ps") - testify.Regexp(t, getServiceRegx("restart", "running"), res.Stdout()) - - // Clean up - c.RunDockerOrExitError("compose", "--project-name", projectName, "down") - }) -} diff --git a/pkg/e2e/scan_message_test.go b/pkg/e2e/scan_message_test.go deleted file mode 100644 index 16f5dad9..00000000 --- a/pkg/e2e/scan_message_test.go +++ /dev/null @@ -1,78 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package e2e - -import ( - "io/ioutil" - "os" - "path/filepath" - "strings" - "testing" - - "github.com/docker/compose-cli/pkg/utils" - - "gotest.tools/v3/assert" - "gotest.tools/v3/icmd" -) - -func TestDisplayScanMessageAfterBuild(t *testing.T) { - c := NewParallelE2eCLI(t, binDir) - - // assert docker scan plugin is available - c.RunDockerOrExitError("scan", "--help") - - t.Run("display on compose build", func(t *testing.T) { - res := c.RunDockerCmd("compose", "-f", "fixtures/simple-build-test/compose.yaml", "-p", "scan-msg-test-compose-build", "build") - defer c.RunDockerOrExitError("rmi", "-f", "scan-msg-test-compose-build_nginx") - res.Assert(t, icmd.Expected{Err: utils.ScanSuggestMsg}) - }) - - t.Run("do not display on compose build with quiet flag", func(t *testing.T) { - res := c.RunDockerCmd("compose", "-f", "fixtures/simple-build-test/compose.yaml", "-p", "scan-msg-test-quiet", "build", "--quiet") - assert.Assert(t, !strings.Contains(res.Combined(), "docker scan"), res.Combined()) - res = c.RunDockerCmd("rmi", "-f", "scan-msg-test-quiet_nginx") - assert.Assert(t, !strings.Contains(res.Combined(), "No such image")) - - res = c.RunDockerCmd("compose", "-f", "fixtures/simple-build-test/compose.yaml", "-p", "scan-msg-test-q", "build", "-q") - defer c.RunDockerOrExitError("rmi", "-f", "scan-msg-test-q_nginx") - assert.Assert(t, !strings.Contains(res.Combined(), "docker scan"), res.Combined()) - }) - - _ = c.RunDockerOrExitError("rmi", "scan-msg-test_nginx") - - t.Run("display on compose up if image is built", func(t *testing.T) { - res := c.RunDockerCmd("compose", "-f", "fixtures/simple-build-test/compose.yaml", "-p", "scan-msg-test", "up", "-d") - defer c.RunDockerOrExitError("compose", "-f", "fixtures/simple-build-test/compose.yaml", "-p", "scan-msg-test", "down") - res.Assert(t, icmd.Expected{Err: utils.ScanSuggestMsg}) - }) - - t.Run("do not display on compose up if no image built", func(t *testing.T) { // re-run the same Compose aproject - res := c.RunDockerCmd("compose", "-f", "fixtures/simple-build-test/compose.yaml", "-p", "scan-msg-test", "up", "-d") - defer c.RunDockerOrExitError("compose", "-f", "fixtures/simple-build-test/compose.yaml", "-p", "scan-msg-test", "down", "--rmi", "all") - assert.Assert(t, !strings.Contains(res.Combined(), "docker scan"), res.Combined()) - }) - - t.Run("do not display if scan already invoked", func(t *testing.T) { - _ = os.MkdirAll(filepath.Join(c.ConfigDir, "scan"), 0755) - scanConfigFile := filepath.Join(c.ConfigDir, "scan", "config.json") - err := ioutil.WriteFile(scanConfigFile, []byte(`{"optin":true}`), 0644) - assert.NilError(t, err) - - res := c.RunDockerCmd("build", "-t", "test-image-scan-msg", "fixtures/simple-build-test/nginx-build") - assert.Assert(t, !strings.Contains(res.Combined(), "docker scan"), res.Combined()) - }) -} diff --git a/pkg/e2e/start_stop_test.go b/pkg/e2e/start_stop_test.go deleted file mode 100644 index 5d897d08..00000000 --- a/pkg/e2e/start_stop_test.go +++ /dev/null @@ -1,97 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package e2e - -import ( - "fmt" - "strings" - "testing" - - testify "github.com/stretchr/testify/assert" - "gotest.tools/v3/assert" -) - -func TestStartStop(t *testing.T) { - c := NewParallelE2eCLI(t, binDir) - const projectName = "e2e-start-stop" - - getProjectRegx := func(status string) string { - // match output with random spaces like: - // e2e-start-stop running(3) - return fmt.Sprintf("%s\\s+%s\\(%d\\)", projectName, status, 2) - } - - getServiceRegx := func(service string, status string) string { - // match output with random spaces like: - // e2e-start-stop_db_1 "echo hello" db running - return fmt.Sprintf("%s_%s_1.+%s\\s+%s", projectName, service, service, status) - } - - t.Run("Up a project", func(t *testing.T) { - res := c.RunDockerCmd("compose", "-f", "./fixtures/start-stop/compose.yaml", "--project-name", projectName, "up", "-d") - assert.Assert(t, strings.Contains(res.Combined(), "Container e2e-start-stop_simple_1 Started"), res.Combined()) - - res = c.RunDockerCmd("compose", "ls", "--all") - testify.Regexp(t, getProjectRegx("running"), res.Stdout()) - - res = c.RunDockerCmd("compose", "--project-name", projectName, "ps") - testify.Regexp(t, getServiceRegx("simple", "running"), res.Stdout()) - testify.Regexp(t, getServiceRegx("another", "running"), res.Stdout()) - }) - - t.Run("stop project", func(t *testing.T) { - c.RunDockerCmd("compose", "-f", "./fixtures/start-stop/compose.yaml", "--project-name", projectName, "stop") - - res := c.RunDockerCmd("compose", "ls") - assert.Assert(t, !strings.Contains(res.Combined(), "e2e-start-stop"), res.Combined()) - - res = c.RunDockerCmd("compose", "ls", "--all") - testify.Regexp(t, getProjectRegx("exited"), res.Stdout()) - - res = c.RunDockerCmd("compose", "--project-name", projectName, "ps") - assert.Assert(t, !strings.Contains(res.Combined(), "e2e-start-stop_words_1"), res.Combined()) - - res = c.RunDockerCmd("compose", "--project-name", projectName, "ps", "--all") - testify.Regexp(t, getServiceRegx("simple", "exited"), res.Stdout()) - testify.Regexp(t, getServiceRegx("another", "exited"), res.Stdout()) - }) - - t.Run("start project", func(t *testing.T) { - c.RunDockerCmd("compose", "-f", "./fixtures/start-stop/compose.yaml", "--project-name", projectName, "start") - - res := c.RunDockerCmd("compose", "ls") - testify.Regexp(t, getProjectRegx("running"), res.Stdout()) - }) - - t.Run("pause project", func(t *testing.T) { - c.RunDockerCmd("compose", "-f", "./fixtures/start-stop/compose.yaml", "--project-name", projectName, "pause") - - res := c.RunDockerCmd("compose", "ls", "--all") - testify.Regexp(t, getProjectRegx("paused"), res.Stdout()) - }) - - t.Run("unpause project", func(t *testing.T) { - c.RunDockerCmd("compose", "-f", "./fixtures/start-stop/compose.yaml", "--project-name", projectName, "unpause") - - res := c.RunDockerCmd("compose", "ls") - testify.Regexp(t, getProjectRegx("running"), res.Stdout()) - }) - - t.Run("down", func(t *testing.T) { - _ = c.RunDockerCmd("compose", "--project-name", projectName, "down") - }) -} diff --git a/pkg/e2e/volumes_test.go b/pkg/e2e/volumes_test.go deleted file mode 100644 index 051cf73a..00000000 --- a/pkg/e2e/volumes_test.go +++ /dev/null @@ -1,90 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package e2e - -import ( - "net/http" - "strings" - "testing" - "time" - - "gotest.tools/v3/assert" -) - -func TestLocalComposeVolume(t *testing.T) { - c := NewParallelE2eCLI(t, binDir) - - const projectName = "compose-e2e-volume" - - t.Run("up with build and no image name, volume", func(t *testing.T) { - // ensure local test run does not reuse previously build image - c.RunDockerOrExitError("rmi", "compose-e2e-volume_nginx") - c.RunDockerOrExitError("volume", "rm", projectName+"_staticVol") - c.RunDockerOrExitError("volume", "rm", "myvolume") - c.RunDockerCmd("compose", "--project-directory", "fixtures/volume-test", "--project-name", projectName, "up", "-d") - }) - - t.Run("access bind mount data", func(t *testing.T) { - output := HTTPGetWithRetry(t, "http://localhost:8090", http.StatusOK, 2*time.Second, 20*time.Second) - assert.Assert(t, strings.Contains(output, "Hello from Nginx container")) - }) - - t.Run("check container volume specs", func(t *testing.T) { - res := c.RunDockerCmd("inspect", "compose-e2e-volume_nginx2_1", "--format", "{{ json .Mounts }}") - output := res.Stdout() - // nolint - assert.Assert(t, strings.Contains(output, `"Destination":"/usr/src/app/node_modules","Driver":"local","Mode":"z","RW":true,"Propagation":""`), output) - assert.Assert(t, strings.Contains(output, `"Destination":"/myconfig","Mode":"","RW":false,"Propagation":"rprivate"`), output) - }) - - t.Run("check config content", func(t *testing.T) { - output := c.RunDockerCmd("exec", "compose-e2e-volume_nginx2_1", "cat", "/myconfig").Stdout() - assert.Assert(t, strings.Contains(output, `Hello from Nginx container`), output) - }) - - t.Run("check secrets content", func(t *testing.T) { - output := c.RunDockerCmd("exec", "compose-e2e-volume_nginx2_1", "cat", "/run/secrets/mysecret").Stdout() - assert.Assert(t, strings.Contains(output, `Hello from Nginx container`), output) - }) - - t.Run("check container bind-mounts specs", func(t *testing.T) { - res := c.RunDockerCmd("inspect", "compose-e2e-volume_nginx_1", "--format", "{{ json .Mounts }}") - output := res.Stdout() - // nolint - assert.Assert(t, strings.Contains(output, `"Type":"bind"`)) - assert.Assert(t, strings.Contains(output, `"Destination":"/usr/share/nginx/html"`)) - }) - - t.Run("should inherit anonymous volumes", func(t *testing.T) { - c.RunDockerOrExitError("exec", "compose-e2e-volume_nginx2_1", "touch", "/usr/src/app/node_modules/test") - c.RunDockerOrExitError("compose", "--project-directory", "fixtures/volume-test", "--project-name", projectName, "up", "--force-recreate", "-d") - c.RunDockerOrExitError("exec", "compose-e2e-volume_nginx2_1", "ls", "/usr/src/app/node_modules/test") - }) - - t.Run("should renew anonymous volumes", func(t *testing.T) { - c.RunDockerOrExitError("exec", "compose-e2e-volume_nginx2_1", "touch", "/usr/src/app/node_modules/test") - c.RunDockerOrExitError("compose", "--project-directory", "fixtures/volume-test", "--project-name", projectName, "up", "--force-recreate", "--renew-anon-volumes", "-d") - c.RunDockerOrExitError("exec", "compose-e2e-volume_nginx2_1", "ls", "/usr/src/app/node_modules/test") - }) - - t.Run("cleanup volume project", func(t *testing.T) { - c.RunDockerCmd("compose", "--project-name", projectName, "down", "--volumes") - res := c.RunDockerCmd("volume", "ls") - assert.Assert(t, !strings.Contains(res.Stdout(), projectName+"_staticVol")) - assert.Assert(t, !strings.Contains(res.Stdout(), "myvolume")) - }) -} diff --git a/pkg/mocks/mock_docker_api.go b/pkg/mocks/mock_docker_api.go deleted file mode 100644 index 398138a7..00000000 --- a/pkg/mocks/mock_docker_api.go +++ /dev/null @@ -1,1812 +0,0 @@ -// Code generated by MockGen. DO NOT EDIT. -// Container: github.com/docker/docker/client (interfaces: APIClient) - -// Package mocks is a generated GoMock package. -package mocks - -import ( - context "context" - types "github.com/docker/docker/api/types" - container "github.com/docker/docker/api/types/container" - events "github.com/docker/docker/api/types/events" - filters "github.com/docker/docker/api/types/filters" - image "github.com/docker/docker/api/types/image" - network "github.com/docker/docker/api/types/network" - registry "github.com/docker/docker/api/types/registry" - swarm "github.com/docker/docker/api/types/swarm" - volume "github.com/docker/docker/api/types/volume" - gomock "github.com/golang/mock/gomock" - v1 "github.com/opencontainers/image-spec/specs-go/v1" - io "io" - net "net" - http "net/http" - reflect "reflect" - time "time" -) - -// MockAPIClient is a mock of APIClient interface -type MockAPIClient struct { - ctrl *gomock.Controller - recorder *MockAPIClientMockRecorder -} - -// MockAPIClientMockRecorder is the mock recorder for MockAPIClient -type MockAPIClientMockRecorder struct { - mock *MockAPIClient -} - -// NewMockAPIClient creates a new mock instance -func NewMockAPIClient(ctrl *gomock.Controller) *MockAPIClient { - mock := &MockAPIClient{ctrl: ctrl} - mock.recorder = &MockAPIClientMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use -func (m *MockAPIClient) EXPECT() *MockAPIClientMockRecorder { - return m.recorder -} - -// BuildCachePrune mocks base method -func (m *MockAPIClient) BuildCachePrune(arg0 context.Context, arg1 types.BuildCachePruneOptions) (*types.BuildCachePruneReport, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "BuildCachePrune", arg0, arg1) - ret0, _ := ret[0].(*types.BuildCachePruneReport) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// BuildCachePrune indicates an expected call of BuildCachePrune -func (mr *MockAPIClientMockRecorder) BuildCachePrune(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BuildCachePrune", reflect.TypeOf((*MockAPIClient)(nil).BuildCachePrune), arg0, arg1) -} - -// BuildCancel mocks base method -func (m *MockAPIClient) BuildCancel(arg0 context.Context, arg1 string) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "BuildCancel", arg0, arg1) - ret0, _ := ret[0].(error) - return ret0 -} - -// BuildCancel indicates an expected call of BuildCancel -func (mr *MockAPIClientMockRecorder) BuildCancel(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BuildCancel", reflect.TypeOf((*MockAPIClient)(nil).BuildCancel), arg0, arg1) -} - -// CheckpointCreate mocks base method -func (m *MockAPIClient) CheckpointCreate(arg0 context.Context, arg1 string, arg2 types.CheckpointCreateOptions) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CheckpointCreate", arg0, arg1, arg2) - ret0, _ := ret[0].(error) - return ret0 -} - -// CheckpointCreate indicates an expected call of CheckpointCreate -func (mr *MockAPIClientMockRecorder) CheckpointCreate(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CheckpointCreate", reflect.TypeOf((*MockAPIClient)(nil).CheckpointCreate), arg0, arg1, arg2) -} - -// CheckpointDelete mocks base method -func (m *MockAPIClient) CheckpointDelete(arg0 context.Context, arg1 string, arg2 types.CheckpointDeleteOptions) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CheckpointDelete", arg0, arg1, arg2) - ret0, _ := ret[0].(error) - return ret0 -} - -// CheckpointDelete indicates an expected call of CheckpointDelete -func (mr *MockAPIClientMockRecorder) CheckpointDelete(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CheckpointDelete", reflect.TypeOf((*MockAPIClient)(nil).CheckpointDelete), arg0, arg1, arg2) -} - -// CheckpointList mocks base method -func (m *MockAPIClient) CheckpointList(arg0 context.Context, arg1 string, arg2 types.CheckpointListOptions) ([]types.Checkpoint, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CheckpointList", arg0, arg1, arg2) - ret0, _ := ret[0].([]types.Checkpoint) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// CheckpointList indicates an expected call of CheckpointList -func (mr *MockAPIClientMockRecorder) CheckpointList(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CheckpointList", reflect.TypeOf((*MockAPIClient)(nil).CheckpointList), arg0, arg1, arg2) -} - -// ClientVersion mocks base method -func (m *MockAPIClient) ClientVersion() string { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ClientVersion") - ret0, _ := ret[0].(string) - return ret0 -} - -// ClientVersion indicates an expected call of ClientVersion -func (mr *MockAPIClientMockRecorder) ClientVersion() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ClientVersion", reflect.TypeOf((*MockAPIClient)(nil).ClientVersion)) -} - -// Close mocks base method -func (m *MockAPIClient) Close() error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Close") - ret0, _ := ret[0].(error) - return ret0 -} - -// Close indicates an expected call of Close -func (mr *MockAPIClientMockRecorder) Close() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockAPIClient)(nil).Close)) -} - -// ConfigCreate mocks base method -func (m *MockAPIClient) ConfigCreate(arg0 context.Context, arg1 swarm.ConfigSpec) (types.ConfigCreateResponse, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ConfigCreate", arg0, arg1) - ret0, _ := ret[0].(types.ConfigCreateResponse) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// ConfigCreate indicates an expected call of ConfigCreate -func (mr *MockAPIClientMockRecorder) ConfigCreate(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ConfigCreate", reflect.TypeOf((*MockAPIClient)(nil).ConfigCreate), arg0, arg1) -} - -// ConfigInspectWithRaw mocks base method -func (m *MockAPIClient) ConfigInspectWithRaw(arg0 context.Context, arg1 string) (swarm.Config, []byte, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ConfigInspectWithRaw", arg0, arg1) - ret0, _ := ret[0].(swarm.Config) - ret1, _ := ret[1].([]byte) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 -} - -// ConfigInspectWithRaw indicates an expected call of ConfigInspectWithRaw -func (mr *MockAPIClientMockRecorder) ConfigInspectWithRaw(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ConfigInspectWithRaw", reflect.TypeOf((*MockAPIClient)(nil).ConfigInspectWithRaw), arg0, arg1) -} - -// ConfigList mocks base method -func (m *MockAPIClient) ConfigList(arg0 context.Context, arg1 types.ConfigListOptions) ([]swarm.Config, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ConfigList", arg0, arg1) - ret0, _ := ret[0].([]swarm.Config) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// ConfigList indicates an expected call of ConfigList -func (mr *MockAPIClientMockRecorder) ConfigList(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ConfigList", reflect.TypeOf((*MockAPIClient)(nil).ConfigList), arg0, arg1) -} - -// ConfigRemove mocks base method -func (m *MockAPIClient) ConfigRemove(arg0 context.Context, arg1 string) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ConfigRemove", arg0, arg1) - ret0, _ := ret[0].(error) - return ret0 -} - -// ConfigRemove indicates an expected call of ConfigRemove -func (mr *MockAPIClientMockRecorder) ConfigRemove(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ConfigRemove", reflect.TypeOf((*MockAPIClient)(nil).ConfigRemove), arg0, arg1) -} - -// ConfigUpdate mocks base method -func (m *MockAPIClient) ConfigUpdate(arg0 context.Context, arg1 string, arg2 swarm.Version, arg3 swarm.ConfigSpec) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ConfigUpdate", arg0, arg1, arg2, arg3) - ret0, _ := ret[0].(error) - return ret0 -} - -// ConfigUpdate indicates an expected call of ConfigUpdate -func (mr *MockAPIClientMockRecorder) ConfigUpdate(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ConfigUpdate", reflect.TypeOf((*MockAPIClient)(nil).ConfigUpdate), arg0, arg1, arg2, arg3) -} - -// ContainerAttach mocks base method -func (m *MockAPIClient) ContainerAttach(arg0 context.Context, arg1 string, arg2 types.ContainerAttachOptions) (types.HijackedResponse, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ContainerAttach", arg0, arg1, arg2) - ret0, _ := ret[0].(types.HijackedResponse) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// ContainerAttach indicates an expected call of ContainerAttach -func (mr *MockAPIClientMockRecorder) ContainerAttach(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ContainerAttach", reflect.TypeOf((*MockAPIClient)(nil).ContainerAttach), arg0, arg1, arg2) -} - -// ContainerCommit mocks base method -func (m *MockAPIClient) ContainerCommit(arg0 context.Context, arg1 string, arg2 types.ContainerCommitOptions) (types.IDResponse, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ContainerCommit", arg0, arg1, arg2) - ret0, _ := ret[0].(types.IDResponse) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// ContainerCommit indicates an expected call of ContainerCommit -func (mr *MockAPIClientMockRecorder) ContainerCommit(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ContainerCommit", reflect.TypeOf((*MockAPIClient)(nil).ContainerCommit), arg0, arg1, arg2) -} - -// ContainerCreate mocks base method -func (m *MockAPIClient) ContainerCreate(arg0 context.Context, arg1 *container.Config, arg2 *container.HostConfig, arg3 *network.NetworkingConfig, arg4 *v1.Platform, arg5 string) (container.ContainerCreateCreatedBody, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ContainerCreate", arg0, arg1, arg2, arg3, arg4, arg5) - ret0, _ := ret[0].(container.ContainerCreateCreatedBody) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// ContainerCreate indicates an expected call of ContainerCreate -func (mr *MockAPIClientMockRecorder) ContainerCreate(arg0, arg1, arg2, arg3, arg4, arg5 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ContainerCreate", reflect.TypeOf((*MockAPIClient)(nil).ContainerCreate), arg0, arg1, arg2, arg3, arg4, arg5) -} - -// ContainerDiff mocks base method -func (m *MockAPIClient) ContainerDiff(arg0 context.Context, arg1 string) ([]container.ContainerChangeResponseItem, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ContainerDiff", arg0, arg1) - ret0, _ := ret[0].([]container.ContainerChangeResponseItem) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// ContainerDiff indicates an expected call of ContainerDiff -func (mr *MockAPIClientMockRecorder) ContainerDiff(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ContainerDiff", reflect.TypeOf((*MockAPIClient)(nil).ContainerDiff), arg0, arg1) -} - -// ContainerExecAttach mocks base method -func (m *MockAPIClient) ContainerExecAttach(arg0 context.Context, arg1 string, arg2 types.ExecStartCheck) (types.HijackedResponse, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ContainerExecAttach", arg0, arg1, arg2) - ret0, _ := ret[0].(types.HijackedResponse) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// ContainerExecAttach indicates an expected call of ContainerExecAttach -func (mr *MockAPIClientMockRecorder) ContainerExecAttach(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ContainerExecAttach", reflect.TypeOf((*MockAPIClient)(nil).ContainerExecAttach), arg0, arg1, arg2) -} - -// ContainerExecCreate mocks base method -func (m *MockAPIClient) ContainerExecCreate(arg0 context.Context, arg1 string, arg2 types.ExecConfig) (types.IDResponse, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ContainerExecCreate", arg0, arg1, arg2) - ret0, _ := ret[0].(types.IDResponse) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// ContainerExecCreate indicates an expected call of ContainerExecCreate -func (mr *MockAPIClientMockRecorder) ContainerExecCreate(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ContainerExecCreate", reflect.TypeOf((*MockAPIClient)(nil).ContainerExecCreate), arg0, arg1, arg2) -} - -// ContainerExecInspect mocks base method -func (m *MockAPIClient) ContainerExecInspect(arg0 context.Context, arg1 string) (types.ContainerExecInspect, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ContainerExecInspect", arg0, arg1) - ret0, _ := ret[0].(types.ContainerExecInspect) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// ContainerExecInspect indicates an expected call of ContainerExecInspect -func (mr *MockAPIClientMockRecorder) ContainerExecInspect(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ContainerExecInspect", reflect.TypeOf((*MockAPIClient)(nil).ContainerExecInspect), arg0, arg1) -} - -// ContainerExecResize mocks base method -func (m *MockAPIClient) ContainerExecResize(arg0 context.Context, arg1 string, arg2 types.ResizeOptions) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ContainerExecResize", arg0, arg1, arg2) - ret0, _ := ret[0].(error) - return ret0 -} - -// ContainerExecResize indicates an expected call of ContainerExecResize -func (mr *MockAPIClientMockRecorder) ContainerExecResize(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ContainerExecResize", reflect.TypeOf((*MockAPIClient)(nil).ContainerExecResize), arg0, arg1, arg2) -} - -// ContainerExecStart mocks base method -func (m *MockAPIClient) ContainerExecStart(arg0 context.Context, arg1 string, arg2 types.ExecStartCheck) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ContainerExecStart", arg0, arg1, arg2) - ret0, _ := ret[0].(error) - return ret0 -} - -// ContainerExecStart indicates an expected call of ContainerExecStart -func (mr *MockAPIClientMockRecorder) ContainerExecStart(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ContainerExecStart", reflect.TypeOf((*MockAPIClient)(nil).ContainerExecStart), arg0, arg1, arg2) -} - -// ContainerExport mocks base method -func (m *MockAPIClient) ContainerExport(arg0 context.Context, arg1 string) (io.ReadCloser, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ContainerExport", arg0, arg1) - ret0, _ := ret[0].(io.ReadCloser) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// ContainerExport indicates an expected call of ContainerExport -func (mr *MockAPIClientMockRecorder) ContainerExport(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ContainerExport", reflect.TypeOf((*MockAPIClient)(nil).ContainerExport), arg0, arg1) -} - -// ContainerInspect mocks base method -func (m *MockAPIClient) ContainerInspect(arg0 context.Context, arg1 string) (types.ContainerJSON, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ContainerInspect", arg0, arg1) - ret0, _ := ret[0].(types.ContainerJSON) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// ContainerInspect indicates an expected call of ContainerInspect -func (mr *MockAPIClientMockRecorder) ContainerInspect(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ContainerInspect", reflect.TypeOf((*MockAPIClient)(nil).ContainerInspect), arg0, arg1) -} - -// ContainerInspectWithRaw mocks base method -func (m *MockAPIClient) ContainerInspectWithRaw(arg0 context.Context, arg1 string, arg2 bool) (types.ContainerJSON, []byte, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ContainerInspectWithRaw", arg0, arg1, arg2) - ret0, _ := ret[0].(types.ContainerJSON) - ret1, _ := ret[1].([]byte) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 -} - -// ContainerInspectWithRaw indicates an expected call of ContainerInspectWithRaw -func (mr *MockAPIClientMockRecorder) ContainerInspectWithRaw(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ContainerInspectWithRaw", reflect.TypeOf((*MockAPIClient)(nil).ContainerInspectWithRaw), arg0, arg1, arg2) -} - -// ContainerKill mocks base method -func (m *MockAPIClient) ContainerKill(arg0 context.Context, arg1, arg2 string) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ContainerKill", arg0, arg1, arg2) - ret0, _ := ret[0].(error) - return ret0 -} - -// ContainerKill indicates an expected call of ContainerKill -func (mr *MockAPIClientMockRecorder) ContainerKill(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ContainerKill", reflect.TypeOf((*MockAPIClient)(nil).ContainerKill), arg0, arg1, arg2) -} - -// ContainerList mocks base method -func (m *MockAPIClient) ContainerList(arg0 context.Context, arg1 types.ContainerListOptions) ([]types.Container, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ContainerList", arg0, arg1) - ret0, _ := ret[0].([]types.Container) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// ContainerList indicates an expected call of ContainerList -func (mr *MockAPIClientMockRecorder) ContainerList(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ContainerList", reflect.TypeOf((*MockAPIClient)(nil).ContainerList), arg0, arg1) -} - -// ContainerLogs mocks base method -func (m *MockAPIClient) ContainerLogs(arg0 context.Context, arg1 string, arg2 types.ContainerLogsOptions) (io.ReadCloser, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ContainerLogs", arg0, arg1, arg2) - ret0, _ := ret[0].(io.ReadCloser) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// ContainerLogs indicates an expected call of ContainerLogs -func (mr *MockAPIClientMockRecorder) ContainerLogs(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ContainerLogs", reflect.TypeOf((*MockAPIClient)(nil).ContainerLogs), arg0, arg1, arg2) -} - -// ContainerPause mocks base method -func (m *MockAPIClient) ContainerPause(arg0 context.Context, arg1 string) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ContainerPause", arg0, arg1) - ret0, _ := ret[0].(error) - return ret0 -} - -// ContainerPause indicates an expected call of ContainerPause -func (mr *MockAPIClientMockRecorder) ContainerPause(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ContainerPause", reflect.TypeOf((*MockAPIClient)(nil).ContainerPause), arg0, arg1) -} - -// ContainerRemove mocks base method -func (m *MockAPIClient) ContainerRemove(arg0 context.Context, arg1 string, arg2 types.ContainerRemoveOptions) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ContainerRemove", arg0, arg1, arg2) - ret0, _ := ret[0].(error) - return ret0 -} - -// ContainerRemove indicates an expected call of ContainerRemove -func (mr *MockAPIClientMockRecorder) ContainerRemove(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ContainerRemove", reflect.TypeOf((*MockAPIClient)(nil).ContainerRemove), arg0, arg1, arg2) -} - -// ContainerRename mocks base method -func (m *MockAPIClient) ContainerRename(arg0 context.Context, arg1, arg2 string) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ContainerRename", arg0, arg1, arg2) - ret0, _ := ret[0].(error) - return ret0 -} - -// ContainerRename indicates an expected call of ContainerRename -func (mr *MockAPIClientMockRecorder) ContainerRename(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ContainerRename", reflect.TypeOf((*MockAPIClient)(nil).ContainerRename), arg0, arg1, arg2) -} - -// ContainerResize mocks base method -func (m *MockAPIClient) ContainerResize(arg0 context.Context, arg1 string, arg2 types.ResizeOptions) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ContainerResize", arg0, arg1, arg2) - ret0, _ := ret[0].(error) - return ret0 -} - -// ContainerResize indicates an expected call of ContainerResize -func (mr *MockAPIClientMockRecorder) ContainerResize(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ContainerResize", reflect.TypeOf((*MockAPIClient)(nil).ContainerResize), arg0, arg1, arg2) -} - -// ContainerRestart mocks base method -func (m *MockAPIClient) ContainerRestart(arg0 context.Context, arg1 string, arg2 *time.Duration) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ContainerRestart", arg0, arg1, arg2) - ret0, _ := ret[0].(error) - return ret0 -} - -// ContainerRestart indicates an expected call of ContainerRestart -func (mr *MockAPIClientMockRecorder) ContainerRestart(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ContainerRestart", reflect.TypeOf((*MockAPIClient)(nil).ContainerRestart), arg0, arg1, arg2) -} - -// ContainerStart mocks base method -func (m *MockAPIClient) ContainerStart(arg0 context.Context, arg1 string, arg2 types.ContainerStartOptions) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ContainerStart", arg0, arg1, arg2) - ret0, _ := ret[0].(error) - return ret0 -} - -// ContainerStart indicates an expected call of ContainerStart -func (mr *MockAPIClientMockRecorder) ContainerStart(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ContainerStart", reflect.TypeOf((*MockAPIClient)(nil).ContainerStart), arg0, arg1, arg2) -} - -// ContainerStatPath mocks base method -func (m *MockAPIClient) ContainerStatPath(arg0 context.Context, arg1, arg2 string) (types.ContainerPathStat, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ContainerStatPath", arg0, arg1, arg2) - ret0, _ := ret[0].(types.ContainerPathStat) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// ContainerStatPath indicates an expected call of ContainerStatPath -func (mr *MockAPIClientMockRecorder) ContainerStatPath(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ContainerStatPath", reflect.TypeOf((*MockAPIClient)(nil).ContainerStatPath), arg0, arg1, arg2) -} - -// ContainerStats mocks base method -func (m *MockAPIClient) ContainerStats(arg0 context.Context, arg1 string, arg2 bool) (types.ContainerStats, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ContainerStats", arg0, arg1, arg2) - ret0, _ := ret[0].(types.ContainerStats) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// ContainerStats indicates an expected call of ContainerStats -func (mr *MockAPIClientMockRecorder) ContainerStats(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ContainerStats", reflect.TypeOf((*MockAPIClient)(nil).ContainerStats), arg0, arg1, arg2) -} - -// ContainerStatsOneShot mocks base method -func (m *MockAPIClient) ContainerStatsOneShot(arg0 context.Context, arg1 string) (types.ContainerStats, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ContainerStatsOneShot", arg0, arg1) - ret0, _ := ret[0].(types.ContainerStats) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// ContainerStatsOneShot indicates an expected call of ContainerStatsOneShot -func (mr *MockAPIClientMockRecorder) ContainerStatsOneShot(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ContainerStatsOneShot", reflect.TypeOf((*MockAPIClient)(nil).ContainerStatsOneShot), arg0, arg1) -} - -// ContainerStop mocks base method -func (m *MockAPIClient) ContainerStop(arg0 context.Context, arg1 string, arg2 *time.Duration) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ContainerStop", arg0, arg1, arg2) - ret0, _ := ret[0].(error) - return ret0 -} - -// ContainerStop indicates an expected call of ContainerStop -func (mr *MockAPIClientMockRecorder) ContainerStop(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ContainerStop", reflect.TypeOf((*MockAPIClient)(nil).ContainerStop), arg0, arg1, arg2) -} - -// ContainerTop mocks base method -func (m *MockAPIClient) ContainerTop(arg0 context.Context, arg1 string, arg2 []string) (container.ContainerTopOKBody, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ContainerTop", arg0, arg1, arg2) - ret0, _ := ret[0].(container.ContainerTopOKBody) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// ContainerTop indicates an expected call of ContainerTop -func (mr *MockAPIClientMockRecorder) ContainerTop(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ContainerTop", reflect.TypeOf((*MockAPIClient)(nil).ContainerTop), arg0, arg1, arg2) -} - -// ContainerUnpause mocks base method -func (m *MockAPIClient) ContainerUnpause(arg0 context.Context, arg1 string) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ContainerUnpause", arg0, arg1) - ret0, _ := ret[0].(error) - return ret0 -} - -// ContainerUnpause indicates an expected call of ContainerUnpause -func (mr *MockAPIClientMockRecorder) ContainerUnpause(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ContainerUnpause", reflect.TypeOf((*MockAPIClient)(nil).ContainerUnpause), arg0, arg1) -} - -// ContainerUpdate mocks base method -func (m *MockAPIClient) ContainerUpdate(arg0 context.Context, arg1 string, arg2 container.UpdateConfig) (container.ContainerUpdateOKBody, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ContainerUpdate", arg0, arg1, arg2) - ret0, _ := ret[0].(container.ContainerUpdateOKBody) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// ContainerUpdate indicates an expected call of ContainerUpdate -func (mr *MockAPIClientMockRecorder) ContainerUpdate(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ContainerUpdate", reflect.TypeOf((*MockAPIClient)(nil).ContainerUpdate), arg0, arg1, arg2) -} - -// ContainerWait mocks base method -func (m *MockAPIClient) ContainerWait(arg0 context.Context, arg1 string, arg2 container.WaitCondition) (<-chan container.ContainerWaitOKBody, <-chan error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ContainerWait", arg0, arg1, arg2) - ret0, _ := ret[0].(<-chan container.ContainerWaitOKBody) - ret1, _ := ret[1].(<-chan error) - return ret0, ret1 -} - -// ContainerWait indicates an expected call of ContainerWait -func (mr *MockAPIClientMockRecorder) ContainerWait(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ContainerWait", reflect.TypeOf((*MockAPIClient)(nil).ContainerWait), arg0, arg1, arg2) -} - -// ContainersPrune mocks base method -func (m *MockAPIClient) ContainersPrune(arg0 context.Context, arg1 filters.Args) (types.ContainersPruneReport, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ContainersPrune", arg0, arg1) - ret0, _ := ret[0].(types.ContainersPruneReport) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// ContainersPrune indicates an expected call of ContainersPrune -func (mr *MockAPIClientMockRecorder) ContainersPrune(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ContainersPrune", reflect.TypeOf((*MockAPIClient)(nil).ContainersPrune), arg0, arg1) -} - -// CopyFromContainer mocks base method -func (m *MockAPIClient) CopyFromContainer(arg0 context.Context, arg1, arg2 string) (io.ReadCloser, types.ContainerPathStat, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CopyFromContainer", arg0, arg1, arg2) - ret0, _ := ret[0].(io.ReadCloser) - ret1, _ := ret[1].(types.ContainerPathStat) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 -} - -// CopyFromContainer indicates an expected call of CopyFromContainer -func (mr *MockAPIClientMockRecorder) CopyFromContainer(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CopyFromContainer", reflect.TypeOf((*MockAPIClient)(nil).CopyFromContainer), arg0, arg1, arg2) -} - -// CopyToContainer mocks base method -func (m *MockAPIClient) CopyToContainer(arg0 context.Context, arg1, arg2 string, arg3 io.Reader, arg4 types.CopyToContainerOptions) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CopyToContainer", arg0, arg1, arg2, arg3, arg4) - ret0, _ := ret[0].(error) - return ret0 -} - -// CopyToContainer indicates an expected call of CopyToContainer -func (mr *MockAPIClientMockRecorder) CopyToContainer(arg0, arg1, arg2, arg3, arg4 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CopyToContainer", reflect.TypeOf((*MockAPIClient)(nil).CopyToContainer), arg0, arg1, arg2, arg3, arg4) -} - -// DaemonHost mocks base method -func (m *MockAPIClient) DaemonHost() string { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "DaemonHost") - ret0, _ := ret[0].(string) - return ret0 -} - -// DaemonHost indicates an expected call of DaemonHost -func (mr *MockAPIClientMockRecorder) DaemonHost() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DaemonHost", reflect.TypeOf((*MockAPIClient)(nil).DaemonHost)) -} - -// DialHijack mocks base method -func (m *MockAPIClient) DialHijack(arg0 context.Context, arg1, arg2 string, arg3 map[string][]string) (net.Conn, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "DialHijack", arg0, arg1, arg2, arg3) - ret0, _ := ret[0].(net.Conn) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// DialHijack indicates an expected call of DialHijack -func (mr *MockAPIClientMockRecorder) DialHijack(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DialHijack", reflect.TypeOf((*MockAPIClient)(nil).DialHijack), arg0, arg1, arg2, arg3) -} - -// Dialer mocks base method -func (m *MockAPIClient) Dialer() func(context.Context) (net.Conn, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Dialer") - ret0, _ := ret[0].(func(context.Context) (net.Conn, error)) - return ret0 -} - -// Dialer indicates an expected call of Dialer -func (mr *MockAPIClientMockRecorder) Dialer() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Dialer", reflect.TypeOf((*MockAPIClient)(nil).Dialer)) -} - -// DiskUsage mocks base method -func (m *MockAPIClient) DiskUsage(arg0 context.Context) (types.DiskUsage, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "DiskUsage", arg0) - ret0, _ := ret[0].(types.DiskUsage) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// DiskUsage indicates an expected call of DiskUsage -func (mr *MockAPIClientMockRecorder) DiskUsage(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DiskUsage", reflect.TypeOf((*MockAPIClient)(nil).DiskUsage), arg0) -} - -// DistributionInspect mocks base method -func (m *MockAPIClient) DistributionInspect(arg0 context.Context, arg1, arg2 string) (registry.DistributionInspect, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "DistributionInspect", arg0, arg1, arg2) - ret0, _ := ret[0].(registry.DistributionInspect) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// DistributionInspect indicates an expected call of DistributionInspect -func (mr *MockAPIClientMockRecorder) DistributionInspect(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DistributionInspect", reflect.TypeOf((*MockAPIClient)(nil).DistributionInspect), arg0, arg1, arg2) -} - -// Events mocks base method -func (m *MockAPIClient) Events(arg0 context.Context, arg1 types.EventsOptions) (<-chan events.Message, <-chan error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Events", arg0, arg1) - ret0, _ := ret[0].(<-chan events.Message) - ret1, _ := ret[1].(<-chan error) - return ret0, ret1 -} - -// Events indicates an expected call of Events -func (mr *MockAPIClientMockRecorder) Events(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Events", reflect.TypeOf((*MockAPIClient)(nil).Events), arg0, arg1) -} - -// HTTPClient mocks base method -func (m *MockAPIClient) HTTPClient() *http.Client { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "HTTPClient") - ret0, _ := ret[0].(*http.Client) - return ret0 -} - -// HTTPClient indicates an expected call of HTTPClient -func (mr *MockAPIClientMockRecorder) HTTPClient() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HTTPClient", reflect.TypeOf((*MockAPIClient)(nil).HTTPClient)) -} - -// ImageBuild mocks base method -func (m *MockAPIClient) ImageBuild(arg0 context.Context, arg1 io.Reader, arg2 types.ImageBuildOptions) (types.ImageBuildResponse, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ImageBuild", arg0, arg1, arg2) - ret0, _ := ret[0].(types.ImageBuildResponse) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// ImageBuild indicates an expected call of ImageBuild -func (mr *MockAPIClientMockRecorder) ImageBuild(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ImageBuild", reflect.TypeOf((*MockAPIClient)(nil).ImageBuild), arg0, arg1, arg2) -} - -// ImageCreate mocks base method -func (m *MockAPIClient) ImageCreate(arg0 context.Context, arg1 string, arg2 types.ImageCreateOptions) (io.ReadCloser, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ImageCreate", arg0, arg1, arg2) - ret0, _ := ret[0].(io.ReadCloser) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// ImageCreate indicates an expected call of ImageCreate -func (mr *MockAPIClientMockRecorder) ImageCreate(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ImageCreate", reflect.TypeOf((*MockAPIClient)(nil).ImageCreate), arg0, arg1, arg2) -} - -// ImageHistory mocks base method -func (m *MockAPIClient) ImageHistory(arg0 context.Context, arg1 string) ([]image.HistoryResponseItem, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ImageHistory", arg0, arg1) - ret0, _ := ret[0].([]image.HistoryResponseItem) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// ImageHistory indicates an expected call of ImageHistory -func (mr *MockAPIClientMockRecorder) ImageHistory(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ImageHistory", reflect.TypeOf((*MockAPIClient)(nil).ImageHistory), arg0, arg1) -} - -// ImageImport mocks base method -func (m *MockAPIClient) ImageImport(arg0 context.Context, arg1 types.ImageImportSource, arg2 string, arg3 types.ImageImportOptions) (io.ReadCloser, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ImageImport", arg0, arg1, arg2, arg3) - ret0, _ := ret[0].(io.ReadCloser) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// ImageImport indicates an expected call of ImageImport -func (mr *MockAPIClientMockRecorder) ImageImport(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ImageImport", reflect.TypeOf((*MockAPIClient)(nil).ImageImport), arg0, arg1, arg2, arg3) -} - -// ImageInspectWithRaw mocks base method -func (m *MockAPIClient) ImageInspectWithRaw(arg0 context.Context, arg1 string) (types.ImageInspect, []byte, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ImageInspectWithRaw", arg0, arg1) - ret0, _ := ret[0].(types.ImageInspect) - ret1, _ := ret[1].([]byte) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 -} - -// ImageInspectWithRaw indicates an expected call of ImageInspectWithRaw -func (mr *MockAPIClientMockRecorder) ImageInspectWithRaw(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ImageInspectWithRaw", reflect.TypeOf((*MockAPIClient)(nil).ImageInspectWithRaw), arg0, arg1) -} - -// ImageList mocks base method -func (m *MockAPIClient) ImageList(arg0 context.Context, arg1 types.ImageListOptions) ([]types.ImageSummary, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ImageList", arg0, arg1) - ret0, _ := ret[0].([]types.ImageSummary) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// ImageList indicates an expected call of ImageList -func (mr *MockAPIClientMockRecorder) ImageList(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ImageList", reflect.TypeOf((*MockAPIClient)(nil).ImageList), arg0, arg1) -} - -// ImageLoad mocks base method -func (m *MockAPIClient) ImageLoad(arg0 context.Context, arg1 io.Reader, arg2 bool) (types.ImageLoadResponse, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ImageLoad", arg0, arg1, arg2) - ret0, _ := ret[0].(types.ImageLoadResponse) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// ImageLoad indicates an expected call of ImageLoad -func (mr *MockAPIClientMockRecorder) ImageLoad(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ImageLoad", reflect.TypeOf((*MockAPIClient)(nil).ImageLoad), arg0, arg1, arg2) -} - -// ImagePull mocks base method -func (m *MockAPIClient) ImagePull(arg0 context.Context, arg1 string, arg2 types.ImagePullOptions) (io.ReadCloser, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ImagePull", arg0, arg1, arg2) - ret0, _ := ret[0].(io.ReadCloser) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// ImagePull indicates an expected call of ImagePull -func (mr *MockAPIClientMockRecorder) ImagePull(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ImagePull", reflect.TypeOf((*MockAPIClient)(nil).ImagePull), arg0, arg1, arg2) -} - -// ImagePush mocks base method -func (m *MockAPIClient) ImagePush(arg0 context.Context, arg1 string, arg2 types.ImagePushOptions) (io.ReadCloser, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ImagePush", arg0, arg1, arg2) - ret0, _ := ret[0].(io.ReadCloser) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// ImagePush indicates an expected call of ImagePush -func (mr *MockAPIClientMockRecorder) ImagePush(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ImagePush", reflect.TypeOf((*MockAPIClient)(nil).ImagePush), arg0, arg1, arg2) -} - -// ImageRemove mocks base method -func (m *MockAPIClient) ImageRemove(arg0 context.Context, arg1 string, arg2 types.ImageRemoveOptions) ([]types.ImageDeleteResponseItem, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ImageRemove", arg0, arg1, arg2) - ret0, _ := ret[0].([]types.ImageDeleteResponseItem) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// ImageRemove indicates an expected call of ImageRemove -func (mr *MockAPIClientMockRecorder) ImageRemove(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ImageRemove", reflect.TypeOf((*MockAPIClient)(nil).ImageRemove), arg0, arg1, arg2) -} - -// ImageSave mocks base method -func (m *MockAPIClient) ImageSave(arg0 context.Context, arg1 []string) (io.ReadCloser, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ImageSave", arg0, arg1) - ret0, _ := ret[0].(io.ReadCloser) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// ImageSave indicates an expected call of ImageSave -func (mr *MockAPIClientMockRecorder) ImageSave(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ImageSave", reflect.TypeOf((*MockAPIClient)(nil).ImageSave), arg0, arg1) -} - -// ImageSearch mocks base method -func (m *MockAPIClient) ImageSearch(arg0 context.Context, arg1 string, arg2 types.ImageSearchOptions) ([]registry.SearchResult, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ImageSearch", arg0, arg1, arg2) - ret0, _ := ret[0].([]registry.SearchResult) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// ImageSearch indicates an expected call of ImageSearch -func (mr *MockAPIClientMockRecorder) ImageSearch(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ImageSearch", reflect.TypeOf((*MockAPIClient)(nil).ImageSearch), arg0, arg1, arg2) -} - -// ImageTag mocks base method -func (m *MockAPIClient) ImageTag(arg0 context.Context, arg1, arg2 string) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ImageTag", arg0, arg1, arg2) - ret0, _ := ret[0].(error) - return ret0 -} - -// ImageTag indicates an expected call of ImageTag -func (mr *MockAPIClientMockRecorder) ImageTag(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ImageTag", reflect.TypeOf((*MockAPIClient)(nil).ImageTag), arg0, arg1, arg2) -} - -// ImagesPrune mocks base method -func (m *MockAPIClient) ImagesPrune(arg0 context.Context, arg1 filters.Args) (types.ImagesPruneReport, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ImagesPrune", arg0, arg1) - ret0, _ := ret[0].(types.ImagesPruneReport) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// ImagesPrune indicates an expected call of ImagesPrune -func (mr *MockAPIClientMockRecorder) ImagesPrune(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ImagesPrune", reflect.TypeOf((*MockAPIClient)(nil).ImagesPrune), arg0, arg1) -} - -// Info mocks base method -func (m *MockAPIClient) Info(arg0 context.Context) (types.Info, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Info", arg0) - ret0, _ := ret[0].(types.Info) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Info indicates an expected call of Info -func (mr *MockAPIClientMockRecorder) Info(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Info", reflect.TypeOf((*MockAPIClient)(nil).Info), arg0) -} - -// NegotiateAPIVersion mocks base method -func (m *MockAPIClient) NegotiateAPIVersion(arg0 context.Context) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "NegotiateAPIVersion", arg0) -} - -// NegotiateAPIVersion indicates an expected call of NegotiateAPIVersion -func (mr *MockAPIClientMockRecorder) NegotiateAPIVersion(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NegotiateAPIVersion", reflect.TypeOf((*MockAPIClient)(nil).NegotiateAPIVersion), arg0) -} - -// NegotiateAPIVersionPing mocks base method -func (m *MockAPIClient) NegotiateAPIVersionPing(arg0 types.Ping) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "NegotiateAPIVersionPing", arg0) -} - -// NegotiateAPIVersionPing indicates an expected call of NegotiateAPIVersionPing -func (mr *MockAPIClientMockRecorder) NegotiateAPIVersionPing(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NegotiateAPIVersionPing", reflect.TypeOf((*MockAPIClient)(nil).NegotiateAPIVersionPing), arg0) -} - -// NetworkConnect mocks base method -func (m *MockAPIClient) NetworkConnect(arg0 context.Context, arg1, arg2 string, arg3 *network.EndpointSettings) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "NetworkConnect", arg0, arg1, arg2, arg3) - ret0, _ := ret[0].(error) - return ret0 -} - -// NetworkConnect indicates an expected call of NetworkConnect -func (mr *MockAPIClientMockRecorder) NetworkConnect(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NetworkConnect", reflect.TypeOf((*MockAPIClient)(nil).NetworkConnect), arg0, arg1, arg2, arg3) -} - -// NetworkCreate mocks base method -func (m *MockAPIClient) NetworkCreate(arg0 context.Context, arg1 string, arg2 types.NetworkCreate) (types.NetworkCreateResponse, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "NetworkCreate", arg0, arg1, arg2) - ret0, _ := ret[0].(types.NetworkCreateResponse) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// NetworkCreate indicates an expected call of NetworkCreate -func (mr *MockAPIClientMockRecorder) NetworkCreate(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NetworkCreate", reflect.TypeOf((*MockAPIClient)(nil).NetworkCreate), arg0, arg1, arg2) -} - -// NetworkDisconnect mocks base method -func (m *MockAPIClient) NetworkDisconnect(arg0 context.Context, arg1, arg2 string, arg3 bool) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "NetworkDisconnect", arg0, arg1, arg2, arg3) - ret0, _ := ret[0].(error) - return ret0 -} - -// NetworkDisconnect indicates an expected call of NetworkDisconnect -func (mr *MockAPIClientMockRecorder) NetworkDisconnect(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NetworkDisconnect", reflect.TypeOf((*MockAPIClient)(nil).NetworkDisconnect), arg0, arg1, arg2, arg3) -} - -// NetworkInspect mocks base method -func (m *MockAPIClient) NetworkInspect(arg0 context.Context, arg1 string, arg2 types.NetworkInspectOptions) (types.NetworkResource, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "NetworkInspect", arg0, arg1, arg2) - ret0, _ := ret[0].(types.NetworkResource) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// NetworkInspect indicates an expected call of NetworkInspect -func (mr *MockAPIClientMockRecorder) NetworkInspect(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NetworkInspect", reflect.TypeOf((*MockAPIClient)(nil).NetworkInspect), arg0, arg1, arg2) -} - -// NetworkInspectWithRaw mocks base method -func (m *MockAPIClient) NetworkInspectWithRaw(arg0 context.Context, arg1 string, arg2 types.NetworkInspectOptions) (types.NetworkResource, []byte, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "NetworkInspectWithRaw", arg0, arg1, arg2) - ret0, _ := ret[0].(types.NetworkResource) - ret1, _ := ret[1].([]byte) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 -} - -// NetworkInspectWithRaw indicates an expected call of NetworkInspectWithRaw -func (mr *MockAPIClientMockRecorder) NetworkInspectWithRaw(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NetworkInspectWithRaw", reflect.TypeOf((*MockAPIClient)(nil).NetworkInspectWithRaw), arg0, arg1, arg2) -} - -// NetworkList mocks base method -func (m *MockAPIClient) NetworkList(arg0 context.Context, arg1 types.NetworkListOptions) ([]types.NetworkResource, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "NetworkList", arg0, arg1) - ret0, _ := ret[0].([]types.NetworkResource) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// NetworkList indicates an expected call of NetworkList -func (mr *MockAPIClientMockRecorder) NetworkList(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NetworkList", reflect.TypeOf((*MockAPIClient)(nil).NetworkList), arg0, arg1) -} - -// NetworkRemove mocks base method -func (m *MockAPIClient) NetworkRemove(arg0 context.Context, arg1 string) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "NetworkRemove", arg0, arg1) - ret0, _ := ret[0].(error) - return ret0 -} - -// NetworkRemove indicates an expected call of NetworkRemove -func (mr *MockAPIClientMockRecorder) NetworkRemove(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NetworkRemove", reflect.TypeOf((*MockAPIClient)(nil).NetworkRemove), arg0, arg1) -} - -// NetworksPrune mocks base method -func (m *MockAPIClient) NetworksPrune(arg0 context.Context, arg1 filters.Args) (types.NetworksPruneReport, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "NetworksPrune", arg0, arg1) - ret0, _ := ret[0].(types.NetworksPruneReport) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// NetworksPrune indicates an expected call of NetworksPrune -func (mr *MockAPIClientMockRecorder) NetworksPrune(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NetworksPrune", reflect.TypeOf((*MockAPIClient)(nil).NetworksPrune), arg0, arg1) -} - -// NodeInspectWithRaw mocks base method -func (m *MockAPIClient) NodeInspectWithRaw(arg0 context.Context, arg1 string) (swarm.Node, []byte, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "NodeInspectWithRaw", arg0, arg1) - ret0, _ := ret[0].(swarm.Node) - ret1, _ := ret[1].([]byte) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 -} - -// NodeInspectWithRaw indicates an expected call of NodeInspectWithRaw -func (mr *MockAPIClientMockRecorder) NodeInspectWithRaw(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NodeInspectWithRaw", reflect.TypeOf((*MockAPIClient)(nil).NodeInspectWithRaw), arg0, arg1) -} - -// NodeList mocks base method -func (m *MockAPIClient) NodeList(arg0 context.Context, arg1 types.NodeListOptions) ([]swarm.Node, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "NodeList", arg0, arg1) - ret0, _ := ret[0].([]swarm.Node) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// NodeList indicates an expected call of NodeList -func (mr *MockAPIClientMockRecorder) NodeList(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NodeList", reflect.TypeOf((*MockAPIClient)(nil).NodeList), arg0, arg1) -} - -// NodeRemove mocks base method -func (m *MockAPIClient) NodeRemove(arg0 context.Context, arg1 string, arg2 types.NodeRemoveOptions) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "NodeRemove", arg0, arg1, arg2) - ret0, _ := ret[0].(error) - return ret0 -} - -// NodeRemove indicates an expected call of NodeRemove -func (mr *MockAPIClientMockRecorder) NodeRemove(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NodeRemove", reflect.TypeOf((*MockAPIClient)(nil).NodeRemove), arg0, arg1, arg2) -} - -// NodeUpdate mocks base method -func (m *MockAPIClient) NodeUpdate(arg0 context.Context, arg1 string, arg2 swarm.Version, arg3 swarm.NodeSpec) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "NodeUpdate", arg0, arg1, arg2, arg3) - ret0, _ := ret[0].(error) - return ret0 -} - -// NodeUpdate indicates an expected call of NodeUpdate -func (mr *MockAPIClientMockRecorder) NodeUpdate(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NodeUpdate", reflect.TypeOf((*MockAPIClient)(nil).NodeUpdate), arg0, arg1, arg2, arg3) -} - -// Ping mocks base method -func (m *MockAPIClient) Ping(arg0 context.Context) (types.Ping, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Ping", arg0) - ret0, _ := ret[0].(types.Ping) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Ping indicates an expected call of Ping -func (mr *MockAPIClientMockRecorder) Ping(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Ping", reflect.TypeOf((*MockAPIClient)(nil).Ping), arg0) -} - -// PluginCreate mocks base method -func (m *MockAPIClient) PluginCreate(arg0 context.Context, arg1 io.Reader, arg2 types.PluginCreateOptions) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "PluginCreate", arg0, arg1, arg2) - ret0, _ := ret[0].(error) - return ret0 -} - -// PluginCreate indicates an expected call of PluginCreate -func (mr *MockAPIClientMockRecorder) PluginCreate(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PluginCreate", reflect.TypeOf((*MockAPIClient)(nil).PluginCreate), arg0, arg1, arg2) -} - -// PluginDisable mocks base method -func (m *MockAPIClient) PluginDisable(arg0 context.Context, arg1 string, arg2 types.PluginDisableOptions) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "PluginDisable", arg0, arg1, arg2) - ret0, _ := ret[0].(error) - return ret0 -} - -// PluginDisable indicates an expected call of PluginDisable -func (mr *MockAPIClientMockRecorder) PluginDisable(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PluginDisable", reflect.TypeOf((*MockAPIClient)(nil).PluginDisable), arg0, arg1, arg2) -} - -// PluginEnable mocks base method -func (m *MockAPIClient) PluginEnable(arg0 context.Context, arg1 string, arg2 types.PluginEnableOptions) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "PluginEnable", arg0, arg1, arg2) - ret0, _ := ret[0].(error) - return ret0 -} - -// PluginEnable indicates an expected call of PluginEnable -func (mr *MockAPIClientMockRecorder) PluginEnable(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PluginEnable", reflect.TypeOf((*MockAPIClient)(nil).PluginEnable), arg0, arg1, arg2) -} - -// PluginInspectWithRaw mocks base method -func (m *MockAPIClient) PluginInspectWithRaw(arg0 context.Context, arg1 string) (*types.Plugin, []byte, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "PluginInspectWithRaw", arg0, arg1) - ret0, _ := ret[0].(*types.Plugin) - ret1, _ := ret[1].([]byte) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 -} - -// PluginInspectWithRaw indicates an expected call of PluginInspectWithRaw -func (mr *MockAPIClientMockRecorder) PluginInspectWithRaw(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PluginInspectWithRaw", reflect.TypeOf((*MockAPIClient)(nil).PluginInspectWithRaw), arg0, arg1) -} - -// PluginInstall mocks base method -func (m *MockAPIClient) PluginInstall(arg0 context.Context, arg1 string, arg2 types.PluginInstallOptions) (io.ReadCloser, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "PluginInstall", arg0, arg1, arg2) - ret0, _ := ret[0].(io.ReadCloser) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// PluginInstall indicates an expected call of PluginInstall -func (mr *MockAPIClientMockRecorder) PluginInstall(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PluginInstall", reflect.TypeOf((*MockAPIClient)(nil).PluginInstall), arg0, arg1, arg2) -} - -// PluginList mocks base method -func (m *MockAPIClient) PluginList(arg0 context.Context, arg1 filters.Args) (types.PluginsListResponse, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "PluginList", arg0, arg1) - ret0, _ := ret[0].(types.PluginsListResponse) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// PluginList indicates an expected call of PluginList -func (mr *MockAPIClientMockRecorder) PluginList(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PluginList", reflect.TypeOf((*MockAPIClient)(nil).PluginList), arg0, arg1) -} - -// PluginPush mocks base method -func (m *MockAPIClient) PluginPush(arg0 context.Context, arg1, arg2 string) (io.ReadCloser, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "PluginPush", arg0, arg1, arg2) - ret0, _ := ret[0].(io.ReadCloser) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// PluginPush indicates an expected call of PluginPush -func (mr *MockAPIClientMockRecorder) PluginPush(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PluginPush", reflect.TypeOf((*MockAPIClient)(nil).PluginPush), arg0, arg1, arg2) -} - -// PluginRemove mocks base method -func (m *MockAPIClient) PluginRemove(arg0 context.Context, arg1 string, arg2 types.PluginRemoveOptions) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "PluginRemove", arg0, arg1, arg2) - ret0, _ := ret[0].(error) - return ret0 -} - -// PluginRemove indicates an expected call of PluginRemove -func (mr *MockAPIClientMockRecorder) PluginRemove(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PluginRemove", reflect.TypeOf((*MockAPIClient)(nil).PluginRemove), arg0, arg1, arg2) -} - -// PluginSet mocks base method -func (m *MockAPIClient) PluginSet(arg0 context.Context, arg1 string, arg2 []string) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "PluginSet", arg0, arg1, arg2) - ret0, _ := ret[0].(error) - return ret0 -} - -// PluginSet indicates an expected call of PluginSet -func (mr *MockAPIClientMockRecorder) PluginSet(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PluginSet", reflect.TypeOf((*MockAPIClient)(nil).PluginSet), arg0, arg1, arg2) -} - -// PluginUpgrade mocks base method -func (m *MockAPIClient) PluginUpgrade(arg0 context.Context, arg1 string, arg2 types.PluginInstallOptions) (io.ReadCloser, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "PluginUpgrade", arg0, arg1, arg2) - ret0, _ := ret[0].(io.ReadCloser) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// PluginUpgrade indicates an expected call of PluginUpgrade -func (mr *MockAPIClientMockRecorder) PluginUpgrade(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PluginUpgrade", reflect.TypeOf((*MockAPIClient)(nil).PluginUpgrade), arg0, arg1, arg2) -} - -// RegistryLogin mocks base method -func (m *MockAPIClient) RegistryLogin(arg0 context.Context, arg1 types.AuthConfig) (registry.AuthenticateOKBody, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "RegistryLogin", arg0, arg1) - ret0, _ := ret[0].(registry.AuthenticateOKBody) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// RegistryLogin indicates an expected call of RegistryLogin -func (mr *MockAPIClientMockRecorder) RegistryLogin(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RegistryLogin", reflect.TypeOf((*MockAPIClient)(nil).RegistryLogin), arg0, arg1) -} - -// SecretCreate mocks base method -func (m *MockAPIClient) SecretCreate(arg0 context.Context, arg1 swarm.SecretSpec) (types.SecretCreateResponse, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SecretCreate", arg0, arg1) - ret0, _ := ret[0].(types.SecretCreateResponse) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// SecretCreate indicates an expected call of SecretCreate -func (mr *MockAPIClientMockRecorder) SecretCreate(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SecretCreate", reflect.TypeOf((*MockAPIClient)(nil).SecretCreate), arg0, arg1) -} - -// SecretInspectWithRaw mocks base method -func (m *MockAPIClient) SecretInspectWithRaw(arg0 context.Context, arg1 string) (swarm.Secret, []byte, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SecretInspectWithRaw", arg0, arg1) - ret0, _ := ret[0].(swarm.Secret) - ret1, _ := ret[1].([]byte) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 -} - -// SecretInspectWithRaw indicates an expected call of SecretInspectWithRaw -func (mr *MockAPIClientMockRecorder) SecretInspectWithRaw(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SecretInspectWithRaw", reflect.TypeOf((*MockAPIClient)(nil).SecretInspectWithRaw), arg0, arg1) -} - -// SecretList mocks base method -func (m *MockAPIClient) SecretList(arg0 context.Context, arg1 types.SecretListOptions) ([]swarm.Secret, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SecretList", arg0, arg1) - ret0, _ := ret[0].([]swarm.Secret) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// SecretList indicates an expected call of SecretList -func (mr *MockAPIClientMockRecorder) SecretList(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SecretList", reflect.TypeOf((*MockAPIClient)(nil).SecretList), arg0, arg1) -} - -// SecretRemove mocks base method -func (m *MockAPIClient) SecretRemove(arg0 context.Context, arg1 string) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SecretRemove", arg0, arg1) - ret0, _ := ret[0].(error) - return ret0 -} - -// SecretRemove indicates an expected call of SecretRemove -func (mr *MockAPIClientMockRecorder) SecretRemove(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SecretRemove", reflect.TypeOf((*MockAPIClient)(nil).SecretRemove), arg0, arg1) -} - -// SecretUpdate mocks base method -func (m *MockAPIClient) SecretUpdate(arg0 context.Context, arg1 string, arg2 swarm.Version, arg3 swarm.SecretSpec) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SecretUpdate", arg0, arg1, arg2, arg3) - ret0, _ := ret[0].(error) - return ret0 -} - -// SecretUpdate indicates an expected call of SecretUpdate -func (mr *MockAPIClientMockRecorder) SecretUpdate(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SecretUpdate", reflect.TypeOf((*MockAPIClient)(nil).SecretUpdate), arg0, arg1, arg2, arg3) -} - -// ServerVersion mocks base method -func (m *MockAPIClient) ServerVersion(arg0 context.Context) (types.Version, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ServerVersion", arg0) - ret0, _ := ret[0].(types.Version) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// ServerVersion indicates an expected call of ServerVersion -func (mr *MockAPIClientMockRecorder) ServerVersion(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ServerVersion", reflect.TypeOf((*MockAPIClient)(nil).ServerVersion), arg0) -} - -// ServiceCreate mocks base method -func (m *MockAPIClient) ServiceCreate(arg0 context.Context, arg1 swarm.ServiceSpec, arg2 types.ServiceCreateOptions) (types.ServiceCreateResponse, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ServiceCreate", arg0, arg1, arg2) - ret0, _ := ret[0].(types.ServiceCreateResponse) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// ServiceCreate indicates an expected call of ServiceCreate -func (mr *MockAPIClientMockRecorder) ServiceCreate(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ServiceCreate", reflect.TypeOf((*MockAPIClient)(nil).ServiceCreate), arg0, arg1, arg2) -} - -// ServiceInspectWithRaw mocks base method -func (m *MockAPIClient) ServiceInspectWithRaw(arg0 context.Context, arg1 string, arg2 types.ServiceInspectOptions) (swarm.Service, []byte, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ServiceInspectWithRaw", arg0, arg1, arg2) - ret0, _ := ret[0].(swarm.Service) - ret1, _ := ret[1].([]byte) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 -} - -// ServiceInspectWithRaw indicates an expected call of ServiceInspectWithRaw -func (mr *MockAPIClientMockRecorder) ServiceInspectWithRaw(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ServiceInspectWithRaw", reflect.TypeOf((*MockAPIClient)(nil).ServiceInspectWithRaw), arg0, arg1, arg2) -} - -// ServiceList mocks base method -func (m *MockAPIClient) ServiceList(arg0 context.Context, arg1 types.ServiceListOptions) ([]swarm.Service, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ServiceList", arg0, arg1) - ret0, _ := ret[0].([]swarm.Service) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// ServiceList indicates an expected call of ServiceList -func (mr *MockAPIClientMockRecorder) ServiceList(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ServiceList", reflect.TypeOf((*MockAPIClient)(nil).ServiceList), arg0, arg1) -} - -// ServiceLogs mocks base method -func (m *MockAPIClient) ServiceLogs(arg0 context.Context, arg1 string, arg2 types.ContainerLogsOptions) (io.ReadCloser, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ServiceLogs", arg0, arg1, arg2) - ret0, _ := ret[0].(io.ReadCloser) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// ServiceLogs indicates an expected call of ServiceLogs -func (mr *MockAPIClientMockRecorder) ServiceLogs(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ServiceLogs", reflect.TypeOf((*MockAPIClient)(nil).ServiceLogs), arg0, arg1, arg2) -} - -// ServiceRemove mocks base method -func (m *MockAPIClient) ServiceRemove(arg0 context.Context, arg1 string) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ServiceRemove", arg0, arg1) - ret0, _ := ret[0].(error) - return ret0 -} - -// ServiceRemove indicates an expected call of ServiceRemove -func (mr *MockAPIClientMockRecorder) ServiceRemove(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ServiceRemove", reflect.TypeOf((*MockAPIClient)(nil).ServiceRemove), arg0, arg1) -} - -// ServiceUpdate mocks base method -func (m *MockAPIClient) ServiceUpdate(arg0 context.Context, arg1 string, arg2 swarm.Version, arg3 swarm.ServiceSpec, arg4 types.ServiceUpdateOptions) (types.ServiceUpdateResponse, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ServiceUpdate", arg0, arg1, arg2, arg3, arg4) - ret0, _ := ret[0].(types.ServiceUpdateResponse) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// ServiceUpdate indicates an expected call of ServiceUpdate -func (mr *MockAPIClientMockRecorder) ServiceUpdate(arg0, arg1, arg2, arg3, arg4 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ServiceUpdate", reflect.TypeOf((*MockAPIClient)(nil).ServiceUpdate), arg0, arg1, arg2, arg3, arg4) -} - -// SwarmGetUnlockKey mocks base method -func (m *MockAPIClient) SwarmGetUnlockKey(arg0 context.Context) (types.SwarmUnlockKeyResponse, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SwarmGetUnlockKey", arg0) - ret0, _ := ret[0].(types.SwarmUnlockKeyResponse) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// SwarmGetUnlockKey indicates an expected call of SwarmGetUnlockKey -func (mr *MockAPIClientMockRecorder) SwarmGetUnlockKey(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SwarmGetUnlockKey", reflect.TypeOf((*MockAPIClient)(nil).SwarmGetUnlockKey), arg0) -} - -// SwarmInit mocks base method -func (m *MockAPIClient) SwarmInit(arg0 context.Context, arg1 swarm.InitRequest) (string, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SwarmInit", arg0, arg1) - ret0, _ := ret[0].(string) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// SwarmInit indicates an expected call of SwarmInit -func (mr *MockAPIClientMockRecorder) SwarmInit(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SwarmInit", reflect.TypeOf((*MockAPIClient)(nil).SwarmInit), arg0, arg1) -} - -// SwarmInspect mocks base method -func (m *MockAPIClient) SwarmInspect(arg0 context.Context) (swarm.Swarm, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SwarmInspect", arg0) - ret0, _ := ret[0].(swarm.Swarm) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// SwarmInspect indicates an expected call of SwarmInspect -func (mr *MockAPIClientMockRecorder) SwarmInspect(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SwarmInspect", reflect.TypeOf((*MockAPIClient)(nil).SwarmInspect), arg0) -} - -// SwarmJoin mocks base method -func (m *MockAPIClient) SwarmJoin(arg0 context.Context, arg1 swarm.JoinRequest) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SwarmJoin", arg0, arg1) - ret0, _ := ret[0].(error) - return ret0 -} - -// SwarmJoin indicates an expected call of SwarmJoin -func (mr *MockAPIClientMockRecorder) SwarmJoin(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SwarmJoin", reflect.TypeOf((*MockAPIClient)(nil).SwarmJoin), arg0, arg1) -} - -// SwarmLeave mocks base method -func (m *MockAPIClient) SwarmLeave(arg0 context.Context, arg1 bool) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SwarmLeave", arg0, arg1) - ret0, _ := ret[0].(error) - return ret0 -} - -// SwarmLeave indicates an expected call of SwarmLeave -func (mr *MockAPIClientMockRecorder) SwarmLeave(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SwarmLeave", reflect.TypeOf((*MockAPIClient)(nil).SwarmLeave), arg0, arg1) -} - -// SwarmUnlock mocks base method -func (m *MockAPIClient) SwarmUnlock(arg0 context.Context, arg1 swarm.UnlockRequest) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SwarmUnlock", arg0, arg1) - ret0, _ := ret[0].(error) - return ret0 -} - -// SwarmUnlock indicates an expected call of SwarmUnlock -func (mr *MockAPIClientMockRecorder) SwarmUnlock(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SwarmUnlock", reflect.TypeOf((*MockAPIClient)(nil).SwarmUnlock), arg0, arg1) -} - -// SwarmUpdate mocks base method -func (m *MockAPIClient) SwarmUpdate(arg0 context.Context, arg1 swarm.Version, arg2 swarm.Spec, arg3 swarm.UpdateFlags) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SwarmUpdate", arg0, arg1, arg2, arg3) - ret0, _ := ret[0].(error) - return ret0 -} - -// SwarmUpdate indicates an expected call of SwarmUpdate -func (mr *MockAPIClientMockRecorder) SwarmUpdate(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SwarmUpdate", reflect.TypeOf((*MockAPIClient)(nil).SwarmUpdate), arg0, arg1, arg2, arg3) -} - -// TaskInspectWithRaw mocks base method -func (m *MockAPIClient) TaskInspectWithRaw(arg0 context.Context, arg1 string) (swarm.Task, []byte, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "TaskInspectWithRaw", arg0, arg1) - ret0, _ := ret[0].(swarm.Task) - ret1, _ := ret[1].([]byte) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 -} - -// TaskInspectWithRaw indicates an expected call of TaskInspectWithRaw -func (mr *MockAPIClientMockRecorder) TaskInspectWithRaw(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TaskInspectWithRaw", reflect.TypeOf((*MockAPIClient)(nil).TaskInspectWithRaw), arg0, arg1) -} - -// TaskList mocks base method -func (m *MockAPIClient) TaskList(arg0 context.Context, arg1 types.TaskListOptions) ([]swarm.Task, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "TaskList", arg0, arg1) - ret0, _ := ret[0].([]swarm.Task) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// TaskList indicates an expected call of TaskList -func (mr *MockAPIClientMockRecorder) TaskList(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TaskList", reflect.TypeOf((*MockAPIClient)(nil).TaskList), arg0, arg1) -} - -// TaskLogs mocks base method -func (m *MockAPIClient) TaskLogs(arg0 context.Context, arg1 string, arg2 types.ContainerLogsOptions) (io.ReadCloser, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "TaskLogs", arg0, arg1, arg2) - ret0, _ := ret[0].(io.ReadCloser) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// TaskLogs indicates an expected call of TaskLogs -func (mr *MockAPIClientMockRecorder) TaskLogs(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TaskLogs", reflect.TypeOf((*MockAPIClient)(nil).TaskLogs), arg0, arg1, arg2) -} - -// VolumeCreate mocks base method -func (m *MockAPIClient) VolumeCreate(arg0 context.Context, arg1 volume.VolumeCreateBody) (types.Volume, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "VolumeCreate", arg0, arg1) - ret0, _ := ret[0].(types.Volume) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// VolumeCreate indicates an expected call of VolumeCreate -func (mr *MockAPIClientMockRecorder) VolumeCreate(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "VolumeCreate", reflect.TypeOf((*MockAPIClient)(nil).VolumeCreate), arg0, arg1) -} - -// VolumeInspect mocks base method -func (m *MockAPIClient) VolumeInspect(arg0 context.Context, arg1 string) (types.Volume, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "VolumeInspect", arg0, arg1) - ret0, _ := ret[0].(types.Volume) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// VolumeInspect indicates an expected call of VolumeInspect -func (mr *MockAPIClientMockRecorder) VolumeInspect(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "VolumeInspect", reflect.TypeOf((*MockAPIClient)(nil).VolumeInspect), arg0, arg1) -} - -// VolumeInspectWithRaw mocks base method -func (m *MockAPIClient) VolumeInspectWithRaw(arg0 context.Context, arg1 string) (types.Volume, []byte, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "VolumeInspectWithRaw", arg0, arg1) - ret0, _ := ret[0].(types.Volume) - ret1, _ := ret[1].([]byte) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 -} - -// VolumeInspectWithRaw indicates an expected call of VolumeInspectWithRaw -func (mr *MockAPIClientMockRecorder) VolumeInspectWithRaw(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "VolumeInspectWithRaw", reflect.TypeOf((*MockAPIClient)(nil).VolumeInspectWithRaw), arg0, arg1) -} - -// VolumeList mocks base method -func (m *MockAPIClient) VolumeList(arg0 context.Context, arg1 filters.Args) (volume.VolumeListOKBody, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "VolumeList", arg0, arg1) - ret0, _ := ret[0].(volume.VolumeListOKBody) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// VolumeList indicates an expected call of VolumeList -func (mr *MockAPIClientMockRecorder) VolumeList(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "VolumeList", reflect.TypeOf((*MockAPIClient)(nil).VolumeList), arg0, arg1) -} - -// VolumeRemove mocks base method -func (m *MockAPIClient) VolumeRemove(arg0 context.Context, arg1 string, arg2 bool) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "VolumeRemove", arg0, arg1, arg2) - ret0, _ := ret[0].(error) - return ret0 -} - -// VolumeRemove indicates an expected call of VolumeRemove -func (mr *MockAPIClientMockRecorder) VolumeRemove(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "VolumeRemove", reflect.TypeOf((*MockAPIClient)(nil).VolumeRemove), arg0, arg1, arg2) -} - -// VolumesPrune mocks base method -func (m *MockAPIClient) VolumesPrune(arg0 context.Context, arg1 filters.Args) (types.VolumesPruneReport, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "VolumesPrune", arg0, arg1) - ret0, _ := ret[0].(types.VolumesPruneReport) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// VolumesPrune indicates an expected call of VolumesPrune -func (mr *MockAPIClientMockRecorder) VolumesPrune(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "VolumesPrune", reflect.TypeOf((*MockAPIClient)(nil).VolumesPrune), arg0, arg1) -} diff --git a/pkg/progress/event.go b/pkg/progress/event.go deleted file mode 100644 index 979a0e53..00000000 --- a/pkg/progress/event.go +++ /dev/null @@ -1,133 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package progress - -import "time" - -// EventStatus indicates the status of an action -type EventStatus int - -const ( - // Working means that the current task is working - Working EventStatus = iota - // Done means that the current task is done - Done - // Error means that the current task has errored - Error -) - -// Event represents a progress event. -type Event struct { - ID string - ParentID string - Text string - Status EventStatus - StatusText string - - startTime time.Time - endTime time.Time - spinner *spinner -} - -// ErrorMessageEvent creates a new Error Event with message -func ErrorMessageEvent(ID string, msg string) Event { - return NewEvent(ID, Error, msg) -} - -// ErrorEvent creates a new Error Event -func ErrorEvent(ID string) Event { - return NewEvent(ID, Error, "Error") -} - -// CreatingEvent creates a new Create in progress Event -func CreatingEvent(ID string) Event { - return NewEvent(ID, Working, "Creating") -} - -// StartingEvent creates a new Starting in progress Event -func StartingEvent(ID string) Event { - return NewEvent(ID, Working, "Starting") -} - -// StartedEvent creates a new Started in progress Event -func StartedEvent(ID string) Event { - return NewEvent(ID, Done, "Started") -} - -// RestartingEvent creates a new Restarting in progress Event -func RestartingEvent(ID string) Event { - return NewEvent(ID, Working, "Restarting") -} - -// RestartedEvent creates a new Restarted in progress Event -func RestartedEvent(ID string) Event { - return NewEvent(ID, Done, "Restarted") -} - -// RunningEvent creates a new Running in progress Event -func RunningEvent(ID string) Event { - return NewEvent(ID, Done, "Running") -} - -// CreatedEvent creates a new Created (done) Event -func CreatedEvent(ID string) Event { - return NewEvent(ID, Done, "Created") -} - -// StoppingEvent creates a new Stopping in progress Event -func StoppingEvent(ID string) Event { - return NewEvent(ID, Working, "Stopping") -} - -// StoppedEvent creates a new Stopping in progress Event -func StoppedEvent(ID string) Event { - return NewEvent(ID, Done, "Stopped") -} - -// KillingEvent creates a new Killing in progress Event -func KillingEvent(ID string) Event { - return NewEvent(ID, Working, "Killing") -} - -// KilledEvent creates a new Killed in progress Event -func KilledEvent(ID string) Event { - return NewEvent(ID, Done, "Killed") -} - -// RemovingEvent creates a new Removing in progress Event -func RemovingEvent(ID string) Event { - return NewEvent(ID, Working, "Removing") -} - -// RemovedEvent creates a new removed (done) Event -func RemovedEvent(ID string) Event { - return NewEvent(ID, Done, "Removed") -} - -// NewEvent new event -func NewEvent(ID string, status EventStatus, statusText string) Event { - return Event{ - ID: ID, - Status: status, - StatusText: statusText, - } -} - -func (e *Event) stop() { - e.endTime = time.Now() - e.spinner.Stop() -} diff --git a/pkg/progress/noop.go b/pkg/progress/noop.go deleted file mode 100644 index 46ec1290..00000000 --- a/pkg/progress/noop.go +++ /dev/null @@ -1,37 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package progress - -import ( - "context" -) - -type noopWriter struct { -} - -func (p *noopWriter) Start(ctx context.Context) error { - return nil -} - -func (p *noopWriter) Event(e Event) { -} - -func (p *noopWriter) TailMsgf(_ string, _ ...interface{}) { -} - -func (p *noopWriter) Stop() { -} diff --git a/pkg/progress/plain.go b/pkg/progress/plain.go deleted file mode 100644 index 98e26abb..00000000 --- a/pkg/progress/plain.go +++ /dev/null @@ -1,49 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package progress - -import ( - "context" - "fmt" - "io" -) - -type plainWriter struct { - out io.Writer - done chan bool -} - -func (p *plainWriter) Start(ctx context.Context) error { - select { - case <-ctx.Done(): - return ctx.Err() - case <-p.done: - return nil - } -} - -func (p *plainWriter) Event(e Event) { - fmt.Fprintln(p.out, e.ID, e.Text, e.StatusText) -} - -func (p *plainWriter) TailMsgf(m string, args ...interface{}) { - fmt.Fprintln(p.out, append([]interface{}{m}, args...)...) -} - -func (p *plainWriter) Stop() { - p.done <- true -} diff --git a/pkg/progress/spinner.go b/pkg/progress/spinner.go deleted file mode 100644 index 3e757acf..00000000 --- a/pkg/progress/spinner.go +++ /dev/null @@ -1,66 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package progress - -import ( - "runtime" - "time" -) - -type spinner struct { - time time.Time - index int - chars []string - stop bool - done string -} - -func newSpinner() *spinner { - chars := []string{ - "⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏", - } - done := "⠿" - - if runtime.GOOS == "windows" { - chars = []string{"-"} - done = "-" - } - - return &spinner{ - index: 0, - time: time.Now(), - chars: chars, - done: done, - } -} - -func (s *spinner) String() string { - if s.stop { - return s.done - } - - d := time.Since(s.time) - if d.Milliseconds() > 100 { - s.index = (s.index + 1) % len(s.chars) - } - - return s.chars[s.index] -} - -func (s *spinner) Stop() { - s.stop = true -} diff --git a/pkg/progress/tty.go b/pkg/progress/tty.go deleted file mode 100644 index f0407254..00000000 --- a/pkg/progress/tty.go +++ /dev/null @@ -1,235 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package progress - -import ( - "context" - "fmt" - "io" - "runtime" - "strings" - "sync" - "time" - - "github.com/docker/compose-cli/pkg/utils" - - "github.com/buger/goterm" - "github.com/morikuni/aec" -) - -type ttyWriter struct { - out io.Writer - events map[string]Event - eventIDs []string - repeated bool - numLines int - done chan bool - mtx *sync.RWMutex - tailEvents []string -} - -func (w *ttyWriter) Start(ctx context.Context) error { - ticker := time.NewTicker(100 * time.Millisecond) - - for { - select { - case <-ctx.Done(): - w.print() - w.printTailEvents() - return ctx.Err() - case <-w.done: - w.print() - w.printTailEvents() - return nil - case <-ticker.C: - w.print() - } - } -} - -func (w *ttyWriter) Stop() { - w.done <- true -} - -func (w *ttyWriter) Event(e Event) { - w.mtx.Lock() - defer w.mtx.Unlock() - if !utils.StringContains(w.eventIDs, e.ID) { - w.eventIDs = append(w.eventIDs, e.ID) - } - if _, ok := w.events[e.ID]; ok { - last := w.events[e.ID] - switch e.Status { - case Done, Error: - if last.Status != e.Status { - last.stop() - } - } - last.Status = e.Status - last.Text = e.Text - last.StatusText = e.StatusText - last.ParentID = e.ParentID - w.events[e.ID] = last - } else { - e.startTime = time.Now() - e.spinner = newSpinner() - if e.Status == Done || e.Status == Error { - e.stop() - } - w.events[e.ID] = e - } -} - -func (w *ttyWriter) TailMsgf(msg string, args ...interface{}) { - w.mtx.Lock() - defer w.mtx.Unlock() - w.tailEvents = append(w.tailEvents, fmt.Sprintf(msg, args...)) -} - -func (w *ttyWriter) printTailEvents() { - w.mtx.Lock() - defer w.mtx.Unlock() - for _, msg := range w.tailEvents { - fmt.Fprintln(w.out, msg) - } -} - -func (w *ttyWriter) print() { - w.mtx.Lock() - defer w.mtx.Unlock() - if len(w.eventIDs) == 0 { - return - } - terminalWidth := goterm.Width() - b := aec.EmptyBuilder - for i := 0; i <= w.numLines; i++ { - b = b.Up(1) - } - if !w.repeated { - b = b.Down(1) - } - w.repeated = true - fmt.Fprint(w.out, b.Column(0).ANSI) - - // Hide the cursor while we are printing - fmt.Fprint(w.out, aec.Hide) - defer fmt.Fprint(w.out, aec.Show) - - firstLine := fmt.Sprintf("[+] Running %d/%d", numDone(w.events), w.numLines) - if w.numLines != 0 && numDone(w.events) == w.numLines { - firstLine = aec.Apply(firstLine, aec.BlueF) - } - fmt.Fprintln(w.out, firstLine) - - var statusPadding int - for _, v := range w.eventIDs { - event := w.events[v] - l := len(fmt.Sprintf("%s %s", event.ID, event.Text)) - if statusPadding < l { - statusPadding = l - } - if event.ParentID != "" { - statusPadding -= 2 - } - } - - numLines := 0 - for _, v := range w.eventIDs { - event := w.events[v] - if event.ParentID != "" { - continue - } - line := lineText(event, "", terminalWidth, statusPadding, runtime.GOOS != "windows") - // nolint: errcheck - fmt.Fprint(w.out, line) - numLines++ - for _, v := range w.eventIDs { - ev := w.events[v] - if ev.ParentID == event.ID { - line := lineText(ev, " ", terminalWidth, statusPadding, runtime.GOOS != "windows") - // nolint: errcheck - fmt.Fprint(w.out, line) - numLines++ - } - } - } - - w.numLines = numLines -} - -func lineText(event Event, pad string, terminalWidth, statusPadding int, color bool) string { - endTime := time.Now() - if event.Status != Working { - endTime = event.startTime - if (event.endTime != time.Time{}) { - endTime = event.endTime - } - } - - elapsed := endTime.Sub(event.startTime).Seconds() - - textLen := len(fmt.Sprintf("%s %s", event.ID, event.Text)) - padding := statusPadding - textLen - if padding < 0 { - padding = 0 - } - // calculate the max length for the status text, on errors it - // is 2-3 lines long and breaks the line formating - maxStatusLen := terminalWidth - textLen - statusPadding - 15 - status := event.StatusText - // in some cases (debugging under VS Code), terminalWidth is set to zero by goterm.Width() ; ensuring we don't tweak strings with negative char index - if maxStatusLen > 0 && len(status) > maxStatusLen { - status = status[:maxStatusLen] + "..." - } - text := fmt.Sprintf("%s %s %s %s%s %s", - pad, - event.spinner.String(), - event.ID, - event.Text, - strings.Repeat(" ", padding), - status, - ) - timer := fmt.Sprintf("%.1fs\n", elapsed) - o := align(text, timer, terminalWidth) - - if color { - color := aec.WhiteF - if event.Status == Done { - color = aec.BlueF - } - if event.Status == Error { - color = aec.RedF - } - return aec.Apply(o, color) - } - - return o -} - -func numDone(events map[string]Event) int { - i := 0 - for _, e := range events { - if e.Status == Done { - i++ - } - } - return i -} - -func align(l, r string, w int) string { - return fmt.Sprintf("%-[2]*[1]s %[3]s", l, w-len(r)-1, r) -} diff --git a/pkg/progress/tty_test.go b/pkg/progress/tty_test.go deleted file mode 100644 index d95af857..00000000 --- a/pkg/progress/tty_test.go +++ /dev/null @@ -1,105 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package progress - -import ( - "fmt" - "sync" - "testing" - "time" - - "gotest.tools/v3/assert" -) - -func TestLineText(t *testing.T) { - now := time.Now() - ev := Event{ - ID: "id", - Text: "Text", - Status: Working, - StatusText: "Status", - endTime: now, - startTime: now, - spinner: &spinner{ - chars: []string{"."}, - }, - } - - lineWidth := len(fmt.Sprintf("%s %s", ev.ID, ev.Text)) - - out := lineText(ev, "", 50, lineWidth, true) - assert.Equal(t, out, "\x1b[37m . id Text Status 0.0s\n\x1b[0m") - - out = lineText(ev, "", 50, lineWidth, false) - assert.Equal(t, out, " . id Text Status 0.0s\n") - - ev.Status = Done - out = lineText(ev, "", 50, lineWidth, true) - assert.Equal(t, out, "\x1b[34m . id Text Status 0.0s\n\x1b[0m") - - ev.Status = Error - out = lineText(ev, "", 50, lineWidth, true) - assert.Equal(t, out, "\x1b[31m . id Text Status 0.0s\n\x1b[0m") -} - -func TestLineTextSingleEvent(t *testing.T) { - now := time.Now() - ev := Event{ - ID: "id", - Text: "Text", - Status: Done, - StatusText: "Status", - startTime: now, - spinner: &spinner{ - chars: []string{"."}, - }, - } - - lineWidth := len(fmt.Sprintf("%s %s", ev.ID, ev.Text)) - - out := lineText(ev, "", 50, lineWidth, true) - assert.Equal(t, out, "\x1b[34m . id Text Status 0.0s\n\x1b[0m") -} - -func TestErrorEvent(t *testing.T) { - w := &ttyWriter{ - events: map[string]Event{}, - mtx: &sync.RWMutex{}, - } - e := Event{ - ID: "id", - Text: "Text", - Status: Working, - StatusText: "Working", - startTime: time.Now(), - spinner: &spinner{ - chars: []string{"."}, - }, - } - // Fire "Working" event and check end time isn't touched - w.Event(e) - event, ok := w.events[e.ID] - assert.Assert(t, ok) - assert.Assert(t, event.endTime.Equal(time.Time{})) - - // Fire "Error" event and check end time is set - e.Status = Error - w.Event(e) - event, ok = w.events[e.ID] - assert.Assert(t, ok) - assert.Assert(t, event.endTime.After(time.Now().Add(-10*time.Second))) -} diff --git a/pkg/progress/writer.go b/pkg/progress/writer.go deleted file mode 100644 index 172de52f..00000000 --- a/pkg/progress/writer.go +++ /dev/null @@ -1,116 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package progress - -import ( - "context" - "os" - "sync" - - "github.com/containerd/console" - "github.com/moby/term" - "golang.org/x/sync/errgroup" -) - -// Writer can write multiple progress events -type Writer interface { - Start(context.Context) error - Stop() - Event(Event) - TailMsgf(string, ...interface{}) -} - -type writerKey struct{} - -// WithContextWriter adds the writer to the context -func WithContextWriter(ctx context.Context, writer Writer) context.Context { - return context.WithValue(ctx, writerKey{}, writer) -} - -// ContextWriter returns the writer from the context -func ContextWriter(ctx context.Context) Writer { - s, ok := ctx.Value(writerKey{}).(Writer) - if !ok { - return &noopWriter{} - } - return s -} - -type progressFunc func(context.Context) error - -type progressFuncWithStatus func(context.Context) (string, error) - -// Run will run a writer and the progress function in parallel -func Run(ctx context.Context, pf progressFunc) error { - _, err := RunWithStatus(ctx, func(ctx context.Context) (string, error) { - return "", pf(ctx) - }) - return err -} - -// RunWithStatus will run a writer and the progress function in parallel and return a status -func RunWithStatus(ctx context.Context, pf progressFuncWithStatus) (string, error) { - eg, _ := errgroup.WithContext(ctx) - w, err := NewWriter(os.Stderr) - var result string - if err != nil { - return "", err - } - eg.Go(func() error { - return w.Start(context.Background()) - }) - - ctx = WithContextWriter(ctx, w) - - eg.Go(func() error { - defer w.Stop() - s, err := pf(ctx) - if err == nil { - result = s - } - return err - }) - - err = eg.Wait() - return result, err -} - -// NewWriter returns a new multi-progress writer -func NewWriter(out console.File) (Writer, error) { - _, isTerminal := term.GetFdInfo(out) - - if isTerminal { - con, err := console.ConsoleFromFile(out) - if err != nil { - return nil, err - } - - return &ttyWriter{ - out: con, - eventIDs: []string{}, - events: map[string]Event{}, - repeated: false, - done: make(chan bool), - mtx: &sync.RWMutex{}, - }, nil - } - - return &plainWriter{ - out: out, - done: make(chan bool), - }, nil -} diff --git a/pkg/progress/writer_test.go b/pkg/progress/writer_test.go deleted file mode 100644 index 2933811b..00000000 --- a/pkg/progress/writer_test.go +++ /dev/null @@ -1,31 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package progress - -import ( - "context" - "testing" - - "gotest.tools/v3/assert" -) - -func TestNoopWriter(t *testing.T) { - todo := context.TODO() - writer := ContextWriter(todo) - - assert.Equal(t, writer, &noopWriter{}) -} diff --git a/pkg/prompt/prompt.go b/pkg/prompt/prompt.go deleted file mode 100644 index fd4142cd..00000000 --- a/pkg/prompt/prompt.go +++ /dev/null @@ -1,78 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package prompt - -import ( - "github.com/AlecAivazis/survey/v2" -) - -//go:generate mockgen -destination=./prompt_mock.go -self_package "github.com/docker/compose-cli/pkg/prompt" -package=prompt . UI - -// UI - prompt user input -type UI interface { - Select(message string, options []string) (int, error) - Input(message string, defaultValue string) (string, error) - Confirm(message string, defaultValue bool) (bool, error) - Password(message string) (string, error) -} - -// User - aggregates prompt methods -type User struct{} - -// Select - displays a list -func (u User) Select(message string, options []string) (int, error) { - qs := &survey.Select{ - Message: message, - Options: options, - } - var selected int - err := survey.AskOne(qs, &selected, nil) - return selected, err -} - -// Input text with default value -func (u User) Input(message string, defaultValue string) (string, error) { - qs := &survey.Input{ - Message: message, - Default: defaultValue, - } - var s string - err := survey.AskOne(qs, &s, nil) - return s, err -} - -// Confirm asks for yes or no input -func (u User) Confirm(message string, defaultValue bool) (bool, error) { - qs := &survey.Confirm{ - Message: message, - Default: defaultValue, - } - var b bool - err := survey.AskOne(qs, &b, nil) - return b, err -} - -// Password implements a text input with masked characters. -func (u User) Password(message string) (string, error) { - qs := &survey.Password{ - Message: message, - } - var p string - err := survey.AskOne(qs, &p, nil) - return p, err - -} diff --git a/pkg/prompt/prompt_mock.go b/pkg/prompt/prompt_mock.go deleted file mode 100644 index 9acee1dd..00000000 --- a/pkg/prompt/prompt_mock.go +++ /dev/null @@ -1,94 +0,0 @@ -// Code generated by MockGen. DO NOT EDIT. -// Container: github.com/docker/compose-cli/pkg/prompt (interfaces: UI) - -// Package prompt is a generated GoMock package. -package prompt - -import ( - reflect "reflect" - - gomock "github.com/golang/mock/gomock" -) - -// MockUI is a mock of UI interface -type MockUI struct { - ctrl *gomock.Controller - recorder *MockUIMockRecorder -} - -// MockUIMockRecorder is the mock recorder for MockUI -type MockUIMockRecorder struct { - mock *MockUI -} - -// NewMockUI creates a new mock instance -func NewMockUI(ctrl *gomock.Controller) *MockUI { - mock := &MockUI{ctrl: ctrl} - mock.recorder = &MockUIMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use -func (m *MockUI) EXPECT() *MockUIMockRecorder { - return m.recorder -} - -// Confirm mocks base method -func (m *MockUI) Confirm(arg0 string, arg1 bool) (bool, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Confirm", arg0, arg1) - ret0, _ := ret[0].(bool) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Confirm indicates an expected call of Confirm -func (mr *MockUIMockRecorder) Confirm(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Confirm", reflect.TypeOf((*MockUI)(nil).Confirm), arg0, arg1) -} - -// Input mocks base method -func (m *MockUI) Input(arg0, arg1 string) (string, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Input", arg0, arg1) - ret0, _ := ret[0].(string) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Input indicates an expected call of Input -func (mr *MockUIMockRecorder) Input(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Input", reflect.TypeOf((*MockUI)(nil).Input), arg0, arg1) -} - -// Password mocks base method -func (m *MockUI) Password(arg0 string) (string, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Password", arg0) - ret0, _ := ret[0].(string) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Password indicates an expected call of Password -func (mr *MockUIMockRecorder) Password(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Password", reflect.TypeOf((*MockUI)(nil).Password), arg0) -} - -// Select mocks base method -func (m *MockUI) Select(arg0 string, arg1 []string) (int, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Select", arg0, arg1) - ret0, _ := ret[0].(int) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Select indicates an expected call of Select -func (mr *MockUIMockRecorder) Select(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Select", reflect.TypeOf((*MockUI)(nil).Select), arg0, arg1) -} diff --git a/pkg/utils/scan_suggest.go b/pkg/utils/scan_suggest.go deleted file mode 100644 index 93059f80..00000000 --- a/pkg/utils/scan_suggest.go +++ /dev/null @@ -1,87 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package utils - -import ( - "encoding/json" - "fmt" - "io/ioutil" - "os" - "path/filepath" - - pluginmanager "github.com/docker/cli/cli-plugins/manager" - "github.com/docker/cli/cli/command" - cliConfig "github.com/docker/cli/cli/config" -) - -// ScanSuggestMsg display a message after a successful build to suggest use of `docker scan` command -const ScanSuggestMsg = "Use 'docker scan' to run Snyk tests against images to find vulnerabilities and learn how to fix them" - -// DisplayScanSuggestMsg displlay a message suggesting users can scan new image -func DisplayScanSuggestMsg() { - if os.Getenv("DOCKER_SCAN_SUGGEST") == "false" { - return - } - if !scanAvailable() { - return - } - if scanAlreadyInvoked() { - return - } - fmt.Fprintf(os.Stderr, "\n"+ScanSuggestMsg+"\n") -} - -func scanAlreadyInvoked() bool { - filename := filepath.Join(cliConfig.Dir(), "scan", "config.json") - f, err := os.Stat(filename) - if os.IsNotExist(err) { - return false - } - if f.IsDir() { // should never happen, do not bother user with suggestion if something goes wrong - return true - } - type scanOptin struct { - Optin bool `json:"optin"` - } - data, err := ioutil.ReadFile(filename) - if err != nil { - return true - } - scanConfig := scanOptin{} - err = json.Unmarshal(data, &scanConfig) - if err != nil { - return true - } - return scanConfig.Optin -} - -func scanAvailable() bool { - cli, err := command.NewDockerCli() - if err != nil { - return false - } - plugins, err := pluginmanager.ListPlugins(cli, nil) - if err != nil { - return false - } - for _, plugin := range plugins { - if plugin.Name == "scan" { - return true - } - } - return false -} diff --git a/pkg/utils/stringutils.go b/pkg/utils/stringutils.go deleted file mode 100644 index fd2dd477..00000000 --- a/pkg/utils/stringutils.go +++ /dev/null @@ -1,27 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package utils - -// StringContains check if an array contains a specific value -func StringContains(array []string, needle string) bool { - for _, val := range array { - if val == needle { - return true - } - } - return false -} diff --git a/pkg/utils/writer.go b/pkg/utils/writer.go deleted file mode 100644 index 83f0bf5c..00000000 --- a/pkg/utils/writer.go +++ /dev/null @@ -1,62 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package utils - -import ( - "bytes" - "io" -) - -// GetWriter creates a io.Writer that will actually split by line and format by LogConsumer -func GetWriter(consumer func(string)) io.WriteCloser { - return &splitWriter{ - buffer: bytes.Buffer{}, - consumer: consumer, - } -} - -type splitWriter struct { - buffer bytes.Buffer - consumer func(string) -} - -// Write implements io.Writer. joins all input, splits on the separator and yields each chunk -func (s *splitWriter) Write(b []byte) (int, error) { - n, err := s.buffer.Write(b) - if err != nil { - return n, err - } - for { - b = s.buffer.Bytes() - index := bytes.Index(b, []byte{'\n'}) - if index < 0 { - break - } - line := s.buffer.Next(index + 1) - s.consumer(string(line[:len(line)-1])) - } - return n, nil -} - -func (s *splitWriter) Close() error { - b := s.buffer.Bytes() - if len(b) == 0 { - return nil - } - s.consumer(string(b)) - return nil -} diff --git a/pkg/utils/writer_test.go b/pkg/utils/writer_test.go deleted file mode 100644 index 7eedb844..00000000 --- a/pkg/utils/writer_test.go +++ /dev/null @@ -1,39 +0,0 @@ -/* - Copyright 2020 Docker Compose CLI authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package utils - -import ( - "testing" - - "gotest.tools/v3/assert" -) - -func TestSplitWriter(t *testing.T) { - var lines []string - w := GetWriter(func(line string) { - lines = append(lines, line) - }) - w.Write([]byte("h")) //nolint: errcheck - w.Write([]byte("e")) //nolint: errcheck - w.Write([]byte("l")) //nolint: errcheck - w.Write([]byte("l")) //nolint: errcheck - w.Write([]byte("o")) //nolint: errcheck - w.Write([]byte("\n")) //nolint: errcheck - w.Write([]byte("world!\n")) //nolint: errcheck - assert.DeepEqual(t, lines, []string{"hello", "world!"}) - -} diff --git a/utils/logs.go b/utils/logs.go index be052640..b7ab58a3 100644 --- a/utils/logs.go +++ b/utils/logs.go @@ -17,7 +17,7 @@ package utils import ( - "github.com/docker/compose-cli/pkg/api" + "github.com/docker/compose/v2/pkg/api" ) // FilteredLogConsumer filters logs for given services