mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Add a --signal option to the kill command to specify a signal.
Docker-DCO-1.1-Signed-off-by: Paul Lietar <paul@lietar.net> (github: plietar)
This commit is contained in:
parent
3cb5bc5ae5
commit
1f75a0bf43
4 changed files with 132 additions and 19 deletions
|
@ -942,7 +942,9 @@ func (cli *DockerCli) CmdRm(args ...string) error {
|
||||||
|
|
||||||
// 'docker kill NAME' kills a running container
|
// 'docker kill NAME' kills a running container
|
||||||
func (cli *DockerCli) CmdKill(args ...string) error {
|
func (cli *DockerCli) CmdKill(args ...string) error {
|
||||||
cmd := cli.Subcmd("kill", "CONTAINER [CONTAINER...]", "Kill a running container (send SIGKILL)")
|
cmd := cli.Subcmd("kill", "[OPTIONS] CONTAINER [CONTAINER...]", "Kill a running container (send SIGKILL, or specified signal)")
|
||||||
|
signal := cmd.String([]string{"s", "-signal"}, "KILL", "Signal to send to the container")
|
||||||
|
|
||||||
if err := cmd.Parse(args); err != nil {
|
if err := cmd.Parse(args); err != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -952,8 +954,8 @@ func (cli *DockerCli) CmdKill(args ...string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
var encounteredError error
|
var encounteredError error
|
||||||
for _, name := range args {
|
for _, name := range cmd.Args() {
|
||||||
if _, _, err := readBody(cli.call("POST", "/containers/"+name+"/kill", nil, false)); err != nil {
|
if _, _, err := readBody(cli.call("POST", fmt.Sprintf("/containers/%s/kill?signal=%s", name, *signal), nil, false)); err != nil {
|
||||||
fmt.Fprintf(cli.err, "%s\n", err)
|
fmt.Fprintf(cli.err, "%s\n", err)
|
||||||
encounteredError = fmt.Errorf("Error: failed to kill one or more containers")
|
encounteredError = fmt.Errorf("Error: failed to kill one or more containers")
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -754,11 +754,13 @@ we ask for the ``HostPort`` field to get the public address.
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
Usage: docker kill CONTAINER [CONTAINER...]
|
Usage: docker kill [OPTIONS] CONTAINER [CONTAINER...]
|
||||||
|
|
||||||
Kill a running container (Send SIGKILL)
|
Kill a running container (send SIGKILL, or specified signal)
|
||||||
|
|
||||||
The main process inside the container will be sent SIGKILL.
|
-s, --signal="KILL": Signal to send to the container
|
||||||
|
|
||||||
|
The main process inside the container will be sent SIGKILL, or any signal specified with option ``--signal``.
|
||||||
|
|
||||||
Known Issues (kill)
|
Known Issues (kill)
|
||||||
~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
|
@ -12,7 +12,9 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"syscall"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
@ -90,18 +92,25 @@ func setTimeout(t *testing.T, msg string, d time.Duration, f func()) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func expectPipe(expected string, r io.Reader) error {
|
||||||
|
o, err := bufio.NewReader(r).ReadString('\n')
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if strings.Trim(o, " \r\n") != expected {
|
||||||
|
return fmt.Errorf("Unexpected output. Expected [%s], received [%s]", expected, o)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func assertPipe(input, output string, r io.Reader, w io.Writer, count int) error {
|
func assertPipe(input, output string, r io.Reader, w io.Writer, count int) error {
|
||||||
for i := 0; i < count; i++ {
|
for i := 0; i < count; i++ {
|
||||||
if _, err := w.Write([]byte(input)); err != nil {
|
if _, err := w.Write([]byte(input)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
o, err := bufio.NewReader(r).ReadString('\n')
|
if err := expectPipe(output, r); err != nil {
|
||||||
if err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if strings.Trim(o, " \r\n") != output {
|
|
||||||
return fmt.Errorf("Unexpected output. Expected [%s], received [%s]", output, o)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -1031,3 +1040,66 @@ func TestContainerOrphaning(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCmdKill(t *testing.T) {
|
||||||
|
stdin, stdinPipe := io.Pipe()
|
||||||
|
stdout, stdoutPipe := io.Pipe()
|
||||||
|
|
||||||
|
cli := docker.NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr)
|
||||||
|
cli2 := docker.NewDockerCli(nil, ioutil.Discard, ioutil.Discard, testDaemonProto, testDaemonAddr)
|
||||||
|
defer cleanup(globalEngine, t)
|
||||||
|
|
||||||
|
ch := make(chan struct{})
|
||||||
|
go func() {
|
||||||
|
defer close(ch)
|
||||||
|
cli.CmdRun("-i", "-t", unitTestImageID, "sh", "-c", "trap 'echo SIGUSR1' USR1; trap 'echo SIGUSR2' USR2; echo Ready; while true; do read; done")
|
||||||
|
}()
|
||||||
|
|
||||||
|
container := waitContainerStart(t, 10*time.Second)
|
||||||
|
|
||||||
|
setTimeout(t, "Read Ready timed out", 3*time.Second, func() {
|
||||||
|
if err := expectPipe("Ready", stdout); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
setTimeout(t, "SIGUSR1 timed out", 2*time.Second, func() {
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
if err := cli2.CmdKill("-s", strconv.Itoa(int(syscall.SIGUSR1)), container.ID); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := expectPipe("SIGUSR1", stdout); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
setTimeout(t, "SIGUSR2 timed out", 2*time.Second, func() {
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
if err := cli2.CmdKill("--signal=USR2", container.ID); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := expectPipe("SIGUSR2", stdout); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
time.Sleep(500 * time.Millisecond)
|
||||||
|
if !container.State.IsRunning() {
|
||||||
|
t.Fatal("The container should be still running")
|
||||||
|
}
|
||||||
|
|
||||||
|
setTimeout(t, "Waiting for container timedout", 5*time.Second, func() {
|
||||||
|
if err := cli2.CmdKill(container.ID); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
<-ch
|
||||||
|
if err := cli2.CmdWait(container.ID); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
closeWrap(stdin, stdinPipe, stdout, stdoutPipe)
|
||||||
|
}
|
||||||
|
|
53
server.go
53
server.go
|
@ -161,6 +161,40 @@ func (v *simpleVersionInfo) Version() string {
|
||||||
// for the container to exit.
|
// for the container to exit.
|
||||||
// If a signal is given, then just send it to the container and return.
|
// If a signal is given, then just send it to the container and return.
|
||||||
func (srv *Server) ContainerKill(job *engine.Job) engine.Status {
|
func (srv *Server) ContainerKill(job *engine.Job) engine.Status {
|
||||||
|
signalMap := map[string]syscall.Signal{
|
||||||
|
"HUP": syscall.SIGHUP,
|
||||||
|
"INT": syscall.SIGINT,
|
||||||
|
"QUIT": syscall.SIGQUIT,
|
||||||
|
"ILL": syscall.SIGILL,
|
||||||
|
"TRAP": syscall.SIGTRAP,
|
||||||
|
"ABRT": syscall.SIGABRT,
|
||||||
|
"BUS": syscall.SIGBUS,
|
||||||
|
"FPE": syscall.SIGFPE,
|
||||||
|
"KILL": syscall.SIGKILL,
|
||||||
|
"USR1": syscall.SIGUSR1,
|
||||||
|
"SEGV": syscall.SIGSEGV,
|
||||||
|
"USR2": syscall.SIGUSR2,
|
||||||
|
"PIPE": syscall.SIGPIPE,
|
||||||
|
"ALRM": syscall.SIGALRM,
|
||||||
|
"TERM": syscall.SIGTERM,
|
||||||
|
//"STKFLT": syscall.SIGSTKFLT,
|
||||||
|
"CHLD": syscall.SIGCHLD,
|
||||||
|
"CONT": syscall.SIGCONT,
|
||||||
|
"STOP": syscall.SIGSTOP,
|
||||||
|
"TSTP": syscall.SIGTSTP,
|
||||||
|
"TTIN": syscall.SIGTTIN,
|
||||||
|
"TTOU": syscall.SIGTTOU,
|
||||||
|
"URG": syscall.SIGURG,
|
||||||
|
"XCPU": syscall.SIGXCPU,
|
||||||
|
"XFSZ": syscall.SIGXFSZ,
|
||||||
|
"VTALRM": syscall.SIGVTALRM,
|
||||||
|
"PROF": syscall.SIGPROF,
|
||||||
|
"WINCH": syscall.SIGWINCH,
|
||||||
|
"IO": syscall.SIGIO,
|
||||||
|
//"PWR": syscall.SIGPWR,
|
||||||
|
"SYS": syscall.SIGSYS,
|
||||||
|
}
|
||||||
|
|
||||||
if n := len(job.Args); n < 1 || n > 2 {
|
if n := len(job.Args); n < 1 || n > 2 {
|
||||||
job.Errorf("Usage: %s CONTAINER [SIGNAL]", job.Name)
|
job.Errorf("Usage: %s CONTAINER [SIGNAL]", job.Name)
|
||||||
return engine.StatusErr
|
return engine.StatusErr
|
||||||
|
@ -168,17 +202,20 @@ func (srv *Server) ContainerKill(job *engine.Job) engine.Status {
|
||||||
name := job.Args[0]
|
name := job.Args[0]
|
||||||
var sig uint64
|
var sig uint64
|
||||||
if len(job.Args) == 2 && job.Args[1] != "" {
|
if len(job.Args) == 2 && job.Args[1] != "" {
|
||||||
var err error
|
sig = uint64(signalMap[job.Args[1]])
|
||||||
// The largest legal signal is 31, so let's parse on 5 bits
|
if sig == 0 {
|
||||||
sig, err = strconv.ParseUint(job.Args[1], 10, 5)
|
var err error
|
||||||
if err != nil {
|
// The largest legal signal is 31, so let's parse on 5 bits
|
||||||
job.Errorf("Invalid signal: %s", job.Args[1])
|
sig, err = strconv.ParseUint(job.Args[1], 10, 5)
|
||||||
return engine.StatusErr
|
if err != nil {
|
||||||
|
job.Errorf("Invalid signal: %s", job.Args[1])
|
||||||
|
return engine.StatusErr
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if container := srv.runtime.Get(name); container != nil {
|
if container := srv.runtime.Get(name); container != nil {
|
||||||
// If no signal is passed, perform regular Kill (SIGKILL + wait())
|
// If no signal is passed, or SIGKILL, perform regular Kill (SIGKILL + wait())
|
||||||
if sig == 0 {
|
if sig == 0 || syscall.Signal(sig) == syscall.SIGKILL {
|
||||||
if err := container.Kill(); err != nil {
|
if err := container.Kill(); err != nil {
|
||||||
job.Errorf("Cannot kill container %s: %s", name, err)
|
job.Errorf("Cannot kill container %s: %s", name, err)
|
||||||
return engine.StatusErr
|
return engine.StatusErr
|
||||||
|
|
Loading…
Reference in a new issue