Browse Source

Merge pull request #1225 from docker/kill

Add kill command
Nicolas De loof 4 năm trước cách đây
mục cha
commit
628417f02d

+ 4 - 0
aci/compose.go

@@ -203,6 +203,10 @@ func (cs *aciComposeService) Convert(ctx context.Context, project *types.Project
 	return nil, errdefs.ErrNotImplemented
 }
 
+func (cs *aciComposeService) Kill(ctx context.Context, project *types.Project, options compose.KillOptions) error {
+	return errdefs.ErrNotImplemented
+}
+
 func (cs *aciComposeService) RunOneOffContainer(ctx context.Context, project *types.Project, opts compose.RunOptions) error {
 	return errdefs.ErrNotImplemented
 }

+ 4 - 0
api/client/compose.go

@@ -76,6 +76,10 @@ func (c *composeService) Convert(context.Context, *types.Project, compose.Conver
 	return nil, errdefs.ErrNotImplemented
 }
 
+func (c *composeService) Kill(ctx context.Context, project *types.Project, options compose.KillOptions) error {
+	return errdefs.ErrNotImplemented
+}
+
 func (c *composeService) RunOneOffContainer(ctx context.Context, project *types.Project, opts compose.RunOptions) error {
 	return errdefs.ErrNotImplemented
 }

+ 8 - 0
api/compose/api.go

@@ -49,6 +49,8 @@ type Service interface {
 	List(ctx context.Context) ([]Stack, error)
 	// Convert translate compose model into backend's native format
 	Convert(ctx context.Context, project *types.Project, options ConvertOptions) ([]byte, error)
+	// Kill executes the equivalent to a `compose kill`
+	Kill(ctx context.Context, project *types.Project, options KillOptions) error
 	// RunOneOffContainer creates a service oneoff container and starts its dependencies
 	RunOneOffContainer(ctx context.Context, project *types.Project, opts RunOptions) error
 }
@@ -81,6 +83,12 @@ type ConvertOptions struct {
 	Format string
 }
 
