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
37
commands.go
37
commands.go
|
@ -6,6 +6,7 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/dotcloud/docker/auth"
|
"github.com/dotcloud/docker/auth"
|
||||||
|
@ -36,6 +37,10 @@ var (
|
||||||
VERSION string
|
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) {
|
func (cli *DockerCli) getMethod(name string) (reflect.Method, bool) {
|
||||||
methodName := "Cmd" + strings.ToUpper(name[:1]) + strings.ToLower(name[1:])
|
methodName := "Cmd" + strings.ToUpper(name[:1]) + strings.ToLower(name[1:])
|
||||||
return reflect.TypeOf(cli).MethodByName(methodName)
|
return reflect.TypeOf(cli).MethodByName(methodName)
|
||||||
|
@ -1258,7 +1263,7 @@ func (cli *DockerCli) CmdAttach(args ...string) error {
|
||||||
|
|
||||||
if container.Config.Tty {
|
if container.Config.Tty {
|
||||||
if err := cli.monitorTtySize(cmd.Arg(0)); err != nil {
|
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
|
// Detached mode
|
||||||
<-wait
|
<-wait
|
||||||
} else {
|
} else {
|
||||||
status, err := waitForExit(cli, runResult.ID)
|
status, err := getExitCode(cli, runResult.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if status != 0 {
|
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)
|
dial, err := net.Dial(cli.proto, cli.addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if strings.Contains(err.Error(), "connection refused") {
|
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
|
return nil, -1, err
|
||||||
}
|
}
|
||||||
|
@ -1647,7 +1652,7 @@ func (cli *DockerCli) call(method, path string, data interface{}) ([]byte, int,
|
||||||
defer clientconn.Close()
|
defer clientconn.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if strings.Contains(err.Error(), "connection refused") {
|
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
|
return nil, -1, err
|
||||||
}
|
}
|
||||||
|
@ -1866,7 +1871,11 @@ func (cli *DockerCli) LoadConfigFile() (err error) {
|
||||||
func waitForExit(cli *DockerCli, containerId string) (int, error) {
|
func waitForExit(cli *DockerCli, containerId string) (int, error) {
|
||||||
body, _, err := cli.call("POST", "/containers/"+containerId+"/wait", nil)
|
body, _, err := cli.call("POST", "/containers/"+containerId+"/wait", nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return -1, err
|
// If we can't connect, then the daemon probably died.
|
||||||
|
if err != ErrConnectionRefused {
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
return -1, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var out APIWait
|
var out APIWait
|
||||||
|
@ -1876,6 +1885,22 @@ func waitForExit(cli *DockerCli, containerId string) (int, error) {
|
||||||
return out.StatusCode, nil
|
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 {
|
func NewDockerCli(in io.ReadCloser, out, err io.Writer, proto, addr string) *DockerCli {
|
||||||
var (
|
var (
|
||||||
isTerminal = false
|
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
|
// Expected behaviour, the process stays alive when the client disconnects
|
||||||
func TestAttachDisconnect(t *testing.T) {
|
func TestAttachDisconnect(t *testing.T) {
|
||||||
stdin, stdinPipe := io.Pipe()
|
stdin, stdinPipe := io.Pipe()
|
||||||
|
|
14
container.go
14
container.go
|
@ -964,14 +964,19 @@ func (container *Container) monitor() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
utils.Debugf("Process finished")
|
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
|
exitCode := -1
|
||||||
if container.cmd != nil {
|
if container.cmd != nil {
|
||||||
exitCode = container.cmd.ProcessState.Sys().(syscall.WaitStatus).ExitStatus()
|
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
|
// Cleanup
|
||||||
container.releaseNetwork()
|
container.releaseNetwork()
|
||||||
if container.Config.OpenStdin {
|
if container.Config.OpenStdin {
|
||||||
|
@ -1001,9 +1006,6 @@ func (container *Container) monitor() {
|
||||||
container.stdin, container.stdinPipe = io.Pipe()
|
container.stdin, container.stdinPipe = io.Pipe()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Report status back
|
|
||||||
container.State.setStopped(exitCode)
|
|
||||||
|
|
||||||
// Release the lock
|
// Release the lock
|
||||||
close(container.waitLock)
|
close(container.waitLock)
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue