From fa5ff73c739407542c7fe9e15a77b73c69748bc0 Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Fri, 21 Jun 2013 10:00:25 +0000 Subject: [PATCH 01/62] add options to docker login Upstream-commit: 42bcfcc927499c63bbc3e3de80e6f52fd02fc9e8 Component: engine --- components/engine/commands.go | 61 ++++++++++++------- .../sources/commandline/command/login.rst | 6 +- 2 files changed, 44 insertions(+), 23 deletions(-) diff --git a/components/engine/commands.go b/components/engine/commands.go index 3d1efe7bf0..4fd175dd3a 100644 --- a/components/engine/commands.go +++ b/components/engine/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/components/engine/docs/sources/commandline/command/login.rst b/components/engine/docs/sources/commandline/command/login.rst index bab4fa34e3..57ecaeb00e 100644 --- a/components/engine/docs/sources/commandline/command/login.rst +++ b/components/engine/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 9a274aa9bda47cc3f63ea33d596f4be193dcb86b Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Sat, 22 Jun 2013 00:37:02 +0000 Subject: [PATCH 02/62] fix raw terminal Upstream-commit: ec6b35240ed19aa68e4295c9211aa13a7e37efad Component: engine --- components/engine/commands.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/components/engine/commands.go b/components/engine/commands.go index 4fd175dd3a..f21dfdfe5c 100644 --- a/components/engine/commands.go +++ b/components/engine/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 6657ec16bd8e9879bc7f3598a10c206964fd17d7 Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Fri, 28 Jun 2013 15:51:58 +0000 Subject: [PATCH 03/62] add /proc to list running processes inside a container Upstream-commit: 2e79719622964eb95eca70372ced0ad6e4dce4f5 Component: engine --- components/engine/api.go | 18 ++++++++++++++++ components/engine/api_params.go | 7 +++++++ components/engine/commands.go | 28 +++++++++++++++++++++++++ components/engine/server.go | 36 ++++++++++++++++++++++++++++++++ components/engine/utils/utils.go | 2 +- 5 files changed, 90 insertions(+), 1 deletion(-) diff --git a/components/engine/api.go b/components/engine/api.go index de4113231c..fb7bbc4614 100644 --- a/components/engine/api.go +++ b/components/engine/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/components/engine/api_params.go b/components/engine/api_params.go index b8af690c7f..89fe180d02 100644 --- a/components/engine/api_params.go +++ b/components/engine/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/components/engine/commands.go b/components/engine/commands.go index 6ada02d552..73f9b949a9 100644 --- a/components/engine/commands.go +++ b/components/engine/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/components/engine/server.go b/components/engine/server.go index cdc01c6b09..7d292fc645 100644 --- a/components/engine/server.go +++ b/components/engine/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/components/engine/utils/utils.go b/components/engine/utils/utils.go index e5b2a0f4fd..2f2a52867e 100644 --- a/components/engine/utils/utils.go +++ b/components/engine/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 5783230d1b8484ff137fb888bd3313619f529602 Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Fri, 28 Jun 2013 18:05:41 +0200 Subject: [PATCH 04/62] Add doc Upstream-commit: 8589fd6db8d52f93ba64a0d72c6eebe8d50530f6 Component: engine --- .../docs/sources/api/docker_remote_api.rst | 5 +++ .../sources/api/docker_remote_api_v1.3.rst | 40 +++++++++++++++++++ .../engine/docs/sources/commandline/cli.rst | 1 + .../docs/sources/commandline/command/proc.rst | 13 ++++++ 4 files changed, 59 insertions(+) create mode 100644 components/engine/docs/sources/commandline/command/proc.rst diff --git a/components/engine/docs/sources/api/docker_remote_api.rst b/components/engine/docs/sources/api/docker_remote_api.rst index c6e50a3f06..f002ac40bc 100644 --- a/components/engine/docs/sources/api/docker_remote_api.rst +++ b/components/engine/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/components/engine/docs/sources/api/docker_remote_api_v1.3.rst b/components/engine/docs/sources/api/docker_remote_api_v1.3.rst index 8eeb010d98..6f7025c449 100644 --- a/components/engine/docs/sources/api/docker_remote_api_v1.3.rst +++ b/components/engine/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/components/engine/docs/sources/commandline/cli.rst b/components/engine/docs/sources/commandline/cli.rst index 118f42f6e8..357423fb93 100644 --- a/components/engine/docs/sources/commandline/cli.rst +++ b/components/engine/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/components/engine/docs/sources/commandline/command/proc.rst b/components/engine/docs/sources/commandline/command/proc.rst new file mode 100644 index 0000000000..b7e9a96ae1 --- /dev/null +++ b/components/engine/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 074020ccddb73c5b8ce904ce648ee0bde3d54c98 Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Fri, 28 Jun 2013 16:27:00 +0000 Subject: [PATCH 05/62] Add test Upstream-commit: 648c4f198b7215a35f31deb9cdf4d370049ef4b2 Component: engine --- components/engine/api_test.go | 55 +++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/components/engine/api_test.go b/components/engine/api_test.go index 4306f74100..f0ce4bf9f7 100644 --- a/components/engine/api_test.go +++ b/components/engine/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 7db2879d66d552f09fe4ad0ac5db9f38be880f1f Mon Sep 17 00:00:00 2001 From: Keli Hu Date: Mon, 1 Jul 2013 15:53:27 +0800 Subject: [PATCH 06/62] Keep debian package up-to-date Upstream-commit: 52cebe19e5323e03ef557102c3dc1fc734fcfd27 Component: engine --- components/engine/packaging/debian/Makefile | 6 ++--- .../packaging/debian/parse_changelog.py | 23 +++++++++++++++++++ 2 files changed, 26 insertions(+), 3 deletions(-) create mode 100755 components/engine/packaging/debian/parse_changelog.py diff --git a/components/engine/packaging/debian/Makefile b/components/engine/packaging/debian/Makefile index 088b4b75dc..1f544b5fe3 100644 --- a/components/engine/packaging/debian/Makefile +++ b/components/engine/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/components/engine/packaging/debian/parse_changelog.py b/components/engine/packaging/debian/parse_changelog.py new file mode 100755 index 0000000000..f14805d659 --- /dev/null +++ b/components/engine/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 ab729772a830ea60556b5ae85e2c3299f852bfdb Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Mon, 1 Jul 2013 15:19:42 +0000 Subject: [PATCH 07/62] change to top Upstream-commit: 11e28842ac74ad0b706d24ec639952f78e9a147c Component: engine --- components/engine/api.go | 6 +++--- components/engine/api_params.go | 2 +- components/engine/api_test.go | 6 +++--- components/engine/commands.go | 10 +++++----- .../engine/docs/sources/api/docker_remote_api.rst | 2 +- .../engine/docs/sources/api/docker_remote_api_v1.3.rst | 4 ++-- components/engine/docs/sources/commandline/cli.rst | 2 +- .../sources/commandline/command/{proc.rst => top.rst} | 8 ++++---- components/engine/server.go | 6 +++--- 9 files changed, 23 insertions(+), 23 deletions(-) rename components/engine/docs/sources/commandline/command/{proc.rst => top.rst} (58%) diff --git a/components/engine/api.go b/components/engine/api.go index fb7bbc4614..f6f54cfa41 100644 --- a/components/engine/api.go +++ b/components/engine/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/components/engine/api_params.go b/components/engine/api_params.go index 89fe180d02..b371ca314f 100644 --- a/components/engine/api_params.go +++ b/components/engine/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/components/engine/api_test.go b/components/engine/api_test.go index f0ce4bf9f7..069a7e5818 100644 --- a/components/engine/api_test.go +++ b/components/engine/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/components/engine/commands.go b/components/engine/commands.go index 73f9b949a9..719326d79e 100644 --- a/components/engine/commands.go +++ b/components/engine/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/components/engine/docs/sources/api/docker_remote_api.rst b/components/engine/docs/sources/api/docker_remote_api.rst index f002ac40bc..ea75ada703 100644 --- a/components/engine/docs/sources/api/docker_remote_api.rst +++ b/components/engine/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/components/engine/docs/sources/api/docker_remote_api_v1.3.rst b/components/engine/docs/sources/api/docker_remote_api_v1.3.rst index 6f7025c449..a56703e6a4 100644 --- a/components/engine/docs/sources/api/docker_remote_api_v1.3.rst +++ b/components/engine/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/components/engine/docs/sources/commandline/cli.rst b/components/engine/docs/sources/commandline/cli.rst index 357423fb93..e499b1f096 100644 --- a/components/engine/docs/sources/commandline/cli.rst +++ b/components/engine/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/components/engine/docs/sources/commandline/command/proc.rst b/components/engine/docs/sources/commandline/command/top.rst similarity index 58% rename from components/engine/docs/sources/commandline/command/proc.rst rename to components/engine/docs/sources/commandline/command/top.rst index b7e9a96ae1..bdd35adcfa 100644 --- a/components/engine/docs/sources/commandline/command/proc.rst +++ b/components/engine/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/components/engine/server.go b/components/engine/server.go index 7d292fc645..b9eba5215b 100644 --- a/components/engine/server.go +++ b/components/engine/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 be9588204f34b538b3b2441f88dd790532e11098 Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Tue, 2 Jul 2013 12:19:25 +0000 Subject: [PATCH 08/62] store hostConfig to /tmp while container is running Upstream-commit: 06b53e3fc7aca2b3dae32edab08c7662d3e9e7e8 Component: engine --- components/engine/container.go | 30 ++++++++++++++++++++++++++++++ components/engine/runtime.go | 4 +++- 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/components/engine/container.go b/components/engine/container.go index 508f39c0ef..f8459ecdc3 100644 --- a/components/engine/container.go +++ b/components/engine/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/components/engine/runtime.go b/components/engine/runtime.go index 06b1f8e1b9..7e728cfe6f 100644 --- a/components/engine/runtime.go +++ b/components/engine/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 c6f9d1d94788b35d59e7d579b25e48ea2ab59ed9 Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Tue, 2 Jul 2013 17:02:42 +0000 Subject: [PATCH 09/62] change file location Upstream-commit: 27a137ccab5990721298cf58aee588ca9b4200ff Component: engine --- components/engine/container.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/engine/container.go b/components/engine/container.go index f8459ecdc3..a2e0c7049c 100644 --- a/components/engine/container.go +++ b/components/engine/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 805a9b96259bc6d0249de7cfa6395e15f8a1bf4e Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Tue, 2 Jul 2013 18:02:16 +0000 Subject: [PATCH 10/62] never remove the file and try to load it in start Upstream-commit: 3042f11666a38ea6b1da206de372bf85463db945 Component: engine --- components/engine/container.go | 10 ++++------ components/engine/runtime.go | 5 +---- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/components/engine/container.go b/components/engine/container.go index a2e0c7049c..7acbc7ba69 100644 --- a/components/engine/container.go +++ b/components/engine/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/components/engine/runtime.go b/components/engine/runtime.go index 7e728cfe6f..d283540abb 100644 --- a/components/engine/runtime.go +++ b/components/engine/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 7935041f18398960af483c8f17f75fc2389e24f7 Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Wed, 3 Jul 2013 17:33:30 -0900 Subject: [PATCH 11/62] Add VOLUME instruction to buildfile Upstream-commit: eb9fef2c424178277cacd29d404cc8e774bb3891 Component: engine --- components/engine/buildfile.go | 21 +++++++++++++++++++ components/engine/buildfile_test.go | 9 ++++++++ .../engine/docs/sources/use/builder.rst | 7 +++++++ components/engine/utils.go | 3 +++ 4 files changed, 40 insertions(+) diff --git a/components/engine/buildfile.go b/components/engine/buildfile.go index a13dc82c55..38e6c330d6 100644 --- a/components/engine/buildfile.go +++ b/components/engine/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/components/engine/buildfile_test.go b/components/engine/buildfile_test.go index 8913284e8f..9f5c692cf2 100644 --- a/components/engine/buildfile_test.go +++ b/components/engine/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/components/engine/docs/sources/use/builder.rst b/components/engine/docs/sources/use/builder.rst index 6a12beadf8..ab416281b4 100644 --- a/components/engine/docs/sources/use/builder.rst +++ b/components/engine/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/components/engine/utils.go b/components/engine/utils.go index 103e762822..caef086289 100644 --- a/components/engine/utils.go +++ b/components/engine/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 8121ba998d19e7ffd55fb63187818e257ffc8ea3 Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Mon, 8 Jul 2013 04:02:06 -0900 Subject: [PATCH 12/62] Add unittest for volume config verification Upstream-commit: 1267e15b0f73f4c40b3a053f0bf02981881a2bdd Component: engine --- components/engine/buildfile_test.go | 37 +++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/components/engine/buildfile_test.go b/components/engine/buildfile_test.go index 9f5c692cf2..89c0916ad0 100644 --- a/components/engine/buildfile_test.go +++ b/components/engine/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 72cf50da581dc1b89325118d04bc38e88bae5f61 Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Wed, 10 Jul 2013 07:03:07 -0900 Subject: [PATCH 13/62] Rebased changes buildfile_test Upstream-commit: 40f1e4edbecc841bd02b3a63b303d522299562c0 Component: engine --- components/engine/buildfile_test.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/components/engine/buildfile_test.go b/components/engine/buildfile_test.go index 89c0916ad0..9250f73765 100644 --- a/components/engine/buildfile_test.go +++ b/components/engine/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 1aa2442604e0ae87f5924f98d5348161296aa27a Mon Sep 17 00:00:00 2001 From: Daniel Mizyrycki Date: Wed, 10 Jul 2013 17:39:00 -0700 Subject: [PATCH 14/62] Packaging, issue #1174: Add pure binary to docker release Upstream-commit: bf26ae03cfe38fc0fdd6f7dedd53041204df0767 Component: engine --- components/engine/Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/components/engine/Makefile b/components/engine/Makefile index 9b06df3d64..8fab579fde 100644 --- a/components/engine/Makefile +++ b/components/engine/Makefile @@ -49,6 +49,7 @@ whichrelease: release: $(BINRELEASE) s3cmd -P put $(BINRELEASE) s3://get.docker.io/builds/`uname -s`/`uname -m`/docker-$(RELEASE_VERSION).tgz s3cmd -P put docker-latest.tgz s3://get.docker.io/builds/`uname -s`/`uname -m`/docker-latest.tgz + s3cmd -P put $(SRCRELEASE)/bin/docker s3://get.docker.io/builds/`uname -s`/`uname -m`/docker srcrelease: $(SRCRELEASE) deps: $(DOCKER_DIR) From de5cf1eef5b40486f559fc43878be4087e14173c Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Thu, 11 Jul 2013 12:21:43 +0000 Subject: [PATCH 15/62] add debug for error in the server Upstream-commit: b7937e268fcbc529a168164fc242edc56d51094c Component: engine --- components/engine/api.go | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/components/engine/api.go b/components/engine/api.go index c4a5222dc6..938af0c40f 100644 --- a/components/engine/api.go +++ b/components/engine/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 750c01d0afc5a20233dc7e84a72b09e66c31902b Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Thu, 11 Jul 2013 17:18:28 +0000 Subject: [PATCH 16/62] wip Upstream-commit: 941e3e2ef09092306a7a287ce62b6fb518af9c56 Component: engine --- components/engine/container.go | 18 ++++++++--- components/engine/runtime.go | 4 +-- components/engine/utils/utils.go | 54 ++++++++++++++++++++++---------- 3 files changed, 53 insertions(+), 23 deletions(-) diff --git a/components/engine/container.go b/components/engine/container.go index 48661a3098..e54440a5ae 100644 --- a/components/engine/container.go +++ b/components/engine/container.go @@ -617,13 +617,21 @@ func (container *Container) Start(hostConfig *HostConfig) error { container.cmd = exec.Command("lxc-start", params...) // Setup logging of stdout and stderr to disk - if err := container.runtime.LogToDisk(container.stdout, container.logPath("stdout")); err != nil { + /* + if err := container.runtime.LogToDisk(container.stdout, container.logPath("stdout"), ""); err != nil { return err } - if err := container.runtime.LogToDisk(container.stderr, container.logPath("stderr")); err != nil { + if err := container.runtime.LogToDisk(container.stderr, container.logPath("stderr"), ""); err != nil { return err } - + */ + if err := container.runtime.LogToDisk(container.stdout, container.logPath("json"), "stdout"); err != nil { + return err + } + if err := container.runtime.LogToDisk(container.stderr, container.logPath("json"), "stderr"); err != nil { + return err + } + var err error if container.Config.Tty { err = container.startPty() @@ -678,13 +686,13 @@ func (container *Container) StdinPipe() (io.WriteCloser, error) { func (container *Container) StdoutPipe() (io.ReadCloser, error) { reader, writer := io.Pipe() - container.stdout.AddWriter(writer) + container.stdout.AddWriter(writer, "") return utils.NewBufReader(reader), nil } func (container *Container) StderrPipe() (io.ReadCloser, error) { reader, writer := io.Pipe() - container.stderr.AddWriter(writer) + container.stderr.AddWriter(writer, "") return utils.NewBufReader(reader), nil } diff --git a/components/engine/runtime.go b/components/engine/runtime.go index 5b0f7b2b2a..d73dd16f70 100644 --- a/components/engine/runtime.go +++ b/components/engine/runtime.go @@ -168,12 +168,12 @@ func (runtime *Runtime) Register(container *Container) error { return nil } -func (runtime *Runtime) LogToDisk(src *utils.WriteBroadcaster, dst string) error { +func (runtime *Runtime) LogToDisk(src *utils.WriteBroadcaster, dst, stream string) error { log, err := os.OpenFile(dst, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0600) if err != nil { return err } - src.AddWriter(log) + src.AddWriter(log, stream) return nil } diff --git a/components/engine/utils/utils.go b/components/engine/utils/utils.go index df615844a7..3139e380a6 100644 --- a/components/engine/utils/utils.go +++ b/components/engine/utils/utils.go @@ -247,30 +247,52 @@ func (r *bufReader) Close() error { type WriteBroadcaster struct { sync.Mutex - writers map[io.WriteCloser]struct{} + writers map[StreamWriter][]byte } -func (w *WriteBroadcaster) AddWriter(writer io.WriteCloser) { +type StreamWriter struct { + wc io.WriteCloser + stream string +} + +func (w *WriteBroadcaster) AddWriter(writer io.WriteCloser, stream string) { w.Lock() - w.writers[writer] = struct{}{} + sw := StreamWriter{wc: writer, stream: stream} + w.writers[sw] = []byte{} w.Unlock() } -// FIXME: Is that function used? -// FIXME: This relies on the concrete writer type used having equality operator -func (w *WriteBroadcaster) RemoveWriter(writer io.WriteCloser) { - w.Lock() - delete(w.writers, writer) - w.Unlock() +type JSONLog struct { + Log string `json:"log,omitempty"` + Stream string `json:"stream,omitempty"` + Created time.Time `json:"time"` } func (w *WriteBroadcaster) Write(p []byte) (n int, err error) { w.Lock() defer w.Unlock() - for writer := range w.writers { - if n, err := writer.Write(p); err != nil || n != len(p) { + for sw := range w.writers { + lp := p + if sw.stream != "" { + w.writers[sw] = append(w.writers[sw], p...) + s := string(p) + if s[len(s)-1] == '\n' { + /* lp, err = json.Marshal(&JSONLog{Log: s, Stream: sw.stream, Created: time.Now()}) + if err != nil { + // On error, evict the writer + delete(w.writers, sw) + continue + } + */ + lp = []byte("[" + time.Now().String() + "] [" + sw.stream + "] " + s) + w.writers[sw] = []byte{} + } else { + continue + } + } + if n, err := sw.wc.Write(lp); err != nil || n != len(lp) { // On error, evict the writer - delete(w.writers, writer) + delete(w.writers, sw) } } return len(p), nil @@ -279,15 +301,15 @@ func (w *WriteBroadcaster) Write(p []byte) (n int, err error) { func (w *WriteBroadcaster) CloseWriters() error { w.Lock() defer w.Unlock() - for writer := range w.writers { - writer.Close() + for sw := range w.writers { + sw.wc.Close() } - w.writers = make(map[io.WriteCloser]struct{}) + w.writers = make(map[StreamWriter][]byte) return nil } func NewWriteBroadcaster() *WriteBroadcaster { - return &WriteBroadcaster{writers: make(map[io.WriteCloser]struct{})} + return &WriteBroadcaster{writers: make(map[StreamWriter][]byte)} } func GetTotalUsedFds() int { From ba109b6d9a30280e82c3cf3f55a8088e73b51710 Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Thu, 11 Jul 2013 19:27:19 +0200 Subject: [PATCH 17/62] fix broken docker port Upstream-commit: affe7caf78288a638df3db37d5cebb4dc7f9ff72 Component: engine --- components/engine/commands.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/components/engine/commands.go b/components/engine/commands.go index feab558259..57c258960d 100644 --- a/components/engine/commands.go +++ b/components/engine/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 5d58cdd260ea09749869680f2eca163d36838263 Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Thu, 11 Jul 2013 20:30:08 +0200 Subject: [PATCH 18/62] change output Upstream-commit: 976428f505b64a51efbd97f71acff5db2f4f5ed0 Component: engine --- components/engine/commands.go | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/components/engine/commands.go b/components/engine/commands.go index 57c258960d..fd401a8c9c 100644 --- a/components/engine/commands.go +++ b/components/engine/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 015f243412c98b55fd6a1b742b814bddc7effe92 Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Thu, 11 Jul 2013 15:12:25 -0900 Subject: [PATCH 19/62] Add verbose output to docker build Verbose output is enabled by default and the flag -q can be used to suppress the verbose output. Upstream-commit: 474191dd7bca9eedaccb9de1771eecfce7dfebbb Component: engine --- components/engine/api.go | 9 ++++++++- components/engine/buildfile.go | 11 ++++++++++- components/engine/buildfile_test.go | 2 +- components/engine/commands.go | 6 ++++++ .../engine/docs/sources/commandline/command/build.rst | 1 + 5 files changed, 26 insertions(+), 3 deletions(-) diff --git a/components/engine/api.go b/components/engine/api.go index c4a5222dc6..9e51f4de77 100644 --- a/components/engine/api.go +++ b/components/engine/api.go @@ -756,6 +756,7 @@ func postBuild(srv *Server, version float64, w http.ResponseWriter, r *http.Requ } remoteURL := r.FormValue("remote") repoName := r.FormValue("t") + rawSuppressOutput := r.FormValue("q") tag := "" if strings.Contains(repoName, ":") { remoteParts := strings.Split(repoName, ":") @@ -802,7 +803,13 @@ func postBuild(srv *Server, version float64, w http.ResponseWriter, r *http.Requ } context = c } - b := NewBuildFile(srv, utils.NewWriteFlusher(w)) + + suppressOutput, err := getBoolParam(rawSuppressOutput) + if err != nil { + return err + } + + b := NewBuildFile(srv, utils.NewWriteFlusher(w), !suppressOutput) id, err := b.Build(context) if err != nil { fmt.Fprintf(w, "Error build: %s\n", err) diff --git a/components/engine/buildfile.go b/components/engine/buildfile.go index 38e6c330d6..02ef00a854 100644 --- a/components/engine/buildfile.go +++ b/components/engine/buildfile.go @@ -28,6 +28,7 @@ type buildFile struct { maintainer string config *Config context string + verbose bool lastContainer *Container tmpContainers map[string]struct{} @@ -303,6 +304,13 @@ func (b *buildFile) run() (string, error) { return "", err } + if b.verbose { + err = <-c.Attach(nil, nil, b.out, b.out) + if err != nil { + return "", err + } + } + // Wait for it to finish if ret := c.Wait(); ret != 0 { return "", fmt.Errorf("The command %v returned a non-zero code: %d", b.config.Cmd, ret) @@ -450,7 +458,7 @@ func (b *buildFile) Build(context io.Reader) (string, error) { return "", fmt.Errorf("An error occured during the build\n") } -func NewBuildFile(srv *Server, out io.Writer) BuildFile { +func NewBuildFile(srv *Server, out io.Writer, verbose bool) BuildFile { return &buildFile{ builder: NewBuilder(srv.runtime), runtime: srv.runtime, @@ -459,5 +467,6 @@ func NewBuildFile(srv *Server, out io.Writer) BuildFile { out: out, tmpContainers: make(map[string]struct{}), tmpImages: make(map[string]struct{}), + verbose: verbose, } } diff --git a/components/engine/buildfile_test.go b/components/engine/buildfile_test.go index 9250f73765..0bd5a3d1c4 100644 --- a/components/engine/buildfile_test.go +++ b/components/engine/buildfile_test.go @@ -117,7 +117,7 @@ func TestBuild(t *testing.T) { pushingPool: make(map[string]struct{}), } - buildfile := NewBuildFile(srv, ioutil.Discard) + buildfile := NewBuildFile(srv, ioutil.Discard, false) if _, err := buildfile.Build(mkTestContext(ctx.dockerfile, ctx.files, t)); err != nil { t.Fatal(err) } diff --git a/components/engine/commands.go b/components/engine/commands.go index feab558259..4a69fb9108 100644 --- a/components/engine/commands.go +++ b/components/engine/commands.go @@ -157,6 +157,8 @@ func mkBuildContext(dockerfile string, files [][2]string) (Archive, error) { func (cli *DockerCli) CmdBuild(args ...string) error { cmd := Subcmd("build", "[OPTIONS] PATH | URL | -", "Build a new container image from the source code at PATH") tag := cmd.String("t", "", "Tag to be applied to the resulting image in case of success") + suppressOutput := cmd.Bool("q", false, "Suppress verbose build output") + if err := cmd.Parse(args); err != nil { return nil } @@ -194,6 +196,10 @@ func (cli *DockerCli) CmdBuild(args ...string) error { // Upload the build context v := &url.Values{} v.Set("t", *tag) + + if *suppressOutput { + v.Set("q", "1") + } if isRemote { v.Set("remote", cmd.Arg(0)) } diff --git a/components/engine/docs/sources/commandline/command/build.rst b/components/engine/docs/sources/commandline/command/build.rst index 1645002ba2..45b6d2ec8e 100644 --- a/components/engine/docs/sources/commandline/command/build.rst +++ b/components/engine/docs/sources/commandline/command/build.rst @@ -11,6 +11,7 @@ Usage: docker build [OPTIONS] PATH | URL | - Build a new container image from the source code at PATH -t="": Tag to be applied to the resulting image in case of success. + -q=false: Suppress verbose build output. When a single Dockerfile is given as URL, then no context is set. When a git repository is set as URL, the repository is used as context From 725d76d0d986047e7fd77b1a116f5cbe6e6b3ce7 Mon Sep 17 00:00:00 2001 From: "Guillaume J. Charmes" Date: Thu, 11 Jul 2013 17:31:07 -0700 Subject: [PATCH 20/62] Hotfix: check the length of entrypoint before comparing. Upstream-commit: 71d2ff494694d7f18310c7994daa34dce33af98b Component: engine --- components/engine/utils.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/components/engine/utils.go b/components/engine/utils.go index caef086289..d55b1ff0bc 100644 --- a/components/engine/utils.go +++ b/components/engine/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 7bf5a45cfe77c9237723e1a4fccaff0a4e461fc4 Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Thu, 11 Jul 2013 15:37:26 -0900 Subject: [PATCH 21/62] Fix buildfile tests after rebase Upstream-commit: 49044a96089487b9df075fa972e83e4c05c7fae8 Component: engine --- components/engine/buildfile_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/engine/buildfile_test.go b/components/engine/buildfile_test.go index 0bd5a3d1c4..514d2f89dc 100644 --- a/components/engine/buildfile_test.go +++ b/components/engine/buildfile_test.go @@ -137,7 +137,7 @@ func TestVolume(t *testing.T) { pushingPool: make(map[string]struct{}), } - buildfile := NewBuildFile(srv, ioutil.Discard) + buildfile := NewBuildFile(srv, ioutil.Discard, false) imgId, err := buildfile.Build(mkTestContext(` from %s VOLUME /test @@ -153,7 +153,7 @@ CMD Hello world if len(img.Config.Volumes) == 0 { t.Fail() } - for key, _ := range img.Config.Volumes { + for key := range img.Config.Volumes { if key != "/test" { t.Fail() } From 89646a08c959d6720c4d7e6f92490179f5fe464d Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Thu, 11 Jul 2013 15:52:08 -0900 Subject: [PATCH 22/62] Revert changes from PR 1030 With streaming output of the build changes in 1030 are no longer required. Upstream-commit: 1104d443cc49fd2a6b9c94a2c9724468f9860799 Component: engine --- components/engine/buildfile.go | 28 ---------------------------- 1 file changed, 28 deletions(-) diff --git a/components/engine/buildfile.go b/components/engine/buildfile.go index 02ef00a854..7ade058c69 100644 --- a/components/engine/buildfile.go +++ b/components/engine/buildfile.go @@ -30,7 +30,6 @@ type buildFile struct { context string verbose bool - lastContainer *Container tmpContainers map[string]struct{} tmpImages map[string]struct{} @@ -255,7 +254,6 @@ func (b *buildFile) CmdAdd(args string) error { return err } b.tmpContainers[container.ID] = struct{}{} - b.lastContainer = container if err := container.EnsureMounted(); err != nil { return err @@ -291,7 +289,6 @@ func (b *buildFile) run() (string, error) { return "", err } b.tmpContainers[c.ID] = struct{}{} - b.lastContainer = c fmt.Fprintf(b.out, " ---> Running in %s\n", utils.TruncateID(c.ID)) // override the entry point that may have been picked up from the base image @@ -345,7 +342,6 @@ func (b *buildFile) commit(id string, autoCmd []string, comment string) error { return err } b.tmpContainers[container.ID] = struct{}{} - b.lastContainer = container fmt.Fprintf(b.out, " ---> Running in %s\n", utils.TruncateID(container.ID)) id = container.ID if err := container.EnsureMounted(); err != nil { @@ -373,29 +369,6 @@ func (b *buildFile) commit(id string, autoCmd []string, comment string) error { } func (b *buildFile) Build(context io.Reader) (string, error) { - defer func() { - // If we have an error and a container, the display the logs - if b.lastContainer != nil { - fmt.Fprintf(b.out, "******** Logs from last container (%s) *******\n", b.lastContainer.ShortID()) - - cLog, err := b.lastContainer.ReadLog("stdout") - if err != nil { - utils.Debugf("Error reading logs (stdout): %s", err) - } - if _, err := io.Copy(b.out, cLog); err != nil { - utils.Debugf("Error streaming logs (stdout): %s", err) - } - cLog, err = b.lastContainer.ReadLog("stderr") - if err != nil { - utils.Debugf("Error reading logs (stderr): %s", err) - } - if _, err := io.Copy(b.out, cLog); err != nil { - utils.Debugf("Error streaming logs (stderr): %s", err) - } - fmt.Fprintf(b.out, "************* End of logs for %s *************\n", b.lastContainer.ShortID()) - } - }() - // FIXME: @creack any reason for using /tmp instead of ""? // FIXME: @creack "name" is a terrible variable name name, err := ioutil.TempDir("/tmp", "docker-build") @@ -448,7 +421,6 @@ func (b *buildFile) Build(context io.Reader) (string, error) { return "", ret.(error) } - b.lastContainer = nil fmt.Fprintf(b.out, " ---> %v\n", utils.TruncateID(b.image)) } if b.image != "" { From 6a215f8175ce2aa9cddb6fb3a836d21dae715e7c Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Thu, 11 Jul 2013 16:41:19 -0900 Subject: [PATCH 23/62] Fix Docker Builder documentation numbering Upstream-commit: 90483dc9123d6cb9eb1aa1bf4ba4e851d751784e Component: engine --- .../engine/docs/sources/use/builder.rst | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/components/engine/docs/sources/use/builder.rst b/components/engine/docs/sources/use/builder.rst index ab416281b4..ceda9c99dd 100644 --- a/components/engine/docs/sources/use/builder.rst +++ b/components/engine/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 ac70e7d7dbdde17e11b4de006bc663a36cc8f655 Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Fri, 12 Jul 2013 11:54:53 +0000 Subject: [PATCH 24/62] fix tests regarding the new test image Upstream-commit: a8a6848ce0c3c54d00cfcd85727546e54a4dcf7e Component: engine --- components/engine/api_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/components/engine/api_test.go b/components/engine/api_test.go index 2ef043acbe..757004b26b 100644 --- a/components/engine/api_test.go +++ b/components/engine/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 3442b5e984e38f6d8005ccfb7706ffb264490c67 Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Fri, 12 Jul 2013 06:22:56 -0900 Subject: [PATCH 25/62] Add param to api docs for verbose build output Upstream-commit: d0c73c28df5627bc7158fd54860f6751b4dab0f9 Component: engine --- components/engine/docs/sources/api/docker_remote_api_v1.3.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/components/engine/docs/sources/api/docker_remote_api_v1.3.rst b/components/engine/docs/sources/api/docker_remote_api_v1.3.rst index b0955ce496..7f7c7db8a8 100644 --- a/components/engine/docs/sources/api/docker_remote_api_v1.3.rst +++ b/components/engine/docs/sources/api/docker_remote_api_v1.3.rst @@ -881,6 +881,7 @@ Build an image from Dockerfile via stdin The Content-type header should be set to "application/tar". :query t: tag to be applied to the resulting image in case of success + :query q: suppress verbose build output :statuscode 200: no error :statuscode 500: server error From c8bc9a992cfcd82d58afb18d828556025c76a6b1 Mon Sep 17 00:00:00 2001 From: Sam Alba Date: Fri, 12 Jul 2013 10:42:54 -0700 Subject: [PATCH 26/62] Fixed tag option for "docker pull" (the option was ignored) Upstream-commit: cd0fef633c5d871b192146e405ca7c5bebb2f3ba Component: engine --- components/engine/commands.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/components/engine/commands.go b/components/engine/commands.go index b3b1a57199..b581590bc2 100644 --- a/components/engine/commands.go +++ b/components/engine/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 014efc77a70a015bc6b143f09cebac882c8c3d95 Mon Sep 17 00:00:00 2001 From: "Guillaume J. Charmes" Date: Mon, 1 Jul 2013 16:48:59 -0700 Subject: [PATCH 27/62] Remove the os.user dependency and manually lookup /etc/passwd instead Upstream-commit: eb38750d99d4f9706cb2abcb861c84e1d309bd40 Component: engine --- components/engine/sysinit.go | 7 ++----- components/engine/utils/utils.go | 21 +++++++++++++++++++++ 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/components/engine/sysinit.go b/components/engine/sysinit.go index 622dbdf095..fb36cd2543 100644 --- a/components/engine/sysinit.go +++ b/components/engine/sysinit.go @@ -3,10 +3,10 @@ package docker import ( "flag" "fmt" + "github.com/dotcloud/docker/utils" "log" "os" "os/exec" - "os/user" "strconv" "strings" "syscall" @@ -27,10 +27,7 @@ func changeUser(u string) { if u == "" { return } - userent, err := user.LookupId(u) - if err != nil { - userent, err = user.Lookup(u) - } + userent, err := utils.UserLookup(u) if err != nil { log.Fatalf("Unable to find user %v: %v", u, err) } diff --git a/components/engine/utils/utils.go b/components/engine/utils/utils.go index df615844a7..ea5c08e60e 100644 --- a/components/engine/utils/utils.go +++ b/components/engine/utils/utils.go @@ -14,6 +14,7 @@ import ( "net/http" "os" "os/exec" + "os/user" "path/filepath" "runtime" "strconv" @@ -700,3 +701,23 @@ func ParseRepositoryTag(repos string) (string, string) { } return repos, "" } + +func UserLookup(uid string) (*user.User, error) { + file, err := ioutil.ReadFile("/etc/passwd") + if err != nil { + return nil, err + } + for _, line := range strings.Split(string(file), "\n") { + data := strings.Split(line, ":") + if len(data) > 5 && (data[0] == uid || data[2] == uid) { + return &user.User{ + Uid: data[2], + Gid: data[3], + Username: data[0], + Name: data[4], + HomeDir: data[5], + }, nil + } + } + return nil, fmt.Errorf("User not found in /etc/passwd") +} From f649b0a0574b3d7ce1a15d6177a35ed5b9000888 Mon Sep 17 00:00:00 2001 From: Ken Cochrane Date: Fri, 12 Jul 2013 13:55:26 -0400 Subject: [PATCH 28/62] updated the help commands on a few commands that were not correct Upstream-commit: 4174e7aa7a6600a2cfedd1c568000556cf1daf79 Component: engine --- components/engine/commands.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/components/engine/commands.go b/components/engine/commands.go index b3b1a57199..20195e2e39 100644 --- a/components/engine/commands.go +++ b/components/engine/commands.go @@ -469,7 +469,7 @@ func (cli *DockerCli) CmdInfo(args ...string) error { func (cli *DockerCli) CmdStop(args ...string) error { cmd := Subcmd("stop", "[OPTIONS] CONTAINER [CONTAINER...]", "Stop a running container") - nSeconds := cmd.Int("t", 10, "wait t seconds before killing the container") + nSeconds := cmd.Int("t", 10, "wait t seconds before stopping the container") if err := cmd.Parse(args); err != nil { return nil } @@ -494,7 +494,7 @@ func (cli *DockerCli) CmdStop(args ...string) error { func (cli *DockerCli) CmdRestart(args ...string) error { cmd := Subcmd("restart", "[OPTIONS] CONTAINER [CONTAINER...]", "Restart a running container") - nSeconds := cmd.Int("t", 10, "wait t seconds before killing the container") + nSeconds := cmd.Int("t", 10, "wait t seconds before restarting the container") if err := cmd.Parse(args); err != nil { return nil } @@ -773,7 +773,7 @@ func (cli *DockerCli) CmdImport(args ...string) error { } func (cli *DockerCli) CmdPush(args ...string) error { - cmd := Subcmd("push", "[OPTION] NAME", "Push an image or a repository to the registry") + cmd := Subcmd("push", "NAME", "Push an image or a repository to the registry") if err := cmd.Parse(args); err != nil { return nil } From aa8559ee188aaba02cdb1e0227027180d4d0fe12 Mon Sep 17 00:00:00 2001 From: Ken Cochrane Date: Fri, 12 Jul 2013 14:05:26 -0400 Subject: [PATCH 29/62] updated the rmi command docs, the had typos Upstream-commit: 364f48d6c7386874ee3fa0696b7a466f76fcf698 Component: engine --- components/engine/docs/sources/commandline/command/rmi.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/engine/docs/sources/commandline/command/rmi.rst b/components/engine/docs/sources/commandline/command/rmi.rst index a0131886d6..954e5222c6 100644 --- a/components/engine/docs/sources/commandline/command/rmi.rst +++ b/components/engine/docs/sources/commandline/command/rmi.rst @@ -8,6 +8,6 @@ :: - Usage: docker rmimage [OPTIONS] IMAGE + Usage: docker rmi IMAGE [IMAGE...] - Remove an image + Remove one or more images From 23db3e215bf5981467a29b211a1a6dbffebca8a9 Mon Sep 17 00:00:00 2001 From: Marcus Farkas Date: Fri, 12 Jul 2013 20:08:45 +0200 Subject: [PATCH 30/62] Revert "Client: better progressbar output" This reverts commit 3ac68f1966222d9a1c0ff867515e3f5c2f83e422. Upstream-commit: a6e5a397bd6b7967a91f1d7cbe581e46211964da Component: engine --- components/engine/utils/utils.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/engine/utils/utils.go b/components/engine/utils/utils.go index df615844a7..6aa121c9e3 100644 --- a/components/engine/utils/utils.go +++ b/components/engine/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 51c31b8a66e8f97a2cf09b32eabe8531cd31a460 Mon Sep 17 00:00:00 2001 From: Marcus Farkas Date: Fri, 12 Jul 2013 20:15:25 +0200 Subject: [PATCH 31/62] *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. Upstream-commit: bac5772312d1fe733511febd117e9f29ff19c698 Component: engine --- components/engine/server.go | 8 ++++---- components/engine/utils/utils.go | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/components/engine/server.go b/components/engine/server.go index 193f9e5778..c43cae0c38 100644 --- a/components/engine/server.go +++ b/components/engine/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/components/engine/utils/utils.go b/components/engine/utils/utils.go index 6aa121c9e3..153b1b910c 100644 --- a/components/engine/utils/utils.go +++ b/components/engine/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 969d894058dd5f1c01974c5ed50f868170664046 Mon Sep 17 00:00:00 2001 From: Daniel Mizyrycki Date: Fri, 12 Jul 2013 15:06:02 -0700 Subject: [PATCH 32/62] Packaging, issue #1202: Upgrade vagrantfile go in debian packaging Upstream-commit: c7a48e91d8d31a8b80b46f4cb17d866e36147ddc Component: engine --- components/engine/packaging/debian/Vagrantfile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/components/engine/packaging/debian/Vagrantfile b/components/engine/packaging/debian/Vagrantfile index 5913882f9f..cb84ed5b8d 100644 --- a/components/engine/packaging/debian/Vagrantfile +++ b/components/engine/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 54ca7a355c213e9d4c5cea9061ae4c152e5dcb47 Mon Sep 17 00:00:00 2001 From: Nick Stenning Date: Fri, 28 Jun 2013 17:06:00 +0100 Subject: [PATCH 33/62] 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. Upstream-commit: 44b3e8d51b655d68d0a253c48c027360ff8c3a97 Component: engine --- components/engine/tags.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/components/engine/tags.go b/components/engine/tags.go index 9ad9d10d05..21c13bdfb2 100644 --- a/components/engine/tags.go +++ b/components/engine/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 } From 6fdf5346ea20f91606ac72b0d750f33d247f7836 Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Sun, 14 Jul 2013 18:23:20 -0900 Subject: [PATCH 34/62] Copy VolumesRW values when using --volumes-from Fixes #1201 Upstream-commit: 5ae8c7a98592f83a31f3f45fc22728e45e95626c Component: engine --- components/engine/container.go | 3 ++ components/engine/container_test.go | 54 +++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+) diff --git a/components/engine/container.go b/components/engine/container.go index 2791289269..c714e9f0e1 100644 --- a/components/engine/container.go +++ b/components/engine/container.go @@ -597,6 +597,9 @@ func (container *Container) Start(hostConfig *HostConfig) error { return nil } container.Volumes[volPath] = id + if isRW, exists := c.VolumesRW[volPath]; exists { + container.VolumesRW[volPath] = isRW + } } } diff --git a/components/engine/container_test.go b/components/engine/container_test.go index e7f6818eb6..3402d39840 100644 --- a/components/engine/container_test.go +++ b/components/engine/container_test.go @@ -1229,3 +1229,57 @@ func TestBindMounts(t *testing.T) { } } + +// Test that VolumesRW values are copied to the new container. Regression test for #1201 +func TestVolumesFromReadonlyMount(t *testing.T) { + runtime := mkRuntime(t) + defer nuke(runtime) + container, err := NewBuilder(runtime).Create( + &Config{ + Image: GetTestImage(runtime).ID, + Cmd: []string{"/bin/echo", "-n", "foobar"}, + Volumes: map[string]struct{}{"/test": {}}, + }, + ) + if err != nil { + t.Fatal(err) + } + defer runtime.Destroy(container) + _, err = container.Output() + if err != nil { + t.Fatal(err) + } + if !container.VolumesRW["/test"] { + t.Fail() + } + + container2, err := NewBuilder(runtime).Create( + &Config{ + Image: GetTestImage(runtime).ID, + Cmd: []string{"/bin/echo", "-n", "foobar"}, + VolumesFrom: container.ID, + }, + ) + if err != nil { + t.Fatal(err) + } + defer runtime.Destroy(container2) + + _, err = container2.Output() + if err != nil { + t.Fatal(err) + } + + if container.Volumes["/test"] != container2.Volumes["/test"] { + t.Fail() + } + + actual, exists := container2.VolumesRW["/test"] + if !exists { + t.Fail() + } + + if container.VolumesRW["/test"] != actual { + t.Fail() + } +} From c3b2bff3758d26160df0a9786f2c79aad5651ab7 Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Mon, 15 Jul 2013 13:12:33 +0000 Subject: [PATCH 35/62] improve mergeconfig, if dns, portspec, env or volumes specify in docker run, apend and not replace Upstream-commit: 193a7e1dc1945bc9988b6205c63db9d2e0578c55 Component: engine --- components/engine/utils.go | 39 +++++++++++++++++++++ components/engine/utils_test.go | 60 ++++++++++++++++++++++++++++++++- 2 files changed, 98 insertions(+), 1 deletion(-) diff --git a/components/engine/utils.go b/components/engine/utils.go index d55b1ff0bc..33cfbe506f 100644 --- a/components/engine/utils.go +++ b/components/engine/utils.go @@ -1,5 +1,9 @@ package docker +import ( + "strings" +) + // Compare two Config struct. Do not compare the "Image" nor "Hostname" fields // If OpenStdin is set, then it differs func CompareConfig(a, b *Config) bool { @@ -68,6 +72,20 @@ func MergeConfig(userConf, imageConf *Config) { } if userConf.PortSpecs == nil || len(userConf.PortSpecs) == 0 { userConf.PortSpecs = imageConf.PortSpecs + } else { + for _, imagePortSpec := range imageConf.PortSpecs { + found := false + imageNat, _ := parseNat(imagePortSpec) + for _, userPortSpec := range userConf.PortSpecs { + userNat, _ := parseNat(userPortSpec) + if imageNat.Proto == userNat.Proto && imageNat.Frontend == userNat.Frontend { + found = true + } + } + if !found { + userConf.PortSpecs = append(userConf.PortSpecs, imagePortSpec) + } + } } if !userConf.Tty { userConf.Tty = imageConf.Tty @@ -80,17 +98,38 @@ func MergeConfig(userConf, imageConf *Config) { } if userConf.Env == nil || len(userConf.Env) == 0 { userConf.Env = imageConf.Env + } else { + for _, imageEnv := range imageConf.Env { + found := false + imageEnvKey := strings.Split(imageEnv, "=")[0] + for _, userEnv := range userConf.Env { + userEnvKey := strings.Split(userEnv, "=")[0] + if imageEnvKey == userEnvKey { + found = true + } + } + if !found { + userConf.Env = append(userConf.Env, imageEnv) + } + } } if userConf.Cmd == nil || len(userConf.Cmd) == 0 { userConf.Cmd = imageConf.Cmd } if userConf.Dns == nil || len(userConf.Dns) == 0 { userConf.Dns = imageConf.Dns + } else { + //duplicates aren't an issue here + userConf.Dns = append(userConf.Dns, imageConf.Dns...) } if userConf.Entrypoint == nil || len(userConf.Entrypoint) == 0 { userConf.Entrypoint = imageConf.Entrypoint } if userConf.Volumes == nil || len(userConf.Volumes) == 0 { userConf.Volumes = imageConf.Volumes + } else { + for k, v := range imageConf.Volumes { + userConf.Volumes[k] = v + } } } diff --git a/components/engine/utils_test.go b/components/engine/utils_test.go index 4951d3a02d..af2dcc88c5 100644 --- a/components/engine/utils_test.go +++ b/components/engine/utils_test.go @@ -1,13 +1,13 @@ package docker import ( + "github.com/dotcloud/docker/utils" "io" "io/ioutil" "os" "path" "strings" "testing" - "github.com/dotcloud/docker/utils" ) // This file contains utility functions for docker's unit test suite. @@ -128,3 +128,61 @@ func runContainer(r *Runtime, args []string, t *testing.T) (output string, err e output = string(data) return } + +func TestMergeConfig(t *testing.T) { + volumesImage := make(map[string]struct{}) + volumesImage["/test1"] = struct{}{} + volumesImage["/test2"] = struct{}{} + configImage := &Config{ + Dns: []string{"1.1.1.1", "2.2.2.2"}, + PortSpecs: []string{"1111:1111", "2222:2222"}, + Env: []string{"VAR1=1", "VAR2=2"}, + Volumes: volumesImage, + } + + volumesUser := make(map[string]struct{}) + volumesUser["/test3"] = struct{}{} + configUser := &Config{ + Dns: []string{"3.3.3.3"}, + PortSpecs: []string{"2222:3333", "3333:3333"}, + Env: []string{"VAR2=3", "VAR3=3"}, + Volumes: volumesUser, + } + + MergeConfig(configUser, configImage) + + if len(configUser.Dns) != 3 { + t.Fatalf("Expected 3 dns, 1.1.1.1, 2.2.2.2 and 3.3.3.3, found %d", len(configUser.Dns)) + } + for _, dns := range configUser.Dns { + if dns != "1.1.1.1" && dns != "2.2.2.2" && dns != "3.3.3.3" { + t.Fatalf("Expected 1.1.1.1 or 2.2.2.2 or 3.3.3.3, found %s", dns) + } + } + + if len(configUser.PortSpecs) != 3 { + t.Fatalf("Expected 3 portSpecs, 1111:1111, 2222:3333 and 3333:3333, found %d", len(configUser.PortSpecs)) + } + for _, portSpecs := range configUser.PortSpecs { + if portSpecs != "1111:1111" && portSpecs != "2222:3333" && portSpecs != "3333:3333" { + t.Fatalf("Expected 1111:1111 or 2222:3333 or 3333:3333, found %s", portSpecs) + } + } + if len(configUser.Env) != 3 { + t.Fatalf("Expected 3 env var, VAR1=1, VAR2=3 and VAR3=3, found %d", len(configUser.Env)) + } + for _, env := range configUser.Env { + if env != "VAR1=1" && env != "VAR2=3" && env != "VAR3=3" { + t.Fatalf("Expected VAR1=1 or VAR2=3 or VAR3=3, found %s", env) + } + } + + if len(configUser.Volumes) != 3 { + t.Fatalf("Expected 3 volumes, /test1, /test2 and /test3, found %d", len(configUser.Volumes)) + } + for v, _ := range configUser.Volumes { + if v != "/test1" && v != "/test2" && v != "/test3" { + t.Fatalf("Expected /test1 or /test2 or /test3, found %s", v) + } + } +} From 80066854b8cbb39027349d91f8eb708e5ec820c5 Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Mon, 15 Jul 2013 16:17:58 +0000 Subject: [PATCH 36/62] store both logs in a same file, as JSON Upstream-commit: 599f85d4e4362f24dc2850c71a689671122c456b Component: engine --- components/engine/commands_test.go | 12 +++++------ components/engine/container.go | 10 +-------- components/engine/server.go | 28 ++++++++++++------------ components/engine/utils/utils.go | 34 ++++++++++++++++-------------- 4 files changed, 40 insertions(+), 44 deletions(-) diff --git a/components/engine/commands_test.go b/components/engine/commands_test.go index 3f4c53db03..233c6337d4 100644 --- a/components/engine/commands_test.go +++ b/components/engine/commands_test.go @@ -59,7 +59,6 @@ func assertPipe(input, output string, r io.Reader, w io.Writer, count int) error return nil } - // TestRunHostname checks that 'docker run -h' correctly sets a custom hostname func TestRunHostname(t *testing.T) { stdout, stdoutPipe := io.Pipe() @@ -91,7 +90,6 @@ func TestRunHostname(t *testing.T) { } - // 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. @@ -144,15 +142,17 @@ func TestRunAttachStdin(t *testing.T) { }) // Check logs - if cmdLogs, err := container.ReadLog("stdout"); err != nil { + if cmdLogs, err := container.ReadLog("json"); err != nil { t.Fatal(err) } else { if output, err := ioutil.ReadAll(cmdLogs); err != nil { t.Fatal(err) } else { - expectedLog := "hello\nhi there\n" - if string(output) != expectedLog { - t.Fatalf("Unexpected logs: should be '%s', not '%s'\n", expectedLog, output) + expectedLogs := []string{"{\"log\":\"hello\\n\",\"stream\":\"stdout\"", "{\"log\":\"hi there\\n\",\"stream\":\"stdout\""} + for _, expectedLog := range expectedLogs { + if !strings.Contains(string(output), expectedLog) { + t.Fatalf("Unexpected logs: should contains '%s', it is not '%s'\n", expectedLog, output) + } } } } diff --git a/components/engine/container.go b/components/engine/container.go index 7b0070094a..1011f7a6e3 100644 --- a/components/engine/container.go +++ b/components/engine/container.go @@ -640,21 +640,13 @@ func (container *Container) Start(hostConfig *HostConfig) error { container.cmd = exec.Command("lxc-start", params...) // Setup logging of stdout and stderr to disk - /* - if err := container.runtime.LogToDisk(container.stdout, container.logPath("stdout"), ""); err != nil { - return err - } - if err := container.runtime.LogToDisk(container.stderr, container.logPath("stderr"), ""); err != nil { - return err - } - */ if err := container.runtime.LogToDisk(container.stdout, container.logPath("json"), "stdout"); err != nil { return err } if err := container.runtime.LogToDisk(container.stderr, container.logPath("json"), "stderr"); err != nil { return err } - + var err error if container.Config.Tty { err = container.startPty() diff --git a/components/engine/server.go b/components/engine/server.go index c43cae0c38..6129e3eb95 100644 --- a/components/engine/server.go +++ b/components/engine/server.go @@ -2,6 +2,7 @@ package docker import ( "bufio" + "encoding/json" "errors" "fmt" "github.com/dotcloud/docker/auth" @@ -1042,20 +1043,21 @@ func (srv *Server) ContainerAttach(name string, logs, stream, stdin, stdout, std } //logs if logs { - if stdout { - cLog, err := container.ReadLog("stdout") - if err != nil { - utils.Debugf("Error reading logs (stdout): %s", err) - } else if _, err := io.Copy(out, cLog); err != nil { - utils.Debugf("Error streaming logs (stdout): %s", err) - } + cLog, err := container.ReadLog("json") + if err != nil { + utils.Debugf("Error reading logs (json): %s", err) } - if stderr { - cLog, err := container.ReadLog("stderr") - if err != nil { - utils.Debugf("Error reading logs (stderr): %s", err) - } else if _, err := io.Copy(out, cLog); err != nil { - utils.Debugf("Error streaming logs (stderr): %s", err) + dec := json.NewDecoder(cLog) + for { + var l utils.JSONLog + if err := dec.Decode(&l); err == io.EOF { + break + } else if err != nil { + utils.Debugf("Error streaming logs: %s", err) + break + } + if (l.Stream == "stdout" && stdout) || (l.Stream == "stderr" && stderr) { + fmt.Fprintf(out, "%s", l.Log) } } } diff --git a/components/engine/utils/utils.go b/components/engine/utils/utils.go index 3ec853bf68..9e6f0c9c0d 100644 --- a/components/engine/utils/utils.go +++ b/components/engine/utils/utils.go @@ -247,47 +247,49 @@ func (r *bufReader) Close() error { type WriteBroadcaster struct { sync.Mutex - writers map[StreamWriter][]byte + buf *bytes.Buffer + writers map[StreamWriter]bool } type StreamWriter struct { - wc io.WriteCloser + wc io.WriteCloser stream string } func (w *WriteBroadcaster) AddWriter(writer io.WriteCloser, stream string) { w.Lock() sw := StreamWriter{wc: writer, stream: stream} - w.writers[sw] = []byte{} + w.writers[sw] = true w.Unlock() } type JSONLog struct { - Log string `json:"log,omitempty"` - Stream string `json:"stream,omitempty"` + Log string `json:"log,omitempty"` + Stream string `json:"stream,omitempty"` Created time.Time `json:"time"` } func (w *WriteBroadcaster) Write(p []byte) (n int, err error) { w.Lock() defer w.Unlock() + w.buf.Write(p) for sw := range w.writers { lp := p if sw.stream != "" { - w.writers[sw] = append(w.writers[sw], p...) - s := string(p) - if s[len(s)-1] == '\n' { - /* lp, err = json.Marshal(&JSONLog{Log: s, Stream: sw.stream, Created: time.Now()}) + lp = nil + for { + line, err := w.buf.ReadString('\n') + if err != nil { + w.buf.Write([]byte(line)) + break + } + b, err := json.Marshal(&JSONLog{Log: line, Stream: sw.stream, Created: time.Now()}) if err != nil { // On error, evict the writer delete(w.writers, sw) continue } - */ - lp = []byte("[" + time.Now().String() + "] [" + sw.stream + "] " + s) - w.writers[sw] = []byte{} - } else { - continue + lp = append(lp, b...) } } if n, err := sw.wc.Write(lp); err != nil || n != len(lp) { @@ -304,12 +306,12 @@ func (w *WriteBroadcaster) CloseWriters() error { for sw := range w.writers { sw.wc.Close() } - w.writers = make(map[StreamWriter][]byte) + w.writers = make(map[StreamWriter]bool) return nil } func NewWriteBroadcaster() *WriteBroadcaster { - return &WriteBroadcaster{writers: make(map[StreamWriter][]byte)} + return &WriteBroadcaster{writers: make(map[StreamWriter]bool), buf: bytes.NewBuffer(nil)} } func GetTotalUsedFds() int { From 08204dd54e889b398b1f2b15a18f2f3cdf39ee4b Mon Sep 17 00:00:00 2001 From: "Guillaume J. Charmes" Date: Mon, 1 Jul 2013 17:08:42 -0700 Subject: [PATCH 37/62] Add unit test to check wrong uid case Upstream-commit: e41507bde2d87cb9bbb0c328e414a39354dae10e Component: engine --- components/engine/container_test.go | 17 +++++++++++++++++ components/engine/utils/utils.go | 3 +++ 2 files changed, 20 insertions(+) diff --git a/components/engine/container_test.go b/components/engine/container_test.go index 7646bb3793..b4e6551a34 100644 --- a/components/engine/container_test.go +++ b/components/engine/container_test.go @@ -849,6 +849,23 @@ func TestUser(t *testing.T) { if !strings.Contains(string(output), "uid=1(daemon) gid=1(daemon)") { t.Error(string(output)) } + + // Test an wrong username + container, err = builder.Create(&Config{ + Image: GetTestImage(runtime).ID, + Cmd: []string{"id"}, + + User: "unkownuser", + }, + ) + if err != nil { + t.Fatal(err) + } + defer runtime.Destroy(container) + output, err = container.Output() + if container.State.ExitCode == 0 { + t.Fatal("Starting container with wrong uid should fail but it passed.") + } } func TestMultipleContainers(t *testing.T) { diff --git a/components/engine/utils/utils.go b/components/engine/utils/utils.go index ea5c08e60e..c1f5f6a00c 100644 --- a/components/engine/utils/utils.go +++ b/components/engine/utils/utils.go @@ -702,6 +702,9 @@ func ParseRepositoryTag(repos string) (string, string) { return repos, "" } +// UserLookup check if the given username or uid is present in /etc/passwd +// and returns the user struct. +// If the username is not found, an error is returned. func UserLookup(uid string) (*user.User, error) { file, err := ioutil.ReadFile("/etc/passwd") if err != nil { From 72d4396796aedba6b7b0380736f984461d2a60ca Mon Sep 17 00:00:00 2001 From: Daniel Mizyrycki Date: Mon, 15 Jul 2013 12:13:20 -0700 Subject: [PATCH 38/62] packaging, issue #1176: Document PPA release step Upstream-commit: 75a0052e64d0b9cc214cd58ba798863c69f6c5d3 Component: engine --- components/engine/hack/RELEASE.md | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/components/engine/hack/RELEASE.md b/components/engine/hack/RELEASE.md index dd5a67623c..5cf407745a 100644 --- a/components/engine/hack/RELEASE.md +++ b/components/engine/hack/RELEASE.md @@ -30,11 +30,12 @@ up-to-date. * CATEGORY should describe which part of the project is affected. Valid categories are: - * Runtime - * Remote API * Builder * Documentation * Hack + * Packaging + * Remote API + * Runtime * DESCRIPTION: a concise description of the change that is relevant to the end-user, using the present tense. @@ -53,6 +54,10 @@ up-to-date. ### 4. Run all tests + ```bash + $ make test + ``` + ### 5. Commit and create a pull request ```bash @@ -109,11 +114,20 @@ up-to-date. ### 9. Publish Ubuntu packages - If everything went well in the previous step, you can finalize the release by submitting the Ubuntu packages. + If everything went well in the previous step, you can finalize the release by submitting the Ubuntu + packages. ```bash $ RELEASE_IMAGE=image_provided_by_infrastructure_maintainers $ docker run -e RELEASE_PPA=1 $RELEASE_IMAGE ``` - If that goes well, congratulations! You're done. + If that goes well, Ubuntu Precise package is in its way. It will take anywhere from 0.5 to 30 hours + for the builders to complete their job depending on builder demand at this time. At this point, Quantal + and Raring packages need to be created using the Launchpad interface: + https://launchpad.net/~dotcloud/+archive/lxc-docker/+packages + + Notify [the packager maintainers](https://github.com/dotcloud/docker/blob/master/packaging/MAINTAINERS) + who will ensure PPA is ready. + + Congratulations! You're done From f98fac335f947fcf65398ea73f4fa68f9fb3be21 Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Sun, 14 Jul 2013 15:26:57 -0900 Subject: [PATCH 39/62] Do not overwrite container volumes from config Fixes #819 Use same persistent volume when a container is restarted Upstream-commit: 92cbb7cc80a63299b5670a9fcbb2d11789200696 Component: engine --- components/engine/container.go | 51 +++++++++++++++-------------- components/engine/container_test.go | 43 ++++++++++++++++++++++++ 2 files changed, 70 insertions(+), 24 deletions(-) diff --git a/components/engine/container.go b/components/engine/container.go index c714e9f0e1..53d720b771 100644 --- a/components/engine/container.go +++ b/components/engine/container.go @@ -516,8 +516,6 @@ func (container *Container) Start(hostConfig *HostConfig) error { log.Printf("WARNING: Your kernel does not support swap limit capabilities. Limitation discarded.\n") container.Config.MemorySwap = -1 } - container.Volumes = make(map[string]string) - container.VolumesRW = make(map[string]bool) // Create the requested bind mounts binds := make(map[string]BindMap) @@ -557,30 +555,35 @@ func (container *Container) Start(hostConfig *HostConfig) error { // FIXME: evaluate volumes-from before individual volumes, so that the latter can override the former. // Create the requested volumes volumes - for volPath := range container.Config.Volumes { - volPath = path.Clean(volPath) - // If an external bind is defined for this volume, use that as a source - if bindMap, exists := binds[volPath]; exists { - container.Volumes[volPath] = bindMap.SrcPath - if strings.ToLower(bindMap.Mode) == "rw" { - container.VolumesRW[volPath] = true + if container.Volumes == nil || len(container.Volumes) == 0 { + container.Volumes = make(map[string]string) + container.VolumesRW = make(map[string]bool) + + for volPath := range container.Config.Volumes { + volPath = path.Clean(volPath) + // If an external bind is defined for this volume, use that as a source + if bindMap, exists := binds[volPath]; exists { + container.Volumes[volPath] = bindMap.SrcPath + if strings.ToLower(bindMap.Mode) == "rw" { + container.VolumesRW[volPath] = true + } + // Otherwise create an directory in $ROOT/volumes/ and use that + } else { + c, err := container.runtime.volumes.Create(nil, container, "", "", nil) + if err != nil { + return err + } + srcPath, err := c.layer() + if err != nil { + return err + } + container.Volumes[volPath] = srcPath + container.VolumesRW[volPath] = true // RW by default } - // Otherwise create an directory in $ROOT/volumes/ and use that - } else { - c, err := container.runtime.volumes.Create(nil, container, "", "", nil) - if err != nil { - return err + // Create the mountpoint + if err := os.MkdirAll(path.Join(container.RootfsPath(), volPath), 0755); err != nil { + return nil } - srcPath, err := c.layer() - if err != nil { - return err - } - container.Volumes[volPath] = srcPath - container.VolumesRW[volPath] = true // RW by default - } - // Create the mountpoint - if err := os.MkdirAll(path.Join(container.RootfsPath(), volPath), 0755); err != nil { - return nil } } diff --git a/components/engine/container_test.go b/components/engine/container_test.go index f431c7dc9a..2209bc0be0 100644 --- a/components/engine/container_test.go +++ b/components/engine/container_test.go @@ -1300,3 +1300,46 @@ func TestVolumesFromReadonlyMount(t *testing.T) { t.Fail() } } + +// Test that restarting a container with a volume does not create a new volume on restart. Regression test for #819. +func TestRestartWithVolumes(t *testing.T) { + runtime := mkRuntime(t) + defer nuke(runtime) + + container, err := NewBuilder(runtime).Create(&Config{ + Image: GetTestImage(runtime).ID, + Cmd: []string{"echo", "-n", "foobar"}, + Volumes: map[string]struct{}{"/test": {}}, + }, + ) + if err != nil { + t.Fatal(err) + } + defer runtime.Destroy(container) + + for key := range container.Config.Volumes { + if key != "/test" { + t.Fail() + } + } + + _, err = container.Output() + if err != nil { + t.Fatal(err) + } + + expected := container.Volumes["/test"] + if expected == "" { + t.Fail() + } + // Run the container again to verify the volume path persists + _, err = container.Output() + if err != nil { + t.Fatal(err) + } + + actual := container.Volumes["/test"] + if expected != actual { + t.Fatalf("Expected volume path: %s Actual path: %s", expected, actual) + } +} From 723f4f9ef033b7d13b098634dbec7adbae40b51d Mon Sep 17 00:00:00 2001 From: "Guillaume J. Charmes" Date: Mon, 15 Jul 2013 14:55:51 -0700 Subject: [PATCH 40/62] Bump version to 0.5.0 Upstream-commit: bc21b3ebf0dadc51e47d971217d132e5299831a0 Component: engine --- components/engine/CHANGELOG.md | 11 +++++++++++ components/engine/commands.go | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/components/engine/CHANGELOG.md b/components/engine/CHANGELOG.md index f4912ae661..92694148a6 100644 --- a/components/engine/CHANGELOG.md +++ b/components/engine/CHANGELOG.md @@ -1,5 +1,16 @@ # Changelog +## 0.5.0 (2013-07-16) + + Remote API: Add /top endpoint + + Runtime: host directories can be mounted as volumes with 'docker run -b' + + Runtime: Add UDP support + + Builder: Add ENTRYPOINT instruction + + Builder: Add VOLUMES instruction + * Runtime: Add options to docker login + * Builder: Display full output by default + - Registry: Fix issues when pushing to 3rd part registries + - Runtime: Skip `hostname` when merging config + ## 0.4.8 (2013-07-01) + Builder: New build operation ENTRYPOINT adds an executable entry point to the container. - Runtime: Fix a bug which caused 'docker run -d' to no longer print the container ID. diff --git a/components/engine/commands.go b/components/engine/commands.go index b581590bc2..d814197ac6 100644 --- a/components/engine/commands.go +++ b/components/engine/commands.go @@ -27,7 +27,7 @@ import ( "unicode" ) -const VERSION = "0.4.8" +const VERSION = "0.5.0" var ( GITCOMMIT string From df2dfb45a85b7f7bd94a511ee1a85a4bac6e3af9 Mon Sep 17 00:00:00 2001 From: "Guillaume J. Charmes" Date: Mon, 15 Jul 2013 17:18:11 -0700 Subject: [PATCH 41/62] Merge -b and -v options Upstream-commit: eefbadd230d2788b0bdf0daac38ada0d145e3861 Component: engine --- components/engine/commands.go | 18 +++++++++++++++--- components/engine/container.go | 20 ++++++++++++-------- components/engine/container_test.go | 7 +++---- components/engine/utils_test.go | 14 +++++++++----- 4 files changed, 39 insertions(+), 20 deletions(-) diff --git a/components/engine/commands.go b/components/engine/commands.go index b581590bc2..ac1c88055f 100644 --- a/components/engine/commands.go +++ b/components/engine/commands.go @@ -1249,10 +1249,22 @@ func (opts PathOpts) String() string { } func (opts PathOpts) Set(val string) error { - if !filepath.IsAbs(val) { - return fmt.Errorf("%s is not an absolute path", val) + var containerPath string + + splited := strings.SplitN(val, ":", 2) + if len(splited) == 1 { + containerPath = splited[0] + val = filepath.Clean(splited[0]) + } else { + containerPath = splited[1] + val = fmt.Sprintf("%s:%s", splited[0], filepath.Clean(splited[1])) } - opts[filepath.Clean(val)] = struct{}{} + + if !filepath.IsAbs(containerPath) { + utils.Debugf("%s is not an absolute path", containerPath) + return fmt.Errorf("%s is not an absolute path", containerPath) + } + opts[val] = struct{}{} return nil } diff --git a/components/engine/container.go b/components/engine/container.go index c714e9f0e1..3772cf29d2 100644 --- a/components/engine/container.go +++ b/components/engine/container.go @@ -121,14 +121,11 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig, cmd.Var(&flDns, "dns", "Set custom dns servers") flVolumes := NewPathOpts() - cmd.Var(flVolumes, "v", "Attach a data volume") + cmd.Var(flVolumes, "v", "Bind mount a volume (e.g. from the host: -v /host:/container, from docker: -v /container)") flVolumesFrom := cmd.String("volumes-from", "", "Mount volumes from the specified container") flEntrypoint := cmd.String("entrypoint", "", "Overwrite the default entrypoint of the image") - var flBinds ListOpts - cmd.Var(&flBinds, "b", "Bind mount a volume from the host (e.g. -b /host:/container)") - if err := cmd.Parse(args); err != nil { return nil, nil, cmd, err } @@ -146,11 +143,17 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig, } } + var binds []string + // add any bind targets to the list of container volumes - for _, bind := range flBinds { + for bind := range flVolumes { arr := strings.Split(bind, ":") - dstDir := arr[1] - flVolumes[dstDir] = struct{}{} + if len(arr) > 1 { + dstDir := arr[1] + flVolumes[dstDir] = struct{}{} + binds = append(binds, bind) + delete(flVolumes, bind) + } } parsedArgs := cmd.Args() @@ -187,7 +190,7 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig, Entrypoint: entrypoint, } hostConfig := &HostConfig{ - Binds: flBinds, + Binds: binds, } if capabilities != nil && *flMemory > 0 && !capabilities.SwapLimit { @@ -493,6 +496,7 @@ 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() } diff --git a/components/engine/container_test.go b/components/engine/container_test.go index f431c7dc9a..6e82dd5ebd 100644 --- a/components/engine/container_test.go +++ b/components/engine/container_test.go @@ -1231,19 +1231,18 @@ func TestBindMounts(t *testing.T) { writeFile(path.Join(tmpDir, "touch-me"), "", t) // Test reading from a read-only bind mount - stdout, _ := runContainer(r, []string{"-b", fmt.Sprintf("%s:/tmp:ro", tmpDir), "_", "ls", "/tmp"}, t) + stdout, _ := runContainer(r, []string{"-v", fmt.Sprintf("%s:/tmp:ro", tmpDir), "_", "ls", "/tmp"}, t) if !strings.Contains(stdout, "touch-me") { t.Fatal("Container failed to read from bind mount") } // test writing to bind mount - runContainer(r, []string{"-b", fmt.Sprintf("%s:/tmp:rw", tmpDir), "_", "touch", "/tmp/holla"}, t) + runContainer(r, []string{"-v", fmt.Sprintf("%s:/tmp:rw", tmpDir), "_", "touch", "/tmp/holla"}, t) readFile(path.Join(tmpDir, "holla"), t) // Will fail if the file doesn't exist // test mounting to an illegal destination directory - if _, err := runContainer(r, []string{"-b", fmt.Sprintf("%s:.", tmpDir), "ls", "."}, nil); err == nil { + if _, err := runContainer(r, []string{"-v", fmt.Sprintf("%s:.", tmpDir), "ls", "."}, nil); err == nil { t.Fatal("Container bind mounted illegal directory") - } } diff --git a/components/engine/utils_test.go b/components/engine/utils_test.go index 4951d3a02d..afa6a3a1a1 100644 --- a/components/engine/utils_test.go +++ b/components/engine/utils_test.go @@ -1,13 +1,13 @@ package docker import ( + "github.com/dotcloud/docker/utils" "io" "io/ioutil" "os" "path" "strings" "testing" - "github.com/dotcloud/docker/utils" ) // This file contains utility functions for docker's unit test suite. @@ -87,17 +87,18 @@ func readFile(src string, t *testing.T) (content string) { // The image name (eg. the XXX in []string{"-i", "-t", "XXX", "bash"}, is dynamically replaced by the current test image. // The caller is responsible for destroying the container. // Call t.Fatal() at the first error. -func mkContainer(r *Runtime, args []string, t *testing.T) (*Container, *HostConfig) { +func mkContainer(r *Runtime, args []string, t *testing.T) (*Container, *HostConfig, error) { config, hostConfig, _, err := ParseRun(args, nil) if err != nil { - t.Fatal(err) + return nil, nil, err } config.Image = GetTestImage(r).ID c, err := NewBuilder(r).Create(config) if err != nil { t.Fatal(err) + return nil, nil, err } - return c, hostConfig + return c, hostConfig, nil } // Create a test container, start it, wait for it to complete, destroy it, @@ -110,7 +111,10 @@ func runContainer(r *Runtime, args []string, t *testing.T) (output string, err e t.Fatal(err) } }() - container, hostConfig := mkContainer(r, args, t) + container, hostConfig, err := mkContainer(r, args, t) + if err != nil { + return "", err + } defer r.Destroy(container) stdout, err := container.StdoutPipe() if err != nil { From ef03a6ab8c9232764086a2769b096160fda07527 Mon Sep 17 00:00:00 2001 From: "Guillaume J. Charmes" Date: Mon, 15 Jul 2013 17:56:18 -0700 Subject: [PATCH 42/62] Hotfix: make sure ./utils tests pass Upstream-commit: 1004d57b85fc3714b089da4c457228690f254504 Component: engine --- components/engine/utils/utils_test.go | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/components/engine/utils/utils_test.go b/components/engine/utils/utils_test.go index 1a7639d8d2..2c68be50e3 100644 --- a/components/engine/utils/utils_test.go +++ b/components/engine/utils/utils_test.go @@ -5,6 +5,7 @@ import ( "errors" "io" "io/ioutil" + "strings" "testing" ) @@ -264,14 +265,16 @@ func TestCompareKernelVersion(t *testing.T) { func TestHumanSize(t *testing.T) { - size1000 := HumanSize(1000) - if size1000 != " 1 kB" { - t.Errorf("1000 -> expected 1 kB, got %s", size1000) + size := strings.Trim(HumanSize(1000), " \t") + expect := "1 kB" + if size != expect { + t.Errorf("1000 -> expected '%s', got '%s'", expect, size) } - size1024 := HumanSize(1024) - if size1024 != "1.024 kB" { - t.Errorf("1024 -> expected 1.024 kB, got %s", size1024) + size = strings.Trim(HumanSize(1024), " \t") + expect = "1.024 kB" + if size != expect { + t.Errorf("1024 -> expected '%s', got '%s'", expect, size) } } From a4a2ab57ce1029fda0aba526786747f7610890a8 Mon Sep 17 00:00:00 2001 From: "Guillaume J. Charmes" Date: Tue, 16 Jul 2013 10:14:21 -0700 Subject: [PATCH 43/62] Update docs Upstream-commit: 18e91d5f85a008ef48d724db964330ddaa1bbf10 Component: engine --- components/engine/docs/sources/commandline/command/run.rst | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/components/engine/docs/sources/commandline/command/run.rst b/components/engine/docs/sources/commandline/command/run.rst index 4529013b2e..1a14a9b616 100644 --- a/components/engine/docs/sources/commandline/command/run.rst +++ b/components/engine/docs/sources/commandline/command/run.rst @@ -23,7 +23,6 @@ -t=false: Allocate a pseudo-tty -u="": Username or UID -d=[]: Set custom dns servers for the container - -v=[]: Creates a new volume and mounts it at the specified path. + -v=[]: Create a bind mount with: [host-dir]:[container-dir]:[rw|ro]. If "host-dir" is missing, then docker creates a new volume. -volumes-from="": Mount all volumes from the given container. - -b=[]: Create a bind mount with: [host-dir]:[container-dir]:[rw|ro] -entrypoint="": Overwrite the default entrypoint set by the image. From 73fd748fb27b141ab5ad3bcc05dd1f2268eb7ebb Mon Sep 17 00:00:00 2001 From: Daniel Mizyrycki Date: Tue, 16 Jul 2013 13:45:43 -0700 Subject: [PATCH 44/62] Testing, issue #1217: Add coverage testing into docker-ci Upstream-commit: 6e8bfc8d12ca062dc4c311d528784c1dec27079d Component: engine --- components/engine/testing/buildbot/master.cfg | 25 +++++++++++++++---- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/components/engine/testing/buildbot/master.cfg b/components/engine/testing/buildbot/master.cfg index 65399bb1a1..61912808ec 100644 --- a/components/engine/testing/buildbot/master.cfg +++ b/components/engine/testing/buildbot/master.cfg @@ -2,6 +2,7 @@ import os from buildbot.buildslave import BuildSlave from buildbot.schedulers.forcesched import ForceScheduler from buildbot.schedulers.basic import SingleBranchScheduler +from buildbot.schedulers.timed import Nightly from buildbot.changes import filter from buildbot.config import BuilderConfig from buildbot.process.factory import BuildFactory @@ -40,12 +41,16 @@ c['db'] = {'db_url':"sqlite:///state.sqlite"} c['slaves'] = [BuildSlave('buildworker', BUILDBOT_PWD)] c['slavePortnum'] = PORT_MASTER -c['schedulers'] = [ForceScheduler(name='trigger',builderNames=[BUILDER_NAME])] -c['schedulers'].append(SingleBranchScheduler(name="all", - change_filter=filter.ChangeFilter(branch='master'),treeStableTimer=None, - builderNames=[BUILDER_NAME])) +# Schedulers +c['schedulers'] = [ForceScheduler(name='trigger', builderNames=[BUILDER_NAME, + 'coverage'])] +c['schedulers'] += [SingleBranchScheduler(name="all", + change_filter=filter.ChangeFilter(branch='master'), treeStableTimer=None, + builderNames=[BUILDER_NAME])] +c['schedulers'] += [Nightly(name='daily', branch=None, builderNames=['coverage'], + hour=0, minute=30)] -# Builder +# Builders factory = BuildFactory() factory.addStep(ShellCommand(description='Docker',logEnviron=False,usePTY=True, command=["sh", "-c", Interpolate("cd ..; rm -rf build; export GOPATH={0}; " @@ -53,6 +58,16 @@ factory.addStep(ShellCommand(description='Docker',logEnviron=False,usePTY=True, "go test -v".format(BUILDER_PATH,GITHUB_DOCKER,DOCKER_BUILD_PATH))])) c['builders'] = [BuilderConfig(name=BUILDER_NAME,slavenames=['buildworker'], factory=factory)] +# Docker coverage test +coverage_cmd = ('GOPATH=`pwd` go get -d github.com/dotcloud/docker\n' + 'GOPATH=`pwd` go get github.com/axw/gocov/gocov\n' + 'sudo -E GOPATH=`pwd` ./bin/gocov test github.com/dotcloud/docker | ' + './bin/gocov report') +factory = BuildFactory() +factory.addStep(ShellCommand(description='Coverage',logEnviron=False,usePTY=True, + command=coverage_cmd)) +c['builders'] += [BuilderConfig(name='coverage',slavenames=['buildworker'], + factory=factory)] # Status authz_cfg = authz.Authz(auth=auth.BasicAuth([(TEST_USER, TEST_PWD)]), From 2953a9d955582d9ce7ce80723a5442438480efc2 Mon Sep 17 00:00:00 2001 From: Andy Rothfusz Date: Tue, 16 Jul 2013 17:04:41 -0700 Subject: [PATCH 45/62] Update repository information. Upstream-commit: 0356081c0ad2f1d95fb435f9a491f7a7a63cbf33 Component: engine --- .../sources/use/workingwithrepository.rst | 124 +++++++++++------- 1 file changed, 76 insertions(+), 48 deletions(-) diff --git a/components/engine/docs/sources/use/workingwithrepository.rst b/components/engine/docs/sources/use/workingwithrepository.rst index 243c99afdf..3cdbfe49d6 100644 --- a/components/engine/docs/sources/use/workingwithrepository.rst +++ b/components/engine/docs/sources/use/workingwithrepository.rst @@ -7,21 +7,69 @@ Working with Repositories ========================= +A *repository* is a hosted collection of tagged :ref:`images +` that together create the file system for a container. The +repository's name is a tag that indicates the provenance of the +repository, i.e. who created it and where the original copy is +located. -Top-level repositories and user repositories --------------------------------------------- +You can find one or more repositories hosted on a *registry*. There +can be an implicit or explicit host name as part of the repository +tag. The implicit registry is located at ``index.docker.io``, the home +of "top-level" repositories and the Central Index. This registry may +also include public "user" repositories. -Generally, there are two types of repositories: Top-level repositories -which are controlled by the people behind Docker, and user -repositories. +So Docker is not only a tool for creating and managing your own +:ref:`containers ` -- **Docker is also a tool for +sharing**. The Docker project provides a Central Registry to host +public repositories, namespaced by user, and a Central Index which +provides user authentication and search over all the public +repositories. You can host your own Registry too! Docker acts as a +client for these services via ``docker search, pull, login`` and +``push``. -* Top-level repositories can easily be recognized by not having a ``/`` (slash) in their name. These repositories can generally be trusted. -* User repositories always come in the form of ``/``. This is what your published images will look like. -* User images are not checked, it is therefore up to you whether or not you trust the creator of this image. +Top-level, User, and Your Own Repositories +------------------------------------------ +There are two types of public repositories: *top-level* repositories +which are controlled by the Docker team, and *user* repositories +created by individual contributors. -Find public images available on the index ------------------------------------------ +* Top-level repositories can easily be recognized by **not** having a + ``/`` (slash) in their name. These repositories can generally be + trusted. +* User repositories always come in the form of + ``/``. This is what your published images will + look like if you push to the public Central Registry. +* Only the authenticated user can push to their *username* namespace + on the Central Registry. +* User images are not checked, it is therefore up to you whether or + not you trust the creator of this image. + +Right now (version 0.5), private repositories are only possible by +hosting `your own registry +`_. To push or pull to a +repository on your own registry, you must prefix the tag with the +address of the registry's host, like this: + +.. code-block:: bash + + # Tag to create a repository with the full registry location. + # The location (e.g. localhost.localdomain:5000) becomes + # a permanent part of the repository name + docker tag 0u812deadbeef localhost.localdomain:5000/repo_name + + # Push the new repository to its home location on localhost + docker push localhost.localdomain:5000/repo_name + +Once a repository has your registry's host name as part of the tag, +you can push and pull it like any other repository, but it will +**not** be searchable (or indexed at all) in the Central Index, and +there will be no user name checking performed. Your registry will +function completely independently from the Central Index. + +Find public images available on the Central Index +------------------------------------------------- Seach by name, namespace or description @@ -37,68 +85,48 @@ Download them simply by their name docker pull -Very similarly you can search for and browse the index online on https://index.docker.io +Very similarly you can search for and browse the index online on +https://index.docker.io -Connecting to the repository ----------------------------- +Connecting to the Central Registry +---------------------------------- -You can create a user on the central docker repository online, or by running +You can create a user on the central Docker Index online, or by running .. code-block:: bash docker login +This will prompt you for a username, which will become a public +namespace for your public repositories. -If your username does not exist it will prompt you to also enter a password and your e-mail address. It will then -automatically log you in. +If your username does not exist it will prompt you to also enter a +password and your e-mail address. It will then automatically log you +in. Committing a container to a named image --------------------------------------- -In order to commit to the repository it is required to have committed your container to an image with your namespace. +In order to commit to the repository it is required to have committed +your container to an image within your username namespace. .. code-block:: bash # for example docker commit $CONTAINER_ID dhrp/kickassapp - docker commit / + docker commit / -Pushing a container to the repository ------------------------------------------ +Pushing a container to its repository +------------------------------------ -In order to push an image to the repository you need to have committed your container to a named image (see above) +In order to push an image to its repository you need to have committed +your container to a named image (see above) Now you can commit this image to the repository .. code-block:: bash # for example docker push dhrp/kickassapp - docker push - - -Changing the server to connect to ----------------------------------- - -When you are running your own index and/or registry, You can change the server the docker client will connect to. - -Variable -^^^^^^^^ - -.. code-block:: sh - - DOCKER_INDEX_URL - -Setting this environment variable on the docker server will change the URL docker index. -This address is used in commands such as ``docker login``, ``docker push`` and ``docker pull``. -The docker daemon doesn't need to be restarted for this parameter to take effect. - -Example -^^^^^^^ - -.. code-block:: sh - - docker -d & - export DOCKER_INDEX_URL="https://index.docker.io" - + docker push / From be8eb02780915a73b4c3ef5c3dc1cbb69864fdc6 Mon Sep 17 00:00:00 2001 From: Louis Opter Date: Wed, 17 Jul 2013 01:02:07 -0700 Subject: [PATCH 46/62] Always stop the opposite goroutine in network_proxy.go (closes #1213) Upstream-commit: c766d064ac6c2183321cb2e47ea8c0b0b2d2d238 Component: engine --- components/engine/network_proxy.go | 1 + 1 file changed, 1 insertion(+) diff --git a/components/engine/network_proxy.go b/components/engine/network_proxy.go index 905773e533..fb91cc1b37 100644 --- a/components/engine/network_proxy.go +++ b/components/engine/network_proxy.go @@ -68,6 +68,7 @@ func (proxy *TCPProxy) clientLoop(client *net.TCPConn, quit chan bool) { from.CloseWrite() } } + to.CloseRead() event <- written } utils.Debugf("Forwarding traffic between tcp/%v and tcp/%v", client.RemoteAddr(), backend.RemoteAddr()) From 7d6b6db98e70e77903c78091d4918a4f9dc7f0fb Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Wed, 17 Jul 2013 15:48:53 +0000 Subject: [PATCH 47/62] fix docker rmi via id Upstream-commit: 5a934fc923af316a6e82b9dd11169484b3b744f6 Component: engine --- components/engine/server.go | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/components/engine/server.go b/components/engine/server.go index c43cae0c38..954bbb208f 100644 --- a/components/engine/server.go +++ b/components/engine/server.go @@ -870,7 +870,6 @@ func (srv *Server) deleteImageAndChildren(id string, imgs *[]APIRmi) error { if len(srv.runtime.repositories.ByID()[id]) != 0 { return ErrImageReferenced } - // If the image is not referenced but has children, go recursive referenced := false byParents, err := srv.runtime.graph.ByParent() @@ -924,8 +923,22 @@ func (srv *Server) deleteImageParents(img *Image, imgs *[]APIRmi) error { } func (srv *Server) deleteImage(img *Image, repoName, tag string) ([]APIRmi, error) { - //Untag the current image imgs := []APIRmi{} + + //If delete by id, see if the id belong only to one repository + if strings.Contains(img.ID, repoName) && tag == "" { + for _, repoAndTag := range srv.runtime.repositories.ByID()[img.ID] { + parsedRepo := strings.Split(repoAndTag, ":")[0] + if strings.Contains(img.ID, repoName) { + repoName = parsedRepo + } else if repoName != parsedRepo { + // the id belongs to multiple repos, like base:latest and user:test, + // in that case return conflict + return imgs, nil + } + } + } + //Untag the current image tagDeleted, err := srv.runtime.repositories.Delete(repoName, tag) if err != nil { return nil, err From 28398dc429db15dca9ae0d3ebdf147a389a6fca8 Mon Sep 17 00:00:00 2001 From: Ken Cochrane Date: Wed, 17 Jul 2013 13:46:11 -0400 Subject: [PATCH 48/62] updated with notes from @vieux Upstream-commit: d0e8ca1257f7f969a035d3d78b55e08c067e8a20 Component: engine --- components/engine/commands.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/components/engine/commands.go b/components/engine/commands.go index 20195e2e39..3223c962ac 100644 --- a/components/engine/commands.go +++ b/components/engine/commands.go @@ -94,8 +94,8 @@ func (cli *DockerCli) CmdHelp(args ...string) error { {"pull", "Pull an image or a repository from the docker registry server"}, {"push", "Push an image or a repository to the docker registry server"}, {"restart", "Restart a running container"}, - {"rm", "Remove a container"}, - {"rmi", "Remove an image"}, + {"rm", "Remove one or more containers"}, + {"rmi", "Remove one or more images"}, {"run", "Run a command in a new container"}, {"search", "Search for an image in the docker index"}, {"start", "Start a stopped container"}, @@ -469,7 +469,7 @@ func (cli *DockerCli) CmdInfo(args ...string) error { func (cli *DockerCli) CmdStop(args ...string) error { cmd := Subcmd("stop", "[OPTIONS] CONTAINER [CONTAINER...]", "Stop a running container") - nSeconds := cmd.Int("t", 10, "wait t seconds before stopping the container") + nSeconds := cmd.Int("t", 10, "Number of seconds to try to stop for before killing the container. Default=10") if err := cmd.Parse(args); err != nil { return nil } @@ -494,7 +494,7 @@ func (cli *DockerCli) CmdStop(args ...string) error { func (cli *DockerCli) CmdRestart(args ...string) error { cmd := Subcmd("restart", "[OPTIONS] CONTAINER [CONTAINER...]", "Restart a running container") - nSeconds := cmd.Int("t", 10, "wait t seconds before restarting the container") + nSeconds := cmd.Int("t", 10, "Number of seconds to try to stop for before killing the container. Once killed it will then be restarted. Default=10") if err := cmd.Parse(args); err != nil { return nil } @@ -638,7 +638,7 @@ func (cli *DockerCli) CmdPort(args ...string) error { // 'docker rmi IMAGE' removes all images with the name IMAGE func (cli *DockerCli) CmdRmi(args ...string) error { - cmd := Subcmd("rmi", "IMAGE [IMAGE...]", "Remove an image") + cmd := Subcmd("rmi", "IMAGE [IMAGE...]", "Remove one or more images") if err := cmd.Parse(args); err != nil { return nil } @@ -703,7 +703,7 @@ func (cli *DockerCli) CmdHistory(args ...string) error { } func (cli *DockerCli) CmdRm(args ...string) error { - cmd := Subcmd("rm", "[OPTIONS] CONTAINER [CONTAINER...]", "Remove a container") + cmd := Subcmd("rm", "[OPTIONS] CONTAINER [CONTAINER...]", "Remove one or more containers") v := cmd.Bool("v", false, "Remove the volumes associated to the container") if err := cmd.Parse(args); err != nil { return nil From 76eb7762ab7caf667a1376be7e01e24540692469 Mon Sep 17 00:00:00 2001 From: Solomon Hykes Date: Wed, 17 Jul 2013 11:39:38 -0700 Subject: [PATCH 49/62] Small changes in changelog wording Upstream-commit: 8af945f353379a96eb036d67fb24d489c5e16808 Component: engine --- components/engine/CHANGELOG.md | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/components/engine/CHANGELOG.md b/components/engine/CHANGELOG.md index 92694148a6..dff042f1da 100644 --- a/components/engine/CHANGELOG.md +++ b/components/engine/CHANGELOG.md @@ -1,15 +1,17 @@ # Changelog ## 0.5.0 (2013-07-16) - + Remote API: Add /top endpoint - + Runtime: host directories can be mounted as volumes with 'docker run -b' - + Runtime: Add UDP support - + Builder: Add ENTRYPOINT instruction - + Builder: Add VOLUMES instruction - * Runtime: Add options to docker login - * Builder: Display full output by default - - Registry: Fix issues when pushing to 3rd part registries - - Runtime: Skip `hostname` when merging config + + Runtime: List all processes running inside a container with 'docker top' + + Runtime: Host directories can be mounted as volumes with 'docker run -b' + + Runtime: Containers can expose public UDP ports + + Runtime: Optionally specify an exact public port (eg. '-p 80:4500') + + Registry: New image naming scheme inspired by Go packaging convention allows arbitrary combinations of registries + + Builder: ENTRYPOINT instruction sets a default binary entry point to a container + + Builder: VOLUME instruction marks a part of the container as persistent data + * Builder: 'docker build' displays the full output of a build by default + * Runtime: 'docker login' supports additional options + - Runtime: Dont save a container's hostname when committing an image. + - Registry: Fix issues when uploading images to a private registry ## 0.4.8 (2013-07-01) + Builder: New build operation ENTRYPOINT adds an executable entry point to the container. From 926b49f01623f4a7f9fef6814ead72241423a73b Mon Sep 17 00:00:00 2001 From: Solomon Hykes Date: Wed, 17 Jul 2013 11:51:26 -0700 Subject: [PATCH 50/62] Changed date on changelog Upstream-commit: ac14c463d55494fdab88d36ea74a4ecef8ab48dc Component: engine --- components/engine/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/engine/CHANGELOG.md b/components/engine/CHANGELOG.md index dff042f1da..0ed6495ae8 100644 --- a/components/engine/CHANGELOG.md +++ b/components/engine/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## 0.5.0 (2013-07-16) +## 0.5.0 (2013-07-17) + Runtime: List all processes running inside a container with 'docker top' + Runtime: Host directories can be mounted as volumes with 'docker run -b' + Runtime: Containers can expose public UDP ports From a44b5f6d5b87db2844dab6535b6432811e844c5e Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Wed, 17 Jul 2013 19:24:54 +0000 Subject: [PATCH 51/62] change rm usage in docs Upstream-commit: 9cf2b41c053d557af6c563311b4953c4b9bab6d6 Component: engine --- components/engine/docs/sources/commandline/command/rm.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/engine/docs/sources/commandline/command/rm.rst b/components/engine/docs/sources/commandline/command/rm.rst index dc6d91632d..8a2309ce79 100644 --- a/components/engine/docs/sources/commandline/command/rm.rst +++ b/components/engine/docs/sources/commandline/command/rm.rst @@ -10,4 +10,4 @@ Usage: docker rm [OPTIONS] CONTAINER - Remove a container + Remove one or more containers From 97e62e30e8eff39efc2b4bde576077ba41b869fc Mon Sep 17 00:00:00 2001 From: Andy Rothfusz Date: Wed, 17 Jul 2013 18:56:40 -0700 Subject: [PATCH 52/62] Make dockerfile docs easier to find. Clean up formatting. Upstream-commit: aa5671411b727d7142c289b5c1b473209aeaf630 Component: engine --- .../engine/docs/sources/use/builder.rst | 160 ++++++++++-------- 1 file changed, 94 insertions(+), 66 deletions(-) diff --git a/components/engine/docs/sources/use/builder.rst b/components/engine/docs/sources/use/builder.rst index ceda9c99dd..9ea8033b98 100644 --- a/components/engine/docs/sources/use/builder.rst +++ b/components/engine/docs/sources/use/builder.rst @@ -1,25 +1,27 @@ -:title: Docker Builder +:title: Dockerfile Builder :description: Docker Builder specifes a simple DSL which allows you to automate the steps you would normally manually take to create an image. :keywords: builder, docker, Docker Builder, automation, image creation -============== -Docker Builder -============== +================== +Dockerfile Builder +================== + +**Docker can act as a builder** and read instructions from a text +Dockerfile to automate the steps you would otherwise make manually to +create an image. Executing ``docker build`` will run your steps and +commit them along the way, giving you a final image. .. contents:: Table of Contents -Docker Builder specifes a simple DSL which allows you to automate the steps you -would normally manually take to create an image. Docker Build will run your -steps and commit them along the way, giving you a final image. - 1. Usage ======== -To build an image from a source repository, create a description file called `Dockerfile` -at the root of your repository. This file will describe the steps to assemble -the image. +To build an image from a source repository, create a description file +called ``Dockerfile`` at the root of your repository. This file will +describe the steps to assemble the image. -Then call `docker build` with the path of your source repository as argument: +Then call ``docker build`` with the path of your source repository as +argument: ``docker build .`` @@ -36,136 +38,162 @@ before finally outputting the ID of your new image. The Dockerfile format is quite simple: - ``instruction arguments`` +:: + + # Comment + INSTRUCTION arguments The Instruction is not case-sensitive, however convention is for them to be UPPERCASE in order to distinguish them from arguments more easily. -Dockerfiles are evaluated in order, therefore the first instruction must be -`FROM` in order to specify the base image from which you are building. +Docker evaluates the instructions in a Dockerfile in order. **The first +instruction must be `FROM`** in order to specify the base image from +which you are building. -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. +Docker will ignore **comment lines** *beginning* with ``#``. A comment +marker anywhere in the rest of the line will be treated as an argument. 3. Instructions =============== -Docker builder comes with a set of instructions, described below. +Here is the set of instructions you can use in a ``Dockerfile`` for +building images. 3.1 FROM -------- ``FROM `` -The `FROM` instruction sets the base image for subsequent instructions. As such, -a valid Dockerfile must have it as its first instruction. +The ``FROM`` instruction sets the :ref:`base_image_def` for subsequent +instructions. As such, a valid Dockerfile must have ``FROM`` as its +first instruction. -`FROM` can be included multiple times within a single Dockerfile in order to -create multiple images. Simply make a note of the last image id output by the -commit before each new `FROM` command. +``FROM`` must be the first non-comment instruction in the +``Dockerfile``. + +``FROM`` can appear multiple times within a single Dockerfile in order +to create multiple images. Simply make a note of the last image id +output by the commit before each new ``FROM`` command. 3.2 MAINTAINER -------------- ``MAINTAINER `` -The `MAINTAINER` instruction allows you to set the Author field of the generated -images. +The ``MAINTAINER`` instruction allows you to set the *Author* field of +the generated images. 3.3 RUN ------- ``RUN `` -The `RUN` instruction will execute any commands on the current image and commit -the results. The resulting committed image will be used for the next step in the -Dockerfile. +The ``RUN`` instruction will execute any commands on the current image +and commit the results. The resulting committed image will be used for +the next step in the Dockerfile. -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. +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. 3.4 CMD ------- ``CMD `` -The `CMD` instruction sets the command to be executed when running the image. -This is functionally equivalent to running -`docker commit -run '{"Cmd": }'` outside the builder. +The ``CMD`` instruction sets the command to be executed when running +the image. This is functionally equivalent to running ``docker commit +-run '{"Cmd": }'`` outside the builder. -.. note:: - Don't confuse `RUN` with `CMD`. `RUN` actually runs a command and commits - the result; `CMD` does not execute anything at build time, but specifies the - intended command for the image. +.. note:: + Don't confuse `RUN` with `CMD`. `RUN` actually runs a + command and commits the result; `CMD` does not execute anything at + build time, but specifies the intended command for the image. 3.5 EXPOSE ---------- ``EXPOSE [...]`` -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. +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. 3.6 ENV ------- ``ENV `` -The `ENV` instruction sets the environment variable `` to the value -``. This value will be passed to all future ``RUN`` instructions. This is -functionally equivalent to prefixing the command with `=` +The ``ENV`` instruction sets the environment variable ```` to the +value ````. This value will be passed to all future ``RUN`` +instructions. This is functionally equivalent to prefixing the command +with ``=`` -.. note:: - The environment variables will persist when a container is run from the resulting image. +.. note:: + The environment variables will persist when a container is run + from the resulting image. 3.7 ADD ------- ``ADD `` -The `ADD` instruction will copy new files from and add them to the container's filesystem at path ``. +The ``ADD`` instruction will copy new files from and add them to +the container's filesystem at path ````. -`` must be the path to a file or directory relative to the source directory being built (also called the -context of the build) or a remote file URL. +```` must be the path to a file or directory relative to the +source directory being built (also called the *context* of the build) or +a remote file URL. -`` is the path at which the source will be copied in the destination container. +```` is the path at which the source will be copied in the +destination container. The copy obeys the following rules: -If `` is a directory, the entire directory is copied, including filesystem metadata. +* If ```` is a directory, the entire directory is copied, + including filesystem metadata. +* If ````` is a tar archive in a recognized compression format + (identity, gzip, bzip2 or xz), it is unpacked as a directory. -If `` is a tar archive in a recognized compression format (identity, gzip, bzip2 or xz), it -is unpacked as a directory. + When a directory is copied or unpacked, it has the same behavior as + ``tar -x``: the result is the union of -When a directory is copied or unpacked, it has the same behavior as 'tar -x': the result is the union of -a) whatever existed at the destination path and b) the contents of the source tree, with conflicts resolved -in favor of b on a file-by-file basis. + 1. whatever existed at the destination path and + 2. the contents of the source tree, -If `` is any other kind of file, it is copied individually along with its metadata. In this case, -if `` ends with a trailing slash '/', it will be considered a directory and the contents of `` -will be written at `/base()`. -If `` does not end with a trailing slash, it will be considered a regular file and the contents -of `` will be written at ``. + with conflicts resolved in favor of 2) on a file-by-file basis. -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. +* If ```` is any other kind of file, it is copied individually + along with its metadata. In this case, if ```` ends with a + trailing slash ``/``, it will be considered a directory and the + contents of ```` will be written at ``/base()``. +* If ```` does not end with a trailing slash, it will be + considered a regular file and the contents 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. 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. +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. 3.9 VOLUME ---------- ``VOLUME ["/data"]`` -The `VOLUME` instruction will add one or more new volumes to any container created from the image. +The ``VOLUME`` instruction will add one or more new volumes to any +container created from the image. 4. Dockerfile Examples ====================== From 9423c7f84d421e0bfc0f54a619271fb6019bf83a Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Thu, 18 Jul 2013 13:25:47 +0000 Subject: [PATCH 53/62] add legacy support Upstream-commit: a926cd4d880904258e01ea521ecd9e1b908f2b97 Component: engine --- components/engine/server.go | 45 ++++++++++++++++++++++++++----------- 1 file changed, 32 insertions(+), 13 deletions(-) diff --git a/components/engine/server.go b/components/engine/server.go index 6129e3eb95..d275fe814b 100644 --- a/components/engine/server.go +++ b/components/engine/server.go @@ -1044,20 +1044,39 @@ func (srv *Server) ContainerAttach(name string, logs, stream, stdin, stdout, std //logs if logs { cLog, err := container.ReadLog("json") - if err != nil { - utils.Debugf("Error reading logs (json): %s", err) - } - dec := json.NewDecoder(cLog) - for { - var l utils.JSONLog - if err := dec.Decode(&l); err == io.EOF { - break - } else if err != nil { - utils.Debugf("Error streaming logs: %s", err) - break + if err != nil && os.IsNotExist(err) { + // Legacy logs + if stdout { + cLog, err := container.ReadLog("stdout") + if err != nil { + utils.Debugf("Error reading logs (stdout): %s", err) + } else if _, err := io.Copy(out, cLog); err != nil { + utils.Debugf("Error streaming logs (stdout): %s", err) + } } - if (l.Stream == "stdout" && stdout) || (l.Stream == "stderr" && stderr) { - fmt.Fprintf(out, "%s", l.Log) + if stderr { + cLog, err := container.ReadLog("stderr") + if err != nil { + utils.Debugf("Error reading logs (stderr): %s", err) + } else if _, err := io.Copy(out, cLog); err != nil { + utils.Debugf("Error streaming logs (stderr): %s", err) + } + } + } else if err != nil { + utils.Debugf("Error reading logs (json): %s", err) + } else { + dec := json.NewDecoder(cLog) + for { + var l utils.JSONLog + if err := dec.Decode(&l); err == io.EOF { + break + } else if err != nil { + utils.Debugf("Error streaming logs: %s", err) + break + } + if (l.Stream == "stdout" && stdout) || (l.Stream == "stderr" && stderr) { + fmt.Fprintf(out, "%s", l.Log) + } } } } From e1b72763dc7558e1e5fc65b73f3072891ea4a03f Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Thu, 18 Jul 2013 13:29:40 +0000 Subject: [PATCH 54/62] add debug and simplify docker logs Upstream-commit: 1b0fd7ead33722d8782634d54cbd797f284aa085 Component: engine --- components/engine/commands.go | 5 +---- components/engine/server.go | 1 + 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/components/engine/commands.go b/components/engine/commands.go index b581590bc2..def2ff72d7 100644 --- a/components/engine/commands.go +++ b/components/engine/commands.go @@ -1099,10 +1099,7 @@ func (cli *DockerCli) CmdLogs(args ...string) error { return nil } - if err := cli.hijack("POST", "/containers/"+cmd.Arg(0)+"/attach?logs=1&stdout=1", false, nil, cli.out); err != nil { - return err - } - if err := cli.hijack("POST", "/containers/"+cmd.Arg(0)+"/attach?logs=1&stderr=1", false, nil, cli.err); err != nil { + if err := cli.hijack("POST", "/containers/"+cmd.Arg(0)+"/attach?logs=1&stdout=1&stderr=1", false, nil, cli.out); err != nil { return err } return nil diff --git a/components/engine/server.go b/components/engine/server.go index d275fe814b..958dc75663 100644 --- a/components/engine/server.go +++ b/components/engine/server.go @@ -1046,6 +1046,7 @@ func (srv *Server) ContainerAttach(name string, logs, stream, stdin, stdout, std cLog, err := container.ReadLog("json") if err != nil && os.IsNotExist(err) { // Legacy logs + utils.Debugf("Old logs format") if stdout { cLog, err := container.ReadLog("stdout") if err != nil { From 1df4154799a298a057fca34e9858fccdd4dbcb39 Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Mon, 8 Jul 2013 17:06:06 -0900 Subject: [PATCH 55/62] Add unit tests for buildfile config instructions Add tests for instructions in the buildfile that modify the config of the resulting image. Upstream-commit: e7f3f6fa5a10f890a1774a2320d31e236af56be9 Component: engine --- components/engine/buildfile_test.go | 100 ++++++++++++++++++++++------ 1 file changed, 79 insertions(+), 21 deletions(-) diff --git a/components/engine/buildfile_test.go b/components/engine/buildfile_test.go index 2a1bebe156..14edbc088f 100644 --- a/components/engine/buildfile_test.go +++ b/components/engine/buildfile_test.go @@ -105,23 +105,11 @@ CMD Hello world func TestBuild(t *testing.T) { for _, ctx := range testContexts { - runtime := mkRuntime(t) - defer nuke(runtime) - - srv := &Server{ - runtime: runtime, - pullingPool: make(map[string]struct{}), - pushingPool: make(map[string]struct{}), - } - - buildfile := NewBuildFile(srv, ioutil.Discard, false) - if _, err := buildfile.Build(mkTestContext(ctx.dockerfile, ctx.files, t)); err != nil { - t.Fatal(err) - } + buildImage(ctx, t) } } -func TestVolume(t *testing.T) { +func buildImage(context testContextTemplate, t *testing.T) *Image { runtime, err := newTestRuntime() if err != nil { t.Fatal(err) @@ -133,20 +121,27 @@ func TestVolume(t *testing.T) { pullingPool: make(map[string]struct{}), pushingPool: make(map[string]struct{}), } - buildfile := NewBuildFile(srv, ioutil.Discard, false) - imgId, err := buildfile.Build(mkTestContext(` -from %s -VOLUME /test -CMD Hello world -`, nil, t)) + + id, err := buildfile.Build(mkTestContext(context.dockerfile, context.files, t)) if err != nil { t.Fatal(err) } - img, err := srv.ImageInspect(imgId) + + img, err := srv.ImageInspect(id) if err != nil { t.Fatal(err) } + return img +} + +func TestVolume(t *testing.T) { + img := buildImage(testContextTemplate{` + from %s + volume /test + cmd Hello world + `, nil}, t) + if len(img.Config.Volumes) == 0 { t.Fail() } @@ -156,3 +151,66 @@ CMD Hello world } } } + +func TestBuildMaintainer(t *testing.T) { + img := buildImage(testContextTemplate{` + from %s + maintainer dockerio + `, nil}, t) + + if img.Author != "dockerio" { + t.Fail() + } +} + +func TestBuildEnv(t *testing.T) { + img := buildImage(testContextTemplate{` + from %s + env port 4243 + `, + nil}, t) + + if img.Config.Env[0] != "port=4243" { + t.Fail() + } +} + +func TestBuildCmd(t *testing.T) { + img := buildImage(testContextTemplate{` + from %s + cmd ["/bin/echo", "Hello World"] + `, + nil}, t) + + if img.Config.Cmd[0] != "/bin/echo" { + t.Log(img.Config.Cmd[0]) + t.Fail() + } + if img.Config.Cmd[1] != "Hello World" { + t.Log(img.Config.Cmd[1]) + t.Fail() + } +} + +func TestBuildExpose(t *testing.T) { + img := buildImage(testContextTemplate{` + from %s + expose 4243 + `, + nil}, t) + + if img.Config.PortSpecs[0] != "4243" { + t.Fail() + } +} + +func TestBuildEntrypoint(t *testing.T) { + img := buildImage(testContextTemplate{` + from %s + entrypoint ["/bin/echo"] + `, + nil}, t) + + if img.Config.Entrypoint[0] != "/bin/echo" { + } +} From 608601a19192fc555959cf047ec9f643cc56e1cb Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Thu, 18 Jul 2013 16:24:12 +0000 Subject: [PATCH 56/62] change -b -> -v and add udp example Upstream-commit: b083418257edbcb769dd1bf9a6a3dafd334d5969 Component: engine --- components/engine/CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/engine/CHANGELOG.md b/components/engine/CHANGELOG.md index 0ed6495ae8..00e358c136 100644 --- a/components/engine/CHANGELOG.md +++ b/components/engine/CHANGELOG.md @@ -2,8 +2,8 @@ ## 0.5.0 (2013-07-17) + Runtime: List all processes running inside a container with 'docker top' - + Runtime: Host directories can be mounted as volumes with 'docker run -b' - + Runtime: Containers can expose public UDP ports + + Runtime: Host directories can be mounted as volumes with 'docker run -v' + + Runtime: Containers can expose public UDP ports (eg, '-p 123/udp') + Runtime: Optionally specify an exact public port (eg. '-p 80:4500') + Registry: New image naming scheme inspired by Go packaging convention allows arbitrary combinations of registries + Builder: ENTRYPOINT instruction sets a default binary entry point to a container From 325ed80b66147ef60134b738ea7143d5cf3db0ea Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Thu, 18 Jul 2013 20:50:04 +0000 Subject: [PATCH 57/62] switch version to -dev Upstream-commit: 0089dd05e910852d5b821074c73287e730040630 Component: engine --- components/engine/commands.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/engine/commands.go b/components/engine/commands.go index f78effe944..936b23fea2 100644 --- a/components/engine/commands.go +++ b/components/engine/commands.go @@ -27,7 +27,7 @@ import ( "unicode" ) -const VERSION = "0.5.0" +const VERSION = "0.5.0-dev" var ( GITCOMMIT string From 3498daf9b5fe147e8151e44e931d9236e45aeea0 Mon Sep 17 00:00:00 2001 From: unclejack Date: Mon, 8 Jul 2013 11:01:16 +0300 Subject: [PATCH 58/62] add support for container ID files (a la pidfile) Upstream-commit: 64e74cefb746caa7f2a581149bbd523dd1ac9215 Component: engine --- components/engine/commands.go | 14 ++++++++++++++ components/engine/container.go | 7 +++++-- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/components/engine/commands.go b/components/engine/commands.go index 936b23fea2..42cccf6b96 100644 --- a/components/engine/commands.go +++ b/components/engine/commands.go @@ -1341,6 +1341,20 @@ func (cli *DockerCli) CmdRun(args ...string) error { for _, warning := range runResult.Warnings { fmt.Fprintf(cli.err, "WARNING: %s\n", warning) } + if len(hostConfig.ContainerIDFile) > 0 { + if _, err := ioutil.ReadFile(hostConfig.ContainerIDFile); err == nil { + return fmt.Errorf("cid file found, make sure the other container isn't running or delete %s", hostConfig.ContainerIDFile) + } + file, err := os.Create(hostConfig.ContainerIDFile) + if err != nil { + return fmt.Errorf("failed to create the container ID file: %s", err) + } + + defer file.Close() + if _, err = file.WriteString(runResult.ID); err != nil { + return fmt.Errorf("failed to write the container ID to the file: %s", err) + } + } //start the container if _, _, err = cli.call("POST", "/containers/"+runResult.ID+"/start", hostConfig); err != nil { diff --git a/components/engine/container.go b/components/engine/container.go index 95c5ba0f72..4443ad52a3 100644 --- a/components/engine/container.go +++ b/components/engine/container.go @@ -80,7 +80,8 @@ type Config struct { } type HostConfig struct { - Binds []string + Binds []string + ContainerIDFile string } type BindMap struct { @@ -103,6 +104,7 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig, flStdin := cmd.Bool("i", false, "Keep stdin open even if not attached") flTty := cmd.Bool("t", false, "Allocate a pseudo-tty") flMemory := cmd.Int64("m", 0, "Memory limit (in bytes)") + flContainerIDFile := cmd.String("cidfile", "", "Write the container ID to the file") if capabilities != nil && *flMemory > 0 && !capabilities.MemoryLimit { //fmt.Fprintf(stdout, "WARNING: Your kernel does not support memory limit capabilities. Limitation discarded.\n") @@ -190,7 +192,8 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig, Entrypoint: entrypoint, } hostConfig := &HostConfig{ - Binds: binds, + Binds: binds, + ContainerIDFile: *flContainerIDFile, } if capabilities != nil && *flMemory > 0 && !capabilities.SwapLimit { From ce2e737919772137afa268bd944e55c176f28ccc Mon Sep 17 00:00:00 2001 From: unclejack Date: Thu, 11 Jul 2013 23:38:43 +0300 Subject: [PATCH 59/62] docs - add cidfile flag to run docs Upstream-commit: 221ee504aa06d06eb868898cca2fcc020a861e84 Component: engine --- components/engine/docs/sources/commandline/command/run.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/components/engine/docs/sources/commandline/command/run.rst b/components/engine/docs/sources/commandline/command/run.rst index 1a14a9b616..bfd35738fa 100644 --- a/components/engine/docs/sources/commandline/command/run.rst +++ b/components/engine/docs/sources/commandline/command/run.rst @@ -14,6 +14,7 @@ -a=map[]: Attach to stdin, stdout or stderr. -c=0: CPU shares (relative weight) + -cidfile="": Write the container ID to the file -d=false: Detached mode: leave the container running in the background -e=[]: Set environment variables -h="": Container host name From 59b7a438c9d2201429d643a94740cba1fbd34748 Mon Sep 17 00:00:00 2001 From: unclejack Date: Fri, 12 Jul 2013 00:50:03 +0300 Subject: [PATCH 60/62] docs - add example for cidfile Upstream-commit: 2a3b91e3b66c48c6a26dbd673957a46c1afacbbe Component: engine --- .../engine/docs/sources/commandline/command/run.rst | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/components/engine/docs/sources/commandline/command/run.rst b/components/engine/docs/sources/commandline/command/run.rst index bfd35738fa..19efd85821 100644 --- a/components/engine/docs/sources/commandline/command/run.rst +++ b/components/engine/docs/sources/commandline/command/run.rst @@ -27,3 +27,13 @@ -v=[]: Create a bind mount with: [host-dir]:[container-dir]:[rw|ro]. If "host-dir" is missing, then docker creates a new volume. -volumes-from="": Mount all volumes from the given container. -entrypoint="": Overwrite the default entrypoint set by the image. + + +Examples +-------- + +.. code-block:: bash + + docker run -cidfile /tmp/docker_test.cid ubuntu echo "test" + +| This will create a container and print "test" to the console. The cidfile flag makes docker attempt to create a new file and write the container ID to it. If the file exists already, docker will return an error. Docker will close this file when docker run exits. From 1b1ac70ce1e733a2c7a93c8933f0fded1a69af5d Mon Sep 17 00:00:00 2001 From: unclejack Date: Fri, 12 Jul 2013 01:11:44 +0300 Subject: [PATCH 61/62] create the cidfile before creating the container This change makes docker attempt to create the container ID file and open it before attempting to create the container. This avoids leaving a stale container behind if docker has failed to create and open the container ID file. The container ID is written to the file after the container is created. Upstream-commit: 25be79208a1473a65be883989ae49b7c71081a83 Component: engine --- components/engine/commands.go | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/components/engine/commands.go b/components/engine/commands.go index 42cccf6b96..7f8f9eec0b 100644 --- a/components/engine/commands.go +++ b/components/engine/commands.go @@ -1311,6 +1311,18 @@ func (cli *DockerCli) CmdRun(args ...string) error { return nil } + var containerIDFile *os.File + if len(hostConfig.ContainerIDFile) > 0 { + if _, err := ioutil.ReadFile(hostConfig.ContainerIDFile); err == nil { + return fmt.Errorf("cid file found, make sure the other container isn't running or delete %s", hostConfig.ContainerIDFile) + } + containerIDFile, err = os.Create(hostConfig.ContainerIDFile) + if err != nil { + return fmt.Errorf("failed to create the container ID file: %s", err) + } + defer containerIDFile.Close() + } + //create the container body, statusCode, err := cli.call("POST", "/containers/create", config) //if image not found try to pull it @@ -1342,16 +1354,7 @@ func (cli *DockerCli) CmdRun(args ...string) error { fmt.Fprintf(cli.err, "WARNING: %s\n", warning) } if len(hostConfig.ContainerIDFile) > 0 { - if _, err := ioutil.ReadFile(hostConfig.ContainerIDFile); err == nil { - return fmt.Errorf("cid file found, make sure the other container isn't running or delete %s", hostConfig.ContainerIDFile) - } - file, err := os.Create(hostConfig.ContainerIDFile) - if err != nil { - return fmt.Errorf("failed to create the container ID file: %s", err) - } - - defer file.Close() - if _, err = file.WriteString(runResult.ID); err != nil { + if _, err = containerIDFile.WriteString(runResult.ID); err != nil { return fmt.Errorf("failed to write the container ID to the file: %s", err) } } From f380201be8fc9f5173d89ad5b7188733cd93390f Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Fri, 19 Jul 2013 13:56:36 +0000 Subject: [PATCH 62/62] fix error in utils tests Upstream-commit: 2e3b660dd0d49dc78f4c486e952ea6db9c007d6a Component: engine --- components/engine/utils/utils_test.go | 31 ++++++++------------------- 1 file changed, 9 insertions(+), 22 deletions(-) diff --git a/components/engine/utils/utils_test.go b/components/engine/utils/utils_test.go index 2c68be50e3..5caa809f67 100644 --- a/components/engine/utils/utils_test.go +++ b/components/engine/utils/utils_test.go @@ -60,9 +60,9 @@ func TestWriteBroadcaster(t *testing.T) { // Test 1: Both bufferA and bufferB should contain "foo" bufferA := &dummyWriter{} - writer.AddWriter(bufferA) + writer.AddWriter(bufferA, "") bufferB := &dummyWriter{} - writer.AddWriter(bufferB) + writer.AddWriter(bufferB, "") writer.Write([]byte("foo")) if bufferA.String() != "foo" { @@ -76,7 +76,7 @@ func TestWriteBroadcaster(t *testing.T) { // Test2: bufferA and bufferB should contain "foobar", // while bufferC should only contain "bar" bufferC := &dummyWriter{} - writer.AddWriter(bufferC) + writer.AddWriter(bufferC, "") writer.Write([]byte("bar")) if bufferA.String() != "foobar" { @@ -91,35 +91,22 @@ func TestWriteBroadcaster(t *testing.T) { t.Errorf("Buffer contains %v", bufferC.String()) } - // Test3: Test removal - writer.RemoveWriter(bufferB) - writer.Write([]byte("42")) - if bufferA.String() != "foobar42" { - t.Errorf("Buffer contains %v", bufferA.String()) - } - if bufferB.String() != "foobar" { - t.Errorf("Buffer contains %v", bufferB.String()) - } - if bufferC.String() != "bar42" { - t.Errorf("Buffer contains %v", bufferC.String()) - } - - // Test4: Test eviction on failure + // Test3: Test eviction on failure bufferA.failOnWrite = true writer.Write([]byte("fail")) - if bufferA.String() != "foobar42" { + if bufferA.String() != "foobar" { t.Errorf("Buffer contains %v", bufferA.String()) } - if bufferC.String() != "bar42fail" { + if bufferC.String() != "barfail" { t.Errorf("Buffer contains %v", bufferC.String()) } // Even though we reset the flag, no more writes should go in there bufferA.failOnWrite = false writer.Write([]byte("test")) - if bufferA.String() != "foobar42" { + if bufferA.String() != "foobar" { t.Errorf("Buffer contains %v", bufferA.String()) } - if bufferC.String() != "bar42failtest" { + if bufferC.String() != "barfailtest" { t.Errorf("Buffer contains %v", bufferC.String()) } @@ -141,7 +128,7 @@ func TestRaceWriteBroadcaster(t *testing.T) { writer := NewWriteBroadcaster() c := make(chan bool) go func() { - writer.AddWriter(devNullCloser(0)) + writer.AddWriter(devNullCloser(0), "") c <- true }() writer.Write([]byte("hello"))