Skip to content

Commit

Permalink
Merge github.com:dotcloud/docker into 333-redis-documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
johncosta committed Apr 9, 2013
2 parents 8f15c42 + 40ebe78 commit 418ef43
Show file tree
Hide file tree
Showing 27 changed files with 594 additions and 191 deletions.
10 changes: 9 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,12 @@ docker pull base
docker run -i -t base /bin/bash
```

Detaching from the interactive shell
------------------------------------
```
# In order to detach without killing the shell, you can use the escape sequence Ctrl-p + Ctrl-q
# Note: this works only in tty mode (run with -t option).
```
Starting a long-running worker process
--------------------------------------
Expand Down Expand Up @@ -183,7 +189,9 @@ JOB=$(docker run -d -p 4444 base /bin/nc -l -p 4444)
PORT=$(docker port $JOB 4444)

# Connect to the public port via the host's public address
echo hello world | nc $(hostname) $PORT
# Please note that because of how routing works connecting to localhost or 127.0.0.1 $PORT will not work.
IP=$(ifconfig eth0 | perl -n -e 'if (m/inet addr:([\d\.]+)/g) { print $1 }')
echo hello world | nc $IP $PORT

# Verify that the network connection worked
echo "Daemon received: $(docker logs $JOB)"
Expand Down
27 changes: 21 additions & 6 deletions commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import (
"unicode"
)

const VERSION = "0.1.3"
const VERSION = "0.1.4"

var GIT_COMMIT string

Expand Down Expand Up @@ -62,7 +62,7 @@ func (srv *Server) Help() string {
}

// 'docker login': login / register a user to registry service.
func (srv *Server) CmdLogin(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
func (srv *Server) CmdLogin(stdin io.ReadCloser, stdout rcli.DockerConn, args ...string) error {
// Read a line on raw terminal with support for simple backspace
// sequences and echo.
//
Expand Down Expand Up @@ -113,6 +113,8 @@ func (srv *Server) CmdLogin(stdin io.ReadCloser, stdout io.Writer, args ...strin
return readStringOnRawTerminal(stdin, stdout, false)
}

stdout.SetOptionRawTerminal()

cmd := rcli.Subcmd(stdout, "login", "", "Register or Login to the docker registry server")
if err := cmd.Parse(args); err != nil {
return nil
Expand Down Expand Up @@ -417,7 +419,8 @@ func (srv *Server) CmdKill(stdin io.ReadCloser, stdout io.Writer, args ...string
return nil
}

func (srv *Server) CmdImport(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
func (srv *Server) CmdImport(stdin io.ReadCloser, stdout rcli.DockerConn, args ...string) error {
stdout.Flush()
cmd := rcli.Subcmd(stdout, "import", "[OPTIONS] URL|- [REPOSITORY [TAG]]", "Create a new filesystem image from the contents of a tarball")
var archive io.Reader
var resp *http.Response
Expand Down Expand Up @@ -464,7 +467,7 @@ func (srv *Server) CmdImport(stdin io.ReadCloser, stdout io.Writer, args ...stri
return nil
}

func (srv *Server) CmdPush(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
func (srv *Server) CmdPush(stdin io.ReadCloser, stdout rcli.DockerConn, args ...string) error {
cmd := rcli.Subcmd(stdout, "push", "NAME", "Push an image or a repository to the registry")
if err := cmd.Parse(args); err != nil {
return nil
Expand Down Expand Up @@ -784,7 +787,7 @@ func (srv *Server) CmdLogs(stdin io.ReadCloser, stdout io.Writer, args ...string
return fmt.Errorf("No such container: %s", cmd.Arg(0))
}

func (srv *Server) CmdAttach(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
func (srv *Server) CmdAttach(stdin io.ReadCloser, stdout rcli.DockerConn, args ...string) error {
cmd := rcli.Subcmd(stdout, "attach", "CONTAINER", "Attach to a running container")
if err := cmd.Parse(args); err != nil {
return nil
Expand All @@ -799,6 +802,11 @@ func (srv *Server) CmdAttach(stdin io.ReadCloser, stdout io.Writer, args ...stri
return fmt.Errorf("No such container: %s", name)
}

if container.Config.Tty {
stdout.SetOptionRawTerminal()
}
// Flush the options to make sure the client sets the raw mode
stdout.Flush()
return <-container.Attach(stdin, nil, stdout, stdout)
}

Expand Down Expand Up @@ -870,7 +878,7 @@ func (srv *Server) CmdTag(stdin io.ReadCloser, stdout io.Writer, args ...string)
return srv.runtime.repositories.Set(cmd.Arg(1), cmd.Arg(2), cmd.Arg(0), *force)
}

func (srv *Server) CmdRun(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
func (srv *Server) CmdRun(stdin io.ReadCloser, stdout rcli.DockerConn, args ...string) error {
config, err := ParseRun(args, stdout)
if err != nil {
return err
Expand All @@ -884,6 +892,13 @@ func (srv *Server) CmdRun(stdin io.ReadCloser, stdout io.Writer, args ...string)
return fmt.Errorf("Command not specified")
}

if config.Tty {
stdout.SetOptionRawTerminal()
}
// Flush the options to make sure the client sets the raw mode
// or tell the client there is no options
stdout.Flush()

// Create new container
container, err := srv.runtime.Create(config)
if err != nil {
Expand Down
110 changes: 89 additions & 21 deletions commands_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ package docker

import (
"bufio"
"bytes"
"fmt"
"github.com/dotcloud/docker/rcli"
"io"
"io/ioutil"
"strings"
Expand Down Expand Up @@ -69,15 +69,27 @@ func TestRunHostname(t *testing.T) {

srv := &Server{runtime: runtime}

var stdin, stdout bytes.Buffer
setTimeout(t, "CmdRun timed out", 2*time.Second, func() {
if err := srv.CmdRun(ioutil.NopCloser(&stdin), &nopWriteCloser{&stdout}, "-h", "foobar", GetTestImage(runtime).Id, "hostname"); err != nil {
stdin, _ := io.Pipe()
stdout, stdoutPipe := io.Pipe()

c := make(chan struct{})
go func() {
if err := srv.CmdRun(stdin, rcli.NewDockerLocalConn(stdoutPipe), "-h", "foobar", GetTestImage(runtime).Id, "hostname"); err != nil {
t.Fatal(err)
}
})
if output := string(stdout.Bytes()); output != "foobar\n" {
t.Fatalf("'hostname' should display '%s', not '%s'", "foobar\n", output)
close(c)
}()
cmdOutput, err := bufio.NewReader(stdout).ReadString('\n')
if err != nil {
t.Fatal(err)
}
if cmdOutput != "foobar\n" {
t.Fatalf("'hostname' should display '%s', not '%s'", "foobar\n", cmdOutput)
}

setTimeout(t, "CmdRun timed out", 2*time.Second, func() {
<-c
})
}

func TestRunExit(t *testing.T) {
Expand All @@ -93,7 +105,7 @@ func TestRunExit(t *testing.T) {
stdout, stdoutPipe := io.Pipe()
c1 := make(chan struct{})
go func() {
srv.CmdRun(stdin, stdoutPipe, "-i", GetTestImage(runtime).Id, "/bin/cat")
srv.CmdRun(stdin, rcli.NewDockerLocalConn(stdoutPipe), "-i", GetTestImage(runtime).Id, "/bin/cat")
close(c1)
}()

Expand Down Expand Up @@ -147,7 +159,7 @@ func TestRunDisconnect(t *testing.T) {
go func() {
// We're simulating a disconnect so the return value doesn't matter. What matters is the
// fact that CmdRun returns.
srv.CmdRun(stdin, stdoutPipe, "-i", GetTestImage(runtime).Id, "/bin/cat")
srv.CmdRun(stdin, rcli.NewDockerLocalConn(stdoutPipe), "-i", GetTestImage(runtime).Id, "/bin/cat")
close(c1)
}()

Expand Down Expand Up @@ -179,42 +191,98 @@ func TestRunDisconnect(t *testing.T) {
})
}

// Expected behaviour: the process dies when the client disconnects
func TestRunDisconnectTty(t *testing.T) {
runtime, err := newTestRuntime()
if err != nil {
t.Fatal(err)
}
defer nuke(runtime)

srv := &Server{runtime: runtime}

stdin, stdinPipe := io.Pipe()
stdout, stdoutPipe := io.Pipe()
c1 := make(chan struct{})
go func() {
// We're simulating a disconnect so the return value doesn't matter. What matters is the
// fact that CmdRun returns.
srv.CmdRun(stdin, rcli.NewDockerLocalConn(stdoutPipe), "-i", "-t", GetTestImage(runtime).Id, "/bin/cat")
close(c1)
}()

setTimeout(t, "Read/Write assertion timed out", 2*time.Second, func() {
if err := assertPipe("hello\n", "hello", stdout, stdinPipe, 15); err != nil {
t.Fatal(err)
}
})

// Close pipes (simulate disconnect)
if err := closeWrap(stdin, stdinPipe, stdout, stdoutPipe); err != nil {
t.Fatal(err)
}

// as the pipes are close, we expect the process to die,
// therefore CmdRun to unblock. Wait for CmdRun
setTimeout(t, "Waiting for CmdRun timed out", 2*time.Second, func() {
<-c1
})

// Client disconnect after run -i should keep stdin out in TTY mode
container := runtime.List()[0]
// Give some time to monitor to do his thing
container.WaitTimeout(500 * time.Millisecond)
if !container.State.Running {
t.Fatalf("/bin/cat should still be running after closing stdin (tty mode)")
}
}

// TestAttachStdin checks attaching to stdin without stdout and stderr.
// 'docker run -i -a stdin' should sends the client's stdin to the command,
// then detach from it and print the container id.
func TestAttachStdin(t *testing.T) {
func TestRunAttachStdin(t *testing.T) {
runtime, err := newTestRuntime()
if err != nil {
t.Fatal(err)
}
defer nuke(runtime)
srv := &Server{runtime: runtime}

stdinR, stdinW := io.Pipe()
var stdout bytes.Buffer
stdin, stdinPipe := io.Pipe()
stdout, stdoutPipe := io.Pipe()

ch := make(chan struct{})
go func() {
srv.CmdRun(stdinR, &stdout, "-i", "-a", "stdin", GetTestImage(runtime).Id, "sh", "-c", "echo hello; cat")
srv.CmdRun(stdin, rcli.NewDockerLocalConn(stdoutPipe), "-i", "-a", "stdin", GetTestImage(runtime).Id, "sh", "-c", "echo hello; cat")
close(ch)
}()

// Send input to the command, close stdin, wait for CmdRun to return
setTimeout(t, "Read/Write timed out", 2*time.Second, func() {
if _, err := stdinW.Write([]byte("hi there\n")); err != nil {
// Send input to the command, close stdin
setTimeout(t, "Write timed out", 2*time.Second, func() {
if _, err := stdinPipe.Write([]byte("hi there\n")); err != nil {
t.Fatal(err)
}
if err := stdinPipe.Close(); err != nil {
t.Fatal(err)
}
stdinW.Close()
<-ch
})

// Check output
cmdOutput := string(stdout.Bytes())
container := runtime.List()[0]

// Check output
cmdOutput, err := bufio.NewReader(stdout).ReadString('\n')
if err != nil {
t.Fatal(err)
}
if cmdOutput != container.ShortId()+"\n" {
t.Fatalf("Wrong output: should be '%s', not '%s'\n", container.ShortId()+"\n", cmdOutput)
}

// wait for CmdRun to return
setTimeout(t, "Waiting for CmdRun timed out", 2*time.Second, func() {
<-ch
})

setTimeout(t, "Waiting for command to exit timed out", 2*time.Second, func() {
container.Wait()
})
Expand Down Expand Up @@ -270,7 +338,7 @@ func TestAttachDisconnect(t *testing.T) {
go func() {
// We're simulating a disconnect so the return value doesn't matter. What matters is the
// fact that CmdAttach returns.
srv.CmdAttach(stdin, stdoutPipe, container.Id)
srv.CmdAttach(stdin, rcli.NewDockerLocalConn(stdoutPipe), container.Id)
close(c1)
}()

Expand Down
Loading

0 comments on commit 418ef43

Please sign in to comment.