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

Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
This commit is contained in:
Nicolas De Loof 2023-05-10 15:21:34 +02:00 committed by Nicolas De loof
parent 68c462e607
commit 93bd27a0cc
6 changed files with 28 additions and 6 deletions

View File

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

View File

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

View File

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

View File

@ -44,7 +44,7 @@ func (s *composeService) Down(ctx context.Context, projectName string, options a
}, s.stdinfo()) }, 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) w := progress.ContextWriter(ctx)
resourceToRemove := false 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 { 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)) serviceContainers := containers.filter(isService(service))
err := s.removeContainers(ctx, w, serviceContainers, options.Timeout, options.Volumes) err := s.removeContainers(ctx, w, serviceContainers, options.Timeout, options.Volumes)
return err return err

View File

@ -102,6 +102,14 @@ func TestLocalComposeUp(t *testing.T) {
res.Assert(t, icmd.Expected{Out: `compose-e2e-demo-words-1 gtardif/sentences-api latest`}) 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) { t.Run("down", func(t *testing.T) {
_ = c.RunDockerComposeCmd(t, "--project-name", projectName, "down") _ = c.RunDockerComposeCmd(t, "--project-name", projectName, "down")
}) })

View File

@ -153,6 +153,15 @@ func RemovedEvent(id string) Event {
return NewEvent(id, Done, "Removed") 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 // NewEvent new event
func NewEvent(id string, status EventStatus, statusText string) Event { func NewEvent(id string, status EventStatus, statusText string) Event {
return Event{ return Event{