From fcfcc1524e1c10596114a964397dbdf82c4517d9 Mon Sep 17 00:00:00 2001 From: Laura Brehm Date: Fri, 19 Aug 2022 04:04:46 +0200 Subject: [PATCH] Apply compose model on `compose kill`, add `--remove-orphans` Signed-off-by: Laura Brehm --- cmd/compose/kill.go | 15 +++++++++++---- pkg/api/api.go | 4 ++++ pkg/compose/kill.go | 11 +++++++++++ pkg/compose/kill_test.go | 13 +++++++++++++ 4 files changed, 39 insertions(+), 4 deletions(-) diff --git a/cmd/compose/kill.go b/cmd/compose/kill.go index 6a96dee09..d1fb16ff7 100644 --- a/cmd/compose/kill.go +++ b/cmd/compose/kill.go @@ -18,15 +18,18 @@ package compose import ( "context" + "os" "github.com/spf13/cobra" "github.com/docker/compose/v2/pkg/api" + "github.com/docker/compose/v2/pkg/utils" ) type killOptions struct { *projectOptions - signal string + removeOrphans bool + signal string } func killCommand(p *projectOptions, backend api.Service) *cobra.Command { @@ -43,20 +46,24 @@ func killCommand(p *projectOptions, backend api.Service) *cobra.Command { } flags := cmd.Flags() + removeOrphans := utils.StringToBool(os.Getenv("COMPOSE_REMOVE_ORPHANS")) + flags.BoolVar(&opts.removeOrphans, "remove-orphans", removeOrphans, "Remove containers for services not defined in the Compose file.") flags.StringVarP(&opts.signal, "signal", "s", "SIGKILL", "SIGNAL to send to the container.") return cmd } func runKill(ctx context.Context, backend api.Service, opts killOptions, services []string) error { - name, err := opts.toProjectName() + project, name, err := opts.projectOrName() if err != nil { return err } return backend.Kill(ctx, name, api.KillOptions{ - Services: services, - Signal: opts.signal, + RemoveOrphans: opts.removeOrphans, + Project: project, + Services: services, + Signal: opts.signal, }) } diff --git a/pkg/api/api.go b/pkg/api/api.go index 9c3fc240b..38af772ca 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -197,6 +197,10 @@ type ImagesOptions struct { // KillOptions group options of the Kill API type KillOptions struct { + // RemoveOrphans will cleanup containers that are not declared on the compose model but own the same labels + RemoveOrphans bool + // Project is the compose project used to define this app. Might be nil if user ran command just with project name + Project *types.Project // Services passed in the command line to be killed Services []string // Signal to send to containers diff --git a/pkg/compose/kill.go b/pkg/compose/kill.go index aa057bc81..6460c4b4d 100644 --- a/pkg/compose/kill.go +++ b/pkg/compose/kill.go @@ -45,6 +45,17 @@ func (s *composeService) kill(ctx context.Context, projectName string, options a return err } + project := options.Project + if project == nil { + project, err = s.getProjectWithResources(ctx, containers, projectName) + if err != nil { + return err + } + } + + if !options.RemoveOrphans { + containers = containers.filter(isService(project.ServiceNames()...)) + } if len(containers) == 0 { fmt.Fprintf(s.stderr(), "no container to kill") } diff --git a/pkg/compose/kill_test.go b/pkg/compose/kill_test.go index 1455b6c2d..e5dc27aae 100644 --- a/pkg/compose/kill_test.go +++ b/pkg/compose/kill_test.go @@ -25,6 +25,7 @@ import ( 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" @@ -52,6 +53,12 @@ func TestKillAll(t *testing.T) { Filters: filters.NewArgs(projectFilter(name)), }).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{ + {ID: "abc123", Name: "testProject_default"}, + }, nil) api.EXPECT().ContainerKill(anyCancellableContext(), "123", "").Return(nil) api.EXPECT().ContainerKill(anyCancellableContext(), "456", "").Return(nil) api.EXPECT().ContainerKill(anyCancellableContext(), "789", "").Return(nil) @@ -77,6 +84,12 @@ func TestKillSignal(t *testing.T) { ctx := context.Background() api.EXPECT().ContainerList(ctx, listOptions).Return([]moby.Container{testContainer(serviceName, "123", 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{ + {ID: "abc123", Name: "testProject_default"}, + }, nil) api.EXPECT().ContainerKill(anyCancellableContext(), "123", "SIGTERM").Return(nil) err := tested.kill(ctx, name, compose.KillOptions{Services: []string{serviceName}, Signal: "SIGTERM"})