Jelajahi Sumber

Merge pull request #9375 from ndeloof/project_or_name

if command is ran with a compose file, apply the compose model
Laura Brehm 3 tahun lalu
induk
melakukan
ca05ffe36e

+ 18 - 0
cmd/compose/compose.go

@@ -136,6 +136,24 @@ func (o *projectOptions) addProjectFlags(f *pflag.FlagSet) {
 	_ = f.MarkHidden("workdir")
 }
 
+func (o *projectOptions) projectOrName() (*types.Project, string, error) {
+	name := o.ProjectName
+	var project *types.Project
+	if o.ProjectName == "" {
+		p, err := o.toProject(nil)
+		if err != nil {
+			envProjectName := os.Getenv("COMPOSE_PROJECT_NAME")
+			if envProjectName != "" {
+				return nil, envProjectName, nil
+			}
+			return nil, "", err
+		}
+		project = p
+		name = p.Name
+	}
+	return project, name, nil
+}
+
 func (o *projectOptions) toProjectName() (string, error) {
 	if o.ProjectName != "" {
 		return o.ProjectName, nil

+ 3 - 10
cmd/compose/down.go

@@ -22,7 +22,6 @@ import (
 	"os"
 	"time"
 
-	"github.com/compose-spec/compose-go/types"
 	"github.com/docker/compose/v2/pkg/utils"
 	"github.com/sirupsen/logrus"
 	"github.com/spf13/cobra"
@@ -79,15 +78,9 @@ func downCommand(p *projectOptions, backend api.Service) *cobra.Command {
 }
 
 func runDown(ctx context.Context, backend api.Service, opts downOptions) error {
-	name := opts.ProjectName
-	var project *types.Project
-	if opts.ProjectName == "" {
-		p, err := opts.toProject(nil)
-		if err != nil {
-			return err
-		}
-		project = p
-		name = p.Name
+	project, name, err := opts.projectOrName()
+	if err != nil {
+		return err
 	}
 
 	var timeout *time.Duration

+ 2 - 2
cmd/compose/events.go

@@ -51,12 +51,12 @@ func eventsCommand(p *projectOptions, backend api.Service) *cobra.Command {
 }
 
 func runEvents(ctx context.Context, backend api.Service, opts eventsOpts, services []string) error {
-	project, err := opts.toProjectName()
+	name, err := opts.toProjectName()
 	if err != nil {
 		return err
 	}
 
-	return backend.Events(ctx, project, api.EventsOptions{
+	return backend.Events(ctx, name, api.EventsOptions{
 		Services: services,
 		Consumer: func(event api.Event) error {
 			if opts.json {

+ 2 - 2
cmd/compose/kill.go

@@ -49,12 +49,12 @@ func killCommand(p *projectOptions, backend api.Service) *cobra.Command {
 }
 
 func runKill(ctx context.Context, backend api.Service, opts killOptions, services []string) error {
-	projectName, err := opts.toProjectName()
+	name, err := opts.toProjectName()
 	if err != nil {
 		return err
 	}
 
-	return backend.Kill(ctx, projectName, api.KillOptions{
+	return backend.Kill(ctx, name, api.KillOptions{
 		Services: services,
 		Signal:   opts.signal,
 	})

+ 6 - 4
cmd/compose/pause.go

@@ -44,13 +44,14 @@ func pauseCommand(p *projectOptions, backend api.Service) *cobra.Command {
 }
 
 func runPause(ctx context.Context, backend api.Service, opts pauseOptions, services []string) error {
-	project, err := opts.toProjectName()
+	project, name, err := opts.projectOrName()
 	if err != nil {
 		return err
 	}
 
-	return backend.Pause(ctx, project, api.PauseOptions{
+	return backend.Pause(ctx, name, api.PauseOptions{
 		Services: services,
+		Project:  project,
 	})
 }
 
@@ -74,12 +75,13 @@ func unpauseCommand(p *projectOptions, backend api.Service) *cobra.Command {
 }
 
 func runUnPause(ctx context.Context, backend api.Service, opts unpauseOptions, services []string) error {
-	project, err := opts.toProjectName()
+	project, name, err := opts.projectOrName()
 	if err != nil {
 		return err
 	}
 
-	return backend.UnPause(ctx, project, api.PauseOptions{
+	return backend.UnPause(ctx, name, api.PauseOptions{
 		Services: services,
+		Project:  project,
 	})
 }

+ 3 - 2
cmd/compose/ps.go

@@ -91,11 +91,12 @@ func psCommand(p *projectOptions, backend api.Service) *cobra.Command {
 }
 
 func runPs(ctx context.Context, backend api.Service, services []string, opts psOptions) error {
-	projectName, err := opts.toProjectName()
+	project, name, err := opts.projectOrName()
 	if err != nil {
 		return err
 	}
-	containers, err := backend.Ps(ctx, projectName, api.PsOptions{
+	containers, err := backend.Ps(ctx, name, api.PsOptions{
+		Project:  project,
 		All:      opts.All,
 		Services: services,
 	})

+ 5 - 3
cmd/compose/remove.go

@@ -59,23 +59,25 @@ 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.toProjectName()
+	project, name, err := opts.projectOrName()
 	if err != nil {
 		return err
 	}
 
 	if opts.stop {
-		err := backend.Stop(ctx, project, api.StopOptions{
+		err := backend.Stop(ctx, name, api.StopOptions{
 			Services: services,
+			Project:  project,
 		})
 		if err != nil {
 			return err
 		}
 	}
 
-	return backend.Remove(ctx, project, api.RemoveOptions{
+	return backend.Remove(ctx, name, api.RemoveOptions{
 		Services: services,
 		Force:    opts.force,
 		Volumes:  opts.volumes,
+		Project:  project,
 	})
 }

+ 3 - 2
cmd/compose/restart.go

@@ -49,14 +49,15 @@ func restartCommand(p *projectOptions, backend api.Service) *cobra.Command {
 }
 
 func runRestart(ctx context.Context, backend api.Service, opts restartOptions, services []string) error {
-	projectName, err := opts.toProjectName()
+	project, name, err := opts.projectOrName()
 	if err != nil {
 		return err
 	}
 
 	timeout := time.Duration(opts.timeout) * time.Second
-	return backend.Restart(ctx, projectName, api.RestartOptions{
+	return backend.Restart(ctx, name, api.RestartOptions{
 		Timeout:  &timeout,
 		Services: services,
+		Project:  project,
 	})
 }

+ 3 - 2
cmd/compose/start.go

@@ -43,12 +43,13 @@ func startCommand(p *projectOptions, backend api.Service) *cobra.Command {
 }
 
 func runStart(ctx context.Context, backend api.Service, opts startOptions, services []string) error {
-	projectName, err := opts.toProjectName()
+	project, name, err := opts.projectOrName()
 	if err != nil {
 		return err
 	}
 
-	return backend.Start(ctx, projectName, api.StartOptions{
+	return backend.Start(ctx, name, api.StartOptions{
 		AttachTo: services,
+		Project:  project,
 	})
 }

+ 3 - 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 {
-	projectName, err := opts.toProjectName()
+	project, name, err := opts.projectOrName()
 	if err != nil {
 		return err
 	}
@@ -63,8 +63,9 @@ func runStop(ctx context.Context, backend api.Service, opts stopOptions, service
 		timeoutValue := time.Duration(opts.timeout) * time.Second
 		timeout = &timeoutValue
 	}
-	return backend.Stop(ctx, projectName, api.StopOptions{
+	return backend.Stop(ctx, name, api.StopOptions{
 		Timeout:  timeout,
 		Services: services,
+		Project:  project,
 	})
 }

+ 12 - 1
pkg/api/api.go

@@ -117,7 +117,7 @@ type CreateOptions struct {
 
 // StartOptions group options of the Start API
 type StartOptions struct {
-	// Project is the compose project used to define this app. Might be nil if user ran `start` just with project name
+	// Project is the compose project used to define this app. Might be nil if user ran command just with project name
 	Project *types.Project
 	// Attach to container and forward logs if not nil
 	Attach LogConsumer
@@ -133,6 +133,8 @@ type StartOptions struct {
 
 // RestartOptions group options of the Restart API
 type RestartOptions struct {
+	// Project is the compose project used to define this app. Might be nil if user ran command just with project name
+	Project *types.Project
 	// Timeout override container restart timeout
 	Timeout *time.Duration
 	// Services passed in the command line to be restarted
@@ -141,6 +143,8 @@ type RestartOptions struct {
 
 // StopOptions group options of the Stop API
 type StopOptions struct {
+	// Project is the compose project used to define this app. Might be nil if user ran command just with project name
+	Project *types.Project
 	// Timeout override container stop timeout
 	Timeout *time.Duration
 	// Services passed in the command line to be stopped
@@ -201,6 +205,8 @@ type KillOptions struct {
 
 // RemoveOptions group options of the Remove API
 type RemoveOptions struct {
+	// Project is the compose project used to define this app. Might be nil if user ran command just with project name
+	Project *types.Project
 	// DryRun just list removable resources
 	DryRun bool
 	// Volumes remove anonymous volumes
@@ -213,6 +219,8 @@ type RemoveOptions struct {
 
 // RunOptions group options of the Run API
 type RunOptions struct {
+	// Project is the compose project used to define this app. Might be nil if user ran command just with project name
+	Project           *types.Project
 	Name              string
 	Service           string
 	Command           []string
@@ -272,6 +280,7 @@ type ListOptions struct {
 
 // PsOptions group options of the Ps API
 type PsOptions struct {
+	Project  *types.Project
 	All      bool
 	Services []string
 }
@@ -377,6 +386,8 @@ type LogOptions struct {
 type PauseOptions struct {
 	// Services passed in the command line to be started
 	Services []string
+	// Project is the compose project used to define this app. Might be nil if user ran command just with project name
+	Project *types.Project
 }
 
 const (

+ 0 - 20
pkg/compose/compose.go

@@ -172,26 +172,6 @@ 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
-}
-
 func (s *composeService) actualVolumes(ctx context.Context, projectName string) (types.Volumes, error) {
 	volumes, err := s.apiClient().VolumeList(ctx, filters.NewArgs(projectFilter(projectName)))
 	if err != nil {

+ 5 - 2
pkg/compose/down.go

@@ -45,8 +45,11 @@ func (s *composeService) down(ctx context.Context, projectName string, options a
 	w := progress.ContextWriter(ctx)
 	resourceToRemove := false
 
-	var containers Containers
-	containers, err := s.getContainers(ctx, projectName, oneOffInclude, true)
+	include := oneOffExclude
+	if options.RemoveOrphans {
+		include = oneOffInclude
+	}
+	containers, err := s.getContainers(ctx, projectName, include, true)
 	if err != nil {
 		return err
 	}

+ 3 - 3
pkg/compose/down_test.go

@@ -40,7 +40,7 @@ func TestDown(t *testing.T) {
 	tested.dockerCli = cli
 	cli.EXPECT().Client().Return(api).AnyTimes()
 
-	api.EXPECT().ContainerList(gomock.Any(), projectFilterListOpt()).Return(
+	api.EXPECT().ContainerList(gomock.Any(), projectFilterListOpt(false)).Return(
 		[]moby.Container{
 			testContainer("service1", "123", false),
 			testContainer("service2", "456", false),
@@ -88,7 +88,7 @@ func TestDownRemoveOrphans(t *testing.T) {
 	tested.dockerCli = cli
 	cli.EXPECT().Client().Return(api).AnyTimes()
 
-	api.EXPECT().ContainerList(gomock.Any(), projectFilterListOpt()).Return(
+	api.EXPECT().ContainerList(gomock.Any(), projectFilterListOpt(true)).Return(
 		[]moby.Container{
 			testContainer("service1", "123", false),
 			testContainer("service2", "789", false),
@@ -125,7 +125,7 @@ func TestDownRemoveVolumes(t *testing.T) {
 	tested.dockerCli = cli
 	cli.EXPECT().Client().Return(api).AnyTimes()
 
-	api.EXPECT().ContainerList(gomock.Any(), projectFilterListOpt()).Return(
+	api.EXPECT().ContainerList(gomock.Any(), projectFilterListOpt(false)).Return(
 		[]moby.Container{testContainer("service1", "123", false)}, nil)
 	api.EXPECT().VolumeList(gomock.Any(), filters.NewArgs(projectFilter(strings.ToLower(testProject)))).
 		Return(volume.VolumeListOKBody{

+ 9 - 2
pkg/compose/kill_test.go

@@ -18,6 +18,7 @@ package compose
 
 import (
 	"context"
+	"fmt"
 	"path/filepath"
 	"strings"
 	"testing"
@@ -110,9 +111,15 @@ func anyCancellableContext() gomock.Matcher {
 	return gomock.AssignableToTypeOf(ctxWithCancel)
 }
 
-func projectFilterListOpt() moby.ContainerListOptions {
+func projectFilterListOpt(withOneOff bool) moby.ContainerListOptions {
+	filter := filters.NewArgs(
+		projectFilter(strings.ToLower(testProject)),
+	)
+	if !withOneOff {
+		filter.Add("label", fmt.Sprintf("%s=False", compose.OneoffLabel))
+	}
 	return moby.ContainerListOptions{
-		Filters: filters.NewArgs(projectFilter(strings.ToLower(testProject))),
+		Filters: filter,
 		All:     true,
 	}
 }

+ 10 - 2
pkg/compose/pause.go

@@ -33,12 +33,16 @@ func (s *composeService) Pause(ctx context.Context, projectName string, options
 	})
 }
 
-func (s *composeService) pause(ctx context.Context, project string, options api.PauseOptions) error {
-	containers, err := s.getContainers(ctx, project, oneOffExclude, false, options.Services...)
+func (s *composeService) pause(ctx context.Context, projectName string, options api.PauseOptions) error {
+	containers, err := s.getContainers(ctx, projectName, oneOffExclude, false, options.Services...)
 	if err != nil {
 		return err
 	}
 
+	if options.Project != nil {
+		containers = containers.filter(isService(options.Project.ServiceNames()...))
+	}
+
 	w := progress.ContextWriter(ctx)
 	eg, ctx := errgroup.WithContext(ctx)
 	containers.forEach(func(container moby.Container) {
@@ -67,6 +71,10 @@ func (s *composeService) unPause(ctx context.Context, projectName string, option
 		return err
 	}
 
+	if options.Project != nil {
+		containers = containers.filter(isService(options.Project.ServiceNames()...))
+	}
+
 	w := progress.ContextWriter(ctx)
 	eg, ctx := errgroup.WithContext(ctx)
 	containers.forEach(func(container moby.Container) {

+ 13 - 0
pkg/compose/ps.go

@@ -37,6 +37,19 @@ func (s *composeService) Ps(ctx context.Context, projectName string, options api
 		return nil, err
 	}
 
+	project := options.Project
+	if project == nil {
+		project, err = s.getProjectWithResources(ctx, containers, projectName)
+		if err != nil {
+			return nil, err
+		}
+	}
+
+	if len(options.Services) == 0 {
+		options.Services = project.ServiceNames()
+	}
+
+	containers = containers.filter(isService(options.Services...))
 	summary := make([]api.ContainerSummary, len(containers))
 	eg, ctx := errgroup.WithContext(ctx)
 	for i, container := range containers {

+ 3 - 0
pkg/compose/ps_test.go

@@ -26,6 +26,7 @@ import (
 
 	moby "github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/filters"
+	"github.com/docker/docker/api/types/volume"
 
 	compose "github.com/docker/compose/v2/pkg/api"
 	"github.com/docker/compose/v2/pkg/mocks"
@@ -48,6 +49,8 @@ func TestPs(t *testing.T) {
 	c2, inspect2 := containerDetails("service1", "456", "running", "", 0)
 	c2.Ports = []moby.Port{{PublicPort: 80, PrivatePort: 90, IP: "localhost"}}
 	c3, inspect3 := containerDetails("service2", "789", "exited", "", 130)
+	api.EXPECT().VolumeList(ctx, gomock.Any()).Return(volume.VolumeListOKBody{}, nil)
+	api.EXPECT().NetworkList(ctx, gomock.Any()).Return([]moby.NetworkResource{}, nil)
 	api.EXPECT().ContainerList(ctx, listOpts).Return([]moby.Container{c1, c2, c3}, nil)
 	api.EXPECT().ContainerInspect(anyCancellableContext(), "123").Return(inspect1, nil)
 	api.EXPECT().ContainerInspect(anyCancellableContext(), "456").Return(inspect2, nil)

+ 5 - 1
pkg/compose/remove.go

@@ -31,7 +31,7 @@ import (
 
 func (s *composeService) Remove(ctx context.Context, projectName string, options api.RemoveOptions) error {
 	projectName = strings.ToLower(projectName)
-	containers, _, err := s.actualState(ctx, projectName, options.Services)
+	containers, err := s.getContainers(ctx, projectName, oneOffExclude, true, options.Services...)
 	if err != nil {
 		if api.IsNotFoundError(err) {
 			fmt.Fprintln(s.stderr(), "No stopped containers")
@@ -40,6 +40,10 @@ func (s *composeService) Remove(ctx context.Context, projectName string, options
 		return err
 	}
 
+	if options.Project != nil {
+		containers = containers.filter(isService(options.Project.ServiceNames()...))
+	}
+
 	stoppedContainers := containers.filter(func(c moby.Container) bool {
 		return c.State != ContainerRunning
 	})

+ 9 - 11
pkg/compose/restart.go

@@ -34,15 +34,17 @@ func (s *composeService) Restart(ctx context.Context, projectName string, option
 }
 
 func (s *composeService) restart(ctx context.Context, projectName string, options api.RestartOptions) error {
-
-	observedState, err := s.getContainers(ctx, projectName, oneOffExclude, true)
+	containers, err := s.getContainers(ctx, projectName, oneOffExclude, true)
 	if err != nil {
 		return err
 	}
 
-	project, err := s.projectFromName(observedState, projectName, options.Services...)
-	if err != nil {
-		return err
+	project := options.Project
+	if project == nil {
+		project, err = s.getProjectWithResources(ctx, containers, projectName)
+		if err != nil {
+			return err
+		}
 	}
 
 	if len(options.Services) == 0 {
@@ -50,12 +52,12 @@ func (s *composeService) restart(ctx context.Context, projectName string, option
 	}
 
 	w := progress.ContextWriter(ctx)
-	err = InDependencyOrder(ctx, project, func(c context.Context, service string) error {
+	return InDependencyOrder(ctx, project, func(c context.Context, service string) error {
 		if !utils.StringContains(options.Services, service) {
 			return nil
 		}
 		eg, ctx := errgroup.WithContext(ctx)
-		for _, container := range observedState.filter(isService(service)) {
+		for _, container := range containers.filter(isService(service)) {
 			container := container
 			eg.Go(func() error {
 				eventName := getContainerProgressName(container)
@@ -69,8 +71,4 @@ func (s *composeService) restart(ctx context.Context, projectName string, option
 		}
 		return eg.Wait()
 	})
-	if err != nil {
-		return err
-	}
-	return nil
 }

+ 19 - 5
pkg/compose/stop.go

@@ -22,6 +22,7 @@ import (
 
 	"github.com/docker/compose/v2/pkg/api"
 	"github.com/docker/compose/v2/pkg/progress"
+	"github.com/docker/compose/v2/pkg/utils"
 )
 
 func (s *composeService) Stop(ctx context.Context, projectName string, options api.StopOptions) error {
@@ -31,15 +32,28 @@ 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)
-
-	containers, project, err := s.actualState(ctx, projectName, options.Services)
+	containers, err := s.getContainers(ctx, projectName, oneOffExclude, true)
 	if err != nil {
 		return err
 	}
 
+	project := options.Project
+	if project == nil {
+		project, err = s.getProjectWithResources(ctx, containers, projectName)
+		if err != nil {
+			return err
+		}
+	}
+
+	if len(options.Services) == 0 {
+		options.Services = project.ServiceNames()
+	}
+
+	w := progress.ContextWriter(ctx)
 	return InReverseDependencyOrder(ctx, project, func(c context.Context, service string) error {
-		containersToStop := containers.filter(isService(service)).filter(isNotOneOff)
-		return s.stopContainers(ctx, w, containersToStop, options.Timeout)
+		if !utils.StringContains(options.Services, service) {
+			return nil
+		}
+		return s.stopContainers(ctx, w, containers.filter(isService(service)).filter(isNotOneOff), options.Timeout)
 	})
 }

+ 7 - 1
pkg/compose/stop_test.go

@@ -26,6 +26,8 @@ import (
 	"github.com/docker/compose/v2/pkg/mocks"
 
 	moby "github.com/docker/docker/api/types"
+	"github.com/docker/docker/api/types/filters"
+	"github.com/docker/docker/api/types/volume"
 	"github.com/golang/mock/gomock"
 	"gotest.tools/v3/assert"
 )
@@ -40,12 +42,16 @@ func TestStopTimeout(t *testing.T) {
 	cli.EXPECT().Client().Return(api).AnyTimes()
 
 	ctx := context.Background()
-	api.EXPECT().ContainerList(gomock.Any(), projectFilterListOpt()).Return(
+	api.EXPECT().ContainerList(gomock.Any(), projectFilterListOpt(false)).Return(
 		[]moby.Container{
 			testContainer("service1", "123", false),
 			testContainer("service1", "456", false),
 			testContainer("service2", "789", false),
 		}, nil)
+	api.EXPECT().VolumeList(gomock.Any(), filters.NewArgs(projectFilter(strings.ToLower(testProject)))).
+		Return(volume.VolumeListOKBody{}, nil)
+	api.EXPECT().NetworkList(gomock.Any(), moby.NetworkListOptions{Filters: filters.NewArgs(projectFilter(strings.ToLower(testProject)))}).
+		Return([]moby.NetworkResource{}, nil)
 
 	timeout := time.Duration(2) * time.Second
 	api.EXPECT().ContainerStop(gomock.Any(), "123", &timeout).Return(nil)

+ 2 - 1
pkg/e2e/compose_run_test.go

@@ -79,7 +79,7 @@ func TestLocalComposeRun(t *testing.T) {
 	})
 
 	t.Run("down", func(t *testing.T) {
-		c.RunDockerComposeCmd(t, "-f", "./fixtures/run-test/compose.yaml", "down")
+		c.RunDockerComposeCmd(t, "-f", "./fixtures/run-test/compose.yaml", "down", "--remove-orphans")
 		res := c.RunDockerCmd(t, "ps", "--all")
 		assert.Assert(t, !strings.Contains(res.Stdout(), "run-test"), res.Stdout())
 	})
@@ -121,6 +121,7 @@ func TestLocalComposeRun(t *testing.T) {
 			c.Env = append(c.Env, "COMPOSE_REMOVE_ORPHANS=True")
 		})
 		res := c.RunDockerCmd(t, "ps", "--all")
+
 		assert.Assert(t, !strings.Contains(res.Stdout(), "run-test"), res.Stdout())
 	})
 

+ 5 - 0
pkg/e2e/fixtures/start-stop/other.yaml

@@ -0,0 +1,5 @@
+services:
+  a-different-one:
+    image:  nginx:alpine
+  and-another-one:
+    image:  nginx:alpine

+ 16 - 0
pkg/e2e/start_stop_test.go

@@ -246,3 +246,19 @@ func TestStartStopMultipleServices(t *testing.T) {
 			fmt.Sprintf("Missing start message for %s\n%s", svc, res.Combined()))
 	}
 }
+
+func TestStartStopMultipleFiles(t *testing.T) {
+	cli := NewParallelCLI(t, WithEnv("COMPOSE_PROJECT_NAME=e2e-start-stop-svc-multiple-files"))
+	t.Cleanup(func() {
+		cli.RunDockerComposeCmd(t, "-p", "e2e-start-stop-svc-multiple-files", "down", "--remove-orphans")
+	})
+
+	cli.RunDockerComposeCmd(t, "-f", "./fixtures/start-stop/compose.yaml", "up", "-d")
+	cli.RunDockerComposeCmd(t, "-f", "./fixtures/start-stop/other.yaml", "up", "-d")
+
+	res := cli.RunDockerComposeCmd(t, "-f", "./fixtures/start-stop/compose.yaml", "stop")
+	assert.Assert(t, strings.Contains(res.Combined(), "Container e2e-start-stop-svc-multiple-files-simple-1  Stopped"), res.Combined())
+	assert.Assert(t, strings.Contains(res.Combined(), "Container e2e-start-stop-svc-multiple-files-another-1  Stopped"), res.Combined())
+	assert.Assert(t, !strings.Contains(res.Combined(), "Container e2e-start-stop-svc-multiple-files-a-different-one-1  Stopped"), res.Combined())
+	assert.Assert(t, !strings.Contains(res.Combined(), "Container e2e-start-stop-svc-multiple-files-and-another-one-1  Stopped"), res.Combined())
+}