mirror of
https://github.com/docker/compose.git
synced 2026-02-10 10:39:23 +08:00
Compare commits
55 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e45e58b3ec | ||
|
|
f52af4c868 | ||
|
|
a54814ff39 | ||
|
|
a2d7548ca9 | ||
|
|
8a2cb90a39 | ||
|
|
cc50ada725 | ||
|
|
5c74f07991 | ||
|
|
7e198ee6a3 | ||
|
|
0566431c64 | ||
|
|
4f6cc2a330 | ||
|
|
2352a4a016 | ||
|
|
1f076a3781 | ||
|
|
009a239510 | ||
|
|
3059574288 | ||
|
|
1229a69384 | ||
|
|
f2a88e02a0 | ||
|
|
7f9101845d | ||
|
|
944e5e67a1 | ||
|
|
23fc76a540 | ||
|
|
053d225824 | ||
|
|
93b597ccec | ||
|
|
4dcaf94c32 | ||
|
|
07e7619f4e | ||
|
|
ed81185c5c | ||
|
|
22f8a7009f | ||
|
|
91a0aa0265 | ||
|
|
7cea455c4d | ||
|
|
559a51e590 | ||
|
|
480a556bf0 | ||
|
|
6263361190 | ||
|
|
9ee03c3fec | ||
|
|
4bf18d2325 | ||
|
|
f0f47a8aa8 | ||
|
|
d6e3fa6d74 | ||
|
|
16e83f002d | ||
|
|
2dbef234dc | ||
|
|
20f0ffec0b | ||
|
|
cee6a3c660 | ||
|
|
fc8c56b407 | ||
|
|
9c998a934f | ||
|
|
0403f0d76d | ||
|
|
91d04a5ca9 | ||
|
|
d2274ebe6c | ||
|
|
6e35652182 | ||
|
|
5bb46035cf | ||
|
|
f8dae06df8 | ||
|
|
955e4ed94e | ||
|
|
60385e6065 | ||
|
|
f5491328bb | ||
|
|
f46689a75e | ||
|
|
8fd0c297f5 | ||
|
|
f3bbfdae58 | ||
|
|
322c531a8c | ||
|
|
bf6b447263 | ||
|
|
a96c305b25 |
3
.github/workflows/ci.yml
vendored
3
.github/workflows/ci.yml
vendored
@@ -306,9 +306,6 @@ jobs:
|
||||
echo "$sum $file" > ${file#\*}.sha256
|
||||
fi
|
||||
done
|
||||
-
|
||||
name: License
|
||||
run: cp packaging/* ./bin/release/
|
||||
-
|
||||
name: List artifacts
|
||||
run: |
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -260,6 +260,7 @@ func runUp(
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
bo.Services = services
|
||||
build = &bo
|
||||
}
|
||||
|
||||
|
||||
74
docs/examples/provider.go
Normal file
74
docs/examples/provider.go
Normal 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
111
docs/extension.md
Normal 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
|
||||
@@ -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 |
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
90
go.mod
@@ -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
206
go.sum
@@ -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=
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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))
|
||||
})
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
23
pkg/compose/plugins_windows.go
Normal file
23
pkg/compose/plugins_windows.go
Normal 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"
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
@@ -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{
|
||||
|
||||
23
pkg/compose/suffix_unix.go
Normal file
23
pkg/compose/suffix_unix.go
Normal 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
|
||||
}
|
||||
@@ -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")
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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`})
|
||||
})
|
||||
}
|
||||
|
||||
7
pkg/e2e/fixtures/network-interface-name/compose.yaml
Normal file
7
pkg/e2e/fixtures/network-interface-name/compose.yaml
Normal file
@@ -0,0 +1,7 @@
|
||||
services:
|
||||
test:
|
||||
image: alpine
|
||||
command: ip link show
|
||||
networks:
|
||||
default:
|
||||
interface_name: foobar
|
||||
@@ -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
|
||||
15
pkg/e2e/fixtures/start_interval/compose.yaml
Normal file
15
pkg/e2e/fixtures/start_interval/compose.yaml
Normal 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"
|
||||
54
pkg/e2e/healthcheck_test.go
Normal file
54
pkg/e2e/healthcheck_test.go
Normal 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
|
||||
}
|
||||
}
|
||||
@@ -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@"})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user