From fac4cedcc105a7b5a6789917ddf9a5e9745bd2d3 Mon Sep 17 00:00:00 2001 From: Hector Castro Date: Tue, 3 Sep 2013 16:29:07 -0400 Subject: [PATCH 01/10] Add a Docker example for running Riak. --- docs/sources/examples/index.rst | 1 + .../sources/examples/running_riak_service.rst | 151 ++++++++++++++++++ 2 files changed, 152 insertions(+) create mode 100644 docs/sources/examples/running_riak_service.rst diff --git a/docs/sources/examples/index.rst b/docs/sources/examples/index.rst index 2664b95e54..1114790280 100644 --- a/docs/sources/examples/index.rst +++ b/docs/sources/examples/index.rst @@ -22,3 +22,4 @@ Contents: couchdb_data_volumes postgresql_service mongodb + running_riak_service diff --git a/docs/sources/examples/running_riak_service.rst b/docs/sources/examples/running_riak_service.rst new file mode 100644 index 0000000000..d98de6a77c --- /dev/null +++ b/docs/sources/examples/running_riak_service.rst @@ -0,0 +1,151 @@ +:title: Running a Riak service +:description: Build a Docker image with Riak pre-installed +:keywords: docker, example, package installation, networking, riak + +Riak Service +============================== + +.. include:: example_header.inc + +The goal of this example is to show you how to build a Docker image with Riak +pre-installed. + +Creating a ``Dockerfile`` ++++++++++++++++++++++++++ + +Create an empty file called ``Dockerfile``: + +.. code-block:: bash + + touch Dockerfile + +Next, define the parent image you want to use to build your image on top of. +We’ll use `Ubuntu `_ (tag: ``latest``), +which is available on the `docker index `_: + +.. code-block:: bash + + # Riak + # + # VERSION 0.1.0 + + # Use the Ubuntu base image provided by dotCloud + FROM ubuntu:latest + MAINTAINER Hector Castro hector@basho.com + +Next, we update the APT cache and apply any updates: + +.. code-block:: bash + + # Update the APT cache + RUN sed -i.bak 's/main$/main universe/' /etc/apt/sources.list + RUN apt-get update + RUN apt-get upgrade -y + +After that, we install and setup a few dependencies: + +- ``curl`` is used to download Basho's APT repository key +- ``lsb-release`` helps us derive the Ubuntu release codename +- ``openssh-server`` allows us to login to containers remotely and join Riak + nodes to form a cluster +- ``supervisor`` is used manage the OpenSSH and Riak processes + +.. code-block:: bash + + # Install and setup project dependencies + RUN apt-get install -y curl lsb-release supervisor openssh-server + + RUN mkdir -p /var/run/sshd + RUN mkdir -p /var/log/supervisor + + RUN locale-gen en_US en_US.UTF-8 + + ADD supervisord.conf /etc/supervisor/conf.d/supervisord.conf + + RUN echo 'root:basho' | chpasswd + +Next, we add Basho's APT repository: + +.. code-block:: bash + + RUN curl -s http://apt.basho.com/gpg/basho.apt.key | apt-key add -- + RUN echo "deb http://apt.basho.com $(lsb_release -cs) main" > /etc/apt/sources.list.d/basho.list + RUN apt-get update + +After that, we install Riak and alter a few defaults: + +.. code-block:: bash + + # Install Riak and prepare it to run + RUN apt-get install -y riak + RUN sed -i.bak 's/127.0.0.1/0.0.0.0/' /etc/riak/app.config + RUN echo "ulimit -n 4096" >> /etc/default/riak + +Almost there. Next, we add a hack to get us by the lack of ``initctl``: + +.. code-block:: bash + + # Hack for initctl + # See: https://github.com/dotcloud/docker/issues/1024 + RUN dpkg-divert --local --rename --add /sbin/initctl + RUN ln -s /bin/true /sbin/initctl + +Then, we expose the Riak Protocol Buffers and HTTP interfaces, along with SSH: + +.. code-block:: bash + + # Expose Riak Protocol Buffers and HTTP interfaces, along with SSH + EXPOSE 8087 8098 22 + +Finally, run ``supervisord`` so that Riak and OpenSSH are started: + +.. code-block:: bash + + CMD ["/usr/bin/supervisord"] + +Create a ``supervisord`` configuration file ++++++++++++++++++++++++++++++++++++++++++++ + +Create an empty file called ``supervisord.conf``. Make sure it's at the same +level as your ``Dockerfile``: + +.. code-block:: bash + + touch supervisord.conf + +Populate it with the following program definitions: + +.. code-block:: bash + + [supervisord] + nodaemon=true + + [program:sshd] + command=/usr/sbin/sshd -D + stdout_logfile=/var/log/supervisor/%(program_name)s.log + stderr_logfile=/var/log/supervisor/%(program_name)s.log + autorestart=true + + [program:riak] + command=bash -c ". /etc/default/riak && /usr/sbin/riak console" + pidfile=/var/log/riak/riak.pid + stdout_logfile=/var/log/supervisor/%(program_name)s.log + stderr_logfile=/var/log/supervisor/%(program_name)s.log + +Build the Docker image for Riak ++++++++++++++++++++++++++++++++ + +Now you should be able to build a Docker image for Riak: + +.. code-block:: bash + + docker build -t "/riak" . + +Next steps +++++++++++ + +Riak is a distributed database. Many production deployments consist of `at +least five nodes `_. See the `docker-riak `_ project details on how to deploy a Riak cluster using Docker and +Pipework. From 82dd417ef6966b0f3d812122c18420feb64f49a7 Mon Sep 17 00:00:00 2001 From: Tianon Gravi Date: Thu, 5 Sep 2013 13:36:16 -0600 Subject: [PATCH 02/10] Reformat Gentoo install instructions to 80 columns --- docs/sources/installation/gentoolinux.rst | 47 ++++++++++++++++++----- 1 file changed, 37 insertions(+), 10 deletions(-) diff --git a/docs/sources/installation/gentoolinux.rst b/docs/sources/installation/gentoolinux.rst index aa809768ee..5749b6dc0b 100644 --- a/docs/sources/installation/gentoolinux.rst +++ b/docs/sources/installation/gentoolinux.rst @@ -11,7 +11,10 @@ Gentoo Linux .. include:: install_unofficial.inc -Installing Docker on Gentoo Linux can be accomplished by using the overlay provided at https://github.com/tianon/docker-overlay. The most up-to-date documentation for properly installing the overlay can be found in the overlay README. The information here is provided for reference, and may be out of date. +Installing Docker on Gentoo Linux can be accomplished by using the overlay +provided at https://github.com/tianon/docker-overlay. The most up-to-date +documentation for properly installing the overlay can be found in the overlay +README. The information here is provided for reference, and may be out of date. Installation ^^^^^^^^^^^^ @@ -22,30 +25,49 @@ Ensure that layman is installed: sudo emerge -av app-portage/layman -Using your favorite editor, add ``https://raw.github.com/tianon/docker-overlay/master/repositories.xml`` to the ``overlays`` section in ``/etc/layman/layman.cfg`` (as per instructions on the `Gentoo Wiki `_), then invoke the following: +Using your favorite editor, add +``https://raw.github.com/tianon/docker-overlay/master/repositories.xml`` to the +``overlays`` section in ``/etc/layman/layman.cfg`` (as per instructions on the +`Gentoo Wiki `_), +then invoke the following: .. code-block:: bash sudo layman -f -a docker -Once that completes, the ``app-emulation/lxc-docker`` package will be available for emerge: +Once that completes, the ``app-emulation/lxc-docker`` package will be available +for emerge: .. code-block:: bash sudo emerge -av app-emulation/lxc-docker -If you prefer to use the official binaries, or just do not wish to compile docker, emerge ``app-emulation/lxc-docker-bin`` instead. It is important to remember that Gentoo is still an unsupported platform, even when using the official binaries. +If you prefer to use the official binaries, or just do not wish to compile +docker, emerge ``app-emulation/lxc-docker-bin`` instead. It is important to +remember that Gentoo is still an unsupported platform, even when using the +official binaries. -The package should already include all the necessary dependencies. For the simplest installation experience, use ``sys-kernel/aufs-sources`` directly as your kernel sources. If you prefer not to use ``sys-kernel/aufs-sources``, the portage tree also contains ``sys-fs/aufs3``, which contains the patches necessary for adding AUFS support to other kernel source packages (and a ``kernel-patch`` use flag to perform the patching automatically). +The package should already include all the necessary dependencies. For the +simplest installation experience, use ``sys-kernel/aufs-sources`` directly as +your kernel sources. If you prefer not to use ``sys-kernel/aufs-sources``, the +portage tree also contains ``sys-fs/aufs3``, which contains the patches +necessary for adding AUFS support to other kernel source packages (and a +``kernel-patch`` use flag to perform the patching automatically). -Between ``app-emulation/lxc`` and ``app-emulation/lxc-docker``, all the necessary kernel configuration flags should be checked for and warned about in the standard manner. +Between ``app-emulation/lxc`` and ``app-emulation/lxc-docker``, all the +necessary kernel configuration flags should be checked for and warned about in +the standard manner. -If any issues arise from this ebuild or the resulting binary, including and especially missing kernel configuration flags and/or dependencies, `open an issue `_ on the docker-overlay repository or ping tianon in the #docker IRC channel. +If any issues arise from this ebuild or the resulting binary, including and +especially missing kernel configuration flags and/or dependencies, `open an +issue `_ on the docker-overlay +repository or ping tianon in the #docker IRC channel. Starting Docker ^^^^^^^^^^^^^^^ -Ensure that you are running a kernel that includes the necessary AUFS support and includes all the necessary modules and/or configuration for LXC. +Ensure that you are running a kernel that includes the necessary AUFS support +and includes all the necessary modules and/or configuration for LXC. OpenRC ------ @@ -80,7 +102,8 @@ To start on system boot: Network Configuration ^^^^^^^^^^^^^^^^^^^^^ -IPv4 packet forwarding is disabled by default, so internet access from inside the container will not work unless ``net.ipv4.ip_forward`` is enabled: +IPv4 packet forwarding is disabled by default, so internet access from inside +the container will not work unless ``net.ipv4.ip_forward`` is enabled: .. code-block:: bash @@ -95,4 +118,8 @@ Or, to enable it more permanently: fork/exec /usr/sbin/lxc-start: operation not permitted ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Unfortunately, Gentoo suffers from `issue #1422 `_, meaning that after every fresh start of docker, the first docker run fails due to some tricky terminal issues, so be sure to run something trivial (such as ``docker run -i -t busybox echo hi``) before attempting to run anything important. +Unfortunately, Gentoo suffers from `issue #1422 +`_, meaning that after every +fresh start of docker, the first docker run fails due to some tricky terminal +issues, so be sure to run something trivial (such as ``docker run -i -t busybox +echo hi``) before attempting to run anything important. From d368c2dee99b35830392938a783137f344e5b89a Mon Sep 17 00:00:00 2001 From: Tianon Gravi Date: Thu, 5 Sep 2013 13:36:56 -0600 Subject: [PATCH 03/10] Update Gentoo docs to reflect the package name change from app-emulation/lxc-docker to app-emulation/docker as discussed in today's #docker-meeting --- docs/sources/installation/gentoolinux.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/sources/installation/gentoolinux.rst b/docs/sources/installation/gentoolinux.rst index 5749b6dc0b..6e41e049d1 100644 --- a/docs/sources/installation/gentoolinux.rst +++ b/docs/sources/installation/gentoolinux.rst @@ -35,15 +35,15 @@ then invoke the following: sudo layman -f -a docker -Once that completes, the ``app-emulation/lxc-docker`` package will be available +Once that completes, the ``app-emulation/docker`` package will be available for emerge: .. code-block:: bash - sudo emerge -av app-emulation/lxc-docker + sudo emerge -av app-emulation/docker If you prefer to use the official binaries, or just do not wish to compile -docker, emerge ``app-emulation/lxc-docker-bin`` instead. It is important to +docker, emerge ``app-emulation/docker-bin`` instead. It is important to remember that Gentoo is still an unsupported platform, even when using the official binaries. @@ -54,7 +54,7 @@ portage tree also contains ``sys-fs/aufs3``, which contains the patches necessary for adding AUFS support to other kernel source packages (and a ``kernel-patch`` use flag to perform the patching automatically). -Between ``app-emulation/lxc`` and ``app-emulation/lxc-docker``, all the +Between ``app-emulation/lxc`` and ``app-emulation/docker``, all the necessary kernel configuration flags should be checked for and warned about in the standard manner. From 3bc73fa21e4428cd7040df6c5a384bd0e4772236 Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Thu, 5 Sep 2013 23:54:03 +0000 Subject: [PATCH 04/10] Return the process exit code for run commands --- commands.go | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/commands.go b/commands.go index b2a4d3db13..7512577a2c 100644 --- a/commands.go +++ b/commands.go @@ -367,16 +367,11 @@ func (cli *DockerCli) CmdWait(args ...string) error { return nil } for _, name := range cmd.Args() { - body, _, err := cli.call("POST", "/containers/"+name+"/wait", nil) + status, err := waitForExit(cli, name) if err != nil { fmt.Fprintf(cli.err, "%s", err) } else { - var out APIWait - err = json.Unmarshal(body, &out) - if err != nil { - return err - } - fmt.Fprintf(cli.out, "%d\n", out.StatusCode) + fmt.Fprintf(cli.out, "%d\n", status) } } return nil @@ -1477,8 +1472,16 @@ func (cli *DockerCli) CmdRun(args ...string) error { } if !config.AttachStdout && !config.AttachStderr { + // Detached mode <-wait + } else { + status, err := waitForExit(cli, runResult.ID) + if err != nil { + return err + } + os.Exit(status) } + return nil } @@ -1759,6 +1762,19 @@ func (cli *DockerCli) LoadConfigFile() (err error) { return err } +func waitForExit(cli *DockerCli, containerId string) (int, error) { + body, _, err := cli.call("POST", "/containers/"+containerId+"/wait", nil) + if err != nil { + return -1, err + } + + var out APIWait + if err := json.Unmarshal(body, &out); err != nil { + return -1, err + } + return out.StatusCode, nil +} + func NewDockerCli(in io.ReadCloser, out, err io.Writer, proto, addr string) *DockerCli { var ( isTerminal = false From 24e02043a2672320abebc3bd5fa6a592e2ebd082 Mon Sep 17 00:00:00 2001 From: Solomon Hykes Date: Fri, 6 Sep 2013 17:33:05 -0700 Subject: [PATCH 05/10] Merge builder.go into runtime.go --- api_test.go | 39 +++++------- builder.go | 154 ---------------------------------------------- buildfile.go | 10 ++- container_test.go | 70 ++++++++++----------- runtime.go | 131 +++++++++++++++++++++++++++++++++++++++ runtime_test.go | 12 ++-- server.go | 10 ++- utils_test.go | 2 +- 8 files changed, 191 insertions(+), 237 deletions(-) delete mode 100644 builder.go diff --git a/api_test.go b/api_test.go index 1e8ed20b98..837a20c244 100644 --- a/api_test.go +++ b/api_test.go @@ -321,7 +321,7 @@ func TestGetContainersJSON(t *testing.T) { srv := &Server{runtime: runtime} - container, err := NewBuilder(runtime).Create(&Config{ + container, err := runtime.Create(&Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"echo", "test"}, }) @@ -357,10 +357,8 @@ func TestGetContainersExport(t *testing.T) { srv := &Server{runtime: runtime} - builder := NewBuilder(runtime) - // Create a container and remove a file - container, err := builder.Create( + container, err := runtime.Create( &Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"touch", "/test"}, @@ -409,10 +407,8 @@ func TestGetContainersChanges(t *testing.T) { srv := &Server{runtime: runtime} - builder := NewBuilder(runtime) - // Create a container and remove a file - container, err := builder.Create( + container, err := runtime.Create( &Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"/bin/rm", "/etc/passwd"}, @@ -458,9 +454,7 @@ func TestGetContainersTop(t *testing.T) { srv := &Server{runtime: runtime} - builder := NewBuilder(runtime) - - container, err := builder.Create( + container, err := runtime.Create( &Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"/bin/sh", "-c", "cat"}, @@ -541,10 +535,8 @@ func TestGetContainersByName(t *testing.T) { srv := &Server{runtime: runtime} - builder := NewBuilder(runtime) - // Create a container and remove a file - container, err := builder.Create( + container, err := runtime.Create( &Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"echo", "test"}, @@ -574,10 +566,9 @@ func TestPostCommit(t *testing.T) { srv := &Server{runtime: runtime} - builder := NewBuilder(runtime) // Create a container and remove a file - container, err := builder.Create( + container, err := runtime.Create( &Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"touch", "/test"}, @@ -671,7 +662,7 @@ func TestPostContainersKill(t *testing.T) { srv := &Server{runtime: runtime} - container, err := NewBuilder(runtime).Create( + container, err := runtime.Create( &Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"/bin/cat"}, @@ -713,7 +704,7 @@ func TestPostContainersRestart(t *testing.T) { srv := &Server{runtime: runtime} - container, err := NewBuilder(runtime).Create( + container, err := runtime.Create( &Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"/bin/cat"}, @@ -767,7 +758,7 @@ func TestPostContainersStart(t *testing.T) { srv := &Server{runtime: runtime} - container, err := NewBuilder(runtime).Create( + container, err := runtime.Create( &Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"/bin/cat"}, @@ -817,7 +808,7 @@ func TestPostContainersStop(t *testing.T) { srv := &Server{runtime: runtime} - container, err := NewBuilder(runtime).Create( + container, err := runtime.Create( &Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"/bin/cat"}, @@ -864,7 +855,7 @@ func TestPostContainersWait(t *testing.T) { srv := &Server{runtime: runtime} - container, err := NewBuilder(runtime).Create( + container, err := runtime.Create( &Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"/bin/sleep", "1"}, @@ -906,7 +897,7 @@ func TestPostContainersAttach(t *testing.T) { srv := &Server{runtime: runtime} - container, err := NewBuilder(runtime).Create( + container, err := runtime.Create( &Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"/bin/cat"}, @@ -998,7 +989,7 @@ func TestDeleteContainers(t *testing.T) { srv := &Server{runtime: runtime} - container, err := NewBuilder(runtime).Create(&Config{ + container, err := runtime.Create(&Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"touch", "/test"}, }) @@ -1185,10 +1176,8 @@ func TestPostContainersCopy(t *testing.T) { srv := &Server{runtime: runtime} - builder := NewBuilder(runtime) - // Create a container and remove a file - container, err := builder.Create( + container, err := runtime.Create( &Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"touch", "/test.txt"}, diff --git a/builder.go b/builder.go deleted file mode 100644 index 9124f76ac1..0000000000 --- a/builder.go +++ /dev/null @@ -1,154 +0,0 @@ -package docker - -import ( - "fmt" - "github.com/dotcloud/docker/utils" - "os" - "path" - "time" -) - -var defaultDns = []string{"8.8.8.8", "8.8.4.4"} - -type Builder struct { - runtime *Runtime - repositories *TagStore - graph *Graph - - config *Config - image *Image -} - -func NewBuilder(runtime *Runtime) *Builder { - return &Builder{ - runtime: runtime, - graph: runtime.graph, - repositories: runtime.repositories, - } -} - -func (builder *Builder) Create(config *Config) (*Container, error) { - // Lookup image - img, err := builder.repositories.LookupImage(config.Image) - if err != nil { - return nil, err - } - - if img.Config != nil { - MergeConfig(config, img.Config) - } - - if len(config.Entrypoint) != 0 && config.Cmd == nil { - config.Cmd = []string{} - } else if config.Cmd == nil || len(config.Cmd) == 0 { - return nil, fmt.Errorf("No command specified") - } - - // Generate id - id := GenerateID() - // Generate default hostname - // FIXME: the lxc template no longer needs to set a default hostname - if config.Hostname == "" { - config.Hostname = id[:12] - } - - var args []string - var entrypoint string - - if len(config.Entrypoint) != 0 { - entrypoint = config.Entrypoint[0] - args = append(config.Entrypoint[1:], config.Cmd...) - } else { - entrypoint = config.Cmd[0] - args = config.Cmd[1:] - } - - container := &Container{ - // FIXME: we should generate the ID here instead of receiving it as an argument - ID: id, - Created: time.Now(), - Path: entrypoint, - Args: args, //FIXME: de-duplicate from config - Config: config, - Image: img.ID, // Always use the resolved image id - NetworkSettings: &NetworkSettings{}, - // FIXME: do we need to store this in the container? - SysInitPath: sysInitPath, - } - container.root = builder.runtime.containerRoot(container.ID) - // Step 1: create the container directory. - // This doubles as a barrier to avoid race conditions. - if err := os.Mkdir(container.root, 0700); err != nil { - return nil, err - } - - resolvConf, err := utils.GetResolvConf() - if err != nil { - return nil, err - } - - if len(config.Dns) == 0 && len(builder.runtime.Dns) == 0 && utils.CheckLocalDns(resolvConf) { - //"WARNING: Docker detected local DNS server on resolv.conf. Using default external servers: %v", defaultDns - builder.runtime.Dns = defaultDns - } - - // If custom dns exists, then create a resolv.conf for the container - if len(config.Dns) > 0 || len(builder.runtime.Dns) > 0 { - var dns []string - if len(config.Dns) > 0 { - dns = config.Dns - } else { - dns = builder.runtime.Dns - } - container.ResolvConfPath = path.Join(container.root, "resolv.conf") - f, err := os.Create(container.ResolvConfPath) - if err != nil { - return nil, err - } - defer f.Close() - for _, dns := range dns { - if _, err := f.Write([]byte("nameserver " + dns + "\n")); err != nil { - return nil, err - } - } - } else { - container.ResolvConfPath = "/etc/resolv.conf" - } - - // Step 2: save the container json - if err := container.ToDisk(); err != nil { - return nil, err - } - // Step 3: register the container - if err := builder.runtime.Register(container); err != nil { - return nil, err - } - return container, nil -} - -// Commit creates a new filesystem image from the current state of a container. -// The image can optionally be tagged into a repository -func (builder *Builder) Commit(container *Container, repository, tag, comment, author string, config *Config) (*Image, error) { - // FIXME: freeze the container before copying it to avoid data corruption? - // FIXME: this shouldn't be in commands. - if err := container.EnsureMounted(); err != nil { - return nil, err - } - - rwTar, err := container.ExportRw() - if err != nil { - return nil, err - } - // Create a new image from the container's base layers + a new layer from container changes - img, err := builder.graph.Create(rwTar, container, comment, author, config) - if err != nil { - return nil, err - } - // Register the image if needed - if repository != "" { - if err := builder.repositories.Set(repository, tag, img.ID, true); err != nil { - return img, err - } - } - return img, nil -} diff --git a/buildfile.go b/buildfile.go index 4c8db2c60e..c3c212c693 100644 --- a/buildfile.go +++ b/buildfile.go @@ -23,7 +23,6 @@ type BuildFile interface { type buildFile struct { runtime *Runtime - builder *Builder srv *Server image string @@ -337,7 +336,7 @@ func (b *buildFile) CmdAdd(args string) error { b.config.Image = b.image // Create the container and start it - container, err := b.builder.Create(b.config) + container, err := b.runtime.Create(b.config) if err != nil { return err } @@ -372,7 +371,7 @@ func (b *buildFile) run() (string, error) { b.config.Image = b.image // Create the container and start it - c, err := b.builder.Create(b.config) + c, err := b.runtime.Create(b.config) if err != nil { return "", err } @@ -428,7 +427,7 @@ func (b *buildFile) commit(id string, autoCmd []string, comment string) error { } } - container, err := b.builder.Create(b.config) + container, err := b.runtime.Create(b.config) if err != nil { return err } @@ -450,7 +449,7 @@ func (b *buildFile) commit(id string, autoCmd []string, comment string) error { autoConfig := *b.config autoConfig.Cmd = autoCmd // Commit the container - image, err := b.builder.Commit(container, "", "", "", b.maintainer, &autoConfig) + image, err := b.runtime.Commit(container, "", "", "", b.maintainer, &autoConfig) if err != nil { return err } @@ -524,7 +523,6 @@ func (b *buildFile) Build(context io.Reader) (string, error) { func NewBuildFile(srv *Server, out io.Writer, verbose, utilizeCache bool) BuildFile { return &buildFile{ - builder: NewBuilder(srv.runtime), runtime: srv.runtime, srv: srv, config: &Config{}, diff --git a/container_test.go b/container_test.go index b06d531cf7..b792af76d5 100644 --- a/container_test.go +++ b/container_test.go @@ -18,7 +18,7 @@ import ( func TestIDFormat(t *testing.T) { runtime := mkRuntime(t) defer nuke(runtime) - container1, err := NewBuilder(runtime).Create( + container1, err := runtime.Create( &Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"/bin/sh", "-c", "echo hello world"}, @@ -388,7 +388,7 @@ func TestRun(t *testing.T) { func TestOutput(t *testing.T) { runtime := mkRuntime(t) defer nuke(runtime) - container, err := NewBuilder(runtime).Create( + container, err := runtime.Create( &Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"echo", "-n", "foobar"}, @@ -411,7 +411,7 @@ func TestKillDifferentUser(t *testing.T) { runtime := mkRuntime(t) defer nuke(runtime) - container, err := NewBuilder(runtime).Create(&Config{ + container, err := runtime.Create(&Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"cat"}, OpenStdin: true, @@ -471,7 +471,7 @@ func TestCreateVolume(t *testing.T) { if err != nil { t.Fatal(err) } - c, err := NewBuilder(runtime).Create(config) + c, err := runtime.Create(config) if err != nil { t.Fatal(err) } @@ -486,7 +486,7 @@ func TestCreateVolume(t *testing.T) { func TestKill(t *testing.T) { runtime := mkRuntime(t) defer nuke(runtime) - container, err := NewBuilder(runtime).Create(&Config{ + container, err := runtime.Create(&Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"sleep", "2"}, }, @@ -530,9 +530,7 @@ func TestExitCode(t *testing.T) { runtime := mkRuntime(t) defer nuke(runtime) - builder := NewBuilder(runtime) - - trueContainer, err := builder.Create(&Config{ + trueContainer, err := runtime.Create(&Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"/bin/true", ""}, }) @@ -547,7 +545,7 @@ func TestExitCode(t *testing.T) { t.Errorf("Unexpected exit code %d (expected 0)", trueContainer.State.ExitCode) } - falseContainer, err := builder.Create(&Config{ + falseContainer, err := runtime.Create(&Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"/bin/false", ""}, }) @@ -566,7 +564,7 @@ func TestExitCode(t *testing.T) { func TestRestart(t *testing.T) { runtime := mkRuntime(t) defer nuke(runtime) - container, err := NewBuilder(runtime).Create(&Config{ + container, err := runtime.Create(&Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"echo", "-n", "foobar"}, }, @@ -596,7 +594,7 @@ func TestRestart(t *testing.T) { func TestRestartStdin(t *testing.T) { runtime := mkRuntime(t) defer nuke(runtime) - container, err := NewBuilder(runtime).Create(&Config{ + container, err := runtime.Create(&Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"cat"}, @@ -673,10 +671,8 @@ func TestUser(t *testing.T) { runtime := mkRuntime(t) defer nuke(runtime) - builder := NewBuilder(runtime) - // Default user must be root - container, err := builder.Create(&Config{ + container, err := runtime.Create(&Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"id"}, }, @@ -694,7 +690,7 @@ func TestUser(t *testing.T) { } // Set a username - container, err = builder.Create(&Config{ + container, err = runtime.Create(&Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"id"}, @@ -714,7 +710,7 @@ func TestUser(t *testing.T) { } // Set a UID - container, err = builder.Create(&Config{ + container, err = runtime.Create(&Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"id"}, @@ -734,7 +730,7 @@ func TestUser(t *testing.T) { } // Set a different user by uid - container, err = builder.Create(&Config{ + container, err = runtime.Create(&Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"id"}, @@ -756,7 +752,7 @@ func TestUser(t *testing.T) { } // Set a different user by username - container, err = builder.Create(&Config{ + container, err = runtime.Create(&Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"id"}, @@ -776,7 +772,7 @@ func TestUser(t *testing.T) { } // Test an wrong username - container, err = builder.Create(&Config{ + container, err = runtime.Create(&Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"id"}, @@ -797,9 +793,7 @@ func TestMultipleContainers(t *testing.T) { runtime := mkRuntime(t) defer nuke(runtime) - builder := NewBuilder(runtime) - - container1, err := builder.Create(&Config{ + container1, err := runtime.Create(&Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"sleep", "2"}, }, @@ -809,7 +803,7 @@ func TestMultipleContainers(t *testing.T) { } defer runtime.Destroy(container1) - container2, err := builder.Create(&Config{ + container2, err := runtime.Create(&Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"sleep", "2"}, }, @@ -853,7 +847,7 @@ func TestMultipleContainers(t *testing.T) { func TestStdin(t *testing.T) { runtime := mkRuntime(t) defer nuke(runtime) - container, err := NewBuilder(runtime).Create(&Config{ + container, err := runtime.Create(&Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"cat"}, @@ -898,7 +892,7 @@ func TestStdin(t *testing.T) { func TestTty(t *testing.T) { runtime := mkRuntime(t) defer nuke(runtime) - container, err := NewBuilder(runtime).Create(&Config{ + container, err := runtime.Create(&Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"cat"}, @@ -943,7 +937,7 @@ func TestTty(t *testing.T) { func TestEnv(t *testing.T) { runtime := mkRuntime(t) defer nuke(runtime) - container, err := NewBuilder(runtime).Create(&Config{ + container, err := runtime.Create(&Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"env"}, }, @@ -992,7 +986,7 @@ func TestEnv(t *testing.T) { func TestEntrypoint(t *testing.T) { runtime := mkRuntime(t) defer nuke(runtime) - container, err := NewBuilder(runtime).Create( + container, err := runtime.Create( &Config{ Image: GetTestImage(runtime).ID, Entrypoint: []string{"/bin/echo"}, @@ -1015,7 +1009,7 @@ func TestEntrypoint(t *testing.T) { func TestEntrypointNoCmd(t *testing.T) { runtime := mkRuntime(t) defer nuke(runtime) - container, err := NewBuilder(runtime).Create( + container, err := runtime.Create( &Config{ Image: GetTestImage(runtime).ID, Entrypoint: []string{"/bin/echo", "foobar"}, @@ -1066,7 +1060,7 @@ func TestLXCConfig(t *testing.T) { cpuMin := 100 cpuMax := 10000 cpu := cpuMin + rand.Intn(cpuMax-cpuMin) - container, err := NewBuilder(runtime).Create(&Config{ + container, err := runtime.Create(&Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"/bin/true"}, @@ -1090,7 +1084,7 @@ func TestLXCConfig(t *testing.T) { func TestCustomLxcConfig(t *testing.T) { runtime := mkRuntime(t) defer nuke(runtime) - container, err := NewBuilder(runtime).Create(&Config{ + container, err := runtime.Create(&Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"/bin/true"}, @@ -1121,7 +1115,7 @@ func BenchmarkRunSequencial(b *testing.B) { runtime := mkRuntime(b) defer nuke(runtime) for i := 0; i < b.N; i++ { - container, err := NewBuilder(runtime).Create(&Config{ + container, err := runtime.Create(&Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"echo", "-n", "foo"}, }, @@ -1153,7 +1147,7 @@ func BenchmarkRunParallel(b *testing.B) { complete := make(chan error) tasks = append(tasks, complete) go func(i int, complete chan error) { - container, err := NewBuilder(runtime).Create(&Config{ + container, err := runtime.Create(&Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"echo", "-n", "foo"}, }, @@ -1229,7 +1223,7 @@ func TestBindMounts(t *testing.T) { func TestVolumesFromReadonlyMount(t *testing.T) { runtime := mkRuntime(t) defer nuke(runtime) - container, err := NewBuilder(runtime).Create( + container, err := runtime.Create( &Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"/bin/echo", "-n", "foobar"}, @@ -1248,7 +1242,7 @@ func TestVolumesFromReadonlyMount(t *testing.T) { t.Fail() } - container2, err := NewBuilder(runtime).Create( + container2, err := runtime.Create( &Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"/bin/echo", "-n", "foobar"}, @@ -1284,7 +1278,7 @@ func TestRestartWithVolumes(t *testing.T) { runtime := mkRuntime(t) defer nuke(runtime) - container, err := NewBuilder(runtime).Create(&Config{ + container, err := runtime.Create(&Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"echo", "-n", "foobar"}, Volumes: map[string]struct{}{"/test": {}}, @@ -1327,7 +1321,7 @@ func TestVolumesFromWithVolumes(t *testing.T) { runtime := mkRuntime(t) defer nuke(runtime) - container, err := NewBuilder(runtime).Create(&Config{ + container, err := runtime.Create(&Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"sh", "-c", "echo -n bar > /test/foo"}, Volumes: map[string]struct{}{"/test": {}}, @@ -1354,7 +1348,7 @@ func TestVolumesFromWithVolumes(t *testing.T) { t.Fail() } - container2, err := NewBuilder(runtime).Create( + container2, err := runtime.Create( &Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"cat", "/test/foo"}, @@ -1395,7 +1389,7 @@ func TestOnlyLoopbackExistsWhenUsingDisableNetworkOption(t *testing.T) { if err != nil { t.Fatal(err) } - c, err := NewBuilder(runtime).Create(config) + c, err := runtime.Create(config) if err != nil { t.Fatal(err) } diff --git a/runtime.go b/runtime.go index 002c0fe10a..92141be1c9 100644 --- a/runtime.go +++ b/runtime.go @@ -14,6 +14,8 @@ import ( "strings" ) +var defaultDns = []string{"8.8.8.8", "8.8.4.4"} + type Capabilities struct { MemoryLimit bool SwapLimit bool @@ -233,6 +235,7 @@ func (runtime *Runtime) restore() error { return nil } +// FIXME: comment please func (runtime *Runtime) UpdateCapabilities(quiet bool) { if cgroupMemoryMountpoint, err := utils.FindCgroupMountpoint("memory"); err != nil { if !quiet { @@ -260,6 +263,133 @@ func (runtime *Runtime) UpdateCapabilities(quiet bool) { } } +// Create creates a new container from the given configuration. +func (runtime *Runtime) Create(config *Config) (*Container, error) { + // Lookup image + img, err := runtime.repositories.LookupImage(config.Image) + if err != nil { + return nil, err + } + + if img.Config != nil { + MergeConfig(config, img.Config) + } + + if len(config.Entrypoint) != 0 && config.Cmd == nil { + config.Cmd = []string{} + } else if config.Cmd == nil || len(config.Cmd) == 0 { + return nil, fmt.Errorf("No command specified") + } + + // Generate id + id := GenerateID() + // Generate default hostname + // FIXME: the lxc template no longer needs to set a default hostname + if config.Hostname == "" { + config.Hostname = id[:12] + } + + var args []string + var entrypoint string + + if len(config.Entrypoint) != 0 { + entrypoint = config.Entrypoint[0] + args = append(config.Entrypoint[1:], config.Cmd...) + } else { + entrypoint = config.Cmd[0] + args = config.Cmd[1:] + } + + container := &Container{ + // FIXME: we should generate the ID here instead of receiving it as an argument + ID: id, + Created: time.Now(), + Path: entrypoint, + Args: args, //FIXME: de-duplicate from config + Config: config, + Image: img.ID, // Always use the resolved image id + NetworkSettings: &NetworkSettings{}, + // FIXME: do we need to store this in the container? + SysInitPath: sysInitPath, + } + container.root = runtime.containerRoot(container.ID) + // Step 1: create the container directory. + // This doubles as a barrier to avoid race conditions. + if err := os.Mkdir(container.root, 0700); err != nil { + return nil, err + } + + resolvConf, err := utils.GetResolvConf() + if err != nil { + return nil, err + } + + if len(config.Dns) == 0 && len(runtime.Dns) == 0 && utils.CheckLocalDns(resolvConf) { + //"WARNING: Docker detected local DNS server on resolv.conf. Using default external servers: %v", defaultDns + runtime.Dns = defaultDns + } + + // If custom dns exists, then create a resolv.conf for the container + if len(config.Dns) > 0 || len(runtime.Dns) > 0 { + var dns []string + if len(config.Dns) > 0 { + dns = config.Dns + } else { + dns = runtime.Dns + } + container.ResolvConfPath = path.Join(container.root, "resolv.conf") + f, err := os.Create(container.ResolvConfPath) + if err != nil { + return nil, err + } + defer f.Close() + for _, dns := range dns { + if _, err := f.Write([]byte("nameserver " + dns + "\n")); err != nil { + return nil, err + } + } + } else { + container.ResolvConfPath = "/etc/resolv.conf" + } + + // Step 2: save the container json + if err := container.ToDisk(); err != nil { + return nil, err + } + // Step 3: register the container + if err := runtime.Register(container); err != nil { + return nil, err + } + return container, nil +} + +// Commit creates a new filesystem image from the current state of a container. +// The image can optionally be tagged into a repository +func (runtime *Runtime) Commit(container *Container, repository, tag, comment, author string, config *Config) (*Image, error) { + // FIXME: freeze the container before copying it to avoid data corruption? + // FIXME: this shouldn't be in commands. + if err := container.EnsureMounted(); err != nil { + return nil, err + } + + rwTar, err := container.ExportRw() + if err != nil { + return nil, err + } + // Create a new image from the container's base layers + a new layer from container changes + img, err := runtime.graph.Create(rwTar, container, comment, author, config) + if err != nil { + return nil, err + } + // Register the image if needed + if repository != "" { + if err := runtime.repositories.Set(repository, tag, img.ID, true); err != nil { + return img, err + } + } + return img, nil +} + // FIXME: harmonize with NewGraph() func NewRuntime(flGraphPath string, autoRestart bool, dns []string) (*Runtime, error) { runtime, err := NewRuntimeFromDirectory(flGraphPath, autoRestart) @@ -347,3 +477,4 @@ func (history *History) Add(container *Container) { *history = append(*history, container) sort.Sort(history) } + diff --git a/runtime_test.go b/runtime_test.go index a65d962fa6..188e9c9076 100644 --- a/runtime_test.go +++ b/runtime_test.go @@ -144,9 +144,7 @@ func TestRuntimeCreate(t *testing.T) { t.Errorf("Expected 0 containers, %v found", len(runtime.List())) } - builder := NewBuilder(runtime) - - container, err := builder.Create(&Config{ + container, err := runtime.Create(&Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"ls", "-al"}, }, @@ -187,7 +185,7 @@ func TestRuntimeCreate(t *testing.T) { } // Make sure crete with bad parameters returns an error - _, err = builder.Create( + _, err = runtime.Create( &Config{ Image: GetTestImage(runtime).ID, }, @@ -196,7 +194,7 @@ func TestRuntimeCreate(t *testing.T) { t.Fatal("Builder.Create should throw an error when Cmd is missing") } - _, err = builder.Create( + _, err = runtime.Create( &Config{ Image: GetTestImage(runtime).ID, Cmd: []string{}, @@ -210,7 +208,7 @@ func TestRuntimeCreate(t *testing.T) { func TestDestroy(t *testing.T) { runtime := mkRuntime(t) defer nuke(runtime) - container, err := NewBuilder(runtime).Create(&Config{ + container, err := runtime.Create(&Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"ls", "-al"}, }, @@ -296,7 +294,7 @@ func startEchoServerContainer(t *testing.T, proto string) (*Runtime, *Container, t.Fatal(fmt.Errorf("Unknown protocol %v", proto)) } t.Log("Trying port", strPort) - container, err = NewBuilder(runtime).Create(&Config{ + container, err = runtime.Create(&Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"sh", "-c", cmd}, PortSpecs: []string{fmt.Sprintf("%s/%s", strPort, proto)}, diff --git a/server.go b/server.go index 69edabf07a..8350ce597f 100644 --- a/server.go +++ b/server.go @@ -139,8 +139,7 @@ func (srv *Server) ImageInsert(name, url, path string, out io.Writer, sf *utils. return "", err } - b := NewBuilder(srv.runtime) - c, err := b.Create(config) + c, err := srv.runtime.Create(config) if err != nil { return "", err } @@ -149,7 +148,7 @@ func (srv *Server) ImageInsert(name, url, path string, out io.Writer, sf *utils. return "", err } // FIXME: Handle custom repo, tag comment, author - img, err = b.Commit(c, "", "", img.Comment, img.Author, nil) + img, err = srv.runtime.Commit(c, "", "", img.Comment, img.Author, nil) if err != nil { return "", err } @@ -400,7 +399,7 @@ func (srv *Server) ContainerCommit(name, repo, tag, author, comment string, conf if container == nil { return "", fmt.Errorf("No such container: %s", name) } - img, err := NewBuilder(srv.runtime).Commit(container, repo, tag, comment, author, config) + img, err := srv.runtime.Commit(container, repo, tag, comment, author, config) if err != nil { return "", err } @@ -904,8 +903,7 @@ func (srv *Server) ContainerCreate(config *Config) (string, error) { if config.Memory > 0 && !srv.runtime.capabilities.SwapLimit { config.MemorySwap = -1 } - b := NewBuilder(srv.runtime) - container, err := b.Create(config) + container, err := srv.runtime.Create(config) if err != nil { if srv.runtime.graph.IsNotExist(err) { diff --git a/utils_test.go b/utils_test.go index e8aae17186..740a5fc1bc 100644 --- a/utils_test.go +++ b/utils_test.go @@ -101,7 +101,7 @@ func mkContainer(r *Runtime, args []string, t *testing.T) (*Container, *HostConf if config.Image == "_" { config.Image = GetTestImage(r).ID } - c, err := NewBuilder(r).Create(config) + c, err := r.Create(config) if err != nil { return nil, nil, err } From 6a9f4ecf9bc4263baef8e9a1d86f1474f9c66d31 Mon Sep 17 00:00:00 2001 From: Solomon Hykes Date: Fri, 6 Sep 2013 17:43:34 -0700 Subject: [PATCH 06/10] Add missing comments to runtime.go --- runtime.go | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/runtime.go b/runtime.go index 92141be1c9..ab514c40d7 100644 --- a/runtime.go +++ b/runtime.go @@ -44,6 +44,7 @@ func init() { sysInitPath = utils.SelfPath() } +// List returns an array of all containers registered in the runtime. func (runtime *Runtime) List() []*Container { containers := new(History) for e := runtime.containers.Front(); e != nil; e = e.Next() { @@ -62,6 +63,8 @@ func (runtime *Runtime) getContainerElement(id string) *list.Element { return nil } +// Get looks for a container by the specified ID or name, and returns it. +// If the container is not found, or if an error occurs, nil is returned. func (runtime *Runtime) Get(name string) *Container { id, err := runtime.idIndex.Get(name) if err != nil { @@ -74,6 +77,8 @@ func (runtime *Runtime) Get(name string) *Container { return e.Value.(*Container) } +// Exists returns a true if a container of the specified ID or name exists, +// false otherwise. func (runtime *Runtime) Exists(id string) bool { return runtime.Get(id) != nil } @@ -82,6 +87,9 @@ func (runtime *Runtime) containerRoot(id string) string { return path.Join(runtime.repository, id) } +// Load reads the contents of a container from disk and registers +// it with Register. +// This is typically done at startup. func (runtime *Runtime) Load(id string) (*Container, error) { container := &Container{root: runtime.containerRoot(id)} if err := container.FromDisk(); err != nil { @@ -179,6 +187,7 @@ func (runtime *Runtime) LogToDisk(src *utils.WriteBroadcaster, dst, stream strin return nil } +// Destroy unregisters a container from the runtime and cleanly removes its contents from the filesystem. func (runtime *Runtime) Destroy(container *Container) error { if container == nil { return fmt.Errorf("The given container is ") @@ -235,7 +244,7 @@ func (runtime *Runtime) restore() error { return nil } -// FIXME: comment please +// FIXME: comment please! func (runtime *Runtime) UpdateCapabilities(quiet bool) { if cgroupMemoryMountpoint, err := utils.FindCgroupMountpoint("memory"); err != nil { if !quiet { @@ -455,6 +464,8 @@ func NewRuntimeFromDirectory(root string, autoRestart bool) (*Runtime, error) { return runtime, nil } +// History is a convenience type for storing a list of containers, +// ordered by creation date. type History []*Container func (history *History) Len() int { From eca861a99d1a5abe91704b55d044eb8280adb8d1 Mon Sep 17 00:00:00 2001 From: Solomon Hykes Date: Sat, 7 Sep 2013 17:03:40 -0700 Subject: [PATCH 07/10] Add missing package import --- runtime.go | 1 + 1 file changed, 1 insertion(+) diff --git a/runtime.go b/runtime.go index ab514c40d7..1ee7aa26f9 100644 --- a/runtime.go +++ b/runtime.go @@ -12,6 +12,7 @@ import ( "path" "sort" "strings" + "time" ) var defaultDns = []string{"8.8.8.8", "8.8.4.4"} From 268928ab35a5402ff37413ab258200c4bed48790 Mon Sep 17 00:00:00 2001 From: Marko Mikulicic Date: Mon, 9 Sep 2013 11:01:15 +0100 Subject: [PATCH 08/10] Please add go-dockerclient to docker API clients doc --- docs/sources/api/docker_remote_api.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/sources/api/docker_remote_api.rst b/docs/sources/api/docker_remote_api.rst index be15c3494e..5d8657c469 100644 --- a/docs/sources/api/docker_remote_api.rst +++ b/docs/sources/api/docker_remote_api.rst @@ -193,3 +193,5 @@ and we will add the libraries here. +----------------------+----------------+--------------------------------------------+ | Erlang | erldocker | https://github.com/proger/erldocker | +----------------------+----------------+--------------------------------------------+ +| Go | go-dockerclient| https://github.com/fsouza/go-dockerclient | ++----------------------+----------------+--------------------------------------------+ From 46a1cd69a99a9adc7bc366f1eb2c03b62f464d39 Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Mon, 9 Sep 2013 21:26:35 +0000 Subject: [PATCH 09/10] only os.Exits on error --- commands.go | 4 +++- docker/docker.go | 3 +++ utils/utils.go | 9 +++++++++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/commands.go b/commands.go index b5eed0888f..df6feac93b 100644 --- a/commands.go +++ b/commands.go @@ -1479,7 +1479,9 @@ func (cli *DockerCli) CmdRun(args ...string) error { if err != nil { return err } - os.Exit(status) + if status != 0 { + return &utils.StatusError{status} + } } return nil diff --git a/docker/docker.go b/docker/docker.go index 9bbd40b696..a0021f3a87 100644 --- a/docker/docker.go +++ b/docker/docker.go @@ -75,6 +75,9 @@ func main() { } protoAddrParts := strings.SplitN(flHosts[0], "://", 2) if err := docker.ParseCommands(protoAddrParts[0], protoAddrParts[1], flag.Args()...); err != nil { + if sterr, ok := err.(*utils.StatusError); ok { + os.Exit(sterr.Status) + } log.Fatal(err) os.Exit(-1) } diff --git a/utils/utils.go b/utils/utils.go index aa34abdddc..d417690c0c 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -1012,3 +1012,12 @@ func (graph *DependencyGraph) GenerateTraversalMap() ([][]string, error) { } return result, nil } + +// An StatusError reports an unsuccessful exit by a command. +type StatusError struct { + Status int +} + +func (e *StatusError) Error() string { + return fmt.Sprintf("Status: %d", e.Status) +} From 3c90e96b6d9e16b3983cd7e387b06e81130f1e7c Mon Sep 17 00:00:00 2001 From: Sridhar Ratnakumar Date: Mon, 9 Sep 2013 14:42:45 -0700 Subject: [PATCH 10/10] remove docker-ruby from docs we don't maintain it anymore as we now recommend proper HTTP api based clients instead. --- docs/sources/api/docker_remote_api.rst | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/sources/api/docker_remote_api.rst b/docs/sources/api/docker_remote_api.rst index be15c3494e..f76d50a62f 100644 --- a/docs/sources/api/docker_remote_api.rst +++ b/docs/sources/api/docker_remote_api.rst @@ -175,8 +175,6 @@ and we will add the libraries here. +======================+================+============================================+ | Python | docker-py | https://github.com/dotcloud/docker-py | +----------------------+----------------+--------------------------------------------+ -| Ruby | docker-ruby | https://github.com/ActiveState/docker-ruby | -+----------------------+----------------+--------------------------------------------+ | Ruby | docker-client | https://github.com/geku/docker-client | +----------------------+----------------+--------------------------------------------+ | Ruby | docker-api | https://github.com/swipely/docker-api |