소스 검색

build full compose model from resources, then filter by services

Signed-off-by: Nicolas De Loof <[email protected]>
Nicolas De Loof 3 년 전
부모
커밋
158b5ff6a3
8개의 변경된 파일60개의 추가작업 그리고 32개의 파일을 삭제
  1. 2 2
      cmd/compose/remove.go
  2. 1 1
      pkg/api/api.go
  3. 2 5
      pkg/api/proxy.go
  4. 20 0
      pkg/compose/compose.go
  5. 2 8
      pkg/compose/remove.go
  6. 1 16
      pkg/compose/stop.go
  7. 8 0
      pkg/e2e/fixtures/dependencies/compose.yaml
  8. 24 0
      pkg/e2e/start_stop_test.go

+ 2 - 2
cmd/compose/remove.go

@@ -59,13 +59,13 @@ Any data which is not in a volume will be lost.`,
 }
 
 func runRemove(ctx context.Context, backend api.Service, opts removeOptions, services []string) error {
-	project, err := opts.toProject(services)
+	project, err := opts.toProjectName()
 	if err != nil {
 		return err
 	}
 
 	if opts.stop {
-		err := backend.Stop(ctx, project.Name, api.StopOptions{
+		err := backend.Stop(ctx, project, api.StopOptions{
 			Services: services,
 		})
 		if err != nil {

+ 1 - 1
pkg/api/api.go

@@ -59,7 +59,7 @@ type Service interface {
 	// RunOneOffContainer creates a service oneoff container and starts its dependencies
 	RunOneOffContainer(ctx context.Context, project *types.Project, opts RunOptions) (int, error)
 	// Remove executes the equivalent to a `compose rm`
-	Remove(ctx context.Context, project *types.Project, options RemoveOptions) error
+	Remove(ctx context.Context, project string, options RemoveOptions) error
 	// Exec executes a command in a running service container
 	Exec(ctx context.Context, project string, opts RunOptions) (int, error)
 	// Copy copies a file/folder between a service container and the local filesystem

+ 2 - 5
pkg/api/proxy.go

@@ -39,7 +39,7 @@ type ServiceProxy struct {
 	ConvertFn            func(ctx context.Context, project *types.Project, options ConvertOptions) ([]byte, error)
 	KillFn               func(ctx context.Context, project *types.Project, options KillOptions) error
 	RunOneOffContainerFn func(ctx context.Context, project *types.Project, opts RunOptions) (int, error)
-	RemoveFn             func(ctx context.Context, project *types.Project, options RemoveOptions) error
+	RemoveFn             func(ctx context.Context, project string, options RemoveOptions) error
 	ExecFn               func(ctx context.Context, project string, opts RunOptions) (int, error)
 	CopyFn               func(ctx context.Context, project string, options CopyOptions) error
 	PauseFn              func(ctx context.Context, project string, options PauseOptions) error
@@ -241,13 +241,10 @@ func (s *ServiceProxy) RunOneOffContainer(ctx context.Context, project *types.Pr
 }
 
 // Remove implements Service interface
-func (s *ServiceProxy) Remove(ctx context.Context, project *types.Project, options RemoveOptions) error {
+func (s *ServiceProxy) Remove(ctx context.Context, project string, options RemoveOptions) error {
 	if s.RemoveFn == nil {
 		return ErrNotImplemented
 	}
-	for _, i := range s.interceptors {
-		i(ctx, project)
-	}
 	return s.RemoveFn(ctx, project, options)
 }
 

+ 20 - 0
pkg/compose/compose.go

@@ -149,3 +149,23 @@ SERVICES:
 
 	return project, nil
 }
+
+// actualState list resources labelled by projectName to rebuild compose project model
+func (s *composeService) actualState(ctx context.Context, projectName string, services []string) (Containers, *types.Project, error) {
+	var containers Containers
+	// don't filter containers by options.Services so projectFromName can rebuild project with all existing resources
+	containers, err := s.getContainers(ctx, projectName, oneOffInclude, true)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	project, err := s.projectFromName(containers, projectName, services...)
+	if err != nil && !api.IsNotFoundError(err) {
+		return nil, nil, err
+	}
+
+	if len(services) > 0 {
+		containers = containers.filter(isService(services...))
+	}
+	return containers, project, nil
+}

+ 2 - 8
pkg/compose/remove.go

@@ -21,7 +21,6 @@ import (
 	"fmt"
 	"strings"
 
-	"github.com/compose-spec/compose-go/types"
 	"github.com/docker/compose/v2/pkg/api"
 	moby "github.com/docker/docker/api/types"
 	"golang.org/x/sync/errgroup"
@@ -30,13 +29,8 @@ import (
 	"github.com/docker/compose/v2/pkg/prompt"
 )
 
-func (s *composeService) Remove(ctx context.Context, project *types.Project, options api.RemoveOptions) error {
-	services := options.Services
-	if len(services) == 0 {
-		services = project.ServiceNames()
-	}
-
-	containers, err := s.getContainers(ctx, project.Name, oneOffInclude, true, services...)
+func (s *composeService) Remove(ctx context.Context, projectName string, options api.RemoveOptions) error {
+	containers, _, err := s.actualState(ctx, projectName, options.Services)
 	if err != nil {
 		return err
 	}

+ 1 - 16
pkg/compose/stop.go

@@ -32,26 +32,11 @@ func (s *composeService) Stop(ctx context.Context, projectName string, options a
 func (s *composeService) stop(ctx context.Context, projectName string, options api.StopOptions) error {
 	w := progress.ContextWriter(ctx)
 
-	services := options.Services
-	if len(services) == 0 {
-		services = []string{}
-	}
-
-	var containers Containers
-	containers, err := s.getContainers(ctx, projectName, oneOffInclude, true, services...)
+	containers, project, err := s.actualState(ctx, projectName, options.Services)
 	if err != nil {
 		return err
 	}
 
-	project, err := s.projectFromName(containers, projectName, services...)
-	if err != nil && !api.IsNotFoundError(err) {
-		return err
-	}
-
-	if len(services) > 0 {
-		containers = containers.filter(isService(services...))
-	}
-
 	return InReverseDependencyOrder(ctx, project, func(c context.Context, service string) error {
 		return s.stopContainers(ctx, w, containers.filter(isService(service)), options.Timeout)
 	})

+ 8 - 0
pkg/e2e/fixtures/dependencies/compose.yaml

@@ -0,0 +1,8 @@
+services:
+  foo:
+    image: nginx:alpine
+    depends_on:
+      - bar
+
+  bar:
+    image: nginx:alpine

+ 24 - 0
pkg/e2e/start_stop_test.go

@@ -95,3 +95,27 @@ func TestStartStop(t *testing.T) {
 		_ = c.RunDockerComposeCmd("--project-name", projectName, "down")
 	})
 }
+
+func TestStopWithDependencies(t *testing.T) {
+	c := NewParallelE2eCLI(t, binDir)
+	const projectName = "e2e-stop-with-dependencies"
+
+	defer c.RunDockerComposeCmd("--project-name", projectName, "rm", "-fsv")
+
+	t.Run("Up", func(t *testing.T) {
+		res := c.RunDockerComposeCmd("-f", "./fixtures/dependencies/compose.yaml", "--project-name", projectName, "up", "-d")
+		assert.Assert(t, strings.Contains(res.Combined(), "Container e2e-stop-with-dependencies-foo-1  Started"), res.Combined())
+		assert.Assert(t, strings.Contains(res.Combined(), "Container e2e-stop-with-dependencies-bar-1  Started"), res.Combined())
+	})
+
+	t.Run("stop foo", func(t *testing.T) {
+		res := c.RunDockerComposeCmd("--project-name", projectName, "stop", "foo")
+
+		assert.Assert(t, strings.Contains(res.Combined(), "Container e2e-stop-with-dependencies-foo-1  Stopped"), res.Combined())
+
+		res = c.RunDockerComposeCmd("--project-name", projectName, "ps", "--status", "running")
+		assert.Assert(t, strings.Contains(res.Combined(), "e2e-stop-with-dependencies-bar-1"), res.Combined())
+		assert.Assert(t, !strings.Contains(res.Combined(), "e2e-stop-with-dependencies-foo-1"), res.Combined())
+	})
+
+}