diff --git a/.gitignore b/.gitignore index ea62e34d19..5843eaf9cc 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,6 @@ docs/_templates .gopath/ .dotcloud *.test +bundles/ +.hg/ +.git/ diff --git a/AUTHORS b/AUTHORS index 271cc5f65b..af0eb3bdd8 100644 --- a/AUTHORS +++ b/AUTHORS @@ -17,6 +17,7 @@ Antony Messerli Barry Allard Brandon Liu Brian McCallister +Brian Olsen Bruno Bigras Caleb Spare Calen Pennington @@ -34,6 +35,7 @@ Dominik Honnef Don Spaulding Dr Nic Williams Elias Probst +Emily Rose Eric Hanchrow Eric Myhre Erno Hopearuoho @@ -73,6 +75,7 @@ Louis Opter Marco Hennings Marcus Farkas Mark McGranaghan +Martin Redmond Maxim Treskin meejah Michael Crosby @@ -103,6 +106,7 @@ Solomon Hykes Sridhar Ratnakumar Stefan Praszalowicz Thatcher Peskens +Thijs Terlouw Thomas Bikeev Thomas Hansen Tianon Gravi diff --git a/CHANGELOG.md b/CHANGELOG.md index 968c6696f2..acd46e87f9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,24 @@ # Changelog +## 0.6.2 (2013-09-17) ++ Hack: Vendor all dependencies ++ Builder: Add -rm option in order to remove intermediate containers ++ Runtime: Add domainname support ++ Runtime: Implement image filtering with path.Match +* Builder: Allow multiline for the RUN instruction +* Runtime: Remove unnecesasry warnings +* Runtime: Only mount the hostname file when the config exists +* Runtime: Handle signals within the `docker login` command +* Runtime: Remove os/user dependency +* Registry: Implement login with private registry +* Remote API: Bump to v1.5 +* Packaging: Break down hack/make.sh into small scripts, one per 'bundle': test, binary, ubuntu etc. +* Documentation: General improvments +- Runtime: UID and GID are now also applied to volumes +- Runtime: `docker start` set error code upon error +- Runtime: `docker run` set the same error code as the process started +- Registry: Fix push issues + ## 0.6.1 (2013-08-23) * Registry: Pass "meta" headers in API calls to the registry - Packaging: Use correct upstart script with new build tool diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7d90e28ca8..d5438c3eae 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -23,7 +23,7 @@ that feature *on top of* docker. ### Discuss your design on the mailing list We recommend discussing your plans [on the mailing -list](https://groups.google.com/forum/?fromgroups#!forum/docker-club) +list](https://groups.google.com/forum/?fromgroups#!forum/docker-dev) before starting to code - especially for more ambitious contributions. This gives other contributors a chance to point you in the right direction, give feedback on your design, and maybe point out if someone diff --git a/Dockerfile b/Dockerfile index 8694f07c37..36054d29b3 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,29 @@ # This file describes the standard way to build Docker, using docker -docker-version 0.4.2 +# +# Usage: +# +# # Assemble the full dev environment. This is slow the first time. +# docker build -t docker . +# # Apparmor messes with privileged mode: disable it +# /etc/init.d/apparmor stop ; /etc/init.d/apparmor teardown +# +# # Mount your source in an interactive container for quick testing: +# docker run -v `pwd`:/go/src/github.com/dotcloud/docker -privileged -lxc-conf=lxc.aa_profile=unconfined -i -t docker bash +# +# +# # Run the test suite: +# docker run -privileged -lxc-conf=lxc.aa_profile=unconfined docker go test -v +# +# # Publish a release: +# docker run -privileged -lxc-conf=lxc.aa_profile=unconfined \ +# -e AWS_S3_BUCKET=baz \ +# -e AWS_ACCESS_KEY=foo \ +# -e AWS_SECRET_KEY=bar \ +# -e GPG_PASSPHRASE=gloubiboulga \ +# -lxc-conf=lxc.aa_profile=unconfined -privileged docker hack/release.sh +# + +docker-version 0.6.1 from ubuntu:12.04 maintainer Solomon Hykes # Build dependencies @@ -11,7 +35,7 @@ run apt-get install -y -q mercurial # Install Go run curl -s https://go.googlecode.com/files/go1.1.2.linux-amd64.tar.gz | tar -v -C /usr/local -xz env PATH /usr/local/go/bin:/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin -env GOPATH /go +env GOPATH /go:/go/src/github.com/dotcloud/docker/vendor env CGO_ENABLED 0 run cd /tmp && echo 'package main' > t.go && go test -a -i -v # Ubuntu stuff @@ -23,15 +47,12 @@ run apt-get install -y -q python-pip run pip install s3cmd run pip install python-magic run /bin/echo -e '[default]\naccess_key=$AWS_ACCESS_KEY\nsecret_key=$AWS_SECRET_KEY\n' > /.s3cfg -# Download dependencies -run PKG=github.com/kr/pty REV=27435c699; git clone http://$PKG /go/src/$PKG && cd /go/src/$PKG && git checkout -f $REV -run PKG=github.com/gorilla/context/ REV=708054d61e5; git clone http://$PKG /go/src/$PKG && cd /go/src/$PKG && git checkout -f $REV -run PKG=github.com/gorilla/mux/ REV=9b36453141c; git clone http://$PKG /go/src/$PKG && cd /go/src/$PKG && git checkout -f $REV -run PKG=github.com/dotcloud/tar/ REV=d06045a6d9; git clone http://$PKG /go/src/$PKG && cd /go/src/$PKG && git checkout -f $REV -run PKG=code.google.com/p/go.net/ REV=84a4013f96e0; hg clone http://$PKG /go/src/$PKG && cd /go/src/$PKG && hg checkout $REV +# Runtime dependencies +run apt-get install -y -q iptables +run apt-get install -y -q lxc +volume /var/lib/docker +workdir /go/src/github.com/dotcloud/docker +# Wrap all commands in the "docker-in-docker" script to allow nested containers +entrypoint ["hack/dind"] # Upload docker source add . /go/src/github.com/dotcloud/docker -run ln -s /go/src/github.com/dotcloud/docker /src -# Build the binary -run cd /go/src/github.com/dotcloud/docker && hack/release/make.sh -cmd cd /go/src/github.com/dotcloud/docker && hack/release/release.sh diff --git a/FIXME b/FIXME index 97a0e0ebb1..6e14a08ee9 100644 --- a/FIXME +++ b/FIXME @@ -34,3 +34,4 @@ to put them - so we put them here :) * entry point config * bring back git revision info, looks like it was lost * Clean up the ProgressReader api, it's a PITA to use +* Use netlink instead of iproute2/iptables (#925) diff --git a/README.md b/README.md index 89767a9cce..a36bfba73f 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ Docker: the Linux container engine ================================== -Docker is an open-source engine which automates the deployment of -applications as highly portable, self-sufficient containers. +Docker is an open source project to pack, ship and run any application +as a lightweight container Docker containers are both *hardware-agnostic* and *platform-agnostic*. This means that they can run anywhere, from your @@ -18,7 +18,7 @@ Platform-as-a-Service. It benefits directly from the experience accumulated over several years of large-scale operation and support of hundreds of thousands of applications and databases. -![Docker L](docs/sources/concepts/images/dockerlogo-h.png "Docker") +![Docker L](docs/sources/static_files/dockerlogo-h.png "Docker") ## Better than VMs @@ -140,148 +140,25 @@ Note that Docker doesn't care *how* dependencies are built - as long as they can be built by running a Unix command in a container. -Install instructions -================== +Getting started +=============== -Quick install on Ubuntu 12.04 and 12.10 ---------------------------------------- +Docker can be installed on your local machine as well as servers - both bare metal and virtualized. +It is available as a binary on most modern Linux systems, or as a VM on Windows, Mac and other systems. -```bash -curl https://get.docker.io | sudo sh -x -``` +We also offer an interactive tutorial for quickly learning the basics of using Docker. -Binary installs ----------------- -Docker supports the following binary installation methods. Note that -some methods are community contributions and not yet officially -supported. +For up-to-date install instructions and online tutorials, see the [Getting Started page](http://www.docker.io/gettingstarted/). -* [Ubuntu 12.04 and 12.10 (officially supported)](http://docs.docker.io/en/latest/installation/ubuntulinux/) -* [Arch Linux](http://docs.docker.io/en/latest/installation/archlinux/) -* [Mac OS X (with Vagrant)](http://docs.docker.io/en/latest/installation/vagrant/) -* [Windows (with Vagrant)](http://docs.docker.io/en/latest/installation/windows/) -* [Amazon EC2 (with Vagrant)](http://docs.docker.io/en/latest/installation/amazon/) - -Installing from source ----------------------- - -1. Install Dependencies - * [Go language 1.1.x](http://golang.org/doc/install) - * [git](http://git-scm.com) - * [lxc](http://lxc.sourceforge.net) - * [aufs-tools](http://aufs.sourceforge.net) - -2. Checkout the source code - - ```bash - git clone http://github.com/dotcloud/docker - ``` - -3. Build the ``docker`` binary - - ```bash - cd docker - make VERBOSE=1 - sudo cp ./bin/docker /usr/local/bin/docker - ``` Usage examples ============== -First run the ``docker`` daemon -------------------------------- +Docker can be used to run short-lived commands, long-running daemons (app servers, databases etc.), +interactive shell sessions, etc. -All the examples assume your machine is running the ``docker`` -daemon. To run the ``docker`` daemon in the background, simply type: - -```bash -# On a production system you want this running in an init script -sudo docker -d & -``` - -Now you can run ``docker`` in client mode: all commands will be -forwarded to the ``docker`` daemon, so the client can run from any -account. - -```bash -# Now you can run docker commands from any account. -docker help -``` - - -Throwaway shell in a base Ubuntu image --------------------------------------- - -```bash -docker pull ubuntu:12.10 - -# Run an interactive shell, allocate a tty, attach stdin and stdout -# To detach the tty without exiting the shell, use the escape sequence Ctrl-p + Ctrl-q -docker run -i -t ubuntu:12.10 /bin/bash -``` - -Starting a long-running worker process --------------------------------------- - -```bash -# Start a very useful long-running process -JOB=$(docker run -d ubuntu /bin/sh -c "while true; do echo Hello world; sleep 1; done") - -# Collect the output of the job so far -docker logs $JOB - -# Kill the job -docker kill $JOB -``` - -Running an irc bouncer ----------------------- - -```bash -BOUNCER_ID=$(docker run -d -p 6667 -u irc shykes/znc zncrun $USER $PASSWORD) -echo "Configure your irc client to connect to port $(docker port $BOUNCER_ID 6667) of this machine" -``` - -Running Redis -------------- - -```bash -REDIS_ID=$(docker run -d -p 6379 shykes/redis redis-server) -echo "Configure your redis client to connect to port $(docker port $REDIS_ID 6379) of this machine" -``` - -Share your own image! ---------------------- - -```bash -CONTAINER=$(docker run -d ubuntu:12.10 apt-get install -y curl) -docker commit -m "Installed curl" $CONTAINER $USER/betterbase -docker push $USER/betterbase -``` - -A list of publicly available images is [available -here](https://github.com/dotcloud/docker/wiki/Public-docker-images). - -Expose a service on a TCP port ------------------------------- - -```bash -# Expose port 4444 of this container, and tell netcat to listen on it -JOB=$(docker run -d -p 4444 base /bin/nc -l -p 4444) - -# Which public port is NATed to my container? -PORT=$(docker port $JOB 4444) - -# Connect to the public port via the host's public address -# Please note that because of how routing works connecting to localhost or 127.0.0.1 $PORT will not work. -# Replace *eth0* according to your local interface name. -IP=$(ip -o -4 addr list eth0 | perl -n -e 'if (m{inet\s([\d\.]+)\/\d+\s}xms) { print $1 }') -echo hello world | nc $IP $PORT - -# Verify that the network connection worked -echo "Daemon received: $(docker logs $JOB)" -``` +You can find a [list of real-world examples](http://docs.docker.io/en/latest/examples/) in the documentation. Under the hood -------------- @@ -305,149 +182,12 @@ Contributing to Docker ====================== Want to hack on Docker? Awesome! There are instructions to get you -started on the website: -http://docs.docker.io/en/latest/contributing/contributing/ +started [here](CONTRIBUTING.md). They are probably not perfect, please let us know if anything feels wrong or incomplete. -Note ----- - -We also keep the documentation in this repository. The website -documentation is generated using Sphinx using these sources. Please -find it under docs/sources/ and read more about it -https://github.com/dotcloud/docker/tree/master/docs/README.md - -Please feel free to fix / update the documentation and send us pull -requests. More tutorials are also welcome. - - -Setting up a dev environment ----------------------------- - -Instructions that have been verified to work on Ubuntu 12.10, - -```bash -sudo apt-get -y install lxc curl xz-utils golang git - -export GOPATH=~/go/ -export PATH=$GOPATH/bin:$PATH - -mkdir -p $GOPATH/src/github.com/dotcloud -cd $GOPATH/src/github.com/dotcloud -git clone https://github.com/dotcloud/docker.git -cd docker - -go get -v github.com/dotcloud/docker/... -go install -v github.com/dotcloud/docker/... -``` - -Then run the docker daemon, - -```bash -sudo $GOPATH/bin/docker -d -``` - -Run the `go install` command (above) to recompile docker. - - -What is a Standard Container? -============================= - -Docker defines a unit of software delivery called a Standard -Container. The goal of a Standard Container is to encapsulate a -software component and all its dependencies in a format that is -self-describing and portable, so that any compliant runtime can run it -without extra dependencies, regardless of the underlying machine and -the contents of the container. - -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 how -Standard Containers are a fundamental unit of software delivery, -shipping containers are a fundamental unit of physical delivery. - -### 1. STANDARD OPERATIONS - -Just like shipping containers, Standard Containers define a set of -STANDARD OPERATIONS. Shipping containers can be lifted, stacked, -locked, loaded, unloaded and labelled. Similarly, Standard Containers -can be started, stopped, copied, snapshotted, downloaded, uploaded and -tagged. - - -### 2. CONTENT-AGNOSTIC - -Just like shipping containers, Standard Containers are -CONTENT-AGNOSTIC: all standard operations have the same effect -regardless of the contents. A shipping container will be stacked in -exactly the same way whether it contains Vietnamese powder coffee or -spare Maserati parts. Similarly, Standard Containers are started or -uploaded in the same way whether they contain a postgres database, a -php application with its dependencies and application server, or Java -build artifacts. - - -### 3. INFRASTRUCTURE-AGNOSTIC - -Both types of containers are INFRASTRUCTURE-AGNOSTIC: they can be -transported to thousands of facilities around the world, and -manipulated by a wide variety of equipment. A shipping container can -be packed in a factory in Ukraine, transported by truck to the nearest -routing center, stacked onto a train, loaded into a German boat by an -Australian-built crane, stored in a warehouse at a US facility, -etc. Similarly, a standard container can be bundled on my laptop, -uploaded to S3, downloaded, run and snapshotted by a build server at -Equinix in Virginia, uploaded to 10 staging servers in a home-made -Openstack cluster, then sent to 30 production instances across 3 EC2 -regions. - - -### 4. DESIGNED FOR AUTOMATION - -Because they offer the same standard operations regardless of content -and infrastructure, Standard Containers, just like their physical -counterparts, are extremely well-suited for automation. In fact, you -could say automation is their secret weapon. - -Many things that once required time-consuming and error-prone human -effort can now be programmed. Before shipping containers, a bag of -powder coffee was hauled, dragged, dropped, rolled and stacked by 10 -different people in 10 different locations by the time it reached its -destination. 1 out of 50 disappeared. 1 out of 20 was damaged. The -process was slow, inefficient and cost a fortune - and was entirely -different depending on the facility and the type of goods. - -Similarly, before Standard Containers, by the time a software -component ran in production, it had been individually built, -configured, bundled, documented, patched, vendored, templated, tweaked -and instrumented by 10 different people on 10 different -computers. Builds failed, libraries conflicted, mirrors crashed, -post-it notes were lost, logs were misplaced, cluster updates were -half-broken. The process was slow, inefficient and cost a fortune - -and was entirely different depending on the language and -infrastructure provider. - - -### 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 -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. - - ### Legal Transfers of Docker shall be in accordance with applicable export diff --git a/VERSION b/VERSION index ee6cdce3c2..b616048743 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.6.1 +0.6.2 diff --git a/Vagrantfile b/Vagrantfile index 18aa5b5afb..85af1280a2 100644 --- a/Vagrantfile +++ b/Vagrantfile @@ -12,22 +12,20 @@ Vagrant::Config.run do |config| # Setup virtual machine box. This VM configuration code is always executed. config.vm.box = BOX_NAME config.vm.box_url = BOX_URI - config.vm.forward_port 4243, 4243 - # Provision docker and new kernel if deployment was not done + # Provision docker and new kernel if deployment was not done. + # It is assumed Vagrant can successfully launch the provider instance. if Dir.glob("#{File.dirname(__FILE__)}/.vagrant/machines/default/*/id").empty? # Add lxc-docker package - pkg_cmd = "apt-get update -qq; apt-get install -q -y python-software-properties; " \ - "add-apt-repository -y ppa:dotcloud/lxc-docker; apt-get update -qq; " \ - "apt-get install -q -y lxc-docker; " - # Add X.org Ubuntu backported 3.8 kernel - pkg_cmd << "add-apt-repository -y ppa:ubuntu-x-swat/r-lts-backport; " \ - "apt-get update -qq; apt-get install -q -y linux-image-3.8.0-19-generic; " - # Add guest additions if local vbox VM - is_vbox = true - ARGV.each do |arg| is_vbox &&= !arg.downcase.start_with?("--provider") end - if is_vbox - pkg_cmd << "apt-get install -q -y linux-headers-3.8.0-19-generic dkms; " \ + pkg_cmd = "wget -q -O - https://get.docker.io/gpg | apt-key add -;" \ + "echo deb http://get.docker.io/ubuntu docker main > /etc/apt/sources.list.d/docker.list;" \ + "apt-get update -qq; apt-get install -q -y --force-yes lxc-docker; " + # Add Ubuntu raring backported kernel + pkg_cmd << "apt-get update -qq; apt-get install -q -y linux-image-generic-lts-raring; " + # Add guest additions if local vbox VM. As virtualbox is the default provider, + # it is assumed it won't be explicitly stated. + if ENV["VAGRANT_DEFAULT_PROVIDER"].nil? && ARGV.none? { |arg| arg.downcase.start_with?("--provider") } + pkg_cmd << "apt-get install -q -y linux-headers-generic-lts-raring dkms; " \ "echo 'Downloading VBox Guest Additions...'; " \ "wget -q http://dlc.sun.com.edgesuite.net/virtualbox/4.2.12/VBoxGuestAdditions_4.2.12.iso; " # Prepare the VM to add guest additions after reboot diff --git a/api.go b/api.go index 9e094231b5..f984b1c49f 100644 --- a/api.go +++ b/api.go @@ -2,6 +2,7 @@ package docker import ( "code.google.com/p/go.net/websocket" + "encoding/base64" "encoding/json" "fmt" "github.com/dotcloud/docker/auth" @@ -20,7 +21,7 @@ import ( "strings" ) -const APIVERSION = 1.4 +const APIVERSION = 1.5 const DEFAULTHTTPHOST = "127.0.0.1" const DEFAULTHTTPPORT = 4243 const DEFAULTUNIXSOCKET = "/var/run/docker.sock" @@ -71,9 +72,18 @@ func httpError(w http.ResponseWriter, err error) { http.Error(w, err.Error(), statusCode) } -func writeJSON(w http.ResponseWriter, b []byte) { +func writeJSON(w http.ResponseWriter, code int, v interface{}) error { + b, err := json.Marshal(v) + + if err != nil { + return err + } + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(code) w.Write(b) + + return nil } func getBoolParam(value string) (bool, error) { @@ -106,25 +116,14 @@ func postAuth(srv *Server, version float64, w http.ResponseWriter, r *http.Reque return err } if status != "" { - b, err := json.Marshal(&APIAuth{Status: status}) - if err != nil { - return err - } - writeJSON(w, b) - return nil + return writeJSON(w, http.StatusOK, &APIAuth{Status: status}) } w.WriteHeader(http.StatusNoContent) return nil } func getVersion(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error { - m := srv.DockerVersion() - b, err := json.Marshal(m) - if err != nil { - return err - } - writeJSON(w, b) - return nil + return writeJSON(w, http.StatusOK, srv.DockerVersion()) } func postContainersKill(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error { @@ -167,12 +166,8 @@ func getImagesJSON(srv *Server, version float64, w http.ResponseWriter, r *http. if err != nil { return err } - b, err := json.Marshal(outs) - if err != nil { - return err - } - writeJSON(w, b) - return nil + + return writeJSON(w, http.StatusOK, outs) } func getImagesViz(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error { @@ -183,13 +178,7 @@ func getImagesViz(srv *Server, version float64, w http.ResponseWriter, r *http.R } func getInfo(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error { - out := srv.DockerInfo() - b, err := json.Marshal(out) - if err != nil { - return err - } - writeJSON(w, b) - return nil + return writeJSON(w, http.StatusOK, srv.DockerInfo()) } func getEvents(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error { @@ -258,12 +247,8 @@ func getImagesHistory(srv *Server, version float64, w http.ResponseWriter, r *ht if err != nil { return err } - b, err := json.Marshal(outs) - if err != nil { - return err - } - writeJSON(w, b) - return nil + + return writeJSON(w, http.StatusOK, outs) } func getContainersChanges(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error { @@ -275,12 +260,8 @@ func getContainersChanges(srv *Server, version float64, w http.ResponseWriter, r if err != nil { return err } - b, err := json.Marshal(changesStr) - if err != nil { - return err - } - writeJSON(w, b) - return nil + + return writeJSON(w, http.StatusOK, changesStr) } func getContainersTop(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error { @@ -299,12 +280,8 @@ func getContainersTop(srv *Server, version float64, w http.ResponseWriter, r *ht if err != nil { return err } - b, err := json.Marshal(procsStr) - if err != nil { - return err - } - writeJSON(w, b) - return nil + + return writeJSON(w, http.StatusOK, procsStr) } func getContainersJSON(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error { @@ -327,12 +304,17 @@ func getContainersJSON(srv *Server, version float64, w http.ResponseWriter, r *h } outs := srv.Containers(all, size, n, since, before) - b, err := json.Marshal(outs) - if err != nil { - return err + + if version < 1.5 { + outs2 := []APIContainersOld{} + for _, ctnr := range outs { + outs2 = append(outs2, ctnr.ToLegacy()) + } + + return writeJSON(w, http.StatusOK, outs2) + } else { + return writeJSON(w, http.StatusOK, outs) } - writeJSON(w, b) - return nil } func postImagesTag(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error { @@ -374,13 +356,8 @@ func postCommit(srv *Server, version float64, w http.ResponseWriter, r *http.Req if err != nil { return err } - b, err := json.Marshal(&APIID{id}) - if err != nil { - return err - } - w.WriteHeader(http.StatusCreated) - writeJSON(w, b) - return nil + + return writeJSON(w, http.StatusCreated, &APIID{id}) } // Creates an image from Pull or from Import @@ -394,6 +371,16 @@ func postImagesCreate(srv *Server, version float64, w http.ResponseWriter, r *ht tag := r.Form.Get("tag") repo := r.Form.Get("repo") + authEncoded := r.Header.Get("X-Registry-Auth") + authConfig := &auth.AuthConfig{} + if authEncoded != "" { + authJson := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded)) + if err := json.NewDecoder(authJson).Decode(authConfig); err != nil { + // for a pull it is not an error if no auth was given + // to increase compatibility with the existing api it is defaulting to be empty + authConfig = &auth.AuthConfig{} + } + } if version > 1.0 { w.Header().Set("Content-Type", "application/json") } @@ -405,7 +392,7 @@ func postImagesCreate(srv *Server, version float64, w http.ResponseWriter, r *ht metaHeaders[k] = v } } - if err := srv.ImagePull(image, tag, w, sf, &auth.AuthConfig{}, metaHeaders, version > 1.3); err != nil { + if err := srv.ImagePull(image, tag, w, sf, authConfig, metaHeaders, version > 1.3); err != nil { if sf.Used() { w.Write(sf.FormatError(err)) return nil @@ -434,12 +421,8 @@ func getImagesSearch(srv *Server, version float64, w http.ResponseWriter, r *htt if err != nil { return err } - b, err := json.Marshal(outs) - if err != nil { - return err - } - writeJSON(w, b) - return nil + + return writeJSON(w, http.StatusOK, outs) } func postImagesInsert(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error { @@ -464,28 +447,37 @@ func postImagesInsert(srv *Server, version float64, w http.ResponseWriter, r *ht return nil } } - b, err := json.Marshal(&APIID{ID: imgID}) - if err != nil { - return err - } - writeJSON(w, b) - return nil + + return writeJSON(w, http.StatusOK, &APIID{ID: imgID}) } func postImagesPush(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error { - authConfig := &auth.AuthConfig{} metaHeaders := map[string][]string{} for k, v := range r.Header { if strings.HasPrefix(k, "X-Meta-") { metaHeaders[k] = v } } - if err := json.NewDecoder(r.Body).Decode(authConfig); err != nil { - return err - } if err := parseForm(r); err != nil { return err } + authConfig := &auth.AuthConfig{} + + authEncoded := r.Header.Get("X-Registry-Auth") + if authEncoded != "" { + // the new format is to handle the authConfig as a header + authJson := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded)) + if err := json.NewDecoder(authJson).Decode(authConfig); err != nil { + // to increase compatibility to existing api it is defaulting to be empty + authConfig = &auth.AuthConfig{} + } + } else { + // the old format is supported for compatibility if there was no authConfig header + if err := json.NewDecoder(r.Body).Decode(authConfig); err != nil { + return err + } + + } if vars == nil { return fmt.Errorf("Missing parameter") @@ -518,7 +510,7 @@ func postContainersCreate(srv *Server, version float64, w http.ResponseWriter, r return err } - if len(config.Dns) == 0 && len(srv.runtime.Dns) == 0 && utils.CheckLocalDns(resolvConf) { + if !config.NetworkDisabled && len(config.Dns) == 0 && len(srv.runtime.Dns) == 0 && utils.CheckLocalDns(resolvConf) { out.Warnings = append(out.Warnings, fmt.Sprintf("Docker detected local DNS server on resolv.conf. Using default external servers: %v", defaultDns)) config.Dns = defaultDns } @@ -538,18 +530,12 @@ func postContainersCreate(srv *Server, version float64, w http.ResponseWriter, r out.Warnings = append(out.Warnings, "Your kernel does not support memory swap capabilities. Limitation discarded.") } - if srv.runtime.capabilities.IPv4ForwardingDisabled { + if !config.NetworkDisabled && srv.runtime.capabilities.IPv4ForwardingDisabled { log.Println("Warning: IPv4 forwarding is disabled.") out.Warnings = append(out.Warnings, "IPv4 forwarding is disabled.") } - b, err := json.Marshal(out) - if err != nil { - return err - } - w.WriteHeader(http.StatusCreated) - writeJSON(w, b) - return nil + return writeJSON(w, http.StatusCreated, out) } func postContainersRestart(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error { @@ -605,11 +591,7 @@ func deleteImages(srv *Server, version float64, w http.ResponseWriter, r *http.R } if imgs != nil { if len(imgs) != 0 { - b, err := json.Marshal(imgs) - if err != nil { - return err - } - writeJSON(w, b) + return writeJSON(w, http.StatusOK, imgs) } else { return fmt.Errorf("Conflict, %s wasn't deleted", name) } @@ -620,11 +602,11 @@ func deleteImages(srv *Server, version float64, w http.ResponseWriter, r *http.R } func postContainersStart(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error { - hostConfig := &HostConfig{} - + var hostConfig *HostConfig // allow a nil body for backwards compatibility if r.Body != nil { if matchesContentType(r.Header.Get("Content-Type"), "application/json") { + hostConfig = &HostConfig{} if err := json.NewDecoder(r.Body).Decode(hostConfig); err != nil { return err } @@ -672,12 +654,8 @@ func postContainersWait(srv *Server, version float64, w http.ResponseWriter, r * if err != nil { return err } - b, err := json.Marshal(&APIWait{StatusCode: status}) - if err != nil { - return err - } - writeJSON(w, b) - return nil + + return writeJSON(w, http.StatusOK, &APIWait{StatusCode: status}) } func postContainersResize(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error { @@ -819,12 +797,13 @@ func getContainersByName(srv *Server, version float64, w http.ResponseWriter, r if err != nil { return err } - b, err := json.Marshal(container) - if err != nil { - return err + + _, err = srv.ImageInspect(name) + if err == nil { + return fmt.Errorf("Conflict between containers and images") } - writeJSON(w, b) - return nil + + return writeJSON(w, http.StatusOK, container) } func getImagesByName(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error { @@ -837,35 +816,13 @@ func getImagesByName(srv *Server, version float64, w http.ResponseWriter, r *htt if err != nil { return err } - b, err := json.Marshal(image) - if err != nil { - return err - } - writeJSON(w, b) - return nil -} -func postImagesGetCache(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error { - apiConfig := &APIImageConfig{} - if err := json.NewDecoder(r.Body).Decode(apiConfig); err != nil { - return err + _, err = srv.ContainerInspect(name) + if err == nil { + return fmt.Errorf("Conflict between containers and images") } - image, err := srv.ImageGetCached(apiConfig.ID, apiConfig.Config) - if err != nil { - return err - } - if image == nil { - w.WriteHeader(http.StatusNotFound) - return nil - } - apiID := &APIID{ID: image.ID} - b, err := json.Marshal(apiID) - if err != nil { - return err - } - writeJSON(w, b) - return nil + return writeJSON(w, http.StatusOK, image) } func postBuild(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error { @@ -876,6 +833,7 @@ func postBuild(srv *Server, version float64, w http.ResponseWriter, r *http.Requ repoName := r.FormValue("t") rawSuppressOutput := r.FormValue("q") rawNoCache := r.FormValue("nocache") + rawRm := r.FormValue("rm") repoName, tag := utils.ParseRepositoryTag(repoName) var context io.Reader @@ -926,8 +884,12 @@ func postBuild(srv *Server, version float64, w http.ResponseWriter, r *http.Requ if err != nil { return err } + rm, err := getBoolParam(rawRm) + if err != nil { + return err + } - b := NewBuildFile(srv, utils.NewWriteFlusher(w), !suppressOutput, !noCache) + b := NewBuildFile(srv, utils.NewWriteFlusher(w), !suppressOutput, !noCache, rm) id, err := b.Build(context) if err != nil { fmt.Fprintf(w, "Error build: %s\n", err) @@ -1043,7 +1005,6 @@ func createRouter(srv *Server, logging bool) (*mux.Router, error) { "/images/{name:.*}/insert": postImagesInsert, "/images/{name:.*}/push": postImagesPush, "/images/{name:.*}/tag": postImagesTag, - "/images/getCache": postImagesGetCache, "/containers/create": postContainersCreate, "/containers/{name:.*}/kill": postContainersKill, "/containers/{name:.*}/restart": postContainersRestart, diff --git a/api_params.go b/api_params.go index df879f63ee..6403bc6a26 100644 --- a/api_params.go +++ b/api_params.go @@ -1,5 +1,7 @@ package docker +import "encoding/json" + type APIHistory struct { ID string `json:"Id"` Tags []string `json:",omitempty"` @@ -42,6 +44,30 @@ type APIRmi struct { } type APIContainers struct { + ID string `json:"Id"` + Image string + Command string + Created int64 + Status string + Ports []APIPort + SizeRw int64 + SizeRootFs int64 +} + +func (self *APIContainers) ToLegacy() APIContainersOld { + return APIContainersOld{ + ID: self.ID, + Image: self.Image, + Command: self.Command, + Created: self.Created, + Status: self.Status, + Ports: displayablePorts(self.Ports), + SizeRw: self.SizeRw, + SizeRootFs: self.SizeRootFs, + } +} + +type APIContainersOld struct { ID string `json:"Id"` Image string Command string @@ -67,7 +93,17 @@ type APIRun struct { } type APIPort struct { - Port string + PrivatePort int64 + PublicPort int64 + Type string +} + +func (port *APIPort) MarshalJSON() ([]byte, error) { + return json.Marshal(map[string]interface{}{ + "PrivatePort": port.PrivatePort, + "PublicPort": port.PublicPort, + "Type": port.Type, + }) } type APIVersion struct { diff --git a/api_test.go b/api_test.go index fc7eeed713..d24cf7cfda 100644 --- a/api_test.go +++ b/api_test.go @@ -68,7 +68,7 @@ func TestGetInfo(t *testing.T) { srv := &Server{runtime: runtime} - initialImages, err := srv.runtime.graph.All() + initialImages, err := srv.runtime.graph.Map() if err != nil { t.Fatal(err) } @@ -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"}, @@ -449,6 +445,7 @@ func TestGetContainersChanges(t *testing.T) { } func TestGetContainersTop(t *testing.T) { + t.Skip("Fixme. Skipping test for now. Reported error when testing using dind: 'api_test.go:527: Expected 2 processes, found 0.'") runtime, err := newTestRuntime() if err != nil { t.Fatal(err) @@ -457,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"}, @@ -540,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"}, @@ -573,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"}, @@ -670,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"}, @@ -712,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"}, @@ -766,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"}, @@ -816,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"}, @@ -863,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"}, @@ -905,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"}, @@ -997,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"}, }) @@ -1184,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/auth/MAINTAINERS b/auth/MAINTAINERS deleted file mode 120000 index dcaec6ec44..0000000000 --- a/auth/MAINTAINERS +++ /dev/null @@ -1 +0,0 @@ -../registry/MAINTAINERS \ No newline at end of file diff --git a/auth/MAINTAINERS b/auth/MAINTAINERS new file mode 100644 index 0000000000..bf3984f5f9 --- /dev/null +++ b/auth/MAINTAINERS @@ -0,0 +1,3 @@ +Sam Alba (@samalba) +Joffrey Fuhrer (@shin-) +Ken Cochrane (@kencochrane) diff --git a/auth/auth.go b/auth/auth.go index 91314877c7..aff6de6dce 100644 --- a/auth/auth.go +++ b/auth/auth.go @@ -26,10 +26,11 @@ var ( ) type AuthConfig struct { - Username string `json:"username,omitempty"` - Password string `json:"password,omitempty"` - Auth string `json:"auth"` - Email string `json:"email"` + Username string `json:"username,omitempty"` + Password string `json:"password,omitempty"` + Auth string `json:"auth"` + Email string `json:"email"` + ServerAddress string `json:"serveraddress,omitempty"` } type ConfigFile struct { @@ -96,6 +97,7 @@ func LoadConfig(rootPath string) (*ConfigFile, error) { } origEmail := strings.Split(arr[1], " = ") authConfig.Email = origEmail[1] + authConfig.ServerAddress = IndexServerAddress() configFile.Configs[IndexServerAddress()] = authConfig } else { for k, authConfig := range configFile.Configs { @@ -105,6 +107,7 @@ func LoadConfig(rootPath string) (*ConfigFile, error) { } authConfig.Auth = "" configFile.Configs[k] = authConfig + authConfig.ServerAddress = k } } return &configFile, nil @@ -125,7 +128,7 @@ func SaveConfig(configFile *ConfigFile) error { authCopy.Auth = encodeAuth(&authCopy) authCopy.Username = "" authCopy.Password = "" - + authCopy.ServerAddress = "" configs[k] = authCopy } @@ -146,14 +149,26 @@ func Login(authConfig *AuthConfig, factory *utils.HTTPRequestFactory) (string, e reqStatusCode := 0 var status string var reqBody []byte - jsonBody, err := json.Marshal(authConfig) + + serverAddress := authConfig.ServerAddress + if serverAddress == "" { + serverAddress = IndexServerAddress() + } + + loginAgainstOfficialIndex := serverAddress == IndexServerAddress() + + // to avoid sending the server address to the server it should be removed before marshalled + authCopy := *authConfig + authCopy.ServerAddress = "" + + jsonBody, err := json.Marshal(authCopy) if err != nil { return "", fmt.Errorf("Config Error: %s", err) } // using `bytes.NewReader(jsonBody)` here causes the server to respond with a 411 status. b := strings.NewReader(string(jsonBody)) - req1, err := http.Post(IndexServerAddress()+"users/", "application/json; charset=utf-8", b) + req1, err := http.Post(serverAddress+"users/", "application/json; charset=utf-8", b) if err != nil { return "", fmt.Errorf("Server Error: %s", err) } @@ -165,14 +180,23 @@ func Login(authConfig *AuthConfig, factory *utils.HTTPRequestFactory) (string, e } if reqStatusCode == 201 { - status = "Account created. Please use the confirmation link we sent" + - " to your e-mail to activate it." + if loginAgainstOfficialIndex { + status = "Account created. Please use the confirmation link we sent" + + " to your e-mail to activate it." + } else { + status = "Account created. Please see the documentation of the registry " + serverAddress + " for instructions how to activate it." + } } else if reqStatusCode == 403 { - return "", fmt.Errorf("Login: Your account hasn't been activated. " + - "Please check your e-mail for a confirmation link.") + if loginAgainstOfficialIndex { + return "", fmt.Errorf("Login: Your account hasn't been activated. " + + "Please check your e-mail for a confirmation link.") + } else { + return "", fmt.Errorf("Login: Your account hasn't been activated. " + + "Please see the documentation of the registry " + serverAddress + " for instructions how to activate it.") + } } else if reqStatusCode == 400 { if string(reqBody) == "\"Username or email already exists\"" { - req, err := factory.NewRequest("GET", IndexServerAddress()+"users/", nil) + req, err := factory.NewRequest("GET", serverAddress+"users/", nil) req.SetBasicAuth(authConfig.Username, authConfig.Password) resp, err := client.Do(req) if err != nil { @@ -199,3 +223,52 @@ func Login(authConfig *AuthConfig, factory *utils.HTTPRequestFactory) (string, e } return status, nil } + +// this method matches a auth configuration to a server address or a url +func (config *ConfigFile) ResolveAuthConfig(registry string) AuthConfig { + if registry == IndexServerAddress() || len(registry) == 0 { + // default to the index server + return config.Configs[IndexServerAddress()] + } + // if its not the index server there are three cases: + // + // 1. this is a full config url -> it should be used as is + // 2. it could be a full url, but with the wrong protocol + // 3. it can be the hostname optionally with a port + // + // as there is only one auth entry which is fully qualified we need to start + // parsing and matching + + swapProtocoll := func(url string) string { + if strings.HasPrefix(url, "http:") { + return strings.Replace(url, "http:", "https:", 1) + } + if strings.HasPrefix(url, "https:") { + return strings.Replace(url, "https:", "http:", 1) + } + return url + } + + resolveIgnoringProtocol := func(url string) AuthConfig { + if c, found := config.Configs[url]; found { + return c + } + registrySwappedProtocoll := swapProtocoll(url) + // now try to match with the different protocol + if c, found := config.Configs[registrySwappedProtocoll]; found { + return c + } + return AuthConfig{} + } + + // match both protocols as it could also be a server name like httpfoo + if strings.HasPrefix(registry, "http:") || strings.HasPrefix(registry, "https:") { + return resolveIgnoringProtocol(registry) + } + + url := "https://" + registry + if !strings.Contains(registry, "/") { + url = url + "/v1/" + } + return resolveIgnoringProtocol(url) +} 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..7f2ea00643 100644 --- a/buildfile.go +++ b/buildfile.go @@ -1,7 +1,6 @@ package docker import ( - "bufio" "encoding/json" "fmt" "github.com/dotcloud/docker/utils" @@ -23,7 +22,6 @@ type BuildFile interface { type buildFile struct { runtime *Runtime - builder *Builder srv *Server image string @@ -32,6 +30,7 @@ type buildFile struct { context string verbose bool utilizeCache bool + rm bool tmpContainers map[string]struct{} tmpImages map[string]struct{} @@ -39,15 +38,11 @@ type buildFile struct { out io.Writer } -func (b *buildFile) clearTmp(containers, images map[string]struct{}) { +func (b *buildFile) clearTmp(containers map[string]struct{}) { for c := range containers { tmp := b.runtime.Get(c) b.runtime.Destroy(tmp) - utils.Debugf("Removing container %s", c) - } - for i := range images { - b.runtime.graph.Delete(i) - utils.Debugf("Removing image %s", i) + fmt.Fprintf(b.out, "Removing intermediate container %s\n", utils.TruncateID(c)) } } @@ -293,7 +288,7 @@ func (b *buildFile) addContext(container *Container, orig, dest string) error { } fi, err := os.Stat(origPath) if err != nil { - return err + return fmt.Errorf("%s: no such file or directory", orig) } if fi.IsDir() { if err := CopyWithTar(origPath, destPath); err != nil { @@ -337,7 +332,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 +367,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 +423,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 +445,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 } @@ -459,6 +454,9 @@ func (b *buildFile) commit(id string, autoCmd []string, comment string) error { return nil } +// Long lines can be split with a backslash +var lineContinuation = regexp.MustCompile(`\s*\\\s*\n`) + func (b *buildFile) Build(context io.Reader) (string, error) { // FIXME: @creack any reason for using /tmp instead of ""? // FIXME: @creack "name" is a terrible variable name @@ -471,22 +469,18 @@ func (b *buildFile) Build(context io.Reader) (string, error) { } defer os.RemoveAll(name) b.context = name - dockerfile, err := os.Open(path.Join(name, "Dockerfile")) - if err != nil { + filename := path.Join(name, "Dockerfile") + if _, err := os.Stat(filename); os.IsNotExist(err) { return "", fmt.Errorf("Can't build a directory with no Dockerfile") } - // FIXME: "file" is also a terrible variable name ;) - file := bufio.NewReader(dockerfile) + fileBytes, err := ioutil.ReadFile(filename) + if err != nil { + return "", err + } + dockerfile := string(fileBytes) + dockerfile = lineContinuation.ReplaceAllString(dockerfile, "") stepN := 0 - for { - line, err := file.ReadString('\n') - if err != nil { - if err == io.EOF && line == "" { - break - } else if err != io.EOF { - return "", err - } - } + for _, line := range strings.Split(dockerfile, "\n") { line = strings.Trim(strings.Replace(line, "\t", " ", -1), " \t\r\n") // Skip comments and empty line if len(line) == 0 || line[0] == '#' { @@ -517,14 +511,16 @@ func (b *buildFile) Build(context io.Reader) (string, error) { } if b.image != "" { fmt.Fprintf(b.out, "Successfully built %s\n", utils.TruncateID(b.image)) + if b.rm { + b.clearTmp(b.tmpContainers) + } return b.image, nil } return "", fmt.Errorf("An error occurred during the build\n") } -func NewBuildFile(srv *Server, out io.Writer, verbose, utilizeCache bool) BuildFile { +func NewBuildFile(srv *Server, out io.Writer, verbose, utilizeCache, rm bool) BuildFile { return &buildFile{ - builder: NewBuilder(srv.runtime), runtime: srv.runtime, srv: srv, config: &Config{}, @@ -533,5 +529,6 @@ func NewBuildFile(srv *Server, out io.Writer, verbose, utilizeCache bool) BuildF tmpImages: make(map[string]struct{}), verbose: verbose, utilizeCache: utilizeCache, + rm: rm, } } diff --git a/buildfile_test.go b/buildfile_test.go index 14986161d8..c3881a214f 100644 --- a/buildfile_test.go +++ b/buildfile_test.go @@ -45,6 +45,34 @@ run [ "$(ls -d /var/run/sshd)" = "/var/run/sshd" ] nil, }, + // Exactly the same as above, except uses a line split with a \ to test + // multiline support. + { + ` +from {IMAGE} +run sh -c 'echo root:testpass \ + > /tmp/passwd' +run mkdir -p /var/run/sshd +run [ "$(cat /tmp/passwd)" = "root:testpass" ] +run [ "$(ls -d /var/run/sshd)" = "/var/run/sshd" ] +`, + nil, + nil, + }, + + // Line containing literal "\n" + { + ` +from {IMAGE} +run sh -c 'echo root:testpass > /tmp/passwd' +run echo "foo \n bar"; echo "baz" +run mkdir -p /var/run/sshd +run [ "$(cat /tmp/passwd)" = "root:testpass" ] +run [ "$(ls -d /var/run/sshd)" = "/var/run/sshd" ] +`, + nil, + nil, + }, { ` from {IMAGE} @@ -229,7 +257,7 @@ func buildImage(context testContextTemplate, t *testing.T, srv *Server, useCache ip := srv.runtime.networkManager.bridgeNetwork.IP dockerfile := constructDockerfile(context.dockerfile, ip, port) - buildfile := NewBuildFile(srv, ioutil.Discard, false, useCache) + buildfile := NewBuildFile(srv, ioutil.Discard, false, useCache, false) id, err := buildfile.Build(mkTestContext(dockerfile, context.files, t)) if err != nil { t.Fatal(err) @@ -470,7 +498,7 @@ func TestForbiddenContextPath(t *testing.T) { ip := srv.runtime.networkManager.bridgeNetwork.IP dockerfile := constructDockerfile(context.dockerfile, ip, port) - buildfile := NewBuildFile(srv, ioutil.Discard, false, true) + buildfile := NewBuildFile(srv, ioutil.Discard, false, true, false) _, err = buildfile.Build(mkTestContext(dockerfile, context.files, t)) if err == nil { @@ -483,3 +511,51 @@ func TestForbiddenContextPath(t *testing.T) { t.Fail() } } + +func TestBuildADDFileNotFound(t *testing.T) { + runtime, err := newTestRuntime() + if err != nil { + t.Fatal(err) + } + defer nuke(runtime) + + srv := &Server{ + runtime: runtime, + pullingPool: make(map[string]struct{}), + pushingPool: make(map[string]struct{}), + } + + context := testContextTemplate{` + from {IMAGE} + add foo /usr/local/bar + `, + nil, nil} + + httpServer, err := mkTestingFileServer(context.remoteFiles) + if err != nil { + t.Fatal(err) + } + defer httpServer.Close() + + idx := strings.LastIndex(httpServer.URL, ":") + if idx < 0 { + t.Fatalf("could not get port from test http server address %s", httpServer.URL) + } + port := httpServer.URL[idx+1:] + + ip := srv.runtime.networkManager.bridgeNetwork.IP + dockerfile := constructDockerfile(context.dockerfile, ip, port) + + buildfile := NewBuildFile(srv, ioutil.Discard, false, true, false) + _, err = buildfile.Build(mkTestContext(dockerfile, context.files, t)) + + if err == nil { + t.Log("Error should not be nil") + t.Fail() + } + + if err.Error() != "foo: no such file or directory" { + t.Logf("Error message is not expected: %s", err.Error()) + t.Fail() + } +} diff --git a/changes.go b/changes.go index dc1b015726..43573cd606 100644 --- a/changes.go +++ b/changes.go @@ -99,7 +99,7 @@ func Changes(layers []string, rw string) ([]Change, error) { changes = append(changes, change) return nil }) - if err != nil { + if err != nil && !os.IsNotExist(err) { return nil, err } return changes, nil diff --git a/commands.go b/commands.go index d90d0eba2a..62d42b9874 100644 --- a/commands.go +++ b/commands.go @@ -2,11 +2,14 @@ package docker import ( "archive/tar" + "bufio" "bytes" + "encoding/base64" "encoding/json" "flag" "fmt" "github.com/dotcloud/docker/auth" + "github.com/dotcloud/docker/registry" "github.com/dotcloud/docker/term" "github.com/dotcloud/docker/utils" "io" @@ -19,17 +22,18 @@ import ( "os/signal" "path/filepath" "reflect" + "runtime" + "sort" "strconv" "strings" "syscall" "text/tabwriter" "time" - "unicode" ) var ( GITCOMMIT string - VERSION string + VERSION string ) func (cli *DockerCli) getMethod(name string) (reflect.Method, bool) { @@ -125,7 +129,7 @@ func (cli *DockerCli) CmdInsert(args ...string) error { v.Set("url", cmd.Arg(1)) v.Set("path", cmd.Arg(2)) - if err := cli.stream("POST", "/images/"+cmd.Arg(0)+"/insert?"+v.Encode(), nil, cli.out); err != nil { + if err := cli.stream("POST", "/images/"+cmd.Arg(0)+"/insert?"+v.Encode(), nil, cli.out, nil); err != nil { return err } return nil @@ -161,6 +165,7 @@ func (cli *DockerCli) CmdBuild(args ...string) error { tag := cmd.String("t", "", "Repository name (and optionally a tag) to be applied to the resulting image in case of success") suppressOutput := cmd.Bool("q", false, "Suppress verbose build output") noCache := cmd.Bool("no-cache", false, "Do not use cache when building the image") + rm := cmd.Bool("rm", false, "Remove intermediate containers after a successful build") if err := cmd.Parse(args); err != nil { return nil } @@ -211,6 +216,9 @@ func (cli *DockerCli) CmdBuild(args ...string) error { if *noCache { v.Set("nocache", "1") } + if *rm { + v.Set("rm", "1") + } req, err := http.NewRequest("POST", fmt.Sprintf("/v%g/build?%s", APIVERSION, v.Encode()), body) if err != nil { return err @@ -251,75 +259,27 @@ func (cli *DockerCli) CmdBuild(args ...string) error { // 'docker login': login / register a user to registry service. func (cli *DockerCli) CmdLogin(args ...string) error { - var readStringOnRawTerminal = func(stdin io.Reader, stdout io.Writer, echo bool) string { - char := make([]byte, 1) - buffer := make([]byte, 64) - var i = 0 - for i < len(buffer) { - n, err := stdin.Read(char) - if n > 0 { - if char[0] == '\r' || char[0] == '\n' { - stdout.Write([]byte{'\r', '\n'}) - break - } else if char[0] == 127 || char[0] == '\b' { - if i > 0 { - if echo { - stdout.Write([]byte{'\b', ' ', '\b'}) - } - i-- - } - } else if !unicode.IsSpace(rune(char[0])) && - !unicode.IsControl(rune(char[0])) { - if echo { - stdout.Write(char) - } - buffer[i] = char[0] - i++ - } - } - if err != nil { - if err != io.EOF { - fmt.Fprintf(stdout, "Read error: %v\r\n", err) - } - break - } - } - return string(buffer[:i]) - } - var readAndEchoString = func(stdin io.Reader, stdout io.Writer) string { - return readStringOnRawTerminal(stdin, stdout, true) - } - var readString = func(stdin io.Reader, stdout io.Writer) string { - return readStringOnRawTerminal(stdin, stdout, false) - } + cmd := Subcmd("login", "[OPTIONS] [SERVER]", "Register or Login to a docker registry server, if no server is specified \""+auth.IndexServerAddress()+"\" is the default.") - cmd := Subcmd("login", "[OPTIONS]", "Register or Login to the docker registry server") - flUsername := cmd.String("u", "", "username") - flPassword := cmd.String("p", "", "password") - flEmail := cmd.String("e", "", "email") + var username, password, email string + + cmd.StringVar(&username, "u", "", "username") + cmd.StringVar(&password, "p", "", "password") + cmd.StringVar(&email, "e", "", "email") err := cmd.Parse(args) if err != nil { return nil } - - cli.LoadConfigFile() - - var oldState *term.State - if *flUsername == "" || *flPassword == "" || *flEmail == "" { - oldState, err = term.SetRawTerminal(cli.terminalFd) + serverAddress := auth.IndexServerAddress() + if len(cmd.Args()) > 0 { + serverAddress, err = registry.ExpandAndVerifyRegistryUrl(cmd.Arg(0)) if err != nil { return err } - defer term.RestoreTerminal(cli.terminalFd, oldState) + fmt.Fprintf(cli.out, "Login against server at %s\n", serverAddress) } - var ( - username string - password string - email string - ) - - var promptDefault = func(prompt string, configDefault string) { + promptDefault := func(prompt string, configDefault string) { if configDefault == "" { fmt.Fprintf(cli.out, "%s: ", prompt) } else { @@ -327,55 +287,64 @@ func (cli *DockerCli) CmdLogin(args ...string) error { } } + readInput := func(in io.Reader, out io.Writer) string { + reader := bufio.NewReader(in) + line, _, err := reader.ReadLine() + if err != nil { + fmt.Fprintln(out, err.Error()) + os.Exit(1) + } + return string(line) + } + + cli.LoadConfigFile() authconfig, ok := cli.configFile.Configs[auth.IndexServerAddress()] if !ok { authconfig = auth.AuthConfig{} } - if *flUsername == "" { + if username == "" { promptDefault("Username", authconfig.Username) - username = readAndEchoString(cli.in, cli.out) + username = readInput(cli.in, cli.out) if username == "" { username = authconfig.Username } - } else { - username = *flUsername } if username != authconfig.Username { - if *flPassword == "" { + if password == "" { + oldState, _ := term.SaveState(cli.terminalFd) fmt.Fprintf(cli.out, "Password: ") - password = readString(cli.in, cli.out) + term.DisableEcho(cli.terminalFd, oldState) + + password = readInput(cli.in, cli.out) + fmt.Fprint(cli.out, "\n") + + term.RestoreTerminal(cli.terminalFd, oldState) if password == "" { return fmt.Errorf("Error : Password Required") } - } else { - password = *flPassword } - if *flEmail == "" { + if email == "" { promptDefault("Email", authconfig.Email) - email = readAndEchoString(cli.in, cli.out) + email = readInput(cli.in, cli.out) if email == "" { email = authconfig.Email } - } else { - email = *flEmail } } else { password = authconfig.Password email = authconfig.Email } - if oldState != nil { - term.RestoreTerminal(cli.terminalFd, oldState) - } authconfig.Username = username authconfig.Password = password authconfig.Email = email - cli.configFile.Configs[auth.IndexServerAddress()] = authconfig + authconfig.ServerAddress = serverAddress + cli.configFile.Configs[serverAddress] = authconfig - body, statusCode, err := cli.call("POST", "/auth", cli.configFile.Configs[auth.IndexServerAddress()]) + body, statusCode, err := cli.call("POST", "/auth", cli.configFile.Configs[serverAddress]) if statusCode == 401 { - delete(cli.configFile.Configs, auth.IndexServerAddress()) + delete(cli.configFile.Configs, serverAddress) auth.SaveConfig(cli.configFile) return err } @@ -407,16 +376,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 @@ -433,6 +397,13 @@ func (cli *DockerCli) CmdVersion(args ...string) error { cmd.Usage() return nil } + if VERSION != "" { + fmt.Fprintf(cli.out, "Client version: %s\n", VERSION) + } + fmt.Fprintf(cli.out, "Go version (client): %s\n", runtime.Version()) + if GITCOMMIT != "" { + fmt.Fprintf(cli.out, "Git commit (client): %s\n", GITCOMMIT) + } body, _, err := cli.call("GET", "/version", nil) if err != nil { @@ -445,19 +416,20 @@ func (cli *DockerCli) CmdVersion(args ...string) error { utils.Debugf("Error unmarshal: body: %s, err: %s\n", body, err) return err } - fmt.Fprintf(cli.out, "Client version: %s\n", VERSION) - fmt.Fprintf(cli.out, "Server version: %s\n", out.Version) + if out.Version != "" { + fmt.Fprintf(cli.out, "Server version: %s\n", out.Version) + } if out.GitCommit != "" { - fmt.Fprintf(cli.out, "Git commit: %s\n", out.GitCommit) + fmt.Fprintf(cli.out, "Git commit (server): %s\n", out.GitCommit) } if out.GoVersion != "" { - fmt.Fprintf(cli.out, "Go version: %s\n", out.GoVersion) + fmt.Fprintf(cli.out, "Go version (server): %s\n", out.GoVersion) } release := utils.GetReleaseVersion() if release != "" { fmt.Fprintf(cli.out, "Last stable version: %s", release) - if strings.Trim(VERSION, "-dev") != release || strings.Trim(out.Version, "-dev") != release { + if (VERSION != "" || out.Version != "") && (strings.Trim(VERSION, "-dev") != release || strings.Trim(out.Version, "-dev") != release) { fmt.Fprintf(cli.out, ", please update docker") } fmt.Fprintf(cli.out, "\n") @@ -578,15 +550,17 @@ func (cli *DockerCli) CmdStart(args ...string) error { return nil } + var encounteredError error for _, name := range args { _, _, err := cli.call("POST", "/containers/"+name+"/start", nil) if err != nil { fmt.Fprintf(cli.err, "%s\n", err) + encounteredError = fmt.Errorf("Error: failed to start one or more containers") } else { fmt.Fprintf(cli.out, "%s\n", name) } } - return nil + return encounteredError } func (cli *DockerCli) CmdInspect(args ...string) error { @@ -607,7 +581,7 @@ func (cli *DockerCli) CmdInspect(args ...string) error { if err != nil { obj, _, err = cli.call("GET", "/images/"+name+"/json", nil) if err != nil { - fmt.Fprintf(cli.err, "%s\n", err) + fmt.Fprintf(cli.err, "No such image or container: %s\n", name) continue } } @@ -821,7 +795,7 @@ func (cli *DockerCli) CmdImport(args ...string) error { v.Set("tag", tag) v.Set("fromSrc", src) - err := cli.stream("POST", "/images/create?"+v.Encode(), cli.in, cli.out) + err := cli.stream("POST", "/images/create?"+v.Encode(), cli.in, cli.out, nil) if err != nil { return err } @@ -842,6 +816,13 @@ func (cli *DockerCli) CmdPush(args ...string) error { cli.LoadConfigFile() + // Resolve the Repository name from fqn to endpoint + name + endpoint, _, err := registry.ResolveRepositoryName(name) + if err != nil { + return err + } + // Resolve the Auth config relevant for this server + authConfig := cli.configFile.ResolveAuthConfig(endpoint) // If we're not using a custom registry, we know the restrictions // applied to repository names and can warn the user in advance. // Custom repositories can have different rules, and we must also @@ -855,22 +836,28 @@ func (cli *DockerCli) CmdPush(args ...string) error { } v := url.Values{} - push := func() error { - buf, err := json.Marshal(cli.configFile.Configs[auth.IndexServerAddress()]) + push := func(authConfig auth.AuthConfig) error { + buf, err := json.Marshal(authConfig) if err != nil { return err } + registryAuthHeader := []string{ + base64.URLEncoding.EncodeToString(buf), + } - return cli.stream("POST", "/images/"+name+"/push?"+v.Encode(), bytes.NewBuffer(buf), cli.out) + return cli.stream("POST", "/images/"+name+"/push?"+v.Encode(), nil, cli.out, map[string][]string{ + "X-Registry-Auth": registryAuthHeader, + }) } - if err := push(); err != nil { - if err.Error() == "Authentication is required." { + if err := push(authConfig); err != nil { + if err.Error() == registry.ErrLoginRequired.Error() { fmt.Fprintln(cli.out, "\nPlease login prior to push:") - if err := cli.CmdLogin(""); err != nil { + if err := cli.CmdLogin(endpoint); err != nil { return err } - return push() + authConfig := cli.configFile.ResolveAuthConfig(endpoint) + return push(authConfig) } return err } @@ -894,11 +881,43 @@ func (cli *DockerCli) CmdPull(args ...string) error { *tag = parsedTag } + // Resolve the Repository name from fqn to endpoint + name + endpoint, _, err := registry.ResolveRepositoryName(remote) + if err != nil { + return err + } + + cli.LoadConfigFile() + + // Resolve the Auth config relevant for this server + authConfig := cli.configFile.ResolveAuthConfig(endpoint) v := url.Values{} v.Set("fromImage", remote) v.Set("tag", *tag) - if err := cli.stream("POST", "/images/create?"+v.Encode(), nil, cli.out); err != nil { + pull := func(authConfig auth.AuthConfig) error { + buf, err := json.Marshal(authConfig) + if err != nil { + return err + } + registryAuthHeader := []string{ + base64.URLEncoding.EncodeToString(buf), + } + + return cli.stream("POST", "/images/create?"+v.Encode(), nil, cli.out, map[string][]string{ + "X-Registry-Auth": registryAuthHeader, + }) + } + + if err := pull(authConfig); err != nil { + if err.Error() == registry.ErrLoginRequired.Error() { + fmt.Fprintln(cli.out, "\nPlease login prior to push:") + if err := cli.CmdLogin(endpoint); err != nil { + return err + } + authConfig := cli.configFile.ResolveAuthConfig(endpoint) + return pull(authConfig) + } return err } @@ -988,6 +1007,19 @@ func (cli *DockerCli) CmdImages(args ...string) error { return nil } +func displayablePorts(ports []APIPort) string { + result := []string{} + for _, port := range ports { + if port.Type == "tcp" { + result = append(result, fmt.Sprintf("%d->%d", port.PublicPort, port.PrivatePort)) + } else { + result = append(result, fmt.Sprintf("%d->%d/%s", port.PublicPort, port.PrivatePort, port.Type)) + } + } + sort.Strings(result) + return strings.Join(result, ", ") +} + func (cli *DockerCli) CmdPs(args ...string) error { cmd := Subcmd("ps", "[OPTIONS]", "List containers") quiet := cmd.Bool("q", false, "Only display numeric IDs") @@ -1045,9 +1077,9 @@ func (cli *DockerCli) CmdPs(args ...string) error { for _, out := range outs { if !*quiet { if *noTrunc { - fmt.Fprintf(w, "%s\t%s\t%s\t%s ago\t%s\t%s\t", out.ID, out.Image, out.Command, utils.HumanDuration(time.Now().Sub(time.Unix(out.Created, 0))), out.Status, out.Ports) + fmt.Fprintf(w, "%s\t%s\t%s\t%s ago\t%s\t%s\t", out.ID, out.Image, out.Command, utils.HumanDuration(time.Now().Sub(time.Unix(out.Created, 0))), out.Status, displayablePorts(out.Ports)) } else { - fmt.Fprintf(w, "%s\t%s\t%s\t%s ago\t%s\t%s\t", utils.TruncateID(out.ID), out.Image, utils.Trunc(out.Command, 20), utils.HumanDuration(time.Now().Sub(time.Unix(out.Created, 0))), out.Status, out.Ports) + fmt.Fprintf(w, "%s\t%s\t%s\t%s ago\t%s\t%s\t", utils.TruncateID(out.ID), out.Image, utils.Trunc(out.Command, 20), utils.HumanDuration(time.Now().Sub(time.Unix(out.Created, 0))), out.Status, displayablePorts(out.Ports)) } if *size { if out.SizeRootFs > 0 { @@ -1132,7 +1164,7 @@ func (cli *DockerCli) CmdEvents(args ...string) error { v.Set("since", *since) } - if err := cli.stream("GET", "/events?"+v.Encode(), nil, cli.out); err != nil { + if err := cli.stream("GET", "/events?"+v.Encode(), nil, cli.out, nil); err != nil { return err } return nil @@ -1149,7 +1181,7 @@ func (cli *DockerCli) CmdExport(args ...string) error { return nil } - if err := cli.stream("GET", "/containers/"+cmd.Arg(0)+"/export", nil, cli.out); err != nil { + if err := cli.stream("GET", "/containers/"+cmd.Arg(0)+"/export", nil, cli.out, nil); err != nil { return err } return nil @@ -1415,13 +1447,36 @@ func (cli *DockerCli) CmdRun(args ...string) error { tag = DEFAULTTAG } - fmt.Printf("Unable to find image '%s' (tag: %s) locally\n", config.Image, tag) + fmt.Fprintf(cli.err, "Unable to find image '%s' (tag: %s) locally\n", config.Image, tag) v := url.Values{} repos, tag := utils.ParseRepositoryTag(config.Image) v.Set("fromImage", repos) v.Set("tag", tag) - err = cli.stream("POST", "/images/create?"+v.Encode(), nil, cli.err) + + // Resolve the Repository name from fqn to endpoint + name + var endpoint string + endpoint, _, err = registry.ResolveRepositoryName(repos) + if err != nil { + return err + } + + // Load the auth config file, to be able to pull the image + cli.LoadConfigFile() + + // Resolve the Auth config relevant for this server + authConfig := cli.configFile.ResolveAuthConfig(endpoint) + buf, err := json.Marshal(authConfig) + if err != nil { + return err + } + + registryAuthHeader := []string{ + base64.URLEncoding.EncodeToString(buf), + } + err = cli.stream("POST", "/images/create?"+v.Encode(), nil, cli.err, map[string][]string{ + "X-Registry-Auth": registryAuthHeader, + }) if err != nil { return err } @@ -1507,8 +1562,18 @@ 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 + } + if status != 0 { + return &utils.StatusError{status} + } } + return nil } @@ -1526,6 +1591,10 @@ func (cli *DockerCli) CmdCp(args ...string) error { var copyData APICopy info := strings.Split(cmd.Arg(0), ":") + if len(info) != 2 { + return fmt.Errorf("Error: Resource not specified") + } + copyData.Resource = info[1] copyData.HostPath = cmd.Arg(1) @@ -1594,7 +1663,7 @@ func (cli *DockerCli) call(method, path string, data interface{}) ([]byte, int, return body, resp.StatusCode, nil } -func (cli *DockerCli) stream(method, path string, in io.Reader, out io.Writer) error { +func (cli *DockerCli) stream(method, path string, in io.Reader, out io.Writer, headers map[string][]string) error { if (method == "POST" || method == "PUT") && in == nil { in = bytes.NewReader([]byte{}) } @@ -1607,6 +1676,13 @@ func (cli *DockerCli) stream(method, path string, in io.Reader, out io.Writer) e if method == "POST" { req.Header.Set("Content-Type", "plain/text") } + + if headers != nil { + for k, v := range headers { + req.Header[k] = v + } + } + dial, err := net.Dial(cli.proto, cli.addr) if err != nil { if strings.Contains(err.Error(), "connection refused") { @@ -1785,6 +1861,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 diff --git a/commands_test.go b/commands_test.go index 25e4804361..2946da8792 100644 --- a/commands_test.go +++ b/commands_test.go @@ -152,7 +152,6 @@ func TestRunWorkdirExists(t *testing.T) { } - func TestRunExit(t *testing.T) { stdin, stdinPipe := io.Pipe() stdout, stdoutPipe := io.Pipe() diff --git a/container.go b/container.go index 7de3539b2f..4ea44f2705 100644 --- a/container.go +++ b/container.go @@ -11,11 +11,11 @@ import ( "io" "io/ioutil" "log" + "net" "os" "os/exec" "path" "path/filepath" - "sort" "strconv" "strings" "syscall" @@ -41,6 +41,8 @@ type Container struct { SysInitPath string ResolvConfPath string + HostnamePath string + HostsPath string cmd *exec.Cmd stdout *utils.WriteBroadcaster @@ -60,6 +62,7 @@ type Container struct { type Config struct { Hostname string + Domainname string User string Memory int64 // Memory limit (in bytes) MemorySwap int64 // Total memory usage (memory + swap); set `-1' to disable swap @@ -106,7 +109,7 @@ type KeyValuePair struct { func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig, *flag.FlagSet, error) { cmd := Subcmd("run", "[OPTIONS] IMAGE [COMMAND] [ARG...]", "Run a command in a new container") - if len(args) > 0 && args[0] != "--help" { + if os.Getenv("TEST") != "" { cmd.SetOutput(ioutil.Discard) cmd.Usage = nil } @@ -202,8 +205,17 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig, return nil, nil, cmd, err } + hostname := *flHostname + domainname := "" + + parts := strings.SplitN(hostname, ".", 2) + if len(parts) > 1 { + hostname = parts[0] + domainname = parts[1] + } config := &Config{ - Hostname: *flHostname, + Hostname: hostname, + Domainname: domainname, PortSpecs: flPorts, User: *flUser, Tty: *flTty, @@ -252,17 +264,28 @@ type NetworkSettings struct { PortMapping map[string]PortMapping } -// String returns a human-readable description of the port mapping defined in the settings -func (settings *NetworkSettings) PortMappingHuman() string { - var mapping []string +// returns a more easy to process description of the port mapping defined in the settings +func (settings *NetworkSettings) PortMappingAPI() []APIPort { + var mapping []APIPort for private, public := range settings.PortMapping["Tcp"] { - mapping = append(mapping, fmt.Sprintf("%s->%s", public, private)) + pubint, _ := strconv.ParseInt(public, 0, 0) + privint, _ := strconv.ParseInt(private, 0, 0) + mapping = append(mapping, APIPort{ + PrivatePort: privint, + PublicPort: pubint, + Type: "tcp", + }) } for private, public := range settings.PortMapping["Udp"] { - mapping = append(mapping, fmt.Sprintf("%s->%s/udp", public, private)) + pubint, _ := strconv.ParseInt(public, 0, 0) + privint, _ := strconv.ParseInt(private, 0, 0) + mapping = append(mapping, APIPort{ + PrivatePort: privint, + PublicPort: pubint, + Type: "udp", + }) } - sort.Strings(mapping) - return strings.Join(mapping, ", ") + return mapping } // Inject the io.Reader at the given path. Note: do not close the reader @@ -541,7 +564,7 @@ func (container *Container) Start(hostConfig *HostConfig) error { container.State.Lock() defer container.State.Unlock() - if len(hostConfig.Binds) == 0 && len(hostConfig.LxcConf) == 0 { + if hostConfig == nil { // in docker start of docker restart we want to reuse previous HostConfigFile hostConfig, _ = container.ReadHostConfig() } @@ -625,7 +648,7 @@ func (container *Container) Start(hostConfig *HostConfig) error { continue } if err := os.MkdirAll(path.Join(container.RootfsPath(), volPath), 0755); err != nil { - return nil + return err } container.Volumes[volPath] = id if isRW, exists := c.VolumesRW[volPath]; exists { @@ -641,11 +664,13 @@ func (container *Container) Start(hostConfig *HostConfig) error { if _, exists := container.Volumes[volPath]; exists { continue } + var srcPath string + srcRW := false // If an external bind is defined for this volume, use that as a source if bindMap, exists := binds[volPath]; exists { - container.Volumes[volPath] = bindMap.SrcPath + srcPath = bindMap.SrcPath if strings.ToLower(bindMap.Mode) == "rw" { - container.VolumesRW[volPath] = true + srcRW = true } // Otherwise create an directory in $ROOT/volumes/ and use that } else { @@ -653,17 +678,49 @@ func (container *Container) Start(hostConfig *HostConfig) error { if err != nil { return err } - srcPath, err := c.layer() + srcPath, err = c.layer() if err != nil { return err } - container.Volumes[volPath] = srcPath - container.VolumesRW[volPath] = true // RW by default + srcRW = true // RW by default } + container.Volumes[volPath] = srcPath + container.VolumesRW[volPath] = srcRW // Create the mountpoint - if err := os.MkdirAll(path.Join(container.RootfsPath(), volPath), 0755); err != nil { + rootVolPath := path.Join(container.RootfsPath(), volPath) + if err := os.MkdirAll(rootVolPath, 0755); err != nil { return nil } + if srcRW { + volList, err := ioutil.ReadDir(rootVolPath) + if err != nil { + return err + } + if len(volList) > 0 { + srcList, err := ioutil.ReadDir(srcPath) + if err != nil { + return err + } + if len(srcList) == 0 { + if err := CopyWithTar(rootVolPath, srcPath); err != nil { + return err + } + } + } + var stat syscall.Stat_t + if err := syscall.Stat(rootVolPath, &stat); err != nil { + return err + } + var srcStat syscall.Stat_t + if err := syscall.Stat(srcPath, &srcStat); err != nil { + return err + } + if stat.Uid != srcStat.Uid || stat.Gid != srcStat.Gid { + if err := os.Chown(srcPath, int(stat.Uid), int(stat.Gid)); err != nil { + return err + } + } + } } if err := container.generateLXCConfig(hostConfig); err != nil { @@ -799,14 +856,44 @@ func (container *Container) allocateNetwork() error { return nil } - iface, err := container.runtime.networkManager.Allocate() - if err != nil { - return err + var iface *NetworkInterface + var err error + if !container.State.Ghost { + iface, err = container.runtime.networkManager.Allocate() + if err != nil { + return err + } + } else { + manager := container.runtime.networkManager + if manager.disabled { + iface = &NetworkInterface{disabled: true} + } else { + iface = &NetworkInterface{ + IPNet: net.IPNet{IP: net.ParseIP(container.NetworkSettings.IPAddress), Mask: manager.bridgeNetwork.Mask}, + Gateway: manager.bridgeNetwork.IP, + manager: manager, + } + ipNum := ipToInt(iface.IPNet.IP) + manager.ipAllocator.inUse[ipNum] = struct{}{} + } } + + var portSpecs []string + if !container.State.Ghost { + portSpecs = container.Config.PortSpecs + } else { + for backend, frontend := range container.NetworkSettings.PortMapping["Tcp"] { + portSpecs = append(portSpecs, fmt.Sprintf("%s:%s/tcp", frontend, backend)) + } + for backend, frontend := range container.NetworkSettings.PortMapping["Udp"] { + portSpecs = append(portSpecs, fmt.Sprintf("%s:%s/udp", frontend, backend)) + } + } + container.NetworkSettings.PortMapping = make(map[string]PortMapping) container.NetworkSettings.PortMapping["Tcp"] = make(PortMapping) container.NetworkSettings.PortMapping["Udp"] = make(PortMapping) - for _, spec := range container.Config.PortSpecs { + for _, spec := range portSpecs { nat, err := iface.AllocatePort(spec) if err != nil { iface.Release() diff --git a/container_test.go b/container_test.go index ba48ceb47a..41940242f9 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"}, @@ -138,12 +138,21 @@ func TestDiff(t *testing.T) { container1, _, _ := mkContainer(runtime, []string{"_", "/bin/rm", "/etc/passwd"}, t) defer runtime.Destroy(container1) + // The changelog should be empty and not fail before run. See #1705 + c, err := container1.Changes() + if err != nil { + t.Fatal(err) + } + if len(c) != 0 { + t.Fatalf("Changelog should be empty before run") + } + if err := container1.Run(); err != nil { t.Fatal(err) } // Check the changelog - c, err := container1.Changes() + c, err = container1.Changes() if err != nil { t.Fatal(err) } @@ -379,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"}, @@ -402,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, @@ -462,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) } @@ -477,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"}, }, @@ -521,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", ""}, }) @@ -538,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", ""}, }) @@ -557,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"}, }, @@ -587,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"}, @@ -664,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"}, }, @@ -685,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"}, @@ -705,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"}, @@ -725,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"}, @@ -747,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"}, @@ -767,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"}, @@ -788,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"}, }, @@ -800,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"}, }, @@ -844,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"}, @@ -889,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"}, @@ -934,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"}, }, @@ -983,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"}, @@ -1006,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"}, @@ -1057,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"}, @@ -1081,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"}, @@ -1112,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"}, }, @@ -1144,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"}, }, @@ -1193,6 +1196,105 @@ func tempDir(t *testing.T) string { return tmpDir } +// Test for #1737 +func TestCopyVolumeUidGid(t *testing.T) { + r := mkRuntime(t) + defer nuke(r) + + // Add directory not owned by root + container1, _, _ := mkContainer(r, []string{"_", "/bin/sh", "-c", "mkdir -p /hello && chown daemon.daemon /hello"}, t) + defer r.Destroy(container1) + + if container1.State.Running { + t.Errorf("Container shouldn't be running") + } + if err := container1.Run(); err != nil { + t.Fatal(err) + } + if container1.State.Running { + t.Errorf("Container shouldn't be running") + } + + rwTar, err := container1.ExportRw() + if err != nil { + t.Error(err) + } + img, err := r.graph.Create(rwTar, container1, "unit test commited image", "", nil) + if err != nil { + t.Error(err) + } + + // Test that the uid and gid is copied from the image to the volume + tmpDir1 := tempDir(t) + defer os.RemoveAll(tmpDir1) + stdout1, _ := runContainer(r, []string{"-v", fmt.Sprintf("%s:/hello", tmpDir1), img.ID, "stat", "-c", "%U %G", "/hello"}, t) + if !strings.Contains(stdout1, "daemon daemon") { + t.Fatal("Container failed to transfer uid and gid to volume") + } + + // Test that the uid and gid is not copied from the image when the volume is read only + tmpDir2 := tempDir(t) + defer os.RemoveAll(tmpDir1) + stdout2, _ := runContainer(r, []string{"-v", fmt.Sprintf("%s:/hello:ro", tmpDir2), img.ID, "stat", "-c", "%U %G", "/hello"}, t) + if strings.Contains(stdout2, "daemon daemon") { + t.Fatal("Container transfered uid and gid to volume") + } +} + +// Test for #1582 +func TestCopyVolumeContent(t *testing.T) { + r := mkRuntime(t) + defer nuke(r) + + // Put some content in a directory of a container and commit it + container1, _, _ := mkContainer(r, []string{"_", "/bin/sh", "-c", "mkdir -p /hello/local && echo hello > /hello/local/world"}, t) + defer r.Destroy(container1) + + if container1.State.Running { + t.Errorf("Container shouldn't be running") + } + if err := container1.Run(); err != nil { + t.Fatal(err) + } + if container1.State.Running { + t.Errorf("Container shouldn't be running") + } + + rwTar, err := container1.ExportRw() + if err != nil { + t.Error(err) + } + img, err := r.graph.Create(rwTar, container1, "unit test commited image", "", nil) + if err != nil { + t.Error(err) + } + + // Test that the content is copied from the image to the volume + tmpDir1 := tempDir(t) + defer os.RemoveAll(tmpDir1) + stdout1, _ := runContainer(r, []string{"-v", fmt.Sprintf("%s:/hello", tmpDir1), img.ID, "find", "/hello"}, t) + if !(strings.Contains(stdout1, "/hello/local/world") && strings.Contains(stdout1, "/hello/local")) { + t.Fatal("Container failed to transfer content to volume") + } + + // Test that the content is not copied when the volume is readonly + tmpDir2 := tempDir(t) + defer os.RemoveAll(tmpDir2) + stdout2, _ := runContainer(r, []string{"-v", fmt.Sprintf("%s:/hello:ro", tmpDir2), img.ID, "find", "/hello"}, t) + if strings.Contains(stdout2, "/hello/local/world") || strings.Contains(stdout2, "/hello/local") { + t.Fatal("Container transfered content to readonly volume") + } + + // Test that the content is not copied when the volume is non-empty + tmpDir3 := tempDir(t) + defer os.RemoveAll(tmpDir3) + writeFile(path.Join(tmpDir3, "touch-me"), "", t) + stdout3, _ := runContainer(r, []string{"-v", fmt.Sprintf("%s:/hello:rw", tmpDir3), img.ID, "find", "/hello"}, t) + if strings.Contains(stdout3, "/hello/local/world") || strings.Contains(stdout3, "/hello/local") || !strings.Contains(stdout3, "/hello/touch-me") { + t.Fatal("Container transfered content to non-empty volume") + } +} + func TestBindMounts(t *testing.T) { r := mkRuntime(t) defer nuke(r) @@ -1220,7 +1322,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"}, @@ -1239,7 +1341,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"}, @@ -1275,7 +1377,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": {}}, @@ -1318,7 +1420,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": {}}, @@ -1345,7 +1447,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"}, @@ -1386,7 +1488,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/contrib/MAINTAINERS b/contrib/MAINTAINERS index 0b7931f907..2531745dc6 100644 --- a/contrib/MAINTAINERS +++ b/contrib/MAINTAINERS @@ -1 +1 @@ -# Maintainer wanted! Enroll on #docker@freenode +Kawsar Saiyeed diff --git a/contrib/docker.bash b/contrib/docker.bash index 6719ef6a92..aa9fb3d7b5 100644 --- a/contrib/docker.bash +++ b/contrib/docker.bash @@ -21,7 +21,6 @@ # If the docker daemon is using a unix socket for communication your user # must have access to the socket for the completions to function correctly -have docker && { __docker_containers_all() { local containers @@ -115,7 +114,7 @@ _docker_build() case "$cur" in -*) - COMPREPLY=( $( compgen -W "-t -q" -- "$cur" ) ) + COMPREPLY=( $( compgen -W "-no-cache -t -q -rm" -- "$cur" ) ) ;; *) _filedir @@ -138,11 +137,37 @@ _docker_commit() COMPREPLY=( $( compgen -W "-author -m -run" -- "$cur" ) ) ;; *) - __docker_containers_all + local counter=$cpos + while [ $counter -le $cword ]; do + case "${words[$counter]}" in + -author|-m|-run) + (( counter++ )) + ;; + -*) + ;; + *) + break + ;; + esac + (( counter++ )) + done + + if [ $counter -eq $cword ]; then + __docker_containers_all + fi ;; esac } +_docker_cp() +{ + if [ $cpos -eq $cword ]; then + __docker_containers_all + else + _filedir + fi +} + _docker_diff() { if [ $cpos -eq $cword ]; then @@ -152,7 +177,21 @@ _docker_diff() _docker_events() { - COMPREPLY=( $( compgen -W "-since" -- "$cur" ) ) + case "$prev" in + -since) + return + ;; + *) + ;; + esac + + case "$cur" in + -*) + COMPREPLY=( $( compgen -W "-since" -- "$cur" ) ) + ;; + *) + ;; + esac } _docker_export() @@ -231,7 +270,21 @@ _docker_kill() _docker_login() { - COMPREPLY=( $( compgen -W "-e -p -u" -- "$cur" ) ) + case "$prev" in + -e|-p|-u) + return + ;; + *) + ;; + esac + + case "$cur" in + -*) + COMPREPLY=( $( compgen -W "-e -p -u" -- "$cur" ) ) + ;; + *) + ;; + esac } _docker_logs() @@ -250,12 +303,40 @@ _docker_port() _docker_ps() { - COMPREPLY=( $( compgen -W "-a -beforeId -l -n -notrunc -q -s -sinceId" -- "$cur" ) ) + case "$prev" in + -beforeId|-n|-sinceId) + return + ;; + *) + ;; + esac + + case "$cur" in + -*) + COMPREPLY=( $( compgen -W "-a -beforeId -l -n -notrunc -q -s -sinceId" -- "$cur" ) ) + ;; + *) + ;; + esac } _docker_pull() { - COMPREPLY=( $( compgen -W "-t" -- "$cur" ) ) + case "$prev" in + -t) + return + ;; + *) + ;; + esac + + case "$cur" in + -*) + COMPREPLY=( $( compgen -W "-t" -- "$cur" ) ) + ;; + *) + ;; + esac } _docker_push() @@ -309,7 +390,7 @@ _docker_run() -volumes-from) __docker_containers_all ;; - -a|-c|-dns|-e|-entrypoint|-h|-m|-p|-u|-v) + -a|-c|-dns|-e|-entrypoint|-h|-lxc-conf|-m|-p|-u|-v|-w) return ;; *) @@ -318,34 +399,27 @@ _docker_run() case "$cur" in -*) - COMPREPLY=( $( compgen -W "-a -c -cidfile -d -dns -e -entrypoint -h -i -m -n -p -t -u -v -volumes-from" -- "$cur" ) ) + COMPREPLY=( $( compgen -W "-a -c -cidfile -d -dns -e -entrypoint -h -i -lxc-conf -m -n -p -privileged -t -u -v -volumes-from -w" -- "$cur" ) ) ;; *) - case "$cur" in - -*) - COMPREPLY=( $( compgen -W "-a -notrunc -q -viz" -- "$cur" ) ) - ;; - *) - local counter=$cpos - while [ $counter -le $cword ]; do - case "${words[$counter]}" in - -a|-c|-cidfile|-dns|-e|-entrypoint|-h|-m|-p|-u|-v|-volumes-from) - (( counter++ )) - ;; - -*) - ;; - *) - break - ;; - esac + local counter=$cpos + while [ $counter -le $cword ]; do + case "${words[$counter]}" in + -a|-c|-cidfile|-dns|-e|-entrypoint|-h|-lxc-conf|-m|-p|-u|-v|-volumes-from|-w) (( counter++ )) - done + ;; + -*) + ;; + *) + break + ;; + esac + (( counter++ )) + done - if [ $counter -eq $cword ]; then - __docker_image_repos_and_tags - fi - ;; - esac + if [ $counter -eq $cword ]; then + __docker_image_repos_and_tags + fi ;; esac } @@ -409,6 +483,7 @@ _docker() attach build commit + cp diff events export @@ -466,4 +541,3 @@ _docker() } complete -F _docker docker -} \ No newline at end of file diff --git a/contrib/install.sh b/contrib/install.sh index 3cf7169a07..40e3aaafb4 100755 --- a/contrib/install.sh +++ b/contrib/install.sh @@ -47,7 +47,7 @@ else echo "Creating /etc/init/dockerd.conf..." cat >/etc/init/dockerd.conf </insert is the same as calling -/v1.4/images//insert +/v1.5/images//insert You can still call an old version of the api using /v1.0/images//insert +:doc:`docker_remote_api_v1.5` +***************************** + +What's new +---------- + +.. http:post:: /images/create + + **New!** You can now pass registry credentials (via an AuthConfig object) + through the `X-Registry-Auth` header + +.. http:post:: /images/(name)/push + + **New!** The AuthConfig object now needs to be passed through + the `X-Registry-Auth` header + +.. http:get:: /containers/json + + **New!** The format of the `Ports` entry has been changed to a list of + dicts each containing `PublicPort`, `PrivatePort` and `Type` describing a + port mapping. + :doc:`docker_remote_api_v1.4` ***************************** @@ -165,7 +187,7 @@ Initial version Docker Remote API Client Libraries ================================== -These libraries have been not tested by the Docker Maintainers for +These libraries have not been tested by the Docker Maintainers for compatibility. Please file issues with the library owners. If you find more library implementations, please list them in Docker doc bugs and we will add the libraries here. @@ -175,12 +197,13 @@ 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 | +----------------------+----------------+--------------------------------------------+ +| Javascript (NodeJS) | docker.io | https://github.com/appersonlabs/docker.io | +| | | Install via NPM: `npm install docker.io` | ++----------------------+----------------+--------------------------------------------+ | Javascript | docker-js | https://github.com/dgoujard/docker-js | +----------------------+----------------+--------------------------------------------+ | Javascript (Angular) | dockerui | https://github.com/crosbymichael/dockerui | @@ -188,4 +211,7 @@ and we will add the libraries here. +----------------------+----------------+--------------------------------------------+ | Java | docker-java | https://github.com/kpelykh/docker-java | +----------------------+----------------+--------------------------------------------+ - +| Erlang | erldocker | https://github.com/proger/erldocker | ++----------------------+----------------+--------------------------------------------+ +| Go | go-dockerclient| https://github.com/fsouza/go-dockerclient | ++----------------------+----------------+--------------------------------------------+ diff --git a/docs/sources/api/docker_remote_api_v1.4.rst b/docs/sources/api/docker_remote_api_v1.4.rst index d512de9ca3..b3628f0252 100644 --- a/docs/sources/api/docker_remote_api_v1.4.rst +++ b/docs/sources/api/docker_remote_api_v1.4.rst @@ -119,6 +119,7 @@ Create a container "AttachStdout":true, "AttachStderr":true, "PortSpecs":null, + "Privileged": false, "Tty":false, "OpenStdin":false, "StdinOnce":false, @@ -223,6 +224,7 @@ Inspect a container :statuscode 200: no error :statuscode 404: no such container + :statuscode 409: conflict between containers and images :statuscode 500: server error @@ -357,7 +359,7 @@ Start a container { "Binds":["/tmp:/tmp"], - "LxcConf":{"lxc.utsname":"docker"} + "LxcConf":[{"Key":"lxc.utsname","Value":"docker"}] } **Example response**: @@ -368,7 +370,7 @@ Start a container Content-Type: text/plain :jsonparam hostConfig: the container's host configuration (optional) - :statuscode 200: no error + :statuscode 204: no error :statuscode 404: no such container :statuscode 500: server error @@ -679,8 +681,8 @@ Create an image :statuscode 500: server error -Insert a file in a image -************************ +Insert a file in an image +************************* .. http:post:: /images/(name)/insert @@ -759,7 +761,8 @@ Inspect an image :statuscode 200: no error :statuscode 404: no such image - :statuscode 500: server error + :statuscode 409: conflict between containers and images + :statuscode 500: server error Get the history of an image @@ -990,7 +993,8 @@ Check auth configuration { "username":"hannibal", "password:"xxxx", - "email":"hannibal@a-team.com" + "email":"hannibal@a-team.com", + "serveraddress":"https://index.docker.io/v1/" } **Example response**: @@ -1082,7 +1086,7 @@ Create a new image from a container's changes POST /commit?container=44c004db4b17&m=message&repo=myrepo HTTP/1.1 - **Example response**: + **Example response**: .. sourcecode:: http @@ -1091,15 +1095,15 @@ Create a new image from a container's changes {"Id":"596069db4bf5"} - :query container: source container - :query repo: repository - :query tag: tag - :query m: commit message - :query author: author (eg. "John Hannibal Smith ") - :query run: config automatically applied when the image is run. (ex: {"Cmd": ["cat", "/world"], "PortSpecs":["22"]}) - :statuscode 201: no error - :statuscode 404: no such container - :statuscode 500: server error + :query container: source container + :query repo: repository + :query tag: tag + :query m: commit message + :query author: author (eg. "John Hannibal Smith ") + :query run: config automatically applied when the image is run. (ex: {"Cmd": ["cat", "/world"], "PortSpecs":["22"]}) + :statuscode 201: no error + :statuscode 404: no such container + :statuscode 500: server error Monitor Docker's events diff --git a/docs/sources/api/docker_remote_api_v1.5.rst b/docs/sources/api/docker_remote_api_v1.5.rst new file mode 100644 index 0000000000..522885ff86 --- /dev/null +++ b/docs/sources/api/docker_remote_api_v1.5.rst @@ -0,0 +1,1176 @@ +:title: Remote API v1.5 +:description: API Documentation for Docker +:keywords: API, Docker, rcli, REST, documentation + +:orphan: + +====================== +Docker Remote API v1.5 +====================== + +.. contents:: Table of Contents + +1. Brief introduction +===================== + +- The Remote API is replacing rcli +- Default port in the docker daemon is 4243 +- 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 +============ + +2.1 Containers +-------------- + +List containers +*************** + +.. http:get:: /containers/json + + List containers + + **Example request**: + + .. sourcecode:: http + + GET /containers/json?all=1&before=8dfafdbc3a40&size=1 HTTP/1.1 + + **Example response**: + + .. sourcecode:: http + + HTTP/1.1 200 OK + Content-Type: application/json + + [ + { + "Id": "8dfafdbc3a40", + "Image": "base:latest", + "Command": "echo 1", + "Created": 1367854155, + "Status": "Exit 0", + "Ports":[{"PrivatePort": 2222, "PublicPort": 3333, "Type": "tcp"}], + "SizeRw":12288, + "SizeRootFs":0 + }, + { + "Id": "9cd87474be90", + "Image": "base:latest", + "Command": "echo 222222", + "Created": 1367854155, + "Status": "Exit 0", + "Ports":[], + "SizeRw":12288, + "SizeRootFs":0 + }, + { + "Id": "3176a2479c92", + "Image": "base:latest", + "Command": "echo 3333333333333333", + "Created": 1367854154, + "Status": "Exit 0", + "Ports":[], + "SizeRw":12288, + "SizeRootFs":0 + }, + { + "Id": "4cb07b47f9fb", + "Image": "base:latest", + "Command": "echo 444444444444444444444444444444444", + "Created": 1367854152, + "Status": "Exit 0", + "Ports":[], + "SizeRw":12288, + "SizeRootFs":0 + } + ] + + :query all: 1/True/true or 0/False/false, Show all containers. Only running containers are shown by default + :query limit: Show ``limit`` last created containers, include non-running ones. + :query since: Show only containers created since Id, include non-running ones. + :query before: Show only containers created before Id, include non-running ones. + :query size: 1/True/true or 0/False/false, Show the containers sizes + :statuscode 200: no error + :statuscode 400: bad parameter + :statuscode 500: server error + + +Create a container +****************** + +.. http:post:: /containers/create + + Create a container + + **Example request**: + + .. sourcecode:: http + + POST /containers/create HTTP/1.1 + Content-Type: application/json + + { + "Hostname":"", + "User":"", + "Memory":0, + "MemorySwap":0, + "AttachStdin":false, + "AttachStdout":true, + "AttachStderr":true, + "PortSpecs":null, + "Privileged": false, + "Tty":false, + "OpenStdin":false, + "StdinOnce":false, + "Env":null, + "Cmd":[ + "date" + ], + "Dns":null, + "Image":"base", + "Volumes":{}, + "VolumesFrom":"", + "WorkingDir":"" + + } + + **Example response**: + + .. sourcecode:: http + + HTTP/1.1 201 OK + Content-Type: application/json + + { + "Id":"e90e34656806" + "Warnings":[] + } + + :jsonparam config: the container's configuration + :statuscode 201: no error + :statuscode 404: no such container + :statuscode 406: impossible to attach (container not running) + :statuscode 500: server error + + +Inspect a container +******************* + +.. http:get:: /containers/(id)/json + + Return low-level information on the container ``id`` + + **Example request**: + + .. sourcecode:: http + + GET /containers/4fa6e0f0c678/json HTTP/1.1 + + **Example response**: + + .. sourcecode:: http + + HTTP/1.1 200 OK + Content-Type: application/json + + { + "Id": "4fa6e0f0c6786287e131c3852c58a2e01cc697a68231826813597e4994f1d6e2", + "Created": "2013-05-07T14:51:42.041847+02:00", + "Path": "date", + "Args": [], + "Config": { + "Hostname": "4fa6e0f0c678", + "User": "", + "Memory": 0, + "MemorySwap": 0, + "AttachStdin": false, + "AttachStdout": true, + "AttachStderr": true, + "PortSpecs": null, + "Tty": false, + "OpenStdin": false, + "StdinOnce": false, + "Env": null, + "Cmd": [ + "date" + ], + "Dns": null, + "Image": "base", + "Volumes": {}, + "VolumesFrom": "", + "WorkingDir":"" + + }, + "State": { + "Running": false, + "Pid": 0, + "ExitCode": 0, + "StartedAt": "2013-05-07T14:51:42.087658+02:01360", + "Ghost": false + }, + "Image": "b750fe79269d2ec9a3c593ef05b4332b1d1a02a62b4accb2c21d589ff2f5f2dc", + "NetworkSettings": { + "IpAddress": "", + "IpPrefixLen": 0, + "Gateway": "", + "Bridge": "", + "PortMapping": null + }, + "SysInitPath": "/home/kitty/go/src/github.com/dotcloud/docker/bin/docker", + "ResolvConfPath": "/etc/resolv.conf", + "Volumes": {} + } + + :statuscode 200: no error + :statuscode 404: no such container + :statuscode 500: server error + + +List processes running inside a container +***************************************** + +.. http:get:: /containers/(id)/top + + List processes running inside the container ``id`` + + **Example request**: + + .. sourcecode:: http + + GET /containers/4fa6e0f0c678/top HTTP/1.1 + + **Example response**: + + .. sourcecode:: http + + HTTP/1.1 200 OK + Content-Type: application/json + + { + "Titles":[ + "USER", + "PID", + "%CPU", + "%MEM", + "VSZ", + "RSS", + "TTY", + "STAT", + "START", + "TIME", + "COMMAND" + ], + "Processes":[ + ["root","20147","0.0","0.1","18060","1864","pts/4","S","10:06","0:00","bash"], + ["root","20271","0.0","0.0","4312","352","pts/4","S+","10:07","0:00","sleep","10"] + ] + } + + :query ps_args: ps arguments to use (eg. aux) + :statuscode 200: no error + :statuscode 404: no such container + :statuscode 500: server error + + +Inspect changes on a container's filesystem +******************************************* + +.. http:get:: /containers/(id)/changes + + Inspect changes on container ``id`` 's filesystem + + **Example request**: + + .. sourcecode:: http + + GET /containers/4fa6e0f0c678/changes HTTP/1.1 + + + **Example response**: + + .. sourcecode:: http + + HTTP/1.1 200 OK + Content-Type: application/json + + [ + { + "Path":"/dev", + "Kind":0 + }, + { + "Path":"/dev/kmsg", + "Kind":1 + }, + { + "Path":"/test", + "Kind":1 + } + ] + + :statuscode 200: no error + :statuscode 404: no such container + :statuscode 500: server error + + +Export a container +****************** + +.. http:get:: /containers/(id)/export + + Export the contents of container ``id`` + + **Example request**: + + .. sourcecode:: http + + GET /containers/4fa6e0f0c678/export HTTP/1.1 + + + **Example response**: + + .. sourcecode:: http + + HTTP/1.1 200 OK + Content-Type: application/octet-stream + + {{ STREAM }} + + :statuscode 200: no error + :statuscode 404: no such container + :statuscode 500: server error + + +Start a container +***************** + +.. http:post:: /containers/(id)/start + + Start the container ``id`` + + **Example request**: + + .. sourcecode:: http + + POST /containers/(id)/start HTTP/1.1 + Content-Type: application/json + + { + "Binds":["/tmp:/tmp"], + "LxcConf":[{"Key":"lxc.utsname","Value":"docker"}] + } + + **Example response**: + + .. sourcecode:: http + + HTTP/1.1 204 No Content + Content-Type: text/plain + + :jsonparam hostConfig: the container's host configuration (optional) + :statuscode 204: no error + :statuscode 404: no such container + :statuscode 500: server error + + +Stop a container +**************** + +.. http:post:: /containers/(id)/stop + + Stop the container ``id`` + + **Example request**: + + .. sourcecode:: http + + POST /containers/e90e34656806/stop?t=5 HTTP/1.1 + + **Example response**: + + .. sourcecode:: http + + HTTP/1.1 204 OK + + :query t: number of seconds to wait before killing the container + :statuscode 204: no error + :statuscode 404: no such container + :statuscode 500: server error + + +Restart a container +******************* + +.. http:post:: /containers/(id)/restart + + Restart the container ``id`` + + **Example request**: + + .. sourcecode:: http + + POST /containers/e90e34656806/restart?t=5 HTTP/1.1 + + **Example response**: + + .. sourcecode:: http + + HTTP/1.1 204 OK + + :query t: number of seconds to wait before killing the container + :statuscode 204: no error + :statuscode 404: no such container + :statuscode 500: server error + + +Kill a container +**************** + +.. http:post:: /containers/(id)/kill + + Kill the container ``id`` + + **Example request**: + + .. sourcecode:: http + + POST /containers/e90e34656806/kill HTTP/1.1 + + **Example response**: + + .. sourcecode:: http + + HTTP/1.1 204 OK + + :statuscode 204: no error + :statuscode 404: no such container + :statuscode 500: server error + + +Attach to a container +********************* + +.. http:post:: /containers/(id)/attach + + Attach to the container ``id`` + + **Example request**: + + .. sourcecode:: http + + POST /containers/16253994b7c4/attach?logs=1&stream=0&stdout=1 HTTP/1.1 + + **Example response**: + + .. sourcecode:: http + + HTTP/1.1 200 OK + Content-Type: application/vnd.docker.raw-stream + + {{ STREAM }} + + :query logs: 1/True/true or 0/False/false, return logs. Default false + :query stream: 1/True/true or 0/False/false, return stream. Default false + :query stdin: 1/True/true or 0/False/false, if stream=true, attach to stdin. Default false + :query stdout: 1/True/true or 0/False/false, if logs=true, return stdout log, if stream=true, attach to stdout. Default false + :query stderr: 1/True/true or 0/False/false, if logs=true, return stderr log, if stream=true, attach to stderr. Default false + :statuscode 200: no error + :statuscode 400: bad parameter + :statuscode 404: no such container + :statuscode 500: server error + + +Wait a container +**************** + +.. http:post:: /containers/(id)/wait + + Block until container ``id`` stops, then returns the exit code + + **Example request**: + + .. sourcecode:: http + + POST /containers/16253994b7c4/wait HTTP/1.1 + + **Example response**: + + .. sourcecode:: http + + HTTP/1.1 200 OK + Content-Type: application/json + + {"StatusCode":0} + + :statuscode 200: no error + :statuscode 404: no such container + :statuscode 500: server error + + +Remove a container +******************* + +.. http:delete:: /containers/(id) + + Remove the container ``id`` from the filesystem + + **Example request**: + + .. sourcecode:: http + + DELETE /containers/16253994b7c4?v=1 HTTP/1.1 + + **Example response**: + + .. sourcecode:: http + + HTTP/1.1 204 OK + + :query v: 1/True/true or 0/False/false, Remove the volumes associated to the container. Default false + :statuscode 204: no error + :statuscode 400: bad parameter + :statuscode 404: no such container + :statuscode 500: server error + + +Copy files or folders from a container +************************************** + +.. http:post:: /containers/(id)/copy + + Copy files or folders of container ``id`` + + **Example request**: + + .. sourcecode:: http + + POST /containers/4fa6e0f0c678/copy HTTP/1.1 + Content-Type: application/json + + { + "Resource":"test.txt" + } + + **Example response**: + + .. sourcecode:: http + + HTTP/1.1 200 OK + Content-Type: application/octet-stream + + {{ STREAM }} + + :statuscode 200: no error + :statuscode 404: no such container + :statuscode 500: server error + + +2.2 Images +---------- + +List Images +*********** + +.. http:get:: /images/(format) + + List images ``format`` could be json or viz (json default) + + **Example request**: + + .. sourcecode:: http + + GET /images/json?all=0 HTTP/1.1 + + **Example response**: + + .. sourcecode:: http + + HTTP/1.1 200 OK + Content-Type: application/json + + [ + { + "Repository":"base", + "Tag":"ubuntu-12.10", + "Id":"b750fe79269d", + "Created":1364102658, + "Size":24653, + "VirtualSize":180116135 + }, + { + "Repository":"base", + "Tag":"ubuntu-quantal", + "Id":"b750fe79269d", + "Created":1364102658, + "Size":24653, + "VirtualSize":180116135 + } + ] + + + **Example request**: + + .. sourcecode:: http + + GET /images/viz HTTP/1.1 + + **Example response**: + + .. sourcecode:: http + + HTTP/1.1 200 OK + Content-Type: text/plain + + digraph docker { + "d82cbacda43a" -> "074be284591f" + "1496068ca813" -> "08306dc45919" + "08306dc45919" -> "0e7893146ac2" + "b750fe79269d" -> "1496068ca813" + base -> "27cf78414709" [style=invis] + "f71189fff3de" -> "9a33b36209ed" + "27cf78414709" -> "b750fe79269d" + "0e7893146ac2" -> "d6434d954665" + "d6434d954665" -> "d82cbacda43a" + base -> "e9aa60c60128" [style=invis] + "074be284591f" -> "f71189fff3de" + "b750fe79269d" [label="b750fe79269d\nbase",shape=box,fillcolor="paleturquoise",style="filled,rounded"]; + "e9aa60c60128" [label="e9aa60c60128\nbase2",shape=box,fillcolor="paleturquoise",style="filled,rounded"]; + "9a33b36209ed" [label="9a33b36209ed\ntest",shape=box,fillcolor="paleturquoise",style="filled,rounded"]; + base [style=invisible] + } + + :query all: 1/True/true or 0/False/false, Show all containers. Only running containers are shown by default + :statuscode 200: no error + :statuscode 400: bad parameter + :statuscode 500: server error + + +Create an image +*************** + +.. http:post:: /images/create + + Create an image, either by pull it from the registry or by importing it + + **Example request**: + + .. sourcecode:: http + + POST /images/create?fromImage=base HTTP/1.1 + + **Example response**: + + .. sourcecode:: http + + HTTP/1.1 200 OK + Content-Type: application/json + + {"status":"Pulling..."} + {"status":"Pulling", "progress":"1/? (n/a)"} + {"error":"Invalid..."} + ... + + When using this endpoint to pull an image from the registry, + the ``X-Registry-Auth`` header can be used to include a + base64-encoded AuthConfig object. + + :query fromImage: name of the image to pull + :query fromSrc: source to import, - means stdin + :query repo: repository + :query tag: tag + :query registry: the registry to pull from + :statuscode 200: no error + :statuscode 500: server error + + +Insert a file in an image +************************* + +.. http:post:: /images/(name)/insert + + Insert a file from ``url`` in the image ``name`` at ``path`` + + **Example request**: + + .. sourcecode:: http + + POST /images/test/insert?path=/usr&url=myurl HTTP/1.1 + + **Example response**: + + .. sourcecode:: http + + HTTP/1.1 200 OK + Content-Type: application/json + + {"status":"Inserting..."} + {"status":"Inserting", "progress":"1/? (n/a)"} + {"error":"Invalid..."} + ... + + :statuscode 200: no error + :statuscode 500: server error + + +Inspect an image +**************** + +.. http:get:: /images/(name)/json + + Return low-level information on the image ``name`` + + **Example request**: + + .. sourcecode:: http + + GET /images/base/json HTTP/1.1 + + **Example response**: + + .. sourcecode:: http + + HTTP/1.1 200 OK + Content-Type: application/json + + { + "id":"b750fe79269d2ec9a3c593ef05b4332b1d1a02a62b4accb2c21d589ff2f5f2dc", + "parent":"27cf784147099545", + "created":"2013-03-23T22:24:18.818426-07:00", + "container":"3d67245a8d72ecf13f33dffac9f79dcdf70f75acb84d308770391510e0c23ad0", + "container_config": + { + "Hostname":"", + "User":"", + "Memory":0, + "MemorySwap":0, + "AttachStdin":false, + "AttachStdout":false, + "AttachStderr":false, + "PortSpecs":null, + "Tty":true, + "OpenStdin":true, + "StdinOnce":false, + "Env":null, + "Cmd": ["/bin/bash"] + ,"Dns":null, + "Image":"base", + "Volumes":null, + "VolumesFrom":"", + "WorkingDir":"" + }, + "Size": 6824592 + } + + :statuscode 200: no error + :statuscode 404: no such image + :statuscode 500: server error + + +Get the history of an image +*************************** + +.. http:get:: /images/(name)/history + + Return the history of the image ``name`` + + **Example request**: + + .. sourcecode:: http + + GET /images/base/history HTTP/1.1 + + **Example response**: + + .. sourcecode:: http + + HTTP/1.1 200 OK + Content-Type: application/json + + [ + { + "Id":"b750fe79269d", + "Created":1364102658, + "CreatedBy":"/bin/bash" + }, + { + "Id":"27cf78414709", + "Created":1364068391, + "CreatedBy":"" + } + ] + + :statuscode 200: no error + :statuscode 404: no such image + :statuscode 500: server error + + +Push an image on the registry +***************************** + +.. http:post:: /images/(name)/push + + Push the image ``name`` on the registry + + **Example request**: + + .. sourcecode:: http + + POST /images/test/push HTTP/1.1 + + **Example response**: + + .. sourcecode:: http + + HTTP/1.1 200 OK + Content-Type: application/json + + {"status":"Pushing..."} + {"status":"Pushing", "progress":"1/? (n/a)"} + {"error":"Invalid..."} + ... + + The ``X-Registry-Auth`` header can be used to include a + base64-encoded AuthConfig object. + + :query registry: the registry you wan to push, optional + :statuscode 200: no error + :statuscode 404: no such image + :statuscode 500: server error + + +Tag an image into a repository +****************************** + +.. http:post:: /images/(name)/tag + + Tag the image ``name`` into a repository + + **Example request**: + + .. sourcecode:: http + + POST /images/test/tag?repo=myrepo&force=0 HTTP/1.1 + + **Example response**: + + .. sourcecode:: http + + HTTP/1.1 200 OK + + :query repo: The repository to tag in + :query force: 1/True/true or 0/False/false, default false + :statuscode 200: no error + :statuscode 400: bad parameter + :statuscode 404: no such image + :statuscode 409: conflict + :statuscode 500: server error + + +Remove an image +*************** + +.. http:delete:: /images/(name) + + Remove the image ``name`` from the filesystem + + **Example request**: + + .. sourcecode:: http + + DELETE /images/test HTTP/1.1 + + **Example response**: + + .. sourcecode:: http + + HTTP/1.1 200 OK + Content-type: application/json + + [ + {"Untagged":"3e2f21a89f"}, + {"Deleted":"3e2f21a89f"}, + {"Deleted":"53b4f83ac9"} + ] + + :statuscode 200: no error + :statuscode 404: no such image + :statuscode 409: conflict + :statuscode 500: server error + + +Search images +************* + +.. http:get:: /images/search + + Search for an image in the docker index + + **Example request**: + + .. sourcecode:: http + + GET /images/search?term=sshd HTTP/1.1 + + **Example response**: + + .. sourcecode:: http + + HTTP/1.1 200 OK + Content-Type: application/json + + [ + { + "Name":"cespare/sshd", + "Description":"" + }, + { + "Name":"johnfuller/sshd", + "Description":"" + }, + { + "Name":"dhrp/mongodb-sshd", + "Description":"" + } + ] + + :query term: term to search + :statuscode 200: no error + :statuscode 500: server error + + +2.3 Misc +-------- + +Build an image from Dockerfile via stdin +**************************************** + +.. http:post:: /build + + Build an image from Dockerfile via stdin + + **Example request**: + + .. sourcecode:: http + + POST /build HTTP/1.1 + + {{ STREAM }} + + **Example response**: + + .. sourcecode:: http + + HTTP/1.1 200 OK + + {{ STREAM }} + + + The stream must be a tar archive compressed with one of the following algorithms: + identity (no compression), gzip, bzip2, xz. The archive must include a file called + `Dockerfile` at its root. It may include any number of other files, which will be + accessible in the build context (See the ADD build command). + + The Content-type header should be set to "application/tar". + + :query t: repository name (and optionally a tag) to be applied to the resulting image in case of success + :query q: suppress verbose build output + :query nocache: do not use the cache when building the image + :query rm: remove intermediate containers after a successful build + :statuscode 200: no error + :statuscode 500: server error + + +Check auth configuration +************************ + +.. http:post:: /auth + + Get the default username and email + + **Example request**: + + .. sourcecode:: http + + POST /auth HTTP/1.1 + Content-Type: application/json + + { + "username":"hannibal", + "password:"xxxx", + "email":"hannibal@a-team.com", + "serveraddress":"https://index.docker.io/v1/" + } + + **Example response**: + + .. sourcecode:: http + + HTTP/1.1 200 OK + + :statuscode 200: no error + :statuscode 204: no error + :statuscode 500: server error + + +Display system-wide information +******************************* + +.. http:get:: /info + + Display system-wide information + + **Example request**: + + .. sourcecode:: http + + GET /info HTTP/1.1 + + **Example response**: + + .. sourcecode:: http + + HTTP/1.1 200 OK + Content-Type: application/json + + { + "Containers":11, + "Images":16, + "Debug":false, + "NFd": 11, + "NGoroutines":21, + "MemoryLimit":true, + "SwapLimit":false, + "IPv4Forwarding":true + } + + :statuscode 200: no error + :statuscode 500: server error + + +Show the docker version information +*********************************** + +.. http:get:: /version + + Show the docker version information + + **Example request**: + + .. sourcecode:: http + + GET /version HTTP/1.1 + + **Example response**: + + .. sourcecode:: http + + HTTP/1.1 200 OK + Content-Type: application/json + + { + "Version":"0.2.2", + "GitCommit":"5a2a5cc+CHANGES", + "GoVersion":"go1.0.3" + } + + :statuscode 200: no error + :statuscode 500: server error + + +Create a new image from a container's changes +********************************************* + +.. http:post:: /commit + + Create a new image from a container's changes + + **Example request**: + + .. sourcecode:: http + + POST /commit?container=44c004db4b17&m=message&repo=myrepo HTTP/1.1 + + **Example response**: + + .. sourcecode:: http + + HTTP/1.1 201 OK + Content-Type: application/vnd.docker.raw-stream + + {"Id":"596069db4bf5"} + + :query container: source container + :query repo: repository + :query tag: tag + :query m: commit message + :query author: author (eg. "John Hannibal Smith ") + :query run: config automatically applied when the image is run. (ex: {"Cmd": ["cat", "/world"], "PortSpecs":["22"]}) + :statuscode 201: no error + :statuscode 404: no such container + :statuscode 500: server error + + +Monitor Docker's events +*********************** + +.. http:get:: /events + + Get events from docker, either in real time via streaming, or via polling (using `since`) + + **Example request**: + + .. sourcecode:: http + + POST /events?since=1374067924 + + **Example response**: + + .. sourcecode:: http + + HTTP/1.1 200 OK + Content-Type: application/json + + {"status":"create","id":"dfdf82bd3881","from":"base:latest","time":1374067924} + {"status":"start","id":"dfdf82bd3881","from":"base:latest","time":1374067924} + {"status":"stop","id":"dfdf82bd3881","from":"base:latest","time":1374067966} + {"status":"destroy","id":"dfdf82bd3881","from":"base:latest","time":1374067970} + + :query since: timestamp used for polling + :statuscode 200: no error + :statuscode 500: server error + + +3. Going further +================ + +3.1 Inside 'docker run' +----------------------- + +Here are the steps of 'docker run' : + +* Create the container +* If the status code is 404, it means the image doesn't exists: + * Try to pull it + * Then retry to create the container +* Start the container +* If you are not in detached mode: + * Attach to the container, using logs=1 (to have stdout and stderr from the container's start) and stream=1 +* If in detached mode or only stdin is attached: + * Display the container's id + + +3.2 Hijacking +------------- + +In this version of the API, /attach, uses hijacking to transport stdin, stdout and stderr on the same socket. This might change in the future. + +3.3 CORS Requests +----------------- + +To enable cross origin requests to the remote api add the flag "-api-enable-cors" when running docker in daemon mode. + +.. code-block:: bash + + docker -d -H="192.168.1.9:4243" -api-enable-cors + diff --git a/docs/sources/commandline/cli.rst b/docs/sources/commandline/cli.rst index 9f8c296fe6..edc618b823 100644 --- a/docs/sources/commandline/cli.rst +++ b/docs/sources/commandline/cli.rst @@ -32,11 +32,13 @@ Available Commands command/commit command/cp command/diff + command/events command/export command/history command/images command/import command/info + command/insert command/inspect command/kill command/login diff --git a/docs/sources/commandline/command/build.rst b/docs/sources/commandline/command/build.rst index cc36aa0dd9..5ce70d85d9 100644 --- a/docs/sources/commandline/command/build.rst +++ b/docs/sources/commandline/command/build.rst @@ -13,6 +13,7 @@ -t="": Repository name (and optionally a tag) to be applied to the resulting image in case of success. -q=false: Suppress verbose build output. -no-cache: Do not use the cache when building the image. + -rm: Remove intermediate containers after a successful build When a single Dockerfile is given as URL, then no context is set. When a git repository is set as URL, the repository is used as context diff --git a/docs/sources/commandline/command/commit.rst b/docs/sources/commandline/command/commit.rst index 64d74157f0..e1d5092fff 100644 --- a/docs/sources/commandline/command/commit.rst +++ b/docs/sources/commandline/command/commit.rst @@ -19,15 +19,31 @@ Full -run example:: - {"Hostname": "", - "User": "", - "CpuShares": 0, - "Memory": 0, - "MemorySwap": 0, - "PortSpecs": ["22", "80", "443"], - "Tty": true, - "OpenStdin": true, - "StdinOnce": true, - "Env": ["FOO=BAR", "FOO2=BAR2"], - "Cmd": ["cat", "-e", "/etc/resolv.conf"], - "Dns": ["8.8.8.8", "8.8.4.4"]} +{ + "Entrypoint" : null, + "Privileged" : false, + "User" : "", + "VolumesFrom" : "", + "Cmd" : ["cat", "-e", "/etc/resolv.conf"], + "Dns" : ["8.8.8.8", "8.8.4.4"], + "MemorySwap" : 0, + "AttachStdin" : false, + "AttachStderr" : false, + "CpuShares" : 0, + "OpenStdin" : false, + "Volumes" : null, + "Hostname" : "122612f45831", + "PortSpecs" : ["22", "80", "443"], + "Image" : "b750fe79269d2ec9a3c593ef05b4332b1d1a02a62b4accb2c21d589ff2f5f2dc", + "Tty" : false, + "Env" : [ + "HOME=/", + "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + ], + "StdinOnce" : false, + "Domainname" : "", + "WorkingDir" : "/", + "NetworkDisabled" : false, + "Memory" : 0, + "AttachStdout" : false +} diff --git a/docs/sources/commandline/command/cp.rst b/docs/sources/commandline/command/cp.rst index 14b5061ef7..ea84fa1f90 100644 --- a/docs/sources/commandline/command/cp.rst +++ b/docs/sources/commandline/command/cp.rst @@ -2,12 +2,13 @@ :description: Copy files/folders from the containers filesystem to the host path :keywords: cp, docker, container, documentation, copy -=========================================================== +============================================================================ ``cp`` -- Copy files/folders from the containers filesystem to the host path -=========================================================== +============================================================================ :: Usage: docker cp CONTAINER:RESOURCE HOSTPATH - Copy files/folders from the containers filesystem to the host path. Paths are relative to the root of the filesystem. + Copy files/folders from the containers filesystem to the host + path. Paths are relative to the root of the filesystem. diff --git a/docs/sources/commandline/command/events.rst b/docs/sources/commandline/command/events.rst new file mode 100644 index 0000000000..b8dd591fb1 --- /dev/null +++ b/docs/sources/commandline/command/events.rst @@ -0,0 +1,34 @@ +:title: Events Command +:description: Get real time events from the server +:keywords: events, docker, documentation + +================================================================= +``events`` -- Get real time events from the server +================================================================= + +:: + + Usage: docker events + + Get real time events from the server + +Examples +-------- + +Starting and stopping a container +................................. + +.. code-block:: bash + + $ sudo docker start 4386fb97867d + $ sudo docker stop 4386fb97867d + +In another shell + +.. code-block:: bash + + $ sudo docker events + [2013-09-03 15:49:26 +0200 CEST] 4386fb97867d: (from 12de384bfb10) start + [2013-09-03 15:49:29 +0200 CEST] 4386fb97867d: (from 12de384bfb10) die + [2013-09-03 15:49:29 +0200 CEST] 4386fb97867d: (from 12de384bfb10) stop + diff --git a/docs/sources/commandline/command/insert.rst b/docs/sources/commandline/command/insert.rst new file mode 100644 index 0000000000..0f2612c9af --- /dev/null +++ b/docs/sources/commandline/command/insert.rst @@ -0,0 +1,23 @@ +:title: Insert Command +:description: Insert a file in an image +:keywords: insert, image, docker, documentation + +========================================================================== +``insert`` -- Insert a file in an image +========================================================================== + +:: + + Usage: docker insert IMAGE URL PATH + + Insert a file from URL in the IMAGE at PATH + +Examples +-------- + +Insert file from github +....................... + +.. code-block:: bash + + $ sudo docker insert 8283e18b24bc https://raw.github.com/metalivedev/django/master/postinstall /tmp/postinstall.sh diff --git a/docs/sources/commandline/command/login.rst b/docs/sources/commandline/command/login.rst index 57ecaeb00e..46f354d6be 100644 --- a/docs/sources/commandline/command/login.rst +++ b/docs/sources/commandline/command/login.rst @@ -8,10 +8,17 @@ :: - Usage: docker login [OPTIONS] + Usage: docker login [OPTIONS] [SERVER] Register or Login to the docker registry server -e="": email -p="": password -u="": username + + If you want to login to a private registry you can + specify this by adding the server name. + + example: + docker login localhost:8080 + diff --git a/docs/sources/commandline/command/run.rst b/docs/sources/commandline/command/run.rst index cd283669e6..d29e70dd50 100644 --- a/docs/sources/commandline/command/run.rst +++ b/docs/sources/commandline/command/run.rst @@ -67,7 +67,7 @@ use-cases, like running Docker within Docker. docker run -w /path/to/dir/ -i -t ubuntu pwd -The ``-w`` lets the command beeing executed inside directory given, +The ``-w`` lets the command being executed inside directory given, here /path/to/dir/. If the path does not exists it is created inside the container. @@ -76,8 +76,8 @@ container. docker run -v `pwd`:`pwd` -w `pwd` -i -t ubuntu pwd The ``-v`` flag mounts the current working directory into the container. -The ``-w`` lets the command beeing executed inside the current -working directory, by changeing into the directory to the value +The ``-w`` lets the command being executed inside the current +working directory, by changing into the directory to the value returned by ``pwd``. So this combination executes the command using the container, but inside the current working directory. diff --git a/docs/sources/commandline/index.rst b/docs/sources/commandline/index.rst index 5c2b373205..0e7c8738b3 100644 --- a/docs/sources/commandline/index.rst +++ b/docs/sources/commandline/index.rst @@ -17,11 +17,13 @@ Contents: commit cp diff + events export history images import info + insert inspect kill login diff --git a/docs/sources/concepts/images/lego_docker.jpg b/docs/sources/concepts/images/lego_docker.jpg deleted file mode 100644 index b3039a2cb5..0000000000 Binary files a/docs/sources/concepts/images/lego_docker.jpg and /dev/null differ diff --git a/docs/sources/concepts/index.rst b/docs/sources/concepts/index.rst deleted file mode 100644 index e1cb8cd1a9..0000000000 --- a/docs/sources/concepts/index.rst +++ /dev/null @@ -1,16 +0,0 @@ -:title: Overview -:description: Docker documentation summary -:keywords: concepts, documentation, docker, containers - - - -Overview -======== - -Contents: - -.. toctree:: - :maxdepth: 1 - - ../index - manifesto diff --git a/docs/sources/concepts/manifesto.rst b/docs/sources/concepts/manifesto.rst deleted file mode 100644 index 7dd4b4bdda..0000000000 --- a/docs/sources/concepts/manifesto.rst +++ /dev/null @@ -1,129 +0,0 @@ -:title: Manifesto -:description: An overview of Docker and standard containers -:keywords: containers, lxc, concepts, explanation - -.. _dockermanifesto: - -Docker Manifesto ----------------- - -Docker complements LXC with a high-level API which operates at the -process level. It runs unix processes with strong guarantees of -isolation and repeatability across servers. - -Docker is a great building block for automating distributed systems: -large-scale web deployments, database clusters, continuous deployment -systems, private PaaS, service-oriented architectures, etc. - -- **Heterogeneous payloads** Any combination of binaries, libraries, - configuration files, scripts, virtualenvs, jars, gems, tarballs, you - name it. No more juggling between domain-specific tools. Docker can - deploy and run them all. -- **Any server** Docker can run on any x64 machine with a modern linux - kernel - whether it's a laptop, a bare metal server or a VM. This - makes it perfect for multi-cloud deployments. -- **Isolation** docker isolates processes from each other and from the - underlying host, using lightweight containers. -- **Repeatability** Because containers are isolated in their own - filesystem, they behave the same regardless of where, when, and - alongside what they run. - -.. image:: images/lego_docker.jpg - :target: http://bricks.argz.com/ins/7823-1/12 - -What is a Standard Container? -............................. - -Docker defines a unit of software delivery called a Standard -Container. The goal of a Standard Container is to encapsulate a -software component and all its dependencies in a format that is -self-describing and portable, so that any compliant runtime can run it -without extra dependency, regardless of the underlying machine and the -contents of the container. - -The spec for Standard Containers is currently 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 are a fundamental unit of physical delivery. - -Standard operations -~~~~~~~~~~~~~~~~~~~ - -Just like shipping containers, Standard Containers define a set of -STANDARD OPERATIONS. Shipping containers can be lifted, stacked, -locked, loaded, unloaded and labelled. Similarly, standard containers -can be started, stopped, copied, snapshotted, downloaded, uploaded and -tagged. - - -Content-agnostic -~~~~~~~~~~~~~~~~~~~ - -Just like shipping containers, Standard Containers are -CONTENT-AGNOSTIC: all standard operations have the same effect -regardless of the contents. A shipping container will be stacked in -exactly the same way whether it contains Vietnamese powder coffee or -spare Maserati parts. Similarly, Standard Containers are started or -uploaded in the same way whether they contain a postgres database, a -php application with its dependencies and application server, or Java -build artifacts. - -Infrastructure-agnostic -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Both types of containers are INFRASTRUCTURE-AGNOSTIC: they can be -transported to thousands of facilities around the world, and -manipulated by a wide variety of equipment. A shipping container can -be packed in a factory in Ukraine, transported by truck to the nearest -routing center, stacked onto a train, loaded into a German boat by an -Australian-built crane, stored in a warehouse at a US facility, -etc. Similarly, a standard container can be bundled on my laptop, -uploaded to S3, downloaded, run and snapshotted by a build server at -Equinix in Virginia, uploaded to 10 staging servers in a home-made -Openstack cluster, then sent to 30 production instances across 3 EC2 -regions. - - -Designed for automation -~~~~~~~~~~~~~~~~~~~~~~~ - -Because they offer the same standard operations regardless of content -and infrastructure, Standard Containers, just like their physical -counterpart, are extremely well-suited for automation. In fact, you -could say automation is their secret weapon. - -Many things that once required time-consuming and error-prone human -effort can now be programmed. Before shipping containers, a bag of -powder coffee was hauled, dragged, dropped, rolled and stacked by 10 -different people in 10 different locations by the time it reached its -destination. 1 out of 50 disappeared. 1 out of 20 was damaged. The -process was slow, inefficient and cost a fortune - and was entirely -different depending on the facility and the type of goods. - -Similarly, before Standard Containers, by the time a software -component ran in production, it had been individually built, -configured, bundled, documented, patched, vendored, templated, tweaked -and instrumented by 10 different people on 10 different -computers. Builds failed, libraries conflicted, mirrors crashed, -post-it notes were lost, logs were misplaced, cluster updates were -half-broken. The process was slow, inefficient and cost a fortune - -and was entirely different depending on the language and -infrastructure provider. - -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. - -With Standard Containers we can put an end to that embarrassment, by -making INDUSTRIAL-GRADE DELIVERY of software a reality. diff --git a/docs/sources/examples/hello_world.rst b/docs/sources/examples/hello_world.rst index 26e6f4c8a5..b7204fdfed 100644 --- a/docs/sources/examples/hello_world.rst +++ b/docs/sources/examples/hello_world.rst @@ -2,6 +2,28 @@ :description: A simple hello world example with Docker :keywords: docker, example, hello world +.. _running_examples: + +Running the Examples +==================== + +All the examples assume your machine is running the docker daemon. To +run the docker daemon in the background, simply type: + +.. code-block:: bash + + sudo docker -d & + +Now you can run docker in client mode: by default all commands will be +forwarded to the ``docker`` daemon via a protected Unix socket, so you +must run as root. + +.. code-block:: bash + + sudo docker help + +---- + .. _hello_world: Hello World @@ -49,4 +71,108 @@ See the example in action -Continue to the :ref:`hello_world_daemon` example. +---- + +.. _hello_world_daemon: + +Hello World Daemon +================== + +.. include:: example_header.inc + +And now for the most boring daemon ever written! + +This example assumes you have Docker installed and the Ubuntu +image already imported with ``docker pull ubuntu``. We will use the Ubuntu +image to run a simple hello world daemon that will just print hello +world to standard out every second. It will continue to do this until +we stop it. + +**Steps:** + +.. code-block:: bash + + CONTAINER_ID=$(sudo docker run -d ubuntu /bin/sh -c "while true; do echo hello world; sleep 1; done") + +We are going to run a simple hello world daemon in a new container +made from the *ubuntu* image. + +- **"docker run -d "** run a command in a new container. We pass "-d" + so it runs as a daemon. +- **"ubuntu"** is the image we want to run the command inside of. +- **"/bin/sh -c"** is the command we want to run in the container +- **"while true; do echo hello world; sleep 1; done"** is the mini + script we want to run, that will just print hello world once a + second until we stop it. +- **$CONTAINER_ID** the output of the run command will return a + container id, we can use in future commands to see what is going on + with this process. + +.. code-block:: bash + + sudo docker logs $CONTAINER_ID + +Check the logs make sure it is working correctly. + +- **"docker logs**" This will return the logs for a container +- **$CONTAINER_ID** The Id of the container we want the logs for. + +.. code-block:: bash + + sudo docker attach $CONTAINER_ID + +Attach to the container to see the results in realtime. + +- **"docker attach**" This will allow us to attach to a background + process to see what is going on. +- **$CONTAINER_ID** The Id of the container we want to attach too. + +Exit from the container attachment by pressing Control-C. + +.. code-block:: bash + + sudo docker ps + +Check the process list to make sure it is running. + +- **"docker ps"** this shows all running process managed by docker + +.. code-block:: bash + + sudo docker stop $CONTAINER_ID + +Stop the container, since we don't need it anymore. + +- **"docker stop"** This stops a container +- **$CONTAINER_ID** The Id of the container we want to stop. + +.. code-block:: bash + + sudo docker ps + +Make sure it is really stopped. + + +**Video:** + +See the example in action + +.. raw:: html + +
+ +
+ +The next example in the series is a :ref:`python_web_app` example, or +you could skip to any of the other examples: + +.. toctree:: + :maxdepth: 1 + + python_web_app + nodejs_web_app + running_redis_service + running_ssh_service + couchdb_data_volumes + postgresql_service + mongodb diff --git a/docs/sources/examples/hello_world_daemon.rst b/docs/sources/examples/hello_world_daemon.rst deleted file mode 100644 index 1d9c4ebe7c..0000000000 --- a/docs/sources/examples/hello_world_daemon.rst +++ /dev/null @@ -1,93 +0,0 @@ -:title: Hello world daemon example -:description: A simple hello world daemon example with Docker -:keywords: docker, example, hello world, daemon - -.. _hello_world_daemon: - -Hello World Daemon -================== - -.. include:: example_header.inc - -The most boring daemon ever written. - -This example assumes you have Docker installed and with the Ubuntu -image already imported ``docker pull ubuntu``. We will use the Ubuntu -image to run a simple hello world daemon that will just print hello -world to standard out every second. It will continue to do this until -we stop it. - -**Steps:** - -.. code-block:: bash - - CONTAINER_ID=$(sudo docker run -d ubuntu /bin/sh -c "while true; do echo hello world; sleep 1; done") - -We are going to run a simple hello world daemon in a new container -made from the *ubuntu* image. - -- **"docker run -d "** run a command in a new container. We pass "-d" - so it runs as a daemon. -- **"ubuntu"** is the image we want to run the command inside of. -- **"/bin/sh -c"** is the command we want to run in the container -- **"while true; do echo hello world; sleep 1; done"** is the mini - script we want to run, that will just print hello world once a - second until we stop it. -- **$CONTAINER_ID** the output of the run command will return a - container id, we can use in future commands to see what is going on - with this process. - -.. code-block:: bash - - sudo docker logs $CONTAINER_ID - -Check the logs make sure it is working correctly. - -- **"docker logs**" This will return the logs for a container -- **$CONTAINER_ID** The Id of the container we want the logs for. - -.. code-block:: bash - - sudo docker attach $CONTAINER_ID - -Attach to the container to see the results in realtime. - -- **"docker attach**" This will allow us to attach to a background - process to see what is going on. -- **$CONTAINER_ID** The Id of the container we want to attach too. - -.. code-block:: bash - - sudo docker ps - -Check the process list to make sure it is running. - -- **"docker ps"** this shows all running process managed by docker - -.. code-block:: bash - - sudo docker stop $CONTAINER_ID - -Stop the container, since we don't need it anymore. - -- **"docker stop"** This stops a container -- **$CONTAINER_ID** The Id of the container we want to stop. - -.. code-block:: bash - - sudo docker ps - -Make sure it is really stopped. - - -**Video:** - -See the example in action - -.. raw:: html - -
- -
- -Continue to the :ref:`python_web_app` example. diff --git a/docs/sources/examples/index.rst b/docs/sources/examples/index.rst index 2664b95e54..904080bb24 100644 --- a/docs/sources/examples/index.rst +++ b/docs/sources/examples/index.rst @@ -5,16 +5,16 @@ Examples -============ +======== -Contents: +Here are some examples of how to use Docker to create running +processes, starting from a very simple *Hello World* and progressing +to more substantial services like you might find in production. .. toctree:: :maxdepth: 1 - running_examples hello_world - hello_world_daemon python_web_app nodejs_web_app running_redis_service @@ -22,3 +22,4 @@ Contents: couchdb_data_volumes postgresql_service mongodb + running_riak_service diff --git a/docs/sources/examples/mongodb.rst b/docs/sources/examples/mongodb.rst index e351b9b384..5527fc00c7 100644 --- a/docs/sources/examples/mongodb.rst +++ b/docs/sources/examples/mongodb.rst @@ -24,8 +24,8 @@ Create an empty file called ``Dockerfile``: touch Dockerfile Next, define the parent image you want to use to build your own image on top of. -Here, we’ll use `CentOS `_ (tag: ``latest``) -available on the `docker index`_: +Here, we’ll use `Ubuntu `_ (tag: ``latest``) +available on the `docker index `_: .. code-block:: bash @@ -65,11 +65,13 @@ run without needing to provide a special configuration file) # Create the MongoDB data directory RUN mkdir -p /data/db -Finally, we'll expose the standard port that MongoDB runs on (27107) +Finally, we'll expose the standard port that MongoDB runs on (27107) as well as +define an ENTRYPOINT for the container. .. code-block:: bash EXPOSE 27017 + ENTRYPOINT ["usr/bin/mongod"] Now, lets build the image which will go through the ``Dockerfile`` we made and run all of the commands. @@ -84,10 +86,10 @@ the local port! .. code-block:: bash # Regular style - MONGO_ID=$(docker run -d /mongodb mongod) + MONGO_ID=$(docker run -d /mongodb) # Lean and mean - MONGO_ID=$(docker run -d /mongodb mongod --noprealloc --smallfiles) + MONGO_ID=$(docker run -d /mongodb --noprealloc --smallfiles) # Check the logs out docker logs $MONGO_ID diff --git a/docs/sources/examples/nodejs_web_app.rst b/docs/sources/examples/nodejs_web_app.rst index cb580c43d9..67584d1794 100644 --- a/docs/sources/examples/nodejs_web_app.rst +++ b/docs/sources/examples/nodejs_web_app.rst @@ -93,7 +93,7 @@ To install the right package for CentOS, we’ll use the instructions from the # Enable EPEL for Node.js RUN rpm -Uvh http://download.fedoraproject.org/pub/epel/6/i386/epel-release-6-8.noarch.rpm # Install Node.js and npm - RUN yum install -y npm-1.2.17-5.el6 + RUN yum install -y npm To bundle your app’s source code inside the docker image, use the ``ADD`` command: @@ -137,7 +137,7 @@ Your ``Dockerfile`` should now look like this: # Enable EPEL for Node.js RUN rpm -Uvh http://download.fedoraproject.org/pub/epel/6/i386/epel-release-6-8.noarch.rpm # Install Node.js and npm - RUN yum install -y npm-1.2.17-5.el6 + RUN yum install -y npm # Bundle app source ADD . /src diff --git a/docs/sources/examples/python_web_app.rst b/docs/sources/examples/python_web_app.rst index 92cac00d18..8654ab5f19 100644 --- a/docs/sources/examples/python_web_app.rst +++ b/docs/sources/examples/python_web_app.rst @@ -86,7 +86,7 @@ http://0.0.0.0:5000/" in the log output. .. code-block:: bash - WEB_PORT=$(docker port $WEB_WORKER 5000) + WEB_PORT=$(sudo docker port $WEB_WORKER 5000) Look up the public-facing port which is NAT-ed. Find the private port used by the container and store it inside of the WEB_PORT variable. diff --git a/docs/sources/examples/running_examples.rst b/docs/sources/examples/running_examples.rst deleted file mode 100644 index 6097f29fec..0000000000 --- a/docs/sources/examples/running_examples.rst +++ /dev/null @@ -1,23 +0,0 @@ -:title: Running the Examples -:description: An overview on how to run the docker examples -:keywords: docker, examples, how to - -.. _running_examples: - -Running the Examples --------------------- - -All the examples assume your machine is running the docker daemon. To -run the docker daemon in the background, simply type: - - .. code-block:: bash - - sudo docker -d & - -Now you can run docker in client mode: by defalt all commands will be -forwarded to the ``docker`` daemon via a protected Unix socket, so you -must run as root. - - .. code-block:: bash - - sudo docker help 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. diff --git a/docs/sources/faq.rst b/docs/sources/faq.rst index dd5fd11fd5..23460f4b55 100644 --- a/docs/sources/faq.rst +++ b/docs/sources/faq.rst @@ -122,6 +122,13 @@ What does Docker add to just plain LXC? (Jenkins, Strider, Travis), etc. Docker is rapidly establishing itself as the standard for container-based tooling. +Do I lose my data when the container exits? +........................................... + +Not at all! Any data that your application writes to disk gets preserved +in its container until you explicitly delete the container. The file +system for the container persists even after the container halts. + Can I help by adding some questions and answers? ................................................ diff --git a/docs/sources/index.rst b/docs/sources/index.rst index 8dfffa718b..2800310e5e 100644 --- a/docs/sources/index.rst +++ b/docs/sources/index.rst @@ -1,11 +1,11 @@ -:title: Welcome to the Docker Documentation +:title: Docker Documentation :description: An overview of the Docker Documentation :keywords: containers, lxc, concepts, explanation -Welcome -======= +.. image:: static_files/dockerlogo-h.png -.. image:: concepts/images/dockerlogo-h.png +Introduction +------------ ``docker``, the Linux Container Runtime, runs Unix processes with strong guarantees of isolation across servers. Your software runs diff --git a/docs/sources/installation/amazon.rst b/docs/sources/installation/amazon.rst index 333374f976..2c74b0fc32 100644 --- a/docs/sources/installation/amazon.rst +++ b/docs/sources/installation/amazon.rst @@ -1,22 +1,77 @@ :title: Installation on Amazon EC2 -:description: Docker installation on Amazon EC2 with a single vagrant command. Vagrant 1.1 or higher is required. +:description: Docker installation on Amazon EC2 :keywords: amazon ec2, virtualization, cloud, docker, documentation, installation -Using Vagrant (Amazon EC2) -========================== +Amazon EC2 +========== -This page explains how to setup and run an Amazon EC2 instance from your local machine. -Vagrant is not necessary to run Docker on EC2. You can follow the :ref:`ubuntu_linux` instructions -installing Docker on any EC2 instance running Ubuntu +.. include:: install_header.inc - 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. +There are several ways to install Docker on AWS EC2: + +* :ref:`amazonquickstart` or +* :ref:`amazonstandard` or +* :ref:`amazonvagrant` + +**You'll need an** `AWS account `_ **first, of course.** + +.. _amazonquickstart: + +Amazon QuickStart +----------------- + +1. **Choose an image:** + + * Open http://cloud-images.ubuntu.com/locator/ec2/ + * Enter ``amd64 precise`` in the search field (it will search as you + type) + * Pick an image by clicking on the image name. *An EBS-enabled + image will let you use a t1.micro instance.* Clicking on the image + name will take you to your AWS Console. + +2. **Tell CloudInit to install Docker:** + + * Enter ``#include https://get.docker.io`` into the instance *User + Data*. `CloudInit `_ + is part of the Ubuntu image you chose and it bootstraps from this + *User Data*. + +3. After a few more standard choices where defaults are probably ok, your + AWS Ubuntu instance with Docker should be running! + +**If this is your first AWS instance, you may need to set up your +Security Group to allow SSH.** By default all incoming ports to your +new instance will be blocked by the AWS Security Group, so you might +just get timeouts when you try to connect. + +Installing with ``get.docker.io`` (as above) will create a service +named ``dockerd``. You may want to set up a :ref:`docker group +` and add the *ubuntu* user to it so that you don't have +to use ``sudo`` for every Docker command. + +Once you've got Docker installed, you're ready to try it out -- head +on over to the :doc:`../use/basics` or :doc:`../examples/index` section. + +.. _amazonstandard: + +Standard Ubuntu Installation +---------------------------- + +If you want a more hands-on installation, then you can follow the +:ref:`ubuntu_linux` instructions installing Docker on any EC2 instance +running Ubuntu. Just follow Step 1 from :ref:`amazonquickstart` to +pick an image (or use one of your own) and skip the step with the +*User Data*. Then continue with the :ref:`ubuntu_linux` instructions. + +.. _amazonvagrant: + +Use Vagrant +----------- + +.. include:: install_unofficial.inc - -Installation ------------- - -Docker can now be installed on Amazon EC2 with a single vagrant command. Vagrant 1.1 or higher is required. +And finally, if you prefer to work through Vagrant, you can install +Docker that way too. Vagrant 1.1 or higher is required. 1. Install vagrant from http://www.vagrantup.com/ (or use your package manager) 2. Install the vagrant aws plugin @@ -35,16 +90,17 @@ Docker can now be installed on Amazon EC2 with a single vagrant command. Vagrant 4. Check your AWS environment. - Create a keypair specifically for EC2, give it a name and save it to your disk. *I usually store these in my ~/.ssh/ folder*. - - Check that your default security group has an inbound rule to accept SSH (port 22) connections. - + Create a keypair specifically for EC2, give it a name and save it + to your disk. *I usually store these in my ~/.ssh/ folder*. + Check that your default security group has an inbound rule to + accept SSH (port 22) connections. 5. Inform Vagrant of your settings - Vagrant will read your access credentials from your environment, so we need to set them there first. Make sure - you have everything on amazon aws setup so you can (manually) deploy a new image to EC2. + Vagrant will read your access credentials from your environment, so + we need to set them there first. Make sure you have everything on + amazon aws setup so you can (manually) deploy a new image to EC2. :: @@ -58,7 +114,8 @@ Docker can now be installed on Amazon EC2 with a single vagrant command. Vagrant * ``AWS_ACCESS_KEY_ID`` - The API key used to make requests to AWS * ``AWS_SECRET_ACCESS_KEY`` - The secret key to make AWS API requests * ``AWS_KEYPAIR_NAME`` - The name of the keypair used for this EC2 instance - * ``AWS_SSH_PRIVKEY`` - The path to the private key for the named keypair, for example ``~/.ssh/docker.pem`` + * ``AWS_SSH_PRIVKEY`` - The path to the private key for the named + keypair, for example ``~/.ssh/docker.pem`` You can check if they are set correctly by doing something like @@ -73,10 +130,12 @@ Docker can now be installed on Amazon EC2 with a single vagrant command. Vagrant vagrant up --provider=aws - If it stalls indefinitely on ``[default] Waiting for SSH to become available...``, Double check your default security - zone on AWS includes rights to SSH (port 22) to your container. + If it stalls indefinitely on ``[default] Waiting for SSH to become + available...``, Double check your default security zone on AWS + includes rights to SSH (port 22) to your container. - If you have an advanced AWS setup, you might want to have a look at https://github.com/mitchellh/vagrant-aws + If you have an advanced AWS setup, you might want to have a look at + https://github.com/mitchellh/vagrant-aws 7. Connect to your machine diff --git a/docs/sources/installation/archlinux.rst b/docs/sources/installation/archlinux.rst index 722c150194..4f0f211747 100644 --- a/docs/sources/installation/archlinux.rst +++ b/docs/sources/installation/archlinux.rst @@ -7,10 +7,6 @@ Arch Linux ========== - 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. - - Installing on Arch Linux is not officially supported but can be handled via either of the following AUR packages: @@ -36,6 +32,10 @@ either AUR package. Installation ------------ +.. include:: install_header.inc + +.. include:: install_unofficial.inc + The instructions here assume **yaourt** is installed. See `Arch User Repository `_ for information on building and installing packages from the AUR if you have not diff --git a/docs/sources/installation/binaries.rst b/docs/sources/installation/binaries.rst index 24de528145..28663fb779 100644 --- a/docs/sources/installation/binaries.rst +++ b/docs/sources/installation/binaries.rst @@ -7,9 +7,10 @@ Binaries ======== - **Please note this project is currently under heavy development. It should not be used in production.** +.. include:: install_header.inc -**This instruction set is meant for hackers who want to try out Docker on a variety of environments.** +**This instruction set is meant for hackers who want to try out Docker +on a variety of environments.** Right now, the officially supported distributions are: @@ -23,22 +24,18 @@ But we know people have had success running it under - Suse - :ref:`arch_linux` +Check Your Kernel +----------------- -Dependencies: -------------- - -* 3.8 Kernel (read more about :ref:`kernel`) -* AUFS filesystem support -* lxc -* xz-utils +Your host's Linux kernel must meet the Docker :ref:`kernel` Get the docker binary: ---------------------- .. code-block:: bash - wget http://get.docker.io/builds/Linux/x86_64/docker-latest.tgz - tar -xf docker-latest.tgz + wget --output-document=docker https://get.docker.io/builds/Linux/x86_64/docker-latest + chmod +x docker Run the docker daemon diff --git a/docs/sources/installation/gentoolinux.rst b/docs/sources/installation/gentoolinux.rst new file mode 100644 index 0000000000..6e41e049d1 --- /dev/null +++ b/docs/sources/installation/gentoolinux.rst @@ -0,0 +1,125 @@ +:title: Installation on Gentoo Linux +:description: Docker installation instructions and nuances for Gentoo Linux. +:keywords: gentoo linux, virtualization, docker, documentation, installation + +.. _gentoo_linux: + +Gentoo Linux +============ + +.. include:: install_header.inc + +.. 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. + +Installation +^^^^^^^^^^^^ + +Ensure that layman is installed: + +.. code-block:: bash + + 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: + +.. code-block:: bash + + sudo layman -f -a docker + +Once that completes, the ``app-emulation/docker`` package will be available +for emerge: + +.. code-block:: bash + + 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/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). + +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. + +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. + +OpenRC +------ + +To start the docker daemon: + +.. code-block:: bash + + sudo /etc/init.d/docker start + +To start on system boot: + +.. code-block:: bash + + sudo rc-update add docker default + +systemd +------- + +To start the docker daemon: + +.. code-block:: bash + + sudo systemctl start docker.service + +To start on system boot: + +.. code-block:: bash + + sudo systemctl enable docker.service + +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: + +.. code-block:: bash + + sudo sysctl -w net.ipv4.ip_forward=1 + +Or, to enable it more permanently: + +.. code-block:: bash + + echo net.ipv4.ip_forward = 1 | sudo tee /etc/sysctl.d/docker.conf + +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. diff --git a/docs/sources/installation/images/win/hp_bios_vm.JPG b/docs/sources/installation/images/win/hp_bios_vm.JPG new file mode 100644 index 0000000000..b44cabeac1 Binary files /dev/null and b/docs/sources/installation/images/win/hp_bios_vm.JPG differ diff --git a/docs/sources/installation/images/win/ts_go_bios.JPG b/docs/sources/installation/images/win/ts_go_bios.JPG new file mode 100644 index 0000000000..05f2310ad3 Binary files /dev/null and b/docs/sources/installation/images/win/ts_go_bios.JPG differ diff --git a/docs/sources/installation/images/win/ts_no_docker.JPG b/docs/sources/installation/images/win/ts_no_docker.JPG new file mode 100644 index 0000000000..51fa766711 Binary files /dev/null and b/docs/sources/installation/images/win/ts_no_docker.JPG differ diff --git a/docs/sources/installation/index.rst b/docs/sources/installation/index.rst index c2a93f5a01..1a73cb7ae6 100644 --- a/docs/sources/installation/index.rst +++ b/docs/sources/installation/index.rst @@ -24,5 +24,6 @@ Contents: amazon rackspace archlinux + gentoolinux upgrading kernel diff --git a/docs/sources/installation/install_header.inc b/docs/sources/installation/install_header.inc new file mode 100644 index 0000000000..c9b9e4c494 --- /dev/null +++ b/docs/sources/installation/install_header.inc @@ -0,0 +1,7 @@ + +.. note:: + + Docker is still under heavy development! We don't recommend using + it in production yet, but we're getting closer with each + release. Please see our blog post, `"Getting to Docker 1.0" + `_ diff --git a/docs/sources/installation/install_unofficial.inc b/docs/sources/installation/install_unofficial.inc new file mode 100644 index 0000000000..8d121918b5 --- /dev/null +++ b/docs/sources/installation/install_unofficial.inc @@ -0,0 +1,7 @@ + +.. note:: + + This is a community contributed installation path. The only + 'official' installation is using the :ref:`ubuntu_linux` + installation path. This version may be out of date because it + depends on some binaries to be updated and published diff --git a/docs/sources/installation/kernel.rst b/docs/sources/installation/kernel.rst index 7c5715a62d..2959fa4fcd 100644 --- a/docs/sources/installation/kernel.rst +++ b/docs/sources/installation/kernel.rst @@ -62,7 +62,7 @@ kernel tree to add AUFS. The process is documented on Cgroups and namespaces ---------------------- -You need to enable namespaces and cgroups, to the extend of what is needed +You need to enable namespaces and cgroups, to the extent 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 diff --git a/docs/sources/installation/rackspace.rst b/docs/sources/installation/rackspace.rst index 7f360682e2..2a4bdbc955 100644 --- a/docs/sources/installation/rackspace.rst +++ b/docs/sources/installation/rackspace.rst @@ -6,21 +6,22 @@ Rackspace Cloud =============== - 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. +.. include:: install_unofficial.inc - -Installing Docker on Ubuntu provided by Rackspace is pretty straightforward, and you should mostly be able to follow the +Installing Docker on Ubuntu provided by Rackspace is pretty +straightforward, and you should mostly be able to follow the :ref:`ubuntu_linux` installation guide. **However, there is one caveat:** -If you are using any linux not already shipping with the 3.8 kernel you will need to install it. And this is a little -more difficult on Rackspace. +If you are using any linux not already shipping with the 3.8 kernel +you will need to install it. And this is a little more difficult on +Rackspace. -Rackspace boots their servers using grub's menu.lst and does not like non 'virtual' packages (e.g. xen compatible) -kernels there, although they do work. This makes ``update-grub`` to not have the expected result, and you need to -set the kernel manually. +Rackspace boots their servers using grub's ``menu.lst`` and does not +like non 'virtual' packages (e.g. xen compatible) kernels there, +although they do work. This makes ``update-grub`` to not have the +expected result, and you need to set the kernel manually. **Do not attempt this on a production machine!** @@ -33,7 +34,8 @@ set the kernel manually. apt-get install linux-generic-lts-raring -Great, now you have kernel installed in /boot/, next is to make it boot next time. +Great, now you have kernel installed in ``/boot/``, next is to make it +boot next time. .. code-block:: bash @@ -43,9 +45,10 @@ Great, now you have kernel installed in /boot/, next is to make it boot next tim # this should return some results -Now you need to manually edit /boot/grub/menu.lst, you will find a section at the bottom with the existing options. -Copy the top one and substitute the new kernel into that. Make sure the new kernel is on top, and double check kernel -and initrd point to the right files. +Now you need to manually edit ``/boot/grub/menu.lst``, you will find a +section at the bottom with the existing options. Copy the top one and +substitute the new kernel into that. Make sure the new kernel is on +top, and double check kernel and initrd point to the right files. Make special care to double check the kernel and initrd entries. @@ -92,4 +95,4 @@ Verify the kernel was updated # nice! 3.8. -Now you can finish with the :ref:`ubuntu_linux` instructions. \ No newline at end of file +Now you can finish with the :ref:`ubuntu_linux` instructions. diff --git a/docs/sources/installation/ubuntulinux.rst b/docs/sources/installation/ubuntulinux.rst index d1103544fd..84cebc1318 100644 --- a/docs/sources/installation/ubuntulinux.rst +++ b/docs/sources/installation/ubuntulinux.rst @@ -7,7 +7,12 @@ Ubuntu Linux ============ - **Please note this project is currently under heavy development. It should not be used in production.** +.. warning:: + + These instructions have changed for 0.6. If you are upgrading from + an earlier version, you will need to follow them again. + +.. include:: install_header.inc Right now, the officially supported distribution are: @@ -19,7 +24,8 @@ Docker has the following dependencies * Linux kernel 3.8 (read more about :ref:`kernel`) * AUFS file system support (we are working on BTRFS support as an alternative) -Please read :ref:`ufw`, if you plan to use `UFW (Uncomplicated Firewall) `_ +Please read :ref:`ufw`, if you plan to use `UFW (Uncomplicated +Firewall) `_ .. _ubuntu_precise: @@ -35,12 +41,13 @@ Dependencies **Linux kernel 3.8** Due to a bug in LXC, docker works best on the 3.8 kernel. Precise -comes with a 3.2 kernel, so we need to upgrade it. The kernel we -install comes with AUFS built in. We also include the generic headers -to enable packages that depend on them, like ZFS and the VirtualBox -guest additions. If you didn't install the headers for your "precise" -kernel, then you can skip these headers for the "raring" kernel. But -it is safer to include them if you're not sure. +comes with a 3.2 kernel, so we need to upgrade it. The kernel you'll +install when following these steps comes with AUFS built in. We also +include the generic headers to enable packages that depend on them, +like ZFS and the VirtualBox guest additions. If you didn't install the +headers for your "precise" kernel, then you can skip these headers for +the "raring" kernel. But it is safer to include them if you're not +sure. .. code-block:: bash @@ -56,14 +63,22 @@ it is safer to include them if you're not sure. Installation ------------ -Docker is available as a Ubuntu PPA (Personal Package Archive), -`hosted on launchpad `_ -which makes installing Docker on Ubuntu very easy. +.. warning:: + + These instructions have changed for 0.6. If you are upgrading from + an earlier version, you will need to follow them again. + +Docker is available as a Debian package, which makes installation easy. + .. code-block:: bash - # Add the PPA sources to your apt sources list. - sudo apt-get install python-software-properties && sudo add-apt-repository ppa:dotcloud/lxc-docker + # Add the Docker repository key to your local keychain + # using apt-key finger you can check the fingerprint matches 36A1 D786 9245 C895 0F96 6E92 D857 6A8B A88D 21E9 + sudo sh -c "curl https://get.docker.io/gpg | apt-key add -" + + # Add the Docker repository to your apt sources list. + sudo sh -c "echo deb http://get.docker.io/ubuntu docker main > /etc/apt/sources.list.d/docker.list" # Update your sources sudo apt-get update @@ -101,30 +116,23 @@ have AUFS filesystem support enabled, so we need to install it. sudo apt-get update sudo apt-get install linux-image-extra-`uname -r` -**add-apt-repository support** - -Some installations of Ubuntu 13.04 require ``software-properties-common`` to be -installed before being able to use add-apt-repository. - -.. code-block:: bash - - sudo apt-get install software-properties-common - Installation ------------ -Docker is available as a Ubuntu PPA (Personal Package Archive), -`hosted on launchpad `_ -which makes installing Docker on Ubuntu very easy. +Docker is available as a Debian package, which makes installation easy. - -Add the custom package sources to your apt sources list. +*Please note that these instructions have changed for 0.6. If you are upgrading from an earlier version, you will need +to follow them again.* .. code-block:: bash - # add the sources to your apt - sudo add-apt-repository ppa:dotcloud/lxc-docker + # Add the Docker repository key to your local keychain + # using apt-key finger you can check the fingerprint matches 36A1 D786 9245 C895 0F96 6E92 D857 6A8B A88D 21E9 + sudo sh -c "curl http://get.docker.io/gpg | apt-key add -" + + # Add the Docker repository to your apt sources list. + sudo sh -c "echo deb http://get.docker.io/ubuntu docker main > /etc/apt/sources.list.d/docker.list" # update sudo apt-get update @@ -137,7 +145,8 @@ Verify it worked .. code-block:: bash - # download the base 'ubuntu' container and run bash inside it while setting up an interactive shell + # download the base 'ubuntu' container + # and run bash inside it while setting up an interactive shell sudo docker run -i -t ubuntu /bin/bash # type exit to exit @@ -151,7 +160,8 @@ Verify it worked Docker and UFW ^^^^^^^^^^^^^^ -Docker uses a bridge to manage containers networking, by default UFW drop all `forwarding`, a first step is to enable forwarding: +Docker uses a bridge to manage containers networking, by default UFW +drop all `forwarding`, a first step is to enable forwarding: .. code-block:: bash @@ -169,8 +179,9 @@ Then reload UFW: sudo ufw reload -UFW's default set of rules denied all `incoming`, so if you want to be able to reach your containers from another host, -you should allow incoming connections on the docker port (default 4243): +UFW's default set of rules denied all `incoming`, so if you want to be +able to reach your containers from another host, you should allow +incoming connections on the docker port (default 4243): .. code-block:: bash diff --git a/docs/sources/installation/upgrading.rst b/docs/sources/installation/upgrading.rst index 9fa47904be..47482314f3 100644 --- a/docs/sources/installation/upgrading.rst +++ b/docs/sources/installation/upgrading.rst @@ -5,18 +5,32 @@ .. _upgrading: Upgrading -============ +========= -**These instructions are for upgrading Docker** +The technique for upgrading ``docker`` to a newer version depends on +how you installed ``docker``. + +.. versionadded:: 0.5.3 + You may wish to add a ``docker`` group to your system to avoid using sudo with ``docker``. (see :ref:`dockergroup`) -After normal installation -------------------------- +After ``apt-get`` +----------------- -If you installed Docker normally using apt-get or used Vagrant, use apt-get to upgrade. +If you installed Docker using ``apt-get`` or Vagrant, then you should +use ``apt-get`` to upgrade. + +.. versionadded:: 0.6 + Add Docker repository information to your system first. .. code-block:: bash + # Add the Docker repository key to your local keychain + sudo sh -c "curl https://get.docker.io/gpg | apt-key add -" + + # Add the Docker repository to your apt sources list. + sudo sh -c "echo deb https://get.docker.io/ubuntu docker main > /etc/apt/sources.list.d/docker.list" + # update your sources list sudo apt-get update @@ -27,7 +41,7 @@ If you installed Docker normally using apt-get or used Vagrant, use apt-get to u After manual installation ------------------------- -If you installed the Docker binary +If you installed the Docker :ref:`binaries` then follow these steps: .. code-block:: bash @@ -48,8 +62,10 @@ If you installed the Docker binary tar -xf docker-latest.tgz -Start docker in daemon mode (-d) and disconnect (&) starting ./docker will start the version in your current dir rather than a version which -might reside in your path. +Start docker in daemon mode (``-d``) and disconnect, running the +daemon in the background (``&``). Starting as ``./docker`` guarantees +to run the version in your current directory rather than a version +which might reside in your path. .. code-block:: bash diff --git a/docs/sources/installation/vagrant.rst b/docs/sources/installation/vagrant.rst index 28b2a4e22d..3d7faf515e 100644 --- a/docs/sources/installation/vagrant.rst +++ b/docs/sources/installation/vagrant.rst @@ -7,30 +7,43 @@ Using Vagrant (Mac, Linux) ========================== -This guide will setup a new virtualbox virtual machine with docker installed on your computer. This works on most operating -systems, including MacOX, Windows, Linux, FreeBSD and others. If you can install these and have at least 400Mb RAM -to spare you should be good. - +This guide will setup a new virtualbox virtual machine with docker +installed on your computer. This works on most operating systems, +including MacOX, Windows, Linux, FreeBSD and others. If you can +install these and have at least 400MB RAM to spare you should be good. Install Vagrant and Virtualbox ------------------------------ -1. Install virtualbox from https://www.virtualbox.org/ (or use your package manager) -2. Install vagrant from http://www.vagrantup.com/ (or use your package manager) -3. Install git if you had not installed it before, check if it is installed by running - ``git`` in a terminal window +.. include:: install_header.inc + +.. include:: install_unofficial.inc + +#. Install virtualbox from https://www.virtualbox.org/ (or use your + package manager) +#. Install vagrant from http://www.vagrantup.com/ (or use your package + manager) +#. Install git if you had not installed it before, check if it is + installed by running ``git`` in a terminal window Spin it up ---------- -1. Fetch the docker sources (this includes the Vagrantfile for machine setup). +1. Fetch the docker sources (this includes the ``Vagrantfile`` for + machine setup). .. code-block:: bash git clone https://github.com/dotcloud/docker.git -2. Run vagrant from the sources directory +2. Change directory to docker + + .. code-block:: bash + + cd docker + +3. Run vagrant from the sources directory .. code-block:: bash diff --git a/docs/sources/installation/windows.rst b/docs/sources/installation/windows.rst index 7830106020..a6b30aa41e 100644 --- a/docs/sources/installation/windows.rst +++ b/docs/sources/installation/windows.rst @@ -7,14 +7,16 @@ Using Vagrant (Windows) ======================= - Please note this is a community contributed installation path. The only 'official' installation is using the :ref:`ubuntu_linux` installation path. This version - may be out of date because it depends on some binaries to be updated and published +Docker can run on Windows using a VM like VirtualBox. You then run +Linux within the VM. - - -Requirements +Installation ------------ +.. include:: install_header.inc + +.. include:: install_unofficial.inc + 1. Install virtualbox from https://www.virtualbox.org - or follow this tutorial__ .. __: http://www.slideshare.net/julienbarbier42/install-virtualbox-on-windows-7 @@ -33,7 +35,10 @@ We recommend having at least 2Gb of free disk space and 2Gb of RAM (or more). Opening a command prompt ------------------------ -First open a cmd prompt. Press Windows key and then press “R” key. This will open the RUN dialog box for you. Type “cmd” and press Enter. Or you can click on Start, type “cmd” in the “Search programs and files” field, and click on cmd.exe. +First open a cmd prompt. Press Windows key and then press “R” +key. This will open the RUN dialog box for you. Type “cmd” and press +Enter. Or you can click on Start, type “cmd” in the “Search programs +and files” field, and click on cmd.exe. .. image:: images/win/_01.gif :alt: Git install @@ -45,12 +50,17 @@ This should open a cmd prompt window. :alt: run docker :align: center -Alternatively, you can also use a Cygwin terminal, or Git Bash (or any other command line program you are usually using). The next steps would be the same. +Alternatively, you can also use a Cygwin terminal, or Git Bash (or any +other command line program you are usually using). The next steps +would be the same. + +.. _launch_ubuntu: Launch an Ubuntu virtual server ------------------------------- -Let’s download and run an Ubuntu image with docker binaries already installed. +Let’s download and run an Ubuntu image with docker binaries already +installed. .. code-block:: bash @@ -62,7 +72,9 @@ Let’s download and run an Ubuntu image with docker binaries already installed. :alt: run docker :align: center -Congratulations! You are running an Ubuntu server with docker installed on it. You do not see it though, because it is running in the background. +Congratulations! You are running an Ubuntu server with docker +installed on it. You do not see it though, because it is running in +the background. Log onto your Ubuntu server --------------------------- @@ -81,7 +93,12 @@ Run the following command vagrant ssh -You may see an error message starting with “`ssh` executable not found”. In this case it means that you do not have SSH in your PATH. If you do not have SSH in your PATH you can set it up with the “set” command. For instance, if your ssh.exe is in the folder named “C:\Program Files (x86)\Git\bin”, then you can run the following command: +You may see an error message starting with “`ssh` executable not +found”. In this case it means that you do not have SSH in your +PATH. If you do not have SSH in your PATH you can set it up with the +“set” command. For instance, if your ssh.exe is in the folder named +“C:\Program Files (x86)\Git\bin”, then you can run the following +command: .. code-block:: bash @@ -100,13 +117,16 @@ First step is to get the IP and port of your Ubuntu server. Simply run: vagrant ssh-config -You should see an output with HostName and Port information. In this example, HostName is 127.0.0.1 and port is 2222. And the User is “vagrant”. The password is not shown, but it is also “vagrant”. +You should see an output with HostName and Port information. In this +example, HostName is 127.0.0.1 and port is 2222. And the User is +“vagrant”. The password is not shown, but it is also “vagrant”. .. image:: images/win/ssh-config.gif :alt: run docker :align: center -You can now use this information for connecting via SSH to your server. To do so you can: +You can now use this information for connecting via SSH to your +server. To do so you can: - Use putty.exe OR - Use SSH from a terminal @@ -114,8 +134,9 @@ You can now use this information for connecting via SSH to your server. To do so Use putty.exe ''''''''''''' -You can download putty.exe from this page http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html -Launch putty.exe and simply enter the information you got from last step. +You can download putty.exe from this page +http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html Launch +putty.exe and simply enter the information you got from last step. .. image:: images/win/putty.gif :alt: run docker @@ -130,7 +151,9 @@ Open, and enter user = vagrant and password = vagrant. SSH from a terminal ''''''''''''''''''' -You can also run this command on your favorite terminal (windows prompt, cygwin, git-bash, …). Make sure to adapt the IP and port from what you got from the vagrant ssh-config command. +You can also run this command on your favorite terminal (windows +prompt, cygwin, git-bash, …). Make sure to adapt the IP and port from +what you got from the vagrant ssh-config command. .. code-block:: bash @@ -142,12 +165,14 @@ Enter user = vagrant and password = vagrant. :alt: run docker :align: center -Congratulations, you are now logged onto your Ubuntu Server, running on top of your Windows machine ! +Congratulations, you are now logged onto your Ubuntu Server, running +on top of your Windows machine ! Running Docker -------------- -First you have to be root in order to run docker. Simply run the following command: +First you have to be root in order to run docker. Simply run the +following command: .. code-block:: bash @@ -166,3 +191,29 @@ You are now ready for the docker’s “hello world” example. Run All done! Now you can continue with the :ref:`hello_world` example. + +Troubleshooting +--------------- + +VM does not boot +```````````````` + +.. image:: images/win/ts_go_bios.JPG + +If you run into this error message "The VM failed to remain in the +'running' state while attempting to boot", please check that your +computer has virtualization technology available and activated by +going to the BIOS. Here's an example for an HP computer (System +configuration / Device configuration) + +.. image:: images/win/hp_bios_vm.JPG + + +Docker is not installed +``````````````````````` + +.. image:: images/win/ts_no_docker.JPG + +If you run into this error message "The program 'docker' is currently +not installed", try deleting the docker folder and restart from +:ref:`launch_ubuntu` diff --git a/docs/sources/concepts/images/dockerlogo-h.png b/docs/sources/static_files/dockerlogo-h.png similarity index 100% rename from docs/sources/concepts/images/dockerlogo-h.png rename to docs/sources/static_files/dockerlogo-h.png diff --git a/docs/sources/concepts/images/dockerlogo-v.png b/docs/sources/static_files/dockerlogo-v.png similarity index 100% rename from docs/sources/concepts/images/dockerlogo-v.png rename to docs/sources/static_files/dockerlogo-v.png diff --git a/docs/sources/terms/images/docker-filesystems-busyboxrw.png b/docs/sources/terms/images/docker-filesystems-busyboxrw.png index 24277dc1f4..ad41c940e4 100644 Binary files a/docs/sources/terms/images/docker-filesystems-busyboxrw.png and b/docs/sources/terms/images/docker-filesystems-busyboxrw.png differ diff --git a/docs/sources/terms/images/docker-filesystems-debian.png b/docs/sources/terms/images/docker-filesystems-debian.png index 8411733a5f..823a215d3e 100644 Binary files a/docs/sources/terms/images/docker-filesystems-debian.png and b/docs/sources/terms/images/docker-filesystems-debian.png differ diff --git a/docs/sources/terms/images/docker-filesystems-debianrw.png b/docs/sources/terms/images/docker-filesystems-debianrw.png index b7b16c1cc2..97c69a9944 100644 Binary files a/docs/sources/terms/images/docker-filesystems-debianrw.png and b/docs/sources/terms/images/docker-filesystems-debianrw.png differ diff --git a/docs/sources/terms/images/docker-filesystems-generic.png b/docs/sources/terms/images/docker-filesystems-generic.png index a6710680a9..fb734b75c6 100644 Binary files a/docs/sources/terms/images/docker-filesystems-generic.png and b/docs/sources/terms/images/docker-filesystems-generic.png differ diff --git a/docs/sources/terms/images/docker-filesystems-multilayer.png b/docs/sources/terms/images/docker-filesystems-multilayer.png index 025a9d47bd..0b3ae19c2c 100644 Binary files a/docs/sources/terms/images/docker-filesystems-multilayer.png and b/docs/sources/terms/images/docker-filesystems-multilayer.png differ diff --git a/docs/sources/terms/images/docker-filesystems-multiroot.png b/docs/sources/terms/images/docker-filesystems-multiroot.png index 42a710cc82..5e864273f3 100644 Binary files a/docs/sources/terms/images/docker-filesystems-multiroot.png and b/docs/sources/terms/images/docker-filesystems-multiroot.png differ diff --git a/docs/sources/terms/images/docker-filesystems.svg b/docs/sources/terms/images/docker-filesystems.svg index 13d61dc1eb..d41aff2522 100644 --- a/docs/sources/terms/images/docker-filesystems.svg +++ b/docs/sources/terms/images/docker-filesystems.svg @@ -26,16 +26,16 @@ inkscape:pageopacity="0.0" inkscape:pageshadow="2" inkscape:zoom="0.82666667" - inkscape:cx="382.45968" - inkscape:cy="348.3871" + inkscape:cx="236.08871" + inkscape:cy="300" inkscape:document-units="px" - inkscape:current-layer="layer4" + inkscape:current-layer="layer2" showgrid="false" width="800px" inkscape:window-width="1327" inkscape:window-height="714" - inkscape:window-x="616" - inkscape:window-y="1483" + inkscape:window-x="686" + inkscape:window-y="219" inkscape:window-maximized="0" showguides="false" inkscape:guide-bbox="true" @@ -48,98 +48,98 @@ inkscape:snap-bbox="false" inkscape:snap-grids="false"> + orientation="0,1" /> + orientation="1,0" /> + orientation="1,0" /> + orientation="1,0" /> + orientation="1,0" /> + orientation="1,0" /> + orientation="0,1" /> + + - - + id="guide5235" /> + id="guide5237" /> + id="guide5239" /> + - - + inkscape:vp_z="1199.1838 : 590.41154 : 1" + inkscape:persp3d-origin="406.04839 : 290.19023 : 1" + id="perspective4014" /> + id="perspective4012" /> + + inkscape:vp_z="1200.5884 : 584.76404 : 1" + inkscape:vp_y="0 : 1000 : 0" + inkscape:vp_x="-407.35594 : 591.07573 : 1" + sodipodi:type="inkscape:persp3d" /> @@ -149,1392 +149,1393 @@ image/svg+xml - + + id="layer8" + inkscape:groupmode="layer" + sodipodi:insensitive="true"> + height="600" + width="800" + id="rect8704" + style="fill:#f0f0f0;fill-opacity:0.51111115;stroke:none" /> + id="layer2" + inkscape:groupmode="layer"> + inkscape:corner0="0.49469727 : -0.063230334 : 0 : 1" + inkscape:perspectiveID="#perspective3054" + id="g3999" + style="fill:#aad3d3;fill-opacity:1;stroke:#000000;stroke-width:2.4000001;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" + sodipodi:type="inkscape:box3d" + inkscape:export-filename="/Users/arothfusz/src/metalivedev/docker/docs/sources/terms/images/docker-filesystems-multiroot.png" + inkscape:export-xdpi="90" + inkscape:export-ydpi="90"> + style="fill:#afafde;fill-rule:evenodd;stroke:#000000;stroke-width:2.4000001;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" + id="path4009" + sodipodi:type="inkscape:box3dside" /> + style="fill:#353564;fill-rule:evenodd;stroke:#000000;stroke-width:2.4000001;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" + id="path4001" + sodipodi:type="inkscape:box3dside" /> + style="fill:#e9e9ff;fill-rule:evenodd;stroke:#000000;stroke-width:2.4000001;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" + id="path4011" + sodipodi:type="inkscape:box3dside" /> + style="fill:#bbdbdb;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:2.4000001;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" + id="path4003" + sodipodi:type="inkscape:box3dside" /> + style="fill:#91c6c6;fill-rule:evenodd;stroke:#000000;stroke-width:2.4000001;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" + id="path4007" + sodipodi:type="inkscape:box3dside" /> - - - - - - - - - + style="fill:#a9d2d2;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:2.4000001;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" + id="path4005" + sodipodi:type="inkscape:box3dside" /> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + transform="matrix(0.75405944,0,0,0.75405944,111.78234,89.235813)" + inkscape:export-filename="/Users/arothfusz/src/metalivedev/docker/docs/sources/terms/images/docker-filesystems-multiroot.png" + inkscape:export-xdpi="90" + inkscape:export-ydpi="90"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +