diff --git a/cmd/nerdctl/compose/compose_down.go b/cmd/nerdctl/compose/compose_down.go index 1099273dcca..3465dcfbe96 100644 --- a/cmd/nerdctl/compose/compose_down.go +++ b/cmd/nerdctl/compose/compose_down.go @@ -27,9 +27,8 @@ import ( func downCommand() *cobra.Command { var cmd = &cobra.Command{ - Use: "down", + Use: "down [flags] [SERVICE...]", Short: "Remove containers and associated resources", - Args: cobra.NoArgs, RunE: downAction, SilenceUsage: true, SilenceErrors: true, @@ -39,7 +38,7 @@ func downCommand() *cobra.Command { return cmd } -func downAction(cmd *cobra.Command, args []string) error { +func downAction(cmd *cobra.Command, services []string) error { globalOptions, err := helpers.ProcessRootCmdFlags(cmd) if err != nil { return err @@ -62,6 +61,8 @@ func downAction(cmd *cobra.Command, args []string) error { if err != nil { return err } + options.Services = services + c, err := compose.New(client, globalOptions, options, cmd.OutOrStdout(), cmd.ErrOrStderr()) if err != nil { return err @@ -71,5 +72,5 @@ func downAction(cmd *cobra.Command, args []string) error { RemoveVolumes: volumes, RemoveOrphans: removeOrphans, } - return c.Down(ctx, downOpts) + return c.Down(ctx, downOpts, services) } diff --git a/cmd/nerdctl/compose/compose_down_linux_test.go b/cmd/nerdctl/compose/compose_down_linux_test.go index 2eea0c01827..709c9f4877d 100644 --- a/cmd/nerdctl/compose/compose_down_linux_test.go +++ b/cmd/nerdctl/compose/compose_down_linux_test.go @@ -152,3 +152,67 @@ services: testCase.Run(t) } + +func TestComposeDownRemoveSpecifiedService(t *testing.T) { + testCase := nerdtest.Setup() + + testCase.Setup = func(data test.Data, helpers test.Helpers) { + dockerComposeYAML := fmt.Sprintf(` +services: + test1: + image: %s + command: "sleep infinity" + test2: + image: %s + command: "sleep infinity" +`, testutil.CommonImage, testutil.CommonImage) + composePath := data.Temp().Save(dockerComposeYAML, "compose.yaml") + + projectName := data.Identifier("project") + t.Logf("projectName=%q", projectName) + + testContainer1 := serviceparser.DefaultContainerName(projectName, "test1", "1") + testContainer2 := serviceparser.DefaultContainerName(projectName, "test2", "1") + + data.Labels().Set("composePath", composePath) + data.Labels().Set("projectName", projectName) + data.Labels().Set("testContainer1", testContainer1) + data.Labels().Set("testContainer2", testContainer2) + + helpers.Ensure("compose", "-p", projectName, "-f", composePath, "up", "-d") + nerdtest.EnsureContainerStarted(helpers, testContainer1) + nerdtest.EnsureContainerStarted(helpers, testContainer2) + } + + testCase.Command = func(data test.Data, helpers test.Helpers) test.TestableCommand { + return helpers.Command("compose", "-p", data.Labels().Get("projectName"), "-f", data.Labels().Get("composePath"), "down", "test1") + } + + testCase.Expected = test.Expects(0, nil, nil) + + testCase.SubTests = []*test.Case{ + { + Description: "only specified service is removed", + Command: func(data test.Data, helpers test.Helpers) test.TestableCommand { + return helpers.Command("compose", "-p", data.Labels().Get("projectName"), "-f", data.Labels().Get("composePath"), "ps", "-a") + }, + Expected: func(data test.Data, helpers test.Helpers) *test.Expected { + return &test.Expected{ + ExitCode: 0, + Output: expect.All( + expect.DoesNotContain(data.Labels().Get("testContainer1")), + expect.Contains(data.Labels().Get("testContainer2")), + ), + } + }, + }, + } + + testCase.Cleanup = func(data test.Data, helpers test.Helpers) { + if composePath := data.Labels().Get("composePath"); composePath != "" { + helpers.Anyhow("compose", "-p", data.Labels().Get("projectName"), "-f", composePath, "down", "-v") + } + } + + testCase.Run(t) +} diff --git a/pkg/composer/down.go b/pkg/composer/down.go index d7d7c2f0f6b..e38e689426f 100644 --- a/pkg/composer/down.go +++ b/pkg/composer/down.go @@ -28,10 +28,11 @@ import ( type DownOptions struct { RemoveVolumes bool RemoveOrphans bool + Services []string } -func (c *Composer) Down(ctx context.Context, downOptions DownOptions) error { - serviceNames, err := c.ServiceNames() +func (c *Composer) Down(ctx context.Context, downOptions DownOptions, services []string) error { + serviceNames, err := c.ServiceNames(services...) if err != nil { return err }