mirror of
https://github.com/docker/compose.git
synced 2025-07-26 23:24:05 +02:00
Merge branch 'v2' into 8768-avoid-pulling-same-image-multiple-times
This commit is contained in:
commit
89dfb9140e
6
.github/workflows/ci.yml
vendored
6
.github/workflows/ci.yml
vendored
@ -28,9 +28,9 @@ jobs:
|
|||||||
- name: Run golangci-lint
|
- name: Run golangci-lint
|
||||||
env:
|
env:
|
||||||
BUILD_TAGS: e2e
|
BUILD_TAGS: e2e
|
||||||
run: |
|
uses: golangci/golangci-lint-action@v2
|
||||||
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sudo sh -s -- -b /usr/bin/ v1.39.0
|
with:
|
||||||
make -f builder.Makefile lint
|
args: --timeout=180s
|
||||||
|
|
||||||
# only on main branch, costs too much for the gain on every PR
|
# only on main branch, costs too much for the gain on every PR
|
||||||
validate-cross-build:
|
validate-cross-build:
|
||||||
|
@ -120,24 +120,6 @@ func (o *projectOptions) WithServices(fn ProjectServicesFunc) func(cmd *cobra.Co
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if o.EnvFile != "" {
|
|
||||||
var services types.Services
|
|
||||||
for _, s := range project.Services {
|
|
||||||
ef := o.EnvFile
|
|
||||||
if ef != "" {
|
|
||||||
if !filepath.IsAbs(ef) {
|
|
||||||
ef = filepath.Join(project.WorkingDir, o.EnvFile)
|
|
||||||
}
|
|
||||||
if s.Labels == nil {
|
|
||||||
s.Labels = make(map[string]string)
|
|
||||||
}
|
|
||||||
s.Labels[api.EnvironmentFileLabel] = ef
|
|
||||||
services = append(services, s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
project.Services = services
|
|
||||||
}
|
|
||||||
|
|
||||||
return fn(ctx, project, args)
|
return fn(ctx, project, args)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -180,6 +162,25 @@ func (o *projectOptions) toProject(services []string, po ...cli.ProjectOptionsFn
|
|||||||
compose.Separator = "_"
|
compose.Separator = "_"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ef := o.EnvFile
|
||||||
|
if ef != "" && !filepath.IsAbs(ef) {
|
||||||
|
ef = filepath.Join(project.WorkingDir, o.EnvFile)
|
||||||
|
}
|
||||||
|
for i, s := range project.Services {
|
||||||
|
s.CustomLabels = map[string]string{
|
||||||
|
api.ProjectLabel: project.Name,
|
||||||
|
api.ServiceLabel: s.Name,
|
||||||
|
api.VersionLabel: api.ComposeVersion,
|
||||||
|
api.WorkingDirLabel: project.WorkingDir,
|
||||||
|
api.ConfigFilesLabel: strings.Join(project.ComposeFiles, ","),
|
||||||
|
api.OneoffLabel: "False", // default, will be overridden by `run` command
|
||||||
|
}
|
||||||
|
if ef != "" {
|
||||||
|
s.CustomLabels[api.EnvironmentFileLabel] = ef
|
||||||
|
}
|
||||||
|
project.Services[i] = s
|
||||||
|
}
|
||||||
|
|
||||||
if len(services) > 0 {
|
if len(services) > 0 {
|
||||||
s, err := project.GetServices(services...)
|
s, err := project.GetServices(services...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -19,10 +19,14 @@ package compose
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/compose-spec/compose-go/types"
|
"github.com/compose-spec/compose-go/types"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/spf13/pflag"
|
||||||
|
|
||||||
"github.com/docker/compose/v2/pkg/api"
|
"github.com/docker/compose/v2/pkg/api"
|
||||||
)
|
)
|
||||||
@ -58,10 +62,19 @@ func downCommand(p *projectOptions, backend api.Service) *cobra.Command {
|
|||||||
ValidArgsFunction: noCompletion(),
|
ValidArgsFunction: noCompletion(),
|
||||||
}
|
}
|
||||||
flags := downCmd.Flags()
|
flags := downCmd.Flags()
|
||||||
flags.BoolVar(&opts.removeOrphans, "remove-orphans", false, "Remove containers for services not defined in the Compose file.")
|
removeOrphans := strings.ToLower(os.Getenv("COMPOSE_REMOVE_ORPHANS ")) == "true"
|
||||||
|
flags.BoolVar(&opts.removeOrphans, "remove-orphans", removeOrphans, "Remove containers for services not defined in the Compose file.")
|
||||||
flags.IntVarP(&opts.timeout, "timeout", "t", 10, "Specify a shutdown timeout in seconds")
|
flags.IntVarP(&opts.timeout, "timeout", "t", 10, "Specify a shutdown timeout in seconds")
|
||||||
flags.BoolVarP(&opts.volumes, "volumes", "v", false, " Remove named volumes declared in the `volumes` section of the Compose file and anonymous volumes attached to containers.")
|
flags.BoolVarP(&opts.volumes, "volumes", "v", false, " Remove named volumes declared in the `volumes` section of the Compose file and anonymous volumes attached to containers.")
|
||||||
flags.StringVar(&opts.images, "rmi", "", `Remove images used by services. "local" remove only images that don't have a custom tag ("local"|"all")`)
|
flags.StringVar(&opts.images, "rmi", "", `Remove images used by services. "local" remove only images that don't have a custom tag ("local"|"all")`)
|
||||||
|
flags.SetNormalizeFunc(func(f *pflag.FlagSet, name string) pflag.NormalizedName {
|
||||||
|
switch name {
|
||||||
|
case "volume":
|
||||||
|
name = "volumes"
|
||||||
|
logrus.Warn("--volume is deprecated, please use --volumes")
|
||||||
|
}
|
||||||
|
return pflag.NormalizedName(name)
|
||||||
|
})
|
||||||
return downCmd
|
return downCmd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,14 +92,15 @@ func runList(ctx context.Context, backend api.Service, opts lsOptions) error {
|
|||||||
view := viewFromStackList(stackList)
|
view := viewFromStackList(stackList)
|
||||||
return formatter.Print(view, opts.Format, os.Stdout, func(w io.Writer) {
|
return formatter.Print(view, opts.Format, os.Stdout, func(w io.Writer) {
|
||||||
for _, stack := range view {
|
for _, stack := range view {
|
||||||
_, _ = fmt.Fprintf(w, "%s\t%s\n", stack.Name, stack.Status)
|
_, _ = fmt.Fprintf(w, "%s\t%s\t%s\n", stack.Name, stack.Status, stack.ConfigFiles)
|
||||||
}
|
}
|
||||||
}, "NAME", "STATUS")
|
}, "NAME", "STATUS", "CONFIG FILES")
|
||||||
}
|
}
|
||||||
|
|
||||||
type stackView struct {
|
type stackView struct {
|
||||||
Name string
|
Name string
|
||||||
Status string
|
Status string
|
||||||
|
ConfigFiles string
|
||||||
}
|
}
|
||||||
|
|
||||||
func viewFromStackList(stackList []api.Stack) []stackView {
|
func viewFromStackList(stackList []api.Stack) []stackView {
|
||||||
@ -108,6 +109,7 @@ func viewFromStackList(stackList []api.Stack) []stackView {
|
|||||||
retList[i] = stackView{
|
retList[i] = stackView{
|
||||||
Name: s.Name,
|
Name: s.Name,
|
||||||
Status: strings.TrimSpace(fmt.Sprintf("%s %s", s.Status, s.Reason)),
|
Status: strings.TrimSpace(fmt.Sprintf("%s %s", s.Status, s.Reason)),
|
||||||
|
ConfigFiles: s.ConfigFiles,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return retList
|
return retList
|
||||||
|
@ -65,7 +65,7 @@ func runRemove(ctx context.Context, backend api.Service, opts removeOptions, ser
|
|||||||
}
|
}
|
||||||
|
|
||||||
if opts.stop {
|
if opts.stop {
|
||||||
err := backend.Stop(ctx, project, api.StopOptions{
|
err := backend.Stop(ctx, project.Name, api.StopOptions{
|
||||||
Services: services,
|
Services: services,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -49,13 +49,13 @@ func restartCommand(p *projectOptions, backend api.Service) *cobra.Command {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func runRestart(ctx context.Context, backend api.Service, opts restartOptions, services []string) error {
|
func runRestart(ctx context.Context, backend api.Service, opts restartOptions, services []string) error {
|
||||||
project, err := opts.toProject(services)
|
projectName, err := opts.toProjectName()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
timeout := time.Duration(opts.timeout) * time.Second
|
timeout := time.Duration(opts.timeout) * time.Second
|
||||||
return backend.Restart(ctx, project, api.RestartOptions{
|
return backend.Restart(ctx, projectName, api.RestartOptions{
|
||||||
Timeout: &timeout,
|
Timeout: &timeout,
|
||||||
Services: services,
|
Services: services,
|
||||||
})
|
})
|
||||||
|
@ -240,5 +240,5 @@ func startDependencies(ctx context.Context, backend api.Service, project types.P
|
|||||||
if err := backend.Create(ctx, &project, api.CreateOptions{}); err != nil {
|
if err := backend.Create(ctx, &project, api.CreateOptions{}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return backend.Start(ctx, &project, api.StartOptions{})
|
return backend.Start(ctx, project.Name, api.StartOptions{})
|
||||||
}
|
}
|
||||||
|
@ -43,10 +43,12 @@ func startCommand(p *projectOptions, backend api.Service) *cobra.Command {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func runStart(ctx context.Context, backend api.Service, opts startOptions, services []string) error {
|
func runStart(ctx context.Context, backend api.Service, opts startOptions, services []string) error {
|
||||||
project, err := opts.toProject(services)
|
projectName, err := opts.toProjectName()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return backend.Start(ctx, project, api.StartOptions{})
|
return backend.Start(ctx, projectName, api.StartOptions{
|
||||||
|
AttachTo: services,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
@ -53,7 +53,7 @@ func stopCommand(p *projectOptions, backend api.Service) *cobra.Command {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func runStop(ctx context.Context, backend api.Service, opts stopOptions, services []string) error {
|
func runStop(ctx context.Context, backend api.Service, opts stopOptions, services []string) error {
|
||||||
project, err := opts.toProject(services)
|
projectName, err := opts.toProjectName()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -63,7 +63,7 @@ func runStop(ctx context.Context, backend api.Service, opts stopOptions, service
|
|||||||
timeoutValue := time.Duration(opts.timeout) * time.Second
|
timeoutValue := time.Duration(opts.timeout) * time.Second
|
||||||
timeout = &timeoutValue
|
timeout = &timeoutValue
|
||||||
}
|
}
|
||||||
return backend.Stop(ctx, project, api.StopOptions{
|
return backend.Stop(ctx, projectName, api.StopOptions{
|
||||||
Timeout: timeout,
|
Timeout: timeout,
|
||||||
Services: services,
|
Services: services,
|
||||||
})
|
})
|
||||||
|
@ -32,7 +32,16 @@ func generateCliYaml(opts *options) error {
|
|||||||
disableFlagsInUseLine(cmd)
|
disableFlagsInUseLine(cmd)
|
||||||
|
|
||||||
cmd.DisableAutoGenTag = true
|
cmd.DisableAutoGenTag = true
|
||||||
return clidocstool.GenYamlTree(cmd, opts.target)
|
tool, err := clidocstool.New(clidocstool.Options{
|
||||||
|
Root: cmd,
|
||||||
|
SourceDir: opts.source,
|
||||||
|
TargetDir: opts.target,
|
||||||
|
Plugin: true,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return tool.GenYamlTree(cmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func disableFlagsInUseLine(cmd *cobra.Command) {
|
func disableFlagsInUseLine(cmd *cobra.Command) {
|
||||||
|
97
go.mod
97
go.mod
@ -6,13 +6,13 @@ require (
|
|||||||
github.com/AlecAivazis/survey/v2 v2.3.2
|
github.com/AlecAivazis/survey/v2 v2.3.2
|
||||||
github.com/buger/goterm v1.0.4
|
github.com/buger/goterm v1.0.4
|
||||||
github.com/cnabio/cnab-to-oci v0.3.1-beta1
|
github.com/cnabio/cnab-to-oci v0.3.1-beta1
|
||||||
github.com/compose-spec/compose-go v1.0.9
|
github.com/compose-spec/compose-go v1.1.0
|
||||||
github.com/containerd/console v1.0.3
|
github.com/containerd/console v1.0.3
|
||||||
github.com/containerd/containerd v1.5.8
|
github.com/containerd/containerd v1.6.0
|
||||||
github.com/distribution/distribution/v3 v3.0.0-20210316161203-a01c71e2477e
|
github.com/distribution/distribution/v3 v3.0.0-20210316161203-a01c71e2477e
|
||||||
github.com/docker/buildx v0.5.2-0.20210422185057-908a856079fc
|
github.com/docker/buildx v0.7.1
|
||||||
github.com/docker/cli v20.10.7+incompatible
|
github.com/docker/cli v20.10.12+incompatible
|
||||||
github.com/docker/cli-docs-tool v0.1.1
|
github.com/docker/cli-docs-tool v0.2.1
|
||||||
github.com/docker/docker v20.10.7+incompatible
|
github.com/docker/docker v20.10.7+incompatible
|
||||||
github.com/docker/go-connections v0.4.0
|
github.com/docker/go-connections v0.4.0
|
||||||
github.com/docker/go-units v0.4.0
|
github.com/docker/go-units v0.4.0
|
||||||
@ -21,7 +21,7 @@ require (
|
|||||||
github.com/hashicorp/go-version v1.3.0
|
github.com/hashicorp/go-version v1.3.0
|
||||||
github.com/mattn/go-isatty v0.0.14
|
github.com/mattn/go-isatty v0.0.14
|
||||||
github.com/mattn/go-shellwords v1.0.12
|
github.com/mattn/go-shellwords v1.0.12
|
||||||
github.com/moby/buildkit v0.8.2-0.20210401015549-df49b648c8bf
|
github.com/moby/buildkit v0.9.1-0.20211019185819-8778943ac3da
|
||||||
github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6
|
github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6
|
||||||
github.com/morikuni/aec v1.0.0
|
github.com/morikuni/aec v1.0.0
|
||||||
github.com/opencontainers/go-digest v1.0.0
|
github.com/opencontainers/go-digest v1.0.0
|
||||||
@ -40,41 +40,38 @@ require (
|
|||||||
require (
|
require (
|
||||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
|
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
|
||||||
github.com/Masterminds/semver v1.5.0 // indirect
|
github.com/Masterminds/semver v1.5.0 // indirect
|
||||||
github.com/Microsoft/go-winio v0.4.17 // indirect
|
github.com/Microsoft/go-winio v0.5.1 // indirect
|
||||||
github.com/Microsoft/hcsshim v0.8.23 // indirect
|
|
||||||
github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412 // indirect
|
github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412 // indirect
|
||||||
github.com/beorn7/perks v1.0.1 // indirect
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
github.com/cespare/xxhash/v2 v2.1.2 // indirect
|
github.com/cespare/xxhash/v2 v2.1.2 // indirect
|
||||||
github.com/cnabio/cnab-go v0.10.0-beta1 // indirect
|
github.com/cnabio/cnab-go v0.10.0-beta1 // indirect
|
||||||
github.com/containerd/cgroups v1.0.1 // indirect
|
github.com/containerd/continuity v0.2.2 // indirect
|
||||||
github.com/containerd/continuity v0.1.0 // indirect
|
|
||||||
github.com/containerd/typeurl v1.0.2 // indirect
|
github.com/containerd/typeurl v1.0.2 // indirect
|
||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
github.com/docker/distribution v2.7.1+incompatible // indirect
|
github.com/docker/distribution v2.8.0+incompatible // indirect
|
||||||
github.com/docker/docker-credential-helpers v0.6.4-0.20210125172408-38bea2ce277a // indirect
|
github.com/docker/docker-credential-helpers v0.6.4 // indirect
|
||||||
github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c // indirect
|
github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c // indirect
|
||||||
github.com/docker/go-metrics v0.0.1 // indirect
|
github.com/docker/go-metrics v0.0.1 // indirect
|
||||||
|
github.com/felixge/httpsnoop v1.0.2 // indirect
|
||||||
github.com/fvbommel/sortorder v1.0.1 // indirect
|
github.com/fvbommel/sortorder v1.0.1 // indirect
|
||||||
github.com/go-logr/logr v0.4.0 // indirect
|
github.com/go-logr/logr v1.2.2 // indirect
|
||||||
|
github.com/go-logr/stdr v1.2.2 // indirect
|
||||||
github.com/gofrs/flock v0.8.0 // indirect
|
github.com/gofrs/flock v0.8.0 // indirect
|
||||||
github.com/gogo/googleapis v1.4.0 // indirect
|
github.com/gogo/googleapis v1.4.0 // indirect
|
||||||
github.com/gogo/protobuf v1.3.2 // indirect
|
github.com/gogo/protobuf v1.3.2 // indirect
|
||||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
|
||||||
github.com/golang/protobuf v1.5.2 // indirect
|
github.com/golang/protobuf v1.5.2 // indirect
|
||||||
github.com/google/go-cmp v0.5.6 // indirect
|
github.com/google/go-cmp v0.5.6 // indirect
|
||||||
github.com/google/gofuzz v1.1.0 // indirect
|
github.com/google/gofuzz v1.2.0 // indirect
|
||||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
|
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
|
||||||
github.com/gorilla/mux v1.8.0 // indirect
|
github.com/gorilla/mux v1.8.0 // indirect
|
||||||
github.com/grpc-ecosystem/go-grpc-middleware v1.2.0 // indirect
|
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 // indirect
|
||||||
github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645 // indirect
|
github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect
|
||||||
github.com/hashicorp/errwrap v1.0.0 // indirect
|
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||||
github.com/imdario/mergo v0.3.12 // indirect
|
github.com/imdario/mergo v0.3.12 // indirect
|
||||||
github.com/inconshreveable/mousetrap v1.0.0 // 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.12 // indirect
|
github.com/json-iterator/go v1.1.12 // indirect
|
||||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
|
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
|
||||||
github.com/klauspost/compress v1.11.13 // indirect
|
github.com/klauspost/compress v1.13.5 // indirect
|
||||||
github.com/kr/pty v1.1.8 // indirect
|
|
||||||
github.com/mattn/go-colorable v0.1.12 // indirect
|
github.com/mattn/go-colorable v0.1.12 // indirect
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // 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/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect
|
||||||
@ -82,48 +79,66 @@ require (
|
|||||||
github.com/mitchellh/mapstructure v1.4.3 // indirect
|
github.com/mitchellh/mapstructure v1.4.3 // indirect
|
||||||
github.com/moby/locker v1.0.1 // indirect
|
github.com/moby/locker v1.0.1 // indirect
|
||||||
github.com/moby/sys/mount v0.2.0 // indirect
|
github.com/moby/sys/mount v0.2.0 // indirect
|
||||||
github.com/moby/sys/mountinfo v0.4.1 // indirect
|
github.com/moby/sys/mountinfo v0.5.0 // indirect
|
||||||
github.com/moby/sys/symlink v0.1.0 // indirect
|
github.com/moby/sys/signal v0.6.0 // indirect
|
||||||
|
github.com/moby/sys/symlink v0.2.0 // indirect
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
github.com/opencontainers/runc v1.0.2 // indirect
|
github.com/opencontainers/runc v1.1.0 // indirect
|
||||||
github.com/opentracing/opentracing-go v1.2.0 // indirect
|
github.com/pelletier/go-toml v1.9.4 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
github.com/prometheus/client_golang v1.7.1 // indirect
|
github.com/prometheus/client_golang v1.11.0 // indirect
|
||||||
github.com/prometheus/client_model v0.2.0 // indirect
|
github.com/prometheus/client_model v0.2.0 // indirect
|
||||||
github.com/prometheus/common v0.10.0 // indirect
|
github.com/prometheus/common v0.30.0 // indirect
|
||||||
github.com/prometheus/procfs v0.6.0 // indirect
|
github.com/prometheus/procfs v0.7.3 // indirect
|
||||||
github.com/qri-io/jsonpointer v0.1.0 // indirect
|
github.com/qri-io/jsonpointer v0.1.0 // indirect
|
||||||
github.com/qri-io/jsonschema v0.1.1 // indirect
|
github.com/qri-io/jsonschema v0.1.1 // indirect
|
||||||
github.com/sergi/go-diff v1.1.0 // indirect
|
github.com/sergi/go-diff v1.1.0 // indirect
|
||||||
github.com/theupdateframework/notary v0.6.1 // indirect
|
github.com/theupdateframework/notary v0.6.1 // indirect
|
||||||
github.com/tonistiigi/fsutil v0.0.0-20201103201449-0834f99b7b85 // indirect
|
github.com/tonistiigi/fsutil v0.0.0-20210818161904-4442383b5028 // indirect
|
||||||
github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea // indirect
|
github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea // indirect
|
||||||
|
github.com/tonistiigi/vt100 v0.0.0-20210615222946-8066bb97264f // indirect
|
||||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
|
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
|
||||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
|
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
|
||||||
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
|
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
|
||||||
go.opencensus.io v0.23.0 // indirect
|
go.opentelemetry.io/contrib v0.21.0 // indirect
|
||||||
|
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.28.0 // indirect
|
||||||
|
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.21.0 // indirect
|
||||||
|
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.21.0 // indirect
|
||||||
|
go.opentelemetry.io/otel v1.3.0 // indirect
|
||||||
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.3.0 // indirect
|
||||||
|
go.opentelemetry.io/otel/internal/metric v0.21.0 // indirect
|
||||||
|
go.opentelemetry.io/otel/metric v0.21.0 // indirect
|
||||||
|
go.opentelemetry.io/otel/sdk v1.3.0 // indirect
|
||||||
|
go.opentelemetry.io/otel/trace v1.3.0 // indirect
|
||||||
|
go.opentelemetry.io/proto/otlp v0.11.0 // indirect
|
||||||
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 // indirect
|
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 // indirect
|
||||||
golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d // indirect
|
golang.org/x/net v0.0.0-20211216030914-fe4d6282115f // indirect
|
||||||
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect
|
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect
|
||||||
golang.org/x/sys v0.0.0-20211205182925-97ca703d548d // indirect
|
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e // indirect
|
||||||
golang.org/x/term v0.0.0-20210503060354-a79de5458b56 // indirect
|
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b // indirect
|
||||||
golang.org/x/text v0.3.7 // indirect
|
golang.org/x/text v0.3.7 // indirect
|
||||||
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba // indirect
|
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect
|
||||||
google.golang.org/appengine v1.6.7 // indirect
|
google.golang.org/appengine v1.6.7 // indirect
|
||||||
google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa // indirect
|
google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa // indirect
|
||||||
google.golang.org/grpc v1.42.0 // indirect
|
google.golang.org/grpc v1.43.0 // indirect
|
||||||
google.golang.org/protobuf v1.27.1 // indirect
|
google.golang.org/protobuf v1.27.1 // indirect
|
||||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
|
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
|
||||||
k8s.io/apimachinery v0.21.0 // indirect
|
k8s.io/apimachinery v0.22.5 // indirect
|
||||||
k8s.io/client-go v0.21.0 // indirect
|
k8s.io/client-go v0.22.5 // indirect
|
||||||
k8s.io/klog/v2 v2.8.0 // indirect
|
k8s.io/klog/v2 v2.30.0 // indirect
|
||||||
k8s.io/utils v0.0.0-20201110183641-67b214c5f920 // indirect
|
k8s.io/utils v0.0.0-20210930125809-cb0fa318a74b // indirect
|
||||||
sigs.k8s.io/structured-merge-diff/v4 v4.1.0 // indirect
|
sigs.k8s.io/structured-merge-diff/v4 v4.1.2 // indirect
|
||||||
sigs.k8s.io/yaml v1.2.0 // indirect
|
sigs.k8s.io/yaml v1.2.0 // indirect
|
||||||
)
|
)
|
||||||
|
|
||||||
// (for buildx)
|
// (for buildx)
|
||||||
replace github.com/jaguilar/vt100 => github.com/tonistiigi/vt100 v0.0.0-20190402012908-ad4c4a574305
|
replace (
|
||||||
|
github.com/docker/cli => github.com/docker/cli v20.10.3-0.20210702143511-f782d1355eff+incompatible
|
||||||
|
github.com/docker/docker => github.com/docker/docker v20.10.3-0.20220121014307-40bb9831756f+incompatible
|
||||||
|
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc => github.com/tonistiigi/opentelemetry-go-contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.0.0-20210714055410-d010b05b4939
|
||||||
|
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace => github.com/tonistiigi/opentelemetry-go-contrib/instrumentation/net/http/httptrace/otelhttptrace v0.0.0-20210714055410-d010b05b4939
|
||||||
|
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp => github.com/tonistiigi/opentelemetry-go-contrib/instrumentation/net/http/otelhttp v0.0.0-20210714055410-d010b05b4939
|
||||||
|
)
|
||||||
|
@ -37,11 +37,11 @@ type Service interface {
|
|||||||
// Create executes the equivalent to a `compose create`
|
// Create executes the equivalent to a `compose create`
|
||||||
Create(ctx context.Context, project *types.Project, opts CreateOptions) error
|
Create(ctx context.Context, project *types.Project, opts CreateOptions) error
|
||||||
// Start executes the equivalent to a `compose start`
|
// Start executes the equivalent to a `compose start`
|
||||||
Start(ctx context.Context, project *types.Project, options StartOptions) error
|
Start(ctx context.Context, projectName string, options StartOptions) error
|
||||||
// Restart restarts containers
|
// Restart restarts containers
|
||||||
Restart(ctx context.Context, project *types.Project, options RestartOptions) error
|
Restart(ctx context.Context, projectName string, options RestartOptions) error
|
||||||
// Stop executes the equivalent to a `compose stop`
|
// Stop executes the equivalent to a `compose stop`
|
||||||
Stop(ctx context.Context, project *types.Project, options StopOptions) error
|
Stop(ctx context.Context, projectName string, options StopOptions) error
|
||||||
// Up executes the equivalent to a `compose up`
|
// Up executes the equivalent to a `compose up`
|
||||||
Up(ctx context.Context, project *types.Project, options UpOptions) error
|
Up(ctx context.Context, project *types.Project, options UpOptions) error
|
||||||
// Down executes the equivalent to a `compose down`
|
// Down executes the equivalent to a `compose down`
|
||||||
@ -407,6 +407,7 @@ type Stack struct {
|
|||||||
ID string
|
ID string
|
||||||
Name string
|
Name string
|
||||||
Status string
|
Status string
|
||||||
|
ConfigFiles string
|
||||||
Reason string
|
Reason string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,9 +28,9 @@ type ServiceProxy struct {
|
|||||||
PushFn func(ctx context.Context, project *types.Project, options PushOptions) error
|
PushFn func(ctx context.Context, project *types.Project, options PushOptions) error
|
||||||
PullFn func(ctx context.Context, project *types.Project, opts PullOptions) error
|
PullFn func(ctx context.Context, project *types.Project, opts PullOptions) error
|
||||||
CreateFn func(ctx context.Context, project *types.Project, opts CreateOptions) error
|
CreateFn func(ctx context.Context, project *types.Project, opts CreateOptions) error
|
||||||
StartFn func(ctx context.Context, project *types.Project, options StartOptions) error
|
StartFn func(ctx context.Context, projectName string, options StartOptions) error
|
||||||
RestartFn func(ctx context.Context, project *types.Project, options RestartOptions) error
|
RestartFn func(ctx context.Context, projectName string, options RestartOptions) error
|
||||||
StopFn func(ctx context.Context, project *types.Project, options StopOptions) error
|
StopFn func(ctx context.Context, projectName string, options StopOptions) error
|
||||||
UpFn func(ctx context.Context, project *types.Project, options UpOptions) error
|
UpFn func(ctx context.Context, project *types.Project, options UpOptions) error
|
||||||
DownFn func(ctx context.Context, projectName string, options DownOptions) error
|
DownFn func(ctx context.Context, projectName string, options DownOptions) error
|
||||||
LogsFn func(ctx context.Context, projectName string, consumer LogConsumer, options LogOptions) error
|
LogsFn func(ctx context.Context, projectName string, consumer LogConsumer, options LogOptions) error
|
||||||
@ -141,36 +141,27 @@ func (s *ServiceProxy) Create(ctx context.Context, project *types.Project, optio
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Start implements Service interface
|
// Start implements Service interface
|
||||||
func (s *ServiceProxy) Start(ctx context.Context, project *types.Project, options StartOptions) error {
|
func (s *ServiceProxy) Start(ctx context.Context, projectName string, options StartOptions) error {
|
||||||
if s.StartFn == nil {
|
if s.StartFn == nil {
|
||||||
return ErrNotImplemented
|
return ErrNotImplemented
|
||||||
}
|
}
|
||||||
for _, i := range s.interceptors {
|
return s.StartFn(ctx, projectName, options)
|
||||||
i(ctx, project)
|
|
||||||
}
|
|
||||||
return s.StartFn(ctx, project, options)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Restart implements Service interface
|
// Restart implements Service interface
|
||||||
func (s *ServiceProxy) Restart(ctx context.Context, project *types.Project, options RestartOptions) error {
|
func (s *ServiceProxy) Restart(ctx context.Context, projectName string, options RestartOptions) error {
|
||||||
if s.RestartFn == nil {
|
if s.RestartFn == nil {
|
||||||
return ErrNotImplemented
|
return ErrNotImplemented
|
||||||
}
|
}
|
||||||
for _, i := range s.interceptors {
|
return s.RestartFn(ctx, projectName, options)
|
||||||
i(ctx, project)
|
|
||||||
}
|
|
||||||
return s.RestartFn(ctx, project, options)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stop implements Service interface
|
// Stop implements Service interface
|
||||||
func (s *ServiceProxy) Stop(ctx context.Context, project *types.Project, options StopOptions) error {
|
func (s *ServiceProxy) Stop(ctx context.Context, projectName string, options StopOptions) error {
|
||||||
if s.StopFn == nil {
|
if s.StopFn == nil {
|
||||||
return ErrNotImplemented
|
return ErrNotImplemented
|
||||||
}
|
}
|
||||||
for _, i := range s.interceptors {
|
return s.StopFn(ctx, projectName, options)
|
||||||
i(ctx, project)
|
|
||||||
}
|
|
||||||
return s.StopFn(ctx, project, options)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Up implements Service interface
|
// Up implements Service interface
|
||||||
|
@ -141,7 +141,7 @@ func (s *composeService) ensureImagesExists(ctx context.Context, project *types.
|
|||||||
if project.Services[i].Labels == nil {
|
if project.Services[i].Labels == nil {
|
||||||
project.Services[i].Labels = types.Labels{}
|
project.Services[i].Labels = types.Labels{}
|
||||||
}
|
}
|
||||||
project.Services[i].Labels[api.ImageDigestLabel] = digest
|
project.Services[i].CustomLabels[api.ImageDigestLabel] = digest
|
||||||
project.Services[i].Image = image
|
project.Services[i].Image = image
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,7 @@ package compose
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/compose-spec/compose-go/types"
|
"github.com/compose-spec/compose-go/types"
|
||||||
"github.com/docker/buildx/build"
|
"github.com/docker/buildx/build"
|
||||||
@ -28,7 +29,7 @@ import (
|
|||||||
|
|
||||||
func (s *composeService) doBuildBuildkit(ctx context.Context, project *types.Project, opts map[string]build.Options, mode string) (map[string]string, error) {
|
func (s *composeService) doBuildBuildkit(ctx context.Context, project *types.Project, opts map[string]build.Options, mode string) (map[string]string, error) {
|
||||||
const drivername = "default"
|
const drivername = "default"
|
||||||
d, err := driver.GetDriver(ctx, drivername, nil, s.apiClient, s.configFile, nil, nil, "", nil, nil, project.WorkingDir)
|
d, err := driver.GetDriver(ctx, drivername, nil, s.apiClient, s.configFile, nil, nil, nil, nil, nil, project.WorkingDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -47,7 +48,7 @@ func (s *composeService) doBuildBuildkit(ctx context.Context, project *types.Pro
|
|||||||
w := xprogress.NewPrinter(progressCtx, os.Stdout, mode)
|
w := xprogress.NewPrinter(progressCtx, os.Stdout, mode)
|
||||||
|
|
||||||
// We rely on buildx "docker" builder integrated in docker engine, so don't need a DockerAPI here
|
// We rely on buildx "docker" builder integrated in docker engine, so don't need a DockerAPI here
|
||||||
response, err := build.Build(ctx, driverInfo, opts, nil, nil, w)
|
response, err := build.Build(ctx, driverInfo, opts, nil, filepath.Dir(s.configFile.Filename), w)
|
||||||
errW := w.Wait()
|
errW := w.Wait()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
err = errW
|
err = errW
|
||||||
|
@ -24,6 +24,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/docker/compose/v2/pkg/api"
|
"github.com/docker/compose/v2/pkg/api"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
"github.com/compose-spec/compose-go/types"
|
"github.com/compose-spec/compose-go/types"
|
||||||
"github.com/docker/cli/cli/config/configfile"
|
"github.com/docker/cli/cli/config/configfile"
|
||||||
@ -92,3 +93,59 @@ func escapeDollarSign(marshal []byte) []byte {
|
|||||||
escDollar := []byte{'$', '$'}
|
escDollar := []byte{'$', '$'}
|
||||||
return bytes.ReplaceAll(marshal, dollar, escDollar)
|
return bytes.ReplaceAll(marshal, dollar, escDollar)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// projectFromName builds a types.Project based on actual resources with compose labels set
|
||||||
|
func (s *composeService) projectFromName(containers Containers, projectName string, services ...string) (*types.Project, error) {
|
||||||
|
project := &types.Project{
|
||||||
|
Name: projectName,
|
||||||
|
}
|
||||||
|
if len(containers) == 0 {
|
||||||
|
return project, errors.New("no such project: " + projectName)
|
||||||
|
}
|
||||||
|
set := map[string]*types.ServiceConfig{}
|
||||||
|
for _, c := range containers {
|
||||||
|
serviceLabel := c.Labels[api.ServiceLabel]
|
||||||
|
_, ok := set[serviceLabel]
|
||||||
|
if !ok {
|
||||||
|
set[serviceLabel] = &types.ServiceConfig{
|
||||||
|
Name: serviceLabel,
|
||||||
|
Image: c.Image,
|
||||||
|
Labels: c.Labels,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
set[serviceLabel].Scale++
|
||||||
|
}
|
||||||
|
for _, service := range set {
|
||||||
|
dependencies := service.Labels[api.DependenciesLabel]
|
||||||
|
if len(dependencies) > 0 {
|
||||||
|
service.DependsOn = types.DependsOnConfig{}
|
||||||
|
for _, dc := range strings.Split(dependencies, ",") {
|
||||||
|
dcArr := strings.Split(dc, ":")
|
||||||
|
condition := ServiceConditionRunningOrHealthy
|
||||||
|
dependency := dcArr[0]
|
||||||
|
|
||||||
|
// backward compatibility
|
||||||
|
if len(dcArr) > 1 {
|
||||||
|
condition = dcArr[1]
|
||||||
|
}
|
||||||
|
service.DependsOn[dependency] = types.ServiceDependency{Condition: condition}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
project.Services = append(project.Services, *service)
|
||||||
|
}
|
||||||
|
SERVICES:
|
||||||
|
for _, qs := range services {
|
||||||
|
for _, es := range project.Services {
|
||||||
|
if es.Name == qs {
|
||||||
|
continue SERVICES
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return project, errors.New("no such service: " + qs)
|
||||||
|
}
|
||||||
|
err := project.ForServices(services)
|
||||||
|
if err != nil {
|
||||||
|
return project, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return project, nil
|
||||||
|
}
|
||||||
|
@ -276,9 +276,10 @@ func (s *composeService) waitDependencies(ctx context.Context, project *types.Pr
|
|||||||
eg, _ := errgroup.WithContext(ctx)
|
eg, _ := errgroup.WithContext(ctx)
|
||||||
w := progress.ContextWriter(ctx)
|
w := progress.ContextWriter(ctx)
|
||||||
for dep, config := range dependencies {
|
for dep, config := range dependencies {
|
||||||
if config.Condition == types.ServiceConditionStarted {
|
if shouldWait, err := shouldWaitForDependency(dep, config, project); err != nil {
|
||||||
// already managed by InDependencyOrder
|
return err
|
||||||
return nil
|
} else if !shouldWait {
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
containers, err := s.getContainers(ctx, project.Name, oneOffExclude, false, dep)
|
containers, err := s.getContainers(ctx, project.Name, oneOffExclude, false, dep)
|
||||||
@ -334,6 +335,20 @@ func (s *composeService) waitDependencies(ctx context.Context, project *types.Pr
|
|||||||
return eg.Wait()
|
return eg.Wait()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func shouldWaitForDependency(serviceName string, dependencyConfig types.ServiceDependency, project *types.Project) (bool, error) {
|
||||||
|
if dependencyConfig.Condition == types.ServiceConditionStarted {
|
||||||
|
// already managed by InDependencyOrder
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
if service, err := project.GetService(serviceName); err != nil {
|
||||||
|
return false, err
|
||||||
|
} else if service.Scale == 0 {
|
||||||
|
// don't wait for the dependency which configured to have 0 containers running
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
func nextContainerNumber(containers []moby.Container) (int, error) {
|
func nextContainerNumber(containers []moby.Container) (int, error) {
|
||||||
max := 0
|
max := 0
|
||||||
for _, c := range containers {
|
for _, c := range containers {
|
||||||
|
@ -19,6 +19,7 @@ package compose
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/compose-spec/compose-go/types"
|
"github.com/compose-spec/compose-go/types"
|
||||||
@ -184,3 +185,31 @@ func TestServiceLinks(t *testing.T) {
|
|||||||
assert.Equal(t, links[2], "testProject-web-1:testProject-web-1")
|
assert.Equal(t, links[2], "testProject-web-1:testProject-web-1")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestWaitDependencies(t *testing.T) {
|
||||||
|
mockCtrl := gomock.NewController(t)
|
||||||
|
defer mockCtrl.Finish()
|
||||||
|
api := mocks.NewMockAPIClient(mockCtrl)
|
||||||
|
tested.apiClient = api
|
||||||
|
|
||||||
|
t.Run("should skip dependencies with scale 0", func(t *testing.T) {
|
||||||
|
dbService := types.ServiceConfig{Name: "db", Scale: 0}
|
||||||
|
redisService := types.ServiceConfig{Name: "redis", Scale: 0}
|
||||||
|
project := types.Project{Name: strings.ToLower(testProject), Services: []types.ServiceConfig{dbService, redisService}}
|
||||||
|
dependencies := types.DependsOnConfig{
|
||||||
|
"db": {Condition: ServiceConditionRunningOrHealthy},
|
||||||
|
"redis": {Condition: ServiceConditionRunningOrHealthy},
|
||||||
|
}
|
||||||
|
assert.NilError(t, tested.waitDependencies(context.Background(), &project, dependencies))
|
||||||
|
})
|
||||||
|
t.Run("should skip dependencies with condition service_started", func(t *testing.T) {
|
||||||
|
dbService := types.ServiceConfig{Name: "db", Scale: 1}
|
||||||
|
redisService := types.ServiceConfig{Name: "redis", Scale: 1}
|
||||||
|
project := types.Project{Name: strings.ToLower(testProject), Services: []types.ServiceConfig{dbService, redisService}}
|
||||||
|
dependencies := types.DependsOnConfig{
|
||||||
|
"db": {Condition: types.ServiceConditionStarted},
|
||||||
|
"redis": {Condition: types.ServiceConditionStarted},
|
||||||
|
}
|
||||||
|
assert.NilError(t, tested.waitDependencies(context.Background(), &project, dependencies))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
@ -229,7 +229,7 @@ func getImageName(service types.ServiceConfig, projectName string) string {
|
|||||||
func (s *composeService) getCreateOptions(ctx context.Context, p *types.Project, service types.ServiceConfig,
|
func (s *composeService) getCreateOptions(ctx context.Context, p *types.Project, service types.ServiceConfig,
|
||||||
number int, inherit *moby.Container, autoRemove bool, attachStdin bool) (*container.Config, *container.HostConfig, *network.NetworkingConfig, error) {
|
number int, inherit *moby.Container, autoRemove bool, attachStdin bool) (*container.Config, *container.HostConfig, *network.NetworkingConfig, error) {
|
||||||
|
|
||||||
labels, err := s.prepareLabels(p, service, number)
|
labels, err := s.prepareLabels(service, number)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
@ -414,31 +414,26 @@ func parseSecurityOpts(p *types.Project, securityOpts []string) ([]string, error
|
|||||||
return securityOpts, nil
|
return securityOpts, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *composeService) prepareLabels(p *types.Project, service types.ServiceConfig, number int) (map[string]string, error) {
|
func (s *composeService) prepareLabels(service types.ServiceConfig, number int) (map[string]string, error) {
|
||||||
labels := map[string]string{}
|
labels := map[string]string{}
|
||||||
for k, v := range service.Labels {
|
for k, v := range service.Labels {
|
||||||
labels[k] = v
|
labels[k] = v
|
||||||
}
|
}
|
||||||
|
for k, v := range service.CustomLabels {
|
||||||
labels[api.ProjectLabel] = p.Name
|
labels[k] = v
|
||||||
labels[api.ServiceLabel] = service.Name
|
|
||||||
labels[api.VersionLabel] = api.ComposeVersion
|
|
||||||
if _, ok := service.Labels[api.OneoffLabel]; !ok {
|
|
||||||
labels[api.OneoffLabel] = "False"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
hash, err := ServiceHash(service)
|
hash, err := ServiceHash(service)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
labels[api.ConfigHashLabel] = hash
|
labels[api.ConfigHashLabel] = hash
|
||||||
labels[api.WorkingDirLabel] = p.WorkingDir
|
|
||||||
labels[api.ConfigFilesLabel] = strings.Join(p.ComposeFiles, ",")
|
|
||||||
labels[api.ContainerNumberLabel] = strconv.Itoa(number)
|
labels[api.ContainerNumberLabel] = strconv.Itoa(number)
|
||||||
|
|
||||||
var dependencies []string
|
var dependencies []string
|
||||||
for s := range service.DependsOn {
|
for s, d := range service.DependsOn {
|
||||||
dependencies = append(dependencies, s)
|
dependencies = append(dependencies, s+":"+d.Condition)
|
||||||
}
|
}
|
||||||
labels[api.DependenciesLabel] = strings.Join(dependencies, ",")
|
labels[api.DependenciesLabel] = strings.Join(dependencies, ",")
|
||||||
return labels, nil
|
return labels, nil
|
||||||
@ -643,15 +638,11 @@ func buildContainerPortBindingOptions(s types.ServiceConfig) nat.PortMap {
|
|||||||
bindings := nat.PortMap{}
|
bindings := nat.PortMap{}
|
||||||
for _, port := range s.Ports {
|
for _, port := range s.Ports {
|
||||||
p := nat.Port(fmt.Sprintf("%d/%s", port.Target, port.Protocol))
|
p := nat.Port(fmt.Sprintf("%d/%s", port.Target, port.Protocol))
|
||||||
bind := bindings[p]
|
|
||||||
binding := nat.PortBinding{
|
binding := nat.PortBinding{
|
||||||
HostIP: port.HostIP,
|
HostIP: port.HostIP,
|
||||||
|
HostPort: port.Published,
|
||||||
}
|
}
|
||||||
if port.Published > 0 {
|
bindings[p] = append(bindings[p], binding)
|
||||||
binding.HostPort = fmt.Sprint(port.Published)
|
|
||||||
}
|
|
||||||
bind = append(bind, binding)
|
|
||||||
bindings[p] = bind
|
|
||||||
}
|
}
|
||||||
return bindings
|
return bindings
|
||||||
}
|
}
|
||||||
@ -1107,7 +1098,7 @@ func (s *composeService) ensureVolume(ctx context.Context, volume types.VolumeCo
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if volume.External.External {
|
if volume.External.External {
|
||||||
return fmt.Errorf("external volume %q not found", volume.External.Name)
|
return fmt.Errorf("external volume %q not found", volume.Name)
|
||||||
}
|
}
|
||||||
err := s.createVolume(ctx, volume)
|
err := s.createVolume(ctx, volume)
|
||||||
return err
|
return err
|
||||||
|
@ -41,6 +41,7 @@ func (s *composeService) Down(ctx context.Context, projectName string, options a
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *composeService) down(ctx context.Context, projectName string, options api.DownOptions) error {
|
func (s *composeService) down(ctx context.Context, projectName string, options api.DownOptions) error {
|
||||||
|
builtFromResources := options.Project == nil
|
||||||
w := progress.ContextWriter(ctx)
|
w := progress.ContextWriter(ctx)
|
||||||
resourceToRemove := false
|
resourceToRemove := false
|
||||||
|
|
||||||
@ -50,8 +51,8 @@ func (s *composeService) down(ctx context.Context, projectName string, options a
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if options.Project == nil {
|
if builtFromResources {
|
||||||
options.Project, err = s.projectFromLabels(ctx, containers.filter(isNotOneOff), projectName)
|
options.Project, err = s.getProjectWithVolumes(ctx, containers, projectName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -232,34 +233,9 @@ func (s *composeService) removeContainers(ctx context.Context, w progress.Writer
|
|||||||
return eg.Wait()
|
return eg.Wait()
|
||||||
}
|
}
|
||||||
|
|
||||||
// projectFromLabels builds a types.Project based on actual resources with compose labels set
|
func (s *composeService) getProjectWithVolumes(ctx context.Context, containers Containers, projectName string) (*types.Project, error) {
|
||||||
func (s *composeService) projectFromLabels(ctx context.Context, containers Containers, projectName string) (*types.Project, error) {
|
containers = containers.filter(isNotOneOff)
|
||||||
project := &types.Project{
|
project, _ := s.projectFromName(containers, projectName)
|
||||||
Name: projectName,
|
|
||||||
}
|
|
||||||
if len(containers) == 0 {
|
|
||||||
return project, nil
|
|
||||||
}
|
|
||||||
set := map[string]moby.Container{}
|
|
||||||
for _, c := range containers {
|
|
||||||
set[c.Labels[api.ServiceLabel]] = c
|
|
||||||
}
|
|
||||||
for s, c := range set {
|
|
||||||
service := types.ServiceConfig{
|
|
||||||
Name: s,
|
|
||||||
Image: c.Image,
|
|
||||||
Labels: c.Labels,
|
|
||||||
}
|
|
||||||
dependencies := c.Labels[api.DependenciesLabel]
|
|
||||||
if len(dependencies) > 0 {
|
|
||||||
service.DependsOn = types.DependsOnConfig{}
|
|
||||||
for _, d := range strings.Split(dependencies, ",") {
|
|
||||||
service.DependsOn[d] = types.ServiceDependency{}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
project.Services = append(project.Services, service)
|
|
||||||
}
|
|
||||||
|
|
||||||
volumes, err := s.apiClient.VolumeList(ctx, filters.NewArgs(projectFilter(projectName)))
|
volumes, err := s.apiClient.VolumeList(ctx, filters.NewArgs(projectFilter(projectName)))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -273,6 +249,5 @@ func (s *composeService) projectFromLabels(ctx context.Context, containers Conta
|
|||||||
Labels: vol.Labels,
|
Labels: vol.Labels,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return project, nil
|
return project, nil
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,6 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// ServiceHash compute configuration has for a service
|
// ServiceHash compute configuration has for a service
|
||||||
// TODO move this to compose-go
|
|
||||||
func ServiceHash(o types.ServiceConfig) (string, error) {
|
func ServiceHash(o types.ServiceConfig) (string, error) {
|
||||||
// remove the Build config when generating the service hash
|
// remove the Build config when generating the service hash
|
||||||
o.Build = nil
|
o.Build = nil
|
||||||
|
@ -20,8 +20,10 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"sort"
|
"sort"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/docker/compose/v2/pkg/api"
|
"github.com/docker/compose/v2/pkg/api"
|
||||||
|
"github.com/docker/compose/v2/pkg/utils"
|
||||||
|
|
||||||
moby "github.com/docker/docker/api/types"
|
moby "github.com/docker/docker/api/types"
|
||||||
"github.com/docker/docker/api/types/filters"
|
"github.com/docker/docker/api/types/filters"
|
||||||
@ -46,15 +48,40 @@ func containersToStacks(containers []moby.Container) ([]api.Stack, error) {
|
|||||||
}
|
}
|
||||||
var projects []api.Stack
|
var projects []api.Stack
|
||||||
for _, project := range keys {
|
for _, project := range keys {
|
||||||
|
configFiles, err := combinedConfigFiles(containersByLabel[project])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
projects = append(projects, api.Stack{
|
projects = append(projects, api.Stack{
|
||||||
ID: project,
|
ID: project,
|
||||||
Name: project,
|
Name: project,
|
||||||
Status: combinedStatus(containerToState(containersByLabel[project])),
|
Status: combinedStatus(containerToState(containersByLabel[project])),
|
||||||
|
ConfigFiles: configFiles,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return projects, nil
|
return projects, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func combinedConfigFiles(containers []moby.Container) (string, error) {
|
||||||
|
configFiles := []string{}
|
||||||
|
|
||||||
|
for _, c := range containers {
|
||||||
|
files, ok := c.Labels[api.ConfigFilesLabel]
|
||||||
|
if !ok {
|
||||||
|
return "", fmt.Errorf("No label %q set on container %q of compose project", api.ConfigFilesLabel, c.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, f := range strings.Split(files, ",") {
|
||||||
|
if !utils.StringContains(configFiles, f) {
|
||||||
|
configFiles = append(configFiles, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings.Join(configFiles, ","), nil
|
||||||
|
}
|
||||||
|
|
||||||
func containerToState(containers []moby.Container) []string {
|
func containerToState(containers []moby.Container) []string {
|
||||||
statuses := []string{}
|
statuses := []string{}
|
||||||
for _, c := range containers {
|
for _, c := range containers {
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
package compose
|
package compose
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/docker/compose/v2/pkg/api"
|
"github.com/docker/compose/v2/pkg/api"
|
||||||
@ -30,17 +31,17 @@ func TestContainersToStacks(t *testing.T) {
|
|||||||
{
|
{
|
||||||
ID: "service1",
|
ID: "service1",
|
||||||
State: "running",
|
State: "running",
|
||||||
Labels: map[string]string{api.ProjectLabel: "project1"},
|
Labels: map[string]string{api.ProjectLabel: "project1", api.ConfigFilesLabel: "/home/docker-compose.yaml"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ID: "service2",
|
ID: "service2",
|
||||||
State: "running",
|
State: "running",
|
||||||
Labels: map[string]string{api.ProjectLabel: "project1"},
|
Labels: map[string]string{api.ProjectLabel: "project1", api.ConfigFilesLabel: "/home/docker-compose.yaml"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ID: "service3",
|
ID: "service3",
|
||||||
State: "running",
|
State: "running",
|
||||||
Labels: map[string]string{api.ProjectLabel: "project2"},
|
Labels: map[string]string{api.ProjectLabel: "project2", api.ConfigFilesLabel: "/home/project2-docker-compose.yaml"},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
stacks, err := containersToStacks(containers)
|
stacks, err := containersToStacks(containers)
|
||||||
@ -50,11 +51,13 @@ func TestContainersToStacks(t *testing.T) {
|
|||||||
ID: "project1",
|
ID: "project1",
|
||||||
Name: "project1",
|
Name: "project1",
|
||||||
Status: "running(2)",
|
Status: "running(2)",
|
||||||
|
ConfigFiles: "/home/docker-compose.yaml",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ID: "project2",
|
ID: "project2",
|
||||||
Name: "project2",
|
Name: "project2",
|
||||||
Status: "running(1)",
|
Status: "running(1)",
|
||||||
|
ConfigFiles: "/home/project2-docker-compose.yaml",
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -64,3 +67,56 @@ func TestStacksMixedStatus(t *testing.T) {
|
|||||||
assert.Equal(t, combinedStatus([]string{"running", "running", "running"}), "running(3)")
|
assert.Equal(t, combinedStatus([]string{"running", "running", "running"}), "running(3)")
|
||||||
assert.Equal(t, combinedStatus([]string{"running", "exited", "running"}), "exited(1), running(2)")
|
assert.Equal(t, combinedStatus([]string{"running", "exited", "running"}), "exited(1), running(2)")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCombinedConfigFiles(t *testing.T) {
|
||||||
|
containersByLabel := map[string][]moby.Container{
|
||||||
|
"project1": {
|
||||||
|
{
|
||||||
|
ID: "service1",
|
||||||
|
State: "running",
|
||||||
|
Labels: map[string]string{api.ProjectLabel: "project1", api.ConfigFilesLabel: "/home/docker-compose.yaml"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: "service2",
|
||||||
|
State: "running",
|
||||||
|
Labels: map[string]string{api.ProjectLabel: "project1", api.ConfigFilesLabel: "/home/docker-compose.yaml"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"project2": {
|
||||||
|
{
|
||||||
|
ID: "service3",
|
||||||
|
State: "running",
|
||||||
|
Labels: map[string]string{api.ProjectLabel: "project2", api.ConfigFilesLabel: "/home/project2-docker-compose.yaml"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"project3": {
|
||||||
|
{
|
||||||
|
ID: "service4",
|
||||||
|
State: "running",
|
||||||
|
Labels: map[string]string{api.ProjectLabel: "project3"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
testData := map[string]struct {
|
||||||
|
ConfigFiles string
|
||||||
|
Error error
|
||||||
|
}{
|
||||||
|
"project1": {ConfigFiles: "/home/docker-compose.yaml", Error: nil},
|
||||||
|
"project2": {ConfigFiles: "/home/project2-docker-compose.yaml", Error: nil},
|
||||||
|
"project3": {ConfigFiles: "", Error: fmt.Errorf("No label %q set on container %q of compose project", api.ConfigFilesLabel, "service4")},
|
||||||
|
}
|
||||||
|
|
||||||
|
for project, containers := range containersByLabel {
|
||||||
|
configFiles, err := combinedConfigFiles(containers)
|
||||||
|
|
||||||
|
expected := testData[project]
|
||||||
|
|
||||||
|
if expected.Error != nil {
|
||||||
|
assert.Equal(t, err.Error(), expected.Error.Error())
|
||||||
|
} else {
|
||||||
|
assert.Equal(t, err, expected.Error)
|
||||||
|
}
|
||||||
|
assert.Equal(t, configFiles, expected.ConfigFiles)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -33,7 +33,7 @@ func (s *composeService) Pause(ctx context.Context, project string, options api.
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *composeService) pause(ctx context.Context, project string, options api.PauseOptions) error {
|
func (s *composeService) pause(ctx context.Context, project string, options api.PauseOptions) error {
|
||||||
containers, err := s.getContainers(ctx, project, oneOffExclude, true, options.Services...)
|
containers, err := s.getContainers(ctx, project, oneOffExclude, false, options.Services...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -61,7 +61,7 @@ func (s *composeService) UnPause(ctx context.Context, project string, options ap
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *composeService) unPause(ctx context.Context, project string, options api.PauseOptions) error {
|
func (s *composeService) unPause(ctx context.Context, project string, options api.PauseOptions) error {
|
||||||
containers, err := s.getContainers(ctx, project, oneOffExclude, true, options.Services...)
|
containers, err := s.getContainers(ctx, project, oneOffExclude, false, options.Services...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,6 @@ package compose
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
"github.com/compose-spec/compose-go/types"
|
|
||||||
"github.com/docker/compose/v2/pkg/api"
|
"github.com/docker/compose/v2/pkg/api"
|
||||||
"golang.org/x/sync/errgroup"
|
"golang.org/x/sync/errgroup"
|
||||||
|
|
||||||
@ -27,14 +26,20 @@ import (
|
|||||||
"github.com/docker/compose/v2/pkg/utils"
|
"github.com/docker/compose/v2/pkg/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *composeService) Restart(ctx context.Context, project *types.Project, options api.RestartOptions) error {
|
func (s *composeService) Restart(ctx context.Context, projectName string, options api.RestartOptions) error {
|
||||||
return progress.Run(ctx, func(ctx context.Context) error {
|
return progress.Run(ctx, func(ctx context.Context) error {
|
||||||
return s.restart(ctx, project, options)
|
return s.restart(ctx, projectName, options)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *composeService) restart(ctx context.Context, project *types.Project, options api.RestartOptions) error {
|
func (s *composeService) restart(ctx context.Context, projectName string, options api.RestartOptions) error {
|
||||||
observedState, err := s.getContainers(ctx, project.Name, oneOffInclude, true)
|
|
||||||
|
observedState, err := s.getContainers(ctx, projectName, oneOffInclude, true)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
project, err := s.projectFromName(observedState, projectName, options.Services...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -153,8 +153,9 @@ func (s *composeService) prepareRun(ctx context.Context, project *types.Project,
|
|||||||
if service.Deploy != nil {
|
if service.Deploy != nil {
|
||||||
service.Deploy.RestartPolicy = nil
|
service.Deploy.RestartPolicy = nil
|
||||||
}
|
}
|
||||||
service.Labels = service.Labels.Add(api.SlugLabel, slug)
|
service.CustomLabels = service.CustomLabels.
|
||||||
service.Labels = service.Labels.Add(api.OneoffLabel, "True")
|
Add(api.SlugLabel, slug).
|
||||||
|
Add(api.OneoffLabel, "True")
|
||||||
|
|
||||||
if err := s.ensureImagesExists(ctx, project, opts.QuietPull); 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
|
return "", err
|
||||||
|
@ -28,15 +28,22 @@ import (
|
|||||||
"github.com/docker/compose/v2/pkg/progress"
|
"github.com/docker/compose/v2/pkg/progress"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *composeService) Start(ctx context.Context, project *types.Project, options api.StartOptions) error {
|
func (s *composeService) Start(ctx context.Context, projectName string, options api.StartOptions) error {
|
||||||
return progress.Run(ctx, func(ctx context.Context) error {
|
return progress.Run(ctx, func(ctx context.Context) error {
|
||||||
return s.start(ctx, project, options, nil)
|
return s.start(ctx, projectName, options, nil)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *composeService) start(ctx context.Context, project *types.Project, options api.StartOptions, listener api.ContainerEventListener) error {
|
func (s *composeService) start(ctx context.Context, projectName string, options api.StartOptions, listener api.ContainerEventListener) error {
|
||||||
if len(options.AttachTo) == 0 {
|
var containers Containers
|
||||||
options.AttachTo = project.ServiceNames()
|
containers, err := s.getContainers(ctx, projectName, oneOffExclude, true)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
project, err := s.projectFromName(containers, projectName, options.AttachTo...)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
eg, ctx := errgroup.WithContext(ctx)
|
eg, ctx := errgroup.WithContext(ctx)
|
||||||
@ -53,7 +60,7 @@ func (s *composeService) start(ctx context.Context, project *types.Project, opti
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
err := InDependencyOrder(ctx, project, func(c context.Context, name string) error {
|
err = InDependencyOrder(ctx, project, func(c context.Context, name string) error {
|
||||||
service, err := project.GetService(name)
|
service, err := project.GetService(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -21,25 +21,29 @@ import (
|
|||||||
|
|
||||||
"github.com/docker/compose/v2/pkg/api"
|
"github.com/docker/compose/v2/pkg/api"
|
||||||
"github.com/docker/compose/v2/pkg/progress"
|
"github.com/docker/compose/v2/pkg/progress"
|
||||||
|
|
||||||
"github.com/compose-spec/compose-go/types"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *composeService) Stop(ctx context.Context, project *types.Project, options api.StopOptions) error {
|
func (s *composeService) Stop(ctx context.Context, projectName string, options api.StopOptions) error {
|
||||||
return progress.Run(ctx, func(ctx context.Context) error {
|
return progress.Run(ctx, func(ctx context.Context) error {
|
||||||
return s.stop(ctx, project, options)
|
return s.stop(ctx, projectName, options)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *composeService) stop(ctx context.Context, project *types.Project, options api.StopOptions) error {
|
func (s *composeService) stop(ctx context.Context, projectName string, options api.StopOptions) error {
|
||||||
w := progress.ContextWriter(ctx)
|
w := progress.ContextWriter(ctx)
|
||||||
|
|
||||||
services := options.Services
|
services := options.Services
|
||||||
if len(services) == 0 {
|
if len(services) == 0 {
|
||||||
services = project.ServiceNames()
|
services = []string{}
|
||||||
}
|
}
|
||||||
|
|
||||||
var containers Containers
|
var containers Containers
|
||||||
containers, err := s.getContainers(ctx, project.Name, oneOffInclude, true, services...)
|
containers, err := s.getContainers(ctx, projectName, oneOffInclude, true, services...)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
project, err := s.projectFromName(containers, projectName, services...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,6 @@ import (
|
|||||||
compose "github.com/docker/compose/v2/pkg/api"
|
compose "github.com/docker/compose/v2/pkg/api"
|
||||||
"github.com/docker/compose/v2/pkg/mocks"
|
"github.com/docker/compose/v2/pkg/mocks"
|
||||||
|
|
||||||
"github.com/compose-spec/compose-go/types"
|
|
||||||
moby "github.com/docker/docker/api/types"
|
moby "github.com/docker/docker/api/types"
|
||||||
"github.com/golang/mock/gomock"
|
"github.com/golang/mock/gomock"
|
||||||
"gotest.tools/v3/assert"
|
"gotest.tools/v3/assert"
|
||||||
@ -50,13 +49,7 @@ func TestStopTimeout(t *testing.T) {
|
|||||||
api.EXPECT().ContainerStop(gomock.Any(), "456", &timeout).Return(nil)
|
api.EXPECT().ContainerStop(gomock.Any(), "456", &timeout).Return(nil)
|
||||||
api.EXPECT().ContainerStop(gomock.Any(), "789", &timeout).Return(nil)
|
api.EXPECT().ContainerStop(gomock.Any(), "789", &timeout).Return(nil)
|
||||||
|
|
||||||
err := tested.Stop(ctx, &types.Project{
|
err := tested.Stop(ctx, strings.ToLower(testProject), compose.StopOptions{
|
||||||
Name: strings.ToLower(testProject),
|
|
||||||
Services: []types.ServiceConfig{
|
|
||||||
{Name: "service1"},
|
|
||||||
{Name: "service2"},
|
|
||||||
},
|
|
||||||
}, compose.StopOptions{
|
|
||||||
Timeout: &timeout,
|
Timeout: &timeout,
|
||||||
})
|
})
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
|
@ -38,7 +38,7 @@ func (s *composeService) Up(ctx context.Context, project *types.Project, options
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if options.Start.Attach == nil {
|
if options.Start.Attach == nil {
|
||||||
return s.start(ctx, project, options.Start, nil)
|
return s.start(ctx, project.Name, options.Start, nil)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
@ -65,7 +65,7 @@ func (s *composeService) Up(ctx context.Context, project *types.Project, options
|
|||||||
})
|
})
|
||||||
}()
|
}()
|
||||||
|
|
||||||
return s.Stop(ctx, project, api.StopOptions{
|
return s.Stop(ctx, project.Name, api.StopOptions{
|
||||||
Services: options.Create.Services,
|
Services: options.Create.Services,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -85,7 +85,7 @@ func (s *composeService) Up(ctx context.Context, project *types.Project, options
|
|||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
|
|
||||||
err = s.start(ctx, project, options.Start, printer.HandleEvent)
|
err = s.start(ctx, project.Name, options.Start, printer.HandleEvent)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user