mirror of
https://github.com/docker/compose.git
synced 2026-02-16 21:42:33 +08:00
Compare commits
28 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f9828dfab9 | ||
|
|
da691c7cc1 | ||
|
|
fefdc95224 | ||
|
|
08c32e9033 | ||
|
|
bdb630fb89 | ||
|
|
3697b128a1 | ||
|
|
b285e48976 | ||
|
|
8193d86d2f | ||
|
|
bfb5511d0d | ||
|
|
7abaa06617 | ||
|
|
3b0e8f538e | ||
|
|
af376603c3 | ||
|
|
7f8814f4c5 | ||
|
|
af0029afe1 | ||
|
|
b76feb66e1 | ||
|
|
9dc7f1e70c | ||
|
|
03205124fe | ||
|
|
8b769bad6b | ||
|
|
671507a8b3 | ||
|
|
56ab28aef3 | ||
|
|
e7d870a106 | ||
|
|
d5bb3387ca | ||
|
|
d91fc63813 | ||
|
|
c51b1fea29 | ||
|
|
fa7549a851 | ||
|
|
a061c17736 | ||
|
|
c5e7d9158c | ||
|
|
3783b8ada3 |
153
.github/workflows/ci.yml
vendored
153
.github/workflows/ci.yml
vendored
@@ -22,24 +22,6 @@ permissions:
|
||||
contents: read # to fetch code (actions/checkout)
|
||||
|
||||
jobs:
|
||||
prepare:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
matrix: ${{ steps.platforms.outputs.matrix }}
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
-
|
||||
name: Create matrix
|
||||
id: platforms
|
||||
run: |
|
||||
echo matrix=$(docker buildx bake binary-cross --print | jq -cr '.target."binary-cross".platforms') >> $GITHUB_OUTPUT
|
||||
-
|
||||
name: Show matrix
|
||||
run: |
|
||||
echo ${{ steps.platforms.outputs.matrix }}
|
||||
|
||||
validate:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
@@ -63,63 +45,88 @@ jobs:
|
||||
make ${{ matrix.target }}
|
||||
|
||||
binary:
|
||||
uses: docker/github-builder/.github/workflows/bake.yml@v1
|
||||
permissions:
|
||||
contents: read # same as global permission
|
||||
id-token: write # for signing attestation(s) with GitHub OIDC Token
|
||||
with:
|
||||
runner: amd64
|
||||
artifact-name: compose
|
||||
artifact-upload: true
|
||||
cache: true
|
||||
cache-scope: binary
|
||||
target: release
|
||||
output: local
|
||||
sbom: true
|
||||
sign: ${{ github.event_name != 'pull_request' }}
|
||||
|
||||
binary-finalize:
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- prepare
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
platform: ${{ fromJson(needs.prepare.outputs.matrix) }}
|
||||
- binary
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
-
|
||||
name: Prepare
|
||||
run: |
|
||||
platform=${MATRIX_PLATFORM}
|
||||
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
|
||||
env:
|
||||
MATRIX_PLATFORM: ${{ matrix.platform }}
|
||||
-
|
||||
name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
-
|
||||
name: Build
|
||||
uses: docker/bake-action@v6
|
||||
name: Download artifacts
|
||||
uses: actions/download-artifact@v7
|
||||
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
|
||||
path: /tmp/compose-output
|
||||
name: ${{ needs.binary.outputs.artifact-name }}
|
||||
-
|
||||
name: Rename provenance and sbom
|
||||
run: |
|
||||
for pdir in /tmp/compose-output/*/; do
|
||||
(
|
||||
cd "$pdir"
|
||||
binname=$(find . -name 'docker-compose-*')
|
||||
filename=$(basename "${binname%.exe}")
|
||||
mv "provenance.json" "${filename}.provenance.json"
|
||||
mv "sbom-binary.spdx.json" "${filename}.sbom.json"
|
||||
find . -name 'sbom*.json' -exec rm {} \;
|
||||
if [ -f "provenance.sigstore.json" ]; then
|
||||
mv "provenance.sigstore.json" "${filename}.sigstore.json"
|
||||
fi
|
||||
)
|
||||
done
|
||||
mkdir -p "./bin/release"
|
||||
mv /tmp/compose-output/**/* "./bin/release/"
|
||||
-
|
||||
name: Create checksum file
|
||||
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
|
||||
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 && "${file#\*}" != *.sigstore.json ]]; then
|
||||
echo "$sum $file" > ${file#\*}.sha256
|
||||
fi
|
||||
done
|
||||
-
|
||||
name: Upload artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: compose-${{ env.PLATFORM_PAIR }}
|
||||
path: ./bin/release
|
||||
name: release
|
||||
path: ./bin/release/*
|
||||
if-no-files-found: error
|
||||
|
||||
bin-image-test:
|
||||
if: github.event_name == 'pull_request'
|
||||
uses: docker/github-builder/.github/workflows/bake.yml@v1
|
||||
with:
|
||||
runner: amd64
|
||||
target: image-cross
|
||||
cache: true
|
||||
cache-scope: bin-image-test
|
||||
output: image
|
||||
push: false
|
||||
sbom: true
|
||||
set-meta-labels: true
|
||||
meta-images: |
|
||||
compose-bin
|
||||
meta-tags: |
|
||||
type=ref,event=pr
|
||||
meta-bake-target: meta-helper
|
||||
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
@@ -147,6 +154,7 @@ jobs:
|
||||
with:
|
||||
paths: bin/coverage/unit/report.xml
|
||||
if: always()
|
||||
|
||||
e2e:
|
||||
runs-on: ubuntu-latest
|
||||
name: e2e (${{ matrix.mode }}, ${{ matrix.channel }})
|
||||
@@ -254,6 +262,7 @@ jobs:
|
||||
with:
|
||||
paths: /tmp/report/report.xml
|
||||
if: always()
|
||||
|
||||
coverage:
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
@@ -290,40 +299,26 @@ jobs:
|
||||
path: ./coverage.txt
|
||||
if-no-files-found: error
|
||||
- name: Upload coverage to Codecov
|
||||
uses: codecov/codecov-action@v3
|
||||
uses: codecov/codecov-action@v5
|
||||
with:
|
||||
files: ./coverage.txt
|
||||
|
||||
release:
|
||||
permissions:
|
||||
contents: write # to create a release (ncipollo/release-action)
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- binary
|
||||
- binary-finalize
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
-
|
||||
name: Download artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
uses: actions/download-artifact@v7
|
||||
with:
|
||||
pattern: compose-*
|
||||
path: ./bin/release
|
||||
merge-multiple: true
|
||||
-
|
||||
name: Create checksums
|
||||
working-directory: ./bin/release
|
||||
run: |
|
||||
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
|
||||
name: release
|
||||
-
|
||||
name: List artifacts
|
||||
run: |
|
||||
|
||||
86
.github/workflows/merge.yml
vendored
86
.github/workflows/merge.yml
vendored
@@ -74,63 +74,41 @@ jobs:
|
||||
run: |
|
||||
make e2e-compose-standalone
|
||||
|
||||
bin-image:
|
||||
runs-on: ubuntu-22.04
|
||||
bin-image-prepare:
|
||||
runs-on: ubuntu-24.04
|
||||
outputs:
|
||||
digest: ${{ fromJSON(steps.bake.outputs.metadata).image-cross['containerimage.digest'] }}
|
||||
repo-slug: ${{ env.REPO_SLUG }}
|
||||
steps:
|
||||
-
|
||||
name: Free disk space
|
||||
uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be # v1.3.1
|
||||
with:
|
||||
android: true
|
||||
dotnet: true
|
||||
haskell: true
|
||||
large-packages: true
|
||||
swap-storage: true
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
-
|
||||
name: Login to DockerHub
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
# FIXME: can't use env object in reusable workflow inputs: https://github.com/orgs/community/discussions/26671
|
||||
- run: echo "Exposing env vars for reusable workflow"
|
||||
|
||||
bin-image:
|
||||
uses: docker/github-builder/.github/workflows/bake.yml@v1
|
||||
needs:
|
||||
- bin-image-prepare
|
||||
permissions:
|
||||
contents: read # same as global permission
|
||||
id-token: write # for signing attestation(s) with GitHub OIDC Token
|
||||
with:
|
||||
runner: amd64
|
||||
target: image-cross
|
||||
cache: true
|
||||
cache-scope: bin-image
|
||||
output: image
|
||||
push: ${{ github.event_name != 'pull_request' }}
|
||||
sbom: true
|
||||
set-meta-labels: true
|
||||
meta-images: |
|
||||
${{ needs.bin-image-prepare.outputs.repo-slug }}
|
||||
meta-tags: |
|
||||
type=ref,event=tag
|
||||
type=edge
|
||||
meta-bake-target: meta-helper
|
||||
secrets:
|
||||
registry-auths: |
|
||||
- registry: docker.io
|
||||
username: ${{ secrets.DOCKERPUBLICBOT_USERNAME }}
|
||||
password: ${{ secrets.DOCKERPUBLICBOT_WRITE_PAT }}
|
||||
-
|
||||
name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
-
|
||||
name: Docker meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: |
|
||||
${{ env.REPO_SLUG }}
|
||||
tags: |
|
||||
type=ref,event=tag
|
||||
type=edge
|
||||
bake-target: meta-helper
|
||||
-
|
||||
name: Build and push image
|
||||
uses: docker/bake-action@v6
|
||||
id: bake
|
||||
with:
|
||||
source: .
|
||||
files: |
|
||||
./docker-bake.hcl
|
||||
${{ steps.meta.outputs.bake-file }}
|
||||
targets: image-cross
|
||||
push: ${{ github.event_name != 'pull_request' }}
|
||||
sbom: true
|
||||
provenance: mode=max
|
||||
set: |
|
||||
*.cache-from=type=gha,scope=bin-image
|
||||
*.cache-to=type=gha,scope=bin-image,mode=max
|
||||
|
||||
desktop-edge-test:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -158,6 +136,6 @@ jobs:
|
||||
workflow_id: 'compose-edge-integration.yml',
|
||||
ref: 'main',
|
||||
inputs: {
|
||||
"image-tag": "${{ needs.bin-image.outputs.digest }}"
|
||||
"image-tag": "${{ env.REPO_SLUG }}:edge"
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1 +1 @@
|
||||
1.24.11
|
||||
1.25.7
|
||||
@@ -15,9 +15,9 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
ARG GO_VERSION=1.24.11
|
||||
ARG GO_VERSION=1.25.7
|
||||
ARG XX_VERSION=1.9.0
|
||||
ARG GOLANGCI_LINT_VERSION=v2.6.2
|
||||
ARG GOLANGCI_LINT_VERSION=v2.8.0
|
||||
ARG ADDLICENSE_VERSION=v1.0.0
|
||||
|
||||
ARG BUILD_TAGS="e2e"
|
||||
|
||||
6
Makefile
6
Makefile
@@ -29,10 +29,6 @@ ifeq ($(DETECTED_OS),Windows)
|
||||
BINARY_EXT=.exe
|
||||
endif
|
||||
|
||||
ifeq ($(DETECTED_OS),Darwin)
|
||||
GO_BUILDTAGS += fsnotify
|
||||
endif
|
||||
|
||||
BUILD_FLAGS?=
|
||||
TEST_FLAGS?=
|
||||
E2E_TEST?=
|
||||
@@ -99,7 +95,7 @@ example-provider: ## build example provider for e2e tests
|
||||
mocks:
|
||||
mockgen --version >/dev/null 2>&1 || go install go.uber.org/mock/mockgen@v0.4.0
|
||||
mockgen -destination pkg/mocks/mock_docker_cli.go -package mocks github.com/docker/cli/cli/command Cli
|
||||
mockgen -destination pkg/mocks/mock_docker_api.go -package mocks github.com/docker/docker/client APIClient
|
||||
mockgen -destination pkg/mocks/mock_docker_api.go -package mocks github.com/moby/moby/client APIClient
|
||||
mockgen -destination pkg/mocks/mock_docker_compose_api.go -package mocks -source=./pkg/api/api.go Service
|
||||
|
||||
.PHONY: e2e
|
||||
|
||||
@@ -19,6 +19,7 @@ package compatibility
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/compose/v5/cmd/compose"
|
||||
@@ -59,7 +60,7 @@ func Convert(args []string) []string {
|
||||
ARGS:
|
||||
for i := 0; i < l; i++ {
|
||||
arg := args[i]
|
||||
if contains(getCompletionCommands(), arg) {
|
||||
if slices.Contains(getCompletionCommands(), arg) {
|
||||
command = append([]string{arg}, command...)
|
||||
continue
|
||||
}
|
||||
@@ -79,7 +80,7 @@ ARGS:
|
||||
arg = "version"
|
||||
}
|
||||
|
||||
if contains(getBoolFlags(), arg) {
|
||||
if slices.Contains(getBoolFlags(), arg) {
|
||||
rootFlags = append(rootFlags, arg)
|
||||
continue
|
||||
}
|
||||
@@ -105,12 +106,3 @@ ARGS:
|
||||
}
|
||||
return append(rootFlags, command...)
|
||||
}
|
||||
|
||||
func contains(array []string, needle string) bool {
|
||||
for _, val := range array {
|
||||
if val == needle {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -23,9 +23,9 @@ import (
|
||||
|
||||
"github.com/distribution/reference"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/docker/api/types/image"
|
||||
"github.com/docker/docker/pkg/stringid"
|
||||
"github.com/docker/go-units"
|
||||
"github.com/moby/moby/api/types/image"
|
||||
"github.com/moby/moby/client/pkg/stringid"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/docker/compose/v5/cmd/formatter"
|
||||
|
||||
@@ -35,7 +35,6 @@ import (
|
||||
composepaths "github.com/compose-spec/compose-go/v2/paths"
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
composegoutils "github.com/compose-spec/compose-go/v2/utils"
|
||||
"github.com/docker/buildx/util/logutil"
|
||||
dockercli "github.com/docker/cli/cli"
|
||||
"github.com/docker/cli/cli-plugins/metadata"
|
||||
"github.com/docker/cli/cli/command"
|
||||
@@ -423,16 +422,6 @@ func (o *BackendOptions) Add(option compose.Option) {
|
||||
|
||||
// RootCommand returns the compose command with its child commands
|
||||
func RootCommand(dockerCli command.Cli, backendOptions *BackendOptions) *cobra.Command { //nolint:gocyclo
|
||||
// filter out useless commandConn.CloseWrite warning message that can occur
|
||||
// when using a remote context that is unreachable: "commandConn.CloseWrite: commandconn: failed to wait: signal: killed"
|
||||
// https://github.com/docker/cli/blob/e1f24d3c93df6752d3c27c8d61d18260f141310c/cli/connhelper/commandconn/commandconn.go#L203-L215
|
||||
logrus.AddHook(logutil.NewFilter([]logrus.Level{
|
||||
logrus.WarnLevel,
|
||||
},
|
||||
"commandConn.CloseWrite:",
|
||||
"commandConn.CloseRead:",
|
||||
))
|
||||
|
||||
opts := ProjectOptions{}
|
||||
var (
|
||||
ansi string
|
||||
@@ -477,7 +466,7 @@ func RootCommand(dockerCli command.Cli, backendOptions *BackendOptions) *cobra.C
|
||||
logrus.SetLevel(logrus.TraceLevel)
|
||||
}
|
||||
|
||||
err := setEnvWithDotEnv(opts)
|
||||
err := setEnvWithDotEnv(opts, dockerCli)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -505,6 +494,7 @@ func RootCommand(dockerCli command.Cli, backendOptions *BackendOptions) *cobra.C
|
||||
display.Mode = display.ModeTTY
|
||||
}
|
||||
|
||||
detached, _ := cmd.Flags().GetBool("detach")
|
||||
var ep api.EventProcessor
|
||||
switch opts.Progress {
|
||||
case "", display.ModeAuto:
|
||||
@@ -513,7 +503,7 @@ func RootCommand(dockerCli command.Cli, backendOptions *BackendOptions) *cobra.C
|
||||
display.Mode = display.ModePlain
|
||||
ep = display.Plain(dockerCli.Err())
|
||||
case dockerCli.Out().IsTerminal():
|
||||
ep = display.Full(dockerCli.Err(), stdinfo(dockerCli))
|
||||
ep = display.Full(dockerCli.Err(), stdinfo(dockerCli), detached)
|
||||
default:
|
||||
ep = display.Plain(dockerCli.Err())
|
||||
}
|
||||
@@ -522,7 +512,7 @@ func RootCommand(dockerCli command.Cli, backendOptions *BackendOptions) *cobra.C
|
||||
return fmt.Errorf("can't use --progress tty while ANSI support is disabled")
|
||||
}
|
||||
display.Mode = display.ModeTTY
|
||||
ep = display.Full(dockerCli.Err(), stdinfo(dockerCli))
|
||||
ep = display.Full(dockerCli.Err(), stdinfo(dockerCli), detached)
|
||||
|
||||
case display.ModePlain:
|
||||
if ansi == "always" {
|
||||
@@ -676,7 +666,21 @@ func stdinfo(dockerCli command.Cli) io.Writer {
|
||||
return dockerCli.Err()
|
||||
}
|
||||
|
||||
func setEnvWithDotEnv(opts ProjectOptions) error {
|
||||
func setEnvWithDotEnv(opts ProjectOptions, dockerCli command.Cli) error {
|
||||
// Check if we're using a remote config (OCI or Git)
|
||||
// If so, skip env loading as remote loaders haven't been initialized yet
|
||||
// and trying to process the path would fail
|
||||
remoteLoaders := opts.remoteLoaders(dockerCli)
|
||||
for _, path := range opts.ConfigPaths {
|
||||
for _, loader := range remoteLoaders {
|
||||
if loader.Accept(path) {
|
||||
// Remote config - skip env loading for now
|
||||
// It will be loaded later when the project is fully initialized
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
options, err := cli.NewProjectOptions(opts.ConfigPaths,
|
||||
cli.WithWorkingDirectory(opts.ProjectDir),
|
||||
cli.WithOsEnv,
|
||||
|
||||
76
cmd/compose/compose_oci_test.go
Normal file
76
cmd/compose/compose_oci_test.go
Normal file
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
Copyright 2020 Docker Compose CLI authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package compose
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"go.uber.org/mock/gomock"
|
||||
"gotest.tools/v3/assert"
|
||||
|
||||
"github.com/docker/compose/v5/pkg/mocks"
|
||||
)
|
||||
|
||||
func TestSetEnvWithDotEnv_WithOCIArtifact(t *testing.T) {
|
||||
// Test that setEnvWithDotEnv doesn't fail when using OCI artifacts
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
cli := mocks.NewMockCli(ctrl)
|
||||
|
||||
opts := ProjectOptions{
|
||||
ConfigPaths: []string{"oci://docker.io/dockersamples/welcome-to-docker"},
|
||||
ProjectDir: "",
|
||||
EnvFiles: []string{},
|
||||
}
|
||||
|
||||
err := setEnvWithDotEnv(opts, cli)
|
||||
assert.NilError(t, err, "setEnvWithDotEnv should not fail with OCI artifact path")
|
||||
}
|
||||
|
||||
func TestSetEnvWithDotEnv_WithGitRemote(t *testing.T) {
|
||||
// Test that setEnvWithDotEnv doesn't fail when using Git remotes
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
cli := mocks.NewMockCli(ctrl)
|
||||
|
||||
opts := ProjectOptions{
|
||||
ConfigPaths: []string{"https://github.com/docker/compose.git"},
|
||||
ProjectDir: "",
|
||||
EnvFiles: []string{},
|
||||
}
|
||||
|
||||
err := setEnvWithDotEnv(opts, cli)
|
||||
assert.NilError(t, err, "setEnvWithDotEnv should not fail with Git remote path")
|
||||
}
|
||||
|
||||
func TestSetEnvWithDotEnv_WithLocalPath(t *testing.T) {
|
||||
// Test that setEnvWithDotEnv still works with local paths
|
||||
// This will fail if the file doesn't exist, but it should not panic
|
||||
// or produce invalid paths
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
cli := mocks.NewMockCli(ctrl)
|
||||
|
||||
opts := ProjectOptions{
|
||||
ConfigPaths: []string{"compose.yaml"},
|
||||
ProjectDir: "",
|
||||
EnvFiles: []string{},
|
||||
}
|
||||
|
||||
// This may error if files don't exist, but should not panic
|
||||
_ = setEnvWithDotEnv(opts, cli)
|
||||
}
|
||||
@@ -23,6 +23,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"slices"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
@@ -454,9 +455,7 @@ func runHash(ctx context.Context, dockerCli command.Cli, opts configOptions) err
|
||||
}
|
||||
|
||||
sorted := services
|
||||
sort.Slice(sorted, func(i, j int) bool {
|
||||
return sorted[i] < sorted[j]
|
||||
})
|
||||
slices.Sort(sorted)
|
||||
|
||||
for _, name := range sorted {
|
||||
s, err := project.GetService(name)
|
||||
|
||||
@@ -198,12 +198,11 @@ func (opts createOptions) Apply(project *types.Project) error {
|
||||
|
||||
func applyScaleOpts(project *types.Project, opts []string) error {
|
||||
for _, scale := range opts {
|
||||
split := strings.Split(scale, "=")
|
||||
if len(split) != 2 {
|
||||
name, val, ok := strings.Cut(scale, "=")
|
||||
if !ok || val == "" {
|
||||
return fmt.Errorf("invalid --scale option %q. Should be SERVICE=NUM", scale)
|
||||
}
|
||||
name := split[0]
|
||||
replicas, err := strconv.Atoi(split[1])
|
||||
replicas, err := strconv.Atoi(val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -27,8 +27,8 @@ import (
|
||||
|
||||
"github.com/containerd/platforms"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/docker/pkg/stringid"
|
||||
"github.com/docker/go-units"
|
||||
"github.com/moby/moby/client/pkg/stringid"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/docker/compose/v5/cmd/formatter"
|
||||
|
||||
@@ -18,12 +18,15 @@ package compose
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/cli/opts"
|
||||
"github.com/moby/moby/client"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/docker/compose/v5/cmd/formatter"
|
||||
@@ -61,11 +64,32 @@ var acceptedListFilters = map[string]bool{
|
||||
"name": true,
|
||||
}
|
||||
|
||||
// match returns true if any of the values at key match the source string
|
||||
func match(filters client.Filters, field, source string) bool {
|
||||
if f, ok := filters[field]; ok && f[source] {
|
||||
return true
|
||||
}
|
||||
|
||||
fieldValues := filters[field]
|
||||
for name2match := range fieldValues {
|
||||
isMatch, err := regexp.MatchString(name2match, source)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if isMatch {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func runList(ctx context.Context, dockerCli command.Cli, backendOptions *BackendOptions, lsOpts lsOptions) error {
|
||||
filters := lsOpts.Filter.Value()
|
||||
err := filters.Validate(acceptedListFilters)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
for filter := range filters {
|
||||
if _, ok := acceptedListFilters[filter]; !ok {
|
||||
return errors.New("invalid filter '" + filter + "'")
|
||||
}
|
||||
}
|
||||
|
||||
backend, err := compose.NewComposeService(dockerCli, backendOptions.Options...)
|
||||
@@ -77,13 +101,12 @@ func runList(ctx context.Context, dockerCli command.Cli, backendOptions *Backend
|
||||
return err
|
||||
}
|
||||
|
||||
if filters.Len() > 0 {
|
||||
if len(filters) > 0 {
|
||||
var filtered []api.Stack
|
||||
for _, s := range stackList {
|
||||
if filters.Contains("name") && !filters.Match("name", s.Name) {
|
||||
continue
|
||||
if match(filters, "name", s.Name) {
|
||||
filtered = append(filtered, s)
|
||||
}
|
||||
filtered = append(filtered, s)
|
||||
}
|
||||
stackList = filtered
|
||||
}
|
||||
|
||||
@@ -213,9 +213,9 @@ 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]
|
||||
key, val, ok := strings.Cut(env, "=")
|
||||
if ok {
|
||||
cmdEnvMap[key] = val
|
||||
}
|
||||
}
|
||||
return cmdEnvMap
|
||||
|
||||
@@ -50,19 +50,19 @@ func (p *psOptions) parseFilter() error {
|
||||
if p.Filter == "" {
|
||||
return nil
|
||||
}
|
||||
parts := strings.SplitN(p.Filter, "=", 2)
|
||||
if len(parts) != 2 {
|
||||
key, val, ok := strings.Cut(p.Filter, "=")
|
||||
if !ok {
|
||||
return errors.New("arguments to --filter should be in form KEY=VAL")
|
||||
}
|
||||
switch parts[0] {
|
||||
switch key {
|
||||
case "status":
|
||||
p.Status = append(p.Status, parts[1])
|
||||
p.Status = append(p.Status, val)
|
||||
return nil
|
||||
case "source":
|
||||
return api.ErrNotImplemented
|
||||
default:
|
||||
return fmt.Errorf("unknown filter %s", parts[0])
|
||||
return fmt.Errorf("unknown filter %s", key)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func psCommand(p *ProjectOptions, dockerCli command.Cli, backendOptions *BackendOptions) *cobra.Command {
|
||||
@@ -167,18 +167,9 @@ func runPs(ctx context.Context, dockerCli command.Cli, backendOptions *BackendOp
|
||||
func filterByStatus(containers []api.ContainerSummary, statuses []string) []api.ContainerSummary {
|
||||
var filtered []api.ContainerSummary
|
||||
for _, c := range containers {
|
||||
if hasStatus(c, statuses) {
|
||||
if slices.Contains(statuses, string(c.State)) {
|
||||
filtered = append(filtered, c)
|
||||
}
|
||||
}
|
||||
return filtered
|
||||
}
|
||||
|
||||
func hasStatus(c api.ContainerSummary, statuses []string) bool {
|
||||
for _, status := range statuses {
|
||||
if c.State == status {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -284,11 +284,11 @@ func runRun(ctx context.Context, backend api.Compose, project *types.Project, op
|
||||
|
||||
labels := types.Labels{}
|
||||
for _, s := range options.labels {
|
||||
parts := strings.SplitN(s, "=", 2)
|
||||
if len(parts) != 2 {
|
||||
key, val, ok := strings.Cut(s, "=")
|
||||
if !ok {
|
||||
return fmt.Errorf("label must be set as KEY=VALUE")
|
||||
}
|
||||
labels[parts[0]] = parts[1]
|
||||
labels[key] = val
|
||||
}
|
||||
|
||||
var buildForRun *api.BuildOptions
|
||||
|
||||
@@ -22,7 +22,7 @@ import (
|
||||
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/cli/cli/command/container"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
"github.com/moby/moby/client"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/docker/compose/v5/pkg/api"
|
||||
@@ -67,18 +67,17 @@ func runStats(ctx context.Context, dockerCli command.Cli, opts statsOptions, ser
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
filter := []filters.KeyValuePair{
|
||||
filters.Arg("label", fmt.Sprintf("%s=%s", api.ProjectLabel, name)),
|
||||
}
|
||||
f := client.Filters{}
|
||||
f.Add("label", fmt.Sprintf("%s=%s", api.ProjectLabel, name))
|
||||
|
||||
if len(service) > 0 {
|
||||
filter = append(filter, filters.Arg("label", fmt.Sprintf("%s=%s", api.ServiceLabel, service[0])))
|
||||
f.Add("label", fmt.Sprintf("%s=%s", api.ServiceLabel, service[0]))
|
||||
}
|
||||
args := filters.NewArgs(filter...)
|
||||
return container.RunStats(ctx, dockerCli, &container.StatsOptions{
|
||||
All: opts.all,
|
||||
NoStream: opts.noStream,
|
||||
NoTrunc: opts.noTrunc,
|
||||
Format: opts.format,
|
||||
Filters: &args,
|
||||
Filters: f,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -37,13 +37,14 @@ import (
|
||||
|
||||
// Full creates an EventProcessor that render advanced UI within a terminal.
|
||||
// On Start, TUI lists task with a progress timer
|
||||
func Full(out io.Writer, info io.Writer) api.EventProcessor {
|
||||
func Full(out io.Writer, info io.Writer, detached bool) api.EventProcessor {
|
||||
return &ttyWriter{
|
||||
out: out,
|
||||
info: info,
|
||||
tasks: map[string]*task{},
|
||||
done: make(chan bool),
|
||||
mtx: &sync.Mutex{},
|
||||
out: out,
|
||||
info: info,
|
||||
tasks: map[string]*task{},
|
||||
done: make(chan bool),
|
||||
mtx: &sync.Mutex{},
|
||||
detached: detached,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,6 +61,7 @@ type ttyWriter struct {
|
||||
ticker *time.Ticker
|
||||
suspended bool
|
||||
info io.Writer
|
||||
detached bool
|
||||
}
|
||||
|
||||
type task struct {
|
||||
@@ -190,7 +192,7 @@ func (w *ttyWriter) On(events ...api.Resource) {
|
||||
continue
|
||||
}
|
||||
|
||||
if w.operation != "start" && (e.Text == api.StatusStarted || e.Text == api.StatusStarting) {
|
||||
if w.operation != "start" && (e.Text == api.StatusStarted || e.Text == api.StatusStarting) && !w.detached {
|
||||
// skip those events to avoid mix with container logs
|
||||
continue
|
||||
}
|
||||
@@ -315,10 +317,7 @@ func (w *ttyWriter) printWithDimensions(terminalWidth, terminalHeight int) {
|
||||
allTasks := slices.Collect(w.parentTasks())
|
||||
|
||||
// Available lines: terminal height - 2 (header line + potential "more" line)
|
||||
maxLines := terminalHeight - 2
|
||||
if maxLines < 1 {
|
||||
maxLines = 1
|
||||
}
|
||||
maxLines := max(terminalHeight-2, 1)
|
||||
|
||||
showMore := len(allTasks) > maxLines
|
||||
tasksToShow := allTasks
|
||||
@@ -352,10 +351,7 @@ func (w *ttyWriter) printWithDimensions(terminalWidth, terminalHeight int) {
|
||||
if showMore {
|
||||
moreCount := len(allTasks) - len(tasksToShow)
|
||||
moreText := fmt.Sprintf(" ... %d more", moreCount)
|
||||
pad := terminalWidth - len(moreText)
|
||||
if pad < 0 {
|
||||
pad = 0
|
||||
}
|
||||
pad := max(terminalWidth-len(moreText), 0)
|
||||
_, _ = fmt.Fprintf(w.out, "%s%s\n", moreText, strings.Repeat(" ", pad))
|
||||
numLines++
|
||||
}
|
||||
@@ -390,10 +386,7 @@ func (w *ttyWriter) applyPadding(lines []lineData, terminalWidth int, timerLen i
|
||||
if l.details != "" {
|
||||
lineLen += 1 + utf8.RuneCountInString(l.details)
|
||||
}
|
||||
l.timerPad = terminalWidth - lineLen - timerLen
|
||||
if l.timerPad < 1 {
|
||||
l.timerPad = 1
|
||||
}
|
||||
l.timerPad = max(terminalWidth-lineLen-timerLen, 1)
|
||||
lines[i] = l
|
||||
|
||||
}
|
||||
@@ -470,10 +463,7 @@ func truncateDetails(lines []lineData, overflow int) bool {
|
||||
for i := range lines {
|
||||
l := &lines[i]
|
||||
if len(l.details) > 3 {
|
||||
reduction := overflow
|
||||
if reduction > len(l.details)-3 {
|
||||
reduction = len(l.details) - 3
|
||||
}
|
||||
reduction := min(overflow, len(l.details)-3)
|
||||
l.details = l.details[:len(l.details)-reduction-3] + "..."
|
||||
return true
|
||||
} else if l.details != "" {
|
||||
@@ -502,10 +492,7 @@ func truncateLongestTaskID(lines []lineData, overflow, minIDLen int) bool {
|
||||
|
||||
l := &lines[longestIdx]
|
||||
reduction := overflow + 3 // account for "..."
|
||||
newLen := len(l.taskID) - reduction
|
||||
if newLen < minIDLen-3 {
|
||||
newLen = minIDLen - 3
|
||||
}
|
||||
newLen := max(len(l.taskID)-reduction, minIDLen-3)
|
||||
if newLen > 0 {
|
||||
l.taskID = l.taskID[:newLen] + "..."
|
||||
}
|
||||
@@ -544,10 +531,7 @@ func (w *ttyWriter) prepareLineData(t *task) lineData {
|
||||
total += child.total
|
||||
current += child.current
|
||||
r := len(percentChars) - 1
|
||||
p := child.percent
|
||||
if p > 100 {
|
||||
p = 100
|
||||
}
|
||||
p := min(child.percent, 100)
|
||||
completion = append(completion, percentChars[r*p/100])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -186,7 +186,7 @@ func TestPrintWithDimensions_TaskWithProgress(t *testing.T) {
|
||||
w.ids = append(w.ids, "Image nginx")
|
||||
|
||||
// Create child tasks to trigger progress display
|
||||
for i := 0; i < 3; i++ {
|
||||
for i := range 3 {
|
||||
child := &task{
|
||||
ID: "layer" + string(rune('a'+i)),
|
||||
parents: map[string]struct{}{"Image nginx": {}},
|
||||
|
||||
@@ -18,14 +18,15 @@ package formatter
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/netip"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/docker/cli/cli/command/formatter"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/pkg/stringid"
|
||||
"github.com/docker/go-units"
|
||||
"github.com/moby/moby/api/types/container"
|
||||
"github.com/moby/moby/client/pkg/stringid"
|
||||
|
||||
"github.com/docker/compose/v5/pkg/api"
|
||||
)
|
||||
@@ -197,7 +198,7 @@ func (c *ContainerContext) ExitCode() int {
|
||||
}
|
||||
|
||||
func (c *ContainerContext) State() string {
|
||||
return c.c.State
|
||||
return string(c.c.State)
|
||||
}
|
||||
|
||||
func (c *ContainerContext) Status() string {
|
||||
@@ -205,7 +206,7 @@ func (c *ContainerContext) Status() string {
|
||||
}
|
||||
|
||||
func (c *ContainerContext) Health() string {
|
||||
return c.c.Health
|
||||
return string(c.c.Health)
|
||||
}
|
||||
|
||||
func (c *ContainerContext) Publishers() api.PortPublishers {
|
||||
@@ -213,10 +214,16 @@ func (c *ContainerContext) Publishers() api.PortPublishers {
|
||||
}
|
||||
|
||||
func (c *ContainerContext) Ports() string {
|
||||
var ports []container.Port
|
||||
var ports []container.PortSummary
|
||||
for _, publisher := range c.c.Publishers {
|
||||
ports = append(ports, container.Port{
|
||||
IP: publisher.URL,
|
||||
var pIP netip.Addr
|
||||
if publisher.URL != "" {
|
||||
if p, err := netip.ParseAddr(publisher.URL); err == nil {
|
||||
pIP = p
|
||||
}
|
||||
}
|
||||
ports = append(ports, container.PortSummary{
|
||||
IP: pIP,
|
||||
PrivatePort: uint16(publisher.TargetPort),
|
||||
PublicPort: uint16(publisher.PublishedPort),
|
||||
Type: publisher.Protocol,
|
||||
|
||||
@@ -26,7 +26,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/buger/goterm"
|
||||
"github.com/docker/docker/pkg/jsonmessage"
|
||||
"github.com/moby/moby/client/pkg/jsonmessage"
|
||||
|
||||
"github.com/docker/compose/v5/pkg/api"
|
||||
)
|
||||
|
||||
@@ -189,7 +189,7 @@ func (lk *LogKeyboard) clearNavigationMenu() {
|
||||
saveCursor()
|
||||
|
||||
// clearLine()
|
||||
for i := 0; i < height; i++ {
|
||||
for range height {
|
||||
moveCursorDown(1)
|
||||
clearLine()
|
||||
}
|
||||
@@ -341,7 +341,7 @@ func (lk *LogKeyboard) EnableDetach(detach func()) {
|
||||
}
|
||||
|
||||
func allocateSpace(lines int) {
|
||||
for i := 0; i < lines; i++ {
|
||||
for range lines {
|
||||
clearLine()
|
||||
newLine()
|
||||
carriageReturn()
|
||||
|
||||
67
go.mod
67
go.mod
@@ -1,6 +1,6 @@
|
||||
module github.com/docker/compose/v5
|
||||
|
||||
go 1.24.3
|
||||
go 1.25.0
|
||||
|
||||
require (
|
||||
github.com/AlecAivazis/survey/v2 v2.3.7
|
||||
@@ -14,11 +14,10 @@ require (
|
||||
github.com/containerd/errdefs v1.0.0
|
||||
github.com/containerd/platforms v1.0.0-rc.2
|
||||
github.com/distribution/reference v0.6.0
|
||||
github.com/docker/buildx v0.30.1
|
||||
github.com/docker/cli v28.5.2+incompatible
|
||||
github.com/docker/buildx v0.31.1
|
||||
github.com/docker/cli v29.2.1+incompatible
|
||||
github.com/docker/cli-docs-tool v0.11.0
|
||||
github.com/docker/docker v28.5.2+incompatible
|
||||
github.com/docker/go-connections v0.6.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
|
||||
@@ -29,8 +28,10 @@ require (
|
||||
github.com/jonboulle/clockwork v0.5.0
|
||||
github.com/mattn/go-shellwords v1.0.12
|
||||
github.com/mitchellh/go-ps v1.0.0
|
||||
github.com/moby/buildkit v0.26.3
|
||||
github.com/moby/go-archive v0.1.0
|
||||
github.com/moby/buildkit v0.27.1
|
||||
github.com/moby/go-archive v0.2.0
|
||||
github.com/moby/moby/api v1.53.0
|
||||
github.com/moby/moby/client v0.2.2
|
||||
github.com/moby/patternmatcher v0.6.0
|
||||
github.com/moby/sys/atomicwriter v0.1.0
|
||||
github.com/morikuni/aec v1.1.0
|
||||
@@ -52,9 +53,9 @@ require (
|
||||
go.opentelemetry.io/otel/trace v1.38.0
|
||||
go.uber.org/goleak v1.3.0
|
||||
go.uber.org/mock v0.6.0
|
||||
go.yaml.in/yaml/v4 v4.0.0-rc.3
|
||||
go.yaml.in/yaml/v4 v4.0.0-rc.4
|
||||
golang.org/x/sync v0.19.0
|
||||
golang.org/x/sys v0.40.0
|
||||
golang.org/x/sys v0.41.0
|
||||
google.golang.org/grpc v1.78.0
|
||||
gotest.tools/v3 v3.5.2
|
||||
tags.cncf.io/container-device-interface v1.1.0
|
||||
@@ -62,9 +63,7 @@ require (
|
||||
|
||||
require (
|
||||
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/cenkalti/backoff/v5 v5.0.3 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/containerd/containerd/api v1.10.0 // indirect
|
||||
github.com/containerd/continuity v0.4.5 // indirect
|
||||
github.com/containerd/errdefs/pkg v0.3.0 // indirect
|
||||
@@ -74,9 +73,8 @@ require (
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/docker/distribution v2.8.3+incompatible // indirect
|
||||
github.com/docker/docker-credential-helpers v0.9.3 // indirect
|
||||
github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c // indirect
|
||||
github.com/docker/go-metrics v0.0.1 // indirect
|
||||
github.com/docker/docker-credential-helpers v0.9.5 // indirect
|
||||
github.com/docker/go-connections v0.6.0 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/fvbommel/sortorder v1.1.0 // indirect
|
||||
github.com/go-logr/logr v1.4.3 // indirect
|
||||
@@ -87,22 +85,19 @@ require (
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
|
||||
github.com/gorilla/mux v1.8.1 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3 // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||
github.com/in-toto/in-toto-golang v0.9.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/inhies/go-bytesize v0.0.0-20220417184213-4913239db9cf // indirect
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
|
||||
github.com/klauspost/compress v1.18.2 // indirect
|
||||
github.com/magiconair/properties v1.8.9 // indirect
|
||||
github.com/klauspost/compress v1.18.3 // indirect
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.16 // 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/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/moby/docker-image-spec v1.3.1 // indirect
|
||||
github.com/moby/locker v1.0.1 // indirect
|
||||
github.com/moby/sys/capability v0.4.0 // indirect
|
||||
@@ -112,24 +107,20 @@ require (
|
||||
github.com/moby/sys/user v0.4.0 // indirect
|
||||
github.com/moby/sys/userns v0.1.0 // indirect
|
||||
github.com/moby/term v0.5.2 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // 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.0 // indirect
|
||||
github.com/prometheus/client_golang v1.23.2 // indirect
|
||||
github.com/prometheus/client_model v0.6.2 // indirect
|
||||
github.com/prometheus/common v0.66.1 // indirect
|
||||
github.com/prometheus/procfs v0.16.1 // indirect
|
||||
github.com/rivo/uniseg v0.2.0 // indirect
|
||||
github.com/rivo/uniseg v0.4.7 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/santhosh-tekuri/jsonschema/v6 v6.0.1 // indirect
|
||||
github.com/secure-systems-lab/go-securesystemslib v0.9.1 // indirect
|
||||
github.com/shibumi/go-pathspec v1.3.0 // indirect
|
||||
github.com/theupdateframework/notary v0.7.0 // indirect
|
||||
github.com/sigstore/sigstore v1.10.0 // indirect
|
||||
github.com/sigstore/sigstore-go v1.1.4-0.20251124094504-b5fe07a5a7d7 // indirect
|
||||
github.com/tonistiigi/dchapes-mode v0.0.0-20250318174251-73d941a28323 // indirect
|
||||
github.com/tonistiigi/fsutil v0.0.0-20250605211040-586307ad452f // indirect
|
||||
github.com/tonistiigi/fsutil v0.0.0-20251211185533-a2aa163d723f // indirect
|
||||
github.com/tonistiigi/go-csvvalue v0.0.0-20240814133006-030d3b2625d0 // indirect
|
||||
github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea // indirect
|
||||
github.com/tonistiigi/vt100 v0.0.0-20240514184818-90bafcd6abab // indirect
|
||||
@@ -142,16 +133,24 @@ require (
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk/metric v1.38.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.7.1 // indirect
|
||||
go.yaml.in/yaml/v2 v2.4.2 // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||
golang.org/x/crypto v0.45.0 // indirect
|
||||
golang.org/x/net v0.47.0 // indirect
|
||||
golang.org/x/term v0.37.0 // indirect
|
||||
golang.org/x/text v0.31.0 // indirect
|
||||
golang.org/x/crypto v0.46.0 // indirect
|
||||
golang.org/x/net v0.48.0 // indirect
|
||||
golang.org/x/term v0.38.0 // indirect
|
||||
golang.org/x/text v0.32.0 // indirect
|
||||
golang.org/x/time v0.14.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20251029180050-ab9386a59fda // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251029180050-ab9386a59fda // indirect
|
||||
google.golang.org/protobuf v1.36.10 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20251103181224-f26f9409b101 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251103181224-f26f9409b101 // indirect
|
||||
google.golang.org/protobuf v1.36.11 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
exclude (
|
||||
// FIXME(thaJeztah): remove this once kubernetes updated their dependencies to no longer need this.
|
||||
//
|
||||
// For additional details, see this PR and links mentioned in that PR:
|
||||
// https://github.com/kubernetes-sigs/kustomize/pull/5830#issuecomment-2569960859
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2
|
||||
)
|
||||
|
||||
352
go.sum
352
go.sum
@@ -1,11 +1,11 @@
|
||||
cyphar.com/go-pathrs v0.2.1 h1:9nx1vOgwVvX1mNBWDu93+vaceedpbsDqo+XuBGL40b8=
|
||||
cyphar.com/go-pathrs v0.2.1/go.mod h1:y8f1EMG7r+hCuFf/rXsKqMJrJAUoADZGNh5/vZPKcGc=
|
||||
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/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/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/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
|
||||
@@ -14,36 +14,22 @@ github.com/Microsoft/hcsshim v0.14.0-rc.1 h1:qAPXKwGOkVn8LlqgBN8GS0bxZ83hOJpcjxz
|
||||
github.com/Microsoft/hcsshim v0.14.0-rc.1/go.mod h1:hTKFGbnDtQb1wHiOWv4v0eN+7boSWAHyK/tNAaYZL0c=
|
||||
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 h1:hi6J4K6DKrR4/ljxn6SF6nURyu785wKMuQcjt7H3VCQ=
|
||||
github.com/Shopify/logrus-bugsnag v0.0.0-20170309145241-6dbc35f2c30d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ=
|
||||
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8=
|
||||
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
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/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=
|
||||
github.com/anchore/go-struct-converter v0.1.0 h1:2rDRssAl6mgKBSLNiVCMADgZRhoqtw9dedlWa0OhD30=
|
||||
github.com/anchore/go-struct-converter v0.1.0/go.mod h1:rYqSE9HbjzpHTI74vwPvae4ZVYZd1lue2ta6xHPdblA=
|
||||
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so=
|
||||
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/bitly/go-hostpool v0.1.0/go.mod h1:4gOCgp6+NZnVqlKyZ/iBZFTAJKembaVENUpMkpg42fw=
|
||||
github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA=
|
||||
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
|
||||
github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ=
|
||||
github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
|
||||
github.com/buger/goterm v1.0.4 h1:Z9YvGmOih81P0FbVtEYTFF6YsSgxSUKEhf/f9bTMXbY=
|
||||
github.com/buger/goterm v1.0.4/go.mod h1:HiFWV3xnkolgrBV3mY8m0X0Pumt4zg4QhbdOzQtB8tE=
|
||||
github.com/bugsnag/bugsnag-go v1.0.5-0.20150529004307-13fd6b8acda0 h1:s7+5BfS4WFJoVF9pnB8kBk03S7pZXRdKamnV0FOl5Sc=
|
||||
github.com/bugsnag/bugsnag-go v1.0.5-0.20150529004307-13fd6b8acda0/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8=
|
||||
github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b h1:otBG+dV+YK+Soembjv71DPz3uX/V/6MMlSyD9JBQ6kQ=
|
||||
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/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM=
|
||||
github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw=
|
||||
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/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb h1:EDmT6Q9Zs+SbUoc7Ik9EfrFqcylYqgPZ9ANSbTAntnE=
|
||||
github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb/go.mod h1:ZjrT6AXHbDs86ZSdt/osfBi5qfexBrKUdONk989Wnk4=
|
||||
github.com/compose-spec/compose-go/v2 v2.10.1 h1:mFbXobojGRFIVi1UknrvaDAZ+PkJfyjqkA1yseh+vAU=
|
||||
@@ -66,8 +52,8 @@ 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.4 h1:l59kGRVMtwMLDLh322HsWhEsBCkRKMkGWYV5vBeLYCE=
|
||||
github.com/containerd/nydus-snapshotter v0.15.4/go.mod h1:eRJqnxQDr48HNop15kZdLZpFF5B6vf6Q11Aq1K0E4Ms=
|
||||
github.com/containerd/nydus-snapshotter v0.15.10 h1:hphjuKOqSHLGznNJiAvmsOWkdu4qFXjf4DzGrWSuIsM=
|
||||
github.com/containerd/nydus-snapshotter v0.15.10/go.mod h1:EWRd/QJ0b6UKHAqYgiV5gHlqLC2qq5cQiSlXEdVovrA=
|
||||
github.com/containerd/platforms v1.0.0-rc.2 h1:0SPgaNZPVWGEi4grZdV8VRYQn78y+nm6acgLGv/QzE4=
|
||||
github.com/containerd/platforms v1.0.0-rc.2/go.mod h1:J71L7B+aiM5SdIEqmd9wp6THLVRzJGXfNuWCZCllLA4=
|
||||
github.com/containerd/plugin v1.0.0 h1:c8Kf1TNl6+e2TtMHZt+39yAPDbouRH9WAToRjex483Y=
|
||||
@@ -82,103 +68,123 @@ github.com/containerd/typeurl/v2 v2.2.3/go.mod h1:95ljDnPfD3bAbDJRugOiShd/DlAAsx
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.7 h1:zbFlGlXEAKlwXpmvle3d8Oe3YnkKIK4xSRTd3sHPnBo=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.7/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
|
||||
github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s=
|
||||
github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE=
|
||||
github.com/cyphar/filepath-securejoin v0.5.1 h1:eYgfMq5yryL4fbWfkLpFFy2ukSELzaJOTaUTuh+oF48=
|
||||
github.com/cyphar/filepath-securejoin v0.5.1/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI=
|
||||
github.com/cyberphone/json-canonicalization v0.0.0-20241213102144-19d51d7fe467 h1:uX1JmpONuD549D73r6cgnxyUu18Zb7yHAy5AYU0Pm4Q=
|
||||
github.com/cyberphone/json-canonicalization v0.0.0-20241213102144-19d51d7fe467/go.mod h1:uzvlm1mxhHkdfqitSA92i7Se+S9ksOn3a3qmv/kyOCw=
|
||||
github.com/cyphar/filepath-securejoin v0.6.0 h1:BtGB77njd6SVO6VztOHfPxKitJvd/VPT+OFBFMOi1Is=
|
||||
github.com/cyphar/filepath-securejoin v0.6.0/go.mod h1:A8hd4EnAeyujCJRrICiOWqjS1AX0a9kM5XL+NwKoYSc=
|
||||
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/denisenkom/go-mssqldb v0.0.0-20191128021309-1d7a30a10f73/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
|
||||
github.com/digitorus/pkcs7 v0.0.0-20230818184609-3a137a874352 h1:ge14PCmCvPjpMQMIAH7uKg0lrtNSOdpYsRXlwk3QbaE=
|
||||
github.com/digitorus/pkcs7 v0.0.0-20230818184609-3a137a874352/go.mod h1:SKVExuS+vpu2l9IoOc0RwqE7NYnb0JlcFHFnEJkVDzc=
|
||||
github.com/digitorus/timestamp v0.0.0-20231217203849-220c5c2851b7 h1:lxmTCgmHE1GUYL7P0MlNa00M67axePTq+9nBSGddR8I=
|
||||
github.com/digitorus/timestamp v0.0.0-20231217203849-220c5c2851b7/go.mod h1:GvWntX9qiTlOud0WkQ6ewFm0LPy5JUR1Xo0Ngbd1w6Y=
|
||||
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/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI=
|
||||
github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
|
||||
github.com/docker/buildx v0.30.1 h1:3vthfaTQOLt5QfN2nl7rKuPLUvx69nL5ZikFIXp//c8=
|
||||
github.com/docker/buildx v0.30.1/go.mod h1:8nwT0V6UNYNo9rXq6WO/BQd9KrJ0JYcY/QX6x0y1Oro=
|
||||
github.com/docker/cli v28.5.2+incompatible h1:XmG99IHcBmIAoC1PPg9eLBZPlTrNUAijsHLm8PjhBlg=
|
||||
github.com/docker/cli v28.5.2+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||
github.com/docker/buildx v0.31.1 h1:zbvbrb9nxBNVV8nnI33f2F+4aAZBA1gY+AmeBFflMqY=
|
||||
github.com/docker/buildx v0.31.1/go.mod h1:SD+jYLnt3S4SXqohVtV+8z+dihnOgwMJ8t+bLQvsaCk=
|
||||
github.com/docker/cli v29.2.1+incompatible h1:n3Jt0QVCN65eiVBoUTZQM9mcQICCJt3akW4pKAbKdJg=
|
||||
github.com/docker/cli v29.2.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||
github.com/docker/cli-docs-tool v0.11.0 h1:7d8QARFb7QEobizqxmEM7fOteZEHwH/zWgHQtHZEcfE=
|
||||
github.com/docker/cli-docs-tool v0.11.0/go.mod h1:ma8BKiisUo8D6W05XEYIh3oa1UbgrZhi1nowyKFJa8Q=
|
||||
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.5.2+incompatible h1:DBX0Y0zAjZbSrm1uzOkdr1onVghKaftjlSWt4AFexzM=
|
||||
github.com/docker/docker v28.5.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/docker-credential-helpers v0.9.3 h1:gAm/VtF9wgqJMoxzT3Gj5p4AqIjCBS4wrsOh9yRqcz8=
|
||||
github.com/docker/docker-credential-helpers v0.9.3/go.mod h1:x+4Gbw9aGmChi3qTLZj8Dfn0TD20M/fuWy0E5+WDeCo=
|
||||
github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c h1:lzqkGL9b3znc+ZUgi7FlLnqjQhcXxkNM/quxIjBVMD0=
|
||||
github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c/go.mod h1:CADgU4DSXK5QUlFslkQu2yW2TKzFZcXq/leZfM0UH5Q=
|
||||
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
|
||||
github.com/docker/docker-credential-helpers v0.9.5 h1:EFNN8DHvaiK8zVqFA2DT6BjXE0GzfLOZ38ggPTKePkY=
|
||||
github.com/docker/docker-credential-helpers v0.9.5/go.mod h1:v1S+hepowrQXITkEfw6o4+BMbGot02wiKpzWhGUZK6c=
|
||||
github.com/docker/go-connections v0.6.0 h1:LlMG9azAe1TqfR7sO+NJttz1gy6KO7VJBh+pMmjSD94=
|
||||
github.com/docker/go-connections v0.6.0/go.mod h1:AahvXYshr6JgfUJGdDCs2b5EZG/vmaMAntpSFH5BFKE=
|
||||
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=
|
||||
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
|
||||
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||
github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 h1:UhxFibDNY/bfvqU5CAUmr9zpesgbU6SWc8/B4mflAE4=
|
||||
github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE=
|
||||
github.com/dvsekhvalnov/jose2go v0.0.0-20170216131308-f21a8cedbbae/go.mod h1:7BvyPhdbLxMXIYTFPLsyJRFMsKmOZnQmzh6Gb+uquuM=
|
||||
github.com/eiannone/keyboard v0.0.0-20220611211555-0d226195f203 h1:XBBHcIb256gUJtLmY22n99HaZTz+r2Z51xUPi01m3wg=
|
||||
github.com/eiannone/keyboard v0.0.0-20220611211555-0d226195f203/go.mod h1:E1jcSv8FaEny+OP/5k9UxZVw9YFWGj7eI4KR/iOBqCg=
|
||||
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=
|
||||
github.com/fsnotify/fsevents v0.2.0 h1:BRlvlqjvNTfogHfeBOFvSC9N0Ddy+wzQCQukyoD7o/c=
|
||||
github.com/fsnotify/fsevents v0.2.0/go.mod h1:B3eEk39i4hz8y1zaWS/wPrAP4O6wkIl7HQwKBr1qH/w=
|
||||
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/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.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
||||
github.com/go-logr/logr v1.4.3/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-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-openapi/analysis v0.24.1 h1:Xp+7Yn/KOnVWYG8d+hPksOYnCYImE3TieBa7rBOesYM=
|
||||
github.com/go-openapi/analysis v0.24.1/go.mod h1:dU+qxX7QGU1rl7IYhBC8bIfmWQdX4Buoea4TGtxXY84=
|
||||
github.com/go-openapi/errors v0.22.4 h1:oi2K9mHTOb5DPW2Zjdzs/NIvwi2N3fARKaTJLdNabaM=
|
||||
github.com/go-openapi/errors v0.22.4/go.mod h1:z9S8ASTUqx7+CP1Q8dD8ewGH/1JWFFLX/2PmAYNQLgk=
|
||||
github.com/go-openapi/jsonpointer v0.22.1 h1:sHYI1He3b9NqJ4wXLoJDKmUmHkWy/L7rtEo92JUxBNk=
|
||||
github.com/go-openapi/jsonpointer v0.22.1/go.mod h1:pQT9OsLkfz1yWoMgYFy4x3U5GY5nUlsOn1qSBH5MkCM=
|
||||
github.com/go-openapi/jsonreference v0.21.3 h1:96Dn+MRPa0nYAR8DR1E03SblB5FJvh7W6krPI0Z7qMc=
|
||||
github.com/go-openapi/jsonreference v0.21.3/go.mod h1:RqkUP0MrLf37HqxZxrIAtTWW4ZJIK1VzduhXYBEeGc4=
|
||||
github.com/go-openapi/loads v0.23.2 h1:rJXAcP7g1+lWyBHC7iTY+WAF0rprtM+pm8Jxv1uQJp4=
|
||||
github.com/go-openapi/loads v0.23.2/go.mod h1:IEVw1GfRt/P2Pplkelxzj9BYFajiWOtY2nHZNj4UnWY=
|
||||
github.com/go-openapi/runtime v0.29.2 h1:UmwSGWNmWQqKm1c2MGgXVpC2FTGwPDQeUsBMufc5Yj0=
|
||||
github.com/go-openapi/runtime v0.29.2/go.mod h1:biq5kJXRJKBJxTDJXAa00DOTa/anflQPhT0/wmjuy+0=
|
||||
github.com/go-openapi/spec v0.22.1 h1:beZMa5AVQzRspNjvhe5aG1/XyBSMeX1eEOs7dMoXh/k=
|
||||
github.com/go-openapi/spec v0.22.1/go.mod h1:c7aeIQT175dVowfp7FeCvXXnjN/MrpaONStibD2WtDA=
|
||||
github.com/go-openapi/strfmt v0.25.0 h1:7R0RX7mbKLa9EYCTHRcCuIPcaqlyQiWNPTXwClK0saQ=
|
||||
github.com/go-openapi/strfmt v0.25.0/go.mod h1:nNXct7OzbwrMY9+5tLX4I21pzcmE6ccMGXl3jFdPfn8=
|
||||
github.com/go-openapi/swag v0.25.3 h1:FAa5wJXyDtI7yUztKDfZxDrSx+8WTg31MfCQ9s3PV+s=
|
||||
github.com/go-openapi/swag v0.25.3/go.mod h1:tX9vI8Mj8Ny+uCEk39I1QADvIPI7lkndX4qCsEqhkS8=
|
||||
github.com/go-openapi/swag/cmdutils v0.25.3 h1:EIwGxN143JCThNHnqfqs85R8lJcJG06qjJRZp3VvjLI=
|
||||
github.com/go-openapi/swag/cmdutils v0.25.3/go.mod h1:pdae/AFo6WxLl5L0rq87eRzVPm/XRHM3MoYgRMvG4A0=
|
||||
github.com/go-openapi/swag/conv v0.25.3 h1:PcB18wwfba7MN5BVlBIV+VxvUUeC2kEuCEyJ2/t2X7E=
|
||||
github.com/go-openapi/swag/conv v0.25.3/go.mod h1:n4Ibfwhn8NJnPXNRhBO5Cqb9ez7alBR40JS4rbASUPU=
|
||||
github.com/go-openapi/swag/fileutils v0.25.3 h1:P52Uhd7GShkeU/a1cBOuqIcHMHBrA54Z2t5fLlE85SQ=
|
||||
github.com/go-openapi/swag/fileutils v0.25.3/go.mod h1:cdOT/PKbwcysVQ9Tpr0q20lQKH7MGhOEb6EwmHOirUk=
|
||||
github.com/go-openapi/swag/jsonname v0.25.3 h1:U20VKDS74HiPaLV7UZkztpyVOw3JNVsit+w+gTXRj0A=
|
||||
github.com/go-openapi/swag/jsonname v0.25.3/go.mod h1:GPVEk9CWVhNvWhZgrnvRA6utbAltopbKwDu8mXNUMag=
|
||||
github.com/go-openapi/swag/jsonutils v0.25.3 h1:kV7wer79KXUM4Ea4tBdAVTU842Rg6tWstX3QbM4fGdw=
|
||||
github.com/go-openapi/swag/jsonutils v0.25.3/go.mod h1:ILcKqe4HC1VEZmJx51cVuZQ6MF8QvdfXsQfiaCs0z9o=
|
||||
github.com/go-openapi/swag/loading v0.25.3 h1:Nn65Zlzf4854MY6Ft0JdNrtnHh2bdcS/tXckpSnOb2Y=
|
||||
github.com/go-openapi/swag/loading v0.25.3/go.mod h1:xajJ5P4Ang+cwM5gKFrHBgkEDWfLcsAKepIuzTmOb/c=
|
||||
github.com/go-openapi/swag/mangling v0.25.3 h1:rGIrEzXaYWuUW1MkFmG3pcH+EIA0/CoUkQnIyB6TUyo=
|
||||
github.com/go-openapi/swag/mangling v0.25.3/go.mod h1:6dxwu6QyORHpIIApsdZgb6wBk/DPU15MdyYj/ikn0Hg=
|
||||
github.com/go-openapi/swag/netutils v0.25.3 h1:XWXHZfL/65ABiv8rvGp9dtE0C6QHTYkCrNV77jTl358=
|
||||
github.com/go-openapi/swag/netutils v0.25.3/go.mod h1:m2W8dtdaoX7oj9rEttLyTeEFFEBvnAx9qHd5nJEBzYg=
|
||||
github.com/go-openapi/swag/stringutils v0.25.3 h1:nAmWq1fUTWl/XiaEPwALjp/8BPZJun70iDHRNq/sH6w=
|
||||
github.com/go-openapi/swag/stringutils v0.25.3/go.mod h1:GTsRvhJW5xM5gkgiFe0fV3PUlFm0dr8vki6/VSRaZK0=
|
||||
github.com/go-openapi/swag/typeutils v0.25.3 h1:2w4mEEo7DQt3V4veWMZw0yTPQibiL3ri2fdDV4t2TQc=
|
||||
github.com/go-openapi/swag/typeutils v0.25.3/go.mod h1:Ou7g//Wx8tTLS9vG0UmzfCsjZjKhpjxayRKTHXf2pTE=
|
||||
github.com/go-openapi/swag/yamlutils v0.25.3 h1:LKTJjCn/W1ZfMec0XDL4Vxh8kyAnv1orH5F2OREDUrg=
|
||||
github.com/go-openapi/swag/yamlutils v0.25.3/go.mod h1:Y7QN6Wc5DOBXK14/xeo1cQlq0EA0wvLoSv13gDQoCao=
|
||||
github.com/go-openapi/validate v0.25.1 h1:sSACUI6Jcnbo5IWqbYHgjibrhhmt3vR6lCzKZnmAgBw=
|
||||
github.com/go-openapi/validate v0.25.1/go.mod h1:RMVyVFYte0gbSTaZ0N4KmTn6u/kClvAFp+mAVfS/DQc=
|
||||
github.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro=
|
||||
github.com/go-viper/mapstructure/v2 v2.5.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||
github.com/gofrs/flock v0.13.0 h1:95JolYOvGMqeH31+FC7D2+uULf6mG61mEZ/A8dRYMzw=
|
||||
github.com/gofrs/flock v0.13.0/go.mod h1:jxeyy9R1auM5S6JYDBhDt+E2TCo7DkratH4Pgi8P+Z0=
|
||||
github.com/gogo/protobuf v1.0.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo=
|
||||
github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
|
||||
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
|
||||
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ=
|
||||
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/google/certificate-transparency-go v1.0.10-0.20180222191210-5ab67e519c93 h1:jc2UWq7CbdszqeH6qu1ougXMIUBfSy8Pbh/anURYbGI=
|
||||
github.com/google/certificate-transparency-go v1.0.10-0.20180222191210-5ab67e519c93/go.mod h1:QeJfpSbVSfYc7RgB3gJFj9cbuQMMchQxrWXz8Ruopmg=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/certificate-transparency-go v1.3.2 h1:9ahSNZF2o7SYMaKaXhAumVEzXB2QaayzII9C8rv7v+A=
|
||||
github.com/google/certificate-transparency-go v1.3.2/go.mod h1:H5FpMUaGa5Ab2+KCYsxg6sELw3Flkl7pGZzWdBoYLXs=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/go-containerregistry v0.20.6 h1:cvWX87UxxLgaH76b4hIvya6Dzz9qHB31qAwjAohdSTU=
|
||||
github.com/google/go-containerregistry v0.20.6/go.mod h1:T0x8MuoAoKX/873bkeSfLD2FAkwCDf9/HZgsFJ02E2Y=
|
||||
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=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
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/grpc-ecosystem/grpc-gateway/v2 v2.27.2 h1:8Tjv8EJ+pM1xP8mK6egEbD1OgnVTyacbefKhmbLhIhU=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2/go.mod h1:pkJQ2tZHJ0aFOVEEot6oZmaVEZcRme73eIFmhiVuRWs=
|
||||
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/grpc-ecosystem/grpc-gateway/v2 v2.27.3 h1:NmZ1PKzSTQbuGHw9DGPFomqkkLWMC+vZCkfs+FHv1Vg=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3/go.mod h1:zQrxl1YP88HQlA6i9c63DSVPFklWpGX4OWAc9bFuaH4=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
|
||||
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
@@ -188,47 +194,26 @@ github.com/hashicorp/go-version v1.8.0 h1:KAkNb1HAiZd1ukkxDFGmokVZe1Xy9HG6NUp+bP
|
||||
github.com/hashicorp/go-version v1.8.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||
github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec h1:qv2VnGeEQHchGaZ/u7lxST/RaJw+cv273q79D81Xbog=
|
||||
github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec/go.mod h1:Q48J4R4DvxnHolD5P8pOtXigYlRuPLGl6moFx3ulM68=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/in-toto/attestation v1.1.2 h1:MBFn6lsMq6dptQZJBhalXTcWMb/aJy3V+GX3VYj/V1E=
|
||||
github.com/in-toto/attestation v1.1.2/go.mod h1:gYFddHMZj3DiQ0b62ltNi1Vj5rC879bTmBbrv9CRHpM=
|
||||
github.com/in-toto/in-toto-golang v0.9.0 h1:tHny7ac4KgtsfrG6ybU8gVOZux2H8jN05AXJ9EBM1XU=
|
||||
github.com/in-toto/in-toto-golang v0.9.0/go.mod h1:xsBVrVsHNsB61++S6Dy2vWosKhuA3lUTQd+eF9HdeMo=
|
||||
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/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/juju/loggo v0.0.0-20190526231331-6e530bcce5d8/go.mod h1:vgyd7OREkbtVEN/8IXZe5Ooef3LQePvuBm9UWj6ZL8U=
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
|
||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/klauspost/compress v1.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk=
|
||||
github.com/klauspost/compress v1.18.2/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/klauspost/compress v1.18.3 h1:9PJRvfbmTabkOX8moIpXPbMMbYN60bWImDDU7L+/6zw=
|
||||
github.com/klauspost/compress v1.18.3/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
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/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/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
|
||||
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
||||
@@ -239,28 +224,24 @@ github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6T
|
||||
github.com/mattn/go-runewidth v0.0.16/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/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=
|
||||
github.com/miekg/pkcs11 v1.1.1 h1:Ugu9pdy6vAYku5DEpVWVFPYnzV+bxB+iRdbuFSu7TvU=
|
||||
github.com/miekg/pkcs11 v1.1.1/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
|
||||
github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc=
|
||||
github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg=
|
||||
github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4=
|
||||
github.com/mitchellh/hashstructure/v2 v2.0.2/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/zz4kQkprJgF2EVszyDE=
|
||||
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.26.3 h1:D+ruZVAk/3ipRq5XRxBH9/DIFpRjSlTtMbghT5gQP9g=
|
||||
github.com/moby/buildkit v0.26.3/go.mod h1:4T4wJzQS4kYWIfFRjsbJry4QoxDBjK+UGOEOs1izL7w=
|
||||
github.com/moby/buildkit v0.27.1 h1:qlIWpnZzqCkrYiGkctM1gBD/YZPOJTjtUdRBlI0oBOU=
|
||||
github.com/moby/buildkit v0.27.1/go.mod h1:99qLrCrIAFgEOiFnCi9Y0Wwp6/qA7QvZ3uq/6wF0IsI=
|
||||
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/go-archive v0.1.0 h1:Kk/5rdW/g+H8NHdJW2gsXyZ7UnzvJNOy6VKJqueWdcQ=
|
||||
github.com/moby/go-archive v0.1.0/go.mod h1:G9B+YoujNohJmrIYFBpSd54GTUB4lt9S+xVQvsJyFuo=
|
||||
github.com/moby/go-archive v0.2.0 h1:zg5QDUM2mi0JIM9fdQZWC7U8+2ZfixfTYoHL7rWUcP8=
|
||||
github.com/moby/go-archive v0.2.0/go.mod h1:mNeivT14o8xU+5q1YnNrkQVpK+dnNe/K6fHqnTg4qPU=
|
||||
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/moby/api v1.53.0 h1:PihqG1ncw4W+8mZs69jlwGXdaYBeb5brF6BL7mPIS/w=
|
||||
github.com/moby/moby/api v1.53.0/go.mod h1:8mb+ReTlisw4pS6BRzCMts5M49W5M7bKt1cJy/YbAqc=
|
||||
github.com/moby/moby/client v0.2.2 h1:Pt4hRMCAIlyjL3cr8M5TrXCwKzguebPAc2do2ur7dEM=
|
||||
github.com/moby/moby/client v0.2.2/go.mod h1:2EkIPVNCqR05CMIzL1mfA07t0HvVUUOl85pasRz/GmQ=
|
||||
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/sys/atomicwriter v0.1.0 h1:kw5D/EqkBwsBFi0ss9v1VG3wIkVhzGvLklJ+w3A14Sw=
|
||||
@@ -281,70 +262,43 @@ 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/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/morikuni/aec v1.1.0 h1:vBBl0pUnvi/Je71dsRrhMBtreIqNMYErSAbEeb8jrXQ=
|
||||
github.com/morikuni/aec v1.1.0/go.mod h1:xDRgiq/iw5l+zkao76YTKzKttOp2cwPEne25HDkJnBw=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg=
|
||||
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/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
|
||||
github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4=
|
||||
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
||||
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/runtime-spec v1.3.0 h1:YZupQUdctfhpZy3TM39nN9Ika5CBWT5diQ8ibYCRkxg=
|
||||
github.com/opencontainers/runtime-spec v1.3.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
|
||||
github.com/opencontainers/selinux v1.13.1 h1:A8nNeceYngH9Ow++M+VVEwJVpdFmrlxsN22F+ISDCJE=
|
||||
github.com/opencontainers/selinux v1.13.1/go.mod h1:S10WXZ/osk2kWOYKy1x2f/eXF5ZHJoUs8UU/2caNRbg=
|
||||
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/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
|
||||
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/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/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.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o=
|
||||
github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg=
|
||||
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.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
|
||||
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
|
||||
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.66.1 h1:h5E0h5/Y8niHc5DlaLlWLArTQI7tMrsfQjHV+d9ZoGs=
|
||||
github.com/prometheus/common v0.66.1/go.mod h1:gcaUsgf3KfRSwHY4dIMXLPV0K/Wg1oZ8+SbZk/HH/dA=
|
||||
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=
|
||||
github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
|
||||
github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg=
|
||||
github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is=
|
||||
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
||||
github.com/prometheus/procfs v0.17.0 h1:FuLQ+05u4ZI+SS/w9+BWEM2TXiHKsUQ9TADiRH7DuK0=
|
||||
github.com/prometheus/procfs v0.17.0/go.mod h1:oPQLaDAMRbA+u8H5Pbfq+dl3VDAvHxMUOVhe0wYB2zw=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
||||
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
||||
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||
@@ -355,52 +309,53 @@ github.com/secure-systems-lab/go-securesystemslib v0.9.1 h1:nZZaNz4DiERIQguNy0cL
|
||||
github.com/secure-systems-lab/go-securesystemslib v0.9.1/go.mod h1:np53YzT0zXGMv6x4iEWc9Z59uR+x+ndLwCLqPYpLXVU=
|
||||
github.com/shibumi/go-pathspec v1.3.0 h1:QUyMZhFo0Md5B8zV8x2tesohbb5kfbpTi9rBnKh5dkI=
|
||||
github.com/shibumi/go-pathspec v1.3.0/go.mod h1:Xutfslp817l2I1cZvgcfeMQJG5QnU2lh5tVaaMCl3jE=
|
||||
github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
|
||||
github.com/sigstore/protobuf-specs v0.5.0 h1:F8YTI65xOHw70NrvPwJ5PhAzsvTnuJMGLkA4FIkofAY=
|
||||
github.com/sigstore/protobuf-specs v0.5.0/go.mod h1:+gXR+38nIa2oEupqDdzg4qSBT0Os+sP7oYv6alWewWc=
|
||||
github.com/sigstore/rekor v1.4.3 h1:2+aw4Gbgumv8vYM/QVg6b+hvr4x4Cukur8stJrVPKU0=
|
||||
github.com/sigstore/rekor v1.4.3/go.mod h1:o0zgY087Q21YwohVvGwV9vK1/tliat5mfnPiVI3i75o=
|
||||
github.com/sigstore/rekor-tiles/v2 v2.0.1 h1:1Wfz15oSRNGF5Dzb0lWn5W8+lfO50ork4PGIfEKjZeo=
|
||||
github.com/sigstore/rekor-tiles/v2 v2.0.1/go.mod h1:Pjsbhzj5hc3MKY8FfVTYHBUHQEnP0ozC4huatu4x7OU=
|
||||
github.com/sigstore/sigstore v1.10.0 h1:lQrmdzqlR8p9SCfWIpFoGUqdXEzJSZT2X+lTXOMPaQI=
|
||||
github.com/sigstore/sigstore v1.10.0/go.mod h1:Ygq+L/y9Bm3YnjpJTlQrOk/gXyrjkpn3/AEJpmk1n9Y=
|
||||
github.com/sigstore/sigstore-go v1.1.4-0.20251124094504-b5fe07a5a7d7 h1:94NLPmq4bxvdmslzcG670IOkrlS98CGpmob8cjpFHuI=
|
||||
github.com/sigstore/sigstore-go v1.1.4-0.20251124094504-b5fe07a5a7d7/go.mod h1:4r/PNX0G7uzkLpc3PSdYs5E2k4bWEJNXTK6kwAyw9TM=
|
||||
github.com/sigstore/timestamp-authority/v2 v2.0.2 h1:WavlEeLh6HKt+osbmsHDg6/FaM/8Pz9iVUMh9pAsl/o=
|
||||
github.com/sigstore/timestamp-authority/v2 v2.0.2/go.mod h1:D+wbQg8ASQzKnwBhLo7rIJD+9Zev4Ppqd4myPe8k57E=
|
||||
github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w=
|
||||
github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g=
|
||||
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 h1:JIAuq3EEf9cgbU6AtGPK4CTG3Zf6CKMNqf0MHTggAUA=
|
||||
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog=
|
||||
github.com/spdx/tools-golang v0.5.5 h1:61c0KLfAcNqAjlg6UNMdkwpMernhw3zVRwDZ2x9XOmk=
|
||||
github.com/spdx/tools-golang v0.5.5/go.mod h1:MVIsXx8ZZzaRWNQpUDhC4Dud34edUYJYecciXgrw5vE=
|
||||
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/spdx/tools-golang v0.5.7 h1:+sWcKGnhwp3vLdMqPcLdA6QK679vd86cK9hQWH3AwCg=
|
||||
github.com/spdx/tools-golang v0.5.7/go.mod h1:jg7w0LOpoNAw6OxKEzCoqPC2GCTj45LyTlVmXubDsYw=
|
||||
github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=
|
||||
github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4=
|
||||
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.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
|
||||
github.com/spf13/pflag v1.0.10/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=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/theupdateframework/notary v0.7.0 h1:QyagRZ7wlSpjT5N2qQAh/pN+DVqgekv4DzbAiAiEL3c=
|
||||
github.com/theupdateframework/notary v0.7.0/go.mod h1:c9DRxcmhHmVLDay4/2fUYdISnHqbFDGRSlXPO0AhYWw=
|
||||
github.com/theupdateframework/go-tuf v0.7.0 h1:CqbQFrWo1ae3/I0UCblSbczevCCbS31Qvs5LdxRWqRI=
|
||||
github.com/theupdateframework/go-tuf/v2 v2.3.0 h1:gt3X8xT8qu/HT4w+n1jgv+p7koi5ad8XEkLXXZqG9AA=
|
||||
github.com/theupdateframework/go-tuf/v2 v2.3.0/go.mod h1:xW8yNvgXRncmovMLvBxKwrKpsOwJZu/8x+aB0KtFcdw=
|
||||
github.com/tilt-dev/fsnotify v1.4.8-0.20220602155310-fff9c274a375 h1:QB54BJwA6x8QU9nHY3xJSZR2kX9bgpZekRKGkLTmEXA=
|
||||
github.com/tilt-dev/fsnotify v1.4.8-0.20220602155310-fff9c274a375/go.mod h1:xRroudyp5iVtxKqZCrA6n2TLFRBf8bmnjr1UD4x+z7g=
|
||||
github.com/tonistiigi/dchapes-mode v0.0.0-20250318174251-73d941a28323 h1:r0p7fK56l8WPequOaR3i9LBqfPtEdXIQbUTzT55iqT4=
|
||||
github.com/tonistiigi/dchapes-mode v0.0.0-20250318174251-73d941a28323/go.mod h1:3Iuxbr0P7D3zUzBMAZB+ois3h/et0shEz0qApgHYGpY=
|
||||
github.com/tonistiigi/fsutil v0.0.0-20250605211040-586307ad452f h1:MoxeMfHAe5Qj/ySSBfL8A7l1V+hxuluj8owsIEEZipI=
|
||||
github.com/tonistiigi/fsutil v0.0.0-20250605211040-586307ad452f/go.mod h1:BKdcez7BiVtBvIcef90ZPc6ebqIWr4JWD7+EvLm6J98=
|
||||
github.com/tonistiigi/fsutil v0.0.0-20251211185533-a2aa163d723f h1:Z4NEQ86qFl1mHuCu9gwcE+EYCwDKfXAYXZbdIXyxmEA=
|
||||
github.com/tonistiigi/fsutil v0.0.0-20251211185533-a2aa163d723f/go.mod h1:BKdcez7BiVtBvIcef90ZPc6ebqIWr4JWD7+EvLm6J98=
|
||||
github.com/tonistiigi/go-csvvalue v0.0.0-20240814133006-030d3b2625d0 h1:2f304B10LaZdB8kkVEaoXvAMVan2tl9AiK4G0odjQtE=
|
||||
github.com/tonistiigi/go-csvvalue v0.0.0-20240814133006-030d3b2625d0/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/transparency-dev/formats v0.0.0-20251017110053-404c0d5b696c h1:5a2XDQ2LiAUV+/RjckMyq9sXudfrPSuCY4FuPC1NyAw=
|
||||
github.com/transparency-dev/formats v0.0.0-20251017110053-404c0d5b696c/go.mod h1:g85IafeFJZLxlzZCDRu4JLpfS7HKzR+Hw9qRh3bVzDI=
|
||||
github.com/transparency-dev/merkle v0.0.2 h1:Q9nBoQcZcgPamMkGn7ghV8XiTZ/kRxn1yCG81+twTK4=
|
||||
github.com/transparency-dev/merkle v0.0.2/go.mod h1:pqSy+OXefQ1EDUVmAJ8MUhHB9TXGuzVAT58PqBoHz1A=
|
||||
github.com/vbatts/tar-split v0.12.2 h1:w/Y6tjxpeiFMR47yzZPlPj/FcPLpXbTUi/9H7d3CPa4=
|
||||
github.com/vbatts/tar-split v0.12.2/go.mod h1:eF6B6i6ftWQcDqEn3/iGFRFRo8cBIMSJVOpnNdfTMFA=
|
||||
github.com/xhit/go-str2duration/v2 v2.1.0 h1:lxklc02Drh6ynqX+DdPyp5pCKLUQpRT8bp8Ydu2Bstc=
|
||||
@@ -408,6 +363,8 @@ github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtX
|
||||
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=
|
||||
go.mongodb.org/mongo-driver v1.17.6 h1:87JUG1wZfWsr6rIz3ZmpH90rL5tea7O3IHuSwHUpsss=
|
||||
go.mongodb.org/mongo-driver v1.17.6/go.mod h1:Hy04i7O2kC4RS06ZrhPRqj/u4DTYkFDAAccj+rVKqgQ=
|
||||
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.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
|
||||
@@ -444,56 +401,40 @@ 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.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y=
|
||||
go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU=
|
||||
go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI=
|
||||
go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU=
|
||||
go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0=
|
||||
go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8=
|
||||
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
||||
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||
go.yaml.in/yaml/v4 v4.0.0-rc.3 h1:3h1fjsh1CTAPjW7q/EMe+C8shx5d8ctzZTrLcs/j8Go=
|
||||
go.yaml.in/yaml/v4 v4.0.0-rc.3/go.mod h1:aZqd9kCMsGL7AuUv/m/PvWLdg5sjJsZ4oHDEnfPPfY0=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
go.yaml.in/yaml/v4 v4.0.0-rc.4 h1:UP4+v6fFrBIb1l934bDl//mmnoIZEDK0idg1+AIvX5U=
|
||||
go.yaml.in/yaml/v4 v4.0.0-rc.4/go.mod h1:aZqd9kCMsGL7AuUv/m/PvWLdg5sjJsZ4oHDEnfPPfY0=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
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.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
|
||||
golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=
|
||||
golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU=
|
||||
golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0=
|
||||
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.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA=
|
||||
golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w=
|
||||
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/mod v0.31.0 h1:HaW9xtz0+kOcWKwli0ZXy79Ix+UW/vOfmWI5QVd2tgI=
|
||||
golang.org/x/mod v0.31.0/go.mod h1:43JraMp9cGx1Rx3AqioxrbrhNsLl2l/iNAvuBkrezpg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
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.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
|
||||
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
|
||||
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=
|
||||
golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
|
||||
golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
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.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
||||
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
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=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210331175145-43e1dd70ce54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -504,19 +445,18 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/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.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=
|
||||
golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
|
||||
golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
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.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU=
|
||||
golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254=
|
||||
golang.org/x/term v0.38.0 h1:PQ5pkm/rLO6HnxFR7N2lJHOZX6Kez5Y1gDSJla6jo7Q=
|
||||
golang.org/x/term v0.38.0/go.mod h1:bSEAKrOT1W+VSu9TSCMtoGEOUcKxOKgl3LE5QEF/xVg=
|
||||
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.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
|
||||
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
|
||||
golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
|
||||
golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
|
||||
golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=
|
||||
golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
@@ -530,39 +470,25 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
||||
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20251029180050-ab9386a59fda h1:+2XxjfsAu6vqFxwGBRcHiMaDCuZiqXGDUDVWVtrFAnE=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20251029180050-ab9386a59fda/go.mod h1:fDMmzKV90WSg1NbozdqrE64fkuTv6mlq2zxo9ad+3yo=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251029180050-ab9386a59fda h1:i/Q+bfisr7gq6feoJnS/DlpdwEL4ihp41fvRiM3Ork0=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251029180050-ab9386a59fda/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk=
|
||||
google.golang.org/grpc v1.0.5/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20251103181224-f26f9409b101 h1:vk5TfqZHNn0obhPIYeS+cxIFKFQgser/M2jnI+9c6MM=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20251103181224-f26f9409b101/go.mod h1:E17fc4PDhkr22dE3RgnH2hEubUaky6ZwW4VhANxyspg=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251103181224-f26f9409b101 h1:tRPGkdGHuewF4UisLzzHHr1spKw92qLM98nIzxbC0wY=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251103181224-f26f9409b101/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk=
|
||||
google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc=
|
||||
google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U=
|
||||
google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
|
||||
google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
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.v2 v2.2.1 h1:eJ9UAg01/HIHG987TwxvnzK2MgxXq97YY6rYDpY9aII=
|
||||
gopkg.in/cenkalti/backoff.v2 v2.2.1/go.mod h1:S0QdOvT2AlerfSBkp0O+dk+bbIMaNbEmVk876gPCthU=
|
||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo=
|
||||
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=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
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=
|
||||
pgregory.net/rapid v1.2.0 h1:keKAYRcjm+e1F0oAuU5F5+YPAWcyxNNRK2wud503Gnk=
|
||||
pgregory.net/rapid v1.2.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04=
|
||||
tags.cncf.io/container-device-interface v1.1.0 h1:RnxNhxF1JOu6CJUVpetTYvrXHdxw9j9jFYgZpI+anSY=
|
||||
tags.cncf.io/container-device-interface v1.1.0/go.mod h1:76Oj0Yqp9FwTx/pySDc8Bxjpg+VqXfDb50cKAXVJ34Q=
|
||||
|
||||
@@ -31,8 +31,8 @@ import (
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/moby/go-archive"
|
||||
"github.com/moby/moby/api/types/container"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/moby/moby/api/types/container"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
)
|
||||
|
||||
@@ -36,15 +36,13 @@ func (m MuxExporter) ExportSpans(ctx context.Context, spans []sdktrace.ReadOnlyS
|
||||
)
|
||||
|
||||
for _, exporter := range m.exporters {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
wg.Go(func() {
|
||||
if err := exporter.ExportSpans(ctx, spans); err != nil {
|
||||
errMu.Lock()
|
||||
errs = append(errs, err)
|
||||
errMu.Unlock()
|
||||
}
|
||||
}()
|
||||
})
|
||||
}
|
||||
wg.Wait()
|
||||
return errors.Join(errs...)
|
||||
@@ -58,15 +56,13 @@ func (m MuxExporter) Shutdown(ctx context.Context) error {
|
||||
)
|
||||
|
||||
for _, exporter := range m.exporters {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
wg.Go(func() {
|
||||
if err := exporter.Shutdown(ctx); err != nil {
|
||||
errMu.Lock()
|
||||
errs = append(errs, err)
|
||||
errMu.Unlock()
|
||||
}
|
||||
}()
|
||||
})
|
||||
}
|
||||
wg.Wait()
|
||||
return errors.Join(errs...)
|
||||
|
||||
@@ -28,7 +28,8 @@ import (
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
"github.com/containerd/platforms"
|
||||
"github.com/docker/cli/opts"
|
||||
"github.com/docker/docker/api/types/volume"
|
||||
"github.com/moby/moby/api/types/container"
|
||||
"github.com/moby/moby/api/types/volume"
|
||||
)
|
||||
|
||||
// LoadListener receives events during project loading.
|
||||
@@ -154,7 +155,7 @@ type VolumesOptions struct {
|
||||
Services []string
|
||||
}
|
||||
|
||||
type VolumesSummary = *volume.Volume
|
||||
type VolumesSummary = volume.Volume
|
||||
|
||||
type ScaleOptions struct {
|
||||
Services []string
|
||||
@@ -544,9 +545,9 @@ type ContainerSummary struct {
|
||||
Project string
|
||||
Service string
|
||||
Created int64
|
||||
State string
|
||||
State container.ContainerState
|
||||
Status string
|
||||
Health string
|
||||
Health container.HealthStatus
|
||||
ExitCode int
|
||||
Publishers PortPublishers
|
||||
Labels map[string]string
|
||||
|
||||
@@ -30,11 +30,11 @@ import (
|
||||
"github.com/containerd/errdefs"
|
||||
"github.com/docker/cli/cli/command"
|
||||
cli "github.com/docker/cli/cli/command/container"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/image"
|
||||
"github.com/docker/docker/api/types/network"
|
||||
"github.com/docker/docker/pkg/jsonmessage"
|
||||
"github.com/docker/go-connections/nat"
|
||||
"github.com/moby/moby/api/types/container"
|
||||
"github.com/moby/moby/api/types/image"
|
||||
"github.com/moby/moby/api/types/network"
|
||||
"github.com/moby/moby/client"
|
||||
"github.com/moby/moby/client/pkg/jsonmessage"
|
||||
"github.com/sirupsen/logrus"
|
||||
"go.yaml.in/yaml/v4"
|
||||
|
||||
@@ -138,10 +138,14 @@ func convert(ctx context.Context, dockerCli command.Cli, model map[string]any, o
|
||||
}
|
||||
containerConfig.User = usr.Uid
|
||||
}
|
||||
created, err := dockerCli.Client().ContainerCreate(ctx, containerConfig, &container.HostConfig{
|
||||
AutoRemove: true,
|
||||
Binds: binds,
|
||||
}, &network.NetworkingConfig{}, nil, "")
|
||||
created, err := dockerCli.Client().ContainerCreate(ctx, client.ContainerCreateOptions{
|
||||
Config: containerConfig,
|
||||
HostConfig: &container.HostConfig{
|
||||
Binds: binds,
|
||||
AutoRemove: true,
|
||||
},
|
||||
NetworkingConfig: &network.NetworkingConfig{},
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -170,7 +174,11 @@ func LoadAdditionalResources(ctx context.Context, dockerCLI command.Cli, project
|
||||
exposed := utils.Set[string]{}
|
||||
exposed.AddAll(service.Expose...)
|
||||
for port := range inspect.Config.ExposedPorts {
|
||||
exposed.Add(nat.Port(port).Port())
|
||||
p, err := network.ParsePort(port)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
exposed.Add(strconv.Itoa(int(p.Num())))
|
||||
}
|
||||
for _, port := range service.Ports {
|
||||
exposed.Add(strconv.Itoa(int(port.Target)))
|
||||
@@ -218,13 +226,14 @@ func inspectWithPull(ctx context.Context, dockerCli command.Cli, imageName strin
|
||||
inspect, err := dockerCli.Client().ImageInspect(ctx, imageName)
|
||||
if errdefs.IsNotFound(err) {
|
||||
var stream io.ReadCloser
|
||||
stream, err = dockerCli.Client().ImagePull(ctx, imageName, image.PullOptions{})
|
||||
stream, err = dockerCli.Client().ImagePull(ctx, imageName, client.ImagePullOptions{})
|
||||
if err != nil {
|
||||
return image.InspectResponse{}, err
|
||||
}
|
||||
defer func() { _ = stream.Close() }()
|
||||
|
||||
err = jsonmessage.DisplayJSONMessagesToStream(stream, dockerCli.Out(), nil)
|
||||
out := dockerCli.Out()
|
||||
err = jsonmessage.DisplayJSONMessagesStream(stream, out, out.FD(), out.IsTerminal(), nil)
|
||||
if err != nil {
|
||||
return image.InspectResponse{}, err
|
||||
}
|
||||
@@ -232,5 +241,5 @@ func inspectWithPull(ctx context.Context, dockerCli command.Cli, imageName strin
|
||||
return image.InspectResponse{}, err
|
||||
}
|
||||
}
|
||||
return inspect, err
|
||||
return inspect.InspectResponse, err
|
||||
}
|
||||
|
||||
@@ -23,11 +23,9 @@ import (
|
||||
"path/filepath"
|
||||
|
||||
"github.com/docker/cli/cli/command"
|
||||
"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/moby/go-archive"
|
||||
"github.com/moby/moby/api/types/image"
|
||||
"github.com/moby/moby/client"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -65,34 +63,39 @@ func CreateTransformer(ctx context.Context, dockerCli command.Cli, options Creat
|
||||
return err
|
||||
}
|
||||
|
||||
created, err := dockerCli.Client().ContainerCreate(ctx, &container.Config{
|
||||
created, err := dockerCli.Client().ContainerCreate(ctx, client.ContainerCreateOptions{
|
||||
Image: options.From,
|
||||
}, &container.HostConfig{}, &network.NetworkingConfig{}, nil, "")
|
||||
})
|
||||
|
||||
defer func() {
|
||||
_ = dockerCli.Client().ContainerRemove(context.Background(), created.ID, container.RemoveOptions{Force: true})
|
||||
_, _ = dockerCli.Client().ContainerRemove(context.Background(), created.ID, client.ContainerRemoveOptions{
|
||||
Force: true,
|
||||
})
|
||||
}()
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
content, stat, err := dockerCli.Client().CopyFromContainer(ctx, created.ID, templatesPath)
|
||||
resp, err := dockerCli.Client().CopyFromContainer(ctx, created.ID, client.CopyFromContainerOptions{
|
||||
SourcePath: templatesPath,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
_ = content.Close()
|
||||
_ = resp.Content.Close()
|
||||
}()
|
||||
|
||||
srcInfo := archive.CopyInfo{
|
||||
Path: templatesPath,
|
||||
Exists: true,
|
||||
IsDir: stat.Mode.IsDir(),
|
||||
IsDir: resp.Stat.Mode.IsDir(),
|
||||
}
|
||||
|
||||
preArchive := content
|
||||
preArchive := resp.Content
|
||||
if srcInfo.RebaseName != "" {
|
||||
_, srcBase := archive.SplitPathDirEntry(srcInfo.Path)
|
||||
preArchive = archive.RebaseArchiveEntries(content, srcBase, srcInfo.RebaseName)
|
||||
preArchive = archive.RebaseArchiveEntries(resp.Content, srcBase, srcInfo.RebaseName)
|
||||
}
|
||||
|
||||
if err := archive.CopyTo(preArchive, srcInfo, out); err != nil {
|
||||
@@ -111,10 +114,11 @@ COPY templates /templates
|
||||
}
|
||||
|
||||
func ListTransformers(ctx context.Context, dockerCli command.Cli) ([]image.Summary, error) {
|
||||
api := dockerCli.Client()
|
||||
return api.ImageList(ctx, image.ListOptions{
|
||||
Filters: filters.NewArgs(
|
||||
filters.Arg("label", fmt.Sprintf("%s=%s", TransformerLabel, "transformation")),
|
||||
),
|
||||
res, err := dockerCli.Client().ImageList(ctx, client.ImageListOptions{
|
||||
Filters: make(client.Filters).Add("label", fmt.Sprintf("%s=%s", TransformerLabel, "transformation")),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return res.Items, nil
|
||||
}
|
||||
|
||||
@@ -19,29 +19,16 @@ package compose
|
||||
// Docker Engine API version constants.
|
||||
// These versions correspond to specific Docker Engine releases and their features.
|
||||
const (
|
||||
// APIVersion144 represents Docker Engine API version 1.44 (Engine v25.0).
|
||||
//
|
||||
// New features in this version:
|
||||
// - Endpoint-specific MAC address configuration
|
||||
// - Multiple networks can be connected during container creation
|
||||
// - healthcheck.start_interval parameter support
|
||||
//
|
||||
// Before this version:
|
||||
// - MAC address was container-wide only
|
||||
// - Extra networks required post-creation NetworkConnect calls
|
||||
// - healthcheck.start_interval was not available
|
||||
APIVersion144 = "1.44"
|
||||
|
||||
// APIVersion148 represents Docker Engine API version 1.48 (Engine v28.0).
|
||||
// apiVersion148 represents Docker Engine API version 1.48 (Engine v28.0).
|
||||
//
|
||||
// New features in this version:
|
||||
// - Volume mounts with type=image support
|
||||
//
|
||||
// Before this version:
|
||||
// - Only bind, volume, and tmpfs mount types were supported
|
||||
APIVersion148 = "1.48"
|
||||
apiVersion148 = "1.48"
|
||||
|
||||
// APIVersion149 represents Docker Engine API version 1.49 (Engine v28.1).
|
||||
// apiVersion149 represents Docker Engine API version 1.49 (Engine v28.1).
|
||||
//
|
||||
// New features in this version:
|
||||
// - Network interface_name configuration
|
||||
@@ -50,17 +37,14 @@ const (
|
||||
// Before this version:
|
||||
// - interface_name was not configurable
|
||||
// - ImageList didn't support platform filtering
|
||||
APIVersion149 = "1.49"
|
||||
apiVersion149 = "1.49"
|
||||
)
|
||||
|
||||
// Docker Engine version strings for user-facing error messages.
|
||||
// These should be used in error messages to provide clear version requirements.
|
||||
const (
|
||||
// DockerEngineV25 is the major version string for Docker Engine 25.x
|
||||
DockerEngineV25 = "v25"
|
||||
|
||||
// DockerEngineV28 is the major version string for Docker Engine 28.x
|
||||
DockerEngineV28 = "v28"
|
||||
// dockerEngineV28 is the major version string for Docker Engine 28.x
|
||||
dockerEngineV28 = "v28"
|
||||
|
||||
// DockerEngineV28_1 is the specific version string for Docker Engine 28.1
|
||||
DockerEngineV28_1 = "v28.1"
|
||||
@@ -68,6 +52,6 @@ const (
|
||||
|
||||
// Build tool version constants
|
||||
const (
|
||||
// BuildxMinVersion is the minimum required version of buildx for compose build
|
||||
BuildxMinVersion = "0.17.0"
|
||||
// buildxMinVersion is the minimum required version of buildx for compose build
|
||||
buildxMinVersion = "0.17.0"
|
||||
)
|
||||
|
||||
@@ -24,8 +24,9 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
containerType "github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/pkg/stdcopy"
|
||||
"github.com/moby/moby/api/pkg/stdcopy"
|
||||
containerType "github.com/moby/moby/api/types/container"
|
||||
"github.com/moby/moby/client"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/docker/compose/v5/pkg/api"
|
||||
@@ -69,7 +70,7 @@ func (s *composeService) attachContainer(ctx context.Context, container containe
|
||||
}
|
||||
|
||||
func (s *composeService) doAttachContainer(ctx context.Context, service, id, name string, listener api.ContainerEventListener) error {
|
||||
inspect, err := s.apiClient().ContainerInspect(ctx, id)
|
||||
inspect, err := s.apiClient().ContainerInspect(ctx, id, client.ContainerInspectOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -93,7 +94,7 @@ func (s *composeService) doAttachContainer(ctx context.Context, service, id, nam
|
||||
})
|
||||
})
|
||||
|
||||
err = s.attachContainerStreams(ctx, id, inspect.Config.Tty, wOut, wErr)
|
||||
err = s.attachContainerStreams(ctx, id, inspect.Container.Config.Tty, wOut, wErr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -136,7 +137,7 @@ func (s *composeService) attachContainerStreams(ctx context.Context, container s
|
||||
}
|
||||
|
||||
func (s *composeService) getContainerStreams(ctx context.Context, container string) (io.ReadCloser, error) {
|
||||
cnx, err := s.apiClient().ContainerAttach(ctx, container, containerType.AttachOptions{
|
||||
cnx, err := s.apiClient().ContainerAttach(ctx, container, client.ContainerAttachOptions{
|
||||
Stream: true,
|
||||
Stdin: false,
|
||||
Stdout: true,
|
||||
@@ -144,12 +145,12 @@ func (s *composeService) getContainerStreams(ctx context.Context, container stri
|
||||
Logs: false,
|
||||
})
|
||||
if err == nil {
|
||||
stdout := ContainerStdout{HijackedResponse: cnx}
|
||||
stdout := ContainerStdout{HijackedResponse: cnx.HijackedResponse}
|
||||
return stdout, nil
|
||||
}
|
||||
|
||||
// Fallback to logs API
|
||||
logs, err := s.apiClient().ContainerLogs(ctx, container, containerType.LogsOptions{
|
||||
logs, err := s.apiClient().ContainerLogs(ctx, container, client.ContainerLogsOptions{
|
||||
ShowStdout: true,
|
||||
ShowStderr: true,
|
||||
Follow: true,
|
||||
|
||||
@@ -158,11 +158,37 @@ func (s *composeService) ensureImagesExists(ctx context.Context, project *types.
|
||||
if ok {
|
||||
service.CustomLabels.Add(api.ImageDigestLabel, img.ID)
|
||||
}
|
||||
|
||||
resolveImageVolumes(&service, images, project.Name)
|
||||
|
||||
project.Services[name] = service
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func resolveImageVolumes(service *types.ServiceConfig, images map[string]api.ImageSummary, projectName string) {
|
||||
for i, vol := range service.Volumes {
|
||||
if vol.Type == types.VolumeTypeImage {
|
||||
imgName := vol.Source
|
||||
if _, ok := images[vol.Source]; !ok {
|
||||
// check if source is another service in the project
|
||||
imgName = api.GetImageNameOrDefault(types.ServiceConfig{Name: vol.Source}, projectName)
|
||||
// If we still can't find it, it might be an external image that wasn't pulled yet or doesn't exist
|
||||
if _, ok := images[imgName]; !ok {
|
||||
continue
|
||||
}
|
||||
}
|
||||
if img, ok := images[imgName]; ok {
|
||||
// Use Image ID directly as source.
|
||||
// Using name@digest format (via reference.WithDigest) fails for local-only images
|
||||
// that don't have RepoDigests (e.g. built locally in CI).
|
||||
// Image ID (sha256:...) is always valid and ensures ServiceHash changes on rebuild.
|
||||
service.Volumes[i].Source = img.ID
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *composeService) getLocalImagesDigests(ctx context.Context, project *types.Project) (map[string]api.ImageSummary, error) {
|
||||
imageNames := utils.Set[string]{}
|
||||
for _, s := range project.Services {
|
||||
|
||||
@@ -39,11 +39,11 @@ import (
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/cli/cli/command/image/build"
|
||||
"github.com/docker/cli/cli/streams"
|
||||
"github.com/docker/docker/api/types/versions"
|
||||
"github.com/google/uuid"
|
||||
"github.com/moby/buildkit/client"
|
||||
gitutil "github.com/moby/buildkit/frontend/dockerfile/dfgitutil"
|
||||
"github.com/moby/buildkit/util/progress/progressui"
|
||||
"github.com/moby/moby/client/pkg/versions"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
"golang.org/x/sync/errgroup"
|
||||
@@ -424,8 +424,8 @@ func (s *composeService) getBuildxPlugin() (*manager.Plugin, error) {
|
||||
return nil, fmt.Errorf("failed to get version of buildx")
|
||||
}
|
||||
|
||||
if versions.LessThan(buildx.Version[1:], BuildxMinVersion) {
|
||||
return nil, fmt.Errorf("compose build requires buildx %s or later", BuildxMinVersion)
|
||||
if versions.LessThan(buildx.Version[1:], buildxMinVersion) {
|
||||
return nil, fmt.Errorf("compose build requires buildx %s or later", buildxMinVersion)
|
||||
}
|
||||
|
||||
return buildx, nil
|
||||
@@ -447,7 +447,7 @@ type _console struct {
|
||||
*streams.Out
|
||||
}
|
||||
|
||||
func (c _console) Read(p []byte) (n int, err error) {
|
||||
func (c _console) Read([]byte) (n int, err error) {
|
||||
return 0, errors.New("not implemented")
|
||||
}
|
||||
|
||||
|
||||
@@ -29,13 +29,15 @@ import (
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
"github.com/docker/cli/cli"
|
||||
"github.com/docker/cli/cli/command/image/build"
|
||||
buildtypes "github.com/docker/docker/api/types/build"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/registry"
|
||||
"github.com/docker/docker/pkg/jsonmessage"
|
||||
"github.com/docker/docker/pkg/progress"
|
||||
"github.com/docker/docker/pkg/streamformatter"
|
||||
"github.com/moby/go-archive"
|
||||
buildtypes "github.com/moby/moby/api/types/build"
|
||||
"github.com/moby/moby/api/types/container"
|
||||
"github.com/moby/moby/api/types/jsonstream"
|
||||
"github.com/moby/moby/api/types/registry"
|
||||
"github.com/moby/moby/client"
|
||||
"github.com/moby/moby/client/pkg/jsonmessage"
|
||||
"github.com/moby/moby/client/pkg/progress"
|
||||
"github.com/moby/moby/client/pkg/streamformatter"
|
||||
"github.com/sirupsen/logrus"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
@@ -266,7 +268,7 @@ func (s *composeService) doBuildImage(ctx context.Context, project *types.Projec
|
||||
defer response.Body.Close() //nolint:errcheck
|
||||
|
||||
imageID := ""
|
||||
aux := func(msg jsonmessage.JSONMessage) {
|
||||
aux := func(msg jsonstream.Message) {
|
||||
var result buildtypes.Result
|
||||
if err := json.Unmarshal(*msg.Aux, &result); err != nil {
|
||||
logrus.Errorf("Failed to parse aux message: %s", err)
|
||||
@@ -277,7 +279,7 @@ func (s *composeService) doBuildImage(ctx context.Context, project *types.Projec
|
||||
|
||||
err = jsonmessage.DisplayJSONMessagesStream(response.Body, buildBuff, progBuff.FD(), true, aux)
|
||||
if err != nil {
|
||||
var jerr *jsonmessage.JSONError
|
||||
var jerr *jsonstream.Error
|
||||
if errors.As(err, &jerr) {
|
||||
// If no error code is set, default to 1
|
||||
if jerr.Code == 0 {
|
||||
@@ -291,9 +293,9 @@ func (s *composeService) doBuildImage(ctx context.Context, project *types.Projec
|
||||
return imageID, nil
|
||||
}
|
||||
|
||||
func imageBuildOptions(proxyConfigs map[string]string, project *types.Project, service types.ServiceConfig, options api.BuildOptions) buildtypes.ImageBuildOptions {
|
||||
func imageBuildOptions(proxyConfigs map[string]string, project *types.Project, service types.ServiceConfig, options api.BuildOptions) client.ImageBuildOptions {
|
||||
config := service.Build
|
||||
return buildtypes.ImageBuildOptions{
|
||||
return client.ImageBuildOptions{
|
||||
Version: buildtypes.BuilderV1,
|
||||
Tags: config.Tags,
|
||||
NoCache: config.NoCache,
|
||||
|
||||
@@ -21,7 +21,7 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/moby/moby/client"
|
||||
|
||||
"github.com/docker/compose/v5/pkg/api"
|
||||
)
|
||||
@@ -58,12 +58,12 @@ func (s *composeService) commit(ctx context.Context, projectName string, options
|
||||
return nil
|
||||
}
|
||||
|
||||
response, err := s.apiClient().ContainerCommit(ctx, ctr.ID, container.CommitOptions{
|
||||
response, err := s.apiClient().ContainerCommit(ctx, ctr.ID, client.ContainerCommitOptions{
|
||||
Reference: options.Reference,
|
||||
Comment: options.Comment,
|
||||
Author: options.Author,
|
||||
Changes: options.Changes.GetSlice(),
|
||||
Pause: options.Pause,
|
||||
NoPause: !options.Pause,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -31,13 +31,10 @@ import (
|
||||
"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/moby/moby/api/types/container"
|
||||
"github.com/moby/moby/api/types/swarm"
|
||||
"github.com/moby/moby/client"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/docker/compose/v5/pkg/api"
|
||||
@@ -434,8 +431,8 @@ func increment(scale *int) *int {
|
||||
}
|
||||
|
||||
func (s *composeService) actualVolumes(ctx context.Context, projectName string) (types.Volumes, error) {
|
||||
opts := volume.ListOptions{
|
||||
Filters: filters.NewArgs(projectFilter(projectName)),
|
||||
opts := client.VolumeListOptions{
|
||||
Filters: projectFilter(projectName),
|
||||
}
|
||||
volumes, err := s.apiClient().VolumeList(ctx, opts)
|
||||
if err != nil {
|
||||
@@ -443,7 +440,7 @@ func (s *composeService) actualVolumes(ctx context.Context, projectName string)
|
||||
}
|
||||
|
||||
actual := types.Volumes{}
|
||||
for _, vol := range volumes.Volumes {
|
||||
for _, vol := range volumes.Items {
|
||||
actual[vol.Labels[api.VolumeLabel]] = types.VolumeConfig{
|
||||
Name: vol.Name,
|
||||
Driver: vol.Driver,
|
||||
@@ -454,15 +451,15 @@ func (s *composeService) actualVolumes(ctx context.Context, projectName string)
|
||||
}
|
||||
|
||||
func (s *composeService) actualNetworks(ctx context.Context, projectName string) (types.Networks, error) {
|
||||
networks, err := s.apiClient().NetworkList(ctx, network.ListOptions{
|
||||
Filters: filters.NewArgs(projectFilter(projectName)),
|
||||
networks, err := s.apiClient().NetworkList(ctx, client.NetworkListOptions{
|
||||
Filters: projectFilter(projectName),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
actual := types.Networks{}
|
||||
for _, net := range networks {
|
||||
for _, net := range networks.Items {
|
||||
actual[net.Labels[api.NetworkLabel]] = types.NetworkConfig{
|
||||
Name: net.Name,
|
||||
Driver: net.Driver,
|
||||
@@ -480,11 +477,11 @@ var swarmEnabled = struct {
|
||||
|
||||
func (s *composeService) isSwarmEnabled(ctx context.Context) (bool, error) {
|
||||
swarmEnabled.once.Do(func() {
|
||||
info, err := s.apiClient().Info(ctx)
|
||||
res, err := s.apiClient().Info(ctx, client.InfoOptions{})
|
||||
if err != nil {
|
||||
swarmEnabled.err = err
|
||||
}
|
||||
switch info.Swarm.LocalNodeState {
|
||||
switch res.Info.Swarm.LocalNodeState {
|
||||
case swarm.LocalNodeStateInactive, swarm.LocalNodeStateLocked:
|
||||
swarmEnabled.val = false
|
||||
default:
|
||||
@@ -503,8 +500,9 @@ type runtimeVersionCache struct {
|
||||
var runtimeVersion runtimeVersionCache
|
||||
|
||||
func (s *composeService) RuntimeVersion(ctx context.Context) (string, error) {
|
||||
// TODO(thaJeztah): this should use Client.ClientVersion), which has the negotiated version.
|
||||
runtimeVersion.once.Do(func() {
|
||||
version, err := s.apiClient().ServerVersion(ctx)
|
||||
version, err := s.apiClient().ServerVersion(ctx, client.ServerVersionOptions{})
|
||||
if err != nil {
|
||||
runtimeVersion.err = err
|
||||
}
|
||||
|
||||
@@ -19,14 +19,14 @@ package compose
|
||||
import (
|
||||
"io"
|
||||
|
||||
moby "github.com/docker/docker/api/types"
|
||||
"github.com/moby/moby/client"
|
||||
)
|
||||
|
||||
var _ io.ReadCloser = ContainerStdout{}
|
||||
|
||||
// ContainerStdout implement ReadCloser for moby.HijackedResponse
|
||||
type ContainerStdout struct {
|
||||
moby.HijackedResponse
|
||||
client.HijackedResponse
|
||||
}
|
||||
|
||||
// Read implement io.ReadCloser
|
||||
@@ -44,7 +44,7 @@ var _ io.WriteCloser = ContainerStdin{}
|
||||
|
||||
// ContainerStdin implement WriteCloser for moby.HijackedResponse
|
||||
type ContainerStdin struct {
|
||||
moby.HijackedResponse
|
||||
client.HijackedResponse
|
||||
}
|
||||
|
||||
// Write implement io.WriteCloser
|
||||
|
||||
@@ -24,8 +24,8 @@ import (
|
||||
"strconv"
|
||||
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
"github.com/moby/moby/api/types/container"
|
||||
"github.com/moby/moby/client"
|
||||
|
||||
"github.com/docker/compose/v5/pkg/api"
|
||||
)
|
||||
@@ -42,32 +42,31 @@ 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{
|
||||
Filters: filters.NewArgs(f...),
|
||||
res, err := s.apiClient().ContainerList(ctx, client.ContainerListOptions{
|
||||
Filters: getDefaultFilters(project, oneOff, selectedServices...),
|
||||
All: all,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
containers := Containers(res.Items)
|
||||
if len(selectedServices) > 1 {
|
||||
containers = containers.filter(isService(selectedServices...))
|
||||
}
|
||||
return containers, nil
|
||||
}
|
||||
|
||||
func getDefaultFilters(projectName string, oneOff oneOff, selectedServices ...string) []filters.KeyValuePair {
|
||||
f := []filters.KeyValuePair{projectFilter(projectName)}
|
||||
func getDefaultFilters(projectName string, oneOff oneOff, selectedServices ...string) client.Filters {
|
||||
f := projectFilter(projectName)
|
||||
if len(selectedServices) == 1 {
|
||||
f = append(f, serviceFilter(selectedServices[0]))
|
||||
f.Add("label", serviceFilter(selectedServices[0]))
|
||||
}
|
||||
f = append(f, hasConfigHashLabel())
|
||||
f.Add("label", hasConfigHashLabel())
|
||||
switch oneOff {
|
||||
case oneOffOnly:
|
||||
f = append(f, oneOffFilter(true))
|
||||
f.Add("label", oneOffFilter(true))
|
||||
case oneOffExclude:
|
||||
f = append(f, oneOffFilter(false))
|
||||
f.Add("label", oneOffFilter(false))
|
||||
case oneOffInclude:
|
||||
}
|
||||
return f
|
||||
@@ -76,17 +75,16 @@ func getDefaultFilters(projectName string, oneOff oneOff, selectedServices ...st
|
||||
func (s *composeService) getSpecifiedContainer(ctx context.Context, projectName string, oneOff oneOff, all bool, serviceName string, containerIndex int) (container.Summary, error) {
|
||||
defaultFilters := getDefaultFilters(projectName, oneOff, serviceName)
|
||||
if containerIndex > 0 {
|
||||
defaultFilters = append(defaultFilters, containerNumberFilter(containerIndex))
|
||||
defaultFilters.Add("label", containerNumberFilter(containerIndex))
|
||||
}
|
||||
containers, err := s.apiClient().ContainerList(ctx, container.ListOptions{
|
||||
Filters: filters.NewArgs(
|
||||
defaultFilters...,
|
||||
),
|
||||
All: all,
|
||||
res, err := s.apiClient().ContainerList(ctx, client.ContainerListOptions{
|
||||
Filters: defaultFilters,
|
||||
All: all,
|
||||
})
|
||||
if err != nil {
|
||||
return container.Summary{}, err
|
||||
}
|
||||
containers := res.Items
|
||||
if len(containers) < 1 {
|
||||
if containerIndex > 0 {
|
||||
return container.Summary{}, fmt.Errorf("service %q is not running container #%d", serviceName, containerIndex)
|
||||
|
||||
@@ -30,9 +30,9 @@ import (
|
||||
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
"github.com/containerd/platforms"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
mmount "github.com/docker/docker/api/types/mount"
|
||||
"github.com/docker/docker/api/types/versions"
|
||||
"github.com/moby/moby/api/types/container"
|
||||
mmount "github.com/moby/moby/api/types/mount"
|
||||
"github.com/moby/moby/client"
|
||||
specs "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/sirupsen/logrus"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
@@ -657,17 +657,19 @@ func (s *composeService) recreateContainer(ctx context.Context, project *types.P
|
||||
}
|
||||
|
||||
timeoutInSecond := utils.DurationSecondToInt(timeout)
|
||||
err = s.apiClient().ContainerStop(ctx, replaced.ID, container.StopOptions{Timeout: timeoutInSecond})
|
||||
_, err = s.apiClient().ContainerStop(ctx, replaced.ID, client.ContainerStopOptions{Timeout: timeoutInSecond})
|
||||
if err != nil {
|
||||
return created, err
|
||||
}
|
||||
|
||||
err = s.apiClient().ContainerRemove(ctx, replaced.ID, container.RemoveOptions{})
|
||||
_, err = s.apiClient().ContainerRemove(ctx, replaced.ID, client.ContainerRemoveOptions{})
|
||||
if err != nil {
|
||||
return created, err
|
||||
}
|
||||
|
||||
err = s.apiClient().ContainerRename(ctx, tmpName, name)
|
||||
_, err = s.apiClient().ContainerRename(ctx, tmpName, client.ContainerRenameOptions{
|
||||
NewName: name,
|
||||
})
|
||||
if err != nil {
|
||||
return created, err
|
||||
}
|
||||
@@ -683,7 +685,7 @@ func (s *composeService) startContainer(ctx context.Context, ctr container.Summa
|
||||
s.events.On(newEvent(getContainerProgressName(ctr), api.Working, "Restart"))
|
||||
startMx.Lock()
|
||||
defer startMx.Unlock()
|
||||
err := s.apiClient().ContainerStart(ctx, ctr.ID, container.StartOptions{})
|
||||
_, err := s.apiClient().ContainerStart(ctx, ctr.ID, client.ContainerStartOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -713,7 +715,13 @@ func (s *composeService) createMobyContainer(ctx context.Context, project *types
|
||||
plat = &p
|
||||
}
|
||||
|
||||
response, err := s.apiClient().ContainerCreate(ctx, cfgs.Container, cfgs.Host, cfgs.Network, plat, name)
|
||||
response, err := s.apiClient().ContainerCreate(ctx, client.ContainerCreateOptions{
|
||||
Name: name,
|
||||
Platform: plat,
|
||||
Config: cfgs.Container,
|
||||
HostConfig: cfgs.Host,
|
||||
NetworkingConfig: cfgs.Network,
|
||||
})
|
||||
if err != nil {
|
||||
return created, err
|
||||
}
|
||||
@@ -724,42 +732,19 @@ func (s *composeService) createMobyContainer(ctx context.Context, project *types
|
||||
Text: warning,
|
||||
})
|
||||
}
|
||||
inspectedContainer, err := s.apiClient().ContainerInspect(ctx, response.ID)
|
||||
res, err := s.apiClient().ContainerInspect(ctx, response.ID, client.ContainerInspectOptions{})
|
||||
if err != nil {
|
||||
return created, err
|
||||
}
|
||||
created = container.Summary{
|
||||
ID: inspectedContainer.ID,
|
||||
Labels: inspectedContainer.Config.Labels,
|
||||
Names: []string{inspectedContainer.Name},
|
||||
ID: res.Container.ID,
|
||||
Labels: res.Container.Config.Labels,
|
||||
Names: []string{res.Container.Name},
|
||||
NetworkSettings: &container.NetworkSettingsSummary{
|
||||
Networks: inspectedContainer.NetworkSettings.Networks,
|
||||
Networks: res.Container.NetworkSettings.Networks,
|
||||
},
|
||||
}
|
||||
|
||||
apiVersion, err := s.RuntimeVersion(ctx)
|
||||
if err != nil {
|
||||
return created, err
|
||||
}
|
||||
// Starting API version 1.44, the ContainerCreate API call takes multiple networks
|
||||
// so we include all the configurations there and can skip the one-by-one calls here
|
||||
if versions.LessThan(apiVersion, APIVersion144) {
|
||||
// the highest-priority network is the primary and is included in the ContainerCreate API
|
||||
// call via container.NetworkMode & network.NetworkingConfig
|
||||
// any remaining networks are connected one-by-one here after creation (but before start)
|
||||
serviceNetworks := service.NetworksByPriority()
|
||||
for _, networkKey := range serviceNetworks {
|
||||
mobyNetworkName := project.Networks[networkKey].Name
|
||||
if string(cfgs.Host.NetworkMode) == mobyNetworkName {
|
||||
// primary network already configured as part of ContainerCreate
|
||||
continue
|
||||
}
|
||||
epSettings := createEndpointSettings(project, service, number, networkKey, cfgs.Links, opts.UseNetworkAliases)
|
||||
if err := s.apiClient().NetworkConnect(ctx, mobyNetworkName, created.ID, epSettings); err != nil {
|
||||
return created, err
|
||||
}
|
||||
}
|
||||
}
|
||||
return created, nil
|
||||
}
|
||||
|
||||
@@ -774,11 +759,10 @@ func (s *composeService) getLinks(ctx context.Context, projectName string, servi
|
||||
}
|
||||
|
||||
for _, rawLink := range service.Links {
|
||||
linkSplit := strings.Split(rawLink, ":")
|
||||
linkServiceName := linkSplit[0]
|
||||
linkName := linkServiceName
|
||||
if len(linkSplit) == 2 {
|
||||
linkName = linkSplit[1] // linkName if informed like in: "serviceName:linkName"
|
||||
// linkName if informed like in: "serviceName[:linkName]"
|
||||
linkServiceName, linkName, ok := strings.Cut(rawLink, ":")
|
||||
if !ok {
|
||||
linkName = linkServiceName
|
||||
}
|
||||
cnts, err := getServiceContainers(linkServiceName)
|
||||
if err != nil {
|
||||
@@ -810,11 +794,9 @@ func (s *composeService) getLinks(ctx context.Context, projectName string, servi
|
||||
}
|
||||
|
||||
for _, rawExtLink := range service.ExternalLinks {
|
||||
extLinkSplit := strings.Split(rawExtLink, ":")
|
||||
externalLink := extLinkSplit[0]
|
||||
linkName := externalLink
|
||||
if len(extLinkSplit) == 2 {
|
||||
linkName = extLinkSplit[1]
|
||||
externalLink, linkName, ok := strings.Cut(rawExtLink, ":")
|
||||
if !ok {
|
||||
linkName = externalLink
|
||||
}
|
||||
links = append(links, format(externalLink, linkName))
|
||||
}
|
||||
@@ -823,10 +805,11 @@ func (s *composeService) getLinks(ctx context.Context, projectName string, servi
|
||||
|
||||
func (s *composeService) isServiceHealthy(ctx context.Context, containers Containers, fallbackRunning bool) (bool, error) {
|
||||
for _, c := range containers {
|
||||
ctr, err := s.apiClient().ContainerInspect(ctx, c.ID)
|
||||
res, err := s.apiClient().ContainerInspect(ctx, c.ID, client.ContainerInspectOptions{})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
ctr := res.Container
|
||||
name := ctr.Name[1:]
|
||||
|
||||
if ctr.State.Status == container.StateExited {
|
||||
@@ -858,12 +841,12 @@ func (s *composeService) isServiceHealthy(ctx context.Context, containers Contai
|
||||
|
||||
func (s *composeService) isServiceCompleted(ctx context.Context, containers Containers) (bool, int, error) {
|
||||
for _, c := range containers {
|
||||
ctr, err := s.apiClient().ContainerInspect(ctx, c.ID)
|
||||
res, err := s.apiClient().ContainerInspect(ctx, c.ID, client.ContainerInspectOptions{})
|
||||
if err != nil {
|
||||
return false, 0, err
|
||||
}
|
||||
if ctr.State != nil && ctr.State.Status == container.StateExited {
|
||||
return true, ctr.State.ExitCode, nil
|
||||
if res.Container.State != nil && res.Container.State.Status == container.StateExited {
|
||||
return true, res.Container.State.ExitCode, nil
|
||||
}
|
||||
}
|
||||
return false, 0, nil
|
||||
@@ -907,7 +890,7 @@ func (s *composeService) startService(ctx context.Context,
|
||||
|
||||
eventName := getContainerProgressName(ctr)
|
||||
s.events.On(startingEvent(eventName))
|
||||
err = s.apiClient().ContainerStart(ctx, ctr.ID, container.StartOptions{})
|
||||
_, err = s.apiClient().ContainerStart(ctx, ctr.ID, client.ContainerStartOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -17,18 +17,18 @@
|
||||
package compose
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/netip"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"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"
|
||||
"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"
|
||||
"github.com/google/go-cmp/cmp/cmpopts"
|
||||
"github.com/moby/moby/api/types/container"
|
||||
"github.com/moby/moby/api/types/network"
|
||||
"github.com/moby/moby/client"
|
||||
"go.uber.org/mock/gomock"
|
||||
"gotest.tools/v3/assert"
|
||||
|
||||
@@ -69,9 +69,8 @@ func TestServiceLinks(t *testing.T) {
|
||||
Scale: intPtr(1),
|
||||
}
|
||||
|
||||
containerListOptions := container.ListOptions{
|
||||
Filters: filters.NewArgs(
|
||||
projectFilter(testProject),
|
||||
containerListOptions := client.ContainerListOptions{
|
||||
Filters: projectFilter(testProject).Add("label",
|
||||
serviceFilter("db"),
|
||||
oneOffFilter(false),
|
||||
hasConfigHashLabel(),
|
||||
@@ -92,7 +91,9 @@ 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(client.ContainerListResult{
|
||||
Items: []container.Summary{c},
|
||||
}, nil)
|
||||
|
||||
links, err := tested.(*composeService).getLinks(t.Context(), testProject, s, 1)
|
||||
assert.NilError(t, err)
|
||||
@@ -116,7 +117,9 @@ 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(client.ContainerListResult{
|
||||
Items: []container.Summary{c},
|
||||
}, nil)
|
||||
links, err := tested.(*composeService).getLinks(t.Context(), testProject, s, 1)
|
||||
assert.NilError(t, err)
|
||||
|
||||
@@ -138,7 +141,9 @@ 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(client.ContainerListResult{
|
||||
Items: []container.Summary{c},
|
||||
}, nil)
|
||||
|
||||
links, err := tested.(*composeService).getLinks(t.Context(), testProject, s, 1)
|
||||
assert.NilError(t, err)
|
||||
@@ -162,7 +167,9 @@ 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(client.ContainerListResult{
|
||||
Items: []container.Summary{c},
|
||||
}, nil)
|
||||
|
||||
links, err := tested.(*composeService).getLinks(t.Context(), testProject, s, 1)
|
||||
assert.NilError(t, err)
|
||||
@@ -190,16 +197,17 @@ func TestServiceLinks(t *testing.T) {
|
||||
s.Labels = s.Labels.Add(api.OneoffLabel, "True")
|
||||
|
||||
c := testContainer("web", webContainerName, true)
|
||||
containerListOptionsOneOff := container.ListOptions{
|
||||
Filters: filters.NewArgs(
|
||||
projectFilter(testProject),
|
||||
containerListOptionsOneOff := client.ContainerListOptions{
|
||||
Filters: projectFilter(testProject).Add("label",
|
||||
serviceFilter("web"),
|
||||
oneOffFilter(false),
|
||||
hasConfigHashLabel(),
|
||||
),
|
||||
All: true,
|
||||
}
|
||||
apiClient.EXPECT().ContainerList(gomock.Any(), containerListOptionsOneOff).Return([]container.Summary{c}, nil)
|
||||
apiClient.EXPECT().ContainerList(gomock.Any(), containerListOptionsOneOff).Return(client.ContainerListResult{
|
||||
Items: []container.Summary{c},
|
||||
}, nil)
|
||||
|
||||
links, err := tested.(*composeService).getLinks(t.Context(), testProject, s, 1)
|
||||
assert.NilError(t, err)
|
||||
@@ -268,15 +276,15 @@ func TestIsServiceHealthy(t *testing.T) {
|
||||
}
|
||||
|
||||
// Container with disabled healthcheck (Test: ["NONE"])
|
||||
apiClient.EXPECT().ContainerInspect(ctx, containerID).Return(container.InspectResponse{
|
||||
ContainerJSONBase: &container.ContainerJSONBase{
|
||||
apiClient.EXPECT().ContainerInspect(ctx, containerID, gomock.Any()).Return(client.ContainerInspectResult{
|
||||
Container: container.InspectResponse{
|
||||
ID: containerID,
|
||||
Name: "test-container",
|
||||
State: &container.State{Status: "running"},
|
||||
},
|
||||
Config: &container.Config{
|
||||
Healthcheck: &container.HealthConfig{
|
||||
Test: []string{"NONE"},
|
||||
Config: &container.Config{
|
||||
Healthcheck: &container.HealthConfig{
|
||||
Test: []string{"NONE"},
|
||||
},
|
||||
},
|
||||
},
|
||||
}, nil)
|
||||
@@ -293,15 +301,15 @@ func TestIsServiceHealthy(t *testing.T) {
|
||||
}
|
||||
|
||||
// Container with disabled healthcheck (Test: ["NONE"]) but fallbackRunning=false
|
||||
apiClient.EXPECT().ContainerInspect(ctx, containerID).Return(container.InspectResponse{
|
||||
ContainerJSONBase: &container.ContainerJSONBase{
|
||||
apiClient.EXPECT().ContainerInspect(ctx, containerID, gomock.Any()).Return(client.ContainerInspectResult{
|
||||
Container: container.InspectResponse{
|
||||
ID: containerID,
|
||||
Name: "test-container",
|
||||
State: &container.State{Status: "running"},
|
||||
},
|
||||
Config: &container.Config{
|
||||
Healthcheck: &container.HealthConfig{
|
||||
Test: []string{"NONE"},
|
||||
Config: &container.Config{
|
||||
Healthcheck: &container.HealthConfig{
|
||||
Test: []string{"NONE"},
|
||||
},
|
||||
},
|
||||
},
|
||||
}, nil)
|
||||
@@ -317,14 +325,14 @@ func TestIsServiceHealthy(t *testing.T) {
|
||||
}
|
||||
|
||||
// Container with no healthcheck at all
|
||||
apiClient.EXPECT().ContainerInspect(ctx, containerID).Return(container.InspectResponse{
|
||||
ContainerJSONBase: &container.ContainerJSONBase{
|
||||
apiClient.EXPECT().ContainerInspect(ctx, containerID, gomock.Any()).Return(client.ContainerInspectResult{
|
||||
Container: container.InspectResponse{
|
||||
ID: containerID,
|
||||
Name: "test-container",
|
||||
State: &container.State{Status: "running"},
|
||||
},
|
||||
Config: &container.Config{
|
||||
Healthcheck: nil,
|
||||
Config: &container.Config{
|
||||
Healthcheck: nil,
|
||||
},
|
||||
},
|
||||
}, nil)
|
||||
|
||||
@@ -340,18 +348,18 @@ func TestIsServiceHealthy(t *testing.T) {
|
||||
}
|
||||
|
||||
// Container with disabled healthcheck but exited
|
||||
apiClient.EXPECT().ContainerInspect(ctx, containerID).Return(container.InspectResponse{
|
||||
ContainerJSONBase: &container.ContainerJSONBase{
|
||||
apiClient.EXPECT().ContainerInspect(ctx, containerID, gomock.Any()).Return(client.ContainerInspectResult{
|
||||
Container: container.InspectResponse{
|
||||
ID: containerID,
|
||||
Name: "test-container",
|
||||
State: &container.State{
|
||||
Status: "exited",
|
||||
ExitCode: 1,
|
||||
},
|
||||
},
|
||||
Config: &container.Config{
|
||||
Healthcheck: &container.HealthConfig{
|
||||
Test: []string{"NONE"},
|
||||
Config: &container.Config{
|
||||
Healthcheck: &container.HealthConfig{
|
||||
Test: []string{"NONE"},
|
||||
},
|
||||
},
|
||||
},
|
||||
}, nil)
|
||||
@@ -367,8 +375,8 @@ func TestIsServiceHealthy(t *testing.T) {
|
||||
}
|
||||
|
||||
// Container with actual healthcheck that is healthy
|
||||
apiClient.EXPECT().ContainerInspect(ctx, containerID).Return(container.InspectResponse{
|
||||
ContainerJSONBase: &container.ContainerJSONBase{
|
||||
apiClient.EXPECT().ContainerInspect(ctx, containerID, gomock.Any()).Return(client.ContainerInspectResult{
|
||||
Container: container.InspectResponse{
|
||||
ID: containerID,
|
||||
Name: "test-container",
|
||||
State: &container.State{
|
||||
@@ -377,10 +385,10 @@ func TestIsServiceHealthy(t *testing.T) {
|
||||
Status: container.Healthy,
|
||||
},
|
||||
},
|
||||
},
|
||||
Config: &container.Config{
|
||||
Healthcheck: &container.HealthConfig{
|
||||
Test: []string{"CMD", "curl", "-f", "http://localhost"},
|
||||
Config: &container.Config{
|
||||
Healthcheck: &container.HealthConfig{
|
||||
Test: []string{"CMD", "curl", "-f", "http://localhost"},
|
||||
},
|
||||
},
|
||||
},
|
||||
}, nil)
|
||||
@@ -392,177 +400,101 @@ func TestIsServiceHealthy(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateMobyContainer(t *testing.T) {
|
||||
t.Run("connects container networks one by one if API <1.44", func(t *testing.T) {
|
||||
mockCtrl := gomock.NewController(t)
|
||||
defer mockCtrl.Finish()
|
||||
apiClient := mocks.NewMockAPIClient(mockCtrl)
|
||||
cli := mocks.NewMockCli(mockCtrl)
|
||||
tested, err := NewComposeService(cli)
|
||||
assert.NilError(t, err)
|
||||
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()
|
||||
// force `RuntimeVersion` to fetch again
|
||||
runtimeVersion = runtimeVersionCache{}
|
||||
apiClient.EXPECT().ServerVersion(gomock.Any()).Return(moby.Version{
|
||||
APIVersion: "1.43",
|
||||
}, nil).AnyTimes()
|
||||
mockCtrl := gomock.NewController(t)
|
||||
defer mockCtrl.Finish()
|
||||
apiClient := mocks.NewMockAPIClient(mockCtrl)
|
||||
cli := mocks.NewMockCli(mockCtrl)
|
||||
tested, err := NewComposeService(cli)
|
||||
assert.NilError(t, err)
|
||||
cli.EXPECT().Client().Return(apiClient).AnyTimes()
|
||||
cli.EXPECT().ConfigFile().Return(&configfile.ConfigFile{}).AnyTimes()
|
||||
apiClient.EXPECT().DaemonHost().Return("").AnyTimes()
|
||||
apiClient.EXPECT().ImageInspect(anyCancellableContext(), gomock.Any()).Return(client.ImageInspectResult{}, nil).AnyTimes()
|
||||
|
||||
service := types.ServiceConfig{
|
||||
Name: "test",
|
||||
Networks: map[string]*types.ServiceNetworkConfig{
|
||||
"a": {
|
||||
Priority: 10,
|
||||
},
|
||||
"b": {
|
||||
Priority: 100,
|
||||
},
|
||||
// force `RuntimeVersion` to fetch fresh version
|
||||
runtimeVersion = runtimeVersionCache{}
|
||||
apiClient.EXPECT().ServerVersion(gomock.Any(), gomock.Any()).Return(client.ServerVersionResult{
|
||||
APIVersion: "1.44",
|
||||
}, nil).AnyTimes()
|
||||
|
||||
service := types.ServiceConfig{
|
||||
Name: "test",
|
||||
Networks: map[string]*types.ServiceNetworkConfig{
|
||||
"a": {
|
||||
Priority: 10,
|
||||
},
|
||||
}
|
||||
project := types.Project{
|
||||
Name: "bork",
|
||||
Services: types.Services{
|
||||
"test": service,
|
||||
"b": {
|
||||
Priority: 100,
|
||||
},
|
||||
Networks: types.Networks{
|
||||
"a": types.NetworkConfig{
|
||||
Name: "a-moby-name",
|
||||
},
|
||||
"b": types.NetworkConfig{
|
||||
Name: "b-moby-name",
|
||||
},
|
||||
},
|
||||
}
|
||||
project := types.Project{
|
||||
Name: "bork",
|
||||
Services: types.Services{
|
||||
"test": service,
|
||||
},
|
||||
Networks: types.Networks{
|
||||
"a": types.NetworkConfig{
|
||||
Name: "a-moby-name",
|
||||
},
|
||||
}
|
||||
"b": types.NetworkConfig{
|
||||
Name: "b-moby-name",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
var falseBool bool
|
||||
apiClient.EXPECT().ContainerCreate(gomock.Any(), gomock.Any(), gomock.Eq(
|
||||
&container.HostConfig{
|
||||
PortBindings: nat.PortMap{},
|
||||
ExtraHosts: []string{},
|
||||
Tmpfs: map[string]string{},
|
||||
Resources: container.Resources{
|
||||
OomKillDisable: &falseBool,
|
||||
},
|
||||
NetworkMode: "b-moby-name",
|
||||
}), gomock.Eq(
|
||||
&network.NetworkingConfig{
|
||||
EndpointsConfig: map[string]*network.EndpointSettings{
|
||||
"b-moby-name": {
|
||||
IPAMConfig: &network.EndpointIPAMConfig{},
|
||||
Aliases: []string{"bork-test-0"},
|
||||
},
|
||||
},
|
||||
}), gomock.Any(), gomock.Any()).Times(1).Return(
|
||||
container.CreateResponse{
|
||||
ID: "an-id",
|
||||
}, nil)
|
||||
|
||||
apiClient.EXPECT().ContainerInspect(gomock.Any(), gomock.Eq("an-id")).Times(1).Return(
|
||||
container.InspectResponse{
|
||||
ContainerJSONBase: &container.ContainerJSONBase{
|
||||
ID: "an-id",
|
||||
Name: "a-name",
|
||||
},
|
||||
Config: &container.Config{},
|
||||
NetworkSettings: &container.NetworkSettings{},
|
||||
}, nil)
|
||||
|
||||
apiClient.EXPECT().NetworkConnect(gomock.Any(), "a-moby-name", "an-id", gomock.Eq(
|
||||
&network.EndpointSettings{
|
||||
IPAMConfig: &network.EndpointIPAMConfig{},
|
||||
Aliases: []string{"bork-test-0"},
|
||||
}))
|
||||
|
||||
_, err = tested.(*composeService).createMobyContainer(t.Context(), &project, service, "test", 0, nil, createOptions{
|
||||
Labels: make(types.Labels),
|
||||
})
|
||||
assert.NilError(t, err)
|
||||
var got client.ContainerCreateOptions
|
||||
apiClient.EXPECT().ContainerCreate(gomock.Any(), gomock.Any()).DoAndReturn(func(_ context.Context, opts client.ContainerCreateOptions) (client.ContainerCreateResult, error) {
|
||||
got = opts
|
||||
return client.ContainerCreateResult{ID: "an-id"}, nil
|
||||
})
|
||||
|
||||
t.Run("includes all container networks in ContainerCreate call if API >=1.44", func(t *testing.T) {
|
||||
mockCtrl := gomock.NewController(t)
|
||||
defer mockCtrl.Finish()
|
||||
apiClient := mocks.NewMockAPIClient(mockCtrl)
|
||||
cli := mocks.NewMockCli(mockCtrl)
|
||||
tested, err := NewComposeService(cli)
|
||||
assert.NilError(t, err)
|
||||
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()
|
||||
// force `RuntimeVersion` to fetch fresh version
|
||||
runtimeVersion = runtimeVersionCache{}
|
||||
apiClient.EXPECT().ServerVersion(gomock.Any()).Return(moby.Version{
|
||||
APIVersion: APIVersion144,
|
||||
}, nil).AnyTimes()
|
||||
apiClient.EXPECT().ContainerInspect(gomock.Any(), gomock.Eq("an-id"), gomock.Any()).Times(1).Return(client.ContainerInspectResult{
|
||||
Container: container.InspectResponse{
|
||||
ID: "an-id",
|
||||
Name: "a-name",
|
||||
Config: &container.Config{},
|
||||
NetworkSettings: &container.NetworkSettings{},
|
||||
},
|
||||
}, nil)
|
||||
|
||||
service := types.ServiceConfig{
|
||||
Name: "test",
|
||||
Networks: map[string]*types.ServiceNetworkConfig{
|
||||
"a": {
|
||||
Priority: 10,
|
||||
},
|
||||
"b": {
|
||||
Priority: 100,
|
||||
},
|
||||
},
|
||||
}
|
||||
project := types.Project{
|
||||
Name: "bork",
|
||||
Services: types.Services{
|
||||
"test": service,
|
||||
},
|
||||
Networks: types.Networks{
|
||||
"a": types.NetworkConfig{
|
||||
Name: "a-moby-name",
|
||||
},
|
||||
"b": types.NetworkConfig{
|
||||
Name: "b-moby-name",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
var falseBool bool
|
||||
apiClient.EXPECT().ContainerCreate(gomock.Any(), gomock.Any(), gomock.Eq(
|
||||
&container.HostConfig{
|
||||
PortBindings: nat.PortMap{},
|
||||
ExtraHosts: []string{},
|
||||
Tmpfs: map[string]string{},
|
||||
Resources: container.Resources{
|
||||
OomKillDisable: &falseBool,
|
||||
},
|
||||
NetworkMode: "b-moby-name",
|
||||
}), gomock.Eq(
|
||||
&network.NetworkingConfig{
|
||||
EndpointsConfig: map[string]*network.EndpointSettings{
|
||||
"a-moby-name": {
|
||||
IPAMConfig: &network.EndpointIPAMConfig{},
|
||||
Aliases: []string{"bork-test-0"},
|
||||
},
|
||||
"b-moby-name": {
|
||||
IPAMConfig: &network.EndpointIPAMConfig{},
|
||||
Aliases: []string{"bork-test-0"},
|
||||
},
|
||||
},
|
||||
}), gomock.Any(), gomock.Any()).Times(1).Return(
|
||||
container.CreateResponse{
|
||||
ID: "an-id",
|
||||
}, nil)
|
||||
|
||||
apiClient.EXPECT().ContainerInspect(gomock.Any(), gomock.Eq("an-id")).Times(1).Return(
|
||||
container.InspectResponse{
|
||||
ContainerJSONBase: &container.ContainerJSONBase{
|
||||
ID: "an-id",
|
||||
Name: "a-name",
|
||||
},
|
||||
Config: &container.Config{},
|
||||
NetworkSettings: &container.NetworkSettings{},
|
||||
}, nil)
|
||||
|
||||
_, err = tested.(*composeService).createMobyContainer(t.Context(), &project, service, "test", 0, nil, createOptions{
|
||||
Labels: make(types.Labels),
|
||||
})
|
||||
assert.NilError(t, err)
|
||||
_, err = tested.(*composeService).createMobyContainer(t.Context(), &project, service, "test", 0, nil, createOptions{
|
||||
Labels: make(types.Labels),
|
||||
})
|
||||
var falseBool bool
|
||||
want := client.ContainerCreateOptions{
|
||||
Config: &container.Config{
|
||||
AttachStdout: true,
|
||||
AttachStderr: true,
|
||||
Image: "bork-test",
|
||||
Labels: map[string]string{
|
||||
"com.docker.compose.config-hash": "8dbce408396f8986266bc5deba0c09cfebac63c95c2238e405c7bee5f1bd84b8",
|
||||
"com.docker.compose.depends_on": "",
|
||||
},
|
||||
},
|
||||
HostConfig: &container.HostConfig{
|
||||
PortBindings: network.PortMap{},
|
||||
ExtraHosts: []string{},
|
||||
Tmpfs: map[string]string{},
|
||||
Resources: container.Resources{
|
||||
OomKillDisable: &falseBool,
|
||||
},
|
||||
NetworkMode: "b-moby-name",
|
||||
},
|
||||
NetworkingConfig: &network.NetworkingConfig{
|
||||
EndpointsConfig: map[string]*network.EndpointSettings{
|
||||
"a-moby-name": {
|
||||
IPAMConfig: &network.EndpointIPAMConfig{},
|
||||
Aliases: []string{"bork-test-0"},
|
||||
},
|
||||
"b-moby-name": {
|
||||
IPAMConfig: &network.EndpointIPAMConfig{},
|
||||
Aliases: []string{"bork-test-0"},
|
||||
},
|
||||
},
|
||||
},
|
||||
Name: "test",
|
||||
}
|
||||
assert.DeepEqual(t, want, got, cmpopts.EquateComparable(netip.Addr{}), cmpopts.EquateEmpty())
|
||||
assert.NilError(t, err)
|
||||
}
|
||||
|
||||
@@ -23,8 +23,7 @@ import (
|
||||
"time"
|
||||
|
||||
compose "github.com/compose-spec/compose-go/v2/types"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/versions"
|
||||
"github.com/moby/moby/api/types/container"
|
||||
)
|
||||
|
||||
// ToMobyEnv convert into []string
|
||||
@@ -69,15 +68,7 @@ func (s *composeService) ToMobyHealthCheck(ctx context.Context, check *compose.H
|
||||
}
|
||||
var startInterval time.Duration
|
||||
if check.StartInterval != nil {
|
||||
version, err := s.RuntimeVersion(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if versions.LessThan(version, APIVersion144) {
|
||||
return nil, fmt.Errorf("can't set healthcheck.start_interval as feature require Docker Engine %s or later", DockerEngineV25)
|
||||
} else {
|
||||
startInterval = time.Duration(*check.StartInterval)
|
||||
}
|
||||
startInterval = time.Duration(*check.StartInterval)
|
||||
if check.StartPeriod == nil {
|
||||
// see https://github.com/moby/moby/issues/48874
|
||||
return nil, errors.New("healthcheck.start_interval requires healthcheck.start_period to be set")
|
||||
|
||||
@@ -26,8 +26,9 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/moby/go-archive"
|
||||
"github.com/moby/moby/api/types/container"
|
||||
"github.com/moby/moby/client"
|
||||
"golang.org/x/sync/errgroup"
|
||||
|
||||
"github.com/docker/compose/v5/pkg/api"
|
||||
@@ -153,7 +154,13 @@ func (s *composeService) copyToContainer(ctx context.Context, containerID string
|
||||
|
||||
// Prepare destination copy info by stat-ing the container path.
|
||||
dstInfo := archive.CopyInfo{Path: dstPath}
|
||||
dstStat, err := s.apiClient().ContainerStatPath(ctx, containerID, dstPath)
|
||||
var dstStat container.PathStat
|
||||
res, err := s.apiClient().ContainerStatPath(ctx, containerID, client.ContainerStatPathOptions{
|
||||
Path: dstPath,
|
||||
})
|
||||
if err == nil {
|
||||
dstStat = res.Stat
|
||||
}
|
||||
|
||||
// If the destination is a symbolic link, we should evaluate it.
|
||||
if err == nil && dstStat.Mode&os.ModeSymlink != 0 {
|
||||
@@ -165,7 +172,12 @@ func (s *composeService) copyToContainer(ctx context.Context, containerID string
|
||||
}
|
||||
|
||||
dstInfo.Path = linkTarget
|
||||
dstStat, err = s.apiClient().ContainerStatPath(ctx, containerID, linkTarget)
|
||||
res, err = s.apiClient().ContainerStatPath(ctx, containerID, client.ContainerStatPathOptions{
|
||||
Path: linkTarget,
|
||||
})
|
||||
if err == nil {
|
||||
dstStat = res.Stat
|
||||
}
|
||||
}
|
||||
|
||||
// Validate the destination path
|
||||
@@ -232,11 +244,13 @@ func (s *composeService) copyToContainer(ctx context.Context, containerID string
|
||||
}
|
||||
}
|
||||
|
||||
options := container.CopyToContainerOptions{
|
||||
_, err = s.apiClient().CopyToContainer(ctx, containerID, client.CopyToContainerOptions{
|
||||
DestinationPath: resolvedDstPath,
|
||||
Content: content,
|
||||
AllowOverwriteDirWithFile: false,
|
||||
CopyUIDGID: opts.CopyUIDGID,
|
||||
}
|
||||
return s.apiClient().CopyToContainer(ctx, containerID, resolvedDstPath, content, options)
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *composeService) copyFromContainer(ctx context.Context, containerID, srcPath, dstPath string, opts api.CopyOptions) error {
|
||||
@@ -256,7 +270,13 @@ func (s *composeService) copyFromContainer(ctx context.Context, containerID, src
|
||||
// if client requests to follow symbol link, then must decide target file to be copied
|
||||
var rebaseName string
|
||||
if opts.FollowLink {
|
||||
srcStat, err := s.apiClient().ContainerStatPath(ctx, containerID, srcPath)
|
||||
var srcStat container.PathStat
|
||||
res, err := s.apiClient().ContainerStatPath(ctx, containerID, client.ContainerStatPathOptions{
|
||||
Path: srcPath,
|
||||
})
|
||||
if err == nil {
|
||||
srcStat = res.Stat
|
||||
}
|
||||
|
||||
// If the destination is a symbolic link, we should follow it.
|
||||
if err == nil && srcStat.Mode&os.ModeSymlink != 0 {
|
||||
@@ -272,28 +292,30 @@ func (s *composeService) copyFromContainer(ctx context.Context, containerID, src
|
||||
}
|
||||
}
|
||||
|
||||
content, stat, err := s.apiClient().CopyFromContainer(ctx, containerID, srcPath)
|
||||
res, err := s.apiClient().CopyFromContainer(ctx, containerID, client.CopyFromContainerOptions{
|
||||
SourcePath: srcPath,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer content.Close() //nolint:errcheck
|
||||
defer res.Content.Close() //nolint:errcheck
|
||||
|
||||
if dstPath == "-" {
|
||||
_, err = io.Copy(s.stdout(), content)
|
||||
_, err = io.Copy(s.stdout(), res.Content)
|
||||
return err
|
||||
}
|
||||
|
||||
srcInfo := archive.CopyInfo{
|
||||
Path: srcPath,
|
||||
Exists: true,
|
||||
IsDir: stat.Mode.IsDir(),
|
||||
IsDir: res.Stat.Mode.IsDir(),
|
||||
RebaseName: rebaseName,
|
||||
}
|
||||
|
||||
preArchive := content
|
||||
preArchive := res.Content
|
||||
if srcInfo.RebaseName != "" {
|
||||
_, srcBase := archive.SplitPathDirEntry(srcInfo.Path)
|
||||
preArchive = archive.RebaseArchiveEntries(content, srcBase, srcInfo.RebaseName)
|
||||
preArchive = archive.RebaseArchiveEntries(res.Content, srcBase, srcInfo.RebaseName)
|
||||
}
|
||||
|
||||
return archive.CopyTo(preArchive, srcInfo, dstPath)
|
||||
@@ -317,15 +339,15 @@ func splitCpArg(arg string) (ctr, path string) {
|
||||
return "", arg
|
||||
}
|
||||
|
||||
parts := strings.SplitN(arg, ":", 2)
|
||||
ctr, path, ok := strings.Cut(arg, ":")
|
||||
|
||||
if len(parts) == 1 || strings.HasPrefix(parts[0], ".") {
|
||||
if !ok || strings.HasPrefix(ctr, ".") {
|
||||
// Either there's no `:` in the arg
|
||||
// OR it's an explicit local relative path like `./file:name.txt`.
|
||||
return "", arg
|
||||
}
|
||||
|
||||
return parts[0], parts[1]
|
||||
return ctr, path
|
||||
}
|
||||
|
||||
func resolveLocalPath(localPath string) (absPath string, err error) {
|
||||
|
||||
@@ -22,6 +22,8 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/netip"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
@@ -31,14 +33,12 @@ import (
|
||||
"github.com/compose-spec/compose-go/v2/paths"
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
"github.com/containerd/errdefs"
|
||||
"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/mount"
|
||||
"github.com/docker/docker/api/types/network"
|
||||
"github.com/docker/docker/api/types/versions"
|
||||
volumetypes "github.com/docker/docker/api/types/volume"
|
||||
"github.com/docker/go-connections/nat"
|
||||
"github.com/moby/moby/api/types/blkiodev"
|
||||
"github.com/moby/moby/api/types/container"
|
||||
"github.com/moby/moby/api/types/mount"
|
||||
"github.com/moby/moby/api/types/network"
|
||||
"github.com/moby/moby/client"
|
||||
"github.com/moby/moby/client/pkg/versions"
|
||||
"github.com/sirupsen/logrus"
|
||||
cdi "tags.cncf.io/container-device-interface/pkg/parser"
|
||||
|
||||
@@ -201,8 +201,7 @@ func (s *composeService) getCreateConfigs(ctx context.Context,
|
||||
mainNw = service.Networks[mainNwName]
|
||||
}
|
||||
|
||||
macAddress, err := s.prepareContainerMACAddress(ctx, service, mainNw, mainNwName)
|
||||
if err != nil {
|
||||
if err := s.prepareContainerMACAddress(service, mainNw, mainNwName); err != nil {
|
||||
return createConfigs{}, err
|
||||
}
|
||||
|
||||
@@ -211,7 +210,7 @@ func (s *composeService) getCreateConfigs(ctx context.Context,
|
||||
return createConfigs{}, err
|
||||
}
|
||||
|
||||
exposed, err := buildContainerPorts(service)
|
||||
exposedPorts, err := buildContainerPorts(service)
|
||||
if err != nil {
|
||||
return createConfigs{}, err
|
||||
}
|
||||
@@ -220,7 +219,7 @@ func (s *composeService) getCreateConfigs(ctx context.Context,
|
||||
Hostname: service.Hostname,
|
||||
Domainname: service.DomainName,
|
||||
User: service.User,
|
||||
ExposedPorts: exposed,
|
||||
ExposedPorts: exposedPorts,
|
||||
Tty: tty,
|
||||
OpenStdin: stdinOpen,
|
||||
StdinOnce: opts.AttachStdin && stdinOpen,
|
||||
@@ -232,7 +231,6 @@ 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.
|
||||
Labels: labels,
|
||||
StopSignal: service.StopSignal,
|
||||
Env: ToMobyEnv(env),
|
||||
@@ -241,11 +239,8 @@ func (s *composeService) getCreateConfigs(ctx context.Context,
|
||||
} // VOLUMES/MOUNTS/FILESYSTEMS
|
||||
tmpfs := map[string]string{}
|
||||
for _, t := range service.Tmpfs {
|
||||
if arr := strings.SplitN(t, ":", 2); len(arr) > 1 {
|
||||
tmpfs[arr[0]] = arr[1]
|
||||
} else {
|
||||
tmpfs[arr[0]] = ""
|
||||
}
|
||||
k, v, _ := strings.Cut(t, ":")
|
||||
tmpfs[k] = v
|
||||
}
|
||||
binds, mounts, err := s.buildContainerVolumes(ctx, *p, service, inherit)
|
||||
if err != nil {
|
||||
@@ -265,7 +260,10 @@ func (s *composeService) getCreateConfigs(ctx context.Context,
|
||||
if err != nil {
|
||||
return createConfigs{}, err
|
||||
}
|
||||
portBindings := buildContainerPortBindingOptions(service)
|
||||
portBindings, err := buildContainerPortBindingOptions(service)
|
||||
if err != nil {
|
||||
return createConfigs{}, err
|
||||
}
|
||||
|
||||
// MISC
|
||||
resources := getDeployResources(service)
|
||||
@@ -281,6 +279,15 @@ func (s *composeService) getCreateConfigs(ctx context.Context,
|
||||
return createConfigs{}, err
|
||||
}
|
||||
|
||||
var dnsIPs []netip.Addr
|
||||
for _, d := range service.DNS {
|
||||
dnsIP, err := netip.ParseAddr(d)
|
||||
if err != nil {
|
||||
return createConfigs{}, fmt.Errorf("invalid DNS address: %w", err)
|
||||
}
|
||||
dnsIPs = append(dnsIPs, dnsIP)
|
||||
}
|
||||
|
||||
hostConfig := container.HostConfig{
|
||||
AutoRemove: opts.AutoRemove,
|
||||
Annotations: service.Annotations,
|
||||
@@ -300,7 +307,7 @@ func (s *composeService) getCreateConfigs(ctx context.Context,
|
||||
Resources: resources,
|
||||
VolumeDriver: service.VolumeDriver,
|
||||
VolumesFrom: service.VolumesFrom,
|
||||
DNS: service.DNS,
|
||||
DNS: dnsIPs,
|
||||
DNSSearch: service.DNSSearch,
|
||||
DNSOptions: service.DNSOpts,
|
||||
ExtraHosts: service.ExtraHosts.AsList(":"),
|
||||
@@ -339,12 +346,7 @@ func (s *composeService) getCreateConfigs(ctx context.Context,
|
||||
// passed mainNw to provide backward-compatibility whenever possible.
|
||||
//
|
||||
// It returns the container-wide MAC address, but this value will be kept empty for newer API versions.
|
||||
func (s *composeService) prepareContainerMACAddress(ctx context.Context, service types.ServiceConfig, mainNw *types.ServiceNetworkConfig, nwName string) (string, error) {
|
||||
version, err := s.RuntimeVersion(ctx)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
func (s *composeService) prepareContainerMACAddress(service types.ServiceConfig, mainNw *types.ServiceNetworkConfig, nwName string) error {
|
||||
// Engine API 1.44 added support for endpoint-specific MAC address and now returns a warning when a MAC address is
|
||||
// set in container.Config. Thus, we have to jump through a number of hoops:
|
||||
//
|
||||
@@ -358,31 +360,12 @@ func (s *composeService) prepareContainerMACAddress(ctx context.Context, service
|
||||
// there's no need to check for API version in defaultNetworkSettings.
|
||||
macAddress := service.MacAddress
|
||||
if macAddress != "" && mainNw != nil && mainNw.MacAddress != "" && mainNw.MacAddress != macAddress {
|
||||
return "", fmt.Errorf("the service-level mac_address should have the same value as network %s", nwName)
|
||||
return fmt.Errorf("the service-level mac_address should have the same value as network %s", nwName)
|
||||
}
|
||||
if versions.GreaterThanOrEqualTo(version, APIVersion144) {
|
||||
if mainNw != nil && mainNw.MacAddress == "" {
|
||||
mainNw.MacAddress = macAddress
|
||||
}
|
||||
macAddress = ""
|
||||
} else if len(service.Networks) > 0 {
|
||||
var withMacAddress []string
|
||||
for nwName, nw := range service.Networks {
|
||||
if nw != nil && nw.MacAddress != "" {
|
||||
withMacAddress = append(withMacAddress, nwName)
|
||||
}
|
||||
}
|
||||
|
||||
if len(withMacAddress) > 1 {
|
||||
return "", fmt.Errorf("a MAC address is specified for multiple networks (%s), but this feature requires Docker Engine %s or later", strings.Join(withMacAddress, ", "), DockerEngineV25)
|
||||
}
|
||||
|
||||
if mainNw != nil && mainNw.MacAddress != "" {
|
||||
macAddress = mainNw.MacAddress
|
||||
}
|
||||
if mainNw != nil && mainNw.MacAddress == "" {
|
||||
mainNw.MacAddress = macAddress
|
||||
}
|
||||
|
||||
return macAddress, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func getAliases(project *types.Project, service types.ServiceConfig, serviceIndex int, cfg *types.ServiceNetworkConfig, useNetworkAliases bool) []string {
|
||||
@@ -396,25 +379,48 @@ func getAliases(project *types.Project, service types.ServiceConfig, serviceInde
|
||||
return aliases
|
||||
}
|
||||
|
||||
func createEndpointSettings(p *types.Project, service types.ServiceConfig, serviceIndex int, networkKey string, links []string, useNetworkAliases bool) *network.EndpointSettings {
|
||||
func createEndpointSettings(p *types.Project, service types.ServiceConfig, serviceIndex int, networkKey string, links []string, useNetworkAliases bool) (*network.EndpointSettings, error) {
|
||||
const ifname = "com.docker.network.endpoint.ifname"
|
||||
|
||||
config := service.Networks[networkKey]
|
||||
var ipam *network.EndpointIPAMConfig
|
||||
var (
|
||||
ipv4Address string
|
||||
ipv6Address string
|
||||
ipv4Address netip.Addr
|
||||
ipv6Address netip.Addr
|
||||
macAddress string
|
||||
driverOpts types.Options
|
||||
gwPriority int
|
||||
)
|
||||
if config != nil {
|
||||
ipv4Address = config.Ipv4Address
|
||||
ipv6Address = config.Ipv6Address
|
||||
var err error
|
||||
if config.Ipv4Address != "" {
|
||||
ipv4Address, err = netip.ParseAddr(config.Ipv4Address)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid IPv4 address: %w", err)
|
||||
}
|
||||
}
|
||||
if config.Ipv6Address != "" {
|
||||
ipv6Address, err = netip.ParseAddr(config.Ipv6Address)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid IPv6 address: %w", err)
|
||||
}
|
||||
}
|
||||
var linkLocalIPs []netip.Addr
|
||||
for _, link := range config.LinkLocalIPs {
|
||||
if link == "" {
|
||||
continue
|
||||
}
|
||||
llIP, err := netip.ParseAddr(link)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid link-local IP: %w", err)
|
||||
}
|
||||
linkLocalIPs = append(linkLocalIPs, llIP)
|
||||
}
|
||||
|
||||
ipam = &network.EndpointIPAMConfig{
|
||||
IPv4Address: ipv4Address,
|
||||
IPv4Address: ipv4Address.Unmap(),
|
||||
IPv6Address: ipv6Address,
|
||||
LinkLocalIPs: config.LinkLocalIPs,
|
||||
LinkLocalIPs: linkLocalIPs,
|
||||
}
|
||||
macAddress = config.MacAddress
|
||||
driverOpts = config.DriverOpts
|
||||
@@ -429,16 +435,25 @@ func createEndpointSettings(p *types.Project, service types.ServiceConfig, servi
|
||||
}
|
||||
gwPriority = config.GatewayPriority
|
||||
}
|
||||
var ma network.HardwareAddr
|
||||
if macAddress != "" {
|
||||
var err error
|
||||
ma, err = parseMACAddr(macAddress)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return &network.EndpointSettings{
|
||||
Aliases: getAliases(p, service, serviceIndex, config, useNetworkAliases),
|
||||
Links: links,
|
||||
IPAddress: ipv4Address,
|
||||
IPv6Gateway: ipv6Address,
|
||||
IPAMConfig: ipam,
|
||||
MacAddress: macAddress,
|
||||
MacAddress: ma,
|
||||
DriverOpts: driverOpts,
|
||||
GwPriority: gwPriority,
|
||||
}
|
||||
}, nil
|
||||
}
|
||||
|
||||
// copy/pasted from https://github.com/docker/cli/blob/9de1b162f/cli/command/container/opts.go#L673-L697 + RelativePath
|
||||
@@ -510,38 +525,10 @@ func defaultNetworkSettings(project *types.Project,
|
||||
}
|
||||
|
||||
if len(project.Networks) == 0 {
|
||||
return "none", nil, nil
|
||||
return network.NetworkNone, nil, nil
|
||||
}
|
||||
|
||||
var primaryNetworkKey string
|
||||
if len(service.Networks) > 0 {
|
||||
primaryNetworkKey = service.NetworksByPriority()[0]
|
||||
} else {
|
||||
primaryNetworkKey = "default"
|
||||
}
|
||||
primaryNetworkMobyNetworkName := project.Networks[primaryNetworkKey].Name
|
||||
primaryNetworkEndpoint := createEndpointSettings(project, service, serviceIndex, primaryNetworkKey, links, useNetworkAliases)
|
||||
endpointsConfig := map[string]*network.EndpointSettings{}
|
||||
|
||||
// Starting from API version 1.44, the Engine will take several EndpointsConfigs
|
||||
// so we can pass all the extra networks we want the container to be connected to
|
||||
// in the network configuration instead of connecting the container to each extra
|
||||
// network individually after creation.
|
||||
if versions.GreaterThanOrEqualTo(version, APIVersion144) {
|
||||
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.LessThan(version, APIVersion149) {
|
||||
if versions.LessThan(version, apiVersion149) {
|
||||
for _, config := range service.Networks {
|
||||
if config != nil && config.InterfaceName != "" {
|
||||
return "", nil, fmt.Errorf("interface_name requires Docker Engine %s or later", DockerEngineV28_1)
|
||||
@@ -549,7 +536,42 @@ func defaultNetworkSettings(project *types.Project,
|
||||
}
|
||||
}
|
||||
|
||||
endpointsConfig[primaryNetworkMobyNetworkName] = primaryNetworkEndpoint
|
||||
serviceNetworks := service.NetworksByPriority()
|
||||
primaryNetworkKey := "default"
|
||||
if len(serviceNetworks) > 0 {
|
||||
primaryNetworkKey = serviceNetworks[0]
|
||||
serviceNetworks = serviceNetworks[1:]
|
||||
}
|
||||
|
||||
primaryNetworkEndpoint, err := createEndpointSettings(project, service, serviceIndex, primaryNetworkKey, links, useNetworkAliases)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
if primaryNetworkEndpoint.MacAddress.String() == "" {
|
||||
primaryNetworkEndpoint.MacAddress, err = parseMACAddr(service.MacAddress)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
}
|
||||
|
||||
primaryNetworkMobyNetworkName := project.Networks[primaryNetworkKey].Name
|
||||
endpointsConfig := map[string]*network.EndpointSettings{
|
||||
primaryNetworkMobyNetworkName: primaryNetworkEndpoint,
|
||||
}
|
||||
|
||||
// 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.
|
||||
for _, networkKey := range serviceNetworks {
|
||||
epSettings, err := createEndpointSettings(project, service, serviceIndex, networkKey, links, useNetworkAliases)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
mobyNetworkName := project.Networks[networkKey].Name
|
||||
endpointsConfig[mobyNetworkName] = epSettings
|
||||
}
|
||||
|
||||
networkConfig := &network.NetworkingConfig{
|
||||
EndpointsConfig: endpointsConfig,
|
||||
}
|
||||
@@ -563,13 +585,13 @@ func defaultNetworkSettings(project *types.Project,
|
||||
func getRestartPolicy(service types.ServiceConfig) container.RestartPolicy {
|
||||
var restart container.RestartPolicy
|
||||
if service.Restart != "" {
|
||||
split := strings.Split(service.Restart, ":")
|
||||
name, num, ok := strings.Cut(service.Restart, ":")
|
||||
var attempts int
|
||||
if len(split) > 1 {
|
||||
attempts, _ = strconv.Atoi(split[1])
|
||||
if ok {
|
||||
attempts, _ = strconv.Atoi(num)
|
||||
}
|
||||
restart = container.RestartPolicy{
|
||||
Name: mapRestartPolicyCondition(split[0]),
|
||||
Name: mapRestartPolicyCondition(name),
|
||||
MaximumRetryCount: attempts,
|
||||
}
|
||||
}
|
||||
@@ -769,37 +791,55 @@ func setBlkio(blkio *types.BlkioConfig, resources *container.Resources) {
|
||||
}
|
||||
}
|
||||
|
||||
func buildContainerPorts(s types.ServiceConfig) (nat.PortSet, error) {
|
||||
ports := nat.PortSet{}
|
||||
for _, s := range s.Expose {
|
||||
proto, port := nat.SplitProtoPort(s)
|
||||
start, end, err := nat.ParsePortRange(port)
|
||||
func buildContainerPorts(s types.ServiceConfig) (network.PortSet, error) {
|
||||
// Add published ports as exposed ports.
|
||||
exposedPorts := network.PortSet{}
|
||||
for _, p := range s.Ports {
|
||||
np, err := network.ParsePort(fmt.Sprintf("%d/%s", p.Target, p.Protocol))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for i := start; i <= end; i++ {
|
||||
p := nat.Port(fmt.Sprintf("%d/%s", i, proto))
|
||||
ports[p] = struct{}{}
|
||||
exposedPorts[np] = struct{}{}
|
||||
}
|
||||
|
||||
// Merge in exposed ports to the map of published ports
|
||||
for _, e := range s.Expose {
|
||||
// support two formats for expose, original format <portnum>/[<proto>]
|
||||
// or <startport-endport>/[<proto>]
|
||||
pr, err := network.ParsePortRange(e)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// parse the start and end port and create a sequence of ports to expose
|
||||
// if expose a port, the start and end port are the same
|
||||
for p := range pr.All() {
|
||||
exposedPorts[p] = struct{}{}
|
||||
}
|
||||
}
|
||||
for _, p := range s.Ports {
|
||||
p := nat.Port(fmt.Sprintf("%d/%s", p.Target, p.Protocol))
|
||||
ports[p] = struct{}{}
|
||||
}
|
||||
return ports, nil
|
||||
return exposedPorts, nil
|
||||
}
|
||||
|
||||
func buildContainerPortBindingOptions(s types.ServiceConfig) nat.PortMap {
|
||||
bindings := nat.PortMap{}
|
||||
func buildContainerPortBindingOptions(s types.ServiceConfig) (network.PortMap, error) {
|
||||
bindings := network.PortMap{}
|
||||
for _, port := range s.Ports {
|
||||
p := nat.Port(fmt.Sprintf("%d/%s", port.Target, port.Protocol))
|
||||
binding := nat.PortBinding{
|
||||
HostIP: port.HostIP,
|
||||
HostPort: port.Published,
|
||||
var err error
|
||||
p, err := network.ParsePort(fmt.Sprintf("%d/%s", port.Target, port.Protocol))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
bindings[p] = append(bindings[p], binding)
|
||||
var hostIP netip.Addr
|
||||
if port.HostIP != "" {
|
||||
hostIP, err = netip.ParseAddr(port.HostIP)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
bindings[p] = append(bindings[p], network.PortBinding{
|
||||
HostIP: hostIP,
|
||||
HostPort: port.Published,
|
||||
})
|
||||
}
|
||||
return bindings
|
||||
return bindings, nil
|
||||
}
|
||||
|
||||
func getDependentServiceFromMode(mode string) string {
|
||||
@@ -861,8 +901,8 @@ func (s *composeService) buildContainerVolumes(
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if versions.LessThan(version, APIVersion148) {
|
||||
return nil, nil, fmt.Errorf("volume with type=image require Docker Engine %s or later", DockerEngineV28)
|
||||
if versions.LessThan(version, apiVersion148) {
|
||||
return nil, nil, fmt.Errorf("volume with type=image require Docker Engine %s or later", dockerEngineV28)
|
||||
}
|
||||
}
|
||||
mounts = append(mounts, m)
|
||||
@@ -1286,8 +1326,9 @@ func (s *composeService) resolveOrCreateNetwork(ctx context.Context, project *ty
|
||||
var dangledContainers Containers
|
||||
|
||||
// First, try to find a unique network matching by name or ID
|
||||
inspect, err := s.apiClient().NetworkInspect(ctx, n.Name, network.InspectOptions{})
|
||||
res, err := s.apiClient().NetworkInspect(ctx, n.Name, client.NetworkInspectOptions{})
|
||||
if err == nil {
|
||||
inspect := res.Network
|
||||
// NetworkInspect will match on ID prefix, so double check we get the expected one
|
||||
// as looking for network named `db` we could erroneously match network ID `db9086999caf`
|
||||
if inspect.Name == n.Name || inspect.ID == n.Name {
|
||||
@@ -1327,22 +1368,22 @@ func (s *composeService) resolveOrCreateNetwork(ctx context.Context, project *ty
|
||||
// ignore other errors. Typically, an ambiguous request by name results in some generic `invalidParameter` error
|
||||
|
||||
// Either not found, or name is ambiguous - use NetworkList to list by name
|
||||
networks, err := s.apiClient().NetworkList(ctx, network.ListOptions{
|
||||
Filters: filters.NewArgs(filters.Arg("name", n.Name)),
|
||||
nwList, err := s.apiClient().NetworkList(ctx, client.NetworkListOptions{
|
||||
Filters: make(client.Filters).Add("name", n.Name),
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// NetworkList Matches all or part of a network name, so we have to filter for a strict match
|
||||
networks = slices.DeleteFunc(networks, func(net network.Summary) bool {
|
||||
networks := slices.DeleteFunc(nwList.Items, func(net network.Summary) bool {
|
||||
return net.Name != n.Name
|
||||
})
|
||||
|
||||
for _, net := range networks {
|
||||
if net.Labels[api.ProjectLabel] == project.Name &&
|
||||
net.Labels[api.NetworkLabel] == name {
|
||||
return net.ID, nil
|
||||
for _, nw := range networks {
|
||||
if nw.Labels[api.ProjectLabel] == project.Name &&
|
||||
nw.Labels[api.NetworkLabel] == name {
|
||||
return nw.ID, nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1359,12 +1400,11 @@ func (s *composeService) resolveOrCreateNetwork(ctx context.Context, project *ty
|
||||
if n.Ipam.Config != nil {
|
||||
var config []network.IPAMConfig
|
||||
for _, pool := range n.Ipam.Config {
|
||||
config = append(config, network.IPAMConfig{
|
||||
Subnet: pool.Subnet,
|
||||
IPRange: pool.IPRange,
|
||||
Gateway: pool.Gateway,
|
||||
AuxAddress: pool.AuxiliaryAddresses,
|
||||
})
|
||||
c, err := parseIPAMPool(pool)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
config = append(config, c)
|
||||
}
|
||||
ipam = &network.IPAM{
|
||||
Driver: n.Ipam.Driver,
|
||||
@@ -1376,7 +1416,7 @@ func (s *composeService) resolveOrCreateNetwork(ctx context.Context, project *ty
|
||||
return "", err
|
||||
}
|
||||
n.CustomLabels = n.CustomLabels.Add(api.ConfigHashLabel, hash)
|
||||
createOpts := network.CreateOptions{
|
||||
createOpts := client.NetworkCreateOptions{
|
||||
Labels: mergeLabels(n.Labels, n.CustomLabels),
|
||||
Driver: n.Driver,
|
||||
Options: n.DriverOpts,
|
||||
@@ -1396,13 +1436,11 @@ func (s *composeService) resolveOrCreateNetwork(ctx context.Context, project *ty
|
||||
}
|
||||
|
||||
for _, ipamConfig := range n.Ipam.Config {
|
||||
config := network.IPAMConfig{
|
||||
Subnet: ipamConfig.Subnet,
|
||||
IPRange: ipamConfig.IPRange,
|
||||
Gateway: ipamConfig.Gateway,
|
||||
AuxAddress: ipamConfig.AuxiliaryAddresses,
|
||||
c, err := parseIPAMPool(ipamConfig)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
createOpts.IPAM.Config = append(createOpts.IPAM.Config, config)
|
||||
createOpts.IPAM.Config = append(createOpts.IPAM.Config, c)
|
||||
}
|
||||
|
||||
networkEventName := fmt.Sprintf("Network %s", n.Name)
|
||||
@@ -1453,7 +1491,7 @@ func (s *composeService) removeDivergedNetwork(ctx context.Context, project *typ
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = s.apiClient().NetworkRemove(ctx, n.Name)
|
||||
_, err = s.apiClient().NetworkRemove(ctx, n.Name, client.NetworkRemoveOptions{})
|
||||
eventName := fmt.Sprintf("Network %s", n.Name)
|
||||
s.events.On(removedEvent(eventName))
|
||||
return containers, err
|
||||
@@ -1465,7 +1503,10 @@ func (s *composeService) disconnectNetwork(
|
||||
containers Containers,
|
||||
) error {
|
||||
for _, c := range containers {
|
||||
err := s.apiClient().NetworkDisconnect(ctx, nwName, c.ID, true)
|
||||
_, err := s.apiClient().NetworkDisconnect(ctx, nwName, client.NetworkDisconnectOptions{
|
||||
Container: c.ID,
|
||||
Force: true,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -1481,7 +1522,10 @@ func (s *composeService) connectNetwork(
|
||||
config *network.EndpointSettings,
|
||||
) error {
|
||||
for _, c := range containers {
|
||||
err := s.apiClient().NetworkConnect(ctx, nwName, c.ID, config)
|
||||
_, err := s.apiClient().NetworkConnect(ctx, nwName, client.NetworkConnectOptions{
|
||||
Container: c.ID,
|
||||
EndpointConfig: config,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -1495,26 +1539,26 @@ func (s *composeService) resolveExternalNetwork(ctx context.Context, n *types.Ne
|
||||
// filter is used to look for an exact match to prevent e.g. a network
|
||||
// named `db` from getting erroneously matched to a network with an ID
|
||||
// like `db9086999caf`
|
||||
networks, err := s.apiClient().NetworkList(ctx, network.ListOptions{
|
||||
Filters: filters.NewArgs(filters.Arg("name", n.Name)),
|
||||
res, err := s.apiClient().NetworkList(ctx, client.NetworkListOptions{
|
||||
Filters: make(client.Filters).Add("name", n.Name),
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
networks := res.Items
|
||||
|
||||
if len(networks) == 0 {
|
||||
// in this instance, n.Name is really an ID
|
||||
sn, err := s.apiClient().NetworkInspect(ctx, n.Name, network.InspectOptions{})
|
||||
sn, err := s.apiClient().NetworkInspect(ctx, n.Name, client.NetworkInspectOptions{})
|
||||
if err == nil {
|
||||
networks = append(networks, sn)
|
||||
networks = append(networks, network.Summary{Network: sn.Network.Network})
|
||||
} else if !errdefs.IsNotFound(err) {
|
||||
return "", err
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// NetworkList API doesn't return the exact name match, so we can retrieve more than one network with a request
|
||||
networks = slices.DeleteFunc(networks, func(net network.Inspect) bool {
|
||||
networks = slices.DeleteFunc(networks, func(net network.Summary) bool {
|
||||
// this function is called during the rebuild stage of `compose watch`.
|
||||
// we still require just one network back, but we need to run the search on the ID
|
||||
return net.Name != n.Name && net.ID != n.Name
|
||||
@@ -1542,7 +1586,7 @@ 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) (string, error) {
|
||||
inspected, err := s.apiClient().VolumeInspect(ctx, volume.Name)
|
||||
inspected, err := s.apiClient().VolumeInspect(ctx, volume.Name, client.VolumeInspectOptions{})
|
||||
if err != nil {
|
||||
if !errdefs.IsNotFound(err) {
|
||||
return "", err
|
||||
@@ -1559,7 +1603,7 @@ func (s *composeService) ensureVolume(ctx context.Context, name string, volume t
|
||||
}
|
||||
|
||||
// Volume exists with name, but let's double-check this is the expected one
|
||||
p, ok := inspected.Labels[api.ProjectLabel]
|
||||
p, ok := inspected.Volume.Labels[api.ProjectLabel]
|
||||
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)
|
||||
}
|
||||
@@ -1571,7 +1615,7 @@ func (s *composeService) ensureVolume(ctx context.Context, name string, volume t
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
actual, ok := inspected.Labels[api.ConfigHashLabel]
|
||||
actual, ok := inspected.Volume.Labels[api.ConfigHashLabel]
|
||||
if ok && actual != expected {
|
||||
msg := fmt.Sprintf("Volume %q exists but doesn't match configuration in compose file. Recreate (data will be lost)?", volume.Name)
|
||||
confirm, err := s.prompt(msg, false)
|
||||
@@ -1586,7 +1630,7 @@ func (s *composeService) ensureVolume(ctx context.Context, name string, volume t
|
||||
return volume.Name, s.createVolume(ctx, volume)
|
||||
}
|
||||
}
|
||||
return inspected.Name, nil
|
||||
return inspected.Volume.Name, nil
|
||||
}
|
||||
|
||||
func (s *composeService) removeDivergedVolume(ctx context.Context, name string, volume types.VolumeConfig, project *types.Project) error {
|
||||
@@ -1626,7 +1670,10 @@ func (s *composeService) removeDivergedVolume(ctx context.Context, name string,
|
||||
return err
|
||||
}
|
||||
|
||||
return s.apiClient().VolumeRemove(ctx, volume.Name, true)
|
||||
_, err = s.apiClient().VolumeRemove(ctx, volume.Name, client.VolumeRemoveOptions{
|
||||
Force: true,
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *composeService) createVolume(ctx context.Context, volume types.VolumeConfig) error {
|
||||
@@ -1637,7 +1684,7 @@ func (s *composeService) createVolume(ctx context.Context, volume types.VolumeCo
|
||||
return err
|
||||
}
|
||||
volume.CustomLabels.Add(api.ConfigHashLabel, hash)
|
||||
_, err = s.apiClient().VolumeCreate(ctx, volumetypes.CreateOptions{
|
||||
_, err = s.apiClient().VolumeCreate(ctx, client.VolumeCreateOptions{
|
||||
Labels: mergeLabels(volume.Labels, volume.CustomLabels),
|
||||
Name: volume.Name,
|
||||
Driver: volume.Driver,
|
||||
@@ -1650,3 +1697,59 @@ func (s *composeService) createVolume(ctx context.Context, volume types.VolumeCo
|
||||
s.events.On(createdEvent(eventName))
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseIPAMPool(pool *types.IPAMPool) (network.IPAMConfig, error) {
|
||||
var (
|
||||
err error
|
||||
subNet netip.Prefix
|
||||
ipRange netip.Prefix
|
||||
gateway netip.Addr
|
||||
auxAddress map[string]netip.Addr
|
||||
)
|
||||
if pool.Subnet != "" {
|
||||
subNet, err = netip.ParsePrefix(pool.Subnet)
|
||||
if err != nil {
|
||||
return network.IPAMConfig{}, fmt.Errorf("invalid subnet: %w", err)
|
||||
}
|
||||
}
|
||||
if pool.IPRange != "" {
|
||||
ipRange, err = netip.ParsePrefix(pool.IPRange)
|
||||
if err != nil {
|
||||
return network.IPAMConfig{}, fmt.Errorf("invalid ip-range: %w", err)
|
||||
}
|
||||
}
|
||||
if pool.Gateway != "" {
|
||||
gateway, err = netip.ParseAddr(pool.Gateway)
|
||||
if err != nil {
|
||||
return network.IPAMConfig{}, fmt.Errorf("invalid gateway address: %w", err)
|
||||
}
|
||||
}
|
||||
if len(pool.AuxiliaryAddresses) > 0 {
|
||||
auxAddress = make(map[string]netip.Addr, len(pool.AuxiliaryAddresses))
|
||||
for auxName, addr := range pool.AuxiliaryAddresses {
|
||||
auxAddr, err := netip.ParseAddr(addr)
|
||||
if err != nil {
|
||||
return network.IPAMConfig{}, fmt.Errorf("invalid auxiliary address: %w", err)
|
||||
}
|
||||
auxAddress[auxName] = auxAddr
|
||||
}
|
||||
|
||||
}
|
||||
return network.IPAMConfig{
|
||||
Subnet: subNet,
|
||||
IPRange: ipRange,
|
||||
Gateway: gateway,
|
||||
AuxAddress: auxAddress,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func parseMACAddr(macAddress string) (network.HardwareAddr, error) {
|
||||
if macAddress == "" {
|
||||
return nil, nil
|
||||
}
|
||||
m, err := net.ParseMAC(macAddress)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid MAC address: %w", err)
|
||||
}
|
||||
return network.HardwareAddr(m), nil
|
||||
}
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
package compose
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/netip"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
@@ -24,10 +26,11 @@ import (
|
||||
|
||||
composeloader "github.com/compose-spec/compose-go/v2/loader"
|
||||
composetypes "github.com/compose-spec/compose-go/v2/types"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/image"
|
||||
mountTypes "github.com/docker/docker/api/types/mount"
|
||||
"github.com/docker/docker/api/types/network"
|
||||
"github.com/google/go-cmp/cmp/cmpopts"
|
||||
"github.com/moby/moby/api/types/container"
|
||||
mountTypes "github.com/moby/moby/api/types/mount"
|
||||
"github.com/moby/moby/api/types/network"
|
||||
"github.com/moby/moby/client"
|
||||
"go.uber.org/mock/gomock"
|
||||
"gotest.tools/v3/assert"
|
||||
"gotest.tools/v3/assert/cmp"
|
||||
@@ -161,7 +164,7 @@ func TestBuildContainerMountOptions(t *testing.T) {
|
||||
s := composeService{
|
||||
dockerCli: cli,
|
||||
}
|
||||
mock.EXPECT().ImageInspect(gomock.Any(), "myProject-myService").AnyTimes().Return(image.InspectResponse{}, nil)
|
||||
mock.EXPECT().ImageInspect(gomock.Any(), "myProject-myService").AnyTimes().Return(client.ImageInspectResult{}, nil)
|
||||
|
||||
mounts, err := s.buildContainerMountOptions(t.Context(), project, project.Services["myService"], inherit)
|
||||
sort.Slice(mounts, func(i, j int) bool {
|
||||
@@ -189,7 +192,7 @@ func TestBuildContainerMountOptions(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDefaultNetworkSettings(t *testing.T) {
|
||||
t.Run("returns the network with the highest priority when service has multiple networks", func(t *testing.T) {
|
||||
t.Run("returns the network with the highest priority as primary when service has multiple networks", func(t *testing.T) {
|
||||
service := composetypes.ServiceConfig{
|
||||
Name: "myService",
|
||||
Networks: map[string]*composetypes.ServiceNetworkConfig{
|
||||
@@ -216,10 +219,11 @@ func TestDefaultNetworkSettings(t *testing.T) {
|
||||
}),
|
||||
}
|
||||
|
||||
networkMode, networkConfig, err := defaultNetworkSettings(&project, service, 1, nil, true, "1.43")
|
||||
networkMode, networkConfig, err := defaultNetworkSettings(&project, service, 1, nil, true, "1.44")
|
||||
assert.NilError(t, err)
|
||||
assert.Equal(t, string(networkMode), "myProject_myNetwork2")
|
||||
assert.Check(t, cmp.Len(networkConfig.EndpointsConfig, 1))
|
||||
assert.Check(t, cmp.Len(networkConfig.EndpointsConfig, 2))
|
||||
assert.Check(t, cmp.Contains(networkConfig.EndpointsConfig, "myProject_myNetwork1"))
|
||||
assert.Check(t, cmp.Contains(networkConfig.EndpointsConfig, "myProject_myNetwork2"))
|
||||
})
|
||||
|
||||
@@ -245,7 +249,7 @@ func TestDefaultNetworkSettings(t *testing.T) {
|
||||
}),
|
||||
}
|
||||
|
||||
networkMode, networkConfig, err := defaultNetworkSettings(&project, service, 1, nil, true, "1.43")
|
||||
networkMode, networkConfig, err := defaultNetworkSettings(&project, service, 1, nil, true, "1.44")
|
||||
assert.NilError(t, err)
|
||||
assert.Equal(t, string(networkMode), "myProject_default")
|
||||
assert.Check(t, cmp.Len(networkConfig.EndpointsConfig, 1))
|
||||
@@ -263,7 +267,7 @@ func TestDefaultNetworkSettings(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
networkMode, networkConfig, err := defaultNetworkSettings(&project, service, 1, nil, true, "1.43")
|
||||
networkMode, networkConfig, err := defaultNetworkSettings(&project, service, 1, nil, true, "1.44")
|
||||
assert.NilError(t, err)
|
||||
assert.Equal(t, string(networkMode), "none")
|
||||
assert.Check(t, cmp.Nil(networkConfig))
|
||||
@@ -284,7 +288,7 @@ func TestDefaultNetworkSettings(t *testing.T) {
|
||||
}),
|
||||
}
|
||||
|
||||
networkMode, networkConfig, err := defaultNetworkSettings(&project, service, 1, nil, true, "1.43")
|
||||
networkMode, networkConfig, err := defaultNetworkSettings(&project, service, 1, nil, true, "1.44")
|
||||
assert.NilError(t, err)
|
||||
assert.Equal(t, string(networkMode), "host")
|
||||
assert.Check(t, cmp.Nil(networkConfig))
|
||||
@@ -292,7 +296,7 @@ func TestDefaultNetworkSettings(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateEndpointSettings(t *testing.T) {
|
||||
eps := createEndpointSettings(&composetypes.Project{
|
||||
eps, err := createEndpointSettings(&composetypes.Project{
|
||||
Name: "projName",
|
||||
}, composetypes.ServiceConfig{
|
||||
Name: "serviceName",
|
||||
@@ -304,7 +308,7 @@ func TestCreateEndpointSettings(t *testing.T) {
|
||||
Ipv4Address: "10.16.17.18",
|
||||
Ipv6Address: "fdb4:7a7f:373a:3f0c::42",
|
||||
LinkLocalIPs: []string{"169.254.10.20"},
|
||||
MacAddress: "10:00:00:00:01",
|
||||
MacAddress: "02:00:00:00:00:01",
|
||||
DriverOpts: composetypes.Options{
|
||||
"driverOpt1": "optval1",
|
||||
"driverOpt2": "optval2",
|
||||
@@ -312,15 +316,17 @@ func TestCreateEndpointSettings(t *testing.T) {
|
||||
},
|
||||
},
|
||||
}, 0, "netName", []string{"link1", "link2"}, true)
|
||||
assert.NilError(t, err)
|
||||
macAddr, _ := net.ParseMAC("02:00:00:00:00:01")
|
||||
assert.Check(t, cmp.DeepEqual(eps, &network.EndpointSettings{
|
||||
IPAMConfig: &network.EndpointIPAMConfig{
|
||||
IPv4Address: "10.16.17.18",
|
||||
IPv6Address: "fdb4:7a7f:373a:3f0c::42",
|
||||
LinkLocalIPs: []string{"169.254.10.20"},
|
||||
IPv4Address: netip.MustParseAddr("10.16.17.18").Unmap(),
|
||||
IPv6Address: netip.MustParseAddr("fdb4:7a7f:373a:3f0c::42"),
|
||||
LinkLocalIPs: []netip.Addr{netip.MustParseAddr("169.254.10.20").Unmap()},
|
||||
},
|
||||
Links: []string{"link1", "link2"},
|
||||
Aliases: []string{"containerName", "serviceName", "alias1", "alias2"},
|
||||
MacAddress: "10:00:00:00:01",
|
||||
MacAddress: network.HardwareAddr(macAddr),
|
||||
DriverOpts: map[string]string{
|
||||
"driverOpt1": "optval1",
|
||||
"driverOpt2": "optval2",
|
||||
@@ -330,9 +336,9 @@ func TestCreateEndpointSettings(t *testing.T) {
|
||||
// - The IPv6 address here is the container's address, not the gateway.
|
||||
// - Both fields will be cleared by the daemon, but they could be removed from
|
||||
// the request.
|
||||
IPAddress: "10.16.17.18",
|
||||
IPv6Gateway: "fdb4:7a7f:373a:3f0c::42",
|
||||
}))
|
||||
IPAddress: netip.MustParseAddr("10.16.17.18").Unmap(),
|
||||
IPv6Gateway: netip.MustParseAddr("fdb4:7a7f:373a:3f0c::42"),
|
||||
}, cmpopts.EquateComparable(netip.Addr{})))
|
||||
}
|
||||
|
||||
func Test_buildContainerVolumes(t *testing.T) {
|
||||
|
||||
@@ -19,6 +19,8 @@ package compose
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/moby/moby/client"
|
||||
)
|
||||
|
||||
// engineLabelDesktopAddress is used to detect that Compose is running with a
|
||||
@@ -27,11 +29,11 @@ import (
|
||||
const engineLabelDesktopAddress = "com.docker.desktop.address"
|
||||
|
||||
func (s *composeService) isDesktopIntegrationActive(ctx context.Context) (bool, error) {
|
||||
info, err := s.apiClient().Info(ctx)
|
||||
res, err := s.apiClient().Info(ctx, client.InfoOptions{})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
for _, l := range info.Labels {
|
||||
for _, l := range res.Info.Labels {
|
||||
k, _, ok := strings.Cut(l, "=")
|
||||
if ok && k == engineLabelDesktopAddress {
|
||||
return true, nil
|
||||
|
||||
@@ -24,10 +24,8 @@ import (
|
||||
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
"github.com/containerd/errdefs"
|
||||
containerType "github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
imageapi "github.com/docker/docker/api/types/image"
|
||||
"github.com/docker/docker/api/types/network"
|
||||
containerType "github.com/moby/moby/api/types/container"
|
||||
"github.com/moby/moby/client"
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/sync/errgroup"
|
||||
|
||||
@@ -196,14 +194,13 @@ func (s *composeService) ensureNetworksDown(ctx context.Context, project *types.
|
||||
}
|
||||
|
||||
func (s *composeService) removeNetwork(ctx context.Context, composeNetworkName string, projectName string, name string) error {
|
||||
networks, err := s.apiClient().NetworkList(ctx, network.ListOptions{
|
||||
Filters: filters.NewArgs(
|
||||
projectFilter(projectName),
|
||||
networkFilter(composeNetworkName)),
|
||||
res, err := s.apiClient().NetworkList(ctx, client.NetworkListOptions{
|
||||
Filters: projectFilter(projectName).Add("label", networkFilter(composeNetworkName)),
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to list networks: %w", err)
|
||||
}
|
||||
networks := res.Items
|
||||
|
||||
if len(networks) == 0 {
|
||||
return nil
|
||||
@@ -217,7 +214,7 @@ func (s *composeService) removeNetwork(ctx context.Context, composeNetworkName s
|
||||
if net.Name != name {
|
||||
continue
|
||||
}
|
||||
nw, err := s.apiClient().NetworkInspect(ctx, net.ID, network.InspectOptions{})
|
||||
nwInspect, err := s.apiClient().NetworkInspect(ctx, net.ID, client.NetworkInspectOptions{})
|
||||
if errdefs.IsNotFound(err) {
|
||||
s.events.On(newEvent(eventName, api.Warning, "No resource found to remove"))
|
||||
return nil
|
||||
@@ -225,13 +222,14 @@ func (s *composeService) removeNetwork(ctx context.Context, composeNetworkName s
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
nw := nwInspect.Network
|
||||
if len(nw.Containers) > 0 {
|
||||
s.events.On(newEvent(eventName, api.Warning, "Resource is still in use"))
|
||||
found++
|
||||
continue
|
||||
}
|
||||
|
||||
if err := s.apiClient().NetworkRemove(ctx, net.ID); err != nil {
|
||||
if _, err := s.apiClient().NetworkRemove(ctx, net.ID, client.NetworkRemoveOptions{}); err != nil {
|
||||
if errdefs.IsNotFound(err) {
|
||||
continue
|
||||
}
|
||||
@@ -255,7 +253,7 @@ func (s *composeService) removeNetwork(ctx context.Context, composeNetworkName s
|
||||
func (s *composeService) removeImage(ctx context.Context, image string) error {
|
||||
id := fmt.Sprintf("Image %s", image)
|
||||
s.events.On(newEvent(id, api.Working, "Removing"))
|
||||
_, err := s.apiClient().ImageRemove(ctx, image, imageapi.RemoveOptions{})
|
||||
_, err := s.apiClient().ImageRemove(ctx, image, client.ImageRemoveOptions{})
|
||||
if err == nil {
|
||||
s.events.On(newEvent(id, api.Done, "Removed"))
|
||||
return nil
|
||||
@@ -274,14 +272,16 @@ func (s *composeService) removeImage(ctx context.Context, image string) error {
|
||||
func (s *composeService) removeVolume(ctx context.Context, id string) error {
|
||||
resource := fmt.Sprintf("Volume %s", id)
|
||||
|
||||
_, err := s.apiClient().VolumeInspect(ctx, id)
|
||||
_, err := s.apiClient().VolumeInspect(ctx, id, client.VolumeInspectOptions{})
|
||||
if errdefs.IsNotFound(err) {
|
||||
// Already gone
|
||||
return nil
|
||||
}
|
||||
|
||||
s.events.On(newEvent(resource, api.Working, "Removing"))
|
||||
err = s.apiClient().VolumeRemove(ctx, id, true)
|
||||
_, err = s.apiClient().VolumeRemove(ctx, id, client.VolumeRemoveOptions{
|
||||
Force: true,
|
||||
})
|
||||
if err == nil {
|
||||
s.events.On(newEvent(resource, api.Done, "Removed"))
|
||||
return nil
|
||||
@@ -314,8 +314,9 @@ func (s *composeService) stopContainer(ctx context.Context, service *types.Servi
|
||||
}
|
||||
}
|
||||
|
||||
timeoutInSecond := utils.DurationSecondToInt(timeout)
|
||||
err := s.apiClient().ContainerStop(ctx, ctr.ID, containerType.StopOptions{Timeout: timeoutInSecond})
|
||||
_, err := s.apiClient().ContainerStop(ctx, ctr.ID, client.ContainerStopOptions{
|
||||
Timeout: utils.DurationSecondToInt(timeout),
|
||||
})
|
||||
if err != nil {
|
||||
s.events.On(errorEvent(eventName, "Error while Stopping"))
|
||||
return err
|
||||
@@ -355,7 +356,7 @@ func (s *composeService) stopAndRemoveContainer(ctx context.Context, ctr contain
|
||||
return err
|
||||
}
|
||||
s.events.On(removingEvent(eventName))
|
||||
err = s.apiClient().ContainerRemove(ctx, ctr.ID, containerType.RemoveOptions{
|
||||
_, err = s.apiClient().ContainerRemove(ctx, ctr.ID, client.ContainerRemoveOptions{
|
||||
Force: true,
|
||||
RemoveVolumes: volumes,
|
||||
})
|
||||
|
||||
@@ -25,11 +25,11 @@ import (
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
"github.com/containerd/errdefs"
|
||||
"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/image"
|
||||
"github.com/docker/docker/api/types/network"
|
||||
"github.com/docker/docker/api/types/volume"
|
||||
"github.com/moby/moby/api/types/container"
|
||||
"github.com/moby/moby/api/types/image"
|
||||
"github.com/moby/moby/api/types/network"
|
||||
"github.com/moby/moby/api/types/volume"
|
||||
"github.com/moby/moby/client"
|
||||
"go.uber.org/mock/gomock"
|
||||
"gotest.tools/v3/assert"
|
||||
|
||||
@@ -46,48 +46,50 @@ func TestDown(t *testing.T) {
|
||||
assert.NilError(t, err)
|
||||
|
||||
api.EXPECT().ContainerList(gomock.Any(), projectFilterListOpt(false)).Return(
|
||||
[]container.Summary{
|
||||
client.ContainerListResult{Items: []container.Summary{
|
||||
testContainer("service1", "123", false),
|
||||
testContainer("service2", "456", false),
|
||||
testContainer("service2", "789", false),
|
||||
testContainer("service_orphan", "321", true),
|
||||
}, nil)
|
||||
}}, nil)
|
||||
api.EXPECT().VolumeList(
|
||||
gomock.Any(),
|
||||
volume.ListOptions{
|
||||
Filters: filters.NewArgs(projectFilter(strings.ToLower(testProject))),
|
||||
client.VolumeListOptions{
|
||||
Filters: projectFilter(strings.ToLower(testProject)),
|
||||
}).
|
||||
Return(volume.ListResponse{}, nil)
|
||||
Return(client.VolumeListResult{}, nil)
|
||||
|
||||
// network names are not guaranteed to be unique, ensure Compose handles
|
||||
// cleanup properly if duplicates are inadvertently created
|
||||
api.EXPECT().NetworkList(gomock.Any(), network.ListOptions{Filters: filters.NewArgs(projectFilter(strings.ToLower(testProject)))}).
|
||||
Return([]network.Summary{
|
||||
{ID: "abc123", Name: "myProject_default", Labels: map[string]string{compose.NetworkLabel: "default"}},
|
||||
{ID: "def456", Name: "myProject_default", Labels: map[string]string{compose.NetworkLabel: "default"}},
|
||||
}, nil)
|
||||
api.EXPECT().NetworkList(gomock.Any(), client.NetworkListOptions{Filters: projectFilter(strings.ToLower(testProject))}).
|
||||
Return(client.NetworkListResult{Items: []network.Summary{
|
||||
{Network: network.Network{ID: "abc123", Name: "myProject_default", Labels: map[string]string{compose.NetworkLabel: "default"}}},
|
||||
{Network: network.Network{ID: "def456", Name: "myProject_default", Labels: map[string]string{compose.NetworkLabel: "default"}}},
|
||||
}}, nil)
|
||||
|
||||
stopOptions := container.StopOptions{}
|
||||
api.EXPECT().ContainerStop(gomock.Any(), "123", stopOptions).Return(nil)
|
||||
api.EXPECT().ContainerStop(gomock.Any(), "456", stopOptions).Return(nil)
|
||||
api.EXPECT().ContainerStop(gomock.Any(), "789", stopOptions).Return(nil)
|
||||
stopOptions := client.ContainerStopOptions{}
|
||||
api.EXPECT().ContainerStop(gomock.Any(), "123", stopOptions).Return(client.ContainerStopResult{}, nil)
|
||||
api.EXPECT().ContainerStop(gomock.Any(), "456", stopOptions).Return(client.ContainerStopResult{}, nil)
|
||||
api.EXPECT().ContainerStop(gomock.Any(), "789", stopOptions).Return(client.ContainerStopResult{}, 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", client.ContainerRemoveOptions{Force: true}).Return(client.ContainerRemoveResult{}, nil)
|
||||
api.EXPECT().ContainerRemove(gomock.Any(), "456", client.ContainerRemoveOptions{Force: true}).Return(client.ContainerRemoveResult{}, nil)
|
||||
api.EXPECT().ContainerRemove(gomock.Any(), "789", client.ContainerRemoveOptions{Force: true}).Return(client.ContainerRemoveResult{}, nil)
|
||||
|
||||
api.EXPECT().NetworkList(gomock.Any(), network.ListOptions{
|
||||
Filters: filters.NewArgs(
|
||||
projectFilter(strings.ToLower(testProject)),
|
||||
networkFilter("default")),
|
||||
}).Return([]network.Summary{
|
||||
{ID: "abc123", Name: "myProject_default"},
|
||||
{ID: "def456", Name: "myProject_default"},
|
||||
api.EXPECT().NetworkList(gomock.Any(), client.NetworkListOptions{
|
||||
Filters: projectFilter(strings.ToLower(testProject)).Add("label", networkFilter("default")),
|
||||
}).Return(client.NetworkListResult{Items: []network.Summary{
|
||||
{Network: network.Network{ID: "abc123", Name: "myProject_default"}},
|
||||
{Network: network.Network{ID: "def456", Name: "myProject_default"}},
|
||||
}}, nil)
|
||||
api.EXPECT().NetworkInspect(gomock.Any(), "abc123", gomock.Any()).Return(client.NetworkInspectResult{
|
||||
Network: network.Inspect{Network: network.Network{ID: "abc123"}},
|
||||
}, nil)
|
||||
api.EXPECT().NetworkInspect(gomock.Any(), "abc123", gomock.Any()).Return(network.Inspect{ID: "abc123"}, nil)
|
||||
api.EXPECT().NetworkInspect(gomock.Any(), "def456", gomock.Any()).Return(network.Inspect{ID: "def456"}, nil)
|
||||
api.EXPECT().NetworkRemove(gomock.Any(), "abc123").Return(nil)
|
||||
api.EXPECT().NetworkRemove(gomock.Any(), "def456").Return(nil)
|
||||
api.EXPECT().NetworkInspect(gomock.Any(), "def456", gomock.Any()).Return(client.NetworkInspectResult{
|
||||
Network: network.Inspect{Network: network.Network{ID: "def456"}},
|
||||
}, nil)
|
||||
api.EXPECT().NetworkRemove(gomock.Any(), "abc123", gomock.Any()).Return(client.NetworkRemoveResult{}, nil)
|
||||
api.EXPECT().NetworkRemove(gomock.Any(), "def456", gomock.Any()).Return(client.NetworkRemoveResult{}, nil)
|
||||
|
||||
err = tested.Down(t.Context(), strings.ToLower(testProject), compose.DownOptions{})
|
||||
assert.NilError(t, err)
|
||||
@@ -101,42 +103,40 @@ func TestDownWithGivenServices(t *testing.T) {
|
||||
tested, err := NewComposeService(cli)
|
||||
assert.NilError(t, err)
|
||||
|
||||
api.EXPECT().ContainerList(gomock.Any(), projectFilterListOpt(false)).Return(
|
||||
[]container.Summary{
|
||||
api.EXPECT().ContainerList(gomock.Any(), projectFilterListOpt(false)).Return(client.ContainerListResult{
|
||||
Items: []container.Summary{
|
||||
testContainer("service1", "123", false),
|
||||
testContainer("service2", "456", false),
|
||||
testContainer("service2", "789", false),
|
||||
testContainer("service_orphan", "321", true),
|
||||
}, nil)
|
||||
},
|
||||
}, nil)
|
||||
api.EXPECT().VolumeList(
|
||||
gomock.Any(),
|
||||
volume.ListOptions{
|
||||
Filters: filters.NewArgs(projectFilter(strings.ToLower(testProject))),
|
||||
client.VolumeListOptions{
|
||||
Filters: projectFilter(strings.ToLower(testProject)),
|
||||
}).
|
||||
Return(volume.ListResponse{}, nil)
|
||||
Return(client.VolumeListResult{}, nil)
|
||||
|
||||
// network names are not guaranteed to be unique, ensure Compose handles
|
||||
// cleanup properly if duplicates are inadvertently created
|
||||
api.EXPECT().NetworkList(gomock.Any(), network.ListOptions{Filters: filters.NewArgs(projectFilter(strings.ToLower(testProject)))}).
|
||||
Return([]network.Summary{
|
||||
{ID: "abc123", Name: "myProject_default", Labels: map[string]string{compose.NetworkLabel: "default"}},
|
||||
{ID: "def456", Name: "myProject_default", Labels: map[string]string{compose.NetworkLabel: "default"}},
|
||||
}, nil)
|
||||
api.EXPECT().NetworkList(gomock.Any(), client.NetworkListOptions{Filters: projectFilter(strings.ToLower(testProject))}).
|
||||
Return(client.NetworkListResult{Items: []network.Summary{
|
||||
{Network: network.Network{ID: "abc123", Name: "myProject_default", Labels: map[string]string{compose.NetworkLabel: "default"}}},
|
||||
{Network: network.Network{ID: "def456", Name: "myProject_default", Labels: map[string]string{compose.NetworkLabel: "default"}}},
|
||||
}}, nil)
|
||||
|
||||
stopOptions := container.StopOptions{}
|
||||
api.EXPECT().ContainerStop(gomock.Any(), "123", stopOptions).Return(nil)
|
||||
api.EXPECT().ContainerStop(gomock.Any(), "123", client.ContainerStopOptions{}).Return(client.ContainerStopResult{}, nil)
|
||||
|
||||
api.EXPECT().ContainerRemove(gomock.Any(), "123", container.RemoveOptions{Force: true}).Return(nil)
|
||||
api.EXPECT().ContainerRemove(gomock.Any(), "123", client.ContainerRemoveOptions{Force: true}).Return(client.ContainerRemoveResult{}, nil)
|
||||
|
||||
api.EXPECT().NetworkList(gomock.Any(), network.ListOptions{
|
||||
Filters: filters.NewArgs(
|
||||
projectFilter(strings.ToLower(testProject)),
|
||||
networkFilter("default")),
|
||||
}).Return([]network.Summary{
|
||||
{ID: "abc123", Name: "myProject_default"},
|
||||
}, nil)
|
||||
api.EXPECT().NetworkInspect(gomock.Any(), "abc123", gomock.Any()).Return(network.Inspect{ID: "abc123"}, nil)
|
||||
api.EXPECT().NetworkRemove(gomock.Any(), "abc123").Return(nil)
|
||||
api.EXPECT().NetworkList(gomock.Any(), client.NetworkListOptions{
|
||||
Filters: projectFilter(strings.ToLower(testProject)).Add("label", networkFilter("default")),
|
||||
}).Return(client.NetworkListResult{Items: []network.Summary{
|
||||
{Network: network.Network{ID: "abc123", Name: "myProject_default"}},
|
||||
}}, nil)
|
||||
api.EXPECT().NetworkInspect(gomock.Any(), "abc123", gomock.Any()).Return(client.NetworkInspectResult{Network: network.Inspect{Network: network.Network{ID: "abc123"}}}, nil)
|
||||
api.EXPECT().NetworkRemove(gomock.Any(), "abc123", gomock.Any()).Return(client.NetworkRemoveResult{}, nil)
|
||||
|
||||
err = tested.Down(t.Context(), strings.ToLower(testProject), compose.DownOptions{
|
||||
Services: []string{"service1", "not-running-service"},
|
||||
@@ -152,27 +152,28 @@ func TestDownWithSpecifiedServiceButTheServicesAreNotRunning(t *testing.T) {
|
||||
tested, err := NewComposeService(cli)
|
||||
assert.NilError(t, err)
|
||||
|
||||
api.EXPECT().ContainerList(gomock.Any(), projectFilterListOpt(false)).Return(
|
||||
[]container.Summary{
|
||||
api.EXPECT().ContainerList(gomock.Any(), projectFilterListOpt(false)).Return(client.ContainerListResult{
|
||||
Items: []container.Summary{
|
||||
testContainer("service1", "123", false),
|
||||
testContainer("service2", "456", false),
|
||||
testContainer("service2", "789", false),
|
||||
testContainer("service_orphan", "321", true),
|
||||
}, nil)
|
||||
},
|
||||
}, nil)
|
||||
api.EXPECT().VolumeList(
|
||||
gomock.Any(),
|
||||
volume.ListOptions{
|
||||
Filters: filters.NewArgs(projectFilter(strings.ToLower(testProject))),
|
||||
client.VolumeListOptions{
|
||||
Filters: projectFilter(strings.ToLower(testProject)),
|
||||
}).
|
||||
Return(volume.ListResponse{}, nil)
|
||||
Return(client.VolumeListResult{}, nil)
|
||||
|
||||
// network names are not guaranteed to be unique, ensure Compose handles
|
||||
// cleanup properly if duplicates are inadvertently created
|
||||
api.EXPECT().NetworkList(gomock.Any(), network.ListOptions{Filters: filters.NewArgs(projectFilter(strings.ToLower(testProject)))}).
|
||||
Return([]network.Summary{
|
||||
{ID: "abc123", Name: "myProject_default", Labels: map[string]string{compose.NetworkLabel: "default"}},
|
||||
{ID: "def456", Name: "myProject_default", Labels: map[string]string{compose.NetworkLabel: "default"}},
|
||||
}, nil)
|
||||
api.EXPECT().NetworkList(gomock.Any(), client.NetworkListOptions{Filters: projectFilter(strings.ToLower(testProject))}).
|
||||
Return(client.NetworkListResult{Items: []network.Summary{
|
||||
{Network: network.Network{ID: "abc123", Name: "myProject_default", Labels: map[string]string{compose.NetworkLabel: "default"}}},
|
||||
{Network: network.Network{ID: "def456", Name: "myProject_default", Labels: map[string]string{compose.NetworkLabel: "default"}}},
|
||||
}}, nil)
|
||||
|
||||
err = tested.Down(t.Context(), strings.ToLower(testProject), compose.DownOptions{
|
||||
Services: []string{"not-running-service1", "not-running-service2"},
|
||||
@@ -189,42 +190,47 @@ func TestDownRemoveOrphans(t *testing.T) {
|
||||
assert.NilError(t, err)
|
||||
|
||||
api.EXPECT().ContainerList(gomock.Any(), projectFilterListOpt(true)).Return(
|
||||
[]container.Summary{
|
||||
testContainer("service1", "123", false),
|
||||
testContainer("service2", "789", false),
|
||||
testContainer("service_orphan", "321", true),
|
||||
client.ContainerListResult{
|
||||
Items: []container.Summary{
|
||||
testContainer("service1", "123", false),
|
||||
testContainer("service2", "789", false),
|
||||
testContainer("service_orphan", "321", true),
|
||||
},
|
||||
}, nil)
|
||||
api.EXPECT().VolumeList(
|
||||
gomock.Any(),
|
||||
volume.ListOptions{
|
||||
Filters: filters.NewArgs(projectFilter(strings.ToLower(testProject))),
|
||||
client.VolumeListOptions{
|
||||
Filters: projectFilter(strings.ToLower(testProject)),
|
||||
}).
|
||||
Return(volume.ListResponse{}, nil)
|
||||
api.EXPECT().NetworkList(gomock.Any(), network.ListOptions{Filters: filters.NewArgs(projectFilter(strings.ToLower(testProject)))}).
|
||||
Return([]network.Summary{
|
||||
{
|
||||
Name: "myProject_default",
|
||||
Labels: map[string]string{compose.NetworkLabel: "default"},
|
||||
},
|
||||
Return(client.VolumeListResult{}, nil)
|
||||
api.EXPECT().NetworkList(gomock.Any(), client.NetworkListOptions{Filters: projectFilter(strings.ToLower(testProject))}).
|
||||
Return(client.NetworkListResult{
|
||||
Items: []network.Summary{{
|
||||
Network: network.Network{
|
||||
Name: "myProject_default",
|
||||
Labels: map[string]string{compose.NetworkLabel: "default"},
|
||||
},
|
||||
}},
|
||||
}, nil)
|
||||
|
||||
stopOptions := container.StopOptions{}
|
||||
api.EXPECT().ContainerStop(gomock.Any(), "123", stopOptions).Return(nil)
|
||||
api.EXPECT().ContainerStop(gomock.Any(), "789", stopOptions).Return(nil)
|
||||
api.EXPECT().ContainerStop(gomock.Any(), "321", stopOptions).Return(nil)
|
||||
stopOptions := client.ContainerStopOptions{}
|
||||
api.EXPECT().ContainerStop(gomock.Any(), "123", stopOptions).Return(client.ContainerStopResult{}, nil)
|
||||
api.EXPECT().ContainerStop(gomock.Any(), "789", stopOptions).Return(client.ContainerStopResult{}, nil)
|
||||
api.EXPECT().ContainerStop(gomock.Any(), "321", stopOptions).Return(client.ContainerStopResult{}, 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", client.ContainerRemoveOptions{Force: true}).Return(client.ContainerRemoveResult{}, nil)
|
||||
api.EXPECT().ContainerRemove(gomock.Any(), "789", client.ContainerRemoveOptions{Force: true}).Return(client.ContainerRemoveResult{}, nil)
|
||||
api.EXPECT().ContainerRemove(gomock.Any(), "321", client.ContainerRemoveOptions{Force: true}).Return(client.ContainerRemoveResult{}, nil)
|
||||
|
||||
api.EXPECT().NetworkList(gomock.Any(), network.ListOptions{
|
||||
Filters: filters.NewArgs(
|
||||
networkFilter("default"),
|
||||
projectFilter(strings.ToLower(testProject)),
|
||||
),
|
||||
}).Return([]network.Summary{{ID: "abc123", Name: "myProject_default"}}, nil)
|
||||
api.EXPECT().NetworkInspect(gomock.Any(), "abc123", gomock.Any()).Return(network.Inspect{ID: "abc123"}, nil)
|
||||
api.EXPECT().NetworkRemove(gomock.Any(), "abc123").Return(nil)
|
||||
api.EXPECT().NetworkList(gomock.Any(), client.NetworkListOptions{
|
||||
Filters: projectFilter(strings.ToLower(testProject)).Add("label", networkFilter("default")),
|
||||
}).Return(client.NetworkListResult{
|
||||
Items: []network.Summary{{Network: network.Network{ID: "abc123", Name: "myProject_default"}}},
|
||||
}, nil)
|
||||
api.EXPECT().NetworkInspect(gomock.Any(), "abc123", gomock.Any()).Return(client.NetworkInspectResult{
|
||||
Network: network.Inspect{Network: network.Network{ID: "abc123"}},
|
||||
}, nil)
|
||||
api.EXPECT().NetworkRemove(gomock.Any(), "abc123", gomock.Any()).Return(client.NetworkRemoveResult{}, nil)
|
||||
|
||||
err = tested.Down(t.Context(), strings.ToLower(testProject), compose.DownOptions{RemoveOrphans: true})
|
||||
assert.NilError(t, err)
|
||||
@@ -239,24 +245,26 @@ func TestDownRemoveVolumes(t *testing.T) {
|
||||
assert.NilError(t, err)
|
||||
|
||||
api.EXPECT().ContainerList(gomock.Any(), projectFilterListOpt(false)).Return(
|
||||
[]container.Summary{testContainer("service1", "123", false)}, nil)
|
||||
client.ContainerListResult{
|
||||
Items: []container.Summary{testContainer("service1", "123", false)},
|
||||
}, nil)
|
||||
api.EXPECT().VolumeList(
|
||||
gomock.Any(),
|
||||
volume.ListOptions{
|
||||
Filters: filters.NewArgs(projectFilter(strings.ToLower(testProject))),
|
||||
client.VolumeListOptions{
|
||||
Filters: projectFilter(strings.ToLower(testProject)),
|
||||
}).
|
||||
Return(volume.ListResponse{
|
||||
Volumes: []*volume.Volume{{Name: "myProject_volume"}},
|
||||
Return(client.VolumeListResult{
|
||||
Items: []volume.Volume{{Name: "myProject_volume"}},
|
||||
}, nil)
|
||||
api.EXPECT().VolumeInspect(gomock.Any(), "myProject_volume").
|
||||
Return(volume.Volume{}, nil)
|
||||
api.EXPECT().NetworkList(gomock.Any(), network.ListOptions{Filters: filters.NewArgs(projectFilter(strings.ToLower(testProject)))}).
|
||||
Return(nil, nil)
|
||||
api.EXPECT().VolumeInspect(gomock.Any(), "myProject_volume", gomock.Any()).
|
||||
Return(client.VolumeInspectResult{}, nil)
|
||||
api.EXPECT().NetworkList(gomock.Any(), client.NetworkListOptions{Filters: projectFilter(strings.ToLower(testProject))}).
|
||||
Return(client.NetworkListResult{}, 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", client.ContainerStopOptions{}).Return(client.ContainerStopResult{}, nil)
|
||||
api.EXPECT().ContainerRemove(gomock.Any(), "123", client.ContainerRemoveOptions{Force: true, RemoveVolumes: true}).Return(client.ContainerRemoveResult{}, nil)
|
||||
|
||||
api.EXPECT().VolumeRemove(gomock.Any(), "myProject_volume", true).Return(nil)
|
||||
api.EXPECT().VolumeRemove(gomock.Any(), "myProject_volume", client.VolumeRemoveOptions{Force: true}).Return(client.VolumeRemoveResult{}, nil)
|
||||
|
||||
err = tested.Down(t.Context(), strings.ToLower(testProject), compose.DownOptions{Volumes: true})
|
||||
assert.NilError(t, err)
|
||||
@@ -285,17 +293,16 @@ func TestDownRemoveImages(t *testing.T) {
|
||||
assert.NilError(t, err)
|
||||
|
||||
api.EXPECT().ContainerList(gomock.Any(), projectFilterListOpt(false)).
|
||||
Return([]container.Summary{
|
||||
testContainer("service1", "123", false),
|
||||
Return(client.ContainerListResult{
|
||||
Items: []container.Summary{
|
||||
testContainer("service1", "123", false),
|
||||
},
|
||||
}, nil).
|
||||
AnyTimes()
|
||||
|
||||
api.EXPECT().ImageList(gomock.Any(), image.ListOptions{
|
||||
Filters: filters.NewArgs(
|
||||
projectFilter(strings.ToLower(testProject)),
|
||||
filters.Arg("dangling", "false"),
|
||||
),
|
||||
}).Return([]image.Summary{
|
||||
api.EXPECT().ImageList(gomock.Any(), client.ImageListOptions{
|
||||
Filters: projectFilter(strings.ToLower(testProject)).Add("dangling", "false"),
|
||||
}).Return(client.ImageListResult{Items: []image.Summary{
|
||||
{
|
||||
Labels: types.Labels{compose.ServiceLabel: "local-anonymous"},
|
||||
RepoTags: []string{"testproject-local-anonymous:latest"},
|
||||
@@ -304,7 +311,7 @@ func TestDownRemoveImages(t *testing.T) {
|
||||
Labels: types.Labels{compose.ServiceLabel: "local-named"},
|
||||
RepoTags: []string{"local-named-image:latest"},
|
||||
},
|
||||
}, nil).AnyTimes()
|
||||
}}, nil).AnyTimes()
|
||||
|
||||
imagesToBeInspected := map[string]bool{
|
||||
"testproject-local-anonymous": true,
|
||||
@@ -323,12 +330,12 @@ func TestDownRemoveImages(t *testing.T) {
|
||||
}
|
||||
|
||||
api.EXPECT().ImageInspect(gomock.Any(), img).
|
||||
Return(resp, err).
|
||||
Return(client.ImageInspectResult{InspectResponse: resp}, 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).
|
||||
Return(client.ImageInspectResult{InspectResponse: image.InspectResponse{RepoTags: []string{"registry.example.com/remote-image-tagged:v1.0"}}}, nil).
|
||||
AnyTimes()
|
||||
|
||||
localImagesToBeRemoved := []string{
|
||||
@@ -338,8 +345,8 @@ func TestDownRemoveImages(t *testing.T) {
|
||||
for _, img := range localImagesToBeRemoved {
|
||||
// test calls down --rmi=local then down --rmi=all, so local images
|
||||
// get "removed" 2x, while other images are only 1x
|
||||
api.EXPECT().ImageRemove(gomock.Any(), img, image.RemoveOptions{}).
|
||||
Return(nil, nil).
|
||||
api.EXPECT().ImageRemove(gomock.Any(), img, client.ImageRemoveOptions{}).
|
||||
Return(client.ImageRemoveResult{}, nil).
|
||||
Times(2)
|
||||
}
|
||||
|
||||
@@ -353,8 +360,8 @@ func TestDownRemoveImages(t *testing.T) {
|
||||
"registry.example.com/remote-image-tagged:v1.0",
|
||||
}
|
||||
for _, img := range otherImagesToBeRemoved {
|
||||
api.EXPECT().ImageRemove(gomock.Any(), img, image.RemoveOptions{}).
|
||||
Return(nil, nil).
|
||||
api.EXPECT().ImageRemove(gomock.Any(), img, client.ImageRemoveOptions{}).
|
||||
Return(client.ImageRemoveResult{}, nil).
|
||||
Times(1)
|
||||
}
|
||||
|
||||
@@ -375,35 +382,32 @@ func TestDownRemoveImages_NoLabel(t *testing.T) {
|
||||
ctr := testContainer("service1", "123", false)
|
||||
|
||||
api.EXPECT().ContainerList(gomock.Any(), projectFilterListOpt(false)).Return(
|
||||
[]container.Summary{ctr}, nil)
|
||||
client.ContainerListResult{
|
||||
Items: []container.Summary{ctr},
|
||||
}, nil)
|
||||
|
||||
api.EXPECT().VolumeList(
|
||||
gomock.Any(),
|
||||
volume.ListOptions{
|
||||
Filters: filters.NewArgs(projectFilter(strings.ToLower(testProject))),
|
||||
client.VolumeListOptions{
|
||||
Filters: projectFilter(strings.ToLower(testProject)),
|
||||
}).
|
||||
Return(volume.ListResponse{
|
||||
Volumes: []*volume.Volume{{Name: "myProject_volume"}},
|
||||
Return(client.VolumeListResult{
|
||||
Items: []volume.Volume{{Name: "myProject_volume"}},
|
||||
}, nil)
|
||||
api.EXPECT().NetworkList(gomock.Any(), network.ListOptions{Filters: filters.NewArgs(projectFilter(strings.ToLower(testProject)))}).
|
||||
Return(nil, nil)
|
||||
api.EXPECT().NetworkList(gomock.Any(), client.NetworkListOptions{Filters: projectFilter(strings.ToLower(testProject))}).
|
||||
Return(client.NetworkListResult{}, nil)
|
||||
|
||||
// ImageList returns no images for the project since they were unlabeled
|
||||
// (created by an older version of Compose)
|
||||
api.EXPECT().ImageList(gomock.Any(), image.ListOptions{
|
||||
Filters: filters.NewArgs(
|
||||
projectFilter(strings.ToLower(testProject)),
|
||||
filters.Arg("dangling", "false"),
|
||||
),
|
||||
}).Return(nil, nil)
|
||||
api.EXPECT().ImageList(gomock.Any(), client.ImageListOptions{
|
||||
Filters: projectFilter(strings.ToLower(testProject)).Add("dangling", "false"),
|
||||
}).Return(client.ImageListResult{}, nil)
|
||||
|
||||
api.EXPECT().ImageInspect(gomock.Any(), "testproject-service1").
|
||||
Return(image.InspectResponse{}, nil)
|
||||
api.EXPECT().ImageInspect(gomock.Any(), "testproject-service1", gomock.Any()).Return(client.ImageInspectResult{}, nil)
|
||||
api.EXPECT().ContainerStop(gomock.Any(), "123", client.ContainerStopOptions{}).Return(client.ContainerStopResult{}, nil)
|
||||
api.EXPECT().ContainerRemove(gomock.Any(), "123", client.ContainerRemoveOptions{Force: true}).Return(client.ContainerRemoveResult{}, nil)
|
||||
|
||||
api.EXPECT().ContainerStop(gomock.Any(), "123", container.StopOptions{}).Return(nil)
|
||||
api.EXPECT().ContainerRemove(gomock.Any(), "123", container.RemoveOptions{Force: true}).Return(nil)
|
||||
|
||||
api.EXPECT().ImageRemove(gomock.Any(), "testproject-service1:latest", image.RemoveOptions{}).Return(nil, nil)
|
||||
api.EXPECT().ImageRemove(gomock.Any(), "testproject-service1:latest", client.ImageRemoveOptions{}).Return(client.ImageRemoveResult{}, nil)
|
||||
|
||||
err = tested.Down(t.Context(), strings.ToLower(testProject), compose.DownOptions{Images: "local"})
|
||||
assert.NilError(t, err)
|
||||
|
||||
@@ -22,29 +22,27 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/api/types/events"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
"github.com/moby/moby/client"
|
||||
|
||||
"github.com/docker/compose/v5/pkg/api"
|
||||
)
|
||||
|
||||
func (s *composeService) Events(ctx context.Context, projectName string, options api.EventsOptions) error {
|
||||
projectName = strings.ToLower(projectName)
|
||||
evts, errors := s.apiClient().Events(ctx, events.ListOptions{
|
||||
Filters: filters.NewArgs(projectFilter(projectName)),
|
||||
res := s.apiClient().Events(ctx, client.EventsListOptions{
|
||||
Filters: projectFilter(projectName),
|
||||
Since: options.Since,
|
||||
Until: options.Until,
|
||||
})
|
||||
for {
|
||||
select {
|
||||
case event := <-evts:
|
||||
case event := <-res.Messages:
|
||||
// TODO: support other event types
|
||||
if event.Type != "container" {
|
||||
continue
|
||||
}
|
||||
|
||||
oneOff := event.Actor.Attributes[api.OneoffLabel]
|
||||
if oneOff == "True" {
|
||||
if event.Actor.Attributes[api.OneoffLabel] == "True" {
|
||||
// ignore
|
||||
continue
|
||||
}
|
||||
@@ -76,7 +74,7 @@ func (s *composeService) Events(ctx context.Context, projectName string, options
|
||||
return err
|
||||
}
|
||||
|
||||
case err := <-errors:
|
||||
case err := <-res.Err:
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ import (
|
||||
|
||||
"github.com/docker/cli/cli"
|
||||
"github.com/docker/cli/cli/command/container"
|
||||
containerType "github.com/docker/docker/api/types/container"
|
||||
containerType "github.com/moby/moby/api/types/container"
|
||||
|
||||
"github.com/docker/compose/v5/pkg/api"
|
||||
)
|
||||
|
||||
@@ -23,6 +23,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/moby/moby/client"
|
||||
"github.com/moby/sys/atomicwriter"
|
||||
|
||||
"github.com/docker/compose/v5/pkg/api"
|
||||
@@ -57,7 +58,7 @@ func (s *composeService) export(ctx context.Context, projectName string, options
|
||||
Status: api.Working,
|
||||
})
|
||||
|
||||
responseBody, err := s.apiClient().ContainerExport(ctx, container.ID)
|
||||
responseBody, err := s.apiClient().ContainerExport(ctx, container.ID, client.ContainerExportOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -19,39 +19,35 @@ package compose
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
"github.com/moby/moby/client"
|
||||
|
||||
"github.com/docker/compose/v5/pkg/api"
|
||||
)
|
||||
|
||||
func projectFilter(projectName string) filters.KeyValuePair {
|
||||
return filters.Arg("label", fmt.Sprintf("%s=%s", api.ProjectLabel, projectName))
|
||||
func projectFilter(projectName string) client.Filters {
|
||||
return make(client.Filters).Add("label", fmt.Sprintf("%s=%s", api.ProjectLabel, projectName))
|
||||
}
|
||||
|
||||
func serviceFilter(serviceName string) filters.KeyValuePair {
|
||||
return filters.Arg("label", fmt.Sprintf("%s=%s", api.ServiceLabel, serviceName))
|
||||
func serviceFilter(serviceName string) string {
|
||||
return fmt.Sprintf("%s=%s", api.ServiceLabel, serviceName)
|
||||
}
|
||||
|
||||
func networkFilter(name string) filters.KeyValuePair {
|
||||
return filters.Arg("label", fmt.Sprintf("%s=%s", api.NetworkLabel, name))
|
||||
func networkFilter(name string) string {
|
||||
return fmt.Sprintf("%s=%s", api.NetworkLabel, name)
|
||||
}
|
||||
|
||||
func oneOffFilter(b bool) filters.KeyValuePair {
|
||||
func oneOffFilter(b bool) string {
|
||||
v := "False"
|
||||
if b {
|
||||
v = "True"
|
||||
}
|
||||
return filters.Arg("label", fmt.Sprintf("%s=%s", api.OneoffLabel, v))
|
||||
return fmt.Sprintf("%s=%s", api.OneoffLabel, v)
|
||||
}
|
||||
|
||||
func containerNumberFilter(index int) filters.KeyValuePair {
|
||||
return filters.Arg("label", fmt.Sprintf("%s=%d", api.ContainerNumberLabel, index))
|
||||
func containerNumberFilter(index int) string {
|
||||
return fmt.Sprintf("%s=%d", api.ContainerNumberLabel, index)
|
||||
}
|
||||
|
||||
func hasProjectLabelFilter() filters.KeyValuePair {
|
||||
return filters.Arg("label", api.ProjectLabel)
|
||||
}
|
||||
|
||||
func hasConfigHashLabel() filters.KeyValuePair {
|
||||
return filters.Arg("label", api.ConfigHashLabel)
|
||||
func hasConfigHashLabel() string {
|
||||
return api.ConfigHashLabel
|
||||
}
|
||||
|
||||
@@ -24,38 +24,33 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
"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"
|
||||
"github.com/moby/moby/api/types/container"
|
||||
"github.com/moby/moby/api/types/mount"
|
||||
"github.com/moby/moby/api/types/network"
|
||||
"github.com/moby/moby/client"
|
||||
|
||||
"github.com/docker/compose/v5/pkg/api"
|
||||
)
|
||||
|
||||
func (s *composeService) Generate(ctx context.Context, options api.GenerateOptions) (*types.Project, error) {
|
||||
filtersListNames := filters.NewArgs()
|
||||
filtersListIDs := filters.NewArgs()
|
||||
for _, containerName := range options.Containers {
|
||||
filtersListNames.Add("name", containerName)
|
||||
filtersListIDs.Add("id", containerName)
|
||||
res, err := s.apiClient().ContainerList(ctx, client.ContainerListOptions{
|
||||
Filters: make(client.Filters).Add("name", options.Containers...),
|
||||
All: true,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
containers, err := s.apiClient().ContainerList(ctx, container.ListOptions{
|
||||
Filters: filtersListNames,
|
||||
containers := res.Items
|
||||
|
||||
containersByIds, err := s.apiClient().ContainerList(ctx, client.ContainerListOptions{
|
||||
Filters: make(client.Filters).Add("id", options.Containers...),
|
||||
All: true,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
containersByIds, err := s.apiClient().ContainerList(ctx, container.ListOptions{
|
||||
Filters: filtersListIDs,
|
||||
All: true,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, ctr := range containersByIds {
|
||||
for _, ctr := range containersByIds.Items {
|
||||
if !slices.ContainsFunc(containers, func(summary container.Summary) bool {
|
||||
return summary.ID == ctr.ID
|
||||
}) {
|
||||
@@ -97,12 +92,12 @@ func (s *composeService) createProjectFromContainers(containers []container.Summ
|
||||
}
|
||||
service.Scale = increment(service.Scale)
|
||||
|
||||
inspect, err := s.apiClient().ContainerInspect(context.Background(), c.ID)
|
||||
inspect, err := s.apiClient().ContainerInspect(context.Background(), c.ID, client.ContainerInspectOptions{})
|
||||
if err != nil {
|
||||
services[serviceLabel] = service
|
||||
continue
|
||||
}
|
||||
s.extractComposeConfiguration(&service, inspect, volumes, secrets, networks)
|
||||
s.extractComposeConfiguration(&service, inspect.Container, volumes, secrets, networks)
|
||||
service.Labels = cleanDockerPreviousLabels(service.Labels)
|
||||
services[serviceLabel] = service
|
||||
}
|
||||
@@ -136,10 +131,10 @@ func (s *composeService) extractComposeConfiguration(service *types.ServiceConfi
|
||||
for key, portBindings := range inspect.HostConfig.PortBindings {
|
||||
for _, portBinding := range portBindings {
|
||||
service.Ports = append(service.Ports, types.ServicePortConfig{
|
||||
Target: uint32(key.Int()),
|
||||
Target: uint32(key.Num()),
|
||||
Published: portBinding.HostPort,
|
||||
Protocol: key.Proto(),
|
||||
HostIP: portBinding.HostIP,
|
||||
Protocol: string(key.Proto()),
|
||||
HostIP: portBinding.HostIP.String(),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -222,12 +217,12 @@ func (s *composeService) toComposeNetwork(networks map[string]*network.EndpointS
|
||||
serviceNetworkConfigs := make(map[string]*types.ServiceNetworkConfig)
|
||||
|
||||
for name, net := range networks {
|
||||
inspect, err := s.apiClient().NetworkInspect(context.Background(), name, network.InspectOptions{})
|
||||
inspect, err := s.apiClient().NetworkInspect(context.Background(), name, client.NetworkInspectOptions{})
|
||||
if err != nil {
|
||||
networkConfigs[name] = types.NetworkConfig{}
|
||||
} else {
|
||||
networkConfigs[name] = types.NetworkConfig{
|
||||
Internal: inspect.Internal,
|
||||
Internal: inspect.Network.Internal,
|
||||
}
|
||||
}
|
||||
serviceNetworkConfigs[name] = &types.ServiceNetworkConfig{
|
||||
|
||||
@@ -23,8 +23,9 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/pkg/stdcopy"
|
||||
"github.com/moby/moby/api/pkg/stdcopy"
|
||||
"github.com/moby/moby/api/types/container"
|
||||
"github.com/moby/moby/client"
|
||||
|
||||
"github.com/docker/compose/v5/pkg/api"
|
||||
"github.com/docker/compose/v5/pkg/utils"
|
||||
@@ -43,7 +44,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().ExecCreate(ctx, ctr.ID, client.ExecCreateOptions{
|
||||
User: hook.User,
|
||||
Privileged: hook.Privileged,
|
||||
Env: ToMobyEnv(hook.Environment),
|
||||
@@ -61,9 +62,12 @@ func (s composeService) runHook(ctx context.Context, ctr container.Summary, serv
|
||||
}
|
||||
|
||||
height, width := s.stdout().GetTtySize()
|
||||
consoleSize := &[2]uint{height, width}
|
||||
attach, err := s.apiClient().ContainerExecAttach(ctx, exec.ID, container.ExecAttachOptions{
|
||||
Tty: service.Tty,
|
||||
consoleSize := client.ConsoleSize{
|
||||
Width: width,
|
||||
Height: height,
|
||||
}
|
||||
attach, err := s.apiClient().ExecAttach(ctx, exec.ID, client.ExecAttachOptions{
|
||||
TTY: service.Tty,
|
||||
ConsoleSize: consoleSize,
|
||||
})
|
||||
if err != nil {
|
||||
@@ -80,7 +84,7 @@ func (s composeService) runHook(ctx context.Context, ctr container.Summary, serv
|
||||
return err
|
||||
}
|
||||
|
||||
inspected, err := s.apiClient().ContainerExecInspect(ctx, exec.ID)
|
||||
inspected, err := s.apiClient().ExecInspect(ctx, exec.ID, client.ExecInspectOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -91,9 +95,9 @@ func (s composeService) runHook(ctx context.Context, ctr container.Summary, serv
|
||||
}
|
||||
|
||||
func (s composeService) runWaitExec(ctx context.Context, execID string, service types.ServiceConfig, listener api.ContainerEventListener) error {
|
||||
err := s.apiClient().ContainerExecStart(ctx, execID, container.ExecStartOptions{
|
||||
_, err := s.apiClient().ExecStart(ctx, execID, client.ExecStartOptions{
|
||||
Detach: listener == nil,
|
||||
Tty: service.Tty,
|
||||
TTY: service.Tty,
|
||||
})
|
||||
if err != nil {
|
||||
return nil
|
||||
@@ -106,7 +110,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().ExecInspect(ctx, execID, client.ExecInspectOptions{})
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -25,9 +25,8 @@ import (
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
"github.com/containerd/errdefs"
|
||||
"github.com/distribution/reference"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
"github.com/docker/docker/api/types/image"
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/moby/moby/api/types/image"
|
||||
"github.com/moby/moby/client"
|
||||
"golang.org/x/sync/errgroup"
|
||||
|
||||
"github.com/docker/compose/v5/pkg/api"
|
||||
@@ -150,23 +149,19 @@ func (p *ImagePruner) namedImages(ctx context.Context) ([]string, error) {
|
||||
// The image name could either have been defined by the user or implicitly
|
||||
// created from the project + service name.
|
||||
func (p *ImagePruner) labeledLocalImages(ctx context.Context) ([]image.Summary, error) {
|
||||
imageListOpts := image.ListOptions{
|
||||
Filters: filters.NewArgs(
|
||||
projectFilter(p.project.Name),
|
||||
// TODO(milas): we should really clean up the dangling images as
|
||||
// well (historically we have NOT); need to refactor this to handle
|
||||
// it gracefully without producing confusing CLI output, i.e. we
|
||||
// do not want to print out a bunch of untagged/dangling image IDs,
|
||||
// they should be grouped into a logical operation for the relevant
|
||||
// service
|
||||
filters.Arg("dangling", "false"),
|
||||
),
|
||||
}
|
||||
projectImages, err := p.client.ImageList(ctx, imageListOpts)
|
||||
res, err := p.client.ImageList(ctx, client.ImageListOptions{
|
||||
// TODO(milas): we should really clean up the dangling images as
|
||||
// well (historically we have NOT); need to refactor this to handle
|
||||
// it gracefully without producing confusing CLI output, i.e. we
|
||||
// do not want to print out a bunch of untagged/dangling image IDs,
|
||||
// they should be grouped into a logical operation for the relevant
|
||||
// service
|
||||
Filters: projectFilter(p.project.Name).Add("dangling", "false"),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return projectImages, nil
|
||||
return res.Items, nil
|
||||
}
|
||||
|
||||
// unlabeledLocalImages are images that match the implicit naming convention
|
||||
|
||||
@@ -27,10 +27,9 @@ import (
|
||||
"github.com/containerd/errdefs"
|
||||
"github.com/containerd/platforms"
|
||||
"github.com/distribution/reference"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
"github.com/docker/docker/api/types/versions"
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/moby/moby/api/types/container"
|
||||
"github.com/moby/moby/client"
|
||||
"github.com/moby/moby/client/pkg/versions"
|
||||
"golang.org/x/sync/errgroup"
|
||||
|
||||
"github.com/docker/compose/v5/pkg/api"
|
||||
@@ -38,9 +37,9 @@ import (
|
||||
|
||||
func (s *composeService) Images(ctx context.Context, projectName string, options api.ImagesOptions) (map[string]api.ImageSummary, error) {
|
||||
projectName = strings.ToLower(projectName)
|
||||
allContainers, err := s.apiClient().ContainerList(ctx, container.ListOptions{
|
||||
allContainers, err := s.apiClient().ContainerList(ctx, client.ContainerListOptions{
|
||||
All: true,
|
||||
Filters: filters.NewArgs(projectFilter(projectName)),
|
||||
Filters: projectFilter(projectName),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -48,20 +47,20 @@ func (s *composeService) Images(ctx context.Context, projectName string, options
|
||||
var containers []container.Summary
|
||||
if len(options.Services) > 0 {
|
||||
// filter service containers
|
||||
for _, c := range allContainers {
|
||||
for _, c := range allContainers.Items {
|
||||
if slices.Contains(options.Services, c.Labels[api.ServiceLabel]) {
|
||||
containers = append(containers, c)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
containers = allContainers
|
||||
containers = allContainers.Items
|
||||
}
|
||||
|
||||
version, err := s.RuntimeVersion(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
withPlatform := versions.GreaterThanOrEqualTo(version, APIVersion149)
|
||||
withPlatform := versions.GreaterThanOrEqualTo(version, apiVersion149)
|
||||
|
||||
summary := map[string]api.ImageSummary{}
|
||||
var mux sync.Mutex
|
||||
|
||||
@@ -17,14 +17,14 @@
|
||||
package compose
|
||||
|
||||
import (
|
||||
"net/netip"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
"github.com/docker/docker/api/types/image"
|
||||
"github.com/moby/moby/api/types/container"
|
||||
"github.com/moby/moby/api/types/image"
|
||||
"github.com/moby/moby/client"
|
||||
"go.uber.org/mock/gomock"
|
||||
"gotest.tools/v3/assert"
|
||||
|
||||
@@ -39,22 +39,24 @@ func TestImages(t *testing.T) {
|
||||
tested, err := NewComposeService(cli)
|
||||
assert.NilError(t, err)
|
||||
|
||||
args := filters.NewArgs(projectFilter(strings.ToLower(testProject)))
|
||||
listOpts := container.ListOptions{All: true, Filters: args}
|
||||
api.EXPECT().ServerVersion(gomock.Any()).Return(types.Version{APIVersion: "1.96"}, nil).AnyTimes()
|
||||
args := projectFilter(strings.ToLower(testProject))
|
||||
listOpts := client.ContainerListOptions{All: true, Filters: args}
|
||||
api.EXPECT().ServerVersion(gomock.Any(), gomock.Any()).Return(client.ServerVersionResult{APIVersion: "1.96"}, nil).AnyTimes()
|
||||
timeStr1 := "2025-06-06T06:06:06.000000000Z"
|
||||
created1, _ := time.Parse(time.RFC3339Nano, timeStr1)
|
||||
timeStr2 := "2025-03-03T03:03:03.000000000Z"
|
||||
created2, _ := time.Parse(time.RFC3339Nano, timeStr2)
|
||||
image1 := imageInspect("image1", "foo:1", 12345, timeStr1)
|
||||
image2 := imageInspect("image2", "bar:2", 67890, timeStr2)
|
||||
api.EXPECT().ImageInspect(anyCancellableContext(), "foo:1").Return(image1, nil).MaxTimes(2)
|
||||
api.EXPECT().ImageInspect(anyCancellableContext(), "bar:2").Return(image2, 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"}}
|
||||
c3 := containerDetail("service2", "789", "exited", "foo:1")
|
||||
api.EXPECT().ContainerList(t.Context(), listOpts).Return([]container.Summary{c1, c2, c3}, nil)
|
||||
api.EXPECT().ImageInspect(anyCancellableContext(), "foo:1").Return(client.ImageInspectResult{InspectResponse: image1}, nil).MaxTimes(2)
|
||||
api.EXPECT().ImageInspect(anyCancellableContext(), "bar:2").Return(client.ImageInspectResult{InspectResponse: image2}, nil)
|
||||
c1 := containerDetail("service1", "123", container.StateRunning, "foo:1")
|
||||
c2 := containerDetail("service1", "456", container.StateRunning, "bar:2")
|
||||
c2.Ports = []container.PortSummary{{PublicPort: 80, PrivatePort: 90, IP: netip.MustParseAddr("127.0.0.1")}}
|
||||
c3 := containerDetail("service2", "789", container.StateExited, "foo:1")
|
||||
api.EXPECT().ContainerList(t.Context(), listOpts).Return(client.ContainerListResult{
|
||||
Items: []container.Summary{c1, c2, c3},
|
||||
}, nil)
|
||||
|
||||
images, err := tested.Images(t.Context(), strings.ToLower(testProject), compose.ImagesOptions{})
|
||||
|
||||
@@ -97,7 +99,7 @@ func imageInspect(id string, imageReference string, size int64, created string)
|
||||
}
|
||||
}
|
||||
|
||||
func containerDetail(service string, id string, status string, imageName string) container.Summary {
|
||||
func containerDetail(service string, id string, status container.ContainerState, imageName string) container.Summary {
|
||||
return container.Summary{
|
||||
ID: id,
|
||||
Names: []string{"/" + id},
|
||||
|
||||
@@ -20,7 +20,8 @@ import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/moby/moby/api/types/container"
|
||||
"github.com/moby/moby/client"
|
||||
"golang.org/x/sync/errgroup"
|
||||
|
||||
"github.com/docker/compose/v5/pkg/api"
|
||||
@@ -61,7 +62,9 @@ func (s *composeService) kill(ctx context.Context, projectName string, options a
|
||||
eg.Go(func() error {
|
||||
eventName := getContainerProgressName(ctr)
|
||||
s.events.On(killingEvent(eventName))
|
||||
err := s.apiClient().ContainerKill(ctx, ctr.ID, options.Signal)
|
||||
_, err := s.apiClient().ContainerKill(ctx, ctr.ID, client.ContainerKillOptions{
|
||||
Signal: options.Signal,
|
||||
})
|
||||
if err != nil {
|
||||
s.events.On(errorEvent(eventName, "Error while Killing"))
|
||||
return err
|
||||
|
||||
@@ -18,15 +18,13 @@ package compose
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"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"
|
||||
"github.com/moby/moby/api/types/container"
|
||||
"github.com/moby/moby/api/types/network"
|
||||
"github.com/moby/moby/client"
|
||||
"go.uber.org/mock/gomock"
|
||||
"gotest.tools/v3/assert"
|
||||
|
||||
@@ -45,23 +43,30 @@ func TestKillAll(t *testing.T) {
|
||||
|
||||
name := strings.ToLower(testProject)
|
||||
|
||||
api.EXPECT().ContainerList(t.Context(), container.ListOptions{
|
||||
Filters: filters.NewArgs(projectFilter(name), hasConfigHashLabel()),
|
||||
}).Return(
|
||||
[]container.Summary{testContainer("service1", "123", false), testContainer("service1", "456", false), testContainer("service2", "789", false)}, nil)
|
||||
api.EXPECT().ContainerList(t.Context(), client.ContainerListOptions{
|
||||
Filters: projectFilter(name).Add("label", hasConfigHashLabel()),
|
||||
}).Return(client.ContainerListResult{
|
||||
Items: []container.Summary{
|
||||
testContainer("service1", "123", false),
|
||||
testContainer("service1", "456", false),
|
||||
testContainer("service2", "789", false),
|
||||
},
|
||||
}, nil)
|
||||
api.EXPECT().VolumeList(
|
||||
gomock.Any(),
|
||||
volume.ListOptions{
|
||||
Filters: filters.NewArgs(projectFilter(strings.ToLower(testProject))),
|
||||
client.VolumeListOptions{
|
||||
Filters: projectFilter(strings.ToLower(testProject)),
|
||||
}).
|
||||
Return(volume.ListResponse{}, nil)
|
||||
api.EXPECT().NetworkList(gomock.Any(), network.ListOptions{Filters: filters.NewArgs(projectFilter(strings.ToLower(testProject)))}).
|
||||
Return([]network.Summary{
|
||||
{ID: "abc123", Name: "testProject_default"},
|
||||
Return(client.VolumeListResult{}, nil)
|
||||
api.EXPECT().NetworkList(gomock.Any(), client.NetworkListOptions{Filters: projectFilter(strings.ToLower(testProject))}).
|
||||
Return(client.NetworkListResult{
|
||||
Items: []network.Summary{{
|
||||
Network: network.Network{ID: "abc123", Name: "testProject_default"},
|
||||
}},
|
||||
}, nil)
|
||||
api.EXPECT().ContainerKill(anyCancellableContext(), "123", "").Return(nil)
|
||||
api.EXPECT().ContainerKill(anyCancellableContext(), "456", "").Return(nil)
|
||||
api.EXPECT().ContainerKill(anyCancellableContext(), "789", "").Return(nil)
|
||||
api.EXPECT().ContainerKill(anyCancellableContext(), "123", client.ContainerKillOptions{}).Return(client.ContainerKillResult{}, nil)
|
||||
api.EXPECT().ContainerKill(anyCancellableContext(), "456", client.ContainerKillOptions{}).Return(client.ContainerKillResult{}, nil)
|
||||
api.EXPECT().ContainerKill(anyCancellableContext(), "789", client.ContainerKillOptions{}).Return(client.ContainerKillResult{}, nil)
|
||||
|
||||
err = tested.Kill(t.Context(), name, compose.KillOptions{})
|
||||
assert.NilError(t, err)
|
||||
@@ -77,22 +82,28 @@ func TestKillSignal(t *testing.T) {
|
||||
assert.NilError(t, err)
|
||||
|
||||
name := strings.ToLower(testProject)
|
||||
listOptions := container.ListOptions{
|
||||
Filters: filters.NewArgs(projectFilter(name), serviceFilter(serviceName), hasConfigHashLabel()),
|
||||
listOptions := client.ContainerListOptions{
|
||||
Filters: projectFilter(name).Add("label", serviceFilter(serviceName), hasConfigHashLabel()),
|
||||
}
|
||||
|
||||
api.EXPECT().ContainerList(t.Context(), listOptions).Return([]container.Summary{testContainer(serviceName, "123", false)}, nil)
|
||||
api.EXPECT().ContainerList(t.Context(), listOptions).Return(client.ContainerListResult{
|
||||
Items: []container.Summary{testContainer(serviceName, "123", false)},
|
||||
}, nil)
|
||||
api.EXPECT().VolumeList(
|
||||
gomock.Any(),
|
||||
volume.ListOptions{
|
||||
Filters: filters.NewArgs(projectFilter(strings.ToLower(testProject))),
|
||||
client.VolumeListOptions{
|
||||
Filters: projectFilter(strings.ToLower(testProject)),
|
||||
}).
|
||||
Return(volume.ListResponse{}, nil)
|
||||
api.EXPECT().NetworkList(gomock.Any(), network.ListOptions{Filters: filters.NewArgs(projectFilter(strings.ToLower(testProject)))}).
|
||||
Return([]network.Summary{
|
||||
{ID: "abc123", Name: "testProject_default"},
|
||||
Return(client.VolumeListResult{}, nil)
|
||||
api.EXPECT().NetworkList(gomock.Any(), client.NetworkListOptions{Filters: projectFilter(strings.ToLower(testProject))}).
|
||||
Return(client.NetworkListResult{
|
||||
Items: []network.Summary{{
|
||||
Network: network.Network{ID: "abc123", Name: "testProject_default"},
|
||||
}},
|
||||
}, nil)
|
||||
api.EXPECT().ContainerKill(anyCancellableContext(), "123", "SIGTERM").Return(nil)
|
||||
api.EXPECT().ContainerKill(anyCancellableContext(), "123", client.ContainerKillOptions{
|
||||
Signal: "SIGTERM",
|
||||
}).Return(client.ContainerKillResult{}, nil)
|
||||
|
||||
err = tested.Kill(t.Context(), name, compose.KillOptions{Services: []string{serviceName}, Signal: "SIGTERM"})
|
||||
assert.NilError(t, err)
|
||||
@@ -133,15 +144,12 @@ func anyCancellableContext() gomock.Matcher {
|
||||
return gomock.AssignableToTypeOf(ctxWithCancel)
|
||||
}
|
||||
|
||||
func projectFilterListOpt(withOneOff bool) container.ListOptions {
|
||||
filter := filters.NewArgs(
|
||||
projectFilter(strings.ToLower(testProject)),
|
||||
hasConfigHashLabel(),
|
||||
)
|
||||
func projectFilterListOpt(withOneOff bool) client.ContainerListOptions {
|
||||
filter := projectFilter(strings.ToLower(testProject)).Add("label", hasConfigHashLabel())
|
||||
if !withOneOff {
|
||||
filter.Add("label", fmt.Sprintf("%s=False", compose.OneoffLabel))
|
||||
filter.Add("label", oneOffFilter(false))
|
||||
}
|
||||
return container.ListOptions{
|
||||
return client.ContainerListOptions{
|
||||
Filters: filter,
|
||||
All: true,
|
||||
}
|
||||
|
||||
@@ -21,8 +21,9 @@ import (
|
||||
"io"
|
||||
|
||||
"github.com/containerd/errdefs"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/pkg/stdcopy"
|
||||
"github.com/moby/moby/api/pkg/stdcopy"
|
||||
"github.com/moby/moby/api/types/container"
|
||||
"github.com/moby/moby/client"
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/sync/errgroup"
|
||||
|
||||
@@ -83,14 +84,14 @@ func (s *composeService) Logs(
|
||||
monitor.withListener(func(event api.ContainerEvent) {
|
||||
if event.Type == api.ContainerEventStarted {
|
||||
eg.Go(func() error {
|
||||
ctr, err := s.apiClient().ContainerInspect(ctx, event.ID)
|
||||
res, err := s.apiClient().ContainerInspect(ctx, event.ID, client.ContainerInspectOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = s.doLogContainer(ctx, consumer, event.Source, ctr, api.LogOptions{
|
||||
err = s.doLogContainer(ctx, consumer, event.Source, res.Container, api.LogOptions{
|
||||
Follow: options.Follow,
|
||||
Since: ctr.State.StartedAt,
|
||||
Since: res.Container.State.StartedAt,
|
||||
Until: options.Until,
|
||||
Tail: options.Tail,
|
||||
Timestamps: options.Timestamps,
|
||||
@@ -113,16 +114,16 @@ func (s *composeService) Logs(
|
||||
}
|
||||
|
||||
func (s *composeService) logContainer(ctx context.Context, consumer api.LogConsumer, c container.Summary, options api.LogOptions) error {
|
||||
ctr, err := s.apiClient().ContainerInspect(ctx, c.ID)
|
||||
res, err := s.apiClient().ContainerInspect(ctx, c.ID, client.ContainerInspectOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
name := getContainerNameWithoutProject(c)
|
||||
return s.doLogContainer(ctx, consumer, name, ctr, options)
|
||||
return s.doLogContainer(ctx, consumer, name, res.Container, options)
|
||||
}
|
||||
|
||||
func (s *composeService) doLogContainer(ctx context.Context, consumer api.LogConsumer, name string, ctr container.InspectResponse, options api.LogOptions) error {
|
||||
r, err := s.apiClient().ContainerLogs(ctx, ctr.ID, container.LogsOptions{
|
||||
r, err := s.apiClient().ContainerLogs(ctx, ctr.ID, client.ContainerLogsOptions{
|
||||
ShowStdout: true,
|
||||
ShowStderr: true,
|
||||
Follow: options.Follow,
|
||||
|
||||
@@ -23,9 +23,9 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
containerType "github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
"github.com/docker/docker/pkg/stdcopy"
|
||||
containerType "github.com/moby/moby/api/types/container"
|
||||
"github.com/moby/moby/client"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.uber.org/mock/gomock"
|
||||
@@ -43,21 +43,25 @@ func TestComposeService_Logs_Demux(t *testing.T) {
|
||||
|
||||
name := strings.ToLower(testProject)
|
||||
|
||||
api.EXPECT().ContainerList(t.Context(), containerType.ListOptions{
|
||||
api.EXPECT().ContainerList(t.Context(), client.ContainerListOptions{
|
||||
All: true,
|
||||
Filters: filters.NewArgs(oneOffFilter(false), projectFilter(name), hasConfigHashLabel()),
|
||||
Filters: projectFilter(name).Add("label", oneOffFilter(false), hasConfigHashLabel()),
|
||||
}).Return(
|
||||
[]containerType.Summary{
|
||||
testContainer("service", "c", false),
|
||||
client.ContainerListResult{
|
||||
Items: []containerType.Summary{
|
||||
testContainer("service", "c", false),
|
||||
},
|
||||
},
|
||||
nil,
|
||||
)
|
||||
|
||||
api.EXPECT().
|
||||
ContainerInspect(anyCancellableContext(), "c").
|
||||
Return(containerType.InspectResponse{
|
||||
ContainerJSONBase: &containerType.ContainerJSONBase{ID: "c"},
|
||||
Config: &containerType.Config{Tty: false},
|
||||
ContainerInspect(anyCancellableContext(), "c", gomock.Any()).
|
||||
Return(client.ContainerInspectResult{
|
||||
Container: containerType.InspectResponse{
|
||||
ID: "c",
|
||||
Config: &containerType.Config{Tty: false},
|
||||
},
|
||||
}, nil)
|
||||
c1Reader, c1Writer := io.Pipe()
|
||||
t.Cleanup(func() {
|
||||
@@ -112,28 +116,32 @@ func TestComposeService_Logs_ServiceFiltering(t *testing.T) {
|
||||
|
||||
name := strings.ToLower(testProject)
|
||||
|
||||
api.EXPECT().ContainerList(t.Context(), containerType.ListOptions{
|
||||
api.EXPECT().ContainerList(t.Context(), client.ContainerListOptions{
|
||||
All: true,
|
||||
Filters: filters.NewArgs(oneOffFilter(false), projectFilter(name), hasConfigHashLabel()),
|
||||
Filters: projectFilter(name).Add("label", oneOffFilter(false), hasConfigHashLabel()),
|
||||
}).Return(
|
||||
[]containerType.Summary{
|
||||
testContainer("serviceA", "c1", false),
|
||||
testContainer("serviceA", "c2", false),
|
||||
// serviceB will be filtered out by the project definition to
|
||||
// ensure we ignore "orphan" containers
|
||||
testContainer("serviceB", "c3", false),
|
||||
testContainer("serviceC", "c4", false),
|
||||
client.ContainerListResult{
|
||||
Items: []containerType.Summary{
|
||||
testContainer("serviceA", "c1", false),
|
||||
testContainer("serviceA", "c2", false),
|
||||
// serviceB will be filtered out by the project definition to
|
||||
// ensure we ignore "orphan" containers
|
||||
testContainer("serviceB", "c3", false),
|
||||
testContainer("serviceC", "c4", false),
|
||||
},
|
||||
},
|
||||
nil,
|
||||
)
|
||||
|
||||
for _, id := range []string{"c1", "c2", "c4"} {
|
||||
api.EXPECT().
|
||||
ContainerInspect(anyCancellableContext(), id).
|
||||
ContainerInspect(anyCancellableContext(), id, gomock.Any()).
|
||||
Return(
|
||||
containerType.InspectResponse{
|
||||
ContainerJSONBase: &containerType.ContainerJSONBase{ID: id},
|
||||
Config: &containerType.Config{Tty: true},
|
||||
client.ContainerInspectResult{
|
||||
Container: containerType.InspectResponse{
|
||||
ID: id,
|
||||
Config: &containerType.Config{Tty: true},
|
||||
},
|
||||
},
|
||||
nil,
|
||||
)
|
||||
|
||||
@@ -23,23 +23,23 @@ import (
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
"github.com/moby/moby/api/types/container"
|
||||
"github.com/moby/moby/client"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/docker/compose/v5/pkg/api"
|
||||
)
|
||||
|
||||
func (s *composeService) List(ctx context.Context, opts api.ListOptions) ([]api.Stack, error) {
|
||||
list, err := s.apiClient().ContainerList(ctx, container.ListOptions{
|
||||
Filters: filters.NewArgs(hasProjectLabelFilter(), hasConfigHashLabel()),
|
||||
list, err := s.apiClient().ContainerList(ctx, client.ContainerListOptions{
|
||||
Filters: make(client.Filters).Add("label", api.ProjectLabel).Add("label", api.ConfigHashLabel),
|
||||
All: opts.All,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return containersToStacks(list)
|
||||
return containersToStacks(list.Items)
|
||||
}
|
||||
|
||||
func containersToStacks(containers []container.Summary) ([]api.Stack, error) {
|
||||
@@ -87,7 +87,7 @@ func combinedConfigFiles(containers []container.Summary) (string, error) {
|
||||
func containerToState(containers []container.Summary) []string {
|
||||
statuses := []string{}
|
||||
for _, c := range containers {
|
||||
statuses = append(statuses, c.State)
|
||||
statuses = append(statuses, string(c.State))
|
||||
}
|
||||
return statuses
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/moby/moby/api/types/container"
|
||||
"gotest.tools/v3/assert"
|
||||
|
||||
"github.com/docker/compose/v5/pkg/api"
|
||||
|
||||
@@ -21,10 +21,8 @@ import (
|
||||
"strconv"
|
||||
|
||||
"github.com/containerd/errdefs"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/events"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/moby/moby/api/types/events"
|
||||
"github.com/moby/moby/client"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/docker/compose/v5/pkg/api"
|
||||
@@ -58,10 +56,9 @@ func (c *monitor) withServices(services []string) {
|
||||
//nolint:gocyclo
|
||||
func (c *monitor) Start(ctx context.Context) error {
|
||||
// collect initial application container
|
||||
initialState, err := c.apiClient.ContainerList(ctx, container.ListOptions{
|
||||
initialState, err := c.apiClient.ContainerList(ctx, client.ContainerListOptions{
|
||||
All: true,
|
||||
Filters: filters.NewArgs(
|
||||
projectFilter(c.project),
|
||||
Filters: projectFilter(c.project).Add("label",
|
||||
oneOffFilter(false),
|
||||
hasConfigHashLabel(),
|
||||
),
|
||||
@@ -72,17 +69,15 @@ func (c *monitor) Start(ctx context.Context) error {
|
||||
|
||||
// containers is the set if container IDs the application is based on
|
||||
containers := utils.Set[string]{}
|
||||
for _, ctr := range initialState {
|
||||
for _, ctr := range initialState.Items {
|
||||
if len(c.services) == 0 || c.services[ctr.Labels[api.ServiceLabel]] {
|
||||
containers.Add(ctr.ID)
|
||||
}
|
||||
}
|
||||
restarting := utils.Set[string]{}
|
||||
|
||||
evtCh, errCh := c.apiClient.Events(ctx, events.ListOptions{
|
||||
Filters: filters.NewArgs(
|
||||
filters.Arg("type", "container"),
|
||||
projectFilter(c.project)),
|
||||
res := c.apiClient.Events(ctx, client.EventsListOptions{
|
||||
Filters: projectFilter(c.project).Add("type", "container"),
|
||||
})
|
||||
for {
|
||||
if len(containers) == 0 {
|
||||
@@ -91,9 +86,9 @@ func (c *monitor) Start(ctx context.Context) error {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil
|
||||
case err := <-errCh:
|
||||
case err := <-res.Err:
|
||||
return err
|
||||
case event := <-evtCh:
|
||||
case event := <-res.Messages:
|
||||
if len(c.services) > 0 && !c.services[event.Actor.Attributes[api.ServiceLabel]] {
|
||||
continue
|
||||
}
|
||||
@@ -140,14 +135,14 @@ func (c *monitor) Start(ctx context.Context) error {
|
||||
logrus.Debugf("container %s restarted", ctr.Name)
|
||||
case events.ActionDie:
|
||||
logrus.Debugf("container %s exited with code %d", ctr.Name, ctr.ExitCode)
|
||||
inspect, err := c.apiClient.ContainerInspect(ctx, event.Actor.ID)
|
||||
inspect, err := c.apiClient.ContainerInspect(ctx, event.Actor.ID, client.ContainerInspectOptions{})
|
||||
if errdefs.IsNotFound(err) {
|
||||
// Source is already removed
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if inspect.State != nil && inspect.State.Restarting || inspect.State.Running {
|
||||
if inspect.Container.State != nil && (inspect.Container.State.Restarting || inspect.Container.State.Running) {
|
||||
// State.Restarting is set by engine when container is configured to restart on exit
|
||||
// on ContainerRestart it doesn't (see https://github.com/moby/moby/issues/45538)
|
||||
// container state still is reported as "running"
|
||||
|
||||
@@ -20,7 +20,8 @@ import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/moby/moby/api/types/container"
|
||||
"github.com/moby/moby/client"
|
||||
"golang.org/x/sync/errgroup"
|
||||
|
||||
"github.com/docker/compose/v5/pkg/api"
|
||||
@@ -45,7 +46,7 @@ func (s *composeService) pause(ctx context.Context, projectName string, options
|
||||
eg, ctx := errgroup.WithContext(ctx)
|
||||
containers.forEach(func(container container.Summary) {
|
||||
eg.Go(func() error {
|
||||
err := s.apiClient().ContainerPause(ctx, container.ID)
|
||||
_, err := s.apiClient().ContainerPause(ctx, container.ID, client.ContainerPauseOptions{})
|
||||
if err == nil {
|
||||
eventName := getContainerProgressName(container)
|
||||
s.events.On(newEvent(eventName, api.Done, "Paused"))
|
||||
@@ -75,7 +76,7 @@ func (s *composeService) unPause(ctx context.Context, projectName string, option
|
||||
eg, ctx := errgroup.WithContext(ctx)
|
||||
containers.forEach(func(ctr container.Summary) {
|
||||
eg.Go(func() error {
|
||||
err = s.apiClient().ContainerUnpause(ctx, ctr.ID)
|
||||
_, err = s.apiClient().ContainerUnpause(ctx, ctr.ID, client.ContainerUnpauseOptions{})
|
||||
if err == nil {
|
||||
eventName := getContainerProgressName(ctr)
|
||||
s.events.On(newEvent(eventName, api.Done, "Unpaused"))
|
||||
|
||||
@@ -21,7 +21,7 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/moby/moby/api/types/container"
|
||||
|
||||
"github.com/docker/compose/v5/pkg/api"
|
||||
)
|
||||
@@ -34,7 +34,7 @@ func (s *composeService) Port(ctx context.Context, projectName string, service s
|
||||
}
|
||||
for _, p := range ctr.Ports {
|
||||
if p.PrivatePort == port && p.Type == options.Protocol {
|
||||
return p.IP, int(p.PublicPort), nil
|
||||
return p.IP.String(), int(p.PublicPort), nil
|
||||
}
|
||||
}
|
||||
return "", 0, portNotFoundError(options.Protocol, port, ctr)
|
||||
|
||||
@@ -21,12 +21,14 @@ import (
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/moby/moby/api/types/container"
|
||||
"github.com/moby/moby/client"
|
||||
"golang.org/x/sync/errgroup"
|
||||
|
||||
"github.com/docker/compose/v5/pkg/api"
|
||||
)
|
||||
|
||||
//nolint:gocyclo
|
||||
func (s *composeService) Ps(ctx context.Context, projectName string, options api.PsOptions) ([]api.ContainerSummary, error) {
|
||||
projectName = strings.ToLower(projectName)
|
||||
oneOff := oneOffExclude
|
||||
@@ -50,15 +52,19 @@ func (s *composeService) Ps(ctx context.Context, projectName string, options api
|
||||
return ctr.Ports[i].PrivatePort < ctr.Ports[j].PrivatePort
|
||||
})
|
||||
for i, p := range ctr.Ports {
|
||||
var url string
|
||||
if p.IP.IsValid() {
|
||||
url = p.IP.String()
|
||||
}
|
||||
publishers[i] = api.PortPublisher{
|
||||
URL: p.IP,
|
||||
URL: url, // TODO(thaJeztah); change this to a netip.Addr ??
|
||||
TargetPort: int(p.PrivatePort),
|
||||
PublishedPort: int(p.PublicPort),
|
||||
Protocol: p.Type,
|
||||
}
|
||||
}
|
||||
|
||||
inspect, err := s.apiClient().ContainerInspect(ctx, ctr.ID)
|
||||
inspect, err := s.apiClient().ContainerInspect(ctx, ctr.ID, client.ContainerInspectOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -67,14 +73,14 @@ func (s *composeService) Ps(ctx context.Context, projectName string, options api
|
||||
health container.HealthStatus
|
||||
exitCode int
|
||||
)
|
||||
if inspect.State != nil {
|
||||
switch inspect.State.Status {
|
||||
if inspect.Container.State != nil {
|
||||
switch inspect.Container.State.Status {
|
||||
case container.StateRunning:
|
||||
if inspect.State.Health != nil {
|
||||
health = inspect.State.Health.Status
|
||||
if inspect.Container.State.Health != nil {
|
||||
health = inspect.Container.State.Health.Status
|
||||
}
|
||||
case container.StateExited, container.StateDead:
|
||||
exitCode = inspect.State.ExitCode
|
||||
exitCode = inspect.Container.State.ExitCode
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,11 +17,12 @@
|
||||
package compose
|
||||
|
||||
import (
|
||||
"net/netip"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
containerType "github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
containerType "github.com/moby/moby/api/types/container"
|
||||
"github.com/moby/moby/client"
|
||||
"go.uber.org/mock/gomock"
|
||||
"gotest.tools/v3/assert"
|
||||
|
||||
@@ -36,17 +37,20 @@ func TestPs(t *testing.T) {
|
||||
tested, err := NewComposeService(cli)
|
||||
assert.NilError(t, err)
|
||||
|
||||
args := filters.NewArgs(projectFilter(strings.ToLower(testProject)), hasConfigHashLabel())
|
||||
args.Add("label", "com.docker.compose.oneoff=False")
|
||||
listOpts := containerType.ListOptions{Filters: args, All: false}
|
||||
listOpts := client.ContainerListOptions{
|
||||
Filters: projectFilter(strings.ToLower(testProject)).Add("label", hasConfigHashLabel(), oneOffFilter(false)),
|
||||
All: false,
|
||||
}
|
||||
c1, inspect1 := containerDetails("service1", "123", containerType.StateRunning, containerType.Healthy, 0)
|
||||
c2, inspect2 := containerDetails("service1", "456", containerType.StateRunning, "", 0)
|
||||
c2.Ports = []containerType.Port{{PublicPort: 80, PrivatePort: 90, IP: "localhost"}}
|
||||
c2.Ports = []containerType.PortSummary{{PublicPort: 80, PrivatePort: 90, IP: netip.MustParseAddr("127.0.0.1")}}
|
||||
c3, inspect3 := containerDetails("service2", "789", containerType.StateExited, "", 130)
|
||||
api.EXPECT().ContainerList(t.Context(), listOpts).Return([]containerType.Summary{c1, c2, c3}, nil)
|
||||
api.EXPECT().ContainerInspect(anyCancellableContext(), "123").Return(inspect1, nil)
|
||||
api.EXPECT().ContainerInspect(anyCancellableContext(), "456").Return(inspect2, nil)
|
||||
api.EXPECT().ContainerInspect(anyCancellableContext(), "789").Return(inspect3, nil)
|
||||
api.EXPECT().ContainerList(t.Context(), listOpts).Return(client.ContainerListResult{
|
||||
Items: []containerType.Summary{c1, c2, c3},
|
||||
}, nil)
|
||||
api.EXPECT().ContainerInspect(anyCancellableContext(), "123", gomock.Any()).Return(client.ContainerInspectResult{Container: inspect1}, nil)
|
||||
api.EXPECT().ContainerInspect(anyCancellableContext(), "456", gomock.Any()).Return(client.ContainerInspectResult{Container: inspect2}, nil)
|
||||
api.EXPECT().ContainerInspect(anyCancellableContext(), "789", gomock.Any()).Return(client.ContainerInspectResult{Container: inspect3}, nil)
|
||||
|
||||
containers, err := tested.Ps(t.Context(), strings.ToLower(testProject), compose.PsOptions{})
|
||||
|
||||
@@ -66,8 +70,7 @@ func TestPs(t *testing.T) {
|
||||
{
|
||||
ID: "456", Name: "456", Names: []string{"/456"}, Image: "foo", Project: strings.ToLower(testProject), Service: "service1",
|
||||
State: containerType.StateRunning,
|
||||
Health: "",
|
||||
Publishers: []compose.PortPublisher{{URL: "localhost", TargetPort: 90, PublishedPort: 80}},
|
||||
Publishers: []compose.PortPublisher{{URL: "127.0.0.1", TargetPort: 90, PublishedPort: 80}},
|
||||
Labels: map[string]string{
|
||||
compose.ProjectLabel: strings.ToLower(testProject),
|
||||
compose.ConfigFilesLabel: "/src/pkg/compose/testdata/compose.yaml",
|
||||
@@ -78,7 +81,6 @@ func TestPs(t *testing.T) {
|
||||
{
|
||||
ID: "789", Name: "789", Names: []string{"/789"}, Image: "foo", Project: strings.ToLower(testProject), Service: "service2",
|
||||
State: containerType.StateExited,
|
||||
Health: "",
|
||||
ExitCode: 130,
|
||||
Publishers: []compose.PortPublisher{},
|
||||
Labels: map[string]string{
|
||||
@@ -102,12 +104,10 @@ func containerDetails(service string, id string, status containerType.ContainerS
|
||||
State: status,
|
||||
}
|
||||
inspect := containerType.InspectResponse{
|
||||
ContainerJSONBase: &containerType.ContainerJSONBase{
|
||||
State: &containerType.State{
|
||||
Status: status,
|
||||
Health: &containerType.Health{Status: health},
|
||||
ExitCode: exitCode,
|
||||
},
|
||||
State: &containerType.State{
|
||||
Status: status,
|
||||
Health: &containerType.Health{Status: health},
|
||||
ExitCode: exitCode,
|
||||
},
|
||||
}
|
||||
return ctr, inspect
|
||||
|
||||
@@ -28,13 +28,15 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
"github.com/containerd/platforms"
|
||||
"github.com/distribution/reference"
|
||||
"github.com/docker/buildx/driver"
|
||||
"github.com/docker/cli/cli/config/configfile"
|
||||
"github.com/docker/docker/api/types/image"
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/docker/docker/pkg/jsonmessage"
|
||||
clitypes "github.com/docker/cli/cli/config/types"
|
||||
"github.com/docker/go-units"
|
||||
"github.com/moby/moby/api/types/jsonstream"
|
||||
"github.com/moby/moby/client"
|
||||
"github.com/opencontainers/go-digest"
|
||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/sync/errgroup"
|
||||
|
||||
@@ -189,9 +191,18 @@ func (s *composeService) pullServiceImage(ctx context.Context, service types.Ser
|
||||
platform = defaultPlatform
|
||||
}
|
||||
|
||||
stream, err := s.apiClient().ImagePull(ctx, service.Image, image.PullOptions{
|
||||
var ociPlatforms []ocispec.Platform
|
||||
if platform != "" {
|
||||
p, err := platforms.Parse(platform)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
ociPlatforms = append(ociPlatforms, p)
|
||||
}
|
||||
|
||||
stream, err := s.apiClient().ImagePull(ctx, service.Image, client.ImagePullOptions{
|
||||
RegistryAuth: encodedAuth,
|
||||
Platform: platform,
|
||||
Platforms: ociPlatforms,
|
||||
})
|
||||
|
||||
if ctx.Err() != nil {
|
||||
@@ -221,7 +232,7 @@ func (s *composeService) pullServiceImage(ctx context.Context, service types.Ser
|
||||
|
||||
dec := json.NewDecoder(stream)
|
||||
for {
|
||||
var jm jsonmessage.JSONMessage
|
||||
var jm jsonstream.Message
|
||||
if err := dec.Decode(&jm); err != nil {
|
||||
if errors.Is(err, io.EOF) {
|
||||
break
|
||||
@@ -251,7 +262,9 @@ func ImageDigestResolver(ctx context.Context, file *configfile.ConfigFile, apiCl
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
inspect, err := apiClient.DistributionInspect(ctx, named.String(), auth)
|
||||
inspect, err := apiClient.DistributionInspect(ctx, named.String(), client.DistributionInspectOptions{
|
||||
EncodedRegistryAuth: auth,
|
||||
})
|
||||
if err != nil {
|
||||
return "",
|
||||
fmt.Errorf("failed to resolve digest for %s: %w", named.String(), err)
|
||||
@@ -260,7 +273,11 @@ func ImageDigestResolver(ctx context.Context, file *configfile.ConfigFile, apiCl
|
||||
}
|
||||
}
|
||||
|
||||
func encodedAuth(ref reference.Named, configFile driver.Auth) (string, error) {
|
||||
type authProvider interface {
|
||||
GetAuthConfig(registryHostname string) (clitypes.AuthConfig, error)
|
||||
}
|
||||
|
||||
func encodedAuth(ref reference.Named, configFile authProvider) (string, error) {
|
||||
authConfig, err := configFile.GetAuthConfig(registry.GetAuthConfigKey(reference.Domain(ref)))
|
||||
if err != nil {
|
||||
return "", err
|
||||
@@ -392,21 +409,19 @@ const (
|
||||
PullCompletePhase = "Pull complete"
|
||||
)
|
||||
|
||||
func toPullProgressEvent(parent string, jm jsonmessage.JSONMessage, events api.EventProcessor) {
|
||||
func toPullProgressEvent(parent string, jm jsonstream.Message, events api.EventProcessor) {
|
||||
if jm.ID == "" || jm.Progress == nil {
|
||||
return
|
||||
}
|
||||
|
||||
var (
|
||||
progress string
|
||||
total int64
|
||||
percent int
|
||||
current int64
|
||||
status = api.Working
|
||||
details string
|
||||
total int64
|
||||
percent int
|
||||
current int64
|
||||
status = api.Working
|
||||
)
|
||||
|
||||
progress = jm.Progress.String()
|
||||
|
||||
switch jm.Status {
|
||||
case PreparingPhase, WaitingPhase, PullingFsPhase:
|
||||
percent = 0
|
||||
@@ -415,10 +430,7 @@ func toPullProgressEvent(parent string, jm jsonmessage.JSONMessage, events api.E
|
||||
current = jm.Progress.Current
|
||||
total = jm.Progress.Total
|
||||
if jm.Progress.Total > 0 {
|
||||
percent = int(jm.Progress.Current * 100 / jm.Progress.Total)
|
||||
if percent > 100 {
|
||||
percent = 100
|
||||
}
|
||||
percent = min(int(jm.Progress.Current*100/jm.Progress.Total), 100)
|
||||
}
|
||||
}
|
||||
case DownloadCompletePhase, AlreadyExistsPhase, PullCompletePhase:
|
||||
@@ -434,7 +446,9 @@ func toPullProgressEvent(parent string, jm jsonmessage.JSONMessage, events api.E
|
||||
|
||||
if jm.Error != nil {
|
||||
status = api.Error
|
||||
progress = jm.Error.Message
|
||||
details = jm.Error.Message
|
||||
} else {
|
||||
details = units.HumanSize(float64(jm.Progress.Current))
|
||||
}
|
||||
|
||||
events.On(api.Resource{
|
||||
@@ -445,6 +459,6 @@ func toPullProgressEvent(parent string, jm jsonmessage.JSONMessage, events api.E
|
||||
Percent: percent,
|
||||
Status: status,
|
||||
Text: jm.Status,
|
||||
Details: progress,
|
||||
Details: details,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -27,8 +27,9 @@ import (
|
||||
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
"github.com/distribution/reference"
|
||||
"github.com/docker/docker/api/types/image"
|
||||
"github.com/docker/docker/pkg/jsonmessage"
|
||||
"github.com/docker/go-units"
|
||||
"github.com/moby/moby/api/types/jsonstream"
|
||||
"github.com/moby/moby/client"
|
||||
"golang.org/x/sync/errgroup"
|
||||
|
||||
"github.com/docker/compose/v5/internal/registry"
|
||||
@@ -101,7 +102,7 @@ func (s *composeService) pushServiceImage(ctx context.Context, tag string, quiet
|
||||
return err
|
||||
}
|
||||
|
||||
stream, err := s.apiClient().ImagePush(ctx, tag, image.PushOptions{
|
||||
stream, err := s.apiClient().ImagePush(ctx, tag, client.ImagePushOptions{
|
||||
RegistryAuth: base64.URLEncoding.EncodeToString(buf),
|
||||
})
|
||||
if err != nil {
|
||||
@@ -109,7 +110,7 @@ func (s *composeService) pushServiceImage(ctx context.Context, tag string, quiet
|
||||
}
|
||||
dec := json.NewDecoder(stream)
|
||||
for {
|
||||
var jm jsonmessage.JSONMessage
|
||||
var jm jsonstream.Message
|
||||
if err := dec.Decode(&jm); err != nil {
|
||||
if errors.Is(err, io.EOF) {
|
||||
break
|
||||
@@ -128,7 +129,7 @@ func (s *composeService) pushServiceImage(ctx context.Context, tag string, quiet
|
||||
return nil
|
||||
}
|
||||
|
||||
func toPushProgressEvent(prefix string, jm jsonmessage.JSONMessage, events api.EventProcessor) {
|
||||
func toPushProgressEvent(prefix string, jm jsonstream.Message, events api.EventProcessor) {
|
||||
if jm.ID == "" {
|
||||
// skipped
|
||||
return
|
||||
@@ -149,15 +150,12 @@ func toPushProgressEvent(prefix string, jm jsonmessage.JSONMessage, events api.E
|
||||
text = jm.Error.Message
|
||||
}
|
||||
if jm.Progress != nil {
|
||||
text = jm.Progress.String()
|
||||
text = progressText(jm.Progress)
|
||||
if jm.Progress.Total != 0 {
|
||||
current = jm.Progress.Current
|
||||
total = jm.Progress.Total
|
||||
if jm.Progress.Total > 0 {
|
||||
percent = int(jm.Progress.Current * 100 / jm.Progress.Total)
|
||||
if percent > 100 {
|
||||
percent = 100
|
||||
}
|
||||
percent = min(int(jm.Progress.Current*100/jm.Progress.Total), 100)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -173,7 +171,7 @@ func toPushProgressEvent(prefix string, jm jsonmessage.JSONMessage, events api.E
|
||||
})
|
||||
}
|
||||
|
||||
func isDone(msg jsonmessage.JSONMessage) bool {
|
||||
func isDone(msg jsonstream.Message) bool {
|
||||
// TODO there should be a better way to detect push is done than such a status message check
|
||||
switch strings.ToLower(msg.Status) {
|
||||
case "pushed", "layer already exists":
|
||||
@@ -182,3 +180,27 @@ func isDone(msg jsonmessage.JSONMessage) bool {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// progressText is a minimal variant of [jsonmessage.JSONProgress.String()]
|
||||
//
|
||||
// [jsonmessage.JSONProgress.String()]: https://github.com/moby/moby/blob/v28.5.2/pkg/jsonmessage/jsonmessage.go#L54-L117
|
||||
func progressText(p *jsonstream.Progress) string {
|
||||
switch {
|
||||
case p.Current <= 0 && p.Total <= 0:
|
||||
return ""
|
||||
case p.Units == "": // no units, use bytes
|
||||
current := units.HumanSize(float64(p.Current))
|
||||
if p.Total <= 0 || p.Total > p.Current {
|
||||
// remove total display if the reported current is wonky.
|
||||
return fmt.Sprintf("%8v", current)
|
||||
}
|
||||
total := units.HumanSize(float64(p.Total))
|
||||
return fmt.Sprintf("%8v/%v", current, total)
|
||||
default:
|
||||
if p.Total <= 0 || p.Total > p.Current {
|
||||
// remove total display if the reported current is wonky.
|
||||
return fmt.Sprintf("%d %s", p.Current, p.Units)
|
||||
}
|
||||
return fmt.Sprintf("%d/%d %s", p.Current, p.Total, p.Units)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,8 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/moby/moby/api/types/container"
|
||||
"github.com/moby/moby/client"
|
||||
"golang.org/x/sync/errgroup"
|
||||
|
||||
"github.com/docker/compose/v5/pkg/api"
|
||||
@@ -56,7 +57,7 @@ func (s *composeService) Remove(ctx context.Context, projectName string, options
|
||||
var stoppedContainers Containers
|
||||
for _, ctr := range containers {
|
||||
// We have to inspect containers, as State reported by getContainers suffers a race condition
|
||||
inspected, err := s.apiClient().ContainerInspect(ctx, ctr.ID)
|
||||
inspected, err := s.apiClient().ContainerInspect(ctx, ctr.ID, client.ContainerInspectOptions{})
|
||||
if api.IsNotFoundError(err) {
|
||||
// Already removed. Maybe configured with auto-remove
|
||||
continue
|
||||
@@ -64,7 +65,7 @@ func (s *composeService) Remove(ctx context.Context, projectName string, options
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !inspected.State.Running || (options.Stop && s.dryRun) {
|
||||
if !inspected.Container.State.Running || (options.Stop && s.dryRun) {
|
||||
stoppedContainers = append(stoppedContainers, ctr)
|
||||
}
|
||||
}
|
||||
@@ -101,7 +102,7 @@ func (s *composeService) remove(ctx context.Context, containers Containers, opti
|
||||
eg.Go(func() error {
|
||||
eventName := getContainerProgressName(ctr)
|
||||
s.events.On(removingEvent(eventName))
|
||||
err := s.apiClient().ContainerRemove(ctx, ctr.ID, container.RemoveOptions{
|
||||
_, err := s.apiClient().ContainerRemove(ctx, ctr.ID, client.ContainerRemoveOptions{
|
||||
RemoveVolumes: options.Volumes,
|
||||
Force: options.Force,
|
||||
})
|
||||
|
||||
@@ -21,7 +21,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/moby/moby/client"
|
||||
"golang.org/x/sync/errgroup"
|
||||
|
||||
"github.com/docker/compose/v5/pkg/api"
|
||||
@@ -94,8 +94,9 @@ func (s *composeService) restart(ctx context.Context, projectName string, option
|
||||
}
|
||||
eventName := getContainerProgressName(ctr)
|
||||
s.events.On(restartingEvent(eventName))
|
||||
timeout := utils.DurationSecondToInt(options.Timeout)
|
||||
err = s.apiClient().ContainerRestart(ctx, ctr.ID, container.StopOptions{Timeout: timeout})
|
||||
_, err = s.apiClient().ContainerRestart(ctx, ctr.ID, client.ContainerRestartOptions{
|
||||
Timeout: utils.DurationSecondToInt(options.Timeout),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -27,7 +27,8 @@ import (
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
"github.com/docker/cli/cli"
|
||||
cmd "github.com/docker/cli/cli/command/container"
|
||||
"github.com/docker/docker/pkg/stringid"
|
||||
"github.com/moby/moby/client"
|
||||
"github.com/moby/moby/client/pkg/stringid"
|
||||
|
||||
"github.com/docker/compose/v5/pkg/api"
|
||||
)
|
||||
@@ -136,17 +137,17 @@ func (s *composeService) prepareRun(ctx context.Context, project *types.Project,
|
||||
return "", err
|
||||
}
|
||||
|
||||
ctr, err := s.apiClient().ContainerInspect(ctx, created.ID)
|
||||
inspect, err := s.apiClient().ContainerInspect(ctx, created.ID, client.ContainerInspectOptions{})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
err = s.injectSecrets(ctx, project, service, ctr.ID)
|
||||
err = s.injectSecrets(ctx, project, service, inspect.Container.ID)
|
||||
if err != nil {
|
||||
return created.ID, err
|
||||
}
|
||||
|
||||
err = s.injectConfigs(ctx, project, service, ctr.ID)
|
||||
err = s.injectConfigs(ctx, project, service, inspect.Container.ID)
|
||||
return created.ID, err
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/moby/moby/client"
|
||||
)
|
||||
|
||||
type mountType string
|
||||
@@ -128,9 +128,12 @@ func (s *composeService) copyFileToContainer(ctx context.Context, id, content st
|
||||
return err
|
||||
}
|
||||
|
||||
return s.apiClient().CopyToContainer(ctx, id, "/", &b, container.CopyToContainerOptions{
|
||||
CopyUIDGID: file.UID != "" || file.GID != "",
|
||||
_, err = s.apiClient().CopyToContainer(ctx, id, client.CopyToContainerOptions{
|
||||
DestinationPath: "/",
|
||||
Content: &b,
|
||||
CopyUIDGID: file.UID != "" || file.GID != "",
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func createTar(env string, config types.FileReferenceConfig) (bytes.Buffer, error) {
|
||||
|
||||
@@ -26,7 +26,7 @@ import (
|
||||
"github.com/docker/cli/cli-plugins/metadata"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/cli/cli/flags"
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/moby/moby/client"
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/propagation"
|
||||
|
||||
|
||||
@@ -23,8 +23,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
containerType "github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
"github.com/moby/moby/client"
|
||||
|
||||
"github.com/docker/compose/v5/pkg/api"
|
||||
)
|
||||
@@ -50,17 +49,14 @@ func (s *composeService) start(ctx context.Context, projectName string, options
|
||||
}
|
||||
}
|
||||
|
||||
var containers Containers
|
||||
containers, err := s.apiClient().ContainerList(ctx, containerType.ListOptions{
|
||||
Filters: filters.NewArgs(
|
||||
projectFilter(project.Name),
|
||||
oneOffFilter(false),
|
||||
),
|
||||
All: true,
|
||||
res, err := s.apiClient().ContainerList(ctx, client.ContainerListOptions{
|
||||
Filters: projectFilter(project.Name).Add("label", oneOffFilter(false)),
|
||||
All: true,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
containers := Containers(res.Items)
|
||||
|
||||
err = InDependencyOrder(ctx, project, func(c context.Context, name string) error {
|
||||
service, err := project.GetService(name)
|
||||
|
||||
@@ -21,10 +21,8 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"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"
|
||||
"github.com/moby/moby/api/types/container"
|
||||
"github.com/moby/moby/client"
|
||||
"go.uber.org/mock/gomock"
|
||||
"gotest.tools/v3/assert"
|
||||
|
||||
@@ -41,25 +39,27 @@ func TestStopTimeout(t *testing.T) {
|
||||
assert.NilError(t, err)
|
||||
|
||||
api.EXPECT().ContainerList(gomock.Any(), projectFilterListOpt(false)).Return(
|
||||
[]container.Summary{
|
||||
testContainer("service1", "123", false),
|
||||
testContainer("service1", "456", false),
|
||||
testContainer("service2", "789", false),
|
||||
client.ContainerListResult{
|
||||
Items: []container.Summary{
|
||||
testContainer("service1", "123", false),
|
||||
testContainer("service1", "456", false),
|
||||
testContainer("service2", "789", false),
|
||||
},
|
||||
}, nil)
|
||||
api.EXPECT().VolumeList(
|
||||
gomock.Any(),
|
||||
volume.ListOptions{
|
||||
Filters: filters.NewArgs(projectFilter(strings.ToLower(testProject))),
|
||||
client.VolumeListOptions{
|
||||
Filters: projectFilter(strings.ToLower(testProject)),
|
||||
}).
|
||||
Return(volume.ListResponse{}, nil)
|
||||
api.EXPECT().NetworkList(gomock.Any(), network.ListOptions{Filters: filters.NewArgs(projectFilter(strings.ToLower(testProject)))}).
|
||||
Return([]network.Summary{}, nil)
|
||||
Return(client.VolumeListResult{}, nil)
|
||||
api.EXPECT().NetworkList(gomock.Any(), client.NetworkListOptions{Filters: projectFilter(strings.ToLower(testProject))}).
|
||||
Return(client.NetworkListResult{}, nil)
|
||||
|
||||
timeout := 2 * time.Second
|
||||
stopConfig := container.StopOptions{Timeout: utils.DurationSecondToInt(&timeout)}
|
||||
api.EXPECT().ContainerStop(gomock.Any(), "123", stopConfig).Return(nil)
|
||||
api.EXPECT().ContainerStop(gomock.Any(), "456", stopConfig).Return(nil)
|
||||
api.EXPECT().ContainerStop(gomock.Any(), "789", stopConfig).Return(nil)
|
||||
stopConfig := client.ContainerStopOptions{Timeout: utils.DurationSecondToInt(&timeout)}
|
||||
api.EXPECT().ContainerStop(gomock.Any(), "123", stopConfig).Return(client.ContainerStopResult{}, nil)
|
||||
api.EXPECT().ContainerStop(gomock.Any(), "456", stopConfig).Return(client.ContainerStopResult{}, nil)
|
||||
api.EXPECT().ContainerStop(gomock.Any(), "789", stopConfig).Return(client.ContainerStopResult{}, nil)
|
||||
|
||||
err = tested.Stop(t.Context(), strings.ToLower(testProject), compose.StopOptions{
|
||||
Timeout: &timeout,
|
||||
|
||||
@@ -20,6 +20,7 @@ import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/moby/moby/client"
|
||||
"golang.org/x/sync/errgroup"
|
||||
|
||||
"github.com/docker/compose/v5/pkg/api"
|
||||
@@ -39,7 +40,9 @@ func (s *composeService) Top(ctx context.Context, projectName string, services [
|
||||
eg, ctx := errgroup.WithContext(ctx)
|
||||
for i, ctr := range containers {
|
||||
eg.Go(func() error {
|
||||
topContent, err := s.apiClient().ContainerTop(ctx, ctr.ID, []string{})
|
||||
topContent, err := s.apiClient().ContainerTop(ctx, ctr.ID, client.ContainerTopOptions{
|
||||
Arguments: []string{},
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ import (
|
||||
"github.com/containerd/errdefs"
|
||||
"github.com/docker/cli/cli"
|
||||
"github.com/eiannone/keyboard"
|
||||
"github.com/moby/moby/client"
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/sync/errgroup"
|
||||
|
||||
@@ -252,15 +253,15 @@ func (s *composeService) Up(ctx context.Context, project *types.Project, options
|
||||
return
|
||||
}
|
||||
eg.Go(func() error {
|
||||
ctr, err := s.apiClient().ContainerInspect(globalCtx, event.ID)
|
||||
res, err := s.apiClient().ContainerInspect(globalCtx, event.ID, client.ContainerInspectOptions{})
|
||||
if err != nil {
|
||||
appendErr(err)
|
||||
return nil
|
||||
}
|
||||
|
||||
err = s.doLogContainer(globalCtx, options.Start.Attach, event.Source, ctr, api.LogOptions{
|
||||
err = s.doLogContainer(globalCtx, options.Start.Attach, event.Source, res.Container, api.LogOptions{
|
||||
Follow: true,
|
||||
Since: ctr.State.StartedAt,
|
||||
Since: res.Container.State.StartedAt,
|
||||
})
|
||||
if errdefs.IsNotImplemented(err) {
|
||||
// container may be configured with logging_driver: none
|
||||
|
||||
@@ -20,16 +20,15 @@ import (
|
||||
"context"
|
||||
"slices"
|
||||
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
"github.com/docker/docker/api/types/volume"
|
||||
"github.com/moby/moby/api/types/container"
|
||||
"github.com/moby/moby/client"
|
||||
|
||||
"github.com/docker/compose/v5/pkg/api"
|
||||
)
|
||||
|
||||
func (s *composeService) Volumes(ctx context.Context, project string, options api.VolumesOptions) ([]api.VolumesSummary, error) {
|
||||
allContainers, err := s.apiClient().ContainerList(ctx, container.ListOptions{
|
||||
Filters: filters.NewArgs(projectFilter(project)),
|
||||
allContainers, err := s.apiClient().ContainerList(ctx, client.ContainerListOptions{
|
||||
Filters: projectFilter(project),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -39,23 +38,23 @@ func (s *composeService) Volumes(ctx context.Context, project string, options ap
|
||||
|
||||
if len(options.Services) > 0 {
|
||||
// filter service containers
|
||||
for _, c := range allContainers {
|
||||
for _, c := range allContainers.Items {
|
||||
if slices.Contains(options.Services, c.Labels[api.ServiceLabel]) {
|
||||
containers = append(containers, c)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
containers = allContainers
|
||||
containers = allContainers.Items
|
||||
}
|
||||
|
||||
volumesResponse, err := s.apiClient().VolumeList(ctx, volume.ListOptions{
|
||||
Filters: filters.NewArgs(projectFilter(project)),
|
||||
volumesResponse, err := s.apiClient().VolumeList(ctx, client.VolumeListOptions{
|
||||
Filters: projectFilter(project),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
projectVolumes := volumesResponse.Volumes
|
||||
projectVolumes := volumesResponse.Items
|
||||
|
||||
if len(options.Services) == 0 {
|
||||
return projectVolumes, nil
|
||||
@@ -66,8 +65,8 @@ func (s *composeService) Volumes(ctx context.Context, project string, options ap
|
||||
// create a name lookup of volumes used by containers
|
||||
serviceVolumes := make(map[string]bool)
|
||||
|
||||
for _, container := range containers {
|
||||
for _, mount := range container.Mounts {
|
||||
for _, ctr := range containers {
|
||||
for _, mount := range ctr.Mounts {
|
||||
serviceVolumes[mount.Name] = true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,9 +19,9 @@ package compose
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
"github.com/docker/docker/api/types/volume"
|
||||
"github.com/moby/moby/api/types/container"
|
||||
"github.com/moby/moby/api/types/volume"
|
||||
"github.com/moby/moby/client"
|
||||
"go.uber.org/mock/gomock"
|
||||
"gotest.tools/v3/assert"
|
||||
|
||||
@@ -38,9 +38,9 @@ func TestVolumes(t *testing.T) {
|
||||
}
|
||||
|
||||
// Create test volumes
|
||||
vol1 := &volume.Volume{Name: testProject + "_vol1"}
|
||||
vol2 := &volume.Volume{Name: testProject + "_vol2"}
|
||||
vol3 := &volume.Volume{Name: testProject + "_vol3"}
|
||||
vol1 := volume.Volume{Name: testProject + "_vol1"}
|
||||
vol2 := volume.Volume{Name: testProject + "_vol2"}
|
||||
vol3 := volume.Volume{Name: testProject + "_vol3"}
|
||||
|
||||
// Create test containers with volume mounts
|
||||
c1 := container.Summary{
|
||||
@@ -57,28 +57,26 @@ func TestVolumes(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
args := filters.NewArgs(projectFilter(testProject))
|
||||
listOpts := container.ListOptions{Filters: args}
|
||||
volumeListArgs := filters.NewArgs(projectFilter(testProject))
|
||||
volumeListOpts := volume.ListOptions{Filters: volumeListArgs}
|
||||
volumeReturn := volume.ListResponse{
|
||||
Volumes: []*volume.Volume{vol1, vol2, vol3},
|
||||
listOpts := client.ContainerListOptions{Filters: projectFilter(testProject)}
|
||||
volumeListOpts := client.VolumeListOptions{Filters: projectFilter(testProject)}
|
||||
volumeReturn := client.VolumeListResult{
|
||||
Items: []volume.Volume{vol1, vol2, vol3},
|
||||
}
|
||||
containerReturn := client.ContainerListResult{
|
||||
Items: []container.Summary{c1, c2},
|
||||
}
|
||||
containerReturn := []container.Summary{c1, c2}
|
||||
|
||||
mockApi.EXPECT().ContainerList(t.Context(), listOpts).Times(2).Return(containerReturn, nil)
|
||||
mockApi.EXPECT().VolumeList(t.Context(), volumeListOpts).Times(2).Return(volumeReturn, nil)
|
||||
|
||||
// Test without service filter - should return all project volumes
|
||||
volumeOptions := api.VolumesOptions{}
|
||||
volumes, err := tested.Volumes(t.Context(), testProject, volumeOptions)
|
||||
volumes, err := tested.Volumes(t.Context(), testProject, api.VolumesOptions{})
|
||||
expected := []api.VolumesSummary{vol1, vol2, vol3}
|
||||
assert.NilError(t, err)
|
||||
assert.DeepEqual(t, volumes, expected)
|
||||
|
||||
// Test with service filter - should only return volumes used by service1
|
||||
volumeOptions = api.VolumesOptions{Services: []string{"service1"}}
|
||||
volumes, err = tested.Volumes(t.Context(), testProject, volumeOptions)
|
||||
volumes, err = tested.Volumes(t.Context(), testProject, api.VolumesOptions{Services: []string{"service1"}})
|
||||
expected = []api.VolumesSummary{vol1, vol2}
|
||||
assert.NilError(t, err)
|
||||
assert.DeepEqual(t, volumes, expected)
|
||||
|
||||
@@ -20,6 +20,7 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/moby/moby/client"
|
||||
"golang.org/x/sync/errgroup"
|
||||
|
||||
"github.com/docker/compose/v5/pkg/api"
|
||||
@@ -39,15 +40,13 @@ func (s *composeService) Wait(ctx context.Context, projectName string, options a
|
||||
for _, ctr := range containers {
|
||||
eg.Go(func() error {
|
||||
var err error
|
||||
resultC, errC := s.apiClient().ContainerWait(waitCtx, ctr.ID, "")
|
||||
|
||||
res := s.apiClient().ContainerWait(waitCtx, ctr.ID, client.ContainerWaitOptions{})
|
||||
select {
|
||||
case result := <-resultC:
|
||||
case result := <-res.Result:
|
||||
_, _ = fmt.Fprintf(s.stdout(), "container %q exited with status code %d\n", ctr.ID, result.StatusCode)
|
||||
statusCode = result.StatusCode
|
||||
case err = <-errC:
|
||||
case err = <-res.Error:
|
||||
}
|
||||
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
@@ -32,11 +32,10 @@ import (
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
"github.com/compose-spec/compose-go/v2/utils"
|
||||
ccli "github.com/docker/cli/cli/command/container"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
"github.com/docker/docker/api/types/image"
|
||||
"github.com/go-viper/mapstructure/v2"
|
||||
"github.com/moby/buildkit/util/progress/progressui"
|
||||
"github.com/moby/moby/api/types/container"
|
||||
"github.com/moby/moby/client"
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/sync/errgroup"
|
||||
|
||||
@@ -459,20 +458,20 @@ func (t tarDockerClient) ContainersForService(ctx context.Context, projectName s
|
||||
}
|
||||
|
||||
func (t tarDockerClient) Exec(ctx context.Context, containerID string, cmd []string, in io.Reader) error {
|
||||
execCfg := container.ExecOptions{
|
||||
execCreateResp, err := t.s.apiClient().ExecCreate(ctx, containerID, client.ExecCreateOptions{
|
||||
Cmd: cmd,
|
||||
AttachStdout: false,
|
||||
AttachStderr: true,
|
||||
AttachStdin: in != nil,
|
||||
Tty: false,
|
||||
}
|
||||
execCreateResp, err := t.s.apiClient().ContainerExecCreate(ctx, containerID, execCfg)
|
||||
TTY: false,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
startCheck := container.ExecStartOptions{Tty: false, Detach: false}
|
||||
conn, err := t.s.apiClient().ContainerExecAttach(ctx, execCreateResp.ID, startCheck)
|
||||
conn, err := t.s.apiClient().ExecAttach(ctx, execCreateResp.ID, client.ExecAttachOptions{
|
||||
TTY: false,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -493,7 +492,10 @@ func (t tarDockerClient) Exec(ctx context.Context, containerID string, cmd []str
|
||||
return err
|
||||
})
|
||||
|
||||
err = t.s.apiClient().ContainerExecStart(ctx, execCreateResp.ID, startCheck)
|
||||
_, err = t.s.apiClient().ExecStart(ctx, execCreateResp.ID, client.ExecStartOptions{
|
||||
TTY: false,
|
||||
Detach: false,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -505,7 +507,7 @@ func (t tarDockerClient) Exec(ctx context.Context, containerID string, cmd []str
|
||||
return err
|
||||
}
|
||||
|
||||
execResult, err := t.s.apiClient().ContainerExecInspect(ctx, execCreateResp.ID)
|
||||
execResult, err := t.s.apiClient().ExecInspect(ctx, execCreateResp.ID, client.ExecInspectOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -519,9 +521,12 @@ func (t tarDockerClient) Exec(ctx context.Context, containerID string, cmd []str
|
||||
}
|
||||
|
||||
func (t tarDockerClient) Untar(ctx context.Context, id string, archive io.ReadCloser) error {
|
||||
return t.s.apiClient().CopyToContainer(ctx, id, "/", archive, container.CopyToContainerOptions{
|
||||
CopyUIDGID: true,
|
||||
_, err := t.s.apiClient().CopyToContainer(ctx, id, client.CopyToContainerOptions{
|
||||
DestinationPath: "/",
|
||||
Content: archive,
|
||||
CopyUIDGID: true,
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
//nolint:gocyclo
|
||||
@@ -705,20 +710,17 @@ func writeWatchSyncMessage(log api.LogConsumer, serviceName string, pathMappings
|
||||
}
|
||||
|
||||
func (s *composeService) pruneDanglingImagesOnRebuild(ctx context.Context, projectName string, imageNameToIdMap map[string]string) {
|
||||
images, err := s.apiClient().ImageList(ctx, image.ListOptions{
|
||||
Filters: filters.NewArgs(
|
||||
filters.Arg("dangling", "true"),
|
||||
filters.Arg("label", api.ProjectLabel+"="+projectName),
|
||||
),
|
||||
images, err := s.apiClient().ImageList(ctx, client.ImageListOptions{
|
||||
Filters: projectFilter(projectName).Add("dangling", "true"),
|
||||
})
|
||||
if err != nil {
|
||||
logrus.Debugf("Failed to list images: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
for _, img := range images {
|
||||
for _, img := range images.Items {
|
||||
if _, ok := imageNameToIdMap[img.ID]; !ok {
|
||||
_, err := s.apiClient().ImageRemove(ctx, img.ID, image.RemoveOptions{})
|
||||
_, err := s.apiClient().ImageRemove(ctx, img.ID, client.ImageRemoveOptions{})
|
||||
if err != nil {
|
||||
logrus.Debugf("Failed to remove image %s: %v", img.ID, err)
|
||||
}
|
||||
@@ -832,20 +834,18 @@ func shouldIgnore(name string, ignore watch.PathMatcher) bool {
|
||||
|
||||
// gets the image creation time for a service
|
||||
func (s *composeService) imageCreatedTime(ctx context.Context, project *types.Project, serviceName string) (time.Time, error) {
|
||||
containers, err := s.apiClient().ContainerList(ctx, container.ListOptions{
|
||||
All: true,
|
||||
Filters: filters.NewArgs(
|
||||
filters.Arg("label", fmt.Sprintf("%s=%s", api.ProjectLabel, project.Name)),
|
||||
filters.Arg("label", fmt.Sprintf("%s=%s", api.ServiceLabel, serviceName))),
|
||||
res, err := s.apiClient().ContainerList(ctx, client.ContainerListOptions{
|
||||
All: true,
|
||||
Filters: projectFilter(project.Name).Add("label", serviceFilter(serviceName)),
|
||||
})
|
||||
if err != nil {
|
||||
return time.Now(), err
|
||||
}
|
||||
if len(containers) == 0 {
|
||||
if len(res.Items) == 0 {
|
||||
return time.Now(), fmt.Errorf("could not get created time for service's image")
|
||||
}
|
||||
|
||||
img, err := s.apiClient().ImageInspect(ctx, containers[0].ImageID)
|
||||
img, err := s.apiClient().ImageInspect(ctx, res.Items[0].ImageID)
|
||||
if err != nil {
|
||||
return time.Now(), err
|
||||
}
|
||||
|
||||
@@ -23,10 +23,10 @@ import (
|
||||
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
"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/image"
|
||||
"github.com/jonboulle/clockwork"
|
||||
"github.com/moby/moby/api/types/container"
|
||||
"github.com/moby/moby/api/types/image"
|
||||
"github.com/moby/moby/client"
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.uber.org/mock/gomock"
|
||||
"gotest.tools/v3/assert"
|
||||
@@ -77,21 +77,24 @@ func TestWatch_Sync(t *testing.T) {
|
||||
cli := mocks.NewMockCli(mockCtrl)
|
||||
cli.EXPECT().Err().Return(streams.NewOut(os.Stderr)).AnyTimes()
|
||||
apiClient := mocks.NewMockAPIClient(mockCtrl)
|
||||
apiClient.EXPECT().ContainerList(gomock.Any(), gomock.Any()).Return([]container.Summary{
|
||||
testContainer("test", "123", false),
|
||||
apiClient.EXPECT().ContainerList(gomock.Any(), gomock.Any()).Return(client.ContainerListResult{
|
||||
Items: []container.Summary{
|
||||
testContainer("test", "123", false),
|
||||
},
|
||||
}, nil).AnyTimes()
|
||||
// we expect the image to be pruned
|
||||
apiClient.EXPECT().ImageList(gomock.Any(), image.ListOptions{
|
||||
Filters: filters.NewArgs(
|
||||
filters.Arg("dangling", "true"),
|
||||
filters.Arg("label", api.ProjectLabel+"=myProjectName"),
|
||||
),
|
||||
}).Return([]image.Summary{
|
||||
{ID: "123"},
|
||||
{ID: "456"},
|
||||
apiClient.EXPECT().ImageList(gomock.Any(), client.ImageListOptions{
|
||||
Filters: make(client.Filters).
|
||||
Add("dangling", "true").
|
||||
Add("label", api.ProjectLabel+"=myProjectName"),
|
||||
}).Return(client.ImageListResult{
|
||||
Items: []image.Summary{
|
||||
{ID: "123"},
|
||||
{ID: "456"},
|
||||
},
|
||||
}, nil).Times(1)
|
||||
apiClient.EXPECT().ImageRemove(gomock.Any(), "123", image.RemoveOptions{}).Times(1)
|
||||
apiClient.EXPECT().ImageRemove(gomock.Any(), "456", image.RemoveOptions{}).Times(1)
|
||||
apiClient.EXPECT().ImageRemove(gomock.Any(), "123", client.ImageRemoveOptions{}).Times(1)
|
||||
apiClient.EXPECT().ImageRemove(gomock.Any(), "456", client.ImageRemoveOptions{}).Times(1)
|
||||
//
|
||||
cli.EXPECT().Client().Return(apiClient).AnyTimes()
|
||||
|
||||
|
||||
@@ -33,21 +33,11 @@ import (
|
||||
"github.com/docker/buildx/builder"
|
||||
"github.com/docker/buildx/util/imagetools"
|
||||
"github.com/docker/cli/cli/command"
|
||||
moby "github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/build"
|
||||
"github.com/docker/docker/api/types/checkpoint"
|
||||
containerType "github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/events"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
"github.com/docker/docker/api/types/image"
|
||||
"github.com/docker/docker/api/types/network"
|
||||
"github.com/docker/docker/api/types/registry"
|
||||
"github.com/docker/docker/api/types/swarm"
|
||||
"github.com/docker/docker/api/types/system"
|
||||
"github.com/docker/docker/api/types/volume"
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/docker/docker/pkg/jsonmessage"
|
||||
specs "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
containerType "github.com/moby/moby/api/types/container"
|
||||
"github.com/moby/moby/api/types/image"
|
||||
"github.com/moby/moby/api/types/jsonstream"
|
||||
"github.com/moby/moby/api/types/volume"
|
||||
"github.com/moby/moby/client"
|
||||
)
|
||||
|
||||
var _ client.APIClient = &DryRunClient{}
|
||||
@@ -65,6 +55,14 @@ type execDetails struct {
|
||||
command []string
|
||||
}
|
||||
|
||||
type fakeStreamResult struct {
|
||||
io.ReadCloser
|
||||
client.ImagePushResponse // same interface as [client.ImagePullResponse]
|
||||
}
|
||||
|
||||
func (e fakeStreamResult) Read(p []byte) (int, error) { return e.ReadCloser.Read(p) }
|
||||
func (e fakeStreamResult) Close() error { return e.ReadCloser.Close() }
|
||||
|
||||
// NewDryRunClient produces a DryRunClient
|
||||
func NewDryRunClient(apiClient client.APIClient, cli command.Cli) (*DryRunClient, error) {
|
||||
b, err := builder.New(cli, builder.WithSkippedValidation())
|
||||
@@ -91,27 +89,25 @@ func getCallingFunction() string {
|
||||
|
||||
// All methods and functions which need to be overridden for dry run.
|
||||
|
||||
func (d *DryRunClient) ContainerAttach(ctx context.Context, container string, options containerType.AttachOptions) (moby.HijackedResponse, error) {
|
||||
return moby.HijackedResponse{}, errors.New("interactive run is not supported in dry-run mode")
|
||||
func (d *DryRunClient) ContainerAttach(ctx context.Context, container string, options client.ContainerAttachOptions) (client.ContainerAttachResult, error) {
|
||||
return client.ContainerAttachResult{}, errors.New("interactive run is not supported in dry-run mode")
|
||||
}
|
||||
|
||||
func (d *DryRunClient) ContainerCreate(ctx context.Context, config *containerType.Config, hostConfig *containerType.HostConfig,
|
||||
networkingConfig *network.NetworkingConfig, platform *specs.Platform, containerName string,
|
||||
) (containerType.CreateResponse, error) {
|
||||
func (d *DryRunClient) ContainerCreate(ctx context.Context, options client.ContainerCreateOptions) (client.ContainerCreateResult, error) {
|
||||
d.containers = append(d.containers, containerType.Summary{
|
||||
ID: containerName,
|
||||
Names: []string{containerName},
|
||||
Labels: config.Labels,
|
||||
ID: options.Name,
|
||||
Names: []string{options.Name},
|
||||
Labels: options.Config.Labels,
|
||||
HostConfig: struct {
|
||||
NetworkMode string `json:",omitempty"`
|
||||
Annotations map[string]string `json:",omitempty"`
|
||||
}{},
|
||||
})
|
||||
return containerType.CreateResponse{ID: containerName}, nil
|
||||
return client.ContainerCreateResult{ID: options.Name}, nil
|
||||
}
|
||||
|
||||
func (d *DryRunClient) ContainerInspect(ctx context.Context, container string) (containerType.InspectResponse, error) {
|
||||
containerJSON, err := d.apiClient.ContainerInspect(ctx, container)
|
||||
func (d *DryRunClient) ContainerInspect(ctx context.Context, container string, options client.ContainerInspectOptions) (client.ContainerInspectResult, error) {
|
||||
containerJSON, err := d.apiClient.ContainerInspect(ctx, container, options)
|
||||
if err != nil {
|
||||
id := "dryRunId"
|
||||
for _, c := range d.containers {
|
||||
@@ -119,8 +115,8 @@ func (d *DryRunClient) ContainerInspect(ctx context.Context, container string) (
|
||||
id = container
|
||||
}
|
||||
}
|
||||
return containerType.InspectResponse{
|
||||
ContainerJSONBase: &containerType.ContainerJSONBase{
|
||||
return client.ContainerInspectResult{
|
||||
Container: containerType.InspectResponse{
|
||||
ID: id,
|
||||
Name: container,
|
||||
State: &containerType.State{
|
||||
@@ -129,117 +125,111 @@ func (d *DryRunClient) ContainerInspect(ctx context.Context, container string) (
|
||||
Status: containerType.Healthy, // needed for healthcheck control
|
||||
},
|
||||
},
|
||||
Mounts: nil,
|
||||
Config: &containerType.Config{},
|
||||
NetworkSettings: &containerType.NetworkSettings{},
|
||||
},
|
||||
Mounts: nil,
|
||||
Config: &containerType.Config{},
|
||||
NetworkSettings: &containerType.NetworkSettings{},
|
||||
}, nil
|
||||
}
|
||||
return containerJSON, err
|
||||
}
|
||||
|
||||
func (d *DryRunClient) ContainerKill(ctx context.Context, container, signal string) error {
|
||||
return nil
|
||||
func (d *DryRunClient) ContainerKill(ctx context.Context, container string, options client.ContainerKillOptions) (client.ContainerKillResult, error) {
|
||||
return client.ContainerKillResult{}, nil
|
||||
}
|
||||
|
||||
func (d *DryRunClient) ContainerList(ctx context.Context, options containerType.ListOptions) ([]containerType.Summary, error) {
|
||||
func (d *DryRunClient) ContainerList(ctx context.Context, options client.ContainerListOptions) (client.ContainerListResult, error) {
|
||||
caller := getCallingFunction()
|
||||
switch caller {
|
||||
case "start":
|
||||
return d.containers, nil
|
||||
return client.ContainerListResult{
|
||||
Items: d.containers,
|
||||
}, nil
|
||||
case "getContainers":
|
||||
if len(d.containers) == 0 {
|
||||
var err error
|
||||
d.containers, err = d.apiClient.ContainerList(ctx, options)
|
||||
return d.containers, err
|
||||
res, err := d.apiClient.ContainerList(ctx, options)
|
||||
if err == nil {
|
||||
d.containers = res.Items
|
||||
}
|
||||
return client.ContainerListResult{
|
||||
Items: d.containers,
|
||||
}, err
|
||||
}
|
||||
}
|
||||
return d.apiClient.ContainerList(ctx, options)
|
||||
}
|
||||
|
||||
func (d *DryRunClient) ContainerPause(ctx context.Context, container string) error {
|
||||
return nil
|
||||
func (d *DryRunClient) ContainerPause(ctx context.Context, container string, options client.ContainerPauseOptions) (client.ContainerPauseResult, error) {
|
||||
return client.ContainerPauseResult{}, nil
|
||||
}
|
||||
|
||||
func (d *DryRunClient) ContainerRemove(ctx context.Context, container string, options containerType.RemoveOptions) error {
|
||||
return nil
|
||||
func (d *DryRunClient) ContainerRemove(ctx context.Context, container string, options client.ContainerRemoveOptions) (client.ContainerRemoveResult, error) {
|
||||
return client.ContainerRemoveResult{}, nil
|
||||
}
|
||||
|
||||
func (d *DryRunClient) ContainerRename(ctx context.Context, container, newContainerName string) error {
|
||||
return nil
|
||||
func (d *DryRunClient) ContainerRename(ctx context.Context, container string, options client.ContainerRenameOptions) (client.ContainerRenameResult, error) {
|
||||
return client.ContainerRenameResult{}, nil
|
||||
}
|
||||
|
||||
func (d *DryRunClient) ContainerRestart(ctx context.Context, container string, options containerType.StopOptions) error {
|
||||
return nil
|
||||
func (d *DryRunClient) ContainerRestart(ctx context.Context, container string, options client.ContainerRestartOptions) (client.ContainerRestartResult, error) {
|
||||
return client.ContainerRestartResult{}, nil
|
||||
}
|
||||
|
||||
func (d *DryRunClient) ContainerStart(ctx context.Context, container string, options containerType.StartOptions) error {
|
||||
return nil
|
||||
func (d *DryRunClient) ContainerStart(ctx context.Context, container string, options client.ContainerStartOptions) (client.ContainerStartResult, error) {
|
||||
return client.ContainerStartResult{}, nil
|
||||
}
|
||||
|
||||
func (d *DryRunClient) ContainerStop(ctx context.Context, container string, options containerType.StopOptions) error {
|
||||
return nil
|
||||
func (d *DryRunClient) ContainerStop(ctx context.Context, container string, options client.ContainerStopOptions) (client.ContainerStopResult, error) {
|
||||
return client.ContainerStopResult{}, nil
|
||||
}
|
||||
|
||||
func (d *DryRunClient) ContainerUnpause(ctx context.Context, container string) error {
|
||||
return nil
|
||||
func (d *DryRunClient) ContainerUnpause(ctx context.Context, container string, options client.ContainerUnpauseOptions) (client.ContainerUnpauseResult, error) {
|
||||
return client.ContainerUnpauseResult{}, nil
|
||||
}
|
||||
|
||||
func (d *DryRunClient) CopyFromContainer(ctx context.Context, container, srcPath string) (io.ReadCloser, containerType.PathStat, error) {
|
||||
rc := io.NopCloser(strings.NewReader(""))
|
||||
if _, err := d.ContainerStatPath(ctx, container, srcPath); err != nil {
|
||||
return rc, containerType.PathStat{}, fmt.Errorf("could not find the file %s in container %s", srcPath, container)
|
||||
func (d *DryRunClient) CopyFromContainer(ctx context.Context, container string, options client.CopyFromContainerOptions) (client.CopyFromContainerResult, error) {
|
||||
if _, err := d.ContainerStatPath(ctx, container, client.ContainerStatPathOptions{Path: options.SourcePath}); err != nil {
|
||||
return client.CopyFromContainerResult{}, fmt.Errorf("could not find the file %s in container %s", options.SourcePath, container)
|
||||
}
|
||||
return rc, containerType.PathStat{}, nil
|
||||
return client.CopyFromContainerResult{}, nil
|
||||
}
|
||||
|
||||
func (d *DryRunClient) CopyToContainer(ctx context.Context, container, path string, content io.Reader, options containerType.CopyToContainerOptions) error {
|
||||
return nil
|
||||
func (d *DryRunClient) CopyToContainer(ctx context.Context, container string, options client.CopyToContainerOptions) (client.CopyToContainerResult, error) {
|
||||
return client.CopyToContainerResult{}, nil
|
||||
}
|
||||
|
||||
func (d *DryRunClient) ImageBuild(ctx context.Context, reader io.Reader, options build.ImageBuildOptions) (build.ImageBuildResponse, error) {
|
||||
rc := io.NopCloser(bytes.NewReader(nil))
|
||||
|
||||
return build.ImageBuildResponse{
|
||||
Body: rc,
|
||||
func (d *DryRunClient) ImageBuild(ctx context.Context, reader io.Reader, options client.ImageBuildOptions) (client.ImageBuildResult, error) {
|
||||
return client.ImageBuildResult{
|
||||
Body: io.NopCloser(bytes.NewReader(nil)),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (d *DryRunClient) ImageInspect(ctx context.Context, imageName string, options ...client.ImageInspectOption) (image.InspectResponse, error) {
|
||||
func (d *DryRunClient) ImageInspect(ctx context.Context, imageName string, options ...client.ImageInspectOption) (client.ImageInspectResult, error) {
|
||||
caller := getCallingFunction()
|
||||
switch caller {
|
||||
case "pullServiceImage", "buildContainerVolumes":
|
||||
return image.InspectResponse{ID: "dryRunId"}, nil
|
||||
return client.ImageInspectResult{
|
||||
InspectResponse: image.InspectResponse{ID: "dryRunId"},
|
||||
}, nil
|
||||
default:
|
||||
return d.apiClient.ImageInspect(ctx, imageName, options...)
|
||||
}
|
||||
}
|
||||
|
||||
// Deprecated: Use [DryRunClient.ImageInspect] instead; raw response can be obtained by [client.ImageInspectWithRawResponse] option.
|
||||
func (d *DryRunClient) ImageInspectWithRaw(ctx context.Context, imageName string) (image.InspectResponse, []byte, error) {
|
||||
var buf bytes.Buffer
|
||||
resp, err := d.ImageInspect(ctx, imageName, client.ImageInspectWithRawResponse(&buf))
|
||||
if err != nil {
|
||||
return image.InspectResponse{}, nil, err
|
||||
}
|
||||
return resp, buf.Bytes(), err
|
||||
}
|
||||
|
||||
func (d *DryRunClient) ImagePull(ctx context.Context, ref string, options image.PullOptions) (io.ReadCloser, error) {
|
||||
func (d *DryRunClient) ImagePull(ctx context.Context, ref string, options client.ImagePullOptions) (client.ImagePullResponse, error) {
|
||||
if _, _, err := d.resolver.Resolve(ctx, ref); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rc := io.NopCloser(strings.NewReader(""))
|
||||
return rc, nil
|
||||
return fakeStreamResult{ReadCloser: http.NoBody}, nil
|
||||
}
|
||||
|
||||
func (d *DryRunClient) ImagePush(ctx context.Context, ref string, options image.PushOptions) (io.ReadCloser, error) {
|
||||
func (d *DryRunClient) ImagePush(ctx context.Context, ref string, options client.ImagePushOptions) (client.ImagePushResponse, error) {
|
||||
if _, _, err := d.resolver.Resolve(ctx, ref); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
jsonMessage, err := json.Marshal(&jsonmessage.JSONMessage{
|
||||
jsonMessage, err := json.Marshal(&jsonstream.Message{
|
||||
Status: "Pushed",
|
||||
Progress: &jsonmessage.JSONProgress{
|
||||
Progress: &jsonstream.Progress{
|
||||
Current: 100,
|
||||
Total: 100,
|
||||
Start: 0,
|
||||
@@ -251,48 +241,48 @@ func (d *DryRunClient) ImagePush(ctx context.Context, ref string, options image.
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rc := io.NopCloser(bytes.NewReader(jsonMessage))
|
||||
return rc, nil
|
||||
return fakeStreamResult{ReadCloser: io.NopCloser(bytes.NewReader(jsonMessage))}, nil
|
||||
}
|
||||
|
||||
func (d *DryRunClient) ImageRemove(ctx context.Context, imageName string, options image.RemoveOptions) ([]image.DeleteResponse, error) {
|
||||
return nil, nil
|
||||
func (d *DryRunClient) ImageRemove(ctx context.Context, imageName string, options client.ImageRemoveOptions) (client.ImageRemoveResult, error) {
|
||||
return client.ImageRemoveResult{}, nil
|
||||
}
|
||||
|
||||
func (d *DryRunClient) NetworkConnect(ctx context.Context, networkName, container string, config *network.EndpointSettings) error {
|
||||
return nil
|
||||
func (d *DryRunClient) NetworkConnect(ctx context.Context, networkName string, options client.NetworkConnectOptions) (client.NetworkConnectResult, error) {
|
||||
return client.NetworkConnectResult{}, nil
|
||||
}
|
||||
|
||||
func (d *DryRunClient) NetworkCreate(ctx context.Context, name string, options network.CreateOptions) (network.CreateResponse, error) {
|
||||
return network.CreateResponse{
|
||||
ID: name,
|
||||
Warning: "",
|
||||
func (d *DryRunClient) NetworkCreate(ctx context.Context, name string, options client.NetworkCreateOptions) (client.NetworkCreateResult, error) {
|
||||
return client.NetworkCreateResult{
|
||||
ID: name,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (d *DryRunClient) NetworkDisconnect(ctx context.Context, networkName, container string, force bool) error {
|
||||
return nil
|
||||
func (d *DryRunClient) NetworkDisconnect(ctx context.Context, networkName string, options client.NetworkDisconnectOptions) (client.NetworkDisconnectResult, error) {
|
||||
return client.NetworkDisconnectResult{}, nil
|
||||
}
|
||||
|
||||
func (d *DryRunClient) NetworkRemove(ctx context.Context, networkName string) error {
|
||||
return nil
|
||||
func (d *DryRunClient) NetworkRemove(ctx context.Context, networkName string, options client.NetworkRemoveOptions) (client.NetworkRemoveResult, error) {
|
||||
return client.NetworkRemoveResult{}, nil
|
||||
}
|
||||
|
||||
func (d *DryRunClient) VolumeCreate(ctx context.Context, options volume.CreateOptions) (volume.Volume, error) {
|
||||
return volume.Volume{
|
||||
ClusterVolume: nil,
|
||||
Driver: options.Driver,
|
||||
Labels: options.Labels,
|
||||
Name: options.Name,
|
||||
Options: options.DriverOpts,
|
||||
func (d *DryRunClient) VolumeCreate(ctx context.Context, options client.VolumeCreateOptions) (client.VolumeCreateResult, error) {
|
||||
return client.VolumeCreateResult{
|
||||
Volume: volume.Volume{
|
||||
ClusterVolume: nil,
|
||||
Driver: options.Driver,
|
||||
Labels: options.Labels,
|
||||
Name: options.Name,
|
||||
Options: options.DriverOpts,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (d *DryRunClient) VolumeRemove(ctx context.Context, volumeID string, force bool) error {
|
||||
return nil
|
||||
func (d *DryRunClient) VolumeRemove(ctx context.Context, volumeID string, options client.VolumeRemoveOptions) (client.VolumeRemoveResult, error) {
|
||||
return client.VolumeRemoveResult{}, nil
|
||||
}
|
||||
|
||||
func (d *DryRunClient) ContainerExecCreate(ctx context.Context, container string, config containerType.ExecOptions) (containerType.ExecCreateResponse, error) {
|
||||
func (d *DryRunClient) ExecCreate(ctx context.Context, container string, config client.ExecCreateOptions) (client.ExecCreateResult, error) {
|
||||
b := make([]byte, 32)
|
||||
_, _ = rand.Read(b)
|
||||
id := fmt.Sprintf("%x", b)
|
||||
@@ -300,347 +290,327 @@ func (d *DryRunClient) ContainerExecCreate(ctx context.Context, container string
|
||||
container: container,
|
||||
command: config.Cmd,
|
||||
})
|
||||
return containerType.ExecCreateResponse{
|
||||
return client.ExecCreateResult{
|
||||
ID: id,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (d *DryRunClient) ContainerExecStart(ctx context.Context, execID string, config containerType.ExecStartOptions) error {
|
||||
func (d *DryRunClient) ExecStart(ctx context.Context, execID string, config client.ExecStartOptions) (client.ExecStartResult, error) {
|
||||
_, ok := d.execs.LoadAndDelete(execID)
|
||||
if !ok {
|
||||
return fmt.Errorf("invalid exec ID %q", execID)
|
||||
return client.ExecStartResult{}, fmt.Errorf("invalid exec ID %q", execID)
|
||||
}
|
||||
return nil
|
||||
return client.ExecStartResult{}, nil
|
||||
}
|
||||
|
||||
// Functions delegated to original APIClient (not used by Compose or not modifying the Compose stack)
|
||||
|
||||
func (d *DryRunClient) ConfigList(ctx context.Context, options swarm.ConfigListOptions) ([]swarm.Config, error) {
|
||||
func (d *DryRunClient) ConfigList(ctx context.Context, options client.ConfigListOptions) (client.ConfigListResult, error) {
|
||||
return d.apiClient.ConfigList(ctx, options)
|
||||
}
|
||||
|
||||
func (d *DryRunClient) ConfigCreate(ctx context.Context, config swarm.ConfigSpec) (swarm.ConfigCreateResponse, error) {
|
||||
return d.apiClient.ConfigCreate(ctx, config)
|
||||
func (d *DryRunClient) ConfigInspect(ctx context.Context, name string, options client.ConfigInspectOptions) (client.ConfigInspectResult, error) {
|
||||
return d.apiClient.ConfigInspect(ctx, name, options)
|
||||
}
|
||||
|
||||
func (d *DryRunClient) ConfigRemove(ctx context.Context, id string) error {
|
||||
return d.apiClient.ConfigRemove(ctx, id)
|
||||
func (d *DryRunClient) ConfigCreate(ctx context.Context, options client.ConfigCreateOptions) (client.ConfigCreateResult, error) {
|
||||
return d.apiClient.ConfigCreate(ctx, options)
|
||||
}
|
||||
|
||||
func (d *DryRunClient) ConfigInspectWithRaw(ctx context.Context, name string) (swarm.Config, []byte, error) {
|
||||
return d.apiClient.ConfigInspectWithRaw(ctx, name)
|
||||
func (d *DryRunClient) ConfigRemove(ctx context.Context, id string, options client.ConfigRemoveOptions) (client.ConfigRemoveResult, error) {
|
||||
return d.apiClient.ConfigRemove(ctx, id, options)
|
||||
}
|
||||
|
||||
func (d *DryRunClient) ConfigUpdate(ctx context.Context, id string, version swarm.Version, config swarm.ConfigSpec) error {
|
||||
return d.apiClient.ConfigUpdate(ctx, id, version, config)
|
||||
func (d *DryRunClient) ConfigUpdate(ctx context.Context, id string, options client.ConfigUpdateOptions) (client.ConfigUpdateResult, error) {
|
||||
return d.apiClient.ConfigUpdate(ctx, id, options)
|
||||
}
|
||||
|
||||
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 client.ContainerCommitOptions) (client.ContainerCommitResult, error) {
|
||||
return d.apiClient.ContainerCommit(ctx, container, options)
|
||||
}
|
||||
|
||||
func (d *DryRunClient) ContainerDiff(ctx context.Context, container string) ([]containerType.FilesystemChange, error) {
|
||||
return d.apiClient.ContainerDiff(ctx, container)
|
||||
func (d *DryRunClient) ContainerDiff(ctx context.Context, container string, options client.ContainerDiffOptions) (client.ContainerDiffResult, error) {
|
||||
return d.apiClient.ContainerDiff(ctx, container, options)
|
||||
}
|
||||
|
||||
func (d *DryRunClient) ContainerExecAttach(ctx context.Context, execID string, config containerType.ExecStartOptions) (moby.HijackedResponse, error) {
|
||||
return moby.HijackedResponse{}, errors.New("interactive exec is not supported in dry-run mode")
|
||||
func (d *DryRunClient) ExecAttach(ctx context.Context, execID string, config client.ExecAttachOptions) (client.ExecAttachResult, error) {
|
||||
return client.ExecAttachResult{}, errors.New("interactive exec is not supported in dry-run mode")
|
||||
}
|
||||
|
||||
func (d *DryRunClient) ContainerExecInspect(ctx context.Context, execID string) (containerType.ExecInspect, error) {
|
||||
return d.apiClient.ContainerExecInspect(ctx, execID)
|
||||
func (d *DryRunClient) ExecInspect(ctx context.Context, execID string, options client.ExecInspectOptions) (client.ExecInspectResult, error) {
|
||||
return d.apiClient.ExecInspect(ctx, execID, options)
|
||||
}
|
||||
|
||||
func (d *DryRunClient) ContainerExecResize(ctx context.Context, execID string, options containerType.ResizeOptions) error {
|
||||
return d.apiClient.ContainerExecResize(ctx, execID, options)
|
||||
func (d *DryRunClient) ExecResize(ctx context.Context, execID string, options client.ExecResizeOptions) (client.ExecResizeResult, error) {
|
||||
return d.apiClient.ExecResize(ctx, execID, options)
|
||||
}
|
||||
|
||||
func (d *DryRunClient) ContainerExport(ctx context.Context, container string) (io.ReadCloser, error) {
|
||||
return d.apiClient.ContainerExport(ctx, container)
|
||||
func (d *DryRunClient) ContainerExport(ctx context.Context, container string, options client.ContainerExportOptions) (client.ContainerExportResult, error) {
|
||||
return d.apiClient.ContainerExport(ctx, container, options)
|
||||
}
|
||||
|
||||
func (d *DryRunClient) ContainerInspectWithRaw(ctx context.Context, container string, getSize bool) (containerType.InspectResponse, []byte, error) {
|
||||
return d.apiClient.ContainerInspectWithRaw(ctx, container, getSize)
|
||||
}
|
||||
|
||||
func (d *DryRunClient) ContainerLogs(ctx context.Context, container string, options containerType.LogsOptions) (io.ReadCloser, error) {
|
||||
func (d *DryRunClient) ContainerLogs(ctx context.Context, container string, options client.ContainerLogsOptions) (client.ContainerLogsResult, error) {
|
||||
return d.apiClient.ContainerLogs(ctx, container, options)
|
||||
}
|
||||
|
||||
func (d *DryRunClient) ContainerResize(ctx context.Context, container string, options containerType.ResizeOptions) error {
|
||||
func (d *DryRunClient) ContainerResize(ctx context.Context, container string, options client.ContainerResizeOptions) (client.ContainerResizeResult, error) {
|
||||
return d.apiClient.ContainerResize(ctx, container, options)
|
||||
}
|
||||
|
||||
func (d *DryRunClient) ContainerStatPath(ctx context.Context, container, path string) (containerType.PathStat, error) {
|
||||
return d.apiClient.ContainerStatPath(ctx, container, path)
|
||||
func (d *DryRunClient) ContainerStatPath(ctx context.Context, container string, options client.ContainerStatPathOptions) (client.ContainerStatPathResult, error) {
|
||||
return d.apiClient.ContainerStatPath(ctx, container, options)
|
||||
}
|
||||
|
||||
func (d *DryRunClient) ContainerStats(ctx context.Context, container string, stream bool) (containerType.StatsResponseReader, error) {
|
||||
return d.apiClient.ContainerStats(ctx, container, stream)
|
||||
func (d *DryRunClient) ContainerStats(ctx context.Context, container string, options client.ContainerStatsOptions) (client.ContainerStatsResult, error) {
|
||||
return d.apiClient.ContainerStats(ctx, container, options)
|
||||
}
|
||||
|
||||
func (d *DryRunClient) ContainerStatsOneShot(ctx context.Context, container string) (containerType.StatsResponseReader, error) {
|
||||
return d.apiClient.ContainerStatsOneShot(ctx, container)
|
||||
func (d *DryRunClient) ContainerTop(ctx context.Context, container string, options client.ContainerTopOptions) (client.ContainerTopResult, error) {
|
||||
return d.apiClient.ContainerTop(ctx, container, options)
|
||||
}
|
||||
|
||||
func (d *DryRunClient) ContainerTop(ctx context.Context, container string, arguments []string) (containerType.TopResponse, error) {
|
||||
return d.apiClient.ContainerTop(ctx, container, arguments)
|
||||
func (d *DryRunClient) ContainerUpdate(ctx context.Context, container string, options client.ContainerUpdateOptions) (client.ContainerUpdateResult, error) {
|
||||
return d.apiClient.ContainerUpdate(ctx, container, options)
|
||||
}
|
||||
|
||||
func (d *DryRunClient) ContainerUpdate(ctx context.Context, container string, updateConfig containerType.UpdateConfig) (containerType.UpdateResponse, error) {
|
||||
return d.apiClient.ContainerUpdate(ctx, container, updateConfig)
|
||||
func (d *DryRunClient) ContainerWait(ctx context.Context, container string, options client.ContainerWaitOptions) client.ContainerWaitResult {
|
||||
return d.apiClient.ContainerWait(ctx, container, options)
|
||||
}
|
||||
|
||||
func (d *DryRunClient) ContainerWait(ctx context.Context, container string, condition containerType.WaitCondition) (<-chan containerType.WaitResponse, <-chan error) {
|
||||
return d.apiClient.ContainerWait(ctx, container, condition)
|
||||
func (d *DryRunClient) ContainerPrune(ctx context.Context, options client.ContainerPruneOptions) (client.ContainerPruneResult, error) {
|
||||
return d.apiClient.ContainerPrune(ctx, options)
|
||||
}
|
||||
|
||||
func (d *DryRunClient) ContainersPrune(ctx context.Context, pruneFilters filters.Args) (containerType.PruneReport, error) {
|
||||
return d.apiClient.ContainersPrune(ctx, pruneFilters)
|
||||
func (d *DryRunClient) DistributionInspect(ctx context.Context, imageName string, options client.DistributionInspectOptions) (client.DistributionInspectResult, error) {
|
||||
return d.apiClient.DistributionInspect(ctx, imageName, options)
|
||||
}
|
||||
|
||||
func (d *DryRunClient) DistributionInspect(ctx context.Context, imageName, encodedRegistryAuth string) (registry.DistributionInspect, error) {
|
||||
return d.apiClient.DistributionInspect(ctx, imageName, encodedRegistryAuth)
|
||||
}
|
||||
|
||||
func (d *DryRunClient) BuildCachePrune(ctx context.Context, opts build.CachePruneOptions) (*build.CachePruneReport, error) {
|
||||
func (d *DryRunClient) BuildCachePrune(ctx context.Context, opts client.BuildCachePruneOptions) (client.BuildCachePruneResult, error) {
|
||||
return d.apiClient.BuildCachePrune(ctx, opts)
|
||||
}
|
||||
|
||||
func (d *DryRunClient) BuildCancel(ctx context.Context, id string) error {
|
||||
return d.apiClient.BuildCancel(ctx, id)
|
||||
func (d *DryRunClient) BuildCancel(ctx context.Context, id string, opts client.BuildCancelOptions) (client.BuildCancelResult, error) {
|
||||
return d.apiClient.BuildCancel(ctx, id, opts)
|
||||
}
|
||||
|
||||
func (d *DryRunClient) ImageCreate(ctx context.Context, parentReference string, options image.CreateOptions) (io.ReadCloser, error) {
|
||||
return d.apiClient.ImageCreate(ctx, parentReference, options)
|
||||
}
|
||||
|
||||
func (d *DryRunClient) ImageHistory(ctx context.Context, imageName string, options ...client.ImageHistoryOption) ([]image.HistoryResponseItem, error) {
|
||||
func (d *DryRunClient) ImageHistory(ctx context.Context, imageName string, options ...client.ImageHistoryOption) (client.ImageHistoryResult, error) {
|
||||
return d.apiClient.ImageHistory(ctx, imageName, options...)
|
||||
}
|
||||
|
||||
func (d *DryRunClient) ImageImport(ctx context.Context, source image.ImportSource, ref string, options image.ImportOptions) (io.ReadCloser, error) {
|
||||
func (d *DryRunClient) ImageImport(ctx context.Context, source client.ImageImportSource, ref string, options client.ImageImportOptions) (client.ImageImportResult, error) {
|
||||
return d.apiClient.ImageImport(ctx, source, ref, options)
|
||||
}
|
||||
|
||||
func (d *DryRunClient) ImageList(ctx context.Context, options image.ListOptions) ([]image.Summary, error) {
|
||||
func (d *DryRunClient) ImageList(ctx context.Context, options client.ImageListOptions) (client.ImageListResult, error) {
|
||||
return d.apiClient.ImageList(ctx, options)
|
||||
}
|
||||
|
||||
func (d *DryRunClient) ImageLoad(ctx context.Context, input io.Reader, options ...client.ImageLoadOption) (image.LoadResponse, error) {
|
||||
func (d *DryRunClient) ImageLoad(ctx context.Context, input io.Reader, options ...client.ImageLoadOption) (client.ImageLoadResult, error) {
|
||||
return d.apiClient.ImageLoad(ctx, input, options...)
|
||||
}
|
||||
|
||||
func (d *DryRunClient) ImageSearch(ctx context.Context, term string, options registry.SearchOptions) ([]registry.SearchResult, error) {
|
||||
func (d *DryRunClient) ImageSearch(ctx context.Context, term string, options client.ImageSearchOptions) (client.ImageSearchResult, error) {
|
||||
return d.apiClient.ImageSearch(ctx, term, options)
|
||||
}
|
||||
|
||||
func (d *DryRunClient) ImageSave(ctx context.Context, images []string, options ...client.ImageSaveOption) (io.ReadCloser, error) {
|
||||
func (d *DryRunClient) ImageSave(ctx context.Context, images []string, options ...client.ImageSaveOption) (client.ImageSaveResult, error) {
|
||||
return d.apiClient.ImageSave(ctx, images, options...)
|
||||
}
|
||||
|
||||
func (d *DryRunClient) ImageTag(ctx context.Context, imageName, ref string) error {
|
||||
return d.apiClient.ImageTag(ctx, imageName, ref)
|
||||
func (d *DryRunClient) ImageTag(ctx context.Context, options client.ImageTagOptions) (client.ImageTagResult, error) {
|
||||
return d.apiClient.ImageTag(ctx, options)
|
||||
}
|
||||
|
||||
func (d *DryRunClient) ImagesPrune(ctx context.Context, pruneFilter filters.Args) (image.PruneReport, error) {
|
||||
return d.apiClient.ImagesPrune(ctx, pruneFilter)
|
||||
func (d *DryRunClient) ImagePrune(ctx context.Context, options client.ImagePruneOptions) (client.ImagePruneResult, error) {
|
||||
return d.apiClient.ImagePrune(ctx, options)
|
||||
}
|
||||
|
||||
func (d *DryRunClient) NodeInspectWithRaw(ctx context.Context, nodeID string) (swarm.Node, []byte, error) {
|
||||
return d.apiClient.NodeInspectWithRaw(ctx, nodeID)
|
||||
func (d *DryRunClient) NodeInspect(ctx context.Context, nodeID string, options client.NodeInspectOptions) (client.NodeInspectResult, error) {
|
||||
return d.apiClient.NodeInspect(ctx, nodeID, options)
|
||||
}
|
||||
|
||||
func (d *DryRunClient) NodeList(ctx context.Context, options swarm.NodeListOptions) ([]swarm.Node, error) {
|
||||
func (d *DryRunClient) NodeList(ctx context.Context, options client.NodeListOptions) (client.NodeListResult, error) {
|
||||
return d.apiClient.NodeList(ctx, options)
|
||||
}
|
||||
|
||||
func (d *DryRunClient) NodeRemove(ctx context.Context, nodeID string, options swarm.NodeRemoveOptions) error {
|
||||
func (d *DryRunClient) NodeRemove(ctx context.Context, nodeID string, options client.NodeRemoveOptions) (client.NodeRemoveResult, error) {
|
||||
return d.apiClient.NodeRemove(ctx, nodeID, options)
|
||||
}
|
||||
|
||||
func (d *DryRunClient) NodeUpdate(ctx context.Context, nodeID string, version swarm.Version, node swarm.NodeSpec) error {
|
||||
return d.apiClient.NodeUpdate(ctx, nodeID, version, node)
|
||||
func (d *DryRunClient) NodeUpdate(ctx context.Context, nodeID string, options client.NodeUpdateOptions) (client.NodeUpdateResult, error) {
|
||||
return d.apiClient.NodeUpdate(ctx, nodeID, options)
|
||||
}
|
||||
|
||||
func (d *DryRunClient) NetworkInspect(ctx context.Context, networkName string, options network.InspectOptions) (network.Inspect, error) {
|
||||
func (d *DryRunClient) NetworkInspect(ctx context.Context, networkName string, options client.NetworkInspectOptions) (client.NetworkInspectResult, error) {
|
||||
return d.apiClient.NetworkInspect(ctx, networkName, options)
|
||||
}
|
||||
|
||||
func (d *DryRunClient) NetworkInspectWithRaw(ctx context.Context, networkName string, options network.InspectOptions) (network.Inspect, []byte, error) {
|
||||
return d.apiClient.NetworkInspectWithRaw(ctx, networkName, options)
|
||||
}
|
||||
|
||||
func (d *DryRunClient) NetworkList(ctx context.Context, options network.ListOptions) ([]network.Inspect, error) {
|
||||
func (d *DryRunClient) NetworkList(ctx context.Context, options client.NetworkListOptions) (client.NetworkListResult, error) {
|
||||
return d.apiClient.NetworkList(ctx, options)
|
||||
}
|
||||
|
||||
func (d *DryRunClient) NetworksPrune(ctx context.Context, pruneFilter filters.Args) (network.PruneReport, error) {
|
||||
return d.apiClient.NetworksPrune(ctx, pruneFilter)
|
||||
func (d *DryRunClient) NetworkPrune(ctx context.Context, options client.NetworkPruneOptions) (client.NetworkPruneResult, error) {
|
||||
return d.apiClient.NetworkPrune(ctx, options)
|
||||
}
|
||||
|
||||
func (d *DryRunClient) PluginList(ctx context.Context, filter filters.Args) (moby.PluginsListResponse, error) {
|
||||
return d.apiClient.PluginList(ctx, filter)
|
||||
func (d *DryRunClient) PluginList(ctx context.Context, options client.PluginListOptions) (client.PluginListResult, error) {
|
||||
return d.apiClient.PluginList(ctx, options)
|
||||
}
|
||||
|
||||
func (d *DryRunClient) PluginRemove(ctx context.Context, name string, options moby.PluginRemoveOptions) error {
|
||||
func (d *DryRunClient) PluginRemove(ctx context.Context, name string, options client.PluginRemoveOptions) (client.PluginRemoveResult, error) {
|
||||
return d.apiClient.PluginRemove(ctx, name, options)
|
||||
}
|
||||
|
||||
func (d *DryRunClient) PluginEnable(ctx context.Context, name string, options moby.PluginEnableOptions) error {
|
||||
func (d *DryRunClient) PluginEnable(ctx context.Context, name string, options client.PluginEnableOptions) (client.PluginEnableResult, error) {
|
||||
return d.apiClient.PluginEnable(ctx, name, options)
|
||||
}
|
||||
|
||||
func (d *DryRunClient) PluginDisable(ctx context.Context, name string, options moby.PluginDisableOptions) error {
|
||||
func (d *DryRunClient) PluginDisable(ctx context.Context, name string, options client.PluginDisableOptions) (client.PluginDisableResult, error) {
|
||||
return d.apiClient.PluginDisable(ctx, name, options)
|
||||
}
|
||||
|
||||
func (d *DryRunClient) PluginInstall(ctx context.Context, name string, options moby.PluginInstallOptions) (io.ReadCloser, error) {
|
||||
func (d *DryRunClient) PluginInstall(ctx context.Context, name string, options client.PluginInstallOptions) (client.PluginInstallResult, error) {
|
||||
return d.apiClient.PluginInstall(ctx, name, options)
|
||||
}
|
||||
|
||||
func (d *DryRunClient) PluginUpgrade(ctx context.Context, name string, options moby.PluginInstallOptions) (io.ReadCloser, error) {
|
||||
func (d *DryRunClient) PluginUpgrade(ctx context.Context, name string, options client.PluginUpgradeOptions) (client.PluginUpgradeResult, error) {
|
||||
return d.apiClient.PluginUpgrade(ctx, name, options)
|
||||
}
|
||||
|
||||
func (d *DryRunClient) PluginPush(ctx context.Context, name string, registryAuth string) (io.ReadCloser, error) {
|
||||
return d.apiClient.PluginPush(ctx, name, registryAuth)
|
||||
func (d *DryRunClient) PluginPush(ctx context.Context, name string, options client.PluginPushOptions) (client.PluginPushResult, error) {
|
||||
return d.apiClient.PluginPush(ctx, name, options)
|
||||
}
|
||||
|
||||
func (d *DryRunClient) PluginSet(ctx context.Context, name string, args []string) error {
|
||||
return d.apiClient.PluginSet(ctx, name, args)
|
||||
func (d *DryRunClient) PluginSet(ctx context.Context, name string, options client.PluginSetOptions) (client.PluginSetResult, error) {
|
||||
return d.apiClient.PluginSet(ctx, name, options)
|
||||
}
|
||||
|
||||
func (d *DryRunClient) PluginInspectWithRaw(ctx context.Context, name string) (*moby.Plugin, []byte, error) {
|
||||
return d.apiClient.PluginInspectWithRaw(ctx, name)
|
||||
func (d *DryRunClient) PluginInspect(ctx context.Context, name string, options client.PluginInspectOptions) (client.PluginInspectResult, error) {
|
||||
return d.apiClient.PluginInspect(ctx, name, options)
|
||||
}
|
||||
|
||||
func (d *DryRunClient) PluginCreate(ctx context.Context, createContext io.Reader, options moby.PluginCreateOptions) error {
|
||||
func (d *DryRunClient) PluginCreate(ctx context.Context, createContext io.Reader, options client.PluginCreateOptions) (client.PluginCreateResult, error) {
|
||||
return d.apiClient.PluginCreate(ctx, createContext, options)
|
||||
}
|
||||
|
||||
func (d *DryRunClient) ServiceCreate(ctx context.Context, service swarm.ServiceSpec, options swarm.ServiceCreateOptions) (swarm.ServiceCreateResponse, error) {
|
||||
return d.apiClient.ServiceCreate(ctx, service, options)
|
||||
func (d *DryRunClient) ServiceCreate(ctx context.Context, options client.ServiceCreateOptions) (client.ServiceCreateResult, error) {
|
||||
return d.apiClient.ServiceCreate(ctx, options)
|
||||
}
|
||||
|
||||
func (d *DryRunClient) ServiceInspectWithRaw(ctx context.Context, serviceID string, options swarm.ServiceInspectOptions) (swarm.Service, []byte, error) {
|
||||
return d.apiClient.ServiceInspectWithRaw(ctx, serviceID, options)
|
||||
func (d *DryRunClient) ServiceInspect(ctx context.Context, serviceID string, options client.ServiceInspectOptions) (client.ServiceInspectResult, error) {
|
||||
return d.apiClient.ServiceInspect(ctx, serviceID, options)
|
||||
}
|
||||
|
||||
func (d *DryRunClient) ServiceList(ctx context.Context, options swarm.ServiceListOptions) ([]swarm.Service, error) {
|
||||
func (d *DryRunClient) ServiceList(ctx context.Context, options client.ServiceListOptions) (client.ServiceListResult, error) {
|
||||
return d.apiClient.ServiceList(ctx, options)
|
||||
}
|
||||
|
||||
func (d *DryRunClient) ServiceRemove(ctx context.Context, serviceID string) error {
|
||||
return d.apiClient.ServiceRemove(ctx, serviceID)
|
||||
func (d *DryRunClient) ServiceRemove(ctx context.Context, serviceID string, options client.ServiceRemoveOptions) (client.ServiceRemoveResult, error) {
|
||||
return d.apiClient.ServiceRemove(ctx, serviceID, options)
|
||||
}
|
||||
|
||||
func (d *DryRunClient) ServiceUpdate(ctx context.Context, serviceID string, version swarm.Version, service swarm.ServiceSpec, options swarm.ServiceUpdateOptions) (swarm.ServiceUpdateResponse, error) {
|
||||
return d.apiClient.ServiceUpdate(ctx, serviceID, version, service, options)
|
||||
func (d *DryRunClient) ServiceUpdate(ctx context.Context, serviceID string, options client.ServiceUpdateOptions) (client.ServiceUpdateResult, error) {
|
||||
return d.apiClient.ServiceUpdate(ctx, serviceID, options)
|
||||
}
|
||||
|
||||
func (d *DryRunClient) ServiceLogs(ctx context.Context, serviceID string, options containerType.LogsOptions) (io.ReadCloser, error) {
|
||||
func (d *DryRunClient) ServiceLogs(ctx context.Context, serviceID string, options client.ServiceLogsOptions) (client.ServiceLogsResult, error) {
|
||||
return d.apiClient.ServiceLogs(ctx, serviceID, options)
|
||||
}
|
||||
|
||||
func (d *DryRunClient) TaskLogs(ctx context.Context, taskID string, options containerType.LogsOptions) (io.ReadCloser, error) {
|
||||
func (d *DryRunClient) TaskLogs(ctx context.Context, taskID string, options client.TaskLogsOptions) (client.TaskLogsResult, error) {
|
||||
return d.apiClient.TaskLogs(ctx, taskID, options)
|
||||
}
|
||||
|
||||
func (d *DryRunClient) TaskInspectWithRaw(ctx context.Context, taskID string) (swarm.Task, []byte, error) {
|
||||
return d.apiClient.TaskInspectWithRaw(ctx, taskID)
|
||||
func (d *DryRunClient) TaskInspect(ctx context.Context, taskID string, options client.TaskInspectOptions) (client.TaskInspectResult, error) {
|
||||
return d.apiClient.TaskInspect(ctx, taskID, options)
|
||||
}
|
||||
|
||||
func (d *DryRunClient) TaskList(ctx context.Context, options swarm.TaskListOptions) ([]swarm.Task, error) {
|
||||
func (d *DryRunClient) TaskList(ctx context.Context, options client.TaskListOptions) (client.TaskListResult, error) {
|
||||
return d.apiClient.TaskList(ctx, options)
|
||||
}
|
||||
|
||||
func (d *DryRunClient) SwarmInit(ctx context.Context, req swarm.InitRequest) (string, error) {
|
||||
return d.apiClient.SwarmInit(ctx, req)
|
||||
func (d *DryRunClient) SwarmInit(ctx context.Context, options client.SwarmInitOptions) (client.SwarmInitResult, error) {
|
||||
return d.apiClient.SwarmInit(ctx, options)
|
||||
}
|
||||
|
||||
func (d *DryRunClient) SwarmJoin(ctx context.Context, req swarm.JoinRequest) error {
|
||||
return d.apiClient.SwarmJoin(ctx, req)
|
||||
func (d *DryRunClient) SwarmJoin(ctx context.Context, options client.SwarmJoinOptions) (client.SwarmJoinResult, error) {
|
||||
return d.apiClient.SwarmJoin(ctx, options)
|
||||
}
|
||||
|
||||
func (d *DryRunClient) SwarmGetUnlockKey(ctx context.Context) (swarm.UnlockKeyResponse, error) {
|
||||
func (d *DryRunClient) SwarmGetUnlockKey(ctx context.Context) (client.SwarmGetUnlockKeyResult, error) {
|
||||
return d.apiClient.SwarmGetUnlockKey(ctx)
|
||||
}
|
||||
|
||||
func (d *DryRunClient) SwarmUnlock(ctx context.Context, req swarm.UnlockRequest) error {
|
||||
return d.apiClient.SwarmUnlock(ctx, req)
|
||||
func (d *DryRunClient) SwarmUnlock(ctx context.Context, options client.SwarmUnlockOptions) (client.SwarmUnlockResult, error) {
|
||||
return d.apiClient.SwarmUnlock(ctx, options)
|
||||
}
|
||||
|
||||
func (d *DryRunClient) SwarmLeave(ctx context.Context, force bool) error {
|
||||
return d.apiClient.SwarmLeave(ctx, force)
|
||||
func (d *DryRunClient) SwarmLeave(ctx context.Context, options client.SwarmLeaveOptions) (client.SwarmLeaveResult, error) {
|
||||
return d.apiClient.SwarmLeave(ctx, options)
|
||||
}
|
||||
|
||||
func (d *DryRunClient) SwarmInspect(ctx context.Context) (swarm.Swarm, error) {
|
||||
return d.apiClient.SwarmInspect(ctx)
|
||||
func (d *DryRunClient) SwarmInspect(ctx context.Context, options client.SwarmInspectOptions) (client.SwarmInspectResult, error) {
|
||||
return d.apiClient.SwarmInspect(ctx, options)
|
||||
}
|
||||
|
||||
func (d *DryRunClient) SwarmUpdate(ctx context.Context, version swarm.Version, swarmSpec swarm.Spec, flags swarm.UpdateFlags) error {
|
||||
return d.apiClient.SwarmUpdate(ctx, version, swarmSpec, flags)
|
||||
func (d *DryRunClient) SwarmUpdate(ctx context.Context, options client.SwarmUpdateOptions) (client.SwarmUpdateResult, error) {
|
||||
return d.apiClient.SwarmUpdate(ctx, options)
|
||||
}
|
||||
|
||||
func (d *DryRunClient) SecretList(ctx context.Context, options swarm.SecretListOptions) ([]swarm.Secret, error) {
|
||||
func (d *DryRunClient) SecretList(ctx context.Context, options client.SecretListOptions) (client.SecretListResult, error) {
|
||||
return d.apiClient.SecretList(ctx, options)
|
||||
}
|
||||
|
||||
func (d *DryRunClient) SecretCreate(ctx context.Context, secret swarm.SecretSpec) (swarm.SecretCreateResponse, error) {
|
||||
return d.apiClient.SecretCreate(ctx, secret)
|
||||
func (d *DryRunClient) SecretCreate(ctx context.Context, options client.SecretCreateOptions) (client.SecretCreateResult, error) {
|
||||
return d.apiClient.SecretCreate(ctx, options)
|
||||
}
|
||||
|
||||
func (d *DryRunClient) SecretRemove(ctx context.Context, id string) error {
|
||||
return d.apiClient.SecretRemove(ctx, id)
|
||||
func (d *DryRunClient) SecretRemove(ctx context.Context, id string, options client.SecretRemoveOptions) (client.SecretRemoveResult, error) {
|
||||
return d.apiClient.SecretRemove(ctx, id, options)
|
||||
}
|
||||
|
||||
func (d *DryRunClient) SecretInspectWithRaw(ctx context.Context, name string) (swarm.Secret, []byte, error) {
|
||||
return d.apiClient.SecretInspectWithRaw(ctx, name)
|
||||
func (d *DryRunClient) SecretInspect(ctx context.Context, name string, options client.SecretInspectOptions) (client.SecretInspectResult, error) {
|
||||
return d.apiClient.SecretInspect(ctx, name, options)
|
||||
}
|
||||
|
||||
func (d *DryRunClient) SecretUpdate(ctx context.Context, id string, version swarm.Version, secret swarm.SecretSpec) error {
|
||||
return d.apiClient.SecretUpdate(ctx, id, version, secret)
|
||||
func (d *DryRunClient) SecretUpdate(ctx context.Context, id string, options client.SecretUpdateOptions) (client.SecretUpdateResult, error) {
|
||||
return d.apiClient.SecretUpdate(ctx, id, options)
|
||||
}
|
||||
|
||||
func (d *DryRunClient) Events(ctx context.Context, options events.ListOptions) (<-chan events.Message, <-chan error) {
|
||||
func (d *DryRunClient) Events(ctx context.Context, options client.EventsListOptions) client.EventsResult {
|
||||
return d.apiClient.Events(ctx, options)
|
||||
}
|
||||
|
||||
func (d *DryRunClient) Info(ctx context.Context) (system.Info, error) {
|
||||
return d.apiClient.Info(ctx)
|
||||
func (d *DryRunClient) Info(ctx context.Context, options client.InfoOptions) (client.SystemInfoResult, error) {
|
||||
return d.apiClient.Info(ctx, options)
|
||||
}
|
||||
|
||||
func (d *DryRunClient) RegistryLogin(ctx context.Context, auth registry.AuthConfig) (registry.AuthenticateOKBody, error) {
|
||||
return d.apiClient.RegistryLogin(ctx, auth)
|
||||
func (d *DryRunClient) RegistryLogin(ctx context.Context, options client.RegistryLoginOptions) (client.RegistryLoginResult, error) {
|
||||
return d.apiClient.RegistryLogin(ctx, options)
|
||||
}
|
||||
|
||||
func (d *DryRunClient) DiskUsage(ctx context.Context, options moby.DiskUsageOptions) (moby.DiskUsage, error) {
|
||||
func (d *DryRunClient) DiskUsage(ctx context.Context, options client.DiskUsageOptions) (client.DiskUsageResult, error) {
|
||||
return d.apiClient.DiskUsage(ctx, options)
|
||||
}
|
||||
|
||||
func (d *DryRunClient) Ping(ctx context.Context) (moby.Ping, error) {
|
||||
return d.apiClient.Ping(ctx)
|
||||
func (d *DryRunClient) Ping(ctx context.Context, options client.PingOptions) (client.PingResult, error) {
|
||||
return d.apiClient.Ping(ctx, options)
|
||||
}
|
||||
|
||||
func (d *DryRunClient) VolumeInspect(ctx context.Context, volumeID string) (volume.Volume, error) {
|
||||
return d.apiClient.VolumeInspect(ctx, volumeID)
|
||||
func (d *DryRunClient) VolumeInspect(ctx context.Context, volumeID string, options client.VolumeInspectOptions) (client.VolumeInspectResult, error) {
|
||||
return d.apiClient.VolumeInspect(ctx, volumeID, options)
|
||||
}
|
||||
|
||||
func (d *DryRunClient) VolumeInspectWithRaw(ctx context.Context, volumeID string) (volume.Volume, []byte, error) {
|
||||
return d.apiClient.VolumeInspectWithRaw(ctx, volumeID)
|
||||
}
|
||||
|
||||
func (d *DryRunClient) VolumeList(ctx context.Context, opts volume.ListOptions) (volume.ListResponse, error) {
|
||||
func (d *DryRunClient) VolumeList(ctx context.Context, opts client.VolumeListOptions) (client.VolumeListResult, error) {
|
||||
return d.apiClient.VolumeList(ctx, opts)
|
||||
}
|
||||
|
||||
func (d *DryRunClient) VolumesPrune(ctx context.Context, pruneFilter filters.Args) (volume.PruneReport, error) {
|
||||
return d.apiClient.VolumesPrune(ctx, pruneFilter)
|
||||
func (d *DryRunClient) VolumePrune(ctx context.Context, options client.VolumePruneOptions) (client.VolumePruneResult, error) {
|
||||
return d.apiClient.VolumePrune(ctx, options)
|
||||
}
|
||||
|
||||
func (d *DryRunClient) VolumeUpdate(ctx context.Context, volumeID string, version swarm.Version, options volume.UpdateOptions) error {
|
||||
return d.apiClient.VolumeUpdate(ctx, volumeID, version, options)
|
||||
func (d *DryRunClient) VolumeUpdate(ctx context.Context, volumeID string, options client.VolumeUpdateOptions) (client.VolumeUpdateResult, error) {
|
||||
return d.apiClient.VolumeUpdate(ctx, volumeID, options)
|
||||
}
|
||||
|
||||
func (d *DryRunClient) ClientVersion() string {
|
||||
@@ -651,20 +621,8 @@ func (d *DryRunClient) DaemonHost() string {
|
||||
return d.apiClient.DaemonHost()
|
||||
}
|
||||
|
||||
func (d *DryRunClient) HTTPClient() *http.Client {
|
||||
return d.apiClient.HTTPClient()
|
||||
}
|
||||
|
||||
func (d *DryRunClient) ServerVersion(ctx context.Context) (moby.Version, error) {
|
||||
return d.apiClient.ServerVersion(ctx)
|
||||
}
|
||||
|
||||
func (d *DryRunClient) NegotiateAPIVersion(ctx context.Context) {
|
||||
d.apiClient.NegotiateAPIVersion(ctx)
|
||||
}
|
||||
|
||||
func (d *DryRunClient) NegotiateAPIVersionPing(ping moby.Ping) {
|
||||
d.apiClient.NegotiateAPIVersionPing(ping)
|
||||
func (d *DryRunClient) ServerVersion(ctx context.Context, options client.ServerVersionOptions) (client.ServerVersionResult, error) {
|
||||
return d.apiClient.ServerVersion(ctx, options)
|
||||
}
|
||||
|
||||
func (d *DryRunClient) DialHijack(ctx context.Context, url, proto string, meta map[string][]string) (net.Conn, error) {
|
||||
@@ -679,14 +637,14 @@ func (d *DryRunClient) Close() error {
|
||||
return d.apiClient.Close()
|
||||
}
|
||||
|
||||
func (d *DryRunClient) CheckpointCreate(ctx context.Context, container string, options checkpoint.CreateOptions) error {
|
||||
func (d *DryRunClient) CheckpointCreate(ctx context.Context, container string, options client.CheckpointCreateOptions) (client.CheckpointCreateResult, error) {
|
||||
return d.apiClient.CheckpointCreate(ctx, container, options)
|
||||
}
|
||||
|
||||
func (d *DryRunClient) CheckpointDelete(ctx context.Context, container string, options checkpoint.DeleteOptions) error {
|
||||
return d.apiClient.CheckpointDelete(ctx, container, options)
|
||||
func (d *DryRunClient) CheckpointRemove(ctx context.Context, container string, options client.CheckpointRemoveOptions) (client.CheckpointRemoveResult, error) {
|
||||
return d.apiClient.CheckpointRemove(ctx, container, options)
|
||||
}
|
||||
|
||||
func (d *DryRunClient) CheckpointList(ctx context.Context, container string, options checkpoint.ListOptions) ([]checkpoint.Summary, error) {
|
||||
func (d *DryRunClient) CheckpointList(ctx context.Context, container string, options client.CheckpointListOptions) (client.CheckpointListResult, error) {
|
||||
return d.apiClient.CheckpointList(ctx, container, options)
|
||||
}
|
||||
|
||||
21
pkg/e2e/fixtures/image-volume-recreate/Dockerfile
Normal file
21
pkg/e2e/fixtures/image-volume-recreate/Dockerfile
Normal file
@@ -0,0 +1,21 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
#
|
||||
# Copyright 2020 Docker Compose CLI authors
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
FROM alpine
|
||||
WORKDIR /app
|
||||
ARG CONTENT=initial
|
||||
RUN echo "$CONTENT" > /app/content.txt
|
||||
18
pkg/e2e/fixtures/image-volume-recreate/compose.yaml
Normal file
18
pkg/e2e/fixtures/image-volume-recreate/compose.yaml
Normal file
@@ -0,0 +1,18 @@
|
||||
services:
|
||||
source:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
image: image-volume-source
|
||||
|
||||
consumer:
|
||||
image: alpine
|
||||
depends_on:
|
||||
- source
|
||||
command: ["cat", "/data/content.txt"]
|
||||
volumes:
|
||||
- type: image
|
||||
source: image-volume-source
|
||||
target: /data
|
||||
image:
|
||||
subpath: app
|
||||
@@ -109,7 +109,7 @@ func TestLocalComposeLargeLogs(t *testing.T) {
|
||||
|
||||
f, err := os.Create(file)
|
||||
assert.NilError(t, err)
|
||||
for i := 0; i < 300_000; i++ {
|
||||
for i := range 300_000 {
|
||||
_, err := io.WriteString(f, fmt.Sprintf("This is line %d in a laaaarge text file\n", i))
|
||||
assert.NilError(t, err)
|
||||
}
|
||||
|
||||
@@ -212,11 +212,9 @@ func TestNetworkRecreate(t *testing.T) {
|
||||
res := c.RunDockerComposeCmd(t, "-f", "./fixtures/network-recreate/compose.yaml", "--project-name", projectName, "--progress=plain", "up", "-d")
|
||||
err := res.Stderr()
|
||||
fmt.Println(err)
|
||||
res.Assert(t, icmd.Expected{Err: `
|
||||
Container network_recreate-web-1 Stopped
|
||||
Network network_recreate_test Removed
|
||||
Network network_recreate_test Creating
|
||||
Network network_recreate_test Created
|
||||
Container network_recreate-web-1 Starting
|
||||
Container network_recreate-web-1 Started`})
|
||||
hasStopped := strings.Contains(err, "Stopped")
|
||||
hasResumed := strings.Contains(err, "Started") || strings.Contains(err, "Recreated")
|
||||
if !hasStopped || !hasResumed {
|
||||
t.Fatalf("unexpected output, missing expected events, stderr: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -190,3 +190,47 @@ func TestImageVolume(t *testing.T) {
|
||||
out := res.Combined()
|
||||
assert.Check(t, strings.Contains(out, "index.html"))
|
||||
}
|
||||
|
||||
func TestImageVolumeRecreateOnRebuild(t *testing.T) {
|
||||
c := NewCLI(t)
|
||||
const projectName = "compose-e2e-image-volume-recreate"
|
||||
t.Cleanup(func() {
|
||||
c.cleanupWithDown(t, projectName)
|
||||
c.RunDockerOrExitError(t, "rmi", "-f", "image-volume-source")
|
||||
})
|
||||
|
||||
version := c.RunDockerCmd(t, "version", "-f", "{{.Server.Version}}")
|
||||
major, _, found := strings.Cut(version.Combined(), ".")
|
||||
assert.Assert(t, found)
|
||||
if major == "26" || major == "27" {
|
||||
t.Skip("Skipping test due to docker version < 28")
|
||||
}
|
||||
|
||||
// First build and run with initial content
|
||||
c.RunDockerComposeCmd(t, "-f", "./fixtures/image-volume-recreate/compose.yaml",
|
||||
"--project-name", projectName, "build", "--build-arg", "CONTENT=foo")
|
||||
res := c.RunDockerComposeCmd(t, "-f", "./fixtures/image-volume-recreate/compose.yaml",
|
||||
"--project-name", projectName, "up", "-d")
|
||||
assert.Check(t, !strings.Contains(res.Combined(), "error"))
|
||||
|
||||
// Check initial content
|
||||
res = c.RunDockerComposeCmd(t, "-f", "./fixtures/image-volume-recreate/compose.yaml",
|
||||
"--project-name", projectName, "logs", "consumer")
|
||||
assert.Check(t, strings.Contains(res.Combined(), "foo"), "Expected 'foo' in output, got: %s", res.Combined())
|
||||
|
||||
// Rebuild source image with different content
|
||||
c.RunDockerComposeCmd(t, "-f", "./fixtures/image-volume-recreate/compose.yaml",
|
||||
"--project-name", projectName, "build", "--build-arg", "CONTENT=bar")
|
||||
|
||||
// Run up again - consumer should be recreated because source image changed
|
||||
res = c.RunDockerComposeCmd(t, "-f", "./fixtures/image-volume-recreate/compose.yaml",
|
||||
"--project-name", projectName, "up", "-d")
|
||||
// The consumer container should be recreated
|
||||
assert.Check(t, strings.Contains(res.Combined(), "Recreate") || strings.Contains(res.Combined(), "Created"),
|
||||
"Expected container to be recreated, got: %s", res.Combined())
|
||||
|
||||
// Check updated content
|
||||
res = c.RunDockerComposeCmd(t, "-f", "./fixtures/image-volume-recreate/compose.yaml",
|
||||
"--project-name", projectName, "logs", "consumer")
|
||||
assert.Check(t, strings.Contains(res.Combined(), "bar"), "Expected 'bar' in output after rebuild, got: %s", res.Combined())
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -17,7 +17,7 @@ import (
|
||||
docker "github.com/docker/cli/cli/context/docker"
|
||||
store "github.com/docker/cli/cli/context/store"
|
||||
streams "github.com/docker/cli/cli/streams"
|
||||
client "github.com/docker/docker/client"
|
||||
client "github.com/moby/moby/client"
|
||||
metric "go.opentelemetry.io/otel/metric"
|
||||
resource "go.opentelemetry.io/otel/sdk/resource"
|
||||
trace "go.opentelemetry.io/otel/trace"
|
||||
@@ -47,24 +47,6 @@ func (m *MockCli) EXPECT() *MockCliMockRecorder {
|
||||
return m.recorder
|
||||
}
|
||||
|
||||
// Apply mocks base method.
|
||||
func (m *MockCli) Apply(arg0 ...command.CLIOption) error {
|
||||
m.ctrl.T.Helper()
|
||||
varargs := []any{}
|
||||
for _, a := range arg0 {
|
||||
varargs = append(varargs, a)
|
||||
}
|
||||
ret := m.ctrl.Call(m, "Apply", varargs...)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// Apply indicates an expected call of Apply.
|
||||
func (mr *MockCliMockRecorder) Apply(arg0 ...any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Apply", reflect.TypeOf((*MockCli)(nil).Apply), arg0...)
|
||||
}
|
||||
|
||||
// BuildKitEnabled mocks base method.
|
||||
func (m *MockCli) BuildKitEnabled() (bool, error) {
|
||||
m.ctrl.T.Helper()
|
||||
|
||||
@@ -31,7 +31,7 @@ func Test_BatchDebounceEvents(t *testing.T) {
|
||||
t.Cleanup(stop)
|
||||
|
||||
eventBatchCh := BatchDebounceEvents(ctx, clock, ch)
|
||||
for i := 0; i < 100; i++ {
|
||||
for i := range 100 {
|
||||
path := "/a"
|
||||
if i%2 == 0 {
|
||||
path = "/b"
|
||||
|
||||
@@ -118,7 +118,7 @@ func TestGitBranchSwitch(t *testing.T) {
|
||||
done := f.consumeEventsInBackground(ctx)
|
||||
|
||||
for i, dir := range dirs {
|
||||
for j := 0; j < count; j++ {
|
||||
for j := range count {
|
||||
base := fmt.Sprintf("x/y/dir-%d/x.txt", j)
|
||||
p := filepath.Join(dir, base)
|
||||
f.WriteFile(p, "contents")
|
||||
|
||||
@@ -22,6 +22,7 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/fsnotify/fsevents"
|
||||
@@ -38,6 +39,7 @@ type fseventNotify struct {
|
||||
stop chan struct{}
|
||||
|
||||
pathsWereWatching map[string]any
|
||||
closeOnce sync.Once
|
||||
}
|
||||
|
||||
func (d *fseventNotify) loop() {
|
||||
@@ -81,6 +83,8 @@ func (d *fseventNotify) Start() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
d.closeOnce = sync.Once{}
|
||||
|
||||
numberOfWatches.Add(int64(len(d.stream.Paths)))
|
||||
|
||||
err := d.stream.Start()
|
||||
@@ -92,11 +96,13 @@ func (d *fseventNotify) Start() error {
|
||||
}
|
||||
|
||||
func (d *fseventNotify) Close() error {
|
||||
numberOfWatches.Add(int64(-len(d.stream.Paths)))
|
||||
d.closeOnce.Do(func() {
|
||||
numberOfWatches.Add(int64(-len(d.stream.Paths)))
|
||||
|
||||
d.stream.Stop()
|
||||
close(d.errors)
|
||||
close(d.stop)
|
||||
d.stream.Stop()
|
||||
close(d.errors)
|
||||
close(d.stop)
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
48
pkg/watch/watcher_darwin_test.go
Normal file
48
pkg/watch/watcher_darwin_test.go
Normal file
@@ -0,0 +1,48 @@
|
||||
//go:build fsnotify
|
||||
|
||||
/*
|
||||
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 watch
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"gotest.tools/v3/assert"
|
||||
)
|
||||
|
||||
func TestFseventNotifyCloseIdempotent(t *testing.T) {
|
||||
// Create a watcher with a temporary directory
|
||||
tmpDir := t.TempDir()
|
||||
watcher, err := newWatcher([]string{tmpDir})
|
||||
assert.NilError(t, err)
|
||||
|
||||
// Start the watcher
|
||||
err = watcher.Start()
|
||||
assert.NilError(t, err)
|
||||
|
||||
// Close should work the first time
|
||||
err = watcher.Close()
|
||||
assert.NilError(t, err)
|
||||
|
||||
// Close should be idempotent - calling it again should not panic
|
||||
err = watcher.Close()
|
||||
assert.NilError(t, err)
|
||||
|
||||
// Even a third time should be safe
|
||||
err = watcher.Close()
|
||||
assert.NilError(t, err)
|
||||
}
|
||||
@@ -56,7 +56,7 @@ func TestDontWatchEachFile(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for i := 0; i < 100; i++ {
|
||||
for i := range 100 {
|
||||
f.WriteFile(f.JoinPath(initialDir, fmt.Sprintf("%d", i)), "initial data")
|
||||
}
|
||||
|
||||
@@ -79,7 +79,7 @@ func TestDontWatchEachFile(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for i := 0; i < 100; i++ {
|
||||
for i := range 100 {
|
||||
f.WriteFile(f.JoinPath(inplaceDir, fmt.Sprintf("%d", i)), "inplace data")
|
||||
}
|
||||
|
||||
@@ -98,7 +98,7 @@ func TestDontWatchEachFile(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for i := 0; i < 100; i++ {
|
||||
for i := range 100 {
|
||||
f.WriteFile(f.JoinPath(stagedDir, fmt.Sprintf("%d", i)), "staged data")
|
||||
}
|
||||
|
||||
@@ -146,7 +146,7 @@ func TestDontRecurseWhenWatchingParentsOfNonExistentFiles(t *testing.T) {
|
||||
f.watch(filepath.Join(watched, ".tiltignore"))
|
||||
|
||||
excludedDir := f.JoinPath(watched, "excluded")
|
||||
for i := 0; i < 10; i++ {
|
||||
for i := range 10 {
|
||||
f.WriteFile(f.JoinPath(excludedDir, fmt.Sprintf("%d", i), "data.txt"), "initial data")
|
||||
}
|
||||
f.fsync()
|
||||
|
||||
Reference in New Issue
Block a user