Compare commits

..

No commits in common. "main" and "v2.31.0" have entirely different histories.

207 changed files with 2131 additions and 5334 deletions

2
.github/CODEOWNERS vendored
View File

@ -1,2 +0,0 @@
# global rules
* @docker/compose-maintainers

View File

@ -56,7 +56,7 @@ jobs:
uses: actions/checkout@v4
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
uses: docker/setup-buildx-action@v2
-
name: Run
run: |
@ -71,45 +71,29 @@ 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@v3
uses: docker/setup-qemu-action@v2
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
uses: docker/setup-buildx-action@v2
-
name: Build
uses: docker/bake-action@v6
uses: docker/bake-action@v2
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
@ -121,12 +105,15 @@ jobs:
test:
runs-on: ubuntu-latest
steps:
-
name: Checkout
uses: actions/checkout@v4
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
uses: docker/setup-buildx-action@v2
-
name: Test
uses: docker/bake-action@v6
uses: docker/bake-action@v2
with:
targets: test
set: |
@ -154,19 +141,19 @@ jobs:
- plugin
- standalone
engine:
- 26
- 27
- 28
- 24.0.9
- 25.0.5
- 26.1.4
- 27.4.0
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
@ -174,24 +161,22 @@ 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@v3
- name: Set up Go
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
-
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@v6
-
name: Build
uses: docker/bake-action@v2
with:
source: .
targets: binary-with-coverage
set: |
*.cache-from=type=gha,scope=binary-linux-amd64
@ -199,37 +184,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
@ -299,11 +284,7 @@ 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
if [[ "${file#\*}" == docker-compose-* && "${file#\*}" != *.provenance.json && "${file#\*}" != *.sbom.json ]]; then
echo "$sum $file" > ${file#\*}.sha256
fi
done
cat checksums.txt | while read sum file; do echo "$sum $file" > ${file#\*}.sha256; done
-
name: License
run: cp packaging/* ./bin/release/

View File

@ -44,7 +44,7 @@ jobs:
retention-days: 1
validate:
uses: docker/docs/.github/workflows/validate-upstream.yml@main
uses: docker/docs/.github/workflows/validate-upstream.yml@919a9b9104a34a40b30d116529bcce589a544d1c # pin for artifact v4 support: https://github.com/docker/docs/pull/19220
needs:
- docs-yaml
with:

View File

@ -82,23 +82,16 @@ 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@v3
uses: docker/setup-qemu-action@v2
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
uses: docker/setup-buildx-action@v2
-
name: Docker meta
id: meta
uses: docker/metadata-action@v5
uses: docker/metadata-action@v4
with:
images: |
${{ env.REPO_SLUG }}
@ -106,22 +99,28 @@ 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@v6
uses: docker/bake-action@v2
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
@ -130,16 +129,14 @@ jobs:
-
name: Generate Token
id: generate_token
uses: actions/create-github-app-token@v1
uses: tibdex/github-app-token@v1
with:
app-id: ${{ vars.DOCKERDESKTOP_APP_ID }}
private-key: ${{ secrets.DOCKERDESKTOP_APP_PRIVATEKEY }}
owner: docker
repositories: |
${{ secrets.DOCKERDESKTOP_REPO }}
app_id: ${{ vars.DOCKERDESKTOP_APP_ID }}
private_key: ${{ secrets.DOCKERDESKTOP_APP_PRIVATEKEY }}
repository: docker/${{ secrets.DOCKERDESKTOP_REPO }}
-
name: Trigger Docker Desktop e2e with edge version
uses: actions/github-script@v7
uses: actions/github-script@v6
with:
github-token: ${{ steps.generate_token.outputs.token }}
script: |

View File

@ -22,12 +22,12 @@ jobs:
steps:
- name: "Checkout code"
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # tag=v4.4.2
uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846 # tag=v3.0.0
with:
persist-credentials: false
- name: "Run analysis"
uses: ossf/scorecard-action@62b2cac7ed8198b15735ed49ab1e5cf35480ba46 # tag=v2.4.0
uses: ossf/scorecard-action@99c53751e09b9529366343771cc321ec74e9bd3d # tag=v2.0.6
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@6f51ac03b9356f520e9adb1b1b7802705f340c2b # tag=v4.5.0
uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 # tag=v3.0.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@3096afedf9873361b2b2f65e1445b13272c83eb8 # tag=v2.20.00
uses: github/codeql-action/upload-sarif@5f532563584d71fdef14ee64d17bafb34f751ce5 # tag=v1.0.26
with:
sarif_file: results.sarif

View File

@ -1,83 +1,82 @@
version: "2"
run:
concurrency: 2
timeout: 10m
linters:
default: none
enable-all: false
disable-all: true
enable:
- copyloopvar
- depguard
- errcheck
- errorlint
- gocritic
- gocyclo
- gofmt
- goimports
- gomodguard
- revive
- gosimple
- govet
- ineffassign
- lll
- misspell
- nakedret
- nolintlint
- revive
- staticcheck
- testifylint
- typecheck
- unconvert
- unparam
- unused
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$
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
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
# Maximum issues count per one linter.
# Set to 0 to disable.
# Default: 50
max-issues-per-linter: 0
# Maximum count of issues with the same text.
# Set to 0 to disable.
# Default: 3
max-same-issues: 0
formatters:
enable:
- gofumpt
- goimports
exclusions:
generated: lax
paths:
- third_party$
- builtin$
- examples$

View File

@ -2,10 +2,10 @@
### Prerequisites
* Windows:
* [Docker Desktop](https://docs.docker.com/desktop/setup/install/windows-install/)
* [Docker Desktop](https://hub.docker.com/editions/community/docker-ce-desktop-windows)
* make
* macOS:
* [Docker Desktop](https://docs.docker.com/desktop/setup/install/mac-install/)
* [Docker Desktop](https://hub.docker.com/editions/community/docker-ce-desktop-mac)
* make
* Linux:
* [Docker 20.10 or later](https://docs.docker.com/engine/install/)

View File

@ -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/).
process](https://docs.docker.com/contribute/overview/).
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/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/docker-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/questions/tagged/docker" target="_blank">Docker questions</a>
monitor <a href="https://stackoverflow.com/search?tab=newest&q=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](https://developercertificate.org/)):
the below (from [developercertificate.org](http://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/main/MAINTAINERS)
[MAINTAINERS](https://github.com/docker/opensource/blob/master/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](https://go.dev/doc/effective_go) and [Go Code Review
Comments](https://go.dev/wiki/CodeReviewComments).
Go](http://golang.org/doc/effective_go.html) and [Go Code Review
Comments](https://github.com/golang/go/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://go.dev/doc/effective_go). The
[Go Blog](https://go.dev/blog/) is also a great resource. Drinking the
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
kool-aid is a lot easier than going thirsty.

View File

@ -15,9 +15,9 @@
# See the License for the specific language governing permissions and
# limitations under the License.
ARG GO_VERSION=1.23.8
ARG XX_VERSION=1.6.1
ARG GOLANGCI_LINT_VERSION=v2.0.2
ARG GO_VERSION=1.22.8
ARG XX_VERSION=1.2.1
ARG GOLANGCI_LINT_VERSION=v1.60.2
ARG ADDLICENSE_VERSION=v1.0.0
ARG BUILD_TAGS="e2e"

View File

@ -116,11 +116,6 @@ 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))

View File

@ -8,7 +8,7 @@
- [Legacy](#legacy)
# Docker Compose v2
[![GitHub release](https://img.shields.io/github/v/release/docker/compose.svg?style=flat-square)](https://github.com/docker/compose/releases/latest)
[![GitHub release](https://img.shields.io/github/release/docker/compose.svg?style=flat-square)](https://github.com/docker/compose/releases/latest)
[![PkgGoDev](https://img.shields.io/badge/go.dev-docs-007d9c?style=flat-square&logo=go&logoColor=white)](https://pkg.go.dev/github.com/docker/compose/v2)
[![Build Status](https://img.shields.io/github/actions/workflow/status/docker/compose/ci.yml?label=ci&logo=github&style=flat-square)](https://github.com/docker/compose/actions?query=workflow%3Aci)
[![Go Report Card](https://goreportcard.com/badge/github.com/docker/compose/v2?style=flat-square)](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

View File

@ -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,14 +115,13 @@ func wrapRunE(c *cobra.Command, cmdSpan trace.Span, tracingShutdown tracing.Shut
}
}
// commandName returns the path components for a given command,
// in reverse alphabetical order for consistent usage metrics.
// commandName returns the path components for a given command.
//
// The root Compose command and anything before (i.e. "docker")
// are not included.
//
// For example:
// - docker compose alpha watch -> [watch, alpha]
// - docker compose alpha watch -> [alpha, watch]
// - docker-compose up -> [up]
func commandName(cmd *cobra.Command) []string {
var name []string

View File

@ -20,8 +20,6 @@ import (
"reflect"
"testing"
commands "github.com/docker/compose/v2/cmd/compose"
"github.com/spf13/cobra"
flag "github.com/spf13/pflag"
)
@ -62,51 +60,5 @@ 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)
}
})
}
}

View File

@ -17,9 +17,6 @@
package compatibility
import (
"errors"
"os"
"os/exec"
"testing"
"gotest.tools/v3/assert"
@ -27,10 +24,9 @@ import (
func Test_convert(t *testing.T) {
tests := []struct {
name string
args []string
want []string
wantErr bool
name string
args []string
want []string
}{
{
name: "compose only",
@ -97,36 +93,11 @@ 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) {
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)
}
got := Convert(tt.args)
assert.DeepEqual(t, tt.want, got)
})
}
}

View File

@ -27,7 +27,6 @@ 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"
@ -45,7 +44,6 @@ type buildOptions struct {
ssh string
builder string
deps bool
print bool
}
func (opts buildOptions) toAPIBuildOptions(services []string) (api.BuildOptions, error) {
@ -78,8 +76,6 @@ 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
@ -135,7 +131,6 @@ 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
}
@ -146,8 +141,6 @@ 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
}
@ -157,23 +150,6 @@ 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()
}

View File

@ -1,57 +0,0 @@
/*
Copyright 2020 Docker Compose CLI authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package 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)
}

View File

@ -90,13 +90,3 @@ 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
}
}

View File

@ -44,6 +44,7 @@ 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"
@ -72,16 +73,17 @@ 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, vars map[string]string, lookup func(key string) (string, bool)) error {
func rawEnv(r io.Reader, filename string, lookup func(key string) (string, bool)) (map[string]string, error) {
lines, err := kvfile.ParseFromReader(r, lookup)
if err != nil {
return fmt.Errorf("failed to parse env_file %s: %w", filename, err)
return nil, 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 nil
return vars, nil
}
func init() {
@ -119,9 +121,17 @@ 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 {
@ -169,7 +179,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.WithoutEnvironmentResolution,
cli.WithDiscardEnvFile,
}
project, metrics, err := o.ToProject(ctx, dockerCli, args, options...)
@ -179,11 +189,6 @@ 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)
})
}
@ -302,7 +307,7 @@ func (o *ProjectOptions) ToProject(ctx context.Context, dockerCli command.Cli, s
options, err := o.toProjectOptions(po...)
if err != nil {
return nil, metrics, err
return nil, metrics, compose.WrapComposeError(err)
}
options.WithListeners(func(event string, metadata map[string]any) {
@ -334,7 +339,7 @@ func (o *ProjectOptions) ToProject(ctx context.Context, dockerCli command.Cli, s
project, err := options.LoadProject(ctx)
if err != nil {
return nil, metrics, err
return nil, metrics, compose.WrapComposeError(err)
}
if project.Name == "" {
@ -376,24 +381,17 @@ func (o *ProjectOptions) remoteLoaders(dockerCli command.Cli) []loader.ResourceL
if o.Offline {
return nil
}
git := remote.NewGitRemoteLoader(dockerCli, o.Offline)
git := remote.NewGitRemoteLoader(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
@ -455,7 +453,7 @@ func RootCommand(dockerCli command.Cli, backend Backend) *cobra.Command { //noli
}
_ = cmd.Help()
return dockercli.StatusError{
StatusCode: 1,
StatusCode: compose.CommandSyntaxFailure.ExitCode,
Status: fmt.Sprintf("unknown docker command: %q", "compose "+args[0]),
}
},
@ -549,7 +547,10 @@ func RootCommand(dockerCli command.Cli, backend Backend) *cobra.Command { //noli
}
composeCmd := cmd
for composeCmd.Name() != PluginName {
for {
if composeCmd.Name() == PluginName {
break
}
if !composeCmd.HasParent() {
return fmt.Errorf("error parsing command line, expected %q", PluginName)
}
@ -633,7 +634,6 @@ 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),
)

View File

@ -47,7 +47,6 @@ type configOptions struct {
noInterpolate bool
noNormalize bool
noResolvePath bool
noResolveEnv bool
services bool
volumes bool
profiles bool
@ -136,7 +135,6 @@ 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.")
@ -192,13 +190,6 @@ 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 {
@ -357,6 +348,7 @@ func runHash(ctx context.Context, dockerCli command.Cli, opts configOptions) err
}
hash, err := compose.ServiceHash(s)
if err != nil {
return err
}

View File

@ -26,9 +26,7 @@ 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"
)
@ -48,7 +46,6 @@ type createOptions struct {
timeout int
quietPull bool
scale []string
AssumeYes bool
}
func createCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
@ -83,15 +80,6 @@ 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
}
@ -119,7 +107,6 @@ func runCreate(ctx context.Context, _ command.Cli, backend api.Service, createOp
Inherit: !createOpts.noInherit,
Timeout: createOpts.GetTimeout(),
QuietPull: createOpts.quietPull,
AssumeYes: createOpts.AssumeYes,
})
}
@ -208,9 +195,7 @@ 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)
}

View File

@ -40,9 +40,7 @@ func TestRunCreate(t *testing.T) {
)
createOpts := createOptions{}
buildOpts := buildOptions{
ProjectOptions: &ProjectOptions{},
}
buildOpts := buildOptions{}
project := sampleProject()
err := runCreate(ctx, nil, backend, createOpts, buildOpts, project, nil)
require.NoError(t, err)
@ -60,9 +58,7 @@ func TestRunCreate_Build(t *testing.T) {
createOpts := createOptions{
Build: true,
}
buildOpts := buildOptions{
ProjectOptions: &ProjectOptions{},
}
buildOpts := buildOptions{}
project := sampleProject()
err := runCreate(ctx, nil, backend, createOpts, buildOpts, project, nil)
require.NoError(t, err)

View File

@ -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() //nolint:staticcheck
projectOptions, err := opts.composeOptions.toProjectOptions()
if err != nil {
return err
}

View File

@ -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 project names")
lsCmd.Flags().BoolVarP(&lsOpts.Quiet, "quiet", "q", false, "Only display IDs")
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")

View File

@ -17,21 +17,9 @@
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"
)
@ -84,208 +72,3 @@ 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
}

View File

@ -17,20 +17,10 @@
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) {
@ -138,257 +128,3 @@ 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()
})
}
}

View File

@ -18,22 +18,17 @@ package compose
import (
"context"
"errors"
"github.com/docker/cli/cli"
"github.com/docker/cli/cli/command"
"github.com/docker/compose/v2/pkg/api"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"github.com/docker/compose/v2/pkg/api"
)
type publishOptions struct {
*ProjectOptions
resolveImageDigests bool
ociVersion string
withEnvironment bool
assumeYes bool
}
func publishCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command {
@ -46,39 +41,22 @@ 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: cli.ExactArgs(1),
Args: cobra.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.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)
})
flags.StringVar(&opts.ociVersion, "oci-version", "", "OCI Image/Artifact specification version (automatically determined by default)")
return cmd
}
func runPublish(ctx context.Context, dockerCli command.Cli, backend api.Service, opts publishOptions, repository string) error {
project, metrics, err := opts.ToProject(ctx, dockerCli, nil)
project, _, 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,
})
}

View File

@ -48,15 +48,12 @@ func pullCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service)
cmd := &cobra.Command{
Use: "pull [OPTIONS] [SERVICE...]",
Short: "Pull service images",
PreRunE: func(cmd *cobra.Command, args []string) error {
if cmd.Flags().Changed("no-parallel") {
PreRunE: Adapt(func(ctx context.Context, args []string) error {
if opts.noParallel {
fmt.Fprint(os.Stderr, aec.Apply("option '--no-parallel' is DEPRECATED and will be ignored.\n", aec.RedF))
}
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)
}),
@ -67,7 +64,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.noParallel, "no-parallel", true, "DEPRECATED disable parallel pulling")
cmd.Flags().BoolVar(&opts.parallel, "no-parallel", true, "DEPRECATED disable parallel pulling")
flags.MarkHidden("no-parallel") //nolint:errcheck
cmd.Flags().BoolVar(&opts.ignorePullFailures, "ignore-pull-failures", false, "Pull what it can and ignores images with pull failures")
cmd.Flags().BoolVar(&opts.noBuildable, "ignore-buildable", false, "Ignore images that can be built")

View File

@ -19,10 +19,8 @@ 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"
@ -46,7 +44,6 @@ type runOptions struct {
Service string
Command []string
environment []string
envFiles []string
Detach bool
Remove bool
noTty bool
@ -67,7 +64,6 @@ type runOptions struct {
noDeps bool
ignoreOrphans bool
removeOrphans bool
quiet bool
quietPull bool
}
@ -120,29 +116,6 @@ 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{
@ -181,24 +154,10 @@ 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.WithoutEnvironmentResolution)
if err != nil {
return err
}
project, err = project.WithServicesEnvironmentResolved(true)
project, _, err := p.ToProject(ctx, dockerCli, []string{options.Service}, cgo.WithResolvedPaths(true), cgo.WithDiscardEnvFile)
if err != nil {
return err
}
@ -215,7 +174,6 @@ 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)")
@ -230,9 +188,6 @@ 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(&options.removeOrphans, "remove-orphans", false, "Remove containers for services not defined in the Compose file")
@ -267,10 +222,6 @@ 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 {
@ -307,11 +258,6 @@ 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,
@ -326,7 +272,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: environment.Values(),
Environment: options.environment,
Entrypoint: options.entrypointCmd,
Labels: labels,
UseNetworkAliases: options.useAliases,

View File

@ -51,7 +51,7 @@ func scaleCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service)
}
return runScale(ctx, dockerCli, backend, opts, serviceTuples)
}),
ValidArgsFunction: completeScaleArgs(dockerCli, p),
ValidArgsFunction: completeServiceNames(dockerCli, p),
}
flags := scaleCmd.Flags()
flags.BoolVar(&opts.noDeps, "no-deps", false, "Don't start linked services")
@ -92,6 +92,7 @@ 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)
}

View File

@ -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/engine/cli/formatting/ for more information about formatting output with templates`)
Refer to https://docs.docker.com/go/formatting/ for more information about formatting output with templates`)
flags.BoolVar(&opts.noStream, "no-stream", false, "Disable streaming stats and only pull the first result")
flags.BoolVar(&opts.noTrunc, "no-trunc", false, "Do not truncate output")
return cmd

View File

@ -49,11 +49,6 @@ 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 {
@ -68,76 +63,30 @@ func runTop(ctx context.Context, dockerCli command.Cli, backend api.Service, opt
return containers[i].Name < containers[j].Name
})
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,
}
for i, title := range container.Titles {
if _, exists := header[title]; !exists {
header[title] = len(header)
_, _ = 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)
}
entry[title] = proc[i]
_, _ = fmt.Fprintf(w, strings.Repeat("%s\t", len(info))+"\n", info...)
}
entries = append(entries, entry)
_, _ = fmt.Fprintln(w)
},
container.Titles...)
if err != nil {
return err
}
}
// 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
return nil
}
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"))
}
func psPrinter(out io.Writer, printer func(writer io.Writer), headers ...string) error {
w := tabwriter.NewWriter(out, 5, 1, 3, ' ', 0)
_, _ = fmt.Fprintln(w, strings.Join(headers, "\t"))
printer(w)
return w.Flush()
}

View File

@ -1,329 +0,0 @@
/*
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()
}

View File

@ -27,9 +27,7 @@ import (
"github.com/compose-spec/compose-go/v2/types"
"github.com/docker/cli/cli/command"
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"
@ -172,15 +170,7 @@ func upCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *c
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
}
@ -202,11 +192,7 @@ 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) {
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")
}
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")
@ -233,10 +219,6 @@ 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
@ -273,7 +255,6 @@ func runUp(
Inherit: !createOptions.noInherit,
Timeout: createOptions.GetTimeout(),
QuietPull: createOptions.quietPull,
AssumeYes: createOptions.AssumeYes,
}
if upOptions.noStart {

View File

@ -47,4 +47,5 @@ func TestApplyScaleOpt(t *testing.T) {
assert.NilError(t, err)
assert.Equal(t, *bar.Scale, 3)
assert.Equal(t, *bar.Deploy.Replicas, 3)
}

View File

@ -1,76 +0,0 @@
/*
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())
})
}
}

View File

@ -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", true, "Prune dangling images on rebuild")
cmd.Flags().BoolVar(&watchOpts.prune, "prune", false, "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, services)
project, _, err := watchOpts.ToProject(ctx, dockerCli, nil)
if err != nil {
return err
}

View File

@ -27,49 +27,42 @@ 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
@ -77,7 +70,6 @@ func ClearLine() {
// Does not move cursor from its current position
fmt.Print(ansi("[2K"))
}
func MoveCursorUp(lines int) {
if disableAnsi {
return
@ -85,7 +77,6 @@ func MoveCursorUp(lines int) {
// Does not add new lines
fmt.Print(ansi(fmt.Sprintf("[%dA", lines)))
}
func MoveCursorDown(lines int) {
if disableAnsi {
return
@ -93,12 +84,10 @@ 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

View File

@ -104,12 +104,10 @@ func makeColorFunc(code string) colorFunc {
}
}
var (
nextColor = rainbowColor
rainbow []colorFunc
currentIndex = 0
mutex sync.Mutex
)
var nextColor = rainbowColor
var rainbow []colorFunc
var currentIndex = 0
var mutex sync.Mutex
func rainbowColor() colorFunc {
mutex.Lock()

View File

@ -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/container"
"github.com/docker/docker/api/types"
"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 []container.Port
var ports []types.Port
for _, publisher := range c.c.Publishers {
ports = append(ports, container.Port{
ports = append(ports, types.Port{
IP: publisher.URL,
PrivatePort: uint16(publisher.TargetPort),
PublicPort: uint16(publisher.PublishedPort),

View File

@ -110,10 +110,8 @@ type LogKeyboard struct {
signalChannel chan<- os.Signal
}
var (
KeyboardManager *LogKeyboard
eg multierror.Group
)
var KeyboardManager *LogKeyboard
var eg multierror.Group
func NewKeyboardManager(ctx context.Context, isDockerDesktopActive, isWatchConfigured bool,
sc chan<- os.Signal,
@ -208,7 +206,7 @@ func (lk *LogKeyboard) navigationMenu() string {
if openDDInfo != "" || openDDUI != "" {
watchInfo = navColor(" ")
}
isEnabled := " Enable"
var isEnabled = " Enable"
if lk.Watch.Watching {
isEnabled = " Disable"
}
@ -238,7 +236,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
@ -255,21 +253,20 @@ 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
@ -299,7 +296,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
}
@ -307,15 +304,10 @@ func (lk *LogKeyboard) StartWatch(ctx context.Context, doneCh chan bool, project
lk.Watch.newContext(ctx)
buildOpts := *options.Create.Build
buildOpts.Quiet = true
err := lk.Watch.WatchFn(lk.Watch.Ctx, doneCh, project, options.Start.Services, api.WatchOptions{
return 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
}))
}
}

View File

@ -62,7 +62,7 @@ func pluginMain() {
cmd.SetFlagErrorFunc(func(c *cobra.Command, err error) error {
return dockercli.StatusError{
StatusCode: 1,
StatusCode: compose.CommandSyntaxFailure.ExitCode,
Status: err.Error(),
}
})

View File

@ -28,7 +28,6 @@ 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 |
@ -68,7 +67,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](/reference/compose-file/).
Use the `-f` flag to specify the location of a Compose configuration file.
#### Specifying multiple Compose files
You can supply multiple `-f` configuration files. When you supply multiple files, Compose combines them into a single
@ -78,10 +77,10 @@ to their predecessors.
For example, consider this command line:
```console
$ docker compose -f compose.yaml -f compose.admin.yaml run backup_db
$ docker compose -f docker-compose.yml -f docker-compose.admin.yml run backup_db
```
The `compose.yaml` file might specify a `webapp` service.
The `docker-compose.yml` file might specify a `webapp` service.
```yaml
services:
@ -92,7 +91,7 @@ services:
volumes:
- "/data"
```
If the `compose.admin.yaml` also specifies this same service, any matching fields override the previous file.
If the `docker-compose.admin.yml` also specifies this same service, any matching fields override the previous file.
New values, add to the `webapp` service configuration.
```yaml
@ -207,4 +206,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.

View File

@ -8,10 +8,8 @@ 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-->

View File

@ -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/main/spec.md#image) name,
[image](https://github.com/compose-spec/compose-spec/blob/master/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/main/spec.md#interpolation).
[variable interpolation](https://github.com/compose-spec/compose-spec/blob/master/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,7 +20,6 @@ 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 |
@ -35,9 +34,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/main/spec.md#image) name,
[image](https://github.com/compose-spec/compose-spec/blob/master/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/main/spec.md#interpolation).
[variable interpolation](https://github.com/compose-spec/compose-spec/blob/master/spec.md#interpolation).
If you change a service's `Dockerfile` or the contents of its build directory,
run `docker compose build` to rebuild it.

View File

@ -19,7 +19,6 @@ 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 |

View File

@ -16,7 +16,6 @@ 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-->

View File

@ -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 project names |
| `-q`, `--quiet` | `bool` | | Only display IDs |
<!---MARKER_GEN_END-->

View File

@ -1,18 +0,0 @@
# 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-->

View File

@ -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/main/spec.md#restart)
or [restart_policy](https://github.com/compose-spec/compose-spec/blob/main/deploy.md#restart_policy).
[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).
### 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/main/spec.md#restart)
or [restart_policy](https://github.com/compose-spec/compose-spec/blob/main/deploy.md#restart_policy).
[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).

View File

@ -57,33 +57,29 @@ 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 |
| `--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 |
| 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 |
<!---MARKER_GEN_END-->

View File

@ -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/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 |
| 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 |
<!---MARKER_GEN_END-->

View File

@ -53,7 +53,6 @@ If the process is interrupted using `SIGINT` (ctrl + C) or `SIGTERM`, the contai
| `--wait` | `bool` | | Wait for services to be running\|healthy. Implies detached mode. |
| `--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-->

View File

@ -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` | `true` | Prune dangling images on rebuild |
| `--prune` | `bool` | | Prune dangling images on rebuild |
| `--quiet` | `bool` | | hide build output |

View File

@ -22,7 +22,6 @@ cname:
- docker compose pause
- docker compose port
- docker compose ps
- docker compose publish
- docker compose pull
- docker compose push
- docker compose restart
@ -56,7 +55,6 @@ 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
@ -231,7 +229,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](/reference/compose-file/).
Use the `-f` flag to specify the location of a Compose configuration file.
#### Specifying multiple Compose files
You can supply multiple `-f` configuration files. When you supply multiple files, Compose combines them into a single
@ -241,10 +239,10 @@ examples: |-
For example, consider this command line:
```console
$ docker compose -f compose.yaml -f compose.admin.yaml run backup_db
$ docker compose -f docker-compose.yml -f docker-compose.admin.yml run backup_db
```
The `compose.yaml` file might specify a `webapp` service.
The `docker-compose.yml` file might specify a `webapp` service.
```yaml
services:
@ -255,7 +253,7 @@ examples: |-
volumes:
- "/data"
```
If the `compose.admin.yaml` also specifies this same service, any matching fields override the previous file.
If the `docker-compose.admin.yml` also specifies this same service, any matching fields override the previous file.
New values, add to the `webapp` service configuration.
```yaml

View File

@ -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,27 +25,6 @@ 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

View File

@ -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/main/spec.md#image) name,
[image](https://github.com/compose-spec/compose-spec/blob/master/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/main/spec.md#interpolation).
[variable interpolation](https://github.com/compose-spec/compose-spec/blob/master/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,16 +96,6 @@ 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

View File

@ -59,16 +59,6 @@ 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"

View File

@ -88,17 +88,6 @@ 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

View File

@ -39,7 +39,7 @@ options:
shorthand: q
value_type: bool
default_value: "false"
description: Only display project names
description: Only display IDs
deprecated: false
hidden: false
experimental: false

View File

@ -1,66 +0,0 @@
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

View File

@ -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/main/spec.md#restart)
or [restart_policy](https://github.com/compose-spec/compose-spec/blob/main/deploy.md#restart_policy).
[restart](https://github.com/compose-spec/compose-spec/blob/master/spec.md#restart)
or [restart_policy](https://github.com/compose-spec/compose-spec/blob/master/deploy.md#restart_policy).
usage: docker compose restart [OPTIONS] [SERVICE...]
pname: docker compose
plink: docker_compose.yaml

View File

@ -117,16 +117,6 @@ 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
@ -190,37 +180,6 @@ 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"

View File

@ -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/engine/cli/formatting/ for more information about formatting output with templates
Refer to https://docs.docker.com/go/formatting/ for more information about formatting output with templates
deprecated: false
hidden: false
experimental: false

View File

@ -309,17 +309,6 @@ 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

View File

@ -19,7 +19,7 @@ options:
swarm: false
- option: prune
value_type: bool
default_value: "true"
default_value: "false"
description: Prune dangling images on rebuild
deprecated: false
hidden: false

189
go.mod
View File

@ -1,97 +1,96 @@
module github.com/docker/compose/v2
go 1.23.8
go 1.22.7
toolchain go1.23.3
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.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/compose-spec/compose-go/v2 v2.4.5
github.com/containerd/containerd v1.7.24
github.com/containerd/platforms v0.2.1
github.com/davecgh/go-spew v1.1.1
github.com/distribution/reference v0.6.0
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/buildx v0.18.0
github.com/docker/cli v27.4.0-rc.2+incompatible
github.com/docker/cli-docs-tool v0.8.0
github.com/docker/docker v27.4.0-rc.2+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.7.0
github.com/google/go-cmp v0.6.0
github.com/hashicorp/go-multierror v1.1.1
github.com/hashicorp/go-version v1.7.0
github.com/jonboulle/clockwork v0.5.0
github.com/jonboulle/clockwork v0.4.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.20.1
github.com/moby/buildkit v0.17.2
github.com/moby/patternmatcher v0.6.0
github.com/moby/term v0.5.2
github.com/moby/term v0.5.0
github.com/morikuni/aec v1.0.0
github.com/opencontainers/go-digest v1.0.0
github.com/opencontainers/image-spec v1.1.1
github.com/otiai10/copy v1.14.1
github.com/opencontainers/image-spec v1.1.0
github.com/otiai10/copy v1.14.0
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.9.1
github.com/spf13/pflag v1.0.6
github.com/spf13/cobra v1.8.1
github.com/spf13/pflag v1.0.5
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.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.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.uber.org/goleak v1.3.0
go.uber.org/mock v0.5.0
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
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0
golang.org/x/sync v0.9.0
golang.org/x/sys v0.27.0
google.golang.org/grpc v1.68.0
gopkg.in/yaml.v3 v3.0.1
gotest.tools/v3 v3.5.2
tags.cncf.io/container-device-interface v1.0.1
gotest.tools/v3 v3.5.1
tags.cncf.io/container-device-interface v0.8.0
)
require (
dario.cat/mergo v1.0.1 // 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/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 // indirect
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
github.com/Masterminds/semver/v3 v3.2.1 // indirect
github.com/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/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/beorn7/perks v1.0.1 // indirect
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
github.com/cenkalti/backoff/v4 v4.2.1 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/containerd/console v1.0.4 // 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/containerd/api v1.7.19 // indirect
github.com/containerd/continuity v0.4.4 // indirect
github.com/containerd/errdefs v0.3.0 // indirect
github.com/containerd/log v0.1.0 // 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/containerd/ttrpc v1.2.5 // indirect
github.com/containerd/typeurl/v2 v2.2.0 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.5 // 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
@ -99,16 +98,14 @@ 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/fxamacker/cbor/v2 v2.7.0 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/logr v1.4.1 // 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.4 // indirect
github.com/go-openapi/swag v0.22.3 // indirect
github.com/go-viper/mapstructure/v2 v2.0.0 // indirect
github.com/gofrs/flock v0.12.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
@ -116,47 +113,45 @@ 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.22.0 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.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.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.20 // indirect
github.com/mattn/go-isatty v0.0.17 // 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.4.0 // indirect
github.com/moby/spdystream v0.2.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
github.com/moby/sys/symlink v0.3.0 // indirect
github.com/moby/sys/symlink v0.2.0 // indirect
github.com/moby/sys/user v0.3.0 // indirect
github.com/moby/sys/userns v0.1.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/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/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/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/prometheus/procfs v0.15.1 // indirect
github.com/rivo/uniseg v0.2.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
@ -164,46 +159,40 @@ require (
github.com/serialx/hashring v0.0.0-20200727003509-22c0c7ab6b1b // indirect
github.com/shibumi/go-pathspec v1.3.0 // 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/fsutil v0.0.0-20241028165955-397af5306b5c // 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
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
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.27.0 // indirect
golang.org/x/net v0.29.0 // indirect
golang.org/x/oauth2 v0.23.0 // indirect
golang.org/x/term v0.24.0 // indirect
golang.org/x/text v0.18.0 // indirect
golang.org/x/time v0.6.0 // 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
google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 // indirect
google.golang.org/protobuf v1.35.1 // 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.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
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
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.4.0 // indirect
sigs.k8s.io/yaml v1.3.0 // indirect
)
replace github.com/compose-spec/compose-go/v2 => github.com/glours/compose-go/v2 v2.0.0-20250403082600-80aa75f06535

450
go.sum
View File

@ -1,23 +1,25 @@
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-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/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/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-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg=
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
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/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.12.9 h1:2zJy5KA+l0loz1HzEGqyNnjd3fyZA31ZBCGKacp6lLg=
github.com/Microsoft/hcsshim v0.12.9/go.mod h1:fJ0gkFAna6ukt0bLdKB8djt4XIJhF/vEPuoIWYVvZ8Y=
github.com/Microsoft/hcsshim v0.12.5 h1:bpTInLlDy/nDRWFVcefDZZ1+U8tS+rz3MxjKgu9boo0=
github.com/Microsoft/hcsshim v0.12.5/go.mod h1:tIUGego4G1EN5Hb6KC90aDYiUI2dqLSTTOCjVNpOgZ8=
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=
@ -29,36 +31,34 @@ 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.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/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/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,69 +75,70 @@ 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.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
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/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-20240905190251-b4127c9b8d78 h1:QVw89YDxXxEe+l8gU8ETbOasdwEV+avkR75ZzsVV9WI=
github.com/cncf/xds/go v0.0.0-20240905190251-b4127c9b8d78/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/containerd/cgroups/v3 v3.0.5 h1:44na7Ud+VwyE7LIoJ8JTNQOa549a8543BmzaJHo6Bzo=
github.com/containerd/cgroups/v3 v3.0.5/go.mod h1:SA5DLYnXO8pTGYiAHXz94qvLQTKfVM5GEVisn4jpins=
github.com/compose-spec/compose-go/v2 v2.4.5 h1:p4ih4Jb6VgGPLPxh3fSFVKAjFHtZd+7HVLCSFzcFx9Y=
github.com/compose-spec/compose-go/v2 v2.4.5/go.mod h1:lFN0DrMxIncJGYAXTfWuajfwj5haBJqrBkarHcnjJKc=
github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM=
github.com/containerd/cgroups/v3 v3.0.2 h1:f5WFqIVSgo5IZmtTT3qVBo6TzI1ON6sycSBKkymb9L0=
github.com/containerd/cgroups/v3 v3.0.2/go.mod h1:JUgITrzdFqp42uI2ryGA+ge0ap/nxzYgkGmIcetmErE=
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/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/containerd v1.7.24 h1:zxszGrGjrra1yYJW/6rhm9cJ1ZQ8rkKBR48brqsa7nA=
github.com/containerd/containerd v1.7.24/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.4 h1:/fNVfTJ7wIl/YPMHjf+5H32uFhl63JucB34PlCpMKII=
github.com/containerd/continuity v0.4.4/go.mod h1:/lNJvtJKUQStBzpVQ1+rasXO1LAWtUQssk28EZvJ3nE=
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/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.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/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/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc=
github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
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.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s=
github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE=
github.com/creack/pty v1.1.21 h1:1/QdRyBaHHJP61QkWMXlOIBfsgdDeeKfK8SYVUWJKf0=
github.com/creack/pty v1.1.21/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
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.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/buildx v0.18.0 h1:rSauXHeJt90NvtXrLK5J992Eb0UPJZs2vV3u1zTf1nE=
github.com/docker/buildx v0.18.0/go.mod h1:JGNSshOhHs5FhG3u51jXUf4lLOeD2QBIlJ2vaRB67p4=
github.com/docker/cli v27.4.0-rc.2+incompatible h1:A0GZwegDlt2wdt3tpmrUzkVOZmbhvd7i05wPSf7Oo74=
github.com/docker/cli v27.4.0-rc.2+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/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 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 v27.4.0-rc.2+incompatible h1:9OJjVGtelk/zGC3TyKweJ29b9Axzh0s/0vtU4mneumE=
github.com/docker/docker v27.4.0-rc.2+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=
@ -145,6 +146,8 @@ 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=
@ -157,6 +160,8 @@ 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=
@ -165,30 +170,26 @@ 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.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
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/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/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-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-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=
@ -197,9 +198,9 @@ github.com/gogo/protobuf v1.0.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7a
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=
@ -214,13 +215,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.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
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/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-20240525223248-4bfdf5a9a2af h1:kmjWCqn2qkEml422C2Rrd27c3VGxi6a/6HNq8QmHRKM=
github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo=
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/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=
@ -228,10 +229,11 @@ 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.22.0 h1:asbCHRVmodnJTuQ3qamDwqVOIjwqUPTYmYuemVOx+Ys=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0/go.mod h1:ggCgvZ2r7uOoQjOyu2Y1NhHmEPPzzuhWgcza5M1Ji1I=
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/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=
@ -253,15 +255,13 @@ 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.5.0 h1:Hyh9A8u51kptdkR+cqRpT1EebBwTn1oK9YfGYbdFz6I=
github.com/jonboulle/clockwork v0.5.0/go.mod h1:3mZlmanh0g2NDKO5TWZVJAfofYk64M7XN3SzBPjZF60=
github.com/jonboulle/clockwork v0.4.0 h1:p4Cf1aMWXnXAUh8lVfewRBx1zaTSYKrKMF2g3ST4RZ4=
github.com/jonboulle/clockwork v0.4.0/go.mod h1:xgRqUGwRcjKCO1vbZUEtSLrqKoPSsUpK7fnezOII0kc=
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=
@ -287,12 +287,9 @@ 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=
@ -300,14 +297,16 @@ 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.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
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-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=
@ -320,16 +319,16 @@ 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.20.1 h1:sT0ZXhhNo5rVbMcYfgttma3TdUHfO5JjFA0UAL8p9fY=
github.com/moby/buildkit v0.20.1/go.mod h1:Rq9nB/fJImdk6QeM0niKtOHJqwKeYMrK847hTTDVuA4=
github.com/moby/buildkit v0.17.2 h1:/jgk/MuXbA7jeXMkknOpHYB+Ct4aNvQHkBB7SxD3D4U=
github.com/moby/buildkit v0.17.2/go.mod h1:vr5vltV8wt4F2jThbNOChfbAklJ0DOW11w36v210hOg=
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.4.0 h1:Vy79D6mHeJJjiPdFEL2yku1kl0chZpJfZcPpb16BRl8=
github.com/moby/spdystream v0.4.0/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI=
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/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=
@ -338,14 +337,14 @@ github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7z
github.com/moby/sys/sequential v0.6.0/go.mod h1:uyv8EUTrca5PnDsdMGXhZe6CCe8U/UiTWd+lL+7b/Ko=
github.com/moby/sys/signal v0.7.1 h1:PrQxdvxcGijdo6UXXo/lU/TvHUWyPhj7UOpSo8tuvk0=
github.com/moby/sys/signal v0.7.1/go.mod h1:Se1VGehYokAkrSQwL4tDzHvETwUZlnY7S5XtQ50mQp8=
github.com/moby/sys/symlink v0.3.0 h1:GZX89mEZ9u53f97npBy4Rc3vJKj7JBDj/PN2I22GrNU=
github.com/moby/sys/symlink v0.3.0/go.mod h1:3eNdhduHmYPcgsJtZXW1W4XUJdZGBIkttZ8xKqPUJq0=
github.com/moby/sys/symlink v0.2.0 h1:tk1rOM+Ljp0nFmfOIBtlV3rTDlWOwFRhjEeAhZB0nZc=
github.com/moby/sys/symlink v0.2.0/go.mod h1:7uZVF2dqJjG/NsClqul95CqKOBRQyYSNnJ6BMgR/gFs=
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.2 h1:6qk3FJAFDs6i/q3W/pQ97SX192qKfZgGjCQqfCJkgzQ=
github.com/moby/term v0.5.2/go.mod h1:d3djjFCrjnB+fl8NJux+EJzu0msscUP+f8it8hPkFLc=
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/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=
@ -364,28 +363,28 @@ 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.19.0 h1:9Cnnf7UHo57Hy3k6/m5k3dRfGTMXGvxhHFvkDTCTpvA=
github.com/onsi/ginkgo/v2 v2.19.0/go.mod h1:rlwLi9PilAFJ8jCg9UE1QP6VBpd6/xj3SRC0d6TU0To=
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/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.19.0 h1:4ieX6qQjPP/BfC3mpsAtIGGlxTWPeA3Inl/7DtXw1tw=
github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro=
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/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.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040=
github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M=
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/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.1 h1:nHFvthhM0qY8/m+vfhJylliSshm8G1jJ2jDMcgULaH8=
github.com/opencontainers/selinux v1.11.1/go.mod h1:E5dMC3VPuVvVHDYmi78qvhJp8+M586T4DlDRYpFkyec=
github.com/opencontainers/selinux v1.11.0 h1:+5Zbo97w3Lbmb3PeqQtpmTkMwsW5nRI3YaLpt7tQ7oU=
github.com/opencontainers/selinux v1.11.0/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.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/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/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=
@ -394,25 +393,24 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
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 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
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.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_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_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.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
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/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.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc=
github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8=
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/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=
@ -423,8 +421,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.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
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/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=
@ -445,13 +443,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.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
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/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.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
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/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=
@ -476,18 +474,16 @@ github.com/tilt-dev/fsnotify v1.4.8-0.20220602155310-fff9c274a375 h1:QB54BJwA6x8
github.com/tilt-dev/fsnotify v1.4.8-0.20220602155310-fff9c274a375/go.mod h1:xRroudyp5iVtxKqZCrA6n2TLFRBf8bmnjr1UD4x+z7g=
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/fsutil v0.0.0-20241028165955-397af5306b5c h1:bQLsfX+uEPZUjyR2qmoJs5F8Z57bPVmF3IsUf22Xo9Y=
github.com/tonistiigi/fsutil v0.0.0-20241028165955-397af5306b5c/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.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/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/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=
@ -495,45 +491,39 @@ 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/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.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.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=
@ -546,15 +536,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.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/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A=
golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70=
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 h1:e66Fs6Z+fZTbFBAxKfP3PALWBtpfqks2bwGcexMxgtk=
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0/go.mod h1:2TbTHSBQa924w8M6Xs1QcRcFwyucIwBGpK1p2f1YFFY=
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.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4=
golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0=
golang.org/x/mod v0.21.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=
@ -565,10 +555,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.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/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo=
golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0=
golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs=
golang.org/x/oauth2 v0.23.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=
@ -576,8 +566,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.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610=
golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ=
golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
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=
@ -592,26 +582,26 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210331175145-43e1dd70ce54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
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.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/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s=
golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
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.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg=
golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek=
golang.org/x/term v0.24.0 h1:Mh5cbb+Zk2hqqXNO7S1iTjEphVL+jb8ZWaqh/g+JWkM=
golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8=
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.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
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=
@ -619,21 +609,23 @@ 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.27.0 h1:qEKojBykQkQ4EynWy4S8Weg69NumxKdn40Fce3uc/8o=
golang.org/x/tools v0.27.0/go.mod h1:sUi0ZgbwW9ZPAq26Ekut+weQPR5eIM6GQLQ1Yjm1H0Q=
golang.org/x/tools v0.25.0 h1:oFU9pkj/iJgs+0DT+VMHrx+oBKs/LJMV+Uvg78sl+fE=
golang.org/x/tools v0.25.0/go.mod h1:/vtpO8WL1N9cQC3FN5zPqb//fRXskFHbLKk4OW1Q7rg=
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/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/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-20240903143218-8af14fe29dc1 h1:hjSy6tcFQZ171igDaN5QHOw2n6vx40juYbC/x67CEhc=
google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:qpvKtACPCQhAdu3PyQgV4l3LMXZEtft7y8QcarRsp9I=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 h1:pPJltXNxVzT4pK9yD8vR9X75DaWYYmLGMsEvBfFQZzQ=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU=
google.golang.org/grpc v1.0.5/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
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=
google.golang.org/grpc v1.68.0 h1:aHQeeJbo8zAkAa3pRzrVjZlbz6uSfeOXlJNQM0RAbz0=
google.golang.org/grpc v1.68.0/go.mod h1:fmSPC5AsjSBCK54MyHRx48kpOti1/jRfOlwEWywNjWA=
google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA=
google.golang.org/protobuf v1.35.1/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=
@ -648,8 +640,6 @@ 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=
@ -662,25 +652,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.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=
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=
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.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=
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=

View File

@ -25,7 +25,7 @@ import (
"path/filepath"
"time"
pusherrors "github.com/containerd/containerd/v2/core/remotes/errors"
pusherrors "github.com/containerd/containerd/remotes/errors"
"github.com/distribution/reference"
"github.com/docker/buildx/util/imagetools"
"github.com/docker/compose/v2/pkg/api"
@ -54,8 +54,6 @@ 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
@ -83,18 +81,6 @@ 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,

View File

@ -16,6 +16,8 @@ package sync
import (
"context"
"github.com/compose-spec/compose-go/v2/types"
)
// PathMapping contains the Compose service and modified host system path.
@ -36,5 +38,5 @@ type PathMapping struct {
}
type Syncer interface {
Sync(ctx context.Context, service string, paths []*PathMapping) error
Sync(ctx context.Context, service types.ServiceConfig, paths []PathMapping) error
}

View File

@ -32,7 +32,8 @@ import (
"github.com/hashicorp/go-multierror"
"github.com/docker/docker/api/types/container"
"github.com/compose-spec/compose-go/v2/types"
moby "github.com/docker/docker/api/types"
"github.com/docker/docker/pkg/archive"
)
@ -43,7 +44,7 @@ type archiveEntry struct {
}
type LowLevelClient interface {
ContainersForService(ctx context.Context, projectName string, serviceName string) ([]container.Summary, error)
ContainersForService(ctx context.Context, projectName string, serviceName string) ([]moby.Container, error)
Exec(ctx context.Context, containerID string, cmd []string, in io.Reader) error
Untar(ctx context.Context, id string, reader io.ReadCloser) error
@ -64,8 +65,8 @@ func NewTar(projectName string, client LowLevelClient) *Tar {
}
}
func (t *Tar) Sync(ctx context.Context, service string, paths []*PathMapping) error {
containers, err := t.client.ContainersForService(ctx, t.projectName, service)
func (t *Tar) Sync(ctx context.Context, service types.ServiceConfig, paths []PathMapping) error {
containers, err := t.client.ContainersForService(ctx, t.projectName, service.Name)
if err != nil {
return err
}
@ -76,7 +77,7 @@ func (t *Tar) Sync(ctx context.Context, service string, paths []*PathMapping) er
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)
}
}

View File

@ -25,7 +25,7 @@ import (
"time"
"github.com/compose-spec/compose-go/v2/types"
"github.com/docker/docker/api/types/container"
moby "github.com/docker/docker/api/types"
"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(ctr container.Summary) SpanOptions {
func ContainerOptions(container moby.Container) SpanOptions {
attrs := []attribute.KeyValue{
attribute.String("container.id", ctr.ID),
attribute.String("container.image", ctr.Image),
unixTimeAttr("container.created_at", ctr.Created),
attribute.String("container.id", container.ID),
attribute.String("container.image", container.Image),
unixTimeAttr("container.created_at", container.Created),
}
if len(ctr.Names) != 0 {
attrs = append(attrs, attribute.String("container.name", strings.TrimPrefix(ctr.Names[0], "/")))
if len(container.Names) != 0 {
attrs = append(attrs, attribute.String("container.name", strings.TrimPrefix(container.Names[0], "/")))
}
return []trace.SpanStartEventOption{

View File

@ -16,5 +16,7 @@
package internal
// Version is the version of the CLI injected in compilation time
var Version = "dev"
var (
// Version is the version of the CLI injected in compilation time
Version = "dev"
)

View File

@ -155,8 +155,6 @@ 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
@ -209,8 +207,6 @@ 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
@ -422,8 +418,6 @@ const (
// PublishOptions group options of the Publish API
type PublishOptions struct {
ResolveImageDigests bool
WithEnvironment bool
AssumeYes bool
OCIVersion OCIVersion
}
@ -435,6 +429,7 @@ 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
@ -525,8 +520,6 @@ type ContainerProcSummary struct {
Name string
Processes [][]string
Titles []string
Service string
Replica string
}
// ImageSummary holds container image description
@ -536,7 +529,6 @@ type ImageSummary struct {
Repository string
Tag string
Size int64
LastTagTime time.Time
}
// ServiceStatus hold status about a service

View File

@ -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 []containerType.Summary
containers []moby.Container
execs sync.Map
resolver *imagetools.Resolver
}
@ -82,7 +82,7 @@ func NewDryRunClient(apiClient client.APIClient, cli command.Cli) (*DryRunClient
}
return &DryRunClient{
apiClient: apiClient,
containers: []containerType.Summary{},
containers: []moby.Container{},
execs: sync.Map{},
resolver: imagetools.New(configFile),
}, nil
@ -101,9 +101,8 @@ 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, containerType.Summary{
networkingConfig *network.NetworkingConfig, platform *specs.Platform, containerName string) (containerType.CreateResponse, error) {
d.containers = append(d.containers, moby.Container{
ID: containerName,
Names: []string{containerName},
Labels: config.Labels,
@ -115,7 +114,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) (containerType.InspectResponse, error) {
func (d *DryRunClient) ContainerInspect(ctx context.Context, container string) (moby.ContainerJSON, error) {
containerJSON, err := d.apiClient.ContainerInspect(ctx, container)
if err != nil {
id := "dryRunId"
@ -124,20 +123,20 @@ func (d *DryRunClient) ContainerInspect(ctx context.Context, container string) (
id = container
}
}
return containerType.InspectResponse{
ContainerJSONBase: &containerType.ContainerJSONBase{
return moby.ContainerJSON{
ContainerJSONBase: &moby.ContainerJSONBase{
ID: id,
Name: container,
State: &containerType.State{
State: &moby.ContainerState{
Status: "running", // needed for --wait option
Health: &containerType.Health{
Status: containerType.Healthy, // needed for healthcheck control
Health: &moby.Health{
Status: moby.Healthy, // needed for healthcheck control
},
},
},
Mounts: nil,
Config: &containerType.Config{},
NetworkSettings: &containerType.NetworkSettings{},
NetworkSettings: &moby.NetworkSettings{},
}, nil
}
return containerJSON, err
@ -147,7 +146,7 @@ func (d *DryRunClient) ContainerKill(ctx context.Context, container, signal stri
return nil
}
func (d *DryRunClient) ContainerList(ctx context.Context, options containerType.ListOptions) ([]containerType.Summary, error) {
func (d *DryRunClient) ContainerList(ctx context.Context, options containerType.ListOptions) ([]moby.Container, error) {
caller := getCallingFunction()
switch caller {
case "start":
@ -222,24 +221,15 @@ func (d *DryRunClient) ImageBuild(ctx context.Context, reader io.Reader, options
}, nil
}
func (d *DryRunClient) ImageInspect(ctx context.Context, imageName string, options ...client.ImageInspectOption) (image.InspectResponse, error) {
func (d *DryRunClient) ImageInspectWithRaw(ctx context.Context, imageName string) (moby.ImageInspect, []byte, error) {
caller := getCallingFunction()
switch caller {
case "pullServiceImage", "buildContainerVolumes":
return image.InspectResponse{ID: "dryRunId"}, nil
return moby.ImageInspect{ID: "dryRunId"}, nil, nil
default:
return d.apiClient.ImageInspect(ctx, imageName, options...)
return d.apiClient.ImageInspectWithRaw(ctx, imageName)
}
}
// 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) {
@ -309,7 +299,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) (containerType.ExecCreateResponse, error) {
func (d *DryRunClient) ContainerExecCreate(ctx context.Context, container string, config containerType.ExecOptions) (moby.IDResponse, error) {
b := make([]byte, 32)
_, _ = rand.Read(b)
id := fmt.Sprintf("%x", b)
@ -317,7 +307,7 @@ func (d *DryRunClient) ContainerExecCreate(ctx context.Context, container string
container: container,
command: config.Cmd,
})
return containerType.ExecCreateResponse{
return moby.IDResponse{
ID: id,
}, nil
}
@ -354,7 +344,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) (containerType.CommitResponse, error) {
func (d *DryRunClient) ContainerCommit(ctx context.Context, container string, options containerType.CommitOptions) (moby.IDResponse, error) {
return d.apiClient.ContainerCommit(ctx, container, options)
}
@ -378,7 +368,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) (containerType.InspectResponse, []byte, error) {
func (d *DryRunClient) ContainerInspectWithRaw(ctx context.Context, container string, getSize bool) (moby.ContainerJSON, []byte, error) {
return d.apiClient.ContainerInspectWithRaw(ctx, container, getSize)
}
@ -402,11 +392,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.TopResponse, error) {
func (d *DryRunClient) ContainerTop(ctx context.Context, container string, arguments []string) (containerType.ContainerTopOKBody, error) {
return d.apiClient.ContainerTop(ctx, container, arguments)
}
func (d *DryRunClient) ContainerUpdate(ctx context.Context, container string, updateConfig containerType.UpdateConfig) (containerType.UpdateResponse, error) {
func (d *DryRunClient) ContainerUpdate(ctx context.Context, container string, updateConfig containerType.UpdateConfig) (containerType.ContainerUpdateOKBody, error) {
return d.apiClient.ContainerUpdate(ctx, container, updateConfig)
}
@ -434,8 +424,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, options ...client.ImageHistoryOption) ([]image.HistoryResponseItem, error) {
return d.apiClient.ImageHistory(ctx, imageName, options...)
func (d *DryRunClient) ImageHistory(ctx context.Context, imageName string) ([]image.HistoryResponseItem, error) {
return d.apiClient.ImageHistory(ctx, imageName)
}
func (d *DryRunClient) ImageImport(ctx context.Context, source image.ImportSource, ref string, options image.ImportOptions) (io.ReadCloser, error) {
@ -446,16 +436,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, options ...client.ImageLoadOption) (image.LoadResponse, error) {
return d.apiClient.ImageLoad(ctx, input, 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) 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, options ...client.ImageSaveOption) (io.ReadCloser, error) {
return d.apiClient.ImageSave(ctx, images, options...)
func (d *DryRunClient) ImageSave(ctx context.Context, images []string) (io.ReadCloser, error) {
return d.apiClient.ImageSave(ctx, images)
}
func (d *DryRunClient) ImageTag(ctx context.Context, imageName, ref string) error {

View File

@ -25,6 +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/pkg/stdcopy"
"github.com/moby/term"
@ -51,8 +52,8 @@ func (s *composeService) attach(ctx context.Context, project *types.Project, lis
_, _ = fmt.Fprintf(s.stdout(), "Attaching to %s\n", strings.Join(names, ", "))
for _, ctr := range containers {
err := s.attachContainer(ctx, ctr, listener)
for _, container := range containers {
err := s.attachContainer(ctx, container, listener)
if err != nil {
return nil, err
}
@ -60,7 +61,7 @@ func (s *composeService) attach(ctx context.Context, project *types.Project, lis
return containers, err
}
func (s *composeService) attachContainer(ctx context.Context, container containerType.Summary, listener api.ContainerEventListener) error {
func (s *composeService) attachContainer(ctx context.Context, container moby.Container, listener api.ContainerEventListener) error {
serviceName := container.Labels[api.ServiceLabel]
containerName := getContainerNameWithoutProject(container)

View File

@ -21,9 +21,6 @@ import (
"errors"
"fmt"
"os"
"strings"
"sync"
"time"
"github.com/compose-spec/compose-go/v2/types"
"github.com/containerd/platforms"
@ -34,7 +31,6 @@ 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"
@ -50,8 +46,6 @@ 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"
@ -63,20 +57,13 @@ func (s *composeService) Build(ctx context.Context, project *types.Project, opti
return err
}
return progress.RunWithTitle(ctx, func(ctx context.Context) error {
return tracing.SpanWrapFunc("project/build", tracing.ProjectOptions(ctx, project),
func(ctx context.Context) error {
_, err := s.build(ctx, project, options, nil)
return err
})(ctx)
_, err := s.build(ctx, project, options, nil)
return err
}, s.stdinfo(), "Building")
}
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]api.ImageSummary) (map[string]string, error) {
func (s *composeService) build(ctx context.Context, project *types.Project, options api.BuildOptions, localImages map[string]string) (map[string]string, error) {
imageIDs := map[string]string{}
serviceToBuild := types.Services{}
@ -84,7 +71,6 @@ func (s *composeService) build(ctx context.Context, project *types.Project, opti
if options.Deps {
policy = types.IncludeDependencies
}
err := project.ForEachService(options.Services, func(serviceName string, service *types.ServiceConfig) error {
if service.Build == nil {
return nil
@ -105,31 +91,10 @@ func (s *composeService) build(ctx context.Context, project *types.Project, opti
if err != nil {
return nil, err
}
if bake || options.Print {
trace.SpanFromContext(ctx).SetAttributes(attribute.String("builder", "bake"))
if 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 {
@ -141,11 +106,6 @@ func (s *composeService) build(ctx context.Context, project *types.Project, opti
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")
@ -169,14 +129,12 @@ 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
}
@ -193,17 +151,15 @@ 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 {
service, ok := serviceToBuild[name]
if !ok {
return nil
}
cw := progress.ContextWriter(ctx)
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 {
@ -227,11 +183,12 @@ func (s *composeService) build(ctx context.Context, project *types.Project, opti
return err
}
trace.SpanFromContext(ctx).SetAttributes(attribute.String("builder", "buildkit"))
cw.Event(progress.BuildingEvent(serviceName))
digest, err := s.doBuildBuildkit(ctx, name, buildOptions, w, nodes)
if err != nil {
return err
}
cw.Event(progress.BuiltEvent(serviceName))
builtDigests[getServiceIndex(name)] = digest
return nil
@ -255,7 +212,6 @@ func (s *composeService) build(ctx context.Context, project *types.Project, opti
service := project.Services[names[i]]
imageRef := api.GetImageNameOrDefault(service, project.Name)
imageIDs[imageRef] = imageDigest
cw.Event(progress.BuiltEvent(names[i]))
}
}
return imageIDs, err
@ -263,7 +219,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.Provider == nil && service.Image == "" && service.Build == nil {
if service.Image == "" && service.Build == nil {
return fmt.Errorf("invalid service %q. Must specify either image or build", name)
}
}
@ -291,11 +247,7 @@ func (s *composeService) ensureImagesExists(ctx context.Context, project *types.
}
for name, digest := range builtImages {
images[name] = api.ImageSummary{
Repository: name,
ID: digest,
LastTagTime: time.Now(),
}
images[name] = digest
}
return nil
},
@ -308,16 +260,19 @@ 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)
img, ok := images[image]
digest, ok := images[image]
if ok {
service.CustomLabels.Add(api.ImageDigestLabel, img.ID)
if service.Labels == nil {
service.Labels = types.Labels{}
}
service.CustomLabels.Add(api.ImageDigestLabel, digest)
}
project.Services[name] = service
}
return nil
}
func (s *composeService) getLocalImagesDigests(ctx context.Context, project *types.Project) (map[string]api.ImageSummary, error) {
func (s *composeService) getLocalImagesDigests(ctx context.Context, project *types.Project) (map[string]string, error) {
var imageNames []string
for _, s := range project.Services {
imgName := api.GetImageNameOrDefault(s, project.Name)
@ -329,10 +284,14 @@ func (s *composeService) getLocalImagesDigests(ctx context.Context, project *typ
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)
img, ok := imgs[imgName]
digest, ok := images[imgName]
if !ok {
continue
}
@ -341,7 +300,7 @@ func (s *composeService) getLocalImagesDigests(ctx context.Context, project *typ
if err != nil {
return nil, err
}
inspect, err := s.apiClient().ImageInspect(ctx, img.ID)
inspect, _, err := s.apiClient().ImageInspectWithRaw(ctx, digest)
if err != nil {
return nil, err
}
@ -354,15 +313,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(imgs, imgName)
delete(images, imgName)
}
}
project.Services[i].CustomLabels.Add(api.ImageDigestLabel, img.ID)
project.Services[i].CustomLabels.Add(api.ImageDigestLabel, digest)
}
return imgs, nil
return images, nil
}
// resolveAndMergeBuildArgs returns the final set of build arguments to use for the service image build.
@ -407,9 +366,7 @@ func (s *composeService) toBuildOptions(project *types.Project, service types.Se
}
sessionConfig := []session.Attachable{
authprovider.NewDockerAuthProvider(authprovider.DockerAuthProviderConfig{
ConfigFile: s.configFile(),
}),
authprovider.NewDockerAuthProvider(s.configFile(), nil),
}
if len(options.SSHs) > 0 || len(service.Build.SSH) > 0 {
sshAgentProvider, err := sshAgentProvider(append(service.Build.SSH, options.SSHs...))
@ -437,7 +394,7 @@ func (s *composeService) toBuildOptions(project *types.Project, service types.Se
return build.Options{}, err
}
if service.Build.Privileged {
allow = append(allow, entitlements.EntitlementSecurityInsecure.String())
allow = append(allow, entitlements.EntitlementSecurityInsecure)
}
imageLabels := getImageBuildLabels(project, service)
@ -469,10 +426,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, project),
NamedContexts: toBuildContexts(service.Build.AdditionalContexts),
},
CacheFrom: pb.CreateCaches(cacheFrom.ToPB()),
CacheTo: pb.CreateCaches(cacheTo.ToPB()),
CacheFrom: pb.CreateCaches(cacheFrom),
CacheTo: pb.CreateCaches(cacheTo),
NoCache: service.Build.NoCache,
Pull: service.Build.Pull,
BuildArgs: flatten(resolveAndMergeBuildArgs(s.dockerCli, project, service, options)),
@ -574,16 +531,9 @@ func getImageBuildLabels(project *types.Project, service types.ServiceConfig) ty
return ret
}
func toBuildContexts(service types.ServiceConfig, project *types.Project) map[string]build.NamedContext {
func toBuildContexts(additionalContexts types.Mapping) map[string]build.NamedContext {
namedContexts := map[string]build.NamedContext{}
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
}
for name, contextPath := range additionalContexts {
namedContexts[name] = build.NamedContext{Path: contextPath}
}
return namedContexts

View File

@ -17,16 +17,15 @@
package compose
import (
"bufio"
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"io"
"os"
"os/exec"
"path/filepath"
"slices"
"strconv"
"strings"
@ -36,10 +35,8 @@ import (
"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"
@ -96,28 +93,19 @@ type bakeGroup struct {
}
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"`
Context string `json:"context,omitempty"`
Dockerfile string `json:"dockerfile,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"`
Secrets []string `json:"secret,omitempty"`
SSH []string `json:"ssh,omitempty"`
Platforms []string `json:"platforms,omitempty"`
Target string `json:"target,omitempty"`
Pull bool `json:"pull,omitempty"`
NoCache bool `json:"no-cache,omitempty"`
}
type bakeMetadata map[string]buildStatus
@ -127,6 +115,11 @@ type buildStatus struct {
}
func (s *composeService) doBuildBake(ctx context.Context, project *types.Project, serviceToBeBuild types.Services, options api.BuildOptions) (map[string]string, error) { //nolint:gocyclo
cw := progress.ContextWriter(ctx)
for name := range serviceToBeBuild {
cw.Event(progress.BuildingEvent(name))
}
eg := errgroup.Group{}
ch := make(chan *client.SolveStatus)
display, err := progressui.NewDisplay(os.Stdout, progressui.DisplayMode(options.Progress))
@ -143,10 +136,8 @@ func (s *composeService) doBuildBake(ctx context.Context, project *types.Project
Targets: map[string]bakeTarget{},
}
var group bakeGroup
var privileged bool
var read []string
for serviceName, service := range serviceToBeBuild {
for name, service := range serviceToBeBuild {
if service.Build == nil {
continue
}
@ -160,71 +151,32 @@ func (s *composeService) doBuildBake(ctx context.Context, project *types.Project
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),
cfg.Targets[name] = bakeTarget{
Context: build.Context,
Dockerfile: dockerFilePath(build.Context, build.Dockerfile),
Args: args,
Labels: build.Labels,
Tags: build.Tags,
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},
Platforms: build.Platforms,
Target: build.Target,
Secrets: toBakeSecrets(project, build.Secrets),
SSH: toBakeSSH(build.SSH),
Pull: options.Pull,
NoCache: options.NoCache,
}
group.Targets = append(group.Targets, serviceName)
group.Targets = append(group.Targets, name)
}
cfg.Groups["default"] = group
b, err := json.MarshalIndent(cfg, "", " ")
b, err := json.Marshal(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
@ -234,26 +186,7 @@ func (s *composeService) doBuildBake(ctx context.Context, project *types.Project
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...)
cmd := exec.CommandContext(ctx, buildx.Path, "bake", "--file", "-", "--progress", "rawjson", "--metadata-file", metadata.Name())
// Remove DOCKER_CLI_PLUGIN... variable so buildx can detect it run standalone
cmd.Env = filter(os.Environ(), manager.ReexecEnvvar)
@ -279,36 +212,29 @@ func (s *composeService) doBuildBake(ctx context.Context, project *types.Project
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)
for {
decoder := json.NewDecoder(pipe)
var s client.SolveStatus
err := decoder.Decode(&s)
if err != nil {
if strings.HasPrefix(line, "ERROR: ") {
errMessage = line[7:]
if errors.Is(err, io.EOF) {
break
}
// bake displays build details at the end of a build, which isn't a json SolveStatus
continue
}
ch <- &status
ch <- &s
}
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)
return nil, err
}
b, err = os.ReadFile(metadata.Name())
@ -322,7 +248,6 @@ func (s *composeService) doBuildBake(ctx context.Context, project *types.Project
return nil, err
}
cw := progress.ContextWriter(ctx)
results := map[string]string{}
for name, m := range md {
results[name] = m.Digest
@ -331,29 +256,6 @@ func (s *composeService) doBuildBake(ctx context.Context, project *types.Project
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 {
@ -366,15 +268,11 @@ func toBakeSecrets(project *types.Project, secrets []types.ServiceSecretConfig)
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))
s = append(s, fmt.Sprintf("id=%s,type=env,env=%s", ref.Source, def.Environment))
case def.File != "":
s = append(s, fmt.Sprintf("id=%s,type=file,src=%s", target, def.File))
s = append(s, fmt.Sprintf("id=%s,type=file,src=%s", ref.Source, def.File))
}
}
return s

View File

@ -48,7 +48,7 @@ func (s *composeService) doBuildBuildkit(ctx context.Context, service string, op
confutil.NewConfig(s.dockerCli),
buildx.WithPrefix(p, service, true))
if err != nil {
return "", err
return "", WrapCategorisedComposeError(err, BuildFailure)
}
}

View File

@ -23,7 +23,7 @@ import (
"github.com/docker/compose/v2/pkg/api"
"github.com/docker/compose/v2/pkg/progress"
"github.com/docker/docker/api/types/container"
containerType "github.com/docker/docker/api/types/container"
)
func (s *composeService) Commit(ctx context.Context, projectName string, options api.CommitOptions) error {
@ -35,7 +35,7 @@ func (s *composeService) Commit(ctx context.Context, projectName string, options
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)
container, err := s.getSpecifiedContainer(ctx, projectName, oneOffInclude, false, options.Service, options.Index)
if err != nil {
return err
}
@ -44,7 +44,7 @@ func (s *composeService) commit(ctx context.Context, projectName string, options
w := progress.ContextWriter(ctx)
name := getCanonicalContainerName(ctr)
name := getCanonicalContainerName(container)
msg := fmt.Sprintf("Commit %s", name)
w.Event(progress.Event{
@ -65,7 +65,7 @@ func (s *composeService) commit(ctx context.Context, projectName string, options
return nil
}
response, err := clnt.ContainerCommit(ctx, ctr.ID, container.CommitOptions{
response, err := clnt.ContainerCommit(ctx, container.ID, containerType.CommitOptions{
Reference: options.Reference,
Comment: options.Comment,
Author: options.Author,

View File

@ -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/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"
moby "github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/api/types/swarm"
"github.com/docker/docker/client"
)
var stdioToStdout bool
@ -139,7 +139,7 @@ func (s *composeService) stdinfo() *streams.Out {
return s.dockerCli.Err()
}
func getCanonicalContainerName(c container.Summary) string {
func getCanonicalContainerName(c moby.Container) 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 container.Summary) string {
return strings.TrimPrefix(c.Names[0], "/")
}
func getContainerNameWithoutProject(c container.Summary) string {
func getContainerNameWithoutProject(c moby.Container) string {
project := c.Labels[api.ProjectLabel]
defaultName := getDefaultContainerName(project, c.Labels[api.ServiceLabel], c.Labels[api.ContainerNumberLabel])
name := getCanonicalContainerName(c)
@ -187,6 +187,7 @@ func (s *composeService) projectFromName(containers Containers, projectName stri
Image: c.Image,
Labels: c.Labels,
}
}
service.Scale = increment(service.Scale)
set[serviceLabel] = service
@ -320,6 +321,7 @@ func (s *composeService) RuntimeVersion(ctx context.Context) (string, error) {
runtimeVersion.val = version.APIVersion
})
return runtimeVersion.val, runtimeVersion.err
}
func (s *composeService) isDesktopIntegrationActive() bool {

View File

@ -25,12 +25,13 @@ import (
"github.com/compose-spec/compose-go/v2/types"
"github.com/docker/compose/v2/pkg/api"
"github.com/docker/compose/v2/pkg/utils"
"github.com/docker/docker/api/types/container"
moby "github.com/docker/docker/api/types"
containerType "github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/filters"
)
// Containers is a set of moby Container
type Containers []container.Summary
type Containers []moby.Container
type oneOff int
@ -43,7 +44,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, container.ListOptions{
containers, err := s.apiClient().ContainerList(ctx, containerType.ListOptions{
Filters: filters.NewArgs(f...),
All: all,
})
@ -72,25 +73,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) (container.Summary, error) {
func (s *composeService) getSpecifiedContainer(ctx context.Context, projectName string, oneOff oneOff, all bool, serviceName string, containerIndex int) (moby.Container, error) {
defaultFilters := getDefaultFilters(projectName, oneOff, serviceName)
if containerIndex > 0 {
defaultFilters = append(defaultFilters, containerNumberFilter(containerIndex))
}
containers, err := s.apiClient().ContainerList(ctx, container.ListOptions{
containers, err := s.apiClient().ContainerList(ctx, containerType.ListOptions{
Filters: filters.NewArgs(
defaultFilters...,
),
All: all,
})
if err != nil {
return container.Summary{}, err
return moby.Container{}, err
}
if len(containers) < 1 {
if containerIndex > 0 {
return container.Summary{}, fmt.Errorf("service %q is not running container #%d", serviceName, containerIndex)
return moby.Container{}, fmt.Errorf("service %q is not running container #%d", serviceName, containerIndex)
}
return container.Summary{}, fmt.Errorf("service %q is not running", serviceName)
return moby.Container{}, fmt.Errorf("service %q is not running", serviceName)
}
// Sort by container number first, then put one-off containers at the end
@ -106,30 +107,22 @@ func (s *composeService) getSpecifiedContainer(ctx context.Context, projectName
return numberLabelX < numberLabelY
})
return containers[0], nil
container := containers[0]
return container, nil
}
// containerPredicate define a predicate we want container to satisfy for filtering operations
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
}
type containerPredicate func(c moby.Container) bool
func isService(services ...string) containerPredicate {
return func(c container.Summary) bool {
return func(c moby.Container) bool {
service := c.Labels[api.ServiceLabel]
return utils.StringContains(services, service)
}
}
func isRunning() containerPredicate {
return func(c container.Summary) bool {
return func(c moby.Container) bool {
return c.State == "running"
}
}
@ -137,7 +130,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 container.Summary) bool {
return func(c moby.Container) bool {
// One-off container
v, ok := c.Labels[api.OneoffLabel]
if ok && v == "True" {
@ -149,16 +142,16 @@ func isOrphaned(project *types.Project) containerPredicate {
}
}
func isNotOneOff(c container.Summary) bool {
func isNotOneOff(c moby.Container) bool {
v, ok := c.Labels[api.OneoffLabel]
return !ok || v == "False"
}
// filter return Containers with elements to match predicate
func (containers Containers) filter(predicates ...containerPredicate) Containers {
func (containers Containers) filter(predicate containerPredicate) Containers {
var filtered Containers
for _, c := range containers {
if matches(c, predicates...) {
if predicate(c) {
filtered = append(filtered, c)
}
}
@ -173,7 +166,7 @@ func (containers Containers) names() []string {
return names
}
func (containers Containers) forEach(fn func(container.Summary)) {
func (containers Containers) forEach(fn func(moby.Container)) {
for _, c := range containers {
fn(c)
}

View File

@ -27,18 +27,19 @@ import (
"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 +48,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"
"Remove the custom name to scale the service.\n"
)
// convergence manages service's container lifecycle.
@ -59,7 +60,6 @@ type convergence struct {
service *composeService
services map[string]Containers
networks map[string]string
volumes map[string]string
stateMutex sync.Mutex
}
@ -75,7 +75,7 @@ func (c *convergence) setObservedState(serviceName string, containers Containers
c.services[serviceName] = containers
}
func newConvergence(services []string, state Containers, networks map[string]string, volumes map[string]string, s *composeService) *convergence {
func newConvergence(services []string, state Containers, networks map[string]string, s *composeService) *convergence {
observedState := map[string]Containers{}
for _, s := range services {
observedState[s] = Containers{}
@ -88,7 +88,6 @@ func newConvergence(services []string, state Containers, networks map[string]str
service: s,
services: observedState,
networks: networks,
volumes: volumes,
}
}
@ -110,9 +109,6 @@ 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
@ -206,6 +202,7 @@ 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{
@ -228,9 +225,7 @@ func (c *convergence) ensureService(ctx context.Context, project *types.Project,
func (c *convergence) stopDependentContainers(ctx context.Context, project *types.Project, service types.ServiceConfig) error {
// Stop dependent containers, so they will be restarted after service is re-created
dependents := project.GetDependentsForService(service, func(dependency types.ServiceDependency) bool {
return dependency.Restart
})
dependents := project.GetDependentsForService(service)
if len(dependents) == 0 {
return nil
}
@ -243,12 +238,12 @@ func (c *convergence) stopDependentContainers(ctx context.Context, project *type
}
for _, name := range dependents {
dependentStates := c.getObservedState(name)
for i, dependent := range dependentStates {
dependents := c.getObservedState(name)
for i, dependent := range dependents {
dependent.State = ContainerExited
dependentStates[i] = dependent
dependents[i] = dependent
}
c.setObservedState(name, dependentStates)
c.setObservedState(name, dependents)
}
return nil
}
@ -328,7 +323,7 @@ func (c *convergence) resolveSharedNamespaces(service *types.ServiceConfig) erro
return nil
}
func (c *convergence) mustRecreate(expected types.ServiceConfig, actual containerType.Summary, policy string) (bool, error) {
func (c *convergence) mustRecreate(expected types.ServiceConfig, actual moby.Container, policy string) (bool, error) {
if policy == api.RecreateNever {
return false, nil
}
@ -346,71 +341,30 @@ func (c *convergence) mustRecreate(expected types.ServiceConfig, actual containe
}
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
// check the networks container is connected to are the expected ones
for net := range expected.Networks {
id := c.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 - maybe recreated ?
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 {
name := getDefaultContainerName(projectName, service.Name, strconv.Itoa(number))
if service.ContainerName != "" {
@ -423,22 +377,22 @@ func getDefaultContainerName(projectName, serviceName, index string) string {
return strings.Join([]string{projectName, serviceName, index}, api.Separator)
}
func getContainerProgressName(ctr containerType.Summary) string {
return "Container " + getCanonicalContainerName(ctr)
func getContainerProgressName(container moby.Container) string {
return "Container " + getCanonicalContainerName(container)
}
func containerEvents(containers Containers, eventFunc func(string) progress.Event) []progress.Event {
events := []progress.Event{}
for _, ctr := range containers {
events = append(events, eventFunc(getContainerProgressName(ctr)))
for _, container := range containers {
events = append(events, eventFunc(getContainerProgressName(container)))
}
return events
}
func containerReasonEvents(containers Containers, eventFunc func(string, string) progress.Event, reason string) []progress.Event {
events := []progress.Event{}
for _, ctr := range containers {
events = append(events, eventFunc(getContainerProgressName(ctr), reason))
for _, container := range containers {
events = append(events, eventFunc(getContainerProgressName(container), reason))
}
return events
}
@ -462,7 +416,7 @@ func (s *composeService) waitDependencies(ctx context.Context, project *types.Pr
continue
}
waitingFor := containers.filter(isService(dep), isNotOneOff)
waitingFor := containers.filter(isService(dep))
w.Events(containerEvents(waitingFor, progress.Waiting))
if len(waitingFor) == 0 {
if config.Required {
@ -472,6 +426,7 @@ 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()
@ -568,7 +523,7 @@ func shouldWaitForDependency(serviceName string, dependencyConfig types.ServiceD
return true, nil
}
func nextContainerNumber(containers []containerType.Summary) int {
func nextContainerNumber(containers []moby.Container) int {
maxNumber := 0
for _, c := range containers {
s, ok := c.Labels[api.ContainerNumberLabel]
@ -585,15 +540,15 @@ func nextContainerNumber(containers []containerType.Summary) int {
}
}
return maxNumber + 1
}
func (s *composeService) createContainer(ctx context.Context, project *types.Project, service types.ServiceConfig,
name string, number int, opts createOptions,
) (ctr containerType.Summary, err error) {
name string, number int, opts createOptions) (container moby.Container, err error) {
w := progress.ContextWriter(ctx)
eventName := "Container " + name
w.Event(progress.CreatingEvent(eventName))
ctr, err = s.createMobyContainer(ctx, project, service, name, number, nil, opts, w)
container, err = s.createMobyContainer(ctx, project, service, name, number, nil, opts, w)
if err != nil {
return
}
@ -602,9 +557,8 @@ func (s *composeService) createContainer(ctx context.Context, project *types.Pro
}
func (s *composeService) recreateContainer(ctx context.Context, project *types.Project, service types.ServiceConfig,
replaced containerType.Summary, inherit bool, timeout *time.Duration,
) (containerType.Summary, error) {
var created containerType.Summary
replaced moby.Container, inherit bool, timeout *time.Duration) (moby.Container, error) {
var created moby.Container
w := progress.ContextWriter(ctx)
w.Event(progress.NewEvent(getContainerProgressName(replaced), progress.Working, "Recreate"))
@ -613,7 +567,7 @@ func (s *composeService) recreateContainer(ctx context.Context, project *types.P
return created, err
}
var inherited *containerType.Summary
var inherited *moby.Container
if inherit {
inherited = &replaced
}
@ -650,14 +604,14 @@ func (s *composeService) recreateContainer(ctx context.Context, project *types.P
return created, err
}
func (s *composeService) startContainer(ctx context.Context, ctr containerType.Summary) error {
func (s *composeService) startContainer(ctx context.Context, container moby.Container) error {
w := progress.ContextWriter(ctx)
w.Event(progress.NewEvent(getContainerProgressName(ctr), progress.Working, "Restart"))
err := s.apiClient().ContainerStart(ctx, ctr.ID, containerType.StartOptions{})
w.Event(progress.NewEvent(getContainerProgressName(container), progress.Working, "Restart"))
err := s.apiClient().ContainerStart(ctx, container.ID, containerType.StartOptions{})
if err != nil {
return err
}
w.Event(progress.NewEvent(getContainerProgressName(ctr), progress.Done, "Restarted"))
w.Event(progress.NewEvent(getContainerProgressName(container), progress.Done, "Restarted"))
return nil
}
@ -666,12 +620,13 @@ func (s *composeService) createMobyContainer(ctx context.Context,
service types.ServiceConfig,
name string,
number int,
inherit *containerType.Summary,
inherit *moby.Container,
opts createOptions,
w progress.Writer,
) (containerType.Summary, error) {
var created containerType.Summary
) (moby.Container, error) {
var created moby.Container
cfgs, err := s.getCreateConfigs(ctx, project, service, number, inherit, opts)
if err != nil {
return created, err
}
@ -704,11 +659,11 @@ func (s *composeService) createMobyContainer(ctx context.Context,
if err != nil {
return created, err
}
created = containerType.Summary{
created = moby.Container{
ID: inspectedContainer.ID,
Labels: inspectedContainer.Config.Labels,
Names: []string{inspectedContainer.Name},
NetworkSettings: &containerType.NetworkSettingsSummary{
NetworkSettings: &moby.SummaryNetworkSettings{
Networks: inspectedContainer.NetworkSettings.Networks,
},
}
@ -825,11 +780,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 containerType.Healthy:
case moby.Healthy:
// Continue by checking the next container.
case containerType.Unhealthy:
case moby.Unhealthy:
return false, fmt.Errorf("container %s is unhealthy", name)
case containerType.Starting:
case moby.Starting:
return false, nil
default:
return false, fmt.Errorf("container %s had unexpected health status %q", name, container.State.Health.Status)
@ -854,8 +809,7 @@ 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
}
@ -873,19 +827,19 @@ func (s *composeService) startService(ctx context.Context,
}
w := progress.ContextWriter(ctx)
for _, ctr := range containers.filter(isService(service.Name)) {
if ctr.State == ContainerRunning {
for _, container := range containers.filter(isService(service.Name)) {
if container.State == ContainerRunning {
continue
}
eventName := getContainerProgressName(ctr)
eventName := getContainerProgressName(container)
w.Event(progress.StartingEvent(eventName))
err = s.apiClient().ContainerStart(ctx, ctr.ID, containerType.StartOptions{})
err = s.apiClient().ContainerStart(ctx, container.ID, containerType.StartOptions{})
if err != nil {
return err
}
for _, hook := range service.PostStart {
err = s.runHook(ctx, ctr, service, hook, listener)
err = s.runHook(ctx, container, service, hook, listener)
if err != nil {
return err
}

View File

@ -25,9 +25,8 @@ import (
"github.com/compose-spec/compose-go/v2/types"
"github.com/docker/cli/cli/config/configfile"
moby "github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
containerType "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"
@ -71,7 +70,7 @@ func TestServiceLinks(t *testing.T) {
Scale: intPtr(1),
}
containerListOptions := container.ListOptions{
containerListOptions := containerType.ListOptions{
Filters: filters.NewArgs(
projectFilter(testProject),
serviceFilter("db"),
@ -95,7 +94,7 @@ func TestServiceLinks(t *testing.T) {
s.Links = []string{"db"}
c := testContainer("db", dbContainerName, false)
apiClient.EXPECT().ContainerList(gomock.Any(), containerListOptions).Return([]container.Summary{c}, nil)
apiClient.EXPECT().ContainerList(gomock.Any(), containerListOptions).Return([]moby.Container{c}, nil)
links, err := tested.getLinks(context.Background(), testProject, s, 1)
assert.NilError(t, err)
@ -120,7 +119,7 @@ func TestServiceLinks(t *testing.T) {
c := testContainer("db", dbContainerName, false)
apiClient.EXPECT().ContainerList(gomock.Any(), containerListOptions).Return([]container.Summary{c}, nil)
apiClient.EXPECT().ContainerList(gomock.Any(), containerListOptions).Return([]moby.Container{c}, nil)
links, err := tested.getLinks(context.Background(), testProject, s, 1)
assert.NilError(t, err)
@ -143,7 +142,7 @@ func TestServiceLinks(t *testing.T) {
s.Links = []string{"db:dbname"}
c := testContainer("db", dbContainerName, false)
apiClient.EXPECT().ContainerList(gomock.Any(), containerListOptions).Return([]container.Summary{c}, nil)
apiClient.EXPECT().ContainerList(gomock.Any(), containerListOptions).Return([]moby.Container{c}, nil)
links, err := tested.getLinks(context.Background(), testProject, s, 1)
assert.NilError(t, err)
@ -168,7 +167,7 @@ func TestServiceLinks(t *testing.T) {
s.ExternalLinks = []string{"db1:db2"}
c := testContainer("db", dbContainerName, false)
apiClient.EXPECT().ContainerList(gomock.Any(), containerListOptions).Return([]container.Summary{c}, nil)
apiClient.EXPECT().ContainerList(gomock.Any(), containerListOptions).Return([]moby.Container{c}, nil)
links, err := tested.getLinks(context.Background(), testProject, s, 1)
assert.NilError(t, err)
@ -197,7 +196,7 @@ func TestServiceLinks(t *testing.T) {
s.Labels = s.Labels.Add(api.OneoffLabel, "True")
c := testContainer("web", webContainerName, true)
containerListOptionsOneOff := container.ListOptions{
containerListOptionsOneOff := containerType.ListOptions{
Filters: filters.NewArgs(
projectFilter(testProject),
serviceFilter("web"),
@ -206,7 +205,7 @@ func TestServiceLinks(t *testing.T) {
),
All: true,
}
apiClient.EXPECT().ContainerList(gomock.Any(), containerListOptionsOneOff).Return([]container.Summary{c}, nil)
apiClient.EXPECT().ContainerList(gomock.Any(), containerListOptionsOneOff).Return([]moby.Container{c}, nil)
links, err := tested.getLinks(context.Background(), testProject, s, 1)
assert.NilError(t, err)
@ -269,7 +268,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().ImageInspect(gomock.Any(), gomock.Any()).Return(image.InspectResponse{}, nil).AnyTimes()
apiClient.EXPECT().ImageInspectWithRaw(gomock.Any(), gomock.Any()).Return(moby.ImageInspect{}, nil, nil).AnyTimes()
// force `RuntimeVersion` to fetch again
runtimeVersion = runtimeVersionCache{}
apiClient.EXPECT().ServerVersion(gomock.Any()).Return(moby.Version{
@ -304,11 +303,11 @@ func TestCreateMobyContainer(t *testing.T) {
var falseBool bool
apiClient.EXPECT().ContainerCreate(gomock.Any(), gomock.Any(), gomock.Eq(
&container.HostConfig{
&containerType.HostConfig{
PortBindings: nat.PortMap{},
ExtraHosts: []string{},
Tmpfs: map[string]string{},
Resources: container.Resources{
Resources: containerType.Resources{
OomKillDisable: &falseBool,
},
NetworkMode: "b-moby-name",
@ -321,18 +320,18 @@ func TestCreateMobyContainer(t *testing.T) {
},
},
}), gomock.Any(), gomock.Any()).Times(1).Return(
container.CreateResponse{
containerType.CreateResponse{
ID: "an-id",
}, nil)
apiClient.EXPECT().ContainerInspect(gomock.Any(), gomock.Eq("an-id")).Times(1).Return(
container.InspectResponse{
ContainerJSONBase: &container.ContainerJSONBase{
moby.ContainerJSON{
ContainerJSONBase: &moby.ContainerJSONBase{
ID: "an-id",
Name: "a-name",
},
Config: &container.Config{},
NetworkSettings: &container.NetworkSettings{},
Config: &containerType.Config{},
NetworkSettings: &moby.NetworkSettings{},
}, nil)
apiClient.EXPECT().NetworkConnect(gomock.Any(), "a-moby-name", "an-id", gomock.Eq(
@ -358,7 +357,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().ImageInspect(gomock.Any(), gomock.Any()).Return(image.InspectResponse{}, nil).AnyTimes()
apiClient.EXPECT().ImageInspectWithRaw(gomock.Any(), gomock.Any()).Return(moby.ImageInspect{}, nil, nil).AnyTimes()
// force `RuntimeVersion` to fetch fresh version
runtimeVersion = runtimeVersionCache{}
apiClient.EXPECT().ServerVersion(gomock.Any()).Return(moby.Version{
@ -393,11 +392,11 @@ func TestCreateMobyContainer(t *testing.T) {
var falseBool bool
apiClient.EXPECT().ContainerCreate(gomock.Any(), gomock.Any(), gomock.Eq(
&container.HostConfig{
&containerType.HostConfig{
PortBindings: nat.PortMap{},
ExtraHosts: []string{},
Tmpfs: map[string]string{},
Resources: container.Resources{
Resources: containerType.Resources{
OomKillDisable: &falseBool,
},
NetworkMode: "b-moby-name",
@ -414,18 +413,18 @@ func TestCreateMobyContainer(t *testing.T) {
},
},
}), gomock.Any(), gomock.Any()).Times(1).Return(
container.CreateResponse{
containerType.CreateResponse{
ID: "an-id",
}, nil)
apiClient.EXPECT().ContainerInspect(gomock.Any(), gomock.Eq("an-id")).Times(1).Return(
container.InspectResponse{
ContainerJSONBase: &container.ContainerJSONBase{
moby.ContainerJSON{
ContainerJSONBase: &moby.ContainerJSONBase{
ID: "an-id",
Name: "a-name",
},
Config: &container.Config{},
NetworkSettings: &container.NetworkSettings{},
Config: &containerType.Config{},
NetworkSettings: &moby.NetworkSettings{},
}, nil)
_, err := tested.createMobyContainer(context.Background(), &project, service, "test", 0, nil, createOptions{
@ -433,4 +432,5 @@ func TestCreateMobyContainer(t *testing.T) {
}, progress.ContextWriter(context.TODO()))
assert.NilError(t, err)
})
}

View File

@ -139,6 +139,7 @@ func (s *composeService) listContainersTargetedForCopy(ctx context.Context, proj
}
if direction == fromService {
return containers[:1], err
}
return containers, err
}

View File

@ -34,12 +34,11 @@ 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"
@ -93,8 +92,7 @@ func (s *composeService) create(ctx context.Context, project *types.Project, opt
return err
}
volumes, err := s.ensureProjectVolumes(ctx, project, options.AssumeYes)
if err != nil {
if err := s.ensureProjectVolumes(ctx, project); err != nil {
return err
}
@ -117,7 +115,7 @@ 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, networks, volumes, s).apply(ctx, project, options)
return newConvergence(options.Services, observedState, networks, s).apply(ctx, project, options)
}
func prepareNetworks(project *types.Project) {
@ -143,17 +141,15 @@ func (s *composeService) ensureNetworks(ctx context.Context, project *types.Proj
return networks, nil
}
func (s *composeService) ensureProjectVolumes(ctx context.Context, project *types.Project, assumeYes bool) (map[string]string, error) {
ids := map[string]string{}
func (s *composeService) ensureProjectVolumes(ctx context.Context, project *types.Project) error {
for k, volume := range project.Volumes {
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)
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)
if err != nil {
return nil, err
return err
}
ids[k] = id
}
err := func() error {
@ -205,17 +201,18 @@ 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 ids, nil
return nil
}
func (s *composeService) getCreateConfigs(ctx context.Context,
p *types.Project,
service types.ServiceConfig,
number int,
inherit *container.Summary,
inherit *moby.Container,
opts createOptions,
) (createConfigs, error) {
labels, err := s.prepareLabels(opts.Labels, service, number)
@ -258,7 +255,7 @@ func (s *composeService) getCreateConfigs(ctx context.Context,
if err != nil {
return createConfigs{}, err
}
containerConfig := container.Config{
var containerConfig = container.Config{
Hostname: service.Hostname,
Domainname: service.DomainName,
User: service.User,
@ -274,7 +271,7 @@ func (s *composeService) getCreateConfigs(ctx context.Context,
WorkingDir: service.WorkingDir,
Entrypoint: entrypoint,
NetworkDisabled: service.NetworkMode == "disabled",
MacAddress: macAddress, // Field is deprecated since API v1.44, but kept for compatibility with older API versions.
MacAddress: macAddress,
Labels: labels,
StopSignal: service.StopSignal,
Env: ToMobyEnv(env),
@ -416,7 +413,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 && mainNw.MacAddress != "" {
if mainNw != nil {
macAddress = mainNw.MacAddress
}
}
@ -424,11 +421,11 @@ func (s *composeService) prepareContainerMACAddress(ctx context.Context, service
return macAddress, nil
}
func getAliases(project *types.Project, service types.ServiceConfig, serviceIndex int, cfg *types.ServiceNetworkConfig, useNetworkAliases bool) []string {
func getAliases(project *types.Project, service types.ServiceConfig, serviceIndex int, networkKey string, useNetworkAliases bool) []string {
aliases := []string{getContainerName(project.Name, service, serviceIndex)}
if useNetworkAliases {
aliases = append(aliases, service.Name)
if cfg != nil {
if cfg := service.Networks[networkKey]; cfg != nil {
aliases = append(aliases, cfg.Aliases...)
}
}
@ -443,7 +440,6 @@ func createEndpointSettings(p *types.Project, service types.ServiceConfig, servi
ipv6Address string
macAddress string
driverOpts types.Options
gwPriority int
)
if config != nil {
ipv4Address = config.Ipv4Address
@ -455,17 +451,15 @@ 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, config, useNetworkAliases),
Aliases: getAliases(p, service, serviceIndex, networkKey, useNetworkAliases),
Links: links,
IPAddress: ipv4Address,
IPv6Gateway: ipv6Address,
IPAMConfig: ipam,
MacAddress: macAddress,
DriverOpts: driverOpts,
GwPriority: gwPriority,
}
}
@ -486,10 +480,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" && con[1] != "builtin" {
if con[0] == "seccomp" && con[1] != "unconfined" {
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)
@ -551,28 +545,23 @@ func defaultNetworkSettings(
primaryNetworkKey = "default"
}
primaryNetworkMobyNetworkName := project.Networks[primaryNetworkKey].Name
primaryNetworkEndpoint := createEndpointSettings(project, service, serviceIndex, primaryNetworkKey, links, useNetworkAliases)
endpointsConfig := map[string]*network.EndpointSettings{}
endpointsConfig := map[string]*network.EndpointSettings{
primaryNetworkMobyNetworkName: createEndpointSettings(project, service, serviceIndex, primaryNetworkKey, links, useNetworkAliases),
}
// 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") {
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
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
}
}
endpointsConfig[primaryNetworkMobyNetworkName] = primaryNetworkEndpoint
networkConfig := &network.NetworkingConfig{
EndpointsConfig: endpointsConfig,
}
@ -832,13 +821,13 @@ func (s *composeService) buildContainerVolumes(
ctx context.Context,
p types.Project,
service types.ServiceConfig,
inherit *container.Summary,
inherit *moby.Container,
) ([]string, []mount.Mount, error) {
var mounts []mount.Mount
var binds []string
img := api.GetImageNameOrDefault(service, p.Name)
imgInspect, err := s.apiClient().ImageInspect(ctx, img)
image := api.GetImageNameOrDefault(service, p.Name)
imgInspect, _, err := s.apiClient().ImageInspectWithRaw(ctx, image)
if err != nil {
return nil, nil, err
}
@ -893,8 +882,8 @@ func requireMountAPI(bind *types.ServiceVolumeBind) bool {
}
}
func buildContainerMountOptions(p types.Project, s types.ServiceConfig, img image.InspectResponse, inherit *container.Summary) ([]mount.Mount, error) {
mounts := map[string]mount.Mount{}
func buildContainerMountOptions(p types.Project, s types.ServiceConfig, img moby.ImageInspect, inherit *moby.Container) ([]mount.Mount, error) {
var mounts = map[string]mount.Mount{}
if inherit != nil {
for _, m := range inherit.Mounts {
if m.Type == "tmpfs" {
@ -980,7 +969,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) {
mounts := map[string]mount.Mount{}
var mounts = map[string]mount.Mount{}
configsBaseDir := "/"
for _, config := range s.Configs {
@ -997,10 +986,10 @@ func buildContainerConfigMounts(p types.Project, s types.ServiceConfig) ([]mount
}
if definedConfig.Driver != "" {
return nil, errors.New("Docker Compose does not support configs.*.driver") //nolint:staticcheck
return nil, errors.New("Docker Compose does not support configs.*.driver")
}
if definedConfig.TemplateDriver != "" {
return nil, errors.New("Docker Compose does not support configs.*.template_driver") //nolint:staticcheck
return nil, errors.New("Docker Compose does not support configs.*.template_driver")
}
if definedConfig.Environment != "" || definedConfig.Content != "" {
@ -1030,7 +1019,7 @@ func buildContainerConfigMounts(p types.Project, s types.ServiceConfig) ([]mount
}
func buildContainerSecretMounts(p types.Project, s types.ServiceConfig) ([]mount.Mount, error) {
mounts := map[string]mount.Mount{}
var mounts = map[string]mount.Mount{}
secretsDir := "/run/secrets/"
for _, secret := range s.Secrets {
@ -1047,10 +1036,10 @@ func buildContainerSecretMounts(p types.Project, s types.ServiceConfig) ([]mount
}
if definedSecret.Driver != "" {
return nil, errors.New("Docker Compose does not support secrets.*.driver") //nolint:staticcheck
return nil, errors.New("Docker Compose does not support secrets.*.driver")
}
if definedSecret.TemplateDriver != "" {
return nil, errors.New("Docker Compose does not support secrets.*.template_driver") //nolint:staticcheck
return nil, errors.New("Docker Compose does not support secrets.*.template_driver")
}
if definedSecret.Environment != "" {
@ -1223,7 +1212,7 @@ func (s *composeService) ensureNetwork(ctx context.Context, project *types.Proje
if errdefs.IsConflict(err) {
// Maybe another execution of `docker compose up|run` created same network
// let's retry once
return s.resolveOrCreateNetwork(ctx, project, name, n)
return s.resolveOrCreateNetwork(ctx, project, "", n)
}
return id, err
}
@ -1328,7 +1317,6 @@ func (s *composeService) resolveOrCreateNetwork(ctx context.Context, project *ty
Attachable: n.Attachable,
IPAM: ipam,
EnableIPv6: n.EnableIPv6,
EnableIPv4: n.EnableIPv4,
}
if n.Ipam.Driver != "" || len(n.Ipam.Config) > 0 {
@ -1348,7 +1336,6 @@ func (s *composeService) resolveOrCreateNetwork(ctx context.Context, project *ty
}
createOpts.IPAM.Config = append(createOpts.IPAM.Config, config)
}
networkEventName := fmt.Sprintf("Network %s", n.Name)
w := progress.ContextWriter(ctx)
w.Event(progress.CreatingEvent(networkEventName))
@ -1396,6 +1383,7 @@ 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
}
@ -1438,21 +1426,21 @@ func (s *composeService) resolveExternalNetwork(ctx context.Context, n *types.Ne
}
}
func (s *composeService) ensureVolume(ctx context.Context, name string, volume types.VolumeConfig, project *types.Project, assumeYes bool) (string, error) {
func (s *composeService) ensureVolume(ctx context.Context, volume types.VolumeConfig, project 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 volume.Name, err
err := s.createVolume(ctx, volume)
return err
}
if volume.External {
return volume.Name, nil
return nil
}
// Volume exists with name, but let's double-check this is the expected one
@ -1460,86 +1448,18 @@ func (s *composeService) ensureVolume(ctx context.Context, name string, volume t
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.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)
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)
}
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)
return nil
}
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))
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),
_, err := s.apiClient().VolumeCreate(ctx, volumetypes.CreateOptions{
Labels: volume.Labels,
Name: volume.Name,
Driver: volume.Driver,
DriverOpts: volume.DriverOpts,

View File

@ -22,14 +22,13 @@ 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"
@ -141,8 +140,8 @@ func TestBuildContainerMountOptions(t *testing.T) {
}),
}
inherit := &container.Summary{
Mounts: []container.MountPoint{
inherit := &moby.Container{
Mounts: []moby.MountPoint{
{
Type: composetypes.VolumeTypeVolume,
Destination: "/var/myvolume1",
@ -154,7 +153,7 @@ func TestBuildContainerMountOptions(t *testing.T) {
},
}
mounts, err := buildContainerMountOptions(project, project.Services["myService"], image.InspectResponse{}, inherit)
mounts, err := buildContainerMountOptions(project, project.Services["myService"], moby.ImageInspect{}, inherit)
sort.Slice(mounts, func(i, j int) bool {
return mounts[i].Target < mounts[j].Target
})
@ -166,7 +165,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"], image.InspectResponse{}, inherit)
mounts, err = buildContainerMountOptions(project, project.Services["myService"], moby.ImageInspect{}, inherit)
sort.Slice(mounts, func(i, j int) bool {
return mounts[i].Target < mounts[j].Target
})
@ -279,26 +278,33 @@ 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, "netName", []string{"link1", "link2"}, true)
0, // serviceIndex
"netName", // networkKey
[]string{"link1", "link2"}, // links
true, // useNetworkAliases
)
assert.Check(t, cmp.DeepEqual(eps, &network.EndpointSettings{
IPAMConfig: &network.EndpointIPAMConfig{
IPv4Address: "10.16.17.18",

View File

@ -172,6 +172,7 @@ 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
@ -437,6 +438,7 @@ 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
}

View File

@ -27,6 +27,7 @@ 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"
@ -83,11 +84,8 @@ func (s *composeService) down(ctx context.Context, projectName string, options a
}
err = InReverseDependencyOrder(ctx, project, func(c context.Context, service string) error {
serv := project.Services[service]
if serv.Provider != nil {
return s.runPlugin(ctx, project, serv, "down")
}
serviceContainers := containers.filter(isService(service))
serv := project.Services[service]
err := s.removeContainers(ctx, serviceContainers, &serv, options.Timeout, options.Volumes)
return err
}, WithRootNodesAndDown(options.Services))
@ -299,13 +297,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, ctr containerType.Summary, timeout *time.Duration) error {
eventName := getContainerProgressName(ctr)
func (s *composeService) stopContainer(ctx context.Context, w progress.Writer, service *types.ServiceConfig, container moby.Container, timeout *time.Duration) error {
eventName := getContainerProgressName(container)
w.Event(progress.StoppingEvent(eventName))
if service != nil {
for _, hook := range service.PreStop {
err := s.runHook(ctx, ctr, *service, hook, nil)
err := s.runHook(ctx, container, *service, hook, nil)
if err != nil {
return err
}
@ -313,7 +311,7 @@ func (s *composeService) stopContainer(ctx context.Context, w progress.Writer, s
}
timeoutInSecond := utils.DurationSecondToInt(timeout)
err := s.apiClient().ContainerStop(ctx, ctr.ID, containerType.StopOptions{Timeout: timeoutInSecond})
err := s.apiClient().ContainerStop(ctx, container.ID, containerType.StopOptions{Timeout: timeoutInSecond})
if err != nil {
w.Event(progress.ErrorMessageEvent(eventName, "Error while Stopping"))
return err
@ -322,30 +320,32 @@ 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 []containerType.Summary, timeout *time.Duration) error {
func (s *composeService) stopContainers(ctx context.Context, w progress.Writer, serv *types.ServiceConfig, containers []moby.Container, timeout *time.Duration) error {
eg, ctx := errgroup.WithContext(ctx)
for _, ctr := range containers {
for _, container := range containers {
container := container
eg.Go(func() error {
return s.stopContainer(ctx, w, serv, ctr, timeout)
return s.stopContainer(ctx, w, serv, container, timeout)
})
}
return eg.Wait()
}
func (s *composeService) removeContainers(ctx context.Context, containers []containerType.Summary, service *types.ServiceConfig, timeout *time.Duration, volumes bool) error {
func (s *composeService) removeContainers(ctx context.Context, containers []moby.Container, service *types.ServiceConfig, timeout *time.Duration, volumes bool) error {
eg, _ := errgroup.WithContext(ctx)
for _, ctr := range containers {
for _, container := range containers {
container := container
eg.Go(func() error {
return s.stopAndRemoveContainer(ctx, ctr, service, timeout, volumes)
return s.stopAndRemoveContainer(ctx, container, service, timeout, volumes)
})
}
return eg.Wait()
}
func (s *composeService) stopAndRemoveContainer(ctx context.Context, ctr containerType.Summary, service *types.ServiceConfig, timeout *time.Duration, volumes bool) error {
func (s *composeService) stopAndRemoveContainer(ctx context.Context, container moby.Container, service *types.ServiceConfig, timeout *time.Duration, volumes bool) error {
w := progress.ContextWriter(ctx)
eventName := getContainerProgressName(ctr)
err := s.stopContainer(ctx, w, service, ctr, timeout)
eventName := getContainerProgressName(container)
err := s.stopContainer(ctx, w, service, container, timeout)
if errdefs.IsNotFound(err) {
w.Event(progress.RemovedEvent(eventName))
return nil
@ -354,7 +354,7 @@ func (s *composeService) stopAndRemoveContainer(ctx context.Context, ctr contain
return err
}
w.Event(progress.RemovingEvent(eventName))
err = s.apiClient().ContainerRemove(ctx, ctr.ID, containerType.RemoveOptions{
err = s.apiClient().ContainerRemove(ctx, container.ID, containerType.RemoveOptions{
Force: true,
RemoveVolumes: volumes,
})

View File

@ -25,7 +25,8 @@ import (
"github.com/compose-spec/compose-go/v2/types"
"github.com/docker/cli/cli/streams"
"github.com/docker/docker/api/types/container"
moby "github.com/docker/docker/api/types"
containerType "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"
@ -48,7 +49,7 @@ func TestDown(t *testing.T) {
}
api.EXPECT().ContainerList(gomock.Any(), projectFilterListOpt(false)).Return(
[]container.Summary{
[]moby.Container{
testContainer("service1", "123", false),
testContainer("service2", "456", false),
testContainer("service2", "789", false),
@ -69,14 +70,14 @@ func TestDown(t *testing.T) {
{ID: "def456", Name: "myProject_default", Labels: map[string]string{compose.NetworkLabel: "default"}},
}, nil)
stopOptions := container.StopOptions{}
stopOptions := containerType.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", 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().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().NetworkList(gomock.Any(), network.ListOptions{
Filters: filters.NewArgs(
@ -105,7 +106,7 @@ func TestDownWithGivenServices(t *testing.T) {
}
api.EXPECT().ContainerList(gomock.Any(), projectFilterListOpt(false)).Return(
[]container.Summary{
[]moby.Container{
testContainer("service1", "123", false),
testContainer("service2", "456", false),
testContainer("service2", "789", false),
@ -126,10 +127,10 @@ func TestDownWithGivenServices(t *testing.T) {
{ID: "def456", Name: "myProject_default", Labels: map[string]string{compose.NetworkLabel: "default"}},
}, nil)
stopOptions := container.StopOptions{}
stopOptions := containerType.StopOptions{}
api.EXPECT().ContainerStop(gomock.Any(), "123", stopOptions).Return(nil)
api.EXPECT().ContainerRemove(gomock.Any(), "123", container.RemoveOptions{Force: true}).Return(nil)
api.EXPECT().ContainerRemove(gomock.Any(), "123", containerType.RemoveOptions{Force: true}).Return(nil)
api.EXPECT().NetworkList(gomock.Any(), network.ListOptions{
Filters: filters.NewArgs(
@ -157,7 +158,7 @@ func TestDownWithSpecifiedServiceButTheServicesAreNotRunning(t *testing.T) {
}
api.EXPECT().ContainerList(gomock.Any(), projectFilterListOpt(false)).Return(
[]container.Summary{
[]moby.Container{
testContainer("service1", "123", false),
testContainer("service2", "456", false),
testContainer("service2", "789", false),
@ -194,7 +195,7 @@ func TestDownRemoveOrphans(t *testing.T) {
}
api.EXPECT().ContainerList(gomock.Any(), projectFilterListOpt(true)).Return(
[]container.Summary{
[]moby.Container{
testContainer("service1", "123", false),
testContainer("service2", "789", false),
testContainer("service_orphan", "321", true),
@ -210,17 +211,16 @@ func TestDownRemoveOrphans(t *testing.T) {
{
Name: "myProject_default",
Labels: map[string]string{compose.NetworkLabel: "default"},
},
}, nil)
}}, nil)
stopOptions := container.StopOptions{}
stopOptions := containerType.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", 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().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().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(
[]container.Summary{testContainer("service1", "123", false)}, nil)
[]moby.Container{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", container.StopOptions{}).Return(nil)
api.EXPECT().ContainerRemove(gomock.Any(), "123", container.RemoveOptions{Force: true, RemoveVolumes: true}).Return(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().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([]container.Summary{
Return([]moby.Container{
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 image.InspectResponse
var resp moby.ImageInspect
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().ImageInspect(gomock.Any(), img).
Return(resp, err).
api.EXPECT().ImageInspectWithRaw(gomock.Any(), img).
Return(resp, nil, err).
AnyTimes()
}
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).
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).
AnyTimes()
localImagesToBeRemoved := []string{
@ -378,10 +378,10 @@ func TestDownRemoveImages_NoLabel(t *testing.T) {
dockerCli: cli,
}
ctr := testContainer("service1", "123", false)
container := testContainer("service1", "123", false)
api.EXPECT().ContainerList(gomock.Any(), projectFilterListOpt(false)).Return(
[]container.Summary{ctr}, nil)
[]moby.Container{container}, nil)
api.EXPECT().VolumeList(
gomock.Any(),
@ -403,11 +403,11 @@ func TestDownRemoveImages_NoLabel(t *testing.T) {
),
}).Return(nil, nil)
api.EXPECT().ImageInspect(gomock.Any(), "testproject-service1").
Return(image.InspectResponse{}, nil)
api.EXPECT().ImageInspectWithRaw(gomock.Any(), "testproject-service1").
Return(moby.ImageInspect{}, nil, 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().ContainerStop(gomock.Any(), "123", containerType.StopOptions{}).Return(nil)
api.EXPECT().ContainerRemove(gomock.Any(), "123", containerType.RemoveOptions{Force: true}).Return(nil)
api.EXPECT().ImageRemove(gomock.Any(), "testproject-service1:latest", image.RemoveOptions{}).Return(nil, nil)

View File

@ -21,8 +21,10 @@ import (
"strings"
)
// isCaseInsensitiveEnvVars is true on platforms where environment variable names are treated case-insensitively.
var isCaseInsensitiveEnvVars = (runtime.GOOS == "windows")
var (
// isCaseInsensitiveEnvVars is true on platforms where environment variable names are treated case-insensitively.
isCaseInsensitiveEnvVars = (runtime.GOOS == "windows")
)
// envResolver returns resolver for environment variables suitable for the current platform.
// Expected to be used with `MappingWithEquals.Resolve`.

71
pkg/compose/errors.go Normal file
View File

@ -0,0 +1,71 @@
/*
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
}

View File

@ -24,7 +24,7 @@ import (
"github.com/docker/cli/cli"
"github.com/docker/cli/cli/command/container"
"github.com/docker/compose/v2/pkg/api"
containerType "github.com/docker/docker/api/types/container"
moby "github.com/docker/docker/api/types"
)
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) (containerType.Summary, error) {
func (s *composeService) getExecTarget(ctx context.Context, projectName string, opts api.RunOptions) (moby.Container, error) {
return s.getSpecifiedContainer(ctx, projectName, oneOffInclude, false, opts.Service, opts.Index)
}

View File

@ -24,7 +24,8 @@ import (
"github.com/compose-spec/compose-go/v2/types"
"github.com/docker/compose/v2/pkg/api"
"github.com/docker/compose/v2/pkg/utils"
"github.com/docker/docker/api/types/container"
moby "github.com/docker/docker/api/types"
containerType "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"
@ -39,7 +40,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, container.ListOptions{
containers, err := s.apiClient().ContainerList(ctx, containerType.ListOptions{
Filters: filtersListNames,
All: true,
})
@ -47,16 +48,16 @@ func (s *composeService) Generate(ctx context.Context, options api.GenerateOptio
return nil, err
}
containersByIds, err := s.apiClient().ContainerList(ctx, container.ListOptions{
containersByIds, err := s.apiClient().ContainerList(ctx, containerType.ListOptions{
Filters: filtersListIDs,
All: true,
})
if err != nil {
return nil, err
}
for _, ctr := range containersByIds {
if !utils.Contains(containers, ctr) {
containers = append(containers, ctr)
for _, container := range containersByIds {
if !utils.Contains(containers, container) {
containers = append(containers, container)
}
}
@ -67,7 +68,7 @@ func (s *composeService) Generate(ctx context.Context, options api.GenerateOptio
return s.createProjectFromContainers(containers, options.ProjectName)
}
func (s *composeService) createProjectFromContainers(containers []container.Summary, projectName string) (*types.Project, error) {
func (s *composeService) createProjectFromContainers(containers []moby.Container, projectName string) (*types.Project, error) {
project := &types.Project{}
services := types.Services{}
networks := types.Networks{}
@ -91,6 +92,7 @@ func (s *composeService) createProjectFromContainers(containers []container.Summ
Image: c.Image,
Labels: c.Labels,
}
}
service.Scale = increment(service.Scale)
@ -111,7 +113,7 @@ func (s *composeService) createProjectFromContainers(containers []container.Summ
return project, nil
}
func (s *composeService) extractComposeConfiguration(service *types.ServiceConfig, inspect container.InspectResponse, volumes types.Volumes, secrets types.Secrets, networks types.Networks) {
func (s *composeService) extractComposeConfiguration(service *types.ServiceConfig, inspect moby.ContainerJSON, 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
@ -143,7 +145,7 @@ func (s *composeService) extractComposeConfiguration(service *types.ServiceConfi
}
}
func (s *composeService) toComposeHealthCheck(healthConfig *container.HealthConfig) *types.HealthCheckConfig {
func (s *composeService) toComposeHealthCheck(healthConfig *containerType.HealthConfig) *types.HealthCheckConfig {
var healthCheck types.HealthCheckConfig
healthCheck.Test = healthConfig.Test
if healthConfig.Timeout != 0 {
@ -169,9 +171,8 @@ func (s *composeService) toComposeHealthCheck(healthConfig *container.HealthConf
return &healthCheck
}
func (s *composeService) toComposeVolumes(volumes []container.MountPoint) (map[string]types.VolumeConfig,
[]types.ServiceVolumeConfig, map[string]types.SecretConfig, []types.ServiceSecretConfig,
) {
func (s *composeService) toComposeVolumes(volumes []moby.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
@ -226,6 +227,7 @@ func (s *composeService) toComposeNetwork(networks map[string]*network.EndpointS
networkConfigs[name] = types.NetworkConfig{
Internal: inspect.Internal,
}
}
serviceNetworkConfigs[name] = &types.ServiceNetworkConfig{
Aliases: net.Aliases,

View File

@ -42,7 +42,6 @@ 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 {
@ -50,15 +49,3 @@ func NetworkHash(o *types.NetworkConfig) (string, error) {
}
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
}

View File

@ -25,16 +25,17 @@ import (
"github.com/compose-spec/compose-go/v2/types"
"github.com/docker/compose/v2/pkg/api"
"github.com/docker/compose/v2/pkg/utils"
"github.com/docker/docker/api/types/container"
moby "github.com/docker/docker/api/types"
containerType "github.com/docker/docker/api/types/container"
"github.com/docker/docker/pkg/stdcopy"
)
func (s composeService) runHook(ctx context.Context, ctr container.Summary, service types.ServiceConfig, hook types.ServiceHook, listener api.ContainerEventListener) error {
func (s composeService) runHook(ctx context.Context, container moby.Container, service types.ServiceConfig, hook types.ServiceHook, listener api.ContainerEventListener) error {
wOut := utils.GetWriter(func(line string) {
listener(api.ContainerEvent{
Type: api.HookEventLog,
Container: getContainerNameWithoutProject(ctr) + " ->",
ID: ctr.ID,
Container: getContainerNameWithoutProject(container) + " ->",
ID: container.ID,
Service: service.Name,
Line: line,
})
@ -42,7 +43,7 @@ func (s composeService) runHook(ctx context.Context, ctr container.Summary, serv
defer wOut.Close() //nolint:errcheck
detached := listener == nil
exec, err := s.apiClient().ContainerExecCreate(ctx, ctr.ID, container.ExecOptions{
exec, err := s.apiClient().ContainerExecCreate(ctx, container.ID, containerType.ExecOptions{
User: hook.User,
Privileged: hook.Privileged,
Env: ToMobyEnv(hook.Environment),
@ -57,12 +58,12 @@ func (s composeService) runHook(ctx context.Context, ctr container.Summary, serv
}
if detached {
return s.runWaitExec(ctx, exec.ID, service, listener)
return s.runWaitExec(ctx, exec, service, listener)
}
height, width := s.stdout().GetTtySize()
consoleSize := &[2]uint{height, width}
attach, err := s.apiClient().ContainerExecAttach(ctx, exec.ID, container.ExecAttachOptions{
attach, err := s.apiClient().ContainerExecAttach(ctx, exec.ID, containerType.ExecAttachOptions{
Tty: service.Tty,
ConsoleSize: consoleSize,
})
@ -90,8 +91,8 @@ func (s composeService) runHook(ctx context.Context, ctr container.Summary, serv
return nil
}
func (s composeService) runWaitExec(ctx context.Context, execID string, service types.ServiceConfig, listener api.ContainerEventListener) error {
err := s.apiClient().ContainerExecStart(ctx, execID, container.ExecStartOptions{
func (s composeService) runWaitExec(ctx context.Context, exec moby.IDResponse, service types.ServiceConfig, listener api.ContainerEventListener) error {
err := s.apiClient().ContainerExecStart(ctx, exec.ID, containerType.ExecStartOptions{
Detach: listener == nil,
Tty: service.Tty,
})
@ -106,7 +107,7 @@ func (s composeService) runWaitExec(ctx context.Context, execID string, service
case <-ctx.Done():
return nil
case <-tick.C:
inspect, err := s.apiClient().ContainerExecInspect(ctx, execID)
inspect, err := s.apiClient().ContainerExecInspect(ctx, exec.ID)
if err != nil {
return nil
}

View File

@ -202,8 +202,9 @@ func (p *ImagePruner) filterImagesByExistence(ctx context.Context, imageNames []
eg, ctx := errgroup.WithContext(ctx)
for _, img := range imageNames {
img := img
eg.Go(func() error {
_, err := p.client.ImageInspect(ctx, img)
_, _, err := p.client.ImageInspectWithRaw(ctx, img)
if errdefs.IsNotFound(err) {
// err on the side of caution: only skip if we successfully
// queried the API and got back a definitive "not exists"

View File

@ -23,7 +23,8 @@ import (
"sync"
"github.com/distribution/reference"
"github.com/docker/docker/api/types/container"
moby "github.com/docker/docker/api/types"
containerType "github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/errdefs"
"golang.org/x/sync/errgroup"
@ -34,19 +35,20 @@ import (
func (s *composeService) Images(ctx context.Context, projectName string, options api.ImagesOptions) ([]api.ImageSummary, error) {
projectName = strings.ToLower(projectName)
allContainers, err := s.apiClient().ContainerList(ctx, container.ListOptions{
allContainers, err := s.apiClient().ContainerList(ctx, containerType.ListOptions{
All: true,
Filters: filters.NewArgs(projectFilter(projectName)),
})
if err != nil {
return nil, err
}
var containers []container.Summary
containers := []moby.Container{}
if len(options.Services) > 0 {
// filter service containers
for _, c := range allContainers {
if utils.StringContains(options.Services, c.Labels[api.ServiceLabel]) {
containers = append(containers, c)
}
}
} else {
@ -64,14 +66,14 @@ func (s *composeService) Images(ctx context.Context, projectName string, options
return nil, err
}
summary := make([]api.ImageSummary, len(containers))
for i, c := range containers {
img, ok := imageSummaries[c.Image]
for i, container := range containers {
img, ok := imageSummaries[container.Image]
if !ok {
return nil, fmt.Errorf("failed to retrieve image for container %s", getCanonicalContainerName(c))
return nil, fmt.Errorf("failed to retrieve image for container %s", getCanonicalContainerName(container))
}
summary[i] = img
summary[i].ContainerName = getCanonicalContainerName(c)
summary[i].ContainerName = getCanonicalContainerName(container)
}
return summary, nil
}
@ -81,8 +83,9 @@ func (s *composeService) getImageSummaries(ctx context.Context, repoTags []strin
l := sync.Mutex{}
eg, ctx := errgroup.WithContext(ctx)
for _, repoTag := range repoTags {
repoTag := repoTag
eg.Go(func() error {
inspect, err := s.apiClient().ImageInspect(ctx, repoTag)
inspect, _, err := s.apiClient().ImageInspectWithRaw(ctx, repoTag)
if err != nil {
if errdefs.IsNotFound(err) {
return nil
@ -92,20 +95,19 @@ func (s *composeService) getImageSummaries(ctx context.Context, repoTags []strin
tag := ""
repository := ""
ref, err := reference.ParseDockerRef(repoTag)
if err == nil {
// ParseDockerRef will reject a local image ID
repository = reference.FamiliarName(ref)
if tagged, ok := ref.(reference.Tagged); ok {
tag = tagged.Tag()
}
if err != nil {
return err
}
repository = reference.FamiliarName(ref)
if tagged, ok := ref.(reference.Tagged); ok {
tag = tagged.Tag()
}
l.Lock()
summary[repoTag] = api.ImageSummary{
ID: inspect.ID,
Repository: repository,
Tag: tag,
Size: inspect.Size,
LastTagTime: inspect.Metadata.LastTagTime,
ID: inspect.ID,
Repository: repository,
Tag: tag,
Size: inspect.Size,
}
l.Unlock()
return nil

View File

@ -21,13 +21,13 @@ import (
"strings"
"testing"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/api/types/image"
containerType "github.com/docker/docker/api/types/container"
"go.uber.org/mock/gomock"
"gotest.tools/v3/assert"
compose "github.com/docker/compose/v2/pkg/api"
moby "github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/filters"
)
func TestImages(t *testing.T) {
@ -41,16 +41,16 @@ func TestImages(t *testing.T) {
ctx := context.Background()
args := filters.NewArgs(projectFilter(strings.ToLower(testProject)))
listOpts := container.ListOptions{All: true, Filters: args}
listOpts := containerType.ListOptions{All: true, Filters: args}
image1 := imageInspect("image1", "foo:1", 12345)
image2 := imageInspect("image2", "bar:2", 67890)
api.EXPECT().ImageInspect(anyCancellableContext(), "foo:1").Return(image1, nil)
api.EXPECT().ImageInspect(anyCancellableContext(), "bar:2").Return(image2, nil)
api.EXPECT().ImageInspectWithRaw(anyCancellableContext(), "foo:1").Return(image1, nil, nil)
api.EXPECT().ImageInspectWithRaw(anyCancellableContext(), "bar:2").Return(image2, nil, nil)
c1 := containerDetail("service1", "123", "running", "foo:1")
c2 := containerDetail("service1", "456", "running", "bar:2")
c2.Ports = []container.Port{{PublicPort: 80, PrivatePort: 90, IP: "localhost"}}
c2.Ports = []moby.Port{{PublicPort: 80, PrivatePort: 90, IP: "localhost"}}
c3 := containerDetail("service2", "789", "exited", "foo:1")
api.EXPECT().ContainerList(ctx, listOpts).Return([]container.Summary{c1, c2, c3}, nil)
api.EXPECT().ContainerList(ctx, listOpts).Return([]moby.Container{c1, c2, c3}, nil)
images, err := tested.Images(ctx, strings.ToLower(testProject), compose.ImagesOptions{})
@ -81,22 +81,22 @@ func TestImages(t *testing.T) {
assert.DeepEqual(t, images, expected)
}
func imageInspect(id string, imageReference string, size int64) image.InspectResponse {
return image.InspectResponse{
func imageInspect(id string, image string, size int64) moby.ImageInspect {
return moby.ImageInspect{
ID: id,
RepoTags: []string{
"someRepo:someTag",
imageReference,
image,
},
Size: size,
}
}
func containerDetail(service string, id string, status string, imageName string) container.Summary {
return container.Summary{
func containerDetail(service string, id string, status string, image string) moby.Container {
return moby.Container{
ID: id,
Names: []string{"/" + id},
Image: imageName,
Image: image,
Labels: containerLabels(service, false),
State: status,
}

View File

@ -21,7 +21,7 @@ import (
"fmt"
"strings"
"github.com/docker/docker/api/types/container"
moby "github.com/docker/docker/api/types"
"golang.org/x/sync/errgroup"
"github.com/docker/compose/v2/pkg/api"
@ -62,18 +62,19 @@ func (s *composeService) kill(ctx context.Context, projectName string, options a
}
eg, ctx := errgroup.WithContext(ctx)
containers.forEach(func(ctr container.Summary) {
eg.Go(func() error {
eventName := getContainerProgressName(ctr)
w.Event(progress.KillingEvent(eventName))
err := s.apiClient().ContainerKill(ctx, ctr.ID, options.Signal)
if err != nil {
w.Event(progress.ErrorMessageEvent(eventName, "Error while Killing"))
return err
}
w.Event(progress.KilledEvent(eventName))
return nil
containers.
forEach(func(container moby.Container) {
eg.Go(func() error {
eventName := getContainerProgressName(container)
w.Event(progress.KillingEvent(eventName))
err := s.apiClient().ContainerKill(ctx, container.ID, options.Signal)
if err != nil {
w.Event(progress.ErrorMessageEvent(eventName, "Error while Killing"))
return err
}
w.Event(progress.KilledEvent(eventName))
return nil
})
})
})
return eg.Wait()
}

View File

@ -23,7 +23,8 @@ import (
"strings"
"testing"
"github.com/docker/docker/api/types/container"
moby "github.com/docker/docker/api/types"
containerType "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/volume"
@ -47,10 +48,10 @@ func TestKillAll(t *testing.T) {
name := strings.ToLower(testProject)
ctx := context.Background()
api.EXPECT().ContainerList(ctx, container.ListOptions{
api.EXPECT().ContainerList(ctx, containerType.ListOptions{
Filters: filters.NewArgs(projectFilter(name), hasConfigHashLabel()),
}).Return(
[]container.Summary{testContainer("service1", "123", false), testContainer("service1", "456", false), testContainer("service2", "789", false)}, nil)
[]moby.Container{testContainer("service1", "123", false), testContainer("service1", "456", false), testContainer("service2", "789", false)}, nil)
api.EXPECT().VolumeList(
gomock.Any(),
volume.ListOptions{
@ -80,12 +81,12 @@ func TestKillSignal(t *testing.T) {
}
name := strings.ToLower(testProject)
listOptions := container.ListOptions{
listOptions := containerType.ListOptions{
Filters: filters.NewArgs(projectFilter(name), serviceFilter(serviceName), hasConfigHashLabel()),
}
ctx := context.Background()
api.EXPECT().ContainerList(ctx, listOptions).Return([]container.Summary{testContainer(serviceName, "123", false)}, nil)
api.EXPECT().ContainerList(ctx, listOptions).Return([]moby.Container{testContainer(serviceName, "123", false)}, nil)
api.EXPECT().VolumeList(
gomock.Any(),
volume.ListOptions{
@ -102,12 +103,12 @@ func TestKillSignal(t *testing.T) {
assert.NilError(t, err)
}
func testContainer(service string, id string, oneOff bool) container.Summary {
func testContainer(service string, id string, oneOff bool) moby.Container {
// canonical docker names in the API start with a leading slash, some
// parts of Compose code will attempt to strip this off, so make sure
// it's consistently present
name := "/" + strings.TrimPrefix(id, "/")
return container.Summary{
return moby.Container{
ID: id,
Names: []string{name},
Labels: containerLabels(service, oneOff),
@ -122,8 +123,7 @@ func containerLabels(service string, oneOff bool) map[string]string {
compose.ServiceLabel: service,
compose.ConfigFilesLabel: composefile,
compose.WorkingDirLabel: workingdir,
compose.ProjectLabel: strings.ToLower(testProject),
}
compose.ProjectLabel: strings.ToLower(testProject)}
if oneOff {
labels[compose.OneoffLabel] = "True"
}
@ -136,7 +136,7 @@ func anyCancellableContext() gomock.Matcher {
return gomock.AssignableToTypeOf(ctxWithCancel)
}
func projectFilterListOpt(withOneOff bool) container.ListOptions {
func projectFilterListOpt(withOneOff bool) containerType.ListOptions {
filter := filters.NewArgs(
projectFilter(strings.ToLower(testProject)),
hasConfigHashLabel(),
@ -144,7 +144,7 @@ func projectFilterListOpt(withOneOff bool) container.ListOptions {
if !withOneOff {
filter.Add("label", fmt.Sprintf("%s=False", compose.OneoffLabel))
}
return container.ListOptions{
return containerType.ListOptions{
Filters: filter,
All: true,
}

Some files were not shown because too many files have changed in this diff Show More