mirror of
https://github.com/docker/compose.git
synced 2026-02-14 04:29:29 +08:00
Compare commits
203 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
143ac0f1b6 | ||
|
|
eb4249ece4 | ||
|
|
1e8241f67c | ||
|
|
388169011f | ||
|
|
053a560466 | ||
|
|
35e262e778 | ||
|
|
34ba0bc9dd | ||
|
|
56e38260ea | ||
|
|
83e020734d | ||
|
|
c28bf5227a | ||
|
|
326ee2ad69 | ||
|
|
51c113b623 | ||
|
|
194b8af242 | ||
|
|
0d895a23f8 | ||
|
|
7cdc7e15e3 | ||
|
|
fb6d92250c | ||
|
|
fb026543f2 | ||
|
|
d688d3bf22 | ||
|
|
898e1b605d | ||
|
|
f414bf7892 | ||
|
|
3c4593f2ad | ||
|
|
d2562029f6 | ||
|
|
26ed1051eb | ||
|
|
191c10b9d0 | ||
|
|
8b16ab1040 | ||
|
|
24d34040e3 | ||
|
|
dbe7819fd4 | ||
|
|
5d05df6e5c | ||
|
|
b621948c1b | ||
|
|
6f62bcccbb | ||
|
|
f5c53c2d07 | ||
|
|
1cfeda71eb | ||
|
|
e9c7a3a9d5 | ||
|
|
2bf2b22fbe | ||
|
|
8d81b87513 | ||
|
|
575f2ed7f2 | ||
|
|
5f4b22ed7c | ||
|
|
dcf6bd779f | ||
|
|
80823b77ef | ||
|
|
2c16e16db7 | ||
|
|
d1be9caf59 | ||
|
|
f659918743 | ||
|
|
c3c0cf3d94 | ||
|
|
eec822de8f | ||
|
|
6df3886b44 | ||
|
|
279874158c | ||
|
|
566d2207f3 | ||
|
|
d09c39dced | ||
|
|
a1d36379a3 | ||
|
|
0c55998b41 | ||
|
|
2f6ec9b777 | ||
|
|
06af729dc8 | ||
|
|
dcbf005fe4 | ||
|
|
e105f16527 | ||
|
|
ce5a0c656f | ||
|
|
f58f23a6a2 | ||
|
|
d82a1a5a03 | ||
|
|
9aa5232601 | ||
|
|
ccd83b8a74 | ||
|
|
9e57850c16 | ||
|
|
35d3a7ca56 | ||
|
|
6c998602bb | ||
|
|
402f368830 | ||
|
|
30dd3e66d7 | ||
|
|
0c4fa017b9 | ||
|
|
b12e23b0bd | ||
|
|
17da54da20 | ||
|
|
1baa4f4489 | ||
|
|
aefc2a111a | ||
|
|
7781b7c992 | ||
|
|
785835b1a2 | ||
|
|
e4fb5545f0 | ||
|
|
74cc091225 | ||
|
|
5e61c62ecf | ||
|
|
fb3868ffaf | ||
|
|
30e80d2440 | ||
|
|
bdbda79043 | ||
|
|
862f2a19f4 | ||
|
|
ae4fd7916c | ||
|
|
26aca867d8 | ||
|
|
138facea62 | ||
|
|
cda04f288e | ||
|
|
85a1aec123 | ||
|
|
9c29d2236d | ||
|
|
df6fe59f72 | ||
|
|
07df9cc46e | ||
|
|
7c8ff36d78 | ||
|
|
111ad3b039 | ||
|
|
8026d0e2f2 | ||
|
|
df1533a1ca | ||
|
|
8639fbae86 | ||
|
|
56e2ad9ee5 | ||
|
|
ce1ddb6c7e | ||
|
|
c5824702bf | ||
|
|
1f148244af | ||
|
|
9faef4aebb | ||
|
|
59f11ecbeb | ||
|
|
750553c866 | ||
|
|
8c964f5ad3 | ||
|
|
90ca13b747 | ||
|
|
f9946127ce | ||
|
|
ddda59a130 | ||
|
|
e981c35863 | ||
|
|
16c4241c0b | ||
|
|
f5e3ff03f0 | ||
|
|
9025d63a63 | ||
|
|
a1de0b96c3 | ||
|
|
ba24a656bf | ||
|
|
caa0cbbc4b | ||
|
|
8c0c5cb671 | ||
|
|
29e9fdba16 | ||
|
|
c665c53cc1 | ||
|
|
a1aceba655 | ||
|
|
a39cf75e86 | ||
|
|
5b0dcc5a44 | ||
|
|
46ba9c99ce | ||
|
|
1148b6765a | ||
|
|
5c5d30c674 | ||
|
|
2068eceee4 | ||
|
|
36fa8d4e71 | ||
|
|
69985b8b89 | ||
|
|
2384635ee1 | ||
|
|
587dba1167 | ||
|
|
2ba5e4c1d0 | ||
|
|
b1a26dac1d | ||
|
|
0ab21a2080 | ||
|
|
5e77ae9247 | ||
|
|
f557220140 | ||
|
|
8e1b32365e | ||
|
|
a1b7bee094 | ||
|
|
7cb1f8baf2 | ||
|
|
cb01186c2b | ||
|
|
efea084df6 | ||
|
|
9c4efbdd92 | ||
|
|
8ea7c9e0d2 | ||
|
|
c16943609c | ||
|
|
254a94b07d | ||
|
|
cf608fa954 | ||
|
|
426377a4c9 | ||
|
|
43c3f54598 | ||
|
|
493f6c8055 | ||
|
|
c1a9ffa07e | ||
|
|
646a8fc0e8 | ||
|
|
2945532f97 | ||
|
|
e5cd265abb | ||
|
|
d646d757a2 | ||
|
|
71237ef62b | ||
|
|
0d905a896d | ||
|
|
b847c7f5a4 | ||
|
|
5e3d8f671d | ||
|
|
6727908803 | ||
|
|
5661fd1bfe | ||
|
|
4cd61957ed | ||
|
|
0d4cbbdbc9 | ||
|
|
9631a49daa | ||
|
|
328ca3f239 | ||
|
|
e1bbfc6376 | ||
|
|
616bba0afd | ||
|
|
ee6e3c2a44 | ||
|
|
c7e31a3c15 | ||
|
|
704a9fd337 | ||
|
|
d9e0e42d95 | ||
|
|
c48e3c4a4f | ||
|
|
dd0803dba1 | ||
|
|
39008c539c | ||
|
|
6ab41d629f | ||
|
|
6c345b3755 | ||
|
|
b8773ad1c5 | ||
|
|
1ffa194e12 | ||
|
|
b92981015e | ||
|
|
af87f10650 | ||
|
|
5e1d3f2b41 | ||
|
|
3d0207ebc8 | ||
|
|
16a7c20960 | ||
|
|
818bc3c34a | ||
|
|
cf3e686d8c | ||
|
|
38bc6d5dbc | ||
|
|
4a2d4c44b2 | ||
|
|
0b6ce6ee42 | ||
|
|
034458dac7 | ||
|
|
ae16bbbf05 | ||
|
|
eb5f01baf4 | ||
|
|
d13ad1f997 | ||
|
|
9b4d577c65 | ||
|
|
b30978fb40 | ||
|
|
14b43c1a93 | ||
|
|
9dd081b92e | ||
|
|
2c0b023273 | ||
|
|
599e4b242a | ||
|
|
fe8c2780c8 | ||
|
|
a345515f91 | ||
|
|
8967df7a91 | ||
|
|
4f694919ff | ||
|
|
6ecab95775 | ||
|
|
12e0ac898a | ||
|
|
a6b7d78575 | ||
|
|
991901f2ef | ||
|
|
bd74a9260d | ||
|
|
2d971fc97d | ||
|
|
78f3361921 | ||
|
|
44d21280e7 | ||
|
|
cd743d17ba | ||
|
|
ff2ff18cdc |
8
.github/workflows/ci.yml
vendored
8
.github/workflows/ci.yml
vendored
@@ -19,7 +19,7 @@ on:
|
||||
default: "false"
|
||||
|
||||
env:
|
||||
DOCKER_CLI_VERSION: "20.10.17"
|
||||
DOCKER_CLI_VERSION: "25.0.0"
|
||||
|
||||
permissions:
|
||||
contents: read # to fetch code (actions/checkout)
|
||||
@@ -138,7 +138,6 @@ jobs:
|
||||
mode:
|
||||
- plugin
|
||||
- standalone
|
||||
- cucumber
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
@@ -198,11 +197,6 @@ jobs:
|
||||
rm -f /usr/local/bin/docker-compose
|
||||
cp bin/build/docker-compose /usr/local/bin
|
||||
make e2e-compose-standalone
|
||||
-
|
||||
name: Run cucumber tests
|
||||
if: ${{ matrix.mode == 'cucumber'}}
|
||||
run: |
|
||||
make test-cucumber
|
||||
|
||||
coverage:
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
58
.github/workflows/codeql.yml
vendored
Normal file
58
.github/workflows/codeql.yml
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
name: codeql
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'main'
|
||||
paths-ignore:
|
||||
- '**/*.md'
|
||||
- '**/*.txt'
|
||||
- '**/*.yaml'
|
||||
- '**/*_test.go'
|
||||
pull_request:
|
||||
branches:
|
||||
- 'main'
|
||||
paths-ignore:
|
||||
- '**/*.md'
|
||||
- '**/*.txt'
|
||||
- '**/*.yaml'
|
||||
- '**/*_test.go'
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
runs-on: 'ubuntu-latest'
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
security-events: write
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language:
|
||||
- go
|
||||
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
-
|
||||
name: Set up Go
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
check-latest: true
|
||||
-
|
||||
name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v2
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
-
|
||||
name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v2
|
||||
-
|
||||
name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v2
|
||||
with:
|
||||
category: "/language:${{matrix.language}}"
|
||||
42
.github/workflows/docs-upstream.yml
vendored
Normal file
42
.github/workflows/docs-upstream.yml
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
# this workflow runs the remote validate bake target from docker/docs
|
||||
# to check if yaml reference docs used in this repo are valid
|
||||
name: docs-upstream
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'main'
|
||||
- 'v[0-9]*'
|
||||
paths:
|
||||
- '.github/workflows/docs-upstream.yml'
|
||||
- 'docs/**'
|
||||
pull_request:
|
||||
paths:
|
||||
- '.github/workflows/docs-upstream.yml'
|
||||
- 'docs/**'
|
||||
|
||||
jobs:
|
||||
docs-yaml:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
-
|
||||
name: Upload reference YAML docs
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: docs-yaml
|
||||
path: docs/reference
|
||||
retention-days: 1
|
||||
|
||||
validate:
|
||||
uses: docker/docs/.github/workflows/validate-upstream.yml@main
|
||||
needs:
|
||||
- docs-yaml
|
||||
with:
|
||||
module-name: docker/compose
|
||||
56
.github/workflows/docs.yml
vendored
56
.github/workflows/docs.yml
vendored
@@ -1,56 +0,0 @@
|
||||
name: Docs
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
permissions: {}
|
||||
jobs:
|
||||
open-pr:
|
||||
permissions:
|
||||
contents: write # to create branch (peter-evans/create-pull-request)
|
||||
pull-requests: write # to create a PR (peter-evans/create-pull-request)
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
-
|
||||
name: Checkout docs repo
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
token: ${{ secrets.GHPAT_DOCS_DISPATCH }}
|
||||
repository: docker/docs
|
||||
ref: main
|
||||
-
|
||||
name: Prepare
|
||||
run: |
|
||||
rm -rf ./_data/compose-cli/*
|
||||
-
|
||||
name: Build
|
||||
uses: docker/build-push-action@v3
|
||||
with:
|
||||
context: ${{ github.server_url }}/${{ github.repository }}.git#${{ github.event.release.name }}
|
||||
target: docs-reference
|
||||
outputs: ./_data/compose-cli
|
||||
-
|
||||
name: Update compose_version in _config.yml
|
||||
run: |
|
||||
sed -i "s|^compose_version\:.*|compose_version\: \"${{ github.event.release.name }}\"|g" _config.yml
|
||||
cat _config.yml | yq .compose_version
|
||||
-
|
||||
name: Commit changes
|
||||
run: |
|
||||
git add -A .
|
||||
-
|
||||
name: Create PR on docs repo
|
||||
uses: peter-evans/create-pull-request@923ad837f191474af6b1721408744feb989a4c27 # v4.0.4
|
||||
with:
|
||||
token: ${{ secrets.GHPAT_DOCS_DISPATCH }}
|
||||
push-to-fork: docker-tools-robot/docker.github.io
|
||||
commit-message: Update Compose reference API to ${{ github.event.release.name }}
|
||||
signoff: true
|
||||
branch: dispatch/compose-api-reference-${{ github.event.release.name }}
|
||||
delete-branch: true
|
||||
title: Update Compose reference API to ${{ github.event.release.name }}
|
||||
body: |
|
||||
Update the Compose reference API documentation to keep in sync with the latest release `${{ github.event.release.name }}`
|
||||
draft: false
|
||||
@@ -7,6 +7,7 @@ linters:
|
||||
enable:
|
||||
- depguard
|
||||
- errcheck
|
||||
- errorlint
|
||||
- gocritic
|
||||
- gocyclo
|
||||
- gofmt
|
||||
@@ -40,6 +41,11 @@ linters-settings:
|
||||
desc: 'compose-go uses yaml.v3'
|
||||
gomodguard:
|
||||
blocked:
|
||||
modules:
|
||||
- github.com/pkg/errors:
|
||||
recommendations:
|
||||
- errors
|
||||
- fmt
|
||||
versions:
|
||||
- github.com/distribution/distribution:
|
||||
reason: "use distribution/reference"
|
||||
|
||||
@@ -66,6 +66,11 @@ When sending lengthy log files, consider posting them as a gist
|
||||
Don't forget to remove sensitive data from your log files before posting (you
|
||||
can replace those parts with "REDACTED").
|
||||
|
||||
_Note:_
|
||||
Maintainers might request additional information to diagnose an issue,
|
||||
if initial reporter doesn't answer within a reasonable delay (a few weeks),
|
||||
issue will be closed.
|
||||
|
||||
## Quick contribution tips and guidelines
|
||||
|
||||
This section gives the experienced contributor some tips and guidelines.
|
||||
|
||||
10
Dockerfile
10
Dockerfile
@@ -15,9 +15,9 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
ARG GO_VERSION=1.21.1
|
||||
ARG GO_VERSION=1.21.6
|
||||
ARG XX_VERSION=1.2.1
|
||||
ARG GOLANGCI_LINT_VERSION=v1.54.2
|
||||
ARG GOLANGCI_LINT_VERSION=v1.55.2
|
||||
ARG ADDLICENSE_VERSION=v1.0.0
|
||||
|
||||
ARG BUILD_TAGS="e2e"
|
||||
@@ -192,9 +192,3 @@ RUN --mount=from=binary \
|
||||
|
||||
FROM scratch AS release
|
||||
COPY --from=releaser /out/ /
|
||||
|
||||
# docs-reference is a target used as remote context to update docs on release
|
||||
# with latest changes on docs.docker.com.
|
||||
# see open-pr job in .github/workflows/docs.yml for more details
|
||||
FROM scratch AS docs-reference
|
||||
COPY docs/reference/*.yaml .
|
||||
|
||||
18
Makefile
18
Makefile
@@ -32,9 +32,13 @@ endif
|
||||
BUILD_FLAGS?=
|
||||
TEST_FLAGS?=
|
||||
E2E_TEST?=
|
||||
ifeq ($(E2E_TEST),)
|
||||
else
|
||||
TEST_FLAGS=-run $(E2E_TEST)
|
||||
ifneq ($(E2E_TEST),)
|
||||
TEST_FLAGS:=$(TEST_FLAGS) -run '$(E2E_TEST)'
|
||||
endif
|
||||
|
||||
EXCLUDE_E2E_TESTS?=
|
||||
ifneq ($(EXCLUDE_E2E_TESTS),)
|
||||
TEST_FLAGS:=$(TEST_FLAGS) -skip '$(EXCLUDE_E2E_TESTS)'
|
||||
endif
|
||||
|
||||
BUILDX_CMD ?= docker buildx
|
||||
@@ -71,16 +75,12 @@ install: binary
|
||||
|
||||
.PHONY: e2e-compose
|
||||
e2e-compose: ## Run end to end local tests in plugin mode. Set E2E_TEST=TestName to run a single test
|
||||
go test $(TEST_FLAGS) -count=1 ./pkg/e2e
|
||||
go test -v $(TEST_FLAGS) -count=1 ./pkg/e2e
|
||||
|
||||
.PHONY: e2e-compose-standalone
|
||||
e2e-compose-standalone: ## Run End to end local tests in standalone mode. Set E2E_TEST=TestName to run a single test
|
||||
go test $(TEST_FLAGS) -v -count=1 -parallel=1 --tags=standalone ./pkg/e2e
|
||||
|
||||
.PHONY: test-cucumber
|
||||
test-cucumber:
|
||||
go test $(TEST_FLAGS) -v -count=1 -parallel=1 ./e2e
|
||||
|
||||
.PHONY: build-and-e2e-compose
|
||||
build-and-e2e-compose: build e2e-compose ## Compile the compose cli-plugin and run end to end local tests in plugin mode. Set E2E_TEST=TestName to run a single test
|
||||
|
||||
@@ -89,7 +89,7 @@ build-and-e2e-compose-standalone: build e2e-compose-standalone ## Compile the co
|
||||
|
||||
.PHONY: mocks
|
||||
mocks:
|
||||
mockgen --version >/dev/null 2>&1 || go install github.com/golang/mock/mockgen@v1.6.0
|
||||
mockgen --version >/dev/null 2>&1 || go install go.uber.org/mock/mockgen@v0.3.0
|
||||
mockgen -destination pkg/mocks/mock_docker_cli.go -package mocks github.com/docker/cli/cli/command Cli
|
||||
mockgen -destination pkg/mocks/mock_docker_api.go -package mocks github.com/docker/docker/client APIClient
|
||||
mockgen -destination pkg/mocks/mock_docker_compose_api.go -package mocks -source=./pkg/api/api.go Service
|
||||
|
||||
@@ -52,7 +52,7 @@ Quick Start
|
||||
Using Docker Compose is a three-step process:
|
||||
1. Define your app's environment with a `Dockerfile` so it can be
|
||||
reproduced anywhere.
|
||||
2. Define the services that make up your app in `docker-compose.yml` so
|
||||
2. Define the services that make up your app in `compose.yaml` so
|
||||
they can be run together in an isolated environment.
|
||||
3. Lastly, run `docker compose up` and Compose will start and run your entire
|
||||
app.
|
||||
|
||||
@@ -31,7 +31,6 @@ func alphaCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service)
|
||||
},
|
||||
}
|
||||
cmd.AddCommand(
|
||||
watchCommand(p, dockerCli, backend),
|
||||
vizCommand(p, dockerCli, backend),
|
||||
publishCommand(p, dockerCli, backend),
|
||||
)
|
||||
|
||||
80
cmd/compose/attach.go
Normal file
80
cmd/compose/attach.go
Normal file
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
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/cli/cli/command"
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type attachOpts struct {
|
||||
*composeOptions
|
||||
|
||||
service string
|
||||
index int
|
||||
|
||||
detachKeys string
|
||||
noStdin bool
|
||||
proxy bool
|
||||
}
|
||||
|
||||
func attachCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
|
||||
opts := attachOpts{
|
||||
composeOptions: &composeOptions{
|
||||
ProjectOptions: p,
|
||||
},
|
||||
}
|
||||
runCmd := &cobra.Command{
|
||||
Use: "attach [OPTIONS] SERVICE",
|
||||
Short: "Attach local standard input, output, and error streams to a service's running container.",
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
PreRunE: Adapt(func(ctx context.Context, args []string) error {
|
||||
opts.service = args[0]
|
||||
return nil
|
||||
}),
|
||||
RunE: Adapt(func(ctx context.Context, args []string) error {
|
||||
return runAttach(ctx, dockerCli, backend, opts)
|
||||
}),
|
||||
ValidArgsFunction: completeServiceNames(dockerCli, p),
|
||||
}
|
||||
|
||||
runCmd.Flags().IntVar(&opts.index, "index", 0, "index of the container if service has multiple replicas.")
|
||||
runCmd.Flags().StringVarP(&opts.detachKeys, "detach-keys", "", "", "Override the key sequence for detaching from a container.")
|
||||
|
||||
runCmd.Flags().BoolVar(&opts.noStdin, "no-stdin", false, "Do not attach STDIN")
|
||||
runCmd.Flags().BoolVar(&opts.proxy, "sig-proxy", true, "Proxy all received signals to the process")
|
||||
return runCmd
|
||||
}
|
||||
|
||||
func runAttach(ctx context.Context, dockerCli command.Cli, backend api.Service, opts attachOpts) error {
|
||||
projectName, err := opts.toProjectName(dockerCli)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
attachOpts := api.AttachOptions{
|
||||
Service: opts.service,
|
||||
Index: opts.index,
|
||||
DetachKeys: opts.detachKeys,
|
||||
NoStdin: opts.noStdin,
|
||||
Proxy: opts.proxy,
|
||||
}
|
||||
return backend.Attach(ctx, projectName, attachOpts)
|
||||
}
|
||||
@@ -22,13 +22,12 @@ import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/compose-spec/compose-go/cli"
|
||||
"github.com/compose-spec/compose-go/loader"
|
||||
"github.com/compose-spec/compose-go/types"
|
||||
buildx "github.com/docker/buildx/util/progress"
|
||||
"github.com/compose-spec/compose-go/v2/cli"
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
"github.com/docker/cli/cli/command"
|
||||
cliopts "github.com/docker/cli/opts"
|
||||
ui "github.com/docker/compose/v2/pkg/progress"
|
||||
buildkit "github.com/moby/buildkit/util/progress/progressui"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
@@ -44,13 +43,21 @@ type buildOptions struct {
|
||||
memory cliopts.MemBytes
|
||||
ssh string
|
||||
builder string
|
||||
deps bool
|
||||
}
|
||||
|
||||
func (opts buildOptions) toAPIBuildOptions(services []string) (api.BuildOptions, error) {
|
||||
var SSHKeys []types.SSHKey
|
||||
var err error
|
||||
if opts.ssh != "" {
|
||||
SSHKeys, err = loader.ParseShortSSHSyntax(opts.ssh)
|
||||
id, path, found := strings.Cut(opts.ssh, "=")
|
||||
if !found && id != "default" {
|
||||
return api.BuildOptions{}, fmt.Errorf("invalid ssh key %q", opts.ssh)
|
||||
}
|
||||
SSHKeys = append(SSHKeys, types.SSHKey{
|
||||
ID: id,
|
||||
Path: path,
|
||||
})
|
||||
if err != nil {
|
||||
return api.BuildOptions{}, err
|
||||
}
|
||||
@@ -68,6 +75,7 @@ func (opts buildOptions) toAPIBuildOptions(services []string) (api.BuildOptions,
|
||||
NoCache: opts.noCache,
|
||||
Quiet: opts.quiet,
|
||||
Services: services,
|
||||
Deps: opts.deps,
|
||||
SSHs: SSHKeys,
|
||||
Builder: builderName,
|
||||
}, nil
|
||||
@@ -96,36 +104,39 @@ func buildCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service)
|
||||
opts.ssh = "default"
|
||||
}
|
||||
if cmd.Flags().Changed("progress") && opts.ssh == "" {
|
||||
fmt.Fprint(os.Stderr, "--progress is a global compose flag, better use `docker compose --progress xx build ...")
|
||||
fmt.Fprint(os.Stderr, "--progress is a global compose flag, better use `docker compose --progress xx build ...\n")
|
||||
}
|
||||
return runBuild(ctx, dockerCli, backend, opts, args)
|
||||
}),
|
||||
ValidArgsFunction: completeServiceNames(dockerCli, p),
|
||||
}
|
||||
cmd.Flags().BoolVar(&opts.push, "push", false, "Push service images.")
|
||||
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().StringArrayVar(&opts.args, "build-arg", []string{}, "Set build-time variables for services.")
|
||||
cmd.Flags().StringVar(&opts.ssh, "ssh", "", "Set SSH authentications used when building service images. (use 'default' for using your default SSH Agent)")
|
||||
cmd.Flags().StringVar(&opts.builder, "builder", "", "Set builder to use.")
|
||||
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().VarP(&opts.memory, "memory", "m", "Set memory limit for the build container. Not supported by BuildKit.")
|
||||
cmd.Flags().StringVar(&p.Progress, "progress", buildx.PrinterModeAuto, fmt.Sprintf(`Set type of ui output (%s)`, strings.Join(printerModes, ", ")))
|
||||
cmd.Flags().MarkHidden("progress") //nolint:errcheck
|
||||
flags := cmd.Flags()
|
||||
flags.BoolVar(&opts.push, "push", false, "Push service images.")
|
||||
flags.BoolVarP(&opts.quiet, "quiet", "q", false, "Don't print anything to STDOUT")
|
||||
flags.BoolVar(&opts.pull, "pull", false, "Always attempt to pull a newer version of the image.")
|
||||
flags.StringArrayVar(&opts.args, "build-arg", []string{}, "Set build-time variables for services.")
|
||||
flags.StringVar(&opts.ssh, "ssh", "", "Set SSH authentications used when building service images. (use 'default' for using your default SSH Agent)")
|
||||
flags.StringVar(&opts.builder, "builder", "", "Set builder to use.")
|
||||
flags.BoolVar(&opts.deps, "with-dependencies", false, "Also build dependencies (transitively).")
|
||||
|
||||
flags.Bool("parallel", true, "Build images in parallel. DEPRECATED")
|
||||
flags.MarkHidden("parallel") //nolint:errcheck
|
||||
flags.Bool("compress", true, "Compress the build context using gzip. DEPRECATED")
|
||||
flags.MarkHidden("compress") //nolint:errcheck
|
||||
flags.Bool("force-rm", true, "Always remove intermediate containers. DEPRECATED")
|
||||
flags.MarkHidden("force-rm") //nolint:errcheck
|
||||
flags.BoolVar(&opts.noCache, "no-cache", false, "Do not use cache when building the image")
|
||||
flags.Bool("no-rm", false, "Do not remove intermediate containers after a successful build. DEPRECATED")
|
||||
flags.MarkHidden("no-rm") //nolint:errcheck
|
||||
flags.VarP(&opts.memory, "memory", "m", "Set memory limit for the build container. Not supported by BuildKit.")
|
||||
flags.StringVar(&p.Progress, "progress", string(buildkit.AutoMode), fmt.Sprintf(`Set type of ui output (%s)`, strings.Join(printerModes, ", ")))
|
||||
flags.MarkHidden("progress") //nolint:errcheck
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runBuild(ctx context.Context, dockerCli command.Cli, backend api.Service, opts buildOptions, services []string) error {
|
||||
project, err := opts.ToProject(dockerCli, services, cli.WithResolvedPaths(true))
|
||||
project, err := opts.ToProject(dockerCli, services, cli.WithResolvedPaths(true), cli.WithoutEnvironmentResolution)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -41,8 +41,8 @@ func completeServiceNames(dockerCli command.Cli, p *ProjectOptions) validArgsFn
|
||||
if err != nil {
|
||||
return nil, cobra.ShellCompDirectiveNoFileComp
|
||||
}
|
||||
var serviceNames []string
|
||||
for _, s := range project.ServiceNames() {
|
||||
serviceNames := append(project.ServiceNames(), project.DisabledServiceNames()...)
|
||||
for _, s := range serviceNames {
|
||||
if toComplete == "" || strings.HasPrefix(s, toComplete) {
|
||||
serviceNames = append(serviceNames, s)
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ package compose
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/signal"
|
||||
@@ -26,18 +27,17 @@ import (
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"github.com/compose-spec/compose-go/cli"
|
||||
"github.com/compose-spec/compose-go/dotenv"
|
||||
"github.com/compose-spec/compose-go/types"
|
||||
composegoutils "github.com/compose-spec/compose-go/utils"
|
||||
"github.com/compose-spec/compose-go/v2/cli"
|
||||
"github.com/compose-spec/compose-go/v2/dotenv"
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
composegoutils "github.com/compose-spec/compose-go/v2/utils"
|
||||
"github.com/docker/buildx/util/logutil"
|
||||
buildx "github.com/docker/buildx/util/progress"
|
||||
dockercli "github.com/docker/cli/cli"
|
||||
"github.com/docker/cli/cli-plugins/manager"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/compose/v2/pkg/remote"
|
||||
buildkit "github.com/moby/buildkit/util/progress/progressui"
|
||||
"github.com/morikuni/aec"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
@@ -60,6 +60,8 @@ const (
|
||||
ComposeRemoveOrphans = "COMPOSE_REMOVE_ORPHANS"
|
||||
// ComposeIgnoreOrphans ignore "orphaned" containers
|
||||
ComposeIgnoreOrphans = "COMPOSE_IGNORE_ORPHANS"
|
||||
// ComposeEnvFiles defines the env files to use if --env-file isn't used
|
||||
ComposeEnvFiles = "COMPOSE_ENV_FILES"
|
||||
)
|
||||
|
||||
// Command defines a compose CLI command as a func with args
|
||||
@@ -71,18 +73,17 @@ type CobraCommand func(context.Context, *cobra.Command, []string) error
|
||||
// AdaptCmd adapt a CobraCommand func to cobra library
|
||||
func AdaptCmd(fn CobraCommand) 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()
|
||||
}()
|
||||
}
|
||||
ctx, cancel := context.WithCancel(cmd.Context())
|
||||
|
||||
s := make(chan os.Signal, 1)
|
||||
signal.Notify(s, syscall.SIGTERM, syscall.SIGINT)
|
||||
go func() {
|
||||
<-s
|
||||
cancel()
|
||||
signal.Stop(s)
|
||||
close(s)
|
||||
}()
|
||||
|
||||
err := fn(ctx, cmd, args)
|
||||
var composeErr compose.Error
|
||||
if api.IsErrCanceled(err) || errors.Is(ctx.Err(), context.Canceled) {
|
||||
@@ -159,7 +160,7 @@ func (o *ProjectOptions) addProjectFlags(f *pflag.FlagSet) {
|
||||
f.StringVar(&o.ProjectDir, "project-directory", "", "Specify an alternate working directory\n(default: the path of the, first specified, Compose file)")
|
||||
f.StringVar(&o.WorkDir, "workdir", "", "DEPRECATED! USE --project-directory INSTEAD.\nSpecify an alternate working directory\n(default: the path of the, first specified, Compose file)")
|
||||
f.BoolVar(&o.Compatibility, "compatibility", false, "Run compose in backward compatibility mode")
|
||||
f.StringVar(&o.Progress, "progress", buildx.PrinterModeAuto, fmt.Sprintf(`Set type of progress output (%s)`, strings.Join(printerModes, ", ")))
|
||||
f.StringVar(&o.Progress, "progress", string(buildkit.AutoMode), fmt.Sprintf(`Set type of progress output (%s)`, strings.Join(printerModes, ", ")))
|
||||
_ = f.MarkHidden("workdir")
|
||||
}
|
||||
|
||||
@@ -200,11 +201,7 @@ func (o *ProjectOptions) toProjectName(dockerCli command.Cli) (string, error) {
|
||||
|
||||
func (o *ProjectOptions) ToProject(dockerCli command.Cli, services []string, po ...cli.ProjectOptionsFn) (*types.Project, error) {
|
||||
if !o.Offline {
|
||||
var err error
|
||||
po, err = o.configureRemoteLoaders(dockerCli, po)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
po = o.configureRemoteLoaders(dockerCli, po)
|
||||
}
|
||||
|
||||
options, err := o.toProjectOptions(po...)
|
||||
@@ -225,15 +222,15 @@ func (o *ProjectOptions) ToProject(dockerCli command.Cli, services []string, po
|
||||
return nil, errors.New("project name can't be empty. Use `--project-name` to set a valid name")
|
||||
}
|
||||
|
||||
err = project.EnableServices(services...)
|
||||
project, err = project.WithServicesEnabled(services...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for i, s := range project.Services {
|
||||
for name, s := range project.Services {
|
||||
s.CustomLabels = map[string]string{
|
||||
api.ProjectLabel: project.Name,
|
||||
api.ServiceLabel: s.Name,
|
||||
api.ServiceLabel: name,
|
||||
api.VersionLabel: api.ComposeVersion,
|
||||
api.WorkingDirLabel: project.WorkingDir,
|
||||
api.ConfigFilesLabel: strings.Join(project.ComposeFiles, ","),
|
||||
@@ -242,40 +239,21 @@ func (o *ProjectOptions) ToProject(dockerCli command.Cli, services []string, po
|
||||
if len(o.EnvFiles) != 0 {
|
||||
s.CustomLabels[api.EnvironmentFileLabel] = strings.Join(o.EnvFiles, ",")
|
||||
}
|
||||
project.Services[i] = s
|
||||
project.Services[name] = s
|
||||
}
|
||||
|
||||
project.WithoutUnnecessaryResources()
|
||||
project = project.WithoutUnnecessaryResources()
|
||||
|
||||
err = project.ForServices(services)
|
||||
project, err = project.WithSelectedServices(services)
|
||||
return project, err
|
||||
}
|
||||
|
||||
func (o *ProjectOptions) configureRemoteLoaders(dockerCli command.Cli, po []cli.ProjectOptionsFn) ([]cli.ProjectOptionsFn, error) {
|
||||
enabled, err := remote.GitRemoteLoaderEnabled()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if enabled {
|
||||
git, err := remote.NewGitRemoteLoader(o.Offline)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
po = append(po, cli.WithResourceLoader(git))
|
||||
}
|
||||
func (o *ProjectOptions) configureRemoteLoaders(dockerCli command.Cli, po []cli.ProjectOptionsFn) []cli.ProjectOptionsFn {
|
||||
git := remote.NewGitRemoteLoader(o.Offline)
|
||||
oci := remote.NewOCIRemoteLoader(dockerCli, o.Offline)
|
||||
|
||||
enabled, err = remote.OCIRemoteLoaderEnabled()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if enabled {
|
||||
git, err := remote.NewOCIRemoteLoader(dockerCli, o.Offline)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
po = append(po, cli.WithResourceLoader(git))
|
||||
}
|
||||
return po, nil
|
||||
po = append(po, cli.WithResourceLoader(git), cli.WithResourceLoader(oci))
|
||||
return po
|
||||
}
|
||||
|
||||
func (o *ProjectOptions) toProjectOptions(po ...cli.ProjectOptionsFn) (*cli.ProjectOptions, error) {
|
||||
@@ -463,6 +441,7 @@ func RootCommand(dockerCli command.Cli, backend api.Service) *cobra.Command { //
|
||||
runCommand(&opts, dockerCli, backend),
|
||||
removeCommand(&opts, dockerCli, backend),
|
||||
execCommand(&opts, dockerCli, backend),
|
||||
attachCommand(&opts, dockerCli, backend),
|
||||
pauseCommand(&opts, dockerCli, backend),
|
||||
unpauseCommand(&opts, dockerCli, backend),
|
||||
topCommand(&opts, dockerCli, backend),
|
||||
@@ -477,6 +456,7 @@ func RootCommand(dockerCli command.Cli, backend api.Service) *cobra.Command { //
|
||||
copyCommand(&opts, dockerCli, backend),
|
||||
waitCommand(&opts, dockerCli, backend),
|
||||
scaleCommand(&opts, dockerCli, backend),
|
||||
statsCommand(&opts, dockerCli),
|
||||
watchCommand(&opts, dockerCli, backend),
|
||||
alphaCommand(&opts, dockerCli, backend),
|
||||
)
|
||||
@@ -517,16 +497,17 @@ func RootCommand(dockerCli command.Cli, backend api.Service) *cobra.Command { //
|
||||
}
|
||||
|
||||
func setEnvWithDotEnv(prjOpts *ProjectOptions) error {
|
||||
if len(prjOpts.EnvFiles) == 0 {
|
||||
if envFiles := os.Getenv(ComposeEnvFiles); envFiles != "" {
|
||||
prjOpts.EnvFiles = strings.Split(envFiles, ",")
|
||||
}
|
||||
}
|
||||
options, err := prjOpts.toProjectOptions()
|
||||
if err != nil {
|
||||
return compose.WrapComposeError(err)
|
||||
}
|
||||
workingDir, err := options.GetWorkingDir()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
envFromFile, err := dotenv.GetEnvFromFile(composegoutils.GetAsEqualsMap(os.Environ()), workingDir, options.EnvFiles)
|
||||
envFromFile, err := dotenv.GetEnvFromFile(composegoutils.GetAsEqualsMap(os.Environ()), options.EnvFiles)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -19,32 +19,32 @@ package compose
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/compose-spec/compose-go/types"
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
"gotest.tools/v3/assert"
|
||||
)
|
||||
|
||||
func TestFilterServices(t *testing.T) {
|
||||
p := &types.Project{
|
||||
Services: []types.ServiceConfig{
|
||||
{
|
||||
Services: types.Services{
|
||||
"foo": {
|
||||
Name: "foo",
|
||||
Links: []string{"bar"},
|
||||
},
|
||||
{
|
||||
"bar": {
|
||||
Name: "bar",
|
||||
DependsOn: map[string]types.ServiceDependency{
|
||||
"zot": {},
|
||||
},
|
||||
},
|
||||
{
|
||||
"zot": {
|
||||
Name: "zot",
|
||||
},
|
||||
{
|
||||
"qix": {
|
||||
Name: "qix",
|
||||
},
|
||||
},
|
||||
}
|
||||
err := p.ForServices([]string{"bar"})
|
||||
p, err := p.WithSelectedServices([]string{"bar"})
|
||||
assert.NilError(t, err)
|
||||
|
||||
assert.Equal(t, len(p.Services), 2)
|
||||
|
||||
@@ -24,8 +24,8 @@ import (
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/compose-spec/compose-go/cli"
|
||||
"github.com/compose-spec/compose-go/types"
|
||||
"github.com/compose-spec/compose-go/v2/cli"
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
@@ -159,10 +159,11 @@ func runServices(ctx context.Context, dockerCli command.Cli, opts configOptions)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return project.WithServices(project.ServiceNames(), func(s types.ServiceConfig) error {
|
||||
fmt.Fprintln(dockerCli.Out(), s.Name)
|
||||
err = project.ForEachService(project.ServiceNames(), func(serviceName string, _ *types.ServiceConfig) error {
|
||||
fmt.Fprintln(dockerCli.Out(), serviceName)
|
||||
return nil
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func runVolumes(ctx context.Context, dockerCli command.Cli, opts configOptions) error {
|
||||
@@ -186,24 +187,31 @@ func runHash(ctx context.Context, dockerCli command.Cli, opts configOptions) err
|
||||
return err
|
||||
}
|
||||
|
||||
if len(services) > 0 {
|
||||
err = project.ForServices(services, types.IgnoreDependencies)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := applyPlatforms(project, true); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sorted := project.Services
|
||||
if len(services) == 0 {
|
||||
services = project.ServiceNames()
|
||||
}
|
||||
|
||||
sorted := services
|
||||
sort.Slice(sorted, func(i, j int) bool {
|
||||
return sorted[i].Name < sorted[j].Name
|
||||
return sorted[i] < sorted[j]
|
||||
})
|
||||
|
||||
for _, s := range sorted {
|
||||
hash, err := compose.ServiceHash(s)
|
||||
for _, name := range sorted {
|
||||
s, err := project.GetService(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprintf(dockerCli.Out(), "%s %s\n", s.Name, hash)
|
||||
|
||||
hash, err := compose.ServiceHash(s)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprintf(dockerCli.Out(), "%s %s\n", name, hash)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -19,11 +19,12 @@ package compose
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/compose-spec/compose-go/types"
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
@@ -73,7 +74,7 @@ func createCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service
|
||||
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 policy.")
|
||||
flags.StringVar(&opts.Pull, "pull", "policy", `Pull image before running ("always"|"policy"|"never")`)
|
||||
flags.StringVar(&opts.Pull, "pull", "policy", `Pull image before running ("always"|"missing"|"never"|"build")`)
|
||||
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.")
|
||||
flags.BoolVar(&opts.removeOrphans, "remove-orphans", false, "Remove containers for services not defined in the Compose file.")
|
||||
@@ -138,6 +139,9 @@ func (opts createOptions) GetTimeout() *time.Duration {
|
||||
|
||||
func (opts createOptions) Apply(project *types.Project) error {
|
||||
if opts.pullChanged {
|
||||
if !opts.isPullPolicyValid() {
|
||||
return fmt.Errorf("invalid --pull option %q", opts.Pull)
|
||||
}
|
||||
for i, service := range project.Services {
|
||||
service.PullPolicy = opts.Pull
|
||||
project.Services[i] = service
|
||||
@@ -155,22 +159,20 @@ func (opts createOptions) Apply(project *types.Project) error {
|
||||
project.Services[i] = service
|
||||
}
|
||||
}
|
||||
// opts.noBuild, however, means do not perform ANY builds
|
||||
if opts.noBuild {
|
||||
for i, service := range project.Services {
|
||||
service.Build = nil
|
||||
if service.Image == "" {
|
||||
service.Image = api.GetImageNameOrDefault(service, project.Name)
|
||||
}
|
||||
project.Services[i] = service
|
||||
}
|
||||
}
|
||||
|
||||
if err := applyPlatforms(project, true); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, scale := range opts.scale {
|
||||
err := applyScaleOpts(project, opts.scale)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func applyScaleOpts(project *types.Project, opts []string) error {
|
||||
for _, scale := range opts {
|
||||
split := strings.Split(scale, "=")
|
||||
if len(split) != 2 {
|
||||
return fmt.Errorf("invalid --scale option %q. Should be SERVICE=NUM", scale)
|
||||
@@ -180,10 +182,16 @@ func (opts createOptions) Apply(project *types.Project) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = setServiceScale(project, name, uint64(replicas))
|
||||
err = setServiceScale(project, name, replicas)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (opts createOptions) isPullPolicyValid() bool {
|
||||
pullPolicies := []string{types.PullPolicyAlways, types.PullPolicyNever, types.PullPolicyBuild,
|
||||
types.PullPolicyMissing, types.PullPolicyIfNotPresent}
|
||||
return slices.Contains(pullPolicies, opts.Pull)
|
||||
}
|
||||
|
||||
@@ -21,13 +21,13 @@ import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/compose-spec/compose-go/types"
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
"github.com/docker/compose/v2/pkg/mocks"
|
||||
"github.com/golang/mock/gomock"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.uber.org/mock/gomock"
|
||||
)
|
||||
|
||||
func TestRunCreate(t *testing.T) {
|
||||
@@ -86,7 +86,7 @@ func sampleProject() *types.Project {
|
||||
return &types.Project{
|
||||
Name: "test",
|
||||
Services: types.Services{
|
||||
{
|
||||
"svc": {
|
||||
Name: "svc",
|
||||
Build: &types.BuildConfig{
|
||||
Context: ".",
|
||||
|
||||
@@ -78,7 +78,7 @@ func downCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service)
|
||||
}
|
||||
|
||||
func runDown(ctx context.Context, dockerCli command.Cli, backend api.Service, opts downOptions, services []string) error {
|
||||
project, name, err := opts.projectOrName(dockerCli)
|
||||
project, name, err := opts.projectOrName(dockerCli, services...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ package compose
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/compose-spec/compose-go/types"
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
"github.com/docker/cli/cli"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
|
||||
@@ -72,12 +72,6 @@ func runList(ctx context.Context, dockerCli command.Cli, backend api.Service, ls
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if lsOpts.Quiet {
|
||||
for _, s := range stackList {
|
||||
fmt.Fprintln(dockerCli.Out(), s.Name)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if filters.Len() > 0 {
|
||||
var filtered []api.Stack
|
||||
@@ -90,6 +84,13 @@ func runList(ctx context.Context, dockerCli command.Cli, backend api.Service, ls
|
||||
stackList = filtered
|
||||
}
|
||||
|
||||
if lsOpts.Quiet {
|
||||
for _, s := range stackList {
|
||||
fmt.Fprintln(dockerCli.Out(), s.Name)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
view := viewFromStackList(stackList)
|
||||
return formatter.Print(view, lsOpts.Format, dockerCli.Out(), func(w io.Writer) {
|
||||
for _, stack := range view {
|
||||
|
||||
@@ -18,6 +18,7 @@ package compose
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/spf13/cobra"
|
||||
@@ -30,6 +31,7 @@ type logsOptions struct {
|
||||
*ProjectOptions
|
||||
composeOptions
|
||||
follow bool
|
||||
index int
|
||||
tail string
|
||||
since string
|
||||
until string
|
||||
@@ -48,10 +50,17 @@ func logsCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service)
|
||||
RunE: Adapt(func(ctx context.Context, args []string) error {
|
||||
return runLogs(ctx, dockerCli, backend, opts, args)
|
||||
}),
|
||||
PreRunE: func(cmd *cobra.Command, args []string) error {
|
||||
if opts.index > 0 && len(args) != 1 {
|
||||
return errors.New("--index requires one service to be selected")
|
||||
}
|
||||
return nil
|
||||
},
|
||||
ValidArgsFunction: completeServiceNames(dockerCli, p),
|
||||
}
|
||||
flags := logsCmd.Flags()
|
||||
flags.BoolVarP(&opts.follow, "follow", "f", false, "Follow log output.")
|
||||
flags.IntVar(&opts.index, "index", 0, "index of the container if service has multiple replicas")
|
||||
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.")
|
||||
@@ -71,6 +80,7 @@ func runLogs(ctx context.Context, dockerCli command.Cli, backend api.Service, op
|
||||
Project: project,
|
||||
Services: services,
|
||||
Follow: opts.follow,
|
||||
Index: opts.index,
|
||||
Tail: opts.tail,
|
||||
Since: opts.since,
|
||||
Until: opts.until,
|
||||
|
||||
@@ -19,16 +19,13 @@ package compose
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/compose-spec/compose-go/types"
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
"github.com/docker/compose/v2/pkg/utils"
|
||||
)
|
||||
|
||||
func applyPlatforms(project *types.Project, buildForSinglePlatform bool) error {
|
||||
defaultPlatform := project.Environment["DOCKER_DEFAULT_PLATFORM"]
|
||||
for i := range project.Services {
|
||||
// mutable reference so platform fields can be updated
|
||||
service := &project.Services[i]
|
||||
|
||||
for name, service := range project.Services {
|
||||
if service.Build == nil {
|
||||
continue
|
||||
}
|
||||
@@ -36,7 +33,7 @@ func applyPlatforms(project *types.Project, buildForSinglePlatform bool) error {
|
||||
// default platform only applies if the service doesn't specify
|
||||
if defaultPlatform != "" && service.Platform == "" {
|
||||
if len(service.Build.Platforms) > 0 && !utils.StringContains(service.Build.Platforms, defaultPlatform) {
|
||||
return fmt.Errorf("service %q build.platforms does not support value set by DOCKER_DEFAULT_PLATFORM: %s", service.Name, defaultPlatform)
|
||||
return fmt.Errorf("service %q build.platforms does not support value set by DOCKER_DEFAULT_PLATFORM: %s", name, defaultPlatform)
|
||||
}
|
||||
service.Platform = defaultPlatform
|
||||
}
|
||||
@@ -44,7 +41,7 @@ func applyPlatforms(project *types.Project, buildForSinglePlatform bool) error {
|
||||
if service.Platform != "" {
|
||||
if len(service.Build.Platforms) > 0 {
|
||||
if !utils.StringContains(service.Build.Platforms, service.Platform) {
|
||||
return fmt.Errorf("service %q build configuration does not support platform: %s", service.Name, service.Platform)
|
||||
return fmt.Errorf("service %q build configuration does not support platform: %s", name, service.Platform)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,6 +68,7 @@ func applyPlatforms(project *types.Project, buildForSinglePlatform bool) error {
|
||||
// empty indicates that the builder gets to decide
|
||||
service.Build.Platforms = nil
|
||||
}
|
||||
project.Services[name] = service
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -19,15 +19,15 @@ package compose
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/compose-spec/compose-go/types"
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestApplyPlatforms_InferFromRuntime(t *testing.T) {
|
||||
makeProject := func() *types.Project {
|
||||
return &types.Project{
|
||||
Services: []types.ServiceConfig{
|
||||
{
|
||||
Services: types.Services{
|
||||
"test": {
|
||||
Name: "test",
|
||||
Image: "foo",
|
||||
Build: &types.BuildConfig{
|
||||
@@ -47,14 +47,14 @@ func TestApplyPlatforms_InferFromRuntime(t *testing.T) {
|
||||
t.Run("SinglePlatform", func(t *testing.T) {
|
||||
project := makeProject()
|
||||
require.NoError(t, applyPlatforms(project, true))
|
||||
require.EqualValues(t, []string{"alice/32"}, project.Services[0].Build.Platforms)
|
||||
require.EqualValues(t, []string{"alice/32"}, project.Services["test"].Build.Platforms)
|
||||
})
|
||||
|
||||
t.Run("MultiPlatform", func(t *testing.T) {
|
||||
project := makeProject()
|
||||
require.NoError(t, applyPlatforms(project, false))
|
||||
require.EqualValues(t, []string{"linux/amd64", "linux/arm64", "alice/32"},
|
||||
project.Services[0].Build.Platforms)
|
||||
project.Services["test"].Build.Platforms)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -64,8 +64,8 @@ func TestApplyPlatforms_DockerDefaultPlatform(t *testing.T) {
|
||||
Environment: map[string]string{
|
||||
"DOCKER_DEFAULT_PLATFORM": "linux/amd64",
|
||||
},
|
||||
Services: []types.ServiceConfig{
|
||||
{
|
||||
Services: types.Services{
|
||||
"test": {
|
||||
Name: "test",
|
||||
Image: "foo",
|
||||
Build: &types.BuildConfig{
|
||||
@@ -83,14 +83,14 @@ func TestApplyPlatforms_DockerDefaultPlatform(t *testing.T) {
|
||||
t.Run("SinglePlatform", func(t *testing.T) {
|
||||
project := makeProject()
|
||||
require.NoError(t, applyPlatforms(project, true))
|
||||
require.EqualValues(t, []string{"linux/amd64"}, project.Services[0].Build.Platforms)
|
||||
require.EqualValues(t, []string{"linux/amd64"}, project.Services["test"].Build.Platforms)
|
||||
})
|
||||
|
||||
t.Run("MultiPlatform", func(t *testing.T) {
|
||||
project := makeProject()
|
||||
require.NoError(t, applyPlatforms(project, false))
|
||||
require.EqualValues(t, []string{"linux/amd64", "linux/arm64"},
|
||||
project.Services[0].Build.Platforms)
|
||||
project.Services["test"].Build.Platforms)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -100,8 +100,8 @@ func TestApplyPlatforms_UnsupportedPlatform(t *testing.T) {
|
||||
Environment: map[string]string{
|
||||
"DOCKER_DEFAULT_PLATFORM": "commodore/64",
|
||||
},
|
||||
Services: []types.ServiceConfig{
|
||||
{
|
||||
Services: types.Services{
|
||||
"test": {
|
||||
Name: "test",
|
||||
Image: "foo",
|
||||
Build: &types.BuildConfig{
|
||||
|
||||
@@ -18,6 +18,7 @@ package compose
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
@@ -29,7 +30,6 @@ import (
|
||||
"github.com/docker/cli/cli/command"
|
||||
cliformatter "github.com/docker/cli/cli/command/formatter"
|
||||
cliflags "github.com/docker/cli/cli/flags"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -41,6 +41,8 @@ type psOptions struct {
|
||||
Services bool
|
||||
Filter string
|
||||
Status []string
|
||||
noTrunc bool
|
||||
Orphans bool
|
||||
}
|
||||
|
||||
func (p *psOptions) parseFilter() error {
|
||||
@@ -83,7 +85,9 @@ func psCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *c
|
||||
flags.StringArrayVar(&opts.Status, "status", []string{}, "Filter services by status. Values: [paused | restarting | removing | running | dead | created | exited]")
|
||||
flags.BoolVarP(&opts.Quiet, "quiet", "q", false, "Only display IDs")
|
||||
flags.BoolVar(&opts.Services, "services", false, "Display services")
|
||||
flags.BoolVar(&opts.Orphans, "orphans", true, "Include orphaned services (not declared by project)")
|
||||
flags.BoolVarP(&opts.All, "all", "a", false, "Show all stopped containers (including those created by the run command)")
|
||||
flags.BoolVar(&opts.noTrunc, "no-trunc", false, "Don't truncate output")
|
||||
return psCmd
|
||||
}
|
||||
|
||||
@@ -93,12 +97,17 @@ func runPs(ctx context.Context, dockerCli command.Cli, backend api.Service, serv
|
||||
return err
|
||||
}
|
||||
|
||||
if project != nil && len(services) > 0 {
|
||||
if project != nil {
|
||||
names := project.ServiceNames()
|
||||
for _, service := range services {
|
||||
if !utils.StringContains(names, service) {
|
||||
return fmt.Errorf("no such service: %s", service)
|
||||
if len(services) > 0 {
|
||||
for _, service := range services {
|
||||
if !utils.StringContains(names, service) {
|
||||
return fmt.Errorf("no such service: %s", service)
|
||||
}
|
||||
}
|
||||
} else if !opts.Orphans {
|
||||
// until user asks to list orphaned services, we only include those declared in project
|
||||
services = names
|
||||
}
|
||||
}
|
||||
|
||||
@@ -145,6 +154,7 @@ func runPs(ctx context.Context, dockerCli command.Cli, backend api.Service, serv
|
||||
containerCtx := cliformatter.Context{
|
||||
Output: dockerCli.Out(),
|
||||
Format: formatter.NewContainerFormat(opts.Format, opts.Quiet, false),
|
||||
Trunc: !opts.noTrunc,
|
||||
}
|
||||
return formatter.ContainerWrite(containerCtx, containers)
|
||||
}
|
||||
|
||||
@@ -26,8 +26,8 @@ import (
|
||||
"github.com/docker/cli/cli/streams"
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
"github.com/docker/compose/v2/pkg/mocks"
|
||||
"github.com/golang/mock/gomock"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"go.uber.org/mock/gomock"
|
||||
)
|
||||
|
||||
func TestPsTable(t *testing.T) {
|
||||
|
||||
@@ -25,11 +25,17 @@ import (
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
)
|
||||
|
||||
type publishOptions struct {
|
||||
*ProjectOptions
|
||||
resolveImageDigests bool
|
||||
ociVersion string
|
||||
}
|
||||
|
||||
func publishCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
|
||||
opts := pushOptions{
|
||||
opts := publishOptions{
|
||||
ProjectOptions: p,
|
||||
}
|
||||
publishCmd := &cobra.Command{
|
||||
cmd := &cobra.Command{
|
||||
Use: "publish [OPTIONS] [REPOSITORY]",
|
||||
Short: "Publish compose application",
|
||||
RunE: Adapt(func(ctx context.Context, args []string) error {
|
||||
@@ -37,14 +43,20 @@ func publishCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Servic
|
||||
}),
|
||||
Args: cobra.ExactArgs(1),
|
||||
}
|
||||
return publishCmd
|
||||
flags := cmd.Flags()
|
||||
flags.BoolVar(&opts.resolveImageDigests, "resolve-image-digests", false, "Pin image tags to digests.")
|
||||
flags.StringVar(&opts.ociVersion, "oci-version", "", "OCI Image/Artifact specification version (automatically determined by default)")
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runPublish(ctx context.Context, dockerCli command.Cli, backend api.Service, opts pushOptions, repository string) error {
|
||||
func runPublish(ctx context.Context, dockerCli command.Cli, backend api.Service, opts publishOptions, repository string) error {
|
||||
project, err := opts.ToProject(dockerCli, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return backend.Publish(ctx, project, repository, api.PublishOptions{})
|
||||
return backend.Publish(ctx, project, repository, api.PublishOptions{
|
||||
ResolveImageDigests: opts.resolveImageDigests,
|
||||
OCIVersion: api.OCIVersion(opts.ociVersion),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/compose-spec/compose-go/types"
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/morikuni/aec"
|
||||
"github.com/spf13/cobra"
|
||||
@@ -72,11 +72,12 @@ func pullCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service)
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (opts pullOptions) apply(project *types.Project, services []string) error {
|
||||
func (opts pullOptions) apply(project *types.Project, services []string) (*types.Project, error) {
|
||||
if !opts.includeDeps {
|
||||
err := project.ForServices(services, types.IgnoreDependencies)
|
||||
var err error
|
||||
project, err = project.WithSelectedServices(services, types.IgnoreDependencies)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,7 +90,7 @@ func (opts pullOptions) apply(project *types.Project, services []string) error {
|
||||
project.Services[i] = service
|
||||
}
|
||||
}
|
||||
return nil
|
||||
return project, nil
|
||||
}
|
||||
|
||||
func runPull(ctx context.Context, dockerCli command.Cli, backend api.Service, opts pullOptions, services []string) error {
|
||||
@@ -98,7 +99,7 @@ func runPull(ctx context.Context, dockerCli command.Cli, backend api.Service, op
|
||||
return err
|
||||
}
|
||||
|
||||
err = opts.apply(project, services)
|
||||
project, err = opts.apply(project, services)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -19,39 +19,39 @@ package compose
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/compose-spec/compose-go/types"
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
"gotest.tools/v3/assert"
|
||||
)
|
||||
|
||||
func TestApplyPullOptions(t *testing.T) {
|
||||
project := &types.Project{
|
||||
Services: []types.ServiceConfig{
|
||||
{
|
||||
Services: types.Services{
|
||||
"must-build": {
|
||||
Name: "must-build",
|
||||
// No image, local build only
|
||||
Build: &types.BuildConfig{
|
||||
Context: ".",
|
||||
},
|
||||
},
|
||||
{
|
||||
"has-build": {
|
||||
Name: "has-build",
|
||||
Image: "registry.example.com/myservice",
|
||||
Build: &types.BuildConfig{
|
||||
Context: ".",
|
||||
},
|
||||
},
|
||||
{
|
||||
"must-pull": {
|
||||
Name: "must-pull",
|
||||
Image: "registry.example.com/another-service",
|
||||
},
|
||||
},
|
||||
}
|
||||
err := pullOptions{
|
||||
project, err := pullOptions{
|
||||
policy: types.PullPolicyMissing,
|
||||
}.apply(project, nil)
|
||||
assert.NilError(t, err)
|
||||
|
||||
assert.Equal(t, project.Services[0].PullPolicy, "") // still default
|
||||
assert.Equal(t, project.Services[1].PullPolicy, types.PullPolicyMissing)
|
||||
assert.Equal(t, project.Services[2].PullPolicy, types.PullPolicyMissing)
|
||||
assert.Equal(t, project.Services["must-build"].PullPolicy, "") // still default
|
||||
assert.Equal(t, project.Services["has-build"].PullPolicy, types.PullPolicyMissing)
|
||||
assert.Equal(t, project.Services["must-pull"].PullPolicy, types.PullPolicyMissing)
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ package compose
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/compose-spec/compose-go/types"
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
@@ -60,7 +60,7 @@ func runPush(ctx context.Context, dockerCli command.Cli, backend api.Service, op
|
||||
}
|
||||
|
||||
if !opts.IncludeDeps {
|
||||
err := project.ForServices(services, types.IgnoreDependencies)
|
||||
project, err = project.WithSelectedServices(services, types.IgnoreDependencies)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ func runRestart(ctx context.Context, dockerCli command.Cli, backend api.Service,
|
||||
}
|
||||
|
||||
if project != nil && len(services) > 0 {
|
||||
err := project.EnableServices(services...)
|
||||
project, err = project.WithServicesEnabled(services...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -21,11 +21,12 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
xprogress "github.com/docker/buildx/util/progress"
|
||||
"github.com/compose-spec/compose-go/v2/format"
|
||||
xprogress "github.com/moby/buildkit/util/progress/progressui"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
cgo "github.com/compose-spec/compose-go/cli"
|
||||
"github.com/compose-spec/compose-go/loader"
|
||||
"github.com/compose-spec/compose-go/types"
|
||||
cgo "github.com/compose-spec/compose-go/v2/cli"
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/cli/opts"
|
||||
"github.com/mattn/go-shellwords"
|
||||
@@ -65,51 +66,53 @@ type runOptions struct {
|
||||
quietPull bool
|
||||
}
|
||||
|
||||
func (options runOptions) apply(project *types.Project) error {
|
||||
func (options runOptions) apply(project *types.Project) (*types.Project, error) {
|
||||
if options.noDeps {
|
||||
err := project.ForServices([]string{options.Service}, types.IgnoreDependencies)
|
||||
var err error
|
||||
project, err = project.WithSelectedServices([]string{options.Service}, types.IgnoreDependencies)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
target, err := project.GetService(options.Service)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
target.Tty = !options.noTty
|
||||
target.StdinOpen = options.interactive
|
||||
|
||||
// --service-ports and --publish are incompatible
|
||||
if !options.servicePorts {
|
||||
target.Ports = []types.ServicePortConfig{}
|
||||
}
|
||||
if len(options.publish) > 0 {
|
||||
if len(target.Ports) > 0 {
|
||||
logrus.Debug("Running service without ports exposed as --service-ports=false")
|
||||
}
|
||||
target.Ports = []types.ServicePortConfig{}
|
||||
for _, p := range options.publish {
|
||||
config, err := types.ParsePortConfig(p)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
target.Ports = append(target.Ports, config...)
|
||||
}
|
||||
}
|
||||
if len(options.volumes) > 0 {
|
||||
for _, v := range options.volumes {
|
||||
volume, err := loader.ParseVolume(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
target.Volumes = append(target.Volumes, volume)
|
||||
|
||||
for _, v := range options.volumes {
|
||||
volume, err := format.ParseVolume(v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
target.Volumes = append(target.Volumes, volume)
|
||||
}
|
||||
|
||||
for i, s := range project.Services {
|
||||
if s.Name == options.Service {
|
||||
project.Services[i] = target
|
||||
for name := range project.Services {
|
||||
if name == options.Service {
|
||||
project.Services[name] = target
|
||||
break
|
||||
}
|
||||
}
|
||||
return nil
|
||||
return project, nil
|
||||
}
|
||||
|
||||
func runCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
|
||||
@@ -159,7 +162,7 @@ func runCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *
|
||||
}
|
||||
|
||||
if createOpts.quietPull {
|
||||
buildOpts.Progress = xprogress.PrinterModeQuiet
|
||||
buildOpts.Progress = string(xprogress.QuietMode)
|
||||
}
|
||||
|
||||
options.ignoreOrphans = utils.StringToBool(project.Environment[ComposeIgnoreOrphans])
|
||||
@@ -183,7 +186,7 @@ func runCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *
|
||||
flags.StringArrayVarP(&options.volumes, "volume", "v", []string{}, "Bind mount a volume.")
|
||||
flags.StringArrayVarP(&options.publish, "publish", "p", []string{}, "Publish a container's port(s) to the host.")
|
||||
flags.BoolVar(&options.useAliases, "use-aliases", false, "Use the service's network useAliases in the network(s) the container connects to.")
|
||||
flags.BoolVar(&options.servicePorts, "service-ports", false, "Run command with the service's ports enabled and mapped to the host.")
|
||||
flags.BoolVarP(&options.servicePorts, "service-ports", "P", false, "Run command with all service's ports enabled and mapped to the host.")
|
||||
flags.BoolVar(&options.quietPull, "quiet-pull", false, "Pull without printing progress information.")
|
||||
flags.BoolVar(&createOpts.Build, "build", false, "Build image before starting container.")
|
||||
flags.BoolVar(&createOpts.removeOrphans, "remove-orphans", false, "Remove containers for services not defined in the Compose file.")
|
||||
@@ -208,7 +211,7 @@ func normalizeRunFlags(f *pflag.FlagSet, name string) pflag.NormalizedName {
|
||||
}
|
||||
|
||||
func runRun(ctx context.Context, backend api.Service, project *types.Project, options runOptions, createOpts createOptions, buildOpts buildOptions, dockerCli command.Cli) error {
|
||||
err := options.apply(project)
|
||||
project, err := options.apply(project)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -277,10 +280,10 @@ func runRun(ctx context.Context, backend api.Service, project *types.Project, op
|
||||
QuietPull: options.quietPull,
|
||||
}
|
||||
|
||||
for i, service := range project.Services {
|
||||
if service.Name == options.Service {
|
||||
for name, service := range project.Services {
|
||||
if name == options.Service {
|
||||
service.StdinOpen = options.interactive
|
||||
project.Services[i] = service
|
||||
project.Services[name] = service
|
||||
}
|
||||
}
|
||||
|
||||
@@ -298,16 +301,16 @@ func runRun(ctx context.Context, backend api.Service, project *types.Project, op
|
||||
func startDependencies(ctx context.Context, backend api.Service, project types.Project, buildOpts *api.BuildOptions, requestedServiceName string, ignoreOrphans bool) error {
|
||||
dependencies := types.Services{}
|
||||
var requestedService types.ServiceConfig
|
||||
for _, service := range project.Services {
|
||||
if service.Name != requestedServiceName {
|
||||
dependencies = append(dependencies, service)
|
||||
for name, service := range project.Services {
|
||||
if name != requestedServiceName {
|
||||
dependencies[name] = service
|
||||
} else {
|
||||
requestedService = service
|
||||
}
|
||||
}
|
||||
|
||||
project.Services = dependencies
|
||||
project.DisabledServices = append(project.DisabledServices, requestedService)
|
||||
project.DisabledServices[requestedServiceName] = requestedService
|
||||
err := backend.Create(ctx, &project, api.CreateOptions{
|
||||
Build: buildOpts,
|
||||
IgnoreOrphans: ignoreOrphans,
|
||||
|
||||
@@ -18,13 +18,13 @@ package compose
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/cli/cli/command"
|
||||
|
||||
"github.com/compose-spec/compose-go/types"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
"golang.org/x/exp/maps"
|
||||
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
@@ -67,24 +67,18 @@ func runScale(ctx context.Context, dockerCli command.Cli, backend api.Service, o
|
||||
}
|
||||
|
||||
if opts.noDeps {
|
||||
if err := project.ForServices(services, types.IgnoreDependencies); err != nil {
|
||||
if project, err = project.WithSelectedServices(services, types.IgnoreDependencies); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for key, value := range serviceReplicaTuples {
|
||||
for i, service := range project.Services {
|
||||
if service.Name != key {
|
||||
continue
|
||||
}
|
||||
if service.Deploy == nil {
|
||||
service.Deploy = &types.DeployConfig{}
|
||||
}
|
||||
scale := uint64(value)
|
||||
service.Deploy.Replicas = &scale
|
||||
project.Services[i] = service
|
||||
break
|
||||
service, err := project.GetService(key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
service.SetScale(value)
|
||||
project.Services[key] = service
|
||||
}
|
||||
|
||||
return backend.Scale(ctx, project, api.ScaleOptions{Services: services})
|
||||
@@ -95,12 +89,12 @@ func parseServicesReplicasArgs(args []string) (map[string]int, error) {
|
||||
for _, arg := range args {
|
||||
key, val, ok := strings.Cut(arg, "=")
|
||||
if !ok || key == "" || val == "" {
|
||||
return nil, errors.Errorf("invalid scale specifier: %s", arg)
|
||||
return nil, fmt.Errorf("invalid scale specifier: %s", arg)
|
||||
}
|
||||
intValue, err := strconv.Atoi(val)
|
||||
|
||||
if err != nil {
|
||||
return nil, errors.Errorf("invalid scale specifier: can't parse replica value as int: %v", arg)
|
||||
return nil, fmt.Errorf("invalid scale specifier: can't parse replica value as int: %v", arg)
|
||||
}
|
||||
serviceReplicaTuples[key] = intValue
|
||||
}
|
||||
|
||||
84
cmd/compose/stats.go
Normal file
84
cmd/compose/stats.go
Normal file
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
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/cli/cli/command"
|
||||
"github.com/docker/cli/cli/command/container"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
)
|
||||
|
||||
type statsOptions struct {
|
||||
ProjectOptions *ProjectOptions
|
||||
all bool
|
||||
format string
|
||||
noStream bool
|
||||
noTrunc bool
|
||||
}
|
||||
|
||||
func statsCommand(p *ProjectOptions, dockerCli command.Cli) *cobra.Command {
|
||||
opts := statsOptions{
|
||||
ProjectOptions: p,
|
||||
}
|
||||
cmd := &cobra.Command{
|
||||
Use: "stats [OPTIONS] [SERVICE]",
|
||||
Short: "Display a live stream of container(s) resource usage statistics",
|
||||
Args: cobra.MaximumNArgs(1),
|
||||
RunE: Adapt(func(ctx context.Context, args []string) error {
|
||||
return runStats(ctx, dockerCli, opts, args)
|
||||
}),
|
||||
ValidArgsFunction: completeServiceNames(dockerCli, p),
|
||||
}
|
||||
flags := cmd.Flags()
|
||||
flags.BoolVarP(&opts.all, "all", "a", false, "Show all containers (default shows just running)")
|
||||
flags.StringVar(&opts.format, "format", "", `Format output using a custom template:
|
||||
'table': Print output in table format with column headers (default)
|
||||
'table TEMPLATE': Print output in table format using the given Go template
|
||||
'json': Print in JSON format
|
||||
'TEMPLATE': Print output using the given Go template.
|
||||
Refer to https://docs.docker.com/go/formatting/ for more information about formatting output with templates`)
|
||||
flags.BoolVar(&opts.noStream, "no-stream", false, "Disable streaming stats and only pull the first result")
|
||||
flags.BoolVar(&opts.noTrunc, "no-trunc", false, "Do not truncate output")
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runStats(ctx context.Context, dockerCli command.Cli, opts statsOptions, service []string) error {
|
||||
name, err := opts.ProjectOptions.toProjectName(dockerCli)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
filter := []filters.KeyValuePair{
|
||||
filters.Arg("label", fmt.Sprintf("%s=%s", api.ProjectLabel, name)),
|
||||
}
|
||||
if len(service) > 0 {
|
||||
filter = append(filter, filters.Arg("label", fmt.Sprintf("%s=%s", api.ServiceLabel, service[0])))
|
||||
}
|
||||
args := filters.NewArgs(filter...)
|
||||
return container.RunStats(ctx, dockerCli, &container.StatsOptions{
|
||||
All: opts.all,
|
||||
NoStream: opts.noStream,
|
||||
NoTrunc: opts.noTrunc,
|
||||
Format: opts.format,
|
||||
Filters: &args,
|
||||
})
|
||||
}
|
||||
@@ -23,9 +23,9 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
xprogress "github.com/docker/buildx/util/progress"
|
||||
xprogress "github.com/moby/buildkit/util/progress/progressui"
|
||||
|
||||
"github.com/compose-spec/compose-go/types"
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/compose/v2/cmd/formatter"
|
||||
"github.com/spf13/cobra"
|
||||
@@ -56,22 +56,23 @@ type upOptions struct {
|
||||
waitTimeout int
|
||||
}
|
||||
|
||||
func (opts upOptions) apply(project *types.Project, services []string) error {
|
||||
func (opts upOptions) apply(project *types.Project, services []string) (*types.Project, error) {
|
||||
if opts.noDeps {
|
||||
err := project.ForServices(services, types.IgnoreDependencies)
|
||||
var err error
|
||||
project, err = project.WithSelectedServices(services, types.IgnoreDependencies)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if opts.exitCodeFrom != "" {
|
||||
_, err := project.GetService(opts.exitCodeFrom)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
return project, nil
|
||||
}
|
||||
|
||||
func upCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
|
||||
@@ -102,7 +103,7 @@ func upCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *c
|
||||
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 policy.")
|
||||
flags.StringVar(&create.Pull, "pull", "policy", `Pull image before running ("always"|"policy"|"never")`)
|
||||
flags.StringVar(&create.Pull, "pull", "policy", `Pull image before running ("always"|"missing"|"never")`)
|
||||
flags.BoolVar(&create.removeOrphans, "remove-orphans", false, "Remove containers for services not defined in the Compose file.")
|
||||
flags.StringArrayVar(&create.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.")
|
||||
@@ -171,17 +172,15 @@ func runUp(
|
||||
return err
|
||||
}
|
||||
|
||||
err = upOptions.apply(project, services)
|
||||
project, err = upOptions.apply(project, services)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var build *api.BuildOptions
|
||||
// this check is technically redundant as createOptions::apply()
|
||||
// already removed all the build sections
|
||||
if !createOptions.noBuild {
|
||||
if createOptions.quietPull {
|
||||
buildOptions.Progress = xprogress.PrinterModeQuiet
|
||||
buildOptions.Progress = string(xprogress.QuietMode)
|
||||
}
|
||||
// BuildOptions here is nested inside CreateOptions, so
|
||||
// no service list is passed, it will implicitly pick all
|
||||
@@ -231,9 +230,9 @@ func runUp(
|
||||
if upOptions.attachDependencies {
|
||||
dependencyOpt = types.IncludeDependencies
|
||||
}
|
||||
if err := project.WithServices(services, func(s types.ServiceConfig) error {
|
||||
if err := project.ForEachService(services, func(serviceName string, s *types.ServiceConfig) error {
|
||||
if s.Attach == nil || *s.Attach {
|
||||
attachSet.Add(s.Name)
|
||||
attachSet.Add(serviceName)
|
||||
}
|
||||
return nil
|
||||
}, dependencyOpt); err != nil {
|
||||
@@ -261,22 +260,12 @@ func runUp(
|
||||
})
|
||||
}
|
||||
|
||||
func setServiceScale(project *types.Project, name string, replicas uint64) error {
|
||||
for i, s := range project.Services {
|
||||
if s.Name != name {
|
||||
continue
|
||||
}
|
||||
|
||||
service, err := project.GetService(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if service.Deploy == nil {
|
||||
service.Deploy = &types.DeployConfig{}
|
||||
}
|
||||
service.Deploy.Replicas = &replicas
|
||||
project.Services[i] = service
|
||||
return nil
|
||||
func setServiceScale(project *types.Project, name string, replicas int) error {
|
||||
service, err := project.GetService(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return fmt.Errorf("unknown service %q", name)
|
||||
service.SetScale(replicas)
|
||||
project.Services[name] = service
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -19,25 +19,33 @@ package compose
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/compose-spec/compose-go/types"
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
"gotest.tools/v3/assert"
|
||||
)
|
||||
|
||||
func TestApplyScaleOpt(t *testing.T) {
|
||||
p := types.Project{
|
||||
Services: []types.ServiceConfig{
|
||||
{
|
||||
Services: types.Services{
|
||||
"foo": {
|
||||
Name: "foo",
|
||||
},
|
||||
{
|
||||
"bar": {
|
||||
Name: "bar",
|
||||
Deploy: &types.DeployConfig{
|
||||
Mode: "test",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
opt := createOptions{scale: []string{"foo=2"}}
|
||||
err := opt.Apply(&p)
|
||||
err := applyScaleOpts(&p, []string{"foo=2", "bar=3"})
|
||||
assert.NilError(t, err)
|
||||
foo, err := p.GetService("foo")
|
||||
assert.NilError(t, err)
|
||||
assert.Equal(t, *foo.Deploy.Replicas, uint64(2))
|
||||
assert.Equal(t, *foo.Scale, 2)
|
||||
|
||||
bar, err := p.GetService("bar")
|
||||
assert.NilError(t, err)
|
||||
assert.Equal(t, *bar.Scale, 3)
|
||||
assert.Equal(t, *bar.Deploy.Replicas, 3)
|
||||
|
||||
}
|
||||
|
||||
@@ -20,6 +20,8 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/compose/v2/internal/locker"
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
@@ -78,13 +80,19 @@ func runWatch(ctx context.Context, dockerCli command.Cli, backend api.Service, w
|
||||
// validation done -- ensure we have the lockfile for this project before doing work
|
||||
l, err := locker.NewPidfile(project.Name)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot take exclusive lock for project %q: %v", project.Name, err)
|
||||
return fmt.Errorf("cannot take exclusive lock for project %q: %w", project.Name, err)
|
||||
}
|
||||
if err := l.Lock(); err != nil {
|
||||
return fmt.Errorf("cannot take exclusive lock for project %q: %v", project.Name, err)
|
||||
return fmt.Errorf("cannot take exclusive lock for project %q: %w", project.Name, err)
|
||||
}
|
||||
|
||||
if !watchOpts.noUp {
|
||||
for index, service := range project.Services {
|
||||
if service.Build != nil && service.Develop != nil {
|
||||
service.PullPolicy = types.PullPolicyBuild
|
||||
}
|
||||
project.Services[index] = service
|
||||
}
|
||||
upOpts := api.UpOptions{
|
||||
Create: api.CreateOptions{
|
||||
Build: &build,
|
||||
|
||||
@@ -33,6 +33,7 @@ const (
|
||||
defaultContainerTableFormat = "table {{.Name}}\t{{.Image}}\t{{.Command}}\t{{.Service}}\t{{.RunningFor}}\t{{.Status}}\t{{.Ports}}"
|
||||
|
||||
nameHeader = "NAME"
|
||||
projectHeader = "PROJECT"
|
||||
serviceHeader = "SERVICE"
|
||||
commandHeader = "COMMAND"
|
||||
runningForHeader = "CREATED"
|
||||
@@ -112,6 +113,7 @@ func NewContainerContext() *ContainerContext {
|
||||
containerCtx.Header = formatter.SubHeaderContext{
|
||||
"ID": formatter.ContainerIDHeader,
|
||||
"Name": nameHeader,
|
||||
"Project": projectHeader,
|
||||
"Service": serviceHeader,
|
||||
"Image": formatter.ImageHeader,
|
||||
"Command": commandHeader,
|
||||
@@ -164,6 +166,10 @@ func (c *ContainerContext) Service() string {
|
||||
return c.c.Service
|
||||
}
|
||||
|
||||
func (c *ContainerContext) Project() string {
|
||||
return c.c.Project
|
||||
}
|
||||
|
||||
func (c *ContainerContext) Image() string {
|
||||
return c.c.Image
|
||||
}
|
||||
|
||||
@@ -23,8 +23,6 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// Print prints formatted lists in different formats
|
||||
@@ -67,7 +65,7 @@ func Print(toJSON interface{}, format string, outWriter io.Writer, writerFn func
|
||||
_, _ = fmt.Fprintln(outWriter, outJSON)
|
||||
}
|
||||
default:
|
||||
return errors.Wrapf(api.ErrParsingFailed, "format value %q could not be parsed", format)
|
||||
return fmt.Errorf("format value %q could not be parsed: %w", format, api.ErrParsingFailed)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
25
cmd/main.go
25
cmd/main.go
@@ -24,25 +24,28 @@ import (
|
||||
"github.com/docker/cli/cli-plugins/plugin"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/compose/v2/cmd/cmdtrace"
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/docker/compose/v2/cmd/compatibility"
|
||||
commands "github.com/docker/compose/v2/cmd/compose"
|
||||
"github.com/docker/compose/v2/internal"
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
"github.com/docker/compose/v2/pkg/compose"
|
||||
)
|
||||
|
||||
func pluginMain() {
|
||||
plugin.Run(func(dockerCli command.Cli) *cobra.Command {
|
||||
serviceProxy := api.NewServiceProxy().WithService(compose.NewComposeService(dockerCli))
|
||||
cmd := commands.RootCommand(dockerCli, serviceProxy)
|
||||
backend := compose.NewComposeService(dockerCli)
|
||||
cmd := commands.RootCommand(dockerCli, backend)
|
||||
originalPreRun := cmd.PersistentPreRunE
|
||||
cmd.PersistentPreRunE = func(cmd *cobra.Command, args []string) error {
|
||||
// initialize the dockerCli instance
|
||||
if err := plugin.PersistentPreRunE(cmd, args); err != nil {
|
||||
return err
|
||||
}
|
||||
// compose-specific initialization
|
||||
dockerCliPostInitialize(dockerCli)
|
||||
|
||||
// TODO(milas): add an env var to enable logging from the
|
||||
// OTel components for debugging purposes
|
||||
_ = cmdtrace.Setup(cmd, dockerCli, os.Args[1:])
|
||||
@@ -68,6 +71,22 @@ func pluginMain() {
|
||||
})
|
||||
}
|
||||
|
||||
// dockerCliPostInitialize performs Compose-specific configuration for the
|
||||
// command.Cli instance provided by the plugin.Run() initialization.
|
||||
//
|
||||
// NOTE: This must be called AFTER plugin.PersistentPreRunE.
|
||||
func dockerCliPostInitialize(dockerCli command.Cli) {
|
||||
// HACK(milas): remove once docker/cli#4574 is merged; for now,
|
||||
// set it in a rather roundabout way by grabbing the underlying
|
||||
// concrete client and manually invoking an option on it
|
||||
_ = dockerCli.Apply(func(cli *command.DockerCli) error {
|
||||
if mobyClient, ok := cli.Client().(*client.Client); ok {
|
||||
_ = client.WithUserAgent("compose/" + internal.Version)(mobyClient)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func main() {
|
||||
if plugin.RunningStandalone() {
|
||||
os.Args = append([]string{"docker"}, compatibility.Convert(os.Args[1:])...)
|
||||
|
||||
@@ -5,36 +5,38 @@ Define and run multi-container applications with Docker.
|
||||
|
||||
### Subcommands
|
||||
|
||||
| Name | Description |
|
||||
|:--------------------------------|:--------------------------------------------------------------------------------------|
|
||||
| [`build`](compose_build.md) | Build or rebuild services |
|
||||
| [`config`](compose_config.md) | Parse, resolve and render compose file in canonical format |
|
||||
| [`cp`](compose_cp.md) | Copy files/folders between a service container and the local filesystem |
|
||||
| [`create`](compose_create.md) | Creates containers for a service. |
|
||||
| [`down`](compose_down.md) | Stop and remove containers, networks |
|
||||
| [`events`](compose_events.md) | Receive real time events from containers. |
|
||||
| [`exec`](compose_exec.md) | Execute a command in a running container. |
|
||||
| [`images`](compose_images.md) | List images used by the created containers |
|
||||
| [`kill`](compose_kill.md) | Force stop service containers. |
|
||||
| [`logs`](compose_logs.md) | View output from containers |
|
||||
| [`ls`](compose_ls.md) | List running compose projects |
|
||||
| [`pause`](compose_pause.md) | Pause services |
|
||||
| [`port`](compose_port.md) | Print the public port for a port binding. |
|
||||
| [`ps`](compose_ps.md) | List containers |
|
||||
| [`pull`](compose_pull.md) | Pull service images |
|
||||
| [`push`](compose_push.md) | Push service images |
|
||||
| [`restart`](compose_restart.md) | Restart service containers |
|
||||
| [`rm`](compose_rm.md) | Removes stopped service containers |
|
||||
| [`run`](compose_run.md) | Run a one-off command on a service. |
|
||||
| [`scale`](compose_scale.md) | Scale services |
|
||||
| [`start`](compose_start.md) | Start services |
|
||||
| [`stop`](compose_stop.md) | Stop services |
|
||||
| [`top`](compose_top.md) | Display the running processes |
|
||||
| [`unpause`](compose_unpause.md) | Unpause services |
|
||||
| [`up`](compose_up.md) | Create and start containers |
|
||||
| [`version`](compose_version.md) | Show the Docker Compose version information |
|
||||
| [`wait`](compose_wait.md) | Block until the first service container stops |
|
||||
| [`watch`](compose_watch.md) | Watch build context for service and rebuild/refresh containers when files are updated |
|
||||
| Name | Description |
|
||||
|:--------------------------------|:-----------------------------------------------------------------------------------------|
|
||||
| [`attach`](compose_attach.md) | Attach local standard input, output, and error streams to a service's running container. |
|
||||
| [`build`](compose_build.md) | Build or rebuild services |
|
||||
| [`config`](compose_config.md) | Parse, resolve and render compose file in canonical format |
|
||||
| [`cp`](compose_cp.md) | Copy files/folders between a service container and the local filesystem |
|
||||
| [`create`](compose_create.md) | Creates containers for a service. |
|
||||
| [`down`](compose_down.md) | Stop and remove containers, networks |
|
||||
| [`events`](compose_events.md) | Receive real time events from containers. |
|
||||
| [`exec`](compose_exec.md) | Execute a command in a running container. |
|
||||
| [`images`](compose_images.md) | List images used by the created containers |
|
||||
| [`kill`](compose_kill.md) | Force stop service containers. |
|
||||
| [`logs`](compose_logs.md) | View output from containers |
|
||||
| [`ls`](compose_ls.md) | List running compose projects |
|
||||
| [`pause`](compose_pause.md) | Pause services |
|
||||
| [`port`](compose_port.md) | Print the public port for a port binding. |
|
||||
| [`ps`](compose_ps.md) | List containers |
|
||||
| [`pull`](compose_pull.md) | Pull service images |
|
||||
| [`push`](compose_push.md) | Push service images |
|
||||
| [`restart`](compose_restart.md) | Restart service containers |
|
||||
| [`rm`](compose_rm.md) | Removes stopped service containers |
|
||||
| [`run`](compose_run.md) | Run a one-off command on a service. |
|
||||
| [`scale`](compose_scale.md) | Scale services |
|
||||
| [`start`](compose_start.md) | Start services |
|
||||
| [`stats`](compose_stats.md) | Display a live stream of container(s) resource usage statistics |
|
||||
| [`stop`](compose_stop.md) | Stop services |
|
||||
| [`top`](compose_top.md) | Display the running processes |
|
||||
| [`unpause`](compose_unpause.md) | Unpause services |
|
||||
| [`up`](compose_up.md) | Create and start containers |
|
||||
| [`version`](compose_version.md) | Show the Docker Compose version information |
|
||||
| [`wait`](compose_wait.md) | Block until the first service container stops |
|
||||
| [`watch`](compose_watch.md) | Watch build context for service and rebuild/refresh containers when files are updated |
|
||||
|
||||
|
||||
### Options
|
||||
@@ -57,7 +59,7 @@ Define and run multi-container applications with Docker.
|
||||
|
||||
## Description
|
||||
|
||||
You can use compose subcommand, `docker compose [-f <arg>...] [options] [COMMAND] [ARGS...]`, to build and manage
|
||||
You can use the compose subcommand, `docker compose [-f <arg>...] [options] [COMMAND] [ARGS...]`, to build and manage
|
||||
multiple services in Docker containers.
|
||||
|
||||
### Use `-f` to specify the name and path of one or more Compose files
|
||||
@@ -146,16 +148,16 @@ demo_1 | 64 bytes from 127.0.0.1: seq=0 ttl=64 time=0.095 ms
|
||||
### Use profiles to enable optional services
|
||||
|
||||
Use `--profile` to specify one or more active profiles
|
||||
Calling `docker compose --profile frontend up` will start the services with the profile `frontend` and services
|
||||
Calling `docker compose --profile frontend up` starts the services with the profile `frontend` and services
|
||||
without any specified profiles.
|
||||
You can also enable multiple profiles, e.g. with `docker compose --profile frontend --profile debug up` the profiles `frontend` and `debug` will be enabled.
|
||||
You can also enable multiple profiles, e.g. with `docker compose --profile frontend --profile debug up` the profiles `frontend` and `debug` is enabled.
|
||||
|
||||
Profiles can also be set by `COMPOSE_PROFILES` environment variable.
|
||||
|
||||
### Configuring parallelism
|
||||
|
||||
Use `--parallel` to specify the maximum level of parallelism for concurrent engine calls.
|
||||
Calling `docker compose --parallel 1 pull` will pull the pullable images defined in the Compose file
|
||||
Calling `docker compose --parallel 1 pull` pulls the pullable images defined in the Compose file
|
||||
one at a time. This can also be used to control build concurrency.
|
||||
|
||||
Parallelism can also be set by the `COMPOSE_PARALLEL_LIMIT` environment variable.
|
||||
@@ -171,7 +173,7 @@ and `COMPOSE_PARALLEL_LIMIT` does the same as the `--parallel` flag.
|
||||
|
||||
If flags are explicitly set on the command line, the associated environment variable is ignored.
|
||||
|
||||
Setting the `COMPOSE_IGNORE_ORPHANS` environment variable to `true` will stop docker compose from detecting orphaned
|
||||
Setting the `COMPOSE_IGNORE_ORPHANS` environment variable to `true` stops docker compose from detecting orphaned
|
||||
containers for the project.
|
||||
|
||||
### Use Dry Run mode to test your command
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# docker compose alpha dry-run
|
||||
|
||||
<!---MARKER_GEN_START-->
|
||||
EXPERIMENTAL - Dry run command allow you to test a command without applying changes
|
||||
Dry run command allows you to test a command without applying changes.
|
||||
|
||||
|
||||
<!---MARKER_GEN_END-->
|
||||
|
||||
@@ -5,9 +5,11 @@ Publish compose application
|
||||
|
||||
### Options
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
|:------------|:-----|:--------|:--------------------------------|
|
||||
| `--dry-run` | | | Execute command in dry run mode |
|
||||
| Name | Type | Default | Description |
|
||||
|:--------------------------|:---------|:--------|:-------------------------------------------------------------------------------|
|
||||
| `--dry-run` | | | Execute command in dry run mode |
|
||||
| `--oci-version` | `string` | | OCI Image/Artifact specification version (automatically determined by default) |
|
||||
| `--resolve-image-digests` | | | Pin image tags to digests. |
|
||||
|
||||
|
||||
<!---MARKER_GEN_END-->
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
# docker compose alpha scale
|
||||
|
||||
<!---MARKER_GEN_START-->
|
||||
Scale services
|
||||
Scale services.
|
||||
|
||||
### Options
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
|:------------|:-----|:--------|:--------------------------------|
|
||||
| `--dry-run` | | | Execute command in dry run mode |
|
||||
| `--no-deps` | | | Don't start linked services. |
|
||||
| `--no-deps` | | | Don't start linked services |
|
||||
|
||||
|
||||
<!---MARKER_GEN_END-->
|
||||
|
||||
18
docs/reference/compose_attach.md
Normal file
18
docs/reference/compose_attach.md
Normal file
@@ -0,0 +1,18 @@
|
||||
# docker compose attach
|
||||
|
||||
<!---MARKER_GEN_START-->
|
||||
Attach local standard input, output, and error streams to a service's running container.
|
||||
|
||||
### Options
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
|:----------------|:---------|:--------|:----------------------------------------------------------|
|
||||
| `--detach-keys` | `string` | | Override the key sequence for detaching from a container. |
|
||||
| `--dry-run` | | | Execute command in dry run mode |
|
||||
| `--index` | `int` | `0` | index of the container if service has multiple replicas. |
|
||||
| `--no-stdin` | | | Do not attach STDIN |
|
||||
| `--sig-proxy` | | | Proxy all received signals to the process |
|
||||
|
||||
|
||||
<!---MARKER_GEN_END-->
|
||||
|
||||
@@ -5,17 +5,18 @@ Build or rebuild services
|
||||
|
||||
### Options
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
|:-----------------|:--------------|:--------|:------------------------------------------------------------------------------------------------------------|
|
||||
| `--build-arg` | `stringArray` | | Set build-time variables for services. |
|
||||
| `--builder` | `string` | | Set builder to use. |
|
||||
| `--dry-run` | | | Execute command in dry run mode |
|
||||
| `-m`, `--memory` | `bytes` | `0` | Set memory limit for the build container. Not supported by BuildKit. |
|
||||
| `--no-cache` | | | Do not use cache when building the image |
|
||||
| `--pull` | | | Always attempt to pull a newer version of the image. |
|
||||
| `--push` | | | Push service images. |
|
||||
| `-q`, `--quiet` | | | Don't print anything to STDOUT |
|
||||
| `--ssh` | `string` | | Set SSH authentications used when building service images. (use 'default' for using your default SSH Agent) |
|
||||
| Name | Type | Default | Description |
|
||||
|:----------------------|:--------------|:--------|:------------------------------------------------------------------------------------------------------------|
|
||||
| `--build-arg` | `stringArray` | | Set build-time variables for services. |
|
||||
| `--builder` | `string` | | Set builder to use. |
|
||||
| `--dry-run` | | | Execute command in dry run mode |
|
||||
| `-m`, `--memory` | `bytes` | `0` | Set memory limit for the build container. Not supported by BuildKit. |
|
||||
| `--no-cache` | | | Do not use cache when building the image |
|
||||
| `--pull` | | | Always attempt to pull a newer version of the image. |
|
||||
| `--push` | | | Push service images. |
|
||||
| `-q`, `--quiet` | | | Don't print anything to STDOUT |
|
||||
| `--ssh` | `string` | | Set SSH authentications used when building service images. (use 'default' for using your default SSH Agent) |
|
||||
| `--with-dependencies` | | | Also build dependencies (transitively). |
|
||||
|
||||
|
||||
<!---MARKER_GEN_END-->
|
||||
|
||||
@@ -31,6 +31,6 @@ Parse, resolve and render compose file in canonical format
|
||||
|
||||
## Description
|
||||
|
||||
`docker compose config` renders the actual data model to be applied on the Docker engine.
|
||||
it merges the Compose files set by `-f` flags, resolves variables in the Compose file, and expands short-notation into
|
||||
`docker compose config` renders the actual data model to be applied on the Docker Engine.
|
||||
It merges the Compose files set by `-f` flags, resolves variables in the Compose file, and expands short-notation into
|
||||
the canonical format.
|
||||
|
||||
@@ -12,7 +12,7 @@ Creates containers for a service.
|
||||
| `--force-recreate` | | | Recreate containers even if their configuration and image haven't changed. |
|
||||
| `--no-build` | | | Don't build an image, even if it's policy. |
|
||||
| `--no-recreate` | | | If containers already exist, don't recreate them. Incompatible with --force-recreate. |
|
||||
| `--pull` | `string` | `policy` | Pull image before running ("always"\|"policy"\|"never") |
|
||||
| `--pull` | `string` | `policy` | Pull image before running ("always"\|"missing"\|"never"\|"build") |
|
||||
| `--remove-orphans` | | | Remove containers for services not defined in the Compose file. |
|
||||
| `--scale` | `stringArray` | | Scale SERVICE to NUM instances. Overrides the `scale` setting in the Compose file if present. |
|
||||
|
||||
|
||||
@@ -22,12 +22,12 @@ Stops containers and removes containers, networks, volumes, and images created b
|
||||
|
||||
By default, the only things removed are:
|
||||
|
||||
- Containers for services defined in the Compose file
|
||||
- Networks defined in the networks section of the Compose file
|
||||
- The default network, if one is used
|
||||
- Containers for services defined in the Compose file.
|
||||
- Networks defined in the networks section of the Compose file.
|
||||
- The default network, if one is used.
|
||||
|
||||
Networks and volumes defined as external are never removed.
|
||||
|
||||
Anonymous volumes are not removed by default. However, as they don’t have a stable name, they will not be automatically
|
||||
Anonymous volumes are not removed by default. However, as they don’t have a stable name, they are not automatically
|
||||
mounted by a subsequent `up`. For data that needs to persist between updates, use explicit paths as bind mounts or
|
||||
named volumes.
|
||||
|
||||
@@ -33,4 +33,4 @@ With the `--json` flag, a json object is printed one per line with the format:
|
||||
}
|
||||
```
|
||||
|
||||
The events that can be received using this can be seen [here](https://docs.docker.com/engine/reference/commandline/events/#object-types).
|
||||
The events that can be received using this can be seen [here](https://docs.docker.com/engine/reference/commandline/system_events/#object-types).
|
||||
|
||||
@@ -9,6 +9,7 @@ View output from containers
|
||||
|:---------------------|:---------|:--------|:-----------------------------------------------------------------------------------------------|
|
||||
| `--dry-run` | | | Execute command in dry run mode |
|
||||
| `-f`, `--follow` | | | Follow log output. |
|
||||
| `--index` | `int` | `0` | index of the container if service has multiple replicas |
|
||||
| `--no-color` | | | Produce monochrome output. |
|
||||
| `--no-log-prefix` | | | Don't print prefix in logs. |
|
||||
| `--since` | `string` | | Show logs since timestamp (e.g. 2013-01-02T13:23:37Z) or relative (e.g. 42m for 42 minutes) |
|
||||
|
||||
@@ -11,6 +11,8 @@ List containers
|
||||
| `--dry-run` | | | Execute command in dry run mode |
|
||||
| [`--filter`](#filter) | `string` | | Filter services by a property (supported filters: status). |
|
||||
| [`--format`](#format) | `string` | `table` | Format output using a custom template:<br>'table': Print output in table format with column headers (default)<br>'table TEMPLATE': Print output in table format using the given Go template<br>'json': Print in JSON format<br>'TEMPLATE': Print output using the given Go template.<br>Refer to https://docs.docker.com/go/formatting/ for more information about formatting output with templates |
|
||||
| `--no-trunc` | | | Don't truncate output |
|
||||
| `--orphans` | | | Include orphaned services (not declared by project) |
|
||||
| `-q`, `--quiet` | | | Only display IDs |
|
||||
| `--services` | | | Display services |
|
||||
| [`--status`](#status) | `stringArray` | | Filter services by status. Values: [paused \| restarting \| removing \| running \| dead \| created \| exited] |
|
||||
@@ -28,7 +30,7 @@ NAME IMAGE COMMAND SERVICE CREATED STATUS
|
||||
example-foo-1 alpine "/entrypoint.…" foo 4 seconds ago Up 2 seconds 0.0.0.0:8080->80/tcp
|
||||
```
|
||||
|
||||
By default, only running containers are shown. `--all` flag can be used to include stopped containers
|
||||
By default, only running containers are shown. `--all` flag can be used to include stopped containers.
|
||||
|
||||
```console
|
||||
$ docker compose ps --all
|
||||
@@ -52,7 +54,7 @@ $ docker compose ps --format json
|
||||
```
|
||||
|
||||
The JSON output allows you to use the information in other tools for further
|
||||
processing, for example, using the [`jq` utility](https://stedolan.github.io/jq/){:target="_blank" rel="noopener" class="_"}
|
||||
processing, for example, using the [`jq` utility](https://stedolan.github.io/jq/)
|
||||
to pretty-print the JSON:
|
||||
|
||||
```console
|
||||
|
||||
@@ -25,7 +25,7 @@ those images.
|
||||
|
||||
## Examples
|
||||
|
||||
suppose you have this `compose.yaml`:
|
||||
Consider the following `compose.yaml`:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
@@ -66,5 +66,4 @@ $ docker compose pull db
|
||||
⠹ c8752d5b785c Waiting 9.3s
|
||||
```
|
||||
|
||||
`docker compose pull` will try to pull image for services with a build section. If pull fails, it will let
|
||||
user know this service image MUST be built. You can skip this by setting `--ignore-buildable` flag
|
||||
`docker compose pull` tries to pull image for services with a build section. If pull fails, it lets you know this service image must be built. You can skip this by setting `--ignore-buildable` flag.
|
||||
|
||||
@@ -23,6 +23,6 @@ after running this command. For example, changes to environment variables (which
|
||||
after a container is built, but before the container's command is executed) are not updated
|
||||
after restarting.
|
||||
|
||||
If you are looking to configure a service's restart policy, please refer to
|
||||
If you are looking to configure a service's restart policy, refer to
|
||||
[restart](https://github.com/compose-spec/compose-spec/blob/master/spec.md#restart)
|
||||
or [restart_policy](https://github.com/compose-spec/compose-spec/blob/master/deploy.md#restart_policy).
|
||||
|
||||
@@ -5,29 +5,29 @@ Run a one-off command on a service.
|
||||
|
||||
### Options
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
|:----------------------|:--------------|:--------|:----------------------------------------------------------------------------------|
|
||||
| `--build` | | | Build image before starting container. |
|
||||
| `--cap-add` | `list` | | Add Linux capabilities |
|
||||
| `--cap-drop` | `list` | | Drop Linux capabilities |
|
||||
| `-d`, `--detach` | | | Run container in background and print container ID |
|
||||
| `--dry-run` | | | Execute command in dry run mode |
|
||||
| `--entrypoint` | `string` | | Override the entrypoint of the image |
|
||||
| `-e`, `--env` | `stringArray` | | Set environment variables |
|
||||
| `-i`, `--interactive` | | | Keep STDIN open even if not attached. |
|
||||
| `-l`, `--label` | `stringArray` | | Add or override a label |
|
||||
| `--name` | `string` | | Assign a name to the container |
|
||||
| `-T`, `--no-TTY` | | | Disable pseudo-TTY allocation (default: auto-detected). |
|
||||
| `--no-deps` | | | Don't start linked services. |
|
||||
| `-p`, `--publish` | `stringArray` | | Publish a container's port(s) to the host. |
|
||||
| `--quiet-pull` | | | Pull without printing progress information. |
|
||||
| `--remove-orphans` | | | Remove containers for services not defined in the Compose file. |
|
||||
| `--rm` | | | Automatically remove the container when it exits |
|
||||
| `--service-ports` | | | Run command with the service's ports enabled and mapped to the host. |
|
||||
| `--use-aliases` | | | Use the service's network useAliases in the network(s) the container connects to. |
|
||||
| `-u`, `--user` | `string` | | Run as specified username or uid |
|
||||
| `-v`, `--volume` | `stringArray` | | Bind mount a volume. |
|
||||
| `-w`, `--workdir` | `string` | | Working directory inside the container |
|
||||
| Name | Type | Default | Description |
|
||||
|:------------------------|:--------------|:--------|:----------------------------------------------------------------------------------|
|
||||
| `--build` | | | Build image before starting container. |
|
||||
| `--cap-add` | `list` | | Add Linux capabilities |
|
||||
| `--cap-drop` | `list` | | Drop Linux capabilities |
|
||||
| `-d`, `--detach` | | | Run container in background and print container ID |
|
||||
| `--dry-run` | | | Execute command in dry run mode |
|
||||
| `--entrypoint` | `string` | | Override the entrypoint of the image |
|
||||
| `-e`, `--env` | `stringArray` | | Set environment variables |
|
||||
| `-i`, `--interactive` | | | Keep STDIN open even if not attached. |
|
||||
| `-l`, `--label` | `stringArray` | | Add or override a label |
|
||||
| `--name` | `string` | | Assign a name to the container |
|
||||
| `-T`, `--no-TTY` | | | Disable pseudo-TTY allocation (default: auto-detected). |
|
||||
| `--no-deps` | | | Don't start linked services. |
|
||||
| `-p`, `--publish` | `stringArray` | | Publish a container's port(s) to the host. |
|
||||
| `--quiet-pull` | | | Pull without printing progress information. |
|
||||
| `--remove-orphans` | | | Remove containers for services not defined in the Compose file. |
|
||||
| `--rm` | | | Automatically remove the container when it exits |
|
||||
| `-P`, `--service-ports` | | | Run command with all service's ports enabled and mapped to the host. |
|
||||
| `--use-aliases` | | | Use the service's network useAliases in the network(s) the container connects to. |
|
||||
| `-u`, `--user` | `string` | | Run as specified username or uid |
|
||||
| `-v`, `--volume` | `stringArray` | | Bind mount a volume. |
|
||||
| `-w`, `--workdir` | `string` | | Working directory inside the container |
|
||||
|
||||
|
||||
<!---MARKER_GEN_END-->
|
||||
|
||||
18
docs/reference/compose_stats.md
Normal file
18
docs/reference/compose_stats.md
Normal file
@@ -0,0 +1,18 @@
|
||||
# docker compose stats
|
||||
|
||||
<!---MARKER_GEN_START-->
|
||||
Display a live stream of container(s) resource usage statistics
|
||||
|
||||
### Options
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
|:--------------|:---------|:--------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `-a`, `--all` | | | Show all containers (default shows just running) |
|
||||
| `--dry-run` | | | Execute command in dry run mode |
|
||||
| `--format` | `string` | | Format output using a custom template:<br>'table': Print output in table format with column headers (default)<br>'table TEMPLATE': Print output in table format using the given Go template<br>'json': Print in JSON format<br>'TEMPLATE': Print output using the given Go template.<br>Refer to https://docs.docker.com/go/formatting/ for more information about formatting output with templates |
|
||||
| `--no-stream` | | | Disable streaming stats and only pull the first result |
|
||||
| `--no-trunc` | | | Do not truncate output |
|
||||
|
||||
|
||||
<!---MARKER_GEN_END-->
|
||||
|
||||
@@ -23,7 +23,7 @@ Create and start containers
|
||||
| `--no-log-prefix` | | | Don't print prefix in logs. |
|
||||
| `--no-recreate` | | | If containers already exist, don't recreate them. Incompatible with --force-recreate. |
|
||||
| `--no-start` | | | Don't start the services after creating them. |
|
||||
| `--pull` | `string` | `policy` | Pull image before running ("always"\|"policy"\|"never") |
|
||||
| `--pull` | `string` | `policy` | Pull image before running ("always"\|"missing"\|"never") |
|
||||
| `--quiet-pull` | | | Pull without printing progress information. |
|
||||
| `--remove-orphans` | | | Remove containers for services not defined in the Compose file. |
|
||||
| `-V`, `--renew-anon-volumes` | | | Recreate anonymous volumes instead of retrieving data from the previous containers. |
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
command: docker compose
|
||||
short: Docker Compose
|
||||
long: |-
|
||||
You can use compose subcommand, `docker compose [-f <arg>...] [options] [COMMAND] [ARGS...]`, to build and manage
|
||||
You can use the compose subcommand, `docker compose [-f <arg>...] [options] [COMMAND] [ARGS...]`, to build and manage
|
||||
multiple services in Docker containers.
|
||||
|
||||
### Use `-f` to specify the name and path of one or more Compose files
|
||||
@@ -90,16 +90,16 @@ long: |-
|
||||
### Use profiles to enable optional services
|
||||
|
||||
Use `--profile` to specify one or more active profiles
|
||||
Calling `docker compose --profile frontend up` will start the services with the profile `frontend` and services
|
||||
Calling `docker compose --profile frontend up` starts the services with the profile `frontend` and services
|
||||
without any specified profiles.
|
||||
You can also enable multiple profiles, e.g. with `docker compose --profile frontend --profile debug up` the profiles `frontend` and `debug` will be enabled.
|
||||
You can also enable multiple profiles, e.g. with `docker compose --profile frontend --profile debug up` the profiles `frontend` and `debug` is enabled.
|
||||
|
||||
Profiles can also be set by `COMPOSE_PROFILES` environment variable.
|
||||
|
||||
### Configuring parallelism
|
||||
|
||||
Use `--parallel` to specify the maximum level of parallelism for concurrent engine calls.
|
||||
Calling `docker compose --parallel 1 pull` will pull the pullable images defined in the Compose file
|
||||
Calling `docker compose --parallel 1 pull` pulls the pullable images defined in the Compose file
|
||||
one at a time. This can also be used to control build concurrency.
|
||||
|
||||
Parallelism can also be set by the `COMPOSE_PARALLEL_LIMIT` environment variable.
|
||||
@@ -115,7 +115,7 @@ long: |-
|
||||
|
||||
If flags are explicitly set on the command line, the associated environment variable is ignored.
|
||||
|
||||
Setting the `COMPOSE_IGNORE_ORPHANS` environment variable to `true` will stop docker compose from detecting orphaned
|
||||
Setting the `COMPOSE_IGNORE_ORPHANS` environment variable to `true` stops docker compose from detecting orphaned
|
||||
containers for the project.
|
||||
|
||||
### Use Dry Run mode to test your command
|
||||
@@ -146,6 +146,7 @@ usage: docker compose
|
||||
pname: docker
|
||||
plink: docker.yaml
|
||||
cname:
|
||||
- docker compose attach
|
||||
- docker compose build
|
||||
- docker compose config
|
||||
- docker compose cp
|
||||
@@ -167,6 +168,7 @@ cname:
|
||||
- docker compose run
|
||||
- docker compose scale
|
||||
- docker compose start
|
||||
- docker compose stats
|
||||
- docker compose stop
|
||||
- docker compose top
|
||||
- docker compose unpause
|
||||
@@ -175,6 +177,7 @@ cname:
|
||||
- docker compose wait
|
||||
- docker compose watch
|
||||
clink:
|
||||
- docker_compose_attach.yaml
|
||||
- docker_compose_build.yaml
|
||||
- docker_compose_config.yaml
|
||||
- docker_compose_cp.yaml
|
||||
@@ -196,6 +199,7 @@ clink:
|
||||
- docker_compose_run.yaml
|
||||
- docker_compose_scale.yaml
|
||||
- docker_compose_start.yaml
|
||||
- docker_compose_stats.yaml
|
||||
- docker_compose_stop.yaml
|
||||
- docker_compose_top.yaml
|
||||
- docker_compose_unpause.yaml
|
||||
|
||||
@@ -6,11 +6,9 @@ plink: docker_compose.yaml
|
||||
cname:
|
||||
- docker compose alpha publish
|
||||
- docker compose alpha viz
|
||||
- docker compose alpha watch
|
||||
clink:
|
||||
- docker_compose_alpha_publish.yaml
|
||||
- docker_compose_alpha_viz.yaml
|
||||
- docker_compose_alpha_watch.yaml
|
||||
inherited_options:
|
||||
- option: dry-run
|
||||
value_type: bool
|
||||
|
||||
@@ -4,6 +4,27 @@ long: Publish compose application
|
||||
usage: docker compose alpha publish [OPTIONS] [REPOSITORY]
|
||||
pname: docker compose alpha
|
||||
plink: docker_compose_alpha.yaml
|
||||
options:
|
||||
- option: oci-version
|
||||
value_type: string
|
||||
description: |
|
||||
OCI Image/Artifact specification version (automatically determined by default)
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
- option: resolve-image-digests
|
||||
value_type: bool
|
||||
default_value: "false"
|
||||
description: Pin image tags to digests.
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
inherited_options:
|
||||
- option: dry-run
|
||||
value_type: bool
|
||||
|
||||
66
docs/reference/docker_compose_attach.yaml
Normal file
66
docs/reference/docker_compose_attach.yaml
Normal file
@@ -0,0 +1,66 @@
|
||||
command: docker compose attach
|
||||
short: |
|
||||
Attach local standard input, output, and error streams to a service's running container.
|
||||
long: |
|
||||
Attach local standard input, output, and error streams to a service's running container.
|
||||
usage: docker compose attach [OPTIONS] SERVICE
|
||||
pname: docker compose
|
||||
plink: docker_compose.yaml
|
||||
options:
|
||||
- option: detach-keys
|
||||
value_type: string
|
||||
description: Override the key sequence for detaching from a container.
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
- option: index
|
||||
value_type: int
|
||||
default_value: "0"
|
||||
description: index of the container if service has multiple replicas.
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
- option: no-stdin
|
||||
value_type: bool
|
||||
default_value: "false"
|
||||
description: Do not attach STDIN
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
- option: sig-proxy
|
||||
value_type: bool
|
||||
default_value: "true"
|
||||
description: Proxy all received signals to the process
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
inherited_options:
|
||||
- option: dry-run
|
||||
value_type: bool
|
||||
default_value: "false"
|
||||
description: Execute command in dry run mode
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
|
||||
@@ -147,6 +147,16 @@ options:
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
- option: with-dependencies
|
||||
value_type: bool
|
||||
default_value: "false"
|
||||
description: Also build dependencies (transitively).
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
inherited_options:
|
||||
- option: dry-run
|
||||
value_type: bool
|
||||
|
||||
@@ -2,8 +2,8 @@ command: docker compose config
|
||||
aliases: docker compose config, docker compose convert
|
||||
short: Parse, resolve and render compose file in canonical format
|
||||
long: |-
|
||||
`docker compose config` renders the actual data model to be applied on the Docker engine.
|
||||
it merges the Compose files set by `-f` flags, resolves variables in the Compose file, and expands short-notation into
|
||||
`docker compose config` renders the actual data model to be applied on the Docker Engine.
|
||||
It merges the Compose files set by `-f` flags, resolves variables in the Compose file, and expands short-notation into
|
||||
the canonical format.
|
||||
usage: docker compose config [OPTIONS] [SERVICE...]
|
||||
pname: docker compose
|
||||
|
||||
@@ -50,7 +50,7 @@ options:
|
||||
- option: pull
|
||||
value_type: string
|
||||
default_value: policy
|
||||
description: Pull image before running ("always"|"policy"|"never")
|
||||
description: Pull image before running ("always"|"missing"|"never"|"build")
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
|
||||
@@ -5,13 +5,13 @@ long: |-
|
||||
|
||||
By default, the only things removed are:
|
||||
|
||||
- Containers for services defined in the Compose file
|
||||
- Networks defined in the networks section of the Compose file
|
||||
- The default network, if one is used
|
||||
- Containers for services defined in the Compose file.
|
||||
- Networks defined in the networks section of the Compose file.
|
||||
- The default network, if one is used.
|
||||
|
||||
Networks and volumes defined as external are never removed.
|
||||
|
||||
Anonymous volumes are not removed by default. However, as they don’t have a stable name, they will not be automatically
|
||||
Anonymous volumes are not removed by default. However, as they don’t have a stable name, they are not automatically
|
||||
mounted by a subsequent `up`. For data that needs to persist between updates, use explicit paths as bind mounts or
|
||||
named volumes.
|
||||
usage: docker compose down [OPTIONS] [SERVICES]
|
||||
|
||||
@@ -19,7 +19,7 @@ long: |-
|
||||
}
|
||||
```
|
||||
|
||||
The events that can be received using this can be seen [here](/engine/reference/commandline/events/#object-types).
|
||||
The events that can be received using this can be seen [here](/engine/reference/commandline/system_events/#object-types).
|
||||
usage: docker compose events [OPTIONS] [SERVICE...]
|
||||
pname: docker compose
|
||||
plink: docker_compose.yaml
|
||||
|
||||
@@ -16,6 +16,16 @@ options:
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
- option: index
|
||||
value_type: int
|
||||
default_value: "0"
|
||||
description: index of the container if service has multiple replicas
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
- option: no-color
|
||||
value_type: bool
|
||||
default_value: "false"
|
||||
|
||||
@@ -9,7 +9,7 @@ long: |-
|
||||
example-foo-1 alpine "/entrypoint.…" foo 4 seconds ago Up 2 seconds 0.0.0.0:8080->80/tcp
|
||||
```
|
||||
|
||||
By default, only running containers are shown. `--all` flag can be used to include stopped containers
|
||||
By default, only running containers are shown. `--all` flag can be used to include stopped containers.
|
||||
|
||||
```console
|
||||
$ docker compose ps --all
|
||||
@@ -60,6 +60,26 @@ options:
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
- option: no-trunc
|
||||
value_type: bool
|
||||
default_value: "false"
|
||||
description: Don't truncate output
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
- option: orphans
|
||||
value_type: bool
|
||||
default_value: "true"
|
||||
description: Include orphaned services (not declared by project)
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
- option: quiet
|
||||
shorthand: q
|
||||
value_type: bool
|
||||
@@ -118,7 +138,7 @@ examples: |-
|
||||
```
|
||||
|
||||
The JSON output allows you to use the information in other tools for further
|
||||
processing, for example, using the [`jq` utility](https://stedolan.github.io/jq/){:target="_blank" rel="noopener" class="_"}
|
||||
processing, for example, using the [`jq` utility](https://stedolan.github.io/jq/)
|
||||
to pretty-print the JSON:
|
||||
|
||||
```console
|
||||
|
||||
@@ -89,7 +89,7 @@ inherited_options:
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
examples: |-
|
||||
suppose you have this `compose.yaml`:
|
||||
Consider the following `compose.yaml`:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
@@ -130,8 +130,7 @@ examples: |-
|
||||
⠹ c8752d5b785c Waiting 9.3s
|
||||
```
|
||||
|
||||
`docker compose pull` will try to pull image for services with a build section. If pull fails, it will let
|
||||
user know this service image MUST be built. You can skip this by setting `--ignore-buildable` flag
|
||||
`docker compose pull` tries to pull image for services with a build section. If pull fails, it lets you know this service image must be built. You can skip this by setting `--ignore-buildable` flag.
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
|
||||
@@ -8,7 +8,7 @@ long: |-
|
||||
after a container is built, but before the container's command is executed) are not updated
|
||||
after restarting.
|
||||
|
||||
If you are looking to configure a service's restart policy, please refer to
|
||||
If you are looking to configure a service's restart policy, refer to
|
||||
[restart](https://github.com/compose-spec/compose-spec/blob/master/spec.md#restart)
|
||||
or [restart_policy](https://github.com/compose-spec/compose-spec/blob/master/deploy.md#restart_policy).
|
||||
usage: docker compose restart [OPTIONS] [SERVICE...]
|
||||
|
||||
@@ -211,10 +211,11 @@ options:
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
- option: service-ports
|
||||
shorthand: P
|
||||
value_type: bool
|
||||
default_value: "false"
|
||||
description: |
|
||||
Run command with the service's ports enabled and mapped to the host.
|
||||
Run command with all service's ports enabled and mapped to the host.
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
|
||||
71
docs/reference/docker_compose_stats.yaml
Normal file
71
docs/reference/docker_compose_stats.yaml
Normal file
@@ -0,0 +1,71 @@
|
||||
command: docker compose stats
|
||||
short: Display a live stream of container(s) resource usage statistics
|
||||
long: Display a live stream of container(s) resource usage statistics
|
||||
usage: docker compose stats [OPTIONS] [SERVICE]
|
||||
pname: docker compose
|
||||
plink: docker_compose.yaml
|
||||
options:
|
||||
- option: all
|
||||
shorthand: a
|
||||
value_type: bool
|
||||
default_value: "false"
|
||||
description: Show all containers (default shows just running)
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
- option: format
|
||||
value_type: string
|
||||
description: |-
|
||||
Format output using a custom template:
|
||||
'table': Print output in table format with column headers (default)
|
||||
'table TEMPLATE': Print output in table format using the given Go template
|
||||
'json': Print in JSON format
|
||||
'TEMPLATE': Print output using the given Go template.
|
||||
Refer to https://docs.docker.com/go/formatting/ for more information about formatting output with templates
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
- option: no-stream
|
||||
value_type: bool
|
||||
default_value: "false"
|
||||
description: Disable streaming stats and only pull the first result
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
- option: no-trunc
|
||||
value_type: bool
|
||||
default_value: "false"
|
||||
description: Do not truncate output
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
inherited_options:
|
||||
- option: dry-run
|
||||
value_type: bool
|
||||
default_value: "false"
|
||||
description: Execute command in dry run mode
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
|
||||
@@ -182,7 +182,7 @@ options:
|
||||
- option: pull
|
||||
value_type: string
|
||||
default_value: policy
|
||||
description: Pull image before running ("always"|"policy"|"never")
|
||||
description: Pull image before running ("always"|"missing"|"never")
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
Feature: Build Contexts
|
||||
|
||||
Background:
|
||||
Given a compose file
|
||||
"""
|
||||
services:
|
||||
a:
|
||||
build:
|
||||
context: .
|
||||
dockerfile_inline: |
|
||||
# syntax=docker/dockerfile:1
|
||||
FROM alpine:latest
|
||||
COPY --from=dep /etc/hostname /
|
||||
additional_contexts:
|
||||
- dep=docker-image://ubuntu:latest
|
||||
"""
|
||||
|
||||
Scenario: Build w/ build context
|
||||
When I run "compose build"
|
||||
Then the exit code is 0
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
Feature: Down
|
||||
|
||||
Scenario: No resources to remove
|
||||
When I run "compose down"
|
||||
Then the output contains "Warning: No resource found to remove for project "no_resources_to_remove""
|
||||
@@ -1,28 +0,0 @@
|
||||
Feature: Report port conflicts
|
||||
|
||||
Background:
|
||||
Given a compose file
|
||||
"""
|
||||
services:
|
||||
web:
|
||||
image: nginx
|
||||
ports:
|
||||
- 31415:80
|
||||
"""
|
||||
And I run "docker rm -f nginx-pi-31415"
|
||||
|
||||
Scenario: Reports a port allocation conflict with another container
|
||||
Given I run "docker run -d -p 31415:80 --name nginx-pi-31415 nginx"
|
||||
When I run "compose up -d"
|
||||
Then the output contains "port is already allocated"
|
||||
And the exit code is 1
|
||||
|
||||
Scenario: Reports a port conflict with some other process
|
||||
Given a process listening on port 31415
|
||||
When I run "compose up -d"
|
||||
Then the output contains "address already in use"
|
||||
And the exit code is 1
|
||||
|
||||
Scenario: Cleanup
|
||||
Given I run "docker rm -f nginx-pi-31415"
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
Feature: PS
|
||||
|
||||
Background:
|
||||
Given a compose file
|
||||
"""
|
||||
services:
|
||||
build:
|
||||
image: test:latest
|
||||
build:
|
||||
context: ./
|
||||
pull:
|
||||
image: alpine
|
||||
command: top
|
||||
"""
|
||||
And a dockerfile
|
||||
"""
|
||||
FROM golang:1.19-alpine
|
||||
"""
|
||||
And I run "docker rm -f external-test"
|
||||
|
||||
Scenario: external container from compose image exists
|
||||
When I run "compose build"
|
||||
Then the exit code is 0
|
||||
And I run "docker run --name external-test test:latest ls"
|
||||
Then the exit code is 0
|
||||
And I run "compose ps -a"
|
||||
Then the output does not contain "external-test"
|
||||
And I run "docker rm -f external-test"
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
Feature: Simple service up
|
||||
|
||||
Background:
|
||||
Given a compose file
|
||||
"""
|
||||
services:
|
||||
simple:
|
||||
image: alpine
|
||||
command: top
|
||||
"""
|
||||
|
||||
Scenario: compose up
|
||||
When I run "compose up -d"
|
||||
Then the output contains "simple-1 Started"
|
||||
And service "simple" is "Up"
|
||||
@@ -1,21 +0,0 @@
|
||||
Feature: Start
|
||||
|
||||
Background:
|
||||
Given a compose file
|
||||
"""
|
||||
services:
|
||||
simple:
|
||||
image: alpine
|
||||
command: top
|
||||
another:
|
||||
image: alpine
|
||||
command: top
|
||||
"""
|
||||
|
||||
Scenario: Start single service
|
||||
When I run "compose create"
|
||||
Then the output contains "simple-1 Created"
|
||||
And the output contains "another-1 Created"
|
||||
Then I run "compose start another"
|
||||
And service "another" is "Up"
|
||||
And service "simple" is "Created"
|
||||
@@ -1,38 +0,0 @@
|
||||
Feature: Stop
|
||||
|
||||
Background:
|
||||
Given a compose file
|
||||
"""
|
||||
services:
|
||||
should_fail:
|
||||
image: alpine
|
||||
command: ['sh', '-c', 'exit 123']
|
||||
sleep: # will be killed
|
||||
image: alpine
|
||||
command: ping localhost
|
||||
init: true
|
||||
"""
|
||||
|
||||
Scenario: Cascade stop
|
||||
When I run "compose up --abort-on-container-exit"
|
||||
Then the output contains "should_fail-1 exited with code 123"
|
||||
And the output contains "Aborting on container exit..."
|
||||
And the exit code is 123
|
||||
|
||||
Scenario: Exit code from
|
||||
When I run "compose up --exit-code-from should_fail"
|
||||
Then the output contains "should_fail-1 exited with code 123"
|
||||
And the output contains "Aborting on container exit..."
|
||||
And the exit code is 123
|
||||
|
||||
# TODO: this is currently not working propagating the exit code properly
|
||||
#Scenario: Exit code from (cascade stop)
|
||||
# When I run "compose up --exit-code-from sleep"
|
||||
# Then the output contains "should_fail-1 exited with code 123"
|
||||
# And the output contains "Aborting on container exit..."
|
||||
# And the exit code is 143
|
||||
|
||||
Scenario: Exit code from unknown service
|
||||
When I run "compose up --exit-code-from unknown"
|
||||
Then the output contains "no such service: unknown"
|
||||
And the exit code is 1
|
||||
@@ -1,17 +0,0 @@
|
||||
Feature: Up
|
||||
|
||||
Background:
|
||||
Given a compose file
|
||||
"""
|
||||
services:
|
||||
simple:
|
||||
image: alpine
|
||||
command: top
|
||||
"""
|
||||
|
||||
Scenario: --pull always
|
||||
When I run "compose up --pull=always -d"
|
||||
And the output contains "simple Pulled"
|
||||
Then I run "compose up --pull=always -d"
|
||||
And the output contains "simple Pulled"
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
Feature: Volume: tmpfs
|
||||
|
||||
Background:
|
||||
Given a compose file
|
||||
"""
|
||||
services:
|
||||
svc:
|
||||
image: busybox
|
||||
volumes:
|
||||
- type: tmpfs
|
||||
target: /volumes/tmpfs
|
||||
tmpfs:
|
||||
size: 2M
|
||||
mode: 0o647
|
||||
"""
|
||||
|
||||
Scenario: tmpfs Permissions Set
|
||||
When I run "compose run --rm svc stat -c "%a" /volumes/tmpfs"
|
||||
Then the output contains "647"
|
||||
|
||||
Scenario: tmpfs Size Set
|
||||
When I run "compose run --rm svc sh -c 'df /volumes/tmpfs | tail -n1 | awk '"'"'{print $4}'"'"'' "
|
||||
Then the output contains "2048"
|
||||
@@ -1,191 +0,0 @@
|
||||
/*
|
||||
Copyright 2022 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 cucumber
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/compose-spec/compose-go/loader"
|
||||
"github.com/cucumber/godog"
|
||||
"github.com/cucumber/godog/colors"
|
||||
"github.com/mattn/go-shellwords"
|
||||
"gotest.tools/v3/icmd"
|
||||
|
||||
"github.com/docker/compose/v2/pkg/e2e"
|
||||
)
|
||||
|
||||
func TestCucumber(t *testing.T) {
|
||||
testingOptions := godog.Options{
|
||||
TestingT: t,
|
||||
Paths: []string{"./cucumber-features"},
|
||||
Output: colors.Colored(os.Stdout),
|
||||
Format: "pretty",
|
||||
}
|
||||
|
||||
status := godog.TestSuite{
|
||||
Name: "godogs",
|
||||
Options: &testingOptions,
|
||||
ScenarioInitializer: setup,
|
||||
}.Run()
|
||||
|
||||
if status == 2 {
|
||||
t.SkipNow()
|
||||
}
|
||||
|
||||
if status != 0 {
|
||||
t.Fatalf("zero status code expected, %d received", status)
|
||||
}
|
||||
}
|
||||
|
||||
func setup(s *godog.ScenarioContext) {
|
||||
t := s.TestingT()
|
||||
projectName := loader.NormalizeProjectName(strings.Split(t.Name(), "/")[1])
|
||||
cli := e2e.NewCLI(t, e2e.WithEnv(
|
||||
fmt.Sprintf("COMPOSE_PROJECT_NAME=%s", projectName),
|
||||
))
|
||||
th := testHelper{
|
||||
T: t,
|
||||
CLI: cli,
|
||||
ProjectName: projectName,
|
||||
}
|
||||
|
||||
s.Before(func(ctx context.Context, sc *godog.Scenario) (context.Context, error) {
|
||||
cli.RunDockerComposeCmd(t, "down", "--remove-orphans", "-v", "-t", "0")
|
||||
return ctx, nil
|
||||
})
|
||||
|
||||
s.After(func(ctx context.Context, sc *godog.Scenario, err error) (context.Context, error) {
|
||||
cli.RunDockerComposeCmd(t, "down", "--remove-orphans", "-v", "-t", "0")
|
||||
return ctx, nil
|
||||
})
|
||||
|
||||
s.Step(`^a compose file$`, th.setComposeFile)
|
||||
s.Step(`^a dockerfile$`, th.setDockerfile)
|
||||
s.Step(`^I run "compose (.*)"$`, th.runComposeCommand)
|
||||
s.Step(`^I run "docker (.*)"$`, th.runDockerCommand)
|
||||
s.Step(`service "(.*)" is "(.*)"$`, th.serviceIsStatus)
|
||||
s.Step(`output contains "(.*)"$`, th.outputContains(true))
|
||||
s.Step(`output does not contain "(.*)"$`, th.outputContains(false))
|
||||
s.Step(`exit code is (\d+)$`, th.exitCodeIs)
|
||||
s.Step(`a process listening on port (\d+)$`, th.listenerOnPort)
|
||||
}
|
||||
|
||||
type testHelper struct {
|
||||
T *testing.T
|
||||
ProjectName string
|
||||
ComposeFile string
|
||||
TestDir string
|
||||
CommandOutput string
|
||||
CommandExitCode int
|
||||
CLI *e2e.CLI
|
||||
}
|
||||
|
||||
func (th *testHelper) serviceIsStatus(service, status string) error {
|
||||
serviceContainerName := fmt.Sprintf("%s-%s-1", strings.ToLower(th.ProjectName), service)
|
||||
statusRegex := fmt.Sprintf("%s.*%s", serviceContainerName, status)
|
||||
res := th.CLI.RunDockerComposeCmd(th.T, "ps", "-a")
|
||||
r, _ := regexp.Compile(statusRegex)
|
||||
if !r.MatchString(res.Combined()) {
|
||||
return fmt.Errorf("Missing/incorrect ps output:\n%s\nregex:\n%s", res.Combined(), statusRegex)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (th *testHelper) outputContains(expected bool) func(string) error {
|
||||
return func(substring string) error {
|
||||
contains := strings.Contains(th.CommandOutput, substring)
|
||||
if contains && !expected {
|
||||
return fmt.Errorf("Unexpected substring in output: %s\noutput: %s", substring, th.CommandOutput)
|
||||
} else if !contains && expected {
|
||||
return fmt.Errorf("Missing substring in output: %s\noutput: %s", substring, th.CommandOutput)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (th *testHelper) exitCodeIs(exitCode int) error {
|
||||
if exitCode != th.CommandExitCode {
|
||||
return fmt.Errorf("Wrong exit code: %d expected: %d || command output: %s", th.CommandExitCode, exitCode, th.CommandOutput)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (th *testHelper) runComposeCommand(command string) error {
|
||||
commandArgs, err := shellwords.Parse(command)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
commandArgs = append([]string{"-f", "-"}, commandArgs...)
|
||||
|
||||
cmd := th.CLI.NewDockerComposeCmd(th.T, commandArgs...)
|
||||
cmd.Stdin = strings.NewReader(th.ComposeFile)
|
||||
cmd.Dir = th.TestDir
|
||||
res := icmd.RunCmd(cmd)
|
||||
th.CommandOutput = res.Combined()
|
||||
th.CommandExitCode = res.ExitCode
|
||||
return nil
|
||||
}
|
||||
|
||||
func (th *testHelper) runDockerCommand(command string) error {
|
||||
commandArgs, err := shellwords.Parse(command)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd := th.CLI.NewDockerCmd(th.T, commandArgs...)
|
||||
cmd.Dir = th.TestDir
|
||||
res := icmd.RunCmd(cmd)
|
||||
th.CommandOutput = res.Combined()
|
||||
th.CommandExitCode = res.ExitCode
|
||||
return nil
|
||||
}
|
||||
|
||||
func (th *testHelper) setComposeFile(composeString string) error {
|
||||
th.ComposeFile = composeString
|
||||
return nil
|
||||
}
|
||||
|
||||
func (th *testHelper) setDockerfile(dockerfileString string) error {
|
||||
tempDir := th.T.TempDir()
|
||||
th.TestDir = tempDir
|
||||
|
||||
err := os.WriteFile(filepath.Join(tempDir, "Dockerfile"), []byte(dockerfileString), 0o644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (th *testHelper) listenerOnPort(port int) error {
|
||||
l, err := net.Listen("tcp", fmt.Sprintf(":%d", port))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
th.T.Cleanup(func() {
|
||||
_ = l.Close()
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
141
go.mod
141
go.mod
@@ -5,60 +5,58 @@ go 1.21
|
||||
require (
|
||||
github.com/AlecAivazis/survey/v2 v2.3.7
|
||||
github.com/Microsoft/go-winio v0.6.1
|
||||
github.com/adrg/xdg v0.4.0
|
||||
github.com/buger/goterm v1.0.4
|
||||
github.com/compose-spec/compose-go v1.19.0
|
||||
github.com/compose-spec/compose-go/v2 v2.0.0-rc.2
|
||||
github.com/containerd/console v1.0.3
|
||||
github.com/containerd/containerd v1.7.6
|
||||
github.com/cucumber/godog v0.0.0-00010101000000-000000000000 // replaced; see replace for the actual version used
|
||||
github.com/containerd/containerd v1.7.12
|
||||
github.com/davecgh/go-spew v1.1.1
|
||||
github.com/distribution/reference v0.5.0
|
||||
github.com/docker/buildx v0.11.2
|
||||
github.com/docker/cli v24.0.6+incompatible
|
||||
github.com/docker/buildx v0.12.0-rc2.0.20231219140829-617f538cb315
|
||||
github.com/docker/cli v25.0.0+incompatible
|
||||
github.com/docker/cli-docs-tool v0.6.0
|
||||
github.com/docker/docker v24.0.6+incompatible
|
||||
github.com/docker/go-connections v0.4.0
|
||||
github.com/docker/docker v25.0.0+incompatible
|
||||
github.com/docker/go-connections v0.5.0
|
||||
github.com/docker/go-units v0.5.0
|
||||
github.com/fsnotify/fsevents v0.1.1
|
||||
github.com/golang/mock v1.6.0
|
||||
github.com/google/go-cmp v0.5.9
|
||||
github.com/google/go-cmp v0.6.0
|
||||
github.com/hashicorp/go-multierror v1.1.1
|
||||
github.com/hashicorp/go-version v1.6.0
|
||||
github.com/jonboulle/clockwork v0.4.0
|
||||
github.com/mattn/go-shellwords v1.0.12
|
||||
github.com/mitchellh/mapstructure v1.5.0
|
||||
github.com/moby/buildkit v0.12.2
|
||||
github.com/moby/buildkit v0.13.0-beta1.0.20231219135447-957cb50df991
|
||||
github.com/moby/patternmatcher v0.6.0
|
||||
github.com/moby/term v0.5.0
|
||||
github.com/morikuni/aec v1.0.0
|
||||
github.com/opencontainers/go-digest v1.0.0
|
||||
github.com/opencontainers/image-spec v1.1.0-rc5
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/otiai10/copy v1.14.0
|
||||
github.com/sirupsen/logrus v1.9.3
|
||||
github.com/spf13/cobra v1.7.0
|
||||
github.com/spf13/cobra v1.8.0
|
||||
github.com/spf13/pflag v1.0.5
|
||||
github.com/stretchr/testify v1.8.4
|
||||
github.com/theupdateframework/notary v0.7.0
|
||||
github.com/tilt-dev/fsnotify v1.4.8-0.20220602155310-fff9c274a375
|
||||
go.opentelemetry.io/otel v1.14.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.14.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.14.0
|
||||
go.opentelemetry.io/otel/sdk v1.14.0
|
||||
go.opentelemetry.io/otel/trace v1.14.0
|
||||
go.uber.org/goleak v1.2.1
|
||||
golang.org/x/sync v0.3.0
|
||||
google.golang.org/grpc v1.58.1
|
||||
go.opentelemetry.io/otel v1.19.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.19.0
|
||||
go.opentelemetry.io/otel/sdk v1.19.0
|
||||
go.opentelemetry.io/otel/trace v1.19.0
|
||||
go.uber.org/goleak v1.3.0
|
||||
go.uber.org/mock v0.4.0
|
||||
golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1
|
||||
golang.org/x/sync v0.6.0
|
||||
golang.org/x/sys v0.16.0
|
||||
google.golang.org/grpc v1.59.0
|
||||
gotest.tools/v3 v3.5.1
|
||||
)
|
||||
|
||||
require golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1
|
||||
|
||||
require (
|
||||
dario.cat/mergo v1.0.0 // indirect
|
||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 // indirect
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
|
||||
github.com/Masterminds/semver/v3 v3.2.1 // indirect
|
||||
github.com/Microsoft/hcsshim v0.11.0 // indirect
|
||||
github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d // indirect
|
||||
github.com/Microsoft/hcsshim v0.11.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2 v1.17.6 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/config v1.18.16 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.13.16 // indirect
|
||||
@@ -72,18 +70,17 @@ require (
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.18.6 // indirect
|
||||
github.com/aws/smithy-go v1.13.5 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.2.0 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.2.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||
github.com/containerd/continuity v0.4.2 // indirect
|
||||
github.com/containerd/log v0.1.0 // indirect
|
||||
github.com/containerd/typeurl/v2 v2.1.1 // indirect
|
||||
github.com/cucumber/gherkin-go/v19 v19.0.3 // indirect
|
||||
github.com/cucumber/messages-go/v16 v16.0.1 // indirect
|
||||
github.com/docker/distribution v2.8.2+incompatible // indirect
|
||||
github.com/docker/docker-credential-helpers v0.7.0 // indirect
|
||||
github.com/docker/distribution v2.8.3+incompatible // indirect
|
||||
github.com/docker/docker-credential-helpers v0.8.0 // indirect
|
||||
github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c // indirect
|
||||
github.com/docker/go-metrics v0.0.1 // indirect
|
||||
github.com/emicklei/go-restful/v3 v3.10.1 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.3 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/fvbommel/sortorder v1.0.2 // indirect
|
||||
github.com/go-logr/logr v1.2.4 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
@@ -91,99 +88,97 @@ require (
|
||||
github.com/go-openapi/jsonreference v0.20.0 // indirect
|
||||
github.com/go-openapi/swag v0.19.14 // indirect
|
||||
github.com/gofrs/flock v0.8.1 // indirect
|
||||
github.com/gofrs/uuid v4.2.0+incompatible // indirect
|
||||
github.com/gogo/googleapis v1.4.1 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
github.com/google/gnostic v0.5.7-v3refs // indirect
|
||||
github.com/google/gofuzz v1.2.0 // indirect
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
|
||||
github.com/gorilla/mux v1.8.0 // indirect
|
||||
github.com/google/uuid v1.5.0 // indirect
|
||||
github.com/gorilla/mux v1.8.1 // indirect
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/hashicorp/go-immutable-radix v1.3.1 // indirect
|
||||
github.com/hashicorp/go-memdb v1.3.2 // indirect
|
||||
github.com/hashicorp/golang-lru v0.5.4 // indirect
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||
github.com/imdario/mergo v0.3.16 // indirect
|
||||
github.com/in-toto/in-toto-golang v0.5.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
|
||||
github.com/klauspost/compress v1.16.5 // indirect
|
||||
github.com/klauspost/compress v1.17.4 // indirect
|
||||
github.com/mailru/easyjson v0.7.6 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.17 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.14 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.15 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
|
||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect
|
||||
github.com/miekg/pkcs11 v1.1.1 // indirect
|
||||
github.com/mitchellh/copystructure v1.2.0 // indirect
|
||||
github.com/mitchellh/reflectwalk v1.0.2 // indirect
|
||||
github.com/moby/locker v1.0.1 // indirect
|
||||
github.com/moby/spdystream v0.2.0 // indirect
|
||||
github.com/moby/sys/mountinfo v0.6.2 // indirect
|
||||
github.com/moby/sys/mountinfo v0.7.1 // indirect
|
||||
github.com/moby/sys/sequential v0.5.0 // indirect
|
||||
github.com/moby/sys/signal v0.7.0 // indirect
|
||||
github.com/moby/sys/symlink v0.2.0 // indirect
|
||||
github.com/moby/sys/user v0.1.0 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/opencontainers/runc v1.1.7 // indirect
|
||||
github.com/pelletier/go-toml v1.9.5 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.0.5 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/prometheus/client_golang v1.14.0 // indirect
|
||||
github.com/prometheus/client_model v0.3.0 // indirect
|
||||
github.com/prometheus/client_golang v1.16.0 // indirect
|
||||
github.com/prometheus/client_model v0.4.0 // indirect
|
||||
github.com/prometheus/common v0.42.0 // indirect
|
||||
github.com/prometheus/procfs v0.9.0 // indirect
|
||||
github.com/prometheus/procfs v0.10.1 // indirect
|
||||
github.com/rivo/uniseg v0.2.0 // indirect
|
||||
github.com/secure-systems-lab/go-securesystemslib v0.4.0 // indirect
|
||||
github.com/serialx/hashring v0.0.0-20190422032157-8b2912629002 // indirect
|
||||
github.com/shibumi/go-pathspec v1.3.0 // indirect
|
||||
github.com/spf13/afero v1.9.2 // indirect
|
||||
github.com/subosito/gotenv v1.4.1 // indirect
|
||||
github.com/tonistiigi/fsutil v0.0.0-20230629203738-36ef4d8c0dbb // indirect
|
||||
github.com/tonistiigi/fsutil v0.0.0-20230825212630-f09800878302 // indirect
|
||||
github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea // indirect
|
||||
github.com/tonistiigi/vt100 v0.0.0-20230623042737-f9a4f7ef6531 // indirect
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
|
||||
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.40.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.40.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.40.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.14.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.14.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v0.37.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v0.19.0 // indirect
|
||||
golang.org/x/crypto v0.11.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.45.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.45.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.45.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.42.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.42.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v0.42.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/prometheus v0.42.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.19.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk/metric v1.19.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.0.0 // indirect
|
||||
golang.org/x/crypto v0.17.0 // indirect
|
||||
golang.org/x/mod v0.11.0 // indirect
|
||||
golang.org/x/net v0.12.0 // indirect
|
||||
golang.org/x/oauth2 v0.10.0 // indirect
|
||||
golang.org/x/sys v0.10.0 // indirect
|
||||
golang.org/x/term v0.10.0 // indirect
|
||||
golang.org/x/text v0.11.0 // indirect
|
||||
golang.org/x/net v0.17.0 // indirect
|
||||
golang.org/x/oauth2 v0.11.0 // indirect
|
||||
golang.org/x/term v0.15.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
golang.org/x/time v0.3.0 // indirect
|
||||
golang.org/x/tools v0.7.0 // indirect
|
||||
golang.org/x/tools v0.10.0 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/genproto v0.0.0-20230711160842-782d3b101e98 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20230711160842-782d3b101e98 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 // indirect
|
||||
google.golang.org/genproto v0.0.0-20230822172742-b8732ec3820d // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d // indirect
|
||||
google.golang.org/protobuf v1.31.0 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
k8s.io/api v0.26.2 // indirect
|
||||
k8s.io/apimachinery v0.26.2 // indirect
|
||||
k8s.io/client-go v0.26.2 // indirect
|
||||
k8s.io/api v0.26.7 // indirect
|
||||
k8s.io/apimachinery v0.26.7 // indirect
|
||||
k8s.io/apiserver v0.26.7 // indirect
|
||||
k8s.io/client-go v0.26.7 // indirect
|
||||
k8s.io/klog/v2 v2.90.1 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280 // indirect
|
||||
k8s.io/utils v0.0.0-20230220204549-a5ecb0141aa5 // indirect
|
||||
sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect
|
||||
sigs.k8s.io/yaml v1.3.0 // indirect
|
||||
tags.cncf.io/container-device-interface v0.6.2 // indirect
|
||||
)
|
||||
|
||||
// Override for e2e tests
|
||||
replace github.com/cucumber/godog => github.com/laurazard/godog v0.0.0-20220922095256-4c4b17abdae7
|
||||
|
||||
@@ -19,8 +19,8 @@ package locker
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/adrg/xdg"
|
||||
"github.com/docker/docker/pkg/pidfile"
|
||||
)
|
||||
|
||||
@@ -29,10 +29,11 @@ type Pidfile struct {
|
||||
}
|
||||
|
||||
func NewPidfile(projectName string) (*Pidfile, error) {
|
||||
path, err := xdg.RuntimeFile(fmt.Sprintf("docker-compose.%s.pid", projectName))
|
||||
run, err := runDir()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
path := filepath.Join(run, fmt.Sprintf("%s.pid", projectName))
|
||||
return &Pidfile{path: path}, nil
|
||||
}
|
||||
|
||||
|
||||
35
internal/locker/runtime.go
Normal file
35
internal/locker/runtime.go
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
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 locker
|
||||
|
||||
import (
|
||||
"os"
|
||||
)
|
||||
|
||||
func runDir() (string, error) {
|
||||
run, ok := os.LookupEnv("XDG_RUNTIME_DIR")
|
||||
if ok {
|
||||
return run, nil
|
||||
}
|
||||
|
||||
path, err := osDependentRunDir()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
err = os.MkdirAll(path, 0o700)
|
||||
return path, err
|
||||
}
|
||||
34
internal/locker/runtime_darwin.go
Normal file
34
internal/locker/runtime_darwin.go
Normal file
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
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 locker
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
// Based on https://github.com/adrg/xdg
|
||||
// Licensed under MIT License (MIT)
|
||||
// Copyright (c) 2014 Adrian-George Bostan <adrg@epistack.com>
|
||||
|
||||
func osDependentRunDir() (string, error) {
|
||||
home, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return filepath.Join(home, "Library", "Application Support", "com.docker.compose"), nil
|
||||
}
|
||||
43
internal/locker/runtime_unix.go
Normal file
43
internal/locker/runtime_unix.go
Normal file
@@ -0,0 +1,43 @@
|
||||
//go:build linux
|
||||
|
||||
/*
|
||||
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 locker
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// Based on https://github.com/adrg/xdg
|
||||
// Licensed under MIT License (MIT)
|
||||
// Copyright (c) 2014 Adrian-George Bostan <adrg@epistack.com>
|
||||
|
||||
func osDependentRunDir() (string, error) {
|
||||
run := filepath.Join("run", "user", strconv.Itoa(os.Getuid()))
|
||||
if _, err := os.Stat(run); err == nil {
|
||||
return run, nil
|
||||
}
|
||||
|
||||
// /run/user/$uid is set by pam_systemd, but might not be present, especially in containerized environments
|
||||
home, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return filepath.Join(home, ".docker", "docker-compose"), nil
|
||||
}
|
||||
49
internal/locker/runtime_windows.go
Normal file
49
internal/locker/runtime_windows.go
Normal file
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
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 locker
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
// Based on https://github.com/adrg/xdg
|
||||
// Licensed under MIT License (MIT)
|
||||
// Copyright (c) 2014 Adrian-George Bostan <adrg@epistack.com>
|
||||
|
||||
func osDependentRunDir() (string, error) {
|
||||
flags := []uint32{windows.KF_FLAG_DEFAULT, windows.KF_FLAG_DEFAULT_PATH}
|
||||
for _, flag := range flags {
|
||||
p, _ := windows.KnownFolderPath(windows.FOLDERID_LocalAppData, flag|windows.KF_FLAG_DONT_VERIFY)
|
||||
if p != "" {
|
||||
return filepath.Join(p, "docker-compose"), nil
|
||||
}
|
||||
}
|
||||
|
||||
appData, ok := os.LookupEnv("LOCALAPPDATA")
|
||||
if ok {
|
||||
return filepath.Join(appData, "docker-compose"), nil
|
||||
}
|
||||
|
||||
home, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return filepath.Join(home, "AppData", "Local", "docker-compose"), nil
|
||||
}
|
||||
212
internal/ocipush/push.go
Normal file
212
internal/ocipush/push.go
Normal file
@@ -0,0 +1,212 @@
|
||||
/*
|
||||
Copyright 2023 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 ocipush
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
pusherrors "github.com/containerd/containerd/remotes/errors"
|
||||
"github.com/distribution/reference"
|
||||
"github.com/docker/buildx/util/imagetools"
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
"github.com/opencontainers/go-digest"
|
||||
"github.com/opencontainers/image-spec/specs-go"
|
||||
v1 "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
)
|
||||
|
||||
const (
|
||||
// ComposeProjectArtifactType is the OCI 1.1-compliant artifact type value
|
||||
// for the generated image manifest.
|
||||
ComposeProjectArtifactType = "application/vnd.docker.compose.project"
|
||||
// ComposeYAMLMediaType is the media type for each layer (Compose file)
|
||||
// in the image manifest.
|
||||
ComposeYAMLMediaType = "application/vnd.docker.compose.file+yaml"
|
||||
// ComposeEmptyConfigMediaType is a media type used for the config descriptor
|
||||
// when doing OCI 1.0-style pushes.
|
||||
//
|
||||
// The content is always `{}`, the same as a normal empty descriptor, but
|
||||
// the specific media type allows clients to fall back to the config media
|
||||
// type to recognize the manifest as a Compose project since the artifact
|
||||
// type field is not available in OCI 1.0.
|
||||
//
|
||||
// This is based on guidance from the OCI 1.1 spec:
|
||||
// > Implementers note: artifacts have historically been created without
|
||||
// > an artifactType field, and tooling to work with artifacts should
|
||||
// > fallback to the config.mediaType value.
|
||||
ComposeEmptyConfigMediaType = "application/vnd.docker.compose.config.empty.v1+json"
|
||||
)
|
||||
|
||||
// clientAuthStatusCodes are client (4xx) errors that are authentication
|
||||
// related.
|
||||
var clientAuthStatusCodes = []int{
|
||||
http.StatusUnauthorized,
|
||||
http.StatusForbidden,
|
||||
http.StatusProxyAuthRequired,
|
||||
}
|
||||
|
||||
type Pushable struct {
|
||||
Descriptor v1.Descriptor
|
||||
Data []byte
|
||||
}
|
||||
|
||||
func DescriptorForComposeFile(path string, content []byte) v1.Descriptor {
|
||||
return v1.Descriptor{
|
||||
MediaType: ComposeYAMLMediaType,
|
||||
Digest: digest.FromString(string(content)),
|
||||
Size: int64(len(content)),
|
||||
Annotations: map[string]string{
|
||||
"com.docker.compose.version": api.ComposeVersion,
|
||||
"com.docker.compose.file": filepath.Base(path),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func PushManifest(
|
||||
ctx context.Context,
|
||||
resolver *imagetools.Resolver,
|
||||
named reference.Named,
|
||||
layers []Pushable,
|
||||
ociVersion api.OCIVersion,
|
||||
) error {
|
||||
// prepare to push the manifest by pushing the layers
|
||||
layerDescriptors := make([]v1.Descriptor, len(layers))
|
||||
for i := range layers {
|
||||
layerDescriptors[i] = layers[i].Descriptor
|
||||
if err := resolver.Push(ctx, named, layers[i].Descriptor, layers[i].Data); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if ociVersion != "" {
|
||||
// if a version was explicitly specified, use it
|
||||
return createAndPushManifest(ctx, resolver, named, layerDescriptors, ociVersion)
|
||||
}
|
||||
|
||||
// try to push in the OCI 1.1 format but fallback to OCI 1.0 on 4xx errors
|
||||
// (other than auth) since it's most likely the result of the registry not
|
||||
// having support
|
||||
err := createAndPushManifest(ctx, resolver, named, layerDescriptors, api.OCIVersion1_1)
|
||||
var pushErr pusherrors.ErrUnexpectedStatus
|
||||
if errors.As(err, &pushErr) && isNonAuthClientError(pushErr.StatusCode) {
|
||||
// TODO(milas): show a warning here (won't work with logrus)
|
||||
return createAndPushManifest(ctx, resolver, named, layerDescriptors, api.OCIVersion1_0)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func createAndPushManifest(
|
||||
ctx context.Context,
|
||||
resolver *imagetools.Resolver,
|
||||
named reference.Named,
|
||||
layers []v1.Descriptor,
|
||||
ociVersion api.OCIVersion,
|
||||
) error {
|
||||
toPush, err := generateManifest(layers, ociVersion)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, p := range toPush {
|
||||
err = resolver.Push(ctx, named, p.Descriptor, p.Data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func isNonAuthClientError(statusCode int) bool {
|
||||
if statusCode < 400 || statusCode >= 500 {
|
||||
// not a client error
|
||||
return false
|
||||
}
|
||||
for _, v := range clientAuthStatusCodes {
|
||||
if statusCode == v {
|
||||
// client auth error
|
||||
return false
|
||||
}
|
||||
}
|
||||
// any other 4xx client error
|
||||
return true
|
||||
}
|
||||
|
||||
func generateManifest(layers []v1.Descriptor, ociCompat api.OCIVersion) ([]Pushable, error) {
|
||||
var toPush []Pushable
|
||||
var config v1.Descriptor
|
||||
var artifactType string
|
||||
switch ociCompat {
|
||||
case api.OCIVersion1_0:
|
||||
// "Content other than OCI container images MAY be packaged using the image manifest.
|
||||
// When this is done, the config.mediaType value MUST be set to a value specific to
|
||||
// the artifact type or the empty value."
|
||||
// Source: https://github.com/opencontainers/image-spec/blob/main/manifest.md#guidelines-for-artifact-usage
|
||||
//
|
||||
// The `ComposeEmptyConfigMediaType` is used specifically for this purpose:
|
||||
// there is no config, and an empty descriptor is used for OCI 1.1 in
|
||||
// conjunction with the `ArtifactType`, but for OCI 1.0 compatibility,
|
||||
// tooling falls back to the config media type, so this is used to
|
||||
// indicate that it's not a container image but custom content.
|
||||
configData := []byte("{}")
|
||||
config = v1.Descriptor{
|
||||
MediaType: ComposeEmptyConfigMediaType,
|
||||
Digest: digest.FromBytes(configData),
|
||||
Size: int64(len(configData)),
|
||||
}
|
||||
// N.B. OCI 1.0 does NOT support specifying the artifact type, so it's
|
||||
// left as an empty string to omit it from the marshaled JSON
|
||||
artifactType = ""
|
||||
toPush = append(toPush, Pushable{Descriptor: config, Data: configData})
|
||||
case api.OCIVersion1_1:
|
||||
config = v1.DescriptorEmptyJSON
|
||||
artifactType = ComposeProjectArtifactType
|
||||
// N.B. the descriptor has the data embedded in it
|
||||
toPush = append(toPush, Pushable{Descriptor: config, Data: nil})
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported OCI version: %s", ociCompat)
|
||||
}
|
||||
|
||||
manifest, err := json.Marshal(v1.Manifest{
|
||||
Versioned: specs.Versioned{SchemaVersion: 2},
|
||||
MediaType: v1.MediaTypeImageManifest,
|
||||
ArtifactType: artifactType,
|
||||
Config: config,
|
||||
Layers: layers,
|
||||
Annotations: map[string]string{
|
||||
"org.opencontainers.image.created": time.Now().Format(time.RFC3339),
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
manifestDescriptor := v1.Descriptor{
|
||||
MediaType: v1.MediaTypeImageManifest,
|
||||
Digest: digest.FromString(string(manifest)),
|
||||
Size: int64(len(manifest)),
|
||||
Annotations: map[string]string{
|
||||
"com.docker.compose.version": api.ComposeVersion,
|
||||
},
|
||||
ArtifactType: artifactType,
|
||||
}
|
||||
toPush = append(toPush, Pushable{Descriptor: manifestDescriptor, Data: manifest})
|
||||
return toPush, nil
|
||||
}
|
||||
@@ -22,7 +22,7 @@ import (
|
||||
"io/fs"
|
||||
"os"
|
||||
|
||||
"github.com/compose-spec/compose-go/types"
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
@@ -62,10 +62,7 @@ func (d *DockerCopy) Sync(ctx context.Context, service types.ServiceConfig, path
|
||||
}
|
||||
|
||||
func (d *DockerCopy) sync(ctx context.Context, service types.ServiceConfig, pathMapping PathMapping) error {
|
||||
scale := 1
|
||||
if service.Deploy != nil && service.Deploy.Replicas != nil {
|
||||
scale = int(*service.Deploy.Replicas)
|
||||
}
|
||||
scale := service.GetScale()
|
||||
|
||||
if fi, statErr := os.Stat(pathMapping.HostPath); statErr == nil {
|
||||
if fi.IsDir() {
|
||||
|
||||
@@ -17,7 +17,7 @@ package sync
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/compose-spec/compose-go/types"
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
)
|
||||
|
||||
// PathMapping contains the Compose service and modified host system path.
|
||||
|
||||
@@ -21,6 +21,7 @@ import (
|
||||
"archive/tar"
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/fs"
|
||||
@@ -30,9 +31,8 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/go-multierror"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/compose-spec/compose-go/types"
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
moby "github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/pkg/archive"
|
||||
)
|
||||
@@ -47,6 +47,7 @@ type LowLevelClient interface {
|
||||
ContainersForService(ctx context.Context, projectName string, serviceName string) ([]moby.Container, error)
|
||||
|
||||
Exec(ctx context.Context, containerID string, cmd []string, in io.Reader) error
|
||||
Untar(ctx context.Context, id string, reader io.ReadCloser) error
|
||||
}
|
||||
|
||||
type Tar struct {
|
||||
@@ -84,39 +85,24 @@ func (t *Tar) Sync(ctx context.Context, service types.ServiceConfig, paths []Pat
|
||||
if len(pathsToDelete) != 0 {
|
||||
deleteCmd = append([]string{"rm", "-rf"}, pathsToDelete...)
|
||||
}
|
||||
copyCmd := []string{"tar", "-v", "-C", "/", "-x", "-f", "-"}
|
||||
|
||||
var eg multierror.Group
|
||||
writers := make([]*io.PipeWriter, len(containers))
|
||||
for i := range containers {
|
||||
containerID := containers[i].ID
|
||||
r, w := io.Pipe()
|
||||
writers[i] = w
|
||||
tarReader := tarArchive(pathsToCopy)
|
||||
|
||||
eg.Go(func() error {
|
||||
if len(deleteCmd) != 0 {
|
||||
if err := t.client.Exec(ctx, containerID, deleteCmd, nil); err != nil {
|
||||
return fmt.Errorf("deleting paths in %s: %w", containerID, err)
|
||||
}
|
||||
}
|
||||
if err := t.client.Exec(ctx, containerID, copyCmd, r); err != nil {
|
||||
|
||||
if err := t.client.Untar(ctx, containerID, tarReader); err != nil {
|
||||
return fmt.Errorf("copying files to %s: %w", containerID, err)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
multiWriter := newLossyMultiWriter(writers...)
|
||||
tarReader := tarArchive(pathsToCopy)
|
||||
defer func() {
|
||||
_ = tarReader.Close()
|
||||
multiWriter.Close()
|
||||
}()
|
||||
_, err = io.Copy(multiWriter, tarReader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
multiWriter.Close()
|
||||
|
||||
return eg.Wait().ErrorOrNil()
|
||||
}
|
||||
|
||||
@@ -212,7 +198,7 @@ func (a *ArchiveBuilder) writeEntry(entry archiveEntry) error {
|
||||
if useBuf {
|
||||
a.copyBuf.Reset()
|
||||
_, err = io.Copy(a.copyBuf, file)
|
||||
if err != nil && err != io.EOF {
|
||||
if err != nil && !errors.Is(err, io.EOF) {
|
||||
return fmt.Errorf("copying %q: %w", pathInTar, err)
|
||||
}
|
||||
header.Size = int64(len(a.copyBuf.Bytes()))
|
||||
@@ -232,7 +218,7 @@ func (a *ArchiveBuilder) writeEntry(entry archiveEntry) error {
|
||||
_, err = io.Copy(a.tw, file)
|
||||
}
|
||||
|
||||
if err != nil && err != io.EOF {
|
||||
if err != nil && !errors.Is(err, io.EOF) {
|
||||
return fmt.Errorf("copying %q: %w", pathInTar, err)
|
||||
}
|
||||
|
||||
|
||||
@@ -18,12 +18,12 @@ package sync
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
@@ -115,7 +115,7 @@ func (b *bufReader) consume() {
|
||||
b.data = append(b.data, buf[:n]...)
|
||||
b.mu.Unlock()
|
||||
}
|
||||
if err == io.EOF {
|
||||
if errors.Is(err, io.EOF) {
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
|
||||
@@ -22,7 +22,7 @@ import (
|
||||
|
||||
"github.com/docker/compose/v2/pkg/utils"
|
||||
|
||||
"github.com/compose-spec/compose-go/types"
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
moby "github.com/docker/docker/api/types"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
@@ -58,17 +58,12 @@ func ProjectOptions(proj *types.Project) SpanOptions {
|
||||
return nil
|
||||
}
|
||||
|
||||
disabledServiceNames := make([]string, len(proj.DisabledServices))
|
||||
for i := range proj.DisabledServices {
|
||||
disabledServiceNames[i] = proj.DisabledServices[i].Name
|
||||
}
|
||||
|
||||
attrs := []attribute.KeyValue{
|
||||
attribute.String("project.name", proj.Name),
|
||||
attribute.String("project.dir", proj.WorkingDir),
|
||||
attribute.StringSlice("project.compose_files", proj.ComposeFiles),
|
||||
attribute.StringSlice("project.services.active", proj.ServiceNames()),
|
||||
attribute.StringSlice("project.services.disabled", disabledServiceNames),
|
||||
attribute.StringSlice("project.services.disabled", proj.DisabledServiceNames()),
|
||||
attribute.StringSlice("project.profiles", proj.Profiles),
|
||||
attribute.StringSlice("project.volumes", proj.VolumeNames()),
|
||||
attribute.StringSlice("project.networks", proj.NetworkNames()),
|
||||
|
||||
@@ -39,7 +39,7 @@ func traceClientFromDockerContext(dockerCli command.Cli, otelEnv envMap) (otlptr
|
||||
// automatic integration with Docker Desktop;
|
||||
cfg, err := ConfigFromDockerContext(dockerCli.ContextStore(), dockerCli.CurrentContext())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("loading otel config from docker context metadata: %v", err)
|
||||
return nil, fmt.Errorf("loading otel config from docker context metadata: %w", err)
|
||||
}
|
||||
|
||||
if cfg.Endpoint == "" {
|
||||
@@ -52,13 +52,13 @@ func traceClientFromDockerContext(dockerCli command.Cli, otelEnv envMap) (otlptr
|
||||
defer func() {
|
||||
for k, v := range otelEnv {
|
||||
if err := os.Setenv(k, v); err != nil {
|
||||
panic(fmt.Errorf("restoring env for %q: %v", k, err))
|
||||
panic(fmt.Errorf("restoring env for %q: %w", k, err))
|
||||
}
|
||||
}
|
||||
}()
|
||||
for k := range otelEnv {
|
||||
if err := os.Unsetenv(k); err != nil {
|
||||
return nil, fmt.Errorf("stashing env for %q: %v", k, err)
|
||||
return nil, fmt.Errorf("stashing env for %q: %w", k, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ func traceClientFromDockerContext(dockerCli command.Cli, otelEnv envMap) (otlptr
|
||||
grpc.WithTransportCredentials(insecure.NewCredentials()),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("initializing otel connection from docker context metadata: %v", err)
|
||||
return nil, fmt.Errorf("initializing otel connection from docker context metadata: %w", err)
|
||||
}
|
||||
|
||||
client := otlptracegrpc.NewClient(otlptracegrpc.WithGRPCConn(conn))
|
||||
|
||||
@@ -37,7 +37,7 @@ import (
|
||||
"go.opentelemetry.io/otel/propagation"
|
||||
"go.opentelemetry.io/otel/sdk/resource"
|
||||
sdktrace "go.opentelemetry.io/otel/sdk/trace"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.18.0"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.19.0"
|
||||
)
|
||||
|
||||
func init() {
|
||||
@@ -111,15 +111,13 @@ func InitProvider(dockerCli command.Cli) (ShutdownFunc, error) {
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create resource: %v", err)
|
||||
return nil, fmt.Errorf("failed to create resource: %w", err)
|
||||
}
|
||||
|
||||
muxExporter := MuxExporter{exporters: exporters}
|
||||
sp := sdktrace.NewSimpleSpanProcessor(muxExporter)
|
||||
tracerProvider := sdktrace.NewTracerProvider(
|
||||
sdktrace.WithSampler(sdktrace.AlwaysSample()),
|
||||
sdktrace.WithResource(res),
|
||||
sdktrace.WithSpanProcessor(sp),
|
||||
sdktrace.WithBatcher(muxExporter),
|
||||
)
|
||||
otel.SetTracerProvider(tracerProvider)
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ import (
|
||||
"context"
|
||||
|
||||
"go.opentelemetry.io/otel/codes"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.18.0"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.19.0"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
)
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/compose-spec/compose-go/types"
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
"github.com/docker/compose/v2/pkg/utils"
|
||||
)
|
||||
|
||||
@@ -62,6 +62,8 @@ type Service interface {
|
||||
Remove(ctx context.Context, projectName string, options RemoveOptions) error
|
||||
// Exec executes a command in a running service container
|
||||
Exec(ctx context.Context, projectName string, options RunOptions) (int, error)
|
||||
// Attach STDIN,STDOUT,STDERR to a running service container
|
||||
Attach(ctx context.Context, projectName string, options AttachOptions) error
|
||||
// Copy copies a file/folder between a service container and the local filesystem
|
||||
Copy(ctx context.Context, projectName string, options CopyOptions) error
|
||||
// Pause executes the equivalent to a `compose pause`
|
||||
@@ -135,6 +137,8 @@ type BuildOptions struct {
|
||||
Quiet bool
|
||||
// Services passed in the command line to be built
|
||||
Services []string
|
||||
// Deps also build selected services dependencies
|
||||
Deps bool
|
||||
// Ssh authentications passed in the command line
|
||||
SSHs []types.SSHKey
|
||||
// Memory limit for the build container
|
||||
@@ -146,9 +150,9 @@ type BuildOptions struct {
|
||||
// Apply mutates project according to build options
|
||||
func (o BuildOptions) Apply(project *types.Project) error {
|
||||
platform := project.Environment["DOCKER_DEFAULT_PLATFORM"]
|
||||
for i, service := range project.Services {
|
||||
for name, service := range project.Services {
|
||||
if service.Image == "" && service.Build == nil {
|
||||
return fmt.Errorf("invalid service %q. Must specify either image or build", service.Name)
|
||||
return fmt.Errorf("invalid service %q. Must specify either image or build", name)
|
||||
}
|
||||
|
||||
if service.Build == nil {
|
||||
@@ -156,20 +160,20 @@ func (o BuildOptions) Apply(project *types.Project) error {
|
||||
}
|
||||
if platform != "" {
|
||||
if len(service.Build.Platforms) > 0 && !utils.StringContains(service.Build.Platforms, platform) {
|
||||
return fmt.Errorf("service %q build.platforms does not support value set by DOCKER_DEFAULT_PLATFORM: %s", service.Name, platform)
|
||||
return fmt.Errorf("service %q build.platforms does not support value set by DOCKER_DEFAULT_PLATFORM: %s", name, platform)
|
||||
}
|
||||
service.Platform = platform
|
||||
}
|
||||
if service.Platform != "" {
|
||||
if len(service.Build.Platforms) > 0 && !utils.StringContains(service.Build.Platforms, service.Platform) {
|
||||
return fmt.Errorf("service %q build configuration does not support platform: %s", service.Name, service.Platform)
|
||||
return fmt.Errorf("service %q build configuration does not support platform: %s", name, service.Platform)
|
||||
}
|
||||
}
|
||||
|
||||
service.Build.Pull = service.Build.Pull || o.Pull
|
||||
service.Build.NoCache = service.Build.NoCache || o.NoCache
|
||||
|
||||
project.Services[i] = service
|
||||
project.Services[name] = service
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -340,6 +344,16 @@ type RunOptions struct {
|
||||
Index int
|
||||
}
|
||||
|
||||
// AttachOptions group options of the Attach API
|
||||
type AttachOptions struct {
|
||||
Project *types.Project
|
||||
Service string
|
||||
Index int
|
||||
DetachKeys string
|
||||
NoStdin bool
|
||||
Proxy bool
|
||||
}
|
||||
|
||||
// EventsOptions group options of the Events API
|
||||
type EventsOptions struct {
|
||||
Services []string
|
||||
@@ -361,8 +375,28 @@ type PortOptions struct {
|
||||
Index int
|
||||
}
|
||||
|
||||
// OCIVersion controls manifest generation to ensure compatibility
|
||||
// with different registries.
|
||||
//
|
||||
// Currently, this is not exposed as an option to the user – Compose uses
|
||||
// OCI 1.0 mode automatically for ECR registries based on domain and OCI 1.1
|
||||
// for all other registries.
|
||||
//
|
||||
// There are likely other popular registries that do not support the OCI 1.1
|
||||
// format, so it might make sense to expose this as a CLI flag or see if
|
||||
// there's a way to generically probe the registry for support level.
|
||||
type OCIVersion string
|
||||
|
||||
const (
|
||||
OCIVersion1_0 OCIVersion = "1.0"
|
||||
OCIVersion1_1 OCIVersion = "1.1"
|
||||
)
|
||||
|
||||
// PublishOptions group options of the Publish API
|
||||
type PublishOptions struct {
|
||||
ResolveImageDigests bool
|
||||
|
||||
OCIVersion OCIVersion
|
||||
}
|
||||
|
||||
func (e Event) String() string {
|
||||
@@ -487,6 +521,7 @@ type ServiceStatus struct {
|
||||
// LogOptions defines optional parameters for the `Log` API
|
||||
type LogOptions struct {
|
||||
Project *types.Project
|
||||
Index int
|
||||
Services []string
|
||||
Tail string
|
||||
Since string
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user