Merge branch 'v2' into 8768-avoid-pulling-same-image-multiple-times

This commit is contained in:
Vedant Koditkar 2022-03-05 21:53:55 +05:30
commit 89dfb9140e
32 changed files with 1319 additions and 542 deletions

View File

@ -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:

View File

@ -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 {

View File

@ -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
} }

View File

@ -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

View File

@ -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 {

View File

@ -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,
}) })

View File

@ -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{})
} }

View File

@ -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,
})
} }

View File

@ -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,
}) })

View File

@ -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
View File

@ -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
)

744
go.sum

File diff suppressed because it is too large Load Diff

View File

@ -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
} }

View File

@ -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

View File

@ -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
} }
} }

View File

@ -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

View File

@ -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
}

View File

@ -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 {

View File

@ -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))
})
}

View File

@ -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

View File

@ -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
} }

View File

@ -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

View File

@ -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 {

View File

@ -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)
}
}

View File

@ -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
} }

View File

@ -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
} }

View File

@ -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

View File

@ -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

View File

@ -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
} }

View File

@ -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)

View File

@ -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