Browse Source

introduce ability to select service to be stopped by `compose down`

Signed-off-by: Nicolas De Loof <[email protected]>
Nicolas De Loof 2 years ago
parent
commit
93bd27a0cc

+ 4 - 4
cmd/compose/down.go

@@ -44,7 +44,7 @@ func downCommand(p *ProjectOptions, backend api.Service) *cobra.Command {
 		ProjectOptions: p,
 	}
 	downCmd := &cobra.Command{
-		Use:   "down [OPTIONS]",
+		Use:   "down [OPTIONS] [SERVICES]",
 		Short: "Stop and remove containers, networks",
 		PreRunE: AdaptCmd(func(ctx context.Context, cmd *cobra.Command, args []string) error {
 			opts.timeChanged = cmd.Flags().Changed("timeout")
@@ -56,9 +56,8 @@ func downCommand(p *ProjectOptions, backend api.Service) *cobra.Command {
 			return nil
 		}),
 		RunE: Adapt(func(ctx context.Context, args []string) error {
-			return runDown(ctx, backend, opts)
+			return runDown(ctx, backend, opts, args)
 		}),
-		Args:              cobra.NoArgs,
 		ValidArgsFunction: noCompletion(),
 	}
 	flags := downCmd.Flags()
@@ -77,7 +76,7 @@ func downCommand(p *ProjectOptions, backend api.Service) *cobra.Command {
 	return downCmd
 }
 
-func runDown(ctx context.Context, backend api.Service, opts downOptions) error {
+func runDown(ctx context.Context, backend api.Service, opts downOptions, services []string) error {
 	project, name, err := opts.projectOrName()
 	if err != nil {
 		return err
@@ -94,5 +93,6 @@ func runDown(ctx context.Context, backend api.Service, opts downOptions) error {
 		Timeout:       timeout,
 		Images:        opts.images,
 		Volumes:       opts.volumes,
+		Services:      services,
 	})
 }

+ 1 - 1
docs/reference/docker_compose_down.yaml

@@ -14,7 +14,7 @@ long: |-
     Anonymous volumes are not removed by default. However, as they don’t have a stable name, they will not be automatically
     mounted by a subsequent `up`. For data that needs to persist between updates, use explicit paths as bind mounts or
     named volumes.
-usage: docker compose down [OPTIONS]
+usage: docker compose down [OPTIONS] [SERVICES]
 pname: docker compose
 plink: docker_compose.yaml
 options:

+ 2 - 0
pkg/api/api.go

@@ -232,6 +232,8 @@ type DownOptions struct {
 	Images string
 	// Volumes remove volumes, both declared in the `volumes` section and anonymous ones
 	Volumes bool
+	// Services passed in the command line to be stopped
+	Services []string
 }
 
 // ConfigOptions group options of the Config API

+ 4 - 1
pkg/compose/down.go

@@ -44,7 +44,7 @@ func (s *composeService) Down(ctx context.Context, projectName string, options a
 	}, s.stdinfo())
 }
 
-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 { //golint:nocyclo
 	w := progress.ContextWriter(ctx)
 	resourceToRemove := false
 
@@ -70,6 +70,9 @@ func (s *composeService) down(ctx context.Context, projectName string, options a
 	}
 
 	err = InReverseDependencyOrder(ctx, project, func(c context.Context, service string) error {
+		if len(options.Services) > 0 && !utils.StringContains(options.Services, service) {
+			return nil
+		}
 		serviceContainers := containers.filter(isService(service))
 		err := s.removeContainers(ctx, w, serviceContainers, options.Timeout, options.Volumes)
 		return err

+ 8 - 0
pkg/e2e/compose_test.go

@@ -102,6 +102,14 @@ func TestLocalComposeUp(t *testing.T) {
 		res.Assert(t, icmd.Expected{Out: `compose-e2e-demo-words-1   gtardif/sentences-api   latest`})
 	})
 
+	t.Run("down SERVICE", func(t *testing.T) {
+		_ = c.RunDockerComposeCmd(t, "--project-name", projectName, "down", "web")
+
+		res := c.RunDockerComposeCmd(t, "--project-name", projectName, "ps")
+		assert.Assert(t, !strings.Contains(res.Combined(), "compose-e2e-demo-web-1"), res.Combined())
+		assert.Assert(t, strings.Contains(res.Combined(), "compose-e2e-demo-db-1"), res.Combined())
+	})
+
 	t.Run("down", func(t *testing.T) {
 		_ = c.RunDockerComposeCmd(t, "--project-name", projectName, "down")
 	})

+ 9 - 0
pkg/progress/event.go

@@ -153,6 +153,15 @@ func RemovedEvent(id string) Event {
 	return NewEvent(id, Done, "Removed")
 }
 
+// SkippedEvent creates a new Skipped Event
+func SkippedEvent(id string, reason string) Event {
+	return Event{
+		ID:         id,
+		Status:     Warning,
+		StatusText: "Skipped: " + reason,
+	}
+}
+
 // NewEvent new event
 func NewEvent(id string, status EventStatus, statusText string) Event {
 	return Event{