From f29e5dc8a15d0ab8e9c6084e41ee373376051659 Mon Sep 17 00:00:00 2001 From: "Guillaume J. Charmes" Date: Thu, 16 May 2013 12:09:06 -0700 Subject: [PATCH 01/16] Remove hijack from api when not necessary --- api.go | 40 ++++++++-------------------------------- commands.go | 23 +++++++++++++---------- registry/registry.go | 1 - server.go | 11 ++++++----- 4 files changed, 27 insertions(+), 48 deletions(-) diff --git a/api.go b/api.go index 8984d00cd9..4cfb0aac74 100644 --- a/api.go +++ b/api.go @@ -283,23 +283,17 @@ func postImagesCreate(srv *Server, w http.ResponseWriter, r *http.Request, vars src := r.Form.Get("fromSrc") image := r.Form.Get("fromImage") - repo := r.Form.Get("repo") tag := r.Form.Get("tag") + repo := r.Form.Get("repo") - in, out, err := hijackServer(w) - if err != nil { - return err - } - defer in.Close() - fmt.Fprintf(out, "HTTP/1.1 200 OK\r\nContent-Type: application/vnd.docker.raw-stream\r\n\r\n") if image != "" { //pull registry := r.Form.Get("registry") - if err := srv.ImagePull(image, tag, registry, out); err != nil { - fmt.Fprintf(out, "Error: %s\n", err) + if err := srv.ImagePull(image, tag, registry, w); err != nil { + return err } } else { //import - if err := srv.ImageImport(src, repo, tag, in, out); err != nil { - fmt.Fprintf(out, "Error: %s\n", err) + if err := srv.ImageImport(src, repo, tag, r.Body, w); err != nil { + return err } } return nil @@ -335,15 +329,9 @@ func postImagesInsert(srv *Server, w http.ResponseWriter, r *http.Request, vars } name := vars["name"] - in, out, err := hijackServer(w) - if err != nil { + if err := srv.ImageInsert(name, url, path, w); err != nil { return err } - defer in.Close() - fmt.Fprintf(out, "HTTP/1.1 200 OK\r\nContent-Type: application/vnd.docker.raw-stream\r\n\r\n") - if err := srv.ImageInsert(name, url, path, out); err != nil { - fmt.Fprintf(out, "Error: %s\n", err) - } return nil } @@ -358,28 +346,16 @@ func postImagesPush(srv *Server, w http.ResponseWriter, r *http.Request, vars ma } name := vars["name"] - in, out, err := hijackServer(w) - if err != nil { + if err := srv.ImagePush(name, registry, w); err != nil { return err } - defer in.Close() - fmt.Fprintf(out, "HTTP/1.1 200 OK\r\nContent-Type: application/vnd.docker.raw-stream\r\n\r\n") - if err := srv.ImagePush(name, registry, out); err != nil { - fmt.Fprintf(out, "Error: %s\n", err) - } return nil } func postBuild(srv *Server, w http.ResponseWriter, r *http.Request, vars map[string]string) error { - in, out, err := hijackServer(w) - if err != nil { + if err := srv.ImageCreateFromFile(r.Body, w); err != nil { return err } - defer in.Close() - fmt.Fprintf(out, "HTTP/1.1 200 OK\r\nContent-Type: application/vnd.docker.raw-stream\r\n\r\n") - if err := srv.ImageCreateFromFile(in, out); err != nil { - fmt.Fprintf(out, "Error: %s\n", err) - } return nil } diff --git a/commands.go b/commands.go index 8734da176f..cbd9d146f4 100644 --- a/commands.go +++ b/commands.go @@ -104,7 +104,7 @@ func (cli *DockerCli) CmdInsert(args ...string) error { v.Set("url", cmd.Arg(1)) v.Set("path", cmd.Arg(2)) - err := cli.hijack("POST", "/images/"+cmd.Arg(0)+"?"+v.Encode(), false) + err := cli.stream("POST", "/images/"+cmd.Arg(0)+"?"+v.Encode(), nil, os.Stdout) if err != nil { return err } @@ -117,7 +117,7 @@ func (cli *DockerCli) CmdBuild(args ...string) error { return nil } - err := cli.hijack("POST", "/build", false) + err := cli.stream("POST", "/build", nil, os.Stdout) if err != nil { return err } @@ -571,7 +571,7 @@ func (cli *DockerCli) CmdImport(args ...string) error { v.Set("tag", tag) v.Set("fromSrc", src) - err := cli.hijack("POST", "/images/create?"+v.Encode(), false) + err := cli.stream("POST", "/images/create?"+v.Encode(), os.Stdin, os.Stdout) if err != nil { return err } @@ -628,7 +628,7 @@ func (cli *DockerCli) CmdPush(args ...string) error { v := url.Values{} v.Set("registry", *registry) - if err := cli.hijack("POST", "/images/"+name+"/push?"+v.Encode(), false); err != nil { + if err := cli.stream("POST", "/images/"+name+"/push?"+v.Encode(), nil, os.Stdout); err != nil { return err } return nil @@ -659,7 +659,7 @@ func (cli *DockerCli) CmdPull(args ...string) error { v.Set("tag", *tag) v.Set("registry", *registry) - if err := cli.hijack("POST", "/images/create?"+v.Encode(), false); err != nil { + if err := cli.stream("POST", "/images/create?"+v.Encode(), nil, os.Stdout); err != nil { return err } @@ -864,7 +864,7 @@ func (cli *DockerCli) CmdExport(args ...string) error { return nil } - if err := cli.stream("GET", "/containers/"+cmd.Arg(0)+"/export"); err != nil { + if err := cli.stream("GET", "/containers/"+cmd.Arg(0)+"/export", nil, os.Stdout); err != nil { return err } return nil @@ -1086,7 +1086,7 @@ func (cli *DockerCli) CmdRun(args ...string) error { if statusCode == 404 { v := url.Values{} v.Set("fromImage", config.Image) - err = cli.hijack("POST", "/images/create?"+v.Encode(), false) + err = cli.stream("POST", "/images/create?"+v.Encode(), nil, os.Stderr) if err != nil { return err } @@ -1179,8 +1179,11 @@ func (cli *DockerCli) call(method, path string, data interface{}) ([]byte, int, return body, resp.StatusCode, nil } -func (cli *DockerCli) stream(method, path string) error { - req, err := http.NewRequest(method, fmt.Sprintf("http://%s:%d%s", cli.host, cli.port, path), nil) +func (cli *DockerCli) stream(method, path string, in io.Reader, out io.Writer) error { + if (method == "POST" || method == "PUT") && in == nil { + in = bytes.NewReader([]byte{}) + } + req, err := http.NewRequest(method, fmt.Sprintf("http://%s:%d%s", cli.host, cli.port, path), in) if err != nil { return err } @@ -1204,7 +1207,7 @@ func (cli *DockerCli) stream(method, path string) error { return fmt.Errorf("error: %s", body) } - if _, err := io.Copy(os.Stdout, resp.Body); err != nil { + if _, err := io.Copy(out, resp.Body); err != nil { return err } return nil diff --git a/registry/registry.go b/registry/registry.go index e2ffb292c5..71648d1803 100644 --- a/registry/registry.go +++ b/registry/registry.go @@ -175,7 +175,6 @@ func (r *Registry) GetRemoteTags(registries []string, repository string, token [ } func (r *Registry) GetRepositoryData(remote string) (*RepositoryData, error) { - utils.Debugf("Pulling repository %s from %s\r\n", remote, auth.IndexServerAddress()) repositoryTarget := auth.IndexServerAddress() + "/repositories/" + remote + "/images" req, err := http.NewRequest("GET", repositoryTarget, nil) diff --git a/server.go b/server.go index 2f45802ca6..f6a242606d 100644 --- a/server.go +++ b/server.go @@ -2,6 +2,7 @@ package docker import ( "fmt" + "github.com/dotcloud/docker/auth" "github.com/dotcloud/docker/registry" "github.com/dotcloud/docker/utils" "io" @@ -322,8 +323,8 @@ func (srv *Server) pullImage(out io.Writer, imgId, registry string, token []stri return nil } -func (srv *Server) pullRepository(stdout io.Writer, remote, askedTag string) error { - utils.Debugf("Retrieving repository data") +func (srv *Server) pullRepository(out io.Writer, remote, askedTag string) error { + fmt.Fprintf(out, "Pulling repository %s from %s\r\n", remote, auth.IndexServerAddress()) repoData, err := srv.registry.GetRepositoryData(remote) if err != nil { return err @@ -349,11 +350,11 @@ func (srv *Server) pullRepository(stdout io.Writer, remote, askedTag string) err if askedTag != "" && askedTag != img.Tag { continue } - fmt.Fprintf(stdout, "Pulling image %s (%s) from %s\n", img.Id, img.Tag, remote) + fmt.Fprintf(out, "Pulling image %s (%s) from %s\n", img.Id, img.Tag, remote) success := false for _, ep := range repoData.Endpoints { - if err := srv.pullImage(stdout, img.Id, "https://"+ep+"/v1", repoData.Tokens); err != nil { - fmt.Fprintf(stdout, "Error while retrieving image for tag: %s (%s); checking next endpoint\n", askedTag, err) + if err := srv.pullImage(out, img.Id, "https://"+ep+"/v1", repoData.Tokens); err != nil { + fmt.Fprintf(out, "Error while retrieving image for tag: %s (%s); checking next endpoint\n", askedTag, err) continue } if err := srv.runtime.repositories.Set(remote, img.Tag, img.Id, true); err != nil { From 6145812444fb3eda2cc362795ed0b1addb8f4847 Mon Sep 17 00:00:00 2001 From: "Guillaume J. Charmes" Date: Thu, 16 May 2013 14:33:10 -0700 Subject: [PATCH 02/16] Update tests to reflect new behavior --- api_test.go | 53 +++++++++++++++++++---------------------------------- commands.go | 2 +- 2 files changed, 20 insertions(+), 35 deletions(-) diff --git a/api_test.go b/api_test.go index 07ecc6d0bf..0827c9b5a4 100644 --- a/api_test.go +++ b/api_test.go @@ -14,6 +14,7 @@ import ( "net/http/httptest" "os" "path" + "strings" "testing" "time" ) @@ -587,45 +588,29 @@ func TestPostBuild(t *testing.T) { srv := &Server{runtime: runtime} - stdin, stdinPipe := io.Pipe() - stdout, stdoutPipe := io.Pipe() + imgs, err := runtime.graph.All() + if err != nil { + t.Fatal(err) + } + beginCount := len(imgs) - c1 := make(chan struct{}) - go func() { - defer close(c1) - r := &hijackTester{ - ResponseRecorder: httptest.NewRecorder(), - in: stdin, - out: stdoutPipe, - } - - if err := postBuild(srv, r, nil, nil); err != nil { - t.Fatal(err) - } - }() - - // Acknowledge hijack - setTimeout(t, "hijack acknowledge timed out", 2*time.Second, func() { - stdout.Read([]byte{}) - stdout.Read(make([]byte, 4096)) - }) - - setTimeout(t, "read/write assertion timed out", 2*time.Second, func() { - if err := assertPipe("from docker-ut\n", "FROM docker-ut", stdout, stdinPipe, 15); err != nil { - t.Fatal(err) - } - }) - - // Close pipes (client disconnects) - if err := closeWrap(stdin, stdinPipe, stdout, stdoutPipe); err != nil { + req, err := http.NewRequest("POST", "/build", strings.NewReader(Dockerfile)) + if err != nil { t.Fatal(err) } - // Wait for build to finish, the client disconnected, therefore, Build finished his job - setTimeout(t, "Waiting for CmdBuild timed out", 2*time.Second, func() { - <-c1 - }) + r := httptest.NewRecorder() + if err := postBuild(srv, r, req, nil); err != nil { + t.Fatal(err) + } + imgs, err = runtime.graph.All() + if err != nil { + t.Fatal(err) + } + if len(imgs) != beginCount+3 { + t.Fatalf("Expected %d images, %d found", beginCount+3, len(imgs)) + } } func TestPostImagesCreate(t *testing.T) { diff --git a/commands.go b/commands.go index cbd9d146f4..4567370d3e 100644 --- a/commands.go +++ b/commands.go @@ -117,7 +117,7 @@ func (cli *DockerCli) CmdBuild(args ...string) error { return nil } - err := cli.stream("POST", "/build", nil, os.Stdout) + err := cli.stream("POST", "/build", os.Stdin, os.Stdout) if err != nil { return err } From 08121c8f6b435779027d837c1e7fc8046bc1e165 Mon Sep 17 00:00:00 2001 From: "Guillaume J. Charmes" Date: Thu, 16 May 2013 14:33:29 -0700 Subject: [PATCH 03/16] Update Push to reflect the correct API --- registry/registry.go | 48 ++++++++++++++++++++++---------------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/registry/registry.go b/registry/registry.go index 71648d1803..ce9b4b4ac7 100644 --- a/registry/registry.go +++ b/registry/registry.go @@ -326,10 +326,11 @@ func (r *Registry) PushImageJsonIndex(remote string, imgList []*ImgData, validat if err != nil { return nil, err } - - utils.Debugf("json sent: %s\n", imgListJson) - - req, err := http.NewRequest("PUT", auth.IndexServerAddress()+"/repositories/"+remote+"/", bytes.NewReader(imgListJson)) + var suffix string + if validate { + suffix = "images" + } + req, err := http.NewRequest("PUT", auth.IndexServerAddress()+"/repositories/"+remote+"/"+suffix, bytes.NewReader(imgListJson)) if err != nil { return nil, err } @@ -361,29 +362,28 @@ func (r *Registry) PushImageJsonIndex(remote string, imgList []*ImgData, validat defer res.Body.Close() } - if res.StatusCode != 200 && res.StatusCode != 201 { - errBody, err := ioutil.ReadAll(res.Body) - if err != nil { - return nil, err + var tokens, endpoints []string + if !validate { + if res.StatusCode != 200 && res.StatusCode != 201 { + errBody, err := ioutil.ReadAll(res.Body) + if err != nil { + return nil, err + } + return nil, fmt.Errorf("Error: Status %d trying to push repository %s: %s", res.StatusCode, remote, errBody) + } + if res.Header.Get("X-Docker-Token") != "" { + tokens = res.Header["X-Docker-Token"] + utils.Debugf("Auth token: %v", tokens) + } else { + return nil, fmt.Errorf("Index response didn't contain an access token") } - return nil, fmt.Errorf("Error: Status %d trying to push repository %s: %s", res.StatusCode, remote, errBody) - } - var tokens []string - if res.Header.Get("X-Docker-Token") != "" { - tokens = res.Header["X-Docker-Token"] - utils.Debugf("Auth token: %v", tokens) - } else { - return nil, fmt.Errorf("Index response didn't contain an access token") + if res.Header.Get("X-Docker-Endpoints") != "" { + endpoints = res.Header["X-Docker-Endpoints"] + } else { + return nil, fmt.Errorf("Index response didn't contain any endpoints") + } } - - var endpoints []string - if res.Header.Get("X-Docker-Endpoints") != "" { - endpoints = res.Header["X-Docker-Endpoints"] - } else { - return nil, fmt.Errorf("Index response didn't contain any endpoints") - } - if validate { if res.StatusCode != 204 { if errBody, err := ioutil.ReadAll(res.Body); err != nil { From 55dd0afb5dd365bd306f698f9ccca1425e5c91a7 Mon Sep 17 00:00:00 2001 From: Gareth Rushgrove Date: Fri, 17 May 2013 17:08:37 +0100 Subject: [PATCH 04/16] add instructions for using docker with puppet --- docs/sources/use/index.rst | 1 + docs/sources/use/puppet.rst | 109 ++++++++++++++++++++++++++++++++++++ 2 files changed, 110 insertions(+) create mode 100644 docs/sources/use/puppet.rst diff --git a/docs/sources/use/index.rst b/docs/sources/use/index.rst index d3b3a31b18..9939dc7ea8 100644 --- a/docs/sources/use/index.rst +++ b/docs/sources/use/index.rst @@ -15,4 +15,5 @@ Contents: basics workingwithrepository builder + puppet diff --git a/docs/sources/use/puppet.rst b/docs/sources/use/puppet.rst new file mode 100644 index 0000000000..af2d5c8d57 --- /dev/null +++ b/docs/sources/use/puppet.rst @@ -0,0 +1,109 @@ + +.. _install_using_puppet: + +Using Puppet +============= + +.. note:: + + Please note this is a community contributed installation path. The only 'official' installation is using the + :ref:`ubuntu_linux` installation path. This version may sometimes be out of date. + +Requirements +------------ + +To use this guide you'll need a working installation of Puppet from `Puppetlabs `_ . + +The module also currently uses the official PPA so only works with Ubuntu. + +Installation +------------ + +The module is available on the `Puppet Forge `_ +and can be installed using the built-in module tool. + + .. code-block:: bash + + puppet module install garethr/docker + +It can also be found on `GitHub `_ +if you would rather download the source. + +Usage +----- + +The module provides a puppet class for installing docker and two defined types +for managing images and containers. + +Installation +~~~~~~~~~~~~ + + .. code-block:: ruby + + include 'docker' + +Images +~~~~~~ + +The next step is probably to install a docker image, for this we have a +defined type which can be used like so: + + .. code-block:: ruby + + docker::image { 'base': } + +This is equivalent to running: + + .. code-block:: bash + + docker pull base + +Note that it will only if the image of that name does not already exist. +This is downloading a large binary so on first run can take a while. +For that reason this define turns off the default 5 minute timeout +for exec. Note that you can also remove images you no longer need with: + + .. code-block:: ruby + + docker::image { 'base': + ensure => 'absent', + } + +Containers +~~~~~~~~~~ + +Now you have an image you can run commands within a container managed by +docker. + + .. code-block:: ruby + + docker::run { 'helloworld': + image => 'base', + command => '/bin/sh -c "while true; do echo hello world; sleep 1; done"', + } + +This is equivalent to running the following command, but under upstart: + + .. code-block:: bash + + docker run -d base /bin/sh -c "while true; do echo hello world; sleep 1; done" + +Run also contains a number of optional parameters: + + .. code-block:: ruby + + docker::run { 'helloworld': + image => 'base', + command => '/bin/sh -c "while true; do echo hello world; sleep 1; done"', + ports => ['4444', '4555'], + volumes => ['/var/lib/counchdb', '/var/log'], + volumes_from => '6446ea52fbc9', + memory_limit => 10485760, # bytes + username => 'example', + hostname => 'example.com', + env => ['FOO=BAR', 'FOO2=BAR2'], + dns => ['8.8.8.8', '8.8.4.4'], + } + +Note that ports, env, dns and volumes can be set with either a single string +or as above with an array of values. From 72360b2cdfcfb70e72295b8f8cf7618257ce8826 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Petazzoni?= Date: Fri, 17 May 2013 05:46:32 -0700 Subject: [PATCH 05/16] Add information about kernel requirements This page will be helpful for people who: - want run run a custom kernel - want to enable memory/swap accounting on Debian/Ubuntu --- docs/sources/installation/index.rst | 1 + docs/sources/installation/kernel.rst | 126 +++++++++++++++++++++++++++ 2 files changed, 127 insertions(+) create mode 100644 docs/sources/installation/kernel.rst diff --git a/docs/sources/installation/index.rst b/docs/sources/installation/index.rst index 698d7f8ff1..1976f30ba0 100644 --- a/docs/sources/installation/index.rst +++ b/docs/sources/installation/index.rst @@ -20,3 +20,4 @@ Contents: rackspace archlinux upgrading + kernel diff --git a/docs/sources/installation/kernel.rst b/docs/sources/installation/kernel.rst new file mode 100644 index 0000000000..30ea192b19 --- /dev/null +++ b/docs/sources/installation/kernel.rst @@ -0,0 +1,126 @@ +.. _kernel: + +Kernel Requirements +=================== + + The officially supported kernel is the one recommended by the + :ref:`ubuntu_linux` installation path. It is the one that most developers + will use, and the one that receives the most attention from the core + contributors. If you decide to go with a different kernel and hit a bug, + please try to reproduce it with the official kernels first. + +If for some reason you cannot or do not want to use the "official" kernels, +here is some technical background about the features (both optional and +mandatory) that docker needs to run successfully. + +In short, you need kernel version 3.8 (or above), compiled to include +`AUFS support `_. Of course, you need to +enable cgroups and namespaces. + + +Namespaces and Cgroups +---------------------- + +You need to enable namespaces and cgroups, to the extend of what is needed +to run LXC containers. Technically, while namespaces have been introduced +in the early 2.6 kernels, we do not advise to try any kernel before 2.6.32 +to run LXC containers. Note that 2.6.32 has some documented issues regarding +network namespace setup and teardown; those issues are not a risk if you +run containers in a private environment, but can lead to denial-of-service +attacks if you want to run untrusted code in your containers. For more details, +see `[LP#720095 `_. + +Kernels 2.6.38, and every version since 3.2, have been deployed successfully +to run containerized production workloads. Feature-wise, there is no huge +improvement between 2.6.38 and up to 3.6 (as far as docker is concerned!). + +Starting with version 3.7, the kernel has basic support for +`Checkpoint/Restore In Userspace `_, which is not used by +docker at this point, but allows to suspend the state of a container to +disk and resume it later. + +Version 3.8 provides improvements in stability, which are deemed necessary +for the operation of docker. Versions 3.2 to 3.5 have been shown to +exhibit a reproducible bug (for more details, see issue +`#407 `_). + +Version 3.8 also brings better support for the +`setns() syscall `_ -- but this should not +be a concern since docker does not leverage on this feature for now. + +If you want a technical overview about those concepts, you might +want to check those articles on dotCloud's blog: +`about namespaces `_ +and `about cgroups `_. + + +Extra Cgroup Controllers +------------------------ + +Most control groups can be enabled or disabled individually. For instance, +you can decide that you do not want to compile support for the CPU or memory +controller. In some cases, the feature can be enabled or disabled at boot +time. It is worth mentioning that some distributions (like Debian) disable +"expensive" features, like the memory controller, because they can have +a significant performance impact. + +In the specific case of the memory cgroup, docker will detect if the cgroup +is available or not. If it's not, it will print a warning, and it won't +use the feature. If you want to enable that feature -- read on! + + +Memory and Swap Accounting on Debian/Ubuntu +------------------------------------------- + +If you use Debian or Ubuntu kernels, and want to enable memory and swap +accounting, you must add the following command-line parameters to your kernel:: + + cgroup_enable=memory swapaccount + +On Debian or Ubuntu systems, if you use the default GRUB bootloader, you can +add those parameters by editing ``/etc/default/grub`` and extending +``GRUB_CMDLINE_LINUX``. Look for the following line:: + + GRUB_CMDLINE_LINUX="" + +And replace it by the following one:: + + GRUB_CMDLINE_LINUX="cgroup_enable=memory swapaccount" + +Then run ``update-grub``, and reboot. + + +AUFS +---- + +Docker currently relies on AUFS, an unioning filesystem. +While AUFS is included in the kernels built by the Debian and Ubuntu +distributions, is not part of the standard kernel. This means that if +you decide to roll your own kernel, you will have to patch your +kernel tree to add AUFS. The process is documented on +`AUFS webpage `_. + +Note: the AUFS patch is fairly intrusive, but for the record, people have +successfully applied GRSEC and AUFS together, to obtain hardened production +kernels. + +If you want more information about that topic, there is an +`article about AUFS on dotCloud's blog +`_. + + +BTRFS, ZFS, OverlayFS... +------------------------ + +There is ongoing development on docker, to implement support for +`BTRFS `_ +(see github issue `#443 `_). + +People have also showed interest for `ZFS `_ +(using e.g. `ZFS-on-Linux `_) and OverlayFS. +The latter is functionally close to AUFS, and it might end up being included +in the stock kernel; so it's a strong candidate! + +Would you like to `contribute +`_ +support for your favorite filesystem? From 6301373c68990e8a92730de23a4da86b3442b4ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Petazzoni?= Date: Fri, 17 May 2013 10:20:30 -0700 Subject: [PATCH 06/16] Add some details about pre-3.8 kernels --- docs/sources/installation/kernel.rst | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/docs/sources/installation/kernel.rst b/docs/sources/installation/kernel.rst index 30ea192b19..2ec5940a7f 100644 --- a/docs/sources/installation/kernel.rst +++ b/docs/sources/installation/kernel.rst @@ -54,6 +54,29 @@ want to check those articles on dotCloud's blog: and `about cgroups `_. +Important Note About Pre-3.8 Kernels +------------------------------------ + +As mentioned above, kernels before 3.8 are not stable when used with docker. +In some circumstances, you will experience kernel "oopses", or even crashes. +The symptoms include: + +- a container being killed in the middle of an operation (e.g. an ``apt-get`` + command doesn't complete); +- kernel messages including mentioning calls to ``mntput`` or + ``d_hash_and_lookup``; +- kernel crash causing the machine to freeze for a few minutes, or even + completely. + +While it is still possible to use older kernels for development, it is +really not advised to do so. + +Docker checks the kernel version when it starts, and emits a warning if it +detects something older than 3.8. + +See issue `#407 `_ for details. + + Extra Cgroup Controllers ------------------------ From 537cce16f2776d076c58b80e9af7e9914e5dac74 Mon Sep 17 00:00:00 2001 From: Thatcher Peskens Date: Fri, 17 May 2013 19:35:46 -0700 Subject: [PATCH 07/16] Added a "we are hiring" to the frontpage of the docker website, and fixed two broken links in the docs. --- docs/Makefile | 9 ++------- docs/sources/api/registry_api.rst | 6 +++--- docs/sources/examples/python_web_app.rst | 4 ++-- docs/sources/faq.rst | 6 +++--- docs/theme/docker/static/img/hiring_graphic.png | Bin 0 -> 6954 bytes docs/website/index.html | 16 ++++++++++++++++ 6 files changed, 26 insertions(+), 15 deletions(-) create mode 100644 docs/theme/docker/static/img/hiring_graphic.png diff --git a/docs/Makefile b/docs/Makefile index 47ecd9ca89..26168b6f38 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -59,18 +59,13 @@ site: connect: @echo connecting dotcloud to www.docker.io website, make sure to use user 1 @cd _build/website/ ; \ - dotcloud list ; \ - dotcloud connect dockerwebsite + dotcloud connect dockerwebsite ; + dotcloud list push: @cd _build/website/ ; \ dotcloud push -github-deploy: docs - rm -fr github-deploy - git clone ssh://git@github.com/dotcloud/docker github-deploy - cd github-deploy && git checkout -f gh-pages && git rm -r * && rsync -avH ../_build/html/ ./ && touch .nojekyll && echo "docker.io" > CNAME && git add * && git commit -m "Updating docs" - $(VERSIONS): @echo "Hello world" diff --git a/docs/sources/api/registry_api.rst b/docs/sources/api/registry_api.rst index 35e1971213..e299584e17 100644 --- a/docs/sources/api/registry_api.rst +++ b/docs/sources/api/registry_api.rst @@ -85,7 +85,7 @@ On top of being a runtime for LXC, Docker is the Registry client. It supports: 5. Index returns true/false lettings registry know if it should proceed or error out 6. Get the payload for all layers -It’s possible to run docker pull https:///repositories/samalba/busybox. In this case, docker bypasses the Index. However the security is not guaranteed (in case Registry A is corrupted) because there won’t be any checksum checks. +It’s possible to run docker pull \https:///repositories/samalba/busybox. In this case, docker bypasses the Index. However the security is not guaranteed (in case Registry A is corrupted) because there won’t be any checksum checks. Currently registry redirects to s3 urls for downloads, going forward all downloads need to be streamed through the registry. The Registry will then abstract the calls to S3 by a top-level class which implements sub-classes for S3 and local storage. @@ -245,8 +245,8 @@ API (pushing repos foo/bar): The Index has two main purposes (along with its fancy social features): - Resolve short names (to avoid passing absolute URLs all the time) - - username/projectname -> https://registry.docker.io/users//repositories// - - team/projectname -> https://registry.docker.io/team//repositories// + - username/projectname -> \https://registry.docker.io/users//repositories// + - team/projectname -> \https://registry.docker.io/team//repositories// - Authenticate a user as a repos owner (for a central referenced repository) 3.1 Without an Index diff --git a/docs/sources/examples/python_web_app.rst b/docs/sources/examples/python_web_app.rst index 33caa52c1e..992a09dc42 100644 --- a/docs/sources/examples/python_web_app.rst +++ b/docs/sources/examples/python_web_app.rst @@ -58,7 +58,7 @@ Use the new image we just created and create a new container with network port 5 .. code-block:: bash docker logs $WEB_WORKER - * Running on http://0.0.0.0:5000/ + * Running on \http://0.0.0.0:5000/ view the logs for the new container using the WEB_WORKER variable, and if everything worked as planned you should see the line "Running on http://0.0.0.0:5000/" in the log output. @@ -70,7 +70,7 @@ lookup the public-facing port which is NAT-ed store the private port used by the .. code-block:: bash - curl http://`hostname`:$WEB_PORT + curl \http://`hostname`:$WEB_PORT Hello world! access the web app using curl. If everything worked as planned you should see the line "Hello world!" inside of your console. diff --git a/docs/sources/faq.rst b/docs/sources/faq.rst index 51fc00b306..b96ed06437 100644 --- a/docs/sources/faq.rst +++ b/docs/sources/faq.rst @@ -15,7 +15,7 @@ Most frequently asked questions. 3. **Does Docker run on Mac OS X or Windows?** - Not at this time, Docker currently only runs on Linux, but you can use VirtualBox to run Docker in a virtual machine on your box, and get the best of both worlds. Check out the MacOSX_ and Windows_ intallation guides. + Not at this time, Docker currently only runs on Linux, but you can use VirtualBox to run Docker in a virtual machine on your box, and get the best of both worlds. Check out the MacOSX_ and Windows_ installation guides. 4. **How do containers compare to virtual machines?** @@ -35,8 +35,8 @@ Most frequently asked questions. * `Ask questions on Stackoverflow`_ * `Join the conversation on Twitter`_ - .. _Windows: ../documentation/installation/windows.html - .. _MacOSX: ../documentation/installation/macos.html + .. _Windows: ../installation/windows/ + .. _MacOSX: ../installation/vagrant/ .. _the repo: http://www.github.com/dotcloud/docker .. _IRC\: docker on freenode: irc://chat.freenode.net#docker .. _Github: http://www.github.com/dotcloud/docker diff --git a/docs/theme/docker/static/img/hiring_graphic.png b/docs/theme/docker/static/img/hiring_graphic.png new file mode 100644 index 0000000000000000000000000000000000000000..b3a6b996440f5366e5bde53f392b0759e87abe17 GIT binary patch literal 6954 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 literal 0 HcmV?d00001 diff --git a/docs/website/index.html b/docs/website/index.html index 777f6b698f..2dad365ce4 100644 --- a/docs/website/index.html +++ b/docs/website/index.html @@ -127,6 +127,22 @@

Repeatability

Because each container is isolated in its own filesystem, they behave the same regardless of where, when, and alongside what they run.

+
+
+
+ +
+
+

Do you think it is cool to hack on docker? Join us!

+
    +
  • Work on open source
  • +
  • Program in Go
  • +
+ read more +
+
+ +
From 0143be42a13b9f8082ceddc64a5e523e45f54d88 Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Sat, 18 May 2013 14:03:53 +0000 Subject: [PATCH 08/16] add flush after each write when needed --- server.go | 36 ++++++++++++++++++------------------ utils/utils.go | 15 ++++++++++++--- 2 files changed, 30 insertions(+), 21 deletions(-) diff --git a/server.go b/server.go index f6a242606d..6576fac0db 100644 --- a/server.go +++ b/server.go @@ -98,7 +98,7 @@ func (srv *Server) ImageInsert(name, url, path string, out io.Writer) error { if err != nil { return err } - fmt.Fprintf(out, "%s\n", img.Id) + utils.FprintfFlush(out, "%s\n", img.Id) return nil } @@ -298,7 +298,7 @@ func (srv *Server) pullImage(out io.Writer, imgId, registry string, token []stri // FIXME: Launch the getRemoteImage() in goroutines for _, id := range history { if !srv.runtime.graph.Exists(id) { - fmt.Fprintf(out, "Pulling %s metadata\r\n", id) + utils.FprintfFlush(out, "Pulling %s metadata\r\n", id) imgJson, err := srv.registry.GetRemoteImageJson(id, registry, token) if err != nil { // FIXME: Keep goging in case of error? @@ -310,7 +310,7 @@ func (srv *Server) pullImage(out io.Writer, imgId, registry string, token []stri } // Get the layer - fmt.Fprintf(out, "Pulling %s fs layer\r\n", img.Id) + utils.FprintfFlush(out, "Pulling %s fs layer\r\n", img.Id) layer, contentLength, err := srv.registry.GetRemoteImageLayer(img.Id, registry, token) if err != nil { return err @@ -324,7 +324,7 @@ func (srv *Server) pullImage(out io.Writer, imgId, registry string, token []stri } func (srv *Server) pullRepository(out io.Writer, remote, askedTag string) error { - fmt.Fprintf(out, "Pulling repository %s from %s\r\n", remote, auth.IndexServerAddress()) + utils.FprintfFlush(out, "Pulling repository %s from %s\r\n", remote, auth.IndexServerAddress()) repoData, err := srv.registry.GetRepositoryData(remote) if err != nil { return err @@ -350,11 +350,11 @@ func (srv *Server) pullRepository(out io.Writer, remote, askedTag string) error if askedTag != "" && askedTag != img.Tag { continue } - fmt.Fprintf(out, "Pulling image %s (%s) from %s\n", img.Id, img.Tag, remote) + utils.FprintfFlush(out, "Pulling image %s (%s) from %s\n", img.Id, img.Tag, remote) success := false for _, ep := range repoData.Endpoints { if err := srv.pullImage(out, img.Id, "https://"+ep+"/v1", repoData.Tokens); err != nil { - fmt.Fprintf(out, "Error while retrieving image for tag: %s (%s); checking next endpoint\n", askedTag, err) + utils.FprintfFlush(out, "Error while retrieving image for tag: %s (%s); checking next endpoint\n", askedTag, err) continue } if err := srv.runtime.repositories.Set(remote, img.Tag, img.Id, true); err != nil { @@ -462,12 +462,12 @@ func (srv *Server) getImageList(localRepo map[string]string) ([]*registry.ImgDat } func (srv *Server) pushRepository(out io.Writer, name string, localRepo map[string]string) error { - fmt.Fprintf(out, "Processing checksums\n") + utils.FprintfFlush(out, "Processing checksums\n") imgList, err := srv.getImageList(localRepo) if err != nil { return err } - fmt.Fprintf(out, "Sending image list\n") + utils.FprintfFlush(out, "Sending image list\n") repoData, err := srv.registry.PushImageJsonIndex(name, imgList, false) if err != nil { @@ -476,18 +476,18 @@ func (srv *Server) pushRepository(out io.Writer, name string, localRepo map[stri // FIXME: Send only needed images for _, ep := range repoData.Endpoints { - fmt.Fprintf(out, "Pushing repository %s to %s (%d tags)\r\n", name, ep, len(localRepo)) + utils.FprintfFlush(out, "Pushing repository %s to %s (%d tags)\r\n", name, ep, len(localRepo)) // For each image within the repo, push them for _, elem := range imgList { if _, exists := repoData.ImgList[elem.Id]; exists { - fmt.Fprintf(out, "Image %s already on registry, skipping\n", name) + utils.FprintfFlush(out, "Image %s already on registry, skipping\n", name) continue } if err := srv.pushImage(out, name, elem.Id, ep, repoData.Tokens); err != nil { // FIXME: Continue on error? return err } - fmt.Fprintf(out, "Pushing tags for rev [%s] on {%s}\n", elem.Id, ep+"/users/"+name+"/"+elem.Tag) + utils.FprintfFlush(out, "Pushing tags for rev [%s] on {%s}\n", elem.Id, ep+"/users/"+name+"/"+elem.Tag) if err := srv.registry.PushRegistryTag(name, elem.Id, elem.Tag, ep, repoData.Tokens); err != nil { return err } @@ -505,7 +505,7 @@ func (srv *Server) pushImage(out io.Writer, remote, imgId, ep string, token []st if err != nil { return fmt.Errorf("Error while retreiving the path for {%s}: %s", imgId, err) } - fmt.Fprintf(out, "Pushing %s\r\n", imgId) + utils.FprintfFlush(out, "Pushing %s\r\n", imgId) // Make sure we have the image's checksum checksum, err := srv.getChecksum(imgId) @@ -520,7 +520,7 @@ func (srv *Server) pushImage(out io.Writer, remote, imgId, ep string, token []st // Send the json if err := srv.registry.PushImageJsonRegistry(imgData, jsonRaw, ep, token); err != nil { if err == registry.ErrAlreadyExists { - fmt.Fprintf(out, "Image %s already uploaded ; skipping\n", imgData.Id) + utils.FprintfFlush(out, "Image %s already uploaded ; skipping\n", imgData.Id) return nil } return err @@ -562,7 +562,7 @@ func (srv *Server) pushImage(out io.Writer, remote, imgId, ep string, token []st func (srv *Server) ImagePush(name, registry string, out io.Writer) error { img, err := srv.runtime.graph.Get(name) if err != nil { - fmt.Fprintf(out, "The push refers to a repository [%s] (len: %d)\n", name, len(srv.runtime.repositories.Repositories[name])) + utils.FprintfFlush(out, "The push refers to a repository [%s] (len: %d)\n", name, len(srv.runtime.repositories.Repositories[name])) // If it fails, try to get the repository if localRepo, exists := srv.runtime.repositories.Repositories[name]; exists { if err := srv.pushRepository(out, name, localRepo); err != nil { @@ -573,7 +573,7 @@ func (srv *Server) ImagePush(name, registry string, out io.Writer) error { return err } - fmt.Fprintf(out, "The push refers to an image: [%s]\n", name) + utils.FprintfFlush(out, "The push refers to an image: [%s]\n", name) if err := srv.pushImage(out, name, img.Id, registry, nil); err != nil { return err } @@ -589,14 +589,14 @@ func (srv *Server) ImageImport(src, repo, tag string, in io.Reader, out io.Write } else { u, err := url.Parse(src) if err != nil { - fmt.Fprintf(out, "Error: %s\n", err) + utils.FprintfFlush(out, "Error: %s\n", err) } if u.Scheme == "" { u.Scheme = "http" u.Host = src u.Path = "" } - fmt.Fprintln(out, "Downloading from", u) + utils.FprintfFlush(out, "Downloading from %s\n", u) // Download with curl (pretty progress bar) // If curl is not available, fallback to http.Get() resp, err = utils.Download(u.String(), out) @@ -615,7 +615,7 @@ func (srv *Server) ImageImport(src, repo, tag string, in io.Reader, out io.Write return err } } - fmt.Fprintln(out, img.ShortId()) + utils.FprintfFlush(out, "%s\n", img.ShortId()) return nil } diff --git a/utils/utils.go b/utils/utils.go index 88d0c87f5c..6a6a9f95b5 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -84,15 +84,15 @@ 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", r.readProgress, r.readTotal, fmt.Sprintf("%.0f%%", float64(r.readProgress)/float64(r.readTotal)*100)) + FprintfFlush(r.output, r.template+"\r", r.readProgress, r.readTotal, fmt.Sprintf("%.0f%%", float64(r.readProgress)/float64(r.readTotal)*100)) } else { - fmt.Fprintf(r.output, r.template+"\r", r.readProgress, "?", "n/a") + FprintfFlush(r.output, r.template+"\r", r.readProgress, "?", "n/a") } r.lastUpdate = r.readProgress } // Send newline when complete if err != nil { - fmt.Fprintf(r.output, "\n") + FprintfFlush(r.output, "\n") } return read, err @@ -530,3 +530,12 @@ func GetKernelVersion() (*KernelVersionInfo, error) { Flavor: flavor, }, nil } + + +func FprintfFlush(w io.Writer, format string, a ...interface{}) (n int, err error) { + n, err = fmt.Fprintf(w, format, a...) + if f, ok := w.(http.Flusher); ok { + f.Flush() + } + return n, err +} \ No newline at end of file From 2b55874584f034b5113359ced6b05c143d31e03c Mon Sep 17 00:00:00 2001 From: Francisco Souza Date: Sat, 18 May 2013 22:55:59 -0300 Subject: [PATCH 09/16] utils: fix compilation on Darwin Although Docker daemon does not work on Darwin, the API client will have to work. That said, I'm fixing the compilation of the package on Darwin. --- utils/uname_darwin.go | 7 +++++-- utils/uname_linux.go | 5 +++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/utils/uname_darwin.go b/utils/uname_darwin.go index d799554bbd..a875e8c600 100644 --- a/utils/uname_darwin.go +++ b/utils/uname_darwin.go @@ -2,9 +2,12 @@ package utils import ( "errors" - "syscall" ) -func uname() (*syscall.Utsname, error) { +type Utsname struct { + Release [65]byte +} + +func uname() (*Utsname, error) { return nil, errors.New("Kernel version detection is not available on darwin") } diff --git a/utils/uname_linux.go b/utils/uname_linux.go index 675a89b001..6e47bcc824 100644 --- a/utils/uname_linux.go +++ b/utils/uname_linux.go @@ -4,8 +4,9 @@ import ( "syscall" ) -// FIXME: Move this to utils package -func uname() (*syscall.Utsname, error) { +type Utsname syscall.Utsname + +func uname() (*Utsname, error) { uts := &syscall.Utsname{} if err := syscall.Uname(uts); err != nil { From ea7fdecd41a107d038503578324197780df002a8 Mon Sep 17 00:00:00 2001 From: Phil Spitler Date: Sun, 19 May 2013 09:37:02 -0300 Subject: [PATCH 10/16] Fixed typo in remote API doc --- docs/sources/api/docker_remote_api.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/sources/api/docker_remote_api.rst b/docs/sources/api/docker_remote_api.rst index 03f1d4b9cf..1dee086d0d 100644 --- a/docs/sources/api/docker_remote_api.rst +++ b/docs/sources/api/docker_remote_api.rst @@ -9,7 +9,7 @@ Docker Remote API - The Remote API is replacing rcli - Default port in the docker deamon is 4243 -- The API tends to be REST, but for some complex commands, like attach or pull, the HTTP connection in hijacked to transport stdout stdin and stderr +- The API tends to be REST, but for some complex commands, like attach or pull, the HTTP connection is hijacked to transport stdout stdin and stderr 2. Endpoints ============ From 8291f8b85ca653e13f17ffb126b57bb6b40c1cb5 Mon Sep 17 00:00:00 2001 From: Francisco Souza Date: Sun, 19 May 2013 11:57:45 -0300 Subject: [PATCH 11/16] docs/remote_api: remove trunc_cmd from /containers/ps example Apparently, this parameter does not exist anymore. --- docs/sources/api/docker_remote_api.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/sources/api/docker_remote_api.rst b/docs/sources/api/docker_remote_api.rst index 1dee086d0d..2b1aad0e84 100644 --- a/docs/sources/api/docker_remote_api.rst +++ b/docs/sources/api/docker_remote_api.rst @@ -28,7 +28,7 @@ List containers .. sourcecode:: http - GET /containers/ps?trunc_cmd=0&all=1&before=8dfafdbc3a40 HTTP/1.1 + GET /containers/ps?all=1&before=8dfafdbc3a40 HTTP/1.1 **Example response**: From 0b785487fe752cc4111a3f0005a2ae090b30c52b Mon Sep 17 00:00:00 2001 From: Kiran Gangadharan Date: Sun, 19 May 2013 21:04:34 +0530 Subject: [PATCH 12/16] Fixed typos --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 1f69e4d833..c83feeae58 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ for containerization, including Linux with [openvz](http://openvz.org), [vserver Docker builds on top of these low-level primitives to offer developers a portable format and runtime environment that solves all 4 problems. Docker containers are small (and their transfer can be optimized with layers), they have basically zero memory and cpu overhead, -the are completely portable and are designed from the ground up with an application-centric design. +they are completely portable and are designed from the ground up with an application-centric design. The best part: because docker operates at the OS level, it can still be run inside a VM! @@ -46,7 +46,7 @@ Docker does not require that you buy into a particular programming language, fra Is your application a unix process? Does it use files, tcp connections, environment variables, standard unix streams and command-line arguments as inputs and outputs? Then docker can run it. -Can your application's build be expressed a sequence of such commands? Then docker can build it. +Can your application's build be expressed as a sequence of such commands? Then docker can build it. ## Escape dependency hell @@ -70,7 +70,7 @@ Docker solves dependency hell by giving the developer a simple way to express *a and streamline the process of assembling them. If this makes you think of [XKCD 927](http://xkcd.com/927/), don't worry. Docker doesn't *replace* your favorite packaging systems. It simply orchestrates their use in a simple and repeatable way. How does it do that? With layers. -Docker defines a build as running a sequence unix commands, one after the other, in the same container. Build commands modify the contents of the container +Docker defines a build as running a sequence of unix commands, one after the other, in the same container. Build commands modify the contents of the container (usually by installing new files on the filesystem), the next command modifies it some more, etc. Since each build command inherits the result of the previous commands, the *order* in which the commands are executed expresses *dependencies*. @@ -293,7 +293,7 @@ a format that is self-describing and portable, so that any compliant runtime can The spec for Standard Containers is currently a work in progress, but it is very straightforward. It mostly defines 1) an image format, 2) a set of standard operations, and 3) an execution environment. -A great analogy for this is the shipping container. Just like Standard Containers are a fundamental unit of software delivery, shipping containers (http://bricks.argz.com/ins/7823-1/12) are a fundamental unit of physical delivery. +A great analogy for this is the shipping container. Just like how Standard Containers are a fundamental unit of software delivery, shipping containers (http://bricks.argz.com/ins/7823-1/12) are a fundamental unit of physical delivery. ### 1. STANDARD OPERATIONS @@ -321,7 +321,7 @@ Similarly, before Standard Containers, by the time a software component ran in p ### 5. INDUSTRIAL-GRADE DELIVERY -There are 17 million shipping containers in existence, packed with every physical good imaginable. Every single one of them can be loaded on the same boats, by the same cranes, in the same facilities, and sent anywhere in the World with incredible efficiency. It is embarrassing to think that a 30 ton shipment of coffee can safely travel half-way across the World in *less time* than it takes a software team to deliver its code from one datacenter to another sitting 10 miles away. +There are 17 million shipping containers in existence, packed with every physical good imaginable. Every single one of them can be loaded onto the same boats, by the same cranes, in the same facilities, and sent anywhere in the World with incredible efficiency. It is embarrassing to think that a 30 ton shipment of coffee can safely travel half-way across the World in *less time* than it takes a software team to deliver its code from one datacenter to another sitting 10 miles away. With Standard Containers we can put an end to that embarrassment, by making INDUSTRIAL-GRADE DELIVERY of software a reality. From 98b0fd173b7b59316d776534b84ecc9ab0a1da77 Mon Sep 17 00:00:00 2001 From: "Guillaume J. Charmes" Date: Mon, 20 May 2013 10:22:50 -0700 Subject: [PATCH 13/16] Make the printflfush an interface --- server.go | 42 ++++++++++++++++++++++++------------------ utils/utils.go | 19 +++++++++++-------- 2 files changed, 35 insertions(+), 26 deletions(-) diff --git a/server.go b/server.go index 6576fac0db..b07e85b44c 100644 --- a/server.go +++ b/server.go @@ -68,6 +68,7 @@ func (srv *Server) ImagesSearch(term string) ([]ApiSearch, error) { } func (srv *Server) ImageInsert(name, url, path string, out io.Writer) error { + out = &utils.WriteFlusher{W: out} img, err := srv.runtime.repositories.LookupImage(name) if err != nil { return err @@ -98,7 +99,7 @@ func (srv *Server) ImageInsert(name, url, path string, out io.Writer) error { if err != nil { return err } - utils.FprintfFlush(out, "%s\n", img.Id) + fmt.Fprintf(out, "%s\n", img.Id) return nil } @@ -289,6 +290,7 @@ func (srv *Server) ContainerTag(name, repo, tag string, force bool) error { } func (srv *Server) pullImage(out io.Writer, imgId, registry string, token []string) error { + out = &utils.WriteFlusher{W: out} history, err := srv.registry.GetRemoteHistory(imgId, registry, token) if err != nil { return err @@ -298,7 +300,7 @@ func (srv *Server) pullImage(out io.Writer, imgId, registry string, token []stri // FIXME: Launch the getRemoteImage() in goroutines for _, id := range history { if !srv.runtime.graph.Exists(id) { - utils.FprintfFlush(out, "Pulling %s metadata\r\n", id) + fmt.Fprintf(out, "Pulling %s metadata\r\n", id) imgJson, err := srv.registry.GetRemoteImageJson(id, registry, token) if err != nil { // FIXME: Keep goging in case of error? @@ -310,7 +312,7 @@ func (srv *Server) pullImage(out io.Writer, imgId, registry string, token []stri } // Get the layer - utils.FprintfFlush(out, "Pulling %s fs layer\r\n", img.Id) + fmt.Fprintf(out, "Pulling %s fs layer\r\n", img.Id) layer, contentLength, err := srv.registry.GetRemoteImageLayer(img.Id, registry, token) if err != nil { return err @@ -324,7 +326,8 @@ func (srv *Server) pullImage(out io.Writer, imgId, registry string, token []stri } func (srv *Server) pullRepository(out io.Writer, remote, askedTag string) error { - utils.FprintfFlush(out, "Pulling repository %s from %s\r\n", remote, auth.IndexServerAddress()) + out = &utils.WriteFlusher{W: out} + fmt.Fprintf(out, "Pulling repository %s from %s\r\n", remote, auth.IndexServerAddress()) repoData, err := srv.registry.GetRepositoryData(remote) if err != nil { return err @@ -350,11 +353,11 @@ func (srv *Server) pullRepository(out io.Writer, remote, askedTag string) error if askedTag != "" && askedTag != img.Tag { continue } - utils.FprintfFlush(out, "Pulling image %s (%s) from %s\n", img.Id, img.Tag, remote) + fmt.Fprintf(out, "Pulling image %s (%s) from %s\n", img.Id, img.Tag, remote) success := false for _, ep := range repoData.Endpoints { if err := srv.pullImage(out, img.Id, "https://"+ep+"/v1", repoData.Tokens); err != nil { - utils.FprintfFlush(out, "Error while retrieving image for tag: %s (%s); checking next endpoint\n", askedTag, err) + fmt.Fprintf(out, "Error while retrieving image for tag: %s (%s); checking next endpoint\n", askedTag, err) continue } if err := srv.runtime.repositories.Set(remote, img.Tag, img.Id, true); err != nil { @@ -462,12 +465,13 @@ func (srv *Server) getImageList(localRepo map[string]string) ([]*registry.ImgDat } func (srv *Server) pushRepository(out io.Writer, name string, localRepo map[string]string) error { - utils.FprintfFlush(out, "Processing checksums\n") + out = &utils.WriteFlusher{W: out} + fmt.Fprintf(out, "Processing checksums\n") imgList, err := srv.getImageList(localRepo) if err != nil { return err } - utils.FprintfFlush(out, "Sending image list\n") + fmt.Fprintf(out, "Sending image list\n") repoData, err := srv.registry.PushImageJsonIndex(name, imgList, false) if err != nil { @@ -476,18 +480,18 @@ func (srv *Server) pushRepository(out io.Writer, name string, localRepo map[stri // FIXME: Send only needed images for _, ep := range repoData.Endpoints { - utils.FprintfFlush(out, "Pushing repository %s to %s (%d tags)\r\n", name, ep, len(localRepo)) + fmt.Fprintf(out, "Pushing repository %s to %s (%d tags)\r\n", name, ep, len(localRepo)) // For each image within the repo, push them for _, elem := range imgList { if _, exists := repoData.ImgList[elem.Id]; exists { - utils.FprintfFlush(out, "Image %s already on registry, skipping\n", name) + fmt.Fprintf(out, "Image %s already on registry, skipping\n", name) continue } if err := srv.pushImage(out, name, elem.Id, ep, repoData.Tokens); err != nil { // FIXME: Continue on error? return err } - utils.FprintfFlush(out, "Pushing tags for rev [%s] on {%s}\n", elem.Id, ep+"/users/"+name+"/"+elem.Tag) + fmt.Fprintf(out, "Pushing tags for rev [%s] on {%s}\n", elem.Id, ep+"/users/"+name+"/"+elem.Tag) if err := srv.registry.PushRegistryTag(name, elem.Id, elem.Tag, ep, repoData.Tokens); err != nil { return err } @@ -501,11 +505,12 @@ func (srv *Server) pushRepository(out io.Writer, name string, localRepo map[stri } func (srv *Server) pushImage(out io.Writer, remote, imgId, ep string, token []string) error { + out = &utils.WriteFlusher{W: out} jsonRaw, err := ioutil.ReadFile(path.Join(srv.runtime.graph.Root, imgId, "json")) if err != nil { return fmt.Errorf("Error while retreiving the path for {%s}: %s", imgId, err) } - utils.FprintfFlush(out, "Pushing %s\r\n", imgId) + fmt.Fprintf(out, "Pushing %s\r\n", imgId) // Make sure we have the image's checksum checksum, err := srv.getChecksum(imgId) @@ -520,7 +525,7 @@ func (srv *Server) pushImage(out io.Writer, remote, imgId, ep string, token []st // Send the json if err := srv.registry.PushImageJsonRegistry(imgData, jsonRaw, ep, token); err != nil { if err == registry.ErrAlreadyExists { - utils.FprintfFlush(out, "Image %s already uploaded ; skipping\n", imgData.Id) + fmt.Fprintf(out, "Image %s already uploaded ; skipping\n", imgData.Id) return nil } return err @@ -560,9 +565,10 @@ func (srv *Server) pushImage(out io.Writer, remote, imgId, ep string, token []st } func (srv *Server) ImagePush(name, registry string, out io.Writer) error { + out = &utils.WriteFlusher{W: out} img, err := srv.runtime.graph.Get(name) if err != nil { - utils.FprintfFlush(out, "The push refers to a repository [%s] (len: %d)\n", name, len(srv.runtime.repositories.Repositories[name])) + fmt.Fprintf(out, "The push refers to a repository [%s] (len: %d)\n", name, len(srv.runtime.repositories.Repositories[name])) // If it fails, try to get the repository if localRepo, exists := srv.runtime.repositories.Repositories[name]; exists { if err := srv.pushRepository(out, name, localRepo); err != nil { @@ -573,7 +579,7 @@ func (srv *Server) ImagePush(name, registry string, out io.Writer) error { return err } - utils.FprintfFlush(out, "The push refers to an image: [%s]\n", name) + fmt.Fprintf(out, "The push refers to an image: [%s]\n", name) if err := srv.pushImage(out, name, img.Id, registry, nil); err != nil { return err } @@ -589,14 +595,14 @@ func (srv *Server) ImageImport(src, repo, tag string, in io.Reader, out io.Write } else { u, err := url.Parse(src) if err != nil { - utils.FprintfFlush(out, "Error: %s\n", err) + fmt.Fprintf(out, "Error: %s\n", err) } if u.Scheme == "" { u.Scheme = "http" u.Host = src u.Path = "" } - utils.FprintfFlush(out, "Downloading from %s\n", u) + fmt.Fprintf(out, "Downloading from %s\n", u) // Download with curl (pretty progress bar) // If curl is not available, fallback to http.Get() resp, err = utils.Download(u.String(), out) @@ -615,7 +621,7 @@ func (srv *Server) ImageImport(src, repo, tag string, in io.Reader, out io.Write return err } } - utils.FprintfFlush(out, "%s\n", img.ShortId()) + fmt.Fprintf(out, "%s\n", img.ShortId()) return nil } diff --git a/utils/utils.go b/utils/utils.go index 6a6a9f95b5..a2fd3bde6d 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -84,15 +84,15 @@ func (r *progressReader) Read(p []byte) (n int, err error) { } if r.readProgress-r.lastUpdate > updateEvery || err != nil { if r.readTotal > 0 { - FprintfFlush(r.output, r.template+"\r", r.readProgress, r.readTotal, fmt.Sprintf("%.0f%%", float64(r.readProgress)/float64(r.readTotal)*100)) + fmt.Fprintf(r.output, r.template+"\r", r.readProgress, r.readTotal, fmt.Sprintf("%.0f%%", float64(r.readProgress)/float64(r.readTotal)*100)) } else { - FprintfFlush(r.output, r.template+"\r", r.readProgress, "?", "n/a") + fmt.Fprintf(r.output, r.template+"\r", r.readProgress, "?", "n/a") } r.lastUpdate = r.readProgress } // Send newline when complete if err != nil { - FprintfFlush(r.output, "\n") + fmt.Fprintf(r.output, "\n") } return read, err @@ -104,7 +104,7 @@ func ProgressReader(r io.ReadCloser, size int, output io.Writer, template string if template == "" { template = "%v/%v (%v)" } - return &progressReader{r, output, size, 0, 0, template} + return &progressReader{r, &WriteFlusher{W: output}, size, 0, 0, template} } // HumanDuration returns a human-readable approximation of a duration @@ -531,11 +531,14 @@ func GetKernelVersion() (*KernelVersionInfo, error) { }, nil } +type WriteFlusher struct { + W io.Writer +} -func FprintfFlush(w io.Writer, format string, a ...interface{}) (n int, err error) { - n, err = fmt.Fprintf(w, format, a...) - if f, ok := w.(http.Flusher); ok { +func (wf *WriteFlusher) Write(b []byte) (n int, err error) { + n, err = wf.W.Write(b) + if f, ok := wf.W.(http.Flusher); ok { f.Flush() } return n, err -} \ No newline at end of file +} From 1b007828c93bc472af3306b8da1d72a84b484f12 Mon Sep 17 00:00:00 2001 From: unclejack Date: Mon, 20 May 2013 20:43:09 +0300 Subject: [PATCH 14/16] fix compilation on linux --- utils/uname_linux.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/uname_linux.go b/utils/uname_linux.go index 6e47bcc824..063f932c99 100644 --- a/utils/uname_linux.go +++ b/utils/uname_linux.go @@ -6,7 +6,7 @@ import ( type Utsname syscall.Utsname -func uname() (*Utsname, error) { +func uname() (*syscall.Utsname, error) { uts := &syscall.Utsname{} if err := syscall.Uname(uts); err != nil { From ae9d7a5167da58de9a1a4beac489cf1e6adcea11 Mon Sep 17 00:00:00 2001 From: "Guillaume J. Charmes" Date: Mon, 20 May 2013 10:58:35 -0700 Subject: [PATCH 15/16] Avoid cast each write for flusher --- server.go | 12 ++++++------ utils/utils.go | 25 +++++++++++++++++++------ 2 files changed, 25 insertions(+), 12 deletions(-) diff --git a/server.go b/server.go index b07e85b44c..956a4f8d36 100644 --- a/server.go +++ b/server.go @@ -68,7 +68,7 @@ func (srv *Server) ImagesSearch(term string) ([]ApiSearch, error) { } func (srv *Server) ImageInsert(name, url, path string, out io.Writer) error { - out = &utils.WriteFlusher{W: out} + out = utils.NewWriteFlusher(out) img, err := srv.runtime.repositories.LookupImage(name) if err != nil { return err @@ -290,7 +290,7 @@ func (srv *Server) ContainerTag(name, repo, tag string, force bool) error { } func (srv *Server) pullImage(out io.Writer, imgId, registry string, token []string) error { - out = &utils.WriteFlusher{W: out} + out = utils.NewWriteFlusher(out) history, err := srv.registry.GetRemoteHistory(imgId, registry, token) if err != nil { return err @@ -326,7 +326,7 @@ func (srv *Server) pullImage(out io.Writer, imgId, registry string, token []stri } func (srv *Server) pullRepository(out io.Writer, remote, askedTag string) error { - out = &utils.WriteFlusher{W: out} + out = utils.NewWriteFlusher(out) fmt.Fprintf(out, "Pulling repository %s from %s\r\n", remote, auth.IndexServerAddress()) repoData, err := srv.registry.GetRepositoryData(remote) if err != nil { @@ -465,7 +465,7 @@ func (srv *Server) getImageList(localRepo map[string]string) ([]*registry.ImgDat } func (srv *Server) pushRepository(out io.Writer, name string, localRepo map[string]string) error { - out = &utils.WriteFlusher{W: out} + out = utils.NewWriteFlusher(out) fmt.Fprintf(out, "Processing checksums\n") imgList, err := srv.getImageList(localRepo) if err != nil { @@ -505,7 +505,7 @@ func (srv *Server) pushRepository(out io.Writer, name string, localRepo map[stri } func (srv *Server) pushImage(out io.Writer, remote, imgId, ep string, token []string) error { - out = &utils.WriteFlusher{W: out} + out = utils.NewWriteFlusher(out) jsonRaw, err := ioutil.ReadFile(path.Join(srv.runtime.graph.Root, imgId, "json")) if err != nil { return fmt.Errorf("Error while retreiving the path for {%s}: %s", imgId, err) @@ -565,7 +565,7 @@ func (srv *Server) pushImage(out io.Writer, remote, imgId, ep string, token []st } func (srv *Server) ImagePush(name, registry string, out io.Writer) error { - out = &utils.WriteFlusher{W: out} + out = utils.NewWriteFlusher(out) img, err := srv.runtime.graph.Get(name) if err != nil { fmt.Fprintf(out, "The push refers to a repository [%s] (len: %d)\n", name, len(srv.runtime.repositories.Repositories[name])) diff --git a/utils/utils.go b/utils/utils.go index a2fd3bde6d..150eae8570 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -104,7 +104,7 @@ func ProgressReader(r io.ReadCloser, size int, output io.Writer, template string if template == "" { template = "%v/%v (%v)" } - return &progressReader{r, &WriteFlusher{W: output}, size, 0, 0, template} + return &progressReader{r, NewWriteFlusher(output), size, 0, 0, template} } // HumanDuration returns a human-readable approximation of a duration @@ -531,14 +531,27 @@ func GetKernelVersion() (*KernelVersionInfo, error) { }, nil } +type NopFlusher struct{} + +func (f *NopFlusher) Flush() {} + type WriteFlusher struct { - W io.Writer + w io.Writer + flusher http.Flusher } func (wf *WriteFlusher) Write(b []byte) (n int, err error) { - n, err = wf.W.Write(b) - if f, ok := wf.W.(http.Flusher); ok { - f.Flush() - } + n, err = wf.w.Write(b) + wf.flusher.Flush() return n, err } + +func NewWriteFlusher(w io.Writer) *WriteFlusher { + var flusher http.Flusher + if f, ok := w.(http.Flusher); ok { + flusher = f + } else { + flusher = &NopFlusher{} + } + return &WriteFlusher{w: w, flusher: flusher} +} From d756ae4cb376e218ff4dd35a7b9a9fa346fbb04d Mon Sep 17 00:00:00 2001 From: "Guillaume J. Charmes" Date: Mon, 20 May 2013 15:19:05 -0700 Subject: [PATCH 16/16] Tag all images after pulling them --- server.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/server.go b/server.go index d336248271..3f27a178cb 100644 --- a/server.go +++ b/server.go @@ -371,11 +371,7 @@ func (srv *Server) pullRepository(out io.Writer, remote, askedTag string) error fmt.Fprintf(out, "Error while retrieving image for tag: %s (%s); checking next endpoint\n", askedTag, err) continue } - if err := srv.runtime.repositories.Set(remote, img.Tag, img.Id, true); err != nil { - return err - } success = true - delete(tagsList, img.Tag) break } if !success {