Merge pull request #1908 from dotcloud/bump_v0.6.2

Bump v0.6.2
This commit is contained in:
Daniel Mizyrycki 2013-09-18 11:21:51 -07:00
commit 081543c49a
357 changed files with 75407 additions and 2900 deletions

3
.gitignore vendored
View File

@ -14,3 +14,6 @@ docs/_templates
.gopath/ .gopath/
.dotcloud .dotcloud
*.test *.test
bundles/
.hg/
.git/

View File

@ -17,6 +17,7 @@ Antony Messerli <amesserl@rackspace.com>
Barry Allard <barry.allard@gmail.com> Barry Allard <barry.allard@gmail.com>
Brandon Liu <bdon@bdon.org> Brandon Liu <bdon@bdon.org>
Brian McCallister <brianm@skife.org> Brian McCallister <brianm@skife.org>
Brian Olsen <brian@maven-group.org>
Bruno Bigras <bigras.bruno@gmail.com> Bruno Bigras <bigras.bruno@gmail.com>
Caleb Spare <cespare@gmail.com> Caleb Spare <cespare@gmail.com>
Calen Pennington <cale@edx.org> Calen Pennington <cale@edx.org>
@ -34,6 +35,7 @@ Dominik Honnef <dominik@honnef.co>
Don Spaulding <donspauldingii@gmail.com> Don Spaulding <donspauldingii@gmail.com>
Dr Nic Williams <drnicwilliams@gmail.com> Dr Nic Williams <drnicwilliams@gmail.com>
Elias Probst <mail@eliasprobst.eu> Elias Probst <mail@eliasprobst.eu>
Emily Rose <emily@contactvibe.com>
Eric Hanchrow <ehanchrow@ine.com> Eric Hanchrow <ehanchrow@ine.com>
Eric Myhre <hash@exultant.us> Eric Myhre <hash@exultant.us>
Erno Hopearuoho <erno.hopearuoho@gmail.com> Erno Hopearuoho <erno.hopearuoho@gmail.com>
@ -73,6 +75,7 @@ Louis Opter <kalessin@kalessin.fr>
Marco Hennings <marco.hennings@freiheit.com> Marco Hennings <marco.hennings@freiheit.com>
Marcus Farkas <toothlessgear@finitebox.com> Marcus Farkas <toothlessgear@finitebox.com>
Mark McGranaghan <mmcgrana@gmail.com> Mark McGranaghan <mmcgrana@gmail.com>
Martin Redmond <mrtodo@gmail.com>
Maxim Treskin <zerthurd@gmail.com> Maxim Treskin <zerthurd@gmail.com>
meejah <meejah@meejah.ca> meejah <meejah@meejah.ca>
Michael Crosby <crosby.michael@gmail.com> Michael Crosby <crosby.michael@gmail.com>
@ -103,6 +106,7 @@ Solomon Hykes <solomon@dotcloud.com>
Sridhar Ratnakumar <sridharr@activestate.com> Sridhar Ratnakumar <sridharr@activestate.com>
Stefan Praszalowicz <stefan@greplin.com> Stefan Praszalowicz <stefan@greplin.com>
Thatcher Peskens <thatcher@dotcloud.com> Thatcher Peskens <thatcher@dotcloud.com>
Thijs Terlouw <thijsterlouw@gmail.com>
Thomas Bikeev <thomas.bikeev@mac.com> Thomas Bikeev <thomas.bikeev@mac.com>
Thomas Hansen <thomas.hansen@gmail.com> Thomas Hansen <thomas.hansen@gmail.com>
Tianon Gravi <admwiggin@gmail.com> Tianon Gravi <admwiggin@gmail.com>

View File

@ -1,5 +1,24 @@
# Changelog # 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) ## 0.6.1 (2013-08-23)
* Registry: Pass "meta" headers in API calls to the registry * Registry: Pass "meta" headers in API calls to the registry
- Packaging: Use correct upstart script with new build tool - Packaging: Use correct upstart script with new build tool

View File

