1
0
Fork 0
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:
Paul Lietar 2014-01-22 04:42:36 +00:00
parent 3cb5bc5ae5
commit 1f75a0bf43
4 changed files with 132 additions and 19 deletions

View file

@ -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 {

View file

@ -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)
~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~

View file

@ -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)
}

View file

@ -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