-
Notifications
You must be signed in to change notification settings - Fork 950
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feature: cli support add kill command #2924
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
package main | ||
|
||
import ( | ||
"context" | ||
"errors" | ||
"fmt" | ||
"strings" | ||
|
||
"github.com/spf13/cobra" | ||
) | ||
|
||
// killDescription is used to describe kill command in detail and auto generate command doc. | ||
var killDescription = "Kill one or more running container objects in Pouchd. " + | ||
"You can kill a container using the container’s ID, ID-prefix, or name. " + | ||
"This is useful when you wish to kill a container which is running." | ||
|
||
// KillCommand use to implement 'kill' command, it kill one or more containers. | ||
type KillCommand struct { | ||
baseCommand | ||
signal string | ||
} | ||
|
||
// Init initialize kill command. | ||
func (s *KillCommand) Init(c *Cli) { | ||
s.cli = c | ||
s.cmd = &cobra.Command{ | ||
Use: "kill [OPTIONS] CONTAINER [CONTAINER...]", | ||
Short: "kill one or more running containers", | ||
Long: killDescription, | ||
Args: cobra.MinimumNArgs(1), | ||
RunE: func(cmd *cobra.Command, args []string) error { | ||
return s.runKill(args) | ||
}, | ||
Example: killExample(), | ||
} | ||
s.addFlags() | ||
} | ||
|
||
// addFlags adds flags for specific command. | ||
func (s *KillCommand) addFlags() { | ||
flagSet := s.cmd.Flags() | ||
flagSet.StringVarP(&s.signal, "signal", "s", "KILL", "Signal to send to the container") | ||
|
||
} | ||
|
||
// runKill is the entry of kill command. | ||
func (s *KillCommand) runKill(args []string) error { | ||
ctx := context.Background() | ||
apiClient := s.cli.Client() | ||
|
||
var errs []string | ||
for _, name := range args { | ||
if err := apiClient.ContainerKill(ctx, name, s.signal); err != nil { | ||
errs = append(errs, err.Error()) | ||
continue | ||
} | ||
fmt.Printf("%s\n", name) | ||
} | ||
|
||
if len(errs) > 0 { | ||
return errors.New(strings.Join(errs, "\n")) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
// killExample shows examples in kill command, and is used in auto-generated cli docs. | ||
func killExample() string { | ||
return `$ pouch ps -a | ||
Name ID Status Created Image Runtime | ||
foo2 5a0ede Up 2 seconds 3 second ago registry.hub.docker.com/library/busybox:latest runc | ||
foo1 e05637 Up 6 seconds 7 seconds ago registry.hub.docker.com/library/busybox:latest runc | ||
$ pouch kill foo1 | ||
foo1 | ||
$ pouch ps | ||
Name ID Status Created Image Runtime | ||
foo2 5a0ede Up 11 seconds 12 seconds ago registry.hub.docker.com/library/busybox:latest runc | ||
` | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package client | ||
|
||
import ( | ||
"context" | ||
"net/url" | ||
) | ||
|
||
// ContainerKill kills a container. | ||
func (client *APIClient) ContainerKill(ctx context.Context, name, signal string) error { | ||
q := url.Values{} | ||
q.Add("signal", signal) | ||
|
||
resp, err := client.post(ctx, "/containers/"+name+"/kill", q, nil, nil) | ||
ensureCloseReader(resp) | ||
|
||
return err | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -352,6 +352,46 @@ func (c *Client) recoverContainer(ctx context.Context, id string, io *containeri | |
return nil | ||
} | ||
|
||
// KillContainer will kill a container by signal | ||
func (c *Client) KillContainer(ctx context.Context, containerID string, signal int) error { | ||
err := c.killContainer(ctx, containerID, signal) | ||
if err != nil { | ||
return convertCtrdErr(err) | ||
} | ||
return nil | ||
} | ||
|
||
// killContainer is the real process of killing a container | ||
func (c *Client) killContainer(ctx context.Context, containerID string, signal int) error { | ||
wrapperCli, err := c.Get(ctx) | ||
if err != nil { | ||
return fmt.Errorf("failed to get a containerd grpc client: %v", err) | ||
} | ||
|
||
ctx = leases.WithLease(ctx, wrapperCli.lease.ID) | ||
|
||
if !c.lock.TrylockWithRetry(ctx, containerID) { | ||
return errtypes.ErrLockfailed | ||
} | ||
defer c.lock.Unlock(containerID) | ||
|
||
pack, err := c.watch.get(containerID) | ||
if err != nil { | ||
return err | ||
} | ||
// the caller need to execute the all hooks. | ||
pack.l.Lock() | ||
pack.skipStopHooks = true | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why need this? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Actually I don't know if it is necessary to add these things. But if you want to kill a container, you should skip the stop hooks. |
||
pack.l.Unlock() | ||
defer func() { | ||
pack.l.Lock() | ||
pack.skipStopHooks = false | ||
pack.l.Unlock() | ||
}() | ||
|
||
return pack.task.Kill(ctx, syscall.Signal(signal), containerd.WithKillAll) | ||
} | ||
|
||
// DestroyContainer kill container and delete it. | ||
func (c *Client) DestroyContainer(ctx context.Context, id string, timeout int64) (*Message, error) { | ||
msg, err := c.destroyContainer(ctx, id, timeout) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -68,6 +68,9 @@ type ContainerMgr interface { | |
// List returns the list of containers. | ||
List(ctx context.Context, option *ContainerListOption) ([]*Container, error) | ||
|
||
// Kill a running container | ||
Kill(ctx context.Context, name string, signal uint64) (err error) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the desc here I think is kill one running container, wdyt? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes. |
||
|
||
// Start a container. | ||
Start(ctx context.Context, id string, options *types.ContainerStartOptions) error | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
package mgr | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"strings" | ||
"syscall" | ||
|
||
"github.com/pkg/errors" | ||
|
||
"github.com/alibaba/pouch/pkg/errtypes" | ||
"github.com/sirupsen/logrus" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. please restruct the package import sequence. |
||
) | ||
|
||
// Kill kills a container. | ||
func (mgr *ContainerManager) Kill(ctx context.Context, name string, signal uint64) (err error) { | ||
c, err := mgr.container(name) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
if syscall.Signal(signal) == syscall.SIGKILL { | ||
return mgr.kill(ctx, c) | ||
} | ||
return mgr.killWithSignal(ctx, c, int(signal)) | ||
} | ||
|
||
func (mgr *ContainerManager) kill(ctx context.Context, c *Container) error { | ||
if !c.IsRunning() { | ||
return fmt.Errorf("Container %s is not running", c.ID) | ||
} | ||
|
||
if err := mgr.killDeadProcess(ctx, c, int(syscall.SIGKILL)); err != nil { | ||
if errtypes.IsNoSuchProcess(err) { | ||
return nil | ||
} | ||
|
||
if c.IsRunning() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The function killDeadProcess will call the function killWithSignal. The latter will lock the container. |
||
return err | ||
} | ||
} | ||
|
||
if pid := c.State.Pid; pid != 0 { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please use |
||
if err := syscall.Kill(int(pid), 9); err != nil { | ||
if err != syscall.ESRCH { | ||
return err | ||
} | ||
|
||
e := errors.Wrapf(errtypes.ErrNoSuchProcess, "Cannot kill process (pid=%d) with signal %d", c.State.Pid, 9) | ||
logrus.Debug(e) | ||
return nil | ||
} | ||
} | ||
return nil | ||
} | ||
|
||
func (mgr *ContainerManager) killDeadProcess(ctx context.Context, c *Container, signal int) error { | ||
err := mgr.killWithSignal(ctx, c, signal) | ||
if err == syscall.ESRCH { | ||
e := errors.Wrapf(errtypes.ErrNoSuchProcess, "Cannot kill process (pid=%d) with signal %d", c.State.Pid, signal) | ||
logrus.Debug(e) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. please add more msg to this err There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. OK. |
||
return e | ||
} | ||
return err | ||
} | ||
|
||
func (mgr *ContainerManager) killWithSignal(ctx context.Context, c *Container, signal int) error { | ||
logrus.Debugf("Sending %d to %s", signal, c.ID) | ||
c.Lock() | ||
defer c.Unlock() | ||
|
||
if c.State.Paused { | ||
return fmt.Errorf("Container %s is paused. Unpause the container before stopping", c.ID) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. before killing? |
||
} | ||
|
||
if !c.State.Running { | ||
return fmt.Errorf("Container %s is not running", c.ID) | ||
} | ||
|
||
if err := mgr.Client.KillContainer(ctx, c.ID, signal); err != nil { | ||
// if container or process not exists, ignore the error | ||
if strings.Contains(err.Error(), "container not found") || | ||
strings.Contains(err.Error(), "no such process") { | ||
logrus.Warnf("container kill failed because of 'container not found' or 'no such process': %s", err.Error()) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. return err? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Actually, return fmt.Errorf("Cannot kill container %s: %s", c.ID, err). |
||
} else { | ||
return fmt.Errorf("Cannot kill container %s: %s", c.ID, err) | ||
} | ||
} | ||
attributes := map[string]string{ | ||
"signal": fmt.Sprintf("%d", signal), | ||
} | ||
mgr.LogContainerEventWithAttributes(ctx, c, "kill", attributes) | ||
return nil | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
## pouch kill | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please remove this file. This file is auto-generated. https://github.com/alibaba/pouch/tree/master/docs#commandline
|
||
|
||
Kill one or more running containers | ||
|
||
### Synopsis | ||
|
||
Kill one or more running containers in Pouchd. You can kill a container using the container’s ID, ID-prefix, or name. This is useful when you wish to kill a container which is running. | ||
|
||
``` | ||
pouch kill [OPTIONS] CONTAINER [CONTAINER...] | ||
``` | ||
|
||
### Examples | ||
|
||
``` | ||
$ pouch ps -a | ||
Name ID Status Created Image Runtime | ||
foo2 5a0ede Up 2 seconds 3 second ago registry.hub.docker.com/library/busybox:latest runc | ||
foo1 e05637 Up 6 seconds 7 seconds ago registry.hub.docker.com/library/busybox:latest runc | ||
$ pouch kill foo1 | ||
foo1 | ||
$ pouch ps | ||
Name ID Status Created Image Runtime | ||
foo2 5a0ede Up 11 seconds 12 seconds ago registry.hub.docker.com/library/busybox:latest runc | ||
``` | ||
|
||
### Options | ||
|
||
``` | ||
-h, --help help for kill | ||
-s, --signal string Signal to send to the container (default "KILL") | ||
``` | ||
|
||
### Options inherited from parent commands | ||
|
||
``` | ||
-D, --debug Switch client log level to DEBUG mode | ||
-H, --host string Specify connecting address of Pouch CLI (default "unix:///var/run/pouchd.sock") | ||
--tlscacert string Specify CA file of TLS | ||
--tlscert string Specify cert file of TLS | ||
--tlskey string Specify key file of TLS | ||
--tlsverify Use TLS and verify remote | ||
``` | ||
|
||
### SEE ALSO | ||
|
||
* [pouch](pouch.md) - An efficient container engine | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please just
return s.ContainerMgr.Kill(ctx, name, uint64(sig))
to simplify code.