+// KillOptions group options of the Kill API
+type KillOptions struct {
+	// Signal to send to containers
+	Signal string
+}
+
 // RunOptions options to execute compose run
 type RunOptions struct {
 	Service    string

+ 12 - 2
api/progress/event.go

@@ -78,16 +78,26 @@ func CreatedEvent(ID string) Event {
 	return NewEvent(ID, Done, "Created")
 }
 
-// StoppingEvent stops a new Stopping in progress Event
+// StoppingEvent creates a new Stopping in progress Event
 func StoppingEvent(ID string) Event {
 	return NewEvent(ID, Working, "Stopping")
 }
 
-// StoppedEvent stops a new Stopping in progress Event
+// StoppedEvent creates a new Stopping in progress Event
 func StoppedEvent(ID string) Event {
 	return NewEvent(ID, Done, "Stopped")
 }
 
+// KillingEvent creates a new Killing in progress Event
+func KillingEvent(ID string) Event {
+	return NewEvent(ID, Working, "Killing")
+}
+
+// KilledEvent creates a new Killed in progress Event
+func KilledEvent(ID string) Event {
+	return NewEvent(ID, Done, "Killed")
+}
+
 // RemovingEvent creates a new Removing in progress Event
 func RemovingEvent(ID string) Event {
 	return NewEvent(ID, Working, "Removing")

+ 1 - 0
cli/cmd/compose/compose.go

@@ -113,6 +113,7 @@ func Command(contextType string) *cobra.Command {
 		listCommand(),
 		logsCommand(&opts, contextType),
 		convertCommand(&opts),
+		killCommand(&opts),
 		runCommand(&opts),
 	)
 

+ 63 - 0
cli/cmd/compose/kill.go

@@ -0,0 +1,63 @@
+/*
+   Copyright 2020 Docker Compose CLI authors
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+package compose
+
+import (
+	"context"
+
+	"github.com/spf13/cobra"
+
+	"github.com/docker/compose-cli/api/client"
+	"github.com/docker/compose-cli/api/compose"
+)
+
+type killOptions struct {
+	*projectOptions
+	Signal string
+}
+
+func killCommand(p *projectOptions) *cobra.Command {
+	opts := killOptions{
+		projectOptions: p,
+	}
+	cmd := &cobra.Command{
+		Use:   "kill [options] [SERVICE...]",
+		Short: "Force stop service containers.",
+		RunE: func(cmd *cobra.Command, args []string) error {
+			return runKill(cmd.Context(), opts, args)
+		},
+	}
+
+	flags := cmd.Flags()
+	flags.StringVarP(&opts.Signal, "signal", "s", "SIGKILL", "SIGNAL to send to the container.")
+
+	return cmd
+}
+
+func runKill(ctx context.Context, opts killOptions, services []string) error {
+	c, err := client.NewWithDefaultLocalBackend(ctx)
+	if err != nil {
+		return err
+	}
+	project, err := opts.toProject(services)
+	if err != nil {
+		return err
+	}
+	return c.ComposeService().Kill(ctx, project, compose.KillOptions{
+		Signal: opts.Signal,
+	})
+}

+ 5 - 0
ecs/cloudformation.go

@@ -25,6 +25,7 @@ import (
 
 	"github.com/docker/compose-cli/api/compose"
 	"github.com/docker/compose-cli/api/config"
+	"github.com/docker/compose-cli/api/errdefs"
 
 	ecsapi "github.com/aws/aws-sdk-go/service/ecs"
 	"github.com/aws/aws-sdk-go/service/elbv2"
@@ -46,6 +47,10 @@ import (
 	"sigs.k8s.io/kustomize/kyaml/yaml/merge2"
 )
 
+func (b *ecsAPIService) Kill(ctx context.Context, project *types.Project, options compose.KillOptions) error {
+	return errdefs.ErrNotImplemented
+}
+
 func (b *ecsAPIService) Convert(ctx context.Context, project *types.Project, options compose.ConvertOptions) ([]byte, error) {
 	err := b.resolveServiceImagesDigests(ctx, project)
 	if err != nil {

+ 4 - 0
ecs/local/compose.go

@@ -65,6 +65,10 @@ func (e ecsLocalSimulation) Up(ctx context.Context, project *types.Project, opti
 	return errdefs.ErrNotImplemented
 }
 
+func (e ecsLocalSimulation) Kill(ctx context.Context, project *types.Project, options compose.KillOptions) error {
+	return e.compose.Kill(ctx, project, options)
+}
+
 func (e ecsLocalSimulation) Convert(ctx context.Context, project *types.Project, options compose.ConvertOptions) ([]byte, error) {
 	enhanced, err := e.enhanceForLocalSimulation(project)
 	if err != nil {

+ 4 - 0
kube/compose.go

@@ -103,6 +103,10 @@ func (s *composeService) Convert(ctx context.Context, project *types.Project, op
 	return nil, errdefs.ErrNotImplemented
 }
 
+func (s *composeService) Kill(ctx context.Context, project *types.Project, options compose.KillOptions) error {
+	return errdefs.ErrNotImplemented
+}
+
 // RunOneOffContainer creates a service oneoff container and starts its dependencies
 func (s *composeService) RunOneOffContainer(ctx context.Context, project *types.Project, opts compose.RunOptions) error {
 	return errdefs.ErrNotImplemented

+ 6 - 0
local/compose/containers.go

@@ -70,3 +70,9 @@ func (containers Containers) names() []string {
 	}
 	return names
 }
+
+func (containers Containers) forEach(fn func(moby.Container)) {
+	for _, c := range containers {
+		fn(c)
+	}
+}

+ 60 - 0
local/compose/kill.go

@@ -0,0 +1,60 @@
+/*
+   Copyright 2020 Docker Compose CLI authors
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+package compose
+
+import (
+	"context"
+
+	"github.com/compose-spec/compose-go/types"
+	moby "github.com/docker/docker/api/types"
+	"github.com/docker/docker/api/types/filters"
+	"golang.org/x/sync/errgroup"
+
+	"github.com/docker/compose-cli/api/compose"
+	"github.com/docker/compose-cli/api/progress"
+)
+
+func (s *composeService) Kill(ctx context.Context, project *types.Project, options compose.KillOptions) error {
+	w := progress.ContextWriter(ctx)
+
+	var containers Containers
+	containers, err := s.apiClient.ContainerList(ctx, moby.ContainerListOptions{
+		Filters: filters.NewArgs(projectFilter(project.Name)),
+		All:     true,
+	})
+	if err != nil {
+		return err
+	}
+
+	eg, ctx := errgroup.WithContext(ctx)
+	containers.
+		filter(isService(project.ServiceNames()...)).
+		forEach(func(container moby.Container) {
+			eg.Go(func() error {
+				eventName := getContainerProgressName(container)
+				w.Event(progress.KillingEvent(eventName))
+				err := s.apiClient.ContainerKill(ctx, container.ID, options.Signal)
+				if err != nil {
+					w.Event(progress.ErrorMessageEvent(eventName, "Error while Killing"))
+					return err
+				}
+				w.Event(progress.KilledEvent(eventName))
+				return nil
+			})
+		})
+	return eg.Wait()
+}

+ 2 - 0
local/compose/stop.go

@@ -38,6 +38,8 @@ func (s *composeService) Stop(ctx context.Context, project *types.Project) error
 		return err
 	}
 
+	containers = containers.filter(isService(project.ServiceNames()...))
+
 	return InReverseDependencyOrder(ctx, project, func(c context.Context, service types.ServiceConfig) error {
 		return s.stopContainers(ctx, w, containers.filter(isService(service.Name)))
 	})