From 42bcfcc927499c63bbc3e3de80e6f52fd02fc9e8 Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Fri, 21 Jun 2013 10:00:25 +0000 Subject: [PATCH 01/26] add options to docker login --- commands.go | 61 ++++++++++++++-------- docs/sources/commandline/command/login.rst | 6 ++- 2 files changed, 44 insertions(+), 23 deletions(-) diff --git a/commands.go b/commands.go index 3d1efe7bf0..4fd175dd3a 100644 --- a/commands.go +++ b/commands.go @@ -282,45 +282,62 @@ func (cli *DockerCli) CmdLogin(args ...string) error { return readStringOnRawTerminal(stdin, stdout, false) } - oldState, err := term.SetRawTerminal() - if err != nil { - return err - } - defer term.RestoreTerminal(oldState) - - cmd := Subcmd("login", "", "Register or Login to the docker registry server") + cmd := Subcmd("login", "[OPTIONS]", "Register or Login to the docker registry server") + flUsername := cmd.String("u", "", "username") + flPassword := cmd.String("p", "", "password") + flEmail := cmd.String("e", "", "email") if err := cmd.Parse(args); err != nil { return nil } + var oldState *term.State + if *flUsername != "" && *flPassword != "" && *flEmail != "" { + oldState, err := term.SetRawTerminal() + if err != nil { + return err + } + defer term.RestoreTerminal(oldState) + } var username string var password string var email string - fmt.Print("Username (", cli.authConfig.Username, "): ") - username = readAndEchoString(os.Stdin, os.Stdout) - if username == "" { - username = cli.authConfig.Username + if *flUsername == "" { + fmt.Print("Username (", cli.authConfig.Username, "): ") + username = readAndEchoString(os.Stdin, os.Stdout) + if username == "" { + username = cli.authConfig.Username + } + } else { + username = *flUsername } if username != cli.authConfig.Username { - fmt.Print("Password: ") - password = readString(os.Stdin, os.Stdout) - - if password == "" { - return fmt.Errorf("Error : Password Required") + if *flPassword == "" { + fmt.Print("Password: ") + password = readString(os.Stdin, os.Stdout) + if password == "" { + return fmt.Errorf("Error : Password Required") + } + } else { + password = *flPassword } - fmt.Print("Email (", cli.authConfig.Email, "): ") - email = readAndEchoString(os.Stdin, os.Stdout) - if email == "" { - email = cli.authConfig.Email + if *flEmail == "" { + fmt.Print("Email (", cli.authConfig.Email, "): ") + email = readAndEchoString(os.Stdin, os.Stdout) + if email == "" { + email = cli.authConfig.Email + } + } else { + email = *flEmail } } else { password = cli.authConfig.Password email = cli.authConfig.Email } - term.RestoreTerminal(oldState) - + if oldState != nil { + term.RestoreTerminal(oldState) + } cli.authConfig.Username = username cli.authConfig.Password = password cli.authConfig.Email = email diff --git a/docs/sources/commandline/command/login.rst b/docs/sources/commandline/command/login.rst index bab4fa34e3..57ecaeb00e 100644 --- a/docs/sources/commandline/command/login.rst +++ b/docs/sources/commandline/command/login.rst @@ -8,6 +8,10 @@ :: - Usage: docker login + Usage: docker login [OPTIONS] Register or Login to the docker registry server + + -e="": email + -p="": password + -u="": username From ec6b35240ed19aa68e4295c9211aa13a7e37efad Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Sat, 22 Jun 2013 00:37:02 +0000 Subject: [PATCH 02/26] fix raw terminal --- commands.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/commands.go b/commands.go index 4fd175dd3a..f21dfdfe5c 100644 --- a/commands.go +++ b/commands.go @@ -286,12 +286,13 @@ func (cli *DockerCli) CmdLogin(args ...string) error { flUsername := cmd.String("u", "", "username") flPassword := cmd.String("p", "", "password") flEmail := cmd.String("e", "", "email") - if err := cmd.Parse(args); err != nil { + err := cmd.Parse(args) + if err != nil { return nil } var oldState *term.State - if *flUsername != "" && *flPassword != "" && *flEmail != "" { - oldState, err := term.SetRawTerminal() + if *flUsername == "" || *flPassword == "" || *flEmail == "" { + oldState, err = term.SetRawTerminal() if err != nil { return err } From 2e79719622964eb95eca70372ced0ad6e4dce4f5 Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Fri, 28 Jun 2013 15:51:58 +0000 Subject: [PATCH 03/26] add /proc to list running processes inside a container --- api.go | 18 ++++++++++++++++++ api_params.go | 7 +++++++ commands.go | 28 ++++++++++++++++++++++++++++ server.go | 36 ++++++++++++++++++++++++++++++++++++ utils/utils.go | 2 +- 5 files changed, 90 insertions(+), 1 deletion(-) diff --git a/api.go b/api.go index de4113231c..fb7bbc4614 100644 --- a/api.go +++ b/api.go @@ -250,6 +250,23 @@ func getContainersChanges(srv *Server, version float64, w http.ResponseWriter, r return nil } +func getContainersProc(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error { + if vars == nil { + return fmt.Errorf("Missing parameter") + } + name := vars["name"] + procsStr, err := srv.ContainerProc(name) + if err != nil { + return err + } + b, err := json.Marshal(procsStr) + if err != nil { + return err + } + writeJSON(w, b) + return nil +} + func getContainersJSON(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := parseForm(r); err != nil { return err @@ -842,6 +859,7 @@ func createRouter(srv *Server, logging bool) (*mux.Router, error) { "/containers/{name:.*}/export": getContainersExport, "/containers/{name:.*}/changes": getContainersChanges, "/containers/{name:.*}/json": getContainersByName, + "/containers/{name:.*}/proc": getContainersProc, }, "POST": { "/auth": postAuth, diff --git a/api_params.go b/api_params.go index b8af690c7f..89fe180d02 100644 --- a/api_params.go +++ b/api_params.go @@ -26,6 +26,13 @@ type APIInfo struct { SwapLimit bool `json:",omitempty"` } +type APIProc struct { + PID string + Tty string + Time string + Cmd string +} + type APIRmi struct { Deleted string `json:",omitempty"` Untagged string `json:",omitempty"` diff --git a/commands.go b/commands.go index 6ada02d552..73f9b949a9 100644 --- a/commands.go +++ b/commands.go @@ -90,6 +90,7 @@ func (cli *DockerCli) CmdHelp(args ...string) error { {"login", "Register or Login to the docker registry server"}, {"logs", "Fetch the logs of a container"}, {"port", "Lookup the public-facing port which is NAT-ed to PRIVATE_PORT"}, + {"proc", "Lookup the running processes of a container"}, {"ps", "List containers"}, {"pull", "Pull an image or a repository from the docker registry server"}, {"push", "Push an image or a repository to the docker registry server"}, @@ -555,6 +556,33 @@ func (cli *DockerCli) CmdInspect(args ...string) error { return nil } +func (cli *DockerCli) CmdProc(args ...string) error { + cmd := Subcmd("proc", "CONTAINER", "Lookup the running processes of a container") + if err := cmd.Parse(args); err != nil { + return nil + } + if cmd.NArg() != 1 { + cmd.Usage() + return nil + } + body, _, err := cli.call("GET", "/containers/"+cmd.Arg(0)+"/proc", nil) + if err != nil { + return err + } + var procs []APIProc + err = json.Unmarshal(body, &procs) + if err != nil { + return err + } + w := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0) + fmt.Fprintln(w, "PID\tTTY\tTIME\tCMD") + for _, proc := range procs { + fmt.Fprintf(w, "%s\t%s\t%s\t%s\n", proc.PID, proc.Tty, proc.Time, proc.Cmd) + } + w.Flush() + return nil +} + func (cli *DockerCli) CmdPort(args ...string) error { cmd := Subcmd("port", "CONTAINER PRIVATE_PORT", "Lookup the public-facing port which is NAT-ed to PRIVATE_PORT") if err := cmd.Parse(args); err != nil { diff --git a/server.go b/server.go index cdc01c6b09..7d292fc645 100644 --- a/server.go +++ b/server.go @@ -1,6 +1,7 @@ package docker import ( + "bufio" "errors" "fmt" "github.com/dotcloud/docker/auth" @@ -12,6 +13,7 @@ import ( "net/http" "net/url" "os" + "os/exec" "path" "runtime" "strings" @@ -247,6 +249,40 @@ func (srv *Server) ImageHistory(name string) ([]APIHistory, error) { } +func (srv *Server) ContainerProc(name string) ([]APIProc, error) { + if container := srv.runtime.Get(name); container != nil { + output, err := exec.Command("lxc-ps", "--name", container.ID).CombinedOutput() + if err != nil { + return nil, fmt.Errorf("Error trying to use lxc-ps: %s (%s)", err, output) + } + var procs []APIProc + for i, line := range strings.Split(string(output), "\n") { + if i == 0 || len(line) == 0 { + continue + } + proc := APIProc{} + scanner := bufio.NewScanner(strings.NewReader(line)) + scanner.Split(bufio.ScanWords) + if !scanner.Scan() { + return nil, fmt.Errorf("Error trying to use lxc-ps") + } + // no scanner.Text because we skip container id + scanner.Scan() + proc.PID = scanner.Text() + scanner.Scan() + proc.Tty = scanner.Text() + scanner.Scan() + proc.Time = scanner.Text() + scanner.Scan() + proc.Cmd = scanner.Text() + procs = append(procs, proc) + } + return procs, nil + + } + return nil, fmt.Errorf("No such container: %s", name) +} + func (srv *Server) ContainerChanges(name string) ([]Change, error) { if container := srv.runtime.Get(name); container != nil { return container.Changes() diff --git a/utils/utils.go b/utils/utils.go index e5b2a0f4fd..2f2a52867e 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -78,7 +78,7 @@ func (r *progressReader) Read(p []byte) (n int, err error) { read, err := io.ReadCloser(r.reader).Read(p) r.readProgress += read - updateEvery := 1024*512 //512kB + updateEvery := 1024 * 512 //512kB if r.readTotal > 0 { // Update progress for every 1% read if 1% < 512kB if increment := int(0.01 * float64(r.readTotal)); increment < updateEvery { From 8589fd6db8d52f93ba64a0d72c6eebe8d50530f6 Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Fri, 28 Jun 2013 18:05:41 +0200 Subject: [PATCH 04/26] Add doc --- docs/sources/api/docker_remote_api.rst | 5 +++ docs/sources/api/docker_remote_api_v1.3.rst | 40 +++++++++++++++++++++ docs/sources/commandline/cli.rst | 1 + docs/sources/commandline/command/proc.rst | 13 +++++++ 4 files changed, 59 insertions(+) create mode 100644 docs/sources/commandline/command/proc.rst diff --git a/docs/sources/api/docker_remote_api.rst b/docs/sources/api/docker_remote_api.rst index c6e50a3f06..f002ac40bc 100644 --- a/docs/sources/api/docker_remote_api.rst +++ b/docs/sources/api/docker_remote_api.rst @@ -29,6 +29,11 @@ You can still call an old version of the api using /v1.0/images//insert What's new ---------- +Listing processes (/proc): + +- List the processes inside a container + + Builder (/build): - Simplify the upload of the build context diff --git a/docs/sources/api/docker_remote_api_v1.3.rst b/docs/sources/api/docker_remote_api_v1.3.rst index 8eeb010d98..6f7025c449 100644 --- a/docs/sources/api/docker_remote_api_v1.3.rst +++ b/docs/sources/api/docker_remote_api_v1.3.rst @@ -220,6 +220,46 @@ Inspect a container :statuscode 500: server error +List processes running inside a container +***************************************** + +.. http:get:: /containers/(id)/proc + + List processes running inside the container ``id`` + + **Example request**: + + .. sourcecode:: http + + GET /containers/4fa6e0f0c678/proc HTTP/1.1 + + **Example response**: + + .. sourcecode:: http + + HTTP/1.1 200 OK + Content-Type: application/json + + [ + { + "PID":"11935", + "Tty":"pts/2", + "Time":"00:00:00", + "Cmd":"sh" + }, + { + "PID":"12140", + "Tty":"pts/2", + "Time":"00:00:00", + "Cmd":"sleep" + } + ] + + :statuscode 200: no error + :statuscode 404: no such container + :statuscode 500: server error + + Inspect changes on a container's filesystem ******************************************* diff --git a/docs/sources/commandline/cli.rst b/docs/sources/commandline/cli.rst index 118f42f6e8..357423fb93 100644 --- a/docs/sources/commandline/cli.rst +++ b/docs/sources/commandline/cli.rst @@ -41,6 +41,7 @@ Available Commands command/login command/logs command/port + command/proc command/ps command/pull command/push diff --git a/docs/sources/commandline/command/proc.rst b/docs/sources/commandline/command/proc.rst new file mode 100644 index 0000000000..b7e9a96ae1 --- /dev/null +++ b/docs/sources/commandline/command/proc.rst @@ -0,0 +1,13 @@ +:title: Proc Command +:description: Lookup the running processes of a container +:keywords: proc, docker, container, documentation + +======================================================= +``proc`` -- Lookup the running processes of a container +======================================================= + +:: + + Usage: docker proc CONTAINER + + Lookup the running processes of a container From 648c4f198b7215a35f31deb9cdf4d370049ef4b2 Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Fri, 28 Jun 2013 16:27:00 +0000 Subject: [PATCH 05/26] Add test --- api_test.go | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/api_test.go b/api_test.go index 4306f74100..f0ce4bf9f7 100644 --- a/api_test.go +++ b/api_test.go @@ -475,6 +475,61 @@ func TestGetContainersChanges(t *testing.T) { } } +func TestGetContainersProc(t *testing.T) { + runtime, err := newTestRuntime() + if err != nil { + t.Fatal(err) + } + defer nuke(runtime) + + srv := &Server{runtime: runtime} + + builder := NewBuilder(runtime) + + container, err := builder.Create( + &Config{ + Image: GetTestImage(runtime).ID, + Cmd: []string{"/bin/sh", "-c", "sleep 2"}, + }, + ) + if err != nil { + t.Fatal(err) + } + defer runtime.Destroy(container) + hostConfig := &HostConfig{} + if err := container.Start(hostConfig); err != nil { + t.Fatal(err) + } + + // Give some time to the process to start + container.WaitTimeout(500 * time.Millisecond) + + if !container.State.Running { + t.Errorf("Container should be running") + } + + r := httptest.NewRecorder() + if err := getContainersProc(srv, APIVERSION, r, nil, map[string]string{"name": container.ID}); err != nil { + t.Fatal(err) + } + procs := []APIProc{} + if err := json.Unmarshal(r.Body.Bytes(), &procs); err != nil { + t.Fatal(err) + } + + if len(procs) != 2 { + t.Fatalf("Expected 2 processes, found %d.", len(procs)) + } + + if procs[0].Cmd != "sh" && procs[0].Cmd != "exe" { + t.Fatalf("Expected `sleep` or `sh`, found %s.", procs[0].Cmd) + } + + if procs[1].Cmd != "sh" && procs[1].Cmd != "exe" { + t.Fatalf("Expected `sleep` or `sh`, found %s.", procs[1].Cmd) + } +} + func TestGetContainersByName(t *testing.T) { runtime, err := newTestRuntime() if err != nil { From 52cebe19e5323e03ef557102c3dc1fc734fcfd27 Mon Sep 17 00:00:00 2001 From: Keli Hu Date: Mon, 1 Jul 2013 15:53:27 +0800 Subject: [PATCH 06/26] Keep debian package up-to-date --- packaging/debian/Makefile | 6 +++--- packaging/debian/parse_changelog.py | 23 +++++++++++++++++++++++ 2 files changed, 26 insertions(+), 3 deletions(-) create mode 100755 packaging/debian/parse_changelog.py diff --git a/packaging/debian/Makefile b/packaging/debian/Makefile index 088b4b75dc..1f544b5fe3 100644 --- a/packaging/debian/Makefile +++ b/packaging/debian/Makefile @@ -13,8 +13,8 @@ PKG_NAME=lxc-docker ROOT_PATH=$(shell git rev-parse --show-toplevel) GITHUB_PATH=github.com/dotcloud/docker BUILD_SRC=build_src -VERSION_TAG?=v$(shell sed -E 's/.+\((.+)-.+\).+/\1/;q' changelog) -VERSION=$(shell echo ${VERSION_TAG} | cut -c2-) +VERSION=$(shell sed -En '0,/^\#\# /{s/^\#\# ([^ ]+).+/\1/p}' ../../CHANGELOG.md) +VERSION_TAG?=v${VERSION} DOCKER_VERSION=${PKG_NAME}_${VERSION} all: @@ -28,7 +28,6 @@ install: mkdir -p $(DESTDIR)/usr/share/doc/lxc-docker install -m 0755 src/${GITHUB_PATH}/docker/docker $(DESTDIR)/usr/bin/lxc-docker cp debian/lxc-docker.1 $(DESTDIR)/usr/share/man/man1 - cp debian/CHANGELOG.md $(DESTDIR)/usr/share/doc/lxc-docker/changelog debian: # Prepare docker source from revision ${VERSION_TAG} @@ -41,6 +40,7 @@ debian: cp -r `ls | grep -v ${BUILD_SRC}` ${BUILD_SRC}/debian cp ${ROOT_PATH}/README.md ${BUILD_SRC} cp ${ROOT_PATH}/CHANGELOG.md ${BUILD_SRC}/debian + ./parse_changelog.py < ../../CHANGELOG.md > ${BUILD_SRC}/debian/changelog # Cleanup rm -rf `find . -name '.git*'` rm -f ${DOCKER_VERSION}* diff --git a/packaging/debian/parse_changelog.py b/packaging/debian/parse_changelog.py new file mode 100755 index 0000000000..f14805d659 --- /dev/null +++ b/packaging/debian/parse_changelog.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python + +'Parse main CHANGELOG.md from stdin outputing on stdout the debian changelog' + +import sys,re, datetime + +on_block=False +for line in sys.stdin.readlines(): + line = line.strip() + if line.startswith('# ') or len(line) == 0: + continue + if line.startswith('## '): + if on_block: + print '\n -- dotCloud {0}\n'.format(date) + version, date = line[3:].split() + date = datetime.datetime.strptime(date, '(%Y-%m-%d)').strftime( + '%a, %d %b %Y 00:00:00 -0700') + on_block = True + print 'lxc-docker ({0}-1) precise; urgency=low'.format(version) + continue + if on_block: + print ' ' + line +print '\n -- dotCloud {0}'.format(date) From 11e28842ac74ad0b706d24ec639952f78e9a147c Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Mon, 1 Jul 2013 15:19:42 +0000 Subject: [PATCH 07/26] change to top --- api.go | 6 +++--- api_params.go | 2 +- api_test.go | 6 +++--- commands.go | 10 +++++----- docs/sources/api/docker_remote_api.rst | 2 +- docs/sources/api/docker_remote_api_v1.3.rst | 4 ++-- docs/sources/commandline/cli.rst | 2 +- docs/sources/commandline/command/{proc.rst => top.rst} | 8 ++++---- server.go | 6 +++--- 9 files changed, 23 insertions(+), 23 deletions(-) rename docs/sources/commandline/command/{proc.rst => top.rst} (58%) diff --git a/api.go b/api.go index fb7bbc4614..f6f54cfa41 100644 --- a/api.go +++ b/api.go @@ -250,12 +250,12 @@ func getContainersChanges(srv *Server, version float64, w http.ResponseWriter, r return nil } -func getContainersProc(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error { +func getContainersTop(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if vars == nil { return fmt.Errorf("Missing parameter") } name := vars["name"] - procsStr, err := srv.ContainerProc(name) + procsStr, err := srv.ContainerTop(name) if err != nil { return err } @@ -859,7 +859,7 @@ func createRouter(srv *Server, logging bool) (*mux.Router, error) { "/containers/{name:.*}/export": getContainersExport, "/containers/{name:.*}/changes": getContainersChanges, "/containers/{name:.*}/json": getContainersByName, - "/containers/{name:.*}/proc": getContainersProc, + "/containers/{name:.*}/top": getContainersTop, }, "POST": { "/auth": postAuth, diff --git a/api_params.go b/api_params.go index 89fe180d02..b371ca314f 100644 --- a/api_params.go +++ b/api_params.go @@ -26,7 +26,7 @@ type APIInfo struct { SwapLimit bool `json:",omitempty"` } -type APIProc struct { +type APITop struct { PID string Tty string Time string diff --git a/api_test.go b/api_test.go index f0ce4bf9f7..069a7e5818 100644 --- a/api_test.go +++ b/api_test.go @@ -475,7 +475,7 @@ func TestGetContainersChanges(t *testing.T) { } } -func TestGetContainersProc(t *testing.T) { +func TestGetContainersTop(t *testing.T) { runtime, err := newTestRuntime() if err != nil { t.Fatal(err) @@ -509,10 +509,10 @@ func TestGetContainersProc(t *testing.T) { } r := httptest.NewRecorder() - if err := getContainersProc(srv, APIVERSION, r, nil, map[string]string{"name": container.ID}); err != nil { + if err := getContainersTop(srv, APIVERSION, r, nil, map[string]string{"name": container.ID}); err != nil { t.Fatal(err) } - procs := []APIProc{} + procs := []APITop{} if err := json.Unmarshal(r.Body.Bytes(), &procs); err != nil { t.Fatal(err) } diff --git a/commands.go b/commands.go index 73f9b949a9..719326d79e 100644 --- a/commands.go +++ b/commands.go @@ -90,7 +90,7 @@ func (cli *DockerCli) CmdHelp(args ...string) error { {"login", "Register or Login to the docker registry server"}, {"logs", "Fetch the logs of a container"}, {"port", "Lookup the public-facing port which is NAT-ed to PRIVATE_PORT"}, - {"proc", "Lookup the running processes of a container"}, + {"top", "Lookup the running processes of a container"}, {"ps", "List containers"}, {"pull", "Pull an image or a repository from the docker registry server"}, {"push", "Push an image or a repository to the docker registry server"}, @@ -556,8 +556,8 @@ func (cli *DockerCli) CmdInspect(args ...string) error { return nil } -func (cli *DockerCli) CmdProc(args ...string) error { - cmd := Subcmd("proc", "CONTAINER", "Lookup the running processes of a container") +func (cli *DockerCli) CmdTop(args ...string) error { + cmd := Subcmd("top", "CONTAINER", "Lookup the running processes of a container") if err := cmd.Parse(args); err != nil { return nil } @@ -565,11 +565,11 @@ func (cli *DockerCli) CmdProc(args ...string) error { cmd.Usage() return nil } - body, _, err := cli.call("GET", "/containers/"+cmd.Arg(0)+"/proc", nil) + body, _, err := cli.call("GET", "/containers/"+cmd.Arg(0)+"/top", nil) if err != nil { return err } - var procs []APIProc + var procs []APITop err = json.Unmarshal(body, &procs) if err != nil { return err diff --git a/docs/sources/api/docker_remote_api.rst b/docs/sources/api/docker_remote_api.rst index f002ac40bc..ea75ada703 100644 --- a/docs/sources/api/docker_remote_api.rst +++ b/docs/sources/api/docker_remote_api.rst @@ -29,7 +29,7 @@ You can still call an old version of the api using /v1.0/images//insert What's new ---------- -Listing processes (/proc): +Listing processes (/top): - List the processes inside a container diff --git a/docs/sources/api/docker_remote_api_v1.3.rst b/docs/sources/api/docker_remote_api_v1.3.rst index 6f7025c449..a56703e6a4 100644 --- a/docs/sources/api/docker_remote_api_v1.3.rst +++ b/docs/sources/api/docker_remote_api_v1.3.rst @@ -223,7 +223,7 @@ Inspect a container List processes running inside a container ***************************************** -.. http:get:: /containers/(id)/proc +.. http:get:: /containers/(id)/top List processes running inside the container ``id`` @@ -231,7 +231,7 @@ List processes running inside a container .. sourcecode:: http - GET /containers/4fa6e0f0c678/proc HTTP/1.1 + GET /containers/4fa6e0f0c678/top HTTP/1.1 **Example response**: diff --git a/docs/sources/commandline/cli.rst b/docs/sources/commandline/cli.rst index 357423fb93..e499b1f096 100644 --- a/docs/sources/commandline/cli.rst +++ b/docs/sources/commandline/cli.rst @@ -41,7 +41,6 @@ Available Commands command/login command/logs command/port - command/proc command/ps command/pull command/push @@ -53,5 +52,6 @@ Available Commands command/start command/stop command/tag + command/top command/version command/wait diff --git a/docs/sources/commandline/command/proc.rst b/docs/sources/commandline/command/top.rst similarity index 58% rename from docs/sources/commandline/command/proc.rst rename to docs/sources/commandline/command/top.rst index b7e9a96ae1..bdd35adcfa 100644 --- a/docs/sources/commandline/command/proc.rst +++ b/docs/sources/commandline/command/top.rst @@ -1,13 +1,13 @@ -:title: Proc Command +:title: Top Command :description: Lookup the running processes of a container -:keywords: proc, docker, container, documentation +:keywords: top, docker, container, documentation ======================================================= -``proc`` -- Lookup the running processes of a container +``top`` -- Lookup the running processes of a container ======================================================= :: - Usage: docker proc CONTAINER + Usage: docker top CONTAINER Lookup the running processes of a container diff --git a/server.go b/server.go index 7d292fc645..b9eba5215b 100644 --- a/server.go +++ b/server.go @@ -249,18 +249,18 @@ func (srv *Server) ImageHistory(name string) ([]APIHistory, error) { } -func (srv *Server) ContainerProc(name string) ([]APIProc, error) { +func (srv *Server) ContainerTop(name string) ([]APITop, error) { if container := srv.runtime.Get(name); container != nil { output, err := exec.Command("lxc-ps", "--name", container.ID).CombinedOutput() if err != nil { return nil, fmt.Errorf("Error trying to use lxc-ps: %s (%s)", err, output) } - var procs []APIProc + var procs []APITop for i, line := range strings.Split(string(output), "\n") { if i == 0 || len(line) == 0 { continue } - proc := APIProc{} + proc := APITop{} scanner := bufio.NewScanner(strings.NewReader(line)) scanner.Split(bufio.ScanWords) if !scanner.Scan() { From 06b53e3fc7aca2b3dae32edab08c7662d3e9e7e8 Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Tue, 2 Jul 2013 12:19:25 +0000 Subject: [PATCH 08/26] store hostConfig to /tmp while container is running --- container.go | 30 ++++++++++++++++++++++++++++++ runtime.go | 4 +++- 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/container.go b/container.go index 508f39c0ef..f8459ecdc3 100644 --- a/container.go +++ b/container.go @@ -265,6 +265,29 @@ func (container *Container) ToDisk() (err error) { return ioutil.WriteFile(container.jsonPath(), data, 0666) } +func (container *Container) ReadTempHostConfig() (*HostConfig, error) { + data, err := ioutil.ReadFile(container.hostConfigPath()) + if err != nil { + return &HostConfig{}, err + } + hostConfig := &HostConfig{} + if err := json.Unmarshal(data, hostConfig); err != nil { + return &HostConfig{}, err + } + return hostConfig, nil +} + +func (container *Container) SaveHostConfig(hostConfig *HostConfig) (err error) { + if hostConfig == nil { + return os.Remove(container.hostConfigPath()) + } + data, err := json.Marshal(hostConfig) + if err != nil { + return + } + return ioutil.WriteFile(container.hostConfigPath(), data, 0666) +} + func (container *Container) generateLXCConfig() error { fo, err := os.Create(container.lxcConfigPath()) if err != nil { @@ -636,6 +659,7 @@ func (container *Container) Start(hostConfig *HostConfig) error { container.waitLock = make(chan struct{}) container.ToDisk() + container.SaveHostConfig(hostConfig) go container.monitor() return nil } @@ -791,6 +815,8 @@ func (container *Container) monitor() { // FIXME: why are we serializing running state to disk in the first place? //log.Printf("%s: Failed to dump configuration to the disk: %s", container.ID, err) } + // Remove temp host config + container.SaveHostConfig(nil) } func (container *Container) kill() error { @@ -970,6 +996,10 @@ func (container *Container) ReadLog(name string) (io.Reader, error) { return os.Open(container.logPath(name)) } +func (container *Container) hostConfigPath() string { + return path.Join("/tmp", container.ID+".config.host") +} + func (container *Container) jsonPath() string { return path.Join(container.root, "config.json") } diff --git a/runtime.go b/runtime.go index 06b1f8e1b9..7e728cfe6f 100644 --- a/runtime.go +++ b/runtime.go @@ -145,7 +145,7 @@ func (runtime *Runtime) Register(container *Container) error { container.State.Ghost = false container.State.setStopped(0) // assume empty host config - hostConfig := &HostConfig{} + hostConfig, _ := container.ReadTempHostConfig() if err := container.Start(hostConfig); err != nil { return err } @@ -156,6 +156,8 @@ func (runtime *Runtime) Register(container *Container) error { if err := container.ToDisk(); err != nil { return err } + // remove temp host config + container.SaveHostConfig(nil) } } } From 27a137ccab5990721298cf58aee588ca9b4200ff Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Tue, 2 Jul 2013 17:02:42 +0000 Subject: [PATCH 09/26] change file location --- container.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/container.go b/container.go index f8459ecdc3..a2e0c7049c 100644 --- a/container.go +++ b/container.go @@ -997,7 +997,7 @@ func (container *Container) ReadLog(name string) (io.Reader, error) { } func (container *Container) hostConfigPath() string { - return path.Join("/tmp", container.ID+".config.host") + return path.Join(container.root, "hostconfig.json") } func (container *Container) jsonPath() string { From 3042f11666a38ea6b1da206de372bf85463db945 Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Tue, 2 Jul 2013 18:02:16 +0000 Subject: [PATCH 10/26] never remove the file and try to load it in start --- container.go | 10 ++++------ runtime.go | 5 +---- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/container.go b/container.go index a2e0c7049c..7acbc7ba69 100644 --- a/container.go +++ b/container.go @@ -265,7 +265,7 @@ func (container *Container) ToDisk() (err error) { return ioutil.WriteFile(container.jsonPath(), data, 0666) } -func (container *Container) ReadTempHostConfig() (*HostConfig, error) { +func (container *Container) ReadHostConfig() (*HostConfig, error) { data, err := ioutil.ReadFile(container.hostConfigPath()) if err != nil { return &HostConfig{}, err @@ -278,9 +278,6 @@ func (container *Container) ReadTempHostConfig() (*HostConfig, error) { } func (container *Container) SaveHostConfig(hostConfig *HostConfig) (err error) { - if hostConfig == nil { - return os.Remove(container.hostConfigPath()) - } data, err := json.Marshal(hostConfig) if err != nil { return @@ -491,6 +488,9 @@ func (container *Container) Attach(stdin io.ReadCloser, stdinCloser io.Closer, s func (container *Container) Start(hostConfig *HostConfig) error { container.State.lock() defer container.State.unlock() + if len(hostConfig.Binds) == 0 { + hostConfig, _ = container.ReadHostConfig() + } if container.State.Running { return fmt.Errorf("The container %s is already running.", container.ID) @@ -815,8 +815,6 @@ func (container *Container) monitor() { // FIXME: why are we serializing running state to disk in the first place? //log.Printf("%s: Failed to dump configuration to the disk: %s", container.ID, err) } - // Remove temp host config - container.SaveHostConfig(nil) } func (container *Container) kill() error { diff --git a/runtime.go b/runtime.go index 7e728cfe6f..d283540abb 100644 --- a/runtime.go +++ b/runtime.go @@ -144,8 +144,7 @@ func (runtime *Runtime) Register(container *Container) error { utils.Debugf("Restarting") container.State.Ghost = false container.State.setStopped(0) - // assume empty host config - hostConfig, _ := container.ReadTempHostConfig() + hostConfig := &HostConfig{} if err := container.Start(hostConfig); err != nil { return err } @@ -156,8 +155,6 @@ func (runtime *Runtime) Register(container *Container) error { if err := container.ToDisk(); err != nil { return err } - // remove temp host config - container.SaveHostConfig(nil) } } } From eb9fef2c424178277cacd29d404cc8e774bb3891 Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Wed, 3 Jul 2013 17:33:30 -0900 Subject: [PATCH 11/26] Add VOLUME instruction to buildfile --- buildfile.go | 21 +++++++++++++++++++++ buildfile_test.go | 9 +++++++++ docs/sources/use/builder.rst | 7 +++++++ utils.go | 3 +++ 4 files changed, 40 insertions(+) diff --git a/buildfile.go b/buildfile.go index a13dc82c55..38e6c330d6 100644 --- a/buildfile.go +++ b/buildfile.go @@ -173,6 +173,27 @@ func (b *buildFile) CmdEntrypoint(args string) error { return nil } +func (b *buildFile) CmdVolume(args string) error { + if args == "" { + return fmt.Errorf("Volume cannot be empty") + } + + var volume []string + if err := json.Unmarshal([]byte(args), &volume); err != nil { + volume = []string{args} + } + if b.config.Volumes == nil { + b.config.Volumes = NewPathOpts() + } + for _, v := range volume { + b.config.Volumes[v] = struct{}{} + } + if err := b.commit("", b.config.Cmd, fmt.Sprintf("VOLUME %s", args)); err != nil { + return err + } + return nil +} + func (b *buildFile) addRemote(container *Container, orig, dest string) error { file, err := utils.Download(orig, ioutil.Discard) if err != nil { diff --git a/buildfile_test.go b/buildfile_test.go index 8913284e8f..9f5c692cf2 100644 --- a/buildfile_test.go +++ b/buildfile_test.go @@ -87,6 +87,15 @@ run [ "$FOO" = "BAR" ] from %s ENTRYPOINT /bin/echo CMD Hello world +`, + nil, + }, + + { + ` +from docker-ut +VOLUME /test +CMD Hello world `, nil, }, diff --git a/docs/sources/use/builder.rst b/docs/sources/use/builder.rst index 6a12beadf8..ab416281b4 100644 --- a/docs/sources/use/builder.rst +++ b/docs/sources/use/builder.rst @@ -160,6 +160,13 @@ files and directories are created with mode 0700, uid and gid 0. The `ENTRYPOINT` instruction adds an entry command that will not be overwritten when arguments are passed to docker run, unlike the behavior of `CMD`. This allows arguments to be passed to the entrypoint. i.e. `docker run -d` will pass the "-d" argument to the entrypoint. +2.9 VOLUME +---------- + + ``VOLUME ["/data"]`` + +The `VOLUME` instruction will add one or more new volumes to any container created from the image. + 3. Dockerfile Examples ====================== diff --git a/utils.go b/utils.go index 103e762822..caef086289 100644 --- a/utils.go +++ b/utils.go @@ -89,4 +89,7 @@ func MergeConfig(userConf, imageConf *Config) { if userConf.Entrypoint == nil || len(userConf.Entrypoint) == 0 { userConf.Entrypoint = imageConf.Entrypoint } + if userConf.Volumes == nil || len(userConf.Volumes) == 0 { + userConf.Volumes = imageConf.Volumes + } } From 1267e15b0f73f4c40b3a053f0bf02981881a2bdd Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Mon, 8 Jul 2013 04:02:06 -0900 Subject: [PATCH 12/26] Add unittest for volume config verification --- buildfile_test.go | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/buildfile_test.go b/buildfile_test.go index 9f5c692cf2..89c0916ad0 100644 --- a/buildfile_test.go +++ b/buildfile_test.go @@ -123,3 +123,40 @@ func TestBuild(t *testing.T) { } } } + +func TestVolume(t *testing.T) { + runtime, err := newTestRuntime() + if err != nil { + t.Fatal(err) + } + defer nuke(runtime) + + srv := &Server{ + runtime: runtime, + lock: &sync.Mutex{}, + pullingPool: make(map[string]struct{}), + pushingPool: make(map[string]struct{}), + } + + buildfile := NewBuildFile(srv, ioutil.Discard) + imgId, err := buildfile.Build(mkTestContext(` +from docker-ut +VOLUME /test +CMD Hello world +`, nil, t)) + if err != nil { + t.Fatal(err) + } + img, err := srv.ImageInspect(imgId) + if err != nil { + t.Fatal(err) + } + if len(img.Config.Volumes) == 0 { + t.Fail() + } + for key, _ := range img.Config.Volumes { + if key != "/test" { + t.Fail() + } + } +} From 40f1e4edbecc841bd02b3a63b303d522299562c0 Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Wed, 10 Jul 2013 07:03:07 -0900 Subject: [PATCH 13/26] Rebased changes buildfile_test --- buildfile_test.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/buildfile_test.go b/buildfile_test.go index 89c0916ad0..9250f73765 100644 --- a/buildfile_test.go +++ b/buildfile_test.go @@ -93,7 +93,7 @@ CMD Hello world { ` -from docker-ut +from %s VOLUME /test CMD Hello world `, @@ -133,14 +133,13 @@ func TestVolume(t *testing.T) { srv := &Server{ runtime: runtime, - lock: &sync.Mutex{}, pullingPool: make(map[string]struct{}), pushingPool: make(map[string]struct{}), } buildfile := NewBuildFile(srv, ioutil.Discard) imgId, err := buildfile.Build(mkTestContext(` -from docker-ut +from %s VOLUME /test CMD Hello world `, nil, t)) From b7937e268fcbc529a168164fc242edc56d51094c Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Thu, 11 Jul 2013 12:21:43 +0000 Subject: [PATCH 14/26] add debug for error in the server --- api.go | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/api.go b/api.go index c4a5222dc6..938af0c40f 100644 --- a/api.go +++ b/api.go @@ -47,21 +47,22 @@ func parseMultipartForm(r *http.Request) error { } func httpError(w http.ResponseWriter, err error) { + statusCode := http.StatusInternalServerError if strings.HasPrefix(err.Error(), "No such") { - http.Error(w, err.Error(), http.StatusNotFound) + statusCode = http.StatusNotFound } else if strings.HasPrefix(err.Error(), "Bad parameter") { - http.Error(w, err.Error(), http.StatusBadRequest) + statusCode = http.StatusBadRequest } else if strings.HasPrefix(err.Error(), "Conflict") { - http.Error(w, err.Error(), http.StatusConflict) + statusCode = http.StatusConflict } else if strings.HasPrefix(err.Error(), "Impossible") { - http.Error(w, err.Error(), http.StatusNotAcceptable) + statusCode = http.StatusNotAcceptable } else if strings.HasPrefix(err.Error(), "Wrong login/password") { - http.Error(w, err.Error(), http.StatusUnauthorized) + statusCode = http.StatusUnauthorized } else if strings.Contains(err.Error(), "hasn't been activated") { - http.Error(w, err.Error(), http.StatusForbidden) - } else { - http.Error(w, err.Error(), http.StatusInternalServerError) + statusCode = http.StatusForbidden } + utils.Debugf("[error %d] %s", statusCode, err) + http.Error(w, err.Error(), statusCode) } func writeJSON(w http.ResponseWriter, b []byte) { From affe7caf78288a638df3db37d5cebb4dc7f9ff72 Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Thu, 11 Jul 2013 19:27:19 +0200 Subject: [PATCH 15/26] fix broken docker port --- commands.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/commands.go b/commands.go index feab558259..57c258960d 100644 --- a/commands.go +++ b/commands.go @@ -574,8 +574,10 @@ func (cli *DockerCli) CmdPort(args ...string) error { return err } - if frontend, exists := out.NetworkSettings.PortMapping[cmd.Arg(1)]; exists { - fmt.Fprintf(cli.out, "%s\n", frontend) + if frontend, exists := out.NetworkSettings.PortMapping["Tcp"][cmd.Arg(1)]; exists { + fmt.Fprintf(cli.out, "tcp: %s\n", frontend) + } else if frontend, exists := out.NetworkSettings.PortMapping["Udp"][cmd.Arg(1)]; exists { + fmt.Fprintf(cli.out, "udp: %s\n", frontend) } else { return fmt.Errorf("Error: No private port '%s' allocated on %s", cmd.Arg(1), cmd.Arg(0)) } From 976428f505b64a51efbd97f71acff5db2f4f5ed0 Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Thu, 11 Jul 2013 20:30:08 +0200 Subject: [PATCH 16/26] change output --- commands.go | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/commands.go b/commands.go index 57c258960d..fd401a8c9c 100644 --- a/commands.go +++ b/commands.go @@ -564,6 +564,13 @@ func (cli *DockerCli) CmdPort(args ...string) error { return nil } + port := cmd.Arg(1) + proto := "Tcp" + parts := strings.SplitN(port, "/", 2) + if len(parts) == 2 && len(parts[1]) != 0 { + port = parts[0] + proto = strings.ToUpper(parts[1][:1]) + strings.ToLower(parts[1][1:]) + } body, _, err := cli.call("GET", "/containers/"+cmd.Arg(0)+"/json", nil) if err != nil { return err @@ -574,10 +581,8 @@ func (cli *DockerCli) CmdPort(args ...string) error { return err } - if frontend, exists := out.NetworkSettings.PortMapping["Tcp"][cmd.Arg(1)]; exists { - fmt.Fprintf(cli.out, "tcp: %s\n", frontend) - } else if frontend, exists := out.NetworkSettings.PortMapping["Udp"][cmd.Arg(1)]; exists { - fmt.Fprintf(cli.out, "udp: %s\n", frontend) + if frontend, exists := out.NetworkSettings.PortMapping[proto][port]; exists { + fmt.Fprintf(cli.out, "%s\n", frontend) } else { return fmt.Errorf("Error: No private port '%s' allocated on %s", cmd.Arg(1), cmd.Arg(0)) } From 71d2ff494694d7f18310c7994daa34dce33af98b Mon Sep 17 00:00:00 2001 From: "Guillaume J. Charmes" Date: Thu, 11 Jul 2013 17:31:07 -0700 Subject: [PATCH 17/26] Hotfix: check the length of entrypoint before comparing. --- utils.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/utils.go b/utils.go index caef086289..d55b1ff0bc 100644 --- a/utils.go +++ b/utils.go @@ -20,7 +20,8 @@ func CompareConfig(a, b *Config) bool { if len(a.Cmd) != len(b.Cmd) || len(a.Dns) != len(b.Dns) || len(a.Env) != len(b.Env) || - len(a.PortSpecs) != len(b.PortSpecs) { + len(a.PortSpecs) != len(b.PortSpecs) || + len(a.Entrypoint) != len(b.Entrypoint) { return false } From 2ac11419806ce39c57d5caf6b41a66e5ddf45bc8 Mon Sep 17 00:00:00 2001 From: Solomon Hykes Date: Thu, 11 Jul 2013 17:58:45 -0700 Subject: [PATCH 18/26] Don't leave broken, commented out tests lying around. --- api_test.go | 238 ------------------------------------- commands_test.go | 301 ----------------------------------------------- 2 files changed, 539 deletions(-) diff --git a/api_test.go b/api_test.go index d111f258c3..95b915b738 100644 --- a/api_test.go +++ b/api_test.go @@ -521,244 +521,6 @@ func TestPostCommit(t *testing.T) { } } -func TestPostImagesCreate(t *testing.T) { - // FIXME: Use the staging in order to perform tests - - // 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() { - // defer close(c1) - - // r := &hijackTester{ - // ResponseRecorder: httptest.NewRecorder(), - // in: stdin, - // out: stdoutPipe, - // } - - // req, err := http.NewRequest("POST", "/images/create?fromImage="+unitTestImageName, bytes.NewReader([]byte{})) - // if err != nil { - // t.Fatal(err) - // } - - // body, err := postImagesCreate(srv, r, req, nil) - // if err != nil { - // t.Fatal(err) - // } - // if body != nil { - // t.Fatalf("No body expected, received: %s\n", body) - // } - // }() - - // // Acknowledge hijack - // setTimeout(t, "hijack acknowledge timed out", 2*time.Second, func() { - // stdout.Read([]byte{}) - // stdout.Read(make([]byte, 4096)) - // }) - - // setTimeout(t, "Waiting for imagesCreate output", 5*time.Second, func() { - // reader := bufio.NewReader(stdout) - // line, err := reader.ReadString('\n') - // if err != nil { - // t.Fatal(err) - // } - // if !strings.HasPrefix(line, "Pulling repository d from") { - // t.Fatalf("Expected Pulling repository docker-ut from..., found %s", line) - // } - // }) - - // // Close pipes (client disconnects) - // if err := closeWrap(stdin, stdinPipe, stdout, stdoutPipe); err != nil { - // t.Fatal(err) - // } - - // // Wait for imagesCreate to finish, the client disconnected, therefore, Create finished his job - // setTimeout(t, "Waiting for imagesCreate timed out", 10*time.Second, func() { - // <-c1 - // }) -} - -func TestPostImagesInsert(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() - - // // Attach to it - // c1 := make(chan struct{}) - // go func() { - // defer close(c1) - // r := &hijackTester{ - // ResponseRecorder: httptest.NewRecorder(), - // in: stdin, - // out: stdoutPipe, - // } - - // req, err := http.NewRequest("POST", "/images/"+unitTestImageName+"/insert?path=%2Ftest&url=https%3A%2F%2Fraw.github.com%2Fdotcloud%2Fdocker%2Fmaster%2FREADME.md", bytes.NewReader([]byte{})) - // if err != nil { - // t.Fatal(err) - // } - // if err := postContainersCreate(srv, r, req, nil); err != nil { - // t.Fatal(err) - // } - // }() - - // // Acknowledge hijack - // setTimeout(t, "hijack acknowledge timed out", 5*time.Second, func() { - // stdout.Read([]byte{}) - // stdout.Read(make([]byte, 4096)) - // }) - - // id := "" - // setTimeout(t, "Waiting for imagesInsert output", 10*time.Second, func() { - // for { - // reader := bufio.NewReader(stdout) - // id, err = reader.ReadString('\n') - // if err != nil { - // t.Fatal(err) - // } - // } - // }) - - // // Close pipes (client disconnects) - // if err := closeWrap(stdin, stdinPipe, stdout, stdoutPipe); err != nil { - // t.Fatal(err) - // } - - // // Wait for attach to finish, the client disconnected, therefore, Attach finished his job - // setTimeout(t, "Waiting for CmdAttach timed out", 2*time.Second, func() { - // <-c1 - // }) - - // img, err := srv.runtime.repositories.LookupImage(id) - // if err != nil { - // t.Fatalf("New image %s expected but not found", id) - // } - - // layer, err := img.layer() - // if err != nil { - // t.Fatal(err) - // } - - // if _, err := os.Stat(path.Join(layer, "test")); err != nil { - // t.Fatalf("The test file has not been found") - // } - - // if err := srv.runtime.graph.Delete(img.ID); err != nil { - // t.Fatal(err) - // } -} - -func TestPostImagesPush(t *testing.T) { - //FIXME: Use staging in order to perform tests - // 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() { - // r := &hijackTester{ - // ResponseRecorder: httptest.NewRecorder(), - // in: stdin, - // out: stdoutPipe, - // } - - // req, err := http.NewRequest("POST", "/images/docker-ut/push", bytes.NewReader([]byte{})) - // if err != nil { - // t.Fatal(err) - // } - - // body, err := postImagesPush(srv, r, req, map[string]string{"name": "docker-ut"}) - // close(c1) - // if err != nil { - // t.Fatal(err) - // } - // if body != nil { - // t.Fatalf("No body expected, received: %s\n", body) - // } - // }() - - // // Acknowledge hijack - // setTimeout(t, "hijack acknowledge timed out", 2*time.Second, func() { - // stdout.Read([]byte{}) - // stdout.Read(make([]byte, 4096)) - // }) - - // setTimeout(t, "Waiting for imagesCreate output", 5*time.Second, func() { - // reader := bufio.NewReader(stdout) - // line, err := reader.ReadString('\n') - // if err != nil { - // t.Fatal(err) - // } - // if !strings.HasPrefix(line, "Processing checksum") { - // t.Fatalf("Processing checksum..., found %s", line) - // } - // }) - - // // Close pipes (client disconnects) - // if err := closeWrap(stdin, stdinPipe, stdout, stdoutPipe); err != nil { - // t.Fatal(err) - // } - - // // Wait for imagesPush to finish, the client disconnected, therefore, Push finished his job - // setTimeout(t, "Waiting for imagesPush timed out", 10*time.Second, func() { - // <-c1 - // }) -} - -func TestPostImagesTag(t *testing.T) { - // FIXME: Use staging in order to perform tests - - // runtime, err := newTestRuntime() - // if err != nil { - // t.Fatal(err) - // } - // defer nuke(runtime) - - // srv := &Server{runtime: runtime} - - // r := httptest.NewRecorder() - - // req, err := http.NewRequest("POST", "/images/docker-ut/tag?repo=testrepo&tag=testtag", bytes.NewReader([]byte{})) - // if err != nil { - // t.Fatal(err) - // } - - // body, err := postImagesTag(srv, r, req, map[string]string{"name": "docker-ut"}) - // if err != nil { - // t.Fatal(err) - // } - - // if body != nil { - // t.Fatalf("No body expected, received: %s\n", body) - // } - // if r.Code != http.StatusCreated { - // t.Fatalf("%d Created expected, received %d\n", http.StatusCreated, r.Code) - // } -} - func TestPostContainersCreate(t *testing.T) { runtime, err := newTestRuntime() if err != nil { diff --git a/commands_test.go b/commands_test.go index fe1c51a2a2..3f4c53db03 100644 --- a/commands_test.go +++ b/commands_test.go @@ -59,79 +59,6 @@ func assertPipe(input, output string, r io.Reader, w io.Writer, count int) error return nil } -/*TODO -func cmdImages(srv *Server, args ...string) (string, error) { - stdout, stdoutPipe := io.Pipe() - - go func() { - if err := srv.CmdImages(nil, stdoutPipe, args...); err != nil { - return - } - - // force the pipe closed, so that the code below gets an EOF - stdoutPipe.Close() - }() - - output, err := ioutil.ReadAll(stdout) - if err != nil { - return "", err - } - - // Cleanup pipes - return string(output), closeWrap(stdout, stdoutPipe) -} - -// TestImages checks that 'docker images' displays information correctly -func TestImages(t *testing.T) { - - runtime, err := newTestRuntime() - if err != nil { - t.Fatal(err) - } - defer nuke(runtime) - - srv := &Server{runtime: runtime} - - output, err := cmdImages(srv) - - if !strings.Contains(output, "REPOSITORY") { - t.Fatal("'images' should have a header") - } - if !strings.Contains(output, "docker-ut") { - t.Fatal("'images' should show the docker-ut image") - } - if !strings.Contains(output, "e9aa60c60128") { - t.Fatal("'images' should show the docker-ut image id") - } - - output, err = cmdImages(srv, "-q") - - if strings.Contains(output, "REPOSITORY") { - t.Fatal("'images -q' should not have a header") - } - if strings.Contains(output, "docker-ut") { - t.Fatal("'images' should not show the docker-ut image name") - } - if !strings.Contains(output, "e9aa60c60128") { - t.Fatal("'images' should show the docker-ut image id") - } - - output, err = cmdImages(srv, "-viz") - - if !strings.HasPrefix(output, "digraph docker {") { - t.Fatal("'images -v' should start with the dot header") - } - if !strings.HasSuffix(output, "}\n") { - t.Fatal("'images -v' should end with a '}'") - } - if !strings.Contains(output, "base -> \"e9aa60c60128\" [style=invis]") { - t.Fatal("'images -v' should have the docker-ut image id node") - } - - // todo: add checks for -a -} - -*/ // TestRunHostname checks that 'docker run -h' correctly sets a custom hostname func TestRunHostname(t *testing.T) { @@ -164,163 +91,6 @@ func TestRunHostname(t *testing.T) { } -/* -func TestRunExit(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() { - srv.CmdRun(stdin, rcli.NewDockerLocalConn(stdoutPipe), "-i", 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) - } - }) - - container := runtime.List()[0] - - // Closing /bin/cat stdin, expect it to exit - p, err := container.StdinPipe() - if err != nil { - t.Fatal(err) - } - if err := p.Close(); err != nil { - t.Fatal(err) - } - - // as the process exited, CmdRun must finish and unblock. Wait for it - setTimeout(t, "Waiting for CmdRun timed out", 2*time.Second, func() { - <-c1 - cmdWait(srv, container) - }) - - // Make sure that the client has been disconnected - setTimeout(t, "The client should have been disconnected once the remote process exited.", 2*time.Second, func() { - // Expecting pipe i/o error, just check that read does not block - stdin.Read([]byte{}) - }) - - // Cleanup pipes - if err := closeWrap(stdin, stdinPipe, stdout, stdoutPipe); err != nil { - t.Fatal(err) - } -} - -// Expected behaviour: the process dies when the client disconnects -func TestRunDisconnect(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", 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 cause stdin to be closed, which should - // cause /bin/cat to exit. - setTimeout(t, "Waiting for /bin/cat to exit timed out", 2*time.Second, func() { - container := runtime.List()[0] - container.Wait() - if container.State.Running { - t.Fatalf("/bin/cat is still running after closing stdin") - } - }) -} - -// 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, "Waiting for the container to be started timed out", 2*time.Second, func() { - for { - // Client disconnect after run -i should keep stdin out in TTY mode - l := runtime.List() - if len(l) == 1 && l[0].State.Running { - break - } - - time.Sleep(10 * time.Millisecond) - } - }) - - // Client disconnect after run -i should keep stdin out in TTY mode - container := runtime.List()[0] - - 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) - } - - // In tty mode, we expect the process to stay alive even after client's stdin closes. - // Do not wait for run to finish - - // 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, @@ -387,74 +157,3 @@ func TestRunAttachStdin(t *testing.T) { } } } - -/* -// Expected behaviour, the process stays alive when the client disconnects -func TestAttachDisconnect(t *testing.T) { - runtime, err := newTestRuntime() - if err != nil { - t.Fatal(err) - } - defer nuke(runtime) - - srv := &Server{runtime: runtime} - - container, err := NewBuilder(runtime).Create( - &Config{ - Image: GetTestImage(runtime).Id, - CpuShares: 1000, - Memory: 33554432, - Cmd: []string{"/bin/cat"}, - OpenStdin: true, - }, - ) - if err != nil { - t.Fatal(err) - } - defer runtime.Destroy(container) - - // Start the process - if err := container.Start(); err != nil { - t.Fatal(err) - } - - stdin, stdinPipe := io.Pipe() - stdout, stdoutPipe := io.Pipe() - - // Attach to it - c1 := make(chan struct{}) - 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, rcli.NewDockerLocalConn(stdoutPipe), container.Id) - close(c1) - }() - - 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) - } - }) - // Close pipes (client disconnects) - if err := closeWrap(stdin, stdinPipe, stdout, stdoutPipe); err != nil { - t.Fatal(err) - } - - // Wait for attach to finish, the client disconnected, therefore, Attach finished his job - setTimeout(t, "Waiting for CmdAttach timed out", 2*time.Second, func() { - <-c1 - }) - - // We closed stdin, expect /bin/cat to still be running - // Wait a little bit to make sure container.monitor() did his thing - err = container.WaitTimeout(500 * time.Millisecond) - if err == nil || !container.State.Running { - t.Fatalf("/bin/cat is not running after closing stdin") - } - - // Try to avoid the timeoout in destroy. Best effort, don't check error - cStdin, _ := container.StdinPipe() - cStdin.Close() - container.Wait() -} -*/ From 6bdb6f226b13d5fdd47f3665c9fb753207bf473b Mon Sep 17 00:00:00 2001 From: Solomon Hykes Date: Thu, 11 Jul 2013 17:59:25 -0700 Subject: [PATCH 19/26] Simplify unit tests code with mkRuntime() --- api_test.go | 111 ++++++++++------------------------------------ buildfile_test.go | 5 +-- container_test.go | 15 ++----- runtime_test.go | 42 +++--------------- server_test.go | 21 +++------ tags_test.go | 5 +-- utils_test.go | 31 ++++++++++++- 7 files changed, 67 insertions(+), 163 deletions(-) diff --git a/api_test.go b/api_test.go index 95b915b738..04fdbb7b4a 100644 --- a/api_test.go +++ b/api_test.go @@ -41,10 +41,8 @@ func TestGetBoolParam(t *testing.T) { } func TestGetVersion(t *testing.T) { - runtime, err := newTestRuntime() - if err != nil { - t.Fatal(err) - } + var err error + runtime := mkRuntime(t) defer nuke(runtime) srv := &Server{runtime: runtime} @@ -65,10 +63,7 @@ func TestGetVersion(t *testing.T) { } func TestGetInfo(t *testing.T) { - runtime, err := newTestRuntime() - if err != nil { - t.Fatal(err) - } + runtime := mkRuntime(t) defer nuke(runtime) srv := &Server{runtime: runtime} @@ -95,10 +90,7 @@ func TestGetInfo(t *testing.T) { } func TestGetImagesJSON(t *testing.T) { - runtime, err := newTestRuntime() - if err != nil { - t.Fatal(err) - } + runtime := mkRuntime(t) defer nuke(runtime) srv := &Server{runtime: runtime} @@ -220,10 +212,7 @@ func TestGetImagesJSON(t *testing.T) { } func TestGetImagesViz(t *testing.T) { - runtime, err := newTestRuntime() - if err != nil { - t.Fatal(err) - } + runtime := mkRuntime(t) defer nuke(runtime) srv := &Server{runtime: runtime} @@ -248,10 +237,7 @@ func TestGetImagesViz(t *testing.T) { } func TestGetImagesHistory(t *testing.T) { - runtime, err := newTestRuntime() - if err != nil { - t.Fatal(err) - } + runtime := mkRuntime(t) defer nuke(runtime) srv := &Server{runtime: runtime} @@ -272,10 +258,7 @@ func TestGetImagesHistory(t *testing.T) { } func TestGetImagesByName(t *testing.T) { - runtime, err := newTestRuntime() - if err != nil { - t.Fatal(err) - } + runtime := mkRuntime(t) defer nuke(runtime) srv := &Server{runtime: runtime} @@ -295,10 +278,7 @@ func TestGetImagesByName(t *testing.T) { } func TestGetContainersJSON(t *testing.T) { - runtime, err := newTestRuntime() - if err != nil { - t.Fatal(err) - } + runtime := mkRuntime(t) defer nuke(runtime) srv := &Server{runtime: runtime} @@ -334,10 +314,7 @@ func TestGetContainersJSON(t *testing.T) { } func TestGetContainersExport(t *testing.T) { - runtime, err := newTestRuntime() - if err != nil { - t.Fatal(err) - } + runtime := mkRuntime(t) defer nuke(runtime) srv := &Server{runtime: runtime} @@ -389,10 +366,7 @@ func TestGetContainersExport(t *testing.T) { } func TestGetContainersChanges(t *testing.T) { - runtime, err := newTestRuntime() - if err != nil { - t.Fatal(err) - } + runtime := mkRuntime(t) defer nuke(runtime) srv := &Server{runtime: runtime} @@ -437,10 +411,7 @@ func TestGetContainersChanges(t *testing.T) { } func TestGetContainersByName(t *testing.T) { - runtime, err := newTestRuntime() - if err != nil { - t.Fatal(err) - } + runtime := mkRuntime(t) defer nuke(runtime) srv := &Server{runtime: runtime} @@ -473,10 +444,7 @@ func TestGetContainersByName(t *testing.T) { } func TestPostCommit(t *testing.T) { - runtime, err := newTestRuntime() - if err != nil { - t.Fatal(err) - } + runtime := mkRuntime(t) defer nuke(runtime) srv := &Server{runtime: runtime} @@ -522,10 +490,7 @@ func TestPostCommit(t *testing.T) { } func TestPostContainersCreate(t *testing.T) { - runtime, err := newTestRuntime() - if err != nil { - t.Fatal(err) - } + runtime := mkRuntime(t) defer nuke(runtime) srv := &Server{runtime: runtime} @@ -576,10 +541,7 @@ func TestPostContainersCreate(t *testing.T) { } func TestPostContainersKill(t *testing.T) { - runtime, err := newTestRuntime() - if err != nil { - t.Fatal(err) - } + runtime := mkRuntime(t) defer nuke(runtime) srv := &Server{runtime: runtime} @@ -621,10 +583,7 @@ func TestPostContainersKill(t *testing.T) { } func TestPostContainersRestart(t *testing.T) { - runtime, err := newTestRuntime() - if err != nil { - t.Fatal(err) - } + runtime := mkRuntime(t) defer nuke(runtime) srv := &Server{runtime: runtime} @@ -678,10 +637,7 @@ func TestPostContainersRestart(t *testing.T) { } func TestPostContainersStart(t *testing.T) { - runtime, err := newTestRuntime() - if err != nil { - t.Fatal(err) - } + runtime := mkRuntime(t) defer nuke(runtime) srv := &Server{runtime: runtime} @@ -731,10 +687,7 @@ func TestPostContainersStart(t *testing.T) { } func TestPostContainersStop(t *testing.T) { - runtime, err := newTestRuntime() - if err != nil { - t.Fatal(err) - } + runtime := mkRuntime(t) defer nuke(runtime) srv := &Server{runtime: runtime} @@ -781,10 +734,7 @@ func TestPostContainersStop(t *testing.T) { } func TestPostContainersWait(t *testing.T) { - runtime, err := newTestRuntime() - if err != nil { - t.Fatal(err) - } + runtime := mkRuntime(t) defer nuke(runtime) srv := &Server{runtime: runtime} @@ -826,10 +776,7 @@ func TestPostContainersWait(t *testing.T) { } func TestPostContainersAttach(t *testing.T) { - runtime, err := newTestRuntime() - if err != nil { - t.Fatal(err) - } + runtime := mkRuntime(t) defer nuke(runtime) srv := &Server{runtime: runtime} @@ -915,10 +862,7 @@ func TestPostContainersAttach(t *testing.T) { // FIXME: Test deleting container with volume // FIXME: Test deleting volume in use by other container func TestDeleteContainers(t *testing.T) { - runtime, err := newTestRuntime() - if err != nil { - t.Fatal(err) - } + runtime := mkRuntime(t) defer nuke(runtime) srv := &Server{runtime: runtime} @@ -958,10 +902,7 @@ func TestDeleteContainers(t *testing.T) { } func TestOptionsRoute(t *testing.T) { - runtime, err := newTestRuntime() - if err != nil { - t.Fatal(err) - } + runtime := mkRuntime(t) defer nuke(runtime) srv := &Server{runtime: runtime, enableCors: true} @@ -984,10 +925,7 @@ func TestOptionsRoute(t *testing.T) { } func TestGetEnabledCors(t *testing.T) { - runtime, err := newTestRuntime() - if err != nil { - t.Fatal(err) - } + runtime := mkRuntime(t) defer nuke(runtime) srv := &Server{runtime: runtime, enableCors: true} @@ -1025,10 +963,7 @@ func TestGetEnabledCors(t *testing.T) { } func TestDeleteImages(t *testing.T) { - runtime, err := newTestRuntime() - if err != nil { - t.Fatal(err) - } + runtime := mkRuntime(t) defer nuke(runtime) srv := &Server{runtime: runtime} diff --git a/buildfile_test.go b/buildfile_test.go index 8913284e8f..9749aa6402 100644 --- a/buildfile_test.go +++ b/buildfile_test.go @@ -96,10 +96,7 @@ CMD Hello world func TestBuild(t *testing.T) { for _, ctx := range testContexts { - runtime, err := newTestRuntime() - if err != nil { - t.Fatal(err) - } + runtime := mkRuntime(t) defer nuke(runtime) srv := &Server{ diff --git a/container_test.go b/container_test.go index 7646bb3793..e7f6818eb6 100644 --- a/container_test.go +++ b/container_test.go @@ -1046,10 +1046,7 @@ func TestEnv(t *testing.T) { } func TestEntrypoint(t *testing.T) { - runtime, err := newTestRuntime() - if err != nil { - t.Fatal(err) - } + runtime := mkRuntime(t) defer nuke(runtime) container, err := NewBuilder(runtime).Create( &Config{ @@ -1125,10 +1122,7 @@ func TestLXCConfig(t *testing.T) { } func BenchmarkRunSequencial(b *testing.B) { - runtime, err := newTestRuntime() - if err != nil { - b.Fatal(err) - } + runtime := mkRuntime(b) defer nuke(runtime) for i := 0; i < b.N; i++ { container, err := NewBuilder(runtime).Create(&Config{ @@ -1154,10 +1148,7 @@ func BenchmarkRunSequencial(b *testing.B) { } func BenchmarkRunParallel(b *testing.B) { - runtime, err := newTestRuntime() - if err != nil { - b.Fatal(err) - } + runtime := mkRuntime(b) defer nuke(runtime) var tasks []chan error diff --git a/runtime_test.go b/runtime_test.go index d8033467c2..9d43bd46e5 100644 --- a/runtime_test.go +++ b/runtime_test.go @@ -114,26 +114,6 @@ func init() { // FIXME: test that ImagePull(json=true) send correct json output -func newTestRuntime() (*Runtime, error) { - root, err := ioutil.TempDir("", "docker-test") - if err != nil { - return nil, err - } - if err := os.Remove(root); err != nil { - return nil, err - } - if err := utils.CopyDirectory(unitTestStoreBase, root); err != nil { - return nil, err - } - - runtime, err := NewRuntimeFromDirectory(root, false) - if err != nil { - return nil, err - } - runtime.UpdateCapabilities(true) - return runtime, nil -} - func GetTestImage(runtime *Runtime) *Image { imgs, err := runtime.graph.All() if err != nil { @@ -148,10 +128,7 @@ func GetTestImage(runtime *Runtime) *Image { } func TestRuntimeCreate(t *testing.T) { - runtime, err := newTestRuntime() - if err != nil { - t.Fatal(err) - } + runtime := mkRuntime(t) defer nuke(runtime) // Make sure we start we 0 containers @@ -223,10 +200,7 @@ func TestRuntimeCreate(t *testing.T) { } func TestDestroy(t *testing.T) { - runtime, err := newTestRuntime() - if err != nil { - t.Fatal(err) - } + runtime := mkRuntime(t) defer nuke(runtime) container, err := NewBuilder(runtime).Create(&Config{ Image: GetTestImage(runtime).ID, @@ -270,10 +244,7 @@ func TestDestroy(t *testing.T) { } func TestGet(t *testing.T) { - runtime, err := newTestRuntime() - if err != nil { - t.Fatal(err) - } + runtime := mkRuntime(t) defer nuke(runtime) builder := NewBuilder(runtime) @@ -323,11 +294,8 @@ func TestGet(t *testing.T) { } func startEchoServerContainer(t *testing.T, proto string) (*Runtime, *Container, string) { - runtime, err := newTestRuntime() - if err != nil { - t.Fatal(err) - } - + var err error + runtime := mkRuntime(t) port := 5554 var container *Container var strPort string diff --git a/server_test.go b/server_test.go index cf3e3f0bc8..05a286aaa8 100644 --- a/server_test.go +++ b/server_test.go @@ -5,10 +5,7 @@ import ( ) func TestContainerTagImageDelete(t *testing.T) { - runtime, err := newTestRuntime() - if err != nil { - t.Fatal(err) - } + runtime := mkRuntime(t) defer nuke(runtime) srv := &Server{runtime: runtime} @@ -62,10 +59,7 @@ func TestContainerTagImageDelete(t *testing.T) { } func TestCreateRm(t *testing.T) { - runtime, err := newTestRuntime() - if err != nil { - t.Fatal(err) - } + runtime := mkRuntime(t) defer nuke(runtime) srv := &Server{runtime: runtime} @@ -95,10 +89,7 @@ func TestCreateRm(t *testing.T) { } func TestCreateStartRestartStopStartKillRm(t *testing.T) { - runtime, err := newTestRuntime() - if err != nil { - t.Fatal(err) - } + runtime := mkRuntime(t) defer nuke(runtime) srv := &Server{runtime: runtime} @@ -154,11 +145,9 @@ func TestCreateStartRestartStopStartKillRm(t *testing.T) { } func TestRunWithTooLowMemoryLimit(t *testing.T) { - runtime, err := newTestRuntime() + var err error + runtime := mkRuntime(t) srv := &Server{runtime: runtime} - if err != nil { - t.Fatal(err) - } defer nuke(runtime) // Try to create a container with a memory limit of 1 byte less than the minimum allowed limit. _, err = srv.ContainerCreate( diff --git a/tags_test.go b/tags_test.go index 1974e751be..d920943795 100644 --- a/tags_test.go +++ b/tags_test.go @@ -5,10 +5,7 @@ import ( ) func TestLookupImage(t *testing.T) { - runtime, err := newTestRuntime() - if err != nil { - t.Fatal(err) - } + runtime := mkRuntime(t) defer nuke(runtime) if img, err := runtime.repositories.LookupImage(unitTestImageName); err != nil { diff --git a/utils_test.go b/utils_test.go index 5958aa70fe..4951d3a02d 100644 --- a/utils_test.go +++ b/utils_test.go @@ -7,6 +7,7 @@ import ( "path" "strings" "testing" + "github.com/dotcloud/docker/utils" ) // This file contains utility functions for docker's unit test suite. @@ -15,14 +16,40 @@ import ( // Create a temporary runtime suitable for unit testing. // Call t.Fatal() at the first error. -func mkRuntime(t *testing.T) *Runtime { +func mkRuntime(f Fataler) *Runtime { runtime, err := newTestRuntime() if err != nil { - t.Fatal(err) + f.Fatal(err) } return runtime } +// A common interface to access the Fatal method of +// both testing.B and testing.T. +type Fataler interface { + Fatal(args ...interface{}) +} + +func newTestRuntime() (*Runtime, error) { + root, err := ioutil.TempDir("", "docker-test") + if err != nil { + return nil, err + } + if err := os.Remove(root); err != nil { + return nil, err + } + if err := utils.CopyDirectory(unitTestStoreBase, root); err != nil { + return nil, err + } + + runtime, err := NewRuntimeFromDirectory(root, false) + if err != nil { + return nil, err + } + runtime.UpdateCapabilities(true) + return runtime, nil +} + // Write `content` to the file at path `dst`, creating it if necessary, // as well as any missing directories. // The file is truncated if it already exists. From 90483dc9123d6cb9eb1aa1bf4ba4e851d751784e Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Thu, 11 Jul 2013 16:41:19 -0900 Subject: [PATCH 20/26] Fix Docker Builder documentation numbering --- docs/sources/use/builder.rst | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/docs/sources/use/builder.rst b/docs/sources/use/builder.rst index ab416281b4..ceda9c99dd 100644 --- a/docs/sources/use/builder.rst +++ b/docs/sources/use/builder.rst @@ -48,12 +48,12 @@ Docker will ignore lines in Dockerfiles prefixed with "`#`", so you may add comment lines. A comment marker in the rest of the line will be treated as an argument. -2. Instructions +3. Instructions =============== Docker builder comes with a set of instructions, described below. -2.1 FROM +3.1 FROM -------- ``FROM `` @@ -65,7 +65,7 @@ a valid Dockerfile must have it as its first instruction. create multiple images. Simply make a note of the last image id output by the commit before each new `FROM` command. -2.2 MAINTAINER +3.2 MAINTAINER -------------- ``MAINTAINER `` @@ -73,7 +73,7 @@ commit before each new `FROM` command. The `MAINTAINER` instruction allows you to set the Author field of the generated images. -2.3 RUN +3.3 RUN ------- ``RUN `` @@ -86,7 +86,7 @@ Layering `RUN` instructions and generating commits conforms to the core concepts of Docker where commits are cheap and containers can be created from any point in an image's history, much like source control. -2.4 CMD +3.4 CMD ------- ``CMD `` @@ -100,7 +100,7 @@ This is functionally equivalent to running the result; `CMD` does not execute anything at build time, but specifies the intended command for the image. -2.5 EXPOSE +3.5 EXPOSE ---------- ``EXPOSE [...]`` @@ -109,7 +109,7 @@ The `EXPOSE` instruction sets ports to be publicly exposed when running the image. This is functionally equivalent to running `docker commit -run '{"PortSpecs": ["", ""]}'` outside the builder. -2.6 ENV +3.6 ENV ------- ``ENV `` @@ -121,7 +121,7 @@ functionally equivalent to prefixing the command with `=` .. note:: The environment variables will persist when a container is run from the resulting image. -2.7 ADD +3.7 ADD ------- ``ADD `` @@ -153,21 +153,21 @@ of `` will be written at ``. If `` doesn't exist, it is created along with all missing directories in its path. All new files and directories are created with mode 0700, uid and gid 0. -2.8 ENTRYPOINT +3.8 ENTRYPOINT ------------- ``ENTRYPOINT /bin/echo`` The `ENTRYPOINT` instruction adds an entry command that will not be overwritten when arguments are passed to docker run, unlike the behavior of `CMD`. This allows arguments to be passed to the entrypoint. i.e. `docker run -d` will pass the "-d" argument to the entrypoint. -2.9 VOLUME +3.9 VOLUME ---------- ``VOLUME ["/data"]`` The `VOLUME` instruction will add one or more new volumes to any container created from the image. -3. Dockerfile Examples +4. Dockerfile Examples ====================== .. code-block:: bash From a8a6848ce0c3c54d00cfcd85727546e54a4dcf7e Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Fri, 12 Jul 2013 11:54:53 +0000 Subject: [PATCH 21/26] fix tests regarding the new test image --- api_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/api_test.go b/api_test.go index 2ef043acbe..757004b26b 100644 --- a/api_test.go +++ b/api_test.go @@ -482,12 +482,12 @@ func TestGetContainersTop(t *testing.T) { t.Fatalf("Expected 2 processes, found %d.", len(procs)) } - if procs[0].Cmd != "sh" && procs[0].Cmd != "exe" { - t.Fatalf("Expected `sleep` or `sh`, found %s.", procs[0].Cmd) + if procs[0].Cmd != "sh" && procs[0].Cmd != "busybox" { + t.Fatalf("Expected `busybox` or `sh`, found %s.", procs[0].Cmd) } - if procs[1].Cmd != "sh" && procs[1].Cmd != "exe" { - t.Fatalf("Expected `sleep` or `sh`, found %s.", procs[1].Cmd) + if procs[1].Cmd != "sh" && procs[1].Cmd != "busybox" { + t.Fatalf("Expected `busybox` or `sh`, found %s.", procs[1].Cmd) } } From cd0fef633c5d871b192146e405ca7c5bebb2f3ba Mon Sep 17 00:00:00 2001 From: Sam Alba Date: Fri, 12 Jul 2013 10:42:54 -0700 Subject: [PATCH 22/26] Fixed tag option for "docker pull" (the option was ignored) --- commands.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/commands.go b/commands.go index b3b1a57199..b581590bc2 100644 --- a/commands.go +++ b/commands.go @@ -821,7 +821,9 @@ func (cli *DockerCli) CmdPull(args ...string) error { } remote, parsedTag := utils.ParseRepositoryTag(cmd.Arg(0)) - *tag = parsedTag + if *tag == "" { + *tag = parsedTag + } v := url.Values{} v.Set("fromImage", remote) From a6e5a397bd6b7967a91f1d7cbe581e46211964da Mon Sep 17 00:00:00 2001 From: Marcus Farkas Date: Fri, 12 Jul 2013 20:08:45 +0200 Subject: [PATCH 23/26] Revert "Client: better progressbar output" This reverts commit 3ac68f1966222d9a1c0ff867515e3f5c2f83e422. --- utils/utils.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/utils/utils.go b/utils/utils.go index df615844a7..6aa121c9e3 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -87,7 +87,7 @@ func (r *progressReader) Read(p []byte) (n int, err error) { } if r.readProgress-r.lastUpdate > updateEvery || err != nil { if r.readTotal > 0 { - fmt.Fprintf(r.output, r.template, HumanSize(int64(r.readProgress)), HumanSize(int64(r.readTotal)), fmt.Sprintf("%2.0f%%", float64(r.readProgress)/float64(r.readTotal)*100)) + fmt.Fprintf(r.output, r.template, HumanSize(int64(r.readProgress)), HumanSize(int64(r.readTotal)), fmt.Sprintf("%.0f%%", float64(r.readProgress)/float64(r.readTotal)*100)) } else { fmt.Fprintf(r.output, r.template, r.readProgress, "?", "n/a") } @@ -147,7 +147,7 @@ func HumanSize(size int64) string { sizef = sizef / 1000.0 i++ } - return fmt.Sprintf("%5.4g %s", sizef, units[i]) + return fmt.Sprintf("%.4g %s", sizef, units[i]) } func Trunc(s string, maxlen int) string { From bac5772312d1fe733511febd117e9f29ff19c698 Mon Sep 17 00:00:00 2001 From: Marcus Farkas Date: Fri, 12 Jul 2013 20:15:25 +0200 Subject: [PATCH 24/26] *Client: Fix the progressbar, without manipulating other outputs Prior this commit, 'docker images' and other cmd's, which used utils.HumanSize(), showed unnecessary whitespaces. Formatting of progress has been moved to FormatProgess(), justifing the string directly in the template. --- server.go | 8 ++++---- utils/utils.go | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/server.go b/server.go index 193f9e5778..c43cae0c38 100644 --- a/server.go +++ b/server.go @@ -100,7 +100,7 @@ func (srv *Server) ImageInsert(name, url, path string, out io.Writer, sf *utils. return "", err } - if err := c.Inject(utils.ProgressReader(file.Body, int(file.ContentLength), out, sf.FormatProgress("Downloading", "%v/%v (%v)"), sf), path); err != nil { + if err := c.Inject(utils.ProgressReader(file.Body, int(file.ContentLength), out, sf.FormatProgress("Downloading", "%8v/%v (%v)"), sf), path); err != nil { return "", err } // FIXME: Handle custom repo, tag comment, author @@ -379,7 +379,7 @@ func (srv *Server) pullImage(r *registry.Registry, out io.Writer, imgID, endpoin return err } defer layer.Close() - if err := srv.runtime.graph.Register(utils.ProgressReader(layer, imgSize, out, sf.FormatProgress("Downloading", "%v/%v (%v)"), sf), false, img); err != nil { + if err := srv.runtime.graph.Register(utils.ProgressReader(layer, imgSize, out, sf.FormatProgress("Downloading", "%8v/%v (%v)"), sf), false, img); err != nil { return err } } @@ -702,7 +702,7 @@ func (srv *Server) pushImage(r *registry.Registry, out io.Writer, remote, imgID, } // Send the layer - if err := r.PushImageLayerRegistry(imgData.ID, utils.ProgressReader(layerData, int(layerData.Size), out, sf.FormatProgress("Pushing", "%v/%v (%v)"), sf), ep, token); err != nil { + if err := r.PushImageLayerRegistry(imgData.ID, utils.ProgressReader(layerData, int(layerData.Size), out, sf.FormatProgress("Pushing", "%8v/%v (%v)"), sf), ep, token); err != nil { return err } return nil @@ -772,7 +772,7 @@ func (srv *Server) ImageImport(src, repo, tag string, in io.Reader, out io.Write if err != nil { return err } - archive = utils.ProgressReader(resp.Body, int(resp.ContentLength), out, sf.FormatProgress("Importing", "%v/%v (%v)"), sf) + archive = utils.ProgressReader(resp.Body, int(resp.ContentLength), out, sf.FormatProgress("Importing", "%8v/%v (%v)"), sf) } img, err := srv.runtime.graph.Create(archive, nil, "Imported from "+src, "", nil) if err != nil { diff --git a/utils/utils.go b/utils/utils.go index 6aa121c9e3..153b1b910c 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -106,7 +106,7 @@ func (r *progressReader) Close() error { func ProgressReader(r io.ReadCloser, size int, output io.Writer, template []byte, sf *StreamFormatter) *progressReader { tpl := string(template) if tpl == "" { - tpl = string(sf.FormatProgress("", "%v/%v (%v)")) + tpl = string(sf.FormatProgress("", "%8v/%v (%v)")) } return &progressReader{r, NewWriteFlusher(output), size, 0, 0, tpl, sf} } From c7a48e91d8d31a8b80b46f4cb17d866e36147ddc Mon Sep 17 00:00:00 2001 From: Daniel Mizyrycki Date: Fri, 12 Jul 2013 15:06:02 -0700 Subject: [PATCH 25/26] Packaging, issue #1202: Upgrade vagrantfile go in debian packaging --- packaging/debian/Vagrantfile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packaging/debian/Vagrantfile b/packaging/debian/Vagrantfile index 5913882f9f..cb84ed5b8d 100644 --- a/packaging/debian/Vagrantfile +++ b/packaging/debian/Vagrantfile @@ -13,6 +13,9 @@ Vagrant::Config.run do |config| # Install debian packaging dependencies and create debian packages pkg_cmd = "apt-get -qq update; DEBIAN_FRONTEND=noninteractive apt-get install -qq -y #{PKG_DEP}; " \ + "curl -s -o /go.tar.gz https://go.googlecode.com/files/go1.1.1.linux-amd64.tar.gz; " \ + "tar -C /usr/local -xzf /go.tar.gz; rm /usr/bin/go; " \ + "ln -s /usr/local/go/bin/go /usr/bin; "\ "export GPG_KEY='#{ENV['GPG_KEY']}'; cd /data/docker/packaging/debian; make debian" config.vm.provision :shell, :inline => pkg_cmd end From 44b3e8d51b655d68d0a253c48c027360ff8c3a97 Mon Sep 17 00:00:00 2001 From: Nick Stenning Date: Fri, 28 Jun 2013 17:06:00 +0100 Subject: [PATCH 26/26] Reverse priority of tag lookup in TagStore.GetImage Currently, if you have the following images: foo/bar 1 23b27d50fb49 foo/bar 2 f2b86ec3fcc4 And you issue the following command: docker tag foo/bar:2 foo/bar latest docker will tag the "wrong" image, because the image id for foo/bar:1 starts with a "2". That is, you'll end up with the following: foo/bar 1 23b27d50fb49 foo/bar 2 f2b86ec3fcc4 foo/bar latest 23b27d50fb49 This commit reverses the priority given to tags vs. image ids in the construction `/:`, meaning that if a tag that is an exact match for the specified `tagOrId`, it will be tagged in preference to an image with an id that happens to start with the correct character sequence. --- tags.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tags.go b/tags.go index 9ad9d10d05..21c13bdfb2 100644 --- a/tags.go +++ b/tags.go @@ -204,15 +204,15 @@ func (store *TagStore) GetImage(repoName, tagOrID string) (*Image, error) { } else if repo == nil { return nil, nil } - //go through all the tags, to see if tag is in fact an ID + if revision, exists := repo[tagOrID]; exists { + return store.graph.Get(revision) + } + // If no matching tag is found, search through images for a matching image id for _, revision := range repo { if strings.HasPrefix(revision, tagOrID) { return store.graph.Get(revision) } } - if revision, exists := repo[tagOrID]; exists { - return store.graph.Get(revision) - } return nil, nil }