mirror of
https://github.com/docker/compose.git
synced 2025-04-08 17:05:13 +02:00
Compare commits
212 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
ee33143026 | ||
|
cb0b5f6e27 | ||
|
1384853538 | ||
|
096b1e32d3 | ||
|
bf71138df6 | ||
|
a1f673dcf5 | ||
|
02c747a7de | ||
|
88f4f265db | ||
|
e67348222f | ||
|
b543380708 | ||
|
2e75185a07 | ||
|
7bedb5a02c | ||
|
f9cd4d0b1d | ||
|
0badcf3c8d | ||
|
ec49db98d4 | ||
|
e5a353b34d | ||
|
43e456145c | ||
|
75368c7859 | ||
|
6e814eac35 | ||
|
a0d1c3f944 | ||
|
0c5bd16da1 | ||
|
b0badf1eb0 | ||
|
342a2a9e71 | ||
|
7814e5798c | ||
|
42b2e11094 | ||
|
6a8c0988cf | ||
|
9129abe516 | ||
|
f38f3f754c | ||
|
ea07ba8e2a | ||
|
432ae23b0e | ||
|
b6f313b8a5 | ||
|
13618756dc | ||
|
6c1e21572a | ||
|
33e863ac6c | ||
|
f70209cf15 | ||
|
62e832eb50 | ||
|
80e8fda14f | ||
|
375a279785 | ||
|
a766e1669a | ||
|
793c6f1715 | ||
|
8e3e1f7f8b | ||
|
83cafe2838 | ||
|
55b5f233c2 | ||
|
c3a0c35681 | ||
|
8615e9a7c1 | ||
|
b23728941d | ||
|
1a7343bc88 | ||
|
41e6094041 | ||
|
66a47169d5 | ||
|
4c72d3a0e3 | ||
|
59f39b9990 | ||
|
7ab65ba127 | ||
|
d9f05d72d2 | ||
|
7b88c5b0ed | ||
|
eaf9800948 | ||
|
4c2ecb542f | ||
|
bcd000ab40 | ||
|
8092ce9414 | ||
|
97595066e3 | ||
|
508309414f | ||
|
b6c8a2b9fc | ||
|
19571c2c81 | ||
|
0ef7bbcddc | ||
|
66dfa7d181 | ||
|
876ecc48be | ||
|
c7bf302c23 | ||
|
7b3bdbe037 | ||
|
094b48fd74 | ||
|
43c52e2a80 | ||
|
6c1ee1069b | ||
|
e38b729a30 | ||
|
145bb8466d | ||
|
acac184135 | ||
|
3292740c19 | ||
|
cae8e84636 | ||
|
da2eff4ba7 | ||
|
20f780e95a | ||
|
cf2fc2005c | ||
|
d0398a4681 | ||
|
ac40aae4c3 | ||
|
f25fea5e6d | ||
|
b27f56eb19 | ||
|
4e593ed074 | ||
|
d956ff13da | ||
|
5f7c9a2b4b | ||
|
fd0c23a1c9 | ||
|
7aa64ae9cb | ||
|
c23eea9341 | ||
|
036da47950 | ||
|
33172d5e4d | ||
|
3f1a6b72a5 | ||
|
f9a6e6c414 | ||
|
18ef6e592b | ||
|
2884d6df0d | ||
|
4459012a4f | ||
|
6f1f76c0e6 | ||
|
ed72c21871 | ||
|
fa4cfb652e | ||
|
200638b024 | ||
|
a0320f12ef | ||
|
f8a912ab9a | ||
|
c23a7e7281 | ||
|
49575ef499 | ||
|
faa46d374d | ||
|
6ecb8d40ae | ||
|
a6a39422e4 | ||
|
40cd08f318 | ||
|
5e2abb6c26 | ||
|
4db5fcd569 | ||
|
f14c15fa57 | ||
|
8d68ef587f | ||
|
cde9ae5952 | ||
|
806ac91cf6 | ||
|
1c073c0a04 | ||
|
840288895e | ||
|
4b70ff0ccd | ||
|
23351ece81 | ||
|
7c7407672a | ||
|
25cfa66a91 | ||
|
f160333e9e | ||
|
d04b3f48e4 | ||
|
ed10804e0f | ||
|
52578c0998 | ||
|
bd2b49a1cf | ||
|
433a60e122 | ||
|
489fe9cf02 | ||
|
ef1931c8de | ||
|
9be7a3c9a1 | ||
|
666996bee2 | ||
|
083f676214 | ||
|
e81de103db | ||
|
fa39503469 | ||
|
a351585024 | ||
|
b6db1380ec | ||
|
2ebb475433 | ||
|
d474515d45 | ||
|
2b21c5df93 | ||
|
1f3c10eb4b | ||
|
68ad165a59 | ||
|
3060ed2795 | ||
|
be09b2e8ce | ||
|
571a1af013 | ||
|
8f644eea73 | ||
|
56e92e34b6 | ||
|
a42a04dfe8 | ||
|
34bcd03a76 | ||
|
ed61e42f93 | ||
|
65696bb1cb | ||
|
446e00520c | ||
|
c01c9c29f4 | ||
|
038c81f34e | ||
|
a20b69ac5b | ||
|
977530c22a | ||
|
d4db8b6b12 | ||
|
f8ce0f04e6 | ||
|
8e0520e71e | ||
|
332311358e | ||
|
df9e420ddd | ||
|
142f5dba84 | ||
|
700c586bcf | ||
|
fc566509d5 | ||
|
e73c2303fb | ||
|
624303233a | ||
|
a1729c52de | ||
|
254224c182 | ||
|
0861e68450 | ||
|
af5b748500 | ||
|
32a22c1f4f | ||
|
e6ea8fb962 | ||
|
043465448f | ||
|
1d08390864 | ||
|
69a83d1303 | ||
|
781b9f1cd5 | ||
|
cbff0e5559 | ||
|
e4222bff53 | ||
|
25197fe6dd | ||
|
85cdaf9ddf | ||
|
a8469db83f | ||
|
08488dae59 | ||
|
cc3a216f2f | ||
|
6e818b9ae0 | ||
|
6b3e57503e | ||
|
8e497a1289 | ||
|
5aed704379 | ||
|
1ff9b758d2 | ||
|
9eaba55973 | ||
|
a85f8a40a9 | ||
|
095f65cb42 | ||
|
208e57ded8 | ||
|
2d148faedf | ||
|
43ac1e31c6 | ||
|
5561a778c9 | ||
|
ae48f488d1 | ||
|
5e3a095380 | ||
|
a2a3eb72e2 | ||
|
3513b42423 | ||
|
d4fa63fdcb | ||
|
c21d4cfb40 | ||
|
61f1d4f69b | ||
|
f7cce281de | ||
|
bcaacc7f23 | ||
|
3f5898f8d0 | ||
|
2bb67f2700 | ||
|
bf521fe3ab | ||
|
11e9621da5 | ||
|
a9de9abcfb | ||
|
799ab842a0 | ||
|
2f65ace2aa | ||
|
aa0a4189ee | ||
|
eba3ff8f37 | ||
|
6313365ba4 | ||
|
dbd51745c4 |
2
.github/CODEOWNERS
vendored
Normal file
2
.github/CODEOWNERS
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
# global rules
|
||||
* @docker/compose-maintainers
|
2
.github/stale.yml
vendored
2
.github/stale.yml
vendored
@ -1,7 +1,7 @@
|
||||
# Configuration for probot-stale - https://github.com/probot/stale
|
||||
|
||||
# Number of days of inactivity before an Issue or Pull Request becomes stale
|
||||
daysUntilStale: 180
|
||||
daysUntilStale: 90
|
||||
|
||||
# Number of days of inactivity before an Issue or Pull Request with the stale label is closed.
|
||||
# Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale.
|
||||
|
93
.github/workflows/ci.yml
vendored
93
.github/workflows/ci.yml
vendored
@ -56,7 +56,7 @@ jobs:
|
||||
uses: actions/checkout@v4
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
uses: docker/setup-buildx-action@v3
|
||||
-
|
||||
name: Run
|
||||
run: |
|
||||
@ -71,29 +71,45 @@ jobs:
|
||||
matrix:
|
||||
platform: ${{ fromJson(needs.prepare.outputs.matrix) }}
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
-
|
||||
name: Prepare
|
||||
run: |
|
||||
platform=${{ matrix.platform }}
|
||||
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
-
|
||||
name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2
|
||||
uses: docker/setup-qemu-action@v3
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
uses: docker/setup-buildx-action@v3
|
||||
-
|
||||
name: Build
|
||||
uses: docker/bake-action@v2
|
||||
uses: docker/bake-action@v6
|
||||
with:
|
||||
source: .
|
||||
targets: release
|
||||
provenance: mode=max
|
||||
sbom: true
|
||||
set: |
|
||||
*.platform=${{ matrix.platform }}
|
||||
*.cache-from=type=gha,scope=binary-${{ env.PLATFORM_PAIR }}
|
||||
*.cache-to=type=gha,scope=binary-${{ env.PLATFORM_PAIR }},mode=max
|
||||
-
|
||||
name: Rename provenance and sbom
|
||||
working-directory: ./bin/release
|
||||
run: |
|
||||
binname=$(find . -name 'docker-compose-*')
|
||||
filename=$(basename "$binname" | sed -E 's/\.exe$//')
|
||||
mv "provenance.json" "${filename}.provenance.json"
|
||||
mv "sbom-binary.spdx.json" "${filename}.sbom.json"
|
||||
find . -name 'sbom*.json' -exec rm {} \;
|
||||
-
|
||||
name: List artifacts
|
||||
run: |
|
||||
tree -nh ./bin/release
|
||||
-
|
||||
name: Upload artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
@ -105,15 +121,12 @@ jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
uses: docker/setup-buildx-action@v3
|
||||
-
|
||||
name: Test
|
||||
uses: docker/bake-action@v2
|
||||
uses: docker/bake-action@v6
|
||||
with:
|
||||
targets: test
|
||||
set: |
|
||||
@ -141,19 +154,19 @@ jobs:
|
||||
- plugin
|
||||
- standalone
|
||||
engine:
|
||||
- 24.0.9
|
||||
- 25.0.5
|
||||
- 26.1.4
|
||||
- 27.3.0
|
||||
- 26
|
||||
- 27
|
||||
- 28
|
||||
steps:
|
||||
- name: Prepare
|
||||
run: |
|
||||
mode=${{ matrix.mode }}
|
||||
engine=${{ matrix.engine }}
|
||||
echo "MODE_ENGINE_PAIR=${mode}-${engine}" >> $GITHUB_ENV
|
||||
-
|
||||
name: Checkout
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install Docker ${{ matrix.engine }}
|
||||
run: |
|
||||
sudo systemctl stop docker.service
|
||||
@ -161,22 +174,24 @@ jobs:
|
||||
sudo apt-get install curl
|
||||
curl -fsSL https://test.docker.com -o get-docker.sh
|
||||
sudo sh ./get-docker.sh --version ${{ matrix.engine }}
|
||||
|
||||
- name: Check Docker Version
|
||||
run: docker --version
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
-
|
||||
name: Set up Go
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version-file: 'go.mod'
|
||||
check-latest: true
|
||||
cache: true
|
||||
-
|
||||
name: Build
|
||||
uses: docker/bake-action@v2
|
||||
|
||||
- name: Build
|
||||
uses: docker/bake-action@v6
|
||||
with:
|
||||
source: .
|
||||
targets: binary-with-coverage
|
||||
set: |
|
||||
*.cache-from=type=gha,scope=binary-linux-amd64
|
||||
@ -184,37 +199,37 @@ jobs:
|
||||
*.cache-to=type=gha,scope=binary-e2e-${{ matrix.mode }},mode=max
|
||||
env:
|
||||
BUILD_TAGS: e2e
|
||||
-
|
||||
name: Setup tmate session
|
||||
|
||||
- name: Setup tmate session
|
||||
if: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.debug_enabled }}
|
||||
uses: mxschmitt/action-tmate@8b4e4ac71822ed7e0ad5fb3d1c33483e9e8fb270 # v3.11
|
||||
with:
|
||||
limit-access-to-actor: true
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
-
|
||||
name: Test plugin mode
|
||||
|
||||
- name: Test plugin mode
|
||||
if: ${{ matrix.mode == 'plugin' }}
|
||||
run: |
|
||||
rm -rf ./bin/coverage/e2e
|
||||
mkdir -p ./bin/coverage/e2e
|
||||
make e2e-compose GOCOVERDIR=bin/coverage/e2e TEST_FLAGS="-v"
|
||||
-
|
||||
name: Gather coverage data
|
||||
|
||||
- name: Gather coverage data
|
||||
if: ${{ matrix.mode == 'plugin' }}
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: coverage-data-e2e-${{ env.MODE_ENGINE_PAIR }}
|
||||
path: bin/coverage/e2e/
|
||||
if-no-files-found: error
|
||||
-
|
||||
name: Test standalone mode
|
||||
|
||||
- name: Test standalone mode
|
||||
if: ${{ matrix.mode == 'standalone' }}
|
||||
run: |
|
||||
rm -f /usr/local/bin/docker-compose
|
||||
cp bin/build/docker-compose /usr/local/bin
|
||||
make e2e-compose-standalone
|
||||
-
|
||||
name: e2e Test Summary
|
||||
|
||||
- name: e2e Test Summary
|
||||
uses: test-summary/action@v2
|
||||
with:
|
||||
paths: /tmp/report/report.xml
|
||||
@ -284,7 +299,11 @@ jobs:
|
||||
find . -type f -print0 | sort -z | xargs -r0 shasum -a 256 -b | sed 's# \*\./# *#' > $RUNNER_TEMP/checksums.txt
|
||||
shasum -a 256 -U -c $RUNNER_TEMP/checksums.txt
|
||||
mv $RUNNER_TEMP/checksums.txt .
|
||||
cat checksums.txt | while read sum file; do echo "$sum $file" > ${file#\*}.sha256; done
|
||||
cat checksums.txt | while read sum file; do
|
||||
if [[ "${file#\*}" == docker-compose-* && "${file#\*}" != *.provenance.json && "${file#\*}" != *.sbom.json ]]; then
|
||||
echo "$sum $file" > ${file#\*}.sha256
|
||||
fi
|
||||
done
|
||||
-
|
||||
name: License
|
||||
run: cp packaging/* ./bin/release/
|
||||
|
2
.github/workflows/docs-upstream.yml
vendored
2
.github/workflows/docs-upstream.yml
vendored
@ -44,7 +44,7 @@ jobs:
|
||||
retention-days: 1
|
||||
|
||||
validate:
|
||||
uses: docker/docs/.github/workflows/validate-upstream.yml@919a9b9104a34a40b30d116529bcce589a544d1c # pin for artifact v4 support: https://github.com/docker/docs/pull/19220
|
||||
uses: docker/docs/.github/workflows/validate-upstream.yml@main
|
||||
needs:
|
||||
- docs-yaml
|
||||
with:
|
||||
|
39
.github/workflows/merge.yml
vendored
39
.github/workflows/merge.yml
vendored
@ -82,16 +82,23 @@ jobs:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
-
|
||||
name: Login to DockerHub
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKERPUBLICBOT_USERNAME }}
|
||||
password: ${{ secrets.DOCKERPUBLICBOT_WRITE_PAT }}
|
||||
-
|
||||
name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2
|
||||
uses: docker/setup-qemu-action@v3
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
uses: docker/setup-buildx-action@v3
|
||||
-
|
||||
name: Docker meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@v4
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: |
|
||||
${{ env.REPO_SLUG }}
|
||||
@ -99,28 +106,22 @@ jobs:
|
||||
type=ref,event=tag
|
||||
type=edge
|
||||
bake-target: meta-helper
|
||||
-
|
||||
name: Login to DockerHub
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
username: ${{ secrets.DOCKERPUBLICBOT_USERNAME }}
|
||||
password: ${{ secrets.DOCKERPUBLICBOT_WRITE_PAT }}
|
||||
-
|
||||
name: Build and push image
|
||||
uses: docker/bake-action@v2
|
||||
uses: docker/bake-action@v6
|
||||
id: bake
|
||||
with:
|
||||
source: .
|
||||
files: |
|
||||
./docker-bake.hcl
|
||||
${{ steps.meta.outputs.bake-file }}
|
||||
targets: image-cross
|
||||
push: ${{ github.event_name != 'pull_request' }}
|
||||
sbom: true
|
||||
provenance: mode=max
|
||||
set: |
|
||||
*.cache-from=type=gha,scope=bin-image
|
||||
*.cache-to=type=gha,scope=bin-image,mode=max
|
||||
*.attest=type=sbom
|
||||
*.attest=type=provenance,mode=max,builder-id=https://github.com/${{ env.GITHUB_REPOSITORY }}/actions/runs/${{ env.GITHUB_RUN_ID }}
|
||||
|
||||
desktop-edge-test:
|
||||
runs-on: ubuntu-latest
|
||||
@ -129,14 +130,16 @@ jobs:
|
||||
-
|
||||
name: Generate Token
|
||||
id: generate_token
|
||||
uses: tibdex/github-app-token@v1
|
||||
uses: actions/create-github-app-token@v1
|
||||
with:
|
||||
app_id: ${{ vars.DOCKERDESKTOP_APP_ID }}
|
||||
private_key: ${{ secrets.DOCKERDESKTOP_APP_PRIVATEKEY }}
|
||||
repository: docker/${{ secrets.DOCKERDESKTOP_REPO }}
|
||||
app-id: ${{ vars.DOCKERDESKTOP_APP_ID }}
|
||||
private-key: ${{ secrets.DOCKERDESKTOP_APP_PRIVATEKEY }}
|
||||
owner: docker
|
||||
repositories: |
|
||||
${{ secrets.DOCKERDESKTOP_REPO }}
|
||||
-
|
||||
name: Trigger Docker Desktop e2e with edge version
|
||||
uses: actions/github-script@v6
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
github-token: ${{ steps.generate_token.outputs.token }}
|
||||
script: |
|
||||
|
8
.github/workflows/scorecards.yml
vendored
8
.github/workflows/scorecards.yml
vendored
@ -22,12 +22,12 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: "Checkout code"
|
||||
uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846 # tag=v3.0.0
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # tag=v4.4.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: "Run analysis"
|
||||
uses: ossf/scorecard-action@99c53751e09b9529366343771cc321ec74e9bd3d # tag=v2.0.6
|
||||
uses: ossf/scorecard-action@62b2cac7ed8198b15735ed49ab1e5cf35480ba46 # tag=v2.4.0
|
||||
with:
|
||||
results_file: results.sarif
|
||||
results_format: sarif
|
||||
@ -41,7 +41,7 @@ jobs:
|
||||
# Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
|
||||
# format to the repository Actions tab.
|
||||
- name: "Upload artifact"
|
||||
uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 # tag=v3.0.0
|
||||
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # tag=v4.5.0
|
||||
with:
|
||||
name: SARIF file
|
||||
path: results.sarif
|
||||
@ -49,6 +49,6 @@ jobs:
|
||||
|
||||
# Upload the results to GitHub's code scanning dashboard.
|
||||
- name: "Upload to code-scanning"
|
||||
uses: github/codeql-action/upload-sarif@5f532563584d71fdef14ee64d17bafb34f751ce5 # tag=v1.0.26
|
||||
uses: github/codeql-action/upload-sarif@3096afedf9873361b2b2f65e1445b13272c83eb8 # tag=v2.20.00
|
||||
with:
|
||||
sarif_file: results.sarif
|
||||
|
114
.golangci.yml
114
.golangci.yml
@ -1,73 +1,83 @@
|
||||
version: "2"
|
||||
run:
|
||||
concurrency: 2
|
||||
timeout: 10m
|
||||
linters:
|
||||
enable-all: false
|
||||
disable-all: true
|
||||
default: none
|
||||
enable:
|
||||
- copyloopvar
|
||||
- depguard
|
||||
- errcheck
|
||||
- errorlint
|
||||
- gocritic
|
||||
- gocyclo
|
||||
- gofmt
|
||||
- goimports
|
||||
- gomodguard
|
||||
- revive
|
||||
- gosimple
|
||||
- govet
|
||||
- ineffassign
|
||||
- lll
|
||||
- misspell
|
||||
- nakedret
|
||||
- nolintlint
|
||||
- revive
|
||||
- staticcheck
|
||||
- typecheck
|
||||
- testifylint
|
||||
- unconvert
|
||||
- unparam
|
||||
- unused
|
||||
linters-settings:
|
||||
revive:
|
||||
rules:
|
||||
- name: package-comments
|
||||
disabled: true
|
||||
depguard:
|
||||
rules:
|
||||
all:
|
||||
deny:
|
||||
- pkg: io/ioutil
|
||||
desc: 'io/ioutil package has been deprecated'
|
||||
- pkg: gopkg.in/yaml.v2
|
||||
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"
|
||||
- gotest.tools:
|
||||
version: "< 3.0.0"
|
||||
reason: "deprecated, pre-modules version"
|
||||
gocritic:
|
||||
# Enable multiple checks by tags, run `GL_DEBUG=gocritic golangci-lint run` to see all tags and checks.
|
||||
# Empty list by default. See https://github.com/go-critic/go-critic#usage -> section "Tags".
|
||||
enabled-tags:
|
||||
- diagnostic
|
||||
- opinionated
|
||||
- style
|
||||
disabled-checks:
|
||||
- paramTypeCombine
|
||||
- unnamedResult
|
||||
- whyNoLint
|
||||
gocyclo:
|
||||
min-complexity: 16
|
||||
lll:
|
||||
line-length: 200
|
||||
settings:
|
||||
depguard:
|
||||
rules:
|
||||
all:
|
||||
deny:
|
||||
- pkg: io/ioutil
|
||||
desc: io/ioutil package has been deprecated
|
||||
- pkg: gopkg.in/yaml.v2
|
||||
desc: compose-go uses yaml.v3
|
||||
gocritic:
|
||||
disabled-checks:
|
||||
- paramTypeCombine
|
||||
- unnamedResult
|
||||
- whyNoLint
|
||||
enabled-tags:
|
||||
- diagnostic
|
||||
- opinionated
|
||||
- style
|
||||
gocyclo:
|
||||
min-complexity: 16
|
||||
gomodguard:
|
||||
blocked:
|
||||
modules:
|
||||
- github.com/pkg/errors:
|
||||
recommendations:
|
||||
- errors
|
||||
- fmt
|
||||
versions:
|
||||
- github.com/distribution/distribution:
|
||||
reason: use distribution/reference
|
||||
- gotest.tools:
|
||||
version: < 3.0.0
|
||||
reason: deprecated, pre-modules version
|
||||
lll:
|
||||
line-length: 200
|
||||
revive:
|
||||
rules:
|
||||
- name: package-comments
|
||||
disabled: true
|
||||
exclusions:
|
||||
generated: lax
|
||||
paths:
|
||||
- third_party$
|
||||
- builtin$
|
||||
- examples$
|
||||
issues:
|
||||
# golangci hides some golint warnings (the warning about exported things
|
||||
# without documentation for example), this will make it show them anyway.
|
||||
exclude-use-default: false
|
||||
max-issues-per-linter: 0
|
||||
max-same-issues: 0
|
||||
formatters:
|
||||
enable:
|
||||
- gofumpt
|
||||
- goimports
|
||||
exclusions:
|
||||
generated: lax
|
||||
paths:
|
||||
- third_party$
|
||||
- builtin$
|
||||
- examples$
|
||||
|
@ -2,10 +2,10 @@
|
||||
### Prerequisites
|
||||
|
||||
* Windows:
|
||||
* [Docker Desktop](https://hub.docker.com/editions/community/docker-ce-desktop-windows)
|
||||
* [Docker Desktop](https://docs.docker.com/desktop/setup/install/windows-install/)
|
||||
* make
|
||||
* macOS:
|
||||
* [Docker Desktop](https://hub.docker.com/editions/community/docker-ce-desktop-mac)
|
||||
* [Docker Desktop](https://docs.docker.com/desktop/setup/install/mac-install/)
|
||||
* make
|
||||
* Linux:
|
||||
* [Docker 20.10 or later](https://docs.docker.com/engine/install/)
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
Want to hack on Docker? Awesome! We have a contributor's guide that explains
|
||||
[setting up a Docker development environment and the contribution
|
||||
process](https://docs.docker.com/contribute/overview/).
|
||||
process](https://docs.docker.com/contribute/).
|
||||
|
||||
This page contains information about reporting issues as well as some tips and
|
||||
guidelines useful to experienced open source contributors. Finally, make sure
|
||||
@ -95,7 +95,7 @@ don't get discouraged!
|
||||
<tr>
|
||||
<td>Community Slack</td>
|
||||
<td>
|
||||
The Docker Community has a dedicated Slack chat to discuss features and issues. You can sign-up <a href="https://www.docker.com/docker-community" target="_blank">with this link</a>.
|
||||
The Docker Community has a dedicated Slack chat to discuss features and issues. You can sign-up <a href="https://www.docker.com/community/" target="_blank">with this link</a>.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
@ -118,7 +118,7 @@ don't get discouraged!
|
||||
<td>Stack Overflow</td>
|
||||
<td>
|
||||
Stack Overflow has over 17000 Docker questions listed. We regularly
|
||||
monitor <a href="https://stackoverflow.com/search?tab=newest&q=docker" target="_blank">Docker questions</a>
|
||||
monitor <a href="https://stackoverflow.com/questions/tagged/docker" target="_blank">Docker questions</a>
|
||||
and so do many other knowledgeable Docker users.
|
||||
</td>
|
||||
</tr>
|
||||
@ -200,7 +200,7 @@ For more details, see the [MAINTAINERS](MAINTAINERS) page.
|
||||
The sign-off is a simple line at the end of the explanation for the patch. Your
|
||||
signature certifies that you wrote the patch or otherwise have the right to pass
|
||||
it on as an open-source patch. The rules are pretty simple: if you can certify
|
||||
the below (from [developercertificate.org](http://developercertificate.org/)):
|
||||
the below (from [developercertificate.org](https://developercertificate.org/)):
|
||||
|
||||
```
|
||||
Developer Certificate of Origin
|
||||
@ -252,7 +252,7 @@ commit automatically with `git commit -s`.
|
||||
### How can I become a maintainer?
|
||||
|
||||
The procedures for adding new maintainers are explained in the global
|
||||
[MAINTAINERS](https://github.com/docker/opensource/blob/master/MAINTAINERS)
|
||||
[MAINTAINERS](https://github.com/docker/opensource/blob/main/MAINTAINERS)
|
||||
file in the
|
||||
[https://github.com/docker/opensource/](https://github.com/docker/opensource/)
|
||||
repository.
|
||||
@ -311,8 +311,8 @@ The rules:
|
||||
2. All code should pass the default levels of
|
||||
[`golint`](https://github.com/golang/lint).
|
||||
3. All code should follow the guidelines covered in [Effective
|
||||
Go](http://golang.org/doc/effective_go.html) and [Go Code Review
|
||||
Comments](https://github.com/golang/go/wiki/CodeReviewComments).
|
||||
Go](https://go.dev/doc/effective_go) and [Go Code Review
|
||||
Comments](https://go.dev/wiki/CodeReviewComments).
|
||||
4. Include code comments. Tell us the why, the history and the context.
|
||||
5. Document _all_ declarations and methods, even private ones. Declare
|
||||
expectations, caveats and anything else that may be important. If a type
|
||||
@ -334,6 +334,6 @@ The rules:
|
||||
guidelines. Since you've read all the rules, you now know that.
|
||||
|
||||
If you are having trouble getting into the mood of idiomatic Go, we recommend
|
||||
reading through [Effective Go](https://golang.org/doc/effective_go.html). The
|
||||
[Go Blog](https://blog.golang.org) is also a great resource. Drinking the
|
||||
reading through [Effective Go](https://go.dev/doc/effective_go). The
|
||||
[Go Blog](https://go.dev/blog/) is also a great resource. Drinking the
|
||||
kool-aid is a lot easier than going thirsty.
|
||||
|
@ -15,9 +15,9 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
ARG GO_VERSION=1.22.8
|
||||
ARG XX_VERSION=1.2.1
|
||||
ARG GOLANGCI_LINT_VERSION=v1.60.2
|
||||
ARG GO_VERSION=1.23.8
|
||||
ARG XX_VERSION=1.6.1
|
||||
ARG GOLANGCI_LINT_VERSION=v2.0.2
|
||||
ARG ADDLICENSE_VERSION=v1.0.0
|
||||
|
||||
ARG BUILD_TAGS="e2e"
|
||||
|
5
Makefile
5
Makefile
@ -116,6 +116,11 @@ cache-clear: ## Clear the builder cache
|
||||
lint: ## run linter(s)
|
||||
$(BUILDX_CMD) bake lint
|
||||
|
||||
.PHONY: fmt
|
||||
fmt:
|
||||
gofumpt --version >/dev/null 2>&1 || go install mvdan.cc/gofumpt@latest
|
||||
gofumpt -w .
|
||||
|
||||
.PHONY: docs
|
||||
docs: ## generate documentation
|
||||
$(eval $@_TMP_OUT := $(shell mktemp -d -t compose-output.XXXXXXXXXX))
|
||||
|
@ -8,7 +8,7 @@
|
||||
- [Legacy](#legacy)
|
||||
# Docker Compose v2
|
||||
|
||||
[](https://github.com/docker/compose/releases/latest)
|
||||
[](https://github.com/docker/compose/releases/latest)
|
||||
[](https://pkg.go.dev/github.com/docker/compose/v2)
|
||||
[](https://github.com/docker/compose/actions?query=workflow%3Aci)
|
||||
[](https://goreportcard.com/report/github.com/docker/compose/v2)
|
||||
@ -28,7 +28,7 @@ single command: `docker compose up`.
|
||||
### Windows and macOS
|
||||
|
||||
Docker Compose is included in
|
||||
[Docker Desktop](https://www.docker.com/products/docker-desktop)
|
||||
[Docker Desktop](https://www.docker.com/products/docker-desktop/)
|
||||
for Windows and macOS.
|
||||
|
||||
### Linux
|
||||
|
@ -55,8 +55,8 @@ func Setup(cmd *cobra.Command, dockerCli command.Cli, args []string) error {
|
||||
ctx,
|
||||
"cli/"+strings.Join(commandName(cmd), "-"),
|
||||
)
|
||||
cmdSpan.SetAttributes(attribute.StringSlice("cli.args", args))
|
||||
cmdSpan.SetAttributes(attribute.StringSlice("cli.flags", getFlags(cmd.Flags())))
|
||||
cmdSpan.SetAttributes(attribute.Bool("cli.isatty", dockerCli.In().IsTerminal()))
|
||||
|
||||
cmd.SetContext(ctx)
|
||||
wrapRunE(cmd, cmdSpan, tracingShutdown)
|
||||
@ -115,13 +115,14 @@ func wrapRunE(c *cobra.Command, cmdSpan trace.Span, tracingShutdown tracing.Shut
|
||||
}
|
||||
}
|
||||
|
||||
// commandName returns the path components for a given command.
|
||||
// commandName returns the path components for a given command,
|
||||
// in reverse alphabetical order for consistent usage metrics.
|
||||
//
|
||||
// The root Compose command and anything before (i.e. "docker")
|
||||
// are not included.
|
||||
//
|
||||
// For example:
|
||||
// - docker compose alpha watch -> [alpha, watch]
|
||||
// - docker compose alpha watch -> [watch, alpha]
|
||||
// - docker-compose up -> [up]
|
||||
func commandName(cmd *cobra.Command) []string {
|
||||
var name []string
|
||||
|
@ -20,6 +20,8 @@ import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
commands "github.com/docker/compose/v2/cmd/compose"
|
||||
"github.com/spf13/cobra"
|
||||
flag "github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
@ -60,5 +62,51 @@ func TestGetFlags(t *testing.T) {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestCommandName(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
setupCmd func() *cobra.Command
|
||||
want []string
|
||||
}{
|
||||
{
|
||||
name: "docker compose alpha watch -> [watch, alpha]",
|
||||
setupCmd: func() *cobra.Command {
|
||||
dockerCmd := &cobra.Command{Use: "docker"}
|
||||
composeCmd := &cobra.Command{Use: commands.PluginName}
|
||||
alphaCmd := &cobra.Command{Use: "alpha"}
|
||||
watchCmd := &cobra.Command{Use: "watch"}
|
||||
|
||||
dockerCmd.AddCommand(composeCmd)
|
||||
composeCmd.AddCommand(alphaCmd)
|
||||
alphaCmd.AddCommand(watchCmd)
|
||||
|
||||
return watchCmd
|
||||
},
|
||||
want: []string{"watch", "alpha"},
|
||||
},
|
||||
{
|
||||
name: "docker-compose up -> [up]",
|
||||
setupCmd: func() *cobra.Command {
|
||||
dockerComposeCmd := &cobra.Command{Use: commands.PluginName}
|
||||
upCmd := &cobra.Command{Use: "up"}
|
||||
|
||||
dockerComposeCmd.AddCommand(upCmd)
|
||||
|
||||
return upCmd
|
||||
},
|
||||
want: []string{"up"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
cmd := tt.setupCmd()
|
||||
got := commandName(cmd)
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("commandName() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -17,6 +17,9 @@
|
||||
package compatibility
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"os/exec"
|
||||
"testing"
|
||||
|
||||
"gotest.tools/v3/assert"
|
||||
@ -24,9 +27,10 @@ import (
|
||||
|
||||
func Test_convert(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
args []string
|
||||
want []string
|
||||
name string
|
||||
args []string
|
||||
want []string
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "compose only",
|
||||
@ -93,11 +97,36 @@ func Test_convert(t *testing.T) {
|
||||
args: []string{"--project-name", "compose", "down", "--remove-orphans"},
|
||||
want: []string{"compose", "--project-name", "compose", "down", "--remove-orphans"},
|
||||
},
|
||||
{
|
||||
name: "completion command",
|
||||
args: []string{"__complete", "up"},
|
||||
want: []string{"__complete", "compose", "up"},
|
||||
},
|
||||
{
|
||||
name: "string flag without argument",
|
||||
args: []string{"--log-level"},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := Convert(tt.args)
|
||||
assert.DeepEqual(t, tt.want, got)
|
||||
if tt.wantErr {
|
||||
if os.Getenv("BE_CRASHER") == "1" {
|
||||
Convert(tt.args)
|
||||
return
|
||||
}
|
||||
cmd := exec.Command(os.Args[0], "-test.run=^"+t.Name()+"$")
|
||||
cmd.Env = append(os.Environ(), "BE_CRASHER=1")
|
||||
err := cmd.Run()
|
||||
var e *exec.ExitError
|
||||
if errors.As(err, &e) && !e.Success() {
|
||||
return
|
||||
}
|
||||
t.Fatalf("process ran with err %v, want exit status 1", err)
|
||||
} else {
|
||||
got := Convert(tt.args)
|
||||
assert.DeepEqual(t, tt.want, got)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -27,6 +27,7 @@ import (
|
||||
"github.com/docker/cli/cli/command"
|
||||
cliopts "github.com/docker/cli/opts"
|
||||
ui "github.com/docker/compose/v2/pkg/progress"
|
||||
"github.com/docker/compose/v2/pkg/utils"
|
||||
buildkit "github.com/moby/buildkit/util/progress/progressui"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
@ -44,6 +45,7 @@ type buildOptions struct {
|
||||
ssh string
|
||||
builder string
|
||||
deps bool
|
||||
print bool
|
||||
}
|
||||
|
||||
func (opts buildOptions) toAPIBuildOptions(services []string) (api.BuildOptions, error) {
|
||||
@ -76,6 +78,8 @@ func (opts buildOptions) toAPIBuildOptions(services []string) (api.BuildOptions,
|
||||
Quiet: opts.quiet,
|
||||
Services: services,
|
||||
Deps: opts.deps,
|
||||
Memory: int64(opts.memory),
|
||||
Print: opts.print,
|
||||
SSHs: SSHKeys,
|
||||
Builder: builderName,
|
||||
}, nil
|
||||
@ -131,6 +135,7 @@ func buildCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service)
|
||||
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
|
||||
flags.BoolVar(&opts.print, "print", false, "Print equivalent bake file")
|
||||
|
||||
return cmd
|
||||
}
|
||||
@ -141,6 +146,8 @@ func runBuild(ctx context.Context, dockerCli command.Cli, backend api.Service, o
|
||||
return err
|
||||
}
|
||||
|
||||
services = addBuildDependencies(services, project)
|
||||
|
||||
if err := applyPlatforms(project, false); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -150,6 +157,23 @@ func runBuild(ctx context.Context, dockerCli command.Cli, backend api.Service, o
|
||||
return err
|
||||
}
|
||||
|
||||
apiBuildOptions.Memory = int64(opts.memory)
|
||||
return backend.Build(ctx, project, apiBuildOptions)
|
||||
}
|
||||
|
||||
func addBuildDependencies(services []string, project *types.Project) []string {
|
||||
servicesWithDependencies := utils.NewSet(services...)
|
||||
for _, service := range services {
|
||||
build := project.Services[service].Build
|
||||
if build != nil {
|
||||
for _, target := range build.AdditionalContexts {
|
||||
if s, found := strings.CutPrefix(target, types.ServicePrefix); found {
|
||||
servicesWithDependencies.Add(s)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(servicesWithDependencies) > len(services) {
|
||||
return addBuildDependencies(servicesWithDependencies.Elements(), project)
|
||||
}
|
||||
return servicesWithDependencies.Elements()
|
||||
}
|
||||
|
57
cmd/compose/build_test.go
Normal file
57
cmd/compose/build_test.go
Normal file
@ -0,0 +1,57 @@
|
||||
/*
|
||||
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 (
|
||||
"slices"
|
||||
"testing"
|
||||
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
"gotest.tools/v3/assert"
|
||||
)
|
||||
|
||||
func Test_addBuildDependencies(t *testing.T) {
|
||||
project := &types.Project{Services: types.Services{
|
||||
"test": types.ServiceConfig{
|
||||
Build: &types.BuildConfig{
|
||||
AdditionalContexts: map[string]string{
|
||||
"foo": "service:foo",
|
||||
"bar": "service:bar",
|
||||
},
|
||||
},
|
||||
},
|
||||
"foo": types.ServiceConfig{
|
||||
Build: &types.BuildConfig{
|
||||
AdditionalContexts: map[string]string{
|
||||
"zot": "service:zot",
|
||||
},
|
||||
},
|
||||
},
|
||||
"bar": types.ServiceConfig{
|
||||
Build: &types.BuildConfig{},
|
||||
},
|
||||
"zot": types.ServiceConfig{
|
||||
Build: &types.BuildConfig{},
|
||||
},
|
||||
}}
|
||||
|
||||
services := addBuildDependencies([]string{"test"}, project)
|
||||
expected := []string{"test", "foo", "bar", "zot"}
|
||||
slices.Sort(services)
|
||||
slices.Sort(expected)
|
||||
assert.DeepEqual(t, services, expected)
|
||||
}
|
93
cmd/compose/commit.go
Normal file
93
cmd/compose/commit.go
Normal file
@ -0,0 +1,93 @@
|
||||
/*
|
||||
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/cli/opts"
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type commitOptions struct {
|
||||
*ProjectOptions
|
||||
|
||||
service string
|
||||
reference string
|
||||
|
||||
pause bool
|
||||
comment string
|
||||
author string
|
||||
changes opts.ListOpts
|
||||
|
||||
index int
|
||||
}
|
||||
|
||||
func commitCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
|
||||
options := commitOptions{
|
||||
ProjectOptions: p,
|
||||
}
|
||||
cmd := &cobra.Command{
|
||||
Use: "commit [OPTIONS] SERVICE [REPOSITORY[:TAG]]",
|
||||
Short: "Create a new image from a service container's changes",
|
||||
Args: cobra.RangeArgs(1, 2),
|
||||
PreRunE: Adapt(func(ctx context.Context, args []string) error {
|
||||
options.service = args[0]
|
||||
if len(args) > 1 {
|
||||
options.reference = args[1]
|
||||
}
|
||||
|
||||
return nil
|
||||
}),
|
||||
RunE: Adapt(func(ctx context.Context, args []string) error {
|
||||
return runCommit(ctx, dockerCli, backend, options)
|
||||
}),
|
||||
ValidArgsFunction: completeServiceNames(dockerCli, p),
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
flags.IntVar(&options.index, "index", 0, "index of the container if service has multiple replicas.")
|
||||
|
||||
flags.BoolVarP(&options.pause, "pause", "p", true, "Pause container during commit")
|
||||
flags.StringVarP(&options.comment, "message", "m", "", "Commit message")
|
||||
flags.StringVarP(&options.author, "author", "a", "", `Author (e.g., "John Hannibal Smith <hannibal@a-team.com>")`)
|
||||
options.changes = opts.NewListOpts(nil)
|
||||
flags.VarP(&options.changes, "change", "c", "Apply Dockerfile instruction to the created image")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runCommit(ctx context.Context, dockerCli command.Cli, backend api.Service, options commitOptions) error {
|
||||
projectName, err := options.toProjectName(ctx, dockerCli)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
commitOptions := api.CommitOptions{
|
||||
Service: options.service,
|
||||
Reference: options.reference,
|
||||
Pause: options.pause,
|
||||
Comment: options.comment,
|
||||
Author: options.author,
|
||||
Changes: options.changes,
|
||||
Index: options.index,
|
||||
}
|
||||
|
||||
return backend.Commit(ctx, projectName, commitOptions)
|
||||
}
|
@ -90,3 +90,13 @@ func completeProfileNames(dockerCli command.Cli, p *ProjectOptions) validArgsFn
|
||||
return values, cobra.ShellCompDirectiveNoFileComp
|
||||
}
|
||||
}
|
||||
|
||||
func completeScaleArgs(cli command.Cli, p *ProjectOptions) cobra.CompletionFunc {
|
||||
return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
completions, directive := completeServiceNames(cli, p)(cmd, args, toComplete)
|
||||
for i, completion := range completions {
|
||||
completions[i] = completion + "="
|
||||
}
|
||||
return completions, directive
|
||||
}
|
||||
}
|
||||
|
@ -44,7 +44,6 @@ import (
|
||||
"github.com/docker/compose/v2/internal/experimental"
|
||||
"github.com/docker/compose/v2/internal/tracing"
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
"github.com/docker/compose/v2/pkg/compose"
|
||||
ui "github.com/docker/compose/v2/pkg/progress"
|
||||
"github.com/docker/compose/v2/pkg/remote"
|
||||
"github.com/docker/compose/v2/pkg/utils"
|
||||
@ -73,17 +72,16 @@ const (
|
||||
)
|
||||
|
||||
// rawEnv load a dot env file using docker/cli key=value parser, without attempt to interpolate or evaluate values
|
||||
func rawEnv(r io.Reader, filename string, lookup func(key string) (string, bool)) (map[string]string, error) {
|
||||
func rawEnv(r io.Reader, filename string, vars map[string]string, lookup func(key string) (string, bool)) error {
|
||||
lines, err := kvfile.ParseFromReader(r, lookup)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse env_file %s: %w", filename, err)
|
||||
return fmt.Errorf("failed to parse env_file %s: %w", filename, err)
|
||||
}
|
||||
vars := types.Mapping{}
|
||||
for _, line := range lines {
|
||||
key, value, _ := strings.Cut(line, "=")
|
||||
vars[key] = value
|
||||
}
|
||||
return vars, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
@ -121,17 +119,9 @@ func AdaptCmd(fn CobraCommand) func(cmd *cobra.Command, args []string) error {
|
||||
}()
|
||||
|
||||
err := fn(ctx, cmd, args)
|
||||
var composeErr compose.Error
|
||||
if api.IsErrCanceled(err) || errors.Is(ctx.Err(), context.Canceled) {
|
||||
err = dockercli.StatusError{
|
||||
StatusCode: 130,
|
||||
Status: compose.CanceledStatus,
|
||||
}
|
||||
}
|
||||
if errors.As(err, &composeErr) {
|
||||
err = dockercli.StatusError{
|
||||
StatusCode: composeErr.GetMetricsFailureCategory().ExitCode,
|
||||
Status: err.Error(),
|
||||
}
|
||||
}
|
||||
if ui.Mode == ui.ModeJSON {
|
||||
@ -179,7 +169,7 @@ func (o *ProjectOptions) WithServices(dockerCli command.Cli, fn ProjectServicesF
|
||||
return Adapt(func(ctx context.Context, args []string) error {
|
||||
options := []cli.ProjectOptionsFn{
|
||||
cli.WithResolvedPaths(true),
|
||||
cli.WithDiscardEnvFile,
|
||||
cli.WithoutEnvironmentResolution,
|
||||
}
|
||||
|
||||
project, metrics, err := o.ToProject(ctx, dockerCli, args, options...)
|
||||
@ -189,6 +179,11 @@ func (o *ProjectOptions) WithServices(dockerCli command.Cli, fn ProjectServicesF
|
||||
|
||||
ctx = context.WithValue(ctx, tracing.MetricsKey{}, metrics)
|
||||
|
||||
project, err = project.WithServicesEnvironmentResolved(true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return fn(ctx, project, args)
|
||||
})
|
||||
}
|
||||
@ -307,7 +302,7 @@ func (o *ProjectOptions) ToProject(ctx context.Context, dockerCli command.Cli, s
|
||||
|
||||
options, err := o.toProjectOptions(po...)
|
||||
if err != nil {
|
||||
return nil, metrics, compose.WrapComposeError(err)
|
||||
return nil, metrics, err
|
||||
}
|
||||
|
||||
options.WithListeners(func(event string, metadata map[string]any) {
|
||||
@ -339,7 +334,7 @@ func (o *ProjectOptions) ToProject(ctx context.Context, dockerCli command.Cli, s
|
||||
|
||||
project, err := options.LoadProject(ctx)
|
||||
if err != nil {
|
||||
return nil, metrics, compose.WrapComposeError(err)
|
||||
return nil, metrics, err
|
||||
}
|
||||
|
||||
if project.Name == "" {
|
||||
@ -381,17 +376,24 @@ func (o *ProjectOptions) remoteLoaders(dockerCli command.Cli) []loader.ResourceL
|
||||
if o.Offline {
|
||||
return nil
|
||||
}
|
||||
git := remote.NewGitRemoteLoader(o.Offline)
|
||||
git := remote.NewGitRemoteLoader(dockerCli, o.Offline)
|
||||
oci := remote.NewOCIRemoteLoader(dockerCli, o.Offline)
|
||||
return []loader.ResourceLoader{git, oci}
|
||||
}
|
||||
|
||||
func (o *ProjectOptions) toProjectOptions(po ...cli.ProjectOptionsFn) (*cli.ProjectOptions, error) {
|
||||
pwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return cli.NewProjectOptions(o.ConfigPaths,
|
||||
append(po,
|
||||
cli.WithWorkingDirectory(o.ProjectDir),
|
||||
// First apply os.Environment, always win
|
||||
cli.WithOsEnv,
|
||||
// set PWD as this variable is not consistently supported on Windows
|
||||
cli.WithEnv([]string{"PWD=" + pwd}),
|
||||
// Load PWD/.env if present and no explicit --env-file has been set
|
||||
cli.WithEnvFiles(o.EnvFiles...),
|
||||
// read dot env file to populate project environment
|
||||
@ -453,7 +455,7 @@ func RootCommand(dockerCli command.Cli, backend Backend) *cobra.Command { //noli
|
||||
}
|
||||
_ = cmd.Help()
|
||||
return dockercli.StatusError{
|
||||
StatusCode: compose.CommandSyntaxFailure.ExitCode,
|
||||
StatusCode: 1,
|
||||
Status: fmt.Sprintf("unknown docker command: %q", "compose "+args[0]),
|
||||
}
|
||||
},
|
||||
@ -547,10 +549,7 @@ func RootCommand(dockerCli command.Cli, backend Backend) *cobra.Command { //noli
|
||||
}
|
||||
|
||||
composeCmd := cmd
|
||||
for {
|
||||
if composeCmd.Name() == PluginName {
|
||||
break
|
||||
}
|
||||
for composeCmd.Name() != PluginName {
|
||||
if !composeCmd.HasParent() {
|
||||
return fmt.Errorf("error parsing command line, expected %q", PluginName)
|
||||
}
|
||||
@ -617,6 +616,7 @@ func RootCommand(dockerCli command.Cli, backend Backend) *cobra.Command { //noli
|
||||
execCommand(&opts, dockerCli, backend),
|
||||
attachCommand(&opts, dockerCli, backend),
|
||||
exportCommand(&opts, dockerCli, backend),
|
||||
commitCommand(&opts, dockerCli, backend),
|
||||
pauseCommand(&opts, dockerCli, backend),
|
||||
unpauseCommand(&opts, dockerCli, backend),
|
||||
topCommand(&opts, dockerCli, backend),
|
||||
@ -633,6 +633,7 @@ func RootCommand(dockerCli command.Cli, backend Backend) *cobra.Command { //noli
|
||||
scaleCommand(&opts, dockerCli, backend),
|
||||
statsCommand(&opts, dockerCli),
|
||||
watchCommand(&opts, dockerCli, backend),
|
||||
publishCommand(&opts, dockerCli, backend),
|
||||
alphaCommand(&opts, dockerCli, backend),
|
||||
)
|
||||
|
||||
|
@ -47,6 +47,7 @@ type configOptions struct {
|
||||
noInterpolate bool
|
||||
noNormalize bool
|
||||
noResolvePath bool
|
||||
noResolveEnv bool
|
||||
services bool
|
||||
volumes bool
|
||||
profiles bool
|
||||
@ -135,6 +136,7 @@ func configCommand(p *ProjectOptions, dockerCli command.Cli) *cobra.Command {
|
||||
flags.BoolVar(&opts.noNormalize, "no-normalize", false, "Don't normalize compose model")
|
||||
flags.BoolVar(&opts.noResolvePath, "no-path-resolution", false, "Don't resolve file paths")
|
||||
flags.BoolVar(&opts.noConsistency, "no-consistency", false, "Don't check model consistency - warning: may produce invalid Compose output")
|
||||
flags.BoolVar(&opts.noResolveEnv, "no-env-resolution", false, "Don't resolve service env files")
|
||||
|
||||
flags.BoolVar(&opts.services, "services", false, "Print the service names, one per line.")
|
||||
flags.BoolVar(&opts.volumes, "volumes", false, "Print the volume names, one per line.")
|
||||
@ -190,6 +192,13 @@ func runConfigInterpolate(ctx context.Context, dockerCli command.Cli, opts confi
|
||||
}
|
||||
}
|
||||
|
||||
if !opts.noResolveEnv {
|
||||
project, err = project.WithServicesEnvironmentResolved(true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if !opts.noConsistency {
|
||||
err := project.CheckContainerNameUnicity()
|
||||
if err != nil {
|
||||
@ -279,6 +288,22 @@ func formatModel(model map[string]any, format string) (content []byte, err error
|
||||
}
|
||||
|
||||
func runServices(ctx context.Context, dockerCli command.Cli, opts configOptions) error {
|
||||
if opts.noInterpolate {
|
||||
// we can't use ToProject, so the model we render here is only partially resolved
|
||||
data, err := opts.ToModel(ctx, dockerCli, nil, cli.WithoutEnvironmentResolution)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, ok := data["services"]; ok {
|
||||
for serviceName := range data["services"].(map[string]any) {
|
||||
_, _ = fmt.Fprintln(dockerCli.Out(), serviceName)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
project, err := opts.ToProject(ctx, dockerCli, nil, cli.WithoutEnvironmentResolution)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -287,6 +312,7 @@ func runServices(ctx context.Context, dockerCli command.Cli, opts configOptions)
|
||||
_, _ = fmt.Fprintln(dockerCli.Out(), serviceName)
|
||||
return nil
|
||||
})
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
@ -331,7 +357,6 @@ func runHash(ctx context.Context, dockerCli command.Cli, opts configOptions) err
|
||||
}
|
||||
|
||||
hash, err := compose.ServiceHash(s)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -26,7 +26,9 @@ import (
|
||||
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
)
|
||||
@ -46,6 +48,7 @@ type createOptions struct {
|
||||
timeout int
|
||||
quietPull bool
|
||||
scale []string
|
||||
AssumeYes bool
|
||||
}
|
||||
|
||||
func createCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
|
||||
@ -80,6 +83,15 @@ func createCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service
|
||||
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")
|
||||
flags.StringArrayVar(&opts.scale, "scale", []string{}, "Scale SERVICE to NUM instances. Overrides the `scale` setting in the Compose file if present.")
|
||||
flags.BoolVarP(&opts.AssumeYes, "yes", "y", false, `Assume "yes" as answer to all prompts and run non-interactively`)
|
||||
flags.SetNormalizeFunc(func(f *pflag.FlagSet, name string) pflag.NormalizedName {
|
||||
// assumeYes was introduced by mistake as `--y`
|
||||
if name == "y" {
|
||||
logrus.Warn("--y is deprecated, please use --yes instead")
|
||||
name = "yes"
|
||||
}
|
||||
return pflag.NormalizedName(name)
|
||||
})
|
||||
return cmd
|
||||
}
|
||||
|
||||
@ -107,6 +119,7 @@ func runCreate(ctx context.Context, _ command.Cli, backend api.Service, createOp
|
||||
Inherit: !createOpts.noInherit,
|
||||
Timeout: createOpts.GetTimeout(),
|
||||
QuietPull: createOpts.quietPull,
|
||||
AssumeYes: createOpts.AssumeYes,
|
||||
})
|
||||
}
|
||||
|
||||
@ -195,7 +208,9 @@ func applyScaleOpts(project *types.Project, opts []string) error {
|
||||
}
|
||||
|
||||
func (opts createOptions) isPullPolicyValid() bool {
|
||||
pullPolicies := []string{types.PullPolicyAlways, types.PullPolicyNever, types.PullPolicyBuild,
|
||||
types.PullPolicyMissing, types.PullPolicyIfNotPresent}
|
||||
pullPolicies := []string{
|
||||
types.PullPolicyAlways, types.PullPolicyNever, types.PullPolicyBuild,
|
||||
types.PullPolicyMissing, types.PullPolicyIfNotPresent,
|
||||
}
|
||||
return slices.Contains(pullPolicies, opts.Pull)
|
||||
}
|
||||
|
@ -40,7 +40,9 @@ func TestRunCreate(t *testing.T) {
|
||||
)
|
||||
|
||||
createOpts := createOptions{}
|
||||
buildOpts := buildOptions{}
|
||||
buildOpts := buildOptions{
|
||||
ProjectOptions: &ProjectOptions{},
|
||||
}
|
||||
project := sampleProject()
|
||||
err := runCreate(ctx, nil, backend, createOpts, buildOpts, project, nil)
|
||||
require.NoError(t, err)
|
||||
@ -58,7 +60,9 @@ func TestRunCreate_Build(t *testing.T) {
|
||||
createOpts := createOptions{
|
||||
Build: true,
|
||||
}
|
||||
buildOpts := buildOptions{}
|
||||
buildOpts := buildOptions{
|
||||
ProjectOptions: &ProjectOptions{},
|
||||
}
|
||||
project := sampleProject()
|
||||
err := runCreate(ctx, nil, backend, createOpts, buildOpts, project, nil)
|
||||
require.NoError(t, err)
|
||||
|
@ -86,7 +86,7 @@ func runExec(ctx context.Context, dockerCli command.Cli, backend api.Service, op
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
projectOptions, err := opts.composeOptions.toProjectOptions()
|
||||
projectOptions, err := opts.composeOptions.toProjectOptions() //nolint:staticcheck
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -50,7 +50,7 @@ func listCommand(dockerCli command.Cli, backend api.Service) *cobra.Command {
|
||||
ValidArgsFunction: noCompletion(),
|
||||
}
|
||||
lsCmd.Flags().StringVar(&lsOpts.Format, "format", "table", "Format the output. Values: [table | json]")
|
||||
lsCmd.Flags().BoolVarP(&lsOpts.Quiet, "quiet", "q", false, "Only display IDs")
|
||||
lsCmd.Flags().BoolVarP(&lsOpts.Quiet, "quiet", "q", false, "Only display project names")
|
||||
lsCmd.Flags().Var(&lsOpts.Filter, "filter", "Filter output based on conditions provided")
|
||||
lsCmd.Flags().BoolVarP(&lsOpts.All, "all", "a", false, "Show all stopped Compose projects")
|
||||
|
||||
|
@ -17,9 +17,21 @@
|
||||
package compose
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/compose-spec/compose-go/v2/cli"
|
||||
"github.com/compose-spec/compose-go/v2/template"
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/compose/v2/internal/tracing"
|
||||
ui "github.com/docker/compose/v2/pkg/progress"
|
||||
"github.com/docker/compose/v2/pkg/prompt"
|
||||
"github.com/docker/compose/v2/pkg/utils"
|
||||
)
|
||||
|
||||
@ -72,3 +84,208 @@ func applyPlatforms(project *types.Project, buildForSinglePlatform bool) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// isRemoteConfig checks if the main compose file is from a remote source (OCI or Git)
|
||||
func isRemoteConfig(dockerCli command.Cli, options buildOptions) bool {
|
||||
if len(options.ConfigPaths) == 0 {
|
||||
return false
|
||||
}
|
||||
remoteLoaders := options.remoteLoaders(dockerCli)
|
||||
for _, loader := range remoteLoaders {
|
||||
if loader.Accept(options.ConfigPaths[0]) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// checksForRemoteStack handles environment variable prompts for remote configurations
|
||||
func checksForRemoteStack(ctx context.Context, dockerCli command.Cli, project *types.Project, options buildOptions, assumeYes bool, cmdEnvs []string) error {
|
||||
if !isRemoteConfig(dockerCli, options) {
|
||||
return nil
|
||||
}
|
||||
if metrics, ok := ctx.Value(tracing.MetricsKey{}).(tracing.Metrics); ok && metrics.CountIncludesRemote > 0 {
|
||||
if err := confirmRemoteIncludes(dockerCli, options, assumeYes); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
displayLocationRemoteStack(dockerCli, project, options)
|
||||
return promptForInterpolatedVariables(ctx, dockerCli, options.ProjectOptions, assumeYes, cmdEnvs)
|
||||
}
|
||||
|
||||
// Prepare the values map and collect all variables info
|
||||
type varInfo struct {
|
||||
name string
|
||||
value string
|
||||
source string
|
||||
required bool
|
||||
defaultValue string
|
||||
}
|
||||
|
||||
// promptForInterpolatedVariables displays all variables and their values at once,
|
||||
// then prompts for confirmation
|
||||
func promptForInterpolatedVariables(ctx context.Context, dockerCli command.Cli, projectOptions *ProjectOptions, assumeYes bool, cmdEnvs []string) error {
|
||||
if assumeYes {
|
||||
return nil
|
||||
}
|
||||
|
||||
varsInfo, noVariables, err := extractInterpolationVariablesFromModel(ctx, dockerCli, projectOptions, cmdEnvs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if noVariables {
|
||||
return nil
|
||||
}
|
||||
|
||||
displayInterpolationVariables(dockerCli.Out(), varsInfo)
|
||||
|
||||
// Prompt for confirmation
|
||||
userInput := prompt.NewPrompt(dockerCli.In(), dockerCli.Out())
|
||||
msg := "\nDo you want to proceed with these variables? [Y/n]: "
|
||||
confirmed, err := userInput.Confirm(msg, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !confirmed {
|
||||
return fmt.Errorf("operation cancelled by user")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func extractInterpolationVariablesFromModel(ctx context.Context, dockerCli command.Cli, projectOptions *ProjectOptions, cmdEnvs []string) ([]varInfo, bool, error) {
|
||||
cmdEnvMap := extractEnvCLIDefined(cmdEnvs)
|
||||
|
||||
// Create a model without interpolation to extract variables
|
||||
opts := configOptions{
|
||||
noInterpolate: true,
|
||||
ProjectOptions: projectOptions,
|
||||
}
|
||||
|
||||
model, err := opts.ToModel(ctx, dockerCli, nil, cli.WithoutEnvironmentResolution)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
// Extract variables that need interpolation
|
||||
variables := template.ExtractVariables(model, template.DefaultPattern)
|
||||
if len(variables) == 0 {
|
||||
return nil, true, nil
|
||||
}
|
||||
|
||||
var varsInfo []varInfo
|
||||
proposedValues := make(map[string]string)
|
||||
|
||||
for name, variable := range variables {
|
||||
info := varInfo{
|
||||
name: name,
|
||||
required: variable.Required,
|
||||
defaultValue: variable.DefaultValue,
|
||||
}
|
||||
|
||||
// Determine value and source based on priority
|
||||
if value, exists := cmdEnvMap[name]; exists {
|
||||
info.value = value
|
||||
info.source = "command-line"
|
||||
proposedValues[name] = value
|
||||
} else if value, exists := os.LookupEnv(name); exists {
|
||||
info.value = value
|
||||
info.source = "environment"
|
||||
proposedValues[name] = value
|
||||
} else if variable.DefaultValue != "" {
|
||||
info.value = variable.DefaultValue
|
||||
info.source = "compose file"
|
||||
proposedValues[name] = variable.DefaultValue
|
||||
} else {
|
||||
info.value = "<unset>"
|
||||
info.source = "none"
|
||||
}
|
||||
|
||||
varsInfo = append(varsInfo, info)
|
||||
}
|
||||
return varsInfo, false, nil
|
||||
}
|
||||
|
||||
func extractEnvCLIDefined(cmdEnvs []string) map[string]string {
|
||||
// Parse command-line environment variables
|
||||
cmdEnvMap := make(map[string]string)
|
||||
for _, env := range cmdEnvs {
|
||||
parts := strings.SplitN(env, "=", 2)
|
||||
if len(parts) == 2 {
|
||||
cmdEnvMap[parts[0]] = parts[1]
|
||||
}
|
||||
}
|
||||
return cmdEnvMap
|
||||
}
|
||||
|
||||
func displayInterpolationVariables(writer io.Writer, varsInfo []varInfo) {
|
||||
// Display all variables in a table format
|
||||
_, _ = fmt.Fprintln(writer, "\nFound the following variables in configuration:")
|
||||
|
||||
w := tabwriter.NewWriter(writer, 0, 0, 3, ' ', 0)
|
||||
_, _ = fmt.Fprintln(w, "VARIABLE\tVALUE\tSOURCE\tREQUIRED\tDEFAULT")
|
||||
sort.Slice(varsInfo, func(a, b int) bool {
|
||||
return varsInfo[a].name < varsInfo[b].name
|
||||
})
|
||||
for _, info := range varsInfo {
|
||||
required := "no"
|
||||
if info.required {
|
||||
required = "yes"
|
||||
}
|
||||
_, _ = fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\n",
|
||||
info.name,
|
||||
info.value,
|
||||
info.source,
|
||||
required,
|
||||
info.defaultValue,
|
||||
)
|
||||
}
|
||||
_ = w.Flush()
|
||||
}
|
||||
|
||||
func displayLocationRemoteStack(dockerCli command.Cli, project *types.Project, options buildOptions) {
|
||||
mainComposeFile := options.ProjectOptions.ConfigPaths[0] //nolint:staticcheck
|
||||
if ui.Mode != ui.ModeQuiet && ui.Mode != ui.ModeJSON {
|
||||
_, _ = fmt.Fprintf(dockerCli.Out(), "Your compose stack %q is stored in %q\n", mainComposeFile, project.WorkingDir)
|
||||
}
|
||||
}
|
||||
|
||||
func confirmRemoteIncludes(dockerCli command.Cli, options buildOptions, assumeYes bool) error {
|
||||
if assumeYes {
|
||||
return nil
|
||||
}
|
||||
|
||||
var remoteIncludes []string
|
||||
remoteLoaders := options.ProjectOptions.remoteLoaders(dockerCli) //nolint:staticcheck
|
||||
for _, cf := range options.ProjectOptions.ConfigPaths { //nolint:staticcheck
|
||||
for _, loader := range remoteLoaders {
|
||||
if loader.Accept(cf) {
|
||||
remoteIncludes = append(remoteIncludes, cf)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(remoteIncludes) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
_, _ = fmt.Fprintln(dockerCli.Out(), "\nWarning: This Compose project includes files from remote sources:")
|
||||
for _, include := range remoteIncludes {
|
||||
_, _ = fmt.Fprintf(dockerCli.Out(), " - %s\n", include)
|
||||
}
|
||||
_, _ = fmt.Fprintln(dockerCli.Out(), "\nRemote includes could potentially be malicious. Make sure you trust the source.")
|
||||
|
||||
msg := "Do you want to continue? [y/N]: "
|
||||
confirmed, err := prompt.NewPrompt(dockerCli.In(), dockerCli.Out()).Confirm(msg, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !confirmed {
|
||||
return fmt.Errorf("operation cancelled by user")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -17,10 +17,20 @@
|
||||
package compose
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
"github.com/docker/cli/cli/streams"
|
||||
"github.com/docker/compose/v2/pkg/mocks"
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.uber.org/mock/gomock"
|
||||
)
|
||||
|
||||
func TestApplyPlatforms_InferFromRuntime(t *testing.T) {
|
||||
@ -128,3 +138,257 @@ func TestApplyPlatforms_UnsupportedPlatform(t *testing.T) {
|
||||
`service "test" build.platforms does not support value set by DOCKER_DEFAULT_PLATFORM: commodore/64`)
|
||||
})
|
||||
}
|
||||
|
||||
func TestIsRemoteConfig(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
cli := mocks.NewMockCli(ctrl)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
configPaths []string
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
name: "empty config paths",
|
||||
configPaths: []string{},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "local file",
|
||||
configPaths: []string{"docker-compose.yaml"},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "OCI reference",
|
||||
configPaths: []string{"oci://registry.example.com/stack:latest"},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "GIT reference",
|
||||
configPaths: []string{"git://github.com/user/repo.git"},
|
||||
want: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
opts := buildOptions{
|
||||
ProjectOptions: &ProjectOptions{
|
||||
ConfigPaths: tt.configPaths,
|
||||
},
|
||||
}
|
||||
got := isRemoteConfig(cli, opts)
|
||||
require.Equal(t, tt.want, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDisplayLocationRemoteStack(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
cli := mocks.NewMockCli(ctrl)
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
cli.EXPECT().Out().Return(streams.NewOut(buf)).AnyTimes()
|
||||
|
||||
project := &types.Project{
|
||||
Name: "test-project",
|
||||
WorkingDir: "/tmp/test",
|
||||
}
|
||||
|
||||
options := buildOptions{
|
||||
ProjectOptions: &ProjectOptions{
|
||||
ConfigPaths: []string{"oci://registry.example.com/stack:latest"},
|
||||
},
|
||||
}
|
||||
|
||||
displayLocationRemoteStack(cli, project, options)
|
||||
|
||||
output := buf.String()
|
||||
require.Equal(t, output, fmt.Sprintf("Your compose stack %q is stored in %q\n", "oci://registry.example.com/stack:latest", "/tmp/test"))
|
||||
}
|
||||
|
||||
func TestDisplayInterpolationVariables(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
|
||||
// Create a temporary directory for the test
|
||||
tmpDir, err := os.MkdirTemp("", "compose-test")
|
||||
require.NoError(t, err)
|
||||
defer func() { _ = os.RemoveAll(tmpDir) }()
|
||||
|
||||
// Create a temporary compose file
|
||||
composeContent := `
|
||||
services:
|
||||
app:
|
||||
image: nginx
|
||||
environment:
|
||||
- TEST_VAR=${TEST_VAR:?required} # required with default
|
||||
- API_KEY=${API_KEY:?} # required without default
|
||||
- DEBUG=${DEBUG:-true} # optional with default
|
||||
- UNSET_VAR # optional without default
|
||||
`
|
||||
composePath := filepath.Join(tmpDir, "docker-compose.yml")
|
||||
err = os.WriteFile(composePath, []byte(composeContent), 0o644)
|
||||
require.NoError(t, err)
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
cli := mocks.NewMockCli(ctrl)
|
||||
cli.EXPECT().Out().Return(streams.NewOut(buf)).AnyTimes()
|
||||
|
||||
// Create ProjectOptions with the temporary compose file
|
||||
projectOptions := &ProjectOptions{
|
||||
ConfigPaths: []string{composePath},
|
||||
}
|
||||
|
||||
// Set up the context with necessary environment variables
|
||||
ctx := context.Background()
|
||||
_ = os.Setenv("TEST_VAR", "test-value")
|
||||
_ = os.Setenv("API_KEY", "123456")
|
||||
defer func() {
|
||||
_ = os.Unsetenv("TEST_VAR")
|
||||
_ = os.Unsetenv("API_KEY")
|
||||
}()
|
||||
|
||||
// Extract variables from the model
|
||||
info, noVariables, err := extractInterpolationVariablesFromModel(ctx, cli, projectOptions, []string{})
|
||||
require.NoError(t, err)
|
||||
require.False(t, noVariables)
|
||||
|
||||
// Display the variables
|
||||
displayInterpolationVariables(cli.Out(), info)
|
||||
|
||||
// Expected output format with proper spacing
|
||||
expected := "\nFound the following variables in configuration:\n" +
|
||||
"VARIABLE VALUE SOURCE REQUIRED DEFAULT\n" +
|
||||
"API_KEY 123456 environment yes \n" +
|
||||
"DEBUG true compose file no true\n" +
|
||||
"TEST_VAR test-value environment yes \n"
|
||||
|
||||
// Normalize spaces and newlines for comparison
|
||||
normalizeSpaces := func(s string) string {
|
||||
// Replace multiple spaces with a single space
|
||||
s = strings.Join(strings.Fields(strings.TrimSpace(s)), " ")
|
||||
return s
|
||||
}
|
||||
|
||||
actualOutput := buf.String()
|
||||
|
||||
// Compare normalized strings
|
||||
require.Equal(t,
|
||||
normalizeSpaces(expected),
|
||||
normalizeSpaces(actualOutput),
|
||||
"\nExpected:\n%s\nGot:\n%s", expected, actualOutput)
|
||||
}
|
||||
|
||||
func TestConfirmRemoteIncludes(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
cli := mocks.NewMockCli(ctrl)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
opts buildOptions
|
||||
assumeYes bool
|
||||
userInput string
|
||||
wantErr bool
|
||||
errMessage string
|
||||
wantPrompt bool
|
||||
wantOutput string
|
||||
}{
|
||||
{
|
||||
name: "no remote includes",
|
||||
opts: buildOptions{
|
||||
ProjectOptions: &ProjectOptions{
|
||||
ConfigPaths: []string{
|
||||
"docker-compose.yaml",
|
||||
"./local/path/compose.yaml",
|
||||
},
|
||||
},
|
||||
},
|
||||
assumeYes: false,
|
||||
wantErr: false,
|
||||
wantPrompt: false,
|
||||
},
|
||||
{
|
||||
name: "assume yes with remote includes",
|
||||
opts: buildOptions{
|
||||
ProjectOptions: &ProjectOptions{
|
||||
ConfigPaths: []string{
|
||||
"oci://registry.example.com/stack:latest",
|
||||
"git://github.com/user/repo.git",
|
||||
},
|
||||
},
|
||||
},
|
||||
assumeYes: true,
|
||||
wantErr: false,
|
||||
wantPrompt: false,
|
||||
},
|
||||
{
|
||||
name: "user confirms remote includes",
|
||||
opts: buildOptions{
|
||||
ProjectOptions: &ProjectOptions{
|
||||
ConfigPaths: []string{
|
||||
"oci://registry.example.com/stack:latest",
|
||||
"git://github.com/user/repo.git",
|
||||
},
|
||||
},
|
||||
},
|
||||
assumeYes: false,
|
||||
userInput: "y\n",
|
||||
wantErr: false,
|
||||
wantPrompt: true,
|
||||
wantOutput: "\nWarning: This Compose project includes files from remote sources:\n" +
|
||||
" - oci://registry.example.com/stack:latest\n" +
|
||||
" - git://github.com/user/repo.git\n" +
|
||||
"\nRemote includes could potentially be malicious. Make sure you trust the source.\n" +
|
||||
"Do you want to continue? [y/N]: ",
|
||||
},
|
||||
{
|
||||
name: "user rejects remote includes",
|
||||
opts: buildOptions{
|
||||
ProjectOptions: &ProjectOptions{
|
||||
ConfigPaths: []string{
|
||||
"oci://registry.example.com/stack:latest",
|
||||
},
|
||||
},
|
||||
},
|
||||
assumeYes: false,
|
||||
userInput: "n\n",
|
||||
wantErr: true,
|
||||
errMessage: "operation cancelled by user",
|
||||
wantPrompt: true,
|
||||
wantOutput: "\nWarning: This Compose project includes files from remote sources:\n" +
|
||||
" - oci://registry.example.com/stack:latest\n" +
|
||||
"\nRemote includes could potentially be malicious. Make sure you trust the source.\n" +
|
||||
"Do you want to continue? [y/N]: ",
|
||||
},
|
||||
}
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
cli.EXPECT().Out().Return(streams.NewOut(buf)).AnyTimes()
|
||||
|
||||
if tt.wantPrompt {
|
||||
inbuf := io.NopCloser(bytes.NewBufferString(tt.userInput))
|
||||
cli.EXPECT().In().Return(streams.NewIn(inbuf)).AnyTimes()
|
||||
}
|
||||
|
||||
err := confirmRemoteIncludes(cli, tt.opts, tt.assumeYes)
|
||||
|
||||
if tt.wantErr {
|
||||
require.Error(t, err)
|
||||
require.Equal(t, tt.errMessage, err.Error())
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
if tt.wantOutput != "" {
|
||||
require.Equal(t, tt.wantOutput, buf.String())
|
||||
}
|
||||
buf.Reset()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -27,6 +27,7 @@ import (
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
"github.com/docker/compose/v2/pkg/mocks"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.uber.org/mock/gomock"
|
||||
)
|
||||
|
||||
@ -74,13 +75,13 @@ func TestPsTable(t *testing.T) {
|
||||
cli.EXPECT().Out().Return(stdout).AnyTimes()
|
||||
cli.EXPECT().ConfigFile().Return(&configfile.ConfigFile{}).AnyTimes()
|
||||
err = runPs(ctx, cli, backend, nil, opts)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = f.Seek(0, 0)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
output, err := os.ReadFile(out)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Contains(t, string(output), "8080/tcp, 8443/tcp")
|
||||
}
|
||||
|
@ -18,17 +18,22 @@ package compose
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"github.com/docker/cli/cli"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
type publishOptions struct {
|
||||
*ProjectOptions
|
||||
resolveImageDigests bool
|
||||
ociVersion string
|
||||
withEnvironment bool
|
||||
assumeYes bool
|
||||
}
|
||||
|
||||
func publishCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
|
||||
@ -41,22 +46,39 @@ func publishCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Servic
|
||||
RunE: Adapt(func(ctx context.Context, args []string) error {
|
||||
return runPublish(ctx, dockerCli, backend, opts, args[0])
|
||||
}),
|
||||
Args: cobra.ExactArgs(1),
|
||||
Args: cli.ExactArgs(1),
|
||||
}
|
||||
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)")
|
||||
flags.StringVar(&opts.ociVersion, "oci-version", "", "OCI image/artifact specification version (automatically determined by default)")
|
||||
flags.BoolVar(&opts.withEnvironment, "with-env", false, "Include environment variables in the published OCI artifact")
|
||||
flags.BoolVarP(&opts.assumeYes, "yes", "y", false, `Assume "yes" as answer to all prompts`)
|
||||
flags.SetNormalizeFunc(func(f *pflag.FlagSet, name string) pflag.NormalizedName {
|
||||
// assumeYes was introduced by mistake as `--y`
|
||||
if name == "y" {
|
||||
logrus.Warn("--y is deprecated, please use --yes instead")
|
||||
name = "yes"
|
||||
}
|
||||
return pflag.NormalizedName(name)
|
||||
})
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runPublish(ctx context.Context, dockerCli command.Cli, backend api.Service, opts publishOptions, repository string) error {
|
||||
project, _, err := opts.ToProject(ctx, dockerCli, nil)
|
||||
project, metrics, err := opts.ToProject(ctx, dockerCli, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if metrics.CountIncludesLocal > 0 {
|
||||
return errors.New("cannot publish compose file with local includes")
|
||||
}
|
||||
|
||||
return backend.Publish(ctx, project, repository, api.PublishOptions{
|
||||
ResolveImageDigests: opts.resolveImageDigests,
|
||||
OCIVersion: api.OCIVersion(opts.ociVersion),
|
||||
WithEnvironment: opts.withEnvironment,
|
||||
AssumeYes: opts.assumeYes,
|
||||
})
|
||||
}
|
||||
|
@ -48,12 +48,15 @@ func pullCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service)
|
||||
cmd := &cobra.Command{
|
||||
Use: "pull [OPTIONS] [SERVICE...]",
|
||||
Short: "Pull service images",
|
||||
PreRunE: Adapt(func(ctx context.Context, args []string) error {
|
||||
if opts.noParallel {
|
||||
PreRunE: func(cmd *cobra.Command, args []string) error {
|
||||
if cmd.Flags().Changed("no-parallel") {
|
||||
fmt.Fprint(os.Stderr, aec.Apply("option '--no-parallel' is DEPRECATED and will be ignored.\n", aec.RedF))
|
||||
}
|
||||
if cmd.Flags().Changed("parallel") {
|
||||
fmt.Fprint(os.Stderr, aec.Apply("option '--parallel' is DEPRECATED and will be ignored.\n", aec.RedF))
|
||||
}
|
||||
return nil
|
||||
}),
|
||||
},
|
||||
RunE: Adapt(func(ctx context.Context, args []string) error {
|
||||
return runPull(ctx, dockerCli, backend, opts, args)
|
||||
}),
|
||||
@ -64,7 +67,7 @@ func pullCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service)
|
||||
cmd.Flags().BoolVar(&opts.includeDeps, "include-deps", false, "Also pull services declared as dependencies")
|
||||
cmd.Flags().BoolVar(&opts.parallel, "parallel", true, "DEPRECATED pull multiple images in parallel")
|
||||
flags.MarkHidden("parallel") //nolint:errcheck
|
||||
cmd.Flags().BoolVar(&opts.parallel, "no-parallel", true, "DEPRECATED disable parallel pulling")
|
||||
cmd.Flags().BoolVar(&opts.noParallel, "no-parallel", true, "DEPRECATED disable parallel pulling")
|
||||
flags.MarkHidden("no-parallel") //nolint:errcheck
|
||||
cmd.Flags().BoolVar(&opts.ignorePullFailures, "ignore-pull-failures", false, "Pull what it can and ignores images with pull failures")
|
||||
cmd.Flags().BoolVar(&opts.noBuildable, "ignore-buildable", false, "Ignore images that can be built")
|
||||
|
@ -19,8 +19,10 @@ package compose
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/compose-spec/compose-go/v2/dotenv"
|
||||
"github.com/compose-spec/compose-go/v2/format"
|
||||
xprogress "github.com/moby/buildkit/util/progress/progressui"
|
||||
"github.com/sirupsen/logrus"
|
||||
@ -44,6 +46,7 @@ type runOptions struct {
|
||||
Service string
|
||||
Command []string
|
||||
environment []string
|
||||
envFiles []string
|
||||
Detach bool
|
||||
Remove bool
|
||||
noTty bool
|
||||
@ -63,6 +66,8 @@ type runOptions struct {
|
||||
name string
|
||||
noDeps bool
|
||||
ignoreOrphans bool
|
||||
removeOrphans bool
|
||||
quiet bool
|
||||
quietPull bool
|
||||
}
|
||||
|
||||
@ -115,6 +120,29 @@ func (options runOptions) apply(project *types.Project) (*types.Project, error)
|
||||
return project, nil
|
||||
}
|
||||
|
||||
func (options runOptions) getEnvironment() (types.Mapping, error) {
|
||||
environment := types.NewMappingWithEquals(options.environment).Resolve(os.LookupEnv).ToMapping()
|
||||
for _, file := range options.envFiles {
|
||||
f, err := os.Open(file)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
vars, err := dotenv.ParseWithLookup(f, func(k string) (string, bool) {
|
||||
value, ok := environment[k]
|
||||
return value, ok
|
||||
})
|
||||
if err != nil {
|
||||
return nil, nil
|
||||
}
|
||||
for k, v := range vars {
|
||||
if _, ok := environment[k]; !ok {
|
||||
environment[k] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
return environment, nil
|
||||
}
|
||||
|
||||
func runCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
|
||||
options := runOptions{
|
||||
composeOptions: &composeOptions{
|
||||
@ -153,10 +181,24 @@ func runCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *
|
||||
options.noTty = !options.tty
|
||||
}
|
||||
}
|
||||
if options.quiet {
|
||||
progress.Mode = progress.ModeQuiet
|
||||
devnull, err := os.Open(os.DevNull)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
os.Stdout = devnull
|
||||
}
|
||||
createOpts.pullChanged = cmd.Flags().Changed("pull")
|
||||
return nil
|
||||
}),
|
||||
RunE: Adapt(func(ctx context.Context, args []string) error {
|
||||
project, _, err := p.ToProject(ctx, dockerCli, []string{options.Service}, cgo.WithResolvedPaths(true), cgo.WithDiscardEnvFile)
|
||||
project, _, err := p.ToProject(ctx, dockerCli, []string{options.Service}, cgo.WithResolvedPaths(true), cgo.WithoutEnvironmentResolution)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
project, err = project.WithServicesEnvironmentResolved(true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -173,6 +215,7 @@ func runCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *
|
||||
flags := cmd.Flags()
|
||||
flags.BoolVarP(&options.Detach, "detach", "d", false, "Run container in background and print container ID")
|
||||
flags.StringArrayVarP(&options.environment, "env", "e", []string{}, "Set environment variables")
|
||||
flags.StringArrayVar(&options.envFiles, "env-from-file", []string{}, "Set environment variables from file")
|
||||
flags.StringArrayVarP(&options.labels, "label", "l", []string{}, "Add or override a label")
|
||||
flags.BoolVar(&options.Remove, "rm", false, "Automatically remove the container when it exits")
|
||||
flags.BoolVarP(&options.noTty, "no-TTY", "T", !dockerCli.Out().IsTerminal(), "Disable pseudo-TTY allocation (default: auto-detected)")
|
||||
@ -187,9 +230,12 @@ func runCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *
|
||||
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.BoolVarP(&options.servicePorts, "service-ports", "P", false, "Run command with all service's ports enabled and mapped to the host")
|
||||
flags.StringVar(&createOpts.Pull, "pull", "policy", `Pull image before running ("always"|"missing"|"never")`)
|
||||
flags.BoolVarP(&options.quiet, "quiet", "q", false, "Don't print anything to STDOUT")
|
||||
flags.BoolVar(&buildOpts.quiet, "quiet-build", false, "Suppress progress output from the build process")
|
||||
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")
|
||||
flags.BoolVar(&options.removeOrphans, "remove-orphans", false, "Remove containers for services not defined in the Compose file")
|
||||
|
||||
cmd.Flags().BoolVarP(&options.interactive, "interactive", "i", true, "Keep STDIN open even if not attached")
|
||||
cmd.Flags().BoolVarP(&options.tty, "tty", "t", true, "Allocate a pseudo-TTY")
|
||||
@ -221,6 +267,10 @@ func runRun(ctx context.Context, backend api.Service, project *types.Project, op
|
||||
return err
|
||||
}
|
||||
|
||||
if err := checksForRemoteStack(ctx, dockerCli, project, buildOpts, createOpts.AssumeYes, []string{}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = progress.Run(ctx, func(ctx context.Context) error {
|
||||
var buildForDeps *api.BuildOptions
|
||||
if !createOpts.noBuild {
|
||||
@ -257,6 +307,11 @@ func runRun(ctx context.Context, backend api.Service, project *types.Project, op
|
||||
buildForRun = &bo
|
||||
}
|
||||
|
||||
environment, err := options.getEnvironment()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// start container and attach to container streams
|
||||
runOpts := api.RunOptions{
|
||||
Build: buildForRun,
|
||||
@ -271,7 +326,7 @@ func runRun(ctx context.Context, backend api.Service, project *types.Project, op
|
||||
User: options.user,
|
||||
CapAdd: options.capAdd.GetAll(),
|
||||
CapDrop: options.capDrop.GetAll(),
|
||||
Environment: options.environment,
|
||||
Environment: environment.Values(),
|
||||
Entrypoint: options.entrypointCmd,
|
||||
Labels: labels,
|
||||
UseNetworkAliases: options.useAliases,
|
||||
@ -314,6 +369,7 @@ func startDependencies(ctx context.Context, backend api.Service, project types.P
|
||||
err := backend.Create(ctx, &project, api.CreateOptions{
|
||||
Build: buildOpts,
|
||||
IgnoreOrphans: options.ignoreOrphans,
|
||||
RemoveOrphans: options.removeOrphans,
|
||||
QuietPull: options.quietPull,
|
||||
})
|
||||
if err != nil {
|
||||
|
@ -51,7 +51,7 @@ func scaleCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service)
|
||||
}
|
||||
return runScale(ctx, dockerCli, backend, opts, serviceTuples)
|
||||
}),
|
||||
ValidArgsFunction: completeServiceNames(dockerCli, p),
|
||||
ValidArgsFunction: completeScaleArgs(dockerCli, p),
|
||||
}
|
||||
flags := scaleCmd.Flags()
|
||||
flags.BoolVar(&opts.noDeps, "no-deps", false, "Don't start linked services")
|
||||
@ -92,7 +92,6 @@ func parseServicesReplicasArgs(args []string) (map[string]int, error) {
|
||||
return nil, fmt.Errorf("invalid scale specifier: %s", arg)
|
||||
}
|
||||
intValue, err := strconv.Atoi(val)
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid scale specifier: can't parse replica value as int: %v", arg)
|
||||
}
|
||||
|
@ -56,7 +56,7 @@ func statsCommand(p *ProjectOptions, dockerCli command.Cli) *cobra.Command {
|
||||
'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`)
|
||||
Refer to https://docs.docker.com/engine/cli/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
|
||||
|
@ -49,6 +49,11 @@ func topCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *
|
||||
return topCmd
|
||||
}
|
||||
|
||||
type (
|
||||
topHeader map[string]int // maps a proc title to its output index
|
||||
topEntries map[string]string
|
||||
)
|
||||
|
||||
func runTop(ctx context.Context, dockerCli command.Cli, backend api.Service, opts topOptions, services []string) error {
|
||||
projectName, err := opts.toProjectName(ctx, dockerCli)
|
||||
if err != nil {
|
||||
@ -63,30 +68,76 @@ func runTop(ctx context.Context, dockerCli command.Cli, backend api.Service, opt
|
||||
return containers[i].Name < containers[j].Name
|
||||
})
|
||||
|
||||
for _, container := range containers {
|
||||
_, _ = fmt.Fprintf(dockerCli.Out(), "%s\n", container.Name)
|
||||
err := psPrinter(dockerCli.Out(), func(w io.Writer) {
|
||||
for _, proc := range container.Processes {
|
||||
info := []interface{}{}
|
||||
for _, p := range proc {
|
||||
info = append(info, p)
|
||||
}
|
||||
_, _ = fmt.Fprintf(w, strings.Repeat("%s\t", len(info))+"\n", info...)
|
||||
header, entries := collectTop(containers)
|
||||
return topPrint(dockerCli.Out(), header, entries)
|
||||
}
|
||||
|
||||
func collectTop(containers []api.ContainerProcSummary) (topHeader, []topEntries) {
|
||||
// map column name to its header (should keep working if backend.Top returns
|
||||
// varying columns for different containers)
|
||||
header := topHeader{"SERVICE": 0, "#": 1}
|
||||
|
||||
// assume one process per container and grow if needed
|
||||
entries := make([]topEntries, 0, len(containers))
|
||||
|
||||
for _, container := range containers {
|
||||
for _, proc := range container.Processes {
|
||||
entry := topEntries{
|
||||
"SERVICE": container.Service,
|
||||
"#": container.Replica,
|
||||
}
|
||||
_, _ = fmt.Fprintln(w)
|
||||
},
|
||||
container.Titles...)
|
||||
if err != nil {
|
||||
return err
|
||||
for i, title := range container.Titles {
|
||||
if _, exists := header[title]; !exists {
|
||||
header[title] = len(header)
|
||||
}
|
||||
entry[title] = proc[i]
|
||||
}
|
||||
entries = append(entries, entry)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
// ensure CMD is the right-most column
|
||||
if pos, ok := header["CMD"]; ok {
|
||||
maxPos := pos
|
||||
for h, i := range header {
|
||||
if i > maxPos {
|
||||
maxPos = i
|
||||
}
|
||||
if i > pos {
|
||||
header[h] = i - 1
|
||||
}
|
||||
}
|
||||
header["CMD"] = maxPos
|
||||
}
|
||||
|
||||
return header, entries
|
||||
}
|
||||
|
||||
func psPrinter(out io.Writer, printer func(writer io.Writer), headers ...string) error {
|
||||
w := tabwriter.NewWriter(out, 5, 1, 3, ' ', 0)
|
||||
_, _ = fmt.Fprintln(w, strings.Join(headers, "\t"))
|
||||
printer(w)
|
||||
func topPrint(out io.Writer, headers topHeader, rows []topEntries) error {
|
||||
if len(rows) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
w := tabwriter.NewWriter(out, 4, 1, 2, ' ', 0)
|
||||
|
||||
// write headers in the order we've encountered them
|
||||
h := make([]string, len(headers))
|
||||
for title, index := range headers {
|
||||
h[index] = title
|
||||
}
|
||||
_, _ = fmt.Fprintln(w, strings.Join(h, "\t"))
|
||||
|
||||
for _, row := range rows {
|
||||
// write proc data in header order
|
||||
r := make([]string, len(headers))
|
||||
for title, index := range headers {
|
||||
if v, ok := row[title]; ok {
|
||||
r[index] = v
|
||||
} else {
|
||||
r[index] = "-"
|
||||
}
|
||||
}
|
||||
_, _ = fmt.Fprintln(w, strings.Join(r, "\t"))
|
||||
}
|
||||
return w.Flush()
|
||||
}
|
||||
|
329
cmd/compose/top_test.go
Normal file
329
cmd/compose/top_test.go
Normal file
@ -0,0 +1,329 @@
|
||||
/*
|
||||
Copyright 2024 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 (
|
||||
"bytes"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var topTestCases = []struct {
|
||||
name string
|
||||
titles []string
|
||||
procs [][]string
|
||||
|
||||
header topHeader
|
||||
entries []topEntries
|
||||
output string
|
||||
}{
|
||||
{
|
||||
name: "noprocs",
|
||||
titles: []string{"UID", "PID", "PPID", "C", "STIME", "TTY", "TIME", "CMD"},
|
||||
procs: [][]string{},
|
||||
header: topHeader{"SERVICE": 0, "#": 1},
|
||||
entries: []topEntries{},
|
||||
output: "",
|
||||
},
|
||||
{
|
||||
name: "simple",
|
||||
titles: []string{"UID", "PID", "PPID", "C", "STIME", "TTY", "TIME", "CMD"},
|
||||
procs: [][]string{{"root", "1", "1", "0", "12:00", "?", "00:00:01", "/entrypoint"}},
|
||||
header: topHeader{
|
||||
"SERVICE": 0,
|
||||
"#": 1,
|
||||
"UID": 2,
|
||||
"PID": 3,
|
||||
"PPID": 4,
|
||||
"C": 5,
|
||||
"STIME": 6,
|
||||
"TTY": 7,
|
||||
"TIME": 8,
|
||||
"CMD": 9,
|
||||
},
|
||||
entries: []topEntries{
|
||||
{
|
||||
"SERVICE": "simple",
|
||||
"#": "1",
|
||||
"UID": "root",
|
||||
"PID": "1",
|
||||
"PPID": "1",
|
||||
"C": "0",
|
||||
"STIME": "12:00",
|
||||
"TTY": "?",
|
||||
"TIME": "00:00:01",
|
||||
"CMD": "/entrypoint",
|
||||
},
|
||||
},
|
||||
output: trim(`
|
||||
SERVICE # UID PID PPID C STIME TTY TIME CMD
|
||||
simple 1 root 1 1 0 12:00 ? 00:00:01 /entrypoint
|
||||
`),
|
||||
},
|
||||
{
|
||||
name: "noppid",
|
||||
titles: []string{"UID", "PID", "C", "STIME", "TTY", "TIME", "CMD"},
|
||||
procs: [][]string{{"root", "1", "0", "12:00", "?", "00:00:02", "/entrypoint"}},
|
||||
header: topHeader{
|
||||
"SERVICE": 0,
|
||||
"#": 1,
|
||||
"UID": 2,
|
||||
"PID": 3,
|
||||
"C": 4,
|
||||
"STIME": 5,
|
||||
"TTY": 6,
|
||||
"TIME": 7,
|
||||
"CMD": 8,
|
||||
},
|
||||
entries: []topEntries{
|
||||
{
|
||||
"SERVICE": "noppid",
|
||||
"#": "1",
|
||||
"UID": "root",
|
||||
"PID": "1",
|
||||
"C": "0",
|
||||
"STIME": "12:00",
|
||||
"TTY": "?",
|
||||
"TIME": "00:00:02",
|
||||
"CMD": "/entrypoint",
|
||||
},
|
||||
},
|
||||
output: trim(`
|
||||
SERVICE # UID PID C STIME TTY TIME CMD
|
||||
noppid 1 root 1 0 12:00 ? 00:00:02 /entrypoint
|
||||
`),
|
||||
},
|
||||
{
|
||||
name: "extra-hdr",
|
||||
titles: []string{"UID", "GID", "PID", "PPID", "C", "STIME", "TTY", "TIME", "CMD"},
|
||||
procs: [][]string{{"root", "1", "1", "1", "0", "12:00", "?", "00:00:03", "/entrypoint"}},
|
||||
header: topHeader{
|
||||
"SERVICE": 0,
|
||||
"#": 1,
|
||||
"UID": 2,
|
||||
"GID": 3,
|
||||
"PID": 4,
|
||||
"PPID": 5,
|
||||
"C": 6,
|
||||
"STIME": 7,
|
||||
"TTY": 8,
|
||||
"TIME": 9,
|
||||
"CMD": 10,
|
||||
},
|
||||
entries: []topEntries{
|
||||
{
|
||||
"SERVICE": "extra-hdr",
|
||||
"#": "1",
|
||||
"UID": "root",
|
||||
"GID": "1",
|
||||
"PID": "1",
|
||||
"PPID": "1",
|
||||
"C": "0",
|
||||
"STIME": "12:00",
|
||||
"TTY": "?",
|
||||
"TIME": "00:00:03",
|
||||
"CMD": "/entrypoint",
|
||||
},
|
||||
},
|
||||
output: trim(`
|
||||
SERVICE # UID GID PID PPID C STIME TTY TIME CMD
|
||||
extra-hdr 1 root 1 1 1 0 12:00 ? 00:00:03 /entrypoint
|
||||
`),
|
||||
},
|
||||
{
|
||||
name: "multiple",
|
||||
titles: []string{"UID", "PID", "PPID", "C", "STIME", "TTY", "TIME", "CMD"},
|
||||
procs: [][]string{
|
||||
{"root", "1", "1", "0", "12:00", "?", "00:00:04", "/entrypoint"},
|
||||
{"root", "123", "1", "0", "12:00", "?", "00:00:42", "sleep infinity"},
|
||||
},
|
||||
header: topHeader{
|
||||
"SERVICE": 0,
|
||||
"#": 1,
|
||||
"UID": 2,
|
||||
"PID": 3,
|
||||
"PPID": 4,
|
||||
"C": 5,
|
||||
"STIME": 6,
|
||||
"TTY": 7,
|
||||
"TIME": 8,
|
||||
"CMD": 9,
|
||||
},
|
||||
entries: []topEntries{
|
||||
{
|
||||
"SERVICE": "multiple",
|
||||
"#": "1",
|
||||
"UID": "root",
|
||||
"PID": "1",
|
||||
"PPID": "1",
|
||||
"C": "0",
|
||||
"STIME": "12:00",
|
||||
"TTY": "?",
|
||||
"TIME": "00:00:04",
|
||||
"CMD": "/entrypoint",
|
||||
},
|
||||
{
|
||||
"SERVICE": "multiple",
|
||||
"#": "1",
|
||||
"UID": "root",
|
||||
"PID": "123",
|
||||
"PPID": "1",
|
||||
"C": "0",
|
||||
"STIME": "12:00",
|
||||
"TTY": "?",
|
||||
"TIME": "00:00:42",
|
||||
"CMD": "sleep infinity",
|
||||
},
|
||||
},
|
||||
output: trim(`
|
||||
SERVICE # UID PID PPID C STIME TTY TIME CMD
|
||||
multiple 1 root 1 1 0 12:00 ? 00:00:04 /entrypoint
|
||||
multiple 1 root 123 1 0 12:00 ? 00:00:42 sleep infinity
|
||||
`),
|
||||
},
|
||||
}
|
||||
|
||||
// TestRunTopCore only tests the core functionality of runTop: formatting
|
||||
// and printing of the output of (api.Service).Top().
|
||||
func TestRunTopCore(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
all := []api.ContainerProcSummary{}
|
||||
|
||||
for _, tc := range topTestCases {
|
||||
summary := api.ContainerProcSummary{
|
||||
Name: "not used",
|
||||
Titles: tc.titles,
|
||||
Processes: tc.procs,
|
||||
Service: tc.name,
|
||||
Replica: "1",
|
||||
}
|
||||
all = append(all, summary)
|
||||
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
header, entries := collectTop([]api.ContainerProcSummary{summary})
|
||||
assert.Equal(t, tc.header, header)
|
||||
assert.Equal(t, tc.entries, entries)
|
||||
|
||||
var buf bytes.Buffer
|
||||
err := topPrint(&buf, header, entries)
|
||||
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, tc.output, buf.String())
|
||||
})
|
||||
}
|
||||
|
||||
t.Run("all", func(t *testing.T) {
|
||||
header, entries := collectTop(all)
|
||||
assert.Equal(t, topHeader{
|
||||
"SERVICE": 0,
|
||||
"#": 1,
|
||||
"UID": 2,
|
||||
"PID": 3,
|
||||
"PPID": 4,
|
||||
"C": 5,
|
||||
"STIME": 6,
|
||||
"TTY": 7,
|
||||
"TIME": 8,
|
||||
"GID": 9,
|
||||
"CMD": 10,
|
||||
}, header)
|
||||
assert.Equal(t, []topEntries{
|
||||
{
|
||||
"SERVICE": "simple",
|
||||
"#": "1",
|
||||
"UID": "root",
|
||||
"PID": "1",
|
||||
"PPID": "1",
|
||||
"C": "0",
|
||||
"STIME": "12:00",
|
||||
"TTY": "?",
|
||||
"TIME": "00:00:01",
|
||||
"CMD": "/entrypoint",
|
||||
}, {
|
||||
"SERVICE": "noppid",
|
||||
"#": "1",
|
||||
"UID": "root",
|
||||
"PID": "1",
|
||||
"C": "0",
|
||||
"STIME": "12:00",
|
||||
"TTY": "?",
|
||||
"TIME": "00:00:02",
|
||||
"CMD": "/entrypoint",
|
||||
}, {
|
||||
"SERVICE": "extra-hdr",
|
||||
"#": "1",
|
||||
"UID": "root",
|
||||
"GID": "1",
|
||||
"PID": "1",
|
||||
"PPID": "1",
|
||||
"C": "0",
|
||||
"STIME": "12:00",
|
||||
"TTY": "?",
|
||||
"TIME": "00:00:03",
|
||||
"CMD": "/entrypoint",
|
||||
}, {
|
||||
"SERVICE": "multiple",
|
||||
"#": "1",
|
||||
"UID": "root",
|
||||
"PID": "1",
|
||||
"PPID": "1",
|
||||
"C": "0",
|
||||
"STIME": "12:00",
|
||||
"TTY": "?",
|
||||
"TIME": "00:00:04",
|
||||
"CMD": "/entrypoint",
|
||||
}, {
|
||||
"SERVICE": "multiple",
|
||||
"#": "1",
|
||||
"UID": "root",
|
||||
"PID": "123",
|
||||
"PPID": "1",
|
||||
"C": "0",
|
||||
"STIME": "12:00",
|
||||
"TTY": "?",
|
||||
"TIME": "00:00:42",
|
||||
"CMD": "sleep infinity",
|
||||
},
|
||||
}, entries)
|
||||
|
||||
var buf bytes.Buffer
|
||||
err := topPrint(&buf, header, entries)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, trim(`
|
||||
SERVICE # UID PID PPID C STIME TTY TIME GID CMD
|
||||
simple 1 root 1 1 0 12:00 ? 00:00:01 - /entrypoint
|
||||
noppid 1 root 1 - 0 12:00 ? 00:00:02 - /entrypoint
|
||||
extra-hdr 1 root 1 1 0 12:00 ? 00:00:03 1 /entrypoint
|
||||
multiple 1 root 1 1 0 12:00 ? 00:00:04 - /entrypoint
|
||||
multiple 1 root 123 1 0 12:00 ? 00:00:42 - sleep infinity
|
||||
`), buf.String())
|
||||
})
|
||||
}
|
||||
|
||||
func trim(s string) string {
|
||||
var out bytes.Buffer
|
||||
for _, line := range strings.Split(strings.TrimSpace(s), "\n") {
|
||||
out.WriteString(strings.TrimSpace(line))
|
||||
out.WriteRune('\n')
|
||||
}
|
||||
return out.String()
|
||||
}
|
@ -26,10 +26,12 @@ import (
|
||||
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/compose/v2/cmd/formatter"
|
||||
xprogress "github.com/moby/buildkit/util/progress/progressui"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
|
||||
"github.com/docker/compose/v2/cmd/formatter"
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
ui "github.com/docker/compose/v2/pkg/progress"
|
||||
"github.com/docker/compose/v2/pkg/utils"
|
||||
@ -167,10 +169,18 @@ func upCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *c
|
||||
flags.StringArrayVar(&up.noAttach, "no-attach", []string{}, "Do not attach (stream logs) to the specified services")
|
||||
flags.BoolVar(&up.attachDependencies, "attach-dependencies", false, "Automatically attach to log output of dependent services")
|
||||
flags.BoolVar(&up.wait, "wait", false, "Wait for services to be running|healthy. Implies detached mode.")
|
||||
flags.IntVar(&up.waitTimeout, "wait-timeout", 0, "Maximum duration to wait for the project to be running|healthy")
|
||||
flags.IntVar(&up.waitTimeout, "wait-timeout", 0, "Maximum duration in seconds to wait for the project to be running|healthy")
|
||||
flags.BoolVarP(&up.watch, "watch", "w", false, "Watch source code and rebuild/refresh containers when files are updated.")
|
||||
flags.BoolVar(&up.navigationMenu, "menu", false, "Enable interactive shortcuts when running attached. Incompatible with --detach. Can also be enable/disable by setting COMPOSE_MENU environment var.")
|
||||
|
||||
flags.BoolVarP(&create.AssumeYes, "yes", "y", false, `Assume "yes" as answer to all prompts and run non-interactively`)
|
||||
flags.SetNormalizeFunc(func(f *pflag.FlagSet, name string) pflag.NormalizedName {
|
||||
// assumeYes was introduced by mistake as `--y`
|
||||
if name == "y" {
|
||||
logrus.Warn("--y is deprecated, please use --yes instead")
|
||||
name = "yes"
|
||||
}
|
||||
return pflag.NormalizedName(name)
|
||||
})
|
||||
return upCmd
|
||||
}
|
||||
|
||||
@ -192,7 +202,11 @@ func validateFlags(up *upOptions, create *createOptions) error {
|
||||
return fmt.Errorf("--build and --no-build are incompatible")
|
||||
}
|
||||
if up.Detach && (up.attachDependencies || up.cascadeStop || up.cascadeFail || len(up.attach) > 0 || up.watch) {
|
||||
return fmt.Errorf("--detach cannot be combined with --abort-on-container-exit, --abort-on-container-failure, --attach, --attach-dependencies or --watch")
|
||||
if up.wait {
|
||||
return fmt.Errorf("--wait cannot be combined with --abort-on-container-exit, --abort-on-container-failure, --attach, --attach-dependencies or --watch")
|
||||
} else {
|
||||
return fmt.Errorf("--detach cannot be combined with --abort-on-container-exit, --abort-on-container-failure, --attach, --attach-dependencies or --watch")
|
||||
}
|
||||
}
|
||||
if create.noInherit && create.noRecreate {
|
||||
return fmt.Errorf("--no-recreate and --renew-anon-volumes are incompatible")
|
||||
@ -219,6 +233,10 @@ func runUp(
|
||||
project *types.Project,
|
||||
services []string,
|
||||
) error {
|
||||
if err := checksForRemoteStack(ctx, dockerCli, project, buildOptions, createOptions.AssumeYes, []string{}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err := createOptions.Apply(project)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -255,6 +273,7 @@ func runUp(
|
||||
Inherit: !createOptions.noInherit,
|
||||
Timeout: createOptions.GetTimeout(),
|
||||
QuietPull: createOptions.quietPull,
|
||||
AssumeYes: createOptions.AssumeYes,
|
||||
}
|
||||
|
||||
if upOptions.noStart {
|
||||
|
@ -47,5 +47,4 @@ func TestApplyScaleOpt(t *testing.T) {
|
||||
assert.NilError(t, err)
|
||||
assert.Equal(t, *bar.Scale, 3)
|
||||
assert.Equal(t, *bar.Deploy.Replicas, 3)
|
||||
|
||||
}
|
||||
|
76
cmd/compose/version_test.go
Normal file
76
cmd/compose/version_test.go
Normal file
@ -0,0 +1,76 @@
|
||||
/*
|
||||
Copyright 2025 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 (
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/cli/cli/streams"
|
||||
"github.com/docker/compose/v2/internal"
|
||||
"github.com/docker/compose/v2/pkg/mocks"
|
||||
"go.uber.org/mock/gomock"
|
||||
"gotest.tools/v3/assert"
|
||||
)
|
||||
|
||||
func TestVersionCommand(t *testing.T) {
|
||||
originalVersion := internal.Version
|
||||
defer func() {
|
||||
internal.Version = originalVersion
|
||||
}()
|
||||
internal.Version = "v9.9.9-test"
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
args []string
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "default",
|
||||
args: []string{},
|
||||
want: "Docker Compose version v9.9.9-test\n",
|
||||
},
|
||||
{
|
||||
name: "short flag",
|
||||
args: []string{"--short"},
|
||||
want: "9.9.9-test\n",
|
||||
},
|
||||
{
|
||||
name: "json flag",
|
||||
args: []string{"--format", "json"},
|
||||
want: `{"version":"v9.9.9-test"}` + "\n",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
cli := mocks.NewMockCli(ctrl)
|
||||
cli.EXPECT().Out().Return(streams.NewOut(buf)).AnyTimes()
|
||||
|
||||
cmd := versionCommand(cli)
|
||||
cmd.SetArgs(test.args)
|
||||
err := cmd.Execute()
|
||||
assert.NilError(t, err)
|
||||
|
||||
assert.Equal(t, test.want, buf.String())
|
||||
})
|
||||
}
|
||||
}
|
@ -17,10 +17,10 @@
|
||||
package compose
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestPreferredIndentationStr(t *testing.T) {
|
||||
@ -83,10 +83,12 @@ func TestPreferredIndentationStr(t *testing.T) {
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := preferredIndentationStr(tt.args.size, tt.args.useSpace)
|
||||
if tt.wantErr && assert.NotNilf(t, err, fmt.Sprintf("preferredIndentationStr(%v, %v)", tt.args.size, tt.args.useSpace)) {
|
||||
return
|
||||
if tt.wantErr {
|
||||
require.Errorf(t, err, "preferredIndentationStr(%v, %v)", tt.args.size, tt.args.useSpace)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
assert.Equalf(t, tt.want, got, "preferredIndentationStr(%v, %v)", tt.args.size, tt.args.useSpace)
|
||||
}
|
||||
assert.Equalf(t, tt.want, got, "preferredIndentationStr(%v, %v)", tt.args.size, tt.args.useSpace)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -59,13 +59,13 @@ func watchCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service)
|
||||
}
|
||||
|
||||
cmd.Flags().BoolVar(&buildOpts.quiet, "quiet", false, "hide build output")
|
||||
cmd.Flags().BoolVar(&watchOpts.prune, "prune", false, "Prune dangling images on rebuild")
|
||||
cmd.Flags().BoolVar(&watchOpts.prune, "prune", true, "Prune dangling images on rebuild")
|
||||
cmd.Flags().BoolVar(&watchOpts.noUp, "no-up", false, "Do not build & start services before watching")
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runWatch(ctx context.Context, dockerCli command.Cli, backend api.Service, watchOpts watchOptions, buildOpts buildOptions, services []string) error {
|
||||
project, _, err := watchOpts.ToProject(ctx, dockerCli, nil)
|
||||
project, _, err := watchOpts.ToProject(ctx, dockerCli, services)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -27,42 +27,49 @@ var disableAnsi bool
|
||||
func ansi(code string) string {
|
||||
return fmt.Sprintf("\033%s", code)
|
||||
}
|
||||
|
||||
func SaveCursor() {
|
||||
if disableAnsi {
|
||||
return
|
||||
}
|
||||
fmt.Print(ansi("7"))
|
||||
}
|
||||
|
||||
func RestoreCursor() {
|
||||
if disableAnsi {
|
||||
return
|
||||
}
|
||||
fmt.Print(ansi("8"))
|
||||
}
|
||||
|
||||
func HideCursor() {
|
||||
if disableAnsi {
|
||||
return
|
||||
}
|
||||
fmt.Print(ansi("[?25l"))
|
||||
}
|
||||
|
||||
func ShowCursor() {
|
||||
if disableAnsi {
|
||||
return
|
||||
}
|
||||
fmt.Print(ansi("[?25h"))
|
||||
}
|
||||
|
||||
func MoveCursor(y, x int) {
|
||||
if disableAnsi {
|
||||
return
|
||||
}
|
||||
fmt.Print(ansi(fmt.Sprintf("[%d;%dH", y, x)))
|
||||
}
|
||||
|
||||
func MoveCursorX(pos int) {
|
||||
if disableAnsi {
|
||||
return
|
||||
}
|
||||
fmt.Print(ansi(fmt.Sprintf("[%dG", pos)))
|
||||
}
|
||||
|
||||
func ClearLine() {
|
||||
if disableAnsi {
|
||||
return
|
||||
@ -70,6 +77,7 @@ func ClearLine() {
|
||||
// Does not move cursor from its current position
|
||||
fmt.Print(ansi("[2K"))
|
||||
}
|
||||
|
||||
func MoveCursorUp(lines int) {
|
||||
if disableAnsi {
|
||||
return
|
||||
@ -77,6 +85,7 @@ func MoveCursorUp(lines int) {
|
||||
// Does not add new lines
|
||||
fmt.Print(ansi(fmt.Sprintf("[%dA", lines)))
|
||||
}
|
||||
|
||||
func MoveCursorDown(lines int) {
|
||||
if disableAnsi {
|
||||
return
|
||||
@ -84,10 +93,12 @@ func MoveCursorDown(lines int) {
|
||||
// Does not add new lines
|
||||
fmt.Print(ansi(fmt.Sprintf("[%dB", lines)))
|
||||
}
|
||||
|
||||
func NewLine() {
|
||||
// Like \n
|
||||
fmt.Print("\012")
|
||||
}
|
||||
|
||||
func lenAnsi(s string) int {
|
||||
// len has into consideration ansi codes, if we want
|
||||
// the len of the actual len(string) we need to strip
|
||||
|
@ -104,10 +104,12 @@ func makeColorFunc(code string) colorFunc {
|
||||
}
|
||||
}
|
||||
|
||||
var nextColor = rainbowColor
|
||||
var rainbow []colorFunc
|
||||
var currentIndex = 0
|
||||
var mutex sync.Mutex
|
||||
var (
|
||||
nextColor = rainbowColor
|
||||
rainbow []colorFunc
|
||||
currentIndex = 0
|
||||
mutex sync.Mutex
|
||||
)
|
||||
|
||||
func rainbowColor() colorFunc {
|
||||
mutex.Lock()
|
||||
|
@ -24,7 +24,7 @@ import (
|
||||
|
||||
"github.com/docker/cli/cli/command/formatter"
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/pkg/stringid"
|
||||
"github.com/docker/go-units"
|
||||
)
|
||||
@ -212,9 +212,9 @@ func (c *ContainerContext) Publishers() api.PortPublishers {
|
||||
}
|
||||
|
||||
func (c *ContainerContext) Ports() string {
|
||||
var ports []types.Port
|
||||
var ports []container.Port
|
||||
for _, publisher := range c.c.Publishers {
|
||||
ports = append(ports, types.Port{
|
||||
ports = append(ports, container.Port{
|
||||
IP: publisher.URL,
|
||||
PrivatePort: uint16(publisher.TargetPort),
|
||||
PublicPort: uint16(publisher.PublishedPort),
|
||||
|
@ -110,8 +110,10 @@ type LogKeyboard struct {
|
||||
signalChannel chan<- os.Signal
|
||||
}
|
||||
|
||||
var KeyboardManager *LogKeyboard
|
||||
var eg multierror.Group
|
||||
var (
|
||||
KeyboardManager *LogKeyboard
|
||||
eg multierror.Group
|
||||
)
|
||||
|
||||
func NewKeyboardManager(ctx context.Context, isDockerDesktopActive, isWatchConfigured bool,
|
||||
sc chan<- os.Signal,
|
||||
@ -206,7 +208,7 @@ func (lk *LogKeyboard) navigationMenu() string {
|
||||
if openDDInfo != "" || openDDUI != "" {
|
||||
watchInfo = navColor(" ")
|
||||
}
|
||||
var isEnabled = " Enable"
|
||||
isEnabled := " Enable"
|
||||
if lk.Watch.Watching {
|
||||
isEnabled = " Disable"
|
||||
}
|
||||
@ -236,7 +238,7 @@ func (lk *LogKeyboard) openDockerDesktop(ctx context.Context, project *types.Pro
|
||||
link := fmt.Sprintf("docker-desktop://dashboard/apps/%s", project.Name)
|
||||
err := open.Run(link)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Could not open Docker Desktop")
|
||||
err = fmt.Errorf("could not open Docker Desktop")
|
||||
lk.keyboardError("View", err)
|
||||
}
|
||||
return err
|
||||
@ -253,20 +255,21 @@ func (lk *LogKeyboard) openDDComposeUI(ctx context.Context, project *types.Proje
|
||||
link := fmt.Sprintf("docker-desktop://dashboard/docker-compose/%s", project.Name)
|
||||
err := open.Run(link)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Could not open Docker Desktop Compose UI")
|
||||
err = fmt.Errorf("could not open Docker Desktop Compose UI")
|
||||
lk.keyboardError("View Config", err)
|
||||
}
|
||||
return err
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
func (lk *LogKeyboard) openDDWatchDocs(ctx context.Context, project *types.Project) {
|
||||
eg.Go(tracing.EventWrapFuncForErrGroup(ctx, "menu/gui/watch", tracing.SpanOptions{},
|
||||
func(ctx context.Context) error {
|
||||
link := fmt.Sprintf("docker-desktop://dashboard/docker-compose/%s/watch", project.Name)
|
||||
err := open.Run(link)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Could not open Docker Desktop Compose UI")
|
||||
err = fmt.Errorf("could not open Docker Desktop Compose UI")
|
||||
lk.keyboardError("Watch Docs", err)
|
||||
}
|
||||
return err
|
||||
@ -296,7 +299,7 @@ func (lk *LogKeyboard) StartWatch(ctx context.Context, doneCh chan bool, project
|
||||
eg.Go(tracing.EventWrapFuncForErrGroup(ctx, "menu/watch", tracing.SpanOptions{},
|
||||
func(ctx context.Context) error {
|
||||
if options.Create.Build == nil {
|
||||
err := fmt.Errorf("Cannot run watch mode with flag --no-build")
|
||||
err := fmt.Errorf("cannot run watch mode with flag --no-build")
|
||||
lk.keyboardError("Watch", err)
|
||||
return err
|
||||
}
|
||||
@ -304,10 +307,15 @@ func (lk *LogKeyboard) StartWatch(ctx context.Context, doneCh chan bool, project
|
||||
lk.Watch.newContext(ctx)
|
||||
buildOpts := *options.Create.Build
|
||||
buildOpts.Quiet = true
|
||||
return lk.Watch.WatchFn(lk.Watch.Ctx, doneCh, project, options.Start.Services, api.WatchOptions{
|
||||
err := lk.Watch.WatchFn(lk.Watch.Ctx, doneCh, project, options.Start.Services, api.WatchOptions{
|
||||
Build: &buildOpts,
|
||||
LogTo: options.Start.Attach,
|
||||
})
|
||||
if err != nil {
|
||||
lk.Watch.switchWatching()
|
||||
options.Start.Attach.Err(api.WatchLogger, err.Error())
|
||||
}
|
||||
return err
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
@ -62,7 +62,7 @@ func pluginMain() {
|
||||
|
||||
cmd.SetFlagErrorFunc(func(c *cobra.Command, err error) error {
|
||||
return dockercli.StatusError{
|
||||
StatusCode: compose.CommandSyntaxFailure.ExitCode,
|
||||
StatusCode: 1,
|
||||
Status: err.Error(),
|
||||
}
|
||||
})
|
||||
|
@ -13,6 +13,7 @@ Define and run multi-container applications with Docker
|
||||
|:--------------------------------|:----------------------------------------------------------------------------------------|
|
||||
| [`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 |
|
||||
| [`commit`](compose_commit.md) | Create a new image from a service container's changes |
|
||||
| [`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 |
|
||||
@ -27,6 +28,7 @@ Define and run multi-container applications with Docker
|
||||
| [`pause`](compose_pause.md) | Pause services |
|
||||
| [`port`](compose_port.md) | Print the public port for a port binding |
|
||||
| [`ps`](compose_ps.md) | List containers |
|
||||
| [`publish`](compose_publish.md) | Publish compose application |
|
||||
| [`pull`](compose_pull.md) | Pull service images |
|
||||
| [`push`](compose_push.md) | Push service images |
|
||||
| [`restart`](compose_restart.md) | Restart service containers |
|
||||
@ -66,7 +68,7 @@ Define and run multi-container applications with Docker
|
||||
## Examples
|
||||
|
||||
### Use `-f` to specify the name and path of one or more Compose files
|
||||
Use the `-f` flag to specify the location of a Compose configuration file.
|
||||
Use the `-f` flag to specify the location of a Compose [configuration file](/reference/compose-file/).
|
||||
|
||||
#### Specifying multiple Compose files
|
||||
You can supply multiple `-f` configuration files. When you supply multiple files, Compose combines them into a single
|
||||
@ -76,10 +78,10 @@ to their predecessors.
|
||||
For example, consider this command line:
|
||||
|
||||
```console
|
||||
$ docker compose -f docker-compose.yml -f docker-compose.admin.yml run backup_db
|
||||
$ docker compose -f compose.yaml -f compose.admin.yaml run backup_db
|
||||
```
|
||||
|
||||
The `docker-compose.yml` file might specify a `webapp` service.
|
||||
The `compose.yaml` file might specify a `webapp` service.
|
||||
|
||||
```yaml
|
||||
services:
|
||||
@ -90,7 +92,7 @@ services:
|
||||
volumes:
|
||||
- "/data"
|
||||
```
|
||||
If the `docker-compose.admin.yml` also specifies this same service, any matching fields override the previous file.
|
||||
If the `compose.admin.yaml` also specifies this same service, any matching fields override the previous file.
|
||||
New values, add to the `webapp` service configuration.
|
||||
|
||||
```yaml
|
||||
@ -205,4 +207,4 @@ $ docker compose --dry-run up --build -d
|
||||
From the example above, you can see that the first step is to pull the image defined by `db` service, then build the `backend` service.
|
||||
Next, the containers are created. The `db` service is started, and the `backend` and `proxy` wait until the `db` service is healthy before starting.
|
||||
|
||||
Dry Run mode works with almost all commands. You cannot use Dry Run mode with a command that doesn't change the state of a Compose stack such as `ps`, `ls`, `logs` for example.
|
||||
Dry Run mode works with almost all commands. You cannot use Dry Run mode with a command that doesn't change the state of a Compose stack such as `ps`, `ls`, `logs` for example.
|
||||
|
@ -8,8 +8,10 @@ Publish compose application
|
||||
| Name | Type | Default | Description |
|
||||
|:--------------------------|:---------|:--------|:-------------------------------------------------------------------------------|
|
||||
| `--dry-run` | `bool` | | Execute command in dry run mode |
|
||||
| `--oci-version` | `string` | | OCI Image/Artifact specification version (automatically determined by default) |
|
||||
| `--oci-version` | `string` | | OCI image/artifact specification version (automatically determined by default) |
|
||||
| `--resolve-image-digests` | `bool` | | Pin image tags to digests |
|
||||
| `--with-env` | `bool` | | Include environment variables in the published OCI artifact |
|
||||
| `-y`, `--yes` | `bool` | | Assume "yes" as answer to all prompts |
|
||||
|
||||
|
||||
<!---MARKER_GEN_END-->
|
||||
|
@ -4,9 +4,9 @@
|
||||
Services are built once and then tagged, by default as `project-service`.
|
||||
|
||||
If the Compose file specifies an
|
||||
[image](https://github.com/compose-spec/compose-spec/blob/master/spec.md#image) name,
|
||||
[image](https://github.com/compose-spec/compose-spec/blob/main/spec.md#image) name,
|
||||
the image is tagged with that name, substituting any variables beforehand. See
|
||||
[variable interpolation](https://github.com/compose-spec/compose-spec/blob/master/spec.md#interpolation).
|
||||
[variable interpolation](https://github.com/compose-spec/compose-spec/blob/main/spec.md#interpolation).
|
||||
|
||||
If you change a service's `Dockerfile` or the contents of its build directory,
|
||||
run `docker compose build` to rebuild it.
|
||||
@ -20,6 +20,7 @@ run `docker compose build` to rebuild it.
|
||||
| `--dry-run` | `bool` | | Execute command in dry run mode |
|
||||
| `-m`, `--memory` | `bytes` | `0` | Set memory limit for the build container. Not supported by BuildKit. |
|
||||
| `--no-cache` | `bool` | | Do not use cache when building the image |
|
||||
| `--print` | `bool` | | Print equivalent bake file |
|
||||
| `--pull` | `bool` | | Always attempt to pull a newer version of the image |
|
||||
| `--push` | `bool` | | Push service images |
|
||||
| `-q`, `--quiet` | `bool` | | Don't print anything to STDOUT |
|
||||
@ -34,9 +35,9 @@ run `docker compose build` to rebuild it.
|
||||
Services are built once and then tagged, by default as `project-service`.
|
||||
|
||||
If the Compose file specifies an
|
||||
[image](https://github.com/compose-spec/compose-spec/blob/master/spec.md#image) name,
|
||||
[image](https://github.com/compose-spec/compose-spec/blob/main/spec.md#image) name,
|
||||
the image is tagged with that name, substituting any variables beforehand. See
|
||||
[variable interpolation](https://github.com/compose-spec/compose-spec/blob/master/spec.md#interpolation).
|
||||
[variable interpolation](https://github.com/compose-spec/compose-spec/blob/main/spec.md#interpolation).
|
||||
|
||||
If you change a service's `Dockerfile` or the contents of its build directory,
|
||||
run `docker compose build` to rebuild it.
|
||||
|
19
docs/reference/compose_commit.md
Normal file
19
docs/reference/compose_commit.md
Normal file
@ -0,0 +1,19 @@
|
||||
# docker compose commit
|
||||
|
||||
<!---MARKER_GEN_START-->
|
||||
Create a new image from a service container's changes
|
||||
|
||||
### Options
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
|:------------------|:---------|:--------|:-----------------------------------------------------------|
|
||||
| `-a`, `--author` | `string` | | Author (e.g., "John Hannibal Smith <hannibal@a-team.com>") |
|
||||
| `-c`, `--change` | `list` | | Apply Dockerfile instruction to the created image |
|
||||
| `--dry-run` | `bool` | | Execute command in dry run mode |
|
||||
| `--index` | `int` | `0` | index of the container if service has multiple replicas. |
|
||||
| `-m`, `--message` | `string` | | Commit message |
|
||||
| `-p`, `--pause` | `bool` | `true` | Pause container during commit |
|
||||
|
||||
|
||||
<!---MARKER_GEN_END-->
|
||||
|
@ -19,6 +19,7 @@ the canonical format.
|
||||
| `--hash` | `string` | | Print the service config hash, one per line. |
|
||||
| `--images` | `bool` | | Print the image names, one per line. |
|
||||
| `--no-consistency` | `bool` | | Don't check model consistency - warning: may produce invalid Compose output |
|
||||
| `--no-env-resolution` | `bool` | | Don't resolve service env files |
|
||||
| `--no-interpolate` | `bool` | | Don't interpolate environment variables |
|
||||
| `--no-normalize` | `bool` | | Don't normalize compose model |
|
||||
| `--no-path-resolution` | `bool` | | Don't resolve file paths |
|
||||
|
@ -16,6 +16,7 @@ Creates containers for a service
|
||||
| `--quiet-pull` | `bool` | | Pull without printing progress information |
|
||||
| `--remove-orphans` | `bool` | | 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. |
|
||||
| `-y`, `--yes` | `bool` | | Assume "yes" as answer to all prompts and run non-interactively |
|
||||
|
||||
|
||||
<!---MARKER_GEN_END-->
|
||||
|
@ -11,7 +11,7 @@ Lists running Compose projects
|
||||
| `--dry-run` | `bool` | | Execute command in dry run mode |
|
||||
| `--filter` | `filter` | | Filter output based on conditions provided |
|
||||
| `--format` | `string` | `table` | Format the output. Values: [table \| json] |
|
||||
| `-q`, `--quiet` | `bool` | | Only display IDs |
|
||||
| `-q`, `--quiet` | `bool` | | Only display project names |
|
||||
|
||||
|
||||
<!---MARKER_GEN_END-->
|
||||
|
18
docs/reference/compose_publish.md
Normal file
18
docs/reference/compose_publish.md
Normal file
@ -0,0 +1,18 @@
|
||||
# docker compose publish
|
||||
|
||||
<!---MARKER_GEN_START-->
|
||||
Publish compose application
|
||||
|
||||
### Options
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
|:--------------------------|:---------|:--------|:-------------------------------------------------------------------------------|
|
||||
| `--dry-run` | `bool` | | Execute command in dry run mode |
|
||||
| `--oci-version` | `string` | | OCI image/artifact specification version (automatically determined by default) |
|
||||
| `--resolve-image-digests` | `bool` | | Pin image tags to digests |
|
||||
| `--with-env` | `bool` | | Include environment variables in the published OCI artifact |
|
||||
| `-y`, `--yes` | `bool` | | Assume "yes" as answer to all prompts |
|
||||
|
||||
|
||||
<!---MARKER_GEN_END-->
|
||||
|
@ -9,8 +9,8 @@ after a container is built, but before the container's command is executed) are
|
||||
after restarting.
|
||||
|
||||
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).
|
||||
[restart](https://github.com/compose-spec/compose-spec/blob/main/spec.md#restart)
|
||||
or [restart_policy](https://github.com/compose-spec/compose-spec/blob/main/deploy.md#restart_policy).
|
||||
|
||||
### Options
|
||||
|
||||
@ -33,5 +33,5 @@ after a container is built, but before the container's command is executed) are
|
||||
after restarting.
|
||||
|
||||
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).
|
||||
[restart](https://github.com/compose-spec/compose-spec/blob/main/spec.md#restart)
|
||||
or [restart_policy](https://github.com/compose-spec/compose-spec/blob/main/deploy.md#restart_policy).
|
||||
|
@ -57,29 +57,33 @@ specified in the service configuration.
|
||||
|
||||
### Options
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
|:------------------------|:--------------|:--------|:---------------------------------------------------------------------------------|
|
||||
| `--build` | `bool` | | Build image before starting container |
|
||||
| `--cap-add` | `list` | | Add Linux capabilities |
|
||||
| `--cap-drop` | `list` | | Drop Linux capabilities |
|
||||
| `-d`, `--detach` | `bool` | | Run container in background and print container ID |
|
||||
| `--dry-run` | `bool` | | Execute command in dry run mode |
|
||||
| `--entrypoint` | `string` | | Override the entrypoint of the image |
|
||||
| `-e`, `--env` | `stringArray` | | Set environment variables |
|
||||
| `-i`, `--interactive` | `bool` | `true` | 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` | `bool` | `true` | Disable pseudo-TTY allocation (default: auto-detected) |
|
||||
| `--no-deps` | `bool` | | Don't start linked services |
|
||||
| `-p`, `--publish` | `stringArray` | | Publish a container's port(s) to the host |
|
||||
| `--quiet-pull` | `bool` | | Pull without printing progress information |
|
||||
| `--remove-orphans` | `bool` | | Remove containers for services not defined in the Compose file |
|
||||
| `--rm` | `bool` | | Automatically remove the container when it exits |
|
||||
| `-P`, `--service-ports` | `bool` | | Run command with all service's ports enabled and mapped to the host |
|
||||
| `--use-aliases` | `bool` | | 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` | `bool` | | Build image before starting container |
|
||||
| `--cap-add` | `list` | | Add Linux capabilities |
|
||||
| `--cap-drop` | `list` | | Drop Linux capabilities |
|
||||
| `-d`, `--detach` | `bool` | | Run container in background and print container ID |
|
||||
| `--dry-run` | `bool` | | Execute command in dry run mode |
|
||||
| `--entrypoint` | `string` | | Override the entrypoint of the image |
|
||||
| `-e`, `--env` | `stringArray` | | Set environment variables |
|
||||
| `--env-from-file` | `stringArray` | | Set environment variables from file |
|
||||
| `-i`, `--interactive` | `bool` | `true` | 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` | `bool` | `true` | Disable pseudo-TTY allocation (default: auto-detected) |
|
||||
| `--no-deps` | `bool` | | Don't start linked services |
|
||||
| `-p`, `--publish` | `stringArray` | | Publish a container's port(s) to the host |
|
||||
| `--pull` | `string` | `policy` | Pull image before running ("always"\|"missing"\|"never") |
|
||||
| `-q`, `--quiet` | `bool` | | Don't print anything to STDOUT |
|
||||
| `--quiet-build` | `bool` | | Suppress progress output from the build process |
|
||||
| `--quiet-pull` | `bool` | | Pull without printing progress information |
|
||||
| `--remove-orphans` | `bool` | | Remove containers for services not defined in the Compose file |
|
||||
| `--rm` | `bool` | | Automatically remove the container when it exits |
|
||||
| `-P`, `--service-ports` | `bool` | | Run command with all service's ports enabled and mapped to the host |
|
||||
| `--use-aliases` | `bool` | | 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-->
|
||||
|
@ -5,13 +5,13 @@ Display a live stream of container(s) resource usage statistics
|
||||
|
||||
### Options
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
|:--------------|:---------|:--------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `-a`, `--all` | `bool` | | Show all containers (default shows just running) |
|
||||
| `--dry-run` | `bool` | | 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` | `bool` | | Disable streaming stats and only pull the first result |
|
||||
| `--no-trunc` | `bool` | | Do not truncate output |
|
||||
| Name | Type | Default | Description |
|
||||
|:--------------|:---------|:--------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `-a`, `--all` | `bool` | | Show all containers (default shows just running) |
|
||||
| `--dry-run` | `bool` | | 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/engine/cli/formatting/ for more information about formatting output with templates |
|
||||
| `--no-stream` | `bool` | | Disable streaming stats and only pull the first result |
|
||||
| `--no-trunc` | `bool` | | Do not truncate output |
|
||||
|
||||
|
||||
<!---MARKER_GEN_END-->
|
||||
|
@ -51,8 +51,9 @@ If the process is interrupted using `SIGINT` (ctrl + C) or `SIGTERM`, the contai
|
||||
| `-t`, `--timeout` | `int` | `0` | Use this timeout in seconds for container shutdown when attached or when containers are already running |
|
||||
| `--timestamps` | `bool` | | Show timestamps |
|
||||
| `--wait` | `bool` | | Wait for services to be running\|healthy. Implies detached mode. |
|
||||
| `--wait-timeout` | `int` | `0` | Maximum duration to wait for the project to be running\|healthy |
|
||||
| `--wait-timeout` | `int` | `0` | Maximum duration in seconds to wait for the project to be running\|healthy |
|
||||
| `-w`, `--watch` | `bool` | | Watch source code and rebuild/refresh containers when files are updated. |
|
||||
| `-y`, `--yes` | `bool` | | Assume "yes" as answer to all prompts and run non-interactively |
|
||||
|
||||
|
||||
<!---MARKER_GEN_END-->
|
||||
|
@ -9,7 +9,7 @@ Watch build context for service and rebuild/refresh containers when files are up
|
||||
|:------------|:-------|:--------|:----------------------------------------------|
|
||||
| `--dry-run` | `bool` | | Execute command in dry run mode |
|
||||
| `--no-up` | `bool` | | Do not build & start services before watching |
|
||||
| `--prune` | `bool` | | Prune dangling images on rebuild |
|
||||
| `--prune` | `bool` | `true` | Prune dangling images on rebuild |
|
||||
| `--quiet` | `bool` | | hide build output |
|
||||
|
||||
|
||||
|
@ -7,6 +7,7 @@ plink: docker.yaml
|
||||
cname:
|
||||
- docker compose attach
|
||||
- docker compose build
|
||||
- docker compose commit
|
||||
- docker compose config
|
||||
- docker compose cp
|
||||
- docker compose create
|
||||
@ -21,6 +22,7 @@ cname:
|
||||
- docker compose pause
|
||||
- docker compose port
|
||||
- docker compose ps
|
||||
- docker compose publish
|
||||
- docker compose pull
|
||||
- docker compose push
|
||||
- docker compose restart
|
||||
@ -39,6 +41,7 @@ cname:
|
||||
clink:
|
||||
- docker_compose_attach.yaml
|
||||
- docker_compose_build.yaml
|
||||
- docker_compose_commit.yaml
|
||||
- docker_compose_config.yaml
|
||||
- docker_compose_cp.yaml
|
||||
- docker_compose_create.yaml
|
||||
@ -53,6 +56,7 @@ clink:
|
||||
- docker_compose_pause.yaml
|
||||
- docker_compose_port.yaml
|
||||
- docker_compose_ps.yaml
|
||||
- docker_compose_publish.yaml
|
||||
- docker_compose_pull.yaml
|
||||
- docker_compose_push.yaml
|
||||
- docker_compose_restart.yaml
|
||||
@ -227,7 +231,7 @@ options:
|
||||
swarm: false
|
||||
examples: |-
|
||||
### Use `-f` to specify the name and path of one or more Compose files
|
||||
Use the `-f` flag to specify the location of a Compose configuration file.
|
||||
Use the `-f` flag to specify the location of a Compose [configuration file](/reference/compose-file/).
|
||||
|
||||
#### Specifying multiple Compose files
|
||||
You can supply multiple `-f` configuration files. When you supply multiple files, Compose combines them into a single
|
||||
@ -237,10 +241,10 @@ examples: |-
|
||||
For example, consider this command line:
|
||||
|
||||
```console
|
||||
$ docker compose -f docker-compose.yml -f docker-compose.admin.yml run backup_db
|
||||
$ docker compose -f compose.yaml -f compose.admin.yaml run backup_db
|
||||
```
|
||||
|
||||
The `docker-compose.yml` file might specify a `webapp` service.
|
||||
The `compose.yaml` file might specify a `webapp` service.
|
||||
|
||||
```yaml
|
||||
services:
|
||||
@ -251,7 +255,7 @@ examples: |-
|
||||
volumes:
|
||||
- "/data"
|
||||
```
|
||||
If the `docker-compose.admin.yml` also specifies this same service, any matching fields override the previous file.
|
||||
If the `compose.admin.yaml` also specifies this same service, any matching fields override the previous file.
|
||||
New values, add to the `webapp` service configuration.
|
||||
|
||||
```yaml
|
||||
|
@ -8,7 +8,7 @@ options:
|
||||
- option: oci-version
|
||||
value_type: string
|
||||
description: |
|
||||
OCI Image/Artifact specification version (automatically determined by default)
|
||||
OCI image/artifact specification version (automatically determined by default)
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
@ -25,6 +25,27 @@ options:
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
- option: with-env
|
||||
value_type: bool
|
||||
default_value: "false"
|
||||
description: Include environment variables in the published OCI artifact
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
- option: "yes"
|
||||
shorthand: "y"
|
||||
value_type: bool
|
||||
default_value: "false"
|
||||
description: Assume "yes" as answer to all prompts
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
inherited_options:
|
||||
- option: dry-run
|
||||
value_type: bool
|
||||
|
@ -4,9 +4,9 @@ long: |-
|
||||
Services are built once and then tagged, by default as `project-service`.
|
||||
|
||||
If the Compose file specifies an
|
||||
[image](https://github.com/compose-spec/compose-spec/blob/master/spec.md#image) name,
|
||||
[image](https://github.com/compose-spec/compose-spec/blob/main/spec.md#image) name,
|
||||
the image is tagged with that name, substituting any variables beforehand. See
|
||||
[variable interpolation](https://github.com/compose-spec/compose-spec/blob/master/spec.md#interpolation).
|
||||
[variable interpolation](https://github.com/compose-spec/compose-spec/blob/main/spec.md#interpolation).
|
||||
|
||||
If you change a service's `Dockerfile` or the contents of its build directory,
|
||||
run `docker compose build` to rebuild it.
|
||||
@ -96,6 +96,16 @@ options:
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
- option: print
|
||||
value_type: bool
|
||||
default_value: "false"
|
||||
description: Print equivalent bake file
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
- option: progress
|
||||
value_type: string
|
||||
default_value: auto
|
||||
|
76
docs/reference/docker_compose_commit.yaml
Normal file
76
docs/reference/docker_compose_commit.yaml
Normal file
@ -0,0 +1,76 @@
|
||||
command: docker compose commit
|
||||
short: Create a new image from a service container's changes
|
||||
long: Create a new image from a service container's changes
|
||||
usage: docker compose commit [OPTIONS] SERVICE [REPOSITORY[:TAG]]
|
||||
pname: docker compose
|
||||
plink: docker_compose.yaml
|
||||
options:
|
||||
- option: author
|
||||
shorthand: a
|
||||
value_type: string
|
||||
description: Author (e.g., "John Hannibal Smith <hannibal@a-team.com>")
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
- option: change
|
||||
shorthand: c
|
||||
value_type: list
|
||||
description: Apply Dockerfile instruction to the created image
|
||||
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: message
|
||||
shorthand: m
|
||||
value_type: string
|
||||
description: Commit message
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
- option: pause
|
||||
shorthand: p
|
||||
value_type: bool
|
||||
default_value: "true"
|
||||
description: Pause container during commit
|
||||
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
|
||||
|
@ -59,6 +59,16 @@ options:
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
- option: no-env-resolution
|
||||
value_type: bool
|
||||
default_value: "false"
|
||||
description: Don't resolve service env files
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
- option: no-interpolate
|
||||
value_type: bool
|
||||
default_value: "false"
|
||||
|
@ -88,6 +88,17 @@ options:
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
- option: "yes"
|
||||
shorthand: "y"
|
||||
value_type: bool
|
||||
default_value: "false"
|
||||
description: Assume "yes" as answer to all prompts and run non-interactively
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
inherited_options:
|
||||
- option: dry-run
|
||||
value_type: bool
|
||||
|
@ -39,7 +39,7 @@ options:
|
||||
shorthand: q
|
||||
value_type: bool
|
||||
default_value: "false"
|
||||
description: Only display IDs
|
||||
description: Only display project names
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
|
66
docs/reference/docker_compose_publish.yaml
Normal file
66
docs/reference/docker_compose_publish.yaml
Normal file
@ -0,0 +1,66 @@
|
||||
command: docker compose publish
|
||||
short: Publish compose application
|
||||
long: Publish compose application
|
||||
usage: docker compose publish [OPTIONS] REPOSITORY[:TAG]
|
||||
pname: docker compose
|
||||
plink: docker_compose.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
|
||||
- option: with-env
|
||||
value_type: bool
|
||||
default_value: "false"
|
||||
description: Include environment variables in the published OCI artifact
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
- option: "yes"
|
||||
shorthand: "y"
|
||||
value_type: bool
|
||||
default_value: "false"
|
||||
description: Assume "yes" as answer to all prompts
|
||||
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
|
||||
|
@ -9,8 +9,8 @@ long: |-
|
||||
after restarting.
|
||||
|
||||
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).
|
||||
[restart](https://github.com/compose-spec/compose-spec/blob/main/spec.md#restart)
|
||||
or [restart_policy](https://github.com/compose-spec/compose-spec/blob/main/deploy.md#restart_policy).
|
||||
usage: docker compose restart [OPTIONS] [SERVICE...]
|
||||
pname: docker compose
|
||||
plink: docker_compose.yaml
|
||||
|
@ -117,6 +117,16 @@ options:
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
- option: env-from-file
|
||||
value_type: stringArray
|
||||
default_value: '[]'
|
||||
description: Set environment variables from file
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
- option: interactive
|
||||
shorthand: i
|
||||
value_type: bool
|
||||
@ -180,6 +190,37 @@ options:
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
- option: pull
|
||||
value_type: string
|
||||
default_value: policy
|
||||
description: Pull image before running ("always"|"missing"|"never")
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
- option: quiet
|
||||
shorthand: q
|
||||
value_type: bool
|
||||
default_value: "false"
|
||||
description: Don't print anything to STDOUT
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
- option: quiet-build
|
||||
value_type: bool
|
||||
default_value: "false"
|
||||
description: Suppress progress output from the build process
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
- option: quiet-pull
|
||||
value_type: bool
|
||||
default_value: "false"
|
||||
|
@ -24,7 +24,7 @@ options:
|
||||
'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
|
||||
Refer to https://docs.docker.com/engine/cli/formatting/ for more information about formatting output with templates
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
|
@ -289,7 +289,8 @@ options:
|
||||
- option: wait-timeout
|
||||
value_type: int
|
||||
default_value: "0"
|
||||
description: Maximum duration to wait for the project to be running|healthy
|
||||
description: |
|
||||
Maximum duration in seconds to wait for the project to be running|healthy
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
@ -308,6 +309,17 @@ options:
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
- option: "yes"
|
||||
shorthand: "y"
|
||||
value_type: bool
|
||||
default_value: "false"
|
||||
description: Assume "yes" as answer to all prompts and run non-interactively
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
inherited_options:
|
||||
- option: dry-run
|
||||
value_type: bool
|
||||
|
@ -19,7 +19,7 @@ options:
|
||||
swarm: false
|
||||
- option: prune
|
||||
value_type: bool
|
||||
default_value: "false"
|
||||
default_value: "true"
|
||||
description: Prune dangling images on rebuild
|
||||
deprecated: false
|
||||
hidden: false
|
||||
|
193
go.mod
193
go.mod
@ -1,94 +1,97 @@
|
||||
module github.com/docker/compose/v2
|
||||
|
||||
go 1.22.0
|
||||
go 1.23.8
|
||||
|
||||
require (
|
||||
github.com/AlecAivazis/survey/v2 v2.3.7
|
||||
github.com/DefangLabs/secret-detector v0.0.0-20250403165618-22662109213e
|
||||
github.com/Microsoft/go-winio v0.6.2
|
||||
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d
|
||||
github.com/buger/goterm v1.0.4
|
||||
github.com/compose-spec/compose-go/v2 v2.4.4
|
||||
github.com/containerd/containerd v1.7.23
|
||||
github.com/containerd/platforms v0.2.1
|
||||
github.com/davecgh/go-spew v1.1.1
|
||||
github.com/compose-spec/compose-go/v2 v2.5.0
|
||||
github.com/containerd/containerd/v2 v2.0.4
|
||||
github.com/containerd/platforms v1.0.0-rc.1
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc
|
||||
github.com/distribution/reference v0.6.0
|
||||
github.com/docker/buildx v0.17.1
|
||||
github.com/docker/cli v27.3.2-0.20241008150905-cb3048fbebb1+incompatible
|
||||
github.com/docker/cli-docs-tool v0.8.0
|
||||
github.com/docker/docker v27.3.1+incompatible
|
||||
github.com/docker/buildx v0.22.0
|
||||
github.com/docker/cli v28.0.4+incompatible
|
||||
github.com/docker/cli-docs-tool v0.9.0
|
||||
github.com/docker/docker v28.0.4+incompatible
|
||||
github.com/docker/go-connections v0.5.0
|
||||
github.com/docker/go-units v0.5.0
|
||||
github.com/eiannone/keyboard v0.0.0-20220611211555-0d226195f203
|
||||
github.com/fsnotify/fsevents v0.2.0
|
||||
github.com/google/go-cmp v0.6.0
|
||||
github.com/google/go-cmp v0.7.0
|
||||
github.com/hashicorp/go-multierror v1.1.1
|
||||
github.com/hashicorp/go-version v1.7.0
|
||||
github.com/jonboulle/clockwork v0.4.0
|
||||
github.com/jonboulle/clockwork v0.5.0
|
||||
github.com/mattn/go-shellwords v1.0.12
|
||||
github.com/mitchellh/go-ps v1.0.0
|
||||
github.com/mitchellh/mapstructure v1.5.0
|
||||
github.com/moby/buildkit v0.16.0
|
||||
github.com/moby/buildkit v0.20.1
|
||||
github.com/moby/patternmatcher v0.6.0
|
||||
github.com/moby/term v0.5.0
|
||||
github.com/moby/term v0.5.2
|
||||
github.com/morikuni/aec v1.0.0
|
||||
github.com/opencontainers/go-digest v1.0.0
|
||||
github.com/opencontainers/image-spec v1.1.0
|
||||
github.com/otiai10/copy v1.14.0
|
||||
github.com/opencontainers/image-spec v1.1.1
|
||||
github.com/otiai10/copy v1.14.1
|
||||
github.com/r3labs/sse v0.0.0-20210224172625-26fe804710bc
|
||||
github.com/sirupsen/logrus v1.9.3
|
||||
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966
|
||||
github.com/spf13/cobra v1.8.1
|
||||
github.com/spf13/pflag v1.0.5
|
||||
github.com/stretchr/testify v1.9.0
|
||||
github.com/spf13/cobra v1.9.1
|
||||
github.com/spf13/pflag v1.0.6
|
||||
github.com/stretchr/testify v1.10.0
|
||||
github.com/theupdateframework/notary v0.7.0
|
||||
github.com/tilt-dev/fsnotify v1.4.8-0.20220602155310-fff9c274a375
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1
|
||||
go.opentelemetry.io/otel v1.21.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.21.0
|
||||
go.opentelemetry.io/otel/metric v1.21.0
|
||||
go.opentelemetry.io/otel/sdk v1.21.0
|
||||
go.opentelemetry.io/otel/trace v1.21.0
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0
|
||||
go.opentelemetry.io/otel v1.34.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.31.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.31.0
|
||||
go.opentelemetry.io/otel/metric v1.34.0
|
||||
go.opentelemetry.io/otel/sdk v1.34.0
|
||||
go.opentelemetry.io/otel/trace v1.34.0
|
||||
go.uber.org/goleak v1.3.0
|
||||
go.uber.org/mock v0.5.0
|
||||
golang.org/x/exp v0.0.0-20240823005443-9b4947da3948
|
||||
golang.org/x/sync v0.8.0
|
||||
golang.org/x/sys v0.26.0
|
||||
google.golang.org/grpc v1.67.1
|
||||
golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f
|
||||
golang.org/x/sync v0.13.0
|
||||
golang.org/x/sys v0.32.0
|
||||
google.golang.org/grpc v1.71.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
gotest.tools/v3 v3.5.1
|
||||
tags.cncf.io/container-device-interface v0.8.0
|
||||
gotest.tools/v3 v3.5.2
|
||||
tags.cncf.io/container-device-interface v1.0.1
|
||||
)
|
||||
|
||||
require (
|
||||
dario.cat/mergo v1.0.1 // 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/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 // indirect
|
||||
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect
|
||||
github.com/Masterminds/semver/v3 v3.2.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2 v1.24.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/config v1.26.6 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.16.16 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.11 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.10 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.10 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.7.3 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.10 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.18.7 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.7 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.26.7 // indirect
|
||||
github.com/aws/smithy-go v1.19.0 // indirect
|
||||
github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2 v1.30.3 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/config v1.27.27 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.27 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.11 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.15 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.15 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.17 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.22.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.30.3 // indirect
|
||||
github.com/aws/smithy-go v1.20.3 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.2.1 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/containerd/console v1.0.4 // indirect
|
||||
github.com/containerd/containerd/api v1.7.19 // indirect
|
||||
github.com/containerd/continuity v0.4.3 // indirect
|
||||
github.com/containerd/errdefs v0.3.0 // indirect
|
||||
github.com/containerd/containerd/api v1.8.0 // indirect
|
||||
github.com/containerd/continuity v0.4.5 // indirect
|
||||
github.com/containerd/errdefs v1.0.0 // indirect
|
||||
github.com/containerd/errdefs/pkg v0.3.0 // indirect
|
||||
github.com/containerd/log v0.1.0 // indirect
|
||||
github.com/containerd/ttrpc v1.2.5 // indirect
|
||||
github.com/containerd/typeurl/v2 v2.2.0 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect
|
||||
github.com/containerd/ttrpc v1.2.7 // indirect
|
||||
github.com/containerd/typeurl/v2 v2.2.3 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6 // indirect
|
||||
github.com/docker/distribution v2.8.3+incompatible // indirect
|
||||
github.com/docker/docker-credential-helpers v0.8.2 // indirect
|
||||
github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c // indirect
|
||||
@ -96,15 +99,16 @@ require (
|
||||
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/fvbommel/sortorder v1.1.0 // indirect
|
||||
github.com/go-logr/logr v1.4.1 // indirect
|
||||
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
|
||||
github.com/go-logr/logr v1.4.2 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.19.6 // indirect
|
||||
github.com/go-openapi/jsonreference v0.20.2 // indirect
|
||||
github.com/go-openapi/swag v0.22.3 // indirect
|
||||
github.com/go-openapi/swag v0.22.4 // indirect
|
||||
github.com/go-viper/mapstructure/v2 v2.0.0 // indirect
|
||||
github.com/gofrs/flock v0.12.1 // indirect
|
||||
github.com/gogo/googleapis v1.4.1 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang-jwt/jwt/v5 v5.2.2 // indirect
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
github.com/google/gnostic-models v0.6.8 // indirect
|
||||
github.com/google/gofuzz v1.2.0 // indirect
|
||||
@ -112,27 +116,29 @@ require (
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/gorilla/mux v1.8.1 // indirect
|
||||
github.com/gorilla/websocket v1.5.0 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 // indirect
|
||||
github.com/hashicorp/errwrap v1.1.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/inhies/go-bytesize v0.0.0-20220417184213-4913239db9cf // 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.17.9 // indirect
|
||||
github.com/klauspost/compress v1.17.11 // indirect
|
||||
github.com/magiconair/properties v1.8.9 // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.17 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // 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/hashstructure/v2 v2.0.2 // indirect
|
||||
github.com/moby/docker-image-spec v1.3.1 // indirect
|
||||
github.com/moby/locker v1.0.1 // indirect
|
||||
github.com/moby/spdystream v0.2.0 // indirect
|
||||
github.com/moby/spdystream v0.4.0 // indirect
|
||||
github.com/moby/sys/capability v0.4.0 // indirect
|
||||
github.com/moby/sys/mountinfo v0.7.2 // indirect
|
||||
github.com/moby/sys/sequential v0.6.0 // indirect
|
||||
github.com/moby/sys/signal v0.7.1 // indirect
|
||||
@ -143,52 +149,61 @@ require (
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect
|
||||
github.com/otiai10/mint v1.6.3 // indirect
|
||||
github.com/pelletier/go-toml v1.9.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.17.0 // indirect
|
||||
github.com/prometheus/client_model v0.5.0 // indirect
|
||||
github.com/prometheus/common v0.44.0 // indirect
|
||||
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/prometheus/client_golang v1.20.5 // indirect
|
||||
github.com/prometheus/client_model v0.6.1 // indirect
|
||||
github.com/prometheus/common v0.55.0 // indirect
|
||||
github.com/prometheus/procfs v0.15.1 // indirect
|
||||
github.com/rivo/uniseg v0.2.0 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/secure-systems-lab/go-securesystemslib v0.4.0 // indirect
|
||||
github.com/serialx/hashring v0.0.0-20200727003509-22c0c7ab6b1b // indirect
|
||||
github.com/shibumi/go-pathspec v1.3.0 // indirect
|
||||
github.com/tonistiigi/fsutil v0.0.0-20240424095704-91a3fc46842c // indirect
|
||||
github.com/tonistiigi/dchapes-mode v0.0.0-20241001053921-ca0759fec205 // indirect
|
||||
github.com/tonistiigi/fsutil v0.0.0-20250113203817-b14e27f4135a // indirect
|
||||
github.com/tonistiigi/go-csvvalue v0.0.0-20240710180619-ddb21b71c0b4 // indirect
|
||||
github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea // indirect
|
||||
github.com/tonistiigi/vt100 v0.0.0-20240514184818-90bafcd6abab // indirect
|
||||
github.com/x448/float16 v0.8.4 // 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.46.1 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.46.1 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.44.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v0.44.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.21.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk/metric v1.21.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.0.0 // indirect
|
||||
golang.org/x/crypto v0.26.0 // indirect
|
||||
golang.org/x/net v0.28.0 // indirect
|
||||
golang.org/x/oauth2 v0.22.0 // indirect
|
||||
golang.org/x/term v0.23.0 // indirect
|
||||
golang.org/x/text v0.17.0 // indirect
|
||||
github.com/xhit/go-str2duration/v2 v2.1.0 // indirect
|
||||
github.com/zclconf/go-cty v1.16.0 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.56.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.56.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.31.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.31.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.31.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk/metric v1.34.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.3.1 // indirect
|
||||
golang.org/x/crypto v0.32.0 // indirect
|
||||
golang.org/x/net v0.34.0 // indirect
|
||||
golang.org/x/oauth2 v0.25.0 // indirect
|
||||
golang.org/x/term v0.28.0 // indirect
|
||||
golang.org/x/text v0.21.0 // indirect
|
||||
golang.org/x/time v0.6.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 // indirect
|
||||
google.golang.org/protobuf v1.34.2 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250106144421-5f5ef82da422 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f // indirect
|
||||
google.golang.org/protobuf v1.36.4 // indirect
|
||||
gopkg.in/cenkalti/backoff.v1 v1.1.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
|
||||
k8s.io/api v0.29.2 // indirect
|
||||
k8s.io/apimachinery v0.29.2 // indirect
|
||||
k8s.io/client-go v0.29.2 // indirect
|
||||
k8s.io/klog/v2 v2.110.1 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 // indirect
|
||||
k8s.io/utils v0.0.0-20230726121419-3b25d923346b // indirect
|
||||
k8s.io/api v0.31.2 // indirect
|
||||
k8s.io/apimachinery v0.31.2 // indirect
|
||||
k8s.io/client-go v0.31.2 // indirect
|
||||
k8s.io/klog/v2 v2.130.1 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect
|
||||
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 // indirect
|
||||
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
|
||||
sigs.k8s.io/yaml v1.3.0 // indirect
|
||||
sigs.k8s.io/yaml v1.4.0 // indirect
|
||||
)
|
||||
|
||||
replace github.com/compose-spec/compose-go/v2 => github.com/glours/compose-go/v2 v2.0.0-20250403082600-80aa75f06535
|
||||
|
459
go.sum
459
go.sum
@ -1,25 +1,23 @@
|
||||
cloud.google.com/go v0.112.0 h1:tpFCD7hpHFlQ8yPwT3x+QeXqc2T6+n6T+hmABHfDUSM=
|
||||
cloud.google.com/go/compute v1.23.3 h1:6sVlXXBmbd7jNX0Ipq0trII3e4n1/MsADLK6a+aiVlk=
|
||||
cloud.google.com/go/compute/metadata v0.5.0 h1:Zr0eK8JbFv6+Wi4ilXAR8FJ3wyNdpxHKJNPos6LTZOY=
|
||||
cloud.google.com/go/compute/metadata v0.5.0/go.mod h1:aHnloV2TPI38yx4s9+wAZhHykWvVCfu7hQbF+9CWoiY=
|
||||
dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s=
|
||||
dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
|
||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU=
|
||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8=
|
||||
github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20230306123547-8075edf89bb0 h1:59MxjQVfjXsBpLy+dbd2/ELV5ofnUkUZBvWSC85sheA=
|
||||
github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20230306123547-8075edf89bb0/go.mod h1:OahwfttHWG6eJ0clwcfBAHoDI6X/LV/15hx/wlMZSrU=
|
||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8afgbRMd7mFxO99hRNu+6tazq8nFF9lIwo9JFroBk=
|
||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8=
|
||||
github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20231105174938-2b5cbb29f3e2 h1:dIScnXFlF784X79oi7MzVT6GWqr/W1uUt0pB5CsDs9M=
|
||||
github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20231105174938-2b5cbb29f3e2/go.mod h1:gCLVsLfv1egrcZu+GoJATN5ts75F2s62ih/457eWzOw=
|
||||
github.com/AlecAivazis/survey/v2 v2.3.7 h1:6I/u8FvytdGsgonrYsVn2t8t4QiRnh6QSTqkkhIiSjQ=
|
||||
github.com/AlecAivazis/survey/v2 v2.3.7/go.mod h1:xUTIdE4KCOIjsBAE1JYsUPoCqYdZ1reCfTwbto0Fduo=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/DefangLabs/secret-detector v0.0.0-20250403165618-22662109213e h1:rd4bOvKmDIx0WeTv9Qz+hghsgyjikFiPrseXHlKepO0=
|
||||
github.com/DefangLabs/secret-detector v0.0.0-20250403165618-22662109213e/go.mod h1:blbwPQh4DTlCZEfk1BLU4oMIhLda2U+A840Uag9DsZw=
|
||||
github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0=
|
||||
github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
|
||||
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
|
||||
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
|
||||
github.com/Microsoft/hcsshim v0.11.7 h1:vl/nj3Bar/CvJSYo7gIQPyRWc9f3c6IeSNavBTSZNZQ=
|
||||
github.com/Microsoft/hcsshim v0.11.7/go.mod h1:MV8xMfmECjl5HdO7U/3/hFVnkmSBjAjmA09d4bExKcU=
|
||||
github.com/Microsoft/hcsshim v0.12.9 h1:2zJy5KA+l0loz1HzEGqyNnjd3fyZA31ZBCGKacp6lLg=
|
||||
github.com/Microsoft/hcsshim v0.12.9/go.mod h1:fJ0gkFAna6ukt0bLdKB8djt4XIJhF/vEPuoIWYVvZ8Y=
|
||||
github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 h1:+vx7roKuyA63nhn5WAunQHLTznkw5W8b1Xc0dNjp83s=
|
||||
github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDeC1lPdgDeDbhX8XFpy1jqjK0IBG8W5K+xYqA0w=
|
||||
github.com/Shopify/logrus-bugsnag v0.0.0-20170309145241-6dbc35f2c30d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ=
|
||||
@ -31,34 +29,36 @@ github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuy
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/anchore/go-struct-converter v0.0.0-20221118182256-c68fdcfa2092 h1:aM1rlcoLz8y5B2r4tTLMiVTrMtpfY0O8EScKJxaSaEc=
|
||||
github.com/anchore/go-struct-converter v0.0.0-20221118182256-c68fdcfa2092/go.mod h1:rYqSE9HbjzpHTI74vwPvae4ZVYZd1lue2ta6xHPdblA=
|
||||
github.com/apparentlymart/go-textseg/v15 v15.0.0 h1:uYvfpb3DyLSCGWnctWKGj857c6ew1u1fNQOlOtuGxQY=
|
||||
github.com/apparentlymart/go-textseg/v15 v15.0.0/go.mod h1:K8XmNZdhEBkdlyDdvbmmsvpAG721bKi0joRfFdHIWJ4=
|
||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
|
||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
|
||||
github.com/aws/aws-sdk-go-v2 v1.24.1 h1:xAojnj+ktS95YZlDf0zxWBkbFtymPeDP+rvUQIH3uAU=
|
||||
github.com/aws/aws-sdk-go-v2 v1.24.1/go.mod h1:LNh45Br1YAkEKaAqvmE1m8FUx6a5b/V0oAKV7of29b4=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.26.6 h1:Z/7w9bUqlRI0FFQpetVuFYEsjzE3h7fpU6HuGmfPL/o=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.26.6/go.mod h1:uKU6cnDmYCvJ+pxO9S4cWDb2yWWIH5hra+32hVh1MI4=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.16.16 h1:8q6Rliyv0aUFAVtzaldUEcS+T5gbadPbWdV1WcAddK8=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.16.16/go.mod h1:UHVZrdUsv63hPXFo1H7c5fEneoVo9UXiz36QG1GEPi0=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.11 h1:c5I5iH+DZcH3xOIMlz3/tCKJDaHFwYEmxvlh2fAcFo8=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.11/go.mod h1:cRrYDYAMUohBJUtUnOhydaMHtiK/1NZ0Otc9lIb6O0Y=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.10 h1:vF+Zgd9s+H4vOXd5BMaPWykta2a6Ih0AKLq/X6NYKn4=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.10/go.mod h1:6BkRjejp/GR4411UGqkX8+wFMbFbqsUIimfK4XjOKR4=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.10 h1:nYPe006ktcqUji8S2mqXf9c/7NdiKriOwMvWQHgYztw=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.10/go.mod h1:6UV4SZkVvmODfXKql4LCbaZUpF7HO2BX38FgBf9ZOLw=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.7.3 h1:n3GDfwqF2tzEkXlv5cuy4iy7LpKDtqDMcNLfZDu9rls=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.7.3/go.mod h1:6fQQgfuGmw8Al/3M2IgIllycxV7ZW7WCdVSqfBeUiCY=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.4 h1:/b31bi3YVNlkzkBrm9LfpaKoaYZUxIAj4sHfOTmLfqw=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.4/go.mod h1:2aGXHFmbInwgP9ZfpmdIfOELL79zhdNYNmReK8qDfdQ=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.10 h1:DBYTXwIGQSGs9w4jKm60F5dmCQ3EEruxdc0MFh+3EY4=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.10/go.mod h1:wohMUQiFdzo0NtxbBg0mSRGZ4vL3n0dKjLTINdcIino=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.18.7 h1:eajuO3nykDPdYicLlP3AGgOyVN3MOlFmZv7WGTuJPow=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.18.7/go.mod h1:+mJNDdF+qiUlNKNC3fxn74WWNN+sOiGOEImje+3ScPM=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.7 h1:QPMJf+Jw8E1l7zqhZmMlFw6w1NmfkfiSK8mS4zOx3BA=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.7/go.mod h1:ykf3COxYI0UJmxcfcxcVuz7b6uADi1FkiUz6Eb7AgM8=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.26.7 h1:NzO4Vrau795RkUdSHKEwiR01FaGzGOH1EETJ+5QHnm0=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.26.7/go.mod h1:6h2YuIoxaMSCFf5fi1EgZAwdfkGMgDY+DVfa61uLe4U=
|
||||
github.com/aws/smithy-go v1.19.0 h1:KWFKQV80DpP3vJrrA9sVAHQ5gc2z8i4EzrLhLlWXcBM=
|
||||
github.com/aws/smithy-go v1.19.0/go.mod h1:NukqUGpCZIILqqiV0NIjeFh24kd/FAa4beRb6nbIUPE=
|
||||
github.com/aws/aws-sdk-go-v2 v1.30.3 h1:jUeBtG0Ih+ZIFH0F4UkmL9w3cSpaMv9tYYDbzILP8dY=
|
||||
github.com/aws/aws-sdk-go-v2 v1.30.3/go.mod h1:nIQjQVp5sfpQcTc9mPSr1B0PaWK5ByX9MOoDadSN4lc=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.27.27 h1:HdqgGt1OAP0HkEDDShEl0oSYa9ZZBSOmKpdpsDMdO90=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.27.27/go.mod h1:MVYamCg76dFNINkZFu4n4RjDixhVr51HLj4ErWzrVwg=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.27 h1:2raNba6gr2IfA0eqqiP2XiQ0UVOpGPgDSi0I9iAP+UI=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.27/go.mod h1:gniiwbGahQByxan6YjQUMcW4Aov6bLC3m+evgcoN4r4=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.11 h1:KreluoV8FZDEtI6Co2xuNk/UqI9iwMrOx/87PBNIKqw=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.11/go.mod h1:SeSUYBLsMYFoRvHE0Tjvn7kbxaUhl75CJi1sbfhMxkU=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.15 h1:SoNJ4RlFEQEbtDcCEt+QG56MY4fm4W8rYirAmq+/DdU=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.15/go.mod h1:U9ke74k1n2bf+RIgoX1SXFed1HLs51OgUSs+Ph0KJP8=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.15 h1:C6WHdGnTDIYETAm5iErQUiVNsclNx9qbJVPIt03B6bI=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.15/go.mod h1:ZQLZqhcu+JhSrA9/NXRm8SkDvsycE+JkV3WGY41e+IM=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 h1:hT8rVHwugYE2lEfdFE0QWVo81lF7jMrYJVDWI+f+VxU=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0/go.mod h1:8tu/lYfQfFe6IGnaOdrpVgEL2IrrDOf6/m9RQum4NkY=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3 h1:dT3MqvGhSoaIhRseqw2I0yH81l7wiR2vjs57O51EAm8=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3/go.mod h1:GlAeCkHwugxdHaueRr4nhPuY+WW+gR8UjlcqzPr1SPI=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.17 h1:HGErhhrxZlQ044RiM+WdoZxp0p+EGM62y3L6pwA4olE=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.17/go.mod h1:RkZEx4l0EHYDJpWppMJ3nD9wZJAa8/0lq9aVC+r2UII=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.22.4 h1:BXx0ZIxvrJdSgSvKTZ+yRBeSqqgPM89VPlulEcl37tM=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.22.4/go.mod h1:ooyCOXjvJEsUw7x+ZDHeISPMhtwI3ZCB7ggFMcFfWLU=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4 h1:yiwVzJW2ZxZTurVbYWA7QOrAaCYQR72t0wrSBfoesUE=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4/go.mod h1:0oxfLkpz3rQ/CHlx5hB7H69YUpFiI1tql6Q6Ne+1bCw=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.30.3 h1:ZsDKRLXGWHk8WdtyYMoGNO7bTudrvuKpDKgMVRlepGE=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.30.3/go.mod h1:zwySh8fpFyXp9yOr/KVzxOl8SRqgf/IDw5aUt9UKFcQ=
|
||||
github.com/aws/smithy-go v1.20.3 h1:ryHwveWzPV5BIof6fyDvor6V3iUL7nTfiTKXHiW05nE=
|
||||
github.com/aws/smithy-go v1.20.3/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC3qS14E=
|
||||
github.com/beorn7/perks v0.0.0-20150223135152-b965b613227f/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||
@ -75,68 +75,69 @@ github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b h1:otBG+dV+YK+Soembj
|
||||
github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50=
|
||||
github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0 h1:nvj0OLI3YqYXer/kZD8Ri1aaunCxIEsOst1BVJswV0o=
|
||||
github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE=
|
||||
github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM=
|
||||
github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
|
||||
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cloudflare/cfssl v0.0.0-20180223231731-4e2dcbde5004 h1:lkAMpLVBDaj17e85keuznYcH5rqI438v41pKcBl4ZxQ=
|
||||
github.com/cloudflare/cfssl v0.0.0-20180223231731-4e2dcbde5004/go.mod h1:yMWuSON2oQp+43nFtAV/uvKQIFpSPerB57DCt9t8sSA=
|
||||
github.com/cncf/xds/go v0.0.0-20240723142845-024c85f92f20 h1:N+3sFI5GUjRKBi+i0TxYVST9h4Ie192jJWpHvthBBgg=
|
||||
github.com/cncf/xds/go v0.0.0-20240723142845-024c85f92f20/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8=
|
||||
github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb h1:EDmT6Q9Zs+SbUoc7Ik9EfrFqcylYqgPZ9ANSbTAntnE=
|
||||
github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb/go.mod h1:ZjrT6AXHbDs86ZSdt/osfBi5qfexBrKUdONk989Wnk4=
|
||||
github.com/compose-spec/compose-go/v2 v2.4.4 h1:cvHBl5Jf1iNBmRrZCICmHvaoskYc1etTPEMLKVwokAY=
|
||||
github.com/compose-spec/compose-go/v2 v2.4.4/go.mod h1:lFN0DrMxIncJGYAXTfWuajfwj5haBJqrBkarHcnjJKc=
|
||||
github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM=
|
||||
github.com/containerd/cgroups v1.1.0/go.mod h1:6ppBcbh/NOOUU+dMKrykgaBnK9lCIBxHqJDGwsa1mIw=
|
||||
github.com/containerd/cgroups/v3 v3.0.5 h1:44na7Ud+VwyE7LIoJ8JTNQOa549a8543BmzaJHo6Bzo=
|
||||
github.com/containerd/cgroups/v3 v3.0.5/go.mod h1:SA5DLYnXO8pTGYiAHXz94qvLQTKfVM5GEVisn4jpins=
|
||||
github.com/containerd/console v1.0.4 h1:F2g4+oChYvBTsASRTz8NP6iIAi97J3TtSAsLbIFn4ro=
|
||||
github.com/containerd/console v1.0.4/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk=
|
||||
github.com/containerd/containerd v1.7.23 h1:H2CClyUkmpKAGlhQp95g2WXHfLYc7whAuvZGBNYOOwQ=
|
||||
github.com/containerd/containerd v1.7.23/go.mod h1:7QUzfURqZWCZV7RLNEn1XjUCQLEf0bkaK4GjUaZehxw=
|
||||
github.com/containerd/containerd/api v1.7.19 h1:VWbJL+8Ap4Ju2mx9c9qS1uFSB1OVYr5JJrW2yT5vFoA=
|
||||
github.com/containerd/containerd/api v1.7.19/go.mod h1:fwGavl3LNwAV5ilJ0sbrABL44AQxmNjDRcwheXDb6Ig=
|
||||
github.com/containerd/continuity v0.4.3 h1:6HVkalIp+2u1ZLH1J/pYX2oBVXlJZvh1X1A7bEZ9Su8=
|
||||
github.com/containerd/continuity v0.4.3/go.mod h1:F6PTNCKepoxEaXLQp3wDAjygEnImnZ/7o4JzpodfroQ=
|
||||
github.com/containerd/errdefs v0.3.0 h1:FSZgGOeK4yuT/+DnF07/Olde/q4KBoMsaamhXxIMDp4=
|
||||
github.com/containerd/errdefs v0.3.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M=
|
||||
github.com/containerd/containerd/api v1.8.0 h1:hVTNJKR8fMc/2Tiw60ZRijntNMd1U+JVMyTRdsD2bS0=
|
||||
github.com/containerd/containerd/api v1.8.0/go.mod h1:dFv4lt6S20wTu/hMcP4350RL87qPWLVa/OHOwmmdnYc=
|
||||
github.com/containerd/containerd/v2 v2.0.4 h1:+r7yJMwhTfMm3CDyiBjMBQO8a9CTBxL2Bg/JtqtIwB8=
|
||||
github.com/containerd/containerd/v2 v2.0.4/go.mod h1:5j9QUUaV/cy9ZeAx4S+8n9ffpf+iYnEj4jiExgcbuLY=
|
||||
github.com/containerd/continuity v0.4.5 h1:ZRoN1sXq9u7V6QoHMcVWGhOwDFqZ4B9i5H6un1Wh0x4=
|
||||
github.com/containerd/continuity v0.4.5/go.mod h1:/lNJvtJKUQStBzpVQ1+rasXO1LAWtUQssk28EZvJ3nE=
|
||||
github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI=
|
||||
github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M=
|
||||
github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE=
|
||||
github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk=
|
||||
github.com/containerd/fifo v1.1.0 h1:4I2mbh5stb1u6ycIABlBw9zgtlK8viPI9QkQNRQEEmY=
|
||||
github.com/containerd/fifo v1.1.0/go.mod h1:bmC4NWMbXlt2EZ0Hc7Fx7QzTFxgPID13eH0Qu+MAb2o=
|
||||
github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
|
||||
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
|
||||
github.com/containerd/nydus-snapshotter v0.14.0 h1:6/eAi6d7MjaeLLuMO8Udfe5GVsDudmrDNO4SGETMBco=
|
||||
github.com/containerd/nydus-snapshotter v0.14.0/go.mod h1:TT4jv2SnIDxEBu4H2YOvWQHPOap031ydTaHTuvc5VQk=
|
||||
github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A=
|
||||
github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw=
|
||||
github.com/containerd/stargz-snapshotter v0.15.1 h1:fpsP4kf/Z4n2EYnU0WT8ZCE3eiKDwikDhL6VwxIlgeA=
|
||||
github.com/containerd/stargz-snapshotter/estargz v0.15.1 h1:eXJjw9RbkLFgioVaTG+G/ZW/0kEe2oEKCdS/ZxIyoCU=
|
||||
github.com/containerd/stargz-snapshotter/estargz v0.15.1/go.mod h1:gr2RNwukQ/S9Nv33Lt6UC7xEx58C+LHRdoqbEKjz1Kk=
|
||||
github.com/containerd/ttrpc v1.2.5 h1:IFckT1EFQoFBMG4c3sMdT8EP3/aKfumK1msY+Ze4oLU=
|
||||
github.com/containerd/ttrpc v1.2.5/go.mod h1:YCXHsb32f+Sq5/72xHubdiJRQY9inL4a4ZQrAbN1q9o=
|
||||
github.com/containerd/typeurl/v2 v2.2.0 h1:6NBDbQzr7I5LHgp34xAXYF5DOTQDn05X58lsPEmzLso=
|
||||
github.com/containerd/typeurl/v2 v2.2.0/go.mod h1:8XOOxnyatxSWuG8OfsZXVnAF4iZfedjS/8UHSPJnX4g=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/containerd/nydus-snapshotter v0.15.0 h1:RqZRs1GPeM6T3wmuxJV9u+2Rg4YETVMwTmiDeX+iWC8=
|
||||
github.com/containerd/nydus-snapshotter v0.15.0/go.mod h1:biq0ijpeZe0I5yZFSJyHzFSjjRZQ7P7y/OuHyd7hYOw=
|
||||
github.com/containerd/platforms v1.0.0-rc.1 h1:83KIq4yy1erSRgOVHNk1HYdPvzdJ5CnsWaRoJX4C41E=
|
||||
github.com/containerd/platforms v1.0.0-rc.1/go.mod h1:J71L7B+aiM5SdIEqmd9wp6THLVRzJGXfNuWCZCllLA4=
|
||||
github.com/containerd/plugin v1.0.0 h1:c8Kf1TNl6+e2TtMHZt+39yAPDbouRH9WAToRjex483Y=
|
||||
github.com/containerd/plugin v1.0.0/go.mod h1:hQfJe5nmWfImiqT1q8Si3jLv3ynMUIBB47bQ+KexvO8=
|
||||
github.com/containerd/stargz-snapshotter v0.16.3 h1:zbQMm8dRuPHEOD4OqAYGajJJUwCeUzt4j7w9Iaw58u4=
|
||||
github.com/containerd/stargz-snapshotter/estargz v0.16.3 h1:7evrXtoh1mSbGj/pfRccTampEyKpjpOnS3CyiV1Ebr8=
|
||||
github.com/containerd/stargz-snapshotter/estargz v0.16.3/go.mod h1:uyr4BfYfOj3G9WBVE8cOlQmXAbPN9VEQpBBeJIuOipU=
|
||||
github.com/containerd/ttrpc v1.2.7 h1:qIrroQvuOL9HQ1X6KHe2ohc7p+HP/0VE6XPU7elJRqQ=
|
||||
github.com/containerd/ttrpc v1.2.7/go.mod h1:YCXHsb32f+Sq5/72xHubdiJRQY9inL4a4ZQrAbN1q9o=
|
||||
github.com/containerd/typeurl/v2 v2.2.3 h1:yNA/94zxWdvYACdYO8zofhrTVuQY73fFU1y++dYSw40=
|
||||
github.com/containerd/typeurl/v2 v2.2.3/go.mod h1:95ljDnPfD3bAbDJRugOiShd/DlAAsxGtUBhJxIn7SCk=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6 h1:XJtiaUW6dEEqVuZiMTn1ldk455QWwEIsMIJlo5vtkx0=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
|
||||
github.com/creack/pty v1.1.21 h1:1/QdRyBaHHJP61QkWMXlOIBfsgdDeeKfK8SYVUWJKf0=
|
||||
github.com/creack/pty v1.1.21/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
|
||||
github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s=
|
||||
github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/denisenkom/go-mssqldb v0.0.0-20191128021309-1d7a30a10f73/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
|
||||
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
|
||||
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
|
||||
github.com/docker/buildx v0.17.1 h1:9ob2jGp4+W9PxWw68GsoNFp+eYFc7eUoRL9VljLCSM4=
|
||||
github.com/docker/buildx v0.17.1/go.mod h1:kJOhOhS47LRvrLFRulFiO5SE6VJf54yYMn7DzjgO5W0=
|
||||
github.com/docker/cli v27.3.2-0.20241008150905-cb3048fbebb1+incompatible h1:fJ3SzYiebfWoas3qOJpmRsNrDL2w1XIpPFywSLFhhfk=
|
||||
github.com/docker/cli v27.3.2-0.20241008150905-cb3048fbebb1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||
github.com/docker/cli-docs-tool v0.8.0 h1:YcDWl7rQJC3lJ7WVZRwSs3bc9nka97QLWfyJQli8yJU=
|
||||
github.com/docker/cli-docs-tool v0.8.0/go.mod h1:8TQQ3E7mOXoYUs811LiPdUnAhXrcVsBIrW21a5pUbdk=
|
||||
github.com/docker/buildx v0.22.0 h1:pGTcGZa+kxpYUlM/6ACsp1hXhkEDulz++RNXPdE8Afk=
|
||||
github.com/docker/buildx v0.22.0/go.mod h1:ThbnUe4kNiStlq6cLXruElyEdSTdPL3k/QerNUmPvHE=
|
||||
github.com/docker/cli v28.0.4+incompatible h1:pBJSJeNd9QeIWPjRcV91RVJihd/TXB77q1ef64XEu4A=
|
||||
github.com/docker/cli v28.0.4+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||
github.com/docker/cli-docs-tool v0.9.0 h1:CVwQbE+ZziwlPqrJ7LRyUF6GvCA+6gj7MTCsayaK9t0=
|
||||
github.com/docker/cli-docs-tool v0.9.0/go.mod h1:ClrwlNW+UioiRyH9GiAOe1o3J/TsY3Tr1ipoypjAUtc=
|
||||
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||
github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk=
|
||||
github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||
github.com/docker/docker v27.3.1+incompatible h1:KttF0XoteNTicmUtBO0L2tP+J7FGRFTjaEF4k6WdhfI=
|
||||
github.com/docker/docker v27.3.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/docker v28.0.4+incompatible h1:JNNkBctYKurkw6FrHfKqY0nKIDf5nrbxjVBtS+cdcok=
|
||||
github.com/docker/docker v28.0.4+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/docker-credential-helpers v0.8.2 h1:bX3YxiGzFP5sOXWc3bTPEXdEaZSeVMrFgOr3T+zrFAo=
|
||||
github.com/docker/docker-credential-helpers v0.8.2/go.mod h1:P3ci7E3lwkZg6XiHdRKft1KckHiO9a2rNtyFbZ/ry9M=
|
||||
github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c h1:lzqkGL9b3znc+ZUgi7FlLnqjQhcXxkNM/quxIjBVMD0=
|
||||
@ -144,8 +145,6 @@ github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c/go.mod h1:CADgU4DSXK
|
||||
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
|
||||
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
|
||||
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
|
||||
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c h1:+pKlWGMw7gf6bQ+oDZB4KHQFypsfjYlq/C4rfL7D3g8=
|
||||
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA=
|
||||
github.com/docker/go-metrics v0.0.0-20180209012529-399ea8c73916/go.mod h1:/u0gXw0Gay3ceNrsHubL3BtdOL2fHf93USgMTe0W5dI=
|
||||
github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQV8=
|
||||
github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw=
|
||||
@ -158,8 +157,6 @@ github.com/eiannone/keyboard v0.0.0-20220611211555-0d226195f203 h1:XBBHcIb256gUJ
|
||||
github.com/eiannone/keyboard v0.0.0-20220611211555-0d226195f203/go.mod h1:E1jcSv8FaEny+OP/5k9UxZVw9YFWGj7eI4KR/iOBqCg=
|
||||
github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g=
|
||||
github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
|
||||
github.com/envoyproxy/protoc-gen-validate v1.1.0 h1:tntQDh69XqOCOZsDz0lVJQez/2L6Uu2PdjCQwWCJ3bM=
|
||||
github.com/envoyproxy/protoc-gen-validate v1.1.0/go.mod h1:sXRDRVmzEbkM7CVcM06s9shE/m23dg3wzjl0UWqJ2q4=
|
||||
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0=
|
||||
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
@ -168,39 +165,41 @@ github.com/fsnotify/fsevents v0.2.0/go.mod h1:B3eEk39i4hz8y1zaWS/wPrAP4O6wkIl7HQ
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fvbommel/sortorder v1.1.0 h1:fUmoe+HLsBTctBDoaBwpQo5N+nrCp8g/BjKb/6ZQmYw=
|
||||
github.com/fvbommel/sortorder v1.1.0/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0=
|
||||
github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
|
||||
github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
|
||||
github.com/glours/compose-go/v2 v2.0.0-20250403082600-80aa75f06535 h1:S/P6v3QxsMpkKn+2OSMPNkfSkadSjSHoMGAc/eBZgMU=
|
||||
github.com/glours/compose-go/v2 v2.0.0-20250403082600-80aa75f06535/go.mod h1:vPlkN0i+0LjLf9rv52lodNMUTJF5YHVfHVGLLIP67NA=
|
||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
|
||||
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE=
|
||||
github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
|
||||
github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE=
|
||||
github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k=
|
||||
github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g=
|
||||
github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
|
||||
github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU=
|
||||
github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
|
||||
github.com/go-sql-driver/mysql v1.3.0 h1:pgwjLi/dvffoP9aabwkT3AKpXQM93QARkjFhDDqC1UE=
|
||||
github.com/go-sql-driver/mysql v1.3.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
|
||||
github.com/go-viper/mapstructure/v2 v2.0.0 h1:dhn8MZ1gZ0mzeodTG3jt5Vj/o87xZKuNAprG2mQfMfc=
|
||||
github.com/go-viper/mapstructure/v2 v2.0.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||
github.com/gofrs/flock v0.12.1 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E=
|
||||
github.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeHCoD0=
|
||||
github.com/gogo/googleapis v1.4.1 h1:1Yx4Myt7BxzvUr5ldGSbwYiZG6t9wGBZ+8/fX3Wvtq0=
|
||||
github.com/gogo/googleapis v1.4.1/go.mod h1:2lpHqI5OcWCtVElxXnPt+s8oJvMpySlOyM6xDCrzib4=
|
||||
github.com/gogo/protobuf v1.0.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
||||
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
|
||||
github.com/golang/glog v1.2.2 h1:1+mZ9upx1Dh6FmUTFR1naJ77miKiXgALjWOZ3NVFPmY=
|
||||
github.com/golang/glog v1.2.2/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w=
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
@ -215,13 +214,13 @@ github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvR
|
||||
github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
||||
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6 h1:k7nVchz72niMH6YLQNvHSdIE7iqsQxK1P41mySCvssg=
|
||||
github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw=
|
||||
github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af h1:kmjWCqn2qkEml422C2Rrd27c3VGxi6a/6HNq8QmHRKM=
|
||||
github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo=
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
@ -229,11 +228,10 @@ github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
|
||||
github.com/gorilla/mux v1.7.0/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
|
||||
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
|
||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
|
||||
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 h1:YBftPWNWd4WwGqtY2yeZL2ef8rHAxPBD8KFhJpmcqms=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 h1:asbCHRVmodnJTuQ3qamDwqVOIjwqUPTYmYuemVOx+Ys=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0/go.mod h1:ggCgvZ2r7uOoQjOyu2Y1NhHmEPPzzuhWgcza5M1Ji1I=
|
||||
github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed h1:5upAirOpQc1Q53c0bnx2ufif5kANL7bfZWcc6VJWJd8=
|
||||
github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
@ -255,13 +253,15 @@ github.com/in-toto/in-toto-golang v0.5.0/go.mod h1:/Rq0IZHLV7Ku5gielPT4wPHJfH1Gd
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/inhies/go-bytesize v0.0.0-20220417184213-4913239db9cf h1:FtEj8sfIcaaBfAKrE1Cwb61YDtYq9JxChK1c7AKce7s=
|
||||
github.com/inhies/go-bytesize v0.0.0-20220417184213-4913239db9cf/go.mod h1:yrqSXGoD/4EKfF26AOGzscPOgTTJcyAwM2rpixWT+t4=
|
||||
github.com/jinzhu/gorm v0.0.0-20170222002820-5409931a1bb8 h1:CZkYfurY6KGhVtlalI4QwQ6T0Cu6iuY3e0x5RLu96WE=
|
||||
github.com/jinzhu/gorm v0.0.0-20170222002820-5409931a1bb8/go.mod h1:Vla75njaFJ8clLU1W44h34PjIkijhjHIYnZxMqCdxqo=
|
||||
github.com/jinzhu/inflection v0.0.0-20170102125226-1c35d901db3d h1:jRQLvyVGL+iVtDElaEIDdKwpPqUIZJfzkNLV34htpEc=
|
||||
github.com/jinzhu/inflection v0.0.0-20170102125226-1c35d901db3d/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||
github.com/jinzhu/now v1.1.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/jonboulle/clockwork v0.4.0 h1:p4Cf1aMWXnXAUh8lVfewRBx1zaTSYKrKMF2g3ST4RZ4=
|
||||
github.com/jonboulle/clockwork v0.4.0/go.mod h1:xgRqUGwRcjKCO1vbZUEtSLrqKoPSsUpK7fnezOII0kc=
|
||||
github.com/jonboulle/clockwork v0.5.0 h1:Hyh9A8u51kptdkR+cqRpT1EebBwTn1oK9YfGYbdFz6I=
|
||||
github.com/jonboulle/clockwork v0.5.0/go.mod h1:3mZlmanh0g2NDKO5TWZVJAfofYk64M7XN3SzBPjZF60=
|
||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
@ -274,8 +274,8 @@ github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNU
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
|
||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
|
||||
github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
|
||||
github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
|
||||
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
@ -287,9 +287,12 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
github.com/lib/pq v0.0.0-20150723085316-0dad96c0b94f/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/magiconair/properties v1.5.3 h1:C8fxWnhYyME3n0klPOhVM7PtYUB3eV1W3DeFmN3j53Y=
|
||||
github.com/magiconair/properties v1.5.3/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/magiconair/properties v1.8.9 h1:nWcCbLq1N2v/cpNsy5WvQ37Fb+YElfq20WJ/a8RkpQM=
|
||||
github.com/magiconair/properties v1.8.9/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
||||
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
||||
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
@ -297,16 +300,14 @@ github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxec
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
|
||||
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
|
||||
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebGE2xrk=
|
||||
github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y=
|
||||
github.com/mattn/go-sqlite3 v1.6.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
|
||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4=
|
||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
|
||||
github.com/miekg/pkcs11 v1.0.2/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
|
||||
@ -319,16 +320,18 @@ github.com/mitchellh/hashstructure/v2 v2.0.2/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/z
|
||||
github.com/mitchellh/mapstructure v0.0.0-20150613213606-2caf8efc9366/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/moby/buildkit v0.16.0 h1:wOVBj1o5YNVad/txPQNXUXdelm7Hs/i0PUFjzbK0VKE=
|
||||
github.com/moby/buildkit v0.16.0/go.mod h1:Xqx/5GlrqE1yIRORk0NSCVDFpQAU1WjlT6KHYZdisIQ=
|
||||
github.com/moby/buildkit v0.20.1 h1:sT0ZXhhNo5rVbMcYfgttma3TdUHfO5JjFA0UAL8p9fY=
|
||||
github.com/moby/buildkit v0.20.1/go.mod h1:Rq9nB/fJImdk6QeM0niKtOHJqwKeYMrK847hTTDVuA4=
|
||||
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
|
||||
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
|
||||
github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg=
|
||||
github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc=
|
||||
github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk=
|
||||
github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc=
|
||||
github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8=
|
||||
github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c=
|
||||
github.com/moby/spdystream v0.4.0 h1:Vy79D6mHeJJjiPdFEL2yku1kl0chZpJfZcPpb16BRl8=
|
||||
github.com/moby/spdystream v0.4.0/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI=
|
||||
github.com/moby/sys/capability v0.4.0 h1:4D4mI6KlNtWMCM1Z/K0i7RV1FkX+DBDHKVJpCndZoHk=
|
||||
github.com/moby/sys/capability v0.4.0/go.mod h1:4g9IK291rVkms3LKCDOoYlnV8xKwoDTpIrNEE35Wq0I=
|
||||
github.com/moby/sys/mountinfo v0.7.2 h1:1shs6aH5s4o5H2zQLn796ADW1wMrIwHsyJ2v9KouLrg=
|
||||
github.com/moby/sys/mountinfo v0.7.2/go.mod h1:1YOa8w8Ih7uW0wALDUgT1dTTSBrZ+HiBLGws92L2RU4=
|
||||
github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU=
|
||||
@ -341,8 +344,8 @@ github.com/moby/sys/user v0.3.0 h1:9ni5DlcW5an3SvRSx4MouotOygvzaXbaSrc/wGDFWPo=
|
||||
github.com/moby/sys/user v0.3.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs=
|
||||
github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g=
|
||||
github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28=
|
||||
github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
|
||||
github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
|
||||
github.com/moby/term v0.5.2 h1:6qk3FJAFDs6i/q3W/pQ97SX192qKfZgGjCQqfCJkgzQ=
|
||||
github.com/moby/term v0.5.2/go.mod h1:d3djjFCrjnB+fl8NJux+EJzu0msscUP+f8it8hPkFLc=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
@ -361,52 +364,55 @@ github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLA
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.12.0 h1:Iw5WCbBcaAAd0fpRb1c9r5YCylv4XDoCSigm1zLevwU=
|
||||
github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg=
|
||||
github.com/onsi/ginkgo/v2 v2.13.0 h1:0jY9lJquiL8fcf3M4LAXN5aMlS/b2BV86HFFPCPMgE4=
|
||||
github.com/onsi/ginkgo/v2 v2.13.0/go.mod h1:TE309ZR8s5FsKKpuB1YAQYBzCaAfUgatB/xlT/ETL/o=
|
||||
github.com/onsi/ginkgo/v2 v2.19.0 h1:9Cnnf7UHo57Hy3k6/m5k3dRfGTMXGvxhHFvkDTCTpvA=
|
||||
github.com/onsi/ginkgo/v2 v2.19.0/go.mod h1:rlwLi9PilAFJ8jCg9UE1QP6VBpd6/xj3SRC0d6TU0To=
|
||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||
github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA=
|
||||
github.com/onsi/gomega v1.29.0 h1:KIA/t2t5UBzoirT4H9tsML45GEbo3ouUnBHsCfD2tVg=
|
||||
github.com/onsi/gomega v1.29.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ=
|
||||
github.com/onsi/gomega v1.19.0 h1:4ieX6qQjPP/BfC3mpsAtIGGlxTWPeA3Inl/7DtXw1tw=
|
||||
github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro=
|
||||
github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
|
||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||
github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
|
||||
github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug=
|
||||
github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM=
|
||||
github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040=
|
||||
github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M=
|
||||
github.com/opencontainers/runtime-spec v1.2.0 h1:z97+pHb3uELt/yiAWD691HNHQIF07bE7dzrbT927iTk=
|
||||
github.com/opencontainers/runtime-spec v1.2.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
|
||||
github.com/opencontainers/selinux v1.11.0 h1:+5Zbo97w3Lbmb3PeqQtpmTkMwsW5nRI3YaLpt7tQ7oU=
|
||||
github.com/opencontainers/selinux v1.11.0/go.mod h1:E5dMC3VPuVvVHDYmi78qvhJp8+M586T4DlDRYpFkyec=
|
||||
github.com/opencontainers/selinux v1.11.1 h1:nHFvthhM0qY8/m+vfhJylliSshm8G1jJ2jDMcgULaH8=
|
||||
github.com/opencontainers/selinux v1.11.1/go.mod h1:E5dMC3VPuVvVHDYmi78qvhJp8+M586T4DlDRYpFkyec=
|
||||
github.com/opentracing/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsqf19k25Ur8rU=
|
||||
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
||||
github.com/otiai10/copy v1.14.0 h1:dCI/t1iTdYGtkvCuBG2BgR6KZa83PTclw4U5n2wAllU=
|
||||
github.com/otiai10/copy v1.14.0/go.mod h1:ECfuL02W+/FkTWZWgQqXPWZgW9oeKCSQ5qVfSc4qc4w=
|
||||
github.com/otiai10/mint v1.5.1 h1:XaPLeE+9vGbuyEHem1JNk3bYc7KKqyI/na0/mLd/Kks=
|
||||
github.com/otiai10/mint v1.5.1/go.mod h1:MJm72SBthJjz8qhefc4z1PYEieWmy8Bku7CjcAqyUSM=
|
||||
github.com/otiai10/copy v1.14.1 h1:5/7E6qsUMBaH5AnQ0sSLzzTg1oTECmcCmT6lvF45Na8=
|
||||
github.com/otiai10/copy v1.14.1/go.mod h1:oQwrEDDOci3IM8dJF0d8+jnbfPDllW6vUjNc3DoZm9I=
|
||||
github.com/otiai10/mint v1.6.3 h1:87qsV/aw1F5as1eH1zS/yqHY85ANKVMgkDrf9rcxbQs=
|
||||
github.com/otiai10/mint v1.6.3/go.mod h1:MJm72SBthJjz8qhefc4z1PYEieWmy8Bku7CjcAqyUSM=
|
||||
github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
|
||||
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo=
|
||||
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_golang v0.9.0-pre1.0.20180209125602-c332b6f63c06/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
||||
github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g=
|
||||
github.com/prometheus/client_golang v1.17.0 h1:rl2sfwZMtSthVU752MqfjQozy7blglC+1SOtjMAMh+Q=
|
||||
github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+LjWfWDUmp1mBz9JgUY=
|
||||
github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y=
|
||||
github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
|
||||
github.com/prometheus/client_model v0.0.0-20171117100541-99fa1f4be8e5/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw=
|
||||
github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI=
|
||||
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
|
||||
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
|
||||
github.com/prometheus/common v0.0.0-20180110214958-89604d197083/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc=
|
||||
github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY=
|
||||
github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY=
|
||||
github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc=
|
||||
github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8=
|
||||
github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
@ -417,8 +423,8 @@ github.com/r3labs/sse v0.0.0-20210224172625-26fe804710bc h1:zAsgcP8MhzAbhMnB1QQ2
|
||||
github.com/r3labs/sse v0.0.0-20210224172625-26fe804710bc/go.mod h1:S8xSOnV3CgpNrWd0GQ/OoQfMtlg2uPRSuTzcSGrzwK8=
|
||||
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
|
||||
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
|
||||
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
|
||||
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
|
||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/secure-systems-lab/go-securesystemslib v0.4.0 h1:b23VGrQhTA8cN2CbBw7/FulN9fTtqYUdS5+Oxzt+DUE=
|
||||
@ -439,13 +445,13 @@ github.com/spdx/tools-golang v0.5.3/go.mod h1:/ETOahiAo96Ob0/RAIBmFZw6XN0yTnyr/u
|
||||
github.com/spf13/cast v0.0.0-20150508191742-4d07383ffe94 h1:JmfC365KywYwHB946TTiQWEb8kqPY+pybPLoGE9GgVk=
|
||||
github.com/spf13/cast v0.0.0-20150508191742-4d07383ffe94/go.mod h1:r2rcYCSwa1IExKTDiTfzaxqT2FNHs8hODu4LnUfgKEg=
|
||||
github.com/spf13/cobra v0.0.1/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
|
||||
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
|
||||
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
|
||||
github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
|
||||
github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
|
||||
github.com/spf13/jwalterweatherman v0.0.0-20141219030609-3d60171a6431 h1:XTHrT015sxHyJ5FnQ0AeemSspZWaDq7DoTRW0EVsDCE=
|
||||
github.com/spf13/jwalterweatherman v0.0.0-20141219030609-3d60171a6431/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||
github.com/spf13/pflag v1.0.0/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
|
||||
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v0.0.0-20150530192845-be5ff3e4840c h1:2EejZtjFjKJGk71ANb+wtFK5EjUzUkEM3R0xnp559xg=
|
||||
github.com/spf13/viper v0.0.0-20150530192845-be5ff3e4840c/go.mod h1:A8kyI5cUJhb8N+3pkfONlcEcZbueH6nhAm0Fq7SrnBM=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
@ -462,22 +468,26 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/theupdateframework/notary v0.7.0 h1:QyagRZ7wlSpjT5N2qQAh/pN+DVqgekv4DzbAiAiEL3c=
|
||||
github.com/theupdateframework/notary v0.7.0/go.mod h1:c9DRxcmhHmVLDay4/2fUYdISnHqbFDGRSlXPO0AhYWw=
|
||||
github.com/tilt-dev/fsnotify v1.4.8-0.20220602155310-fff9c274a375 h1:QB54BJwA6x8QU9nHY3xJSZR2kX9bgpZekRKGkLTmEXA=
|
||||
github.com/tilt-dev/fsnotify v1.4.8-0.20220602155310-fff9c274a375/go.mod h1:xRroudyp5iVtxKqZCrA6n2TLFRBf8bmnjr1UD4x+z7g=
|
||||
github.com/tonistiigi/fsutil v0.0.0-20240424095704-91a3fc46842c h1:+6wg/4ORAbnSoGDzg2Q1i3CeMcT/jjhye/ZfnBHy7/M=
|
||||
github.com/tonistiigi/fsutil v0.0.0-20240424095704-91a3fc46842c/go.mod h1:vbbYqJlnswsbJqWUcJN8fKtBhnEgldDrcagTgnBVKKM=
|
||||
github.com/tonistiigi/dchapes-mode v0.0.0-20241001053921-ca0759fec205 h1:eUk79E1w8yMtXeHSzjKorxuC8qJOnyXQnLaJehxpJaI=
|
||||
github.com/tonistiigi/dchapes-mode v0.0.0-20241001053921-ca0759fec205/go.mod h1:3Iuxbr0P7D3zUzBMAZB+ois3h/et0shEz0qApgHYGpY=
|
||||
github.com/tonistiigi/fsutil v0.0.0-20250113203817-b14e27f4135a h1:EfGw4G0x/8qXWgtcZ6KVaPS+wpWOQMaypczzP8ojkMY=
|
||||
github.com/tonistiigi/fsutil v0.0.0-20250113203817-b14e27f4135a/go.mod h1:Dl/9oEjK7IqnjAm21Okx/XIxUCFJzvh+XdVHUlBwXTw=
|
||||
github.com/tonistiigi/go-csvvalue v0.0.0-20240710180619-ddb21b71c0b4 h1:7I5c2Ig/5FgqkYOh/N87NzoyI9U15qUPXhDD8uCupv8=
|
||||
github.com/tonistiigi/go-csvvalue v0.0.0-20240710180619-ddb21b71c0b4/go.mod h1:278M4p8WsNh3n4a1eqiFcV2FGk7wE5fwUpUom9mK9lE=
|
||||
github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea h1:SXhTLE6pb6eld/v/cCndK0AMpt1wiVFb/YYmqB3/QG0=
|
||||
github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea/go.mod h1:WPnis/6cRcDZSUvVmezrxJPkiO87ThFYsoUiMwWNDJk=
|
||||
github.com/tonistiigi/vt100 v0.0.0-20240514184818-90bafcd6abab h1:H6aJ0yKQ0gF49Qb2z5hI1UHxSQt4JMyxebFR15KnApw=
|
||||
github.com/tonistiigi/vt100 v0.0.0-20240514184818-90bafcd6abab/go.mod h1:ulncasL3N9uLrVann0m+CDlJKWsIAP34MPcOJF6VRvc=
|
||||
github.com/vbatts/tar-split v0.11.5 h1:3bHCTIheBm1qFTcgh9oPu+nNBtX+XJIupG/vacinCts=
|
||||
github.com/vbatts/tar-split v0.11.5/go.mod h1:yZbwRsSeGjusneWgA781EKej9HF8vme8okylkAeNKLk=
|
||||
github.com/vbatts/tar-split v0.11.6 h1:4SjTW5+PU11n6fZenf2IPoV8/tz3AaYHMWjf23envGs=
|
||||
github.com/vbatts/tar-split v0.11.6/go.mod h1:dqKNtesIOr2j2Qv3W/cHjnvk9I8+G7oAkFDFN6TCBEI=
|
||||
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
|
||||
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
||||
@ -485,39 +495,45 @@ github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHo
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
|
||||
github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
|
||||
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
|
||||
github.com/xhit/go-str2duration/v2 v2.1.0 h1:lxklc02Drh6ynqX+DdPyp5pCKLUQpRT8bp8Ydu2Bstc=
|
||||
github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/zclconf/go-cty v1.16.0 h1:xPKEhst+BW5D0wxebMZkxgapvOE/dw7bFTlgSc9nD6w=
|
||||
github.com/zclconf/go-cty v1.16.0/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE=
|
||||
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
|
||||
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1 h1:SpGay3w+nEwMpfVnbqOLH5gY52/foP8RE8UzTZ1pdSE=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1/go.mod h1:4UoMYEZOC0yN/sPGH76KPkkU7zgiEWYWL9vwmbnTJPE=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.46.1 h1:gbhw/u49SS3gkPWiYweQNJGm/uJN5GkI/FrosxSHT7A=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.46.1/go.mod h1:GnOaBaFQ2we3b9AGWJpsBa7v1S5RlQzlC3O7dRMxZhM=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1 h1:aFJWCqJMNjENlcleuuOkGAPH82y0yULBScfXcIEdS24=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1/go.mod h1:sEGXWArGqc3tVa+ekntsN65DmVbVeW+7lTKTjZF3/Fo=
|
||||
go.opentelemetry.io/otel v1.21.0 h1:hzLeKBZEL7Okw2mGzZ0cc4k/A7Fta0uoPgaJCr8fsFc=
|
||||
go.opentelemetry.io/otel v1.21.0/go.mod h1:QZzNPQPm1zLX4gZK4cMi+71eaorMSGT3A4znnUvNNEo=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.44.0 h1:jd0+5t/YynESZqsSyPz+7PAFdEop0dlN0+PkyHYo8oI=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.44.0/go.mod h1:U707O40ee1FpQGyhvqnzmCJm1Wh6OX6GGBVn0E6Uyyk=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v0.44.0 h1:bflGWrfYyuulcdxf14V6n9+CoQcu5SAAdHmDPAJnlps=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v0.44.0/go.mod h1:qcTO4xHAxZLaLxPd60TdE88rxtItPHgHWqOhOGRr0as=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0 h1:cl5P5/GIfFh4t6xyruOgJP5QiA1pw4fYYdv6nc6CBWw=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0/go.mod h1:zgBdWWAu7oEEMC06MMKc5NLbA/1YDXV1sMpSqEeLQLg=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.21.0 h1:tIqheXEFWAZ7O8A7m+J0aPTmpJN3YQ7qetUAdkkkKpk=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.21.0/go.mod h1:nUeKExfxAQVbiVFn32YXpXZZHZ61Cc3s3Rn1pDBGAb0=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.21.0 h1:digkEZCJWobwBqMwC0cwCq8/wkkRy/OowZg5OArWZrM=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.21.0/go.mod h1:/OpE/y70qVkndM0TrxT4KBoN3RsFZP0QaofcfYrj76I=
|
||||
go.opentelemetry.io/otel/metric v1.21.0 h1:tlYWfeo+Bocx5kLEloTjbcDwBuELRrIFxwdQ36PlJu4=
|
||||
go.opentelemetry.io/otel/metric v1.21.0/go.mod h1:o1p3CA8nNHW8j5yuQLdc1eeqEaPfzug24uvsyIEJRWM=
|
||||
go.opentelemetry.io/otel/sdk v1.21.0 h1:FTt8qirL1EysG6sTQRZ5TokkU8d0ugCj8htOgThZXQ8=
|
||||
go.opentelemetry.io/otel/sdk v1.21.0/go.mod h1:Nna6Yv7PWTdgJHVRD9hIYywQBRx7pbox6nwBnZIxl/E=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.21.0 h1:smhI5oD714d6jHE6Tie36fPx4WDFIg+Y6RfAY4ICcR0=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.21.0/go.mod h1:FJ8RAsoPGv/wYMgBdUJXOm+6pzFY3YdljnXtv1SBE8Q=
|
||||
go.opentelemetry.io/otel/trace v1.21.0 h1:WD9i5gzvoUPuXIXH24ZNBudiarZDKuekPqi/E8fpfLc=
|
||||
go.opentelemetry.io/otel/trace v1.21.0/go.mod h1:LGbsEB0f9LGjN+OZaQQ26sohbOmiMR+BaslueVtS/qQ=
|
||||
go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I=
|
||||
go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.56.0 h1:yMkBS9yViCc7U7yeLzJPM2XizlfdVvBRSmsQDWu6qc0=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.56.0/go.mod h1:n8MR6/liuGB5EmTETUBeU5ZgqMOlqKRxUaqPQBOANZ8=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.56.0 h1:4BZHA+B1wXEQoGNHxW8mURaLhcdGwvRnmhGbm+odRbc=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.56.0/go.mod h1:3qi2EEwMgB4xnKgPLqsDP3j9qxnHDZeHsnAxfjQqTko=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0 h1:UP6IpuHFkUgOQL9FFQFrZ+5LiwhhYRbi7VZSIx6Nj5s=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0/go.mod h1:qxuZLtbq5QDtdeSHsS7bcf6EH6uO6jUAgk764zd3rhM=
|
||||
go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY=
|
||||
go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.31.0 h1:FZ6ei8GFW7kyPYdxJaV2rgI6M+4tvZzhYsQ2wgyVC08=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.31.0/go.mod h1:MdEu/mC6j3D+tTEfvI15b5Ci2Fn7NneJ71YMoiS3tpI=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.31.0 h1:ZsXq73BERAiNuuFXYqP4MR5hBrjXfMGSO+Cx7qoOZiM=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.31.0/go.mod h1:hg1zaDMpyZJuUzjFxFsRYBoccE86tM9Uf4IqNMUxvrY=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.31.0 h1:K0XaT3DwHAcV4nKLzcQvwAgSyisUghWoY20I7huthMk=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.31.0/go.mod h1:B5Ki776z/MBnVha1Nzwp5arlzBbE3+1jk+pGmaP5HME=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.31.0 h1:FFeLy03iVTXP6ffeN2iXrxfGsZGCjVx0/4KlizjyBwU=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.31.0/go.mod h1:TMu73/k1CP8nBUpDLc71Wj/Kf7ZS9FK5b53VapRsP9o=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.31.0 h1:lUsI2TYsQw2r1IASwoROaCnjdj2cvC2+Jbxvk6nHnWU=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.31.0/go.mod h1:2HpZxxQurfGxJlJDblybejHB6RX6pmExPNe517hREw4=
|
||||
go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ=
|
||||
go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE=
|
||||
go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A=
|
||||
go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.34.0 h1:5CeK9ujjbFVL5c1PhLuStg1wxA7vQv7ce1EK0Gyvahk=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.34.0/go.mod h1:jQ/r8Ze28zRKoNRdkjCZxfs6YvBTG1+YIqyFVFYec5w=
|
||||
go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k=
|
||||
go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE=
|
||||
go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0=
|
||||
go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU=
|
||||
@ -530,15 +546,15 @@ golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPh
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201117144127-c1f2f97bffc9/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw=
|
||||
golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
|
||||
golang.org/x/exp v0.0.0-20240823005443-9b4947da3948 h1:kx6Ds3MlpiUHKj7syVnbp57++8WpuKPcR5yjLBjvLEA=
|
||||
golang.org/x/exp v0.0.0-20240823005443-9b4947da3948/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ=
|
||||
golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
|
||||
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
|
||||
golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f h1:XdNn9LlyWAhLVp6P/i8QYBW+hlyhrhei9uErw2B5GJo=
|
||||
golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f/go.mod h1:D5SMRVC3C2/4+F/DB1wZsLRnSNimn2Sp/NPsCrsv8ak=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0=
|
||||
golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4=
|
||||
golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
@ -549,10 +565,10 @@ golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLL
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE=
|
||||
golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg=
|
||||
golang.org/x/oauth2 v0.22.0 h1:BzDx2FehcG7jJwgWLELCdmLuxk2i+x9UDpSiss2u0ZA=
|
||||
golang.org/x/oauth2 v0.22.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
||||
golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
|
||||
golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
|
||||
golang.org/x/oauth2 v0.25.0 h1:CY4y7XT9v0cRI9oupztF8AgiIu99L/ksR/Xp/6jrZ70=
|
||||
golang.org/x/oauth2 v0.25.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@ -560,8 +576,8 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
|
||||
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610=
|
||||
golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@ -582,19 +598,20 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
|
||||
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
|
||||
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU=
|
||||
golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk=
|
||||
golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg=
|
||||
golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc=
|
||||
golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||
golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U=
|
||||
golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
@ -602,23 +619,21 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24=
|
||||
golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ=
|
||||
golang.org/x/tools v0.27.0 h1:qEKojBykQkQ4EynWy4S8Weg69NumxKdn40Fce3uc/8o=
|
||||
golang.org/x/tools v0.27.0/go.mod h1:sUi0ZgbwW9ZPAq26Ekut+weQPR5eIM6GQLQ1Yjm1H0Q=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80 h1:KAeGQVN3M9nD0/bQXnr/ClcEMJ968gUXJQ9pwfSynuQ=
|
||||
google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80/go.mod h1:cc8bqMqtv9gMOr0zHg2Vzff5ULhhL2IXP4sbcn32Dro=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142 h1:wKguEg1hsxI2/L3hUYrpo1RVi48K+uTyzKqprwLXsb8=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142/go.mod h1:d6be+8HhtEtucleCbxpPW9PA9XwISACu8nvpPqF0BVo=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 h1:e7S5W7MGGLaSu8j3YjdezkZ+m1/Nm0uRVRMEMGk26Xs=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250106144421-5f5ef82da422 h1:GVIKPyP/kLIyVOgOnTwFOrvQaQUzOzGMCxgFUOEmm24=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250106144421-5f5ef82da422/go.mod h1:b6h1vNKhxaSoEI+5jc3PJUCustfli/mRab7295pY7rw=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f h1:OxYkA3wjPsZyBylwymxSHa7ViiW1Sml4ToBrncvFehI=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f/go.mod h1:+2Yz8+CLJbIfL9z73EW45avw8Lmge3xVElCP9zEKi50=
|
||||
google.golang.org/grpc v1.0.5/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
||||
google.golang.org/grpc v1.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E=
|
||||
google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA=
|
||||
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
|
||||
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
|
||||
google.golang.org/grpc v1.71.0 h1:kF77BGdPTQ4/JZWMlb9VpJ5pa25aqvVqogsxNHHdeBg=
|
||||
google.golang.org/grpc v1.71.0/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec=
|
||||
google.golang.org/protobuf v1.36.4 h1:6A3ZDJHn/eNqc1i+IdefRzy/9PokBTPvcqMySR7NNIM=
|
||||
google.golang.org/protobuf v1.36.4/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||
gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gopkg.in/cenkalti/backoff.v1 v1.1.0 h1:Arh75ttbsvlpVA7WtVpH4u9h6Zl46xuptxqLxPiSo4Y=
|
||||
@ -633,6 +648,8 @@ gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMy
|
||||
gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo=
|
||||
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
|
||||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
||||
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/rethinkdb/rethinkdb-go.v6 v6.2.1 h1:d4KQkxAaAiRY2h5Zqis161Pv91A37uZyJOx73duwUwM=
|
||||
gopkg.in/rethinkdb/rethinkdb-go.v6 v6.2.1/go.mod h1:WbjuEoo1oadwzQ4apSDU+JTvmllEHtsNHS6y7vFc7iw=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
@ -645,25 +662,25 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU=
|
||||
gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU=
|
||||
k8s.io/api v0.29.2 h1:hBC7B9+MU+ptchxEqTNW2DkUosJpp1P+Wn6YncZ474A=
|
||||
k8s.io/api v0.29.2/go.mod h1:sdIaaKuU7P44aoyyLlikSLayT6Vb7bvJNCX105xZXY0=
|
||||
k8s.io/apimachinery v0.29.2 h1:EWGpfJ856oj11C52NRCHuU7rFDwxev48z+6DSlGNsV8=
|
||||
k8s.io/apimachinery v0.29.2/go.mod h1:6HVkd1FwxIagpYrHSwJlQqZI3G9LfYWRPAkUvLnXTKU=
|
||||
k8s.io/client-go v0.29.2 h1:FEg85el1TeZp+/vYJM7hkDlSTFZ+c5nnK44DJ4FyoRg=
|
||||
k8s.io/client-go v0.29.2/go.mod h1:knlvFZE58VpqbQpJNbCbctTVXcd35mMyAAwBdpt4jrA=
|
||||
k8s.io/klog/v2 v2.110.1 h1:U/Af64HJf7FcwMcXyKm2RPM22WZzyR7OSpYj5tg3cL0=
|
||||
k8s.io/klog/v2 v2.110.1/go.mod h1:YGtd1984u+GgbuZ7e08/yBuAfKLSO0+uR1Fhi6ExXjo=
|
||||
k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 h1:aVUu9fTY98ivBPKR9Y5w/AuzbMm96cd3YHRTU83I780=
|
||||
k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00/go.mod h1:AsvuZPBlUDVuCdzJ87iajxtXuR9oktsTctW/R9wwouA=
|
||||
k8s.io/utils v0.0.0-20230726121419-3b25d923346b h1:sgn3ZU783SCgtaSJjpcVVlRqd6GSnlTLKgpAAttJvpI=
|
||||
k8s.io/utils v0.0.0-20230726121419-3b25d923346b/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
||||
gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q=
|
||||
gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA=
|
||||
k8s.io/api v0.31.2 h1:3wLBbL5Uom/8Zy98GRPXpJ254nEFpl+hwndmk9RwmL0=
|
||||
k8s.io/api v0.31.2/go.mod h1:bWmGvrGPssSK1ljmLzd3pwCQ9MgoTsRCuK35u6SygUk=
|
||||
k8s.io/apimachinery v0.31.2 h1:i4vUt2hPK56W6mlT7Ry+AO8eEsyxMD1U44NR22CLTYw=
|
||||
k8s.io/apimachinery v0.31.2/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo=
|
||||
k8s.io/client-go v0.31.2 h1:Y2F4dxU5d3AQj+ybwSMqQnpZH9F30//1ObxOKlTI9yc=
|
||||
k8s.io/client-go v0.31.2/go.mod h1:NPa74jSVR/+eez2dFsEIHNa+3o09vtNaWwWwb1qSxSs=
|
||||
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
|
||||
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
|
||||
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag=
|
||||
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98=
|
||||
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1J8+AsQnQCKsi8A=
|
||||
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
||||
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=
|
||||
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08=
|
||||
sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=
|
||||
sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
|
||||
tags.cncf.io/container-device-interface v0.8.0 h1:8bCFo/g9WODjWx3m6EYl3GfUG31eKJbaggyBDxEldRc=
|
||||
tags.cncf.io/container-device-interface v0.8.0/go.mod h1:Apb7N4VdILW0EVdEMRYXIDVRZfNJZ+kmEUss2kRRQ6Y=
|
||||
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
|
||||
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
|
||||
tags.cncf.io/container-device-interface v1.0.1 h1:KqQDr4vIlxwfYh0Ed/uJGVgX+CHAkahrgabg6Q8GYxc=
|
||||
tags.cncf.io/container-device-interface v1.0.1/go.mod h1:JojJIOeW3hNbcnOH2q0NrWNha/JuHoDZcmYxAZwb2i0=
|
||||
|
@ -25,7 +25,7 @@ import (
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
pusherrors "github.com/containerd/containerd/remotes/errors"
|
||||
pusherrors "github.com/containerd/containerd/v2/core/remotes/errors"
|
||||
"github.com/distribution/reference"
|
||||
"github.com/docker/buildx/util/imagetools"
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
@ -54,6 +54,8 @@ const (
|
||||
// > 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"
|
||||
// ComposeEnvFileMediaType is the media type for each Env File layer in the image manifest.
|
||||
ComposeEnvFileMediaType = "application/vnd.docker.compose.envfile"
|
||||
)
|
||||
|
||||
// clientAuthStatusCodes are client (4xx) errors that are authentication
|
||||
@ -81,6 +83,18 @@ func DescriptorForComposeFile(path string, content []byte) v1.Descriptor {
|
||||
}
|
||||
}
|
||||
|
||||
func DescriptorForEnvFile(path string, content []byte) v1.Descriptor {
|
||||
return v1.Descriptor{
|
||||
MediaType: ComposeEnvFileMediaType,
|
||||
Digest: digest.FromString(string(content)),
|
||||
Size: int64(len(content)),
|
||||
Annotations: map[string]string{
|
||||
"com.docker.compose.version": api.ComposeVersion,
|
||||
"com.docker.compose.envfile": filepath.Base(path),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func PushManifest(
|
||||
ctx context.Context,
|
||||
resolver *imagetools.Resolver,
|
||||
@ -88,6 +102,10 @@ func PushManifest(
|
||||
layers []Pushable,
|
||||
ociVersion api.OCIVersion,
|
||||
) error {
|
||||
// Check if we need an extra empty layer for the manifest config
|
||||
if ociVersion == api.OCIVersion1_1 || ociVersion == "" {
|
||||
layers = append(layers, Pushable{Descriptor: v1.DescriptorEmptyJSON, Data: []byte("{}")})
|
||||
}
|
||||
// prepare to push the manifest by pushing the layers
|
||||
layerDescriptors := make([]v1.Descriptor, len(layers))
|
||||
for i := range layers {
|
||||
|
@ -16,8 +16,6 @@ package sync
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
)
|
||||
|
||||
// PathMapping contains the Compose service and modified host system path.
|
||||
@ -38,5 +36,5 @@ type PathMapping struct {
|
||||
}
|
||||
|
||||
type Syncer interface {
|
||||
Sync(ctx context.Context, service types.ServiceConfig, paths []PathMapping) error
|
||||
Sync(ctx context.Context, service string, paths []*PathMapping) error
|
||||
}
|
||||
|
@ -32,8 +32,7 @@ import (
|
||||
|
||||
"github.com/hashicorp/go-multierror"
|
||||
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
moby "github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/pkg/archive"
|
||||
)
|
||||
|
||||
@ -44,7 +43,7 @@ type archiveEntry struct {
|
||||
}
|
||||
|
||||
type LowLevelClient interface {
|
||||
ContainersForService(ctx context.Context, projectName string, serviceName string) ([]moby.Container, error)
|
||||
ContainersForService(ctx context.Context, projectName string, serviceName string) ([]container.Summary, error)
|
||||
|
||||
Exec(ctx context.Context, containerID string, cmd []string, in io.Reader) error
|
||||
Untar(ctx context.Context, id string, reader io.ReadCloser) error
|
||||
@ -65,8 +64,8 @@ func NewTar(projectName string, client LowLevelClient) *Tar {
|
||||
}
|
||||
}
|
||||
|
||||
func (t *Tar) Sync(ctx context.Context, service types.ServiceConfig, paths []PathMapping) error {
|
||||
containers, err := t.client.ContainersForService(ctx, t.projectName, service.Name)
|
||||
func (t *Tar) Sync(ctx context.Context, service string, paths []*PathMapping) error {
|
||||
containers, err := t.client.ContainersForService(ctx, t.projectName, service)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -77,7 +76,7 @@ func (t *Tar) Sync(ctx context.Context, service types.ServiceConfig, paths []Pat
|
||||
if _, err := os.Stat(p.HostPath); err != nil && errors.Is(err, fs.ErrNotExist) {
|
||||
pathsToDelete = append(pathsToDelete, p.ContainerPath)
|
||||
} else {
|
||||
pathsToCopy = append(pathsToCopy, p)
|
||||
pathsToCopy = append(pathsToCopy, *p)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -25,7 +25,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
moby "github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
)
|
||||
@ -140,15 +140,15 @@ func ServiceOptions(service types.ServiceConfig) SpanOptions {
|
||||
// For convenience, it's returned as a SpanOptions object to allow it to be
|
||||
// passed directly to the wrapping helper methods in this package such as
|
||||
// SpanWrapFunc.
|
||||
func ContainerOptions(container moby.Container) SpanOptions {
|
||||
func ContainerOptions(ctr container.Summary) SpanOptions {
|
||||
attrs := []attribute.KeyValue{
|
||||
attribute.String("container.id", container.ID),
|
||||
attribute.String("container.image", container.Image),
|
||||
unixTimeAttr("container.created_at", container.Created),
|
||||
attribute.String("container.id", ctr.ID),
|
||||
attribute.String("container.image", ctr.Image),
|
||||
unixTimeAttr("container.created_at", ctr.Created),
|
||||
}
|
||||
|
||||
if len(container.Names) != 0 {
|
||||
attrs = append(attrs, attribute.String("container.name", strings.TrimPrefix(container.Names[0], "/")))
|
||||
if len(ctr.Names) != 0 {
|
||||
attrs = append(attrs, attribute.String("container.name", strings.TrimPrefix(ctr.Names[0], "/")))
|
||||
}
|
||||
|
||||
return []trace.SpanStartEventOption{
|
||||
|
@ -16,7 +16,5 @@
|
||||
|
||||
package internal
|
||||
|
||||
var (
|
||||
// Version is the version of the CLI injected in compilation time
|
||||
Version = "dev"
|
||||
)
|
||||
// Version is the version of the CLI injected in compilation time
|
||||
var Version = "dev"
|
||||
|
@ -23,6 +23,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
"github.com/docker/cli/opts"
|
||||
"github.com/docker/compose/v2/pkg/utils"
|
||||
)
|
||||
|
||||
@ -92,6 +93,8 @@ type Service interface {
|
||||
Scale(ctx context.Context, project *types.Project, options ScaleOptions) error
|
||||
// Export a service container's filesystem as a tar archive
|
||||
Export(ctx context.Context, projectName string, options ExportOptions) error
|
||||
// Create a new image from a service container's changes
|
||||
Commit(ctx context.Context, projectName string, options CommitOptions) error
|
||||
// Generate generates a Compose Project from existing containers
|
||||
Generate(ctx context.Context, options GenerateOptions) (*types.Project, error)
|
||||
}
|
||||
@ -152,6 +155,8 @@ type BuildOptions struct {
|
||||
Memory int64
|
||||
// Builder name passed in the command line
|
||||
Builder string
|
||||
// Print don't actually run builder but print equivalent build config
|
||||
Print bool
|
||||
}
|
||||
|
||||
// Apply mutates project according to build options
|
||||
@ -204,6 +209,8 @@ type CreateOptions struct {
|
||||
Timeout *time.Duration
|
||||
// QuietPull makes the pulling process quiet
|
||||
QuietPull bool
|
||||
// AssumeYes assume "yes" as answer to all prompts and run non-interactively
|
||||
AssumeYes bool
|
||||
}
|
||||
|
||||
// StartOptions group options of the Start API
|
||||
@ -415,6 +422,8 @@ const (
|
||||
// PublishOptions group options of the Publish API
|
||||
type PublishOptions struct {
|
||||
ResolveImageDigests bool
|
||||
WithEnvironment bool
|
||||
AssumeYes bool
|
||||
|
||||
OCIVersion OCIVersion
|
||||
}
|
||||
@ -426,7 +435,6 @@ func (e Event) String() string {
|
||||
attr = append(attr, fmt.Sprintf("%s=%s", k, v))
|
||||
}
|
||||
return fmt.Sprintf("%s container %s %s (%s)\n", t, e.Status, e.Container, strings.Join(attr, ", "))
|
||||
|
||||
}
|
||||
|
||||
// ListOptions group options of the ls API
|
||||
@ -517,6 +525,8 @@ type ContainerProcSummary struct {
|
||||
Name string
|
||||
Processes [][]string
|
||||
Titles []string
|
||||
Service string
|
||||
Replica string
|
||||
}
|
||||
|
||||
// ImageSummary holds container image description
|
||||
@ -526,6 +536,7 @@ type ImageSummary struct {
|
||||
Repository string
|
||||
Tag string
|
||||
Size int64
|
||||
LastTagTime time.Time
|
||||
}
|
||||
|
||||
// ServiceStatus hold status about a service
|
||||
@ -565,6 +576,19 @@ type ExportOptions struct {
|
||||
Output string
|
||||
}
|
||||
|
||||
// CommitOptions group options of the Commit API
|
||||
type CommitOptions struct {
|
||||
Service string
|
||||
Reference string
|
||||
|
||||
Pause bool
|
||||
Comment string
|
||||
Author string
|
||||
Changes opts.ListOpts
|
||||
|
||||
Index int
|
||||
}
|
||||
|
||||
type GenerateOptions struct {
|
||||
// ProjectName to set in the Compose file
|
||||
ProjectName string
|
||||
|
@ -60,7 +60,7 @@ type DryRunKey struct{}
|
||||
// DryRunClient implements APIClient by delegating to implementation functions. This allows lazy init and per-method overrides
|
||||
type DryRunClient struct {
|
||||
apiClient client.APIClient
|
||||
containers []moby.Container
|
||||
containers []containerType.Summary
|
||||
execs sync.Map
|
||||
resolver *imagetools.Resolver
|
||||
}
|
||||
@ -82,7 +82,7 @@ func NewDryRunClient(apiClient client.APIClient, cli command.Cli) (*DryRunClient
|
||||
}
|
||||
return &DryRunClient{
|
||||
apiClient: apiClient,
|
||||
containers: []moby.Container{},
|
||||
containers: []containerType.Summary{},
|
||||
execs: sync.Map{},
|
||||
resolver: imagetools.New(configFile),
|
||||
}, nil
|
||||
@ -101,8 +101,9 @@ func (d *DryRunClient) ContainerAttach(ctx context.Context, container string, op
|
||||
}
|
||||
|
||||
func (d *DryRunClient) ContainerCreate(ctx context.Context, config *containerType.Config, hostConfig *containerType.HostConfig,
|
||||
networkingConfig *network.NetworkingConfig, platform *specs.Platform, containerName string) (containerType.CreateResponse, error) {
|
||||
d.containers = append(d.containers, moby.Container{
|
||||
networkingConfig *network.NetworkingConfig, platform *specs.Platform, containerName string,
|
||||
) (containerType.CreateResponse, error) {
|
||||
d.containers = append(d.containers, containerType.Summary{
|
||||
ID: containerName,
|
||||
Names: []string{containerName},
|
||||
Labels: config.Labels,
|
||||
@ -114,7 +115,7 @@ func (d *DryRunClient) ContainerCreate(ctx context.Context, config *containerTyp
|
||||
return containerType.CreateResponse{ID: containerName}, nil
|
||||
}
|
||||
|
||||
func (d *DryRunClient) ContainerInspect(ctx context.Context, container string) (moby.ContainerJSON, error) {
|
||||
func (d *DryRunClient) ContainerInspect(ctx context.Context, container string) (containerType.InspectResponse, error) {
|
||||
containerJSON, err := d.apiClient.ContainerInspect(ctx, container)
|
||||
if err != nil {
|
||||
id := "dryRunId"
|
||||
@ -123,20 +124,20 @@ func (d *DryRunClient) ContainerInspect(ctx context.Context, container string) (
|
||||
id = container
|
||||
}
|
||||
}
|
||||
return moby.ContainerJSON{
|
||||
ContainerJSONBase: &moby.ContainerJSONBase{
|
||||
return containerType.InspectResponse{
|
||||
ContainerJSONBase: &containerType.ContainerJSONBase{
|
||||
ID: id,
|
||||
Name: container,
|
||||
State: &moby.ContainerState{
|
||||
State: &containerType.State{
|
||||
Status: "running", // needed for --wait option
|
||||
Health: &moby.Health{
|
||||
Status: moby.Healthy, // needed for healthcheck control
|
||||
Health: &containerType.Health{
|
||||
Status: containerType.Healthy, // needed for healthcheck control
|
||||
},
|
||||
},
|
||||
},
|
||||
Mounts: nil,
|
||||
Config: &containerType.Config{},
|
||||
NetworkSettings: &moby.NetworkSettings{},
|
||||
NetworkSettings: &containerType.NetworkSettings{},
|
||||
}, nil
|
||||
}
|
||||
return containerJSON, err
|
||||
@ -146,7 +147,7 @@ func (d *DryRunClient) ContainerKill(ctx context.Context, container, signal stri
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *DryRunClient) ContainerList(ctx context.Context, options containerType.ListOptions) ([]moby.Container, error) {
|
||||
func (d *DryRunClient) ContainerList(ctx context.Context, options containerType.ListOptions) ([]containerType.Summary, error) {
|
||||
caller := getCallingFunction()
|
||||
switch caller {
|
||||
case "start":
|
||||
@ -221,15 +222,24 @@ func (d *DryRunClient) ImageBuild(ctx context.Context, reader io.Reader, options
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (d *DryRunClient) ImageInspectWithRaw(ctx context.Context, imageName string) (moby.ImageInspect, []byte, error) {
|
||||
func (d *DryRunClient) ImageInspect(ctx context.Context, imageName string, options ...client.ImageInspectOption) (image.InspectResponse, error) {
|
||||
caller := getCallingFunction()
|
||||
switch caller {
|
||||
case "pullServiceImage", "buildContainerVolumes":
|
||||
return moby.ImageInspect{ID: "dryRunId"}, nil, nil
|
||||
return image.InspectResponse{ID: "dryRunId"}, nil
|
||||
default:
|
||||
return d.apiClient.ImageInspectWithRaw(ctx, imageName)
|
||||
return d.apiClient.ImageInspect(ctx, imageName, options...)
|
||||
}
|
||||
}
|
||||
|
||||
// Deprecated: Use [DryRunClient.ImageInspect] instead; raw response can be obtained by [client.ImageInspectWithRawResponse] option.
|
||||
func (d *DryRunClient) ImageInspectWithRaw(ctx context.Context, imageName string) (image.InspectResponse, []byte, error) {
|
||||
var buf bytes.Buffer
|
||||
resp, err := d.ImageInspect(ctx, imageName, client.ImageInspectWithRawResponse(&buf))
|
||||
if err != nil {
|
||||
return image.InspectResponse{}, nil, err
|
||||
}
|
||||
return resp, buf.Bytes(), err
|
||||
}
|
||||
|
||||
func (d *DryRunClient) ImagePull(ctx context.Context, ref string, options image.PullOptions) (io.ReadCloser, error) {
|
||||
@ -299,7 +309,7 @@ func (d *DryRunClient) VolumeRemove(ctx context.Context, volumeID string, force
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *DryRunClient) ContainerExecCreate(ctx context.Context, container string, config containerType.ExecOptions) (moby.IDResponse, error) {
|
||||
func (d *DryRunClient) ContainerExecCreate(ctx context.Context, container string, config containerType.ExecOptions) (containerType.ExecCreateResponse, error) {
|
||||
b := make([]byte, 32)
|
||||
_, _ = rand.Read(b)
|
||||
id := fmt.Sprintf("%x", b)
|
||||
@ -307,7 +317,7 @@ func (d *DryRunClient) ContainerExecCreate(ctx context.Context, container string
|
||||
container: container,
|
||||
command: config.Cmd,
|
||||
})
|
||||
return moby.IDResponse{
|
||||
return containerType.ExecCreateResponse{
|
||||
ID: id,
|
||||
}, nil
|
||||
}
|
||||
@ -344,7 +354,7 @@ func (d *DryRunClient) ConfigUpdate(ctx context.Context, id string, version swar
|
||||
return d.apiClient.ConfigUpdate(ctx, id, version, config)
|
||||
}
|
||||
|
||||
func (d *DryRunClient) ContainerCommit(ctx context.Context, container string, options containerType.CommitOptions) (moby.IDResponse, error) {
|
||||
func (d *DryRunClient) ContainerCommit(ctx context.Context, container string, options containerType.CommitOptions) (containerType.CommitResponse, error) {
|
||||
return d.apiClient.ContainerCommit(ctx, container, options)
|
||||
}
|
||||
|
||||
@ -368,7 +378,7 @@ func (d *DryRunClient) ContainerExport(ctx context.Context, container string) (i
|
||||
return d.apiClient.ContainerExport(ctx, container)
|
||||
}
|
||||
|
||||
func (d *DryRunClient) ContainerInspectWithRaw(ctx context.Context, container string, getSize bool) (moby.ContainerJSON, []byte, error) {
|
||||
func (d *DryRunClient) ContainerInspectWithRaw(ctx context.Context, container string, getSize bool) (containerType.InspectResponse, []byte, error) {
|
||||
return d.apiClient.ContainerInspectWithRaw(ctx, container, getSize)
|
||||
}
|
||||
|
||||
@ -392,11 +402,11 @@ func (d *DryRunClient) ContainerStatsOneShot(ctx context.Context, container stri
|
||||
return d.apiClient.ContainerStatsOneShot(ctx, container)
|
||||
}
|
||||
|
||||
func (d *DryRunClient) ContainerTop(ctx context.Context, container string, arguments []string) (containerType.ContainerTopOKBody, error) {
|
||||
func (d *DryRunClient) ContainerTop(ctx context.Context, container string, arguments []string) (containerType.TopResponse, error) {
|
||||
return d.apiClient.ContainerTop(ctx, container, arguments)
|
||||
}
|
||||
|
||||
func (d *DryRunClient) ContainerUpdate(ctx context.Context, container string, updateConfig containerType.UpdateConfig) (containerType.ContainerUpdateOKBody, error) {
|
||||
func (d *DryRunClient) ContainerUpdate(ctx context.Context, container string, updateConfig containerType.UpdateConfig) (containerType.UpdateResponse, error) {
|
||||
return d.apiClient.ContainerUpdate(ctx, container, updateConfig)
|
||||
}
|
||||
|
||||
@ -424,8 +434,8 @@ func (d *DryRunClient) ImageCreate(ctx context.Context, parentReference string,
|
||||
return d.apiClient.ImageCreate(ctx, parentReference, options)
|
||||
}
|
||||
|
||||
func (d *DryRunClient) ImageHistory(ctx context.Context, imageName string) ([]image.HistoryResponseItem, error) {
|
||||
return d.apiClient.ImageHistory(ctx, imageName)
|
||||
func (d *DryRunClient) ImageHistory(ctx context.Context, imageName string, options ...client.ImageHistoryOption) ([]image.HistoryResponseItem, error) {
|
||||
return d.apiClient.ImageHistory(ctx, imageName, options...)
|
||||
}
|
||||
|
||||
func (d *DryRunClient) ImageImport(ctx context.Context, source image.ImportSource, ref string, options image.ImportOptions) (io.ReadCloser, error) {
|
||||
@ -436,16 +446,16 @@ func (d *DryRunClient) ImageList(ctx context.Context, options image.ListOptions)
|
||||
return d.apiClient.ImageList(ctx, options)
|
||||
}
|
||||
|
||||
func (d *DryRunClient) ImageLoad(ctx context.Context, input io.Reader, quiet bool) (image.LoadResponse, error) {
|
||||
return d.apiClient.ImageLoad(ctx, input, quiet)
|
||||
func (d *DryRunClient) ImageLoad(ctx context.Context, input io.Reader, options ...client.ImageLoadOption) (image.LoadResponse, error) {
|
||||
return d.apiClient.ImageLoad(ctx, input, options...)
|
||||
}
|
||||
|
||||
func (d *DryRunClient) ImageSearch(ctx context.Context, term string, options registry.SearchOptions) ([]registry.SearchResult, error) {
|
||||
return d.apiClient.ImageSearch(ctx, term, options)
|
||||
}
|
||||
|
||||
func (d *DryRunClient) ImageSave(ctx context.Context, images []string) (io.ReadCloser, error) {
|
||||
return d.apiClient.ImageSave(ctx, images)
|
||||
func (d *DryRunClient) ImageSave(ctx context.Context, images []string, options ...client.ImageSaveOption) (io.ReadCloser, error) {
|
||||
return d.apiClient.ImageSave(ctx, images, options...)
|
||||
}
|
||||
|
||||
func (d *DryRunClient) ImageTag(ctx context.Context, imageName, ref string) error {
|
||||
|
@ -25,7 +25,6 @@ import (
|
||||
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
"github.com/docker/cli/cli/streams"
|
||||
moby "github.com/docker/docker/api/types"
|
||||
containerType "github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/pkg/stdcopy"
|
||||
"github.com/moby/term"
|
||||
@ -52,8 +51,8 @@ func (s *composeService) attach(ctx context.Context, project *types.Project, lis
|
||||
|
||||
_, _ = fmt.Fprintf(s.stdout(), "Attaching to %s\n", strings.Join(names, ", "))
|
||||
|
||||
for _, container := range containers {
|
||||
err := s.attachContainer(ctx, container, listener)
|
||||
for _, ctr := range containers {
|
||||
err := s.attachContainer(ctx, ctr, listener)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -61,7 +60,7 @@ func (s *composeService) attach(ctx context.Context, project *types.Project, lis
|
||||
return containers, err
|
||||
}
|
||||
|
||||
func (s *composeService) attachContainer(ctx context.Context, container moby.Container, listener api.ContainerEventListener) error {
|
||||
func (s *composeService) attachContainer(ctx context.Context, container containerType.Summary, listener api.ContainerEventListener) error {
|
||||
serviceName := container.Labels[api.ServiceLabel]
|
||||
containerName := getContainerNameWithoutProject(container)
|
||||
|
||||
|
@ -21,7 +21,9 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
"github.com/containerd/platforms"
|
||||
@ -32,13 +34,13 @@ import (
|
||||
"github.com/docker/buildx/util/buildflags"
|
||||
xprogress "github.com/docker/buildx/util/progress"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/cli/cli/hints"
|
||||
cliopts "github.com/docker/cli/opts"
|
||||
"github.com/docker/compose/v2/internal/tracing"
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
"github.com/docker/compose/v2/pkg/progress"
|
||||
"github.com/docker/compose/v2/pkg/utils"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/builder/remotecontext/urlutil"
|
||||
bclient "github.com/moby/buildkit/client"
|
||||
"github.com/moby/buildkit/session"
|
||||
"github.com/moby/buildkit/session/auth/authprovider"
|
||||
@ -48,6 +50,8 @@ import (
|
||||
"github.com/moby/buildkit/util/progress/progressui"
|
||||
specs "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/sirupsen/logrus"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
|
||||
// required to get default driver registered
|
||||
_ "github.com/docker/buildx/driver/docker"
|
||||
@ -59,31 +63,29 @@ func (s *composeService) Build(ctx context.Context, project *types.Project, opti
|
||||
return err
|
||||
}
|
||||
return progress.RunWithTitle(ctx, func(ctx context.Context) error {
|
||||
_, err := s.build(ctx, project, options, nil)
|
||||
return err
|
||||
return tracing.SpanWrapFunc("project/build", tracing.ProjectOptions(ctx, project),
|
||||
func(ctx context.Context) error {
|
||||
_, err := s.build(ctx, project, options, nil)
|
||||
return err
|
||||
})(ctx)
|
||||
}, s.stdinfo(), "Building")
|
||||
}
|
||||
|
||||
type serviceToBuild struct {
|
||||
name string
|
||||
service types.ServiceConfig
|
||||
}
|
||||
const bakeSuggest = "Compose can now delegate builds to bake for better performance.\n To do so, set COMPOSE_BAKE=true."
|
||||
|
||||
var suggest sync.Once
|
||||
|
||||
//nolint:gocyclo
|
||||
func (s *composeService) build(ctx context.Context, project *types.Project, options api.BuildOptions, localImages map[string]string) (map[string]string, error) {
|
||||
buildkitEnabled, err := s.dockerCli.BuildKitEnabled()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func (s *composeService) build(ctx context.Context, project *types.Project, options api.BuildOptions, localImages map[string]api.ImageSummary) (map[string]string, error) {
|
||||
imageIDs := map[string]string{}
|
||||
serviceToBeBuild := map[string]serviceToBuild{}
|
||||
serviceToBuild := types.Services{}
|
||||
|
||||
var policy types.DependencyOption = types.IgnoreDependencies
|
||||
if options.Deps {
|
||||
policy = types.IncludeDependencies
|
||||
}
|
||||
err = project.ForEachService(options.Services, func(serviceName string, service *types.ServiceConfig) error {
|
||||
|
||||
err := project.ForEachService(options.Services, func(serviceName string, service *types.ServiceConfig) error {
|
||||
if service.Build == nil {
|
||||
return nil
|
||||
}
|
||||
@ -92,20 +94,58 @@ func (s *composeService) build(ctx context.Context, project *types.Project, opti
|
||||
if localImagePresent && service.PullPolicy != types.PullPolicyBuild {
|
||||
return nil
|
||||
}
|
||||
serviceToBeBuild[serviceName] = serviceToBuild{name: serviceName, service: *service}
|
||||
serviceToBuild[serviceName] = *service
|
||||
return nil
|
||||
}, policy)
|
||||
if err != nil || len(serviceToBeBuild) == 0 {
|
||||
if err != nil || len(serviceToBuild) == 0 {
|
||||
return imageIDs, err
|
||||
}
|
||||
|
||||
bake, err := buildWithBake(s.dockerCli)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if bake || options.Print {
|
||||
trace.SpanFromContext(ctx).SetAttributes(attribute.String("builder", "bake"))
|
||||
return s.doBuildBake(ctx, project, serviceToBuild, options)
|
||||
}
|
||||
|
||||
// Not using bake, additional_context: service:xx is implemented by building images in dependency order
|
||||
project, err = project.WithServicesTransform(func(serviceName string, service types.ServiceConfig) (types.ServiceConfig, error) {
|
||||
if service.Build != nil {
|
||||
for _, c := range service.Build.AdditionalContexts {
|
||||
if t, found := strings.CutPrefix(c, types.ServicePrefix); found {
|
||||
if service.DependsOn == nil {
|
||||
service.DependsOn = map[string]types.ServiceDependency{}
|
||||
}
|
||||
service.DependsOn[t] = types.ServiceDependency{
|
||||
Condition: "build", // non-canonical, but will force dependency graph ordering
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return service, nil
|
||||
})
|
||||
if err != nil {
|
||||
return imageIDs, err
|
||||
}
|
||||
|
||||
// Initialize buildkit nodes
|
||||
buildkitEnabled, err := s.dockerCli.BuildKitEnabled()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var (
|
||||
b *builder.Builder
|
||||
nodes []builder.Node
|
||||
w *xprogress.Printer
|
||||
)
|
||||
if buildkitEnabled {
|
||||
if hints.Enabled() && progress.Mode != progress.ModeQuiet && progress.Mode != progress.ModeJSON {
|
||||
suggest.Do(func() {
|
||||
fmt.Fprintln(s.dockerCli.Out(), bakeSuggest) //nolint:errcheck
|
||||
})
|
||||
}
|
||||
builderName := options.Builder
|
||||
if builderName == "" {
|
||||
builderName = os.Getenv("BUILDX_BUILDER")
|
||||
@ -129,12 +169,14 @@ func (s *composeService) build(ctx context.Context, project *types.Project, opti
|
||||
if options.Quiet {
|
||||
options.Progress = progress.ModeQuiet
|
||||
}
|
||||
if options.Progress == "" {
|
||||
options.Progress = os.Getenv("BUILDKIT_PROGRESS")
|
||||
}
|
||||
w, err = xprogress.NewPrinter(progressCtx, os.Stdout, progressui.DisplayMode(options.Progress),
|
||||
xprogress.WithDesc(
|
||||
fmt.Sprintf("building with %q instance using %s driver", b.Name, b.Driver),
|
||||
fmt.Sprintf("%s:%s", b.Driver, b.Name),
|
||||
))
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -151,18 +193,23 @@ func (s *composeService) build(ctx context.Context, project *types.Project, opti
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
cw := progress.ContextWriter(ctx)
|
||||
err = InDependencyOrder(ctx, project, func(ctx context.Context, name string) error {
|
||||
serviceToBuild, ok := serviceToBeBuild[name]
|
||||
service, ok := serviceToBuild[name]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
service := serviceToBuild.service
|
||||
serviceName := fmt.Sprintf("Service %s", name)
|
||||
|
||||
if !buildkitEnabled {
|
||||
trace.SpanFromContext(ctx).SetAttributes(attribute.String("builder", "classic"))
|
||||
cw.Event(progress.BuildingEvent(serviceName))
|
||||
id, err := s.doBuildClassic(ctx, project, service, options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cw.Event(progress.BuiltEvent(serviceName))
|
||||
builtDigests[getServiceIndex(name)] = id
|
||||
|
||||
if options.Push {
|
||||
@ -180,6 +227,7 @@ func (s *composeService) build(ctx context.Context, project *types.Project, opti
|
||||
return err
|
||||
}
|
||||
|
||||
trace.SpanFromContext(ctx).SetAttributes(attribute.String("builder", "buildkit"))
|
||||
digest, err := s.doBuildBuildkit(ctx, name, buildOptions, w, nodes)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -204,8 +252,10 @@ func (s *composeService) build(ctx context.Context, project *types.Project, opti
|
||||
|
||||
for i, imageDigest := range builtDigests {
|
||||
if imageDigest != "" {
|
||||
imageRef := api.GetImageNameOrDefault(project.Services[names[i]], project.Name)
|
||||
service := project.Services[names[i]]
|
||||
imageRef := api.GetImageNameOrDefault(service, project.Name)
|
||||
imageIDs[imageRef] = imageDigest
|
||||
cw.Event(progress.BuiltEvent(names[i]))
|
||||
}
|
||||
}
|
||||
return imageIDs, err
|
||||
@ -213,7 +263,7 @@ func (s *composeService) build(ctx context.Context, project *types.Project, opti
|
||||
|
||||
func (s *composeService) ensureImagesExists(ctx context.Context, project *types.Project, buildOpts *api.BuildOptions, quietPull bool) error {
|
||||
for name, service := range project.Services {
|
||||
if service.Image == "" && service.Build == nil {
|
||||
if service.Provider == nil && service.Image == "" && service.Build == nil {
|
||||
return fmt.Errorf("invalid service %q. Must specify either image or build", name)
|
||||
}
|
||||
}
|
||||
@ -241,7 +291,11 @@ func (s *composeService) ensureImagesExists(ctx context.Context, project *types.
|
||||
}
|
||||
|
||||
for name, digest := range builtImages {
|
||||
images[name] = digest
|
||||
images[name] = api.ImageSummary{
|
||||
Repository: name,
|
||||
ID: digest,
|
||||
LastTagTime: time.Now(),
|
||||
}
|
||||
}
|
||||
return nil
|
||||
},
|
||||
@ -254,19 +308,16 @@ func (s *composeService) ensureImagesExists(ctx context.Context, project *types.
|
||||
// set digest as com.docker.compose.image label so we can detect outdated containers
|
||||
for name, service := range project.Services {
|
||||
image := api.GetImageNameOrDefault(service, project.Name)
|
||||
digest, ok := images[image]
|
||||
img, ok := images[image]
|
||||
if ok {
|
||||
if service.Labels == nil {
|
||||
service.Labels = types.Labels{}
|
||||
}
|
||||
service.CustomLabels.Add(api.ImageDigestLabel, digest)
|
||||
service.CustomLabels.Add(api.ImageDigestLabel, img.ID)
|
||||
}
|
||||
project.Services[name] = service
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *composeService) getLocalImagesDigests(ctx context.Context, project *types.Project) (map[string]string, error) {
|
||||
func (s *composeService) getLocalImagesDigests(ctx context.Context, project *types.Project) (map[string]api.ImageSummary, error) {
|
||||
var imageNames []string
|
||||
for _, s := range project.Services {
|
||||
imgName := api.GetImageNameOrDefault(s, project.Name)
|
||||
@ -274,18 +325,14 @@ func (s *composeService) getLocalImagesDigests(ctx context.Context, project *typ
|
||||
imageNames = append(imageNames, imgName)
|
||||
}
|
||||
}
|
||||
imgs, err := s.getImages(ctx, imageNames)
|
||||
imgs, err := s.getImageSummaries(ctx, imageNames)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
images := map[string]string{}
|
||||
for name, info := range imgs {
|
||||
images[name] = info.ID
|
||||
}
|
||||
|
||||
for i, service := range project.Services {
|
||||
imgName := api.GetImageNameOrDefault(service, project.Name)
|
||||
digest, ok := images[imgName]
|
||||
img, ok := imgs[imgName]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
@ -294,7 +341,7 @@ func (s *composeService) getLocalImagesDigests(ctx context.Context, project *typ
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
inspect, _, err := s.apiClient().ImageInspectWithRaw(ctx, digest)
|
||||
inspect, err := s.apiClient().ImageInspect(ctx, img.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -307,15 +354,15 @@ func (s *composeService) getLocalImagesDigests(ctx context.Context, project *typ
|
||||
// there is a local image, but it's for the wrong platform, so
|
||||
// pretend it doesn't exist so that we can pull/build an image
|
||||
// for the correct platform instead
|
||||
delete(images, imgName)
|
||||
delete(imgs, imgName)
|
||||
}
|
||||
}
|
||||
|
||||
project.Services[i].CustomLabels.Add(api.ImageDigestLabel, digest)
|
||||
project.Services[i].CustomLabels.Add(api.ImageDigestLabel, img.ID)
|
||||
|
||||
}
|
||||
|
||||
return images, nil
|
||||
return imgs, nil
|
||||
}
|
||||
|
||||
// resolveAndMergeBuildArgs returns the final set of build arguments to use for the service image build.
|
||||
@ -327,12 +374,7 @@ func (s *composeService) getLocalImagesDigests(ctx context.Context, project *typ
|
||||
//
|
||||
// Finally, standard proxy variables based on the Docker client configuration are added, but will not overwrite
|
||||
// any values if already present.
|
||||
func resolveAndMergeBuildArgs(
|
||||
dockerCli command.Cli,
|
||||
project *types.Project,
|
||||
service types.ServiceConfig,
|
||||
opts api.BuildOptions,
|
||||
) types.MappingWithEquals {
|
||||
func resolveAndMergeBuildArgs(dockerCli command.Cli, project *types.Project, service types.ServiceConfig, opts api.BuildOptions) types.MappingWithEquals {
|
||||
result := make(types.MappingWithEquals).
|
||||
OverrideBy(service.Build.Args).
|
||||
OverrideBy(opts.Args).
|
||||
@ -365,7 +407,9 @@ func (s *composeService) toBuildOptions(project *types.Project, service types.Se
|
||||
}
|
||||
|
||||
sessionConfig := []session.Attachable{
|
||||
authprovider.NewDockerAuthProvider(s.configFile(), nil),
|
||||
authprovider.NewDockerAuthProvider(authprovider.DockerAuthProviderConfig{
|
||||
ConfigFile: s.configFile(),
|
||||
}),
|
||||
}
|
||||
if len(options.SSHs) > 0 || len(service.Build.SSH) > 0 {
|
||||
sshAgentProvider, err := sshAgentProvider(append(service.Build.SSH, options.SSHs...))
|
||||
@ -393,7 +437,7 @@ func (s *composeService) toBuildOptions(project *types.Project, service types.Se
|
||||
return build.Options{}, err
|
||||
}
|
||||
if service.Build.Privileged {
|
||||
allow = append(allow, entitlements.EntitlementSecurityInsecure)
|
||||
allow = append(allow, entitlements.EntitlementSecurityInsecure.String())
|
||||
}
|
||||
|
||||
imageLabels := getImageBuildLabels(project, service)
|
||||
@ -425,10 +469,10 @@ func (s *composeService) toBuildOptions(project *types.Project, service types.Se
|
||||
ContextPath: service.Build.Context,
|
||||
DockerfileInline: service.Build.DockerfileInline,
|
||||
DockerfilePath: dockerFilePath(service.Build.Context, service.Build.Dockerfile),
|
||||
NamedContexts: toBuildContexts(service.Build.AdditionalContexts),
|
||||
NamedContexts: toBuildContexts(service, project),
|
||||
},
|
||||
CacheFrom: pb.CreateCaches(cacheFrom),
|
||||
CacheTo: pb.CreateCaches(cacheTo),
|
||||
CacheFrom: pb.CreateCaches(cacheFrom.ToPB()),
|
||||
CacheTo: pb.CreateCaches(cacheTo.ToPB()),
|
||||
NoCache: service.Build.NoCache,
|
||||
Pull: service.Build.Pull,
|
||||
BuildArgs: flatten(resolveAndMergeBuildArgs(s.dockerCli, project, service, options)),
|
||||
@ -472,16 +516,6 @@ func flatten(in types.MappingWithEquals) types.Mapping {
|
||||
return out
|
||||
}
|
||||
|
||||
func dockerFilePath(ctxName string, dockerfile string) string {
|
||||
if dockerfile == "" {
|
||||
return ""
|
||||
}
|
||||
if urlutil.IsGitURL(ctxName) || filepath.IsAbs(dockerfile) {
|
||||
return dockerfile
|
||||
}
|
||||
return filepath.Join(ctxName, dockerfile)
|
||||
}
|
||||
|
||||
func sshAgentProvider(sshKeys types.SSHConfig) (session.Attachable, error) {
|
||||
sshConfig := make([]sshprovider.AgentConfig, 0, len(sshKeys))
|
||||
for _, sshKey := range sshKeys {
|
||||
@ -540,9 +574,16 @@ func getImageBuildLabels(project *types.Project, service types.ServiceConfig) ty
|
||||
return ret
|
||||
}
|
||||
|
||||
func toBuildContexts(additionalContexts types.Mapping) map[string]build.NamedContext {
|
||||
func toBuildContexts(service types.ServiceConfig, project *types.Project) map[string]build.NamedContext {
|
||||
namedContexts := map[string]build.NamedContext{}
|
||||
for name, contextPath := range additionalContexts {
|
||||
for name, contextPath := range service.Build.AdditionalContexts {
|
||||
if strings.HasPrefix(contextPath, types.ServicePrefix) {
|
||||
// image we depend on has been built previously, as we run in dependency order.
|
||||
// so we convert the service reference into an image reference
|
||||
target := contextPath[len(types.ServicePrefix):]
|
||||
image := api.GetImageNameOrDefault(project.Services[target], project.Name)
|
||||
contextPath = "docker-image://" + image
|
||||
}
|
||||
namedContexts[name] = build.NamedContext{Path: contextPath}
|
||||
}
|
||||
return namedContexts
|
||||
|
407
pkg/compose/build_bake.go
Normal file
407
pkg/compose/build_bake.go
Normal file
@ -0,0 +1,407 @@
|
||||
/*
|
||||
Copyright 2020 Docker Compose CLI authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package compose
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
"github.com/docker/cli/cli-plugins/manager"
|
||||
"github.com/docker/cli/cli-plugins/socket"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
"github.com/docker/compose/v2/pkg/progress"
|
||||
"github.com/docker/docker/api/types/versions"
|
||||
"github.com/docker/docker/builder/remotecontext/urlutil"
|
||||
"github.com/moby/buildkit/client"
|
||||
"github.com/moby/buildkit/util/gitutil"
|
||||
"github.com/moby/buildkit/util/progress/progressui"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/propagation"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
func buildWithBake(dockerCli command.Cli) (bool, error) {
|
||||
b, ok := os.LookupEnv("COMPOSE_BAKE")
|
||||
if !ok {
|
||||
if dockerCli.ConfigFile().Plugins["compose"]["build"] == "bake" {
|
||||
b, ok = "true", true
|
||||
}
|
||||
}
|
||||
if !ok {
|
||||
return false, nil
|
||||
}
|
||||
bake, err := strconv.ParseBool(b)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if !bake {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
enabled, err := dockerCli.BuildKitEnabled()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if !enabled {
|
||||
logrus.Warnf("Docker Compose is configured to build using Bake, but buildkit isn't enabled")
|
||||
}
|
||||
|
||||
_, err = manager.GetPlugin("buildx", dockerCli, &cobra.Command{})
|
||||
if err != nil {
|
||||
if manager.IsNotFound(err) {
|
||||
logrus.Warnf("Docker Compose is configured to build using Bake, but buildx isn't installed")
|
||||
return false, nil
|
||||
}
|
||||
return false, err
|
||||
}
|
||||
return true, err
|
||||
}
|
||||
|
||||
// We _could_ use bake.* types from github.com/docker/buildx but long term plan is to remove buildx as a dependency
|
||||
type bakeConfig struct {
|
||||
Groups map[string]bakeGroup `json:"group"`
|
||||
Targets map[string]bakeTarget `json:"target"`
|
||||
}
|
||||
|
||||
type bakeGroup struct {
|
||||
Targets []string `json:"targets"`
|
||||
}
|
||||
|
||||
type bakeTarget struct {
|
||||
Context string `json:"context,omitempty"`
|
||||
Contexts map[string]string `json:"contexts,omitempty"`
|
||||
Dockerfile string `json:"dockerfile,omitempty"`
|
||||
DockerfileInline string `json:"dockerfile-inline,omitempty"`
|
||||
Args map[string]string `json:"args,omitempty"`
|
||||
Labels map[string]string `json:"labels,omitempty"`
|
||||
Tags []string `json:"tags,omitempty"`
|
||||
CacheFrom []string `json:"cache-from,omitempty"`
|
||||
CacheTo []string `json:"cache-to,omitempty"`
|
||||
Target string `json:"target,omitempty"`
|
||||
Secrets []string `json:"secret,omitempty"`
|
||||
SSH []string `json:"ssh,omitempty"`
|
||||
Platforms []string `json:"platforms,omitempty"`
|
||||
Pull bool `json:"pull,omitempty"`
|
||||
NoCache bool `json:"no-cache,omitempty"`
|
||||
NetworkMode string `json:"network,omitempty"`
|
||||
NoCacheFilter []string `json:"no-cache-filter,omitempty"`
|
||||
ShmSize types.UnitBytes `json:"shm-size,omitempty"`
|
||||
Ulimits []string `json:"ulimits,omitempty"`
|
||||
Call string `json:"call,omitempty"`
|
||||
Entitlements []string `json:"entitlements,omitempty"`
|
||||
Outputs []string `json:"output,omitempty"`
|
||||
}
|
||||
|
||||
type bakeMetadata map[string]buildStatus
|
||||
|
||||
type buildStatus struct {
|
||||
Digest string `json:"containerimage.digest"`
|
||||
}
|
||||
|
||||
func (s *composeService) doBuildBake(ctx context.Context, project *types.Project, serviceToBeBuild types.Services, options api.BuildOptions) (map[string]string, error) { //nolint:gocyclo
|
||||
eg := errgroup.Group{}
|
||||
ch := make(chan *client.SolveStatus)
|
||||
display, err := progressui.NewDisplay(os.Stdout, progressui.DisplayMode(options.Progress))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
eg.Go(func() error {
|
||||
_, err := display.UpdateFrom(ctx, ch)
|
||||
return err
|
||||
})
|
||||
|
||||
cfg := bakeConfig{
|
||||
Groups: map[string]bakeGroup{},
|
||||
Targets: map[string]bakeTarget{},
|
||||
}
|
||||
var group bakeGroup
|
||||
var privileged bool
|
||||
var read []string
|
||||
|
||||
for serviceName, service := range serviceToBeBuild {
|
||||
if service.Build == nil {
|
||||
continue
|
||||
}
|
||||
build := *service.Build
|
||||
|
||||
args := types.Mapping{}
|
||||
for k, v := range resolveAndMergeBuildArgs(s.dockerCli, project, service, options) {
|
||||
if v == nil {
|
||||
continue
|
||||
}
|
||||
args[k] = *v
|
||||
}
|
||||
|
||||
image := api.GetImageNameOrDefault(service, project.Name)
|
||||
|
||||
entitlements := build.Entitlements
|
||||
if slices.Contains(build.Entitlements, "security.insecure") {
|
||||
privileged = true
|
||||
}
|
||||
if build.Privileged {
|
||||
entitlements = append(entitlements, "security.insecure")
|
||||
privileged = true
|
||||
}
|
||||
|
||||
var output string
|
||||
push := options.Push && service.Image != ""
|
||||
if len(service.Build.Platforms) > 1 {
|
||||
output = fmt.Sprintf("type=image,push=%t", push)
|
||||
} else {
|
||||
output = fmt.Sprintf("type=docker,load=true,push=%t", push)
|
||||
}
|
||||
|
||||
read = append(read, build.Context)
|
||||
for _, path := range build.AdditionalContexts {
|
||||
_, err := gitutil.ParseGitRef(path)
|
||||
if !strings.Contains(path, "://") && err != nil {
|
||||
read = append(read, path)
|
||||
}
|
||||
}
|
||||
|
||||
cfg.Targets[serviceName] = bakeTarget{
|
||||
Context: build.Context,
|
||||
Contexts: additionalContexts(build.AdditionalContexts),
|
||||
Dockerfile: dockerFilePath(build.Context, build.Dockerfile),
|
||||
DockerfileInline: strings.ReplaceAll(build.DockerfileInline, "${", "$${"),
|
||||
Args: args,
|
||||
Labels: build.Labels,
|
||||
Tags: append(build.Tags, image),
|
||||
|
||||
CacheFrom: build.CacheFrom,
|
||||
// CacheTo: TODO
|
||||
Platforms: build.Platforms,
|
||||
Target: build.Target,
|
||||
Secrets: toBakeSecrets(project, build.Secrets),
|
||||
SSH: toBakeSSH(append(build.SSH, options.SSHs...)),
|
||||
Pull: options.Pull,
|
||||
NoCache: options.NoCache,
|
||||
ShmSize: build.ShmSize,
|
||||
Ulimits: toBakeUlimits(build.Ulimits),
|
||||
Entitlements: entitlements,
|
||||
Outputs: []string{output},
|
||||
}
|
||||
group.Targets = append(group.Targets, serviceName)
|
||||
}
|
||||
|
||||
cfg.Groups["default"] = group
|
||||
|
||||
b, err := json.MarshalIndent(cfg, "", " ")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if options.Print {
|
||||
_, err = fmt.Fprintln(s.stdinfo(), string(b))
|
||||
return nil, err
|
||||
}
|
||||
logrus.Debugf("bake build config:\n%s", string(b))
|
||||
|
||||
metadata, err := os.CreateTemp(os.TempDir(), "compose")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
buildx, err := manager.GetPlugin("buildx", s.dockerCli, &cobra.Command{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
args := []string{"bake", "--file", "-", "--progress", "rawjson", "--metadata-file", metadata.Name()}
|
||||
mustAllow := buildx.Version != "" && versions.GreaterThanOrEqualTo(buildx.Version[1:], "0.17.0")
|
||||
if mustAllow {
|
||||
// FIXME we should prompt user about this, but this is a breaking change in UX
|
||||
for _, path := range read {
|
||||
args = append(args, "--allow", "fs.read="+path)
|
||||
}
|
||||
if privileged {
|
||||
args = append(args, "--allow", "security.insecure")
|
||||
}
|
||||
}
|
||||
|
||||
if options.Builder != "" {
|
||||
args = append(args, "--builder", options.Builder)
|
||||
}
|
||||
|
||||
logrus.Debugf("Executing bake with args: %v", args)
|
||||
|
||||
cmd := exec.CommandContext(ctx, buildx.Path, args...)
|
||||
// Remove DOCKER_CLI_PLUGIN... variable so buildx can detect it run standalone
|
||||
cmd.Env = filter(os.Environ(), manager.ReexecEnvvar)
|
||||
|
||||
// Use docker/cli mechanism to propagate termination signal to child process
|
||||
server, err := socket.NewPluginServer(nil)
|
||||
if err != nil {
|
||||
defer server.Close() //nolint:errcheck
|
||||
cmd.Cancel = server.Close
|
||||
cmd.Env = replace(cmd.Env, socket.EnvKey, server.Addr().String())
|
||||
}
|
||||
|
||||
cmd.Env = append(cmd.Env, fmt.Sprintf("DOCKER_CONTEXT=%s", s.dockerCli.CurrentContext()))
|
||||
|
||||
// propagate opentelemetry context to child process, see https://github.com/open-telemetry/oteps/blob/main/text/0258-env-context-baggage-carriers.md
|
||||
carrier := propagation.MapCarrier{}
|
||||
otel.GetTextMapPropagator().Inject(ctx, &carrier)
|
||||
cmd.Env = append(cmd.Env, types.Mapping(carrier).Values()...)
|
||||
|
||||
cmd.Stdout = s.stdout()
|
||||
cmd.Stdin = bytes.NewBuffer(b)
|
||||
pipe, err := cmd.StderrPipe()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var errMessage string
|
||||
scanner := bufio.NewScanner(pipe)
|
||||
scanner.Split(bufio.ScanLines)
|
||||
|
||||
err = cmd.Start()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
eg.Go(cmd.Wait)
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
decoder := json.NewDecoder(strings.NewReader(line))
|
||||
var status client.SolveStatus
|
||||
err := decoder.Decode(&status)
|
||||
if err != nil {
|
||||
if strings.HasPrefix(line, "ERROR: ") {
|
||||
errMessage = line[7:]
|
||||
}
|
||||
continue
|
||||
}
|
||||
ch <- &status
|
||||
}
|
||||
close(ch) // stop build progress UI
|
||||
|
||||
err = eg.Wait()
|
||||
if err != nil {
|
||||
if errMessage != "" {
|
||||
return nil, errors.New(errMessage)
|
||||
}
|
||||
return nil, fmt.Errorf("failed to execute bake: %w", err)
|
||||
}
|
||||
|
||||
b, err = os.ReadFile(metadata.Name())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var md bakeMetadata
|
||||
err = json.Unmarshal(b, &md)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cw := progress.ContextWriter(ctx)
|
||||
results := map[string]string{}
|
||||
for name, m := range md {
|
||||
results[name] = m.Digest
|
||||
cw.Event(progress.BuiltEvent(name))
|
||||
}
|
||||
return results, nil
|
||||
}
|
||||
|
||||
func additionalContexts(contexts types.Mapping) map[string]string {
|
||||
ac := map[string]string{}
|
||||
for k, v := range contexts {
|
||||
if target, found := strings.CutPrefix(v, types.ServicePrefix); found {
|
||||
v = "target:" + target
|
||||
}
|
||||
ac[k] = v
|
||||
}
|
||||
return ac
|
||||
}
|
||||
|
||||
func toBakeUlimits(ulimits map[string]*types.UlimitsConfig) []string {
|
||||
s := []string{}
|
||||
for u, l := range ulimits {
|
||||
if l.Single > 0 {
|
||||
s = append(s, fmt.Sprintf("%s=%d", u, l.Single))
|
||||
} else {
|
||||
s = append(s, fmt.Sprintf("%s=%d:%d", u, l.Soft, l.Hard))
|
||||
}
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func toBakeSSH(ssh types.SSHConfig) []string {
|
||||
var s []string
|
||||
for _, key := range ssh {
|
||||
s = append(s, fmt.Sprintf("%s=%s", key.ID, key.Path))
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func toBakeSecrets(project *types.Project, secrets []types.ServiceSecretConfig) []string {
|
||||
var s []string
|
||||
for _, ref := range secrets {
|
||||
def := project.Secrets[ref.Source]
|
||||
target := ref.Target
|
||||
if target == "" {
|
||||
target = ref.Source
|
||||
}
|
||||
switch {
|
||||
case def.Environment != "":
|
||||
s = append(s, fmt.Sprintf("id=%s,type=env,env=%s", target, def.Environment))
|
||||
case def.File != "":
|
||||
s = append(s, fmt.Sprintf("id=%s,type=file,src=%s", target, def.File))
|
||||
}
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func filter(environ []string, variable string) []string {
|
||||
prefix := variable + "="
|
||||
filtered := make([]string, 0, len(environ))
|
||||
for _, val := range environ {
|
||||
if !strings.HasPrefix(val, prefix) {
|
||||
filtered = append(filtered, val)
|
||||
}
|
||||
}
|
||||
return filtered
|
||||
}
|
||||
|
||||
func replace(environ []string, variable, value string) []string {
|
||||
filtered := filter(environ, variable)
|
||||
return append(filtered, fmt.Sprintf("%s=%s", variable, value))
|
||||
}
|
||||
|
||||
func dockerFilePath(ctxName string, dockerfile string) string {
|
||||
if dockerfile == "" {
|
||||
return ""
|
||||
}
|
||||
if urlutil.IsGitURL(ctxName) || filepath.IsAbs(dockerfile) {
|
||||
return dockerfile
|
||||
}
|
||||
return filepath.Join(ctxName, dockerfile)
|
||||
}
|
@ -45,10 +45,10 @@ func (s *composeService) doBuildBuildkit(ctx context.Context, service string, op
|
||||
response, err = build.Build(ctx, nodes,
|
||||
map[string]build.Options{service: opts},
|
||||
dockerutil.NewClient(s.dockerCli),
|
||||
confutil.ConfigDir(s.dockerCli),
|
||||
confutil.NewConfig(s.dockerCli),
|
||||
buildx.WithPrefix(p, service, true))
|
||||
if err != nil {
|
||||
return "", WrapCategorisedComposeError(err, BuildFailure)
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
@ -70,11 +70,6 @@ func (s composeService) dryRunBuildResponse(ctx context.Context, name string, op
|
||||
w := progress.ContextWriter(ctx)
|
||||
buildResponse := map[string]*client.SolveResponse{}
|
||||
dryRunUUID := fmt.Sprintf("dryRun-%x", sha1.Sum([]byte(name)))
|
||||
w.Event(progress.Event{
|
||||
ID: " ",
|
||||
Status: progress.Done,
|
||||
Text: fmt.Sprintf("build service %s", name),
|
||||
})
|
||||
w.Event(progress.Event{
|
||||
ID: "==>",
|
||||
Status: progress.Done,
|
||||
|
87
pkg/compose/commit.go
Normal file
87
pkg/compose/commit.go
Normal file
@ -0,0 +1,87 @@
|
||||
/*
|
||||
Copyright 2020 Docker Compose CLI authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package compose
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
"github.com/docker/compose/v2/pkg/progress"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
)
|
||||
|
||||
func (s *composeService) Commit(ctx context.Context, projectName string, options api.CommitOptions) error {
|
||||
return progress.RunWithTitle(ctx, func(ctx context.Context) error {
|
||||
return s.commit(ctx, projectName, options)
|
||||
}, s.stdinfo(), "Committing")
|
||||
}
|
||||
|
||||
func (s *composeService) commit(ctx context.Context, projectName string, options api.CommitOptions) error {
|
||||
projectName = strings.ToLower(projectName)
|
||||
|
||||
ctr, err := s.getSpecifiedContainer(ctx, projectName, oneOffInclude, false, options.Service, options.Index)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
clnt := s.dockerCli.Client()
|
||||
|
||||
w := progress.ContextWriter(ctx)
|
||||
|
||||
name := getCanonicalContainerName(ctr)
|
||||
msg := fmt.Sprintf("Commit %s", name)
|
||||
|
||||
w.Event(progress.Event{
|
||||
ID: name,
|
||||
Text: msg,
|
||||
Status: progress.Working,
|
||||
StatusText: "Committing",
|
||||
})
|
||||
|
||||
if s.dryRun {
|
||||
w.Event(progress.Event{
|
||||
ID: name,
|
||||
Text: msg,
|
||||
Status: progress.Done,
|
||||
StatusText: "Committed",
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
response, err := clnt.ContainerCommit(ctx, ctr.ID, container.CommitOptions{
|
||||
Reference: options.Reference,
|
||||
Comment: options.Comment,
|
||||
Author: options.Author,
|
||||
Changes: options.Changes.GetAll(),
|
||||
Pause: options.Pause,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
w.Event(progress.Event{
|
||||
ID: name,
|
||||
Text: msg,
|
||||
Status: progress.Done,
|
||||
StatusText: fmt.Sprintf("Committed as %s", response.ID),
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
@ -25,22 +25,22 @@ import (
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/docker/compose/v2/internal/desktop"
|
||||
"github.com/docker/compose/v2/internal/experimental"
|
||||
"github.com/docker/docker/api/types/network"
|
||||
"github.com/docker/docker/api/types/volume"
|
||||
"github.com/jonboulle/clockwork"
|
||||
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/cli/cli/config/configfile"
|
||||
"github.com/docker/cli/cli/flags"
|
||||
"github.com/docker/cli/cli/streams"
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
moby "github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
"github.com/docker/docker/api/types/network"
|
||||
"github.com/docker/docker/api/types/swarm"
|
||||
"github.com/docker/docker/api/types/volume"
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/jonboulle/clockwork"
|
||||
|
||||
"github.com/docker/compose/v2/internal/desktop"
|
||||
"github.com/docker/compose/v2/internal/experimental"
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
)
|
||||
|
||||
var stdioToStdout bool
|
||||
@ -139,7 +139,7 @@ func (s *composeService) stdinfo() *streams.Out {
|
||||
return s.dockerCli.Err()
|
||||
}
|
||||
|
||||
func getCanonicalContainerName(c moby.Container) string {
|
||||
func getCanonicalContainerName(c container.Summary) string {
|
||||
if len(c.Names) == 0 {
|
||||
// corner case, sometime happens on removal. return short ID as a safeguard value
|
||||
return c.ID[:12]
|
||||
@ -154,7 +154,7 @@ func getCanonicalContainerName(c moby.Container) string {
|
||||
return strings.TrimPrefix(c.Names[0], "/")
|
||||
}
|
||||
|
||||
func getContainerNameWithoutProject(c moby.Container) string {
|
||||
func getContainerNameWithoutProject(c container.Summary) string {
|
||||
project := c.Labels[api.ProjectLabel]
|
||||
defaultName := getDefaultContainerName(project, c.Labels[api.ServiceLabel], c.Labels[api.ContainerNumberLabel])
|
||||
name := getCanonicalContainerName(c)
|
||||
@ -187,7 +187,6 @@ func (s *composeService) projectFromName(containers Containers, projectName stri
|
||||
Image: c.Image,
|
||||
Labels: c.Labels,
|
||||
}
|
||||
|
||||
}
|
||||
service.Scale = increment(service.Scale)
|
||||
set[serviceLabel] = service
|
||||
@ -321,7 +320,6 @@ func (s *composeService) RuntimeVersion(ctx context.Context) (string, error) {
|
||||
runtimeVersion.val = version.APIVersion
|
||||
})
|
||||
return runtimeVersion.val, runtimeVersion.err
|
||||
|
||||
}
|
||||
|
||||
func (s *composeService) isDesktopIntegrationActive() bool {
|
||||
|
@ -25,13 +25,12 @@ import (
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
"github.com/docker/compose/v2/pkg/utils"
|
||||
moby "github.com/docker/docker/api/types"
|
||||
containerType "github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
)
|
||||
|
||||
// Containers is a set of moby Container
|
||||
type Containers []moby.Container
|
||||
type Containers []container.Summary
|
||||
|
||||
type oneOff int
|
||||
|
||||
@ -44,7 +43,7 @@ const (
|
||||
func (s *composeService) getContainers(ctx context.Context, project string, oneOff oneOff, all bool, selectedServices ...string) (Containers, error) {
|
||||
var containers Containers
|
||||
f := getDefaultFilters(project, oneOff, selectedServices...)
|
||||
containers, err := s.apiClient().ContainerList(ctx, containerType.ListOptions{
|
||||
containers, err := s.apiClient().ContainerList(ctx, container.ListOptions{
|
||||
Filters: filters.NewArgs(f...),
|
||||
All: all,
|
||||
})
|
||||
@ -73,25 +72,25 @@ func getDefaultFilters(projectName string, oneOff oneOff, selectedServices ...st
|
||||
return f
|
||||
}
|
||||
|
||||
func (s *composeService) getSpecifiedContainer(ctx context.Context, projectName string, oneOff oneOff, all bool, serviceName string, containerIndex int) (moby.Container, error) {
|
||||
func (s *composeService) getSpecifiedContainer(ctx context.Context, projectName string, oneOff oneOff, all bool, serviceName string, containerIndex int) (container.Summary, error) {
|
||||
defaultFilters := getDefaultFilters(projectName, oneOff, serviceName)
|
||||
if containerIndex > 0 {
|
||||
defaultFilters = append(defaultFilters, containerNumberFilter(containerIndex))
|
||||
}
|
||||
containers, err := s.apiClient().ContainerList(ctx, containerType.ListOptions{
|
||||
containers, err := s.apiClient().ContainerList(ctx, container.ListOptions{
|
||||
Filters: filters.NewArgs(
|
||||
defaultFilters...,
|
||||
),
|
||||
All: all,
|
||||
})
|
||||
if err != nil {
|
||||
return moby.Container{}, err
|
||||
return container.Summary{}, err
|
||||
}
|
||||
if len(containers) < 1 {
|
||||
if containerIndex > 0 {
|
||||
return moby.Container{}, fmt.Errorf("service %q is not running container #%d", serviceName, containerIndex)
|
||||
return container.Summary{}, fmt.Errorf("service %q is not running container #%d", serviceName, containerIndex)
|
||||
}
|
||||
return moby.Container{}, fmt.Errorf("service %q is not running", serviceName)
|
||||
return container.Summary{}, fmt.Errorf("service %q is not running", serviceName)
|
||||
}
|
||||
|
||||
// Sort by container number first, then put one-off containers at the end
|
||||
@ -107,22 +106,30 @@ func (s *composeService) getSpecifiedContainer(ctx context.Context, projectName
|
||||
|
||||
return numberLabelX < numberLabelY
|
||||
})
|
||||
container := containers[0]
|
||||
return container, nil
|
||||
return containers[0], nil
|
||||
}
|
||||
|
||||
// containerPredicate define a predicate we want container to satisfy for filtering operations
|
||||
type containerPredicate func(c moby.Container) bool
|
||||
type containerPredicate func(c container.Summary) bool
|
||||
|
||||
func matches(c container.Summary, predicates ...containerPredicate) bool {
|
||||
for _, predicate := range predicates {
|
||||
if !predicate(c) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func isService(services ...string) containerPredicate {
|
||||
return func(c moby.Container) bool {
|
||||
return func(c container.Summary) bool {
|
||||
service := c.Labels[api.ServiceLabel]
|
||||
return utils.StringContains(services, service)
|
||||
}
|
||||
}
|
||||
|
||||
func isRunning() containerPredicate {
|
||||
return func(c moby.Container) bool {
|
||||
return func(c container.Summary) bool {
|
||||
return c.State == "running"
|
||||
}
|
||||
}
|
||||
@ -130,7 +137,7 @@ func isRunning() containerPredicate {
|
||||
// isOrphaned is a predicate to select containers without a matching service definition in compose project
|
||||
func isOrphaned(project *types.Project) containerPredicate {
|
||||
services := append(project.ServiceNames(), project.DisabledServiceNames()...)
|
||||
return func(c moby.Container) bool {
|
||||
return func(c container.Summary) bool {
|
||||
// One-off container
|
||||
v, ok := c.Labels[api.OneoffLabel]
|
||||
if ok && v == "True" {
|
||||
@ -142,16 +149,16 @@ func isOrphaned(project *types.Project) containerPredicate {
|
||||
}
|
||||
}
|
||||
|
||||
func isNotOneOff(c moby.Container) bool {
|
||||
func isNotOneOff(c container.Summary) bool {
|
||||
v, ok := c.Labels[api.OneoffLabel]
|
||||
return !ok || v == "False"
|
||||
}
|
||||
|
||||
// filter return Containers with elements to match predicate
|
||||
func (containers Containers) filter(predicate containerPredicate) Containers {
|
||||
func (containers Containers) filter(predicates ...containerPredicate) Containers {
|
||||
var filtered Containers
|
||||
for _, c := range containers {
|
||||
if predicate(c) {
|
||||
if matches(c, predicates...) {
|
||||
filtered = append(filtered, c)
|
||||
}
|
||||
}
|
||||
@ -166,7 +173,7 @@ func (containers Containers) names() []string {
|
||||
return names
|
||||
}
|
||||
|
||||
func (containers Containers) forEach(fn func(moby.Container)) {
|
||||
func (containers Containers) forEach(fn func(container.Summary)) {
|
||||
for _, c := range containers {
|
||||
fn(c)
|
||||
}
|
||||
|
@ -20,25 +20,25 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"slices"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
"github.com/containerd/platforms"
|
||||
"github.com/docker/compose/v2/internal/tracing"
|
||||
moby "github.com/docker/docker/api/types"
|
||||
containerType "github.com/docker/docker/api/types/container"
|
||||
mmount "github.com/docker/docker/api/types/mount"
|
||||
"github.com/docker/docker/api/types/versions"
|
||||
specs "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/sirupsen/logrus"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
"golang.org/x/sync/errgroup"
|
||||
|
||||
"github.com/docker/compose/v2/internal/tracing"
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
"github.com/docker/compose/v2/pkg/progress"
|
||||
"github.com/docker/compose/v2/pkg/utils"
|
||||
@ -47,7 +47,7 @@ import (
|
||||
const (
|
||||
doubledContainerNameWarning = "WARNING: The %q service is using the custom container name %q. " +
|
||||
"Docker requires each container to have a unique name. " +
|
||||
"Remove the custom name to scale the service.\n"
|
||||
"Remove the custom name to scale the service"
|
||||
)
|
||||
|
||||
// convergence manages service's container lifecycle.
|
||||
@ -56,24 +56,26 @@ const (
|
||||
// Cross services dependencies are managed by creating services in expected order and updating `service:xx` reference
|
||||
// when a service has converged, so dependent ones can be managed with resolved containers references.
|
||||
type convergence struct {
|
||||
service *composeService
|
||||
observedState map[string]Containers
|
||||
stateMutex sync.Mutex
|
||||
service *composeService
|
||||
services map[string]Containers
|
||||
networks map[string]string
|
||||
volumes map[string]string
|
||||
stateMutex sync.Mutex
|
||||
}
|
||||
|
||||
func (c *convergence) getObservedState(serviceName string) Containers {
|
||||
c.stateMutex.Lock()
|
||||
defer c.stateMutex.Unlock()
|
||||
return c.observedState[serviceName]
|
||||
return c.services[serviceName]
|
||||
}
|
||||
|
||||
func (c *convergence) setObservedState(serviceName string, containers Containers) {
|
||||
c.stateMutex.Lock()
|
||||
defer c.stateMutex.Unlock()
|
||||
c.observedState[serviceName] = containers
|
||||
c.services[serviceName] = containers
|
||||
}
|
||||
|
||||
func newConvergence(services []string, state Containers, s *composeService) *convergence {
|
||||
func newConvergence(services []string, state Containers, networks map[string]string, volumes map[string]string, s *composeService) *convergence {
|
||||
observedState := map[string]Containers{}
|
||||
for _, s := range services {
|
||||
observedState[s] = Containers{}
|
||||
@ -83,8 +85,10 @@ func newConvergence(services []string, state Containers, s *composeService) *con
|
||||
observedState[service] = append(observedState[service], c)
|
||||
}
|
||||
return &convergence{
|
||||
service: s,
|
||||
observedState: observedState,
|
||||
service: s,
|
||||
services: observedState,
|
||||
networks: networks,
|
||||
volumes: volumes,
|
||||
}
|
||||
}
|
||||
|
||||
@ -106,6 +110,9 @@ func (c *convergence) apply(ctx context.Context, project *types.Project, options
|
||||
}
|
||||
|
||||
func (c *convergence) ensureService(ctx context.Context, project *types.Project, service types.ServiceConfig, recreate string, inherit bool, timeout *time.Duration) error { //nolint:gocyclo
|
||||
if service.Provider != nil {
|
||||
return c.service.runPlugin(ctx, project, service, "up")
|
||||
}
|
||||
expected, err := getScale(service)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -123,11 +130,11 @@ func (c *convergence) ensureService(ctx context.Context, project *types.Project,
|
||||
|
||||
sort.Slice(containers, func(i, j int) bool {
|
||||
// select obsolete containers first, so they get removed as we scale down
|
||||
if obsolete, _ := mustRecreate(service, containers[i], recreate); obsolete {
|
||||
if obsolete, _ := c.mustRecreate(service, containers[i], recreate); obsolete {
|
||||
// i is obsolete, so must be first in the list
|
||||
return true
|
||||
}
|
||||
if obsolete, _ := mustRecreate(service, containers[j], recreate); obsolete {
|
||||
if obsolete, _ := c.mustRecreate(service, containers[j], recreate); obsolete {
|
||||
// j is obsolete, so must be first in the list
|
||||
return false
|
||||
}
|
||||
@ -136,16 +143,18 @@ func (c *convergence) ensureService(ctx context.Context, project *types.Project,
|
||||
ni, erri := strconv.Atoi(containers[i].Labels[api.ContainerNumberLabel])
|
||||
nj, errj := strconv.Atoi(containers[j].Labels[api.ContainerNumberLabel])
|
||||
if erri == nil && errj == nil {
|
||||
return ni < nj
|
||||
return ni > nj
|
||||
}
|
||||
|
||||
// If we don't get a container number (?) just sort by creation date
|
||||
return containers[i].Created < containers[j].Created
|
||||
})
|
||||
|
||||
slices.Reverse(containers)
|
||||
for i, container := range containers {
|
||||
if i >= expected {
|
||||
// Scale Down
|
||||
// As we sorted containers, obsolete ones and/or highest number will be removed
|
||||
container := container
|
||||
traceOpts := append(tracing.ServiceOptions(service), tracing.ContainerOptions(container)...)
|
||||
eg.Go(tracing.SpanWrapFuncForErrGroup(ctx, "service/scale/down", traceOpts, func(ctx context.Context) error {
|
||||
@ -154,7 +163,7 @@ func (c *convergence) ensureService(ctx context.Context, project *types.Project,
|
||||
continue
|
||||
}
|
||||
|
||||
mustRecreate, err := mustRecreate(service, container, recreate)
|
||||
mustRecreate, err := c.mustRecreate(service, container, recreate)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -197,7 +206,6 @@ func (c *convergence) ensureService(ctx context.Context, project *types.Project,
|
||||
// Scale UP
|
||||
number := next + i
|
||||
name := getContainerName(project.Name, service, number)
|
||||
i := i
|
||||
eventOpts := tracing.SpanOptions{trace.WithAttributes(attribute.String("container.name", name))}
|
||||
eg.Go(tracing.EventWrapFuncForErrGroup(ctx, "service/scale/up", eventOpts, func(ctx context.Context) error {
|
||||
opts := createOptions{
|
||||
@ -219,20 +227,28 @@ func (c *convergence) ensureService(ctx context.Context, project *types.Project,
|
||||
}
|
||||
|
||||
func (c *convergence) stopDependentContainers(ctx context.Context, project *types.Project, service types.ServiceConfig) error {
|
||||
w := progress.ContextWriter(ctx)
|
||||
// Stop dependent containers, so they will be restarted after service is re-created
|
||||
dependents := project.GetDependentsForService(service)
|
||||
dependents := project.GetDependentsForService(service, func(dependency types.ServiceDependency) bool {
|
||||
return dependency.Restart
|
||||
})
|
||||
if len(dependents) == 0 {
|
||||
return nil
|
||||
}
|
||||
err := c.service.stop(ctx, project.Name, api.StopOptions{
|
||||
Services: dependents,
|
||||
Project: project,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, name := range dependents {
|
||||
dependents := c.getObservedState(name)
|
||||
err := c.service.stopContainers(ctx, w, &service, dependents, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for i, dependent := range dependents {
|
||||
dependentStates := c.getObservedState(name)
|
||||
for i, dependent := range dependentStates {
|
||||
dependent.State = ContainerExited
|
||||
dependents[i] = dependent
|
||||
dependentStates[i] = dependent
|
||||
}
|
||||
c.setObservedState(name, dependents)
|
||||
c.setObservedState(name, dependentStates)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@ -312,7 +328,7 @@ func (c *convergence) resolveSharedNamespaces(service *types.ServiceConfig) erro
|
||||
return nil
|
||||
}
|
||||
|
||||
func mustRecreate(expected types.ServiceConfig, actual moby.Container, policy string) (bool, error) {
|
||||
func (c *convergence) mustRecreate(expected types.ServiceConfig, actual containerType.Summary, policy string) (bool, error) {
|
||||
if policy == api.RecreateNever {
|
||||
return false, nil
|
||||
}
|
||||
@ -325,7 +341,74 @@ func mustRecreate(expected types.ServiceConfig, actual moby.Container, policy st
|
||||
}
|
||||
configChanged := actual.Labels[api.ConfigHashLabel] != configHash
|
||||
imageUpdated := actual.Labels[api.ImageDigestLabel] != expected.CustomLabels[api.ImageDigestLabel]
|
||||
return configChanged || imageUpdated, nil
|
||||
if configChanged || imageUpdated {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
if c.networks != nil && actual.State == "running" {
|
||||
if checkExpectedNetworks(expected, actual, c.networks) {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
|
||||
if c.volumes != nil {
|
||||
if checkExpectedVolumes(expected, actual, c.volumes) {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func checkExpectedNetworks(expected types.ServiceConfig, actual containerType.Summary, networks map[string]string) bool {
|
||||
// check the networks container is connected to are the expected ones
|
||||
for net := range expected.Networks {
|
||||
id := networks[net]
|
||||
if id == "swarm" {
|
||||
// corner-case : swarm overlay network isn't visible until a container is attached
|
||||
continue
|
||||
}
|
||||
found := false
|
||||
for _, settings := range actual.NetworkSettings.Networks {
|
||||
if settings.NetworkID == id {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
// config is up-to-date but container is not connected to network
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func checkExpectedVolumes(expected types.ServiceConfig, actual containerType.Summary, volumes map[string]string) bool {
|
||||
// check container's volume mounts and search for the expected ones
|
||||
for _, vol := range expected.Volumes {
|
||||
if vol.Type != string(mmount.TypeVolume) {
|
||||
continue
|
||||
}
|
||||
if vol.Source == "" {
|
||||
continue
|
||||
}
|
||||
id := volumes[vol.Source]
|
||||
found := false
|
||||
for _, mount := range actual.Mounts {
|
||||
if mount.Type != mmount.TypeVolume {
|
||||
continue
|
||||
}
|
||||
if mount.Name == id {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
// config is up-to-date but container doesn't have volume mounted
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func getContainerName(projectName string, service types.ServiceConfig, number int) string {
|
||||
@ -340,22 +423,22 @@ func getDefaultContainerName(projectName, serviceName, index string) string {
|
||||
return strings.Join([]string{projectName, serviceName, index}, api.Separator)
|
||||
}
|
||||
|
||||
func getContainerProgressName(container moby.Container) string {
|
||||
return "Container " + getCanonicalContainerName(container)
|
||||
func getContainerProgressName(ctr containerType.Summary) string {
|
||||
return "Container " + getCanonicalContainerName(ctr)
|
||||
}
|
||||
|
||||
func containerEvents(containers Containers, eventFunc func(string) progress.Event) []progress.Event {
|
||||
events := []progress.Event{}
|
||||
for _, container := range containers {
|
||||
events = append(events, eventFunc(getContainerProgressName(container)))
|
||||
for _, ctr := range containers {
|
||||
events = append(events, eventFunc(getContainerProgressName(ctr)))
|
||||
}
|
||||
return events
|
||||
}
|
||||
|
||||
func containerReasonEvents(containers Containers, eventFunc func(string, string) progress.Event, reason string) []progress.Event {
|
||||
events := []progress.Event{}
|
||||
for _, container := range containers {
|
||||
events = append(events, eventFunc(getContainerProgressName(container), reason))
|
||||
for _, ctr := range containers {
|
||||
events = append(events, eventFunc(getContainerProgressName(ctr), reason))
|
||||
}
|
||||
return events
|
||||
}
|
||||
@ -379,7 +462,7 @@ func (s *composeService) waitDependencies(ctx context.Context, project *types.Pr
|
||||
continue
|
||||
}
|
||||
|
||||
waitingFor := containers.filter(isService(dep))
|
||||
waitingFor := containers.filter(isService(dep), isNotOneOff)
|
||||
w.Events(containerEvents(waitingFor, progress.Waiting))
|
||||
if len(waitingFor) == 0 {
|
||||
if config.Required {
|
||||
@ -389,7 +472,6 @@ func (s *composeService) waitDependencies(ctx context.Context, project *types.Pr
|
||||
continue
|
||||
}
|
||||
|
||||
dep, config := dep, config
|
||||
eg.Go(func() error {
|
||||
ticker := time.NewTicker(500 * time.Millisecond)
|
||||
defer ticker.Stop()
|
||||
@ -486,7 +568,7 @@ func shouldWaitForDependency(serviceName string, dependencyConfig types.ServiceD
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func nextContainerNumber(containers []moby.Container) int {
|
||||
func nextContainerNumber(containers []containerType.Summary) int {
|
||||
maxNumber := 0
|
||||
for _, c := range containers {
|
||||
s, ok := c.Labels[api.ContainerNumberLabel]
|
||||
@ -503,15 +585,15 @@ func nextContainerNumber(containers []moby.Container) int {
|
||||
}
|
||||
}
|
||||
return maxNumber + 1
|
||||
|
||||
}
|
||||
|
||||
func (s *composeService) createContainer(ctx context.Context, project *types.Project, service types.ServiceConfig,
|
||||
name string, number int, opts createOptions) (container moby.Container, err error) {
|
||||
name string, number int, opts createOptions,
|
||||
) (ctr containerType.Summary, err error) {
|
||||
w := progress.ContextWriter(ctx)
|
||||
eventName := "Container " + name
|
||||
w.Event(progress.CreatingEvent(eventName))
|
||||
container, err = s.createMobyContainer(ctx, project, service, name, number, nil, opts, w)
|
||||
ctr, err = s.createMobyContainer(ctx, project, service, name, number, nil, opts, w)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@ -520,8 +602,9 @@ func (s *composeService) createContainer(ctx context.Context, project *types.Pro
|
||||
}
|
||||
|
||||
func (s *composeService) recreateContainer(ctx context.Context, project *types.Project, service types.ServiceConfig,
|
||||
replaced moby.Container, inherit bool, timeout *time.Duration) (moby.Container, error) {
|
||||
var created moby.Container
|
||||
replaced containerType.Summary, inherit bool, timeout *time.Duration,
|
||||
) (containerType.Summary, error) {
|
||||
var created containerType.Summary
|
||||
w := progress.ContextWriter(ctx)
|
||||
w.Event(progress.NewEvent(getContainerProgressName(replaced), progress.Working, "Recreate"))
|
||||
|
||||
@ -530,7 +613,7 @@ func (s *composeService) recreateContainer(ctx context.Context, project *types.P
|
||||
return created, err
|
||||
}
|
||||
|
||||
var inherited *moby.Container
|
||||
var inherited *containerType.Summary
|
||||
if inherit {
|
||||
inherited = &replaced
|
||||
}
|
||||
@ -567,14 +650,14 @@ func (s *composeService) recreateContainer(ctx context.Context, project *types.P
|
||||
return created, err
|
||||
}
|
||||
|
||||
func (s *composeService) startContainer(ctx context.Context, container moby.Container) error {
|
||||
func (s *composeService) startContainer(ctx context.Context, ctr containerType.Summary) error {
|
||||
w := progress.ContextWriter(ctx)
|
||||
w.Event(progress.NewEvent(getContainerProgressName(container), progress.Working, "Restart"))
|
||||
err := s.apiClient().ContainerStart(ctx, container.ID, containerType.StartOptions{})
|
||||
w.Event(progress.NewEvent(getContainerProgressName(ctr), progress.Working, "Restart"))
|
||||
err := s.apiClient().ContainerStart(ctx, ctr.ID, containerType.StartOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
w.Event(progress.NewEvent(getContainerProgressName(container), progress.Done, "Restarted"))
|
||||
w.Event(progress.NewEvent(getContainerProgressName(ctr), progress.Done, "Restarted"))
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -583,13 +666,12 @@ func (s *composeService) createMobyContainer(ctx context.Context,
|
||||
service types.ServiceConfig,
|
||||
name string,
|
||||
number int,
|
||||
inherit *moby.Container,
|
||||
inherit *containerType.Summary,
|
||||
opts createOptions,
|
||||
w progress.Writer,
|
||||
) (moby.Container, error) {
|
||||
var created moby.Container
|
||||
) (containerType.Summary, error) {
|
||||
var created containerType.Summary
|
||||
cfgs, err := s.getCreateConfigs(ctx, project, service, number, inherit, opts)
|
||||
|
||||
if err != nil {
|
||||
return created, err
|
||||
}
|
||||
@ -622,11 +704,11 @@ func (s *composeService) createMobyContainer(ctx context.Context,
|
||||
if err != nil {
|
||||
return created, err
|
||||
}
|
||||
created = moby.Container{
|
||||
created = containerType.Summary{
|
||||
ID: inspectedContainer.ID,
|
||||
Labels: inspectedContainer.Config.Labels,
|
||||
Names: []string{inspectedContainer.Name},
|
||||
NetworkSettings: &moby.SummaryNetworkSettings{
|
||||
NetworkSettings: &containerType.NetworkSettingsSummary{
|
||||
Networks: inspectedContainer.NetworkSettings.Networks,
|
||||
},
|
||||
}
|
||||
@ -743,11 +825,11 @@ func (s *composeService) isServiceHealthy(ctx context.Context, containers Contai
|
||||
return false, fmt.Errorf("container %s has no healthcheck configured", name)
|
||||
}
|
||||
switch container.State.Health.Status {
|
||||
case moby.Healthy:
|
||||
case containerType.Healthy:
|
||||
// Continue by checking the next container.
|
||||
case moby.Unhealthy:
|
||||
case containerType.Unhealthy:
|
||||
return false, fmt.Errorf("container %s is unhealthy", name)
|
||||
case moby.Starting:
|
||||
case containerType.Starting:
|
||||
return false, nil
|
||||
default:
|
||||
return false, fmt.Errorf("container %s had unexpected health status %q", name, container.State.Health.Status)
|
||||
@ -772,7 +854,8 @@ func (s *composeService) isServiceCompleted(ctx context.Context, containers Cont
|
||||
func (s *composeService) startService(ctx context.Context,
|
||||
project *types.Project, service types.ServiceConfig,
|
||||
containers Containers, listener api.ContainerEventListener,
|
||||
timeout time.Duration) error {
|
||||
timeout time.Duration,
|
||||
) error {
|
||||
if service.Deploy != nil && service.Deploy.Replicas != nil && *service.Deploy.Replicas == 0 {
|
||||
return nil
|
||||
}
|
||||
@ -790,19 +873,19 @@ func (s *composeService) startService(ctx context.Context,
|
||||
}
|
||||
|
||||
w := progress.ContextWriter(ctx)
|
||||
for _, container := range containers.filter(isService(service.Name)) {
|
||||
if container.State == ContainerRunning {
|
||||
for _, ctr := range containers.filter(isService(service.Name)) {
|
||||
if ctr.State == ContainerRunning {
|
||||
continue
|
||||
}
|
||||
eventName := getContainerProgressName(container)
|
||||
eventName := getContainerProgressName(ctr)
|
||||
w.Event(progress.StartingEvent(eventName))
|
||||
err = s.apiClient().ContainerStart(ctx, container.ID, containerType.StartOptions{})
|
||||
err = s.apiClient().ContainerStart(ctx, ctr.ID, containerType.StartOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, hook := range service.PostStart {
|
||||
err = s.runHook(ctx, container, service, hook, listener)
|
||||
err = s.runHook(ctx, ctr, service, hook, listener)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -25,8 +25,9 @@ import (
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
"github.com/docker/cli/cli/config/configfile"
|
||||
moby "github.com/docker/docker/api/types"
|
||||
containerType "github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
"github.com/docker/docker/api/types/image"
|
||||
"github.com/docker/docker/api/types/network"
|
||||
"github.com/docker/go-connections/nat"
|
||||
"go.uber.org/mock/gomock"
|
||||
@ -70,7 +71,7 @@ func TestServiceLinks(t *testing.T) {
|
||||
Scale: intPtr(1),
|
||||
}
|
||||
|
||||
containerListOptions := containerType.ListOptions{
|
||||
containerListOptions := container.ListOptions{
|
||||
Filters: filters.NewArgs(
|
||||
projectFilter(testProject),
|
||||
serviceFilter("db"),
|
||||
@ -94,7 +95,7 @@ func TestServiceLinks(t *testing.T) {
|
||||
s.Links = []string{"db"}
|
||||
|
||||
c := testContainer("db", dbContainerName, false)
|
||||
apiClient.EXPECT().ContainerList(gomock.Any(), containerListOptions).Return([]moby.Container{c}, nil)
|
||||
apiClient.EXPECT().ContainerList(gomock.Any(), containerListOptions).Return([]container.Summary{c}, nil)
|
||||
|
||||
links, err := tested.getLinks(context.Background(), testProject, s, 1)
|
||||
assert.NilError(t, err)
|
||||
@ -119,7 +120,7 @@ func TestServiceLinks(t *testing.T) {
|
||||
|
||||
c := testContainer("db", dbContainerName, false)
|
||||
|
||||
apiClient.EXPECT().ContainerList(gomock.Any(), containerListOptions).Return([]moby.Container{c}, nil)
|
||||
apiClient.EXPECT().ContainerList(gomock.Any(), containerListOptions).Return([]container.Summary{c}, nil)
|
||||
links, err := tested.getLinks(context.Background(), testProject, s, 1)
|
||||
assert.NilError(t, err)
|
||||
|
||||
@ -142,7 +143,7 @@ func TestServiceLinks(t *testing.T) {
|
||||
s.Links = []string{"db:dbname"}
|
||||
|
||||
c := testContainer("db", dbContainerName, false)
|
||||
apiClient.EXPECT().ContainerList(gomock.Any(), containerListOptions).Return([]moby.Container{c}, nil)
|
||||
apiClient.EXPECT().ContainerList(gomock.Any(), containerListOptions).Return([]container.Summary{c}, nil)
|
||||
|
||||
links, err := tested.getLinks(context.Background(), testProject, s, 1)
|
||||
assert.NilError(t, err)
|
||||
@ -167,7 +168,7 @@ func TestServiceLinks(t *testing.T) {
|
||||
s.ExternalLinks = []string{"db1:db2"}
|
||||
|
||||
c := testContainer("db", dbContainerName, false)
|
||||
apiClient.EXPECT().ContainerList(gomock.Any(), containerListOptions).Return([]moby.Container{c}, nil)
|
||||
apiClient.EXPECT().ContainerList(gomock.Any(), containerListOptions).Return([]container.Summary{c}, nil)
|
||||
|
||||
links, err := tested.getLinks(context.Background(), testProject, s, 1)
|
||||
assert.NilError(t, err)
|
||||
@ -196,7 +197,7 @@ func TestServiceLinks(t *testing.T) {
|
||||
s.Labels = s.Labels.Add(api.OneoffLabel, "True")
|
||||
|
||||
c := testContainer("web", webContainerName, true)
|
||||
containerListOptionsOneOff := containerType.ListOptions{
|
||||
containerListOptionsOneOff := container.ListOptions{
|
||||
Filters: filters.NewArgs(
|
||||
projectFilter(testProject),
|
||||
serviceFilter("web"),
|
||||
@ -205,7 +206,7 @@ func TestServiceLinks(t *testing.T) {
|
||||
),
|
||||
All: true,
|
||||
}
|
||||
apiClient.EXPECT().ContainerList(gomock.Any(), containerListOptionsOneOff).Return([]moby.Container{c}, nil)
|
||||
apiClient.EXPECT().ContainerList(gomock.Any(), containerListOptionsOneOff).Return([]container.Summary{c}, nil)
|
||||
|
||||
links, err := tested.getLinks(context.Background(), testProject, s, 1)
|
||||
assert.NilError(t, err)
|
||||
@ -268,7 +269,7 @@ func TestCreateMobyContainer(t *testing.T) {
|
||||
cli.EXPECT().Client().Return(apiClient).AnyTimes()
|
||||
cli.EXPECT().ConfigFile().Return(&configfile.ConfigFile{}).AnyTimes()
|
||||
apiClient.EXPECT().DaemonHost().Return("").AnyTimes()
|
||||
apiClient.EXPECT().ImageInspectWithRaw(gomock.Any(), gomock.Any()).Return(moby.ImageInspect{}, nil, nil).AnyTimes()
|
||||
apiClient.EXPECT().ImageInspect(gomock.Any(), gomock.Any()).Return(image.InspectResponse{}, nil).AnyTimes()
|
||||
// force `RuntimeVersion` to fetch again
|
||||
runtimeVersion = runtimeVersionCache{}
|
||||
apiClient.EXPECT().ServerVersion(gomock.Any()).Return(moby.Version{
|
||||
@ -303,11 +304,11 @@ func TestCreateMobyContainer(t *testing.T) {
|
||||
|
||||
var falseBool bool
|
||||
apiClient.EXPECT().ContainerCreate(gomock.Any(), gomock.Any(), gomock.Eq(
|
||||
&containerType.HostConfig{
|
||||
&container.HostConfig{
|
||||
PortBindings: nat.PortMap{},
|
||||
ExtraHosts: []string{},
|
||||
Tmpfs: map[string]string{},
|
||||
Resources: containerType.Resources{
|
||||
Resources: container.Resources{
|
||||
OomKillDisable: &falseBool,
|
||||
},
|
||||
NetworkMode: "b-moby-name",
|
||||
@ -320,18 +321,18 @@ func TestCreateMobyContainer(t *testing.T) {
|
||||
},
|
||||
},
|
||||
}), gomock.Any(), gomock.Any()).Times(1).Return(
|
||||
containerType.CreateResponse{
|
||||
container.CreateResponse{
|
||||
ID: "an-id",
|
||||
}, nil)
|
||||
|
||||
apiClient.EXPECT().ContainerInspect(gomock.Any(), gomock.Eq("an-id")).Times(1).Return(
|
||||
moby.ContainerJSON{
|
||||
ContainerJSONBase: &moby.ContainerJSONBase{
|
||||
container.InspectResponse{
|
||||
ContainerJSONBase: &container.ContainerJSONBase{
|
||||
ID: "an-id",
|
||||
Name: "a-name",
|
||||
},
|
||||
Config: &containerType.Config{},
|
||||
NetworkSettings: &moby.NetworkSettings{},
|
||||
Config: &container.Config{},
|
||||
NetworkSettings: &container.NetworkSettings{},
|
||||
}, nil)
|
||||
|
||||
apiClient.EXPECT().NetworkConnect(gomock.Any(), "a-moby-name", "an-id", gomock.Eq(
|
||||
@ -357,7 +358,7 @@ func TestCreateMobyContainer(t *testing.T) {
|
||||
cli.EXPECT().Client().Return(apiClient).AnyTimes()
|
||||
cli.EXPECT().ConfigFile().Return(&configfile.ConfigFile{}).AnyTimes()
|
||||
apiClient.EXPECT().DaemonHost().Return("").AnyTimes()
|
||||
apiClient.EXPECT().ImageInspectWithRaw(gomock.Any(), gomock.Any()).Return(moby.ImageInspect{}, nil, nil).AnyTimes()
|
||||
apiClient.EXPECT().ImageInspect(gomock.Any(), gomock.Any()).Return(image.InspectResponse{}, nil).AnyTimes()
|
||||
// force `RuntimeVersion` to fetch fresh version
|
||||
runtimeVersion = runtimeVersionCache{}
|
||||
apiClient.EXPECT().ServerVersion(gomock.Any()).Return(moby.Version{
|
||||
@ -392,11 +393,11 @@ func TestCreateMobyContainer(t *testing.T) {
|
||||
|
||||
var falseBool bool
|
||||
apiClient.EXPECT().ContainerCreate(gomock.Any(), gomock.Any(), gomock.Eq(
|
||||
&containerType.HostConfig{
|
||||
&container.HostConfig{
|
||||
PortBindings: nat.PortMap{},
|
||||
ExtraHosts: []string{},
|
||||
Tmpfs: map[string]string{},
|
||||
Resources: containerType.Resources{
|
||||
Resources: container.Resources{
|
||||
OomKillDisable: &falseBool,
|
||||
},
|
||||
NetworkMode: "b-moby-name",
|
||||
@ -413,18 +414,18 @@ func TestCreateMobyContainer(t *testing.T) {
|
||||
},
|
||||
},
|
||||
}), gomock.Any(), gomock.Any()).Times(1).Return(
|
||||
containerType.CreateResponse{
|
||||
container.CreateResponse{
|
||||
ID: "an-id",
|
||||
}, nil)
|
||||
|
||||
apiClient.EXPECT().ContainerInspect(gomock.Any(), gomock.Eq("an-id")).Times(1).Return(
|
||||
moby.ContainerJSON{
|
||||
ContainerJSONBase: &moby.ContainerJSONBase{
|
||||
container.InspectResponse{
|
||||
ContainerJSONBase: &container.ContainerJSONBase{
|
||||
ID: "an-id",
|
||||
Name: "a-name",
|
||||
},
|
||||
Config: &containerType.Config{},
|
||||
NetworkSettings: &moby.NetworkSettings{},
|
||||
Config: &container.Config{},
|
||||
NetworkSettings: &container.NetworkSettings{},
|
||||
}, nil)
|
||||
|
||||
_, err := tested.createMobyContainer(context.Background(), &project, service, "test", 0, nil, createOptions{
|
||||
@ -432,5 +433,4 @@ func TestCreateMobyContainer(t *testing.T) {
|
||||
}, progress.ContextWriter(context.TODO()))
|
||||
assert.NilError(t, err)
|
||||
})
|
||||
|
||||
}
|
||||
|
@ -139,7 +139,6 @@ func (s *composeService) listContainersTargetedForCopy(ctx context.Context, proj
|
||||
}
|
||||
if direction == fromService {
|
||||
return containers[:1], err
|
||||
|
||||
}
|
||||
return containers, err
|
||||
}
|
||||
|
@ -34,11 +34,12 @@ import (
|
||||
pathutil "github.com/docker/compose/v2/internal/paths"
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
"github.com/docker/compose/v2/pkg/progress"
|
||||
"github.com/docker/compose/v2/pkg/prompt"
|
||||
"github.com/docker/compose/v2/pkg/utils"
|
||||
moby "github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/blkiodev"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
"github.com/docker/docker/api/types/image"
|
||||
"github.com/docker/docker/api/types/mount"
|
||||
"github.com/docker/docker/api/types/network"
|
||||
"github.com/docker/docker/api/types/strslice"
|
||||
@ -80,12 +81,6 @@ func (s *composeService) create(ctx context.Context, project *types.Project, opt
|
||||
return err
|
||||
}
|
||||
|
||||
var observedState Containers
|
||||
observedState, err = s.getContainers(ctx, project.Name, oneOffInclude, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = s.ensureImagesExists(ctx, project, options.Build, options.QuietPull)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -93,14 +88,21 @@ func (s *composeService) create(ctx context.Context, project *types.Project, opt
|
||||
|
||||
prepareNetworks(project)
|
||||
|
||||
if err := s.ensureNetworks(ctx, project.Networks); err != nil {
|
||||
networks, err := s.ensureNetworks(ctx, project)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := s.ensureProjectVolumes(ctx, project); err != nil {
|
||||
volumes, err := s.ensureProjectVolumes(ctx, project, options.AssumeYes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var observedState Containers
|
||||
observedState, err = s.getContainers(ctx, project.Name, oneOffInclude, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
orphans := observedState.filter(isOrphaned(project))
|
||||
if len(orphans) > 0 && !options.IgnoreOrphans {
|
||||
if options.RemoveOrphans {
|
||||
@ -115,38 +117,43 @@ func (s *composeService) create(ctx context.Context, project *types.Project, opt
|
||||
"--remove-orphans flag to clean it up.", orphans.names())
|
||||
}
|
||||
}
|
||||
return newConvergence(options.Services, observedState, s).apply(ctx, project, options)
|
||||
return newConvergence(options.Services, observedState, networks, volumes, s).apply(ctx, project, options)
|
||||
}
|
||||
|
||||
func prepareNetworks(project *types.Project) {
|
||||
for k, nw := range project.Networks {
|
||||
nw.Labels = nw.Labels.Add(api.NetworkLabel, k)
|
||||
nw.Labels = nw.Labels.Add(api.ProjectLabel, project.Name)
|
||||
nw.Labels = nw.Labels.Add(api.VersionLabel, api.ComposeVersion)
|
||||
nw.CustomLabels = nw.CustomLabels.
|
||||
Add(api.NetworkLabel, k).
|
||||
Add(api.ProjectLabel, project.Name).
|
||||
Add(api.VersionLabel, api.ComposeVersion)
|
||||
project.Networks[k] = nw
|
||||
}
|
||||
}
|
||||
|
||||
func (s *composeService) ensureNetworks(ctx context.Context, networks types.Networks) error {
|
||||
for i, nw := range networks {
|
||||
err := s.ensureNetwork(ctx, &nw)
|
||||
func (s *composeService) ensureNetworks(ctx context.Context, project *types.Project) (map[string]string, error) {
|
||||
networks := map[string]string{}
|
||||
for name, nw := range project.Networks {
|
||||
id, err := s.ensureNetwork(ctx, project, name, &nw)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
networks[i] = nw
|
||||
networks[name] = id
|
||||
project.Networks[name] = nw
|
||||
}
|
||||
return nil
|
||||
return networks, nil
|
||||
}
|
||||
|
||||
func (s *composeService) ensureProjectVolumes(ctx context.Context, project *types.Project) error {
|
||||
func (s *composeService) ensureProjectVolumes(ctx context.Context, project *types.Project, assumeYes bool) (map[string]string, error) {
|
||||
ids := map[string]string{}
|
||||
for k, volume := range project.Volumes {
|
||||
volume.Labels = volume.Labels.Add(api.VolumeLabel, k)
|
||||
volume.Labels = volume.Labels.Add(api.ProjectLabel, project.Name)
|
||||
volume.Labels = volume.Labels.Add(api.VersionLabel, api.ComposeVersion)
|
||||
err := s.ensureVolume(ctx, volume, project.Name)
|
||||
volume.CustomLabels = volume.CustomLabels.Add(api.VolumeLabel, k)
|
||||
volume.CustomLabels = volume.CustomLabels.Add(api.ProjectLabel, project.Name)
|
||||
volume.CustomLabels = volume.CustomLabels.Add(api.VersionLabel, api.ComposeVersion)
|
||||
id, err := s.ensureVolume(ctx, k, volume, project, assumeYes)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
ids[k] = id
|
||||
}
|
||||
|
||||
err := func() error {
|
||||
@ -198,18 +205,17 @@ func (s *composeService) ensureProjectVolumes(ctx context.Context, project *type
|
||||
}
|
||||
return nil
|
||||
}()
|
||||
|
||||
if err != nil {
|
||||
progress.ContextWriter(ctx).TailMsgf("Failed to prepare Synchronized file shares: %v", err)
|
||||
}
|
||||
return nil
|
||||
return ids, nil
|
||||
}
|
||||
|
||||
func (s *composeService) getCreateConfigs(ctx context.Context,
|
||||
p *types.Project,
|
||||
service types.ServiceConfig,
|
||||
number int,
|
||||
inherit *moby.Container,
|
||||
inherit *container.Summary,
|
||||
opts createOptions,
|
||||
) (createConfigs, error) {
|
||||
labels, err := s.prepareLabels(opts.Labels, service, number)
|
||||
@ -252,7 +258,7 @@ func (s *composeService) getCreateConfigs(ctx context.Context,
|
||||
if err != nil {
|
||||
return createConfigs{}, err
|
||||
}
|
||||
var containerConfig = container.Config{
|
||||
containerConfig := container.Config{
|
||||
Hostname: service.Hostname,
|
||||
Domainname: service.DomainName,
|
||||
User: service.User,
|
||||
@ -268,7 +274,7 @@ func (s *composeService) getCreateConfigs(ctx context.Context,
|
||||
WorkingDir: service.WorkingDir,
|
||||
Entrypoint: entrypoint,
|
||||
NetworkDisabled: service.NetworkMode == "disabled",
|
||||
MacAddress: macAddress,
|
||||
MacAddress: macAddress, // Field is deprecated since API v1.44, but kept for compatibility with older API versions.
|
||||
Labels: labels,
|
||||
StopSignal: service.StopSignal,
|
||||
Env: ToMobyEnv(env),
|
||||
@ -410,7 +416,7 @@ func (s *composeService) prepareContainerMACAddress(ctx context.Context, service
|
||||
return "", fmt.Errorf("a MAC address is specified for multiple networks (%s), but this feature requires Docker Engine 1.44 or later (currently: %s)", strings.Join(withMacAddress, ", "), version)
|
||||
}
|
||||
|
||||
if mainNw != nil {
|
||||
if mainNw != nil && mainNw.MacAddress != "" {
|
||||
macAddress = mainNw.MacAddress
|
||||
}
|
||||
}
|
||||
@ -418,11 +424,11 @@ func (s *composeService) prepareContainerMACAddress(ctx context.Context, service
|
||||
return macAddress, nil
|
||||
}
|
||||
|
||||
func getAliases(project *types.Project, service types.ServiceConfig, serviceIndex int, networkKey string, useNetworkAliases bool) []string {
|
||||
func getAliases(project *types.Project, service types.ServiceConfig, serviceIndex int, cfg *types.ServiceNetworkConfig, useNetworkAliases bool) []string {
|
||||
aliases := []string{getContainerName(project.Name, service, serviceIndex)}
|
||||
if useNetworkAliases {
|
||||
aliases = append(aliases, service.Name)
|
||||
if cfg := service.Networks[networkKey]; cfg != nil {
|
||||
if cfg != nil {
|
||||
aliases = append(aliases, cfg.Aliases...)
|
||||
}
|
||||
}
|
||||
@ -437,6 +443,7 @@ func createEndpointSettings(p *types.Project, service types.ServiceConfig, servi
|
||||
ipv6Address string
|
||||
macAddress string
|
||||
driverOpts types.Options
|
||||
gwPriority int
|
||||
)
|
||||
if config != nil {
|
||||
ipv4Address = config.Ipv4Address
|
||||
@ -448,15 +455,17 @@ func createEndpointSettings(p *types.Project, service types.ServiceConfig, servi
|
||||
}
|
||||
macAddress = config.MacAddress
|
||||
driverOpts = config.DriverOpts
|
||||
gwPriority = config.GatewayPriority
|
||||
}
|
||||
return &network.EndpointSettings{
|
||||
Aliases: getAliases(p, service, serviceIndex, networkKey, useNetworkAliases),
|
||||
Aliases: getAliases(p, service, serviceIndex, config, useNetworkAliases),
|
||||
Links: links,
|
||||
IPAddress: ipv4Address,
|
||||
IPv6Gateway: ipv6Address,
|
||||
IPAMConfig: ipam,
|
||||
MacAddress: macAddress,
|
||||
DriverOpts: driverOpts,
|
||||
GwPriority: gwPriority,
|
||||
}
|
||||
}
|
||||
|
||||
@ -477,10 +486,10 @@ func parseSecurityOpts(p *types.Project, securityOpts []string) ([]string, bool,
|
||||
if strings.Contains(opt, ":") {
|
||||
con = strings.SplitN(opt, ":", 2)
|
||||
} else {
|
||||
return securityOpts, false, fmt.Errorf("Invalid security-opt: %q", opt)
|
||||
return securityOpts, false, fmt.Errorf("invalid security-opt: %q", opt)
|
||||
}
|
||||
}
|
||||
if con[0] == "seccomp" && con[1] != "unconfined" {
|
||||
if con[0] == "seccomp" && con[1] != "unconfined" && con[1] != "builtin" {
|
||||
f, err := os.ReadFile(p.RelativePath(con[1]))
|
||||
if err != nil {
|
||||
return securityOpts, false, fmt.Errorf("opening seccomp profile (%s) failed: %w", con[1], err)
|
||||
@ -542,23 +551,28 @@ func defaultNetworkSettings(
|
||||
primaryNetworkKey = "default"
|
||||
}
|
||||
primaryNetworkMobyNetworkName := project.Networks[primaryNetworkKey].Name
|
||||
endpointsConfig := map[string]*network.EndpointSettings{
|
||||
primaryNetworkMobyNetworkName: createEndpointSettings(project, service, serviceIndex, primaryNetworkKey, links, useNetworkAliases),
|
||||
}
|
||||
primaryNetworkEndpoint := createEndpointSettings(project, service, serviceIndex, primaryNetworkKey, links, useNetworkAliases)
|
||||
endpointsConfig := map[string]*network.EndpointSettings{}
|
||||
|
||||
// Starting from API version 1.44, the Engine will take several EndpointsConfigs
|
||||
// so we can pass all the extra networks we want the container to be connected to
|
||||
// in the network configuration instead of connecting the container to each extra
|
||||
// network individually after creation.
|
||||
if versions.GreaterThanOrEqualTo(version, "1.44") && len(service.Networks) > 1 {
|
||||
serviceNetworks := service.NetworksByPriority()
|
||||
for _, networkKey := range serviceNetworks[1:] {
|
||||
mobyNetworkName := project.Networks[networkKey].Name
|
||||
epSettings := createEndpointSettings(project, service, serviceIndex, networkKey, links, useNetworkAliases)
|
||||
endpointsConfig[mobyNetworkName] = epSettings
|
||||
if versions.GreaterThanOrEqualTo(version, "1.44") {
|
||||
if len(service.Networks) > 1 {
|
||||
serviceNetworks := service.NetworksByPriority()
|
||||
for _, networkKey := range serviceNetworks[1:] {
|
||||
mobyNetworkName := project.Networks[networkKey].Name
|
||||
epSettings := createEndpointSettings(project, service, serviceIndex, networkKey, links, useNetworkAliases)
|
||||
endpointsConfig[mobyNetworkName] = epSettings
|
||||
}
|
||||
}
|
||||
if primaryNetworkEndpoint.MacAddress == "" {
|
||||
primaryNetworkEndpoint.MacAddress = service.MacAddress
|
||||
}
|
||||
}
|
||||
|
||||
endpointsConfig[primaryNetworkMobyNetworkName] = primaryNetworkEndpoint
|
||||
networkConfig := &network.NetworkingConfig{
|
||||
EndpointsConfig: endpointsConfig,
|
||||
}
|
||||
@ -818,13 +832,13 @@ func (s *composeService) buildContainerVolumes(
|
||||
ctx context.Context,
|
||||
p types.Project,
|
||||
service types.ServiceConfig,
|
||||
inherit *moby.Container,
|
||||
inherit *container.Summary,
|
||||
) ([]string, []mount.Mount, error) {
|
||||
var mounts []mount.Mount
|
||||
var binds []string
|
||||
|
||||
image := api.GetImageNameOrDefault(service, p.Name)
|
||||
imgInspect, _, err := s.apiClient().ImageInspectWithRaw(ctx, image)
|
||||
img := api.GetImageNameOrDefault(service, p.Name)
|
||||
imgInspect, err := s.apiClient().ImageInspect(ctx, img)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
@ -879,8 +893,8 @@ func requireMountAPI(bind *types.ServiceVolumeBind) bool {
|
||||
}
|
||||
}
|
||||
|
||||
func buildContainerMountOptions(p types.Project, s types.ServiceConfig, img moby.ImageInspect, inherit *moby.Container) ([]mount.Mount, error) {
|
||||
var mounts = map[string]mount.Mount{}
|
||||
func buildContainerMountOptions(p types.Project, s types.ServiceConfig, img image.InspectResponse, inherit *container.Summary) ([]mount.Mount, error) {
|
||||
mounts := map[string]mount.Mount{}
|
||||
if inherit != nil {
|
||||
for _, m := range inherit.Mounts {
|
||||
if m.Type == "tmpfs" {
|
||||
@ -966,7 +980,7 @@ func fillBindMounts(p types.Project, s types.ServiceConfig, m map[string]mount.M
|
||||
}
|
||||
|
||||
func buildContainerConfigMounts(p types.Project, s types.ServiceConfig) ([]mount.Mount, error) {
|
||||
var mounts = map[string]mount.Mount{}
|
||||
mounts := map[string]mount.Mount{}
|
||||
|
||||
configsBaseDir := "/"
|
||||
for _, config := range s.Configs {
|
||||
@ -983,10 +997,10 @@ func buildContainerConfigMounts(p types.Project, s types.ServiceConfig) ([]mount
|
||||
}
|
||||
|
||||
if definedConfig.Driver != "" {
|
||||
return nil, errors.New("Docker Compose does not support configs.*.driver")
|
||||
return nil, errors.New("Docker Compose does not support configs.*.driver") //nolint:staticcheck
|
||||
}
|
||||
if definedConfig.TemplateDriver != "" {
|
||||
return nil, errors.New("Docker Compose does not support configs.*.template_driver")
|
||||
return nil, errors.New("Docker Compose does not support configs.*.template_driver") //nolint:staticcheck
|
||||
}
|
||||
|
||||
if definedConfig.Environment != "" || definedConfig.Content != "" {
|
||||
@ -1016,7 +1030,7 @@ func buildContainerConfigMounts(p types.Project, s types.ServiceConfig) ([]mount
|
||||
}
|
||||
|
||||
func buildContainerSecretMounts(p types.Project, s types.ServiceConfig) ([]mount.Mount, error) {
|
||||
var mounts = map[string]mount.Mount{}
|
||||
mounts := map[string]mount.Mount{}
|
||||
|
||||
secretsDir := "/run/secrets/"
|
||||
for _, secret := range s.Secrets {
|
||||
@ -1033,10 +1047,10 @@ func buildContainerSecretMounts(p types.Project, s types.ServiceConfig) ([]mount
|
||||
}
|
||||
|
||||
if definedSecret.Driver != "" {
|
||||
return nil, errors.New("Docker Compose does not support secrets.*.driver")
|
||||
return nil, errors.New("Docker Compose does not support secrets.*.driver") //nolint:staticcheck
|
||||
}
|
||||
if definedSecret.TemplateDriver != "" {
|
||||
return nil, errors.New("Docker Compose does not support secrets.*.template_driver")
|
||||
return nil, errors.New("Docker Compose does not support secrets.*.template_driver") //nolint:staticcheck
|
||||
}
|
||||
|
||||
if definedSecret.Environment != "" {
|
||||
@ -1200,24 +1214,21 @@ func buildTmpfsOptions(tmpfs *types.ServiceVolumeTmpfs) *mount.TmpfsOptions {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *composeService) ensureNetwork(ctx context.Context, n *types.NetworkConfig) error {
|
||||
func (s *composeService) ensureNetwork(ctx context.Context, project *types.Project, name string, n *types.NetworkConfig) (string, error) {
|
||||
if n.External {
|
||||
return s.resolveExternalNetwork(ctx, n)
|
||||
}
|
||||
|
||||
err := s.resolveOrCreateNetwork(ctx, n)
|
||||
id, err := s.resolveOrCreateNetwork(ctx, project, name, n)
|
||||
if errdefs.IsConflict(err) {
|
||||
// Maybe another execution of `docker compose up|run` created same network
|
||||
// let's retry once
|
||||
return s.resolveOrCreateNetwork(ctx, n)
|
||||
return s.resolveOrCreateNetwork(ctx, project, name, n)
|
||||
}
|
||||
return err
|
||||
return id, err
|
||||
}
|
||||
|
||||
func (s *composeService) resolveOrCreateNetwork(ctx context.Context, n *types.NetworkConfig) error { //nolint:gocyclo
|
||||
expectedNetworkLabel := n.Labels[api.NetworkLabel]
|
||||
expectedProjectLabel := n.Labels[api.ProjectLabel]
|
||||
|
||||
func (s *composeService) resolveOrCreateNetwork(ctx context.Context, project *types.Project, name string, n *types.NetworkConfig) (string, error) { //nolint:gocyclo
|
||||
// First, try to find a unique network matching by name or ID
|
||||
inspect, err := s.apiClient().NetworkInspect(ctx, n.Name, network.InspectOptions{})
|
||||
if err == nil {
|
||||
@ -1228,20 +1239,33 @@ func (s *composeService) resolveOrCreateNetwork(ctx context.Context, n *types.Ne
|
||||
if !ok {
|
||||
logrus.Warnf("a network with name %s exists but was not created by compose.\n"+
|
||||
"Set `external: true` to use an existing network", n.Name)
|
||||
} else if p != expectedProjectLabel {
|
||||
} else if p != project.Name {
|
||||
logrus.Warnf("a network with name %s exists but was not created for project %q.\n"+
|
||||
"Set `external: true` to use an existing network", n.Name, expectedProjectLabel)
|
||||
"Set `external: true` to use an existing network", n.Name, project.Name)
|
||||
}
|
||||
if inspect.Labels[api.NetworkLabel] != expectedNetworkLabel {
|
||||
return fmt.Errorf(
|
||||
if inspect.Labels[api.NetworkLabel] != name {
|
||||
return "", fmt.Errorf(
|
||||
"network %s was found but has incorrect label %s set to %q (expected: %q)",
|
||||
n.Name,
|
||||
api.NetworkLabel,
|
||||
inspect.Labels[api.NetworkLabel],
|
||||
expectedNetworkLabel,
|
||||
name,
|
||||
)
|
||||
}
|
||||
return nil
|
||||
|
||||
hash := inspect.Labels[api.ConfigHashLabel]
|
||||
expected, err := NetworkHash(n)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if hash == "" || hash == expected {
|
||||
return inspect.ID, nil
|
||||
}
|
||||
|
||||
err = s.removeDivergedNetwork(ctx, project, name, n)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
}
|
||||
// ignore other errors. Typically, an ambiguous request by name results in some generic `invalidParameter` error
|
||||
@ -1251,7 +1275,7 @@ func (s *composeService) resolveOrCreateNetwork(ctx context.Context, n *types.Ne
|
||||
Filters: filters.NewArgs(filters.Arg("name", n.Name)),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
return "", err
|
||||
}
|
||||
|
||||
// NetworkList Matches all or part of a network name, so we have to filter for a strict match
|
||||
@ -1260,9 +1284,9 @@ func (s *composeService) resolveOrCreateNetwork(ctx context.Context, n *types.Ne
|
||||
})
|
||||
|
||||
for _, net := range networks {
|
||||
if net.Labels[api.ProjectLabel] == expectedProjectLabel &&
|
||||
net.Labels[api.NetworkLabel] == expectedNetworkLabel {
|
||||
return nil
|
||||
if net.Labels[api.ProjectLabel] == project.Name &&
|
||||
net.Labels[api.NetworkLabel] == name {
|
||||
return net.ID, nil
|
||||
}
|
||||
}
|
||||
|
||||
@ -1272,7 +1296,7 @@ func (s *composeService) resolveOrCreateNetwork(ctx context.Context, n *types.Ne
|
||||
if len(networks) > 0 {
|
||||
logrus.Warnf("a network with name %s exists but was not created by compose.\n"+
|
||||
"Set `external: true` to use an existing network", n.Name)
|
||||
return nil
|
||||
return networks[0].ID, nil
|
||||
}
|
||||
|
||||
var ipam *network.IPAM
|
||||
@ -1291,14 +1315,20 @@ func (s *composeService) resolveOrCreateNetwork(ctx context.Context, n *types.Ne
|
||||
Config: config,
|
||||
}
|
||||
}
|
||||
hash, err := NetworkHash(n)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
n.CustomLabels = n.CustomLabels.Add(api.ConfigHashLabel, hash)
|
||||
createOpts := network.CreateOptions{
|
||||
Labels: n.Labels,
|
||||
Labels: mergeLabels(n.Labels, n.CustomLabels),
|
||||
Driver: n.Driver,
|
||||
Options: n.DriverOpts,
|
||||
Internal: n.Internal,
|
||||
Attachable: n.Attachable,
|
||||
IPAM: ipam,
|
||||
EnableIPv6: n.EnableIPv6,
|
||||
EnableIPv4: n.EnableIPv4,
|
||||
}
|
||||
|
||||
if n.Ipam.Driver != "" || len(n.Ipam.Config) > 0 {
|
||||
@ -1318,20 +1348,47 @@ func (s *composeService) resolveOrCreateNetwork(ctx context.Context, n *types.Ne
|
||||
}
|
||||
createOpts.IPAM.Config = append(createOpts.IPAM.Config, config)
|
||||
}
|
||||
|
||||
networkEventName := fmt.Sprintf("Network %s", n.Name)
|
||||
w := progress.ContextWriter(ctx)
|
||||
w.Event(progress.CreatingEvent(networkEventName))
|
||||
|
||||
_, err = s.apiClient().NetworkCreate(ctx, n.Name, createOpts)
|
||||
resp, err := s.apiClient().NetworkCreate(ctx, n.Name, createOpts)
|
||||
if err != nil {
|
||||
w.Event(progress.ErrorEvent(networkEventName))
|
||||
return fmt.Errorf("failed to create network %s: %w", n.Name, err)
|
||||
return "", fmt.Errorf("failed to create network %s: %w", n.Name, err)
|
||||
}
|
||||
w.Event(progress.CreatedEvent(networkEventName))
|
||||
return nil
|
||||
return resp.ID, nil
|
||||
}
|
||||
|
||||
func (s *composeService) resolveExternalNetwork(ctx context.Context, n *types.NetworkConfig) error {
|
||||
func (s *composeService) removeDivergedNetwork(ctx context.Context, project *types.Project, name string, n *types.NetworkConfig) error {
|
||||
// Remove services attached to this network to force recreation
|
||||
var services []string
|
||||
for _, service := range project.Services.Filter(func(config types.ServiceConfig) bool {
|
||||
_, ok := config.Networks[name]
|
||||
return ok
|
||||
}) {
|
||||
services = append(services, service.Name)
|
||||
}
|
||||
|
||||
// Stop containers so we can remove network
|
||||
// They will be restarted (actually: recreated) with the updated network
|
||||
err := s.stop(ctx, project.Name, api.StopOptions{
|
||||
Services: services,
|
||||
Project: project,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = s.apiClient().NetworkRemove(ctx, n.Name)
|
||||
eventName := fmt.Sprintf("Network %s", n.Name)
|
||||
progress.ContextWriter(ctx).Event(progress.RemovedEvent(eventName))
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *composeService) resolveExternalNetwork(ctx context.Context, n *types.NetworkConfig) (string, error) {
|
||||
// NetworkInspect will match on ID prefix, so NetworkList with a name
|
||||
// filter is used to look for an exact match to prevent e.g. a network
|
||||
// named `db` from getting erroneously matched to a network with an ID
|
||||
@ -1339,16 +1396,15 @@ func (s *composeService) resolveExternalNetwork(ctx context.Context, n *types.Ne
|
||||
networks, err := s.apiClient().NetworkList(ctx, network.ListOptions{
|
||||
Filters: filters.NewArgs(filters.Arg("name", n.Name)),
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
return "", err
|
||||
}
|
||||
|
||||
if len(networks) == 0 {
|
||||
// in this instance, n.Name is really an ID
|
||||
sn, err := s.apiClient().NetworkInspect(ctx, n.Name, network.InspectOptions{})
|
||||
if err != nil && !errdefs.IsNotFound(err) {
|
||||
return err
|
||||
return "", err
|
||||
}
|
||||
networks = append(networks, sn)
|
||||
}
|
||||
@ -1363,40 +1419,40 @@ func (s *composeService) resolveExternalNetwork(ctx context.Context, n *types.Ne
|
||||
|
||||
switch len(networks) {
|
||||
case 1:
|
||||
return nil
|
||||
return networks[0].ID, nil
|
||||
case 0:
|
||||
enabled, err := s.isSWarmEnabled(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
return "", err
|
||||
}
|
||||
if enabled {
|
||||
// Swarm nodes do not register overlay networks that were
|
||||
// created on a different node unless they're in use.
|
||||
// So we can't preemptively check network exists, but
|
||||
// networkAttach will later fail anyway if network actually doesn't exist
|
||||
return nil
|
||||
return "swarm", nil
|
||||
}
|
||||
return fmt.Errorf("network %s declared as external, but could not be found", n.Name)
|
||||
return "", fmt.Errorf("network %s declared as external, but could not be found", n.Name)
|
||||
default:
|
||||
return fmt.Errorf("multiple networks with name %q were found. Use network ID as `name` to avoid ambiguity", n.Name)
|
||||
return "", fmt.Errorf("multiple networks with name %q were found. Use network ID as `name` to avoid ambiguity", n.Name)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *composeService) ensureVolume(ctx context.Context, volume types.VolumeConfig, project string) error {
|
||||
func (s *composeService) ensureVolume(ctx context.Context, name string, volume types.VolumeConfig, project *types.Project, assumeYes bool) (string, error) {
|
||||
inspected, err := s.apiClient().VolumeInspect(ctx, volume.Name)
|
||||
if err != nil {
|
||||
if !errdefs.IsNotFound(err) {
|
||||
return err
|
||||
return "", err
|
||||
}
|
||||
if volume.External {
|
||||
return fmt.Errorf("external volume %q not found", volume.Name)
|
||||
return "", fmt.Errorf("external volume %q not found", volume.Name)
|
||||
}
|
||||
err := s.createVolume(ctx, volume)
|
||||
return err
|
||||
err = s.createVolume(ctx, volume)
|
||||
return volume.Name, err
|
||||
}
|
||||
|
||||
if volume.External {
|
||||
return nil
|
||||
return volume.Name, nil
|
||||
}
|
||||
|
||||
// Volume exists with name, but let's double-check this is the expected one
|
||||
@ -1404,18 +1460,86 @@ func (s *composeService) ensureVolume(ctx context.Context, volume types.VolumeCo
|
||||
if !ok {
|
||||
logrus.Warnf("volume %q already exists but was not created by Docker Compose. Use `external: true` to use an existing volume", volume.Name)
|
||||
}
|
||||
if ok && p != project {
|
||||
logrus.Warnf("volume %q already exists but was created for project %q (expected %q). Use `external: true` to use an existing volume", volume.Name, p, project)
|
||||
if ok && p != project.Name {
|
||||
logrus.Warnf("volume %q already exists but was created for project %q (expected %q). Use `external: true` to use an existing volume", volume.Name, p, project.Name)
|
||||
}
|
||||
return nil
|
||||
|
||||
expected, err := VolumeHash(volume)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
actual, ok := inspected.Labels[api.ConfigHashLabel]
|
||||
if ok && actual != expected {
|
||||
confirm := assumeYes
|
||||
if !assumeYes {
|
||||
msg := fmt.Sprintf("Volume %q exists but doesn't match configuration in compose file. Recreate (data will be lost)?", volume.Name)
|
||||
confirm, err = prompt.NewPrompt(s.stdin(), s.stdout()).Confirm(msg, false)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
if confirm {
|
||||
err = s.removeDivergedVolume(ctx, name, volume, project)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return volume.Name, s.createVolume(ctx, volume)
|
||||
}
|
||||
}
|
||||
return inspected.Name, nil
|
||||
}
|
||||
|
||||
func (s *composeService) removeDivergedVolume(ctx context.Context, name string, volume types.VolumeConfig, project *types.Project) error {
|
||||
// Remove services mounting divergent volume
|
||||
var services []string
|
||||
for _, service := range project.Services.Filter(func(config types.ServiceConfig) bool {
|
||||
for _, cfg := range config.Volumes {
|
||||
if cfg.Source == name {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}) {
|
||||
services = append(services, service.Name)
|
||||
}
|
||||
|
||||
err := s.stop(ctx, project.Name, api.StopOptions{
|
||||
Services: services,
|
||||
Project: project,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
containers, err := s.getContainers(ctx, project.Name, oneOffExclude, true, services...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// FIXME (ndeloof) we have to remove container so we can recreate volume
|
||||
// but doing so we can't inherit anonymous volumes from previous instance
|
||||
err = s.remove(ctx, containers, api.RemoveOptions{
|
||||
Services: services,
|
||||
Project: project,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return s.apiClient().VolumeRemove(ctx, volume.Name, true)
|
||||
}
|
||||
|
||||
func (s *composeService) createVolume(ctx context.Context, volume types.VolumeConfig) error {
|
||||
eventName := fmt.Sprintf("Volume %q", volume.Name)
|
||||
w := progress.ContextWriter(ctx)
|
||||
w.Event(progress.CreatingEvent(eventName))
|
||||
_, err := s.apiClient().VolumeCreate(ctx, volumetypes.CreateOptions{
|
||||
Labels: volume.Labels,
|
||||
hash, err := VolumeHash(volume)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
volume.CustomLabels.Add(api.ConfigHashLabel, hash)
|
||||
_, err = s.apiClient().VolumeCreate(ctx, volumetypes.CreateOptions{
|
||||
Labels: mergeLabels(volume.Labels, volume.CustomLabels),
|
||||
Name: volume.Name,
|
||||
Driver: volume.Driver,
|
||||
DriverOpts: volume.DriverOpts,
|
||||
|
@ -22,13 +22,14 @@ import (
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/image"
|
||||
"gotest.tools/v3/assert/cmp"
|
||||
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
"github.com/docker/docker/api/types/network"
|
||||
|
||||
composetypes "github.com/compose-spec/compose-go/v2/types"
|
||||
moby "github.com/docker/docker/api/types"
|
||||
mountTypes "github.com/docker/docker/api/types/mount"
|
||||
|
||||
"gotest.tools/v3/assert"
|
||||
@ -92,7 +93,7 @@ func TestPrepareNetworkLabels(t *testing.T) {
|
||||
Networks: composetypes.Networks(map[string]composetypes.NetworkConfig{"skynet": {}}),
|
||||
}
|
||||
prepareNetworks(&project)
|
||||
assert.DeepEqual(t, project.Networks["skynet"].Labels, composetypes.Labels(map[string]string{
|
||||
assert.DeepEqual(t, project.Networks["skynet"].CustomLabels, composetypes.Labels(map[string]string{
|
||||
"com.docker.compose.network": "skynet",
|
||||
"com.docker.compose.project": "myProject",
|
||||
"com.docker.compose.version": api.ComposeVersion,
|
||||
@ -140,8 +141,8 @@ func TestBuildContainerMountOptions(t *testing.T) {
|
||||
}),
|
||||
}
|
||||
|
||||
inherit := &moby.Container{
|
||||
Mounts: []moby.MountPoint{
|
||||
inherit := &container.Summary{
|
||||
Mounts: []container.MountPoint{
|
||||
{
|
||||
Type: composetypes.VolumeTypeVolume,
|
||||
Destination: "/var/myvolume1",
|
||||
@ -153,7 +154,7 @@ func TestBuildContainerMountOptions(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
mounts, err := buildContainerMountOptions(project, project.Services["myService"], moby.ImageInspect{}, inherit)
|
||||
mounts, err := buildContainerMountOptions(project, project.Services["myService"], image.InspectResponse{}, inherit)
|
||||
sort.Slice(mounts, func(i, j int) bool {
|
||||
return mounts[i].Target < mounts[j].Target
|
||||
})
|
||||
@ -165,7 +166,7 @@ func TestBuildContainerMountOptions(t *testing.T) {
|
||||
assert.Equal(t, mounts[2].VolumeOptions.Subpath, "etc")
|
||||
assert.Equal(t, mounts[3].Target, "\\\\.\\pipe\\docker_engine")
|
||||
|
||||
mounts, err = buildContainerMountOptions(project, project.Services["myService"], moby.ImageInspect{}, inherit)
|
||||
mounts, err = buildContainerMountOptions(project, project.Services["myService"], image.InspectResponse{}, inherit)
|
||||
sort.Slice(mounts, func(i, j int) bool {
|
||||
return mounts[i].Target < mounts[j].Target
|
||||
})
|
||||
@ -278,33 +279,26 @@ func TestDefaultNetworkSettings(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateEndpointSettings(t *testing.T) {
|
||||
eps := createEndpointSettings(
|
||||
&composetypes.Project{
|
||||
Name: "projName",
|
||||
},
|
||||
composetypes.ServiceConfig{
|
||||
Name: "serviceName",
|
||||
ContainerName: "containerName",
|
||||
Networks: map[string]*composetypes.ServiceNetworkConfig{
|
||||
"netName": {
|
||||
Priority: 100,
|
||||
Aliases: []string{"alias1", "alias2"},
|
||||
Ipv4Address: "10.16.17.18",
|
||||
Ipv6Address: "fdb4:7a7f:373a:3f0c::42",
|
||||
LinkLocalIPs: []string{"169.254.10.20"},
|
||||
MacAddress: "10:00:00:00:01",
|
||||
DriverOpts: composetypes.Options{
|
||||
"driverOpt1": "optval1",
|
||||
"driverOpt2": "optval2",
|
||||
},
|
||||
eps := createEndpointSettings(&composetypes.Project{
|
||||
Name: "projName",
|
||||
}, composetypes.ServiceConfig{
|
||||
Name: "serviceName",
|
||||
ContainerName: "containerName",
|
||||
Networks: map[string]*composetypes.ServiceNetworkConfig{
|
||||
"netName": {
|
||||
Priority: 100,
|
||||
Aliases: []string{"alias1", "alias2"},
|
||||
Ipv4Address: "10.16.17.18",
|
||||
Ipv6Address: "fdb4:7a7f:373a:3f0c::42",
|
||||
LinkLocalIPs: []string{"169.254.10.20"},
|
||||
MacAddress: "10:00:00:00:01",
|
||||
DriverOpts: composetypes.Options{
|
||||
"driverOpt1": "optval1",
|
||||
"driverOpt2": "optval2",
|
||||
},
|
||||
},
|
||||
},
|
||||
0, // serviceIndex
|
||||
"netName", // networkKey
|
||||
[]string{"link1", "link2"}, // links
|
||||
true, // useNetworkAliases
|
||||
)
|
||||
}, 0, "netName", []string{"link1", "link2"}, true)
|
||||
assert.Check(t, cmp.DeepEqual(eps, &network.EndpointSettings{
|
||||
IPAMConfig: &network.EndpointIPAMConfig{
|
||||
IPv4Address: "10.16.17.18",
|
||||
|
@ -172,7 +172,6 @@ func (t *graphTraversal) run(ctx context.Context, graph *Graph, eg *errgroup.Gro
|
||||
continue
|
||||
}
|
||||
|
||||
node := node
|
||||
if !t.consume(node.Key) {
|
||||
// another worker already visited this node
|
||||
continue
|
||||
@ -438,7 +437,6 @@ func (g *Graph) HasCycles() (bool, error) {
|
||||
if !utils.StringContains(discovered, vertex.Key) && !utils.StringContains(finished, vertex.Key) {
|
||||
var err error
|
||||
discovered, finished, err = g.visit(vertex.Key, path, discovered, finished)
|
||||
|
||||
if err != nil {
|
||||
return true, err
|
||||
}
|
||||
|
@ -27,7 +27,6 @@ import (
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
"github.com/docker/compose/v2/pkg/progress"
|
||||
"github.com/docker/compose/v2/pkg/utils"
|
||||
moby "github.com/docker/docker/api/types"
|
||||
containerType "github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
imageapi "github.com/docker/docker/api/types/image"
|
||||
@ -84,8 +83,11 @@ func (s *composeService) down(ctx context.Context, projectName string, options a
|
||||
}
|
||||
|
||||
err = InReverseDependencyOrder(ctx, project, func(c context.Context, service string) error {
|
||||
serviceContainers := containers.filter(isService(service))
|
||||
serv := project.Services[service]
|
||||
if serv.Provider != nil {
|
||||
return s.runPlugin(ctx, project, serv, "down")
|
||||
}
|
||||
serviceContainers := containers.filter(isService(service))
|
||||
err := s.removeContainers(ctx, serviceContainers, &serv, options.Timeout, options.Volumes)
|
||||
return err
|
||||
}, WithRootNodesAndDown(options.Services))
|
||||
@ -297,13 +299,13 @@ func (s *composeService) removeVolume(ctx context.Context, id string, w progress
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *composeService) stopContainer(ctx context.Context, w progress.Writer, service *types.ServiceConfig, container moby.Container, timeout *time.Duration) error {
|
||||
eventName := getContainerProgressName(container)
|
||||
func (s *composeService) stopContainer(ctx context.Context, w progress.Writer, service *types.ServiceConfig, ctr containerType.Summary, timeout *time.Duration) error {
|
||||
eventName := getContainerProgressName(ctr)
|
||||
w.Event(progress.StoppingEvent(eventName))
|
||||
|
||||
if service != nil {
|
||||
for _, hook := range service.PreStop {
|
||||
err := s.runHook(ctx, container, *service, hook, nil)
|
||||
err := s.runHook(ctx, ctr, *service, hook, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -311,7 +313,7 @@ func (s *composeService) stopContainer(ctx context.Context, w progress.Writer, s
|
||||
}
|
||||
|
||||
timeoutInSecond := utils.DurationSecondToInt(timeout)
|
||||
err := s.apiClient().ContainerStop(ctx, container.ID, containerType.StopOptions{Timeout: timeoutInSecond})
|
||||
err := s.apiClient().ContainerStop(ctx, ctr.ID, containerType.StopOptions{Timeout: timeoutInSecond})
|
||||
if err != nil {
|
||||
w.Event(progress.ErrorMessageEvent(eventName, "Error while Stopping"))
|
||||
return err
|
||||
@ -320,32 +322,30 @@ func (s *composeService) stopContainer(ctx context.Context, w progress.Writer, s
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *composeService) stopContainers(ctx context.Context, w progress.Writer, serv *types.ServiceConfig, containers []moby.Container, timeout *time.Duration) error {
|
||||
func (s *composeService) stopContainers(ctx context.Context, w progress.Writer, serv *types.ServiceConfig, containers []containerType.Summary, timeout *time.Duration) error {
|
||||
eg, ctx := errgroup.WithContext(ctx)
|
||||
for _, container := range containers {
|
||||
container := container
|
||||
for _, ctr := range containers {
|
||||
eg.Go(func() error {
|
||||
return s.stopContainer(ctx, w, serv, container, timeout)
|
||||
return s.stopContainer(ctx, w, serv, ctr, timeout)
|
||||
})
|
||||
}
|
||||
return eg.Wait()
|
||||
}
|
||||
|
||||
func (s *composeService) removeContainers(ctx context.Context, containers []moby.Container, service *types.ServiceConfig, timeout *time.Duration, volumes bool) error {
|
||||
func (s *composeService) removeContainers(ctx context.Context, containers []containerType.Summary, service *types.ServiceConfig, timeout *time.Duration, volumes bool) error {
|
||||
eg, _ := errgroup.WithContext(ctx)
|
||||
for _, container := range containers {
|
||||
container := container
|
||||
for _, ctr := range containers {
|
||||
eg.Go(func() error {
|
||||
return s.stopAndRemoveContainer(ctx, container, service, timeout, volumes)
|
||||
return s.stopAndRemoveContainer(ctx, ctr, service, timeout, volumes)
|
||||
})
|
||||
}
|
||||
return eg.Wait()
|
||||
}
|
||||
|
||||
func (s *composeService) stopAndRemoveContainer(ctx context.Context, container moby.Container, service *types.ServiceConfig, timeout *time.Duration, volumes bool) error {
|
||||
func (s *composeService) stopAndRemoveContainer(ctx context.Context, ctr containerType.Summary, service *types.ServiceConfig, timeout *time.Duration, volumes bool) error {
|
||||
w := progress.ContextWriter(ctx)
|
||||
eventName := getContainerProgressName(container)
|
||||
err := s.stopContainer(ctx, w, service, container, timeout)
|
||||
eventName := getContainerProgressName(ctr)
|
||||
err := s.stopContainer(ctx, w, service, ctr, timeout)
|
||||
if errdefs.IsNotFound(err) {
|
||||
w.Event(progress.RemovedEvent(eventName))
|
||||
return nil
|
||||
@ -354,7 +354,7 @@ func (s *composeService) stopAndRemoveContainer(ctx context.Context, container m
|
||||
return err
|
||||
}
|
||||
w.Event(progress.RemovingEvent(eventName))
|
||||
err = s.apiClient().ContainerRemove(ctx, container.ID, containerType.RemoveOptions{
|
||||
err = s.apiClient().ContainerRemove(ctx, ctr.ID, containerType.RemoveOptions{
|
||||
Force: true,
|
||||
RemoveVolumes: volumes,
|
||||
})
|
||||
|
@ -25,8 +25,7 @@ import (
|
||||
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
"github.com/docker/cli/cli/streams"
|
||||
moby "github.com/docker/docker/api/types"
|
||||
containerType "github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
"github.com/docker/docker/api/types/image"
|
||||
"github.com/docker/docker/api/types/network"
|
||||
@ -49,7 +48,7 @@ func TestDown(t *testing.T) {
|
||||
}
|
||||
|
||||
api.EXPECT().ContainerList(gomock.Any(), projectFilterListOpt(false)).Return(
|
||||
[]moby.Container{
|
||||
[]container.Summary{
|
||||
testContainer("service1", "123", false),
|
||||
testContainer("service2", "456", false),
|
||||
testContainer("service2", "789", false),
|
||||
@ -70,14 +69,14 @@ func TestDown(t *testing.T) {
|
||||
{ID: "def456", Name: "myProject_default", Labels: map[string]string{compose.NetworkLabel: "default"}},
|
||||
}, nil)
|
||||
|
||||
stopOptions := containerType.StopOptions{}
|
||||
stopOptions := container.StopOptions{}
|
||||
api.EXPECT().ContainerStop(gomock.Any(), "123", stopOptions).Return(nil)
|
||||
api.EXPECT().ContainerStop(gomock.Any(), "456", stopOptions).Return(nil)
|
||||
api.EXPECT().ContainerStop(gomock.Any(), "789", stopOptions).Return(nil)
|
||||
|
||||
api.EXPECT().ContainerRemove(gomock.Any(), "123", containerType.RemoveOptions{Force: true}).Return(nil)
|
||||
api.EXPECT().ContainerRemove(gomock.Any(), "456", containerType.RemoveOptions{Force: true}).Return(nil)
|
||||
api.EXPECT().ContainerRemove(gomock.Any(), "789", containerType.RemoveOptions{Force: true}).Return(nil)
|
||||
api.EXPECT().ContainerRemove(gomock.Any(), "123", container.RemoveOptions{Force: true}).Return(nil)
|
||||
api.EXPECT().ContainerRemove(gomock.Any(), "456", container.RemoveOptions{Force: true}).Return(nil)
|
||||
api.EXPECT().ContainerRemove(gomock.Any(), "789", container.RemoveOptions{Force: true}).Return(nil)
|
||||
|
||||
api.EXPECT().NetworkList(gomock.Any(), network.ListOptions{
|
||||
Filters: filters.NewArgs(
|
||||
@ -106,7 +105,7 @@ func TestDownWithGivenServices(t *testing.T) {
|
||||
}
|
||||
|
||||
api.EXPECT().ContainerList(gomock.Any(), projectFilterListOpt(false)).Return(
|
||||
[]moby.Container{
|
||||
[]container.Summary{
|
||||
testContainer("service1", "123", false),
|
||||
testContainer("service2", "456", false),
|
||||
testContainer("service2", "789", false),
|
||||
@ -127,10 +126,10 @@ func TestDownWithGivenServices(t *testing.T) {
|
||||
{ID: "def456", Name: "myProject_default", Labels: map[string]string{compose.NetworkLabel: "default"}},
|
||||
}, nil)
|
||||
|
||||
stopOptions := containerType.StopOptions{}
|
||||
stopOptions := container.StopOptions{}
|
||||
api.EXPECT().ContainerStop(gomock.Any(), "123", stopOptions).Return(nil)
|
||||
|
||||
api.EXPECT().ContainerRemove(gomock.Any(), "123", containerType.RemoveOptions{Force: true}).Return(nil)
|
||||
api.EXPECT().ContainerRemove(gomock.Any(), "123", container.RemoveOptions{Force: true}).Return(nil)
|
||||
|
||||
api.EXPECT().NetworkList(gomock.Any(), network.ListOptions{
|
||||
Filters: filters.NewArgs(
|
||||
@ -158,7 +157,7 @@ func TestDownWithSpecifiedServiceButTheServicesAreNotRunning(t *testing.T) {
|
||||
}
|
||||
|
||||
api.EXPECT().ContainerList(gomock.Any(), projectFilterListOpt(false)).Return(
|
||||
[]moby.Container{
|
||||
[]container.Summary{
|
||||
testContainer("service1", "123", false),
|
||||
testContainer("service2", "456", false),
|
||||
testContainer("service2", "789", false),
|
||||
@ -195,7 +194,7 @@ func TestDownRemoveOrphans(t *testing.T) {
|
||||
}
|
||||
|
||||
api.EXPECT().ContainerList(gomock.Any(), projectFilterListOpt(true)).Return(
|
||||
[]moby.Container{
|
||||
[]container.Summary{
|
||||
testContainer("service1", "123", false),
|
||||
testContainer("service2", "789", false),
|
||||
testContainer("service_orphan", "321", true),
|
||||
@ -211,16 +210,17 @@ func TestDownRemoveOrphans(t *testing.T) {
|
||||
{
|
||||
Name: "myProject_default",
|
||||
Labels: map[string]string{compose.NetworkLabel: "default"},
|
||||
}}, nil)
|
||||
},
|
||||
}, nil)
|
||||
|
||||
stopOptions := containerType.StopOptions{}
|
||||
stopOptions := container.StopOptions{}
|
||||
api.EXPECT().ContainerStop(gomock.Any(), "123", stopOptions).Return(nil)
|
||||
api.EXPECT().ContainerStop(gomock.Any(), "789", stopOptions).Return(nil)
|
||||
api.EXPECT().ContainerStop(gomock.Any(), "321", stopOptions).Return(nil)
|
||||
|
||||
api.EXPECT().ContainerRemove(gomock.Any(), "123", containerType.RemoveOptions{Force: true}).Return(nil)
|
||||
api.EXPECT().ContainerRemove(gomock.Any(), "789", containerType.RemoveOptions{Force: true}).Return(nil)
|
||||
api.EXPECT().ContainerRemove(gomock.Any(), "321", containerType.RemoveOptions{Force: true}).Return(nil)
|
||||
api.EXPECT().ContainerRemove(gomock.Any(), "123", container.RemoveOptions{Force: true}).Return(nil)
|
||||
api.EXPECT().ContainerRemove(gomock.Any(), "789", container.RemoveOptions{Force: true}).Return(nil)
|
||||
api.EXPECT().ContainerRemove(gomock.Any(), "321", container.RemoveOptions{Force: true}).Return(nil)
|
||||
|
||||
api.EXPECT().NetworkList(gomock.Any(), network.ListOptions{
|
||||
Filters: filters.NewArgs(
|
||||
@ -245,7 +245,7 @@ func TestDownRemoveVolumes(t *testing.T) {
|
||||
}
|
||||
|
||||
api.EXPECT().ContainerList(gomock.Any(), projectFilterListOpt(false)).Return(
|
||||
[]moby.Container{testContainer("service1", "123", false)}, nil)
|
||||
[]container.Summary{testContainer("service1", "123", false)}, nil)
|
||||
api.EXPECT().VolumeList(
|
||||
gomock.Any(),
|
||||
volume.ListOptions{
|
||||
@ -257,8 +257,8 @@ func TestDownRemoveVolumes(t *testing.T) {
|
||||
api.EXPECT().NetworkList(gomock.Any(), network.ListOptions{Filters: filters.NewArgs(projectFilter(strings.ToLower(testProject)))}).
|
||||
Return(nil, nil)
|
||||
|
||||
api.EXPECT().ContainerStop(gomock.Any(), "123", containerType.StopOptions{}).Return(nil)
|
||||
api.EXPECT().ContainerRemove(gomock.Any(), "123", containerType.RemoveOptions{Force: true, RemoveVolumes: true}).Return(nil)
|
||||
api.EXPECT().ContainerStop(gomock.Any(), "123", container.StopOptions{}).Return(nil)
|
||||
api.EXPECT().ContainerRemove(gomock.Any(), "123", container.RemoveOptions{Force: true, RemoveVolumes: true}).Return(nil)
|
||||
|
||||
api.EXPECT().VolumeRemove(gomock.Any(), "myProject_volume", true).Return(nil)
|
||||
|
||||
@ -290,7 +290,7 @@ func TestDownRemoveImages(t *testing.T) {
|
||||
}
|
||||
|
||||
api.EXPECT().ContainerList(gomock.Any(), projectFilterListOpt(false)).
|
||||
Return([]moby.Container{
|
||||
Return([]container.Summary{
|
||||
testContainer("service1", "123", false),
|
||||
}, nil).
|
||||
AnyTimes()
|
||||
@ -319,7 +319,7 @@ func TestDownRemoveImages(t *testing.T) {
|
||||
"missing-named-image": false,
|
||||
}
|
||||
for img, exists := range imagesToBeInspected {
|
||||
var resp moby.ImageInspect
|
||||
var resp image.InspectResponse
|
||||
var err error
|
||||
if exists {
|
||||
resp.RepoTags = []string{img}
|
||||
@ -327,13 +327,13 @@ func TestDownRemoveImages(t *testing.T) {
|
||||
err = errdefs.NotFound(fmt.Errorf("test specified that image %q should not exist", img))
|
||||
}
|
||||
|
||||
api.EXPECT().ImageInspectWithRaw(gomock.Any(), img).
|
||||
Return(resp, nil, err).
|
||||
api.EXPECT().ImageInspect(gomock.Any(), img).
|
||||
Return(resp, err).
|
||||
AnyTimes()
|
||||
}
|
||||
|
||||
api.EXPECT().ImageInspectWithRaw(gomock.Any(), "registry.example.com/remote-image-tagged:v1.0").
|
||||
Return(moby.ImageInspect{RepoTags: []string{"registry.example.com/remote-image-tagged:v1.0"}}, nil, nil).
|
||||
api.EXPECT().ImageInspect(gomock.Any(), "registry.example.com/remote-image-tagged:v1.0").
|
||||
Return(image.InspectResponse{RepoTags: []string{"registry.example.com/remote-image-tagged:v1.0"}}, nil).
|
||||
AnyTimes()
|
||||
|
||||
localImagesToBeRemoved := []string{
|
||||
@ -378,10 +378,10 @@ func TestDownRemoveImages_NoLabel(t *testing.T) {
|
||||
dockerCli: cli,
|
||||
}
|
||||
|
||||
container := testContainer("service1", "123", false)
|
||||
ctr := testContainer("service1", "123", false)
|
||||
|
||||
api.EXPECT().ContainerList(gomock.Any(), projectFilterListOpt(false)).Return(
|
||||
[]moby.Container{container}, nil)
|
||||
[]container.Summary{ctr}, nil)
|
||||
|
||||
api.EXPECT().VolumeList(
|
||||
gomock.Any(),
|
||||
@ -403,11 +403,11 @@ func TestDownRemoveImages_NoLabel(t *testing.T) {
|
||||
),
|
||||
}).Return(nil, nil)
|
||||
|
||||
api.EXPECT().ImageInspectWithRaw(gomock.Any(), "testproject-service1").
|
||||
Return(moby.ImageInspect{}, nil, nil)
|
||||
api.EXPECT().ImageInspect(gomock.Any(), "testproject-service1").
|
||||
Return(image.InspectResponse{}, nil)
|
||||
|
||||
api.EXPECT().ContainerStop(gomock.Any(), "123", containerType.StopOptions{}).Return(nil)
|
||||
api.EXPECT().ContainerRemove(gomock.Any(), "123", containerType.RemoveOptions{Force: true}).Return(nil)
|
||||
api.EXPECT().ContainerStop(gomock.Any(), "123", container.StopOptions{}).Return(nil)
|
||||
api.EXPECT().ContainerRemove(gomock.Any(), "123", container.RemoveOptions{Force: true}).Return(nil)
|
||||
|
||||
api.EXPECT().ImageRemove(gomock.Any(), "testproject-service1:latest", image.RemoveOptions{}).Return(nil, nil)
|
||||
|
||||
|
@ -21,10 +21,8 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
// isCaseInsensitiveEnvVars is true on platforms where environment variable names are treated case-insensitively.
|
||||
isCaseInsensitiveEnvVars = (runtime.GOOS == "windows")
|
||||
)
|
||||
// isCaseInsensitiveEnvVars is true on platforms where environment variable names are treated case-insensitively.
|
||||
var isCaseInsensitiveEnvVars = (runtime.GOOS == "windows")
|
||||
|
||||
// envResolver returns resolver for environment variables suitable for the current platform.
|
||||
// Expected to be used with `MappingWithEquals.Resolve`.
|
||||
|
@ -1,71 +0,0 @@
|
||||
/*
|
||||
Copyright 2020 Docker Compose CLI authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package compose
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io/fs"
|
||||
|
||||
"github.com/compose-spec/compose-go/v2/errdefs"
|
||||
)
|
||||
|
||||
// Error error to categorize failures and extract metrics info
|
||||
type Error struct {
|
||||
Err error
|
||||
Category *FailureCategory
|
||||
}
|
||||
|
||||
// WrapComposeError wraps the error if not nil, otherwise returns nil
|
||||
func WrapComposeError(err error) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
return Error{
|
||||
Err: err,
|
||||
}
|
||||
}
|
||||
|
||||
// WrapCategorisedComposeError wraps the error if not nil, otherwise returns nil
|
||||
func WrapCategorisedComposeError(err error, failure FailureCategory) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
return Error{
|
||||
Err: err,
|
||||
Category: &failure,
|
||||
}
|
||||
}
|
||||
|
||||
// Unwrap get underlying error
|
||||
func (e Error) Unwrap() error { return e.Err }
|
||||
|
||||
func (e Error) Error() string { return e.Err.Error() }
|
||||
|
||||
// GetMetricsFailureCategory get metrics status and error code corresponding to this error
|
||||
func (e Error) GetMetricsFailureCategory() FailureCategory {
|
||||
if e.Category != nil {
|
||||
return *e.Category
|
||||
}
|
||||
var pathError *fs.PathError
|
||||
if errors.As(e.Err, &pathError) {
|
||||
return FileNotFoundFailure
|
||||
}
|
||||
if errdefs.IsNotFoundError(e.Err) {
|
||||
return FileNotFoundFailure
|
||||
}
|
||||
return ComposeParseFailure
|
||||
}
|
@ -24,7 +24,7 @@ import (
|
||||
"github.com/docker/cli/cli"
|
||||
"github.com/docker/cli/cli/command/container"
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
moby "github.com/docker/docker/api/types"
|
||||
containerType "github.com/docker/docker/api/types/container"
|
||||
)
|
||||
|
||||
func (s *composeService) Exec(ctx context.Context, projectName string, options api.RunOptions) (int, error) {
|
||||
@ -57,6 +57,6 @@ func (s *composeService) Exec(ctx context.Context, projectName string, options a
|
||||
return 0, err
|
||||
}
|
||||
|
||||
func (s *composeService) getExecTarget(ctx context.Context, projectName string, opts api.RunOptions) (moby.Container, error) {
|
||||
func (s *composeService) getExecTarget(ctx context.Context, projectName string, opts api.RunOptions) (containerType.Summary, error) {
|
||||
return s.getSpecifiedContainer(ctx, projectName, oneOffInclude, false, opts.Service, opts.Index)
|
||||
}
|
||||
|
@ -24,8 +24,7 @@ import (
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
"github.com/docker/compose/v2/pkg/utils"
|
||||
moby "github.com/docker/docker/api/types"
|
||||
containerType "github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
"github.com/docker/docker/api/types/mount"
|
||||
"github.com/docker/docker/api/types/network"
|
||||
@ -40,7 +39,7 @@ func (s *composeService) Generate(ctx context.Context, options api.GenerateOptio
|
||||
filtersListNames.Add("name", containerName)
|
||||
filtersListIDs.Add("id", containerName)
|
||||
}
|
||||
containers, err := s.apiClient().ContainerList(ctx, containerType.ListOptions{
|
||||
containers, err := s.apiClient().ContainerList(ctx, container.ListOptions{
|
||||
Filters: filtersListNames,
|
||||
All: true,
|
||||
})
|
||||
@ -48,16 +47,16 @@ func (s *composeService) Generate(ctx context.Context, options api.GenerateOptio
|
||||
return nil, err
|
||||
}
|
||||
|
||||
containersByIds, err := s.apiClient().ContainerList(ctx, containerType.ListOptions{
|
||||
containersByIds, err := s.apiClient().ContainerList(ctx, container.ListOptions{
|
||||
Filters: filtersListIDs,
|
||||
All: true,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, container := range containersByIds {
|
||||
if !utils.Contains(containers, container) {
|
||||
containers = append(containers, container)
|
||||
for _, ctr := range containersByIds {
|
||||
if !utils.Contains(containers, ctr) {
|
||||
containers = append(containers, ctr)
|
||||
}
|
||||
}
|
||||
|
||||
@ -68,7 +67,7 @@ func (s *composeService) Generate(ctx context.Context, options api.GenerateOptio
|
||||
return s.createProjectFromContainers(containers, options.ProjectName)
|
||||
}
|
||||
|
||||
func (s *composeService) createProjectFromContainers(containers []moby.Container, projectName string) (*types.Project, error) {
|
||||
func (s *composeService) createProjectFromContainers(containers []container.Summary, projectName string) (*types.Project, error) {
|
||||
project := &types.Project{}
|
||||
services := types.Services{}
|
||||
networks := types.Networks{}
|
||||
@ -92,7 +91,6 @@ func (s *composeService) createProjectFromContainers(containers []moby.Container
|
||||
Image: c.Image,
|
||||
Labels: c.Labels,
|
||||
}
|
||||
|
||||
}
|
||||
service.Scale = increment(service.Scale)
|
||||
|
||||
@ -113,7 +111,7 @@ func (s *composeService) createProjectFromContainers(containers []moby.Container
|
||||
return project, nil
|
||||
}
|
||||
|
||||
func (s *composeService) extractComposeConfiguration(service *types.ServiceConfig, inspect moby.ContainerJSON, volumes types.Volumes, secrets types.Secrets, networks types.Networks) {
|
||||
func (s *composeService) extractComposeConfiguration(service *types.ServiceConfig, inspect container.InspectResponse, volumes types.Volumes, secrets types.Secrets, networks types.Networks) {
|
||||
service.Environment = types.NewMappingWithEquals(inspect.Config.Env)
|
||||
if inspect.Config.Healthcheck != nil {
|
||||
healthConfig := inspect.Config.Healthcheck
|
||||
@ -145,7 +143,7 @@ func (s *composeService) extractComposeConfiguration(service *types.ServiceConfi
|
||||
}
|
||||
}
|
||||
|
||||
func (s *composeService) toComposeHealthCheck(healthConfig *containerType.HealthConfig) *types.HealthCheckConfig {
|
||||
func (s *composeService) toComposeHealthCheck(healthConfig *container.HealthConfig) *types.HealthCheckConfig {
|
||||
var healthCheck types.HealthCheckConfig
|
||||
healthCheck.Test = healthConfig.Test
|
||||
if healthConfig.Timeout != 0 {
|
||||
@ -171,8 +169,9 @@ func (s *composeService) toComposeHealthCheck(healthConfig *containerType.Health
|
||||
return &healthCheck
|
||||
}
|
||||
|
||||
func (s *composeService) toComposeVolumes(volumes []moby.MountPoint) (map[string]types.VolumeConfig,
|
||||
[]types.ServiceVolumeConfig, map[string]types.SecretConfig, []types.ServiceSecretConfig) {
|
||||
func (s *composeService) toComposeVolumes(volumes []container.MountPoint) (map[string]types.VolumeConfig,
|
||||
[]types.ServiceVolumeConfig, map[string]types.SecretConfig, []types.ServiceSecretConfig,
|
||||
) {
|
||||
volumeConfigs := make(map[string]types.VolumeConfig)
|
||||
secretConfigs := make(map[string]types.SecretConfig)
|
||||
var serviceVolumeConfigs []types.ServiceVolumeConfig
|
||||
@ -227,7 +226,6 @@ func (s *composeService) toComposeNetwork(networks map[string]*network.EndpointS
|
||||
networkConfigs[name] = types.NetworkConfig{
|
||||
Internal: inspect.Internal,
|
||||
}
|
||||
|
||||
}
|
||||
serviceNetworkConfigs[name] = &types.ServiceNetworkConfig{
|
||||
Aliases: net.Aliases,
|
||||
|
@ -41,3 +41,24 @@ func ServiceHash(o types.ServiceConfig) (string, error) {
|
||||
}
|
||||
return digest.SHA256.FromBytes(bytes).Encoded(), nil
|
||||
}
|
||||
|
||||
// NetworkHash computes the configuration hash for a network.
|
||||
func NetworkHash(o *types.NetworkConfig) (string, error) {
|
||||
bytes, err := json.Marshal(o)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return digest.SHA256.FromBytes(bytes).Encoded(), nil
|
||||
}
|
||||
|
||||
// VolumeHash computes the configuration hash for a volume.
|
||||
func VolumeHash(o types.VolumeConfig) (string, error) {
|
||||
if o.Driver == "" { // (TODO: jhrotko) This probably should be fixed in compose-go
|
||||
o.Driver = "local"
|
||||
}
|
||||
bytes, err := json.Marshal(o)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return digest.SHA256.FromBytes(bytes).Encoded(), nil
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user