@ -23,7 +23,7 @@ that feature *on top of* docker.
### Discuss your design on the mailing list ### Discuss your design on the mailing list
We recommend discussing your plans [on the mailing 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. before starting to code - especially for more ambitious contributions.
This gives other contributors a chance to point you in the right This gives other contributors a chance to point you in the right
direction, give feedback on your design, and maybe point out if someone direction, give feedback on your design, and maybe point out if someone

View File

@ -1,5 +1,29 @@
# This file describes the standard way to build Docker, using docker # 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 from ubuntu:12.04
maintainer Solomon Hykes <solomon@dotcloud.com> maintainer Solomon Hykes <solomon@dotcloud.com>
# Build dependencies # Build dependencies
@ -11,7 +35,7 @@ run apt-get install -y -q mercurial
# Install Go # Install Go
run curl -s https://go.googlecode.com/files/go1.1.2.linux-amd64.tar.gz | tar -v -C /usr/local -xz 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 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 env CGO_ENABLED 0
run cd /tmp && echo 'package main' > t.go && go test -a -i -v run cd /tmp && echo 'package main' > t.go && go test -a -i -v
# Ubuntu stuff # Ubuntu stuff
@ -23,15 +47,12 @@ run apt-get install -y -q python-pip
run pip install s3cmd run pip install s3cmd
run pip install python-magic run pip install python-magic
run /bin/echo -e '[default]\naccess_key=$AWS_ACCESS_KEY\nsecret_key=$AWS_SECRET_KEY\n' > /.s3cfg run /bin/echo -e '[default]\naccess_key=$AWS_ACCESS_KEY\nsecret_key=$AWS_SECRET_KEY\n' > /.s3cfg
# Download dependencies # Runtime 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 apt-get install -y -q iptables
run PKG=github.com/gorilla/context/ REV=708054d61e5; git clone http://$PKG /go/src/$PKG && cd /go/src/$PKG && git checkout -f $REV run apt-get install -y -q lxc
run PKG=github.com/gorilla/mux/ REV=9b36453141c; git clone http://$PKG /go/src/$PKG && cd /go/src/$PKG && git checkout -f $REV volume /var/lib/docker
run PKG=github.com/dotcloud/tar/ REV=d06045a6d9; git clone http://$PKG /go/src/$PKG && cd /go/src/$PKG && git checkout -f $REV workdir /go/src/github.com/dotcloud/docker
run PKG=code.google.com/p/go.net/ REV=84a4013f96e0; hg clone http://$PKG /go/src/$PKG && cd /go/src/$PKG && hg checkout $REV # Wrap all commands in the "docker-in-docker" script to allow nested containers
entrypoint ["hack/dind"]
# Upload docker source # Upload docker source
add . /go/src/github.com/dotcloud/docker 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

1
FIXME
View File

@ -34,3 +34,4 @@ to put them - so we put them here :)
* entry point config * entry point config
* bring back git revision info, looks like it was lost * bring back git revision info, looks like it was lost
* Clean up the ProgressReader api, it's a PITA to use * Clean up the ProgressReader api, it's a PITA to use
* Use netlink instead of iproute2/iptables (#925)

286
README.md
View File

@ -1,8 +1,8 @@
Docker: the Linux container engine Docker: the Linux container engine
================================== ==================================
Docker is an open-source engine which automates the deployment of Docker is an open source project to pack, ship and run any application
applications as highly portable, self-sufficient containers. as a lightweight container
Docker containers are both *hardware-agnostic* and Docker containers are both *hardware-agnostic* and
*platform-agnostic*. This means that they can run anywhere, from your *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 accumulated over several years of large-scale operation and support of
hundreds of thousands of applications and databases. 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 ## 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. 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 We also offer an interactive tutorial for quickly learning the basics of using Docker.
curl https://get.docker.io | sudo sh -x
```
Binary installs
----------------
Docker supports the following binary installation methods. Note that For up-to-date install instructions and online tutorials, see the [Getting Started page](http://www.docker.io/gettingstarted/).
some methods are community contributions and not yet officially
supported.
* [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 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`` You can find a [list of real-world examples](http://docs.docker.io/en/latest/examples/) in the documentation.
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)"
```
Under the hood Under the hood
-------------- --------------
@ -305,149 +182,12 @@ Contributing to Docker
====================== ======================
Want to hack on Docker? Awesome! There are instructions to get you Want to hack on Docker? Awesome! There are instructions to get you
started on the website: started [here](CONTRIBUTING.md).
http://docs.docker.io/en/latest/contributing/contributing/
They are probably not perfect, please let us know if anything feels They are probably not perfect, please let us know if anything feels
wrong or incomplete. 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 ### Legal
Transfers of Docker shall be in accordance with applicable export Transfers of Docker shall be in accordance with applicable export

View File

@ -1 +1 @@
0.6.1 0.6.2

24
Vagrantfile vendored
View File

@ -12,22 +12,20 @@ Vagrant::Config.run do |config|
# Setup virtual machine box. This VM configuration code is always executed. # Setup virtual machine box. This VM configuration code is always executed.
config.vm.box = BOX_NAME config.vm.box = BOX_NAME
config.vm.box_url = BOX_URI 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? if Dir.glob("#{File.dirname(__FILE__)}/.vagrant/machines/default/*/id").empty?
# Add lxc-docker package # Add lxc-docker package
pkg_cmd = "apt-get update -qq; apt-get install -q -y python-software-properties; " \ pkg_cmd = "wget -q -O - https://get.docker.io/gpg | apt-key add -;" \
"add-apt-repository -y ppa:dotcloud/lxc-docker; apt-get update -qq; " \ "echo deb http://get.docker.io/ubuntu docker main > /etc/apt/sources.list.d/docker.list;" \
"apt-get install -q -y lxc-docker; " "apt-get update -qq; apt-get install -q -y --force-yes lxc-docker; "
# Add X.org Ubuntu backported 3.8 kernel # Add Ubuntu raring backported kernel
pkg_cmd << "add-apt-repository -y ppa:ubuntu-x-swat/r-lts-backport; " \ pkg_cmd << "apt-get update -qq; apt-get install -q -y linux-image-generic-lts-raring; "
"apt-get update -qq; apt-get install -q -y linux-image-3.8.0-19-generic; " # Add guest additions if local vbox VM. As virtualbox is the default provider,
# Add guest additions if local vbox VM # it is assumed it won't be explicitly stated.
is_vbox = true if ENV["VAGRANT_DEFAULT_PROVIDER"].nil? && ARGV.none? { |arg| arg.downcase.start_with?("--provider") }
ARGV.each do |arg| is_vbox &&= !arg.downcase.start_with?("--provider") end pkg_cmd << "apt-get install -q -y linux-headers-generic-lts-raring dkms; " \
if is_vbox
pkg_cmd << "apt-get install -q -y linux-headers-3.8.0-19-generic dkms; " \
"echo 'Downloading VBox Guest Additions...'; " \ "echo 'Downloading VBox Guest Additions...'; " \
"wget -q http://dlc.sun.com.edgesuite.net/virtualbox/4.2.12/VBoxGuestAdditions_4.2.12.iso; " "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 # Prepare the VM to add guest additions after reboot

221
api.go
View File

@ -2,6 +2,7 @@ package docker
import ( import (
"code.google.com/p/go.net/websocket" "code.google.com/p/go.net/websocket"
"encoding/base64"
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/dotcloud/docker/auth" "github.com/dotcloud/docker/auth"
@ -20,7 +21,7 @@ import (
"strings" "strings"
) )
const APIVERSION = 1.4 const APIVERSION = 1.5
const DEFAULTHTTPHOST = "127.0.0.1" const DEFAULTHTTPHOST = "127.0.0.1"
const DEFAULTHTTPPORT = 4243 const DEFAULTHTTPPORT = 4243
const DEFAULTUNIXSOCKET = "/var/run/docker.sock" const DEFAULTUNIXSOCKET = "/var/run/docker.sock"
@ -71,9 +72,18 @@ func httpError(w http.ResponseWriter, err error) {
http.Error(w, err.Error(), statusCode) 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.Header().Set("Content-Type", "application/json")
w.WriteHeader(code)
w.Write(b) w.Write(b)
return nil
} }
func getBoolParam(value string) (bool, error) { func getBoolParam(value string) (bool, error) {
@ -106,25 +116,14 @@ func postAuth(srv *Server, version float64, w http.ResponseWriter, r *http.Reque
return err return err
} }
if status != "" { if status != "" {
b, err := json.Marshal(&APIAuth{Status: status}) return writeJSON(w, http.StatusOK, &APIAuth{Status: status})
if err != nil {
return err
}
writeJSON(w, b)
return nil
} }
w.WriteHeader(http.StatusNoContent) w.WriteHeader(http.StatusNoContent)
return nil return nil
} }
func getVersion(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error { func getVersion(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
m := srv.DockerVersion() return writeJSON(w, http.StatusOK, srv.DockerVersion())
b, err := json.Marshal(m)
if err != nil {
return err
}
writeJSON(w, b)
return nil
} }
func postContainersKill(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 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 { if err != nil {
return err return err
} }
b, err := json.Marshal(outs)
if err != nil { return writeJSON(w, http.StatusOK, outs)
return err
}
writeJSON(w, b)
return nil
} }
func getImagesViz(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 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 { func getInfo(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
out := srv.DockerInfo() return writeJSON(w, http.StatusOK, srv.DockerInfo())
b, err := json.Marshal(out)
if err != nil {
return err
}
writeJSON(w, b)
return nil
} }
func getEvents(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 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 { if err != nil {
return err return err
} }
b, err := json.Marshal(outs)
if err != nil { return writeJSON(w, http.StatusOK, outs)
return err
}
writeJSON(w, b)
return nil
} }
func getContainersChanges(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 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 { if err != nil {
return err return err
} }
b, err := json.Marshal(changesStr)
if err != nil { return writeJSON(w, http.StatusOK, changesStr)
return err
}
writeJSON(w, b)
return nil
} }
func getContainersTop(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error { func getContainersTop(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
@ -299,12 +280,8 @@ func getContainersTop(srv *Server, version float64, w http.ResponseWriter, r *ht
if err != nil { if err != nil {
return err return err
} }
b, err := json.Marshal(procsStr)
if err != nil { return writeJSON(w, http.StatusOK, procsStr)
return err
}
writeJSON(w, b)
return nil
} }
func getContainersJSON(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 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) outs := srv.Containers(all, size, n, since, before)
b, err := json.Marshal(outs)
if err != nil { if version < 1.5 {
return err 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 { 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 { if err != nil {
return err return err
} }
b, err := json.Marshal(&APIID{id})
if err != nil { return writeJSON(w, http.StatusCreated, &APIID{id})
return err
}
w.WriteHeader(http.StatusCreated)
writeJSON(w, b)
return nil
} }
// Creates an image from Pull or from Import // 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") tag := r.Form.Get("tag")
repo := r.Form.Get("repo") 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 { if version > 1.0 {
w.Header().Set("Content-Type", "application/json") 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 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() { if sf.Used() {
w.Write(sf.FormatError(err)) w.Write(sf.FormatError(err))
return nil return nil
@ -434,12 +421,8 @@ func getImagesSearch(srv *Server, version float64, w http.ResponseWriter, r *htt
if err != nil { if err != nil {
return err return err
} }
b, err := json.Marshal(outs)
if err != nil { return writeJSON(w, http.StatusOK, outs)
return err
}
writeJSON(w, b)
return nil
} }
func postImagesInsert(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 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 return nil
} }
} }
b, err := json.Marshal(&APIID{ID: imgID})
if err != nil { return writeJSON(w, http.StatusOK, &APIID{ID: imgID})
return err
}
writeJSON(w, b)
return nil
} }
func postImagesPush(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error { func postImagesPush(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
authConfig := &auth.AuthConfig{}
metaHeaders := map[string][]string{} metaHeaders := map[string][]string{}
for k, v := range r.Header { for k, v := range r.Header {
if strings.HasPrefix(k, "X-Meta-") { if strings.HasPrefix(k, "X-Meta-") {
metaHeaders[k] = v metaHeaders[k] = v
} }
} }
if err := json.NewDecoder(r.Body).Decode(authConfig); err != nil {
return err
}
if err := parseForm(r); err != nil { if err := parseForm(r); err != nil {
return err 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 { if vars == nil {
return fmt.Errorf("Missing parameter") return fmt.Errorf("Missing parameter")
@ -518,7 +510,7 @@ func postContainersCreate(srv *Server, version float64, w http.ResponseWriter, r
return err 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)) out.Warnings = append(out.Warnings, fmt.Sprintf("Docker detected local DNS server on resolv.conf. Using default external servers: %v", defaultDns))
config.Dns = 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.") 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.") log.Println("Warning: IPv4 forwarding is disabled.")
out.Warnings = append(out.Warnings, "IPv4 forwarding is disabled.") out.Warnings = append(out.Warnings, "IPv4 forwarding is disabled.")
} }
b, err := json.Marshal(out) return writeJSON(w, http.StatusCreated, out)
if err != nil {
return err
}
w.WriteHeader(http.StatusCreated)
writeJSON(w, b)
return nil
} }
func postContainersRestart(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 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 imgs != nil {
if len(imgs) != 0 { if len(imgs) != 0 {
b, err := json.Marshal(imgs) return writeJSON(w, http.StatusOK, imgs)
if err != nil {
return err
}
writeJSON(w, b)
} else { } else {
return fmt.Errorf("Conflict, %s wasn't deleted", name) 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 { 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 // allow a nil body for backwards compatibility
if r.Body != nil { if r.Body != nil {
if matchesContentType(r.Header.Get("Content-Type"), "application/json") { if matchesContentType(r.Header.Get("Content-Type"), "application/json") {
hostConfig = &HostConfig{}
if err := json.NewDecoder(r.Body).Decode(hostConfig); err != nil { if err := json.NewDecoder(r.Body).Decode(hostConfig); err != nil {
return err return err
} }
@ -672,12 +654,8 @@ func postContainersWait(srv *Server, version float64, w http.ResponseWriter, r *
if err != nil { if err != nil {
return err return err
} }
b, err := json.Marshal(&APIWait{StatusCode: status})
if err != nil { return writeJSON(w, http.StatusOK, &APIWait{StatusCode: status})
return err
}
writeJSON(w, b)
return nil
} }
func postContainersResize(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 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 { if err != nil {
return err return err
} }
b, err := json.Marshal(container)
if err != nil { _, err = srv.ImageInspect(name)
return err 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 { 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 { if err != nil {
return err 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 { _, err = srv.ContainerInspect(name)
apiConfig := &APIImageConfig{} if err == nil {
if err := json.NewDecoder(r.Body).Decode(apiConfig); err != nil { return fmt.Errorf("Conflict between containers and images")
return err
} }
image, err := srv.ImageGetCached(apiConfig.ID, apiConfig.Config) return writeJSON(w, http.StatusOK, image)
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
} }
func postBuild(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 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") repoName := r.FormValue("t")
rawSuppressOutput := r.FormValue("q") rawSuppressOutput := r.FormValue("q")
rawNoCache := r.FormValue("nocache") rawNoCache := r.FormValue("nocache")
rawRm := r.FormValue("rm")
repoName, tag := utils.ParseRepositoryTag(repoName) repoName, tag := utils.ParseRepositoryTag(repoName)
var context io.Reader var context io.Reader
@ -926,8 +884,12 @@ func postBuild(srv *Server, version float64, w http.ResponseWriter, r *http.Requ
if err != nil { if err != nil {
return err 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) id, err := b.Build(context)
if err != nil { if err != nil {
fmt.Fprintf(w, "Error build: %s\n", err) 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:.*}/insert": postImagesInsert,
"/images/{name:.*}/push": postImagesPush, "/images/{name:.*}/push": postImagesPush,
"/images/{name:.*}/tag": postImagesTag, "/images/{name:.*}/tag": postImagesTag,
"/images/getCache": postImagesGetCache,
"/containers/create": postContainersCreate, "/containers/create": postContainersCreate,
"/containers/{name:.*}/kill": postContainersKill, "/containers/{name:.*}/kill": postContainersKill,
"/containers/{name:.*}/restart": postContainersRestart, "/containers/{name:.*}/restart": postContainersRestart,

View File

@ -1,5 +1,7 @@
package docker package docker
import "encoding/json"
type APIHistory struct { type APIHistory struct {
ID string `json:"Id"` ID string `json:"Id"`
Tags []string `json:",omitempty"` Tags []string `json:",omitempty"`
@ -42,6 +44,30 @@ type APIRmi struct {
} }
type APIContainers 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"` ID string `json:"Id"`
Image string Image string
Command string Command string
@ -67,7 +93,17 @@ type APIRun struct {
} }
type APIPort 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 { type APIVersion struct {

View File

@ -68,7 +68,7 @@ func TestGetInfo(t *testing.T) {
srv := &Server{runtime: runtime} srv := &Server{runtime: runtime}
initialImages, err := srv.runtime.graph.All() initialImages, err := srv.runtime.graph.Map()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -321,7 +321,7 @@ func TestGetContainersJSON(t *testing.T) {
srv := &Server{runtime: runtime} srv := &Server{runtime: runtime}
container, err := NewBuilder(runtime).Create(&Config{ container, err := runtime.Create(&Config{
Image: GetTestImage(runtime).ID, Image: GetTestImage(runtime).ID,
Cmd: []string{"echo", "test"}, Cmd: []string{"echo", "test"},
}) })
@ -357,10 +357,8 @@ func TestGetContainersExport(t *testing.T) {
srv := &Server{runtime: runtime} srv := &Server{runtime: runtime}
builder := NewBuilder(runtime)
// Create a container and remove a file // Create a container and remove a file
container, err := builder.Create( container, err := runtime.Create(
&Config{ &Config{
Image: GetTestImage(runtime).ID, Image: GetTestImage(runtime).ID,
Cmd: []string{"touch", "/test"}, Cmd: []string{"touch", "/test"},
@ -409,10 +407,8 @@ func TestGetContainersChanges(t *testing.T) {
srv := &Server{runtime: runtime} srv := &Server{runtime: runtime}
builder := NewBuilder(runtime)
// Create a container and remove a file // Create a container and remove a file
container, err := builder.Create( container, err := runtime.Create(
&Config{ &Config{
Image: GetTestImage(runtime).ID, Image: GetTestImage(runtime).ID,
Cmd: []string{"/bin/rm", "/etc/passwd"}, Cmd: []string{"/bin/rm", "/etc/passwd"},
@ -449,6 +445,7 @@ func TestGetContainersChanges(t *testing.T) {
} }
func TestGetContainersTop(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() runtime, err := newTestRuntime()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@ -457,9 +454,7 @@ func TestGetContainersTop(t *testing.T) {
srv := &Server{runtime: runtime} srv := &Server{runtime: runtime}
builder := NewBuilder(runtime) container, err := runtime.Create(
container, err := builder.Create(
&Config{ &Config{
Image: GetTestImage(runtime).ID, Image: GetTestImage(runtime).ID,
Cmd: []string{"/bin/sh", "-c", "cat"}, Cmd: []string{"/bin/sh", "-c", "cat"},
@ -540,10 +535,8 @@ func TestGetContainersByName(t *testing.T) {
srv := &Server{runtime: runtime} srv := &Server{runtime: runtime}
builder := NewBuilder(runtime)
// Create a container and remove a file // Create a container and remove a file
container, err := builder.Create( container, err := runtime.Create(
&Config{ &Config{
Image: GetTestImage(runtime).ID, Image: GetTestImage(runtime).ID,
Cmd: []string{"echo", "test"}, Cmd: []string{"echo", "test"},
@ -573,10 +566,9 @@ func TestPostCommit(t *testing.T) {
srv := &Server{runtime: runtime} srv := &Server{runtime: runtime}
builder := NewBuilder(runtime)
// Create a container and remove a file // Create a container and remove a file
container, err := builder.Create( container, err := runtime.Create(
&Config{ &Config{
Image: GetTestImage(runtime).ID, Image: GetTestImage(runtime).ID,
Cmd: []string{"touch", "/test"}, Cmd: []string{"touch", "/test"},
@ -670,7 +662,7 @@ func TestPostContainersKill(t *testing.T) {
srv := &Server{runtime: runtime} srv := &Server{runtime: runtime}
container, err := NewBuilder(runtime).Create( container, err := runtime.Create(
&Config{ &Config{
Image: GetTestImage(runtime).ID, Image: GetTestImage(runtime).ID,
Cmd: []string{"/bin/cat"}, Cmd: []string{"/bin/cat"},
@ -712,7 +704,7 @@ func TestPostContainersRestart(t *testing.T) {
srv := &Server{runtime: runtime} srv := &Server{runtime: runtime}
container, err := NewBuilder(runtime).Create( container, err := runtime.Create(
&Config{ &Config{
Image: GetTestImage(runtime).ID, Image: GetTestImage(runtime).ID,
Cmd: []string{"/bin/cat"}, Cmd: []string{"/bin/cat"},
@ -766,7 +758,7 @@ func TestPostContainersStart(t *testing.T) {
srv := &Server{runtime: runtime} srv := &Server{runtime: runtime}
container, err := NewBuilder(runtime).Create( container, err := runtime.Create(
&Config{ &Config{
Image: GetTestImage(runtime).ID, Image: GetTestImage(runtime).ID,
Cmd: []string{"/bin/cat"}, Cmd: []string{"/bin/cat"},
@ -816,7 +808,7 @@ func TestPostContainersStop(t *testing.T) {
srv := &Server{runtime: runtime} srv := &Server{runtime: runtime}
container, err := NewBuilder(runtime).Create( container, err := runtime.Create(
&Config{ &Config{
Image: GetTestImage(runtime).ID, Image: GetTestImage(runtime).ID,
Cmd: []string{"/bin/cat"}, Cmd: []string{"/bin/cat"},
@ -863,7 +855,7 @@ func TestPostContainersWait(t *testing.T) {
srv := &Server{runtime: runtime} srv := &Server{runtime: runtime}
container, err := NewBuilder(runtime).Create( container, err := runtime.Create(
&Config{ &Config{
Image: GetTestImage(runtime).ID, Image: GetTestImage(runtime).ID,
Cmd: []string{"/bin/sleep", "1"}, Cmd: []string{"/bin/sleep", "1"},
@ -905,7 +897,7 @@ func TestPostContainersAttach(t *testing.T) {
srv := &Server{runtime: runtime} srv := &Server{runtime: runtime}
container, err := NewBuilder(runtime).Create( container, err := runtime.Create(
&Config{ &Config{
Image: GetTestImage(runtime).ID, Image: GetTestImage(runtime).ID,
Cmd: []string{"/bin/cat"}, Cmd: []string{"/bin/cat"},
@ -997,7 +989,7 @@ func TestDeleteContainers(t *testing.T) {
srv := &Server{runtime: runtime} srv := &Server{runtime: runtime}
container, err := NewBuilder(runtime).Create(&Config{ container, err := runtime.Create(&Config{
Image: GetTestImage(runtime).ID, Image: GetTestImage(runtime).ID,
Cmd: []string{"touch", "/test"}, Cmd: []string{"touch", "/test"},
}) })
@ -1184,10 +1176,8 @@ func TestPostContainersCopy(t *testing.T) {
srv := &Server{runtime: runtime} srv := &Server{runtime: runtime}
builder := NewBuilder(runtime)
// Create a container and remove a file // Create a container and remove a file
container, err := builder.Create( container, err := runtime.Create(
&Config{ &Config{
Image: GetTestImage(runtime).ID, Image: GetTestImage(runtime).ID,
Cmd: []string{"touch", "/test.txt"}, Cmd: []string{"touch", "/test.txt"},

View File

@ -1 +0,0 @@
../registry/MAINTAINERS

3
auth/MAINTAINERS Normal file
View File

@ -0,0 +1,3 @@
Sam Alba <sam@dotcloud.com> (@samalba)
Joffrey Fuhrer <joffrey@dotcloud.com> (@shin-)
Ken Cochrane <ken@dotcloud.com> (@kencochrane)

View File

@ -26,10 +26,11 @@ var (
) )
type AuthConfig struct { type AuthConfig struct {
Username string `json:"username,omitempty"` Username string `json:"username,omitempty"`
Password string `json:"password,omitempty"` Password string `json:"password,omitempty"`
Auth string `json:"auth"` Auth string `json:"auth"`
Email string `json:"email"` Email string `json:"email"`
ServerAddress string `json:"serveraddress,omitempty"`
} }
type ConfigFile struct { type ConfigFile struct {
@ -96,6 +97,7 @@ func LoadConfig(rootPath string) (*ConfigFile, error) {
} }
origEmail := strings.Split(arr[1], " = ") origEmail := strings.Split(arr[1], " = ")
authConfig.Email = origEmail[1] authConfig.Email = origEmail[1]
authConfig.ServerAddress = IndexServerAddress()
configFile.Configs[IndexServerAddress()] = authConfig configFile.Configs[IndexServerAddress()] = authConfig
} else { } else {
for k, authConfig := range configFile.Configs { for k, authConfig := range configFile.Configs {
@ -105,6 +107,7 @@ func LoadConfig(rootPath string) (*ConfigFile, error) {
} }
authConfig.Auth = "" authConfig.Auth = ""
configFile.Configs[k] = authConfig configFile.Configs[k] = authConfig
authConfig.ServerAddress = k
} }
} }
return &configFile, nil return &configFile, nil
@ -125,7 +128,7 @@ func SaveConfig(configFile *ConfigFile) error {
authCopy.Auth = encodeAuth(&authCopy) authCopy.Auth = encodeAuth(&authCopy)
authCopy.Username = "" authCopy.Username = ""
authCopy.Password = "" authCopy.Password = ""
authCopy.ServerAddress = ""
configs[k] = authCopy configs[k] = authCopy
} }
@ -146,14 +149,26 @@ func Login(authConfig *AuthConfig, factory *utils.HTTPRequestFactory) (string, e
reqStatusCode := 0 reqStatusCode := 0
var status string var status string
var reqBody []byte 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 { if err != nil {
return "", fmt.Errorf("Config Error: %s", err) return "", fmt.Errorf("Config Error: %s", err)
} }
// using `bytes.NewReader(jsonBody)` here causes the server to respond with a 411 status. // using `bytes.NewReader(jsonBody)` here causes the server to respond with a 411 status.
b := strings.NewReader(string(jsonBody)) 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 { if err != nil {
return "", fmt.Errorf("Server Error: %s", err) return "", fmt.Errorf("Server Error: %s", err)
} }
@ -165,14 +180,23 @@ func Login(authConfig *AuthConfig, factory *utils.HTTPRequestFactory) (string, e
} }
if reqStatusCode == 201 { if reqStatusCode == 201 {
status = "Account created. Please use the confirmation link we sent" + if loginAgainstOfficialIndex {
" to your e-mail to activate it." 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 { } else if reqStatusCode == 403 {
return "", fmt.Errorf("Login: Your account hasn't been activated. " + if loginAgainstOfficialIndex {
"Please check your e-mail for a confirmation link.") 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 { } else if reqStatusCode == 400 {
if string(reqBody) == "\"Username or email already exists\"" { 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) req.SetBasicAuth(authConfig.Username, authConfig.Password)
resp, err := client.Do(req) resp, err := client.Do(req)
if err != nil { if err != nil {
@ -199,3 +223,52 @@ func Login(authConfig *AuthConfig, factory *utils.HTTPRequestFactory) (string, e
} }
return status, nil 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)
}

View File

@ -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
}

View File

@ -1,7 +1,6 @@
package docker package docker
import ( import (
"bufio"
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/dotcloud/docker/utils" "github.com/dotcloud/docker/utils"
@ -23,7 +22,6 @@ type BuildFile interface {
type buildFile struct { type buildFile struct {
runtime *Runtime runtime *Runtime
builder *Builder
srv *Server srv *Server
image string image string
@ -32,6 +30,7 @@ type buildFile struct {
context string context string
verbose bool verbose bool
utilizeCache bool utilizeCache bool
rm bool
tmpContainers map[string]struct{} tmpContainers map[string]struct{}
tmpImages map[string]struct{} tmpImages map[string]struct{}
@ -39,15 +38,11 @@ type buildFile struct {
out io.Writer out io.Writer
} }
func (b *buildFile) clearTmp(containers, images map[string]struct{}) { func (b *buildFile) clearTmp(containers map[string]struct{}) {
for c := range containers { for c := range containers {
tmp := b.runtime.Get(c) tmp := b.runtime.Get(c)
b.runtime.Destroy(tmp) b.runtime.Destroy(tmp)
utils.Debugf("Removing container %s", c) fmt.Fprintf(b.out, "Removing intermediate container %s\n", utils.TruncateID(c))
}
for i := range images {
b.runtime.graph.Delete(i)
utils.Debugf("Removing image %s", i)
} }
} }
@ -293,7 +288,7 @@ func (b *buildFile) addContext(container *Container, orig, dest string) error {
} }
fi, err := os.Stat(origPath) fi, err := os.Stat(origPath)
if err != nil { if err != nil {
return err return fmt.Errorf("%s: no such file or directory", orig)
} }
if fi.IsDir() { if fi.IsDir() {
if err := CopyWithTar(origPath, destPath); err != nil { if err := CopyWithTar(origPath, destPath); err != nil {
@ -337,7 +332,7 @@ func (b *buildFile) CmdAdd(args string) error {
b.config.Image = b.image b.config.Image = b.image
// Create the container and start it // Create the container and start it
container, err := b.builder.Create(b.config) container, err := b.runtime.Create(b.config)
if err != nil { if err != nil {
return err return err
} }
@ -372,7 +367,7 @@ func (b *buildFile) run() (string, error) {
b.config.Image = b.image b.config.Image = b.image
// Create the container and start it // Create the container and start it
c, err := b.builder.Create(b.config) c, err := b.runtime.Create(b.config)
if err != nil { if err != nil {
return "", err 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 { if err != nil {
return err return err
} }
@ -450,7 +445,7 @@ func (b *buildFile) commit(id string, autoCmd []string, comment string) error {
autoConfig := *b.config autoConfig := *b.config
autoConfig.Cmd = autoCmd autoConfig.Cmd = autoCmd
// Commit the container // Commit the container
image, err := b.builder.Commit(container, "", "", "", b.maintainer, &autoConfig) image, err := b.runtime.Commit(container, "", "", "", b.maintainer, &autoConfig)
if err != nil { if err != nil {
return err return err
} }
@ -459,6 +454,9 @@ func (b *buildFile) commit(id string, autoCmd []string, comment string) error {
return nil 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) { func (b *buildFile) Build(context io.Reader) (string, error) {
// FIXME: @creack any reason for using /tmp instead of ""? // FIXME: @creack any reason for using /tmp instead of ""?
// FIXME: @creack "name" is a terrible variable name // 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) defer os.RemoveAll(name)
b.context = name b.context = name
dockerfile, err := os.Open(path.Join(name, "Dockerfile")) filename := path.Join(name, "Dockerfile")
if err != nil { if _, err := os.Stat(filename); os.IsNotExist(err) {
return "", fmt.Errorf("Can't build a directory with no Dockerfile") return "", fmt.Errorf("Can't build a directory with no Dockerfile")
} }
// FIXME: "file" is also a terrible variable name ;) fileBytes, err := ioutil.ReadFile(filename)
file := bufio.NewReader(dockerfile) if err != nil {
return "", err
}
dockerfile := string(fileBytes)
dockerfile = lineContinuation.ReplaceAllString(dockerfile, "")
stepN := 0 stepN := 0
for { for _, line := range strings.Split(dockerfile, "\n") {
line, err := file.ReadString('\n')
if err != nil {
if err == io.EOF && line == "" {
break
} else if err != io.EOF {
return "", err
}
}
line = strings.Trim(strings.Replace(line, "\t", " ", -1), " \t\r\n") line = strings.Trim(strings.Replace(line, "\t", " ", -1), " \t\r\n")
// Skip comments and empty line // Skip comments and empty line
if len(line) == 0 || line[0] == '#' { if len(line) == 0 || line[0] == '#' {
@ -517,14 +511,16 @@ func (b *buildFile) Build(context io.Reader) (string, error) {
} }
if b.image != "" { if b.image != "" {
fmt.Fprintf(b.out, "Successfully built %s\n", utils.TruncateID(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 b.image, nil
} }
return "", fmt.Errorf("An error occurred during the build\n") 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{ return &buildFile{
builder: NewBuilder(srv.runtime),
runtime: srv.runtime, runtime: srv.runtime,
srv: srv, srv: srv,
config: &Config{}, config: &Config{},
@ -533,5 +529,6 @@ func NewBuildFile(srv *Server, out io.Writer, verbose, utilizeCache bool) BuildF
tmpImages: make(map[string]struct{}), tmpImages: make(map[string]struct{}),
verbose: verbose, verbose: verbose,
utilizeCache: utilizeCache, utilizeCache: utilizeCache,
rm: rm,
} }
} }

View File

@ -45,6 +45,34 @@ run [ "$(ls -d /var/run/sshd)" = "/var/run/sshd" ]
nil, 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} from {IMAGE}
@ -229,7 +257,7 @@ func buildImage(context testContextTemplate, t *testing.T, srv *Server, useCache
ip := srv.runtime.networkManager.bridgeNetwork.IP ip := srv.runtime.networkManager.bridgeNetwork.IP
dockerfile := constructDockerfile(context.dockerfile, ip, port) 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)) id, err := buildfile.Build(mkTestContext(dockerfile, context.files, t))
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@ -470,7 +498,7 @@ func TestForbiddenContextPath(t *testing.T) {
ip := srv.runtime.networkManager.bridgeNetwork.IP ip := srv.runtime.networkManager.bridgeNetwork.IP
dockerfile := constructDockerfile(context.dockerfile, ip, port) 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)) _, err = buildfile.Build(mkTestContext(dockerfile, context.files, t))
if err == nil { if err == nil {
@ -483,3 +511,51 @@ func TestForbiddenContextPath(t *testing.T) {
t.Fail() 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()
}
}

View File

@ -99,7 +99,7 @@ func Changes(layers []string, rw string) ([]Change, error) {
changes = append(changes, change) changes = append(changes, change)
return nil return nil
}) })
if err != nil { if err != nil && !os.IsNotExist(err) {
return nil, err return nil, err
} }
return changes, nil return changes, nil

View File

@ -2,11 +2,14 @@ package docker
import ( import (
"archive/tar" "archive/tar"
"bufio"
"bytes" "bytes"
"encoding/base64"
"encoding/json" "encoding/json"
"flag" "flag"
"fmt" "fmt"
"github.com/dotcloud/docker/auth" "github.com/dotcloud/docker/auth"
"github.com/dotcloud/docker/registry"
"github.com/dotcloud/docker/term" "github.com/dotcloud/docker/term"
"github.com/dotcloud/docker/utils" "github.com/dotcloud/docker/utils"
"io" "io"
@ -19,17 +22,18 @@ import (
"os/signal" "os/signal"
"path/filepath" "path/filepath"
"reflect" "reflect"
"runtime"
"sort"
"strconv" "strconv"
"strings" "strings"
"syscall" "syscall"
"text/tabwriter" "text/tabwriter"
"time" "time"
"unicode"
) )
var ( var (
GITCOMMIT string GITCOMMIT string
VERSION string VERSION string
) )
func (cli *DockerCli) getMethod(name string) (reflect.Method, bool) { 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("url", cmd.Arg(1))
v.Set("path", cmd.Arg(2)) 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 err
} }
return nil 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") 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") suppressOutput := cmd.Bool("q", false, "Suppress verbose build output")
noCache := cmd.Bool("no-cache", false, "Do not use cache when building the image") 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 { if err := cmd.Parse(args); err != nil {
return nil return nil
} }
@ -211,6 +216,9 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
if *noCache { if *noCache {
v.Set("nocache", "1") 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) req, err := http.NewRequest("POST", fmt.Sprintf("/v%g/build?%s", APIVERSION, v.Encode()), body)
if err != nil { if err != nil {
return err return err
@ -251,75 +259,27 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
// 'docker login': login / register a user to registry service. // 'docker login': login / register a user to registry service.
func (cli *DockerCli) CmdLogin(args ...string) error { func (cli *DockerCli) CmdLogin(args ...string) error {
var readStringOnRawTerminal = func(stdin io.Reader, stdout io.Writer, echo bool) string { cmd := Subcmd("login", "[OPTIONS] [SERVER]", "Register or Login to a docker registry server, if no server is specified \""+auth.IndexServerAddress()+"\" is the default.")
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]", "Register or Login to the docker registry server") var username, password, email string
flUsername := cmd.String("u", "", "username")
flPassword := cmd.String("p", "", "password") cmd.StringVar(&username, "u", "", "username")
flEmail := cmd.String("e", "", "email") cmd.StringVar(&password, "p", "", "password")
cmd.StringVar(&email, "e", "", "email")
err := cmd.Parse(args) err := cmd.Parse(args)
if err != nil { if err != nil {
return nil return nil
} }
serverAddress := auth.IndexServerAddress()
cli.LoadConfigFile() if len(cmd.Args()) > 0 {
serverAddress, err = registry.ExpandAndVerifyRegistryUrl(cmd.Arg(0))
var oldState *term.State
if *flUsername == "" || *flPassword == "" || *flEmail == "" {
oldState, err = term.SetRawTerminal(cli.terminalFd)
if err != nil { if err != nil {
return err return err
} }
defer term.RestoreTerminal(cli.terminalFd, oldState) fmt.Fprintf(cli.out, "Login against server at %s\n", serverAddress)
} }
var ( promptDefault := func(prompt string, configDefault string) {
username string
password string
email string
)
var promptDefault = func(prompt string, configDefault string) {
if configDefault == "" { if configDefault == "" {
fmt.Fprintf(cli.out, "%s: ", prompt) fmt.Fprintf(cli.out, "%s: ", prompt)
} else { } 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()] authconfig, ok := cli.configFile.Configs[auth.IndexServerAddress()]
if !ok { if !ok {
authconfig = auth.AuthConfig{} authconfig = auth.AuthConfig{}
} }
if *flUsername == "" { if username == "" {
promptDefault("Username", authconfig.Username) promptDefault("Username", authconfig.Username)
username = readAndEchoString(cli.in, cli.out) username = readInput(cli.in, cli.out)
if username == "" { if username == "" {
username = authconfig.Username username = authconfig.Username
} }
} else {
username = *flUsername
} }
if username != authconfig.Username { if username != authconfig.Username {
if *flPassword == "" { if password == "" {
oldState, _ := term.SaveState(cli.terminalFd)
fmt.Fprintf(cli.out, "Password: ") 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 == "" { if password == "" {
return fmt.Errorf("Error : Password Required") return fmt.Errorf("Error : Password Required")
} }
} else {
password = *flPassword
} }
if *flEmail == "" { if email == "" {
promptDefault("Email", authconfig.Email) promptDefault("Email", authconfig.Email)
email = readAndEchoString(cli.in, cli.out) email = readInput(cli.in, cli.out)
if email == "" { if email == "" {
email = authconfig.Email email = authconfig.Email
} }
} else {
email = *flEmail
} }
} else { } else {
password = authconfig.Password password = authconfig.Password
email = authconfig.Email email = authconfig.Email
} }
if oldState != nil {
term.RestoreTerminal(cli.terminalFd, oldState)
}
authconfig.Username = username authconfig.Username = username
authconfig.Password = password authconfig.Password = password
authconfig.Email = email 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 { if statusCode == 401 {
delete(cli.configFile.Configs, auth.IndexServerAddress()) delete(cli.configFile.Configs, serverAddress)
auth.SaveConfig(cli.configFile) auth.SaveConfig(cli.configFile)
return err return err
} }
@ -407,16 +376,11 @@ func (cli *DockerCli) CmdWait(args ...string) error {
return nil return nil
} }
for _, name := range cmd.Args() { for _, name := range cmd.Args() {
body, _, err := cli.call("POST", "/containers/"+name+"/wait", nil) status, err := waitForExit(cli, name)
if err != nil { if err != nil {
fmt.Fprintf(cli.err, "%s", err) fmt.Fprintf(cli.err, "%s", err)
} else { } else {
var out APIWait fmt.Fprintf(cli.out, "%d\n", status)
err = json.Unmarshal(body, &out)
if err != nil {
return err
}
fmt.Fprintf(cli.out, "%d\n", out.StatusCode)
} }
} }
return nil return nil
@ -433,6 +397,13 @@ func (cli *DockerCli) CmdVersion(args ...string) error {
cmd.Usage() cmd.Usage()
return nil 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) body, _, err := cli.call("GET", "/version", nil)
if err != 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) utils.Debugf("Error unmarshal: body: %s, err: %s\n", body, err)
return err return err
} }
fmt.Fprintf(cli.out, "Client version: %s\n", VERSION) if out.Version != "" {
fmt.Fprintf(cli.out, "Server version: %s\n", out.Version) fmt.Fprintf(cli.out, "Server version: %s\n", out.Version)
}
if out.GitCommit != "" { 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 != "" { 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() release := utils.GetReleaseVersion()
if release != "" { if release != "" {
fmt.Fprintf(cli.out, "Last stable version: %s", 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, ", please update docker")
} }
fmt.Fprintf(cli.out, "\n") fmt.Fprintf(cli.out, "\n")
@ -578,15 +550,17 @@ func (cli *DockerCli) CmdStart(args ...string) error {
return nil return nil
} }
var encounteredError error
for _, name := range args { for _, name := range args {
_, _, err := cli.call("POST", "/containers/"+name+"/start", nil) _, _, err := cli.call("POST", "/containers/"+name+"/start", nil)
if err != nil { if err != nil {
fmt.Fprintf(cli.err, "%s\n", err) fmt.Fprintf(cli.err, "%s\n", err)
encounteredError = fmt.Errorf("Error: failed to start one or more containers")
} else { } else {
fmt.Fprintf(cli.out, "%s\n", name) fmt.Fprintf(cli.out, "%s\n", name)
} }
} }
return nil return encounteredError
} }
func (cli *DockerCli) CmdInspect(args ...string) error { func (cli *DockerCli) CmdInspect(args ...string) error {
@ -607,7 +581,7 @@ func (cli *DockerCli) CmdInspect(args ...string) error {
if err != nil { if err != nil {
obj, _, err = cli.call("GET", "/images/"+name+"/json", nil) obj, _, err = cli.call("GET", "/images/"+name+"/json", nil)
if err != nil { if err != nil {
fmt.Fprintf(cli.err, "%s\n", err) fmt.Fprintf(cli.err, "No such image or container: %s\n", name)
continue continue
} }
} }
@ -821,7 +795,7 @@ func (cli *DockerCli) CmdImport(args ...string) error {
v.Set("tag", tag) v.Set("tag", tag)
v.Set("fromSrc", src) 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 { if err != nil {
return err return err
} }
@ -842,6 +816,13 @@ func (cli *DockerCli) CmdPush(args ...string) error {
cli.LoadConfigFile() 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 // If we're not using a custom registry, we know the restrictions
// applied to repository names and can warn the user in advance. // applied to repository names and can warn the user in advance.
// Custom repositories can have different rules, and we must also // Custom repositories can have different rules, and we must also
@ -855,22 +836,28 @@ func (cli *DockerCli) CmdPush(args ...string) error {
} }
v := url.Values{} v := url.Values{}
push := func() error { push := func(authConfig auth.AuthConfig) error {
buf, err := json.Marshal(cli.configFile.Configs[auth.IndexServerAddress()]) buf, err := json.Marshal(authConfig)
if err != nil { if err != nil {
return err 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 := push(authConfig); err != nil {
if err.Error() == "Authentication is required." { if err.Error() == registry.ErrLoginRequired.Error() {
fmt.Fprintln(cli.out, "\nPlease login prior to push:") 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 err
} }
return push() authConfig := cli.configFile.ResolveAuthConfig(endpoint)
return push(authConfig)
} }
return err return err
} }
@ -894,11 +881,43 @@ func (cli *DockerCli) CmdPull(args ...string) error {
*tag = parsedTag *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 := url.Values{}
v.Set("fromImage", remote) v.Set("fromImage", remote)
v.Set("tag", *tag) 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 return err
} }
@ -988,6 +1007,19 @@ func (cli *DockerCli) CmdImages(args ...string) error {
return nil 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 { func (cli *DockerCli) CmdPs(args ...string) error {
cmd := Subcmd("ps", "[OPTIONS]", "List containers") cmd := Subcmd("ps", "[OPTIONS]", "List containers")
quiet := cmd.Bool("q", false, "Only display numeric IDs") quiet := cmd.Bool("q", false, "Only display numeric IDs")
@ -1045,9 +1077,9 @@ func (cli *DockerCli) CmdPs(args ...string) error {
for _, out := range outs { for _, out := range outs {
if !*quiet { if !*quiet {
if *noTrunc { 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 { } 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 *size {
if out.SizeRootFs > 0 { if out.SizeRootFs > 0 {
@ -1132,7 +1164,7 @@ func (cli *DockerCli) CmdEvents(args ...string) error {
v.Set("since", *since) 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 err
} }
return nil return nil
@ -1149,7 +1181,7 @@ func (cli *DockerCli) CmdExport(args ...string) error {
return nil 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 err
} }
return nil return nil
@ -1415,13 +1447,36 @@ func (cli *DockerCli) CmdRun(args ...string) error {
tag = DEFAULTTAG 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{} v := url.Values{}
repos, tag := utils.ParseRepositoryTag(config.Image) repos, tag := utils.ParseRepositoryTag(config.Image)
v.Set("fromImage", repos) v.Set("fromImage", repos)
v.Set("tag", tag) 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 { if err != nil {
return err return err
} }
@ -1507,8 +1562,18 @@ func (cli *DockerCli) CmdRun(args ...string) error {
} }
if !config.AttachStdout && !config.AttachStderr { if !config.AttachStdout && !config.AttachStderr {
// Detached mode
<-wait <-wait
} else {
status, err := waitForExit(cli, runResult.ID)
if err != nil {
return err
}
if status != 0 {
return &utils.StatusError{status}
}
} }
return nil return nil
} }
@ -1526,6 +1591,10 @@ func (cli *DockerCli) CmdCp(args ...string) error {
var copyData APICopy var copyData APICopy
info := strings.Split(cmd.Arg(0), ":") info := strings.Split(cmd.Arg(0), ":")
if len(info) != 2 {
return fmt.Errorf("Error: Resource not specified")
}
copyData.Resource = info[1] copyData.Resource = info[1]
copyData.HostPath = cmd.Arg(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 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 { if (method == "POST" || method == "PUT") && in == nil {
in = bytes.NewReader([]byte{}) 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" { if method == "POST" {
req.Header.Set("Content-Type", "plain/text") 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) dial, err := net.Dial(cli.proto, cli.addr)
if err != nil { if err != nil {
if strings.Contains(err.Error(), "connection refused") { if strings.Contains(err.Error(), "connection refused") {
@ -1785,6 +1861,19 @@ func (cli *DockerCli) LoadConfigFile() (err error) {
return err 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 { func NewDockerCli(in io.ReadCloser, out, err io.Writer, proto, addr string) *DockerCli {
var ( var (
isTerminal = false isTerminal = false

View File

@ -152,7 +152,6 @@ func TestRunWorkdirExists(t *testing.T) {
} }
func TestRunExit(t *testing.T) { func TestRunExit(t *testing.T) {
stdin, stdinPipe := io.Pipe() stdin, stdinPipe := io.Pipe()
stdout, stdoutPipe := io.Pipe() stdout, stdoutPipe := io.Pipe()

View File

@ -11,11 +11,11 @@ import (
"io" "io"
"io/ioutil" "io/ioutil"
"log" "log"
"net"
"os" "os"
"os/exec" "os/exec"
"path" "path"
"path/filepath" "path/filepath"
"sort"
"strconv" "strconv"
"strings" "strings"
"syscall" "syscall"
@ -41,6 +41,8 @@ type Container struct {
SysInitPath string SysInitPath string
ResolvConfPath string ResolvConfPath string
HostnamePath string
HostsPath string
cmd *exec.Cmd cmd *exec.Cmd
stdout *utils.WriteBroadcaster stdout *utils.WriteBroadcaster
@ -60,6 +62,7 @@ type Container struct {
type Config struct { type Config struct {
Hostname string Hostname string
Domainname string
User string User string
Memory int64 // Memory limit (in bytes) Memory int64 // Memory limit (in bytes)
MemorySwap int64 // Total memory usage (memory + swap); set `-1' to disable swap 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) { 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") 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.SetOutput(ioutil.Discard)
cmd.Usage = nil cmd.Usage = nil
} }
@ -202,8 +205,17 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig,
return nil, nil, cmd, err 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{ config := &Config{
Hostname: *flHostname, Hostname: hostname,
Domainname: domainname,
PortSpecs: flPorts, PortSpecs: flPorts,
User: *flUser, User: *flUser,
Tty: *flTty, Tty: *flTty,
@ -252,17 +264,28 @@ type NetworkSettings struct {
PortMapping map[string]PortMapping PortMapping map[string]PortMapping
} }
// String returns a human-readable description of the port mapping defined in the settings // returns a more easy to process description of the port mapping defined in the settings
func (settings *NetworkSettings) PortMappingHuman() string { func (settings *NetworkSettings) PortMappingAPI() []APIPort {
var mapping []string var mapping []APIPort
for private, public := range settings.PortMapping["Tcp"] { 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"] { 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 mapping
return strings.Join(mapping, ", ")
} }
// Inject the io.Reader at the given path. Note: do not close the reader // 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() container.State.Lock()
defer container.State.Unlock() 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() hostConfig, _ = container.ReadHostConfig()
} }
@ -625,7 +648,7 @@ func (container *Container) Start(hostConfig *HostConfig) error {
continue continue
} }
if err := os.MkdirAll(path.Join(container.RootfsPath(), volPath), 0755); err != nil { if err := os.MkdirAll(path.Join(container.RootfsPath(), volPath), 0755); err != nil {
return nil return err
} }
container.Volumes[volPath] = id container.Volumes[volPath] = id
if isRW, exists := c.VolumesRW[volPath]; exists { if isRW, exists := c.VolumesRW[volPath]; exists {
@ -641,11 +664,13 @@ func (container *Container) Start(hostConfig *HostConfig) error {
if _, exists := container.Volumes[volPath]; exists { if _, exists := container.Volumes[volPath]; exists {
continue continue
} }
var srcPath string
srcRW := false
// If an external bind is defined for this volume, use that as a source // If an external bind is defined for this volume, use that as a source
if bindMap, exists := binds[volPath]; exists { if bindMap, exists := binds[volPath]; exists {
container.Volumes[volPath] = bindMap.SrcPath srcPath = bindMap.SrcPath
if strings.ToLower(bindMap.Mode) == "rw" { if strings.ToLower(bindMap.Mode) == "rw" {
container.VolumesRW[volPath] = true srcRW = true
} }
// Otherwise create an directory in $ROOT/volumes/ and use that // Otherwise create an directory in $ROOT/volumes/ and use that
} else { } else {
@ -653,17 +678,49 @@ func (container *Container) Start(hostConfig *HostConfig) error {
if err != nil { if err != nil {
return err return err
} }
srcPath, err := c.layer() srcPath, err = c.layer()
if err != nil { if err != nil {
return err return err
} }
container.Volumes[volPath] = srcPath srcRW = true // RW by default
container.VolumesRW[volPath] = true // RW by default
} }
container.Volumes[volPath] = srcPath
container.VolumesRW[volPath] = srcRW
// Create the mountpoint // 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 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 { if err := container.generateLXCConfig(hostConfig); err != nil {
@ -799,14 +856,44 @@ func (container *Container) allocateNetwork() error {
return nil return nil
} }
iface, err := container.runtime.networkManager.Allocate() var iface *NetworkInterface
if err != nil { var err error
return err 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 = make(map[string]PortMapping)
container.NetworkSettings.PortMapping["Tcp"] = make(PortMapping) container.NetworkSettings.PortMapping["Tcp"] = make(PortMapping)
container.NetworkSettings.PortMapping["Udp"] = make(PortMapping) container.NetworkSettings.PortMapping["Udp"] = make(PortMapping)
for _, spec := range container.Config.PortSpecs { for _, spec := range portSpecs {
nat, err := iface.AllocatePort(spec) nat, err := iface.AllocatePort(spec)
if err != nil { if err != nil {
iface.Release() iface.Release()

View File

@ -18,7 +18,7 @@ import (
func TestIDFormat(t *testing.T) { func TestIDFormat(t *testing.T) {
runtime := mkRuntime(t) runtime := mkRuntime(t)
defer nuke(runtime) defer nuke(runtime)
container1, err := NewBuilder(runtime).Create( container1, err := runtime.Create(
&Config{ &Config{
Image: GetTestImage(runtime).ID, Image: GetTestImage(runtime).ID,
Cmd: []string{"/bin/sh", "-c", "echo hello world"}, 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) container1, _, _ := mkContainer(runtime, []string{"_", "/bin/rm", "/etc/passwd"}, t)
defer runtime.Destroy(container1) 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 { if err := container1.Run(); err != nil {
t.Fatal(err) t.Fatal(err)
} }
// Check the changelog // Check the changelog
c, err := container1.Changes() c, err = container1.Changes()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -379,7 +388,7 @@ func TestRun(t *testing.T) {
func TestOutput(t *testing.T) { func TestOutput(t *testing.T) {
runtime := mkRuntime(t) runtime := mkRuntime(t)
defer nuke(runtime) defer nuke(runtime)
container, err := NewBuilder(runtime).Create( container, err := runtime.Create(
&Config{ &Config{
Image: GetTestImage(runtime).ID, Image: GetTestImage(runtime).ID,
Cmd: []string{"echo", "-n", "foobar"}, Cmd: []string{"echo", "-n", "foobar"},
@ -402,7 +411,7 @@ func TestKillDifferentUser(t *testing.T) {
runtime := mkRuntime(t) runtime := mkRuntime(t)
defer nuke(runtime) defer nuke(runtime)
container, err := NewBuilder(runtime).Create(&Config{ container, err := runtime.Create(&Config{
Image: GetTestImage(runtime).ID, Image: GetTestImage(runtime).ID,
Cmd: []string{"cat"}, Cmd: []string{"cat"},
OpenStdin: true, OpenStdin: true,
@ -462,7 +471,7 @@ func TestCreateVolume(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
c, err := NewBuilder(runtime).Create(config) c, err := runtime.Create(config)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -477,7 +486,7 @@ func TestCreateVolume(t *testing.T) {
func TestKill(t *testing.T) { func TestKill(t *testing.T) {
runtime := mkRuntime(t) runtime := mkRuntime(t)
defer nuke(runtime) defer nuke(runtime)
container, err := NewBuilder(runtime).Create(&Config{ container, err := runtime.Create(&Config{
Image: GetTestImage(runtime).ID, Image: GetTestImage(runtime).ID,
Cmd: []string{"sleep", "2"}, Cmd: []string{"sleep", "2"},
}, },
@ -521,9 +530,7 @@ func TestExitCode(t *testing.T) {
runtime := mkRuntime(t) runtime := mkRuntime(t)
defer nuke(runtime) defer nuke(runtime)
builder := NewBuilder(runtime) trueContainer, err := runtime.Create(&Config{
trueContainer, err := builder.Create(&Config{
Image: GetTestImage(runtime).ID, Image: GetTestImage(runtime).ID,
Cmd: []string{"/bin/true", ""}, Cmd: []string{"/bin/true", ""},
}) })
@ -538,7 +545,7 @@ func TestExitCode(t *testing.T) {
t.Errorf("Unexpected exit code %d (expected 0)", trueContainer.State.ExitCode) 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, Image: GetTestImage(runtime).ID,
Cmd: []string{"/bin/false", ""}, Cmd: []string{"/bin/false", ""},
}) })
@ -557,7 +564,7 @@ func TestExitCode(t *testing.T) {
func TestRestart(t *testing.T) { func TestRestart(t *testing.T) {
runtime := mkRuntime(t) runtime := mkRuntime(t)
defer nuke(runtime) defer nuke(runtime)
container, err := NewBuilder(runtime).Create(&Config{ container, err := runtime.Create(&Config{
Image: GetTestImage(runtime).ID, Image: GetTestImage(runtime).ID,
Cmd: []string{"echo", "-n", "foobar"}, Cmd: []string{"echo", "-n", "foobar"},
}, },
@ -587,7 +594,7 @@ func TestRestart(t *testing.T) {
func TestRestartStdin(t *testing.T) { func TestRestartStdin(t *testing.T) {
runtime := mkRuntime(t) runtime := mkRuntime(t)
defer nuke(runtime) defer nuke(runtime)
container, err := NewBuilder(runtime).Create(&Config{ container, err := runtime.Create(&Config{
Image: GetTestImage(runtime).ID, Image: GetTestImage(runtime).ID,
Cmd: []string{"cat"}, Cmd: []string{"cat"},
@ -664,10 +671,8 @@ func TestUser(t *testing.T) {
runtime := mkRuntime(t) runtime := mkRuntime(t)
defer nuke(runtime) defer nuke(runtime)
builder := NewBuilder(runtime)
// Default user must be root // Default user must be root
container, err := builder.Create(&Config{ container, err := runtime.Create(&Config{
Image: GetTestImage(runtime).ID, Image: GetTestImage(runtime).ID,
Cmd: []string{"id"}, Cmd: []string{"id"},
}, },
@ -685,7 +690,7 @@ func TestUser(t *testing.T) {
} }
// Set a username // Set a username
container, err = builder.Create(&Config{ container, err = runtime.Create(&Config{
Image: GetTestImage(runtime).ID, Image: GetTestImage(runtime).ID,
Cmd: []string{"id"}, Cmd: []string{"id"},
@ -705,7 +710,7 @@ func TestUser(t *testing.T) {
} }
// Set a UID // Set a UID
container, err = builder.Create(&Config{ container, err = runtime.Create(&Config{
Image: GetTestImage(runtime).ID, Image: GetTestImage(runtime).ID,
Cmd: []string{"id"}, Cmd: []string{"id"},
@ -725,7 +730,7 @@ func TestUser(t *testing.T) {
} }
// Set a different user by uid // Set a different user by uid
container, err = builder.Create(&Config{ container, err = runtime.Create(&Config{
Image: GetTestImage(runtime).ID, Image: GetTestImage(runtime).ID,
Cmd: []string{"id"}, Cmd: []string{"id"},
@ -747,7 +752,7 @@ func TestUser(t *testing.T) {
} }
// Set a different user by username // Set a different user by username
container, err = builder.Create(&Config{ container, err = runtime.Create(&Config{
Image: GetTestImage(runtime).ID, Image: GetTestImage(runtime).ID,
Cmd: []string{"id"}, Cmd: []string{"id"},
@ -767,7 +772,7 @@ func TestUser(t *testing.T) {
} }
// Test an wrong username // Test an wrong username
container, err = builder.Create(&Config{ container, err = runtime.Create(&Config{
Image: GetTestImage(runtime).ID, Image: GetTestImage(runtime).ID,
Cmd: []string{"id"}, Cmd: []string{"id"},
@ -788,9 +793,7 @@ func TestMultipleContainers(t *testing.T) {
runtime := mkRuntime(t) runtime := mkRuntime(t)
defer nuke(runtime) defer nuke(runtime)
builder := NewBuilder(runtime) container1, err := runtime.Create(&Config{
container1, err := builder.Create(&Config{
Image: GetTestImage(runtime).ID, Image: GetTestImage(runtime).ID,
Cmd: []string{"sleep", "2"}, Cmd: []string{"sleep", "2"},
}, },
@ -800,7 +803,7 @@ func TestMultipleContainers(t *testing.T) {
} }
defer runtime.Destroy(container1) defer runtime.Destroy(container1)
container2, err := builder.Create(&Config{ container2, err := runtime.Create(&Config{
Image: GetTestImage(runtime).ID, Image: GetTestImage(runtime).ID,
Cmd: []string{"sleep", "2"}, Cmd: []string{"sleep", "2"},
}, },
@ -844,7 +847,7 @@ func TestMultipleContainers(t *testing.T) {
func TestStdin(t *testing.T) { func TestStdin(t *testing.T) {
runtime := mkRuntime(t) runtime := mkRuntime(t)
defer nuke(runtime) defer nuke(runtime)
container, err := NewBuilder(runtime).Create(&Config{ container, err := runtime.Create(&Config{
Image: GetTestImage(runtime).ID, Image: GetTestImage(runtime).ID,
Cmd: []string{"cat"}, Cmd: []string{"cat"},
@ -889,7 +892,7 @@ func TestStdin(t *testing.T) {
func TestTty(t *testing.T) { func TestTty(t *testing.T) {
runtime := mkRuntime(t) runtime := mkRuntime(t)
defer nuke(runtime) defer nuke(runtime)
container, err := NewBuilder(runtime).Create(&Config{ container, err := runtime.Create(&Config{
Image: GetTestImage(runtime).ID, Image: GetTestImage(runtime).ID,
Cmd: []string{"cat"}, Cmd: []string{"cat"},
@ -934,7 +937,7 @@ func TestTty(t *testing.T) {
func TestEnv(t *testing.T) { func TestEnv(t *testing.T) {
runtime := mkRuntime(t) runtime := mkRuntime(t)
defer nuke(runtime) defer nuke(runtime)
container, err := NewBuilder(runtime).Create(&Config{ container, err := runtime.Create(&Config{
Image: GetTestImage(runtime).ID, Image: GetTestImage(runtime).ID,
Cmd: []string{"env"}, Cmd: []string{"env"},
}, },
@ -983,7 +986,7 @@ func TestEnv(t *testing.T) {
func TestEntrypoint(t *testing.T) { func TestEntrypoint(t *testing.T) {
runtime := mkRuntime(t) runtime := mkRuntime(t)
defer nuke(runtime) defer nuke(runtime)
container, err := NewBuilder(runtime).Create( container, err := runtime.Create(
&Config{ &Config{
Image: GetTestImage(runtime).ID, Image: GetTestImage(runtime).ID,
Entrypoint: []string{"/bin/echo"}, Entrypoint: []string{"/bin/echo"},
@ -1006,7 +1009,7 @@ func TestEntrypoint(t *testing.T) {
func TestEntrypointNoCmd(t *testing.T) { func TestEntrypointNoCmd(t *testing.T) {
runtime := mkRuntime(t) runtime := mkRuntime(t)
defer nuke(runtime) defer nuke(runtime)
container, err := NewBuilder(runtime).Create( container, err := runtime.Create(
&Config{ &Config{
Image: GetTestImage(runtime).ID, Image: GetTestImage(runtime).ID,
Entrypoint: []string{"/bin/echo", "foobar"}, Entrypoint: []string{"/bin/echo", "foobar"},
@ -1057,7 +1060,7 @@ func TestLXCConfig(t *testing.T) {
cpuMin := 100 cpuMin := 100
cpuMax := 10000 cpuMax := 10000
cpu := cpuMin + rand.Intn(cpuMax-cpuMin) cpu := cpuMin + rand.Intn(cpuMax-cpuMin)
container, err := NewBuilder(runtime).Create(&Config{ container, err := runtime.Create(&Config{
Image: GetTestImage(runtime).ID, Image: GetTestImage(runtime).ID,
Cmd: []string{"/bin/true"}, Cmd: []string{"/bin/true"},
@ -1081,7 +1084,7 @@ func TestLXCConfig(t *testing.T) {
func TestCustomLxcConfig(t *testing.T) { func TestCustomLxcConfig(t *testing.T) {
runtime := mkRuntime(t) runtime := mkRuntime(t)
defer nuke(runtime) defer nuke(runtime)
container, err := NewBuilder(runtime).Create(&Config{ container, err := runtime.Create(&Config{
Image: GetTestImage(runtime).ID, Image: GetTestImage(runtime).ID,
Cmd: []string{"/bin/true"}, Cmd: []string{"/bin/true"},
@ -1112,7 +1115,7 @@ func BenchmarkRunSequencial(b *testing.B) {
runtime := mkRuntime(b) runtime := mkRuntime(b)
defer nuke(runtime) defer nuke(runtime)
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
container, err := NewBuilder(runtime).Create(&Config{ container, err := runtime.Create(&Config{
Image: GetTestImage(runtime).ID, Image: GetTestImage(runtime).ID,
Cmd: []string{"echo", "-n", "foo"}, Cmd: []string{"echo", "-n", "foo"},
}, },
@ -1144,7 +1147,7 @@ func BenchmarkRunParallel(b *testing.B) {
complete := make(chan error) complete := make(chan error)
tasks = append(tasks, complete) tasks = append(tasks, complete)
go func(i int, complete chan error) { go func(i int, complete chan error) {
container, err := NewBuilder(runtime).Create(&Config{ container, err := runtime.Create(&Config{
Image: GetTestImage(runtime).ID, Image: GetTestImage(runtime).ID,
Cmd: []string{"echo", "-n", "foo"}, Cmd: []string{"echo", "-n", "foo"},
}, },
@ -1193,6 +1196,105 @@ func tempDir(t *testing.T) string {
return tmpDir 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) { func TestBindMounts(t *testing.T) {
r := mkRuntime(t) r := mkRuntime(t)
defer nuke(r) defer nuke(r)
@ -1220,7 +1322,7 @@ func TestBindMounts(t *testing.T) {
func TestVolumesFromReadonlyMount(t *testing.T) { func TestVolumesFromReadonlyMount(t *testing.T) {
runtime := mkRuntime(t) runtime := mkRuntime(t)
defer nuke(runtime) defer nuke(runtime)
container, err := NewBuilder(runtime).Create( container, err := runtime.Create(
&Config{ &Config{
Image: GetTestImage(runtime).ID, Image: GetTestImage(runtime).ID,
Cmd: []string{"/bin/echo", "-n", "foobar"}, Cmd: []string{"/bin/echo", "-n", "foobar"},
@ -1239,7 +1341,7 @@ func TestVolumesFromReadonlyMount(t *testing.T) {
t.Fail() t.Fail()
} }
container2, err := NewBuilder(runtime).Create( container2, err := runtime.Create(
&Config{ &Config{
Image: GetTestImage(runtime).ID, Image: GetTestImage(runtime).ID,
Cmd: []string{"/bin/echo", "-n", "foobar"}, Cmd: []string{"/bin/echo", "-n", "foobar"},
@ -1275,7 +1377,7 @@ func TestRestartWithVolumes(t *testing.T) {
runtime := mkRuntime(t) runtime := mkRuntime(t)
defer nuke(runtime) defer nuke(runtime)
container, err := NewBuilder(runtime).Create(&Config{ container, err := runtime.Create(&Config{
Image: GetTestImage(runtime).ID, Image: GetTestImage(runtime).ID,
Cmd: []string{"echo", "-n", "foobar"}, Cmd: []string{"echo", "-n", "foobar"},
Volumes: map[string]struct{}{"/test": {}}, Volumes: map[string]struct{}{"/test": {}},
@ -1318,7 +1420,7 @@ func TestVolumesFromWithVolumes(t *testing.T) {
runtime := mkRuntime(t) runtime := mkRuntime(t)
defer nuke(runtime) defer nuke(runtime)
container, err := NewBuilder(runtime).Create(&Config{ container, err := runtime.Create(&Config{
Image: GetTestImage(runtime).ID, Image: GetTestImage(runtime).ID,
Cmd: []string{"sh", "-c", "echo -n bar > /test/foo"}, Cmd: []string{"sh", "-c", "echo -n bar > /test/foo"},
Volumes: map[string]struct{}{"/test": {}}, Volumes: map[string]struct{}{"/test": {}},
@ -1345,7 +1447,7 @@ func TestVolumesFromWithVolumes(t *testing.T) {
t.Fail() t.Fail()
} }
container2, err := NewBuilder(runtime).Create( container2, err := runtime.Create(
&Config{ &Config{
Image: GetTestImage(runtime).ID, Image: GetTestImage(runtime).ID,
Cmd: []string{"cat", "/test/foo"}, Cmd: []string{"cat", "/test/foo"},
@ -1386,7 +1488,7 @@ func TestOnlyLoopbackExistsWhenUsingDisableNetworkOption(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
c, err := NewBuilder(runtime).Create(config) c, err := runtime.Create(config)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

View File

@ -1 +1 @@
# Maintainer wanted! Enroll on #docker@freenode Kawsar Saiyeed <kawsar.saiyeed@projiris.com>

View File

@ -21,7 +21,6 @@
# If the docker daemon is using a unix socket for communication your user # 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 # must have access to the socket for the completions to function correctly
have docker && {
__docker_containers_all() __docker_containers_all()
{ {
local containers local containers
@ -115,7 +114,7 @@ _docker_build()
case "$cur" in case "$cur" in
-*) -*)
COMPREPLY=( $( compgen -W "-t -q" -- "$cur" ) ) COMPREPLY=( $( compgen -W "-no-cache -t -q -rm" -- "$cur" ) )
;; ;;
*) *)
_filedir _filedir
@ -138,11 +137,37 @@ _docker_commit()
COMPREPLY=( $( compgen -W "-author -m -run" -- "$cur" ) ) 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 esac
} }
_docker_cp()
{
if [ $cpos -eq $cword ]; then
__docker_containers_all
else
_filedir
fi
}
_docker_diff() _docker_diff()
{ {
if [ $cpos -eq $cword ]; then if [ $cpos -eq $cword ]; then
@ -152,7 +177,21 @@ _docker_diff()
_docker_events() _docker_events()
{ {
COMPREPLY=( $( compgen -W "-since" -- "$cur" ) ) case "$prev" in
-since)
return
;;
*)
;;
esac
case "$cur" in
-*)
COMPREPLY=( $( compgen -W "-since" -- "$cur" ) )
;;
*)
;;
esac
} }
_docker_export() _docker_export()
@ -231,7 +270,21 @@ _docker_kill()
_docker_login() _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() _docker_logs()
@ -250,12 +303,40 @@ _docker_port()
_docker_ps() _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() _docker_pull()
{ {
COMPREPLY=( $( compgen -W "-t" -- "$cur" ) ) case "$prev" in
-t)
return
;;
*)
;;
esac
case "$cur" in
-*)
COMPREPLY=( $( compgen -W "-t" -- "$cur" ) )
;;
*)
;;
esac
} }
_docker_push() _docker_push()
@ -309,7 +390,7 @@ _docker_run()
-volumes-from) -volumes-from)
__docker_containers_all __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 return
;; ;;
*) *)
@ -318,34 +399,27 @@ _docker_run()
case "$cur" in 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 local counter=$cpos
-*) while [ $counter -le $cword ]; do
COMPREPLY=( $( compgen -W "-a -notrunc -q -viz" -- "$cur" ) ) case "${words[$counter]}" in
;; -a|-c|-cidfile|-dns|-e|-entrypoint|-h|-lxc-conf|-m|-p|-u|-v|-volumes-from|-w)
*)
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
(( counter++ )) (( counter++ ))
done ;;
-*)
;;
*)
break
;;
esac
(( counter++ ))
done
if [ $counter -eq $cword ]; then if [ $counter -eq $cword ]; then
__docker_image_repos_and_tags __docker_image_repos_and_tags
fi fi
;;
esac
;; ;;
esac esac
} }
@ -409,6 +483,7 @@ _docker()
attach attach
build build
commit commit
cp
diff diff
events events
export export
@ -466,4 +541,3 @@ _docker()
} }
complete -F _docker docker complete -F _docker docker
}

View File

@ -47,7 +47,7 @@ else
echo "Creating /etc/init/dockerd.conf..." echo "Creating /etc/init/dockerd.conf..."
cat >/etc/init/dockerd.conf <<EOF cat >/etc/init/dockerd.conf <<EOF
description "Docker daemon" description "Docker daemon"
start on filesystem or runlevel [2345] start on filesystem and started lxc-net
stop on runlevel [!2345] stop on runlevel [!2345]
respawn respawn
exec /usr/local/bin/docker -d exec /usr/local/bin/docker -d

View File

@ -16,7 +16,7 @@ import (
var ( var (
GITCOMMIT string GITCOMMIT string
VERSION string VERSION string
) )
func main() { func main() {
@ -75,6 +75,9 @@ func main() {
} }
protoAddrParts := strings.SplitN(flHosts[0], "://", 2) protoAddrParts := strings.SplitN(flHosts[0], "://", 2)
if err := docker.ParseCommands(protoAddrParts[0], protoAddrParts[1], flag.Args()...); err != nil { if err := docker.ParseCommands(protoAddrParts[0], protoAddrParts[1], flag.Args()...); err != nil {
if sterr, ok := err.(*utils.StatusError); ok {
os.Exit(sterr.Status)
}
log.Fatal(err) log.Fatal(err)
os.Exit(-1) os.Exit(-1)
} }

15
docs/Dockerfile Normal file
View File

@ -0,0 +1,15 @@
from ubuntu:12.04
maintainer Nick Stinemates
run apt-get update
run apt-get install -y python-setuptools make
run easy_install pip
add . /docs
run pip install -r /docs/requirements.txt
run cd /docs; make docs
expose 8000
workdir /docs/_build/html
entrypoint ["python", "-m", "SimpleHTTPServer"]

View File

@ -27,14 +27,36 @@ Docker Remote API
2. Versions 2. Versions
=========== ===========
The current version of the API is 1.4 The current version of the API is 1.5
Calling /images/<name>/insert is the same as calling Calling /images/<name>/insert is the same as calling
/v1.4/images/<name>/insert /v1.5/images/<name>/insert
You can still call an old version of the api using You can still call an old version of the api using
/v1.0/images/<name>/insert /v1.0/images/<name>/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` :doc:`docker_remote_api_v1.4`
***************************** *****************************
@ -165,7 +187,7 @@ Initial version
Docker Remote API Client Libraries 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 compatibility. Please file issues with the library owners. If you
find more library implementations, please list them in Docker doc bugs find more library implementations, please list them in Docker doc bugs
and we will add the libraries here. 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 | | 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-client | https://github.com/geku/docker-client |
+----------------------+----------------+--------------------------------------------+ +----------------------+----------------+--------------------------------------------+
| Ruby | docker-api | https://github.com/swipely/docker-api | | 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 | docker-js | https://github.com/dgoujard/docker-js |
+----------------------+----------------+--------------------------------------------+ +----------------------+----------------+--------------------------------------------+
| Javascript (Angular) | dockerui | https://github.com/crosbymichael/dockerui | | 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 | | 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 |
+----------------------+----------------+--------------------------------------------+

View File

@ -119,6 +119,7 @@ Create a container
"AttachStdout":true, "AttachStdout":true,
"AttachStderr":true, "AttachStderr":true,
"PortSpecs":null, "PortSpecs":null,
"Privileged": false,
"Tty":false, "Tty":false,
"OpenStdin":false, "OpenStdin":false,
"StdinOnce":false, "StdinOnce":false,
@ -223,6 +224,7 @@ Inspect a container
:statuscode 200: no error :statuscode 200: no error
:statuscode 404: no such container :statuscode 404: no such container
:statuscode 409: conflict between containers and images
:statuscode 500: server error :statuscode 500: server error
@ -357,7 +359,7 @@ Start a container
{ {
"Binds":["/tmp:/tmp"], "Binds":["/tmp:/tmp"],
"LxcConf":{"lxc.utsname":"docker"} "LxcConf":[{"Key":"lxc.utsname","Value":"docker"}]
} }
**Example response**: **Example response**:
@ -368,7 +370,7 @@ Start a container
Content-Type: text/plain Content-Type: text/plain
:jsonparam hostConfig: the container's host configuration (optional) :jsonparam hostConfig: the container's host configuration (optional)
:statuscode 200: no error :statuscode 204: no error
:statuscode 404: no such container :statuscode 404: no such container
:statuscode 500: server error :statuscode 500: server error
@ -679,8 +681,8 @@ Create an image
:statuscode 500: server error :statuscode 500: server error
Insert a file in a image Insert a file in an image
************************ *************************
.. http:post:: /images/(name)/insert .. http:post:: /images/(name)/insert
@ -759,7 +761,8 @@ Inspect an image
:statuscode 200: no error :statuscode 200: no error
:statuscode 404: no such image :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 Get the history of an image
@ -990,7 +993,8 @@ Check auth configuration
{ {
"username":"hannibal", "username":"hannibal",
"password:"xxxx", "password:"xxxx",
"email":"hannibal@a-team.com" "email":"hannibal@a-team.com",
"serveraddress":"https://index.docker.io/v1/"
} }
**Example response**: **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 POST /commit?container=44c004db4b17&m=message&repo=myrepo HTTP/1.1
**Example response**: **Example response**:
.. sourcecode:: http .. sourcecode:: http
@ -1091,15 +1095,15 @@ Create a new image from a container's changes
{"Id":"596069db4bf5"} {"Id":"596069db4bf5"}
:query container: source container :query container: source container
:query repo: repository :query repo: repository
:query tag: tag :query tag: tag
:query m: commit message :query m: commit message
:query author: author (eg. "John Hannibal Smith <hannibal@a-team.com>") :query author: author (eg. "John Hannibal Smith <hannibal@a-team.com>")
:query run: config automatically applied when the image is run. (ex: {"Cmd": ["cat", "/world"], "PortSpecs":["22"]}) :query run: config automatically applied when the image is run. (ex: {"Cmd": ["cat", "/world"], "PortSpecs":["22"]})
:statuscode 201: no error :statuscode 201: no error
:statuscode 404: no such container :statuscode 404: no such container
:statuscode 500: server error :statuscode 500: server error
Monitor Docker's events Monitor Docker's events

File diff suppressed because it is too large Load Diff

View File

@ -32,11 +32,13 @@ Available Commands
command/commit command/commit
command/cp command/cp
command/diff command/diff
command/events
command/export command/export
command/history command/history
command/images command/images
command/import command/import
command/info command/info
command/insert
command/inspect command/inspect
command/kill command/kill
command/login command/login

View File

@ -13,6 +13,7 @@
-t="": Repository name (and optionally a tag) to be applied to the resulting image in case of success. -t="": Repository name (and optionally a tag) to be applied to the resulting image in case of success.
-q=false: Suppress verbose build output. -q=false: Suppress verbose build output.
-no-cache: Do not use the cache when building the image. -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 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

View File

@ -19,15 +19,31 @@
Full -run example:: Full -run example::
{"Hostname": "", {
"User": "", "Entrypoint" : null,
"CpuShares": 0, "Privileged" : false,
"Memory": 0, "User" : "",
"MemorySwap": 0, "VolumesFrom" : "",
"PortSpecs": ["22", "80", "443"], "Cmd" : ["cat", "-e", "/etc/resolv.conf"],
"Tty": true, "Dns" : ["8.8.8.8", "8.8.4.4"],
"OpenStdin": true, "MemorySwap" : 0,
"StdinOnce": true, "AttachStdin" : false,
"Env": ["FOO=BAR", "FOO2=BAR2"], "AttachStderr" : false,
"Cmd": ["cat", "-e", "/etc/resolv.conf"], "CpuShares" : 0,
"Dns": ["8.8.8.8", "8.8.4.4"]} "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
}

View File

@ -2,12 +2,13 @@
:description: Copy files/folders from the containers filesystem to the host path :description: Copy files/folders from the containers filesystem to the host path
:keywords: cp, docker, container, documentation, copy :keywords: cp, docker, container, documentation, copy
=========================================================== ============================================================================
``cp`` -- Copy files/folders from the containers filesystem to the host path ``cp`` -- Copy files/folders from the containers filesystem to the host path
=========================================================== ============================================================================
:: ::
Usage: docker cp CONTAINER:RESOURCE HOSTPATH 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.

View File

@ -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

View File

@ -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

View File

@ -8,10 +8,17 @@
:: ::
Usage: docker login [OPTIONS] Usage: docker login [OPTIONS] [SERVER]
Register or Login to the docker registry server Register or Login to the docker registry server
-e="": email -e="": email
-p="": password -p="": password
-u="": username -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

View File

@ -67,7 +67,7 @@ use-cases, like running Docker within Docker.
docker run -w /path/to/dir/ -i -t ubuntu pwd 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 here /path/to/dir/. If the path does not exists it is created inside the
container. container.
@ -76,8 +76,8 @@ container.
docker run -v `pwd`:`pwd` -w `pwd` -i -t ubuntu pwd docker run -v `pwd`:`pwd` -w `pwd` -i -t ubuntu pwd
The ``-v`` flag mounts the current working directory into the container. The ``-v`` flag mounts the current working directory into the container.
The ``-w`` lets the command beeing executed inside the current The ``-w`` lets the command being executed inside the current
working directory, by changeing into the directory to the value working directory, by changing into the directory to the value
returned by ``pwd``. So this combination executes the command returned by ``pwd``. So this combination executes the command
using the container, but inside the current working directory. using the container, but inside the current working directory.

View File

@ -17,11 +17,13 @@ Contents:
commit <command/commit> commit <command/commit>
cp <command/cp> cp <command/cp>
diff <command/diff> diff <command/diff>
events <command/events>
export <command/export> export <command/export>
history <command/history> history <command/history>
images <command/images> images <command/images>
import <command/import> import <command/import>
info <command/info> info <command/info>
insert <command/insert>
inspect <command/inspect> inspect <command/inspect>
kill <command/kill> kill <command/kill>
login <command/login> login <command/login>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 194 KiB

View File

@ -1,16 +0,0 @@
:title: Overview
:description: Docker documentation summary
:keywords: concepts, documentation, docker, containers
Overview
========
Contents:
.. toctree::
:maxdepth: 1
../index
manifesto

View File

@ -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.

View File

@ -2,6 +2,28 @@
:description: A simple hello world example with Docker :description: A simple hello world example with Docker
:keywords: docker, example, hello world :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:
Hello World Hello World
@ -49,4 +71,108 @@ See the example in action
</div> </div>
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
<div style="margin-top:10px;">
<iframe width="560" height="350" src="http://ascii.io/a/2562/raw" frameborder="0"></iframe>
</div>
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

View File

@ -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
<div style="margin-top:10px;">
<iframe width="560" height="350" src="http://ascii.io/a/2562/raw" frameborder="0"></iframe>
</div>
Continue to the :ref:`python_web_app` example.

View File

@ -5,16 +5,16 @@
Examples 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:: .. toctree::
:maxdepth: 1 :maxdepth: 1
running_examples
hello_world hello_world
hello_world_daemon
python_web_app python_web_app
nodejs_web_app nodejs_web_app
running_redis_service running_redis_service
@ -22,3 +22,4 @@ Contents:
couchdb_data_volumes couchdb_data_volumes
postgresql_service postgresql_service
mongodb mongodb
running_riak_service

View File

@ -24,8 +24,8 @@ Create an empty file called ``Dockerfile``:
touch Dockerfile touch Dockerfile
Next, define the parent image you want to use to build your own image on top of. Next, define the parent image you want to use to build your own image on top of.
Here, well use `CentOS <https://index.docker.io/_/ubuntu/>`_ (tag: ``latest``) Here, well use `Ubuntu <https://index.docker.io/_/ubuntu/>`_ (tag: ``latest``)
available on the `docker index`_: available on the `docker index <http://index.docker.io>`_:
.. code-block:: bash .. code-block:: bash
@ -65,11 +65,13 @@ run without needing to provide a special configuration file)
# Create the MongoDB data directory # Create the MongoDB data directory
RUN mkdir -p /data/db 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 .. code-block:: bash
EXPOSE 27017 EXPOSE 27017
ENTRYPOINT ["usr/bin/mongod"]
Now, lets build the image which will go through the ``Dockerfile`` we made and Now, lets build the image which will go through the ``Dockerfile`` we made and
run all of the commands. run all of the commands.
@ -84,10 +86,10 @@ the local port!
.. code-block:: bash .. code-block:: bash
# Regular style # Regular style
MONGO_ID=$(docker run -d <yourname>/mongodb mongod) MONGO_ID=$(docker run -d <yourname>/mongodb)
# Lean and mean # Lean and mean
MONGO_ID=$(docker run -d <yourname>/mongodb mongod --noprealloc --smallfiles) MONGO_ID=$(docker run -d <yourname>/mongodb --noprealloc --smallfiles)
# Check the logs out # Check the logs out
docker logs $MONGO_ID docker logs $MONGO_ID

View File

@ -93,7 +93,7 @@ To install the right package for CentOS, well use the instructions from the
# Enable EPEL for Node.js # Enable EPEL for Node.js
RUN rpm -Uvh http://download.fedoraproject.org/pub/epel/6/i386/epel-release-6-8.noarch.rpm RUN rpm -Uvh http://download.fedoraproject.org/pub/epel/6/i386/epel-release-6-8.noarch.rpm
# Install Node.js and npm # Install Node.js and npm
RUN yum install -y npm-1.2.17-5.el6 RUN yum install -y npm
To bundle your apps source code inside the docker image, use the ``ADD`` To bundle your apps source code inside the docker image, use the ``ADD``
command: command:
@ -137,7 +137,7 @@ Your ``Dockerfile`` should now look like this:
# Enable EPEL for Node.js # Enable EPEL for Node.js
RUN rpm -Uvh http://download.fedoraproject.org/pub/epel/6/i386/epel-release-6-8.noarch.rpm RUN rpm -Uvh http://download.fedoraproject.org/pub/epel/6/i386/epel-release-6-8.noarch.rpm
# Install Node.js and npm # Install Node.js and npm
RUN yum install -y npm-1.2.17-5.el6 RUN yum install -y npm
# Bundle app source # Bundle app source
ADD . /src ADD . /src

View File

@ -86,7 +86,7 @@ http://0.0.0.0:5000/" in the log output.
.. code-block:: bash .. 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 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. used by the container and store it inside of the WEB_PORT variable.

View File

@ -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

View File

@ -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.
Well use `Ubuntu <https://index.docker.io/_/ubuntu/>`_ (tag: ``latest``),
which is available on the `docker index <http://index.docker.io>`_:
.. 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 "<yourname>/riak" .
Next steps
++++++++++
Riak is a distributed database. Many production deployments consist of `at
least five nodes <http://basho.com/why-your-riak-cluster-should-have-at-least-
five-nodes/>`_. See the `docker-riak <https://github.com/hectcastro /docker-
riak>`_ project details on how to deploy a Riak cluster using Docker and
Pipework.

View File

@ -122,6 +122,13 @@ What does Docker add to just plain LXC?
(Jenkins, Strider, Travis), etc. Docker is rapidly establishing (Jenkins, Strider, Travis), etc. Docker is rapidly establishing
itself as the standard for container-based tooling. 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? Can I help by adding some questions and answers?
................................................ ................................................

View File

@ -1,11 +1,11 @@
:title: Welcome to the Docker Documentation :title: Docker Documentation
:description: An overview of the Docker Documentation :description: An overview of the Docker Documentation
:keywords: containers, lxc, concepts, explanation :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 ``docker``, the Linux Container Runtime, runs Unix processes with
strong guarantees of isolation across servers. Your software runs strong guarantees of isolation across servers. Your software runs

View File

@ -1,22 +1,77 @@
:title: Installation on Amazon EC2 :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 :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. .. include:: install_header.inc
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
Please note this is a community contributed installation path. The only 'official' installation is using the There are several ways to install Docker on AWS EC2:
:ref:`ubuntu_linux` installation path. This version may sometimes be out of date.
* :ref:`amazonquickstart` or
* :ref:`amazonstandard` or
* :ref:`amazonvagrant`
**You'll need an** `AWS account <http://aws.amazon.com/>`_ **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 <https://help.ubuntu.com/community/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
<dockergroup>` 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
And finally, if you prefer to work through Vagrant, you can install
Installation Docker that way too. Vagrant 1.1 or higher is required.
------------
Docker can now be installed on Amazon EC2 with a single vagrant command. Vagrant 1.1 or higher is required.
1. Install vagrant from http://www.vagrantup.com/ (or use your package manager) 1. Install vagrant from http://www.vagrantup.com/ (or use your package manager)
2. Install the vagrant aws plugin 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. 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*. 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.
Check that your default security group has an inbound rule to
accept SSH (port 22) connections.
5. Inform Vagrant of your settings 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 Vagrant will read your access credentials from your environment, so
you have everything on amazon aws setup so you can (manually) deploy a new image to EC2. 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_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_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_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 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 vagrant up --provider=aws
If it stalls indefinitely on ``[default] Waiting for SSH to become available...``, Double check your default security If it stalls indefinitely on ``[default] Waiting for SSH to become
zone on AWS includes rights to SSH (port 22) to your container. 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 7. Connect to your machine

View File

@ -7,10 +7,6 @@
Arch Linux 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 Installing on Arch Linux is not officially supported but can be handled via
either of the following AUR packages: either of the following AUR packages:
@ -36,6 +32,10 @@ either AUR package.
Installation Installation
------------ ------------
.. include:: install_header.inc
.. include:: install_unofficial.inc
The instructions here assume **yaourt** is installed. See The instructions here assume **yaourt** is installed. See
`Arch User Repository <https://wiki.archlinux.org/index.php/Arch_User_Repository#Installing_packages>`_ `Arch User Repository <https://wiki.archlinux.org/index.php/Arch_User_Repository#Installing_packages>`_
for information on building and installing packages from the AUR if you have not for information on building and installing packages from the AUR if you have not

View File

@ -7,9 +7,10 @@
Binaries 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: Right now, the officially supported distributions are:
@ -23,22 +24,18 @@ But we know people have had success running it under
- Suse - Suse
- :ref:`arch_linux` - :ref:`arch_linux`
Check Your Kernel
-----------------
Dependencies: Your host's Linux kernel must meet the Docker :ref:`kernel`
-------------
* 3.8 Kernel (read more about :ref:`kernel`)
* AUFS filesystem support
* lxc
* xz-utils
Get the docker binary: Get the docker binary:
---------------------- ----------------------
.. code-block:: bash .. code-block:: bash
wget http://get.docker.io/builds/Linux/x86_64/docker-latest.tgz wget --output-document=docker https://get.docker.io/builds/Linux/x86_64/docker-latest
tar -xf docker-latest.tgz chmod +x docker
Run the docker daemon Run the docker daemon

View File

@ -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 <http://wiki.gentoo.org/wiki/Layman#Adding_custom_overlays>`_),
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 <https://github.com/tianon/docker-overlay/issues>`_ 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
<https://github.com/dotcloud/docker/issues/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.

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

View File

@ -24,5 +24,6 @@ Contents:
amazon amazon
rackspace rackspace
archlinux archlinux
gentoolinux
upgrading upgrading
kernel kernel

View File

@ -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"
<http://blog.docker.io/2013/08/getting-to-docker-1-0/>`_

View File

@ -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

View File

@ -62,7 +62,7 @@ kernel tree to add AUFS. The process is documented on
Cgroups and namespaces 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 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 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 to run LXC containers. Note that 2.6.32 has some documented issues regarding

View File

@ -6,21 +6,22 @@
Rackspace Cloud Rackspace Cloud
=============== ===============
Please note this is a community contributed installation path. The only 'official' installation is using the .. include:: install_unofficial.inc
:ref:`ubuntu_linux` installation path. This version may sometimes be out of date.
Installing Docker on Ubuntu provided by Rackspace is pretty
Installing Docker on Ubuntu provided by Rackspace is pretty straightforward, and you should mostly be able to follow the straightforward, and you should mostly be able to follow the
:ref:`ubuntu_linux` installation guide. :ref:`ubuntu_linux` installation guide.
**However, there is one caveat:** **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 If you are using any linux not already shipping with the 3.8 kernel
more difficult on Rackspace. 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) Rackspace boots their servers using grub's ``menu.lst`` and does not
kernels there, although they do work. This makes ``update-grub`` to not have the expected result, and you need to like non 'virtual' packages (e.g. xen compatible) kernels there,
set the kernel manually. 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!** **Do not attempt this on a production machine!**
@ -33,7 +34,8 @@ set the kernel manually.
apt-get install linux-generic-lts-raring 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 .. 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 # 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. Now you need to manually edit ``/boot/grub/menu.lst``, you will find a
Copy the top one and substitute the new kernel into that. Make sure the new kernel is on top, and double check kernel section at the bottom with the existing options. Copy the top one and
and initrd point to the right files. 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. Make special care to double check the kernel and initrd entries.
@ -92,4 +95,4 @@ Verify the kernel was updated
# nice! 3.8. # nice! 3.8.
Now you can finish with the :ref:`ubuntu_linux` instructions. Now you can finish with the :ref:`ubuntu_linux` instructions.

View File

@ -7,7 +7,12 @@
Ubuntu Linux 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: 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`) * Linux kernel 3.8 (read more about :ref:`kernel`)
* AUFS file system support (we are working on BTRFS support as an alternative) * 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) <https://help.ubuntu.com/community/UFW>`_ Please read :ref:`ufw`, if you plan to use `UFW (Uncomplicated
Firewall) <https://help.ubuntu.com/community/UFW>`_
.. _ubuntu_precise: .. _ubuntu_precise:
@ -35,12 +41,13 @@ Dependencies
**Linux kernel 3.8** **Linux kernel 3.8**
Due to a bug in LXC, docker works best on the 3.8 kernel. Precise 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 comes with a 3.2 kernel, so we need to upgrade it. The kernel you'll
install comes with AUFS built in. We also include the generic headers install when following these steps comes with AUFS built in. We also
to enable packages that depend on them, like ZFS and the VirtualBox include the generic headers to enable packages that depend on them,
guest additions. If you didn't install the headers for your "precise" like ZFS and the VirtualBox guest additions. If you didn't install the
kernel, then you can skip these headers for the "raring" kernel. But headers for your "precise" kernel, then you can skip these headers for
it is safer to include them if you're not sure. the "raring" kernel. But it is safer to include them if you're not
sure.
.. code-block:: bash .. code-block:: bash
@ -56,14 +63,22 @@ it is safer to include them if you're not sure.
Installation Installation
------------ ------------
Docker is available as a Ubuntu PPA (Personal Package Archive), .. warning::
`hosted on launchpad <https://launchpad.net/~dotcloud/+archive/lxc-docker>`_
which makes installing Docker on Ubuntu very easy. 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 .. code-block:: bash
# Add the PPA sources to your apt sources list. # Add the Docker repository key to your local keychain
sudo apt-get install python-software-properties && sudo add-apt-repository ppa:dotcloud/lxc-docker # 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 # Update your sources
sudo apt-get update 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 update
sudo apt-get install linux-image-extra-`uname -r` 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 Installation
------------ ------------
Docker is available as a Ubuntu PPA (Personal Package Archive), Docker is available as a Debian package, which makes installation easy.
`hosted on launchpad <https://launchpad.net/~dotcloud/+archive/lxc-docker>`_
which makes installing Docker on Ubuntu very easy.
*Please note that these instructions have changed for 0.6. If you are upgrading from an earlier version, you will need
Add the custom package sources to your apt sources list. to follow them again.*
.. code-block:: bash .. code-block:: bash
# add the sources to your apt # Add the Docker repository key to your local keychain
sudo add-apt-repository ppa:dotcloud/lxc-docker # 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 # update
sudo apt-get update sudo apt-get update
@ -137,7 +145,8 @@ Verify it worked
.. code-block:: bash .. 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 sudo docker run -i -t ubuntu /bin/bash
# type exit to exit # type exit to exit
@ -151,7 +160,8 @@ Verify it worked
Docker and UFW 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 .. code-block:: bash
@ -169,8 +179,9 @@ Then reload UFW:
sudo ufw reload 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, UFW's default set of rules denied all `incoming`, so if you want to be
you should allow incoming connections on the docker port (default 4243): able to reach your containers from another host, you should allow
incoming connections on the docker port (default 4243):
.. code-block:: bash .. code-block:: bash

View File

@ -5,18 +5,32 @@
.. _upgrading: .. _upgrading:
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 .. 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 # update your sources list
sudo apt-get update 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 After manual installation
------------------------- -------------------------
If you installed the Docker binary If you installed the Docker :ref:`binaries` then follow these steps:
.. code-block:: bash .. code-block:: bash
@ -48,8 +62,10 @@ If you installed the Docker binary
tar -xf docker-latest.tgz 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 Start docker in daemon mode (``-d``) and disconnect, running the
might reside in your path. 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 .. code-block:: bash

View File

@ -7,30 +7,43 @@
Using Vagrant (Mac, Linux) Using Vagrant (Mac, Linux)
========================== ==========================
This guide will setup a new virtualbox virtual machine with docker installed on your computer. This works on most operating This guide will setup a new virtualbox virtual machine with docker
systems, including MacOX, Windows, Linux, FreeBSD and others. If you can install these and have at least 400Mb RAM installed on your computer. This works on most operating systems,
to spare you should be good. 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 Install Vagrant and Virtualbox
------------------------------ ------------------------------
1. Install virtualbox from https://www.virtualbox.org/ (or use your package manager) .. include:: install_header.inc
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 .. include:: install_unofficial.inc
``git`` in a terminal window
#. 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 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 .. code-block:: bash
git clone https://github.com/dotcloud/docker.git 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 .. code-block:: bash

View File

@ -7,14 +7,16 @@
Using Vagrant (Windows) 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 Docker can run on Windows using a VM like VirtualBox. You then run
may be out of date because it depends on some binaries to be updated and published Linux within the VM.
Installation
Requirements
------------ ------------
.. include:: install_header.inc
.. include:: install_unofficial.inc
1. Install virtualbox from https://www.virtualbox.org - or follow this tutorial__ 1. Install virtualbox from https://www.virtualbox.org - or follow this tutorial__
.. __: http://www.slideshare.net/julienbarbier42/install-virtualbox-on-windows-7 .. __: 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 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 .. image:: images/win/_01.gif
:alt: Git install :alt: Git install
@ -45,12 +50,17 @@ This should open a cmd prompt window.
:alt: run docker :alt: run docker
:align: center :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 Launch an Ubuntu virtual server
------------------------------- -------------------------------
Lets download and run an Ubuntu image with docker binaries already installed. Lets download and run an Ubuntu image with docker binaries already
installed.
.. code-block:: bash .. code-block:: bash
@ -62,7 +72,9 @@ Lets download and run an Ubuntu image with docker binaries already installed.
:alt: run docker :alt: run docker
:align: center :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 Log onto your Ubuntu server
--------------------------- ---------------------------
@ -81,7 +93,12 @@ Run the following command
vagrant ssh 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 .. 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 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 .. image:: images/win/ssh-config.gif
:alt: run docker :alt: run docker
:align: center :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 putty.exe OR
- Use SSH from a terminal - 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 Use putty.exe
''''''''''''' '''''''''''''
You can download putty.exe from this page http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html You can download putty.exe from this page
Launch putty.exe and simply enter the information you got from last step. 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 .. image:: images/win/putty.gif
:alt: run docker :alt: run docker
@ -130,7 +151,9 @@ Open, and enter user = vagrant and password = vagrant.
SSH from a terminal 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 .. code-block:: bash
@ -142,12 +165,14 @@ Enter user = vagrant and password = vagrant.
:alt: run docker :alt: run docker
:align: center :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 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 .. code-block:: bash
@ -166,3 +191,29 @@ You are now ready for the dockers “hello world” example. Run
All done! All done!
Now you can continue with the :ref:`hello_world` example. 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`

View File

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 117 KiB

After

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 90 KiB

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 75 KiB

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 110 KiB

After

Width:  |  Height:  |  Size: 125 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 KiB

After

Width:  |  Height:  |  Size: 71 KiB

File diff suppressed because it is too large Load Diff

Before

Width:  |  Height:  |  Size: 272 KiB

After

Width:  |  Height:  |  Size: 275 KiB

View File

@ -10,7 +10,7 @@ This documentation has the following resources:
.. toctree:: .. toctree::
:titlesonly: :titlesonly:
concepts/index Introduction <index>
installation/index installation/index
use/index use/index
examples/index examples/index
@ -19,6 +19,3 @@ This documentation has the following resources:
api/index api/index
terms/index terms/index
faq faq

View File

@ -0,0 +1,43 @@
:title: Base Image Creation
:description: How to create base images
:keywords: Examples, Usage, base image, docker, documentation, examples
.. _base_image_creation:
Base Image Creation
===================
So you want to create your own :ref:`base_image_def`? Great!
The specific process will depend heavily on the Linux distribution you
want to package. We have some examples below, and you are encouraged
to submit pull requests to contribute new ones.
Getting Started
...............
In general, you'll want to start with a working machine that is
running the distribution you'd like to package as a base image, though
that is not required for some tools like Debian's `Debootstrap
<https://wiki.debian.org/Debootstrap>`_, which you can also use to
build Ubuntu images.
It can be as simple as this to create an Ubuntu base image::
$ sudo debootstrap raring raring > /dev/null
$ sudo tar -C raring -c . | sudo docker import - raring
a29c15f1bf7a
$ sudo docker run raring cat /etc/lsb-release
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=13.04
DISTRIB_CODENAME=raring
DISTRIB_DESCRIPTION="Ubuntu 13.04"
There are more example scripts for creating base images in the
Docker Github Repo:
* `BusyBox <https://github.com/dotcloud/docker/blob/master/contrib/mkimage-busybox.sh>`_
* `CentOS
<https://github.com/dotcloud/docker/blob/master/contrib/mkimage-centos.sh>`_
* `Debian
<https://github.com/dotcloud/docker/blob/master/contrib/mkimage-debian.sh>`_

View File

@ -33,8 +33,12 @@ Running an interactive shell
# Run an interactive shell in the ubuntu image, # Run an interactive shell in the ubuntu image,
# allocate a tty, attach stdin and stdout # allocate a tty, attach stdin and stdout
# To detach the tty without exiting the shell,
# use the escape sequence Ctrl-p + Ctrl-q
sudo docker run -i -t ubuntu /bin/bash sudo docker run -i -t ubuntu /bin/bash
.. _dockergroup:
Why ``sudo``? Why ``sudo``?
------------- -------------
@ -138,7 +142,7 @@ Expose a service on a TCP port
.. code-block:: bash .. code-block:: bash
# Expose port 4444 of this container, and tell netcat to listen on it # Expose port 4444 of this container, and tell netcat to listen on it
JOB=$(sudo docker run -d -p 4444 ubuntu /bin/nc -l -p 4444) JOB=$(sudo docker run -d -p 4444 ubuntu:12.10 /bin/nc -l -p 4444)
# Which public port is NATed to my container? # Which public port is NATed to my container?
PORT=$(sudo docker port $JOB 4444) PORT=$(sudo docker port $JOB 4444)

View File

@ -4,13 +4,13 @@
.. _dockerbuilder: .. _dockerbuilder:
================== ======================
Dockerfile Builder Dockerfiles for Images
================== ======================
**Docker can act as a builder** and read instructions from a text **Docker can act as a builder** and read instructions from a text
Dockerfile to automate the steps you would otherwise make manually to ``Dockerfile`` to automate the steps you would otherwise take manually
create an image. Executing ``docker build`` will run your steps and to create an image. Executing ``docker build`` will run your steps and
commit them along the way, giving you a final image. commit them along the way, giving you a final image.
.. contents:: Table of Contents .. contents:: Table of Contents
@ -35,6 +35,8 @@ build succeeds:
Docker will run your steps one-by-one, committing the result if necessary, Docker will run your steps one-by-one, committing the result if necessary,
before finally outputting the ID of your new image. before finally outputting the ID of your new image.
When you're done with your build, you're ready to look into :ref:`image_push`.
2. Format 2. Format
========= =========
@ -48,9 +50,9 @@ The Dockerfile format is quite simple:
The Instruction is not case-sensitive, however convention is for them to be The Instruction is not case-sensitive, however convention is for them to be
UPPERCASE in order to distinguish them from arguments more easily. UPPERCASE in order to distinguish them from arguments more easily.
Docker evaluates the instructions in a Dockerfile in order. **The first Docker evaluates the instructions in a Dockerfile in order. **The
instruction must be `FROM`** in order to specify the base image from first instruction must be `FROM`** in order to specify the
which you are building. :ref:`base_image_def` from which you are building.
Docker will ignore **comment lines** *beginning* with ``#``. A comment Docker will ignore **comment lines** *beginning* with ``#``. A comment
marker anywhere in the rest of the line will be treated as an argument. marker anywhere in the rest of the line will be treated as an argument.
@ -66,9 +68,15 @@ building images.
``FROM <image>`` ``FROM <image>``
Or
``FROM <image>:<tag>``
The ``FROM`` instruction sets the :ref:`base_image_def` for subsequent The ``FROM`` instruction sets the :ref:`base_image_def` for subsequent
instructions. As such, a valid Dockerfile must have ``FROM`` as its instructions. As such, a valid Dockerfile must have ``FROM`` as its
first instruction. first instruction. The image can be any valid image -- it is
especially easy to start by **pulling an image** from the
:ref:`using_public_repositories`.
``FROM`` must be the first non-comment instruction in the ``FROM`` must be the first non-comment instruction in the
``Dockerfile``. ``Dockerfile``.
@ -77,6 +85,9 @@ first instruction.
to create multiple images. Simply make a note of the last image id to create multiple images. Simply make a note of the last image id
output by the commit before each new ``FROM`` command. output by the commit before each new ``FROM`` command.
If no ``tag`` is given to the ``FROM`` instruction, ``latest`` is
assumed. If the used tag does not exist, an error will be returned.
3.2 MAINTAINER 3.2 MAINTAINER
-------------- --------------
@ -160,7 +171,7 @@ override the default specified in CMD.
The ``EXPOSE`` instruction sets ports to be publicly exposed when The ``EXPOSE`` instruction sets ports to be publicly exposed when
running the image. This is functionally equivalent to running ``docker running the image. This is functionally equivalent to running ``docker
commit -run '{"PortSpecs": ["<port>", "<port2>"]}'`` outside the commit -run '{"PortSpecs": ["<port>", "<port2>"]}'`` outside the
builder. builder. Take a look at :ref:`port_redirection` for more information.
3.6 ENV 3.6 ENV
------- -------

View File

@ -13,8 +13,8 @@ Contents:
:maxdepth: 1 :maxdepth: 1
basics basics
workingwithrepository
port_redirection
builder builder
workingwithrepository
baseimages
port_redirection
puppet puppet

View File

@ -3,10 +3,12 @@
:keywords: Usage, basic port, docker, documentation, examples :keywords: Usage, basic port, docker, documentation, examples
.. _port_redirection:
Port redirection Port redirection
================ ================
Docker can redirect public TCP ports to your container, so it can be Docker can redirect public TCP and UDP ports to your container, so it can be
reached over the network. Port redirection is done on ``docker run`` reached over the network. Port redirection is done on ``docker run``
using the -p flag. using the -p flag.
@ -23,6 +25,12 @@ will be allocated.
# PUBLIC port 80 is redirected to PRIVATE port 80 # PUBLIC port 80 is redirected to PRIVATE port 80
sudo docker run -p 80:80 <image> <cmd> sudo docker run -p 80:80 <image> <cmd>
To redirect a UDP port the redirection must be expressed as *PUBLIC:PRIVATE/udp*:
.. code-block:: bash
# PUBLIC port 5300 is redirected to the PRIVATE port 53 using UDP
sudo docker run -p 5300:53/udp <image> <cmd>
Default port redirects can be built into a container with the Default port redirects can be built into a container with the
``EXPOSE`` build command. ``EXPOSE`` build command.

View File

@ -28,12 +28,18 @@ repositories. You can host your own Registry too! Docker acts as a
client for these services via ``docker search, pull, login`` and client for these services via ``docker search, pull, login`` and
``push``. ``push``.
Top-level, User, and Your Own Repositories .. _using_public_repositories:
------------------------------------------
Public Repositories
-------------------
There are two types of public repositories: *top-level* repositories There are two types of public repositories: *top-level* repositories
which are controlled by the Docker team, and *user* repositories which are controlled by the Docker team, and *user* repositories
created by individual contributors. created by individual contributors. Anyone can read from these
repositories -- they really help people get started quickly! You could
also use :ref:`using_private_repositories` if you need to keep control
of who accesses your images, but we will only refer to public
repositories in these examples.
* Top-level repositories can easily be recognized by **not** having a * Top-level repositories can easily be recognized by **not** having a
``/`` (slash) in their name. These repositories can generally be ``/`` (slash) in their name. These repositories can generally be
@ -46,28 +52,6 @@ created by individual contributors.
* User images are not checked, it is therefore up to you whether or * User images are not checked, it is therefore up to you whether or
not you trust the creator of this image. not you trust the creator of this image.
Right now (version 0.5), private repositories are only possible by
hosting `your own registry
<https://github.com/dotcloud/docker-registry>`_. To push or pull to a
repository on your own registry, you must prefix the tag with the
address of the registry's host, like this:
.. code-block:: bash
# Tag to create a repository with the full registry location.
# The location (e.g. localhost.localdomain:5000) becomes
# a permanent part of the repository name
sudo docker tag 0u812deadbeef localhost.localdomain:5000/repo_name
# Push the new repository to its home location on localhost
sudo docker push localhost.localdomain:5000/repo_name
Once a repository has your registry's host name as part of the tag,
you can push and pull it like any other repository, but it will
**not** be searchable (or indexed at all) in the Central Index, and
there will be no user name checking performed. Your registry will
function completely independently from the Central Index.
Find public images available on the Central Index Find public images available on the Central Index
------------------------------------------------- -------------------------------------------------
@ -105,6 +89,7 @@ If your username does not exist it will prompt you to also enter a
password and your e-mail address. It will then automatically log you password and your e-mail address. It will then automatically log you
in. in.
.. _container_commit:
Committing a container to a named image Committing a container to a named image
--------------------------------------- ---------------------------------------
@ -117,16 +102,45 @@ your container to an image within your username namespace.
# for example docker commit $CONTAINER_ID dhrp/kickassapp # for example docker commit $CONTAINER_ID dhrp/kickassapp
sudo docker commit <container_id> <username>/<repo_name> sudo docker commit <container_id> <username>/<repo_name>
.. _image_push:
Pushing a container to its repository Pushing an image to its repository
------------------------------------- ----------------------------------
In order to push an image to its repository you need to have committed In order to push an image to its repository you need to have committed
your container to a named image (see above) your container to a named image (see above)
Now you can commit this image to the repository Now you can commit this image to the repository designated by its name
or tag.
.. code-block:: bash .. code-block:: bash
# for example docker push dhrp/kickassapp # for example docker push dhrp/kickassapp
sudo docker push <username>/<repo_name> sudo docker push <username>/<repo_name>
.. _using_private_repositories:
Private Repositories
--------------------
Right now (version 0.5), private repositories are only possible by
hosting `your own registry
<https://github.com/dotcloud/docker-registry>`_. To push or pull to a
repository on your own registry, you must prefix the tag with the
address of the registry's host, like this:
.. code-block:: bash
# Tag to create a repository with the full registry location.
# The location (e.g. localhost.localdomain:5000) becomes
# a permanent part of the repository name
sudo docker tag 0u812deadbeef localhost.localdomain:5000/repo_name
# Push the new repository to its home location on localhost
sudo docker push localhost.localdomain:5000/repo_name
Once a repository has your registry's host name as part of the tag,
you can push and pull it like any other repository, but it will
**not** be searchable (or indexed at all) in the Central Index, and
there will be no user name checking performed. Your registry will
function completely independently from the Central Index.

View File

@ -6,6 +6,7 @@
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="google-site-verification" content="UxV66EKuPe87dgnH1sbrldrx6VsoWMrx5NjwkgUFxXI" /> <meta name="google-site-verification" content="UxV66EKuPe87dgnH1sbrldrx6VsoWMrx5NjwkgUFxXI" />
<title>{{ meta['title'] if meta and meta['title'] else title }} - Docker Documentation</title> <title>{{ meta['title'] if meta and meta['title'] else title }} - Docker Documentation</title>
@ -110,6 +111,9 @@
<div class="span3 sidebar bs-docs-sidebar"> <div class="span3 sidebar bs-docs-sidebar">
{{ toctree(collapse=False, maxdepth=3) }} {{ toctree(collapse=False, maxdepth=3) }}
<form>
<input type="text" id="st-search-input" class="st-search-input span3" style="width:160px;" />
</form>
</div> </div>
<!-- body block --> <!-- body block -->
@ -121,6 +125,26 @@
{% block body %}{% endblock %} {% block body %}{% endblock %}
</section> </section>
<!-- Swiftype search -->
<div id="st-results-container"></div>
<script type="text/javascript">
var Swiftype = window.Swiftype || {};
(function() {
Swiftype.key = 'pWPnnyvwcfpcrw1o51Sz';
Swiftype.inputElement = '#st-search-input';
Swiftype.resultContainingElement = '#st-results-container';
Swiftype.attachElement = '#st-search-input';
Swiftype.renderStyle = "overlay";
var script = document.createElement('script');
script.type = 'text/javascript';
script.async = true;
script.src = "//swiftype.com/embed.js";
var entry = document.getElementsByTagName('script')[0];
entry.parentNode.insertBefore(script, entry);
}());
</script>
</div> </div>
</div> </div>
</div> </div>
@ -212,7 +236,9 @@
} }
// attached handler on click // attached handler on click
$('.sidebar > ul > li > a').not(':last').click(function(){ // Do not attach to first element or last (intro, faq) so that
// first and last link directly instead of accordian
$('.sidebar > ul > li > a').not(':last').not(':first').click(function(){
var index = $.inArray(this.href, openmenus) var index = $.inArray(this.href, openmenus)

View File

@ -391,3 +391,21 @@ dt:hover > a.headerlink {
float: right; float: right;
visibility: hidden; visibility: hidden;
} }
/* Swiftype style */
#st-search-input {
margin-right: 14px;
margin-left: 9px;
height: 19px;
width: 120px;
}
#swiftype-img {
border: none;
width: 145px;
height: auto;
margin: 0px auto;
margin-left: 13px;
margin-top: -30px;
}

View File

@ -202,6 +202,8 @@ func (graph *Graph) getDockerInitLayer() (string, error) {
"/sys": "dir", "/sys": "dir",
"/.dockerinit": "file", "/.dockerinit": "file",
"/etc/resolv.conf": "file", "/etc/resolv.conf": "file",
"/etc/hosts": "file",
"/etc/hostname": "file",
// "var/run": "dir", // "var/run": "dir",
// "var/lock": "dir", // "var/lock": "dir",
} { } {
@ -272,30 +274,19 @@ func (graph *Graph) Delete(name string) error {
// Map returns a list of all images in the graph, addressable by ID. // Map returns a list of all images in the graph, addressable by ID.
func (graph *Graph) Map() (map[string]*Image, error) { func (graph *Graph) Map() (map[string]*Image, error) {
// FIXME: this should replace All() images := make(map[string]*Image)
all, err := graph.All() err := graph.walkAll(func(image *Image) {
images[image.ID] = image
})
if err != nil { if err != nil {
return nil, err return nil, err
} }
images := make(map[string]*Image, len(all))
for _, image := range all {
images[image.ID] = image
}
return images, nil return images, nil
} }
// All returns a list of all images in the graph. // walkAll iterates over each image in the graph, and passes it to a handler.
func (graph *Graph) All() ([]*Image, error) {
var images []*Image
err := graph.WalkAll(func(image *Image) {
images = append(images, image)
})
return images, err
}
// WalkAll iterates over each image in the graph, and passes it to a handler.
// The walking order is undetermined. // The walking order is undetermined.
func (graph *Graph) WalkAll(handler func(*Image)) error { func (graph *Graph) walkAll(handler func(*Image)) error {
files, err := ioutil.ReadDir(graph.Root) files, err := ioutil.ReadDir(graph.Root)
if err != nil { if err != nil {
return err return err
@ -317,7 +308,7 @@ func (graph *Graph) WalkAll(handler func(*Image)) error {
// If an image has no children, it will not have an entry in the table. // If an image has no children, it will not have an entry in the table.
func (graph *Graph) ByParent() (map[string][]*Image, error) { func (graph *Graph) ByParent() (map[string][]*Image, error) {
byParent := make(map[string][]*Image) byParent := make(map[string][]*Image)
err := graph.WalkAll(func(image *Image) { err := graph.walkAll(func(image *Image) {
parent, err := graph.Get(image.Parent) parent, err := graph.Get(image.Parent)
if err != nil { if err != nil {
return return
@ -339,7 +330,7 @@ func (graph *Graph) Heads() (map[string]*Image, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
err = graph.WalkAll(func(image *Image) { err = graph.walkAll(func(image *Image) {
// If it's not in the byParent lookup table, then // If it's not in the byParent lookup table, then
// it's not a parent -> so it's a head! // it's not a parent -> so it's a head!
if _, exists := byParent[image.ID]; !exists { if _, exists := byParent[image.ID]; !exists {

View File

@ -20,11 +20,11 @@ func TestInit(t *testing.T) {
if _, err := os.Stat(graph.Root); err != nil { if _, err := os.Stat(graph.Root); err != nil {
t.Fatal(err) t.Fatal(err)
} }
// All() should be empty // Map() should be empty
if l, err := graph.All(); err != nil { if l, err := graph.Map(); err != nil {
t.Fatal(err) t.Fatal(err)
} else if len(l) != 0 { } else if len(l) != 0 {
t.Fatalf("List() should return %d, not %d", 0, len(l)) t.Fatalf("len(Map()) should return %d, not %d", 0, len(l))
} }
} }
@ -76,11 +76,15 @@ func TestGraphCreate(t *testing.T) {
if image.DockerVersion != VERSION { if image.DockerVersion != VERSION {
t.Fatalf("Wrong docker_version: should be '%s', not '%s'", VERSION, image.DockerVersion) t.Fatalf("Wrong docker_version: should be '%s', not '%s'", VERSION, image.DockerVersion)
} }
if images, err := graph.All(); err != nil { images, err := graph.Map()
if err != nil {
t.Fatal(err) t.Fatal(err)
} else if l := len(images); l != 1 { } else if l := len(images); l != 1 {
t.Fatalf("Wrong number of images. Should be %d, not %d", 1, l) t.Fatalf("Wrong number of images. Should be %d, not %d", 1, l)
} }
if images[image.ID] == nil {
t.Fatalf("Could not find image with id %s", image.ID)
}
} }
func TestRegister(t *testing.T) { func TestRegister(t *testing.T) {
@ -99,7 +103,7 @@ func TestRegister(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
if images, err := graph.All(); err != nil { if images, err := graph.Map(); err != nil {
t.Fatal(err) t.Fatal(err)
} else if l := len(images); l != 1 { } else if l := len(images); l != 1 {
t.Fatalf("Wrong number of images. Should be %d, not %d", 1, l) t.Fatalf("Wrong number of images. Should be %d, not %d", 1, l)
@ -274,7 +278,7 @@ func TestByParent(t *testing.T) {
} }
func assertNImages(graph *Graph, t *testing.T, n int) { func assertNImages(graph *Graph, t *testing.T, n int) {
if images, err := graph.All(); err != nil { if images, err := graph.Map(); err != nil {
t.Fatal(err) t.Fatal(err)
} else if actualN := len(images); actualN != n { } else if actualN := len(images); actualN != n {
t.Fatalf("Expected %d images, found %d", n, actualN) t.Fatalf("Expected %d images, found %d", n, actualN)

1
hack/MAINTAINERS Normal file
View File

@ -0,0 +1 @@
Solomon Hykes <solomon@dotcloud.com> (@shykes)

137
hack/PACKAGERS.md Normal file
View File

@ -0,0 +1,137 @@
Dear packager.
If you are looking to make docker available on your favorite software distribution,
this document is for you. It summarizes the requirements for building and running
docker.
## Getting started
We really want to help you package Docker successfully. Before anything, a good first step
is to introduce yourself on the [docker-dev mailing list](https://groups.google.com/forum/?fromgroups#!forum/docker-dev)
, explain what you''re trying to achieve, and tell us how we can help. Don''t worry, we don''t bite!
There might even be someone already working on packaging for the same distro!
You can also join the IRC channel - #docker and #docker-dev on Freenode are both active and friendly.
## Package name
If possible, your package should be called "docker". If that name is already taken, a second
choice is "lxc-docker".
## Official build vs distro build
The Docker project maintains its own build and release toolchain. It is pretty neat and entirely
based on Docker (surprise!). This toolchain is the canonical way to build Docker, and the only
method supported by the development team. We encourage you to give it a try, and if the circumstances
allow you to use it, we recommend that you do.
You might not be able to use the official build toolchain - usually because your distribution has a
toolchain and packaging policy of its own. We get it! Your house, your rules. The rest of this document
should give you the information you need to package Docker your way, without denaturing it in
the process.
## System build dependencies
To build docker, you will need the following system dependencies
* An amd64 machine
* A recent version of git and mercurial
* Go version 1.1.2
* A clean checkout of the source must be added to a valid Go [workspace](http://golang.org/doc/code.html#Workspaces)
under the path *src/github.com/dotcloud/docker*. See
## Go dependencies
All Go dependencies are vendored under ./vendor. They are used by the official build,
so the source of truth for the current version is whatever is in ./vendor.
To use the vendored dependencies, simply make sure the path to ./vendor is included in $GOPATH.
If you would rather package these dependencies yourself, take a look at ./hack/vendor.sh for an
easy-to-parse list of the exact version for each.
NOTE: if you''re not able to package the exact version (to the exact commit) of a given dependency,
please get in touch so we can remediate! Who knows what discrepancies can be caused by even the
slightest deviation. We promise to do our best to make everybody happy.
## Disabling CGO
Make sure to disable CGO on your system, and then recompile the standard library on the build
machine:
```bash
export CGO_ENABLED=0
cd /tmp && echo 'package main' > t.go && go test -a -i -v
```
## Building Docker
To build the docker binary, run the following command with the source checkout as the
working directory:
```
./hack/make.sh binary
```
This will create a static binary under *./bundles/$VERSION/binary/docker-$VERSION*, where
*$VERSION* is the contents of the file *./VERSION*.
You are encouraged to use ./hack/make.sh without modification. If you must absolutely write
your own script (are you really, really sure you need to? make.sh is really not that complicated),
then please take care the respect the following:
* In *./hack/make.sh*: $LDFLAGS, $VERSION and $GITCOMMIT
* In *./hack/make/binary*: the exact build command to run
You may be tempted to tweak these settings. In particular, being a rigorous maintainer, you may want
to disable static linking. Please don''t! Docker *needs* to be statically linked to function properly.
You would do the users of your distro a disservice and "void the docker warranty" by changing the flags.
A good comparison is Busybox: all distros package it as a statically linked binary, because it just
makes sense. Docker is the same way.
## Testing Docker
Before releasing your binary, make sure to run the tests! Run the following command with the source
checkout as the working directory:
```bash
./hack/make.sh test
```
The test suite includes both live integration tests and unit tests, so you will need all runtime
dependencies to be installed (see below).
The test suite will also download a small test container, so you will need internet connectivity.
## Runtime dependencies
To run properly, docker needs the following software to be installed at runtime:
* GNU Tar version 1.26 or later
* iproute2 version 3.5 or later (build after 2012-05-21), and specifically the "ip" utility.
* iptables version 1.4 or later
* The lxc utility scripts (http://lxc.sourceforge.net) version 0.8 or later.
* Git version 1.7 or later
## Kernel dependencies
Docker in daemon mode has specific kernel requirements. For details, see
http://docs.docker.io/en/latest/installation/kernel/
Note that Docker also has a client mode, which can run on virtually any linux kernel (it even builds
on OSX!).
## Init script
Docker expects to run as a daemon at machine startup. Your package will need to include a script
for your distro''s process supervisor of choice.
Docker should be run as root, with the following arguments:
```
docker -d
```

View File

@ -7,99 +7,39 @@ For a more complete view of planned and requested improvements, see [the Github
Tu suggest changes to the roadmap, including additions, please write the change as if it were already in effect, and make a pull request. Tu suggest changes to the roadmap, including additions, please write the change as if it were already in effect, and make a pull request.
Broader kernel support
----------------------
Our goal is to make Docker run everywhere, but currently Docker requires [Linux version 3.8 or higher with lxc and aufs support](http://docs.docker.io/en/latest/installation/kernel.html). If you're deploying new machines for the purpose of running Docker, this is a fairly easy requirement to meet. ## Container wiring and service discovery
However, if you're adding Docker to an existing deployment, you may not have the flexibility to update and patch the kernel.
Expanding Docker's kernel support is a priority. This includes running on older kernel versions, In its current version, docker doesnt make it very easy to manipulate multiple containers as a cohesive group (ie. orchestration), and it doesnt make it seamless for containers to connect to each other as network services (ie. wiring).
but also on kernels with no AUFS support, or with incomplete lxc capabilities.
To achieve wiring and orchestration with docker today, you need to write glue scripts yourself, or use one several companion tools available, like Orchestra, Shipper, Deis, Pipeworks, etc.
We want the Docker API to support orchestration and wiring natively, so that these tools can cleanly and seamlessly integrate into the Docker user experience, and remain interoperable with each other.
Cross-architecture support ## Better integration with process supervisors
--------------------------
Our goal is to make Docker run everywhere. However currently Docker only runs on x86_64 systems. For docker to be fully usable in production, it needs to cleanly integrate with the host machines process supervisor of choice. Whether its sysV-init, upstart, systemd, runit or supervisord, we want to make sure docker plays nice with your existing system. This will be a major focus of the 0.7 release.
We plan on expanding architecture support, so that Docker containers can be created and used on more architectures.
Even more integrations ## Plugin API
----------------------
We want Docker to be the secret ingredient that makes your existing tools more awesome. We want Docker to run everywhere, and to integrate with every devops tool. Those are ambitious goals, and the only way to reach them is with the Docker community. For the community to participate fully, we need an API which allows Docker to be deeply and easily customized.
Thanks to this philosophy, Docker has already been integrated with
[Puppet](http://forge.puppetlabs.com/garethr/docker), [Chef](http://www.opscode.com/chef),
[Openstack Nova](https://github.com/dotcloud/openstack-docker), [Jenkins](https://github.com/georgebashi/jenkins-docker-plugin),
[DotCloud sandbox](http://github.com/dotcloud/sandbox), [Pallet](https://github.com/pallet/pallet-docker),
[Strider CI](http://blog.frozenridge.co/next-generation-continuous-integration-deployment-with-dotclouds-docker-and-strider/)
and even [Heroku buildpacks](https://github.com/progrium/buildstep).
Expect Docker to integrate with even more of your favorite tools going forward, including: We are working on a plugin API which will make Docker very, very customization-friendly. We believe it will facilitate the integrations listed above and many more we didnt even think about.
* Alternative storage backends such as ZFS, LVM or [BTRFS](github.com/dotcloud/docker/issues/443)
* Alternative containerization backends such as [OpenVZ](http://openvz.org), Solaris Zones, BSD Jails and even plain Chroot.
* Process managers like [Supervisord](http://supervisord.org/), [Runit](http://smarden.org/runit/), [Gaffer](https://gaffer.readthedocs.org/en/latest/#gaffer) and [Systemd](http://www.freedesktop.org/wiki/Software/systemd/)
* Build and integration tools like Make, Maven, Scons, Jenkins, Buildbot and Cruise Control.
* Configuration management tools like [Puppet](http://puppetlabs.com), [Chef](http://www.opscode.com/chef/) and [Salt](http://saltstack.org)
* Personal development environments like [Vagrant](http://vagrantup.com), [Boxen](http://boxen.github.com/), [Koding](http://koding.com) and [Cloud9](http://c9.io).
* Orchestration tools like [Zookeeper](http://zookeeper.apache.org/), [Mesos](http://incubator.apache.org/mesos/) and [Galaxy](https://github.com/ning/galaxy)
* Infrastructure deployment tools like [Openstack](http://openstack.org), [Apache Cloudstack](http://apache.cloudstack.org), [Ganeti](https://code.google.com/p/ganeti/)
Plugin API ## Broader kernel support
----------
We want Docker to run everywhere, and to integrate with every devops tool. Our goal is to make Docker run everywhere, but currently Docker requires Linux version 3.8 or higher with lxc and aufs support. If youre deploying new machines for the purpose of running Docker, this is a fairly easy requirement to meet. However, if youre adding Docker to an existing deployment, you may not have the flexibility to update and patch the kernel.
Those are ambitious goals, and the only way to reach them is with the Docker community.
For the community to participate fully, we need an API which allows Docker to be deeply and easily customized.
We are working on a plugin API which will make Docker very, very customization-friendly. Expanding Dockers kernel support is a priority. This includes running on older kernel versions, but also on kernels with no AUFS support, or with incomplete lxc capabilities.
We believe it will facilitate the integrations listed above - and many more we didn't even think about.
Let us know if you want to start playing with the API before it's generally available.
Externally mounted volumes ## Cross-architecture support
--------------------------
In 0.3 we [introduced data volumes](https://github.com/dotcloud/docker/wiki/Docker-0.3.0-release-note%2C-May-6-2013#data-volumes), Our goal is to make Docker run everywhere. However currently Docker only runs on x86_64 systems. We plan on expanding architecture support, so that Docker containers can be created and used on more architectures.
a great mechanism for manipulating persistent data such as database files, log files, etc.
Data volumes can be shared between containers, a powerful capability [which allows many advanced use cases](http://docs.docker.io/en/latest/examples/couchdb_data_volumes.html). In the future it will also be possible to share volumes between a container and the underlying host. This will make certain scenarios much easier, such as using a high-performance storage backend for your production database, ## Production-ready
making live development changes available to a container, etc.
Docker is still beta software, and not suited for production. We are working hard to get there, and we are confident that it will be possible within a few months. Stay tuned for a more detailed roadmap soon.
Better documentation
--------------------
We believe that great documentation is worth 10 features. We are often told that "Docker's documentation is great for a 2-month old project".
Our goal is to make it great, period.
If you have feedback on how to improve our documentation, please get in touch by replying to this email,
or by [filing an issue](https://github.com/dotcloud/docker/issues). We always appreciate it!
Production-ready
----------------
Docker is still alpha software, and not suited for production.
We are working hard to get there, and we are confident that it will be possible within a few months.
Advanced port redirections
--------------------------
Docker currently supports 2 flavors of port redirection: STATIC->STATIC (eg. "redirect public port 80 to private port 80")
and RANDOM->STATIC (eg. "redirect any public port to private port 80").
With these 2 flavors, docker can support the majority of backend programs out there. But some applications have more exotic
requirements, generally to implement custom clustering techniques. These applications include Hadoop, MongoDB, Riak, RabbitMQ,
Disco, and all programs relying on Erlang's OTP.
To support these applications, Docker needs to support more advanced redirection flavors, including:
* RANDOM->RANDOM
* STATIC1->STATIC2
These flavors should be implemented without breaking existing semantics, if at all possible.

59
hack/dind Executable file
View File

@ -0,0 +1,59 @@
#!/bin/bash
# DinD: a wrapper script which allows docker to be run inside a docker container.
# Original version by Jerome Petazzoni <jerome@dotcloud.com>
# See the blog post: http://blog.docker.io/2013/09/docker-can-now-run-within-docker/
#
# This script should be executed inside a docker container in privilieged mode
# ('docker run -privileged', introduced in docker 0.6).
# Usage: dind CMD [ARG...]
# First, make sure that cgroups are mounted correctly.
CGROUP=/sys/fs/cgroup
[ -d $CGROUP ] ||
mkdir $CGROUP
mountpoint -q $CGROUP ||
mount -n -t tmpfs -o uid=0,gid=0,mode=0755 cgroup $CGROUP || {
echo "Could not make a tmpfs mount. Did you use -privileged?"
exit 1
}
# Mount the cgroup hierarchies exactly as they are in the parent system.
for SUBSYS in $(cut -d: -f2 /proc/1/cgroup)
do
[ -d $CGROUP/$SUBSYS ] || mkdir $CGROUP/$SUBSYS
mountpoint -q $CGROUP/$SUBSYS ||
mount -n -t cgroup -o $SUBSYS cgroup $CGROUP/$SUBSYS
done
# Note: as I write those lines, the LXC userland tools cannot setup
# a "sub-container" properly if the "devices" cgroup is not in its
# own hierarchy. Let's detect this and issue a warning.
grep -q :devices: /proc/1/cgroup ||
echo "WARNING: the 'devices' cgroup should be in its own hierarchy."
grep -qw devices /proc/1/cgroup ||
echo "WARNING: it looks like the 'devices' cgroup is not mounted."
# Now, close extraneous file descriptors.
pushd /proc/self/fd
for FD in *
do
case "$FD" in
# Keep stdin/stdout/stderr
[012])
;;
# Nuke everything else
*)
eval exec "$FD>&-"
;;
esac
done
popd
# Mount /tmp
mount -t tmpfs none /tmp
exec $*

View File

@ -0,0 +1,15 @@
docker-ci github pull request
=============================
The entire docker pull request test workflow is event driven by github. Its
usage is fully automatic and the results are logged in docker-ci.dotcloud.com
Each time there is a pull request on docker's github project, github connects
to docker-ci using github's rest API documented in http://developer.github.com/v3/repos/hooks
The issued command to program github's notification PR event was:
curl -u GITHUB_USER:GITHUB_PASSWORD -d '{"name":"web","active":true,"events":["pull_request"],"config":{"url":"http://docker-ci.dotcloud.com:8011/change_hook/github?project=docker"}}' https://api.github.com/repos/dotcloud/docker/hooks
buildbot (0.8.7p1) was patched using ./testing/buildbot/github.py, so it
can understand the PR data github sends to it. Originally PR #1603 (ee64e099e0)
implemented this capability. Also we added a new scheduler to exclusively filter
PRs. and the 'pullrequest' builder to rebase the PR on top of master and test it.

91
hack/make.sh Executable file
View File

@ -0,0 +1,91 @@
#!/bin/bash
# This script builds various binary artifacts from a checkout of the docker
# source code.
#
# Requirements:
# - The current directory should be a checkout of the docker source code
# (http://github.com/dotcloud/docker). Whatever version is checked out
# will be built.
# - The VERSION file, at the root of the repository, should exist, and
# will be used as Docker binary version and package version.
# - The hash of the git commit will also be included in the Docker binary,
# with the suffix -dirty if the repository isn't clean.
# - The script is intented to be run inside the docker container specified
# in the Dockerfile at the root of the source. In other words:
# DO NOT CALL THIS SCRIPT DIRECTLY.
# - The right way to call this script is to invoke "docker build ." from
# your checkout of the Docker repository, and then
# "docker run hack/make.sh" in the resulting container image.
#
set -e
# We're a nice, sexy, little shell script, and people might try to run us;
# but really, they shouldn't. We want to be in a container!
RESOLVCONF=$(readlink --canonicalize /etc/resolv.conf)
grep -q "$RESOLVCONF" /proc/mounts || {
echo "# WARNING! I don't seem to be running in a docker container.
echo "# The result of this command might be an incorrect build, and will not be officially supported."
echo "# Try this: 'docker build -t docker . && docker run docker ./hack/make.sh'
}
# List of bundles to create when no argument is passed
DEFAULT_BUNDLES=(
test
binary
ubuntu
)
VERSION=$(cat ./VERSION)
GITCOMMIT=$(git rev-parse --short HEAD)
if test -n "$(git status --porcelain)"
then
GITCOMMIT="$GITCOMMIT-dirty"
fi
# Use these flags when compiling the tests and final binary
LDFLAGS="-X main.GITCOMMIT $GITCOMMIT -X main.VERSION $VERSION -d -w"
bundle() {
bundlescript=$1
bundle=$(basename $bundlescript)
echo "---> Making bundle: $bundle"
mkdir -p bundles/$VERSION/$bundle
source $bundlescript $(pwd)/bundles/$VERSION/$bundle
}
main() {
# We want this to fail if the bundles already exist.
# This is to avoid mixing bundles from different versions of the code.
mkdir -p bundles
if [ -e "bundles/$VERSION" ]; then
echo "bundles/$VERSION already exists. Removing."
rm -fr bundles/$VERSION && mkdir bundles/$VERSION || exit 1
fi
SCRIPTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
if [ $# -lt 1 ]; then
bundles=($DEFAULT_BUNDLES)
else
bundles=($@)
fi
for bundle in ${bundles[@]}; do
bundle $SCRIPTDIR/make/$bundle
done
cat <<EOF
###############################################################################
Now run the resulting image, making sure that you set AWS_S3_BUCKET,
AWS_ACCESS_KEY, and AWS_SECRET_KEY environment variables:
docker run -e AWS_S3_BUCKET=get-staging.docker.io \\
AWS_ACCESS_KEY=AKI1234... \\
AWS_SECRET_KEY=sEs3mE... \\
GPG_PASSPHRASE=sesame... \\
image_id_or_name
###############################################################################
EOF
}
main "$@"

17
hack/make/README.md Normal file
View File

@ -0,0 +1,17 @@
This directory holds scripts called by `make.sh` in the parent directory.
Each script is named after the bundle it creates.
They should not be called directly - instead, pass it as argument to make.sh, for example:
```
./hack/make.sh test
./hack/make.sh binary ubuntu
# Or to run all bundles:
./hack/make.sh
```
To add a bundle:
* Create a shell-compatible file here
* Add it to $DEFAULT_BUNDLES in make.sh

4
hack/make/binary Normal file
View File

@ -0,0 +1,4 @@
DEST=$1
go build -o $DEST/docker-$VERSION -ldflags "$LDFLAGS" ./docker

27
hack/make/test Normal file
View File

@ -0,0 +1,27 @@
DEST=$1
set -e
# Run Docker's test suite, including sub-packages, and store their output as a bundle
bundle_test() {
{
date
for test_dir in $(find_test_dirs); do (
set -x
cd $test_dir
go test -v -ldflags "$LDFLAGS"
) done
} 2>&1 | tee $DEST/test.log
}
# This helper function walks the current directory looking for directories
# holding Go test files, and prints their paths on standard output, one per
# line.
find_test_dirs() {
find . -name '*_test.go' | grep -v '^./vendor' |
{ while read f; do dirname $f; done; } |
sort -u
}
bundle_test

94
hack/make/ubuntu Normal file
View File

@ -0,0 +1,94 @@
#!/bin/sh
DEST=$1
PKGVERSION="$VERSION"
if test -n "$(git status --porcelain)"
then
PKGVERSION="$PKGVERSION-$(date +%Y%m%d%H%M%S)-$GITCOMMIT"
fi
PACKAGE_ARCHITECTURE="$(dpkg-architecture -qDEB_HOST_ARCH)"
PACKAGE_URL="http://www.docker.io/"
PACKAGE_MAINTAINER="docker@dotcloud.com"
PACKAGE_DESCRIPTION="lxc-docker is a Linux container runtime
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."
UPSTART_SCRIPT='description "Docker daemon"
start on filesystem and started lxc-net
stop on runlevel [!2345]
respawn
script
/usr/bin/docker -d
end script
'
# Build docker as an ubuntu package using FPM and REPREPRO (sue me).
# bundle_binary must be called first.
bundle_ubuntu() {
DIR=$DEST/build
# Generate an upstart config file (ubuntu-specific)
mkdir -p $DIR/etc/init
echo "$UPSTART_SCRIPT" > $DIR/etc/init/docker.conf
# Copy the binary
# This will fail if the binary bundle hasn't been built
mkdir -p $DIR/usr/bin
# Copy the binary
# This will fail if the binary bundle hasn't been built
cp $DEST/../binary/docker-$VERSION $DIR/usr/bin/docker
# Generate postinstall/prerm scripts
cat >/tmp/postinstall <<EOF
#!/bin/sh
/sbin/stop docker || true
/sbin/start docker
EOF
cat >/tmp/prerm <<EOF
#!/bin/sh
/sbin/stop docker || true
EOF
chmod +x /tmp/postinstall /tmp/prerm
(
cd $DEST
fpm -s dir -C $DIR \
--name lxc-docker-$VERSION --version $PKGVERSION \
--after-install /tmp/postinstall \
--before-remove /tmp/prerm \
--architecture "$PACKAGE_ARCHITECTURE" \
--prefix / \
--depends lxc --depends aufs-tools \
--description "$PACKAGE_DESCRIPTION" \
--maintainer "$PACKAGE_MAINTAINER" \
--conflicts lxc-docker-virtual-package \
--provides lxc-docker \
--provides lxc-docker-virtual-package \
--replaces lxc-docker \
--replaces lxc-docker-virtual-package \
--url "$PACKAGE_URL" \
--vendor "$PACKAGE_VENDOR" \
-t deb .
mkdir empty
fpm -s dir -C empty \
--name lxc-docker --version $PKGVERSION \
--architecture "$PACKAGE_ARCHITECTURE" \
--depends lxc-docker-$VERSION \
--description "$PACKAGE_DESCRIPTION" \
--maintainer "$PACKAGE_MAINTAINER" \
--url "$PACKAGE_URL" \
--vendor "$PACKAGE_VENDOR" \
-t deb .
)
}
bundle_ubuntu

Some files were not shown because too many files have changed in this diff Show More