From f355d33b5fe37ce7c0c25373255ea8afd931f4e7 Mon Sep 17 00:00:00 2001 From: "Guillaume J. Charmes" Date: Thu, 6 Jun 2013 18:16:16 -0700 Subject: [PATCH 01/31] Make the progressbar take the image size into consideration --- registry/registry.go | 30 ++++++++++++++++++------------ server.go | 6 +++--- 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/registry/registry.go b/registry/registry.go index bd5c6b79c..a2b43eeda 100644 --- a/registry/registry.go +++ b/registry/registry.go @@ -12,6 +12,7 @@ import ( "io/ioutil" "net/http" "net/url" + "strconv" "strings" ) @@ -106,40 +107,45 @@ func (r *Registry) getImagesInRepository(repository string, authConfig *auth.Aut } // Retrieve an image from the Registry. -// Returns the Image object as well as the layer as an Archive (io.Reader) -func (r *Registry) GetRemoteImageJSON(imgId, registry string, token []string) ([]byte, error) { +func (r *Registry) GetRemoteImageJSON(imgId, registry string, token []string) ([]byte, int, error) { // Get the JSON req, err := http.NewRequest("GET", registry+"/images/"+imgId+"/json", nil) if err != nil { - return nil, fmt.Errorf("Failed to download json: %s", err) + return nil, -1, fmt.Errorf("Failed to download json: %s", err) } req.Header.Set("Authorization", "Token "+strings.Join(token, ", ")) res, err := r.client.Do(req) if err != nil { - return nil, fmt.Errorf("Failed to download json: %s", err) + return nil, -1, fmt.Errorf("Failed to download json: %s", err) } defer res.Body.Close() if res.StatusCode != 200 { - return nil, fmt.Errorf("HTTP code %d", res.StatusCode) + return nil, -1, fmt.Errorf("HTTP code %d", res.StatusCode) } + + imageSize, err := strconv.Atoi(res.Header.Get("X-Docker-Size")) + if err != nil { + return nil, -1, err + } + jsonString, err := ioutil.ReadAll(res.Body) if err != nil { - return nil, fmt.Errorf("Failed to parse downloaded json: %s (%s)", err, jsonString) + return nil, -1, fmt.Errorf("Failed to parse downloaded json: %s (%s)", err, jsonString) } - return jsonString, nil + return jsonString, imageSize, nil } -func (r *Registry) GetRemoteImageLayer(imgId, registry string, token []string) (io.ReadCloser, int, error) { +func (r *Registry) GetRemoteImageLayer(imgId, registry string, token []string) (io.ReadCloser, error) { req, err := http.NewRequest("GET", registry+"/images/"+imgId+"/layer", nil) if err != nil { - return nil, -1, fmt.Errorf("Error while getting from the server: %s\n", err) + return nil, fmt.Errorf("Error while getting from the server: %s\n", err) } req.Header.Set("Authorization", "Token "+strings.Join(token, ", ")) res, err := r.client.Do(req) if err != nil { - return nil, -1, err + return nil, err } - return res.Body, int(res.ContentLength), nil + return res.Body, nil } func (r *Registry) GetRemoteTags(registries []string, repository string, token []string) (map[string]string, error) { @@ -479,7 +485,7 @@ func NewRegistry(root string) *Registry { httpTransport := &http.Transport{ DisableKeepAlives: true, - Proxy: http.ProxyFromEnvironment, + Proxy: http.ProxyFromEnvironment, } r := &Registry{ diff --git a/server.go b/server.go index 666612365..37a053abb 100644 --- a/server.go +++ b/server.go @@ -305,7 +305,7 @@ func (srv *Server) pullImage(r *registry.Registry, out io.Writer, imgId, endpoin for _, id := range history { if !srv.runtime.graph.Exists(id) { out.Write(sf.FormatStatus("Pulling %s metadata", id)) - imgJSON, err := r.GetRemoteImageJSON(id, endpoint, token) + imgJSON, imgSize, err := r.GetRemoteImageJSON(id, endpoint, token) if err != nil { // FIXME: Keep goging in case of error? return err @@ -317,12 +317,12 @@ func (srv *Server) pullImage(r *registry.Registry, out io.Writer, imgId, endpoin // Get the layer out.Write(sf.FormatStatus("Pulling %s fs layer", id)) - layer, contentLength, err := r.GetRemoteImageLayer(img.ID, endpoint, token) + layer, err := r.GetRemoteImageLayer(img.ID, endpoint, token) if err != nil { return err } defer layer.Close() - if err := srv.runtime.graph.Register(utils.ProgressReader(layer, contentLength, 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", "%v/%v (%v)"), sf), false, img); err != nil { return err } } From 1e0738f63f55d489d3d96274f312c93cc5d69ffa Mon Sep 17 00:00:00 2001 From: "Guillaume J. Charmes" Date: Thu, 6 Jun 2013 18:42:52 -0700 Subject: [PATCH 02/31] Make the progressbar human readable --- utils/utils.go | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/utils/utils.go b/utils/utils.go index c3f9e571d..b92b30633 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -70,7 +70,7 @@ type progressReader struct { readProgress int // How much has been read so far (bytes) lastUpdate int // How many bytes read at least update template string // Template to print. Default "%v/%v (%v)" - sf *StreamFormatter + sf *StreamFormatter } func (r *progressReader) Read(p []byte) (n int, err error) { @@ -86,7 +86,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, r.readProgress, r.readTotal, fmt.Sprintf("%.0f%%", float64(r.readProgress)/float64(r.readTotal)*100)) + fmt.Fprintf(r.output, r.template, HumanSize(r.readProgress), HumanSize(r.readTotal), fmt.Sprintf("%.0f%%", float64(r.readProgress)/float64(r.readTotal)*100)) } else { fmt.Fprintf(r.output, r.template, r.readProgress, "?", "n/a") } @@ -103,13 +103,25 @@ func (r *progressReader) Close() error { return io.ReadCloser(r.reader).Close() } func ProgressReader(r io.ReadCloser, size int, output io.Writer, template []byte, sf *StreamFormatter) *progressReader { - tpl := string(template) + tpl := string(template) if tpl == "" { tpl = string(sf.FormatProgress("", "%v/%v (%v)")) } return &progressReader{r, NewWriteFlusher(output), size, 0, 0, tpl, sf} } +func HumanSize(origSize int) string { + size := float64(origSize) + for _, unit := range []string{"b", "Kb", "Mb", "Gb", "Tb"} { + if int(size)/1024 == 0 { + return fmt.Sprintf("%.03f%s", size, unit) + } else { + size = size / 1024 + } + } + return strconv.Itoa(origSize) +} + // HumanDuration returns a human-readable approximation of a duration // (eg. "About a minute", "4 hours ago", etc.) func HumanDuration(d time.Duration) string { @@ -585,7 +597,7 @@ func (sf *StreamFormatter) FormatStatus(format string, a ...interface{}) []byte sf.used = true str := fmt.Sprintf(format, a...) if sf.json { - b, err := json.Marshal(&JSONMessage{Status:str}); + b, err := json.Marshal(&JSONMessage{Status: str}) if err != nil { return sf.FormatError(err) } @@ -597,7 +609,7 @@ func (sf *StreamFormatter) FormatStatus(format string, a ...interface{}) []byte func (sf *StreamFormatter) FormatError(err error) []byte { sf.used = true if sf.json { - if b, err := json.Marshal(&JSONMessage{Error:err.Error()}); err == nil { + if b, err := json.Marshal(&JSONMessage{Error: err.Error()}); err == nil { return b } return []byte("{\"error\":\"format error\"}") @@ -608,10 +620,10 @@ func (sf *StreamFormatter) FormatError(err error) []byte { func (sf *StreamFormatter) FormatProgress(action, str string) []byte { sf.used = true if sf.json { - b, err := json.Marshal(&JSONMessage{Status: action, Progress:str}) + b, err := json.Marshal(&JSONMessage{Status: action, Progress: str}) if err != nil { - return nil - } + return nil + } return b } return []byte(action + " " + str + "\r") From 0425f65e6373dc38cd18a6d0f2a50671544ad4b2 Mon Sep 17 00:00:00 2001 From: "Guillaume J. Charmes" Date: Thu, 13 Jun 2013 17:53:38 -0700 Subject: [PATCH 03/31] Remove bsdtar by checking magic --- archive.go | 48 +++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 45 insertions(+), 3 deletions(-) diff --git a/archive.go b/archive.go index 06466627a..568640817 100644 --- a/archive.go +++ b/archive.go @@ -1,8 +1,10 @@ package docker import ( + "bytes" "errors" "fmt" + "github.com/dotcloud/docker/utils" "io" "io/ioutil" "os" @@ -20,6 +22,37 @@ const ( Xz ) +func DetectCompression(source []byte) Compression { + for _, c := range source[:10] { + utils.Debugf("%x", c) + } + + sourceLen := len(source) + for compression, m := range map[Compression][]byte{ + Bzip2: {0x42, 0x5A, 0x68}, + Gzip: {0x1F, 0x8B, 0x08}, + Xz: {0xFD, 0x37, 0x7A, 0x58, 0x5A, 0x00}, + } { + fail := false + if len(m) > sourceLen { + utils.Debugf("Len too short") + continue + } + i := 0 + for _, b := range m { + if b != source[i] { + fail = true + break + } + i++ + } + if !fail { + return compression + } + } + return Uncompressed +} + func (compression *Compression) Flag() string { switch *compression { case Bzip2: @@ -47,12 +80,21 @@ func (compression *Compression) Extension() string { } func Tar(path string, compression Compression) (io.Reader, error) { - cmd := exec.Command("bsdtar", "-f", "-", "-C", path, "-c"+compression.Flag(), ".") - return CmdStream(cmd) + return CmdStream(exec.Command("tar", "-f", "-", "-C", path, "-c"+compression.Flag(), ".")) } func Untar(archive io.Reader, path string) error { - cmd := exec.Command("bsdtar", "-f", "-", "-C", path, "-x") + + buf := make([]byte, 10) + if _, err := archive.Read(buf); err != nil { + return err + } + compression := DetectCompression(buf) + archive = io.MultiReader(bytes.NewReader(buf), archive) + + utils.Debugf("Archive compression detected: %s", compression.Extension()) + + cmd := exec.Command("tar", "-f", "-", "-C", path, "-x"+compression.Flag()) cmd.Stdin = archive // Hardcode locale environment for predictable outcome regardless of host configuration. // (see https://github.com/dotcloud/docker/issues/355) From 6f7de49aa8771ef4bc35548f9aa07fee660f1844 Mon Sep 17 00:00:00 2001 From: "Guillaume J. Charmes" Date: Fri, 14 Jun 2013 10:47:49 -0700 Subject: [PATCH 04/31] Add unit tests for tar/untar with multiple compression + detection --- archive_test.go | 51 ++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 46 insertions(+), 5 deletions(-) diff --git a/archive_test.go b/archive_test.go index f58360449..bb4235ad5 100644 --- a/archive_test.go +++ b/archive_test.go @@ -1,10 +1,13 @@ package docker import ( + "bytes" + "fmt" "io" "io/ioutil" "os" "os/exec" + "path" "testing" "time" ) @@ -58,20 +61,58 @@ func TestCmdStreamGood(t *testing.T) { } } -func TestTarUntar(t *testing.T) { - archive, err := Tar(".", Uncompressed) +func tarUntar(t *testing.T, origin string, compression Compression) error { + archive, err := Tar(origin, compression) if err != nil { t.Fatal(err) } + + buf := make([]byte, 10) + if _, err := archive.Read(buf); err != nil { + return err + } + archive = io.MultiReader(bytes.NewReader(buf), archive) + + detectedCompression := DetectCompression(buf) + if detectedCompression.Extension() != compression.Extension() { + return fmt.Errorf("Wrong compression detected. Actual compression: %s, found %s", compression.Extension(), detectedCompression.Extension()) + } + tmp, err := ioutil.TempDir("", "docker-test-untar") if err != nil { - t.Fatal(err) + return err } defer os.RemoveAll(tmp) if err := Untar(archive, tmp); err != nil { - t.Fatal(err) + return err } if _, err := os.Stat(tmp); err != nil { - t.Fatalf("Error stating %s: %s", tmp, err.Error()) + return err + } + return nil +} + +func TestTarUntar(t *testing.T) { + origin, err := ioutil.TempDir("", "docker-test-untar-origin") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(origin) + if err := ioutil.WriteFile(path.Join(origin, "1"), []byte("hello world"), 0700); err != nil { + t.Fatal(err) + } + if err := ioutil.WriteFile(path.Join(origin, "2"), []byte("welcome!"), 0700); err != nil { + t.Fatal(err) + } + + for _, c := range []Compression{ + Uncompressed, + Gzip, + Bzip2, + Xz, + } { + if err := tarUntar(t, origin, c); err != nil { + t.Fatalf("Error tar/untar for compression %s: %s", c.Extension(), err) + } } } From 79fe864d9a117193b4dce8b7fe156a55bbadcce6 Mon Sep 17 00:00:00 2001 From: "Guillaume J. Charmes" Date: Fri, 14 Jun 2013 10:58:16 -0700 Subject: [PATCH 05/31] Update docs --- README.md | 2 +- docs/sources/contributing/devenvironment.rst | 2 +- docs/sources/installation/binaries.rst | 3 +-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 1c909e543..d15ee3cc4 100644 --- a/README.md +++ b/README.md @@ -262,7 +262,7 @@ Setting up a dev environment Instructions that have been verified to work on Ubuntu 12.10, ```bash -sudo apt-get -y install lxc wget bsdtar curl golang git +sudo apt-get -y install lxc curl xz-utils golang git export GOPATH=~/go/ export PATH=$GOPATH/bin:$PATH diff --git a/docs/sources/contributing/devenvironment.rst b/docs/sources/contributing/devenvironment.rst index 5d937c5a4..8b26688bc 100644 --- a/docs/sources/contributing/devenvironment.rst +++ b/docs/sources/contributing/devenvironment.rst @@ -33,7 +33,7 @@ Installation sudo apt-get install python-software-properties sudo add-apt-repository ppa:gophers/go sudo apt-get update - sudo apt-get -y install lxc wget bsdtar curl golang-stable git + sudo apt-get -y install lxc xz-utils curl golang-stable git export GOPATH=~/go/ export PATH=$GOPATH/bin:$PATH diff --git a/docs/sources/installation/binaries.rst b/docs/sources/installation/binaries.rst index e7a07b6db..6d8778775 100644 --- a/docs/sources/installation/binaries.rst +++ b/docs/sources/installation/binaries.rst @@ -30,8 +30,7 @@ Dependencies: * 3.8 Kernel (read more about :ref:`kernel`) * AUFS filesystem support * lxc -* bsdtar - +* xz-utils Get the docker binary: ---------------------- From 76a568fc9717ff69999ab54fba9277a0d31c305d Mon Sep 17 00:00:00 2001 From: "Guillaume J. Charmes" Date: Fri, 14 Jun 2013 16:08:08 -0700 Subject: [PATCH 06/31] Fix merge issue --- utils/utils.go | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/utils/utils.go b/utils/utils.go index 4c9f9eeee..7e95e074d 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -86,7 +86,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(r.readProgress), HumanSize(r.readTotal), fmt.Sprintf("%.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") } @@ -110,18 +110,6 @@ func ProgressReader(r io.ReadCloser, size int, output io.Writer, template []byte return &progressReader{r, NewWriteFlusher(output), size, 0, 0, tpl, sf} } -func HumanSize(origSize int) string { - size := float64(origSize) - for _, unit := range []string{"b", "Kb", "Mb", "Gb", "Tb"} { - if int(size)/1024 == 0 { - return fmt.Sprintf("%.03f%s", size, unit) - } else { - size = size / 1024 - } - } - return strconv.Itoa(origSize) -} - // HumanDuration returns a human-readable approximation of a duration // (eg. "About a minute", "4 hours ago", etc.) func HumanDuration(d time.Duration) string { From 13e03a691145921ffc17c56db24f06eacca99a77 Mon Sep 17 00:00:00 2001 From: "Guillaume J. Charmes" Date: Mon, 17 Jun 2013 11:29:02 -0700 Subject: [PATCH 07/31] Fix the auth tests and add the offline mode --- auth/auth_test.go | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/auth/auth_test.go b/auth/auth_test.go index 6c8d032cf..e49ec0372 100644 --- a/auth/auth_test.go +++ b/auth/auth_test.go @@ -10,8 +10,8 @@ import ( func TestEncodeAuth(t *testing.T) { newAuthConfig := &AuthConfig{Username: "ken", Password: "test", Email: "test@example.com"} - authStr := EncodeAuth(newAuthConfig) - decAuthConfig, err := DecodeAuth(authStr) + authStr := encodeAuth(newAuthConfig) + decAuthConfig, err := decodeAuth(authStr) if err != nil { t.Fatal(err) } @@ -27,10 +27,13 @@ func TestEncodeAuth(t *testing.T) { } func TestLogin(t *testing.T) { + if os.Getenv("OFFLINE") != "" { + t.Skip("Offline mode, skipping.") + } os.Setenv("DOCKER_INDEX_URL", "https://indexstaging-docker.dotcloud.com") defer os.Setenv("DOCKER_INDEX_URL", "") authConfig := NewAuthConfig("unittester", "surlautrerivejetattendrai", "noise+unittester@dotcloud.com", "/tmp") - status, err := Login(authConfig) + status, err := Login(authConfig, false) if err != nil { t.Fatal(err) } @@ -40,6 +43,9 @@ func TestLogin(t *testing.T) { } func TestCreateAccount(t *testing.T) { + if os.Getenv("OFFLINE") != "" { + t.Skip("Offline mode, skipping.") + } os.Setenv("DOCKER_INDEX_URL", "https://indexstaging-docker.dotcloud.com") defer os.Setenv("DOCKER_INDEX_URL", "") tokenBuffer := make([]byte, 16) @@ -50,7 +56,7 @@ func TestCreateAccount(t *testing.T) { token := hex.EncodeToString(tokenBuffer)[:12] username := "ut" + token authConfig := NewAuthConfig(username, "test42", "docker-ut+"+token+"@example.com", "/tmp") - status, err := Login(authConfig) + status, err := Login(authConfig, false) if err != nil { t.Fatal(err) } @@ -60,7 +66,7 @@ func TestCreateAccount(t *testing.T) { t.Fatalf("Expected status: \"%s\", found \"%s\" instead.", expectedStatus, status) } - status, err = Login(authConfig) + status, err = Login(authConfig, false) if err == nil { t.Fatalf("Expected error but found nil instead") } From 3a0ffbc77267e395676860db265ee3476c45b3c2 Mon Sep 17 00:00:00 2001 From: "Guillaume J. Charmes" Date: Mon, 17 Jun 2013 14:44:35 -0700 Subject: [PATCH 08/31] - Runtime: Fixes #884 enforce stdout/err sync by merging the stream --- commands.go | 28 +++++++--------------------- 1 file changed, 7 insertions(+), 21 deletions(-) diff --git a/commands.go b/commands.go index ce15fd6cf..abe91f6ea 100644 --- a/commands.go +++ b/commands.go @@ -1058,37 +1058,23 @@ func (cli *DockerCli) CmdAttach(args ...string) error { return err } - splitStderr := container.Config.Tty - - connections := 1 - if splitStderr { - connections += 1 - } - chErrors := make(chan error, connections) + chErrors := make(chan error) if container.Config.Tty { cli.monitorTtySize(cmd.Arg(0)) } - if splitStderr { - go func() { - chErrors <- cli.hijack("POST", "/containers/"+cmd.Arg(0)+"/attach?stream=1&stderr=1", false, nil, os.Stderr) - }() - } + v := url.Values{} v.Set("stream", "1") v.Set("stdin", "1") v.Set("stdout", "1") - if !splitStderr { - v.Set("stderr", "1") - } + v.Set("stderr", "1") + go func() { chErrors <- cli.hijack("POST", "/containers/"+cmd.Arg(0)+"/attach?"+v.Encode(), container.Config.Tty, os.Stdin, os.Stdout) }() - for connections > 0 { - err := <-chErrors - if err != nil { - return err - } - connections -= 1 + + if err := <-chErrors; err != nil { + return err } return nil } From c106ed32ea7613573a2081d47ad2498429ac86f2 Mon Sep 17 00:00:00 2001 From: "Guillaume J. Charmes" Date: Mon, 17 Jun 2013 15:40:04 -0700 Subject: [PATCH 09/31] Move the attach prevention from server to client --- commands.go | 4 ++++ server.go | 3 --- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/commands.go b/commands.go index ce15fd6cf..4297ac0c1 100644 --- a/commands.go +++ b/commands.go @@ -1058,6 +1058,10 @@ func (cli *DockerCli) CmdAttach(args ...string) error { return err } + if !container.State.Running { + return fmt.Errorf("Impossible to attach to a stopped container, start it first") + } + splitStderr := container.Config.Tty connections := 1 diff --git a/server.go b/server.go index 30e3ec6b3..ece6a93ce 100644 --- a/server.go +++ b/server.go @@ -930,9 +930,6 @@ func (srv *Server) ContainerAttach(name string, logs, stream, stdin, stdout, std if container.State.Ghost { return fmt.Errorf("Impossible to attach to a ghost container") } - if !container.State.Running { - return fmt.Errorf("Impossible to attach to a stopped container, start it first") - } var ( cStdin io.ReadCloser From 2b6ca3872883dcb487d8a39a1a8530be6a62f947 Mon Sep 17 00:00:00 2001 From: "Guillaume J. Charmes" Date: Mon, 17 Jun 2013 15:45:08 -0700 Subject: [PATCH 10/31] Remove Run race condition --- commands.go | 33 ++++++--------------------------- 1 file changed, 6 insertions(+), 27 deletions(-) diff --git a/commands.go b/commands.go index 4297ac0c1..fb8bb528b 100644 --- a/commands.go +++ b/commands.go @@ -1261,16 +1261,6 @@ func (cli *DockerCli) CmdRun(args ...string) error { fmt.Fprintln(os.Stderr, "WARNING: ", warning) } - splitStderr := !config.Tty - - connections := 0 - if config.AttachStdin || config.AttachStdout || (!splitStderr && config.AttachStderr) { - connections += 1 - } - if splitStderr && config.AttachStderr { - connections += 1 - } - //start the container _, _, err = cli.call("POST", "/containers/"+out.ID+"/start", nil) if err != nil { @@ -1279,19 +1269,12 @@ func (cli *DockerCli) CmdRun(args ...string) error { if !config.AttachStdout && !config.AttachStderr { fmt.Println(out.ID) - } - if connections > 0 { - chErrors := make(chan error, connections) + } else { + chErrors := make(chan error) if config.Tty { cli.monitorTtySize(out.ID) } - if splitStderr && config.AttachStderr { - go func() { - chErrors <- cli.hijack("POST", "/containers/"+out.ID+"/attach?logs=1&stream=1&stderr=1", config.Tty, nil, os.Stderr) - }() - } - v := url.Values{} v.Set("logs", "1") v.Set("stream", "1") @@ -1302,19 +1285,15 @@ func (cli *DockerCli) CmdRun(args ...string) error { if config.AttachStdout { v.Set("stdout", "1") } - if !splitStderr && config.AttachStderr { + if config.AttachStderr { v.Set("stderr", "1") } go func() { chErrors <- cli.hijack("POST", "/containers/"+out.ID+"/attach?"+v.Encode(), config.Tty, os.Stdin, os.Stdout) }() - for connections > 0 { - err := <-chErrors - if err != nil { - utils.Debugf("Error hijack: %s", err) - return err - } - connections -= 1 + if err := <-chErrors; err != nil { + utils.Debugf("Error hijack: %s", err) + return err } } return nil From fe204e6f48eb47a1deb3553003eaf9863a66fd1a Mon Sep 17 00:00:00 2001 From: "Guillaume J. Charmes" Date: Mon, 17 Jun 2013 16:10:00 -0700 Subject: [PATCH 11/31] - Runtime: Forbid parralel push/pull for a single image/repo. Fixes #311 --- runtime_test.go | 6 ++++- server.go | 62 +++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 62 insertions(+), 6 deletions(-) diff --git a/runtime_test.go b/runtime_test.go index d7d9a5a31..db6367dfa 100644 --- a/runtime_test.go +++ b/runtime_test.go @@ -65,7 +65,11 @@ func init() { // Create the "Server" srv := &Server{ - runtime: runtime, + runtime: runtime, + enableCors: false, + lock: &sync.Mutex{}, + pullingPool: make(map[string]struct{}), + pushingPool: make(map[string]struct{}), } // Retrieve the Image if err := srv.ImagePull(unitTestImageName, "", "", os.Stdout, utils.NewStreamFormatter(false), nil); err != nil { diff --git a/server.go b/server.go index 30e3ec6b3..34040df3a 100644 --- a/server.go +++ b/server.go @@ -15,6 +15,7 @@ import ( "path" "runtime" "strings" + "sync" ) func (srv *Server) DockerVersion() APIVersion { @@ -401,7 +402,47 @@ func (srv *Server) pullRepository(r *registry.Registry, out io.Writer, local, re return nil } +func (srv *Server) poolAdd(kind, key string) error { + srv.lock.Lock() + defer srv.lock.Unlock() + + if _, exists := srv.pullingPool[key]; exists { + return fmt.Errorf("%s %s is already in progress", key, kind) + } + + switch kind { + case "pull": + srv.pullingPool[key] = struct{}{} + break + case "push": + srv.pushingPool[key] = struct{}{} + break + default: + return fmt.Errorf("Unkown pool type") + } + return nil +} + +func (srv *Server) poolRemove(kind, key string) error { + switch kind { + case "pull": + delete(srv.pullingPool, key) + break + case "push": + delete(srv.pushingPool, key) + break + default: + return fmt.Errorf("Unkown pool type") + } + return nil +} + func (srv *Server) ImagePull(name, tag, endpoint string, out io.Writer, sf *utils.StreamFormatter, authConfig *auth.AuthConfig) error { + if err := srv.poolAdd("pull", name+":"+tag); err != nil { + return err + } + defer srv.poolRemove("pull", name+":"+tag) + r := registry.NewRegistry(srv.runtime.root, authConfig) out = utils.NewWriteFlusher(out) if endpoint != "" { @@ -418,7 +459,6 @@ func (srv *Server) ImagePull(name, tag, endpoint string, out io.Writer, sf *util if err := srv.pullRepository(r, out, name, remote, tag, sf); err != nil { return err } - return nil } @@ -593,7 +633,13 @@ func (srv *Server) pushImage(r *registry.Registry, out io.Writer, remote, imgId, return nil } +// FIXME: Allow to interupt current push when new push of same image is done. func (srv *Server) ImagePush(name, endpoint string, out io.Writer, sf *utils.StreamFormatter, authConfig *auth.AuthConfig) error { + if err := srv.poolAdd("push", name); err != nil { + return err + } + defer srv.poolRemove("push", name) + out = utils.NewWriteFlusher(out) img, err := srv.runtime.graph.Get(name) r := registry.NewRegistry(srv.runtime.root, authConfig) @@ -991,14 +1037,20 @@ func NewServer(autoRestart, enableCors bool, dns ListOpts) (*Server, error) { return nil, err } srv := &Server{ - runtime: runtime, - enableCors: enableCors, + runtime: runtime, + enableCors: enableCors, + lock: &sync.Mutex{}, + pullingPool: make(map[string]struct{}), + pushingPool: make(map[string]struct{}), } runtime.srv = srv return srv, nil } type Server struct { - runtime *Runtime - enableCors bool + runtime *Runtime + enableCors bool + lock *sync.Mutex + pullingPool map[string]struct{} + pushingPool map[string]struct{} } From 02c291d13be261f84cdfa1d51513bf4dba31ce72 Mon Sep 17 00:00:00 2001 From: "Guillaume J. Charmes" Date: Mon, 17 Jun 2013 18:11:58 -0700 Subject: [PATCH 12/31] Fix bug on compression detection when chunck < 10bytes --- archive.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/archive.go b/archive.go index 568640817..1e5b68376 100644 --- a/archive.go +++ b/archive.go @@ -1,7 +1,7 @@ package docker import ( - "bytes" + "bufio" "errors" "fmt" "github.com/dotcloud/docker/utils" @@ -85,17 +85,17 @@ func Tar(path string, compression Compression) (io.Reader, error) { func Untar(archive io.Reader, path string) error { - buf := make([]byte, 10) - if _, err := archive.Read(buf); err != nil { + bufferedArchive := bufio.NewReaderSize(archive, 10) + buf, err := bufferedArchive.Peek(10) + if err != nil { return err } compression := DetectCompression(buf) - archive = io.MultiReader(bytes.NewReader(buf), archive) utils.Debugf("Archive compression detected: %s", compression.Extension()) cmd := exec.Command("tar", "-f", "-", "-C", path, "-x"+compression.Flag()) - cmd.Stdin = archive + cmd.Stdin = bufferedArchive // Hardcode locale environment for predictable outcome regardless of host configuration. // (see https://github.com/dotcloud/docker/issues/355) cmd.Env = []string{"LANG=en_US.utf-8", "LC_ALL=en_US.utf-8"} From 8281a0fa1cea0199ab183c0925a41e79a18382dc Mon Sep 17 00:00:00 2001 From: Sam J Sharpe Date: Sun, 2 Jun 2013 22:08:41 +0100 Subject: [PATCH 13/31] Vagrantfile: Add support for VMWare Fusion provider As a user who has blown $150 on VMWare Fusion and vagrant-vmware, I would like to use my new shiny to hack on Docker. Docker already has a multi-provider Vagrantfile, so adding another one presents little risk. Known Issues: - The docker install of a new kernel breaks the Vagrant shared folder. - This seems to be because the VMWare hgfs module doesn't build against a 3.8 kernel. - I don't believe that shared folder support is actually in use --- Vagrantfile | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Vagrantfile b/Vagrantfile index 5b3a1f476..aadabb871 100644 --- a/Vagrantfile +++ b/Vagrantfile @@ -3,6 +3,7 @@ BOX_NAME = ENV['BOX_NAME'] || "ubuntu" BOX_URI = ENV['BOX_URI'] || "http://files.vagrantup.com/precise64.box" +VF_BOX_URI = ENV['BOX_URI'] || "http://files.vagrantup.com/precise64_vmware_fusion.box" AWS_REGION = ENV['AWS_REGION'] || "us-east-1" AWS_AMI = ENV['AWS_AMI'] || "ami-d0f89fb9" FORWARD_DOCKER_PORTS = ENV['FORWARD_DOCKER_PORTS'] @@ -67,6 +68,13 @@ Vagrant::VERSION >= "1.1.0" and Vagrant.configure("2") do |config| rs.image = /Ubuntu/ end + config.vm.provider :vmware_fusion do |f, override| + override.vm.box = BOX_NAME + override.vm.box_url = VF_BOX_URI + override.vm.synced_folder ".", "/vagrant", disabled: true + f.vmx["displayName"] = "docker" + end + config.vm.provider :virtualbox do |vb| config.vm.box = BOX_NAME config.vm.box_url = BOX_URI From e2d034e48858d0afe9ee0f88f04e40cbf95ab8ba Mon Sep 17 00:00:00 2001 From: "Guillaume J. Charmes" Date: Tue, 18 Jun 2013 10:06:26 -0700 Subject: [PATCH 14/31] Remove useless goroutine --- commands.go | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/commands.go b/commands.go index 847b5d3b3..0703f4ebe 100644 --- a/commands.go +++ b/commands.go @@ -1270,7 +1270,6 @@ func (cli *DockerCli) CmdRun(args ...string) error { if !config.AttachStdout && !config.AttachStderr { fmt.Println(out.ID) } else { - chErrors := make(chan error) if config.Tty { cli.monitorTtySize(out.ID) } @@ -1288,10 +1287,7 @@ func (cli *DockerCli) CmdRun(args ...string) error { if config.AttachStderr { v.Set("stderr", "1") } - go func() { - chErrors <- cli.hijack("POST", "/containers/"+out.ID+"/attach?"+v.Encode(), config.Tty, os.Stdin, os.Stdout) - }() - if err := <-chErrors; err != nil { + if err := cli.hijack("POST", "/containers/"+out.ID+"/attach?"+v.Encode(), config.Tty, os.Stdin, os.Stdout); err != nil { utils.Debugf("Error hijack: %s", err) return err } From 3dc93e390ad3d310dede84948b726ce67e261375 Mon Sep 17 00:00:00 2001 From: "Guillaume J. Charmes" Date: Tue, 18 Jun 2013 10:10:03 -0700 Subject: [PATCH 15/31] Remove useless goroutine --- commands.go | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/commands.go b/commands.go index abe91f6ea..19fb32f96 100644 --- a/commands.go +++ b/commands.go @@ -1058,7 +1058,6 @@ func (cli *DockerCli) CmdAttach(args ...string) error { return err } - chErrors := make(chan error) if container.Config.Tty { cli.monitorTtySize(cmd.Arg(0)) } @@ -1069,11 +1068,7 @@ func (cli *DockerCli) CmdAttach(args ...string) error { v.Set("stdout", "1") v.Set("stderr", "1") - go func() { - chErrors <- cli.hijack("POST", "/containers/"+cmd.Arg(0)+"/attach?"+v.Encode(), container.Config.Tty, os.Stdin, os.Stdout) - }() - - if err := <-chErrors; err != nil { + if err := cli.hijack("POST", "/containers/"+cmd.Arg(0)+"/attach?"+v.Encode(), container.Config.Tty, os.Stdin, os.Stdout); err != nil { return err } return nil From 6f511ac29b1c3cf1c2424b3c39ee14a32aecb5d7 Mon Sep 17 00:00:00 2001 From: Solomon Hykes Date: Tue, 18 Jun 2013 10:23:45 -0700 Subject: [PATCH 16/31] Remove bsdtar dependency in various install scripts --- contrib/install.sh | 2 +- hack/Vagrantfile | 2 +- testing/Vagrantfile | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/contrib/install.sh b/contrib/install.sh index 7db577a9d..cf097da67 100755 --- a/contrib/install.sh +++ b/contrib/install.sh @@ -8,7 +8,7 @@ echo "Ensuring basic dependencies are installed..." apt-get -qq update -apt-get -qq install lxc wget bsdtar +apt-get -qq install lxc wget echo "Looking in /proc/filesystems to see if we have AUFS support..." if grep -q aufs /proc/filesystems diff --git a/hack/Vagrantfile b/hack/Vagrantfile index 318f835f4..e02dfe06d 100644 --- a/hack/Vagrantfile +++ b/hack/Vagrantfile @@ -22,7 +22,7 @@ Vagrant::Config.run do |config| pkg_cmd = "touch #{DOCKER_PATH}; " # Install docker dependencies pkg_cmd << "export DEBIAN_FRONTEND=noninteractive; apt-get -qq update; " \ - "apt-get install -q -y lxc bsdtar git aufs-tools golang make linux-image-extra-3.8.0-19-generic; " \ + "apt-get install -q -y lxc git aufs-tools golang make linux-image-extra-3.8.0-19-generic; " \ "chown -R #{USER}.#{USER} #{GOPATH}; " \ "install -m 0664 #{CFG_PATH}/bash_profile /home/#{USER}/.bash_profile" config.vm.provision :shell, :inline => pkg_cmd diff --git a/testing/Vagrantfile b/testing/Vagrantfile index e304a8d08..f2f6ca824 100644 --- a/testing/Vagrantfile +++ b/testing/Vagrantfile @@ -30,7 +30,7 @@ Vagrant::Config.run do |config| # Install docker dependencies pkg_cmd << "apt-get install -q -y python-software-properties; " \ "add-apt-repository -y ppa:gophers/go/ubuntu; apt-get update -qq; " \ - "DEBIAN_FRONTEND=noninteractive apt-get install -q -y lxc bsdtar git golang-stable aufs-tools make; " + "DEBIAN_FRONTEND=noninteractive apt-get install -q -y lxc git golang-stable aufs-tools make; " # Activate new kernel pkg_cmd << "shutdown -r +1; " config.vm.provision :shell, :inline => pkg_cmd From c2e95997d4c4dda9edbb4adaab8920f1b5982c25 Mon Sep 17 00:00:00 2001 From: Elias Probst Date: Tue, 18 Jun 2013 19:55:59 +0200 Subject: [PATCH 17/31] Fixed #923 by replacing the usage of 'ifconfig' with 'ip a' where appropriate and added a note to use 'ip a' instead of 'ifconfig' for a screencast transscript. --- README.md | 3 ++- docs/sources/examples/running_redis_service.rst | 2 +- docs/sources/examples/running_ssh_service.rst | 2 ++ docs/sources/use/basics.rst | 3 ++- 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 1c909e543..05312a3b4 100644 --- a/README.md +++ b/README.md @@ -216,7 +216,8 @@ PORT=$(docker port $JOB 4444) # Connect to the public port via the host's public address # Please note that because of how routing works connecting to localhost or 127.0.0.1 $PORT will not work. -IP=$(ifconfig eth0 | perl -n -e 'if (m/inet addr:([\d\.]+)/g) { print $1 }') +# Replace *eth0* according to your local interface name. +IP=$(ip -o -4 addr list eth0 | perl -n -e 'if (m{inet\s([\d\.]+)\/\d+\s}xms) { print $1 }') echo hello world | nc $IP $PORT # Verify that the network connection worked diff --git a/docs/sources/examples/running_redis_service.rst b/docs/sources/examples/running_redis_service.rst index 48d083aa8..5f51fd33a 100644 --- a/docs/sources/examples/running_redis_service.rst +++ b/docs/sources/examples/running_redis_service.rst @@ -72,7 +72,7 @@ Connect to the host os with the redis-cli. docker ps # grab the new container id docker port 6379 # grab the external port - ifconfig # grab the host ip address + ip a s # grab the host ip address redis-cli -h -p redis 192.168.0.1:49153> set docker awesome OK diff --git a/docs/sources/examples/running_ssh_service.rst b/docs/sources/examples/running_ssh_service.rst index 6183c3a55..b32a648a7 100644 --- a/docs/sources/examples/running_ssh_service.rst +++ b/docs/sources/examples/running_ssh_service.rst @@ -59,6 +59,7 @@ The password is 'screencast' # it has now given us a port to connect to # we have to connect using a public ip of our host $ hostname + # *ifconfig* is deprecated, better use *ip a s* now $ ifconfig $ ssh root@192.168.33.10 -p 49153 # Ah! forgot to set root passwd @@ -70,6 +71,7 @@ The password is 'screencast' $ docker commit 9e863f0ca0af31c8b951048ba87641d67c382d08d655c2e4879c51410e0fedc1 dhrp/sshd $ docker run -d -p 22 dhrp/sshd /usr/sbin/sshd -D $ docker port a0aaa9558c90cf5c7782648df904a82365ebacce523e4acc085ac1213bfe2206 22 + # *ifconfig* is deprecated, better use *ip a s* now $ ifconfig $ ssh root@192.168.33.10 -p 49154 # Thanks for watching, Thatcher thatcher@dotcloud.com diff --git a/docs/sources/use/basics.rst b/docs/sources/use/basics.rst index 444b74db5..a8f7a9bad 100644 --- a/docs/sources/use/basics.rst +++ b/docs/sources/use/basics.rst @@ -82,7 +82,8 @@ Expose a service on a TCP port # Connect to the public port via the host's public address # Please note that because of how routing works connecting to localhost or 127.0.0.1 $PORT will not work. - IP=$(ifconfig eth0 | perl -n -e 'if (m/inet addr:([\d\.]+)/g) { print $1 }') + # Replace *eth0* according to your local interface name. + IP=$(ip -o -4 addr list eth0 | perl -n -e 'if (m{inet\s([\d\.]+)\/\d+\s}xms) { print $1 }') echo hello world | nc $IP $PORT # Verify that the network connection worked From bc9b91e501369264966263fc28bb516e6048b7e5 Mon Sep 17 00:00:00 2001 From: Elias Probst Date: Wed, 19 Jun 2013 00:57:43 +0200 Subject: [PATCH 18/31] Use the canonical 'ip' commands to make it easier for new 'iproute2' users to understand the usage. --- docs/sources/examples/running_redis_service.rst | 2 +- docs/sources/examples/running_ssh_service.rst | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/sources/examples/running_redis_service.rst b/docs/sources/examples/running_redis_service.rst index 5f51fd33a..4996802e9 100644 --- a/docs/sources/examples/running_redis_service.rst +++ b/docs/sources/examples/running_redis_service.rst @@ -72,7 +72,7 @@ Connect to the host os with the redis-cli. docker ps # grab the new container id docker port 6379 # grab the external port - ip a s # grab the host ip address + ip addr show # grab the host ip address redis-cli -h -p redis 192.168.0.1:49153> set docker awesome OK diff --git a/docs/sources/examples/running_ssh_service.rst b/docs/sources/examples/running_ssh_service.rst index b32a648a7..c2f8b86ac 100644 --- a/docs/sources/examples/running_ssh_service.rst +++ b/docs/sources/examples/running_ssh_service.rst @@ -59,7 +59,7 @@ The password is 'screencast' # it has now given us a port to connect to # we have to connect using a public ip of our host $ hostname - # *ifconfig* is deprecated, better use *ip a s* now + # *ifconfig* is deprecated, better use *ip addr show* now $ ifconfig $ ssh root@192.168.33.10 -p 49153 # Ah! forgot to set root passwd @@ -71,7 +71,7 @@ The password is 'screencast' $ docker commit 9e863f0ca0af31c8b951048ba87641d67c382d08d655c2e4879c51410e0fedc1 dhrp/sshd $ docker run -d -p 22 dhrp/sshd /usr/sbin/sshd -D $ docker port a0aaa9558c90cf5c7782648df904a82365ebacce523e4acc085ac1213bfe2206 22 - # *ifconfig* is deprecated, better use *ip a s* now + # *ifconfig* is deprecated, better use *ip addr show* now $ ifconfig $ ssh root@192.168.33.10 -p 49154 # Thanks for watching, Thatcher thatcher@dotcloud.com From 6dccdd657f715c164f2fe6fc786c8274a2425f1b Mon Sep 17 00:00:00 2001 From: "Guillaume J. Charmes" Date: Tue, 18 Jun 2013 17:09:47 -0700 Subject: [PATCH 19/31] remove offline mode from auth unit tests --- auth/auth_test.go | 6 ------ 1 file changed, 6 deletions(-) diff --git a/auth/auth_test.go b/auth/auth_test.go index e49ec0372..ead69e891 100644 --- a/auth/auth_test.go +++ b/auth/auth_test.go @@ -27,9 +27,6 @@ func TestEncodeAuth(t *testing.T) { } func TestLogin(t *testing.T) { - if os.Getenv("OFFLINE") != "" { - t.Skip("Offline mode, skipping.") - } os.Setenv("DOCKER_INDEX_URL", "https://indexstaging-docker.dotcloud.com") defer os.Setenv("DOCKER_INDEX_URL", "") authConfig := NewAuthConfig("unittester", "surlautrerivejetattendrai", "noise+unittester@dotcloud.com", "/tmp") @@ -43,9 +40,6 @@ func TestLogin(t *testing.T) { } func TestCreateAccount(t *testing.T) { - if os.Getenv("OFFLINE") != "" { - t.Skip("Offline mode, skipping.") - } os.Setenv("DOCKER_INDEX_URL", "https://indexstaging-docker.dotcloud.com") defer os.Setenv("DOCKER_INDEX_URL", "") tokenBuffer := make([]byte, 16) From 42ce68894a33a7d966f8dd767aee46dea6dbc346 Mon Sep 17 00:00:00 2001 From: "Guillaume J. Charmes" Date: Tue, 18 Jun 2013 17:22:32 -0700 Subject: [PATCH 20/31] Fix issue within TestDelete. The archive is now consumed by graph functions --- graph_test.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/graph_test.go b/graph_test.go index 8dedb9666..18682338d 100644 --- a/graph_test.go +++ b/graph_test.go @@ -192,11 +192,19 @@ func TestDelete(t *testing.T) { } assertNImages(graph, t, 0) + archive, err = fakeTar() + if err != nil { + t.Fatal(err) + } // Test 2 create (same name) / 1 delete img1, err := graph.Create(archive, nil, "Testing", "", nil) if err != nil { t.Fatal(err) } + archive, err = fakeTar() + if err != nil { + t.Fatal(err) + } if _, err = graph.Create(archive, nil, "Testing", "", nil); err != nil { t.Fatal(err) } @@ -212,6 +220,10 @@ func TestDelete(t *testing.T) { } assertNImages(graph, t, 1) + archive, err = fakeTar() + if err != nil { + t.Fatal(err) + } // Test delete twice (pull -> rm -> pull -> rm) if err := graph.Register(archive, false, img1); err != nil { t.Fatal(err) From 1f8b679b18984eeecc98d6de79a77cbd405cd56d Mon Sep 17 00:00:00 2001 From: Andrew Munsell Date: Tue, 18 Jun 2013 19:19:07 -0600 Subject: [PATCH 21/31] Fix Mac OS X installation instructions URL --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 323a147c4..44ff97729 100644 --- a/README.md +++ b/README.md @@ -108,7 +108,7 @@ Note that some methods are community contributions and not yet officially suppor * [Ubuntu 12.04 and 12.10 (officially supported)](http://docs.docker.io/en/latest/installation/ubuntulinux/) * [Arch Linux](http://docs.docker.io/en/latest/installation/archlinux/) -* [MacOS X (with Vagrant)](http://docs.docker.io/en/latest/installation/macos/) +* [Mac OS X (with Vagrant)](http://docs.docker.io/en/latest/installation/vagrant/) * [Windows (with Vagrant)](http://docs.docker.io/en/latest/installation/windows/) * [Amazon EC2 (with Vagrant)](http://docs.docker.io/en/latest/installation/amazon/) From 5be7b9af3ee9b884482220979735e2a8ea969ce3 Mon Sep 17 00:00:00 2001 From: Solomon Hykes Date: Tue, 18 Jun 2013 20:28:49 -0700 Subject: [PATCH 22/31] * Builder: fixed the behavior of ADD to be (mostly) reverse-compatible, predictable and well-documented. --- archive.go | 83 ++++++++++++++++++++++++++++++++++-- buildfile.go | 8 ++-- docs/sources/use/builder.rst | 36 ++++++++++++++-- 3 files changed, 117 insertions(+), 10 deletions(-) diff --git a/archive.go b/archive.go index 44fdd56be..e10fbfdae 100644 --- a/archive.go +++ b/archive.go @@ -3,10 +3,12 @@ package docker import ( "errors" "fmt" + "github.com/dotcloud/docker/utils" "io" "io/ioutil" "os" "os/exec" + "path" ) type Archive io.Reader @@ -46,11 +48,30 @@ func (compression *Compression) Extension() string { return "" } +// Tar creates an archive from the directory at `path`, and returns it as a +// stream of bytes. func Tar(path string, compression Compression) (io.Reader, error) { - cmd := exec.Command("bsdtar", "-f", "-", "-C", path, "-c"+compression.Flag(), ".") + return TarFilter(path, compression, nil) +} + +// Tar creates an archive from the directory at `path`, only including files whose relative +// paths are included in `filter`. If `filter` is nil, then all files are included. +func TarFilter(path string, compression Compression, filter []string) (io.Reader, error) { + args := []string{"bsdtar", "-f", "-", "-C", path} + if filter == nil { + filter = []string{"."} + } + for _, f := range filter { + args = append(args, "-c"+compression.Flag(), f) + } + cmd := exec.Command(args[0], args[1:]...) return CmdStream(cmd) } +// Untar reads a stream of bytes from `archive`, parses it as a tar archive, +// and unpacks it into the directory at `path`. +// The archive may be compressed with one of the following algorithgms: +// identity (uncompressed), gzip, bzip2, xz. // FIXME: specify behavior when target path exists vs. doesn't exist. func Untar(archive io.Reader, path string) error { cmd := exec.Command("bsdtar", "-f", "-", "-C", path, "-x") @@ -65,6 +86,18 @@ func Untar(archive io.Reader, path string) error { return nil } +// TarUntar is a convenience function which calls Tar and Untar, with +// the output of one piped into the other. If either Tar or Untar fails, +// TarUntar aborts and returns the error. +func TarUntar(src string, filter []string, dst string) error { + utils.Debugf("TarUntar(%s %s %s)", src, filter, dst) + archive, err := TarFilter(src, Uncompressed, filter) + if err != nil { + return err + } + return Untar(archive, dst) +} + // UntarPath is a convenience function which looks for an archive // at filesystem path `src`, and unpacks it at `dst`. func UntarPath(src, dst string) error { @@ -82,11 +115,55 @@ func UntarPath(src, dst string) error { // intermediary disk IO. // func CopyWithTar(src, dst string) error { - archive, err := Tar(src, Uncompressed) + srcSt, err := os.Stat(src) if err != nil { return err } - return Untar(archive, dst) + var dstExists bool + dstSt, err := os.Stat(dst) + if err != nil { + if !os.IsNotExist(err) { + return err + } + } else { + dstExists = true + } + // Things that can go wrong if the source is a directory + if srcSt.IsDir() { + // The destination exists and is a regular file + if dstExists && !dstSt.IsDir() { + return fmt.Errorf("Can't copy a directory over a regular file") + } + // Things that can go wrong if the source is a regular file + } else { + utils.Debugf("The destination exists, it's a directory, and doesn't end in /") + // The destination exists, it's a directory, and doesn't end in / + if dstExists && dstSt.IsDir() && dst[len(dst)-1] != '/' { + return fmt.Errorf("Can't copy a regular file over a directory %s |%s|", dst, dst[len(dst)-1]) + } + } + // Create the destination + var dstDir string + if dst[len(dst)-1] == '/' { + // The destination ends in / + // --> dst is the holding directory + dstDir = dst + } else { + // The destination doesn't end in / + // --> dst is the file + dstDir = path.Dir(dst) + } + if !dstExists { + // Create the holding directory if necessary + utils.Debugf("Creating the holding directory %s", dstDir) + if err := os.MkdirAll(dstDir, 0700); err != nil && !os.IsExist(err) { + return err + } + } + if !srcSt.IsDir() { + return TarUntar(path.Dir(src), []string{path.Base(src)}, dstDir) + } + return TarUntar(src, nil, dstDir) } // CmdStream executes a command, and returns its stdout as a stream. diff --git a/buildfile.go b/buildfile.go index eb322d817..b8ac55640 100644 --- a/buildfile.go +++ b/buildfile.go @@ -195,15 +195,15 @@ func (b *buildFile) CmdAdd(args string) error { origPath := path.Join(b.context, orig) destPath := path.Join(container.RootfsPath(), dest) - + // Preserve the trailing '/' + if dest[len(dest)-1] == '/' { + destPath = destPath + "/" + } fi, err := os.Stat(origPath) if err != nil { return err } if fi.IsDir() { - if err := os.MkdirAll(destPath, 0700); err != nil { - return err - } if err := CopyWithTar(origPath, destPath); err != nil { return err } diff --git a/docs/sources/use/builder.rst b/docs/sources/use/builder.rst index c703fc776..830d517c0 100644 --- a/docs/sources/use/builder.rst +++ b/docs/sources/use/builder.rst @@ -138,9 +138,39 @@ curl was installed within the image. ``ADD `` -The `ADD` instruction will insert the files from the `` path of the context into `` path -of the container. -The context must be set in order to use this instruction. (see examples) +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). + +`` 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 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 +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. + +If `` is any other kind of file, it is copied individually along with its metadata. + +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 `` ends with a trailing slash '/', the contents of `` is copied `inside` it. +For example "ADD foo /usr/src/" creates /usr/src/foo in the container. If `` already exists, +it MUST be a directory. + +If `` does not end with a trailing slash '/', the contents of `` is copied `over` it. +For example "ADD foo /usr/src" creates /usr/src with the contents of the "foo". If `` already +exists, it MUST be of the same type as the source. + + + 3. Dockerfile Examples ====================== From c88b763e80d6ad0253da060896da10d60f58f829 Mon Sep 17 00:00:00 2001 From: Thomas Hansen Date: Wed, 19 Jun 2013 11:38:58 -0500 Subject: [PATCH 23/31] use https repo url to clone for dev setup instructions the git clone line in the dev setup instructions does not work as is, unless the user has write access --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 44ff97729..8def9bc19 100644 --- a/README.md +++ b/README.md @@ -269,7 +269,7 @@ export PATH=$GOPATH/bin:$PATH mkdir -p $GOPATH/src/github.com/dotcloud cd $GOPATH/src/github.com/dotcloud -git clone git@github.com:dotcloud/docker.git +git clone https://github.com/dotcloud/docker.git cd docker go get -v github.com/dotcloud/docker/... From 96988a37f52b65e8b703b6c2de138c34486215ad Mon Sep 17 00:00:00 2001 From: globalcitizen Date: Thu, 20 Jun 2013 00:37:08 +0700 Subject: [PATCH 24/31] Add healthy procfs/sysfs warnings --- lxc_template.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lxc_template.go b/lxc_template.go index 3d102a5a2..4cca08382 100644 --- a/lxc_template.go +++ b/lxc_template.go @@ -67,7 +67,11 @@ lxc.cgroup.devices.allow = c 10:200 rwm # standard mount point +# WARNING: procfs is a known attack vector and should probably be disabled +# if your userspace allows it. eg. see http://blog.zx2c4.com/749 lxc.mount.entry = proc {{$ROOTFS}}/proc proc nosuid,nodev,noexec 0 0 +# WARNING: sysfs is a known attack vector and should probably be disabled +# if your userspace allows it. eg. see http://bit.ly/T9CkqJ lxc.mount.entry = sysfs {{$ROOTFS}}/sys sysfs nosuid,nodev,noexec 0 0 lxc.mount.entry = devpts {{$ROOTFS}}/dev/pts devpts newinstance,ptmxmode=0666,nosuid,noexec 0 0 #lxc.mount.entry = varrun {{$ROOTFS}}/var/run tmpfs mode=755,size=4096k,nosuid,nodev,noexec 0 0 From 788d66f409ce3a7e464bbb68d909960648f2515c Mon Sep 17 00:00:00 2001 From: globalcitizen Date: Thu, 20 Jun 2013 00:39:35 +0700 Subject: [PATCH 25/31] Add note about lxc.cap.keep > lxc.cap.drop --- lxc_template.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lxc_template.go b/lxc_template.go index 4cca08382..45408d4bf 100644 --- a/lxc_template.go +++ b/lxc_template.go @@ -90,6 +90,9 @@ lxc.mount.entry = {{$realPath}} {{$ROOTFS}}/{{$virtualPath}} none bind,rw 0 0 {{end}} # drop linux capabilities (apply mainly to the user root in the container) +# (Note: 'lxc.cap.keep' is coming soon and should replace this under the +# security principle 'deny all unless explicitly permitted', see +# http://sourceforge.net/mailarchive/message.php?msg_id=31054627 ) lxc.cap.drop = audit_control audit_write mac_admin mac_override mknod setfcap setpcap sys_admin sys_boot sys_module sys_nice sys_pacct sys_rawio sys_resource sys_time sys_tty_config # limits From a7e14a3065ccd97fcd77b4038abdf30bee78a2b4 Mon Sep 17 00:00:00 2001 From: "Guillaume J. Charmes" Date: Wed, 19 Jun 2013 11:07:36 -0700 Subject: [PATCH 26/31] hotfix: nil pointer uppon some registry error --- registry/registry.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/registry/registry.go b/registry/registry.go index 18bdad26f..276c9f865 100644 --- a/registry/registry.go +++ b/registry/registry.go @@ -162,10 +162,10 @@ func (r *Registry) GetRemoteTags(registries []string, repository string, token [ } req.Header.Set("Authorization", "Token "+strings.Join(token, ", ")) res, err := r.client.Do(req) - utils.Debugf("Got status code %d from %s", res.StatusCode, endpoint) if err != nil { return nil, err } + utils.Debugf("Got status code %d from %s", res.StatusCode, endpoint) defer res.Body.Close() if res.StatusCode != 200 && res.StatusCode != 404 { From 0312bbc535de01cade67299dd41a69935c7241ba Mon Sep 17 00:00:00 2001 From: shin- Date: Wed, 19 Jun 2013 13:48:49 -0700 Subject: [PATCH 27/31] Use opaque requests when we need to preserve urlencoding in registry requests --- registry/registry.go | 19 ++++++++++++++----- server.go | 2 +- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/registry/registry.go b/registry/registry.go index 276c9f865..81b16d8d1 100644 --- a/registry/registry.go +++ b/registry/registry.go @@ -156,7 +156,7 @@ func (r *Registry) GetRemoteTags(registries []string, repository string, token [ } for _, host := range registries { endpoint := fmt.Sprintf("https://%s/v1/repositories/%s/tags", host, repository) - req, err := http.NewRequest("GET", endpoint, nil) + req, err := r.opaqueRequest("GET", endpoint, nil) if err != nil { return nil, err } @@ -190,7 +190,7 @@ func (r *Registry) GetRemoteTags(registries []string, repository string, token [ func (r *Registry) GetRepositoryData(remote string) (*RepositoryData, error) { repositoryTarget := auth.IndexServerAddress() + "/repositories/" + remote + "/images" - req, err := http.NewRequest("GET", repositoryTarget, nil) + req, err := r.opaqueRequest("GET", repositoryTarget, nil) if err != nil { return nil, err } @@ -309,6 +309,15 @@ func (r *Registry) PushImageLayerRegistry(imgId string, layer io.Reader, registr return nil } +func (r *Registry) opaqueRequest(method, urlStr string, body io.Reader) (*http.Request, error) { + req, err := http.NewRequest(method, urlStr, body) + if err != nil { + return nil, err + } + req.URL.Opaque = strings.Replace(urlStr, req.URL.Scheme + ":", "", 1) + return req, err +} + // push a tag on the registry. // Remote has the format '/ func (r *Registry) PushRegistryTag(remote, revision, tag, registry string, token []string) error { @@ -316,7 +325,7 @@ func (r *Registry) PushRegistryTag(remote, revision, tag, registry string, token revision = "\"" + revision + "\"" registry = "https://" + registry + "/v1" - req, err := http.NewRequest("PUT", registry+"/repositories/"+remote+"/tags/"+tag, strings.NewReader(revision)) + req, err := r.opaqueRequest("PUT", registry+"/repositories/"+remote+"/tags/"+tag, strings.NewReader(revision)) if err != nil { return err } @@ -346,7 +355,7 @@ func (r *Registry) PushImageJSONIndex(remote string, imgList []*ImgData, validat utils.Debugf("Image list pushed to index:\n%s\n", imgListJSON) - req, err := http.NewRequest("PUT", auth.IndexServerAddress()+"/repositories/"+remote+"/"+suffix, bytes.NewReader(imgListJSON)) + req, err := r.opaqueRequest("PUT", auth.IndexServerAddress()+"/repositories/"+remote+"/"+suffix, bytes.NewReader(imgListJSON)) if err != nil { return nil, err } @@ -366,7 +375,7 @@ func (r *Registry) PushImageJSONIndex(remote string, imgList []*ImgData, validat // Redirect if necessary for res.StatusCode >= 300 && res.StatusCode < 400 { utils.Debugf("Redirected to %s\n", res.Header.Get("Location")) - req, err = http.NewRequest("PUT", res.Header.Get("Location"), bytes.NewReader(imgListJSON)) + req, err = r.opaqueRequest("PUT", res.Header.Get("Location"), bytes.NewReader(imgListJSON)) if err != nil { return nil, err } diff --git a/server.go b/server.go index 7375dddd9..df20c36b8 100644 --- a/server.go +++ b/server.go @@ -532,7 +532,7 @@ func (srv *Server) pushRepository(r *registry.Registry, out io.Writer, name stri // FIXME: Continue on error? return err } - out.Write(sf.FormatStatus("Pushing tags for rev [%s] on {%s}", elem.ID, ep+"/users/"+srvName+"/"+elem.Tag)) + out.Write(sf.FormatStatus("Pushing tags for rev [%s] on {%s}", elem.ID, ep+"/repositories/"+srvName+"/tags/"+elem.Tag)) if err := r.PushRegistryTag(srvName, elem.ID, elem.Tag, ep, repoData.Tokens); err != nil { return err } From 507ea757a5d72af47b37af0e384e83fcea613a7b Mon Sep 17 00:00:00 2001 From: Solomon Hykes Date: Wed, 19 Jun 2013 14:26:11 -0700 Subject: [PATCH 28/31] * Builder: correct the behavior of ADD when copying directories. --- archive.go | 6 +++--- docs/sources/use/builder.rst | 17 +++++------------ 2 files changed, 8 insertions(+), 15 deletions(-) diff --git a/archive.go b/archive.go index b129e2879..16401e29f 100644 --- a/archive.go +++ b/archive.go @@ -185,9 +185,9 @@ func CopyWithTar(src, dst string) error { } // Create the destination var dstDir string - if dst[len(dst)-1] == '/' { - // The destination ends in / - // --> dst is the holding directory + if srcSt.IsDir() || dst[len(dst)-1] == '/' { + // The destination ends in /, or the source is a directory + // --> dst is the holding directory and needs to be created for -C dstDir = dst } else { // The destination doesn't end in / diff --git a/docs/sources/use/builder.rst b/docs/sources/use/builder.rst index 830d517c0..5ceba4b21 100644 --- a/docs/sources/use/builder.rst +++ b/docs/sources/use/builder.rst @@ -156,22 +156,15 @@ When a directory is copied or unpacked, it has the same behavior as 'tar -x': th 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. -If `` is any other kind of file, it is copied individually along with its metadata. +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. -If `` ends with a trailing slash '/', the contents of `` is copied `inside` it. -For example "ADD foo /usr/src/" creates /usr/src/foo in the container. If `` already exists, -it MUST be a directory. - -If `` does not end with a trailing slash '/', the contents of `` is copied `over` it. -For example "ADD foo /usr/src" creates /usr/src with the contents of the "foo". If `` already -exists, it MUST be of the same type as the source. - - - - 3. Dockerfile Examples ====================== From 88dcba3482df33f47a0b89c0811a96abf0609840 Mon Sep 17 00:00:00 2001 From: Daniel Mizyrycki Date: Wed, 19 Jun 2013 16:31:55 -0700 Subject: [PATCH 29/31] Packaging|ubuntu, issue #954: Generate debian/changelog from main CHANGELOG.md --- packaging/ubuntu/Makefile | 18 +- packaging/ubuntu/changelog | 246 ---------------------------- packaging/ubuntu/parse_changelog.py | 23 +++ 3 files changed, 31 insertions(+), 256 deletions(-) delete mode 100644 packaging/ubuntu/changelog create mode 100755 packaging/ubuntu/parse_changelog.py diff --git a/packaging/ubuntu/Makefile b/packaging/ubuntu/Makefile index dbdf1af7a..f82892c81 100644 --- a/packaging/ubuntu/Makefile +++ b/packaging/ubuntu/Makefile @@ -1,6 +1,6 @@ # Ubuntu package Makefile # -# Dependencies: debhelper autotools-dev devscripts golang +# Dependencies: debhelper autotools-dev devscripts golang-stable # Notes: # Use 'make ubuntu' to create the ubuntu package # GPG_KEY environment variable needs to contain a GPG private key for package to be signed @@ -9,12 +9,9 @@ # status code 2 PKG_NAME=lxc-docker -VERSION=$(shell head -1 changelog | sed 's/^.\+(\(.\+\)..).\+$$/\1/') GITHUB_PATH=github.com/dotcloud/docker -DOCKER_VERSION=${PKG_NAME}_${VERSION} -DOCKER_FVERSION=${PKG_NAME}_$(shell head -1 changelog | sed 's/^.\+(\(.\+\)).\+$$/\1/') BUILD_SRC=${CURDIR}/../../build_src -VERSION_TAG=v$(shell head -1 changelog | sed 's/^.\+(\(.\+\)-[0-9]\+).\+$$/\1/') +VERSION=$(shell sed -En '0,/^\#\# /{s/^\#\# ([^ ]+).+/\1/p}' ../../CHANGELOG.md) all: # Compile docker. Used by dpkg-buildpackage. @@ -35,18 +32,19 @@ ubuntu: # Retrieve docker project and its go structure from internet rm -rf ${BUILD_SRC} git clone $(shell git rev-parse --show-toplevel) ${BUILD_SRC}/${GITHUB_PATH} - cd ${BUILD_SRC}/${GITHUB_PATH}; git checkout ${VERSION_TAG} && GOPATH=${BUILD_SRC} go get -d + cd ${BUILD_SRC}/${GITHUB_PATH}; git checkout v${VERSION} && GOPATH=${BUILD_SRC} go get -d # Add debianization mkdir ${BUILD_SRC}/debian cp Makefile ${BUILD_SRC} cp -r * ${BUILD_SRC}/debian cp ../../README.md ${BUILD_SRC} + ./parse_changelog.py < ../../CHANGELOG.md > ${BUILD_SRC}/debian/changelog # Cleanup for d in `find ${BUILD_SRC} -name '.git*'`; do rm -rf $$d; done - rm -rf ${BUILD_SRC}/../${DOCKER_VERSION}.orig.tar.gz + rm -rf ${BUILD_SRC}/../${PKG_NAME}_${VERSION}.orig.tar.gz rm -rf ${BUILD_SRC}/pkg # Create docker debian files - cd ${BUILD_SRC}; tar czf ../${DOCKER_VERSION}.orig.tar.gz . + cd ${BUILD_SRC}; tar czf ../${PKG_NAME}_${VERSION}.orig.tar.gz . cd ${BUILD_SRC}; dpkg-buildpackage -us -uc rm -rf ${BUILD_SRC} # Sign package and upload it to PPA if GPG_KEY environment variable @@ -56,7 +54,7 @@ ubuntu: # Import gpg signing key echo "$${GPG_KEY}" | gpg --allow-secret-key-import --import # Sign the package - cd ${BUILD_SRC}; dpkg-source -x ${BUILD_SRC}/../${DOCKER_FVERSION}.dsc + cd ${BUILD_SRC}; dpkg-source -x ${BUILD_SRC}/../${PKG_NAME}_${VERSION}-1.dsc cd ${BUILD_SRC}/${PKG_NAME}-${VERSION}; debuild -S -sa - cd ${BUILD_SRC};dput ppa:dotcloud/lxc-docker ${DOCKER_FVERSION}_source.changes + cd ${BUILD_SRC};dput ppa:dotcloud/lxc-docker ${PKG_NAME}_${VERSION}-1_source.changes rm -rf ${BUILD_SRC} diff --git a/packaging/ubuntu/changelog b/packaging/ubuntu/changelog deleted file mode 100644 index ef7efba47..000000000 --- a/packaging/ubuntu/changelog +++ /dev/null @@ -1,246 +0,0 @@ -lxc-docker (0.4.2-1) precise; urgency=low - - Packaging: Bumped version to work around an Ubuntu bug - - -- dotCloud Mon, 17 Jun 2013 00:00:00 -0700 - -lxc-docker (0.4.1-1) precise; urgency=low - - Builder: don't ignore last line in Dockerfile when it doesn't end with \n - - Client: allow multiple params in inspect - - Client: Print the container id before the hijack in `docker run` - - Remote Api: Add flag to enable cross domain requests - - Remote Api/Client: Add images and containers sizes in docker ps and docker images - - Registry: add regexp check on repo's name - - Registry: Move auth to the client - - Registry: Remove login check on pull - - Runtime: Configure dns configuration host-wide with 'docker -d -dns' - - Runtime: Detect faulty DNS configuration and replace it with a public default - - Runtime: allow docker run : - - Runtime: you can now specify public port (ex: -p 80:4500) - - Runtime: improved image removal to garbage-collect unreferenced parents - - Vagrantfile: Add the rest api port to vagrantfile's port_forward - - Upgrade to Go 1.1 - - -- dotCloud Mon, 17 Jun 2013 00:00:00 -0700 - -lxc-docker (0.4.0-1) precise; urgency=low - - Introducing Builder: 'docker build' builds a container, layer by layer, from a source repository containing a Dockerfile - - Introducing Remote API: control Docker programmatically using a simple HTTP/json API - - Runtime: various reliability and usability improvements - - -- dotCloud Mon, 03 Jun 2013 00:00:00 -0700 - -lxc-docker (0.3.4-1) precise; urgency=low - - Builder: 'docker build' builds a container, layer by layer, from a source repository containing a Dockerfile - - Builder: 'docker build -t FOO' applies the tag FOO to the newly built container. - - Runtime: interactive TTYs correctly handle window resize - - Runtime: fix how configuration is merged between layers - - Remote API: split stdout and stderr on 'docker run' - - Remote API: optionally listen on a different IP and port (use at your own risk) - - Documentation: improved install instructions. - - -- dotCloud Thu, 30 May 2013 00:00:00 -0700 - - -lxc-docker (0.3.3-1) precise; urgency=low - - Registry: Fix push regression - - Various bugfixes - - -- dotCloud Thu, 23 May 2013 00:00:00 -0700 - - -lxc-docker (0.3.2-1) precise; urgency=low - - Runtime: Store the actual archive on commit - - Registry: Improve the checksum process - - Registry: Use the size to have a good progress bar while pushing - - Registry: Use the actual archive if it exists in order to speed up the push - - Registry: Fix error 400 on push - - -- dotCloud Fri, 9 May 2013 00:00:00 -0700 - - -lxc-docker (0.3.1-1) precise; urgency=low - - Builder: Implement the autorun capability within docker builder - - Builder: Add caching to docker builder - - Builder: Add support for docker builder with native API as top level command - - Runtime: Add go version to debug infos - - Builder: Implement ENV within docker builder - - Registry: Add docker search top level command in order to search a repository - - Images: output graph of images to dot (graphviz) - - Documentation: new introduction and high-level overview - - Documentation: Add the documentation for docker builder - - Website: new high-level overview - - Makefile: Swap "go get" for "go get -d", especially to compile on go1.1rc - - Images: fix ByParent function - - Builder: Check the command existance prior create and add Unit tests for the case - - Registry: Fix pull for official images with specific tag - - Registry: Fix issue when login in with a different user and trying to push - - Documentation: CSS fix for docker documentation to make REST API docs look better. - - Documentation: Fixed CouchDB example page header mistake - - Documentation: fixed README formatting - - Registry: Improve checksum - async calculation - - Runtime: kernel version - don't show the dash if flavor is empty - - Documentation: updated www.docker.io website. - - Builder: use any whitespaces instead of tabs - - Packaging: packaging ubuntu; issue #510: Use goland-stable PPA package to build docker - - -- dotCloud Fri, 8 May 2013 00:00:00 -0700 - - -lxc-docker (0.3.0-1) precise; urgency=low - - Registry: Implement the new registry - - Documentation: new example: sharing data between 2 couchdb databases - - Runtime: Fix the command existance check - - Runtime: strings.Split may return an empty string on no match - - Runtime: Fix an index out of range crash if cgroup memory is not - - Documentation: Various improvments - - Vagrant: Use only one deb line in /etc/apt - - -- dotCloud Fri, 5 May 2013 00:00:00 -0700 - - -lxc-docker (0.2.2-1) precise; urgency=low - - Support for data volumes ('docker run -v=PATH') - - Share data volumes between containers ('docker run -volumes-from') - - Improved documentation - - Upgrade to Go 1.0.3 - - Various upgrades to the dev environment for contributors - - -- dotCloud Fri, 3 May 2013 00:00:00 -0700 - - -lxc-docker (0.2.1-1) precise; urgency=low - - - 'docker commit -run' bundles a layer with default runtime options: command, ports etc. - - Improve install process on Vagrant - - New Dockerfile operation: "maintainer" - - New Dockerfile operation: "expose" - - New Dockerfile operation: "cmd" - - Contrib script to build a Debian base layer - - 'docker -d -r': restart crashed containers at daemon startup - - Runtime: improve test coverage - - -- dotCloud Wed, 1 May 2013 00:00:00 -0700 - - -lxc-docker (0.2.0-1) precise; urgency=low - - - Runtime: ghost containers can be killed and waited for - - Documentation: update install intructions - - Packaging: fix Vagrantfile - - Development: automate releasing binaries and ubuntu packages - - Add a changelog - - Various bugfixes - - -- dotCloud Mon, 23 Apr 2013 00:00:00 -0700 - - -lxc-docker (0.1.8-1) precise; urgency=low - - - Dynamically detect cgroup capabilities - - Issue stability warning on kernels <3.8 - - 'docker push' buffers on disk instead of memory - - Fix 'docker diff' for removed files - - Fix 'docker stop' for ghost containers - - Fix handling of pidfile - - Various bugfixes and stability improvements - - -- dotCloud Mon, 22 Apr 2013 00:00:00 -0700 - - -lxc-docker (0.1.7-1) precise; urgency=low - - - Container ports are available on localhost - - 'docker ps' shows allocated TCP ports - - Contributors can run 'make hack' to start a continuous integration VM - - Streamline ubuntu packaging & uploading - - Various bugfixes and stability improvements - - -- dotCloud Thu, 18 Apr 2013 00:00:00 -0700 - - -lxc-docker (0.1.6-1) precise; urgency=low - - - Record the author an image with 'docker commit -author' - - -- dotCloud Wed, 17 Apr 2013 00:00:00 -0700 - - -lxc-docker (0.1.5-1) precise; urgency=low - - - Disable standalone mode - - Use a custom DNS resolver with 'docker -d -dns' - - Detect ghost containers - - Improve diagnosis of missing system capabilities - - Allow disabling memory limits at compile time - - Add debian packaging - - Documentation: installing on Arch Linux - - Documentation: running Redis on docker - - Fixed lxc 0.9 compatibility - - Automatically load aufs module - - Various bugfixes and stability improvements - - -- dotCloud Wed, 17 Apr 2013 00:00:00 -0700 - - -lxc-docker (0.1.4-1) precise; urgency=low - - - Full support for TTY emulation - - Detach from a TTY session with the escape sequence `C-p C-q` - - Various bugfixes and stability improvements - - Minor UI improvements - - Automatically create our own bridge interface 'docker0' - - -- dotCloud Tue, 9 Apr 2013 00:00:00 -0700 - - -lxc-docker (0.1.3-1) precise; urgency=low - - - Choose TCP frontend port with '-p :PORT' - - Layer format is versioned - - Major reliability improvements to the process manager - - Various bugfixes and stability improvements - - -- dotCloud Thu, 4 Apr 2013 00:00:00 -0700 - - -lxc-docker (0.1.2-1) precise; urgency=low - - - Set container hostname with 'docker run -h' - - Selective attach at run with 'docker run -a [stdin[,stdout[,stderr]]]' - - Various bugfixes and stability improvements - - UI polish - - Progress bar on push/pull - - Use XZ compression by default - - Make IP allocator lazy - - -- dotCloud Wed, 3 Apr 2013 00:00:00 -0700 - - -lxc-docker (0.1.1-1) precise; urgency=low - - - Display shorthand IDs for convenience - - Stabilize process management - - Layers can include a commit message - - Simplified 'docker attach' - - Fixed support for re-attaching - - Various bugfixes and stability improvements - - Auto-download at run - - Auto-login on push - - Beefed up documentation - - -- dotCloud Sun, 31 Mar 2013 00:00:00 -0700 - - -lxc-docker (0.1.0-1) precise; urgency=low - - - First release - - Implement registry in order to push/pull images - - TCP port allocation - - Fix termcaps on Linux - - Add documentation - - Add Vagrant support with Vagrantfile - - Add unit tests - - Add repository/tags to ease image management - - Improve the layer implementation - - -- dotCloud Sat, 23 Mar 2013 00:00:00 -0700 diff --git a/packaging/ubuntu/parse_changelog.py b/packaging/ubuntu/parse_changelog.py new file mode 100755 index 000000000..d19a3424e --- /dev/null +++ b/packaging/ubuntu/parse_changelog.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python + +'Parse main CHANGELOG.md from stdin outputing on stdout the ubuntu 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 1c841d4feed99ad568b7a5b04cedf8d65c3bb92c Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Thu, 20 Jun 2013 15:45:30 +0000 Subject: [PATCH 30/31] add warning when you rm a running container --- server.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/server.go b/server.go index e9be3a166..e51613e2c 100644 --- a/server.go +++ b/server.go @@ -751,6 +751,9 @@ func (srv *Server) ContainerRestart(name string, t int) error { func (srv *Server) ContainerDestroy(name string, removeVolume bool) error { if container := srv.runtime.Get(name); container != nil { + if container.State.Running { + return fmt.Errorf("Impossible to remove a running container, please stop it first") + } volumes := make(map[string]struct{}) // Store all the deleted containers volumes for _, volumeId := range container.Volumes { From d8887f34888c1e6c1a846d54e3e8afc4791d88d0 Mon Sep 17 00:00:00 2001 From: Daniel Mizyrycki Date: Thu, 20 Jun 2013 08:57:28 -0700 Subject: [PATCH 31/31] Packaging|ubuntu, issue #960: Add docker PPA staging in release process --- packaging/ubuntu/Makefile | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/packaging/ubuntu/Makefile b/packaging/ubuntu/Makefile index f82892c81..582d5bcb2 100644 --- a/packaging/ubuntu/Makefile +++ b/packaging/ubuntu/Makefile @@ -2,11 +2,11 @@ # # Dependencies: debhelper autotools-dev devscripts golang-stable # Notes: -# Use 'make ubuntu' to create the ubuntu package -# GPG_KEY environment variable needs to contain a GPG private key for package to be signed -# and uploaded to docker PPA. -# If GPG_KEY is not defined, make ubuntu will create docker package and exit with -# status code 2 +# Use 'make ubuntu' to create the ubuntu package and push it to stating PPA by +# default. To push to production, set PUBLISH_PPA=1 before doing 'make ubuntu' +# GPG_KEY environment variable needs to contain a GPG private key for package +# to be signed and uploaded to docker PPA. If GPG_KEY is not defined, +# make ubuntu will create docker package and exit with status code 2 PKG_NAME=lxc-docker GITHUB_PATH=github.com/dotcloud/docker @@ -52,9 +52,11 @@ ubuntu: if /usr/bin/test "$${GPG_KEY}" == ""; then exit 2; fi mkdir ${BUILD_SRC} # Import gpg signing key - echo "$${GPG_KEY}" | gpg --allow-secret-key-import --import + echo "$${GPG_KEY}" | gpg --allow-secret-key-import --import || true # Sign the package cd ${BUILD_SRC}; dpkg-source -x ${BUILD_SRC}/../${PKG_NAME}_${VERSION}-1.dsc cd ${BUILD_SRC}/${PKG_NAME}-${VERSION}; debuild -S -sa - cd ${BUILD_SRC};dput ppa:dotcloud/lxc-docker ${PKG_NAME}_${VERSION}-1_source.changes + # Upload to PPA + if [ "${PUBLISH_PPA}" = "1" ]; then cd ${BUILD_SRC};dput ppa:dotcloud/lxc-docker ${PKG_NAME}_${VERSION}-1_source.changes; fi + if [ "${PUBLISH_PPA}" != "1" ]; then cd ${BUILD_SRC};dput ppa:dotcloud/docker-staging ${PKG_NAME}_${VERSION}-1_source.changes; fi rm -rf ${BUILD_SRC}