зеркало из https://github.com/microsoft/docker.git
Merge pull request #3712 from plietar/kill-signal
Add a -s option to the kill command to specify a signal.
This commit is contained in:
Коммит
44a0bab304
|
@ -942,7 +942,9 @@ func (cli *DockerCli) CmdRm(args ...string) error {
|
|||
|
||||
// 'docker kill NAME' kills a running container
|
||||
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 {
|
||||
return nil
|
||||
}
|
||||
|
@ -952,8 +954,8 @@ func (cli *DockerCli) CmdKill(args ...string) error {
|
|||
}
|
||||
|
||||
var encounteredError error
|
||||
for _, name := range args {
|
||||
if _, _, err := readBody(cli.call("POST", "/containers/"+name+"/kill", nil, false)); err != nil {
|
||||
for _, name := range cmd.Args() {
|
||||
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)
|
||||
encounteredError = fmt.Errorf("Error: failed to kill one or more containers")
|
||||
} 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)
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
|
|
@ -12,7 +12,9 @@ import (
|
|||
"os"
|
||||
"path"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
"testing"
|
||||
"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 {
|
||||
for i := 0; i < count; i++ {
|
||||
if _, err := w.Write([]byte(input)); err != nil {
|
||||
return err
|
||||
}
|
||||
o, err := bufio.NewReader(r).ReadString('\n')
|
||||
if err != nil {
|
||||
if err := expectPipe(output, r); err != nil {
|
||||
return err
|
||||
}
|
||||
if strings.Trim(o, " \r\n") != output {
|
||||
return fmt.Errorf("Unexpected output. Expected [%s], received [%s]", output, o)
|
||||
}
|
||||
}
|
||||
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.
|
||||
// If a signal is given, then just send it to the container and return.
|
||||
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 {
|
||||
job.Errorf("Usage: %s CONTAINER [SIGNAL]", job.Name)
|
||||
return engine.StatusErr
|
||||
|
@ -168,17 +202,20 @@ func (srv *Server) ContainerKill(job *engine.Job) engine.Status {
|
|||
name := job.Args[0]
|
||||
var sig uint64
|
||||
if len(job.Args) == 2 && job.Args[1] != "" {
|
||||
var err error
|
||||
// The largest legal signal is 31, so let's parse on 5 bits
|
||||
sig, err = strconv.ParseUint(job.Args[1], 10, 5)
|
||||
if err != nil {
|
||||
job.Errorf("Invalid signal: %s", job.Args[1])
|
||||
return engine.StatusErr
|
||||
sig = uint64(signalMap[job.Args[1]])
|
||||
if sig == 0 {
|
||||
var err error
|
||||
// The largest legal signal is 31, so let's parse on 5 bits
|
||||
sig, err = strconv.ParseUint(job.Args[1], 10, 5)
|
||||
if err != nil {
|
||||
job.Errorf("Invalid signal: %s", job.Args[1])
|
||||
return engine.StatusErr
|
||||
}
|
||||
}
|
||||
}
|
||||
if container := srv.runtime.Get(name); container != nil {
|
||||
// If no signal is passed, perform regular Kill (SIGKILL + wait())
|
||||
if sig == 0 {
|
||||
// If no signal is passed, or SIGKILL, perform regular Kill (SIGKILL + wait())
|
||||
if sig == 0 || syscall.Signal(sig) == syscall.SIGKILL {
|
||||
if err := container.Kill(); err != nil {
|
||||
job.Errorf("Cannot kill container %s: %s", name, err)
|
||||
return engine.StatusErr
|
||||
|
|
Загрузка…
Ссылка в новой задаче