Compare commits

...

55 Commits

Author SHA1 Message Date
Guillaume Lours
e45e58b3ec bumpd buildkit v0.22.0 and buildx v0.24.0
Signed-off-by: Guillaume Lours <705411+glours@users.noreply.github.com>
2025-05-23 15:03:36 +02:00
Guillaume Lours
f52af4c868 bump compose-go to v2.6.4
Signed-off-by: Guillaume Lours <705411+glours@users.noreply.github.com>
2025-05-23 14:53:24 +02:00
Nicolas De Loof
a54814ff39 e2e test
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2025-05-22 14:58:53 +02:00
Nicolas De Loof
a2d7548ca9 fix up --build with additional_context dependency
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2025-05-22 14:26:34 +02:00
Nicolas De Loof
8a2cb90a39 example provider implementation
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2025-05-21 15:59:30 +02:00
Nicolas De Loof
cc50ada725 report error (re)creating container
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2025-05-21 15:40:10 +02:00
dependabot[bot]
5c74f07991 build(deps): bump github.com/containerd/containerd/v2
Bumps [github.com/containerd/containerd/v2](https://github.com/containerd/containerd) from 2.1.0 to 2.1.1.
- [Release notes](https://github.com/containerd/containerd/releases)
- [Changelog](https://github.com/containerd/containerd/blob/main/RELEASES.md)
- [Commits](https://github.com/containerd/containerd/compare/v2.1.0...v2.1.1)

---
updated-dependencies:
- dependency-name: github.com/containerd/containerd/v2
  dependency-version: 2.1.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-21 12:03:59 +02:00
Guillaume Lours
7e198ee6a3 remove provenance build flag for now
Signed-off-by: Guillaume Lours <705411+glours@users.noreply.github.com>
2025-05-20 18:21:05 +02:00
Guillaume Lours
0566431c64 only use attestation when building image outside the development inner loop
when building a image, by default attestation are generated and modify the image ID which trigger a container recreation on up, run command even if there isn't any changes on the image content itself

Signed-off-by: Guillaume Lours <705411+glours@users.noreply.github.com>
2025-05-20 18:21:05 +02:00
Nicolas De Loof
4f6cc2a330 run ContainerStart sequentially
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2025-05-20 09:47:58 +02:00
Nicolas De Loof
2352a4a016 introduce config --lock-image-digests
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2025-05-19 13:53:01 +02:00
Guillaume Lours
1f076a3781 bump compose-go to v2.6.3
Signed-off-by: Guillaume Lours <705411+glours@users.noreply.github.com>
2025-05-19 12:35:17 +02:00
Guillaume Lours
009a239510 remove convert alias from config command
Signed-off-by: Guillaume Lours <705411+glours@users.noreply.github.com>
2025-05-19 12:30:09 +02:00
dependabot[bot]
3059574288 build(deps): bump google.golang.org/grpc from 1.72.0 to 1.72.1
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.72.0 to 1.72.1.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.72.0...v1.72.1)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-version: 1.72.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-19 12:20:37 +02:00
dependabot[bot]
1229a69384 build(deps): bump github.com/containerd/containerd/v2
Bumps [github.com/containerd/containerd/v2](https://github.com/containerd/containerd) from 2.0.5 to 2.1.0.
- [Release notes](https://github.com/containerd/containerd/releases)
- [Changelog](https://github.com/containerd/containerd/blob/main/RELEASES.md)
- [Commits](https://github.com/containerd/containerd/compare/v2.0.5...v2.1.0)

---
updated-dependencies:
- dependency-name: github.com/containerd/containerd/v2
  dependency-version: 2.1.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-19 12:07:46 +02:00
Nicolas De Loof
f2a88e02a0 ensure build dependencies are enabled
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2025-05-19 11:51:01 +02:00
Nicolas De Loof
7f9101845d report cancelled pull after another one failed
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2025-05-19 11:41:05 +02:00
Guillaume Lours
944e5e67a1 do not throw an error on build with provider services
Signed-off-by: Guillaume Lours <705411+glours@users.noreply.github.com>
2025-05-16 17:47:09 +02:00
Anvar Umuraliev
23fc76a540 Fix quiet option when using COMPOSE_BAKE=1
Signed-off-by: Anvar Umuraliev <an.umuraliev@gmail.com>
2025-05-15 15:42:25 +02:00
Nicolas De Loof
053d225824 append .exe to provider name doing executable lookup on windows
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2025-05-14 17:19:35 +02:00
Nicolas De Loof
93b597ccec remove Docker EULA licensing which isn't relevant since Docker switched to a subscription model
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2025-05-14 15:02:13 +02:00
Guillaume Lours
4dcaf94c32 add support of 'debug' messages in the communication between Compose and provider binaries
Signed-off-by: Guillaume Lours <705411+glours@users.noreply.github.com>
2025-05-13 18:32:03 +02:00
Nicolas De Loof
07e7619f4e set provider environment
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2025-05-13 11:31:38 +02:00
Nicolas De Loof
ed81185c5c fix provider info message
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2025-05-12 14:15:28 +02:00
Nicolas De Loof
22f8a7009f provider.options can be an array
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2025-05-12 11:49:49 +02:00
Guillaume Lours
91a0aa0265 skip push step for provider services
Signed-off-by: Guillaume Lours <705411+glours@users.noreply.github.com>
2025-05-12 10:43:25 +02:00
Nicolas De Loof
7cea455c4d simplification
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2025-05-07 15:53:05 +02:00
dependabot[bot]
559a51e590 build(deps): bump golang.org/x/sys from 0.32.0 to 0.33.0
Bumps [golang.org/x/sys](https://github.com/golang/sys) from 0.32.0 to 0.33.0.
- [Commits](https://github.com/golang/sys/compare/v0.32.0...v0.33.0)

---
updated-dependencies:
- dependency-name: golang.org/x/sys
  dependency-version: 0.33.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-07 12:31:41 +02:00
dependabot[bot]
480a556bf0 build(deps): bump golang.org/x/sync from 0.13.0 to 0.14.0
Bumps [golang.org/x/sync](https://github.com/golang/sync) from 0.13.0 to 0.14.0.
- [Commits](https://github.com/golang/sync/compare/v0.13.0...v0.14.0)

---
updated-dependencies:
- dependency-name: golang.org/x/sync
  dependency-version: 0.14.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-07 12:19:47 +02:00
dependabot[bot]
6263361190 build(deps): bump github.com/moby/buildkit from 0.21.0 to 0.21.1
Bumps [github.com/moby/buildkit](https://github.com/moby/buildkit) from 0.21.0 to 0.21.1.
- [Release notes](https://github.com/moby/buildkit/releases)
- [Commits](https://github.com/moby/buildkit/compare/v0.21.0...v0.21.1)

---
updated-dependencies:
- dependency-name: github.com/moby/buildkit
  dependency-version: 0.21.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-07 12:07:27 +02:00
Guillaume Lours
9ee03c3fec bump compose-go to v2.6.2
Signed-off-by: Guillaume Lours <705411+glours@users.noreply.github.com>
2025-05-07 11:43:56 +02:00
Alessio Perugini
4bf18d2325 docs: regenerate
Signed-off-by: Alessio Perugini <alessio@perugini.xyz>
2025-05-06 22:33:52 +02:00
Alessio Perugini
f0f47a8aa8 e2e: add tests
Signed-off-by: Alessio Perugini <alessio@perugini.xyz>
2025-05-06 22:33:52 +02:00
Alessio Perugini
d6e3fa6d74 Fix config --variables not honoring the --format flag
When providing the --variables with --format flag, the current
implementation always printed in human readable form.
This patch correctly add the missing format in the formatter.Print
function, making the commands behave as an user would expect.

Example:
`config --variables --format json`

Signed-off-by: Alessio Perugini <alessio@perugini.xyz>
2025-05-06 22:33:52 +02:00
Nicolas De Loof
16e83f002d introduce build --check
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2025-05-06 07:54:30 +02:00
Nicolas De Loof
2dbef234dc document behavior on missing extension
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2025-05-05 14:33:35 +02:00
Nicolas De Loof
20f0ffec0b seach for provider binary in PATH
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2025-05-05 11:21:21 +02:00
Nicolas De Loof
cee6a3c660 document extensibility using service.provider
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2025-05-05 11:21:21 +02:00
Nicolas De Loof
fc8c56b407 select services implicitly declared by a service:xx build dependency
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2025-05-05 10:20:19 +02:00
Nicolas De Loof
9c998a934f fix collect image digests for service images built by bake
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2025-05-05 10:14:35 +02:00
Nicolas De Loof
0403f0d76d e2e test for start_interval
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2025-04-30 11:08:09 +02:00
dependabot[bot]
91d04a5ca9 build(deps): bump go.uber.org/mock from 0.5.1 to 0.5.2
Bumps [go.uber.org/mock](https://github.com/uber/mock) from 0.5.1 to 0.5.2.
- [Release notes](https://github.com/uber/mock/releases)
- [Changelog](https://github.com/uber-go/mock/blob/main/CHANGELOG.md)
- [Commits](https://github.com/uber/mock/compare/v0.5.1...v0.5.2)

---
updated-dependencies:
- dependency-name: go.uber.org/mock
  dependency-version: 0.5.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-29 11:56:08 +02:00
Guillaume Lours
d2274ebe6c display proper event message for provider services on up and down
Signed-off-by: Guillaume Lours <705411+glours@users.noreply.github.com>
2025-04-28 14:48:26 +02:00
Nicolas De Loof
6e35652182 fix support for remote absolute path
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2025-04-28 11:05:06 +02:00
Anvar Umuraliev
5bb46035cf Set --progress flag default value from env if provided
Signed-off-by: Anvar Umuraliev <an.umuraliev@gmail.com>
2025-04-25 11:56:07 +02:00
Anvar Umuraliev
f8dae06df8 Add support for COMPOSE_PROGRESS env variable
COMPOSE_PROGRESS variable supports 5 values:
- "auto" - detect console capabilities
- "tty" - use terminal capability for advanced rendering
- "plain" - dump raw events to output
- "quiet" - don't display events
- "json" - outputs a machine-readable JSON stream

Signed-off-by: Anvar Umuraliev <an.umuraliev@gmail.com>
2025-04-25 11:56:07 +02:00
Nicolas De Loof
955e4ed94e introduce networks.interface_name
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
2025-04-24 12:06:17 +02:00
Guillaume Lours
60385e6065 bump compose-go to v2.6.1
fixing parsing of npipe as volume type

Signed-off-by: Guillaume Lours <705411+glours@users.noreply.github.com>
2025-04-22 17:17:34 +02:00
Guillaume Lours
f5491328bb remove support of Synchronize File Shares integration with Docker Desktop
Signed-off-by: Guillaume Lours <705411+glours@users.noreply.github.com>
2025-04-22 15:29:23 +02:00
dependabot[bot]
f46689a75e build(deps): bump github.com/containerd/containerd/v2
Bumps [github.com/containerd/containerd/v2](https://github.com/containerd/containerd) from 2.0.4 to 2.0.5.
- [Release notes](https://github.com/containerd/containerd/releases)
- [Changelog](https://github.com/containerd/containerd/blob/main/RELEASES.md)
- [Commits](https://github.com/containerd/containerd/compare/v2.0.4...v2.0.5)

---
updated-dependencies:
- dependency-name: github.com/containerd/containerd/v2
  dependency-version: 2.0.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-22 15:16:18 +02:00
dependabot[bot]
8fd0c297f5 build(deps): bump google.golang.org/grpc from 1.71.1 to 1.72.0
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.71.1 to 1.72.0.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.71.1...v1.72.0)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-version: 1.72.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-22 15:03:54 +02:00
dependabot[bot]
f3bbfdae58 build(deps): bump github.com/docker/docker
Bumps [github.com/docker/docker](https://github.com/docker/docker) from 28.1.0+incompatible to 28.1.1+incompatible.
- [Release notes](https://github.com/docker/docker/releases)
- [Commits](https://github.com/docker/docker/compare/v28.1.0...v28.1.1)

---
updated-dependencies:
- dependency-name: github.com/docker/docker
  dependency-version: 28.1.1+incompatible
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-22 14:52:31 +02:00
dependabot[bot]
322c531a8c build(deps): bump github.com/docker/cli
Bumps [github.com/docker/cli](https://github.com/docker/cli) from 28.1.0+incompatible to 28.1.1+incompatible.
- [Commits](https://github.com/docker/cli/compare/v28.1.0...v28.1.1)

---
updated-dependencies:
- dependency-name: github.com/docker/cli
  dependency-version: 28.1.1+incompatible
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-22 14:40:27 +02:00
skanehira
bf6b447263 fix: concurrent map writes when pulling
Signed-off-by: skanehira <sho19921005@gmail.com>
2025-04-22 10:46:05 +02:00
Simon Ser
a96c305b25 build: write --print output to stdout
stdinfo should only be used for status/progress messages: it
defaults to stderr and makes piping the output trickier. bakex
always writes `docker buildx bake --print` always uses stdout.

Signed-off-by: Simon Ser <contact@emersion.fr>
2025-04-22 10:13:07 +02:00
40 changed files with 941 additions and 1049 deletions

View File

@@ -306,9 +306,6 @@ jobs:
echo "$sum $file" > ${file#\*}.sha256
fi
done
-
name: License
run: cp packaging/* ./bin/release/
-
name: List artifacts
run: |

View File

@@ -27,7 +27,6 @@ import (
"github.com/docker/cli/cli/command"
cliopts "github.com/docker/cli/opts"
ui "github.com/docker/compose/v2/pkg/progress"
"github.com/docker/compose/v2/pkg/utils"
buildkit "github.com/moby/buildkit/util/progress/progressui"
"github.com/spf13/cobra"
@@ -36,16 +35,18 @@ import (
type buildOptions struct {
*ProjectOptions
quiet bool
pull bool
push bool
args []string
noCache bool
memory cliopts.MemBytes
ssh string
builder string
deps bool
print bool
quiet bool
pull bool
push bool
args []string
noCache bool
memory cliopts.MemBytes
ssh string
builder string
deps bool
print bool
check bool
provenance bool
}
func (opts buildOptions) toAPIBuildOptions(services []string) (api.BuildOptions, error) {
@@ -69,19 +70,22 @@ func (opts buildOptions) toAPIBuildOptions(services []string) (api.BuildOptions,
if uiMode == ui.ModeJSON {
uiMode = "rawjson"
}
return api.BuildOptions{
Pull: opts.pull,
Push: opts.push,
Progress: uiMode,
Args: types.NewMappingWithEquals(opts.args),
NoCache: opts.noCache,
Quiet: opts.quiet,
Services: services,
Deps: opts.deps,
Memory: int64(opts.memory),
Print: opts.print,
SSHs: SSHKeys,
Builder: builderName,
Pull: opts.pull,
Push: opts.push,
Progress: uiMode,
Args: types.NewMappingWithEquals(opts.args),
NoCache: opts.noCache,
Quiet: opts.quiet,
Services: services,
Deps: opts.deps,
Memory: int64(opts.memory),
Print: opts.print,
Check: opts.check,
SSHs: SSHKeys,
Builder: builderName,
Provenance: opts.provenance,
}, nil
}
@@ -136,44 +140,26 @@ func buildCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service)
flags.StringVar(&p.Progress, "progress", string(buildkit.AutoMode), fmt.Sprintf(`Set type of ui output (%s)`, strings.Join(printerModes, ", ")))
flags.MarkHidden("progress") //nolint:errcheck
flags.BoolVar(&opts.print, "print", false, "Print equivalent bake file")
flags.BoolVar(&opts.check, "check", false, "Check build configuration")
return cmd
}
func runBuild(ctx context.Context, dockerCli command.Cli, backend api.Service, opts buildOptions, services []string) error {
project, _, err := opts.ToProject(ctx, dockerCli, services, cli.WithResolvedPaths(true), cli.WithoutEnvironmentResolution)
project, _, err := opts.ToProject(ctx, dockerCli, nil, cli.WithResolvedPaths(true), cli.WithoutEnvironmentResolution)
if err != nil {
return err
}
services = addBuildDependencies(services, project)
if err := applyPlatforms(project, false); err != nil {
return err
}
apiBuildOptions, err := opts.toAPIBuildOptions(services)
apiBuildOptions.Provenance = true
if err != nil {
return err
}
return backend.Build(ctx, project, apiBuildOptions)
}
func addBuildDependencies(services []string, project *types.Project) []string {
servicesWithDependencies := utils.NewSet(services...)
for _, service := range services {
build := project.Services[service].Build
if build != nil {
for _, target := range build.AdditionalContexts {
if s, found := strings.CutPrefix(target, types.ServicePrefix); found {
servicesWithDependencies.Add(s)
}
}
}
}
if len(servicesWithDependencies) > len(services) {
return addBuildDependencies(servicesWithDependencies.Elements(), project)
}
return servicesWithDependencies.Elements()
}

View File

@@ -69,6 +69,8 @@ const (
ComposeEnvFiles = "COMPOSE_ENV_FILES"
// ComposeMenu defines if the navigation menu should be rendered. Can be also set via --menu
ComposeMenu = "COMPOSE_MENU"
// ComposeProgress defines type of progress output, if --progress isn't used
ComposeProgress = "COMPOSE_PROGRESS"
)
// rawEnv load a dot env file using docker/cli key=value parser, without attempt to interpolate or evaluate values
@@ -228,7 +230,7 @@ func (o *ProjectOptions) addProjectFlags(f *pflag.FlagSet) {
f.StringVar(&o.ProjectDir, "project-directory", "", "Specify an alternate working directory\n(default: the path of the, first specified, Compose file)")
f.StringVar(&o.WorkDir, "workdir", "", "DEPRECATED! USE --project-directory INSTEAD.\nSpecify an alternate working directory\n(default: the path of the, first specified, Compose file)")
f.BoolVar(&o.Compatibility, "compatibility", false, "Run compose in backward compatibility mode")
f.StringVar(&o.Progress, "progress", string(buildkit.AutoMode), fmt.Sprintf(`Set type of progress output (%s)`, strings.Join(printerModes, ", ")))
f.StringVar(&o.Progress, "progress", defaultStringVar(ComposeProgress, string(buildkit.AutoMode)), fmt.Sprintf(`Set type of progress output (%s)`, strings.Join(printerModes, ", ")))
f.BoolVar(&o.All, "all-resources", false, "Include all resources, even those not used by services")
_ = f.MarkHidden("workdir")
}
@@ -240,6 +242,14 @@ func defaultStringArrayVar(env string) []string {
})
}
// get default value for a command line flag from the env variable, if the env variable is not set, it returns the provided default value 'def'
func defaultStringVar(env, def string) string {
if v, ok := os.LookupEnv(env); ok {
return v
}
return def
}
func (o *ProjectOptions) projectOrName(ctx context.Context, dockerCli command.Cli, services ...string) (*types.Project, string, error) {
name := o.ProjectName
var project *types.Project

View File

@@ -56,6 +56,7 @@ type configOptions struct {
noConsistency bool
variables bool
environment bool
lockImageDigests bool
}
func (o *configOptions) ToProject(ctx context.Context, dockerCli command.Cli, services []string, po ...cli.ProjectOptionsFn) (*types.Project, error) {
@@ -85,9 +86,8 @@ func configCommand(p *ProjectOptions, dockerCli command.Cli) *cobra.Command {
ProjectOptions: p,
}
cmd := &cobra.Command{
Aliases: []string{"convert"}, // for backward compatibility with Cloud integrations
Use: "config [OPTIONS] [SERVICE...]",
Short: "Parse, resolve and render compose file in canonical format",
Use: "config [OPTIONS] [SERVICE...]",
Short: "Parse, resolve and render compose file in canonical format",
PreRunE: Adapt(func(ctx context.Context, args []string) error {
if opts.quiet {
devnull, err := os.Open(os.DevNull)
@@ -99,6 +99,9 @@ func configCommand(p *ProjectOptions, dockerCli command.Cli) *cobra.Command {
if p.Compatibility {
opts.noNormalize = true
}
if opts.lockImageDigests {
opts.resolveImageDigests = true
}
return nil
}),
RunE: Adapt(func(ctx context.Context, args []string) error {
@@ -124,13 +127,17 @@ func configCommand(p *ProjectOptions, dockerCli command.Cli) *cobra.Command {
return runEnvironment(ctx, dockerCli, opts, args)
}
if opts.Format == "" {
opts.Format = "yaml"
}
return runConfig(ctx, dockerCli, opts, args)
}),
ValidArgsFunction: completeServiceNames(dockerCli, p),
}
flags := cmd.Flags()
flags.StringVar(&opts.Format, "format", "yaml", "Format the output. Values: [yaml | json]")
flags.StringVar(&opts.Format, "format", "", "Format the output. Values: [yaml | json]")
flags.BoolVar(&opts.resolveImageDigests, "resolve-image-digests", false, "Pin image tags to digests")
flags.BoolVar(&opts.lockImageDigests, "lock-image-digests", false, "Produces an override file with image digests")
flags.BoolVarP(&opts.quiet, "quiet", "q", false, "Only validate the configuration, don't print anything")
flags.BoolVar(&opts.noInterpolate, "no-interpolate", false, "Don't interpolate environment variables")
flags.BoolVar(&opts.noNormalize, "no-normalize", false, "Don't normalize compose model")
@@ -206,6 +213,10 @@ func runConfigInterpolate(ctx context.Context, dockerCli command.Cli, opts confi
}
}
if opts.lockImageDigests {
project = imagesOnly(project)
}
var content []byte
switch opts.Format {
case "json":
@@ -221,6 +232,18 @@ func runConfigInterpolate(ctx context.Context, dockerCli command.Cli, opts confi
return content, nil
}
// imagesOnly return project with all attributes removed but service.images
func imagesOnly(project *types.Project) *types.Project {
digests := types.Services{}
for name, config := range project.Services {
digests[name] = types.ServiceConfig{
Image: config.Image,
}
}
project = &types.Project{Services: digests}
return project
}
func runConfigNoInterpolate(ctx context.Context, dockerCli command.Cli, opts configOptions, services []string) ([]byte, error) {
// we can't use ToProject, so the model we render here is only partially resolved
model, err := opts.ToModel(ctx, dockerCli, services)
@@ -235,6 +258,23 @@ func runConfigNoInterpolate(ctx context.Context, dockerCli command.Cli, opts con
}
}
if opts.lockImageDigests {
for key, e := range model {
if key != "services" {
delete(model, key)
} else {
for _, s := range e.(map[string]any) {
service := s.(map[string]any)
for key := range service {
if key != "image" {
delete(service, key)
}
}
}
}
}
}
return formatModel(model, opts.Format)
}
@@ -408,7 +448,16 @@ func runVariables(ctx context.Context, dockerCli command.Cli, opts configOptions
variables := template.ExtractVariables(model, template.DefaultPattern)
return formatter.Print(variables, "", dockerCli.Out(), func(w io.Writer) {
if opts.Format == "yaml" {
result, err := yaml.Marshal(variables)
if err != nil {
return err
}
fmt.Print(string(result))
return nil
}
return formatter.Print(variables, opts.Format, dockerCli.Out(), func(w io.Writer) {
for name, variable := range variables {
_, _ = fmt.Fprintf(w, "%s\t%t\t%s\t%s\n", name, variable.Required, variable.DefaultValue, variable.PresenceValue)
}

View File

@@ -260,6 +260,7 @@ func runUp(
if err != nil {
return err
}
bo.Services = services
build = &bo
}

74
docs/examples/provider.go Normal file
View File

@@ -0,0 +1,74 @@
/*
Copyright 2020 Docker Compose CLI authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package main
import (
"fmt"
"os"
"time"
"github.com/spf13/cobra"
)
func main() {
cmd := &cobra.Command{
Short: "Compose Provider Example",
Use: "demo",
}
cmd.AddCommand(composeCommand())
err := cmd.Execute()
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}
func composeCommand() *cobra.Command {
c := &cobra.Command{
Use: "compose EVENT",
TraverseChildren: true,
}
c.PersistentFlags().String("project-name", "", "compose project name") // unused
c.AddCommand(&cobra.Command{
Use: "up",
Run: up,
Args: cobra.ExactArgs(1),
})
c.AddCommand(&cobra.Command{
Use: "down",
Run: down,
Args: cobra.ExactArgs(1),
})
return c
}
const lineSeparator = "\n"
func up(_ *cobra.Command, args []string) {
servicename := args[0]
fmt.Printf(`{ "type": "debug", "message": "Starting %s" }%s`, servicename, lineSeparator)
for i := 0; i < 100; i += 10 {
time.Sleep(1 * time.Second)
fmt.Printf(`{ "type": "info", "message": "Processing ... %d%%" }%s`, i, lineSeparator)
}
fmt.Printf(`{ "type": "setenv", "message": "URL=https://magic.cloud/%s" }%s`, servicename, lineSeparator)
}
func down(_ *cobra.Command, _ []string) {
fmt.Printf(`{ "type": "error", "message": "Permission error" }%s`, lineSeparator)
}

111
docs/extension.md Normal file
View File

@@ -0,0 +1,111 @@
# About
The Compose application model defines `service` as an abstraction for a computing unit managing (a subset of)
application needs, which can interact with other service by relying on network(s). Docker Compose is designed
to use the Docker Engine ("Moby") API to manage services as containers, but the abstraction _could_ also cover
many other runtimes, typically cloud services or services natively provided by host.
The Compose extensibility model has been designed to extend the `service` support to runtimes accessible through
third-party tooling.
# Architecture
Compose extensibility relies on the `provider` attribute to select the actual binary responsible for managing
the resource(s) needed to run a service.
```yaml
database:
provider:
type: awesomecloud
options:
type: mysql
size: 256
```
`provider.type` tells Compose the binary to run, which can be either:
- Another Docker CLI plugin (typically, `model` to run `docker-model`)
- An executable in user's `PATH`
If `provider.type` doesn't resolve into any of those, Compose will report an error and interrupt the `up` command.
To be a valid Compose extension, provider command *MUST* accept a `compose` command (which can be hidden)
with subcommands `up` and `down`.
## Up lifecycle
To execute an application's `up` lifecycle, Compose executes the provider's `compose up` command, passing
the project name, service name, and additional options. The `provider.options` are translated
into command line flags. For example:
```console
awesomecloud compose --project-name <NAME> up --type=mysql --size=256 "database"
```
> __Note:__ `project-name` _should_ be used by the provider to tag resources
> set for project, so that later execution with `down` subcommand releases
> all allocated resources set for the project.
## Communication with Compose
Providers can interact with Compose using `stdout` as a channel, sending JSON line delimited messages.
JSON messages MUST include a `type` and a `message` attribute.
```json
{ "type": "info", "message": "preparing mysql ..." }
```
`type` can be either:
- `info`: Reports status updates to the user. Compose will render message as the service state in the progress UI
- `error`: Let's the user know something went wrong with details about the error. Compose will render the message as the reason for the service failure.
- `setenv`: Let's the plugin tell Compose how dependent services can access the created resource. See next section for further details.
- `debug`: Those messages could help debugging the provider, but are not rendered to the user by default. They are rendered when Compose is started with `--verbose` flag.
```mermaid
sequenceDiagram
Shell->>Compose: docker compose up
Compose->>Provider: compose up --project-name=xx --foo=bar "database"
Provider--)Compose: json { "info": "pulling 25%" }
Compose-)Shell: pulling 25%
Provider--)Compose: json { "info": "pulling 50%" }
Compose-)Shell: pulling 50%
Provider--)Compose: json { "info": "pulling 75%" }
Compose-)Shell: pulling 75%
Provider--)Compose: json { "setenv": "URL=http://cloud.com/abcd:1234" }
Compose-)Compose: set DATABASE_URL
Provider-)Compose: EOF (command complete) exit 0
Compose-)Shell: service started
```
## Connection to a service managed by a provider
A service in the Compose application can declare dependency on a service managed by an external provider:
```yaml
services:
app:
image: myapp
depends_on:
- database
database:
provider:
type: awesomecloud
```
When the provider command sends a `setenv` JSON message, Compose injects the specified variable into any dependent service,
automatically prefixing it with the service name. For example, if `awesomecloud compose up` returns:
```json
{"type": "setenv", "message": "URL=https://awesomecloud.com/db:1234"}
```
Then the `app` service, which depends on the service managed by the provider, will receive a `DATABASE_URL` environment variable injected
into its runtime environment.
> __Note:__ The `compose up` provider command _MUST_ be idempotent. If resource is already running, the command _MUST_ set
> the same environment variables to ensure consistent configuration of dependent services.
## Down lifecycle
`down` lifecycle is equivalent to `up` with the `<provider> compose --project-name <NAME> down <SERVICE>` command.
The provider is responsible for releasing all resources associated with the service.
## Examples
See [example](examples/provider.go) for illustration on implementing this API in a command line

View File

@@ -17,6 +17,7 @@ run `docker compose build` to rebuild it.
|:----------------------|:--------------|:--------|:------------------------------------------------------------------------------------------------------------|
| `--build-arg` | `stringArray` | | Set build-time variables for services |
| `--builder` | `string` | | Set builder to use |
| `--check` | `bool` | | Check build configuration |
| `--dry-run` | `bool` | | Execute command in dry run mode |
| `-m`, `--memory` | `bytes` | `0` | Set memory limit for the build container. Not supported by BuildKit. |
| `--no-cache` | `bool` | | Do not use cache when building the image |

View File

@@ -5,19 +5,16 @@
It merges the Compose files set by `-f` flags, resolves variables in the Compose file, and expands short-notation into
the canonical format.
### Aliases
`docker compose config`, `docker compose convert`
### Options
| Name | Type | Default | Description |
|:--------------------------|:---------|:--------|:----------------------------------------------------------------------------|
| `--dry-run` | `bool` | | Execute command in dry run mode |
| `--environment` | `bool` | | Print environment used for interpolation. |
| `--format` | `string` | `yaml` | Format the output. Values: [yaml \| json] |
| `--format` | `string` | | Format the output. Values: [yaml \| json] |
| `--hash` | `string` | | Print the service config hash, one per line. |
| `--images` | `bool` | | Print the image names, one per line. |
| `--lock-image-digests` | `bool` | | Produces an override file with image digests |
| `--no-consistency` | `bool` | | Don't check model consistency - warning: may produce invalid Compose output |
| `--no-env-resolution` | `bool` | | Don't resolve service env files |
| `--no-interpolate` | `bool` | | Don't interpolate environment variables |

View File

@@ -33,6 +33,16 @@ options:
experimentalcli: false
kubernetes: false
swarm: false
- option: check
value_type: bool
default_value: "false"
description: Check build configuration
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
swarm: false
- option: compress
value_type: bool
default_value: "true"

View File

@@ -1,5 +1,4 @@
command: docker compose config
aliases: docker compose config, docker compose convert
short: Parse, resolve and render compose file in canonical format
long: |-
`docker compose config` renders the actual data model to be applied on the Docker Engine.
@@ -21,7 +20,6 @@ options:
swarm: false
- option: format
value_type: string
default_value: yaml
description: 'Format the output. Values: [yaml | json]'
deprecated: false
hidden: false
@@ -48,6 +46,16 @@ options:
experimentalcli: false
kubernetes: false
swarm: false
- option: lock-image-digests
value_type: bool
default_value: "false"
description: Produces an override file with image digests
deprecated: false
hidden: false
experimental: false
experimentalcli: false
kubernetes: false
swarm: false
- option: no-consistency
value_type: bool
default_value: "false"

90
go.mod
View File

@@ -8,15 +8,15 @@ require (
github.com/Microsoft/go-winio v0.6.2
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d
github.com/buger/goterm v1.0.4
github.com/compose-spec/compose-go/v2 v2.6.0
github.com/containerd/containerd/v2 v2.0.4
github.com/compose-spec/compose-go/v2 v2.6.4
github.com/containerd/containerd/v2 v2.1.1
github.com/containerd/platforms v1.0.0-rc.1
github.com/davecgh/go-spew v1.1.1
github.com/distribution/reference v0.6.0
github.com/docker/buildx v0.23.0
github.com/docker/cli v28.1.0+incompatible
github.com/docker/buildx v0.24.0
github.com/docker/cli v28.1.1+incompatible
github.com/docker/cli-docs-tool v0.9.0
github.com/docker/docker v28.1.0+incompatible
github.com/docker/docker v28.1.1+incompatible
github.com/docker/go-connections v0.5.0
github.com/docker/go-units v0.5.0
github.com/eiannone/keyboard v0.0.0-20220611211555-0d226195f203
@@ -28,7 +28,7 @@ require (
github.com/mattn/go-shellwords v1.0.12
github.com/mitchellh/go-ps v1.0.0
github.com/mitchellh/mapstructure v1.5.0
github.com/moby/buildkit v0.21.0
github.com/moby/buildkit v0.22.0
github.com/moby/go-archive v0.1.0
github.com/moby/patternmatcher v0.6.0
github.com/moby/sys/atomicwriter v0.1.0
@@ -37,7 +37,6 @@ require (
github.com/opencontainers/go-digest v1.0.0
github.com/opencontainers/image-spec v1.1.1
github.com/otiai10/copy v1.14.1
github.com/r3labs/sse v0.0.0-20210224172625-26fe804710bc
github.com/sirupsen/logrus v1.9.3
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966
github.com/spf13/cobra v1.9.1
@@ -45,19 +44,19 @@ require (
github.com/stretchr/testify v1.10.0
github.com/theupdateframework/notary v0.7.0
github.com/tilt-dev/fsnotify v1.4.8-0.20220602155310-fff9c274a375
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0
go.opentelemetry.io/otel v1.34.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.31.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.31.0
go.opentelemetry.io/otel/metric v1.34.0
go.opentelemetry.io/otel/sdk v1.34.0
go.opentelemetry.io/otel/trace v1.34.0
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0
go.opentelemetry.io/otel v1.35.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0
go.opentelemetry.io/otel/metric v1.35.0
go.opentelemetry.io/otel/sdk v1.35.0
go.opentelemetry.io/otel/trace v1.35.0
go.uber.org/goleak v1.3.0
go.uber.org/mock v0.5.1
go.uber.org/mock v0.5.2
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0
golang.org/x/sync v0.13.0
golang.org/x/sys v0.32.0
google.golang.org/grpc v1.71.1
golang.org/x/sync v0.14.0
golang.org/x/sys v0.33.0
google.golang.org/grpc v1.72.1
gopkg.in/yaml.v3 v3.0.1
gotest.tools/v3 v3.5.2
tags.cncf.io/container-device-interface v1.0.1
@@ -65,7 +64,6 @@ require (
require (
dario.cat/mergo v1.0.1 // indirect
github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 // indirect
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect
github.com/Masterminds/semver/v3 v3.2.1 // indirect
github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect
@@ -86,7 +84,7 @@ require (
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/containerd/console v1.0.4 // indirect
github.com/containerd/containerd/api v1.8.0 // indirect
github.com/containerd/containerd/api v1.9.0 // indirect
github.com/containerd/continuity v0.4.5 // indirect
github.com/containerd/errdefs v1.0.0 // indirect
github.com/containerd/errdefs/pkg v0.3.0 // indirect
@@ -104,9 +102,9 @@ require (
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-openapi/jsonpointer v0.19.6 // indirect
github.com/go-openapi/jsonpointer v0.21.0 // indirect
github.com/go-openapi/jsonreference v0.20.2 // indirect
github.com/go-openapi/swag v0.22.4 // indirect
github.com/go-openapi/swag v0.23.0 // indirect
github.com/go-viper/mapstructure/v2 v2.0.0 // indirect
github.com/gofrs/flock v0.12.1 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
@@ -118,10 +116,9 @@ require (
github.com/google/uuid v1.6.0 // indirect
github.com/gorilla/mux v1.8.1 // indirect
github.com/gorilla/websocket v1.5.0 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/imdario/mergo v0.3.16 // indirect
github.com/in-toto/in-toto-golang v0.5.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/inhies/go-bytesize v0.0.0-20220417184213-4913239db9cf // indirect
@@ -133,13 +130,13 @@ require (
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.15 // 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/moby/docker-image-spec v1.3.1 // indirect
github.com/moby/locker v1.0.1 // indirect
github.com/moby/spdystream v0.4.0 // indirect
github.com/moby/spdystream v0.5.0 // indirect
github.com/moby/sys/capability v0.4.0 // indirect
github.com/moby/sys/mountinfo v0.7.2 // indirect
github.com/moby/sys/sequential v0.6.0 // indirect
@@ -156,9 +153,9 @@ require (
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.20.5 // indirect
github.com/prometheus/client_golang v1.22.0 // indirect
github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.55.0 // indirect
github.com/prometheus/common v0.62.0 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
github.com/rivo/uniseg v0.2.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
@@ -166,7 +163,7 @@ require (
github.com/serialx/hashring v0.0.0-20200727003509-22c0c7ab6b1b // indirect
github.com/shibumi/go-pathspec v1.3.0 // indirect
github.com/tonistiigi/dchapes-mode v0.0.0-20250318174251-73d941a28323 // indirect
github.com/tonistiigi/fsutil v0.0.0-20250410151801-5b74a7ad7583 // indirect
github.com/tonistiigi/fsutil v0.0.0-20250417144416-3f76f8130144 // indirect
github.com/tonistiigi/go-csvvalue v0.0.0-20240710180619-ddb21b71c0b4 // indirect
github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea // indirect
github.com/tonistiigi/vt100 v0.0.0-20240514184818-90bafcd6abab // indirect
@@ -175,36 +172,35 @@ require (
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
github.com/xhit/go-str2duration/v2 v2.1.0 // indirect
github.com/zclconf/go-cty v1.16.0 // indirect
github.com/zclconf/go-cty v1.16.2 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.56.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.56.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.31.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.31.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.31.0 // indirect
go.opentelemetry.io/otel/sdk/metric v1.34.0 // indirect
go.opentelemetry.io/proto/otlp v1.3.1 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0 // indirect
go.opentelemetry.io/otel/sdk/metric v1.35.0 // indirect
go.opentelemetry.io/proto/otlp v1.5.0 // indirect
golang.org/x/crypto v0.37.0 // indirect
golang.org/x/net v0.39.0 // indirect
golang.org/x/oauth2 v0.25.0 // indirect
golang.org/x/oauth2 v0.29.0 // indirect
golang.org/x/term v0.31.0 // indirect
golang.org/x/text v0.24.0 // indirect
golang.org/x/time v0.11.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20250106144421-5f5ef82da422 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f // indirect
google.golang.org/protobuf v1.36.4 // indirect
gopkg.in/cenkalti/backoff.v1 v1.1.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20250218202821-56aae31c358a // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a // indirect
google.golang.org/protobuf v1.36.6 // indirect
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
k8s.io/api v0.31.2 // indirect
k8s.io/apimachinery v0.31.2 // indirect
k8s.io/client-go v0.31.2 // indirect
k8s.io/api v0.32.3 // indirect
k8s.io/apimachinery v0.32.3 // indirect
k8s.io/client-go v0.32.3 // indirect
k8s.io/klog/v2 v2.130.1 // indirect
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 // indirect
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f // indirect
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.4.2 // indirect
sigs.k8s.io/yaml v1.4.0 // indirect
)

206
go.sum
View File

@@ -2,8 +2,6 @@ dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s=
dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8afgbRMd7mFxO99hRNu+6tazq8nFF9lIwo9JFroBk=
github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8=
github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20231105174938-2b5cbb29f3e2 h1:dIScnXFlF784X79oi7MzVT6GWqr/W1uUt0pB5CsDs9M=
github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20231105174938-2b5cbb29f3e2/go.mod h1:gCLVsLfv1egrcZu+GoJATN5ts75F2s62ih/457eWzOw=
github.com/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=
@@ -16,8 +14,8 @@ github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0
github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
github.com/Microsoft/hcsshim v0.12.9 h1:2zJy5KA+l0loz1HzEGqyNnjd3fyZA31ZBCGKacp6lLg=
github.com/Microsoft/hcsshim v0.12.9/go.mod h1:fJ0gkFAna6ukt0bLdKB8djt4XIJhF/vEPuoIWYVvZ8Y=
github.com/Microsoft/hcsshim v0.13.0 h1:/BcXOiS6Qi7N9XqUcv27vkIuVOkBEcWstd2pMlWSeaA=
github.com/Microsoft/hcsshim v0.13.0/go.mod h1:9KWJ/8DgU+QzYGupX4tzMhRQE8h6w90lH6HAaclpEok=
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=
@@ -82,16 +80,16 @@ github.com/cloudflare/cfssl v0.0.0-20180223231731-4e2dcbde5004 h1:lkAMpLVBDaj17e
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.6.0 h1:/+oBD2ixSENOeN/TlJqWZmUak0xM8A7J08w/z661Wd4=
github.com/compose-spec/compose-go/v2 v2.6.0/go.mod h1:vPlkN0i+0LjLf9rv52lodNMUTJF5YHVfHVGLLIP67NA=
github.com/compose-spec/compose-go/v2 v2.6.4 h1:Gjv6x8eAhqwwWvoXIo0oZ4bDQBh0OMwdU7LUL9PDLiM=
github.com/compose-spec/compose-go/v2 v2.6.4/go.mod h1:vPlkN0i+0LjLf9rv52lodNMUTJF5YHVfHVGLLIP67NA=
github.com/containerd/cgroups/v3 v3.0.5 h1:44na7Ud+VwyE7LIoJ8JTNQOa549a8543BmzaJHo6Bzo=
github.com/containerd/cgroups/v3 v3.0.5/go.mod h1:SA5DLYnXO8pTGYiAHXz94qvLQTKfVM5GEVisn4jpins=
github.com/containerd/console v1.0.4 h1:F2g4+oChYvBTsASRTz8NP6iIAi97J3TtSAsLbIFn4ro=
github.com/containerd/console v1.0.4/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk=
github.com/containerd/containerd/api v1.8.0 h1:hVTNJKR8fMc/2Tiw60ZRijntNMd1U+JVMyTRdsD2bS0=
github.com/containerd/containerd/api v1.8.0/go.mod h1:dFv4lt6S20wTu/hMcP4350RL87qPWLVa/OHOwmmdnYc=
github.com/containerd/containerd/v2 v2.0.4 h1:+r7yJMwhTfMm3CDyiBjMBQO8a9CTBxL2Bg/JtqtIwB8=
github.com/containerd/containerd/v2 v2.0.4/go.mod h1:5j9QUUaV/cy9ZeAx4S+8n9ffpf+iYnEj4jiExgcbuLY=
github.com/containerd/containerd/api v1.9.0 h1:HZ/licowTRazus+wt9fM6r/9BQO7S0vD5lMcWspGIg0=
github.com/containerd/containerd/api v1.9.0/go.mod h1:GhghKFmTR3hNtyznBoQ0EMWr9ju5AqHjcZPsSpTKutI=
github.com/containerd/containerd/v2 v2.1.1 h1:znnkm7Ajz8lg8BcIPMhc/9yjBRN3B+OkNKqKisKfwwM=
github.com/containerd/containerd/v2 v2.1.1/go.mod h1:zIfkQj4RIodclYQkX7GSSswSwgP8d/XxDOtOAoSDIGU=
github.com/containerd/continuity v0.4.5 h1:ZRoN1sXq9u7V6QoHMcVWGhOwDFqZ4B9i5H6un1Wh0x4=
github.com/containerd/continuity v0.4.5/go.mod h1:/lNJvtJKUQStBzpVQ1+rasXO1LAWtUQssk28EZvJ3nE=
github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI=
@@ -127,17 +125,17 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
github.com/denisenkom/go-mssqldb v0.0.0-20191128021309-1d7a30a10f73/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
github.com/docker/buildx v0.23.0 h1:qoYhuWyZ6PVCrWbkxClLzBWDBCUkyFK6Chjzg6nU+V8=
github.com/docker/buildx v0.23.0/go.mod h1:y/6Zf/y3Bf0zTWqgg8PuNFATcqnuhFmQuNf4VyrnPtg=
github.com/docker/cli v28.1.0+incompatible h1:WiJhUBbuIH/BsJtth+C1hPwra4P0nsKJiWy9ie5My5s=
github.com/docker/cli v28.1.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/buildx v0.24.0 h1:qiD+xktY+Fs3R79oz8M+7pbhip78qGLx6LBuVmyb+64=
github.com/docker/buildx v0.24.0/go.mod h1:vYkdBUBjFo/i5vUE0mkajGlk03gE0T/HaGXXhgIxo8E=
github.com/docker/cli v28.1.1+incompatible h1:eyUemzeI45DY7eDPuwUcmDyDj1pM98oD5MdSpiItp8k=
github.com/docker/cli v28.1.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/cli-docs-tool v0.9.0 h1:CVwQbE+ZziwlPqrJ7LRyUF6GvCA+6gj7MTCsayaK9t0=
github.com/docker/cli-docs-tool v0.9.0/go.mod h1:ClrwlNW+UioiRyH9GiAOe1o3J/TsY3Tr1ipoypjAUtc=
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk=
github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/docker v28.1.0+incompatible h1:4iqpcWQCt3Txcz7iWIb1U3SZ/n9ffo4U+ryY5/3eOp0=
github.com/docker/docker v28.1.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker v28.1.1+incompatible h1:49M11BFLsVO1gxY9UX9p/zwkE/rswggs8AdFmXQw51I=
github.com/docker/docker v28.1.1+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=
@@ -175,13 +173,14 @@ github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE=
github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE=
github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k=
github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU=
github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
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=
@@ -217,8 +216,8 @@ github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af h1:kmjWCqn2qkEml422C2Rrd27c3VGxi6a/6HNq8QmHRKM=
github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo=
github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo=
github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
@@ -228,8 +227,8 @@ github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 h1:asbCHRVmodnJTuQ3qamDwqVOIjwqUPTYmYuemVOx+Ys=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0/go.mod h1:ggCgvZ2r7uOoQjOyu2Y1NhHmEPPzzuhWgcza5M1Ji1I=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1 h1:e9Rjr40Z98/clHv5Yg79Is0NtosR5LXRvdr7o/6NwbA=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1/go.mod h1:tIxuGz/9mpox++sgp9fJjHO0+q1X9/UOWd798aAm22M=
github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed h1:5upAirOpQc1Q53c0bnx2ufif5kANL7bfZWcc6VJWJd8=
github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
@@ -244,8 +243,6 @@ github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09
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/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4=
github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY=
github.com/in-toto/in-toto-golang v0.5.0 h1:hb8bgwr0M2hGdDsLjkJ3ZqJ8JFLL/tgYdAxF/XEFBbY=
github.com/in-toto/in-toto-golang v0.5.0/go.mod h1:/Rq0IZHLV7Ku5gielPT4wPHJfH1GdHMCq8+WPxw8/BE=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
@@ -300,8 +297,8 @@ github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
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=
@@ -318,8 +315,8 @@ github.com/mitchellh/hashstructure/v2 v2.0.2/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/z
github.com/mitchellh/mapstructure v0.0.0-20150613213606-2caf8efc9366/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/moby/buildkit v0.21.0 h1:+z4vVqgt0spLrOSxi4DLedRbIh2gbNVlZ5q4rsnNp60=
github.com/moby/buildkit v0.21.0/go.mod h1:mBq0D44uCyz2PdX8T/qym5LBbkBO3GGv0wqgX9ABYYw=
github.com/moby/buildkit v0.22.0 h1:aWN06w1YGSVN1XfeZbj2ZbgY+zi5xDAjEFI8Cy9fTjA=
github.com/moby/buildkit v0.22.0/go.mod h1:j4pP5hxiTWcz7xuTK2cyxQislHl/N2WWHzOy43DlLJw=
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=
@@ -328,8 +325,8 @@ github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg=
github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc=
github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk=
github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc=
github.com/moby/spdystream v0.4.0 h1:Vy79D6mHeJJjiPdFEL2yku1kl0chZpJfZcPpb16BRl8=
github.com/moby/spdystream v0.4.0/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI=
github.com/moby/spdystream v0.5.0 h1:7r0J1Si3QO/kjRitvSLVVFUjxMEb/YLj6S9FF62JBCU=
github.com/moby/spdystream v0.5.0/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI=
github.com/moby/sys/atomicwriter v0.1.0 h1:kw5D/EqkBwsBFi0ss9v1VG3wIkVhzGvLklJ+w3A14Sw=
github.com/moby/sys/atomicwriter v0.1.0/go.mod h1:Ul8oqv2ZMNHOceF643P6FKPXeCmYtlQMvpizfsSoaWs=
github.com/moby/sys/capability v0.4.0 h1:4D4mI6KlNtWMCM1Z/K0i7RV1FkX+DBDHKVJpCndZoHk=
@@ -366,22 +363,22 @@ github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLA
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.0 h1:Iw5WCbBcaAAd0fpRb1c9r5YCylv4XDoCSigm1zLevwU=
github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg=
github.com/onsi/ginkgo/v2 v2.19.0 h1:9Cnnf7UHo57Hy3k6/m5k3dRfGTMXGvxhHFvkDTCTpvA=
github.com/onsi/ginkgo/v2 v2.19.0/go.mod h1:rlwLi9PilAFJ8jCg9UE1QP6VBpd6/xj3SRC0d6TU0To=
github.com/onsi/ginkgo/v2 v2.21.0 h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM=
github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA=
github.com/onsi/gomega v1.19.0 h1:4ieX6qQjPP/BfC3mpsAtIGGlxTWPeA3Inl/7DtXw1tw=
github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro=
github.com/onsi/gomega v1.35.1 h1:Cwbd75ZBPxFSuZ6T+rN/WCb/gOc6YgFBXLlZLhC7Ds4=
github.com/onsi/gomega v1.35.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog=
github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040=
github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M=
github.com/opencontainers/runtime-spec v1.2.0 h1:z97+pHb3uELt/yiAWD691HNHQIF07bE7dzrbT927iTk=
github.com/opencontainers/runtime-spec v1.2.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opencontainers/selinux v1.11.1 h1:nHFvthhM0qY8/m+vfhJylliSshm8G1jJ2jDMcgULaH8=
github.com/opencontainers/selinux v1.11.1/go.mod h1:E5dMC3VPuVvVHDYmi78qvhJp8+M586T4DlDRYpFkyec=
github.com/opencontainers/runtime-spec v1.2.1 h1:S4k4ryNgEpxW1dzyqffOmhI1BHYcjzU8lpJfSlR0xww=
github.com/opencontainers/runtime-spec v1.2.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opencontainers/selinux v1.12.0 h1:6n5JV4Cf+4y0KNXW48TLj5DwfXpvWlxXplUkdTrmPb8=
github.com/opencontainers/selinux v1.12.0/go.mod h1:BTPX+bjVbWGXw7ZZWUbdENt8w0htPSrlgOOysQaU62U=
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=
@@ -402,8 +399,8 @@ github.com/prometheus/client_golang v0.9.0-pre1.0.20180209125602-c332b6f63c06/go
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g=
github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y=
github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q=
github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0=
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=
@@ -412,16 +409,14 @@ github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQy
github.com/prometheus/common v0.0.0-20180110214958-89604d197083/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc=
github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc=
github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8=
github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io=
github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
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.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
github.com/r3labs/sse v0.0.0-20210224172625-26fe804710bc h1:zAsgcP8MhzAbhMnB1QQ2O7ZhWYVGYSR2iVcjzQuPV+o=
github.com/r3labs/sse v0.0.0-20210224172625-26fe804710bc/go.mod h1:S8xSOnV3CgpNrWd0GQ/OoQfMtlg2uPRSuTzcSGrzwK8=
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
@@ -477,16 +472,16 @@ github.com/tilt-dev/fsnotify v1.4.8-0.20220602155310-fff9c274a375 h1:QB54BJwA6x8
github.com/tilt-dev/fsnotify v1.4.8-0.20220602155310-fff9c274a375/go.mod h1:xRroudyp5iVtxKqZCrA6n2TLFRBf8bmnjr1UD4x+z7g=
github.com/tonistiigi/dchapes-mode v0.0.0-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-20250410151801-5b74a7ad7583 h1:mK+ZskNt7SG4dxfKIi27C7qHAQzyjAVt1iyTf0hmsNc=
github.com/tonistiigi/fsutil v0.0.0-20250410151801-5b74a7ad7583/go.mod h1:BKdcez7BiVtBvIcef90ZPc6ebqIWr4JWD7+EvLm6J98=
github.com/tonistiigi/fsutil v0.0.0-20250417144416-3f76f8130144 h1:k9tdF32oJYwtjzMx+D26M6eYiCaAPdJ7tyN7tF1oU5Q=
github.com/tonistiigi/fsutil v0.0.0-20250417144416-3f76f8130144/go.mod h1:BKdcez7BiVtBvIcef90ZPc6ebqIWr4JWD7+EvLm6J98=
github.com/tonistiigi/go-csvvalue v0.0.0-20240710180619-ddb21b71c0b4 h1:7I5c2Ig/5FgqkYOh/N87NzoyI9U15qUPXhDD8uCupv8=
github.com/tonistiigi/go-csvvalue v0.0.0-20240710180619-ddb21b71c0b4/go.mod h1:278M4p8WsNh3n4a1eqiFcV2FGk7wE5fwUpUom9mK9lE=
github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea h1:SXhTLE6pb6eld/v/cCndK0AMpt1wiVFb/YYmqB3/QG0=
github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea/go.mod h1:WPnis/6cRcDZSUvVmezrxJPkiO87ThFYsoUiMwWNDJk=
github.com/tonistiigi/vt100 v0.0.0-20240514184818-90bafcd6abab h1:H6aJ0yKQ0gF49Qb2z5hI1UHxSQt4JMyxebFR15KnApw=
github.com/tonistiigi/vt100 v0.0.0-20240514184818-90bafcd6abab/go.mod h1:ulncasL3N9uLrVann0m+CDlJKWsIAP34MPcOJF6VRvc=
github.com/vbatts/tar-split v0.11.6 h1:4SjTW5+PU11n6fZenf2IPoV8/tz3AaYHMWjf23envGs=
github.com/vbatts/tar-split v0.11.6/go.mod h1:dqKNtesIOr2j2Qv3W/cHjnvk9I8+G7oAkFDFN6TCBEI=
github.com/vbatts/tar-split v0.12.1 h1:CqKoORW7BUWBe7UL/iqTVvkTBOF8UvOMKOIZykxnnbo=
github.com/vbatts/tar-split v0.12.1/go.mod h1:eF6B6i6ftWQcDqEn3/iGFRFRo8cBIMSJVOpnNdfTMFA=
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
@@ -501,44 +496,44 @@ 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=
github.com/zclconf/go-cty v1.16.0 h1:xPKEhst+BW5D0wxebMZkxgapvOE/dw7bFTlgSc9nD6w=
github.com/zclconf/go-cty v1.16.0/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE=
github.com/zclconf/go-cty v1.16.2 h1:LAJSwc3v81IRBZyUVQDUdZ7hs3SYs9jv0eZJDWHD/70=
github.com/zclconf/go-cty v1.16.2/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE=
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.56.0 h1:yMkBS9yViCc7U7yeLzJPM2XizlfdVvBRSmsQDWu6qc0=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.56.0/go.mod h1:n8MR6/liuGB5EmTETUBeU5ZgqMOlqKRxUaqPQBOANZ8=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 h1:x7wzEgXfnzJcHDwStJT+mxOz4etr2EcexjqhBvmoakw=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0/go.mod h1:rg+RlpR5dKwaS95IyyZqj5Wd4E13lk/msnTS0Xl9lJM=
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.56.0 h1:4BZHA+B1wXEQoGNHxW8mURaLhcdGwvRnmhGbm+odRbc=
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.56.0/go.mod h1:3qi2EEwMgB4xnKgPLqsDP3j9qxnHDZeHsnAxfjQqTko=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0 h1:UP6IpuHFkUgOQL9FFQFrZ+5LiwhhYRbi7VZSIx6Nj5s=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0/go.mod h1:qxuZLtbq5QDtdeSHsS7bcf6EH6uO6jUAgk764zd3rhM=
go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY=
go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 h1:sbiXRNDSWJOTobXh5HyQKjq6wUC5tNybqjIqDpAY4CU=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0/go.mod h1:69uWxva0WgAA/4bu2Yy70SLDBwZXuQ6PbBpbsa5iZrQ=
go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ=
go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.31.0 h1:FZ6ei8GFW7kyPYdxJaV2rgI6M+4tvZzhYsQ2wgyVC08=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.31.0/go.mod h1:MdEu/mC6j3D+tTEfvI15b5Ci2Fn7NneJ71YMoiS3tpI=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.31.0 h1:ZsXq73BERAiNuuFXYqP4MR5hBrjXfMGSO+Cx7qoOZiM=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.31.0/go.mod h1:hg1zaDMpyZJuUzjFxFsRYBoccE86tM9Uf4IqNMUxvrY=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.31.0 h1:K0XaT3DwHAcV4nKLzcQvwAgSyisUghWoY20I7huthMk=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.31.0/go.mod h1:B5Ki776z/MBnVha1Nzwp5arlzBbE3+1jk+pGmaP5HME=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.31.0 h1:FFeLy03iVTXP6ffeN2iXrxfGsZGCjVx0/4KlizjyBwU=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.31.0/go.mod h1:TMu73/k1CP8nBUpDLc71Wj/Kf7ZS9FK5b53VapRsP9o=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.31.0 h1:lUsI2TYsQw2r1IASwoROaCnjdj2cvC2+Jbxvk6nHnWU=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.31.0/go.mod h1:2HpZxxQurfGxJlJDblybejHB6RX6pmExPNe517hREw4=
go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ=
go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE=
go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A=
go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU=
go.opentelemetry.io/otel/sdk/metric v1.34.0 h1:5CeK9ujjbFVL5c1PhLuStg1wxA7vQv7ce1EK0Gyvahk=
go.opentelemetry.io/otel/sdk/metric v1.34.0/go.mod h1:jQ/r8Ze28zRKoNRdkjCZxfs6YvBTG1+YIqyFVFYec5w=
go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k=
go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE=
go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0=
go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 h1:1fTNlAIJZGWLP5FVu0fikVry1IsiUnXjf7QFvoNN3Xw=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0/go.mod h1:zjPK58DtkqQFn+YUMbx0M2XV3QgKU0gS9LeGohREyK4=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0 h1:m639+BofXTvcY1q8CGs4ItwQarYtJPOWmVobfM1HpVI=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0/go.mod h1:LjReUci/F4BUyv+y4dwnq3h/26iNOeC3wAIqgvTIZVo=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0 h1:xJ2qHD0C1BeYVTLLR9sX12+Qb95kfeD/byKj6Ky1pXg=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0/go.mod h1:u5BF1xyjstDowA1R5QAO9JHzqK+ublenEW/dyqTjBVk=
go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M=
go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE=
go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY=
go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg=
go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o=
go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w=
go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs=
go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc=
go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4=
go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/mock v0.5.1 h1:ASgazW/qBmR+A32MYFDB6E2POoTgOwT509VP0CT/fjs=
go.uber.org/mock v0.5.1/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM=
go.uber.org/mock v0.5.2 h1:LbtPTcP8A5k9WPXj54PPPbjcI4Y6lhyOZXn+VS7wNko=
go.uber.org/mock v0.5.2/go.mod h1:wLlUxC2vVTPTaE3UD51E0BGOAElKrILxhVSDYQLld5o=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
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=
@@ -561,15 +556,14 @@ golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73r
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-20191116160921-f9c825593386/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.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY=
golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=
golang.org/x/oauth2 v0.25.0 h1:CY4y7XT9v0cRI9oupztF8AgiIu99L/ksR/Xp/6jrZ70=
golang.org/x/oauth2 v0.25.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/oauth2 v0.29.0 h1:WdYw2tdTK1S8olAzWHdgeqfy+Mtm9XNhv/xJsY65d98=
golang.org/x/oauth2 v0.29.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
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=
@@ -577,8 +571,8 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610=
golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ=
golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -600,8 +594,8 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
@@ -626,25 +620,25 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/genproto/googleapis/api v0.0.0-20250106144421-5f5ef82da422 h1:GVIKPyP/kLIyVOgOnTwFOrvQaQUzOzGMCxgFUOEmm24=
google.golang.org/genproto/googleapis/api v0.0.0-20250106144421-5f5ef82da422/go.mod h1:b6h1vNKhxaSoEI+5jc3PJUCustfli/mRab7295pY7rw=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f h1:OxYkA3wjPsZyBylwymxSHa7ViiW1Sml4ToBrncvFehI=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f/go.mod h1:+2Yz8+CLJbIfL9z73EW45avw8Lmge3xVElCP9zEKi50=
google.golang.org/genproto/googleapis/api v0.0.0-20250218202821-56aae31c358a h1:nwKuGPlUAt+aR+pcrkfFRrTU1BVrSmYyYMxYbUIVHr0=
google.golang.org/genproto/googleapis/api v0.0.0-20250218202821-56aae31c358a/go.mod h1:3kWAYMk1I75K4vykHtKt2ycnOgpA6974V7bREqbsenU=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a h1:51aaUVRocpvUOSQKM6Q7VuoaktNIaMCLuhZB6DKksq4=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a/go.mod h1:uRxBH1mhmO8PGhU89cMcHaXKZqO+OfakD8QQO0oYwlQ=
google.golang.org/grpc v1.0.5/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
google.golang.org/grpc v1.71.1 h1:ffsFWr7ygTUscGPI0KKK6TLrGz0476KUvvsbqWK0rPI=
google.golang.org/grpc v1.71.1/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec=
google.golang.org/protobuf v1.36.4 h1:6A3ZDJHn/eNqc1i+IdefRzy/9PokBTPvcqMySR7NNIM=
google.golang.org/protobuf v1.36.4/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
google.golang.org/grpc v1.72.1 h1:HR03wO6eyZ7lknl75XlxABNVLLFc2PAb6mHlYh756mA=
google.golang.org/grpc v1.72.1/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM=
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/cenkalti/backoff.v1 v1.1.0 h1:Arh75ttbsvlpVA7WtVpH4u9h6Zl46xuptxqLxPiSo4Y=
gopkg.in/cenkalti/backoff.v1 v1.1.0/go.mod h1:J6Vskwqd+OMVJl8C33mmtxTBs2gyzfv7UDAkHu8BrjI=
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=
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/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4=
gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M=
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/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
@@ -665,22 +659,22 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q=
gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA=
k8s.io/api v0.31.2 h1:3wLBbL5Uom/8Zy98GRPXpJ254nEFpl+hwndmk9RwmL0=
k8s.io/api v0.31.2/go.mod h1:bWmGvrGPssSK1ljmLzd3pwCQ9MgoTsRCuK35u6SygUk=
k8s.io/apimachinery v0.31.2 h1:i4vUt2hPK56W6mlT7Ry+AO8eEsyxMD1U44NR22CLTYw=
k8s.io/apimachinery v0.31.2/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo=
k8s.io/client-go v0.31.2 h1:Y2F4dxU5d3AQj+ybwSMqQnpZH9F30//1ObxOKlTI9yc=
k8s.io/client-go v0.31.2/go.mod h1:NPa74jSVR/+eez2dFsEIHNa+3o09vtNaWwWwb1qSxSs=
k8s.io/api v0.32.3 h1:Hw7KqxRusq+6QSplE3NYG4MBxZw1BZnq4aP4cJVINls=
k8s.io/api v0.32.3/go.mod h1:2wEDTXADtm/HA7CCMD8D8bK4yuBUptzaRhYcYEEYA3k=
k8s.io/apimachinery v0.32.3 h1:JmDuDarhDmA/Li7j3aPrwhpNBA94Nvk5zLeOge9HH1U=
k8s.io/apimachinery v0.32.3/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE=
k8s.io/client-go v0.32.3 h1:RKPVltzopkSgHS7aS98QdscAgtgah/+zmpAogooIqVU=
k8s.io/client-go v0.32.3/go.mod h1:3v0+3k4IcT9bXTc4V2rt+d2ZPPG700Xy6Oi0Gdl2PaY=
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag=
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98=
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1J8+AsQnQCKsi8A=
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4=
sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08=
k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f h1:GA7//TjRY9yWGy1poLzYYJJ4JRdzg3+O6e8I+e+8T5Y=
k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f/go.mod h1:R/HEjbvWI0qdfb8viZUeVZm0X6IZnxAydC7YU42CMw4=
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6JSWYFzOFnYeS6Ro=
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8=
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3/go.mod h1:18nIHnGi6636UCz6m8i4DhaJ65T6EruyzmoQqI2BVDo=
sigs.k8s.io/structured-merge-diff/v4 v4.4.2 h1:MdmvkGuXi/8io6ixD5wud3vOLwc1rj0aNqRlpuvjmwA=
sigs.k8s.io/structured-merge-diff/v4 v4.4.2/go.mod h1:N8f93tFZh9U6vpxwRArLiikrE5/2tiu1w1AGfACIGE4=
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
tags.cncf.io/container-device-interface v1.0.1 h1:KqQDr4vIlxwfYh0Ed/uJGVgX+CHAkahrgabg6Q8GYxc=

View File

@@ -17,10 +17,8 @@
package desktop
import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"io"
"net"
@@ -29,7 +27,6 @@ import (
"github.com/docker/compose/v2/internal"
"github.com/docker/compose/v2/internal/memnet"
"github.com/r3labs/sse"
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
)
@@ -130,212 +127,6 @@ func (c *Client) FeatureFlags(ctx context.Context) (FeatureFlagResponse, error)
return ret, nil
}
type GetFileSharesConfigResponse struct {
Active bool `json:"active"`
Compose struct {
ManageBindMounts bool `json:"manageBindMounts"`
}
}
func (c *Client) GetFileSharesConfig(ctx context.Context) (*GetFileSharesConfigResponse, error) {
req, err := c.newRequest(ctx, http.MethodGet, "/mutagen/file-shares/config", http.NoBody)
if err != nil {
return nil, err
}
resp, err := c.client.Do(req)
if err != nil {
return nil, err
}
defer func() {
_ = resp.Body.Close()
}()
if resp.StatusCode != http.StatusOK {
return nil, newHTTPStatusCodeError(resp)
}
var ret GetFileSharesConfigResponse
if err := json.NewDecoder(resp.Body).Decode(&ret); err != nil {
return nil, err
}
return &ret, nil
}
type CreateFileShareRequest struct {
HostPath string `json:"hostPath"`
Labels map[string]string `json:"labels,omitempty"`
}
type CreateFileShareResponse struct {
FileShareID string `json:"fileShareID"`
}
func (c *Client) CreateFileShare(ctx context.Context, r CreateFileShareRequest) (*CreateFileShareResponse, error) {
rawBody, err := json.Marshal(r)
if err != nil {
return nil, fmt.Errorf("failed to marshal request: %w", err)
}
req, err := c.newRequest(ctx, http.MethodPost, "/mutagen/file-shares", bytes.NewReader(rawBody))
if err != nil {
return nil, err
}
req.Header.Set("Content-Type", "application/json")
resp, err := c.client.Do(req)
if err != nil {
return nil, err
}
defer func() {
_ = resp.Body.Close()
}()
if resp.StatusCode != http.StatusOK {
errBody, _ := io.ReadAll(resp.Body)
return nil, fmt.Errorf("unexpected status code %d: %s", resp.StatusCode, string(errBody))
}
var ret CreateFileShareResponse
if err := json.NewDecoder(resp.Body).Decode(&ret); err != nil {
return nil, err
}
return &ret, nil
}
type FileShareReceiverState struct {
TotalReceivedSize uint64 `json:"totalReceivedSize"`
}
type FileShareEndpoint struct {
Path string `json:"path"`
TotalFileSize uint64 `json:"totalFileSize,omitempty"`
StagingProgress *FileShareReceiverState `json:"stagingProgress"`
}
type FileShareSession struct {
SessionID string `json:"identifier"`
Alpha FileShareEndpoint `json:"alpha"`
Beta FileShareEndpoint `json:"beta"`
Labels map[string]string `json:"labels"`
Status string `json:"status"`
}
func (c *Client) ListFileShares(ctx context.Context) ([]FileShareSession, error) {
req, err := c.newRequest(ctx, http.MethodGet, "/mutagen/file-shares", http.NoBody)
if err != nil {
return nil, err
}
resp, err := c.client.Do(req)
if err != nil {
return nil, err
}
defer func() {
_ = resp.Body.Close()
}()
if resp.StatusCode != http.StatusOK {
return nil, newHTTPStatusCodeError(resp)
}
var ret []FileShareSession
if err := json.NewDecoder(resp.Body).Decode(&ret); err != nil {
return nil, err
}
return ret, nil
}
func (c *Client) DeleteFileShare(ctx context.Context, id string) error {
req, err := c.newRequest(ctx, http.MethodDelete, "/mutagen/file-shares/"+id, http.NoBody)
if err != nil {
return err
}
resp, err := c.client.Do(req)
if err != nil {
return err
}
defer func() {
_ = resp.Body.Close()
}()
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
return newHTTPStatusCodeError(resp)
}
return nil
}
type EventMessage[T any] struct {
Value T
Error error
}
func newHTTPStatusCodeError(resp *http.Response) error {
r := io.LimitReader(resp.Body, 2048)
body, err := io.ReadAll(r)
if err != nil {
return fmt.Errorf("http status code %d", resp.StatusCode)
}
return fmt.Errorf("http status code %d: %s", resp.StatusCode, string(body))
}
func (c *Client) StreamFileShares(ctx context.Context) (<-chan EventMessage[[]FileShareSession], error) {
req, err := c.newRequest(ctx, http.MethodGet, "/mutagen/file-shares/stream", http.NoBody)
if err != nil {
return nil, err
}
resp, err := c.client.Do(req)
if err != nil {
return nil, err
}
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
_ = resp.Body.Close()
return nil, newHTTPStatusCodeError(resp)
}
events := make(chan EventMessage[[]FileShareSession])
go func(ctx context.Context) {
defer func() {
_ = resp.Body.Close()
close(events)
}()
if err := readEvents(ctx, resp.Body, events); err != nil {
select {
case <-ctx.Done():
case events <- EventMessage[[]FileShareSession]{Error: err}:
}
}
}(ctx)
return events, nil
}
func readEvents[T any](ctx context.Context, r io.Reader, events chan<- EventMessage[T]) error {
eventReader := sse.NewEventStreamReader(r)
for {
msg, err := eventReader.ReadEvent()
if errors.Is(err, io.EOF) {
return nil
} else if err != nil {
return fmt.Errorf("reading events: %w", err)
}
msg = bytes.TrimPrefix(msg, []byte("data: "))
var event T
if err := json.Unmarshal(msg, &event); err != nil {
return err
}
select {
case <-ctx.Done():
return context.Cause(ctx)
case events <- EventMessage[T]{Value: event}:
// event was sent to channel, read next
}
}
}
func (c *Client) newRequest(ctx context.Context, method, path string, body io.Reader) (*http.Request, error) {
req, err := http.NewRequestWithContext(ctx, method, backendURL(path), body)
if err != nil {

View File

@@ -1,384 +0,0 @@
/*
Copyright 2024 Docker Compose CLI authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package desktop
import (
"context"
"errors"
"fmt"
"strings"
"sync"
"github.com/docker/compose/v2/internal/paths"
"github.com/docker/compose/v2/pkg/api"
"github.com/docker/compose/v2/pkg/progress"
"github.com/docker/go-units"
"github.com/sirupsen/logrus"
)
// fileShareProgressID is the identifier used for the root grouping of file
// share events in the progress writer.
const fileShareProgressID = "Synchronized File Shares"
// RemoveFileSharesForProject removes any Synchronized File Shares that were
// created by Compose for this project in the past if possible.
//
// Errors are not propagated; they are only sent to the progress writer.
func RemoveFileSharesForProject(ctx context.Context, c *Client, projectName string) {
w := progress.ContextWriter(ctx)
existing, err := c.ListFileShares(ctx)
if err != nil {
w.TailMsgf("Synchronized File Shares not removed due to error: %v", err)
return
}
// filter the list first, so we can early return and not show the event if
// there's no sessions to clean up
var toRemove []FileShareSession
for _, share := range existing {
if share.Labels["com.docker.compose.project"] == projectName {
toRemove = append(toRemove, share)
}
}
if len(toRemove) == 0 {
return
}
w.Event(progress.NewEvent(fileShareProgressID, progress.Working, "Removing"))
rootResult := progress.Done
defer func() {
w.Event(progress.NewEvent(fileShareProgressID, rootResult, ""))
}()
for _, share := range toRemove {
shareID := share.Labels["com.docker.desktop.mutagen.file-share.id"]
if shareID == "" {
w.Event(progress.Event{
ID: share.Alpha.Path,
ParentID: fileShareProgressID,
Status: progress.Warning,
StatusText: "Invalid",
})
continue
}
w.Event(progress.Event{
ID: share.Alpha.Path,
ParentID: fileShareProgressID,
Status: progress.Working,
})
var status progress.EventStatus
var statusText string
if err := c.DeleteFileShare(ctx, shareID); err != nil {
// TODO(milas): Docker Desktop is doing weird things with error responses,
// once fixed, we can return proper error types from the client
if strings.Contains(err.Error(), "file share in use") {
status = progress.Warning
statusText = "Resource is still in use"
if rootResult != progress.Error {
// error takes precedence over warning
rootResult = progress.Warning
}
} else {
logrus.Debugf("Error deleting file share %q: %v", shareID, err)
status = progress.Error
rootResult = progress.Error
}
} else {
logrus.Debugf("Deleted file share: %s", shareID)
status = progress.Done
}
w.Event(progress.Event{
ID: share.Alpha.Path,
ParentID: fileShareProgressID,
Status: status,
StatusText: statusText,
})
}
}
// FileShareManager maps between Compose bind mounts and Desktop File Shares
// state.
type FileShareManager struct {
mu sync.Mutex
cli *Client
projectName string
hostPaths []string
// state holds session status keyed by file share ID.
state map[string]*FileShareSession
}
func NewFileShareManager(cli *Client, projectName string, hostPaths []string) *FileShareManager {
return &FileShareManager{
cli: cli,
projectName: projectName,
hostPaths: hostPaths,
state: make(map[string]*FileShareSession),
}
}
// EnsureExists looks for existing File Shares or creates new ones for the
// host paths.
//
// This function blocks until each share reaches steady state, at which point
// flow can continue.
func (m *FileShareManager) EnsureExists(ctx context.Context) (err error) {
w := progress.ContextWriter(ctx)
w.Event(progress.NewEvent(fileShareProgressID, progress.Working, ""))
defer func() {
if err != nil {
w.Event(progress.NewEvent(fileShareProgressID, progress.Error, ""))
} else {
w.Event(progress.NewEvent(fileShareProgressID, progress.Done, ""))
}
}()
wait := &waiter{
shareIDs: make(map[string]struct{}),
done: make(chan struct{}),
}
handler := m.eventHandler(w, wait)
ctx, cancel := context.WithCancel(ctx)
defer cancel()
// stream session events to update internal state for project
monitorErr := make(chan error, 1)
go func() {
defer close(monitorErr)
if err := m.watch(ctx, handler); err != nil && ctx.Err() == nil {
monitorErr <- err
}
}()
if err := m.initialize(ctx, wait, handler); err != nil {
return err
}
waitCh := wait.start()
if waitCh != nil {
select {
case <-ctx.Done():
return context.Cause(ctx)
case err := <-monitorErr:
if err != nil {
return fmt.Errorf("watching file share sessions: %w", err)
} else if ctx.Err() == nil {
// this indicates a bug - it should not stop w/o an error if the context is still active
return errors.New("file share session watch stopped unexpectedly")
}
case <-wait.start():
// everything is done
}
}
return nil
}
// initialize finds existing shares or creates new ones for the host paths.
//
// Once a share is found/created, its progress is monitored via the watch.
func (m *FileShareManager) initialize(ctx context.Context, wait *waiter, handler func(FileShareSession)) error {
// the watch is already running in the background, so the lock is taken
// throughout to prevent interleaving writes
m.mu.Lock()
defer m.mu.Unlock()
existing, err := m.cli.ListFileShares(ctx)
if err != nil {
return err
}
for _, path := range m.hostPaths {
var fileShareID string
var fss *FileShareSession
if fss = findExistingShare(path, existing); fss != nil {
fileShareID = fss.Beta.Path
logrus.Debugf("Found existing suitable file share %s for path %q [%s]", fileShareID, path, fss.Alpha.Path)
wait.addShare(fileShareID)
handler(*fss)
continue
} else {
req := CreateFileShareRequest{
HostPath: path,
Labels: map[string]string{
"com.docker.compose.project": m.projectName,
},
}
createResp, err := m.cli.CreateFileShare(ctx, req)
if err != nil {
return fmt.Errorf("creating file share: %w", err)
}
fileShareID = createResp.FileShareID
fss = m.state[fileShareID]
logrus.Debugf("Created file share %s for path %q", fileShareID, path)
}
wait.addShare(fileShareID)
if fss != nil {
handler(*fss)
}
}
return nil
}
func (m *FileShareManager) watch(ctx context.Context, handler func(FileShareSession)) error {
events, err := m.cli.StreamFileShares(ctx)
if err != nil {
return fmt.Errorf("streaming file shares: %w", err)
}
for {
select {
case <-ctx.Done():
return nil
case event := <-events:
if event.Error != nil {
return fmt.Errorf("reading file share events: %w", event.Error)
}
// closure for lock
func() {
m.mu.Lock()
defer m.mu.Unlock()
for _, fss := range event.Value {
handler(fss)
}
}()
}
}
}
// eventHandler updates internal state, keeps track of in-progress syncs, and
// prints relevant events to progress.
func (m *FileShareManager) eventHandler(w progress.Writer, wait *waiter) func(fss FileShareSession) {
return func(fss FileShareSession) {
fileShareID := fss.Beta.Path
shouldPrint := wait.isWatching(fileShareID)
forProject := fss.Labels[api.ProjectLabel] == m.projectName
if shouldPrint || forProject {
m.state[fileShareID] = &fss
}
var percent int
var current, total int64
if fss.Beta.StagingProgress != nil {
current = int64(fss.Beta.StagingProgress.TotalReceivedSize)
} else {
current = int64(fss.Beta.TotalFileSize)
}
total = int64(fss.Alpha.TotalFileSize)
if total != 0 {
percent = int(current * 100 / total)
}
var status progress.EventStatus
var text string
switch {
case strings.HasPrefix(fss.Status, "halted"):
wait.shareDone(fileShareID)
status = progress.Error
case fss.Status == "watching":
wait.shareDone(fileShareID)
status = progress.Done
percent = 100
case fss.Status == "staging-beta":
status = progress.Working
// TODO(milas): the printer doesn't style statuses for children nicely
text = fmt.Sprintf(" Syncing (%7s / %-7s)",
units.HumanSize(float64(current)),
units.HumanSize(float64(total)),
)
default:
// catch-all for various other transitional statuses
status = progress.Working
}
evt := progress.Event{
ID: fss.Alpha.Path,
Status: status,
Text: text,
ParentID: fileShareProgressID,
Current: current,
Total: total,
Percent: percent,
}
if shouldPrint {
w.Event(evt)
}
}
}
func findExistingShare(path string, existing []FileShareSession) *FileShareSession {
for _, share := range existing {
if paths.IsChild(share.Alpha.Path, path) {
return &share
}
}
return nil
}
type waiter struct {
mu sync.Mutex
shareIDs map[string]struct{}
done chan struct{}
}
func (w *waiter) addShare(fileShareID string) {
w.mu.Lock()
defer w.mu.Unlock()
w.shareIDs[fileShareID] = struct{}{}
}
func (w *waiter) isWatching(fileShareID string) bool {
w.mu.Lock()
defer w.mu.Unlock()
_, ok := w.shareIDs[fileShareID]
return ok
}
// start returns a channel to wait for any outstanding shares to be ready.
//
// If no shares are registered when this is called, nil is returned.
func (w *waiter) start() <-chan struct{} {
w.mu.Lock()
defer w.mu.Unlock()
if len(w.shareIDs) == 0 {
return nil
}
if w.done == nil {
w.done = make(chan struct{})
}
return w.done
}
func (w *waiter) shareDone(fileShareID string) {
w.mu.Lock()
defer w.mu.Unlock()
delete(w.shareIDs, fileShareID)
if len(w.shareIDs) == 0 && w.done != nil {
close(w.done)
w.done = nil
}
}

View File

@@ -1,2 +0,0 @@
The Docker End User License Agreement (https://www.docker.com/legal/docker-software-end-user-license-agreement) describes Docker's Terms for this software.
By downloading, accessing, or using this software you expressly accept and agree to the Terms set out in the Docker End User License Agreement.

View File

@@ -157,13 +157,17 @@ type BuildOptions struct {
Builder string
// Print don't actually run builder but print equivalent build config
Print bool
// Check let builder validate build configuration
Check bool
// Provenance
Provenance bool
}
// Apply mutates project according to build options
func (o BuildOptions) Apply(project *types.Project) error {
platform := project.Environment["DOCKER_DEFAULT_PLATFORM"]
for name, service := range project.Services {
if service.Image == "" && service.Build == nil {
if service.Provider == nil && service.Image == "" && service.Build == nil {
return fmt.Errorf("invalid service %q. Must specify either image or build", name)
}

View File

@@ -85,7 +85,22 @@ func (s *composeService) build(ctx context.Context, project *types.Project, opti
policy = types.IncludeDependencies
}
err := project.ForEachService(options.Services, func(serviceName string, service *types.ServiceConfig) error {
var err error
if len(options.Services) > 0 {
// As user requested some services to be built, also include those used as additional_contexts
options.Services = addBuildDependencies(options.Services, project)
// Some build dependencies we just introduced may not be enabled
project, err = project.WithServicesEnabled(options.Services...)
if err != nil {
return nil, err
}
}
project, err = project.WithSelectedServices(options.Services)
if err != nil {
return nil, err
}
err = project.ForEachService(options.Services, func(serviceName string, service *types.ServiceConfig) error {
if service.Build == nil {
return nil
}
@@ -466,6 +481,11 @@ func (s *composeService) toBuildOptions(project *types.Project, service types.Se
return build.Options{}, err
}
attests := map[string]*string{}
if !options.Provenance {
attests["provenance"] = nil
}
return build.Options{
Inputs: build.Inputs{
ContextPath: service.Build.Context,
@@ -489,6 +509,7 @@ func (s *composeService) toBuildOptions(project *types.Project, service types.Se
Session: sessionConfig,
Allow: allow,
SourcePolicy: sp,
Attests: attests,
}, nil
}
@@ -613,3 +634,21 @@ func parsePlatforms(service types.ServiceConfig) ([]specs.Platform, error) {
return ret, nil
}
func addBuildDependencies(services []string, project *types.Project) []string {
servicesWithDependencies := utils.NewSet(services...)
for _, service := range services {
b := project.Services[service].Build
if b != nil {
for _, target := range b.AdditionalContexts {
if s, found := strings.CutPrefix(target, types.ServicePrefix); found {
servicesWithDependencies.Add(s)
}
}
}
}
if len(servicesWithDependencies) > len(services) {
return addBuildDependencies(servicesWithDependencies.Elements(), project)
}
return servicesWithDependencies.Elements()
}

View File

@@ -124,6 +124,7 @@ type bakeMetadata map[string]buildStatus
type buildStatus struct {
Digest string `json:"containerimage.digest"`
Image string `json:"image.name"`
}
func (s *composeService) doBuildBake(ctx context.Context, project *types.Project, serviceToBeBuild types.Services, options api.BuildOptions) (map[string]string, error) { //nolint:gocyclo
@@ -142,9 +143,12 @@ func (s *composeService) doBuildBake(ctx context.Context, project *types.Project
Groups: map[string]bakeGroup{},
Targets: map[string]bakeTarget{},
}
var group bakeGroup
var privileged bool
var read []string
var (
group bakeGroup
privileged bool
read []string
expectedImages = make(map[string]string, len(serviceToBeBuild)) // service name -> expected image
)
for serviceName, service := range serviceToBeBuild {
if service.Build == nil {
@@ -161,6 +165,7 @@ func (s *composeService) doBuildBake(ctx context.Context, project *types.Project
}
image := api.GetImageNameOrDefault(service, project.Name)
expectedImages[serviceName] = image
entitlements := build.Entitlements
if slices.Contains(build.Entitlements, "security.insecure") {
@@ -171,12 +176,16 @@ func (s *composeService) doBuildBake(ctx context.Context, project *types.Project
privileged = true
}
var output string
var outputs []string
var call string
push := options.Push && service.Image != ""
if len(service.Build.Platforms) > 1 {
output = fmt.Sprintf("type=image,push=%t", push)
} else {
output = fmt.Sprintf("type=docker,load=true,push=%t", push)
switch {
case options.Check:
call = "lint"
case len(service.Build.Platforms) > 1:
outputs = []string{fmt.Sprintf("type=image,push=%t", push)}
default:
outputs = []string{fmt.Sprintf("type=docker,load=true,push=%t", push)}
}
read = append(read, build.Context)
@@ -207,7 +216,9 @@ func (s *composeService) doBuildBake(ctx context.Context, project *types.Project
ShmSize: build.ShmSize,
Ulimits: toBakeUlimits(build.Ulimits),
Entitlements: entitlements,
Outputs: []string{output},
Outputs: outputs,
Call: call,
}
group.Targets = append(group.Targets, serviceName)
}
@@ -220,7 +231,7 @@ func (s *composeService) doBuildBake(ctx context.Context, project *types.Project
}
if options.Print {
_, err = fmt.Fprintln(s.stdinfo(), string(b))
_, err = fmt.Fprintln(s.stdout(), string(b))
return nil, err
}
logrus.Debugf("bake build config:\n%s", string(b))
@@ -250,6 +261,9 @@ func (s *composeService) doBuildBake(ctx context.Context, project *types.Project
if options.Builder != "" {
args = append(args, "--builder", options.Builder)
}
if options.Quiet {
args = append(args, "--progress=quiet")
}
logrus.Debugf("Executing bake with args: %v", args)
@@ -279,7 +293,7 @@ func (s *composeService) doBuildBake(ctx context.Context, project *types.Project
return nil, err
}
var errMessage string
var errMessage []string
scanner := bufio.NewScanner(pipe)
scanner.Split(bufio.ScanLines)
@@ -295,7 +309,9 @@ func (s *composeService) doBuildBake(ctx context.Context, project *types.Project
err := decoder.Decode(&status)
if err != nil {
if strings.HasPrefix(line, "ERROR: ") {
errMessage = line[7:]
errMessage = append(errMessage, line[7:])
} else {
errMessage = append(errMessage, line)
}
continue
}
@@ -305,8 +321,8 @@ func (s *composeService) doBuildBake(ctx context.Context, project *types.Project
err = eg.Wait()
if err != nil {
if errMessage != "" {
return nil, errors.New(errMessage)
if len(errMessage) > 0 {
return nil, errors.New(strings.Join(errMessage, "\n"))
}
return nil, fmt.Errorf("failed to execute bake: %w", err)
}
@@ -324,8 +340,12 @@ func (s *composeService) doBuildBake(ctx context.Context, project *types.Project
cw := progress.ContextWriter(ctx)
results := map[string]string{}
for name, m := range md {
results[name] = m.Digest
for service, name := range expectedImages {
built, ok := md[service] // bake target == service name
if !ok {
return nil, fmt.Errorf("build result not found in Bake metadata for service %s", service)
}
results[name] = built.Digest
cw.Event(progress.BuiltEvent(name))
}
return results, nil

View File

@@ -595,6 +595,13 @@ func (s *composeService) createContainer(ctx context.Context, project *types.Pro
w.Event(progress.CreatingEvent(eventName))
ctr, err = s.createMobyContainer(ctx, project, service, name, number, nil, opts, w)
if err != nil {
if ctx.Err() == nil {
w.Event(progress.Event{
ID: eventName,
Status: progress.Error,
StatusText: err.Error(),
})
}
return
}
w.Event(progress.CreatedEvent(eventName))
@@ -603,10 +610,19 @@ func (s *composeService) createContainer(ctx context.Context, project *types.Pro
func (s *composeService) recreateContainer(ctx context.Context, project *types.Project, service types.ServiceConfig,
replaced containerType.Summary, inherit bool, timeout *time.Duration,
) (containerType.Summary, error) {
var created containerType.Summary
) (created containerType.Summary, err error) {
w := progress.ContextWriter(ctx)
w.Event(progress.NewEvent(getContainerProgressName(replaced), progress.Working, "Recreate"))
eventName := getContainerProgressName(replaced)
w.Event(progress.NewEvent(eventName, progress.Working, "Recreate"))
defer func() {
if err != nil && ctx.Err() == nil {
w.Event(progress.Event{
ID: eventName,
Status: progress.Error,
StatusText: err.Error(),
})
}
}()
number, err := strconv.Atoi(replaced.Labels[api.ContainerNumberLabel])
if err != nil {
@@ -646,13 +662,18 @@ func (s *composeService) recreateContainer(ctx context.Context, project *types.P
return created, err
}
w.Event(progress.NewEvent(getContainerProgressName(replaced), progress.Done, "Recreated"))
w.Event(progress.NewEvent(eventName, progress.Done, "Recreated"))
return created, err
}
// force sequential calls to ContainerStart to prevent race condition in engine assigning ports from ranges
var startMx sync.Mutex
func (s *composeService) startContainer(ctx context.Context, ctr containerType.Summary) error {
w := progress.ContextWriter(ctx)
w.Event(progress.NewEvent(getContainerProgressName(ctr), progress.Working, "Restart"))
startMx.Lock()
defer startMx.Unlock()
err := s.apiClient().ContainerStart(ctx, ctr.ID, containerType.StartOptions{})
if err != nil {
return err

View File

@@ -78,6 +78,10 @@ func (s *composeService) ToMobyHealthCheck(ctx context.Context, check *compose.H
} else {
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")
}
}
return &container.HealthConfig{
Test: test,

View File

@@ -22,16 +22,13 @@ import (
"encoding/json"
"errors"
"fmt"
"io/fs"
"os"
"path/filepath"
"sort"
"strconv"
"strings"
"github.com/compose-spec/compose-go/v2/paths"
"github.com/compose-spec/compose-go/v2/types"
"github.com/docker/compose/v2/internal/desktop"
pathutil "github.com/docker/compose/v2/internal/paths"
"github.com/docker/compose/v2/pkg/api"
"github.com/docker/compose/v2/pkg/progress"
"github.com/docker/compose/v2/pkg/prompt"
@@ -155,61 +152,10 @@ func (s *composeService) ensureProjectVolumes(ctx context.Context, project *type
ids[k] = id
}
err := func() error {
if s.manageDesktopFileSharesEnabled(ctx) {
// collect all the bind mount paths and try to set up file shares in
// Docker Desktop for them
var paths []string
for _, svcName := range project.ServiceNames() {
svc := project.Services[svcName]
for _, vol := range svc.Volumes {
if vol.Type != string(mount.TypeBind) {
continue
}
p := filepath.Clean(vol.Source)
if !filepath.IsAbs(p) {
return fmt.Errorf("file share path is not absolute: %s", p)
}
if fi, err := os.Stat(p); errors.Is(err, fs.ErrNotExist) {
// actual directory will be implicitly created when the
// file share is initialized if it doesn't exist, so
// need to filter out any that should not be auto-created
if vol.Bind != nil && !vol.Bind.CreateHostPath {
logrus.Debugf("Skipping creating file share for %q: does not exist and `create_host_path` is false", p)
continue
}
} else if err != nil {
// if we can't read the path, we won't be able to make
// a file share for it
logrus.Debugf("Skipping creating file share for %q: %v", p, err)
continue
} else if !fi.IsDir() {
// ignore files & special types (e.g. Unix sockets)
logrus.Debugf("Skipping creating file share for %q: not a directory", p)
continue
}
paths = append(paths, p)
}
}
// remove duplicate/unnecessary child paths and sort them for predictability
paths = pathutil.EncompassingPaths(paths)
sort.Strings(paths)
fileShareManager := desktop.NewFileShareManager(s.desktopCli, project.Name, paths)
if err := fileShareManager.EnsureExists(ctx); err != nil {
return fmt.Errorf("initializing file shares: %w", err)
}
}
return nil
}()
if err != nil {
progress.ContextWriter(ctx).TailMsgf("Failed to prepare Synchronized file shares: %v", err)
}
return ids, nil
}
//nolint:gocyclo
func (s *composeService) getCreateConfigs(ctx context.Context,
p *types.Project,
service types.ServiceConfig,
@@ -302,7 +248,10 @@ func (s *composeService) getCreateConfigs(ctx context.Context,
if err != nil {
return createConfigs{}, err
}
networkMode, networkingConfig := defaultNetworkSettings(p, service, number, links, opts.UseNetworkAliases, apiVersion)
networkMode, networkingConfig, err := defaultNetworkSettings(p, service, number, links, opts.UseNetworkAliases, apiVersion)
if err != nil {
return createConfigs{}, err
}
portBindings := buildContainerPortBindingOptions(service)
// MISC
@@ -412,7 +361,7 @@ func (s *composeService) prepareContainerMACAddress(ctx context.Context, service
}
if len(withMacAddress) > 1 {
return "", fmt.Errorf("a MAC address is specified for multiple networks (%s), but this feature requires Docker Engine 1.44 or later (currently: %s)", strings.Join(withMacAddress, ", "), version)
return "", fmt.Errorf("a MAC address is specified for multiple networks (%s), but this feature requires Docker Engine v25 or later", strings.Join(withMacAddress, ", "))
}
if mainNw != nil && mainNw.MacAddress != "" {
@@ -435,6 +384,8 @@ func getAliases(project *types.Project, service types.ServiceConfig, serviceInde
}
func createEndpointSettings(p *types.Project, service types.ServiceConfig, serviceIndex int, networkKey string, links []string, useNetworkAliases bool) *network.EndpointSettings {
const ifname = "com.docker.network.endpoint.ifname"
config := service.Networks[networkKey]
var ipam *network.EndpointIPAMConfig
var (
@@ -454,6 +405,15 @@ func createEndpointSettings(p *types.Project, service types.ServiceConfig, servi
}
macAddress = config.MacAddress
driverOpts = config.DriverOpts
if config.InterfaceName != "" {
if driverOpts == nil {
driverOpts = map[string]string{}
}
if name, ok := driverOpts[ifname]; ok && name != config.InterfaceName {
logrus.Warnf("ignoring services.%s.networks.%s.interface_name as %s driver_opts is already declared", service.Name, networkKey, ifname)
}
driverOpts[ifname] = config.InterfaceName
}
gwPriority = config.GatewayPriority
}
return &network.EndpointSettings{
@@ -527,20 +487,17 @@ func (s *composeService) prepareLabels(labels types.Labels, service types.Servic
}
// defaultNetworkSettings determines the container.NetworkMode and corresponding network.NetworkingConfig (nil if not applicable).
func defaultNetworkSettings(
project *types.Project,
service types.ServiceConfig,
serviceIndex int,
links []string,
useNetworkAliases bool,
func defaultNetworkSettings(project *types.Project,
service types.ServiceConfig, serviceIndex int,
links []string, useNetworkAliases bool,
version string,
) (container.NetworkMode, *network.NetworkingConfig) {
) (container.NetworkMode, *network.NetworkingConfig, error) {
if service.NetworkMode != "" {
return container.NetworkMode(service.NetworkMode), nil
return container.NetworkMode(service.NetworkMode), nil, nil
}
if len(project.Networks) == 0 {
return "none", nil
return "none", nil, nil
}
var primaryNetworkKey string
@@ -571,6 +528,14 @@ func defaultNetworkSettings(
}
}
if versions.LessThan(version, "1.49") {
for _, config := range service.Networks {
if config != nil && config.InterfaceName != "" {
return "", nil, fmt.Errorf("interface_name requires Docker Engine v28.1 or later")
}
}
}
endpointsConfig[primaryNetworkMobyNetworkName] = primaryNetworkEndpoint
networkConfig := &network.NetworkingConfig{
EndpointsConfig: endpointsConfig,
@@ -579,7 +544,7 @@ func defaultNetworkSettings(
// From the Engine API docs:
// > Supported standard values are: bridge, host, none, and container:<name|id>.
// > Any other value is taken as a custom network's name to which this container should connect to.
return container.NetworkMode(primaryNetworkMobyNetworkName), networkConfig
return container.NetworkMode(primaryNetworkMobyNetworkName), networkConfig, nil
}
func getRestartPolicy(service types.ServiceConfig) container.RestartPolicy {
@@ -1162,28 +1127,22 @@ func isUnixAbs(p string) bool {
}
func isWindowsAbs(p string) bool {
if strings.HasPrefix(p, "\\\\") {
return true
}
if len(p) > 2 && p[1] == ':' {
return p[2] == '\\'
}
return false
return paths.IsWindowsAbs(p)
}
func buildMount(project types.Project, volume types.ServiceVolumeConfig) (mount.Mount, error) {
source := volume.Source
// on windows, filepath.IsAbs(source) is false for unix style abs path like /var/run/docker.sock.
// do not replace these with filepath.Abs(source) that will include a default drive.
if volume.Type == types.VolumeTypeBind && !filepath.IsAbs(source) && !strings.HasPrefix(source, "/") {
// volume source has already been prefixed with workdir if required, by compose-go project loader
var err error
source, err = filepath.Abs(source)
if err != nil {
return mount.Mount{}, err
switch volume.Type {
case types.VolumeTypeBind:
if !filepath.IsAbs(source) && !isUnixAbs(source) && !isWindowsAbs(source) {
// volume source has already been prefixed with workdir if required, by compose-go project loader
var err error
source, err = filepath.Abs(source)
if err != nil {
return mount.Mount{}, err
}
}
}
if volume.Type == types.VolumeTypeVolume {
case types.VolumeTypeVolume:
if volume.Source != "" {
pVolume, ok := project.Volumes[volume.Source]
if ok {

View File

@@ -219,7 +219,8 @@ func TestDefaultNetworkSettings(t *testing.T) {
}),
}
networkMode, networkConfig := defaultNetworkSettings(&project, service, 1, nil, true, "1.43")
networkMode, networkConfig, err := defaultNetworkSettings(&project, service, 1, nil, true, "1.43")
assert.NilError(t, err)
assert.Equal(t, string(networkMode), "myProject_myNetwork2")
assert.Check(t, cmp.Len(networkConfig.EndpointsConfig, 1))
assert.Check(t, cmp.Contains(networkConfig.EndpointsConfig, "myProject_myNetwork2"))
@@ -247,7 +248,8 @@ func TestDefaultNetworkSettings(t *testing.T) {
}),
}
networkMode, networkConfig := defaultNetworkSettings(&project, service, 1, nil, true, "1.43")
networkMode, networkConfig, err := defaultNetworkSettings(&project, service, 1, nil, true, "1.43")
assert.NilError(t, err)
assert.Equal(t, string(networkMode), "myProject_default")
assert.Check(t, cmp.Len(networkConfig.EndpointsConfig, 1))
assert.Check(t, cmp.Contains(networkConfig.EndpointsConfig, "myProject_default"))
@@ -264,7 +266,8 @@ func TestDefaultNetworkSettings(t *testing.T) {
},
}
networkMode, networkConfig := defaultNetworkSettings(&project, service, 1, nil, true, "1.43")
networkMode, networkConfig, err := defaultNetworkSettings(&project, service, 1, nil, true, "1.43")
assert.NilError(t, err)
assert.Equal(t, string(networkMode), "none")
assert.Check(t, cmp.Nil(networkConfig))
})
@@ -284,7 +287,8 @@ func TestDefaultNetworkSettings(t *testing.T) {
}),
}
networkMode, networkConfig := defaultNetworkSettings(&project, service, 1, nil, true, "1.43")
networkMode, networkConfig, err := defaultNetworkSettings(&project, service, 1, nil, true, "1.43")
assert.NilError(t, err)
assert.Equal(t, string(networkMode), "host")
assert.Check(t, cmp.Nil(networkConfig))
})

View File

@@ -17,11 +17,8 @@
package compose
import (
"context"
"github.com/docker/compose/v2/internal/desktop"
"github.com/docker/compose/v2/internal/experimental"
"github.com/sirupsen/logrus"
)
func (s *composeService) SetDesktopClient(cli *desktop.Client) {
@@ -31,18 +28,3 @@ func (s *composeService) SetDesktopClient(cli *desktop.Client) {
func (s *composeService) SetExperiments(experiments *experimental.State) {
s.experiments = experiments
}
func (s *composeService) manageDesktopFileSharesEnabled(ctx context.Context) bool {
if !s.isDesktopIntegrationActive() {
return false
}
// synchronized file share support in Docker Desktop is dependent upon
// a variety of factors (settings, OS, etc), which this endpoint abstracts
fileSharesConfig, err := s.desktopCli.GetFileSharesConfig(ctx)
if err != nil {
logrus.Debugf("Failed to retrieve file shares config: %v", err)
return false
}
return fileSharesConfig.Active && fileSharesConfig.Compose.ManageBindMounts
}

View File

@@ -23,7 +23,6 @@ import (
"time"
"github.com/compose-spec/compose-go/v2/types"
"github.com/docker/compose/v2/internal/desktop"
"github.com/docker/compose/v2/pkg/api"
"github.com/docker/compose/v2/pkg/progress"
"github.com/docker/compose/v2/pkg/utils"
@@ -157,13 +156,6 @@ func (s *composeService) ensureVolumesDown(ctx context.Context, project *types.P
})
}
if s.manageDesktopFileSharesEnabled(ctx) {
ops = append(ops, func() error {
desktop.RemoveFileSharesForProject(ctx, s.desktopCli, project.Name)
return nil
})
}
return ops
}

View File

@@ -30,10 +30,10 @@ import (
"github.com/docker/cli/cli-plugins/manager"
"github.com/docker/cli/cli-plugins/socket"
"github.com/docker/compose/v2/pkg/progress"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/propagation"
"golang.org/x/sync/errgroup"
)
type JsonMessage struct {
@@ -45,6 +45,7 @@ const (
ErrorType = "error"
InfoType = "info"
SetEnvType = "setenv"
DebugType = "debug"
)
func (s *composeService) runPlugin(ctx context.Context, project *types.Project, service types.ServiceConfig, command string) error {
@@ -55,67 +56,16 @@ func (s *composeService) runPlugin(ctx context.Context, project *types.Project,
return err
}
if err := s.checkPluginEnabledInDD(ctx, plugin); err != nil {
return err
}
cmd := s.setupPluginCommand(ctx, project, service, plugin, command)
cmd := s.setupPluginCommand(ctx, project, provider, plugin.Path, command)
eg := errgroup.Group{}
stdout, err := cmd.StdoutPipe()
variables, err := s.executePlugin(ctx, cmd, command, service)
if err != nil {
return err
}
err = cmd.Start()
if err != nil {
return err
}
eg.Go(cmd.Wait)
decoder := json.NewDecoder(stdout)
defer func() { _ = stdout.Close() }()
variables := types.Mapping{}
pw := progress.ContextWriter(ctx)
pw.Event(progress.CreatingEvent(service.Name))
for {
var msg JsonMessage
err = decoder.Decode(&msg)
if errors.Is(err, io.EOF) {
break
}
if err != nil {
return err
}
switch msg.Type {
case ErrorType:
pw.Event(progress.ErrorMessageEvent(service.Name, "error"))
return errors.New(msg.Message)
case InfoType:
pw.Event(progress.ErrorMessageEvent(service.Name, msg.Message))
case SetEnvType:
key, val, found := strings.Cut(msg.Message, "=")
if !found {
return fmt.Errorf("invalid response from plugin: %s", msg.Message)
}
variables[key] = val
default:
return fmt.Errorf("invalid response from plugin: %s", msg.Type)
}
}
err = eg.Wait()
if err != nil {
pw.Event(progress.ErrorMessageEvent(service.Name, err.Error()))
return fmt.Errorf("failed to create external service: %s", err.Error())
}
pw.Event(progress.CreatedEvent(service.Name))
prefix := strings.ToUpper(service.Name) + "_"
for name, s := range project.Services {
if _, ok := s.DependsOn[service.Name]; ok {
prefix := strings.ToUpper(service.Name) + "_"
for key, val := range variables {
s.Environment[prefix+key] = &val
}
@@ -125,20 +75,112 @@ func (s *composeService) runPlugin(ctx context.Context, project *types.Project,
return nil
}
func (s *composeService) getPluginBinaryPath(providerType string) (*manager.Plugin, error) {
// Only support Docker CLI plugins for first iteration. Could support any binary from PATH
return manager.GetPlugin(providerType, s.dockerCli, &cobra.Command{})
}
func (s *composeService) setupPluginCommand(ctx context.Context, project *types.Project, provider types.ServiceProviderConfig, path, command string) *exec.Cmd {
args := []string{"compose", "--project-name", project.Name, command}
for k, v := range provider.Options {
args = append(args, fmt.Sprintf("--%s=%s", k, v))
func (s *composeService) executePlugin(ctx context.Context, cmd *exec.Cmd, command string, service types.ServiceConfig) (types.Mapping, error) {
pw := progress.ContextWriter(ctx)
var action string
switch command {
case "up":
pw.Event(progress.CreatingEvent(service.Name))
action = "create"
case "down":
pw.Event(progress.RemovingEvent(service.Name))
action = "remove"
default:
return nil, fmt.Errorf("unsupported plugin command: %s", command)
}
stdout, err := cmd.StdoutPipe()
if err != nil {
return nil, err
}
err = cmd.Start()
if err != nil {
return nil, err
}
decoder := json.NewDecoder(stdout)
defer func() { _ = stdout.Close() }()
variables := types.Mapping{}
for {
var msg JsonMessage
err = decoder.Decode(&msg)
if errors.Is(err, io.EOF) {
break
}
if err != nil {
return nil, err
}
switch msg.Type {
case ErrorType:
pw.Event(progress.NewEvent(service.Name, progress.Error, msg.Message))
return nil, errors.New(msg.Message)
case InfoType:
pw.Event(progress.NewEvent(service.Name, progress.Working, msg.Message))
case SetEnvType:
key, val, found := strings.Cut(msg.Message, "=")
if !found {
return nil, fmt.Errorf("invalid response from plugin: %s", msg.Message)
}
variables[key] = val
case DebugType:
logrus.Debugf("%s: %s", service.Name, msg.Message)
default:
return nil, fmt.Errorf("invalid response from plugin: %s", msg.Type)
}
}
err = cmd.Wait()
if err != nil {
pw.Event(progress.ErrorMessageEvent(service.Name, err.Error()))
return nil, fmt.Errorf("failed to %s service provider: %s", action, err.Error())
}
switch command {
case "up":
pw.Event(progress.CreatedEvent(service.Name))
case "down":
pw.Event(progress.RemovedEvent(service.Name))
}
return variables, nil
}
func (s *composeService) getPluginBinaryPath(provider string) (path string, err error) {
if provider == "compose" {
return "", errors.New("'compose' is not a valid provider type")
}
plugin, err := manager.GetPlugin(provider, s.dockerCli, &cobra.Command{})
if err == nil {
path = plugin.Path
}
if manager.IsNotFound(err) {
path, err = exec.LookPath(executable(provider))
}
return path, err
}
func (s *composeService) setupPluginCommand(ctx context.Context, project *types.Project, service types.ServiceConfig, path, command string) *exec.Cmd {
provider := *service.Provider
args := []string{"compose", "--project-name", project.Name, command}
for k, v := range provider.Options {
for _, value := range v {
args = append(args, fmt.Sprintf("--%s=%s", k, value))
}
}
args = append(args, service.Name)
cmd := exec.CommandContext(ctx, path, args...)
// Remove DOCKER_CLI_PLUGIN... variable so plugin can detect it run standalone
cmd.Env = filter(os.Environ(), manager.ReexecEnvvar)
// exec provider command with same environment Compose is running
env := types.NewMapping(os.Environ())
// but remove DOCKER_CLI_PLUGIN... variable so plugin can detect it run standalone
delete(env, manager.ReexecEnvvar)
// and add the explicit environment variables set for service
for key, val := range service.Environment.RemoveEmpty().ToMapping() {
env[key] = val
}
cmd.Env = env.Values()
// Use docker/cli mechanism to propagate termination signal to child process
server, err := socket.NewPluginServer(nil)
@@ -156,24 +198,3 @@ func (s *composeService) setupPluginCommand(ctx context.Context, project *types.
cmd.Env = append(cmd.Env, types.Mapping(carrier).Values()...)
return cmd
}
func (s *composeService) checkPluginEnabledInDD(ctx context.Context, plugin *manager.Plugin) error {
if integrationEnabled := s.isDesktopIntegrationActive(); !integrationEnabled {
return fmt.Errorf("you should enable Docker Desktop integration to use %q provider services", plugin.Name)
}
// Until we support more use cases, check explicitly status of model runner
if plugin.Name == "model" {
cmd := exec.CommandContext(ctx, "docker", "model", "status")
_, err := cmd.CombinedOutput()
if err != nil {
var exitErr *exec.ExitError
if errors.As(err, &exitErr) && exitErr.ExitCode() == 1 {
return fmt.Errorf("you should enable model runner to use %q provider services: %s", plugin.Name, err.Error())
}
}
} else {
return fmt.Errorf("unsupported provider %q", plugin.Name)
}
return nil
}

View File

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

View File

@@ -24,6 +24,7 @@ import (
"fmt"
"io"
"strings"
"sync"
"time"
"github.com/compose-spec/compose-go/v2/types"
@@ -203,6 +204,15 @@ func (s *composeService) pullServiceImage(ctx context.Context, service types.Ser
Platform: platform,
})
if ctx.Err() != nil {
w.Event(progress.Event{
ID: service.Name,
Status: progress.Warning,
StatusText: "Interrupted",
})
return "", nil
}
// check if has error and the service has a build section
// then the status should be warning instead of error
if err != nil && service.Build != nil {
@@ -322,9 +332,12 @@ func (s *composeService) pullRequiredImages(ctx context.Context, project *types.
eg, ctx := errgroup.WithContext(ctx)
eg.SetLimit(s.maxConcurrency)
pulledImages := map[string]api.ImageSummary{}
var mutex sync.Mutex
for name, service := range needPull {
eg.Go(func() error {
id, err := s.pullServiceImage(ctx, service, s.configFile(), w, quietPull, project.Environment["DOCKER_DEFAULT_PLATFORM"])
mutex.Lock()
defer mutex.Unlock()
pulledImages[name] = api.ImageSummary{
ID: id,
Repository: service.Image,

View File

@@ -62,7 +62,7 @@ func (s *composeService) push(ctx context.Context, project *types.Project, optio
w := progress.ContextWriter(ctx)
for _, service := range project.Services {
if service.Build == nil || service.Image == "" {
if options.ImageMandatory && service.Image == "" {
if options.ImageMandatory && service.Image == "" && service.Provider == nil {
return fmt.Errorf("%q attribute is mandatory to push an image for service %q", "service.image", service.Name)
}
w.Event(progress.Event{

View File

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

View File

@@ -117,17 +117,22 @@ func TestLocalComposeBuild(t *testing.T) {
})
t.Run(env+" rebuild when up --build", func(t *testing.T) {
res := c.RunDockerComposeCmd(t, "--workdir", "fixtures/build-test", "up", "-d", "--build")
res := c.RunDockerComposeCmd(t, "--project-directory", "fixtures/build-test", "up", "-d", "--build")
res.Assert(t, icmd.Expected{Out: "COPY static /usr/share/nginx/html"})
res.Assert(t, icmd.Expected{Out: "COPY static2 /usr/share/nginx/html"})
})
t.Run(env+" build --push ignored for unnamed images", func(t *testing.T) {
res := c.RunDockerComposeCmd(t, "--workdir", "fixtures/build-test", "build", "--push", "nginx")
res := c.RunDockerComposeCmd(t, "--project-directory", "fixtures/build-test", "build", "--push", "nginx")
assert.Assert(t, !strings.Contains(res.Stdout(), "failed to push"), res.Stdout())
})
t.Run(env+" build --quiet", func(t *testing.T) {
res := c.RunDockerComposeCmd(t, "--project-directory", "fixtures/build-test", "build", "--quiet")
res.Assert(t, icmd.Expected{Out: ""})
})
t.Run(env+" cleanup build project", func(t *testing.T) {
c.RunDockerComposeCmd(t, "--project-directory", "fixtures/build-test", "down")
c.RunDockerOrExitError(t, "rmi", "-f", "build-test-nginx")
@@ -232,7 +237,7 @@ func TestBuildTags(t *testing.T) {
}
func TestBuildImageDependencies(t *testing.T) {
doTest := func(t *testing.T, cli *CLI) {
doTest := func(t *testing.T, cli *CLI, args ...string) {
resetState := func() {
cli.RunDockerComposeCmd(t, "down", "--rmi=all", "-t=0")
res := cli.RunDockerOrExitError(t, "image", "rm", "build-dependencies-service")
@@ -250,7 +255,7 @@ func TestBuildImageDependencies(t *testing.T) {
Err: "No such image: build-dependencies-service",
})
res = cli.RunDockerComposeCmd(t, "build")
res = cli.RunDockerComposeCmd(t, args...)
t.Log(res.Combined())
res = cli.RunDockerCmd(t,
@@ -273,7 +278,8 @@ func TestBuildImageDependencies(t *testing.T) {
"DOCKER_BUILDKIT=0",
"COMPOSE_FILE=./fixtures/build-dependencies/classic.yaml",
))
doTest(t, cli)
doTest(t, cli, "build")
doTest(t, cli, "build", "--with-dependencies", "service")
})
t.Run("BuildKit by dependency order", func(t *testing.T) {
@@ -281,7 +287,8 @@ func TestBuildImageDependencies(t *testing.T) {
"DOCKER_BUILDKIT=1",
"COMPOSE_FILE=./fixtures/build-dependencies/classic.yaml",
))
doTest(t, cli)
doTest(t, cli, "build")
doTest(t, cli, "build", "--with-dependencies", "service")
})
t.Run("BuildKit by additional contexts", func(t *testing.T) {
@@ -289,7 +296,9 @@ func TestBuildImageDependencies(t *testing.T) {
"DOCKER_BUILDKIT=1",
"COMPOSE_FILE=./fixtures/build-dependencies/compose.yaml",
))
doTest(t, cli)
doTest(t, cli, "build")
doTest(t, cli, "build", "service")
doTest(t, cli, "up", "--build", "service")
})
t.Run("Bake by additional contexts", func(t *testing.T) {
@@ -297,7 +306,9 @@ func TestBuildImageDependencies(t *testing.T) {
"DOCKER_BUILDKIT=1", "COMPOSE_BAKE=1",
"COMPOSE_FILE=./fixtures/build-dependencies/compose.yaml",
))
doTest(t, cli)
doTest(t, cli, "--verbose", "build")
doTest(t, cli, "--verbose", "build", "service")
doTest(t, cli, "--verbose", "up", "--build", "service")
})
}

View File

@@ -170,6 +170,18 @@ func TestLocalComposeRun(t *testing.T) {
assert.Assert(t, strings.Contains(res.Combined(), "Pulled"), res.Combined())
})
t.Run("COMPOSE_PROGRESS quiet", func(t *testing.T) {
res := c.RunDockerComposeCmd(t, "-f", "./fixtures/run-test/quiet-pull.yaml", "down", "--remove-orphans", "--rmi", "all")
res.Assert(t, icmd.Success)
cmd := c.NewDockerComposeCmd(t, "-f", "./fixtures/run-test/quiet-pull.yaml", "run", "backend")
res = icmd.RunCmd(cmd, func(c *icmd.Cmd) {
c.Env = append(c.Env, "COMPOSE_PROGRESS=quiet")
})
assert.Assert(t, !strings.Contains(res.Combined(), "Pull complete"), res.Combined())
assert.Assert(t, !strings.Contains(res.Combined(), "Pulled"), res.Combined())
})
t.Run("--pull", func(t *testing.T) {
res := c.RunDockerComposeCmd(t, "-f", "./fixtures/run-test/pull.yaml", "down", "--remove-orphans", "--rmi", "all")
res.Assert(t, icmd.Success)

View File

@@ -235,14 +235,14 @@ func TestCompatibility(t *testing.T) {
}
func TestConfig(t *testing.T) {
const projectName = "compose-e2e-convert"
const projectName = "compose-e2e-config"
c := NewParallelCLI(t)
wd, err := os.Getwd()
assert.NilError(t, err)
t.Run("up", func(t *testing.T) {
res := c.RunDockerComposeCmd(t, "-f", "./fixtures/simple-build-test/compose.yaml", "-p", projectName, "convert")
res := c.RunDockerComposeCmd(t, "-f", "./fixtures/simple-build-test/compose.yaml", "-p", projectName, "config")
res.Assert(t, icmd.Expected{Out: fmt.Sprintf(`name: %s
services:
nginx:
@@ -253,24 +253,24 @@ services:
default: null
networks:
default:
name: compose-e2e-convert_default
name: compose-e2e-config_default
`, projectName, filepath.Join(wd, "fixtures", "simple-build-test", "nginx-build")), ExitCode: 0})
})
}
func TestConfigInterpolate(t *testing.T) {
const projectName = "compose-e2e-convert-interpolate"
const projectName = "compose-e2e-config-interpolate"
c := NewParallelCLI(t)
wd, err := os.Getwd()
assert.NilError(t, err)
t.Run("convert", func(t *testing.T) {
res := c.RunDockerComposeCmd(t, "-f", "./fixtures/simple-build-test/compose-interpolate.yaml", "-p", projectName, "convert", "--no-interpolate")
t.Run("config", func(t *testing.T) {
res := c.RunDockerComposeCmd(t, "-f", "./fixtures/simple-build-test/compose-interpolate.yaml", "-p", projectName, "config", "--no-interpolate")
res.Assert(t, icmd.Expected{Out: fmt.Sprintf(`name: %s
networks:
default:
name: compose-e2e-convert-interpolate_default
name: compose-e2e-config-interpolate_default
services:
nginx:
build:

View File

@@ -46,4 +46,25 @@ func TestLocalComposeConfig(t *testing.T) {
res := c.RunDockerComposeCmd(t, "-f", "./fixtures/config/compose.yaml", "--project-name", projectName, "config", "--no-interpolate")
res.Assert(t, icmd.Expected{Out: `- ${PORT:-8080}:80`})
})
t.Run("--variables --format json", func(t *testing.T) {
res := c.RunDockerComposeCmd(t, "-f", "./fixtures/config/compose.yaml", "--project-name", projectName, "config", "--variables", "--format", "json")
res.Assert(t, icmd.Expected{Out: `{
"PORT": {
"Name": "PORT",
"DefaultValue": "8080",
"PresenceValue": "",
"Required": false
}
}`})
})
t.Run("--variables --format yaml", func(t *testing.T) {
res := c.RunDockerComposeCmd(t, "-f", "./fixtures/config/compose.yaml", "--project-name", projectName, "config", "--variables", "--format", "yaml")
res.Assert(t, icmd.Expected{Out: `PORT:
name: PORT
defaultvalue: "8080"
presencevalue: ""
required: false`})
})
}

View File

@@ -0,0 +1,7 @@
services:
test:
image: alpine
command: ip link show
networks:
default:
interface_name: foobar

View File

@@ -3,4 +3,14 @@ services:
image: nginx:alpine
scale: 5
ports:
- "6005-6015:80"
- "6005-6015:80"
b:
image: nginx:alpine
ports:
- 80
c:
image: nginx:alpine
ports:
- 80

View File

@@ -0,0 +1,15 @@
services:
test:
image: "nginx"
healthcheck:
interval: 30s
start_period: 10s
start_interval: 1s
test: "/bin/true"
error:
image: "nginx"
healthcheck:
interval: 30s
start_interval: 1s
test: "/bin/true"

View File

@@ -0,0 +1,54 @@
/*
Copyright 2023 Docker Compose CLI authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package e2e
import (
"strings"
"testing"
"time"
"gotest.tools/v3/assert"
"gotest.tools/v3/icmd"
)
func TestStartInterval(t *testing.T) {
c := NewParallelCLI(t)
const projectName = "e2e-start-interval"
t.Cleanup(func() {
c.cleanupWithDown(t, projectName)
})
res := c.RunDockerComposeCmdNoCheck(t, "-f", "fixtures/start_interval/compose.yaml", "--project-name", projectName, "up", "--wait", "-d", "error")
res.Assert(t, icmd.Expected{ExitCode: 1, Err: "healthcheck.start_interval requires healthcheck.start_period to be set"})
timeout := time.After(30 * time.Second)
done := make(chan bool)
go func() {
res := c.RunDockerComposeCmd(t, "-f", "fixtures/start_interval/compose.yaml", "--project-name", projectName, "up", "--wait", "-d", "test")
out := res.Combined()
assert.Assert(t, strings.Contains(out, "Healthy"), out)
done <- true
}()
select {
case <-timeout:
t.Fatal("test did not finish in time")
case <-done:
break
}
}

View File

@@ -181,3 +181,21 @@ func TestMacAddress(t *testing.T) {
res := c.RunDockerCmd(t, "inspect", fmt.Sprintf("%s-test-1", projectName), "-f", "{{ (index .NetworkSettings.Networks \"network_mac_address_default\" ).MacAddress }}")
res.Assert(t, icmd.Expected{Out: "00:e0:84:35:d0:e8"})
}
func TestInterfaceName(t *testing.T) {
c := NewCLI(t)
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")
}
const projectName = "network_interface_name"
res := c.RunDockerComposeCmd(t, "-f", "./fixtures/network-interface-name/compose.yaml", "--project-name", projectName, "run", "test")
t.Cleanup(func() {
c.cleanupWithDown(t, projectName)
})
res.Assert(t, icmd.Expected{Out: "foobar@"})
}