mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
commit
2fafe1efce
3 changed files with 143 additions and 12 deletions
35
commands.go
35
commands.go
|
@ -6,6 +6,7 @@ import (
|
|||
"bytes"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"github.com/dotcloud/docker/auth"
|
||||
|
@ -36,6 +37,10 @@ var (
|
|||
VERSION string
|
||||
)
|
||||
|
||||
var (
|
||||
ErrConnectionRefused = errors.New("Can't connect to docker daemon. Is 'docker -d' running on this host?")
|
||||
)
|
||||
|
||||
func (cli *DockerCli) getMethod(name string) (reflect.Method, bool) {
|
||||
methodName := "Cmd" + strings.ToUpper(name[:1]) + strings.ToLower(name[1:])
|
||||
return reflect.TypeOf(cli).MethodByName(methodName)
|
||||
|
@ -1258,7 +1263,7 @@ func (cli *DockerCli) CmdAttach(args ...string) error {
|
|||
|
||||
if container.Config.Tty {
|
||||
if err := cli.monitorTtySize(cmd.Arg(0)); err != nil {
|
||||
return err
|
||||
utils.Debugf("Error monitoring tty size: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1567,12 +1572,12 @@ func (cli *DockerCli) CmdRun(args ...string) error {
|
|||
// Detached mode
|
||||
<-wait
|
||||
} else {
|
||||
status, err := waitForExit(cli, runResult.ID)
|
||||
status, err := getExitCode(cli, runResult.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if status != 0 {
|
||||
return &utils.StatusError{status}
|
||||
return &utils.StatusError{Status: status}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1638,7 +1643,7 @@ func (cli *DockerCli) call(method, path string, data interface{}) ([]byte, int,
|
|||
dial, err := net.Dial(cli.proto, cli.addr)
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "connection refused") {
|
||||
return nil, -1, fmt.Errorf("Can't connect to docker daemon. Is 'docker -d' running on this host?")
|
||||
return nil, -1, ErrConnectionRefused
|
||||
}
|
||||
return nil, -1, err
|
||||
}
|
||||
|
@ -1647,7 +1652,7 @@ func (cli *DockerCli) call(method, path string, data interface{}) ([]byte, int,
|
|||
defer clientconn.Close()
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "connection refused") {
|
||||
return nil, -1, fmt.Errorf("Can't connect to docker daemon. Is 'docker -d' running on this host?")
|
||||
return nil, -1, ErrConnectionRefused
|
||||
}
|
||||
return nil, -1, err
|
||||
}
|
||||
|
@ -1866,8 +1871,12 @@ func (cli *DockerCli) LoadConfigFile() (err error) {
|
|||
func waitForExit(cli *DockerCli, containerId string) (int, error) {
|
||||
body, _, err := cli.call("POST", "/containers/"+containerId+"/wait", nil)
|
||||
if err != nil {
|
||||
// If we can't connect, then the daemon probably died.
|
||||
if err != ErrConnectionRefused {
|
||||
return -1, err
|
||||
}
|
||||
return -1, nil
|
||||
}
|
||||
|
||||
var out APIWait
|
||||
if err := json.Unmarshal(body, &out); err != nil {
|
||||
|
@ -1876,6 +1885,22 @@ func waitForExit(cli *DockerCli, containerId string) (int, error) {
|
|||
return out.StatusCode, nil
|
||||
}
|
||||
|
||||
func getExitCode(cli *DockerCli, containerId string) (int, error) {
|
||||
body, _, err := cli.call("GET", "/containers/"+containerId+"/json", nil)
|
||||
if err != nil {
|
||||
// If we can't connect, then the daemon probably died.
|
||||
if err != ErrConnectionRefused {
|
||||
return -1, err
|
||||
}
|
||||
return -1, nil
|
||||
}
|
||||
c := &Container{}
|
||||
if err := json.Unmarshal(body, c); err != nil {
|
||||
return -1, err
|
||||
}
|
||||
return c.State.ExitCode, nil
|
||||
}
|
||||
|
||||
func NewDockerCli(in io.ReadCloser, out, err io.Writer, proto, addr string) *DockerCli {
|
||||
var (
|
||||
isTerminal = false
|
||||
|
|
104
commands_test.go
104
commands_test.go
|
@ -369,6 +369,110 @@ func TestRunAttachStdin(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// TestRunDetach checks attaching and detaching with the escape sequence.
|
||||
func TestRunDetach(t *testing.T) {
|
||||
|
||||
stdin, stdinPipe := io.Pipe()
|
||||
stdout, stdoutPipe := io.Pipe()
|
||||
|
||||
cli := NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr)
|
||||
defer cleanup(globalRuntime)
|
||||
|
||||
ch := make(chan struct{})
|
||||
go func() {
|
||||
defer close(ch)
|
||||
cli.CmdRun("-i", "-t", unitTestImageID, "cat")
|
||||
}()
|
||||
|
||||
setTimeout(t, "First read/write assertion timed out", 2*time.Second, func() {
|
||||
if err := assertPipe("hello\n", "hello", stdout, stdinPipe, 15); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
})
|
||||
|
||||
container := globalRuntime.List()[0]
|
||||
|
||||
setTimeout(t, "Escape sequence timeout", 5*time.Second, func() {
|
||||
stdinPipe.Write([]byte{'', ''})
|
||||
if err := stdinPipe.Close(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
})
|
||||
|
||||
// wait for CmdRun to return
|
||||
setTimeout(t, "Waiting for CmdRun timed out", 5*time.Second, func() {
|
||||
<-ch
|
||||
})
|
||||
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
if !container.State.Running {
|
||||
t.Fatal("The detached container should be still running")
|
||||
}
|
||||
|
||||
setTimeout(t, "Waiting for container to die timed out", 20*time.Second, func() {
|
||||
container.Kill()
|
||||
container.Wait()
|
||||
})
|
||||
}
|
||||
|
||||
// TestAttachDetach checks that attach in tty mode can be detached
|
||||
func TestAttachDetach(t *testing.T) {
|
||||
stdin, stdinPipe := io.Pipe()
|
||||
stdout, stdoutPipe := io.Pipe()
|
||||
|
||||
cli := NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr)
|
||||
defer cleanup(globalRuntime)
|
||||
|
||||
go stdout.Read(make([]byte, 1024))
|
||||
setTimeout(t, "Starting container timed out", 2*time.Second, func() {
|
||||
if err := cli.CmdRun("-i", "-t", "-d", unitTestImageID, "cat"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
})
|
||||
|
||||
container := globalRuntime.List()[0]
|
||||
|
||||
stdin, stdinPipe = io.Pipe()
|
||||
stdout, stdoutPipe = io.Pipe()
|
||||
cli = NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr)
|
||||
|
||||
ch := make(chan struct{})
|
||||
go func() {
|
||||
defer close(ch)
|
||||
if err := cli.CmdAttach(container.ShortID()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}()
|
||||
|
||||
setTimeout(t, "First read/write assertion timed out", 2*time.Second, func() {
|
||||
if err := assertPipe("hello\n", "hello", stdout, stdinPipe, 15); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
})
|
||||
|
||||
setTimeout(t, "Escape sequence timeout", 5*time.Second, func() {
|
||||
stdinPipe.Write([]byte{'', ''})
|
||||
if err := stdinPipe.Close(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
})
|
||||
|
||||
// wait for CmdRun to return
|
||||
setTimeout(t, "Waiting for CmdAttach timed out", 5*time.Second, func() {
|
||||
<-ch
|
||||
})
|
||||
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
if !container.State.Running {
|
||||
t.Fatal("The detached container should be still running")
|
||||
}
|
||||
|
||||
setTimeout(t, "Waiting for container to die timedout", 5*time.Second, func() {
|
||||
container.Kill()
|
||||
container.Wait()
|
||||
})
|
||||
}
|
||||
|
||||
// Expected behaviour, the process stays alive when the client disconnects
|
||||
func TestAttachDisconnect(t *testing.T) {
|
||||
stdin, stdinPipe := io.Pipe()
|
||||
|
|
14
container.go
14
container.go
|
@ -964,14 +964,19 @@ func (container *Container) monitor() {
|
|||
}
|
||||
}
|
||||
utils.Debugf("Process finished")
|
||||
if container.runtime != nil && container.runtime.srv != nil {
|
||||
container.runtime.srv.LogEvent("die", container.ShortID(), container.runtime.repositories.ImageName(container.Image))
|
||||
}
|
||||
|
||||
exitCode := -1
|
||||
if container.cmd != nil {
|
||||
exitCode = container.cmd.ProcessState.Sys().(syscall.WaitStatus).ExitStatus()
|
||||
}
|
||||
|
||||
// Report status back
|
||||
container.State.setStopped(exitCode)
|
||||
|
||||
if container.runtime != nil && container.runtime.srv != nil {
|
||||
container.runtime.srv.LogEvent("die", container.ShortID(), container.runtime.repositories.ImageName(container.Image))
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
container.releaseNetwork()
|
||||
if container.Config.OpenStdin {
|
||||
|
@ -1001,9 +1006,6 @@ func (container *Container) monitor() {
|
|||
container.stdin, container.stdinPipe = io.Pipe()
|
||||
}
|
||||
|
||||
// Report status back
|
||||
container.State.setStopped(exitCode)
|
||||
|
||||
// Release the lock
|
||||
close(container.waitLock)
|
||||
|
||||
|
|
Loading…
Reference in a new issue