mirror of
https://github.com/docker/compose.git
synced 2026-02-11 11:09:23 +08:00
Compare commits
2 Commits
v2.20.1
...
multiplaye
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f06caeb844 | ||
|
|
49d1bc7524 |
@@ -15,7 +15,7 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
ARG GO_VERSION=1.20.6
|
||||
ARG GO_VERSION=1.20.5
|
||||
ARG XX_VERSION=1.2.1
|
||||
ARG GOLANGCI_LINT_VERSION=v1.53.2
|
||||
ARG ADDLICENSE_VERSION=v1.0.0
|
||||
@@ -91,7 +91,6 @@ FROM build-base AS lint
|
||||
ARG BUILD_TAGS
|
||||
RUN --mount=type=bind,target=. \
|
||||
--mount=type=cache,target=/root/.cache \
|
||||
--mount=type=cache,target=/go/pkg/mod \
|
||||
--mount=from=golangci-lint,source=/usr/bin/golangci-lint,target=/usr/bin/golangci-lint \
|
||||
golangci-lint run --build-tags "$BUILD_TAGS" ./...
|
||||
|
||||
@@ -130,7 +129,6 @@ FROM base AS docsgen
|
||||
WORKDIR /src
|
||||
RUN --mount=target=. \
|
||||
--mount=target=/root/.cache,type=cache \
|
||||
--mount=type=cache,target=/go/pkg/mod \
|
||||
go build -o /out/docsgen ./docs/yaml/main/generate.go
|
||||
|
||||
FROM --platform=${BUILDPLATFORM} alpine AS docs-build
|
||||
|
||||
@@ -26,7 +26,6 @@ import (
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"github.com/compose-spec/compose-go/dotenv"
|
||||
buildx "github.com/docker/buildx/util/progress"
|
||||
"github.com/docker/cli/cli/command"
|
||||
|
||||
@@ -428,6 +427,7 @@ func RootCommand(streams command.Cli, backend api.Service) *cobra.Command { //no
|
||||
imagesCommand(&opts, streams, backend),
|
||||
versionCommand(streams),
|
||||
buildCommand(&opts, &progress, backend),
|
||||
publishCommand(&opts, backend),
|
||||
pushCommand(&opts, backend),
|
||||
pullCommand(&opts, backend),
|
||||
createCommand(&opts, backend),
|
||||
@@ -473,7 +473,7 @@ func setEnvWithDotEnv(prjOpts *ProjectOptions) error {
|
||||
return err
|
||||
}
|
||||
|
||||
envFromFile, err := dotenv.GetEnvFromFile(composegoutils.GetAsEqualsMap(os.Environ()), workingDir, options.EnvFiles)
|
||||
envFromFile, err := cli.GetEnvFromFile(composegoutils.GetAsEqualsMap(os.Environ()), workingDir, options.EnvFiles)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -64,10 +64,10 @@ func copyCommand(p *ProjectOptions, backend api.Service) *cobra.Command {
|
||||
}
|
||||
|
||||
flags := copyCmd.Flags()
|
||||
flags.IntVar(&opts.index, "index", 0, "index of the container if service has multiple replicas")
|
||||
flags.BoolVar(&opts.all, "all", false, "copy to all the containers of the service.")
|
||||
flags.IntVar(&opts.index, "index", 0, "Index of the container if there are multiple instances of a service .")
|
||||
flags.BoolVar(&opts.all, "all", false, "Copy to all the containers of the service.")
|
||||
flags.MarkHidden("all") //nolint:errcheck
|
||||
flags.MarkDeprecated("all", "by default all the containers of the service will get the source file/directory to be copied.") //nolint:errcheck
|
||||
flags.MarkDeprecated("all", "By default all the containers of the service will get the source file/directory to be copied.") //nolint:errcheck
|
||||
flags.BoolVarP(&opts.followLink, "follow-link", "L", false, "Always follow symbol link in SRC_PATH")
|
||||
flags.BoolVarP(&opts.copyUIDGID, "archive", "a", false, "Archive mode (copy all uid/gid information)")
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@ func execCommand(p *ProjectOptions, streams api.Streams, backend api.Service) *c
|
||||
|
||||
runCmd.Flags().BoolVarP(&opts.detach, "detach", "d", false, "Detached mode: Run command in the background.")
|
||||
runCmd.Flags().StringArrayVarP(&opts.environment, "env", "e", []string{}, "Set environment variables")
|
||||
runCmd.Flags().IntVar(&opts.index, "index", 0, "index of the container if service has multiple replicas")
|
||||
runCmd.Flags().IntVar(&opts.index, "index", 1, "index of the container if there are multiple instances of a service [default: 1].")
|
||||
runCmd.Flags().BoolVarP(&opts.privileged, "privileged", "", false, "Give extended privileges to the process.")
|
||||
runCmd.Flags().StringVarP(&opts.user, "user", "u", "", "Run the command as this user.")
|
||||
runCmd.Flags().BoolVarP(&opts.noTty, "no-TTY", "T", !streams.Out().IsTerminal(), "Disable pseudo-TTY allocation. By default `docker compose exec` allocates a TTY.")
|
||||
|
||||
@@ -57,7 +57,7 @@ func portCommand(p *ProjectOptions, streams api.Streams, backend api.Service) *c
|
||||
ValidArgsFunction: completeServiceNames(p),
|
||||
}
|
||||
cmd.Flags().StringVar(&opts.protocol, "protocol", "tcp", "tcp or udp")
|
||||
cmd.Flags().IntVar(&opts.index, "index", 0, "index of the container if service has multiple replicas")
|
||||
cmd.Flags().IntVar(&opts.index, "index", 1, "index of the container if service has multiple replicas")
|
||||
return cmd
|
||||
}
|
||||
|
||||
|
||||
55
cmd/compose/publish.go
Normal file
55
cmd/compose/publish.go
Normal file
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
Copyright 2020 Docker Compose CLI authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package compose
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
)
|
||||
|
||||
type publishOptions struct {
|
||||
*ProjectOptions
|
||||
composeOptions
|
||||
Repository string
|
||||
}
|
||||
|
||||
func publishCommand(p *ProjectOptions, backend api.Service) *cobra.Command {
|
||||
opts := pushOptions{
|
||||
ProjectOptions: p,
|
||||
}
|
||||
publishCmd := &cobra.Command{
|
||||
Use: "publish [OPTIONS] [REPOSITORY]",
|
||||
Short: "Publish compose application",
|
||||
RunE: Adapt(func(ctx context.Context, args []string) error {
|
||||
return runPublish(ctx, backend, opts, args[0])
|
||||
}),
|
||||
Args: cobra.ExactArgs(1),
|
||||
}
|
||||
return publishCmd
|
||||
}
|
||||
|
||||
func runPublish(ctx context.Context, backend api.Service, opts pushOptions, repository string) error {
|
||||
project, err := opts.ToProject(nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return backend.Publish(ctx, project, repository)
|
||||
}
|
||||
@@ -31,6 +31,7 @@ type pushOptions struct {
|
||||
IncludeDeps bool
|
||||
Ignorefailures bool
|
||||
Quiet bool
|
||||
Repository string
|
||||
}
|
||||
|
||||
func pushCommand(p *ProjectOptions, backend api.Service) *cobra.Command {
|
||||
@@ -48,6 +49,7 @@ func pushCommand(p *ProjectOptions, backend api.Service) *cobra.Command {
|
||||
pushCmd.Flags().BoolVar(&opts.Ignorefailures, "ignore-push-failures", false, "Push what it can and ignores images with push failures")
|
||||
pushCmd.Flags().BoolVar(&opts.IncludeDeps, "include-deps", false, "Also push images of services declared as dependencies")
|
||||
pushCmd.Flags().BoolVarP(&opts.Quiet, "quiet", "q", false, "Push without printing progress information")
|
||||
pushCmd.Flags().StringVarP(&opts.Repository, "repository", "r", "", "Also publish the compose application in repository")
|
||||
|
||||
return pushCmd
|
||||
}
|
||||
@@ -68,5 +70,6 @@ func runPush(ctx context.Context, backend api.Service, opts pushOptions, service
|
||||
return backend.Push(ctx, project, api.PushOptions{
|
||||
IgnoreFailures: opts.Ignorefailures,
|
||||
Quiet: opts.Quiet,
|
||||
Repository: opts.Repository,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -63,13 +63,6 @@ type runOptions struct {
|
||||
}
|
||||
|
||||
func (options runOptions) apply(project *types.Project) error {
|
||||
if options.noDeps {
|
||||
err := project.ForServices([]string{options.Service}, types.IgnoreDependencies)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
target, err := project.GetService(options.Service)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -100,6 +93,13 @@ func (options runOptions) apply(project *types.Project) error {
|
||||
}
|
||||
}
|
||||
|
||||
if options.noDeps {
|
||||
err := project.ForServices([]string{options.Service}, types.IgnoreDependencies)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for i, s := range project.Services {
|
||||
if s.Name == options.Service {
|
||||
project.Services[i] = target
|
||||
|
||||
@@ -21,8 +21,9 @@ import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/compose-spec/compose-go/types"
|
||||
"github.com/docker/compose/v2/cmd/formatter"
|
||||
|
||||
"github.com/compose-spec/compose-go/types"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
@@ -163,31 +164,17 @@ func runUp(ctx context.Context, streams api.Streams, backend api.Service, create
|
||||
consumer = formatter.NewLogConsumer(ctx, streams.Out(), streams.Err(), !upOptions.noColor, !upOptions.noPrefix, upOptions.timestamp)
|
||||
}
|
||||
|
||||
attachTo := utils.Set[string]{}
|
||||
attachTo := services
|
||||
if len(upOptions.attach) > 0 {
|
||||
attachTo.AddAll(upOptions.attach...)
|
||||
attachTo = upOptions.attach
|
||||
}
|
||||
if upOptions.attachDependencies {
|
||||
if err := project.WithServices(attachTo.Elements(), func(s types.ServiceConfig) error {
|
||||
if s.Attach == nil || *s.Attach {
|
||||
attachTo.Add(s.Name)
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
attachTo = project.ServiceNames()
|
||||
}
|
||||
if len(attachTo) == 0 {
|
||||
if err := project.WithServices(services, func(s types.ServiceConfig) error {
|
||||
if s.Attach == nil || *s.Attach {
|
||||
attachTo.Add(s.Name)
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
attachTo = project.ServiceNames()
|
||||
}
|
||||
attachTo.RemoveAll(upOptions.noAttach...)
|
||||
attachTo = utils.Remove(attachTo, upOptions.noAttach...)
|
||||
|
||||
create := api.CreateOptions{
|
||||
Services: services,
|
||||
@@ -211,7 +198,7 @@ func runUp(ctx context.Context, streams api.Streams, backend api.Service, create
|
||||
Start: api.StartOptions{
|
||||
Project: project,
|
||||
Attach: consumer,
|
||||
AttachTo: attachTo.Elements(),
|
||||
AttachTo: attachTo,
|
||||
ExitCodeFrom: upOptions.exitCodeFrom,
|
||||
CascadeStop: upOptions.cascadeStop,
|
||||
Wait: upOptions.wait,
|
||||
|
||||
@@ -7,6 +7,7 @@ Define and run multi-container applications with Docker.
|
||||
|
||||
| Name | Description |
|
||||
|:--------------------------------|:------------------------------------------------------------------------|
|
||||
| [`alpha`](compose_alpha.md) | Experimental commands |
|
||||
| [`build`](compose_build.md) | Build or rebuild services |
|
||||
| [`config`](compose_config.md) | Parse, resolve and render compose file in canonical format |
|
||||
| [`cp`](compose_cp.md) | Copy files/folders between a service container and the local filesystem |
|
||||
|
||||
@@ -5,12 +5,12 @@ Copy files/folders between a service container and the local filesystem
|
||||
|
||||
### Options
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
|:----------------------|:------|:--------|:--------------------------------------------------------|
|
||||
| `-a`, `--archive` | | | Archive mode (copy all uid/gid information) |
|
||||
| `--dry-run` | | | Execute command in dry run mode |
|
||||
| `-L`, `--follow-link` | | | Always follow symbol link in SRC_PATH |
|
||||
| `--index` | `int` | `0` | index of the container if service has multiple replicas |
|
||||
| Name | Type | Default | Description |
|
||||
|:----------------------|:------|:--------|:----------------------------------------------------------------------|
|
||||
| `-a`, `--archive` | | | Archive mode (copy all uid/gid information) |
|
||||
| `--dry-run` | | | Execute command in dry run mode |
|
||||
| `-L`, `--follow-link` | | | Always follow symbol link in SRC_PATH |
|
||||
| `--index` | `int` | `0` | Index of the container if there are multiple instances of a service . |
|
||||
|
||||
|
||||
<!---MARKER_GEN_END-->
|
||||
|
||||
@@ -5,16 +5,16 @@ Execute a command in a running container.
|
||||
|
||||
### Options
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
|:------------------|:--------------|:--------|:---------------------------------------------------------------------------------|
|
||||
| `-d`, `--detach` | | | Detached mode: Run command in the background. |
|
||||
| `--dry-run` | | | Execute command in dry run mode |
|
||||
| `-e`, `--env` | `stringArray` | | Set environment variables |
|
||||
| `--index` | `int` | `0` | index of the container if service has multiple replicas |
|
||||
| `-T`, `--no-TTY` | | | Disable pseudo-TTY allocation. By default `docker compose exec` allocates a TTY. |
|
||||
| `--privileged` | | | Give extended privileges to the process. |
|
||||
| `-u`, `--user` | `string` | | Run the command as this user. |
|
||||
| `-w`, `--workdir` | `string` | | Path to workdir directory for this command. |
|
||||
| Name | Type | Default | Description |
|
||||
|:------------------|:--------------|:--------|:----------------------------------------------------------------------------------|
|
||||
| `-d`, `--detach` | | | Detached mode: Run command in the background. |
|
||||
| `--dry-run` | | | Execute command in dry run mode |
|
||||
| `-e`, `--env` | `stringArray` | | Set environment variables |
|
||||
| `--index` | `int` | `1` | index of the container if there are multiple instances of a service [default: 1]. |
|
||||
| `-T`, `--no-TTY` | | | Disable pseudo-TTY allocation. By default `docker compose exec` allocates a TTY. |
|
||||
| `--privileged` | | | Give extended privileges to the process. |
|
||||
| `-u`, `--user` | `string` | | Run the command as this user. |
|
||||
| `-w`, `--workdir` | `string` | | Path to workdir directory for this command. |
|
||||
|
||||
|
||||
<!---MARKER_GEN_END-->
|
||||
|
||||
@@ -8,7 +8,7 @@ Print the public port for a port binding.
|
||||
| Name | Type | Default | Description |
|
||||
|:-------------|:---------|:--------|:--------------------------------------------------------|
|
||||
| `--dry-run` | | | Execute command in dry run mode |
|
||||
| `--index` | `int` | `0` | index of the container if service has multiple replicas |
|
||||
| `--index` | `int` | `1` | index of the container if service has multiple replicas |
|
||||
| `--protocol` | `string` | `tcp` | tcp or udp |
|
||||
|
||||
|
||||
|
||||
@@ -347,7 +347,6 @@ options:
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
|
||||
@@ -21,7 +21,6 @@ inherited_options:
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
deprecated: false
|
||||
hidden: true
|
||||
experimental: false
|
||||
experimentalcli: true
|
||||
kubernetes: false
|
||||
|
||||
@@ -69,7 +69,6 @@ inherited_options:
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
experimentalcli: true
|
||||
kubernetes: false
|
||||
|
||||
@@ -29,7 +29,6 @@ inherited_options:
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
experimentalcli: true
|
||||
kubernetes: false
|
||||
|
||||
@@ -159,7 +159,6 @@ inherited_options:
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
|
||||
@@ -152,7 +152,6 @@ inherited_options:
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
|
||||
@@ -10,7 +10,7 @@ options:
|
||||
- option: all
|
||||
value_type: bool
|
||||
default_value: "false"
|
||||
description: copy to all the containers of the service.
|
||||
description: Copy to all the containers of the service.
|
||||
deprecated: true
|
||||
hidden: true
|
||||
experimental: false
|
||||
@@ -42,7 +42,8 @@ options:
|
||||
- option: index
|
||||
value_type: int
|
||||
default_value: "0"
|
||||
description: index of the container if service has multiple replicas
|
||||
description: |
|
||||
Index of the container if there are multiple instances of a service .
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
@@ -61,7 +62,6 @@ inherited_options:
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
|
||||
@@ -90,7 +90,6 @@ inherited_options:
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
|
||||
@@ -73,7 +73,6 @@ inherited_options:
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
|
||||
@@ -46,7 +46,6 @@ inherited_options:
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
|
||||
@@ -33,8 +33,9 @@ options:
|
||||
swarm: false
|
||||
- option: index
|
||||
value_type: int
|
||||
default_value: "0"
|
||||
description: index of the container if service has multiple replicas
|
||||
default_value: "1"
|
||||
description: |
|
||||
index of the container if there are multiple instances of a service [default: 1].
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
@@ -117,7 +118,6 @@ inherited_options:
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
|
||||
@@ -38,7 +38,6 @@ inherited_options:
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
|
||||
@@ -43,7 +43,6 @@ inherited_options:
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
|
||||
@@ -91,7 +91,6 @@ inherited_options:
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
|
||||
@@ -58,7 +58,6 @@ inherited_options:
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
|
||||
@@ -17,7 +17,6 @@ inherited_options:
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
|
||||
@@ -7,7 +7,7 @@ plink: docker_compose.yaml
|
||||
options:
|
||||
- option: index
|
||||
value_type: int
|
||||
default_value: "0"
|
||||
default_value: "1"
|
||||
description: index of the container if service has multiple replicas
|
||||
deprecated: false
|
||||
hidden: false
|
||||
@@ -37,7 +37,6 @@ inherited_options:
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
|
||||
@@ -180,7 +180,6 @@ examples: |-
|
||||
The `docker compose ps` command currently only supports the `--filter status=<status>`
|
||||
option, but additional filter options may be added in the future.
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
|
||||
@@ -124,7 +124,6 @@ examples: |-
|
||||
`docker compose pull` will try to pull image for services with a build section. If pull fails, it will let
|
||||
user know this service image MUST be built. You can skip this by setting `--ignore-buildable` flag
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
|
||||
@@ -66,7 +66,6 @@ inherited_options:
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
|
||||
@@ -48,7 +48,6 @@ inherited_options:
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
|
||||
@@ -76,7 +76,6 @@ inherited_options:
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
|
||||
@@ -286,7 +286,6 @@ inherited_options:
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
|
||||
@@ -16,7 +16,6 @@ inherited_options:
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
|
||||
@@ -29,7 +29,6 @@ inherited_options:
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
|
||||
@@ -23,7 +23,6 @@ examples: |-
|
||||
root 142353 142331 2 15:33 ? 00:00:00 ping localhost -c 5
|
||||
```
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
|
||||
@@ -16,7 +16,6 @@ inherited_options:
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
|
||||
@@ -285,7 +285,6 @@ inherited_options:
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
|
||||
@@ -37,7 +37,6 @@ inherited_options:
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
|
||||
@@ -27,7 +27,6 @@ inherited_options:
|
||||
kubernetes: false
|
||||
swarm: false
|
||||
deprecated: false
|
||||
hidden: false
|
||||
experimental: false
|
||||
experimentalcli: false
|
||||
kubernetes: false
|
||||
|
||||
22
go.mod
22
go.mod
@@ -6,15 +6,15 @@ require (
|
||||
github.com/AlecAivazis/survey/v2 v2.3.7
|
||||
github.com/Microsoft/go-winio v0.6.1
|
||||
github.com/buger/goterm v1.0.4
|
||||
github.com/compose-spec/compose-go v1.16.0
|
||||
github.com/compose-spec/compose-go v1.15.1
|
||||
github.com/containerd/console v1.0.3
|
||||
github.com/containerd/containerd v1.7.2
|
||||
github.com/cucumber/godog v0.0.0-00010101000000-000000000000 // replaced; see replace for the actual version used
|
||||
github.com/distribution/distribution/v3 v3.0.0-20230601133803-97b1d649c493
|
||||
github.com/docker/buildx v0.11.2
|
||||
github.com/docker/cli v24.0.4+incompatible
|
||||
github.com/docker/cli-docs-tool v0.6.0
|
||||
github.com/docker/docker v24.0.5-0.20230714235725-36e9e796c6fc+incompatible // v24.0.5-dev
|
||||
github.com/docker/buildx v0.11.0
|
||||
github.com/docker/cli v24.0.2+incompatible
|
||||
github.com/docker/cli-docs-tool v0.5.1
|
||||
github.com/docker/docker v24.0.2+incompatible
|
||||
github.com/docker/go-connections v0.4.0
|
||||
github.com/docker/go-units v0.5.0
|
||||
github.com/fsnotify/fsevents v0.1.1
|
||||
@@ -24,7 +24,7 @@ require (
|
||||
github.com/jonboulle/clockwork v0.4.0
|
||||
github.com/mattn/go-shellwords v1.0.12
|
||||
github.com/mitchellh/mapstructure v1.5.0
|
||||
github.com/moby/buildkit v0.12.1-0.20230717122532-faa0cc7da353 // v0.12 release branch
|
||||
github.com/moby/buildkit v0.11.0-rc3.0.20230609092854-67a08623b95a
|
||||
github.com/moby/patternmatcher v0.5.0
|
||||
github.com/moby/term v0.5.0
|
||||
github.com/morikuni/aec v1.0.0
|
||||
@@ -44,9 +44,9 @@ require (
|
||||
go.opentelemetry.io/otel/trace v1.14.0
|
||||
go.uber.org/goleak v1.2.1
|
||||
golang.org/x/sync v0.3.0
|
||||
google.golang.org/grpc v1.56.2
|
||||
google.golang.org/grpc v1.56.0
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
gotest.tools/v3 v3.5.0
|
||||
gotest.tools/v3 v3.4.0
|
||||
)
|
||||
|
||||
require (
|
||||
@@ -71,6 +71,7 @@ require (
|
||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||
github.com/cloudflare/cfssl v1.6.4 // indirect
|
||||
github.com/containerd/continuity v0.4.1 // indirect
|
||||
github.com/containerd/ttrpc v1.2.2 // indirect
|
||||
github.com/containerd/typeurl/v2 v2.1.1 // indirect
|
||||
github.com/cucumber/gherkin-go/v19 v19.0.3 // indirect
|
||||
github.com/cucumber/messages-go/v16 v16.0.1 // indirect
|
||||
@@ -147,9 +148,9 @@ require (
|
||||
github.com/spf13/cast v1.5.0 // indirect
|
||||
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
||||
github.com/subosito/gotenv v1.4.1 // indirect
|
||||
github.com/tonistiigi/fsutil v0.0.0-20230629203738-36ef4d8c0dbb // indirect
|
||||
github.com/tonistiigi/fsutil v0.0.0-20230407161946-9e7a6df48576 // indirect
|
||||
github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea // indirect
|
||||
github.com/tonistiigi/vt100 v0.0.0-20230623042737-f9a4f7ef6531 // indirect
|
||||
github.com/tonistiigi/vt100 v0.0.0-20210615222946-8066bb97264f // indirect
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
|
||||
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
|
||||
@@ -182,6 +183,7 @@ require (
|
||||
k8s.io/klog/v2 v2.90.1 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280 // indirect
|
||||
k8s.io/utils v0.0.0-20230220204549-a5ecb0141aa5 // indirect
|
||||
oras.land/oras-go/v2 v2.2.0 // indirect
|
||||
sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect
|
||||
sigs.k8s.io/yaml v1.3.0 // indirect
|
||||
|
||||
48
go.sum
48
go.sum
@@ -131,8 +131,8 @@ github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWH
|
||||
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4 h1:/inchEIKaYC1Akx+H+gqO04wryn5h75LSazbRlnya1k=
|
||||
github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb h1:EDmT6Q9Zs+SbUoc7Ik9EfrFqcylYqgPZ9ANSbTAntnE=
|
||||
github.com/compose-spec/compose-go v1.16.0 h1:HYk4uYWXgArHh6NG+WE4yGYayOXw+hjqJ+eJxpjWWjk=
|
||||
github.com/compose-spec/compose-go v1.16.0/go.mod h1:3yngGBGfls6FHGQsg4B1z6gz8ej9SOvmAJtxCwgbcnc=
|
||||
github.com/compose-spec/compose-go v1.15.1 h1:0yaEt6/66dLN0bNWYDTj0CDx626uCdQ9ipJVIJx8O8M=
|
||||
github.com/compose-spec/compose-go v1.15.1/go.mod h1:3yngGBGfls6FHGQsg4B1z6gz8ej9SOvmAJtxCwgbcnc=
|
||||
github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM=
|
||||
github.com/containerd/console v1.0.3 h1:lIr7SlA5PxZyMV30bDW0MGbiOPXwc63yRuCP0ARubLw=
|
||||
github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U=
|
||||
@@ -145,6 +145,7 @@ github.com/containerd/nydus-snapshotter v0.8.2 h1:7SOrMU2YmLzfbsr5J7liMZJlNi5WT6
|
||||
github.com/containerd/stargz-snapshotter v0.14.3 h1:OTUVZoPSPs8mGgmQUE1dqw3WX/3nrsmsurW7UPLWl1U=
|
||||
github.com/containerd/stargz-snapshotter/estargz v0.14.3 h1:OqlDCK3ZVUO6C3B/5FSkDwbkEETK84kQgEeFwDC+62k=
|
||||
github.com/containerd/ttrpc v1.2.2 h1:9vqZr0pxwOF5koz6N0N3kJ0zDHokrcPxIR/ZR2YFtOs=
|
||||
github.com/containerd/ttrpc v1.2.2/go.mod h1:sIT6l32Ph/H9cvnJsfXM5drIVzTr5A2flTf1G5tYZak=
|
||||
github.com/containerd/typeurl/v2 v2.1.1 h1:3Q4Pt7i8nYwy2KmQWIw2+1hTvwTE/6w9FqcttATPO/4=
|
||||
github.com/containerd/typeurl/v2 v2.1.1/go.mod h1:IDp2JFvbwZ31H8dQbEIY7sDl2L3o3HZj1hsSQlywkQ0=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
@@ -166,17 +167,17 @@ github.com/denisenkom/go-mssqldb v0.0.0-20190515213511-eb9f6a1743f3/go.mod h1:zA
|
||||
github.com/denisenkom/go-mssqldb v0.0.0-20191128021309-1d7a30a10f73/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
|
||||
github.com/distribution/distribution/v3 v3.0.0-20230601133803-97b1d649c493 h1:fm5DpBD+A7o0+x9Nf+o9/4/qPGbfxLpr9qIPVuV8vQc=
|
||||
github.com/distribution/distribution/v3 v3.0.0-20230601133803-97b1d649c493/go.mod h1:+fqBJ4vPYo4Uu1ZE4d+bUtTLRXfdSL3NvCZIZ9GHv58=
|
||||
github.com/docker/buildx v0.11.2 h1:R3p9F0gnI4FwvQ0p40UwdX1T4ugap4UWxY3TFHoP4Ws=
|
||||
github.com/docker/buildx v0.11.2/go.mod h1:CWAABt10iIuGpleypA3103mplDfcGu0A2AvT03xfpTc=
|
||||
github.com/docker/cli v24.0.4+incompatible h1:Y3bYF9ekNTm2VFz5U/0BlMdJy73D+Y1iAAZ8l63Ydzw=
|
||||
github.com/docker/cli v24.0.4+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||
github.com/docker/cli-docs-tool v0.6.0 h1:Z9x10SaZgFaB6jHgz3OWooynhSa40CsWkpe5hEnG/qA=
|
||||
github.com/docker/cli-docs-tool v0.6.0/go.mod h1:zMjqTFCU361PRh8apiXzeAZ1Q/xupbIwTusYpzCXS/o=
|
||||
github.com/docker/buildx v0.11.0 h1:DNCOIYT/7J0sPBlU/ozEhFd4MtbnbFByn45yeTMHXVU=
|
||||
github.com/docker/buildx v0.11.0/go.mod h1:Yq7ZNjrwXKzW0uSFMk46dl5Gl903k5+bp6U4apsM5rs=
|
||||
github.com/docker/cli v24.0.2+incompatible h1:QdqR7znue1mtkXIJ+ruQMGQhpw2JzMJLRXp6zpzF6tM=
|
||||
github.com/docker/cli v24.0.2+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||
github.com/docker/cli-docs-tool v0.5.1 h1:jIk/cCZurZERhALPVKhqlNxTQGxn2kcI+56gE57PQXg=
|
||||
github.com/docker/cli-docs-tool v0.5.1/go.mod h1:zMjqTFCU361PRh8apiXzeAZ1Q/xupbIwTusYpzCXS/o=
|
||||
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||
github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8=
|
||||
github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||
github.com/docker/docker v24.0.5-0.20230714235725-36e9e796c6fc+incompatible h1:sdGvA1bxu/1J51gAs1XU0bZC+2WxncYnI210as3c6g8=
|
||||
github.com/docker/docker v24.0.5-0.20230714235725-36e9e796c6fc+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/docker v24.0.2+incompatible h1:eATx+oLz9WdNVkQrr0qjQ8HvRJ4bOOxfzEo8R+dA3cg=
|
||||
github.com/docker/docker v24.0.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/docker-credential-helpers v0.7.0 h1:xtCHsjxogADNZcdv1pKUHXryefjlVRqWqIhk/uXJp0A=
|
||||
github.com/docker/docker-credential-helpers v0.7.0/go.mod h1:rETQfLdHNT3foU5kuNkFR1R1V12OJRRO5lzt2D1b5X0=
|
||||
github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c h1:lzqkGL9b3znc+ZUgi7FlLnqjQhcXxkNM/quxIjBVMD0=
|
||||
@@ -464,8 +465,8 @@ github.com/miekg/pkcs11 v1.1.1/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WT
|
||||
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.12.1-0.20230717122532-faa0cc7da353 h1:/ZIwqvOF3QKObJbjX96xVvAKtnWdw/AuEqysbbujaZA=
|
||||
github.com/moby/buildkit v0.12.1-0.20230717122532-faa0cc7da353/go.mod h1:+n9GmkxwBCjVz4u7wmiyh+oqvjIjQM+1zk3iJrWfdos=
|
||||
github.com/moby/buildkit v0.11.0-rc3.0.20230609092854-67a08623b95a h1:1k3bAXwxC2N1FncWijq/43sLj2OVIZ11FT0APIXWhMg=
|
||||
github.com/moby/buildkit v0.11.0-rc3.0.20230609092854-67a08623b95a/go.mod h1:4sM7BBBqXOQ+vV6LrVAOAMhZI9cVNYV5RhZCl906a64=
|
||||
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.5.0 h1:YCZgJOeULcxLw1Q+sVR636pmS7sPEn1Qo2iAN6M7DBo=
|
||||
@@ -510,6 +511,8 @@ github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1
|
||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||
github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
|
||||
github.com/opencontainers/image-spec v1.1.0-rc3 h1:fzg1mXZFj8YdPeNkRXMg+zb88BFV0Ys52cJydRwBkb8=
|
||||
github.com/opencontainers/image-spec v1.1.0-rc3/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8=
|
||||
github.com/opencontainers/image-spec v1.1.0-rc4 h1:oOxKUJWnFC4YGHCCMNql1x4YaDfYBTS5Y4x/Cgeo1E0=
|
||||
github.com/opencontainers/image-spec v1.1.0-rc4/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8=
|
||||
github.com/opencontainers/runc v1.1.7 h1:y2EZDS8sNng4Ksf0GUYNhKbTShZJPJg1FiXJNH/uoCk=
|
||||
@@ -556,6 +559,7 @@ github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R
|
||||
github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/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.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
||||
github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI=
|
||||
github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY=
|
||||
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
||||
@@ -577,6 +581,7 @@ github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPx
|
||||
github.com/sirupsen/logrus v1.3.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
|
||||
@@ -623,12 +628,12 @@ github.com/theupdateframework/notary v0.7.0 h1:QyagRZ7wlSpjT5N2qQAh/pN+DVqgekv4D
|
||||
github.com/theupdateframework/notary v0.7.0/go.mod h1:c9DRxcmhHmVLDay4/2fUYdISnHqbFDGRSlXPO0AhYWw=
|
||||
github.com/tilt-dev/fsnotify v1.4.8-0.20220602155310-fff9c274a375 h1:QB54BJwA6x8QU9nHY3xJSZR2kX9bgpZekRKGkLTmEXA=
|
||||
github.com/tilt-dev/fsnotify v1.4.8-0.20220602155310-fff9c274a375/go.mod h1:xRroudyp5iVtxKqZCrA6n2TLFRBf8bmnjr1UD4x+z7g=
|
||||
github.com/tonistiigi/fsutil v0.0.0-20230629203738-36ef4d8c0dbb h1:uUe8rNyVXM8moActoBol6Xf6xX2GMr7SosR2EywMvGg=
|
||||
github.com/tonistiigi/fsutil v0.0.0-20230629203738-36ef4d8c0dbb/go.mod h1:SxX/oNQ/ag6Vaoli547ipFK9J7BZn5JqJG0JE8lf8bA=
|
||||
github.com/tonistiigi/fsutil v0.0.0-20230407161946-9e7a6df48576 h1:fZXPQDVh5fm2x7pA0CH1TtH80tiZ0L7i834kZqZN8Pw=
|
||||
github.com/tonistiigi/fsutil v0.0.0-20230407161946-9e7a6df48576/go.mod h1:q1CxMSzcAbjUkVGHoZeQUcCaALnaE4XdWk+zJcgMYFw=
|
||||
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-20230623042737-f9a4f7ef6531 h1:Y/M5lygoNPKwVNLMPXgVfsRT40CSFKXCxuU8LoHySjs=
|
||||
github.com/tonistiigi/vt100 v0.0.0-20230623042737-f9a4f7ef6531/go.mod h1:ulncasL3N9uLrVann0m+CDlJKWsIAP34MPcOJF6VRvc=
|
||||
github.com/tonistiigi/vt100 v0.0.0-20210615222946-8066bb97264f h1:DLpt6B5oaaS8jyXHa9VA4rrZloBVPVXeCtrOsrFauxc=
|
||||
github.com/tonistiigi/vt100 v0.0.0-20210615222946-8066bb97264f/go.mod h1:ulncasL3N9uLrVann0m+CDlJKWsIAP34MPcOJF6VRvc=
|
||||
github.com/vbatts/tar-split v0.11.2 h1:Via6XqJr0hceW4wff3QRzD5gAk/tatMw/4ZA7cTlIME=
|
||||
github.com/weppos/publicsuffix-go v0.15.1-0.20220329081811-9a40b608a236 h1:vMJBP3PQViZsF6cOINtvyMC8ptpLsyJ4EwyFnzuWNxc=
|
||||
github.com/weppos/publicsuffix-go v0.15.1-0.20220329081811-9a40b608a236/go.mod h1:HYux0V0Zi04bHNwOHy4cXJVz/TQjYonnF6aoYhj+3QE=
|
||||
@@ -860,6 +865,7 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
|
||||
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
@@ -1035,8 +1041,8 @@ google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAG
|
||||
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||
google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
|
||||
google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
|
||||
google.golang.org/grpc v1.56.2 h1:fVRFRnXvU+x6C4IlHZewvJOVHoOv1TUuQyoRsYnB4bI=
|
||||
google.golang.org/grpc v1.56.2/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s=
|
||||
google.golang.org/grpc v1.56.0 h1:+y7Bs8rtMd07LeXmL3NxcTLn7mUkbKZqEpPhMNkwJEE=
|
||||
google.golang.org/grpc v1.56.0/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
@@ -1084,8 +1090,8 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C
|
||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gotest.tools/v3 v3.5.0 h1:Ljk6PdHdOhAb5aDMWXjDLMMhph+BpztA4v1QdqEW2eY=
|
||||
gotest.tools/v3 v3.5.0/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU=
|
||||
gotest.tools/v3 v3.4.0 h1:ZazjZUfuVeZGLAmlKKuyv3IKP5orXcwtOwDQH6YVr6o=
|
||||
gotest.tools/v3 v3.4.0/go.mod h1:CtbdzLSsqVhDgMtKsx03ird5YTGB3ar27v0u/yKBW5g=
|
||||
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
@@ -1106,6 +1112,8 @@ k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280 h1:+70TFaan3hfJzs+7VK2o+O
|
||||
k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280/go.mod h1:+Axhij7bCpeqhklhUTe3xmOn6bWxolyZEeyaFpjGtl4=
|
||||
k8s.io/utils v0.0.0-20230220204549-a5ecb0141aa5 h1:kmDqav+P+/5e1i9tFfHq1qcF3sOrDp+YEkVDAHu7Jwk=
|
||||
k8s.io/utils v0.0.0-20230220204549-a5ecb0141aa5/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
||||
oras.land/oras-go/v2 v2.2.0 h1:E1fqITD56Eg5neZbxBtAdZVgDHD6wBabJo6xESTcQyo=
|
||||
oras.land/oras-go/v2 v2.2.0/go.mod h1:pXjn0+KfarspMHHNR3A56j3tgvr+mxArHuI8qVn59v8=
|
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
||||
|
||||
@@ -74,6 +74,8 @@ type Service interface {
|
||||
Events(ctx context.Context, projectName string, options EventsOptions) error
|
||||
// Port executes the equivalent to a `compose port`
|
||||
Port(ctx context.Context, projectName string, service string, port uint16, options PortOptions) (string, int, error)
|
||||
// Publish executes the equivalent to a `compose publish`
|
||||
Publish(ctx context.Context, project *types.Project, repository string) error
|
||||
// Images executes the equivalent of a `compose images`
|
||||
Images(ctx context.Context, projectName string, options ImagesOptions) ([]ImageSummary, error)
|
||||
// MaxConcurrency defines upper limit for concurrent operations against engine API
|
||||
@@ -260,6 +262,7 @@ type ConfigOptions struct {
|
||||
// PushOptions group options of the Push API
|
||||
type PushOptions struct {
|
||||
Quiet bool
|
||||
Repository string
|
||||
IgnoreFailures bool
|
||||
}
|
||||
|
||||
|
||||
@@ -70,7 +70,7 @@ type execDetails struct {
|
||||
}
|
||||
|
||||
// NewDryRunClient produces a DryRunClient
|
||||
func NewDryRunClient(apiClient client.APIClient, cli command.Cli) (*DryRunClient, error) {
|
||||
func NewDryRunClient(apiClient client.APIClient, cli *command.DockerCli) (*DryRunClient, error) {
|
||||
b, err := builder.New(cli, builder.WithSkippedValidation())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -55,6 +55,7 @@ type ServiceProxy struct {
|
||||
DryRunModeFn func(ctx context.Context, dryRun bool) (context.Context, error)
|
||||
VizFn func(ctx context.Context, project *types.Project, options VizOptions) (string, error)
|
||||
WaitFn func(ctx context.Context, projectName string, options WaitOptions) (int64, error)
|
||||
PublishFn func(ctx context.Context, project *types.Project, repository string) error
|
||||
interceptors []Interceptor
|
||||
}
|
||||
|
||||
@@ -91,6 +92,7 @@ func (s *ServiceProxy) WithService(service Service) *ServiceProxy {
|
||||
s.TopFn = service.Top
|
||||
s.EventsFn = service.Events
|
||||
s.PortFn = service.Port
|
||||
s.PublishFn = service.Publish
|
||||
s.ImagesFn = service.Images
|
||||
s.WatchFn = service.Watch
|
||||
s.MaxConcurrencyFn = service.MaxConcurrency
|
||||
@@ -217,7 +219,7 @@ func (s *ServiceProxy) List(ctx context.Context, options ListOptions) ([]Stack,
|
||||
return s.ListFn(ctx, options)
|
||||
}
|
||||
|
||||
// Config implements Service interface
|
||||
// Convert implements Service interface
|
||||
func (s *ServiceProxy) Config(ctx context.Context, project *types.Project, options ConfigOptions) ([]byte, error) {
|
||||
if s.ConfigFn == nil {
|
||||
return nil, ErrNotImplemented
|
||||
@@ -311,6 +313,10 @@ func (s *ServiceProxy) Port(ctx context.Context, projectName string, service str
|
||||
return s.PortFn(ctx, projectName, service, port, options)
|
||||
}
|
||||
|
||||
func (s *ServiceProxy) Publish(ctx context.Context, project *types.Project, repository string) error {
|
||||
return s.PublishFn(ctx, project, repository)
|
||||
}
|
||||
|
||||
// Images implements Service interface
|
||||
func (s *ServiceProxy) Images(ctx context.Context, project string, options ImagesOptions) ([]ImageSummary, error) {
|
||||
if s.ImagesFn == nil {
|
||||
|
||||
@@ -88,11 +88,8 @@ func (s *composeService) DryRunMode(ctx context.Context, dryRun bool) (context.C
|
||||
if err != nil {
|
||||
return ctx, err
|
||||
}
|
||||
|
||||
options := flags.NewClientOptions()
|
||||
options.Context = s.dockerCli.CurrentContext()
|
||||
err = cli.Initialize(options, command.WithInitializeClient(func(cli *command.DockerCli) (client.APIClient, error) {
|
||||
return api.NewDryRunClient(s.apiClient(), s.dockerCli)
|
||||
err = cli.Initialize(flags.NewClientOptions(), command.WithInitializeClient(func(cli *command.DockerCli) (client.APIClient, error) {
|
||||
return api.NewDryRunClient(s.apiClient(), cli)
|
||||
}))
|
||||
if err != nil {
|
||||
return ctx, err
|
||||
|
||||
@@ -20,7 +20,6 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strconv"
|
||||
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
"github.com/docker/compose/v2/pkg/utils"
|
||||
@@ -73,9 +72,7 @@ func getDefaultFilters(projectName string, oneOff oneOff, selectedServices ...st
|
||||
|
||||
func (s *composeService) getSpecifiedContainer(ctx context.Context, projectName string, oneOff oneOff, stopped bool, serviceName string, containerIndex int) (moby.Container, error) {
|
||||
defaultFilters := getDefaultFilters(projectName, oneOff, serviceName)
|
||||
if containerIndex > 0 {
|
||||
defaultFilters = append(defaultFilters, containerNumberFilter(containerIndex))
|
||||
}
|
||||
defaultFilters = append(defaultFilters, containerNumberFilter(containerIndex))
|
||||
containers, err := s.apiClient().ContainerList(ctx, moby.ContainerListOptions{
|
||||
Filters: filters.NewArgs(
|
||||
defaultFilters...,
|
||||
@@ -86,16 +83,8 @@ func (s *composeService) getSpecifiedContainer(ctx context.Context, projectName
|
||||
return moby.Container{}, err
|
||||
}
|
||||
if len(containers) < 1 {
|
||||
if containerIndex > 0 {
|
||||
return moby.Container{}, fmt.Errorf("service %q is not running container #%d", serviceName, containerIndex)
|
||||
}
|
||||
return moby.Container{}, fmt.Errorf("service %q is not running", serviceName)
|
||||
return moby.Container{}, fmt.Errorf("service %q is not running container #%d", serviceName, containerIndex)
|
||||
}
|
||||
sort.Slice(containers, func(i, j int) bool {
|
||||
x, _ := strconv.Atoi(containers[i].Labels[api.ContainerNumberLabel])
|
||||
y, _ := strconv.Atoi(containers[j].Labels[api.ContainerNumberLabel])
|
||||
return x < y
|
||||
})
|
||||
container := containers[0]
|
||||
return container, nil
|
||||
}
|
||||
|
||||
@@ -549,15 +549,13 @@ func (s *composeService) createMobyContainer(ctx context.Context,
|
||||
// call via container.NetworkMode & network.NetworkingConfig
|
||||
// any remaining networks are connected one-by-one here after creation (but before start)
|
||||
serviceNetworks := service.NetworksByPriority()
|
||||
for _, networkKey := range serviceNetworks {
|
||||
mobyNetworkName := project.Networks[networkKey].Name
|
||||
if string(cfgs.Host.NetworkMode) == mobyNetworkName {
|
||||
// primary network already configured as part of ContainerCreate
|
||||
continue
|
||||
}
|
||||
epSettings := createEndpointSettings(project, service, number, networkKey, cfgs.Links, opts.UseNetworkAliases)
|
||||
if err := s.apiClient().NetworkConnect(ctx, mobyNetworkName, created.ID, epSettings); err != nil {
|
||||
return created, err
|
||||
if len(serviceNetworks) > 1 {
|
||||
for _, networkKey := range serviceNetworks[1:] {
|
||||
mobyNetworkName := project.Networks[networkKey].Name
|
||||
epSettings := createEndpointSettings(project, service, number, networkKey, cfgs.Links, opts.UseNetworkAliases)
|
||||
if err := s.apiClient().NetworkConnect(ctx, mobyNetworkName, created.ID, epSettings); err != nil {
|
||||
return created, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -24,14 +24,25 @@ import (
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
|
||||
moby "github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
)
|
||||
|
||||
func (s *composeService) Port(ctx context.Context, projectName string, service string, port uint16, options api.PortOptions) (string, int, error) {
|
||||
projectName = strings.ToLower(projectName)
|
||||
container, err := s.getSpecifiedContainer(ctx, projectName, oneOffInclude, false, service, options.Index)
|
||||
list, err := s.apiClient().ContainerList(ctx, moby.ContainerListOptions{
|
||||
Filters: filters.NewArgs(
|
||||
projectFilter(projectName),
|
||||
serviceFilter(service),
|
||||
containerNumberFilter(options.Index),
|
||||
),
|
||||
})
|
||||
if err != nil {
|
||||
return "", 0, err
|
||||
}
|
||||
if len(list) == 0 {
|
||||
return "", 0, fmt.Errorf("no container found for %s%s%d", service, api.Separator, options.Index)
|
||||
}
|
||||
container := list[0]
|
||||
for _, p := range container.Ports {
|
||||
if p.PrivatePort == port && p.Type == options.Protocol {
|
||||
return p.IP, int(p.PublicPort), nil
|
||||
|
||||
185
pkg/compose/publish.go
Normal file
185
pkg/compose/publish.go
Normal file
@@ -0,0 +1,185 @@
|
||||
/*
|
||||
Copyright 2020 Docker Compose CLI authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package compose
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
|
||||
"github.com/compose-spec/compose-go/types"
|
||||
"github.com/distribution/distribution/v3/reference"
|
||||
client2 "github.com/docker/cli/cli/registry/client"
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
"github.com/opencontainers/go-digest"
|
||||
"github.com/opencontainers/image-spec/specs-go"
|
||||
v1 "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
)
|
||||
|
||||
func (s *composeService) Publish(ctx context.Context, project *types.Project, repository string) error {
|
||||
err := s.Push(ctx, project, api.PushOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
target, err := reference.ParseDockerRef(repository)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
client := s.dockerCli.RegistryClient(false)
|
||||
for i, service := range project.Services {
|
||||
ref, err := reference.ParseDockerRef(service.Image)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
auth, err := encodedAuth(ref, s.configFile())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
inspect, err := s.apiClient().DistributionInspect(ctx, ref.String(), auth)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
canonical, err := reference.WithDigest(ref, inspect.Descriptor.Digest)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
to, err := reference.WithDigest(target, inspect.Descriptor.Digest)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = client.MountBlob(ctx, canonical, to)
|
||||
switch err.(type) {
|
||||
case client2.ErrBlobCreated:
|
||||
default:
|
||||
return err
|
||||
}
|
||||
service.Image = to.String()
|
||||
project.Services[i] = service
|
||||
}
|
||||
|
||||
err = s.publishComposeYaml(ctx, project, repository)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *composeService) publishComposeYaml(ctx context.Context, project *types.Project, repository string) error {
|
||||
ref, err := reference.ParseDockerRef(repository)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var manifests []v1.Descriptor
|
||||
|
||||
for _, composeFile := range project.ComposeFiles {
|
||||
stat, err := os.Stat(composeFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd := exec.CommandContext(ctx, "oras", "push", "--artifact-type", "application/vnd.docker.compose.yaml", ref.String(), composeFile)
|
||||
stdout, err := cmd.StdoutPipe()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cmd.Stderr = s.stderr()
|
||||
|
||||
err = cmd.Start()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
out, err := io.ReadAll(stdout)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var composeFileDigest string
|
||||
for _, line := range strings.Split(string(out), "\n") {
|
||||
if strings.HasPrefix(line, "Digest: ") {
|
||||
composeFileDigest = line[len("Digest: "):]
|
||||
}
|
||||
fmt.Fprintln(s.stdout(), line)
|
||||
}
|
||||
if composeFileDigest == "" {
|
||||
return fmt.Errorf("expected oras to display `Digest: xxx`")
|
||||
}
|
||||
|
||||
err = cmd.Wait()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
manifests = append(manifests, v1.Descriptor{
|
||||
MediaType: "application/vnd.oci.image.manifest.v1+json",
|
||||
Digest: digest.Digest(composeFileDigest),
|
||||
Size: stat.Size(),
|
||||
ArtifactType: "application/vnd.docker.compose.yaml",
|
||||
})
|
||||
}
|
||||
|
||||
for _, service := range project.Services {
|
||||
dockerRef, err := reference.ParseDockerRef(service.Image)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
manifests = append(manifests, v1.Descriptor{
|
||||
MediaType: v1.MediaTypeImageIndex,
|
||||
Digest: dockerRef.(reference.Digested).Digest(),
|
||||
Annotations: map[string]string{
|
||||
"com.docker.compose.service": service.Name,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
manifest := v1.Index{
|
||||
Versioned: specs.Versioned{
|
||||
SchemaVersion: 2,
|
||||
},
|
||||
MediaType: v1.MediaTypeImageIndex,
|
||||
Manifests: manifests,
|
||||
Annotations: map[string]string{
|
||||
"com.docker.compose": api.ComposeVersion,
|
||||
},
|
||||
}
|
||||
manifestContent, err := json.Marshal(manifest)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
temp, err := os.CreateTemp(os.TempDir(), "compose")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = os.WriteFile(temp.Name(), manifestContent, 0o700)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer os.Remove(temp.Name())
|
||||
|
||||
cmd := exec.CommandContext(ctx, "oras", "manifest", "push", ref.String(), temp.Name())
|
||||
cmd.Stdout = s.stdout()
|
||||
cmd.Stderr = s.stderr()
|
||||
err = cmd.Run()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -17,6 +17,7 @@
|
||||
package compose
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
@@ -26,14 +27,17 @@ import (
|
||||
"github.com/compose-spec/compose-go/types"
|
||||
"github.com/distribution/distribution/v3/reference"
|
||||
"github.com/docker/buildx/driver"
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
"github.com/docker/compose/v2/pkg/progress"
|
||||
moby "github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/pkg/jsonmessage"
|
||||
"github.com/docker/docker/registry"
|
||||
"github.com/opencontainers/go-digest"
|
||||
v1 "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/sync/errgroup"
|
||||
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
"github.com/docker/compose/v2/pkg/progress"
|
||||
"oras.land/oras-go/v2/content"
|
||||
"oras.land/oras-go/v2/registry/remote"
|
||||
)
|
||||
|
||||
func (s *composeService) Push(ctx context.Context, project *types.Project, options api.PushOptions) error {
|
||||
@@ -45,8 +49,8 @@ func (s *composeService) Push(ctx context.Context, project *types.Project, optio
|
||||
}, s.stdinfo(), "Pushing")
|
||||
}
|
||||
|
||||
func (s *composeService) push(ctx context.Context, project *types.Project, options api.PushOptions) error {
|
||||
eg, ctx := errgroup.WithContext(ctx)
|
||||
func (s *composeService) push(upctx context.Context, project *types.Project, options api.PushOptions) error {
|
||||
eg, ctx := errgroup.WithContext(upctx)
|
||||
eg.SetLimit(s.maxConcurrency)
|
||||
|
||||
info, err := s.apiClient().Info(ctx)
|
||||
@@ -79,7 +83,66 @@ func (s *composeService) push(ctx context.Context, project *types.Project, optio
|
||||
return nil
|
||||
})
|
||||
}
|
||||
return eg.Wait()
|
||||
|
||||
err = eg.Wait()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ctx = upctx
|
||||
|
||||
if options.Repository != "" {
|
||||
repository, err := remote.NewRepository(options.Repository)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
yaml, err := project.MarshalYAML()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
manifests := []v1.Descriptor{
|
||||
{
|
||||
MediaType: "application/vnd.oci.artifact.manifest.v1+json",
|
||||
Digest: digest.FromBytes(yaml),
|
||||
Size: int64(len(yaml)),
|
||||
Data: yaml,
|
||||
ArtifactType: "application/vnd.docker.compose.yaml",
|
||||
},
|
||||
}
|
||||
for _, service := range project.Services {
|
||||
inspected, _, err := s.dockerCli.Client().ImageInspectWithRaw(ctx, service.Image)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
manifests = append(manifests, v1.Descriptor{
|
||||
MediaType: v1.MediaTypeImageIndex,
|
||||
Digest: digest.Digest(inspected.RepoDigests[0]),
|
||||
Size: inspected.Size,
|
||||
Annotations: map[string]string{
|
||||
"com.docker.compose.service": service.Name,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
manifest := v1.Index{
|
||||
MediaType: v1.MediaTypeImageIndex,
|
||||
Manifests: manifests,
|
||||
Annotations: map[string]string{
|
||||
"com.docker.compose": api.ComposeVersion,
|
||||
},
|
||||
}
|
||||
manifestContent, err := json.Marshal(manifest)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
manifestDescriptor := content.NewDescriptorFromBytes(v1.MediaTypeImageIndex, manifestContent)
|
||||
|
||||
err = repository.Push(ctx, manifestDescriptor, bytes.NewReader(manifestContent))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *composeService) pushServiceImage(ctx context.Context, service types.ServiceConfig, info moby.Info, configFile driver.Auth, w progress.Writer, quietPush bool) error {
|
||||
|
||||
@@ -58,7 +58,7 @@ func createTar(env string, config types.ServiceSecretConfig) (bytes.Buffer, erro
|
||||
value := []byte(env)
|
||||
b := bytes.Buffer{}
|
||||
tarWriter := tar.NewWriter(&b)
|
||||
mode := uint32(0o444)
|
||||
mode := uint32(0o400)
|
||||
if config.Mode != nil {
|
||||
mode = *config.Mode
|
||||
}
|
||||
|
||||
@@ -35,12 +35,6 @@ func TestLocalComposeExec(t *testing.T) {
|
||||
return ret
|
||||
}
|
||||
|
||||
cleanup := func() {
|
||||
c.RunDockerComposeCmd(t, cmdArgs("down", "--timeout=0")...)
|
||||
}
|
||||
cleanup()
|
||||
t.Cleanup(cleanup)
|
||||
|
||||
c.RunDockerComposeCmd(t, cmdArgs("up", "-d")...)
|
||||
|
||||
t.Run("exec true", func(t *testing.T) {
|
||||
|
||||
@@ -136,12 +136,4 @@ func TestLocalComposeRun(t *testing.T) {
|
||||
|
||||
c.RunDockerComposeCmd(t, "-f", "./fixtures/run-test/deps.yaml", "down", "--remove-orphans")
|
||||
})
|
||||
|
||||
t.Run("run without dependencies", func(t *testing.T) {
|
||||
res := c.RunDockerComposeCmd(t, "-f", "./fixtures/run-test/deps.yaml", "run", "--no-deps", "service_a")
|
||||
assert.Assert(t, !strings.Contains(res.Combined(), "shared_dep"), res.Combined())
|
||||
assert.Assert(t, !strings.Contains(res.Combined(), "service_b"), res.Combined())
|
||||
|
||||
c.RunDockerComposeCmd(t, "-f", "./fixtures/run-test/deps.yaml", "down", "--remove-orphans")
|
||||
})
|
||||
}
|
||||
|
||||
@@ -281,12 +281,8 @@ func TestStopWithDependenciesAttached(t *testing.T) {
|
||||
const projectName = "compose-e2e-stop-with-deps"
|
||||
c := NewParallelCLI(t, WithEnv("COMMAND=echo hello"))
|
||||
|
||||
cleanup := func() {
|
||||
c.RunDockerComposeCmd(t, "-p", projectName, "down", "--remove-orphans", "--timeout=0")
|
||||
}
|
||||
cleanup()
|
||||
t.Cleanup(cleanup)
|
||||
|
||||
res := c.RunDockerComposeCmd(t, "-f", "./fixtures/dependencies/compose.yaml", "-p", projectName, "up", "--attach-dependencies", "foo")
|
||||
res.Assert(t, icmd.Expected{Out: "exited with code 0"})
|
||||
t.Run("up", func(t *testing.T) {
|
||||
res := c.RunDockerComposeCmd(t, "-f", "./fixtures/dependencies/compose.yaml", "-p", projectName, "up", "--attach-dependencies", "foo")
|
||||
res.Assert(t, icmd.Expected{Out: "exited with code 0"})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -51,18 +51,8 @@ func TestUpExitCodeFrom(t *testing.T) {
|
||||
c := NewParallelCLI(t)
|
||||
const projectName = "e2e-exit-code-from"
|
||||
|
||||
res := c.RunDockerComposeCmdNoCheck(t, "-f", "fixtures/start-fail/start-depends_on-long-lived.yaml", "--project-name", projectName, "up", "--exit-code-from=failure", "failure")
|
||||
res.Assert(t, icmd.Expected{ExitCode: 42})
|
||||
|
||||
c.RunDockerComposeCmd(t, "--project-name", projectName, "down", "--remove-orphans")
|
||||
}
|
||||
|
||||
func TestUpExitCodeFromContainerKilled(t *testing.T) {
|
||||
c := NewParallelCLI(t)
|
||||
const projectName = "e2e-exit-code-from-kill"
|
||||
|
||||
res := c.RunDockerComposeCmdNoCheck(t, "-f", "fixtures/start-fail/start-depends_on-long-lived.yaml", "--project-name", projectName, "up", "--exit-code-from=test")
|
||||
res.Assert(t, icmd.Expected{ExitCode: 143})
|
||||
res.Assert(t, icmd.Expected{ExitCode: 137})
|
||||
|
||||
c.RunDockerComposeCmd(t, "--project-name", projectName, "down", "--remove-orphans")
|
||||
}
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
services:
|
||||
base:
|
||||
image: base
|
||||
init: true
|
||||
build:
|
||||
context: .
|
||||
dockerfile: base.dockerfile
|
||||
service:
|
||||
init: true
|
||||
depends_on:
|
||||
- base
|
||||
build:
|
||||
|
||||
@@ -4,7 +4,6 @@ services:
|
||||
command: echo 'hello world'
|
||||
longrunning:
|
||||
image: alpine
|
||||
init: true
|
||||
depends_on:
|
||||
oneshot:
|
||||
condition: service_completed_successfully
|
||||
|
||||
@@ -3,7 +3,6 @@ services:
|
||||
my-service:
|
||||
image: alpine
|
||||
command: tail -f /dev/null
|
||||
init: true
|
||||
depends_on:
|
||||
nginx: {condition: service_healthy}
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
services:
|
||||
foo:
|
||||
image: alpine
|
||||
init: true
|
||||
entrypoint: ["sleep", "600"]
|
||||
networks:
|
||||
default:
|
||||
@@ -10,4 +9,4 @@ networks:
|
||||
default:
|
||||
ipam:
|
||||
config:
|
||||
- subnet: 10.1.0.0/16
|
||||
- subnet: 10.1.0.0/16
|
||||
@@ -1,7 +1,6 @@
|
||||
services:
|
||||
ping:
|
||||
image: alpine
|
||||
init: true
|
||||
command: ping localhost -c ${REPEAT:-1}
|
||||
hello:
|
||||
image: alpine
|
||||
|
||||
@@ -6,14 +6,12 @@ services:
|
||||
- MYSQL_ALLOW_EMPTY_PASSWORD=yes
|
||||
db:
|
||||
image: gtardif/sentences-db
|
||||
init: true
|
||||
networks:
|
||||
- dbnet
|
||||
- closesnetworkname1
|
||||
- closesnetworkname2
|
||||
words:
|
||||
image: gtardif/sentences-api
|
||||
init: true
|
||||
ports:
|
||||
- "8080:8080"
|
||||
networks:
|
||||
@@ -21,7 +19,6 @@ services:
|
||||
- servicenet
|
||||
web:
|
||||
image: gtardif/sentences-web
|
||||
init: true
|
||||
ports:
|
||||
- "80:80"
|
||||
labels:
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
services:
|
||||
with-restart:
|
||||
image: alpine
|
||||
init: true
|
||||
command: tail -f /dev/null
|
||||
depends_on:
|
||||
nginx: {condition: service_healthy, restart: true}
|
||||
|
||||
no-restart:
|
||||
image: alpine
|
||||
init: true
|
||||
command: tail -f /dev/null
|
||||
depends_on:
|
||||
nginx: { condition: service_healthy }
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
services:
|
||||
restart:
|
||||
image: alpine
|
||||
init: true
|
||||
command: ash -c "if [[ -f /tmp/restart.lock ]] ; then sleep infinity; else touch /tmp/restart.lock; fi"
|
||||
|
||||
@@ -1,15 +1,12 @@
|
||||
services:
|
||||
db:
|
||||
image: gtardif/sentences-db
|
||||
init: true
|
||||
words:
|
||||
image: gtardif/sentences-api
|
||||
init: true
|
||||
ports:
|
||||
- "95:8080"
|
||||
web:
|
||||
image: gtardif/sentences-web
|
||||
init: true
|
||||
ports:
|
||||
- "90:80"
|
||||
labels:
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
services:
|
||||
fail:
|
||||
image: alpine
|
||||
init: true
|
||||
command: sleep infinity
|
||||
healthcheck:
|
||||
test: "false"
|
||||
@@ -9,7 +8,6 @@ services:
|
||||
retries: 3
|
||||
depends:
|
||||
image: alpine
|
||||
init: true
|
||||
command: sleep infinity
|
||||
depends_on:
|
||||
fail:
|
||||
|
||||
@@ -1,14 +1,11 @@
|
||||
services:
|
||||
safe:
|
||||
image: 'alpine'
|
||||
init: true
|
||||
command: ['/bin/sh', '-c', 'sleep infinity'] # never exiting
|
||||
failure:
|
||||
image: 'alpine'
|
||||
init: true
|
||||
command: ['/bin/sh', '-c', 'sleep 1 ; echo "exiting with error" ; exit 42']
|
||||
command: ['/bin/sh', '-c', 'sleep 2 ; echo "exiting" ; exit 42']
|
||||
test:
|
||||
image: 'alpine'
|
||||
init: true
|
||||
command: ['/bin/sh', '-c', 'sleep 99999 ; echo "tests are OK"'] # very long job
|
||||
depends_on: [safe]
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
services:
|
||||
stderr:
|
||||
image: alpine
|
||||
init: true
|
||||
command: /bin/ash /log_to_stderr.sh
|
||||
volumes:
|
||||
- ./log_to_stderr.sh:/log_to_stderr.sh
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
x-dev: &x-dev
|
||||
watch:
|
||||
- action: sync
|
||||
path: ./data
|
||||
target: /app/data
|
||||
ignore:
|
||||
- '*.foo'
|
||||
- ./ignored
|
||||
|
||||
services:
|
||||
alpine:
|
||||
build:
|
||||
dockerfile_inline: |-
|
||||
FROM alpine
|
||||
RUN mkdir -p /app/data
|
||||
init: true
|
||||
command: sleep infinity
|
||||
x-develop: *x-dev
|
||||
busybox:
|
||||
build:
|
||||
dockerfile_inline: |-
|
||||
FROM busybox
|
||||
RUN mkdir -p /app/data
|
||||
init: true
|
||||
command: sleep infinity
|
||||
x-develop: *x-dev
|
||||
debian:
|
||||
build:
|
||||
dockerfile_inline: |-
|
||||
FROM debian
|
||||
RUN mkdir -p /app/data
|
||||
init: true
|
||||
command: sleep infinity
|
||||
x-develop: *x-dev
|
||||
@@ -1 +0,0 @@
|
||||
hello world
|
||||
@@ -29,34 +29,47 @@ import (
|
||||
|
||||
func TestNetworks(t *testing.T) {
|
||||
// fixture is shared with TestNetworkModes and is not safe to run concurrently
|
||||
c := NewCLI(t)
|
||||
|
||||
const projectName = "network-e2e"
|
||||
c := NewCLI(t, WithEnv(
|
||||
"COMPOSE_PROJECT_NAME="+projectName,
|
||||
"COMPOSE_FILE=./fixtures/network-test/compose.yaml",
|
||||
))
|
||||
|
||||
c.RunDockerComposeCmd(t, "down", "-t0", "-v")
|
||||
t.Run("ensure we do not reuse previous networks", func(t *testing.T) {
|
||||
c.RunDockerOrExitError(t, "network", "rm", projectName+"-dbnet")
|
||||
c.RunDockerOrExitError(t, "network", "rm", "microservices")
|
||||
})
|
||||
|
||||
c.RunDockerComposeCmd(t, "up", "-d")
|
||||
t.Run("up", func(t *testing.T) {
|
||||
c.RunDockerComposeCmd(t, "-f", "./fixtures/network-test/compose.yaml", "--project-name", projectName, "up",
|
||||
"-d")
|
||||
})
|
||||
|
||||
res := c.RunDockerComposeCmd(t, "ps")
|
||||
res.Assert(t, icmd.Expected{Out: `web`})
|
||||
t.Run("check running project", func(t *testing.T) {
|
||||
res := c.RunDockerComposeCmd(t, "-p", projectName, "ps")
|
||||
res.Assert(t, icmd.Expected{Out: `web`})
|
||||
|
||||
endpoint := "http://localhost:80"
|
||||
output := HTTPGetWithRetry(t, endpoint+"/words/noun", http.StatusOK, 2*time.Second, 20*time.Second)
|
||||
assert.Assert(t, strings.Contains(output, `"word":`))
|
||||
endpoint := "http://localhost:80"
|
||||
output := HTTPGetWithRetry(t, endpoint+"/words/noun", http.StatusOK, 2*time.Second, 20*time.Second)
|
||||
assert.Assert(t, strings.Contains(output, `"word":`))
|
||||
|
||||
res = c.RunDockerCmd(t, "network", "ls")
|
||||
res.Assert(t, icmd.Expected{Out: projectName + "_dbnet"})
|
||||
res.Assert(t, icmd.Expected{Out: "microservices"})
|
||||
res = c.RunDockerCmd(t, "network", "ls")
|
||||
res.Assert(t, icmd.Expected{Out: projectName + "_dbnet"})
|
||||
res.Assert(t, icmd.Expected{Out: "microservices"})
|
||||
})
|
||||
|
||||
res = c.RunDockerComposeCmd(t, "port", "words", "8080")
|
||||
res.Assert(t, icmd.Expected{Out: `0.0.0.0:8080`})
|
||||
t.Run("port", func(t *testing.T) {
|
||||
res := c.RunDockerComposeCmd(t, "--project-name", projectName, "port", "words", "8080")
|
||||
res.Assert(t, icmd.Expected{Out: `0.0.0.0:8080`})
|
||||
})
|
||||
|
||||
c.RunDockerComposeCmd(t, "down", "-t0", "-v")
|
||||
res = c.RunDockerCmd(t, "network", "ls")
|
||||
assert.Assert(t, !strings.Contains(res.Combined(), projectName), res.Combined())
|
||||
assert.Assert(t, !strings.Contains(res.Combined(), "microservices"), res.Combined())
|
||||
t.Run("down", func(t *testing.T) {
|
||||
_ = c.RunDockerComposeCmd(t, "--project-name", projectName, "down")
|
||||
})
|
||||
|
||||
t.Run("check networks after down", func(t *testing.T) {
|
||||
res := c.RunDockerCmd(t, "network", "ls")
|
||||
assert.Assert(t, !strings.Contains(res.Combined(), projectName), res.Combined())
|
||||
assert.Assert(t, !strings.Contains(res.Combined(), "microservices"), res.Combined())
|
||||
})
|
||||
}
|
||||
|
||||
func TestNetworkAliases(t *testing.T) {
|
||||
|
||||
@@ -21,8 +21,6 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"gotest.tools/v3/icmd"
|
||||
|
||||
"gotest.tools/v3/assert"
|
||||
)
|
||||
|
||||
@@ -30,12 +28,6 @@ func TestWaitOnFaster(t *testing.T) {
|
||||
const projectName = "e2e-wait-faster"
|
||||
c := NewParallelCLI(t)
|
||||
|
||||
cleanup := func() {
|
||||
c.RunDockerComposeCmd(t, "--project-name", projectName, "down", "--timeout=0", "--remove-orphans")
|
||||
}
|
||||
t.Cleanup(cleanup)
|
||||
cleanup()
|
||||
|
||||
c.RunDockerComposeCmd(t, "-f", "./fixtures/wait/compose.yaml", "--project-name", projectName, "up", "-d")
|
||||
c.RunDockerComposeCmd(t, "--project-name", projectName, "wait", "faster")
|
||||
}
|
||||
@@ -44,12 +36,6 @@ func TestWaitOnSlower(t *testing.T) {
|
||||
const projectName = "e2e-wait-slower"
|
||||
c := NewParallelCLI(t)
|
||||
|
||||
cleanup := func() {
|
||||
c.RunDockerComposeCmd(t, "--project-name", projectName, "down", "--timeout=0", "--remove-orphans")
|
||||
}
|
||||
t.Cleanup(cleanup)
|
||||
cleanup()
|
||||
|
||||
c.RunDockerComposeCmd(t, "-f", "./fixtures/wait/compose.yaml", "--project-name", projectName, "up", "-d")
|
||||
c.RunDockerComposeCmd(t, "--project-name", projectName, "wait", "slower")
|
||||
}
|
||||
@@ -58,27 +44,12 @@ func TestWaitOnInfinity(t *testing.T) {
|
||||
const projectName = "e2e-wait-infinity"
|
||||
c := NewParallelCLI(t)
|
||||
|
||||
cleanup := func() {
|
||||
c.RunDockerComposeCmd(t, "--project-name", projectName, "down", "--timeout=0", "--remove-orphans")
|
||||
}
|
||||
t.Cleanup(cleanup)
|
||||
cleanup()
|
||||
|
||||
c.RunDockerComposeCmd(t, "-f", "./fixtures/wait/compose.yaml", "--project-name", projectName, "up", "-d")
|
||||
|
||||
cmd := c.NewDockerComposeCmd(t, "--project-name", projectName, "wait", "infinity")
|
||||
r := icmd.StartCmd(cmd)
|
||||
assert.NilError(t, r.Error)
|
||||
t.Cleanup(func() {
|
||||
if r.Cmd.Process != nil {
|
||||
_ = r.Cmd.Process.Kill()
|
||||
}
|
||||
})
|
||||
|
||||
finished := make(chan struct{})
|
||||
ticker := time.NewTicker(7 * time.Second)
|
||||
go func() {
|
||||
_ = r.Cmd.Wait()
|
||||
c.RunDockerComposeCmd(t, "--project-name", projectName, "wait", "infinity")
|
||||
finished <- struct{}{}
|
||||
}()
|
||||
|
||||
@@ -93,12 +64,6 @@ func TestWaitAndDrop(t *testing.T) {
|
||||
const projectName = "e2e-wait-and-drop"
|
||||
c := NewParallelCLI(t)
|
||||
|
||||
cleanup := func() {
|
||||
c.RunDockerComposeCmd(t, "--project-name", projectName, "down", "--timeout=0", "--remove-orphans")
|
||||
}
|
||||
t.Cleanup(cleanup)
|
||||
cleanup()
|
||||
|
||||
c.RunDockerComposeCmd(t, "-f", "./fixtures/wait/compose.yaml", "--project-name", projectName, "up", "-d")
|
||||
c.RunDockerComposeCmd(t, "--project-name", projectName, "wait", "--down-project", "faster")
|
||||
|
||||
|
||||
@@ -1,184 +0,0 @@
|
||||
/*
|
||||
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 (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
|
||||
"github.com/distribution/distribution/v3/uuid"
|
||||
"github.com/stretchr/testify/require"
|
||||
"gotest.tools/v3/assert"
|
||||
"gotest.tools/v3/assert/cmp"
|
||||
"gotest.tools/v3/icmd"
|
||||
"gotest.tools/v3/poll"
|
||||
)
|
||||
|
||||
func TestWatch(t *testing.T) {
|
||||
services := []string{"alpine", "busybox", "debian"}
|
||||
for _, svcName := range services {
|
||||
t.Run(svcName, func(t *testing.T) {
|
||||
t.Helper()
|
||||
doTest(t, svcName)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: these tests all share a single Compose file but are safe to run concurrently
|
||||
func doTest(t *testing.T, svcName string) {
|
||||
tmpdir := t.TempDir()
|
||||
dataDir := filepath.Join(tmpdir, "data")
|
||||
writeDataFile := func(name string, contents string) {
|
||||
t.Helper()
|
||||
dest := filepath.Join(dataDir, name)
|
||||
require.NoError(t, os.MkdirAll(filepath.Dir(dest), 0o700))
|
||||
t.Logf("writing %q to %q", contents, dest)
|
||||
require.NoError(t, os.WriteFile(dest, []byte(contents+"\n"), 0o600))
|
||||
}
|
||||
|
||||
composeFilePath := filepath.Join(tmpdir, "compose.yaml")
|
||||
CopyFile(t, filepath.Join("fixtures", "watch", "compose.yaml"), composeFilePath)
|
||||
|
||||
projName := "e2e-watch-" + svcName
|
||||
env := []string{
|
||||
"COMPOSE_FILE=" + composeFilePath,
|
||||
"COMPOSE_PROJECT_NAME=" + projName,
|
||||
}
|
||||
|
||||
cli := NewParallelCLI(t, WithEnv(env...))
|
||||
|
||||
cleanup := func() {
|
||||
cli.RunDockerComposeCmd(t, "down", svcName, "--timeout=0", "--remove-orphans", "--volumes")
|
||||
}
|
||||
cleanup()
|
||||
t.Cleanup(cleanup)
|
||||
|
||||
cli.RunDockerComposeCmd(t, "up", svcName, "--wait", "--build")
|
||||
|
||||
cmd := cli.NewDockerComposeCmd(t, "--verbose", "alpha", "watch", svcName)
|
||||
// stream output since watch runs in the background
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
r := icmd.StartCmd(cmd)
|
||||
require.NoError(t, r.Error)
|
||||
t.Cleanup(func() {
|
||||
// IMPORTANT: watch doesn't exit on its own, don't leak processes!
|
||||
if r.Cmd.Process != nil {
|
||||
_ = r.Cmd.Process.Kill()
|
||||
}
|
||||
})
|
||||
var testComplete atomic.Bool
|
||||
go func() {
|
||||
// if the process exits abnormally before the test is done, fail the test
|
||||
if err := r.Cmd.Wait(); err != nil && !testComplete.Load() {
|
||||
assert.Check(t, cmp.Nil(err))
|
||||
}
|
||||
}()
|
||||
|
||||
require.NoError(t, os.Mkdir(dataDir, 0o700))
|
||||
|
||||
checkFileContents := func(path string, contents string) poll.Check {
|
||||
return func(pollLog poll.LogT) poll.Result {
|
||||
if r.Cmd.ProcessState != nil {
|
||||
return poll.Error(fmt.Errorf("watch process exited early: %s", r.Cmd.ProcessState))
|
||||
}
|
||||
res := icmd.RunCmd(cli.NewDockerComposeCmd(t, "exec", svcName, "cat", path))
|
||||
if strings.Contains(res.Stdout(), contents) {
|
||||
return poll.Success()
|
||||
}
|
||||
return poll.Continue(res.Combined())
|
||||
}
|
||||
}
|
||||
|
||||
waitForFlush := func() {
|
||||
sentinelVal := uuid.Generate().String()
|
||||
writeDataFile("wait.txt", sentinelVal)
|
||||
poll.WaitOn(t, checkFileContents("/app/data/wait.txt", sentinelVal))
|
||||
}
|
||||
|
||||
t.Logf("Writing to a file until Compose watch is up and running")
|
||||
poll.WaitOn(t, func(t poll.LogT) poll.Result {
|
||||
writeDataFile("hello.txt", "hello world")
|
||||
return checkFileContents("/app/data/hello.txt", "hello world")(t)
|
||||
})
|
||||
|
||||
t.Logf("Modifying file contents")
|
||||
writeDataFile("hello.txt", "hello watch")
|
||||
poll.WaitOn(t, checkFileContents("/app/data/hello.txt", "hello watch"))
|
||||
|
||||
t.Logf("Deleting file")
|
||||
require.NoError(t, os.Remove(filepath.Join(dataDir, "hello.txt")))
|
||||
waitForFlush()
|
||||
cli.RunDockerComposeCmdNoCheck(t, "exec", svcName, "stat", "/app/data/hello.txt").
|
||||
Assert(t, icmd.Expected{
|
||||
ExitCode: 1,
|
||||
Err: "No such file or directory",
|
||||
},
|
||||
)
|
||||
|
||||
t.Logf("Writing to ignored paths")
|
||||
writeDataFile("data.foo", "ignored")
|
||||
writeDataFile(filepath.Join("ignored", "hello.txt"), "ignored")
|
||||
waitForFlush()
|
||||
cli.RunDockerComposeCmdNoCheck(t, "exec", svcName, "stat", "/app/data/data.foo").
|
||||
Assert(t, icmd.Expected{
|
||||
ExitCode: 1,
|
||||
Err: "No such file or directory",
|
||||
},
|
||||
)
|
||||
cli.RunDockerComposeCmdNoCheck(t, "exec", svcName, "stat", "/app/data/ignored").
|
||||
Assert(t, icmd.Expected{
|
||||
ExitCode: 1,
|
||||
Err: "No such file or directory",
|
||||
},
|
||||
)
|
||||
|
||||
t.Logf("Creating subdirectory")
|
||||
require.NoError(t, os.Mkdir(filepath.Join(dataDir, "subdir"), 0o700))
|
||||
waitForFlush()
|
||||
cli.RunDockerComposeCmd(t, "exec", svcName, "stat", "/app/data/subdir")
|
||||
|
||||
t.Logf("Writing to file in subdirectory")
|
||||
writeDataFile(filepath.Join("subdir", "file.txt"), "a")
|
||||
poll.WaitOn(t, checkFileContents("/app/data/subdir/file.txt", "a"))
|
||||
|
||||
t.Logf("Writing to file multiple times")
|
||||
writeDataFile(filepath.Join("subdir", "file.txt"), "x")
|
||||
writeDataFile(filepath.Join("subdir", "file.txt"), "y")
|
||||
writeDataFile(filepath.Join("subdir", "file.txt"), "z")
|
||||
poll.WaitOn(t, checkFileContents("/app/data/subdir/file.txt", "z"))
|
||||
writeDataFile(filepath.Join("subdir", "file.txt"), "z")
|
||||
writeDataFile(filepath.Join("subdir", "file.txt"), "y")
|
||||
writeDataFile(filepath.Join("subdir", "file.txt"), "x")
|
||||
poll.WaitOn(t, checkFileContents("/app/data/subdir/file.txt", "x"))
|
||||
|
||||
t.Logf("Deleting directory")
|
||||
require.NoError(t, os.RemoveAll(filepath.Join(dataDir, "subdir")))
|
||||
waitForFlush()
|
||||
cli.RunDockerComposeCmdNoCheck(t, "exec", svcName, "stat", "/app/data/subdir").
|
||||
Assert(t, icmd.Expected{
|
||||
ExitCode: 1,
|
||||
Err: "No such file or directory",
|
||||
},
|
||||
)
|
||||
|
||||
testComplete.Store(true)
|
||||
}
|
||||
@@ -47,9 +47,3 @@ func (s Set[T]) Elements() []T {
|
||||
}
|
||||
return elements
|
||||
}
|
||||
|
||||
func (s Set[T]) RemoveAll(elements ...T) {
|
||||
for _, e := range elements {
|
||||
s.Remove(e)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ func Contains[T any](origin []T, element T) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// Remove removes all elements from origin slice
|
||||
// RemoveAll removes all elements from origin slice
|
||||
func Remove[T any](origin []T, elements ...T) []T {
|
||||
var filtered []T
|
||||
for _, v := range origin {
|
||||
|
||||
Reference in New Issue
Block a user