浏览代码

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

Vedant Koditkar 3 年之前
父节点
当前提交
89dfb9140e

+ 3 - 3
.github/workflows/ci.yml

@@ -28,9 +28,9 @@ jobs:
       - name: Run golangci-lint
       - name: Run golangci-lint
         env:
         env:
           BUILD_TAGS: e2e
           BUILD_TAGS: e2e
-        run: |
-          curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sudo sh -s -- -b /usr/bin/ v1.39.0
-          make -f builder.Makefile lint
+        uses: golangci/golangci-lint-action@v2
+        with:
+          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:

+ 19 - 18
cmd/compose/compose.go

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

+ 14 - 1
cmd/compose/down.go

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

+ 8 - 6
cmd/compose/list.go

@@ -92,22 +92,24 @@ 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
-	Status string
+	Name        string
+	Status      string
+	ConfigFiles string
 }
 }
 
 
 func viewFromStackList(stackList []api.Stack) []stackView {
 func viewFromStackList(stackList []api.Stack) []stackView {
 	retList := make([]stackView, len(stackList))
 	retList := make([]stackView, len(stackList))
 	for i, s := range stackList {
 	for i, s := range stackList {
 		retList[i] = stackView{
 		retList[i] = stackView{
-			Name:   s.Name,
-			Status: strings.TrimSpace(fmt.Sprintf("%s %s", s.Status, s.Reason)),
+			Name:        s.Name,
+			Status:      strings.TrimSpace(fmt.Sprintf("%s %s", s.Status, s.Reason)),
+			ConfigFiles: s.ConfigFiles,
 		}
 		}
 	}
 	}
 	return retList
 	return retList

+ 1 - 1
cmd/compose/remove.go

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

+ 2 - 2
cmd/compose/restart.go

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

+ 1 - 1
cmd/compose/run.go

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

+ 4 - 2
cmd/compose/start.go

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

+ 2 - 2
cmd/compose/stop.go

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

+ 10 - 1
docs/yaml/main/generate.go

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

+ 56 - 41
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/cli v20.10.7+incompatible
-	github.com/docker/cli-docs-tool v0.1.1
+	github.com/docker/buildx v0.7.1
+	github.com/docker/cli v20.10.12+incompatible
+	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/hcsshim v0.8.23 // indirect
+	github.com/Microsoft/go-winio v0.5.1 // 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.1.0 // indirect
+	github.com/containerd/continuity v0.2.2 // 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/docker-credential-helpers v0.6.4-0.20210125172408-38bea2ce277a // indirect
+	github.com/docker/distribution v2.8.0+incompatible // 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/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645 // indirect
-	github.com/hashicorp/errwrap v1.0.0 // indirect
+	github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 // indirect
+	github.com/grpc-ecosystem/grpc-gateway v1.16.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/kr/pty v1.1.8 // indirect
+	github.com/klauspost/compress v1.13.5 // 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/symlink v0.1.0 // indirect
+	github.com/moby/sys/mountinfo v0.5.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/opentracing/opentracing-go v1.2.0 // indirect
+	github.com/opencontainers/runc v1.1.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/procfs v0.6.0 // indirect
+	github.com/prometheus/common v0.30.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/term v0.0.0-20210503060354-a79de5458b56 // indirect
+	golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e // 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/client-go v0.21.0 // indirect
-	k8s.io/klog/v2 v2.8.0 // indirect
-	k8s.io/utils v0.0.0-20201110183641-67b214c5f920 // indirect
-	sigs.k8s.io/structured-merge-diff/v4 v4.1.0 // indirect
+	k8s.io/apimachinery v0.22.5 // indirect
+	k8s.io/client-go v0.22.5 // indirect
+	k8s.io/klog/v2 v2.30.0 // indirect
+	k8s.io/utils v0.0.0-20210930125809-cb0fa318a74b // 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
+)

文件差异内容过多而无法显示
+ 337 - 49
go.sum


+ 8 - 7
pkg/api/api.go

@@ -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`
@@ -404,10 +404,11 @@ const (
 
 
 // Stack holds the name and state of a compose application/stack
 // Stack holds the name and state of a compose application/stack
 type Stack struct {
 type Stack struct {
-	ID     string
-	Name   string
-	Status string
-	Reason string
+	ID          string
+	Name        string
+	Status      string
+	ConfigFiles string
+	Reason      string
 }
 }
 
 
 // LogConsumer is a callback to process log messages from services
 // LogConsumer is a callback to process log messages from services

+ 9 - 18
pkg/api/proxy.go

@@ -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
-	RestartFn            func(ctx context.Context, project *types.Project, options RestartOptions) error
-	StopFn               func(ctx context.Context, project *types.Project, options StopOptions) error
+	StartFn              func(ctx context.Context, projectName string, options StartOptions) error
+	RestartFn            func(ctx context.Context, projectName string, options RestartOptions) 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 {
-		i(ctx, project)
-	}
-	return s.StartFn(ctx, project, options)
+	return s.StartFn(ctx, projectName, 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 {
-		i(ctx, project)
-	}
-	return s.RestartFn(ctx, project, options)
+	return s.RestartFn(ctx, projectName, 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 {
-		i(ctx, project)
-	}
-	return s.StopFn(ctx, project, options)
+	return s.StopFn(ctx, projectName, options)
 }
 }
 
 
 // Up implements Service interface
 // Up implements Service interface

+ 1 - 1
pkg/compose/build.go

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

+ 3 - 2
pkg/compose/build_buildkit.go

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

+ 57 - 0
pkg/compose/compose.go

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

+ 18 - 3
pkg/compose/convergence.go

@@ -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 {
-			// already managed by InDependencyOrder
-			return nil
+		if shouldWait, err := shouldWaitForDependency(dep, config, project); err != nil {
+			return err
+		} 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 {

+ 29 - 0
pkg/compose/convergence_test.go

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

+ 12 - 21
pkg/compose/create.go

@@ -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
 	}
 	}
-
-	labels[api.ProjectLabel] = p.Name
-	labels[api.ServiceLabel] = service.Name
-	labels[api.VersionLabel] = api.ComposeVersion
-	if _, ok := service.Labels[api.OneoffLabel]; !ok {
-		labels[api.OneoffLabel] = "False"
+	for k, v := range service.CustomLabels {
+		labels[k] = v
 	}
 	}
 
 
 	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 {
-		dependencies = append(dependencies, s)
+	for s, d := range service.DependsOn {
+		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,
-		}
-		if port.Published > 0 {
-			binding.HostPort = fmt.Sprint(port.Published)
+			HostIP:   port.HostIP,
+			HostPort: port.Published,
 		}
 		}
-		bind = append(bind, binding)
-		bindings[p] = bind
+		bindings[p] = append(bindings[p], binding)
 	}
 	}
 	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

+ 6 - 31
pkg/compose/down.go

@@ -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 {
-		options.Project, err = s.projectFromLabels(ctx, containers.filter(isNotOneOff), projectName)
+	if builtFromResources {
+		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) projectFromLabels(ctx context.Context, containers Containers, projectName string) (*types.Project, error) {
-	project := &types.Project{
-		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)
-	}
-
+func (s *composeService) getProjectWithVolumes(ctx context.Context, containers Containers, projectName string) (*types.Project, error) {
+	containers = containers.filter(isNotOneOff)
+	project, _ := s.projectFromName(containers, projectName)
 	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
 }
 }

+ 0 - 1
pkg/compose/hash.go

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

+ 30 - 3
pkg/compose/ls.go

@@ -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,
-			Name:   project,
-			Status: combinedStatus(containerToState(containersByLabel[project])),
+			ID:          project,
+			Name:        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 {

+ 65 - 9
pkg/compose/ls_test.go

@@ -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,31 +31,33 @@ 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)
 	assert.NilError(t, err)
 	assert.NilError(t, err)
 	assert.DeepEqual(t, stacks, []api.Stack{
 	assert.DeepEqual(t, stacks, []api.Stack{
 		{
 		{
-			ID:     "project1",
-			Name:   "project1",
-			Status: "running(2)",
+			ID:          "project1",
+			Name:        "project1",
+			Status:      "running(2)",
+			ConfigFiles: "/home/docker-compose.yaml",
 		},
 		},
 		{
 		{
-			ID:     "project2",
-			Name:   "project2",
-			Status: "running(1)",
+			ID:          "project2",
+			Name:        "project2",
+			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)
+	}
+}

+ 2 - 2
pkg/compose/pause.go

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

+ 10 - 5
pkg/compose/restart.go

@@ -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 {
-	observedState, err := s.getContainers(ctx, project.Name, oneOffInclude, true)
+func (s *composeService) restart(ctx context.Context, projectName string, options api.RestartOptions) error {
+
+	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
 	}
 	}

+ 3 - 2
pkg/compose/run.go

@@ -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.Labels = service.Labels.Add(api.OneoffLabel, "True")
+	service.CustomLabels = service.CustomLabels.
+		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

+ 13 - 6
pkg/compose/start.go

@@ -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 {
-	if len(options.AttachTo) == 0 {
-		options.AttachTo = project.ServiceNames()
+func (s *composeService) start(ctx context.Context, projectName string, options api.StartOptions, listener api.ContainerEventListener) error {
+	var containers Containers
+	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

+ 11 - 7
pkg/compose/stop.go

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

+ 1 - 8
pkg/compose/stop_test.go

@@ -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{
-		Name: strings.ToLower(testProject),
-		Services: []types.ServiceConfig{
-			{Name: "service1"},
-			{Name: "service2"},
-		},
-	}, compose.StopOptions{
+	err := tested.Stop(ctx, strings.ToLower(testProject), compose.StopOptions{
 		Timeout: &timeout,
 		Timeout: &timeout,
 	})
 	})
 	assert.NilError(t, err)
 	assert.NilError(t, err)

+ 3 - 3
pkg/compose/up.go

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

文件差异内容过多而无法显示
+ 134 - 133
pkg/mocks/mock_docker_api.go


部分文件因为文件数量过多而无法显示