mirror of
https://github.com/docker/compose.git
synced 2026-02-10 10:39:23 +08:00
Compare commits
56 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e2f33af831 | ||
|
|
19b9fdf536 | ||
|
|
a842522f49 | ||
|
|
bc1160de72 | ||
|
|
f791bc8a42 | ||
|
|
0d7567131a | ||
|
|
7b84f2c2a5 | ||
|
|
cf7b1441d9 | ||
|
|
32005b0bfe | ||
|
|
025a72a417 | ||
|
|
95c4502b81 | ||
|
|
2290ce2c24 | ||
|
|
bac732837e | ||
|
|
b725c56c42 | ||
|
|
cfcc9533b3 | ||
|
|
cffdb69c5e | ||
|
|
709190312c | ||
|
|
e1a38f984b | ||
|
|
4dafeb57a5 | ||
|
|
45956c36fb | ||
|
|
9eb69465b7 | ||
|
|
5f392258cb | ||
|
|
382c1cd68e | ||
|
|
5994050f51 | ||
|
|
28a00571ef | ||
|
|
d00eacbba0 | ||
|
|
a6c76a9c0f | ||
|
|
5754d6084c | ||
|
|
10cd7e130f | ||
|
|
8f9dc2e7f8 | ||
|
|
dfa93d834f | ||
|
|
f69a613e69 | ||
|
|
a8fbbd9e5c | ||
|
|
ba724576a1 | ||
|
|
3261b60fd1 | ||
|
|
cb425a23c0 | ||
|
|
29179840c3 | ||
|
|
7205d918ad | ||
|
|
413f46ade0 | ||
|
|
8a9c4b52b4 | ||
|
|
865b82da6a | ||
|
|
e44222664a | ||
|
|
d4e3f191ac | ||
|
|
577bee955b | ||
|
|
ed2395819d | ||
|
|
e6599c7213 | ||
|
|
9c01e41adf | ||
|
|
6df30f39f2 | ||
|
|
a79346b978 | ||
|
|
125752c127 | ||
|
|
95f0431127 | ||
|
|
2bee75c3c4 | ||
|
|
a1f7be7b5c | ||
|
|
72e4519cbf | ||
|
|
9e6f51d262 | ||
|
|
fc827f295b |
5
.github/workflows/artifacts.yml
vendored
5
.github/workflows/artifacts.yml
vendored
@@ -7,10 +7,10 @@ jobs:
|
||||
if: github.event.issue.pull_request != '' && contains(github.event.comment.body, '/generate-artifacts')
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Set up Go 1.16
|
||||
- name: Set up Go 1.17
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.16
|
||||
go-version: 1.17
|
||||
id: go
|
||||
|
||||
- name: Checkout code into the Go module directory
|
||||
@@ -55,4 +55,3 @@ jobs:
|
||||
body: |
|
||||
This PR can be tested using [binaries](https://github.com/docker/compose-cli/actions/runs/${{ github.run_id }}).
|
||||
reactions: eyes
|
||||
|
||||
|
||||
12
.github/workflows/ci.yml
vendored
12
.github/workflows/ci.yml
vendored
@@ -13,10 +13,10 @@ jobs:
|
||||
env:
|
||||
GO111MODULE: "on"
|
||||
steps:
|
||||
- name: Set up Go 1.16
|
||||
- name: Set up Go 1.17
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.16
|
||||
go-version: 1.17
|
||||
id: go
|
||||
|
||||
- name: Checkout code into the Go module directory
|
||||
@@ -40,10 +40,10 @@ jobs:
|
||||
env:
|
||||
GO111MODULE: "on"
|
||||
steps:
|
||||
- name: Set up Go 1.16
|
||||
- name: Set up Go 1.17
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.16
|
||||
go-version: 1.17
|
||||
id: go
|
||||
|
||||
- name: Checkout code into the Go module directory
|
||||
@@ -65,10 +65,10 @@ jobs:
|
||||
env:
|
||||
GO111MODULE: "on"
|
||||
steps:
|
||||
- name: Set up Go 1.16
|
||||
- name: Set up Go 1.17
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.16
|
||||
go-version: 1.17
|
||||
id: go
|
||||
|
||||
- name: Set up gosum
|
||||
|
||||
6
.github/workflows/release.yaml
vendored
6
.github/workflows/release.yaml
vendored
@@ -4,17 +4,17 @@ on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
tag:
|
||||
description: 'Release Tag'
|
||||
description: "Release Tag"
|
||||
required: true
|
||||
|
||||
jobs:
|
||||
upload-release:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Set up Go 1.16
|
||||
- name: Set up Go 1.17
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.16
|
||||
go-version: 1.17
|
||||
id: go
|
||||
|
||||
- name: Setup docker CLI
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
ARG GO_VERSION=1.16-alpine
|
||||
ARG GO_VERSION=1.17-alpine
|
||||
ARG GOLANGCI_LINT_VERSION=v1.40.1-alpine
|
||||
ARG PROTOC_GEN_GO_VERSION=v1.4.3
|
||||
|
||||
|
||||
@@ -24,8 +24,8 @@
|
||||
people = [
|
||||
"rumpl",
|
||||
"gtardif",
|
||||
"ndeloof"
|
||||
"chris-crone"
|
||||
"ndeloof",
|
||||
"chris-crone",
|
||||
"ulyssessouza"
|
||||
]
|
||||
|
||||
|
||||
@@ -20,9 +20,12 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/compose-spec/compose-go/cli"
|
||||
"github.com/compose-spec/compose-go/types"
|
||||
buildx "github.com/docker/buildx/util/progress"
|
||||
"github.com/docker/compose/v2/pkg/utils"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
@@ -39,6 +42,13 @@ type buildOptions struct {
|
||||
memory string
|
||||
}
|
||||
|
||||
var printerModes = []string{
|
||||
buildx.PrinterModeAuto,
|
||||
buildx.PrinterModeTty,
|
||||
buildx.PrinterModePlain,
|
||||
buildx.PrinterModeQuiet,
|
||||
}
|
||||
|
||||
func buildCommand(p *projectOptions, backend api.Service) *cobra.Command {
|
||||
opts := buildOptions{
|
||||
projectOptions: p,
|
||||
@@ -51,12 +61,16 @@ func buildCommand(p *projectOptions, backend api.Service) *cobra.Command {
|
||||
fmt.Println("WARNING --memory is ignored as not supported in buildkit.")
|
||||
}
|
||||
if opts.quiet {
|
||||
opts.progress = buildx.PrinterModeQuiet
|
||||
devnull, err := os.Open(os.DevNull)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
os.Stdout = devnull
|
||||
}
|
||||
if !utils.StringContains(printerModes, opts.progress) {
|
||||
return fmt.Errorf("unsupported --progress value %q", opts.progress)
|
||||
}
|
||||
return nil
|
||||
}),
|
||||
RunE: Adapt(func(ctx context.Context, args []string) error {
|
||||
@@ -66,7 +80,7 @@ func buildCommand(p *projectOptions, backend api.Service) *cobra.Command {
|
||||
}
|
||||
cmd.Flags().BoolVarP(&opts.quiet, "quiet", "q", false, "Don't print anything to STDOUT")
|
||||
cmd.Flags().BoolVar(&opts.pull, "pull", false, "Always attempt to pull a newer version of the image.")
|
||||
cmd.Flags().StringVar(&opts.progress, "progress", "auto", `Set type of progress output ("auto", "plain", "noTty")`)
|
||||
cmd.Flags().StringVar(&opts.progress, "progress", buildx.PrinterModeAuto, fmt.Sprintf(`Set type of progress output (%s)`, strings.Join(printerModes, ", ")))
|
||||
cmd.Flags().StringArrayVar(&opts.args, "build-arg", []string{}, "Set build-time variables for services.")
|
||||
cmd.Flags().Bool("parallel", true, "Build images in parallel. DEPRECATED")
|
||||
cmd.Flags().MarkHidden("parallel") //nolint:errcheck
|
||||
|
||||
@@ -212,11 +212,11 @@ func (o *projectOptions) toProjectOptions(po ...cli.ProjectOptionsFn) (*cli.Proj
|
||||
cli.WithName(o.ProjectName))...)
|
||||
}
|
||||
|
||||
const pluginName = "compose"
|
||||
const PluginName = "compose"
|
||||
|
||||
// RunningAsStandalone detects when running as a standalone program
|
||||
func RunningAsStandalone() bool {
|
||||
return len(os.Args) < 2 || os.Args[1] != manager.MetadataSubcommandName && os.Args[1] != pluginName
|
||||
return len(os.Args) < 2 || os.Args[1] != manager.MetadataSubcommandName && os.Args[1] != PluginName
|
||||
}
|
||||
|
||||
// RootCommand returns the compose command with its child commands
|
||||
@@ -230,7 +230,7 @@ func RootCommand(backend api.Service) *cobra.Command {
|
||||
)
|
||||
command := &cobra.Command{
|
||||
Short: "Docker Compose",
|
||||
Use: pluginName,
|
||||
Use: PluginName,
|
||||
TraverseChildren: true,
|
||||
// By default (no Run/RunE in parent command) for typos in subcommands, cobra displays the help of parent command but exit(0) !
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
|
||||
@@ -44,9 +44,11 @@ type convertOptions struct {
|
||||
quiet bool
|
||||
resolveImageDigests bool
|
||||
noInterpolate bool
|
||||
noNormalize bool
|
||||
services bool
|
||||
volumes bool
|
||||
profiles bool
|
||||
images bool
|
||||
hash string
|
||||
}
|
||||
|
||||
@@ -66,6 +68,9 @@ func convertCommand(p *projectOptions, backend api.Service) *cobra.Command {
|
||||
}
|
||||
os.Stdout = devnull
|
||||
}
|
||||
if p.Compatibility {
|
||||
opts.noNormalize = true
|
||||
}
|
||||
return nil
|
||||
}),
|
||||
RunE: Adapt(func(ctx context.Context, args []string) error {
|
||||
@@ -81,6 +86,9 @@ func convertCommand(p *projectOptions, backend api.Service) *cobra.Command {
|
||||
if opts.profiles {
|
||||
return runProfiles(opts, args)
|
||||
}
|
||||
if opts.images {
|
||||
return runConfigImages(opts, args)
|
||||
}
|
||||
|
||||
return runConvert(ctx, backend, opts, args)
|
||||
}),
|
||||
@@ -91,10 +99,12 @@ func convertCommand(p *projectOptions, backend api.Service) *cobra.Command {
|
||||
flags.BoolVar(&opts.resolveImageDigests, "resolve-image-digests", false, "Pin image tags to 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.")
|
||||
|
||||
flags.BoolVar(&opts.services, "services", false, "Print the service names, one per line.")
|
||||
flags.BoolVar(&opts.volumes, "volumes", false, "Print the volume names, one per line.")
|
||||
flags.BoolVar(&opts.profiles, "profiles", false, "Print the profile names, one per line.")
|
||||
flags.BoolVar(&opts.images, "images", false, "Print the image names, one per line.")
|
||||
flags.StringVar(&opts.hash, "hash", "", "Print the service config hash, one per line.")
|
||||
flags.StringVarP(&opts.Output, "output", "o", "", "Save to file (default to stdout)")
|
||||
|
||||
@@ -103,7 +113,10 @@ func convertCommand(p *projectOptions, backend api.Service) *cobra.Command {
|
||||
|
||||
func runConvert(ctx context.Context, backend api.Service, opts convertOptions, services []string) error {
|
||||
var json []byte
|
||||
project, err := opts.toProject(services, cli.WithInterpolation(!opts.noInterpolate), cli.WithResolvedPaths(true))
|
||||
project, err := opts.toProject(services,
|
||||
cli.WithInterpolation(!opts.noInterpolate),
|
||||
cli.WithResolvedPaths(true),
|
||||
cli.WithNormalization(!opts.noNormalize))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -207,3 +220,18 @@ func runProfiles(opts convertOptions, services []string) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func runConfigImages(opts convertOptions, services []string) error {
|
||||
project, err := opts.toProject(services)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, s := range project.Services {
|
||||
if s.Image != "" {
|
||||
fmt.Println(s.Image)
|
||||
} else {
|
||||
fmt.Printf("%s_%s\n", project.Name, s.Name)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -21,11 +21,12 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/compose-spec/compose-go/types"
|
||||
"github.com/containerd/console"
|
||||
"github.com/docker/cli/cli"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
"github.com/docker/compose/v2/pkg/compose"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type execOpts struct {
|
||||
@@ -77,15 +78,22 @@ func execCommand(p *projectOptions, backend api.Service) *cobra.Command {
|
||||
}
|
||||
|
||||
func runExec(ctx context.Context, backend api.Service, opts execOpts) error {
|
||||
project, err := opts.toProjectName()
|
||||
projectName, err := opts.toProjectName()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
projectOptions, err := opts.composeOptions.toProjectOptions()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
lookupFn := func(k string) (string, bool) {
|
||||
v, ok := projectOptions.Environment[k]
|
||||
return v, ok
|
||||
}
|
||||
execOpts := api.RunOptions{
|
||||
Service: opts.service,
|
||||
Command: opts.command,
|
||||
Environment: opts.environment,
|
||||
Environment: compose.ToMobyEnv(types.NewMappingWithEquals(opts.environment).Resolve(lookupFn)),
|
||||
Tty: !opts.noTty,
|
||||
User: opts.user,
|
||||
Privileged: opts.privileged,
|
||||
@@ -113,7 +121,7 @@ func runExec(ctx context.Context, backend api.Service, opts execOpts) error {
|
||||
execOpts.Stdout = con
|
||||
execOpts.Stderr = con
|
||||
}
|
||||
exitCode, err := backend.Exec(ctx, project, execOpts)
|
||||
exitCode, err := backend.Exec(ctx, projectName, execOpts)
|
||||
if exitCode != 0 {
|
||||
errMsg := ""
|
||||
if err != nil {
|
||||
|
||||
@@ -54,6 +54,7 @@ type runOptions struct {
|
||||
servicePorts bool
|
||||
name string
|
||||
noDeps bool
|
||||
quietPull bool
|
||||
}
|
||||
|
||||
func (opts runOptions) apply(project *types.Project) error {
|
||||
@@ -153,6 +154,7 @@ func runCommand(p *projectOptions, backend api.Service) *cobra.Command {
|
||||
flags.StringArrayVarP(&opts.publish, "publish", "p", []string{}, "Publish a container's port(s) to the host.")
|
||||
flags.BoolVar(&opts.useAliases, "use-aliases", false, "Use the service's network useAliases in the network(s) the container connects to.")
|
||||
flags.BoolVar(&opts.servicePorts, "service-ports", false, "Run command with the service's ports enabled and mapped to the host.")
|
||||
flags.BoolVar(&opts.quietPull, "quiet-pull", false, "Pull without printing progress information.")
|
||||
|
||||
flags.SetNormalizeFunc(normalizeRunFlags)
|
||||
flags.SetInterspersed(false)
|
||||
@@ -215,6 +217,7 @@ func runRun(ctx context.Context, backend api.Service, project *types.Project, op
|
||||
UseNetworkAliases: opts.useAliases,
|
||||
NoDeps: opts.noDeps,
|
||||
Index: 0,
|
||||
QuietPull: opts.quietPull,
|
||||
}
|
||||
exitCode, err := backend.RunOneOffContainer(ctx, project, runOpts)
|
||||
if exitCode != 0 {
|
||||
|
||||
@@ -40,7 +40,6 @@ type composeOptions struct {
|
||||
type upOptions struct {
|
||||
*composeOptions
|
||||
Detach bool
|
||||
Environment []string
|
||||
noStart bool
|
||||
noDeps bool
|
||||
cascadeStop bool
|
||||
@@ -50,6 +49,7 @@ type upOptions struct {
|
||||
noPrefix bool
|
||||
attachDependencies bool
|
||||
attach []string
|
||||
wait bool
|
||||
}
|
||||
|
||||
func (opts upOptions) apply(project *types.Project, services []string) error {
|
||||
@@ -100,22 +100,7 @@ func upCommand(p *projectOptions, backend api.Service) *cobra.Command {
|
||||
Short: "Create and start containers",
|
||||
PreRunE: AdaptCmd(func(ctx context.Context, cmd *cobra.Command, args []string) error {
|
||||
create.timeChanged = cmd.Flags().Changed("timeout")
|
||||
if up.exitCodeFrom != "" {
|
||||
up.cascadeStop = true
|
||||
}
|
||||
if create.Build && create.noBuild {
|
||||
return fmt.Errorf("--build and --no-build are incompatible")
|
||||
}
|
||||
if up.Detach && (up.attachDependencies || up.cascadeStop || len(up.attach) > 0) {
|
||||
return fmt.Errorf("--detach cannot be combined with --abort-on-container-exit, --attach or --attach-dependencies")
|
||||
}
|
||||
if create.forceRecreate && create.noRecreate {
|
||||
return fmt.Errorf("--force-recreate and --no-recreate are incompatible")
|
||||
}
|
||||
if create.recreateDeps && create.noRecreate {
|
||||
return fmt.Errorf("--always-recreate-deps and --no-recreate are incompatible")
|
||||
}
|
||||
return nil
|
||||
return validateFlags(&up, &create)
|
||||
}),
|
||||
RunE: p.WithServices(func(ctx context.Context, project *types.Project, services []string) error {
|
||||
ignore := project.Environment["COMPOSE_IGNORE_ORPHANS"]
|
||||
@@ -128,7 +113,6 @@ func upCommand(p *projectOptions, backend api.Service) *cobra.Command {
|
||||
ValidArgsFunction: serviceCompletion(p),
|
||||
}
|
||||
flags := upCmd.Flags()
|
||||
flags.StringArrayVarP(&up.Environment, "environment", "e", []string{}, "Environment variables")
|
||||
flags.BoolVarP(&up.Detach, "detach", "d", false, "Detached mode: Run containers in the background")
|
||||
flags.BoolVar(&create.Build, "build", false, "Build images before starting containers.")
|
||||
flags.BoolVar(&create.noBuild, "no-build", false, "Don't build an image, even if it's missing.")
|
||||
@@ -148,10 +132,36 @@ func upCommand(p *projectOptions, backend api.Service) *cobra.Command {
|
||||
flags.BoolVar(&up.attachDependencies, "attach-dependencies", false, "Attach to dependent containers.")
|
||||
flags.BoolVar(&create.quietPull, "quiet-pull", false, "Pull without printing progress information.")
|
||||
flags.StringArrayVar(&up.attach, "attach", []string{}, "Attach to service output.")
|
||||
flags.BoolVar(&up.wait, "wait", false, "Wait for services to be running|healthy. Implies detached mode.")
|
||||
|
||||
return upCmd
|
||||
}
|
||||
|
||||
func validateFlags(up *upOptions, create *createOptions) error {
|
||||
if up.exitCodeFrom != "" {
|
||||
up.cascadeStop = true
|
||||
}
|
||||
if up.wait {
|
||||
if up.attachDependencies || up.cascadeStop || len(up.attach) > 0 {
|
||||
return fmt.Errorf("--wait cannot be combined with --abort-on-container-exit, --attach or --attach-dependencies")
|
||||
}
|
||||
up.Detach = true
|
||||
}
|
||||
if create.Build && create.noBuild {
|
||||
return fmt.Errorf("--build and --no-build are incompatible")
|
||||
}
|
||||
if up.Detach && (up.attachDependencies || up.cascadeStop || len(up.attach) > 0) {
|
||||
return fmt.Errorf("--detach cannot be combined with --abort-on-container-exit, --attach or --attach-dependencies")
|
||||
}
|
||||
if create.forceRecreate && create.noRecreate {
|
||||
return fmt.Errorf("--force-recreate and --no-recreate are incompatible")
|
||||
}
|
||||
if create.recreateDeps && create.noRecreate {
|
||||
return fmt.Errorf("--always-recreate-deps and --no-recreate are incompatible")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func runUp(ctx context.Context, backend api.Service, createOptions createOptions, upOptions upOptions, project *types.Project, services []string) error {
|
||||
if len(project.Services) == 0 {
|
||||
return fmt.Errorf("no service selected")
|
||||
@@ -199,6 +209,7 @@ func runUp(ctx context.Context, backend api.Service, createOptions createOptions
|
||||
AttachTo: attachTo,
|
||||
ExitCodeFrom: upOptions.exitCodeFrom,
|
||||
CascadeStop: upOptions.cascadeStop,
|
||||
Wait: upOptions.wait,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
96
cmd/convert.go
Normal file
96
cmd/convert.go
Normal file
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
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"
|
||||
|
||||
"github.com/docker/compose/v2/cmd/compose"
|
||||
)
|
||||
|
||||
func getBoolFlags() []string {
|
||||
return []string{
|
||||
"--debug", "-D",
|
||||
"--verbose",
|
||||
"--tls",
|
||||
"--tlsverify",
|
||||
}
|
||||
}
|
||||
|
||||
func getStringFlags() []string {
|
||||
return []string{
|
||||
"--tlscacert",
|
||||
"--tlscert",
|
||||
"--tlskey",
|
||||
"--host", "-H",
|
||||
"--context",
|
||||
"--log-level",
|
||||
}
|
||||
}
|
||||
|
||||
func convert(args []string) []string {
|
||||
var rootFlags []string
|
||||
command := []string{compose.PluginName}
|
||||
l := len(args)
|
||||
for i := 0; i < l; i++ {
|
||||
arg := args[i]
|
||||
if arg[0] != '-' {
|
||||
// not a top-level flag anymore, keep the rest of the command unmodified
|
||||
if arg == compose.PluginName {
|
||||
i++
|
||||
}
|
||||
command = append(command, args[i:]...)
|
||||
break
|
||||
}
|
||||
if arg == "--verbose" {
|
||||
arg = "--debug"
|
||||
}
|
||||
if arg == "-h" {
|
||||
// docker cli has deprecated -h to avoid ambiguity with -H, while docker-compose still support it
|
||||
arg = "--help"
|
||||
}
|
||||
if arg == "--version" || arg == "-v" {
|
||||
// redirect --version pseudo-command to actual command
|
||||
arg = "version"
|
||||
}
|
||||
if contains(getBoolFlags(), arg) {
|
||||
rootFlags = append(rootFlags, arg)
|
||||
continue
|
||||
}
|
||||
if contains(getStringFlags(), arg) {
|
||||
i++
|
||||
if i >= l {
|
||||
fmt.Fprintf(os.Stderr, "flag needs an argument: '%s'\n", arg)
|
||||
os.Exit(1)
|
||||
}
|
||||
rootFlags = append(rootFlags, arg, args[i])
|
||||
continue
|
||||
}
|
||||
command = append(command, arg)
|
||||
}
|
||||
return append(rootFlags, command...)
|
||||
}
|
||||
|
||||
func contains(array []string, needle string) bool {
|
||||
for _, val := range array {
|
||||
if val == needle {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
78
cmd/convert_test.go
Normal file
78
cmd/convert_test.go
Normal file
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
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 (
|
||||
"testing"
|
||||
|
||||
"gotest.tools/v3/assert"
|
||||
)
|
||||
|
||||
func Test_convert(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
args []string
|
||||
want []string
|
||||
}{
|
||||
{
|
||||
name: "compose only",
|
||||
args: []string{"up"},
|
||||
want: []string{"compose", "up"},
|
||||
},
|
||||
{
|
||||
name: "with context",
|
||||
args: []string{"--context", "foo", "-f", "compose.yaml", "up"},
|
||||
want: []string{"--context", "foo", "compose", "-f", "compose.yaml", "up"},
|
||||
},
|
||||
{
|
||||
name: "with host",
|
||||
args: []string{"--host", "tcp://1.2.3.4", "up"},
|
||||
want: []string{"--host", "tcp://1.2.3.4", "compose", "up"},
|
||||
},
|
||||
{
|
||||
name: "compose --version",
|
||||
args: []string{"--version"},
|
||||
want: []string{"compose", "version"},
|
||||
},
|
||||
{
|
||||
name: "help",
|
||||
args: []string{"-h"},
|
||||
want: []string{"compose", "--help"},
|
||||
},
|
||||
{
|
||||
name: "issues/1962",
|
||||
args: []string{"psql", "-h", "postgres"},
|
||||
want: []string{"compose", "psql", "-h", "postgres"}, // -h should not be converted to --help
|
||||
},
|
||||
{
|
||||
name: "issues/8648",
|
||||
args: []string{"exec", "mongo", "mongo", "--host", "mongo"},
|
||||
want: []string{"compose", "exec", "mongo", "mongo", "--host", "mongo"}, // --host is passed to exec
|
||||
},
|
||||
{
|
||||
name: "issues/12",
|
||||
args: []string{"--log-level", "INFO", "up"},
|
||||
want: []string{"--log-level", "INFO", "compose", "up"},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := convert(tt.args)
|
||||
assert.DeepEqual(t, tt.want, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,6 @@ import (
|
||||
"github.com/docker/cli/cli-plugins/manager"
|
||||
"github.com/docker/cli/cli-plugins/plugin"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/compose-switch/redirect"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
commands "github.com/docker/compose/v2/cmd/compose"
|
||||
@@ -69,7 +68,7 @@ func pluginMain() {
|
||||
|
||||
func main() {
|
||||
if commands.RunningAsStandalone() {
|
||||
os.Args = append([]string{"docker"}, redirect.Convert(os.Args[1:])...)
|
||||
os.Args = append([]string{"docker"}, convert(os.Args[1:])...)
|
||||
}
|
||||
pluginMain()
|
||||
}
|
||||
|
||||
102
go.mod
102
go.mod
@@ -1,47 +1,129 @@
|
||||
module github.com/docker/compose/v2
|
||||
|
||||
go 1.16
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
github.com/AlecAivazis/survey/v2 v2.2.3
|
||||
github.com/buger/goterm v1.0.0
|
||||
github.com/cnabio/cnab-to-oci v0.3.1-beta1
|
||||
github.com/compose-spec/compose-go v1.0.5
|
||||
github.com/compose-spec/compose-go v1.0.8
|
||||
github.com/containerd/console v1.0.2
|
||||
github.com/containerd/containerd v1.5.5
|
||||
github.com/containerd/containerd v1.5.8
|
||||
github.com/distribution/distribution/v3 v3.0.0-20210316161203-a01c71e2477e
|
||||
github.com/docker/buildx v0.5.2-0.20210422185057-908a856079fc
|
||||
github.com/docker/cli v20.10.7+incompatible
|
||||
github.com/docker/cli-docs-tool v0.1.1
|
||||
github.com/docker/compose-switch v1.0.2
|
||||
github.com/docker/docker v20.10.7+incompatible
|
||||
github.com/docker/go-connections v0.4.0
|
||||
github.com/docker/go-units v0.4.0
|
||||
github.com/gofrs/flock v0.8.0 // indirect
|
||||
github.com/golang/mock v1.5.0
|
||||
github.com/hashicorp/go-multierror v1.1.0
|
||||
github.com/hashicorp/go-version v1.3.0
|
||||
github.com/kr/pty v1.1.8 // indirect
|
||||
github.com/mattn/go-colorable v0.1.6 // indirect
|
||||
github.com/mattn/go-isatty v0.0.12
|
||||
github.com/mattn/go-shellwords v1.0.12
|
||||
github.com/moby/buildkit v0.8.2-0.20210401015549-df49b648c8bf
|
||||
github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6
|
||||
github.com/morikuni/aec v1.0.0
|
||||
github.com/opencontainers/go-digest v1.0.0
|
||||
github.com/opencontainers/image-spec v1.0.1
|
||||
github.com/opencontainers/image-spec v1.0.2
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/sanathkr/go-yaml v0.0.0-20170819195128-ed9d249f429b
|
||||
github.com/sergi/go-diff v1.1.0 // indirect
|
||||
github.com/sirupsen/logrus v1.8.1
|
||||
github.com/spf13/cobra v1.2.1
|
||||
github.com/spf13/pflag v1.0.5
|
||||
github.com/stretchr/testify v1.7.0
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
|
||||
gotest.tools v2.2.0+incompatible
|
||||
gotest.tools/v3 v3.0.3
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
|
||||
github.com/Masterminds/semver v1.5.0 // indirect
|
||||
github.com/Microsoft/go-winio v0.4.17 // indirect
|
||||
github.com/Microsoft/hcsshim v0.8.23 // indirect
|
||||
github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.1.1 // indirect
|
||||
github.com/cnabio/cnab-go v0.10.0-beta1 // indirect
|
||||
github.com/compose-spec/godotenv v1.1.1 // indirect
|
||||
github.com/containerd/cgroups v1.0.1 // indirect
|
||||
github.com/containerd/continuity v0.1.0 // indirect
|
||||
github.com/containerd/typeurl v1.0.2 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/docker/distribution v2.7.1+incompatible // indirect
|
||||
github.com/docker/docker-credential-helpers v0.6.4-0.20210125172408-38bea2ce277a // indirect
|
||||
github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c // indirect
|
||||
github.com/docker/go-metrics v0.0.1 // indirect
|
||||
github.com/fvbommel/sortorder v1.0.1 // indirect
|
||||
github.com/go-logr/logr v0.4.0 // indirect
|
||||
github.com/gofrs/flock v0.8.0 // indirect
|
||||
github.com/gogo/googleapis v1.4.0 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/google/go-cmp v0.5.5 // indirect
|
||||
github.com/google/gofuzz v1.1.0 // indirect
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
|
||||
github.com/gorilla/mux v1.8.0 // indirect
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.2.0 // indirect
|
||||
github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645 // indirect
|
||||
github.com/hashicorp/errwrap v1.0.0 // indirect
|
||||
github.com/imdario/mergo v0.3.12 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.0.0 // indirect
|
||||
github.com/jaguilar/vt100 v0.0.0-20150826170717-2703a27b14ea // indirect
|
||||
github.com/json-iterator/go v1.1.11 // indirect
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
|
||||
github.com/klauspost/compress v1.11.13 // indirect
|
||||
github.com/kr/pty v1.1.8 // indirect
|
||||
github.com/mattn/go-colorable v0.1.6 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect
|
||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect
|
||||
github.com/miekg/pkcs11 v1.0.3 // indirect
|
||||
github.com/mitchellh/mapstructure v1.4.2 // indirect
|
||||
github.com/moby/locker v1.0.1 // indirect
|
||||
github.com/moby/sys/mount v0.2.0 // indirect
|
||||
github.com/moby/sys/mountinfo v0.4.1 // indirect
|
||||
github.com/moby/sys/symlink v0.1.0 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.1 // indirect
|
||||
github.com/opencontainers/runc v1.0.2 // indirect
|
||||
github.com/opentracing/opentracing-go v1.2.0 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/prometheus/client_golang v1.7.1 // indirect
|
||||
github.com/prometheus/client_model v0.2.0 // indirect
|
||||
github.com/prometheus/common v0.10.0 // indirect
|
||||
github.com/prometheus/procfs v0.6.0 // indirect
|
||||
github.com/qri-io/jsonpointer v0.1.0 // indirect
|
||||
github.com/qri-io/jsonschema v0.1.1 // indirect
|
||||
github.com/sergi/go-diff v1.1.0 // indirect
|
||||
github.com/theupdateframework/notary v0.6.1 // indirect
|
||||
github.com/tonistiigi/fsutil v0.0.0-20201103201449-0834f99b7b85 // indirect
|
||||
github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea // 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
|
||||
go.opencensus.io v0.23.0 // indirect
|
||||
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 // indirect
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 // indirect
|
||||
golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602 // indirect
|
||||
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22 // indirect
|
||||
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d // indirect
|
||||
golang.org/x/text v0.3.5 // indirect
|
||||
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c // indirect
|
||||
google.golang.org/grpc v1.38.0 // indirect
|
||||
google.golang.org/protobuf v1.27.1 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
|
||||
k8s.io/apimachinery v0.21.0 // indirect
|
||||
k8s.io/client-go v0.21.0 // indirect
|
||||
k8s.io/klog/v2 v2.8.0 // indirect
|
||||
k8s.io/utils v0.0.0-20201110183641-67b214c5f920 // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.1.0 // indirect
|
||||
sigs.k8s.io/yaml v1.2.0 // indirect
|
||||
)
|
||||
|
||||
// (for buildx)
|
||||
|
||||
35
go.sum
35
go.sum
@@ -91,8 +91,8 @@ github.com/Microsoft/hcsshim v0.8.9/go.mod h1:5692vkUqntj1idxauYlpoINNKeqCiG6Sg3
|
||||
github.com/Microsoft/hcsshim v0.8.14/go.mod h1:NtVKoYxQuTLx6gEq0L96c9Ju4JbRJ4nY2ow3VK6a9Lg=
|
||||
github.com/Microsoft/hcsshim v0.8.15/go.mod h1:x38A4YbHbdxJtc0sF6oIz+RG0npwSCAvn69iY6URG00=
|
||||
github.com/Microsoft/hcsshim v0.8.16/go.mod h1:o5/SZqmR7x9JNKsW3pu+nqHm0MF8vbA+VxGOoXdC600=
|
||||
github.com/Microsoft/hcsshim v0.8.18 h1:cYnKADiM1869gvBpos3YCteeT6sZLB48lB5dmMMs8Tg=
|
||||
github.com/Microsoft/hcsshim v0.8.18/go.mod h1:+w2gRZ5ReXQhFOrvSQeNfhrYB/dg3oDwTOcER2fw4I4=
|
||||
github.com/Microsoft/hcsshim v0.8.23 h1:47MSwtKGXet80aIn+7h4YI6fwPmwIghAnsx2aOUrG2M=
|
||||
github.com/Microsoft/hcsshim v0.8.23/go.mod h1:4zegtUJth7lAvFyc6cH2gGQ5B3OFQim01nnU2M8jKDg=
|
||||
github.com/Microsoft/hcsshim/test v0.0.0-20201218223536-d3e5debf77da/go.mod h1:5hlzMzRKMLyo42nCZ9oml8AdTlq/0cvIaBv6tK1RehU=
|
||||
github.com/Microsoft/hcsshim/test v0.0.0-20210227013316-43a75bb4edd3/go.mod h1:mw7qgWloBUl75W/gVH3cQszUg1+gUITj7D6NY7ywVnY=
|
||||
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
|
||||
@@ -163,6 +163,7 @@ github.com/bugsnag/panicwrap v1.2.0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywR
|
||||
github.com/cenkalti/backoff v2.1.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
|
||||
github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4=
|
||||
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
|
||||
github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/certifi/gocertifi v0.0.0-20180118203423-deb3ae2ef261/go.mod h1:GJKEexRPVJrBSOjoqN5VNOIKJ5Q3RViH6eu3puDRwx4=
|
||||
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
|
||||
@@ -196,10 +197,10 @@ github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnht
|
||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
|
||||
github.com/codahale/hdrhistogram v0.0.0-20160425231609-f8ad88b59a58/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=
|
||||
github.com/compose-spec/compose-go v1.0.5 h1:WtfK7tJsk5C8h12iggum7p28kTxeXH7Xi5c/pLfnBwk=
|
||||
github.com/compose-spec/compose-go v1.0.5/go.mod h1:LQ/JAjSIyh8bTu4RV6nkyf0Ow/Yf3qpvzrdEigxduiw=
|
||||
github.com/compose-spec/godotenv v1.1.0 h1:wzShe5P6L/Aw3wsV357eWlZdMcPaOe2V2+3+qGwMEL4=
|
||||
github.com/compose-spec/godotenv v1.1.0/go.mod h1:zF/3BOa18Z24tts5qnO/E9YURQanJTBUf7nlcCTNsyc=
|
||||
github.com/compose-spec/compose-go v1.0.8 h1:fgT7mYYu5Sp37i2lUIAAvwJpkAHk6dP5ITHy/LlutUk=
|
||||
github.com/compose-spec/compose-go v1.0.8/go.mod h1:REnCbBugoIdHB7S1sfkN/aJ7AJpNApGNjNiVjA9L8x4=
|
||||
github.com/compose-spec/godotenv v1.1.1 h1:lp+WpAInnw06YN9sV/XLUOV/9z4C+6wjJdWlrdVac7o=
|
||||
github.com/compose-spec/godotenv v1.1.1/go.mod h1:zF/3BOa18Z24tts5qnO/E9YURQanJTBUf7nlcCTNsyc=
|
||||
github.com/containerd/aufs v0.0.0-20200908144142-dab0cbea06f4/go.mod h1:nukgQABAEopAHvB6j7cnP5zJ+/3aVcE7hCYqvIwAHyE=
|
||||
github.com/containerd/aufs v0.0.0-20201003224125-76a6863f2989/go.mod h1:AkGGQs9NM2vtYHaUen+NljV0/baGCAPELGm2q9ZXpWU=
|
||||
github.com/containerd/aufs v0.0.0-20210316121734-20793ff83c97/go.mod h1:kL5kd6KM5TzQjR79jljyi4olc1Vrx6XBlcyj3gNv2PU=
|
||||
@@ -231,14 +232,14 @@ github.com/containerd/containerd v1.3.2/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMX
|
||||
github.com/containerd/containerd v1.4.0-beta.2.0.20200729163537-40b22ef07410/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
|
||||
github.com/containerd/containerd v1.4.1/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
|
||||
github.com/containerd/containerd v1.4.3/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
|
||||
github.com/containerd/containerd v1.4.9/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
|
||||
github.com/containerd/containerd v1.5.0-beta.0.0.20210122062454-5a66c2ae5cec/go.mod h1:p9z+oqCID32tZ7LKgej316N9pJf1iIkQ/7nK1VvEFZE=
|
||||
github.com/containerd/containerd v1.5.0-beta.1/go.mod h1:5HfvG1V2FsKesEGQ17k5/T7V960Tmcumvqn8Mc+pCYQ=
|
||||
github.com/containerd/containerd v1.5.0-beta.3/go.mod h1:/wr9AVtEM7x9c+n0+stptlo/uBBoBORwEx6ardVcmKU=
|
||||
github.com/containerd/containerd v1.5.0-beta.4/go.mod h1:GmdgZd2zA2GYIBZ0w09ZvgqEq8EfBp/m3lcVZIvPHhI=
|
||||
github.com/containerd/containerd v1.5.0-rc.0/go.mod h1:V/IXoMqNGgBlabz3tHD2TWDoTJseu1FGOKuoA4nNb2s=
|
||||
github.com/containerd/containerd v1.5.1/go.mod h1:0DOxVqwDy2iZvrZp2JUx/E+hS0UNTVn7dJnIOwtYR4g=
|
||||
github.com/containerd/containerd v1.5.5 h1:q1gxsZsGZ8ddVe98yO6pR21b5xQSMiR61lD0W96pgQo=
|
||||
github.com/containerd/containerd v1.5.5/go.mod h1:oSTh0QpT1w6jYcGmbiSbxv9OSQYaa88mPyWIuU79zyo=
|
||||
github.com/containerd/containerd v1.5.8 h1:NmkCC1/QxyZFBny8JogwLpOy2f+VEbO/f6bV2Mqtwuw=
|
||||
github.com/containerd/containerd v1.5.8/go.mod h1:YdFSv5bTFLpG2HIYmfqDpSYYTDX+mc5qtSuYx1YUb/s=
|
||||
github.com/containerd/continuity v0.0.0-20181203112020-004b46473808/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
|
||||
github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
|
||||
github.com/containerd/continuity v0.0.0-20190815185530-f2a389ac0a02/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
|
||||
@@ -277,8 +278,9 @@ github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDG
|
||||
github.com/containerd/ttrpc v0.0.0-20190828172938-92c8520ef9f8/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o=
|
||||
github.com/containerd/ttrpc v0.0.0-20191028202541-4f1b8fe65a5c/go.mod h1:LPm1u0xBw8r8NOKoOdNMeVHSawSsltak+Ihv+etqsE8=
|
||||
github.com/containerd/ttrpc v1.0.1/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y=
|
||||
github.com/containerd/ttrpc v1.0.2 h1:2/O3oTZN36q2xRolk0a2WWGgh7/Vf/liElg5hFYLX9U=
|
||||
github.com/containerd/ttrpc v1.0.2/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y=
|
||||
github.com/containerd/ttrpc v1.1.0 h1:GbtyLRxb0gOLR0TYQWt3O6B0NvT8tMdorEHqIQo/lWI=
|
||||
github.com/containerd/ttrpc v1.1.0/go.mod h1:XX4ZTnoOId4HklF4edwc4DcqskFZuvXB1Evzy5KFQpQ=
|
||||
github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc=
|
||||
github.com/containerd/typeurl v0.0.0-20190911142611-5eb25027c9fd/go.mod h1:GeKYzf2pQcqv7tJ0AoCuuhtnqhva5LNU3U+OyKxxJpk=
|
||||
github.com/containerd/typeurl v1.0.1/go.mod h1:TB1hUtrpaiO88KEK56ijojHS1+NeF0izUACaJW2mdXg=
|
||||
@@ -351,8 +353,6 @@ github.com/docker/cli v20.10.7+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHv
|
||||
github.com/docker/cli-docs-tool v0.1.1 h1:c6vuTMvogCkSFQCXIr6Mb4gFgUpdZ+28YMbCBfaQLik=
|
||||
github.com/docker/cli-docs-tool v0.1.1/go.mod h1:oMzPNt1wC3TcxuY22GMnOODNOxkwGH51gV3AhqAjFQ4=
|
||||
github.com/docker/compose-on-kubernetes v0.4.19-0.20190128150448-356b2919c496/go.mod h1:iT2pYfi580XlpaV4KmK0T6+4/9+XoKmk/fhoDod1emE=
|
||||
github.com/docker/compose-switch v1.0.2 h1:chXFNNcnRvmtQYzwTaVsv/KSLRt8riSRAiSav89mLfk=
|
||||
github.com/docker/compose-switch v1.0.2/go.mod h1:uyPj8S3oH1O9rSZ5QVozw28OIjdNIflSSYElC2P0plQ=
|
||||
github.com/docker/distribution v0.0.0-20190905152932-14b96e55d84c/go.mod h1:0+TTO4EOBfRPhZXAeF1Vu+W3hHZ8eLp8PgKVZlcvtFY=
|
||||
github.com/docker/distribution v2.6.0-rc.1.0.20180327202408-83389a148052+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||
github.com/docker/distribution v2.7.0+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||
@@ -826,8 +826,9 @@ github.com/opencontainers/go-digest v1.0.0-rc1.0.20180430190053-c9281466c8b2/go.
|
||||
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.0/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
|
||||
github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI=
|
||||
github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
|
||||
github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM=
|
||||
github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
|
||||
github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
|
||||
github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
|
||||
github.com/opencontainers/runc v1.0.0-rc10/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
|
||||
@@ -835,8 +836,8 @@ github.com/opencontainers/runc v1.0.0-rc8.0.20190926000215-3e425f80a8c9/go.mod h
|
||||
github.com/opencontainers/runc v1.0.0-rc9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
|
||||
github.com/opencontainers/runc v1.0.0-rc92/go.mod h1:X1zlU4p7wOlX4+WRCz+hvlRv8phdL7UqbYD+vQwNMmE=
|
||||
github.com/opencontainers/runc v1.0.0-rc93/go.mod h1:3NOsor4w32B2tC0Zbl8Knk4Wg84SM2ImC1fxBuqJ/H0=
|
||||
github.com/opencontainers/runc v1.0.1 h1:G18PGckGdAm3yVQRWDVQ1rLSLntiniKJ0cNRT2Tm5gs=
|
||||
github.com/opencontainers/runc v1.0.1/go.mod h1:aTaHFFwQXuA71CiyxOdFFIorAoemI04suvGRQFzWTD0=
|
||||
github.com/opencontainers/runc v1.0.2 h1:opHZMaswlyxz1OuGpBE53Dwe4/xF7EZTY0A2L/FpCOg=
|
||||
github.com/opencontainers/runc v1.0.2/go.mod h1:aTaHFFwQXuA71CiyxOdFFIorAoemI04suvGRQFzWTD0=
|
||||
github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
|
||||
github.com/opencontainers/runtime-spec v1.0.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
|
||||
github.com/opencontainers/runtime-spec v1.0.2-0.20190207185410-29686dbc5559/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
|
||||
@@ -964,7 +965,6 @@ github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3
|
||||
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
|
||||
github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
|
||||
github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI=
|
||||
github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo=
|
||||
github.com/spf13/cobra v1.2.1 h1:+KmjbUw1hriSNMF55oPrkZcb27aECyrj8V2ytv7kWDw=
|
||||
github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk=
|
||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||
@@ -1526,8 +1526,9 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD
|
||||
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
|
||||
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
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/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
||||
@@ -124,6 +124,8 @@ type StartOptions struct {
|
||||
CascadeStop bool
|
||||
// ExitCodeFrom return exit code from specified service
|
||||
ExitCodeFrom string
|
||||
// Wait won't return until containers reached the running|healthy state
|
||||
Wait bool
|
||||
}
|
||||
|
||||
// RestartOptions group options of the Restart API
|
||||
@@ -225,6 +227,8 @@ type RunOptions struct {
|
||||
Privileged bool
|
||||
UseNetworkAliases bool
|
||||
NoDeps bool
|
||||
// QuietPull makes the pulling process quiet
|
||||
QuietPull bool
|
||||
// used by exec
|
||||
Index int
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ import (
|
||||
"github.com/docker/buildx/util/buildflags"
|
||||
xprogress "github.com/docker/buildx/util/progress"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/docker/pkg/urlutil"
|
||||
bclient "github.com/moby/buildkit/client"
|
||||
"github.com/moby/buildkit/session"
|
||||
"github.com/moby/buildkit/session/auth/authprovider"
|
||||
@@ -246,7 +247,7 @@ func (s *composeService) toBuildOptions(project *types.Project, service types.Se
|
||||
return build.Options{
|
||||
Inputs: build.Inputs{
|
||||
ContextPath: service.Build.Context,
|
||||
DockerfilePath: filepath.Join(service.Build.Context, service.Build.Dockerfile),
|
||||
DockerfilePath: dockerFilePath(service.Build.Context, service.Build.Dockerfile),
|
||||
},
|
||||
BuildArgs: buildArgs,
|
||||
Tags: tags,
|
||||
@@ -285,3 +286,10 @@ func mergeArgs(m ...types.Mapping) types.Mapping {
|
||||
}
|
||||
return merged
|
||||
}
|
||||
|
||||
func dockerFilePath(context string, dockerfile string) string {
|
||||
if urlutil.IsGitURL(context) || filepath.IsAbs(dockerfile) {
|
||||
return dockerfile
|
||||
}
|
||||
return filepath.Join(context, dockerfile)
|
||||
}
|
||||
|
||||
@@ -243,6 +243,7 @@ func imageBuildOptions(options buildx.Options) dockertypes.ImageBuildOptions {
|
||||
func toMapStringStringPtr(source map[string]string) map[string]*string {
|
||||
dest := make(map[string]*string)
|
||||
for k, v := range source {
|
||||
v := v
|
||||
dest[k] = &v
|
||||
}
|
||||
return dest
|
||||
|
||||
@@ -261,9 +261,12 @@ func getContainerProgressName(container moby.Container) string {
|
||||
return "Container " + getCanonicalContainerName(container)
|
||||
}
|
||||
|
||||
func (s *composeService) waitDependencies(ctx context.Context, project *types.Project, service types.ServiceConfig) error {
|
||||
// ServiceConditionRunningOrHealthy is a service condition on statys running or healthy
|
||||
const ServiceConditionRunningOrHealthy = "running_or_healthy"
|
||||
|
||||
func (s *composeService) waitDependencies(ctx context.Context, project *types.Project, dependencies types.DependsOnConfig) error {
|
||||
eg, _ := errgroup.WithContext(ctx)
|
||||
for dep, config := range service.DependsOn {
|
||||
for dep, config := range dependencies {
|
||||
dep, config := dep, config
|
||||
eg.Go(func() error {
|
||||
ticker := time.NewTicker(500 * time.Millisecond)
|
||||
@@ -271,8 +274,16 @@ func (s *composeService) waitDependencies(ctx context.Context, project *types.Pr
|
||||
for {
|
||||
<-ticker.C
|
||||
switch config.Condition {
|
||||
case ServiceConditionRunningOrHealthy:
|
||||
healthy, err := s.isServiceHealthy(ctx, project, dep, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if healthy {
|
||||
return nil
|
||||
}
|
||||
case types.ServiceConditionHealthy:
|
||||
healthy, err := s.isServiceHealthy(ctx, project, dep)
|
||||
healthy, err := s.isServiceHealthy(ctx, project, dep, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -438,7 +449,10 @@ func (s *composeService) createMobyContainer(ctx context.Context, project *types
|
||||
Networks: inspectedContainer.NetworkSettings.Networks,
|
||||
},
|
||||
}
|
||||
links := append(service.Links, service.ExternalLinks...)
|
||||
links, err := s.getLinks(ctx, project.Name, service, number)
|
||||
if err != nil {
|
||||
return created, err
|
||||
}
|
||||
for _, netName := range service.NetworksByPriority() {
|
||||
netwrk := project.Networks[netName]
|
||||
cfg := service.Networks[netName]
|
||||
@@ -466,6 +480,64 @@ func (s *composeService) createMobyContainer(ctx context.Context, project *types
|
||||
return created, err
|
||||
}
|
||||
|
||||
// getLinks mimics V1 compose/service.py::Service::_get_links()
|
||||
func (s composeService) getLinks(ctx context.Context, projectName string, service types.ServiceConfig, number int) ([]string, error) {
|
||||
var links []string
|
||||
format := func(k, v string) string {
|
||||
return fmt.Sprintf("%s:%s", k, v)
|
||||
}
|
||||
getServiceContainers := func(serviceName string) (Containers, error) {
|
||||
return s.getContainers(ctx, projectName, oneOffExclude, true, serviceName)
|
||||
}
|
||||
|
||||
for _, rawLink := range service.Links {
|
||||
linkSplit := strings.Split(rawLink, ":")
|
||||
linkServiceName := linkSplit[0]
|
||||
linkName := linkServiceName
|
||||
if len(linkSplit) == 2 {
|
||||
linkName = linkSplit[1] // linkName if informed like in: "serviceName:linkName"
|
||||
}
|
||||
cnts, err := getServiceContainers(linkServiceName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, c := range cnts {
|
||||
containerName := getCanonicalContainerName(c)
|
||||
links = append(links,
|
||||
format(containerName, linkName),
|
||||
format(containerName, strings.Join([]string{linkServiceName, strconv.Itoa(number)}, Separator)),
|
||||
format(containerName, strings.Join([]string{projectName, linkServiceName, strconv.Itoa(number)}, Separator)),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if service.Labels[api.OneoffLabel] == "True" {
|
||||
cnts, err := getServiceContainers(service.Name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, c := range cnts {
|
||||
containerName := getCanonicalContainerName(c)
|
||||
links = append(links,
|
||||
format(containerName, service.Name),
|
||||
format(containerName, strings.TrimPrefix(containerName, projectName+Separator)),
|
||||
format(containerName, containerName),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
for _, rawExtLink := range service.ExternalLinks {
|
||||
extLinkSplit := strings.Split(rawExtLink, ":")
|
||||
externalLink := extLinkSplit[0]
|
||||
linkName := externalLink
|
||||
if len(extLinkSplit) == 2 {
|
||||
linkName = extLinkSplit[1]
|
||||
}
|
||||
links = append(links, format(externalLink, linkName))
|
||||
}
|
||||
return links, nil
|
||||
}
|
||||
|
||||
func shortIDAliasExists(containerID string, aliases ...string) bool {
|
||||
for _, alias := range aliases {
|
||||
if alias == containerID[:12] {
|
||||
@@ -502,7 +574,7 @@ func (s *composeService) connectContainerToNetwork(ctx context.Context, id strin
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *composeService) isServiceHealthy(ctx context.Context, project *types.Project, service string) (bool, error) {
|
||||
func (s *composeService) isServiceHealthy(ctx context.Context, project *types.Project, service string, fallbackRunning bool) (bool, error) {
|
||||
containers, err := s.getContainers(ctx, project.Name, oneOffExclude, false, service)
|
||||
if err != nil {
|
||||
return false, err
|
||||
@@ -516,6 +588,11 @@ func (s *composeService) isServiceHealthy(ctx context.Context, project *types.Pr
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if container.Config.Healthcheck == nil && fallbackRunning {
|
||||
// Container does not define a health check, but we can fall back to "running" state
|
||||
return container.State != nil && container.State.Status == "running", nil
|
||||
}
|
||||
|
||||
if container.State == nil || container.State.Health == nil {
|
||||
return false, fmt.Errorf("container for service %q has no healthcheck configured", service)
|
||||
}
|
||||
@@ -544,7 +621,11 @@ func (s *composeService) isServiceCompleted(ctx context.Context, project *types.
|
||||
}
|
||||
|
||||
func (s *composeService) startService(ctx context.Context, project *types.Project, service types.ServiceConfig) error {
|
||||
err := s.waitDependencies(ctx, project, service)
|
||||
if service.Deploy != nil && service.Deploy.Replicas != nil && *service.Deploy.Replicas == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
err := s.waitDependencies(ctx, project, service.DependsOn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -564,7 +645,7 @@ func (s *composeService) startService(ctx context.Context, project *types.Projec
|
||||
if scale, err := getScale(service); err != nil && scale == 0 {
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("no containers to start")
|
||||
return fmt.Errorf("service %q has no container to start", service.Name)
|
||||
}
|
||||
|
||||
w := progress.ContextWriter(ctx)
|
||||
|
||||
@@ -17,10 +17,16 @@
|
||||
package compose
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/compose-spec/compose-go/types"
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
"github.com/docker/compose/v2/pkg/mocks"
|
||||
moby "github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
"github.com/golang/mock/gomock"
|
||||
"gotest.tools/assert"
|
||||
)
|
||||
|
||||
@@ -46,3 +52,135 @@ func TestContainerName(t *testing.T) {
|
||||
_, err = getScale(s)
|
||||
assert.Error(t, err, fmt.Sprintf(doubledContainerNameWarning, s.Name, s.ContainerName))
|
||||
}
|
||||
|
||||
func TestServiceLinks(t *testing.T) {
|
||||
const dbContainerName = "/" + testProject + "-db-1"
|
||||
const webContainerName = "/" + testProject + "-web-1"
|
||||
s := types.ServiceConfig{
|
||||
Name: "web",
|
||||
Scale: 1,
|
||||
}
|
||||
|
||||
containerListOptions := moby.ContainerListOptions{
|
||||
Filters: filters.NewArgs(
|
||||
projectFilter(testProject),
|
||||
serviceFilter("db"),
|
||||
oneOffFilter(false),
|
||||
),
|
||||
All: true,
|
||||
}
|
||||
|
||||
t.Run("service links default", func(t *testing.T) {
|
||||
mockCtrl := gomock.NewController(t)
|
||||
defer mockCtrl.Finish()
|
||||
apiClient := mocks.NewMockAPIClient(mockCtrl)
|
||||
tested.apiClient = apiClient
|
||||
|
||||
s.Links = []string{"db"}
|
||||
|
||||
c := testContainer("db", dbContainerName, false)
|
||||
apiClient.EXPECT().ContainerList(gomock.Any(), containerListOptions).Return([]moby.Container{c}, nil)
|
||||
|
||||
links, err := tested.getLinks(context.Background(), testProject, s, 1)
|
||||
assert.NilError(t, err)
|
||||
|
||||
assert.Equal(t, len(links), 3)
|
||||
assert.Equal(t, links[0], "testProject-db-1:db")
|
||||
assert.Equal(t, links[1], "testProject-db-1:db-1")
|
||||
assert.Equal(t, links[2], "testProject-db-1:testProject-db-1")
|
||||
})
|
||||
|
||||
t.Run("service links", func(t *testing.T) {
|
||||
mockCtrl := gomock.NewController(t)
|
||||
defer mockCtrl.Finish()
|
||||
apiClient := mocks.NewMockAPIClient(mockCtrl)
|
||||
tested.apiClient = apiClient
|
||||
|
||||
s.Links = []string{"db:db"}
|
||||
|
||||
c := testContainer("db", dbContainerName, false)
|
||||
|
||||
apiClient.EXPECT().ContainerList(gomock.Any(), containerListOptions).Return([]moby.Container{c}, nil)
|
||||
links, err := tested.getLinks(context.Background(), testProject, s, 1)
|
||||
assert.NilError(t, err)
|
||||
|
||||
assert.Equal(t, len(links), 3)
|
||||
assert.Equal(t, links[0], "testProject-db-1:db")
|
||||
assert.Equal(t, links[1], "testProject-db-1:db-1")
|
||||
assert.Equal(t, links[2], "testProject-db-1:testProject-db-1")
|
||||
})
|
||||
|
||||
t.Run("service links name", func(t *testing.T) {
|
||||
mockCtrl := gomock.NewController(t)
|
||||
defer mockCtrl.Finish()
|
||||
apiClient := mocks.NewMockAPIClient(mockCtrl)
|
||||
tested.apiClient = apiClient
|
||||
|
||||
s.Links = []string{"db:dbname"}
|
||||
|
||||
c := testContainer("db", dbContainerName, false)
|
||||
apiClient.EXPECT().ContainerList(gomock.Any(), containerListOptions).Return([]moby.Container{c}, nil)
|
||||
|
||||
links, err := tested.getLinks(context.Background(), testProject, s, 1)
|
||||
assert.NilError(t, err)
|
||||
|
||||
assert.Equal(t, len(links), 3)
|
||||
assert.Equal(t, links[0], "testProject-db-1:dbname")
|
||||
assert.Equal(t, links[1], "testProject-db-1:db-1")
|
||||
assert.Equal(t, links[2], "testProject-db-1:testProject-db-1")
|
||||
})
|
||||
|
||||
t.Run("service links external links", func(t *testing.T) {
|
||||
mockCtrl := gomock.NewController(t)
|
||||
defer mockCtrl.Finish()
|
||||
apiClient := mocks.NewMockAPIClient(mockCtrl)
|
||||
tested.apiClient = apiClient
|
||||
|
||||
s.Links = []string{"db:dbname"}
|
||||
s.ExternalLinks = []string{"db1:db2"}
|
||||
|
||||
c := testContainer("db", dbContainerName, false)
|
||||
apiClient.EXPECT().ContainerList(gomock.Any(), containerListOptions).Return([]moby.Container{c}, nil)
|
||||
|
||||
links, err := tested.getLinks(context.Background(), testProject, s, 1)
|
||||
assert.NilError(t, err)
|
||||
|
||||
assert.Equal(t, len(links), 4)
|
||||
assert.Equal(t, links[0], "testProject-db-1:dbname")
|
||||
assert.Equal(t, links[1], "testProject-db-1:db-1")
|
||||
assert.Equal(t, links[2], "testProject-db-1:testProject-db-1")
|
||||
|
||||
// ExternalLink
|
||||
assert.Equal(t, links[3], "db1:db2")
|
||||
})
|
||||
|
||||
t.Run("service links itself oneoff", func(t *testing.T) {
|
||||
mockCtrl := gomock.NewController(t)
|
||||
defer mockCtrl.Finish()
|
||||
apiClient := mocks.NewMockAPIClient(mockCtrl)
|
||||
tested.apiClient = apiClient
|
||||
|
||||
s.Links = []string{}
|
||||
s.ExternalLinks = []string{}
|
||||
s.Labels = s.Labels.Add(api.OneoffLabel, "True")
|
||||
|
||||
c := testContainer("web", webContainerName, true)
|
||||
containerListOptionsOneOff := moby.ContainerListOptions{
|
||||
Filters: filters.NewArgs(
|
||||
projectFilter(testProject),
|
||||
serviceFilter("web"),
|
||||
oneOffFilter(false),
|
||||
),
|
||||
All: true,
|
||||
}
|
||||
apiClient.EXPECT().ContainerList(gomock.Any(), containerListOptionsOneOff).Return([]moby.Container{c}, nil)
|
||||
|
||||
links, err := tested.getLinks(context.Background(), testProject, s, 1)
|
||||
assert.NilError(t, err)
|
||||
|
||||
assert.Equal(t, len(links), 3)
|
||||
assert.Equal(t, links[0], "testProject-web-1:web")
|
||||
assert.Equal(t, links[1], "testProject-web-1:web-1")
|
||||
assert.Equal(t, links[2], "testProject-web-1:testProject-web-1")
|
||||
})
|
||||
}
|
||||
|
||||
@@ -210,7 +210,7 @@ func (s *composeService) ensureProjectVolumes(ctx context.Context, project *type
|
||||
volume.Labels = volume.Labels.Add(api.VolumeLabel, k)
|
||||
volume.Labels = volume.Labels.Add(api.ProjectLabel, project.Name)
|
||||
volume.Labels = volume.Labels.Add(api.VersionLabel, api.ComposeVersion)
|
||||
err := s.ensureVolume(ctx, volume)
|
||||
err := s.ensureVolume(ctx, volume, project.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -493,6 +493,7 @@ func getDeployResources(s types.ServiceConfig) container.Resources {
|
||||
MemorySwap: int64(s.MemSwapLimit),
|
||||
MemorySwappiness: swappiness,
|
||||
MemoryReservation: int64(s.MemReservation),
|
||||
OomKillDisable: &s.OomKillDisable,
|
||||
CPUCount: s.CPUCount,
|
||||
CPUPeriod: s.CPUPeriod,
|
||||
CPUQuota: s.CPUQuota,
|
||||
@@ -503,6 +504,10 @@ func getDeployResources(s types.ServiceConfig) container.Resources {
|
||||
CpusetCpus: s.CPUSet,
|
||||
}
|
||||
|
||||
if s.PidsLimit != 0 {
|
||||
resources.PidsLimit = &s.PidsLimit
|
||||
}
|
||||
|
||||
setBlkio(s.BlkioConfig, &resources)
|
||||
|
||||
if s.Deploy != nil {
|
||||
@@ -947,8 +952,8 @@ func buildMountOptions(volume types.ServiceVolumeConfig) (*mount.BindOptions, *m
|
||||
if volume.Bind != nil {
|
||||
logrus.Warnf("mount of type `tmpfs` should not define `bind` option")
|
||||
}
|
||||
if volume.Tmpfs != nil {
|
||||
logrus.Warnf("mount of type `tmpfs` should not define `volumeZ` option")
|
||||
if volume.Volume != nil {
|
||||
logrus.Warnf("mount of type `tmpfs` should not define `volume` option")
|
||||
}
|
||||
return nil, nil, buildTmpfsOptions(volume.Tmpfs)
|
||||
}
|
||||
@@ -1081,27 +1086,48 @@ func (s *composeService) removeNetwork(ctx context.Context, networkID string, ne
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *composeService) ensureVolume(ctx context.Context, volume types.VolumeConfig) error {
|
||||
// TODO could identify volume by label vs name
|
||||
_, err := s.apiClient.VolumeInspect(ctx, volume.Name)
|
||||
func (s *composeService) ensureVolume(ctx context.Context, volume types.VolumeConfig, project string) error {
|
||||
inspected, err := s.apiClient.VolumeInspect(ctx, volume.Name)
|
||||
if err != nil {
|
||||
if !errdefs.IsNotFound(err) {
|
||||
return err
|
||||
}
|
||||
eventName := fmt.Sprintf("Volume %q", volume.Name)
|
||||
w := progress.ContextWriter(ctx)
|
||||
w.Event(progress.CreatingEvent(eventName))
|
||||
_, err := s.apiClient.VolumeCreate(ctx, volume_api.VolumeCreateBody{
|
||||
Labels: volume.Labels,
|
||||
Name: volume.Name,
|
||||
Driver: volume.Driver,
|
||||
DriverOpts: volume.DriverOpts,
|
||||
})
|
||||
if err != nil {
|
||||
w.Event(progress.ErrorEvent(eventName))
|
||||
return err
|
||||
if volume.External.External {
|
||||
return fmt.Errorf("external volume %q not found", volume.External.Name)
|
||||
}
|
||||
w.Event(progress.CreatedEvent(eventName))
|
||||
err := s.createVolume(ctx, volume)
|
||||
return err
|
||||
}
|
||||
|
||||
if volume.External.External {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Volume exists with name, but let's double-check this is the expected one
|
||||
p, ok := inspected.Labels[api.ProjectLabel]
|
||||
if !ok {
|
||||
logrus.Warnf("volume %q already exists but was not created by Docker Compose. Use `external: true` to use an existing volume", volume.Name)
|
||||
}
|
||||
if ok && p != project {
|
||||
logrus.Warnf("volume %q already exists but was not created for project %q. Use `external: true` to use an existing volume", volume.Name, p)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *composeService) createVolume(ctx context.Context, volume types.VolumeConfig) error {
|
||||
eventName := fmt.Sprintf("Volume %q", volume.Name)
|
||||
w := progress.ContextWriter(ctx)
|
||||
w.Event(progress.CreatingEvent(eventName))
|
||||
_, err := s.apiClient.VolumeCreate(ctx, volume_api.VolumeCreateBody{
|
||||
Labels: volume.Labels,
|
||||
Name: volume.Name,
|
||||
Driver: volume.Driver,
|
||||
DriverOpts: volume.DriverOpts,
|
||||
})
|
||||
if err != nil {
|
||||
w.Event(progress.ErrorEvent(eventName))
|
||||
return err
|
||||
}
|
||||
w.Event(progress.CreatedEvent(eventName))
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -93,7 +93,7 @@ func run(ctx context.Context, graph *Graph, eg *errgroup.Group, nodes []*Vertex,
|
||||
for _, node := range nodes {
|
||||
// Don't start this service yet if all of its children have
|
||||
// not been started yet.
|
||||
if len(traversalConfig.filterAdjacentByStatusFn(graph, node.Service, traversalConfig.adjacentServiceStatusToSkip)) != 0 {
|
||||
if len(traversalConfig.filterAdjacentByStatusFn(graph, node.Key, traversalConfig.adjacentServiceStatusToSkip)) != 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -104,7 +104,7 @@ func run(ctx context.Context, graph *Graph, eg *errgroup.Group, nodes []*Vertex,
|
||||
return err
|
||||
}
|
||||
|
||||
graph.UpdateStatus(node.Service, traversalConfig.targetServiceStatus)
|
||||
graph.UpdateStatus(node.Key, traversalConfig.targetServiceStatus)
|
||||
|
||||
return run(ctx, graph, eg, traversalConfig.adjacentNodesFn(node), traversalConfig, fn)
|
||||
})
|
||||
|
||||
@@ -34,25 +34,43 @@ func (s *composeService) Logs(ctx context.Context, projectName string, consumer
|
||||
}
|
||||
|
||||
eg, ctx := errgroup.WithContext(ctx)
|
||||
if options.Follow {
|
||||
printer := newLogPrinter(consumer)
|
||||
eg.Go(func() error {
|
||||
return s.watchContainers(ctx, projectName, options.Services, printer.HandleEvent, containers, func(c types.Container) error {
|
||||
return s.logContainers(ctx, consumer, c, options)
|
||||
})
|
||||
})
|
||||
eg.Go(func() error {
|
||||
_, err := printer.Run(false, "", nil)
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
for _, c := range containers {
|
||||
c := c
|
||||
eg.Go(func() error {
|
||||
return s.logContainers(ctx, consumer, c, options)
|
||||
})
|
||||
}
|
||||
|
||||
if options.Follow {
|
||||
printer := newLogPrinter(consumer)
|
||||
eg.Go(func() error {
|
||||
for _, c := range containers {
|
||||
printer.HandleEvent(api.ContainerEvent{
|
||||
Type: api.ContainerEventAttach,
|
||||
Container: getContainerNameWithoutProject(c),
|
||||
Service: c.Labels[api.ServiceLabel],
|
||||
})
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
eg.Go(func() error {
|
||||
return s.watchContainers(ctx, projectName, options.Services, printer.HandleEvent, containers, func(c types.Container) error {
|
||||
printer.HandleEvent(api.ContainerEvent{
|
||||
Type: api.ContainerEventAttach,
|
||||
Container: getContainerNameWithoutProject(c),
|
||||
Service: c.Labels[api.ServiceLabel],
|
||||
})
|
||||
return s.logContainers(ctx, consumer, c, options)
|
||||
})
|
||||
})
|
||||
|
||||
eg.Go(func() error {
|
||||
_, err := printer.Run(ctx, false, "", nil)
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
return eg.Wait()
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package compose
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
@@ -27,7 +28,7 @@ import (
|
||||
// logPrinter watch application containers an collect their logs
|
||||
type logPrinter interface {
|
||||
HandleEvent(event api.ContainerEvent)
|
||||
Run(cascadeStop bool, exitCodeFrom string, stopFn func() error) (int, error)
|
||||
Run(ctx context.Context, cascadeStop bool, exitCodeFrom string, stopFn func() error) (int, error)
|
||||
Cancel()
|
||||
}
|
||||
|
||||
@@ -56,56 +57,61 @@ func (p *printer) HandleEvent(event api.ContainerEvent) {
|
||||
p.queue <- event
|
||||
}
|
||||
|
||||
func (p *printer) Run(cascadeStop bool, exitCodeFrom string, stopFn func() error) (int, error) {
|
||||
//nolint:gocyclo
|
||||
func (p *printer) Run(ctx context.Context, cascadeStop bool, exitCodeFrom string, stopFn func() error) (int, error) {
|
||||
var (
|
||||
aborting bool
|
||||
exitCode int
|
||||
)
|
||||
containers := map[string]struct{}{}
|
||||
for {
|
||||
event := <-p.queue
|
||||
container := event.Container
|
||||
switch event.Type {
|
||||
case api.UserCancel:
|
||||
aborting = true
|
||||
case api.ContainerEventAttach:
|
||||
if _, ok := containers[container]; ok {
|
||||
continue
|
||||
}
|
||||
containers[container] = struct{}{}
|
||||
p.consumer.Register(container)
|
||||
case api.ContainerEventExit:
|
||||
if !event.Restarting {
|
||||
delete(containers, container)
|
||||
}
|
||||
if !aborting {
|
||||
p.consumer.Status(container, fmt.Sprintf("exited with code %d", event.ExitCode))
|
||||
}
|
||||
if cascadeStop {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return exitCode, ctx.Err()
|
||||
case event := <-p.queue:
|
||||
container := event.Container
|
||||
switch event.Type {
|
||||
case api.UserCancel:
|
||||
aborting = true
|
||||
case api.ContainerEventAttach:
|
||||
if _, ok := containers[container]; ok {
|
||||
continue
|
||||
}
|
||||
containers[container] = struct{}{}
|
||||
p.consumer.Register(container)
|
||||
case api.ContainerEventExit:
|
||||
if !event.Restarting {
|
||||
delete(containers, container)
|
||||
}
|
||||
if !aborting {
|
||||
aborting = true
|
||||
fmt.Println("Aborting on container exit...")
|
||||
err := stopFn()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
p.consumer.Status(container, fmt.Sprintf("exited with code %d", event.ExitCode))
|
||||
}
|
||||
if cascadeStop {
|
||||
if !aborting {
|
||||
aborting = true
|
||||
fmt.Println("Aborting on container exit...")
|
||||
err := stopFn()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
if exitCodeFrom == "" {
|
||||
exitCodeFrom = event.Service
|
||||
}
|
||||
if exitCodeFrom == event.Service {
|
||||
logrus.Error(event.ExitCode)
|
||||
exitCode = event.ExitCode
|
||||
}
|
||||
}
|
||||
if exitCodeFrom == "" {
|
||||
exitCodeFrom = event.Service
|
||||
if len(containers) == 0 {
|
||||
// Last container terminated, done
|
||||
return exitCode, nil
|
||||
}
|
||||
if exitCodeFrom == event.Service {
|
||||
logrus.Error(event.ExitCode)
|
||||
exitCode = event.ExitCode
|
||||
case api.ContainerEventLog:
|
||||
if !aborting {
|
||||
p.consumer.Log(container, event.Service, event.Line)
|
||||
}
|
||||
}
|
||||
if len(containers) == 0 {
|
||||
// Last container terminated, done
|
||||
return exitCode, nil
|
||||
}
|
||||
case api.ContainerEventLog:
|
||||
if !aborting {
|
||||
p.consumer.Log(container, event.Service, event.Line)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ func (s *composeService) runInteractive(ctx context.Context, containerID string,
|
||||
}
|
||||
|
||||
in := streams.NewIn(opts.Stdin)
|
||||
if in.IsTerminal() {
|
||||
if in.IsTerminal() && opts.Tty {
|
||||
state, err := term.SetRawTerminal(in.FD())
|
||||
if err != nil {
|
||||
return 0, err
|
||||
@@ -156,11 +156,11 @@ func (s *composeService) prepareRun(ctx context.Context, project *types.Project,
|
||||
service.Labels = service.Labels.Add(api.SlugLabel, slug)
|
||||
service.Labels = service.Labels.Add(api.OneoffLabel, "True")
|
||||
|
||||
if err := s.ensureImagesExists(ctx, project, false); err != nil { // all dependencies already checked, but might miss service img
|
||||
if err := s.ensureImagesExists(ctx, project, opts.QuietPull); err != nil { // all dependencies already checked, but might miss service img
|
||||
return "", err
|
||||
}
|
||||
if !opts.NoDeps {
|
||||
if err := s.waitDependencies(ctx, project, service); err != nil {
|
||||
if err := s.waitDependencies(ctx, project, service.DependsOn); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,11 +58,26 @@ func (s *composeService) start(ctx context.Context, project *types.Project, opti
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return s.startService(ctx, project, service)
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if options.Wait {
|
||||
depends := types.DependsOnConfig{}
|
||||
for _, s := range project.Services {
|
||||
depends[s.Name] = types.ServiceDependency{
|
||||
Condition: ServiceConditionRunningOrHealthy,
|
||||
}
|
||||
}
|
||||
err = s.waitDependencies(ctx, project, depends)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return eg.Wait()
|
||||
}
|
||||
|
||||
@@ -79,6 +94,12 @@ func (s *composeService) watchContainers(ctx context.Context, projectName string
|
||||
err := s.Events(ctx, projectName, api.EventsOptions{
|
||||
Services: services,
|
||||
Consumer: func(event api.Event) error {
|
||||
if event.Status == "destroy" {
|
||||
// This container can't be inspected, because it's gone.
|
||||
// It's already been removed from the watched map.
|
||||
return nil
|
||||
}
|
||||
|
||||
inspected, err := s.apiClient.ContainerInspect(ctx, event.Container)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -94,7 +115,7 @@ func (s *composeService) watchContainers(ctx context.Context, projectName string
|
||||
restarted := watched[container.ID]
|
||||
watched[container.ID] = restarted + 1
|
||||
// Container terminated.
|
||||
willRestart := inspected.HostConfig.RestartPolicy.MaximumRetryCount > restarted
|
||||
willRestart := willContainerRestart(inspected, restarted)
|
||||
|
||||
listener(api.ContainerEvent{
|
||||
Type: api.ContainerEventExit,
|
||||
@@ -141,3 +162,14 @@ func (s *composeService) watchContainers(ctx context.Context, projectName string
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func willContainerRestart(container moby.ContainerJSON, restarted int) bool {
|
||||
policy := container.HostConfig.RestartPolicy
|
||||
if policy.IsAlways() || policy.IsUnlessStopped() {
|
||||
return true
|
||||
}
|
||||
if policy.IsOnFailure() {
|
||||
return container.State.ExitCode != 0 && policy.MaximumRetryCount > restarted
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -80,7 +80,7 @@ func (s *composeService) Up(ctx context.Context, project *types.Project, options
|
||||
var exitCode int
|
||||
eg, ctx := errgroup.WithContext(ctx)
|
||||
eg.Go(func() error {
|
||||
code, err := printer.Run(options.Start.CascadeStop, options.Start.ExitCodeFrom, stopFunc)
|
||||
code, err := printer.Run(context.Background(), options.Start.CascadeStop, options.Start.ExitCodeFrom, stopFunc)
|
||||
exitCode = code
|
||||
return err
|
||||
})
|
||||
|
||||
@@ -66,6 +66,19 @@ func TestLocalComposeBuild(t *testing.T) {
|
||||
res.Assert(t, icmd.Expected{Out: `"FOO": "BAR"`})
|
||||
})
|
||||
|
||||
t.Run("build with multiple build-args ", func(t *testing.T) {
|
||||
// ensure local test run does not reuse previously build image
|
||||
c.RunDockerOrExitError("rmi", "-f", "multi-args_multiargs")
|
||||
cmd := c.NewDockerCmd("compose", "--project-directory", "fixtures/build-test/multi-args", "build")
|
||||
|
||||
icmd.RunCmd(cmd, func(cmd *icmd.Cmd) {
|
||||
cmd.Env = append(cmd.Env, "DOCKER_BUILDKIT=0")
|
||||
})
|
||||
|
||||
res := c.RunDockerCmd("image", "inspect", "multi-args_multiargs")
|
||||
res.Assert(t, icmd.Expected{Out: `"RESULT": "SUCCESS"`})
|
||||
})
|
||||
|
||||
t.Run("build as part of up", func(t *testing.T) {
|
||||
c.RunDockerOrExitError("rmi", "build-test_nginx")
|
||||
c.RunDockerOrExitError("rmi", "custom-nginx")
|
||||
|
||||
19
pkg/e2e/fixtures/build-test/multi-args/Dockerfile
Normal file
19
pkg/e2e/fixtures/build-test/multi-args/Dockerfile
Normal file
@@ -0,0 +1,19 @@
|
||||
# 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.
|
||||
|
||||
ARG IMAGE=666
|
||||
ARG TAG=666
|
||||
|
||||
FROM ${IMAGE}:${TAG}
|
||||
RUN echo "SUCCESS"
|
||||
9
pkg/e2e/fixtures/build-test/multi-args/compose.yaml
Normal file
9
pkg/e2e/fixtures/build-test/multi-args/compose.yaml
Normal file
@@ -0,0 +1,9 @@
|
||||
services:
|
||||
multiargs:
|
||||
build:
|
||||
context: .
|
||||
args:
|
||||
IMAGE: alpine
|
||||
TAG: latest
|
||||
labels:
|
||||
- RESULT=SUCCESS
|
||||
@@ -104,9 +104,9 @@ func newE2eCLI(t *testing.T, binDir string) *E2eCLI {
|
||||
}
|
||||
|
||||
func dirContents(dir string) []string {
|
||||
res := []string{}
|
||||
var res []string
|
||||
_ = filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
|
||||
res = append(res, filepath.Join(dir, path))
|
||||
res = append(res, path)
|
||||
return nil
|
||||
})
|
||||
return res
|
||||
|
||||
@@ -36,7 +36,7 @@ func TestComposeMetrics(t *testing.T) {
|
||||
res = c.RunDockerOrExitError("compose", "-f", "fixtures/wrong-composefile/compose.yaml", "up", "-d")
|
||||
res.Assert(t, icmd.Expected{ExitCode: 15, Err: "services.simple Additional property wrongField is not allowed"})
|
||||
res = c.RunDockerOrExitError("compose", "up")
|
||||
res.Assert(t, icmd.Expected{ExitCode: 14, Err: "can't find a suitable configuration file in this directory or any parent: not found"})
|
||||
res.Assert(t, icmd.Expected{ExitCode: 14, Err: "no configuration file provided: not found"})
|
||||
res = c.RunDockerOrExitError("compose", "up", "-f", "fixtures/wrong-composefile/compose.yaml")
|
||||
res.Assert(t, icmd.Expected{ExitCode: 16, Err: "unknown shorthand flag: 'f' in -f"})
|
||||
res = c.RunDockerOrExitError("compose", "up", "--file", "fixtures/wrong-composefile/compose.yaml")
|
||||
|
||||
Reference in New Issue
Block a user